oh-my-codex 0.20.1 → 0.20.2

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 (654) hide show
  1. package/Cargo.lock +6 -6
  2. package/Cargo.toml +1 -1
  3. package/README.md +10 -1
  4. package/dist/adapt/__tests__/hermes.test.js.map +1 -1
  5. package/dist/adapt/contracts.d.ts.map +1 -1
  6. package/dist/agents/native-config.js.map +1 -1
  7. package/dist/auth/__tests__/hotswap.test.d.ts +2 -0
  8. package/dist/auth/__tests__/hotswap.test.d.ts.map +1 -0
  9. package/dist/auth/__tests__/hotswap.test.js +183 -0
  10. package/dist/auth/__tests__/hotswap.test.js.map +1 -0
  11. package/dist/auth/hotswap.d.ts.map +1 -1
  12. package/dist/auth/hotswap.js +17 -4
  13. package/dist/auth/hotswap.js.map +1 -1
  14. package/dist/auth/paths.js.map +1 -1
  15. package/dist/auth/storage.js.map +1 -1
  16. package/dist/autopilot/__tests__/ralplan-gate.test.js +4 -4
  17. package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -1
  18. package/dist/autopilot/completion-gate.js.map +1 -1
  19. package/dist/autopilot/fsm.d.ts +1 -1
  20. package/dist/autopilot/fsm.d.ts.map +1 -1
  21. package/dist/autoresearch/goal.js.map +1 -1
  22. package/dist/autoresearch/runtime.js.map +1 -1
  23. package/dist/capabilities/lockfile.js.map +1 -1
  24. package/dist/catalog/reader.js.map +1 -1
  25. package/dist/catalog/skill-mirror.js.map +1 -1
  26. package/dist/cli/__tests__/adapt-help.test.js.map +1 -1
  27. package/dist/cli/__tests__/agents-init.test.js.map +1 -1
  28. package/dist/cli/__tests__/agents.test.js.map +1 -1
  29. package/dist/cli/__tests__/api.test.js.map +1 -1
  30. package/dist/cli/__tests__/ask.test.js.map +1 -1
  31. package/dist/cli/__tests__/auth.test.js.map +1 -1
  32. package/dist/cli/__tests__/autoresearch-guided.test.js.map +1 -1
  33. package/dist/cli/__tests__/autoresearch.test.js.map +1 -1
  34. package/dist/cli/__tests__/capabilities.test.js.map +1 -1
  35. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
  36. package/dist/cli/__tests__/doctor-context-window-warning.test.js.map +1 -1
  37. package/dist/cli/__tests__/doctor-invalid-config.test.js +47 -4
  38. package/dist/cli/__tests__/doctor-invalid-config.test.js.map +1 -1
  39. package/dist/cli/__tests__/doctor-team.test.js.map +1 -1
  40. package/dist/cli/__tests__/doctor-warning-copy.test.js +650 -15
  41. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  42. package/dist/cli/__tests__/exec.test.js.map +1 -1
  43. package/dist/cli/__tests__/imagegen-continuation.test.js.map +1 -1
  44. package/dist/cli/__tests__/index.test.js +216 -11
  45. package/dist/cli/__tests__/index.test.js.map +1 -1
  46. package/dist/cli/__tests__/launch-fallback.test.js +49 -3
  47. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  48. package/dist/cli/__tests__/mcp-parity.test.js +84 -12
  49. package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
  50. package/dist/cli/__tests__/native-hook-claim-journal.test.d.ts +2 -0
  51. package/dist/cli/__tests__/native-hook-claim-journal.test.d.ts.map +1 -0
  52. package/dist/cli/__tests__/native-hook-claim-journal.test.js +128 -0
  53. package/dist/cli/__tests__/native-hook-claim-journal.test.js.map +1 -0
  54. package/dist/cli/__tests__/nested-help-routing.test.js.map +1 -1
  55. package/dist/cli/__tests__/question.test.js.map +1 -1
  56. package/dist/cli/__tests__/ralph-deslop-contract.test.js.map +1 -1
  57. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +6 -0
  58. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
  59. package/dist/cli/__tests__/ralph-prd-deep-interview.test.js.map +1 -1
  60. package/dist/cli/__tests__/ralph-prd-smoke.test.js.map +1 -1
  61. package/dist/cli/__tests__/ralph.test.js +8 -3
  62. package/dist/cli/__tests__/ralph.test.js.map +1 -1
  63. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.d.ts +2 -0
  64. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.d.ts.map +1 -0
  65. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js +187 -0
  66. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js.map +1 -0
  67. package/dist/cli/__tests__/ralplan.test.d.ts +2 -0
  68. package/dist/cli/__tests__/ralplan.test.d.ts.map +1 -0
  69. package/dist/cli/__tests__/ralplan.test.js +129 -0
  70. package/dist/cli/__tests__/ralplan.test.js.map +1 -0
  71. package/dist/cli/__tests__/resume.test.js +6 -1
  72. package/dist/cli/__tests__/resume.test.js.map +1 -1
  73. package/dist/cli/__tests__/session-scoped-runtime.test.js +25 -0
  74. package/dist/cli/__tests__/session-scoped-runtime.test.js.map +1 -1
  75. package/dist/cli/__tests__/session-search-help.test.js.map +1 -1
  76. package/dist/cli/__tests__/session-search.test.js.map +1 -1
  77. package/dist/cli/__tests__/setup-agents-overwrite.test.js +79 -0
  78. package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
  79. package/dist/cli/__tests__/setup-gh-star.test.js.map +1 -1
  80. package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js +77 -3
  81. package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js.map +1 -1
  82. package/dist/cli/__tests__/setup-hooks-trust-e2e.test.d.ts +2 -0
  83. package/dist/cli/__tests__/setup-hooks-trust-e2e.test.d.ts.map +1 -0
  84. package/dist/cli/__tests__/setup-hooks-trust-e2e.test.js +285 -0
  85. package/dist/cli/__tests__/setup-hooks-trust-e2e.test.js.map +1 -0
  86. package/dist/cli/__tests__/setup-install-mode.test.js +2261 -14
  87. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  88. package/dist/cli/__tests__/setup-preferences.test.d.ts +2 -0
  89. package/dist/cli/__tests__/setup-preferences.test.d.ts.map +1 -0
  90. package/dist/cli/__tests__/setup-preferences.test.js +79 -0
  91. package/dist/cli/__tests__/setup-preferences.test.js.map +1 -0
  92. package/dist/cli/__tests__/setup-refresh.test.js +33 -2
  93. package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
  94. package/dist/cli/__tests__/setup-scope.test.js +46 -3
  95. package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
  96. package/dist/cli/__tests__/sparkshell-cli.test.js.map +1 -1
  97. package/dist/cli/__tests__/state.test.js +37 -0
  98. package/dist/cli/__tests__/state.test.js.map +1 -1
  99. package/dist/cli/__tests__/team.test.js.map +1 -1
  100. package/dist/cli/__tests__/ultragoal.test.js +61 -1
  101. package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
  102. package/dist/cli/__tests__/uninstall.test.js +1771 -24
  103. package/dist/cli/__tests__/uninstall.test.js.map +1 -1
  104. package/dist/cli/__tests__/update.test.js +58 -0
  105. package/dist/cli/__tests__/update.test.js.map +1 -1
  106. package/dist/cli/__tests__/url.test.js.map +1 -1
  107. package/dist/cli/__tests__/windows-popup-loop-contract.test.js.map +1 -1
  108. package/dist/cli/adapt.js.map +1 -1
  109. package/dist/cli/agents-init.js.map +1 -1
  110. package/dist/cli/agents.js.map +1 -1
  111. package/dist/cli/api.js.map +1 -1
  112. package/dist/cli/ask.d.ts +1 -1
  113. package/dist/cli/ask.d.ts.map +1 -1
  114. package/dist/cli/ask.js.map +1 -1
  115. package/dist/cli/auth.js.map +1 -1
  116. package/dist/cli/autoresearch-guided.js.map +1 -1
  117. package/dist/cli/autoresearch-intake.js.map +1 -1
  118. package/dist/cli/capabilities.js.map +1 -1
  119. package/dist/cli/cleanup.js.map +1 -1
  120. package/dist/cli/codex-feature-probe.js.map +1 -1
  121. package/dist/cli/codex-home.js.map +1 -1
  122. package/dist/cli/doctor.d.ts +18 -0
  123. package/dist/cli/doctor.d.ts.map +1 -1
  124. package/dist/cli/doctor.js +594 -130
  125. package/dist/cli/doctor.js.map +1 -1
  126. package/dist/cli/explore.js.map +1 -1
  127. package/dist/cli/index.d.ts +11 -3
  128. package/dist/cli/index.d.ts.map +1 -1
  129. package/dist/cli/index.js +108 -20
  130. package/dist/cli/index.js.map +1 -1
  131. package/dist/cli/mcp-serve.js.map +1 -1
  132. package/dist/cli/mission.js.map +1 -1
  133. package/dist/cli/native-assets.js.map +1 -1
  134. package/dist/cli/native-hook-claim-journal.d.ts +18 -0
  135. package/dist/cli/native-hook-claim-journal.d.ts.map +1 -0
  136. package/dist/cli/native-hook-claim-journal.js +239 -0
  137. package/dist/cli/native-hook-claim-journal.js.map +1 -0
  138. package/dist/cli/omx.js.map +1 -1
  139. package/dist/cli/plugin-marketplace.js.map +1 -1
  140. package/dist/cli/project-runtime-codex-homes.js.map +1 -1
  141. package/dist/cli/question.js.map +1 -1
  142. package/dist/cli/ralph.d.ts.map +1 -1
  143. package/dist/cli/ralph.js +3 -2
  144. package/dist/cli/ralph.js.map +1 -1
  145. package/dist/cli/ralplan.d.ts +14 -0
  146. package/dist/cli/ralplan.d.ts.map +1 -0
  147. package/dist/cli/ralplan.js +203 -0
  148. package/dist/cli/ralplan.js.map +1 -0
  149. package/dist/cli/setup-preferences.d.ts +11 -0
  150. package/dist/cli/setup-preferences.d.ts.map +1 -1
  151. package/dist/cli/setup-preferences.js +32 -0
  152. package/dist/cli/setup-preferences.js.map +1 -1
  153. package/dist/cli/setup.d.ts +53 -0
  154. package/dist/cli/setup.d.ts.map +1 -1
  155. package/dist/cli/setup.js +1401 -398
  156. package/dist/cli/setup.js.map +1 -1
  157. package/dist/cli/sparkshell.js.map +1 -1
  158. package/dist/cli/star-prompt.js.map +1 -1
  159. package/dist/cli/state.d.ts.map +1 -1
  160. package/dist/cli/state.js +2 -1
  161. package/dist/cli/state.js.map +1 -1
  162. package/dist/cli/team.js.map +1 -1
  163. package/dist/cli/ultragoal.d.ts +5 -1
  164. package/dist/cli/ultragoal.d.ts.map +1 -1
  165. package/dist/cli/ultragoal.js +34 -2
  166. package/dist/cli/ultragoal.js.map +1 -1
  167. package/dist/cli/uninstall.d.ts +8 -0
  168. package/dist/cli/uninstall.d.ts.map +1 -1
  169. package/dist/cli/uninstall.js +944 -95
  170. package/dist/cli/uninstall.js.map +1 -1
  171. package/dist/cli/update.d.ts.map +1 -1
  172. package/dist/cli/update.js +8 -1
  173. package/dist/cli/update.js.map +1 -1
  174. package/dist/cli/url.js.map +1 -1
  175. package/dist/compat/__tests__/doctor-contract.test.js +5 -1
  176. package/dist/compat/__tests__/doctor-contract.test.js.map +1 -1
  177. package/dist/compat/__tests__/rust-runtime-compat.test.js.map +1 -1
  178. package/dist/config/__tests__/codex-hooks.test.js +1287 -11
  179. package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
  180. package/dist/config/__tests__/generator-idempotent.test.js +850 -32
  181. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  182. package/dist/config/__tests__/generator-notify.test.js +6 -1
  183. package/dist/config/__tests__/generator-notify.test.js.map +1 -1
  184. package/dist/config/codex-feature-flags.d.ts.map +1 -1
  185. package/dist/config/codex-feature-flags.js.map +1 -1
  186. package/dist/config/codex-hooks.d.ts +124 -1
  187. package/dist/config/codex-hooks.d.ts.map +1 -1
  188. package/dist/config/codex-hooks.js +2142 -265
  189. package/dist/config/codex-hooks.js.map +1 -1
  190. package/dist/config/commit-lore-guard.js.map +1 -1
  191. package/dist/config/generator.d.ts +21 -16
  192. package/dist/config/generator.d.ts.map +1 -1
  193. package/dist/config/generator.js +991 -137
  194. package/dist/config/generator.js.map +1 -1
  195. package/dist/config/mcp-registry.js.map +1 -1
  196. package/dist/config/models.d.ts +2 -2
  197. package/dist/config/models.d.ts.map +1 -1
  198. package/dist/config/models.js.map +1 -1
  199. package/dist/config/omx-first-party-mcp.js.map +1 -1
  200. package/dist/config/team-mode.d.ts.map +1 -1
  201. package/dist/document-refresh/__tests__/enforcer.test.js.map +1 -1
  202. package/dist/document-refresh/enforcer.js.map +1 -1
  203. package/dist/exec/followup.js.map +1 -1
  204. package/dist/goal-workflows/artifacts.js.map +1 -1
  205. package/dist/hooks/__tests__/agents-overlay.test.js +7 -4
  206. package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
  207. package/dist/hooks/__tests__/analyze-routing-contract.test.js.map +1 -1
  208. package/dist/hooks/__tests__/analyze-skill-contract.test.js.map +1 -1
  209. package/dist/hooks/__tests__/anti-slop-workflow.test.js.map +1 -1
  210. package/dist/hooks/__tests__/autopilot-skill-contract.test.js +9 -3
  211. package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
  212. package/dist/hooks/__tests__/code-review-skill-contract.test.js.map +1 -1
  213. package/dist/hooks/__tests__/consensus-execution-handoff.test.js.map +1 -1
  214. package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
  215. package/dist/hooks/__tests__/design-skill.test.js.map +1 -1
  216. package/dist/hooks/__tests__/keyword-detector.test.js +1372 -2
  217. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  218. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +14 -0
  219. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  220. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
  221. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +669 -1
  222. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  223. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js +2 -0
  224. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js.map +1 -1
  225. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js +142 -1
  226. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js.map +1 -1
  227. package/dist/hooks/__tests__/notify-hook-modules.test.js.map +1 -1
  228. package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.js +48 -2
  229. package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.js.map +1 -1
  230. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js +61 -44
  231. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js.map +1 -1
  232. package/dist/hooks/__tests__/notify-hook-regression-205.test.js.map +1 -1
  233. package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.js +3 -2
  234. package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.js.map +1 -1
  235. package/dist/hooks/__tests__/notify-hook-session-scope.test.js +243 -4
  236. package/dist/hooks/__tests__/notify-hook-session-scope.test.js.map +1 -1
  237. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
  238. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +8 -1
  239. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  240. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
  241. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  242. package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js.map +1 -1
  243. package/dist/hooks/__tests__/notify-hook-visual-verdict.test.js.map +1 -1
  244. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
  245. package/dist/hooks/__tests__/openclaw-setup-contract.test.js.map +1 -1
  246. package/dist/hooks/__tests__/pre-context-gate-skills.test.js.map +1 -1
  247. package/dist/hooks/__tests__/prometheus-strict-contract.test.js.map +1 -1
  248. package/dist/hooks/__tests__/prompt-guidance-fragments.test.js.map +1 -1
  249. package/dist/hooks/__tests__/prompt-guidance-test-helpers.js.map +1 -1
  250. package/dist/hooks/__tests__/prompt-session-provenance.test.d.ts +2 -0
  251. package/dist/hooks/__tests__/prompt-session-provenance.test.d.ts.map +1 -0
  252. package/dist/hooks/__tests__/prompt-session-provenance.test.js +82 -0
  253. package/dist/hooks/__tests__/prompt-session-provenance.test.js.map +1 -0
  254. package/dist/hooks/__tests__/session.test.js +532 -11
  255. package/dist/hooks/__tests__/session.test.js.map +1 -1
  256. package/dist/hooks/__tests__/skill-catalog-hygiene.test.js.map +1 -1
  257. package/dist/hooks/__tests__/team-runtime-gating-docs-contract.test.js.map +1 -1
  258. package/dist/hooks/__tests__/visual-ralph-skill.test.js.map +1 -1
  259. package/dist/hooks/__tests__/visual-verdict-loop.test.js.map +1 -1
  260. package/dist/hooks/agents-overlay.js +5 -5
  261. package/dist/hooks/agents-overlay.js.map +1 -1
  262. package/dist/hooks/code-simplifier/index.js.map +1 -1
  263. package/dist/hooks/explore-routing.js.map +1 -1
  264. package/dist/hooks/extensibility/dispatcher.js.map +1 -1
  265. package/dist/hooks/extensibility/events.js.map +1 -1
  266. package/dist/hooks/extensibility/loader.js.map +1 -1
  267. package/dist/hooks/extensibility/plugin-runner-stdin.js.map +1 -1
  268. package/dist/hooks/extensibility/sdk/logging.js.map +1 -1
  269. package/dist/hooks/keyword-detector.d.ts +33 -7
  270. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  271. package/dist/hooks/keyword-detector.js +2228 -119
  272. package/dist/hooks/keyword-detector.js.map +1 -1
  273. package/dist/hooks/keyword-registry.d.ts +12 -0
  274. package/dist/hooks/keyword-registry.d.ts.map +1 -1
  275. package/dist/hooks/keyword-registry.js +41 -0
  276. package/dist/hooks/keyword-registry.js.map +1 -1
  277. package/dist/hooks/prompt-session-provenance.d.ts +87 -0
  278. package/dist/hooks/prompt-session-provenance.d.ts.map +1 -0
  279. package/dist/hooks/prompt-session-provenance.js +180 -0
  280. package/dist/hooks/prompt-session-provenance.js.map +1 -0
  281. package/dist/hooks/session.d.ts +139 -34
  282. package/dist/hooks/session.d.ts.map +1 -1
  283. package/dist/hooks/session.js +1239 -317
  284. package/dist/hooks/session.js.map +1 -1
  285. package/dist/hooks/task-size-detector.js.map +1 -1
  286. package/dist/hud/__tests__/state.test.js.map +1 -1
  287. package/dist/hud/authority.js.map +1 -1
  288. package/dist/hud/constants.js.map +1 -1
  289. package/dist/hud/index.js.map +1 -1
  290. package/dist/hud/reconcile.js.map +1 -1
  291. package/dist/hud/render.js.map +1 -1
  292. package/dist/hud/state.js.map +1 -1
  293. package/dist/hud/tmux.js.map +1 -1
  294. package/dist/leader/__tests__/contract.test.js +103 -1
  295. package/dist/leader/__tests__/contract.test.js.map +1 -1
  296. package/dist/leader/contract.d.ts +26 -5
  297. package/dist/leader/contract.d.ts.map +1 -1
  298. package/dist/leader/contract.js +109 -3
  299. package/dist/leader/contract.js.map +1 -1
  300. package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
  301. package/dist/mcp/__tests__/server-lifecycle.test.js +15 -2
  302. package/dist/mcp/__tests__/server-lifecycle.test.js.map +1 -1
  303. package/dist/mcp/__tests__/state-paths.test.js +98 -1
  304. package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
  305. package/dist/mcp/bootstrap.js.map +1 -1
  306. package/dist/mcp/code-intel-server.d.ts +58 -58
  307. package/dist/mcp/code-intel-server.d.ts.map +1 -1
  308. package/dist/mcp/code-intel-server.js.map +1 -1
  309. package/dist/mcp/hermes-bridge.js.map +1 -1
  310. package/dist/mcp/hermes-server.d.ts +70 -70
  311. package/dist/mcp/hermes-server.d.ts.map +1 -1
  312. package/dist/mcp/lifecycle-telemetry.js.map +1 -1
  313. package/dist/mcp/memory-server.d.ts +34 -34
  314. package/dist/mcp/memory-server.d.ts.map +1 -1
  315. package/dist/mcp/memory-server.js.map +1 -1
  316. package/dist/mcp/state-paths.d.ts +14 -0
  317. package/dist/mcp/state-paths.d.ts.map +1 -1
  318. package/dist/mcp/state-paths.js +96 -20
  319. package/dist/mcp/state-paths.js.map +1 -1
  320. package/dist/mcp/state-server.d.ts +47 -47
  321. package/dist/mcp/state-server.d.ts.map +1 -1
  322. package/dist/mcp/trace-server.d.ts +4 -4
  323. package/dist/mcp/trace-server.d.ts.map +1 -1
  324. package/dist/mcp/trace-server.js.map +1 -1
  325. package/dist/mcp/wiki-server.d.ts +23 -23
  326. package/dist/mcp/wiki-server.d.ts.map +1 -1
  327. package/dist/mcp/wiki-server.js.map +1 -1
  328. package/dist/modes/__tests__/base-session-scope.test.js +53 -1
  329. package/dist/modes/__tests__/base-session-scope.test.js.map +1 -1
  330. package/dist/modes/base.d.ts.map +1 -1
  331. package/dist/modes/base.js +5 -5
  332. package/dist/modes/base.js.map +1 -1
  333. package/dist/notifications/__tests__/lifecycle-dedupe.test.js.map +1 -1
  334. package/dist/notifications/__tests__/reply-listener.test.js.map +1 -1
  335. package/dist/notifications/__tests__/session-idle-tail-dedupe.test.js +177 -85
  336. package/dist/notifications/__tests__/session-idle-tail-dedupe.test.js.map +1 -1
  337. package/dist/notifications/__tests__/session-registry.test.js.map +1 -1
  338. package/dist/notifications/__tests__/template-engine.test.js.map +1 -1
  339. package/dist/notifications/__tests__/verbosity.test.js.map +1 -1
  340. package/dist/notifications/http-client.js.map +1 -1
  341. package/dist/notifications/index.d.ts +4 -1
  342. package/dist/notifications/index.d.ts.map +1 -1
  343. package/dist/notifications/index.js +3 -9
  344. package/dist/notifications/index.js.map +1 -1
  345. package/dist/notifications/lifecycle-dedupe.js.map +1 -1
  346. package/dist/notifications/reply-listener.d.ts.map +1 -1
  347. package/dist/notifications/reply-listener.js.map +1 -1
  348. package/dist/notifications/session-registry.js.map +1 -1
  349. package/dist/notifications/session-status.js.map +1 -1
  350. package/dist/notifications/temp-contract.js.map +1 -1
  351. package/dist/notifications/tmux-detector.js.map +1 -1
  352. package/dist/notifications/tmux.js.map +1 -1
  353. package/dist/openclaw/config.js.map +1 -1
  354. package/dist/openclaw/dispatcher.js.map +1 -1
  355. package/dist/pipeline/__tests__/orchestrator.test.js.map +1 -1
  356. package/dist/pipeline/__tests__/stages.test.js +8 -5
  357. package/dist/pipeline/__tests__/stages.test.js.map +1 -1
  358. package/dist/pipeline/stages/code-review.js.map +1 -1
  359. package/dist/pipeline/stages/ralph-verify.js.map +1 -1
  360. package/dist/pipeline/stages/ralplan.js.map +1 -1
  361. package/dist/pipeline/stages/team-exec.js.map +1 -1
  362. package/dist/pipeline/stages/ultragoal.js.map +1 -1
  363. package/dist/pipeline/stages/ultraqa.js.map +1 -1
  364. package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.js.map +1 -1
  365. package/dist/planning/artifact-names.js.map +1 -1
  366. package/dist/planning/artifacts.js.map +1 -1
  367. package/dist/question/__tests__/state.test.js.map +1 -1
  368. package/dist/question/__tests__/ui.test.js.map +1 -1
  369. package/dist/question/autopilot-wait.js.map +1 -1
  370. package/dist/question/client.d.ts.map +1 -1
  371. package/dist/question/client.js.map +1 -1
  372. package/dist/question/deep-interview.js.map +1 -1
  373. package/dist/question/events.js.map +1 -1
  374. package/dist/question/renderer.js.map +1 -1
  375. package/dist/question/state.d.ts.map +1 -1
  376. package/dist/question/state.js.map +1 -1
  377. package/dist/question/ui.js.map +1 -1
  378. package/dist/ralph/contract.d.ts +1 -1
  379. package/dist/ralph/contract.d.ts.map +1 -1
  380. package/dist/ralplan/__tests__/consensus-gate.test.js +403 -8
  381. package/dist/ralplan/__tests__/consensus-gate.test.js.map +1 -1
  382. package/dist/ralplan/__tests__/runtime.test.js +112 -14
  383. package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
  384. package/dist/ralplan/consensus-gate.d.ts +3 -3
  385. package/dist/ralplan/consensus-gate.d.ts.map +1 -1
  386. package/dist/ralplan/consensus-gate.js +155 -60
  387. package/dist/ralplan/consensus-gate.js.map +1 -1
  388. package/dist/ralplan/runtime.d.ts +2 -2
  389. package/dist/ralplan/runtime.d.ts.map +1 -1
  390. package/dist/ralplan/runtime.js +1 -2
  391. package/dist/ralplan/runtime.js.map +1 -1
  392. package/dist/runtime/bridge.d.ts.map +1 -1
  393. package/dist/runtime/bridge.js.map +1 -1
  394. package/dist/runtime/process-tree.js.map +1 -1
  395. package/dist/runtime/run-loop.js.map +1 -1
  396. package/dist/runtime/run-outcome.d.ts +3 -3
  397. package/dist/runtime/run-outcome.d.ts.map +1 -1
  398. package/dist/runtime/run-outcome.js.map +1 -1
  399. package/dist/runtime/run-state.js.map +1 -1
  400. package/dist/scripts/__tests__/codex-native-hook.test.js +4121 -592
  401. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  402. package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.d.ts +2 -0
  403. package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.d.ts.map +1 -0
  404. package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.js +634 -0
  405. package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.js.map +1 -0
  406. package/dist/scripts/__tests__/generate-release-body.test.js.map +1 -1
  407. package/dist/scripts/__tests__/hook-derived-watcher.test.js.map +1 -1
  408. package/dist/scripts/__tests__/notify-dispatcher.test.js.map +1 -1
  409. package/dist/scripts/__tests__/notify-fallback-delivery.test.d.ts +2 -0
  410. package/dist/scripts/__tests__/notify-fallback-delivery.test.d.ts.map +1 -0
  411. package/dist/scripts/__tests__/notify-fallback-delivery.test.js +174 -0
  412. package/dist/scripts/__tests__/notify-fallback-delivery.test.js.map +1 -0
  413. package/dist/scripts/__tests__/notify-state-io.test.js +57 -1
  414. package/dist/scripts/__tests__/notify-state-io.test.js.map +1 -1
  415. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.d.ts +2 -0
  416. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.d.ts.map +1 -0
  417. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js +205 -0
  418. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js.map +1 -0
  419. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.d.ts +2 -0
  420. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.d.ts.map +1 -0
  421. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js +175 -0
  422. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js.map +1 -0
  423. package/dist/scripts/__tests__/run-test-files.test.js.map +1 -1
  424. package/dist/scripts/__tests__/smoke-packed-install.test.js +848 -4
  425. package/dist/scripts/__tests__/smoke-packed-install.test.js.map +1 -1
  426. package/dist/scripts/__tests__/test-reply-listener-live.test.js.map +1 -1
  427. package/dist/scripts/build-api.js.map +1 -1
  428. package/dist/scripts/build-explore-harness.js.map +1 -1
  429. package/dist/scripts/build-sparkshell.js.map +1 -1
  430. package/dist/scripts/cleanup-explore-harness.js.map +1 -1
  431. package/dist/scripts/codex-execution-surface.js.map +1 -1
  432. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  433. package/dist/scripts/codex-native-hook.js +689 -156
  434. package/dist/scripts/codex-native-hook.js.map +1 -1
  435. package/dist/scripts/generate-native-release-manifest.js.map +1 -1
  436. package/dist/scripts/generate-release-body.js.map +1 -1
  437. package/dist/scripts/hook-payload-guard.d.ts.map +1 -1
  438. package/dist/scripts/hook-payload-guard.js.map +1 -1
  439. package/dist/scripts/notify-fallback-delivery.d.ts +48 -0
  440. package/dist/scripts/notify-fallback-delivery.d.ts.map +1 -0
  441. package/dist/scripts/notify-fallback-delivery.js +419 -0
  442. package/dist/scripts/notify-fallback-delivery.js.map +1 -0
  443. package/dist/scripts/notify-fallback-watcher.js +136 -30
  444. package/dist/scripts/notify-fallback-watcher.js.map +1 -1
  445. package/dist/scripts/notify-hook/auto-nudge.d.ts +18 -9
  446. package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
  447. package/dist/scripts/notify-hook/auto-nudge.js +39 -13
  448. package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
  449. package/dist/scripts/notify-hook/managed-tmux.d.ts +23 -0
  450. package/dist/scripts/notify-hook/managed-tmux.d.ts.map +1 -1
  451. package/dist/scripts/notify-hook/managed-tmux.js +122 -12
  452. package/dist/scripts/notify-hook/managed-tmux.js.map +1 -1
  453. package/dist/scripts/notify-hook/orchestration-intent.d.ts +7 -7
  454. package/dist/scripts/notify-hook/orchestration-intent.d.ts.map +1 -1
  455. package/dist/scripts/notify-hook/ralph-session-resume.d.ts +5 -2
  456. package/dist/scripts/notify-hook/ralph-session-resume.d.ts.map +1 -1
  457. package/dist/scripts/notify-hook/ralph-session-resume.js +33 -37
  458. package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -1
  459. package/dist/scripts/notify-hook/state-io.d.ts +17 -0
  460. package/dist/scripts/notify-hook/state-io.d.ts.map +1 -1
  461. package/dist/scripts/notify-hook/state-io.js +73 -11
  462. package/dist/scripts/notify-hook/state-io.js.map +1 -1
  463. package/dist/scripts/notify-hook/team-dispatch.d.ts +1 -1
  464. package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
  465. package/dist/scripts/notify-hook/team-leader-nudge.d.ts +2 -2
  466. package/dist/scripts/notify-hook/team-tmux-guard.d.ts +4 -4
  467. package/dist/scripts/notify-hook/team-worker-posttooluse.js.map +1 -1
  468. package/dist/scripts/notify-hook/team-worker-stop.d.ts +1 -1
  469. package/dist/scripts/notify-hook/team-worker.d.ts +2 -2
  470. package/dist/scripts/notify-hook/tmux-injection.d.ts +8 -1
  471. package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
  472. package/dist/scripts/notify-hook/tmux-injection.js +25 -16
  473. package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
  474. package/dist/scripts/notify-hook/visual-verdict.d.ts +1 -1
  475. package/dist/scripts/notify-hook/visual-verdict.d.ts.map +1 -1
  476. package/dist/scripts/notify-hook/visual-verdict.js +15 -13
  477. package/dist/scripts/notify-hook/visual-verdict.js.map +1 -1
  478. package/dist/scripts/notify-hook.d.ts +24 -1
  479. package/dist/scripts/notify-hook.d.ts.map +1 -1
  480. package/dist/scripts/notify-hook.js +266 -90
  481. package/dist/scripts/notify-hook.js.map +1 -1
  482. package/dist/scripts/postinstall.js.map +1 -1
  483. package/dist/scripts/prompt-inventory.js.map +1 -1
  484. package/dist/scripts/smoke-packed-install.d.ts +879 -2
  485. package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
  486. package/dist/scripts/smoke-packed-install.js +1528 -7
  487. package/dist/scripts/smoke-packed-install.js.map +1 -1
  488. package/dist/scripts/sync-plugin-mirror.js.map +1 -1
  489. package/dist/scripts/test-reply-listener-live.js.map +1 -1
  490. package/dist/scripts/test-sparkshell.js.map +1 -1
  491. package/dist/scripts/verify-native-agents.js.map +1 -1
  492. package/dist/session-history/friction.js.map +1 -1
  493. package/dist/session-history/search.js.map +1 -1
  494. package/dist/sidecar/__tests__/boundary.test.js.map +1 -1
  495. package/dist/sidecar/collector.js.map +1 -1
  496. package/dist/sidecar/index.js.map +1 -1
  497. package/dist/sidecar/render.js.map +1 -1
  498. package/dist/sidecar/tmux.js.map +1 -1
  499. package/dist/state/__tests__/operations.test.js +64 -15
  500. package/dist/state/__tests__/operations.test.js.map +1 -1
  501. package/dist/state/__tests__/skill-active.test.js +19 -1
  502. package/dist/state/__tests__/skill-active.test.js.map +1 -1
  503. package/dist/state/__tests__/workflow-transition.test.js +61 -0
  504. package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
  505. package/dist/state/mode-state-context.js.map +1 -1
  506. package/dist/state/operations.d.ts +1 -1
  507. package/dist/state/operations.d.ts.map +1 -1
  508. package/dist/state/operations.js +49 -55
  509. package/dist/state/operations.js.map +1 -1
  510. package/dist/state/paths.d.ts +1 -1
  511. package/dist/state/paths.d.ts.map +1 -1
  512. package/dist/state/paths.js +1 -1
  513. package/dist/state/paths.js.map +1 -1
  514. package/dist/state/skill-active.d.ts +13 -1
  515. package/dist/state/skill-active.d.ts.map +1 -1
  516. package/dist/state/skill-active.js +43 -4
  517. package/dist/state/skill-active.js.map +1 -1
  518. package/dist/state/terminal-normalization.js.map +1 -1
  519. package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
  520. package/dist/state/workflow-transition-reconcile.js +20 -4
  521. package/dist/state/workflow-transition-reconcile.js.map +1 -1
  522. package/dist/state/workflow-transition.d.ts +1 -1
  523. package/dist/state/workflow-transition.d.ts.map +1 -1
  524. package/dist/state/workflow-transition.js.map +1 -1
  525. package/dist/subagents/__tests__/adapted-role-binding.test.d.ts +2 -0
  526. package/dist/subagents/__tests__/adapted-role-binding.test.d.ts.map +1 -0
  527. package/dist/subagents/__tests__/adapted-role-binding.test.js +988 -0
  528. package/dist/subagents/__tests__/adapted-role-binding.test.js.map +1 -0
  529. package/dist/subagents/__tests__/leader-bootstrap-3181.test.d.ts +2 -0
  530. package/dist/subagents/__tests__/leader-bootstrap-3181.test.d.ts.map +1 -0
  531. package/dist/subagents/__tests__/leader-bootstrap-3181.test.js +273 -0
  532. package/dist/subagents/__tests__/leader-bootstrap-3181.test.js.map +1 -0
  533. package/dist/subagents/__tests__/tracker.test.js +1550 -10
  534. package/dist/subagents/__tests__/tracker.test.js.map +1 -1
  535. package/dist/subagents/adapted-role-binding.d.ts +17 -0
  536. package/dist/subagents/adapted-role-binding.d.ts.map +1 -0
  537. package/dist/subagents/adapted-role-binding.js +75 -0
  538. package/dist/subagents/adapted-role-binding.js.map +1 -0
  539. package/dist/subagents/role-routing-marker.d.ts +10 -0
  540. package/dist/subagents/role-routing-marker.d.ts.map +1 -0
  541. package/dist/subagents/role-routing-marker.js +136 -0
  542. package/dist/subagents/role-routing-marker.js.map +1 -0
  543. package/dist/subagents/tracker.d.ts +172 -0
  544. package/dist/subagents/tracker.d.ts.map +1 -1
  545. package/dist/subagents/tracker.js +1077 -61
  546. package/dist/subagents/tracker.js.map +1 -1
  547. package/dist/team/__tests__/approved-execution.test.js.map +1 -1
  548. package/dist/team/__tests__/delivery-e2e-smoke.test.js.map +1 -1
  549. package/dist/team/__tests__/model-contract.test.js +231 -1
  550. package/dist/team/__tests__/model-contract.test.js.map +1 -1
  551. package/dist/team/__tests__/role-router.test.js.map +1 -1
  552. package/dist/team/__tests__/runtime.test.js +401 -1
  553. package/dist/team/__tests__/runtime.test.js.map +1 -1
  554. package/dist/team/__tests__/scaling.test.js +181 -0
  555. package/dist/team/__tests__/scaling.test.js.map +1 -1
  556. package/dist/team/__tests__/state-root.test.js.map +1 -1
  557. package/dist/team/__tests__/tmux-session.test.js +434 -4
  558. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  559. package/dist/team/__tests__/tmux-test-fixture.js.map +1 -1
  560. package/dist/team/api-interop.d.ts +2 -2
  561. package/dist/team/api-interop.d.ts.map +1 -1
  562. package/dist/team/api-interop.js.map +1 -1
  563. package/dist/team/commit-hygiene.js.map +1 -1
  564. package/dist/team/contracts.d.ts +5 -5
  565. package/dist/team/contracts.d.ts.map +1 -1
  566. package/dist/team/delivery-log.js.map +1 -1
  567. package/dist/team/followup-planner.js.map +1 -1
  568. package/dist/team/goal-workflow.js.map +1 -1
  569. package/dist/team/idle-nudge.d.ts.map +1 -1
  570. package/dist/team/model-contract.d.ts +20 -1
  571. package/dist/team/model-contract.d.ts.map +1 -1
  572. package/dist/team/model-contract.js +267 -30
  573. package/dist/team/model-contract.js.map +1 -1
  574. package/dist/team/orchestrator.js.map +1 -1
  575. package/dist/team/pane-status.js.map +1 -1
  576. package/dist/team/phase-controller.js.map +1 -1
  577. package/dist/team/reminder-intents.d.ts +1 -1
  578. package/dist/team/reminder-intents.d.ts.map +1 -1
  579. package/dist/team/reminder-intents.js.map +1 -1
  580. package/dist/team/runtime-cli.js.map +1 -1
  581. package/dist/team/runtime.d.ts.map +1 -1
  582. package/dist/team/runtime.js +83 -52
  583. package/dist/team/runtime.js.map +1 -1
  584. package/dist/team/scaling.d.ts +9 -7
  585. package/dist/team/scaling.d.ts.map +1 -1
  586. package/dist/team/scaling.js +69 -28
  587. package/dist/team/scaling.js.map +1 -1
  588. package/dist/team/state/dispatch-lock.js.map +1 -1
  589. package/dist/team/state/dispatch.js.map +1 -1
  590. package/dist/team/state/events.js.map +1 -1
  591. package/dist/team/state/tasks.js.map +1 -1
  592. package/dist/team/state-root.js.map +1 -1
  593. package/dist/team/state.js.map +1 -1
  594. package/dist/team/team-identity.d.ts.map +1 -1
  595. package/dist/team/team-identity.js.map +1 -1
  596. package/dist/team/tmux-session.d.ts +2 -0
  597. package/dist/team/tmux-session.d.ts.map +1 -1
  598. package/dist/team/tmux-session.js +90 -42
  599. package/dist/team/tmux-session.js.map +1 -1
  600. package/dist/team/worker-bootstrap.js.map +1 -1
  601. package/dist/team/worktree.js.map +1 -1
  602. package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
  603. package/dist/ultragoal/__tests__/docs-contract.test.js.map +1 -1
  604. package/dist/ultragoal/artifacts.d.ts.map +1 -1
  605. package/dist/ultragoal/artifacts.js +4 -1
  606. package/dist/ultragoal/artifacts.js.map +1 -1
  607. package/dist/url-reader/index.js.map +1 -1
  608. package/dist/utils/agents-model-table.js.map +1 -1
  609. package/dist/utils/package.js.map +1 -1
  610. package/dist/utils/paths.js.map +1 -1
  611. package/dist/utils/platform-command.js.map +1 -1
  612. package/dist/utils/repo-deps.js.map +1 -1
  613. package/dist/utils/version.js.map +1 -1
  614. package/dist/verification/__tests__/ci-rust-gates.test.js +1 -1
  615. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js.map +1 -1
  616. package/dist/verification/verifier.js.map +1 -1
  617. package/dist/visual/constants.d.ts +1 -1
  618. package/dist/visual/constants.d.ts.map +1 -1
  619. package/dist/vscode/__tests__/index.test.js.map +1 -1
  620. package/dist/vscode/index.js.map +1 -1
  621. package/dist/wiki/__tests__/cjk-tokenize.test.js.map +1 -1
  622. package/dist/wiki/__tests__/lint.test.js.map +1 -1
  623. package/dist/wiki/__tests__/query.test.js.map +1 -1
  624. package/dist/wiki/__tests__/storage.test.js.map +1 -1
  625. package/dist/wiki/__tests__/test-helpers.d.ts.map +1 -1
  626. package/dist/wiki/lint.js.map +1 -1
  627. package/dist/wiki/query.js.map +1 -1
  628. package/dist/wiki/storage.js.map +1 -1
  629. package/package.json +2 -2
  630. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  631. package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +10 -5
  632. package/plugins/oh-my-codex/skills/ralph/SKILL.md +4 -2
  633. package/plugins/oh-my-codex/skills/ralplan/SKILL.md +5 -2
  634. package/skills/omx-setup/SKILL.md +10 -5
  635. package/skills/ralph/SKILL.md +4 -2
  636. package/skills/ralplan/SKILL.md +5 -2
  637. package/src/scripts/__tests__/codex-native-hook.test.ts +17522 -10630
  638. package/src/scripts/__tests__/fixtures/issue-3138-installed-smoke.ts +734 -0
  639. package/src/scripts/__tests__/notify-fallback-delivery.test.ts +177 -0
  640. package/src/scripts/__tests__/notify-state-io.test.ts +73 -0
  641. package/src/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.ts +215 -0
  642. package/src/scripts/__tests__/role-intent-durable-recovery-3181.test.ts +193 -0
  643. package/src/scripts/__tests__/smoke-packed-install.test.ts +991 -4
  644. package/src/scripts/codex-native-hook.ts +779 -159
  645. package/src/scripts/notify-fallback-delivery.ts +416 -0
  646. package/src/scripts/notify-fallback-watcher.ts +132 -40
  647. package/src/scripts/notify-hook/auto-nudge.ts +43 -12
  648. package/src/scripts/notify-hook/managed-tmux.ts +149 -12
  649. package/src/scripts/notify-hook/ralph-session-resume.ts +37 -36
  650. package/src/scripts/notify-hook/state-io.ts +100 -13
  651. package/src/scripts/notify-hook/tmux-injection.ts +38 -21
  652. package/src/scripts/notify-hook/visual-verdict.ts +22 -12
  653. package/src/scripts/notify-hook.ts +335 -90
  654. package/src/scripts/smoke-packed-install.ts +1829 -6
@@ -0,0 +1,988 @@
1
+ import assert from 'node:assert/strict';
2
+ import { createHash } from 'node:crypto';
3
+ import { existsSync } from 'node:fs';
4
+ import { mkdir, mkdtemp, rm, symlink, writeFile } from 'node:fs/promises';
5
+ import { tmpdir } from 'node:os';
6
+ import { join } from 'node:path';
7
+ import { describe, it } from 'node:test';
8
+ import { getBaseStateDir } from '../../state/paths.js';
9
+ import { canonicalizeOriginCwd } from '../../leader/contract.js';
10
+ import { bindAndPublishAdaptedRole, recoverAdaptedRoleBindings, } from '../adapted-role-binding.js';
11
+ import { NATIVE_SUBAGENT_ROLE_ROUTING_MARKER_FILE, readRoleRoutingMarker, writeRoleRoutingMarker } from '../role-routing-marker.js';
12
+ import { bindPendingRoleIntentUnderLock, completeAdaptedRoleBinding, consumePendingRoleIntent, listBoundAdaptedRoleIntents, OMX_ADAPTED_PROVENANCE, recordPendingRoleIntent, recordSubagentTurn, readSubagentTrackingState, subagentTrackingPath, } from '../tracker.js';
13
+ const NOW_MS = Date.now();
14
+ const credentialDigest = (value) => createHash('sha256').update(value).digest('hex');
15
+ const canonicalCorrelationToken = (value) => credentialDigest(value).slice(0, 32);
16
+ const canonicalClaimantToken = (value) => {
17
+ const digest = credentialDigest(value);
18
+ return `${digest.slice(0, 8)}-${digest.slice(8, 12)}-4${digest.slice(13, 16)}-8${digest.slice(17, 20)}-${digest.slice(20, 32)}`;
19
+ };
20
+ function bindAdaptedTurn(sessionId, threadId) {
21
+ return (state, intent) => recordSubagentTurn(state, {
22
+ sessionId,
23
+ threadId,
24
+ kind: 'subagent',
25
+ role: intent.role,
26
+ provenanceKind: intent.provenanceKind,
27
+ timestamp: new Date(NOW_MS).toISOString(),
28
+ });
29
+ }
30
+ function recordIntent(cwd, sessionId, parentThreadId, correlationToken) {
31
+ assert.equal(recordPendingRoleIntent(cwd, {
32
+ role: 'architect',
33
+ sessionId,
34
+ parentThreadId,
35
+ correlationToken: /^[0-9a-f]{32}$/.test(correlationToken) ? correlationToken : canonicalCorrelationToken(correlationToken),
36
+ nowMs: NOW_MS,
37
+ }).ok, true);
38
+ }
39
+ describe('adapted role binding', () => {
40
+ it('commits adapted tracker evidence, publishes a marker, and completes the retained intent', async () => {
41
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-adapted-binding-'));
42
+ const stateDir = getBaseStateDir(cwd);
43
+ try {
44
+ recordIntent(cwd, 'session-happy', 'parent-happy', 'tokenhappy');
45
+ assert.deepEqual(bindAndPublishAdaptedRole(cwd, stateDir, {
46
+ correlationSessionId: 'session-happy',
47
+ parentThreadId: 'parent-happy',
48
+ correlationToken: canonicalCorrelationToken('tokenhappy'),
49
+ nowMs: NOW_MS,
50
+ }, bindAdaptedTurn('session-happy', 'child-happy')), { role: 'architect' });
51
+ const state = await readSubagentTrackingState(cwd);
52
+ assert.equal(state.sessions['session-happy']?.threads['child-happy']?.role, 'architect');
53
+ assert.equal(state.sessions['session-happy']?.threads['child-happy']?.provenance_kind, OMX_ADAPTED_PROVENANCE);
54
+ assert.deepEqual(state.pending_role_intents, []);
55
+ assert.equal(readRoleRoutingMarker(stateDir, {
56
+ cwd,
57
+ sessionId: 'session-happy',
58
+ parentThreadId: 'parent-happy',
59
+ nowMs: NOW_MS,
60
+ })?.evidence, 'validated OMX adapted role intent correlated to an untyped native child');
61
+ }
62
+ finally {
63
+ await rm(cwd, { recursive: true, force: true });
64
+ }
65
+ });
66
+ it('does not create adapted authority or a marker before a matching bind begins', async () => {
67
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-adapted-binding-'));
68
+ const stateDir = getBaseStateDir(cwd);
69
+ try {
70
+ recordIntent(cwd, 'session-before', 'parent-before', 'tokenbefore');
71
+ assert.equal(bindAndPublishAdaptedRole(cwd, stateDir, {
72
+ correlationSessionId: 'session-before',
73
+ parentThreadId: 'parent-before',
74
+ correlationToken: canonicalCorrelationToken('wrong-token'),
75
+ nowMs: NOW_MS,
76
+ }, bindAdaptedTurn('session-before', 'child-before')), null);
77
+ assert.equal((await readSubagentTrackingState(cwd)).sessions['session-before'], undefined);
78
+ assert.deepEqual(listBoundAdaptedRoleIntents(cwd), []);
79
+ assert.equal(existsSync(join(stateDir, NATIVE_SUBAGENT_ROLE_ROUTING_MARKER_FILE)), false);
80
+ }
81
+ finally {
82
+ await rm(cwd, { recursive: true, force: true });
83
+ }
84
+ });
85
+ it('recovers a crash after tracker commit and before marker publication', async () => {
86
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-adapted-binding-'));
87
+ const stateDir = getBaseStateDir(cwd);
88
+ try {
89
+ recordIntent(cwd, 'session-after-tracker', 'parent-after-tracker', 'tokenaftertracker');
90
+ const binding = bindPendingRoleIntentUnderLock(cwd, {
91
+ sessionId: 'session-after-tracker',
92
+ parentThreadId: 'parent-after-tracker',
93
+ correlationToken: canonicalCorrelationToken('tokenaftertracker'),
94
+ nowMs: NOW_MS,
95
+ }, bindAdaptedTurn('session-after-tracker', 'child-after-tracker'));
96
+ assert.equal(binding?.alreadyBound, false);
97
+ assert.equal(listBoundAdaptedRoleIntents(cwd).length, 1);
98
+ assert.equal(existsSync(join(stateDir, NATIVE_SUBAGENT_ROLE_ROUTING_MARKER_FILE)), false);
99
+ recoverAdaptedRoleBindings(cwd, stateDir, NOW_MS);
100
+ assert.deepEqual(listBoundAdaptedRoleIntents(cwd), []);
101
+ assert.equal(readRoleRoutingMarker(stateDir, {
102
+ cwd,
103
+ sessionId: 'session-after-tracker',
104
+ parentThreadId: 'parent-after-tracker',
105
+ nowMs: NOW_MS,
106
+ })?.session_id, 'session-after-tracker');
107
+ }
108
+ finally {
109
+ await rm(cwd, { recursive: true, force: true });
110
+ }
111
+ });
112
+ it('idempotently recovers a crash after marker publication and before completion', async () => {
113
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-adapted-binding-'));
114
+ const stateDir = getBaseStateDir(cwd);
115
+ try {
116
+ recordIntent(cwd, 'session-after-marker', 'parent-after-marker', 'tokenaftermarker');
117
+ const binding = bindPendingRoleIntentUnderLock(cwd, {
118
+ sessionId: 'session-after-marker',
119
+ parentThreadId: 'parent-after-marker',
120
+ correlationToken: canonicalCorrelationToken('tokenaftermarker'),
121
+ nowMs: NOW_MS,
122
+ }, bindAdaptedTurn('session-after-marker', 'child-after-marker'));
123
+ assert.equal(binding?.alreadyBound, false);
124
+ writeRoleRoutingMarker(stateDir, {
125
+ schema_version: 1,
126
+ cwd,
127
+ session_id: 'session-after-marker',
128
+ parent_thread_id: 'parent-after-marker',
129
+ observed_at: new Date(NOW_MS).toISOString(),
130
+ expires_at: new Date(NOW_MS + 60_000).toISOString(),
131
+ });
132
+ recoverAdaptedRoleBindings(cwd, stateDir, NOW_MS);
133
+ assert.deepEqual(listBoundAdaptedRoleIntents(cwd), []);
134
+ assert.equal(readRoleRoutingMarker(stateDir, {
135
+ cwd,
136
+ sessionId: 'session-after-marker',
137
+ parentThreadId: 'parent-after-marker',
138
+ nowMs: NOW_MS,
139
+ })?.session_id, 'session-after-marker');
140
+ }
141
+ finally {
142
+ await rm(cwd, { recursive: true, force: true });
143
+ }
144
+ });
145
+ it('recovers retained bindings from the durable tracker journal after process restart', async () => {
146
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-adapted-binding-'));
147
+ const stateDir = getBaseStateDir(cwd);
148
+ try {
149
+ recordIntent(cwd, 'session-restart', 'parent-restart', 'tokenrestart');
150
+ bindPendingRoleIntentUnderLock(cwd, {
151
+ sessionId: 'session-restart',
152
+ parentThreadId: 'parent-restart',
153
+ correlationToken: canonicalCorrelationToken('tokenrestart'),
154
+ nowMs: NOW_MS,
155
+ }, bindAdaptedTurn('session-restart', 'child-restart'));
156
+ recoverAdaptedRoleBindings(cwd, stateDir, NOW_MS);
157
+ assert.deepEqual(listBoundAdaptedRoleIntents(cwd), []);
158
+ assert.equal(readRoleRoutingMarker(stateDir, {
159
+ cwd,
160
+ sessionId: 'session-restart',
161
+ parentThreadId: 'parent-restart',
162
+ nowMs: NOW_MS,
163
+ })?.session_id, 'session-restart');
164
+ }
165
+ finally {
166
+ await rm(cwd, { recursive: true, force: true });
167
+ }
168
+ });
169
+ it('recovers an owned cwd-default claimant-less legacy bound journal', async () => {
170
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-adapted-legacy-bound-recovery-'));
171
+ const previousOmxRoot = process.env.OMX_ROOT;
172
+ const previousTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
173
+ const previousStateRoot = process.env.OMX_STATE_ROOT;
174
+ try {
175
+ delete process.env.OMX_ROOT;
176
+ delete process.env.OMX_TEAM_STATE_ROOT;
177
+ delete process.env.OMX_STATE_ROOT;
178
+ const stateDir = getBaseStateDir(cwd);
179
+ await mkdir(stateDir, { recursive: true });
180
+ await writeFile(subagentTrackingPath(cwd), `${JSON.stringify({
181
+ schemaVersion: 1,
182
+ sessions: {},
183
+ pending_role_intents: [{
184
+ role: 'architect',
185
+ session_id: 'legacy-bound-recovery-session',
186
+ parent_thread_id: 'legacy-bound-recovery-parent',
187
+ correlation_token: canonicalCorrelationToken('a3118r'),
188
+ created_at: new Date(NOW_MS).toISOString(),
189
+ expires_at: new Date(NOW_MS + 600_000).toISOString(),
190
+ binding_state: 'bound',
191
+ bound_at: new Date(NOW_MS).toISOString(),
192
+ }],
193
+ })}\n`);
194
+ recoverAdaptedRoleBindings(cwd, stateDir, NOW_MS);
195
+ assert.deepEqual(listBoundAdaptedRoleIntents(cwd), []);
196
+ assert.equal(readRoleRoutingMarker(stateDir, {
197
+ cwd,
198
+ sessionId: 'legacy-bound-recovery-session',
199
+ parentThreadId: 'legacy-bound-recovery-parent',
200
+ nowMs: NOW_MS,
201
+ })?.session_id, 'legacy-bound-recovery-session');
202
+ }
203
+ finally {
204
+ if (previousOmxRoot === undefined)
205
+ delete process.env.OMX_ROOT;
206
+ else
207
+ process.env.OMX_ROOT = previousOmxRoot;
208
+ if (previousTeamStateRoot === undefined)
209
+ delete process.env.OMX_TEAM_STATE_ROOT;
210
+ else
211
+ process.env.OMX_TEAM_STATE_ROOT = previousTeamStateRoot;
212
+ if (previousStateRoot === undefined)
213
+ delete process.env.OMX_STATE_ROOT;
214
+ else
215
+ process.env.OMX_STATE_ROOT = previousStateRoot;
216
+ await rm(cwd, { recursive: true, force: true });
217
+ }
218
+ });
219
+ it('does not duplicate adapted authority on an idempotent retry', async () => {
220
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-adapted-binding-'));
221
+ const stateDir = getBaseStateDir(cwd);
222
+ let bindCount = 0;
223
+ const bind = (state, intent) => {
224
+ bindCount += 1;
225
+ return bindAdaptedTurn('session-retry', 'child-retry')(state, intent);
226
+ };
227
+ try {
228
+ recordIntent(cwd, 'session-retry', 'parent-retry', 'tokenretry');
229
+ const input = {
230
+ correlationSessionId: 'session-retry',
231
+ parentThreadId: 'parent-retry',
232
+ correlationToken: canonicalCorrelationToken('tokenretry'),
233
+ nowMs: NOW_MS,
234
+ };
235
+ assert.deepEqual(bindAndPublishAdaptedRole(cwd, stateDir, input, bind), { role: 'architect' });
236
+ assert.equal(bindAndPublishAdaptedRole(cwd, stateDir, input, bind), null);
237
+ const state = await readSubagentTrackingState(cwd);
238
+ assert.equal(bindCount, 1);
239
+ assert.equal(state.sessions['session-retry']?.threads['child-retry']?.turn_count, 1);
240
+ assert.deepEqual(state.pending_role_intents, []);
241
+ assert.equal(readRoleRoutingMarker(stateDir, {
242
+ cwd,
243
+ sessionId: 'session-retry',
244
+ parentThreadId: 'parent-retry',
245
+ nowMs: NOW_MS,
246
+ })?.session_id, 'session-retry');
247
+ }
248
+ finally {
249
+ await rm(cwd, { recursive: true, force: true });
250
+ }
251
+ });
252
+ it('fences stale claimants and treats a post-recovery stale completion as a no-op', async () => {
253
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-adapted-binding-'));
254
+ const stateDir = getBaseStateDir(cwd);
255
+ const input = {
256
+ sessionId: 'session-stale',
257
+ parentThreadId: 'parent-stale',
258
+ correlationToken: canonicalCorrelationToken('tokenstale'),
259
+ nowMs: NOW_MS,
260
+ };
261
+ try {
262
+ recordIntent(cwd, input.sessionId, input.parentThreadId, input.correlationToken);
263
+ const first = bindPendingRoleIntentUnderLock(cwd, input, bindAdaptedTurn(input.sessionId, 'child-stale'));
264
+ assert.ok(first?.claimantToken);
265
+ assert.equal(completeAdaptedRoleBinding(cwd, { ...input, claimantToken: first.claimantToken }), 'completed');
266
+ recordIntent(cwd, input.sessionId, input.parentThreadId, input.correlationToken);
267
+ const successor = bindPendingRoleIntentUnderLock(cwd, input, bindAdaptedTurn(input.sessionId, 'child-stale'));
268
+ assert.ok(successor?.claimantToken);
269
+ assert.equal(completeAdaptedRoleBinding(cwd, { ...input, claimantToken: first.claimantToken }), 'claimant_mismatch');
270
+ assert.equal(listBoundAdaptedRoleIntents(cwd).length, 1);
271
+ recoverAdaptedRoleBindings(cwd, stateDir, NOW_MS);
272
+ assert.deepEqual(listBoundAdaptedRoleIntents(cwd), []);
273
+ assert.equal(completeAdaptedRoleBinding(cwd, { ...input, claimantToken: first.claimantToken }), 'not_found');
274
+ }
275
+ finally {
276
+ await rm(cwd, { recursive: true, force: true });
277
+ }
278
+ });
279
+ it('recovers a malformed bound journal without a claimant token', async () => {
280
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-adapted-binding-'));
281
+ const stateDir = getBaseStateDir(cwd);
282
+ try {
283
+ await mkdir(stateDir, { recursive: true });
284
+ await writeFile(subagentTrackingPath(cwd), `${JSON.stringify({
285
+ schemaVersion: 1,
286
+ sessions: {},
287
+ pending_role_intents: [{
288
+ role: 'architect',
289
+ session_id: 'session-malformed',
290
+ parent_thread_id: 'parent-malformed',
291
+ correlation_token: canonicalCorrelationToken('token-malformed'),
292
+ created_at: new Date(NOW_MS).toISOString(),
293
+ expires_at: new Date(NOW_MS - 1).toISOString(),
294
+ binding_state: 'bound',
295
+ bound_at: new Date(NOW_MS).toISOString(),
296
+ origin_cwd: cwd,
297
+ }],
298
+ }, null, 2)}\n`);
299
+ recoverAdaptedRoleBindings(cwd, stateDir, NOW_MS);
300
+ assert.deepEqual(listBoundAdaptedRoleIntents(cwd), []);
301
+ assert.equal(readRoleRoutingMarker(stateDir, {
302
+ cwd,
303
+ sessionId: 'session-malformed',
304
+ parentThreadId: 'parent-malformed',
305
+ nowMs: NOW_MS,
306
+ })?.session_id, 'session-malformed');
307
+ }
308
+ finally {
309
+ await rm(cwd, { recursive: true, force: true });
310
+ }
311
+ });
312
+ it('preserves a stored claimant when bound_at is malformed and fails closed on omitted completion', async () => {
313
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-adapted-binding-'));
314
+ const stateDir = getBaseStateDir(cwd);
315
+ try {
316
+ await mkdir(stateDir, { recursive: true });
317
+ await writeFile(subagentTrackingPath(cwd), `${JSON.stringify({
318
+ schemaVersion: 1,
319
+ sessions: {},
320
+ pending_role_intents: [{
321
+ role: 'architect',
322
+ session_id: 'session-boundat',
323
+ parent_thread_id: 'parent-boundat',
324
+ correlation_token: canonicalCorrelationToken('token-boundat'),
325
+ created_at: new Date(NOW_MS).toISOString(),
326
+ expires_at: new Date(NOW_MS + 600_000).toISOString(),
327
+ binding_state: 'bound',
328
+ binding_claimant_token: canonicalClaimantToken('stored-claimant'),
329
+ bound_at: 'not-a-valid-date',
330
+ origin_cwd: cwd,
331
+ }],
332
+ }, null, 2)}\n`);
333
+ // A malformed bound_at must NOT erase the stored security identity.
334
+ const normalized = (await readSubagentTrackingState(cwd)).pending_role_intents[0];
335
+ assert.equal(normalized?.binding_claimant_token, canonicalClaimantToken('stored-claimant'));
336
+ assert.equal(normalized?.bound_at, undefined);
337
+ // Fail closed: an omitted caller token cannot complete the claimed journal.
338
+ assert.equal(completeAdaptedRoleBinding(cwd, {
339
+ sessionId: 'session-boundat',
340
+ parentThreadId: 'parent-boundat',
341
+ correlationToken: canonicalCorrelationToken('token-boundat'),
342
+ }), 'claimant_mismatch');
343
+ assert.equal(listBoundAdaptedRoleIntents(cwd).length, 1);
344
+ // The exact stored token still completes.
345
+ assert.equal(completeAdaptedRoleBinding(cwd, {
346
+ sessionId: 'session-boundat',
347
+ parentThreadId: 'parent-boundat',
348
+ correlationToken: canonicalCorrelationToken('token-boundat'),
349
+ claimantToken: canonicalClaimantToken('stored-claimant'),
350
+ }), 'completed');
351
+ }
352
+ finally {
353
+ await rm(cwd, { recursive: true, force: true });
354
+ }
355
+ });
356
+ it('keeps same-scope markers and tracker evidence isolated by canonical workspace under a shared state root', async () => {
357
+ const sharedRoot = await mkdtemp(join(tmpdir(), 'omx-adapted-shared-markers-'));
358
+ const cwdA = join(sharedRoot, 'workspace-a');
359
+ const cwdB = join(sharedRoot, 'workspace-b');
360
+ const previousStateRoot = process.env.OMX_STATE_ROOT;
361
+ try {
362
+ process.env.OMX_STATE_ROOT = sharedRoot;
363
+ await mkdir(cwdA, { recursive: true });
364
+ await mkdir(cwdB, { recursive: true });
365
+ const sharedStateDir = getBaseStateDir(cwdA);
366
+ assert.equal(sharedStateDir, getBaseStateDir(cwdB));
367
+ const scope = { sessionId: 'session-shared-marker', parentThreadId: 'parent-shared-marker', nowMs: NOW_MS };
368
+ assert.equal(recordPendingRoleIntent(cwdA, {
369
+ role: 'architect', ...scope, correlationToken: canonicalCorrelationToken('tokena'),
370
+ }).ok, true);
371
+ assert.equal(recordPendingRoleIntent(cwdB, {
372
+ role: 'critic', ...scope, correlationToken: canonicalCorrelationToken('tokenb'),
373
+ }).ok, true);
374
+ assert.deepEqual(bindAndPublishAdaptedRole(cwdA, sharedStateDir, {
375
+ correlationSessionId: scope.sessionId,
376
+ parentThreadId: scope.parentThreadId,
377
+ correlationToken: canonicalCorrelationToken('tokena'),
378
+ nowMs: NOW_MS,
379
+ }, bindAdaptedTurn(scope.sessionId, 'child-a')), { role: 'architect' });
380
+ assert.deepEqual(bindAndPublishAdaptedRole(cwdB, sharedStateDir, {
381
+ correlationSessionId: scope.sessionId,
382
+ parentThreadId: scope.parentThreadId,
383
+ correlationToken: canonicalCorrelationToken('tokenb'),
384
+ nowMs: NOW_MS,
385
+ }, bindAdaptedTurn(scope.sessionId, 'child-b')), { role: 'critic' });
386
+ // B's completed journal removal must not overwrite A's same-scope marker or tracker evidence.
387
+ const state = await readSubagentTrackingState(cwdA);
388
+ assert.equal(state.sessions[scope.sessionId]?.threads['child-a']?.role, 'architect');
389
+ assert.equal(state.sessions[scope.sessionId]?.threads['child-b']?.role, 'critic');
390
+ assert.deepEqual(state.pending_role_intents, []);
391
+ assert.equal(readRoleRoutingMarker(sharedStateDir, {
392
+ cwd: cwdA, sessionId: scope.sessionId, parentThreadId: scope.parentThreadId, nowMs: NOW_MS,
393
+ })?.cwd, canonicalizeOriginCwd(cwdA));
394
+ assert.equal(readRoleRoutingMarker(sharedStateDir, {
395
+ cwd: cwdB, sessionId: scope.sessionId, parentThreadId: scope.parentThreadId, nowMs: NOW_MS,
396
+ })?.cwd, canonicalizeOriginCwd(cwdB));
397
+ }
398
+ finally {
399
+ if (previousStateRoot === undefined)
400
+ delete process.env.OMX_STATE_ROOT;
401
+ else
402
+ process.env.OMX_STATE_ROOT = previousStateRoot;
403
+ await rm(sharedRoot, { recursive: true, force: true });
404
+ }
405
+ });
406
+ it('preserves legacy no-cwd markers and matches them for any cwd (backward compatibility)', async () => {
407
+ const stateDir = await mkdtemp(join(tmpdir(), 'omx-legacy-marker-'));
408
+ try {
409
+ // Legacy marker shape: no cwd field (pre-cwd-identity markers).
410
+ writeRoleRoutingMarker(stateDir, {
411
+ schema_version: 1,
412
+ session_id: 'session-legacy',
413
+ parent_thread_id: 'parent-legacy',
414
+ observed_at: new Date(NOW_MS + 1).toISOString(),
415
+ expires_at: new Date(NOW_MS + 60_000).toISOString(),
416
+ evidence: 'legacy',
417
+ });
418
+ // A legacy no-cwd marker still matches a read for any cwd (backward compatibility).
419
+ assert.equal(readRoleRoutingMarker(stateDir, {
420
+ cwd: '/some/other/workspace', sessionId: 'session-legacy', parentThreadId: 'parent-legacy', nowMs: NOW_MS,
421
+ })?.evidence, 'legacy');
422
+ // A newer legacy marker must not shadow an exact cwd-bearing marker for the same scope.
423
+ writeRoleRoutingMarker(stateDir, {
424
+ schema_version: 1,
425
+ cwd: stateDir,
426
+ session_id: 'session-legacy',
427
+ parent_thread_id: 'parent-legacy',
428
+ observed_at: new Date(NOW_MS).toISOString(),
429
+ expires_at: new Date(NOW_MS + 60_000).toISOString(),
430
+ evidence: 'cwd-scoped',
431
+ });
432
+ // The legacy marker still resolves for an unrelated cwd; the cwd-scoped one resolves for its cwd.
433
+ assert.equal(readRoleRoutingMarker(stateDir, {
434
+ cwd: '/some/other/workspace', sessionId: 'session-legacy', parentThreadId: 'parent-legacy', nowMs: NOW_MS,
435
+ })?.evidence, 'legacy');
436
+ assert.equal(readRoleRoutingMarker(stateDir, {
437
+ cwd: stateDir, sessionId: 'session-legacy', parentThreadId: 'parent-legacy', nowMs: NOW_MS,
438
+ })?.evidence, 'cwd-scoped');
439
+ }
440
+ finally {
441
+ await rm(stateDir, { recursive: true, force: true });
442
+ }
443
+ });
444
+ it('retains expired foreign journals through every successful shared-root writeback', async () => {
445
+ const sharedRoot = await mkdtemp(join(tmpdir(), 'omx-adapted-shared-writeback-'));
446
+ const cwdA = join(sharedRoot, 'workspace-a');
447
+ const cwdB = join(sharedRoot, 'workspace-b');
448
+ const previousStateRoot = process.env.OMX_STATE_ROOT;
449
+ try {
450
+ process.env.OMX_STATE_ROOT = sharedRoot;
451
+ await mkdir(cwdA, { recursive: true });
452
+ await mkdir(cwdB, { recursive: true });
453
+ assert.equal(getBaseStateDir(cwdA), getBaseStateDir(cwdB));
454
+ const foreignResult = recordPendingRoleIntent(cwdA, {
455
+ role: 'architect',
456
+ sessionId: 'session-expired-a',
457
+ parentThreadId: 'parent-expired-a',
458
+ correlationToken: canonicalCorrelationToken('tokenexpireda'),
459
+ ttlMs: 1,
460
+ nowMs: NOW_MS,
461
+ });
462
+ assert.equal(foreignResult.ok, true);
463
+ if (!foreignResult.ok)
464
+ throw new Error('Expected the foreign role intent to record.');
465
+ const expiredForeignIntent = foreignResult.intent;
466
+ const lateNowMs = NOW_MS + 2;
467
+ const assertForeignRetained = async () => {
468
+ const foreignIntent = (await readSubagentTrackingState(cwdB)).pending_role_intents
469
+ .find((intent) => intent.correlation_token === expiredForeignIntent.correlation_token);
470
+ assert.deepEqual(foreignIntent, expiredForeignIntent);
471
+ };
472
+ assert.equal(recordPendingRoleIntent(cwdB, {
473
+ role: 'critic',
474
+ sessionId: 'session-bound-b',
475
+ parentThreadId: 'parent-bound-b',
476
+ correlationToken: canonicalCorrelationToken('tokenboundb'),
477
+ nowMs: lateNowMs,
478
+ }).ok, true);
479
+ await assertForeignRetained();
480
+ const bound = bindPendingRoleIntentUnderLock(cwdB, {
481
+ sessionId: 'session-bound-b',
482
+ parentThreadId: 'parent-bound-b',
483
+ correlationToken: canonicalCorrelationToken('tokenboundb'),
484
+ nowMs: lateNowMs,
485
+ }, bindAdaptedTurn('session-bound-b', 'child-bound-b'));
486
+ assert.ok(bound?.claimantToken);
487
+ await assertForeignRetained();
488
+ assert.equal(completeAdaptedRoleBinding(cwdB, {
489
+ sessionId: 'session-bound-b',
490
+ parentThreadId: 'parent-bound-b',
491
+ correlationToken: canonicalCorrelationToken('tokenboundb'),
492
+ claimantToken: bound?.claimantToken,
493
+ nowMs: lateNowMs,
494
+ }), 'completed');
495
+ await assertForeignRetained();
496
+ assert.equal(recordPendingRoleIntent(cwdB, {
497
+ role: 'critic',
498
+ sessionId: 'session-consume-b',
499
+ parentThreadId: 'parent-consume-b',
500
+ correlationToken: canonicalCorrelationToken('tokenconsumeb'),
501
+ nowMs: lateNowMs,
502
+ }).ok, true);
503
+ await assertForeignRetained();
504
+ assert.deepEqual(consumePendingRoleIntent(cwdB, {
505
+ sessionId: 'session-consume-b',
506
+ parentThreadId: 'parent-consume-b',
507
+ correlationToken: canonicalCorrelationToken('tokenconsumeb'),
508
+ nowMs: lateNowMs,
509
+ }), { role: 'critic', provenanceKind: OMX_ADAPTED_PROVENANCE });
510
+ await assertForeignRetained();
511
+ }
512
+ finally {
513
+ if (previousStateRoot === undefined)
514
+ delete process.env.OMX_STATE_ROOT;
515
+ else
516
+ process.env.OMX_STATE_ROOT = previousStateRoot;
517
+ await rm(sharedRoot, { recursive: true, force: true });
518
+ }
519
+ });
520
+ it('allows one same-scope winner per origin and lets a bound A journal coexist with B', async () => {
521
+ const sharedRoot = await mkdtemp(join(tmpdir(), 'omx-adapted-shared-single-flight-'));
522
+ const cwdA = join(sharedRoot, 'workspace-a');
523
+ const cwdB = join(sharedRoot, 'workspace-b');
524
+ const previousStateRoot = process.env.OMX_STATE_ROOT;
525
+ const scope = { sessionId: 'session-single-flight', parentThreadId: 'parent-single-flight', nowMs: NOW_MS };
526
+ try {
527
+ process.env.OMX_STATE_ROOT = sharedRoot;
528
+ await mkdir(cwdA, { recursive: true });
529
+ await mkdir(cwdB, { recursive: true });
530
+ assert.equal(getBaseStateDir(cwdA), getBaseStateDir(cwdB));
531
+ assert.equal(recordPendingRoleIntent(cwdA, {
532
+ role: 'architect', ...scope, correlationToken: canonicalCorrelationToken('tokena'),
533
+ }).ok, true);
534
+ assert.equal(recordPendingRoleIntent(cwdB, {
535
+ role: 'critic', ...scope, correlationToken: canonicalCorrelationToken('tokenb'),
536
+ }).ok, true);
537
+ assert.deepEqual(recordPendingRoleIntent(cwdA, {
538
+ role: 'critic', ...scope, correlationToken: canonicalCorrelationToken('tokenasecond'),
539
+ }), { ok: false, reason: 'single_flight_conflict' });
540
+ assert.deepEqual(recordPendingRoleIntent(cwdB, {
541
+ role: 'architect', ...scope, correlationToken: canonicalCorrelationToken('tokenbsecond'),
542
+ }), { ok: false, reason: 'single_flight_conflict' });
543
+ const boundA = bindPendingRoleIntentUnderLock(cwdA, {
544
+ ...scope,
545
+ correlationToken: canonicalCorrelationToken('tokena'),
546
+ }, bindAdaptedTurn(scope.sessionId, 'child-a'));
547
+ assert.ok(boundA?.claimantToken);
548
+ assert.deepEqual(consumePendingRoleIntent(cwdB, {
549
+ ...scope,
550
+ correlationToken: canonicalCorrelationToken('tokenb'),
551
+ }), { role: 'critic', provenanceKind: OMX_ADAPTED_PROVENANCE });
552
+ // A's bound journal still occupies only A's scope; B can independently record (S, P).
553
+ assert.equal(recordPendingRoleIntent(cwdB, {
554
+ role: 'critic', ...scope, correlationToken: canonicalCorrelationToken('tokenbafterabound'),
555
+ }).ok, true);
556
+ }
557
+ finally {
558
+ if (previousStateRoot === undefined)
559
+ delete process.env.OMX_STATE_ROOT;
560
+ else
561
+ process.env.OMX_STATE_ROOT = previousStateRoot;
562
+ await rm(sharedRoot, { recursive: true, force: true });
563
+ }
564
+ });
565
+ it('does not let a foreign workspace recover a retained intent under a shared state root', async () => {
566
+ const sharedRoot = await mkdtemp(join(tmpdir(), 'omx-adapted-shared-root-'));
567
+ const cwdA = join(sharedRoot, 'workspace-a');
568
+ const cwdB = join(sharedRoot, 'workspace-b');
569
+ const previousStateRoot = process.env.OMX_STATE_ROOT;
570
+ try {
571
+ process.env.OMX_STATE_ROOT = sharedRoot;
572
+ await mkdir(cwdA, { recursive: true });
573
+ await mkdir(cwdB, { recursive: true });
574
+ // Under a shared OMX_STATE_ROOT, A and B share one tracker + marker store.
575
+ const sharedStateDir = getBaseStateDir(cwdA);
576
+ assert.equal(sharedStateDir, getBaseStateDir(cwdB));
577
+ assert.equal(recordPendingRoleIntent(cwdA, {
578
+ role: 'architect',
579
+ sessionId: 'session-a',
580
+ parentThreadId: 'parent-a',
581
+ correlationToken: canonicalCorrelationToken('tokena'),
582
+ nowMs: NOW_MS,
583
+ }).ok, true);
584
+ // A binds (tracker committed) then crashes before publishing the marker.
585
+ assert.ok(bindPendingRoleIntentUnderLock(cwdA, {
586
+ sessionId: 'session-a',
587
+ parentThreadId: 'parent-a',
588
+ correlationToken: canonicalCorrelationToken('tokena'),
589
+ nowMs: NOW_MS,
590
+ }, bindAdaptedTurn('session-a', 'child-a'))?.claimantToken);
591
+ // Workspace B recovery must NOT steal A's journal or publish an A-scoped marker.
592
+ recoverAdaptedRoleBindings(cwdB, sharedStateDir, NOW_MS);
593
+ assert.equal(listBoundAdaptedRoleIntents(cwdB).length, 1);
594
+ assert.equal(readRoleRoutingMarker(sharedStateDir, {
595
+ cwd: cwdA,
596
+ sessionId: 'session-a',
597
+ parentThreadId: 'parent-a',
598
+ nowMs: NOW_MS,
599
+ }), null);
600
+ assert.equal(existsSync(join(sharedStateDir, NATIVE_SUBAGENT_ROLE_ROUTING_MARKER_FILE)), false);
601
+ // The origin workspace A recovery reconstructs the pair and completes it.
602
+ recoverAdaptedRoleBindings(cwdA, sharedStateDir, NOW_MS);
603
+ assert.deepEqual(listBoundAdaptedRoleIntents(cwdA), []);
604
+ assert.equal(readRoleRoutingMarker(sharedStateDir, {
605
+ cwd: cwdA,
606
+ sessionId: 'session-a',
607
+ parentThreadId: 'parent-a',
608
+ nowMs: NOW_MS,
609
+ })?.session_id, 'session-a');
610
+ }
611
+ finally {
612
+ if (previousStateRoot === undefined)
613
+ delete process.env.OMX_STATE_ROOT;
614
+ else
615
+ process.env.OMX_STATE_ROOT = previousStateRoot;
616
+ await rm(sharedRoot, { recursive: true, force: true });
617
+ }
618
+ });
619
+ it('does not recover a legacy no-origin bound journal from another workspace under a shared state root', async () => {
620
+ const sharedRoot = await mkdtemp(join(tmpdir(), 'omx-adapted-shared-legacy-recovery-'));
621
+ const cwdA = join(sharedRoot, 'workspace-a');
622
+ const cwdB = join(sharedRoot, 'workspace-b');
623
+ const previousOmxRoot = process.env.OMX_ROOT;
624
+ const previousTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
625
+ const previousStateRoot = process.env.OMX_STATE_ROOT;
626
+ try {
627
+ delete process.env.OMX_ROOT;
628
+ delete process.env.OMX_TEAM_STATE_ROOT;
629
+ process.env.OMX_STATE_ROOT = sharedRoot;
630
+ await mkdir(cwdA, { recursive: true });
631
+ await mkdir(cwdB, { recursive: true });
632
+ const sharedStateDir = getBaseStateDir(cwdA);
633
+ await mkdir(sharedStateDir, { recursive: true });
634
+ await writeFile(subagentTrackingPath(cwdA), `${JSON.stringify({
635
+ schemaVersion: 1,
636
+ sessions: {},
637
+ pending_role_intents: [{
638
+ role: 'architect',
639
+ session_id: 'shared-legacy-bound-session',
640
+ parent_thread_id: 'shared-legacy-bound-parent',
641
+ correlation_token: canonicalCorrelationToken('a3118s'),
642
+ created_at: new Date(NOW_MS).toISOString(),
643
+ expires_at: new Date(NOW_MS + 600_000).toISOString(),
644
+ binding_state: 'bound',
645
+ binding_claimant_token: canonicalClaimantToken('legacyclaimant'),
646
+ bound_at: new Date(NOW_MS).toISOString(),
647
+ }],
648
+ })}\n`);
649
+ recoverAdaptedRoleBindings(cwdB, sharedStateDir, NOW_MS);
650
+ assert.equal(listBoundAdaptedRoleIntents(cwdB).length, 1);
651
+ assert.equal(existsSync(join(sharedStateDir, NATIVE_SUBAGENT_ROLE_ROUTING_MARKER_FILE)), false);
652
+ assert.equal((await readSubagentTrackingState(cwdB)).pending_role_intents[0]?.origin_cwd, undefined);
653
+ }
654
+ finally {
655
+ if (previousOmxRoot === undefined)
656
+ delete process.env.OMX_ROOT;
657
+ else
658
+ process.env.OMX_ROOT = previousOmxRoot;
659
+ if (previousTeamStateRoot === undefined)
660
+ delete process.env.OMX_TEAM_STATE_ROOT;
661
+ else
662
+ process.env.OMX_TEAM_STATE_ROOT = previousTeamStateRoot;
663
+ if (previousStateRoot === undefined)
664
+ delete process.env.OMX_STATE_ROOT;
665
+ else
666
+ process.env.OMX_STATE_ROOT = previousStateRoot;
667
+ await rm(sharedRoot, { recursive: true, force: true });
668
+ }
669
+ });
670
+ it('fails closed when a foreign workspace drives the primary bind path under a shared state root', async () => {
671
+ const sharedRoot = await mkdtemp(join(tmpdir(), 'omx-adapted-shared-bind-'));
672
+ const cwdA = join(sharedRoot, 'workspace-a');
673
+ const cwdB = join(sharedRoot, 'workspace-b');
674
+ const previousStateRoot = process.env.OMX_STATE_ROOT;
675
+ let foreignCallbackRuns = 0;
676
+ const foreignCallback = (state) => {
677
+ foreignCallbackRuns += 1;
678
+ return state;
679
+ };
680
+ try {
681
+ process.env.OMX_STATE_ROOT = sharedRoot;
682
+ await mkdir(cwdA, { recursive: true });
683
+ await mkdir(cwdB, { recursive: true });
684
+ const sharedStateDir = getBaseStateDir(cwdA);
685
+ assert.equal(sharedStateDir, getBaseStateDir(cwdB));
686
+ const scope = { sessionId: 'session-a', parentThreadId: 'parent-a', correlationToken: canonicalCorrelationToken('tokena'), nowMs: NOW_MS };
687
+ const legacyScope = {
688
+ sessionId: 'session-legacy-no-origin',
689
+ parentThreadId: 'parent-legacy-no-origin',
690
+ correlationToken: canonicalCorrelationToken('token-legacy-no-origin'),
691
+ nowMs: NOW_MS,
692
+ };
693
+ await mkdir(sharedStateDir, { recursive: true });
694
+ await writeFile(subagentTrackingPath(cwdA), `${JSON.stringify({
695
+ schemaVersion: 1,
696
+ sessions: {},
697
+ pending_role_intents: [{
698
+ role: 'architect',
699
+ session_id: legacyScope.sessionId,
700
+ parent_thread_id: legacyScope.parentThreadId,
701
+ correlation_token: legacyScope.correlationToken,
702
+ created_at: new Date(NOW_MS).toISOString(),
703
+ expires_at: new Date(NOW_MS + 600_000).toISOString(),
704
+ }],
705
+ }, null, 2)}\n`);
706
+ // A shared root carries no workspace identity for a pre-upgrade journal, so B cannot
707
+ // claim it through either direct binding or the adapted binding surface.
708
+ assert.equal(bindPendingRoleIntentUnderLock(cwdB, legacyScope, foreignCallback), null);
709
+ assert.equal(bindAndPublishAdaptedRole(cwdB, sharedStateDir, {
710
+ correlationSessionId: legacyScope.sessionId,
711
+ parentThreadId: legacyScope.parentThreadId,
712
+ correlationToken: legacyScope.correlationToken,
713
+ nowMs: NOW_MS,
714
+ }, foreignCallback), null);
715
+ assert.equal(foreignCallbackRuns, 0);
716
+ assert.equal(recordPendingRoleIntent(cwdA, { role: 'architect', ...scope }).ok, true);
717
+ // Foreign B primary bind against A's UNBOUND intent: null, zero side effects.
718
+ assert.equal(bindPendingRoleIntentUnderLock(cwdB, scope, foreignCallback), null);
719
+ assert.equal(bindAndPublishAdaptedRole(cwdB, sharedStateDir, {
720
+ correlationSessionId: 'session-a', parentThreadId: 'parent-a', correlationToken: canonicalCorrelationToken('tokena'), nowMs: NOW_MS,
721
+ }, foreignCallback), null);
722
+ assert.equal(foreignCallbackRuns, 0);
723
+ const stillPending = (await readSubagentTrackingState(cwdA)).pending_role_intents;
724
+ assert.equal(stillPending.find((intent) => intent.correlation_token === scope.correlationToken)?.binding_state, undefined);
725
+ assert.equal(stillPending.find((intent) => intent.correlation_token === legacyScope.correlationToken)?.origin_cwd, undefined);
726
+ assert.equal(existsSync(join(sharedStateDir, NATIVE_SUBAGENT_ROLE_ROUTING_MARKER_FILE)), false);
727
+ // Origin A binds its own intent (now already-bound).
728
+ const originBind = bindPendingRoleIntentUnderLock(cwdA, scope, bindAdaptedTurn('session-a', 'child-a'));
729
+ assert.equal(originBind?.alreadyBound, false);
730
+ assert.ok(originBind?.claimantToken);
731
+ // Foreign B primary bind against A's ALREADY-BOUND intent: null, no claimant disclosure.
732
+ assert.equal(bindPendingRoleIntentUnderLock(cwdB, scope, foreignCallback), null);
733
+ // Foreign B cannot complete A's bound journal even with the correct token.
734
+ assert.equal(completeAdaptedRoleBinding(cwdB, {
735
+ sessionId: 'session-a', parentThreadId: 'parent-a', correlationToken: canonicalCorrelationToken('tokena'), claimantToken: originBind?.claimantToken,
736
+ }), 'not_found');
737
+ // Foreign B recovery is likewise a no-op.
738
+ recoverAdaptedRoleBindings(cwdB, sharedStateDir, NOW_MS);
739
+ assert.equal(foreignCallbackRuns, 0);
740
+ assert.equal(listBoundAdaptedRoleIntents(cwdA).length, 1);
741
+ // A same-workspace replay sees no durable claimant disclosure.
742
+ const originRetry = bindPendingRoleIntentUnderLock(cwdA, scope, foreignCallback);
743
+ assert.equal(originRetry?.alreadyBound, true);
744
+ assert.equal(originRetry?.claimantToken, undefined);
745
+ assert.equal(foreignCallbackRuns, 0);
746
+ // Origin A recovery converges the pair.
747
+ recoverAdaptedRoleBindings(cwdA, sharedStateDir, NOW_MS);
748
+ assert.deepEqual(listBoundAdaptedRoleIntents(cwdA), []);
749
+ assert.equal(readRoleRoutingMarker(sharedStateDir, {
750
+ cwd: cwdA, sessionId: 'session-a', parentThreadId: 'parent-a', nowMs: NOW_MS,
751
+ })?.session_id, 'session-a');
752
+ }
753
+ finally {
754
+ if (previousStateRoot === undefined)
755
+ delete process.env.OMX_STATE_ROOT;
756
+ else
757
+ process.env.OMX_STATE_ROOT = previousStateRoot;
758
+ await rm(sharedRoot, { recursive: true, force: true });
759
+ }
760
+ });
761
+ it('authenticates a symlink-aliased origin workspace as the same canonical origin', async () => {
762
+ const realCwd = await mkdtemp(join(tmpdir(), 'omx-adapted-real-'));
763
+ const aliasParent = await mkdtemp(join(tmpdir(), 'omx-adapted-alias-'));
764
+ const aliasCwd = join(aliasParent, 'alias');
765
+ try {
766
+ await symlink(realCwd, aliasCwd);
767
+ const stateDir = getBaseStateDir(realCwd);
768
+ writeRoleRoutingMarker(stateDir, {
769
+ schema_version: 1,
770
+ cwd: realCwd,
771
+ session_id: 'session-alias-marker',
772
+ parent_thread_id: 'parent-alias-marker',
773
+ observed_at: new Date(NOW_MS).toISOString(),
774
+ expires_at: new Date(NOW_MS + 600_000).toISOString(),
775
+ });
776
+ assert.equal(readRoleRoutingMarker(stateDir, {
777
+ cwd: aliasCwd,
778
+ sessionId: 'session-alias-marker',
779
+ parentThreadId: 'parent-alias-marker',
780
+ nowMs: NOW_MS,
781
+ })?.cwd, realCwd);
782
+ const scope = { sessionId: 'session-alias', parentThreadId: 'parent-alias', correlationToken: canonicalCorrelationToken('tokenalias'), nowMs: NOW_MS };
783
+ // Record via the real path.
784
+ assert.equal(recordPendingRoleIntent(realCwd, { role: 'architect', ...scope }).ok, true);
785
+ // Bind via the symlink alias -> same canonical origin -> authenticated success.
786
+ const binding = bindPendingRoleIntentUnderLock(aliasCwd, scope, bindAdaptedTurn('session-alias', 'child-alias'));
787
+ assert.equal(binding?.alreadyBound, false);
788
+ assert.ok(binding?.claimantToken);
789
+ // Complete via the real path with the alias-minted claimant.
790
+ assert.equal(completeAdaptedRoleBinding(realCwd, {
791
+ sessionId: 'session-alias', parentThreadId: 'parent-alias', correlationToken: canonicalCorrelationToken('tokenalias'), claimantToken: binding?.claimantToken,
792
+ }), 'completed');
793
+ }
794
+ finally {
795
+ await rm(realCwd, { recursive: true, force: true });
796
+ await rm(aliasParent, { recursive: true, force: true });
797
+ }
798
+ });
799
+ it('never publishes a recovery marker for an invalid dominant durable credential', async () => {
800
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-adapted-invalid-dominant-'));
801
+ const stateDir = getBaseStateDir(cwd);
802
+ const base = {
803
+ role: 'architect',
804
+ session_id: 'invalid-dominant-session',
805
+ parent_thread_id: 'invalid-dominant-parent',
806
+ created_at: new Date(NOW_MS).toISOString(),
807
+ expires_at: new Date(NOW_MS + 60_000).toISOString(),
808
+ binding_state: 'bound',
809
+ origin_cwd: cwd,
810
+ };
811
+ try {
812
+ await mkdir(stateDir, { recursive: true });
813
+ const invalidCorrelation = { ...base, correlation_token: '', binding_claimant_token: canonicalClaimantToken('claimant') };
814
+ const validCredential = { ...base, correlation_token: canonicalCorrelationToken('zvalidtoken'), binding_claimant_token: canonicalClaimantToken('claimant') };
815
+ for (const intents of [[invalidCorrelation, validCredential], [validCredential, invalidCorrelation]]) {
816
+ await writeFile(subagentTrackingPath(cwd), `${JSON.stringify({ schemaVersion: 1, sessions: {}, pending_role_intents: intents })}\n`);
817
+ recoverAdaptedRoleBindings(cwd, stateDir, NOW_MS);
818
+ assert.equal(readRoleRoutingMarker(stateDir, {
819
+ cwd, sessionId: base.session_id, parentThreadId: base.parent_thread_id, nowMs: NOW_MS,
820
+ }), null);
821
+ assert.equal((await readSubagentTrackingState(cwd)).pending_role_intents.length, 2);
822
+ }
823
+ const validDominant = { ...base, correlation_token: '00000000000000000000000000000000', binding_claimant_token: canonicalClaimantToken('claimant') };
824
+ const invalidLower = { ...base, correlation_token: 'ffffffffffffffffffffffffffffffff', binding_claimant_token: ' ' };
825
+ for (const intents of [[validDominant, invalidLower], [invalidLower, validDominant]]) {
826
+ await writeFile(subagentTrackingPath(cwd), `${JSON.stringify({ schemaVersion: 1, sessions: {}, pending_role_intents: intents })}\n`);
827
+ recoverAdaptedRoleBindings(cwd, stateDir, NOW_MS);
828
+ assert.equal(readRoleRoutingMarker(stateDir, {
829
+ cwd, sessionId: base.session_id, parentThreadId: base.parent_thread_id, nowMs: NOW_MS,
830
+ })?.session_id, base.session_id);
831
+ assert.equal((await readSubagentTrackingState(cwd)).pending_role_intents.length, 1);
832
+ }
833
+ }
834
+ finally {
835
+ await rm(cwd, { recursive: true, force: true });
836
+ }
837
+ });
838
+ it('fails closed on a malformed caller origin but migrates a cwd-partitioned originless journal', async () => {
839
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-adapted-binding-'));
840
+ const stateDir = getBaseStateDir(cwd);
841
+ let callbackRuns = 0;
842
+ const callback = (state) => {
843
+ callbackRuns += 1;
844
+ return state;
845
+ };
846
+ try {
847
+ // A blank caller origin cannot be canonicalized -> fail closed, no lock, no callback.
848
+ assert.equal(bindPendingRoleIntentUnderLock(' ', {
849
+ sessionId: 'session-x', parentThreadId: 'parent-x', correlationToken: canonicalCorrelationToken('token-x'), nowMs: NOW_MS,
850
+ }, callback), null);
851
+ assert.equal(callbackRuns, 0);
852
+ assert.equal(consumePendingRoleIntent(' ', {
853
+ sessionId: 'session-x', parentThreadId: 'parent-x', correlationToken: canonicalCorrelationToken('token-x'), nowMs: NOW_MS,
854
+ }), null);
855
+ assert.equal(completeAdaptedRoleBinding(' ', {
856
+ sessionId: 'session-x', parentThreadId: 'parent-x', correlationToken: canonicalCorrelationToken('token-x'), nowMs: NOW_MS,
857
+ }), 'not_found');
858
+ assert.equal(recordPendingRoleIntent(cwd, {
859
+ role: 'critic',
860
+ sessionId: 'session-normal',
861
+ parentThreadId: 'parent-normal',
862
+ correlationToken: canonicalCorrelationToken('tokennormal'),
863
+ nowMs: NOW_MS,
864
+ }).ok, true);
865
+ // A cwd-partitioned base state root identifies a legacy no-origin journal with this
866
+ // workspace, so a successful bind stamps its canonical origin for future isolation.
867
+ await mkdir(stateDir, { recursive: true });
868
+ await writeFile(subagentTrackingPath(cwd), `${JSON.stringify({
869
+ schemaVersion: 1,
870
+ sessions: {},
871
+ pending_role_intents: [{
872
+ role: 'architect',
873
+ session_id: 'session-x',
874
+ parent_thread_id: 'parent-x',
875
+ correlation_token: canonicalCorrelationToken('token-x'),
876
+ created_at: new Date(NOW_MS).toISOString(),
877
+ expires_at: new Date(NOW_MS + 600_000).toISOString(),
878
+ }],
879
+ }, null, 2)}\n`);
880
+ const legacyBinding = bindPendingRoleIntentUnderLock(cwd, {
881
+ sessionId: 'session-x', parentThreadId: 'parent-x', correlationToken: canonicalCorrelationToken('token-x'), nowMs: NOW_MS,
882
+ }, callback);
883
+ assert.equal(legacyBinding?.alreadyBound, false);
884
+ assert.ok(legacyBinding?.claimantToken);
885
+ assert.equal((await readSubagentTrackingState(cwd)).pending_role_intents[0]?.origin_cwd, canonicalizeOriginCwd(cwd));
886
+ }
887
+ finally {
888
+ await rm(cwd, { recursive: true, force: true });
889
+ }
890
+ });
891
+ it('keeps a bound intent isolated to its workspace state directory', async () => {
892
+ const cwdA = await mkdtemp(join(tmpdir(), 'omx-adapted-binding-a-'));
893
+ const cwdB = await mkdtemp(join(tmpdir(), 'omx-adapted-binding-b-'));
894
+ const stateDirA = getBaseStateDir(cwdA);
895
+ const stateDirB = getBaseStateDir(cwdB);
896
+ try {
897
+ recordIntent(cwdA, 'session-workspace', 'parent-workspace', 'tokenworkspace');
898
+ bindPendingRoleIntentUnderLock(cwdA, {
899
+ sessionId: 'session-workspace',
900
+ parentThreadId: 'parent-workspace',
901
+ correlationToken: canonicalCorrelationToken('tokenworkspace'),
902
+ nowMs: NOW_MS,
903
+ }, bindAdaptedTurn('session-workspace', 'child-workspace'));
904
+ recoverAdaptedRoleBindings(cwdA, stateDirA, NOW_MS);
905
+ assert.equal(readRoleRoutingMarker(stateDirA, {
906
+ cwd: cwdA,
907
+ sessionId: 'session-workspace',
908
+ parentThreadId: 'parent-workspace',
909
+ nowMs: NOW_MS,
910
+ })?.session_id, 'session-workspace');
911
+ assert.equal(existsSync(join(stateDirB, NATIVE_SUBAGENT_ROLE_ROUTING_MARKER_FILE)), false);
912
+ }
913
+ finally {
914
+ await rm(cwdA, { recursive: true, force: true });
915
+ await rm(cwdB, { recursive: true, force: true });
916
+ }
917
+ });
918
+ it('recovers distinct session and parent scopes into distinct markers', async () => {
919
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-adapted-binding-'));
920
+ const stateDir = getBaseStateDir(cwd);
921
+ try {
922
+ recordIntent(cwd, 'session-a-one', 'parent-a-one', 'tokenaone');
923
+ recordIntent(cwd, 'session-a-two', 'parent-a-two', 'tokenatwo');
924
+ bindPendingRoleIntentUnderLock(cwd, {
925
+ sessionId: 'session-a-one',
926
+ parentThreadId: 'parent-a-one',
927
+ correlationToken: canonicalCorrelationToken('tokenaone'),
928
+ nowMs: NOW_MS,
929
+ }, bindAdaptedTurn('session-a-one', 'child-a-one'));
930
+ bindPendingRoleIntentUnderLock(cwd, {
931
+ sessionId: 'session-a-two',
932
+ parentThreadId: 'parent-a-two',
933
+ correlationToken: canonicalCorrelationToken('tokenatwo'),
934
+ nowMs: NOW_MS,
935
+ }, bindAdaptedTurn('session-a-two', 'child-a-two'));
936
+ recoverAdaptedRoleBindings(cwd, stateDir, NOW_MS);
937
+ assert.equal(readRoleRoutingMarker(stateDir, {
938
+ cwd,
939
+ sessionId: 'session-a-one',
940
+ parentThreadId: 'parent-a-one',
941
+ nowMs: NOW_MS,
942
+ })?.session_id, 'session-a-one');
943
+ assert.equal(readRoleRoutingMarker(stateDir, {
944
+ cwd,
945
+ sessionId: 'session-a-two',
946
+ parentThreadId: 'parent-a-two',
947
+ nowMs: NOW_MS,
948
+ })?.session_id, 'session-a-two');
949
+ }
950
+ finally {
951
+ await rm(cwd, { recursive: true, force: true });
952
+ }
953
+ });
954
+ it('rejects every malformed persisted credential without publishing or deleting it', async () => {
955
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-adapted-invalid-credential-'));
956
+ const stateDir = getBaseStateDir(cwd);
957
+ const base = {
958
+ role: 'architect',
959
+ session_id: 'invalid-credential-session',
960
+ parent_thread_id: 'invalid-credential-parent',
961
+ created_at: new Date(NOW_MS).toISOString(),
962
+ expires_at: new Date(NOW_MS + 60_000).toISOString(),
963
+ binding_state: 'bound',
964
+ origin_cwd: cwd,
965
+ };
966
+ const validCorrelation = canonicalCorrelationToken('valid-correlation');
967
+ const validClaimant = canonicalClaimantToken('valid-claimant');
968
+ try {
969
+ await mkdir(stateDir, { recursive: true });
970
+ for (const [field, invalid] of [
971
+ ['correlation_token', ''], ['correlation_token', ' '], ['correlation_token', null], ['correlation_token', 1], ['correlation_token', true], ['correlation_token', []], ['correlation_token', {}],
972
+ ['binding_claimant_token', ''], ['binding_claimant_token', ' '], ['binding_claimant_token', null], ['binding_claimant_token', 1], ['binding_claimant_token', true], ['binding_claimant_token', []], ['binding_claimant_token', {}],
973
+ ]) {
974
+ const invalidIntent = field === 'correlation_token'
975
+ ? { ...base, correlation_token: invalid, binding_claimant_token: validClaimant }
976
+ : { ...base, correlation_token: validCorrelation, binding_claimant_token: invalid };
977
+ await writeFile(subagentTrackingPath(cwd), `${JSON.stringify({ schemaVersion: 1, sessions: {}, pending_role_intents: [invalidIntent] })}\n`);
978
+ recoverAdaptedRoleBindings(cwd, stateDir, NOW_MS);
979
+ assert.equal(readRoleRoutingMarker(stateDir, { cwd, sessionId: base.session_id, parentThreadId: base.parent_thread_id, nowMs: NOW_MS }), null);
980
+ assert.equal((await readSubagentTrackingState(cwd)).pending_role_intents.length, 1);
981
+ }
982
+ }
983
+ finally {
984
+ await rm(cwd, { recursive: true, force: true });
985
+ }
986
+ });
987
+ });
988
+ //# sourceMappingURL=adapted-role-binding.test.js.map