@gajae-code/coding-agent 0.17.1 → 0.17.4

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 (375) hide show
  1. package/CHANGELOG.md +266 -0
  2. package/dist/types/async/job-manager.d.ts +7 -6
  3. package/dist/types/cli/daemon-cli.d.ts +3 -1
  4. package/dist/types/cli/github-release.d.ts +17 -0
  5. package/dist/types/cli/public-command-entry.d.ts +56 -0
  6. package/dist/types/cli/public-command-errors.d.ts +126 -0
  7. package/dist/types/cli/public-command-evidence.d.ts +99 -0
  8. package/dist/types/cli/public-command-help.d.ts +65 -0
  9. package/dist/types/cli/public-command-registry.d.ts +69 -0
  10. package/dist/types/cli/session-picker.d.ts +1 -1
  11. package/dist/types/cli/setup-cli.d.ts +1 -0
  12. package/dist/types/cli/skills-cli.d.ts +5 -0
  13. package/dist/types/cli.d.ts +3 -0
  14. package/dist/types/commands/daemon.d.ts +4 -50
  15. package/dist/types/commands/sdk.d.ts +20 -37
  16. package/dist/types/commands/setup.d.ts +3 -0
  17. package/dist/types/commands/skills.d.ts +9 -0
  18. package/dist/types/config/autorouting-tier-map.d.ts +24 -0
  19. package/dist/types/config/file-lock.d.ts +10 -1
  20. package/dist/types/config/model-profile-activation.d.ts +63 -4
  21. package/dist/types/config/model-registry.d.ts +20 -2
  22. package/dist/types/config/settings-schema.d.ts +11 -0
  23. package/dist/types/coordinator-mcp/question-state.d.ts +6 -1
  24. package/dist/types/coordinator-mcp/server.d.ts +3 -0
  25. package/dist/types/daemon/operator-contract.d.ts +5 -1
  26. package/dist/types/eval/js/worker-protocol.d.ts +2 -0
  27. package/dist/types/extensibility/extensions/types.d.ts +12 -0
  28. package/dist/types/extensibility/gjc-plugins/registry.d.ts +4 -0
  29. package/dist/types/extensibility/runtime-skill-discovery.d.ts +18 -0
  30. package/dist/types/extensibility/shared-events.d.ts +3 -1
  31. package/dist/types/gjc-runtime/session-state-lock.d.ts +61 -0
  32. package/dist/types/internal-urls/embedded-protocol.d.ts +9 -0
  33. package/dist/types/internal-urls/index.d.ts +1 -0
  34. package/dist/types/internal-urls/parse.d.ts +9 -1
  35. package/dist/types/main.d.ts +22 -0
  36. package/dist/types/modes/acp/acp-agent.d.ts +94 -1
  37. package/dist/types/modes/acp/startup-provenance.d.ts +16 -0
  38. package/dist/types/modes/components/custom-provider-wizard.d.ts +19 -1
  39. package/dist/types/modes/components/session-selector.d.ts +5 -2
  40. package/dist/types/modes/components/thinking-selector.d.ts +2 -1
  41. package/dist/types/modes/components/user-message-selector.d.ts +5 -2
  42. package/dist/types/modes/controllers/command-controller.d.ts +1 -1
  43. package/dist/types/modes/controllers/selector-controller.d.ts +1 -1
  44. package/dist/types/modes/interactive-mode.d.ts +1 -1
  45. package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +11 -1
  46. package/dist/types/modes/shared/agent-wire/workflow-gate-types.d.ts +14 -1
  47. package/dist/types/modes/types.d.ts +2 -2
  48. package/dist/types/runtime/process-lifecycle.d.ts +2 -0
  49. package/dist/types/runtime-mcp/manager.d.ts +56 -0
  50. package/dist/types/runtime-mcp/pool.d.ts +17 -0
  51. package/dist/types/runtime-mcp/redaction.d.ts +20 -0
  52. package/dist/types/runtime-mcp/startup-policy.d.ts +6 -0
  53. package/dist/types/runtime-mcp/tool-bridge.d.ts +9 -0
  54. package/dist/types/runtime-mcp/transports/http.d.ts +11 -0
  55. package/dist/types/sdk/acp/adapter.d.ts +27 -0
  56. package/dist/types/sdk/acp/final-text.d.ts +8 -0
  57. package/dist/types/sdk/broker/broker.d.ts +13 -2
  58. package/dist/types/sdk/broker/ensure.d.ts +46 -1
  59. package/dist/types/sdk/broker/identity.d.ts +3 -1
  60. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +23 -3
  61. package/dist/types/sdk/broker/lifecycle.d.ts +15 -8
  62. package/dist/types/sdk/broker/runtime.d.ts +1 -1
  63. package/dist/types/sdk/broker/session-index.d.ts +22 -3
  64. package/dist/types/sdk/broker/spawn-authority.d.ts +4 -0
  65. package/dist/types/sdk/broker/stderr-drainer.d.ts +13 -0
  66. package/dist/types/sdk/bus/index.d.ts +19 -0
  67. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +3 -1
  68. package/dist/types/sdk/bus/telegram-daemon.d.ts +5 -0
  69. package/dist/types/sdk/cli/master-cli.d.ts +6 -10
  70. package/dist/types/sdk/cli/rows.d.ts +19 -1
  71. package/dist/types/sdk/cli/session-cli.d.ts +22 -5
  72. package/dist/types/sdk/client/client.d.ts +2 -0
  73. package/dist/types/sdk/guides/cli.d.ts +3 -4
  74. package/dist/types/sdk/guides/verify.d.ts +2 -0
  75. package/dist/types/sdk/host/host.d.ts +9 -0
  76. package/dist/types/sdk/host/session-runtime.d.ts +31 -0
  77. package/dist/types/sdk/host/types.d.ts +10 -0
  78. package/dist/types/sdk/index.d.ts +1 -0
  79. package/dist/types/sdk/prompt-deadline-flush.d.ts +83 -0
  80. package/dist/types/sdk/prompt-deadline-manager.d.ts +38 -1
  81. package/dist/types/sdk/prompt-failure.d.ts +30 -1
  82. package/dist/types/sdk/prompt-status.d.ts +8 -0
  83. package/dist/types/sdk/prompt-tool-boundary.d.ts +73 -0
  84. package/dist/types/sdk/router/replay-retention-gap-cache.d.ts +13 -0
  85. package/dist/types/sdk/router/session-router.d.ts +2 -0
  86. package/dist/types/sdk/startup-capability.d.ts +2 -0
  87. package/dist/types/sdk/transport/relay.d.ts +1 -0
  88. package/dist/types/sdk/transport/serve-cli.d.ts +73 -2
  89. package/dist/types/sdk/transport/stdio.d.ts +5 -1
  90. package/dist/types/session/agent-session.d.ts +168 -21
  91. package/dist/types/session/fallback-chain-controller.d.ts +6 -6
  92. package/dist/types/session/fold-coordinator.d.ts +17 -3
  93. package/dist/types/session/internal/managed-session-storage.d.ts +7 -1
  94. package/dist/types/session/session-manager.d.ts +64 -1
  95. package/dist/types/session/session-work-lease.d.ts +14 -0
  96. package/dist/types/setup/model-onboarding-guidance.d.ts +1 -1
  97. package/dist/types/setup/provider-onboarding.d.ts +81 -1
  98. package/dist/types/thinking.d.ts +3 -1
  99. package/dist/types/tools/bash.d.ts +37 -11
  100. package/dist/types/tools/job.d.ts +1 -1
  101. package/dist/types/tools/steer-fold.d.ts +31 -0
  102. package/dist/types/utils/git.d.ts +28 -2
  103. package/dist/types/utils/mupdf-embedded.d.ts +2 -0
  104. package/dist/types/utils/mupdf-wasm-embedded.d.ts +2 -0
  105. package/dist/types/utils/mupdf.d.ts +4 -0
  106. package/dist/types/utils/session-title-generation.d.ts +6 -0
  107. package/dist/types/utils/title-generator.d.ts +11 -2
  108. package/dist/types/web/scrapers/types.d.ts +3 -0
  109. package/package.json +18 -10
  110. package/scripts/build-binary.ts +9 -3
  111. package/scripts/compile-args.ts +0 -9
  112. package/scripts/embed-mupdf.ts +52 -0
  113. package/scripts/generate-sdk-operation-inventory.ts +31 -1
  114. package/scripts/vendor-markit.ts +196 -0
  115. package/src/async/job-manager.ts +60 -9
  116. package/src/cli/daemon-cli.ts +53 -18
  117. package/src/cli/github-release.ts +87 -5
  118. package/src/cli/mcp-cli.ts +38 -4
  119. package/src/cli/public-command-entry.ts +450 -0
  120. package/src/cli/public-command-errors.ts +581 -0
  121. package/src/cli/public-command-evidence.ts +717 -0
  122. package/src/cli/public-command-help.ts +360 -0
  123. package/src/cli/public-command-registry.ts +535 -0
  124. package/src/cli/session-picker.ts +2 -1
  125. package/src/cli/setup-cli.ts +9 -1
  126. package/src/cli/skills-cli.ts +46 -2
  127. package/src/cli.ts +61 -1
  128. package/src/commands/daemon.ts +65 -69
  129. package/src/commands/sdk.ts +232 -100
  130. package/src/commands/setup.ts +2 -0
  131. package/src/commands/skills.ts +11 -0
  132. package/src/config/autorouting-tier-map.ts +16 -0
  133. package/src/config/file-lock.ts +199 -68
  134. package/src/config/model-profile-activation.ts +406 -53
  135. package/src/config/model-registry.ts +204 -25
  136. package/src/config/settings-schema.ts +26 -2
  137. package/src/coordinator-mcp/policy.ts +10 -5
  138. package/src/coordinator-mcp/question-gate-codec.ts +17 -0
  139. package/src/coordinator-mcp/question-state.ts +175 -8
  140. package/src/coordinator-mcp/server.ts +528 -87
  141. package/src/coordinator-mcp/session-reaper.ts +6 -4
  142. package/src/crash/record-loader.ts +6 -2
  143. package/src/daemon/operator-contract.ts +26 -12
  144. package/src/debug/report-bundle.ts +114 -24
  145. package/src/defaults/gjc/skills/ultragoal/SKILL.md +1 -1
  146. package/src/eval/js/context-manager.ts +8 -8
  147. package/src/eval/js/worker-core.ts +9 -4
  148. package/src/eval/js/worker-protocol.ts +2 -0
  149. package/src/extensibility/extensions/runner.ts +52 -4
  150. package/src/extensibility/extensions/types.ts +10 -0
  151. package/src/extensibility/gjc-plugins/registry.ts +196 -37
  152. package/src/extensibility/runtime-skill-discovery.ts +61 -3
  153. package/src/extensibility/shared-events.ts +3 -1
  154. package/src/gjc-runtime/managed-owner-admission.ts +67 -50
  155. package/src/gjc-runtime/session-state-lock.ts +165 -24
  156. package/src/gjc-runtime/ultragoal-succession.ts +31 -3
  157. package/src/internal-urls/docs-index.generated.ts +21 -19
  158. package/src/internal-urls/embedded-protocol.ts +83 -0
  159. package/src/internal-urls/index.ts +1 -0
  160. package/src/internal-urls/parse.ts +21 -1
  161. package/src/internal-urls/router.ts +4 -2
  162. package/src/main.ts +123 -55
  163. package/src/modes/acp/acp-agent.ts +593 -47
  164. package/src/modes/acp/acp-event-mapper.ts +9 -0
  165. package/src/modes/acp/startup-provenance.ts +59 -0
  166. package/src/modes/components/custom-provider-wizard.ts +308 -7
  167. package/src/modes/components/model-selector.ts +31 -15
  168. package/src/modes/components/oauth-selector.ts +9 -0
  169. package/src/modes/components/session-selector.ts +47 -4
  170. package/src/modes/components/status-line/segments.ts +6 -1
  171. package/src/modes/components/thinking-selector.ts +7 -3
  172. package/src/modes/components/user-message-selector.ts +57 -15
  173. package/src/modes/controllers/command-controller.ts +58 -32
  174. package/src/modes/controllers/extension-ui-controller.ts +33 -7
  175. package/src/modes/controllers/input-controller.ts +4 -0
  176. package/src/modes/controllers/selector-controller.ts +177 -18
  177. package/src/modes/interactive-mode.ts +21 -7
  178. package/src/modes/runtime-init.ts +1 -0
  179. package/src/modes/shared/agent-wire/workflow-gate-broker.ts +124 -7
  180. package/src/modes/shared/agent-wire/workflow-gate-types.ts +16 -2
  181. package/src/modes/types.ts +2 -2
  182. package/src/prompts/agent-fragments/browser-aside.md +1 -0
  183. package/src/prompts/system/title-system.md +1 -1
  184. package/src/prompts/tools/bash.md +1 -1
  185. package/src/prompts/tools/job.md +1 -1
  186. package/src/prompts/tools/read.md +2 -0
  187. package/src/runtime/network-prewarm-service.test.ts +36 -1
  188. package/src/runtime/network-prewarm-service.ts +28 -3
  189. package/src/runtime/process-lifecycle.ts +22 -11
  190. package/src/runtime-mcp/config.ts +16 -1
  191. package/src/runtime-mcp/manager.ts +689 -55
  192. package/src/runtime-mcp/pool.test.ts +31 -0
  193. package/src/runtime-mcp/pool.ts +304 -28
  194. package/src/runtime-mcp/redaction.ts +42 -0
  195. package/src/runtime-mcp/startup-policy.ts +7 -0
  196. package/src/runtime-mcp/tool-bridge.ts +71 -0
  197. package/src/runtime-mcp/transports/http.ts +108 -25
  198. package/src/sdk/acp/adapter.ts +90 -9
  199. package/src/sdk/acp/final-text.ts +19 -2
  200. package/src/sdk/broker/broker.ts +378 -52
  201. package/src/sdk/broker/ensure.ts +97 -19
  202. package/src/sdk/broker/identity.ts +9 -1
  203. package/src/sdk/broker/lifecycle-ledger.ts +95 -31
  204. package/src/sdk/broker/lifecycle.ts +690 -141
  205. package/src/sdk/broker/runtime.ts +1 -1
  206. package/src/sdk/broker/session-index.ts +97 -4
  207. package/src/sdk/broker/spawn-authority.ts +35 -7
  208. package/src/sdk/broker/stderr-drainer.ts +59 -0
  209. package/src/sdk/bus/chat-daemon-control.ts +2 -2
  210. package/src/sdk/bus/chat-daemon-runtime.ts +1 -0
  211. package/src/sdk/bus/index.ts +721 -84
  212. package/src/sdk/bus/telegram-daemon-cli.ts +31 -2
  213. package/src/sdk/bus/telegram-daemon-contract.ts +3 -1
  214. package/src/sdk/bus/telegram-daemon.ts +10 -3
  215. package/src/sdk/cli/master-cli.ts +47 -36
  216. package/src/sdk/cli/rows.ts +30 -7
  217. package/src/sdk/cli/session-cli.ts +687 -184
  218. package/src/sdk/client/client.ts +18 -0
  219. package/src/sdk/guides/catalog.ts +2 -2
  220. package/src/sdk/guides/cli.ts +20 -62
  221. package/src/sdk/guides/verify.ts +14 -0
  222. package/src/sdk/host/host.ts +49 -5
  223. package/src/sdk/host/session-runtime.test.ts +636 -9
  224. package/src/sdk/host/session-runtime.ts +499 -118
  225. package/src/sdk/host/types.ts +12 -1
  226. package/src/sdk/host/websocket-transport.lifecycle.test.ts +84 -0
  227. package/src/sdk/host/websocket-transport.ts +40 -6
  228. package/src/sdk/index.ts +11 -0
  229. package/src/sdk/prompt-deadline-flush.ts +334 -0
  230. package/src/sdk/prompt-deadline-manager.ts +136 -4
  231. package/src/sdk/prompt-failure.ts +43 -1
  232. package/src/sdk/prompt-status.ts +8 -0
  233. package/src/sdk/prompt-tool-boundary.ts +121 -0
  234. package/src/sdk/protocol/operation-inventory.generated.json +106 -0
  235. package/src/sdk/router/replay-retention-gap-cache.ts +27 -0
  236. package/src/sdk/router/session-router.ts +31 -4
  237. package/src/sdk/session.ts +194 -16
  238. package/src/sdk/startup-capability.ts +1 -1
  239. package/src/sdk/transport/relay.ts +71 -6
  240. package/src/sdk/transport/serve-cli.ts +494 -49
  241. package/src/sdk/transport/socket.ts +12 -11
  242. package/src/sdk/transport/stdio.ts +7 -3
  243. package/src/session/agent-session.ts +2437 -400
  244. package/src/session/fallback-chain-controller.ts +6 -7
  245. package/src/session/fold-coordinator.ts +24 -5
  246. package/src/session/internal/managed-session-scope.ts +58 -18
  247. package/src/session/internal/managed-session-storage.ts +304 -11
  248. package/src/session/session-manager.ts +382 -44
  249. package/src/session/session-work-lease.ts +34 -0
  250. package/src/session-import/codex.ts +12 -3
  251. package/src/setup/model-onboarding-guidance.ts +1 -1
  252. package/src/setup/provider-onboarding.ts +397 -24
  253. package/src/slash-commands/builtin-registry.ts +258 -14
  254. package/src/task/index.ts +14 -2
  255. package/src/thinking.ts +21 -2
  256. package/src/tools/bash.ts +98 -78
  257. package/src/tools/fetch.ts +119 -20
  258. package/src/tools/job.ts +151 -25
  259. package/src/tools/output-meta.ts +15 -2
  260. package/src/tools/path-utils.ts +27 -3
  261. package/src/tools/read.ts +3 -3
  262. package/src/tools/steer-fold.ts +107 -0
  263. package/src/tools/tool-catalog.generated.ts +29 -2
  264. package/src/tools/tool-errors.ts +4 -0
  265. package/src/utils/git.ts +53 -3
  266. package/src/utils/markit.ts +55 -41
  267. package/src/utils/mupdf-assets.d.ts +4 -0
  268. package/src/utils/mupdf-embedded.ts +4 -0
  269. package/src/utils/mupdf-wasm-embedded.ts +3 -0
  270. package/src/utils/mupdf-wasm.ts +24 -2
  271. package/src/utils/mupdf.ts +129 -0
  272. package/src/utils/session-title-generation.ts +29 -0
  273. package/src/utils/title-generator.ts +46 -2
  274. package/src/web/scrapers/types.ts +24 -3
  275. package/vendor/markit-ai/LICENSE +21 -0
  276. package/vendor/markit-ai/MANIFEST.json +412 -0
  277. package/vendor/markit-ai/dist/commands/config.d.ts +4 -0
  278. package/vendor/markit-ai/dist/commands/config.js +139 -0
  279. package/vendor/markit-ai/dist/commands/convert.d.ts +6 -0
  280. package/vendor/markit-ai/dist/commands/convert.js +113 -0
  281. package/vendor/markit-ai/dist/commands/formats.d.ts +2 -0
  282. package/vendor/markit-ai/dist/commands/formats.js +92 -0
  283. package/vendor/markit-ai/dist/commands/init.d.ts +2 -0
  284. package/vendor/markit-ai/dist/commands/init.js +29 -0
  285. package/vendor/markit-ai/dist/commands/onboard.d.ts +2 -0
  286. package/vendor/markit-ai/dist/commands/onboard.js +61 -0
  287. package/vendor/markit-ai/dist/commands/plugin.d.ts +4 -0
  288. package/vendor/markit-ai/dist/commands/plugin.js +58 -0
  289. package/vendor/markit-ai/dist/config.d.ts +26 -0
  290. package/vendor/markit-ai/dist/config.js +42 -0
  291. package/vendor/markit-ai/dist/converters/audio.d.ts +7 -0
  292. package/vendor/markit-ai/dist/converters/audio.js +104 -0
  293. package/vendor/markit-ai/dist/converters/csv.d.ts +7 -0
  294. package/vendor/markit-ai/dist/converters/csv.js +83 -0
  295. package/vendor/markit-ai/dist/converters/docx.d.ts +6 -0
  296. package/vendor/markit-ai/dist/converters/docx.js +57 -0
  297. package/vendor/markit-ai/dist/converters/epub.d.ts +8 -0
  298. package/vendor/markit-ai/dist/converters/epub.js +128 -0
  299. package/vendor/markit-ai/dist/converters/github.d.ts +18 -0
  300. package/vendor/markit-ai/dist/converters/github.js +148 -0
  301. package/vendor/markit-ai/dist/converters/html.d.ts +6 -0
  302. package/vendor/markit-ai/dist/converters/html.js +30 -0
  303. package/vendor/markit-ai/dist/converters/image.d.ts +6 -0
  304. package/vendor/markit-ai/dist/converters/image.js +124 -0
  305. package/vendor/markit-ai/dist/converters/ipynb.d.ts +6 -0
  306. package/vendor/markit-ai/dist/converters/ipynb.js +75 -0
  307. package/vendor/markit-ai/dist/converters/iwork.d.ts +20 -0
  308. package/vendor/markit-ai/dist/converters/iwork.js +391 -0
  309. package/vendor/markit-ai/dist/converters/json.d.ts +6 -0
  310. package/vendor/markit-ai/dist/converters/json.js +21 -0
  311. package/vendor/markit-ai/dist/converters/pdf/columns.d.ts +35 -0
  312. package/vendor/markit-ai/dist/converters/pdf/columns.js +93 -0
  313. package/vendor/markit-ai/dist/converters/pdf/extract.d.ts +19 -0
  314. package/vendor/markit-ai/dist/converters/pdf/extract.js +514 -0
  315. package/vendor/markit-ai/dist/converters/pdf/grid.d.ts +25 -0
  316. package/vendor/markit-ai/dist/converters/pdf/grid.js +795 -0
  317. package/vendor/markit-ai/dist/converters/pdf/headers.d.ts +24 -0
  318. package/vendor/markit-ai/dist/converters/pdf/headers.js +108 -0
  319. package/vendor/markit-ai/dist/converters/pdf/index.d.ts +19 -0
  320. package/vendor/markit-ai/dist/converters/pdf/index.js +136 -0
  321. package/vendor/markit-ai/dist/converters/pdf/mupdf-loader.js +9 -0
  322. package/vendor/markit-ai/dist/converters/pdf/render.d.ts +24 -0
  323. package/vendor/markit-ai/dist/converters/pdf/render.js +513 -0
  324. package/vendor/markit-ai/dist/converters/pdf/types.d.ts +75 -0
  325. package/vendor/markit-ai/dist/converters/pdf/types.js +1 -0
  326. package/vendor/markit-ai/dist/converters/plain-text.d.ts +6 -0
  327. package/vendor/markit-ai/dist/converters/plain-text.js +87 -0
  328. package/vendor/markit-ai/dist/converters/pptx.d.ts +8 -0
  329. package/vendor/markit-ai/dist/converters/pptx.js +289 -0
  330. package/vendor/markit-ai/dist/converters/rss.d.ts +11 -0
  331. package/vendor/markit-ai/dist/converters/rss.js +138 -0
  332. package/vendor/markit-ai/dist/converters/wikipedia.d.ts +6 -0
  333. package/vendor/markit-ai/dist/converters/wikipedia.js +33 -0
  334. package/vendor/markit-ai/dist/converters/xlsx.d.ts +8 -0
  335. package/vendor/markit-ai/dist/converters/xlsx.js +147 -0
  336. package/vendor/markit-ai/dist/converters/xml.d.ts +6 -0
  337. package/vendor/markit-ai/dist/converters/xml.js +18 -0
  338. package/vendor/markit-ai/dist/converters/yaml.d.ts +6 -0
  339. package/vendor/markit-ai/dist/converters/yaml.js +17 -0
  340. package/vendor/markit-ai/dist/converters/zip.d.ts +8 -0
  341. package/vendor/markit-ai/dist/converters/zip.js +57 -0
  342. package/vendor/markit-ai/dist/index.d.ts +30 -0
  343. package/vendor/markit-ai/dist/index.js +26 -0
  344. package/vendor/markit-ai/dist/main.d.ts +2 -0
  345. package/vendor/markit-ai/dist/main.js +191 -0
  346. package/vendor/markit-ai/dist/markit.d.ts +45 -0
  347. package/vendor/markit-ai/dist/markit.js +267 -0
  348. package/vendor/markit-ai/dist/plugins/api.d.ts +7 -0
  349. package/vendor/markit-ai/dist/plugins/api.js +42 -0
  350. package/vendor/markit-ai/dist/plugins/index.d.ts +4 -0
  351. package/vendor/markit-ai/dist/plugins/index.js +3 -0
  352. package/vendor/markit-ai/dist/plugins/installer.d.ts +25 -0
  353. package/vendor/markit-ai/dist/plugins/installer.js +176 -0
  354. package/vendor/markit-ai/dist/plugins/loader.d.ts +6 -0
  355. package/vendor/markit-ai/dist/plugins/loader.js +61 -0
  356. package/vendor/markit-ai/dist/plugins/types.d.ts +25 -0
  357. package/vendor/markit-ai/dist/plugins/types.js +1 -0
  358. package/vendor/markit-ai/dist/providers/anthropic.d.ts +2 -0
  359. package/vendor/markit-ai/dist/providers/anthropic.js +47 -0
  360. package/vendor/markit-ai/dist/providers/index.d.ts +21 -0
  361. package/vendor/markit-ai/dist/providers/index.js +56 -0
  362. package/vendor/markit-ai/dist/providers/openai.d.ts +2 -0
  363. package/vendor/markit-ai/dist/providers/openai.js +76 -0
  364. package/vendor/markit-ai/dist/providers/types.d.ts +26 -0
  365. package/vendor/markit-ai/dist/providers/types.js +1 -0
  366. package/vendor/markit-ai/dist/types.d.ts +36 -0
  367. package/vendor/markit-ai/dist/types.js +1 -0
  368. package/vendor/markit-ai/dist/utils/exit-codes.d.ts +4 -0
  369. package/vendor/markit-ai/dist/utils/exit-codes.js +4 -0
  370. package/vendor/markit-ai/dist/utils/output.d.ts +22 -0
  371. package/vendor/markit-ai/dist/utils/output.js +31 -0
  372. package/vendor/markit-ai/dist/utils/turndown.d.ts +8 -0
  373. package/vendor/markit-ai/dist/utils/turndown.js +70 -0
  374. package/vendor/markit-ai/package.json +74 -0
  375. package/vendor/markit-ai.patch +49 -0
@@ -37,7 +37,12 @@ import {
37
37
  redactBrokerDiscovery,
38
38
  } from "./discovery";
39
39
  import { endpointIncarnation, matchesIndexedEndpointFile, readEndpointFile } from "./endpoint-authority";
40
- import { deriveIdempotencyIdentity, getBrokerIdentityKey } from "./identity";
40
+ import {
41
+ deriveIdempotencyIdentity,
42
+ deriveLegacyIdentity,
43
+ deriveLegacyTargetIdentity,
44
+ getBrokerIdentityKey,
45
+ } from "./identity";
41
46
  import {
42
47
  canonicalDeleteLocatorPath,
43
48
  executeLifecycle,
@@ -52,6 +57,7 @@ import {
52
57
  LifecycleLedger,
53
58
  type LifecycleStartupFailureReceipt,
54
59
  type LifecycleState,
60
+ type TerminalReadBack,
55
61
  } from "./lifecycle-ledger";
56
62
  import { createMasterCapabilityVerifier, readEndpoint } from "./master-capability";
57
63
  import { sdkInternalRuntimeImage } from "./runtime";
@@ -334,7 +340,7 @@ function lifecycleResponseState(response: BrokerResponse): LifecycleState {
334
340
  : "terminal_error";
335
341
  }
336
342
 
337
- type InputNormalization = { input: Record<string, unknown> } | BrokerResponse;
343
+ export type InputNormalization = { input: Record<string, unknown> } | BrokerResponse;
338
344
 
339
345
  type SessionListCursor = {
340
346
  sessions: IndexedSession[];
@@ -563,6 +569,26 @@ function matchesSpawnPin(candidate: IndexedSession, pinned: SpawnHostRegistratio
563
569
  );
564
570
  }
565
571
 
572
+ /** Exact admission predicate for the child host row published after launch. */
573
+ export function spawnRegistrationMatches(
574
+ candidate: IndexedSession,
575
+ expected: { childId: string; cwd: string; stateRoot: string },
576
+ ): boolean {
577
+ return (
578
+ candidate.sessionId === expected.childId &&
579
+ candidate.endpointGeneration > 0 &&
580
+ candidate.live &&
581
+ !candidate.terminal &&
582
+ !candidate.terminalUncertain &&
583
+ // The child publishes a realpath-canonicalized locator while the launch
584
+ // spec carries the caller's lexical spelling. Compare path identity so a
585
+ // symlinked workspace (macOS /var vs /private/var) cannot strand a valid
586
+ // registration behind different spellings.
587
+ resolveEquivalentPath(candidate.locator.cwd) === resolveEquivalentPath(expected.cwd) &&
588
+ resolveEquivalentPath(candidate.locator.stateRoot) === resolveEquivalentPath(expected.stateRoot)
589
+ );
590
+ }
591
+
566
592
  /** Rebuilds the provider proof from durable authority facts only. */
567
593
  function spawnProofFromAuthority(authority: SpawnAuthorityV1): SpawnSubstrateProof {
568
594
  return {
@@ -635,7 +661,7 @@ function normalizeAliasedString(
635
661
  return { value: values[0] };
636
662
  }
637
663
 
638
- function normalizeBrokerInput(operation: string, input: Record<string, unknown>): InputNormalization {
664
+ export function normalizeBrokerInput(operation: string, input: Record<string, unknown>): InputNormalization {
639
665
  const normalized: Record<string, unknown> = { ...input };
640
666
  const session = normalizeAliasedString(input, "sessionId", ["id"]);
641
667
  if (session.error) return error("invalid_input", session.error);
@@ -725,11 +751,47 @@ function canonicalJson(value: unknown): string {
725
751
  if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
726
752
  const record = value as Record<string, unknown>;
727
753
  return `{${Object.keys(record)
754
+ .filter(key => record[key] !== undefined)
728
755
  .sort()
729
756
  .map(key => `${JSON.stringify(key)}:${canonicalJson(record[key])}`)
730
757
  .join(",")}}`;
731
758
  }
732
759
 
760
+ type TerminalPersistenceVerification =
761
+ | { kind: "unverified" }
762
+ | { kind: "verified" }
763
+ | { kind: "uncertain"; mismatches: readonly string[] };
764
+
765
+ function verifyTerminalPersistence(
766
+ readBack: TerminalReadBack,
767
+ storedResponse: BrokerResponse,
768
+ durableEffects: LifecycleDurableEffectsReceipt | undefined,
769
+ startupFailure: LifecycleStartupFailureReceipt | undefined,
770
+ ): TerminalPersistenceVerification {
771
+ // An absent read-back is a verification failure, not evidence that the
772
+ // operation itself is ambiguous. The terminal transition was already synced
773
+ // before this read, so preserve its settled outcome and let a later reader
774
+ // reconcile the durable row.
775
+ if (readBack.kind === "absent") return { kind: "unverified" };
776
+ // A *rejected* row is the opposite: the ledger holds something for this request
777
+ // and refused it. That is damaged durable state, which previously fenced the
778
+ // session and must keep fencing it — treating it as "not yet written" would let
779
+ // a corrupt row clear the fence it exists to raise.
780
+ if (readBack.kind === "rejected") return { kind: "uncertain", mismatches: [`readBack:${readBack.reason}`] };
781
+ const persisted = readBack.entry;
782
+ const optionalReceiptJson = (value: unknown): string | undefined =>
783
+ value === undefined || value === null ? undefined : canonicalJson(value);
784
+ // The field name is always present; only the two compared receipt renderings are
785
+ // optional. Naming that shape keeps the mismatch list a plain string list.
786
+ const comparisons: readonly (readonly [string, string | undefined, string | undefined])[] = [
787
+ ["response", canonicalJson(persisted.response), canonicalJson(storedResponse)],
788
+ ["durableEffects", optionalReceiptJson(persisted.durableEffects), optionalReceiptJson(durableEffects)],
789
+ ["startupFailure", optionalReceiptJson(persisted.startupFailure), optionalReceiptJson(startupFailure)],
790
+ ];
791
+ const mismatches = comparisons.filter(([, actual, expected]) => actual !== expected).map(([field]) => field);
792
+ return mismatches.length === 0 ? { kind: "verified" } : { kind: "uncertain", mismatches };
793
+ }
794
+
733
795
  function credentialFreeLifecycleResponse(value: unknown): unknown {
734
796
  if (Array.isArray(value)) return value.map(credentialFreeLifecycleResponse);
735
797
  if (value === null || typeof value !== "object") return value;
@@ -903,15 +965,31 @@ function lifecycleTarget(operation: string, input: Record<string, unknown>): unk
903
965
  sourceSessionPath: string(input.sourceSessionPath, input.sourcePath, input.sessionPath),
904
966
  };
905
967
  case "session.resume":
906
- case "session.close":
907
968
  case "session.delete":
908
969
  case "session.reconcile_uncertain":
909
970
  return { sessionId: id };
971
+ case "session.close":
972
+ return { sessionId: id, ...closeTargetAuthority(input) };
910
973
  default:
911
974
  return { operation, root, sessionId: id };
912
975
  }
913
976
  }
914
977
 
978
+ function closeTargetAuthority(input: Record<string, unknown>): {
979
+ endpointGeneration?: number;
980
+ endpointIncarnation?: string;
981
+ } {
982
+ if (
983
+ typeof input.endpointGeneration !== "number" ||
984
+ !Number.isSafeInteger(input.endpointGeneration) ||
985
+ input.endpointGeneration <= 0 ||
986
+ typeof input.endpointIncarnation !== "string" ||
987
+ !/^[a-f0-9]{64}$/u.test(input.endpointIncarnation)
988
+ )
989
+ return {};
990
+ return { endpointGeneration: input.endpointGeneration, endpointIncarnation: input.endpointIncarnation };
991
+ }
992
+
915
993
  /**
916
994
  * A lifecycle request without a worktree remains serialized by its state root.
917
995
  * Worktree launches instead serialize on the source repository plus the
@@ -990,10 +1068,11 @@ function isBrokerLockArtifactName(name: string): boolean {
990
1068
  * Decide whether one candidate directory is provably abandoned.
991
1069
  *
992
1070
  * Fail-closed by construction: every branch that cannot prove abandonment
993
- * returns a retention reason. A tombstone is only abandoned when its owner
994
- * record parses and names a dead PID — an unreadable, permission-denied, or
995
- * absent record keeps it forever. Backup directories carry no owner contract,
996
- * so an absent record there is not ambiguity and age alone governs.
1071
+ * returns a retention reason. A tombstone with an owner record is only
1072
+ * abandoned when that record names a dead PID. A missing record is abandoned
1073
+ * only when the aged tombstone is empty; non-empty tombstones remain retained
1074
+ * as ambiguous evidence. Backup directories carry no owner contract, so an
1075
+ * absent record there is not ambiguity and age alone governs.
997
1076
  */
998
1077
  async function classifyBrokerLockArtifact(
999
1078
  directory: string,
@@ -1014,7 +1093,14 @@ async function classifyBrokerLockArtifact(
1014
1093
  } catch (error) {
1015
1094
  const code = (error as NodeJS.ErrnoException).code;
1016
1095
  if (code !== "ENOENT" && code !== "ENOTDIR") return "owner-record-unreadable";
1017
- return name.startsWith(BROKER_LOCK_TOMBSTONE_PREFIX) ? "owner-record-missing" : "abandoned";
1096
+ if (!name.startsWith(BROKER_LOCK_TOMBSTONE_PREFIX)) return "abandoned";
1097
+ try {
1098
+ const entries = await fs.readdir(target);
1099
+ return entries.length === 0 ? "abandoned" : "owner-record-missing";
1100
+ } catch (readError) {
1101
+ if ((readError as NodeJS.ErrnoException).code === "ENOENT") throw readError;
1102
+ return "owner-record-unreadable";
1103
+ }
1018
1104
  }
1019
1105
  let pid: unknown;
1020
1106
  try {
@@ -1034,7 +1120,8 @@ async function classifyBrokerLockArtifact(
1034
1120
  * of the lock's dev+ino, so a machine accrues one directory per dead owner and
1035
1121
  * nothing ever removed them (54 on the install in #3963). Reaping is
1036
1122
  * best-effort and fail-closed: anything live, unreadable, permission-denied, or
1037
- * otherwise ambiguous is kept and the reason is logged.
1123
+ * otherwise ambiguous is kept. Retained paths are logged at debug level and
1124
+ * summarized in one warning per reap pass.
1038
1125
  */
1039
1126
  export async function reapStaleBrokerLockArtifacts(input: {
1040
1127
  agentDir: string;
@@ -1048,6 +1135,7 @@ export async function reapStaleBrokerLockArtifacts(input: {
1048
1135
  const pidAlive = input.pidAlive ?? isPidAlive;
1049
1136
  const removed: string[] = [];
1050
1137
  const retained: BrokerLockArtifactRetention[] = [];
1138
+ const retainedByReason = new Map<BrokerLockArtifactRetentionReason, number>();
1051
1139
  let names: string[];
1052
1140
  try {
1053
1141
  names = await fs.readdir(directory);
@@ -1069,7 +1157,10 @@ export async function reapStaleBrokerLockArtifacts(input: {
1069
1157
  }
1070
1158
  if (verdict !== "abandoned") {
1071
1159
  retained.push({ path: target, reason: verdict });
1072
- if (verdict !== "within-grace") logger.warn(`sdk broker: retained stale lock artifact ${name} (${verdict})`);
1160
+ if (verdict !== "within-grace") {
1161
+ logger.debug(`sdk broker: retained stale lock artifact ${name} (${verdict})`);
1162
+ retainedByReason.set(verdict, (retainedByReason.get(verdict) ?? 0) + 1);
1163
+ }
1073
1164
  continue;
1074
1165
  }
1075
1166
  try {
@@ -1078,9 +1169,18 @@ export async function reapStaleBrokerLockArtifacts(input: {
1078
1169
  } catch (error) {
1079
1170
  if ((error as NodeJS.ErrnoException).code === "ENOENT") continue;
1080
1171
  retained.push({ path: target, reason: "removal-failed" });
1081
- logger.warn(`sdk broker: retained stale lock artifact ${name} (removal-failed)`);
1172
+ logger.debug(`sdk broker: retained stale lock artifact ${name} (removal-failed)`);
1173
+ retainedByReason.set("removal-failed", (retainedByReason.get("removal-failed") ?? 0) + 1);
1082
1174
  }
1083
1175
  }
1176
+ if (retainedByReason.size > 0) {
1177
+ const details = [...retainedByReason.entries()]
1178
+ .sort(([left], [right]) => left.localeCompare(right))
1179
+ .map(([reason, count]) => `${reason}: ${count}`)
1180
+ .join(", ");
1181
+ const retainedCount = [...retainedByReason.values()].reduce((total, count) => total + count, 0);
1182
+ logger.warn(`sdk broker: retained ${retainedCount} stale lock artifact(s) (${details})`);
1183
+ }
1084
1184
  if (removed.length > 0) logger.info(`sdk broker: reaped ${removed.length} stale lock artifact(s)`);
1085
1185
  return { removed, retained };
1086
1186
  }
@@ -1369,11 +1469,17 @@ export class Broker {
1369
1469
  async #handleSpawn(input: Record<string, unknown>, idempotencyKey: string | undefined): Promise<BrokerResponse> {
1370
1470
  const admission = parseSpawnInput(input, idempotencyKey);
1371
1471
  if (isBrokerResponse(admission)) return admission;
1372
- const lifecycleIdentity = await deriveIdempotencyIdentity(
1472
+ // session.spawn predates target-bound lifecycle identities and its durable
1473
+ // authority store already binds the raw request. Keep the v3 identity for
1474
+ // new claims, while recognizing the v4 identity written by the short-lived
1475
+ // target-bound implementation so an upgrade cannot admit a second child.
1476
+ const targetBoundIdentity = await deriveIdempotencyIdentity(
1373
1477
  this.settings.agentDir,
1374
1478
  "session.spawn",
1375
1479
  idempotencyKey!,
1376
1480
  );
1481
+ const legacyIdentity = await deriveLegacyIdentity(this.settings.agentDir, "session.spawn", idempotencyKey!);
1482
+ const lifecycleIdentity = this.#spawnAuthority?.claim(targetBoundIdentity) ? targetBoundIdentity : legacyIdentity;
1377
1483
  const active = this.#spawnInFlight.get(lifecycleIdentity);
1378
1484
  if (active) {
1379
1485
  if (this.#spawnTasks.get(active) !== admission.task)
@@ -1504,8 +1610,11 @@ export class Broker {
1504
1610
  );
1505
1611
  if (decision.kind === "in_progress")
1506
1612
  return finish(error("spawn_in_progress", `session.spawn is ${decision.claim.state}`));
1507
- if (decision.kind === "terminal_uncertain")
1613
+ if (decision.kind === "terminal_uncertain") {
1614
+ if (decision.claim.substrateProof !== undefined && decision.claim.authorityRef === undefined)
1615
+ return finish(await this.#reconcileUncertainRegistration(lifecycleIdentity, decision.claim));
1508
1616
  return finish(error("terminal_uncertain", "session.spawn outcome is uncertain"));
1617
+ }
1509
1618
  if (decision.kind === "terminal") return finish(this.#spawnTerminalResponse(decision.claim));
1510
1619
  if (decision.kind === "replay")
1511
1620
  return finish(await this.#reconcileSpawnReplay(lifecycleIdentity, decision.claim));
@@ -1560,6 +1669,53 @@ export class Broker {
1560
1669
  return error("resource_gone", "session.spawn claim is closed");
1561
1670
  }
1562
1671
 
1672
+ /** Re-prove an unbound launch on an idempotent retry instead of fencing it forever. */
1673
+ async #reconcileUncertainRegistration(lifecycleIdentity: string, claim: SpawnClaimV2): Promise<BrokerResponse> {
1674
+ const proof = claim.substrateProof;
1675
+ if (claim.state !== "uncertain" || claim.authorityRef !== undefined || !claim.childId || proof === undefined)
1676
+ return error("terminal_uncertain", "session.spawn registration authority is unavailable for reconciliation");
1677
+ const provider = this.#spawnSubstrateProvider();
1678
+ let verdict: "verified" | "mismatch" | "gone";
1679
+ try {
1680
+ verdict = await provider.verify(proof);
1681
+ } catch {
1682
+ return error("terminal_uncertain", "session.spawn registration proof could not be re-verified");
1683
+ }
1684
+ if (verdict === "mismatch")
1685
+ return error("terminal_uncertain", "session.spawn registration proof no longer matches the launch");
1686
+ if (verdict === "verified") {
1687
+ try {
1688
+ const closed = await provider.close(proof);
1689
+ if (!closed.ok)
1690
+ return error(
1691
+ "terminal_uncertain",
1692
+ "session.spawn child registration remains unresolved after the bounded close attempt",
1693
+ );
1694
+ } catch {
1695
+ return error("terminal_uncertain", "session.spawn child registration close could not be verified");
1696
+ }
1697
+ }
1698
+ const failure: SpawnSubstrateFailure = {
1699
+ substrateKind: proof.substrateKind,
1700
+ code: "child_registration_reconciled_failed",
1701
+ message:
1702
+ verdict === "gone"
1703
+ ? "session.spawn child host exited before registration"
1704
+ : "session.spawn child host was closed before registration",
1705
+ };
1706
+ try {
1707
+ await this.#spawnAuthority?.persistTransition(lifecycleIdentity, {
1708
+ claimId: claim.claimId,
1709
+ from: "uncertain",
1710
+ to: "pre_send_rejected",
1711
+ failure,
1712
+ });
1713
+ } catch {
1714
+ return error("terminal_uncertain", "session.spawn registration reconciliation could not be persisted");
1715
+ }
1716
+ return spawnFailureError(failure);
1717
+ }
1718
+
1563
1719
  #spawnSubstrateProvider(): SpawnSubstrateProvider {
1564
1720
  if (this.settings.spawnSubstrateProvider) return this.settings.spawnSubstrateProvider;
1565
1721
  this.settings.spawnSubstrateProvider = createSpawnSubstrateProvider();
@@ -1635,8 +1791,26 @@ export class Broker {
1635
1791
  code: "substrate_proof_failed",
1636
1792
  message: "session.spawn substrate lacks lifecycle process authority",
1637
1793
  };
1638
- await this.#releaseUnownedSubstrate(provider, launchedProof);
1639
- launchedProof = undefined;
1794
+ const release = await this.#releaseUnownedSubstrate(provider, launchedProof);
1795
+ if (release === "unresolved") {
1796
+ current = (
1797
+ await store.persistTransition(lifecycleIdentity, {
1798
+ claimId: current.claimId,
1799
+ from: "substrate_starting",
1800
+ to: "uncertain",
1801
+ failure: {
1802
+ substrateKind: launched.proof.substrateKind,
1803
+ code: "child_registration_release_unproven",
1804
+ message: "session.spawn child registration could not prove substrate release",
1805
+ },
1806
+ })
1807
+ ).claim;
1808
+ launchedProof = undefined;
1809
+ return error(
1810
+ "terminal_uncertain",
1811
+ "session.spawn substrate release could not be proven after the launch proof failed",
1812
+ );
1813
+ }
1640
1814
  current = (
1641
1815
  await store.persistTransition(lifecycleIdentity, {
1642
1816
  claimId: current.claimId,
@@ -1645,8 +1819,18 @@ export class Broker {
1645
1819
  failure,
1646
1820
  })
1647
1821
  ).claim;
1822
+ launchedProof = undefined;
1648
1823
  return spawnFailureError(failure);
1649
1824
  }
1825
+ current = (
1826
+ await store.persistTransition(lifecycleIdentity, {
1827
+ claimId: current.claimId,
1828
+ from: "substrate_starting",
1829
+ to: "substrate_starting",
1830
+ childId: prep.childId,
1831
+ substrateProof: launched.proof,
1832
+ })
1833
+ ).claim;
1650
1834
  const marker = { pid, incarnation, effectMarker: prep.effectMarker };
1651
1835
  await writeEffectMarker(prep.stateRoot, prep.childId, marker);
1652
1836
  const registration = await this.#spawnPromptLayer.awaitRegistration({
@@ -1656,18 +1840,44 @@ export class Broker {
1656
1840
  });
1657
1841
  if (!registration.ok) {
1658
1842
  const startupFailure = await readSessionLifecycleFailure(prep.stateRoot, prep.childId, marker);
1659
- await this.#releaseUnownedSubstrate(provider, launchedProof);
1660
- launchedProof = undefined;
1843
+ const release = await this.#releaseUnownedSubstrate(provider, launchedProof);
1844
+ if (release === "closed" || release === "gone") {
1845
+ const failure: SpawnSubstrateFailure = {
1846
+ substrateKind: launched.proof.substrateKind,
1847
+ code: startupFailure === undefined ? "child_registration_timeout" : "child_registration_failed",
1848
+ message:
1849
+ startupFailure === undefined
1850
+ ? "session.spawn child host did not register before the bounded admission deadline"
1851
+ : `session.spawn child host failed during ${startupFailure.phase}/${startupFailure.reason}`,
1852
+ };
1853
+ current = (
1854
+ await store.persistTransition(lifecycleIdentity, {
1855
+ claimId: current.claimId,
1856
+ from: "substrate_starting",
1857
+ to: "pre_send_rejected",
1858
+ failure,
1859
+ })
1860
+ ).claim;
1861
+ launchedProof = undefined;
1862
+ return spawnFailureError(failure);
1863
+ }
1864
+ const unresolvedRelease: SpawnSubstrateFailure = {
1865
+ substrateKind: launched.proof.substrateKind,
1866
+ code: "child_registration_release_unproven",
1867
+ message: "session.spawn child registration could not prove substrate release",
1868
+ };
1661
1869
  current = (
1662
1870
  await store.persistTransition(lifecycleIdentity, {
1663
1871
  claimId: current.claimId,
1664
1872
  from: "substrate_starting",
1665
1873
  to: "uncertain",
1874
+ failure: unresolvedRelease,
1666
1875
  })
1667
1876
  ).claim;
1877
+ launchedProof = undefined;
1668
1878
  return error(
1669
1879
  "terminal_uncertain",
1670
- "session.spawn child registration is uncertain" +
1880
+ "session.spawn child registration is uncertain (substrate release could not be proven)" +
1671
1881
  (startupFailure ? ` (${startupFailure.phase}/${startupFailure.reason})` : ""),
1672
1882
  );
1673
1883
  }
@@ -1833,15 +2043,72 @@ export class Broker {
1833
2043
  } catch {
1834
2044
  // Once a substrate exists the outcome is ambiguous even before handoff:
1835
2045
  // reporting an ordinary failure would downgrade retained uncertainty.
1836
- const ambiguous = handedOff || launchedProof !== undefined;
2046
+ let ambiguous = handedOff || launchedProof !== undefined;
1837
2047
  // A launched substrate with no persisted authority is invisible to the
1838
2048
  // reaper, so in-process cleanup is its only chance. This must run on
1839
2049
  // EVERY post-launch failure exit, not just a returned registration
1840
2050
  // failure: a throw from awaitRegistration, verify, close, or a durable
1841
2051
  // transition all land here.
1842
- if (!handedOff) await this.#releaseUnownedSubstrate(provider, launchedProof);
2052
+ if (!handedOff) {
2053
+ const release = await this.#releaseUnownedSubstrate(provider, launchedProof);
2054
+ if ((release === "closed" || release === "gone") && current.state === "substrate_starting") {
2055
+ const failure: SpawnSubstrateFailure = {
2056
+ substrateKind: launchedProof?.substrateKind ?? "headless",
2057
+ code: "child_registration_failed",
2058
+ message: "session.spawn child host failed before registration",
2059
+ };
2060
+ try {
2061
+ await store.persistTransition(lifecycleIdentity, {
2062
+ claimId: current.claimId,
2063
+ from: "substrate_starting",
2064
+ to: "pre_send_rejected",
2065
+ failure,
2066
+ });
2067
+ launchedProof = undefined;
2068
+ return spawnFailureError(failure);
2069
+ } catch {
2070
+ // The substrate is closed, but a failed terminal write still leaves
2071
+ // the durable claim unresolved for the next broker recovery pass.
2072
+ }
2073
+ } else if (release === "unresolved" && current.state === "substrate_starting") {
2074
+ try {
2075
+ current = (
2076
+ await store.persistTransition(lifecycleIdentity, {
2077
+ claimId: current.claimId,
2078
+ from: "substrate_starting",
2079
+ to: "uncertain",
2080
+ failure: {
2081
+ substrateKind: launchedProof?.substrateKind ?? "headless",
2082
+ code: "child_registration_release_unproven",
2083
+ message: "session.spawn child registration could not prove substrate release",
2084
+ },
2085
+ })
2086
+ ).claim;
2087
+ launchedProof = undefined;
2088
+ } catch {
2089
+ // Preserve the generic durable-state uncertainty if the reason itself
2090
+ // cannot be appended.
2091
+ }
2092
+ } else if (release === "absent" && current.state === "substrate_starting") {
2093
+ try {
2094
+ current = (
2095
+ await store.persistTransition(lifecycleIdentity, {
2096
+ claimId: current.claimId,
2097
+ from: "substrate_starting",
2098
+ to: "uncertain",
2099
+ })
2100
+ ).claim;
2101
+ ambiguous = true;
2102
+ } catch {
2103
+ // Keep the generic failure when even the uncertainty transition cannot be written.
2104
+ }
2105
+ }
2106
+ }
1843
2107
  return ambiguous
1844
- ? error("terminal_uncertain", "session.spawn state could not be advanced durably")
2108
+ ? error(
2109
+ "terminal_uncertain",
2110
+ current.failure?.message ?? "session.spawn state could not be advanced durably",
2111
+ )
1845
2112
  : error("spawn_failed", "session.spawn could not be advanced durably");
1846
2113
  }
1847
2114
  }
@@ -1855,22 +2122,26 @@ export class Broker {
1855
2122
  async #releaseUnownedSubstrate(
1856
2123
  provider: SpawnSubstrateProvider,
1857
2124
  proof: SpawnSubstrateProof | undefined,
1858
- ): Promise<void> {
1859
- if (!proof) return;
2125
+ ): Promise<"closed" | "gone" | "absent" | "unresolved"> {
2126
+ // A launch that throws before returning proof has no substrate identity to
2127
+ // classify as gone; keep the claim uncertain instead of fabricating a kind.
2128
+ if (!proof) return "absent";
1860
2129
  let verdict: "verified" | "mismatch" | "gone";
1861
2130
  try {
1862
2131
  verdict = await provider.verify(proof);
1863
2132
  } catch {
1864
2133
  // An unprovable substrate is never mutated; uncertainty is retained by
1865
2134
  // the caller's durable transition instead.
1866
- return;
2135
+ return "unresolved";
1867
2136
  }
1868
- if (verdict !== "verified") return;
2137
+ if (verdict === "gone") return "gone";
2138
+ if (verdict !== "verified") return "unresolved";
1869
2139
  try {
1870
- await provider.close(proof);
2140
+ return (await provider.close(proof)).ok ? "closed" : "unresolved";
1871
2141
  } catch {
1872
2142
  // The substrate may survive. The caller still records uncertainty, which
1873
2143
  // is the honest durable outcome for an unclosed unowned substrate.
2144
+ return "unresolved";
1874
2145
  }
1875
2146
  }
1876
2147
 
@@ -2043,21 +2314,9 @@ export class Broker {
2043
2314
  await this.index.refresh();
2044
2315
  // The launch locator is authority: a same-id row registered by an
2045
2316
  // unrelated workspace must never be adopted as this spawn's child.
2046
- const row = this.index.listSessionIdentities().find(
2047
- candidate =>
2048
- candidate.sessionId === input.childId &&
2049
- candidate.endpointGeneration > 0 &&
2050
- candidate.live &&
2051
- !candidate.terminal &&
2052
- !candidate.terminalUncertain &&
2053
- // Path IDENTITY, not spelling. The child publishes a
2054
- // realpath-canonicalized locator while the launch spec carries the
2055
- // caller's lexical path, so `path.resolve` never reconciles a
2056
- // symlinked workspace (macOS /var vs /private/var) and a
2057
- // legitimate child would never match.
2058
- resolveEquivalentPath(candidate.locator.cwd) === resolveEquivalentPath(input.cwd) &&
2059
- resolveEquivalentPath(candidate.locator.stateRoot) === resolveEquivalentPath(input.stateRoot),
2060
- );
2317
+ const row = this.index
2318
+ .listSessionIdentities()
2319
+ .find(candidate => spawnRegistrationMatches(candidate, input));
2061
2320
  if (row) {
2062
2321
  const incarnation = row.hostIncarnation ?? row.processIncarnation;
2063
2322
  // An incarnation-less row is incomplete endpoint evidence; a partial pin
@@ -3390,7 +3649,9 @@ export class Broker {
3390
3649
  return error("invalid_input", "session.spawn does not support lifecycle lookup");
3391
3650
  if (!LIFECYCLE_OPERATIONS.has(requestedOperation)) return error("not_found", "lifecycle operation was not found");
3392
3651
  const identity = await deriveIdempotencyIdentity(this.settings.agentDir, requestedOperation, idempotencyKey);
3393
- const entry = this.ledger.get(identity);
3652
+ const entry =
3653
+ this.ledger.get(identity) ??
3654
+ this.ledger.findByOperationKey(`${requestedOperation}\0${idempotencyKey}`, requestedFingerprint);
3394
3655
  if (!entry) return error("not_found", "lifecycle operation was not found");
3395
3656
  if (entry.fingerprint !== requestedFingerprint)
3396
3657
  return error("idempotency_conflict", "lifecycle request fingerprint differs");
@@ -3450,10 +3711,10 @@ export class Broker {
3450
3711
  lifecycleFingerprint(lookup.operation, lookup.target),
3451
3712
  );
3452
3713
  }
3453
- const fingerprint = lifecycleFingerprint(operation, input);
3454
3714
  const normalization = normalizeBrokerInput(operation, input);
3455
3715
  if (isBrokerResponse(normalization)) return normalization;
3456
3716
  input = normalization.input;
3717
+ const fingerprint = lifecycleFingerprint(operation, input);
3457
3718
  if (operation === "session.list") {
3458
3719
  if (input.cursor === undefined) await this.index.refresh();
3459
3720
  const page = await this.#sessionListPage(input, this.index.listSessions());
@@ -3523,8 +3784,64 @@ export class Broker {
3523
3784
  const target = createHash("sha256")
3524
3785
  .update(canonicalJson(lifecycleTarget(operation, input)))
3525
3786
  .digest("hex");
3526
- const identity = await deriveIdempotencyIdentity(this.settings.agentDir, operation, idempotencyKey, target);
3787
+ const identity = await deriveIdempotencyIdentity(
3788
+ this.settings.agentDir,
3789
+ operation,
3790
+ idempotencyKey,
3791
+ operation === "session.create" ? undefined : target,
3792
+ );
3527
3793
  const operationKey = `${operation}\0${idempotencyKey}`;
3794
+ const requestedRequestHash = createHash("sha256")
3795
+ .update(canonicalJson({ operation, input: lifecycleRequestIdentity(input) }))
3796
+ .digest("hex");
3797
+ const legacyIdentity = await deriveLegacyIdentity(this.settings.agentDir, operation, idempotencyKey);
3798
+ const legacyTargetIdentity = await deriveLegacyTargetIdentity(
3799
+ this.settings.agentDir,
3800
+ operation,
3801
+ idempotencyKey,
3802
+ target,
3803
+ );
3804
+ const metadata = {
3805
+ operationKey,
3806
+ fingerprint,
3807
+ ...(operation === "session.close" && typeof input.sessionId === "string"
3808
+ ? { intendedSessionId: input.sessionId }
3809
+ : {}),
3810
+ };
3811
+ if (!this.ledger.get(identity)) {
3812
+ const matchingOperation = this.ledger.findAnyByOperationKey(operationKey);
3813
+ const sameCloseSession =
3814
+ operation === "session.close" &&
3815
+ (matchingOperation?.intendedSessionId === input.sessionId ||
3816
+ matchingOperation?.resultSessionId === input.sessionId);
3817
+ if (
3818
+ matchingOperation &&
3819
+ !sameCloseSession &&
3820
+ (matchingOperation.fingerprint !== fingerprint || matchingOperation.requestHash !== requestedRequestHash)
3821
+ )
3822
+ return error("idempotency_conflict", "idempotency key was used with a different request");
3823
+ const matchingLegacy = matchingOperation;
3824
+ if (matchingLegacy) {
3825
+ if (matchingLegacy.identity === legacyIdentity || matchingLegacy.identity === legacyTargetIdentity)
3826
+ await this.ledger.migrateIdentity(matchingLegacy.identity, identity, metadata);
3827
+ } else if (this.ledger.get(legacyIdentity)) {
3828
+ return error("idempotency_conflict", "idempotency key was used with a different request");
3829
+ } else {
3830
+ const legacyTargetEntry = this.ledger.get(legacyTargetIdentity);
3831
+ if (legacyTargetEntry) {
3832
+ if (legacyTargetEntry.requestHash !== requestedRequestHash)
3833
+ return error("idempotency_conflict", "idempotency key was used with a different request");
3834
+ await this.ledger.migrateIdentity(legacyTargetIdentity, identity, metadata);
3835
+ // Exact target-derived legacy identity migrated without granting new authority.
3836
+ } else if (
3837
+ this.ledger.hasLegacyIdentity(
3838
+ new Set(this.#spawnAuthority?.claims().map(claim => claim.lifecycleIdentity) ?? []),
3839
+ )
3840
+ ) {
3841
+ return error("idempotency_conflict", "legacy lifecycle request has an ambiguous target");
3842
+ }
3843
+ }
3844
+ }
3528
3845
 
3529
3846
  let reconstructedDeleteCleanup: BrokerCleanupEvidence | undefined;
3530
3847
  if (operation === "session.delete" && input.cwd === undefined && input.sessionPath === undefined) {
@@ -3594,7 +3911,7 @@ export class Broker {
3594
3911
  await prev;
3595
3912
  try {
3596
3913
  const beforeBegin = this.ledger.get(identity);
3597
- const begun = await this.ledger.begin(identity, requestHash, { operationKey, fingerprint });
3914
+ const begun = await this.ledger.begin(identity, requestHash, metadata);
3598
3915
  if (begun.kind === "replay") {
3599
3916
  const replay = begun.entry.response as BrokerResponse;
3600
3917
  const cleanup = cleanupFromResponse(replay) ?? reconstructedDeleteCleanup;
@@ -3710,12 +4027,21 @@ export class Broker {
3710
4027
  });
3711
4028
  if (isCleanupPending(response)) return response;
3712
4029
  const persisted = await this.ledger.readTerminal(identity, requestHash);
3713
- const persistenceVerified =
3714
- persisted !== undefined &&
3715
- canonicalJson(persisted.response) === canonicalJson(storedResponse) &&
3716
- canonicalJson(persisted.durableEffects) === canonicalJson(outcome.durableEffects) &&
3717
- canonicalJson(persisted.startupFailure) === canonicalJson(outcome.startupFailure);
3718
- if (!persistenceVerified) {
4030
+ const persistenceVerification = verifyTerminalPersistence(
4031
+ persisted,
4032
+ storedResponse,
4033
+ outcome.durableEffects,
4034
+ outcome.startupFailure,
4035
+ );
4036
+ if (persistenceVerification.kind === "unverified") {
4037
+ logger.warn("sdk broker terminal persistence read-back was unavailable; preserving settled outcome", {
4038
+ identity,
4039
+ });
4040
+ } else if (persistenceVerification.kind === "uncertain") {
4041
+ logger.warn("sdk broker terminal persistence verification found conflicting evidence", {
4042
+ identity,
4043
+ mismatches: persistenceVerification.mismatches,
4044
+ });
3719
4045
  const uncertain = error(
3720
4046
  "terminal_uncertain",
3721
4047
  "Lifecycle terminal evidence could not be verified after persistence; retained artifacts require reconciliation.",