@namzu/sdk 32.0.0 → 33.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 (305) hide show
  1. package/CHANGELOG.md +157 -0
  2. package/dist/authorization/gate.d.ts +1 -1
  3. package/dist/authorization/gate.js +1 -1
  4. package/dist/bridge/a2a/mapper.d.ts.map +1 -1
  5. package/dist/bridge/a2a/mapper.js +13 -0
  6. package/dist/bridge/a2a/mapper.js.map +1 -1
  7. package/dist/bridge/sse/mapper.d.ts.map +1 -1
  8. package/dist/bridge/sse/mapper.js +3 -0
  9. package/dist/bridge/sse/mapper.js.map +1 -1
  10. package/dist/connector/index.d.ts +1 -1
  11. package/dist/connector/index.d.ts.map +1 -1
  12. package/dist/connector/index.js +1 -1
  13. package/dist/connector/index.js.map +1 -1
  14. package/dist/connector/mcp/adapter.d.ts.map +1 -1
  15. package/dist/connector/mcp/adapter.js +71 -16
  16. package/dist/connector/mcp/adapter.js.map +1 -1
  17. package/dist/connector/mcp/client.d.ts.map +1 -1
  18. package/dist/connector/mcp/client.js +3 -2
  19. package/dist/connector/mcp/client.js.map +1 -1
  20. package/dist/connector/mcp/http-redirect.d.ts +17 -0
  21. package/dist/connector/mcp/http-redirect.d.ts.map +1 -0
  22. package/dist/connector/mcp/http-redirect.js +31 -0
  23. package/dist/connector/mcp/http-redirect.js.map +1 -0
  24. package/dist/connector/mcp/http-sse.d.ts.map +1 -1
  25. package/dist/connector/mcp/http-sse.js +5 -0
  26. package/dist/connector/mcp/http-sse.js.map +1 -1
  27. package/dist/connector/mcp/image-admission.d.ts +16 -0
  28. package/dist/connector/mcp/image-admission.d.ts.map +1 -0
  29. package/dist/connector/mcp/image-admission.js +288 -0
  30. package/dist/connector/mcp/image-admission.js.map +1 -0
  31. package/dist/connector/mcp/stdio.d.ts +13 -0
  32. package/dist/connector/mcp/stdio.d.ts.map +1 -1
  33. package/dist/connector/mcp/stdio.js +66 -23
  34. package/dist/connector/mcp/stdio.js.map +1 -1
  35. package/dist/connector/mcp/streamable-http.d.ts.map +1 -1
  36. package/dist/connector/mcp/streamable-http.js +3 -0
  37. package/dist/connector/mcp/streamable-http.js.map +1 -1
  38. package/dist/constants/tools/index.js +1 -1
  39. package/dist/constants/tools/index.js.map +1 -1
  40. package/dist/execution/base.d.ts +13 -0
  41. package/dist/execution/base.d.ts.map +1 -1
  42. package/dist/execution/base.js +103 -8
  43. package/dist/execution/base.js.map +1 -1
  44. package/dist/execution/errors.d.ts +12 -0
  45. package/dist/execution/errors.d.ts.map +1 -0
  46. package/dist/execution/errors.js +21 -0
  47. package/dist/execution/errors.js.map +1 -0
  48. package/dist/execution/factory.d.ts.map +1 -1
  49. package/dist/execution/factory.js +1 -0
  50. package/dist/execution/factory.js.map +1 -1
  51. package/dist/execution/hybrid.d.ts +5 -0
  52. package/dist/execution/hybrid.d.ts.map +1 -1
  53. package/dist/execution/hybrid.js +36 -16
  54. package/dist/execution/hybrid.js.map +1 -1
  55. package/dist/execution/index.d.ts +1 -0
  56. package/dist/execution/index.d.ts.map +1 -1
  57. package/dist/execution/index.js +1 -0
  58. package/dist/execution/index.js.map +1 -1
  59. package/dist/execution/local.d.ts +13 -0
  60. package/dist/execution/local.d.ts.map +1 -1
  61. package/dist/execution/local.js +145 -19
  62. package/dist/execution/local.js.map +1 -1
  63. package/dist/execution/output.d.ts +27 -0
  64. package/dist/execution/output.d.ts.map +1 -0
  65. package/dist/execution/output.js +105 -0
  66. package/dist/execution/output.js.map +1 -0
  67. package/dist/execution/remote.d.ts +26 -2
  68. package/dist/execution/remote.d.ts.map +1 -1
  69. package/dist/execution/remote.js +86 -15
  70. package/dist/execution/remote.js.map +1 -1
  71. package/dist/plugin/lifecycle.d.ts.map +1 -1
  72. package/dist/plugin/lifecycle.js +9 -3
  73. package/dist/plugin/lifecycle.js.map +1 -1
  74. package/dist/process/kill-tree.d.ts +11 -13
  75. package/dist/process/kill-tree.d.ts.map +1 -1
  76. package/dist/process/kill-tree.js +11 -13
  77. package/dist/process/kill-tree.js.map +1 -1
  78. package/dist/provider/capabilities.d.ts +2 -0
  79. package/dist/provider/capabilities.d.ts.map +1 -1
  80. package/dist/provider/capabilities.js +6 -0
  81. package/dist/provider/capabilities.js.map +1 -1
  82. package/dist/provider/fallback.d.ts.map +1 -1
  83. package/dist/provider/fallback.js +31 -0
  84. package/dist/provider/fallback.js.map +1 -1
  85. package/dist/provider/idle-timeout.d.ts.map +1 -1
  86. package/dist/provider/idle-timeout.js +5 -0
  87. package/dist/provider/idle-timeout.js.map +1 -1
  88. package/dist/provider/retry.d.ts.map +1 -1
  89. package/dist/provider/retry.js +5 -0
  90. package/dist/provider/retry.js.map +1 -1
  91. package/dist/public-runtime.d.ts +2 -2
  92. package/dist/public-runtime.d.ts.map +1 -1
  93. package/dist/public-runtime.js +2 -2
  94. package/dist/public-runtime.js.map +1 -1
  95. package/dist/registry/tool/execute.d.ts +7 -1
  96. package/dist/registry/tool/execute.d.ts.map +1 -1
  97. package/dist/registry/tool/execute.js +201 -54
  98. package/dist/registry/tool/execute.js.map +1 -1
  99. package/dist/run/command-gate.d.ts.map +1 -1
  100. package/dist/run/command-gate.js +11 -0
  101. package/dist/run/command-gate.js.map +1 -1
  102. package/dist/run/workspace-fingerprint.d.ts +4 -4
  103. package/dist/run/workspace-fingerprint.d.ts.map +1 -1
  104. package/dist/run/workspace-fingerprint.js +6 -4
  105. package/dist/run/workspace-fingerprint.js.map +1 -1
  106. package/dist/runtime/bidi/index.d.ts +1 -1
  107. package/dist/runtime/bidi/index.d.ts.map +1 -1
  108. package/dist/runtime/bidi/index.js +1 -1
  109. package/dist/runtime/bidi/index.js.map +1 -1
  110. package/dist/runtime/bidi/session.d.ts +19 -0
  111. package/dist/runtime/bidi/session.d.ts.map +1 -1
  112. package/dist/runtime/bidi/session.js +150 -26
  113. package/dist/runtime/bidi/session.js.map +1 -1
  114. package/dist/runtime/query/executor.d.ts +43 -1
  115. package/dist/runtime/query/executor.d.ts.map +1 -1
  116. package/dist/runtime/query/executor.js +626 -161
  117. package/dist/runtime/query/executor.js.map +1 -1
  118. package/dist/runtime/query/index.d.ts +3 -1
  119. package/dist/runtime/query/index.d.ts.map +1 -1
  120. package/dist/runtime/query/index.js +48 -4
  121. package/dist/runtime/query/index.js.map +1 -1
  122. package/dist/runtime/query/iteration/index.d.ts +10 -0
  123. package/dist/runtime/query/iteration/index.d.ts.map +1 -1
  124. package/dist/runtime/query/iteration/index.js +67 -0
  125. package/dist/runtime/query/iteration/index.js.map +1 -1
  126. package/dist/runtime/query/iteration/phases/context.d.ts +5 -0
  127. package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
  128. package/dist/runtime/query/iteration/phases/context.js.map +1 -1
  129. package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
  130. package/dist/runtime/query/iteration/phases/tool-review.js +84 -22
  131. package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
  132. package/dist/runtime/query/request-rich-content.d.ts.map +1 -1
  133. package/dist/runtime/query/request-rich-content.js +33 -13
  134. package/dist/runtime/query/request-rich-content.js.map +1 -1
  135. package/dist/runtime/query/result.d.ts.map +1 -1
  136. package/dist/runtime/query/result.js +25 -22
  137. package/dist/runtime/query/result.js.map +1 -1
  138. package/dist/runtime/query/resume-pending.d.ts +5 -1
  139. package/dist/runtime/query/resume-pending.d.ts.map +1 -1
  140. package/dist/runtime/query/resume-pending.js +66 -1
  141. package/dist/runtime/query/resume-pending.js.map +1 -1
  142. package/dist/runtime/query/tool-output-budget.d.ts.map +1 -1
  143. package/dist/runtime/query/tool-output-budget.js +45 -16
  144. package/dist/runtime/query/tool-output-budget.js.map +1 -1
  145. package/dist/runtime/query/tooling.d.ts +6 -0
  146. package/dist/runtime/query/tooling.d.ts.map +1 -1
  147. package/dist/runtime/query/tooling.js +4 -0
  148. package/dist/runtime/query/tooling.js.map +1 -1
  149. package/dist/sandbox/provider/local.d.ts.map +1 -1
  150. package/dist/sandbox/provider/local.js +193 -41
  151. package/dist/sandbox/provider/local.js.map +1 -1
  152. package/dist/scheduler/delegating.d.ts +2 -1
  153. package/dist/scheduler/delegating.d.ts.map +1 -1
  154. package/dist/scheduler/delegating.js +6 -5
  155. package/dist/scheduler/delegating.js.map +1 -1
  156. package/dist/scheduler/local.d.ts +2 -1
  157. package/dist/scheduler/local.d.ts.map +1 -1
  158. package/dist/scheduler/local.js +2 -2
  159. package/dist/scheduler/local.js.map +1 -1
  160. package/dist/session/workspace/driver.d.ts +12 -4
  161. package/dist/session/workspace/driver.d.ts.map +1 -1
  162. package/dist/session/workspace/git-worktree.d.ts +17 -3
  163. package/dist/session/workspace/git-worktree.d.ts.map +1 -1
  164. package/dist/session/workspace/git-worktree.js +153 -34
  165. package/dist/session/workspace/git-worktree.js.map +1 -1
  166. package/dist/skills/registry.d.ts +15 -0
  167. package/dist/skills/registry.d.ts.map +1 -1
  168. package/dist/skills/registry.js +33 -0
  169. package/dist/skills/registry.js.map +1 -1
  170. package/dist/tools/builtins/computer-use.d.ts.map +1 -1
  171. package/dist/tools/builtins/computer-use.js +76 -2
  172. package/dist/tools/builtins/computer-use.js.map +1 -1
  173. package/dist/tools/builtins/run-code.d.ts.map +1 -1
  174. package/dist/tools/builtins/run-code.js +9 -11
  175. package/dist/tools/builtins/run-code.js.map +1 -1
  176. package/dist/tools/builtins/skill.d.ts +2 -1
  177. package/dist/tools/builtins/skill.d.ts.map +1 -1
  178. package/dist/tools/builtins/skill.js +250 -6
  179. package/dist/tools/builtins/skill.js.map +1 -1
  180. package/dist/tools/coordinator/agent.d.ts.map +1 -1
  181. package/dist/tools/coordinator/agent.js +100 -30
  182. package/dist/tools/coordinator/agent.js.map +1 -1
  183. package/dist/types/agent/manager.d.ts +1 -1
  184. package/dist/types/agent/manager.d.ts.map +1 -1
  185. package/dist/types/agent/scheduler.d.ts +10 -1
  186. package/dist/types/agent/scheduler.d.ts.map +1 -1
  187. package/dist/types/authorization/index.d.ts +12 -12
  188. package/dist/types/bidi/index.d.ts +19 -2
  189. package/dist/types/bidi/index.d.ts.map +1 -1
  190. package/dist/types/computer-use/index.d.ts +18 -0
  191. package/dist/types/computer-use/index.d.ts.map +1 -1
  192. package/dist/types/connector/execution-context.d.ts +3 -1
  193. package/dist/types/connector/execution-context.d.ts.map +1 -1
  194. package/dist/types/errors/catalog.d.ts.map +1 -1
  195. package/dist/types/errors/catalog.js +13 -6
  196. package/dist/types/errors/catalog.js.map +1 -1
  197. package/dist/types/errors/index.d.ts.map +1 -1
  198. package/dist/types/errors/index.js +21 -8
  199. package/dist/types/errors/index.js.map +1 -1
  200. package/dist/types/execution/index.d.ts +43 -1
  201. package/dist/types/execution/index.d.ts.map +1 -1
  202. package/dist/types/hitl/index.d.ts +11 -0
  203. package/dist/types/hitl/index.d.ts.map +1 -1
  204. package/dist/types/hitl/index.js.map +1 -1
  205. package/dist/types/message/index.d.ts +1 -1
  206. package/dist/types/message/index.d.ts.map +1 -1
  207. package/dist/types/message/index.js +2 -1
  208. package/dist/types/message/index.js.map +1 -1
  209. package/dist/types/plugin/index.d.ts +14 -1
  210. package/dist/types/plugin/index.d.ts.map +1 -1
  211. package/dist/types/plugin/index.js +1 -0
  212. package/dist/types/plugin/index.js.map +1 -1
  213. package/dist/types/provider/config.d.ts +9 -0
  214. package/dist/types/provider/config.d.ts.map +1 -1
  215. package/dist/types/provider/interface.d.ts +14 -0
  216. package/dist/types/provider/interface.d.ts.map +1 -1
  217. package/dist/types/run/events.d.ts +17 -0
  218. package/dist/types/run/events.d.ts.map +1 -1
  219. package/dist/types/run/events.js.map +1 -1
  220. package/dist/types/sandbox/index.d.ts +15 -10
  221. package/dist/types/sandbox/index.d.ts.map +1 -1
  222. package/dist/types/sandbox/index.js.map +1 -1
  223. package/dist/types/tool/index.d.ts +77 -6
  224. package/dist/types/tool/index.d.ts.map +1 -1
  225. package/dist/types/tool/index.js.map +1 -1
  226. package/dist/types/tool/presentation.d.ts +6 -2
  227. package/dist/types/tool/presentation.d.ts.map +1 -1
  228. package/dist/types/workspace/ref.d.ts +5 -0
  229. package/dist/types/workspace/ref.d.ts.map +1 -1
  230. package/dist/utils/abort.d.ts +8 -0
  231. package/dist/utils/abort.d.ts.map +1 -1
  232. package/dist/utils/abort.js +16 -0
  233. package/dist/utils/abort.js.map +1 -1
  234. package/package.json +4 -4
  235. package/src/authorization/gate.ts +1 -1
  236. package/src/bridge/a2a/mapper.ts +23 -4
  237. package/src/bridge/sse/mapper.ts +3 -0
  238. package/src/connector/index.ts +2 -0
  239. package/src/connector/mcp/adapter.ts +82 -21
  240. package/src/connector/mcp/client.ts +2 -1
  241. package/src/connector/mcp/http-redirect.ts +32 -0
  242. package/src/connector/mcp/http-sse.ts +5 -0
  243. package/src/connector/mcp/image-admission.ts +272 -0
  244. package/src/connector/mcp/stdio.ts +63 -21
  245. package/src/connector/mcp/streamable-http.ts +3 -0
  246. package/src/constants/tools/index.ts +1 -1
  247. package/src/execution/base.ts +124 -7
  248. package/src/execution/errors.ts +24 -0
  249. package/src/execution/factory.ts +1 -0
  250. package/src/execution/hybrid.ts +47 -16
  251. package/src/execution/index.ts +1 -0
  252. package/src/execution/local.ts +164 -19
  253. package/src/execution/output.ts +104 -0
  254. package/src/execution/remote.ts +122 -17
  255. package/src/plugin/lifecycle.ts +9 -3
  256. package/src/process/kill-tree.ts +11 -13
  257. package/src/provider/capabilities.ts +10 -0
  258. package/src/provider/fallback.ts +39 -0
  259. package/src/provider/idle-timeout.ts +8 -0
  260. package/src/provider/retry.ts +8 -0
  261. package/src/public-runtime.ts +7 -1
  262. package/src/registry/tool/execute.ts +230 -64
  263. package/src/run/command-gate.ts +12 -0
  264. package/src/run/workspace-fingerprint.ts +5 -4
  265. package/src/runtime/bidi/index.ts +1 -1
  266. package/src/runtime/bidi/session.ts +177 -28
  267. package/src/runtime/query/executor.ts +769 -163
  268. package/src/runtime/query/index.ts +58 -7
  269. package/src/runtime/query/iteration/index.ts +72 -0
  270. package/src/runtime/query/iteration/phases/context.ts +5 -0
  271. package/src/runtime/query/iteration/phases/tool-review.ts +87 -23
  272. package/src/runtime/query/request-rich-content.ts +39 -14
  273. package/src/runtime/query/result.ts +25 -23
  274. package/src/runtime/query/resume-pending.ts +83 -1
  275. package/src/runtime/query/tool-output-budget.ts +52 -17
  276. package/src/runtime/query/tooling.ts +10 -0
  277. package/src/sandbox/provider/local.ts +213 -45
  278. package/src/scheduler/delegating.ts +6 -5
  279. package/src/scheduler/local.ts +3 -2
  280. package/src/session/workspace/driver.ts +12 -4
  281. package/src/session/workspace/git-worktree.ts +173 -34
  282. package/src/skills/registry.ts +47 -0
  283. package/src/tools/builtins/computer-use.ts +86 -2
  284. package/src/tools/builtins/run-code.ts +9 -11
  285. package/src/tools/builtins/skill.ts +325 -7
  286. package/src/tools/coordinator/agent.ts +108 -33
  287. package/src/types/agent/manager.ts +1 -1
  288. package/src/types/agent/scheduler.ts +10 -1
  289. package/src/types/bidi/index.ts +19 -2
  290. package/src/types/computer-use/index.ts +19 -0
  291. package/src/types/connector/execution-context.ts +3 -0
  292. package/src/types/errors/catalog.ts +14 -6
  293. package/src/types/errors/index.ts +22 -8
  294. package/src/types/execution/index.ts +46 -1
  295. package/src/types/hitl/index.ts +11 -0
  296. package/src/types/message/index.ts +3 -2
  297. package/src/types/plugin/index.ts +15 -0
  298. package/src/types/provider/config.ts +9 -0
  299. package/src/types/provider/interface.ts +18 -0
  300. package/src/types/run/events.ts +17 -3
  301. package/src/types/sandbox/index.ts +15 -10
  302. package/src/types/tool/index.ts +85 -6
  303. package/src/types/tool/presentation.ts +10 -3
  304. package/src/types/workspace/ref.ts +5 -0
  305. package/src/utils/abort.ts +18 -0
@@ -230,6 +230,40 @@ function memberReasoningEffortLevels(
230
230
  return member.provider.effortLevelsFor?.(model, thinking)
231
231
  }
232
232
 
233
+ /** Read one member's model-owned default using its routed model id. */
234
+ function memberReasoningEffortDefault(
235
+ member: ProviderChainMember,
236
+ requestModel: string,
237
+ thinking?: ThinkingConfig,
238
+ ): ReasoningEffort | undefined {
239
+ return member.provider.reasoningEffortDefaultFor?.(member.model ?? requestModel, thinking)
240
+ }
241
+
242
+ /**
243
+ * A fallback request has one unambiguous default only when every route agrees.
244
+ *
245
+ * Omitting effort lets whichever member serves the request choose its own
246
+ * default. Publishing the head's answer as the chain's answer can therefore
247
+ * make the same shortcut move in opposite directions after fallback. The
248
+ * agreed value must also survive the chain-wide exact menu.
249
+ */
250
+ function commonReasoningEffortDefault(
251
+ members: readonly ProviderChainMember[],
252
+ requestModel: string,
253
+ thinking?: ThinkingConfig,
254
+ ): ReasoningEffort | undefined {
255
+ const common = commonReasoningEffortLevels(members, requestModel, thinking)
256
+ if (common === undefined) return undefined
257
+ let agreed: ReasoningEffort | undefined
258
+ for (const member of members) {
259
+ const value = memberReasoningEffortDefault(member, requestModel, thinking)
260
+ if (value === undefined) return undefined
261
+ if (agreed !== undefined && value !== agreed) return undefined
262
+ agreed = value
263
+ }
264
+ return agreed !== undefined && common.includes(agreed) ? agreed : undefined
265
+ }
266
+
233
267
  /**
234
268
  * The levels one request may carry safely through every member it can reach.
235
269
  *
@@ -463,6 +497,11 @@ export function withProviderFallback(
463
497
  // answer when any member cannot enumerate its selected model.
464
498
  reasoningEffortLevelsFor: (model: string, thinking?: ThinkingConfig) =>
465
499
  commonReasoningEffortLevels(members, model, thinking),
500
+ // Present on the chain wrapper for the same reason the menu method is:
501
+ // `undefined` is the honest answer when a member cannot answer, defaults
502
+ // disagree, or the value is absent from the common exact menu.
503
+ reasoningEffortDefaultFor: (model: string, thinking?: ThinkingConfig) =>
504
+ commonReasoningEffortDefault(members, model, thinking),
466
505
  // Compatibility projection only. Existing consumers of the deprecated
467
506
  // member keep the head-only answer they had; new consumers use the
468
507
  // canonical chain-aware member above.
@@ -220,6 +220,14 @@ export function withStreamIdleTimeout(
220
220
  ) => provider.reasoningEffortLevelsFor?.(model, thinking),
221
221
  }
222
222
  : {}),
223
+ ...(provider.reasoningEffortDefaultFor
224
+ ? {
225
+ reasoningEffortDefaultFor: (
226
+ model: string,
227
+ thinking?: Parameters<NonNullable<LLMProvider['reasoningEffortDefaultFor']>>[1],
228
+ ) => provider.reasoningEffortDefaultFor?.(model, thinking),
229
+ }
230
+ : {}),
223
231
  ...(provider.effortLevelsFor
224
232
  ? {
225
233
  effortLevelsFor: (
@@ -275,6 +275,14 @@ export function withProviderRetry(
275
275
  ) => provider.reasoningEffortLevelsFor?.(model, thinking),
276
276
  }
277
277
  : {}),
278
+ ...(provider.reasoningEffortDefaultFor
279
+ ? {
280
+ reasoningEffortDefaultFor: (
281
+ model: string,
282
+ thinking?: Parameters<NonNullable<LLMProvider['reasoningEffortDefaultFor']>>[1],
283
+ ) => provider.reasoningEffortDefaultFor?.(model, thinking),
284
+ }
285
+ : {}),
278
286
  // Forwarded for the same reason `healthCheck` is, and both were
279
287
  // missing until something consumed them. A member this wrapper drops
280
288
  // does not fail — it reads as "this driver cannot answer", which is a
@@ -293,7 +293,11 @@ export {
293
293
  formatSteeringNote,
294
294
  } from './runtime/query/steering.js'
295
295
  export type { SteeringChannel } from './runtime/query/steering.js'
296
- export { createMockBidiProvider, startBidiRun } from './runtime/bidi/index.js'
296
+ export {
297
+ BidiSessionCloseTimeoutError,
298
+ createMockBidiProvider,
299
+ startBidiRun,
300
+ } from './runtime/bidi/index.js'
297
301
  export { PromptCache } from './runtime/query/prompt-cache.js'
298
302
  export {
299
303
  CheckpointManager,
@@ -672,6 +676,7 @@ export {
672
676
  export {
673
677
  BaseConnector,
674
678
  BaseExecutionContext,
679
+ CommandCancellationUnsupportedError,
675
680
  ConnectorManager,
676
681
  ConnectorRegistry,
677
682
  EnvironmentConnectorManager,
@@ -695,6 +700,7 @@ export {
695
700
  mcpToolResultToToolResult,
696
701
  mcpToolToToolDefinition,
697
702
  RemoteExecutionContext,
703
+ RemoteExecutionBusyError,
698
704
  ScopedConnectorRegistry,
699
705
  // The only transport in the tree that can RUN an `MCPServer`. It reached
700
706
  // `connector/mcp/index.ts` and stopped there, so the server was public
@@ -7,10 +7,12 @@ import { isTrustedReadOnly } from '../../tools/trusted-read-only.js'
7
7
  import type { ToolResultGuardrailSpec } from '../../types/guardrail/index.js'
8
8
  import type {
9
9
  LLMToolSchema,
10
+ PreparedToolExecution,
10
11
  ToolAvailability,
11
12
  ToolContext,
12
13
  ToolDefinition,
13
14
  ToolExecutionResult,
15
+ ToolPreparationResult,
14
16
  ToolRegistryConfig,
15
17
  ToolTierConfig,
16
18
  } from '../../types/tool/index.js'
@@ -87,6 +89,82 @@ export function assertToolName(name: string): void {
87
89
  )
88
90
  }
89
91
 
92
+ function clonePreparedInput(value: unknown, freeze: boolean, path = '$'): unknown {
93
+ const active = new WeakSet<object>()
94
+ const copies = new WeakMap<object, object>()
95
+
96
+ const clone = (candidate: unknown, at: string): unknown => {
97
+ if (candidate === null || typeof candidate === 'string' || typeof candidate === 'boolean') {
98
+ return candidate
99
+ }
100
+ if (typeof candidate === 'number') {
101
+ if (!Number.isFinite(candidate) || Object.is(candidate, -0)) {
102
+ throw new TypeError(`${at} must be a finite JSON number`)
103
+ }
104
+ return candidate
105
+ }
106
+ if (typeof candidate !== 'object') {
107
+ throw new TypeError(`${at} must be a JSON value, not ${typeof candidate}`)
108
+ }
109
+ if (active.has(candidate)) throw new TypeError(`${at} contains a cycle`)
110
+ const existing = copies.get(candidate)
111
+ if (existing) return existing
112
+
113
+ active.add(candidate)
114
+ try {
115
+ if (Array.isArray(candidate)) {
116
+ const extra = Reflect.ownKeys(candidate).filter(
117
+ (key) => key !== 'length' && (typeof key !== 'string' || !/^(0|[1-9]\d*)$/.test(key)),
118
+ )
119
+ if (extra.length > 0) throw new TypeError(`${at} has non-JSON array properties`)
120
+ const result: unknown[] = new Array(candidate.length)
121
+ copies.set(candidate, result)
122
+ for (let index = 0; index < candidate.length; index++) {
123
+ const descriptor = Object.getOwnPropertyDescriptor(candidate, String(index))
124
+ if (!descriptor) throw new TypeError(`${at}[${index}] is a sparse array hole`)
125
+ if (!descriptor.enumerable || !('value' in descriptor)) {
126
+ throw new TypeError(`${at}[${index}] must be an enumerable data property`)
127
+ }
128
+ result[index] = clone(descriptor.value, `${at}[${index}]`)
129
+ }
130
+ if (freeze) Object.freeze(result)
131
+ return result
132
+ }
133
+
134
+ const prototype = Object.getPrototypeOf(candidate)
135
+ if (prototype !== Object.prototype && prototype !== null) {
136
+ throw new TypeError(`${at} must be a plain JSON object`)
137
+ }
138
+ // JSON does not preserve prototypes. Canonicalize both admitted plain
139
+ // shapes to an ordinary object so a disk checkpoint round-trip compares
140
+ // equal to a fresh preparation of the same semantic value. Properties
141
+ // are still defined explicitly, so a literal "__proto__" key remains
142
+ // data rather than changing this object's prototype.
143
+ const result: Record<string, unknown> = {}
144
+ copies.set(candidate, result)
145
+ for (const key of Reflect.ownKeys(candidate)) {
146
+ if (typeof key !== 'string') throw new TypeError(`${at} has a symbol property`)
147
+ const descriptor = Object.getOwnPropertyDescriptor(candidate, key)
148
+ if (!descriptor?.enumerable || !('value' in descriptor)) {
149
+ throw new TypeError(`${at}.${key} must be an enumerable data property`)
150
+ }
151
+ Object.defineProperty(result, key, {
152
+ value: clone(descriptor.value, `${at}.${key}`),
153
+ enumerable: true,
154
+ writable: !freeze,
155
+ configurable: !freeze,
156
+ })
157
+ }
158
+ if (freeze) Object.freeze(result)
159
+ return result
160
+ } finally {
161
+ active.delete(candidate)
162
+ }
163
+ }
164
+
165
+ return clone(value, path)
166
+ }
167
+
90
168
  /**
91
169
  * Two sources contributed the same tool name and neither may take it.
92
170
  *
@@ -136,9 +214,17 @@ export class ToolRegistry extends ManagedRegistry<ToolDefinition> {
136
214
  private availability: Map<string, ToolAvailability> = new Map()
137
215
  private tierConfig?: ToolTierConfig
138
216
  private resultGuardrails?: readonly ToolResultGuardrailSpec[]
217
+ private readonly preparations = new WeakMap<
218
+ PreparedToolExecution,
219
+ { readonly tool: ToolDefinition; readonly input: unknown }
220
+ >()
139
221
 
140
222
  constructor(config?: ToolRegistryConfig) {
141
- super({ componentName: 'ToolRegistry', idField: 'name', logger: config?.logger })
223
+ super({
224
+ componentName: 'ToolRegistry',
225
+ idField: 'name',
226
+ logger: config?.logger,
227
+ })
142
228
  this.tierConfig = config?.tierConfig
143
229
  this.resultGuardrails = config?.resultGuardrails
144
230
  }
@@ -401,10 +487,106 @@ Executable tool names, descriptions, and JSON input schemas are attached through
401
487
  return this.getByAvailability(['active'], toolNames)
402
488
  }
403
489
 
490
+ prepareExecution(toolName: string, rawInput: unknown): ToolPreparationResult {
491
+ const tool = this.getOrThrow(toolName)
492
+ const parseResult = tool.inputSchema.safeParse(rawInput)
493
+ if (!parseResult.success) {
494
+ return {
495
+ success: false,
496
+ result: this.validationFailure(tool, rawInput, parseResult.error),
497
+ }
498
+ }
499
+
500
+ let retainedInput: unknown
501
+ let reviewInput: unknown
502
+ try {
503
+ retainedInput = clonePreparedInput(parseResult.data, false)
504
+ reviewInput = clonePreparedInput(retainedInput, true)
505
+ } catch (err) {
506
+ const message = `Tool "${toolName}" produced an input that cannot be safely prepared for review and execution: ${toErrorMessage(err)}`
507
+ this.log.error('Prepared tool input could not be detached for review and execution', {
508
+ 'namzu.tool.name': toolName,
509
+ 'exception.message': toErrorMessage(err),
510
+ })
511
+ return {
512
+ success: false,
513
+ result: { success: false, output: '', error: message },
514
+ }
515
+ }
516
+
517
+ const prepared = Object.freeze({ toolName, input: reviewInput })
518
+ this.preparations.set(prepared, { tool, input: retainedInput })
519
+ return { success: true, prepared }
520
+ }
521
+
522
+ async executePrepared(
523
+ prepared: PreparedToolExecution,
524
+ context: ToolContext,
525
+ ): Promise<ToolExecutionResult> {
526
+ const retained = this.preparations.get(prepared)
527
+ if (!retained) {
528
+ return {
529
+ success: false,
530
+ output: '',
531
+ error: `Tool "${prepared.toolName}" preparation is not owned by this registry or is no longer valid.`,
532
+ }
533
+ }
534
+ if (this.get(prepared.toolName) !== retained.tool) {
535
+ return {
536
+ success: false,
537
+ output: '',
538
+ error: `Tool "${prepared.toolName}" changed after its input was reviewed; prepare the call again.`,
539
+ }
540
+ }
541
+ return this.executeRetained(prepared.toolName, retained.tool, retained.input, context)
542
+ }
543
+
404
544
  async execute(
405
545
  toolName: string,
406
546
  rawInput: unknown,
407
547
  context: ToolContext,
548
+ ): Promise<ToolExecutionResult> {
549
+ let preparation: ToolPreparationResult
550
+ try {
551
+ preparation = this.prepareExecution(toolName, rawInput)
552
+ } catch (err) {
553
+ return this.rejectPreparationWithClosedSpan(toolName, context, err)
554
+ }
555
+ if (!preparation.success) return preparation.result
556
+ return this.executePrepared(preparation.prepared, context)
557
+ }
558
+
559
+ private rejectPreparationWithClosedSpan(
560
+ toolName: string,
561
+ context: ToolContext,
562
+ err: unknown,
563
+ ): Promise<never> {
564
+ const tracer = getTracer()
565
+ const parentCtx = context.parentSpan
566
+ ? trace.setSpan(otelContext.active(), context.parentSpan)
567
+ : otelContext.active()
568
+ return tracer.startActiveSpan(toolSpanName(toolName), {}, parentCtx, async (span) => {
569
+ try {
570
+ span.setAttributes({
571
+ [GENAI.TOOL_NAME]: toolName,
572
+ [GENAI.TOOL_TYPE]: 'function',
573
+ ...(context.toolUseId !== undefined ? { [GENAI.TOOL_CALL_ID]: context.toolUseId } : {}),
574
+ [NAMZU.TOOL_SUCCESS]: false,
575
+ [NAMZU.TOOL_ERROR]: toErrorMessage(err),
576
+ })
577
+ span.setStatus({ code: SpanStatusCode.ERROR, message: toErrorMessage(err) })
578
+ throw err
579
+ } finally {
580
+ span.end()
581
+ }
582
+ })
583
+ }
584
+
585
+ private async executeRetained(
586
+ toolName: string,
587
+ tool: ToolDefinition,
588
+ finalInput: unknown,
589
+ context: ToolContext,
408
590
  ): Promise<ToolExecutionResult> {
409
591
  const tracer = getTracer()
410
592
 
@@ -437,8 +619,6 @@ Executable tool names, descriptions, and JSON input schemas are attached through
437
619
  ...(context.toolUseId !== undefined ? { [GENAI.TOOL_CALL_ID]: context.toolUseId } : {}),
438
620
  })
439
621
 
440
- const tool = this.getOrThrow(toolName)
441
-
442
622
  const availability = this.getAvailability(toolName)
443
623
  if (availability !== 'active') {
444
624
  const msg = `Tool "${toolName}" is ${availability} and cannot be executed`
@@ -492,7 +672,7 @@ Executable tool names, descriptions, and JSON input schemas are attached through
492
672
 
493
673
  const mode = context.permissionContext?.mode ?? 'auto'
494
674
  if (mode === 'plan') {
495
- const isReadOnly = isTrustedReadOnly(tool, rawInput)
675
+ const isReadOnly = isTrustedReadOnly(tool, finalInput)
496
676
  if (!isReadOnly) {
497
677
  const msg = `plan mode: non-read-only tool "${toolName}" blocked`
498
678
  span.setAttributes({
@@ -510,63 +690,6 @@ Executable tool names, descriptions, and JSON input schemas are attached through
510
690
  }
511
691
  }
512
692
 
513
- const parseResult = tool.inputSchema.safeParse(rawInput)
514
- if (!parseResult.success) {
515
- const errorMessage = parseResult.error.issues
516
- .map((i) => `${i.path.join('.')}: ${i.message}`)
517
- .join('; ')
518
-
519
- // Distinguish "model sent an empty/no-arg call" from
520
- // "model sent partial args" — the first is most often a
521
- // streaming hiccup or a definition-test ping (a provider
522
- // occasionally pings tool surfaces with `{}` while the
523
- // schema is still loading), the second is a genuine
524
- // programming mistake by the model. The model self-
525
- // corrects MUCH more reliably when the error tells it
526
- // (a) which fields are required, (b) their types, and
527
- // (c) a minimal example call. Without these hints the
528
- // downstream UI just shows a red "Failed" row and the
529
- // model rarely retries with the right args.
530
- const isEmptyInput =
531
- rawInput === null ||
532
- rawInput === undefined ||
533
- (typeof rawInput === 'object' &&
534
- !Array.isArray(rawInput) &&
535
- Object.keys(rawInput as Record<string, unknown>).length === 0)
536
-
537
- const requiredHint = describeRequiredInput(tool.inputSchema)
538
- // A conditional schema's required shape cannot be reconstructed
539
- // from JSON Schema's top-level `required`, so the author gets to
540
- // say what a valid retry looks like.
541
- const recoveryHint = tool.validationErrorHint?.trim()
542
- ? ` ${tool.validationErrorHint.trim()}`
543
- : ''
544
-
545
- const enrichedMessage = isEmptyInput
546
- ? `Tool "${toolName}" was called with no arguments. ${requiredHint}${recoveryHint} Retry the call with the required parameters populated.`
547
- : `Validation failed for "${toolName}": ${errorMessage}. ${requiredHint}${recoveryHint}`
548
-
549
- this.log.error('Tool input validation failed', {
550
- 'namzu.tool.name': toolName,
551
- 'namzu.registry.errors': errorMessage,
552
- 'namzu.registry.empty': isEmptyInput,
553
- })
554
-
555
- span.setAttributes({
556
- [NAMZU.TOOL_SUCCESS]: false,
557
- [NAMZU.TOOL_ERROR]: `Validation: ${errorMessage}`,
558
- })
559
- span.setStatus({ code: SpanStatusCode.ERROR, message: errorMessage })
560
-
561
- return {
562
- success: false,
563
- output: '',
564
- error: enrichedMessage,
565
- }
566
- }
567
-
568
- const finalInput = parseResult.data
569
-
570
693
  try {
571
694
  this.log.debug('Executing tool', { 'namzu.tool.name': toolName })
572
695
  const startedAt = Date.now()
@@ -604,7 +727,10 @@ Executable tool names, descriptions, and JSON input schemas are attached through
604
727
  )
605
728
  if (!result.success && result.error) {
606
729
  span.setAttribute(NAMZU.TOOL_ERROR, result.error)
607
- span.setStatus({ code: SpanStatusCode.ERROR, message: result.error })
730
+ span.setStatus({
731
+ code: SpanStatusCode.ERROR,
732
+ message: result.error,
733
+ })
608
734
  } else {
609
735
  span.setStatus({ code: SpanStatusCode.OK })
610
736
  }
@@ -622,7 +748,10 @@ Executable tool names, descriptions, and JSON input schemas are attached through
622
748
  [NAMZU.TOOL_SUCCESS]: false,
623
749
  [NAMZU.TOOL_ERROR]: err.message,
624
750
  })
625
- span.setStatus({ code: SpanStatusCode.ERROR, message: err.message })
751
+ span.setStatus({
752
+ code: SpanStatusCode.ERROR,
753
+ message: err.message,
754
+ })
626
755
  throw err
627
756
  }
628
757
  const errorMessage = toErrorMessage(err)
@@ -635,7 +764,10 @@ Executable tool names, descriptions, and JSON input schemas are attached through
635
764
  [NAMZU.TOOL_SUCCESS]: false,
636
765
  [NAMZU.TOOL_ERROR]: errorMessage,
637
766
  })
638
- span.setStatus({ code: SpanStatusCode.ERROR, message: errorMessage })
767
+ span.setStatus({
768
+ code: SpanStatusCode.ERROR,
769
+ message: errorMessage,
770
+ })
639
771
  span.recordException(err instanceof Error ? err : new Error(errorMessage))
640
772
 
641
773
  return {
@@ -655,6 +787,40 @@ Executable tool names, descriptions, and JSON input schemas are attached through
655
787
  })
656
788
  }
657
789
 
790
+ private validationFailure(
791
+ tool: ToolDefinition,
792
+ rawInput: unknown,
793
+ error: {
794
+ readonly issues: readonly {
795
+ readonly path: readonly PropertyKey[]
796
+ readonly message: string
797
+ }[]
798
+ },
799
+ ): ToolExecutionResult {
800
+ const errorMessage = error.issues
801
+ .map((issue) => `${issue.path.join('.')}: ${issue.message}`)
802
+ .join('; ')
803
+ const isEmptyInput =
804
+ rawInput === null ||
805
+ rawInput === undefined ||
806
+ (typeof rawInput === 'object' &&
807
+ !Array.isArray(rawInput) &&
808
+ Object.keys(rawInput as Record<string, unknown>).length === 0)
809
+ const requiredHint = describeRequiredInput(tool.inputSchema)
810
+ const recoveryHint = tool.validationErrorHint?.trim()
811
+ ? ` ${tool.validationErrorHint.trim()}`
812
+ : ''
813
+ const enrichedMessage = isEmptyInput
814
+ ? `Tool "${tool.name}" was called with no arguments. ${requiredHint}${recoveryHint} Retry the call with the required parameters populated.`
815
+ : `Validation failed for "${tool.name}": ${errorMessage}. ${requiredHint}${recoveryHint}`
816
+ this.log.error('Tool input validation failed', {
817
+ 'namzu.tool.name': tool.name,
818
+ 'namzu.registry.errors': errorMessage,
819
+ 'namzu.registry.empty': isEmptyInput,
820
+ })
821
+ return { success: false, output: '', error: enrichedMessage }
822
+ }
823
+
658
824
  private getByAvailability(states: ToolAvailability[], filter?: string[]): ToolDefinition[] {
659
825
  const candidates = filter ? filter.map((n) => this.getOrThrow(n)) : this.getAll()
660
826
  return candidates.filter((t) => states.includes(this.getAvailability(t.name)))
@@ -130,6 +130,17 @@ function failureFeedback(
130
130
  maxOutputChars: number,
131
131
  ): string {
132
132
  const output = clipOutput(`${result.stdout}\n${result.stderr}`, maxOutputChars)
133
+ const truncatedStreams = [
134
+ result.stdoutTruncated === true ? 'stdout' : undefined,
135
+ result.stderrTruncated === true ? 'stderr' : undefined,
136
+ ].filter((stream): stream is string => stream !== undefined)
137
+ const truncationWarning =
138
+ truncatedStreams.length === 0
139
+ ? []
140
+ : [
141
+ '',
142
+ `Retained output is incomplete: ${truncatedStreams.join(' and ')} ${truncatedStreams.length === 1 ? 'was' : 'were'} truncated by the execution context. Re-run with a narrower command or filter to recover the missing diagnostics.`,
143
+ ]
133
144
  return [
134
145
  `The answer was not accepted: \`${command}\` failed (attempt ${attempt}, exit ${result.exitCode}).`,
135
146
  '',
@@ -137,6 +148,7 @@ function failureFeedback(
137
148
  '```',
138
149
  output || '(no output)',
139
150
  '```',
151
+ ...truncationWarning,
140
152
  '',
141
153
  'Fix the cause and then finish. Do not restate the failure back to me; change the code so the command passes.',
142
154
  ].join('\n')
@@ -41,10 +41,10 @@
41
41
  * told to edit something it had already edited.
42
42
  *
43
43
  * So: a non-zero exit from any git invocation, a repository with no commits,
44
- * a timeout, or output past the size cap all produce `null` rather than a
45
- * partial hash. A truncated diff that hashed successfully would be the worst
46
- * outcome available here, because two different workspaces truncated at the
47
- * same point collide.
44
+ * a timeout, an executor-reported truncated stream, or output past the size
45
+ * cap all produce `null` rather than a partial hash. A truncated diff that
46
+ * hashed successfully would be the worst outcome available here, because two
47
+ * different workspaces truncated at the same point collide.
48
48
  */
49
49
 
50
50
  import { createHash } from 'node:crypto'
@@ -156,6 +156,7 @@ export async function fingerprintWorkspace(
156
156
  // repository" and "no commits yet". All three mean the same thing to
157
157
  // this function: it has no basis for a comparison.
158
158
  if (result.exitCode !== 0) return null
159
+ if (result.stdoutTruncated === true || result.stderrTruncated === true) return null
159
160
  if (Buffer.byteLength(result.stdout, 'utf8') > maxBytes) return null
160
161
  return result.stdout
161
162
  }
@@ -1,4 +1,4 @@
1
1
  export { createMockBidiProvider } from './mock.js'
2
2
  export type { MockBidiScript, MockBidiSession } from './mock.js'
3
- export { startBidiRun } from './session.js'
3
+ export { BidiSessionCloseTimeoutError, startBidiRun } from './session.js'
4
4
  export type { BidiRun, BidiRunParams } from './session.js'