Context
This is the hook-specific portion of #2126, extracted from #2132. It depends on the canonical tool execution API in #2138.
Feature Request
Replace universal AgentHook::on_event(StepEvent) -> Flow control with one hook method, event payload, and action type for each lifecycle event.
The current API lets every Flow action compile for every StepEvent, even when an action is meaningless for that event. Composition rules are also hidden: completion patches accumulate, argument and presentation rewrites chain, terminal actions short-circuit, and observe-only events should not expose steering actions.
Required design
Lifecycle methods should cover:
- before a completion request;
- after a non-streaming completion response;
- after an accepted model turn;
- invalid tool calls;
- before tool execution;
- after tool execution;
- streaming text/tool-call deltas;
- streaming response completion.
Every method receives a run-scoped HookContext with run ID, one-based turn, streaming flag, optional agent name, and shared typed Scratchpad.
Use event-specific actions:
CompletionCallAction: continue, per-turn RequestPatch, stop;
ToolCallAction: run, rewrite arguments, skip, stop;
ToolResultAction: keep presentation, rewrite presentation, stop;
InvalidToolCallAction: fail, retry, repair, skip, stop;
ObservationAction: continue, stop.
Invalid-call deferral must be an explicit documented action/stack behavior, not an undocumented Option<Action>. Use one canonical public spelling per event; do not add aliases such as both CompletionCall and CompletionCallEvent.
HookStack contract
Hooks execute in registration order.
RequestPatch merging:
- append extra context;
- shallow-merge object additional parameters;
- intersect active tools;
- last writer wins for scalar fields/history, with a warning;
- never persist a patch into later turns.
Tool calls thread rewrites into later hooks; Skip and Stop are terminal. Tool results thread presentation rewrites into later hooks while keeping raw ToolResult and ToolContext immutable. Nested stacks must preserve the same merge/chaining behavior. Observe-only Stop short-circuits.
Scope
Replace StepEvent, Flow, and on_event; add typed events/actions; update HookStack, blocking runner, streaming path, exports, examples, tests, and API docs; delete old names and aliases in the same PR.
Non-goals
Dependencies
Depends on #2138. It does not depend on registry-snapshot or MCP-refresh work.
Acceptance criteria
Tests
Add focused tests for every patch merge rule, conflicts, active-tool intersections, chained/nested rewrites, rewrite-before-skip/stop, raw-result immutability, every invalid-call action, observe-only stop, and streaming event-interest aggregation. Add paired run/stream agent tests for patches, rewrites, skips, result redaction, invalid calls, and stop actions.
Context
This is the hook-specific portion of #2126, extracted from #2132. It depends on the canonical tool execution API in #2138.
Feature Request
Replace universal
AgentHook::on_event(StepEvent) -> Flowcontrol with one hook method, event payload, and action type for each lifecycle event.The current API lets every Flow action compile for every StepEvent, even when an action is meaningless for that event. Composition rules are also hidden: completion patches accumulate, argument and presentation rewrites chain, terminal actions short-circuit, and observe-only events should not expose steering actions.
Required design
Lifecycle methods should cover:
Every method receives a run-scoped
HookContextwith run ID, one-based turn, streaming flag, optional agent name, and shared typed Scratchpad.Use event-specific actions:
CompletionCallAction: continue, per-turn RequestPatch, stop;ToolCallAction: run, rewrite arguments, skip, stop;ToolResultAction: keep presentation, rewrite presentation, stop;InvalidToolCallAction: fail, retry, repair, skip, stop;ObservationAction: continue, stop.Invalid-call deferral must be an explicit documented action/stack behavior, not an undocumented
Option<Action>. Use one canonical public spelling per event; do not add aliases such as both CompletionCall and CompletionCallEvent.HookStack contract
Hooks execute in registration order.
RequestPatch merging:
Tool calls thread rewrites into later hooks; Skip and Stop are terminal. Tool results thread presentation rewrites into later hooks while keeping raw ToolResult and ToolContext immutable. Nested stacks must preserve the same merge/chaining behavior. Observe-only Stop short-circuits.
Scope
Replace StepEvent, Flow, and on_event; add typed events/actions; update HookStack, blocking runner, streaming path, exports, examples, tests, and API docs; delete old names and aliases in the same PR.
Non-goals
Dependencies
Depends on #2138. It does not depend on registry-snapshot or MCP-refresh work.
Acceptance criteria
Tests
Add focused tests for every patch merge rule, conflicts, active-tool intersections, chained/nested rewrites, rewrite-before-skip/stop, raw-result immutability, every invalid-call action, observe-only stop, and streaming event-interest aggregation. Add paired run/stream agent tests for patches, rewrites, skips, result redaction, invalid calls, and stop actions.