You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(hooks): add 4 new hook events to match Claude Code / OpenCode / pi
Adds the four hook events that all three competitor coding agents
expose but docker-agent did not:
- user_prompt_submit: fires once per user message, after submission and
before the first model call. Can block the prompt or contribute
transient additional_context. Skipped for sub-sessions whose
kick-off message is synthesised by the runtime.
- pre_compact: fires before context-window compaction (manual / auto /
overflow / tool_overflow trigger). Can cancel compaction or append
guidance to the compaction prompt.
- subagent_stop: fires when a sub-agent (transfer_task, background
agent, skill sub-session) finishes. Runs against the parent's hooks
executor so handlers placed on the orchestrator see every child.
- permission_request: fires just before the runtime would prompt the
user to approve a tool call. Hooks can short-circuit the prompt by
returning permission_decision=allow|deny, mirroring pre_tool_use.
Also fixes the post_tool_use documentation everywhere (Go doc, schema,
docs/, example) to state that it fires on both success and failure;
tool_response.is_error distinguishes the two.
Adds five contract-widening tests pinning the new events' wire-format
contract.
Assisted-By: docker-agent
Copy file name to clipboardExpand all lines: agent-schema.json
+29-1Lines changed: 29 additions & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -523,7 +523,14 @@
523
523
},
524
524
"post_tool_use": {
525
525
"type": "array",
526
-
"description": "Hooks that run after a tool completes. Can provide validation or additional context.",
526
+
"description": "Hooks that run after a tool completes (both success and failure). The result is delivered in tool_response (failed calls carry an is_error flag and any error text). Returning decision=block or exit code 2 stops the run loop after the current tool batch — useful for circuit-breaker patterns.",
527
+
"items": {
528
+
"$ref": "#/definitions/HookMatcherConfig"
529
+
}
530
+
},
531
+
"permission_request": {
532
+
"type": "array",
533
+
"description": "Hooks that run just before the runtime prompts the user to approve a tool call (i.e. when --yolo and permission rules did not short-circuit the decision). Hooks may auto-allow or auto-deny via hook_specific_output.permission_decision; otherwise control falls through to the interactive confirmation. Tool-matched, like pre_tool_use.",
527
534
"items": {
528
535
"$ref": "#/definitions/HookMatcherConfig"
529
536
}
@@ -535,6 +542,13 @@
535
542
"$ref": "#/definitions/HookDefinition"
536
543
}
537
544
},
545
+
"user_prompt_submit": {
546
+
"type": "array",
547
+
"description": "Hooks that run once per user message, after the user has submitted their prompt and before the first model call of the turn. The submitted text is passed in the prompt field. Hooks can block submission (decision=block / continue=false / exit code 2) or contribute additional_context that is spliced into the conversation as a transient system message for that turn only. Sub-sessions (transferred tasks, background agents) do NOT fire this event because their kick-off message is synthesised by the runtime.",
548
+
"items": {
549
+
"$ref": "#/definitions/HookDefinition"
550
+
}
551
+
},
538
552
"turn_start": {
539
553
"type": "array",
540
554
"description": "Hooks that run at the start of every agent turn (each model call). Their AdditionalContext is appended as transient system messages for that turn only — it is NOT persisted to the session, so per-turn signals (date, prompt files) are recomputed every turn instead of bloating message history on every resume.",
@@ -563,6 +577,20 @@
563
577
"$ref": "#/definitions/HookDefinition"
564
578
}
565
579
},
580
+
"pre_compact": {
581
+
"type": "array",
582
+
"description": "Hooks that run just before the runtime compacts the session transcript into a summary. The trigger is reported in source: 'manual' (user-initiated /compact), 'auto' (proactive threshold), 'overflow' (context-overflow recovery), or 'tool_overflow' (proactive after tool results pushed past the threshold). Hooks may block compaction (decision=block / continue=false / exit code 2) or contribute additional_context that is appended to the compaction prompt — useful for steering the summary without modifying the agent's instruction.",
583
+
"items": {
584
+
"$ref": "#/definitions/HookDefinition"
585
+
}
586
+
},
587
+
"subagent_stop": {
588
+
"type": "array",
589
+
"description": "Hooks that run when a sub-agent (transferred task, background agent, skill sub-session) finishes. Fires against the parent agent's executor so handlers configured on the orchestrator see every child completion. The sub-agent's name is in agent_name and its final assistant message in stop_response.",
590
+
"items": {
591
+
"$ref": "#/definitions/HookDefinition"
592
+
}
593
+
},
566
594
"on_user_input": {
567
595
"type": "array",
568
596
"description": "Hooks that run when the agent needs user input. Can send notifications or log events.",
The `source` field for `session_start` can be: `startup`, `resume`, `clear`, or `compact`.
208
+
The `source` field for `pre_compact` can be: `manual`(user-initiated `/compact`), `auto` (proactive threshold), `overflow` (context-overflow recovery), or `tool_overflow` (proactive recovery after tool results pushed past the threshold).
197
209
198
210
The `reason` field for `session_end` can be: `clear`, `logout`, `prompt_input_exit`, or `other`.
199
211
200
-
The `stop_response` field contains the model's final text response.
212
+
The `prompt` field for `user_prompt_submit` is the text the user just submitted. Sub-sessions (transferred tasks, background agents, skills) do **not** fire this event because their kick-off message is synthesised by the runtime, not authored by the user.
213
+
214
+
The `agent_name` field for `subagent_stop` is the name of the sub-agent that just finished; `parent_session_id` is the session that spawned it.
215
+
216
+
The `stop_response` field contains the model's final text response (for both `stop` and `subagent_stop`).
201
217
202
218
The `notification_level` field can be: `error`or `warning`.
203
219
@@ -245,7 +261,7 @@ The `hook_specific_output` for `pre_tool_use` supports:
245
261
246
262
### Plain Text Output
247
263
248
-
For `session_start`, `post_tool_use`, and `stop` hooks, plain text written to stdout (i.e., output that is not valid JSON) is captured as additional context for the agent.
264
+
For `session_start`, `user_prompt_submit`, `post_tool_use`, `pre_compact`, and `stop` hooks, plain text written to stdout (i.e., output that is not valid JSON) is captured as additional context for the agent. For `pre_compact` it is appended to the compaction prompt; for the others it is spliced into the conversation as a (transient or persisted) system message depending on the event.
249
265
250
266
## Exit Codes
251
267
@@ -410,6 +426,51 @@ The `notification` hook fires when:
410
426
- A degenerate tool call loop is detected
411
427
- The maximum iteration limit is reached
412
428
429
+
### Pre-Compact: steer the summary
430
+
431
+
`pre_compact` fires just before the runtime compacts the session transcript. Its `source` field tells you why compaction was triggered:
432
+
433
+
- `manual`— the user invoked `/compact`
434
+
- `auto`— proactive compaction at the configured threshold
435
+
- `overflow`— emergency compaction after a context-overflow error
436
+
- `tool_overflow`— proactive compaction triggered by tool results pushing the estimated context past the threshold
437
+
438
+
Return `additional_context` (or plain stdout) to append guidance to the compaction prompt without modifying the agent's instruction. Block the event (`decision: block` / exit code 2) to cancel compaction — useful when you want to handle truncation yourself.
439
+
440
+
### User-Prompt-Submit: gate or enrich every user message
441
+
442
+
`user_prompt_submit` fires once per user message, after the prompt is recorded in the session and before the first model call. The submitted text is in `prompt`. Use it to:
- inject per-prompt context (`additional_context` is spliced as a transient system message for that turn),
446
+
- audit user prompts to a log.
447
+
448
+
It does **not** fire for sub-sessions (transferred tasks, background agents, skill sub-sessions) because their kick-off message is synthesised by the runtime.
449
+
450
+
### Subagent-Stop: observe handoff completions
451
+
452
+
`subagent_stop`fires whenever a sub-agent finishes — `transfer_task` returns, a background agent completes, or a skill sub-session ends. It runs against the *parent* agent's hooks executor, so handlers configured on the orchestrator see every child completion in one place. The sub-agent's name is in `agent_name`, the parent's session ID in `parent_session_id`, and the child's final assistant message in `stop_response`.
`permission_request` fires just before the runtime would prompt the user to approve a tool call (i.e. when neither `--yolo` nor a permissions rule short-circuited the decision and the tool is not read-only). Use the same `hook_specific_output.permission_decision` shape as `pre_tool_use` to auto-approve or auto-deny the call:
0 commit comments