@gajae-code/coding-agent 0.17.1 → 0.17.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (375) hide show
  1. package/CHANGELOG.md +266 -0
  2. package/dist/types/async/job-manager.d.ts +7 -6
  3. package/dist/types/cli/daemon-cli.d.ts +3 -1
  4. package/dist/types/cli/github-release.d.ts +17 -0
  5. package/dist/types/cli/public-command-entry.d.ts +56 -0
  6. package/dist/types/cli/public-command-errors.d.ts +126 -0
  7. package/dist/types/cli/public-command-evidence.d.ts +99 -0
  8. package/dist/types/cli/public-command-help.d.ts +65 -0
  9. package/dist/types/cli/public-command-registry.d.ts +69 -0
  10. package/dist/types/cli/session-picker.d.ts +1 -1
  11. package/dist/types/cli/setup-cli.d.ts +1 -0
  12. package/dist/types/cli/skills-cli.d.ts +5 -0
  13. package/dist/types/cli.d.ts +3 -0
  14. package/dist/types/commands/daemon.d.ts +4 -50
  15. package/dist/types/commands/sdk.d.ts +20 -37
  16. package/dist/types/commands/setup.d.ts +3 -0
  17. package/dist/types/commands/skills.d.ts +9 -0
  18. package/dist/types/config/autorouting-tier-map.d.ts +24 -0
  19. package/dist/types/config/file-lock.d.ts +10 -1
  20. package/dist/types/config/model-profile-activation.d.ts +63 -4
  21. package/dist/types/config/model-registry.d.ts +20 -2
  22. package/dist/types/config/settings-schema.d.ts +11 -0
  23. package/dist/types/coordinator-mcp/question-state.d.ts +6 -1
  24. package/dist/types/coordinator-mcp/server.d.ts +3 -0
  25. package/dist/types/daemon/operator-contract.d.ts +5 -1
  26. package/dist/types/eval/js/worker-protocol.d.ts +2 -0
  27. package/dist/types/extensibility/extensions/types.d.ts +12 -0
  28. package/dist/types/extensibility/gjc-plugins/registry.d.ts +4 -0
  29. package/dist/types/extensibility/runtime-skill-discovery.d.ts +18 -0
  30. package/dist/types/extensibility/shared-events.d.ts +3 -1
  31. package/dist/types/gjc-runtime/session-state-lock.d.ts +61 -0
  32. package/dist/types/internal-urls/embedded-protocol.d.ts +9 -0
  33. package/dist/types/internal-urls/index.d.ts +1 -0
  34. package/dist/types/internal-urls/parse.d.ts +9 -1
  35. package/dist/types/main.d.ts +22 -0
  36. package/dist/types/modes/acp/acp-agent.d.ts +94 -1
  37. package/dist/types/modes/acp/startup-provenance.d.ts +16 -0
  38. package/dist/types/modes/components/custom-provider-wizard.d.ts +19 -1
  39. package/dist/types/modes/components/session-selector.d.ts +5 -2
  40. package/dist/types/modes/components/thinking-selector.d.ts +2 -1
  41. package/dist/types/modes/components/user-message-selector.d.ts +5 -2
  42. package/dist/types/modes/controllers/command-controller.d.ts +1 -1
  43. package/dist/types/modes/controllers/selector-controller.d.ts +1 -1
  44. package/dist/types/modes/interactive-mode.d.ts +1 -1
  45. package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +11 -1
  46. package/dist/types/modes/shared/agent-wire/workflow-gate-types.d.ts +14 -1
  47. package/dist/types/modes/types.d.ts +2 -2
  48. package/dist/types/runtime/process-lifecycle.d.ts +2 -0
  49. package/dist/types/runtime-mcp/manager.d.ts +56 -0
  50. package/dist/types/runtime-mcp/pool.d.ts +17 -0
  51. package/dist/types/runtime-mcp/redaction.d.ts +20 -0
  52. package/dist/types/runtime-mcp/startup-policy.d.ts +6 -0
  53. package/dist/types/runtime-mcp/tool-bridge.d.ts +9 -0
  54. package/dist/types/runtime-mcp/transports/http.d.ts +11 -0
  55. package/dist/types/sdk/acp/adapter.d.ts +27 -0
  56. package/dist/types/sdk/acp/final-text.d.ts +8 -0
  57. package/dist/types/sdk/broker/broker.d.ts +13 -2
  58. package/dist/types/sdk/broker/ensure.d.ts +46 -1
  59. package/dist/types/sdk/broker/identity.d.ts +3 -1
  60. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +23 -3
  61. package/dist/types/sdk/broker/lifecycle.d.ts +15 -8
  62. package/dist/types/sdk/broker/runtime.d.ts +1 -1
  63. package/dist/types/sdk/broker/session-index.d.ts +22 -3
  64. package/dist/types/sdk/broker/spawn-authority.d.ts +4 -0
  65. package/dist/types/sdk/broker/stderr-drainer.d.ts +13 -0
  66. package/dist/types/sdk/bus/index.d.ts +19 -0
  67. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +3 -1
  68. package/dist/types/sdk/bus/telegram-daemon.d.ts +5 -0
  69. package/dist/types/sdk/cli/master-cli.d.ts +6 -10
  70. package/dist/types/sdk/cli/rows.d.ts +19 -1
  71. package/dist/types/sdk/cli/session-cli.d.ts +22 -5
  72. package/dist/types/sdk/client/client.d.ts +2 -0
  73. package/dist/types/sdk/guides/cli.d.ts +3 -4
  74. package/dist/types/sdk/guides/verify.d.ts +2 -0
  75. package/dist/types/sdk/host/host.d.ts +9 -0
  76. package/dist/types/sdk/host/session-runtime.d.ts +31 -0
  77. package/dist/types/sdk/host/types.d.ts +10 -0
  78. package/dist/types/sdk/index.d.ts +1 -0
  79. package/dist/types/sdk/prompt-deadline-flush.d.ts +83 -0
  80. package/dist/types/sdk/prompt-deadline-manager.d.ts +38 -1
  81. package/dist/types/sdk/prompt-failure.d.ts +30 -1
  82. package/dist/types/sdk/prompt-status.d.ts +8 -0
  83. package/dist/types/sdk/prompt-tool-boundary.d.ts +73 -0
  84. package/dist/types/sdk/router/replay-retention-gap-cache.d.ts +13 -0
  85. package/dist/types/sdk/router/session-router.d.ts +2 -0
  86. package/dist/types/sdk/startup-capability.d.ts +2 -0
  87. package/dist/types/sdk/transport/relay.d.ts +1 -0
  88. package/dist/types/sdk/transport/serve-cli.d.ts +73 -2
  89. package/dist/types/sdk/transport/stdio.d.ts +5 -1
  90. package/dist/types/session/agent-session.d.ts +168 -21
  91. package/dist/types/session/fallback-chain-controller.d.ts +6 -6
  92. package/dist/types/session/fold-coordinator.d.ts +17 -3
  93. package/dist/types/session/internal/managed-session-storage.d.ts +7 -1
  94. package/dist/types/session/session-manager.d.ts +64 -1
  95. package/dist/types/session/session-work-lease.d.ts +14 -0
  96. package/dist/types/setup/model-onboarding-guidance.d.ts +1 -1
  97. package/dist/types/setup/provider-onboarding.d.ts +81 -1
  98. package/dist/types/thinking.d.ts +3 -1
  99. package/dist/types/tools/bash.d.ts +37 -11
  100. package/dist/types/tools/job.d.ts +1 -1
  101. package/dist/types/tools/steer-fold.d.ts +31 -0
  102. package/dist/types/utils/git.d.ts +28 -2
  103. package/dist/types/utils/mupdf-embedded.d.ts +2 -0
  104. package/dist/types/utils/mupdf-wasm-embedded.d.ts +2 -0
  105. package/dist/types/utils/mupdf.d.ts +4 -0
  106. package/dist/types/utils/session-title-generation.d.ts +6 -0
  107. package/dist/types/utils/title-generator.d.ts +11 -2
  108. package/dist/types/web/scrapers/types.d.ts +3 -0
  109. package/package.json +18 -10
  110. package/scripts/build-binary.ts +9 -3
  111. package/scripts/compile-args.ts +0 -9
  112. package/scripts/embed-mupdf.ts +52 -0
  113. package/scripts/generate-sdk-operation-inventory.ts +31 -1
  114. package/scripts/vendor-markit.ts +196 -0
  115. package/src/async/job-manager.ts +60 -9
  116. package/src/cli/daemon-cli.ts +53 -18
  117. package/src/cli/github-release.ts +87 -5
  118. package/src/cli/mcp-cli.ts +38 -4
  119. package/src/cli/public-command-entry.ts +450 -0
  120. package/src/cli/public-command-errors.ts +581 -0
  121. package/src/cli/public-command-evidence.ts +717 -0
  122. package/src/cli/public-command-help.ts +360 -0
  123. package/src/cli/public-command-registry.ts +535 -0
  124. package/src/cli/session-picker.ts +2 -1
  125. package/src/cli/setup-cli.ts +9 -1
  126. package/src/cli/skills-cli.ts +46 -2
  127. package/src/cli.ts +61 -1
  128. package/src/commands/daemon.ts +65 -69
  129. package/src/commands/sdk.ts +232 -100
  130. package/src/commands/setup.ts +2 -0
  131. package/src/commands/skills.ts +11 -0
  132. package/src/config/autorouting-tier-map.ts +16 -0
  133. package/src/config/file-lock.ts +199 -68
  134. package/src/config/model-profile-activation.ts +406 -53
  135. package/src/config/model-registry.ts +204 -25
  136. package/src/config/settings-schema.ts +26 -2
  137. package/src/coordinator-mcp/policy.ts +10 -5
  138. package/src/coordinator-mcp/question-gate-codec.ts +17 -0
  139. package/src/coordinator-mcp/question-state.ts +175 -8
  140. package/src/coordinator-mcp/server.ts +528 -87
  141. package/src/coordinator-mcp/session-reaper.ts +6 -4
  142. package/src/crash/record-loader.ts +6 -2
  143. package/src/daemon/operator-contract.ts +26 -12
  144. package/src/debug/report-bundle.ts +114 -24
  145. package/src/defaults/gjc/skills/ultragoal/SKILL.md +1 -1
  146. package/src/eval/js/context-manager.ts +8 -8
  147. package/src/eval/js/worker-core.ts +9 -4
  148. package/src/eval/js/worker-protocol.ts +2 -0
  149. package/src/extensibility/extensions/runner.ts +52 -4
  150. package/src/extensibility/extensions/types.ts +10 -0
  151. package/src/extensibility/gjc-plugins/registry.ts +196 -37
  152. package/src/extensibility/runtime-skill-discovery.ts +61 -3
  153. package/src/extensibility/shared-events.ts +3 -1
  154. package/src/gjc-runtime/managed-owner-admission.ts +67 -50
  155. package/src/gjc-runtime/session-state-lock.ts +165 -24
  156. package/src/gjc-runtime/ultragoal-succession.ts +31 -3
  157. package/src/internal-urls/docs-index.generated.ts +21 -19
  158. package/src/internal-urls/embedded-protocol.ts +83 -0
  159. package/src/internal-urls/index.ts +1 -0
  160. package/src/internal-urls/parse.ts +21 -1
  161. package/src/internal-urls/router.ts +4 -2
  162. package/src/main.ts +123 -55
  163. package/src/modes/acp/acp-agent.ts +593 -47
  164. package/src/modes/acp/acp-event-mapper.ts +9 -0
  165. package/src/modes/acp/startup-provenance.ts +59 -0
  166. package/src/modes/components/custom-provider-wizard.ts +308 -7
  167. package/src/modes/components/model-selector.ts +31 -15
  168. package/src/modes/components/oauth-selector.ts +9 -0
  169. package/src/modes/components/session-selector.ts +47 -4
  170. package/src/modes/components/status-line/segments.ts +6 -1
  171. package/src/modes/components/thinking-selector.ts +7 -3
  172. package/src/modes/components/user-message-selector.ts +57 -15
  173. package/src/modes/controllers/command-controller.ts +58 -32
  174. package/src/modes/controllers/extension-ui-controller.ts +33 -7
  175. package/src/modes/controllers/input-controller.ts +4 -0
  176. package/src/modes/controllers/selector-controller.ts +177 -18
  177. package/src/modes/interactive-mode.ts +21 -7
  178. package/src/modes/runtime-init.ts +1 -0
  179. package/src/modes/shared/agent-wire/workflow-gate-broker.ts +124 -7
  180. package/src/modes/shared/agent-wire/workflow-gate-types.ts +16 -2
  181. package/src/modes/types.ts +2 -2
  182. package/src/prompts/agent-fragments/browser-aside.md +1 -0
  183. package/src/prompts/system/title-system.md +1 -1
  184. package/src/prompts/tools/bash.md +1 -1
  185. package/src/prompts/tools/job.md +1 -1
  186. package/src/prompts/tools/read.md +2 -0
  187. package/src/runtime/network-prewarm-service.test.ts +36 -1
  188. package/src/runtime/network-prewarm-service.ts +28 -3
  189. package/src/runtime/process-lifecycle.ts +22 -11
  190. package/src/runtime-mcp/config.ts +16 -1
  191. package/src/runtime-mcp/manager.ts +689 -55
  192. package/src/runtime-mcp/pool.test.ts +31 -0
  193. package/src/runtime-mcp/pool.ts +304 -28
  194. package/src/runtime-mcp/redaction.ts +42 -0
  195. package/src/runtime-mcp/startup-policy.ts +7 -0
  196. package/src/runtime-mcp/tool-bridge.ts +71 -0
  197. package/src/runtime-mcp/transports/http.ts +108 -25
  198. package/src/sdk/acp/adapter.ts +90 -9
  199. package/src/sdk/acp/final-text.ts +19 -2
  200. package/src/sdk/broker/broker.ts +378 -52
  201. package/src/sdk/broker/ensure.ts +97 -19
  202. package/src/sdk/broker/identity.ts +9 -1
  203. package/src/sdk/broker/lifecycle-ledger.ts +95 -31
  204. package/src/sdk/broker/lifecycle.ts +690 -141
  205. package/src/sdk/broker/runtime.ts +1 -1
  206. package/src/sdk/broker/session-index.ts +97 -4
  207. package/src/sdk/broker/spawn-authority.ts +35 -7
  208. package/src/sdk/broker/stderr-drainer.ts +59 -0
  209. package/src/sdk/bus/chat-daemon-control.ts +2 -2
  210. package/src/sdk/bus/chat-daemon-runtime.ts +1 -0
  211. package/src/sdk/bus/index.ts +721 -84
  212. package/src/sdk/bus/telegram-daemon-cli.ts +31 -2
  213. package/src/sdk/bus/telegram-daemon-contract.ts +3 -1
  214. package/src/sdk/bus/telegram-daemon.ts +10 -3
  215. package/src/sdk/cli/master-cli.ts +47 -36
  216. package/src/sdk/cli/rows.ts +30 -7
  217. package/src/sdk/cli/session-cli.ts +687 -184
  218. package/src/sdk/client/client.ts +18 -0
  219. package/src/sdk/guides/catalog.ts +2 -2
  220. package/src/sdk/guides/cli.ts +20 -62
  221. package/src/sdk/guides/verify.ts +14 -0
  222. package/src/sdk/host/host.ts +49 -5
  223. package/src/sdk/host/session-runtime.test.ts +636 -9
  224. package/src/sdk/host/session-runtime.ts +499 -118
  225. package/src/sdk/host/types.ts +12 -1
  226. package/src/sdk/host/websocket-transport.lifecycle.test.ts +84 -0
  227. package/src/sdk/host/websocket-transport.ts +40 -6
  228. package/src/sdk/index.ts +11 -0
  229. package/src/sdk/prompt-deadline-flush.ts +334 -0
  230. package/src/sdk/prompt-deadline-manager.ts +136 -4
  231. package/src/sdk/prompt-failure.ts +43 -1
  232. package/src/sdk/prompt-status.ts +8 -0
  233. package/src/sdk/prompt-tool-boundary.ts +121 -0
  234. package/src/sdk/protocol/operation-inventory.generated.json +106 -0
  235. package/src/sdk/router/replay-retention-gap-cache.ts +27 -0
  236. package/src/sdk/router/session-router.ts +31 -4
  237. package/src/sdk/session.ts +194 -16
  238. package/src/sdk/startup-capability.ts +1 -1
  239. package/src/sdk/transport/relay.ts +71 -6
  240. package/src/sdk/transport/serve-cli.ts +494 -49
  241. package/src/sdk/transport/socket.ts +12 -11
  242. package/src/sdk/transport/stdio.ts +7 -3
  243. package/src/session/agent-session.ts +2437 -400
  244. package/src/session/fallback-chain-controller.ts +6 -7
  245. package/src/session/fold-coordinator.ts +24 -5
  246. package/src/session/internal/managed-session-scope.ts +58 -18
  247. package/src/session/internal/managed-session-storage.ts +304 -11
  248. package/src/session/session-manager.ts +382 -44
  249. package/src/session/session-work-lease.ts +34 -0
  250. package/src/session-import/codex.ts +12 -3
  251. package/src/setup/model-onboarding-guidance.ts +1 -1
  252. package/src/setup/provider-onboarding.ts +397 -24
  253. package/src/slash-commands/builtin-registry.ts +258 -14
  254. package/src/task/index.ts +14 -2
  255. package/src/thinking.ts +21 -2
  256. package/src/tools/bash.ts +98 -78
  257. package/src/tools/fetch.ts +119 -20
  258. package/src/tools/job.ts +151 -25
  259. package/src/tools/output-meta.ts +15 -2
  260. package/src/tools/path-utils.ts +27 -3
  261. package/src/tools/read.ts +3 -3
  262. package/src/tools/steer-fold.ts +107 -0
  263. package/src/tools/tool-catalog.generated.ts +29 -2
  264. package/src/tools/tool-errors.ts +4 -0
  265. package/src/utils/git.ts +53 -3
  266. package/src/utils/markit.ts +55 -41
  267. package/src/utils/mupdf-assets.d.ts +4 -0
  268. package/src/utils/mupdf-embedded.ts +4 -0
  269. package/src/utils/mupdf-wasm-embedded.ts +3 -0
  270. package/src/utils/mupdf-wasm.ts +24 -2
  271. package/src/utils/mupdf.ts +129 -0
  272. package/src/utils/session-title-generation.ts +29 -0
  273. package/src/utils/title-generator.ts +46 -2
  274. package/src/web/scrapers/types.ts +24 -3
  275. package/vendor/markit-ai/LICENSE +21 -0
  276. package/vendor/markit-ai/MANIFEST.json +412 -0
  277. package/vendor/markit-ai/dist/commands/config.d.ts +4 -0
  278. package/vendor/markit-ai/dist/commands/config.js +139 -0
  279. package/vendor/markit-ai/dist/commands/convert.d.ts +6 -0
  280. package/vendor/markit-ai/dist/commands/convert.js +113 -0
  281. package/vendor/markit-ai/dist/commands/formats.d.ts +2 -0
  282. package/vendor/markit-ai/dist/commands/formats.js +92 -0
  283. package/vendor/markit-ai/dist/commands/init.d.ts +2 -0
  284. package/vendor/markit-ai/dist/commands/init.js +29 -0
  285. package/vendor/markit-ai/dist/commands/onboard.d.ts +2 -0
  286. package/vendor/markit-ai/dist/commands/onboard.js +61 -0
  287. package/vendor/markit-ai/dist/commands/plugin.d.ts +4 -0
  288. package/vendor/markit-ai/dist/commands/plugin.js +58 -0
  289. package/vendor/markit-ai/dist/config.d.ts +26 -0
  290. package/vendor/markit-ai/dist/config.js +42 -0
  291. package/vendor/markit-ai/dist/converters/audio.d.ts +7 -0
  292. package/vendor/markit-ai/dist/converters/audio.js +104 -0
  293. package/vendor/markit-ai/dist/converters/csv.d.ts +7 -0
  294. package/vendor/markit-ai/dist/converters/csv.js +83 -0
  295. package/vendor/markit-ai/dist/converters/docx.d.ts +6 -0
  296. package/vendor/markit-ai/dist/converters/docx.js +57 -0
  297. package/vendor/markit-ai/dist/converters/epub.d.ts +8 -0
  298. package/vendor/markit-ai/dist/converters/epub.js +128 -0
  299. package/vendor/markit-ai/dist/converters/github.d.ts +18 -0
  300. package/vendor/markit-ai/dist/converters/github.js +148 -0
  301. package/vendor/markit-ai/dist/converters/html.d.ts +6 -0
  302. package/vendor/markit-ai/dist/converters/html.js +30 -0
  303. package/vendor/markit-ai/dist/converters/image.d.ts +6 -0
  304. package/vendor/markit-ai/dist/converters/image.js +124 -0
  305. package/vendor/markit-ai/dist/converters/ipynb.d.ts +6 -0
  306. package/vendor/markit-ai/dist/converters/ipynb.js +75 -0
  307. package/vendor/markit-ai/dist/converters/iwork.d.ts +20 -0
  308. package/vendor/markit-ai/dist/converters/iwork.js +391 -0
  309. package/vendor/markit-ai/dist/converters/json.d.ts +6 -0
  310. package/vendor/markit-ai/dist/converters/json.js +21 -0
  311. package/vendor/markit-ai/dist/converters/pdf/columns.d.ts +35 -0
  312. package/vendor/markit-ai/dist/converters/pdf/columns.js +93 -0
  313. package/vendor/markit-ai/dist/converters/pdf/extract.d.ts +19 -0
  314. package/vendor/markit-ai/dist/converters/pdf/extract.js +514 -0
  315. package/vendor/markit-ai/dist/converters/pdf/grid.d.ts +25 -0
  316. package/vendor/markit-ai/dist/converters/pdf/grid.js +795 -0
  317. package/vendor/markit-ai/dist/converters/pdf/headers.d.ts +24 -0
  318. package/vendor/markit-ai/dist/converters/pdf/headers.js +108 -0
  319. package/vendor/markit-ai/dist/converters/pdf/index.d.ts +19 -0
  320. package/vendor/markit-ai/dist/converters/pdf/index.js +136 -0
  321. package/vendor/markit-ai/dist/converters/pdf/mupdf-loader.js +9 -0
  322. package/vendor/markit-ai/dist/converters/pdf/render.d.ts +24 -0
  323. package/vendor/markit-ai/dist/converters/pdf/render.js +513 -0
  324. package/vendor/markit-ai/dist/converters/pdf/types.d.ts +75 -0
  325. package/vendor/markit-ai/dist/converters/pdf/types.js +1 -0
  326. package/vendor/markit-ai/dist/converters/plain-text.d.ts +6 -0
  327. package/vendor/markit-ai/dist/converters/plain-text.js +87 -0
  328. package/vendor/markit-ai/dist/converters/pptx.d.ts +8 -0
  329. package/vendor/markit-ai/dist/converters/pptx.js +289 -0
  330. package/vendor/markit-ai/dist/converters/rss.d.ts +11 -0
  331. package/vendor/markit-ai/dist/converters/rss.js +138 -0
  332. package/vendor/markit-ai/dist/converters/wikipedia.d.ts +6 -0
  333. package/vendor/markit-ai/dist/converters/wikipedia.js +33 -0
  334. package/vendor/markit-ai/dist/converters/xlsx.d.ts +8 -0
  335. package/vendor/markit-ai/dist/converters/xlsx.js +147 -0
  336. package/vendor/markit-ai/dist/converters/xml.d.ts +6 -0
  337. package/vendor/markit-ai/dist/converters/xml.js +18 -0
  338. package/vendor/markit-ai/dist/converters/yaml.d.ts +6 -0
  339. package/vendor/markit-ai/dist/converters/yaml.js +17 -0
  340. package/vendor/markit-ai/dist/converters/zip.d.ts +8 -0
  341. package/vendor/markit-ai/dist/converters/zip.js +57 -0
  342. package/vendor/markit-ai/dist/index.d.ts +30 -0
  343. package/vendor/markit-ai/dist/index.js +26 -0
  344. package/vendor/markit-ai/dist/main.d.ts +2 -0
  345. package/vendor/markit-ai/dist/main.js +191 -0
  346. package/vendor/markit-ai/dist/markit.d.ts +45 -0
  347. package/vendor/markit-ai/dist/markit.js +267 -0
  348. package/vendor/markit-ai/dist/plugins/api.d.ts +7 -0
  349. package/vendor/markit-ai/dist/plugins/api.js +42 -0
  350. package/vendor/markit-ai/dist/plugins/index.d.ts +4 -0
  351. package/vendor/markit-ai/dist/plugins/index.js +3 -0
  352. package/vendor/markit-ai/dist/plugins/installer.d.ts +25 -0
  353. package/vendor/markit-ai/dist/plugins/installer.js +176 -0
  354. package/vendor/markit-ai/dist/plugins/loader.d.ts +6 -0
  355. package/vendor/markit-ai/dist/plugins/loader.js +61 -0
  356. package/vendor/markit-ai/dist/plugins/types.d.ts +25 -0
  357. package/vendor/markit-ai/dist/plugins/types.js +1 -0
  358. package/vendor/markit-ai/dist/providers/anthropic.d.ts +2 -0
  359. package/vendor/markit-ai/dist/providers/anthropic.js +47 -0
  360. package/vendor/markit-ai/dist/providers/index.d.ts +21 -0
  361. package/vendor/markit-ai/dist/providers/index.js +56 -0
  362. package/vendor/markit-ai/dist/providers/openai.d.ts +2 -0
  363. package/vendor/markit-ai/dist/providers/openai.js +76 -0
  364. package/vendor/markit-ai/dist/providers/types.d.ts +26 -0
  365. package/vendor/markit-ai/dist/providers/types.js +1 -0
  366. package/vendor/markit-ai/dist/types.d.ts +36 -0
  367. package/vendor/markit-ai/dist/types.js +1 -0
  368. package/vendor/markit-ai/dist/utils/exit-codes.d.ts +4 -0
  369. package/vendor/markit-ai/dist/utils/exit-codes.js +4 -0
  370. package/vendor/markit-ai/dist/utils/output.d.ts +22 -0
  371. package/vendor/markit-ai/dist/utils/output.js +31 -0
  372. package/vendor/markit-ai/dist/utils/turndown.d.ts +8 -0
  373. package/vendor/markit-ai/dist/utils/turndown.js +70 -0
  374. package/vendor/markit-ai/package.json +74 -0
  375. package/vendor/markit-ai.patch +49 -0
package/src/tools/bash.ts CHANGED
@@ -66,10 +66,13 @@ import {
66
66
  } from "./output-meta";
67
67
  import { resolveToCwd } from "./path-utils";
68
68
  import { formatToolWorkingDirectory, replaceTabs } from "./render-utils";
69
+ import { steerFoldReasonLine, watchSteerForFold } from "./steer-fold";
69
70
  import { checkTmuxSelfInjection } from "./tmux-self-injection-guard";
70
71
  import { ToolAbortError, ToolError } from "./tool-errors";
71
72
  import { toolResult } from "./tool-result";
72
73
 
74
+ export { STEER_FOLD_GRACE_MS, steerFoldReasonLine } from "./steer-fold";
75
+
73
76
  function sliceTextAfterUtf8ByteOffset(text: string, offsetBytes: number): string {
74
77
  if (offsetBytes <= 0) return text;
75
78
  let consumed = 0;
@@ -86,19 +89,6 @@ function sliceTextAfterUtf8ByteOffset(text: string, offsetBytes: number): string
86
89
  import { clampTimeout, TOOL_TIMEOUTS } from "./tool-timeouts";
87
90
 
88
91
  export const BASH_DEFAULT_PREVIEW_LINES = 10;
89
- /**
90
- * A user steer folds a running foreground bash call only after it has been
91
- * running at least this long. Shorter commands finish normally and the steer is
92
- * consumed at the ordinary tool boundary, so a quick `git status` never turns
93
- * into a background job plus a wake-up turn.
94
- */
95
- export const STEER_FOLD_GRACE_MS = 2_000;
96
-
97
- /** Model-facing line appended to a steer-folded background-start result. */
98
- export function steerFoldReasonLine(jobId: string): string {
99
- return `Folded into background job ${jobId} because a user steer arrived; the command keeps running with its original timeout and its result will wake a later turn.`;
100
- }
101
-
102
92
  const BASH_ERROR_MAX_BYTES = 4096;
103
93
  const ARTIFACT_SAVE_DIAGNOSTIC_MAX_BYTES = 256;
104
94
  const BASH_ENV_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
@@ -481,12 +471,50 @@ export async function saveBashOriginalArtifactForTests(
481
471
  return result.status === "saved" ? result.artifactId : undefined;
482
472
  }
483
473
 
474
+ /**
475
+ * Model-declared activity metadata for the browser-backend routing contract
476
+ * (`browser.backend: aside`). When that contract routes a browser task through
477
+ * Bash, it requires the call to declare the activity structurally so consumers
478
+ * can project it without parsing the command text. The declaration is
479
+ * model-declared and schema-validated only: nothing here verifies that the
480
+ * command actually invokes Aside, and the executor never inspects `command`.
481
+ */
482
+ export type BashActivityProvider = "aside";
483
+ export type BashActivityMode = "repl" | "exec";
484
+ export interface BashActivityDeclaration {
485
+ kind: "browser";
486
+ provider: BashActivityProvider;
487
+ mode: BashActivityMode;
488
+ }
489
+
490
+ const bashActivityDeclarationSchema = z
491
+ .object({
492
+ kind: z.literal("browser"),
493
+ provider: z.literal("aside"),
494
+ mode: z.enum(["repl", "exec"]),
495
+ })
496
+ .strict();
497
+
498
+ /**
499
+ * Optional activity declaration. Anything that is not the exact supported
500
+ * shape is dropped rather than failing the call: the declaration is routing
501
+ * metadata, and a malformed value must never make an otherwise valid command
502
+ * unusable. Values are never coerced — `mode: "EXEC"` is dropped, not guessed.
503
+ */
504
+ const optionalBashActivity = z
505
+ .preprocess(
506
+ value => (bashActivityDeclarationSchema.safeParse(value).success ? value : undefined),
507
+ bashActivityDeclarationSchema.optional(),
508
+ )
509
+ .describe("declare browser activity for the aside browser backend: repl or exec");
510
+
484
511
  const bashSchemaBase = z.object({
485
512
  command: z.string().describe("command to execute"),
486
513
  env: z.record(z.string().regex(BASH_ENV_NAME_PATTERN), z.string()).optional().describe("extra env vars"),
487
514
  timeout: z.number().default(300).describe("timeout in seconds, NOT milliseconds (30 = 30s)").optional(),
488
515
  cwd: z.string().describe("working directory").optional(),
489
516
  pty: z.boolean().describe("run in pty mode").optional(),
517
+ activity: optionalBashActivity,
490
518
  });
491
519
 
492
520
  const bashSchemaWithAsync = bashSchemaBase.extend({
@@ -503,6 +531,8 @@ export interface BashToolInput {
503
531
 
504
532
  async?: boolean;
505
533
  pty?: boolean;
534
+ /** Accepted activity declaration, mirrored into {@link BashToolDetails.activity}. */
535
+ activity?: BashActivityDeclaration;
506
536
  }
507
537
 
508
538
  export interface BashToolDetails {
@@ -512,6 +542,8 @@ export interface BashToolDetails {
512
542
  terminalId?: string;
513
543
  /** Why the foreground wait was folded, when this result is a background-start after a fold. */
514
544
  foldReason?: FoldReason;
545
+ /** The accepted model-declared activity declaration, when the call carried one. */
546
+ activity?: BashActivityDeclaration;
515
547
  async?: {
516
548
  state: "running" | "completed" | "failed";
517
549
  jobId: string;
@@ -924,6 +956,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
924
956
  notices?: readonly string[];
925
957
  terminalId?: string;
926
958
  artifactReferenceInBody?: boolean;
959
+ activity?: BashActivityDeclaration;
927
960
  } = {},
928
961
  ): AgentToolResult<BashToolDetails> {
929
962
  const shortArtifactInBody = !result.truncated && result.artifactId !== undefined;
@@ -947,6 +980,9 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
947
980
  if (options.terminalId !== undefined) {
948
981
  details.terminalId = options.terminalId;
949
982
  }
983
+ if (options.activity !== undefined) {
984
+ details.activity = options.activity;
985
+ }
950
986
  const resultBuilder = toolResult(details)
951
987
  .text(outputText)
952
988
  .truncationFromSummary(result, {
@@ -967,6 +1003,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
967
1003
  notices?: readonly string[];
968
1004
  terminalId?: string;
969
1005
  foldReason?: FoldReason;
1006
+ activity?: BashActivityDeclaration;
970
1007
  } = {},
971
1008
  ): AgentToolResult<BashToolDetails> {
972
1009
  const details: BashToolDetails = {
@@ -980,6 +1017,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
980
1017
  details.requestedTimeoutSeconds = options.requestedTimeoutSec;
981
1018
  }
982
1019
  if (options.foldReason !== undefined) details.foldReason = options.foldReason;
1020
+ if (options.activity !== undefined) details.activity = options.activity;
983
1021
  const lines: string[] = [];
984
1022
  const trimmedPreview = previewText.trimEnd();
985
1023
  if (trimmedPreview.length > 0) {
@@ -1019,6 +1057,8 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
1019
1057
  onCompletion?: () => void;
1020
1058
  /** Immutable attempt-scoped tool call id, when executed via a tool call. */
1021
1059
  toolCallId?: string;
1060
+ /** Accepted activity declaration, mirrored into job progress and result details. */
1061
+ activity?: BashActivityDeclaration;
1022
1062
  }): ManagedBashJobHandle {
1023
1063
  const manager = this.#resolveOwnedJobManager();
1024
1064
  if (!manager) {
@@ -1032,12 +1072,13 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
1032
1072
  : options.command;
1033
1073
  let latestText = "";
1034
1074
  let backgrounded = options.startBackgrounded;
1075
+ const activityDetails = options.activity === undefined ? {} : { activity: options.activity };
1035
1076
  const runningDetails = (jobId: string): Record<string, unknown> | undefined =>
1036
- backgrounded ? { async: { state: "running", jobId, type: "bash" } } : undefined;
1077
+ backgrounded ? { async: { state: "running", jobId, type: "bash" }, ...activityDetails } : undefined;
1037
1078
  const completedDetails = (jobId: string): Record<string, unknown> | undefined =>
1038
- backgrounded ? { async: { state: "completed", jobId, type: "bash" } } : undefined;
1079
+ backgrounded ? { async: { state: "completed", jobId, type: "bash" }, ...activityDetails } : undefined;
1039
1080
  const failedDetails = (jobId: string): Record<string, unknown> | undefined =>
1040
- backgrounded ? { async: { state: "failed", jobId, type: "bash" } } : undefined;
1081
+ backgrounded ? { async: { state: "failed", jobId, type: "bash" }, ...activityDetails } : undefined;
1041
1082
  const completion = Promise.withResolvers<ManagedBashJobCompletion>();
1042
1083
 
1043
1084
  const jobId = manager.register(
@@ -1088,6 +1129,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
1088
1129
  requestedTimeoutSec: options.requestedTimeoutSec,
1089
1130
  notices: options.notices,
1090
1131
  artifactReferenceInBody: true,
1132
+ activity: options.activity,
1091
1133
  });
1092
1134
  const finalText = this.#extractTextResult(finalResult);
1093
1135
  latestText = finalText;
@@ -1110,7 +1152,10 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
1110
1152
  latestText = text;
1111
1153
  await options.onUpdate?.({
1112
1154
  content: [{ type: "text", text }],
1113
- details: backgrounded ? ((details ?? {}) as BashToolDetails) : {},
1155
+ // Backgrounded frames carry the job details verbatim; a foreground
1156
+ // frame has no job facts, but the activity declaration is per-call
1157
+ // metadata and stays attached so partial results are self-describing.
1158
+ details: backgrounded ? ((details ?? {}) as BashToolDetails) : (activityDetails as BashToolDetails),
1114
1159
  });
1115
1160
  },
1116
1161
  },
@@ -1155,62 +1200,6 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
1155
1200
  return AsyncJobManager.forEndpoint(endpointId) ?? AsyncJobManager.instance();
1156
1201
  }
1157
1202
 
1158
- /**
1159
- * Whether a queued user steer may fold the running foreground wait. Mirrors
1160
- * the loop's steer admission: `busyPromptMode=queue` never admits a steer
1161
- * into the busy run, so it never folds. `toolInterruptPolicy` is
1162
- * deliberately NOT a gate: `finish_tools` means "do not kill the batch to
1163
- * deliver a steer", and a fold kills nothing. It is the one way to deliver
1164
- * the steer now AND let the command finish, so it applies under both
1165
- * policies; the policy only decides what the loop does with sibling tools
1166
- * after the fold returns. The auto-background setting is likewise not
1167
- * consulted: like the chord, a steer fold is a user action.
1168
- *
1169
- * Fails closed: a session that cannot report newly admitted steering or
1170
- * accept a fold request is not steer-foldable.
1171
- */
1172
- #steerFoldEnabled(): boolean {
1173
- const { waitForUserSteering, requestForegroundBashBackground } = this.session;
1174
- if (!waitForUserSteering || !requestForegroundBashBackground) return false;
1175
- return this.session.settings.get("busyPromptMode") === "steer";
1176
- }
1177
-
1178
- /**
1179
- * Fold `adapter` on the first user steer that ARRIVES after the wait has run
1180
- * for {@link STEER_FOLD_GRACE_MS} since `startedAt`. A steer already queued
1181
- * when the wait starts, or one that arrives inside the grace window, never
1182
- * folds: the command finishes normally and that steer is consumed at the
1183
- * ordinary tool boundary. The watcher keeps observing so a later qualifying
1184
- * steer still folds, and re-checks the gate at that moment so a
1185
- * `busyPromptMode` change during the command is honored.
1186
- * Returns a stop function; call it when the wait settles. No-op when steer
1187
- * folding is gated off at start.
1188
- */
1189
- #watchSteerForFold(adapter: FoldAdapter, startedAt: number): () => void {
1190
- if (!this.#steerFoldEnabled()) return () => {};
1191
- const waitForSteer = this.session.waitForUserSteering;
1192
- const requestFold = this.session.requestForegroundBashBackground;
1193
- if (!waitForSteer || !requestFold) return () => {};
1194
- const watch = new AbortController();
1195
- const observe = async (): Promise<void> => {
1196
- while (!watch.signal.aborted) {
1197
- await waitForSteer(watch.signal);
1198
- if (watch.signal.aborted) return;
1199
- if (Date.now() - startedAt < STEER_FOLD_GRACE_MS) continue;
1200
- if (!this.#steerFoldEnabled()) continue;
1201
- await requestFold("steer", adapter);
1202
- return;
1203
- }
1204
- };
1205
- observe().catch((error: unknown) => {
1206
- logger.warn("Steer-triggered fold failed", {
1207
- jobId: adapter.jobId,
1208
- error: error instanceof Error ? error.message : String(error),
1209
- });
1210
- });
1211
- return () => watch.abort();
1212
- }
1213
-
1214
1203
  /**
1215
1204
  * Race a managed job against the auto-background threshold, an explicit fold
1216
1205
  * (chord / SDK control / steer), and abort. A steer that arrives once the
@@ -1269,7 +1258,9 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
1269
1258
  }
1270
1259
 
1271
1260
  const stopSteerWatch =
1272
- backgroundRequest && foldAdapter ? this.#watchSteerForFold(foldAdapter, startedAt) : () => {};
1261
+ backgroundRequest && foldAdapter
1262
+ ? watchSteerForFold(this.session, startedAt, fold => fold("steer", foldAdapter), foldAdapter.jobId)
1263
+ : () => {};
1273
1264
  try {
1274
1265
  return await Promise.race(waiters);
1275
1266
  } finally {
@@ -1571,9 +1562,10 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
1571
1562
  notices: readonly string[],
1572
1563
  signal: AbortSignal | undefined,
1573
1564
  resolvedEnv: Record<string, string>,
1565
+ activity: BashActivityDeclaration | undefined,
1574
1566
  ): Promise<AgentToolResult<BashToolDetails>> {
1575
1567
  const result = await this.#runDirectMasterSpawn(command, commandCwd, timeoutMs, signal, resolvedEnv);
1576
- return this.#buildCompletedResult(result, timeoutSec, { requestedTimeoutSec, notices });
1568
+ return this.#buildCompletedResult(result, timeoutSec, { requestedTimeoutSec, notices, activity });
1577
1569
  }
1578
1570
 
1579
1571
  /**
@@ -1723,6 +1715,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
1723
1715
 
1724
1716
  async: asyncRequested = false,
1725
1717
  pty = false,
1718
+ activity,
1726
1719
  }: BashToolInput,
1727
1720
  signal?: AbortSignal,
1728
1721
  onUpdate?: AgentToolUpdateCallback<BashToolDetails>,
@@ -1767,6 +1760,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
1767
1760
  pendingNotices,
1768
1761
  signal,
1769
1762
  resolvedEnv,
1763
+ activity,
1770
1764
  );
1771
1765
  }
1772
1766
 
@@ -1792,6 +1786,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
1792
1786
  onUpdate,
1793
1787
  startBackgrounded: true,
1794
1788
  toolCallId,
1789
+ activity,
1795
1790
  });
1796
1791
  const jobGeneration = asyncManager.getJob(job.jobId)?.generation ?? job.jobId;
1797
1792
  job.setBackgrounded(true);
@@ -1799,6 +1794,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
1799
1794
  return this.#buildBackgroundStartResult(job.jobId, job.label, "", timeoutSec, {
1800
1795
  requestedTimeoutSec,
1801
1796
  notices: pendingNotices,
1797
+ activity,
1802
1798
  });
1803
1799
  }
1804
1800
 
@@ -1845,6 +1841,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
1845
1841
  managedForegroundSettled = true;
1846
1842
  },
1847
1843
  toolCallId,
1844
+ activity,
1848
1845
  });
1849
1846
  const jobGeneration = ownedManager.getJob(job.jobId)?.generation ?? job.jobId;
1850
1847
  if (startBackgrounded) {
@@ -1853,6 +1850,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
1853
1850
  return this.#buildBackgroundStartResult(job.jobId, job.label, "", timeoutSec, {
1854
1851
  requestedTimeoutSec,
1855
1852
  notices: pendingNotices,
1853
+ activity,
1856
1854
  });
1857
1855
  }
1858
1856
  const backgroundRequest = Promise.withResolvers<FoldReason>();
@@ -1948,6 +1946,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
1948
1946
  requestedTimeoutSec,
1949
1947
  notices: pendingNotices,
1950
1948
  foldReason: waitResult.reason,
1949
+ activity,
1951
1950
  });
1952
1951
  }
1953
1952
 
@@ -2246,6 +2245,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
2246
2245
  requestedTimeoutSec,
2247
2246
  notices: timeoutNotices,
2248
2247
  terminalId: handle.terminalId,
2248
+ activity,
2249
2249
  });
2250
2250
  }
2251
2251
 
@@ -2378,6 +2378,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
2378
2378
  requestedTimeoutSec,
2379
2379
  notices: bridgeNotices,
2380
2380
  terminalId: handle.terminalId,
2381
+ activity,
2381
2382
  });
2382
2383
  };
2383
2384
 
@@ -2419,7 +2420,12 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
2419
2420
  bridgeCompletion.resolve({ kind: "completed", result });
2420
2421
  await reportProgress(
2421
2422
  finalText,
2422
- bridgeBackgrounded ? { async: { state: "completed", jobId, type: "bash" } } : undefined,
2423
+ bridgeBackgrounded
2424
+ ? {
2425
+ async: { state: "completed", jobId, type: "bash" },
2426
+ ...(activity === undefined ? {} : { activity }),
2427
+ }
2428
+ : undefined,
2423
2429
  );
2424
2430
  return finalText;
2425
2431
  } catch (error) {
@@ -2429,7 +2435,12 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
2429
2435
  bridgeCompletion.resolve({ kind: "failed", error });
2430
2436
  await reportProgress(
2431
2437
  message,
2432
- bridgeBackgrounded ? { async: { state: "failed", jobId, type: "bash" } } : undefined,
2438
+ bridgeBackgrounded
2439
+ ? {
2440
+ async: { state: "failed", jobId, type: "bash" },
2441
+ ...(activity === undefined ? {} : { activity }),
2442
+ }
2443
+ : undefined,
2433
2444
  );
2434
2445
  throw error;
2435
2446
  } finally {
@@ -2556,6 +2567,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
2556
2567
  notices: pendingNotices,
2557
2568
  terminalId: handle.terminalId,
2558
2569
  foldReason: bridgeWait.reason,
2570
+ activity,
2559
2571
  });
2560
2572
  }
2561
2573
 
@@ -2640,6 +2652,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
2640
2652
  outcome = await controls.terminalCompletion;
2641
2653
  const completed = this.#buildCompletedResult(outcome, timeoutSec, {
2642
2654
  requestedTimeoutSec,
2655
+ activity,
2643
2656
  });
2644
2657
  return this.#extractTextResult(completed);
2645
2658
  } finally {
@@ -2707,6 +2720,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
2707
2720
  requestedTimeoutSec,
2708
2721
  notices: pendingNotices,
2709
2722
  foldReason: receipt.reason,
2723
+ activity,
2710
2724
  });
2711
2725
  const outcome = controls.detachObserver(
2712
2726
  interactiveResultFromText(this.#extractTextResult(started)),
@@ -2722,7 +2736,12 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
2722
2736
  resolveForegroundObserver: () => "already-settled",
2723
2737
  };
2724
2738
  const unregisterPtyFold = this.session.registerForegroundFoldParticipant?.(ptyFoldAdapter);
2725
- const stopPtySteerWatch = this.#watchSteerForFold(ptyFoldAdapter, ptyStartedAt);
2739
+ const stopPtySteerWatch = watchSteerForFold(
2740
+ this.session,
2741
+ ptyStartedAt,
2742
+ fold => fold("steer", ptyFoldAdapter),
2743
+ ptyFoldAdapter.jobId,
2744
+ );
2726
2745
  ptyFoldUnregister = () => {
2727
2746
  stopPtySteerWatch();
2728
2747
  unregisterPtyFold?.();
@@ -2790,6 +2809,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
2790
2809
  return this.#buildCompletedResult(result, timeoutSec, {
2791
2810
  requestedTimeoutSec,
2792
2811
  notices: pendingNotices,
2812
+ activity,
2793
2813
  });
2794
2814
  }
2795
2815
  }
@@ -49,6 +49,7 @@ const FETCH_DEFAULT_MAX_LINES = 300;
49
49
  // Convertible document types handled by markit.
50
50
  const CONVERTIBLE_MIMES = new Set([
51
51
  "application/pdf",
52
+ "application/x-pdf",
52
53
  "application/msword",
53
54
  "application/vnd.ms-powerpoint",
54
55
  "application/vnd.ms-excel",
@@ -228,17 +229,52 @@ function normalizeMime(contentType: string): string {
228
229
  return contentType.split(";")[0].trim().toLowerCase();
229
230
  }
230
231
 
232
+ // Bound both parsing work and decoded filename size to a single modest header.
233
+ function getDispositionFilename(header: string): string | undefined {
234
+ if (header.length > 8192 || /[\x00-\x1f\x7f]/.test(header.replaceAll("\t", ""))) return undefined;
235
+ const separator = header.indexOf(";");
236
+ if (separator < 0) return undefined;
237
+ const parameter = /;[ \t]*([!#$%&'*+.^_`|~\w-]+)[ \t]*=[ \t]*(?:"((?:[^"\\]|\\.)*)"|([!#$%&'*+.^_`|~\w-]+))[ \t]*/y;
238
+ let offset = separator;
239
+ let plain: string | undefined;
240
+ let extended: string | undefined;
241
+ const seen = new Set<string>();
242
+ while (offset < header.length) {
243
+ parameter.lastIndex = offset;
244
+ const match = parameter.exec(header);
245
+ if (!match) return undefined;
246
+ offset = parameter.lastIndex;
247
+ const name = match[1].toLowerCase();
248
+ if (name !== "filename" && name !== "filename*") continue;
249
+ // Duplicate filename parameters are ambiguous, not an ordering override.
250
+ if (seen.has(name)) return undefined;
251
+ seen.add(name);
252
+ const value = match[2] === undefined ? match[3] : match[2].replace(/\\(.)/g, "$1");
253
+ if (name === "filename") {
254
+ if (value) plain = value;
255
+ continue;
256
+ }
257
+ // RFC 8187 ext-value: only UTF-8 is supported; quoted ext-values are invalid.
258
+ const encoded =
259
+ match[2] === undefined ? /^UTF-8'[A-Za-z0-9-]*'((?:[!#$&+.^_`|~\w-]|%[\da-fA-F]{2})*)$/i.exec(value) : null;
260
+ if (!encoded) continue;
261
+ try {
262
+ const decoded = decodeURIComponent(encoded[1]);
263
+ if (decoded && !/[\x00-\x1f\x7f]/.test(decoded)) extended = decoded;
264
+ } catch {
265
+ // Invalid UTF-8 or percent encoding must not escape classification.
266
+ }
267
+ }
268
+ return extended ?? plain;
269
+ }
270
+
231
271
  /**
232
272
  * Get extension from URL or Content-Disposition
233
273
  */
234
274
  function getExtensionHint(url: string, contentDisposition?: string): string {
235
- // Try Content-Disposition filename first
236
275
  if (contentDisposition) {
237
- const match = contentDisposition.match(/filename[*]?=["']?([^"';\n]+)/i);
238
- if (match) {
239
- const ext = path.extname(match[1]).toLowerCase();
240
- if (ext) return ext;
241
- }
276
+ const filename = getDispositionFilename(contentDisposition);
277
+ if (filename !== undefined) return path.extname(filename).toLowerCase();
242
278
  }
243
279
 
244
280
  // Fall back to URL path
@@ -251,21 +287,25 @@ function getExtensionHint(url: string, contentDisposition?: string): string {
251
287
  return "";
252
288
  }
253
289
 
290
+ function isGenericMimeType(mime: string): boolean {
291
+ return (
292
+ mime.length === 0 || mime === "application/octet-stream" || mime === "binary/octet-stream" || mime === "unknown"
293
+ );
294
+ }
295
+
254
296
  /**
255
297
  * Check if content type is convertible via markit.
256
298
  */
257
299
  function isConvertible(mime: string, extensionHint: string): boolean {
258
- if (CONVERTIBLE_MIMES.has(mime)) return true;
259
- if (mime === "application/octet-stream" && CONVERTIBLE_EXTENSIONS.has(extensionHint)) return true;
260
- if (CONVERTIBLE_EXTENSIONS.has(extensionHint)) return true;
261
- return false;
300
+ return (
301
+ CONVERTIBLE_MIMES.has(mime) ||
302
+ (CONVERTIBLE_EXTENSIONS.has(extensionHint) && (extensionHint !== ".pdf" || isGenericMimeType(mime)))
303
+ );
262
304
  }
263
305
 
264
306
  function resolveImageMimeType(mime: string, extensionHint: string): string | null {
265
307
  if (mime.startsWith("image/")) return mime;
266
- const shouldUseExtensionHint =
267
- mime.length === 0 || mime === "application/octet-stream" || mime === "binary/octet-stream" || mime === "unknown";
268
- if (!shouldUseExtensionHint) return null;
308
+ if (!isGenericMimeType(mime)) return null;
269
309
  return IMAGE_MIME_BY_EXTENSION.get(extensionHint) ?? null;
270
310
  }
271
311
 
@@ -741,15 +781,60 @@ async function renderUrl(
741
781
  const { finalUrl, content: rawContent } = response;
742
782
  const mime = normalizeMime(response.contentType);
743
783
  const extHint = getExtensionHint(finalUrl);
784
+ let isPdf =
785
+ mime === "application/pdf" || mime === "application/x-pdf" || (extHint === ".pdf" && isGenericMimeType(mime));
786
+
787
+ // Raw PDF inspection is explicit; never substitute PDF bytes for failed text extraction.
788
+ if (raw && isPdf) {
789
+ const output = finalizeOutput(rawContent);
790
+ return {
791
+ url,
792
+ finalUrl,
793
+ contentType: mime,
794
+ method: "raw",
795
+ content: output.content,
796
+ fetchedAt,
797
+ truncated: output.truncated,
798
+ notes,
799
+ };
800
+ }
744
801
 
745
- const imageMimeType = resolveImageMimeType(mime, extHint);
802
+ // Classify generic downloads from the bounded binary response before dispatch.
803
+ // Both image rendering and document conversion reuse these bytes.
804
+ const dispositionBinary =
805
+ !raw && isGenericMimeType(mime)
806
+ ? response.buffer
807
+ ? { ok: true as const, buffer: response.buffer, contentDisposition: response.contentDisposition }
808
+ : await fetchBinary(finalUrl, timeout, signal)
809
+ : undefined;
810
+ if (dispositionBinary && !dispositionBinary.ok) {
811
+ notes.push(dispositionBinary.error ? `Binary fetch failed: ${dispositionBinary.error}` : "Binary fetch failed");
812
+ return {
813
+ url,
814
+ finalUrl,
815
+ contentType: mime,
816
+ method: "failed",
817
+ content: "",
818
+ fetchedAt,
819
+ truncated: false,
820
+ notes,
821
+ };
822
+ }
823
+ const effectiveExt = dispositionBinary?.ok
824
+ ? getExtensionHint(finalUrl, dispositionBinary.contentDisposition)
825
+ : extHint;
826
+ isPdf =
827
+ mime === "application/pdf" ||
828
+ mime === "application/x-pdf" ||
829
+ (effectiveExt === ".pdf" && isGenericMimeType(mime));
830
+ const imageMimeType = isPdf ? undefined : resolveImageMimeType(mime, effectiveExt);
746
831
  let skipConvertibleBinaryRetry = false;
747
832
  if (imageMimeType) {
748
833
  if (!isInlineImageMimeTypeSupported(imageMimeType)) {
749
834
  notes.push(
750
835
  `Image MIME type ${imageMimeType} is unsupported for inline model serialization; returning text metadata only`,
751
836
  );
752
- const shouldTryConvertibleFallback = isConvertible(mime, extHint);
837
+ const shouldTryConvertibleFallback = isConvertible(mime, effectiveExt);
753
838
  if (shouldTryConvertibleFallback) {
754
839
  notes.push("Attempting binary conversion fallback for unsupported image MIME type");
755
840
  } else {
@@ -757,7 +842,7 @@ async function renderUrl(
757
842
  }
758
843
  skipConvertibleBinaryRetry = !shouldTryConvertibleFallback;
759
844
  } else {
760
- const binary = await fetchBinary(finalUrl, timeout, signal);
845
+ const binary = dispositionBinary ?? (await fetchBinary(finalUrl, timeout, signal));
761
846
  if (binary.ok) {
762
847
  notes.push("Fetched image binary");
763
848
  const conversionExtension = getExtensionHint(finalUrl, binary.contentDisposition) || extHint;
@@ -868,10 +953,10 @@ async function renderUrl(
868
953
  }
869
954
 
870
955
  // Step 3: Handle convertible binary files (PDF, DOCX, etc.)
871
- if (!skipConvertibleBinaryRetry && isConvertible(mime, extHint)) {
872
- const binary = await fetchBinary(finalUrl, timeout, signal);
956
+ if (!skipConvertibleBinaryRetry && (isPdf || isConvertible(mime, effectiveExt))) {
957
+ const binary = dispositionBinary ?? (await fetchBinary(finalUrl, timeout, signal));
873
958
  if (binary.ok) {
874
- const ext = getExtensionHint(finalUrl, binary.contentDisposition) || extHint;
959
+ const ext = isPdf ? ".pdf" : getExtensionHint(finalUrl, binary.contentDisposition) || extHint;
875
960
  const converted = await convertWithMarkit(binary.buffer, ext, timeout, signal);
876
961
  if (converted.ok) {
877
962
  // Any non-empty markit conversion is preferable to a raw-bytes
@@ -902,6 +987,18 @@ async function renderUrl(
902
987
  } else {
903
988
  notes.push("Binary fetch failed");
904
989
  }
990
+ if (isPdf) {
991
+ return {
992
+ url,
993
+ finalUrl,
994
+ contentType: mime,
995
+ method: "failed",
996
+ content: "",
997
+ fetchedAt,
998
+ truncated: false,
999
+ notes,
1000
+ };
1001
+ }
905
1002
  }
906
1003
 
907
1004
  // Step 4: Handle non-HTML text content
@@ -1066,7 +1163,7 @@ async function renderUrl(
1066
1163
  if (binary.ok) {
1067
1164
  const ext = getExtensionHint(docUrl, binary.contentDisposition);
1068
1165
  const converted = await convertWithMarkit(binary.buffer, ext, timeout, signal);
1069
- if (converted.ok && converted.content.trim().length > htmlResult.content.length) {
1166
+ if (converted.ok && converted.content.trim().length > (ext === ".pdf" ? 0 : htmlResult.content.length)) {
1070
1167
  notes.push(`Extracted and converted document: ${docUrl}`);
1071
1168
  const output = finalizeOutput(converted.content);
1072
1169
  return {
@@ -1082,6 +1179,8 @@ async function renderUrl(
1082
1179
  }
1083
1180
  if (!converted.ok && converted.error) {
1084
1181
  notes.push(`markit conversion failed: ${converted.error}`);
1182
+ } else if (converted.ok && !converted.content.trim()) {
1183
+ notes.push("markit conversion produced no usable output");
1085
1184
  }
1086
1185
  } else if (binary.error) {
1087
1186
  notes.push(`Binary fetch failed: ${binary.error}`);