fix(tools): hint model when shell commands target DB-backed memory files

Memory files (MEMORY.md, memory/*.md) are stored in PostgreSQL, not on
disk. When the model uses exec to run cat/ls/grep/find on memory paths,
short-circuit with a hint to use memory_search or memory_get instead.
Also append [Source: database, not filesystem] to read_file and
list_files results served from the memory interceptor.
This commit is contained in:
viettranx committed 2026-04-02 19:28:04 +07:00
1 parent 99720892a7
commit 7ba3a5115a
4 files changed
+54 -2

No files matched your search

+47
View File
@@ -0,0 +1,47 @@
package tools
import "strings"
// memoryHintPrefixes are shell commands that read/list/search filesystem content.
// If these target memory paths, the model should use dedicated memory tools instead.
var memoryHintPrefixes = []string{
"cat ", "head ", "tail ", "less ", "more ",
"grep ", "rg ", "ag ",
"find ", "ls ", "wc ",
}
// memoryPathTokens are path fragments indicating memory file access.
var memoryPathTokens = []string{
"MEMORY.md", "memory.md", "memory/",
}
// MaybeMemoryExecHint checks if a shell command targets memory files
// (which live in the database, not on disk) and returns an LLM hint.
// Returns empty string if no memory path detected.
func MaybeMemoryExecHint(command string) string {
trimmed := strings.TrimSpace(command)
if trimmed == "" {
return ""
}
// Check if command uses a filesystem read/list/search binary.
hasReadCmd := false
for _, prefix := range memoryHintPrefixes {
if strings.HasPrefix(trimmed, prefix) {
hasReadCmd = true
break
}
}
if !hasReadCmd {
return ""
}
// Check if the command references a memory path.
for _, token := range memoryPathTokens {
if strings.Contains(trimmed, token) {
return "[HINT] Memory files are in the database, not on disk. Use memory_search or memory_get tool instead."
}
}
return ""
}
+1 -1
View File
@@ -139,7 +139,7 @@ func (t *ReadFileTool) Execute(ctx context.Context, args map[string]any) *Result
if content == "" {
return SilentResult(fmt.Sprintf("(memory file %s does not exist yet — it will be created when memory is saved)", path))
}
return SilentResult(content)
return SilentResult(content + "\n\n[Source: database, not filesystem]")
}
}
+1 -1
View File
@@ -73,7 +73,7 @@ func (t *ListFilesTool) Execute(ctx context.Context, args map[string]any) *Resul
if listing == "" {
return SilentResult("No memory files stored yet")
}
return SilentResult(listing)
return SilentResult(listing + "\n[Source: database, not filesystem]")
}
}
+5
View File
@@ -199,6 +199,11 @@ func (t *ExecTool) Execute(ctx context.Context, args map[string]any) *Result {
}
}
// Memory path hint: shell commands can't access DB-backed memory files.
if hint := MaybeMemoryExecHint(normalizedCommand); hint != "" {
return SilentResult(hint)
}
// Credentialed exec: if command matches a configured binary, use Direct Exec Mode.
// This bypasses approval (admin trust) and shell (security).
if cred, binary, cmdArgs := t.lookupCredentialedBinary(ctx, command); cred != nil {