Summary
The hooks guide (docs/en/customization/hooks.md) documents the base stdin payload a hook receives, but never documents the per-event additional fields. The doc itself promises them — and the doc's own example relies on one — yet the field names appear nowhere in the docs, only in the engine source. A hook author has to read packages/agent-core to discover the JSON keys for each event.
(Filed as a documentation issue; runtime fields like version/plan/model/platform are N/A. Suggested label: documentation.)
Where the docs fall short
docs/en/customization/hooks.md, "Event Data Format" (lines 57–69) shows only the base payload:
{
"hook_event_name": "PreToolUse",
"session_id": "session_abc",
"cwd": "/path/to/project"
}
and then states (line 69):
Specific events will also include additional fields (such as tool name and command content); see the event reference below. All field names use snake_case.
But the "Event Reference" table (lines ~92–114) only documents what the matcher matches per event — it never lists the JSON keys each event adds to the payload. So "see the event reference below" points at a table that doesn't contain the promised fields. The only place a concrete field surfaces is the worked example at line 137, which reads payload.tool_input?.command — a field that is otherwise undocumented.
Why it matters
I hit this building a cross-platform MCP/hook adapter that has to write hooks targeting many CLIs. Because the per-event keys aren't documented, the field names have to be reverse-engineered from the engine; an integrator can easily reach for the wrong key (e.g. assuming UserPromptSubmit's prompt is a plain string, or guessing tool_response instead of tool_output for PostToolUse) and get a silent no-op with no diagnostic. Documenting the schema makes hooks usable without reading agent-core.
The actual fields (verified against main)
All keys are emitted camelCase in source and snake_cased by toHookInputData/camelToSnake (packages/agent-core/src/session/hooks/engine.ts:176-184). Per-event keys, with their trigger sites:
| Event |
Added stdin fields |
Source |
PreToolUse, PermissionRequest |
tool_name, tool_input, tool_call_id |
permission/policies/pre-tool-call-hook.ts:15-17; permission/index.ts:139-144 |
PostToolUse |
tool_name, tool_input, tool_call_id, tool_output (truncated to 2000 chars) |
agent/turn/index.ts:735-739 |
PostToolUseFailure |
tool_name, tool_input, tool_call_id, error (an object: code/message/name/details/retryable) |
agent/turn/index.ts:735-738 |
PermissionResult |
tool_name, tool_input, tool_call_id, result |
permission/index.ts:170-219 |
UserPromptSubmit |
prompt — a ContentPart[] array (e.g. [{ "type": "text", "text": "..." }]), not a plain string |
agent/turn/index.ts:568-571 |
SubagentStart |
agent_name, prompt (preview-truncated) |
session/subagent-host.ts:381-386 |
SubagentStop |
agent_name, response (preview-truncated) |
session/subagent-host.ts:392-396 |
StopFailure |
error_type, error_message |
agent/turn/index.ts:484-486 |
Interrupt |
reason |
agent/turn/index.ts:524-525 |
SessionStart |
source ("startup" | "resume") |
session/index.ts:685-686 |
SessionEnd |
reason ("exit") |
session/index.ts (triggerSessionEnd("exit")) |
PreCompact |
trigger ("manual" | "auto"), token_count |
agent/compaction/full.ts:373-378 |
PostCompact |
trigger, estimated_token_count |
agent/compaction/full.ts:388-392 |
Notification |
notification_type, title, body, severity, source_kind, source_id, sink |
agent/background/index.ts:130-140 |
(The doc already correctly notes the snake_case convention, so only the per-event field list is missing.)
Two fields are especially easy to get wrong and worth calling out explicitly in the docs:
UserPromptSubmit.prompt is a ContentPart[] array, not a string.
PostToolUse emits tool_output; the failure variant PostToolUseFailure emits error instead (and no tool_output).
Related
Issue #345 ("PreToolUse hooks: input mutation via updatedInput") is about the same hook output surface — a clearer payload reference would complement that discussion. PR #578 also touches this file (anchor-link fix only), so a small rebase awareness is worth noting if both land.
Proposed fix
Add a "Per-event payload fields" subsection (or a column on the existing Event Reference table) to docs/en/customization/hooks.md listing the keys above, and mirror it in docs/zh/customization/hooks.md. Docs-only, so no changeset/tests per CONTRIBUTING.
I'm happy to open the PR (title docs(hooks): document per-event stdin payload fields) if a maintainer confirms the table format you'd prefer (separate subsection vs. an extra "Payload fields" column on the existing table).
Summary
The hooks guide (
docs/en/customization/hooks.md) documents the base stdin payload a hook receives, but never documents the per-event additional fields. The doc itself promises them — and the doc's own example relies on one — yet the field names appear nowhere in the docs, only in the engine source. A hook author has to readpackages/agent-coreto discover the JSON keys for each event.(Filed as a documentation issue; runtime fields like version/plan/model/platform are N/A. Suggested label:
documentation.)Where the docs fall short
docs/en/customization/hooks.md, "Event Data Format" (lines 57–69) shows only the base payload:{ "hook_event_name": "PreToolUse", "session_id": "session_abc", "cwd": "/path/to/project" }and then states (line 69):
But the "Event Reference" table (lines ~92–114) only documents what the matcher matches per event — it never lists the JSON keys each event adds to the payload. So "see the event reference below" points at a table that doesn't contain the promised fields. The only place a concrete field surfaces is the worked example at line 137, which reads
payload.tool_input?.command— a field that is otherwise undocumented.Why it matters
I hit this building a cross-platform MCP/hook adapter that has to write hooks targeting many CLIs. Because the per-event keys aren't documented, the field names have to be reverse-engineered from the engine; an integrator can easily reach for the wrong key (e.g. assuming
UserPromptSubmit'spromptis a plain string, or guessingtool_responseinstead oftool_outputforPostToolUse) and get a silent no-op with no diagnostic. Documenting the schema makes hooks usable without readingagent-core.The actual fields (verified against
main)All keys are emitted camelCase in source and snake_cased by
toHookInputData/camelToSnake(packages/agent-core/src/session/hooks/engine.ts:176-184). Per-event keys, with their trigger sites:PreToolUse,PermissionRequesttool_name,tool_input,tool_call_idpermission/policies/pre-tool-call-hook.ts:15-17;permission/index.ts:139-144PostToolUsetool_name,tool_input,tool_call_id,tool_output(truncated to 2000 chars)agent/turn/index.ts:735-739PostToolUseFailuretool_name,tool_input,tool_call_id,error(an object: code/message/name/details/retryable)agent/turn/index.ts:735-738PermissionResulttool_name,tool_input,tool_call_id,resultpermission/index.ts:170-219UserPromptSubmitprompt— aContentPart[]array (e.g.[{ "type": "text", "text": "..." }]), not a plain stringagent/turn/index.ts:568-571SubagentStartagent_name,prompt(preview-truncated)session/subagent-host.ts:381-386SubagentStopagent_name,response(preview-truncated)session/subagent-host.ts:392-396StopFailureerror_type,error_messageagent/turn/index.ts:484-486Interruptreasonagent/turn/index.ts:524-525SessionStartsource("startup"|"resume")session/index.ts:685-686SessionEndreason("exit")session/index.ts(triggerSessionEnd("exit"))PreCompacttrigger("manual"|"auto"),token_countagent/compaction/full.ts:373-378PostCompacttrigger,estimated_token_countagent/compaction/full.ts:388-392Notificationnotification_type,title,body,severity,source_kind,source_id,sinkagent/background/index.ts:130-140(The doc already correctly notes the snake_case convention, so only the per-event field list is missing.)
Two fields are especially easy to get wrong and worth calling out explicitly in the docs:
UserPromptSubmit.promptis aContentPart[]array, not a string.PostToolUseemitstool_output; the failure variantPostToolUseFailureemitserrorinstead (and notool_output).Related
Issue #345 ("PreToolUse hooks: input mutation via
updatedInput") is about the same hook output surface — a clearer payload reference would complement that discussion. PR #578 also touches this file (anchor-link fix only), so a small rebase awareness is worth noting if both land.Proposed fix
Add a "Per-event payload fields" subsection (or a column on the existing Event Reference table) to
docs/en/customization/hooks.mdlisting the keys above, and mirror it indocs/zh/customization/hooks.md. Docs-only, so no changeset/tests per CONTRIBUTING.I'm happy to open the PR (title
docs(hooks): document per-event stdin payload fields) if a maintainer confirms the table format you'd prefer (separate subsection vs. an extra "Payload fields" column on the existing table).