mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
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:
committed by
GitHub
Unverified
parent
d269aa2af9
commit
d661ab70ed
@@ -49,6 +49,7 @@ pub(crate) fn select_handlers_for_matcher_inputs(
|
||||
| HookEventName::PermissionRequest
|
||||
| HookEventName::PostToolUse
|
||||
| HookEventName::SessionStart
|
||||
| HookEventName::SubagentStart
|
||||
| HookEventName::PreCompact
|
||||
| HookEventName::PostCompact => {
|
||||
if matcher_inputs.is_empty() {
|
||||
@@ -139,7 +140,7 @@ pub(crate) fn completed_summary(
|
||||
|
||||
fn scope_for_event(event_name: HookEventName) -> HookScope {
|
||||
match event_name {
|
||||
HookEventName::SessionStart => HookScope::Thread,
|
||||
HookEventName::SessionStart | HookEventName::SubagentStart => HookScope::Thread,
|
||||
HookEventName::PreToolUse
|
||||
| HookEventName::PermissionRequest
|
||||
| HookEventName::PostToolUse
|
||||
|
||||
@@ -70,6 +70,7 @@ impl ConfiguredHandler {
|
||||
codex_protocol::protocol::HookEventName::PostCompact => "post-compact",
|
||||
codex_protocol::protocol::HookEventName::SessionStart => "session-start",
|
||||
codex_protocol::protocol::HookEventName::UserPromptSubmit => "user-prompt-submit",
|
||||
codex_protocol::protocol::HookEventName::SubagentStart => "subagent-start",
|
||||
codex_protocol::protocol::HookEventName::Stop => "stop",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,17 +86,34 @@ use crate::schema::PreToolUseDecisionWire;
|
||||
use crate::schema::PreToolUsePermissionDecisionWire;
|
||||
use crate::schema::SessionStartCommandOutputWire;
|
||||
use crate::schema::StopCommandOutputWire;
|
||||
use crate::schema::SubagentStartCommandOutputWire;
|
||||
use crate::schema::UserPromptSubmitCommandOutputWire;
|
||||
|
||||
pub(crate) fn parse_session_start(stdout: &str) -> Option<SessionStartOutput> {
|
||||
let wire: SessionStartCommandOutputWire = parse_json(stdout)?;
|
||||
let additional_context = wire
|
||||
.hook_specific_output
|
||||
.and_then(|output| output.additional_context);
|
||||
Some(SessionStartOutput {
|
||||
universal: UniversalOutput::from(wire.universal),
|
||||
Some(session_start_output(
|
||||
wire.universal,
|
||||
wire.hook_specific_output,
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) fn parse_subagent_start(stdout: &str) -> Option<SessionStartOutput> {
|
||||
let wire: SubagentStartCommandOutputWire = parse_json(stdout)?;
|
||||
Some(session_start_output(
|
||||
wire.universal,
|
||||
wire.hook_specific_output,
|
||||
))
|
||||
}
|
||||
|
||||
fn session_start_output(
|
||||
universal: HookUniversalOutputWire,
|
||||
hook_specific_output: Option<crate::schema::SessionStartHookSpecificOutputWire>,
|
||||
) -> SessionStartOutput {
|
||||
let additional_context = hook_specific_output.and_then(|output| output.additional_context);
|
||||
SessionStartOutput {
|
||||
universal: UniversalOutput::from(universal),
|
||||
additional_context,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn parse_pre_tool_use(stdout: &str) -> Option<PreToolUseOutput> {
|
||||
|
||||
@@ -16,6 +16,8 @@ pub(crate) struct GeneratedHookSchemas {
|
||||
pub pre_compact_command_output: Value,
|
||||
pub session_start_command_input: Value,
|
||||
pub session_start_command_output: Value,
|
||||
pub subagent_start_command_input: Value,
|
||||
pub subagent_start_command_output: Value,
|
||||
pub user_prompt_submit_command_input: Value,
|
||||
pub user_prompt_submit_command_output: Value,
|
||||
pub stop_command_input: Value,
|
||||
@@ -73,6 +75,14 @@ pub(crate) fn generated_hook_schemas() -> &'static GeneratedHookSchemas {
|
||||
"session-start.command.output",
|
||||
include_str!("../../schema/generated/session-start.command.output.schema.json"),
|
||||
),
|
||||
subagent_start_command_input: parse_json_schema(
|
||||
"subagent-start.command.input",
|
||||
include_str!("../../schema/generated/subagent-start.command.input.schema.json"),
|
||||
),
|
||||
subagent_start_command_output: parse_json_schema(
|
||||
"subagent-start.command.output",
|
||||
include_str!("../../schema/generated/subagent-start.command.output.schema.json"),
|
||||
),
|
||||
user_prompt_submit_command_input: parse_json_schema(
|
||||
"user-prompt-submit.command.input",
|
||||
include_str!("../../schema/generated/user-prompt-submit.command.input.schema.json"),
|
||||
@@ -118,6 +128,8 @@ mod tests {
|
||||
assert_eq!(schemas.pre_compact_command_output["type"], "object");
|
||||
assert_eq!(schemas.session_start_command_input["type"], "object");
|
||||
assert_eq!(schemas.session_start_command_output["type"], "object");
|
||||
assert_eq!(schemas.subagent_start_command_input["type"], "object");
|
||||
assert_eq!(schemas.subagent_start_command_output["type"], "object");
|
||||
assert_eq!(schemas.user_prompt_submit_command_input["type"], "object");
|
||||
assert_eq!(schemas.user_prompt_submit_command_output["type"], "object");
|
||||
assert_eq!(schemas.stop_command_input["type"], "object");
|
||||
|
||||
Reference in New Issue
Block a user