@tanstack/ai 0.41.0 → 0.43.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 (272) hide show
  1. package/README.md +15 -1
  2. package/dist/esm/activities/chat/adapter.js +23 -16
  3. package/dist/esm/activities/chat/adapter.js.map +1 -1
  4. package/dist/esm/activities/chat/agent-loop-strategies.d.ts +10 -4
  5. package/dist/esm/activities/chat/agent-loop-strategies.js +75 -17
  6. package/dist/esm/activities/chat/agent-loop-strategies.js.map +1 -1
  7. package/dist/esm/activities/chat/cancel.d.ts +40 -0
  8. package/dist/esm/activities/chat/cancel.js +54 -0
  9. package/dist/esm/activities/chat/cancel.js.map +1 -0
  10. package/dist/esm/activities/chat/index.d.ts +28 -16
  11. package/dist/esm/activities/chat/index.js +2100 -1744
  12. package/dist/esm/activities/chat/index.js.map +1 -1
  13. package/dist/esm/activities/chat/mcp/manager.d.ts +2 -2
  14. package/dist/esm/activities/chat/mcp/manager.js +90 -77
  15. package/dist/esm/activities/chat/mcp/manager.js.map +1 -1
  16. package/dist/esm/activities/chat/mcp/types.d.ts +2 -2
  17. package/dist/esm/activities/chat/messages.js +397 -346
  18. package/dist/esm/activities/chat/messages.js.map +1 -1
  19. package/dist/esm/activities/chat/middleware/builder.js +17 -15
  20. package/dist/esm/activities/chat/middleware/builder.js.map +1 -1
  21. package/dist/esm/activities/chat/middleware/capabilities.js +78 -43
  22. package/dist/esm/activities/chat/middleware/capabilities.js.map +1 -1
  23. package/dist/esm/activities/chat/middleware/compose.d.ts +94 -1
  24. package/dist/esm/activities/chat/middleware/compose.js +623 -531
  25. package/dist/esm/activities/chat/middleware/compose.js.map +1 -1
  26. package/dist/esm/activities/chat/middleware/define.js +12 -5
  27. package/dist/esm/activities/chat/middleware/define.js.map +1 -1
  28. package/dist/esm/activities/chat/middleware/index.d.ts +5 -1
  29. package/dist/esm/activities/chat/middleware/locks.d.ts +50 -0
  30. package/dist/esm/activities/chat/middleware/locks.js +71 -0
  31. package/dist/esm/activities/chat/middleware/locks.js.map +1 -0
  32. package/dist/esm/activities/chat/middleware/pending-turn.d.ts +15 -0
  33. package/dist/esm/activities/chat/middleware/pending-turn.js +35 -0
  34. package/dist/esm/activities/chat/middleware/pending-turn.js.map +1 -0
  35. package/dist/esm/activities/chat/middleware/run-disconnect.d.ts +23 -0
  36. package/dist/esm/activities/chat/middleware/run-disconnect.js +42 -0
  37. package/dist/esm/activities/chat/middleware/run-disconnect.js.map +1 -0
  38. package/dist/esm/activities/chat/middleware/run-store.d.ts +283 -0
  39. package/dist/esm/activities/chat/middleware/run-store.js +176 -0
  40. package/dist/esm/activities/chat/middleware/run-store.js.map +1 -0
  41. package/dist/esm/activities/chat/middleware/sandbox-runtime.js +14 -8
  42. package/dist/esm/activities/chat/middleware/sandbox-runtime.js.map +1 -1
  43. package/dist/esm/activities/chat/middleware/tool-cache-middleware.js +79 -70
  44. package/dist/esm/activities/chat/middleware/tool-cache-middleware.js.map +1 -1
  45. package/dist/esm/activities/chat/middleware/types.d.ts +59 -2
  46. package/dist/esm/activities/chat/middleware/validate.js +23 -28
  47. package/dist/esm/activities/chat/middleware/validate.js.map +1 -1
  48. package/dist/esm/activities/chat/stream/json-parser.js +39 -25
  49. package/dist/esm/activities/chat/stream/json-parser.js.map +1 -1
  50. package/dist/esm/activities/chat/stream/message-updaters.js +275 -234
  51. package/dist/esm/activities/chat/stream/message-updaters.js.map +1 -1
  52. package/dist/esm/activities/chat/stream/processor.d.ts +24 -4
  53. package/dist/esm/activities/chat/stream/processor.js +1341 -1542
  54. package/dist/esm/activities/chat/stream/processor.js.map +1 -1
  55. package/dist/esm/activities/chat/stream/strategies.js +69 -53
  56. package/dist/esm/activities/chat/stream/strategies.js.map +1 -1
  57. package/dist/esm/activities/chat/tools/approval-schema.d.ts +19 -0
  58. package/dist/esm/activities/chat/tools/approval-schema.js +117 -0
  59. package/dist/esm/activities/chat/tools/approval-schema.js.map +1 -0
  60. package/dist/esm/activities/chat/tools/lazy-tool-manager.js +164 -191
  61. package/dist/esm/activities/chat/tools/lazy-tool-manager.js.map +1 -1
  62. package/dist/esm/activities/chat/tools/lazy-tools.js +24 -12
  63. package/dist/esm/activities/chat/tools/lazy-tools.js.map +1 -1
  64. package/dist/esm/activities/chat/tools/schema-converter.js +293 -146
  65. package/dist/esm/activities/chat/tools/schema-converter.js.map +1 -1
  66. package/dist/esm/activities/chat/tools/tool-calls.d.ts +18 -2
  67. package/dist/esm/activities/chat/tools/tool-calls.js +522 -531
  68. package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
  69. package/dist/esm/activities/chat/tools/tool-definition.d.ts +75 -16
  70. package/dist/esm/activities/chat/tools/tool-definition.js +95 -23
  71. package/dist/esm/activities/chat/tools/tool-definition.js.map +1 -1
  72. package/dist/esm/activities/error-payload.js +85 -47
  73. package/dist/esm/activities/error-payload.js.map +1 -1
  74. package/dist/esm/activities/generateAudio/adapter.js +22 -15
  75. package/dist/esm/activities/generateAudio/adapter.js.map +1 -1
  76. package/dist/esm/activities/generateAudio/index.d.ts +4 -0
  77. package/dist/esm/activities/generateAudio/index.js +141 -105
  78. package/dist/esm/activities/generateAudio/index.js.map +1 -1
  79. package/dist/esm/activities/generateImage/adapter.js +22 -15
  80. package/dist/esm/activities/generateImage/adapter.js.map +1 -1
  81. package/dist/esm/activities/generateImage/index.d.ts +4 -0
  82. package/dist/esm/activities/generateImage/index.js +155 -111
  83. package/dist/esm/activities/generateImage/index.js.map +1 -1
  84. package/dist/esm/activities/generateSpeech/adapter.js +22 -15
  85. package/dist/esm/activities/generateSpeech/adapter.js.map +1 -1
  86. package/dist/esm/activities/generateSpeech/index.d.ts +4 -0
  87. package/dist/esm/activities/generateSpeech/index.js +159 -110
  88. package/dist/esm/activities/generateSpeech/index.js.map +1 -1
  89. package/dist/esm/activities/generateTranscription/adapter.js +22 -15
  90. package/dist/esm/activities/generateTranscription/adapter.js.map +1 -1
  91. package/dist/esm/activities/generateTranscription/index.d.ts +4 -0
  92. package/dist/esm/activities/generateTranscription/index.js +159 -100
  93. package/dist/esm/activities/generateTranscription/index.js.map +1 -1
  94. package/dist/esm/activities/generateVideo/adapter.js +36 -29
  95. package/dist/esm/activities/generateVideo/adapter.js.map +1 -1
  96. package/dist/esm/activities/generateVideo/index.d.ts +143 -19
  97. package/dist/esm/activities/generateVideo/index.js +456 -279
  98. package/dist/esm/activities/generateVideo/index.js.map +1 -1
  99. package/dist/esm/activities/generateVideo/snap.js +60 -48
  100. package/dist/esm/activities/generateVideo/snap.js.map +1 -1
  101. package/dist/esm/activities/index.js +8 -34
  102. package/dist/esm/activities/middleware/index.d.ts +1 -1
  103. package/dist/esm/activities/middleware/run.d.ts +10 -0
  104. package/dist/esm/activities/middleware/run.js +53 -29
  105. package/dist/esm/activities/middleware/run.js.map +1 -1
  106. package/dist/esm/activities/middleware/types.d.ts +44 -6
  107. package/dist/esm/activities/stream-generation-result.d.ts +4 -1
  108. package/dist/esm/activities/stream-generation-result.js +79 -44
  109. package/dist/esm/activities/stream-generation-result.js.map +1 -1
  110. package/dist/esm/activities/summarize/adapter.js +22 -15
  111. package/dist/esm/activities/summarize/adapter.js.map +1 -1
  112. package/dist/esm/activities/summarize/chat-stream-summarize.js +252 -202
  113. package/dist/esm/activities/summarize/chat-stream-summarize.js.map +1 -1
  114. package/dist/esm/activities/summarize/index.d.ts +27 -0
  115. package/dist/esm/activities/summarize/index.js +268 -102
  116. package/dist/esm/activities/summarize/index.js.map +1 -1
  117. package/dist/esm/adapter-internals.d.ts +2 -1
  118. package/dist/esm/adapter-internals.js +4 -11
  119. package/dist/esm/client.d.ts +25 -3
  120. package/dist/esm/client.js +131 -64
  121. package/dist/esm/client.js.map +1 -1
  122. package/dist/esm/custom-events.d.ts +76 -0
  123. package/dist/esm/custom-events.js +37 -0
  124. package/dist/esm/custom-events.js.map +1 -0
  125. package/dist/esm/delivery-detach.d.ts +50 -0
  126. package/dist/esm/delivery-detach.js +71 -0
  127. package/dist/esm/delivery-detach.js.map +1 -0
  128. package/dist/esm/delivery-disconnect.d.ts +62 -0
  129. package/dist/esm/delivery-disconnect.js +81 -0
  130. package/dist/esm/delivery-disconnect.js.map +1 -0
  131. package/dist/esm/extend-adapter.js +19 -17
  132. package/dist/esm/extend-adapter.js.map +1 -1
  133. package/dist/esm/index.d.ts +23 -5
  134. package/dist/esm/index.js +30 -97
  135. package/dist/esm/interrupt-resume.d.ts +71 -0
  136. package/dist/esm/interrupt-resume.js +438 -0
  137. package/dist/esm/interrupt-resume.js.map +1 -0
  138. package/dist/esm/interrupt-serialization.d.ts +12 -0
  139. package/dist/esm/interrupt-serialization.js +178 -0
  140. package/dist/esm/interrupt-serialization.js.map +1 -0
  141. package/dist/esm/interrupts.d.ts +84 -0
  142. package/dist/esm/interrupts.js +31 -0
  143. package/dist/esm/interrupts.js.map +1 -0
  144. package/dist/esm/locks.d.ts +10 -0
  145. package/dist/esm/locks.js +2 -0
  146. package/dist/esm/logger/console-logger.js +101 -78
  147. package/dist/esm/logger/console-logger.js.map +1 -1
  148. package/dist/esm/logger/internal-logger.js +104 -89
  149. package/dist/esm/logger/internal-logger.js.map +1 -1
  150. package/dist/esm/logger/resolve.js +54 -49
  151. package/dist/esm/logger/resolve.js.map +1 -1
  152. package/dist/esm/logger/types.d.ts +1 -1
  153. package/dist/esm/middlewares/content-guard.js +142 -148
  154. package/dist/esm/middlewares/content-guard.js.map +1 -1
  155. package/dist/esm/middlewares/index.js +2 -6
  156. package/dist/esm/middlewares/otel.js +598 -732
  157. package/dist/esm/middlewares/otel.js.map +1 -1
  158. package/dist/esm/middlewares/usage-attributes.js +47 -40
  159. package/dist/esm/middlewares/usage-attributes.js.map +1 -1
  160. package/dist/esm/realtime/event-emitter.js +24 -25
  161. package/dist/esm/realtime/event-emitter.js.map +1 -1
  162. package/dist/esm/realtime/index.d.ts +5 -9
  163. package/dist/esm/realtime/index.js +29 -6
  164. package/dist/esm/realtime/index.js.map +1 -1
  165. package/dist/esm/scope.d.ts +47 -0
  166. package/dist/esm/stream-durability.d.ts +171 -0
  167. package/dist/esm/stream-durability.js +295 -0
  168. package/dist/esm/stream-durability.js.map +1 -0
  169. package/dist/esm/stream-to-response.d.ts +178 -13
  170. package/dist/esm/stream-to-response.js +663 -115
  171. package/dist/esm/stream-to-response.js.map +1 -1
  172. package/dist/esm/strip-to-spec-middleware.js +30 -16
  173. package/dist/esm/strip-to-spec-middleware.js.map +1 -1
  174. package/dist/esm/system-prompts.js +27 -21
  175. package/dist/esm/system-prompts.js.map +1 -1
  176. package/dist/esm/tool-registry.js +72 -45
  177. package/dist/esm/tool-registry.js.map +1 -1
  178. package/dist/esm/tools/provider-tool.js +14 -5
  179. package/dist/esm/tools/provider-tool.js.map +1 -1
  180. package/dist/esm/types.d.ts +332 -21
  181. package/dist/esm/types.js +2 -0
  182. package/dist/esm/utilities/ag-ui-wire.js +79 -93
  183. package/dist/esm/utilities/ag-ui-wire.js.map +1 -1
  184. package/dist/esm/utilities/chat-params.d.ts +26 -4
  185. package/dist/esm/utilities/chat-params.js +218 -92
  186. package/dist/esm/utilities/chat-params.js.map +1 -1
  187. package/dist/esm/utilities/errors.js +28 -18
  188. package/dist/esm/utilities/errors.js.map +1 -1
  189. package/dist/esm/utilities/media-prompt.js +46 -41
  190. package/dist/esm/utilities/media-prompt.js.map +1 -1
  191. package/dist/esm/utilities/numbers.js +13 -10
  192. package/dist/esm/utilities/numbers.js.map +1 -1
  193. package/dist/esm/utilities/provider-executed.js +20 -11
  194. package/dist/esm/utilities/provider-executed.js.map +1 -1
  195. package/dist/esm/utilities/sampling-keys.js +31 -19
  196. package/dist/esm/utilities/sampling-keys.js.map +1 -1
  197. package/dist/esm/utilities/tool-result.js +42 -30
  198. package/dist/esm/utilities/tool-result.js.map +1 -1
  199. package/dist/esm/utilities/usage.js +27 -9
  200. package/dist/esm/utilities/usage.js.map +1 -1
  201. package/dist/esm/utils.js +26 -18
  202. package/dist/esm/utils.js.map +1 -1
  203. package/package.json +10 -6
  204. package/skills/ai-core/SKILL.md +69 -18
  205. package/skills/ai-core/adapter-configuration/SKILL.md +44 -21
  206. package/skills/ai-core/adapter-configuration/references/anthropic-adapter.md +1 -3
  207. package/skills/ai-core/adapter-configuration/references/byteplus-adapter.md +148 -0
  208. package/skills/ai-core/adapter-configuration/references/gemini-adapter.md +2 -6
  209. package/skills/ai-core/adapter-configuration/references/groq-adapter.md +2 -6
  210. package/skills/ai-core/adapter-configuration/references/openai-adapter.md +1 -3
  211. package/skills/ai-core/ag-ui-protocol/SKILL.md +1 -1
  212. package/skills/ai-core/chat-experience/SKILL.md +156 -11
  213. package/skills/ai-core/client-persistence/SKILL.md +277 -0
  214. package/skills/ai-core/custom-backend-integration/SKILL.md +1 -1
  215. package/skills/ai-core/debug-logging/SKILL.md +1 -1
  216. package/skills/ai-core/locks/SKILL.md +143 -0
  217. package/skills/ai-core/media-generation/SKILL.md +144 -12
  218. package/skills/ai-core/middleware/SKILL.md +258 -33
  219. package/skills/ai-core/structured-outputs/SKILL.md +1 -1
  220. package/skills/ai-core/tool-calling/SKILL.md +54 -59
  221. package/src/activities/chat/agent-loop-strategies.ts +10 -4
  222. package/src/activities/chat/cancel.ts +81 -0
  223. package/src/activities/chat/index.ts +1152 -153
  224. package/src/activities/chat/mcp/manager.ts +4 -4
  225. package/src/activities/chat/mcp/types.ts +2 -2
  226. package/src/activities/chat/messages.ts +5 -3
  227. package/src/activities/chat/middleware/builder.ts +1 -1
  228. package/src/activities/chat/middleware/compose.ts +186 -9
  229. package/src/activities/chat/middleware/index.ts +26 -0
  230. package/src/activities/chat/middleware/locks.ts +102 -0
  231. package/src/activities/chat/middleware/pending-turn.ts +47 -0
  232. package/src/activities/chat/middleware/run-disconnect.ts +62 -0
  233. package/src/activities/chat/middleware/run-store.ts +412 -0
  234. package/src/activities/chat/middleware/types.ts +62 -1
  235. package/src/activities/chat/stream/processor.ts +189 -5
  236. package/src/activities/chat/tools/approval-schema.ts +205 -0
  237. package/src/activities/chat/tools/tool-calls.ts +106 -13
  238. package/src/activities/chat/tools/tool-definition.ts +210 -39
  239. package/src/activities/generateAudio/index.ts +20 -3
  240. package/src/activities/generateImage/index.ts +20 -3
  241. package/src/activities/generateSpeech/index.ts +25 -3
  242. package/src/activities/generateTranscription/index.ts +26 -3
  243. package/src/activities/generateVideo/index.ts +345 -82
  244. package/src/activities/middleware/index.ts +2 -0
  245. package/src/activities/middleware/run.ts +31 -0
  246. package/src/activities/middleware/types.ts +49 -5
  247. package/src/activities/stream-generation-result.ts +30 -2
  248. package/src/activities/summarize/chat-stream-summarize.ts +5 -0
  249. package/src/activities/summarize/index.ts +200 -10
  250. package/src/adapter-internals.ts +10 -1
  251. package/src/client.ts +244 -0
  252. package/src/custom-events.ts +107 -0
  253. package/src/delivery-detach.ts +72 -0
  254. package/src/delivery-disconnect.ts +84 -0
  255. package/src/index.ts +138 -0
  256. package/src/interrupt-resume.ts +824 -0
  257. package/src/interrupt-serialization.ts +183 -0
  258. package/src/interrupts.ts +146 -0
  259. package/src/locks.ts +17 -0
  260. package/src/logger/types.ts +1 -1
  261. package/src/middlewares/otel.ts +1 -0
  262. package/src/realtime/index.ts +5 -9
  263. package/src/scope.ts +47 -0
  264. package/src/stream-durability.ts +598 -0
  265. package/src/stream-to-response.ts +1051 -95
  266. package/src/strip-to-spec-middleware.ts +3 -3
  267. package/src/types.ts +416 -24
  268. package/src/utilities/chat-params.ts +245 -55
  269. package/dist/esm/activities/index.js.map +0 -1
  270. package/dist/esm/adapter-internals.js.map +0 -1
  271. package/dist/esm/index.js.map +0 -1
  272. package/dist/esm/middlewares/index.js.map +0 -1
@@ -1,5 +1,6 @@
1
1
  import { normalizeToolResult } from '../../../utilities/tool-result'
2
2
  import { isStandardSchema, parseWithStandardSchema } from './schema-converter'
3
+ import type { ToolApprovalResolution } from '../../../interrupts'
3
4
  import type {
4
5
  AnyTool,
5
6
  ContentPart,
@@ -311,6 +312,13 @@ export class ToolCallManager<
311
312
 
312
313
  let toolResultContent: string | Array<ContentPart>
313
314
  let toolResultState: ToolOutputState | undefined
315
+ // Holds the parsed/validated execution output before serialization.
316
+ // Surfaced on the emitted `TOOL_CALL_END` event as `output` so
317
+ // consumers can read it typed (via `TypedStreamChunk` distribution
318
+ // over the tools array) without re-parsing `result`.
319
+ // Stays `undefined` when the tool has no `execute` (client-only
320
+ // tools) or when execution throws.
321
+ let toolOutput: unknown
314
322
  if (tool?.execute) {
315
323
  try {
316
324
  // Parse arguments (normalize null/non-object to {} for empty tool_use blocks)
@@ -369,6 +377,7 @@ export class ToolCallManager<
369
377
  }
370
378
  }
371
379
 
380
+ toolOutput = result
372
381
  toolResultContent = normalizeToolResult(result)
373
382
  } catch (error: unknown) {
374
383
  // If tool execution fails, add error message
@@ -390,9 +399,11 @@ export class ToolCallManager<
390
399
  toolName: toolCall.function.name,
391
400
  model: finishEvent.model,
392
401
  timestamp: Date.now(),
402
+ // Typed parsed output (undefined for failed exec / client-only tools).
403
+ ...(toolOutput !== undefined ? { output: toolOutput } : {}),
393
404
  result: toolResultContent,
394
405
  ...(toolResultState !== undefined && { state: toolResultState }),
395
- } as ToolCallEndEvent
406
+ }
396
407
 
397
408
  // Add tool result message
398
409
  toolResults.push({
@@ -420,6 +431,17 @@ export interface ToolResult {
420
431
  state?: 'output-available' | 'output-error'
421
432
  /** Duration of tool execution in milliseconds (only for server-executed tools) */
422
433
  duration?: number
434
+ /**
435
+ * Parsed tool input (after JSON parse + optional Standard Schema validation).
436
+ * Surfaced on engine-emitted `TOOL_CALL_END` events for TypedStreamChunk consumers.
437
+ */
438
+ input?: unknown
439
+ /**
440
+ * Parsed tool output before wire serialization. Surfaced on engine-emitted
441
+ * `TOOL_CALL_END` events so consumers can read typed `output` without
442
+ * re-parsing `result`. Undefined on error paths and when execution is skipped.
443
+ */
444
+ output?: unknown
423
445
  }
424
446
 
425
447
  export interface ApprovalRequest {
@@ -435,6 +457,36 @@ export interface ClientToolRequest {
435
457
  input: any
436
458
  }
437
459
 
460
+ export interface ToolResumeExecutionState {
461
+ deniedToolResults?: ReadonlyMap<string, unknown>
462
+ cancelledToolCallIds?: ReadonlySet<string>
463
+ }
464
+
465
+ function approvalResolution(
466
+ approvals: ReadonlyMap<string, ToolApprovalResolution>,
467
+ toolCallId: string,
468
+ ): ToolApprovalResolution | undefined {
469
+ return approvals.get(toolCallId) ?? approvals.get(`approval_${toolCallId}`)
470
+ }
471
+
472
+ function isApproved(resolution: ToolApprovalResolution): boolean {
473
+ return typeof resolution === 'boolean' ? resolution : resolution.approved
474
+ }
475
+
476
+ function editedApprovalArgs(
477
+ resolution: ToolApprovalResolution,
478
+ ): unknown | undefined {
479
+ return typeof resolution === 'object' && resolution.approved
480
+ ? resolution.editedArgs
481
+ : undefined
482
+ }
483
+
484
+ function deniedApprovalResult(resolution: ToolApprovalResolution): unknown {
485
+ return typeof resolution === 'object' && !resolution.approved
486
+ ? (resolution.payload ?? { error: 'User declined tool execution' })
487
+ : { error: 'User declined tool execution' }
488
+ }
489
+
438
490
  interface ExecuteToolCallsResult {
439
491
  /** Tool results ready to send to LLM */
440
492
  results: Array<ToolResult>
@@ -588,6 +640,8 @@ export async function* executeServerTool<TContext = unknown>(
588
640
  toolCallId: toolCall.id,
589
641
  toolName,
590
642
  result: finalResult,
643
+ input,
644
+ output: finalResult,
591
645
  duration,
592
646
  })
593
647
 
@@ -620,6 +674,7 @@ export async function* executeServerTool<TContext = unknown>(
620
674
  toolCallId: toolCall.id,
621
675
  toolName,
622
676
  result: { error: message },
677
+ input,
623
678
  state: 'output-error',
624
679
  duration,
625
680
  })
@@ -643,6 +698,7 @@ function buildClientToolResult(
643
698
  toolName: string,
644
699
  tool: AnyTool,
645
700
  rawResult: unknown,
701
+ input?: unknown,
646
702
  ): ToolResult {
647
703
  try {
648
704
  let result = rawResult
@@ -650,11 +706,14 @@ function buildClientToolResult(
650
706
  result = parseWithStandardSchema(tool.outputSchema, result)
651
707
  }
652
708
 
709
+ const parsed =
710
+ typeof result === 'string' ? safeJsonParse(result) : (result ?? null)
653
711
  return {
654
712
  toolCallId,
655
713
  toolName,
656
- result:
657
- typeof result === 'string' ? safeJsonParse(result) : (result ?? null),
714
+ result: parsed,
715
+ input,
716
+ output: parsed,
658
717
  }
659
718
  } catch (error: unknown) {
660
719
  const message = error instanceof Error ? error.message : 'Validation failed'
@@ -662,6 +721,7 @@ function buildClientToolResult(
662
721
  toolCallId,
663
722
  toolName,
664
723
  result: { error: message },
724
+ input,
665
725
  state: 'output-error',
666
726
  }
667
727
  }
@@ -678,14 +738,14 @@ function buildClientToolResult(
678
738
  *
679
739
  * @param toolCalls - Tool calls from the LLM
680
740
  * @param tools - Available tools with their configurations
681
- * @param approvals - Map of approval decisions (approval.id -> approved boolean)
741
+ * @param approvals - Map keyed by toolCallId (or `approval_${toolCallId}`) → ToolApprovalResolution
682
742
  * @param clientResults - Map of client-side execution results (toolCallId -> result)
683
743
  * @param createCustomEventChunk - Factory to create CustomEvent chunks (optional)
684
744
  */
685
745
  export async function* executeToolCalls<TContext = unknown>(
686
746
  toolCalls: Array<ToolCall>,
687
747
  tools: ReadonlyArray<AnyTool>,
688
- approvals: Map<string, boolean> = new Map(),
748
+ approvals: Map<string, ToolApprovalResolution> = new Map(),
689
749
  clientResults: Map<string, any> = new Map(),
690
750
  createCustomEventChunk?: (
691
751
  eventName: string,
@@ -694,6 +754,7 @@ export async function* executeToolCalls<TContext = unknown>(
694
754
  middlewareHooks?: ToolExecutionMiddlewareHooks,
695
755
  userContext?: TContext,
696
756
  abortSignal?: AbortSignal,
757
+ resumeState?: ToolResumeExecutionState,
697
758
  ): AsyncGenerator<CustomEvent, ExecuteToolCallsResult, void> {
698
759
  const results: Array<ToolResult> = []
699
760
  const needsApproval: Array<ApprovalRequest> = []
@@ -709,7 +770,11 @@ export async function* executeToolCalls<TContext = unknown>(
709
770
  // defer all execution so side effects don't happen before the user decides.
710
771
  const hasPendingApprovals = toolCalls.some((tc) => {
711
772
  const t = toolMap.get(tc.function.name)
712
- return t?.needsApproval && !approvals.has(`approval_${tc.id}`)
773
+ return (
774
+ t?.needsApproval &&
775
+ approvalResolution(approvals, tc.id) === undefined &&
776
+ !resumeState?.cancelledToolCallIds?.has(tc.id)
777
+ )
713
778
  })
714
779
 
715
780
  for (const toolCall of toolCalls) {
@@ -729,11 +794,25 @@ export async function* executeToolCalls<TContext = unknown>(
729
794
 
730
795
  // Skip non-pending tools while approvals are outstanding
731
796
  if (hasPendingApprovals) {
732
- if (!tool.needsApproval || approvals.has(`approval_${toolCall.id}`)) {
797
+ const isPendingApproval =
798
+ tool.needsApproval &&
799
+ approvalResolution(approvals, toolCall.id) === undefined
800
+ const isPlainClientRequest = !tool.needsApproval && !tool.execute
801
+ if (!isPendingApproval && !isPlainClientRequest) {
733
802
  continue
734
803
  }
735
804
  }
736
805
 
806
+ if (resumeState?.cancelledToolCallIds?.has(toolCall.id)) {
807
+ results.push({
808
+ toolCallId: toolCall.id,
809
+ toolName,
810
+ result: { error: 'Tool execution cancelled' },
811
+ state: 'output-error',
812
+ })
813
+ continue
814
+ }
815
+
737
816
  // Parse arguments, throwing error if invalid JSON
738
817
  let input: unknown = {}
739
818
  const argsStr = toolCall.function.arguments.trim() || '{}'
@@ -763,6 +842,8 @@ export async function* executeToolCalls<TContext = unknown>(
763
842
  result: {
764
843
  error: `Input validation failed for tool ${tool.name}: ${message}`,
765
844
  },
845
+ // raw parse may have failed validation — still attach best-effort input
846
+ input,
766
847
  state: 'output-error',
767
848
  })
768
849
  continue
@@ -792,12 +873,14 @@ export async function* executeToolCalls<TContext = unknown>(
792
873
  // Check if tool needs approval
793
874
  if (tool.needsApproval) {
794
875
  const approvalId = `approval_${toolCall.id}`
876
+ const resolution = approvalResolution(approvals, toolCall.id)
795
877
 
796
878
  // Check if approval decision exists
797
- if (approvals.has(approvalId)) {
798
- const approved = approvals.get(approvalId)
879
+ if (resolution !== undefined) {
880
+ const approved = isApproved(resolution)
799
881
 
800
882
  if (approved) {
883
+ input = editedApprovalArgs(resolution) ?? input
801
884
  // Approved - check if client has executed
802
885
  if (clientResults.has(toolCall.id)) {
803
886
  results.push(
@@ -806,6 +889,7 @@ export async function* executeToolCalls<TContext = unknown>(
806
889
  toolName,
807
890
  tool,
808
891
  clientResults.get(toolCall.id),
892
+ input,
809
893
  ),
810
894
  )
811
895
  } else {
@@ -821,7 +905,10 @@ export async function* executeToolCalls<TContext = unknown>(
821
905
  results.push({
822
906
  toolCallId: toolCall.id,
823
907
  toolName,
824
- result: { error: 'User declined tool execution' },
908
+ result:
909
+ resumeState?.deniedToolResults?.get(toolCall.id) ??
910
+ deniedApprovalResult(resolution),
911
+ input,
825
912
  state: 'output-error',
826
913
  })
827
914
  }
@@ -843,6 +930,7 @@ export async function* executeToolCalls<TContext = unknown>(
843
930
  toolName,
844
931
  tool,
845
932
  clientResults.get(toolCall.id),
933
+ input,
846
934
  ),
847
935
  )
848
936
  } else {
@@ -860,12 +948,14 @@ export async function* executeToolCalls<TContext = unknown>(
860
948
  // CASE 2: Server tool with approval required
861
949
  if (tool.needsApproval) {
862
950
  const approvalId = `approval_${toolCall.id}`
951
+ const resolution = approvalResolution(approvals, toolCall.id)
863
952
 
864
953
  // Check if approval decision exists
865
- if (approvals.has(approvalId)) {
866
- const approved = approvals.get(approvalId)
954
+ if (resolution !== undefined) {
955
+ const approved = isApproved(resolution)
867
956
 
868
957
  if (approved) {
958
+ input = editedApprovalArgs(resolution) ?? input
869
959
  // Apply middleware before-hook for approved tools
870
960
  if (middlewareHooks) {
871
961
  const decision = await applyBeforeToolCallDecision(
@@ -895,7 +985,10 @@ export async function* executeToolCalls<TContext = unknown>(
895
985
  results.push({
896
986
  toolCallId: toolCall.id,
897
987
  toolName,
898
- result: { error: 'User declined tool execution' },
988
+ result:
989
+ resumeState?.deniedToolResults?.get(toolCall.id) ??
990
+ deniedApprovalResult(resolution),
991
+ input,
899
992
  state: 'output-error',
900
993
  })
901
994
  }
@@ -1,36 +1,120 @@
1
- import type { StandardJSONSchemaV1 } from '@standard-schema/spec'
2
1
  import type {
2
+ InferSchemaType,
3
3
  JSONSchema,
4
4
  SchemaInput,
5
5
  Tool,
6
6
  ToolExecuteFunction,
7
7
  } from '../../../types'
8
+ import type {
9
+ StandardJSONSchemaV1,
10
+ StandardSchemaV1,
11
+ } from '@standard-schema/spec'
12
+
13
+ /**
14
+ * Type-level brand key for {@link ToolApprovalCapabilityMarker}. Only ever used
15
+ * in type positions, but it must stay exported: a `unique symbol` referenced by
16
+ * an exported interface has to be nameable in the emitted declarations.
17
+ *
18
+ * @public
19
+ */
20
+ export declare const toolApprovalCapability: unique symbol
21
+
22
+ export interface ToolApprovalCapabilityMarker<
23
+ TNeedsApproval extends boolean,
24
+ TApprovalSchema,
25
+ > {
26
+ readonly [toolApprovalCapability]?: {
27
+ needsApproval: TNeedsApproval
28
+ approvalSchema: TApprovalSchema
29
+ }
30
+ }
31
+
32
+ export type ApprovalSchemaConfig =
33
+ | SchemaInput
34
+ | { approve: SchemaInput; reject?: SchemaInput }
35
+ | { approve?: SchemaInput; reject: SchemaInput }
36
+
37
+ type ApprovalConfig<
38
+ TNeedsApproval extends boolean,
39
+ TApprovalSchema extends ApprovalSchemaConfig | undefined,
40
+ > = TNeedsApproval extends true
41
+ ? { needsApproval: TNeedsApproval; approvalSchema?: TApprovalSchema }
42
+ : { needsApproval?: TNeedsApproval; approvalSchema?: never }
43
+
44
+ export type ApprovalCapabilityOf<TTool> =
45
+ TTool extends ToolApprovalCapabilityMarker<infer TNeeds, unknown>
46
+ ? TNeeds
47
+ : false
48
+
49
+ export type ApprovalSchemaOf<TTool> =
50
+ TTool extends ToolApprovalCapabilityMarker<boolean, infer TSchema>
51
+ ? TSchema
52
+ : undefined
53
+
54
+ export declare const noSchema: unique symbol
55
+ export type NoSchema = typeof noSchema
56
+
57
+ export type InputSchemaOf<TTool> = TTool extends {
58
+ inputSchema: infer TInput
59
+ }
60
+ ? TInput extends undefined
61
+ ? NoSchema
62
+ : TInput
63
+ : NoSchema
64
+
65
+ export type OutputSchemaOf<TTool> = TTool extends {
66
+ outputSchema: infer TOutput
67
+ }
68
+ ? TOutput extends undefined
69
+ ? NoSchema
70
+ : TOutput
71
+ : NoSchema
72
+
73
+ type BuiltToolSchemaFields<
74
+ TInput extends SchemaInput | undefined,
75
+ TOutput extends SchemaInput | undefined,
76
+ TApprovalSchema extends ApprovalSchemaConfig | undefined,
77
+ > = {
78
+ inputSchema: TInput
79
+ outputSchema: TOutput
80
+ approvalSchema: TApprovalSchema
81
+ }
8
82
 
9
83
  /**
10
84
  * Marker type for server-side tools
11
85
  */
12
86
  export interface ServerTool<
13
- TInput extends SchemaInput = SchemaInput,
14
- TOutput extends SchemaInput = SchemaInput,
87
+ TInput extends SchemaInput | undefined = undefined,
88
+ TOutput extends SchemaInput | undefined = undefined,
15
89
  TName extends string = string,
16
90
  TContext = unknown,
17
- > extends Tool<TInput, TOutput, TName, TContext> {
91
+ TNeedsApproval extends boolean = false,
92
+ TApprovalSchema extends ApprovalSchemaConfig | undefined = undefined,
93
+ >
94
+ extends
95
+ Tool<TInput, TOutput, TName, TContext>,
96
+ ToolApprovalCapabilityMarker<TNeedsApproval, TApprovalSchema> {
18
97
  __toolSide: 'server'
98
+ inputSchema?: TInput
99
+ outputSchema?: TOutput
100
+ needsApproval?: TNeedsApproval
101
+ approvalSchema?: TApprovalSchema
19
102
  }
20
103
 
21
104
  /**
22
105
  * Marker type for client-side tools
23
106
  */
24
107
  export interface ClientTool<
25
- TInput extends SchemaInput = SchemaInput,
26
- TOutput extends SchemaInput = SchemaInput,
108
+ TInput extends SchemaInput | undefined = undefined,
109
+ TOutput extends SchemaInput | undefined = undefined,
27
110
  TName extends string = string,
28
111
  TContext = unknown,
29
112
  // Captured as a literal (`true` / `false`) so downstream types — notably
30
113
  // the tool-call part's `approval` field — can be gated on it. Defaults to
31
114
  // `false` when the tool config omits `needsApproval`.
32
115
  TNeedsApproval extends boolean = false,
33
- > {
116
+ TApprovalSchema extends ApprovalSchemaConfig | undefined = undefined,
117
+ > extends ToolApprovalCapabilityMarker<TNeedsApproval, TApprovalSchema> {
34
118
  __toolSide: 'client'
35
119
  name: TName
36
120
  description: string
@@ -42,35 +126,55 @@ export interface ClientTool<
42
126
  inputSchema?: TInput
43
127
  outputSchema?: TOutput
44
128
  needsApproval?: TNeedsApproval
129
+ approvalSchema?: TApprovalSchema
45
130
  lazy?: boolean
46
131
  metadata?: Record<string, unknown>
47
132
  execute?: ToolExecuteFunction<TInput, TOutput, TContext>
48
133
  }
49
134
 
135
+ /** Broad server-tool shape for heterogeneous internal collections. */
136
+ export type AnyServerTool = Omit<
137
+ ServerTool<any, any, string, any, boolean, any>,
138
+ 'execute'
139
+ > & {
140
+ execute?: ((args: any, context?: any) => any) | undefined
141
+ }
142
+
50
143
  /**
51
144
  * Tool definition that can be used directly or instantiated for server/client
52
145
  */
53
146
  export interface ToolDefinitionInstance<
54
- TInput extends SchemaInput = SchemaInput,
55
- TOutput extends SchemaInput = SchemaInput,
147
+ TInput extends SchemaInput | undefined = undefined,
148
+ TOutput extends SchemaInput | undefined = undefined,
56
149
  TName extends string = string,
57
150
  TContext = unknown,
58
151
  TNeedsApproval extends boolean = false,
152
+ TApprovalSchema extends ApprovalSchemaConfig | undefined = undefined,
59
153
  > extends Tool<TInput, TOutput, TName, TContext> {
60
154
  __toolSide: 'definition'
61
155
  // Narrow the base `needsApproval?: boolean` to the captured literal so it
62
156
  // survives into `ToolCallPartForTool`'s approval gate.
157
+ inputSchema: TInput
158
+ outputSchema: TOutput
63
159
  needsApproval?: TNeedsApproval
160
+ approvalSchema: TApprovalSchema
161
+ readonly [toolApprovalCapability]?: {
162
+ needsApproval: TNeedsApproval
163
+ approvalSchema: TApprovalSchema
164
+ }
64
165
  }
65
166
 
66
167
  /**
67
168
  * Union type for any kind of client-side tool (client tool or definition)
68
169
  */
69
170
  export type AnyClientTool =
70
- | (Omit<ClientTool<any, any, string, any, boolean>, 'execute'> & {
171
+ | (Omit<ClientTool<any, any, string, any, boolean, any>, 'execute'> & {
71
172
  execute?: ((args: any, context?: any) => any) | undefined
72
173
  })
73
- | (Omit<ToolDefinitionInstance<any, any, string, any, boolean>, 'execute'> & {
174
+ | (Omit<
175
+ ToolDefinitionInstance<any, any, string, any, boolean, any>,
176
+ 'execute'
177
+ > & {
74
178
  execute?: ((args: any, context?: any) => any) | undefined
75
179
  })
76
180
 
@@ -83,63 +187,73 @@ export type InferToolName<T> = T extends { name: infer N } ? N : never
83
187
  * Extract the input type from a tool (inferred from Standard JSON Schema, or `unknown` for plain JSONSchema)
84
188
  */
85
189
  export type InferToolInput<T> = T extends { inputSchema?: infer TInput }
86
- ? TInput extends StandardJSONSchemaV1<infer TInferred, unknown>
87
- ? TInferred
88
- : TInput extends JSONSchema
89
- ? unknown
90
- : unknown
190
+ ? TInput extends JSONSchema
191
+ ? unknown
192
+ : InferSchemaType<TInput>
91
193
  : unknown
92
194
 
93
195
  /**
94
196
  * Extract the output type from a tool (inferred from Standard JSON Schema, or `unknown` for plain JSONSchema)
95
197
  */
96
198
  export type InferToolOutput<T> = T extends { outputSchema?: infer TOutput }
97
- ? TOutput extends StandardJSONSchemaV1<infer TInferred, unknown>
98
- ? TInferred
99
- : TOutput extends JSONSchema
100
- ? unknown
101
- : unknown
199
+ ? TOutput extends StandardJSONSchemaV1<any, any>
200
+ ? InferSchemaType<TOutput>
201
+ : TOutput extends StandardSchemaV1<any, any>
202
+ ? InferSchemaType<TOutput>
203
+ : TOutput extends JSONSchema
204
+ ? unknown
205
+ : InferSchemaType<TOutput>
102
206
  : unknown
103
207
 
104
208
  /**
105
209
  * Tool definition configuration
106
210
  */
107
- export interface ToolDefinitionConfig<
108
- TInput extends SchemaInput = SchemaInput,
109
- TOutput extends SchemaInput = SchemaInput,
211
+ export type ToolDefinitionConfig<
212
+ TInput extends SchemaInput | undefined = undefined,
213
+ TOutput extends SchemaInput | undefined = undefined,
110
214
  TName extends string = string,
111
215
  TNeedsApproval extends boolean = false,
112
- > {
216
+ TApprovalSchema extends ApprovalSchemaConfig | undefined = undefined,
217
+ > = {
113
218
  name: TName
114
219
  description: string
115
220
  inputSchema?: TInput
116
221
  outputSchema?: TOutput
117
- needsApproval?: TNeedsApproval
118
222
  lazy?: boolean
119
223
  metadata?: Record<string, unknown>
120
- }
224
+ } & ApprovalConfig<TNeedsApproval, TApprovalSchema>
121
225
 
122
226
  /**
123
227
  * Tool definition builder that allows creating server or client tools from a shared definition
124
228
  */
125
229
  export interface ToolDefinition<
126
- TInput extends SchemaInput = SchemaInput,
127
- TOutput extends SchemaInput = SchemaInput,
230
+ TInput extends SchemaInput | undefined = undefined,
231
+ TOutput extends SchemaInput | undefined = undefined,
128
232
  TName extends string = string,
129
233
  TNeedsApproval extends boolean = false,
234
+ TApprovalSchema extends ApprovalSchemaConfig | undefined = undefined,
130
235
  > extends ToolDefinitionInstance<
131
236
  TInput,
132
237
  TOutput,
133
238
  TName,
134
239
  unknown,
135
- TNeedsApproval
240
+ TNeedsApproval,
241
+ TApprovalSchema
136
242
  > {
137
243
  /**
138
244
  * Create a server-side tool with execute function
139
245
  */
140
246
  server: <TContext = unknown>(
141
247
  execute: ToolExecuteFunction<TInput, TOutput, TContext>,
142
- ) => ServerTool<TInput, TOutput, TName, TContext>
248
+ ) => ServerTool<
249
+ TInput,
250
+ TOutput,
251
+ TName,
252
+ TContext,
253
+ TNeedsApproval,
254
+ TApprovalSchema
255
+ > &
256
+ BuiltToolSchemaFields<TInput, TOutput, TApprovalSchema>
143
257
 
144
258
  /**
145
259
  * Create a client-side tool with optional execute function.
@@ -148,7 +262,15 @@ export interface ToolDefinition<
148
262
  */
149
263
  client: <TContext = unknown>(
150
264
  execute?: ToolExecuteFunction<TInput, TOutput, TContext>,
151
- ) => ClientTool<TInput, TOutput, TName, TContext, TNeedsApproval>
265
+ ) => ClientTool<
266
+ TInput,
267
+ TOutput,
268
+ TName,
269
+ TContext,
270
+ TNeedsApproval,
271
+ TApprovalSchema
272
+ > &
273
+ BuiltToolSchemaFields<TInput, TOutput, TApprovalSchema>
152
274
  }
153
275
 
154
276
  /**
@@ -207,35 +329,84 @@ export interface ToolDefinition<
207
329
  * ```
208
330
  */
209
331
  export function toolDefinition<
210
- TInput extends SchemaInput = SchemaInput,
211
- TOutput extends SchemaInput = SchemaInput,
332
+ TInput extends SchemaInput | undefined = undefined,
333
+ TOutput extends SchemaInput | undefined = undefined,
212
334
  TName extends string = string,
213
335
  // `const` forces the literal (`true` / `false`) to be captured from the
214
336
  // config's optional `needsApproval` — without it TS widens to `boolean`,
215
337
  // which collapses the approval gate in `ToolCallPartForTool`.
216
338
  const TNeedsApproval extends boolean = false,
339
+ TApprovalSchema extends ApprovalSchemaConfig | undefined = undefined,
217
340
  >(
218
- config: ToolDefinitionConfig<TInput, TOutput, TName, TNeedsApproval>,
219
- ): ToolDefinition<TInput, TOutput, TName, TNeedsApproval> {
220
- const definition: ToolDefinition<TInput, TOutput, TName, TNeedsApproval> = {
341
+ config: ToolDefinitionConfig<
342
+ TInput,
343
+ TOutput,
344
+ TName,
345
+ TNeedsApproval,
346
+ TApprovalSchema
347
+ >,
348
+ ): ToolDefinition<TInput, TOutput, TName, TNeedsApproval, TApprovalSchema> {
349
+ if (config.approvalSchema !== undefined && config.needsApproval !== true) {
350
+ throw new TypeError('approvalSchema requires needsApproval: true.')
351
+ }
352
+ const inputSchema = config.inputSchema as TInput
353
+ const outputSchema = config.outputSchema as TOutput
354
+ const approvalSchema = config.approvalSchema as TApprovalSchema
355
+ const needsApproval = config.needsApproval as TNeedsApproval | undefined
356
+
357
+ const definition: ToolDefinition<
358
+ TInput,
359
+ TOutput,
360
+ TName,
361
+ TNeedsApproval,
362
+ TApprovalSchema
363
+ > = {
221
364
  __toolSide: 'definition',
222
365
  ...config,
366
+ inputSchema,
367
+ outputSchema,
368
+ approvalSchema,
369
+ needsApproval,
223
370
  server<TContext = unknown>(
224
371
  execute: ToolExecuteFunction<TInput, TOutput, TContext>,
225
- ): ServerTool<TInput, TOutput, TName, TContext> {
372
+ ): ServerTool<
373
+ TInput,
374
+ TOutput,
375
+ TName,
376
+ TContext,
377
+ TNeedsApproval,
378
+ TApprovalSchema
379
+ > &
380
+ BuiltToolSchemaFields<TInput, TOutput, TApprovalSchema> {
226
381
  return {
227
382
  __toolSide: 'server',
228
383
  ...config,
384
+ inputSchema,
385
+ outputSchema,
386
+ approvalSchema,
387
+ needsApproval,
229
388
  execute,
230
389
  }
231
390
  },
232
391
 
233
392
  client<TContext = unknown>(
234
393
  execute?: ToolExecuteFunction<TInput, TOutput, TContext>,
235
- ): ClientTool<TInput, TOutput, TName, TContext, TNeedsApproval> {
394
+ ): ClientTool<
395
+ TInput,
396
+ TOutput,
397
+ TName,
398
+ TContext,
399
+ TNeedsApproval,
400
+ TApprovalSchema
401
+ > &
402
+ BuiltToolSchemaFields<TInput, TOutput, TApprovalSchema> {
236
403
  return {
237
404
  __toolSide: 'client',
238
405
  ...config,
406
+ inputSchema,
407
+ outputSchema,
408
+ approvalSchema,
409
+ needsApproval,
239
410
  ...(execute !== undefined && { execute }),
240
411
  }
241
412
  },