oh-my-codex 0.18.2 → 0.18.3

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 (169) hide show
  1. package/Cargo.toml +1 -1
  2. package/dist/agents/__tests__/definitions.test.js +9 -0
  3. package/dist/agents/__tests__/definitions.test.js.map +1 -1
  4. package/dist/agents/__tests__/native-config.test.js +1 -0
  5. package/dist/agents/__tests__/native-config.test.js.map +1 -1
  6. package/dist/agents/definitions.d.ts.map +1 -1
  7. package/dist/agents/definitions.js +10 -0
  8. package/dist/agents/definitions.js.map +1 -1
  9. package/dist/auth/__tests__/config-sessions.test.d.ts +2 -0
  10. package/dist/auth/__tests__/config-sessions.test.d.ts.map +1 -0
  11. package/dist/auth/__tests__/config-sessions.test.js +48 -0
  12. package/dist/auth/__tests__/config-sessions.test.js.map +1 -0
  13. package/dist/auth/__tests__/quota-rotation.test.d.ts +2 -0
  14. package/dist/auth/__tests__/quota-rotation.test.d.ts.map +1 -0
  15. package/dist/auth/__tests__/quota-rotation.test.js +33 -0
  16. package/dist/auth/__tests__/quota-rotation.test.js.map +1 -0
  17. package/dist/auth/__tests__/redact.test.d.ts +2 -0
  18. package/dist/auth/__tests__/redact.test.d.ts.map +1 -0
  19. package/dist/auth/__tests__/redact.test.js +20 -0
  20. package/dist/auth/__tests__/redact.test.js.map +1 -0
  21. package/dist/auth/__tests__/storage.test.d.ts +2 -0
  22. package/dist/auth/__tests__/storage.test.d.ts.map +1 -0
  23. package/dist/auth/__tests__/storage.test.js +108 -0
  24. package/dist/auth/__tests__/storage.test.js.map +1 -0
  25. package/dist/auth/config.d.ts +9 -0
  26. package/dist/auth/config.d.ts.map +1 -0
  27. package/dist/auth/config.js +77 -0
  28. package/dist/auth/config.js.map +1 -0
  29. package/dist/auth/hotswap.d.ts +36 -0
  30. package/dist/auth/hotswap.d.ts.map +1 -0
  31. package/dist/auth/hotswap.js +159 -0
  32. package/dist/auth/hotswap.js.map +1 -0
  33. package/dist/auth/index.d.ts +8 -0
  34. package/dist/auth/index.d.ts.map +1 -0
  35. package/dist/auth/index.js +8 -0
  36. package/dist/auth/index.js.map +1 -0
  37. package/dist/auth/paths.d.ts +12 -0
  38. package/dist/auth/paths.d.ts.map +1 -0
  39. package/dist/auth/paths.js +78 -0
  40. package/dist/auth/paths.js.map +1 -0
  41. package/dist/auth/quota-detector.d.ts +10 -0
  42. package/dist/auth/quota-detector.d.ts.map +1 -0
  43. package/dist/auth/quota-detector.js +40 -0
  44. package/dist/auth/quota-detector.js.map +1 -0
  45. package/dist/auth/redact.d.ts +2 -0
  46. package/dist/auth/redact.d.ts.map +1 -0
  47. package/dist/auth/redact.js +26 -0
  48. package/dist/auth/redact.js.map +1 -0
  49. package/dist/auth/rotation.d.ts +9 -0
  50. package/dist/auth/rotation.d.ts.map +1 -0
  51. package/dist/auth/rotation.js +26 -0
  52. package/dist/auth/rotation.js.map +1 -0
  53. package/dist/auth/sessions.d.ts +15 -0
  54. package/dist/auth/sessions.d.ts.map +1 -0
  55. package/dist/auth/sessions.js +62 -0
  56. package/dist/auth/sessions.js.map +1 -0
  57. package/dist/auth/storage.d.ts +27 -0
  58. package/dist/auth/storage.d.ts.map +1 -0
  59. package/dist/auth/storage.js +111 -0
  60. package/dist/auth/storage.js.map +1 -0
  61. package/dist/cli/__tests__/auth.test.d.ts +2 -0
  62. package/dist/cli/__tests__/auth.test.d.ts.map +1 -0
  63. package/dist/cli/__tests__/auth.test.js +168 -0
  64. package/dist/cli/__tests__/auth.test.js.map +1 -0
  65. package/dist/cli/__tests__/doctor-warning-copy.test.js +51 -0
  66. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  67. package/dist/cli/__tests__/explore.test.js +20 -0
  68. package/dist/cli/__tests__/explore.test.js.map +1 -1
  69. package/dist/cli/__tests__/index.test.js +10 -0
  70. package/dist/cli/__tests__/index.test.js.map +1 -1
  71. package/dist/cli/__tests__/nested-help-routing.test.js +1 -0
  72. package/dist/cli/__tests__/nested-help-routing.test.js.map +1 -1
  73. package/dist/cli/__tests__/setup-agents-overwrite.test.js +30 -1
  74. package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
  75. package/dist/cli/__tests__/setup-install-mode.test.js +47 -0
  76. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  77. package/dist/cli/auth.d.ts +4 -0
  78. package/dist/cli/auth.d.ts.map +1 -0
  79. package/dist/cli/auth.js +89 -0
  80. package/dist/cli/auth.js.map +1 -0
  81. package/dist/cli/doctor.d.ts.map +1 -1
  82. package/dist/cli/doctor.js +19 -7
  83. package/dist/cli/doctor.js.map +1 -1
  84. package/dist/cli/explore.d.ts.map +1 -1
  85. package/dist/cli/explore.js +12 -0
  86. package/dist/cli/explore.js.map +1 -1
  87. package/dist/cli/index.d.ts +20 -2
  88. package/dist/cli/index.d.ts.map +1 -1
  89. package/dist/cli/index.js +104 -6
  90. package/dist/cli/index.js.map +1 -1
  91. package/dist/cli/setup.d.ts.map +1 -1
  92. package/dist/cli/setup.js +11 -3
  93. package/dist/cli/setup.js.map +1 -1
  94. package/dist/config/__tests__/deep-interview.test.d.ts +2 -0
  95. package/dist/config/__tests__/deep-interview.test.d.ts.map +1 -0
  96. package/dist/config/__tests__/deep-interview.test.js +239 -0
  97. package/dist/config/__tests__/deep-interview.test.js.map +1 -0
  98. package/dist/config/__tests__/generator-idempotent.test.js +123 -0
  99. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  100. package/dist/config/deep-interview.d.ts +22 -0
  101. package/dist/config/deep-interview.d.ts.map +1 -0
  102. package/dist/config/deep-interview.js +151 -0
  103. package/dist/config/deep-interview.js.map +1 -0
  104. package/dist/config/generator.d.ts +5 -2
  105. package/dist/config/generator.d.ts.map +1 -1
  106. package/dist/config/generator.js +106 -36
  107. package/dist/config/generator.js.map +1 -1
  108. package/dist/hooks/__tests__/agents-overlay.test.js +2 -0
  109. package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
  110. package/dist/hooks/__tests__/explore-routing.test.js +1 -0
  111. package/dist/hooks/__tests__/explore-routing.test.js.map +1 -1
  112. package/dist/hooks/__tests__/keyword-detector.test.js +301 -0
  113. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  114. package/dist/hooks/deep-interview-config-instruction.d.ts +3 -0
  115. package/dist/hooks/deep-interview-config-instruction.d.ts.map +1 -0
  116. package/dist/hooks/deep-interview-config-instruction.js +47 -0
  117. package/dist/hooks/deep-interview-config-instruction.js.map +1 -0
  118. package/dist/hooks/explore-routing.d.ts.map +1 -1
  119. package/dist/hooks/explore-routing.js +1 -0
  120. package/dist/hooks/explore-routing.js.map +1 -1
  121. package/dist/hooks/keyword-detector.d.ts +5 -0
  122. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  123. package/dist/hooks/keyword-detector.js +52 -8
  124. package/dist/hooks/keyword-detector.js.map +1 -1
  125. package/dist/hud/__tests__/hud-tmux-injection.test.js +4 -4
  126. package/dist/hud/__tests__/hud-tmux-injection.test.js.map +1 -1
  127. package/dist/hud/__tests__/reconcile.test.js +94 -9
  128. package/dist/hud/__tests__/reconcile.test.js.map +1 -1
  129. package/dist/hud/__tests__/tmux.test.js +103 -1
  130. package/dist/hud/__tests__/tmux.test.js.map +1 -1
  131. package/dist/hud/reconcile.d.ts +1 -1
  132. package/dist/hud/reconcile.d.ts.map +1 -1
  133. package/dist/hud/reconcile.js +8 -0
  134. package/dist/hud/reconcile.js.map +1 -1
  135. package/dist/hud/tmux.d.ts +7 -0
  136. package/dist/hud/tmux.d.ts.map +1 -1
  137. package/dist/hud/tmux.js +46 -9
  138. package/dist/hud/tmux.js.map +1 -1
  139. package/dist/question/deep-interview.d.ts +2 -0
  140. package/dist/question/deep-interview.d.ts.map +1 -1
  141. package/dist/question/deep-interview.js.map +1 -1
  142. package/dist/scripts/__tests__/codex-native-hook.test.js +387 -0
  143. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  144. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  145. package/dist/scripts/codex-native-hook.js +24 -2
  146. package/dist/scripts/codex-native-hook.js.map +1 -1
  147. package/dist/state/__tests__/planning-gate.test.d.ts +2 -0
  148. package/dist/state/__tests__/planning-gate.test.d.ts.map +1 -0
  149. package/dist/state/__tests__/planning-gate.test.js +219 -0
  150. package/dist/state/__tests__/planning-gate.test.js.map +1 -0
  151. package/dist/state/workflow-transition.d.ts +23 -0
  152. package/dist/state/workflow-transition.d.ts.map +1 -1
  153. package/dist/state/workflow-transition.js +63 -0
  154. package/dist/state/workflow-transition.js.map +1 -1
  155. package/dist/team/__tests__/tmux-session.test.js +86 -0
  156. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  157. package/dist/team/tmux-session.d.ts.map +1 -1
  158. package/dist/team/tmux-session.js +7 -0
  159. package/dist/team/tmux-session.js.map +1 -1
  160. package/package.json +1 -1
  161. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  162. package/plugins/oh-my-codex/skills/deep-interview/SKILL.md +10 -0
  163. package/plugins/oh-my-codex/skills/ralplan/SKILL.md +6 -2
  164. package/prompts/scholastic.md +11 -0
  165. package/skills/deep-interview/SKILL.md +10 -0
  166. package/skills/ralplan/SKILL.md +6 -2
  167. package/src/scripts/__tests__/codex-native-hook.test.ts +485 -0
  168. package/src/scripts/codex-native-hook.ts +23 -1
  169. package/templates/catalog-manifest.json +5 -0
@@ -14,6 +14,7 @@ import { writeSessionStart } from "../../hooks/session.js";
14
14
  import { resetTriageConfigCache } from "../../hooks/triage-config.js";
15
15
  import { executeStateOperation } from "../../state/operations.js";
16
16
  import { OMX_TMUX_HUD_OWNER_ENV } from "../../hud/reconcile.js";
17
+ import { OMX_TMUX_HUD_LEADER_PANE_ENV } from "../../hud/tmux.js";
17
18
  import { readAllState } from "../../hud/state.js";
18
19
  import { renderHud } from "../../hud/render.js";
19
20
  import { getLegacyWikiDir, serializePage, writePage } from "../../wiki/storage.js";
@@ -42,6 +43,21 @@ async function writeJson(path, value) {
42
43
  await mkdir(dirname(path), { recursive: true }).catch(() => { });
43
44
  await writeFile(path, JSON.stringify(value, null, 2));
44
45
  }
46
+ async function withIsolatedHome(prefix, run) {
47
+ const homeDir = await mkdtemp(join(tmpdir(), `omx-native-hook-home-${prefix}-`));
48
+ const previousHome = process.env.HOME;
49
+ try {
50
+ process.env.HOME = homeDir;
51
+ return await run(homeDir);
52
+ }
53
+ finally {
54
+ if (typeof previousHome === "string")
55
+ process.env.HOME = previousHome;
56
+ else
57
+ delete process.env.HOME;
58
+ await rm(homeDir, { recursive: true, force: true });
59
+ }
60
+ }
45
61
  async function withLoreGuardConfig(value, prefix, run) {
46
62
  const cwd = await mkdtemp(join(tmpdir(), `omx-native-hook-pretool-git-commit-lore-${prefix}-`));
47
63
  const codexHome = await mkdtemp(join(tmpdir(), `omx-native-hook-codex-home-lore-${prefix}-`));
@@ -1291,6 +1307,308 @@ describe("codex native hook dispatch", () => {
1291
1307
  await rm(cwd, { recursive: true, force: true });
1292
1308
  }
1293
1309
  });
1310
+ it("injects deep-interview config overrides into UserPromptSubmit developer context", async () => {
1311
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-deep-interview-config-"));
1312
+ try {
1313
+ await mkdir(join(cwd, ".omx", "state"), { recursive: true });
1314
+ await writeFile(join(cwd, ".omx", "config.toml"), `[omx.deepInterview]
1315
+ defaultProfile = "standard"
1316
+ standardThreshold = 0.05
1317
+ standardMaxRounds = 15
1318
+ enableChallengeModes = false
1319
+ `);
1320
+ const result = await dispatchCodexNativeHook({
1321
+ hook_event_name: "UserPromptSubmit",
1322
+ cwd,
1323
+ session_id: "sess-deep-interview-config",
1324
+ thread_id: "thread-1",
1325
+ turn_id: "turn-1",
1326
+ prompt: "$deep-interview prove config reflection",
1327
+ }, { cwd });
1328
+ assert.equal(result.omxEventName, "keyword-detector");
1329
+ assert.equal(result.skillState?.skill, "deep-interview");
1330
+ const serializedOutput = JSON.stringify(result.outputJson);
1331
+ assert.match(serializedOutput, /Deep-interview config override active/);
1332
+ assert.match(serializedOutput, /threshold=0\.05/);
1333
+ assert.match(serializedOutput, /max_rounds=15/);
1334
+ assert.match(serializedOutput, /enableChallengeModes=false/);
1335
+ const modeState = JSON.parse(await readFile(join(cwd, ".omx", "state", "sessions", "sess-deep-interview-config", "deep-interview-state.json"), "utf-8"));
1336
+ assert.equal(modeState.profile, "standard");
1337
+ assert.equal(modeState.threshold, 0.05);
1338
+ assert.equal(modeState.max_rounds, 15);
1339
+ }
1340
+ finally {
1341
+ await rm(cwd, { recursive: true, force: true });
1342
+ }
1343
+ });
1344
+ it("proves UserPromptSubmit context changes before and after adding deep-interview config", async () => {
1345
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-deep-interview-config-before-after-"));
1346
+ const sessionId = "sess-deep-interview-config-before-after";
1347
+ try {
1348
+ await mkdir(join(cwd, ".omx", "state"), { recursive: true });
1349
+ const before = await withIsolatedHome("deep-interview-config-before-after", async () => (dispatchCodexNativeHook({
1350
+ hook_event_name: "UserPromptSubmit",
1351
+ cwd,
1352
+ session_id: sessionId,
1353
+ thread_id: "thread-before-after",
1354
+ turn_id: "turn-before",
1355
+ prompt: "$deep-interview prove before config context",
1356
+ }, { cwd })));
1357
+ const beforeOutput = JSON.stringify(before.outputJson);
1358
+ const beforeState = JSON.parse(await readFile(join(cwd, ".omx", "state", "sessions", sessionId, "deep-interview-state.json"), "utf-8"));
1359
+ assert.equal(before.skillState?.skill, "deep-interview");
1360
+ assert.doesNotMatch(beforeOutput, /Deep-interview config override active/);
1361
+ assert.equal(before.skillState?.deep_interview_config, undefined);
1362
+ assert.equal(beforeState.deep_interview_config, undefined);
1363
+ assert.equal(beforeState.threshold, undefined);
1364
+ assert.equal(beforeState.max_rounds, undefined);
1365
+ await writeFile(join(cwd, ".omx", "config.toml"), `[omx.deepInterview]
1366
+ defaultProfile = "standard"
1367
+ standardThreshold = 0.05
1368
+ standardMaxRounds = 15
1369
+ `);
1370
+ const after = await dispatchCodexNativeHook({
1371
+ hook_event_name: "UserPromptSubmit",
1372
+ cwd,
1373
+ session_id: sessionId,
1374
+ thread_id: "thread-before-after",
1375
+ turn_id: "turn-after",
1376
+ prompt: "$deep-interview prove after config context",
1377
+ }, { cwd });
1378
+ const afterOutput = JSON.stringify(after.outputJson);
1379
+ const afterState = JSON.parse(await readFile(join(cwd, ".omx", "state", "sessions", sessionId, "deep-interview-state.json"), "utf-8"));
1380
+ assert.equal(after.skillState?.deep_interview_config?.profile, "standard");
1381
+ assert.match(afterOutput, /Deep-interview config override active/);
1382
+ assert.match(afterOutput, /threshold=0\.05/);
1383
+ assert.match(afterOutput, /max_rounds=15/);
1384
+ assert.equal(afterState.deep_interview_config?.profile, "standard");
1385
+ assert.equal(afterState.threshold, 0.05);
1386
+ assert.equal(afterState.max_rounds, 15);
1387
+ }
1388
+ finally {
1389
+ await rm(cwd, { recursive: true, force: true });
1390
+ }
1391
+ });
1392
+ it("injects deep-interview config for mixed workflow prompts that defer execution modes", async () => {
1393
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-deep-interview-config-mixed-"));
1394
+ const sessionId = "sess-deep-interview-config-mixed";
1395
+ try {
1396
+ await mkdir(join(cwd, ".omx", "state"), { recursive: true });
1397
+ await writeFile(join(cwd, ".omx", "config.toml"), `[omx.deepInterview]
1398
+ defaultProfile = "deep"
1399
+ deepThreshold = 0.13
1400
+ deepMaxRounds = 21
1401
+ enableChallengeModes = false
1402
+ `);
1403
+ const result = await withIsolatedHome("deep-interview-config-mixed", async () => (dispatchCodexNativeHook({
1404
+ hook_event_name: "UserPromptSubmit",
1405
+ cwd,
1406
+ session_id: sessionId,
1407
+ thread_id: "thread-mixed-config",
1408
+ turn_id: "turn-mixed-config",
1409
+ prompt: "$autopilot $deep-interview prove mixed config context",
1410
+ }, { cwd })));
1411
+ const serializedOutput = JSON.stringify(result.outputJson);
1412
+ const modeState = JSON.parse(await readFile(join(cwd, ".omx", "state", "sessions", sessionId, "deep-interview-state.json"), "utf-8"));
1413
+ assert.equal(result.skillState?.skill, "deep-interview");
1414
+ assert.deepEqual(result.skillState?.deferred_skills, ["autopilot"]);
1415
+ assert.equal(result.skillState?.deep_interview_config?.profile, "deep");
1416
+ assert.equal(result.skillState?.deep_interview_config?.threshold, 0.13);
1417
+ assert.equal(result.skillState?.deep_interview_config?.maxRounds, 21);
1418
+ assert.equal(result.skillState?.deep_interview_config?.enableChallengeModes, false);
1419
+ assert.match(serializedOutput, /Deep-interview config override active/);
1420
+ assert.match(serializedOutput, /profile=deep/);
1421
+ assert.match(serializedOutput, /threshold=0\.13/);
1422
+ assert.match(serializedOutput, /max_rounds=21/);
1423
+ assert.match(serializedOutput, /enableChallengeModes=false/);
1424
+ assert.equal(modeState.deep_interview_config?.profile, "deep");
1425
+ assert.equal(modeState.profile, "deep");
1426
+ assert.equal(modeState.threshold, 0.13);
1427
+ assert.equal(modeState.max_rounds, 21);
1428
+ assert.equal(modeState.enable_challenge_modes, false);
1429
+ }
1430
+ finally {
1431
+ await rm(cwd, { recursive: true, force: true });
1432
+ }
1433
+ });
1434
+ it("keeps deep-interview config override context on continuation prompts", async () => {
1435
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-deep-interview-config-continuation-"));
1436
+ const sessionId = "sess-deep-interview-config-continuation";
1437
+ try {
1438
+ await mkdir(join(cwd, ".omx", "state"), { recursive: true });
1439
+ await writeFile(join(cwd, ".omx", "config.toml"), `[omx.deepInterview]
1440
+ defaultProfile = "standard"
1441
+ standardThreshold = 0.05
1442
+ standardMaxRounds = 15
1443
+ `);
1444
+ await dispatchCodexNativeHook({
1445
+ hook_event_name: "UserPromptSubmit",
1446
+ cwd,
1447
+ session_id: sessionId,
1448
+ thread_id: "thread-continuation",
1449
+ turn_id: "turn-start",
1450
+ prompt: "$deep-interview prove config continuation",
1451
+ }, { cwd });
1452
+ const continued = await dispatchCodexNativeHook({
1453
+ hook_event_name: "UserPromptSubmit",
1454
+ cwd,
1455
+ session_id: sessionId,
1456
+ thread_id: "thread-continuation",
1457
+ turn_id: "turn-continue",
1458
+ prompt: "continue",
1459
+ }, { cwd });
1460
+ const serializedOutput = JSON.stringify(continued.outputJson);
1461
+ const modeState = JSON.parse(await readFile(join(cwd, ".omx", "state", "sessions", sessionId, "deep-interview-state.json"), "utf-8"));
1462
+ assert.equal(continued.skillState?.skill, "deep-interview");
1463
+ assert.match(serializedOutput, /Deep-interview config override active/);
1464
+ assert.match(serializedOutput, /threshold=0\.05/);
1465
+ assert.match(serializedOutput, /max_rounds=15/);
1466
+ assert.equal(modeState.profile, "standard");
1467
+ assert.equal(modeState.threshold, 0.05);
1468
+ assert.equal(modeState.max_rounds, 15);
1469
+ }
1470
+ finally {
1471
+ await rm(cwd, { recursive: true, force: true });
1472
+ }
1473
+ });
1474
+ it("keeps explicit deep-interview profile flags reflected on continuation prompts", async () => {
1475
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-deep-interview-config-profile-continuation-"));
1476
+ const sessionId = "sess-deep-interview-config-profile-continuation";
1477
+ try {
1478
+ await mkdir(join(cwd, ".omx", "state"), { recursive: true });
1479
+ await writeFile(join(cwd, ".omx", "config.toml"), `[omx.deepInterview]
1480
+ defaultProfile = "standard"
1481
+ standardThreshold = 0.22
1482
+ standardMaxRounds = 13
1483
+ deepThreshold = 0.13
1484
+ deepMaxRounds = 21
1485
+ `);
1486
+ await dispatchCodexNativeHook({
1487
+ hook_event_name: "UserPromptSubmit",
1488
+ cwd,
1489
+ session_id: sessionId,
1490
+ thread_id: "thread-profile-continuation",
1491
+ turn_id: "turn-start",
1492
+ prompt: "$deep-interview --deep prove explicit profile continuation",
1493
+ }, { cwd });
1494
+ const continued = await dispatchCodexNativeHook({
1495
+ hook_event_name: "UserPromptSubmit",
1496
+ cwd,
1497
+ session_id: sessionId,
1498
+ thread_id: "thread-profile-continuation",
1499
+ turn_id: "turn-continue",
1500
+ prompt: "continue",
1501
+ }, { cwd });
1502
+ const serializedOutput = JSON.stringify(continued.outputJson);
1503
+ const modeState = JSON.parse(await readFile(join(cwd, ".omx", "state", "sessions", sessionId, "deep-interview-state.json"), "utf-8"));
1504
+ assert.equal(continued.skillState?.skill, "deep-interview");
1505
+ assert.equal(continued.skillState?.deep_interview_config?.profile, "deep");
1506
+ assert.match(serializedOutput, /Deep-interview config override active/);
1507
+ assert.match(serializedOutput, /profile=deep/);
1508
+ assert.match(serializedOutput, /threshold=0\.13/);
1509
+ assert.match(serializedOutput, /max_rounds=21/);
1510
+ assert.equal(modeState.deep_interview_config?.profile, "deep");
1511
+ assert.equal(modeState.profile, "deep");
1512
+ assert.equal(modeState.threshold, 0.13);
1513
+ assert.equal(modeState.max_rounds, 21);
1514
+ }
1515
+ finally {
1516
+ await rm(cwd, { recursive: true, force: true });
1517
+ }
1518
+ });
1519
+ it("keeps the documented deep-interview Suggested Config reflected in UserPromptSubmit context", async () => {
1520
+ const skillDoc = await readFile(join(process.cwd(), "skills", "deep-interview", "SKILL.md"), "utf-8");
1521
+ const markerIndex = skillDoc.indexOf("## Suggested Config (optional)");
1522
+ assert.notEqual(markerIndex, -1);
1523
+ const configMatch = skillDoc.slice(markerIndex).match(/```toml\n([\s\S]*?)\n```/);
1524
+ assert.ok(configMatch);
1525
+ const documentedConfig = configMatch[1]?.trimEnd();
1526
+ assert.ok(documentedConfig);
1527
+ assert.match(documentedConfig, /standardThreshold = 0\.20/);
1528
+ assert.match(documentedConfig, /standardMaxRounds = 12/);
1529
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-deep-interview-doc-config-"));
1530
+ const sessionId = "sess-deep-interview-doc-config";
1531
+ try {
1532
+ await mkdir(join(cwd, ".omx", "state"), { recursive: true });
1533
+ await writeFile(join(cwd, ".omx", "config.toml"), `${documentedConfig}\n`);
1534
+ const result = await dispatchCodexNativeHook({
1535
+ hook_event_name: "UserPromptSubmit",
1536
+ cwd,
1537
+ session_id: sessionId,
1538
+ thread_id: "thread-doc-config",
1539
+ turn_id: "turn-doc-config",
1540
+ prompt: "$deep-interview prove documented config context",
1541
+ }, { cwd });
1542
+ const serializedOutput = JSON.stringify(result.outputJson);
1543
+ const modeState = JSON.parse(await readFile(join(cwd, ".omx", "state", "sessions", sessionId, "deep-interview-state.json"), "utf-8"));
1544
+ assert.equal(result.skillState?.deep_interview_config?.profile, "standard");
1545
+ assert.equal(result.skillState?.deep_interview_config?.threshold, 0.2);
1546
+ assert.equal(result.skillState?.deep_interview_config?.maxRounds, 12);
1547
+ assert.match(serializedOutput, /Deep-interview config override active/);
1548
+ assert.match(serializedOutput, /profile=standard/);
1549
+ assert.match(serializedOutput, /threshold=0\.2/);
1550
+ assert.match(serializedOutput, /max_rounds=12/);
1551
+ assert.equal(modeState.deep_interview_config?.profile, "standard");
1552
+ assert.equal(modeState.profile, "standard");
1553
+ assert.equal(modeState.threshold, 0.2);
1554
+ assert.equal(modeState.max_rounds, 12);
1555
+ }
1556
+ finally {
1557
+ await rm(cwd, { recursive: true, force: true });
1558
+ }
1559
+ });
1560
+ it("injects deep-interview config overrides when state is boxed under OMX_ROOT", async () => {
1561
+ const root = await mkdtemp(join(tmpdir(), "omx-native-hook-deep-interview-config-boxed-"));
1562
+ const cwd = join(root, "source");
1563
+ const omxRoot = join(root, "box");
1564
+ const sessionId = "sess-boxed-deep-interview-config";
1565
+ const previousOmxRoot = process.env.OMX_ROOT;
1566
+ const previousOmxStateRoot = process.env.OMX_STATE_ROOT;
1567
+ const previousTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
1568
+ try {
1569
+ await mkdir(join(cwd, ".omx", "state"), { recursive: true });
1570
+ await writeFile(join(cwd, ".omx", "config.toml"), `[omx.deepInterview]
1571
+ defaultProfile = "standard"
1572
+ standardThreshold = 0.05
1573
+ standardMaxRounds = 15
1574
+ `);
1575
+ process.env.OMX_ROOT = omxRoot;
1576
+ delete process.env.OMX_STATE_ROOT;
1577
+ delete process.env.OMX_TEAM_STATE_ROOT;
1578
+ const result = await dispatchCodexNativeHook({
1579
+ hook_event_name: "UserPromptSubmit",
1580
+ cwd,
1581
+ session_id: sessionId,
1582
+ thread_id: "thread-boxed",
1583
+ turn_id: "turn-boxed",
1584
+ prompt: "$deep-interview prove boxed config reflection",
1585
+ }, { cwd });
1586
+ assert.equal(result.omxEventName, "keyword-detector");
1587
+ assert.equal(result.skillState?.initialized_state_path, `.omx/state/sessions/${sessionId}/deep-interview-state.json`);
1588
+ const boxedStatePath = join(omxRoot, ".omx", "state", "sessions", sessionId, "deep-interview-state.json");
1589
+ assert.equal(existsSync(boxedStatePath), true);
1590
+ assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", sessionId, "deep-interview-state.json")), false);
1591
+ const serializedOutput = JSON.stringify(result.outputJson);
1592
+ assert.match(serializedOutput, /Deep-interview config override active/);
1593
+ assert.match(serializedOutput, /threshold=0\.05/);
1594
+ assert.match(serializedOutput, /max_rounds=15/);
1595
+ }
1596
+ finally {
1597
+ if (typeof previousOmxRoot === "string")
1598
+ process.env.OMX_ROOT = previousOmxRoot;
1599
+ else
1600
+ delete process.env.OMX_ROOT;
1601
+ if (typeof previousOmxStateRoot === "string")
1602
+ process.env.OMX_STATE_ROOT = previousOmxStateRoot;
1603
+ else
1604
+ delete process.env.OMX_STATE_ROOT;
1605
+ if (typeof previousTeamStateRoot === "string")
1606
+ process.env.OMX_TEAM_STATE_ROOT = previousTeamStateRoot;
1607
+ else
1608
+ delete process.env.OMX_TEAM_STATE_ROOT;
1609
+ await rm(root, { recursive: true, force: true });
1610
+ }
1611
+ });
1294
1612
  it("records boxed keyword activation mode detail and skill state under OMX_ROOT", async () => {
1295
1613
  const root = await mkdtemp(join(tmpdir(), "omx-native-hook-boxed-"));
1296
1614
  const cwd = join(root, "source");
@@ -2925,6 +3243,75 @@ esac
2925
3243
  await rm(cwd, { recursive: true, force: true });
2926
3244
  }
2927
3245
  });
3246
+ it("reuses an existing owner-tagged HUD pane when UserPromptSubmit revives with the canonical session id", async () => {
3247
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-hud-reuse-"));
3248
+ const originalTmux = process.env.TMUX;
3249
+ const originalTmuxPane = process.env.TMUX_PANE;
3250
+ const originalPath = process.env.PATH;
3251
+ const originalHudOwner = process.env[OMX_TMUX_HUD_OWNER_ENV];
3252
+ try {
3253
+ process.env.TMUX = "1";
3254
+ process.env.TMUX_PANE = "%1";
3255
+ process.env[OMX_TMUX_HUD_OWNER_ENV] = "1";
3256
+ const canonicalSessionId = "omx-canonical-hud-reuse";
3257
+ const nativeSessionId = "codex-native-hud-reuse";
3258
+ await mkdir(join(cwd, ".omx", "state", "sessions", canonicalSessionId), { recursive: true });
3259
+ await writeSessionStart(cwd, canonicalSessionId);
3260
+ const binDir = await mkdtemp(join(tmpdir(), "omx-native-hook-hud-reuse-bin-"));
3261
+ const tmuxLog = join(cwd, "tmux.log");
3262
+ await writeFile(join(binDir, "tmux"), `#!/usr/bin/env bash
3263
+ set -euo pipefail
3264
+ printf '%s\n' "$*" >> ${JSON.stringify(tmuxLog)}
3265
+ case "$1" in
3266
+ list-panes)
3267
+ printf '%%1\tcodex\tcodex\n'
3268
+ printf '%%2\tnode\texec env OMX_TMUX_HUD_OWNER='"'"'1'"'"' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='"'"'%%1'"'"' /node /omx.js hud --watch\n'
3269
+ ;;
3270
+ display-message)
3271
+ printf '80\t24\n'
3272
+ ;;
3273
+ resize-pane)
3274
+ ;;
3275
+ split-window)
3276
+ printf '%%9\n'
3277
+ ;;
3278
+ esac
3279
+ `);
3280
+ await chmod(join(binDir, "tmux"), 0o755);
3281
+ process.env.PATH = `${binDir}:${originalPath}`;
3282
+ const result = await dispatchCodexNativeHook({
3283
+ hook_event_name: "UserPromptSubmit",
3284
+ cwd,
3285
+ session_id: nativeSessionId,
3286
+ thread_id: "thread-hud-reuse",
3287
+ turn_id: "turn-hud-reuse",
3288
+ prompt: "$ralplan prepare plan",
3289
+ }, { cwd });
3290
+ assert.equal(result.omxEventName, "keyword-detector");
3291
+ const tmuxCalls = await readFile(tmuxLog, "utf-8");
3292
+ assert.match(tmuxCalls, /list-panes -t %1 -F/);
3293
+ assert.match(tmuxCalls, /resize-pane -t %2 -y 3/);
3294
+ assert.doesNotMatch(tmuxCalls, /split-window/);
3295
+ assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", canonicalSessionId, "ralplan-state.json")), true);
3296
+ assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", nativeSessionId, "ralplan-state.json")), false);
3297
+ }
3298
+ finally {
3299
+ if (originalTmux === undefined)
3300
+ delete process.env.TMUX;
3301
+ else
3302
+ process.env.TMUX = originalTmux;
3303
+ if (originalTmuxPane === undefined)
3304
+ delete process.env.TMUX_PANE;
3305
+ else
3306
+ process.env.TMUX_PANE = originalTmuxPane;
3307
+ if (originalHudOwner === undefined)
3308
+ delete process.env[OMX_TMUX_HUD_OWNER_ENV];
3309
+ else
3310
+ process.env[OMX_TMUX_HUD_OWNER_ENV] = originalHudOwner;
3311
+ process.env.PATH = originalPath;
3312
+ await rm(cwd, { recursive: true, force: true });
3313
+ }
3314
+ });
2928
3315
  it("skips prompt-submit HUD reconciliation inside unowned tmux panes", async () => {
2929
3316
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-hud-unowned-"));
2930
3317
  const originalTmux = process.env.TMUX;