oh-my-codex 0.20.1 → 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 (791) hide show
  1. package/Cargo.lock +7 -6
  2. package/Cargo.toml +1 -1
  3. package/README.md +36 -1
  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/adapt/__tests__/hermes.test.js.map +1 -1
  11. package/dist/adapt/contracts.d.ts.map +1 -1
  12. package/dist/agents/__tests__/definitions.test.js +81 -0
  13. package/dist/agents/__tests__/definitions.test.js.map +1 -1
  14. package/dist/agents/__tests__/native-config.test.js +25 -0
  15. package/dist/agents/__tests__/native-config.test.js.map +1 -1
  16. package/dist/agents/native-config.d.ts +2 -1
  17. package/dist/agents/native-config.d.ts.map +1 -1
  18. package/dist/agents/native-config.js.map +1 -1
  19. package/dist/auth/__tests__/hotswap.test.d.ts +2 -0
  20. package/dist/auth/__tests__/hotswap.test.d.ts.map +1 -0
  21. package/dist/auth/__tests__/hotswap.test.js +183 -0
  22. package/dist/auth/__tests__/hotswap.test.js.map +1 -0
  23. package/dist/auth/hotswap.d.ts +6 -0
  24. package/dist/auth/hotswap.d.ts.map +1 -1
  25. package/dist/auth/hotswap.js +102 -19
  26. package/dist/auth/hotswap.js.map +1 -1
  27. package/dist/auth/paths.js.map +1 -1
  28. package/dist/auth/storage.js.map +1 -1
  29. package/dist/autopilot/__tests__/ralplan-gate.test.js +5 -5
  30. package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -1
  31. package/dist/autopilot/completion-gate.js.map +1 -1
  32. package/dist/autopilot/fsm.d.ts +1 -1
  33. package/dist/autopilot/fsm.d.ts.map +1 -1
  34. package/dist/autoresearch/goal.js.map +1 -1
  35. package/dist/autoresearch/runtime.js.map +1 -1
  36. package/dist/capabilities/lockfile.js.map +1 -1
  37. package/dist/catalog/reader.js.map +1 -1
  38. package/dist/catalog/skill-mirror.js.map +1 -1
  39. package/dist/cli/__tests__/adapt-help.test.js.map +1 -1
  40. package/dist/cli/__tests__/agents-init.test.js.map +1 -1
  41. package/dist/cli/__tests__/agents.test.js.map +1 -1
  42. package/dist/cli/__tests__/api.test.js.map +1 -1
  43. package/dist/cli/__tests__/ask.test.js.map +1 -1
  44. package/dist/cli/__tests__/auth.test.js +60 -6
  45. package/dist/cli/__tests__/auth.test.js.map +1 -1
  46. package/dist/cli/__tests__/autoresearch-guided.test.js.map +1 -1
  47. package/dist/cli/__tests__/autoresearch.test.js.map +1 -1
  48. package/dist/cli/__tests__/capabilities.test.js.map +1 -1
  49. package/dist/cli/__tests__/codex-plugin-layout.test.js +80 -0
  50. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
  51. package/dist/cli/__tests__/doctor-context-window-warning.test.js.map +1 -1
  52. package/dist/cli/__tests__/doctor-invalid-config.test.js +47 -4
  53. package/dist/cli/__tests__/doctor-invalid-config.test.js.map +1 -1
  54. package/dist/cli/__tests__/doctor-team.test.js.map +1 -1
  55. package/dist/cli/__tests__/doctor-warning-copy.test.js +752 -15
  56. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  57. package/dist/cli/__tests__/exec.test.js +136 -0
  58. package/dist/cli/__tests__/exec.test.js.map +1 -1
  59. package/dist/cli/__tests__/imagegen-continuation.test.js.map +1 -1
  60. package/dist/cli/__tests__/index.test.js +473 -14
  61. package/dist/cli/__tests__/index.test.js.map +1 -1
  62. package/dist/cli/__tests__/launch-fallback.test.js +336 -4
  63. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  64. package/dist/cli/__tests__/mcp-parity.test.js +87 -12
  65. package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
  66. package/dist/cli/__tests__/native-hook-claim-journal.test.d.ts +2 -0
  67. package/dist/cli/__tests__/native-hook-claim-journal.test.d.ts.map +1 -0
  68. package/dist/cli/__tests__/native-hook-claim-journal.test.js +206 -0
  69. package/dist/cli/__tests__/native-hook-claim-journal.test.js.map +1 -0
  70. package/dist/cli/__tests__/nested-help-routing.test.js.map +1 -1
  71. package/dist/cli/__tests__/question.test.js.map +1 -1
  72. package/dist/cli/__tests__/ralph-deslop-contract.test.js.map +1 -1
  73. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +6 -0
  74. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
  75. package/dist/cli/__tests__/ralph-prd-deep-interview.test.js.map +1 -1
  76. package/dist/cli/__tests__/ralph-prd-smoke.test.js.map +1 -1
  77. package/dist/cli/__tests__/ralph.test.js +8 -3
  78. package/dist/cli/__tests__/ralph.test.js.map +1 -1
  79. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.d.ts +2 -0
  80. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.d.ts.map +1 -0
  81. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js +72 -0
  82. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js.map +1 -0
  83. package/dist/cli/__tests__/ralplan.test.d.ts +2 -0
  84. package/dist/cli/__tests__/ralplan.test.d.ts.map +1 -0
  85. package/dist/cli/__tests__/ralplan.test.js +55 -0
  86. package/dist/cli/__tests__/ralplan.test.js.map +1 -0
  87. package/dist/cli/__tests__/resume.test.js +17 -11
  88. package/dist/cli/__tests__/resume.test.js.map +1 -1
  89. package/dist/cli/__tests__/session-scoped-runtime.test.js +25 -0
  90. package/dist/cli/__tests__/session-scoped-runtime.test.js.map +1 -1
  91. package/dist/cli/__tests__/session-search-help.test.js +25 -1
  92. package/dist/cli/__tests__/session-search-help.test.js.map +1 -1
  93. package/dist/cli/__tests__/session-search.test.js.map +1 -1
  94. package/dist/cli/__tests__/setup-agents-overwrite.test.js +79 -0
  95. package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
  96. package/dist/cli/__tests__/setup-gh-star.test.js.map +1 -1
  97. package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js +77 -3
  98. package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js.map +1 -1
  99. package/dist/cli/__tests__/setup-hooks-trust-e2e.test.d.ts +2 -0
  100. package/dist/cli/__tests__/setup-hooks-trust-e2e.test.d.ts.map +1 -0
  101. package/dist/cli/__tests__/setup-hooks-trust-e2e.test.js +285 -0
  102. package/dist/cli/__tests__/setup-hooks-trust-e2e.test.js.map +1 -0
  103. package/dist/cli/__tests__/setup-install-mode.test.js +2579 -294
  104. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  105. package/dist/cli/__tests__/setup-preferences.test.d.ts +2 -0
  106. package/dist/cli/__tests__/setup-preferences.test.d.ts.map +1 -0
  107. package/dist/cli/__tests__/setup-preferences.test.js +79 -0
  108. package/dist/cli/__tests__/setup-preferences.test.js.map +1 -0
  109. package/dist/cli/__tests__/setup-refresh.test.js +33 -2
  110. package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
  111. package/dist/cli/__tests__/setup-scope.test.js +46 -3
  112. package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
  113. package/dist/cli/__tests__/sparkshell-cli.test.js.map +1 -1
  114. package/dist/cli/__tests__/state.test.js +37 -0
  115. package/dist/cli/__tests__/state.test.js.map +1 -1
  116. package/dist/cli/__tests__/team.test.js +105 -7
  117. package/dist/cli/__tests__/team.test.js.map +1 -1
  118. package/dist/cli/__tests__/ultragoal.test.js +61 -1
  119. package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
  120. package/dist/cli/__tests__/uninstall.test.js +1817 -24
  121. package/dist/cli/__tests__/uninstall.test.js.map +1 -1
  122. package/dist/cli/__tests__/update.test.js +58 -0
  123. package/dist/cli/__tests__/update.test.js.map +1 -1
  124. package/dist/cli/__tests__/url.test.js.map +1 -1
  125. package/dist/cli/__tests__/windows-popup-loop-contract.test.js +2 -1
  126. package/dist/cli/__tests__/windows-popup-loop-contract.test.js.map +1 -1
  127. package/dist/cli/adapt.js.map +1 -1
  128. package/dist/cli/agents-init.js.map +1 -1
  129. package/dist/cli/agents.js.map +1 -1
  130. package/dist/cli/api.js.map +1 -1
  131. package/dist/cli/ask.d.ts +1 -1
  132. package/dist/cli/ask.d.ts.map +1 -1
  133. package/dist/cli/ask.js.map +1 -1
  134. package/dist/cli/auth.js.map +1 -1
  135. package/dist/cli/autoresearch-guided.js.map +1 -1
  136. package/dist/cli/autoresearch-intake.js.map +1 -1
  137. package/dist/cli/capabilities.js.map +1 -1
  138. package/dist/cli/cleanup.js.map +1 -1
  139. package/dist/cli/codex-feature-probe.js.map +1 -1
  140. package/dist/cli/codex-home.js.map +1 -1
  141. package/dist/cli/doctor.d.ts +21 -0
  142. package/dist/cli/doctor.d.ts.map +1 -1
  143. package/dist/cli/doctor.js +607 -130
  144. package/dist/cli/doctor.js.map +1 -1
  145. package/dist/cli/explore.js.map +1 -1
  146. package/dist/cli/index.d.ts +14 -3
  147. package/dist/cli/index.d.ts.map +1 -1
  148. package/dist/cli/index.js +245 -57
  149. package/dist/cli/index.js.map +1 -1
  150. package/dist/cli/mcp-parity.js +2 -2
  151. package/dist/cli/mcp-parity.js.map +1 -1
  152. package/dist/cli/mcp-serve.js.map +1 -1
  153. package/dist/cli/mission.js.map +1 -1
  154. package/dist/cli/native-assets.js.map +1 -1
  155. package/dist/cli/native-hook-claim-journal.d.ts +29 -0
  156. package/dist/cli/native-hook-claim-journal.d.ts.map +1 -0
  157. package/dist/cli/native-hook-claim-journal.js +252 -0
  158. package/dist/cli/native-hook-claim-journal.js.map +1 -0
  159. package/dist/cli/omx.js.map +1 -1
  160. package/dist/cli/plugin-marketplace.js.map +1 -1
  161. package/dist/cli/project-runtime-codex-homes.js.map +1 -1
  162. package/dist/cli/question.js.map +1 -1
  163. package/dist/cli/ralph.d.ts.map +1 -1
  164. package/dist/cli/ralph.js +3 -2
  165. package/dist/cli/ralph.js.map +1 -1
  166. package/dist/cli/ralplan.d.ts +10 -0
  167. package/dist/cli/ralplan.d.ts.map +1 -0
  168. package/dist/cli/ralplan.js +110 -0
  169. package/dist/cli/ralplan.js.map +1 -0
  170. package/dist/cli/session-search.d.ts +5 -0
  171. package/dist/cli/session-search.d.ts.map +1 -1
  172. package/dist/cli/session-search.js +87 -0
  173. package/dist/cli/session-search.js.map +1 -1
  174. package/dist/cli/setup-preferences.d.ts +11 -0
  175. package/dist/cli/setup-preferences.d.ts.map +1 -1
  176. package/dist/cli/setup-preferences.js +32 -0
  177. package/dist/cli/setup-preferences.js.map +1 -1
  178. package/dist/cli/setup.d.ts +58 -0
  179. package/dist/cli/setup.d.ts.map +1 -1
  180. package/dist/cli/setup.js +1450 -398
  181. package/dist/cli/setup.js.map +1 -1
  182. package/dist/cli/sparkshell.js.map +1 -1
  183. package/dist/cli/star-prompt.js.map +1 -1
  184. package/dist/cli/state.d.ts.map +1 -1
  185. package/dist/cli/state.js +2 -1
  186. package/dist/cli/state.js.map +1 -1
  187. package/dist/cli/team.js.map +1 -1
  188. package/dist/cli/ultragoal.d.ts +5 -1
  189. package/dist/cli/ultragoal.d.ts.map +1 -1
  190. package/dist/cli/ultragoal.js +34 -2
  191. package/dist/cli/ultragoal.js.map +1 -1
  192. package/dist/cli/uninstall.d.ts +17 -0
  193. package/dist/cli/uninstall.d.ts.map +1 -1
  194. package/dist/cli/uninstall.js +986 -95
  195. package/dist/cli/uninstall.js.map +1 -1
  196. package/dist/cli/update.d.ts.map +1 -1
  197. package/dist/cli/update.js +8 -1
  198. package/dist/cli/update.js.map +1 -1
  199. package/dist/cli/url.js.map +1 -1
  200. package/dist/compat/__tests__/doctor-contract.test.js +5 -1
  201. package/dist/compat/__tests__/doctor-contract.test.js.map +1 -1
  202. package/dist/compat/__tests__/rust-runtime-compat.test.js +1 -1
  203. package/dist/compat/__tests__/rust-runtime-compat.test.js.map +1 -1
  204. package/dist/config/__tests__/codex-hooks.test.js +1287 -11
  205. package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
  206. package/dist/config/__tests__/generator-idempotent.test.js +1179 -32
  207. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  208. package/dist/config/__tests__/generator-notify.test.js +6 -1
  209. package/dist/config/__tests__/generator-notify.test.js.map +1 -1
  210. package/dist/config/__tests__/models.test.js +67 -3
  211. package/dist/config/__tests__/models.test.js.map +1 -1
  212. package/dist/config/codex-feature-flags.d.ts.map +1 -1
  213. package/dist/config/codex-feature-flags.js.map +1 -1
  214. package/dist/config/codex-hooks.d.ts +124 -1
  215. package/dist/config/codex-hooks.d.ts.map +1 -1
  216. package/dist/config/codex-hooks.js +2142 -265
  217. package/dist/config/codex-hooks.js.map +1 -1
  218. package/dist/config/commit-lore-guard.js.map +1 -1
  219. package/dist/config/generator.d.ts +23 -34
  220. package/dist/config/generator.d.ts.map +1 -1
  221. package/dist/config/generator.js +1480 -246
  222. package/dist/config/generator.js.map +1 -1
  223. package/dist/config/mcp-registry.js.map +1 -1
  224. package/dist/config/models.d.ts +14 -4
  225. package/dist/config/models.d.ts.map +1 -1
  226. package/dist/config/models.js +15 -1
  227. package/dist/config/models.js.map +1 -1
  228. package/dist/config/omx-first-party-mcp.js.map +1 -1
  229. package/dist/config/team-mode.d.ts.map +1 -1
  230. package/dist/document-refresh/__tests__/enforcer.test.js.map +1 -1
  231. package/dist/document-refresh/enforcer.js.map +1 -1
  232. package/dist/exec/followup.js.map +1 -1
  233. package/dist/goal-workflows/artifacts.js.map +1 -1
  234. package/dist/hooks/__tests__/agents-overlay.test.js +7 -4
  235. package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
  236. package/dist/hooks/__tests__/analyze-routing-contract.test.js.map +1 -1
  237. package/dist/hooks/__tests__/analyze-skill-contract.test.js.map +1 -1
  238. package/dist/hooks/__tests__/anti-slop-workflow.test.js.map +1 -1
  239. package/dist/hooks/__tests__/autopilot-skill-contract.test.js +9 -3
  240. package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
  241. package/dist/hooks/__tests__/code-review-skill-contract.test.js.map +1 -1
  242. package/dist/hooks/__tests__/consensus-execution-handoff.test.js.map +1 -1
  243. package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
  244. package/dist/hooks/__tests__/design-skill.test.js.map +1 -1
  245. package/dist/hooks/__tests__/keyword-detector.test.js +1376 -6
  246. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  247. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +252 -543
  248. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  249. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js +38 -4
  250. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
  251. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +765 -10
  252. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  253. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js +2 -0
  254. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js.map +1 -1
  255. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js +142 -1
  256. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js.map +1 -1
  257. package/dist/hooks/__tests__/notify-hook-modules.test.js.map +1 -1
  258. package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.js +48 -2
  259. package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.js.map +1 -1
  260. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js +61 -44
  261. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js.map +1 -1
  262. package/dist/hooks/__tests__/notify-hook-regression-205.test.js.map +1 -1
  263. package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.js +3 -2
  264. package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.js.map +1 -1
  265. package/dist/hooks/__tests__/notify-hook-session-scope.test.js +243 -4
  266. package/dist/hooks/__tests__/notify-hook-session-scope.test.js.map +1 -1
  267. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +531 -12
  268. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
  269. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +163 -24
  270. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  271. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js +482 -32
  272. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
  273. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +68 -1
  274. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  275. package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js +7 -1
  276. package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js.map +1 -1
  277. package/dist/hooks/__tests__/notify-hook-visual-verdict.test.js.map +1 -1
  278. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +43 -4
  279. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
  280. package/dist/hooks/__tests__/openclaw-setup-contract.test.js.map +1 -1
  281. package/dist/hooks/__tests__/pre-context-gate-skills.test.js.map +1 -1
  282. package/dist/hooks/__tests__/prometheus-strict-contract.test.js.map +1 -1
  283. package/dist/hooks/__tests__/prompt-guidance-fragments.test.js.map +1 -1
  284. package/dist/hooks/__tests__/prompt-guidance-test-helpers.js.map +1 -1
  285. package/dist/hooks/__tests__/prompt-session-provenance.test.d.ts +2 -0
  286. package/dist/hooks/__tests__/prompt-session-provenance.test.d.ts.map +1 -0
  287. package/dist/hooks/__tests__/prompt-session-provenance.test.js +82 -0
  288. package/dist/hooks/__tests__/prompt-session-provenance.test.js.map +1 -0
  289. package/dist/hooks/__tests__/session.test.js +878 -11
  290. package/dist/hooks/__tests__/session.test.js.map +1 -1
  291. package/dist/hooks/__tests__/skill-catalog-hygiene.test.js.map +1 -1
  292. package/dist/hooks/__tests__/team-runtime-gating-docs-contract.test.js.map +1 -1
  293. package/dist/hooks/__tests__/visual-ralph-skill.test.js.map +1 -1
  294. package/dist/hooks/__tests__/visual-verdict-loop.test.js.map +1 -1
  295. package/dist/hooks/agents-overlay.js +5 -5
  296. package/dist/hooks/agents-overlay.js.map +1 -1
  297. package/dist/hooks/code-simplifier/index.js.map +1 -1
  298. package/dist/hooks/explore-routing.js.map +1 -1
  299. package/dist/hooks/extensibility/dispatcher.js.map +1 -1
  300. package/dist/hooks/extensibility/events.js.map +1 -1
  301. package/dist/hooks/extensibility/loader.js.map +1 -1
  302. package/dist/hooks/extensibility/plugin-runner-stdin.js.map +1 -1
  303. package/dist/hooks/extensibility/sdk/logging.js.map +1 -1
  304. package/dist/hooks/keyword-detector.d.ts +33 -7
  305. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  306. package/dist/hooks/keyword-detector.js +2228 -119
  307. package/dist/hooks/keyword-detector.js.map +1 -1
  308. package/dist/hooks/keyword-registry.d.ts +12 -0
  309. package/dist/hooks/keyword-registry.d.ts.map +1 -1
  310. package/dist/hooks/keyword-registry.js +41 -0
  311. package/dist/hooks/keyword-registry.js.map +1 -1
  312. package/dist/hooks/prompt-session-provenance.d.ts +87 -0
  313. package/dist/hooks/prompt-session-provenance.d.ts.map +1 -0
  314. package/dist/hooks/prompt-session-provenance.js +180 -0
  315. package/dist/hooks/prompt-session-provenance.js.map +1 -0
  316. package/dist/hooks/session.d.ts +163 -34
  317. package/dist/hooks/session.d.ts.map +1 -1
  318. package/dist/hooks/session.js +1395 -317
  319. package/dist/hooks/session.js.map +1 -1
  320. package/dist/hooks/task-size-detector.js.map +1 -1
  321. package/dist/hud/__tests__/state.test.js.map +1 -1
  322. package/dist/hud/authority.js.map +1 -1
  323. package/dist/hud/constants.js.map +1 -1
  324. package/dist/hud/index.js.map +1 -1
  325. package/dist/hud/reconcile.js.map +1 -1
  326. package/dist/hud/render.js.map +1 -1
  327. package/dist/hud/state.js.map +1 -1
  328. package/dist/hud/tmux.js.map +1 -1
  329. package/dist/leader/__tests__/contract.test.js +153 -1
  330. package/dist/leader/__tests__/contract.test.js.map +1 -1
  331. package/dist/leader/contract.d.ts +42 -5
  332. package/dist/leader/contract.d.ts.map +1 -1
  333. package/dist/leader/contract.js +184 -3
  334. package/dist/leader/contract.js.map +1 -1
  335. package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
  336. package/dist/mcp/__tests__/code-intel-server.test.js +31 -3
  337. package/dist/mcp/__tests__/code-intel-server.test.js.map +1 -1
  338. package/dist/mcp/__tests__/server-lifecycle.test.js +15 -2
  339. package/dist/mcp/__tests__/server-lifecycle.test.js.map +1 -1
  340. package/dist/mcp/__tests__/state-paths.test.js +98 -1
  341. package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
  342. package/dist/mcp/__tests__/wiki-server.test.js +18 -0
  343. package/dist/mcp/__tests__/wiki-server.test.js.map +1 -1
  344. package/dist/mcp/bootstrap.js.map +1 -1
  345. package/dist/mcp/code-intel-server.d.ts +58 -58
  346. package/dist/mcp/code-intel-server.d.ts.map +1 -1
  347. package/dist/mcp/code-intel-server.js +13 -3
  348. package/dist/mcp/code-intel-server.js.map +1 -1
  349. package/dist/mcp/hermes-bridge.js.map +1 -1
  350. package/dist/mcp/hermes-server.d.ts +70 -70
  351. package/dist/mcp/hermes-server.d.ts.map +1 -1
  352. package/dist/mcp/lifecycle-telemetry.js.map +1 -1
  353. package/dist/mcp/memory-server.d.ts +34 -34
  354. package/dist/mcp/memory-server.d.ts.map +1 -1
  355. package/dist/mcp/memory-server.js.map +1 -1
  356. package/dist/mcp/state-paths.d.ts +14 -0
  357. package/dist/mcp/state-paths.d.ts.map +1 -1
  358. package/dist/mcp/state-paths.js +96 -20
  359. package/dist/mcp/state-paths.js.map +1 -1
  360. package/dist/mcp/state-server.d.ts +47 -47
  361. package/dist/mcp/state-server.d.ts.map +1 -1
  362. package/dist/mcp/trace-server.d.ts +4 -4
  363. package/dist/mcp/trace-server.d.ts.map +1 -1
  364. package/dist/mcp/trace-server.js.map +1 -1
  365. package/dist/mcp/wiki-server.d.ts +34 -23
  366. package/dist/mcp/wiki-server.d.ts.map +1 -1
  367. package/dist/mcp/wiki-server.js +9 -2
  368. package/dist/mcp/wiki-server.js.map +1 -1
  369. package/dist/modes/__tests__/base-session-scope.test.js +53 -1
  370. package/dist/modes/__tests__/base-session-scope.test.js.map +1 -1
  371. package/dist/modes/__tests__/base-tmux-pane.test.js +4 -4
  372. package/dist/modes/__tests__/base-tmux-pane.test.js.map +1 -1
  373. package/dist/modes/base.d.ts.map +1 -1
  374. package/dist/modes/base.js +5 -5
  375. package/dist/modes/base.js.map +1 -1
  376. package/dist/notifications/__tests__/lifecycle-dedupe.test.js.map +1 -1
  377. package/dist/notifications/__tests__/reply-listener.test.js.map +1 -1
  378. package/dist/notifications/__tests__/session-idle-tail-dedupe.test.js +177 -85
  379. package/dist/notifications/__tests__/session-idle-tail-dedupe.test.js.map +1 -1
  380. package/dist/notifications/__tests__/session-registry.test.js.map +1 -1
  381. package/dist/notifications/__tests__/template-engine.test.js.map +1 -1
  382. package/dist/notifications/__tests__/verbosity.test.js.map +1 -1
  383. package/dist/notifications/http-client.js.map +1 -1
  384. package/dist/notifications/index.d.ts +4 -1
  385. package/dist/notifications/index.d.ts.map +1 -1
  386. package/dist/notifications/index.js +3 -9
  387. package/dist/notifications/index.js.map +1 -1
  388. package/dist/notifications/lifecycle-dedupe.js.map +1 -1
  389. package/dist/notifications/reply-listener.d.ts.map +1 -1
  390. package/dist/notifications/reply-listener.js.map +1 -1
  391. package/dist/notifications/session-registry.js.map +1 -1
  392. package/dist/notifications/session-status.js.map +1 -1
  393. package/dist/notifications/temp-contract.d.ts.map +1 -1
  394. package/dist/notifications/temp-contract.js +4 -0
  395. package/dist/notifications/temp-contract.js.map +1 -1
  396. package/dist/notifications/tmux-detector.js.map +1 -1
  397. package/dist/notifications/tmux.js.map +1 -1
  398. package/dist/openclaw/config.js.map +1 -1
  399. package/dist/openclaw/dispatcher.js.map +1 -1
  400. package/dist/pipeline/__tests__/orchestrator.test.js +4 -4
  401. package/dist/pipeline/__tests__/orchestrator.test.js.map +1 -1
  402. package/dist/pipeline/__tests__/stages.test.js +16 -13
  403. package/dist/pipeline/__tests__/stages.test.js.map +1 -1
  404. package/dist/pipeline/stages/code-review.js.map +1 -1
  405. package/dist/pipeline/stages/ralph-verify.js.map +1 -1
  406. package/dist/pipeline/stages/ralplan.js.map +1 -1
  407. package/dist/pipeline/stages/team-exec.js.map +1 -1
  408. package/dist/pipeline/stages/ultragoal.js.map +1 -1
  409. package/dist/pipeline/stages/ultraqa.js.map +1 -1
  410. package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.js.map +1 -1
  411. package/dist/planning/artifact-names.js.map +1 -1
  412. package/dist/planning/artifacts.js.map +1 -1
  413. package/dist/question/__tests__/state.test.js.map +1 -1
  414. package/dist/question/__tests__/ui.test.js.map +1 -1
  415. package/dist/question/autopilot-wait.js.map +1 -1
  416. package/dist/question/client.d.ts.map +1 -1
  417. package/dist/question/client.js.map +1 -1
  418. package/dist/question/deep-interview.js.map +1 -1
  419. package/dist/question/events.js.map +1 -1
  420. package/dist/question/renderer.js.map +1 -1
  421. package/dist/question/state.d.ts.map +1 -1
  422. package/dist/question/state.js.map +1 -1
  423. package/dist/question/ui.js.map +1 -1
  424. package/dist/ralph/contract.d.ts +1 -1
  425. package/dist/ralph/contract.d.ts.map +1 -1
  426. package/dist/ralplan/__tests__/consensus-gate.test.js +527 -8
  427. package/dist/ralplan/__tests__/consensus-gate.test.js.map +1 -1
  428. package/dist/ralplan/__tests__/documented-leader-preflight.test.d.ts +2 -0
  429. package/dist/ralplan/__tests__/documented-leader-preflight.test.d.ts.map +1 -0
  430. package/dist/ralplan/__tests__/documented-leader-preflight.test.js +46 -0
  431. package/dist/ralplan/__tests__/documented-leader-preflight.test.js.map +1 -0
  432. package/dist/ralplan/__tests__/runtime.test.js +116 -14
  433. package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
  434. package/dist/ralplan/consensus-gate.d.ts +3 -3
  435. package/dist/ralplan/consensus-gate.d.ts.map +1 -1
  436. package/dist/ralplan/consensus-gate.js +173 -70
  437. package/dist/ralplan/consensus-gate.js.map +1 -1
  438. package/dist/ralplan/documented-leader-preflight.d.ts +34 -0
  439. package/dist/ralplan/documented-leader-preflight.d.ts.map +1 -0
  440. package/dist/ralplan/documented-leader-preflight.js +58 -0
  441. package/dist/ralplan/documented-leader-preflight.js.map +1 -0
  442. package/dist/ralplan/runtime.d.ts +3 -2
  443. package/dist/ralplan/runtime.d.ts.map +1 -1
  444. package/dist/ralplan/runtime.js +12 -9
  445. package/dist/ralplan/runtime.js.map +1 -1
  446. package/dist/runtime/__tests__/bridge.test.js +42 -1
  447. package/dist/runtime/__tests__/bridge.test.js.map +1 -1
  448. package/dist/runtime/bridge.d.ts +12 -0
  449. package/dist/runtime/bridge.d.ts.map +1 -1
  450. package/dist/runtime/bridge.js +52 -1
  451. package/dist/runtime/bridge.js.map +1 -1
  452. package/dist/runtime/process-tree.js.map +1 -1
  453. package/dist/runtime/run-loop.js.map +1 -1
  454. package/dist/runtime/run-outcome.d.ts +3 -3
  455. package/dist/runtime/run-outcome.d.ts.map +1 -1
  456. package/dist/runtime/run-outcome.js.map +1 -1
  457. package/dist/runtime/run-state.js.map +1 -1
  458. package/dist/scripts/__tests__/codex-native-hook.test.js +6657 -995
  459. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  460. package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.d.ts +2 -0
  461. package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.d.ts.map +1 -0
  462. package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.js +634 -0
  463. package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.js.map +1 -0
  464. package/dist/scripts/__tests__/generate-release-body.test.js.map +1 -1
  465. package/dist/scripts/__tests__/hook-derived-watcher.test.js.map +1 -1
  466. package/dist/scripts/__tests__/notify-dispatcher.test.js.map +1 -1
  467. package/dist/scripts/__tests__/notify-fallback-delivery.test.d.ts +2 -0
  468. package/dist/scripts/__tests__/notify-fallback-delivery.test.d.ts.map +1 -0
  469. package/dist/scripts/__tests__/notify-fallback-delivery.test.js +174 -0
  470. package/dist/scripts/__tests__/notify-fallback-delivery.test.js.map +1 -0
  471. package/dist/scripts/__tests__/notify-state-io.test.js +57 -1
  472. package/dist/scripts/__tests__/notify-state-io.test.js.map +1 -1
  473. package/dist/scripts/__tests__/reasoning-artifact-contract.test.d.ts +2 -0
  474. package/dist/scripts/__tests__/reasoning-artifact-contract.test.d.ts.map +1 -0
  475. package/dist/scripts/__tests__/reasoning-artifact-contract.test.js +108 -0
  476. package/dist/scripts/__tests__/reasoning-artifact-contract.test.js.map +1 -0
  477. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.d.ts +2 -0
  478. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.d.ts.map +1 -0
  479. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js +78 -0
  480. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js.map +1 -0
  481. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.d.ts +2 -0
  482. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.d.ts.map +1 -0
  483. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js +55 -0
  484. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js.map +1 -0
  485. package/dist/scripts/__tests__/run-test-files.test.js.map +1 -1
  486. package/dist/scripts/__tests__/smoke-packed-install.test.js +1100 -4
  487. package/dist/scripts/__tests__/smoke-packed-install.test.js.map +1 -1
  488. package/dist/scripts/__tests__/test-reply-listener-live.test.js.map +1 -1
  489. package/dist/scripts/build-api.js.map +1 -1
  490. package/dist/scripts/build-explore-harness.js.map +1 -1
  491. package/dist/scripts/build-sparkshell.js.map +1 -1
  492. package/dist/scripts/cleanup-explore-harness.js.map +1 -1
  493. package/dist/scripts/codex-execution-surface.js.map +1 -1
  494. package/dist/scripts/codex-native-hook.d.ts +4 -1
  495. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  496. package/dist/scripts/codex-native-hook.js +11577 -1184
  497. package/dist/scripts/codex-native-hook.js.map +1 -1
  498. package/dist/scripts/generate-native-release-manifest.js.map +1 -1
  499. package/dist/scripts/generate-release-body.js.map +1 -1
  500. package/dist/scripts/hook-payload-guard.d.ts.map +1 -1
  501. package/dist/scripts/hook-payload-guard.js.map +1 -1
  502. package/dist/scripts/notify-fallback-delivery.d.ts +48 -0
  503. package/dist/scripts/notify-fallback-delivery.d.ts.map +1 -0
  504. package/dist/scripts/notify-fallback-delivery.js +419 -0
  505. package/dist/scripts/notify-fallback-delivery.js.map +1 -0
  506. package/dist/scripts/notify-fallback-watcher.js +218 -138
  507. package/dist/scripts/notify-fallback-watcher.js.map +1 -1
  508. package/dist/scripts/notify-hook/auto-nudge.d.ts +18 -9
  509. package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
  510. package/dist/scripts/notify-hook/auto-nudge.js +112 -15
  511. package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
  512. package/dist/scripts/notify-hook/managed-tmux.d.ts +23 -0
  513. package/dist/scripts/notify-hook/managed-tmux.d.ts.map +1 -1
  514. package/dist/scripts/notify-hook/managed-tmux.js +122 -12
  515. package/dist/scripts/notify-hook/managed-tmux.js.map +1 -1
  516. package/dist/scripts/notify-hook/orchestration-intent.d.ts +7 -7
  517. package/dist/scripts/notify-hook/orchestration-intent.d.ts.map +1 -1
  518. package/dist/scripts/notify-hook/ralph-session-resume.d.ts +5 -2
  519. package/dist/scripts/notify-hook/ralph-session-resume.d.ts.map +1 -1
  520. package/dist/scripts/notify-hook/ralph-session-resume.js +33 -37
  521. package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -1
  522. package/dist/scripts/notify-hook/state-io.d.ts +17 -0
  523. package/dist/scripts/notify-hook/state-io.d.ts.map +1 -1
  524. package/dist/scripts/notify-hook/state-io.js +73 -11
  525. package/dist/scripts/notify-hook/state-io.js.map +1 -1
  526. package/dist/scripts/notify-hook/team-dispatch.d.ts +10 -1
  527. package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
  528. package/dist/scripts/notify-hook/team-dispatch.js +234 -79
  529. package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
  530. package/dist/scripts/notify-hook/team-leader-nudge.d.ts +2 -2
  531. package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
  532. package/dist/scripts/notify-hook/team-leader-nudge.js +39 -13
  533. package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
  534. package/dist/scripts/notify-hook/team-tmux-guard.d.ts +16 -8
  535. package/dist/scripts/notify-hook/team-tmux-guard.d.ts.map +1 -1
  536. package/dist/scripts/notify-hook/team-tmux-guard.js +365 -44
  537. package/dist/scripts/notify-hook/team-tmux-guard.js.map +1 -1
  538. package/dist/scripts/notify-hook/team-worker-posttooluse.js.map +1 -1
  539. package/dist/scripts/notify-hook/team-worker-stop.d.ts +1 -1
  540. package/dist/scripts/notify-hook/team-worker-stop.d.ts.map +1 -1
  541. package/dist/scripts/notify-hook/team-worker-stop.js +19 -18
  542. package/dist/scripts/notify-hook/team-worker-stop.js.map +1 -1
  543. package/dist/scripts/notify-hook/team-worker.d.ts +5 -2
  544. package/dist/scripts/notify-hook/team-worker.d.ts.map +1 -1
  545. package/dist/scripts/notify-hook/team-worker.js +37 -28
  546. package/dist/scripts/notify-hook/team-worker.js.map +1 -1
  547. package/dist/scripts/notify-hook/tmux-injection.d.ts +8 -1
  548. package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
  549. package/dist/scripts/notify-hook/tmux-injection.js +53 -18
  550. package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
  551. package/dist/scripts/notify-hook/visual-verdict.d.ts +1 -1
  552. package/dist/scripts/notify-hook/visual-verdict.d.ts.map +1 -1
  553. package/dist/scripts/notify-hook/visual-verdict.js +15 -13
  554. package/dist/scripts/notify-hook/visual-verdict.js.map +1 -1
  555. package/dist/scripts/notify-hook.d.ts +24 -1
  556. package/dist/scripts/notify-hook.d.ts.map +1 -1
  557. package/dist/scripts/notify-hook.js +267 -90
  558. package/dist/scripts/notify-hook.js.map +1 -1
  559. package/dist/scripts/postinstall.js.map +1 -1
  560. package/dist/scripts/prompt-inventory.js.map +1 -1
  561. package/dist/scripts/smoke-packed-install.d.ts +927 -5
  562. package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
  563. package/dist/scripts/smoke-packed-install.js +4317 -13
  564. package/dist/scripts/smoke-packed-install.js.map +1 -1
  565. package/dist/scripts/sync-plugin-mirror.js.map +1 -1
  566. package/dist/scripts/test-reply-listener-live.js.map +1 -1
  567. package/dist/scripts/test-sparkshell.js.map +1 -1
  568. package/dist/scripts/verify-native-agents.js.map +1 -1
  569. package/dist/session-history/friction.js.map +1 -1
  570. package/dist/session-history/search.js.map +1 -1
  571. package/dist/sidecar/__tests__/boundary.test.js.map +1 -1
  572. package/dist/sidecar/collector.js.map +1 -1
  573. package/dist/sidecar/index.js.map +1 -1
  574. package/dist/sidecar/render.js.map +1 -1
  575. package/dist/sidecar/tmux.js.map +1 -1
  576. package/dist/state/__tests__/mode-state-context.test.js +54 -0
  577. package/dist/state/__tests__/mode-state-context.test.js.map +1 -1
  578. package/dist/state/__tests__/operations.test.js +66 -15
  579. package/dist/state/__tests__/operations.test.js.map +1 -1
  580. package/dist/state/__tests__/skill-active.test.js +19 -1
  581. package/dist/state/__tests__/skill-active.test.js.map +1 -1
  582. package/dist/state/__tests__/workflow-transition.test.js +61 -0
  583. package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
  584. package/dist/state/mode-state-context.d.ts +5 -0
  585. package/dist/state/mode-state-context.d.ts.map +1 -1
  586. package/dist/state/mode-state-context.js +59 -0
  587. package/dist/state/mode-state-context.js.map +1 -1
  588. package/dist/state/operations.d.ts +1 -1
  589. package/dist/state/operations.d.ts.map +1 -1
  590. package/dist/state/operations.js +49 -55
  591. package/dist/state/operations.js.map +1 -1
  592. package/dist/state/paths.d.ts +1 -1
  593. package/dist/state/paths.d.ts.map +1 -1
  594. package/dist/state/paths.js +1 -1
  595. package/dist/state/paths.js.map +1 -1
  596. package/dist/state/skill-active.d.ts +13 -1
  597. package/dist/state/skill-active.d.ts.map +1 -1
  598. package/dist/state/skill-active.js +43 -4
  599. package/dist/state/skill-active.js.map +1 -1
  600. package/dist/state/terminal-normalization.js.map +1 -1
  601. package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
  602. package/dist/state/workflow-transition-reconcile.js +20 -4
  603. package/dist/state/workflow-transition-reconcile.js.map +1 -1
  604. package/dist/state/workflow-transition.d.ts +1 -1
  605. package/dist/state/workflow-transition.d.ts.map +1 -1
  606. package/dist/state/workflow-transition.js.map +1 -1
  607. package/dist/subagents/__tests__/leader-bootstrap-3181.test.d.ts +2 -0
  608. package/dist/subagents/__tests__/leader-bootstrap-3181.test.d.ts.map +1 -0
  609. package/dist/subagents/__tests__/leader-bootstrap-3181.test.js +52 -0
  610. package/dist/subagents/__tests__/leader-bootstrap-3181.test.js.map +1 -0
  611. package/dist/subagents/__tests__/tracker.test.js +1550 -10
  612. package/dist/subagents/__tests__/tracker.test.js.map +1 -1
  613. package/dist/subagents/role-routing-marker.d.ts +10 -0
  614. package/dist/subagents/role-routing-marker.d.ts.map +1 -0
  615. package/dist/subagents/role-routing-marker.js +136 -0
  616. package/dist/subagents/role-routing-marker.js.map +1 -0
  617. package/dist/subagents/tracker.d.ts +91 -0
  618. package/dist/subagents/tracker.d.ts.map +1 -1
  619. package/dist/subagents/tracker.js +806 -62
  620. package/dist/subagents/tracker.js.map +1 -1
  621. package/dist/team/__tests__/api-interop.test.js +91 -1
  622. package/dist/team/__tests__/api-interop.test.js.map +1 -1
  623. package/dist/team/__tests__/approved-execution.test.js.map +1 -1
  624. package/dist/team/__tests__/delivery-e2e-smoke.test.js +52 -10
  625. package/dist/team/__tests__/delivery-e2e-smoke.test.js.map +1 -1
  626. package/dist/team/__tests__/idle-nudge.test.js +45 -15
  627. package/dist/team/__tests__/idle-nudge.test.js.map +1 -1
  628. package/dist/team/__tests__/mcp-comm.test.js +93 -27
  629. package/dist/team/__tests__/mcp-comm.test.js.map +1 -1
  630. package/dist/team/__tests__/model-contract.test.js +341 -4
  631. package/dist/team/__tests__/model-contract.test.js.map +1 -1
  632. package/dist/team/__tests__/role-router.test.js.map +1 -1
  633. package/dist/team/__tests__/runtime-cli.test.js +9 -0
  634. package/dist/team/__tests__/runtime-cli.test.js.map +1 -1
  635. package/dist/team/__tests__/runtime.test.js +3624 -355
  636. package/dist/team/__tests__/runtime.test.js.map +1 -1
  637. package/dist/team/__tests__/scaling.test.js +2516 -118
  638. package/dist/team/__tests__/scaling.test.js.map +1 -1
  639. package/dist/team/__tests__/state-root.test.js.map +1 -1
  640. package/dist/team/__tests__/state.test.js +804 -36
  641. package/dist/team/__tests__/state.test.js.map +1 -1
  642. package/dist/team/__tests__/team-ops-contract.test.js +6 -0
  643. package/dist/team/__tests__/team-ops-contract.test.js.map +1 -1
  644. package/dist/team/__tests__/tmux-session.test.js +3664 -969
  645. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  646. package/dist/team/__tests__/tmux-test-fixture.js.map +1 -1
  647. package/dist/team/__tests__/worker-runtime-identity.test.js +38 -2
  648. package/dist/team/__tests__/worker-runtime-identity.test.js.map +1 -1
  649. package/dist/team/__tests__/worktree.test.js +6 -0
  650. package/dist/team/__tests__/worktree.test.js.map +1 -1
  651. package/dist/team/api-interop.d.ts +2 -2
  652. package/dist/team/api-interop.d.ts.map +1 -1
  653. package/dist/team/api-interop.js +15 -9
  654. package/dist/team/api-interop.js.map +1 -1
  655. package/dist/team/commit-hygiene.js.map +1 -1
  656. package/dist/team/contracts.d.ts +5 -5
  657. package/dist/team/contracts.d.ts.map +1 -1
  658. package/dist/team/delivery-log.js.map +1 -1
  659. package/dist/team/exact-pane.d.ts +23 -0
  660. package/dist/team/exact-pane.d.ts.map +1 -0
  661. package/dist/team/exact-pane.js +133 -0
  662. package/dist/team/exact-pane.js.map +1 -0
  663. package/dist/team/followup-planner.js.map +1 -1
  664. package/dist/team/goal-workflow.js.map +1 -1
  665. package/dist/team/idle-nudge.d.ts +12 -1
  666. package/dist/team/idle-nudge.d.ts.map +1 -1
  667. package/dist/team/idle-nudge.js +41 -14
  668. package/dist/team/idle-nudge.js.map +1 -1
  669. package/dist/team/model-contract.d.ts +18 -2
  670. package/dist/team/model-contract.d.ts.map +1 -1
  671. package/dist/team/model-contract.js +314 -35
  672. package/dist/team/model-contract.js.map +1 -1
  673. package/dist/team/orchestrator.js.map +1 -1
  674. package/dist/team/pane-status.js.map +1 -1
  675. package/dist/team/phase-controller.js.map +1 -1
  676. package/dist/team/reminder-intents.d.ts +1 -1
  677. package/dist/team/reminder-intents.d.ts.map +1 -1
  678. package/dist/team/reminder-intents.js.map +1 -1
  679. package/dist/team/runtime-cli.js.map +1 -1
  680. package/dist/team/runtime.d.ts +1 -0
  681. package/dist/team/runtime.d.ts.map +1 -1
  682. package/dist/team/runtime.js +1326 -231
  683. package/dist/team/runtime.js.map +1 -1
  684. package/dist/team/scaling.d.ts +22 -8
  685. package/dist/team/scaling.d.ts.map +1 -1
  686. package/dist/team/scaling.js +1463 -376
  687. package/dist/team/scaling.js.map +1 -1
  688. package/dist/team/state/dispatch-lock.js.map +1 -1
  689. package/dist/team/state/dispatch.d.ts.map +1 -1
  690. package/dist/team/state/dispatch.js +26 -8
  691. package/dist/team/state/dispatch.js.map +1 -1
  692. package/dist/team/state/events.js.map +1 -1
  693. package/dist/team/state/locks.d.ts.map +1 -1
  694. package/dist/team/state/locks.js +61 -13
  695. package/dist/team/state/locks.js.map +1 -1
  696. package/dist/team/state/tasks.d.ts.map +1 -1
  697. package/dist/team/state/tasks.js +4 -3
  698. package/dist/team/state/tasks.js.map +1 -1
  699. package/dist/team/state-root.js.map +1 -1
  700. package/dist/team/state.d.ts +67 -1
  701. package/dist/team/state.d.ts.map +1 -1
  702. package/dist/team/state.js +636 -129
  703. package/dist/team/state.js.map +1 -1
  704. package/dist/team/team-identity.d.ts.map +1 -1
  705. package/dist/team/team-identity.js.map +1 -1
  706. package/dist/team/team-ops.d.ts +5 -0
  707. package/dist/team/team-ops.d.ts.map +1 -1
  708. package/dist/team/team-ops.js +4 -0
  709. package/dist/team/team-ops.js.map +1 -1
  710. package/dist/team/tmux-session.d.ts +125 -23
  711. package/dist/team/tmux-session.d.ts.map +1 -1
  712. package/dist/team/tmux-session.js +1340 -333
  713. package/dist/team/tmux-session.js.map +1 -1
  714. package/dist/team/worker-bootstrap.js.map +1 -1
  715. package/dist/team/worktree.d.ts.map +1 -1
  716. package/dist/team/worktree.js +4 -0
  717. package/dist/team/worktree.js.map +1 -1
  718. package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
  719. package/dist/ultragoal/__tests__/docs-contract.test.js.map +1 -1
  720. package/dist/ultragoal/artifacts.d.ts.map +1 -1
  721. package/dist/ultragoal/artifacts.js +4 -1
  722. package/dist/ultragoal/artifacts.js.map +1 -1
  723. package/dist/url-reader/index.js.map +1 -1
  724. package/dist/utils/__tests__/file-durability.test.d.ts +2 -0
  725. package/dist/utils/__tests__/file-durability.test.d.ts.map +1 -0
  726. package/dist/utils/__tests__/file-durability.test.js +57 -0
  727. package/dist/utils/__tests__/file-durability.test.js.map +1 -0
  728. package/dist/utils/agents-model-table.js.map +1 -1
  729. package/dist/utils/file-durability.d.ts +15 -0
  730. package/dist/utils/file-durability.d.ts.map +1 -0
  731. package/dist/utils/file-durability.js +37 -0
  732. package/dist/utils/file-durability.js.map +1 -0
  733. package/dist/utils/package.js.map +1 -1
  734. package/dist/utils/paths.js.map +1 -1
  735. package/dist/utils/platform-command.js.map +1 -1
  736. package/dist/utils/repo-deps.js.map +1 -1
  737. package/dist/utils/version.js.map +1 -1
  738. package/dist/verification/__tests__/ci-rust-gates.test.js +1 -1
  739. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js +3 -0
  740. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js.map +1 -1
  741. package/dist/verification/verifier.js.map +1 -1
  742. package/dist/visual/constants.d.ts +1 -1
  743. package/dist/visual/constants.d.ts.map +1 -1
  744. package/dist/vscode/__tests__/index.test.js.map +1 -1
  745. package/dist/vscode/index.js.map +1 -1
  746. package/dist/wiki/__tests__/cjk-tokenize.test.js.map +1 -1
  747. package/dist/wiki/__tests__/lint.test.js.map +1 -1
  748. package/dist/wiki/__tests__/query.test.js.map +1 -1
  749. package/dist/wiki/__tests__/storage.test.js.map +1 -1
  750. package/dist/wiki/__tests__/test-helpers.d.ts.map +1 -1
  751. package/dist/wiki/lint.d.ts +4 -1
  752. package/dist/wiki/lint.d.ts.map +1 -1
  753. package/dist/wiki/lint.js +3 -2
  754. package/dist/wiki/lint.js.map +1 -1
  755. package/dist/wiki/query.js.map +1 -1
  756. package/dist/wiki/storage.js.map +1 -1
  757. package/package.json +2 -2
  758. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  759. package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +24 -0
  760. package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +10 -5
  761. package/plugins/oh-my-codex/skills/ralph/SKILL.md +6 -2
  762. package/plugins/oh-my-codex/skills/ralplan/SKILL.md +6 -2
  763. package/plugins/oh-my-codex/skills/team/SKILL.md +13 -9
  764. package/skills/omx-setup/SKILL.md +10 -5
  765. package/skills/ralph/SKILL.md +6 -2
  766. package/skills/ralplan/SKILL.md +6 -2
  767. package/skills/team/SKILL.md +13 -9
  768. package/src/scripts/__tests__/codex-native-hook.test.ts +20048 -10827
  769. package/src/scripts/__tests__/fixtures/issue-3138-installed-smoke.ts +734 -0
  770. package/src/scripts/__tests__/notify-fallback-delivery.test.ts +177 -0
  771. package/src/scripts/__tests__/notify-state-io.test.ts +73 -0
  772. package/src/scripts/__tests__/reasoning-artifact-contract.test.ts +129 -0
  773. package/src/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.ts +78 -0
  774. package/src/scripts/__tests__/role-intent-durable-recovery-3181.test.ts +60 -0
  775. package/src/scripts/__tests__/smoke-packed-install.test.ts +1309 -4
  776. package/src/scripts/codex-native-hook.ts +11429 -1163
  777. package/src/scripts/notify-fallback-delivery.ts +416 -0
  778. package/src/scripts/notify-fallback-watcher.ts +216 -155
  779. package/src/scripts/notify-hook/auto-nudge.ts +113 -14
  780. package/src/scripts/notify-hook/managed-tmux.ts +149 -12
  781. package/src/scripts/notify-hook/ralph-session-resume.ts +37 -36
  782. package/src/scripts/notify-hook/state-io.ts +100 -13
  783. package/src/scripts/notify-hook/team-dispatch.ts +242 -75
  784. package/src/scripts/notify-hook/team-leader-nudge.ts +40 -18
  785. package/src/scripts/notify-hook/team-tmux-guard.ts +366 -39
  786. package/src/scripts/notify-hook/team-worker-stop.ts +20 -16
  787. package/src/scripts/notify-hook/team-worker.ts +39 -26
  788. package/src/scripts/notify-hook/tmux-injection.ts +66 -23
  789. package/src/scripts/notify-hook/visual-verdict.ts +22 -12
  790. package/src/scripts/notify-hook.ts +336 -90
  791. package/src/scripts/smoke-packed-install.ts +5154 -149
@@ -1,10 +1,12 @@
1
1
  import { describe, it } from 'node:test';
2
2
  import assert from 'node:assert/strict';
3
- import { mkdir, mkdtemp, readFile, rm, symlink, writeFile } from 'node:fs/promises';
3
+ import { spawn } from 'node:child_process';
4
+ import { once } from 'node:events';
4
5
  import { existsSync } from 'node:fs';
6
+ import { mkdir, mkdtemp, readFile, readdir, rename, rm, rmdir, symlink, writeFile } from 'node:fs/promises';
5
7
  import { tmpdir } from 'node:os';
6
8
  import { join } from 'node:path';
7
- import { resetSessionMetrics, reconcileNativeSessionStart, writeSessionStart, writeSessionEnd, readSessionState, readUsableSessionState, isSessionStale, } from '../session.js';
9
+ import { __createDefaultPidProbeForTests, appendPromptSessionProvenanceRejection, __resetSessionPointerTransactionDependenciesForTests, __setSessionPointerTransactionDependenciesForTests, inspectSessionPointerLock, isSessionPointerLaunchAbort, isSessionStale, readSessionPointer, readSessionState, readUsableSessionState, reconcileNativeSessionStart, recoverSessionPointerLock, resetSessionMetrics, resolveSessionPointerContext, writeSessionEnd, writeSessionStart, } from '../session.js';
8
10
  function todayIsoDate() {
9
11
  return new Date().toISOString().slice(0, 10);
10
12
  }
@@ -17,6 +19,66 @@ function makeState(overrides = {}) {
17
19
  ...overrides,
18
20
  };
19
21
  }
22
+ const TEST_TOKEN = 'transaction_token_123456';
23
+ const FOREIGN_TOKEN = 'foreign_token_123456789';
24
+ const SUCCESSOR_TOKEN = 'successor_token_123456789';
25
+ function codedError(code) {
26
+ return Object.assign(new Error(code), { code });
27
+ }
28
+ async function withPointerDependencies(overrides, run) {
29
+ __setSessionPointerTransactionDependenciesForTests(overrides);
30
+ try {
31
+ await run();
32
+ }
33
+ finally {
34
+ __resetSessionPointerTransactionDependenciesForTests();
35
+ }
36
+ }
37
+ async function withOwnerEnvironment(sessionId, run) {
38
+ const previous = process.env.OMX_SESSION_ID;
39
+ if (sessionId === undefined)
40
+ delete process.env.OMX_SESSION_ID;
41
+ else
42
+ process.env.OMX_SESSION_ID = sessionId;
43
+ try {
44
+ await run();
45
+ }
46
+ finally {
47
+ if (previous === undefined)
48
+ delete process.env.OMX_SESSION_ID;
49
+ else
50
+ process.env.OMX_SESSION_ID = previous;
51
+ }
52
+ }
53
+ function matchingProcessIdentity() {
54
+ return { status: 'matching', startTicks: 1 };
55
+ }
56
+ async function writeLockOwner(cwd, owner) {
57
+ const context = resolveSessionPointerContext(cwd);
58
+ await mkdir(context.lockPath, { recursive: true });
59
+ await writeFile(join(context.lockPath, 'owner.json'), JSON.stringify(owner), 'utf-8');
60
+ return context.lockPath;
61
+ }
62
+ function validLockOwner(overrides = {}) {
63
+ return {
64
+ version: 1,
65
+ token: TEST_TOKEN,
66
+ pid: process.pid,
67
+ platform: 'linux',
68
+ pid_start_ticks: 1,
69
+ created_at: '2026-07-14T00:00:00.000Z',
70
+ ...overrides,
71
+ };
72
+ }
73
+ async function linuxProcessStartTicks(pid) {
74
+ const stat = await readFile(`/proc/${pid}/stat`, 'utf-8');
75
+ const closingParen = stat.lastIndexOf(')');
76
+ assert.notEqual(closingParen, -1);
77
+ const fieldsAfterCommand = stat.slice(closingParen + 2).trim().split(/\s+/);
78
+ const startTicks = Number.parseInt(fieldsAfterCommand[19] ?? '', 10);
79
+ assert.equal(Number.isInteger(startTicks), true);
80
+ return startTicks;
81
+ }
20
82
  describe('session lifecycle manager', () => {
21
83
  it('resets session metrics files with zeroed counters', async () => {
22
84
  const cwd = await mkdtemp(join(tmpdir(), 'omx-session-metrics-'));
@@ -104,6 +166,55 @@ describe('session lifecycle manager', () => {
104
166
  await rm(cwd, { recursive: true, force: true });
105
167
  }
106
168
  });
169
+ it('emits the session-end warning only after successful end finalization', async () => {
170
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-session-end-durability-'));
171
+ const originalWrite = process.stderr.write;
172
+ const warnings = [];
173
+ process.stderr.write = ((value) => {
174
+ warnings.push(value);
175
+ return true;
176
+ });
177
+ try {
178
+ await writeSessionEnd(cwd, 'sess-end-durability', {
179
+ platform: 'win32',
180
+ regularFileSync: async () => { throw codedError('EPERM'); },
181
+ });
182
+ assert.deepEqual(warnings, [
183
+ '[omx] warning: Windows EPERM regular-file fsync unsupported in session pointer end; operation succeeded with degraded durability.\n',
184
+ ]);
185
+ }
186
+ finally {
187
+ process.stderr.write = originalWrite;
188
+ await rm(cwd, { recursive: true, force: true });
189
+ }
190
+ });
191
+ it('keeps session-end durability warnings silent when finalization or release fails', async () => {
192
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-session-end-durability-failure-'));
193
+ const originalWrite = process.stderr.write;
194
+ const warnings = [];
195
+ process.stderr.write = ((value) => {
196
+ warnings.push(value);
197
+ return true;
198
+ });
199
+ const regularFileSync = async () => { throw codedError('EPERM'); };
200
+ try {
201
+ await withPointerDependencies({
202
+ fs: {
203
+ rmdir: async () => { throw new Error('release failure'); },
204
+ },
205
+ }, async () => {
206
+ await assert.rejects(writeSessionEnd(cwd, 'sess-end-release-failure', {
207
+ platform: 'win32',
208
+ regularFileSync,
209
+ }));
210
+ });
211
+ assert.deepEqual(warnings, []);
212
+ }
213
+ finally {
214
+ process.stderr.write = originalWrite;
215
+ await rm(cwd, { recursive: true, force: true });
216
+ }
217
+ });
107
218
  it('does not delete the current session pointer when ending a different session', async () => {
108
219
  const cwd = await mkdtemp(join(tmpdir(), 'omx-session-end-owner-'));
109
220
  try {
@@ -249,7 +360,7 @@ describe('session lifecycle manager', () => {
249
360
  nativeSessionId: 'codex-native-old',
250
361
  });
251
362
  await reconcileNativeSessionStart(cwd, 'codex-native-new', {
252
- pid: 54321,
363
+ pid: process.pid,
253
364
  platform: 'win32',
254
365
  });
255
366
  await writeSessionEnd(cwd, 'omx-owner-session');
@@ -356,22 +467,26 @@ describe('session lifecycle manager', () => {
356
467
  await rm(cwd, { recursive: true, force: true });
357
468
  }
358
469
  });
359
- it('falls back to a fresh canonical session when reconciling without authoritative launch state', async () => {
360
- const cwd = await mkdtemp(join(tmpdir(), 'omx-session-native-fallback-'));
470
+ it('preserves foreign selected-pointer evidence instead of replacing it during native reconciliation', async () => {
471
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-session-native-foreign-'));
361
472
  try {
362
473
  const statePath = join(cwd, '.omx', 'state', 'session.json');
363
474
  await resetSessionMetrics(cwd);
364
475
  await writeFile(statePath, JSON.stringify({
365
476
  session_id: 'sess-other-worktree',
366
477
  cwd: join(cwd, '..', 'different-worktree'),
478
+ pid: process.pid,
479
+ platform: 'win32',
367
480
  }), 'utf-8');
368
- const reconciled = await reconcileNativeSessionStart(cwd, 'codex-fallback-1', {
369
- pid: 67890,
481
+ await assert.rejects(reconcileNativeSessionStart(cwd, 'codex-fallback-1', { platform: 'win32' }), (error) => isSessionPointerLaunchAbort(error)
482
+ && error.code === 'session_pointer_unusable'
483
+ && error.pointerStatus === 'foreign-cwd');
484
+ assert.equal(await readFile(statePath, 'utf-8'), JSON.stringify({
485
+ session_id: 'sess-other-worktree',
486
+ cwd: join(cwd, '..', 'different-worktree'),
487
+ pid: process.pid,
370
488
  platform: 'win32',
371
- });
372
- assert.equal(reconciled.session_id, 'codex-fallback-1');
373
- assert.equal(reconciled.native_session_id, 'codex-fallback-1');
374
- assert.equal(reconciled.pid, 67890);
489
+ }));
375
490
  }
376
491
  finally {
377
492
  await rm(cwd, { recursive: true, force: true });
@@ -501,4 +616,756 @@ describe('isSessionStale', () => {
501
616
  assert.equal(stale, false);
502
617
  });
503
618
  });
619
+ describe('session pointer transaction', () => {
620
+ it('makes root resolution failures pathless typed launch aborts', async () => {
621
+ await assert.rejects(writeSessionStart('bad\0cwd', 'sess-context'), (error) => isSessionPointerLaunchAbort(error)
622
+ && error.code === 'session_pointer_context_failure'
623
+ && error.operation === 'pointer-context-resolve'
624
+ && !('pointerPath' in error));
625
+ });
626
+ it('classifies only the exact selected pointer as absent, usable, stale, indeterminate, malformed, or foreign', async () => {
627
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-session-pointer-status-'));
628
+ try {
629
+ const context = resolveSessionPointerContext(cwd);
630
+ await withPointerDependencies({
631
+ probePid: () => 'alive',
632
+ readProcessIdentity: () => matchingProcessIdentity(),
633
+ }, async () => {
634
+ assert.equal((await readSessionPointer(context)).status, 'absent');
635
+ await mkdir(context.baseStateDir, { recursive: true });
636
+ await writeFile(context.sessionPath, JSON.stringify({
637
+ session_id: 'sess-usable',
638
+ started_at: '2026-07-14T00:00:00.000Z',
639
+ cwd,
640
+ pid: process.pid,
641
+ platform: 'linux',
642
+ pid_start_ticks: 1,
643
+ }), 'utf-8');
644
+ assert.equal((await readSessionPointer(context)).status, 'usable');
645
+ __setSessionPointerTransactionDependenciesForTests({
646
+ probePid: () => 'dead',
647
+ readProcessIdentity: () => matchingProcessIdentity(),
648
+ });
649
+ assert.equal((await readSessionPointer(context)).status, 'stale-dead');
650
+ __setSessionPointerTransactionDependenciesForTests({
651
+ probePid: () => 'indeterminate',
652
+ readProcessIdentity: () => matchingProcessIdentity(),
653
+ });
654
+ assert.equal((await readSessionPointer(context)).status, 'identity-indeterminate');
655
+ await writeFile(context.sessionPath, '{ not-json', 'utf-8');
656
+ assert.equal((await readSessionPointer(context)).status, 'malformed');
657
+ await writeFile(context.sessionPath, JSON.stringify({
658
+ session_id: 'sess-foreign',
659
+ started_at: '2026-07-14T00:00:00.000Z',
660
+ cwd: join(cwd, '..', 'foreign-worktree'),
661
+ pid: process.pid,
662
+ platform: 'win32',
663
+ }), 'utf-8');
664
+ assert.equal((await readSessionPointer(context)).status, 'foreign-cwd');
665
+ });
666
+ }
667
+ finally {
668
+ __resetSessionPointerTransactionDependenciesForTests();
669
+ await rm(cwd, { recursive: true, force: true });
670
+ }
671
+ });
672
+ it('maps only ESRCH to a definitely dead default PID probe', () => {
673
+ const alive = __createDefaultPidProbeForTests(() => { });
674
+ const esrch = __createDefaultPidProbeForTests(() => { throw codedError('ESRCH'); });
675
+ const eperm = __createDefaultPidProbeForTests(() => { throw codedError('EPERM'); });
676
+ const unknown = __createDefaultPidProbeForTests(() => { throw codedError('EACCES'); });
677
+ const noCode = __createDefaultPidProbeForTests(() => { throw new Error('unknown'); });
678
+ const primitive = __createDefaultPidProbeForTests(() => { throw 'unknown'; });
679
+ assert.equal(alive(1), 'alive');
680
+ assert.equal(esrch(1), 'dead');
681
+ assert.equal(eperm(1), 'indeterminate');
682
+ assert.equal(unknown(1), 'indeterminate');
683
+ assert.equal(noCode(1), 'indeterminate');
684
+ assert.equal(primitive(1), 'indeterminate');
685
+ });
686
+ it('never reaps dead, reused, indeterminate, missing, or malformed lock evidence', async () => {
687
+ const cases = [
688
+ { name: 'dead', owner: validLockOwner(), probePid: 'dead', expected: 'dead' },
689
+ { name: 'reused', owner: validLockOwner(), probePid: 'alive', identity: { status: 'reused', startTicks: 2 }, expected: 'reused' },
690
+ { name: 'indeterminate-probe', owner: validLockOwner(), probePid: 'indeterminate', expected: 'identity-indeterminate' },
691
+ { name: 'indeterminate-identity', owner: validLockOwner(), probePid: 'alive', identity: { status: 'indeterminate' }, expected: 'identity-indeterminate' },
692
+ { name: 'unsubstantiated-reuse', owner: validLockOwner(), probePid: 'alive', identity: { status: 'reused', startTicks: 1 }, expected: 'identity-indeterminate' },
693
+ { name: 'missing-required-hash', owner: validLockOwner({ pid_cmdline_hash: 'a'.repeat(64) }), probePid: 'alive', identity: matchingProcessIdentity(), expected: 'identity-indeterminate' },
694
+ { name: 'missing', probePid: 'alive', expected: 'missing' },
695
+ { name: 'malformed', owner: { version: 1 }, probePid: 'alive', expected: 'malformed' },
696
+ ];
697
+ for (const testCase of cases) {
698
+ const cwd = await mkdtemp(join(tmpdir(), `omx-session-lock-${testCase.name}-`));
699
+ try {
700
+ const context = resolveSessionPointerContext(cwd);
701
+ await mkdir(context.lockPath, { recursive: true });
702
+ if (testCase.owner) {
703
+ await writeFile(join(context.lockPath, 'owner.json'), JSON.stringify(testCase.owner), 'utf-8');
704
+ }
705
+ const before = await readdir(context.lockPath);
706
+ await withPointerDependencies({
707
+ token: () => TEST_TOKEN,
708
+ probePid: () => testCase.probePid,
709
+ readProcessIdentity: () => testCase.identity ?? matchingProcessIdentity(),
710
+ }, async () => {
711
+ await assert.rejects(writeSessionStart(cwd, 'sess-lock', { platform: 'win32' }), (error) => isSessionPointerLaunchAbort(error)
712
+ && error.code === 'session_pointer_lock_recovery_required'
713
+ && error.lockOwnerStatus === testCase.expected);
714
+ });
715
+ assert.deepEqual(await readdir(context.lockPath), before);
716
+ }
717
+ finally {
718
+ await rm(cwd, { recursive: true, force: true });
719
+ }
720
+ }
721
+ });
722
+ it('atomically quarantines dead pre-rename temporary owner evidence and stays idempotent', async () => {
723
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-session-lock-recovery-dead-temp-'));
724
+ try {
725
+ const context = resolveSessionPointerContext(cwd);
726
+ await mkdir(context.lockPath, { recursive: true });
727
+ await writeFile(join(context.lockPath, `owner.${TEST_TOKEN}.tmp`), JSON.stringify(validLockOwner()), 'utf-8');
728
+ await withPointerDependencies({ token: () => SUCCESSOR_TOKEN, probePid: () => 'dead' }, async () => {
729
+ const inspected = await inspectSessionPointerLock(cwd);
730
+ assert.equal(inspected.status, 'dead');
731
+ assert.equal(inspected.evidenceSource, 'owner-temp');
732
+ assert.equal(inspected.safeToRecover, true);
733
+ const recovered = await recoverSessionPointerLock(cwd);
734
+ assert.equal(recovered.recovered, true);
735
+ assert.equal(recovered.action, 'quarantined');
736
+ assert.equal(existsSync(context.lockPath), false);
737
+ assert.equal(existsSync(recovered.quarantinePath), true);
738
+ const repeated = await recoverSessionPointerLock(cwd);
739
+ assert.equal(repeated.recovered, false);
740
+ assert.equal(repeated.action, 'none');
741
+ assert.equal(repeated.status, 'absent');
742
+ });
743
+ }
744
+ finally {
745
+ await rm(cwd, { recursive: true, force: true });
746
+ }
747
+ });
748
+ it('does not recover a dead canonical owner because owner.json is not an exact acquisition claim', async () => {
749
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-session-lock-recovery-dead-canonical-'));
750
+ try {
751
+ const context = resolveSessionPointerContext(cwd);
752
+ await writeLockOwner(cwd, validLockOwner());
753
+ await withPointerDependencies({ token: () => SUCCESSOR_TOKEN, probePid: () => 'dead' }, async () => {
754
+ const recovered = await recoverSessionPointerLock(cwd);
755
+ assert.equal(recovered.status, 'dead');
756
+ assert.equal(recovered.evidenceSource, 'owner.json');
757
+ assert.equal(recovered.safeToRecover, false);
758
+ assert.equal(recovered.recovered, false);
759
+ assert.equal(recovered.action, 'none');
760
+ assert.equal(existsSync(context.lockPath), true);
761
+ });
762
+ }
763
+ finally {
764
+ await rm(cwd, { recursive: true, force: true });
765
+ }
766
+ });
767
+ it('distinguishes a paused live pre-rename owner from the same owner after SIGKILL', async (t) => {
768
+ if (process.platform !== 'linux') {
769
+ t.skip('Linux process start identity is required for deterministic PID reuse protection.');
770
+ return;
771
+ }
772
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-session-lock-recovery-sigkill-'));
773
+ const child = spawn(process.execPath, ['-e', 'setInterval(() => {}, 1_000)'], { stdio: 'ignore' });
774
+ try {
775
+ assert.ok(child.pid);
776
+ await once(child, 'spawn');
777
+ const context = resolveSessionPointerContext(cwd);
778
+ await mkdir(context.lockPath, { recursive: true });
779
+ await writeFile(join(context.lockPath, `owner.${TEST_TOKEN}.tmp`), JSON.stringify(validLockOwner({
780
+ pid: child.pid,
781
+ pid_start_ticks: await linuxProcessStartTicks(child.pid),
782
+ })), 'utf-8');
783
+ const paused = await recoverSessionPointerLock(cwd);
784
+ assert.equal(paused.status, 'live');
785
+ assert.equal(paused.recovered, false);
786
+ assert.equal(paused.action, 'none');
787
+ assert.equal(existsSync(context.lockPath), true);
788
+ child.kill('SIGKILL');
789
+ const [exitCode, signal] = await once(child, 'exit');
790
+ assert.equal(exitCode, null);
791
+ assert.equal(signal, 'SIGKILL');
792
+ const orphaned = await recoverSessionPointerLock(cwd);
793
+ assert.equal(orphaned.status, 'dead');
794
+ assert.equal(orphaned.recovered, true);
795
+ assert.equal(orphaned.action, 'quarantined');
796
+ assert.equal(existsSync(context.lockPath), false);
797
+ }
798
+ finally {
799
+ if (child.exitCode === null && child.signalCode === null)
800
+ child.kill('SIGKILL');
801
+ await rm(cwd, { recursive: true, force: true });
802
+ }
803
+ });
804
+ it('fails closed for live pre-rename, PID reuse, malformed, and ambiguous lock evidence', async () => {
805
+ const cases = [
806
+ { name: 'live-temp', files: [[`owner.${TEST_TOKEN}.tmp`, JSON.stringify(validLockOwner())]], probePid: 'alive', identity: matchingProcessIdentity() },
807
+ { name: 'reused', files: [['owner.json', JSON.stringify(validLockOwner())]], probePid: 'alive', identity: { status: 'matching', startTicks: 2 } },
808
+ { name: 'malformed', files: [['owner.json', '{']], probePid: 'dead' },
809
+ { name: 'ambiguous', files: [['owner.json', JSON.stringify(validLockOwner())], [`owner.${SUCCESSOR_TOKEN}.tmp`, JSON.stringify(validLockOwner({ token: SUCCESSOR_TOKEN }))]], probePid: 'dead' },
810
+ ];
811
+ for (const testCase of cases) {
812
+ const cwd = await mkdtemp(join(tmpdir(), `omx-session-lock-recovery-${testCase.name}-`));
813
+ try {
814
+ const context = resolveSessionPointerContext(cwd);
815
+ await mkdir(context.lockPath, { recursive: true });
816
+ await Promise.all(testCase.files.map(async ([name, contents]) => await writeFile(join(context.lockPath, name), contents, 'utf-8')));
817
+ await withPointerDependencies({
818
+ token: () => SUCCESSOR_TOKEN,
819
+ probePid: () => testCase.probePid,
820
+ readProcessIdentity: () => testCase.identity ?? matchingProcessIdentity(),
821
+ }, async () => {
822
+ const before = await readdir(context.lockPath);
823
+ const recovered = await recoverSessionPointerLock(cwd);
824
+ assert.equal(recovered.recovered, false);
825
+ assert.equal(recovered.action, 'none');
826
+ assert.equal(recovered.safeToRecover, false);
827
+ assert.deepEqual(await readdir(context.lockPath), before);
828
+ });
829
+ }
830
+ finally {
831
+ await rm(cwd, { recursive: true, force: true });
832
+ }
833
+ }
834
+ });
835
+ it('claims stale evidence before quarantine so a successor cannot be mistaken for it', async () => {
836
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-session-lock-recovery-race-'));
837
+ try {
838
+ const context = resolveSessionPointerContext(cwd);
839
+ await mkdir(context.lockPath, { recursive: true });
840
+ await writeFile(join(context.lockPath, `owner.${TEST_TOKEN}.tmp`), JSON.stringify(validLockOwner()), 'utf-8');
841
+ const renames = [];
842
+ let successorBlocked = false;
843
+ await withPointerDependencies({
844
+ token: () => SUCCESSOR_TOKEN,
845
+ probePid: () => 'dead',
846
+ fs: {
847
+ rename: async (from, to) => {
848
+ renames.push([from, to]);
849
+ await rename(from, to);
850
+ if (from.endsWith(`owner.${TEST_TOKEN}.tmp`)) {
851
+ await assert.rejects(mkdir(context.lockPath), { code: 'EEXIST' });
852
+ successorBlocked = true;
853
+ }
854
+ },
855
+ },
856
+ }, async () => {
857
+ const recovered = await recoverSessionPointerLock(cwd);
858
+ assert.equal(recovered.recovered, true);
859
+ });
860
+ assert.equal(successorBlocked, true);
861
+ assert.match(renames[0][0], /owner\.transaction_token_123456\.tmp$/);
862
+ assert.match(renames[0][1], new RegExp(`owner\\.${TEST_TOKEN}\\.${SUCCESSOR_TOKEN}\\.recovery$`));
863
+ assert.match(renames[1][0], new RegExp(`owner\\.${TEST_TOKEN}\\.${SUCCESSOR_TOKEN}\\.recovery$`));
864
+ assert.equal(renames[1][1].includes('.quarantine.'), true);
865
+ }
866
+ finally {
867
+ await rm(cwd, { recursive: true, force: true });
868
+ }
869
+ });
870
+ it('does not claim a successor lock when the inspected orphan is displaced before the exact temp rename', async () => {
871
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-session-lock-recovery-successor-race-'));
872
+ try {
873
+ const context = resolveSessionPointerContext(cwd);
874
+ const staleTemp = join(context.lockPath, `owner.${TEST_TOKEN}.tmp`);
875
+ const successorTemp = join(context.lockPath, `owner.${SUCCESSOR_TOKEN}.tmp`);
876
+ const displacedPath = `${context.lockPath}.displaced`;
877
+ await mkdir(context.lockPath, { recursive: true });
878
+ await writeFile(staleTemp, JSON.stringify(validLockOwner()), 'utf-8');
879
+ let displaced = false;
880
+ await withPointerDependencies({
881
+ token: () => SUCCESSOR_TOKEN,
882
+ probePid: () => 'dead',
883
+ fs: {
884
+ rename: async (from, to) => {
885
+ if (!displaced && from === staleTemp) {
886
+ displaced = true;
887
+ await rename(context.lockPath, displacedPath);
888
+ await mkdir(context.lockPath);
889
+ await writeFile(successorTemp, JSON.stringify(validLockOwner({ token: SUCCESSOR_TOKEN })), 'utf-8');
890
+ }
891
+ await rename(from, to);
892
+ },
893
+ },
894
+ }, async () => {
895
+ const recovered = await recoverSessionPointerLock(cwd);
896
+ assert.equal(recovered.recovered, false);
897
+ assert.equal(recovered.action, 'none');
898
+ assert.match(recovered.reason, /changed before recovery claim/i);
899
+ });
900
+ assert.equal(displaced, true);
901
+ assert.deepEqual(await readdir(context.lockPath), [`owner.${SUCCESSOR_TOKEN}.tmp`]);
902
+ assert.equal(await readFile(successorTemp, 'utf-8'), JSON.stringify(validLockOwner({ token: SUCCESSOR_TOKEN })));
903
+ }
904
+ finally {
905
+ await rm(cwd, { recursive: true, force: true });
906
+ }
907
+ });
908
+ it('does not quarantine a successor live lock displaced into the canonical path after claim revalidation', async () => {
909
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-session-lock-recovery-post-claim-race-'));
910
+ try {
911
+ const context = resolveSessionPointerContext(cwd);
912
+ const staleTemp = join(context.lockPath, `owner.${TEST_TOKEN}.tmp`);
913
+ const successorTemp = join(context.lockPath, `owner.${SUCCESSOR_TOKEN}.tmp`);
914
+ const displacedPath = `${context.lockPath}.claimed-displaced`;
915
+ await mkdir(context.lockPath, { recursive: true });
916
+ await writeFile(staleTemp, JSON.stringify(validLockOwner()), 'utf-8');
917
+ let displaced = false;
918
+ await withPointerDependencies({
919
+ token: () => SUCCESSOR_TOKEN,
920
+ probePid: () => 'dead',
921
+ fs: {
922
+ rmdir: async (path) => {
923
+ if (!displaced && path === context.lockPath) {
924
+ displaced = true;
925
+ await rename(context.lockPath, displacedPath);
926
+ await mkdir(context.lockPath);
927
+ await writeFile(successorTemp, JSON.stringify(validLockOwner({ token: SUCCESSOR_TOKEN })), 'utf-8');
928
+ }
929
+ await rmdir(path);
930
+ },
931
+ },
932
+ }, async () => {
933
+ const recovered = await recoverSessionPointerLock(cwd);
934
+ assert.equal(recovered.recovered, false);
935
+ assert.equal(recovered.action, 'none');
936
+ assert.match(recovered.reason, /canonical lock directory was not empty/i);
937
+ assert.ok(recovered.quarantinePath);
938
+ });
939
+ assert.equal(displaced, true);
940
+ assert.deepEqual(await readdir(context.lockPath), [`owner.${SUCCESSOR_TOKEN}.tmp`]);
941
+ assert.equal(await readFile(successorTemp, 'utf-8'), JSON.stringify(validLockOwner({ token: SUCCESSOR_TOKEN })));
942
+ }
943
+ finally {
944
+ await rm(cwd, { recursive: true, force: true });
945
+ }
946
+ });
947
+ it('retries a positively matching live owner with the bounded 25/50/100 schedule before timing out', async () => {
948
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-session-lock-timeout-'));
949
+ try {
950
+ await writeLockOwner(cwd, validLockOwner());
951
+ const delays = [];
952
+ let now = 0;
953
+ await withPointerDependencies({
954
+ nowMs: () => now,
955
+ sleep: async (ms) => { delays.push(ms); now += ms; },
956
+ token: () => TEST_TOKEN,
957
+ probePid: () => 'alive',
958
+ readProcessIdentity: () => matchingProcessIdentity(),
959
+ }, async () => {
960
+ await assert.rejects(writeSessionStart(cwd, 'sess-timeout', { platform: 'win32' }), (error) => isSessionPointerLaunchAbort(error)
961
+ && error.code === 'session_pointer_lock_timeout'
962
+ && error.lockOwnerStatus === 'live');
963
+ });
964
+ assert.deepEqual(delays.slice(0, 3), [25, 50, 100]);
965
+ assert.equal(delays.at(-1), 25);
966
+ assert.equal(existsSync(resolveSessionPointerContext(cwd).lockPath), true);
967
+ }
968
+ finally {
969
+ await rm(cwd, { recursive: true, force: true });
970
+ }
971
+ });
972
+ it('rolls back only its unpublished owner artifacts and reports rollback ambiguity', async () => {
973
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-session-owner-publish-'));
974
+ try {
975
+ const context = resolveSessionPointerContext(cwd);
976
+ await withPointerDependencies({
977
+ token: () => TEST_TOKEN,
978
+ fs: {
979
+ writeFile: async (path, data, options) => {
980
+ if (path.endsWith(`owner.${TEST_TOKEN}.tmp`))
981
+ throw new Error('owner write failure');
982
+ await writeFile(path, data, options);
983
+ },
984
+ },
985
+ }, async () => {
986
+ await assert.rejects(writeSessionStart(cwd, 'sess-owner-publish', { platform: 'win32' }), (error) => isSessionPointerLaunchAbort(error)
987
+ && error.code === 'session_pointer_io_failure'
988
+ && error.operation === 'lock-owner-publish');
989
+ });
990
+ assert.equal(existsSync(context.lockPath), false);
991
+ await withPointerDependencies({
992
+ token: () => TEST_TOKEN,
993
+ fs: {
994
+ writeFile: async (path, data, options) => {
995
+ if (path.endsWith(`owner.${TEST_TOKEN}.tmp`))
996
+ throw new Error('owner write failure');
997
+ await writeFile(path, data, options);
998
+ },
999
+ rmdir: async (path) => {
1000
+ if (path === context.lockPath)
1001
+ throw new Error('keep lock evidence');
1002
+ await rm(path, { recursive: false });
1003
+ },
1004
+ },
1005
+ }, async () => {
1006
+ await assert.rejects(writeSessionStart(cwd, 'sess-owner-residue', { platform: 'win32' }), (error) => isSessionPointerLaunchAbort(error)
1007
+ && error.code === 'session_pointer_lock_recovery_required'
1008
+ && error.primaryOperation === 'lock-owner-publish'
1009
+ && error.secondaryFailures?.[0]?.phase === 'remove-unpublished-lock');
1010
+ });
1011
+ assert.equal(existsSync(context.lockPath), true);
1012
+ }
1013
+ finally {
1014
+ await rm(cwd, { recursive: true, force: true });
1015
+ }
1016
+ });
1017
+ it('publishes the owner and pointer on Windows when regular-file fsync reports EPERM', async () => {
1018
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-session-windows-eperm-'));
1019
+ let syncCalls = 0;
1020
+ const regularFileSync = async (platform) => {
1021
+ assert.equal(platform, 'win32');
1022
+ syncCalls += 1;
1023
+ throw codedError('EPERM');
1024
+ };
1025
+ try {
1026
+ const state = await writeSessionStart(cwd, 'sess-windows-eperm', {
1027
+ platform: 'win32',
1028
+ regularFileSync,
1029
+ });
1030
+ const context = resolveSessionPointerContext(cwd);
1031
+ assert.equal(state.session_id, 'sess-windows-eperm');
1032
+ assert.equal(syncCalls, 2, 'owner publication and pointer publication must both attempt fsync');
1033
+ assert.equal(existsSync(join(context.lockPath, 'owner.json')), false, 'lock owner is removed after commit');
1034
+ assert.equal(JSON.parse(await readFile(context.sessionPath, 'utf8')).session_id, 'sess-windows-eperm');
1035
+ }
1036
+ finally {
1037
+ await rm(cwd, { recursive: true, force: true });
1038
+ }
1039
+ });
1040
+ it('emits one post-release warning for a degraded session start and stays silent for synced or failed transactions', async () => {
1041
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-session-durability-warning-'));
1042
+ const originalWrite = process.stderr.write;
1043
+ const warnings = [];
1044
+ process.stderr.write = ((value) => {
1045
+ warnings.push({ value, lockExists: existsSync(resolveSessionPointerContext(cwd).lockPath) });
1046
+ return true;
1047
+ });
1048
+ const regularFileSync = async () => { throw codedError('EPERM'); };
1049
+ try {
1050
+ await writeSessionStart(cwd, 'sess-degraded-start', { platform: 'win32', regularFileSync });
1051
+ assert.deepEqual(warnings, [{
1052
+ value: '[omx] warning: Windows EPERM regular-file fsync unsupported in session pointer start/reconcile; operation succeeded with degraded durability.\n',
1053
+ lockExists: false,
1054
+ }]);
1055
+ warnings.length = 0;
1056
+ await writeSessionStart(cwd, 'sess-degraded-start', { platform: 'win32' });
1057
+ assert.deepEqual(warnings, []);
1058
+ await withPointerDependencies({
1059
+ fs: {
1060
+ rename: async (from, to) => {
1061
+ if (to === resolveSessionPointerContext(cwd).sessionPath)
1062
+ throw new Error('rename failure');
1063
+ await rename(from, to);
1064
+ },
1065
+ },
1066
+ }, async () => {
1067
+ await assert.rejects(writeSessionStart(cwd, 'sess-degraded-start', { platform: 'win32', regularFileSync }));
1068
+ });
1069
+ assert.deepEqual(warnings, []);
1070
+ }
1071
+ finally {
1072
+ process.stderr.write = originalWrite;
1073
+ await rm(cwd, { recursive: true, force: true });
1074
+ }
1075
+ });
1076
+ it('types state-directory, pointer-read, owner-sync, owner-rename, and invalid-token failures before commit', async () => {
1077
+ const failures = [
1078
+ {
1079
+ name: 'state-dir',
1080
+ operation: 'state-dir-create',
1081
+ dependencies: (context) => ({
1082
+ fs: {
1083
+ mkdir: async (path, options) => {
1084
+ if (path === context.baseStateDir)
1085
+ throw new Error('state directory failure');
1086
+ await mkdir(path, options);
1087
+ },
1088
+ },
1089
+ }),
1090
+ },
1091
+ {
1092
+ name: 'pointer-read',
1093
+ operation: 'pointer-read',
1094
+ dependencies: (context) => ({
1095
+ token: () => TEST_TOKEN,
1096
+ fs: {
1097
+ readFile: async (path, encoding) => {
1098
+ if (path === context.sessionPath)
1099
+ throw new Error('pointer read failure');
1100
+ return await readFile(path, encoding);
1101
+ },
1102
+ },
1103
+ }),
1104
+ },
1105
+ {
1106
+ name: 'owner-sync',
1107
+ operation: 'lock-owner-publish',
1108
+ dependencies: (context) => ({
1109
+ token: () => TEST_TOKEN,
1110
+ fs: {
1111
+ openAndSync: async (path) => {
1112
+ if (path === join(context.lockPath, `owner.${TEST_TOKEN}.tmp`))
1113
+ throw new Error('owner sync failure');
1114
+ return 'synced';
1115
+ },
1116
+ },
1117
+ }),
1118
+ },
1119
+ {
1120
+ name: 'owner-rename',
1121
+ operation: 'lock-owner-publish',
1122
+ dependencies: (context) => ({
1123
+ token: () => TEST_TOKEN,
1124
+ fs: {
1125
+ rename: async (from, to) => {
1126
+ if (from === join(context.lockPath, `owner.${TEST_TOKEN}.tmp`))
1127
+ throw new Error('owner rename failure');
1128
+ await rename(from, to);
1129
+ },
1130
+ },
1131
+ }),
1132
+ },
1133
+ ];
1134
+ for (const failure of failures) {
1135
+ const cwd = await mkdtemp(join(tmpdir(), `omx-session-${failure.name}-`));
1136
+ try {
1137
+ const context = resolveSessionPointerContext(cwd);
1138
+ await withPointerDependencies(failure.dependencies(context), async () => {
1139
+ await assert.rejects(writeSessionStart(cwd, 'sess-precommit', { platform: 'win32' }), (error) => isSessionPointerLaunchAbort(error)
1140
+ && error.code === 'session_pointer_io_failure'
1141
+ && error.operation === failure.operation);
1142
+ });
1143
+ assert.equal(existsSync(context.lockPath), false);
1144
+ }
1145
+ finally {
1146
+ await rm(cwd, { recursive: true, force: true });
1147
+ }
1148
+ }
1149
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-session-invalid-token-'));
1150
+ try {
1151
+ const context = resolveSessionPointerContext(cwd);
1152
+ await withPointerDependencies({ token: () => 'bad' }, async () => {
1153
+ await assert.rejects(writeSessionStart(cwd, 'sess-invalid-token', { platform: 'win32' }), (error) => isSessionPointerLaunchAbort(error)
1154
+ && error.code === 'session_pointer_io_failure'
1155
+ && error.operation === 'lock-owner-publish');
1156
+ });
1157
+ assert.equal(existsSync(context.lockPath), false);
1158
+ assert.equal(existsSync(`${context.sessionPath}.tmp-bad`), false);
1159
+ }
1160
+ finally {
1161
+ await rm(cwd, { recursive: true, force: true });
1162
+ }
1163
+ });
1164
+ it('types pointer write, sync, and rename failures and removes only the owned temporary file', async () => {
1165
+ const phases = [
1166
+ { operation: 'pointer-temp-write', fail: 'write' },
1167
+ { operation: 'pointer-fsync', fail: 'sync' },
1168
+ { operation: 'pointer-rename', fail: 'rename' },
1169
+ ];
1170
+ for (const phase of phases) {
1171
+ const cwd = await mkdtemp(join(tmpdir(), `omx-session-${phase.fail}-`));
1172
+ try {
1173
+ const context = resolveSessionPointerContext(cwd);
1174
+ const tempPath = `${context.sessionPath}.tmp-${TEST_TOKEN}`;
1175
+ await withPointerDependencies({
1176
+ token: () => TEST_TOKEN,
1177
+ fs: {
1178
+ writeFile: async (path, data, options) => {
1179
+ await writeFile(path, data, options);
1180
+ if (phase.fail === 'write' && path === tempPath)
1181
+ throw new Error('pointer write failure');
1182
+ },
1183
+ openAndSync: async (path) => {
1184
+ if (phase.fail === 'sync' && path === tempPath)
1185
+ throw new Error('pointer sync failure');
1186
+ return 'synced';
1187
+ },
1188
+ rename: async (from, to) => {
1189
+ if (phase.fail === 'rename' && from === tempPath && to === context.sessionPath) {
1190
+ throw new Error('pointer rename failure');
1191
+ }
1192
+ await rename(from, to);
1193
+ },
1194
+ },
1195
+ }, async () => {
1196
+ await assert.rejects(writeSessionStart(cwd, 'sess-failure', { platform: 'win32' }), (error) => isSessionPointerLaunchAbort(error)
1197
+ && error.code === 'session_pointer_io_failure'
1198
+ && error.operation === phase.operation);
1199
+ });
1200
+ assert.equal(existsSync(tempPath), false);
1201
+ assert.equal(existsSync(context.lockPath), false);
1202
+ }
1203
+ finally {
1204
+ await rm(cwd, { recursive: true, force: true });
1205
+ }
1206
+ }
1207
+ });
1208
+ it('preserves owned pointer residue and ordered cleanup evidence when cleanup or release cannot complete', async () => {
1209
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-session-pointer-residue-'));
1210
+ try {
1211
+ const context = resolveSessionPointerContext(cwd);
1212
+ const tempPath = `${context.sessionPath}.tmp-${TEST_TOKEN}`;
1213
+ await withPointerDependencies({
1214
+ token: () => TEST_TOKEN,
1215
+ fs: {
1216
+ writeFile: async (path, data, options) => {
1217
+ await writeFile(path, data, options);
1218
+ if (path === tempPath)
1219
+ throw new Error('pointer write failure');
1220
+ },
1221
+ unlink: async (path) => {
1222
+ if (path === tempPath)
1223
+ throw new Error('pointer temp cleanup failure');
1224
+ await rm(path, { force: false });
1225
+ },
1226
+ rmdir: async (path) => {
1227
+ if (path.endsWith(`.release-${TEST_TOKEN}`))
1228
+ throw new Error('release cleanup failure');
1229
+ await rm(path, { recursive: false });
1230
+ },
1231
+ },
1232
+ }, async () => {
1233
+ await assert.rejects(writeSessionStart(cwd, 'sess-residue', { platform: 'win32' }), (error) => isSessionPointerLaunchAbort(error)
1234
+ && error.code === 'session_pointer_lock_recovery_required'
1235
+ && error.primaryOperation === 'pointer-temp-write'
1236
+ && error.secondaryFailures?.map((failure) => failure.phase).join(',') === 'remove-pointer-temp,remove-release-dir'
1237
+ && error.secondaryFailures?.[0]?.evidencePath === tempPath);
1238
+ });
1239
+ assert.equal(existsSync(tempPath), true);
1240
+ assert.equal(existsSync(`${context.lockPath}.release-${TEST_TOKEN}`), true);
1241
+ }
1242
+ finally {
1243
+ await rm(cwd, { recursive: true, force: true });
1244
+ }
1245
+ });
1246
+ it('leaves foreign pointer temps inert and a successor commits after explicit lock recovery', async () => {
1247
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-session-pointer-successor-'));
1248
+ try {
1249
+ const context = resolveSessionPointerContext(cwd);
1250
+ const ownTempPath = `${context.sessionPath}.tmp-${TEST_TOKEN}`;
1251
+ const foreignTempPath = `${context.sessionPath}.tmp-${FOREIGN_TOKEN}`;
1252
+ await mkdir(context.baseStateDir, { recursive: true });
1253
+ await writeFile(foreignTempPath, 'foreign transaction evidence', 'utf-8');
1254
+ await withPointerDependencies({
1255
+ token: () => TEST_TOKEN,
1256
+ fs: {
1257
+ writeFile: async (path, data, options) => {
1258
+ await writeFile(path, data, options);
1259
+ if (path === ownTempPath)
1260
+ throw new Error('pointer write failure');
1261
+ },
1262
+ unlink: async (path) => {
1263
+ if (path === ownTempPath)
1264
+ throw new Error('keep own residue');
1265
+ await rm(path, { force: false });
1266
+ },
1267
+ rename: async (from, to) => {
1268
+ if (from === context.lockPath)
1269
+ throw new Error('preserve canonical lock');
1270
+ await rename(from, to);
1271
+ },
1272
+ },
1273
+ }, async () => {
1274
+ await assert.rejects(writeSessionStart(cwd, 'sess-first', { platform: 'win32' }), isSessionPointerLaunchAbort);
1275
+ });
1276
+ assert.equal(existsSync(foreignTempPath), true);
1277
+ assert.equal(existsSync(context.lockPath), true);
1278
+ // This is documented stopped-session operator recovery, deliberately not
1279
+ // product transaction behavior.
1280
+ await rm(context.lockPath, { recursive: true, force: true });
1281
+ await withPointerDependencies({ token: () => SUCCESSOR_TOKEN }, async () => {
1282
+ await writeSessionStart(cwd, 'sess-successor', { platform: 'win32' });
1283
+ });
1284
+ assert.equal((await readSessionState(cwd))?.session_id, 'sess-successor');
1285
+ assert.equal(existsSync(ownTempPath), true);
1286
+ assert.equal(existsSync(foreignTempPath), true);
1287
+ assert.equal(existsSync(context.lockPath), false);
1288
+ }
1289
+ finally {
1290
+ await rm(cwd, { recursive: true, force: true });
1291
+ }
1292
+ });
1293
+ it('binds an owner alias only on a verified same-native/absent transition and never during replacement', async () => {
1294
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-session-owner-alias-'));
1295
+ try {
1296
+ const nativeOnly = await reconcileNativeSessionStart(cwd, 'native-first', {
1297
+ platform: 'win32',
1298
+ ownerOmxSessionId: 'omx-owner',
1299
+ });
1300
+ assert.equal(nativeOnly.owner_omx_session_id, undefined);
1301
+ await withOwnerEnvironment('omx-owner', async () => {
1302
+ const bound = await reconcileNativeSessionStart(cwd, 'native-first', {
1303
+ platform: 'win32',
1304
+ ownerAliasVerified: true,
1305
+ });
1306
+ assert.equal(bound.session_id, 'native-first');
1307
+ assert.equal(bound.owner_omx_session_id, 'omx-owner');
1308
+ const replacement = await reconcileNativeSessionStart(cwd, 'native-second', {
1309
+ platform: 'win32',
1310
+ ownerAliasVerified: true,
1311
+ });
1312
+ assert.equal(replacement.session_id, 'native-second');
1313
+ assert.equal(replacement.owner_omx_session_id, 'omx-owner');
1314
+ });
1315
+ }
1316
+ finally {
1317
+ await rm(cwd, { recursive: true, force: true });
1318
+ }
1319
+ });
1320
+ it('preserves pointer evidence when history cannot be appended and rejects unusable end states before history or HUD cleanup', async () => {
1321
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-session-end-preserve-'));
1322
+ try {
1323
+ const context = resolveSessionPointerContext(cwd);
1324
+ await writeSessionStart(cwd, 'sess-history', { platform: 'win32' });
1325
+ await mkdir(join(cwd, '.omx', 'logs'), { recursive: true });
1326
+ await rm(join(cwd, '.omx', 'logs'), { recursive: true, force: true });
1327
+ await writeFile(join(cwd, '.omx', 'logs'), 'not-a-directory', 'utf-8');
1328
+ await assert.rejects(writeSessionEnd(cwd, 'sess-history'));
1329
+ assert.equal((await readSessionState(cwd))?.session_id, 'sess-history');
1330
+ await rm(join(cwd, '.omx', 'logs'), { force: true });
1331
+ await writeFile(context.sessionPath, '{ malformed', 'utf-8');
1332
+ await assert.rejects(writeSessionEnd(cwd, 'sess-history'), (error) => isSessionPointerLaunchAbort(error)
1333
+ && error.code === 'session_pointer_unusable'
1334
+ && error.pointerStatus === 'malformed');
1335
+ assert.equal(await readFile(context.sessionPath, 'utf-8'), '{ malformed');
1336
+ assert.equal(existsSync(join(cwd, '.omx', 'logs', 'session-history.jsonl')), false);
1337
+ }
1338
+ finally {
1339
+ await rm(cwd, { recursive: true, force: true });
1340
+ }
1341
+ });
1342
+ });
1343
+ describe('prompt provenance diagnostics', () => {
1344
+ it('writes exactly one redacted record at the supplied selected root', async () => {
1345
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-provenance-log-'));
1346
+ try {
1347
+ const stateDir = join(cwd, 'selected', '.omx', 'state');
1348
+ await appendPromptSessionProvenanceRejection({
1349
+ cwd,
1350
+ baseStateDir: stateDir,
1351
+ rootSource: 'cwd-default',
1352
+ sessionPath: join(stateDir, 'session.json'),
1353
+ lockPath: join(stateDir, 'session.json.lock'),
1354
+ }, {
1355
+ reason: 'payload_session_invalid',
1356
+ producer: 'native',
1357
+ selectedRootStatus: 'malformed',
1358
+ timestamp: '2026-07-14T00:00:00.000Z',
1359
+ });
1360
+ const log = await readFile(join(cwd, 'selected', '.omx', 'logs', `omx-${todayIsoDate()}.jsonl`), 'utf-8');
1361
+ assert.equal(log.trim().split('\n').length, 1);
1362
+ assert.match(log, /"event":"prompt_session_provenance_rejected"/);
1363
+ assert.equal(log.includes(cwd), false);
1364
+ assert.equal(existsSync(join(cwd, '.omx', 'logs', `omx-${todayIsoDate()}.jsonl`)), false);
1365
+ }
1366
+ finally {
1367
+ await rm(cwd, { recursive: true, force: true });
1368
+ }
1369
+ });
1370
+ });
504
1371
  //# sourceMappingURL=session.test.js.map