Summary
Add beforeToolCall and afterToolCall lifecycle hooks to the agent tool execution path, allowing user scripts to enforce tool policies, audit tool usage, or abort specific tool calls.
Problem
Agent tool calls (Bash, Read, Write, web search, etc.) execute with no user-configurable interception. There is no config-driven way to: block specific tools by name or pattern, log tool inputs/outputs to an audit trail, enforce tool-level rate limits, or alert on high-cost or sensitive tool calls.
Acceptance criteria
Implementation plan
- Locate the tool dispatch point —
src/agents/pi-tool-definition-adapter.ts (wraps tool calls) already has before-tool-call internal hooks; evaluate extending vs. adding alongside
- Add user-script hook calls before and after
tool.run() in the adapter
- Re-use
runCronHooks / loadHookEntries with workflow: 'tool'
- Add
ToolHooksConfig type with optional filter.tool: string[] field
- Add Zod schema and config loading
- Add unit tests
Files affected
src/cron/hooks.ts (reuse — may need filter.tool added to CronHookEntry)
src/config/types.hooks.ts (add ToolHooksConfig)
src/config/zod-schema.ts (Zod schema)
src/agents/pi-tool-definition-adapter.ts (wire hook call sites)
src/agents/pi-tool-definition-adapter.hooks.test.ts (new — tests)
Additional notes
src/agents/pi-tools.before-tool-call.ts already implements an internal before-tool-call hook (used for tool loop detection and param adjustment). This is a separate, user-configurable layer — do not replace the internal hook
- Implement last of the four: most granular, highest call frequency, needs careful performance consideration (hook overhead per tool call)
ctx should include: toolName, toolInput (sanitized), agentId, sessionKey, meta bag
- May require adding
filter.tool to the shared CronHookEntry / HookEntry type
Summary
Add
beforeToolCallandafterToolCalllifecycle hooks to the agent tool execution path, allowing user scripts to enforce tool policies, audit tool usage, or abort specific tool calls.Problem
Agent tool calls (Bash, Read, Write, web search, etc.) execute with no user-configurable interception. There is no config-driven way to: block specific tools by name or pattern, log tool inputs/outputs to an audit trail, enforce tool-level rate limits, or alert on high-cost or sensitive tool calls.
Acceptance criteria
beforeToolCallhook fires before a tool executes; returning{ abort: true }skips the call and returns an error result to the agentafterToolCallhook fires after tool completion with tool name, input, output summary, duration, and statusopenclaw.jsonwithfilter.tool(tool name allowlist/denylist) supportImplementation plan
src/agents/pi-tool-definition-adapter.ts(wraps tool calls) already hasbefore-tool-callinternal hooks; evaluate extending vs. adding alongsidetool.run()in the adapterrunCronHooks/loadHookEntrieswithworkflow: 'tool'ToolHooksConfigtype with optionalfilter.tool: string[]fieldFiles affected
src/cron/hooks.ts(reuse — may needfilter.tooladded toCronHookEntry)src/config/types.hooks.ts(add ToolHooksConfig)src/config/zod-schema.ts(Zod schema)src/agents/pi-tool-definition-adapter.ts(wire hook call sites)src/agents/pi-tool-definition-adapter.hooks.test.ts(new — tests)Additional notes
src/agents/pi-tools.before-tool-call.tsalready implements an internal before-tool-call hook (used for tool loop detection and param adjustment). This is a separate, user-configurable layer — do not replace the internal hookctxshould include:toolName,toolInput(sanitized),agentId,sessionKey,metabagfilter.toolto the sharedCronHookEntry/HookEntrytype