oh-my-codex 0.18.1 → 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 (310) hide show
  1. package/Cargo.lock +6 -6
  2. package/Cargo.toml +1 -1
  3. package/README.md +4 -2
  4. package/dist/agents/__tests__/definitions.test.js +23 -0
  5. package/dist/agents/__tests__/definitions.test.js.map +1 -1
  6. package/dist/agents/__tests__/native-config.test.js +20 -0
  7. package/dist/agents/__tests__/native-config.test.js.map +1 -1
  8. package/dist/agents/definitions.d.ts.map +1 -1
  9. package/dist/agents/definitions.js +40 -0
  10. package/dist/agents/definitions.js.map +1 -1
  11. package/dist/agents/native-config.d.ts +1 -0
  12. package/dist/agents/native-config.d.ts.map +1 -1
  13. package/dist/agents/native-config.js +4 -0
  14. package/dist/agents/native-config.js.map +1 -1
  15. package/dist/auth/__tests__/config-sessions.test.d.ts +2 -0
  16. package/dist/auth/__tests__/config-sessions.test.d.ts.map +1 -0
  17. package/dist/auth/__tests__/config-sessions.test.js +48 -0
  18. package/dist/auth/__tests__/config-sessions.test.js.map +1 -0
  19. package/dist/auth/__tests__/quota-rotation.test.d.ts +2 -0
  20. package/dist/auth/__tests__/quota-rotation.test.d.ts.map +1 -0
  21. package/dist/auth/__tests__/quota-rotation.test.js +33 -0
  22. package/dist/auth/__tests__/quota-rotation.test.js.map +1 -0
  23. package/dist/auth/__tests__/redact.test.d.ts +2 -0
  24. package/dist/auth/__tests__/redact.test.d.ts.map +1 -0
  25. package/dist/auth/__tests__/redact.test.js +20 -0
  26. package/dist/auth/__tests__/redact.test.js.map +1 -0
  27. package/dist/auth/__tests__/storage.test.d.ts +2 -0
  28. package/dist/auth/__tests__/storage.test.d.ts.map +1 -0
  29. package/dist/auth/__tests__/storage.test.js +108 -0
  30. package/dist/auth/__tests__/storage.test.js.map +1 -0
  31. package/dist/auth/config.d.ts +9 -0
  32. package/dist/auth/config.d.ts.map +1 -0
  33. package/dist/auth/config.js +77 -0
  34. package/dist/auth/config.js.map +1 -0
  35. package/dist/auth/hotswap.d.ts +36 -0
  36. package/dist/auth/hotswap.d.ts.map +1 -0
  37. package/dist/auth/hotswap.js +159 -0
  38. package/dist/auth/hotswap.js.map +1 -0
  39. package/dist/auth/index.d.ts +8 -0
  40. package/dist/auth/index.d.ts.map +1 -0
  41. package/dist/auth/index.js +8 -0
  42. package/dist/auth/index.js.map +1 -0
  43. package/dist/auth/paths.d.ts +12 -0
  44. package/dist/auth/paths.d.ts.map +1 -0
  45. package/dist/auth/paths.js +78 -0
  46. package/dist/auth/paths.js.map +1 -0
  47. package/dist/auth/quota-detector.d.ts +10 -0
  48. package/dist/auth/quota-detector.d.ts.map +1 -0
  49. package/dist/auth/quota-detector.js +40 -0
  50. package/dist/auth/quota-detector.js.map +1 -0
  51. package/dist/auth/redact.d.ts +2 -0
  52. package/dist/auth/redact.d.ts.map +1 -0
  53. package/dist/auth/redact.js +26 -0
  54. package/dist/auth/redact.js.map +1 -0
  55. package/dist/auth/rotation.d.ts +9 -0
  56. package/dist/auth/rotation.d.ts.map +1 -0
  57. package/dist/auth/rotation.js +26 -0
  58. package/dist/auth/rotation.js.map +1 -0
  59. package/dist/auth/sessions.d.ts +15 -0
  60. package/dist/auth/sessions.d.ts.map +1 -0
  61. package/dist/auth/sessions.js +62 -0
  62. package/dist/auth/sessions.js.map +1 -0
  63. package/dist/auth/storage.d.ts +27 -0
  64. package/dist/auth/storage.d.ts.map +1 -0
  65. package/dist/auth/storage.js +111 -0
  66. package/dist/auth/storage.js.map +1 -0
  67. package/dist/catalog/__tests__/generator.test.js +4 -0
  68. package/dist/catalog/__tests__/generator.test.js.map +1 -1
  69. package/dist/cli/__tests__/auth.test.d.ts +2 -0
  70. package/dist/cli/__tests__/auth.test.d.ts.map +1 -0
  71. package/dist/cli/__tests__/auth.test.js +168 -0
  72. package/dist/cli/__tests__/auth.test.js.map +1 -0
  73. package/dist/cli/__tests__/doctor-warning-copy.test.js +112 -5
  74. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  75. package/dist/cli/__tests__/explore.test.js +20 -0
  76. package/dist/cli/__tests__/explore.test.js.map +1 -1
  77. package/dist/cli/__tests__/index.test.js +171 -21
  78. package/dist/cli/__tests__/index.test.js.map +1 -1
  79. package/dist/cli/__tests__/launch-fallback.test.js +51 -3
  80. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  81. package/dist/cli/__tests__/nested-help-routing.test.js +1 -0
  82. package/dist/cli/__tests__/nested-help-routing.test.js.map +1 -1
  83. package/dist/cli/__tests__/question.test.js +2 -2
  84. package/dist/cli/__tests__/question.test.js.map +1 -1
  85. package/dist/cli/__tests__/setup-agents-overwrite.test.js +30 -1
  86. package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
  87. package/dist/cli/__tests__/setup-install-mode.test.js +47 -0
  88. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  89. package/dist/cli/auth.d.ts +4 -0
  90. package/dist/cli/auth.d.ts.map +1 -0
  91. package/dist/cli/auth.js +89 -0
  92. package/dist/cli/auth.js.map +1 -0
  93. package/dist/cli/doctor.d.ts.map +1 -1
  94. package/dist/cli/doctor.js +190 -7
  95. package/dist/cli/doctor.js.map +1 -1
  96. package/dist/cli/explore.d.ts.map +1 -1
  97. package/dist/cli/explore.js +12 -0
  98. package/dist/cli/explore.js.map +1 -1
  99. package/dist/cli/index.d.ts +27 -3
  100. package/dist/cli/index.d.ts.map +1 -1
  101. package/dist/cli/index.js +245 -47
  102. package/dist/cli/index.js.map +1 -1
  103. package/dist/cli/setup.d.ts.map +1 -1
  104. package/dist/cli/setup.js +11 -3
  105. package/dist/cli/setup.js.map +1 -1
  106. package/dist/config/__tests__/codex-hooks.test.js +3 -3
  107. package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
  108. package/dist/config/__tests__/deep-interview.test.d.ts +2 -0
  109. package/dist/config/__tests__/deep-interview.test.d.ts.map +1 -0
  110. package/dist/config/__tests__/deep-interview.test.js +239 -0
  111. package/dist/config/__tests__/deep-interview.test.js.map +1 -0
  112. package/dist/config/__tests__/generator-idempotent.test.js +123 -0
  113. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  114. package/dist/config/codex-hooks.d.ts +1 -0
  115. package/dist/config/codex-hooks.d.ts.map +1 -1
  116. package/dist/config/codex-hooks.js +2 -4
  117. package/dist/config/codex-hooks.js.map +1 -1
  118. package/dist/config/deep-interview.d.ts +22 -0
  119. package/dist/config/deep-interview.d.ts.map +1 -0
  120. package/dist/config/deep-interview.js +151 -0
  121. package/dist/config/deep-interview.js.map +1 -0
  122. package/dist/config/generator.d.ts +19 -2
  123. package/dist/config/generator.d.ts.map +1 -1
  124. package/dist/config/generator.js +198 -29
  125. package/dist/config/generator.js.map +1 -1
  126. package/dist/goal-workflows/__tests__/codex-goal-snapshot.test.js +21 -0
  127. package/dist/goal-workflows/__tests__/codex-goal-snapshot.test.js.map +1 -1
  128. package/dist/goal-workflows/codex-goal-snapshot.d.ts +3 -0
  129. package/dist/goal-workflows/codex-goal-snapshot.d.ts.map +1 -1
  130. package/dist/goal-workflows/codex-goal-snapshot.js +45 -2
  131. package/dist/goal-workflows/codex-goal-snapshot.js.map +1 -1
  132. package/dist/hooks/__tests__/agents-overlay.test.js +2 -0
  133. package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
  134. package/dist/hooks/__tests__/autopilot-skill-contract.test.js +17 -0
  135. package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
  136. package/dist/hooks/__tests__/explore-routing.test.js +1 -0
  137. package/dist/hooks/__tests__/explore-routing.test.js.map +1 -1
  138. package/dist/hooks/__tests__/keyword-detector.test.js +471 -15
  139. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  140. package/dist/hooks/__tests__/prometheus-strict-contract.test.d.ts +2 -0
  141. package/dist/hooks/__tests__/prometheus-strict-contract.test.d.ts.map +1 -0
  142. package/dist/hooks/__tests__/prometheus-strict-contract.test.js +320 -0
  143. package/dist/hooks/__tests__/prometheus-strict-contract.test.js.map +1 -0
  144. package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js +12 -0
  145. package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js.map +1 -1
  146. package/dist/hooks/__tests__/research-workflow-boundaries.test.d.ts +2 -0
  147. package/dist/hooks/__tests__/research-workflow-boundaries.test.d.ts.map +1 -0
  148. package/dist/hooks/__tests__/research-workflow-boundaries.test.js +35 -0
  149. package/dist/hooks/__tests__/research-workflow-boundaries.test.js.map +1 -0
  150. package/dist/hooks/deep-interview-config-instruction.d.ts +3 -0
  151. package/dist/hooks/deep-interview-config-instruction.d.ts.map +1 -0
  152. package/dist/hooks/deep-interview-config-instruction.js +47 -0
  153. package/dist/hooks/deep-interview-config-instruction.js.map +1 -0
  154. package/dist/hooks/explore-routing.d.ts.map +1 -1
  155. package/dist/hooks/explore-routing.js +1 -0
  156. package/dist/hooks/explore-routing.js.map +1 -1
  157. package/dist/hooks/keyword-detector.d.ts +6 -1
  158. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  159. package/dist/hooks/keyword-detector.js +80 -14
  160. package/dist/hooks/keyword-detector.js.map +1 -1
  161. package/dist/hooks/keyword-registry.d.ts.map +1 -1
  162. package/dist/hooks/keyword-registry.js +1 -0
  163. package/dist/hooks/keyword-registry.js.map +1 -1
  164. package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
  165. package/dist/hooks/prompt-guidance-contract.js +11 -0
  166. package/dist/hooks/prompt-guidance-contract.js.map +1 -1
  167. package/dist/hud/__tests__/hud-tmux-injection.test.js +22 -0
  168. package/dist/hud/__tests__/hud-tmux-injection.test.js.map +1 -1
  169. package/dist/hud/__tests__/reconcile.test.js +213 -17
  170. package/dist/hud/__tests__/reconcile.test.js.map +1 -1
  171. package/dist/hud/__tests__/render.test.js +84 -0
  172. package/dist/hud/__tests__/render.test.js.map +1 -1
  173. package/dist/hud/__tests__/state.test.js +51 -1
  174. package/dist/hud/__tests__/state.test.js.map +1 -1
  175. package/dist/hud/__tests__/tmux.test.js +171 -23
  176. package/dist/hud/__tests__/tmux.test.js.map +1 -1
  177. package/dist/hud/index.d.ts +1 -1
  178. package/dist/hud/index.d.ts.map +1 -1
  179. package/dist/hud/index.js +8 -3
  180. package/dist/hud/index.js.map +1 -1
  181. package/dist/hud/reconcile.d.ts +1 -1
  182. package/dist/hud/reconcile.d.ts.map +1 -1
  183. package/dist/hud/reconcile.js +14 -3
  184. package/dist/hud/reconcile.js.map +1 -1
  185. package/dist/hud/render.d.ts.map +1 -1
  186. package/dist/hud/render.js +26 -0
  187. package/dist/hud/render.js.map +1 -1
  188. package/dist/hud/state.d.ts +2 -1
  189. package/dist/hud/state.d.ts.map +1 -1
  190. package/dist/hud/state.js +62 -1
  191. package/dist/hud/state.js.map +1 -1
  192. package/dist/hud/tmux.d.ts +17 -3
  193. package/dist/hud/tmux.d.ts.map +1 -1
  194. package/dist/hud/tmux.js +96 -10
  195. package/dist/hud/tmux.js.map +1 -1
  196. package/dist/hud/types.d.ts +22 -0
  197. package/dist/hud/types.d.ts.map +1 -1
  198. package/dist/hud/types.js.map +1 -1
  199. package/dist/pipeline/__tests__/orchestrator.test.js +63 -1
  200. package/dist/pipeline/__tests__/orchestrator.test.js.map +1 -1
  201. package/dist/pipeline/__tests__/stages.test.js +410 -4
  202. package/dist/pipeline/__tests__/stages.test.js.map +1 -1
  203. package/dist/pipeline/orchestrator.d.ts.map +1 -1
  204. package/dist/pipeline/orchestrator.js +29 -2
  205. package/dist/pipeline/orchestrator.js.map +1 -1
  206. package/dist/pipeline/stages/ralplan.d.ts.map +1 -1
  207. package/dist/pipeline/stages/ralplan.js +41 -6
  208. package/dist/pipeline/stages/ralplan.js.map +1 -1
  209. package/dist/question/__tests__/ui.test.js +43 -10
  210. package/dist/question/__tests__/ui.test.js.map +1 -1
  211. package/dist/question/deep-interview.d.ts +2 -0
  212. package/dist/question/deep-interview.d.ts.map +1 -1
  213. package/dist/question/deep-interview.js.map +1 -1
  214. package/dist/question/ui.d.ts +12 -0
  215. package/dist/question/ui.d.ts.map +1 -1
  216. package/dist/question/ui.js +83 -46
  217. package/dist/question/ui.js.map +1 -1
  218. package/dist/ralplan/__tests__/runtime.test.js +200 -10
  219. package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
  220. package/dist/ralplan/consensus-gate.d.ts +23 -0
  221. package/dist/ralplan/consensus-gate.d.ts.map +1 -0
  222. package/dist/ralplan/consensus-gate.js +212 -0
  223. package/dist/ralplan/consensus-gate.js.map +1 -0
  224. package/dist/ralplan/runtime.d.ts +25 -0
  225. package/dist/ralplan/runtime.d.ts.map +1 -1
  226. package/dist/ralplan/runtime.js +144 -8
  227. package/dist/ralplan/runtime.js.map +1 -1
  228. package/dist/scripts/__tests__/codex-native-hook.test.js +1034 -28
  229. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  230. package/dist/scripts/__tests__/docs-site-contract.test.d.ts +2 -0
  231. package/dist/scripts/__tests__/docs-site-contract.test.d.ts.map +1 -0
  232. package/dist/scripts/__tests__/docs-site-contract.test.js +42 -0
  233. package/dist/scripts/__tests__/docs-site-contract.test.js.map +1 -0
  234. package/dist/scripts/__tests__/notify-dispatcher.test.js +115 -2
  235. package/dist/scripts/__tests__/notify-dispatcher.test.js.map +1 -1
  236. package/dist/scripts/__tests__/run-test-files.test.js +57 -0
  237. package/dist/scripts/__tests__/run-test-files.test.js.map +1 -1
  238. package/dist/scripts/__tests__/verify-native-agents.test.js +2 -2
  239. package/dist/scripts/__tests__/verify-native-agents.test.js.map +1 -1
  240. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  241. package/dist/scripts/codex-native-hook.js +238 -36
  242. package/dist/scripts/codex-native-hook.js.map +1 -1
  243. package/dist/scripts/notify-dispatcher.js +188 -4
  244. package/dist/scripts/notify-dispatcher.js.map +1 -1
  245. package/dist/scripts/run-test-files.js +13 -0
  246. package/dist/scripts/run-test-files.js.map +1 -1
  247. package/dist/state/__tests__/planning-gate.test.d.ts +2 -0
  248. package/dist/state/__tests__/planning-gate.test.d.ts.map +1 -0
  249. package/dist/state/__tests__/planning-gate.test.js +219 -0
  250. package/dist/state/__tests__/planning-gate.test.js.map +1 -0
  251. package/dist/state/__tests__/workflow-transition.test.js +6 -0
  252. package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
  253. package/dist/state/workflow-transition.d.ts +24 -1
  254. package/dist/state/workflow-transition.d.ts.map +1 -1
  255. package/dist/state/workflow-transition.js +70 -0
  256. package/dist/state/workflow-transition.js.map +1 -1
  257. package/dist/subagents/tracker.d.ts.map +1 -1
  258. package/dist/subagents/tracker.js +4 -3
  259. package/dist/subagents/tracker.js.map +1 -1
  260. package/dist/team/__tests__/runtime.test.js +36 -44
  261. package/dist/team/__tests__/runtime.test.js.map +1 -1
  262. package/dist/team/__tests__/tmux-session.test.js +144 -18
  263. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  264. package/dist/team/runtime.d.ts.map +1 -1
  265. package/dist/team/runtime.js +10 -20
  266. package/dist/team/runtime.js.map +1 -1
  267. package/dist/team/tmux-session.d.ts.map +1 -1
  268. package/dist/team/tmux-session.js +22 -6
  269. package/dist/team/tmux-session.js.map +1 -1
  270. package/dist/ultragoal/__tests__/artifacts.test.js +50 -0
  271. package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
  272. package/dist/ultragoal/artifacts.d.ts.map +1 -1
  273. package/dist/ultragoal/artifacts.js +28 -2
  274. package/dist/ultragoal/artifacts.js.map +1 -1
  275. package/package.json +1 -1
  276. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  277. package/plugins/oh-my-codex/skills/autopilot/SKILL.md +16 -4
  278. package/plugins/oh-my-codex/skills/autoresearch/SKILL.md +4 -0
  279. package/plugins/oh-my-codex/skills/autoresearch-goal/SKILL.md +1 -1
  280. package/plugins/oh-my-codex/skills/best-practice-research/SKILL.md +1 -1
  281. package/plugins/oh-my-codex/skills/deep-interview/SKILL.md +10 -0
  282. package/plugins/oh-my-codex/skills/pipeline/SKILL.md +1 -1
  283. package/plugins/oh-my-codex/skills/plan/SKILL.md +1 -1
  284. package/plugins/oh-my-codex/skills/prometheus-strict/README.md +35 -0
  285. package/plugins/oh-my-codex/skills/prometheus-strict/SKILL.md +219 -0
  286. package/plugins/oh-my-codex/skills/ralplan/SKILL.md +24 -5
  287. package/prompts/prometheus-strict-metis.md +274 -0
  288. package/prompts/prometheus-strict-momus.md +82 -0
  289. package/prompts/prometheus-strict-oracle.md +107 -0
  290. package/prompts/researcher.md +22 -3
  291. package/prompts/scholastic.md +11 -0
  292. package/skills/autopilot/SKILL.md +16 -4
  293. package/skills/autoresearch/SKILL.md +4 -0
  294. package/skills/autoresearch-goal/SKILL.md +1 -1
  295. package/skills/best-practice-research/SKILL.md +1 -1
  296. package/skills/deep-interview/SKILL.md +10 -0
  297. package/skills/pipeline/SKILL.md +1 -1
  298. package/skills/plan/SKILL.md +1 -1
  299. package/skills/prometheus-strict/README.md +35 -0
  300. package/skills/prometheus-strict/SKILL.md +219 -0
  301. package/skills/ralplan/SKILL.md +24 -5
  302. package/src/scripts/__tests__/codex-native-hook.test.ts +1307 -61
  303. package/src/scripts/__tests__/docs-site-contract.test.ts +47 -0
  304. package/src/scripts/__tests__/notify-dispatcher.test.ts +132 -3
  305. package/src/scripts/__tests__/run-test-files.test.ts +67 -0
  306. package/src/scripts/__tests__/verify-native-agents.test.ts +2 -2
  307. package/src/scripts/codex-native-hook.ts +260 -31
  308. package/src/scripts/notify-dispatcher.ts +202 -4
  309. package/src/scripts/run-test-files.ts +13 -0
  310. package/templates/catalog-manifest.json +27 -0
@@ -3,8 +3,9 @@ import { closeSync, existsSync, openSync, readFileSync, readSync } from "fs";
3
3
  import { appendFile, mkdir, readFile, readdir, stat, writeFile } from "fs/promises";
4
4
  import { extname, join, relative, resolve } from "path";
5
5
  import { pathToFileURL } from "url";
6
- import { readModeState, readModeStateForActiveDecision, readModeStateForSession, updateModeState } from "../modes/base.js";
6
+ import { readModeStateForActiveDecision, readModeStateForSession, updateModeState } from "../modes/base.js";
7
7
  import {
8
+ SKILL_ACTIVE_STATE_FILE,
8
9
  extractSessionIdFromInitializedStatePath,
9
10
  getSkillActiveStatePathsForStateDir,
10
11
  listActiveSkills,
@@ -14,6 +15,7 @@ import {
14
15
  } from "../state/skill-active.js";
15
16
  import {
16
17
  readSubagentSessionSummary,
18
+ readSubagentTrackingState,
17
19
  recordSubagentTurnForSession,
18
20
  } from "../subagents/tracker.js";
19
21
  import { resolveCanonicalTeamStateRoot, resolveWorkerNotifyTeamStateRootPath } from "../team/state-root.js";
@@ -42,6 +44,7 @@ import {
42
44
  recordSkillActivation,
43
45
  type SkillActiveState,
44
46
  } from "../hooks/keyword-detector.js";
47
+ import { buildDeepInterviewConfigInstruction } from "../hooks/deep-interview-config-instruction.js";
45
48
  import {
46
49
  detectNativeStopStallPattern,
47
50
  loadAutoNudgeConfig,
@@ -297,18 +300,32 @@ async function isNativeSubagentHook(
297
300
  nativeSessionId: string,
298
301
  threadId: string,
299
302
  ): Promise<boolean> {
300
- const sessionId = canonicalSessionId.trim();
301
- if (!sessionId) return false;
302
-
303
- const summary = await readSubagentSessionSummary(cwd, sessionId).catch(() => null);
304
- if (!summary) return false;
305
-
306
303
  const candidateIds = [nativeSessionId, threadId]
307
304
  .map((value) => value.trim())
308
305
  .filter(Boolean);
309
306
  if (candidateIds.length === 0) return false;
310
307
 
311
- return candidateIds.some((id) => summary.allSubagentThreadIds.includes(id));
308
+ const sessionId = canonicalSessionId.trim();
309
+ if (sessionId) {
310
+ const summary = await readSubagentSessionSummary(cwd, sessionId).catch(() => null);
311
+ if (summary && candidateIds.some((id) => summary.allSubagentThreadIds.includes(id))) {
312
+ return true;
313
+ }
314
+ }
315
+
316
+ // Native Codex resume can report the child native session as the canonical
317
+ // session id before OMX reconciles it back to the owning session. In that
318
+ // window the per-session summary lookup above misses the child and a
319
+ // subagent UserPromptSubmit can accidentally activate workflow keywords from
320
+ // quoted review context. Fall back to the global tracking index so any known
321
+ // subagent thread is treated as subagent-scoped, regardless of the current
322
+ // hook payload's session-id mapping.
323
+ const trackingState = await readSubagentTrackingState(cwd).catch(() => null);
324
+ if (!trackingState) return false;
325
+
326
+ return Object.values(trackingState.sessions).some((session) => (
327
+ candidateIds.some((id) => session.threads[id]?.kind === "subagent")
328
+ ));
312
329
  }
313
330
 
314
331
  function shouldSuppressSubagentLifecycleHookDispatch(): boolean {
@@ -1683,6 +1700,17 @@ function buildSkillStateCliInstruction(mode: string, statePath: string): string
1683
1700
  return `skill: ${mode} activated and initial state initialized at ${statePath}; use CLI-first state updates via \`omx state write/read/clear --input '<json>' --json\`; use omx_state MCP only when explicit MCP compatibility is enabled.`;
1684
1701
  }
1685
1702
 
1703
+ function buildAutopilotPromptActivationNote(skillState?: SkillActiveState | null): string | null {
1704
+ if (skillState?.initialized_mode !== "autopilot") return null;
1705
+ return [
1706
+ "Autopilot protocol: the durable default chain is $deep-interview -> $ralplan -> $ultragoal (+ $team if needed) -> $code-review -> $ultraqa (deep-interview -> ralplan -> ultragoal -> code-review -> ultraqa).",
1707
+ "Start/resume at current_phase=deep-interview unless the task is clear and bounded; if deep-interview is intentionally skipped, persist and state an explicit deep_interview_gate.skip_reason before moving to ralplan.",
1708
+ "The ralplan phase is not complete until Planner output has been reviewed sequentially by Architect and then Critic; do not hand off to Ultragoal or implementation until the ralplan state/artifact records both ralplan_architect_review and ralplan_critic_review with approval or an explicit blocker.",
1709
+ "Do not silently fall back to ordinary $plan/ralplan-only handling; keep autopilot-state.json, skill-active-state.json, HUD/statusline, and Codex goal-mode handoff guidance visible while the workflow is active.",
1710
+ "When Codex goal tools are available, call get_goal/create_goal only from the active thread handoff and treat the active goal as the completion contract until code-review and ultraqa are clean.",
1711
+ ].join(" ");
1712
+ }
1713
+
1686
1714
  function buildAdditionalContextMessage(
1687
1715
  prompt: string,
1688
1716
  skillState?: SkillActiveState | null,
@@ -1693,7 +1721,24 @@ function buildAdditionalContextMessage(
1693
1721
  const promptPriorityMessage = buildPromptPriorityMessage(prompt);
1694
1722
  const matches = detectKeywords(prompt);
1695
1723
  const match = detectPrimaryKeyword(prompt);
1696
- if (!match) return promptPriorityMessage;
1724
+ if (!match) {
1725
+ const continuedSkill = safeString(skillState?.skill).trim();
1726
+ if (!continuedSkill) return promptPriorityMessage;
1727
+ const deepInterviewPromptActivationNote = skillState?.initialized_mode === "deep-interview"
1728
+ ? buildDeepInterviewQuestionBridgeInstruction(cwd, payload)
1729
+ : null;
1730
+ const deepInterviewConfigPromptActivationNote = buildDeepInterviewConfigInstruction(cwd, skillState);
1731
+ return [
1732
+ `OMX native UserPromptSubmit continued active workflow skill "${continuedSkill}".`,
1733
+ promptPriorityMessage,
1734
+ skillState?.initialized_mode && skillState.initialized_state_path
1735
+ ? buildSkillStateCliInstruction(skillState.initialized_mode, skillState.initialized_state_path)
1736
+ : null,
1737
+ deepInterviewPromptActivationNote,
1738
+ deepInterviewConfigPromptActivationNote,
1739
+ "Follow AGENTS.md routing and preserve workflow transition and planning-safety rules.",
1740
+ ].filter(Boolean).join(" ");
1741
+ }
1697
1742
  const detectedKeywordMessage = matches.length > 1
1698
1743
  ? `OMX native UserPromptSubmit detected workflow keywords ${matches.map((entry) => `"${entry.keyword}" -> ${entry.skill}`).join(", ")}.`
1699
1744
  : `OMX native UserPromptSubmit detected workflow keyword "${match.keyword}" -> ${match.skill}.`;
@@ -1710,12 +1755,14 @@ function buildAdditionalContextMessage(
1710
1755
  const deepInterviewPromptActivationNote = skillState?.initialized_mode === "deep-interview"
1711
1756
  ? buildDeepInterviewQuestionBridgeInstruction(cwd, payload)
1712
1757
  : null;
1758
+ const deepInterviewConfigPromptActivationNote = buildDeepInterviewConfigInstruction(cwd, skillState);
1713
1759
  const ultraworkPromptActivationNote = skillState?.initialized_mode === "ultrawork"
1714
1760
  ? "Ultrawork protocol: ground the task before editing, define pass/fail acceptance criteria, keep shared-file work local, and use direct-tool plus background evidence lanes only for truly independent work. Direct ultrawork provides lightweight verification only; Ralph owns persistence and the full verified-completion promise."
1715
1761
  : null;
1716
1762
  const ultragoalPromptActivationNote = match.skill === "ultragoal"
1717
1763
  ? "Ultragoal protocol: use `omx ultragoal create-goals` / `complete-goals` / `checkpoint` for `.omx/ultragoal` artifacts, then use Codex goal model tools only from the active agent handoff (`get_goal`, `create_goal`, `update_goal`) and never overwrite a different active Codex goal. Ultragoal does not call `/goal clear`; for multiple sequential ultragoal runs in one Codex session/thread, manually clear the completed Codex goal in the UI before creating the next aggregate goal."
1718
1764
  : null;
1765
+ const autopilotPromptActivationNote = buildAutopilotPromptActivationNote(skillState);
1719
1766
  const combinedTransitionMessage = (() => {
1720
1767
  if (!skillState?.transition_message) return null;
1721
1768
  if (matches.length <= 1 || activeSkills.length <= 1) return skillState.transition_message;
@@ -1743,6 +1790,8 @@ function buildAdditionalContextMessage(
1743
1790
  : null,
1744
1791
  promptPriorityMessage,
1745
1792
  ultragoalPromptActivationNote,
1793
+ autopilotPromptActivationNote,
1794
+ deepInterviewConfigPromptActivationNote,
1746
1795
  skillState.initialized_mode && skillState.initialized_state_path
1747
1796
  ? buildSkillStateCliInstruction(skillState.initialized_mode, skillState.initialized_state_path)
1748
1797
  : null,
@@ -1767,8 +1816,10 @@ function buildAdditionalContextMessage(
1767
1816
  promptPriorityMessage,
1768
1817
  initializedStateMessage,
1769
1818
  deepInterviewPromptActivationNote,
1819
+ deepInterviewConfigPromptActivationNote,
1770
1820
  ultraworkPromptActivationNote,
1771
1821
  ultragoalPromptActivationNote,
1822
+ autopilotPromptActivationNote,
1772
1823
  buildTeamRuntimeInstruction(cwd, payload),
1773
1824
  buildTeamHelpInstruction(cwd, payload),
1774
1825
  "Follow AGENTS.md routing and preserve workflow transition and planning-safety rules.",
@@ -1785,14 +1836,16 @@ function buildAdditionalContextMessage(
1785
1836
  promptPriorityMessage,
1786
1837
  buildSkillStateCliInstruction(skillState.initialized_mode, skillState.initialized_state_path),
1787
1838
  deepInterviewPromptActivationNote,
1839
+ deepInterviewConfigPromptActivationNote,
1788
1840
  ultraworkPromptActivationNote,
1789
1841
  ultragoalPromptActivationNote,
1842
+ autopilotPromptActivationNote,
1790
1843
  ralphPromptActivationNote,
1791
1844
  "Follow AGENTS.md routing and preserve workflow transition and planning-safety rules.",
1792
1845
  ].join(" ");
1793
1846
  }
1794
1847
 
1795
- return [detectedKeywordMessage, promptPriorityMessage, ultragoalPromptActivationNote, "Follow AGENTS.md routing and preserve workflow transition and planning-safety rules."].filter(Boolean).join(" ");
1848
+ return [detectedKeywordMessage, promptPriorityMessage, ultragoalPromptActivationNote, autopilotPromptActivationNote, "Follow AGENTS.md routing and preserve workflow transition and planning-safety rules."].filter(Boolean).join(" ");
1796
1849
  }
1797
1850
 
1798
1851
  function parseTeamWorkerEnv(rawValue: string): { teamName: string; workerName: string } | null {
@@ -2041,6 +2094,7 @@ async function findActiveGoalWorkflowReconciliationRequirement(cwd: string): Pro
2041
2094
  `If get_goal returns a completed task-scoped objective for the same aggregate ultragoal plan, checkpoint ${goalId} with evidence naming ${goalId} plus .omx/ultragoal/goals.json or ledger.jsonl and pass final quality-gate JSON; OMX will reconcile the completed planned scope without mutating Codex goal state.`,
2042
2095
  `If get_goal instead returns a different completed legacy objective and complete checkpointing fails, do not repeat --status complete in this thread.`,
2043
2096
  `Record the non-terminal blocker with: omx ultragoal checkpoint --goal-id ${goalId} --status blocked --codex-goal-json '<different completed get_goal JSON or path>' --evidence '<completed legacy Codex goal blocks create_goal in this thread>'.`,
2097
+ `If get_goal itself is unavailable with a Codex DB/schema/context error such as "no such table: thread_goals", record an auditable safe-recovery blocker instead: omx ultragoal checkpoint --goal-id ${goalId} --status blocked --codex-goal-json '<unavailable get_goal error JSON or path>' --evidence '<get_goal unavailable due to Codex DB/schema/context error; safe recovery requires a working Codex goal context>'.`,
2044
2098
  "Then continue only from a Codex goal context with no active/completed conflicting goal in the same repo/worktree and create the intended goal there.",
2045
2099
  ].join(" "),
2046
2100
  };
@@ -2127,36 +2181,60 @@ async function buildGoalWorkflowReconciliationStopOutput(
2127
2181
  };
2128
2182
  }
2129
2183
 
2184
+ interface TeamModeStateForStop {
2185
+ state: Record<string, unknown>;
2186
+ scope: "session" | "root";
2187
+ }
2188
+
2189
+ function teamStateMatchesThreadForStop(
2190
+ state: Record<string, unknown>,
2191
+ threadId?: string,
2192
+ options: { requireOwnerThread?: boolean } = {},
2193
+ ): boolean {
2194
+ const normalizedThreadId = safeString(threadId).trim();
2195
+ if (!normalizedThreadId) return true;
2196
+
2197
+ const ownerThreadId = safeString(state.owner_codex_thread_id ?? state.thread_id).trim();
2198
+ if (!ownerThreadId) return options.requireOwnerThread !== true;
2199
+ return ownerThreadId === normalizedThreadId;
2200
+ }
2201
+
2130
2202
  async function readTeamModeStateForStop(
2131
2203
  cwd: string,
2132
2204
  stateDir: string,
2133
2205
  sessionId?: string,
2134
- ): Promise<Record<string, unknown> | null> {
2206
+ threadId?: string,
2207
+ ): Promise<TeamModeStateForStop | null> {
2135
2208
  const normalizedSessionId = safeString(sessionId).trim();
2136
- if (!normalizedSessionId) {
2137
- return await readModeState("team", cwd);
2138
- }
2209
+ if (!normalizedSessionId) return null;
2139
2210
 
2140
2211
  const scopedState = await readStopSessionPinnedState("team-state.json", cwd, normalizedSessionId, stateDir);
2141
- if (scopedState) return scopedState;
2212
+ if (scopedState) {
2213
+ return teamStateMatchesThreadForStop(scopedState, threadId)
2214
+ ? { state: scopedState, scope: "session" }
2215
+ : null;
2216
+ }
2142
2217
 
2143
2218
  const rootState = await readJsonIfExists(join(stateDir, "team-state.json"));
2144
2219
  if (rootState?.active !== true) return null;
2145
2220
 
2221
+ const teamName = safeString(rootState.team_name).trim();
2222
+ if (!teamName) return null;
2223
+
2146
2224
  const ownerSessionId = safeString(rootState.session_id).trim();
2147
- if (ownerSessionId && ownerSessionId !== normalizedSessionId) {
2148
- return null;
2149
- }
2225
+ if (!ownerSessionId || ownerSessionId !== normalizedSessionId) return null;
2226
+ if (!teamStateMatchesThreadForStop(rootState, threadId, { requireOwnerThread: true })) return null;
2150
2227
 
2151
- return rootState;
2228
+ return { state: rootState, scope: "root" };
2152
2229
  }
2153
2230
 
2154
- async function buildTeamStopOutput(cwd: string, sessionId?: string): Promise<Record<string, unknown> | null> {
2231
+ async function buildTeamStopOutput(cwd: string, sessionId?: string, threadId?: string): Promise<Record<string, unknown> | null> {
2155
2232
  if (await readCanonicalTerminalRunStateForStop(cwd, sessionId, "team")) {
2156
2233
  return null;
2157
2234
  }
2158
- const teamState = await readTeamModeStateForStop(cwd, getBaseStateDir(cwd), sessionId);
2159
- if (teamState?.active !== true) return null;
2235
+ const teamStateForStop = await readTeamModeStateForStop(cwd, getBaseStateDir(cwd), sessionId, threadId);
2236
+ if (!teamStateForStop || teamStateForStop.state.active !== true) return null;
2237
+ const teamState = teamStateForStop.state;
2160
2238
  const teamName = safeString(teamState.team_name).trim();
2161
2239
  if (teamName) {
2162
2240
  const canonicalTeamDir = join(resolveCanonicalTeamStateRoot(cwd), "team", teamName);
@@ -2165,7 +2243,9 @@ async function buildTeamStopOutput(cwd: string, sessionId?: string): Promise<Rec
2165
2243
  }
2166
2244
  }
2167
2245
  const coarsePhase = teamState.current_phase;
2168
- const canonicalPhase = teamName ? (await readTeamPhase(teamName, cwd))?.current_phase ?? coarsePhase : coarsePhase;
2246
+ const canonicalPhaseState = teamName ? await readTeamPhase(teamName, cwd) : null;
2247
+ if (teamStateForStop.scope === "root" && !canonicalPhaseState) return null;
2248
+ const canonicalPhase = canonicalPhaseState?.current_phase ?? coarsePhase;
2169
2249
  if (!isNonTerminalPhase(canonicalPhase)) return null;
2170
2250
  return buildTeamStopOutputForPhase(teamName, formatPhase(canonicalPhase));
2171
2251
  }
@@ -2272,6 +2352,151 @@ async function readStopSessionPinnedState(
2272
2352
  return readJsonIfExists(statePath);
2273
2353
  }
2274
2354
 
2355
+ const DEEP_INTERVIEW_ALLOWED_WRITE_PREFIXES = [
2356
+ ".omx/context",
2357
+ ".omx/interviews",
2358
+ ".omx/specs",
2359
+ ".omx/state",
2360
+ ] as const;
2361
+
2362
+ const DEEP_INTERVIEW_IMPLEMENTATION_TOOL_NAMES = new Set([
2363
+ "Write",
2364
+ "Edit",
2365
+ "MultiEdit",
2366
+ "apply_patch",
2367
+ "ApplyPatch",
2368
+ ]);
2369
+
2370
+ function isActiveDeepInterviewPhase(state: Record<string, unknown> | null): boolean {
2371
+ if (!state || state.active !== true) return false;
2372
+ const mode = safeString(state.mode).trim();
2373
+ if (mode && mode !== "deep-interview") return false;
2374
+ const phase = safeString(state.current_phase ?? state.currentPhase).trim().toLowerCase();
2375
+ if (phase && (TERMINAL_MODE_PHASES.has(phase) || phase === "completing")) return false;
2376
+ return true;
2377
+ }
2378
+
2379
+ function isAllowedDeepInterviewArtifactPath(cwd: string, rawPath: string): boolean {
2380
+ const trimmed = rawPath.trim().replace(/^['"]|['"]$/g, "");
2381
+ if (!trimmed || trimmed.includes("\0")) return false;
2382
+ let relativePath: string;
2383
+ try {
2384
+ const absolute = resolve(cwd, trimmed);
2385
+ relativePath = relative(cwd, absolute).replace(/\\/g, "/");
2386
+ } catch {
2387
+ return false;
2388
+ }
2389
+ if (!relativePath || relativePath.startsWith("..") || relativePath.startsWith("/")) return false;
2390
+ return DEEP_INTERVIEW_ALLOWED_WRITE_PREFIXES.some((prefix) => (
2391
+ relativePath === prefix || relativePath.startsWith(`${prefix}/`)
2392
+ ));
2393
+ }
2394
+
2395
+ function readPreToolUseCommand(payload: CodexHookPayload): string {
2396
+ const toolInput = safeObject(payload.tool_input);
2397
+ return safeString(toolInput.command).trim();
2398
+ }
2399
+
2400
+ function readPreToolUsePathCandidates(payload: CodexHookPayload): string[] {
2401
+ const input = safeObject(payload.tool_input);
2402
+ const candidates = [
2403
+ input.file_path,
2404
+ input.filePath,
2405
+ input.path,
2406
+ input.target_path,
2407
+ input.targetPath,
2408
+ ];
2409
+ return candidates.map((candidate) => safeString(candidate).trim()).filter(Boolean);
2410
+ }
2411
+
2412
+ function commandHasDeepInterviewWriteIntent(command: string): boolean {
2413
+ return /\bapply_patch\b/.test(command)
2414
+ || /(?:^|[;&|]\s*)(?:cat|printf|echo)\b[\s\S]{0,240}>\s*[^\s&|;]+/.test(command)
2415
+ || /\btee\s+(?:-a\s+)?[^\s&|;]+/.test(command)
2416
+ || /\bsed\s+(?:[^\n;&|]*\s)?-i(?:\b|['"])/.test(command)
2417
+ || /\b(?:python3?|node|perl|ruby)\b[\s\S]{0,260}\b(?:writeFileSync|writeFile|write_text|open\([^)]*["']w|File\.write|Path\()/.test(command)
2418
+ || /\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);
2419
+ }
2420
+
2421
+ function extractDeepInterviewCommandWriteTargets(command: string): string[] {
2422
+ const targets: string[] = [];
2423
+ for (const match of command.matchAll(/(?:^|[^>])>{1,2}\s*(["']?)([^\s&|;<>]+)\1/g)) {
2424
+ const candidate = safeString(match[2]).trim();
2425
+ if (candidate) targets.push(candidate);
2426
+ }
2427
+ for (const match of command.matchAll(/\btee\s+(?:-a\s+)?(["']?)([^\s&|;<>]+)\1/g)) {
2428
+ const candidate = safeString(match[2]).trim();
2429
+ if (candidate) targets.push(candidate);
2430
+ }
2431
+ return targets;
2432
+ }
2433
+
2434
+ function isAllowedDeepInterviewBashWrite(cwd: string, command: string): boolean {
2435
+ if (!commandHasDeepInterviewWriteIntent(command)) return true;
2436
+ if (/\bomx\s+(?:state\s+(?:write|read|clear)|question)\b/.test(command)) return true;
2437
+ const targets = extractDeepInterviewCommandWriteTargets(command);
2438
+ return targets.length > 0 && targets.every((target) => isAllowedDeepInterviewArtifactPath(cwd, target));
2439
+ }
2440
+
2441
+ async function readActiveDeepInterviewStateForPreToolUse(
2442
+ cwd: string,
2443
+ stateDir: string,
2444
+ sessionId: string,
2445
+ threadId: string,
2446
+ ): Promise<Record<string, unknown> | null> {
2447
+ const modeState = sessionId
2448
+ ? await readStopSessionPinnedState("deep-interview-state.json", cwd, sessionId, stateDir)
2449
+ : await readJsonIfExists(join(stateDir, "deep-interview-state.json"));
2450
+ if (!isActiveDeepInterviewPhase(modeState) || !modeState) return null;
2451
+ if (!modeStateMatchesSkillStopContext(modeState, cwd, sessionId)) return null;
2452
+
2453
+ const canonicalState = sessionId
2454
+ ? await readVisibleSkillActiveStateForStateDir(stateDir, sessionId)
2455
+ : await readSkillActiveState(join(stateDir, SKILL_ACTIVE_STATE_FILE));
2456
+ if (!canonicalState) return modeState;
2457
+ const hasActiveDeepInterviewSkill = listActiveSkills(canonicalState).some((entry) => (
2458
+ entry.skill === "deep-interview"
2459
+ && matchesSkillStopContext(entry, canonicalState, sessionId, threadId)
2460
+ ));
2461
+ return hasActiveDeepInterviewSkill ? modeState : null;
2462
+ }
2463
+
2464
+ async function buildDeepInterviewPreToolUseBoundaryOutput(
2465
+ payload: CodexHookPayload,
2466
+ cwd: string,
2467
+ stateDir: string,
2468
+ ): Promise<Record<string, unknown> | null> {
2469
+ const sessionId = readPayloadSessionId(payload);
2470
+ const threadId = readPayloadThreadId(payload);
2471
+ const activeState = await readActiveDeepInterviewStateForPreToolUse(cwd, stateDir, sessionId, threadId);
2472
+ if (!activeState) return null;
2473
+
2474
+ const toolName = safeString(payload.tool_name).trim();
2475
+ const command = readPreToolUseCommand(payload);
2476
+ const pathCandidates = readPreToolUsePathCandidates(payload);
2477
+ let blocked = false;
2478
+
2479
+ if (toolName === "Bash") {
2480
+ blocked = !isAllowedDeepInterviewBashWrite(cwd, command);
2481
+ } else if (DEEP_INTERVIEW_IMPLEMENTATION_TOOL_NAMES.has(toolName)) {
2482
+ blocked = pathCandidates.length === 0
2483
+ || !pathCandidates.every((candidate) => isAllowedDeepInterviewArtifactPath(cwd, candidate));
2484
+ }
2485
+
2486
+ if (!blocked) return null;
2487
+
2488
+ const phase = formatPhase(activeState.current_phase ?? activeState.currentPhase, "planning");
2489
+ return {
2490
+ decision: "block",
2491
+ reason: `Deep-interview is active (phase: ${phase}); implementation/write tools are blocked until an explicit handoff workflow is activated.`,
2492
+ hookSpecificOutput: {
2493
+ hookEventName: "PreToolUse",
2494
+ additionalContext:
2495
+ "Deep-interview is requirements/spec mode. Treat detailed user answers as interview/spec material, not implicit implementation authorization. You may write only deep-interview artifacts under `.omx/context/`, `.omx/interviews/`, `.omx/specs/`, or required `.omx/state/` files. To implement, first ask for or process an explicit transition such as `$ralplan`, `$autopilot`, `$ralph`, `$team`, or `$ultragoal`.",
2496
+ },
2497
+ };
2498
+ }
2499
+
2275
2500
  function matchesSkillStopContext(
2276
2501
  entry: { session_id?: string; thread_id?: string },
2277
2502
  state: { session_id?: string; thread_id?: string },
@@ -2909,6 +3134,7 @@ async function returnPersistentStopBlock(
2909
3134
  async function findCanonicalActiveTeamForSession(
2910
3135
  cwd: string,
2911
3136
  sessionId: string,
3137
+ threadId?: string,
2912
3138
  ): Promise<{ teamName: string; phase: string } | null> {
2913
3139
  if (!sessionId.trim()) return null;
2914
3140
  const teamsRoot = join(resolveCanonicalTeamStateRoot(cwd), "team");
@@ -2927,6 +3153,7 @@ async function findCanonicalActiveTeamForSession(
2927
3153
  if (!manifest || !phaseState) continue;
2928
3154
  const ownerSessionId = (manifest.leader?.session_id ?? "").trim();
2929
3155
  if (ownerSessionId && ownerSessionId !== sessionId.trim()) continue;
3156
+ if (!teamStateMatchesThreadForStop(manifest.leader as unknown as Record<string, unknown>, threadId)) continue;
2930
3157
  if (!isNonTerminalPhase(phaseState.current_phase)) continue;
2931
3158
 
2932
3159
  return {
@@ -2942,12 +3169,13 @@ async function resolveActiveTeamNameForStop(
2942
3169
  cwd: string,
2943
3170
  stateDir: string,
2944
3171
  sessionId: string,
3172
+ threadId?: string,
2945
3173
  ): Promise<string> {
2946
- const directState = await readTeamModeStateForStop(cwd, stateDir, sessionId);
2947
- const directTeamName = safeString(directState?.team_name).trim();
2948
- if (directState?.active === true && directTeamName) return directTeamName;
3174
+ const directState = await readTeamModeStateForStop(cwd, stateDir, sessionId, threadId);
3175
+ const directTeamName = safeString(directState?.state.team_name).trim();
3176
+ if (directState?.state.active === true && directTeamName) return directTeamName;
2949
3177
 
2950
- const canonicalTeam = await findCanonicalActiveTeamForSession(cwd, sessionId);
3178
+ const canonicalTeam = await findCanonicalActiveTeamForSession(cwd, sessionId, threadId);
2951
3179
  return canonicalTeam?.teamName ?? "";
2952
3180
  }
2953
3181
 
@@ -2959,7 +3187,7 @@ async function maybeBuildReleaseReadinessFinalizeStopOutput(
2959
3187
  ): Promise<{ matched: boolean; output: Record<string, unknown> | null }> {
2960
3188
  if (!sessionId) return { matched: false, output: null };
2961
3189
 
2962
- const teamName = await resolveActiveTeamNameForStop(cwd, stateDir, sessionId);
3190
+ const teamName = await resolveActiveTeamNameForStop(cwd, stateDir, sessionId, readPayloadThreadId(payload));
2963
3191
  if (!teamName) return { matched: false, output: null };
2964
3192
 
2965
3193
  const explicitReleaseReadinessContext =
@@ -3288,7 +3516,7 @@ async function buildStopHookOutput(
3288
3516
  );
3289
3517
  if (releaseReadinessFinalizeResult.matched) return releaseReadinessFinalizeResult.output;
3290
3518
 
3291
- const teamOutput = await buildTeamStopOutput(cwd, canonicalSessionId);
3519
+ const teamOutput = await buildTeamStopOutput(cwd, canonicalSessionId, threadId);
3292
3520
  if (teamOutput) {
3293
3521
  return await returnPersistentStopBlock(
3294
3522
  payload,
@@ -3320,7 +3548,7 @@ async function buildStopHookOutput(
3320
3548
 
3321
3549
  const canonicalTeam = await readCanonicalTerminalRunStateForStop(cwd, canonicalSessionId, "team")
3322
3550
  ? null
3323
- : await findCanonicalActiveTeamForSession(cwd, canonicalSessionId);
3551
+ : await findCanonicalActiveTeamForSession(cwd, canonicalSessionId, threadId);
3324
3552
  if (canonicalTeam) {
3325
3553
  const canonicalTeamOutput = buildTeamStopOutputForPhase(
3326
3554
  canonicalTeam.teamName,
@@ -3710,7 +3938,8 @@ export async function dispatchCodexNativeHook(
3710
3938
  };
3711
3939
  }
3712
3940
  } else if (hookEventName === "PreToolUse") {
3713
- outputJson = buildNativePreToolUseOutput(payload);
3941
+ outputJson = await buildDeepInterviewPreToolUseBoundaryOutput(payload, cwd, stateDir)
3942
+ ?? buildNativePreToolUseOutput(payload);
3714
3943
  } else if (hookEventName === "PostToolUse") {
3715
3944
  if (detectMcpTransportFailure(payload)) {
3716
3945
  await markTeamTransportFailure(cwd, payload);