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

View File

@@ -11,6 +11,7 @@
"PostCompact",
"SessionStart",
"UserPromptSubmit",
"SubagentStart",
"Stop"
],
"type": "string"

View File

@@ -17,6 +17,7 @@
"PostCompact",
"SessionStart",
"UserPromptSubmit",
"SubagentStart",
"Stop"
],
"type": "string"

View File

@@ -11,6 +11,7 @@
"PostCompact",
"SessionStart",
"UserPromptSubmit",
"SubagentStart",
"Stop"
],
"type": "string"

View File

@@ -11,6 +11,7 @@
"PostCompact",
"SessionStart",
"UserPromptSubmit",
"SubagentStart",
"Stop"
],
"type": "string"

View File

@@ -0,0 +1,63 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"additionalProperties": false,
"definitions": {
"NullableString": {
"type": [
"string",
"null"
]
}
},
"properties": {
"agent_id": {
"type": "string"
},
"agent_type": {
"type": "string"
},
"cwd": {
"type": "string"
},
"hook_event_name": {
"const": "SubagentStart",
"type": "string"
},
"model": {
"type": "string"
},
"permission_mode": {
"enum": [
"default",
"acceptEdits",
"plan",
"dontAsk",
"bypassPermissions"
],
"type": "string"
},
"session_id": {
"type": "string"
},
"transcript_path": {
"$ref": "#/definitions/NullableString"
},
"turn_id": {
"description": "Codex extension: expose the active turn id to internal turn-scoped hooks.",
"type": "string"
}
},
"required": [
"agent_id",
"agent_type",
"cwd",
"hook_event_name",
"model",
"permission_mode",
"session_id",
"transcript_path",
"turn_id"
],
"title": "subagent-start.command.input",
"type": "object"
}

View File

@@ -0,0 +1,64 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"additionalProperties": false,
"definitions": {
"HookEventNameWire": {
"enum": [
"PreToolUse",
"PermissionRequest",
"PostToolUse",
"PreCompact",
"PostCompact",
"SessionStart",
"UserPromptSubmit",
"SubagentStart",
"Stop"
],
"type": "string"
},
"SessionStartHookSpecificOutputWire": {
"additionalProperties": false,
"properties": {
"additionalContext": {
"default": null,
"type": "string"
},
"hookEventName": {
"$ref": "#/definitions/HookEventNameWire"
}
},
"required": [
"hookEventName"
],
"type": "object"
}
},
"properties": {
"continue": {
"default": true,
"type": "boolean"
},
"hookSpecificOutput": {
"allOf": [
{
"$ref": "#/definitions/SessionStartHookSpecificOutputWire"
}
],
"default": null
},
"stopReason": {
"default": null,
"type": "string"
},
"suppressOutput": {
"default": false,
"type": "boolean"
},
"systemMessage": {
"default": null,
"type": "string"
}
},
"title": "subagent-start.command.output",
"type": "object"
}

View File

@@ -17,6 +17,7 @@
"PostCompact",
"SessionStart",
"UserPromptSubmit",
"SubagentStart",
"Stop"
],
"type": "string"

View File

@@ -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

View File

@@ -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",
}
}

View File

@@ -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> {

View File

@@ -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");

View File

@@ -104,6 +104,7 @@ pub(crate) fn matcher_pattern_for_event(
| HookEventName::PermissionRequest
| HookEventName::PostToolUse
| HookEventName::SessionStart
| HookEventName::SubagentStart
| HookEventName::PreCompact
| HookEventName::PostCompact => matcher,
HookEventName::UserPromptSubmit | HookEventName::Stop => None,

View File

@@ -15,7 +15,9 @@ use crate::engine::ConfiguredHandler;
use crate::engine::command_runner::CommandRunResult;
use crate::engine::dispatcher;
use crate::engine::output_parser;
use crate::schema::NullableString;
use crate::schema::SessionStartCommandInput;
use crate::schema::SubagentStartCommandInput;
#[derive(Debug, Clone, Copy)]
pub enum SessionStartSource {
@@ -41,7 +43,35 @@ pub struct SessionStartRequest {
pub transcript_path: Option<PathBuf>,
pub model: String,
pub permission_mode: String,
pub source: SessionStartSource,
pub target: StartHookTarget,
}
#[derive(Debug, Clone)]
pub enum StartHookTarget {
SessionStart {
source: SessionStartSource,
},
SubagentStart {
turn_id: String,
agent_id: String,
agent_type: String,
},
}
impl StartHookTarget {
fn event_name(&self) -> HookEventName {
match self {
Self::SessionStart { .. } => HookEventName::SessionStart,
Self::SubagentStart { .. } => HookEventName::SubagentStart,
}
}
fn matcher_input(&self) -> &str {
match self {
Self::SessionStart { source } => source.as_str(),
Self::SubagentStart { agent_type, .. } => agent_type.as_str(),
}
}
}
#[derive(Debug)]
@@ -65,8 +95,8 @@ pub(crate) fn preview(
) -> Vec<HookRunSummary> {
dispatcher::select_handlers(
handlers,
HookEventName::SessionStart,
Some(request.source.as_str()),
request.target.event_name(),
Some(request.target.matcher_input()),
)
.into_iter()
.map(|handler| dispatcher::running_summary(&handler))
@@ -81,8 +111,8 @@ pub(crate) async fn run(
) -> SessionStartOutcome {
let matched = dispatcher::select_handlers(
handlers,
HookEventName::SessionStart,
Some(request.source.as_str()),
request.target.event_name(),
Some(request.target.matcher_input()),
);
if matched.is_empty() {
return SessionStartOutcome {
@@ -93,21 +123,58 @@ pub(crate) async fn run(
};
}
let input_json = match serde_json::to_string(&SessionStartCommandInput::new(
request.session_id.to_string(),
request.transcript_path.clone(),
request.cwd.display().to_string(),
request.model.clone(),
request.permission_mode.clone(),
request.source.as_str().to_string(),
)) {
Ok(input_json) => input_json,
Err(error) => {
return serialization_failure_outcome(common::serialization_failure_hook_events(
matched,
turn_id,
format!("failed to serialize session start hook input: {error}"),
));
let (input_json, turn_id) = match request.target {
StartHookTarget::SessionStart { source } => {
let input_json = match serde_json::to_string(&SessionStartCommandInput::new(
request.session_id.to_string(),
request.transcript_path.clone(),
request.cwd.display().to_string(),
request.model.clone(),
request.permission_mode.clone(),
source.as_str().to_string(),
)) {
Ok(input_json) => input_json,
Err(error) => {
return serialization_failure_outcome(
common::serialization_failure_hook_events(
matched,
turn_id,
format!("failed to serialize session start hook input: {error}"),
),
);
}
};
(input_json, turn_id)
}
StartHookTarget::SubagentStart {
turn_id: subagent_turn_id,
agent_id,
agent_type,
} => {
let input = SubagentStartCommandInput {
session_id: request.session_id.to_string(),
turn_id: subagent_turn_id.clone(),
transcript_path: NullableString::from_path(request.transcript_path.clone()),
cwd: request.cwd.display().to_string(),
hook_event_name: "SubagentStart".to_string(),
model: request.model.clone(),
permission_mode: request.permission_mode.clone(),
agent_id,
agent_type,
};
let input_json = match serde_json::to_string(&input) {
Ok(input_json) => input_json,
Err(error) => {
return serialization_failure_outcome(
common::serialization_failure_hook_events(
matched,
Some(subagent_turn_id),
format!("failed to serialize subagent start hook input: {error}"),
),
);
}
};
(input_json, Some(subagent_turn_id))
}
};
@@ -139,6 +206,12 @@ pub(crate) async fn run(
}
}
/// Interprets completed `SessionStart` and `SubagentStart` hook runs.
///
/// The two events have different input payloads but share most output
/// handling: hook JSON can emit warnings/context, invalid JSON-looking stdout
/// fails, and plain stdout becomes model context. Only `SessionStart` honors
/// `continue:false`; `SubagentStart` stays context-injection-only.
fn parse_completed(
handler: &ConfiguredHandler,
run_result: CommandRunResult,
@@ -162,8 +235,17 @@ fn parse_completed(
Some(0) => {
let trimmed_stdout = run_result.stdout.trim();
if trimmed_stdout.is_empty() {
} else if let Some(parsed) = output_parser::parse_session_start(&run_result.stdout)
{
} else if let Some(parsed) = match handler.event_name {
HookEventName::SessionStart => {
output_parser::parse_session_start(&run_result.stdout)
}
HookEventName::SubagentStart => {
output_parser::parse_subagent_start(&run_result.stdout)
}
event_name => {
panic!("expected start hook event, got {event_name:?}")
}
} {
if let Some(system_message) = parsed.universal.system_message {
entries.push(HookOutputEntry {
kind: HookOutputEntryKind::Warning,
@@ -178,7 +260,9 @@ fn parse_completed(
);
}
let _ = parsed.universal.suppress_output;
if !parsed.universal.continue_processing {
if handler.event_name == HookEventName::SessionStart
&& !parsed.universal.continue_processing
{
status = HookRunStatus::Stopped;
should_stop = true;
stop_reason = parsed.universal.stop_reason.clone();
@@ -189,12 +273,22 @@ fn parse_completed(
});
}
}
// Preserve plain-text context support without treating malformed JSON as context.
} else if output_parser::looks_like_json(&run_result.stdout) {
status = HookRunStatus::Failed;
entries.push(HookOutputEntry {
kind: HookOutputEntryKind::Error,
text: "hook returned invalid session start JSON output".to_string(),
text: match handler.event_name {
HookEventName::SessionStart => {
"hook returned invalid session start JSON output"
}
HookEventName::SubagentStart => {
"hook returned invalid subagent start JSON output"
}
event_name => {
panic!("expected start hook event, got {event_name:?}")
}
}
.to_string(),
});
} else {
let additional_context = trimmed_stdout.to_string();
@@ -354,9 +448,71 @@ mod tests {
);
}
#[test]
fn subagent_start_plain_stdout_becomes_model_context() {
let parsed = parse_completed(
&handler_for(HookEventName::SubagentStart),
run_result(Some(0), "hello from subagent hook\n", ""),
/*turn_id*/ Some("turn-1".to_string()),
);
assert_eq!(
parsed.data,
SessionStartHandlerData {
should_stop: false,
stop_reason: None,
additional_contexts_for_model: vec!["hello from subagent hook".to_string()],
}
);
assert_eq!(parsed.completed.turn_id.as_deref(), Some("turn-1"));
assert_eq!(parsed.completed.run.status, HookRunStatus::Completed);
assert_eq!(
parsed.completed.run.entries,
vec![HookOutputEntry {
kind: HookOutputEntryKind::Context,
text: "hello from subagent hook".to_string(),
}]
);
}
#[test]
fn subagent_start_continue_false_is_ignored() {
let parsed = parse_completed(
&handler_for(HookEventName::SubagentStart),
run_result(
Some(0),
r#"{"continue":false,"stopReason":"skip child","hookSpecificOutput":{"hookEventName":"SubagentStart","additionalContext":"child context"}}"#,
"",
),
/*turn_id*/ Some("turn-1".to_string()),
);
assert_eq!(
parsed.data,
SessionStartHandlerData {
should_stop: false,
stop_reason: None,
additional_contexts_for_model: vec!["child context".to_string()],
}
);
assert_eq!(parsed.completed.turn_id.as_deref(), Some("turn-1"));
assert_eq!(parsed.completed.run.status, HookRunStatus::Completed);
assert_eq!(
parsed.completed.run.entries,
vec![HookOutputEntry {
kind: HookOutputEntryKind::Context,
text: "child context".to_string(),
}]
);
}
fn handler() -> ConfiguredHandler {
handler_for(HookEventName::SessionStart)
}
fn handler_for(event_name: HookEventName) -> ConfiguredHandler {
ConfiguredHandler {
event_name: HookEventName::SessionStart,
event_name,
matcher: None,
command: "echo hook".to_string(),
timeout_sec: 600,

View File

@@ -15,7 +15,7 @@ pub use declarations::PluginHookDeclaration;
pub use declarations::plugin_hook_declarations;
pub use engine::HookListEntry;
/// Hook event names as they appear in hooks JSON and config files.
pub const HOOK_EVENT_NAMES: [&str; 8] = [
pub const HOOK_EVENT_NAMES: [&str; 9] = [
"PreToolUse",
"PermissionRequest",
"PostToolUse",
@@ -23,6 +23,7 @@ pub const HOOK_EVENT_NAMES: [&str; 8] = [
"PostCompact",
"SessionStart",
"UserPromptSubmit",
"SubagentStart",
"Stop",
];
@@ -31,13 +32,14 @@ pub const HOOK_EVENT_NAMES: [&str; 8] = [
/// Other events can appear in hooks JSON, but Codex ignores their matcher
/// fields because those events do not dispatch against a tool, compaction
/// trigger, or session-start source.
pub const HOOK_EVENT_NAMES_WITH_MATCHERS: [&str; 6] = [
pub const HOOK_EVENT_NAMES_WITH_MATCHERS: [&str; 7] = [
"PreToolUse",
"PermissionRequest",
"PostToolUse",
"PreCompact",
"PostCompact",
"SessionStart",
"SubagentStart",
];
pub use events::compact::PostCompactRequest;
@@ -54,6 +56,7 @@ pub use events::pre_tool_use::PreToolUseRequest;
pub use events::session_start::SessionStartOutcome;
pub use events::session_start::SessionStartRequest;
pub use events::session_start::SessionStartSource;
pub use events::session_start::StartHookTarget;
pub use events::stop::StopOutcome;
pub use events::stop::StopRequest;
pub use events::user_prompt_submit::UserPromptSubmitOutcome;
@@ -83,6 +86,7 @@ pub fn hook_event_key_label(event_name: HookEventName) -> &'static str {
HookEventName::PostCompact => "post_compact",
HookEventName::SessionStart => "session_start",
HookEventName::UserPromptSubmit => "user_prompt_submit",
HookEventName::SubagentStart => "subagent_start",
HookEventName::Stop => "stop",
}
}

View File

@@ -27,6 +27,8 @@ const SESSION_START_INPUT_FIXTURE: &str = "session-start.command.input.schema.js
const SESSION_START_OUTPUT_FIXTURE: &str = "session-start.command.output.schema.json";
const USER_PROMPT_SUBMIT_INPUT_FIXTURE: &str = "user-prompt-submit.command.input.schema.json";
const USER_PROMPT_SUBMIT_OUTPUT_FIXTURE: &str = "user-prompt-submit.command.output.schema.json";
const SUBAGENT_START_INPUT_FIXTURE: &str = "subagent-start.command.input.schema.json";
const SUBAGENT_START_OUTPUT_FIXTURE: &str = "subagent-start.command.output.schema.json";
const STOP_INPUT_FIXTURE: &str = "stop.command.input.schema.json";
const STOP_OUTPUT_FIXTURE: &str = "stop.command.output.schema.json";
@@ -87,6 +89,8 @@ pub(crate) enum HookEventNameWire {
SessionStart,
#[serde(rename = "UserPromptSubmit")]
UserPromptSubmit,
#[serde(rename = "SubagentStart")]
SubagentStart,
#[serde(rename = "Stop")]
Stop,
}
@@ -345,6 +349,17 @@ pub(crate) struct SessionStartHookSpecificOutputWire {
pub additional_context: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
#[serde(deny_unknown_fields)]
#[schemars(rename = "subagent-start.command.output")]
pub(crate) struct SubagentStartCommandOutputWire {
#[serde(flatten)]
pub universal: HookUniversalOutputWire,
#[serde(default)]
pub hook_specific_output: Option<SessionStartHookSpecificOutputWire>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
#[serde(deny_unknown_fields)]
@@ -427,6 +442,24 @@ impl SessionStartCommandInput {
}
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
#[serde(deny_unknown_fields)]
#[schemars(rename = "subagent-start.command.input")]
pub(crate) struct SubagentStartCommandInput {
pub session_id: String,
/// Codex extension: expose the active turn id to internal turn-scoped hooks.
pub turn_id: String,
pub transcript_path: NullableString,
pub cwd: String,
#[schemars(schema_with = "subagent_start_hook_event_name_schema")]
pub hook_event_name: String,
pub model: String,
#[schemars(schema_with = "permission_mode_schema")]
pub permission_mode: String,
pub agent_id: String,
pub agent_type: String,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
#[serde(deny_unknown_fields)]
#[schemars(rename = "user-prompt-submit.command.input")]
@@ -522,6 +555,14 @@ pub fn write_schema_fixtures(schema_root: &Path) -> anyhow::Result<()> {
&generated_dir.join(USER_PROMPT_SUBMIT_OUTPUT_FIXTURE),
schema_json::<UserPromptSubmitCommandOutputWire>()?,
)?;
write_schema(
&generated_dir.join(SUBAGENT_START_INPUT_FIXTURE),
schema_json::<SubagentStartCommandInput>()?,
)?;
write_schema(
&generated_dir.join(SUBAGENT_START_OUTPUT_FIXTURE),
schema_json::<SubagentStartCommandOutputWire>()?,
)?;
write_schema(
&generated_dir.join(STOP_INPUT_FIXTURE),
schema_json::<StopCommandInput>()?,
@@ -613,6 +654,10 @@ fn user_prompt_submit_hook_event_name_schema(_gen: &mut SchemaGenerator) -> Sche
string_const_schema("UserPromptSubmit")
}
fn subagent_start_hook_event_name_schema(_gen: &mut SchemaGenerator) -> Schema {
string_const_schema("SubagentStart")
}
fn stop_hook_event_name_schema(_gen: &mut SchemaGenerator) -> Schema {
string_const_schema("Stop")
}
@@ -683,7 +728,10 @@ mod tests {
use super::SESSION_START_OUTPUT_FIXTURE;
use super::STOP_INPUT_FIXTURE;
use super::STOP_OUTPUT_FIXTURE;
use super::SUBAGENT_START_INPUT_FIXTURE;
use super::SUBAGENT_START_OUTPUT_FIXTURE;
use super::StopCommandInput;
use super::SubagentStartCommandInput;
use super::USER_PROMPT_SUBMIT_INPUT_FIXTURE;
use super::USER_PROMPT_SUBMIT_OUTPUT_FIXTURE;
use super::UserPromptSubmitCommandInput;
@@ -737,6 +785,12 @@ mod tests {
USER_PROMPT_SUBMIT_OUTPUT_FIXTURE => {
include_str!("../schema/generated/user-prompt-submit.command.output.schema.json")
}
SUBAGENT_START_INPUT_FIXTURE => {
include_str!("../schema/generated/subagent-start.command.input.schema.json")
}
SUBAGENT_START_OUTPUT_FIXTURE => {
include_str!("../schema/generated/subagent-start.command.output.schema.json")
}
STOP_INPUT_FIXTURE => {
include_str!("../schema/generated/stop.command.input.schema.json")
}
@@ -772,6 +826,8 @@ mod tests {
SESSION_START_OUTPUT_FIXTURE,
USER_PROMPT_SUBMIT_INPUT_FIXTURE,
USER_PROMPT_SUBMIT_OUTPUT_FIXTURE,
SUBAGENT_START_INPUT_FIXTURE,
SUBAGENT_START_OUTPUT_FIXTURE,
STOP_INPUT_FIXTURE,
STOP_OUTPUT_FIXTURE,
] {
@@ -814,6 +870,11 @@ mod tests {
.expect("serialize user prompt submit input schema"),
)
.expect("parse user prompt submit input schema");
let subagent_start: Value = serde_json::from_slice(
&schema_json::<SubagentStartCommandInput>()
.expect("serialize subagent start input schema"),
)
.expect("parse subagent start input schema");
let stop: Value = serde_json::from_slice(
&schema_json::<StopCommandInput>().expect("serialize stop input schema"),
)
@@ -826,6 +887,7 @@ mod tests {
&pre_compact,
&post_compact,
&user_prompt_submit,
&subagent_start,
&stop,
] {
assert_eq!(schema["properties"]["turn_id"]["type"], "string");