@qodo/sdk 0.13.3 → 2.0.0-next.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 (764) hide show
  1. package/LICENSE +31 -118
  2. package/README.md +133 -121
  3. package/bin/qodo-skills.mjs +13 -0
  4. package/bundled-skills/code-review/SKILL.md +41 -0
  5. package/bundled-skills/pr-summary/SKILL.md +59 -0
  6. package/bundled-skills/test-gen/SKILL.md +47 -0
  7. package/dist/auth/index.browser.d.ts +38 -0
  8. package/dist/auth/index.browser.d.ts.map +1 -0
  9. package/dist/auth/index.browser.js +62 -0
  10. package/dist/auth/index.browser.js.map +1 -0
  11. package/dist/auth/index.d.ts +44 -30
  12. package/dist/auth/index.d.ts.map +1 -1
  13. package/dist/auth/index.js +57 -110
  14. package/dist/auth/index.js.map +1 -1
  15. package/dist/client/AgentsClient.d.ts +33 -0
  16. package/dist/client/AgentsClient.d.ts.map +1 -0
  17. package/dist/client/AgentsClient.js +40 -0
  18. package/dist/client/AgentsClient.js.map +1 -0
  19. package/dist/client/ArtifactsClient.d.ts +43 -0
  20. package/dist/client/ArtifactsClient.d.ts.map +1 -0
  21. package/dist/client/ArtifactsClient.js +54 -0
  22. package/dist/client/ArtifactsClient.js.map +1 -0
  23. package/dist/client/BulletinClient.d.ts +45 -0
  24. package/dist/client/BulletinClient.d.ts.map +1 -0
  25. package/dist/client/BulletinClient.js +51 -0
  26. package/dist/client/BulletinClient.js.map +1 -0
  27. package/dist/client/InfoClient.d.ts +58 -0
  28. package/dist/client/InfoClient.d.ts.map +1 -0
  29. package/dist/client/InfoClient.js +135 -0
  30. package/dist/client/InfoClient.js.map +1 -0
  31. package/dist/client/PipelineClient.d.ts +162 -0
  32. package/dist/client/PipelineClient.d.ts.map +1 -0
  33. package/dist/client/PipelineClient.js +340 -0
  34. package/dist/client/PipelineClient.js.map +1 -0
  35. package/dist/client/QarRegistryClient.d.ts +396 -0
  36. package/dist/client/QarRegistryClient.d.ts.map +1 -0
  37. package/dist/client/QarRegistryClient.js +536 -0
  38. package/dist/client/QarRegistryClient.js.map +1 -0
  39. package/dist/client/QodoClient.d.ts +296 -0
  40. package/dist/client/QodoClient.d.ts.map +1 -0
  41. package/dist/client/QodoClient.js +803 -0
  42. package/dist/client/QodoClient.js.map +1 -0
  43. package/dist/client/SpecsClient.d.ts +121 -0
  44. package/dist/client/SpecsClient.d.ts.map +1 -0
  45. package/dist/client/SpecsClient.js +252 -0
  46. package/dist/client/SpecsClient.js.map +1 -0
  47. package/dist/client/StateClient.d.ts +35 -0
  48. package/dist/client/StateClient.d.ts.map +1 -0
  49. package/dist/client/StateClient.js +36 -0
  50. package/dist/client/StateClient.js.map +1 -0
  51. package/dist/client/TaskClient.d.ts +706 -0
  52. package/dist/client/TaskClient.d.ts.map +1 -0
  53. package/dist/client/TaskClient.js +2522 -0
  54. package/dist/client/TaskClient.js.map +1 -0
  55. package/dist/client/ToolClient.d.ts +278 -0
  56. package/dist/client/ToolClient.d.ts.map +1 -0
  57. package/dist/client/ToolClient.js +1115 -0
  58. package/dist/client/ToolClient.js.map +1 -0
  59. package/dist/client/a2a/index.d.ts +10 -0
  60. package/dist/client/a2a/index.d.ts.map +1 -0
  61. package/dist/client/a2a/index.js +9 -0
  62. package/dist/client/a2a/index.js.map +1 -0
  63. package/dist/client/a2a/registerA2A.d.ts +170 -0
  64. package/dist/client/a2a/registerA2A.d.ts.map +1 -0
  65. package/dist/client/a2a/registerA2A.js +85 -0
  66. package/dist/client/a2a/registerA2A.js.map +1 -0
  67. package/dist/client/connection.d.ts +800 -0
  68. package/dist/client/connection.d.ts.map +1 -0
  69. package/dist/client/connection.js +2020 -0
  70. package/dist/client/connection.js.map +1 -0
  71. package/dist/client/errors.d.ts +735 -0
  72. package/dist/client/errors.d.ts.map +1 -0
  73. package/dist/client/errors.js +921 -0
  74. package/dist/client/errors.js.map +1 -0
  75. package/dist/client/index.d.ts +26 -0
  76. package/dist/client/index.d.ts.map +1 -0
  77. package/dist/client/index.js +20 -0
  78. package/dist/client/index.js.map +1 -0
  79. package/dist/client/inlineGraph.d.ts +66 -0
  80. package/dist/client/inlineGraph.d.ts.map +1 -0
  81. package/dist/client/inlineGraph.js +500 -0
  82. package/dist/client/inlineGraph.js.map +1 -0
  83. package/dist/client/internal/thenable.d.ts +27 -0
  84. package/dist/client/internal/thenable.d.ts.map +1 -0
  85. package/dist/client/internal/thenable.js +31 -0
  86. package/dist/client/internal/thenable.js.map +1 -0
  87. package/dist/client/iterator.d.ts +32 -0
  88. package/dist/client/iterator.d.ts.map +1 -0
  89. package/dist/client/iterator.js +73 -0
  90. package/dist/client/iterator.js.map +1 -0
  91. package/dist/client/mcp/McpClientPool.browser.d.ts +76 -0
  92. package/dist/client/mcp/McpClientPool.browser.d.ts.map +1 -0
  93. package/dist/client/mcp/McpClientPool.browser.js +78 -0
  94. package/dist/client/mcp/McpClientPool.browser.js.map +1 -0
  95. package/dist/client/mcp/McpClientPool.d.ts +236 -0
  96. package/dist/client/mcp/McpClientPool.d.ts.map +1 -0
  97. package/dist/client/mcp/McpClientPool.js +585 -0
  98. package/dist/client/mcp/McpClientPool.js.map +1 -0
  99. package/dist/client/mcp/projection.d.ts +109 -0
  100. package/dist/client/mcp/projection.d.ts.map +1 -0
  101. package/dist/client/mcp/projection.js +446 -0
  102. package/dist/client/mcp/projection.js.map +1 -0
  103. package/dist/client/mcp/substituteEnv.browser.d.ts +18 -0
  104. package/dist/client/mcp/substituteEnv.browser.d.ts.map +1 -0
  105. package/dist/client/mcp/substituteEnv.browser.js +20 -0
  106. package/dist/client/mcp/substituteEnv.browser.js.map +1 -0
  107. package/dist/client/mcp/substituteEnv.d.ts +45 -0
  108. package/dist/client/mcp/substituteEnv.d.ts.map +1 -0
  109. package/dist/client/mcp/substituteEnv.js +63 -0
  110. package/dist/client/mcp/substituteEnv.js.map +1 -0
  111. package/dist/client/observers.d.ts +57 -0
  112. package/dist/client/observers.d.ts.map +1 -0
  113. package/dist/client/observers.js +203 -0
  114. package/dist/client/observers.js.map +1 -0
  115. package/dist/client/options.d.ts +269 -0
  116. package/dist/client/options.d.ts.map +1 -0
  117. package/dist/client/options.js +9 -0
  118. package/dist/client/options.js.map +1 -0
  119. package/dist/client/tools/_readlineApprovalPrompt.browser.d.ts +17 -0
  120. package/dist/client/tools/_readlineApprovalPrompt.browser.d.ts.map +1 -0
  121. package/dist/client/tools/_readlineApprovalPrompt.browser.js +24 -0
  122. package/dist/client/tools/_readlineApprovalPrompt.browser.js.map +1 -0
  123. package/dist/client/tools/_readlineApprovalPrompt.d.ts +33 -0
  124. package/dist/client/tools/_readlineApprovalPrompt.d.ts.map +1 -0
  125. package/dist/client/tools/_readlineApprovalPrompt.js +90 -0
  126. package/dist/client/tools/_readlineApprovalPrompt.js.map +1 -0
  127. package/dist/client/tools/approval.d.ts +280 -0
  128. package/dist/client/tools/approval.d.ts.map +1 -0
  129. package/dist/client/tools/approval.js +229 -0
  130. package/dist/client/tools/approval.js.map +1 -0
  131. package/dist/client/tools/bindFunctionToolDefs.d.ts +156 -0
  132. package/dist/client/tools/bindFunctionToolDefs.d.ts.map +1 -0
  133. package/dist/client/tools/bindFunctionToolDefs.js +360 -0
  134. package/dist/client/tools/bindFunctionToolDefs.js.map +1 -0
  135. package/dist/client/tools/defineFunctionTool.d.ts +277 -0
  136. package/dist/client/tools/defineFunctionTool.d.ts.map +1 -0
  137. package/dist/client/tools/defineFunctionTool.js +190 -0
  138. package/dist/client/tools/defineFunctionTool.js.map +1 -0
  139. package/dist/client/transport.browser.d.ts +20 -0
  140. package/dist/client/transport.browser.d.ts.map +1 -0
  141. package/dist/client/transport.browser.js +29 -0
  142. package/dist/client/transport.browser.js.map +1 -0
  143. package/dist/client/transport.d.ts +47 -0
  144. package/dist/client/transport.d.ts.map +1 -0
  145. package/dist/client/transport.js +102 -0
  146. package/dist/client/transport.js.map +1 -0
  147. package/dist/client/transport.shared.d.ts +30 -0
  148. package/dist/client/transport.shared.d.ts.map +1 -0
  149. package/dist/client/transport.shared.js +40 -0
  150. package/dist/client/transport.shared.js.map +1 -0
  151. package/dist/client/uuid.d.ts +32 -0
  152. package/dist/client/uuid.d.ts.map +1 -0
  153. package/dist/client/uuid.js +65 -0
  154. package/dist/client/uuid.js.map +1 -0
  155. package/dist/index.d.ts +88 -39
  156. package/dist/index.d.ts.map +1 -1
  157. package/dist/index.js +166 -43
  158. package/dist/index.js.map +1 -1
  159. package/dist/observability/attributes.d.ts +136 -0
  160. package/dist/observability/attributes.d.ts.map +1 -0
  161. package/dist/observability/attributes.js +184 -0
  162. package/dist/observability/attributes.js.map +1 -0
  163. package/dist/observability/index.d.ts +14 -0
  164. package/dist/observability/index.d.ts.map +1 -0
  165. package/dist/observability/index.js +11 -0
  166. package/dist/observability/index.js.map +1 -0
  167. package/dist/observability/resolveOTel.browser.d.ts +13 -0
  168. package/dist/observability/resolveOTel.browser.d.ts.map +1 -0
  169. package/dist/observability/resolveOTel.browser.js +14 -0
  170. package/dist/observability/resolveOTel.browser.js.map +1 -0
  171. package/dist/observability/resolveOTel.d.ts +28 -0
  172. package/dist/observability/resolveOTel.d.ts.map +1 -0
  173. package/dist/observability/resolveOTel.js +74 -0
  174. package/dist/observability/resolveOTel.js.map +1 -0
  175. package/dist/observability/spans.d.ts +198 -0
  176. package/dist/observability/spans.d.ts.map +1 -0
  177. package/dist/observability/spans.js +300 -0
  178. package/dist/observability/spans.js.map +1 -0
  179. package/dist/observability/traceContext.d.ts +51 -0
  180. package/dist/observability/traceContext.d.ts.map +1 -0
  181. package/dist/observability/traceContext.js +151 -0
  182. package/dist/observability/traceContext.js.map +1 -0
  183. package/dist/observability/transportMetrics.d.ts +58 -0
  184. package/dist/observability/transportMetrics.d.ts.map +1 -0
  185. package/dist/observability/transportMetrics.js +55 -0
  186. package/dist/observability/transportMetrics.js.map +1 -0
  187. package/dist/qar/agentSpec.d.ts +93 -0
  188. package/dist/qar/agentSpec.d.ts.map +1 -0
  189. package/dist/qar/agentSpec.js +184 -0
  190. package/dist/qar/agentSpec.js.map +1 -0
  191. package/dist/qar/clientEvents.d.ts +86 -0
  192. package/dist/qar/clientEvents.d.ts.map +1 -0
  193. package/dist/qar/clientEvents.js +36 -0
  194. package/dist/qar/clientEvents.js.map +1 -0
  195. package/dist/qar/envelopes.d.ts +227 -0
  196. package/dist/qar/envelopes.d.ts.map +1 -0
  197. package/dist/qar/envelopes.js +67 -0
  198. package/dist/qar/envelopes.js.map +1 -0
  199. package/dist/qar/generated/envelope.d.ts +332 -0
  200. package/dist/qar/generated/envelope.d.ts.map +1 -0
  201. package/dist/qar/generated/envelope.js +15 -0
  202. package/dist/qar/generated/envelope.js.map +1 -0
  203. package/dist/qar/generated/qar-info.d.ts +76 -0
  204. package/dist/qar/generated/qar-info.d.ts.map +1 -0
  205. package/dist/qar/generated/qar-info.js +15 -0
  206. package/dist/qar/generated/qar-info.js.map +1 -0
  207. package/dist/qar/generated/qodo-task-start-payload.d.ts +54 -0
  208. package/dist/qar/generated/qodo-task-start-payload.d.ts.map +1 -0
  209. package/dist/qar/generated/qodo-task-start-payload.js +15 -0
  210. package/dist/qar/generated/qodo-task-start-payload.js.map +1 -0
  211. package/dist/qar/ids.d.ts +19 -0
  212. package/dist/qar/ids.d.ts.map +1 -0
  213. package/dist/qar/ids.js +11 -0
  214. package/dist/qar/ids.js.map +1 -0
  215. package/dist/qar/index.d.ts +24 -0
  216. package/dist/qar/index.d.ts.map +1 -0
  217. package/dist/qar/index.js +16 -0
  218. package/dist/qar/index.js.map +1 -0
  219. package/dist/qar/info.d.ts +37 -0
  220. package/dist/qar/info.d.ts.map +1 -0
  221. package/dist/qar/info.js +17 -0
  222. package/dist/qar/info.js.map +1 -0
  223. package/dist/qar/json.d.ts +14 -0
  224. package/dist/qar/json.d.ts.map +1 -0
  225. package/dist/qar/json.js +9 -0
  226. package/dist/qar/json.js.map +1 -0
  227. package/dist/qar/payloads.d.ts +480 -0
  228. package/dist/qar/payloads.d.ts.map +1 -0
  229. package/dist/qar/payloads.js +37 -0
  230. package/dist/qar/payloads.js.map +1 -0
  231. package/dist/qar/specs.d.ts +604 -0
  232. package/dist/qar/specs.d.ts.map +1 -0
  233. package/dist/qar/specs.js +29 -0
  234. package/dist/qar/specs.js.map +1 -0
  235. package/dist/qar/taskEvents.d.ts +25 -0
  236. package/dist/qar/taskEvents.d.ts.map +1 -0
  237. package/dist/qar/taskEvents.js +22 -0
  238. package/dist/qar/taskEvents.js.map +1 -0
  239. package/dist/qar/trace.d.ts +12 -0
  240. package/dist/qar/trace.d.ts.map +1 -0
  241. package/dist/qar/trace.js +12 -0
  242. package/dist/qar/trace.js.map +1 -0
  243. package/dist/skills/activation.d.ts +177 -0
  244. package/dist/skills/activation.d.ts.map +1 -0
  245. package/dist/skills/activation.js +428 -0
  246. package/dist/skills/activation.js.map +1 -0
  247. package/dist/skills/cli/index.browser.d.ts +18 -0
  248. package/dist/skills/cli/index.browser.d.ts.map +1 -0
  249. package/dist/skills/cli/index.browser.js +27 -0
  250. package/dist/skills/cli/index.browser.js.map +1 -0
  251. package/dist/skills/cli/index.d.ts +37 -0
  252. package/dist/skills/cli/index.d.ts.map +1 -0
  253. package/dist/skills/cli/index.js +494 -0
  254. package/dist/skills/cli/index.js.map +1 -0
  255. package/dist/skills/events.d.ts +255 -0
  256. package/dist/skills/events.d.ts.map +1 -0
  257. package/dist/skills/events.js +224 -0
  258. package/dist/skills/events.js.map +1 -0
  259. package/dist/skills/index.d.ts +45 -0
  260. package/dist/skills/index.d.ts.map +1 -0
  261. package/dist/skills/index.js +34 -0
  262. package/dist/skills/index.js.map +1 -0
  263. package/dist/skills/inject.d.ts +57 -0
  264. package/dist/skills/inject.d.ts.map +1 -0
  265. package/dist/skills/inject.js +162 -0
  266. package/dist/skills/inject.js.map +1 -0
  267. package/dist/skills/lockfile.browser.d.ts +56 -0
  268. package/dist/skills/lockfile.browser.d.ts.map +1 -0
  269. package/dist/skills/lockfile.browser.js +55 -0
  270. package/dist/skills/lockfile.browser.js.map +1 -0
  271. package/dist/skills/lockfile.d.ts +137 -0
  272. package/dist/skills/lockfile.d.ts.map +1 -0
  273. package/dist/skills/lockfile.js +423 -0
  274. package/dist/skills/lockfile.js.map +1 -0
  275. package/dist/skills/manager.browser.d.ts +94 -0
  276. package/dist/skills/manager.browser.d.ts.map +1 -0
  277. package/dist/skills/manager.browser.js +159 -0
  278. package/dist/skills/manager.browser.js.map +1 -0
  279. package/dist/skills/manager.d.ts +362 -0
  280. package/dist/skills/manager.d.ts.map +1 -0
  281. package/dist/skills/manager.js +1386 -0
  282. package/dist/skills/manager.js.map +1 -0
  283. package/dist/skills/mcp/index.d.ts +15 -0
  284. package/dist/skills/mcp/index.d.ts.map +1 -0
  285. package/dist/skills/mcp/index.js +12 -0
  286. package/dist/skills/mcp/index.js.map +1 -0
  287. package/dist/skills/mcp/path.browser.d.ts +27 -0
  288. package/dist/skills/mcp/path.browser.d.ts.map +1 -0
  289. package/dist/skills/mcp/path.browser.js +33 -0
  290. package/dist/skills/mcp/path.browser.js.map +1 -0
  291. package/dist/skills/mcp/path.d.ts +57 -0
  292. package/dist/skills/mcp/path.d.ts.map +1 -0
  293. package/dist/skills/mcp/path.js +150 -0
  294. package/dist/skills/mcp/path.js.map +1 -0
  295. package/dist/skills/mcp/server.browser.d.ts +32 -0
  296. package/dist/skills/mcp/server.browser.d.ts.map +1 -0
  297. package/dist/skills/mcp/server.browser.js +53 -0
  298. package/dist/skills/mcp/server.browser.js.map +1 -0
  299. package/dist/skills/mcp/server.d.ts +144 -0
  300. package/dist/skills/mcp/server.d.ts.map +1 -0
  301. package/dist/skills/mcp/server.js +841 -0
  302. package/dist/skills/mcp/server.js.map +1 -0
  303. package/dist/skills/mcp/types.d.ts +72 -0
  304. package/dist/skills/mcp/types.d.ts.map +1 -0
  305. package/dist/skills/mcp/types.js +20 -0
  306. package/dist/skills/mcp/types.js.map +1 -0
  307. package/dist/skills/mcp/wireDefs.d.ts +58 -0
  308. package/dist/skills/mcp/wireDefs.d.ts.map +1 -0
  309. package/dist/skills/mcp/wireDefs.js +141 -0
  310. package/dist/skills/mcp/wireDefs.js.map +1 -0
  311. package/dist/skills/parser.d.ts +63 -0
  312. package/dist/skills/parser.d.ts.map +1 -0
  313. package/dist/skills/parser.js +755 -0
  314. package/dist/skills/parser.js.map +1 -0
  315. package/dist/skills/prefilter.d.ts +104 -0
  316. package/dist/skills/prefilter.d.ts.map +1 -0
  317. package/dist/skills/prefilter.js +398 -0
  318. package/dist/skills/prefilter.js.map +1 -0
  319. package/dist/skills/preprocess.d.ts +169 -0
  320. package/dist/skills/preprocess.d.ts.map +1 -0
  321. package/dist/skills/preprocess.js +535 -0
  322. package/dist/skills/preprocess.js.map +1 -0
  323. package/dist/skills/render.d.ts +83 -0
  324. package/dist/skills/render.d.ts.map +1 -0
  325. package/dist/skills/render.js +397 -0
  326. package/dist/skills/render.js.map +1 -0
  327. package/dist/skills/sources/index.browser.d.ts +29 -0
  328. package/dist/skills/sources/index.browser.d.ts.map +1 -0
  329. package/dist/skills/sources/index.browser.js +16 -0
  330. package/dist/skills/sources/index.browser.js.map +1 -0
  331. package/dist/skills/sources/index.d.ts +59 -0
  332. package/dist/skills/sources/index.d.ts.map +1 -0
  333. package/dist/skills/sources/index.js +471 -0
  334. package/dist/skills/sources/index.js.map +1 -0
  335. package/dist/skills/sources/walk.browser.d.ts +17 -0
  336. package/dist/skills/sources/walk.browser.d.ts.map +1 -0
  337. package/dist/skills/sources/walk.browser.js +19 -0
  338. package/dist/skills/sources/walk.browser.js.map +1 -0
  339. package/dist/skills/sources/walk.d.ts +68 -0
  340. package/dist/skills/sources/walk.d.ts.map +1 -0
  341. package/dist/skills/sources/walk.js +264 -0
  342. package/dist/skills/sources/walk.js.map +1 -0
  343. package/dist/skills/substitute.d.ts +87 -0
  344. package/dist/skills/substitute.d.ts.map +1 -0
  345. package/dist/skills/substitute.js +322 -0
  346. package/dist/skills/substitute.js.map +1 -0
  347. package/dist/skills/testing/SkillKit.browser.d.ts +62 -0
  348. package/dist/skills/testing/SkillKit.browser.d.ts.map +1 -0
  349. package/dist/skills/testing/SkillKit.browser.js +41 -0
  350. package/dist/skills/testing/SkillKit.browser.js.map +1 -0
  351. package/dist/skills/testing/SkillKit.d.ts +130 -0
  352. package/dist/skills/testing/SkillKit.d.ts.map +1 -0
  353. package/dist/skills/testing/SkillKit.js +316 -0
  354. package/dist/skills/testing/SkillKit.js.map +1 -0
  355. package/dist/skills/testing/index.d.ts +9 -0
  356. package/dist/skills/testing/index.d.ts.map +1 -0
  357. package/dist/skills/testing/index.js +8 -0
  358. package/dist/skills/testing/index.js.map +1 -0
  359. package/dist/skills/trust.d.ts +72 -0
  360. package/dist/skills/trust.d.ts.map +1 -0
  361. package/dist/skills/trust.js +183 -0
  362. package/dist/skills/trust.js.map +1 -0
  363. package/dist/skills/types.d.ts +627 -0
  364. package/dist/skills/types.d.ts.map +1 -0
  365. package/dist/skills/types.js +85 -0
  366. package/dist/skills/types.js.map +1 -0
  367. package/dist/skills/validator.d.ts +95 -0
  368. package/dist/skills/validator.d.ts.map +1 -0
  369. package/dist/skills/validator.js +486 -0
  370. package/dist/skills/validator.js.map +1 -0
  371. package/dist/tracing/PipelineTracer.d.ts +35 -22
  372. package/dist/tracing/PipelineTracer.d.ts.map +1 -1
  373. package/dist/tracing/PipelineTracer.js +106 -61
  374. package/dist/tracing/PipelineTracer.js.map +1 -1
  375. package/dist/tracing/SdkTracer.d.ts +63 -61
  376. package/dist/tracing/SdkTracer.d.ts.map +1 -1
  377. package/dist/tracing/SdkTracer.js +185 -177
  378. package/dist/tracing/SdkTracer.js.map +1 -1
  379. package/dist/tracing/index.d.ts +10 -1
  380. package/dist/tracing/index.d.ts.map +1 -1
  381. package/dist/tracing/index.js +9 -0
  382. package/dist/tracing/index.js.map +1 -1
  383. package/dist/tracing/types.d.ts +89 -16
  384. package/dist/tracing/types.d.ts.map +1 -1
  385. package/dist/tracing/types.js +17 -4
  386. package/dist/tracing/types.js.map +1 -1
  387. package/dist/types.d.ts +6 -1
  388. package/dist/types.d.ts.map +1 -1
  389. package/dist/types.js +4 -0
  390. package/dist/types.js.map +1 -1
  391. package/dist/version.d.ts.map +1 -1
  392. package/dist/version.js +10 -20
  393. package/dist/version.js.map +1 -1
  394. package/package.json +53 -39
  395. package/.claude/skills/qodo-agent/SKILL.md +0 -974
  396. package/.claude/skills/qodo-agent/assets/programmatic-agent.ts +0 -407
  397. package/.claude/skills/qodo-agent/references/builtin-tools.md +0 -342
  398. package/.claude/skills/qodo-agent/references/common-issues.md +0 -537
  399. package/bin/rg +0 -0
  400. package/dist/api/agent.d.ts +0 -104
  401. package/dist/api/agent.d.ts.map +0 -1
  402. package/dist/api/agent.js +0 -939
  403. package/dist/api/agent.js.map +0 -1
  404. package/dist/api/analytics.d.ts +0 -43
  405. package/dist/api/analytics.d.ts.map +0 -1
  406. package/dist/api/analytics.js +0 -163
  407. package/dist/api/analytics.js.map +0 -1
  408. package/dist/api/http.d.ts +0 -5
  409. package/dist/api/http.d.ts.map +0 -1
  410. package/dist/api/http.js +0 -62
  411. package/dist/api/http.js.map +0 -1
  412. package/dist/api/index.d.ts +0 -12
  413. package/dist/api/index.d.ts.map +0 -1
  414. package/dist/api/index.js +0 -17
  415. package/dist/api/index.js.map +0 -1
  416. package/dist/api/taskTracking.d.ts +0 -54
  417. package/dist/api/taskTracking.d.ts.map +0 -1
  418. package/dist/api/taskTracking.js +0 -208
  419. package/dist/api/taskTracking.js.map +0 -1
  420. package/dist/api/types.d.ts +0 -93
  421. package/dist/api/types.d.ts.map +0 -1
  422. package/dist/api/types.js +0 -2
  423. package/dist/api/types.js.map +0 -1
  424. package/dist/api/utils.d.ts +0 -8
  425. package/dist/api/utils.d.ts.map +0 -1
  426. package/dist/api/utils.js +0 -63
  427. package/dist/api/utils.js.map +0 -1
  428. package/dist/api/websocket.d.ts +0 -203
  429. package/dist/api/websocket.d.ts.map +0 -1
  430. package/dist/api/websocket.js +0 -1166
  431. package/dist/api/websocket.js.map +0 -1
  432. package/dist/bin/install-skill.d.ts +0 -14
  433. package/dist/bin/install-skill.d.ts.map +0 -1
  434. package/dist/bin/install-skill.js +0 -125
  435. package/dist/bin/install-skill.js.map +0 -1
  436. package/dist/bin/run-helpers.d.ts +0 -34
  437. package/dist/bin/run-helpers.d.ts.map +0 -1
  438. package/dist/bin/run-helpers.js +0 -186
  439. package/dist/bin/run-helpers.js.map +0 -1
  440. package/dist/bin/run.d.ts +0 -13
  441. package/dist/bin/run.d.ts.map +0 -1
  442. package/dist/bin/run.js +0 -57
  443. package/dist/bin/run.js.map +0 -1
  444. package/dist/clients/index.d.ts +0 -10
  445. package/dist/clients/index.d.ts.map +0 -1
  446. package/dist/clients/index.js +0 -8
  447. package/dist/clients/index.js.map +0 -1
  448. package/dist/clients/info/InfoClient.d.ts +0 -37
  449. package/dist/clients/info/InfoClient.d.ts.map +0 -1
  450. package/dist/clients/info/InfoClient.js +0 -69
  451. package/dist/clients/info/InfoClient.js.map +0 -1
  452. package/dist/clients/info/index.d.ts +0 -4
  453. package/dist/clients/info/index.d.ts.map +0 -1
  454. package/dist/clients/info/index.js +0 -2
  455. package/dist/clients/info/index.js.map +0 -1
  456. package/dist/clients/info/types.d.ts +0 -21
  457. package/dist/clients/info/types.d.ts.map +0 -1
  458. package/dist/clients/info/types.js +0 -2
  459. package/dist/clients/info/types.js.map +0 -1
  460. package/dist/clients/sessions/SessionsClient.d.ts +0 -34
  461. package/dist/clients/sessions/SessionsClient.d.ts.map +0 -1
  462. package/dist/clients/sessions/SessionsClient.js +0 -71
  463. package/dist/clients/sessions/SessionsClient.js.map +0 -1
  464. package/dist/clients/sessions/index.d.ts +0 -4
  465. package/dist/clients/sessions/index.d.ts.map +0 -1
  466. package/dist/clients/sessions/index.js +0 -2
  467. package/dist/clients/sessions/index.js.map +0 -1
  468. package/dist/clients/sessions/types.d.ts +0 -20
  469. package/dist/clients/sessions/types.d.ts.map +0 -1
  470. package/dist/clients/sessions/types.js +0 -2
  471. package/dist/clients/sessions/types.js.map +0 -1
  472. package/dist/clients/tools/ToolsClient.d.ts +0 -39
  473. package/dist/clients/tools/ToolsClient.d.ts.map +0 -1
  474. package/dist/clients/tools/ToolsClient.js +0 -95
  475. package/dist/clients/tools/ToolsClient.js.map +0 -1
  476. package/dist/clients/tools/index.d.ts +0 -4
  477. package/dist/clients/tools/index.d.ts.map +0 -1
  478. package/dist/clients/tools/index.js +0 -2
  479. package/dist/clients/tools/index.js.map +0 -1
  480. package/dist/clients/tools/types.d.ts +0 -14
  481. package/dist/clients/tools/types.d.ts.map +0 -1
  482. package/dist/clients/tools/types.js +0 -2
  483. package/dist/clients/tools/types.js.map +0 -1
  484. package/dist/config/ConfigManager.d.ts +0 -43
  485. package/dist/config/ConfigManager.d.ts.map +0 -1
  486. package/dist/config/ConfigManager.js +0 -472
  487. package/dist/config/ConfigManager.js.map +0 -1
  488. package/dist/config/index.d.ts +0 -6
  489. package/dist/config/index.d.ts.map +0 -1
  490. package/dist/config/index.js +0 -7
  491. package/dist/config/index.js.map +0 -1
  492. package/dist/config/urlConfig.d.ts +0 -15
  493. package/dist/config/urlConfig.d.ts.map +0 -1
  494. package/dist/config/urlConfig.js +0 -75
  495. package/dist/config/urlConfig.js.map +0 -1
  496. package/dist/constants/errors.d.ts +0 -2
  497. package/dist/constants/errors.d.ts.map +0 -1
  498. package/dist/constants/errors.js +0 -2
  499. package/dist/constants/errors.js.map +0 -1
  500. package/dist/constants/index.d.ts +0 -7
  501. package/dist/constants/index.d.ts.map +0 -1
  502. package/dist/constants/index.js +0 -11
  503. package/dist/constants/index.js.map +0 -1
  504. package/dist/constants/tools.d.ts +0 -4
  505. package/dist/constants/tools.d.ts.map +0 -1
  506. package/dist/constants/tools.js +0 -4
  507. package/dist/constants/tools.js.map +0 -1
  508. package/dist/constants/versions.d.ts +0 -2
  509. package/dist/constants/versions.d.ts.map +0 -1
  510. package/dist/constants/versions.js +0 -2
  511. package/dist/constants/versions.js.map +0 -1
  512. package/dist/context/buildUserContext.d.ts +0 -18
  513. package/dist/context/buildUserContext.d.ts.map +0 -1
  514. package/dist/context/buildUserContext.js +0 -34
  515. package/dist/context/buildUserContext.js.map +0 -1
  516. package/dist/context/index.d.ts +0 -9
  517. package/dist/context/index.d.ts.map +0 -1
  518. package/dist/context/index.js +0 -9
  519. package/dist/context/index.js.map +0 -1
  520. package/dist/context/messageManager.d.ts +0 -42
  521. package/dist/context/messageManager.d.ts.map +0 -1
  522. package/dist/context/messageManager.js +0 -322
  523. package/dist/context/messageManager.js.map +0 -1
  524. package/dist/context/taskFocus.d.ts +0 -2
  525. package/dist/context/taskFocus.d.ts.map +0 -1
  526. package/dist/context/taskFocus.js +0 -26
  527. package/dist/context/taskFocus.js.map +0 -1
  528. package/dist/context/userInput.d.ts +0 -3
  529. package/dist/context/userInput.d.ts.map +0 -1
  530. package/dist/context/userInput.js +0 -20
  531. package/dist/context/userInput.js.map +0 -1
  532. package/dist/mcp/MCPManager.d.ts +0 -109
  533. package/dist/mcp/MCPManager.d.ts.map +0 -1
  534. package/dist/mcp/MCPManager.js +0 -592
  535. package/dist/mcp/MCPManager.js.map +0 -1
  536. package/dist/mcp/approvedTools.d.ts +0 -4
  537. package/dist/mcp/approvedTools.d.ts.map +0 -1
  538. package/dist/mcp/approvedTools.js +0 -19
  539. package/dist/mcp/approvedTools.js.map +0 -1
  540. package/dist/mcp/baseServer.d.ts +0 -75
  541. package/dist/mcp/baseServer.d.ts.map +0 -1
  542. package/dist/mcp/baseServer.js +0 -107
  543. package/dist/mcp/baseServer.js.map +0 -1
  544. package/dist/mcp/builtinServers.d.ts +0 -15
  545. package/dist/mcp/builtinServers.d.ts.map +0 -1
  546. package/dist/mcp/builtinServers.js +0 -141
  547. package/dist/mcp/builtinServers.js.map +0 -1
  548. package/dist/mcp/dynamicBEServer.d.ts +0 -20
  549. package/dist/mcp/dynamicBEServer.d.ts.map +0 -1
  550. package/dist/mcp/dynamicBEServer.js +0 -52
  551. package/dist/mcp/dynamicBEServer.js.map +0 -1
  552. package/dist/mcp/index.d.ts +0 -18
  553. package/dist/mcp/index.d.ts.map +0 -1
  554. package/dist/mcp/index.js +0 -23
  555. package/dist/mcp/index.js.map +0 -1
  556. package/dist/mcp/mcpInitialization.d.ts +0 -2
  557. package/dist/mcp/mcpInitialization.d.ts.map +0 -1
  558. package/dist/mcp/mcpInitialization.js +0 -56
  559. package/dist/mcp/mcpInitialization.js.map +0 -1
  560. package/dist/mcp/servers/filesystem.d.ts +0 -44
  561. package/dist/mcp/servers/filesystem.d.ts.map +0 -1
  562. package/dist/mcp/servers/filesystem.js +0 -776
  563. package/dist/mcp/servers/filesystem.js.map +0 -1
  564. package/dist/mcp/servers/git.d.ts +0 -18
  565. package/dist/mcp/servers/git.d.ts.map +0 -1
  566. package/dist/mcp/servers/git.js +0 -441
  567. package/dist/mcp/servers/git.js.map +0 -1
  568. package/dist/mcp/servers/ripgrep.d.ts +0 -39
  569. package/dist/mcp/servers/ripgrep.d.ts.map +0 -1
  570. package/dist/mcp/servers/ripgrep.js +0 -550
  571. package/dist/mcp/servers/ripgrep.js.map +0 -1
  572. package/dist/mcp/servers/shell.d.ts +0 -20
  573. package/dist/mcp/servers/shell.d.ts.map +0 -1
  574. package/dist/mcp/servers/shell.js +0 -519
  575. package/dist/mcp/servers/shell.js.map +0 -1
  576. package/dist/mcp/serversRegistry.d.ts +0 -55
  577. package/dist/mcp/serversRegistry.d.ts.map +0 -1
  578. package/dist/mcp/serversRegistry.js +0 -416
  579. package/dist/mcp/serversRegistry.js.map +0 -1
  580. package/dist/mcp/toolProcessor.d.ts +0 -82
  581. package/dist/mcp/toolProcessor.d.ts.map +0 -1
  582. package/dist/mcp/toolProcessor.js +0 -392
  583. package/dist/mcp/toolProcessor.js.map +0 -1
  584. package/dist/mcp/types.d.ts +0 -29
  585. package/dist/mcp/types.d.ts.map +0 -1
  586. package/dist/mcp/types.js +0 -2
  587. package/dist/mcp/types.js.map +0 -1
  588. package/dist/messages/index.d.ts +0 -8
  589. package/dist/messages/index.d.ts.map +0 -1
  590. package/dist/messages/index.js +0 -7
  591. package/dist/messages/index.js.map +0 -1
  592. package/dist/messages/openai.d.ts +0 -26
  593. package/dist/messages/openai.d.ts.map +0 -1
  594. package/dist/messages/openai.js +0 -55
  595. package/dist/messages/openai.js.map +0 -1
  596. package/dist/messages/types.d.ts +0 -73
  597. package/dist/messages/types.d.ts.map +0 -1
  598. package/dist/messages/types.js +0 -78
  599. package/dist/messages/types.js.map +0 -1
  600. package/dist/parser/index.d.ts +0 -72
  601. package/dist/parser/index.d.ts.map +0 -1
  602. package/dist/parser/index.js +0 -967
  603. package/dist/parser/index.js.map +0 -1
  604. package/dist/parser/types.d.ts +0 -153
  605. package/dist/parser/types.d.ts.map +0 -1
  606. package/dist/parser/types.js +0 -6
  607. package/dist/parser/types.js.map +0 -1
  608. package/dist/parser/utils.d.ts +0 -18
  609. package/dist/parser/utils.d.ts.map +0 -1
  610. package/dist/parser/utils.js +0 -64
  611. package/dist/parser/utils.js.map +0 -1
  612. package/dist/sdk/QodoSDK.d.ts +0 -218
  613. package/dist/sdk/QodoSDK.d.ts.map +0 -1
  614. package/dist/sdk/QodoSDK.js +0 -1115
  615. package/dist/sdk/QodoSDK.js.map +0 -1
  616. package/dist/sdk/artifacts.d.ts +0 -156
  617. package/dist/sdk/artifacts.d.ts.map +0 -1
  618. package/dist/sdk/artifacts.js +0 -166
  619. package/dist/sdk/artifacts.js.map +0 -1
  620. package/dist/sdk/bootstrap.d.ts +0 -16
  621. package/dist/sdk/bootstrap.d.ts.map +0 -1
  622. package/dist/sdk/bootstrap.js +0 -28
  623. package/dist/sdk/bootstrap.js.map +0 -1
  624. package/dist/sdk/builders.d.ts +0 -54
  625. package/dist/sdk/builders.d.ts.map +0 -1
  626. package/dist/sdk/builders.js +0 -117
  627. package/dist/sdk/builders.js.map +0 -1
  628. package/dist/sdk/defaults.d.ts +0 -11
  629. package/dist/sdk/defaults.d.ts.map +0 -1
  630. package/dist/sdk/defaults.js +0 -39
  631. package/dist/sdk/defaults.js.map +0 -1
  632. package/dist/sdk/discovery.d.ts +0 -2
  633. package/dist/sdk/discovery.d.ts.map +0 -1
  634. package/dist/sdk/discovery.js +0 -25
  635. package/dist/sdk/discovery.js.map +0 -1
  636. package/dist/sdk/events.d.ts +0 -269
  637. package/dist/sdk/events.d.ts.map +0 -1
  638. package/dist/sdk/events.js +0 -69
  639. package/dist/sdk/events.js.map +0 -1
  640. package/dist/sdk/exit-expression.d.ts +0 -13
  641. package/dist/sdk/exit-expression.d.ts.map +0 -1
  642. package/dist/sdk/exit-expression.js +0 -35
  643. package/dist/sdk/exit-expression.js.map +0 -1
  644. package/dist/sdk/index.d.ts +0 -17
  645. package/dist/sdk/index.d.ts.map +0 -1
  646. package/dist/sdk/index.js +0 -17
  647. package/dist/sdk/index.js.map +0 -1
  648. package/dist/sdk/middleware.d.ts +0 -59
  649. package/dist/sdk/middleware.d.ts.map +0 -1
  650. package/dist/sdk/middleware.js +0 -69
  651. package/dist/sdk/middleware.js.map +0 -1
  652. package/dist/sdk/pipeline/PipelineBuilder.d.ts +0 -79
  653. package/dist/sdk/pipeline/PipelineBuilder.d.ts.map +0 -1
  654. package/dist/sdk/pipeline/PipelineBuilder.js +0 -129
  655. package/dist/sdk/pipeline/PipelineBuilder.js.map +0 -1
  656. package/dist/sdk/pipeline/PipelineRunner.d.ts +0 -28
  657. package/dist/sdk/pipeline/PipelineRunner.d.ts.map +0 -1
  658. package/dist/sdk/pipeline/PipelineRunner.js +0 -326
  659. package/dist/sdk/pipeline/PipelineRunner.js.map +0 -1
  660. package/dist/sdk/pipeline/compiler.d.ts +0 -24
  661. package/dist/sdk/pipeline/compiler.d.ts.map +0 -1
  662. package/dist/sdk/pipeline/compiler.js +0 -199
  663. package/dist/sdk/pipeline/compiler.js.map +0 -1
  664. package/dist/sdk/pipeline/declarative.d.ts +0 -34
  665. package/dist/sdk/pipeline/declarative.d.ts.map +0 -1
  666. package/dist/sdk/pipeline/declarative.js +0 -9
  667. package/dist/sdk/pipeline/declarative.js.map +0 -1
  668. package/dist/sdk/pipeline/index.d.ts +0 -20
  669. package/dist/sdk/pipeline/index.d.ts.map +0 -1
  670. package/dist/sdk/pipeline/index.js +0 -19
  671. package/dist/sdk/pipeline/index.js.map +0 -1
  672. package/dist/sdk/pipeline/types.d.ts +0 -93
  673. package/dist/sdk/pipeline/types.d.ts.map +0 -1
  674. package/dist/sdk/pipeline/types.js +0 -10
  675. package/dist/sdk/pipeline/types.js.map +0 -1
  676. package/dist/sdk/policies.d.ts +0 -163
  677. package/dist/sdk/policies.d.ts.map +0 -1
  678. package/dist/sdk/policies.js +0 -243
  679. package/dist/sdk/policies.js.map +0 -1
  680. package/dist/sdk/runner/AgentRunner.d.ts +0 -22
  681. package/dist/sdk/runner/AgentRunner.d.ts.map +0 -1
  682. package/dist/sdk/runner/AgentRunner.js +0 -222
  683. package/dist/sdk/runner/AgentRunner.js.map +0 -1
  684. package/dist/sdk/runner/finalize.d.ts +0 -56
  685. package/dist/sdk/runner/finalize.d.ts.map +0 -1
  686. package/dist/sdk/runner/finalize.js +0 -155
  687. package/dist/sdk/runner/finalize.js.map +0 -1
  688. package/dist/sdk/runner/formats.d.ts +0 -7
  689. package/dist/sdk/runner/formats.d.ts.map +0 -1
  690. package/dist/sdk/runner/formats.js +0 -76
  691. package/dist/sdk/runner/formats.js.map +0 -1
  692. package/dist/sdk/runner/index.d.ts +0 -9
  693. package/dist/sdk/runner/index.d.ts.map +0 -1
  694. package/dist/sdk/runner/index.js +0 -9
  695. package/dist/sdk/runner/index.js.map +0 -1
  696. package/dist/sdk/runner/progress.d.ts +0 -3
  697. package/dist/sdk/runner/progress.d.ts.map +0 -1
  698. package/dist/sdk/runner/progress.js +0 -16
  699. package/dist/sdk/runner/progress.js.map +0 -1
  700. package/dist/sdk/schemas.d.ts +0 -72
  701. package/dist/sdk/schemas.d.ts.map +0 -1
  702. package/dist/sdk/schemas.js +0 -282
  703. package/dist/sdk/schemas.js.map +0 -1
  704. package/dist/sdk/trigger-context.d.ts +0 -24
  705. package/dist/sdk/trigger-context.d.ts.map +0 -1
  706. package/dist/sdk/trigger-context.js +0 -136
  707. package/dist/sdk/trigger-context.js.map +0 -1
  708. package/dist/session/SessionContext.d.ts +0 -89
  709. package/dist/session/SessionContext.d.ts.map +0 -1
  710. package/dist/session/SessionContext.js +0 -410
  711. package/dist/session/SessionContext.js.map +0 -1
  712. package/dist/session/environment.d.ts +0 -52
  713. package/dist/session/environment.d.ts.map +0 -1
  714. package/dist/session/environment.js +0 -27
  715. package/dist/session/environment.js.map +0 -1
  716. package/dist/session/history.d.ts +0 -18
  717. package/dist/session/history.d.ts.map +0 -1
  718. package/dist/session/history.js +0 -68
  719. package/dist/session/history.js.map +0 -1
  720. package/dist/session/index.d.ts +0 -10
  721. package/dist/session/index.d.ts.map +0 -1
  722. package/dist/session/index.js +0 -9
  723. package/dist/session/index.js.map +0 -1
  724. package/dist/session/serverData.d.ts +0 -38
  725. package/dist/session/serverData.d.ts.map +0 -1
  726. package/dist/session/serverData.js +0 -261
  727. package/dist/session/serverData.js.map +0 -1
  728. package/dist/tracing/pipelineHelpers.d.ts +0 -29
  729. package/dist/tracing/pipelineHelpers.d.ts.map +0 -1
  730. package/dist/tracing/pipelineHelpers.js +0 -224
  731. package/dist/tracing/pipelineHelpers.js.map +0 -1
  732. package/dist/tracking/Tracker.d.ts +0 -55
  733. package/dist/tracking/Tracker.d.ts.map +0 -1
  734. package/dist/tracking/Tracker.js +0 -217
  735. package/dist/tracking/Tracker.js.map +0 -1
  736. package/dist/tracking/index.d.ts +0 -8
  737. package/dist/tracking/index.d.ts.map +0 -1
  738. package/dist/tracking/index.js +0 -8
  739. package/dist/tracking/index.js.map +0 -1
  740. package/dist/tracking/schemas.d.ts +0 -292
  741. package/dist/tracking/schemas.d.ts.map +0 -1
  742. package/dist/tracking/schemas.js +0 -91
  743. package/dist/tracking/schemas.js.map +0 -1
  744. package/dist/utils/extractSetFlags.d.ts +0 -6
  745. package/dist/utils/extractSetFlags.d.ts.map +0 -1
  746. package/dist/utils/extractSetFlags.js +0 -16
  747. package/dist/utils/extractSetFlags.js.map +0 -1
  748. package/dist/utils/formatTimeAgo.d.ts +0 -2
  749. package/dist/utils/formatTimeAgo.d.ts.map +0 -1
  750. package/dist/utils/formatTimeAgo.js +0 -20
  751. package/dist/utils/formatTimeAgo.js.map +0 -1
  752. package/dist/utils/index.d.ts +0 -12
  753. package/dist/utils/index.d.ts.map +0 -1
  754. package/dist/utils/index.js +0 -12
  755. package/dist/utils/index.js.map +0 -1
  756. package/dist/utils/machineId.d.ts +0 -14
  757. package/dist/utils/machineId.d.ts.map +0 -1
  758. package/dist/utils/machineId.js +0 -66
  759. package/dist/utils/machineId.js.map +0 -1
  760. package/dist/utils/pathUtils.d.ts +0 -22
  761. package/dist/utils/pathUtils.d.ts.map +0 -1
  762. package/dist/utils/pathUtils.js +0 -54
  763. package/dist/utils/pathUtils.js.map +0 -1
  764. package/scripts/download-ripgrep.js +0 -269
@@ -0,0 +1,1115 @@
1
+ /**
2
+ * `ToolClient` — surfaces inbound `tool.request` envelopes
3
+ * (parallel-batched) and accepts outbound `tool.response` envelopes
4
+ * (same shape).
5
+ *
6
+ * The parallel-batched envelope shape:
7
+ *
8
+ * - `tool.request.payload` carries `calls: ToolCall[]` (always a list, even
9
+ * for a single call — modern LLM `parallel_tool_use`) and a required
10
+ * `node_name`. The handler receives the full envelope; `req.payload.calls`
11
+ * is the call list, `req.payload.node_name` is the originating node.
12
+ * - `tool.response.payload` carries `responses: ToolResponseItem[]` (same
13
+ * length / membership as the request's `calls`) and the same `node_name`.
14
+ *
15
+ * Handler return shapes:
16
+ *
17
+ * - returns `ToolResponseResult` (single SDK union) → auto-batched
18
+ * into `responses: [resp]` against `calls[0]`. Requires `calls.length === 1`;
19
+ * otherwise a `QodoWireValidationError` surfaces. The most common shape
20
+ * after a 1.x → 2.0 migration (where every request was single-call).
21
+ * - returns `readonly ToolResponseResult[]` (one per call) → auto-batched
22
+ * positionally; length must equal `calls.length`.
23
+ * - returns `undefined` → fall through
24
+ * to the next handler in the stack; if every handler returns `undefined`
25
+ * the consumer is expected to call `respond()` later (approval flow / async
26
+ * tool). With zero handlers, the SDK emits a `denied` envelope so QAR's
27
+ * agent loop doesn't hang.
28
+ *
29
+ * `respond(nodeName, response | responses)` is the manual escape hatch. It
30
+ * builds a `tool.response` envelope and writes it through the connection. No
31
+ * ack: QAR's `task.delta` / `task.done` chain advances on its own once the
32
+ * response lands.
33
+ *
34
+ * Concurrency: each `tool.request` envelope carries N calls but is dispatched
35
+ * as a single handler invocation. QAR may emit multiple `tool.request`
36
+ * envelopes per task (separate dispatch rounds); each runs through the stack
37
+ * independently. The wire is keyed on `tool_call_id` for each individual call.
38
+ *
39
+ * Error mapping (per-call):
40
+ * - handler throws `ToolDeniedError` → `status: 'error'`, `error.code: 'denied'`
41
+ * - handler throws anything else → `status: 'error'`, `error.code: 'error'`
42
+ * - returns malformed result → `status: 'error'`, generic message
43
+ * (handler internals never leak to QAR)
44
+ *
45
+ * Error/reason strings are truncated so a stack trace can't blow up the wire
46
+ * payload.
47
+ */
48
+ import { QodoWireValidationError, ToolDeniedError } from './errors.js';
49
+ import { isThenable } from './internal/thenable.js';
50
+ import { toolOutcomeFor } from '../observability/spans.js';
51
+ import { QAR_TOOL_OUTCOME } from '../observability/attributes.js';
52
+ import { FunctionToolRouter } from './tools/bindFunctionToolDefs.js';
53
+ import { defaultApprovalHandler, } from './tools/approval.js';
54
+ /** Cap on `error.message` strings serialized into `tool.response` items. */
55
+ const MAX_ERROR_LEN = 1024;
56
+ export class ToolClient {
57
+ resolveConnection;
58
+ spanRecorder;
59
+ handlers = [];
60
+ /**
61
+ * SDK-attached auto-bridge handlers (qodo-skills, remote-MCP). Distinct
62
+ * from {@link handlers} (consumer-registered via `onRequest`) so the
63
+ * dispatcher's deterministic-deny path can fire when only SDK fallbacks
64
+ * are in the stack and no consumer-side handler exists.
65
+ *
66
+ * Without this split, the bridge-being-attached defeats the
67
+ * zero-primary-handler deny fallback. Walking primaries first,
68
+ * fallbacks second, with deny keyed on `primaries.length === 0`
69
+ * (regardless of fallback count) gives consumers the
70
+ * deterministic-deny ergonomic they expect from "I didn't register
71
+ * a handler" + lets the SDK auto-bridges still route MCP / skills
72
+ * calls when the consumer DOES register handlers later.
73
+ */
74
+ fallbackHandlers = [];
75
+ wireValidationHandlers = [];
76
+ dispatcher = null;
77
+ dispatcherConnection = null;
78
+ /**
79
+ * `defineFunctionTool` router — owns the per-tool-name handler map
80
+ * and a single `onRequest` subscription that dispatches inbound
81
+ * `tool.request` calls by name. Lazily attached the first time a
82
+ * function-tool handler is registered.
83
+ */
84
+ functionToolRouter = new FunctionToolRouter();
85
+ /**
86
+ * S4 — consumer-overridable approval resolver for
87
+ * `requireApproval`-gated function tools. Defaults to the
88
+ * stdin-prompt-when-TTY-else-deny handler from
89
+ * `src/client/tools/approval.ts`. Production apps register a custom
90
+ * handler (Slack, web UI, pager) via {@link setApprovalHandler}.
91
+ *
92
+ * Mutable so a `setApprovalHandler` call AFTER the router attaches
93
+ * still applies to subsequent batches — the router captures a
94
+ * getter, not the value, at attach time.
95
+ */
96
+ approvalHandler = defaultApprovalHandler;
97
+ /**
98
+ * @param resolveConnection Returns the live `Connection` or throws if the
99
+ * client isn't connected yet.
100
+ * @param spanRecorder Recorder for `qar.client.tool.*` spans. No-op
101
+ * when OTel isn't configured.
102
+ */
103
+ constructor(resolveConnection, spanRecorder) {
104
+ this.resolveConnection = resolveConnection;
105
+ this.spanRecorder = spanRecorder;
106
+ }
107
+ /**
108
+ * Register a handler for inbound `tool.request` events.
109
+ *
110
+ * Stacking semantics: the first handler that returns a non-`undefined`
111
+ * value (single result or array) claims the entire request; its responses
112
+ * are auto-sent. If every handler returns `undefined`, the consumer must
113
+ * call `respond()` explicitly within QAR's per-task tool-response timeout.
114
+ *
115
+ * `QodoClient.connect()` already attaches the fan-out dispatcher on the
116
+ * live connection (so a `tool.request` arriving with zero handlers
117
+ * deterministically emits `status: 'error'` / `error.code: 'denied'`
118
+ * instead of leaving QAR paused on the call set). `onRequest` just adds to
119
+ * the stack.
120
+ *
121
+ * If `attachDispatcher()` throws — e.g. the caller registered a handler
122
+ * before `connect()` and we have no connection to attach to — we roll
123
+ * back the handler push so consumers don't end up with an orphaned
124
+ * registration they can't `unsubscribe()`.
125
+ */
126
+ onRequest(handler) {
127
+ this.handlers.push(handler);
128
+ try {
129
+ this.attachDispatcher();
130
+ }
131
+ catch (err) {
132
+ // Roll back the just-added handler so the failed call leaves no trace.
133
+ const idx = this.handlers.lastIndexOf(handler);
134
+ if (idx >= 0)
135
+ this.handlers.splice(idx, 1);
136
+ throw err;
137
+ }
138
+ return {
139
+ unsubscribe: () => {
140
+ const idx = this.handlers.indexOf(handler);
141
+ if (idx >= 0)
142
+ this.handlers.splice(idx, 1);
143
+ // Don't detach the dispatcher when the stack empties — the bound
144
+ // dispatcher is the deterministic-deny path for `tool.request`s
145
+ // that arrive while no handler is registered. Detach only on
146
+ // disconnect (handled by the dispatcher's `onConnectionClosed`).
147
+ },
148
+ };
149
+ }
150
+ /**
151
+ * Register `defineFunctionTool`-produced entries against the
152
+ * client's internal router. Any tool in `tools` carrying the
153
+ * `defineFunctionTool` handler symbol gets added to the router's
154
+ * name → handler table; tools without the symbol (e.g. hand-authored
155
+ * `FunctionToolDef` literals) are skipped.
156
+ *
157
+ * Idempotent — re-registering the same tool name replaces the
158
+ * existing handler in-place. The router's `onRequest` subscription
159
+ * is attached lazily on first registration; it stays attached for
160
+ * the lifetime of the client.
161
+ *
162
+ * `tasks.startWithAgent` / `tasks.startWithGraph` auto-call this
163
+ * with `agent.tools` so the common path "drop helpers into
164
+ * `tools[]`" needs no manual binding. The explicit method is
165
+ * available for consumers using `tasks.start` (server-defined
166
+ * agent_id) who still want type-safe handler organization by tool
167
+ * name, or for any pre-task registration flow.
168
+ *
169
+ * @returns Number of newly registered handlers from this call.
170
+ */
171
+ bindFunctionTools(tools) {
172
+ const added = this.functionToolRouter.register(tools);
173
+ if (added > 0)
174
+ this.functionToolRouter.attach(this);
175
+ return added;
176
+ }
177
+ /**
178
+ * Register the resolver invoked when a `requireApproval`-gated
179
+ * function tool is about to dispatch (S4). The handler decides
180
+ * approve / deny; the SDK runs the function-tool handler on
181
+ * approve and returns `outcome: 'denied'` to QAR on deny.
182
+ *
183
+ * The default handler prompts on stdin when `process.stdin.isTTY`
184
+ * is true; in non-interactive contexts it deterministic-denies
185
+ * with a typed error message pointing at the missing registration.
186
+ * Production apps that route approvals to Slack / a web UI / a
187
+ * pager should register a custom handler at startup.
188
+ *
189
+ * Late binding is fine — a handler registered AFTER the router
190
+ * has attached still applies to subsequent inbound batches.
191
+ *
192
+ * @see {@link ApprovalHandler} for the resolver contract.
193
+ * @see `src/client/tools/approval.ts` for the default behavior.
194
+ */
195
+ setApprovalHandler(handler) {
196
+ if (typeof handler !== 'function') {
197
+ throw new TypeError('ToolClient.setApprovalHandler: handler must be a function');
198
+ }
199
+ this.approvalHandler = handler;
200
+ }
201
+ /**
202
+ * @internal — read by `FunctionToolRouter.attach` so dispatch-time
203
+ * approvals see the consumer's current handler (or the default).
204
+ * Exposed under the `_` prefix to keep it off the public IDE
205
+ * autocomplete; same convention as
206
+ * {@link _attachFallbackHandler} / {@link _bindConnection}.
207
+ */
208
+ _getApprovalHandler() {
209
+ return this.approvalHandler;
210
+ }
211
+ /**
212
+ * @internal — used by `FunctionToolRouter` to pre-check the wire
213
+ * after an approval resolves, BEFORE invoking the function-tool
214
+ * handler. Without the gate, a long-parked approval (Slack / web UI
215
+ * / pager round-trip) can resolve approve AFTER the auto-reconnect
216
+ * loop exhausted; the handler then runs (real-world side effect),
217
+ * `sendResponse` throws {@link QodoColdAddressError} because
218
+ * `clearTerminalState` already wiped the per-tool-call session map,
219
+ * and the swallow path in {@link attachDispatcher}'s send-fallback
220
+ * drops the response without a signal. Pinned in
221
+ * `tests/qar-mode/hitl-state-machine-matrix.test.ts`.
222
+ *
223
+ * Returns `false` when the connection is not currently addressable
224
+ * for outbound sends — either pre-`connect()`, mid-reconnect, or
225
+ * post-`failed`. The router synthesizes
226
+ * `outcome: 'error', error: 'connection lost before approval resolved'`
227
+ * for this case so the handler never executes; the result still
228
+ * routes through the normal dispatcher send path, which (correctly)
229
+ * swallows it once more because the wire is dead — but the
230
+ * load-bearing behavior is the **handler skip**, not the response.
231
+ */
232
+ _isConnectionLive() {
233
+ return this.isConnectionLive();
234
+ }
235
+ /**
236
+ * @internal — wired by `QodoClient.connect()` once the WS is open. Attaches
237
+ * a fan-out dispatcher to the connection so an inbound `tool.request` always
238
+ * round-trips a response, even when no `onRequest` handler is registered.
239
+ * Without this seam QAR's agent loop would hang on the call set.
240
+ */
241
+ _bindConnection(connection) {
242
+ this.attachDispatcher(connection);
243
+ }
244
+ /**
245
+ * @internal — used by `QodoClient` to attach SDK-side auto-bridges
246
+ * (`qodo-skills` MCP, generic remote-MCP). Fallback handlers are
247
+ * walked AFTER primary handlers (consumer-registered via {@link
248
+ * onRequest}). They don't count toward the dispatcher's
249
+ * "zero-handlers → deterministic deny" check, so a consumer who
250
+ * registers a remote MCP but no `onRequest` handler still sees the
251
+ * deny fallback fire on unknown tools rather than hanging.
252
+ *
253
+ * Same registration / unsubscribe ergonomics as {@link onRequest};
254
+ * the API split is purely internal so consumers don't accidentally
255
+ * register against the fallback stack.
256
+ */
257
+ _attachFallbackHandler(handler) {
258
+ this.fallbackHandlers.push(handler);
259
+ try {
260
+ this.attachDispatcher();
261
+ }
262
+ catch (err) {
263
+ const idx = this.fallbackHandlers.lastIndexOf(handler);
264
+ if (idx >= 0)
265
+ this.fallbackHandlers.splice(idx, 1);
266
+ throw err;
267
+ }
268
+ return {
269
+ unsubscribe: () => {
270
+ const idx = this.fallbackHandlers.indexOf(handler);
271
+ if (idx >= 0)
272
+ this.fallbackHandlers.splice(idx, 1);
273
+ },
274
+ };
275
+ }
276
+ /**
277
+ * Register a handler for inbound wire-validation failures — `tool.request`
278
+ * envelopes the SDK dropped because they violated the wire shape
279
+ * (missing `node_name`, empty `calls`). The handler receives a typed
280
+ * `QodoWireValidationError` that carries the offending `message_id` and
281
+ * the field that failed validation, so consumer telemetry can branch on
282
+ * the exact protocol drift without parsing the `[@qodo/sdk]` log line.
283
+ *
284
+ * Observer-style: every registered handler fires on every drop (no
285
+ * winner-takes-all dispatch). Handlers run inside a defensive try/catch
286
+ * — a throwing handler logs to `console.error` and is otherwise swallowed
287
+ * so the dispatch hot path stays alive.
288
+ *
289
+ * The SDK has no `tool_call_id` to respond against for these envelopes,
290
+ * so registering a handler does NOT make the runtime see a response —
291
+ * QAR's per-task tool-response timeout remains the wire-side safety net.
292
+ * This surface exists purely so consumer code can observe the failure.
293
+ *
294
+ * Returning `Subscription` mirrors `onRequest` — `unsubscribe()` removes
295
+ * the handler; the dispatcher itself stays attached (it's the path that
296
+ * fires the breadcrumb log even when no handler is registered).
297
+ */
298
+ onWireValidationError(handler) {
299
+ this.wireValidationHandlers.push(handler);
300
+ try {
301
+ this.attachDispatcher();
302
+ }
303
+ catch (err) {
304
+ // Roll back the registration so the failed call leaves no trace.
305
+ const idx = this.wireValidationHandlers.lastIndexOf(handler);
306
+ if (idx >= 0)
307
+ this.wireValidationHandlers.splice(idx, 1);
308
+ throw err;
309
+ }
310
+ return {
311
+ unsubscribe: () => {
312
+ const idx = this.wireValidationHandlers.indexOf(handler);
313
+ if (idx >= 0)
314
+ this.wireValidationHandlers.splice(idx, 1);
315
+ },
316
+ };
317
+ }
318
+ /**
319
+ * @internal — invoked by the dispatcher when it drops a malformed inbound
320
+ * `tool.request`. Fires every registered `onWireValidationError` handler
321
+ * with a typed error; per-handler exceptions are logged + swallowed so
322
+ * one buggy observer can't crash dispatch.
323
+ */
324
+ emitWireValidationError(err) {
325
+ // Snapshot the handler list — a handler may unsubscribe itself while
326
+ // we iterate. Mirrors `dispatch()`'s pattern with `onRequest` handlers.
327
+ const snapshot = [...this.wireValidationHandlers];
328
+ for (const handler of snapshot) {
329
+ try {
330
+ // Sync throw → caught here. Async rejection → caught by the
331
+ // Promise.resolve() chain below. Without the async guard a
332
+ // consumer registering `async (err) => { await persist(err); }`
333
+ // would surface their handler's rejections as a process-level
334
+ // `unhandledRejection`.
335
+ const result = handler(err);
336
+ if (isThenable(result)) {
337
+ result.then(undefined, (handlerErr) => {
338
+ logHandlerObserverError('onWireValidationError', handlerErr);
339
+ });
340
+ }
341
+ }
342
+ catch (handlerErr) {
343
+ logHandlerObserverError('onWireValidationError', handlerErr);
344
+ }
345
+ }
346
+ }
347
+ async respond(nodeName, responses, opts) {
348
+ validateNodeName(nodeName, 'ToolClient.respond');
349
+ const batch = Array.isArray(responses)
350
+ ? responses
351
+ : [responses];
352
+ if (batch.length === 0) {
353
+ throw new QodoWireValidationError('ToolClient.respond: responses must be a non-empty list — tool.response always carries ≥ 1 item.');
354
+ }
355
+ for (const r of batch) {
356
+ if (!isValidToolResponseResult(r.result)) {
357
+ throw new TypeError('ToolClient.respond: every response.result must be a ToolResponseResult ({ outcome: "success" | "error" | "denied", ... })');
358
+ }
359
+ if (typeof r.tool_call_id !== 'string' || r.tool_call_id.length === 0) {
360
+ throw new TypeError('ToolClient.respond: every response must carry a non-empty tool_call_id');
361
+ }
362
+ }
363
+ const connection = this.resolveConnection();
364
+ // Per-call span family — one `qar.client.tool.respond` span per response
365
+ // item so per-call latency + outcome land independently in traces. Spans
366
+ // share the same parent (whatever was active when the consumer called
367
+ // respond()).
368
+ //
369
+ // The per-call `session_id` is looked up by tool_call_id — every
370
+ // response item echoes back to the session of its originating
371
+ // `tool.request`. When the consumer passed `opts.sessionId`
372
+ // (cold-respond after process restart), use it for the span
373
+ // attribute — the in-memory map will be empty in that scenario, but
374
+ // the consumer knows the right value. `Connection.sendEnvelope`
375
+ // performs its own lookup and throws `QodoColdAddressError` on miss
376
+ // if no override is provided.
377
+ const spans = batch.map((r) => this.spanRecorder.startToolRespondSpan({
378
+ sessionId: opts?.sessionId ?? connection.getSessionForToolCall(r.tool_call_id),
379
+ toolCallId: r.tool_call_id,
380
+ outcome: toolOutcomeFor(r.result.outcome),
381
+ nodeName,
382
+ }));
383
+ try {
384
+ // Use the FIRST span's context for the actual WS write so the outbound
385
+ // envelope's `traceparent` references something deterministic. The other
386
+ // per-call spans still record latency individually; their context isn't
387
+ // attached to the wire frame (a single envelope can carry only one
388
+ // traceparent). Cross-process trace continuity per call is preserved by
389
+ // the matched `qar.tool_call_id` attribute on both sides.
390
+ spans[0].withContext(() => this.sendResponse(nodeName, batch, opts?.sessionId));
391
+ for (const s of spans)
392
+ s.succeed();
393
+ }
394
+ catch (err) {
395
+ for (const s of spans)
396
+ s.fail(err);
397
+ throw err;
398
+ }
399
+ }
400
+ attachDispatcher(explicitConnection) {
401
+ const connection = explicitConnection ?? this.resolveConnection();
402
+ if (this.dispatcher !== null && this.dispatcherConnection === connection)
403
+ return;
404
+ // Connection swap (rare — disconnect + reconnect between onRequest calls):
405
+ // detach the stale dispatcher from the old connection before re-attaching.
406
+ if (this.dispatcher !== null && this.dispatcherConnection !== null) {
407
+ try {
408
+ this.dispatcherConnection.unsubscribe(this.dispatcher);
409
+ }
410
+ catch {
411
+ // The old connection may already be torn down — best-effort cleanup.
412
+ }
413
+ }
414
+ this.dispatcher = new ToolRequestDispatcher(this.handlers, this.fallbackHandlers, (nodeName, responses) => {
415
+ // Two failure modes for the send: connection closed mid-flight (no
416
+ // useful retry), or `JSON.stringify` blew up on a payload that slipped
417
+ // past the validator (e.g., a Date / Map masquerading as a JsonObject —
418
+ // the recursive validator catches cycles + non-JSON primitives but
419
+ // can't catch every prototype-shaped impostor). For the second case
420
+ // we attempt a deterministic per-call `status: 'error'` envelope so
421
+ // QAR's agent loop unblocks instead of timing out.
422
+ try {
423
+ this.sendResponse(nodeName, responses);
424
+ return;
425
+ }
426
+ catch (err) {
427
+ if (!this.isConnectionLive())
428
+ return;
429
+ if (this.isInternalSerializationError(err, responses)) {
430
+ try {
431
+ const fallback = responses.map((r) => ({
432
+ tool_call_id: r.tool_call_id,
433
+ result: {
434
+ outcome: 'error',
435
+ error: 'internal: failed to serialize tool response',
436
+ },
437
+ }));
438
+ this.sendResponse(nodeName, fallback);
439
+ }
440
+ catch {
441
+ // Fallback also failed — best-effort; QAR's per-task
442
+ // tool-response timeout is the safety net.
443
+ }
444
+ }
445
+ }
446
+ }, () => {
447
+ // Connection closed (clean or via transport error). Drop our refs so
448
+ // a later `onRequest` (after a fresh `connect()`) re-attaches against
449
+ // the new connection instead of pointing at a dead one.
450
+ this.dispatcher = null;
451
+ this.dispatcherConnection = null;
452
+ },
453
+ // Per-call session lookup via the connection's `toolCallSessions`
454
+ // map — populated when the inbound `tool.request` landed, so it's
455
+ // guaranteed to be present here (the dispatcher only builds spans
456
+ // for envelopes it just absorbed).
457
+ (call, nodeName) => this.spanRecorder.startToolHandlerSpan({
458
+ sessionId: connection.getSessionForToolCall(call.tool_call_id),
459
+ toolCallId: call.tool_call_id,
460
+ toolName: call.name,
461
+ nodeName,
462
+ }), (err) => this.emitWireValidationError(err));
463
+ connection.subscribe(this.dispatcher);
464
+ this.dispatcherConnection = connection;
465
+ }
466
+ sendResponse(nodeName, responses, sessionOverride) {
467
+ const connection = this.resolveConnection();
468
+ const payload = {
469
+ node_name: nodeName,
470
+ responses: responses.map((r) => toResponseItem(r.tool_call_id, r.result)),
471
+ };
472
+ connection.sendEnvelope({ kind: 'tool.response', payload }, sessionOverride !== undefined ? { sessionId: sessionOverride } : undefined);
473
+ }
474
+ /**
475
+ * Snapshot whether the underlying connection is still open. Used by the
476
+ * dispatcher's send-fallback to distinguish "connection died" (give up) from
477
+ * "the payload itself was poisoned" (try a safe `status: 'error'`).
478
+ */
479
+ isConnectionLive() {
480
+ try {
481
+ return this.resolveConnection().isOpen;
482
+ }
483
+ catch {
484
+ return false;
485
+ }
486
+ }
487
+ /**
488
+ * Heuristic: a send failure is an internal serialization error (rather than
489
+ * a connection-level one) when the connection is live AND the batch we just
490
+ * tried to send wasn't already the safe internal-error fallback. Filtering
491
+ * on the fallback's own message stops a serialization-failed loop from
492
+ * looping.
493
+ */
494
+ isInternalSerializationError(_err, lastBatch) {
495
+ const alreadyFallback = lastBatch.every((r) => r.result.outcome === 'error' && r.result.error.startsWith('internal: '));
496
+ return !alreadyFallback;
497
+ }
498
+ }
499
+ /**
500
+ * The fan-out subscription: walks the handler stack for every `tool.request`,
501
+ * routes the first non-`undefined` handler-return (single result or array of
502
+ * results) to the connection.
503
+ *
504
+ * Handlers receive the full `ToolRequestEnvelope` (so they can read
505
+ * `node_name` and the entire `calls` list). The dispatcher itself doesn't
506
+ * claim the envelope (`consider` returns `false`) so task subscriptions still
507
+ * see `tool.request` on their `for await` stream if the consumer wants to
508
+ * observe both surfaces.
509
+ */
510
+ class ToolRequestDispatcher {
511
+ handlers;
512
+ fallbackHandlers;
513
+ onResult;
514
+ onConnectionClosed;
515
+ buildHandlerSpan;
516
+ onWireValidationError;
517
+ constructor(handlers,
518
+ /**
519
+ * SDK-side fallback handler stack (auto-bridges). Walked AFTER the
520
+ * primary {@link handlers} stack so consumer-registered `onRequest`
521
+ * handlers get first claim; the dispatcher's
522
+ * "zero-primary-handlers → deterministic deny" check is keyed off
523
+ * `handlers.length` only (independent of fallback presence) so
524
+ * consumer-side deny semantics aren't shadowed by SDK auto-bridges.
525
+ */
526
+ fallbackHandlers, onResult, onConnectionClosed,
527
+ /**
528
+ * Span builder for the per-call inbound `tool.request` → handler invocation.
529
+ * Each dispatch opens one span per `ToolCall`; the dispatcher closes each
530
+ * with the matching per-call outcome before batching the responses.
531
+ */
532
+ buildHandlerSpan,
533
+ /**
534
+ * Fired when an inbound `tool.request` is dropped because it violates
535
+ * the wire shape (empty `node_name` / empty `calls`). Lets the parent
536
+ * `ToolClient` route the typed error to its registered
537
+ * `onWireValidationError` observers AFTER the `[@qodo/sdk]` breadcrumb
538
+ * lands on `console.error`.
539
+ */
540
+ onWireValidationError) {
541
+ this.handlers = handlers;
542
+ this.fallbackHandlers = fallbackHandlers;
543
+ this.onResult = onResult;
544
+ this.onConnectionClosed = onConnectionClosed;
545
+ this.buildHandlerSpan = buildHandlerSpan;
546
+ this.onWireValidationError = onWireValidationError;
547
+ }
548
+ consider(env) {
549
+ if (env.kind !== 'tool.request')
550
+ return false;
551
+ // Fire-and-forget guard against unhandled promise rejections.
552
+ // `dispatch()` already wraps its body in a top-level try/catch and
553
+ // best-efforts a `denied` envelope per call via
554
+ // `emitContainedFailure`. The .catch() here only fires if THAT path
555
+ // also throws — e.g. a sync exception escapes before the inner try runs
556
+ // (buggy interceptor wrapping the async function), or
557
+ // `emitContainedFailure` itself throws past its own swallowing layer.
558
+ // Either way: log a single breadcrumb so the failure is observable in
559
+ // operator logs, then swallow. NEVER let this become a process-level
560
+ // `unhandledRejection`.
561
+ void this.dispatch(env).catch((err) => {
562
+ logUnhandledDispatchFailure(err, env.message_id);
563
+ });
564
+ // Don't claim — task subscriptions also surface tool.request on the
565
+ // iterator for consumers that want to observe the round-trip.
566
+ return false;
567
+ }
568
+ considerClient(_ev) {
569
+ // Tool dispatcher is wire-only — it doesn't yield client lifecycle
570
+ // events to anyone. The `qar.client.*` events surface on `TaskEvent`
571
+ // iterators and on `client.receive()` instead. Parameter kept (unused)
572
+ // to satisfy the `InboundSubscription` interface signature exactly.
573
+ }
574
+ fail(_err) {
575
+ // The connection layer reports the failure to every subscription. We
576
+ // hand it back to ToolClient via the close hook so future onRequest()
577
+ // calls re-attach against a fresh connection rather than the dead one.
578
+ this.onConnectionClosed();
579
+ }
580
+ close() {
581
+ this.onConnectionClosed();
582
+ }
583
+ async dispatch(env) {
584
+ // Top-level guard — anything from `buildHandlerSpan` throwing (buggy OTel
585
+ // tracer impl) to a handler throwing synchronously past the inner mapper
586
+ // lands here. The fire-and-forget caller in `consider()` would otherwise
587
+ // surface this as an unhandled promise rejection.
588
+ // Containment policy: best-effort emit a `denied`/`error` envelope per
589
+ // call so QAR's agent loop unblocks; if even that fails, swallow — the
590
+ // runtime's per-task tool-response timeout is the safety net.
591
+ try {
592
+ await this.dispatchInner(env);
593
+ }
594
+ catch (err) {
595
+ this.emitContainedFailure(env, err);
596
+ }
597
+ }
598
+ async dispatchInner(env) {
599
+ const { calls, node_name: nodeName } = env.payload;
600
+ // Inbound wire validation. An inbound envelope with empty
601
+ // `node_name` or empty `calls` can't be routed (no `tool_call_id` to
602
+ // respond against, no node to address). Throwing here would fail the
603
+ // connection across every subscription — far worse for a single
604
+ // malformed envelope. Log + drop, and let the runtime time out the call
605
+ // set on its end.
606
+ // Task subscriptions still observe the malformed envelope on their
607
+ // iterator, so consumers can also branch on `event.kind === 'tool.request'`
608
+ // and inspect `payload` if they need their own surface.
609
+ if (typeof nodeName !== 'string' || nodeName.length === 0) {
610
+ const violation = 'node_name is required and must be a non-empty string';
611
+ logWireValidationDrop({ violation, messageId: env.message_id });
612
+ this.fireWireValidationError(violation, env.message_id, undefined);
613
+ return;
614
+ }
615
+ if (!Array.isArray(calls) || calls.length === 0) {
616
+ const violation = 'calls must be a non-empty list';
617
+ logWireValidationDrop({ violation, messageId: env.message_id, nodeName });
618
+ this.fireWireValidationError(violation, env.message_id, nodeName);
619
+ return;
620
+ }
621
+ // Per-call spans (one per `ToolCall`). All share the same OTel parent —
622
+ // whatever context was active when the inbound envelope arrived. Spans
623
+ // close per-call with the matching outcome.
624
+ const spans = calls.map((c) => this.buildHandlerSpan(c, nodeName));
625
+ // `primaries.length === 0` is the consumer-side signal "no handler
626
+ // registered" (the dispatcher tracks consumer handlers separately
627
+ // from SDK auto-bridges). When no consumer registered a handler AND
628
+ // no SDK fallback claims, the dispatcher emits the deterministic
629
+ // deny envelope so QAR's agent loop unblocks.
630
+ const noPrimaryHandlers = this.handlers.length === 0;
631
+ if (noPrimaryHandlers && this.fallbackHandlers.length === 0) {
632
+ // Pre-fix behavior preserved verbatim for the "absolutely no
633
+ // handlers anywhere" case.
634
+ for (const s of spans)
635
+ this.finishHandlerSpan(s, 'denied');
636
+ const responses = calls.map((c) => ({
637
+ tool_call_id: c.tool_call_id,
638
+ result: { outcome: 'denied', error: 'no handler registered' },
639
+ }));
640
+ this.onResult(nodeName, responses);
641
+ return;
642
+ }
643
+ // Stage 1 — walk consumer-registered handlers. If any claims (returns
644
+ // non-undefined), we emit + return. Snapshot the stack so a handler
645
+ // unsubscribing itself mid-iterate doesn't break the walk.
646
+ const primaryResult = await this.walkHandlerStack([...this.handlers], env, calls, spans, nodeName);
647
+ if (primaryResult === 'claimed')
648
+ return;
649
+ // Stage 2 — walk SDK fallback handlers (auto-bridges).
650
+ const fallbackResult = await this.walkHandlerStack([...this.fallbackHandlers], env, calls, spans, nodeName);
651
+ if (fallbackResult === 'claimed')
652
+ return;
653
+ // Both stages exhausted with no claim. Two terminal cases:
654
+ // (a) No consumer handlers existed → emit the deterministic deny
655
+ // so QAR's agent loop unblocks. This is the post-fix semantic:
656
+ // the SDK auto-bridges are advisory — they don't suppress the
657
+ // deny that a zero-consumer-handlers config would otherwise
658
+ // get.
659
+ // (b) Consumer handlers existed but all returned undefined → defer
660
+ // to manual `respond()`. The consumer's contract is "I'll
661
+ // respond later"; QAR's per-task tool-response timeout is the
662
+ // wire-side safety net.
663
+ if (noPrimaryHandlers) {
664
+ for (const s of spans)
665
+ this.finishHandlerSpan(s, 'denied');
666
+ const responses = calls.map((c) => ({
667
+ tool_call_id: c.tool_call_id,
668
+ result: { outcome: 'denied', error: 'no handler registered' },
669
+ }));
670
+ this.onResult(nodeName, responses);
671
+ return;
672
+ }
673
+ // Every handler returned undefined — defer to manual `respond()`. The
674
+ // handler spans end here with no outcome attribute; the manual respond
675
+ // path opens its own `qar.client.tool.respond` spans carrying the
676
+ // outcome. QAR enforces a per-tool-response timeout server-side; the
677
+ // SDK doesn't double-time-out here.
678
+ for (const s of spans)
679
+ s.succeed();
680
+ }
681
+ /**
682
+ * Walk an ordered handler stack once, returning `'claimed'` when any
683
+ * handler produces a non-`undefined` result (the dispatcher emits the
684
+ * response + returns to the caller), or `'all-undefined'` when every
685
+ * handler in the stack returned `undefined` (caller decides the next
686
+ * step — try fallbacks, deny, or defer).
687
+ *
688
+ * Centralizes the per-handler invocation + per-call response
689
+ * normalization + span outcome attribution so primary + fallback
690
+ * stacks share identical semantics.
691
+ */
692
+ async walkHandlerStack(snapshot, env, calls, spans, nodeName) {
693
+ for (const handler of snapshot) {
694
+ let raw;
695
+ try {
696
+ // Run the handler inside the FIRST span's context so any
697
+ // child spans the handler opens (e.g. wrapping its own work in
698
+ // `tracer.startActiveSpan`) parent under our
699
+ // `qar.client.tool.handler` — without this, the handler's spans
700
+ // would leaf back to whatever was active when the inbound
701
+ // `tool.request` arrived. For multi-call batches, the parent is
702
+ // calls[0]'s span; per-call attribution still lands via
703
+ // `qar.tool_call_id` on each sibling span.
704
+ raw = await spans[0].withContext(() => handler(env));
705
+ }
706
+ catch (err) {
707
+ // A handler-thrown exception applies to the whole batch — there's no
708
+ // per-call attribution available from the throw. Map it once and
709
+ // replicate across every call. `denied` (ToolDeniedError) is intent,
710
+ // not failure → succeed-with-attr; anything else is an error path.
711
+ const mapped = mapHandlerError(err);
712
+ const outcome = mapped.outcome;
713
+ for (const s of spans) {
714
+ if (outcome === 'denied') {
715
+ this.finishHandlerSpan(s, 'denied');
716
+ }
717
+ else {
718
+ s.setAttribute(QAR_TOOL_OUTCOME, outcome);
719
+ s.fail(err);
720
+ }
721
+ }
722
+ const responses = calls.map((c) => ({
723
+ tool_call_id: c.tool_call_id,
724
+ result: mapped,
725
+ }));
726
+ this.onResult(nodeName, responses);
727
+ return 'claimed';
728
+ }
729
+ if (raw === undefined)
730
+ continue;
731
+ const normalized = this.normalizeHandlerReturn(raw, calls);
732
+ if (normalized === null) {
733
+ // Length mismatch / malformed — fail the whole batch, surface generic.
734
+ for (const s of spans) {
735
+ s.setAttribute(QAR_TOOL_OUTCOME, 'error');
736
+ s.fail(new Error('handler returned malformed ToolResponseResult batch'));
737
+ }
738
+ const fallback = calls.map((c) => ({
739
+ tool_call_id: c.tool_call_id,
740
+ result: {
741
+ outcome: 'error',
742
+ error: 'handler returned malformed ToolResponseResult batch',
743
+ },
744
+ }));
745
+ this.onResult(nodeName, fallback);
746
+ return 'claimed';
747
+ }
748
+ const validated = [];
749
+ let anyMalformed = false;
750
+ normalized.forEach((result, idx) => {
751
+ if (!isValidToolResponseResult(result)) {
752
+ anyMalformed = true;
753
+ spans[idx].setAttribute(QAR_TOOL_OUTCOME, 'error');
754
+ spans[idx].fail(new Error('handler returned malformed ToolResponseResult'));
755
+ validated.push({
756
+ tool_call_id: calls[idx].tool_call_id,
757
+ result: {
758
+ outcome: 'error',
759
+ error: 'handler returned malformed ToolResponseResult',
760
+ },
761
+ });
762
+ return;
763
+ }
764
+ this.finishHandlerSpan(spans[idx], toolOutcomeFor(result.outcome));
765
+ validated.push({ tool_call_id: calls[idx].tool_call_id, result });
766
+ });
767
+ void anyMalformed;
768
+ this.onResult(nodeName, validated);
769
+ return 'claimed';
770
+ }
771
+ return 'all-undefined';
772
+ }
773
+ /**
774
+ * Coerce the handler's return into a positional `ToolResponseResult[]`
775
+ * matching `calls`. Returns `null` on length mismatch / non-array, non-result
776
+ * shape — the caller emits a generic "malformed" batch.
777
+ */
778
+ normalizeHandlerReturn(raw, calls) {
779
+ if (Array.isArray(raw)) {
780
+ const arr = raw;
781
+ if (arr.length !== calls.length)
782
+ return null;
783
+ return arr;
784
+ }
785
+ // Single result — only valid when the request has exactly one call.
786
+ if (calls.length !== 1)
787
+ return null;
788
+ return [raw];
789
+ }
790
+ /**
791
+ * Close a handler span with the documented outcome attribute. `error`
792
+ * outcomes (handler returned `outcome: "error"`) are *not* span errors —
793
+ * the wire path succeeded; the tool itself reported failure. Span ERROR
794
+ * status is reserved for unhandled exceptions in the dispatch path.
795
+ */
796
+ finishHandlerSpan(span, outcome) {
797
+ span.setAttribute(QAR_TOOL_OUTCOME, outcome);
798
+ span.succeed();
799
+ }
800
+ /**
801
+ * Best-effort containment for an unexpected failure in `dispatchInner` —
802
+ * triggered when something outside the handler-error mapping path throws
803
+ * (most realistically: an OTel tracer impl throwing from `buildHandlerSpan`
804
+ * before the per-call try/catch can run). Emits a `denied` envelope per
805
+ * call so QAR's agent loop unblocks. Any further failure (e.g., the
806
+ * tracer is wedged AND the send fails too) is swallowed — the runtime's
807
+ * per-task tool-response timeout is the final safety net.
808
+ */
809
+ emitContainedFailure(env, _err) {
810
+ try {
811
+ const nodeName = env.payload?.node_name;
812
+ const calls = env.payload?.calls;
813
+ if (typeof nodeName !== 'string' ||
814
+ nodeName.length === 0 ||
815
+ !Array.isArray(calls) ||
816
+ calls.length === 0) {
817
+ return;
818
+ }
819
+ const responses = calls.map((c) => ({
820
+ tool_call_id: c.tool_call_id,
821
+ result: { outcome: 'denied', error: 'internal: dispatch failed' },
822
+ }));
823
+ this.onResult(nodeName, responses);
824
+ }
825
+ catch {
826
+ // Containment of the containment — swallow.
827
+ }
828
+ }
829
+ /**
830
+ * Build a `QodoWireValidationError` carrying structured drop metadata and
831
+ * hand it to the parent `ToolClient` for fan-out to registered
832
+ * `onWireValidationError` observers. Defensive: any failure inside the
833
+ * handler-fan-out path is swallowed by the parent, but a synchronous
834
+ * exception during error CONSTRUCTION (extremely unlikely) is also
835
+ * caught here so it can't escape the dispatch hot path.
836
+ */
837
+ fireWireValidationError(violation, messageId, nodeName) {
838
+ try {
839
+ const err = new QodoWireValidationError(`inbound tool.request envelope dropped: ${violation}`, { messageId, nodeName, scope: 'inbound' });
840
+ this.onWireValidationError(err);
841
+ }
842
+ catch {
843
+ // Even constructing the error / dispatching the observers wedged.
844
+ // The breadcrumb log already landed; that's the contract.
845
+ }
846
+ }
847
+ }
848
+ /**
849
+ * Map an exception thrown by a `ToolRequestHandler` to a `ToolResponseResult`.
850
+ * `ToolDeniedError` is the typed escape path (operator approval flow rejected,
851
+ * capability lookup denied) → `outcome: 'denied'`. Everything else maps to
852
+ * `outcome: 'error'` so the agent loop sees a generic failure rather than a
853
+ * silent denial. Truncation lives at the wire boundary (`toResponseItem`) so
854
+ * the cap applies uniformly to handler-thrown, handler-returned, and
855
+ * manual-respond paths.
856
+ */
857
+ function mapHandlerError(err) {
858
+ if (err instanceof ToolDeniedError) {
859
+ return { outcome: 'denied', error: err.message };
860
+ }
861
+ const message = err instanceof Error ? err.message : String(err);
862
+ return { outcome: 'error', error: message };
863
+ }
864
+ function truncate(s) {
865
+ if (s.length <= MAX_ERROR_LEN)
866
+ return s;
867
+ return `${s.slice(0, MAX_ERROR_LEN - 1)}…`;
868
+ }
869
+ /**
870
+ * Validate a `node_name` argument supplied at the outbound boundary. The wire
871
+ * requires a non-empty string; empty / non-string values produce a typed
872
+ * `QodoWireValidationError` so consumer bugs surface synchronously instead
873
+ * of as a malformed envelope.
874
+ */
875
+ function validateNodeName(value, where) {
876
+ if (typeof value !== 'string' || value.length === 0) {
877
+ throw new QodoWireValidationError(`${where}: node_name is required and must be a non-empty string.`);
878
+ }
879
+ }
880
+ /** Cap on individual field values rendered into the wire-validation breadcrumb. */
881
+ const LOG_FIELD_MAX_LEN = 200;
882
+ /**
883
+ * Emit a single diagnostic line when the dispatcher drops a malformed inbound
884
+ * `tool.request` envelope (empty `node_name` or empty `calls`). Without a
885
+ * signal, consumers have nothing to grep for when QAR's tool-loop times
886
+ * out and they need to diagnose protocol drift.
887
+ *
888
+ * Wire-provided fields (`nodeName`, `messageId`) are sanitized via
889
+ * `safeForLog` before interpolation — control characters in a malformed
890
+ * envelope would otherwise corrupt the log format and let an upstream
891
+ * bug (or hostile peer) forge fake breadcrumb lines. Each field is also
892
+ * clamped to `LOG_FIELD_MAX_LEN` so an attacker can't blow up the log
893
+ * line size.
894
+ *
895
+ * Logged at `console.error` because `console.warn` isn't part of the SDK's
896
+ * existing in-repo logging vocabulary (only `console.error` is used; see
897
+ * `src/client/observers.ts`). Wrapped defensively — a hostile or wedged
898
+ * `console` must never crash the dispatch hot path.
899
+ */
900
+ function logWireValidationDrop(fields) {
901
+ try {
902
+ // `violation` is SDK-authored static prose, never wire-derived — no need
903
+ // to sanitize. `messageId` and `nodeName` come from the inbound envelope
904
+ // and MUST be sanitized.
905
+ const parts = [
906
+ `[@qodo/sdk] tool.request envelope dropped: ${fields.violation}`,
907
+ `message_id=${safeForLog(fields.messageId)}`,
908
+ ];
909
+ if (fields.nodeName !== undefined) {
910
+ parts.push(`node_name=${safeForLog(fields.nodeName)}`);
911
+ }
912
+ // eslint-disable-next-line no-console
913
+ console.error(parts.join(' '));
914
+ }
915
+ catch {
916
+ // Console itself is wedged — give up; the wire path is the contract.
917
+ }
918
+ }
919
+ /**
920
+ * Logged when an observer registered via `onWireValidationError(handler)`
921
+ * itself throws. Mirrors the swallow-and-surface pattern in
922
+ * `KindObserverPort.logHandlerError` (`src/client/observers.ts`) so the same
923
+ * "[@qodo/sdk]" breadcrumb vocabulary covers every consumer-handler failure.
924
+ */
925
+ // `isThenable` lives in `./internal/thenable.ts` so this module and
926
+ // `observers.ts` share one canonical implementation — a local copy here
927
+ // missing the `typeof === 'function'` arm would let callable thenables
928
+ // escape the async-containment path.
929
+ function logHandlerObserverError(surface, err) {
930
+ try {
931
+ const msg = err instanceof Error ? err.message : String(err);
932
+ // eslint-disable-next-line no-console
933
+ console.error(`[@qodo/sdk] ${surface} handler threw — swallowed: ${safeForLog(msg)}`);
934
+ }
935
+ catch {
936
+ try {
937
+ // eslint-disable-next-line no-console
938
+ console.error(`[@qodo/sdk] ${surface} handler threw — swallowed (logger error)`);
939
+ }
940
+ catch {
941
+ // Console fully wedged — drop.
942
+ }
943
+ }
944
+ }
945
+ function logUnhandledDispatchFailure(err, messageId) {
946
+ try {
947
+ const message = err instanceof Error ? err.message : String(err);
948
+ // eslint-disable-next-line no-console
949
+ console.error(`[@qodo/sdk] tool.request dispatch failed past containment: ${safeForLog(message)} ` +
950
+ `message_id=${safeForLog(messageId)}`);
951
+ }
952
+ catch {
953
+ // Even the breadcrumb is wedged. Drop silently — the priority is keeping
954
+ // the dispatch hot path alive.
955
+ }
956
+ }
957
+ /**
958
+ * Render a wire-provided value into a single log-safe token. Replaces
959
+ * control characters (newlines, tabs, carriage returns, NULs, every C0/C1
960
+ * range) with their `\xNN` / `\uNNNN` escapes, wraps the result in quotes,
961
+ * and clamps to `LOG_FIELD_MAX_LEN` so a single malformed field can't
962
+ * dominate the log line. Non-string values fall back to `JSON.stringify`
963
+ * (defensive — TS callers always pass strings, but inbound envelopes are
964
+ * runtime-untrusted).
965
+ */
966
+ function safeForLog(value) {
967
+ let s;
968
+ if (typeof value === 'string') {
969
+ s = value;
970
+ }
971
+ else {
972
+ try {
973
+ s = JSON.stringify(value);
974
+ }
975
+ catch {
976
+ s = String(value);
977
+ }
978
+ }
979
+ // Escape:
980
+ // - ASCII C0 control chars (0x00-0x1F, 0x7F) and the C1 range (0x80-0x9F)
981
+ // — any of these can break grep, terminal output, or log aggregators.
982
+ // - U+2028 / U+2029 — Unicode line/paragraph separators; some log parsers
983
+ // (and some V8 versions' `JSON.stringify`) treat them as newlines.
984
+ // - U+202A–U+202E + U+2066–U+2069 — Unicode bidi overrides (LRE/RLE/PDF/
985
+ // LRO/RLO + FSI/PDI family). Without escaping these, a hostile node_name
986
+ // can right-to-left-flip part of the log line so tampering visually
987
+ // looks like a legitimate field.
988
+ // - U+2060 (Word Joiner) + U+FEFF (BOM / ZWNBSP) — invisible format chars
989
+ // that defeat grep-based debugging by inserting zero-width separators.
990
+ // Ordinary printable Unicode is left intact so non-ASCII node names still
991
+ // render readably.
992
+ s = s.replace(/[\x00-\x1f\x7f-\x9f\u2028\u2029\u202a-\u202e\u2066-\u2069\u2060\ufeff]/g, (c) => {
993
+ const code = c.charCodeAt(0);
994
+ return code <= 0xff
995
+ ? `\\x${code.toString(16).padStart(2, '0')}`
996
+ : `\\u${code.toString(16).padStart(4, '0')}`;
997
+ });
998
+ if (s.length > LOG_FIELD_MAX_LEN) {
999
+ s = `${s.slice(0, LOG_FIELD_MAX_LEN - 1)}…`;
1000
+ }
1001
+ return JSON.stringify(s);
1002
+ }
1003
+ /**
1004
+ * Runtime guard for the SDK-side `ToolResponseResult` discriminated union.
1005
+ *
1006
+ * The wire shape is the flat `ToolResponseItem` (`status` + optional `result`
1007
+ * / `error`); the SDK convenience union narrows that. Validating here means a
1008
+ * buggy handler can't ship a malformed envelope onto the wire.
1009
+ */
1010
+ function isValidToolResponseResult(x) {
1011
+ if (typeof x !== 'object' || x === null)
1012
+ return false;
1013
+ const r = x;
1014
+ switch (r.outcome) {
1015
+ case 'success':
1016
+ // Recursive JSON-shape check (catches cycles + non-JSON primitives like
1017
+ // BigInt, Symbol, function, NaN/Infinity, undefined). Without this the
1018
+ // wire-side `JSON.stringify` would either throw (cycle) or silently
1019
+ // drop fields (non-JSON primitives).
1020
+ return isJsonObject(r.result);
1021
+ case 'error':
1022
+ return typeof r.error === 'string';
1023
+ case 'denied':
1024
+ return r.error === undefined || typeof r.error === 'string';
1025
+ default:
1026
+ return false;
1027
+ }
1028
+ }
1029
+ /**
1030
+ * Recursive JSON-object validator with cycle detection. Mirrors the
1031
+ * `JsonObject` / `JsonValue` shapes in `src/qar/json.ts`.
1032
+ *
1033
+ * - rejects arrays (a top-level `result` must be a JSON object, not array);
1034
+ * - rejects `BigInt`, `Symbol`, `function`, `undefined` values;
1035
+ * - rejects non-finite numbers (`NaN`, `Infinity`) — these become `null`
1036
+ * under `JSON.stringify` which silently corrupts the result on the wire;
1037
+ * - tracks visited objects in a `WeakSet` so a cyclic payload returns
1038
+ * `false` instead of triggering the recursion bomb at serialize time.
1039
+ */
1040
+ function isJsonObject(x, seen = new WeakSet()) {
1041
+ if (typeof x !== 'object' || x === null || Array.isArray(x))
1042
+ return false;
1043
+ if (seen.has(x))
1044
+ return false;
1045
+ seen.add(x);
1046
+ for (const value of Object.values(x)) {
1047
+ if (!isJsonValue(value, seen))
1048
+ return false;
1049
+ }
1050
+ return true;
1051
+ }
1052
+ function isJsonValue(x, seen) {
1053
+ if (x === null)
1054
+ return true;
1055
+ switch (typeof x) {
1056
+ case 'string':
1057
+ case 'boolean':
1058
+ return true;
1059
+ case 'number':
1060
+ return Number.isFinite(x);
1061
+ case 'object': {
1062
+ if (Array.isArray(x)) {
1063
+ if (seen.has(x))
1064
+ return false;
1065
+ seen.add(x);
1066
+ return x.every((item) => isJsonValue(item, seen));
1067
+ }
1068
+ return isJsonObject(x, seen);
1069
+ }
1070
+ default:
1071
+ // 'bigint', 'symbol', 'function', 'undefined' → not JSON-representable.
1072
+ return false;
1073
+ }
1074
+ }
1075
+ /**
1076
+ * Translate one SDK-side `ToolResponseResult` into the flat wire item.
1077
+ *
1078
+ * Wire-faithful mapping:
1079
+ *
1080
+ * - `success` → `status: 'ok'`, `result` carried as-is.
1081
+ * - `error` → `status: 'error'`, `error: { code: 'error', message }`.
1082
+ * - `denied` → `status: 'error'`, `error: { code: 'denied', message? }`.
1083
+ *
1084
+ * `denied` collapses to wire `status: 'error'` with a distinct `error.code` so
1085
+ * QAR's tool-loop telemetry can still distinguish policy rejection from
1086
+ * generic failure. Truncation happens HERE (not in callers) so every outbound
1087
+ * error/denied envelope is bounded — handler-thrown, handler-returned, and
1088
+ * `respond()` paths all go through this single seam.
1089
+ */
1090
+ function toResponseItem(toolCallId, result) {
1091
+ switch (result.outcome) {
1092
+ case 'success':
1093
+ return {
1094
+ tool_call_id: toolCallId,
1095
+ status: 'ok',
1096
+ result: result.result,
1097
+ };
1098
+ case 'error':
1099
+ return {
1100
+ tool_call_id: toolCallId,
1101
+ status: 'error',
1102
+ error: { code: 'error', message: truncate(result.error) },
1103
+ };
1104
+ case 'denied':
1105
+ return {
1106
+ tool_call_id: toolCallId,
1107
+ status: 'error',
1108
+ error: {
1109
+ code: 'denied',
1110
+ message: result.error !== undefined ? truncate(result.error) : 'denied',
1111
+ },
1112
+ };
1113
+ }
1114
+ }
1115
+ //# sourceMappingURL=ToolClient.js.map