spexcode 0.6.2 → 0.6.6

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 (495) hide show
  1. package/README.md +3 -1
  2. package/bin/spex.mjs +4 -0
  3. package/node_modules/@spexcode/session-core/dist/delivery-queue.d.ts +23 -0
  4. package/node_modules/@spexcode/session-core/dist/delivery-queue.js +195 -0
  5. package/node_modules/@spexcode/session-core/dist/index.d.ts +5 -0
  6. package/node_modules/@spexcode/session-core/dist/index.js +5 -0
  7. package/node_modules/@spexcode/session-core/dist/internal.d.ts +3 -0
  8. package/node_modules/@spexcode/session-core/dist/internal.js +3 -0
  9. package/node_modules/@spexcode/session-core/dist/message.d.ts +22 -0
  10. package/node_modules/@spexcode/session-core/dist/message.js +52 -0
  11. package/node_modules/@spexcode/session-core/dist/record-lock.d.ts +7 -0
  12. package/node_modules/@spexcode/session-core/dist/record-lock.js +152 -0
  13. package/node_modules/@spexcode/session-core/dist/runtime-session.d.ts +50 -0
  14. package/node_modules/@spexcode/session-core/dist/runtime-session.js +286 -0
  15. package/node_modules/@spexcode/session-core/dist/session-cursors.d.ts +14 -0
  16. package/node_modules/@spexcode/session-core/dist/session-cursors.js +82 -0
  17. package/node_modules/@spexcode/session-core/dist/session-timeline.d.ts +46 -0
  18. package/node_modules/@spexcode/session-core/dist/session-timeline.js +216 -0
  19. package/node_modules/@spexcode/session-core/package.json +33 -0
  20. package/node_modules/@spexcode/spec-cli/bin/spex.mjs +77 -0
  21. package/node_modules/@spexcode/spec-cli/dist/attach.d.ts +2 -0
  22. package/node_modules/@spexcode/spec-cli/dist/attach.js +48 -0
  23. package/node_modules/@spexcode/spec-cli/dist/claude-headless.d.ts +36 -0
  24. package/node_modules/@spexcode/spec-cli/dist/claude-headless.js +316 -0
  25. package/node_modules/@spexcode/spec-cli/dist/cli.d.ts +1 -0
  26. package/node_modules/@spexcode/spec-cli/dist/cli.js +1908 -0
  27. package/node_modules/@spexcode/spec-cli/dist/client.d.ts +112 -0
  28. package/node_modules/@spexcode/spec-cli/dist/client.js +481 -0
  29. package/node_modules/@spexcode/spec-cli/dist/cockpit.d.ts +7 -0
  30. package/node_modules/@spexcode/spec-cli/dist/cockpit.js +26 -0
  31. package/node_modules/@spexcode/spec-cli/dist/codex-headless.d.ts +1 -0
  32. package/{spec-cli/src/codex-headless.ts → node_modules/@spexcode/spec-cli/dist/codex-headless.js} +3 -9
  33. package/node_modules/@spexcode/spec-cli/dist/codex-runtime-generations.d.ts +58 -0
  34. package/node_modules/@spexcode/spec-cli/dist/codex-runtime-generations.js +798 -0
  35. package/node_modules/@spexcode/spec-cli/dist/commit-surgery.d.ts +1 -0
  36. package/node_modules/@spexcode/spec-cli/dist/commit-surgery.js +90 -0
  37. package/node_modules/@spexcode/spec-cli/dist/contract-filter.d.ts +14 -0
  38. package/node_modules/@spexcode/spec-cli/dist/contract-filter.js +219 -0
  39. package/node_modules/@spexcode/spec-cli/dist/dashboard-assets.d.ts +6 -0
  40. package/node_modules/@spexcode/spec-cli/dist/dashboard-assets.js +50 -0
  41. package/node_modules/@spexcode/spec-cli/dist/doctor.d.ts +9 -0
  42. package/node_modules/@spexcode/spec-cli/dist/doctor.js +553 -0
  43. package/node_modules/@spexcode/spec-cli/dist/eval-host.d.ts +1 -0
  44. package/node_modules/@spexcode/spec-cli/dist/eval-host.js +19 -0
  45. package/node_modules/@spexcode/spec-cli/dist/execution-trace.d.ts +26 -0
  46. package/node_modules/@spexcode/spec-cli/dist/execution-trace.js +475 -0
  47. package/node_modules/@spexcode/spec-cli/dist/file-write.d.ts +2 -0
  48. package/node_modules/@spexcode/spec-cli/dist/file-write.js +26 -0
  49. package/node_modules/@spexcode/spec-cli/dist/flat.d.ts +89 -0
  50. package/node_modules/@spexcode/spec-cli/dist/flat.js +1076 -0
  51. package/node_modules/@spexcode/spec-cli/dist/gateway-auth.d.ts +56 -0
  52. package/node_modules/@spexcode/spec-cli/dist/gateway-auth.js +168 -0
  53. package/node_modules/@spexcode/spec-cli/dist/gateway-hub.d.ts +28 -0
  54. package/node_modules/@spexcode/spec-cli/dist/gateway-hub.js +363 -0
  55. package/node_modules/@spexcode/spec-cli/dist/gateway.d.ts +38 -0
  56. package/node_modules/@spexcode/spec-cli/dist/gateway.js +527 -0
  57. package/node_modules/@spexcode/spec-cli/dist/graphCache.d.ts +27 -0
  58. package/node_modules/@spexcode/spec-cli/dist/graphCache.js +717 -0
  59. package/node_modules/@spexcode/spec-cli/dist/graphSnapshot.d.ts +14 -0
  60. package/node_modules/@spexcode/spec-cli/dist/graphSnapshot.js +36 -0
  61. package/node_modules/@spexcode/spec-cli/dist/graphStream.d.ts +85 -0
  62. package/node_modules/@spexcode/spec-cli/dist/graphStream.js +1195 -0
  63. package/node_modules/@spexcode/spec-cli/dist/guidance-catalog.d.ts +64 -0
  64. package/node_modules/@spexcode/spec-cli/dist/guidance-catalog.js +166 -0
  65. package/node_modules/@spexcode/spec-cli/dist/guide.d.ts +8 -0
  66. package/{spec-cli/src/guide.ts → node_modules/@spexcode/spec-cli/dist/guide.js} +36 -32
  67. package/node_modules/@spexcode/spec-cli/dist/harness-select.d.ts +18 -0
  68. package/node_modules/@spexcode/spec-cli/dist/harness-select.js +62 -0
  69. package/node_modules/@spexcode/spec-cli/dist/harness.d.ts +338 -0
  70. package/node_modules/@spexcode/spec-cli/dist/harness.js +3146 -0
  71. package/node_modules/@spexcode/spec-cli/dist/headless-controller.d.ts +9 -0
  72. package/node_modules/@spexcode/spec-cli/dist/headless-controller.js +47 -0
  73. package/node_modules/@spexcode/spec-cli/dist/help.d.ts +15 -0
  74. package/node_modules/@spexcode/spec-cli/dist/help.js +594 -0
  75. package/node_modules/@spexcode/spec-cli/dist/hook-prompts.d.ts +18 -0
  76. package/node_modules/@spexcode/spec-cli/dist/hook-prompts.js +122 -0
  77. package/node_modules/@spexcode/spec-cli/dist/hooks.d.ts +1 -0
  78. package/{spec-cli/src/hooks.ts → node_modules/@spexcode/spec-cli/dist/hooks.js} +15 -15
  79. package/node_modules/@spexcode/spec-cli/dist/host-resources.d.ts +97 -0
  80. package/node_modules/@spexcode/spec-cli/dist/host-resources.js +795 -0
  81. package/node_modules/@spexcode/spec-cli/dist/host.d.ts +88 -0
  82. package/node_modules/@spexcode/spec-cli/dist/host.js +663 -0
  83. package/node_modules/@spexcode/spec-cli/dist/index.d.ts +1 -0
  84. package/node_modules/@spexcode/spec-cli/dist/index.js +899 -0
  85. package/node_modules/@spexcode/spec-cli/dist/init.d.ts +1 -0
  86. package/node_modules/@spexcode/spec-cli/dist/init.js +284 -0
  87. package/node_modules/@spexcode/spec-cli/dist/issues-cli.d.ts +6 -0
  88. package/node_modules/@spexcode/spec-cli/dist/issues-cli.js +334 -0
  89. package/node_modules/@spexcode/spec-cli/dist/issues.d.ts +69 -0
  90. package/node_modules/@spexcode/spec-cli/dist/issues.js +189 -0
  91. package/node_modules/@spexcode/spec-cli/dist/lint.d.ts +34 -0
  92. package/node_modules/@spexcode/spec-cli/dist/lint.js +476 -0
  93. package/node_modules/@spexcode/spec-cli/dist/listen.d.ts +8 -0
  94. package/node_modules/@spexcode/spec-cli/dist/listen.js +24 -0
  95. package/node_modules/@spexcode/spec-cli/dist/localIssues.d.ts +65 -0
  96. package/node_modules/@spexcode/spec-cli/dist/localIssues.js +582 -0
  97. package/node_modules/@spexcode/spec-cli/dist/login-page.d.ts +5 -0
  98. package/{spec-cli/src/login-page.ts → node_modules/@spexcode/spec-cli/dist/login-page.js} +7 -7
  99. package/node_modules/@spexcode/spec-cli/dist/loop-in.d.ts +23 -0
  100. package/{spec-cli/src/loop-in.ts → node_modules/@spexcode/spec-cli/dist/loop-in.js} +35 -45
  101. package/node_modules/@spexcode/spec-cli/dist/machine-peer.d.ts +69 -0
  102. package/node_modules/@spexcode/spec-cli/dist/machine-peer.js +603 -0
  103. package/node_modules/@spexcode/spec-cli/dist/materialize.d.ts +18 -0
  104. package/node_modules/@spexcode/spec-cli/dist/materialize.js +468 -0
  105. package/node_modules/@spexcode/spec-cli/dist/mentions.d.ts +50 -0
  106. package/node_modules/@spexcode/spec-cli/dist/mentions.js +117 -0
  107. package/node_modules/@spexcode/spec-cli/dist/opencode-headless.d.ts +5 -0
  108. package/node_modules/@spexcode/spec-cli/dist/opencode-headless.js +215 -0
  109. package/node_modules/@spexcode/spec-cli/dist/opencode.d.ts +3 -0
  110. package/{spec-cli/src/opencode.ts → node_modules/@spexcode/spec-cli/dist/opencode.js} +9 -13
  111. package/node_modules/@spexcode/spec-cli/dist/pi-harness.d.ts +3 -0
  112. package/{spec-cli/src/pi-harness.ts → node_modules/@spexcode/spec-cli/dist/pi-harness.js} +45 -40
  113. package/node_modules/@spexcode/spec-cli/dist/pi-headless.d.ts +25 -0
  114. package/node_modules/@spexcode/spec-cli/dist/pi-headless.js +196 -0
  115. package/node_modules/@spexcode/spec-cli/dist/plugin-harness.d.ts +21 -0
  116. package/node_modules/@spexcode/spec-cli/dist/plugin-harness.js +146 -0
  117. package/node_modules/@spexcode/spec-cli/dist/pty-bridge.d.ts +10 -0
  118. package/node_modules/@spexcode/spec-cli/dist/pty-bridge.js +404 -0
  119. package/node_modules/@spexcode/spec-cli/dist/pty-helper.d.mts +1 -0
  120. package/node_modules/@spexcode/spec-cli/dist/pty-helper.mjs +101 -0
  121. package/node_modules/@spexcode/spec-cli/dist/pty-native-helper.d.mts +2 -0
  122. package/node_modules/@spexcode/spec-cli/dist/pty-native-helper.mjs +23 -0
  123. package/node_modules/@spexcode/spec-cli/dist/public-graph.d.ts +46 -0
  124. package/node_modules/@spexcode/spec-cli/dist/public-graph.js +56 -0
  125. package/node_modules/@spexcode/spec-cli/dist/ranker.d.ts +15 -0
  126. package/node_modules/@spexcode/spec-cli/dist/ranker.js +188 -0
  127. package/node_modules/@spexcode/spec-cli/dist/reaper.d.ts +6 -0
  128. package/node_modules/@spexcode/spec-cli/dist/reaper.js +81 -0
  129. package/node_modules/@spexcode/spec-cli/dist/reviews.d.ts +189 -0
  130. package/node_modules/@spexcode/spec-cli/dist/reviews.js +350 -0
  131. package/node_modules/@spexcode/spec-cli/dist/runtime-guard.d.ts +6 -0
  132. package/node_modules/@spexcode/spec-cli/dist/runtime-guard.js +37 -0
  133. package/node_modules/@spexcode/spec-cli/dist/runtime-ownership.d.ts +21 -0
  134. package/node_modules/@spexcode/spec-cli/dist/runtime-ownership.js +84 -0
  135. package/node_modules/@spexcode/spec-cli/dist/runtime-rotate.d.ts +1 -0
  136. package/node_modules/@spexcode/spec-cli/dist/runtime-rotate.js +58 -0
  137. package/node_modules/@spexcode/spec-cli/dist/search.bench.d.mts +1 -0
  138. package/node_modules/@spexcode/spec-cli/dist/search.bench.mjs +75 -0
  139. package/node_modules/@spexcode/spec-cli/dist/search.d.ts +20 -0
  140. package/node_modules/@spexcode/spec-cli/dist/search.js +61 -0
  141. package/node_modules/@spexcode/spec-cli/dist/session-declarations.d.ts +13 -0
  142. package/node_modules/@spexcode/spec-cli/dist/session-declarations.js +126 -0
  143. package/node_modules/@spexcode/spec-cli/dist/session-execution.d.ts +10 -0
  144. package/node_modules/@spexcode/spec-cli/dist/session-execution.js +70 -0
  145. package/node_modules/@spexcode/spec-cli/dist/session-files.d.ts +28 -0
  146. package/node_modules/@spexcode/spec-cli/dist/session-files.js +130 -0
  147. package/node_modules/@spexcode/spec-cli/dist/session-follow.d.ts +31 -0
  148. package/node_modules/@spexcode/spec-cli/dist/session-follow.js +179 -0
  149. package/node_modules/@spexcode/spec-cli/dist/session-reparent.d.ts +10 -0
  150. package/node_modules/@spexcode/spec-cli/dist/session-reparent.js +33 -0
  151. package/node_modules/@spexcode/spec-cli/dist/session-timeline.d.ts +11 -0
  152. package/node_modules/@spexcode/spec-cli/dist/session-timeline.js +20 -0
  153. package/node_modules/@spexcode/spec-cli/dist/session-web.d.ts +24 -0
  154. package/node_modules/@spexcode/spec-cli/dist/session-web.js +141 -0
  155. package/node_modules/@spexcode/spec-cli/dist/sessions.d.ts +433 -0
  156. package/node_modules/@spexcode/spec-cli/dist/sessions.js +4667 -0
  157. package/node_modules/@spexcode/spec-cli/dist/sh.d.ts +1 -0
  158. package/node_modules/@spexcode/spec-cli/dist/sh.js +3 -0
  159. package/node_modules/@spexcode/spec-cli/dist/shim-runtime.d.ts +1 -0
  160. package/{spec-cli/src/shim-runtime.ts → node_modules/@spexcode/spec-cli/dist/shim-runtime.js} +3 -4
  161. package/node_modules/@spexcode/spec-cli/dist/slash-commands.d.ts +9 -0
  162. package/node_modules/@spexcode/spec-cli/dist/slash-commands.js +307 -0
  163. package/node_modules/@spexcode/spec-cli/dist/source-files.d.ts +9 -0
  164. package/node_modules/@spexcode/spec-cli/dist/source-files.js +113 -0
  165. package/node_modules/@spexcode/spec-cli/dist/supervise.d.ts +1 -0
  166. package/node_modules/@spexcode/spec-cli/dist/supervise.js +308 -0
  167. package/node_modules/@spexcode/spec-cli/dist/tree.d.ts +25 -0
  168. package/node_modules/@spexcode/spec-cli/dist/tree.js +98 -0
  169. package/node_modules/@spexcode/spec-cli/dist/tsx-bin.d.ts +3 -0
  170. package/node_modules/@spexcode/spec-cli/dist/tsx-bin.js +41 -0
  171. package/node_modules/@spexcode/spec-cli/dist/uninstall.d.ts +3 -0
  172. package/node_modules/@spexcode/spec-cli/dist/uninstall.js +177 -0
  173. package/node_modules/@spexcode/spec-cli/dist/uploads.d.ts +36 -0
  174. package/node_modules/@spexcode/spec-cli/dist/uploads.js +243 -0
  175. package/node_modules/@spexcode/spec-cli/dist/worktree-sources.d.ts +1 -0
  176. package/node_modules/@spexcode/spec-cli/dist/worktree-sources.js +47 -0
  177. package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/dispatch.sh +5 -5
  178. package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/harness.sh +1 -1
  179. package/node_modules/@spexcode/spec-cli/package.json +49 -0
  180. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/commands/supervisor/spec.md +8 -0
  181. package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-first/spec-first.sh +1 -1
  182. package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-of-file/spec-of-file.sh +1 -0
  183. package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/stop-gate/spec.md +2 -2
  184. package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/stop-gate/stop-gate.sh +31 -29
  185. package/node_modules/@spexcode/spec-core/dist/anchors.d.ts +94 -0
  186. package/node_modules/@spexcode/spec-core/dist/anchors.js +730 -0
  187. package/node_modules/@spexcode/spec-core/dist/git.d.ts +170 -0
  188. package/node_modules/@spexcode/spec-core/dist/git.js +2768 -0
  189. package/node_modules/@spexcode/spec-core/dist/graph-delta.d.ts +44 -0
  190. package/node_modules/@spexcode/spec-core/dist/graph-delta.js +72 -0
  191. package/node_modules/@spexcode/spec-core/dist/graph.d.ts +34 -0
  192. package/node_modules/@spexcode/spec-core/dist/graph.js +237 -0
  193. package/node_modules/@spexcode/spec-core/dist/graphDelta.d.ts +3 -0
  194. package/node_modules/@spexcode/spec-core/dist/graphDelta.js +13 -0
  195. package/node_modules/@spexcode/spec-core/dist/harness-identity.d.ts +31 -0
  196. package/node_modules/@spexcode/spec-core/dist/harness-identity.js +20 -0
  197. package/node_modules/@spexcode/spec-core/dist/identity-presets.d.ts +152 -0
  198. package/node_modules/@spexcode/spec-core/dist/identity-presets.js +132 -0
  199. package/node_modules/@spexcode/spec-core/dist/index.d.ts +45 -0
  200. package/node_modules/@spexcode/spec-core/dist/index.js +19 -0
  201. package/node_modules/@spexcode/spec-core/dist/layout.d.ts +187 -0
  202. package/node_modules/@spexcode/spec-core/dist/layout.js +548 -0
  203. package/node_modules/@spexcode/spec-core/dist/process-identity.d.ts +37 -0
  204. package/node_modules/@spexcode/spec-core/dist/process-identity.js +214 -0
  205. package/node_modules/@spexcode/spec-core/dist/project-identity.d.ts +12 -0
  206. package/node_modules/@spexcode/spec-core/dist/project-identity.js +71 -0
  207. package/node_modules/@spexcode/spec-core/dist/project-store.d.ts +3 -0
  208. package/node_modules/@spexcode/spec-core/dist/project-store.js +14 -0
  209. package/node_modules/@spexcode/spec-core/dist/resilience.d.ts +2 -0
  210. package/node_modules/@spexcode/spec-core/dist/resilience.js +40 -0
  211. package/node_modules/@spexcode/spec-core/dist/review/index.d.ts +3 -0
  212. package/node_modules/@spexcode/spec-core/dist/review/index.js +5 -0
  213. package/node_modules/@spexcode/spec-core/dist/review/reviewFilters.d.ts +77 -0
  214. package/node_modules/@spexcode/spec-core/dist/review/reviewFilters.js +308 -0
  215. package/node_modules/@spexcode/spec-core/dist/review/reviewQuery.d.ts +66 -0
  216. package/node_modules/@spexcode/spec-core/dist/review/reviewQuery.js +180 -0
  217. package/node_modules/@spexcode/spec-core/dist/review/session.d.ts +4 -0
  218. package/node_modules/@spexcode/spec-core/dist/review/session.js +8 -0
  219. package/node_modules/@spexcode/spec-core/dist/reviewSnapshot.d.ts +15 -0
  220. package/node_modules/@spexcode/spec-core/dist/reviewSnapshot.js +12 -0
  221. package/node_modules/@spexcode/spec-core/dist/root-lru.d.ts +4 -0
  222. package/{spec-cli/src/root-lru.ts → node_modules/@spexcode/spec-core/dist/root-lru.js} +26 -30
  223. package/node_modules/@spexcode/spec-core/dist/specs.d.ts +117 -0
  224. package/node_modules/@spexcode/spec-core/dist/specs.js +489 -0
  225. package/node_modules/@spexcode/spec-core/package.json +28 -0
  226. package/node_modules/@spexcode/spec-eval/dist/cache.d.ts +10 -0
  227. package/node_modules/@spexcode/spec-eval/dist/cache.js +50 -0
  228. package/node_modules/@spexcode/spec-eval/dist/cli.d.ts +22 -0
  229. package/node_modules/@spexcode/spec-eval/dist/cli.js +981 -0
  230. package/node_modules/@spexcode/spec-eval/dist/evaltab.d.ts +98 -0
  231. package/node_modules/@spexcode/spec-eval/dist/evaltab.js +176 -0
  232. package/node_modules/@spexcode/spec-eval/dist/filing.d.ts +16 -0
  233. package/node_modules/@spexcode/spec-eval/dist/filing.js +36 -0
  234. package/node_modules/@spexcode/spec-eval/dist/freshness.d.ts +48 -0
  235. package/node_modules/@spexcode/spec-eval/dist/freshness.js +799 -0
  236. package/node_modules/@spexcode/spec-eval/dist/host.d.ts +46 -0
  237. package/node_modules/@spexcode/spec-eval/dist/host.js +53 -0
  238. package/node_modules/@spexcode/spec-eval/dist/humanok.d.ts +11 -0
  239. package/node_modules/@spexcode/spec-eval/dist/humanok.js +27 -0
  240. package/node_modules/@spexcode/spec-eval/dist/index.d.ts +4 -0
  241. package/node_modules/@spexcode/spec-eval/dist/index.js +4 -0
  242. package/node_modules/@spexcode/spec-eval/dist/remarks.d.ts +31 -0
  243. package/node_modules/@spexcode/spec-eval/dist/remarks.js +1 -0
  244. package/node_modules/@spexcode/spec-eval/dist/scenariofresh.d.ts +13 -0
  245. package/node_modules/@spexcode/spec-eval/dist/scenariofresh.js +303 -0
  246. package/node_modules/@spexcode/spec-eval/dist/scenarios.d.ts +99 -0
  247. package/node_modules/@spexcode/spec-eval/dist/scenarios.js +650 -0
  248. package/node_modules/@spexcode/spec-eval/dist/sessioneval.d.ts +307 -0
  249. package/node_modules/@spexcode/spec-eval/dist/sessioneval.js +1883 -0
  250. package/node_modules/@spexcode/spec-eval/dist/sidecar.d.ts +55 -0
  251. package/node_modules/@spexcode/spec-eval/dist/sidecar.js +82 -0
  252. package/node_modules/@spexcode/spec-eval/dist/timeline.d.ts +25 -0
  253. package/node_modules/@spexcode/spec-eval/dist/timeline.js +65 -0
  254. package/node_modules/@spexcode/spec-eval/dist/ui-path.d.ts +1 -0
  255. package/node_modules/@spexcode/spec-eval/dist/ui-path.js +2 -0
  256. package/node_modules/@spexcode/spec-eval/package.json +36 -0
  257. package/node_modules/@spexcode/spec-forge/dist/cache.d.ts +29 -0
  258. package/node_modules/@spexcode/spec-forge/dist/cache.js +59 -0
  259. package/node_modules/@spexcode/spec-forge/dist/cli.d.ts +1 -0
  260. package/node_modules/@spexcode/spec-forge/dist/cli.js +100 -0
  261. package/node_modules/@spexcode/spec-forge/dist/drivers/github.d.ts +2 -0
  262. package/node_modules/@spexcode/spec-forge/dist/drivers/github.js +116 -0
  263. package/node_modules/@spexcode/spec-forge/dist/drivers/gitlab.d.ts +7 -0
  264. package/node_modules/@spexcode/spec-forge/dist/drivers/gitlab.js +144 -0
  265. package/node_modules/@spexcode/spec-forge/dist/drivers.d.ts +10 -0
  266. package/node_modules/@spexcode/spec-forge/dist/drivers.js +83 -0
  267. package/node_modules/@spexcode/spec-forge/dist/index.d.ts +4 -0
  268. package/node_modules/@spexcode/spec-forge/dist/index.js +4 -0
  269. package/node_modules/@spexcode/spec-forge/dist/links.d.ts +15 -0
  270. package/node_modules/@spexcode/spec-forge/dist/links.js +65 -0
  271. package/node_modules/@spexcode/spec-forge/dist/needs-eval.d.ts +10 -0
  272. package/node_modules/@spexcode/spec-forge/dist/needs-eval.js +20 -0
  273. package/node_modules/@spexcode/spec-forge/dist/port.d.ts +53 -0
  274. package/node_modules/@spexcode/spec-forge/dist/port.js +1 -0
  275. package/node_modules/@spexcode/spec-forge/dist/resident.d.ts +7 -0
  276. package/node_modules/@spexcode/spec-forge/dist/resident.js +43 -0
  277. package/node_modules/@spexcode/spec-forge/package.json +32 -0
  278. package/package.json +33 -19
  279. package/spec-cli/bin/spex.mjs +0 -69
  280. package/spec-cli/src/anchors.ts +0 -728
  281. package/spec-cli/src/attach.ts +0 -43
  282. package/spec-cli/src/claude-headless.ts +0 -321
  283. package/spec-cli/src/cli.ts +0 -1258
  284. package/spec-cli/src/client.ts +0 -414
  285. package/spec-cli/src/cockpit.ts +0 -43
  286. package/spec-cli/src/codex-runtime-generations.ts +0 -646
  287. package/spec-cli/src/commit-surgery.ts +0 -68
  288. package/spec-cli/src/contract-filter.ts +0 -202
  289. package/spec-cli/src/delivery-queue.ts +0 -165
  290. package/spec-cli/src/doctor.ts +0 -516
  291. package/spec-cli/src/execution-trace.ts +0 -444
  292. package/spec-cli/src/file-write.ts +0 -22
  293. package/spec-cli/src/gateway-auth.ts +0 -181
  294. package/spec-cli/src/gateway-hub.ts +0 -358
  295. package/spec-cli/src/gateway.ts +0 -461
  296. package/spec-cli/src/git.ts +0 -2556
  297. package/spec-cli/src/graph.ts +0 -271
  298. package/spec-cli/src/graphCache.ts +0 -710
  299. package/spec-cli/src/graphDelta.ts +0 -77
  300. package/spec-cli/src/graphStream.ts +0 -1019
  301. package/spec-cli/src/harness-select.ts +0 -77
  302. package/spec-cli/src/harness.ts +0 -3025
  303. package/spec-cli/src/headless-controller.ts +0 -63
  304. package/spec-cli/src/help.ts +0 -492
  305. package/spec-cli/src/host-resources.ts +0 -762
  306. package/spec-cli/src/host.ts +0 -611
  307. package/spec-cli/src/identity-presets.d.ts +0 -13
  308. package/spec-cli/src/identity-presets.js +0 -138
  309. package/spec-cli/src/index.ts +0 -818
  310. package/spec-cli/src/init.ts +0 -258
  311. package/spec-cli/src/issues-cli.ts +0 -276
  312. package/spec-cli/src/issues.ts +0 -237
  313. package/spec-cli/src/layout.ts +0 -640
  314. package/spec-cli/src/lint.ts +0 -488
  315. package/spec-cli/src/listen.ts +0 -28
  316. package/spec-cli/src/localIssues.ts +0 -538
  317. package/spec-cli/src/materialize.ts +0 -417
  318. package/spec-cli/src/mentions.ts +0 -81
  319. package/spec-cli/src/opencode-headless.ts +0 -205
  320. package/spec-cli/src/pi-headless.ts +0 -153
  321. package/spec-cli/src/plugin-harness.ts +0 -157
  322. package/spec-cli/src/process-identity.ts +0 -207
  323. package/spec-cli/src/project-identity.ts +0 -73
  324. package/spec-cli/src/project-store.ts +0 -17
  325. package/spec-cli/src/pty-bridge.ts +0 -396
  326. package/spec-cli/src/pty-helper.mjs +0 -96
  327. package/spec-cli/src/pty-native-helper.mjs +0 -22
  328. package/spec-cli/src/ranker.ts +0 -191
  329. package/spec-cli/src/reaper.ts +0 -125
  330. package/spec-cli/src/resilience.ts +0 -41
  331. package/spec-cli/src/reviewSnapshot.ts +0 -28
  332. package/spec-cli/src/reviews.ts +0 -434
  333. package/spec-cli/src/runtime-guard.ts +0 -35
  334. package/spec-cli/src/runtime-ownership.ts +0 -88
  335. package/spec-cli/src/search.bench.mjs +0 -80
  336. package/spec-cli/src/search.ts +0 -63
  337. package/spec-cli/src/session-cursors.ts +0 -91
  338. package/spec-cli/src/session-execution.ts +0 -68
  339. package/spec-cli/src/session-files.ts +0 -121
  340. package/spec-cli/src/session-follow.ts +0 -182
  341. package/spec-cli/src/session-reparent.ts +0 -33
  342. package/spec-cli/src/session-timeline.ts +0 -247
  343. package/spec-cli/src/session-web.ts +0 -135
  344. package/spec-cli/src/sessions.ts +0 -3883
  345. package/spec-cli/src/sh.ts +0 -3
  346. package/spec-cli/src/slash-commands.ts +0 -318
  347. package/spec-cli/src/source-files.ts +0 -104
  348. package/spec-cli/src/specs.ts +0 -498
  349. package/spec-cli/src/supervise.ts +0 -245
  350. package/spec-cli/src/tree.ts +0 -118
  351. package/spec-cli/src/tsx-bin.ts +0 -18
  352. package/spec-cli/src/uninstall.ts +0 -171
  353. package/spec-cli/src/uploads.ts +0 -253
  354. package/spec-cli/src/worktree-sources.ts +0 -42
  355. package/spec-cli/templates/spec/project/.plugins/commands/supervisor/spec.md +0 -8
  356. package/spec-dashboard/dist/assets/App-B6IbFbGu.js +0 -2
  357. package/spec-dashboard/dist/assets/Dashboard-C5ap-Sga.css +0 -1
  358. package/spec-dashboard/dist/assets/Dashboard-_sDk7Rcg.js +0 -27
  359. package/spec-dashboard/dist/assets/EvalsPage-BT357Lyt.js +0 -2
  360. package/spec-dashboard/dist/assets/IssuesPage-BG9rNOgV.js +0 -1
  361. package/spec-dashboard/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  362. package/spec-dashboard/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  363. package/spec-dashboard/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  364. package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  365. package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  366. package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  367. package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  368. package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  369. package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  370. package/spec-dashboard/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  371. package/spec-dashboard/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  372. package/spec-dashboard/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  373. package/spec-dashboard/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  374. package/spec-dashboard/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  375. package/spec-dashboard/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  376. package/spec-dashboard/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  377. package/spec-dashboard/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  378. package/spec-dashboard/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  379. package/spec-dashboard/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  380. package/spec-dashboard/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  381. package/spec-dashboard/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  382. package/spec-dashboard/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  383. package/spec-dashboard/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  384. package/spec-dashboard/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  385. package/spec-dashboard/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  386. package/spec-dashboard/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  387. package/spec-dashboard/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  388. package/spec-dashboard/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  389. package/spec-dashboard/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  390. package/spec-dashboard/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  391. package/spec-dashboard/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  392. package/spec-dashboard/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  393. package/spec-dashboard/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  394. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  395. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  396. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  397. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  398. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  399. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  400. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  401. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  402. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  403. package/spec-dashboard/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  404. package/spec-dashboard/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  405. package/spec-dashboard/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  406. package/spec-dashboard/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  407. package/spec-dashboard/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  408. package/spec-dashboard/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  409. package/spec-dashboard/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  410. package/spec-dashboard/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  411. package/spec-dashboard/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  412. package/spec-dashboard/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  413. package/spec-dashboard/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  414. package/spec-dashboard/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  415. package/spec-dashboard/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  416. package/spec-dashboard/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  417. package/spec-dashboard/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  418. package/spec-dashboard/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  419. package/spec-dashboard/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  420. package/spec-dashboard/dist/assets/MobileApp-D9yPMR3A.js +0 -2
  421. package/spec-dashboard/dist/assets/Modal-B3EcgeA3.js +0 -1
  422. package/spec-dashboard/dist/assets/PageScroll-CNZOugWV.js +0 -1
  423. package/spec-dashboard/dist/assets/ProjectsPage-C2CRHmvs.js +0 -1
  424. package/spec-dashboard/dist/assets/SessionInterface-6GBZ9nXN.css +0 -32
  425. package/spec-dashboard/dist/assets/SessionInterface-DOEjuT3l.js +0 -40
  426. package/spec-dashboard/dist/assets/SessionWindow-1nYgZ4CL.js +0 -1
  427. package/spec-dashboard/dist/assets/Settings-7WeTJn0V.js +0 -1
  428. package/spec-dashboard/dist/assets/Thread-CEbzAFQn.js +0 -13
  429. package/spec-dashboard/dist/assets/bindings-BC9vqpYU.js +0 -1
  430. package/spec-dashboard/dist/assets/data-SlvxNA2r.js +0 -1
  431. package/spec-dashboard/dist/assets/index-DLVeOHL-.js +0 -41
  432. package/spec-dashboard/dist/assets/index-DYLGfPjx.css +0 -1
  433. package/spec-dashboard/dist/assets/launch-Cp579UoJ.css +0 -1
  434. package/spec-dashboard/dist/assets/launch-yxCYr64x.js +0 -276
  435. package/spec-dashboard/dist/assets/sessionSurface--7l69h4F.js +0 -1
  436. package/spec-dashboard/dist/index.html +0 -29
  437. package/spec-dashboard/src/reviewFilters.js +0 -322
  438. package/spec-dashboard/src/reviewQuery.js +0 -174
  439. package/spec-dashboard/src/session.js +0 -199
  440. package/spec-eval/src/cache.ts +0 -57
  441. package/spec-eval/src/cli.ts +0 -894
  442. package/spec-eval/src/evaltab.ts +0 -264
  443. package/spec-eval/src/filing.ts +0 -39
  444. package/spec-eval/src/freshness.ts +0 -836
  445. package/spec-eval/src/humanok.ts +0 -28
  446. package/spec-eval/src/scenariofresh.ts +0 -244
  447. package/spec-eval/src/scenarios.ts +0 -672
  448. package/spec-eval/src/sessioneval.ts +0 -2220
  449. package/spec-eval/src/sidecar.ts +0 -93
  450. package/spec-eval/src/timeline.ts +0 -54
  451. package/spec-forge/src/__fixtures__/github-forge.json +0 -63
  452. package/spec-forge/src/cache.ts +0 -66
  453. package/spec-forge/src/cli.ts +0 -99
  454. package/spec-forge/src/drivers/github.ts +0 -132
  455. package/spec-forge/src/drivers/gitlab.ts +0 -153
  456. package/spec-forge/src/drivers.ts +0 -73
  457. package/spec-forge/src/links.ts +0 -72
  458. package/spec-forge/src/needs-eval.ts +0 -29
  459. package/spec-forge/src/port.ts +0 -36
  460. package/spec-forge/src/resident.ts +0 -46
  461. /package/{spec-cli → node_modules/@spexcode/spec-cli}/README.md +0 -0
  462. /package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/compat/mark-active-0.5.2-eef1.fixture +0 -0
  463. /package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/compat/mark-active-sed-v0.fixture +0 -0
  464. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/commit-msg +0 -0
  465. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/post-checkout +0 -0
  466. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/post-merge +0 -0
  467. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/pre-commit +0 -0
  468. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/prepare-commit-msg +0 -0
  469. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/reference-transaction +0 -0
  470. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/extract/spec.md +0 -0
  471. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/regroup/spec.md +0 -0
  472. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/rename/spec.md +0 -0
  473. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/spec.md +0 -0
  474. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/tidy/spec.md +0 -0
  475. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/comment-altitude/spec.md +0 -0
  476. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/idle/idle.sh +0 -0
  477. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/idle/spec.md +0 -0
  478. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/mark-active/mark-active.sh +0 -0
  479. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/mark-active/spec.md +0 -0
  480. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/session-fail/fail.sh +0 -0
  481. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/session-fail/spec.md +0 -0
  482. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-first/spec.md +0 -0
  483. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-of-file/spec.md +0 -0
  484. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec.md +0 -0
  485. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/atomic-landing/spec.md +0 -0
  486. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/forge-link/spec.md +0 -0
  487. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/memory-hygiene/spec.md +0 -0
  488. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/reproduce-before-fix/spec.md +0 -0
  489. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/spec.md +0 -0
  490. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/skills/distill/digest.mjs +0 -0
  491. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/skills/distill/spec.md +0 -0
  492. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/skills/spec.md +0 -0
  493. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/spec.md +0 -0
  494. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/spec.md +0 -0
  495. /package/{spec-cli → node_modules/@spexcode/spec-core}/templates/spexcode.json +0 -0
@@ -1,646 +0,0 @@
1
- import { createHash, randomUUID } from 'node:crypto'
2
- import { createConnection, type Socket } from 'node:net'
3
- import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from 'node:fs'
4
- import { dirname, join } from 'node:path'
5
- import { tmpdir } from 'node:os'
6
- import { detachedRuntimeIsGone, processStartToken, verifyDetachedRuntime } from './process-identity.js'
7
-
8
- export type CodexGenerationEndpoint = Readonly<{
9
- id: string
10
- pidFile: string
11
- receiptFile: string
12
- logFile: string
13
- socketPath: string
14
- }>
15
-
16
- type CodexGenerationState = 'current' | 'draining' | 'reclaimed' | 'starting'
17
- type GenerationReservation = Readonly<{ pid: number; startToken: string }>
18
- type CodexGeneration = Readonly<{ state: CodexGenerationState; endpoint: CodexGenerationEndpoint; reservation?: GenerationReservation }>
19
- export type CodexGenerationBinding = Readonly<{ generationId: string; threadId: string; phase?: 'record-pending' | 'record-removing' }>
20
-
21
- export type CodexGenerationLedger = Readonly<{
22
- version: 3
23
- revision: number
24
- current: string | null
25
- pending: string | null
26
- generations: Record<string, CodexGeneration>
27
- bindings: Record<string, CodexGenerationBinding>
28
- }>
29
-
30
- const generationsDir = (root: string) => join(root, 'codex-app-server-generations')
31
- const ledgerPath = (root: string) => join(root, 'codex-app-server-generations.json')
32
- const lockPath = (root: string) => join(root, 'codex-app-server-generations.lock')
33
- const lockOwnerPath = (root: string) => join(lockPath(root), 'owner.json')
34
- const lockReaperPath = (root: string) => join(lockPath(root), 'reaper')
35
- const lockReaperOwnerPath = (root: string) => join(lockReaperPath(root), 'owner.json')
36
- const sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms))
37
-
38
- export function codexGenerationSocketPath(root: string, generationId?: string): string {
39
- const base = process.env.SPEXCODE_CODEX_SOCKET_DIR || join(tmpdir(), `spexcode-cx-${process.getuid?.() ?? 0}`)
40
- mkdirSync(base, { recursive: true, mode: 0o700 })
41
- // No suffix preserves the deployed legacy location. Every detached-v3 generation has an independent socket.
42
- const key = generationId ? `${root}\0${generationId}` : root
43
- return join(base, `spexcode-cx-${createHash('sha1').update(key).digest('hex').slice(0, 16)}.sock`)
44
- }
45
-
46
- export function legacyCodexGenerationEndpoint(root: string): CodexGenerationEndpoint {
47
- return Object.freeze({
48
- id: 'legacy',
49
- pidFile: join(root, 'codex-app-server.pid'),
50
- receiptFile: join(root, 'codex-app-server.detached.json'),
51
- logFile: join(root, 'codex-app-server.log'),
52
- socketPath: codexGenerationSocketPath(root),
53
- })
54
- }
55
-
56
- function newEndpoint(root: string, id = `detached-v3-${randomUUID()}`): CodexGenerationEndpoint {
57
- const dir = join(generationsDir(root), id)
58
- return Object.freeze({
59
- id,
60
- pidFile: join(dir, 'pid'),
61
- receiptFile: join(dir, 'detached.json'),
62
- logFile: join(dir, 'app-server.log'),
63
- socketPath: codexGenerationSocketPath(root, id),
64
- })
65
- }
66
-
67
- const emptyLedger = (): CodexGenerationLedger => Object.freeze({ version: 3, revision: 0, current: null, pending: null, generations: {}, bindings: {} })
68
-
69
- function isEndpoint(value: unknown): value is CodexGenerationEndpoint {
70
- if (!value || typeof value !== 'object') return false
71
- const candidate = value as Partial<CodexGenerationEndpoint>
72
- return typeof candidate.id === 'string' && typeof candidate.pidFile === 'string' && typeof candidate.receiptFile === 'string' &&
73
- typeof candidate.logFile === 'string' && typeof candidate.socketPath === 'string'
74
- }
75
-
76
- function isReservation(value: unknown): value is GenerationReservation {
77
- if (!value || typeof value !== 'object') return false
78
- const candidate = value as Partial<GenerationReservation>
79
- return Number.isInteger(candidate.pid) && candidate.pid! > 0 && typeof candidate.startToken === 'string' && !!candidate.startToken
80
- }
81
-
82
- function parseLedger(value: unknown): CodexGenerationLedger {
83
- if (!value || typeof value !== 'object') throw new Error('Codex generation ledger is malformed')
84
- const raw = value as Partial<CodexGenerationLedger>
85
- if (raw.version !== 3 || !Number.isSafeInteger(raw.revision) || raw.revision! < 0 ||
86
- (raw.current !== null && typeof raw.current !== 'string') || (raw.pending !== null && typeof raw.pending !== 'string') ||
87
- !raw.generations || typeof raw.generations !== 'object' || !raw.bindings || typeof raw.bindings !== 'object') {
88
- throw new Error('Codex generation ledger has the wrong detached-v3 shape')
89
- }
90
- for (const [id, generation] of Object.entries(raw.generations)) {
91
- if (!generation || typeof generation !== 'object' || !['current', 'draining', 'reclaimed', 'starting'].includes((generation as CodexGeneration).state) ||
92
- !isEndpoint((generation as CodexGeneration).endpoint) || (generation as CodexGeneration).endpoint.id !== id ||
93
- ((generation as CodexGeneration).state === 'starting' && !isReservation((generation as CodexGeneration).reservation))) {
94
- throw new Error(`Codex generation ledger has malformed generation ${id}`)
95
- }
96
- }
97
- for (const [sessionId, binding] of Object.entries(raw.bindings)) {
98
- if (!binding || typeof binding !== 'object' || typeof (binding as CodexGenerationBinding).generationId !== 'string' ||
99
- typeof (binding as CodexGenerationBinding).threadId !== 'string' || !(binding as CodexGenerationBinding).threadId ||
100
- ((binding as CodexGenerationBinding).phase !== undefined && (binding as CodexGenerationBinding).phase !== 'record-pending' &&
101
- (binding as CodexGenerationBinding).phase !== 'record-removing')) {
102
- throw new Error(`Codex generation ledger has malformed binding ${sessionId}`)
103
- }
104
- }
105
- return raw as CodexGenerationLedger
106
- }
107
-
108
- export function readCodexGenerationLedger(root: string): CodexGenerationLedger {
109
- if (!existsSync(ledgerPath(root))) return emptyLedger()
110
- return parseLedger(JSON.parse(readFileSync(ledgerPath(root), 'utf8')))
111
- }
112
-
113
- type LedgerWrite = Omit<CodexGenerationLedger, 'revision' | 'version'>
114
- function writeLedger(root: string, previous: CodexGenerationLedger, next: LedgerWrite): CodexGenerationLedger {
115
- const observed = readCodexGenerationLedger(root)
116
- if (observed.revision !== previous.revision) throw new Error('Codex generation ledger CAS lost; retry')
117
- const value: CodexGenerationLedger = {
118
- ...next,
119
- version: 3,
120
- revision: previous.revision + 1,
121
- }
122
- mkdirSync(dirname(ledgerPath(root)), { recursive: true, mode: 0o700 })
123
- const temp = `${ledgerPath(root)}.${process.pid}.${randomUUID()}.tmp`
124
- writeFileSync(temp, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 })
125
- renameSync(temp, ledgerPath(root))
126
- return value
127
- }
128
-
129
- type LedgerLockOwner = Readonly<{ pid: number; startToken: string; nonce: string | null }>
130
- type LedgerLockObservation = Readonly<{ owner: LedgerLockOwner | null; mtimeMs: number }>
131
-
132
- function readLedgerLockOwner(path: string): LedgerLockOwner | null {
133
- try {
134
- const value: unknown = JSON.parse(readFileSync(path, 'utf8'))
135
- if (!isReservation(value)) return null
136
- const nonce = (value as { nonce?: unknown }).nonce
137
- return typeof nonce === 'string' && nonce ? { ...(value as GenerationReservation), nonce } : { ...(value as GenerationReservation), nonce: null }
138
- } catch { return null }
139
- }
140
-
141
- function ledgerLockObservation(root: string): LedgerLockObservation | null {
142
- try { return { owner: readLedgerLockOwner(lockOwnerPath(root)), mtimeMs: statSync(lockPath(root)).mtimeMs } }
143
- catch { return null }
144
- }
145
-
146
- const sameLedgerLockOwner = (left: LedgerLockOwner | null, right: LedgerLockOwner | null) =>
147
- !!left && !!right && left.pid === right.pid && left.startToken === right.startToken && left.nonce === right.nonce
148
-
149
- function staleLedgerLock(observation: LedgerLockObservation): boolean {
150
- const owner = observation.owner
151
- if (owner) return processStartToken(owner.pid) !== owner.startToken
152
- // A creator publishes owner.json immediately after mkdir. A short grace avoids mistaking that tiny window
153
- // for an abandoned lock, while a crashed creator remains recoverable instead of wedging every retry.
154
- return Date.now() - observation.mtimeMs > 500
155
- }
156
-
157
- function releaseLedgerLock(root: string, owner: LedgerLockOwner): void {
158
- if (!sameLedgerLockOwner(readLedgerLockOwner(lockOwnerPath(root)), owner) || existsSync(lockReaperPath(root))) return
159
- rmSync(lockPath(root), { recursive: true, force: true })
160
- }
161
-
162
- function releaseLedgerReaper(root: string, owner: LedgerLockOwner): void {
163
- if (sameLedgerLockOwner(readLedgerLockOwner(lockReaperOwnerPath(root)), owner))
164
- rmSync(lockReaperPath(root), { recursive: true, force: true })
165
- }
166
-
167
- function reapStaleLedgerLock(root: string, observed: LedgerLockObservation): boolean {
168
- const token = processStartToken(process.pid)
169
- if (!token) throw new Error('cannot prove coordinator process identity for Codex generation ledger reaper')
170
- const reaper: LedgerLockOwner = { pid: process.pid, startToken: token, nonce: randomUUID() }
171
- try {
172
- mkdirSync(lockReaperPath(root), { mode: 0o700 })
173
- try { writeFileSync(lockReaperOwnerPath(root), `${JSON.stringify(reaper)}\n`, { mode: 0o600 }) }
174
- catch (error) { rmSync(lockReaperPath(root), { recursive: true, force: true }); throw error }
175
- } catch (error) {
176
- if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error
177
- const existing = readLedgerLockOwner(lockReaperOwnerPath(root))
178
- let stale = existing ? processStartToken(existing.pid) !== existing.startToken : false
179
- if (!existing) { try { stale = Date.now() - statSync(lockReaperPath(root)).mtimeMs > 500 } catch { stale = false } }
180
- if (stale) rmSync(lockReaperPath(root), { recursive: true, force: true })
181
- return false
182
- }
183
- const current = ledgerLockObservation(root)
184
- const sameObservedOwner = !!current && ((!current.owner && !observed.owner) || sameLedgerLockOwner(current.owner, observed.owner))
185
- // Creating reaper/ updates the parent directory mtime, so an ownerless creator crash is judged from the
186
- // stable observation made before that child directory existed. An owned lock is judged from its identity.
187
- const remainsStale = current && current.owner ? staleLedgerLock(current) : staleLedgerLock(observed)
188
- if (!sameObservedOwner || !current || !remainsStale ||
189
- !sameLedgerLockOwner(readLedgerLockOwner(lockReaperOwnerPath(root)), reaper)) {
190
- releaseLedgerReaper(root, reaper)
191
- return false
192
- }
193
- // The reaper lease is an atomic election inside the still-present lock directory. A live owner sees that
194
- // lease and never releases the parent; another reaper cannot remove this one while its exact PID/start lives.
195
- rmSync(lockPath(root), { recursive: true, force: true })
196
- return true
197
- }
198
-
199
- function createLedgerLock(root: string): LedgerLockOwner | null {
200
- const lock = lockPath(root)
201
- const token = processStartToken(process.pid)
202
- if (!token) throw new Error('cannot prove coordinator process identity for Codex generation ledger lock')
203
- const owner: LedgerLockOwner = { pid: process.pid, startToken: token, nonce: randomUUID() }
204
- mkdirSync(root, { recursive: true, mode: 0o700 })
205
- try { mkdirSync(lock, { mode: 0o700 }) }
206
- catch (error) {
207
- if ((error as NodeJS.ErrnoException).code === 'EEXIST') return null
208
- throw error
209
- }
210
- try { writeFileSync(lockOwnerPath(root), `${JSON.stringify(owner)}\n`, { mode: 0o600 }) }
211
- catch (error) { rmSync(lock, { recursive: true, force: true }); throw error }
212
- return owner
213
- }
214
-
215
- async function acquireLedgerLock(root: string): Promise<LedgerLockOwner> {
216
- const deadline = Date.now() + 10_000
217
- for (;;) {
218
- const owner = createLedgerLock(root)
219
- if (owner) return owner
220
- const observed = ledgerLockObservation(root)
221
- if (observed && staleLedgerLock(observed) && reapStaleLedgerLock(root, observed)) continue
222
- if (Date.now() >= deadline) throw new Error('Codex generation ledger lock is busy; retry')
223
- await sleep(25)
224
- }
225
- }
226
-
227
- function acquireLedgerLockSync(root: string): LedgerLockOwner {
228
- const deadline = Date.now() + 5_000
229
- const sleeper = new Int32Array(new SharedArrayBuffer(4))
230
- for (;;) {
231
- const owner = createLedgerLock(root)
232
- if (owner) return owner
233
- const observed = ledgerLockObservation(root)
234
- if (observed && staleLedgerLock(observed) && reapStaleLedgerLock(root, observed)) continue
235
- if (Date.now() >= deadline) throw new Error('Codex generation ledger lock is busy; retry')
236
- Atomics.wait(sleeper, 0, 0, 10)
237
- }
238
- }
239
-
240
- async function withLedgerLock<T>(root: string, body: () => Promise<T>): Promise<T> {
241
- const owner = await acquireLedgerLock(root)
242
- try { return await body() }
243
- finally { releaseLedgerLock(root, owner) }
244
- }
245
-
246
- function withLedgerLockSync<T>(root: string, body: () => T): T {
247
- const owner = acquireLedgerLockSync(root)
248
- try { return body() }
249
- finally { releaseLedgerLock(root, owner) }
250
- }
251
-
252
- type EndpointIdentity = { pid: number; token: string; socketDev: number; socketIno: number }
253
-
254
- function endpointIdentity(endpoint: CodexGenerationEndpoint): EndpointIdentity | null {
255
- try {
256
- const pid = Number(readFileSync(endpoint.pidFile, 'utf8').trim())
257
- const detached = verifyDetachedRuntime(pid, endpoint.receiptFile)
258
- const socket = statSync(endpoint.socketPath)
259
- if (!detached.ok || !socket.isSocket()) return null
260
- return {
261
- pid,
262
- token: `${detached.identity.pid}|${detached.identity.startToken}|${detached.identity.processGroupId}|${detached.identity.linuxSessionId ?? '-'}`,
263
- socketDev: socket.dev,
264
- socketIno: socket.ino,
265
- }
266
- } catch { return null }
267
- }
268
-
269
- function hasLegacyResidue(root: string): boolean {
270
- const endpoint = legacyCodexGenerationEndpoint(root)
271
- return [endpoint.pidFile, endpoint.receiptFile, endpoint.socketPath].some(existsSync)
272
- }
273
-
274
- // A generation whose recorded process is provably gone — the host restarted, the OOM killer fired, a /tmp sweep
275
- // took the socket with it. Death is a POSITIVE fact, not the absence of proof: such a root holds no threads, no
276
- // peers, and no protective references, so nothing it once carried can be lost by retiring it. Everything else
277
- // that fails `endpointIdentity` is AMBIGUOUS — a live process we can no longer address — and keeps its root.
278
- function goneGeneration(endpoint: CodexGenerationEndpoint): boolean {
279
- if (endpointIdentity(endpoint)) return false
280
- let pid: number
281
- try { pid = Number(readFileSync(endpoint.pidFile, 'utf8').trim()) }
282
- catch { return false }
283
- return Number.isInteger(pid) && pid > 0 && detachedRuntimeIsGone(pid, endpoint.receiptFile)
284
- }
285
-
286
- // Retire a gone generation inside the caller's ledger lock, returning the published ledger (null when the
287
- // generation is still there to serve or merely unaddressable). Its bindings deliberately survive: a session's
288
- // conversation lives in Codex's on-disk rollout, so those rows are stale pointers to re-pin, not lost threads.
289
- function retireGoneGenerationLocked(root: string, previous: CodexGenerationLedger, id: string): CodexGenerationLedger | null {
290
- const generation = previous.generations[id]
291
- if (!generation || generation.state === 'reclaimed' || !goneGeneration(generation.endpoint)) return null
292
- return writeLedger(root, previous, {
293
- current: previous.current === id ? null : previous.current,
294
- pending: previous.pending === id ? null : previous.pending,
295
- generations: { ...previous.generations, [id]: { state: 'reclaimed', endpoint: generation.endpoint } },
296
- bindings: previous.bindings,
297
- })
298
- }
299
-
300
- function bootstrapBindings(root: string, generationId: string): Record<string, CodexGenerationBinding> {
301
- const result: Record<string, CodexGenerationBinding> = {}
302
- const sessions = join(root, 'sessions')
303
- let ids: string[] = []
304
- try { ids = readdirSync(sessions) } catch { return result }
305
- for (const sessionId of ids) {
306
- try {
307
- const record = JSON.parse(readFileSync(join(sessions, sessionId, 'session.json'), 'utf8')) as Record<string, unknown>
308
- const harness = record.harness
309
- const threadId = record.harness_session_id
310
- if (record.governed === true && (harness === 'codex' || harness === 'codex-headless') && typeof threadId === 'string' && threadId) {
311
- result[sessionId] = { generationId, threadId }
312
- }
313
- } catch { /* unreadable records remain unbound and fail loud at use */ }
314
- }
315
- return result
316
- }
317
-
318
- function pendingBindingHasRecord(root: string, sessionId: string, binding: CodexGenerationBinding): boolean {
319
- if (!sessionId || sessionId.includes('/') || sessionId.includes('\\')) return false
320
- try {
321
- const record = JSON.parse(readFileSync(join(root, 'sessions', sessionId, 'session.json'), 'utf8')) as Record<string, unknown>
322
- const harness = record.harness
323
- return record.governed === true && (harness === 'codex' || harness === 'codex-headless') && record.harness_session_id === binding.threadId
324
- } catch { return false }
325
- }
326
-
327
- function bindingProtectsGeneration(root: string, sessionId: string, binding: CodexGenerationBinding): boolean {
328
- return binding.phase === undefined || pendingBindingHasRecord(root, sessionId, binding)
329
- }
330
-
331
- function bootstrapLedger(root: string): CodexGenerationLedger {
332
- const legacy = legacyCodexGenerationEndpoint(root)
333
- if (!hasLegacyResidue(root)) return emptyLedger()
334
- // Residue left by a legacy root that has since died enters the ledger already reclaimed rather than blocking
335
- // the bootstrap: it has nothing to drain. Its governed bindings are still recorded so those sessions resolve
336
- // to a stale pointer they can re-pin, instead of losing the thread they own.
337
- const gone = goneGeneration(legacy)
338
- if (!gone && !endpointIdentity(legacy))
339
- throw new Error('legacy Codex root is present but its exact detached PID/start/receipt/socket identity is unproven')
340
- return {
341
- version: 3,
342
- revision: 0,
343
- current: null,
344
- pending: null,
345
- generations: { [legacy.id]: { state: gone ? 'reclaimed' : 'draining', endpoint: legacy } },
346
- bindings: bootstrapBindings(root, legacy.id),
347
- }
348
- }
349
-
350
- async function waitForEndpoint(endpoint: CodexGenerationEndpoint, timeoutMs = 10_000): Promise<boolean> {
351
- const deadline = Date.now() + timeoutMs
352
- while (Date.now() < deadline) {
353
- if (endpointIdentity(endpoint)) {
354
- const listening = await new Promise<boolean>((resolve) => {
355
- let settled = false
356
- const done = (value: boolean) => { if (!settled) { settled = true; client?.destroy(); resolve(value) } }
357
- let client: Socket | null = null
358
- try { client = createConnection(endpoint.socketPath) }
359
- catch { return resolve(false) }
360
- client.once('connect', () => done(true))
361
- client.once('error', () => done(false))
362
- })
363
- if (listening) return true
364
- }
365
- await sleep(25)
366
- }
367
- return false
368
- }
369
-
370
- type EnsureAction =
371
- | { kind: 'start'; endpoint: CodexGenerationEndpoint }
372
- | { kind: 'publish'; endpoint: CodexGenerationEndpoint }
373
- | { kind: 'wait' }
374
- | { kind: 'current'; endpoint: CodexGenerationEndpoint }
375
-
376
- function publishPendingGeneration(root: string, endpoint: CodexGenerationEndpoint): Promise<CodexGenerationEndpoint> {
377
- return withLedgerLock(root, async () => {
378
- const previous = readCodexGenerationLedger(root)
379
- if (previous.current) {
380
- const current = previous.generations[previous.current]
381
- if (!current || current.state !== 'current' || !endpointIdentity(current.endpoint))
382
- throw new Error('another Codex generation switch published an unproven current endpoint')
383
- return current.endpoint
384
- }
385
- if (previous.pending !== endpoint.id || !endpointIdentity(endpoint))
386
- throw new Error('Codex generation switch CAS lost or candidate identity changed before publication')
387
- const generations = {
388
- ...previous.generations,
389
- [endpoint.id]: { state: 'current' as const, endpoint },
390
- }
391
- writeLedger(root, previous, { current: endpoint.id, pending: null, generations, bindings: previous.bindings })
392
- return endpoint
393
- })
394
- }
395
-
396
- export async function ensureCodexCurrentGeneration(root: string, start: (endpoint: CodexGenerationEndpoint) => Promise<void>): Promise<CodexGenerationEndpoint> {
397
- const deadline = Date.now() + 30_000
398
- for (;;) {
399
- const action = await withLedgerLock(root, async (): Promise<EnsureAction> => {
400
- let previous = readCodexGenerationLedger(root)
401
- if (previous.revision === 0 && !existsSync(ledgerPath(root))) {
402
- const bootstrapped = bootstrapLedger(root)
403
- previous = writeLedger(root, previous, bootstrapped)
404
- }
405
- if (previous.current) {
406
- const generation = previous.generations[previous.current]
407
- if (generation && generation.state === 'current' && endpointIdentity(generation.endpoint))
408
- return { kind: 'current', endpoint: generation.endpoint }
409
- // A canonical root that is provably gone is retired here and replaced by the rest of this same call:
410
- // refusing forever would make one host restart permanently un-routable. Anything else stays a refusal.
411
- const healed = retireGoneGenerationLocked(root, previous, previous.current)
412
- if (!healed) throw new Error('canonical Codex generation is missing, replaced, or unproven; refusing to route new traffic')
413
- previous = healed
414
- }
415
- if (previous.pending) {
416
- const pending = previous.generations[previous.pending]
417
- if (!pending || pending.state !== 'starting') throw new Error('Codex generation ledger pending switch is malformed')
418
- // A coordinator can crash after the detached endpoint proves live but before the CAS publish. The
419
- // durable pending record is sufficient for any later coordinator to complete that same switch.
420
- if (endpointIdentity(pending.endpoint)) return { kind: 'publish', endpoint: pending.endpoint }
421
- if (pending.reservation && processStartToken(pending.reservation.pid) === pending.reservation.startToken)
422
- return { kind: 'wait' }
423
- const generations = { ...previous.generations }
424
- delete generations[pending.endpoint.id]
425
- writeLedger(root, previous, { current: null, pending: null, generations, bindings: previous.bindings })
426
- }
427
- const latest = readCodexGenerationLedger(root)
428
- const endpoint = newEndpoint(root)
429
- const startToken = processStartToken(process.pid)
430
- if (!startToken) throw new Error('cannot prove coordinator process identity for Codex generation reservation')
431
- mkdirSync(dirname(endpoint.pidFile), { recursive: true, mode: 0o700 })
432
- writeLedger(root, latest, {
433
- current: latest.current,
434
- pending: endpoint.id,
435
- generations: { ...latest.generations, [endpoint.id]: { state: 'starting', endpoint, reservation: { pid: process.pid, startToken } } },
436
- bindings: latest.bindings,
437
- })
438
- return { kind: 'start', endpoint }
439
- })
440
- if (action.kind === 'current') return action.endpoint
441
- if (action.kind === 'publish') return publishPendingGeneration(root, action.endpoint)
442
- if (action.kind === 'wait') {
443
- if (Date.now() >= deadline) throw new Error('Codex generation switch reservation owner is still live but did not publish; retry after it exits')
444
- await sleep(50)
445
- continue
446
- }
447
- try { await start(action.endpoint) }
448
- catch (error) {
449
- await withLedgerLock(root, async () => {
450
- const previous = readCodexGenerationLedger(root)
451
- if (previous.pending !== action.endpoint.id) return
452
- if (endpointIdentity(action.endpoint)) return
453
- const generations = { ...previous.generations }
454
- delete generations[action.endpoint.id]
455
- writeLedger(root, previous, { current: previous.current, pending: null, generations, bindings: previous.bindings })
456
- })
457
- throw error
458
- }
459
- if (!await waitForEndpoint(action.endpoint)) {
460
- await withLedgerLock(root, async () => {
461
- const previous = readCodexGenerationLedger(root)
462
- if (previous.pending !== action.endpoint.id || endpointIdentity(action.endpoint)) return
463
- const generations = { ...previous.generations }
464
- delete generations[action.endpoint.id]
465
- writeLedger(root, previous, { current: previous.current, pending: null, generations, bindings: previous.bindings })
466
- })
467
- throw new Error(`candidate Codex generation ${action.endpoint.id} did not prove a live detached endpoint; current pointer was not switched`)
468
- }
469
- return publishPendingGeneration(root, action.endpoint)
470
- }
471
- }
472
-
473
- export function bindCodexGeneration(root: string, sessionId: string, threadId: string, generationId: string | null): void {
474
- withLedgerLockSync(root, () => {
475
- const previous = readCodexGenerationLedger(root)
476
- if (generationId === null && previous.revision === 0 && !previous.current && !Object.keys(previous.generations).length) return
477
- const bindings = { ...previous.bindings }
478
- if (generationId === null) delete bindings[sessionId]
479
- else {
480
- const generation = previous.generations[generationId]
481
- if (!generation || generation.state === 'reclaimed') throw new Error(`Codex generation ${generationId} is absent or reclaimed`)
482
- bindings[sessionId] = { generationId, threadId }
483
- }
484
- writeLedger(root, previous, { current: previous.current, pending: previous.pending, generations: previous.generations, bindings })
485
- })
486
- }
487
-
488
- // Native thread creation and session.json persistence are separate writes. A pending registration can be
489
- // recovered only when the durable record names the same exact thread; otherwise it cannot pin a root forever.
490
- export function prepareCodexGenerationRegistration(root: string, sessionId: string, threadId: string, generationId: string): void {
491
- withLedgerLockSync(root, () => {
492
- const previous = readCodexGenerationLedger(root)
493
- const generation = previous.generations[generationId]
494
- if (!generation || generation.state === 'reclaimed') throw new Error(`Codex generation ${generationId} is absent or reclaimed`)
495
- const existing = previous.bindings[sessionId]
496
- if (existing) {
497
- if (existing.generationId !== generationId || existing.threadId !== threadId)
498
- throw new Error(`Codex session ${sessionId} already has a different exact generation binding`)
499
- return
500
- }
501
- const bindings = { ...previous.bindings, [sessionId]: { generationId, threadId, phase: 'record-pending' as const } }
502
- writeLedger(root, previous, { current: previous.current, pending: previous.pending, generations: previous.generations, bindings })
503
- })
504
- }
505
-
506
- export function commitCodexGenerationRegistration(root: string, sessionId: string, threadId: string, generationId: string): void {
507
- withLedgerLockSync(root, () => {
508
- const previous = readCodexGenerationLedger(root)
509
- const binding = previous.bindings[sessionId]
510
- const generation = previous.generations[generationId]
511
- if (!binding || binding.generationId !== generationId || binding.threadId !== threadId || !generation || generation.state === 'reclaimed')
512
- throw new Error(`Codex session ${sessionId} registration no longer names its exact generation and thread`)
513
- if (binding.phase !== 'record-pending') return
514
- const bindings = { ...previous.bindings, [sessionId]: { generationId, threadId } }
515
- writeLedger(root, previous, { current: previous.current, pending: previous.pending, generations: previous.generations, bindings })
516
- })
517
- }
518
-
519
- // Close has the inverse crash boundary: retain the exact binding while its record still exists, but mark it
520
- // record-removing before any destructive step so an interrupted removal cannot leave a committed ghost binding.
521
- export function prepareCodexGenerationClose(root: string, sessionId: string, threadId: string): void {
522
- withLedgerLockSync(root, () => {
523
- const previous = readCodexGenerationLedger(root)
524
- if (previous.revision === 0 && !previous.current && !Object.keys(previous.generations).length) return
525
- const binding = previous.bindings[sessionId]
526
- if (!binding || binding.threadId !== threadId) throw new Error(`Codex session ${sessionId} has no exact generation binding to close`)
527
- if (binding.phase === 'record-removing') return
528
- const generation = previous.generations[binding.generationId]
529
- // A retired root can neither route this thread nor be pinned by it, so there is nothing for the removal
530
- // marker to protect: drop the binding outright and let close proceed. An ABSENT generation is corruption.
531
- if (!generation) throw new Error(`Codex session ${sessionId} binding names an absent generation`)
532
- if (generation.state === 'reclaimed') {
533
- const remaining = { ...previous.bindings }
534
- delete remaining[sessionId]
535
- writeLedger(root, previous, { current: previous.current, pending: previous.pending, generations: previous.generations, bindings: remaining })
536
- return
537
- }
538
- const bindings = { ...previous.bindings, [sessionId]: { ...binding, phase: 'record-removing' as const } }
539
- writeLedger(root, previous, { current: previous.current, pending: previous.pending, generations: previous.generations, bindings })
540
- })
541
- }
542
-
543
- // Re-pin an existing exact binding onto another live generation. The transaction phase is carried over, so a
544
- // registration or close that is mid-flight keeps its crash boundary while its route is corrected.
545
- export function repinCodexGeneration(root: string, sessionId: string, threadId: string, generationId: string): void {
546
- withLedgerLockSync(root, () => {
547
- const previous = readCodexGenerationLedger(root)
548
- const binding = previous.bindings[sessionId]
549
- if (!binding || binding.threadId !== threadId) throw new Error(`Codex session ${sessionId} has no exact binding to re-pin`)
550
- const generation = previous.generations[generationId]
551
- if (!generation || generation.state === 'reclaimed') throw new Error(`Codex generation ${generationId} is absent or reclaimed`)
552
- if (binding.generationId === generationId) return
553
- const bindings = { ...previous.bindings, [sessionId]: { ...binding, generationId } }
554
- writeLedger(root, previous, { current: previous.current, pending: previous.pending, generations: previous.generations, bindings })
555
- })
556
- }
557
-
558
- // The resume boundary. A binding to a LIVE generation still routes only there — an existing conversation is
559
- // never moved out from under the client that holds it. But a binding whose root is gone names a process, not a
560
- // conversation: the thread's durable home is its on-disk rollout, which any generation can load. So the gone
561
- // root is retired and the session is re-pinned to the canonical one, which loads that same rollout. Missing,
562
- // mismatched, ambiguous, or unprotected bindings remain refusals — resume never invents a route.
563
- export async function resolveCodexGenerationForResume(
564
- root: string,
565
- sessionId: string,
566
- threadId: string,
567
- start: (endpoint: CodexGenerationEndpoint) => Promise<void>,
568
- ): Promise<CodexGenerationEndpoint | null> {
569
- const ledger = readCodexGenerationLedger(root)
570
- const binding = ledger.bindings[sessionId]
571
- if (!binding || binding.threadId !== threadId || !bindingProtectsGeneration(root, sessionId, binding)) return null
572
- const generation = ledger.generations[binding.generationId]
573
- if (!generation) return null
574
- if (generation.state !== 'reclaimed') {
575
- if (endpointIdentity(generation.endpoint)) return generation.endpoint
576
- const retired = await withLedgerLock(root, async () => {
577
- const previous = readCodexGenerationLedger(root)
578
- if (previous.bindings[sessionId]?.generationId !== binding.generationId) return false
579
- const observed = previous.generations[binding.generationId]
580
- if (!observed) return false
581
- return observed.state === 'reclaimed' || !!retireGoneGenerationLocked(root, previous, binding.generationId)
582
- })
583
- if (!retired) return null
584
- }
585
- const current = await ensureCodexCurrentGeneration(root, start)
586
- repinCodexGeneration(root, sessionId, threadId, current.id)
587
- return current
588
- }
589
-
590
- export function resolveCodexGenerationForSession(root: string, sessionId: string, threadId: string): CodexGenerationEndpoint | null {
591
- const ledger = readCodexGenerationLedger(root)
592
- const binding = ledger.bindings[sessionId]
593
- if (!binding || binding.threadId !== threadId || !bindingProtectsGeneration(root, sessionId, binding)) return null
594
- const generation = ledger.generations[binding.generationId]
595
- return generation && generation.state !== 'reclaimed' ? generation.endpoint : null
596
- }
597
-
598
- export function codexGenerationBindingForSession(root: string, sessionId: string): CodexGenerationBinding | null {
599
- return readCodexGenerationLedger(root).bindings[sessionId] ?? null
600
- }
601
-
602
- export function currentCodexGeneration(root: string): CodexGenerationEndpoint | null {
603
- const ledger = readCodexGenerationLedger(root)
604
- const generation = ledger.current ? ledger.generations[ledger.current] : null
605
- return generation?.state === 'current' ? generation.endpoint : null
606
- }
607
-
608
- export function codexGenerationEndpoints(root: string): CodexGenerationEndpoint[] {
609
- return Object.values(readCodexGenerationLedger(root).generations)
610
- .filter((generation) => generation.state !== 'reclaimed')
611
- .map((generation) => generation.endpoint)
612
- }
613
-
614
- export async function reclaimDrainingCodexGeneration(
615
- root: string,
616
- generationId: string,
617
- census: (endpoint: CodexGenerationEndpoint) => Promise<{ healthy: boolean; referenceIds: string[]; peerCount: number }>,
618
- ): Promise<{ reclaimed: boolean; reason: string }> {
619
- return withLedgerLock(root, async () => {
620
- const previous = readCodexGenerationLedger(root)
621
- const generation = previous.generations[generationId]
622
- if (!generation) return { reclaimed: false, reason: 'generation is absent' }
623
- if (generation.state === 'reclaimed') return { reclaimed: true, reason: 'generation is already reclaimed' }
624
- if (generation.state !== 'draining') return { reclaimed: false, reason: 'only draining generations may be reclaimed' }
625
- const identity = endpointIdentity(generation.endpoint)
626
- if (!identity) return { reclaimed: false, reason: 'exact PID/start/receipt/socket identity is unproven' }
627
- const bound = Object.entries(previous.bindings).filter(([sessionId, binding]) => binding.generationId === generationId && bindingProtectsGeneration(root, sessionId, binding))
628
- if (bound.length) return { reclaimed: false, reason: `generation retains ${bound.length} governed binding(s)` }
629
- const before = await census(generation.endpoint)
630
- if (!before.healthy) return { reclaimed: false, reason: 'native reference census is unhealthy' }
631
- if (before.referenceIds.length || before.peerCount) return { reclaimed: false, reason: `generation retains ${before.referenceIds.length} loaded references and ${before.peerCount} peers` }
632
- const again = endpointIdentity(generation.endpoint)
633
- if (!again || again.token !== identity.token || again.socketDev !== identity.socketDev || again.socketIno !== identity.socketIno)
634
- return { reclaimed: false, reason: 'generation identity changed during reclaim guard' }
635
- const after = await census(generation.endpoint)
636
- if (!after.healthy || after.referenceIds.length || after.peerCount)
637
- return { reclaimed: false, reason: 'generation reference census changed during reclaim guard' }
638
- try { process.kill(identity.pid, 'SIGTERM') }
639
- catch (error) { return { reclaimed: false, reason: `could not stop exact drained generation: ${(error as Error).message}` } }
640
- for (let attempt = 0; attempt < 100 && processStartToken(identity.pid); attempt++) await sleep(20)
641
- if (processStartToken(identity.pid)) return { reclaimed: false, reason: 'exact drained generation did not exit after SIGTERM' }
642
- const generations = { ...previous.generations, [generationId]: { state: 'reclaimed' as const, endpoint: generation.endpoint } }
643
- writeLedger(root, previous, { current: previous.current, pending: previous.pending, generations, bindings: previous.bindings })
644
- return { reclaimed: true, reason: 'exact drained generation reclaimed after zero-reference census' }
645
- })
646
- }