memory_append.go
1 package tools 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "strings" 8 9 "github.com/Kocoro-lab/ShanClaw/internal/agent" 10 ctxwin "github.com/Kocoro-lab/ShanClaw/internal/context" 11 ) 12 13 // MemoryAppendTool appends entries to the agent's MEMORY.md via BoundedAppend, 14 // preventing concurrent sessions from clobbering each other's writes and 15 // overflowing to detail files when the line limit is reached. 16 // The memory directory is read from context (set by AgentLoop.Run). 17 type MemoryAppendTool struct{} 18 19 type memoryAppendArgs struct { 20 Content string `json:"content"` 21 } 22 23 func (t *MemoryAppendTool) Info() agent.ToolInfo { 24 return agent.ToolInfo{ 25 Name: "memory_append", 26 Description: "Append new entries to MEMORY.md. Use this instead of file_write or file_edit for memory updates. Writes are atomic, flock-protected, and auto-overflow to detail files when MEMORY.md exceeds the line limit.", 27 Parameters: map[string]any{ 28 "type": "object", 29 "properties": map[string]any{ 30 "content": map[string]any{ 31 "type": "string", 32 "description": "New entries to append (markdown bullet points)", 33 }, 34 }, 35 }, 36 Required: []string{"content"}, 37 } 38 } 39 40 func (t *MemoryAppendTool) Run(ctx context.Context, argsJSON string) (agent.ToolResult, error) { 41 var args memoryAppendArgs 42 if err := json.Unmarshal([]byte(argsJSON), &args); err != nil { 43 return agent.ToolResult{Content: fmt.Sprintf("invalid arguments: %v", err), IsError: true}, nil 44 } 45 46 if strings.TrimSpace(args.Content) == "" { 47 return agent.ToolResult{Content: "content must not be empty", IsError: true}, nil 48 } 49 50 memDir := agent.MemoryDirFromContext(ctx) 51 if memDir == "" { 52 return agent.ToolResult{Content: "memory not configured for this agent", IsError: true}, nil 53 } 54 55 if err := ctxwin.BoundedAppend(memDir, args.Content); err != nil { 56 return agent.ToolResult{Content: fmt.Sprintf("error appending: %v", err), IsError: true}, nil 57 } 58 59 return agent.ToolResult{Content: fmt.Sprintf("appended to %s/MEMORY.md", memDir)}, nil 60 } 61 62 func (t *MemoryAppendTool) RequiresApproval() bool { return false } 63 64 func (t *MemoryAppendTool) IsReadOnlyCall(string) bool { return false }