oh-my-codex 0.18.11 → 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 (196) hide show
  1. package/Cargo.lock +6 -6
  2. package/Cargo.toml +1 -1
  3. package/README.md +9 -1
  4. package/dist/autopilot/__tests__/ralplan-gate.test.js +668 -0
  5. package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -1
  6. package/dist/autopilot/completion-gate.d.ts +10 -0
  7. package/dist/autopilot/completion-gate.d.ts.map +1 -0
  8. package/dist/autopilot/completion-gate.js +154 -0
  9. package/dist/autopilot/completion-gate.js.map +1 -0
  10. package/dist/autopilot/ralplan-gate.d.ts.map +1 -1
  11. package/dist/autopilot/ralplan-gate.js +42 -21
  12. package/dist/autopilot/ralplan-gate.js.map +1 -1
  13. package/dist/cli/__tests__/codex-plugin-layout.test.js +46 -3
  14. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
  15. package/dist/cli/__tests__/doctor-invalid-config.test.js +35 -0
  16. package/dist/cli/__tests__/doctor-invalid-config.test.js.map +1 -1
  17. package/dist/cli/__tests__/doctor-warning-copy.test.js +317 -0
  18. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  19. package/dist/cli/__tests__/index.test.js +120 -2
  20. package/dist/cli/__tests__/index.test.js.map +1 -1
  21. package/dist/cli/__tests__/launch-fallback.test.js +1 -1
  22. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  23. package/dist/cli/__tests__/resume.test.js +217 -1
  24. package/dist/cli/__tests__/resume.test.js.map +1 -1
  25. package/dist/cli/__tests__/session-scoped-runtime.test.js +101 -0
  26. package/dist/cli/__tests__/session-scoped-runtime.test.js.map +1 -1
  27. package/dist/cli/__tests__/session-search-help.test.js +3 -2
  28. package/dist/cli/__tests__/session-search-help.test.js.map +1 -1
  29. package/dist/cli/__tests__/session-search.test.js +64 -2
  30. package/dist/cli/__tests__/session-search.test.js.map +1 -1
  31. package/dist/cli/__tests__/setup-agents-overwrite.test.js +289 -1
  32. package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
  33. package/dist/cli/__tests__/setup-install-mode.test.js +290 -17
  34. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  35. package/dist/cli/__tests__/setup-prompts-overwrite.test.js +74 -0
  36. package/dist/cli/__tests__/setup-prompts-overwrite.test.js.map +1 -1
  37. package/dist/cli/__tests__/setup-scope.test.js +45 -0
  38. package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
  39. package/dist/cli/__tests__/state.test.js +93 -0
  40. package/dist/cli/__tests__/state.test.js.map +1 -1
  41. package/dist/cli/__tests__/update.test.js +157 -3
  42. package/dist/cli/__tests__/update.test.js.map +1 -1
  43. package/dist/cli/__tests__/version-sync-contract.test.js +2 -0
  44. package/dist/cli/__tests__/version-sync-contract.test.js.map +1 -1
  45. package/dist/cli/doctor.d.ts.map +1 -1
  46. package/dist/cli/doctor.js +90 -12
  47. package/dist/cli/doctor.js.map +1 -1
  48. package/dist/cli/index.d.ts +13 -4
  49. package/dist/cli/index.d.ts.map +1 -1
  50. package/dist/cli/index.js +439 -46
  51. package/dist/cli/index.js.map +1 -1
  52. package/dist/cli/project-runtime-codex-homes.d.ts +6 -0
  53. package/dist/cli/project-runtime-codex-homes.d.ts.map +1 -0
  54. package/dist/cli/project-runtime-codex-homes.js +27 -0
  55. package/dist/cli/project-runtime-codex-homes.js.map +1 -0
  56. package/dist/cli/session-search.d.ts.map +1 -1
  57. package/dist/cli/session-search.js +8 -1
  58. package/dist/cli/session-search.js.map +1 -1
  59. package/dist/cli/setup.d.ts +2 -2
  60. package/dist/cli/setup.d.ts.map +1 -1
  61. package/dist/cli/setup.js +482 -126
  62. package/dist/cli/setup.js.map +1 -1
  63. package/dist/cli/state.d.ts.map +1 -1
  64. package/dist/cli/state.js +79 -8
  65. package/dist/cli/state.js.map +1 -1
  66. package/dist/cli/update.d.ts +1 -0
  67. package/dist/cli/update.d.ts.map +1 -1
  68. package/dist/cli/update.js +42 -10
  69. package/dist/cli/update.js.map +1 -1
  70. package/dist/config/__tests__/codex-hooks.test.js +73 -29
  71. package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
  72. package/dist/config/codex-hooks.d.ts +14 -0
  73. package/dist/config/codex-hooks.d.ts.map +1 -1
  74. package/dist/config/codex-hooks.js +54 -51
  75. package/dist/config/codex-hooks.js.map +1 -1
  76. package/dist/config/generator.d.ts +1 -1
  77. package/dist/config/generator.d.ts.map +1 -1
  78. package/dist/config/generator.js +1 -1
  79. package/dist/config/generator.js.map +1 -1
  80. package/dist/hooks/__tests__/best-practice-research-skill.test.js +12 -0
  81. package/dist/hooks/__tests__/best-practice-research-skill.test.js.map +1 -1
  82. package/dist/hud/__tests__/authority.test.js +45 -12
  83. package/dist/hud/__tests__/authority.test.js.map +1 -1
  84. package/dist/hud/__tests__/reconcile.test.js +95 -0
  85. package/dist/hud/__tests__/reconcile.test.js.map +1 -1
  86. package/dist/hud/__tests__/render.test.js +6 -6
  87. package/dist/hud/__tests__/render.test.js.map +1 -1
  88. package/dist/hud/__tests__/tmux.test.js +2 -2
  89. package/dist/hud/__tests__/tmux.test.js.map +1 -1
  90. package/dist/hud/authority.d.ts.map +1 -1
  91. package/dist/hud/authority.js +17 -2
  92. package/dist/hud/authority.js.map +1 -1
  93. package/dist/hud/index.js +1 -4
  94. package/dist/hud/index.js.map +1 -1
  95. package/dist/hud/reconcile.d.ts.map +1 -1
  96. package/dist/hud/reconcile.js +42 -0
  97. package/dist/hud/reconcile.js.map +1 -1
  98. package/dist/hud/render.d.ts.map +1 -1
  99. package/dist/hud/render.js +6 -0
  100. package/dist/hud/render.js.map +1 -1
  101. package/dist/hud/tmux.d.ts.map +1 -1
  102. package/dist/hud/tmux.js +5 -4
  103. package/dist/hud/tmux.js.map +1 -1
  104. package/dist/mcp/__tests__/bootstrap.test.js +31 -1
  105. package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
  106. package/dist/mcp/bootstrap.d.ts +1 -0
  107. package/dist/mcp/bootstrap.d.ts.map +1 -1
  108. package/dist/mcp/bootstrap.js +32 -0
  109. package/dist/mcp/bootstrap.js.map +1 -1
  110. package/dist/modes/__tests__/base-autopilot-gates.test.d.ts +2 -0
  111. package/dist/modes/__tests__/base-autopilot-gates.test.d.ts.map +1 -0
  112. package/dist/modes/__tests__/base-autopilot-gates.test.js +154 -0
  113. package/dist/modes/__tests__/base-autopilot-gates.test.js.map +1 -0
  114. package/dist/modes/base.d.ts +4 -1
  115. package/dist/modes/base.d.ts.map +1 -1
  116. package/dist/modes/base.js +71 -1
  117. package/dist/modes/base.js.map +1 -1
  118. package/dist/pipeline/__tests__/orchestrator.test.js +144 -3
  119. package/dist/pipeline/__tests__/orchestrator.test.js.map +1 -1
  120. package/dist/pipeline/__tests__/stages.test.js +109 -0
  121. package/dist/pipeline/__tests__/stages.test.js.map +1 -1
  122. package/dist/pipeline/orchestrator.d.ts.map +1 -1
  123. package/dist/pipeline/orchestrator.js +11 -4
  124. package/dist/pipeline/orchestrator.js.map +1 -1
  125. package/dist/pipeline/stages/code-review.d.ts +2 -0
  126. package/dist/pipeline/stages/code-review.d.ts.map +1 -1
  127. package/dist/pipeline/stages/code-review.js +2 -0
  128. package/dist/pipeline/stages/code-review.js.map +1 -1
  129. package/dist/pipeline/stages/ultraqa.d.ts +3 -0
  130. package/dist/pipeline/stages/ultraqa.d.ts.map +1 -1
  131. package/dist/pipeline/stages/ultraqa.js +3 -0
  132. package/dist/pipeline/stages/ultraqa.js.map +1 -1
  133. package/dist/ralplan/__tests__/consensus-gate.test.d.ts +2 -0
  134. package/dist/ralplan/__tests__/consensus-gate.test.d.ts.map +1 -0
  135. package/dist/ralplan/__tests__/consensus-gate.test.js +631 -0
  136. package/dist/ralplan/__tests__/consensus-gate.test.js.map +1 -0
  137. package/dist/ralplan/consensus-gate.d.ts +9 -1
  138. package/dist/ralplan/consensus-gate.d.ts.map +1 -1
  139. package/dist/ralplan/consensus-gate.js +287 -65
  140. package/dist/ralplan/consensus-gate.js.map +1 -1
  141. package/dist/scripts/__tests__/codex-native-hook.test.js +481 -0
  142. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  143. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  144. package/dist/scripts/codex-native-hook.js +145 -25
  145. package/dist/scripts/codex-native-hook.js.map +1 -1
  146. package/dist/scripts/codex-native-pre-post.d.ts +1 -0
  147. package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
  148. package/dist/scripts/codex-native-pre-post.js +130 -0
  149. package/dist/scripts/codex-native-pre-post.js.map +1 -1
  150. package/dist/session-history/__tests__/search.test.js +166 -0
  151. package/dist/session-history/__tests__/search.test.js.map +1 -1
  152. package/dist/session-history/search.d.ts +7 -0
  153. package/dist/session-history/search.d.ts.map +1 -1
  154. package/dist/session-history/search.js +83 -24
  155. package/dist/session-history/search.js.map +1 -1
  156. package/dist/sidecar/__tests__/collector.test.js +60 -0
  157. package/dist/sidecar/__tests__/collector.test.js.map +1 -1
  158. package/dist/sidecar/collector.d.ts.map +1 -1
  159. package/dist/sidecar/collector.js +3 -6
  160. package/dist/sidecar/collector.js.map +1 -1
  161. package/dist/state/__tests__/operations.test.js +622 -0
  162. package/dist/state/__tests__/operations.test.js.map +1 -1
  163. package/dist/state/__tests__/skill-active.test.js +82 -0
  164. package/dist/state/__tests__/skill-active.test.js.map +1 -1
  165. package/dist/state/operations.d.ts.map +1 -1
  166. package/dist/state/operations.js +31 -9
  167. package/dist/state/operations.js.map +1 -1
  168. package/dist/state/skill-active.d.ts.map +1 -1
  169. package/dist/state/skill-active.js +41 -1
  170. package/dist/state/skill-active.js.map +1 -1
  171. package/dist/team/__tests__/runtime.test.js +81 -57
  172. package/dist/team/__tests__/runtime.test.js.map +1 -1
  173. package/dist/team/runtime.js +4 -4
  174. package/dist/team/runtime.js.map +1 -1
  175. package/dist/utils/__tests__/paths.test.js +23 -0
  176. package/dist/utils/__tests__/paths.test.js.map +1 -1
  177. package/dist/utils/__tests__/version.test.js +27 -0
  178. package/dist/utils/__tests__/version.test.js.map +1 -1
  179. package/dist/utils/paths.d.ts.map +1 -1
  180. package/dist/utils/paths.js +4 -2
  181. package/dist/utils/paths.js.map +1 -1
  182. package/dist/utils/version.d.ts.map +1 -1
  183. package/dist/utils/version.js +7 -2
  184. package/dist/utils/version.js.map +1 -1
  185. package/dist/verification/__tests__/ci-rust-gates.test.js +4 -2
  186. package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
  187. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js +71 -3
  188. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js.map +1 -1
  189. package/package.json +1 -1
  190. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  191. package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +53 -2
  192. package/plugins/oh-my-codex/skills/best-practice-research/SKILL.md +6 -1
  193. package/skills/best-practice-research/SKILL.md +6 -1
  194. package/src/scripts/__tests__/codex-native-hook.test.ts +615 -0
  195. package/src/scripts/codex-native-hook.ts +162 -32
  196. package/src/scripts/codex-native-pre-post.ts +137 -0
@@ -1 +1 @@
1
- {"version":3,"file":"codex-native-hook.d.ts","sourceRoot":"","sources":["../../src/scripts/codex-native-hook.ts"],"names":[],"mappings":"AA2CA,OAAO,EAIL,KAAK,gBAAgB,EACtB,MAAM,8BAA8B,CAAC;AA+BtC,OAAO,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAwClE,KAAK,kBAAkB,GACnB,cAAc,GACd,YAAY,GACZ,aAAa,GACb,kBAAkB,GAClB,YAAY,GACZ,aAAa,GACb,MAAM,CAAC;AAEX,KAAK,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEhD,UAAU,yBAAyB;IACjC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,6BAA6B,CAAC,EAAE,OAAO,2BAA2B,CAAC;CACpE;AAED,MAAM,WAAW,wBAAwB;IACvC,aAAa,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACzC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACpC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC5C;AAyZD,wBAAgB,2BAA2B,CACzC,aAAa,EAAE,kBAAkB,GAAG,IAAI,GACvC,MAAM,GAAG,IAAI,CAmBf;AAwoBD,wBAAgB,kCAAkC,CAChD,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IACP,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IAC/C,kBAAkB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;CACzC,GACL,MAAM,GAAG,IAAI,CAuBf;AA6+BD,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAMnE;AAqzDD,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,gBAAgB,EACzB,OAAO,GAAE,yBAA8B,GACtC,OAAO,CAAC,wBAAwB,CAAC,CA8TnC;AA6BD,wBAAgB,2BAA2B,CACzC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GAAG,SAAS,GACxB,OAAO,CAGT;AAyMD,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC,CAyC3D"}
1
+ {"version":3,"file":"codex-native-hook.d.ts","sourceRoot":"","sources":["../../src/scripts/codex-native-hook.ts"],"names":[],"mappings":"AA2CA,OAAO,EAIL,KAAK,gBAAgB,EACtB,MAAM,8BAA8B,CAAC;AAgCtC,OAAO,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAwClE,KAAK,kBAAkB,GACnB,cAAc,GACd,YAAY,GACZ,aAAa,GACb,kBAAkB,GAClB,YAAY,GACZ,aAAa,GACb,MAAM,CAAC;AAEX,KAAK,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEhD,UAAU,yBAAyB;IACjC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,6BAA6B,CAAC,EAAE,OAAO,2BAA2B,CAAC;CACpE;AAED,MAAM,WAAW,wBAAwB;IACvC,aAAa,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACzC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACpC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC5C;AAyZD,wBAAgB,2BAA2B,CACzC,aAAa,EAAE,kBAAkB,GAAG,IAAI,GACvC,MAAM,GAAG,IAAI,CAmBf;AAwoBD,wBAAgB,kCAAkC,CAChD,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IACP,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IAC/C,kBAAkB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;CACzC,GACL,MAAM,GAAG,IAAI,CAuBf;AA6+BD,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAMnE;AAg6DD,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,gBAAgB,EACzB,OAAO,GAAE,yBAA8B,GACtC,OAAO,CAAC,wBAAwB,CAAC,CAoVnC;AA6BD,wBAAgB,2BAA2B,CACzC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GAAG,SAAS,GACxB,OAAO,CAGT;AAyMD,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC,CAyC3D"}
@@ -17,7 +17,7 @@ import { detectKeywords, detectPrimaryKeyword, recordSkillActivation, } from "..
17
17
  import { buildDeepInterviewConfigInstruction } from "../hooks/deep-interview-config-instruction.js";
18
18
  import { readTeamModeConfig } from "../config/team-mode.js";
19
19
  import { detectNativeStopStallPattern, loadAutoNudgeConfig, normalizeAutoNudgeSignatureText, resolveEffectiveAutoNudgeResponse, } from "./notify-hook/auto-nudge.js";
20
- import { SLOPPY_FALLBACK_GROUNDING_PATTERNS, SLOPPY_FALLBACK_IMPLEMENTATION_CONTEXT_PATTERNS, SLOPPY_FALLBACK_PHRASE_PATTERNS, buildNativePostToolUseOutput, buildNativePreToolUseOutput, detectMcpTransportFailure, hasAnyPattern, } from "./codex-native-pre-post.js";
20
+ import { SLOPPY_FALLBACK_GROUNDING_PATTERNS, SLOPPY_FALLBACK_IMPLEMENTATION_CONTEXT_PATTERNS, SLOPPY_FALLBACK_PHRASE_PATTERNS, buildNativePostToolUseOutput, buildNativePreToolUseOutput, commandInvokesApplyPatch, detectMcpTransportFailure, hasAnyPattern, } from "./codex-native-pre-post.js";
21
21
  import { handleTeamWorkerPostToolUseSuccess } from "./notify-hook/team-worker-posttooluse.js";
22
22
  import { maybeNudgeLeaderForAllowedWorkerStop } from "./notify-hook/team-worker-stop.js";
23
23
  import { resolveCodexExecutionSurface, } from "./codex-execution-surface.js";
@@ -2223,10 +2223,69 @@ function readPreToolUsePathCandidates(payload) {
2223
2223
  ];
2224
2224
  return candidates.map((candidate) => safeString(candidate).trim()).filter(Boolean);
2225
2225
  }
2226
+ const APPLY_PATCH_TOOL_NAMES = new Set(["apply_patch", "ApplyPatch"]);
2227
+ function isApplyPatchToolName(toolName) {
2228
+ return APPLY_PATCH_TOOL_NAMES.has(toolName);
2229
+ }
2230
+ function readApplyPatchText(payload) {
2231
+ const input = safeObject(payload.tool_input);
2232
+ for (const key of ["input", "patch", "content", "text", "command"]) {
2233
+ const value = safeString(input[key]).trim();
2234
+ if (value)
2235
+ return value;
2236
+ }
2237
+ return "";
2238
+ }
2239
+ function extractApplyPatchTargetPaths(patchText) {
2240
+ if (!patchText)
2241
+ return [];
2242
+ const paths = [];
2243
+ for (const match of patchText.matchAll(/^\s*\*\*\*\s+(?:Add|Update|Delete)\s+File:\s*(.+?)\s*$/gm)) {
2244
+ const candidate = safeString(match[1]).trim();
2245
+ if (candidate)
2246
+ paths.push(candidate);
2247
+ }
2248
+ for (const match of patchText.matchAll(/^\s*\*\*\*\s+Move\s+to:\s*(.+?)\s*$/gm)) {
2249
+ const candidate = safeString(match[1]).trim();
2250
+ if (candidate)
2251
+ paths.push(candidate);
2252
+ }
2253
+ return paths;
2254
+ }
2255
+ function collectImplementationToolPathCandidates(payload, toolName, structuredCandidates) {
2256
+ if (!isApplyPatchToolName(toolName))
2257
+ return structuredCandidates;
2258
+ return [...structuredCandidates, ...extractApplyPatchTargetPaths(readApplyPatchText(payload))];
2259
+ }
2226
2260
  function isNullDeviceRedirectTarget(target) {
2227
2261
  const normalized = target.trim().replace(/^['"]|['"]$/g, "").toLowerCase();
2228
2262
  return normalized === "/dev/null" || normalized === "nul";
2229
2263
  }
2264
+ // Collects same-command literal variable assignments (`NAME="value"`), skipping
2265
+ // any value that involves expansion (`$`, backticks) so unresolved/dynamic
2266
+ // targets stay conservatively blocked.
2267
+ function extractCommandLiteralAssignments(command) {
2268
+ const assignments = new Map();
2269
+ const pattern = /(?:^|[\n;&|(]|&&|\|\|)\s*([A-Za-z_][A-Za-z0-9_]*)=(?:"([^"$`]*)"|'([^']*)'|([^\s"'$`;&|<>]+))/g;
2270
+ for (const match of command.matchAll(pattern)) {
2271
+ const name = safeString(match[1]).trim();
2272
+ if (!name)
2273
+ continue;
2274
+ const value = match[2] ?? match[3] ?? match[4] ?? "";
2275
+ assignments.set(name, value);
2276
+ }
2277
+ return assignments;
2278
+ }
2279
+ // Resolves a redirect/tee target of the form `$NAME`/`${NAME}` against
2280
+ // same-command literal assignments; non-variable or unresolved targets are
2281
+ // returned unchanged so they remain subject to the allowed-path check.
2282
+ function resolveCommandRedirectTarget(target, assignments) {
2283
+ const variableMatch = target.match(/^\$\{?([A-Za-z_][A-Za-z0-9_]*)\}?$/);
2284
+ if (!variableMatch)
2285
+ return target;
2286
+ const resolved = assignments.get(safeString(variableMatch[1]));
2287
+ return resolved !== undefined ? resolved : target;
2288
+ }
2230
2289
  function extractDeepInterviewCommandRedirectTargets(command) {
2231
2290
  const targets = [];
2232
2291
  for (const match of command.matchAll(/(?:^|[^>])>{1,2}\s*(["']?)([^\s&|;<>]+)\1/g)) {
@@ -2237,7 +2296,7 @@ function extractDeepInterviewCommandRedirectTargets(command) {
2237
2296
  return targets;
2238
2297
  }
2239
2298
  function commandHasDeepInterviewWriteIntent(command) {
2240
- return /\bapply_patch\b/.test(command)
2299
+ return commandInvokesApplyPatch(command)
2241
2300
  || extractDeepInterviewCommandRedirectTargets(command).length > 0
2242
2301
  || /\btee\s+(?:-a\s+)?[^\s&|;]+/.test(command)
2243
2302
  || /\bsed\s+(?:[^\n;&|]*\s)?-i(?:\b|['"])/.test(command)
@@ -2245,11 +2304,13 @@ function commandHasDeepInterviewWriteIntent(command) {
2245
2304
  || /\b(?:git\s+(?:checkout|switch|restore|reset|apply|am|merge|rebase)|npm\s+(?:install|i|ci)|pnpm\s+(?:install|i)|yarn\s+(?:install|add))\b/.test(command);
2246
2305
  }
2247
2306
  function extractDeepInterviewCommandWriteTargets(command) {
2248
- const targets = extractDeepInterviewCommandRedirectTargets(command);
2307
+ const assignments = extractCommandLiteralAssignments(command);
2308
+ const targets = extractDeepInterviewCommandRedirectTargets(command)
2309
+ .map((target) => resolveCommandRedirectTarget(target, assignments));
2249
2310
  for (const match of command.matchAll(/\btee\s+(?:-a\s+)?(["']?)([^\s&|;<>]+)\1/g)) {
2250
2311
  const candidate = safeString(match[2]).trim();
2251
2312
  if (candidate)
2252
- targets.push(candidate);
2313
+ targets.push(resolveCommandRedirectTarget(candidate, assignments));
2253
2314
  }
2254
2315
  return targets;
2255
2316
  }
@@ -2262,21 +2323,45 @@ function isAllowedDeepInterviewBashWrite(cwd, command) {
2262
2323
  return targets.length > 0 && targets.every((target) => isAllowedDeepInterviewArtifactPath(cwd, target));
2263
2324
  }
2264
2325
  async function readActiveDeepInterviewStateForPreToolUse(cwd, stateDir, sessionId, threadId) {
2326
+ const canonicalState = sessionId
2327
+ ? await readVisibleSkillActiveStateForStateDir(stateDir, sessionId)
2328
+ : await readSkillActiveState(join(stateDir, SKILL_ACTIVE_STATE_FILE));
2329
+ if (!canonicalState)
2330
+ return null;
2265
2331
  const modeState = sessionId
2266
2332
  ? await readStopSessionPinnedState("deep-interview-state.json", cwd, sessionId, stateDir)
2267
2333
  : await readJsonIfExists(join(stateDir, "deep-interview-state.json"));
2268
- if (!isActiveDeepInterviewPhase(modeState) || !modeState)
2334
+ if (isActiveDeepInterviewPhase(modeState) && modeState && modeStateMatchesSkillStopContext(modeState, cwd, sessionId)) {
2335
+ const hasActiveDeepInterviewSkill = listActiveSkills(canonicalState).some((entry) => (entry.skill === "deep-interview"
2336
+ && matchesSkillStopContext(entry, canonicalState, sessionId, threadId)));
2337
+ if (hasActiveDeepInterviewSkill)
2338
+ return modeState;
2339
+ }
2340
+ const autopilotState = sessionId
2341
+ ? await readStopSessionPinnedState("autopilot-state.json", cwd, sessionId, stateDir)
2342
+ : await readJsonIfExists(join(stateDir, "autopilot-state.json"));
2343
+ if (!autopilotState || autopilotState.active !== true)
2269
2344
  return null;
2270
- if (!modeStateMatchesSkillStopContext(modeState, cwd, sessionId))
2345
+ const autopilotMode = safeString(autopilotState.mode).trim();
2346
+ if (autopilotMode && autopilotMode !== "autopilot")
2271
2347
  return null;
2272
- const canonicalState = sessionId
2273
- ? await readVisibleSkillActiveStateForStateDir(stateDir, sessionId)
2274
- : await readSkillActiveState(join(stateDir, SKILL_ACTIVE_STATE_FILE));
2275
- if (!canonicalState)
2348
+ if (!modeStateMatchesSkillStopContext(autopilotState, cwd, sessionId))
2349
+ return null;
2350
+ const terminalAutopilotRunState = await readCanonicalTerminalRunStateForStop(cwd, sessionId, "autopilot");
2351
+ if (terminalAutopilotRunState)
2276
2352
  return null;
2277
- const hasActiveDeepInterviewSkill = listActiveSkills(canonicalState).some((entry) => (entry.skill === "deep-interview"
2353
+ const autopilotStatePhase = safeString(autopilotState.current_phase ?? autopilotState.currentPhase).trim().toLowerCase();
2354
+ const autopilotIsDeepInterview = normalizeAutopilotPhase(autopilotStatePhase) === "deep-interview";
2355
+ const hasDeepInterviewScopedAutopilotSkill = listActiveSkills(canonicalState).some((entry) => (entry.skill === "autopilot"
2356
+ && normalizeAutopilotPhase(safeString(entry.phase).trim().toLowerCase()) === "deep-interview"
2357
+ && matchesSkillStopContext(entry, canonicalState, sessionId, threadId)));
2358
+ const hasActiveAutopilotSkill = listActiveSkills(canonicalState).some((entry) => (entry.skill === "autopilot"
2278
2359
  && matchesSkillStopContext(entry, canonicalState, sessionId, threadId)));
2279
- return hasActiveDeepInterviewSkill ? modeState : null;
2360
+ if (!hasActiveAutopilotSkill)
2361
+ return null;
2362
+ if (!autopilotIsDeepInterview && !hasDeepInterviewScopedAutopilotSkill)
2363
+ return null;
2364
+ return autopilotState;
2280
2365
  }
2281
2366
  async function readActiveRalplanStateForPreToolUse(cwd, stateDir, sessionId, threadId) {
2282
2367
  const modeState = sessionId
@@ -2342,12 +2427,29 @@ async function buildRalplanPreToolUseBoundaryOutput(payload, cwd, stateDir, reso
2342
2427
  const command = readPreToolUseCommand(payload);
2343
2428
  const pathCandidates = readPreToolUsePathCandidates(payload);
2344
2429
  let blocked = false;
2430
+ let blockedDetail = "implementation/write tools are blocked until an explicit execution handoff workflow is activated";
2345
2431
  if (toolName === "Bash") {
2346
2432
  blocked = !isAllowedRalplanBashWrite(cwd, command);
2433
+ if (blocked) {
2434
+ const targets = extractDeepInterviewCommandWriteTargets(command);
2435
+ const blockedTarget = targets.find((target) => !isAllowedRalplanArtifactPath(cwd, target));
2436
+ blockedDetail = blockedTarget
2437
+ ? `write target ${blockedTarget} is not under allowed planning artifact paths (${RALPLAN_ALLOWED_WRITE_PREFIXES.join(", ")})`
2438
+ : "Bash write intent did not identify an allowed planning artifact path";
2439
+ }
2347
2440
  }
2348
2441
  else if (PLANNING_MODE_IMPLEMENTATION_TOOL_NAMES.has(toolName)) {
2349
- blocked = pathCandidates.length === 0
2350
- || !pathCandidates.every((candidate) => isAllowedRalplanArtifactPath(cwd, candidate));
2442
+ if (pathCandidates.length === 0) {
2443
+ blocked = true;
2444
+ blockedDetail = `${toolName} did not include a file path; only planning artifact paths are allowed`;
2445
+ }
2446
+ else {
2447
+ const blockedPath = pathCandidates.find((candidate) => !isAllowedRalplanArtifactPath(cwd, candidate));
2448
+ blocked = blockedPath !== undefined;
2449
+ if (blockedPath !== undefined) {
2450
+ blockedDetail = `path ${blockedPath} is not under allowed planning artifact paths (${RALPLAN_ALLOWED_WRITE_PREFIXES.join(", ")})`;
2451
+ }
2452
+ }
2351
2453
  }
2352
2454
  if (!blocked)
2353
2455
  return null;
@@ -2359,7 +2461,7 @@ async function buildRalplanPreToolUseBoundaryOutput(payload, cwd, stateDir, reso
2359
2461
  : "Ralplan is consensus-planning mode";
2360
2462
  return {
2361
2463
  decision: "block",
2362
- reason: `${planningModeLabel} is active (phase: ${phase}); implementation/write tools are blocked until an explicit execution handoff workflow is activated.`,
2464
+ reason: `${planningModeLabel} is active (phase: ${phase}); implementation/write tools are blocked until an explicit execution handoff workflow is activated; ${blockedDetail}.`,
2363
2465
  hookSpecificOutput: {
2364
2466
  hookEventName: "PreToolUse",
2365
2467
  additionalContext: `${planningModeDescription}. `
@@ -2383,8 +2485,9 @@ async function buildDeepInterviewPreToolUseBoundaryOutput(payload, cwd, stateDir
2383
2485
  blocked = !isAllowedDeepInterviewBashWrite(cwd, command);
2384
2486
  }
2385
2487
  else if (DEEP_INTERVIEW_IMPLEMENTATION_TOOL_NAMES.has(toolName)) {
2386
- blocked = pathCandidates.length === 0
2387
- || !pathCandidates.every((candidate) => isAllowedDeepInterviewArtifactPath(cwd, candidate));
2488
+ const candidates = collectImplementationToolPathCandidates(payload, toolName, pathCandidates);
2489
+ blocked = candidates.length === 0
2490
+ || !candidates.every((candidate) => isAllowedDeepInterviewArtifactPath(cwd, candidate));
2388
2491
  }
2389
2492
  if (!blocked)
2390
2493
  return null;
@@ -3217,18 +3320,35 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
3217
3320
  if (hookEventName === "SessionStart" && nativeSessionId) {
3218
3321
  const transcriptPath = safeString(payload.transcript_path ?? payload.transcriptPath).trim();
3219
3322
  const subagentSessionStart = readNativeSubagentSessionStartMetadata(transcriptPath);
3220
- if (subagentSessionStart && canonicalSessionId) {
3323
+ if (subagentSessionStart) {
3324
+ // A native child/subagent SessionStart carries a parent_thread_id in its
3325
+ // transcript session_meta. Treat it as a child-agent lifecycle event for
3326
+ // notification suppression and subagent tracking even when the canonical
3327
+ // leader session has not been reconciled yet (#2831). A child start must
3328
+ // never promote itself into a root/leader session or emit an independent
3329
+ // session-start notification at session/minimal verbosity.
3221
3330
  isSubagentSessionStart = true;
3222
- const belongsToCanonicalSession = await nativeSubagentSessionStartBelongsToCanonicalSession(cwd, canonicalSessionId, currentSessionState, subagentSessionStart);
3223
- if (belongsToCanonicalSession) {
3224
- resolvedNativeSessionId = nativeSessionId;
3225
- await recordNativeSubagentSessionStart(cwd, canonicalSessionId, nativeSessionId, subagentSessionStart, transcriptPath);
3331
+ if (canonicalSessionId) {
3332
+ const belongsToCanonicalSession = await nativeSubagentSessionStartBelongsToCanonicalSession(cwd, canonicalSessionId, currentSessionState, subagentSessionStart);
3333
+ if (belongsToCanonicalSession) {
3334
+ resolvedNativeSessionId = nativeSessionId;
3335
+ await recordNativeSubagentSessionStart(cwd, canonicalSessionId, nativeSessionId, subagentSessionStart, transcriptPath);
3336
+ }
3337
+ else {
3338
+ skipCanonicalSessionStartContext = true;
3339
+ resolvedNativeSessionId =
3340
+ safeString(currentSessionState?.native_session_id).trim() || nativeSessionId;
3341
+ await recordIgnoredNativeSubagentSessionStart(cwd, canonicalSessionId, nativeSessionId, subagentSessionStart, transcriptPath);
3342
+ }
3226
3343
  }
3227
3344
  else {
3345
+ // No canonical leader session is resolved in this worktree yet. Still
3346
+ // register the child thread under its parent so its later Stop is
3347
+ // recognized as subagent-scoped, skip leader SessionStart context, and
3348
+ // do not reconcile the child as a new root session.
3228
3349
  skipCanonicalSessionStartContext = true;
3229
- resolvedNativeSessionId =
3230
- safeString(currentSessionState?.native_session_id).trim() || nativeSessionId;
3231
- await recordIgnoredNativeSubagentSessionStart(cwd, canonicalSessionId, nativeSessionId, subagentSessionStart, transcriptPath);
3350
+ resolvedNativeSessionId = nativeSessionId;
3351
+ await recordNativeSubagentSessionStart(cwd, canonicalSessionId, nativeSessionId, subagentSessionStart, transcriptPath);
3232
3352
  }
3233
3353
  }
3234
3354
  else {