* fix(#2289): context-monitor emits injection envelope only for supported events gsd-context-monitor is wired to Codex Stop/SubagentStart/SubagentStop/PreCompact (#772), but it emitted a hookSpecificOutput.additionalContext envelope for every event. Codex's Stop schema rejects that shape ("hook returned invalid stop hook JSON output") exactly when context is low. Use a positive allowlist: emit only for context-injection events (PostToolUse, AfterTool, and the pre-existing Gemini missing-name fallback); exit 0 silently for Stop and every other event. Debounce and critical-session bookkeeping still run on silenced events. Behavioral regression tests drive the real hook. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(#2289): backfill PR number 2324 into changeset Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(#2285): de-flake per-plan use_worktree property test on duplicate briefs The fast-check property in claude-orchestration.test.cjs located each plan's agent() call via indexOf on the brief, but only plan IDs were unique — briefs could collide (fast-check shrinks toward short strings). On a colliding seed the lookup found the first duplicate's line and misattributed its isolation, failing "use_worktree:true must carry isolation" intermittently (surfaced on macOS CI). emitWorkflowScript is correct for duplicate briefs (verified); suffix the unique id onto each agent() label so the test probe is unambiguous. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -180,15 +180,33 @@ process.stdin.on('end', () => {
|
||||
'starting new complex work.';
|
||||
}
|
||||
|
||||
const output = {
|
||||
hookSpecificOutput: {
|
||||
hookEventName: (data.hook_event_name && data.hook_event_name.trim())
|
||||
|| (process.env.GEMINI_API_KEY ? "AfterTool" : "PostToolUse"),
|
||||
additionalContext: message
|
||||
}
|
||||
};
|
||||
// #2289: the hookSpecificOutput.additionalContext envelope is only a valid
|
||||
// output shape for the context-injection events (PostToolUse, and AfterTool
|
||||
// for the Gemini dialect). This hook is also wired to other lifecycle events
|
||||
// on some hosts — Codex registers it under Stop / SubagentStart /
|
||||
// SubagentStop / PreCompact (#772) — and those reject the envelope
|
||||
// ("hook returned invalid stop hook JSON output"). Use a POSITIVE allowlist:
|
||||
// emit only for injection-capable events; every other event, and a
|
||||
// missing/unrecognized name, exits 0 with no stdout. A Stop-only blacklist is
|
||||
// not enough — a missing name would still fall through to the injection path.
|
||||
// All side effects above (debounce counter, one-time critical-session
|
||||
// recording) have already run regardless of whether output is emitted.
|
||||
const eventName = (data.hook_event_name && data.hook_event_name.trim()) || "";
|
||||
// Preserve the pre-#2289 Gemini fallback: a missing event name under a
|
||||
// Gemini-dialect runtime (GEMINI_API_KEY set) still means AfterTool, so its
|
||||
// advisory output is unchanged. A missing name on any other host is silent.
|
||||
const geminiFallback = eventName === "" && !!process.env.GEMINI_API_KEY;
|
||||
const injectionSupported = eventName === "PostToolUse" || eventName === "AfterTool" || geminiFallback;
|
||||
|
||||
process.stdout.write(JSON.stringify(output));
|
||||
if (injectionSupported) {
|
||||
const output = {
|
||||
hookSpecificOutput: {
|
||||
hookEventName: eventName || "AfterTool",
|
||||
additionalContext: message
|
||||
}
|
||||
};
|
||||
process.stdout.write(JSON.stringify(output));
|
||||
}
|
||||
} catch (e) {
|
||||
// Silent fail -- never block tool execution
|
||||
process.exit(0);
|
||||
|
||||
Reference in New Issue
Block a user