oh-my-codex 0.18.12 → 0.18.13

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 (95) hide show
  1. package/Cargo.lock +6 -6
  2. package/Cargo.toml +1 -1
  3. package/README.md +7 -0
  4. package/dist/autopilot/__tests__/ralplan-gate.test.js +621 -0
  5. package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -1
  6. package/dist/autopilot/ralplan-gate.d.ts.map +1 -1
  7. package/dist/autopilot/ralplan-gate.js +32 -18
  8. package/dist/autopilot/ralplan-gate.js.map +1 -1
  9. package/dist/cli/__tests__/doctor-invalid-config.test.js +35 -0
  10. package/dist/cli/__tests__/doctor-invalid-config.test.js.map +1 -1
  11. package/dist/cli/__tests__/index.test.js +54 -1
  12. package/dist/cli/__tests__/index.test.js.map +1 -1
  13. package/dist/cli/__tests__/resume.test.js +217 -1
  14. package/dist/cli/__tests__/resume.test.js.map +1 -1
  15. package/dist/cli/__tests__/session-search-help.test.js +3 -2
  16. package/dist/cli/__tests__/session-search-help.test.js.map +1 -1
  17. package/dist/cli/__tests__/session-search.test.js +64 -2
  18. package/dist/cli/__tests__/session-search.test.js.map +1 -1
  19. package/dist/cli/__tests__/setup-install-mode.test.js +6 -5
  20. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  21. package/dist/cli/__tests__/setup-prompts-overwrite.test.js +74 -0
  22. package/dist/cli/__tests__/setup-prompts-overwrite.test.js.map +1 -1
  23. package/dist/cli/__tests__/setup-scope.test.js +45 -0
  24. package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
  25. package/dist/cli/__tests__/update.test.js +5 -2
  26. package/dist/cli/__tests__/update.test.js.map +1 -1
  27. package/dist/cli/doctor.d.ts.map +1 -1
  28. package/dist/cli/doctor.js +9 -1
  29. package/dist/cli/doctor.js.map +1 -1
  30. package/dist/cli/index.d.ts +9 -1
  31. package/dist/cli/index.d.ts.map +1 -1
  32. package/dist/cli/index.js +209 -7
  33. package/dist/cli/index.js.map +1 -1
  34. package/dist/cli/project-runtime-codex-homes.d.ts +6 -0
  35. package/dist/cli/project-runtime-codex-homes.d.ts.map +1 -0
  36. package/dist/cli/project-runtime-codex-homes.js +27 -0
  37. package/dist/cli/project-runtime-codex-homes.js.map +1 -0
  38. package/dist/cli/session-search.d.ts.map +1 -1
  39. package/dist/cli/session-search.js +8 -1
  40. package/dist/cli/session-search.js.map +1 -1
  41. package/dist/cli/setup.d.ts +1 -0
  42. package/dist/cli/setup.d.ts.map +1 -1
  43. package/dist/cli/setup.js +168 -4
  44. package/dist/cli/setup.js.map +1 -1
  45. package/dist/cli/update.d.ts.map +1 -1
  46. package/dist/cli/update.js +2 -0
  47. package/dist/cli/update.js.map +1 -1
  48. package/dist/config/__tests__/codex-hooks.test.js +38 -24
  49. package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
  50. package/dist/config/codex-hooks.d.ts +6 -0
  51. package/dist/config/codex-hooks.d.ts.map +1 -1
  52. package/dist/config/codex-hooks.js +34 -49
  53. package/dist/config/codex-hooks.js.map +1 -1
  54. package/dist/pipeline/__tests__/orchestrator.test.js +125 -0
  55. package/dist/pipeline/__tests__/orchestrator.test.js.map +1 -1
  56. package/dist/pipeline/__tests__/stages.test.js +109 -0
  57. package/dist/pipeline/__tests__/stages.test.js.map +1 -1
  58. package/dist/pipeline/orchestrator.d.ts.map +1 -1
  59. package/dist/pipeline/orchestrator.js +7 -0
  60. package/dist/pipeline/orchestrator.js.map +1 -1
  61. package/dist/ralplan/__tests__/consensus-gate.test.js +440 -1
  62. package/dist/ralplan/__tests__/consensus-gate.test.js.map +1 -1
  63. package/dist/ralplan/consensus-gate.d.ts +2 -0
  64. package/dist/ralplan/consensus-gate.d.ts.map +1 -1
  65. package/dist/ralplan/consensus-gate.js +173 -71
  66. package/dist/ralplan/consensus-gate.js.map +1 -1
  67. package/dist/scripts/__tests__/codex-native-hook.test.js +273 -0
  68. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  69. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  70. package/dist/scripts/codex-native-hook.js +56 -12
  71. package/dist/scripts/codex-native-hook.js.map +1 -1
  72. package/dist/scripts/codex-native-pre-post.d.ts +1 -0
  73. package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
  74. package/dist/scripts/codex-native-pre-post.js +130 -0
  75. package/dist/scripts/codex-native-pre-post.js.map +1 -1
  76. package/dist/session-history/__tests__/search.test.js +166 -0
  77. package/dist/session-history/__tests__/search.test.js.map +1 -1
  78. package/dist/session-history/search.d.ts +7 -0
  79. package/dist/session-history/search.d.ts.map +1 -1
  80. package/dist/session-history/search.js +83 -24
  81. package/dist/session-history/search.js.map +1 -1
  82. package/dist/sidecar/__tests__/collector.test.js +60 -0
  83. package/dist/sidecar/__tests__/collector.test.js.map +1 -1
  84. package/dist/sidecar/collector.d.ts.map +1 -1
  85. package/dist/sidecar/collector.js +3 -6
  86. package/dist/sidecar/collector.js.map +1 -1
  87. package/dist/verification/__tests__/ci-rust-gates.test.js +4 -2
  88. package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
  89. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js +71 -3
  90. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js.map +1 -1
  91. package/package.json +1 -1
  92. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  93. package/src/scripts/__tests__/codex-native-hook.test.ts +363 -0
  94. package/src/scripts/codex-native-hook.ts +68 -18
  95. package/src/scripts/codex-native-pre-post.ts +137 -0
@@ -1 +1 @@
1
- {"version":3,"file":"dev-merge-issue-close-workflow.test.js","sourceRoot":"","sources":["../../../src/verification/__tests__/dev-merge-issue-close-workflow.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dev-merge-issue-close-workflow.test.js","sourceRoot":"","sources":["../../../src/verification/__tests__/dev-merge-issue-close-workflow.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,EACJ,2BAA2B,EAC3B,wBAAwB,EACxB,8BAA8B,EAC9B,4BAA4B,EAC5B,2BAA2B,GAC5B,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,2BAA2B,CAAC,CAoBjF,CAAC;AAEF,QAAQ,CAAC,gCAAgC,EAAE,GAAG,EAAE;IAC9C,EAAE,CAAC,gGAAgG,EAAE,GAAG,EAAE;QACxG,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,WAAW,EAAE,2BAA2B,CAAC,CAAC;QAC9F,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,qBAAqB,YAAY,EAAE,CAAC,CAAC;QAElF,MAAM,QAAQ,GAAG,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QACrD,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,+BAA+B,CAAC,CAAC;QACxD,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,iDAAiD,CAAC,CAAC;QAC1E,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,iBAAiB,CAAC,CAAC;QAC1C,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,uBAAuB,CAAC,CAAC;QAChD,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,gGAAgG,CAAC,CAAC;QACzH,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,gEAAgE,CAAC,CAAC;QACzF,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,6BAA6B,CAAC,CAAC;QACtD,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,2BAA2B,CAAC,CAAC;QACpD,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,iCAAiC,CAAC,CAAC;QAC1D,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,iCAAiC,CAAC,CAAC;QAC1D,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,oCAAoC,CAAC,CAAC;QAC7D,kFAAkF;QAClF,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,0DAA0D,CAAC,CAAC;QACnF,oEAAoE;QACpE,MAAM,CAAC,KAAK,CACV,QAAQ,EACR,wEAAwE,CACzE,CAAC;QACF,MAAM,CAAC,YAAY,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;QACzC,MAAM,CAAC,YAAY,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gFAAgF,EAAE,GAAG,EAAE;QACxF,MAAM,CAAC,SAAS,CACd,8BAA8B,CAAC;YAC7B,KAAK,EAAE,oBAAoB;YAC3B,IAAI,EAAE,yGAAyG;YAC/G,KAAK,EAAE,aAAa;YACpB,IAAI,EAAE,aAAa;SACpB,CAAC,EACF,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CACzB,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gFAAgF,EAAE,GAAG,EAAE;QACxF,MAAM,CAAC,SAAS,CACd,8BAA8B,CAAC;YAC7B,KAAK,EAAE,YAAY;YACnB,IAAI,EAAE;gBACJ,yCAAyC;gBACzC,yBAAyB;gBACzB,0CAA0C;aAC3C,CAAC,IAAI,CAAC,IAAI,CAAC;YACZ,KAAK,EAAE,aAAa;YACpB,IAAI,EAAE,aAAa;SACpB,CAAC,EACF,EAAE,CACH,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8EAA8E,EAAE,GAAG,EAAE;QACtF,MAAM,CAAC,SAAS,CACd,8BAA8B,CAAC;YAC7B,KAAK,EAAE,aAAa;YACpB,IAAI,EAAE,wDAAwD;YAC9D,KAAK,EAAE,aAAa;YACpB,IAAI,EAAE,aAAa;SACpB,CAAC,EACF,CAAC,IAAI,CAAC,CACP,CAAC;QACF,MAAM,OAAO,GAAG,2BAA2B,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QAChE,MAAM,CAAC,KAAK,CACV,OAAO,EACP,6HAA6H,CAC9H,CAAC;QACF,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,oCAAoC,CAAC,CAAC;QAC5D,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,oBAAoB,CAAC,CAAC;QAC5C,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,2CAA2C,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;QAC1E,MAAM,OAAO,GAAG,wBAAwB,CAAC,EAAE,YAAY,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;QACzE,MAAM,CAAC,KAAK,CACV,OAAO,EACP,qFAAqF,CACtF,CAAC;QACF,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,oCAAoC,CAAC,CAAC;QAC5D,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,mDAAmD,CAAC,CAAC;QAC3E,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,2CAA2C,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;QAClE,MAAM,KAAK,GAAmC,EAAE,CAAC;QACjD,MAAM,QAAQ,GAAa,EAAE,CAAC;QAC9B,MAAM,MAAM,GAAG;YACb,IAAI,EAAE;gBACJ,MAAM,EAAE;oBACN,aAAa,EAAE,KAAK,EAAE,IAA6B,EAAE,EAAE;wBACrD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;wBACjB,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;oBAC7B,CAAC;iBACF;aACF;SACF,CAAC;QACF,MAAM,IAAI,GAAG,EAAE,OAAO,EAAE,CAAC,OAAe,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAEtE,MAAM,MAAM,GAAG,MAAM,2BAA2B,CAAC;YAC/C,MAAM;YACN,IAAI;YACJ,KAAK,EAAE,aAAa;YACpB,IAAI,EAAE,aAAa;YACnB,QAAQ,EAAE,IAAI;YACd,YAAY,EAAE,CAAC,IAAI,CAAC;SACrB,CAAC,CAAC;QAEH,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3C,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;QAC5C,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;QAC3C,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;QAC1C,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,6EAA6E,CAAC,CAAC;QACnH,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uEAAuE,EAAE,KAAK,IAAI,EAAE;QACrF,MAAM,QAAQ,GAAa,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,wCAAwC,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QAClG,MAAM,MAAM,GAAG;YACb,IAAI,EAAE;gBACJ,MAAM,EAAE;oBACN,aAAa,EAAE,KAAK,IAAI,EAAE;wBACxB,MAAM,KAAK,CAAC;oBACd,CAAC;iBACF;aACF;SACF,CAAC;QACF,MAAM,IAAI,GAAG,EAAE,OAAO,EAAE,CAAC,OAAe,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAEtE,MAAM,MAAM,GAAG,MAAM,2BAA2B,CAAC;YAC/C,MAAM;YACN,IAAI;YACJ,KAAK,EAAE,aAAa;YACpB,IAAI,EAAE,aAAa;YACnB,QAAQ,EAAE,IAAI;YACd,YAAY,EAAE,CAAC,IAAI,CAAC;SACrB,CAAC,CAAC;QAEH,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QACnC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAClC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QACjC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,mDAAmD,CAAC,CAAC;QAC/E,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,4CAA4C,CAAC,CAAC;QACxE,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,wCAAwC,CAAC,CAAC;IACtE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;QAC7E,MAAM,CAAC,KAAK,CAAC,4BAA4B,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;QAClE,MAAM,CAAC,KAAK,CAAC,4BAA4B,CAAC,EAAE,OAAO,EAAE,wCAAwC,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;QACxG,MAAM,CAAC,KAAK,CAAC,4BAA4B,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC;QACzF,MAAM,CAAC,KAAK,CAAC,4BAA4B,CAAC,SAAS,CAAC,EAAE,KAAK,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;AAEL,CAAC,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-my-codex",
3
- "version": "0.18.12",
3
+ "version": "0.18.13",
4
4
  "description": "Multi-agent orchestration layer for OpenAI Codex CLI",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-my-codex",
3
- "version": "0.18.12",
3
+ "version": "0.18.13",
4
4
  "description": "Multi-agent orchestration layer for OpenAI Codex CLI",
5
5
  "author": {
6
6
  "name": "Yeachan Heo",
@@ -1464,6 +1464,156 @@ describe("codex native hook dispatch", () => {
1464
1464
  }
1465
1465
  });
1466
1466
 
1467
+ it("suppresses child-agent SessionStart and Stop before the canonical leader session is reconciled (#2831)", async () => {
1468
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-no-canonical-"));
1469
+ const originalCodexHome = process.env.CODEX_HOME;
1470
+ try {
1471
+ process.env.CODEX_HOME = join(cwd, "codex-home");
1472
+ await writeJson(join(process.env.CODEX_HOME, ".omx-config.json"), {
1473
+ notifications: {
1474
+ enabled: true,
1475
+ verbosity: "session",
1476
+ telegram: { enabled: true, botToken: "123:abc", chatId: "456" },
1477
+ },
1478
+ });
1479
+ const stateDir = join(cwd, ".omx", "state");
1480
+ const leaderNativeSessionId = "codex-leader-thread-no-canonical";
1481
+ const childNativeSessionId = "codex-child-thread-no-canonical";
1482
+ await mkdir(join(cwd, ".omx", "hooks"), { recursive: true });
1483
+ await writeFile(
1484
+ join(cwd, ".omx", "hooks", "record-lifecycle.mjs"),
1485
+ [
1486
+ "import { appendFileSync } from 'node:fs';",
1487
+ "export async function onHookEvent(event) {",
1488
+ " appendFileSync('hook-events.jsonl', `${JSON.stringify({ event: event.event })}\\n`);",
1489
+ "}",
1490
+ ].join("\n"),
1491
+ );
1492
+ const transcriptPath = join(cwd, "no-canonical-subagent-rollout.jsonl");
1493
+ await writeFile(
1494
+ transcriptPath,
1495
+ `${JSON.stringify({
1496
+ type: "session_meta",
1497
+ payload: {
1498
+ id: childNativeSessionId,
1499
+ source: {
1500
+ subagent: {
1501
+ thread_spawn: {
1502
+ parent_thread_id: leaderNativeSessionId,
1503
+ agent_role: "explorer",
1504
+ },
1505
+ },
1506
+ },
1507
+ },
1508
+ })}\n`,
1509
+ );
1510
+
1511
+ await dispatchCodexNativeHook(
1512
+ {
1513
+ hook_event_name: "SessionStart",
1514
+ cwd,
1515
+ session_id: childNativeSessionId,
1516
+ transcript_path: transcriptPath,
1517
+ },
1518
+ { cwd, sessionOwnerPid: process.pid },
1519
+ );
1520
+
1521
+ assert.equal(
1522
+ existsSync(join(cwd, "hook-events.jsonl")),
1523
+ false,
1524
+ "child SessionStart must be suppressed even before the canonical leader session is reconciled",
1525
+ );
1526
+ assert.equal(
1527
+ existsSync(join(stateDir, "session.json")),
1528
+ false,
1529
+ "child SessionStart must not be promoted into a root/leader session",
1530
+ );
1531
+
1532
+ const tracking = JSON.parse(
1533
+ await readFile(join(stateDir, "subagent-tracking.json"), "utf-8"),
1534
+ ) as {
1535
+ sessions?: Record<string, {
1536
+ leader_thread_id?: string;
1537
+ threads?: Record<string, { kind?: string; mode?: string }>;
1538
+ }>;
1539
+ };
1540
+ assert.equal(tracking.sessions?.[leaderNativeSessionId]?.leader_thread_id, leaderNativeSessionId);
1541
+ assert.equal(tracking.sessions?.[leaderNativeSessionId]?.threads?.[childNativeSessionId]?.kind, "subagent");
1542
+
1543
+ await dispatchCodexNativeHook(
1544
+ {
1545
+ hook_event_name: "Stop",
1546
+ cwd,
1547
+ session_id: childNativeSessionId,
1548
+ thread_id: childNativeSessionId,
1549
+ turn_id: "no-canonical-child-stop-turn",
1550
+ },
1551
+ { cwd },
1552
+ );
1553
+
1554
+ assert.equal(
1555
+ existsSync(join(cwd, "hook-events.jsonl")),
1556
+ false,
1557
+ "child Stop must be suppressed when the start was recognized as subagent-scoped",
1558
+ );
1559
+ } finally {
1560
+ if (originalCodexHome === undefined) {
1561
+ delete process.env.CODEX_HOME;
1562
+ } else {
1563
+ process.env.CODEX_HOME = originalCodexHome;
1564
+ }
1565
+ await rm(cwd, { recursive: true, force: true });
1566
+ }
1567
+ });
1568
+
1569
+ it("preserves root/leader SessionStart dispatch at session verbosity (#2831)", async () => {
1570
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-root-session-start-preserved-"));
1571
+ const originalCodexHome = process.env.CODEX_HOME;
1572
+ try {
1573
+ process.env.CODEX_HOME = join(cwd, "codex-home");
1574
+ await writeJson(join(process.env.CODEX_HOME, ".omx-config.json"), {
1575
+ notifications: {
1576
+ enabled: true,
1577
+ verbosity: "session",
1578
+ telegram: { enabled: true, botToken: "123:abc", chatId: "456" },
1579
+ },
1580
+ });
1581
+ await mkdir(join(cwd, ".omx", "hooks"), { recursive: true });
1582
+ await writeFile(
1583
+ join(cwd, ".omx", "hooks", "record-lifecycle.mjs"),
1584
+ [
1585
+ "import { appendFileSync } from 'node:fs';",
1586
+ "export async function onHookEvent(event) {",
1587
+ " appendFileSync('hook-events.jsonl', `${JSON.stringify({ event: event.event })}\\n`);",
1588
+ "}",
1589
+ ].join("\n"),
1590
+ );
1591
+
1592
+ await dispatchCodexNativeHook(
1593
+ {
1594
+ hook_event_name: "SessionStart",
1595
+ cwd,
1596
+ session_id: "codex-root-thread-preserved",
1597
+ },
1598
+ { cwd, sessionOwnerPid: process.pid },
1599
+ );
1600
+
1601
+ const hookEvents = await readFile(join(cwd, "hook-events.jsonl"), "utf-8");
1602
+ assert.match(
1603
+ hookEvents,
1604
+ /"event":"session-start"/,
1605
+ "root/leader SessionStart must still dispatch at session verbosity",
1606
+ );
1607
+ } finally {
1608
+ if (originalCodexHome === undefined) {
1609
+ delete process.env.CODEX_HOME;
1610
+ } else {
1611
+ process.env.CODEX_HOME = originalCodexHome;
1612
+ }
1613
+ await rm(cwd, { recursive: true, force: true });
1614
+ }
1615
+ });
1616
+
1467
1617
  it("keeps a self-parented native role thread as subagent evidence", async () => {
1468
1618
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-self-parented-subagent-"));
1469
1619
  try {
@@ -5903,6 +6053,219 @@ exit 0
5903
6053
  }
5904
6054
  });
5905
6055
 
6056
+ it("allows read-only diagnostics mentioning apply_patch while deep-interview blocks real apply_patch invocations", async () => {
6057
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-deep-interview-grep-apply-patch-"));
6058
+ try {
6059
+ const stateDir = join(cwd, ".omx", "state");
6060
+ const sessionDir = join(stateDir, "sessions", "sess-di-grep");
6061
+ await mkdir(sessionDir, { recursive: true });
6062
+ await writeJson(join(stateDir, "session.json"), { session_id: "sess-di-grep", cwd });
6063
+ await writeJson(join(sessionDir, "skill-active-state.json"), {
6064
+ version: 1,
6065
+ active: true,
6066
+ skill: "deep-interview",
6067
+ phase: "planning",
6068
+ session_id: "sess-di-grep",
6069
+ active_skills: [{ skill: "deep-interview", phase: "planning", active: true, session_id: "sess-di-grep" }],
6070
+ });
6071
+ await writeJson(join(sessionDir, "deep-interview-state.json"), {
6072
+ active: true,
6073
+ mode: "deep-interview",
6074
+ current_phase: "intent-first",
6075
+ session_id: "sess-di-grep",
6076
+ });
6077
+
6078
+ const allowedGrep = await dispatchCodexNativeHook(
6079
+ {
6080
+ hook_event_name: "PreToolUse",
6081
+ cwd,
6082
+ session_id: "sess-di-grep",
6083
+ tool_name: "Bash",
6084
+ tool_use_id: "tool-di-grep",
6085
+ tool_input: { command: 'grep -n "apply_patch" dist/scripts/codex-native-hook.js' },
6086
+ },
6087
+ { cwd },
6088
+ );
6089
+ assert.equal(allowedGrep.outputJson, null);
6090
+
6091
+ const allowedSubshellGrep = await dispatchCodexNativeHook(
6092
+ {
6093
+ hook_event_name: "PreToolUse",
6094
+ cwd,
6095
+ session_id: "sess-di-grep",
6096
+ tool_name: "Bash",
6097
+ tool_use_id: "tool-di-grep-subshell",
6098
+ tool_input: { command: '(grep -n "apply_patch" dist/scripts/codex-native-hook.js)' },
6099
+ },
6100
+ { cwd },
6101
+ );
6102
+ assert.equal(allowedSubshellGrep.outputJson, null);
6103
+
6104
+ // Double-quoted spans that merely mention the literal token, expand a
6105
+ // parameter, or run a substitution that is not `apply_patch` stay allowed
6106
+ // — the quoted-substitution fix must not over-block read-only diagnostics.
6107
+ const allowedQuotedMention = await dispatchCodexNativeHook(
6108
+ {
6109
+ hook_event_name: "PreToolUse",
6110
+ cwd,
6111
+ session_id: "sess-di-grep",
6112
+ tool_name: "Bash",
6113
+ tool_use_id: "tool-di-grep-quoted-mention",
6114
+ tool_input: { command: 'echo "${apply_patch} $(echo apply_patch)"' },
6115
+ },
6116
+ { cwd },
6117
+ );
6118
+ assert.equal(allowedQuotedMention.outputJson, null);
6119
+
6120
+ const blockedApplyPatch = await dispatchCodexNativeHook(
6121
+ {
6122
+ hook_event_name: "PreToolUse",
6123
+ cwd,
6124
+ session_id: "sess-di-grep",
6125
+ tool_name: "Bash",
6126
+ tool_use_id: "tool-di-apply-patch-invoke",
6127
+ tool_input: { command: "apply_patch <<'EOF'\n*** Begin Patch\n*** Add File: src/leak.ts\n+export const x = 1;\n*** End Patch\nEOF" },
6128
+ },
6129
+ { cwd },
6130
+ );
6131
+ assert.equal((blockedApplyPatch.outputJson as { decision?: string } | null)?.decision, "block");
6132
+
6133
+ const blockedEnvAssignmentApplyPatch = await dispatchCodexNativeHook(
6134
+ {
6135
+ hook_event_name: "PreToolUse",
6136
+ cwd,
6137
+ session_id: "sess-di-grep",
6138
+ tool_name: "Bash",
6139
+ tool_use_id: "tool-di-apply-patch-env-assignment",
6140
+ tool_input: { command: "FOO=bar apply_patch <<'EOF'\n*** Begin Patch\n*** Add File: src/leak.ts\n+export const x = 1;\n*** End Patch\nEOF" },
6141
+ },
6142
+ { cwd },
6143
+ );
6144
+ assert.equal((blockedEnvAssignmentApplyPatch.outputJson as { decision?: string } | null)?.decision, "block");
6145
+
6146
+ const blockedEnvWrapperApplyPatch = await dispatchCodexNativeHook(
6147
+ {
6148
+ hook_event_name: "PreToolUse",
6149
+ cwd,
6150
+ session_id: "sess-di-grep",
6151
+ tool_name: "Bash",
6152
+ tool_use_id: "tool-di-apply-patch-env-wrapper",
6153
+ tool_input: { command: "env FOO=bar apply_patch <<'EOF'\n*** Begin Patch\n*** Add File: src/leak.ts\n+export const x = 1;\n*** End Patch\nEOF" },
6154
+ },
6155
+ { cwd },
6156
+ );
6157
+ assert.equal((blockedEnvWrapperApplyPatch.outputJson as { decision?: string } | null)?.decision, "block");
6158
+
6159
+ const heredocBody = "\n*** Begin Patch\n*** Add File: src/leak.ts\n+export const x = 1;\n*** End Patch\nEOF";
6160
+ const blockedRealApplyPatchForms: Array<{ id: string; command: string }> = [
6161
+ { id: "tool-di-apply-patch-env-i", command: `env -i apply_patch <<'EOF'${heredocBody}` },
6162
+ { id: "tool-di-apply-patch-env-unset", command: `env -u FOO apply_patch <<'EOF'${heredocBody}` },
6163
+ { id: "tool-di-apply-patch-env-i-assignment", command: `env -i FOO=bar apply_patch <<'EOF'${heredocBody}` },
6164
+ { id: "tool-di-apply-patch-assignment-env", command: `FOO=bar env apply_patch <<'EOF'${heredocBody}` },
6165
+ { id: "tool-di-apply-patch-exec-env-assignment", command: `exec env FOO=bar apply_patch <<'EOF'${heredocBody}` },
6166
+ { id: "tool-di-apply-patch-absolute-path", command: `/usr/bin/apply_patch <<'EOF'${heredocBody}` },
6167
+ { id: "tool-di-apply-patch-relative-path", command: `./apply_patch <<'EOF'${heredocBody}` },
6168
+ { id: "tool-di-apply-patch-subshell", command: `(apply_patch <<'EOF'${heredocBody}\n)` },
6169
+ { id: "tool-di-apply-patch-subshell-spaced", command: `( apply_patch <<'EOF'${heredocBody}\n)` },
6170
+ { id: "tool-di-apply-patch-double-subshell", command: `((apply_patch <<'EOF'${heredocBody}\n))` },
6171
+ { id: "tool-di-apply-patch-pipe-subshell", command: `true | (apply_patch <<'EOF'${heredocBody}\n)` },
6172
+ { id: "tool-di-apply-patch-subshell-env", command: `(env apply_patch <<'EOF'${heredocBody}\n)` },
6173
+ { id: "tool-di-apply-patch-command-substitution", command: `x=$(apply_patch <<'EOF'${heredocBody}\n)` },
6174
+ { id: "tool-di-apply-patch-brace-group", command: `{ apply_patch <<'EOF'${heredocBody}\n}` },
6175
+ // Command substitution runs even inside double quotes, so quoting the
6176
+ // already-blocked `$(…)` / `` `…` `` form must not bypass the guard.
6177
+ { id: "tool-di-apply-patch-quoted-command-substitution", command: `echo "$(apply_patch <<'EOF'${heredocBody}\n)"` },
6178
+ { id: "tool-di-apply-patch-quoted-backtick", command: `echo "\`apply_patch <<'EOF'${heredocBody}\`"` },
6179
+ { id: "tool-di-apply-patch-quoted-command-substitution-prefixed", command: `echo "patched: $(apply_patch <<'EOF'${heredocBody}\n) done"` },
6180
+ ];
6181
+ for (const form of blockedRealApplyPatchForms) {
6182
+ const blockedForm = await dispatchCodexNativeHook(
6183
+ {
6184
+ hook_event_name: "PreToolUse",
6185
+ cwd,
6186
+ session_id: "sess-di-grep",
6187
+ tool_name: "Bash",
6188
+ tool_use_id: form.id,
6189
+ tool_input: { command: form.command },
6190
+ },
6191
+ { cwd },
6192
+ );
6193
+ assert.equal(
6194
+ (blockedForm.outputJson as { decision?: string } | null)?.decision,
6195
+ "block",
6196
+ `expected deep-interview to block real apply_patch form: ${form.command}`,
6197
+ );
6198
+ }
6199
+ } finally {
6200
+ await rm(cwd, { recursive: true, force: true });
6201
+ }
6202
+ });
6203
+
6204
+ it("allows deep-interview same-command literal variable redirects to artifacts while blocking variable redirects outside them", async () => {
6205
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-deep-interview-var-redirect-"));
6206
+ try {
6207
+ const stateDir = join(cwd, ".omx", "state");
6208
+ const sessionDir = join(stateDir, "sessions", "sess-di-var-redirect");
6209
+ await mkdir(sessionDir, { recursive: true });
6210
+ await writeJson(join(stateDir, "session.json"), { session_id: "sess-di-var-redirect", cwd });
6211
+ await writeJson(join(sessionDir, "skill-active-state.json"), {
6212
+ version: 1,
6213
+ active: true,
6214
+ skill: "deep-interview",
6215
+ phase: "planning",
6216
+ session_id: "sess-di-var-redirect",
6217
+ active_skills: [{ skill: "deep-interview", phase: "planning", active: true, session_id: "sess-di-var-redirect" }],
6218
+ });
6219
+ await writeJson(join(sessionDir, "deep-interview-state.json"), {
6220
+ active: true,
6221
+ mode: "deep-interview",
6222
+ current_phase: "intent-first",
6223
+ session_id: "sess-di-var-redirect",
6224
+ });
6225
+
6226
+ const allowedVarRedirect = await dispatchCodexNativeHook(
6227
+ {
6228
+ hook_event_name: "PreToolUse",
6229
+ cwd,
6230
+ session_id: "sess-di-var-redirect",
6231
+ tool_name: "Bash",
6232
+ tool_use_id: "tool-di-var-redirect-allow",
6233
+ tool_input: { command: 'SNAP=".omx/context/example.md"\ncat > "$SNAP" <<\'EOF\'\ncontent\nEOF' },
6234
+ },
6235
+ { cwd },
6236
+ );
6237
+ assert.equal(allowedVarRedirect.outputJson, null);
6238
+
6239
+ const blockedVarRedirect = await dispatchCodexNativeHook(
6240
+ {
6241
+ hook_event_name: "PreToolUse",
6242
+ cwd,
6243
+ session_id: "sess-di-var-redirect",
6244
+ tool_name: "Bash",
6245
+ tool_use_id: "tool-di-var-redirect-block",
6246
+ tool_input: { command: 'SNAP="src/leak.ts"\ncat > "$SNAP" <<\'EOF\'\ncontent\nEOF' },
6247
+ },
6248
+ { cwd },
6249
+ );
6250
+ assert.equal((blockedVarRedirect.outputJson as { decision?: string } | null)?.decision, "block");
6251
+
6252
+ const blockedUnresolvedVarRedirect = await dispatchCodexNativeHook(
6253
+ {
6254
+ hook_event_name: "PreToolUse",
6255
+ cwd,
6256
+ session_id: "sess-di-var-redirect",
6257
+ tool_name: "Bash",
6258
+ tool_use_id: "tool-di-var-redirect-unresolved",
6259
+ tool_input: { command: 'cat > "$SNAP" <<\'EOF\'\ncontent\nEOF' },
6260
+ },
6261
+ { cwd },
6262
+ );
6263
+ assert.equal((blockedUnresolvedVarRedirect.outputJson as { decision?: string } | null)?.decision, "block");
6264
+ } finally {
6265
+ await rm(cwd, { recursive: true, force: true });
6266
+ }
6267
+ });
6268
+
5906
6269
  it("allows deep-interview apply_patch artifact writes from freeform patch text while blocking outside paths", async () => {
5907
6270
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-deep-interview-apply-patch-"));
5908
6271
  try {
@@ -61,6 +61,7 @@ import {
61
61
  SLOPPY_FALLBACK_PHRASE_PATTERNS,
62
62
  buildNativePostToolUseOutput,
63
63
  buildNativePreToolUseOutput,
64
+ commandInvokesApplyPatch,
64
65
  detectMcpTransportFailure,
65
66
  hasAnyPattern,
66
67
  } from "./codex-native-pre-post.js";
@@ -2751,6 +2752,31 @@ function isNullDeviceRedirectTarget(target: string): boolean {
2751
2752
  return normalized === "/dev/null" || normalized === "nul";
2752
2753
  }
2753
2754
 
2755
+ // Collects same-command literal variable assignments (`NAME="value"`), skipping
2756
+ // any value that involves expansion (`$`, backticks) so unresolved/dynamic
2757
+ // targets stay conservatively blocked.
2758
+ function extractCommandLiteralAssignments(command: string): Map<string, string> {
2759
+ const assignments = new Map<string, string>();
2760
+ const pattern = /(?:^|[\n;&|(]|&&|\|\|)\s*([A-Za-z_][A-Za-z0-9_]*)=(?:"([^"$`]*)"|'([^']*)'|([^\s"'$`;&|<>]+))/g;
2761
+ for (const match of command.matchAll(pattern)) {
2762
+ const name = safeString(match[1]).trim();
2763
+ if (!name) continue;
2764
+ const value = match[2] ?? match[3] ?? match[4] ?? "";
2765
+ assignments.set(name, value);
2766
+ }
2767
+ return assignments;
2768
+ }
2769
+
2770
+ // Resolves a redirect/tee target of the form `$NAME`/`${NAME}` against
2771
+ // same-command literal assignments; non-variable or unresolved targets are
2772
+ // returned unchanged so they remain subject to the allowed-path check.
2773
+ function resolveCommandRedirectTarget(target: string, assignments: Map<string, string>): string {
2774
+ const variableMatch = target.match(/^\$\{?([A-Za-z_][A-Za-z0-9_]*)\}?$/);
2775
+ if (!variableMatch) return target;
2776
+ const resolved = assignments.get(safeString(variableMatch[1]));
2777
+ return resolved !== undefined ? resolved : target;
2778
+ }
2779
+
2754
2780
  function extractDeepInterviewCommandRedirectTargets(command: string): string[] {
2755
2781
  const targets: string[] = [];
2756
2782
  for (const match of command.matchAll(/(?:^|[^>])>{1,2}\s*(["']?)([^\s&|;<>]+)\1/g)) {
@@ -2761,7 +2787,7 @@ function extractDeepInterviewCommandRedirectTargets(command: string): string[] {
2761
2787
  }
2762
2788
 
2763
2789
  function commandHasDeepInterviewWriteIntent(command: string): boolean {
2764
- return /\bapply_patch\b/.test(command)
2790
+ return commandInvokesApplyPatch(command)
2765
2791
  || extractDeepInterviewCommandRedirectTargets(command).length > 0
2766
2792
  || /\btee\s+(?:-a\s+)?[^\s&|;]+/.test(command)
2767
2793
  || /\bsed\s+(?:[^\n;&|]*\s)?-i(?:\b|['"])/.test(command)
@@ -2770,10 +2796,12 @@ function commandHasDeepInterviewWriteIntent(command: string): boolean {
2770
2796
  }
2771
2797
 
2772
2798
  function extractDeepInterviewCommandWriteTargets(command: string): string[] {
2773
- const targets = extractDeepInterviewCommandRedirectTargets(command);
2799
+ const assignments = extractCommandLiteralAssignments(command);
2800
+ const targets = extractDeepInterviewCommandRedirectTargets(command)
2801
+ .map((target) => resolveCommandRedirectTarget(target, assignments));
2774
2802
  for (const match of command.matchAll(/\btee\s+(?:-a\s+)?(["']?)([^\s&|;<>]+)\1/g)) {
2775
2803
  const candidate = safeString(match[2]).trim();
2776
- if (candidate) targets.push(candidate);
2804
+ if (candidate) targets.push(resolveCommandRedirectTarget(candidate, assignments));
2777
2805
  }
2778
2806
  return targets;
2779
2807
  }
@@ -4221,28 +4249,50 @@ export async function dispatchCodexNativeHook(
4221
4249
  if (hookEventName === "SessionStart" && nativeSessionId) {
4222
4250
  const transcriptPath = safeString(payload.transcript_path ?? payload.transcriptPath).trim();
4223
4251
  const subagentSessionStart = readNativeSubagentSessionStartMetadata(transcriptPath);
4224
- if (subagentSessionStart && canonicalSessionId) {
4252
+ if (subagentSessionStart) {
4253
+ // A native child/subagent SessionStart carries a parent_thread_id in its
4254
+ // transcript session_meta. Treat it as a child-agent lifecycle event for
4255
+ // notification suppression and subagent tracking even when the canonical
4256
+ // leader session has not been reconciled yet (#2831). A child start must
4257
+ // never promote itself into a root/leader session or emit an independent
4258
+ // session-start notification at session/minimal verbosity.
4225
4259
  isSubagentSessionStart = true;
4226
- const belongsToCanonicalSession = await nativeSubagentSessionStartBelongsToCanonicalSession(
4227
- cwd,
4228
- canonicalSessionId,
4229
- currentSessionState,
4230
- subagentSessionStart,
4231
- );
4232
- if (belongsToCanonicalSession) {
4233
- resolvedNativeSessionId = nativeSessionId;
4234
- await recordNativeSubagentSessionStart(
4260
+ if (canonicalSessionId) {
4261
+ const belongsToCanonicalSession = await nativeSubagentSessionStartBelongsToCanonicalSession(
4235
4262
  cwd,
4236
4263
  canonicalSessionId,
4237
- nativeSessionId,
4264
+ currentSessionState,
4238
4265
  subagentSessionStart,
4239
- transcriptPath,
4240
4266
  );
4267
+ if (belongsToCanonicalSession) {
4268
+ resolvedNativeSessionId = nativeSessionId;
4269
+ await recordNativeSubagentSessionStart(
4270
+ cwd,
4271
+ canonicalSessionId,
4272
+ nativeSessionId,
4273
+ subagentSessionStart,
4274
+ transcriptPath,
4275
+ );
4276
+ } else {
4277
+ skipCanonicalSessionStartContext = true;
4278
+ resolvedNativeSessionId =
4279
+ safeString(currentSessionState?.native_session_id).trim() || nativeSessionId;
4280
+ await recordIgnoredNativeSubagentSessionStart(
4281
+ cwd,
4282
+ canonicalSessionId,
4283
+ nativeSessionId,
4284
+ subagentSessionStart,
4285
+ transcriptPath,
4286
+ );
4287
+ }
4241
4288
  } else {
4289
+ // No canonical leader session is resolved in this worktree yet. Still
4290
+ // register the child thread under its parent so its later Stop is
4291
+ // recognized as subagent-scoped, skip leader SessionStart context, and
4292
+ // do not reconcile the child as a new root session.
4242
4293
  skipCanonicalSessionStartContext = true;
4243
- resolvedNativeSessionId =
4244
- safeString(currentSessionState?.native_session_id).trim() || nativeSessionId;
4245
- await recordIgnoredNativeSubagentSessionStart(
4294
+ resolvedNativeSessionId = nativeSessionId;
4295
+ await recordNativeSubagentSessionStart(
4246
4296
  cwd,
4247
4297
  canonicalSessionId,
4248
4298
  nativeSessionId,