oh-my-codex 0.20.2 → 0.20.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 (367) hide show
  1. package/Cargo.lock +7 -6
  2. package/Cargo.toml +1 -1
  3. package/README.md +26 -0
  4. package/crates/omx-runtime/Cargo.toml +1 -0
  5. package/crates/omx-runtime/src/main.rs +25 -2
  6. package/crates/omx-runtime/tests/execution.rs +219 -0
  7. package/crates/omx-runtime-core/src/dispatch.rs +96 -10
  8. package/crates/omx-runtime-core/src/engine.rs +839 -29
  9. package/crates/omx-runtime-core/src/lib.rs +8 -0
  10. package/dist/agents/__tests__/definitions.test.js +81 -0
  11. package/dist/agents/__tests__/definitions.test.js.map +1 -1
  12. package/dist/agents/__tests__/native-config.test.js +25 -0
  13. package/dist/agents/__tests__/native-config.test.js.map +1 -1
  14. package/dist/agents/native-config.d.ts +2 -1
  15. package/dist/agents/native-config.d.ts.map +1 -1
  16. package/dist/agents/native-config.js.map +1 -1
  17. package/dist/auth/hotswap.d.ts +6 -0
  18. package/dist/auth/hotswap.d.ts.map +1 -1
  19. package/dist/auth/hotswap.js +85 -15
  20. package/dist/auth/hotswap.js.map +1 -1
  21. package/dist/autopilot/__tests__/ralplan-gate.test.js +1 -1
  22. package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -1
  23. package/dist/cli/__tests__/auth.test.js +60 -6
  24. package/dist/cli/__tests__/auth.test.js.map +1 -1
  25. package/dist/cli/__tests__/codex-plugin-layout.test.js +80 -0
  26. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
  27. package/dist/cli/__tests__/doctor-warning-copy.test.js +103 -1
  28. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  29. package/dist/cli/__tests__/exec.test.js +136 -0
  30. package/dist/cli/__tests__/exec.test.js.map +1 -1
  31. package/dist/cli/__tests__/index.test.js +258 -4
  32. package/dist/cli/__tests__/index.test.js.map +1 -1
  33. package/dist/cli/__tests__/launch-fallback.test.js +287 -1
  34. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  35. package/dist/cli/__tests__/mcp-parity.test.js +4 -1
  36. package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
  37. package/dist/cli/__tests__/native-hook-claim-journal.test.js +84 -6
  38. package/dist/cli/__tests__/native-hook-claim-journal.test.js.map +1 -1
  39. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +3 -3
  40. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
  41. package/dist/cli/__tests__/ralph.test.js +3 -3
  42. package/dist/cli/__tests__/ralph.test.js.map +1 -1
  43. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js +56 -171
  44. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js.map +1 -1
  45. package/dist/cli/__tests__/ralplan.test.js +37 -111
  46. package/dist/cli/__tests__/ralplan.test.js.map +1 -1
  47. package/dist/cli/__tests__/resume.test.js +11 -10
  48. package/dist/cli/__tests__/resume.test.js.map +1 -1
  49. package/dist/cli/__tests__/session-search-help.test.js +25 -1
  50. package/dist/cli/__tests__/session-search-help.test.js.map +1 -1
  51. package/dist/cli/__tests__/setup-install-mode.test.js +42 -4
  52. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  53. package/dist/cli/__tests__/team.test.js +105 -7
  54. package/dist/cli/__tests__/team.test.js.map +1 -1
  55. package/dist/cli/__tests__/uninstall.test.js +46 -0
  56. package/dist/cli/__tests__/uninstall.test.js.map +1 -1
  57. package/dist/cli/__tests__/windows-popup-loop-contract.test.js +2 -1
  58. package/dist/cli/__tests__/windows-popup-loop-contract.test.js.map +1 -1
  59. package/dist/cli/doctor.d.ts +3 -0
  60. package/dist/cli/doctor.d.ts.map +1 -1
  61. package/dist/cli/doctor.js +15 -2
  62. package/dist/cli/doctor.js.map +1 -1
  63. package/dist/cli/index.d.ts +4 -1
  64. package/dist/cli/index.d.ts.map +1 -1
  65. package/dist/cli/index.js +138 -38
  66. package/dist/cli/index.js.map +1 -1
  67. package/dist/cli/mcp-parity.js +2 -2
  68. package/dist/cli/mcp-parity.js.map +1 -1
  69. package/dist/cli/native-hook-claim-journal.d.ts +16 -5
  70. package/dist/cli/native-hook-claim-journal.d.ts.map +1 -1
  71. package/dist/cli/native-hook-claim-journal.js +42 -29
  72. package/dist/cli/native-hook-claim-journal.js.map +1 -1
  73. package/dist/cli/ralph.js +2 -2
  74. package/dist/cli/ralph.js.map +1 -1
  75. package/dist/cli/ralplan.d.ts +4 -8
  76. package/dist/cli/ralplan.d.ts.map +1 -1
  77. package/dist/cli/ralplan.js +19 -112
  78. package/dist/cli/ralplan.js.map +1 -1
  79. package/dist/cli/session-search.d.ts +5 -0
  80. package/dist/cli/session-search.d.ts.map +1 -1
  81. package/dist/cli/session-search.js +87 -0
  82. package/dist/cli/session-search.js.map +1 -1
  83. package/dist/cli/setup.d.ts +5 -0
  84. package/dist/cli/setup.d.ts.map +1 -1
  85. package/dist/cli/setup.js +80 -31
  86. package/dist/cli/setup.js.map +1 -1
  87. package/dist/cli/uninstall.d.ts +9 -0
  88. package/dist/cli/uninstall.d.ts.map +1 -1
  89. package/dist/cli/uninstall.js +61 -19
  90. package/dist/cli/uninstall.js.map +1 -1
  91. package/dist/compat/__tests__/rust-runtime-compat.test.js +1 -1
  92. package/dist/config/__tests__/generator-idempotent.test.js +330 -1
  93. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  94. package/dist/config/__tests__/generator-notify.test.js +2 -2
  95. package/dist/config/__tests__/generator-notify.test.js.map +1 -1
  96. package/dist/config/__tests__/models.test.js +67 -3
  97. package/dist/config/__tests__/models.test.js.map +1 -1
  98. package/dist/config/generator.d.ts +4 -20
  99. package/dist/config/generator.d.ts.map +1 -1
  100. package/dist/config/generator.js +499 -119
  101. package/dist/config/generator.js.map +1 -1
  102. package/dist/config/models.d.ts +12 -2
  103. package/dist/config/models.d.ts.map +1 -1
  104. package/dist/config/models.js +15 -1
  105. package/dist/config/models.js.map +1 -1
  106. package/dist/hooks/__tests__/agents-overlay.test.js +2 -2
  107. package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
  108. package/dist/hooks/__tests__/keyword-detector.test.js +4 -4
  109. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  110. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +238 -543
  111. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  112. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js +38 -4
  113. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
  114. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +96 -9
  115. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  116. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +531 -12
  117. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
  118. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +155 -23
  119. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  120. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js +482 -32
  121. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
  122. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +68 -1
  123. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  124. package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js +7 -1
  125. package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js.map +1 -1
  126. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +43 -4
  127. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
  128. package/dist/hooks/__tests__/session.test.js +348 -2
  129. package/dist/hooks/__tests__/session.test.js.map +1 -1
  130. package/dist/hooks/agents-overlay.js +1 -1
  131. package/dist/hooks/session.d.ts +26 -2
  132. package/dist/hooks/session.d.ts.map +1 -1
  133. package/dist/hooks/session.js +169 -13
  134. package/dist/hooks/session.js.map +1 -1
  135. package/dist/leader/__tests__/contract.test.js +57 -7
  136. package/dist/leader/__tests__/contract.test.js.map +1 -1
  137. package/dist/leader/contract.d.ts +16 -0
  138. package/dist/leader/contract.d.ts.map +1 -1
  139. package/dist/leader/contract.js +79 -4
  140. package/dist/leader/contract.js.map +1 -1
  141. package/dist/mcp/__tests__/code-intel-server.test.js +31 -3
  142. package/dist/mcp/__tests__/code-intel-server.test.js.map +1 -1
  143. package/dist/mcp/__tests__/wiki-server.test.js +18 -0
  144. package/dist/mcp/__tests__/wiki-server.test.js.map +1 -1
  145. package/dist/mcp/code-intel-server.d.ts.map +1 -1
  146. package/dist/mcp/code-intel-server.js +13 -3
  147. package/dist/mcp/code-intel-server.js.map +1 -1
  148. package/dist/mcp/wiki-server.d.ts +11 -0
  149. package/dist/mcp/wiki-server.d.ts.map +1 -1
  150. package/dist/mcp/wiki-server.js +9 -2
  151. package/dist/mcp/wiki-server.js.map +1 -1
  152. package/dist/modes/__tests__/base-tmux-pane.test.js +4 -4
  153. package/dist/modes/__tests__/base-tmux-pane.test.js.map +1 -1
  154. package/dist/notifications/temp-contract.d.ts.map +1 -1
  155. package/dist/notifications/temp-contract.js +4 -0
  156. package/dist/notifications/temp-contract.js.map +1 -1
  157. package/dist/pipeline/__tests__/orchestrator.test.js +4 -4
  158. package/dist/pipeline/__tests__/orchestrator.test.js.map +1 -1
  159. package/dist/pipeline/__tests__/stages.test.js +8 -8
  160. package/dist/pipeline/__tests__/stages.test.js.map +1 -1
  161. package/dist/ralplan/__tests__/consensus-gate.test.js +134 -10
  162. package/dist/ralplan/__tests__/consensus-gate.test.js.map +1 -1
  163. package/dist/ralplan/__tests__/documented-leader-preflight.test.d.ts +2 -0
  164. package/dist/ralplan/__tests__/documented-leader-preflight.test.d.ts.map +1 -0
  165. package/dist/ralplan/__tests__/documented-leader-preflight.test.js +46 -0
  166. package/dist/ralplan/__tests__/documented-leader-preflight.test.js.map +1 -0
  167. package/dist/ralplan/__tests__/runtime.test.js +8 -4
  168. package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
  169. package/dist/ralplan/consensus-gate.d.ts.map +1 -1
  170. package/dist/ralplan/consensus-gate.js +58 -50
  171. package/dist/ralplan/consensus-gate.js.map +1 -1
  172. package/dist/ralplan/documented-leader-preflight.d.ts +34 -0
  173. package/dist/ralplan/documented-leader-preflight.d.ts.map +1 -0
  174. package/dist/ralplan/documented-leader-preflight.js +58 -0
  175. package/dist/ralplan/documented-leader-preflight.js.map +1 -0
  176. package/dist/ralplan/runtime.d.ts +1 -0
  177. package/dist/ralplan/runtime.d.ts.map +1 -1
  178. package/dist/ralplan/runtime.js +11 -7
  179. package/dist/ralplan/runtime.js.map +1 -1
  180. package/dist/runtime/__tests__/bridge.test.js +42 -1
  181. package/dist/runtime/__tests__/bridge.test.js.map +1 -1
  182. package/dist/runtime/bridge.d.ts +12 -0
  183. package/dist/runtime/bridge.d.ts.map +1 -1
  184. package/dist/runtime/bridge.js +52 -1
  185. package/dist/runtime/bridge.js.map +1 -1
  186. package/dist/scripts/__tests__/codex-native-hook.test.js +2620 -487
  187. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  188. package/dist/scripts/__tests__/reasoning-artifact-contract.test.d.ts +2 -0
  189. package/dist/scripts/__tests__/reasoning-artifact-contract.test.d.ts.map +1 -0
  190. package/dist/scripts/__tests__/reasoning-artifact-contract.test.js +108 -0
  191. package/dist/scripts/__tests__/reasoning-artifact-contract.test.js.map +1 -0
  192. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js +46 -173
  193. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js.map +1 -1
  194. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js +39 -159
  195. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js.map +1 -1
  196. package/dist/scripts/__tests__/smoke-packed-install.test.js +254 -2
  197. package/dist/scripts/__tests__/smoke-packed-install.test.js.map +1 -1
  198. package/dist/scripts/codex-native-hook.d.ts +4 -1
  199. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  200. package/dist/scripts/codex-native-hook.js +11013 -1153
  201. package/dist/scripts/codex-native-hook.js.map +1 -1
  202. package/dist/scripts/notify-fallback-watcher.js +82 -108
  203. package/dist/scripts/notify-fallback-watcher.js.map +1 -1
  204. package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
  205. package/dist/scripts/notify-hook/auto-nudge.js +74 -3
  206. package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
  207. package/dist/scripts/notify-hook/team-dispatch.d.ts +9 -0
  208. package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
  209. package/dist/scripts/notify-hook/team-dispatch.js +234 -79
  210. package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
  211. package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
  212. package/dist/scripts/notify-hook/team-leader-nudge.js +39 -13
  213. package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
  214. package/dist/scripts/notify-hook/team-tmux-guard.d.ts +12 -4
  215. package/dist/scripts/notify-hook/team-tmux-guard.d.ts.map +1 -1
  216. package/dist/scripts/notify-hook/team-tmux-guard.js +365 -44
  217. package/dist/scripts/notify-hook/team-tmux-guard.js.map +1 -1
  218. package/dist/scripts/notify-hook/team-worker-stop.d.ts.map +1 -1
  219. package/dist/scripts/notify-hook/team-worker-stop.js +19 -18
  220. package/dist/scripts/notify-hook/team-worker-stop.js.map +1 -1
  221. package/dist/scripts/notify-hook/team-worker.d.ts +3 -0
  222. package/dist/scripts/notify-hook/team-worker.d.ts.map +1 -1
  223. package/dist/scripts/notify-hook/team-worker.js +37 -28
  224. package/dist/scripts/notify-hook/team-worker.js.map +1 -1
  225. package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
  226. package/dist/scripts/notify-hook/tmux-injection.js +28 -2
  227. package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
  228. package/dist/scripts/notify-hook.js +1 -0
  229. package/dist/scripts/notify-hook.js.map +1 -1
  230. package/dist/scripts/smoke-packed-install.d.ts +48 -3
  231. package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
  232. package/dist/scripts/smoke-packed-install.js +2796 -13
  233. package/dist/scripts/smoke-packed-install.js.map +1 -1
  234. package/dist/state/__tests__/mode-state-context.test.js +54 -0
  235. package/dist/state/__tests__/mode-state-context.test.js.map +1 -1
  236. package/dist/state/__tests__/operations.test.js +2 -0
  237. package/dist/state/__tests__/operations.test.js.map +1 -1
  238. package/dist/state/mode-state-context.d.ts +5 -0
  239. package/dist/state/mode-state-context.d.ts.map +1 -1
  240. package/dist/state/mode-state-context.js +59 -0
  241. package/dist/state/mode-state-context.js.map +1 -1
  242. package/dist/subagents/__tests__/leader-bootstrap-3181.test.js +24 -245
  243. package/dist/subagents/__tests__/leader-bootstrap-3181.test.js.map +1 -1
  244. package/dist/subagents/tracker.d.ts +1 -82
  245. package/dist/subagents/tracker.d.ts.map +1 -1
  246. package/dist/subagents/tracker.js +3 -275
  247. package/dist/subagents/tracker.js.map +1 -1
  248. package/dist/team/__tests__/api-interop.test.js +91 -1
  249. package/dist/team/__tests__/api-interop.test.js.map +1 -1
  250. package/dist/team/__tests__/delivery-e2e-smoke.test.js +52 -10
  251. package/dist/team/__tests__/delivery-e2e-smoke.test.js.map +1 -1
  252. package/dist/team/__tests__/idle-nudge.test.js +45 -15
  253. package/dist/team/__tests__/idle-nudge.test.js.map +1 -1
  254. package/dist/team/__tests__/mcp-comm.test.js +93 -27
  255. package/dist/team/__tests__/mcp-comm.test.js.map +1 -1
  256. package/dist/team/__tests__/model-contract.test.js +110 -3
  257. package/dist/team/__tests__/model-contract.test.js.map +1 -1
  258. package/dist/team/__tests__/runtime-cli.test.js +9 -0
  259. package/dist/team/__tests__/runtime-cli.test.js.map +1 -1
  260. package/dist/team/__tests__/runtime.test.js +3204 -335
  261. package/dist/team/__tests__/runtime.test.js.map +1 -1
  262. package/dist/team/__tests__/scaling.test.js +2359 -142
  263. package/dist/team/__tests__/scaling.test.js.map +1 -1
  264. package/dist/team/__tests__/state.test.js +804 -36
  265. package/dist/team/__tests__/state.test.js.map +1 -1
  266. package/dist/team/__tests__/team-ops-contract.test.js +6 -0
  267. package/dist/team/__tests__/team-ops-contract.test.js.map +1 -1
  268. package/dist/team/__tests__/tmux-session.test.js +3049 -784
  269. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  270. package/dist/team/__tests__/worker-runtime-identity.test.js +38 -2
  271. package/dist/team/__tests__/worker-runtime-identity.test.js.map +1 -1
  272. package/dist/team/__tests__/worktree.test.js +6 -0
  273. package/dist/team/__tests__/worktree.test.js.map +1 -1
  274. package/dist/team/api-interop.d.ts.map +1 -1
  275. package/dist/team/api-interop.js +15 -9
  276. package/dist/team/api-interop.js.map +1 -1
  277. package/dist/team/exact-pane.d.ts +23 -0
  278. package/dist/team/exact-pane.d.ts.map +1 -0
  279. package/dist/team/exact-pane.js +133 -0
  280. package/dist/team/exact-pane.js.map +1 -0
  281. package/dist/team/idle-nudge.d.ts +12 -1
  282. package/dist/team/idle-nudge.d.ts.map +1 -1
  283. package/dist/team/idle-nudge.js +41 -14
  284. package/dist/team/idle-nudge.js.map +1 -1
  285. package/dist/team/model-contract.d.ts +2 -5
  286. package/dist/team/model-contract.d.ts.map +1 -1
  287. package/dist/team/model-contract.js +59 -17
  288. package/dist/team/model-contract.js.map +1 -1
  289. package/dist/team/runtime.d.ts +1 -0
  290. package/dist/team/runtime.d.ts.map +1 -1
  291. package/dist/team/runtime.js +1244 -180
  292. package/dist/team/runtime.js.map +1 -1
  293. package/dist/team/scaling.d.ts +13 -1
  294. package/dist/team/scaling.d.ts.map +1 -1
  295. package/dist/team/scaling.js +1430 -384
  296. package/dist/team/scaling.js.map +1 -1
  297. package/dist/team/state/dispatch.d.ts.map +1 -1
  298. package/dist/team/state/dispatch.js +26 -8
  299. package/dist/team/state/dispatch.js.map +1 -1
  300. package/dist/team/state/locks.d.ts.map +1 -1
  301. package/dist/team/state/locks.js +61 -13
  302. package/dist/team/state/locks.js.map +1 -1
  303. package/dist/team/state/tasks.d.ts.map +1 -1
  304. package/dist/team/state/tasks.js +4 -3
  305. package/dist/team/state/tasks.js.map +1 -1
  306. package/dist/team/state.d.ts +67 -1
  307. package/dist/team/state.d.ts.map +1 -1
  308. package/dist/team/state.js +636 -129
  309. package/dist/team/state.js.map +1 -1
  310. package/dist/team/team-ops.d.ts +5 -0
  311. package/dist/team/team-ops.d.ts.map +1 -1
  312. package/dist/team/team-ops.js +4 -0
  313. package/dist/team/team-ops.js.map +1 -1
  314. package/dist/team/tmux-session.d.ts +123 -23
  315. package/dist/team/tmux-session.d.ts.map +1 -1
  316. package/dist/team/tmux-session.js +1250 -291
  317. package/dist/team/tmux-session.js.map +1 -1
  318. package/dist/team/worktree.d.ts.map +1 -1
  319. package/dist/team/worktree.js +4 -0
  320. package/dist/team/worktree.js.map +1 -1
  321. package/dist/utils/__tests__/file-durability.test.d.ts +2 -0
  322. package/dist/utils/__tests__/file-durability.test.d.ts.map +1 -0
  323. package/dist/utils/__tests__/file-durability.test.js +57 -0
  324. package/dist/utils/__tests__/file-durability.test.js.map +1 -0
  325. package/dist/utils/file-durability.d.ts +15 -0
  326. package/dist/utils/file-durability.d.ts.map +1 -0
  327. package/dist/utils/file-durability.js +37 -0
  328. package/dist/utils/file-durability.js.map +1 -0
  329. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js +3 -0
  330. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js.map +1 -1
  331. package/dist/wiki/lint.d.ts +4 -1
  332. package/dist/wiki/lint.d.ts.map +1 -1
  333. package/dist/wiki/lint.js +3 -2
  334. package/dist/wiki/lint.js.map +1 -1
  335. package/package.json +1 -1
  336. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  337. package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +24 -0
  338. package/plugins/oh-my-codex/skills/ralph/SKILL.md +6 -4
  339. package/plugins/oh-my-codex/skills/ralplan/SKILL.md +3 -2
  340. package/plugins/oh-my-codex/skills/team/SKILL.md +13 -9
  341. package/skills/ralph/SKILL.md +6 -4
  342. package/skills/ralplan/SKILL.md +3 -2
  343. package/skills/team/SKILL.md +13 -9
  344. package/src/scripts/__tests__/codex-native-hook.test.ts +2826 -497
  345. package/src/scripts/__tests__/reasoning-artifact-contract.test.ts +129 -0
  346. package/src/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.ts +48 -185
  347. package/src/scripts/__tests__/role-intent-durable-recovery-3181.test.ts +44 -177
  348. package/src/scripts/__tests__/smoke-packed-install.test.ts +320 -2
  349. package/src/scripts/codex-native-hook.ts +10715 -1069
  350. package/src/scripts/notify-fallback-watcher.ts +84 -115
  351. package/src/scripts/notify-hook/auto-nudge.ts +71 -3
  352. package/src/scripts/notify-hook/team-dispatch.ts +242 -75
  353. package/src/scripts/notify-hook/team-leader-nudge.ts +40 -18
  354. package/src/scripts/notify-hook/team-tmux-guard.ts +366 -39
  355. package/src/scripts/notify-hook/team-worker-stop.ts +20 -16
  356. package/src/scripts/notify-hook/team-worker.ts +39 -26
  357. package/src/scripts/notify-hook/tmux-injection.ts +28 -2
  358. package/src/scripts/notify-hook.ts +1 -0
  359. package/src/scripts/smoke-packed-install.ts +3196 -14
  360. package/dist/subagents/__tests__/adapted-role-binding.test.d.ts +0 -2
  361. package/dist/subagents/__tests__/adapted-role-binding.test.d.ts.map +0 -1
  362. package/dist/subagents/__tests__/adapted-role-binding.test.js +0 -988
  363. package/dist/subagents/__tests__/adapted-role-binding.test.js.map +0 -1
  364. package/dist/subagents/adapted-role-binding.d.ts +0 -17
  365. package/dist/subagents/adapted-role-binding.d.ts.map +0 -1
  366. package/dist/subagents/adapted-role-binding.js +0 -75
  367. package/dist/subagents/adapted-role-binding.js.map +0 -1
@@ -1,988 +0,0 @@
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