oh-my-codex 0.18.9 → 0.18.11

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 (195) hide show
  1. package/Cargo.lock +6 -6
  2. package/Cargo.toml +1 -1
  3. package/README.md +6 -6
  4. package/dist/autopilot/__tests__/deep-interview-gate.test.d.ts +2 -0
  5. package/dist/autopilot/__tests__/deep-interview-gate.test.d.ts.map +1 -0
  6. package/dist/autopilot/__tests__/deep-interview-gate.test.js +215 -0
  7. package/dist/autopilot/__tests__/deep-interview-gate.test.js.map +1 -0
  8. package/dist/autopilot/__tests__/ralplan-gate.test.js +148 -0
  9. package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -1
  10. package/dist/autopilot/deep-interview-gate.d.ts.map +1 -1
  11. package/dist/autopilot/deep-interview-gate.js +140 -0
  12. package/dist/autopilot/deep-interview-gate.js.map +1 -1
  13. package/dist/catalog/__tests__/schema.test.js +6 -0
  14. package/dist/catalog/__tests__/schema.test.js.map +1 -1
  15. package/dist/cli/__tests__/auth.test.js +36 -3
  16. package/dist/cli/__tests__/auth.test.js.map +1 -1
  17. package/dist/cli/__tests__/codex-feature-probe.test.d.ts +2 -0
  18. package/dist/cli/__tests__/codex-feature-probe.test.d.ts.map +1 -0
  19. package/dist/cli/__tests__/codex-feature-probe.test.js +46 -0
  20. package/dist/cli/__tests__/codex-feature-probe.test.js.map +1 -0
  21. package/dist/cli/__tests__/doctor-spark-routing.test.d.ts +2 -0
  22. package/dist/cli/__tests__/doctor-spark-routing.test.d.ts.map +1 -0
  23. package/dist/cli/__tests__/doctor-spark-routing.test.js +79 -0
  24. package/dist/cli/__tests__/doctor-spark-routing.test.js.map +1 -0
  25. package/dist/cli/__tests__/doctor-warning-copy.test.js +2 -0
  26. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  27. package/dist/cli/__tests__/explore.test.js +47 -1175
  28. package/dist/cli/__tests__/explore.test.js.map +1 -1
  29. package/dist/cli/__tests__/index.test.js +276 -6
  30. package/dist/cli/__tests__/index.test.js.map +1 -1
  31. package/dist/cli/__tests__/launch-fallback.test.js +19 -5
  32. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  33. package/dist/cli/__tests__/nested-help-routing.test.js +1 -1
  34. package/dist/cli/__tests__/nested-help-routing.test.js.map +1 -1
  35. package/dist/cli/__tests__/package-bin-contract.test.js +19 -6
  36. package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
  37. package/dist/cli/__tests__/setup-refresh.test.js +6 -2
  38. package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
  39. package/dist/cli/__tests__/sparkshell-packaging.test.js +45 -2
  40. package/dist/cli/__tests__/sparkshell-packaging.test.js.map +1 -1
  41. package/dist/cli/__tests__/team-decompose.test.js +10 -5
  42. package/dist/cli/__tests__/team-decompose.test.js.map +1 -1
  43. package/dist/cli/__tests__/team.test.js +45 -1
  44. package/dist/cli/__tests__/team.test.js.map +1 -1
  45. package/dist/cli/__tests__/ultragoal.test.js +75 -0
  46. package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
  47. package/dist/cli/auth.d.ts.map +1 -1
  48. package/dist/cli/auth.js +25 -1
  49. package/dist/cli/auth.js.map +1 -1
  50. package/dist/cli/codex-feature-probe.d.ts +5 -2
  51. package/dist/cli/codex-feature-probe.d.ts.map +1 -1
  52. package/dist/cli/codex-feature-probe.js +25 -9
  53. package/dist/cli/codex-feature-probe.js.map +1 -1
  54. package/dist/cli/doctor.d.ts +17 -0
  55. package/dist/cli/doctor.d.ts.map +1 -1
  56. package/dist/cli/doctor.js +121 -0
  57. package/dist/cli/doctor.js.map +1 -1
  58. package/dist/cli/explore.d.ts +2 -21
  59. package/dist/cli/explore.d.ts.map +1 -1
  60. package/dist/cli/explore.js +25 -624
  61. package/dist/cli/explore.js.map +1 -1
  62. package/dist/cli/index.d.ts +34 -2
  63. package/dist/cli/index.d.ts.map +1 -1
  64. package/dist/cli/index.js +166 -89
  65. package/dist/cli/index.js.map +1 -1
  66. package/dist/cli/setup.d.ts.map +1 -1
  67. package/dist/cli/setup.js +9 -1
  68. package/dist/cli/setup.js.map +1 -1
  69. package/dist/cli/team.d.ts +4 -0
  70. package/dist/cli/team.d.ts.map +1 -1
  71. package/dist/cli/team.js +43 -4
  72. package/dist/cli/team.js.map +1 -1
  73. package/dist/cli/ultragoal.d.ts.map +1 -1
  74. package/dist/cli/ultragoal.js +29 -0
  75. package/dist/cli/ultragoal.js.map +1 -1
  76. package/dist/hooks/__tests__/agents-overlay.test.js +8 -10
  77. package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
  78. package/dist/hooks/__tests__/autopilot-skill-contract.test.js +15 -0
  79. package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
  80. package/dist/hooks/__tests__/deep-interview-contract.test.js +16 -0
  81. package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
  82. package/dist/hooks/__tests__/explore-routing.test.js +5 -8
  83. package/dist/hooks/__tests__/explore-routing.test.js.map +1 -1
  84. package/dist/hooks/__tests__/explore-sparkshell-guidance-contract.test.js +6 -6
  85. package/dist/hooks/__tests__/explore-sparkshell-guidance-contract.test.js.map +1 -1
  86. package/dist/hooks/__tests__/skill-guidance-contract.test.js +14 -5
  87. package/dist/hooks/__tests__/skill-guidance-contract.test.js.map +1 -1
  88. package/dist/hooks/agents-overlay.d.ts.map +1 -1
  89. package/dist/hooks/agents-overlay.js +2 -1
  90. package/dist/hooks/agents-overlay.js.map +1 -1
  91. package/dist/hooks/explore-routing.d.ts +1 -1
  92. package/dist/hooks/explore-routing.d.ts.map +1 -1
  93. package/dist/hooks/explore-routing.js +3 -8
  94. package/dist/hooks/explore-routing.js.map +1 -1
  95. package/dist/hooks/extensibility/__tests__/plugin-runner.test.js +112 -1
  96. package/dist/hooks/extensibility/__tests__/plugin-runner.test.js.map +1 -1
  97. package/dist/hooks/extensibility/plugin-runner-stdin.d.ts +2 -0
  98. package/dist/hooks/extensibility/plugin-runner-stdin.d.ts.map +1 -0
  99. package/dist/hooks/extensibility/plugin-runner-stdin.js +16 -0
  100. package/dist/hooks/extensibility/plugin-runner-stdin.js.map +1 -0
  101. package/dist/hooks/extensibility/plugin-runner.js +2 -4
  102. package/dist/hooks/extensibility/plugin-runner.js.map +1 -1
  103. package/dist/hud/__tests__/index.test.js +23 -2
  104. package/dist/hud/__tests__/index.test.js.map +1 -1
  105. package/dist/hud/__tests__/reconcile.test.js +367 -1
  106. package/dist/hud/__tests__/reconcile.test.js.map +1 -1
  107. package/dist/hud/__tests__/tmux.test.js +118 -7
  108. package/dist/hud/__tests__/tmux.test.js.map +1 -1
  109. package/dist/hud/constants.d.ts +10 -0
  110. package/dist/hud/constants.d.ts.map +1 -1
  111. package/dist/hud/constants.js +19 -0
  112. package/dist/hud/constants.js.map +1 -1
  113. package/dist/hud/index.d.ts +6 -1
  114. package/dist/hud/index.d.ts.map +1 -1
  115. package/dist/hud/index.js +12 -3
  116. package/dist/hud/index.js.map +1 -1
  117. package/dist/hud/reconcile.d.ts +10 -2
  118. package/dist/hud/reconcile.d.ts.map +1 -1
  119. package/dist/hud/reconcile.js +78 -30
  120. package/dist/hud/reconcile.js.map +1 -1
  121. package/dist/hud/tmux.d.ts +14 -1
  122. package/dist/hud/tmux.d.ts.map +1 -1
  123. package/dist/hud/tmux.js +129 -15
  124. package/dist/hud/tmux.js.map +1 -1
  125. package/dist/mcp/wiki-server.d.ts +3 -3
  126. package/dist/ralplan/consensus-gate.js +9 -1
  127. package/dist/ralplan/consensus-gate.js.map +1 -1
  128. package/dist/scripts/__tests__/codex-native-hook.test.js +170 -17
  129. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  130. package/dist/scripts/__tests__/run-test-files.test.js +115 -1
  131. package/dist/scripts/__tests__/run-test-files.test.js.map +1 -1
  132. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  133. package/dist/scripts/codex-native-hook.js +74 -11
  134. package/dist/scripts/codex-native-hook.js.map +1 -1
  135. package/dist/scripts/notify-hook/team-worker-stop.d.ts.map +1 -1
  136. package/dist/scripts/notify-hook/team-worker-stop.js +54 -21
  137. package/dist/scripts/notify-hook/team-worker-stop.js.map +1 -1
  138. package/dist/scripts/run-test-files.js +218 -160
  139. package/dist/scripts/run-test-files.js.map +1 -1
  140. package/dist/state/__tests__/operations.test.js +463 -0
  141. package/dist/state/__tests__/operations.test.js.map +1 -1
  142. package/dist/team/__tests__/delivery-log.test.js +18 -0
  143. package/dist/team/__tests__/delivery-log.test.js.map +1 -1
  144. package/dist/team/__tests__/runtime.test.js +48 -0
  145. package/dist/team/__tests__/runtime.test.js.map +1 -1
  146. package/dist/team/__tests__/tmux-session.test.js +107 -0
  147. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  148. package/dist/team/__tests__/tmux-test-fixture.d.ts.map +1 -1
  149. package/dist/team/__tests__/tmux-test-fixture.js +14 -2
  150. package/dist/team/__tests__/tmux-test-fixture.js.map +1 -1
  151. package/dist/team/__tests__/tmux-test-fixture.test.js +1 -0
  152. package/dist/team/__tests__/tmux-test-fixture.test.js.map +1 -1
  153. package/dist/team/__tests__/worker-bootstrap.test.js +54 -1
  154. package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
  155. package/dist/team/delivery-log.d.ts +1 -1
  156. package/dist/team/delivery-log.d.ts.map +1 -1
  157. package/dist/team/delivery-log.js.map +1 -1
  158. package/dist/team/repo-aware-decomposition.d.ts +4 -0
  159. package/dist/team/repo-aware-decomposition.d.ts.map +1 -1
  160. package/dist/team/repo-aware-decomposition.js.map +1 -1
  161. package/dist/team/runtime.d.ts.map +1 -1
  162. package/dist/team/runtime.js +78 -9
  163. package/dist/team/runtime.js.map +1 -1
  164. package/dist/team/tmux-session.d.ts +1 -0
  165. package/dist/team/tmux-session.d.ts.map +1 -1
  166. package/dist/team/tmux-session.js +16 -5
  167. package/dist/team/tmux-session.js.map +1 -1
  168. package/dist/team/ultragoal-context.d.ts +12 -0
  169. package/dist/team/ultragoal-context.d.ts.map +1 -1
  170. package/dist/team/ultragoal-context.js +32 -8
  171. package/dist/team/ultragoal-context.js.map +1 -1
  172. package/dist/utils/__tests__/paths.test.js +23 -0
  173. package/dist/utils/__tests__/paths.test.js.map +1 -1
  174. package/dist/utils/paths.d.ts.map +1 -1
  175. package/dist/utils/paths.js +4 -2
  176. package/dist/utils/paths.js.map +1 -1
  177. package/dist/utils/toml.d.ts +4 -0
  178. package/dist/utils/toml.d.ts.map +1 -0
  179. package/dist/utils/toml.js +75 -0
  180. package/dist/utils/toml.js.map +1 -0
  181. package/package.json +1 -1
  182. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  183. package/plugins/oh-my-codex/skills/autopilot/SKILL.md +3 -0
  184. package/plugins/oh-my-codex/skills/deep-interview/SKILL.md +34 -0
  185. package/plugins/oh-my-codex/skills/ultrawork/SKILL.md +32 -17
  186. package/skills/autopilot/SKILL.md +3 -0
  187. package/skills/deep-interview/SKILL.md +34 -0
  188. package/skills/ultrawork/SKILL.md +32 -17
  189. package/src/scripts/__tests__/codex-native-hook.test.ts +218 -28
  190. package/src/scripts/__tests__/run-test-files.test.ts +138 -2
  191. package/src/scripts/codex-native-hook.ts +80 -10
  192. package/src/scripts/notify-hook/team-worker-stop.ts +58 -18
  193. package/src/scripts/run-test-files.ts +229 -150
  194. package/templates/AGENTS.md +39 -199
  195. package/templates/catalog-manifest.json +7 -0
@@ -309,13 +309,52 @@ describe("codex native hook dispatch", () => {
309
309
  it("does not treat a different module url as the main module", () => {
310
310
  assert.equal(isCodexNativeHookMainModule(pathToFileURL("/tmp/omx native/other-script.js").href, "/tmp/omx native/codex-native-hook.js"), false);
311
311
  });
312
- it("emits schema-safe JSON stdout when CLI stdin is malformed", () => {
313
- const stdout = runNativeHookCli("{");
314
- const output = parseSingleJsonStdout(stdout);
315
- assert.equal(output.continue, false);
316
- assert.equal(output.stopReason, "native_hook_stdin_parse_error");
317
- assert.equal(output.hookSpecificOutput, undefined);
318
- assert.match(String(output.systemMessage ?? ""), /stdin JSON parsing failed inside codex-native-hook:/);
312
+ it("emits Stop-schema-safe block JSON when unidentifiable malformed stdin has native Stop runtime surface", async () => {
313
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-cli-malformed-stop-surface-"));
314
+ try {
315
+ await mkdir(join(cwd, ".omx"), { recursive: true });
316
+ const result = spawnSync(process.execPath, [nativeHookScriptPath()], {
317
+ cwd,
318
+ input: "{",
319
+ encoding: "utf-8",
320
+ stdio: ["pipe", "pipe", "pipe"],
321
+ });
322
+ assert.equal(result.status, 0, result.stderr || result.stdout);
323
+ assert.equal(result.stderr, "");
324
+ const output = parseSingleJsonStdout(result.stdout);
325
+ assert.equal(output.decision, "block");
326
+ assert.equal(output.continue, undefined);
327
+ assert.equal(output.reason, "OMX native hook received malformed JSON input. Preserve runtime state, inspect the emitting hook payload yourself, and retry with valid JSON.");
328
+ assert.equal(output.stopReason, "native_hook_stdin_parse_error");
329
+ assert.equal(output.hookSpecificOutput, undefined);
330
+ assert.match(String(output.systemMessage ?? ""), /stdin JSON parsing failed inside codex-native-hook:/);
331
+ }
332
+ finally {
333
+ await rm(cwd, { recursive: true, force: true });
334
+ }
335
+ });
336
+ it("preserves non-Stop fail-closed JSON when malformed stdin identifies a non-Stop hook", async () => {
337
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-cli-malformed-nonstop-"));
338
+ try {
339
+ await mkdir(join(cwd, ".omx"), { recursive: true });
340
+ const result = spawnSync(process.execPath, [nativeHookScriptPath()], {
341
+ cwd,
342
+ input: '{hook_event_name:"PreToolUse",',
343
+ encoding: "utf-8",
344
+ stdio: ["pipe", "pipe", "pipe"],
345
+ });
346
+ assert.equal(result.status, 0, result.stderr || result.stdout);
347
+ assert.equal(result.stderr, "");
348
+ const output = parseSingleJsonStdout(result.stdout);
349
+ assert.equal(output.continue, false);
350
+ assert.equal(output.decision, undefined);
351
+ assert.equal(output.stopReason, "native_hook_stdin_parse_error");
352
+ assert.equal(output.hookSpecificOutput, undefined);
353
+ assert.match(String(output.systemMessage ?? ""), /stdin JSON parsing failed inside codex-native-hook:/);
354
+ }
355
+ finally {
356
+ await rm(cwd, { recursive: true, force: true });
357
+ }
319
358
  });
320
359
  it("redacts unterminated prompt-like malformed stdin fields", async () => {
321
360
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-cli-malformed-unterminated-"));
@@ -4194,7 +4233,7 @@ case "$1" in
4194
4233
  printf '%%1\\tcodex\\tcodex\\n'
4195
4234
  ;;
4196
4235
  display-message)
4197
- printf '80\\t24\\n'
4236
+ printf '200\\t60\\n'
4198
4237
  ;;
4199
4238
  split-window)
4200
4239
  printf '%%9\\n'
@@ -4316,7 +4355,7 @@ case "$1" in
4316
4355
  printf '%%2\tnode\texec env OMX_TMUX_HUD_OWNER='"'"'1'"'"' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='"'"'%%1'"'"' /node /omx.js hud --watch\n'
4317
4356
  ;;
4318
4357
  display-message)
4319
- printf '80\t24\n'
4358
+ printf '200\t60\n'
4320
4359
  ;;
4321
4360
  resize-pane)
4322
4361
  ;;
@@ -4876,6 +4915,66 @@ exit 0
4876
4915
  await rm(cwd, { recursive: true, force: true });
4877
4916
  }
4878
4917
  });
4918
+ it("allows null-device fd redirects while deep-interview blocks real Bash writes", async () => {
4919
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-deep-interview-null-redirect-"));
4920
+ try {
4921
+ const stateDir = join(cwd, ".omx", "state");
4922
+ const sessionDir = join(stateDir, "sessions", "sess-di-null-redirect");
4923
+ await mkdir(sessionDir, { recursive: true });
4924
+ await writeJson(join(stateDir, "session.json"), { session_id: "sess-di-null-redirect", cwd });
4925
+ await writeJson(join(sessionDir, "skill-active-state.json"), {
4926
+ version: 1,
4927
+ active: true,
4928
+ skill: "deep-interview",
4929
+ phase: "planning",
4930
+ session_id: "sess-di-null-redirect",
4931
+ active_skills: [{ skill: "deep-interview", phase: "planning", active: true, session_id: "sess-di-null-redirect" }],
4932
+ });
4933
+ await writeJson(join(sessionDir, "deep-interview-state.json"), {
4934
+ active: true,
4935
+ mode: "deep-interview",
4936
+ current_phase: "intent-first",
4937
+ session_id: "sess-di-null-redirect",
4938
+ });
4939
+ const allowedCommands = [
4940
+ "find application -type d -name 'bug-tracking*' 2>/dev/null | head -20",
4941
+ "find application -type d -name 'bug-tracking*' 2> /dev/null | head -20",
4942
+ "find application -type d -name 'bug-tracking*' 2>NUL | head -20",
4943
+ "find application -type d -name 'bug-tracking*' 1>/dev/null",
4944
+ "find application -type d -name 'bug-tracking*' &>/dev/null",
4945
+ ];
4946
+ for (const [index, command] of allowedCommands.entries()) {
4947
+ const result = await dispatchCodexNativeHook({
4948
+ hook_event_name: "PreToolUse",
4949
+ cwd,
4950
+ session_id: "sess-di-null-redirect",
4951
+ tool_name: "Bash",
4952
+ tool_use_id: `tool-di-null-redirect-${index}`,
4953
+ tool_input: { command },
4954
+ }, { cwd });
4955
+ assert.equal(result.outputJson, null, command);
4956
+ }
4957
+ const blockedCommands = [
4958
+ "find application -type d -name 'bug-tracking*' 2>errors.log | head -20",
4959
+ "find application -type d -name 'bug-tracking*' > /tmp/bug-tracking.txt",
4960
+ "find application -type d -name 'bug-tracking*' | tee /dev/null",
4961
+ ];
4962
+ for (const [index, command] of blockedCommands.entries()) {
4963
+ const result = await dispatchCodexNativeHook({
4964
+ hook_event_name: "PreToolUse",
4965
+ cwd,
4966
+ session_id: "sess-di-null-redirect",
4967
+ tool_name: "Bash",
4968
+ tool_use_id: `tool-di-real-redirect-${index}`,
4969
+ tool_input: { command },
4970
+ }, { cwd });
4971
+ assert.equal(result.outputJson?.decision, "block", command);
4972
+ }
4973
+ }
4974
+ finally {
4975
+ await rm(cwd, { recursive: true, force: true });
4976
+ }
4977
+ });
4879
4978
  it("allows implementation tools after an explicit deep-interview handoff deactivates the mode", async () => {
4880
4979
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-deep-interview-handoff-"));
4881
4980
  try {
@@ -6890,6 +6989,43 @@ exit 0
6890
6989
  await rm(cwd, { recursive: true, force: true });
6891
6990
  }
6892
6991
  });
6992
+ it("suppresses parent Autopilot Stop continuation in side conversations", async () => {
6993
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-autopilot-side-conversation-"));
6994
+ try {
6995
+ const stateDir = join(cwd, ".omx", "state");
6996
+ const sessionId = "sess-stop-autopilot-side-conversation";
6997
+ const transcriptPath = join(cwd, "side-conversation-rollout.jsonl");
6998
+ await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
6999
+ await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), {
7000
+ active: true,
7001
+ mode: "autopilot",
7002
+ current_phase: "deep-interview",
7003
+ });
7004
+ await writeFile(transcriptPath, `${JSON.stringify({
7005
+ type: "message",
7006
+ role: "user",
7007
+ content: [
7008
+ "Side conversation boundary.",
7009
+ "Everything before this boundary is inherited history from the parent thread. It is reference context only. It is not your current task.",
7010
+ "Only messages submitted after this boundary are active user instructions for this side conversation.",
7011
+ "You are a side-conversation assistant, separate from the main thread.",
7012
+ ].join("\n\n"),
7013
+ })}\n`, "utf-8");
7014
+ const result = await dispatchCodexNativeHook({
7015
+ hook_event_name: "Stop",
7016
+ cwd,
7017
+ session_id: sessionId,
7018
+ thread_id: "thread-stop-autopilot-side-conversation",
7019
+ transcript_path: transcriptPath,
7020
+ last_assistant_message: "Waiting for a new side-conversation question.",
7021
+ }, { cwd });
7022
+ assert.equal(result.omxEventName, "stop");
7023
+ assert.equal(result.outputJson, null);
7024
+ }
7025
+ finally {
7026
+ await rm(cwd, { recursive: true, force: true });
7027
+ }
7028
+ });
6893
7029
  it("requires Autopilot code review after a compact-boundary Stop exemption", async () => {
6894
7030
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-autopilot-review-compact-"));
6895
7031
  try {
@@ -7502,7 +7638,7 @@ exit 0
7502
7638
  await rm(cwd, { recursive: true, force: true });
7503
7639
  }
7504
7640
  });
7505
- it("queues worker Stop leader nudge with Tab and submit when leader pane is busy", async () => {
7641
+ it("steers worker Stop leader nudge directly when leader pane is busy", async () => {
7506
7642
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-busy-leader-"));
7507
7643
  const prevTeamWorker = process.env.OMX_TEAM_WORKER;
7508
7644
  const prevTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
@@ -7561,11 +7697,11 @@ exit 0
7561
7697
  assert.equal(result.outputJson, null);
7562
7698
  const tmuxLog = await readFile(tmuxLogPath, "utf-8");
7563
7699
  assert.match(tmuxLog, /send-keys -t %42 -l \[OMX\] worker-1 native Stop allowed/);
7564
- assert.match(tmuxLog, /send-keys -t %42 Tab/);
7565
- assert.match(tmuxLog, /send-keys -t %42 C-m/);
7566
- assert.ok(tmuxLog.indexOf("send-keys -t %42 Tab") < tmuxLog.indexOf("send-keys -t %42 C-m"), "busy worker-stop nudge should press Tab before C-m");
7700
+ assert.doesNotMatch(tmuxLog, /send-keys -t %42 Tab/);
7701
+ const submits = tmuxLog.match(/send-keys -t %42 C-m/g) || [];
7702
+ assert.equal(submits.length, 2, "busy worker-stop nudge should submit directly as steering, not queue via Tab");
7567
7703
  const nudgeState = JSON.parse(await readFile(join(workerDir, "worker-stop-nudge.json"), "utf-8"));
7568
- assert.equal(nudgeState.delivery, "queued");
7704
+ assert.equal(nudgeState.delivery, "steered");
7569
7705
  }
7570
7706
  finally {
7571
7707
  if (typeof prevTeamWorker === "string")
@@ -7705,6 +7841,14 @@ exit 0
7705
7841
  });
7706
7842
  assert.equal(shutdownResult.result, "team_state_gone_or_shutdown");
7707
7843
  assert.equal(existsSync(join(stateDir, "team", "shutdown-team", "worker-stop-nudge.json")), false);
7844
+ const deliveryLogPath = join(logsDir, `team-delivery-${new Date().toISOString().split("T")[0]}.jsonl`);
7845
+ const deliveryEvents = (await readFile(deliveryLogPath, "utf-8"))
7846
+ .trim()
7847
+ .split("\n")
7848
+ .map((line) => JSON.parse(line));
7849
+ const suppressedEvents = deliveryEvents.filter((event) => event.reason === "team_state_gone_or_shutdown");
7850
+ assert.equal(suppressedEvents.length, 2, "late closed-team Stop nudges should be diagnostics, not queued prompts");
7851
+ assert.equal(suppressedEvents.every((event) => event.result === "suppressed" && event.transport === "none"), true);
7708
7852
  }
7709
7853
  finally {
7710
7854
  await rm(cwd, { recursive: true, force: true });
@@ -7739,13 +7883,13 @@ exit 0
7739
7883
  logsDir,
7740
7884
  workerContext: { teamName, workerName: "worker-2" },
7741
7885
  });
7742
- assert.equal(result.result, "queued");
7886
+ assert.equal(result.result, "steered");
7743
7887
  const tmuxLog = await readFile(tmuxLogPath, "utf-8");
7744
7888
  assert.match(tmuxLog, /send-keys -t %42 -l \[OMX\] worker-2 native Stop allowed/);
7745
- assert.match(tmuxLog, /send-keys -t %42 Tab/);
7889
+ assert.doesNotMatch(tmuxLog, /send-keys -t %42 Tab/);
7746
7890
  const teamNudgeState = JSON.parse(await readFile(join(teamDir, "worker-stop-nudge.json"), "utf-8"));
7747
7891
  assert.equal(teamNudgeState.worker, "worker-2");
7748
- assert.equal(teamNudgeState.delivery, "queued");
7892
+ assert.equal(teamNudgeState.delivery, "steered");
7749
7893
  }
7750
7894
  finally {
7751
7895
  if (typeof prevPath === "string")
@@ -7875,6 +8019,15 @@ exit 0
7875
8019
  assert.equal(existsSync(teamDir), false, "deferred worker Stop recording must not recreate removed team state");
7876
8020
  const tmuxLog = await readFile(tmuxLogPath, "utf-8");
7877
8021
  assert.doesNotMatch(tmuxLog, /send-keys -t %42 -l \[OMX\] worker-1 native Stop allowed/);
8022
+ const deliveryLogPath = join(logsDir, `team-delivery-${new Date().toISOString().split("T")[0]}.jsonl`);
8023
+ const deliveryEvents = (await readFile(deliveryLogPath, "utf-8"))
8024
+ .trim()
8025
+ .split("\n")
8026
+ .map((line) => JSON.parse(line));
8027
+ assert.equal(deliveryEvents.some((event) => event.team === teamName
8028
+ && event.result === "suppressed"
8029
+ && event.transport === "none"
8030
+ && event.reason === "team_state_gone_or_shutdown"), true, "teardown-race worker Stop nudges should be diagnostic suppression events, not queued prompts");
7878
8031
  }
7879
8032
  finally {
7880
8033
  if (typeof prevPath === "string")