// kv_delete removes a single entry by (scope, key). Missing rows // surface as the literal string "not_found" rather than an error so the // LLM can reason "did this row exist?" without wrapping the call in // error handling. package tools import ( "context" "errors" "fmt" "gitea.stevedudenhoeffer.com/steve/executus/tool" ) type kvDeleteArgs struct { Scope string `json:"scope" description:"Storage scope: 'skill', 'user:', 'run:', or 'root_run:'."` Key string `json:"key" description:"Key within the scope."` } // NewKVDelete constructs the kv_delete tool. storage nil → "not // configured" at execute time. func NewKVDelete(storage KVStorage) tool.Tool { return tool.NewGatedTool[kvDeleteArgs]( "kv_delete", "Remove an entry by (scope, key). Returns 'ok' on success or 'not_found' if no row matched.", tool.Permission{ AuthoringRequirement: tool.RequirementAnyone, OperatesOn: tool.ScopeCaller, SafeForShare: true, Categories: []string{"storage", "write"}, }, func(ctx context.Context, inv tool.Invocation, args kvDeleteArgs) (string, error) { if storage == nil { return "", fmt.Errorf("kv_delete: not configured") } if err := ValidateScope(inv, args.Scope, inv.CallerIsAdmin); err != nil { return "", fmt.Errorf("kv_delete: %w", err) } if args.Key == "" { return "", fmt.Errorf("kv_delete: key required") } if err := storage.KVDelete(ctx, kvPartition(inv, args.Scope), args.Scope, args.Key); err != nil { if errors.Is(err, ErrKVNotFound) { return "not_found", nil } return "", fmt.Errorf("kv_delete: %w", err) } return "ok", nil }, ) }