diff --git a/codex-rs/hooks/schema/generated/permission-request.command.output.schema.json b/codex-rs/hooks/schema/generated/permission-request.command.output.schema.json index 7d63b56c9..347820f7e 100644 --- a/codex-rs/hooks/schema/generated/permission-request.command.output.schema.json +++ b/codex-rs/hooks/schema/generated/permission-request.command.output.schema.json @@ -2,21 +2,6 @@ "$schema": "http://json-schema.org/draft-07/schema#", "additionalProperties": false, "definitions": { - "HookEventNameWire": { - "enum": [ - "PreToolUse", - "PermissionRequest", - "PostToolUse", - "PreCompact", - "PostCompact", - "SessionStart", - "UserPromptSubmit", - "SubagentStart", - "SubagentStop", - "Stop" - ], - "type": "string" - }, "PermissionRequestBehaviorWire": { "enum": [ "allow", @@ -65,7 +50,8 @@ "default": null }, "hookEventName": { - "$ref": "#/definitions/HookEventNameWire" + "const": "PermissionRequest", + "type": "string" } }, "required": [ diff --git a/codex-rs/hooks/schema/generated/post-tool-use.command.output.schema.json b/codex-rs/hooks/schema/generated/post-tool-use.command.output.schema.json index 964c3ba62..011f88726 100644 --- a/codex-rs/hooks/schema/generated/post-tool-use.command.output.schema.json +++ b/codex-rs/hooks/schema/generated/post-tool-use.command.output.schema.json @@ -8,21 +8,6 @@ ], "type": "string" }, - "HookEventNameWire": { - "enum": [ - "PreToolUse", - "PermissionRequest", - "PostToolUse", - "PreCompact", - "PostCompact", - "SessionStart", - "UserPromptSubmit", - "SubagentStart", - "SubagentStop", - "Stop" - ], - "type": "string" - }, "PostToolUseHookSpecificOutputWire": { "additionalProperties": false, "properties": { @@ -31,7 +16,8 @@ "type": "string" }, "hookEventName": { - "$ref": "#/definitions/HookEventNameWire" + "const": "PostToolUse", + "type": "string" }, "updatedMCPToolOutput": { "default": null diff --git a/codex-rs/hooks/schema/generated/pre-tool-use.command.output.schema.json b/codex-rs/hooks/schema/generated/pre-tool-use.command.output.schema.json index a74abd14e..6730b27fd 100644 --- a/codex-rs/hooks/schema/generated/pre-tool-use.command.output.schema.json +++ b/codex-rs/hooks/schema/generated/pre-tool-use.command.output.schema.json @@ -2,21 +2,6 @@ "$schema": "http://json-schema.org/draft-07/schema#", "additionalProperties": false, "definitions": { - "HookEventNameWire": { - "enum": [ - "PreToolUse", - "PermissionRequest", - "PostToolUse", - "PreCompact", - "PostCompact", - "SessionStart", - "UserPromptSubmit", - "SubagentStart", - "SubagentStop", - "Stop" - ], - "type": "string" - }, "PreToolUseDecisionWire": { "enum": [ "approve", @@ -32,7 +17,8 @@ "type": "string" }, "hookEventName": { - "$ref": "#/definitions/HookEventNameWire" + "const": "PreToolUse", + "type": "string" }, "permissionDecision": { "allOf": [ diff --git a/codex-rs/hooks/schema/generated/session-start.command.output.schema.json b/codex-rs/hooks/schema/generated/session-start.command.output.schema.json index e1abb88a5..1dd51e001 100644 --- a/codex-rs/hooks/schema/generated/session-start.command.output.schema.json +++ b/codex-rs/hooks/schema/generated/session-start.command.output.schema.json @@ -2,21 +2,6 @@ "$schema": "http://json-schema.org/draft-07/schema#", "additionalProperties": false, "definitions": { - "HookEventNameWire": { - "enum": [ - "PreToolUse", - "PermissionRequest", - "PostToolUse", - "PreCompact", - "PostCompact", - "SessionStart", - "UserPromptSubmit", - "SubagentStart", - "SubagentStop", - "Stop" - ], - "type": "string" - }, "SessionStartHookSpecificOutputWire": { "additionalProperties": false, "properties": { @@ -25,7 +10,8 @@ "type": "string" }, "hookEventName": { - "$ref": "#/definitions/HookEventNameWire" + "const": "SessionStart", + "type": "string" } }, "required": [ diff --git a/codex-rs/hooks/schema/generated/subagent-start.command.output.schema.json b/codex-rs/hooks/schema/generated/subagent-start.command.output.schema.json index 4e645bb91..02493da98 100644 --- a/codex-rs/hooks/schema/generated/subagent-start.command.output.schema.json +++ b/codex-rs/hooks/schema/generated/subagent-start.command.output.schema.json @@ -2,22 +2,7 @@ "$schema": "http://json-schema.org/draft-07/schema#", "additionalProperties": false, "definitions": { - "HookEventNameWire": { - "enum": [ - "PreToolUse", - "PermissionRequest", - "PostToolUse", - "PreCompact", - "PostCompact", - "SessionStart", - "UserPromptSubmit", - "SubagentStart", - "SubagentStop", - "Stop" - ], - "type": "string" - }, - "SessionStartHookSpecificOutputWire": { + "SubagentStartHookSpecificOutputWire": { "additionalProperties": false, "properties": { "additionalContext": { @@ -25,7 +10,8 @@ "type": "string" }, "hookEventName": { - "$ref": "#/definitions/HookEventNameWire" + "const": "SubagentStart", + "type": "string" } }, "required": [ @@ -42,7 +28,7 @@ "hookSpecificOutput": { "allOf": [ { - "$ref": "#/definitions/SessionStartHookSpecificOutputWire" + "$ref": "#/definitions/SubagentStartHookSpecificOutputWire" } ], "default": null diff --git a/codex-rs/hooks/schema/generated/user-prompt-submit.command.output.schema.json b/codex-rs/hooks/schema/generated/user-prompt-submit.command.output.schema.json index 61c530ce0..97875c108 100644 --- a/codex-rs/hooks/schema/generated/user-prompt-submit.command.output.schema.json +++ b/codex-rs/hooks/schema/generated/user-prompt-submit.command.output.schema.json @@ -8,21 +8,6 @@ ], "type": "string" }, - "HookEventNameWire": { - "enum": [ - "PreToolUse", - "PermissionRequest", - "PostToolUse", - "PreCompact", - "PostCompact", - "SessionStart", - "UserPromptSubmit", - "SubagentStart", - "SubagentStop", - "Stop" - ], - "type": "string" - }, "UserPromptSubmitHookSpecificOutputWire": { "additionalProperties": false, "properties": { @@ -31,7 +16,8 @@ "type": "string" }, "hookEventName": { - "$ref": "#/definitions/HookEventNameWire" + "const": "UserPromptSubmit", + "type": "string" } }, "required": [ diff --git a/codex-rs/hooks/src/engine/output_parser.rs b/codex-rs/hooks/src/engine/output_parser.rs index 11b44395c..b4f214146 100644 --- a/codex-rs/hooks/src/engine/output_parser.rs +++ b/codex-rs/hooks/src/engine/output_parser.rs @@ -94,7 +94,8 @@ pub(crate) fn parse_session_start(stdout: &str) -> Option { let wire: SessionStartCommandOutputWire = parse_json(stdout)?; Some(session_start_output( wire.universal, - wire.hook_specific_output, + wire.hook_specific_output + .and_then(|output| output.additional_context), )) } @@ -102,15 +103,15 @@ pub(crate) fn parse_subagent_start(stdout: &str) -> Option { let wire: SubagentStartCommandOutputWire = parse_json(stdout)?; Some(session_start_output( wire.universal, - wire.hook_specific_output, + wire.hook_specific_output + .and_then(|output| output.additional_context), )) } fn session_start_output( universal: HookUniversalOutputWire, - hook_specific_output: Option, + additional_context: Option, ) -> SessionStartOutput { - let additional_context = hook_specific_output.and_then(|output| output.additional_context); SessionStartOutput { universal: UniversalOutput::from(universal), additional_context, diff --git a/codex-rs/hooks/src/schema.rs b/codex-rs/hooks/src/schema.rs index bd2ac16df..d90d8a3e5 100644 --- a/codex-rs/hooks/src/schema.rs +++ b/codex-rs/hooks/src/schema.rs @@ -182,6 +182,7 @@ pub(crate) struct PostCompactCommandOutputWire { #[serde(rename_all = "camelCase")] #[serde(deny_unknown_fields)] pub(crate) struct PermissionRequestHookSpecificOutputWire { + #[schemars(schema_with = "permission_request_hook_event_name_schema")] pub hook_event_name: HookEventNameWire, #[serde(default)] pub decision: Option, @@ -223,6 +224,7 @@ pub(crate) enum PermissionRequestBehaviorWire { #[serde(rename_all = "camelCase")] #[serde(deny_unknown_fields)] pub(crate) struct PostToolUseHookSpecificOutputWire { + #[schemars(schema_with = "post_tool_use_hook_event_name_schema")] pub hook_event_name: HookEventNameWire, #[serde(default)] pub additional_context: Option, @@ -235,6 +237,7 @@ pub(crate) struct PostToolUseHookSpecificOutputWire { #[serde(rename_all = "camelCase")] #[serde(deny_unknown_fields)] pub(crate) struct PreToolUseHookSpecificOutputWire { + #[schemars(schema_with = "pre_tool_use_hook_event_name_schema")] pub hook_event_name: HookEventNameWire, #[serde(default)] pub permission_decision: Option, @@ -388,6 +391,7 @@ pub(crate) struct SessionStartCommandOutputWire { #[serde(rename_all = "camelCase")] #[serde(deny_unknown_fields)] pub(crate) struct SessionStartHookSpecificOutputWire { + #[schemars(schema_with = "session_start_hook_event_name_schema")] pub hook_event_name: HookEventNameWire, #[serde(default)] pub additional_context: Option, @@ -401,7 +405,17 @@ pub(crate) struct SubagentStartCommandOutputWire { #[serde(flatten)] pub universal: HookUniversalOutputWire, #[serde(default)] - pub hook_specific_output: Option, + pub hook_specific_output: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "camelCase")] +#[serde(deny_unknown_fields)] +pub(crate) struct SubagentStartHookSpecificOutputWire { + #[schemars(schema_with = "subagent_start_hook_event_name_schema")] + pub hook_event_name: HookEventNameWire, + #[serde(default)] + pub additional_context: Option, } #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] @@ -423,6 +437,7 @@ pub(crate) struct UserPromptSubmitCommandOutputWire { #[serde(rename_all = "camelCase")] #[serde(deny_unknown_fields)] pub(crate) struct UserPromptSubmitHookSpecificOutputWire { + #[schemars(schema_with = "user_prompt_submit_hook_event_name_schema")] pub hook_event_name: HookEventNameWire, #[serde(default)] pub additional_context: Option, @@ -817,10 +832,13 @@ mod tests { use super::PRE_TOOL_USE_INPUT_FIXTURE; use super::PRE_TOOL_USE_OUTPUT_FIXTURE; use super::PermissionRequestCommandInput; + use super::PermissionRequestCommandOutputWire; use super::PostCompactCommandInput; use super::PostToolUseCommandInput; + use super::PostToolUseCommandOutputWire; use super::PreCompactCommandInput; use super::PreToolUseCommandInput; + use super::PreToolUseCommandOutputWire; use super::SESSION_START_INPUT_FIXTURE; use super::SESSION_START_OUTPUT_FIXTURE; use super::STOP_INPUT_FIXTURE; @@ -829,17 +847,21 @@ mod tests { use super::SUBAGENT_START_OUTPUT_FIXTURE; use super::SUBAGENT_STOP_INPUT_FIXTURE; use super::SUBAGENT_STOP_OUTPUT_FIXTURE; + use super::SessionStartCommandOutputWire; use super::StopCommandInput; use super::SubagentCommandInputFields; use super::SubagentStartCommandInput; + use super::SubagentStartCommandOutputWire; use super::SubagentStopCommandInput; use super::USER_PROMPT_SUBMIT_INPUT_FIXTURE; use super::USER_PROMPT_SUBMIT_OUTPUT_FIXTURE; use super::UserPromptSubmitCommandInput; + use super::UserPromptSubmitCommandOutputWire; use super::schema_json; use super::write_schema_fixtures; use crate::events::common::SubagentHookContext; use pretty_assertions::assert_eq; + use schemars::JsonSchema; use serde_json::Value; use serde_json::json; use tempfile::TempDir; @@ -914,6 +936,20 @@ mod tests { value.replace("\r\n", "\n") } + fn assert_output_hook_event_name_const(definition: &str, expected: &str) { + let schema: Value = + serde_json::from_slice(&schema_json::().expect("serialize hook output schema")) + .expect("parse hook output schema"); + + assert_eq!( + schema["definitions"][definition]["properties"]["hookEventName"], + json!({ + "const": expected, + "type": "string", + }) + ); + } + #[test] fn generated_hook_schemas_match_fixtures() { let temp_dir = TempDir::new().expect("create temp dir"); @@ -950,6 +986,34 @@ mod tests { } } + #[test] + fn hook_specific_output_event_names_are_event_specific_in_output_schemas() { + assert_output_hook_event_name_const::( + "PermissionRequestHookSpecificOutputWire", + "PermissionRequest", + ); + assert_output_hook_event_name_const::( + "PostToolUseHookSpecificOutputWire", + "PostToolUse", + ); + assert_output_hook_event_name_const::( + "PreToolUseHookSpecificOutputWire", + "PreToolUse", + ); + assert_output_hook_event_name_const::( + "SessionStartHookSpecificOutputWire", + "SessionStart", + ); + assert_output_hook_event_name_const::( + "SubagentStartHookSpecificOutputWire", + "SubagentStart", + ); + assert_output_hook_event_name_const::( + "UserPromptSubmitHookSpecificOutputWire", + "UserPromptSubmit", + ); + } + #[test] fn turn_scoped_hook_inputs_include_codex_turn_id_extension() { // Codex intentionally diverges from Claude's public hook docs here so