mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-19 18:15:41 +00:00
fix: invalidate ContextFileInterceptor cache on agents.files.set (#29)
* fix: invalidate ContextFileInterceptor cache on agents.files.set When agents.files.set (or agents.update) wrote context files to agent_context_files in Postgres, only the agent router cache was invalidated via agents.InvalidateAgent(). The ContextFileInterceptor maintains its own separate in-memory cache with a 5-minute TTL which was never cleared, causing the agent to serve stale file content (e.g. wizard-written SOUL.md/IDENTITY.md) for up to 5 minutes. Fix: inject *tools.ContextFileInterceptor into AgentsMethods and call intc.InvalidateAgent(ag.ID) immediately after each successful DB write in handleFilesSet and handleUpdate. Wire path: wireManagedExtras now returns the interceptor it creates; gateway.go captures it and passes it through registerAllMethods → NewAgentsMethods. The interceptor is nil in standalone mode so all nil guards are in place. * test: add unit tests for ContextFileInterceptor cache invalidation Covers: - Cache hit: second read does not call store again - InvalidateAgent clears agent cache → fresh content served from store - InvalidateAgent clears user cache for that agent - InvalidateAgent does not affect other agents' cache entries - TTL expiry causes re-fetch from store
This commit is contained in:
1 parent
82ce20a029
commit
8d513763f2
6 files changed
+288
-14
No files matched your search
+7
-2
@@ -579,6 +579,11 @@ func runGateway() {
|
||||
server.SetPolicyEngine(permPE)
|
||||
server.SetPairingService(pairingStore)
|
||||
|
||||
// contextFileInterceptor is created inside wireManagedExtras (managed mode only).
|
||||
// Declared here so it can be passed to registerAllMethods → AgentsMethods
|
||||
// for immediate cache invalidation on agents.files.set.
|
||||
var contextFileInterceptor *tools.ContextFileInterceptor
|
||||
|
||||
// Managed mode: set agent store for tools_invoke context injection + wire extras
|
||||
if managedStores != nil && managedStores.Agents != nil {
|
||||
server.SetAgentStore(managedStores.Agents)
|
||||
@@ -593,7 +598,7 @@ func runGateway() {
|
||||
}
|
||||
}
|
||||
|
||||
wireManagedExtras(managedStores, agentRouter, providerRegistry, msgBus, sessStore, toolsReg, toolPE, skillsLoader, hasMemory, traceCollector, workspace, cfg.Gateway.InjectionAction, cfg, sandboxMgr, dynamicLoader)
|
||||
contextFileInterceptor = wireManagedExtras(managedStores, agentRouter, providerRegistry, msgBus, sessStore, toolsReg, toolPE, skillsLoader, hasMemory, traceCollector, workspace, cfg.Gateway.InjectionAction, cfg, sandboxMgr, dynamicLoader)
|
||||
agentsH, skillsH, tracesH, mcpH, customToolsH, channelInstancesH, providersH, delegationsH, builtinToolsH := wireManagedHTTP(managedStores, cfg.Gateway.Token, msgBus, toolsReg, providerRegistry, permPE.IsOwner)
|
||||
if agentsH != nil {
|
||||
server.SetAgentsHandler(agentsH)
|
||||
@@ -654,7 +659,7 @@ func runGateway() {
|
||||
teamStoreForRPC = managedStores.Teams
|
||||
}
|
||||
|
||||
pairingMethods := registerAllMethods(server, agentRouter, sessStore, cronStore, pairingStore, cfg, cfgPath, workspace, dataDir, msgBus, execApprovalMgr, agentStoreForRPC, isManaged, skillStore, configSecretsStore, teamStoreForRPC)
|
||||
pairingMethods := registerAllMethods(server, agentRouter, sessStore, cronStore, pairingStore, cfg, cfgPath, workspace, dataDir, msgBus, execApprovalMgr, agentStoreForRPC, isManaged, skillStore, configSecretsStore, teamStoreForRPC, contextFileInterceptor)
|
||||
|
||||
// Channel manager
|
||||
channelMgr := channels.NewManager(msgBus)
|
||||
|
||||
@@ -25,6 +25,8 @@ import (
|
||||
// agent resolver (lazy-creates Loops from DB), virtual FS interceptors, memory tools,
|
||||
// and cache invalidation event subscribers.
|
||||
// PG store creation and tracing are handled in gateway.go before this is called.
|
||||
// Returns the ContextFileInterceptor so callers can pass it to AgentsMethods
|
||||
// for immediate cache invalidation on agents.files.set.
|
||||
func wireManagedExtras(
|
||||
stores *store.Stores,
|
||||
agentRouter *agent.Router,
|
||||
@@ -41,7 +43,7 @@ func wireManagedExtras(
|
||||
appCfg *config.Config,
|
||||
sandboxMgr sandbox.Manager,
|
||||
dynamicLoader *tools.DynamicToolLoader,
|
||||
) {
|
||||
) *tools.ContextFileInterceptor {
|
||||
// 1. Context file interceptor (created before resolver so callbacks can reference it)
|
||||
var contextFileInterceptor *tools.ContextFileInterceptor
|
||||
if stores.Agents != nil {
|
||||
@@ -354,6 +356,7 @@ func wireManagedExtras(
|
||||
}
|
||||
|
||||
slog.Info("managed mode: resolver + interceptors + cache subscribers wired")
|
||||
return contextFileInterceptor
|
||||
}
|
||||
|
||||
// wireManagedHTTP creates managed-mode HTTP handlers (agents + skills + traces + MCP + custom tools + channel instances + providers + delegations + builtin tools).
|
||||
|
||||
@@ -12,12 +12,12 @@ import (
|
||||
"github.com/nextlevelbuilder/goclaw/internal/tools"
|
||||
)
|
||||
|
||||
func registerAllMethods(server *gateway.Server, agents *agent.Router, sessStore store.SessionStore, cronStore store.CronStore, pairingStore store.PairingStore, cfg *config.Config, cfgPath, workspace, dataDir string, msgBus *bus.MessageBus, execApprovalMgr *tools.ExecApprovalManager, agentStore store.AgentStore, isManaged bool, skillStore store.SkillStore, configSecretsStore store.ConfigSecretsStore, teamStore store.TeamStore) *methods.PairingMethods {
|
||||
func registerAllMethods(server *gateway.Server, agents *agent.Router, sessStore store.SessionStore, cronStore store.CronStore, pairingStore store.PairingStore, cfg *config.Config, cfgPath, workspace, dataDir string, msgBus *bus.MessageBus, execApprovalMgr *tools.ExecApprovalManager, agentStore store.AgentStore, isManaged bool, skillStore store.SkillStore, configSecretsStore store.ConfigSecretsStore, teamStore store.TeamStore, contextFileInterceptor *tools.ContextFileInterceptor) *methods.PairingMethods {
|
||||
router := server.Router()
|
||||
|
||||
// Phase 1: Core methods
|
||||
methods.NewChatMethods(agents, sessStore, isManaged, server.RateLimiter()).Register(router)
|
||||
methods.NewAgentsMethods(agents, cfg, cfgPath, workspace, agentStore, isManaged).Register(router)
|
||||
methods.NewAgentsMethods(agents, cfg, cfgPath, workspace, agentStore, isManaged, contextFileInterceptor).Register(router)
|
||||
methods.NewSessionsMethods(sessStore).Register(router)
|
||||
methods.NewConfigMethods(cfg, cfgPath, isManaged, configSecretsStore).Register(router)
|
||||
|
||||
|
||||
Reference in new issue
Block a user