Add SubagentStart hook (#22782)

# What

`SubagentStart` runs once when Codex creates a thread-spawned subagent,
before that child sends its first model request. Thread-spawned
subagents use `SubagentStart` instead of the normal root-agent
`SessionStart` hook.

Configured handlers match on the subagent `agent_type`, using the same
value passed to `spawn_agent`. When no agent type is specified, Codex
uses the default agent type.

Hook input includes the normal session-start fields plus:

- `agent_id`: the child thread id.
- `agent_type`: the resolved subagent type.

`SubagentStart` may return `hookSpecificOutput.additionalContext`. That
context is added to the child conversation before the first model
request.

# Lifecycle Scope

Only thread-spawned subagents run `SubagentStart`.

Internal/system subagents such as Review, Compact, MemoryConsolidation,
and Other do not run normal `SessionStart` hooks and do not run
`SubagentStart`. This avoids exposing synthetic matcher labels for
internal implementation paths.

Also the `SessionStart` hook no longer fires for subagents, this matches
behavior with other coding agents' implementation

# Stack

1. This PR: add `SubagentStart`.
2. #22873: add `SubagentStop`.
3. #22882: add subagent identity to normal hook inputs.
This commit is contained in:
Abhinav
2026-05-19 12:45:08 -07:00
committed by GitHub
parent d269aa2af9
commit d661ab70ed
43 changed files with 716 additions and 44 deletions
+8 -1
View File
@@ -45,6 +45,8 @@ pub struct HookEventsToml {
pub session_start: Vec<MatcherGroup>,
#[serde(rename = "UserPromptSubmit", default)]
pub user_prompt_submit: Vec<MatcherGroup>,
#[serde(rename = "SubagentStart", default)]
pub subagent_start: Vec<MatcherGroup>,
#[serde(rename = "Stop", default)]
pub stop: Vec<MatcherGroup>,
}
@@ -59,6 +61,7 @@ impl HookEventsToml {
post_compact,
session_start,
user_prompt_submit,
subagent_start,
stop,
} = self;
pre_tool_use.is_empty()
@@ -68,6 +71,7 @@ impl HookEventsToml {
&& post_compact.is_empty()
&& session_start.is_empty()
&& user_prompt_submit.is_empty()
&& subagent_start.is_empty()
&& stop.is_empty()
}
@@ -80,6 +84,7 @@ impl HookEventsToml {
post_compact,
session_start,
user_prompt_submit,
subagent_start,
stop,
} = self;
[
@@ -90,6 +95,7 @@ impl HookEventsToml {
post_compact,
session_start,
user_prompt_submit,
subagent_start,
stop,
]
.into_iter()
@@ -98,7 +104,7 @@ impl HookEventsToml {
.sum()
}
pub fn into_matcher_groups(self) -> [(HookEventName, Vec<MatcherGroup>); 8] {
pub fn into_matcher_groups(self) -> [(HookEventName, Vec<MatcherGroup>); 9] {
[
(HookEventName::PreToolUse, self.pre_tool_use),
(HookEventName::PermissionRequest, self.permission_request),
@@ -107,6 +113,7 @@ impl HookEventsToml {
(HookEventName::PostCompact, self.post_compact),
(HookEventName::SessionStart, self.session_start),
(HookEventName::UserPromptSubmit, self.user_prompt_submit),
(HookEventName::SubagentStart, self.subagent_start),
(HookEventName::Stop, self.stop),
]
}