oh-my-codex 0.21.13 → 0.21.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/Cargo.lock +6 -6
  2. package/Cargo.toml +1 -1
  3. package/README.md +17 -0
  4. package/dist/adapt/herdr/__tests__/wiring.test.js +21 -0
  5. package/dist/adapt/herdr/__tests__/wiring.test.js.map +1 -1
  6. package/dist/adapt/herdr/wiring.d.ts.map +1 -1
  7. package/dist/adapt/herdr/wiring.js +5 -0
  8. package/dist/adapt/herdr/wiring.js.map +1 -1
  9. package/dist/auth/__tests__/hotswap.test.js +28 -0
  10. package/dist/auth/__tests__/hotswap.test.js.map +1 -1
  11. package/dist/auth/hotswap.d.ts +1 -0
  12. package/dist/auth/hotswap.d.ts.map +1 -1
  13. package/dist/auth/hotswap.js +4 -1
  14. package/dist/auth/hotswap.js.map +1 -1
  15. package/dist/cli/__tests__/codex-plugin-layout.test.js +196 -0
  16. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
  17. package/dist/cli/__tests__/doctor-warning-copy.test.js +2 -1
  18. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  19. package/dist/cli/__tests__/index.test.js +14 -1
  20. package/dist/cli/__tests__/index.test.js.map +1 -1
  21. package/dist/cli/__tests__/session-timeline.test.d.ts +2 -0
  22. package/dist/cli/__tests__/session-timeline.test.d.ts.map +1 -0
  23. package/dist/cli/__tests__/session-timeline.test.js +155 -0
  24. package/dist/cli/__tests__/session-timeline.test.js.map +1 -0
  25. package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js +1 -0
  26. package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js.map +1 -1
  27. package/dist/cli/doctor.d.ts.map +1 -1
  28. package/dist/cli/doctor.js +1 -0
  29. package/dist/cli/doctor.js.map +1 -1
  30. package/dist/cli/index.d.ts +1 -0
  31. package/dist/cli/index.d.ts.map +1 -1
  32. package/dist/cli/index.js +11 -0
  33. package/dist/cli/index.js.map +1 -1
  34. package/dist/cli/session-search.d.ts.map +1 -1
  35. package/dist/cli/session-search.js +7 -0
  36. package/dist/cli/session-search.js.map +1 -1
  37. package/dist/cli/session-timeline.d.ts +9 -0
  38. package/dist/cli/session-timeline.d.ts.map +1 -0
  39. package/dist/cli/session-timeline.js +174 -0
  40. package/dist/cli/session-timeline.js.map +1 -0
  41. package/dist/config/__tests__/codex-hooks.test.js +3 -1
  42. package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
  43. package/dist/config/__tests__/generator-idempotent.test.js +1 -0
  44. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  45. package/dist/config/codex-hooks.d.ts +1 -1
  46. package/dist/config/codex-hooks.d.ts.map +1 -1
  47. package/dist/config/codex-hooks.js +5 -0
  48. package/dist/config/codex-hooks.js.map +1 -1
  49. package/dist/evals/astra-defaults/__tests__/collector.test.js +51 -23
  50. package/dist/evals/astra-defaults/__tests__/collector.test.js.map +1 -1
  51. package/dist/evals/astra-defaults/collector.d.ts +2 -0
  52. package/dist/evals/astra-defaults/collector.d.ts.map +1 -1
  53. package/dist/evals/astra-defaults/collector.js +19 -8
  54. package/dist/evals/astra-defaults/collector.js.map +1 -1
  55. package/dist/hooks/extensibility/__tests__/dispatcher.test.js +143 -0
  56. package/dist/hooks/extensibility/__tests__/dispatcher.test.js.map +1 -1
  57. package/dist/hooks/extensibility/dispatcher.d.ts.map +1 -1
  58. package/dist/hooks/extensibility/dispatcher.js +120 -28
  59. package/dist/hooks/extensibility/dispatcher.js.map +1 -1
  60. package/dist/hooks/extensibility/runtime.d.ts.map +1 -1
  61. package/dist/hooks/extensibility/runtime.js +1 -0
  62. package/dist/hooks/extensibility/runtime.js.map +1 -1
  63. package/dist/hooks/extensibility/types.d.ts +2 -0
  64. package/dist/hooks/extensibility/types.d.ts.map +1 -1
  65. package/dist/hooks/native/types.d.ts +1 -1
  66. package/dist/hooks/native/types.d.ts.map +1 -1
  67. package/dist/hooks/native/types.js.map +1 -1
  68. package/dist/notifications/__tests__/lifecycle-dedupe.test.js +200 -2
  69. package/dist/notifications/__tests__/lifecycle-dedupe.test.js.map +1 -1
  70. package/dist/notifications/lifecycle-dedupe.d.ts +10 -0
  71. package/dist/notifications/lifecycle-dedupe.d.ts.map +1 -1
  72. package/dist/notifications/lifecycle-dedupe.js +175 -19
  73. package/dist/notifications/lifecycle-dedupe.js.map +1 -1
  74. package/dist/scripts/__tests__/codex-native-hook.test.js +278 -6
  75. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  76. package/dist/scripts/__tests__/native-hook-protocol.test.d.ts +2 -0
  77. package/dist/scripts/__tests__/native-hook-protocol.test.d.ts.map +1 -0
  78. package/dist/scripts/__tests__/native-hook-protocol.test.js +329 -0
  79. package/dist/scripts/__tests__/native-hook-protocol.test.js.map +1 -0
  80. package/dist/scripts/__tests__/smoke-packed-install.test.js +7 -6
  81. package/dist/scripts/__tests__/smoke-packed-install.test.js.map +1 -1
  82. package/dist/scripts/codex-execution-surface.d.ts +1 -1
  83. package/dist/scripts/codex-execution-surface.d.ts.map +1 -1
  84. package/dist/scripts/codex-execution-surface.js.map +1 -1
  85. package/dist/scripts/codex-native-hook.d.ts +1 -1
  86. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  87. package/dist/scripts/codex-native-hook.js +124 -13
  88. package/dist/scripts/codex-native-hook.js.map +1 -1
  89. package/dist/scripts/hook-payload-guard.d.ts +1 -1
  90. package/dist/scripts/hook-payload-guard.d.ts.map +1 -1
  91. package/dist/scripts/hook-payload-guard.js +1 -0
  92. package/dist/scripts/hook-payload-guard.js.map +1 -1
  93. package/dist/scripts/smoke-packed-install.d.ts +2 -2
  94. package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
  95. package/dist/scripts/smoke-packed-install.js +12 -1
  96. package/dist/scripts/smoke-packed-install.js.map +1 -1
  97. package/dist/session-history/__tests__/timeline.test.d.ts +2 -0
  98. package/dist/session-history/__tests__/timeline.test.d.ts.map +1 -0
  99. package/dist/session-history/__tests__/timeline.test.js +459 -0
  100. package/dist/session-history/__tests__/timeline.test.js.map +1 -0
  101. package/dist/session-history/export.d.ts +2 -3
  102. package/dist/session-history/export.d.ts.map +1 -1
  103. package/dist/session-history/export.js +5 -66
  104. package/dist/session-history/export.js.map +1 -1
  105. package/dist/session-history/timeline.d.ts +97 -0
  106. package/dist/session-history/timeline.d.ts.map +1 -0
  107. package/dist/session-history/timeline.js +325 -0
  108. package/dist/session-history/timeline.js.map +1 -0
  109. package/dist/session-history/transcript.d.ts +16 -0
  110. package/dist/session-history/transcript.d.ts.map +1 -0
  111. package/dist/session-history/transcript.js +111 -0
  112. package/dist/session-history/transcript.js.map +1 -0
  113. package/dist/subagents/tracker.d.ts +6 -0
  114. package/dist/subagents/tracker.d.ts.map +1 -1
  115. package/dist/subagents/tracker.js +32 -0
  116. package/dist/subagents/tracker.js.map +1 -1
  117. package/dist/team/__tests__/runtime.test.js +12 -3
  118. package/dist/team/__tests__/runtime.test.js.map +1 -1
  119. package/dist/team/__tests__/scaling.test.js +25 -0
  120. package/dist/team/__tests__/scaling.test.js.map +1 -1
  121. package/dist/team/__tests__/tmux-session.test.js +30 -0
  122. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  123. package/dist/team/tmux-session.d.ts.map +1 -1
  124. package/dist/team/tmux-session.js +9 -1
  125. package/dist/team/tmux-session.js.map +1 -1
  126. package/dist/wiki/__tests__/session-hooks.test.js +26 -0
  127. package/dist/wiki/__tests__/session-hooks.test.js.map +1 -1
  128. package/dist/wiki/lifecycle.d.ts.map +1 -1
  129. package/dist/wiki/lifecycle.js +6 -1
  130. package/dist/wiki/lifecycle.js.map +1 -1
  131. package/package.json +1 -1
  132. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  133. package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +85 -18
  134. package/plugins/oh-my-codex/hooks/hooks.json +10 -0
  135. package/src/scripts/__tests__/codex-native-hook.test.ts +273 -5
  136. package/src/scripts/__tests__/native-hook-protocol.test.ts +362 -0
  137. package/src/scripts/__tests__/smoke-packed-install.test.ts +7 -6
  138. package/src/scripts/codex-execution-surface.ts +1 -0
  139. package/src/scripts/codex-native-hook.ts +130 -11
  140. package/src/scripts/hook-payload-guard.ts +1 -0
  141. package/src/scripts/smoke-packed-install.ts +12 -1
@@ -7,7 +7,7 @@ import { fileURLToPath, pathToFileURL } from "url";
7
7
  import { readModeStateForActiveDecision, readModeStateForSession, updateModeState } from "../modes/base.js";
8
8
  import { redactAuthSecrets } from "../auth/redact.js";
9
9
  import { SKILL_ACTIVE_STATE_FILE, extractSessionIdFromInitializedStatePath, getSkillActiveStatePathsForStateDir, listActiveSkills, readSkillActiveState, readVisibleSkillActiveStateForStateDir, } from "../state/skill-active.js";
10
- import { fenceNativeSubagentAuthorities, forceGlobalAuthorityDenial, consumeDirectChildReopenContext, isTrustedSubagentThread, readSubagentSessionSummary, readSubagentTrackingState, recordNativeSubagentAuthorityObservation, recordSubagentTurnForSession, repairPersistedRootIdentity, quarantinePersistedRootAuthority, revokeNativeSubagentAuthorities, resolveInstalledRoleName, } from "../subagents/tracker.js";
10
+ import { fenceNativeSubagentAuthorities, forceGlobalAuthorityDenial, consumeDirectChildReopenContext, isTrustedSubagentThread, readSubagentSessionSummary, readSubagentTrackingState, recordNativeSubagentAuthorityObservation, recordSubagentTurnForSession, recordSubagentTurnForSessionPartitions, repairPersistedRootIdentity, quarantinePersistedRootAuthority, revokeNativeSubagentAuthorities, resolveInstalledRoleName, } from "../subagents/tracker.js";
11
11
  import { readRoleRoutingMarker, writeRoleRoutingMarker } from "../subagents/role-routing-marker.js";
12
12
  import { reconcileRalplanAdvisory, observeRalplanAdvisoryPrompt, } from "../ralplan/advisory.js";
13
13
  import { activateOrResumeRalplanAdvisory } from "../ralplan/advisory-activation.js";
@@ -39,6 +39,7 @@ import { dispatchHookEventRuntime } from "../hooks/extensibility/runtime.js";
39
39
  import { getNotificationConfig, getVerbosity } from "../notifications/config.js";
40
40
  import { reconcileHudForPromptSubmit } from "../hud/reconcile.js";
41
41
  import { onPreCompact as buildWikiPreCompactContext, onSessionStart as buildWikiSessionStartContext, } from "../wiki/lifecycle.js";
42
+ import { sessionModelInstructionsPath } from "../hooks/agents-overlay.js";
42
43
  import { readAutoresearchCompletionStatus, readAutoresearchModeStateForActiveDecision } from "../autoresearch/skill-validation.js";
43
44
  import { deriveAutopilotChildPhase, normalizeAutopilotPhase } from "../autopilot/fsm.js";
44
45
  import { CONDUCTOR_ORCHESTRATION_METADATA_PREFIXES, LEADER_CONDUCTOR_BLOCK, LEADER_CONDUCTOR_REUSE_AND_LEDGER_GUIDANCE, NATIVE_SUBAGENT_SUPPORT_BLOCKER_FILE, actionKindForConductorArtifact, authorizeConductorAction, buildRoleRoutingUnavailableGuidance, buildUnsupportedNativeSubagentGuidance, canonicalizeNativeCollaborationToolName, classifyConductorArtifactKind, parseNativeSubagentResultDisposition, resolveNativeSubagentSupportStatus } from "../leader/contract.js";
@@ -449,6 +450,7 @@ function readHookEventName(payload) {
449
450
  || raw === "UserPromptSubmit"
450
451
  || raw === "PreCompact"
451
452
  || raw === "PostCompact"
453
+ || raw === "SubagentStop"
452
454
  || raw === "Stop") {
453
455
  return raw;
454
456
  }
@@ -472,6 +474,8 @@ export function mapCodexHookEventToOmxEvent(hookEventName) {
472
474
  return "pre-compact";
473
475
  case "PostCompact":
474
476
  return "post-compact";
477
+ case "SubagentStop":
478
+ return "subagent-stop";
475
479
  case "Stop":
476
480
  return "stop";
477
481
  default:
@@ -605,9 +609,11 @@ function sanitizePayloadForHookContext(payload, hookEventName, canonicalSessionI
605
609
  delete sanitized.text;
606
610
  return sanitized;
607
611
  }
608
- if (hookEventName === "Stop") {
612
+ if (hookEventName === "Stop" || hookEventName === "SubagentStop") {
609
613
  delete sanitized.stop_hook_active;
610
614
  delete sanitized.stopHookActive;
615
+ }
616
+ if (hookEventName === "Stop") {
611
617
  delete sanitized.sessionId;
612
618
  sanitized.session_id = canonicalSessionId.trim() || safeString(payload.session_id ?? payload.sessionId).trim();
613
619
  }
@@ -1789,6 +1795,21 @@ async function buildPersistedSubagentReopenContext(cwd, sessionId, options) {
1789
1795
  }
1790
1796
  async function buildSessionStartContext(cwd, sessionId, options = {}) {
1791
1797
  const sections = [];
1798
+ let startupInstructions = "";
1799
+ const startupFile = process.env.OMX_STARTUP_MODEL_INSTRUCTIONS_FILE;
1800
+ if (readSessionStartSource(options.payload) === "startup" && startupFile
1801
+ && resolve(startupFile) === sessionModelInstructionsPath(cwd, sessionId)) {
1802
+ try {
1803
+ startupInstructions = await readFile(startupFile, "utf-8");
1804
+ }
1805
+ catch {
1806
+ // Missing launch evidence must preserve hook-delivered memory.
1807
+ }
1808
+ }
1809
+ const startupMemory = startupInstructions.match(/(?:^|\n)\*\*Project Context:\*\*\r?\n([\s\S]*?)(?=\r?\n\r?\n|$)/)?.[1] ?? "";
1810
+ const alreadyInStartupInstructions = (label, value) => startupMemory.split(/\r?\n/).some(line => line.trim().replace(/\s+/g, " ") === `- ${label}: ${value}`);
1811
+ const startupPriority = startupInstructions.match(/(?:^|\n)\*\*Priority Notes:\*\*\r?\n([\s\S]*?)(?=\r?\n\r?\n|$)/)?.[1]
1812
+ .replace(/\s+/g, " ").trim();
1792
1813
  sections.push(buildExecutionEnvironmentSection(cwd, {
1793
1814
  hookEventName: options.hookEventName,
1794
1815
  payload: options.payload,
@@ -1831,16 +1852,16 @@ async function buildSessionStartContext(cwd, sessionId, options = {}) {
1831
1852
  const summary = [];
1832
1853
  const relativeMemoryPath = relative(cwd, projectMemoryPath).replace(/\\/g, "/");
1833
1854
  summary.push(`- source: ${relativeMemoryPath === "project-memory.json" ? "project-memory.json" : ".omx/project-memory.json"}`);
1834
- if (techStack)
1855
+ if (techStack && !alreadyInStartupInstructions("Stack", techStack))
1835
1856
  summary.push(`- stack: ${techStack}`);
1836
- if (conventions)
1857
+ if (conventions && !alreadyInStartupInstructions("Conventions", conventions))
1837
1858
  summary.push(`- conventions: ${conventions}`);
1838
- if (build)
1859
+ if (build && !alreadyInStartupInstructions("Build", build))
1839
1860
  summary.push(`- build: ${build}`);
1840
1861
  if (directives.length > 0) {
1841
1862
  const firstDirective = directives[0];
1842
1863
  const directive = safeContextSnippet(firstDirective.directive);
1843
- if (directive)
1864
+ if (directive && !alreadyInStartupInstructions("Directive", directive))
1844
1865
  summary.push(`- directive: ${directive}`);
1845
1866
  }
1846
1867
  if (notes.length > 0) {
@@ -1867,7 +1888,7 @@ async function buildSessionStartContext(cwd, sessionId, options = {}) {
1867
1888
  .map((line) => line.trim())
1868
1889
  .filter(Boolean)
1869
1890
  .join(" ");
1870
- if (section) {
1891
+ if (section && startupPriority !== section.replace(/\s+/g, " ")) {
1871
1892
  sections.push(`[Priority notes]\n- ${section.slice(0, 220)}`);
1872
1893
  }
1873
1894
  }
@@ -20952,6 +20973,75 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
20952
20973
  const cwd = options.cwd ?? (safeString(payload.cwd).trim() || process.cwd());
20953
20974
  const reconcileAdvisory = options.reconcileRalplanAdvisoryFn ?? reconcileRalplanAdvisory;
20954
20975
  const omxEventName = mapCodexHookEventToOmxEvent(hookEventName);
20976
+ if (hookEventName === "SubagentStop") {
20977
+ // This event names the parent session and child agent separately; it is not a leader Stop.
20978
+ const parentSessionId = readStrictStopPayloadSessionId(payload);
20979
+ const agentId = normalizeSessionId(safeString(payload.agent_id).trim());
20980
+ let sessionId = null;
20981
+ let pluginDiscoveryCwd;
20982
+ if (parentSessionId && agentId && agentId !== parentSessionId) {
20983
+ const authoritativeTeamWorker = hasRawTeamWorkerDeclaration()
20984
+ ? await resolveAuthoritativeTeamWorkerContext(cwd)
20985
+ : null;
20986
+ if (authoritativeTeamWorker) {
20987
+ // Team workers can own child agents without the shared leader pointer or tracker summary.
20988
+ sessionId = parentSessionId;
20989
+ pluginDiscoveryCwd = authoritativeTeamWorker.canonicalLeaderCwd;
20990
+ }
20991
+ else {
20992
+ const pointer = await readSessionPointer(resolveSessionPointerContext(cwd));
20993
+ if (pointer.status === "usable" && pointer.state && !sessionPointerAliases(pointer.state).has(agentId)) {
20994
+ const candidateSessionId = pointer.state.session_id;
20995
+ const summary = await readSubagentSessionSummary(cwd, candidateSessionId);
20996
+ if (payloadMatchesSessionPointer(parentSessionId, pointer.state)
20997
+ || summary?.allThreadIds.includes(parentSessionId)) {
20998
+ sessionId = candidateSessionId;
20999
+ }
21000
+ }
21001
+ }
21002
+ }
21003
+ if (sessionId && agentId) {
21004
+ try {
21005
+ await recordSubagentTurnForSessionPartitions(cwd, {
21006
+ sessionId,
21007
+ threadId: agentId,
21008
+ turnId: readPayloadTurnId(payload) || undefined,
21009
+ kind: "subagent",
21010
+ mode: safeString(payload.agent_type).trim() || undefined,
21011
+ completed: true,
21012
+ completionSource: "native_stop_event",
21013
+ });
21014
+ }
21015
+ catch (error) {
21016
+ await logNativeHookCliError(cwd, "native_subagent_completion_tracking_error", error, payload, { agent_id: agentId });
21017
+ return { hookEventName, omxEventName, skillState: null, outputJson: null };
21018
+ }
21019
+ if (!shouldSuppressSubagentLifecycleHookDispatch()) {
21020
+ try {
21021
+ await dispatchHookEventRuntime({
21022
+ event: buildNativeHookEvent(omxEventName, {
21023
+ ...buildBaseContext(cwd, payload, hookEventName, sessionId),
21024
+ native_session_id: agentId,
21025
+ codex_session_id: agentId,
21026
+ omx_session_id: sessionId,
21027
+ }, {
21028
+ session_id: sessionId,
21029
+ thread_id: agentId,
21030
+ turn_id: readPayloadTurnId(payload) || undefined,
21031
+ mode: safeString(payload.agent_type).trim() || undefined,
21032
+ }),
21033
+ cwd,
21034
+ ...(pluginDiscoveryCwd ? { pluginDiscoveryCwd } : {}),
21035
+ allowTeamWorkerSideEffects: false,
21036
+ });
21037
+ }
21038
+ catch (error) {
21039
+ await logNativeHookCliError(cwd, "native_subagent_completion_dispatch_error", error, payload, { agent_id: agentId });
21040
+ }
21041
+ }
21042
+ }
21043
+ return { hookEventName, omxEventName, skillState: null, outputJson: null };
21044
+ }
20955
21045
  // #3497 / C1 boundary: documented-leader PreToolUse hard gate removed.
20956
21046
  if (hookEventName === "PreToolUse" && safeString(payload.tool_name).trim() === "Bash") {
20957
21047
  void payload;
@@ -21100,7 +21190,18 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
21100
21190
  // never promote itself into a root/leader session or emit an independent
21101
21191
  // session-start notification at session/minimal verbosity.
21102
21192
  isSubagentSessionStart = true;
21103
- if (canonicalSessionId) {
21193
+ if (authoritativeTeamWorker && authoritativeWorkerPayloadSessionId === nativeSessionId) {
21194
+ // A Team worker shares the leader pointer but owns its native child tree.
21195
+ // Retain descriptive child records under the transcript's immediate parent
21196
+ // without letting this child inherit the leader's session scope.
21197
+ canonicalSessionId = "";
21198
+ resolvedNativeSessionId = nativeSessionId;
21199
+ skipCanonicalSessionStartContext = true;
21200
+ allowImplicitSessionSideEffects = false;
21201
+ recordedNativeSubagentSessionStart = true;
21202
+ await recordNativeSubagentSessionStart(cwd, subagentSessionStart.parentThreadId, nativeSessionId, subagentSessionStart, transcriptPath, "", "absent", implicatedChildThreadIds);
21203
+ }
21204
+ else if (canonicalSessionId) {
21104
21205
  const belongsToCanonicalSession = await nativeSubagentSessionStartBelongsToCanonicalSession(cwd, canonicalSessionId, currentSessionState, subagentSessionStart);
21105
21206
  if (belongsToCanonicalSession) {
21106
21207
  resolvedNativeSessionId = nativeSessionId;
@@ -21569,6 +21670,8 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
21569
21670
  && /^[ \t]*omx[ \t]+cancel\b/.test(readPreToolUseCommand(payload));
21570
21671
  if (omxEventName && !bypassGenericPreToolUsePluginDispatch && allowImplicitSessionSideEffects && allowGlobalSideEffects && !skipCanonicalSessionStartContext && !suppressNoisySubagentLifecycleDispatch) {
21571
21672
  const baseContext = buildBaseContext(cwd, payload, hookEventName, canonicalSessionId);
21673
+ if (isSubagentStop)
21674
+ baseContext.is_subagent_stop = true;
21572
21675
  if (resolvedNativeSessionId) {
21573
21676
  baseContext.native_session_id = resolvedNativeSessionId;
21574
21677
  baseContext.codex_session_id = resolvedNativeSessionId;
@@ -21585,6 +21688,7 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
21585
21688
  await dispatchHookEventRuntime({
21586
21689
  event,
21587
21690
  cwd,
21691
+ pluginDiscoveryCwd: authoritativeTeamWorkerEvidence?.canonicalLeaderCwd,
21588
21692
  allowTeamWorkerSideEffects: false,
21589
21693
  });
21590
21694
  }
@@ -21730,6 +21834,7 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
21730
21834
  await recordSubagentTurnForSession(cwd, {
21731
21835
  sessionId: canonicalSessionId.trim(),
21732
21836
  threadId: threadId.trim(),
21837
+ turnId: readPayloadTurnId(payload) || undefined,
21733
21838
  completed: true,
21734
21839
  completionSource: 'native_stop_event',
21735
21840
  timestamp: new Date().toISOString(),
@@ -21832,8 +21937,10 @@ async function readStdinJson() {
21832
21937
  };
21833
21938
  }
21834
21939
  }
21835
- function inferHookEventNameFromMalformedInput(raw) {
21836
- const match = raw.match(/(?:\"|['"])?hook[_-]?event[_-]?name(?:\"|['"])?\s*:\s*(?:\"|['"])?(SessionStart|PreToolUse|PostToolUse|UserPromptSubmit|PreCompact|PostCompact|Stop)\b/i);
21940
+ function inferHookEventNameFromMalformedInput(raw, rawHookEventName) {
21941
+ if (rawHookEventName)
21942
+ return rawHookEventName;
21943
+ const match = raw.match(/(?:\"|['"])?hook[_-]?event[_-]?name(?:\"|['"])?\s*:\s*(?:\"|['"])?(SessionStart|PreToolUse|PostToolUse|UserPromptSubmit|PreCompact|PostCompact|SubagentStop|Stop)\b/i);
21837
21944
  const value = match?.[1];
21838
21945
  if (!value)
21839
21946
  return null;
@@ -21849,13 +21956,15 @@ function buildUnparseablePreToolUseDenyOutput(reason, systemMessage) {
21849
21956
  },
21850
21957
  };
21851
21958
  }
21852
- function buildMalformedStdinHookOutput(parseError, rawInput, cwd = process.cwd()) {
21959
+ function buildMalformedStdinHookOutput(parseError, rawInput, cwd = process.cwd(), rawHookEventName) {
21853
21960
  const reason = "OMX native hook received malformed JSON input. Preserve runtime state, inspect the emitting hook payload yourself, and retry with valid JSON.";
21854
21961
  const systemMessage = `${reason} stdin JSON parsing failed inside codex-native-hook: ${parseError.message}.`;
21855
- const inferredHookEventName = inferHookEventNameFromMalformedInput(rawInput);
21962
+ const inferredHookEventName = inferHookEventNameFromMalformedInput(rawInput, rawHookEventName);
21856
21963
  if (inferredHookEventName === "PreToolUse") {
21857
21964
  return buildUnparseablePreToolUseDenyOutput(reason, systemMessage);
21858
21965
  }
21966
+ if (inferredHookEventName === "SubagentStop")
21967
+ return {};
21859
21968
  if (inferredHookEventName === "Stop" || (!inferredHookEventName && hasNativeStopRuntimeSurface(cwd))) {
21860
21969
  return {
21861
21970
  decision: "block",
@@ -21895,6 +22004,8 @@ function buildOversizedStopInactiveWorkflowOutput() {
21895
22004
  }
21896
22005
  async function buildOversizedStdinHookOutput(rawHookEventName, cwd) {
21897
22006
  const systemMessage = `OMX native hook rejected oversized stdin JSON before parsing; maxBytes=${MAX_NATIVE_STDIN_JSON_BYTES}.`;
22007
+ if (rawHookEventName === "SubagentStop")
22008
+ return {};
21898
22009
  if (rawHookEventName === "PreToolUse") {
21899
22010
  return buildUnparseablePreToolUseDenyOutput(systemMessage, systemMessage);
21900
22011
  }
@@ -21974,7 +22085,7 @@ export async function runCodexNativeHookCli() {
21974
22085
  }
21975
22086
  if (parseError) {
21976
22087
  await logNativeHookCliError(process.cwd(), "native_hook_stdin_parse_error", parseError, {}, buildRawInputLogFields(rawInput));
21977
- writeNativeHookJsonStdout(buildMalformedStdinHookOutput(parseError, rawInput, process.cwd()));
22088
+ writeNativeHookJsonStdout(buildMalformedStdinHookOutput(parseError, rawInput, process.cwd(), rawHookEventName));
21978
22089
  return;
21979
22090
  }
21980
22091
  try {