experimental-a2 0.3.0 → 0.5.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 (205) hide show
  1. package/CHANGELOG.md +207 -0
  2. package/dist/ai-CFNeCrRl.d.ts +334 -0
  3. package/dist/ai-CFNeCrRl.d.ts.map +1 -0
  4. package/dist/ai-server.browser.js +2 -0
  5. package/dist/ai-server.browser.js.map +1 -0
  6. package/dist/ai-server.d.ts +49 -9
  7. package/dist/ai-server.d.ts.map +1 -0
  8. package/dist/ai-server.js +123 -49
  9. package/dist/ai-server.js.map +1 -0
  10. package/dist/ai.d.ts +2 -303
  11. package/dist/ai.js +233 -86
  12. package/dist/ai.js.map +1 -0
  13. package/dist/cli-B3VuxoDe.js +599 -0
  14. package/dist/cli-B3VuxoDe.js.map +1 -0
  15. package/dist/cli-bin.d.ts +1 -0
  16. package/dist/cli-bin.js +7 -0
  17. package/dist/cli-bin.js.map +1 -0
  18. package/dist/cli.d.ts +20 -0
  19. package/dist/cli.d.ts.map +1 -0
  20. package/dist/cli.js +2 -0
  21. package/dist/client-BKlyLiOU.js +1008 -0
  22. package/dist/client-BKlyLiOU.js.map +1 -0
  23. package/dist/client-D7mvIXrF.d.ts +191 -0
  24. package/dist/client-D7mvIXrF.d.ts.map +1 -0
  25. package/dist/client.d.ts +2 -90
  26. package/dist/client.js +1 -409
  27. package/dist/{contract-CG_adnu_.js → contract-48bUMgcL.js} +12 -2
  28. package/dist/contract-48bUMgcL.js.map +1 -0
  29. package/dist/{contract-C_3dIIEU.d.ts → contract-jIfaR085.d.ts} +63 -8
  30. package/dist/contract-jIfaR085.d.ts.map +1 -0
  31. package/dist/devtools-J_jZ2vQf.d.ts +152 -0
  32. package/dist/devtools-J_jZ2vQf.d.ts.map +1 -0
  33. package/dist/devtools-kJJaORn-.js +340 -0
  34. package/dist/devtools-kJJaORn-.js.map +1 -0
  35. package/dist/devtools-server.browser.js +3 -1
  36. package/dist/devtools-server.browser.js.map +1 -0
  37. package/dist/devtools-server.d.ts +4 -3
  38. package/dist/devtools-server.d.ts.map +1 -0
  39. package/dist/devtools-server.js +226 -43
  40. package/dist/devtools-server.js.map +1 -0
  41. package/dist/devtools.d.ts +2 -0
  42. package/dist/devtools.js +2 -0
  43. package/dist/{errors-BJRMd-h6.js → errors-BQuJpe82.js} +6 -4
  44. package/dist/errors-BQuJpe82.js.map +1 -0
  45. package/dist/{errors-xL_JTXsY.d.ts → errors-W6nwJ-fm.d.ts} +3 -2
  46. package/dist/errors-W6nwJ-fm.d.ts.map +1 -0
  47. package/dist/http.d.ts +135 -28
  48. package/dist/http.d.ts.map +1 -0
  49. package/dist/http.js +647 -61
  50. package/dist/http.js.map +1 -0
  51. package/dist/{idempotent-replay-BMyHrP0L.js → idempotent-replay-DuqEkYA7.js} +4 -2
  52. package/dist/idempotent-replay-DuqEkYA7.js.map +1 -0
  53. package/dist/index.d.ts +5 -5
  54. package/dist/index.js +2 -2
  55. package/dist/{inspection-E7qbD0Xj.js → inspection-DaxB5jM2.js} +4 -1
  56. package/dist/inspection-DaxB5jM2.js.map +1 -0
  57. package/dist/internal-DstsI6Re.js +211 -0
  58. package/dist/internal-DstsI6Re.js.map +1 -0
  59. package/dist/otel.d.ts +3 -2
  60. package/dist/otel.d.ts.map +1 -0
  61. package/dist/otel.js +2 -0
  62. package/dist/otel.js.map +1 -0
  63. package/dist/platform-B4TnJtWu.js +34 -0
  64. package/dist/platform-B4TnJtWu.js.map +1 -0
  65. package/dist/react.d.ts +47 -15
  66. package/dist/react.d.ts.map +1 -0
  67. package/dist/react.js +30 -15
  68. package/dist/react.js.map +1 -0
  69. package/dist/retryable-lazy-DZWmHpii.js +2 -0
  70. package/dist/retryable-lazy-DZWmHpii.js.map +1 -0
  71. package/dist/scheduler-qstash.d.ts +79 -0
  72. package/dist/scheduler-qstash.d.ts.map +1 -0
  73. package/dist/scheduler-qstash.js +501 -0
  74. package/dist/scheduler-qstash.js.map +1 -0
  75. package/dist/scheduler-task-BpzhPnRS.js +56 -0
  76. package/dist/scheduler-task-BpzhPnRS.js.map +1 -0
  77. package/dist/{recovery-vercel.d.ts → scheduler-vercel.d.ts} +18 -24
  78. package/dist/scheduler-vercel.d.ts.map +1 -0
  79. package/dist/scheduler-vercel.js +228 -0
  80. package/dist/scheduler-vercel.js.map +1 -0
  81. package/dist/server-C72KOw51.js +1457 -0
  82. package/dist/server-C72KOw51.js.map +1 -0
  83. package/dist/server-DUF9pjsx.d.ts +261 -0
  84. package/dist/server-DUF9pjsx.d.ts.map +1 -0
  85. package/dist/server.browser.js +2 -0
  86. package/dist/server.browser.js.map +1 -0
  87. package/dist/server.d.ts +4 -155
  88. package/dist/server.js +2 -2
  89. package/dist/{log-ldf5g8Cx.d.ts → store-DysUkTH3.d.ts} +120 -35
  90. package/dist/store-DysUkTH3.d.ts.map +1 -0
  91. package/dist/{log-yJbXUf72.js → store-N8PXxDAS.js} +3 -1
  92. package/dist/store-N8PXxDAS.js.map +1 -0
  93. package/dist/store-codec-DTG0Ftek.js +10 -0
  94. package/dist/store-codec-DTG0Ftek.js.map +1 -0
  95. package/dist/store-memory.d.ts +12 -0
  96. package/dist/store-memory.d.ts.map +1 -0
  97. package/dist/{log-memory.js → store-memory.js} +145 -34
  98. package/dist/store-memory.js.map +1 -0
  99. package/dist/{log-polling-DZ1MiKLg.js → store-polling-dSeLxzfb.js} +7 -5
  100. package/dist/store-polling-dSeLxzfb.js.map +1 -0
  101. package/dist/{log-postgres.d.ts → store-postgres.d.ts} +7 -6
  102. package/dist/store-postgres.d.ts.map +1 -0
  103. package/dist/{log-postgres.js → store-postgres.js} +214 -24
  104. package/dist/store-postgres.js.map +1 -0
  105. package/dist/{log-redis-core-CyJ5L8yR.js → store-redis-core-BFLwz0Wj.js} +271 -66
  106. package/dist/store-redis-core-BFLwz0Wj.js.map +1 -0
  107. package/dist/{log-redis-http.d.ts → store-redis-http.d.ts} +8 -7
  108. package/dist/store-redis-http.d.ts.map +1 -0
  109. package/dist/{log-redis-http.js → store-redis-http.js} +22 -12
  110. package/dist/store-redis-http.js.map +1 -0
  111. package/dist/{log-redis.d.ts → store-redis.d.ts} +7 -6
  112. package/dist/store-redis.d.ts.map +1 -0
  113. package/dist/{log-redis.js → store-redis.js} +101 -39
  114. package/dist/store-redis.js.map +1 -0
  115. package/dist/{log-sqlite.d.ts → store-sqlite.d.ts} +7 -6
  116. package/dist/store-sqlite.d.ts.map +1 -0
  117. package/dist/{log-sqlite.js → store-sqlite.js} +118 -22
  118. package/dist/store-sqlite.js.map +1 -0
  119. package/dist/{telemetry-Cso0qyHQ.d.ts → telemetry-BjYHTfh2.d.ts} +3 -2
  120. package/dist/telemetry-BjYHTfh2.d.ts.map +1 -0
  121. package/dist/testing.browser.d.ts +1 -0
  122. package/dist/testing.browser.js +6 -0
  123. package/dist/testing.browser.js.map +1 -0
  124. package/dist/testing.d.ts +32 -0
  125. package/dist/testing.d.ts.map +1 -0
  126. package/dist/testing.js +103 -0
  127. package/dist/testing.js.map +1 -0
  128. package/dist/validate-XKT4FSNn.js +2 -0
  129. package/dist/validate-XKT4FSNn.js.map +1 -0
  130. package/dist/wire-BFQmSJ-9.js +284 -0
  131. package/dist/wire-BFQmSJ-9.js.map +1 -0
  132. package/docs/01-quickstart.mdx +3 -3
  133. package/docs/concepts/01-contracts.mdx +18 -13
  134. package/docs/concepts/02-handlers.mdx +7 -7
  135. package/docs/concepts/03-durability.mdx +21 -21
  136. package/docs/concepts/04-state.mdx +18 -21
  137. package/docs/guides/01-timers.mdx +154 -49
  138. package/docs/guides/02-cancellation.mdx +30 -4
  139. package/docs/guides/03-react.mdx +76 -57
  140. package/docs/guides/04-local-first.mdx +1 -1
  141. package/docs/guides/05-production.mdx +310 -61
  142. package/docs/guides/06-ai-agents.mdx +252 -74
  143. package/docs/guides/07-devtools.mdx +137 -12
  144. package/docs/guides/08-application-data.mdx +1 -1
  145. package/docs/guides/09-presence.mdx +263 -0
  146. package/docs/guides/10-transports.mdx +140 -0
  147. package/docs/index.mdx +18 -14
  148. package/docs/reference/01-api.mdx +839 -110
  149. package/docs/reference/02-errors.mdx +13 -6
  150. package/package.json +24 -7
  151. package/src/ai-coordinator.ts +358 -0
  152. package/src/ai-projector.ts +524 -0
  153. package/src/ai-sdk-step.ts +261 -0
  154. package/src/ai-server.browser.ts +5 -0
  155. package/src/ai-server.ts +1719 -0
  156. package/src/ai.ts +2155 -0
  157. package/src/cache-indexeddb.ts +10 -0
  158. package/src/cli-bin.ts +5 -0
  159. package/src/cli.ts +1046 -0
  160. package/src/client.ts +1826 -0
  161. package/src/contract.ts +206 -0
  162. package/src/deterministic-id.ts +72 -0
  163. package/src/devtools-app.ts +989 -0
  164. package/src/devtools-server.browser.ts +5 -0
  165. package/src/devtools-server.ts +604 -0
  166. package/src/devtools.ts +716 -0
  167. package/src/errors.ts +50 -0
  168. package/src/http.ts +394 -0
  169. package/src/idempotent-replay.ts +53 -0
  170. package/src/index.ts +37 -0
  171. package/src/inspection.ts +39 -0
  172. package/src/internal.ts +426 -0
  173. package/src/otel.ts +59 -0
  174. package/src/platform.ts +60 -0
  175. package/src/push-envelope.ts +137 -0
  176. package/src/react.ts +284 -0
  177. package/src/reducer.ts +108 -0
  178. package/src/retryable-lazy.ts +27 -0
  179. package/src/scheduler-qstash.ts +915 -0
  180. package/src/scheduler-task.ts +106 -0
  181. package/src/scheduler-vercel.ts +437 -0
  182. package/src/server.browser.ts +12 -0
  183. package/src/server.ts +2700 -0
  184. package/src/session-socket.ts +548 -0
  185. package/src/sse.ts +141 -0
  186. package/src/standard-schema.ts +77 -0
  187. package/src/store-codec.ts +10 -0
  188. package/src/store-memory.ts +788 -0
  189. package/src/store-polling.ts +102 -0
  190. package/src/store-postgres.ts +1212 -0
  191. package/src/store-redis-core.ts +1494 -0
  192. package/src/store-redis-http.ts +116 -0
  193. package/src/store-redis.ts +458 -0
  194. package/src/store-sqlite.ts +1108 -0
  195. package/src/store.ts +385 -0
  196. package/src/telemetry.ts +47 -0
  197. package/src/testing.browser.ts +5 -0
  198. package/src/testing.ts +185 -0
  199. package/src/validate.ts +39 -0
  200. package/src/wire.ts +454 -0
  201. package/dist/internal-gCd5qMry.js +0 -44
  202. package/dist/log-memory.d.ts +0 -11
  203. package/dist/recovery-vercel.js +0 -119
  204. package/dist/server-BcLa4RFL.js +0 -877
  205. package/dist/wire-BVsgR8o9.js +0 -62
@@ -0,0 +1,524 @@
1
+ import type { UIMessage, UIMessageChunk } from 'ai'
2
+
3
+ type MutablePart = Record<string, unknown>
4
+
5
+ type PartialToolCall = {
6
+ text: string
7
+ toolName: string
8
+ dynamic: boolean
9
+ providerExecuted?: boolean
10
+ providerMetadata?: unknown
11
+ toolMetadata?: unknown
12
+ title?: string
13
+ }
14
+
15
+ const isRecord = (value: unknown): value is Record<string, unknown> =>
16
+ typeof value === 'object' && value !== null && !Array.isArray(value)
17
+
18
+ const cloneMessage = <M extends UIMessage>(messageId: string, base?: M): M =>
19
+ structuredClone(base ?? { id: messageId, role: 'assistant', parts: [] }) as M
20
+
21
+ const currentStepParts = (parts: MutablePart[]): MutablePart[] => {
22
+ let start = parts.length - 1
23
+ while (start >= 0 && parts[start]?.['type'] !== 'step-start') start -= 1
24
+ return parts.slice(start + 1)
25
+ }
26
+
27
+ const isToolPart = (part: MutablePart): boolean =>
28
+ typeof part['toolCallId'] === 'string' &&
29
+ (part['type'] === 'dynamic-tool' ||
30
+ (typeof part['type'] === 'string' && part['type'].startsWith('tool-')))
31
+
32
+ const findToolPart = (
33
+ parts: MutablePart[],
34
+ toolCallId: string,
35
+ ): MutablePart | undefined => {
36
+ const current = currentStepParts(parts).find(
37
+ (part) => isToolPart(part) && part['toolCallId'] === toolCallId,
38
+ )
39
+ if (current) return current
40
+ return parts.findLast(
41
+ (part) => isToolPart(part) && part['toolCallId'] === toolCallId,
42
+ )
43
+ }
44
+
45
+ const findApprovalPart = (
46
+ parts: MutablePart[],
47
+ approvalId: string,
48
+ ): MutablePart | undefined =>
49
+ parts.find((part) => {
50
+ const approval = part['approval']
51
+ return (
52
+ isToolPart(part) && isRecord(approval) && approval['id'] === approvalId
53
+ )
54
+ })
55
+
56
+ const setOptional = (
57
+ target: MutablePart,
58
+ key: string,
59
+ value: unknown,
60
+ ): void => {
61
+ if (value !== undefined) target[key] = value
62
+ }
63
+
64
+ const setToolPart = (options: {
65
+ parts: MutablePart[]
66
+ toolCallId: string
67
+ toolName: string
68
+ dynamic: boolean
69
+ state: string
70
+ input?: unknown
71
+ rawInput?: unknown
72
+ output?: unknown
73
+ errorText?: string
74
+ preliminary?: boolean | undefined
75
+ providerExecuted?: boolean | undefined
76
+ providerMetadata?: unknown
77
+ toolMetadata?: unknown
78
+ title?: string | undefined
79
+ }): MutablePart => {
80
+ let part = findToolPart(options.parts, options.toolCallId)
81
+ if (!part) {
82
+ part = {
83
+ type: options.dynamic ? 'dynamic-tool' : `tool-${options.toolName}`,
84
+ ...(options.dynamic ? { toolName: options.toolName } : {}),
85
+ toolCallId: options.toolCallId,
86
+ }
87
+ options.parts.push(part)
88
+ }
89
+
90
+ part['state'] = options.state
91
+ part['input'] = options.input
92
+ delete part['output']
93
+ delete part['rawInput']
94
+ delete part['errorText']
95
+ delete part['preliminary']
96
+ setOptional(part, 'rawInput', options.rawInput)
97
+ setOptional(part, 'output', options.output)
98
+ setOptional(part, 'errorText', options.errorText)
99
+ setOptional(part, 'preliminary', options.preliminary)
100
+ setOptional(part, 'providerExecuted', options.providerExecuted)
101
+ setOptional(part, 'title', options.title)
102
+ setOptional(part, 'toolMetadata', options.toolMetadata)
103
+ if (options.dynamic) part['toolName'] = options.toolName
104
+ if (options.providerMetadata !== undefined) {
105
+ part[
106
+ options.state === 'output-available' || options.state === 'output-error'
107
+ ? 'resultProviderMetadata'
108
+ : 'callProviderMetadata'
109
+ ] = options.providerMetadata
110
+ }
111
+ return part
112
+ }
113
+
114
+ const mergeMetadata = (current: unknown, update: unknown): unknown => {
115
+ if (update === undefined || update === null) return current
116
+ return isRecord(current) && isRecord(update)
117
+ ? { ...current, ...update }
118
+ : update
119
+ }
120
+
121
+ const parseCompleteJSON = (value: string): unknown => {
122
+ try {
123
+ return JSON.parse(value) as unknown
124
+ } catch {
125
+ return undefined
126
+ }
127
+ }
128
+
129
+ /** Internal synchronous counterpart of the AI SDK UI message stream fold. */
130
+ export function projectUIMessage<M extends UIMessage>(options: {
131
+ messageId: string
132
+ base?: M
133
+ chunks: readonly UIMessageChunk[]
134
+ }): M {
135
+ const message = cloneMessage(options.messageId, options.base)
136
+ const record = message as unknown as Record<string, unknown>
137
+ const parts = message.parts as unknown as MutablePart[]
138
+ const activeText = new Map<string, MutablePart>()
139
+ const activeReasoning = new Map<string, MutablePart>()
140
+ const partialTools = new Map<string, PartialToolCall>()
141
+
142
+ for (const chunk of options.chunks) {
143
+ switch (chunk.type) {
144
+ case 'text-start': {
145
+ const part: MutablePart = {
146
+ type: 'text',
147
+ text: '',
148
+ state: 'streaming',
149
+ ...(chunk.providerMetadata === undefined
150
+ ? {}
151
+ : { providerMetadata: chunk.providerMetadata }),
152
+ }
153
+ activeText.set(chunk.id, part)
154
+ parts.push(part)
155
+ break
156
+ }
157
+ case 'text-delta': {
158
+ let part = activeText.get(chunk.id)
159
+ if (!part) {
160
+ part = { type: 'text', text: '', state: 'streaming' }
161
+ activeText.set(chunk.id, part)
162
+ parts.push(part)
163
+ }
164
+ part['text'] =
165
+ `${typeof part['text'] === 'string' ? part['text'] : ''}${chunk.delta}`
166
+ setOptional(part, 'providerMetadata', chunk.providerMetadata)
167
+ break
168
+ }
169
+ case 'text-end': {
170
+ const part = activeText.get(chunk.id)
171
+ if (part) {
172
+ part['state'] = 'done'
173
+ setOptional(part, 'providerMetadata', chunk.providerMetadata)
174
+ activeText.delete(chunk.id)
175
+ }
176
+ break
177
+ }
178
+ case 'reasoning-start': {
179
+ const part: MutablePart = {
180
+ type: 'reasoning',
181
+ text: '',
182
+ state: 'streaming',
183
+ ...(chunk.providerMetadata === undefined
184
+ ? {}
185
+ : { providerMetadata: chunk.providerMetadata }),
186
+ }
187
+ activeReasoning.set(chunk.id, part)
188
+ parts.push(part)
189
+ break
190
+ }
191
+ case 'reasoning-delta': {
192
+ let part = activeReasoning.get(chunk.id)
193
+ if (!part) {
194
+ part = { type: 'reasoning', text: '', state: 'streaming' }
195
+ activeReasoning.set(chunk.id, part)
196
+ parts.push(part)
197
+ }
198
+ part['text'] =
199
+ `${typeof part['text'] === 'string' ? part['text'] : ''}${chunk.delta}`
200
+ setOptional(part, 'providerMetadata', chunk.providerMetadata)
201
+ break
202
+ }
203
+ case 'reasoning-end': {
204
+ const part = activeReasoning.get(chunk.id)
205
+ if (part) {
206
+ part['state'] = 'done'
207
+ setOptional(part, 'providerMetadata', chunk.providerMetadata)
208
+ activeReasoning.delete(chunk.id)
209
+ }
210
+ break
211
+ }
212
+ case 'custom':
213
+ parts.push({
214
+ type: 'custom',
215
+ kind: chunk.kind,
216
+ ...(chunk.providerMetadata === undefined
217
+ ? {}
218
+ : { providerMetadata: chunk.providerMetadata }),
219
+ })
220
+ break
221
+ case 'file':
222
+ case 'reasoning-file':
223
+ parts.push({
224
+ type: chunk.type,
225
+ mediaType: chunk.mediaType,
226
+ url: chunk.url,
227
+ ...(chunk.providerMetadata === undefined
228
+ ? {}
229
+ : { providerMetadata: chunk.providerMetadata }),
230
+ })
231
+ break
232
+ case 'source-url':
233
+ parts.push({
234
+ type: chunk.type,
235
+ sourceId: chunk.sourceId,
236
+ url: chunk.url,
237
+ ...(chunk.title === undefined ? {} : { title: chunk.title }),
238
+ ...(chunk.providerMetadata === undefined
239
+ ? {}
240
+ : { providerMetadata: chunk.providerMetadata }),
241
+ })
242
+ break
243
+ case 'source-document':
244
+ parts.push({
245
+ type: chunk.type,
246
+ sourceId: chunk.sourceId,
247
+ mediaType: chunk.mediaType,
248
+ title: chunk.title,
249
+ ...(chunk.filename === undefined ? {} : { filename: chunk.filename }),
250
+ ...(chunk.providerMetadata === undefined
251
+ ? {}
252
+ : { providerMetadata: chunk.providerMetadata }),
253
+ })
254
+ break
255
+ case 'tool-input-start': {
256
+ partialTools.set(chunk.toolCallId, {
257
+ text: '',
258
+ toolName: chunk.toolName,
259
+ dynamic: chunk.dynamic === true,
260
+ ...(chunk.providerExecuted === undefined
261
+ ? {}
262
+ : { providerExecuted: chunk.providerExecuted }),
263
+ ...(chunk.providerMetadata === undefined
264
+ ? {}
265
+ : { providerMetadata: chunk.providerMetadata }),
266
+ ...(chunk.toolMetadata === undefined
267
+ ? {}
268
+ : { toolMetadata: chunk.toolMetadata }),
269
+ ...(chunk.title === undefined ? {} : { title: chunk.title }),
270
+ })
271
+ setToolPart({
272
+ parts,
273
+ toolCallId: chunk.toolCallId,
274
+ toolName: chunk.toolName,
275
+ dynamic: chunk.dynamic === true,
276
+ state: 'input-streaming',
277
+ providerExecuted: chunk.providerExecuted,
278
+ providerMetadata: chunk.providerMetadata,
279
+ toolMetadata: chunk.toolMetadata,
280
+ title: chunk.title,
281
+ })
282
+ break
283
+ }
284
+ case 'tool-input-delta': {
285
+ const partial = partialTools.get(chunk.toolCallId)
286
+ if (!partial) break
287
+ partial.text += chunk.inputTextDelta
288
+ setToolPart({
289
+ parts,
290
+ toolCallId: chunk.toolCallId,
291
+ toolName: partial.toolName,
292
+ dynamic: partial.dynamic,
293
+ state: 'input-streaming',
294
+ input: parseCompleteJSON(partial.text),
295
+ providerExecuted: partial.providerExecuted,
296
+ providerMetadata: partial.providerMetadata,
297
+ toolMetadata: partial.toolMetadata,
298
+ title: partial.title,
299
+ })
300
+ break
301
+ }
302
+ case 'tool-input-available':
303
+ setToolPart({
304
+ parts,
305
+ toolCallId: chunk.toolCallId,
306
+ toolName: chunk.toolName,
307
+ dynamic: chunk.dynamic === true,
308
+ state: 'input-available',
309
+ input: chunk.input,
310
+ providerExecuted: chunk.providerExecuted,
311
+ providerMetadata: chunk.providerMetadata,
312
+ toolMetadata: chunk.toolMetadata,
313
+ title: chunk.title,
314
+ })
315
+ partialTools.delete(chunk.toolCallId)
316
+ break
317
+ case 'tool-input-error': {
318
+ const existing = findToolPart(parts, chunk.toolCallId)
319
+ const dynamic = existing?.['type'] === 'dynamic-tool' || chunk.dynamic
320
+ setToolPart({
321
+ parts,
322
+ toolCallId: chunk.toolCallId,
323
+ toolName: chunk.toolName,
324
+ dynamic: dynamic === true,
325
+ state: 'output-error',
326
+ input: dynamic ? chunk.input : undefined,
327
+ rawInput: dynamic ? undefined : chunk.input,
328
+ errorText: chunk.errorText,
329
+ providerExecuted: chunk.providerExecuted,
330
+ providerMetadata: chunk.providerMetadata,
331
+ toolMetadata: chunk.toolMetadata,
332
+ title: chunk.title,
333
+ })
334
+ partialTools.delete(chunk.toolCallId)
335
+ break
336
+ }
337
+ case 'tool-approval-request': {
338
+ const part = findToolPart(parts, chunk.toolCallId)
339
+ if (!part) break
340
+ part['state'] = 'approval-requested'
341
+ part['approval'] = {
342
+ id: chunk.approvalId,
343
+ ...(chunk.isAutomatic === true ? { isAutomatic: true } : {}),
344
+ ...(chunk.signature === undefined
345
+ ? {}
346
+ : { signature: chunk.signature }),
347
+ }
348
+ break
349
+ }
350
+ case 'tool-approval-response': {
351
+ const part = findApprovalPart(parts, chunk.approvalId)
352
+ if (!part) break
353
+ const approval = isRecord(part['approval']) ? part['approval'] : {}
354
+ part['state'] = 'approval-responded'
355
+ part['approval'] = {
356
+ id: chunk.approvalId,
357
+ approved: chunk.approved,
358
+ ...(chunk.reason === undefined ? {} : { reason: chunk.reason }),
359
+ ...(approval['isAutomatic'] === true ? { isAutomatic: true } : {}),
360
+ ...(typeof approval['signature'] === 'string'
361
+ ? { signature: approval['signature'] }
362
+ : {}),
363
+ }
364
+ setOptional(part, 'providerExecuted', chunk.providerExecuted)
365
+ setOptional(part, 'callProviderMetadata', chunk.providerMetadata)
366
+ break
367
+ }
368
+ case 'tool-output-available': {
369
+ const part = findToolPart(parts, chunk.toolCallId)
370
+ if (!part) break
371
+ setToolPart({
372
+ parts,
373
+ toolCallId: chunk.toolCallId,
374
+ toolName:
375
+ part['type'] === 'dynamic-tool' &&
376
+ typeof part['toolName'] === 'string'
377
+ ? part['toolName']
378
+ : String(part['type']).slice('tool-'.length),
379
+ dynamic: part['type'] === 'dynamic-tool',
380
+ state: 'output-available',
381
+ input: part['input'],
382
+ output: chunk.output,
383
+ preliminary: chunk.preliminary,
384
+ providerExecuted: chunk.providerExecuted,
385
+ providerMetadata: chunk.providerMetadata,
386
+ toolMetadata: chunk.toolMetadata ?? part['toolMetadata'],
387
+ title: typeof part['title'] === 'string' ? part['title'] : undefined,
388
+ })
389
+ break
390
+ }
391
+ case 'tool-output-error': {
392
+ const part = findToolPart(parts, chunk.toolCallId)
393
+ if (!part) break
394
+ setToolPart({
395
+ parts,
396
+ toolCallId: chunk.toolCallId,
397
+ toolName:
398
+ part['type'] === 'dynamic-tool' &&
399
+ typeof part['toolName'] === 'string'
400
+ ? part['toolName']
401
+ : String(part['type']).slice('tool-'.length),
402
+ dynamic: part['type'] === 'dynamic-tool',
403
+ state: 'output-error',
404
+ input: part['input'],
405
+ rawInput: part['rawInput'],
406
+ errorText: chunk.errorText,
407
+ providerExecuted: chunk.providerExecuted,
408
+ providerMetadata: chunk.providerMetadata,
409
+ toolMetadata: chunk.toolMetadata ?? part['toolMetadata'],
410
+ title: typeof part['title'] === 'string' ? part['title'] : undefined,
411
+ })
412
+ break
413
+ }
414
+ case 'tool-output-denied': {
415
+ const part = findToolPart(parts, chunk.toolCallId)
416
+ if (part) part['state'] = 'output-denied'
417
+ break
418
+ }
419
+ case 'start-step':
420
+ parts.push({ type: 'step-start' })
421
+ break
422
+ case 'finish-step':
423
+ activeText.clear()
424
+ activeReasoning.clear()
425
+ break
426
+ case 'start':
427
+ if (chunk.messageId !== undefined) record['id'] = chunk.messageId
428
+ if (
429
+ chunk.messageMetadata !== undefined &&
430
+ chunk.messageMetadata !== null
431
+ ) {
432
+ record['metadata'] = mergeMetadata(
433
+ record['metadata'],
434
+ chunk.messageMetadata,
435
+ )
436
+ }
437
+ break
438
+ case 'finish':
439
+ if (
440
+ chunk.messageMetadata !== undefined &&
441
+ chunk.messageMetadata !== null
442
+ ) {
443
+ record['metadata'] = mergeMetadata(
444
+ record['metadata'],
445
+ chunk.messageMetadata,
446
+ )
447
+ }
448
+ break
449
+ case 'message-metadata':
450
+ if (
451
+ chunk.messageMetadata !== undefined &&
452
+ chunk.messageMetadata !== null
453
+ ) {
454
+ record['metadata'] = mergeMetadata(
455
+ record['metadata'],
456
+ chunk.messageMetadata,
457
+ )
458
+ }
459
+ break
460
+ case 'abort':
461
+ case 'error':
462
+ break
463
+ default: {
464
+ if (!chunk.type.startsWith('data-') || chunk.transient) break
465
+ const existing =
466
+ chunk.id === undefined
467
+ ? undefined
468
+ : parts.find(
469
+ (part) =>
470
+ part['type'] === chunk.type && part['id'] === chunk.id,
471
+ )
472
+ if (existing) existing['data'] = chunk.data
473
+ else
474
+ parts.push({
475
+ type: chunk.type,
476
+ ...(chunk.id === undefined ? {} : { id: chunk.id }),
477
+ data: chunk.data,
478
+ })
479
+ }
480
+ }
481
+ }
482
+
483
+ return message
484
+ }
485
+
486
+ export function interruptUIMessage<M extends UIMessage>(
487
+ message: M,
488
+ toolError = 'Tool execution was interrupted.',
489
+ ): M {
490
+ const next = structuredClone(message)
491
+ for (const value of next.parts as unknown as MutablePart[]) {
492
+ if (value['type'] === 'text' || value['type'] === 'reasoning') {
493
+ if (value['state'] === 'streaming') value['state'] = 'done'
494
+ continue
495
+ }
496
+ if (!isToolPart(value)) continue
497
+ const state = value['state']
498
+ if (state === 'approval-responded') {
499
+ const approval = value['approval']
500
+ if (isRecord(approval) && approval['approved'] === false) {
501
+ value['state'] = 'output-denied'
502
+ continue
503
+ }
504
+ }
505
+ if (state === 'output-available' && value['preliminary'] === true) {
506
+ delete value['output']
507
+ delete value['preliminary']
508
+ value['state'] = 'output-error'
509
+ value['errorText'] = toolError
510
+ continue
511
+ }
512
+ if (
513
+ state === 'input-streaming' ||
514
+ state === 'input-available' ||
515
+ state === 'approval-requested' ||
516
+ state === 'approval-responded'
517
+ ) {
518
+ if (state === 'approval-requested') delete value['approval']
519
+ value['state'] = 'output-error'
520
+ value['errorText'] = toolError
521
+ }
522
+ }
523
+ return next
524
+ }