From 3465b1a3ca4bdfaac0d9f67c7c969d0d0990f259 Mon Sep 17 00:00:00 2001 From: saime428 <51110572+saime428@users.noreply.github.com> Date: Thu, 9 Jul 2026 18:16:30 -0500 Subject: [PATCH] Fix Codex hook output schema (#573) (#574) Restore hookSpecificOutput so strict Codex schemas accept SessionStart, UserPromptSubmit, and SubagentStart context. Co-authored-by: saime <2286263079@qq.com> --- hooks/ponytail-runtime.js | 8 ++++---- tests/hooks.test.js | 14 +++++++++----- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/hooks/ponytail-runtime.js b/hooks/ponytail-runtime.js index 7b5a662..37d682f 100644 --- a/hooks/ponytail-runtime.js +++ b/hooks/ponytail-runtime.js @@ -43,10 +43,10 @@ function writeHookOutput(event, mode, context = '') { if (isCodex) { const output = { systemMessage: `PONYTAIL:${mode.toUpperCase()}` }; if (context) { - // ponytail: Codex CLI expects additionalContext at top level, not nested - // under hookSpecificOutput. The hookSpecificOutput wrapper is for Claude - // Code's SubagentStart. See issue #505. - output.additionalContext = context; + output.hookSpecificOutput = { + hookEventName: event, + additionalContext: context, + }; } process.stdout.write(JSON.stringify(output)); return; diff --git a/tests/hooks.test.js b/tests/hooks.test.js index bc2ab20..2d2df1d 100644 --- a/tests/hooks.test.js +++ b/tests/hooks.test.js @@ -60,11 +60,12 @@ assert.equal(result.status, 0, result.stderr); assert.equal(fs.readFileSync(codexState, 'utf8'), 'ultra'); let output = JSON.parse(result.stdout); assert.equal(output.systemMessage, 'PONYTAIL:ULTRA'); +assert.equal(output.additionalContext, undefined, 'Codex must not emit additionalContext at top level (#573)'); +assert.equal(output.hookSpecificOutput.hookEventName, 'SessionStart'); assert.match( - output.additionalContext, + output.hookSpecificOutput.additionalContext, /PONYTAIL MODE ACTIVE — level: ultra/, ); -assert.equal(output.hookSpecificOutput, undefined, 'Codex should not use hookSpecificOutput (#505)'); result = run( 'ponytail-mode-tracker.js', @@ -85,8 +86,10 @@ result = run( assert.equal(result.status, 0, result.stderr); assert.equal(fs.readFileSync(codexState, 'utf8'), 'lite'); output = JSON.parse(result.stdout); +assert.equal(output.additionalContext, undefined, 'Codex must not emit additionalContext at top level (#573)'); +assert.equal(output.hookSpecificOutput.hookEventName, 'UserPromptSubmit'); assert.match( - output.additionalContext, + output.hookSpecificOutput.additionalContext, /PONYTAIL MODE ACTIVE — level: lite/, ); @@ -230,8 +233,9 @@ result = run('ponytail-subagent.js', { HOME: subHome, USERPROFILE: subHome, PLUG assert.equal(result.status, 0, result.stderr); output = JSON.parse(result.stdout); assert.equal(output.systemMessage, 'PONYTAIL:FULL'); -assert.ok(output.additionalContext, 'Codex SubagentStart should have additionalContext at top level (#505)'); -assert.match(output.additionalContext, /PONYTAIL MODE ACTIVE — level: full/); +assert.equal(output.additionalContext, undefined, 'Codex must not emit additionalContext at top level (#573)'); +assert.equal(output.hookSpecificOutput.hookEventName, 'SubagentStart'); +assert.match(output.hookSpecificOutput.additionalContext, /PONYTAIL MODE ACTIVE — level: full/); // SubagentStart scoping (issue #506): PONYTAIL_SUBAGENT_MATCHER limits the // injection to agent types whose name matches the regex. Unset keeps the