@gajae-code/coding-agent 0.17.2 → 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 (349) hide show
  1. package/CHANGELOG.md +194 -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 +18 -0
  18. package/dist/types/config/file-lock.d.ts +9 -1
  19. package/dist/types/config/model-profile-activation.d.ts +45 -1
  20. package/dist/types/config/model-registry.d.ts +11 -2
  21. package/dist/types/config/settings-schema.d.ts +11 -0
  22. package/dist/types/coordinator-mcp/question-state.d.ts +6 -1
  23. package/dist/types/daemon/operator-contract.d.ts +5 -1
  24. package/dist/types/eval/js/worker-protocol.d.ts +2 -0
  25. package/dist/types/extensibility/extensions/types.d.ts +12 -0
  26. package/dist/types/extensibility/gjc-plugins/registry.d.ts +4 -0
  27. package/dist/types/extensibility/runtime-skill-discovery.d.ts +18 -0
  28. package/dist/types/extensibility/shared-events.d.ts +3 -1
  29. package/dist/types/gjc-runtime/session-state-lock.d.ts +61 -0
  30. package/dist/types/internal-urls/embedded-protocol.d.ts +9 -0
  31. package/dist/types/internal-urls/index.d.ts +1 -0
  32. package/dist/types/internal-urls/parse.d.ts +9 -1
  33. package/dist/types/main.d.ts +22 -0
  34. package/dist/types/modes/acp/acp-agent.d.ts +55 -1
  35. package/dist/types/modes/acp/startup-provenance.d.ts +16 -0
  36. package/dist/types/modes/components/custom-provider-wizard.d.ts +19 -1
  37. package/dist/types/modes/components/session-selector.d.ts +5 -2
  38. package/dist/types/modes/components/user-message-selector.d.ts +5 -2
  39. package/dist/types/modes/controllers/command-controller.d.ts +1 -1
  40. package/dist/types/modes/controllers/selector-controller.d.ts +1 -1
  41. package/dist/types/modes/interactive-mode.d.ts +1 -1
  42. package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +11 -1
  43. package/dist/types/modes/shared/agent-wire/workflow-gate-types.d.ts +14 -1
  44. package/dist/types/modes/types.d.ts +2 -2
  45. package/dist/types/runtime/process-lifecycle.d.ts +2 -0
  46. package/dist/types/runtime-mcp/manager.d.ts +56 -0
  47. package/dist/types/runtime-mcp/pool.d.ts +17 -0
  48. package/dist/types/runtime-mcp/redaction.d.ts +20 -0
  49. package/dist/types/runtime-mcp/startup-policy.d.ts +6 -0
  50. package/dist/types/runtime-mcp/tool-bridge.d.ts +9 -0
  51. package/dist/types/runtime-mcp/transports/http.d.ts +11 -0
  52. package/dist/types/sdk/broker/broker.d.ts +13 -2
  53. package/dist/types/sdk/broker/identity.d.ts +3 -1
  54. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +23 -3
  55. package/dist/types/sdk/broker/lifecycle.d.ts +15 -8
  56. package/dist/types/sdk/broker/runtime.d.ts +1 -1
  57. package/dist/types/sdk/broker/session-index.d.ts +22 -3
  58. package/dist/types/sdk/broker/spawn-authority.d.ts +4 -0
  59. package/dist/types/sdk/broker/stderr-drainer.d.ts +13 -0
  60. package/dist/types/sdk/bus/index.d.ts +19 -0
  61. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +3 -1
  62. package/dist/types/sdk/bus/telegram-daemon.d.ts +5 -0
  63. package/dist/types/sdk/cli/master-cli.d.ts +6 -10
  64. package/dist/types/sdk/cli/rows.d.ts +19 -1
  65. package/dist/types/sdk/cli/session-cli.d.ts +22 -5
  66. package/dist/types/sdk/client/client.d.ts +2 -0
  67. package/dist/types/sdk/guides/cli.d.ts +3 -4
  68. package/dist/types/sdk/guides/verify.d.ts +2 -0
  69. package/dist/types/sdk/host/host.d.ts +9 -0
  70. package/dist/types/sdk/host/session-runtime.d.ts +31 -0
  71. package/dist/types/sdk/host/types.d.ts +10 -0
  72. package/dist/types/sdk/index.d.ts +1 -0
  73. package/dist/types/sdk/prompt-deadline-flush.d.ts +83 -0
  74. package/dist/types/sdk/prompt-deadline-manager.d.ts +37 -0
  75. package/dist/types/sdk/prompt-tool-boundary.d.ts +73 -0
  76. package/dist/types/sdk/router/session-router.d.ts +2 -0
  77. package/dist/types/sdk/startup-capability.d.ts +2 -0
  78. package/dist/types/sdk/transport/relay.d.ts +1 -0
  79. package/dist/types/sdk/transport/serve-cli.d.ts +48 -4
  80. package/dist/types/sdk/transport/stdio.d.ts +5 -1
  81. package/dist/types/session/agent-session.d.ts +168 -21
  82. package/dist/types/session/fallback-chain-controller.d.ts +6 -6
  83. package/dist/types/session/fold-coordinator.d.ts +17 -3
  84. package/dist/types/session/internal/managed-session-storage.d.ts +7 -1
  85. package/dist/types/session/session-manager.d.ts +64 -1
  86. package/dist/types/session/session-work-lease.d.ts +14 -0
  87. package/dist/types/setup/model-onboarding-guidance.d.ts +1 -1
  88. package/dist/types/setup/provider-onboarding.d.ts +81 -1
  89. package/dist/types/tools/bash.d.ts +37 -11
  90. package/dist/types/tools/job.d.ts +1 -1
  91. package/dist/types/tools/steer-fold.d.ts +31 -0
  92. package/dist/types/utils/git.d.ts +28 -2
  93. package/dist/types/utils/mupdf-embedded.d.ts +2 -0
  94. package/dist/types/utils/mupdf.d.ts +4 -0
  95. package/dist/types/utils/session-title-generation.d.ts +6 -0
  96. package/dist/types/utils/title-generator.d.ts +11 -2
  97. package/dist/types/web/scrapers/types.d.ts +3 -0
  98. package/package.json +18 -10
  99. package/scripts/build-binary.ts +9 -3
  100. package/scripts/compile-args.ts +0 -9
  101. package/scripts/embed-mupdf.ts +52 -0
  102. package/scripts/generate-sdk-operation-inventory.ts +31 -1
  103. package/scripts/vendor-markit.ts +196 -0
  104. package/src/async/job-manager.ts +22 -1
  105. package/src/cli/daemon-cli.ts +53 -18
  106. package/src/cli/github-release.ts +87 -5
  107. package/src/cli/mcp-cli.ts +38 -4
  108. package/src/cli/public-command-entry.ts +450 -0
  109. package/src/cli/public-command-errors.ts +581 -0
  110. package/src/cli/public-command-evidence.ts +717 -0
  111. package/src/cli/public-command-help.ts +360 -0
  112. package/src/cli/public-command-registry.ts +535 -0
  113. package/src/cli/session-picker.ts +2 -1
  114. package/src/cli/setup-cli.ts +9 -1
  115. package/src/cli/skills-cli.ts +46 -2
  116. package/src/cli.ts +61 -1
  117. package/src/commands/daemon.ts +65 -69
  118. package/src/commands/sdk.ts +175 -117
  119. package/src/commands/setup.ts +2 -0
  120. package/src/commands/skills.ts +11 -0
  121. package/src/config/autorouting-tier-map.ts +14 -0
  122. package/src/config/file-lock.ts +147 -51
  123. package/src/config/model-profile-activation.ts +334 -43
  124. package/src/config/model-registry.ts +183 -25
  125. package/src/config/settings-schema.ts +26 -2
  126. package/src/coordinator-mcp/policy.ts +10 -5
  127. package/src/coordinator-mcp/question-gate-codec.ts +17 -0
  128. package/src/coordinator-mcp/question-state.ts +175 -8
  129. package/src/coordinator-mcp/server.ts +507 -81
  130. package/src/coordinator-mcp/session-reaper.ts +6 -4
  131. package/src/crash/record-loader.ts +6 -2
  132. package/src/daemon/operator-contract.ts +26 -12
  133. package/src/defaults/gjc/skills/ultragoal/SKILL.md +1 -1
  134. package/src/eval/js/context-manager.ts +8 -8
  135. package/src/eval/js/worker-core.ts +9 -4
  136. package/src/eval/js/worker-protocol.ts +2 -0
  137. package/src/extensibility/extensions/runner.ts +52 -4
  138. package/src/extensibility/extensions/types.ts +10 -0
  139. package/src/extensibility/gjc-plugins/registry.ts +196 -37
  140. package/src/extensibility/runtime-skill-discovery.ts +61 -3
  141. package/src/extensibility/shared-events.ts +3 -1
  142. package/src/gjc-runtime/session-state-lock.ts +165 -24
  143. package/src/gjc-runtime/ultragoal-succession.ts +31 -3
  144. package/src/internal-urls/docs-index.generated.ts +20 -19
  145. package/src/internal-urls/embedded-protocol.ts +83 -0
  146. package/src/internal-urls/index.ts +1 -0
  147. package/src/internal-urls/parse.ts +21 -1
  148. package/src/internal-urls/router.ts +4 -2
  149. package/src/main.ts +123 -55
  150. package/src/modes/acp/acp-agent.ts +408 -35
  151. package/src/modes/acp/startup-provenance.ts +59 -0
  152. package/src/modes/components/custom-provider-wizard.ts +308 -7
  153. package/src/modes/components/oauth-selector.ts +9 -0
  154. package/src/modes/components/session-selector.ts +47 -4
  155. package/src/modes/components/user-message-selector.ts +57 -15
  156. package/src/modes/controllers/command-controller.ts +58 -32
  157. package/src/modes/controllers/extension-ui-controller.ts +33 -7
  158. package/src/modes/controllers/input-controller.ts +4 -0
  159. package/src/modes/controllers/selector-controller.ts +161 -15
  160. package/src/modes/interactive-mode.ts +21 -7
  161. package/src/modes/runtime-init.ts +1 -0
  162. package/src/modes/shared/agent-wire/workflow-gate-broker.ts +124 -7
  163. package/src/modes/shared/agent-wire/workflow-gate-types.ts +16 -2
  164. package/src/modes/types.ts +2 -2
  165. package/src/prompts/agent-fragments/browser-aside.md +1 -0
  166. package/src/prompts/system/title-system.md +1 -1
  167. package/src/prompts/tools/bash.md +1 -1
  168. package/src/prompts/tools/job.md +1 -1
  169. package/src/prompts/tools/read.md +2 -0
  170. package/src/runtime/network-prewarm-service.test.ts +36 -1
  171. package/src/runtime/network-prewarm-service.ts +28 -3
  172. package/src/runtime/process-lifecycle.ts +22 -11
  173. package/src/runtime-mcp/config.ts +16 -1
  174. package/src/runtime-mcp/manager.ts +689 -55
  175. package/src/runtime-mcp/pool.test.ts +31 -0
  176. package/src/runtime-mcp/pool.ts +304 -28
  177. package/src/runtime-mcp/redaction.ts +42 -0
  178. package/src/runtime-mcp/startup-policy.ts +7 -0
  179. package/src/runtime-mcp/tool-bridge.ts +71 -0
  180. package/src/runtime-mcp/transports/http.ts +108 -25
  181. package/src/sdk/acp/adapter.ts +27 -8
  182. package/src/sdk/broker/broker.ts +378 -52
  183. package/src/sdk/broker/identity.ts +9 -1
  184. package/src/sdk/broker/lifecycle-ledger.ts +95 -31
  185. package/src/sdk/broker/lifecycle.ts +690 -141
  186. package/src/sdk/broker/runtime.ts +1 -1
  187. package/src/sdk/broker/session-index.ts +97 -4
  188. package/src/sdk/broker/spawn-authority.ts +35 -7
  189. package/src/sdk/broker/stderr-drainer.ts +59 -0
  190. package/src/sdk/bus/chat-daemon-control.ts +2 -2
  191. package/src/sdk/bus/chat-daemon-runtime.ts +1 -0
  192. package/src/sdk/bus/index.ts +690 -80
  193. package/src/sdk/bus/telegram-daemon-cli.ts +31 -2
  194. package/src/sdk/bus/telegram-daemon-contract.ts +3 -1
  195. package/src/sdk/bus/telegram-daemon.ts +10 -3
  196. package/src/sdk/cli/master-cli.ts +47 -36
  197. package/src/sdk/cli/rows.ts +30 -7
  198. package/src/sdk/cli/session-cli.ts +687 -184
  199. package/src/sdk/client/client.ts +18 -0
  200. package/src/sdk/guides/catalog.ts +2 -2
  201. package/src/sdk/guides/cli.ts +20 -62
  202. package/src/sdk/guides/verify.ts +14 -0
  203. package/src/sdk/host/host.ts +49 -5
  204. package/src/sdk/host/session-runtime.test.ts +138 -2
  205. package/src/sdk/host/session-runtime.ts +311 -97
  206. package/src/sdk/host/types.ts +12 -1
  207. package/src/sdk/host/websocket-transport.lifecycle.test.ts +84 -0
  208. package/src/sdk/host/websocket-transport.ts +40 -6
  209. package/src/sdk/index.ts +11 -0
  210. package/src/sdk/prompt-deadline-flush.ts +334 -0
  211. package/src/sdk/prompt-deadline-manager.ts +133 -1
  212. package/src/sdk/prompt-tool-boundary.ts +121 -0
  213. package/src/sdk/protocol/operation-inventory.generated.json +106 -0
  214. package/src/sdk/router/session-router.ts +9 -1
  215. package/src/sdk/session.ts +182 -7
  216. package/src/sdk/startup-capability.ts +1 -1
  217. package/src/sdk/transport/relay.ts +71 -6
  218. package/src/sdk/transport/serve-cli.ts +397 -42
  219. package/src/sdk/transport/socket.ts +12 -11
  220. package/src/sdk/transport/stdio.ts +7 -3
  221. package/src/session/agent-session.ts +2437 -400
  222. package/src/session/fallback-chain-controller.ts +6 -7
  223. package/src/session/fold-coordinator.ts +24 -5
  224. package/src/session/internal/managed-session-scope.ts +58 -18
  225. package/src/session/internal/managed-session-storage.ts +304 -11
  226. package/src/session/session-manager.ts +382 -44
  227. package/src/session/session-work-lease.ts +34 -0
  228. package/src/setup/model-onboarding-guidance.ts +1 -1
  229. package/src/setup/provider-onboarding.ts +397 -24
  230. package/src/slash-commands/builtin-registry.ts +258 -14
  231. package/src/task/index.ts +14 -2
  232. package/src/tools/bash.ts +98 -78
  233. package/src/tools/fetch.ts +119 -20
  234. package/src/tools/job.ts +151 -25
  235. package/src/tools/output-meta.ts +15 -2
  236. package/src/tools/path-utils.ts +27 -3
  237. package/src/tools/read.ts +3 -3
  238. package/src/tools/steer-fold.ts +107 -0
  239. package/src/tools/tool-catalog.generated.ts +29 -2
  240. package/src/tools/tool-errors.ts +4 -0
  241. package/src/utils/git.ts +53 -3
  242. package/src/utils/markit.ts +55 -41
  243. package/src/utils/mupdf-assets.d.ts +4 -0
  244. package/src/utils/mupdf-embedded.ts +4 -0
  245. package/src/utils/mupdf.ts +129 -0
  246. package/src/utils/session-title-generation.ts +29 -0
  247. package/src/utils/title-generator.ts +46 -2
  248. package/src/web/scrapers/types.ts +24 -3
  249. package/vendor/markit-ai/LICENSE +21 -0
  250. package/vendor/markit-ai/MANIFEST.json +412 -0
  251. package/vendor/markit-ai/dist/commands/config.d.ts +4 -0
  252. package/vendor/markit-ai/dist/commands/config.js +139 -0
  253. package/vendor/markit-ai/dist/commands/convert.d.ts +6 -0
  254. package/vendor/markit-ai/dist/commands/convert.js +113 -0
  255. package/vendor/markit-ai/dist/commands/formats.d.ts +2 -0
  256. package/vendor/markit-ai/dist/commands/formats.js +92 -0
  257. package/vendor/markit-ai/dist/commands/init.d.ts +2 -0
  258. package/vendor/markit-ai/dist/commands/init.js +29 -0
  259. package/vendor/markit-ai/dist/commands/onboard.d.ts +2 -0
  260. package/vendor/markit-ai/dist/commands/onboard.js +61 -0
  261. package/vendor/markit-ai/dist/commands/plugin.d.ts +4 -0
  262. package/vendor/markit-ai/dist/commands/plugin.js +58 -0
  263. package/vendor/markit-ai/dist/config.d.ts +26 -0
  264. package/vendor/markit-ai/dist/config.js +42 -0
  265. package/vendor/markit-ai/dist/converters/audio.d.ts +7 -0
  266. package/vendor/markit-ai/dist/converters/audio.js +104 -0
  267. package/vendor/markit-ai/dist/converters/csv.d.ts +7 -0
  268. package/vendor/markit-ai/dist/converters/csv.js +83 -0
  269. package/vendor/markit-ai/dist/converters/docx.d.ts +6 -0
  270. package/vendor/markit-ai/dist/converters/docx.js +57 -0
  271. package/vendor/markit-ai/dist/converters/epub.d.ts +8 -0
  272. package/vendor/markit-ai/dist/converters/epub.js +128 -0
  273. package/vendor/markit-ai/dist/converters/github.d.ts +18 -0
  274. package/vendor/markit-ai/dist/converters/github.js +148 -0
  275. package/vendor/markit-ai/dist/converters/html.d.ts +6 -0
  276. package/vendor/markit-ai/dist/converters/html.js +30 -0
  277. package/vendor/markit-ai/dist/converters/image.d.ts +6 -0
  278. package/vendor/markit-ai/dist/converters/image.js +124 -0
  279. package/vendor/markit-ai/dist/converters/ipynb.d.ts +6 -0
  280. package/vendor/markit-ai/dist/converters/ipynb.js +75 -0
  281. package/vendor/markit-ai/dist/converters/iwork.d.ts +20 -0
  282. package/vendor/markit-ai/dist/converters/iwork.js +391 -0
  283. package/vendor/markit-ai/dist/converters/json.d.ts +6 -0
  284. package/vendor/markit-ai/dist/converters/json.js +21 -0
  285. package/vendor/markit-ai/dist/converters/pdf/columns.d.ts +35 -0
  286. package/vendor/markit-ai/dist/converters/pdf/columns.js +93 -0
  287. package/vendor/markit-ai/dist/converters/pdf/extract.d.ts +19 -0
  288. package/vendor/markit-ai/dist/converters/pdf/extract.js +514 -0
  289. package/vendor/markit-ai/dist/converters/pdf/grid.d.ts +25 -0
  290. package/vendor/markit-ai/dist/converters/pdf/grid.js +795 -0
  291. package/vendor/markit-ai/dist/converters/pdf/headers.d.ts +24 -0
  292. package/vendor/markit-ai/dist/converters/pdf/headers.js +108 -0
  293. package/vendor/markit-ai/dist/converters/pdf/index.d.ts +19 -0
  294. package/vendor/markit-ai/dist/converters/pdf/index.js +136 -0
  295. package/vendor/markit-ai/dist/converters/pdf/mupdf-loader.js +9 -0
  296. package/vendor/markit-ai/dist/converters/pdf/render.d.ts +24 -0
  297. package/vendor/markit-ai/dist/converters/pdf/render.js +513 -0
  298. package/vendor/markit-ai/dist/converters/pdf/types.d.ts +75 -0
  299. package/vendor/markit-ai/dist/converters/pdf/types.js +1 -0
  300. package/vendor/markit-ai/dist/converters/plain-text.d.ts +6 -0
  301. package/vendor/markit-ai/dist/converters/plain-text.js +87 -0
  302. package/vendor/markit-ai/dist/converters/pptx.d.ts +8 -0
  303. package/vendor/markit-ai/dist/converters/pptx.js +289 -0
  304. package/vendor/markit-ai/dist/converters/rss.d.ts +11 -0
  305. package/vendor/markit-ai/dist/converters/rss.js +138 -0
  306. package/vendor/markit-ai/dist/converters/wikipedia.d.ts +6 -0
  307. package/vendor/markit-ai/dist/converters/wikipedia.js +33 -0
  308. package/vendor/markit-ai/dist/converters/xlsx.d.ts +8 -0
  309. package/vendor/markit-ai/dist/converters/xlsx.js +147 -0
  310. package/vendor/markit-ai/dist/converters/xml.d.ts +6 -0
  311. package/vendor/markit-ai/dist/converters/xml.js +18 -0
  312. package/vendor/markit-ai/dist/converters/yaml.d.ts +6 -0
  313. package/vendor/markit-ai/dist/converters/yaml.js +17 -0
  314. package/vendor/markit-ai/dist/converters/zip.d.ts +8 -0
  315. package/vendor/markit-ai/dist/converters/zip.js +57 -0
  316. package/vendor/markit-ai/dist/index.d.ts +30 -0
  317. package/vendor/markit-ai/dist/index.js +26 -0
  318. package/vendor/markit-ai/dist/main.d.ts +2 -0
  319. package/vendor/markit-ai/dist/main.js +191 -0
  320. package/vendor/markit-ai/dist/markit.d.ts +45 -0
  321. package/vendor/markit-ai/dist/markit.js +267 -0
  322. package/vendor/markit-ai/dist/plugins/api.d.ts +7 -0
  323. package/vendor/markit-ai/dist/plugins/api.js +42 -0
  324. package/vendor/markit-ai/dist/plugins/index.d.ts +4 -0
  325. package/vendor/markit-ai/dist/plugins/index.js +3 -0
  326. package/vendor/markit-ai/dist/plugins/installer.d.ts +25 -0
  327. package/vendor/markit-ai/dist/plugins/installer.js +176 -0
  328. package/vendor/markit-ai/dist/plugins/loader.d.ts +6 -0
  329. package/vendor/markit-ai/dist/plugins/loader.js +61 -0
  330. package/vendor/markit-ai/dist/plugins/types.d.ts +25 -0
  331. package/vendor/markit-ai/dist/plugins/types.js +1 -0
  332. package/vendor/markit-ai/dist/providers/anthropic.d.ts +2 -0
  333. package/vendor/markit-ai/dist/providers/anthropic.js +47 -0
  334. package/vendor/markit-ai/dist/providers/index.d.ts +21 -0
  335. package/vendor/markit-ai/dist/providers/index.js +56 -0
  336. package/vendor/markit-ai/dist/providers/openai.d.ts +2 -0
  337. package/vendor/markit-ai/dist/providers/openai.js +76 -0
  338. package/vendor/markit-ai/dist/providers/types.d.ts +26 -0
  339. package/vendor/markit-ai/dist/providers/types.js +1 -0
  340. package/vendor/markit-ai/dist/types.d.ts +36 -0
  341. package/vendor/markit-ai/dist/types.js +1 -0
  342. package/vendor/markit-ai/dist/utils/exit-codes.d.ts +4 -0
  343. package/vendor/markit-ai/dist/utils/exit-codes.js +4 -0
  344. package/vendor/markit-ai/dist/utils/output.d.ts +22 -0
  345. package/vendor/markit-ai/dist/utils/output.js +31 -0
  346. package/vendor/markit-ai/dist/utils/turndown.d.ts +8 -0
  347. package/vendor/markit-ai/dist/utils/turndown.js +70 -0
  348. package/vendor/markit-ai/package.json +74 -0
  349. package/vendor/markit-ai.patch +49 -0
@@ -24,6 +24,7 @@ import type {
24
24
  WorkflowGateOption,
25
25
  WorkflowGateQueryRecord,
26
26
  WorkflowGateResolution,
27
+ WorkflowGateResolutionDiagnostic,
27
28
  WorkflowGateResponse,
28
29
  WorkflowGateValidationError,
29
30
  WorkflowStage,
@@ -35,6 +36,9 @@ export type PersistedResolutionOrigin =
35
36
  | { kind: "generic"; channel: "sdk" | "other" }
36
37
  | { kind: "telegram_notification"; interactionActionId: string };
37
38
 
39
+ /** Keep accepted diagnostics below the coordinator's cumulative Q12 snapshot cap. */
40
+ export const MAX_ACCEPTED_WORKFLOW_GATE_QUERY_RECORDS = 32;
41
+
38
42
  export type PersistedAckPolicy =
39
43
  | { kind: "none"; reason: "non_telegram" | "semantic_noncommit" | "legacy_unproven" }
40
44
  | {
@@ -152,7 +156,7 @@ export class BrokerWorkflowGateEmitter implements WorkflowGateEmitter {
152
156
  readonly #emitterHooks: Pick<BrokerHooks, "advance">;
153
157
  #runtimeTurnProvider: (() => string | undefined) | undefined;
154
158
 
155
- constructor(runId: string, store: GateStore, emitterHooks: Pick<BrokerHooks, "advance"> = {}) {
159
+ constructor(runId: string, store: GateStore, emitterHooks: Pick<BrokerHooks, "advance" | "continuationLost"> = {}) {
156
160
  this.#runId = runId;
157
161
  this.#store = store;
158
162
  this.#emitterHooks = emitterHooks;
@@ -164,6 +168,7 @@ export class BrokerWorkflowGateEmitter implements WorkflowGateEmitter {
164
168
  completeAccepted: record => this.#completeAccepted(record),
165
169
  finalizeAccepted: record => this.#finalizeAccepted(record),
166
170
  terminalizeAccepted: record => this.#terminalizeAccepted(record),
171
+ continuationLost: record => emitterHooks.continuationLost?.(record),
167
172
  });
168
173
  }
169
174
 
@@ -1147,6 +1152,8 @@ export type GateAuditEvent =
1147
1152
  | { event: "gate_response_accepted"; gate_id: string; answer_hash: string }
1148
1153
  | { event: "gate_response_rejected"; gate_id: string; answer_hash: string }
1149
1154
  | { event: "gate_response_idempotent_replay"; gate_id: string }
1155
+ /** An idempotent replay refused because this runtime cannot prove the continuation ran. */
1156
+ | { event: "gate_response_replay_unproven"; gate_id: string }
1150
1157
  | { event: "gate_response_idempotency_conflict"; gate_id: string }
1151
1158
  | { event: "gate_response_already_resolved"; gate_id: string }
1152
1159
  | { event: "gate_response_unknown_gate"; gate_id: string }
@@ -1171,6 +1178,8 @@ export interface BrokerHooks {
1171
1178
  terminalizeAccepted?(record: PersistedGate): WorkflowGateTerminalProof | Promise<WorkflowGateTerminalProof>;
1172
1179
  /** Runs only after a successful advance has been durably marked advanced. */
1173
1180
  completeAccepted?(record: PersistedGate): void | Promise<void>;
1181
+ /** Notifies the owning runtime when the accepted answer cannot reach its continuation. */
1182
+ continuationLost?(record: PersistedGate): void;
1174
1183
  /** Append-only audit sink. */
1175
1184
  audit?(event: GateAuditEvent): void;
1176
1185
  }
@@ -1222,6 +1231,14 @@ export class WorkflowGateBroker {
1222
1231
  private readonly gateLocks = new Map<string, Promise<void>>();
1223
1232
  private readonly continuations = new Map<string, GateContinuation>();
1224
1233
  readonly #terminalProofs = new Map<string, WorkflowGateTerminalProof>();
1234
+ /**
1235
+ * Gates whose `completeAccepted` hook threw after `advanced:true` was already
1236
+ * committed, so the durable record claims completion the continuation never
1237
+ * received. In-memory by design: the stranded turn belongs to this process's
1238
+ * waiter, so a restart cannot resolve it either way and must not inherit a
1239
+ * claim it cannot honor (#5599).
1240
+ */
1241
+ readonly #continuationLost = new Set<string>();
1225
1242
 
1226
1243
  private async withGateLock<T>(gateId: string, operation: () => Promise<T>): Promise<T> {
1227
1244
  const previous = this.gateLocks.get(gateId) ?? Promise.resolve();
@@ -1282,6 +1299,34 @@ export class WorkflowGateBroker {
1282
1299
  `idempotency_conflict: gate ${response.gate_id} resolved with a different body`,
1283
1300
  );
1284
1301
  if (response.idempotency_key !== undefined && sameKey && sameBody) {
1302
+ // A record whose `completeAccepted` threw is durably indistinguishable from a
1303
+ // genuinely completed one: `advanced:true` is committed before the waiter is
1304
+ // resolved, so the shape below matches either way. Reporting `completed` there
1305
+ // told `resolveSdkWorkflowGate`'s catch path to return a successful resolution
1306
+ // for a turn whose continuation was never resolved — `ok:true status:"accepted"`
1307
+ // with an empty pending-gate list and a prompt stuck `in_flight` forever, with
1308
+ // no recovery path because `recover()` skips advanced records (#5599).
1309
+ //
1310
+ // Downgrading to `accepted_incomplete` routes it through the existing
1311
+ // recover-then-recheck branch and, when that cannot resolve it, surfaces the
1312
+ // documented `terminal_uncertain` instead of a false success.
1313
+ //
1314
+ // TWO conditions, because the loss cannot be persisted: an `accepted` record
1315
+ // may not carry a `lifecycle` and a `quarantined` one must be `advanced:false`,
1316
+ // so the durable shape has nowhere to record it.
1317
+ //
1318
+ // 1. This runtime saw the hook throw.
1319
+ if (this.#continuationLost.has(response.gate_id)) return { kind: "accepted_incomplete" };
1320
+ // 2. The record belongs to a PREVIOUS runtime. A continuation waiter is
1321
+ // process-local and cannot survive a restart, so this process can never
1322
+ // prove the prior turn was resolved — and the in-memory set above is empty
1323
+ // after a restart, which is exactly how the first fix still handed back a
1324
+ // false `completed` on a cross-restart idempotent retry (#5599 review).
1325
+ // Fail closed instead. The cost is telling a retry "uncertain" about a gate
1326
+ // that did complete under the dead runtime; that is the honest answer
1327
+ // regardless, because the turn it belonged to died with that process and no
1328
+ // retry can continue it.
1329
+ if (record.ownerInstanceId !== this.instanceId) return { kind: "accepted_incomplete" };
1285
1330
  if (record.terminalized === true && record.advanced === true && record.resolution)
1286
1331
  return { kind: "completed", resolution: record.resolution };
1287
1332
  return { kind: "accepted_incomplete" };
@@ -1311,6 +1356,17 @@ export class WorkflowGateBroker {
1311
1356
  return this.continuations.get(gateId)?.isLive(gateId) === true;
1312
1357
  }
1313
1358
 
1359
+ private recordContinuationLoss(gateId: string): void {
1360
+ this.#continuationLost.add(gateId);
1361
+ const lost = this.store.get(gateId);
1362
+ if (!lost) return;
1363
+ try {
1364
+ this.hooks.continuationLost?.(lost);
1365
+ } catch {
1366
+ // A settlement notification must never replace the original resolution error.
1367
+ }
1368
+ }
1369
+
1314
1370
  hasRecoverableAcceptedGate(): boolean {
1315
1371
  return this.store
1316
1372
  .list()
@@ -1395,8 +1451,10 @@ export class WorkflowGateBroker {
1395
1451
  .filter(
1396
1452
  (
1397
1453
  record,
1398
- ): record is PersistedGate & { status: "quarantined"; lifecycle: WorkflowGateDiagnostic["lifecycle"] } =>
1399
- record.status === "quarantined" && record.lifecycle !== undefined,
1454
+ ): record is PersistedGate & {
1455
+ status: "quarantined";
1456
+ lifecycle: WorkflowGateDiagnostic["lifecycle"];
1457
+ } => record.status === "quarantined" && record.lifecycle !== undefined,
1400
1458
  )
1401
1459
  .sort(
1402
1460
  (left, right) =>
@@ -1408,6 +1466,7 @@ export class WorkflowGateBroker {
1408
1466
  id: `diagnostic:${record.gate.gate_id}`,
1409
1467
  tag: "quarantined",
1410
1468
  lifecycle: record.lifecycle,
1469
+ answer_recorded: Object.hasOwn(record, "answer"),
1411
1470
  }));
1412
1471
  }
1413
1472
 
@@ -1421,8 +1480,38 @@ export class WorkflowGateBroker {
1421
1480
  this.hasLiveContinuation(record.gate.gate_id),
1422
1481
  )
1423
1482
  .sort(compareGates)
1424
- .map(record => ({ ...record.gate, id: `pending:${record.gate.gate_id}`, tag: "pending" }));
1425
- return [...pending, ...this.listGateDiagnostics()];
1483
+ .map(record => ({
1484
+ ...record.gate,
1485
+ id: `pending:${record.gate.gate_id}`,
1486
+ tag: "pending" as const,
1487
+ answer_recorded: false as const,
1488
+ }));
1489
+ const accepted: WorkflowGateResolutionDiagnostic[] = this.store
1490
+ .list()
1491
+ .filter((record): record is PersistedGate & { status: "accepted" } => record.status === "accepted")
1492
+ .sort(
1493
+ (left, right) =>
1494
+ (right.resolution?.resolved_at ?? "").localeCompare(left.resolution?.resolved_at ?? "") ||
1495
+ right.gate.gate_id.localeCompare(left.gate.gate_id),
1496
+ )
1497
+ .slice(0, MAX_ACCEPTED_WORKFLOW_GATE_QUERY_RECORDS)
1498
+ .map(record => ({
1499
+ ...record.gate,
1500
+ id: `diagnostic:${record.gate.gate_id}`,
1501
+ tag: "accepted" as const,
1502
+ answer_recorded: true as const,
1503
+ resolved_at: record.resolution?.resolved_at ?? record.gate.created_at,
1504
+ post_accept_disposition: this.#continuationLost.has(record.gate.gate_id)
1505
+ ? "continuation_lost"
1506
+ : record.advanced
1507
+ ? record.ownerInstanceId === this.instanceId
1508
+ ? "advanced"
1509
+ : "unknown"
1510
+ : record.terminalized === true
1511
+ ? "terminalized"
1512
+ : "accepted",
1513
+ }));
1514
+ return [...pending, ...accepted, ...this.listGateDiagnostics()];
1426
1515
  }
1427
1516
 
1428
1517
  /** Open and emit a gate. The pending record is persisted BEFORE emission. */
@@ -1516,6 +1605,17 @@ export class WorkflowGateBroker {
1516
1605
  const sameBody = record.responseHash === responseHash;
1517
1606
  const sameKey = record.idempotencyKey === response.idempotency_key;
1518
1607
  if (response.idempotency_key !== undefined && sameKey && sameBody) {
1608
+ // Same provenance rule as `lookupCompletedResolution`: an idempotent replay must
1609
+ // not hand back an accepted resolution for a continuation this runtime cannot
1610
+ // prove was resolved. Without this, a direct `resolveGate()` retry bypassed the
1611
+ // lookup entirely and reproduced the original false success (#5599 review).
1612
+ if (this.#continuationLost.has(response.gate_id) || record.ownerInstanceId !== this.instanceId) {
1613
+ this.hooks.audit?.({ event: "gate_response_replay_unproven", gate_id: response.gate_id });
1614
+ throw new WorkflowGateBrokerError(
1615
+ "unknown_gate",
1616
+ `accepted gate ${response.gate_id} cannot be replayed: this runtime cannot prove its continuation was resolved`,
1617
+ );
1618
+ }
1519
1619
  this.hooks.audit?.({ event: "gate_response_idempotent_replay", gate_id: response.gate_id });
1520
1620
  return record.resolution as WorkflowGateResolution;
1521
1621
  }
@@ -1609,7 +1709,16 @@ export class WorkflowGateBroker {
1609
1709
  `accepted gate ${response.gate_id} lost its continuation before advance completed`,
1610
1710
  );
1611
1711
  this.store.put({ ...latest, advanced: true });
1612
- await this.hooks.completeAccepted?.(this.store.get(response.gate_id) as PersistedGate);
1712
+ // `advanced:true` is already durable, so a throw here cannot be undone — but it
1713
+ // must not be reported as a completed resolution either. Mark the loss before
1714
+ // rethrowing so `lookupCompletedResolution` stops claiming completion for a
1715
+ // continuation that was never resolved (#5599).
1716
+ try {
1717
+ await this.hooks.completeAccepted?.(this.store.get(response.gate_id) as PersistedGate);
1718
+ } catch (error) {
1719
+ this.recordContinuationLoss(response.gate_id);
1720
+ throw error;
1721
+ }
1613
1722
  this.continuations.get(response.gate_id)?.release?.(response.gate_id);
1614
1723
  this.continuations.delete(response.gate_id);
1615
1724
  return resolution;
@@ -1659,7 +1768,15 @@ export class WorkflowGateBroker {
1659
1768
  if (latest?.status !== "accepted" || latest.advanced || !this.hasLiveContinuation(listed.gate.gate_id))
1660
1769
  return;
1661
1770
  this.store.put({ ...latest, advanced: true });
1662
- await this.hooks.completeAccepted?.(this.store.get(latest.gate.gate_id) as PersistedGate);
1771
+ // Same durable-commit-then-resolve ordering as `resolve()`; mark the
1772
+ // loss so a recovery attempt cannot leave behind a record that claims
1773
+ // completion its continuation never received (#5599).
1774
+ try {
1775
+ await this.hooks.completeAccepted?.(this.store.get(latest.gate.gate_id) as PersistedGate);
1776
+ } catch (error) {
1777
+ this.recordContinuationLoss(latest.gate.gate_id);
1778
+ throw error;
1779
+ }
1663
1780
  this.continuations.get(latest.gate.gate_id)?.release?.(latest.gate.gate_id);
1664
1781
  this.continuations.delete(latest.gate.gate_id);
1665
1782
  this.hooks.audit?.({ event: "gate_advance_recovered", gate_id: latest.gate.gate_id });
@@ -375,12 +375,26 @@ export interface WorkflowGateDiagnostic extends WorkflowGate {
375
375
  id: string;
376
376
  tag: "quarantined";
377
377
  lifecycle: WorkflowGateLifecycle;
378
+ /** Whether the durable broker retained an answer for this gate. */
379
+ answer_recorded: boolean;
380
+ }
381
+
382
+ /** Durable acceptance evidence for a gate that is no longer pending. */
383
+ export interface WorkflowGateResolutionDiagnostic extends WorkflowGate {
384
+ id: string;
385
+ tag: "accepted";
386
+ /** True when the accepted record contains the answer body. */
387
+ answer_recorded: true;
388
+ resolved_at: string;
389
+ /** Single post-accept disposition; raw answers and derived answer values stay private. */
390
+ post_accept_disposition: "accepted" | "terminalized" | "advanced" | "continuation_lost" | "unknown";
378
391
  }
379
392
 
380
393
  /** Stable Q12 row preserving the root WorkflowGate shape. */
381
394
  export type WorkflowGateQueryRecord =
382
- | (WorkflowGate & { id: string; tag: "pending"; lifecycle?: undefined })
383
- | WorkflowGateDiagnostic;
395
+ | (WorkflowGate & { id: string; tag: "pending"; lifecycle?: undefined; answer_recorded: false })
396
+ | WorkflowGateDiagnostic
397
+ | WorkflowGateResolutionDiagnostic;
384
398
 
385
399
  /** Inbound: the agent's answer to a workflow gate. */
386
400
  export interface WorkflowGateResponse {
@@ -409,7 +409,7 @@ export interface InteractiveModeContext {
409
409
  handleHandoffCommand(customInstructions?: string): Promise<void>;
410
410
  handleContributionPrepCommand(customInstructions?: string): Promise<void>;
411
411
  handleMoveCommand(targetPath: string): Promise<void>;
412
- handleRenameCommand(title: string): Promise<void>;
412
+ handleRenameCommand(title?: string): Promise<void>;
413
413
  handleMemoryCommand(text: string): Promise<void>;
414
414
  handleSTTToggle(): Promise<void>;
415
415
  executeCompaction(
@@ -490,7 +490,7 @@ export interface InteractiveModeContext {
490
490
  >;
491
491
  goalModeController: Pick<
492
492
  import("./controllers/goal-mode-controller").GoalModeController,
493
- "enabled" | "paused" | "handleCommand"
493
+ "enabled" | "paused" | "handleCommand" | "cancelContinuation" | "scheduleContinuation"
494
494
  >;
495
495
  setHookWidget(key: string, content: ExtensionWidgetContent, options?: ExtensionWidgetOptions): void;
496
496
  setHookStatus(key: string, text: string | undefined): void;
@@ -5,6 +5,7 @@ Routing:
5
5
  - Every task that requires a rendered page, authenticated/private browser state, live tabs, browser UI, screenshots, downloads behind cookies, history, saved credentials, or profile data MUST use Bash to invoke the installed Aside CLI.
6
6
  - Default to `aside repl '<JavaScript>'` for deterministic Playwright inspection and actions, even when the answer is unknown but the procedure is discoverable from `snapshot(page, { interactive: true })`.
7
7
  - Use `aside exec '<prompt>'` only for runtime judgment, source-heavy research, browser-history search, MFA or opaque password autofill, approval/notification waits, long monitoring, multi-site synthesis, routines, or work worth a second model. Continue with `aside exec --session <id> '<follow-up>'`. Use `--account`, `--model`, `--provider`, `--speed`, or `--effort` only when the task requires overriding user defaults.
8
+ - Declare every Aside invocation on the Bash call itself with the structured `activity` argument: `{"kind":"browser","provider":"aside","mode":"repl"}` for `aside repl`, and `{"kind":"browser","provider":"aside","mode":"exec"}` for `aside exec` including `--session <id>` follow-ups. The declaration names the operation, not the command spelling: send it unchanged across wrappers, env prefixes, absolute CLI paths, quoting, and multiline commands, and omit it on Bash calls that do not invoke Aside.
8
9
  - Cookie-free public HTTP content that does not require rendered or authenticated browser state may use GJC `read` directly.
9
10
 
10
11
  Deterministic procedure:
@@ -1,2 +1,2 @@
1
- Generate a 3-6 word title for a coding session from the user's first message. Capture the main task or topic.
1
+ Generate a 3-6 word title for a coding session from the supplied user message or conversation digest. If multiple messages are supplied, identify the primary/current task from the digest, weighting the newest substantive message highest; do not assume the first message is the task. If one message is supplied, capture its main task or topic.
2
2
  Output ONLY the title. No quotes or trailing punctuation.
@@ -17,7 +17,7 @@ Executes bash command in shell session for terminal operations like git, bun, ca
17
17
  - Use `async: true` for long-running commands when you don't need immediate output; the call returns a background job ID and the result is delivered automatically as a follow-up.
18
18
  {{/if}}
19
19
  {{#if foregroundFoldEnabled}}
20
- - In the interactive TUI, the user can press the fold chord twice (default `Alt+Shift+B`, plus `Cmd+B` on macOS; remappable via the `app.tool.backgroundFold` keybinding) while a foreground command is still running to fold it into a background job. All three command surfaces fold: managed non-PTY bash, editor/ACP client-terminal commands, and PTY-mode commands. A folded command keeps its original timeout and is never killed or restarted by folding; a folded PTY continues output-only, so it stops accepting input. The turn then ends cleanly and a later turn resumes automatically with the command's result, so the interrupted task is finished rather than abandoned. `task`/`subagent` waits are deliberately NOT foldable. Do not instruct users to use raw shell `Ctrl+Z`/`bg` inside the GJC TUI; ownership and output routing are not safe there.
20
+ - In the interactive TUI, the user can press the fold chord twice (default `Alt+Shift+B`, plus `Cmd+B` on macOS; remappable via the `app.tool.backgroundFold` keybinding) while a foreground command is still running to fold it into a background job. All three command surfaces fold: managed non-PTY bash, editor/ACP client-terminal commands, and PTY-mode commands. A folded command keeps its original timeout and is never killed or restarted by folding; a folded PTY continues output-only, so it stops accepting input. The turn then ends cleanly and a later turn resumes automatically with the command's result, so the interrupted task is finished rather than abandoned. The `job` tool's own await uses the same steer-fold behavior and grace window; `task`/`subagent` waits are deliberately NOT foldable. Do not instruct users to use raw shell `Ctrl+Z`/`bg` inside the GJC TUI; ownership and output routing are not safe there.
21
21
  - A user steer folds the same way: when the user sends a message while a foreground command has already run for at least 2 seconds, the command is folded into a background job and this call returns the background-start result plus the line `Folded into background job <id> because a user steer arrived; the command keeps running with its original timeout and its result will wake a later turn.` The steer is then delivered at this tool boundary, so handle it next; do not re-run the folded command. Steers that arrive within the first 2 seconds let the command finish normally. This applies only while `busyPromptMode` is `steer`; `toolInterruptPolicy` is not a gate, since a fold never kills the command.
22
22
  {{/if}}
23
23
  </instruction>
@@ -2,7 +2,7 @@ Inspects, waits, or cancels async jobs.
2
2
 
3
3
  Background job results are delivered automatically when complete. Jobs that back task subagents should be controlled via the `subagent` tool when it is available; use `job` for non-subagent jobs (async bash, monitors) and as a compatibility fallback. Running job output stays quiet by default to avoid flooding the conversation; use `tail` when you explicitly want to show/reopen retained output. Reach for this tool only when you need to inspect or intervene.
4
4
 
5
- In the interactive TUI, a running foreground command can be folded into a background job by pressing the fold chord twice (default `Alt+Shift+B`, plus `Cmd+B` on macOS; remappable via `app.tool.backgroundFold`), or automatically when the user sends a steer after the command has run for at least 2 seconds (with `busyPromptMode=steer` (`toolInterruptPolicy` is not a gate)). This covers managed non-PTY bash, editor/ACP client-terminal commands, and PTY-mode commands. A folded job keeps its original deadline, is never killed or restarted by folding, and its result wakes a later turn automatically; a folded PTY continues output-only and no longer accepts input. Job listings expose `foldReason` (`steer`, `chord`, `timer`, or `sdk_control`) beside `backgrounded` so you can tell why a job left the foreground. `task`/`subagent` waits are not foldable. Raw shell `Ctrl+Z`/`bg` is not the supported path inside GJC because it bypasses job ownership and output-routing contracts.
5
+ In the interactive TUI, a running foreground command can be folded into a background job by pressing the fold chord twice (default `Alt+Shift+B`, plus `Cmd+B` on macOS; remappable via `app.tool.backgroundFold`), or automatically when the user sends a steer after the command has run for at least 2 seconds (with `busyPromptMode=steer` (`toolInterruptPolicy` is not a gate)). This covers managed non-PTY bash, editor/ACP client-terminal commands, and PTY-mode commands. A folded job keeps its original deadline, is never killed or restarted by folding, and its result wakes a later turn automatically; a folded PTY continues output-only and no longer accepts input. A `job` await also folds after the same 2-second grace window when a steer arrives, returning immediately with the still-running job id(s) while those jobs keep their original deadlines and wake a later turn with their results. Job listings expose `foldReason` (`steer`, `chord`, `timer`, or `sdk_control`) beside `backgrounded` so you can tell why a job left the foreground. `task`/`subagent` waits are not foldable. Raw shell `Ctrl+Z`/`bg` is not the supported path inside GJC because it bypasses job ownership and output-routing contracts.
6
6
 
7
7
  # Operations
8
8
 
@@ -73,6 +73,8 @@ For `.sqlite`, `.sqlite3`, `.db`, `.db3`:
73
73
 
74
74
  `agent://<id>`, `artifact://<id>`, `rule://<name>`, and `local://<name>.md` resolve transparently and accept the same line selectors as filesystem paths. Use `artifact://<id>` to recover full output that a previous bash/eval/tool result spilled or truncated.
75
75
 
76
+ Bundled skills have no filesystem home, so the skill tool and skill discovery report them as `embedded:gjc/skills/<name>/SKILL.md`; read that identifier verbatim. Bundled skill fragments, when surfaced, likewise use `embedded:gjc/skill-fragments/...` identifiers.
77
+
76
78
  <critical>
77
79
  - Always include `path`; never call `read` with `{}`.
78
80
  - For line ranges, append the selector to `path`.
@@ -42,7 +42,42 @@ describe("network prewarm runtime service", () => {
42
42
  const runtime = await service.get("legacy-startup");
43
43
  runtime.preconnect("https://example.test");
44
44
 
45
- expect(calls).toEqual(["https://example.test"]);
45
+ expect(calls).toEqual(["https://example.test:443/"]);
46
+ await service.dispose();
47
+ });
48
+
49
+ test("normalizes a portless HTTPS model host before preconnect", async () => {
50
+ const calls: string[] = [];
51
+ const fetchWithPreconnect = Object.assign(async () => new Response("ok"), {
52
+ preconnect: (url: string) => {
53
+ calls.push(url);
54
+ },
55
+ }) as typeof fetch & { preconnect: (url: string) => void };
56
+ globalThis.fetch = fetchWithPreconnect;
57
+
58
+ const service = createNetworkPrewarmService(Settings.isolated());
59
+ const runtime = await service.get("portless-https");
60
+ runtime.preconnect("https://api.anthropic.com/v1/messages");
61
+
62
+ expect(calls).toEqual(["https://api.anthropic.com:443/v1/messages"]);
63
+ await service.dispose();
64
+ });
65
+
66
+ test("falls back to Bun's accepted TCP form when default-port HTTPS is rejected", async () => {
67
+ const calls: string[] = [];
68
+ const fetchWithPreconnect = Object.assign(async () => new Response("ok"), {
69
+ preconnect: (url: string) => {
70
+ calls.push(url);
71
+ if (url.startsWith("https://")) throw new TypeError("Invalid port");
72
+ },
73
+ }) as typeof fetch & { preconnect: (url: string) => void };
74
+ globalThis.fetch = fetchWithPreconnect;
75
+
76
+ const service = createNetworkPrewarmService(Settings.isolated());
77
+ const runtime = await service.get("bun-1.4.0");
78
+ runtime.preconnect("https://api.anthropic.com");
79
+
80
+ expect(calls).toEqual(["https://api.anthropic.com:443/", "http://api.anthropic.com:443/"]);
46
81
  await service.dispose();
47
82
  });
48
83
  });
@@ -4,6 +4,24 @@ import { createLazyService, type LazyService } from "./lazy-service";
4
4
 
5
5
  type FetchWithPreconnect = typeof fetch & { preconnect?: (url: string) => void };
6
6
 
7
+ function withDefaultPort(baseUrl: string): { normalized: string; fallback?: string } {
8
+ try {
9
+ const parsed = new URL(baseUrl);
10
+ if (parsed.port || parsed.protocol !== "https:") return { normalized: baseUrl };
11
+ const port = "443";
12
+ const path = `${parsed.pathname}${parsed.search}${parsed.hash}`;
13
+ const authority = parsed.host;
14
+ const normalized = `${parsed.protocol}//${authority}:${port}${path}`;
15
+ // Bun 1.4.0 rejects the default port after WHATWG URL normalization. Its
16
+ // HTTP-form URL still reaches the same port and keeps the TCP warmup alive
17
+ // until the runtime accepts the canonical HTTPS form.
18
+ const fallback = parsed.protocol === "https:" ? `http://${authority}:${port}${path}` : undefined;
19
+ return { normalized, fallback };
20
+ } catch {
21
+ return { normalized: baseUrl };
22
+ }
23
+ }
24
+
7
25
  /** Runtime network prewarm and first-request latency diagnostics. */
8
26
  export interface NetworkPrewarmRuntime {
9
27
  enabled: boolean;
@@ -30,11 +48,18 @@ export function createNetworkPrewarmService(settings: Settings): LazyService<Net
30
48
  if (!enabled || !baseUrl) return;
31
49
  const preconnect = (globalThis.fetch as FetchWithPreconnect).preconnect;
32
50
  if (typeof preconnect !== "function") return;
51
+ const { normalized, fallback } = withDefaultPort(baseUrl);
33
52
  try {
34
- preconnect(baseUrl);
53
+ preconnect(normalized);
35
54
  } catch (error) {
36
- // Preserve the legacy best-effort optimization contract while keeping
37
- // the diagnostic visible to debug logging.
55
+ if (fallback) {
56
+ try {
57
+ preconnect(fallback);
58
+ return;
59
+ } catch {
60
+ // Fall through to the best-effort diagnostic below.
61
+ }
62
+ }
38
63
  logger.debug("Model-host preconnect failed", {
39
64
  baseUrl,
40
65
  error: error instanceof Error ? error.message : String(error),
@@ -436,6 +436,7 @@ type ResourceDisposer = () => void | Promise<void>;
436
436
 
437
437
  const resourceOwners = new Map<string, ResourceDisposer>();
438
438
  let resourcePostmortemRegistered = false;
439
+ let activeResourceOwnerDisposals = 0;
439
440
 
440
441
  function ensureResourcePostmortem(): void {
441
442
  if (resourcePostmortemRegistered) return;
@@ -477,23 +478,33 @@ export function resourceOwnerCount(): number {
477
478
  return resourceOwners.size;
478
479
  }
479
480
 
481
+ /** True while a process-level resource-owner sweep is in progress. */
482
+ export function isResourceOwnerDisposalActive(): boolean {
483
+ return activeResourceOwnerDisposals > 0;
484
+ }
485
+
480
486
  /**
481
487
  * Run and clear every registered resource disposer. Attempts all disposers even
482
488
  * if some throw, then surfaces the failures as an `AggregateError` so callers
483
489
  * can distinguish "all closed" from "a resource may still be alive".
484
490
  */
485
491
  export async function disposeAllResourceOwners(): Promise<void> {
486
- const disposers = [...resourceOwners.values()];
487
- resourceOwners.clear();
488
- const errors: unknown[] = [];
489
- for (const disposer of disposers) {
490
- try {
491
- await disposer();
492
- } catch (err) {
493
- errors.push(err);
492
+ activeResourceOwnerDisposals += 1;
493
+ try {
494
+ const disposers = [...resourceOwners.values()];
495
+ resourceOwners.clear();
496
+ const errors: unknown[] = [];
497
+ for (const disposer of disposers) {
498
+ try {
499
+ await disposer();
500
+ } catch (err) {
501
+ errors.push(err);
502
+ }
494
503
  }
495
- }
496
- if (errors.length > 0) {
497
- throw new AggregateError(errors, `${errors.length} resource disposer(s) failed during teardown`);
504
+ if (errors.length > 0) {
505
+ throw new AggregateError(errors, `${errors.length} resource disposer(s) failed during teardown`);
506
+ }
507
+ } finally {
508
+ activeResourceOwnerDisposals -= 1;
498
509
  }
499
510
  }
@@ -4,7 +4,7 @@
4
4
  * Uses the capability system to load MCP servers from multiple sources.
5
5
  */
6
6
 
7
- import { getMCPConfigPath } from "@gajae-code/utils";
7
+ import { getMCPConfigPath, logger } from "@gajae-code/utils";
8
8
  import { mcpCapability } from "../capability/mcp";
9
9
  import type { SourceMeta } from "../capability/types";
10
10
  import type { Settings } from "../config/settings";
@@ -16,6 +16,14 @@ import { canonicalizeMCPEndpoint } from "./pool-key";
16
16
  import { isMCPProtocolPreference } from "./protocol";
17
17
  import type { MCPServerConfig } from "./types";
18
18
 
19
+ function logSkippedServer(name: string, reason: string): void {
20
+ logger.warn("Skipping MCP autoload registration", {
21
+ path: `mcp:${name}`,
22
+ serverName: name,
23
+ reason,
24
+ });
25
+ }
26
+
19
27
  /** Options for loading MCP configs */
20
28
  export interface LoadMCPConfigsOptions {
21
29
  /** Owning session settings for capability filtering. */
@@ -174,9 +182,16 @@ export async function loadAllMCPConfigs(cwd: string, options?: LoadMCPConfigsOpt
174
182
  for (const server of servers) {
175
183
  const config = convertToLegacyConfig(server);
176
184
  if (config.enabled === false || disabledServers.has(server.name)) {
185
+ if (!exactConfig) {
186
+ logSkippedServer(
187
+ server.name,
188
+ config.enabled === false ? "registration is disabled" : "server is listed in disabledServers",
189
+ );
190
+ }
177
191
  continue;
178
192
  }
179
193
  if (autoloadOnly && config.autoload === false) {
194
+ if (!exactConfig) logSkippedServer(server.name, "autoload is disabled for this registration");
180
195
  continue;
181
196
  }
182
197
  configs[server.name] = config;