@gajae-code/coding-agent 0.17.2 → 0.17.5

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 (390) hide show
  1. package/CHANGELOG.md +267 -0
  2. package/dist/types/cli/daemon-cli.d.ts +3 -1
  3. package/dist/types/cli/github-release.d.ts +17 -0
  4. package/dist/types/cli/public-command-entry.d.ts +56 -0
  5. package/dist/types/cli/public-command-errors.d.ts +126 -0
  6. package/dist/types/cli/public-command-evidence.d.ts +99 -0
  7. package/dist/types/cli/public-command-help.d.ts +65 -0
  8. package/dist/types/cli/public-command-registry.d.ts +69 -0
  9. package/dist/types/cli/session-picker.d.ts +1 -1
  10. package/dist/types/cli/setup-cli.d.ts +1 -0
  11. package/dist/types/cli/skills-cli.d.ts +5 -0
  12. package/dist/types/cli.d.ts +3 -0
  13. package/dist/types/commands/daemon.d.ts +4 -50
  14. package/dist/types/commands/sdk.d.ts +20 -37
  15. package/dist/types/commands/setup.d.ts +3 -0
  16. package/dist/types/commands/skills.d.ts +9 -0
  17. package/dist/types/config/autorouting-tier-map.d.ts +1049 -3
  18. package/dist/types/config/file-lock.d.ts +13 -3
  19. package/dist/types/config/model-profile-activation.d.ts +45 -1
  20. package/dist/types/config/model-registry.d.ts +14 -3
  21. package/dist/types/config/model-resolver.d.ts +1 -1
  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/daemon/operator-contract.d.ts +5 -1
  25. package/dist/types/eval/js/worker-protocol.d.ts +2 -0
  26. package/dist/types/exec/bash-executor.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/gjc-plugins/runtime-adapters.d.ts +11 -2
  30. package/dist/types/extensibility/runtime-skill-discovery.d.ts +18 -0
  31. package/dist/types/extensibility/shared-events.d.ts +3 -1
  32. package/dist/types/gjc-runtime/session-state-lock.d.ts +61 -0
  33. package/dist/types/gjc-runtime/state-writer.d.ts +14 -1
  34. package/dist/types/harness-control-plane/control-endpoint.d.ts +4 -1
  35. package/dist/types/harness-control-plane/finalize.d.ts +14 -3
  36. package/dist/types/internal-urls/embedded-protocol.d.ts +9 -0
  37. package/dist/types/internal-urls/index.d.ts +1 -0
  38. package/dist/types/internal-urls/parse.d.ts +9 -1
  39. package/dist/types/main.d.ts +23 -1
  40. package/dist/types/modes/acp/acp-agent.d.ts +55 -1
  41. package/dist/types/modes/acp/startup-provenance.d.ts +16 -0
  42. package/dist/types/modes/components/custom-provider-wizard.d.ts +19 -1
  43. package/dist/types/modes/components/session-selector.d.ts +5 -2
  44. package/dist/types/modes/components/user-message-selector.d.ts +5 -2
  45. package/dist/types/modes/controllers/command-controller.d.ts +1 -1
  46. package/dist/types/modes/controllers/selector-controller.d.ts +1 -1
  47. package/dist/types/modes/interactive-mode.d.ts +1 -1
  48. package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +11 -1
  49. package/dist/types/modes/shared/agent-wire/workflow-gate-types.d.ts +14 -1
  50. package/dist/types/modes/types.d.ts +2 -2
  51. package/dist/types/runtime/process-lifecycle.d.ts +2 -0
  52. package/dist/types/runtime-mcp/loader.d.ts +2 -0
  53. package/dist/types/runtime-mcp/manager.d.ts +69 -6
  54. package/dist/types/runtime-mcp/pool.d.ts +17 -0
  55. package/dist/types/runtime-mcp/redaction.d.ts +20 -0
  56. package/dist/types/runtime-mcp/startup-policy.d.ts +6 -0
  57. package/dist/types/runtime-mcp/tool-bridge.d.ts +9 -0
  58. package/dist/types/runtime-mcp/transports/http.d.ts +11 -0
  59. package/dist/types/sdk/broker/broker.d.ts +17 -4
  60. package/dist/types/sdk/broker/ensure.d.ts +3 -0
  61. package/dist/types/sdk/broker/identity.d.ts +5 -1
  62. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +23 -3
  63. package/dist/types/sdk/broker/lifecycle.d.ts +15 -8
  64. package/dist/types/sdk/broker/managed-task-dag.d.ts +1335 -0
  65. package/dist/types/sdk/broker/managed-task-verification.d.ts +37 -0
  66. package/dist/types/sdk/broker/runtime.d.ts +1 -1
  67. package/dist/types/sdk/broker/session-index.d.ts +22 -3
  68. package/dist/types/sdk/broker/spawn-authority.d.ts +12 -0
  69. package/dist/types/sdk/broker/startup-failure.d.ts +4 -0
  70. package/dist/types/sdk/broker/stderr-drainer.d.ts +13 -0
  71. package/dist/types/sdk/bus/index.d.ts +19 -0
  72. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +7 -1
  73. package/dist/types/sdk/bus/telegram-daemon.d.ts +5 -0
  74. package/dist/types/sdk/cli/master-cli.d.ts +6 -10
  75. package/dist/types/sdk/cli/rows.d.ts +19 -1
  76. package/dist/types/sdk/cli/session-cli.d.ts +22 -5
  77. package/dist/types/sdk/client/client.d.ts +2 -0
  78. package/dist/types/sdk/guides/cli.d.ts +3 -4
  79. package/dist/types/sdk/guides/verify.d.ts +2 -0
  80. package/dist/types/sdk/host/host.d.ts +9 -0
  81. package/dist/types/sdk/host/session-runtime.d.ts +31 -0
  82. package/dist/types/sdk/host/types.d.ts +10 -0
  83. package/dist/types/sdk/index.d.ts +1 -0
  84. package/dist/types/sdk/prompt-deadline-flush.d.ts +85 -0
  85. package/dist/types/sdk/prompt-deadline-manager.d.ts +37 -0
  86. package/dist/types/sdk/prompt-tool-boundary.d.ts +73 -0
  87. package/dist/types/sdk/router/session-router.d.ts +2 -0
  88. package/dist/types/sdk/session.d.ts +3 -3
  89. package/dist/types/sdk/startup-capability.d.ts +2 -0
  90. package/dist/types/sdk/transport/relay.d.ts +1 -0
  91. package/dist/types/sdk/transport/serve-cli.d.ts +48 -4
  92. package/dist/types/sdk/transport/stdio.d.ts +5 -1
  93. package/dist/types/session/agent-session.d.ts +175 -23
  94. package/dist/types/session/fallback-chain-controller.d.ts +6 -6
  95. package/dist/types/session/fold-coordinator.d.ts +17 -3
  96. package/dist/types/session/internal/managed-session-storage.d.ts +7 -1
  97. package/dist/types/session/session-manager.d.ts +64 -1
  98. package/dist/types/session/session-work-lease.d.ts +14 -0
  99. package/dist/types/setup/model-onboarding-guidance.d.ts +1 -1
  100. package/dist/types/setup/provider-onboarding.d.ts +81 -1
  101. package/dist/types/task/types.d.ts +2 -0
  102. package/dist/types/tools/bash-interactive.d.ts +1 -0
  103. package/dist/types/tools/bash.d.ts +37 -11
  104. package/dist/types/tools/fetch.d.ts +10 -1
  105. package/dist/types/tools/job.d.ts +1 -1
  106. package/dist/types/tools/steer-fold.d.ts +31 -0
  107. package/dist/types/tools/subagent.d.ts +8 -1
  108. package/dist/types/utils/git.d.ts +28 -2
  109. package/dist/types/utils/mupdf-embedded.d.ts +2 -0
  110. package/dist/types/utils/mupdf.d.ts +4 -0
  111. package/dist/types/utils/session-title-generation.d.ts +6 -0
  112. package/dist/types/utils/title-generator.d.ts +11 -2
  113. package/dist/types/web/scrapers/types.d.ts +3 -0
  114. package/package.json +18 -10
  115. package/scripts/build-binary.ts +9 -3
  116. package/scripts/compile-args.ts +0 -9
  117. package/scripts/embed-mupdf.ts +52 -0
  118. package/scripts/generate-sdk-operation-inventory.ts +31 -1
  119. package/scripts/vendor-markit.ts +196 -0
  120. package/src/async/job-manager.ts +22 -1
  121. package/src/cli/daemon-cli.ts +53 -18
  122. package/src/cli/github-release.ts +87 -5
  123. package/src/cli/mcp-cli.ts +38 -4
  124. package/src/cli/public-command-entry.ts +450 -0
  125. package/src/cli/public-command-errors.ts +581 -0
  126. package/src/cli/public-command-evidence.ts +717 -0
  127. package/src/cli/public-command-help.ts +360 -0
  128. package/src/cli/public-command-registry.ts +541 -0
  129. package/src/cli/session-picker.ts +2 -1
  130. package/src/cli/setup-cli.ts +10 -1
  131. package/src/cli/skills-cli.ts +46 -2
  132. package/src/cli/stats-cli.ts +11 -1
  133. package/src/cli.ts +61 -1
  134. package/src/commands/daemon.ts +65 -69
  135. package/src/commands/sdk.ts +175 -117
  136. package/src/commands/setup.ts +2 -0
  137. package/src/commands/skills.ts +11 -0
  138. package/src/config/autorouting-tier-map.ts +543 -3
  139. package/src/config/file-lock.ts +206 -65
  140. package/src/config/model-discovery-manager.ts +4 -4
  141. package/src/config/model-profile-activation.ts +417 -66
  142. package/src/config/model-registry.ts +254 -36
  143. package/src/config/model-resolver.ts +6 -1
  144. package/src/config/settings-schema.ts +26 -2
  145. package/src/coordinator-mcp/policy.ts +10 -5
  146. package/src/coordinator-mcp/question-gate-codec.ts +17 -0
  147. package/src/coordinator-mcp/question-state.ts +179 -9
  148. package/src/coordinator-mcp/server.ts +521 -87
  149. package/src/coordinator-mcp/session-reaper.ts +6 -4
  150. package/src/crash/record-loader.ts +6 -2
  151. package/src/daemon/operator-contract.ts +26 -12
  152. package/src/defaults/gjc/skills/ultragoal/SKILL.md +1 -1
  153. package/src/eval/js/context-manager.ts +8 -8
  154. package/src/eval/js/worker-core.ts +9 -4
  155. package/src/eval/js/worker-protocol.ts +2 -0
  156. package/src/exec/bash-executor.ts +3 -0
  157. package/src/extensibility/extensions/runner.ts +52 -4
  158. package/src/extensibility/extensions/types.ts +10 -0
  159. package/src/extensibility/gjc-plugins/registry.ts +196 -37
  160. package/src/extensibility/gjc-plugins/runtime-adapters.ts +46 -8
  161. package/src/extensibility/runtime-skill-discovery.ts +61 -3
  162. package/src/extensibility/shared-events.ts +3 -1
  163. package/src/gjc-runtime/session-state-lock.ts +165 -24
  164. package/src/gjc-runtime/state-writer.ts +317 -6
  165. package/src/gjc-runtime/ultragoal-succession.ts +31 -3
  166. package/src/harness-control-plane/control-endpoint.ts +24 -6
  167. package/src/harness-control-plane/finalize.ts +312 -25
  168. package/src/harness-control-plane/owner.ts +323 -89
  169. package/src/internal-urls/docs-index.generated.ts +23 -20
  170. package/src/internal-urls/embedded-protocol.ts +83 -0
  171. package/src/internal-urls/index.ts +1 -0
  172. package/src/internal-urls/parse.ts +21 -1
  173. package/src/internal-urls/router.ts +4 -2
  174. package/src/main.ts +192 -74
  175. package/src/modes/acp/acp-agent.ts +973 -56
  176. package/src/modes/acp/startup-provenance.ts +59 -0
  177. package/src/modes/components/custom-provider-wizard.ts +308 -7
  178. package/src/modes/components/gjc-bundle-settings.ts +21 -1
  179. package/src/modes/components/oauth-selector.ts +9 -0
  180. package/src/modes/components/session-selector.ts +47 -4
  181. package/src/modes/components/tool-status-header.ts +72 -6
  182. package/src/modes/components/user-message-selector.ts +57 -15
  183. package/src/modes/controllers/command-controller.ts +58 -32
  184. package/src/modes/controllers/extension-ui-controller.ts +33 -7
  185. package/src/modes/controllers/input-controller.ts +4 -0
  186. package/src/modes/controllers/runtime-mcp-command-controller.ts +1 -1
  187. package/src/modes/controllers/selector-controller.ts +161 -15
  188. package/src/modes/interactive-mode.ts +21 -7
  189. package/src/modes/runtime-init.ts +1 -0
  190. package/src/modes/shared/agent-wire/workflow-gate-broker.ts +124 -7
  191. package/src/modes/shared/agent-wire/workflow-gate-types.ts +16 -2
  192. package/src/modes/types.ts +2 -2
  193. package/src/prompts/agent-fragments/browser-aside.md +1 -0
  194. package/src/prompts/system/title-system.md +1 -1
  195. package/src/prompts/tools/bash.md +1 -1
  196. package/src/prompts/tools/job.md +1 -1
  197. package/src/prompts/tools/read.md +2 -0
  198. package/src/runtime/network-prewarm-service.test.ts +36 -1
  199. package/src/runtime/network-prewarm-service.ts +28 -3
  200. package/src/runtime/process-lifecycle.ts +22 -11
  201. package/src/runtime-mcp/config.ts +16 -1
  202. package/src/runtime-mcp/loader.ts +6 -3
  203. package/src/runtime-mcp/manager.ts +845 -109
  204. package/src/runtime-mcp/pool.test.ts +31 -0
  205. package/src/runtime-mcp/pool.ts +304 -28
  206. package/src/runtime-mcp/redaction.ts +42 -0
  207. package/src/runtime-mcp/startup-policy.ts +7 -0
  208. package/src/runtime-mcp/tool-bridge.ts +71 -0
  209. package/src/runtime-mcp/transports/http.ts +108 -25
  210. package/src/sdk/acp/adapter.ts +27 -8
  211. package/src/sdk/broker/broker.ts +1431 -198
  212. package/src/sdk/broker/ensure.ts +21 -3
  213. package/src/sdk/broker/identity.ts +22 -1
  214. package/src/sdk/broker/lifecycle-ledger.ts +95 -31
  215. package/src/sdk/broker/lifecycle.ts +690 -141
  216. package/src/sdk/broker/managed-task-dag.ts +1927 -0
  217. package/src/sdk/broker/managed-task-verification.ts +642 -0
  218. package/src/sdk/broker/runtime.ts +1 -1
  219. package/src/sdk/broker/session-index.ts +97 -4
  220. package/src/sdk/broker/spawn-authority.ts +175 -14
  221. package/src/sdk/broker/startup-failure.ts +80 -1
  222. package/src/sdk/broker/stderr-drainer.ts +59 -0
  223. package/src/sdk/broker/transport.ts +1 -0
  224. package/src/sdk/bus/chat-daemon-control.ts +2 -2
  225. package/src/sdk/bus/chat-daemon-runtime.ts +1 -0
  226. package/src/sdk/bus/index.ts +707 -81
  227. package/src/sdk/bus/telegram-daemon-cli.ts +31 -2
  228. package/src/sdk/bus/telegram-daemon-contract.ts +7 -1
  229. package/src/sdk/bus/telegram-daemon.ts +10 -3
  230. package/src/sdk/cli/master-cli.ts +47 -36
  231. package/src/sdk/cli/rows.ts +30 -7
  232. package/src/sdk/cli/session-cli.ts +705 -184
  233. package/src/sdk/client/client.ts +18 -0
  234. package/src/sdk/guides/catalog.ts +2 -2
  235. package/src/sdk/guides/cli.ts +20 -62
  236. package/src/sdk/guides/verify.ts +14 -0
  237. package/src/sdk/host/host.ts +49 -5
  238. package/src/sdk/host/session-runtime.test.ts +184 -2
  239. package/src/sdk/host/session-runtime.ts +322 -101
  240. package/src/sdk/host/types.ts +12 -1
  241. package/src/sdk/host/websocket-transport.lifecycle.test.ts +84 -0
  242. package/src/sdk/host/websocket-transport.ts +40 -6
  243. package/src/sdk/index.ts +11 -0
  244. package/src/sdk/lifecycle/service.ts +2 -0
  245. package/src/sdk/prompt-deadline-flush.ts +356 -0
  246. package/src/sdk/prompt-deadline-manager.ts +133 -1
  247. package/src/sdk/prompt-tool-boundary.ts +121 -0
  248. package/src/sdk/protocol/operation-inventory.generated.json +106 -0
  249. package/src/sdk/router/session-router.ts +9 -1
  250. package/src/sdk/session.ts +763 -116
  251. package/src/sdk/startup-capability.ts +1 -1
  252. package/src/sdk/transport/relay.ts +71 -6
  253. package/src/sdk/transport/serve-cli.ts +397 -42
  254. package/src/sdk/transport/socket.ts +12 -11
  255. package/src/sdk/transport/stdio.ts +7 -3
  256. package/src/session/agent-session.ts +2472 -407
  257. package/src/session/fallback-chain-controller.ts +6 -7
  258. package/src/session/fold-coordinator.ts +24 -5
  259. package/src/session/internal/managed-session-scope.ts +68 -18
  260. package/src/session/internal/managed-session-storage.ts +334 -12
  261. package/src/session/session-manager.ts +382 -44
  262. package/src/session/session-work-lease.ts +34 -0
  263. package/src/setup/model-onboarding-guidance.ts +1 -1
  264. package/src/setup/provider-onboarding.ts +397 -24
  265. package/src/setup/provider-presets.json +2 -1
  266. package/src/slash-commands/builtin-registry.ts +259 -15
  267. package/src/task/executor.ts +46 -17
  268. package/src/task/index.ts +14 -2
  269. package/src/task/types.ts +2 -0
  270. package/src/tools/bash-interactive.ts +2 -0
  271. package/src/tools/bash.ts +118 -78
  272. package/src/tools/fetch.ts +140 -25
  273. package/src/tools/job.ts +151 -25
  274. package/src/tools/output-meta.ts +15 -2
  275. package/src/tools/path-utils.ts +27 -3
  276. package/src/tools/read.ts +3 -3
  277. package/src/tools/steer-fold.ts +107 -0
  278. package/src/tools/subagent-render.ts +24 -1
  279. package/src/tools/subagent.ts +27 -1
  280. package/src/tools/tool-catalog.generated.ts +29 -2
  281. package/src/tools/tool-errors.ts +4 -0
  282. package/src/utils/git.ts +53 -3
  283. package/src/utils/markit.ts +55 -41
  284. package/src/utils/mupdf-assets.d.ts +4 -0
  285. package/src/utils/mupdf-embedded.ts +4 -0
  286. package/src/utils/mupdf.ts +136 -0
  287. package/src/utils/session-title-generation.ts +29 -0
  288. package/src/utils/title-generator.ts +46 -2
  289. package/src/web/scrapers/types.ts +24 -3
  290. package/vendor/markit-ai/LICENSE +21 -0
  291. package/vendor/markit-ai/MANIFEST.json +412 -0
  292. package/vendor/markit-ai/dist/commands/config.d.ts +4 -0
  293. package/vendor/markit-ai/dist/commands/config.js +139 -0
  294. package/vendor/markit-ai/dist/commands/convert.d.ts +6 -0
  295. package/vendor/markit-ai/dist/commands/convert.js +113 -0
  296. package/vendor/markit-ai/dist/commands/formats.d.ts +2 -0
  297. package/vendor/markit-ai/dist/commands/formats.js +92 -0
  298. package/vendor/markit-ai/dist/commands/init.d.ts +2 -0
  299. package/vendor/markit-ai/dist/commands/init.js +29 -0
  300. package/vendor/markit-ai/dist/commands/onboard.d.ts +2 -0
  301. package/vendor/markit-ai/dist/commands/onboard.js +61 -0
  302. package/vendor/markit-ai/dist/commands/plugin.d.ts +4 -0
  303. package/vendor/markit-ai/dist/commands/plugin.js +58 -0
  304. package/vendor/markit-ai/dist/config.d.ts +26 -0
  305. package/vendor/markit-ai/dist/config.js +42 -0
  306. package/vendor/markit-ai/dist/converters/audio.d.ts +7 -0
  307. package/vendor/markit-ai/dist/converters/audio.js +104 -0
  308. package/vendor/markit-ai/dist/converters/csv.d.ts +7 -0
  309. package/vendor/markit-ai/dist/converters/csv.js +83 -0
  310. package/vendor/markit-ai/dist/converters/docx.d.ts +6 -0
  311. package/vendor/markit-ai/dist/converters/docx.js +57 -0
  312. package/vendor/markit-ai/dist/converters/epub.d.ts +8 -0
  313. package/vendor/markit-ai/dist/converters/epub.js +128 -0
  314. package/vendor/markit-ai/dist/converters/github.d.ts +18 -0
  315. package/vendor/markit-ai/dist/converters/github.js +148 -0
  316. package/vendor/markit-ai/dist/converters/html.d.ts +6 -0
  317. package/vendor/markit-ai/dist/converters/html.js +30 -0
  318. package/vendor/markit-ai/dist/converters/image.d.ts +6 -0
  319. package/vendor/markit-ai/dist/converters/image.js +124 -0
  320. package/vendor/markit-ai/dist/converters/ipynb.d.ts +6 -0
  321. package/vendor/markit-ai/dist/converters/ipynb.js +75 -0
  322. package/vendor/markit-ai/dist/converters/iwork.d.ts +20 -0
  323. package/vendor/markit-ai/dist/converters/iwork.js +391 -0
  324. package/vendor/markit-ai/dist/converters/json.d.ts +6 -0
  325. package/vendor/markit-ai/dist/converters/json.js +21 -0
  326. package/vendor/markit-ai/dist/converters/pdf/columns.d.ts +35 -0
  327. package/vendor/markit-ai/dist/converters/pdf/columns.js +93 -0
  328. package/vendor/markit-ai/dist/converters/pdf/extract.d.ts +19 -0
  329. package/vendor/markit-ai/dist/converters/pdf/extract.js +514 -0
  330. package/vendor/markit-ai/dist/converters/pdf/grid.d.ts +25 -0
  331. package/vendor/markit-ai/dist/converters/pdf/grid.js +795 -0
  332. package/vendor/markit-ai/dist/converters/pdf/headers.d.ts +24 -0
  333. package/vendor/markit-ai/dist/converters/pdf/headers.js +108 -0
  334. package/vendor/markit-ai/dist/converters/pdf/index.d.ts +19 -0
  335. package/vendor/markit-ai/dist/converters/pdf/index.js +136 -0
  336. package/vendor/markit-ai/dist/converters/pdf/mupdf-loader.js +9 -0
  337. package/vendor/markit-ai/dist/converters/pdf/render.d.ts +24 -0
  338. package/vendor/markit-ai/dist/converters/pdf/render.js +513 -0
  339. package/vendor/markit-ai/dist/converters/pdf/types.d.ts +75 -0
  340. package/vendor/markit-ai/dist/converters/pdf/types.js +1 -0
  341. package/vendor/markit-ai/dist/converters/plain-text.d.ts +6 -0
  342. package/vendor/markit-ai/dist/converters/plain-text.js +87 -0
  343. package/vendor/markit-ai/dist/converters/pptx.d.ts +8 -0
  344. package/vendor/markit-ai/dist/converters/pptx.js +289 -0
  345. package/vendor/markit-ai/dist/converters/rss.d.ts +11 -0
  346. package/vendor/markit-ai/dist/converters/rss.js +138 -0
  347. package/vendor/markit-ai/dist/converters/wikipedia.d.ts +6 -0
  348. package/vendor/markit-ai/dist/converters/wikipedia.js +33 -0
  349. package/vendor/markit-ai/dist/converters/xlsx.d.ts +8 -0
  350. package/vendor/markit-ai/dist/converters/xlsx.js +147 -0
  351. package/vendor/markit-ai/dist/converters/xml.d.ts +6 -0
  352. package/vendor/markit-ai/dist/converters/xml.js +18 -0
  353. package/vendor/markit-ai/dist/converters/yaml.d.ts +6 -0
  354. package/vendor/markit-ai/dist/converters/yaml.js +17 -0
  355. package/vendor/markit-ai/dist/converters/zip.d.ts +8 -0
  356. package/vendor/markit-ai/dist/converters/zip.js +57 -0
  357. package/vendor/markit-ai/dist/index.d.ts +30 -0
  358. package/vendor/markit-ai/dist/index.js +26 -0
  359. package/vendor/markit-ai/dist/main.d.ts +2 -0
  360. package/vendor/markit-ai/dist/main.js +191 -0
  361. package/vendor/markit-ai/dist/markit.d.ts +45 -0
  362. package/vendor/markit-ai/dist/markit.js +267 -0
  363. package/vendor/markit-ai/dist/plugins/api.d.ts +7 -0
  364. package/vendor/markit-ai/dist/plugins/api.js +42 -0
  365. package/vendor/markit-ai/dist/plugins/index.d.ts +4 -0
  366. package/vendor/markit-ai/dist/plugins/index.js +3 -0
  367. package/vendor/markit-ai/dist/plugins/installer.d.ts +25 -0
  368. package/vendor/markit-ai/dist/plugins/installer.js +176 -0
  369. package/vendor/markit-ai/dist/plugins/loader.d.ts +6 -0
  370. package/vendor/markit-ai/dist/plugins/loader.js +61 -0
  371. package/vendor/markit-ai/dist/plugins/types.d.ts +25 -0
  372. package/vendor/markit-ai/dist/plugins/types.js +1 -0
  373. package/vendor/markit-ai/dist/providers/anthropic.d.ts +2 -0
  374. package/vendor/markit-ai/dist/providers/anthropic.js +47 -0
  375. package/vendor/markit-ai/dist/providers/index.d.ts +21 -0
  376. package/vendor/markit-ai/dist/providers/index.js +56 -0
  377. package/vendor/markit-ai/dist/providers/openai.d.ts +2 -0
  378. package/vendor/markit-ai/dist/providers/openai.js +76 -0
  379. package/vendor/markit-ai/dist/providers/types.d.ts +26 -0
  380. package/vendor/markit-ai/dist/providers/types.js +1 -0
  381. package/vendor/markit-ai/dist/types.d.ts +36 -0
  382. package/vendor/markit-ai/dist/types.js +1 -0
  383. package/vendor/markit-ai/dist/utils/exit-codes.d.ts +4 -0
  384. package/vendor/markit-ai/dist/utils/exit-codes.js +4 -0
  385. package/vendor/markit-ai/dist/utils/output.d.ts +22 -0
  386. package/vendor/markit-ai/dist/utils/output.js +31 -0
  387. package/vendor/markit-ai/dist/utils/turndown.d.ts +8 -0
  388. package/vendor/markit-ai/dist/utils/turndown.js +70 -0
  389. package/vendor/markit-ai/package.json +74 -0
  390. package/vendor/markit-ai.patch +49 -0
@@ -67,6 +67,8 @@ export interface SdkClientOptions {
67
67
  timeoutMs?: number;
68
68
  /** Absolute wall-clock deadline shared by connect, hello, retry, and request work. */
69
69
  deadline?: number;
70
+ /** Optional client capabilities sent in the protocol hello handshake. */
71
+ capabilities?: readonly string[];
70
72
 
71
73
  reconnectAttempts?: number;
72
74
  reconnectBackoffMs?: number;
@@ -288,6 +290,7 @@ export class SdkClient {
288
290
  */
289
291
  readonly #closeGraceMs: number;
290
292
  readonly #deadline?: number;
293
+ readonly #capabilities?: readonly string[];
291
294
  #currentSocketRecord: Incarnation | null = null;
292
295
  #opening: Cycle | null = null;
293
296
  #cycleGeneration = 0;
@@ -309,6 +312,7 @@ export class SdkClient {
309
312
  this.#closeGraceMs = Math.max(1, Math.min(this.#timeoutMs, 1_000));
310
313
  this.#deadline =
311
314
  typeof options.deadline === "number" && Number.isFinite(options.deadline) ? options.deadline : undefined;
315
+ this.#capabilities = options.capabilities;
312
316
 
313
317
  this.#reconnectAttempts = options.reconnectAttempts ?? 3;
314
318
  this.#reconnectBackoffMs = options.reconnectBackoffMs ?? 25;
@@ -831,6 +835,20 @@ export class SdkClient {
831
835
  incarnation.resolveOpen = undefined;
832
836
  incarnation.rejectOpen = undefined;
833
837
  this.#beginHello(incarnation);
838
+ if (this.#capabilities !== undefined) {
839
+ try {
840
+ socket.send(
841
+ JSON.stringify({
842
+ type: "hello",
843
+ protocolVersion: 3,
844
+ capabilities: [...this.#capabilities],
845
+ }),
846
+ );
847
+ } catch (error) {
848
+ this.#onSocketFailure(incarnation, error as Event);
849
+ return;
850
+ }
851
+ }
834
852
  const earlyHello = incarnation.earlyHello;
835
853
  if (earlyHello) {
836
854
  incarnation.earlyHello = undefined;
@@ -241,13 +241,13 @@ export async function fetchGuideManifestOnline(params: {
241
241
  */
242
242
  const BUNDLED_GUIDE_MANIFEST_ID = "gajae-code-advisory-bundled";
243
243
  const BUNDLED_GUIDE_SIGNATURE_HEX =
244
- "682028a6834dfd2b39db57fb6cc185b9e2bdf21a3256d907fbbb8ddb20a0ce285f3a21b970df7d4055acfb2aede13d8f69931249c6d27fc092bfa2b60e936303";
244
+ "62715b7716b30d3ea7b6ef41dfd7848c82dffed972a8729e4a5b8e3a9ee05d8cbcefeabc27b8796b1754622672a8fd2698a7ab638ca205c202f7479c34763c00";
245
245
 
246
246
  const bundledGuideAdvisoryTexts: Readonly<Record<string, string>> = {
247
247
  "getting-started":
248
248
  "GJC ships a small set of signed advisory guides with the client so `gjc sdk guides list` and `gjc sdk guides show <guideId>` work offline on a fresh install. Guides are advisory text only: they are rendered for reading and never executed or applied as configuration. To receive newer guides, run `gjc sdk guides refresh --url <https manifest url>`. The manifest must come from the allowlisted HTTPS host and must be signed by a pinned Ed25519 key; a rejected refresh falls back to the last verified cache, then to the bundled seed, and any rejection is reported in the warnings list.",
249
249
  "sdk/session-cli":
250
- "`gjc sdk session` is the broker-bound command family for operating live GJC SDK sessions from the terminal: `list` enumerates managed sessions, `inspect` shows session details, `send` submits a prompt turn, `status` reports session health, and `tail` follows the event stream. The explicit `raw` hatch dispatches one SDK operation as `control`, `query`, or `global`. Authority resolves through the local broker and endpoint credentials are never rendered by the CLI.",
250
+ "`gjc sdk session` is the broker-bound command family for operating live GJC SDK sessions from the terminal: `list` enumerates managed sessions, `inspect` shows session details, `send` submits a prompt turn, `status` reports session health, `tail` follows the event stream, and `close` ends the current live host generation. The explicit `raw` hatch dispatches one SDK operation as `control`, `query`, or `global`. Authority resolves through the local broker and endpoint credentials are never rendered by the CLI.",
251
251
  "troubleshooting/sdk-connection":
252
252
  "When an SDK connection fails, first check that the session host is alive and healthy: `gjc sdk session status` reports readiness and liveness. If the broker is gone, restart it and re-list sessions; detached hosts are reaped after a bounded absence grace. Fetch-boundary failures (offline host, allowlisted URL violations, signature rejections) are reported as typed errors with exit code 1 so scripts can fail closed instead of silently serving unverified content.",
253
253
  };
@@ -1,4 +1,5 @@
1
1
  import { getAgentDir } from "@gajae-code/utils";
2
+ import { normalizePublicCommandFailure, PublicCommandFailure } from "../../cli/public-command-errors";
2
3
  import { readGuideCache } from "./cache";
3
4
  import { BUNDLED_GUIDE_MANIFESTS, GuideCatalog, guideFetchPolicy, isGuideFetchUrlAllowed } from "./catalog";
4
5
  import { GUIDE_CLIENT_VERSION, GUIDE_PINNED_KEYS } from "./verify";
@@ -39,50 +40,20 @@ export interface SdkGuidesCliArgs {
39
40
 
40
41
  const GUIDE_CLI_ACTIONS: readonly string[] = ["refresh", "list", "show", "status", "trust"];
41
42
 
42
- class SdkGuidesCliError extends Error {
43
- constructor(
44
- readonly code: string,
45
- message: string,
46
- readonly exitCode: 1 | 2,
47
- ) {
48
- super(message);
49
- }
50
- }
51
-
52
- function writeJson(value: unknown): void {
53
- process.stdout.write(`${JSON.stringify(value)}\n`);
54
- }
55
-
56
- function usageFailure(message: string): SdkGuidesCliError {
57
- return new SdkGuidesCliError("usage", message, 2);
43
+ function usageFailure(): PublicCommandFailure {
44
+ return new PublicCommandFailure({ kind: "usage", proof: "pre-effect" });
58
45
  }
59
46
 
60
- function operationalFailure(code: string, message: string): SdkGuidesCliError {
61
- return new SdkGuidesCliError(code, message, 1);
47
+ function operationalFailure(): PublicCommandFailure {
48
+ return new PublicCommandFailure({ kind: "operation_failed", proof: "unknown" });
62
49
  }
63
50
 
64
- /**
65
- * Usage-shaped errors (bad verb, missing argument, URL outside the allowlist)
66
- * exit 2; every verification/operational failure exits 1. Fail-closed codes
67
- * from the verification pipeline map to exit 1 so scripts observe a hard
68
- * failure on tamper, unknown key, rollback, expiry, or corrupt cache.
69
- */
70
- function toCliError(error: unknown): SdkGuidesCliError {
71
- if (error instanceof SdkGuidesCliError) return error;
72
- const code = error instanceof Error && "code" in error ? String((error as { code: unknown }).code) : undefined;
73
- const message = error instanceof Error ? error.message : String(error);
74
- switch (code) {
75
- case "usage":
76
- case "invalid_input":
77
- case "fetch_forbidden":
78
- return new SdkGuidesCliError(code ?? "operation_failed", message, 2);
79
- default:
80
- return operationalFailure(code ?? "operation_failed", message);
81
- }
51
+ function writeJson(value: unknown): void {
52
+ process.stdout.write(`${JSON.stringify(value)}\n`);
82
53
  }
83
54
 
84
- function requireValue(value: string | undefined, flag: string): string {
85
- if (value === undefined || value.length === 0) throw usageFailure(`${flag} is required.`);
55
+ function requireValue(value: string | undefined): string {
56
+ if (value === undefined || value.length === 0) throw usageFailure();
86
57
  return value;
87
58
  }
88
59
 
@@ -105,8 +76,8 @@ function manifestSummary(manifest: {
105
76
  }
106
77
 
107
78
  async function runRefresh(agentDir: string, args: SdkGuidesCliArgs): Promise<unknown> {
108
- const url = requireValue(args.url, "--url");
109
- if (!isGuideFetchUrlAllowed(url)) throw usageFailure(`--url ${url} is outside the HTTPS allowlist.`);
79
+ const url = requireValue(args.url);
80
+ if (!isGuideFetchUrlAllowed(url)) throw usageFailure();
110
81
  const catalog = new GuideCatalog({
111
82
  agentDir,
112
83
  onlineUrl: url,
@@ -114,15 +85,9 @@ async function runRefresh(agentDir: string, args: SdkGuidesCliArgs): Promise<unk
114
85
  fetchImpl: args.fetchImpl,
115
86
  });
116
87
  const result = await catalog.refresh();
117
- if (!result.ok) throw operationalFailure(result.error.code, result.error.message);
88
+ if (!result.ok) throw operationalFailure();
118
89
  const selection = result.value;
119
- if (selection.source !== "online") {
120
- const reasons = selection.warnings.length > 0 ? ` ${selection.warnings.join("; ")}` : "";
121
- throw operationalFailure(
122
- "online_refresh_failed",
123
- `Online refresh did not select an online source (selected ${selection.source} instead)${reasons}.`,
124
- );
125
- }
90
+ if (selection.source !== "online") throw operationalFailure();
126
91
  return {
127
92
  source: selection.source,
128
93
  manifest: manifestSummary(selection.manifest),
@@ -134,7 +99,7 @@ async function runRefresh(agentDir: string, args: SdkGuidesCliArgs): Promise<unk
134
99
  async function runList(agentDir: string): Promise<unknown> {
135
100
  const catalog = new GuideCatalog({ agentDir });
136
101
  const result = await catalog.load();
137
- if (!result.ok) throw operationalFailure(result.error.code, result.error.message);
102
+ if (!result.ok) throw operationalFailure();
138
103
  const selection = result.value;
139
104
  return {
140
105
  source: selection.source,
@@ -145,10 +110,10 @@ async function runList(agentDir: string): Promise<unknown> {
145
110
  }
146
111
 
147
112
  async function runShow(agentDir: string, args: SdkGuidesCliArgs): Promise<unknown> {
148
- const guideId = requireValue(args.guideId, "<guideId>");
113
+ const guideId = requireValue(args.guideId);
149
114
  const catalog = new GuideCatalog({ agentDir });
150
115
  const result = await catalog.advisory(guideId);
151
- if (!result.ok) throw operationalFailure(result.error.code, result.error.message);
116
+ if (!result.ok) throw operationalFailure();
152
117
  return {
153
118
  source: result.value.source,
154
119
  guideId: result.value.guideId,
@@ -194,21 +159,16 @@ function runTrust(): unknown {
194
159
 
195
160
  /**
196
161
  * Runs the `gjc sdk guides` command family. Exported for command routing from
197
- * `src/commands/sdk.ts` and for direct service use; the injected `write` /
198
- * `setExitCode` hooks keep the surface testable without touching stdout or
199
- * the process exit code.
162
+ * `src/commands/sdk.ts` and for direct service use. Only successful results are
163
+ * written here; typed failures belong to the outer public family writer.
200
164
  */
201
165
  export async function runSdkGuidesCli(
202
166
  args: SdkGuidesCliArgs,
203
167
  writeOutput: (value: unknown) => void = writeJson,
204
- setExitCode: (exitCode: 1 | 2) => void = exitCode => {
205
- process.exitCode = exitCode;
206
- },
207
168
  ): Promise<void> {
208
169
  try {
209
170
  const action = args.action;
210
- if (action === undefined || !GUIDE_CLI_ACTIONS.includes(action))
211
- throw usageFailure(`Expected one of: ${GUIDE_CLI_ACTIONS.join(", ")}.`);
171
+ if (action === undefined || !GUIDE_CLI_ACTIONS.includes(action)) throw usageFailure();
212
172
  const agentDir = args.agentDir ?? getAgentDir();
213
173
  switch (action) {
214
174
  case "refresh":
@@ -228,8 +188,6 @@ export async function runSdkGuidesCli(
228
188
  return;
229
189
  }
230
190
  } catch (error) {
231
- const cliError = toCliError(error);
232
- writeOutput({ ok: false, error: { code: cliError.code, message: cliError.message } });
233
- setExitCode(cliError.exitCode);
191
+ throw normalizePublicCommandFailure(error);
234
192
  }
235
193
  }
@@ -21,6 +21,8 @@ export interface GuidePinnedKey {
21
21
  /** DER-encoded SPKI public key, hex. */
22
22
  spkiDerHex: string;
23
23
  source: "bundled";
24
+ /** Optional retirement bound for old bundled signers. */
25
+ validUntil?: number;
24
26
  }
25
27
 
26
28
  /**
@@ -30,10 +32,20 @@ export interface GuidePinnedKey {
30
32
  * Private key material is never shipped; manifests are signed out-of-band.
31
33
  */
32
34
  const guidePinnedKeys: GuidePinnedKey[] = [
35
+ // Bundled advisory seed signer for the current offline catalog. The previous
36
+ // key remains trusted below until the current seed's 2036 expiry so already-
37
+ // cached manifests do not lose authority during a bundled-guide refresh.
38
+ {
39
+ keyId: "71efa9f212e39350e34f88aed5ab7242854e7e20ea91455e8861c973a6fc2eaf",
40
+ spkiDerHex: "302a300506032b657003210062c441fc5953cdc67d0c46f09b814dcb30de1465dcafb385092dbdcf9844cfeb",
41
+ source: "bundled",
42
+ validUntil: Date.UTC(2036, 0, 1),
43
+ },
33
44
  {
34
45
  keyId: "6c4b134ff9fb86a52d55cb6bb7c2fab938405b53b4148afc4249a2cb6f504bce",
35
46
  spkiDerHex: "302a300506032b6570032100ef665d05c6795341dfc893866d8fe5be4b48891c0ed0d125940d7032de37723e",
36
47
  source: "bundled",
48
+ validUntil: Date.UTC(2036, 0, 1),
37
49
  },
38
50
  ];
39
51
  export const GUIDE_PINNED_KEYS: readonly GuidePinnedKey[] = Object.freeze(
@@ -125,6 +137,8 @@ export function verifyGuideManifest(params: {
125
137
  }
126
138
  if (!valid)
127
139
  return guideFailure("invalid_signature", "Detached signature does not match the canonical manifest bytes.");
140
+ if (pinned.validUntil !== undefined && now > pinned.validUntil)
141
+ return guideFailure("expired", `Guide signer ${pinned.keyId} expired before this manifest was read.`);
128
142
  if (!Number.isSafeInteger(now) || now < manifest.issuedAt)
129
143
  return guideFailure("not_yet_valid", "Guide manifest is not authoritative until its issuedAt instant.");
130
144
  if (now > manifest.expiresAt)
@@ -103,9 +103,18 @@ export interface SessionSdkHostOptions extends HostEndpointAdapters {
103
103
  * must see the same capability-gated event kinds on both legs, or live and
104
104
  * replay delivery diverge for the same subscriber. */
105
105
  export const TOOL_ACTIVITY_CAPABILITY = "tool_activity_v2";
106
+ /** Capability for non-replayable streamed turn content. */
107
+ export const TURN_STREAM_CAPABILITY = "turn_stream";
108
+ /** Capability used by notification adapters that observe a host without owning its work. */
109
+ export const SESSION_HOST_OBSERVER_CAPABILITY = "session_host_observer_v1";
106
110
  export const CAP_GATED_FRAME_KINDS: ReadonlySet<string> = new Set(["tool_activity", "reasoning_summary"]);
107
111
  const EMPTY_CAPABILITIES: ReadonlySet<string> = new Set();
108
112
 
113
+ /** Keep live delivery and replay on one capability gate. */
114
+ export function canDeliverSdkEvent(kind: string, capabilities: ReadonlySet<string> | undefined): boolean {
115
+ return !CAP_GATED_FRAME_KINDS.has(kind) || capabilities?.has(TOOL_ACTIVITY_CAPABILITY) === true;
116
+ }
117
+
109
118
  /** Safe, identifier-free explanations for every refused activation status. */
110
119
  const ACTIVATION_MESSAGES: Record<
111
120
  Exclude<SessionActivationOutcome, "activated" | "already"> | "session_mismatch",
@@ -210,6 +219,9 @@ export class SessionSdkHost {
210
219
  #stopPromise?: Promise<"stopped">;
211
220
  #unsubscribe?: () => void;
212
221
  #registration?: { writer: BrokerIndexWriter; generation: number };
222
+ #reportedActivityState?: "active" | "idle";
223
+ /** Serializes durable activity transitions so a later state cannot be lost. */
224
+ #activityReportTail: Promise<void> = Promise.resolve();
213
225
  /** The generation whose readiness signal has already been published. */
214
226
  #readyGeneration?: number;
215
227
  /** Serializes activation attempts so a concurrent pair cannot both publish. */
@@ -276,6 +288,7 @@ export class SessionSdkHost {
276
288
 
277
289
  async start(): Promise<"started" | "already"> {
278
290
  if (this.#started) return "already";
291
+ this.#reportedActivityState = undefined;
279
292
  this.events.restart();
280
293
  if (isAutoroutingInactive(this)) this.emitAutoroutingInactiveNotice();
281
294
  if (this.#options.readiness !== "deferred") this.#publishReadiness();
@@ -298,6 +311,9 @@ export class SessionSdkHost {
298
311
  stateRoot: this.#options.stateRoot,
299
312
  endpointGeneration: this.events.generation,
300
313
  });
314
+ void this.reportActivity("idle").catch(error =>
315
+ logger.warn(`sdk: initial idle activity checkpoint failed: ${String(error)}`),
316
+ );
301
317
  return "started";
302
318
  } catch (error) {
303
319
  this.#unsubscribe?.();
@@ -364,7 +380,9 @@ export class SessionSdkHost {
364
380
  return "activated";
365
381
  }
366
382
 
367
- async stop(options: { allowLockContention?: boolean } = {}): Promise<"stopped" | "already"> {
383
+ async stop(
384
+ options: { allowLockContention?: boolean; unregisterReason?: "detached_idle" } = {},
385
+ ): Promise<"stopped" | "already"> {
368
386
  if (this.#stopPromise) return this.#stopPromise;
369
387
  if (!this.#started) return "already";
370
388
  const stopPromise = this.#stopStartedHost(options);
@@ -376,7 +394,10 @@ export class SessionSdkHost {
376
394
  }
377
395
  }
378
396
 
379
- async #stopStartedHost(options: { allowLockContention?: boolean }): Promise<"stopped"> {
397
+ async #stopStartedHost(options: {
398
+ allowLockContention?: boolean;
399
+ unregisterReason?: "detached_idle";
400
+ }): Promise<"stopped"> {
380
401
  // Fence before the first await: everything after this point is teardown,
381
402
  // and no in-flight activation may publish readiness across it.
382
403
  this.#stopping = true;
@@ -388,6 +409,7 @@ export class SessionSdkHost {
388
409
  sessionId: this.#options.sessionId,
389
410
  stateRoot: this.#options.stateRoot,
390
411
  endpointGeneration: this.events.generation,
412
+ ...(options.unregisterReason === undefined ? {} : { reason: options.unregisterReason }),
391
413
  });
392
414
  } catch (error) {
393
415
  // A live broker may hold the shared session-index lock beyond the
@@ -415,6 +437,30 @@ export class SessionSdkHost {
415
437
  stateRoot: this.#options.stateRoot,
416
438
  endpointGeneration: this.events.generation,
417
439
  });
440
+ if (this.#started)
441
+ void this.reportActivity("idle").catch(error =>
442
+ logger.warn(`sdk: initial idle activity checkpoint failed: ${String(error)}`),
443
+ );
444
+ }
445
+
446
+ /** Persist the host's observable activity for broker/session-list consumers. */
447
+ async reportActivity(state: "active" | "idle", at = Date.now()): Promise<void> {
448
+ const registration = this.#registration;
449
+ if (!this.#started || this.#stopping || !registration?.writer.heartbeat) return;
450
+ if (!Number.isFinite(at)) at = Date.now();
451
+ const write = async (): Promise<void> => {
452
+ if (this.#reportedActivityState === state) return;
453
+ await registration.writer.heartbeat!({
454
+ sessionId: this.#options.sessionId,
455
+ stateRoot: this.#options.stateRoot,
456
+ endpointGeneration: this.events.generation,
457
+ activity: { state, at },
458
+ });
459
+ this.#reportedActivityState = state;
460
+ };
461
+ const report = this.#activityReportTail.then(write, write);
462
+ this.#activityReportTail = report.catch(() => undefined);
463
+ await report;
418
464
  }
419
465
 
420
466
  async #send(connectionId: string, frame: SdkFrame): Promise<"written" | "dropped"> {
@@ -563,9 +609,7 @@ export class SessionSdkHost {
563
609
  const sinceSeq = rawSeq;
564
610
  const replay = this.events.replay(sinceSeq, sinceGeneration);
565
611
  const capabilities = this.#options.connectionCapabilities?.(connectionId) ?? EMPTY_CAPABILITIES;
566
- const events = replay.events.filter(
567
- event => !CAP_GATED_FRAME_KINDS.has(String(event.kind)) || capabilities.has(TOOL_ACTIVITY_CAPABILITY),
568
- );
612
+ const events = replay.events.filter(event => canDeliverSdkEvent(String(event.kind), capabilities));
569
613
  await this.#send(connectionId, {
570
614
  type: "event_replay_result",
571
615
  id,
@@ -19,6 +19,7 @@ import { createKindAwareReconciliation } from "../bus/kind-aware-reconciliation"
19
19
  import { createPromptReconciliation } from "../bus/prompt-reconciliation";
20
20
  import { createReconciliationStore } from "../bus/reconciliation-store";
21
21
  import { BROKER_RUNTIME_ABORT_CAPABILITY_FIELD, setBrokerRuntimeAbortCapabilityForTest } from "./control/runtime-gate";
22
+ import { SESSION_HOST_OBSERVER_CAPABILITY, TURN_STREAM_CAPABILITY } from "./host";
22
23
  import { CursorRegistry, QueryHandlers, type QueryResponse, RevisionStore } from "./query";
23
24
  import {
24
25
  createInvocationReconciliation,
@@ -26,6 +27,7 @@ import {
26
27
  createSdkSurfaceFactory,
27
28
  type SdkOnlyInvocationRecord,
28
29
  type SdkOnlyReconciliationStore,
30
+ type SdkOnlyTerminalAbortSeams,
29
31
  SessionSdkSessionRuntime,
30
32
  type SessionSdkTransport,
31
33
  } from "./session-runtime";
@@ -1260,6 +1262,72 @@ describe("SessionSdkSessionRuntime", () => {
1260
1262
  );
1261
1263
  await runtime.stop();
1262
1264
  });
1265
+
1266
+ test("uses a custom capability provider for live delivery without transport negotiation hooks", async () => {
1267
+ const transport = memoryTransport();
1268
+ const runtime = new SessionSdkSessionRuntime({
1269
+ transport,
1270
+ connectionCapabilities: connectionId =>
1271
+ connectionId === "observer"
1272
+ ? new Set([TURN_STREAM_CAPABILITY, SESSION_HOST_OBSERVER_CAPABILITY])
1273
+ : new Set(),
1274
+ });
1275
+ await runtime.start();
1276
+ transport.feed("observer", { type: "event_replay", id: "replay", sinceSeq: 0 });
1277
+ runtime.sendFrameToCapabilities([TURN_STREAM_CAPABILITY, SESSION_HOST_OBSERVER_CAPABILITY], {
1278
+ type: "event",
1279
+ kind: "message_update",
1280
+ payload: {},
1281
+ });
1282
+ expect(transport.sent).toContainEqual({ type: "event", kind: "message_update", payload: {} });
1283
+ await runtime.stop();
1284
+ });
1285
+ test("ignores late negotiated capabilities after the authoritative connection closes", async () => {
1286
+ const transport = memoryTransport();
1287
+ let closeConnection: ((connectionId: string) => void) | undefined;
1288
+ let negotiateCapabilities: ((connectionId: string, capabilities: readonly string[]) => void) | undefined;
1289
+ const hookedTransport: SessionSdkTransport = {
1290
+ ...transport,
1291
+ onConnectionClose(handler) {
1292
+ closeConnection = handler;
1293
+ return () => {
1294
+ if (closeConnection === handler) closeConnection = undefined;
1295
+ };
1296
+ },
1297
+ onNegotiatedCapabilities(handler) {
1298
+ negotiateCapabilities = handler;
1299
+ return () => {
1300
+ if (negotiateCapabilities === handler) negotiateCapabilities = undefined;
1301
+ };
1302
+ },
1303
+ };
1304
+ const liveCapabilities = new Map<string, ReadonlySet<string>>();
1305
+ const providerReads: string[] = [];
1306
+ const runtime = new SessionSdkSessionRuntime({
1307
+ transport: hookedTransport,
1308
+ connectionCapabilities: connectionId => {
1309
+ providerReads.push(connectionId);
1310
+ return liveCapabilities.get(connectionId);
1311
+ },
1312
+ });
1313
+ await runtime.start();
1314
+ try {
1315
+ if (!closeConnection || !negotiateCapabilities) throw new Error("transport callbacks were not registered");
1316
+ const capabilities = [TURN_STREAM_CAPABILITY, SESSION_HOST_OBSERVER_CAPABILITY];
1317
+ liveCapabilities.set("observer", new Set(capabilities));
1318
+ negotiateCapabilities("observer", capabilities);
1319
+ expect(runtime.connectionIdsWithCapabilities(capabilities)).toEqual(["observer"]);
1320
+
1321
+ liveCapabilities.delete("observer");
1322
+ closeConnection("observer");
1323
+ negotiateCapabilities("observer", capabilities);
1324
+ providerReads.length = 0;
1325
+ expect(runtime.connectionIdsWithCapabilities(capabilities)).toEqual([]);
1326
+ expect(providerReads).toEqual([]);
1327
+ } finally {
1328
+ await runtime.stop();
1329
+ }
1330
+ });
1263
1331
  test("SDK-only host publishes one replayable bash_folded frame per fold and drops the subscription on shutdown", async () => {
1264
1332
  const cwd = await mkdtemp(path.join(os.tmpdir(), "gjc-sdk-bash-folded-"));
1265
1333
  const handlers = new Map<string, (event: unknown, ctx: ExtensionContext) => Promise<void> | void>();
@@ -3784,6 +3852,8 @@ async function invocationHarness(
3784
3852
  agentFailedWriteFailures?: number;
3785
3853
  branch?: unknown[];
3786
3854
  onInvocationCompletionReconciled?: (kind: string, correlation: { commandId: string; turnId: string }) => void;
3855
+ /** Override/extend the INTERNAL terminal-abort seams the runtime is threaded. */
3856
+ terminalAbortSeams?: Partial<SdkOnlyTerminalAbortSeams>;
3787
3857
  },
3788
3858
  ): Promise<InvocationHarness> {
3789
3859
  const waiters = new Map<string, (frame: ResponseFrame) => void>();
@@ -3812,14 +3882,15 @@ async function invocationHarness(
3812
3882
  createSdkSessionRuntimeExtension(api, {
3813
3883
  agentDir: cwd,
3814
3884
  ...(hooks.onLifecycleDrainTimeout ? { onLifecycleDrainTimeoutForTests: hooks.onLifecycleDrainTimeout } : {}),
3815
- ...(interceptorStore
3885
+ ...(interceptorStore || hooks.terminalAbortSeams
3816
3886
  ? {
3817
3887
  terminalAbortSeams: {
3818
- getReconciliationStore: () => interceptorStore,
3819
3888
  getTerminalTurnEpoch: () => undefined,
3820
3889
  getActivePromptHandle: () => undefined,
3821
3890
  cancelPendingPreflightForTerminalAbort: () => {},
3822
3891
  abortPromptAndWaitWithTerminal: async () => ({ status: "settled", terminalScope: {} }),
3892
+ ...(interceptorStore ? { getReconciliationStore: () => interceptorStore } : {}),
3893
+ ...hooks.terminalAbortSeams,
3823
3894
  },
3824
3895
  }
3825
3896
  : {}),
@@ -5800,6 +5871,113 @@ describe("accepted-control zero-execution bound (#4668)", () => {
5800
5871
  while (Date.now() < budgetEndsAt && !ready(correlatedFrames(harness, correlation))) await Bun.sleep(5);
5801
5872
  };
5802
5873
 
5874
+ test("an SDK-only prompt deadline publishes its terminal frames without aborting the run", async () => {
5875
+ // #5637 review finding 1. The bus route's deadline fences the run through
5876
+ // `abortPromptAndWaitWithTerminal`, which aborts the cancellation domain
5877
+ // BEFORE it waits for settlement — that is the mid-tool kill the boundary
5878
+ // wait exists to prevent, and it is why the bus consumes the ledger seam.
5879
+ //
5880
+ // This route is architecturally different: its `PromptDeadlineManager.onExpired`
5881
+ // publishes `agent_failed` + `agent_end` and cleans up lifecycle references,
5882
+ // and aborts NOTHING. That is precisely why no boundary wait is required
5883
+ // here, and it is the fact this case pins. If someone later wires an abort
5884
+ // into this path, the `abortPromptAndWaitWithTerminal` assertion below fails
5885
+ // loudly — and whoever does it must add a boundary wait, for which the
5886
+ // `pendingToolExecutions` seam is now threaded (both hosts expose one
5887
+ // contract; only the bus consumes it today).
5888
+ const cwd = await mkdtemp(path.join(os.tmpdir(), "gjc-sdk-only-deadline-no-abort-"));
5889
+ const abortCalls: Array<{ handle: string; graceMs: number }> = [];
5890
+ const pendingLedgerReads: string[] = [];
5891
+ try {
5892
+ const harness = await invocationHarness("sdk-only-deadline-no-abort", cwd, {
5893
+ settings: zeroProgressSettings,
5894
+ sendUserMessage: async (_content, options) => {
5895
+ await options?.onPreflightAcceptCommit?.();
5896
+ await neverSettlingPromise();
5897
+ },
5898
+ terminalAbortSeams: {
5899
+ getActivePromptHandle: () => "sdk-only-deadline-handle",
5900
+ abortPromptAndWaitWithTerminal: async (handle, seamOptions) => {
5901
+ abortCalls.push({ handle, graceMs: seamOptions.graceMs });
5902
+ return { status: "settled", terminalScope: {} };
5903
+ },
5904
+ // Threaded through `SdkOnlyTerminalAbortSeams` (change 1): the seam
5905
+ // must type-check and the extension must construct with it present.
5906
+ // Reads are recorded rather than asserted non-empty — nothing on this
5907
+ // route consults it yet, which is the honest state of the contract.
5908
+ pendingToolExecutions: handle => {
5909
+ pendingLedgerReads.push(handle);
5910
+ return ["sdk-only-mutating-tool"];
5911
+ },
5912
+ },
5913
+ });
5914
+ const accepted = await harness.control("turn.prompt", { text: "sdk-only deadline" });
5915
+ expect(accepted.ok).toBe(true);
5916
+ const correlation = {
5917
+ commandId: accepted.result?.commandId,
5918
+ turnId: accepted.result?.turnId,
5919
+ };
5920
+ await harness.emit("agent_start");
5921
+ // A dispatched tool is running when the deadline expires: its start has
5922
+ // been delivered and no end ever follows.
5923
+ await harness.emit("tool_execution_start", {
5924
+ type: "tool_execution_start",
5925
+ toolCallId: "sdk-only-mutating-tool",
5926
+ toolName: "apply_patch",
5927
+ args: {},
5928
+ });
5929
+
5930
+ expect(await settledStatus(harness, "turn.prompt_status", correlation)).toMatchObject({
5931
+ status: "failed",
5932
+ error: { code: "prompt_deadline_exceeded" },
5933
+ outcome: { kind: "failed", code: "prompt_deadline_exceeded", provenance: "deadline" },
5934
+ });
5935
+ await awaitCorrelatedFrames(
5936
+ harness,
5937
+ correlation,
5938
+ frames =>
5939
+ frames.some(frame => frame.kind === "agent_failed") && frames.some(frame => frame.kind === "agent_end"),
5940
+ );
5941
+ const frames = correlatedFrames(harness, correlation);
5942
+ expect(frames.filter(frame => frame.kind === "agent_failed")).toEqual([
5943
+ expect.objectContaining({
5944
+ payload: expect.objectContaining({
5945
+ error: expect.objectContaining({ code: "prompt_deadline_exceeded" }),
5946
+ }),
5947
+ }),
5948
+ ]);
5949
+ expect(frames.filter(frame => frame.kind === "agent_end")).toEqual([
5950
+ expect.objectContaining({
5951
+ payload: expect.objectContaining({
5952
+ outcome: expect.objectContaining({
5953
+ kind: "failed",
5954
+ code: "prompt_deadline_exceeded",
5955
+ provenance: "deadline",
5956
+ }),
5957
+ }),
5958
+ }),
5959
+ ]);
5960
+
5961
+ // THE point of the case: the terminal was published, and the run was never
5962
+ // aborted — so there is no mid-tool kill to prevent on this route.
5963
+ expect(abortCalls).toEqual([]);
5964
+ // A late real boundary for the still-"running" tool changes nothing.
5965
+ await harness.emit("tool_execution_end", {
5966
+ type: "tool_execution_end",
5967
+ toolCallId: "sdk-only-mutating-tool",
5968
+ toolName: "apply_patch",
5969
+ isError: false,
5970
+ });
5971
+ await Bun.sleep(50);
5972
+ expect(abortCalls).toEqual([]);
5973
+ expect(correlatedFrames(harness, correlation).filter(frame => frame.kind === "agent_end")).toHaveLength(1);
5974
+ await harness.stop();
5975
+ } finally {
5976
+ await Bun.sleep(50);
5977
+ await rm(cwd, { recursive: true, force: true });
5978
+ }
5979
+ });
5980
+
5803
5981
  test("the deadline/agent_end overlap publishes exactly one correlated terminal boundary", async () => {
5804
5982
  // Review P1: the two terminal publishers genuinely overlap. The provider
5805
5983
  // agent_end handler captures its transitions synchronously and then awaits
@@ -6085,6 +6263,10 @@ describe("accepted-control zero-execution bound (#4668)", () => {
6085
6263
  const expiringIds = { commandId: expiring.result?.commandId, turnId: expiring.result?.turnId };
6086
6264
  const leadingIds = { commandId: leading.result?.commandId, turnId: leading.result?.turnId };
6087
6265
  await harness.emit("agent_start");
6266
+ await awaitCorrelatedFrames(harness, expiringIds, frames =>
6267
+ frames.some(frame => frame.kind === "agent_start"),
6268
+ );
6269
+ await awaitCorrelatedFrames(harness, leadingIds, frames => frames.some(frame => frame.kind === "agent_start"));
6088
6270
  // Both must be in the drained batch: a lease that fired before the drain
6089
6271
  // would leave the handler with nothing to park on and prove nothing.
6090
6272
  expect({