@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,107 +1,93 @@
1
+ //#region src/utilities/ag-ui-wire.ts
2
+ /**
3
+ * Serialize TanStack `UIMessage`s into the AG-UI `RunAgentInput.messages`
4
+ * wire shape. Each anchor (system/user/assistant) carries the canonical
5
+ * `parts` array verbatim plus AG-UI mirror fields (`content`, `toolCalls`)
6
+ * so AG-UI Zod parsing succeeds. Tool results and thinking parts on
7
+ * assistant messages are additionally emitted as fan-out
8
+ * `{role:'tool',...}` and `{role:'reasoning',...}` entries for strict
9
+ * AG-UI server consumers.
10
+ */
1
11
  function uiMessagesToWire(messages) {
2
- const wire = [];
3
- for (const msg of messages) {
4
- const parts = (
5
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion -- runtime input may be ModelMessage-shaped (no `parts`); cast forces the optional-chain fallback below to remain in scope
6
- msg.parts ?? []
7
- );
8
- if (msg.role === "system") {
9
- wire.push({
10
- ...msg,
11
- content: parts.length > 0 ? collectText(parts) : msg.content ?? ""
12
- });
13
- continue;
14
- }
15
- if (msg.role === "user") {
16
- wire.push({
17
- ...msg,
18
- content: parts.length > 0 ? collectUserContent(parts) : msg.content ?? ""
19
- });
20
- continue;
21
- }
22
- for (const part of parts) {
23
- if (part.type === "thinking") {
24
- wire.push({
25
- role: "reasoning",
26
- id: deriveReasoningId(msg.id, part),
27
- content: part.content
28
- });
29
- }
30
- }
31
- const text = collectText(parts);
32
- const toolCalls = collectToolCalls(parts);
33
- wire.push({
34
- ...msg,
35
- ...text !== "" && { content: text },
36
- ...toolCalls && { toolCalls }
37
- });
38
- for (const part of parts) {
39
- if (part.type === "tool-result") {
40
- wire.push({
41
- role: "tool",
42
- id: deriveToolMessageId(part.toolCallId),
43
- toolCallId: part.toolCallId,
44
- content: typeof part.content === "string" ? part.content : JSON.stringify(part.content),
45
- ...part.error !== void 0 && { error: part.error }
46
- });
47
- }
48
- }
49
- }
50
- return wire;
12
+ const wire = [];
13
+ for (const msg of messages) {
14
+ const parts = msg.parts ?? [];
15
+ if (msg.role === "system") {
16
+ wire.push({
17
+ ...msg,
18
+ content: parts.length > 0 ? collectText(parts) : msg.content ?? ""
19
+ });
20
+ continue;
21
+ }
22
+ if (msg.role === "user") {
23
+ wire.push({
24
+ ...msg,
25
+ content: parts.length > 0 ? collectUserContent(parts) : msg.content ?? ""
26
+ });
27
+ continue;
28
+ }
29
+ for (const part of parts) if (part.type === "thinking") wire.push({
30
+ role: "reasoning",
31
+ id: deriveReasoningId(msg.id, part),
32
+ content: part.content
33
+ });
34
+ const text = collectText(parts);
35
+ const toolCalls = collectToolCalls(parts);
36
+ wire.push({
37
+ ...msg,
38
+ ...text !== "" && { content: text },
39
+ ...toolCalls && { toolCalls }
40
+ });
41
+ for (const part of parts) if (part.type === "tool-result") wire.push({
42
+ role: "tool",
43
+ id: deriveToolMessageId(part.toolCallId),
44
+ toolCallId: part.toolCallId,
45
+ content: typeof part.content === "string" ? part.content : JSON.stringify(part.content),
46
+ ...part.error !== void 0 && { error: part.error }
47
+ });
48
+ }
49
+ return wire;
51
50
  }
52
51
  function collectText(parts) {
53
- const out = [];
54
- for (const p of parts) {
55
- if (p.type === "text") {
56
- out.push(p.content);
57
- } else if (p.type === "structured-output" && p.status === "complete" && p.raw !== "") {
58
- out.push(p.raw);
59
- }
60
- }
61
- return out.join("");
52
+ const out = [];
53
+ for (const p of parts) if (p.type === "text") out.push(p.content);
54
+ else if (p.type === "structured-output" && p.status === "complete" && p.raw !== "") out.push(p.raw);
55
+ return out.join("");
62
56
  }
63
57
  function collectUserContent(parts) {
64
- const hasMultimodal = parts.some(
65
- (p) => p.type === "image" || p.type === "audio" || p.type === "video" || p.type === "document"
66
- );
67
- if (!hasMultimodal) {
68
- return collectText(parts);
69
- }
70
- const out = [];
71
- for (const p of parts) {
72
- if (p.type === "text") {
73
- out.push({ type: "text", text: p.content });
74
- } else if (p.type === "image" || p.type === "audio" || p.type === "video" || p.type === "document") {
75
- out.push(p);
76
- }
77
- }
78
- return out;
58
+ if (!parts.some((p) => p.type === "image" || p.type === "audio" || p.type === "video" || p.type === "document")) return collectText(parts);
59
+ const out = [];
60
+ for (const p of parts) if (p.type === "text") out.push({
61
+ type: "text",
62
+ text: p.content
63
+ });
64
+ else if (p.type === "image" || p.type === "audio" || p.type === "video" || p.type === "document") out.push(p);
65
+ return out;
79
66
  }
80
67
  function collectToolCalls(parts) {
81
- const calls = [];
82
- for (const p of parts) {
83
- if (p.type === "tool-call") {
84
- calls.push({
85
- id: p.id,
86
- type: "function",
87
- function: { name: p.name, arguments: p.arguments }
88
- });
89
- }
90
- }
91
- return calls.length > 0 ? calls : void 0;
68
+ const calls = [];
69
+ for (const p of parts) if (p.type === "tool-call") calls.push({
70
+ id: p.id,
71
+ type: "function",
72
+ function: {
73
+ name: p.name,
74
+ arguments: p.arguments
75
+ }
76
+ });
77
+ return calls.length > 0 ? calls : void 0;
92
78
  }
93
79
  function deriveReasoningId(messageId, part) {
94
- return `${messageId}-reasoning-${part.id ?? hashContent(part.content)}`;
80
+ return `${messageId}-reasoning-${part.id ?? hashContent(part.content)}`;
95
81
  }
96
82
  function deriveToolMessageId(toolCallId) {
97
- return `tool-${toolCallId}`;
83
+ return `tool-${toolCallId}`;
98
84
  }
99
85
  function hashContent(s) {
100
- let h = 0;
101
- for (let i = 0; i < s.length; i++) h = h * 31 + s.charCodeAt(i) | 0;
102
- return Math.abs(h).toString(36);
86
+ let h = 0;
87
+ for (let i = 0; i < s.length; i++) h = h * 31 + s.charCodeAt(i) | 0;
88
+ return Math.abs(h).toString(36);
103
89
  }
104
- export {
105
- uiMessagesToWire
106
- };
107
- //# sourceMappingURL=ag-ui-wire.js.map
90
+ //#endregion
91
+ export { uiMessagesToWire };
92
+
93
+ //# sourceMappingURL=ag-ui-wire.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ag-ui-wire.js","sources":["../../../src/utilities/ag-ui-wire.ts"],"sourcesContent":["import type { ContentPart, MessagePart, UIMessage } from '../types'\n\ntype AGUITextInputContent = { type: 'text'; text: string }\ntype AGUIInputContent =\n | AGUITextInputContent\n | (ContentPart & { type: 'image' | 'audio' | 'video' | 'document' })\n\ntype AGUIToolCallMirror = {\n id: string\n type: 'function'\n function: { name: string; arguments: string }\n}\n\ntype AGUIToolMessage = {\n role: 'tool'\n id: string\n toolCallId: string\n content: string\n error?: string\n}\n\ntype AGUIReasoningMessage = {\n role: 'reasoning'\n id: string\n content: string\n}\n\ntype WireAnchorMessage = UIMessage & {\n content?: string | Array<AGUIInputContent>\n toolCalls?: Array<AGUIToolCallMirror>\n}\n\nexport type WireMessage =\n | WireAnchorMessage\n | AGUIToolMessage\n | AGUIReasoningMessage\n\n/**\n * Serialize TanStack `UIMessage`s into the AG-UI `RunAgentInput.messages`\n * wire shape. Each anchor (system/user/assistant) carries the canonical\n * `parts` array verbatim plus AG-UI mirror fields (`content`, `toolCalls`)\n * so AG-UI Zod parsing succeeds. Tool results and thinking parts on\n * assistant messages are additionally emitted as fan-out\n * `{role:'tool',...}` and `{role:'reasoning',...}` entries for strict\n * AG-UI server consumers.\n */\nexport function uiMessagesToWire(\n messages: Array<UIMessage>,\n): Array<WireMessage> {\n const wire: Array<WireMessage> = []\n\n for (const msg of messages) {\n // Defensive: if parts is missing (ModelMessage-shaped input), pass through as-is.\n // UIMessage always has parts; ModelMessage uses content directly.\n const parts: ReadonlyArray<MessagePart> =\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion -- runtime input may be ModelMessage-shaped (no `parts`); cast forces the optional-chain fallback below to remain in scope\n (msg.parts as ReadonlyArray<MessagePart> | undefined) ?? []\n\n if (msg.role === 'system') {\n wire.push({\n ...msg,\n content:\n parts.length > 0\n ? collectText(parts)\n : ((msg as { content?: string }).content ?? ''),\n })\n continue\n }\n\n if (msg.role === 'user') {\n wire.push({\n ...msg,\n content:\n parts.length > 0\n ? collectUserContent(parts)\n : ((msg as { content?: string }).content ?? ''),\n })\n continue\n }\n\n // assistant: emit reasoning fan-outs first, then anchor, then tool fan-outs\n for (const part of parts) {\n if (part.type === 'thinking') {\n wire.push({\n role: 'reasoning',\n id: deriveReasoningId(msg.id, part),\n content: part.content,\n })\n }\n }\n\n const text = collectText(parts)\n const toolCalls = collectToolCalls(parts)\n wire.push({\n ...msg,\n ...(text !== '' && { content: text }),\n ...(toolCalls && { toolCalls }),\n })\n\n for (const part of parts) {\n if (part.type === 'tool-result') {\n wire.push({\n role: 'tool',\n id: deriveToolMessageId(part.toolCallId),\n toolCallId: part.toolCallId,\n content:\n typeof part.content === 'string'\n ? part.content\n : JSON.stringify(part.content),\n ...(part.error !== undefined && { error: part.error }),\n })\n }\n }\n }\n\n return wire\n}\n\nfunction collectText(parts: ReadonlyArray<MessagePart>): string {\n // The streamed JSON of a completed structured-output part is the source of\n // truth for multi-turn coherence — emitting it back as assistant content\n // lets the LLM see its own prior structured response. Streaming/errored\n // parts are skipped: they'd ship malformed JSON fragments and confuse the\n // model. `completeStructuredOutputPart` tries hard to populate `raw`\n // (caller → existing buffer → `JSON.stringify(data)`), but the stringify\n // fallback can leave it empty when `data` is unserializable (BigInt,\n // circular). The `p.raw !== ''` guard below is what enforces \"no malformed\n // round-trip\" in that case — without it we'd ship `''` and the model would\n // see an empty assistant turn.\n const out: Array<string> = []\n for (const p of parts) {\n if (p.type === 'text') {\n out.push(p.content)\n } else if (\n p.type === 'structured-output' &&\n p.status === 'complete' &&\n p.raw !== ''\n ) {\n out.push(p.raw)\n }\n }\n return out.join('')\n}\n\nfunction collectUserContent(\n parts: ReadonlyArray<MessagePart>,\n): string | Array<AGUIInputContent> {\n const hasMultimodal = parts.some(\n (p) =>\n p.type === 'image' ||\n p.type === 'audio' ||\n p.type === 'video' ||\n p.type === 'document',\n )\n if (!hasMultimodal) {\n return collectText(parts)\n }\n const out: Array<AGUIInputContent> = []\n for (const p of parts) {\n if (p.type === 'text') {\n out.push({ type: 'text', text: p.content })\n } else if (\n p.type === 'image' ||\n p.type === 'audio' ||\n p.type === 'video' ||\n p.type === 'document'\n ) {\n out.push(p)\n }\n }\n return out\n}\n\nfunction collectToolCalls(\n parts: ReadonlyArray<MessagePart>,\n): Array<AGUIToolCallMirror> | undefined {\n const calls: Array<AGUIToolCallMirror> = []\n for (const p of parts) {\n if (p.type === 'tool-call') {\n calls.push({\n id: p.id,\n type: 'function',\n function: { name: p.name, arguments: p.arguments },\n })\n }\n }\n return calls.length > 0 ? calls : undefined\n}\n\nfunction deriveReasoningId(messageId: string, part: MessagePart): string {\n return `${messageId}-reasoning-${(part as { id?: string }).id ?? hashContent((part as { content: string }).content)}`\n}\n\nfunction deriveToolMessageId(toolCallId: string): string {\n return `tool-${toolCallId}`\n}\n\nfunction hashContent(s: string): string {\n // Cheap deterministic id suffix; collisions are tolerable since\n // reasoning ids only matter for AG-UI server consumers, not for our\n // own server's dedup logic (which keys on toolCallId, not reasoning id).\n let h = 0\n for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) | 0\n return Math.abs(h).toString(36)\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ag-ui-wire.js","names":[],"sources":["../../../src/utilities/ag-ui-wire.ts"],"sourcesContent":["import type { ContentPart, MessagePart, UIMessage } from '../types'\n\ntype AGUITextInputContent = { type: 'text'; text: string }\ntype AGUIInputContent =\n | AGUITextInputContent\n | (ContentPart & { type: 'image' | 'audio' | 'video' | 'document' })\n\ntype AGUIToolCallMirror = {\n id: string\n type: 'function'\n function: { name: string; arguments: string }\n}\n\ntype AGUIToolMessage = {\n role: 'tool'\n id: string\n toolCallId: string\n content: string\n error?: string\n}\n\ntype AGUIReasoningMessage = {\n role: 'reasoning'\n id: string\n content: string\n}\n\ntype WireAnchorMessage = UIMessage & {\n content?: string | Array<AGUIInputContent>\n toolCalls?: Array<AGUIToolCallMirror>\n}\n\nexport type WireMessage =\n | WireAnchorMessage\n | AGUIToolMessage\n | AGUIReasoningMessage\n\n/**\n * Serialize TanStack `UIMessage`s into the AG-UI `RunAgentInput.messages`\n * wire shape. Each anchor (system/user/assistant) carries the canonical\n * `parts` array verbatim plus AG-UI mirror fields (`content`, `toolCalls`)\n * so AG-UI Zod parsing succeeds. Tool results and thinking parts on\n * assistant messages are additionally emitted as fan-out\n * `{role:'tool',...}` and `{role:'reasoning',...}` entries for strict\n * AG-UI server consumers.\n */\nexport function uiMessagesToWire(\n messages: Array<UIMessage>,\n): Array<WireMessage> {\n const wire: Array<WireMessage> = []\n\n for (const msg of messages) {\n // Defensive: if parts is missing (ModelMessage-shaped input), pass through as-is.\n // UIMessage always has parts; ModelMessage uses content directly.\n const parts: ReadonlyArray<MessagePart> =\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion -- runtime input may be ModelMessage-shaped (no `parts`); cast forces the optional-chain fallback below to remain in scope\n (msg.parts as ReadonlyArray<MessagePart> | undefined) ?? []\n\n if (msg.role === 'system') {\n wire.push({\n ...msg,\n content:\n parts.length > 0\n ? collectText(parts)\n : ((msg as { content?: string }).content ?? ''),\n })\n continue\n }\n\n if (msg.role === 'user') {\n wire.push({\n ...msg,\n content:\n parts.length > 0\n ? collectUserContent(parts)\n : ((msg as { content?: string }).content ?? ''),\n })\n continue\n }\n\n // assistant: emit reasoning fan-outs first, then anchor, then tool fan-outs\n for (const part of parts) {\n if (part.type === 'thinking') {\n wire.push({\n role: 'reasoning',\n id: deriveReasoningId(msg.id, part),\n content: part.content,\n })\n }\n }\n\n const text = collectText(parts)\n const toolCalls = collectToolCalls(parts)\n wire.push({\n ...msg,\n ...(text !== '' && { content: text }),\n ...(toolCalls && { toolCalls }),\n })\n\n for (const part of parts) {\n if (part.type === 'tool-result') {\n wire.push({\n role: 'tool',\n id: deriveToolMessageId(part.toolCallId),\n toolCallId: part.toolCallId,\n content:\n typeof part.content === 'string'\n ? part.content\n : JSON.stringify(part.content),\n ...(part.error !== undefined && { error: part.error }),\n })\n }\n }\n }\n\n return wire\n}\n\nfunction collectText(parts: ReadonlyArray<MessagePart>): string {\n // The streamed JSON of a completed structured-output part is the source of\n // truth for multi-turn coherence — emitting it back as assistant content\n // lets the LLM see its own prior structured response. Streaming/errored\n // parts are skipped: they'd ship malformed JSON fragments and confuse the\n // model. `completeStructuredOutputPart` tries hard to populate `raw`\n // (caller → existing buffer → `JSON.stringify(data)`), but the stringify\n // fallback can leave it empty when `data` is unserializable (BigInt,\n // circular). The `p.raw !== ''` guard below is what enforces \"no malformed\n // round-trip\" in that case — without it we'd ship `''` and the model would\n // see an empty assistant turn.\n const out: Array<string> = []\n for (const p of parts) {\n if (p.type === 'text') {\n out.push(p.content)\n } else if (\n p.type === 'structured-output' &&\n p.status === 'complete' &&\n p.raw !== ''\n ) {\n out.push(p.raw)\n }\n }\n return out.join('')\n}\n\nfunction collectUserContent(\n parts: ReadonlyArray<MessagePart>,\n): string | Array<AGUIInputContent> {\n const hasMultimodal = parts.some(\n (p) =>\n p.type === 'image' ||\n p.type === 'audio' ||\n p.type === 'video' ||\n p.type === 'document',\n )\n if (!hasMultimodal) {\n return collectText(parts)\n }\n const out: Array<AGUIInputContent> = []\n for (const p of parts) {\n if (p.type === 'text') {\n out.push({ type: 'text', text: p.content })\n } else if (\n p.type === 'image' ||\n p.type === 'audio' ||\n p.type === 'video' ||\n p.type === 'document'\n ) {\n out.push(p)\n }\n }\n return out\n}\n\nfunction collectToolCalls(\n parts: ReadonlyArray<MessagePart>,\n): Array<AGUIToolCallMirror> | undefined {\n const calls: Array<AGUIToolCallMirror> = []\n for (const p of parts) {\n if (p.type === 'tool-call') {\n calls.push({\n id: p.id,\n type: 'function',\n function: { name: p.name, arguments: p.arguments },\n })\n }\n }\n return calls.length > 0 ? calls : undefined\n}\n\nfunction deriveReasoningId(messageId: string, part: MessagePart): string {\n return `${messageId}-reasoning-${(part as { id?: string }).id ?? hashContent((part as { content: string }).content)}`\n}\n\nfunction deriveToolMessageId(toolCallId: string): string {\n return `tool-${toolCallId}`\n}\n\nfunction hashContent(s: string): string {\n // Cheap deterministic id suffix; collisions are tolerable since\n // reasoning ids only matter for AG-UI server consumers, not for our\n // own server's dedup logic (which keys on toolCallId, not reasoning id).\n let h = 0\n for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) | 0\n return Math.abs(h).toString(36)\n}\n"],"mappings":";;;;;;;;;;AA8CA,SAAgB,iBACd,UACoB;CACpB,MAAM,OAA2B,CAAC;CAElC,KAAK,MAAM,OAAO,UAAU;EAG1B,MAAM,QAEH,IAAI,SAAoD,CAAC;EAE5D,IAAI,IAAI,SAAS,UAAU;GACzB,KAAK,KAAK;IACR,GAAG;IACH,SACE,MAAM,SAAS,IACX,YAAY,KAAK,IACf,IAA6B,WAAW;GAClD,CAAC;GACD;EACF;EAEA,IAAI,IAAI,SAAS,QAAQ;GACvB,KAAK,KAAK;IACR,GAAG;IACH,SACE,MAAM,SAAS,IACX,mBAAmB,KAAK,IACtB,IAA6B,WAAW;GAClD,CAAC;GACD;EACF;EAGA,KAAK,MAAM,QAAQ,OACjB,IAAI,KAAK,SAAS,YAChB,KAAK,KAAK;GACR,MAAM;GACN,IAAI,kBAAkB,IAAI,IAAI,IAAI;GAClC,SAAS,KAAK;EAChB,CAAC;EAIL,MAAM,OAAO,YAAY,KAAK;EAC9B,MAAM,YAAY,iBAAiB,KAAK;EACxC,KAAK,KAAK;GACR,GAAG;GACH,GAAI,SAAS,MAAM,EAAE,SAAS,KAAK;GACnC,GAAI,aAAa,EAAE,UAAU;EAC/B,CAAC;EAED,KAAK,MAAM,QAAQ,OACjB,IAAI,KAAK,SAAS,eAChB,KAAK,KAAK;GACR,MAAM;GACN,IAAI,oBAAoB,KAAK,UAAU;GACvC,YAAY,KAAK;GACjB,SACE,OAAO,KAAK,YAAY,WACpB,KAAK,UACL,KAAK,UAAU,KAAK,OAAO;GACjC,GAAI,KAAK,UAAU,KAAA,KAAa,EAAE,OAAO,KAAK,MAAM;EACtD,CAAC;CAGP;CAEA,OAAO;AACT;AAEA,SAAS,YAAY,OAA2C;CAW9D,MAAM,MAAqB,CAAC;CAC5B,KAAK,MAAM,KAAK,OACd,IAAI,EAAE,SAAS,QACb,IAAI,KAAK,EAAE,OAAO;MACb,IACL,EAAE,SAAS,uBACX,EAAE,WAAW,cACb,EAAE,QAAQ,IAEV,IAAI,KAAK,EAAE,GAAG;CAGlB,OAAO,IAAI,KAAK,EAAE;AACpB;AAEA,SAAS,mBACP,OACkC;CAQlC,IAAI,CAPkB,MAAM,MACzB,MACC,EAAE,SAAS,WACX,EAAE,SAAS,WACX,EAAE,SAAS,WACX,EAAE,SAAS,UAEV,GACH,OAAO,YAAY,KAAK;CAE1B,MAAM,MAA+B,CAAC;CACtC,KAAK,MAAM,KAAK,OACd,IAAI,EAAE,SAAS,QACb,IAAI,KAAK;EAAE,MAAM;EAAQ,MAAM,EAAE;CAAQ,CAAC;MACrC,IACL,EAAE,SAAS,WACX,EAAE,SAAS,WACX,EAAE,SAAS,WACX,EAAE,SAAS,YAEX,IAAI,KAAK,CAAC;CAGd,OAAO;AACT;AAEA,SAAS,iBACP,OACuC;CACvC,MAAM,QAAmC,CAAC;CAC1C,KAAK,MAAM,KAAK,OACd,IAAI,EAAE,SAAS,aACb,MAAM,KAAK;EACT,IAAI,EAAE;EACN,MAAM;EACN,UAAU;GAAE,MAAM,EAAE;GAAM,WAAW,EAAE;EAAU;CACnD,CAAC;CAGL,OAAO,MAAM,SAAS,IAAI,QAAQ,KAAA;AACpC;AAEA,SAAS,kBAAkB,WAAmB,MAA2B;CACvE,OAAO,GAAG,UAAU,aAAc,KAAyB,MAAM,YAAa,KAA6B,OAAO;AACpH;AAEA,SAAS,oBAAoB,YAA4B;CACvD,OAAO,QAAQ;AACjB;AAEA,SAAS,YAAY,GAAmB;CAItC,IAAI,IAAI;CACR,KAAK,IAAI,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK,IAAK,IAAI,KAAK,EAAE,WAAW,CAAC,IAAK;CACpE,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,EAAE;AAChC"}
@@ -1,5 +1,5 @@
1
1
  import { Context as AGUIContext } from '@ag-ui/core';
2
- import { JSONSchema, ModelMessage, SchemaInput, Tool, UIMessage } from '../types.js';
2
+ import { AnyTool, JSONSchema, ModelMessage, RunAgentResumeItem, UIMessage } from '../types.js';
3
3
  /**
4
4
  * Parse and validate an HTTP request body as an AG-UI `RunAgentInput`.
5
5
  *
@@ -8,8 +8,11 @@ import { JSONSchema, ModelMessage, SchemaInput, Tool, UIMessage } from '../types
8
8
  * `convertMessagesToModelMessages` handles AG-UI fan-out dedup and
9
9
  * reasoning/activity/developer-role normalization internally.
10
10
  *
11
+ * Validated structurally against the AG-UI `RunAgentInput` contract without a
12
+ * schema library, so this package pulls in no validation runtime of its own.
13
+ *
11
14
  * @throws An error with a migration-pointing message when the body does
12
- * not conform to AG-UI 0.0.52 `RunAgentInputSchema`. Surface this as a
15
+ * not conform to AG-UI `RunAgentInput`. Surface this as a
13
16
  * 400 Bad Request to the client.
14
17
  */
15
18
  export declare function chatParamsFromRequestBody(body: unknown): Promise<{
@@ -24,6 +27,7 @@ export declare function chatParamsFromRequestBody(body: unknown): Promise<{
24
27
  }>;
25
28
  forwardedProps: Record<string, unknown>;
26
29
  state: unknown;
30
+ resume?: Array<RunAgentResumeItem>;
27
31
  /**
28
32
  * @deprecated Use `aguiContext` instead. This alias will be removed in a
29
33
  * future release.
@@ -57,6 +61,17 @@ export declare function chatParamsFromRequestBody(body: unknown): Promise<{
57
61
  * @throws {Response} 400 on malformed JSON or invalid AG-UI shape.
58
62
  */
59
63
  export declare function chatParamsFromRequest(req: Request): Promise<Awaited<ReturnType<typeof chatParamsFromRequestBody>>>;
64
+ /**
65
+ * Client-declared tool stub (no execute). `name` is `string`, so arrays that
66
+ * include these stubs intentionally widen `TypedStreamChunk` tool-name
67
+ * discrimination — pass server tools alone when you need a closed name union.
68
+ */
69
+ export type ClientToolDeclaration = {
70
+ name: string;
71
+ description: string;
72
+ inputSchema: JSONSchema;
73
+ };
74
+ export type MergedAgentTools<TServerTools extends ReadonlyArray<AnyTool>> = ReadonlyArray<TServerTools[number] | ClientToolDeclaration>;
60
75
  /**
61
76
  * Merge a server-side tool array with the AG-UI client-declared tools
62
77
  * received in the request body.
@@ -72,14 +87,21 @@ export declare function chatParamsFromRequest(req: Request): Promise<Awaited<Ret
72
87
  * them — server emits a tool-call request, client executes via its
73
88
  * registered handler, client posts back the result.
74
89
  *
90
+ * Typing:
91
+ * - Empty `clientTools` preserves the server tuple (closed name union).
92
+ * - Non-empty `clientTools` returns a widened array that honestly includes
93
+ * client stubs, so `TypedStreamChunk` does not claim a closed server-only
94
+ * name union.
95
+ *
75
96
  * @param serverTools - The server's tool array (e.g. from
76
97
  * `[myToolDef.server(...)]`). Pass directly to `chat({ tools })`.
77
98
  * @param clientTools - The `tools` array received from
78
99
  * `chatParamsFromRequest(...)` / `chatParamsFromRequestBody(...)`.
79
100
  * @returns A merged array suitable for `chat({ tools })`.
80
101
  */
81
- export declare function mergeAgentTools<TContext = unknown>(serverTools: ReadonlyArray<Tool<SchemaInput, SchemaInput, string, TContext>>, clientTools: ReadonlyArray<{
102
+ export declare function mergeAgentTools<const TServerTools extends ReadonlyArray<AnyTool>>(serverTools: TServerTools, clientTools: readonly []): TServerTools;
103
+ export declare function mergeAgentTools<const TServerTools extends ReadonlyArray<AnyTool>>(serverTools: TServerTools, clientTools: ReadonlyArray<{
82
104
  name: string;
83
105
  description: string;
84
106
  parameters: JSONSchema;
85
- }>): Array<Tool<SchemaInput, SchemaInput, string, TContext>>;
107
+ }>): MergedAgentTools<TServerTools>;
@@ -1,100 +1,226 @@
1
- import { RunAgentInputSchema, AGUIError } from "@ag-ui/core";
2
- const KNOWN_PART_TYPES = /* @__PURE__ */ new Set([
3
- "text",
4
- "image",
5
- "audio",
6
- "video",
7
- "document",
8
- "tool-call",
9
- "tool-result",
10
- "thinking"
1
+ import { AGUIError } from "@ag-ui/core";
2
+ //#region src/utilities/chat-params.ts
3
+ var KNOWN_PART_TYPES = /* @__PURE__ */ new Set([
4
+ "text",
5
+ "image",
6
+ "audio",
7
+ "video",
8
+ "document",
9
+ "tool-call",
10
+ "tool-result",
11
+ "thinking"
11
12
  ]);
12
13
  function isValidParts(value) {
13
- if (!Array.isArray(value)) return false;
14
- for (const p of value) {
15
- if (!p || typeof p !== "object") return false;
16
- const type = p.type;
17
- if (typeof type !== "string" || !KNOWN_PART_TYPES.has(type)) return false;
18
- }
19
- return true;
14
+ if (!Array.isArray(value)) return false;
15
+ for (const p of value) {
16
+ if (!p || typeof p !== "object") return false;
17
+ const type = p.type;
18
+ if (typeof type !== "string" || !KNOWN_PART_TYPES.has(type)) return false;
19
+ }
20
+ return true;
20
21
  }
21
- function chatParamsFromRequestBody(body) {
22
- const parseResult = RunAgentInputSchema.safeParse(body);
23
- if (!parseResult.success) {
24
- return Promise.reject(
25
- new AGUIError(
26
- `Request body is not a valid AG-UI RunAgentInput. If you're upgrading from a previous @tanstack/ai-client release, see docs/migration/ag-ui-compliance.md. Validation errors: ${parseResult.error.message}`
27
- )
28
- );
29
- }
30
- const parsed = parseResult.data;
31
- const aguiContext = parsed.context;
32
- const rawMessages = body.messages ?? [];
33
- const messages = parsed.messages.map((m, i) => {
34
- const raw = rawMessages[i];
35
- if (raw && typeof raw === "object" && "parts" in raw && isValidParts(raw.parts)) {
36
- return { ...m, parts: raw.parts };
37
- }
38
- return m;
39
- });
40
- return Promise.resolve({
41
- messages,
42
- threadId: parsed.threadId,
43
- runId: parsed.runId,
44
- parentRunId: parsed.parentRunId,
45
- tools: parsed.tools,
46
- forwardedProps: parsed.forwardedProps ?? {},
47
- state: parsed.state,
48
- context: aguiContext,
49
- aguiContext
50
- });
22
+ /**
23
+ * Keyed by `AGUIRole` so a role added upstream fails to compile here until it
24
+ * is handled, rather than silently falling through as an unknown role.
25
+ */
26
+ var AGUI_ROLES = {
27
+ developer: true,
28
+ system: true,
29
+ assistant: true,
30
+ user: true,
31
+ tool: true,
32
+ activity: true,
33
+ reasoning: true
34
+ };
35
+ function isAGUIRole(value) {
36
+ return typeof value === "string" && value in AGUI_ROLES;
37
+ }
38
+ function isRecord(value) {
39
+ return typeof value === "object" && value !== null && !Array.isArray(value);
40
+ }
41
+ /**
42
+ * Reject the request body, pointing at the migration guide. Mirrors the
43
+ * message the previous `RunAgentInputSchema.safeParse` failure produced.
44
+ */
45
+ function invalidBody(reason) {
46
+ throw new AGUIError(`Request body is not a valid AG-UI RunAgentInput. If you're upgrading from a previous @tanstack/ai-client release, see docs/migration/ag-ui-compliance.md. Validation errors: ${reason}`);
47
+ }
48
+ function requireString(value, at) {
49
+ if (typeof value !== "string") invalidBody(`${at} must be a string`);
50
+ return value;
51
+ }
52
+ function requireArray(value, at) {
53
+ if (!Array.isArray(value)) invalidBody(`${at} must be an array`);
54
+ return value;
55
+ }
56
+ /**
57
+ * Assert one AG-UI `Message`, discriminating on `role` exactly as the upstream
58
+ * `MessageSchema` discriminated union does. The record view is retained on the
59
+ * asserted type so callers can still inspect non-AG-UI extras like `parts`.
60
+ */
61
+ function assertAGUIMessage(value, at) {
62
+ requireString(value.id, `${at}.id`);
63
+ const role = value.role;
64
+ if (!isAGUIRole(role)) invalidBody(`${at}.role must be one of ${Object.keys(AGUI_ROLES).join(" | ")}`);
65
+ switch (role) {
66
+ case "assistant":
67
+ if (value.content !== void 0) requireString(value.content, `${at}.content`);
68
+ if (value.toolCalls !== void 0) requireArray(value.toolCalls, `${at}.toolCalls`);
69
+ break;
70
+ case "user":
71
+ if (typeof value.content !== "string" && !Array.isArray(value.content)) invalidBody(`${at}.content must be a string or an array of content parts`);
72
+ break;
73
+ case "tool":
74
+ requireString(value.content, `${at}.content`);
75
+ requireString(value.toolCallId, `${at}.toolCallId`);
76
+ break;
77
+ case "activity":
78
+ requireString(value.activityType, `${at}.activityType`);
79
+ if (!isRecord(value.content)) invalidBody(`${at}.content must be an object`);
80
+ break;
81
+ case "developer":
82
+ case "system":
83
+ case "reasoning":
84
+ requireString(value.content, `${at}.content`);
85
+ break;
86
+ }
51
87
  }
88
+ function validateMessage(value, index) {
89
+ const at = `messages[${index}]`;
90
+ if (!isRecord(value)) invalidBody(`${at} must be an object`);
91
+ assertAGUIMessage(value, at);
92
+ if ("parts" in value && !isValidParts(value.parts)) {
93
+ const withoutParts = { ...value };
94
+ Reflect.deleteProperty(withoutParts, "parts");
95
+ return withoutParts;
96
+ }
97
+ return value;
98
+ }
99
+ function validateTool(value, index) {
100
+ const at = `tools[${index}]`;
101
+ if (!isRecord(value)) invalidBody(`${at} must be an object`);
102
+ return {
103
+ name: requireString(value.name, `${at}.name`),
104
+ description: requireString(value.description, `${at}.description`),
105
+ parameters: value.parameters
106
+ };
107
+ }
108
+ function validateContext(value, index) {
109
+ const at = `context[${index}]`;
110
+ if (!isRecord(value)) invalidBody(`${at} must be an object`);
111
+ return {
112
+ description: requireString(value.description, `${at}.description`),
113
+ value: requireString(value.value, `${at}.value`)
114
+ };
115
+ }
116
+ function validateResumeEntry(value, index) {
117
+ const at = `resume[${index}]`;
118
+ if (!isRecord(value)) invalidBody(`${at} must be an object`);
119
+ const status = value.status;
120
+ if (status !== "resolved" && status !== "cancelled") invalidBody(`${at}.status must be "resolved" or "cancelled"`);
121
+ const entry = {
122
+ interruptId: requireString(value.interruptId, `${at}.interruptId`),
123
+ status
124
+ };
125
+ if (value.payload !== void 0) entry.payload = value.payload;
126
+ return entry;
127
+ }
128
+ /**
129
+ * Parse and validate an HTTP request body as an AG-UI `RunAgentInput`.
130
+ *
131
+ * Returns a spread-friendly object whose `messages` field is suitable for
132
+ * passing directly to `chat({ messages })`. The existing
133
+ * `convertMessagesToModelMessages` handles AG-UI fan-out dedup and
134
+ * reasoning/activity/developer-role normalization internally.
135
+ *
136
+ * Validated structurally against the AG-UI `RunAgentInput` contract without a
137
+ * schema library, so this package pulls in no validation runtime of its own.
138
+ *
139
+ * @throws An error with a migration-pointing message when the body does
140
+ * not conform to AG-UI `RunAgentInput`. Surface this as a
141
+ * 400 Bad Request to the client.
142
+ */
143
+ async function chatParamsFromRequestBody(body) {
144
+ if (!isRecord(body)) invalidBody("body must be a JSON object");
145
+ const threadId = requireString(body.threadId, "threadId");
146
+ const runId = requireString(body.runId, "runId");
147
+ const parentRunId = body.parentRunId === void 0 ? void 0 : requireString(body.parentRunId, "parentRunId");
148
+ const messages = requireArray(body.messages, "messages").map(validateMessage);
149
+ const tools = requireArray(body.tools, "tools").map(validateTool);
150
+ const aguiContext = requireArray(body.context, "context").map(validateContext);
151
+ const resume = body.resume === void 0 ? void 0 : requireArray(body.resume, "resume").map(validateResumeEntry);
152
+ if (body.forwardedProps !== void 0 && !isRecord(body.forwardedProps)) invalidBody("forwardedProps must be an object");
153
+ return {
154
+ messages,
155
+ threadId,
156
+ runId,
157
+ parentRunId,
158
+ tools,
159
+ forwardedProps: body.forwardedProps ?? {},
160
+ state: body.state,
161
+ resume,
162
+ context: aguiContext,
163
+ aguiContext
164
+ };
165
+ }
166
+ /**
167
+ * Read an HTTP `Request`, parse its JSON body, and validate it as an
168
+ * AG-UI `RunAgentInput` — collapsing the standard `req.json()` +
169
+ * `chatParamsFromRequestBody(...)` pair into a single call.
170
+ *
171
+ * On a malformed body or invalid AG-UI shape, this **throws a
172
+ * `Response`** with status 400 and a migration-pointing message in the
173
+ * body. Frameworks that natively handle thrown `Response` objects
174
+ * (TanStack Start, SolidStart, Remix, React Router 7) will return the
175
+ * 400 to the client automatically, so the handler reduces to:
176
+ *
177
+ * ```ts
178
+ * export async function POST(req: Request) {
179
+ * const params = await chatParamsFromRequest(req)
180
+ * // ...use params
181
+ * }
182
+ * ```
183
+ *
184
+ * In frameworks that do not auto-handle thrown `Response` objects
185
+ * (Next.js Route Handlers, SvelteKit, Hono, raw Node), wrap the call
186
+ * with try/catch and return the caught Response yourself, or use
187
+ * `chatParamsFromRequestBody` directly with your own JSON-parsing.
188
+ *
189
+ * @throws {Response} 400 on malformed JSON or invalid AG-UI shape.
190
+ */
52
191
  async function chatParamsFromRequest(req) {
53
- let body;
54
- try {
55
- body = await req.json();
56
- } catch (cause) {
57
- const res = new Response(
58
- "Invalid AG-UI request body. See docs/migration/ag-ui-compliance.md.",
59
- { status: 400 }
60
- );
61
- res.cause = cause;
62
- throw res;
63
- }
64
- try {
65
- return await chatParamsFromRequestBody(body);
66
- } catch (cause) {
67
- const res = new Response(
68
- "Invalid AG-UI request body. See docs/migration/ag-ui-compliance.md.",
69
- { status: 400 }
70
- );
71
- res.cause = cause;
72
- throw res;
73
- }
192
+ let body;
193
+ try {
194
+ body = await req.json();
195
+ } catch (cause) {
196
+ const res = new Response("Invalid AG-UI request body. See docs/migration/ag-ui-compliance.md.", { status: 400 });
197
+ res.cause = cause;
198
+ throw res;
199
+ }
200
+ try {
201
+ return await chatParamsFromRequestBody(body);
202
+ } catch (cause) {
203
+ const res = new Response("Invalid AG-UI request body. See docs/migration/ag-ui-compliance.md.", { status: 400 });
204
+ res.cause = cause;
205
+ throw res;
206
+ }
74
207
  }
75
208
  function mergeAgentTools(serverTools, clientTools) {
76
- const seen = new Set(serverTools.map((t) => t.name));
77
- const merged = [
78
- ...serverTools
79
- ];
80
- for (const ct of clientTools) {
81
- if (seen.has(ct.name)) {
82
- continue;
83
- }
84
- seen.add(ct.name);
85
- merged.push({
86
- name: ct.name,
87
- description: ct.description,
88
- inputSchema: ct.parameters
89
- // No `execute` — runtime treats this as a client-side tool and
90
- // emits ClientToolRequest events.
91
- });
92
- }
93
- return merged;
209
+ if (clientTools.length === 0) return serverTools;
210
+ const seen = new Set(serverTools.map((t) => t.name));
211
+ const merged = [...serverTools];
212
+ for (const ct of clientTools) {
213
+ if (seen.has(ct.name)) continue;
214
+ seen.add(ct.name);
215
+ merged.push({
216
+ name: ct.name,
217
+ description: ct.description,
218
+ inputSchema: ct.parameters
219
+ });
220
+ }
221
+ return merged;
94
222
  }
95
- export {
96
- chatParamsFromRequest,
97
- chatParamsFromRequestBody,
98
- mergeAgentTools
99
- };
100
- //# sourceMappingURL=chat-params.js.map
223
+ //#endregion
224
+ export { chatParamsFromRequest, chatParamsFromRequestBody, mergeAgentTools };
225
+
226
+ //# sourceMappingURL=chat-params.js.map