@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
@@ -1,6 +1,14 @@
1
1
  import { createHash, randomBytes } from "node:crypto";
2
2
  import * as fs from "node:fs/promises";
3
+ import * as os from "node:os";
3
4
  import * as path from "node:path";
5
+ import {
6
+ exactUnlink,
7
+ linkNoReplacePath,
8
+ type NativeExactFileIdentity,
9
+ type NativeNoReplaceResult,
10
+ renameNoReplacePath,
11
+ } from "@gajae-code/natives";
4
12
  import { compileGjcPluginBundle } from "./compiler";
5
13
  import { migrateGjcPluginEntries } from "./migration";
6
14
  import { gjcPluginProjectRoot, gjcPluginUserRoot } from "./paths";
@@ -11,6 +19,11 @@ const LOCK_FILENAME = "registry.lock";
11
19
  const LOCK_TIMEOUT_MS = 5000;
12
20
  const LOCK_RETRY_MS = 50;
13
21
 
22
+ export const RegistryLockTestHooks: {
23
+ beforeEviction?: (lockPath: string, raw: string) => void | Promise<void>;
24
+ beforePublish?: (stagingPath: string, lockPath: string) => void | Promise<void>;
25
+ } = {};
26
+
14
27
  export function registryRootForScope(scope: GjcPluginScope, cwd: string): string {
15
28
  return scope === "user" ? gjcPluginUserRoot() : gjcPluginProjectRoot(cwd);
16
29
  }
@@ -323,57 +336,203 @@ export async function readRegistry(
323
336
  if (!migrated.changed && discovered.length === 0) return registry;
324
337
  // Re-check under the lock before persisting. Migration and legacy-root
325
338
  // discovery are one transaction, never a normal runtime loader path.
326
- return await withRegistryLock(scope, cwd, async () => {
327
- const latest = await readRegistryRaw(scope, cwd);
328
- const latestDiscovered = await discoverLegacyEntries(scope, cwd, latest.plugins);
329
- const latestMigrated = await migrateGjcPluginEntries([...latest.plugins, ...latestDiscovered]);
330
- if (latestMigrated.changed || latestDiscovered.length > 0) {
331
- const next: GjcPluginRegistry = { ...latest, plugins: sortRegistryEntries(latestMigrated.entries) };
332
- await writeRegistryUnlocked(next, cwd, scope);
333
- return next;
339
+ // Contention (a concurrent install elsewhere) must not fail startup reads:
340
+ // the effective registry is already computed, so degrade to returning it
341
+ // unpersisted and let a later uncontended session write the migrated
342
+ // state. Mutating install paths keep their fail-loud install_conflict.
343
+ try {
344
+ return await withRegistryLock(scope, cwd, async () => {
345
+ const latest = await readRegistryRaw(scope, cwd);
346
+ const latestDiscovered = await discoverLegacyEntries(scope, cwd, latest.plugins);
347
+ const latestMigrated = await migrateGjcPluginEntries([...latest.plugins, ...latestDiscovered]);
348
+ if (latestMigrated.changed || latestDiscovered.length > 0) {
349
+ const next: GjcPluginRegistry = { ...latest, plugins: sortRegistryEntries(latestMigrated.entries) };
350
+ await writeRegistryUnlocked(next, cwd, scope);
351
+ return next;
352
+ }
353
+ return latest;
354
+ });
355
+ } catch (error) {
356
+ if (error instanceof GjcPluginLoadError && error.code === "install_conflict") {
357
+ return { ...registry, plugins: sortRegistryEntries(migrated.entries) };
334
358
  }
335
- return latest;
359
+ throw error;
360
+ }
361
+ }
362
+
363
+ const LOCK_NONCE_PATTERN = /^[0-9a-f]{16}$/;
364
+
365
+ interface RegistryLockToken {
366
+ pid: number;
367
+ /** Host segment of a host-tagged token; null for legacy `pid-nonce` tokens. */
368
+ host: string | null;
369
+ }
370
+
371
+ function parseRegistryLockToken(raw: string): RegistryLockToken | null {
372
+ const parts = raw.trim().split("-");
373
+ if (parts.length < 2) return null;
374
+ const pid = Number(parts[0]);
375
+ const nonce = parts[parts.length - 1];
376
+ if (!Number.isInteger(pid) || pid <= 0 || !LOCK_NONCE_PATTERN.test(nonce)) return null;
377
+ // Hostnames may contain '-', so everything between the first and last
378
+ // segment is the host; a single middle segment that is empty (or absent)
379
+ // means a legacy token.
380
+ const host = parts.slice(1, -1).join("-");
381
+ return { pid, host: host.length > 0 ? host : null };
382
+ }
383
+
384
+ /**
385
+ * A holder is assumed alive unless provably dead: ourselves, a live PID
386
+ * (EPERM counts — the process exists under another user), or a lock written
387
+ * on another host (a project registry can live on shared storage, where a
388
+ * local PID probe would be meaningless) all stay fail-closed.
389
+ */
390
+ function registryLockHolderAlive(holder: RegistryLockToken): boolean {
391
+ if (holder.pid === process.pid) return true;
392
+ if (holder.host !== null && holder.host !== os.hostname()) return true;
393
+ try {
394
+ process.kill(holder.pid, 0);
395
+ return true;
396
+ } catch (error) {
397
+ return (error as NodeJS.ErrnoException)?.code === "EPERM";
398
+ }
399
+ }
400
+
401
+ async function captureRegistryLockIdentity(lockPath: string): Promise<NativeExactFileIdentity | null> {
402
+ try {
403
+ const [bytes, stat, parent] = await Promise.all([
404
+ fs.readFile(lockPath),
405
+ fs.lstat(lockPath, { bigint: true }),
406
+ fs.stat(path.dirname(lockPath), { bigint: true }),
407
+ ]);
408
+ if (!stat.isFile()) return null;
409
+ return {
410
+ dev: stat.dev,
411
+ ino: stat.ino,
412
+ nlink: stat.nlink,
413
+ parentDev: parent.dev,
414
+ parentIno: parent.ino,
415
+ size: stat.size,
416
+ mtimeNs: stat.mtimeNs,
417
+ sha256: createHash("sha256").update(bytes).digest("hex"),
418
+ };
419
+ } catch (error) {
420
+ if (isEnoent(error)) return null;
421
+ throw error;
422
+ }
423
+ }
424
+
425
+ async function evictRegistryLock(lockPath: string, raw: string): Promise<boolean> {
426
+ const identity = await captureRegistryLockIdentity(lockPath);
427
+ if (!identity) return false;
428
+ const expectedHash = createHash("sha256").update(raw).digest("hex");
429
+ if (identity.sha256 !== expectedHash) return false;
430
+ await RegistryLockTestHooks.beforeEviction?.(lockPath, raw);
431
+ const result = exactUnlink(lockPath, {
432
+ ...identity,
433
+ quarantineName: `${LOCK_FILENAME}.evict-${process.pid}-${randomBytes(4).toString("hex")}`,
336
434
  });
435
+ return result.ok || result.code === "cleanup_pending";
436
+ }
437
+
438
+ function publishRegistryLock(stagingPath: string, lockPath: string): NativeNoReplaceResult {
439
+ let result = renameNoReplacePath(stagingPath, lockPath);
440
+ if (
441
+ !result.ok &&
442
+ result.mutationState === "not_committed" &&
443
+ (result.reason === "atomic_unavailable" || result.reason === "invalid_request")
444
+ ) {
445
+ // linkat is the no-overwrite fallback on filesystems without a
446
+ // no-replace rename. Both paths expose the complete token only after the
447
+ // atomic claim, so a crash cannot publish an empty or partial lock.
448
+ result = linkNoReplacePath(stagingPath, lockPath);
449
+ }
450
+ return result;
337
451
  }
338
452
 
339
453
  async function acquireLock(lockPath: string): Promise<() => Promise<void>> {
340
454
  await fs.mkdir(path.dirname(lockPath), { recursive: true });
341
- const token = `${process.pid}-${randomBytes(8).toString("hex")}`;
455
+ const token = `${process.pid}-${os.hostname()}-${randomBytes(8).toString("hex")}`;
342
456
  const deadline = Date.now() + LOCK_TIMEOUT_MS;
343
457
  for (;;) {
458
+ const stagingPath = `${lockPath}.tmp-${process.pid}-${randomBytes(4).toString("hex")}`;
459
+ let publication: NativeNoReplaceResult | undefined;
344
460
  try {
345
- const handle = await fs.open(lockPath, "wx");
346
- try {
347
- await handle.writeFile(token);
348
- } finally {
349
- await handle.close();
461
+ await fs.writeFile(stagingPath, token, { encoding: "utf8", flag: "wx", mode: 0o600 });
462
+ await RegistryLockTestHooks.beforePublish?.(stagingPath, lockPath);
463
+ publication = publishRegistryLock(stagingPath, lockPath);
464
+ if (publication.ok) {
465
+ let released = false;
466
+ return async () => {
467
+ if (released) return;
468
+ released = true;
469
+ // Owner-safe release: only remove the lock if it is still ours.
470
+ try {
471
+ const current = await fs.readFile(lockPath, "utf8");
472
+ if (current === token) await fs.rm(lockPath, { force: true });
473
+ } catch {
474
+ // Lock already gone; nothing to release.
475
+ }
476
+ };
477
+ }
478
+ if (publication.reason !== "destination_exists") {
479
+ throw new Error(
480
+ `Failed to publish GJC plugin registry lock at ${lockPath}: ${publication.code ?? publication.reason}`,
481
+ );
350
482
  }
351
- let released = false;
352
- return async () => {
353
- if (released) return;
354
- released = true;
355
- // Owner-safe release: only remove the lock if it is still ours.
356
- try {
357
- const current = await fs.readFile(lockPath, "utf8");
358
- if (current === token) await fs.rm(lockPath, { force: true });
359
- } catch {
360
- // Lock already gone; nothing to release.
361
- }
362
- };
363
483
  } catch (error) {
364
- if ((error as NodeJS.ErrnoException)?.code !== "EEXIST") throw error;
365
- // Fail-closed: never auto-evict an existing lock (a live holder may run
366
- // longer than the timeout). Time out instead and leave the lock for
367
- // diagnostics/manual cleanup. A lease/heartbeat protocol can be added
368
- // later if automatic stale recovery becomes necessary.
369
- if (Date.now() > deadline) {
370
- throw new GjcPluginLoadError(
371
- "install_conflict",
372
- `Timed out acquiring GJC plugin registry lock at ${lockPath}; remove it manually if no install is running`,
484
+ if (publication?.reason === "destination_exists") {
485
+ // Continue into stale inspection below.
486
+ } else if (
487
+ publication === undefined &&
488
+ ["EACCES", "EPERM"].includes((error as NodeJS.ErrnoException)?.code ?? "")
489
+ ) {
490
+ // A read-only registry directory can still contain a lock left by a
491
+ // dead writer. Inspect that lock and return the normal bounded
492
+ // install_conflict rather than surfacing the staging-file permission
493
+ // error immediately.
494
+ const lockExists = await fs.stat(lockPath).then(
495
+ () => true,
496
+ (error: unknown) => !isEnoent(error),
373
497
  );
498
+ if (!lockExists) throw error;
499
+ } else throw error;
500
+ } finally {
501
+ if (publication?.mutationState !== "unknown") await fs.rm(stagingPath, { force: true }).catch(() => {});
502
+ }
503
+
504
+ // Stale recovery: a writer that died mid-transaction never releases
505
+ // its lock, and without recovery that lock poisons every later
506
+ // session until manual cleanup. Evict only a provably dead holder:
507
+ // a host-tagged token on this host. Legacy `pid-nonce` tokens have no
508
+ // trustworthy host identity, so they always fail closed: a local PID
509
+ // probe cannot prove that a remote holder on shared storage is dead.
510
+ // Live holders still fail closed below — they may legitimately outlive
511
+ // our timeout.
512
+ const raw = await fs.readFile(lockPath, "utf8").catch(() => null);
513
+ const holder = raw === null ? null : parseRegistryLockToken(raw);
514
+ if (raw !== null && holder !== null && holder.host !== null && !registryLockHolderAlive(holder)) {
515
+ try {
516
+ // exactUnlink verifies the observed inode, parent, size, mtime,
517
+ // and bytes inside the removal primitive. A replacement published
518
+ // after this read is therefore never consumed by stale recovery.
519
+ await evictRegistryLock(lockPath, raw);
520
+ } catch {
521
+ // Raced with another evictor or a fresh holder, or the removal
522
+ // itself failed (lockfile owned by another user, read-only mount,
523
+ // EBUSY/EPERM on Windows). Fall through to the shared deadline
524
+ // and backoff below either way: a removal that keeps failing must
525
+ // end in the bounded install_conflict timeout, never an unbounded
526
+ // spin.
374
527
  }
375
- await new Promise(resolve => setTimeout(resolve, LOCK_RETRY_MS));
376
528
  }
529
+ if (Date.now() > deadline) {
530
+ throw new GjcPluginLoadError(
531
+ "install_conflict",
532
+ `Timed out acquiring GJC plugin registry lock at ${lockPath}; remove it manually if no install is running`,
533
+ );
534
+ }
535
+ await new Promise(resolve => setTimeout(resolve, LOCK_RETRY_MS));
377
536
  }
378
537
  }
379
538
 
@@ -54,6 +54,16 @@ export interface RuntimeSkillDiscoveryResult {
54
54
  candidates: RuntimeSkillDiscoveryCandidate[];
55
55
  /** Deduped skill metadata the query filter ran against, including bundled entries; candidates.length <= scanned. */
56
56
  scanned: number;
57
+ /**
58
+ * Skills left after the query filter, before the page slice: the denominator
59
+ * a page is a page *of*. `scanned` counts everything the filter ran against,
60
+ * so it cannot serve this role.
61
+ */
62
+ matching: number;
63
+ /** Normalized zero-based offset the returned page starts at. */
64
+ offset: number;
65
+ /** Offset of the next page, present only while one exists; omitted on the final page. */
66
+ nextOffset?: number;
57
67
  diagnostics: RuntimeSkillDiscoveryDiagnostics;
58
68
  }
59
69
 
@@ -65,6 +75,8 @@ export interface DiscoverRuntimeSkillsOptions {
65
75
  profileAuthority?: "default" | "custom";
66
76
  query?: string;
67
77
  limit?: number;
78
+ /** Zero-based index into the matching set the returned page starts at; defaults to 0. */
79
+ offset?: number;
68
80
  source?: RuntimeSkillDiscoveryScope | "all";
69
81
  policy?: SkillsSettings;
70
82
  }
@@ -74,11 +86,26 @@ function getRuntimeHome(): string {
74
86
  }
75
87
 
76
88
  const DEFAULT_LIMIT = 20;
77
- const MAX_LIMIT = 50;
89
+ /**
90
+ * Upper bound every caller's `limit` clamps to. Exported so `gjc skills discover`
91
+ * can default a human to the widest page the library will serve instead of the
92
+ * model-context-sized `DEFAULT_LIMIT`.
93
+ */
94
+ export const SKILL_DISCOVERY_MAX_LIMIT = 50;
78
95
  const MAX_DIAGNOSTICS = 10;
79
96
  function normalizeLimit(limit: number | undefined): number {
80
97
  if (limit === undefined || !Number.isFinite(limit)) return DEFAULT_LIMIT;
81
- return Math.max(1, Math.min(MAX_LIMIT, Math.trunc(limit)));
98
+ return Math.max(1, Math.min(SKILL_DISCOVERY_MAX_LIMIT, Math.trunc(limit)));
99
+ }
100
+
101
+ /**
102
+ * Offsets are clamped from below only. An offset past the end is a legitimate
103
+ * empty final page (the catalog shrank between pages), not an error, so it is
104
+ * reported rather than silently snapped back onto the last populated page.
105
+ */
106
+ function normalizeOffset(offset: number | undefined): number {
107
+ if (offset === undefined || !Number.isFinite(offset) || offset < 0) return 0;
108
+ return Math.trunc(offset);
82
109
  }
83
110
 
84
111
  interface ProjectScanDir {
@@ -558,9 +585,40 @@ export async function discoverRuntimeSkills(
558
585
  seenNames,
559
586
  diagnostics,
560
587
  );
588
+ const limit = normalizeLimit(options.limit);
589
+ const offset = normalizeOffset(options.offset);
590
+ const matching = candidates.length;
591
+ const page = candidates.slice(offset, offset + limit);
592
+ // Continuation is omitted on the final page, so "is there more" is the
593
+ // presence of the field rather than a value the caller has to compare.
594
+ const nextOffset = offset + page.length < matching ? offset + page.length : undefined;
595
+ // Appended directly instead of through pushDiagnostic: these are the only
596
+ // messages that explain why a skill the caller can see on disk is absent
597
+ // from the result, and the reported case (several stale
598
+ // skills.customDirectories entries, one "does not exist" message each)
599
+ // fills the MAX_DIAGNOSTICS budget before the slice is ever reached. A
600
+ // dropped paging notice makes the truncation invisible again.
601
+ if (page.length === 0) {
602
+ if (offset > 0 && matching > 0) {
603
+ diagnostics.push(
604
+ `no skills at offset ${offset}; ${matching} matching skill${matching === 1 ? "" : "s"} total, pass --offset 0 to start over`,
605
+ );
606
+ }
607
+ } else if (nextOffset !== undefined) {
608
+ diagnostics.push(
609
+ `showing ${offset + 1}-${offset + page.length} of ${matching} matching skills; pass --offset ${nextOffset} for the next page, or narrow the query`,
610
+ );
611
+ } else if (offset > 0) {
612
+ // Final page of a multi-page walk: no continuation to offer, but the
613
+ // caller still needs to know this page is not the whole matching set.
614
+ diagnostics.push(`showing ${offset + 1}-${offset + page.length} of ${matching} matching skills`);
615
+ }
561
616
  return {
562
- candidates: candidates.slice(0, normalizeLimit(options.limit)),
617
+ candidates: page,
563
618
  scanned,
619
+ matching,
620
+ offset,
621
+ ...(nextOffset === undefined ? {} : { nextOffset }),
564
622
  diagnostics: { messages: diagnostics },
565
623
  };
566
624
  }
@@ -93,9 +93,11 @@ export interface SessionCompactEvent {
93
93
  fromExtension: boolean;
94
94
  }
95
95
 
96
- /** Fired on process exit (SIGINT/SIGTERM) */
96
+ /** Fired during session teardown, including process exit and SDK host self-reap. */
97
97
  export interface SessionShutdownEvent {
98
98
  type: "session_shutdown";
99
+ /** Set only when the SDK host reaped itself after detached-idle grace. */
100
+ reason?: "detached_idle";
99
101
  }
100
102
 
101
103
  /** Preparation data for tree navigation (used by session_before_tree event) */
@@ -128,8 +128,15 @@ function safeChildToken(value: string): boolean {
128
128
  }
129
129
  }
130
130
 
131
- async function readExactJsons(root: string, files: readonly string[]): Promise<unknown[] | null> {
132
- if (process.platform !== "linux") return null;
131
+ /**
132
+ * The exact reader needs the Linux-only recovery-fs authority, so an unavailable
133
+ * read has two very different causes. A blocked operator cannot act on either
134
+ * unless the outcome says which one it was, so the unavailable arm names it.
135
+ */
136
+ type ExactJsonRead = { values: unknown[] } | { reader: "unsupported_platform" | "read_failed" };
137
+
138
+ async function readExactJsons(root: string, files: readonly string[]): Promise<ExactJsonRead> {
139
+ if (process.platform !== "linux") return { reader: "unsupported_platform" };
133
140
  try {
134
141
  const { openRecoveryFsRoot } = require("@gajae-code/natives") as Pick<
135
142
  typeof import("@gajae-code/natives"),
@@ -140,17 +147,37 @@ async function readExactJsons(root: string, files: readonly string[]): Promise<u
140
147
  const values: unknown[] = [];
141
148
  for (const file of files) {
142
149
  const result = authority.read(file, 64 * 1024);
143
- if (!result.ok || !result.data) return null;
150
+ if (!result.ok || !result.data) return { reader: "read_failed" };
144
151
  const content = Buffer.from(result.data).toString("utf8");
145
- if (!content.endsWith("\n") || content.indexOf("\n") !== content.length - 1) return null;
152
+ if (!content.endsWith("\n") || content.indexOf("\n") !== content.length - 1)
153
+ return { reader: "read_failed" };
146
154
  values.push(JSON.parse(content));
147
155
  }
148
- return values;
156
+ return { values };
149
157
  } finally {
150
158
  authority.close();
151
159
  }
152
160
  } catch {
153
- return null;
161
+ return { reader: "read_failed" };
162
+ }
163
+ }
164
+
165
+ function exactJsonValues(read: ExactJsonRead): unknown[] {
166
+ return "values" in read ? read.values : [];
167
+ }
168
+
169
+ function exactReaderDetails(read: ExactJsonRead): Record<string, unknown> {
170
+ return "values" in read ? {} : { platform: process.platform, evidence_reader: read.reader };
171
+ }
172
+
173
+ function readerExplanation(details: Record<string, unknown>): string {
174
+ switch (details.evidence_reader) {
175
+ case "unsupported_platform":
176
+ return ` (platform ${process.platform}: exact binding reader unsupported)`;
177
+ case "read_failed":
178
+ return ` (platform ${process.platform}: exact binding evidence unreadable)`;
179
+ default:
180
+ return "";
154
181
  }
155
182
  }
156
183
 
@@ -187,6 +214,22 @@ async function durableHandoff(
187
214
  }
188
215
  }
189
216
 
217
+ /**
218
+ * Fails closed and says so. The durable handoff is the record of authority, but
219
+ * nothing reads it during an incident, so the same reason also goes to stderr:
220
+ * otherwise a blocked child exits 75 having printed nothing at all.
221
+ */
222
+ async function blockAdmission(
223
+ owner: { root: string; generation: string; sessionId: string },
224
+ reason: string,
225
+ details: Record<string, unknown> = {},
226
+ ): Promise<{ kind: "blocked" }> {
227
+ await durableHandoff(owner.root, owner.generation, owner.sessionId, reason, details);
228
+ process.stderr.write(`child admission blocked: ${reason}${readerExplanation(details)}\n`);
229
+ process.exitCode = 75;
230
+ return { kind: "blocked" };
231
+ }
232
+
190
233
  /**
191
234
  * This is the pre-CLI barrier. A replacement is identified only by the exact
192
235
  * predecessor token supplied by its launch binding; directory enumeration is
@@ -198,12 +241,9 @@ export async function admitManagedOwnerBeforeCli(): Promise<ManagedOwnerAdmissio
198
241
  const childToken = process.env[MANAGED_OWNER_CHILD_TOKEN_ENV]?.trim();
199
242
  const predecessorToken = process.env[MANAGED_OWNER_PREDECESSOR_TOKEN_ENV]?.trim();
200
243
  if (childToken && !predecessorToken) {
201
- if (!safeChildToken(childToken)) {
202
- await durableHandoff(owner.root, owner.generation, owner.sessionId, "exact_child_binding_unavailable");
203
- process.exitCode = 75;
204
- return { kind: "blocked" };
205
- }
206
- const [binding] = (await readExactJsons(owner.root, [`child-${childToken}.binding.json`])) ?? [];
244
+ if (!safeChildToken(childToken)) return blockAdmission(owner, "exact_child_binding_unavailable");
245
+ const read = await readExactJsons(owner.root, [`child-${childToken}.binding.json`]);
246
+ const [binding] = exactJsonValues(read);
207
247
  if (
208
248
  isBinding(binding, {
209
249
  generation: owner.generation,
@@ -214,33 +254,20 @@ export async function admitManagedOwnerBeforeCli(): Promise<ManagedOwnerAdmissio
214
254
  })
215
255
  )
216
256
  return { kind: "supervised" };
217
- await durableHandoff(owner.root, owner.generation, owner.sessionId, "exact_child_binding_unavailable");
218
- process.exitCode = 75;
219
- return { kind: "blocked" };
257
+ return blockAdmission(owner, "exact_child_binding_unavailable", exactReaderDetails(read));
220
258
  }
221
259
  const predecessorGeneration = process.env[MANAGED_OWNER_PREDECESSOR_GENERATION_ENV]?.trim();
222
260
  const predecessorRunId = process.env[MANAGED_OWNER_PREDECESSOR_RUN_ID_ENV]?.trim();
223
261
  const predecessorIncarnation = process.env[MANAGED_OWNER_PREDECESSOR_INCARNATION_ENV]?.trim();
224
- if (!predecessorGeneration || !predecessorRunId || !predecessorIncarnation) {
225
- await durableHandoff(owner.root, owner.generation, owner.sessionId, "replacement_predecessor_identity_missing");
226
- process.exitCode = 75;
227
- return { kind: "blocked" };
228
- }
229
- if (!predecessorToken) {
230
- await durableHandoff(owner.root, owner.generation, owner.sessionId, "replacement_predecessor_binding_missing");
231
- process.exitCode = 75;
232
- return { kind: "blocked" };
233
- }
234
- if (!safeChildToken(predecessorToken)) {
235
- await durableHandoff(owner.root, owner.generation, owner.sessionId, "replacement_predecessor_binding_untrusted");
236
- process.exitCode = 75;
237
- return { kind: "blocked" };
238
- }
239
- const [binding, receipt] =
240
- (await readExactJsons(owner.root, [
241
- `child-${predecessorToken}.binding.json`,
242
- `sigabrt-${predecessorToken}.receipt.json`,
243
- ])) ?? [];
262
+ if (!predecessorGeneration || !predecessorRunId || !predecessorIncarnation)
263
+ return blockAdmission(owner, "replacement_predecessor_identity_missing");
264
+ if (!predecessorToken) return blockAdmission(owner, "replacement_predecessor_binding_missing");
265
+ if (!safeChildToken(predecessorToken)) return blockAdmission(owner, "replacement_predecessor_binding_untrusted");
266
+ const read = await readExactJsons(owner.root, [
267
+ `child-${predecessorToken}.binding.json`,
268
+ `sigabrt-${predecessorToken}.receipt.json`,
269
+ ]);
270
+ const [binding, receipt] = exactJsonValues(read);
244
271
  if (
245
272
  !isBinding(binding, {
246
273
  generation: predecessorGeneration,
@@ -249,16 +276,10 @@ export async function admitManagedOwnerBeforeCli(): Promise<ManagedOwnerAdmissio
249
276
  incarnation: predecessorIncarnation,
250
277
  token: predecessorToken,
251
278
  })
252
- ) {
253
- await durableHandoff(owner.root, owner.generation, owner.sessionId, "replacement_predecessor_binding_untrusted");
254
- process.exitCode = 75;
255
- return { kind: "blocked" };
256
- }
257
- if (!isReceipt(receipt, binding)) {
258
- await durableHandoff(owner.root, owner.generation, owner.sessionId, "exact_sigabrt_receipt_untrusted");
259
- process.exitCode = 75;
260
- return { kind: "blocked" };
261
- }
279
+ )
280
+ return blockAdmission(owner, "replacement_predecessor_binding_untrusted", exactReaderDetails(read));
281
+ if (!isReceipt(receipt, binding))
282
+ return blockAdmission(owner, "exact_sigabrt_receipt_untrusted", exactReaderDetails(read));
262
283
  const admission = {
263
284
  session_id: owner.sessionId,
264
285
  endpoint_incarnation: predecessorIncarnation,
@@ -287,11 +308,7 @@ export async function admitManagedOwnerBeforeCli(): Promise<ManagedOwnerAdmissio
287
308
  },
288
309
  decision,
289
310
  });
290
- if (decision.disposition !== "resume") {
291
- await durableHandoff(owner.root, owner.generation, owner.sessionId, decision.reason);
292
- process.exitCode = 75;
293
- return { kind: "blocked" };
294
- }
311
+ if (decision.disposition !== "resume") return blockAdmission(owner, decision.reason);
295
312
  return { kind: "recovery", context: { root: owner.root, binding, receipt, admission, decision } };
296
313
  }
297
314