@pentoshi/clai 3.5.1 → 3.7.2

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 (369) hide show
  1. package/README.md +1 -1
  2. package/dist/agent/compaction-summary.d.ts +17 -0
  3. package/dist/agent/compaction-summary.js +64 -0
  4. package/dist/agent/compaction-summary.js.map +1 -0
  5. package/dist/agent/confirm-port.d.ts +3 -1
  6. package/dist/agent/confirm-port.js +5 -1
  7. package/dist/agent/confirm-port.js.map +1 -1
  8. package/dist/agent/context-manager.js +53 -24
  9. package/dist/agent/context-manager.js.map +1 -1
  10. package/dist/agent/events.d.ts +6 -0
  11. package/dist/agent/evidence-governor.d.ts +37 -0
  12. package/dist/agent/evidence-governor.js +70 -0
  13. package/dist/agent/evidence-governor.js.map +1 -0
  14. package/dist/agent/loop-guard.d.ts +29 -9
  15. package/dist/agent/loop-guard.js +117 -26
  16. package/dist/agent/loop-guard.js.map +1 -1
  17. package/dist/agent/must-continue.d.ts +56 -0
  18. package/dist/agent/must-continue.js +240 -0
  19. package/dist/agent/must-continue.js.map +1 -0
  20. package/dist/agent/outcomes.d.ts +101 -0
  21. package/dist/agent/outcomes.js +359 -0
  22. package/dist/agent/outcomes.js.map +1 -0
  23. package/dist/agent/plan-decision.d.ts +33 -0
  24. package/dist/agent/plan-decision.js +104 -0
  25. package/dist/agent/plan-decision.js.map +1 -0
  26. package/dist/agent/plan-tool.d.ts +34 -0
  27. package/dist/agent/plan-tool.js +594 -76
  28. package/dist/agent/plan-tool.js.map +1 -1
  29. package/dist/agent/project-root.d.ts +33 -0
  30. package/dist/agent/project-root.js +161 -0
  31. package/dist/agent/project-root.js.map +1 -0
  32. package/dist/agent/prompt-composer.d.ts +21 -0
  33. package/dist/agent/prompt-composer.js +46 -0
  34. package/dist/agent/prompt-composer.js.map +1 -0
  35. package/dist/agent/runner.d.ts +11 -1
  36. package/dist/agent/runner.js +1915 -886
  37. package/dist/agent/runner.js.map +1 -1
  38. package/dist/agent/scope-context.d.ts +3 -0
  39. package/dist/agent/scope-context.js +24 -0
  40. package/dist/agent/scope-context.js.map +1 -0
  41. package/dist/agent/scratch-write.d.ts +4 -0
  42. package/dist/agent/scratch-write.js +54 -0
  43. package/dist/agent/scratch-write.js.map +1 -0
  44. package/dist/agent/session-policy.d.ts +7 -0
  45. package/dist/agent/session-policy.js +61 -0
  46. package/dist/agent/session-policy.js.map +1 -1
  47. package/dist/agent/session-state.d.ts +41 -0
  48. package/dist/agent/session-state.js +107 -0
  49. package/dist/agent/session-state.js.map +1 -0
  50. package/dist/agent/step-budget.d.ts +14 -0
  51. package/dist/agent/step-budget.js +26 -0
  52. package/dist/agent/step-budget.js.map +1 -0
  53. package/dist/agent/task-analyzer.d.ts +12 -3
  54. package/dist/agent/task-analyzer.js +263 -23
  55. package/dist/agent/task-analyzer.js.map +1 -1
  56. package/dist/agent/task-evidence.d.ts +173 -0
  57. package/dist/agent/task-evidence.js +802 -0
  58. package/dist/agent/task-evidence.js.map +1 -0
  59. package/dist/agent/task-plan.d.ts +57 -0
  60. package/dist/agent/task-plan.js +147 -1
  61. package/dist/agent/task-plan.js.map +1 -1
  62. package/dist/agent/tool-call-parser.d.ts +19 -11
  63. package/dist/agent/tool-call-parser.js +96 -71
  64. package/dist/agent/tool-call-parser.js.map +1 -1
  65. package/dist/agent/tool-history.d.ts +34 -0
  66. package/dist/agent/tool-history.js +200 -0
  67. package/dist/agent/tool-history.js.map +1 -0
  68. package/dist/agent/tool-output-formatting.d.ts +13 -1
  69. package/dist/agent/tool-output-formatting.js +75 -22
  70. package/dist/agent/tool-output-formatting.js.map +1 -1
  71. package/dist/agent/turn-outcome.d.ts +11 -0
  72. package/dist/agent/turn-outcome.js +17 -0
  73. package/dist/agent/turn-outcome.js.map +1 -0
  74. package/dist/agent/turn-state.d.ts +11 -0
  75. package/dist/agent/turn-state.js +28 -0
  76. package/dist/agent/turn-state.js.map +1 -0
  77. package/dist/agent/workspace-orient.d.ts +75 -0
  78. package/dist/agent/workspace-orient.js +509 -0
  79. package/dist/agent/workspace-orient.js.map +1 -0
  80. package/dist/app/adapters/agent-event-adapter.d.ts +2 -13
  81. package/dist/app/adapters/agent-event-adapter.js +63 -25
  82. package/dist/app/adapters/agent-event-adapter.js.map +1 -1
  83. package/dist/app/adapters/current-agent-adapter.d.ts +0 -5
  84. package/dist/app/adapters/current-agent-adapter.js +4 -7
  85. package/dist/app/adapters/current-agent-adapter.js.map +1 -1
  86. package/dist/app/adapters/current-jobs-adapter.js +1 -0
  87. package/dist/app/adapters/current-jobs-adapter.js.map +1 -1
  88. package/dist/app/adapters/current-terminal-adapter.d.ts +0 -7
  89. package/dist/app/adapters/current-terminal-adapter.js +0 -7
  90. package/dist/app/adapters/current-terminal-adapter.js.map +1 -1
  91. package/dist/app/adapters/current-updates-adapter.d.ts +0 -5
  92. package/dist/app/adapters/current-updates-adapter.js +0 -5
  93. package/dist/app/adapters/current-updates-adapter.js.map +1 -1
  94. package/dist/app/adapters/in-memory-clipboard-adapter.d.ts +0 -5
  95. package/dist/app/adapters/in-memory-clipboard-adapter.js +0 -5
  96. package/dist/app/adapters/in-memory-clipboard-adapter.js.map +1 -1
  97. package/dist/app/commands/command.d.ts +0 -5
  98. package/dist/app/commands/command.js.map +1 -1
  99. package/dist/app/commands/registry.d.ts +0 -14
  100. package/dist/app/commands/registry.js +0 -20
  101. package/dist/app/commands/registry.js.map +1 -1
  102. package/dist/app/controllers/disposable.d.ts +0 -5
  103. package/dist/app/controllers/disposable.js +0 -5
  104. package/dist/app/controllers/disposable.js.map +1 -1
  105. package/dist/app/controllers/job-controller.d.ts +1 -5
  106. package/dist/app/controllers/job-controller.js +0 -4
  107. package/dist/app/controllers/job-controller.js.map +1 -1
  108. package/dist/app/controllers/plan-controller.d.ts +5 -8
  109. package/dist/app/controllers/plan-controller.js +30 -12
  110. package/dist/app/controllers/plan-controller.js.map +1 -1
  111. package/dist/app/controllers/session-controller.d.ts +17 -32
  112. package/dist/app/controllers/session-controller.js +60 -35
  113. package/dist/app/controllers/session-controller.js.map +1 -1
  114. package/dist/app/controllers/turn-controller.d.ts +2 -6
  115. package/dist/app/controllers/turn-controller.js +8 -8
  116. package/dist/app/controllers/turn-controller.js.map +1 -1
  117. package/dist/app/events/app-event.d.ts +4 -7
  118. package/dist/app/events/app-event.js +0 -6
  119. package/dist/app/events/app-event.js.map +1 -1
  120. package/dist/app/events/event-buffer.d.ts +6 -17
  121. package/dist/app/events/event-buffer.js +27 -24
  122. package/dist/app/events/event-buffer.js.map +1 -1
  123. package/dist/app/events/sequencer.d.ts +0 -9
  124. package/dist/app/events/sequencer.js +0 -4
  125. package/dist/app/events/sequencer.js.map +1 -1
  126. package/dist/app/ports/agent-port.d.ts +13 -3
  127. package/dist/app/ports/clipboard-port.d.ts +0 -5
  128. package/dist/app/ports/confirm-port.d.ts +0 -7
  129. package/dist/app/ports/jobs-port.d.ts +2 -5
  130. package/dist/app/ports/persistence-port.d.ts +0 -4
  131. package/dist/app/ports/secret-port.d.ts +0 -5
  132. package/dist/app/ports/terminal-port.d.ts +0 -6
  133. package/dist/app/ports/updates-port.d.ts +0 -4
  134. package/dist/attachments/service.d.ts +29 -0
  135. package/dist/attachments/service.js +47 -0
  136. package/dist/attachments/service.js.map +1 -0
  137. package/dist/commands/doctor.js +3 -0
  138. package/dist/commands/doctor.js.map +1 -1
  139. package/dist/commands/providers.js +0 -3
  140. package/dist/commands/providers.js.map +1 -1
  141. package/dist/commands/search-providers.d.ts +0 -33
  142. package/dist/commands/search-providers.js +0 -33
  143. package/dist/commands/search-providers.js.map +1 -1
  144. package/dist/commands/update.js +1 -9
  145. package/dist/commands/update.js.map +1 -1
  146. package/dist/index.js +22 -42
  147. package/dist/index.js.map +1 -1
  148. package/dist/llm/adapters/anthropic-tools.d.ts +99 -0
  149. package/dist/llm/adapters/anthropic-tools.js +225 -0
  150. package/dist/llm/adapters/anthropic-tools.js.map +1 -0
  151. package/dist/llm/adapters/gemini-tools.d.ts +54 -0
  152. package/dist/llm/adapters/gemini-tools.js +139 -0
  153. package/dist/llm/adapters/gemini-tools.js.map +1 -0
  154. package/dist/llm/adapters/ollama-tools.d.ts +18 -0
  155. package/dist/llm/adapters/ollama-tools.js +58 -0
  156. package/dist/llm/adapters/ollama-tools.js.map +1 -0
  157. package/dist/llm/adapters/openai-tools.d.ts +39 -0
  158. package/dist/llm/adapters/openai-tools.js +71 -0
  159. package/dist/llm/adapters/openai-tools.js.map +1 -0
  160. package/dist/llm/agentrouter.js +23 -4
  161. package/dist/llm/agentrouter.js.map +1 -1
  162. package/dist/llm/anthropic.js +93 -92
  163. package/dist/llm/anthropic.js.map +1 -1
  164. package/dist/llm/aws-mantle.js +93 -56
  165. package/dist/llm/aws-mantle.js.map +1 -1
  166. package/dist/llm/bynara.js +23 -4
  167. package/dist/llm/bynara.js.map +1 -1
  168. package/dist/llm/capabilities.d.ts +13 -6
  169. package/dist/llm/capabilities.js +113 -13
  170. package/dist/llm/capabilities.js.map +1 -1
  171. package/dist/llm/gemini.js +63 -48
  172. package/dist/llm/gemini.js.map +1 -1
  173. package/dist/llm/groq.js +23 -10
  174. package/dist/llm/groq.js.map +1 -1
  175. package/dist/llm/http.d.ts +22 -34
  176. package/dist/llm/http.js +97 -69
  177. package/dist/llm/http.js.map +1 -1
  178. package/dist/llm/kimchi.js +23 -4
  179. package/dist/llm/kimchi.js.map +1 -1
  180. package/dist/llm/nvidia.js +23 -4
  181. package/dist/llm/nvidia.js.map +1 -1
  182. package/dist/llm/ollama.js +49 -31
  183. package/dist/llm/ollama.js.map +1 -1
  184. package/dist/llm/openai.js +23 -4
  185. package/dist/llm/openai.js.map +1 -1
  186. package/dist/llm/openrouter.js +23 -4
  187. package/dist/llm/openrouter.js.map +1 -1
  188. package/dist/llm/qwen-cloud.js +23 -4
  189. package/dist/llm/qwen-cloud.js.map +1 -1
  190. package/dist/llm/router.d.ts +0 -6
  191. package/dist/llm/router.js +48 -21
  192. package/dist/llm/router.js.map +1 -1
  193. package/dist/llm/tool-protocol.d.ts +67 -0
  194. package/dist/llm/tool-protocol.js +278 -0
  195. package/dist/llm/tool-protocol.js.map +1 -0
  196. package/dist/modes/agent.d.ts +2 -1
  197. package/dist/modes/agent.js +4 -2
  198. package/dist/modes/agent.js.map +1 -1
  199. package/dist/modes/ask.d.ts +0 -18
  200. package/dist/modes/ask.js +71 -66
  201. package/dist/modes/ask.js.map +1 -1
  202. package/dist/os/cwd.d.ts +0 -21
  203. package/dist/os/cwd.js +0 -24
  204. package/dist/os/cwd.js.map +1 -1
  205. package/dist/os/pkgmgr.js +0 -6
  206. package/dist/os/pkgmgr.js.map +1 -1
  207. package/dist/prompts/index.d.ts +17 -21
  208. package/dist/prompts/index.js +131 -259
  209. package/dist/prompts/index.js.map +1 -1
  210. package/dist/prompts/system.agent.md +242 -0
  211. package/dist/prompts/system.ask.md +56 -0
  212. package/dist/repl/prompt-line.js +0 -36
  213. package/dist/repl/prompt-line.js.map +1 -1
  214. package/dist/repl/slash-commands.d.ts +0 -9
  215. package/dist/repl/slash-commands.js +2 -23
  216. package/dist/repl/slash-commands.js.map +1 -1
  217. package/dist/repl.d.ts +5 -0
  218. package/dist/repl.js +176 -55
  219. package/dist/repl.js.map +1 -1
  220. package/dist/safety/classifier.js +17 -9
  221. package/dist/safety/classifier.js.map +1 -1
  222. package/dist/safety/engagement-policy.d.ts +46 -0
  223. package/dist/safety/engagement-policy.js +198 -0
  224. package/dist/safety/engagement-policy.js.map +1 -0
  225. package/dist/store/config.d.ts +7 -0
  226. package/dist/store/config.js +1 -0
  227. package/dist/store/config.js.map +1 -1
  228. package/dist/store/engagement.d.ts +115 -0
  229. package/dist/store/engagement.js +278 -0
  230. package/dist/store/engagement.js.map +1 -0
  231. package/dist/store/logs.d.ts +5 -0
  232. package/dist/store/logs.js +52 -2
  233. package/dist/store/logs.js.map +1 -1
  234. package/dist/store/plan.d.ts +59 -5
  235. package/dist/store/plan.js +182 -8
  236. package/dist/store/plan.js.map +1 -1
  237. package/dist/store/scope.d.ts +4 -1
  238. package/dist/store/scope.js +2 -2
  239. package/dist/store/scope.js.map +1 -1
  240. package/dist/tools/capabilities.d.ts +2 -0
  241. package/dist/tools/capabilities.js +101 -6
  242. package/dist/tools/capabilities.js.map +1 -1
  243. package/dist/tools/command-intent.d.ts +1 -0
  244. package/dist/tools/command-intent.js +31 -0
  245. package/dist/tools/command-intent.js.map +1 -1
  246. package/dist/tools/definitions.d.ts +20 -0
  247. package/dist/tools/definitions.js +578 -0
  248. package/dist/tools/definitions.js.map +1 -0
  249. package/dist/tools/elevated-shell.d.ts +60 -0
  250. package/dist/tools/elevated-shell.js +233 -0
  251. package/dist/tools/elevated-shell.js.map +1 -0
  252. package/dist/tools/file-diff.d.ts +125 -0
  253. package/dist/tools/file-diff.js +495 -0
  254. package/dist/tools/file-diff.js.map +1 -0
  255. package/dist/tools/fs.d.ts +11 -13
  256. package/dist/tools/fs.js +280 -80
  257. package/dist/tools/fs.js.map +1 -1
  258. package/dist/tools/http.d.ts +15 -0
  259. package/dist/tools/http.js +202 -61
  260. package/dist/tools/http.js.map +1 -1
  261. package/dist/tools/jobs.d.ts +71 -10
  262. package/dist/tools/jobs.js +403 -124
  263. package/dist/tools/jobs.js.map +1 -1
  264. package/dist/tools/net-ping-sweep.js +69 -16
  265. package/dist/tools/net-ping-sweep.js.map +1 -1
  266. package/dist/tools/nmap-runner.d.ts +19 -7
  267. package/dist/tools/nmap-runner.js +182 -80
  268. package/dist/tools/nmap-runner.js.map +1 -1
  269. package/dist/tools/pentest-workflows.d.ts +8 -0
  270. package/dist/tools/pentest-workflows.js +71 -0
  271. package/dist/tools/pentest-workflows.js.map +1 -0
  272. package/dist/tools/registry.d.ts +9 -0
  273. package/dist/tools/registry.js +272 -63
  274. package/dist/tools/registry.js.map +1 -1
  275. package/dist/tools/shell.d.ts +3 -0
  276. package/dist/tools/shell.js +35 -13
  277. package/dist/tools/shell.js.map +1 -1
  278. package/dist/tools/tool-types.d.ts +11 -0
  279. package/dist/tools/validate.d.ts +11 -0
  280. package/dist/tools/validate.js +53 -0
  281. package/dist/tools/validate.js.map +1 -1
  282. package/dist/tools/web/search.js +48 -4
  283. package/dist/tools/web/search.js.map +1 -1
  284. package/dist/tui-v2/app/App.js +25 -39
  285. package/dist/tui-v2/app/App.js.map +1 -1
  286. package/dist/tui-v2/app/command-handlers.js +20 -1
  287. package/dist/tui-v2/app/command-handlers.js.map +1 -1
  288. package/dist/tui-v2/app/commands/picker-commands.js +3 -0
  289. package/dist/tui-v2/app/commands/picker-commands.js.map +1 -1
  290. package/dist/tui-v2/app/commands/session-commands.js +5 -0
  291. package/dist/tui-v2/app/commands/session-commands.js.map +1 -1
  292. package/dist/tui-v2/app/plan-lifecycle.d.ts +25 -3
  293. package/dist/tui-v2/app/plan-lifecycle.js +87 -24
  294. package/dist/tui-v2/app/plan-lifecycle.js.map +1 -1
  295. package/dist/tui-v2/bootstrap/patch-opentui-text.d.ts +6 -0
  296. package/dist/tui-v2/bootstrap/patch-opentui-text.js +53 -0
  297. package/dist/tui-v2/bootstrap/patch-opentui-text.js.map +1 -0
  298. package/dist/tui-v2/bootstrap/start-tui-v2.js +7 -0
  299. package/dist/tui-v2/bootstrap/start-tui-v2.js.map +1 -1
  300. package/dist/tui-v2/components/jobs/jobs-panel.js +23 -14
  301. package/dist/tui-v2/components/jobs/jobs-panel.js.map +1 -1
  302. package/dist/tui-v2/components/modal/confirm-modal.d.ts +1 -1
  303. package/dist/tui-v2/components/modal/confirm-modal.js +18 -7
  304. package/dist/tui-v2/components/modal/confirm-modal.js.map +1 -1
  305. package/dist/tui-v2/components/overlay/overlay-host.js +1 -1
  306. package/dist/tui-v2/components/overlay/overlay-host.js.map +1 -1
  307. package/dist/tui-v2/components/pager/pager.d.ts +6 -0
  308. package/dist/tui-v2/components/pager/pager.js +313 -67
  309. package/dist/tui-v2/components/pager/pager.js.map +1 -1
  310. package/dist/tui-v2/components/status/status-line.d.ts +9 -0
  311. package/dist/tui-v2/components/status/status-line.js +17 -3
  312. package/dist/tui-v2/components/status/status-line.js.map +1 -1
  313. package/dist/tui-v2/components/transcript/assistant-message.js +1 -1
  314. package/dist/tui-v2/components/transcript/assistant-message.js.map +1 -1
  315. package/dist/tui-v2/components/transcript/compacted-row.js +1 -1
  316. package/dist/tui-v2/components/transcript/compacted-row.js.map +1 -1
  317. package/dist/tui-v2/components/transcript/tool-card.d.ts +9 -1
  318. package/dist/tui-v2/components/transcript/tool-card.js +115 -10
  319. package/dist/tui-v2/components/transcript/tool-card.js.map +1 -1
  320. package/dist/tui-v2/components/transcript/transcript-row.js +2 -2
  321. package/dist/tui-v2/components/transcript/transcript-row.js.map +1 -1
  322. package/dist/tui-v2/composer/composer-editor.js +16 -0
  323. package/dist/tui-v2/composer/composer-editor.js.map +1 -1
  324. package/dist/tui-v2/controllers/overlay-controller.d.ts +16 -2
  325. package/dist/tui-v2/controllers/overlay-controller.js +92 -12
  326. package/dist/tui-v2/controllers/overlay-controller.js.map +1 -1
  327. package/dist/tui-v2/rendering/artifact-pager-source.d.ts +19 -0
  328. package/dist/tui-v2/rendering/artifact-pager-source.js +103 -0
  329. package/dist/tui-v2/rendering/artifact-pager-source.js.map +1 -0
  330. package/dist/tui-v2/rendering/file-diff-view.d.ts +63 -0
  331. package/dist/tui-v2/rendering/file-diff-view.js +263 -0
  332. package/dist/tui-v2/rendering/file-diff-view.js.map +1 -0
  333. package/dist/tui-v2/rendering/open-tool-output.d.ts +9 -1
  334. package/dist/tui-v2/rendering/open-tool-output.js +65 -12
  335. package/dist/tui-v2/rendering/open-tool-output.js.map +1 -1
  336. package/dist/tui-v2/rendering/pager-chrome.d.ts +12 -0
  337. package/dist/tui-v2/rendering/pager-chrome.js +56 -0
  338. package/dist/tui-v2/rendering/pager-chrome.js.map +1 -0
  339. package/dist/tui-v2/rendering/plan-view.js +1 -1
  340. package/dist/tui-v2/rendering/plan-view.js.map +1 -1
  341. package/dist/tui-v2/rendering/syntax-highlight.d.ts +37 -0
  342. package/dist/tui-v2/rendering/syntax-highlight.js +1303 -0
  343. package/dist/tui-v2/rendering/syntax-highlight.js.map +1 -0
  344. package/dist/tui-v2/rendering/theme.d.ts +28 -0
  345. package/dist/tui-v2/rendering/theme.js +28 -0
  346. package/dist/tui-v2/rendering/theme.js.map +1 -1
  347. package/dist/tui-v2/rendering/tool-presenter.d.ts +11 -1
  348. package/dist/tui-v2/rendering/tool-presenter.js +101 -5
  349. package/dist/tui-v2/rendering/tool-presenter.js.map +1 -1
  350. package/dist/tui-v2/state/transcript-hydrate.js +5 -0
  351. package/dist/tui-v2/state/transcript-hydrate.js.map +1 -1
  352. package/dist/tui-v2/state/transcript-reducer.js +12 -2
  353. package/dist/tui-v2/state/transcript-reducer.js.map +1 -1
  354. package/dist/tui-v2/state/transcript-store.d.ts +7 -0
  355. package/dist/tui-v2/state/transcript-store.js +46 -0
  356. package/dist/tui-v2/state/transcript-store.js.map +1 -1
  357. package/dist/tui-v2/state/transcript-types.d.ts +12 -0
  358. package/dist/tui-v2/state/transcript-types.js +9 -0
  359. package/dist/tui-v2/state/transcript-types.js.map +1 -1
  360. package/dist/types.d.ts +75 -1
  361. package/dist/ui/ansi-box.d.ts +2 -0
  362. package/dist/ui/ansi-box.js +8 -1
  363. package/dist/ui/ansi-box.js.map +1 -1
  364. package/dist/ui/mentions.d.ts +6 -0
  365. package/dist/ui/mentions.js +50 -12
  366. package/dist/ui/mentions.js.map +1 -1
  367. package/dist/ui/plan-pane.js +1 -1
  368. package/dist/ui/plan-pane.js.map +1 -1
  369. package/package.json +16 -11
@@ -0,0 +1,278 @@
1
+ /** Max argument JSON size while streaming (~32 MB). */
2
+ export const MAX_TOOL_ARG_BYTES = 32 * 1024 * 1024;
3
+ /** Primary wire form: dots → underscores, keep camelCase (fs.writeMany → fs_writeMany). */
4
+ export function toWireName(canonical) {
5
+ return canonical.replace(/\./g, "_");
6
+ }
7
+ /**
8
+ * Full snake_case wire form models sometimes emit
9
+ * (fs.writeMany → fs_write_many). Used as a reverse alias only.
10
+ */
11
+ export function toSnakeWireName(canonical) {
12
+ return canonical
13
+ .replace(/([a-z0-9])([A-Z])/g, "$1_$2")
14
+ .replace(/\./g, "_")
15
+ .toLowerCase();
16
+ }
17
+ /**
18
+ * Reverse wire → canonical for known tools. Unknown wires return undefined
19
+ * so the runner can surface a clear error.
20
+ */
21
+ const wireToCanonical = new Map();
22
+ export function registerWireName(canonical, wire) {
23
+ const w = wire ?? toWireName(canonical);
24
+ wireToCanonical.set(w, canonical);
25
+ }
26
+ /** Register primary wire + snake_case alias when they differ. */
27
+ export function registerWireNamesFor(canonical) {
28
+ const wire = toWireName(canonical);
29
+ registerWireName(canonical, wire);
30
+ const snake = toSnakeWireName(canonical);
31
+ if (snake !== wire) {
32
+ registerWireName(canonical, snake);
33
+ }
34
+ return wire;
35
+ }
36
+ /**
37
+ * Strip model channel / commentary / XML junk from tool names
38
+ * (e.g. `fs.write<|channel|>commentary` → `fs.write`).
39
+ */
40
+ export function sanitizeToolName(raw) {
41
+ let n = raw.trim();
42
+ if (!n)
43
+ return n;
44
+ // GPT-OSS / channel style: <|channel|>, <|tool_call_begin|>, …
45
+ n = n.replace(/<\|[^|]*\|>/g, "");
46
+ // XML-ish tags
47
+ n = n.replace(/<\/?[A-Za-z][^>]*>/g, "");
48
+ n = n.replace(/^(?:functions\.|tools\.|tool\.)/i, "");
49
+ n = n.replace(/:\d+$/, "");
50
+ // Drop trailing non-name junk after a clean dotted/underscored stem
51
+ n = n.replace(/[^A-Za-z0-9._-]+.*$/, "");
52
+ // Role words models glue after tags (fs.writecommentary)
53
+ n = n.replace(/(?:commentary|analysis|channel|tool_call|toolcall|final)$/i, "");
54
+ n = n.replace(/(?:[_./-]+(?:commentary|analysis|channel|final))+$/i, "");
55
+ n = n.trim().replace(/^[_./-]+|[_./-]+$/g, "");
56
+ return n;
57
+ }
58
+ /**
59
+ * Longest registered wire/canonical prefix match for polluted names.
60
+ * e.g. `fs_write_channel_commentary` → `fs_write` if registered.
61
+ */
62
+ function longestRegisteredPrefix(cleaned) {
63
+ if (!cleaned)
64
+ return undefined;
65
+ if (wireToCanonical.has(cleaned))
66
+ return cleaned;
67
+ // Try stripping trailing segments after _ or .
68
+ let best;
69
+ for (const [wire] of wireToCanonical) {
70
+ if (cleaned === wire ||
71
+ cleaned.startsWith(wire + "_") ||
72
+ cleaned.startsWith(wire + ".") ||
73
+ cleaned.startsWith(wire + "<")) {
74
+ if (!best || wire.length > best.length)
75
+ best = wire;
76
+ }
77
+ }
78
+ return best;
79
+ }
80
+ export function fromWireName(wire) {
81
+ const cleaned = sanitizeToolName(wire);
82
+ if (!cleaned)
83
+ return undefined;
84
+ if (wireToCanonical.has(cleaned))
85
+ return wireToCanonical.get(cleaned);
86
+ // Polluted name that still starts with a registered wire form
87
+ const prefix = longestRegisteredPrefix(cleaned);
88
+ if (prefix)
89
+ return wireToCanonical.get(prefix);
90
+ // Also try original if sanitize changed nothing useful
91
+ if (wireToCanonical.has(wire))
92
+ return wireToCanonical.get(wire);
93
+ // Fallback: underscore → first-dot heuristic for unregistered names.
94
+ if (cleaned.includes("."))
95
+ return cleaned;
96
+ const idx = cleaned.indexOf("_");
97
+ if (idx <= 0)
98
+ return undefined;
99
+ return `${cleaned.slice(0, idx)}.${cleaned.slice(idx + 1).replace(/_/g, ".")}`;
100
+ }
101
+ export function parseToolArguments(raw) {
102
+ if (raw == null)
103
+ return {};
104
+ if (typeof raw === "object" && !Array.isArray(raw)) {
105
+ return raw;
106
+ }
107
+ if (typeof raw === "string") {
108
+ const t = raw.trim();
109
+ if (!t)
110
+ return {};
111
+ try {
112
+ const parsed = JSON.parse(t);
113
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
114
+ return parsed;
115
+ }
116
+ return { _parseError: true, _raw: t };
117
+ }
118
+ catch {
119
+ return { _parseError: true, _raw: t };
120
+ }
121
+ }
122
+ return {};
123
+ }
124
+ let syntheticToolCallSequence = 0;
125
+ export function syntheticToolCallId(index) {
126
+ syntheticToolCallSequence += 1;
127
+ return `call_${index}_${Date.now().toString(36)}_${syntheticToolCallSequence.toString(36)}`;
128
+ }
129
+ export function isToolsUnsupportedError(error) {
130
+ const status = error && typeof error === "object" && "status" in error
131
+ ? Number(error.status)
132
+ : undefined;
133
+ const body = error && typeof error === "object" && "body" in error
134
+ ? String(error.body ?? "")
135
+ : "";
136
+ const message = error instanceof Error ? error.message : String(error);
137
+ const hay = `${message}\n${body}`.toLowerCase();
138
+ // Explicit capability rejections (any HTTP status).
139
+ if (/tools?\s+(is|are)\s+not\s+supported|does not support tools|function calling is not enabled|tool[_ ]?use is not supported|tools? not supported|tool calling is not supported|does not support function|function[_ ]?call(ing)? (is )?(not supported|disabled|unavailable)|unknown (request )?parameter ['"]?tools?|['"]tools?['"] is not (a )?valid|tool_choice.*(not supported|unknown|disabled)/i.test(hay)) {
140
+ return true;
141
+ }
142
+ // 400 bodies that mention tools — only when they clearly mean unsupported.
143
+ // Do NOT treat schema/arg validation as unsupported (no bare /tool/i).
144
+ if (status === 400 &&
145
+ /\btools?\b|\btool_choice\b|\bfunction[_ ]?call/i.test(hay)) {
146
+ if (/invalid schema|invalid argument|missing required|parse error|type error|validation|must be|expected/i.test(hay)) {
147
+ return false;
148
+ }
149
+ return /not support|unsupported|disabled|not enabled|not available|unknown parameter|unrecognized/i.test(hay);
150
+ }
151
+ return false;
152
+ }
153
+ export function mapToolChoiceToOpenAi(choice) {
154
+ if (choice === undefined || choice === "auto")
155
+ return "auto";
156
+ if (choice === "none")
157
+ return "none";
158
+ if (choice === "required")
159
+ return "required";
160
+ if (typeof choice === "object" && choice.type === "function") {
161
+ return {
162
+ type: "function",
163
+ function: { name: toWireName(choice.name) },
164
+ };
165
+ }
166
+ return "auto";
167
+ }
168
+ export function mapToolChoiceToAnthropic(choice) {
169
+ if (choice === undefined || choice === "auto")
170
+ return { type: "auto" };
171
+ if (choice === "none")
172
+ return { type: "none" };
173
+ if (choice === "required")
174
+ return { type: "any" };
175
+ if (typeof choice === "object" && choice.type === "function") {
176
+ return { type: "tool", name: toWireName(choice.name) };
177
+ }
178
+ return { type: "auto" };
179
+ }
180
+ export function mapToolChoiceToGemini(choice) {
181
+ if (choice === "none")
182
+ return { mode: "NONE" };
183
+ if (choice === "required")
184
+ return { mode: "ANY" };
185
+ if (typeof choice === "object" && choice.type === "function") {
186
+ return {
187
+ mode: "ANY",
188
+ allowedFunctionNames: [toWireName(choice.name)],
189
+ };
190
+ }
191
+ return { mode: "AUTO" };
192
+ }
193
+ /** Session-sticky models forced to text protocol after tools-unsupported. */
194
+ const textOnlyModels = new Set();
195
+ export function textOnlyKey(provider, model) {
196
+ return `${provider}::${model}`;
197
+ }
198
+ export function markTextOnlyModel(provider, model) {
199
+ textOnlyModels.add(textOnlyKey(provider, model));
200
+ }
201
+ export function isTextOnlyModel(provider, model) {
202
+ return textOnlyModels.has(textOnlyKey(provider, model));
203
+ }
204
+ export function clearTextOnlyModels() {
205
+ textOnlyModels.clear();
206
+ }
207
+ /** Accumulate OpenAI-style streaming tool_calls deltas by index. */
208
+ export function accumulateOpenAiToolCallDelta(state, entry) {
209
+ const index = entry.index ?? 0;
210
+ let acc = state.get(index);
211
+ if (!acc) {
212
+ acc = { arguments: "" };
213
+ state.set(index, acc);
214
+ }
215
+ const hadName = Boolean(acc.name);
216
+ if (entry.id)
217
+ acc.id = entry.id;
218
+ if (entry.function?.name) {
219
+ const nextName = sanitizeToolName(entry.function.name) || entry.function.name;
220
+ // X2: if this index already has a different clean name, do not clobber
221
+ // args with a second tool's payload — keep the first name.
222
+ if (acc.name &&
223
+ nextName &&
224
+ acc.name !== nextName &&
225
+ sanitizeToolName(acc.name) !== sanitizeToolName(nextName)) {
226
+ // Ignore subsequent name flips on the same index (stream corruption).
227
+ }
228
+ else {
229
+ acc.name = nextName;
230
+ }
231
+ }
232
+ if (typeof entry.function?.arguments === "string") {
233
+ acc.arguments += entry.function.arguments;
234
+ if (acc.arguments.length > MAX_TOOL_ARG_BYTES) {
235
+ throw new Error(`Tool call arguments exceeded ${MAX_TOOL_ARG_BYTES} bytes — split the file or reduce content size.`);
236
+ }
237
+ }
238
+ return {
239
+ index,
240
+ ...(acc.id !== undefined ? { id: acc.id } : {}),
241
+ ...(acc.name !== undefined ? { name: acc.name } : {}),
242
+ nameBecameKnown: Boolean(acc.name) && !hadName,
243
+ argumentsBytes: acc.arguments.length,
244
+ };
245
+ }
246
+ export function finalizeOpenAiToolCalls(state) {
247
+ const indices = [...state.keys()].sort((a, b) => a - b);
248
+ const out = [];
249
+ for (const index of indices) {
250
+ const acc = state.get(index);
251
+ const wire = acc.name ?? "";
252
+ const canonical = fromWireName(wire) ?? wire;
253
+ const rawArguments = acc.arguments;
254
+ const args = parseToolArguments(rawArguments);
255
+ out.push({
256
+ id: acc.id ?? syntheticToolCallId(index),
257
+ name: canonical,
258
+ args,
259
+ ...(rawArguments ? { rawArguments } : {}),
260
+ });
261
+ }
262
+ return out;
263
+ }
264
+ export function parseOpenAiMessageToolCalls(toolCalls) {
265
+ if (!toolCalls?.length)
266
+ return [];
267
+ return toolCalls.map((tc, i) => {
268
+ const wire = tc.function?.name ?? "";
269
+ const rawArguments = tc.function?.arguments ?? "";
270
+ return {
271
+ id: tc.id ?? syntheticToolCallId(i),
272
+ name: fromWireName(wire) ?? wire,
273
+ args: parseToolArguments(rawArguments),
274
+ ...(rawArguments ? { rawArguments } : {}),
275
+ };
276
+ });
277
+ }
278
+ //# sourceMappingURL=tool-protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-protocol.js","sourceRoot":"","sources":["../../src/llm/tool-protocol.ts"],"names":[],"mappings":"AAaA,uDAAuD;AACvD,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAEnD,2FAA2F;AAC3F,MAAM,UAAU,UAAU,CAAC,SAAiB;IAC1C,OAAO,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AACvC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,SAAiB;IAC/C,OAAO,SAAS;SACb,OAAO,CAAC,oBAAoB,EAAE,OAAO,CAAC;SACtC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,WAAW,EAAE,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,MAAM,eAAe,GAAG,IAAI,GAAG,EAAkB,CAAC;AAElD,MAAM,UAAU,gBAAgB,CAAC,SAAiB,EAAE,IAAa;IAC/D,MAAM,CAAC,GAAG,IAAI,IAAI,UAAU,CAAC,SAAS,CAAC,CAAC;IACxC,eAAe,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;AACpC,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,oBAAoB,CAAC,SAAiB;IACpD,MAAM,IAAI,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC;IACnC,gBAAgB,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAClC,MAAM,KAAK,GAAG,eAAe,CAAC,SAAS,CAAC,CAAC;IACzC,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,gBAAgB,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IACrC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,IAAI,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IACnB,IAAI,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC;IACjB,+DAA+D;IAC/D,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;IAClC,eAAe;IACf,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,qBAAqB,EAAE,EAAE,CAAC,CAAC;IACzC,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,kCAAkC,EAAE,EAAE,CAAC,CAAC;IACtD,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAC3B,oEAAoE;IACpE,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,qBAAqB,EAAE,EAAE,CAAC,CAAC;IACzC,yDAAyD;IACzD,CAAC,GAAG,CAAC,CAAC,OAAO,CACX,4DAA4D,EAC5D,EAAE,CACH,CAAC;IACF,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,qDAAqD,EAAE,EAAE,CAAC,CAAC;IACzE,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,oBAAoB,EAAE,EAAE,CAAC,CAAC;IAC/C,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;;GAGG;AACH,SAAS,uBAAuB,CAAC,OAAe;IAC9C,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IAC/B,IAAI,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC;QAAE,OAAO,OAAO,CAAC;IACjD,+CAA+C;IAC/C,IAAI,IAAwB,CAAC;IAC7B,KAAK,MAAM,CAAC,IAAI,CAAC,IAAI,eAAe,EAAE,CAAC;QACrC,IACE,OAAO,KAAK,IAAI;YAChB,OAAO,CAAC,UAAU,CAAC,IAAI,GAAG,GAAG,CAAC;YAC9B,OAAO,CAAC,UAAU,CAAC,IAAI,GAAG,GAAG,CAAC;YAC9B,OAAO,CAAC,UAAU,CAAC,IAAI,GAAG,GAAG,CAAC,EAC9B,CAAC;YACD,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM;gBAAE,IAAI,GAAG,IAAI,CAAC;QACtD,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,MAAM,OAAO,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IAC/B,IAAI,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC;QAAE,OAAO,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACtE,8DAA8D;IAC9D,MAAM,MAAM,GAAG,uBAAuB,CAAC,OAAO,CAAC,CAAC;IAChD,IAAI,MAAM;QAAE,OAAO,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC/C,uDAAuD;IACvD,IAAI,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAChE,qEAAqE;IACrE,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,OAAO,CAAC;IAC1C,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,GAAG,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IAC/B,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;AACjF,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,GAAY;IAC7C,IAAI,GAAG,IAAI,IAAI;QAAE,OAAO,EAAE,CAAC;IAC3B,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACnD,OAAO,GAA8B,CAAC;IACxC,CAAC;IACD,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;QACrB,IAAI,CAAC,CAAC;YAAE,OAAO,EAAE,CAAC;QAClB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAY,CAAC;YACxC,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;gBACnE,OAAO,MAAiC,CAAC;YAC3C,CAAC;YACD,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QACxC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QACxC,CAAC;IACH,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,IAAI,yBAAyB,GAAG,CAAC,CAAC;AAElC,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAC/C,yBAAyB,IAAI,CAAC,CAAC;IAC/B,OAAO,QAAQ,KAAK,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,yBAAyB,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;AAC9F,CAAC;AAGD,MAAM,UAAU,uBAAuB,CAAC,KAAc;IACpD,MAAM,MAAM,GACV,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,QAAQ,IAAI,KAAK;QACrD,CAAC,CAAC,MAAM,CAAE,KAA6B,CAAC,MAAM,CAAC;QAC/C,CAAC,CAAC,SAAS,CAAC;IAChB,MAAM,IAAI,GACR,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,IAAI,KAAK;QACnD,CAAC,CAAC,MAAM,CAAE,KAA2B,CAAC,IAAI,IAAI,EAAE,CAAC;QACjD,CAAC,CAAC,EAAE,CAAC;IACT,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACvE,MAAM,GAAG,GAAG,GAAG,OAAO,KAAK,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAEhD,oDAAoD;IACpD,IACE,mYAAmY,CAAC,IAAI,CACtY,GAAG,CACJ,EACD,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,2EAA2E;IAC3E,uEAAuE;IACvE,IACE,MAAM,KAAK,GAAG;QACd,iDAAiD,CAAC,IAAI,CAAC,GAAG,CAAC,EAC3D,CAAC;QACD,IACE,sGAAsG,CAAC,IAAI,CACzG,GAAG,CACJ,EACD,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;QACD,OAAO,4FAA4F,CAAC,IAAI,CACtG,GAAG,CACJ,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,qBAAqB,CACnC,MAA8B;IAE9B,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,MAAM;QAAE,OAAO,MAAM,CAAC;IAC7D,IAAI,MAAM,KAAK,MAAM;QAAE,OAAO,MAAM,CAAC;IACrC,IAAI,MAAM,KAAK,UAAU;QAAE,OAAO,UAAU,CAAC;IAC7C,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QAC7D,OAAO;YACL,IAAI,EAAE,UAAU;YAChB,QAAQ,EAAE,EAAE,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE;SAC5C,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,wBAAwB,CACtC,MAA8B;IAE9B,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,MAAM;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IACvE,IAAI,MAAM,KAAK,MAAM;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAC/C,IAAI,MAAM,KAAK,UAAU;QAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IAClD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QAC7D,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;IACzD,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,qBAAqB,CACnC,MAA8B;IAE9B,IAAI,MAAM,KAAK,MAAM;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAC/C,IAAI,MAAM,KAAK,UAAU;QAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IAClD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QAC7D,OAAO;YACL,IAAI,EAAE,KAAK;YACX,oBAAoB,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;SAChD,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AAC1B,CAAC;AAED,6EAA6E;AAC7E,MAAM,cAAc,GAAG,IAAI,GAAG,EAAU,CAAC;AAEzC,MAAM,UAAU,WAAW,CAAC,QAAoB,EAAE,KAAa;IAC7D,OAAO,GAAG,QAAQ,KAAK,KAAK,EAAE,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,QAAoB,EAAE,KAAa;IACnE,cAAc,CAAC,GAAG,CAAC,WAAW,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,QAAoB,EAAE,KAAa;IACjE,OAAO,cAAc,CAAC,GAAG,CAAC,WAAW,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,mBAAmB;IACjC,cAAc,CAAC,KAAK,EAAE,CAAC;AACzB,CAAC;AAiBD,oEAAoE;AACpE,MAAM,UAAU,6BAA6B,CAC3C,KAA6C,EAC7C,KAKC;IAED,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC;IAC/B,IAAI,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC3B,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,GAAG,GAAG,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;QACxB,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACxB,CAAC;IACD,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,KAAK,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC;IAChC,IAAI,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,gBAAgB,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;QAC9E,uEAAuE;QACvE,2DAA2D;QAC3D,IACE,GAAG,CAAC,IAAI;YACR,QAAQ;YACR,GAAG,CAAC,IAAI,KAAK,QAAQ;YACrB,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,gBAAgB,CAAC,QAAQ,CAAC,EACzD,CAAC;YACD,sEAAsE;QACxE,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,IAAI,GAAG,QAAQ,CAAC;QACtB,CAAC;IACH,CAAC;IACD,IAAI,OAAO,KAAK,CAAC,QAAQ,EAAE,SAAS,KAAK,QAAQ,EAAE,CAAC;QAClD,GAAG,CAAC,SAAS,IAAI,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC;QAC1C,IAAI,GAAG,CAAC,SAAS,CAAC,MAAM,GAAG,kBAAkB,EAAE,CAAC;YAC9C,MAAM,IAAI,KAAK,CACb,gCAAgC,kBAAkB,iDAAiD,CACpG,CAAC;QACJ,CAAC;IACH,CAAC;IACD,OAAO;QACL,KAAK;QACL,GAAG,CAAC,GAAG,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/C,GAAG,CAAC,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACrD,eAAe,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO;QAC9C,cAAc,EAAE,GAAG,CAAC,SAAS,CAAC,MAAM;KACrC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,uBAAuB,CACrC,KAA6C;IAE7C,MAAM,OAAO,GAAG,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACxD,MAAM,GAAG,GAAqB,EAAE,CAAC;IACjC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK,CAAE,CAAC;QAC9B,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC;QAC5B,MAAM,SAAS,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;QAC7C,MAAM,YAAY,GAAG,GAAG,CAAC,SAAS,CAAC;QACnC,MAAM,IAAI,GAAG,kBAAkB,CAAC,YAAY,CAAC,CAAC;QAC9C,GAAG,CAAC,IAAI,CAAC;YACP,EAAE,EAAE,GAAG,CAAC,EAAE,IAAI,mBAAmB,CAAC,KAAK,CAAC;YACxC,IAAI,EAAE,SAAS;YACf,IAAI;YACJ,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC1C,CAAC,CAAC;IACL,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,2BAA2B,CACzC,SAMa;IAEb,IAAI,CAAC,SAAS,EAAE,MAAM;QAAE,OAAO,EAAE,CAAC;IAClC,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE;QAC7B,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,EAAE,IAAI,IAAI,EAAE,CAAC;QACrC,MAAM,YAAY,GAAG,EAAE,CAAC,QAAQ,EAAE,SAAS,IAAI,EAAE,CAAC;QAClD,OAAO;YACL,EAAE,EAAE,EAAE,CAAC,EAAE,IAAI,mBAAmB,CAAC,CAAC,CAAC;YACnC,IAAI,EAAE,YAAY,CAAC,IAAI,CAAC,IAAI,IAAI;YAChC,IAAI,EAAE,kBAAkB,CAAC,YAAY,CAAC;YACtC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC1C,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -1,4 +1,4 @@
1
- import type { ChatMessage, ChatImage, ProviderId, ToolCall } from "../types.js";
1
+ import type { ChatMessage, ChatImage, Mode, ProviderId, ToolCall } from "../types.js";
2
2
  import type { AgentEvent } from "../agent/events.js";
3
3
  import { parseToolCall, createSessionPolicy, type ConfirmPort, type SessionPolicy } from "../agent/runner.js";
4
4
  export interface AgentOptions {
@@ -23,6 +23,7 @@ export interface AgentOptions {
23
23
  onEvent?: ((event: AgentEvent) => void) | undefined;
24
24
  onMessages?: ((messages: ChatMessage[]) => void) | undefined;
25
25
  confirm?: ConfirmPort | undefined;
26
+ mode?: Mode | undefined;
26
27
  }
27
28
  export { parseToolCall, createSessionPolicy };
28
29
  export type { SessionPolicy };
@@ -1,6 +1,8 @@
1
- import { runAgentLoop, parseToolCall, createSessionPolicy, } from "../agent/runner.js";
1
+ import { renderTurnOutcome } from "../agent/turn-outcome.js";
2
+ import { runAgentTurn, parseToolCall, createSessionPolicy, } from "../agent/runner.js";
2
3
  export { parseToolCall, createSessionPolicy };
3
4
  export async function runAgent(prompt, options = {}) {
4
- return runAgentLoop(prompt, options);
5
+ const outcome = await runAgentTurn(prompt, options);
6
+ return renderTurnOutcome(outcome);
5
7
  }
6
8
  //# sourceMappingURL=agent.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"agent.js","sourceRoot":"","sources":["../../src/modes/agent.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,YAAY,EACZ,aAAa,EACb,mBAAmB,GAGpB,MAAM,oBAAoB,CAAC;AAwB5B,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,CAAC;AAG9C,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,MAAc,EACd,UAAwB,EAAE;IAE1B,OAAO,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AACvC,CAAC"}
1
+ {"version":3,"file":"agent.js","sourceRoot":"","sources":["../../src/modes/agent.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EACL,YAAY,EACZ,aAAa,EACb,mBAAmB,GAGpB,MAAM,oBAAoB,CAAC;AAyB5B,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,CAAC;AAG9C,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,MAAc,EACd,UAAwB,EAAE;IAE1B,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpD,OAAO,iBAAiB,CAAC,OAAO,CAAC,CAAC;AACpC,CAAC"}
@@ -1,11 +1,5 @@
1
1
  import type { ChatMessage, ChatImage, ProviderId } from "../types.js";
2
2
  import type { AgentEvent } from "../agent/events.js";
3
- /**
4
- * Signal raised when the model, while in ask mode, tries to call a mutating
5
- * tool (run a command, write a file, install a package, …). Ask mode is
6
- * read-only, so instead of leaking the raw tool-call JSON as the "answer" we
7
- * surface this so the caller can offer to switch into agent mode.
8
- */
9
3
  export interface AskActionRequired {
10
4
  /** The original prompt, so the caller can re-run it in agent mode. */
11
5
  prompt: string;
@@ -20,19 +14,7 @@ export interface AskOptions {
20
14
  history?: ChatMessage[] | undefined;
21
15
  signal?: AbortSignal | undefined;
22
16
  images?: ChatImage[] | undefined;
23
- /**
24
- * Invoked when the task needs actions ask mode can't perform. When set, the
25
- * research loop returns an empty string and lets the caller drive the
26
- * follow-up (e.g. prompt to switch to agent mode). When unset, ask mode
27
- * falls back to a clean explanatory message instead of raw tool-call text.
28
- */
29
17
  onActionRequired?: ((info: AskActionRequired) => void) | undefined;
30
- /**
31
- * Optional event sink for live research activity. Ask mode emits
32
- * `tool-call`/`tool-result` events for each read-only research tool it runs
33
- * (web.search, web.fetch, …) so the UI can show what it's searching or
34
- * fetching, mirroring agent-mode tool cards.
35
- */
36
18
  onEvent?: ((event: AgentEvent) => void) | undefined;
37
19
  }
38
20
  export declare function runAsk(prompt: string, options?: AskOptions): Promise<string>;
package/dist/modes/ask.js CHANGED
@@ -1,10 +1,14 @@
1
1
  import { streamWithProvider } from "../llm/router.js";
2
+ import { resolveToolDialect } from "../llm/capabilities.js";
3
+ import { syntheticToolCallId } from "../llm/tool-protocol.js";
2
4
  import { renderAskSystemPrompt } from "../prompts/index.js";
3
5
  import { getConfig } from "../store/config.js";
4
6
  import { ensureProviderConfigured } from "../commands/providers.js";
5
7
  import { loadProjectContext } from "../store/project.js";
6
8
  import { parseAllToolCalls, formatToolArgs, looksLikePromptLeak } from "../agent/runner.js";
7
9
  import { runToolCall } from "../tools/registry.js";
10
+ import { getToolDefinitions } from "../tools/definitions.js";
11
+ import { appendAssistantWithTools, appendToolResult, } from "../agent/tool-history.js";
8
12
  /** Strip tool-call markup so only the model's prose preamble remains. */
9
13
  function stripToolCallSyntax(text) {
10
14
  return text
@@ -42,12 +46,6 @@ function researchResultSummary(call, ok) {
42
46
  return "done";
43
47
  }
44
48
  }
45
- /**
46
- * Read-only tools ask mode is allowed to call during its research loop.
47
- * Everything here is non-mutating: web lookups and local file inspection
48
- * only. Ask mode never runs shell commands, installs packages, or writes
49
- * files.
50
- */
51
49
  const ASK_RESEARCH_TOOLS = new Set([
52
50
  "web.search",
53
51
  "web.fetch",
@@ -88,17 +86,19 @@ async function buildAskMessages(prompt, options) {
88
86
  const config = getConfig();
89
87
  const provider = options.provider ?? config.defaultProvider;
90
88
  await ensureProviderConfigured(provider);
89
+ const model = options.model ?? config.defaultModel;
91
90
  const projectContext = await loadProjectContext();
91
+ const native = resolveToolDialect(provider, model, config.toolCalling) !== "none";
92
92
  const systemPrompt = projectContext
93
- ? `${renderAskSystemPrompt()}\n\nProject context from .clai/context.md:\n${projectContext}`
94
- : renderAskSystemPrompt();
93
+ ? `${renderAskSystemPrompt({ nativeTools: native })}\n\nProject context from .clai/context.md:\n${projectContext}`
94
+ : renderAskSystemPrompt({ nativeTools: native });
95
95
  const userMessage = { role: "user", content: prompt };
96
96
  if (options.images && options.images.length > 0) {
97
97
  userMessage.images = options.images;
98
98
  }
99
99
  return {
100
100
  provider,
101
- model: options.model ?? config.defaultModel,
101
+ model,
102
102
  messages: [
103
103
  { role: "system", content: systemPrompt },
104
104
  ...(options.history ?? []),
@@ -106,13 +106,6 @@ async function buildAskMessages(prompt, options) {
106
106
  ],
107
107
  };
108
108
  }
109
- /**
110
- * Find where (if anywhere) a tool-call block begins in `text`. Ask mode
111
- * streams each model round to the live display, but a round may turn out to
112
- * be a tool call rather than prose. We mirror tokens to the screen only up to
113
- * the point a tool-call delimiter appears, so raw tool JSON never streams to
114
- * the user. Returns -1 when no tool-call marker is present.
115
- */
116
109
  function toolCallStartIndex(text) {
117
110
  const indicators = [
118
111
  /```\s*tool/i,
@@ -130,24 +123,18 @@ function toolCallStartIndex(text) {
130
123
  }
131
124
  return min;
132
125
  }
133
- /**
134
- * Run one model round as a stream, mirroring visible prose tokens to `onToken`
135
- * as they arrive (so the user sees the answer build up live) while suppressing
136
- * anything from a tool-call delimiter onward. Returns the round's full raw
137
- * text so the caller can detect/parse tool calls. Providers without a native
138
- * stream fall back to a single onToken call inside streamWithProvider.
139
- */
140
126
  async function streamAskRound(request, messages, onToken) {
141
127
  let full = "";
142
128
  let forwardedLen = 0;
143
129
  let suppressed = false;
144
- await streamWithProvider({ ...request, messages }, (token) => {
130
+ const completion = await streamWithProvider({ ...request, messages }, (token) => {
145
131
  full += token;
146
132
  if (suppressed)
147
133
  return;
134
+ // When native tools are attached, do not suppress prose on fence markers
135
+ // (there should be none); still suppress if the model emits fences anyway.
148
136
  const toolAt = toolCallStartIndex(full);
149
137
  if (toolAt >= 0) {
150
- // Forward only the clean prefix before the tool-call marker, once.
151
138
  if (toolAt > forwardedLen)
152
139
  onToken(full.slice(forwardedLen, toolAt));
153
140
  forwardedLen = full.length;
@@ -159,23 +146,30 @@ async function streamAskRound(request, messages, onToken) {
159
146
  forwardedLen = full.length;
160
147
  }
161
148
  });
162
- return full;
149
+ const text = full || completion.text;
150
+ if (completion.toolCalls?.length) {
151
+ return {
152
+ text,
153
+ toolCalls: completion.toolCalls.map((tc) => ({
154
+ name: tc.name,
155
+ args: tc.args,
156
+ })),
157
+ nativeIds: completion.toolCalls.map((tc) => tc.id),
158
+ };
159
+ }
160
+ return { text };
163
161
  }
164
- /**
165
- * Drive ask mode to its final answer.
166
- *
167
- * Ask mode is non-agentic for the user — there is no plan, no confirmations,
168
- * no system changes — but it MAY ground its answer in current facts via a
169
- * bounded loop of read-only tools (web.search/web.fetch/tool.batch and
170
- * read-only fs.*). Each round is streamed to `onToken`: prose answers appear
171
- * live, and rounds that turn out to be tool calls are suppressed mid-stream so
172
- * raw tool JSON never reaches the screen. The returned string is always the
173
- * clean final answer, which callers treat as authoritative even if the live
174
- * stream briefly showed a tool-call preamble.
175
- */
176
162
  async function resolveAskAnswer(originalPrompt, provider, model, messages, options, onToken) {
177
163
  const config = getConfig();
178
164
  const maxTokens = config.thinking?.enabled ? 8_192 : 4_096;
165
+ const native = resolveToolDialect(provider, model, config.toolCalling) !== "none";
166
+ // Research tools + agent.handoff (meta; not executed as research — action UX).
167
+ const askDefs = native
168
+ ? [
169
+ ...getToolDefinitions({ askMode: true }).filter((d) => ASK_RESEARCH_TOOLS.has(d.name)),
170
+ ...getToolDefinitions({ names: ["agent.handoff"] }),
171
+ ]
172
+ : undefined;
179
173
  const baseRequest = {
180
174
  provider,
181
175
  model,
@@ -183,6 +177,9 @@ async function resolveAskAnswer(originalPrompt, provider, model, messages, optio
183
177
  maxTokens,
184
178
  thinking: config.thinking,
185
179
  ...(options.signal ? { signal: options.signal } : {}),
180
+ ...(askDefs?.length
181
+ ? { tools: askDefs, toolChoice: "auto" }
182
+ : {}),
186
183
  };
187
184
  // Surface research activity (web.search/web.fetch/…) to the UI as tool
188
185
  // events so the user can see what's being searched/fetched.
@@ -219,34 +216,26 @@ async function resolveAskAnswer(originalPrompt, provider, model, messages, optio
219
216
  role: "user",
220
217
  content: `Fresh web.search was run before answering because the user requested current/web-backed information.\n` +
221
218
  `Query: ${query}\nResult:\n${truncateToolOutput(output, "web.search")}\n\n` +
222
- "Answer from these current results. If the result is insufficient or contradictory, call web.search/web.fetch again before answering. Cite URLs you used.",
219
+ "Answer from these current results. Prefer high-trust hosts (.gov, major news). Treat a single junk/contradictory snippet as unverified until confirmed. If insufficient, call web.search/web.fetch again before answering. Your final answer MUST cite 1–3 URLs from tool results.",
223
220
  });
224
221
  }
225
222
  for (let round = 0; round < ASK_MAX_RESEARCH_ROUNDS; round += 1) {
226
223
  options.signal?.throwIfAborted();
227
- const text = await streamAskRound(baseRequest, messages, onToken);
228
- // ── Prompt-leak guard ──────────────────────────────────────────────
229
- // If the model's response contains distinctive system-prompt markers,
230
- // it is repeating its instructions (e.g. in response to "repeat your
231
- // instructions verbatim"). Any ```tool blocks in that output are
232
- // EXAMPLES from the prompt, not real tool requests. Treat the whole
233
- // response as a final text answer to avoid executing stale examples.
224
+ const roundResult = await streamAskRound(baseRequest, messages, onToken);
225
+ const text = roundResult.text;
234
226
  if (looksLikePromptLeak(text)) {
235
227
  return text;
236
228
  }
237
- const allCalls = parseAllToolCalls(text);
229
+ // Prefer native toolCalls; fall back to text fences.
230
+ const allCalls = roundResult.toolCalls?.length
231
+ ? roundResult.toolCalls
232
+ : parseAllToolCalls(text);
233
+ const nativeIds = roundResult.nativeIds ??
234
+ allCalls.map((_, i) => syntheticToolCallId(i));
238
235
  const calls = allCalls.filter((call) => ASK_RESEARCH_TOOLS.has(call.name));
239
236
  if (calls.length === 0) {
240
- // No allowed research tool requested. If the model instead asked for a
241
- // mutating/action tool, this is an agent task — surface it rather than
242
- // returning the raw tool-call JSON as the answer.
243
237
  const actionCalls = allCalls.filter((call) => !ASK_RESEARCH_TOOLS.has(call.name));
244
238
  if (actionCalls.length > 0) {
245
- // The model signals it wants to act either via an explicit
246
- // `agent.handoff` call (preferred — see the ask system prompt) or by
247
- // emitting a real mutating tool call. Pull the reason out of a handoff
248
- // when present, and never surface "agent.handoff" itself as a tool the
249
- // user would recognize.
250
239
  const handoff = actionCalls.find((call) => call.name === "agent.handoff" || call.name === "agent.run");
251
240
  const reason = handoff && typeof handoff.args.reason === "string"
252
241
  ? handoff.args.reason.trim()
@@ -279,8 +268,19 @@ async function resolveAskAnswer(originalPrompt, provider, model, messages, optio
279
268
  }
280
269
  // Record the model's tool-call turn, then run the read-only tools and
281
270
  // feed their outputs back so the next round can synthesize.
282
- messages.push({ role: "assistant", content: text });
283
- for (const call of calls.slice(0, ASK_MAX_TOOLS_PER_ROUND)) {
271
+ if (roundResult.toolCalls?.length) {
272
+ appendAssistantWithTools(messages, stripToolCallSyntax(text), roundResult.toolCalls.map((c, i) => ({
273
+ id: nativeIds[i] ?? syntheticToolCallId(i),
274
+ name: c.name,
275
+ args: c.args,
276
+ })));
277
+ }
278
+ else {
279
+ messages.push({ role: "assistant", content: text });
280
+ }
281
+ for (const [callIndex, call] of calls
282
+ .slice(0, ASK_MAX_TOOLS_PER_ROUND)
283
+ .entries()) {
284
284
  options.signal?.throwIfAborted();
285
285
  const id = `ask-${(toolSeq += 1)}`;
286
286
  emit({
@@ -308,19 +308,27 @@ async function resolveAskAnswer(originalPrompt, provider, model, messages, optio
308
308
  ok,
309
309
  summary: researchResultSummary(call, ok),
310
310
  });
311
- messages.push({
312
- role: "user",
313
- content: `Result of ${call.name}(${JSON.stringify(call.args)}):\n${truncateToolOutput(output, call.name)}`,
314
- });
311
+ const resultBody = `Result of ${call.name}(${JSON.stringify(call.args)}):\n${truncateToolOutput(output, call.name)}`;
312
+ if (roundResult.toolCalls?.length) {
313
+ const sourceIndex = allCalls.indexOf(call);
314
+ appendToolResult(messages, nativeIds[sourceIndex] ?? syntheticToolCallId(callIndex), resultBody, call.name, ok);
315
+ }
316
+ else {
317
+ messages.push({
318
+ role: "user",
319
+ content: resultBody,
320
+ });
321
+ }
315
322
  }
316
323
  }
317
324
  // Round cap reached — force a tool-free final answer from what we gathered.
318
325
  options.signal?.throwIfAborted();
319
326
  messages.push({
320
327
  role: "user",
321
- content: "Stop researching now. Using only what you have already gathered above, give your final answer to the original question. Do NOT call any more tools.",
328
+ content: "Stop researching now. Using only what you have already gathered above, give your final answer to the original question. Do NOT call any more tools. Prefer high-trust sources; only claim a page confirms a fact if that fact appears in the tool output; include 1–3 source URLs from the results above.",
322
329
  });
323
- return streamAskRound(baseRequest, messages, onToken);
330
+ const finalRound = await streamAskRound(baseRequest, messages, onToken);
331
+ return finalRound.text;
324
332
  }
325
333
  export async function runAsk(prompt, options = {}) {
326
334
  const request = await buildAskMessages(prompt, options);
@@ -329,9 +337,6 @@ export async function runAsk(prompt, options = {}) {
329
337
  }
330
338
  export async function runAskStream(prompt, onToken, options = {}) {
331
339
  const request = await buildAskMessages(prompt, options);
332
- // The answer is streamed to onToken live (with tool-call rounds suppressed),
333
- // and the returned string is the authoritative final answer for callers that
334
- // commit/persist it.
335
340
  return resolveAskAnswer(prompt, request.provider, request.model, request.messages, options, onToken);
336
341
  }
337
342
  //# sourceMappingURL=ask.js.map