fix(agent): include TOOLS.md in bootstrap files and cache tracking

TOOLS.md is documented in README as a workspace bootstrap file and
migrated from openclaw projects, but was never read into the agent
context. Add it to LoadBootstrapFiles() and sourcePaths() so its
content is injected into the system prompt and changes trigger cache
invalidation.

Fixes #1315
This commit is contained in:
MahendraTeja95 2026-03-11 14:25:06 +05:30
parent dea06c391c
commit 8539b2e7ba
2 changed files with 53 additions and 0 deletions

View file

@ -227,6 +227,7 @@ func (cb *ContextBuilder) sourcePaths() []string {
filepath.Join(cb.workspace, "SOUL.md"), filepath.Join(cb.workspace, "SOUL.md"),
filepath.Join(cb.workspace, "USER.md"), filepath.Join(cb.workspace, "USER.md"),
filepath.Join(cb.workspace, "IDENTITY.md"), filepath.Join(cb.workspace, "IDENTITY.md"),
filepath.Join(cb.workspace, "TOOLS.md"),
filepath.Join(cb.workspace, "memory", "MEMORY.md"), filepath.Join(cb.workspace, "memory", "MEMORY.md"),
} }
} }
@ -437,6 +438,7 @@ func (cb *ContextBuilder) LoadBootstrapFiles() string {
"SOUL.md", "SOUL.md",
"USER.md", "USER.md",
"IDENTITY.md", "IDENTITY.md",
"TOOLS.md",
} }
var sb strings.Builder var sb strings.Builder

View file

@ -126,6 +126,57 @@ func TestSingleSystemMessage(t *testing.T) {
} }
} }
// TestToolsMdIncludedInBootstrap verifies that TOOLS.md is loaded into the
// system prompt as a bootstrap file, alongside AGENTS.md, SOUL.md, etc.
// Fixes #1315: TOOLS.md was documented in README and migrated from openclaw,
// but never read into the agent context.
func TestToolsMdIncludedInBootstrap(t *testing.T) {
tmpDir := setupWorkspace(t, map[string]string{
"TOOLS.md": "# Tools\nUse the search tool for web queries.",
})
defer os.RemoveAll(tmpDir)
cb := NewContextBuilder(tmpDir)
prompt := cb.BuildSystemPrompt()
if !strings.Contains(prompt, "Use the search tool for web queries") {
t.Error("system prompt should contain TOOLS.md content")
}
if !strings.Contains(prompt, "TOOLS.md") {
t.Error("system prompt should contain TOOLS.md header")
}
}
// TestToolsMdCacheInvalidation verifies that changes to TOOLS.md invalidate
// the cached system prompt.
func TestToolsMdCacheInvalidation(t *testing.T) {
tmpDir := setupWorkspace(t, map[string]string{
"TOOLS.md": "# Tools v1",
})
defer os.RemoveAll(tmpDir)
cb := NewContextBuilder(tmpDir)
sp1 := cb.BuildSystemPromptWithCache()
if !strings.Contains(sp1, "Tools v1") {
t.Fatal("initial prompt should contain TOOLS.md content")
}
// Update TOOLS.md
toolsPath := filepath.Join(tmpDir, "TOOLS.md")
os.WriteFile(toolsPath, []byte("# Tools v2\nUpdated tool descriptions."), 0o644)
future := time.Now().Add(2 * time.Second)
os.Chtimes(toolsPath, future, future)
// Cache should auto-invalidate
sp2 := cb.BuildSystemPromptWithCache()
if !strings.Contains(sp2, "Tools v2") {
t.Error("rebuilt prompt should contain updated TOOLS.md content")
}
if sp1 == sp2 {
t.Error("cache should be invalidated when TOOLS.md changes")
}
}
// TestMtimeAutoInvalidation verifies that the cache detects source file changes // TestMtimeAutoInvalidation verifies that the cache detects source file changes
// via mtime without requiring explicit InvalidateCache(). // via mtime without requiring explicit InvalidateCache().
// Fix: original implementation had no auto-invalidation — edits to bootstrap files, // Fix: original implementation had no auto-invalidation — edits to bootstrap files,