@namzu/sdk 51.1.0 → 52.1.0

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 (395) hide show
  1. package/CHANGELOG.md +119 -0
  2. package/dist/agents/QueryAgent.d.ts.map +1 -1
  3. package/dist/agents/QueryAgent.js +2 -0
  4. package/dist/agents/QueryAgent.js.map +1 -1
  5. package/dist/agents/SupervisorAgent.d.ts +1 -1
  6. package/dist/agents/SupervisorAgent.d.ts.map +1 -1
  7. package/dist/agents/SupervisorAgent.js.map +1 -1
  8. package/dist/agents/examples/SupervisorAgent.d.ts +42 -0
  9. package/dist/agents/examples/SupervisorAgent.d.ts.map +1 -1
  10. package/dist/agents/examples/SupervisorAgent.js +80 -4
  11. package/dist/agents/examples/SupervisorAgent.js.map +1 -1
  12. package/dist/agents/index.d.ts +1 -1
  13. package/dist/agents/index.d.ts.map +1 -1
  14. package/dist/agents/index.js.map +1 -1
  15. package/dist/authorization/protected-paths.d.ts +36 -0
  16. package/dist/authorization/protected-paths.d.ts.map +1 -0
  17. package/dist/authorization/protected-paths.js +107 -0
  18. package/dist/authorization/protected-paths.js.map +1 -0
  19. package/dist/authorization/readonly-commands.d.ts +39 -0
  20. package/dist/authorization/readonly-commands.d.ts.map +1 -0
  21. package/dist/authorization/readonly-commands.js +797 -0
  22. package/dist/authorization/readonly-commands.js.map +1 -0
  23. package/dist/authorization/readonly-flags.d.ts +19 -0
  24. package/dist/authorization/readonly-flags.d.ts.map +1 -0
  25. package/dist/authorization/readonly-flags.js +178 -0
  26. package/dist/authorization/readonly-flags.js.map +1 -0
  27. package/dist/authorization/removal.d.ts +52 -0
  28. package/dist/authorization/removal.d.ts.map +1 -0
  29. package/dist/authorization/removal.js +283 -0
  30. package/dist/authorization/removal.js.map +1 -0
  31. package/dist/authorization/shell-paths.d.ts +66 -0
  32. package/dist/authorization/shell-paths.d.ts.map +1 -0
  33. package/dist/authorization/shell-paths.js +697 -0
  34. package/dist/authorization/shell-paths.js.map +1 -0
  35. package/dist/bridge/acp/update.d.ts.map +1 -1
  36. package/dist/bridge/acp/update.js +1 -0
  37. package/dist/bridge/acp/update.js.map +1 -1
  38. package/dist/compaction/context-window.d.ts +24 -0
  39. package/dist/compaction/context-window.d.ts.map +1 -1
  40. package/dist/compaction/context-window.js +34 -0
  41. package/dist/compaction/context-window.js.map +1 -1
  42. package/dist/compaction/manual.d.ts.map +1 -1
  43. package/dist/compaction/manual.js +10 -2
  44. package/dist/compaction/manual.js.map +1 -1
  45. package/dist/compaction/plan.d.ts +4 -0
  46. package/dist/compaction/plan.d.ts.map +1 -1
  47. package/dist/compaction/plan.js +6 -2
  48. package/dist/compaction/plan.js.map +1 -1
  49. package/dist/compaction/verifier.d.ts +11 -1
  50. package/dist/compaction/verifier.d.ts.map +1 -1
  51. package/dist/compaction/verifier.js +75 -23
  52. package/dist/compaction/verifier.js.map +1 -1
  53. package/dist/config/runtime.d.ts +55 -7
  54. package/dist/config/runtime.d.ts.map +1 -1
  55. package/dist/config/runtime.js +23 -2
  56. package/dist/config/runtime.js.map +1 -1
  57. package/dist/connector/web/guarded-fetch.d.ts.map +1 -1
  58. package/dist/connector/web/guarded-fetch.js +24 -0
  59. package/dist/connector/web/guarded-fetch.js.map +1 -1
  60. package/dist/connector/web/index.d.ts +1 -1
  61. package/dist/connector/web/index.d.ts.map +1 -1
  62. package/dist/connector/web/page.d.ts +59 -0
  63. package/dist/connector/web/page.d.ts.map +1 -0
  64. package/dist/connector/web/page.js +112 -0
  65. package/dist/connector/web/page.js.map +1 -0
  66. package/dist/connector/web/types.d.ts +47 -0
  67. package/dist/connector/web/types.d.ts.map +1 -1
  68. package/dist/connector/web/types.js.map +1 -1
  69. package/dist/constants/continuation.d.ts +8 -0
  70. package/dist/constants/continuation.d.ts.map +1 -1
  71. package/dist/constants/continuation.js +8 -0
  72. package/dist/constants/continuation.js.map +1 -1
  73. package/dist/constants/plugin/index.d.ts +1 -1
  74. package/dist/constants/plugin/index.d.ts.map +1 -1
  75. package/dist/constants/plugin/index.js +1 -1
  76. package/dist/constants/plugin/index.js.map +1 -1
  77. package/dist/contracts/a2a.d.ts +2 -2
  78. package/dist/manager/agent/lifecycle.d.ts +7 -0
  79. package/dist/manager/agent/lifecycle.d.ts.map +1 -1
  80. package/dist/manager/agent/lifecycle.js +11 -2
  81. package/dist/manager/agent/lifecycle.js.map +1 -1
  82. package/dist/manager/session/turn-recorder.d.ts.map +1 -1
  83. package/dist/manager/session/turn-recorder.js +4 -2
  84. package/dist/manager/session/turn-recorder.js.map +1 -1
  85. package/dist/plugin/index.d.ts +1 -1
  86. package/dist/plugin/index.d.ts.map +1 -1
  87. package/dist/plugin/index.js +1 -1
  88. package/dist/plugin/index.js.map +1 -1
  89. package/dist/plugin/lifecycle.d.ts +6 -0
  90. package/dist/plugin/lifecycle.d.ts.map +1 -1
  91. package/dist/plugin/lifecycle.js +98 -114
  92. package/dist/plugin/lifecycle.js.map +1 -1
  93. package/dist/plugin/shell-hook.d.ts +90 -22
  94. package/dist/plugin/shell-hook.d.ts.map +1 -1
  95. package/dist/plugin/shell-hook.js +434 -41
  96. package/dist/plugin/shell-hook.js.map +1 -1
  97. package/dist/provider/capabilities.d.ts +9 -0
  98. package/dist/provider/capabilities.d.ts.map +1 -1
  99. package/dist/provider/capabilities.js +15 -0
  100. package/dist/provider/capabilities.js.map +1 -1
  101. package/dist/provider/errors.d.ts.map +1 -1
  102. package/dist/provider/errors.js +13 -0
  103. package/dist/provider/errors.js.map +1 -1
  104. package/dist/provider/fallback.d.ts.map +1 -1
  105. package/dist/provider/fallback.js +10 -0
  106. package/dist/provider/fallback.js.map +1 -1
  107. package/dist/provider/idle-timeout.d.ts.map +1 -1
  108. package/dist/provider/idle-timeout.js +5 -0
  109. package/dist/provider/idle-timeout.js.map +1 -1
  110. package/dist/provider/image-input-refusal.d.ts +9 -0
  111. package/dist/provider/image-input-refusal.d.ts.map +1 -0
  112. package/dist/provider/image-input-refusal.js +54 -0
  113. package/dist/provider/image-input-refusal.js.map +1 -0
  114. package/dist/provider/index.d.ts +2 -1
  115. package/dist/provider/index.d.ts.map +1 -1
  116. package/dist/provider/index.js +2 -1
  117. package/dist/provider/index.js.map +1 -1
  118. package/dist/provider/retry.d.ts +7 -2
  119. package/dist/provider/retry.d.ts.map +1 -1
  120. package/dist/provider/retry.js +38 -4
  121. package/dist/provider/retry.js.map +1 -1
  122. package/dist/public-runtime.d.ts +5 -2
  123. package/dist/public-runtime.d.ts.map +1 -1
  124. package/dist/public-runtime.js +5 -2
  125. package/dist/public-runtime.js.map +1 -1
  126. package/dist/public-types.d.ts +2 -2
  127. package/dist/public-types.d.ts.map +1 -1
  128. package/dist/runtime/jobs/registry.d.ts +21 -0
  129. package/dist/runtime/jobs/registry.d.ts.map +1 -1
  130. package/dist/runtime/jobs/registry.js +15 -2
  131. package/dist/runtime/jobs/registry.js.map +1 -1
  132. package/dist/runtime/query/deferred-sandbox.d.ts +54 -0
  133. package/dist/runtime/query/deferred-sandbox.d.ts.map +1 -0
  134. package/dist/runtime/query/deferred-sandbox.js +75 -0
  135. package/dist/runtime/query/deferred-sandbox.js.map +1 -0
  136. package/dist/runtime/query/executor/tool-call-admission.d.ts +10 -0
  137. package/dist/runtime/query/executor/tool-call-admission.d.ts.map +1 -1
  138. package/dist/runtime/query/executor/tool-call-admission.js +74 -13
  139. package/dist/runtime/query/executor/tool-call-admission.js.map +1 -1
  140. package/dist/runtime/query/executor.d.ts +51 -2
  141. package/dist/runtime/query/executor.d.ts.map +1 -1
  142. package/dist/runtime/query/executor.js +214 -9
  143. package/dist/runtime/query/executor.js.map +1 -1
  144. package/dist/runtime/query/finalize-turn.d.ts +6 -0
  145. package/dist/runtime/query/finalize-turn.d.ts.map +1 -1
  146. package/dist/runtime/query/finalize-turn.js +1 -1
  147. package/dist/runtime/query/finalize-turn.js.map +1 -1
  148. package/dist/runtime/query/index.d.ts +29 -0
  149. package/dist/runtime/query/index.d.ts.map +1 -1
  150. package/dist/runtime/query/index.js +108 -28
  151. package/dist/runtime/query/index.js.map +1 -1
  152. package/dist/runtime/query/iteration/index.d.ts +57 -0
  153. package/dist/runtime/query/iteration/index.d.ts.map +1 -1
  154. package/dist/runtime/query/iteration/index.js +380 -28
  155. package/dist/runtime/query/iteration/index.js.map +1 -1
  156. package/dist/runtime/query/iteration/phases/compaction-breaker.d.ts +61 -0
  157. package/dist/runtime/query/iteration/phases/compaction-breaker.d.ts.map +1 -0
  158. package/dist/runtime/query/iteration/phases/compaction-breaker.js +94 -0
  159. package/dist/runtime/query/iteration/phases/compaction-breaker.js.map +1 -0
  160. package/dist/runtime/query/iteration/phases/compaction.d.ts +42 -1
  161. package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
  162. package/dist/runtime/query/iteration/phases/compaction.js +203 -23
  163. package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
  164. package/dist/runtime/query/iteration/phases/context.d.ts +10 -0
  165. package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
  166. package/dist/runtime/query/iteration/phases/context.js.map +1 -1
  167. package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
  168. package/dist/runtime/query/iteration/phases/tool-review.js +165 -2
  169. package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
  170. package/dist/runtime/query/iteration/provider-rejected-image.d.ts +21 -1
  171. package/dist/runtime/query/iteration/provider-rejected-image.d.ts.map +1 -1
  172. package/dist/runtime/query/iteration/provider-rejected-image.js +53 -7
  173. package/dist/runtime/query/iteration/provider-rejected-image.js.map +1 -1
  174. package/dist/runtime/query/iteration/request-repair.d.ts +108 -0
  175. package/dist/runtime/query/iteration/request-repair.d.ts.map +1 -0
  176. package/dist/runtime/query/iteration/request-repair.js +593 -0
  177. package/dist/runtime/query/iteration/request-repair.js.map +1 -0
  178. package/dist/runtime/query/iteration/stream-turn.d.ts +6 -0
  179. package/dist/runtime/query/iteration/stream-turn.d.ts.map +1 -1
  180. package/dist/runtime/query/iteration/stream-turn.js +2 -2
  181. package/dist/runtime/query/iteration/stream-turn.js.map +1 -1
  182. package/dist/runtime/query/path-escalation.d.ts +36 -0
  183. package/dist/runtime/query/path-escalation.d.ts.map +1 -0
  184. package/dist/runtime/query/path-escalation.js +184 -0
  185. package/dist/runtime/query/path-escalation.js.map +1 -0
  186. package/dist/runtime/query/plugin-hooks.d.ts.map +1 -1
  187. package/dist/runtime/query/plugin-hooks.js +8 -0
  188. package/dist/runtime/query/plugin-hooks.js.map +1 -1
  189. package/dist/runtime/query/reminders.d.ts +35 -0
  190. package/dist/runtime/query/reminders.d.ts.map +1 -0
  191. package/dist/runtime/query/reminders.js +7 -0
  192. package/dist/runtime/query/reminders.js.map +1 -0
  193. package/dist/runtime/query/request-rich-content.d.ts +5 -1
  194. package/dist/runtime/query/request-rich-content.d.ts.map +1 -1
  195. package/dist/runtime/query/request-rich-content.js +67 -25
  196. package/dist/runtime/query/request-rich-content.js.map +1 -1
  197. package/dist/runtime/query/resume-pending.d.ts.map +1 -1
  198. package/dist/runtime/query/resume-pending.js +26 -0
  199. package/dist/runtime/query/resume-pending.js.map +1 -1
  200. package/dist/runtime/query/review-policy.d.ts +26 -0
  201. package/dist/runtime/query/review-policy.d.ts.map +1 -1
  202. package/dist/runtime/query/review-policy.js +79 -5
  203. package/dist/runtime/query/review-policy.js.map +1 -1
  204. package/dist/runtime/query/tool-output-budget.d.ts +46 -0
  205. package/dist/runtime/query/tool-output-budget.d.ts.map +1 -1
  206. package/dist/runtime/query/tool-output-budget.js +85 -1
  207. package/dist/runtime/query/tool-output-budget.js.map +1 -1
  208. package/dist/runtime/query/tooling.d.ts +1 -0
  209. package/dist/runtime/query/tooling.d.ts.map +1 -1
  210. package/dist/runtime/query/tooling.js +5 -0
  211. package/dist/runtime/query/tooling.js.map +1 -1
  212. package/dist/session/paths.d.ts +5 -1
  213. package/dist/session/paths.d.ts.map +1 -1
  214. package/dist/session/paths.js +8 -0
  215. package/dist/session/paths.js.map +1 -1
  216. package/dist/store/memory/disk.d.ts.map +1 -1
  217. package/dist/store/memory/disk.js +2 -2
  218. package/dist/store/memory/disk.js.map +1 -1
  219. package/dist/store/memory/markdown.d.ts.map +1 -1
  220. package/dist/store/memory/markdown.js +2 -2
  221. package/dist/store/memory/markdown.js.map +1 -1
  222. package/dist/store/memory/naming.d.ts +9 -0
  223. package/dist/store/memory/naming.d.ts.map +1 -1
  224. package/dist/store/memory/naming.js +12 -0
  225. package/dist/store/memory/naming.js.map +1 -1
  226. package/dist/tools/builtins/bash.d.ts +30 -1
  227. package/dist/tools/builtins/bash.d.ts.map +1 -1
  228. package/dist/tools/builtins/bash.js +170 -26
  229. package/dist/tools/builtins/bash.js.map +1 -1
  230. package/dist/tools/builtins/computer-use.d.ts +1 -1
  231. package/dist/tools/builtins/computer-use.d.ts.map +1 -1
  232. package/dist/tools/builtins/computer-use.js +12 -5
  233. package/dist/tools/builtins/computer-use.js.map +1 -1
  234. package/dist/tools/builtins/content-fingerprint.d.ts +2 -0
  235. package/dist/tools/builtins/content-fingerprint.d.ts.map +1 -1
  236. package/dist/tools/builtins/content-fingerprint.js +4 -0
  237. package/dist/tools/builtins/content-fingerprint.js.map +1 -1
  238. package/dist/tools/builtins/edit.d.ts.map +1 -1
  239. package/dist/tools/builtins/edit.js +60 -18
  240. package/dist/tools/builtins/edit.js.map +1 -1
  241. package/dist/tools/builtins/grep.d.ts +8 -1
  242. package/dist/tools/builtins/grep.d.ts.map +1 -1
  243. package/dist/tools/builtins/grep.js +152 -33
  244. package/dist/tools/builtins/grep.js.map +1 -1
  245. package/dist/tools/builtins/read-file.d.ts +1 -0
  246. package/dist/tools/builtins/read-file.d.ts.map +1 -1
  247. package/dist/tools/builtins/read-file.js +44 -5
  248. package/dist/tools/builtins/read-file.js.map +1 -1
  249. package/dist/tools/builtins/read-render.d.ts +52 -0
  250. package/dist/tools/builtins/read-render.d.ts.map +1 -1
  251. package/dist/tools/builtins/read-render.js +143 -0
  252. package/dist/tools/builtins/read-render.js.map +1 -1
  253. package/dist/tools/builtins/web.d.ts +13 -2
  254. package/dist/tools/builtins/web.d.ts.map +1 -1
  255. package/dist/tools/builtins/web.js +225 -32
  256. package/dist/tools/builtins/web.js.map +1 -1
  257. package/dist/tools/coordinator/ask-user-question.d.ts +2 -0
  258. package/dist/tools/coordinator/ask-user-question.d.ts.map +1 -1
  259. package/dist/tools/coordinator/ask-user-question.js +256 -92
  260. package/dist/tools/coordinator/ask-user-question.js.map +1 -1
  261. package/dist/tools/coordinator/question-options.d.ts +1 -0
  262. package/dist/tools/coordinator/question-options.d.ts.map +1 -1
  263. package/dist/tools/coordinator/question-options.js +6 -1
  264. package/dist/tools/coordinator/question-options.js.map +1 -1
  265. package/dist/tools/defineTool.d.ts +4 -1
  266. package/dist/tools/defineTool.d.ts.map +1 -1
  267. package/dist/tools/defineTool.js +6 -1
  268. package/dist/tools/defineTool.js.map +1 -1
  269. package/dist/tools/paths.d.ts +6 -0
  270. package/dist/tools/paths.d.ts.map +1 -1
  271. package/dist/tools/paths.js +1 -1
  272. package/dist/tools/paths.js.map +1 -1
  273. package/dist/types/agent/query.d.ts +3 -0
  274. package/dist/types/agent/query.d.ts.map +1 -1
  275. package/dist/types/errors/catalog.js +1 -1
  276. package/dist/types/errors/catalog.js.map +1 -1
  277. package/dist/types/hitl/index.d.ts +104 -0
  278. package/dist/types/hitl/index.d.ts.map +1 -1
  279. package/dist/types/hitl/index.js.map +1 -1
  280. package/dist/types/message/index.d.ts +1 -1
  281. package/dist/types/message/index.d.ts.map +1 -1
  282. package/dist/types/message/index.js +2 -0
  283. package/dist/types/message/index.js.map +1 -1
  284. package/dist/types/plugin/index.d.ts +93 -19
  285. package/dist/types/plugin/index.d.ts.map +1 -1
  286. package/dist/types/plugin/index.js +3 -0
  287. package/dist/types/plugin/index.js.map +1 -1
  288. package/dist/types/provider/errors.d.ts +19 -0
  289. package/dist/types/provider/errors.d.ts.map +1 -1
  290. package/dist/types/provider/errors.js +54 -5
  291. package/dist/types/provider/errors.js.map +1 -1
  292. package/dist/types/provider/interface.d.ts +12 -0
  293. package/dist/types/provider/interface.d.ts.map +1 -1
  294. package/dist/types/provider/stream.d.ts +1 -1
  295. package/dist/types/session/config.d.ts +14 -0
  296. package/dist/types/session/config.d.ts.map +1 -1
  297. package/dist/types/session/events.d.ts +27 -4
  298. package/dist/types/session/events.d.ts.map +1 -1
  299. package/dist/types/session/events.js.map +1 -1
  300. package/dist/types/session/records.d.ts +9 -9
  301. package/dist/types/session/records.d.ts.map +1 -1
  302. package/dist/types/session/records.js +1 -0
  303. package/dist/types/session/records.js.map +1 -1
  304. package/dist/types/session/stop-reason.d.ts +5 -0
  305. package/dist/types/session/stop-reason.d.ts.map +1 -1
  306. package/dist/types/tool/index.d.ts +52 -0
  307. package/dist/types/tool/index.d.ts.map +1 -1
  308. package/dist/types/tool/index.js.map +1 -1
  309. package/package.json +4 -2
  310. package/src/agents/QueryAgent.ts +5 -1
  311. package/src/agents/SupervisorAgent.ts +1 -1
  312. package/src/agents/examples/SupervisorAgent.ts +217 -106
  313. package/src/agents/index.ts +1 -1
  314. package/src/authorization/protected-paths.ts +110 -0
  315. package/src/authorization/readonly-commands.ts +771 -0
  316. package/src/authorization/readonly-flags.ts +196 -0
  317. package/src/authorization/removal.ts +326 -0
  318. package/src/authorization/shell-paths.ts +927 -0
  319. package/src/bridge/acp/update.ts +1 -0
  320. package/src/compaction/context-window.ts +41 -0
  321. package/src/compaction/manual.ts +10 -2
  322. package/src/compaction/plan.ts +11 -2
  323. package/src/compaction/verifier.ts +84 -22
  324. package/src/config/runtime.ts +23 -2
  325. package/src/connector/web/guarded-fetch.ts +26 -0
  326. package/src/connector/web/index.ts +3 -0
  327. package/src/connector/web/page.ts +130 -0
  328. package/src/connector/web/types.ts +47 -0
  329. package/src/constants/continuation.ts +10 -0
  330. package/src/constants/plugin/index.ts +1 -1
  331. package/src/manager/agent/lifecycle.ts +12 -4
  332. package/src/manager/session/turn-recorder.ts +10 -2
  333. package/src/plugin/index.ts +1 -0
  334. package/src/plugin/lifecycle.ts +107 -119
  335. package/src/plugin/shell-hook.ts +477 -43
  336. package/src/provider/capabilities.ts +17 -0
  337. package/src/provider/errors.ts +13 -0
  338. package/src/provider/fallback.ts +10 -0
  339. package/src/provider/idle-timeout.ts +5 -0
  340. package/src/provider/image-input-refusal.ts +54 -0
  341. package/src/provider/index.ts +2 -0
  342. package/src/provider/retry.ts +42 -4
  343. package/src/public-runtime.ts +11 -0
  344. package/src/public-types.ts +4 -0
  345. package/src/runtime/jobs/registry.ts +39 -2
  346. package/src/runtime/query/deferred-sandbox.ts +110 -0
  347. package/src/runtime/query/executor/tool-call-admission.ts +79 -13
  348. package/src/runtime/query/executor.ts +267 -11
  349. package/src/runtime/query/finalize-turn.ts +7 -1
  350. package/src/runtime/query/index.ts +169 -52
  351. package/src/runtime/query/iteration/index.ts +439 -35
  352. package/src/runtime/query/iteration/phases/compaction-breaker.ts +108 -0
  353. package/src/runtime/query/iteration/phases/compaction.ts +241 -38
  354. package/src/runtime/query/iteration/phases/context.ts +11 -0
  355. package/src/runtime/query/iteration/phases/tool-review.ts +177 -2
  356. package/src/runtime/query/iteration/provider-rejected-image.ts +71 -5
  357. package/src/runtime/query/iteration/request-repair.ts +743 -0
  358. package/src/runtime/query/iteration/stream-turn.ts +11 -1
  359. package/src/runtime/query/path-escalation.ts +204 -0
  360. package/src/runtime/query/plugin-hooks.ts +8 -0
  361. package/src/runtime/query/reminders.ts +45 -0
  362. package/src/runtime/query/request-rich-content.ts +77 -27
  363. package/src/runtime/query/resume-pending.ts +27 -0
  364. package/src/runtime/query/review-policy.ts +106 -5
  365. package/src/runtime/query/tool-output-budget.ts +115 -1
  366. package/src/runtime/query/tooling.ts +6 -0
  367. package/src/session/paths.ts +11 -1
  368. package/src/store/memory/disk.ts +2 -1
  369. package/src/store/memory/markdown.ts +2 -1
  370. package/src/store/memory/naming.ts +13 -0
  371. package/src/tools/builtins/bash.ts +203 -34
  372. package/src/tools/builtins/computer-use.ts +13 -4
  373. package/src/tools/builtins/content-fingerprint.ts +5 -0
  374. package/src/tools/builtins/edit.ts +69 -19
  375. package/src/tools/builtins/grep.ts +159 -38
  376. package/src/tools/builtins/read-file.ts +48 -5
  377. package/src/tools/builtins/read-render.ts +193 -0
  378. package/src/tools/builtins/web.ts +258 -33
  379. package/src/tools/coordinator/ask-user-question.ts +309 -103
  380. package/src/tools/coordinator/question-options.ts +8 -1
  381. package/src/tools/defineTool.ts +11 -2
  382. package/src/tools/paths.ts +1 -1
  383. package/src/types/agent/query.ts +3 -0
  384. package/src/types/errors/catalog.ts +1 -1
  385. package/src/types/hitl/index.ts +104 -0
  386. package/src/types/message/index.ts +2 -0
  387. package/src/types/plugin/index.ts +101 -18
  388. package/src/types/provider/errors.ts +66 -5
  389. package/src/types/provider/interface.ts +13 -0
  390. package/src/types/provider/stream.ts +1 -1
  391. package/src/types/session/config.ts +14 -0
  392. package/src/types/session/events.ts +32 -4
  393. package/src/types/session/records.ts +1 -0
  394. package/src/types/session/stop-reason.ts +5 -0
  395. package/src/types/tool/index.ts +53 -2
@@ -12,7 +12,13 @@
12
12
 
13
13
  import { z } from 'zod'
14
14
  import type { PendingAnswers, QuestionParkRecorder } from '../../runtime/query/question-park.js'
15
- import type { ResumeHandler, UserQuestionOption } from '../../types/hitl/index.js'
15
+ import type {
16
+ HITLResumeDecision,
17
+ ResumeHandler,
18
+ UserQuestionData,
19
+ UserQuestionItem,
20
+ UserQuestionOption,
21
+ } from '../../types/hitl/index.js'
16
22
  import type { SessionId, TurnId } from '../../types/ids/index.js'
17
23
  import type { ToolDefinition } from '../../types/tool/index.js'
18
24
  import { generateCheckpointId } from '../../utils/id.js'
@@ -31,7 +37,7 @@ export const ASK_USER_QUESTION_TOOL_NAME = 'ask_user_question' as const
31
37
  * `additionalProperties: false` turns that into a refusal at generation time
32
38
  * rather than a rejection after the fact.
33
39
  */
34
- const askUserQuestionModelInputSchema: Record<string, unknown> = {
40
+ const askUserQuestionItemModelSchema: Record<string, unknown> = {
35
41
  type: 'object',
36
42
  properties: {
37
43
  question: {
@@ -40,7 +46,8 @@ const askUserQuestionModelInputSchema: Record<string, unknown> = {
40
46
  },
41
47
  header: {
42
48
  type: 'string',
43
- description: 'Optional very short topic label, no more than 24 characters.',
49
+ description:
50
+ 'Optional very short topic label, no more than 24 characters; unique within the call.',
44
51
  },
45
52
  options: {
46
53
  type: 'array',
@@ -62,6 +69,11 @@ const askUserQuestionModelInputSchema: Record<string, unknown> = {
62
69
  description:
63
70
  'True on the option you recommend; put that option first. The host shows the recommendation, so never write it into the label.',
64
71
  },
72
+ preview: {
73
+ type: 'string',
74
+ description:
75
+ 'Optional plain text or markdown shown beside the options while this one is focused: a snippet, a mock-up, a side-by-side. Leave it out unless it helps the choice.',
76
+ },
65
77
  },
66
78
  required: ['label'],
67
79
  additionalProperties: false,
@@ -80,6 +92,135 @@ const askUserQuestionModelInputSchema: Record<string, unknown> = {
80
92
  additionalProperties: false,
81
93
  }
82
94
 
95
+ /**
96
+ * The single closed shape a capable provider constrains this call to: a
97
+ * `questions` array of one to four questions. The older single-question
98
+ * input (`question` and `options` at the top level) is still accepted at
99
+ * run time as a one-element batch, but is not offered to the model.
100
+ */
101
+ const askUserQuestionModelInputSchema: Record<string, unknown> = {
102
+ type: 'object',
103
+ properties: {
104
+ questions: {
105
+ type: 'array',
106
+ description:
107
+ 'A JSON array of 1-4 questions. Batch only related decisions that all block your next step; ask a single decision as one entry.',
108
+ items: askUserQuestionItemModelSchema,
109
+ },
110
+ },
111
+ required: ['questions'],
112
+ additionalProperties: false,
113
+ }
114
+
115
+ /** The most questions one call may carry. */
116
+ export const MAX_QUESTIONS_PER_CALL = 4
117
+
118
+ const optionSchema = z
119
+ .object({
120
+ label: z
121
+ .string()
122
+ .min(1)
123
+ .max(80)
124
+ .describe(
125
+ 'Concise option label (1-5 words), plain text: no "(Recommended)" or other parenthesised note at the end.',
126
+ ),
127
+ description: z
128
+ .string()
129
+ .max(300)
130
+ .optional()
131
+ .describe('One line on what practically changes if this option is chosen.'),
132
+ recommended: z
133
+ .boolean()
134
+ .optional()
135
+ .describe(
136
+ 'True on the option you recommend; put it first. Never write the recommendation into the label.',
137
+ ),
138
+ preview: z
139
+ .string()
140
+ .max(4000)
141
+ .optional()
142
+ .describe(
143
+ 'Optional plain text or markdown shown beside the options while this one is focused.',
144
+ ),
145
+ })
146
+ .strict()
147
+
148
+ const questionSchema = z
149
+ .object({
150
+ question: z
151
+ .string()
152
+ .min(1)
153
+ .describe('Full question text — clear, specific, ends with a question mark.'),
154
+ header: z
155
+ .string()
156
+ .max(24)
157
+ .optional()
158
+ .describe('Very short topic label for the question (e.g. "Audience", "Auth method").'),
159
+ options: z
160
+ .array(optionSchema)
161
+ .min(2)
162
+ .max(4)
163
+ .describe('2-4 genuinely distinct, context-derived options.'),
164
+ multiSelect: z
165
+ .boolean()
166
+ .optional()
167
+ .default(false)
168
+ .describe('True only when several options can apply at once.'),
169
+ allowFreeText: z
170
+ .boolean()
171
+ .optional()
172
+ .default(true)
173
+ .describe('Whether the user may answer in their own words.'),
174
+ })
175
+ .strict()
176
+
177
+ /**
178
+ * The old single-question input is a one-element batch. Anything that has
179
+ * `questions` is left alone, so a call mixing both shapes fails the strict
180
+ * object below instead of silently dropping one.
181
+ */
182
+ function batchFromLegacy(input: unknown): unknown {
183
+ if (typeof input !== 'object' || input === null || Array.isArray(input)) return input
184
+ const record = input as Record<string, unknown>
185
+ if ('questions' in record || !('question' in record)) return input
186
+ return { questions: [record] }
187
+ }
188
+
189
+ const inputSchema = z
190
+ .preprocess(
191
+ batchFromLegacy,
192
+ z
193
+ .object({
194
+ questions: z
195
+ .array(questionSchema)
196
+ .min(1)
197
+ .max(MAX_QUESTIONS_PER_CALL)
198
+ .describe('1-4 related questions, asked together; one answer comes back per question.'),
199
+ })
200
+ .strict(),
201
+ )
202
+ .superRefine((value, ctx) => {
203
+ const questions = value.questions.map((q) => q.question.trim().toLowerCase())
204
+ if (new Set(questions).size !== questions.length) {
205
+ ctx.addIssue({
206
+ code: 'custom',
207
+ path: ['questions'],
208
+ message: 'Question texts must be unique within one call.',
209
+ })
210
+ }
211
+ const headers = value.questions.flatMap((q) =>
212
+ q.header !== undefined ? [q.header.trim().toLowerCase()] : [],
213
+ )
214
+ if (new Set(headers).size !== headers.length) {
215
+ ctx.addIssue({
216
+ code: 'custom',
217
+ path: ['questions'],
218
+ message:
219
+ 'Headers must be unique within one call: a host uses them as tab labels and answer keys.',
220
+ })
221
+ }
222
+ })
223
+
83
224
  export interface AskUserQuestionToolOptions {
84
225
  /** Where the question goes; the turn parks on it until an answer comes back. */
85
226
  resumeHandler: ResumeHandler
@@ -96,68 +237,141 @@ export interface AskUserQuestionToolOptions {
96
237
  pendingAnswers?: PendingAnswers
97
238
  }
98
239
 
240
+ const NO_ANSWER_TEXT =
241
+ 'The user did not answer this question. Do not assume a choice or consent; proceed on your best judgment or continue without this information.'
242
+
243
+ const NO_ANSWER_BATCH_TEXT =
244
+ 'The user did not answer these questions. Do not assume a choice or consent; proceed on your best judgment or continue without this information.'
245
+
246
+ /** One question's answer as the tool reads it, after the host's entry is checked. */
247
+ interface ReadAnswer {
248
+ readonly item: UserQuestionItem
249
+ readonly selected: readonly {
250
+ id: string
251
+ label: string
252
+ recommended?: true
253
+ }[]
254
+ readonly freeText: string
255
+ }
256
+
257
+ const isAnswered = (answer: ReadAnswer): boolean =>
258
+ answer.selected.length > 0 || answer.freeText !== ''
259
+
260
+ /**
261
+ * Which answer belongs to which question. `answers` is the batch form; a
262
+ * decision without it answers the FIRST question only — the shape a host
263
+ * that predates batches sends, and what a single-question call has always
264
+ * received. An entry that names no question of this call, or names one
265
+ * twice, is dropped: it was meant for something else.
266
+ */
267
+ function readAnswers(
268
+ items: readonly UserQuestionItem[],
269
+ decision: Extract<HITLResumeDecision, { action: 'answer_question' }>,
270
+ ): ReadAnswer[] {
271
+ const entries =
272
+ decision.answers ??
273
+ (items[0]
274
+ ? [
275
+ {
276
+ questionId: items[0].id,
277
+ selectedOptionIds: decision.selectedOptionIds,
278
+ freeText: decision.freeText,
279
+ },
280
+ ]
281
+ : [])
282
+ const byId = new Map<string, (typeof entries)[number]>()
283
+ for (const entry of entries) {
284
+ if (!byId.has(entry.questionId)) byId.set(entry.questionId, entry)
285
+ }
286
+ return items.map((item) => {
287
+ const entry = byId.get(item.id)
288
+ // The label the user saw, never a marker: `questionOptions` took it
289
+ // out. Whether it was the recommended option is a field of its own.
290
+ const chosen = (entry?.selectedOptionIds ?? [])
291
+ .map((id) => item.options.find((opt) => opt.id === id))
292
+ .filter((opt): opt is UserQuestionOption => opt !== undefined)
293
+ const selected = (item.multiSelect ? chosen : chosen.slice(0, 1)).map((opt) => ({
294
+ id: opt.id,
295
+ label: opt.label,
296
+ ...(opt.recommended === true ? { recommended: true as const } : {}),
297
+ }))
298
+ return { item, selected, freeText: entry?.freeText?.trim() ?? '' }
299
+ })
300
+ }
301
+
302
+ function describeSingle(answer: ReadAnswer): {
303
+ output: string
304
+ data: Record<string, unknown>
305
+ } {
306
+ const { item, selected, freeText } = answer
307
+ let output: string
308
+ if (selected.length > 0) {
309
+ const joined = selected.map((s) => `"${s.label}"`).join(', ')
310
+ output = `User answered "${item.question}": ${joined}`
311
+ if (freeText) output += `\nAdditional note from the user: "${freeText}"`
312
+ } else {
313
+ output = `User answered "${item.question}" in their own words: "${freeText}"`
314
+ }
315
+ return {
316
+ output,
317
+ data: {
318
+ question: item.question,
319
+ selected,
320
+ ...(freeText ? { freeText } : {}),
321
+ answered: true,
322
+ },
323
+ }
324
+ }
325
+
326
+ const labelOf = (item: UserQuestionItem): string => item.header ?? item.question
327
+
328
+ function describeBatch(answers: readonly ReadAnswer[]): {
329
+ output: string
330
+ data: Record<string, unknown>
331
+ } {
332
+ const lines = answers.map((answer) => {
333
+ const { item, selected, freeText } = answer
334
+ if (selected.length > 0) {
335
+ const joined = selected.map((s) => `"${s.label}"`).join(', ')
336
+ return `${labelOf(item)}: ${joined}${freeText ? ` (note: "${freeText}")` : ''}`
337
+ }
338
+ if (freeText) return `${labelOf(item)} (own words): "${freeText}"`
339
+ return `${labelOf(item)}: not answered, assume nothing`
340
+ })
341
+ const answered = answers.filter(isAnswered).length
342
+ return {
343
+ output: `The user answered ${answered} of ${answers.length} questions:\n${lines.join('\n')}`,
344
+ data: {
345
+ questions: answers.map(({ item }) => ({
346
+ questionId: item.id,
347
+ ...(item.header !== undefined ? { header: item.header } : {}),
348
+ question: item.question,
349
+ })),
350
+ answers: answers.map((answer) => ({
351
+ questionId: answer.item.id,
352
+ ...(answer.item.header !== undefined ? { header: answer.item.header } : {}),
353
+ question: answer.item.question,
354
+ selected: answer.selected,
355
+ ...(answer.freeText ? { freeText: answer.freeText } : {}),
356
+ answered: isAnswered(answer),
357
+ })),
358
+ answered: true,
359
+ },
360
+ }
361
+ }
362
+
99
363
  export function buildAskUserQuestionTool(config: AskUserQuestionToolOptions): ToolDefinition {
100
364
  const parkHandler = config.resumeHandler
101
365
  const { questionParks, pendingAnswers } = config
102
366
  return defineTool({
103
367
  name: ASK_USER_QUESTION_TOOL_NAME,
104
368
  description:
105
- 'Ask the user ONE question ONLY when you are blocked on a decision that is genuinely theirs to make — one you cannot resolve from their request, your tools, the files you can read, or sensible defaults. The question must be the genuinely undecidable thing in THIS task. Never ask for information a tool can discover (do not ask what you can read, list, or search), never re-ask what the conversation already answers, and never ask meta-questions like "Shall I proceed?" — plan ratification goes through approve_plan. Provide 2-4 genuinely distinct options derived from the actual context — concrete paths, never generic placeholders (for example, asked to prepare a presentation, ask "Who is the audience?" with options like Board / Engineering team / Customer); keep labels short (1-5 words) and give each option a one-line description of what practically changes if it is chosen. If you recommend an option, put it FIRST and set recommended: true on it; the host marks it for the user, so never write "(Recommended)" or any translation of it into a label, and never end a label with a parenthesised note (put qualifiers in the description). Set multiSelect: true only when several options can apply at once. A free-text "Something else" escape hatch is always shown automatically — do not add your own "Other" option. Ask ONE question per call and prefer at most one question per assistant turn; if several decisions block you, ask only the ones that materially change your next actions, in sequence — most work needs at most 2-3 questions, so prefer proceeding on stated defaults over interrogating the user. Never invent answers or synthetic content on the user\'s behalf unless they explicitly asked for a random/test scenario. The answer arrives as this tool\'s result; if the result says the user did not answer, do not ask this or any other question again — proceed on your best judgment without assuming consent.',
106
- inputSchema: z
107
- .object({
108
- question: z
109
- .string()
110
- .min(1)
111
- .describe('Full question text — clear, specific, ends with a question mark.'),
112
- header: z
113
- .string()
114
- .max(24)
115
- .optional()
116
- .describe('Very short topic label for the question (e.g. "Audience", "Auth method").'),
117
- options: z
118
- .array(
119
- z
120
- .object({
121
- label: z
122
- .string()
123
- .min(1)
124
- .max(80)
125
- .describe(
126
- 'Concise option label (1-5 words), plain text: no "(Recommended)" or other parenthesised note at the end.',
127
- ),
128
- description: z
129
- .string()
130
- .max(300)
131
- .optional()
132
- .describe('One line on what practically changes if this option is chosen.'),
133
- recommended: z
134
- .boolean()
135
- .optional()
136
- .describe(
137
- 'True on the option you recommend; put it first. Never write the recommendation into the label.',
138
- ),
139
- })
140
- .strict(),
141
- )
142
- .min(2)
143
- .max(4)
144
- .describe('2-4 genuinely distinct, context-derived options.'),
145
- multiSelect: z
146
- .boolean()
147
- .optional()
148
- .default(false)
149
- .describe('True only when several options can apply at once.'),
150
- allowFreeText: z
151
- .boolean()
152
- .optional()
153
- .default(true)
154
- .describe('Whether the user may answer in their own words.'),
155
- })
156
- .strict(),
369
+ 'Ask the user when you are blocked on a decision that is genuinely theirs to make — one you cannot resolve from their request, your tools, the files you can read, or sensible defaults. The question must be the genuinely undecidable thing in THIS task. Never ask for information a tool can discover (do not ask what you can read, list, or search), never re-ask what the conversation already answers, and never ask meta-questions like "Shall I proceed?" — plan ratification goes through approve_plan. Provide 2-4 genuinely distinct options derived from the actual context — concrete paths, never generic placeholders (for example, asked to prepare a presentation, ask "Who is the audience?" with options like Board / Engineering team / Customer); keep labels short (1-5 words) and give each option a one-line description of what practically changes if it is chosen. If you recommend an option, put it FIRST and set recommended: true on it; the host marks it for the user, so never write "(Recommended)" or any translation of it into a label, and never end a label with a parenthesised note (put qualifiers in the description). Set multiSelect: true only when several options can apply at once. An option may carry a preview (plain text or markdown) that the host shows beside the list while it is focused; use it only when seeing the thing helps the choice. A free-text "Something else" escape hatch is always shown automatically — do not add your own "Other" option. Put the questions in the questions array. Use ONE entry for a single decision. Put several entries in the same call only when they are related decisions that ALL block your next step and the user can answer them together (for example which account, for what purpose, which environment before a credentialed step), at most 4, each with a short unique header; the user answers them all in one card and you get one answer per question. Never batch questions that depend on one another\'s answers — ask the first, then decide. Most work needs at most 2-3 questions in all, so prefer proceeding on stated defaults over interrogating the user. Never invent answers or synthetic content on the user\'s behalf unless they explicitly asked for a random/test scenario. The answers arrive as this tool\'s result; a question the result says was not answered must not be assumed, and if the result says the user did not answer at all, do not ask again — proceed on your best judgment without assuming consent.',
370
+ inputSchema,
157
371
  modelInputSchema: structuredClone(askUserQuestionModelInputSchema),
158
372
  enforceModelInput: true,
159
373
  validationErrorHint:
160
- 'Required shape: {"question":"...?","options":[{"label":"First","description":"What changes","recommended":true},{"label":"Second","description":"What changes"}]}. "options" must be a JSON array of 2-4 objects, never a string.',
374
+ 'Required shape: {"questions":[{"question":"...?","header":"Short","options":[{"label":"First","description":"What changes","recommended":true},{"label":"Second","description":"What changes"}]}]}. "questions" must be a JSON array of 1-4 objects. "options" must be a JSON array of 2-4 objects, never a string. Question texts and headers must be unique within one call.',
161
375
  category: 'custom',
162
376
  permissions: [],
163
377
  readOnly: true,
@@ -168,7 +382,11 @@ export function buildAskUserQuestionTool(config: AskUserQuestionToolOptions): To
168
382
  // registries by turn — concurrent parks on one turn clobber each
169
383
  // other and the first promise never resolves (the turn hangs to TTL).
170
384
  concurrencySafe: false,
171
- async execute({ question, header, options, multiSelect, allowFreeText }, context) {
385
+ async execute(input, context) {
386
+ // A host that calls the tool without going through the registry
387
+ // hands over the older single-question shape or unfilled defaults;
388
+ // parsing again is a no-op for input the registry already parsed.
389
+ const { questions } = inputSchema.parse(input)
172
390
  const toolUseId = context.toolUseId
173
391
  if (!toolUseId) {
174
392
  // Without the executing tool_use_id the question has no
@@ -186,15 +404,28 @@ export function buildAskUserQuestionTool(config: AskUserQuestionToolOptions): To
186
404
  // Labels without a recommendation marker, in any language, and the
187
405
  // recommendation as a field: what the host shows and what the
188
406
  // answer below quotes back to the model.
189
- const shownOptions = questionOptions(options)
407
+ const items: UserQuestionItem[] = questions.map((q, index) => ({
408
+ id: `${toolUseId}#${index}`,
409
+ question: q.question,
410
+ ...(q.header !== undefined ? { header: q.header } : {}),
411
+ options: questionOptions(q.options),
412
+ multiSelect: q.multiSelect,
413
+ allowFreeText: q.allowFreeText,
414
+ }))
415
+ const batch = items.length > 1
416
+ const [first] = items
417
+ if (!first) return { success: false, output: '', error: 'No question was asked.' }
190
418
 
191
- const questionData = {
419
+ // The top level mirrors the first question, so a host that does
420
+ // not know `questions` still shows and answers a real question.
421
+ const questionData: UserQuestionData = {
192
422
  questionId: toolUseId,
193
- question,
194
- ...(header !== undefined ? { header } : {}),
195
- options: shownOptions,
196
- multiSelect,
197
- allowFreeText,
423
+ question: first.question,
424
+ ...(first.header !== undefined ? { header: first.header } : {}),
425
+ options: first.options,
426
+ multiSelect: first.multiSelect,
427
+ allowFreeText: first.allowFreeText,
428
+ ...(batch ? { questions: items } : {}),
198
429
  }
199
430
 
200
431
  // An answer carried in from a resumed turn. Checked before the
@@ -237,17 +468,23 @@ export function buildAskUserQuestionTool(config: AskUserQuestionToolOptions): To
237
468
  // carry a different question's id.
238
469
  const noAnswer = {
239
470
  success: true,
240
- output:
241
- 'The user did not answer this question. Do not assume a choice or consent; proceed on your best judgment or continue without this information.',
242
- data: { question, answered: false },
471
+ output: batch ? NO_ANSWER_BATCH_TEXT : NO_ANSWER_TEXT,
472
+ data: batch
473
+ ? {
474
+ question: first.question,
475
+ questions: items.map((i) => i.question),
476
+ answered: false,
477
+ }
478
+ : { question: first.question, answered: false },
243
479
  }
244
480
 
245
481
  if (decision.action === 'abort') {
482
+ // One decline ends the whole batch: there is no partial refusal.
246
483
  return {
247
484
  success: false,
248
485
  output:
249
486
  'The user declined to answer and asked to stop. Acknowledge briefly and end your turn.',
250
- data: { question, answered: false, declined: true },
487
+ data: { question: first.question, answered: false, declined: true },
251
488
  }
252
489
  }
253
490
 
@@ -259,42 +496,11 @@ export function buildAskUserQuestionTool(config: AskUserQuestionToolOptions): To
259
496
  return noAnswer
260
497
  }
261
498
 
262
- // The label the user saw, never a marker: `questionOptions` took it
263
- // out. Whether it was the recommended option is a field of its own.
264
- const selected = decision.selectedOptionIds
265
- .map((id) => shownOptions.find((opt) => opt.id === id))
266
- .filter((opt): opt is UserQuestionOption => opt !== undefined)
267
- .map((opt) => ({
268
- id: opt.id,
269
- label: opt.label,
270
- ...(opt.recommended === true ? { recommended: true } : {}),
271
- }))
272
-
273
- const freeText = decision.freeText?.trim() ?? ''
274
-
275
- if (selected.length === 0 && !freeText) return noAnswer
276
-
277
- let output: string
278
- if (selected.length > 0) {
279
- const joined = selected.map((s) => `"${s.label}"`).join(', ')
280
- output = `User answered "${question}": ${joined}`
281
- if (freeText) {
282
- output += `\nAdditional note from the user: "${freeText}"`
283
- }
284
- } else {
285
- output = `User answered "${question}" in their own words: "${freeText}"`
286
- }
499
+ const answers = readAnswers(items, decision)
500
+ if (!answers.some(isAnswered)) return noAnswer
287
501
 
288
- return {
289
- success: true,
290
- output,
291
- data: {
292
- question,
293
- selected,
294
- ...(freeText ? { freeText } : {}),
295
- answered: true,
296
- },
297
- }
502
+ const described = batch ? describeBatch(answers) : describeSingle(answers[0] as ReadAnswer)
503
+ return { success: true, output: described.output, data: described.data }
298
504
  },
299
505
  })
300
506
  }
@@ -55,6 +55,7 @@ export interface AuthoredQuestionOption {
55
55
  readonly label: string
56
56
  readonly description?: string
57
57
  readonly recommended?: boolean
58
+ readonly preview?: string
58
59
  }
59
60
 
60
61
  /** A label's last parenthesised group, ASCII "(…)" or full-width "(…)". */
@@ -75,7 +76,10 @@ interface TrailingGroup {
75
76
  function trailingGroup(label: string): TrailingGroup | null {
76
77
  const match = TRAILING_GROUP.exec(label)
77
78
  if (!match) return null
78
- return { base: (match[1] ?? '').trim(), text: (match[2] ?? match[3] ?? '').trim() }
79
+ return {
80
+ base: (match[1] ?? '').trim(),
81
+ text: (match[2] ?? match[3] ?? '').trim(),
82
+ }
79
83
  }
80
84
 
81
85
  /**
@@ -126,6 +130,7 @@ function isMarker(grouped: readonly { option: Working; group: TrailingGroup }[])
126
130
 
127
131
  interface Working {
128
132
  label: string
133
+ readonly preview?: string
129
134
  readonly recommended: boolean
130
135
  /**
131
136
  * The label ended in "(Recommended)", which came off. That was the marker,
@@ -154,6 +159,7 @@ export function questionOptions(options: readonly AuthoredQuestionOption[]): Use
154
159
  recommended: option.recommended ?? english,
155
160
  hadEnglishMarker: removed,
156
161
  description: option.description,
162
+ ...(option.preview !== undefined ? { preview: option.preview } : {}),
157
163
  }
158
164
  })
159
165
 
@@ -190,6 +196,7 @@ export function questionOptions(options: readonly AuthoredQuestionOption[]): Use
190
196
  id: `opt_${index + 1}`,
191
197
  label: option.label,
192
198
  ...(option.description !== undefined ? { description: option.description } : {}),
199
+ ...(option.preview !== undefined ? { preview: option.preview } : {}),
193
200
  ...(option.recommended ? { recommended: true } : {}),
194
201
  }))
195
202
  }
@@ -27,7 +27,8 @@ export interface DefineToolOptions<S extends z.ZodType> {
27
27
  /** Whether this exact call only observes state; conservative for unknown inputs. */
28
28
  readOnly: boolean | ((input: z.infer<S>) => boolean)
29
29
  destructive: boolean | ((input: z.infer<S>) => boolean)
30
- concurrencySafe: boolean
30
+ /** Whether this exact call may run beside others; a function decides per call. */
31
+ concurrencySafe: boolean | ((input: z.infer<S>) => boolean)
31
32
  /** Whether this exact call sends the screen to the provider; see {@link ToolDefinition.capturesScreen}. */
32
33
  capturesScreen?: boolean | ((input: z.infer<S>) => boolean)
33
34
  /** Whether this exact call always needs a person's approval; see {@link ToolDefinition.requiresApproval}. */
@@ -36,6 +37,8 @@ export interface DefineToolOptions<S extends z.ZodType> {
36
37
  metadata?: Readonly<Record<string, unknown>>
37
38
  /** Batch ordering boundary; see {@link ToolDefinition.executionBarrier}. */
38
39
  executionBarrier?: boolean
40
+ /** The tool bounds its own output; see {@link ToolDefinition.selfLimitingOutput}. */
41
+ selfLimitingOutput?: boolean
39
42
  tier?: string
40
43
  /**
41
44
  * How this tool's call and result should be shown; see
@@ -152,6 +155,9 @@ export function defineTool<S extends z.ZodType>(
152
155
  ...(options.executionBarrier !== undefined
153
156
  ? { executionBarrier: options.executionBarrier }
154
157
  : {}),
158
+ ...(options.selfLimitingOutput !== undefined
159
+ ? { selfLimitingOutput: options.selfLimitingOutput }
160
+ : {}),
155
161
  category: options.category,
156
162
  permissions: options.permissions,
157
163
  isReadOnly:
@@ -160,7 +166,10 @@ export function defineTool<S extends z.ZodType>(
160
166
  typeof options.destructive === 'function'
161
167
  ? options.destructive
162
168
  : constantDestructive(options.destructive as boolean),
163
- isConcurrencySafe: () => options.concurrencySafe,
169
+ isConcurrencySafe:
170
+ typeof options.concurrencySafe === 'function'
171
+ ? options.concurrencySafe
172
+ : () => options.concurrencySafe as boolean,
164
173
  ...(options.capturesScreen !== undefined
165
174
  ? {
166
175
  capturesScreen:
@@ -229,7 +229,7 @@ export async function resolveWithinReal(
229
229
  * existing ancestor with its links resolved, and the rest appended as
230
230
  * written. A path of which nothing exists is returned as it stands.
231
231
  */
232
- async function canonicalize(absolute: string): Promise<string> {
232
+ export async function canonicalize(absolute: string): Promise<string> {
233
233
  // Walk up to the deepest existing ancestor. Terminates at the filesystem
234
234
  // root, where `dirname` becomes a fixed point.
235
235
  let existing = absolute
@@ -94,7 +94,10 @@ export interface QueryAgentConfig extends BaseAgentConfig {
94
94
  toolTimeoutMs?: number
95
95
  toolRetryBackoff?: QueryParams['toolRetryBackoff']
96
96
  maxToolConcurrency?: number
97
+ /** See {@link QueryParams.maxOutputContinuations}. */
98
+ maxOutputContinuations?: QueryParams['maxOutputContinuations']
97
99
  maxToolOutputChars?: number
100
+ maxToolOutputCharsPerMessage?: number
98
101
  retainedToolPreviewChars?: QueryParams['retainedToolPreviewChars']
99
102
  /**
100
103
  * Cap on the RICH channel of a single tool result, in base64 characters.
@@ -165,7 +165,7 @@ export const DEFAULT_ERROR_RULES: readonly ErrorCatalogRule[] = [
165
165
  id: 'provider.context_overflow',
166
166
  when: (facts) => facts.code === 'context_length_exceeded',
167
167
  message: 'The prompt is larger than the model can accept.',
168
- hint: 'Compaction ran and could not make enough room. Lower `compactionConfig.triggerThreshold`, reduce `keepRecentMessages`, or cap large tool outputs so history stops growing faster than it can be shed.',
168
+ hint: 'Compaction ran and could not make enough room. Set a lower `compactionConfig.triggerThreshold`, reduce `keepRecentMessages`, or cap large tool outputs so history stops growing faster than it can be shed.',
169
169
  },
170
170
  {
171
171
  id: 'provider.unavailable',