@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
@@ -70,6 +70,72 @@ const SPAWN_LOCK_RETRY_DELAY_MS = 50;
70
70
  const SPAWN_LOCK_TARGET_NAME = "broker.spawn";
71
71
  const STARTUP_LOCK_TARGET_NAME = "broker.startup";
72
72
 
73
+ export interface BrokerStartupLockTestHooks {
74
+ onAcquired?: () => void;
75
+ onContended?: () => void;
76
+ }
77
+
78
+ /**
79
+ * Test-only phase observation for detached broker startup regressions. The
80
+ * signal is opt-in through the child environment and never affects broker
81
+ * ownership or error handling when the test seam is absent.
82
+ */
83
+ export async function emitBrokerStartupTestSignal(signal: string): Promise<void> {
84
+ const directory = process.env.GJC_SDK_TEST_BROKER_SIGNAL_DIR;
85
+ if (!directory || !/^[a-z0-9-]+$/.test(signal)) return;
86
+ try {
87
+ await fs.mkdir(directory, { recursive: true, mode: 0o700 });
88
+ await fs.writeFile(path.join(directory, signal), `${process.pid}\n`);
89
+ } catch {
90
+ // Test observability must never change broker startup semantics.
91
+ }
92
+ }
93
+
94
+ /**
95
+ * The parent discovery budget published as a composition rather than a number.
96
+ * A test that stages a fence contention has to time itself against these legs,
97
+ * and retyping the sum makes it win or lose on scheduler noise while letting a
98
+ * change to any single leg silently re-tune it (#5604). This is the one source
99
+ * of truth for how the budget is built; nothing may restate the arithmetic.
100
+ */
101
+ export const BROKER_DISCOVERY_BUDGET = {
102
+ staleRetirementMs: STALE_BROKER_RETIREMENT_TIMEOUT_MS,
103
+ startupLockWaitMs: STARTUP_LOCK_WAIT_MS,
104
+ publicationMs: BROKER_PUBLICATION_TIMEOUT_MS,
105
+ /** What the startup fence grants its holder: retirement plus one publication attempt. */
106
+ fenceOperationMs: STALE_BROKER_RETIREMENT_TIMEOUT_MS + BROKER_PUBLICATION_TIMEOUT_MS,
107
+ /**
108
+ * When a child spawned under the parent's spawn lock gives up on the fence,
109
+ * measured from that lock acquisition: the parent retires the stale incumbent
110
+ * first, then the child exhausts its own fence wait. Not a production
111
+ * constant -- an emergent property of the two legs that precede publication.
112
+ */
113
+ childFenceWaitMs: STALE_BROKER_RETIREMENT_TIMEOUT_MS + STARTUP_LOCK_WAIT_MS,
114
+ discoveryMs: DISCOVERY_TIMEOUT_MS,
115
+ } as const;
116
+
117
+ const sleep = (ms: number): Promise<void> => new Promise(resolve => setTimeout(resolve, ms));
118
+
119
+ /**
120
+ * Clock the discovery budget is measured on. The default is the real one; a test
121
+ * installs a virtual clock to assert the composition order -- stale retirement,
122
+ * then the child fence, then startup and publication -- in-process instead of
123
+ * racing three wall clocks against each other (#5604). Reaping keeps its own
124
+ * {@link ReapTiming} seam and is deliberately not driven from here.
125
+ */
126
+ export interface EnsureBrokerTiming {
127
+ now(): number;
128
+ sleep(ms: number): Promise<void>;
129
+ }
130
+
131
+ const REAL_ENSURE_BROKER_TIMING: EnsureBrokerTiming = { now: Date.now, sleep };
132
+ let ensureBrokerTiming: EnsureBrokerTiming = REAL_ENSURE_BROKER_TIMING;
133
+
134
+ /** Test hook: drives the discovery budget on a controllable clock. */
135
+ export function setEnsureBrokerTimingForTest(timing: EnsureBrokerTiming | undefined): void {
136
+ ensureBrokerTiming = timing ?? REAL_ENSURE_BROKER_TIMING;
137
+ }
138
+
73
139
  type SpawnLockOptions = Pick<FileLockOptions, "retries" | "retryDelayMs" | "signal">;
74
140
 
75
141
  interface BrokerLockHostIdentity {
@@ -104,7 +170,7 @@ async function readBrokerDiscoveryBeforeDeadline(
104
170
  heartbeatTtlMs: number | undefined,
105
171
  deadline: number,
106
172
  ): Promise<BrokerDiscovery | null> {
107
- const remainingMs = deadline - Date.now();
173
+ const remainingMs = deadline - ensureBrokerTiming.now();
108
174
  if (!Number.isFinite(remainingMs)) return await readBrokerDiscovery(agentDir, heartbeatTtlMs);
109
175
  if (remainingMs <= 0) throw new Error("Timed out reading SDK broker discovery.");
110
176
  const read = readBrokerDiscovery(agentDir, heartbeatTtlMs);
@@ -151,14 +217,17 @@ async function acquireSpawnLock(agentDir: string, options: SpawnLockOptions = {}
151
217
  export async function withBrokerStartupLock<T>(
152
218
  agentDir: string,
153
219
  operation: (deadline: number) => Promise<T>,
220
+ testHooks: BrokerStartupLockTestHooks = {},
154
221
  ): Promise<T> {
155
222
  const hostIdentity = await brokerLockHostIdentity(agentDir);
156
223
  return withFileLock(
157
224
  path.join(agentDir, "sdk", STARTUP_LOCK_TARGET_NAME),
158
- () => operation(Date.now() + STALE_BROKER_RETIREMENT_TIMEOUT_MS + BROKER_PUBLICATION_TIMEOUT_MS),
225
+ () => operation(ensureBrokerTiming.now() + BROKER_DISCOVERY_BUDGET.fenceOperationMs),
159
226
  {
160
227
  retries: Math.ceil(STARTUP_LOCK_WAIT_MS / SPAWN_LOCK_RETRY_DELAY_MS),
161
228
  retryDelayMs: SPAWN_LOCK_RETRY_DELAY_MS,
229
+ onAcquired: testHooks.onAcquired,
230
+ onContended: testHooks.onContended,
162
231
  ...hostIdentity,
163
232
  },
164
233
  );
@@ -275,7 +344,6 @@ const DEFAULT_REAP_TIMING: ReapTiming = {
275
344
  gracefulMs: REAP_GRACEFUL_MS,
276
345
  killVerifyMs: REAP_SIGKILL_CAP_MS,
277
346
  };
278
- const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
279
347
 
280
348
  /**
281
349
  * Terminate and reap a detached broker this process spawned, targeting the exact
@@ -410,9 +478,9 @@ export async function closeBrokerClientBeforeDeadline(
410
478
  ): Promise<void> {
411
479
  const close = client.close();
412
480
  void close.catch(() => undefined);
413
- const remainingMs = deadline - Date.now();
481
+ const remainingMs = deadline - ensureBrokerTiming.now();
414
482
  if (remainingMs <= 0) return;
415
- await Promise.race([close, Bun.sleep(remainingMs)]);
483
+ await Promise.race([close, ensureBrokerTiming.sleep(remainingMs)]);
416
484
  }
417
485
 
418
486
  async function retireUnusableBroker(
@@ -420,7 +488,7 @@ async function retireUnusableBroker(
420
488
  settings: EnsureBrokerSettings,
421
489
  outerDeadline = Number.POSITIVE_INFINITY,
422
490
  ): Promise<void> {
423
- const deadline = Math.min(outerDeadline, Date.now() + STALE_BROKER_RETIREMENT_TIMEOUT_MS);
491
+ const deadline = Math.min(outerDeadline, ensureBrokerTiming.now() + BROKER_DISCOVERY_BUDGET.staleRetirementMs);
424
492
  let shutdownSucceeded = false;
425
493
  try {
426
494
  const current = await readBrokerDiscoveryBeforeDeadline(settings.agentDir, settings.heartbeatTtlMs, deadline);
@@ -443,10 +511,10 @@ async function retireUnusableBroker(
443
511
  } catch {}
444
512
  }
445
513
  // Wait for the stale identity to disappear (owner-fenced: pid+incarnation).
446
- while (Date.now() < deadline) {
514
+ while (ensureBrokerTiming.now() < deadline) {
447
515
  const current = await readBrokerDiscoveryBeforeDeadline(settings.agentDir, settings.heartbeatTtlMs, deadline);
448
516
  if (!sameBrokerOwner(current, stale)) break;
449
- await sleep(STALE_BROKER_POLL_MS);
517
+ await ensureBrokerTiming.sleep(STALE_BROKER_POLL_MS);
450
518
  }
451
519
  }
452
520
 
@@ -459,6 +527,7 @@ export async function reconcileBrokerGenerationForStartup(
459
527
  if (!current) return undefined;
460
528
  if (await isBrokerReusable(current)) return current;
461
529
  await retireUnusableBroker(current, settings, deadline);
530
+ await emitBrokerStartupTestSignal("retirement-finished");
462
531
  const replacement = await readBrokerDiscoveryBeforeDeadline(settings.agentDir, settings.heartbeatTtlMs, deadline);
463
532
  return replacement && (await isBrokerReusable(replacement)) ? replacement : undefined;
464
533
  }
@@ -506,7 +575,7 @@ function createFixtureLease(owner: BrokerOwner, child: ChildProcess): ExactFixtu
506
575
 
507
576
  async function ensureBrokerOnce(settings: EnsureBrokerSettings, initiator: EnsureInitiator): Promise<EnsureOutcome> {
508
577
  const initialDiscoveryDeadline =
509
- Date.now() + (initiator === "fixture-lease" ? FIXTURE_DISCOVERY_TIMEOUT_MS : DISCOVERY_TIMEOUT_MS);
578
+ ensureBrokerTiming.now() + (initiator === "fixture-lease" ? FIXTURE_DISCOVERY_TIMEOUT_MS : DISCOVERY_TIMEOUT_MS);
510
579
  const priorOwner = owners.get(settings.agentDir);
511
580
  const existing = await readBrokerDiscoveryBeforeDeadline(
512
581
  settings.agentDir,
@@ -514,7 +583,11 @@ async function ensureBrokerOnce(settings: EnsureBrokerSettings, initiator: Ensur
514
583
  initialDiscoveryDeadline,
515
584
  );
516
585
  const restartIntent = await readBrokerRestartIntent(settings.agentDir);
517
- if (restartIntent && restartIntent.expiresAt > Date.now() && restartIntent.requestId !== settings.restartRequestId)
586
+ if (
587
+ restartIntent &&
588
+ restartIntent.expiresAt > ensureBrokerTiming.now() &&
589
+ restartIntent.requestId !== settings.restartRequestId
590
+ )
518
591
  throw new Error("broker_restart_in_progress");
519
592
  if (initiator === "fixture-lease" && (priorOwner || existing)) throw fixtureLeaseUnavailable();
520
593
  if (priorOwner) {
@@ -543,9 +616,14 @@ async function ensureBrokerOnce(settings: EnsureBrokerSettings, initiator: Ensur
543
616
  const releaseSpawnLock = initiator === "fixture-lease" ? async () => {} : await acquireSpawnLock(settings.agentDir);
544
617
  try {
545
618
  const deadline =
546
- Date.now() + (initiator === "fixture-lease" ? FIXTURE_DISCOVERY_TIMEOUT_MS : DISCOVERY_TIMEOUT_MS);
619
+ ensureBrokerTiming.now() +
620
+ (initiator === "fixture-lease" ? FIXTURE_DISCOVERY_TIMEOUT_MS : DISCOVERY_TIMEOUT_MS);
547
621
  const lockedIntent = await readBrokerRestartIntent(settings.agentDir);
548
- if (lockedIntent && lockedIntent.expiresAt > Date.now() && lockedIntent.requestId !== settings.restartRequestId)
622
+ if (
623
+ lockedIntent &&
624
+ lockedIntent.expiresAt > ensureBrokerTiming.now() &&
625
+ lockedIntent.requestId !== settings.restartRequestId
626
+ )
549
627
  throw new Error("broker_restart_in_progress");
550
628
  // The lock winner may still find a discovery published by an earlier winner
551
629
  // that finished between our first read and the lock acquisition.
@@ -572,7 +650,7 @@ async function ensureBrokerOnce(settings: EnsureBrokerSettings, initiator: Ensur
572
650
  // parent's diagnostic handle must not discard exact ownership of a live child.
573
651
  await spawnLog?.handle.close().catch(() => undefined);
574
652
  let discoveryError: unknown;
575
- while (Date.now() < deadline) {
653
+ while (ensureBrokerTiming.now() < deadline) {
576
654
  if (spawnError || child.exitCode !== null || child.signalCode !== null) break;
577
655
  try {
578
656
  const discovered = await readBrokerDiscoveryBeforeDeadline(
@@ -582,7 +660,7 @@ async function ensureBrokerOnce(settings: EnsureBrokerSettings, initiator: Ensur
582
660
  );
583
661
  if (discovered) {
584
662
  if (!(await isBrokerReusable(discovered))) {
585
- await sleep(50);
663
+ await ensureBrokerTiming.sleep(50);
586
664
  continue;
587
665
  }
588
666
  if (owner.markReady(discovered)) {
@@ -596,7 +674,7 @@ async function ensureBrokerOnce(settings: EnsureBrokerSettings, initiator: Ensur
596
674
  } catch (error) {
597
675
  discoveryError = error;
598
676
  }
599
- await sleep(50);
677
+ await ensureBrokerTiming.sleep(50);
600
678
  }
601
679
  const exitedBeforeDiscovery = child.exitCode !== null || child.signalCode !== null;
602
680
  if (exitedBeforeDiscovery && child.exitCode === 0) {
@@ -616,7 +694,7 @@ async function ensureBrokerOnce(settings: EnsureBrokerSettings, initiator: Ensur
616
694
  await owner.stop();
617
695
  return { kind: "external-discovery", discovery: winner };
618
696
  }
619
- await sleep(50);
697
+ await ensureBrokerTiming.sleep(50);
620
698
  }
621
699
  } catch {
622
700
  // fall through to cleanup + failure
@@ -649,8 +727,8 @@ async function ensureBrokerOnce(settings: EnsureBrokerSettings, initiator: Ensur
649
727
  // reaches the end of its publication allowance, so keep the parent attached
650
728
  // to the exact agent dir and give that incumbent one final bounded chance to
651
729
  // publish before treating the replacement failure as terminal.
652
- const recoveryDeadline = Math.min(deadline, Date.now() + BROKER_PUBLICATION_TIMEOUT_MS);
653
- while (Date.now() < recoveryDeadline) {
730
+ const recoveryDeadline = Math.min(deadline, ensureBrokerTiming.now() + BROKER_DISCOVERY_BUDGET.publicationMs);
731
+ while (ensureBrokerTiming.now() < recoveryDeadline) {
654
732
  try {
655
733
  const incumbent = await readBrokerDiscoveryBeforeDeadline(
656
734
  settings.agentDir,
@@ -665,7 +743,7 @@ async function ensureBrokerOnce(settings: EnsureBrokerSettings, initiator: Ensur
665
743
  // Keep the bounded retry alive; a transient read failure is not
666
744
  // authority to classify the incumbent as failed.
667
745
  }
668
- await sleep(50);
746
+ await ensureBrokerTiming.sleep(50);
669
747
  }
670
748
  }
671
749
  const spawnLogTail = exitedBeforeDiscovery && spawnLog ? await readBrokerSpawnLogTail(spawnLog.path) : "";
@@ -43,8 +43,16 @@ export async function deriveIdempotencyIdentity(
43
43
  agentDir: string,
44
44
  operation: string,
45
45
  callerKey: string,
46
- _protocolVersionOrLegacyTargetHash?: string,
46
+ targetHash?: string,
47
47
  ): Promise<string> {
48
+ const key = await getBrokerIdentityKey(agentDir);
49
+ return createHmac("sha256", Buffer.from(key, "hex"))
50
+ .update(`4|${operation}|${callerKey}|${targetHash ?? ""}`)
51
+ .digest("hex");
52
+ }
53
+
54
+ /** Identity format used by lifecycle rows written before target binding. */
55
+ export async function deriveLegacyIdentity(agentDir: string, operation: string, callerKey: string): Promise<string> {
48
56
  const key = await getBrokerIdentityKey(agentDir);
49
57
  return createHmac("sha256", Buffer.from(key, "hex")).update(`3|${operation}|${callerKey}`).digest("hex");
50
58
  }
@@ -100,6 +100,32 @@ export type BeginResult =
100
100
  | { kind: "idempotency_conflict" }
101
101
  | { kind: "terminal_uncertain"; entry: LifecycleLedgerEntry }
102
102
  | { kind: "in_progress"; entry: LifecycleLedgerEntry };
103
+
104
+ type BoundedSourceReadRejection = "not-regular-file" | "oversized-by-stat" | "oversized-by-read" | "read-error";
105
+
106
+ /**
107
+ * Why a terminal read-back produced no entry. `absent` means nothing terminal is
108
+ * recorded for the request; `rejected` means the source or a row was refused, which
109
+ * is evidence of a damaged ledger rather than of an unpersisted operation. Collapsing
110
+ * the two lets a corrupt source or row read as "not yet written" and unfence the session.
111
+ */
112
+ export type TerminalReadBackRejection =
113
+ | "bounds"
114
+ | "shape"
115
+ | "undecodable"
116
+ | "row-not-attributable"
117
+ | "partial-row"
118
+ | BoundedSourceReadRejection;
119
+ export type TerminalReadBack =
120
+ | { kind: "terminal"; entry: LifecycleLedgerEntry }
121
+ | { kind: "absent" }
122
+ | { kind: "rejected"; reason: TerminalReadBackRejection };
123
+
124
+ type BoundedSourceRead =
125
+ | { kind: "absent" }
126
+ | { kind: "source"; source: Buffer }
127
+ | { kind: "rejected"; reason: BoundedSourceReadRejection };
128
+
103
129
  const terminal = (s: LifecycleState) => s === "terminal_ok" || s === "terminal_error";
104
130
  const final = (s: LifecycleState) => terminal(s) || s === "terminal_uncertain";
105
131
  export interface LifecycleLedgerLimits {
@@ -117,6 +143,11 @@ function canonicalCleanupSessionId(value: unknown): value is string {
117
143
  }
118
144
 
119
145
  const DEFAULT_LIFECYCLE_LEDGER_LIMITS: Required<LifecycleLedgerLimits> = {
146
+ // Terminal evidence intentionally has no time-based expiry: forgetting a
147
+ // completed close would allow a delayed duplicate to reach a later host
148
+ // generation. The row/byte bounds below are storage limits; compaction keeps
149
+ // each identity's accepted anchor and latest terminal state instead of
150
+ // expiring replay authority.
120
151
  maxBytes: 64 * 1024 * 1024,
121
152
  maxLineBytes: 8 * 1024 * 1024,
122
153
  maxRows: 10_000,
@@ -376,9 +407,11 @@ export class LifecycleLedger {
376
407
  * uncertainly persists that conclusion, and refusing to read it back would report a
377
408
  * synced row as unpersisted and replace its true reason with a generic one.
378
409
  */
379
- async readTerminal(identity: string, requestHash: string): Promise<LifecycleLedgerEntry | undefined> {
380
- const source = await this.#readBoundedSourceReadOnly();
381
- if (!source) return undefined;
410
+ async readTerminal(identity: string, requestHash: string): Promise<TerminalReadBack> {
411
+ const sourceRead = await this.#readBoundedSourceReadOnly();
412
+ if (sourceRead.kind === "absent") return sourceRead;
413
+ if (sourceRead.kind === "rejected") return sourceRead;
414
+ const { source } = sourceRead;
382
415
  let prior: LifecycleLedgerEntry | undefined;
383
416
  let latest: LifecycleLedgerEntry | undefined;
384
417
  let rows = 0;
@@ -391,15 +424,16 @@ export class LifecycleLedger {
391
424
  lineStart = offset + 1;
392
425
  if (line.length === 0) continue;
393
426
  rows += 1;
394
- if (rows > this.#limits.maxRows || line.length > this.#limits.maxLineBytes) return undefined;
427
+ if (rows > this.#limits.maxRows || line.length > this.#limits.maxLineBytes)
428
+ return { kind: "rejected", reason: "bounds" };
395
429
  let entry: LifecycleLedgerEntry;
396
430
  try {
397
431
  const value = parseLifecycleJson(line);
398
432
  assertSupportedStateVersion(this.#file, value);
399
- if (!isLifecycleLedgerEntry(value)) return undefined;
433
+ if (!isLifecycleLedgerEntry(value)) return { kind: "rejected", reason: "shape" };
400
434
  entry = value;
401
435
  } catch {
402
- return undefined;
436
+ return { kind: "rejected", reason: "undecodable" };
403
437
  }
404
438
  if (entry.identity !== identity) continue;
405
439
  if (
@@ -407,7 +441,7 @@ export class LifecycleLedger {
407
441
  !hasValidTerminalDigests(entry) ||
408
442
  !this.#isValidHistoryContinuation(prior, entry)
409
443
  )
410
- return undefined;
444
+ return { kind: "rejected", reason: "row-not-attributable" };
411
445
  prior = entry;
412
446
  latest = entry;
413
447
  }
@@ -417,9 +451,11 @@ export class LifecycleLedger {
417
451
  // a partially persisted row from this identity without inspecting arbitrary
418
452
  // malformed data as a valid record.
419
453
  const identityMarker = Buffer.from(`"identity":${JSON.stringify(identity)}`);
420
- if (tail.includes(identityMarker)) return undefined;
454
+ if (tail.includes(identityMarker)) return { kind: "rejected", reason: "partial-row" };
421
455
  }
422
- return latest && final(latest.state) ? latest : undefined;
456
+ // No matching row, or one that has not reached a terminal state yet, is genuine
457
+ // absence: nothing was rejected, there is simply nothing terminal to read back.
458
+ return latest && final(latest.state) ? { kind: "terminal", entry: latest } : { kind: "absent" };
423
459
  }
424
460
 
425
461
  /**
@@ -512,19 +548,21 @@ export class LifecycleLedger {
512
548
  if (handle) await handle.close();
513
549
  }
514
550
  }
515
- async #readBoundedSourceReadOnly(): Promise<Buffer | undefined> {
551
+ async #readBoundedSourceReadOnly(): Promise<BoundedSourceRead> {
516
552
  let handle: fs.FileHandle | undefined;
517
553
  try {
518
- handle = await fs.open(this.#file, fsSync.constants.O_RDONLY | fsSync.constants.O_NOFOLLOW);
554
+ const nonBlocking = process.platform === "win32" ? 0 : fsSync.constants.O_NONBLOCK;
555
+ handle = await fs.open(this.#file, fsSync.constants.O_RDONLY | fsSync.constants.O_NOFOLLOW | nonBlocking);
519
556
  const stat = await handle.stat({ bigint: true });
520
- if (!stat.isFile() || stat.size > BigInt(this.#limits.maxBytes)) return undefined;
557
+ if (!stat.isFile()) return { kind: "rejected", reason: "not-regular-file" };
558
+ if (stat.size > BigInt(this.#limits.maxBytes)) return { kind: "rejected", reason: "oversized-by-stat" };
521
559
  const bytes = Buffer.alloc(Number(stat.size) + 1);
522
560
  const { bytesRead } = await handle.read(bytes, 0, bytes.length, 0);
523
- if (bytesRead > this.#limits.maxBytes) return undefined;
524
- return bytes.subarray(0, bytesRead);
561
+ if (bytesRead > this.#limits.maxBytes) return { kind: "rejected", reason: "oversized-by-read" };
562
+ return { kind: "source", source: bytes.subarray(0, bytesRead) };
525
563
  } catch (error) {
526
- if ((error as NodeJS.ErrnoException).code === "ENOENT") return undefined;
527
- return undefined;
564
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") return { kind: "absent" };
565
+ return { kind: "rejected", reason: "read-error" };
528
566
  } finally {
529
567
  if (handle) await handle.close();
530
568
  }
@@ -621,7 +659,7 @@ export class LifecycleLedger {
621
659
  for (const [identity, latest] of compacted) {
622
660
  const anchor = anchors.get(identity);
623
661
  if (!anchor) throw new Error("Lifecycle ledger compaction requires an accepted identity anchor.");
624
- snapshot.push(anchor);
662
+ snapshot.push(replacement?.identity === identity && latest.state === "accepted" ? latest : anchor);
625
663
  if (latest.state !== "accepted") snapshot.push(latest);
626
664
  }
627
665
  const contents = Buffer.from(snapshot.map(entry => `${JSON.stringify(entry)}\n`).join(""));
@@ -663,16 +701,24 @@ export class LifecycleLedger {
663
701
  }
664
702
  return replacement !== undefined;
665
703
  }
666
- findByOperationKey(operationKey: string): LifecycleLedgerEntry | undefined {
667
- return [...this.#byIdentity.values()].find(entry => entry.operationKey === operationKey);
704
+ findByOperationKey(operationKey: string, fingerprint?: string): LifecycleLedgerEntry | undefined {
705
+ return [...this.#byIdentity.values()].findLast(
706
+ entry =>
707
+ entry.operationKey === operationKey && (fingerprint === undefined || entry.fingerprint === fingerprint),
708
+ );
709
+ }
710
+ findAnyByOperationKey(operationKey: string): LifecycleLedgerEntry | undefined {
711
+ return [...this.#byIdentity.values()].findLast(entry => entry.operationKey === operationKey);
668
712
  }
669
713
  /**
670
714
  * Legacy target-inclusive rows predate the operation/key index. Their opaque
671
715
  * identities cannot establish that a different target is safe, so callers
672
716
  * must reject rather than create a second admission.
673
717
  */
674
- hasLegacyIdentity(): boolean {
675
- return [...this.#byIdentity.values()].some(entry => entry.operationKey === undefined);
718
+ hasLegacyIdentity(excludedIdentities?: ReadonlySet<string>): boolean {
719
+ return [...this.#byIdentity.values()].some(
720
+ entry => entry.operationKey === undefined && !excludedIdentities?.has(entry.identity),
721
+ );
676
722
  }
677
723
  async migrateIdentity(
678
724
  from: string,
@@ -680,15 +726,32 @@ export class LifecycleLedger {
680
726
  metadata: { operationKey: string; fingerprint: string },
681
727
  ): Promise<LifecycleLedgerEntry | undefined> {
682
728
  return this.#mutate(async () => {
683
- if (this.#byIdentity.has(to)) return this.#byIdentity.get(to);
729
+ const existing = this.#byIdentity.get(to);
684
730
  const entry = this.#byIdentity.get(from);
685
- if (!entry) return undefined;
686
- const migrated = await this.#append({ ...entry, identity: to, ...metadata, ts: Date.now() });
687
- // Retire the legacy row so hasLegacyIdentity() returns false and future
688
- // unrelated lifecycle requests are not globally blocked. The legacy
689
- // identity gets a metadata-bearing replacement row in the append-only
690
- // log, superseding the original metadata-free entry in #byIdentity.
691
- if (entry.operationKey === undefined) await this.#append({ ...entry, ...metadata, ts: Date.now() });
731
+ if (!entry) return existing;
732
+ if (existing) {
733
+ if (entry.operationKey === undefined) {
734
+ await this.#compact({ ...entry, ...metadata, ts: Date.now() });
735
+ }
736
+ return existing;
737
+ }
738
+ // A migrated identity needs the same accepted anchor as a fresh request;
739
+ // appending only a terminal row would be quarantined on the next restart.
740
+ await this.#append({
741
+ version: SDK_STATE_VERSION,
742
+ identity: to,
743
+ requestHash: entry.requestHash,
744
+ ...metadata,
745
+ state: "accepted",
746
+ ts: Date.now(),
747
+ });
748
+ const migrated =
749
+ entry.state === "accepted"
750
+ ? this.#byIdentity.get(to)
751
+ : await this.#append({ ...entry, identity: to, ...metadata, ts: Date.now() });
752
+ // Retire metadata-free legacy rows by rewriting their latest row in place;
753
+ // appending a duplicate terminal row would violate the ledger history rules.
754
+ if (entry.operationKey === undefined) await this.#compact({ ...entry, ...metadata, ts: Date.now() });
692
755
  return migrated;
693
756
  });
694
757
  }
@@ -723,7 +786,7 @@ export class LifecycleLedger {
723
786
  async begin(
724
787
  identity: string,
725
788
  requestHash: string,
726
- metadata: { operationKey?: string; fingerprint?: string } = {},
789
+ metadata: { operationKey?: string; fingerprint?: string; intendedSessionId?: string } = {},
727
790
  ): Promise<BeginResult> {
728
791
  return this.#mutate(async () => this.#begin(identity, requestHash, metadata));
729
792
  }
@@ -731,7 +794,7 @@ export class LifecycleLedger {
731
794
  async #begin(
732
795
  identity: string,
733
796
  requestHash: string,
734
- metadata: { operationKey?: string; fingerprint?: string },
797
+ metadata: { operationKey?: string; fingerprint?: string; intendedSessionId?: string },
735
798
  ): Promise<BeginResult> {
736
799
  const prior = this.#byIdentity.get(identity);
737
800
  if (!prior)
@@ -743,6 +806,7 @@ export class LifecycleLedger {
743
806
  requestHash,
744
807
  operationKey: metadata.operationKey,
745
808
  fingerprint: metadata.fingerprint,
809
+ intendedSessionId: metadata.intendedSessionId,
746
810
  state: "accepted",
747
811
  ts: Date.now(),
748
812
  }),