Add observation-only model request lifecycle hooks (#55144)

## What changed

- Add `PreModelRequest` and `PostModelRequest` hooks around model request attempts, including retries. Pass model, session, turn, and optional subagent context; report completion, failure, or cancellation with errors and token usage when available.
- Treat hook output as diagnostic data without allowing it to veto requests or alter model context. Wait for synchronous post-request hooks before dispatching tools, while asynchronous hooks allow streaming tool dispatch to continue.
- Apply memory-consolidation hook filtering and expose the new events through configuration, protocol schemas, generated clients, and the hooks browser.

## Testing

Add integration coverage for failures and retries, ignored control directives, cancellation, synchronous and asynchronous tool ordering, interrupted-turn resume and token accounting, and memory-consolidation filtering. Update hook schema fixtures and hooks browser snapshots.

GitOrigin-RevId: 6ed7b39fc8bb94cf467e350d07d97fcc729c21ce
This commit is contained in:
Vivian Fang
2026-10-18 22:43:56 +00:00
committed by copyberry
parent 8c076942cb
commit 70b3bb6d88
46 changed files with 1014 additions and 60 deletions
@@ -1984,6 +1984,8 @@ class HookErrorInfo(BaseModel):
class HookEventName(Enum):
pre_model_request = "preModelRequest"
post_model_request = "postModelRequest"
pre_tool_use = "preToolUse"
permission_request = "permissionRequest"
post_tool_use = "postToolUse"
@@ -2422,8 +2424,14 @@ class ManagedHooksRequirements(BaseModel):
list[ConfiguredHookMatcherGroup], Field(alias="PermissionRequest")
]
post_compact: Annotated[list[ConfiguredHookMatcherGroup], Field(alias="PostCompact")]
post_model_request: Annotated[
list[ConfiguredHookMatcherGroup] | None, Field(alias="PostModelRequest")
] = []
post_tool_use: Annotated[list[ConfiguredHookMatcherGroup], Field(alias="PostToolUse")]
pre_compact: Annotated[list[ConfiguredHookMatcherGroup], Field(alias="PreCompact")]
pre_model_request: Annotated[
list[ConfiguredHookMatcherGroup] | None, Field(alias="PreModelRequest")
] = []
pre_tool_use: Annotated[list[ConfiguredHookMatcherGroup], Field(alias="PreToolUse")]
session_end: Annotated[list[ConfiguredHookMatcherGroup] | None, Field(alias="SessionEnd")] = []
session_start: Annotated[list[ConfiguredHookMatcherGroup], Field(alias="SessionStart")]