experimental-a2 0.15.0 → 0.16.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 (101) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/dist/actor-client.d.ts +1 -1
  3. package/dist/{actor-BfQSE0KC.d.ts → actor-ohPC-81x.d.ts} +5 -5
  4. package/dist/{actor-BfQSE0KC.d.ts.map → actor-ohPC-81x.d.ts.map} +1 -1
  5. package/dist/actor-react.d.ts +3 -3
  6. package/dist/actor.d.ts +1 -1
  7. package/dist/actor.js +1 -1
  8. package/dist/ai-CrLYwNEx.js +2431 -0
  9. package/dist/ai-CrLYwNEx.js.map +1 -0
  10. package/dist/{ai-Cai-lCbj.d.ts → ai-HJ9fHfYI.d.ts} +72 -13
  11. package/dist/ai-HJ9fHfYI.d.ts.map +1 -0
  12. package/dist/ai-server.d.ts +4 -5
  13. package/dist/ai-server.d.ts.map +1 -1
  14. package/dist/ai-server.js +755 -290
  15. package/dist/ai-server.js.map +1 -1
  16. package/dist/ai.d.ts +2 -2
  17. package/dist/ai.js +1 -1999
  18. package/dist/client-BYzHjkwU.js.map +1 -1
  19. package/dist/{client-BAEABRZB.d.ts → client-CzyacQpJ.d.ts} +9 -9
  20. package/dist/client-CzyacQpJ.d.ts.map +1 -0
  21. package/dist/client.d.ts +1 -1
  22. package/dist/idempotent-id-BRJVeylj.js +13 -0
  23. package/dist/idempotent-id-BRJVeylj.js.map +1 -0
  24. package/dist/index.d.ts +3 -3
  25. package/dist/index.js +1 -12
  26. package/dist/otel.d.ts +1 -1
  27. package/dist/react.d.ts +13 -13
  28. package/dist/react.d.ts.map +1 -1
  29. package/dist/react.js.map +1 -1
  30. package/dist/reducer-DEMjEY_O.js.map +1 -1
  31. package/dist/{reducer-BcS9VDKC.d.ts → reducer-otzHjuJj.d.ts} +3 -3
  32. package/dist/{reducer-BcS9VDKC.d.ts.map → reducer-otzHjuJj.d.ts.map} +1 -1
  33. package/dist/scheduler-qstash.d.ts +2 -2
  34. package/dist/scheduler-qstash.js +1 -1
  35. package/dist/scheduler-vercel.d.ts +2 -2
  36. package/dist/scheduler-vercel.js +1 -1
  37. package/dist/{server-CjJSGcF7.js → server-BD5ckxZb.js} +39 -26
  38. package/dist/server-BD5ckxZb.js.map +1 -0
  39. package/dist/{server-Bp5Nd1pF.d.ts → server-DbD7IJVK.d.ts} +5 -5
  40. package/dist/{server-Bp5Nd1pF.d.ts.map → server-DbD7IJVK.d.ts.map} +1 -1
  41. package/dist/server.d.ts +3 -3
  42. package/dist/server.js +1 -1
  43. package/dist/{store-D_yhNdPz.d.ts → store-DyZM6fS5.d.ts} +3 -2
  44. package/dist/{store-D_yhNdPz.d.ts.map → store-DyZM6fS5.d.ts.map} +1 -1
  45. package/dist/store-N8PXxDAS.js.map +1 -1
  46. package/dist/store-memory.d.ts +1 -1
  47. package/dist/store-postgres.d.ts +1 -1
  48. package/dist/store-redis-http.d.ts +1 -1
  49. package/dist/store-redis-http.js +1 -1
  50. package/dist/{store-redis-notify-D2EI6gwX.js → store-redis-notify-BVLUfI3j.js} +14 -13
  51. package/dist/store-redis-notify-BVLUfI3j.js.map +1 -0
  52. package/dist/store-redis.d.ts +1 -1
  53. package/dist/store-redis.js +1 -1
  54. package/dist/store-sqlite.d.ts +1 -1
  55. package/dist/{telemetry-CpeclqB2.d.ts → telemetry-B5jzpy6y.d.ts} +2 -2
  56. package/dist/telemetry-B5jzpy6y.d.ts.map +1 -0
  57. package/docs/concepts/04-state.mdx +9 -4
  58. package/docs/guides/06-ai-agents.mdx +134 -43
  59. package/docs/reference/01-api.mdx +104 -27
  60. package/examples/playground/app/agent/[agentId]/agent-client.tsx +20 -28
  61. package/examples/playground/app/agent/[agentId]/agent-queue.test.tsx +123 -0
  62. package/examples/playground/app/agent/[agentId]/agent-queue.tsx +37 -43
  63. package/examples/playground/app/agent/[agentId]/compaction-panel.tsx +44 -24
  64. package/examples/playground/app/agent/compaction-timeline.test.ts +47 -7
  65. package/examples/playground/app/agent/compaction-timeline.ts +13 -3
  66. package/examples/playground/app/chat/[chatId]/chat-client.tsx +1 -5
  67. package/examples/playground/app/chat/[chatId]/session.ts +17 -2
  68. package/examples/playground/app/chat/model.test.ts +4 -4
  69. package/examples/playground/app/chat/model.ts +18 -6
  70. package/examples/playground/app/chat/server.ts +6 -3
  71. package/examples/playground/package.json +3 -1
  72. package/package.json +1 -1
  73. package/src/ai-client-state.ts +0 -2
  74. package/src/ai-context-schema.ts +296 -0
  75. package/src/ai-context.ts +680 -0
  76. package/src/ai-control-server.ts +160 -131
  77. package/src/ai-control-state.ts +6 -0
  78. package/src/ai-control.ts +6 -7
  79. package/src/ai-coordinator.ts +92 -38
  80. package/src/ai-id.ts +6 -0
  81. package/src/ai-input-tokens.ts +75 -0
  82. package/src/ai-message-projection.ts +348 -0
  83. package/src/ai-server.ts +232 -334
  84. package/src/ai-stored-state.ts +484 -0
  85. package/src/ai.ts +252 -418
  86. package/src/client.ts +21 -15
  87. package/src/react.ts +25 -19
  88. package/src/reducer.ts +2 -1
  89. package/src/server.ts +73 -56
  90. package/src/store-redis-core.ts +15 -17
  91. package/src/store.ts +1 -0
  92. package/src/telemetry.ts +2 -1
  93. package/dist/ai-Cai-lCbj.d.ts.map +0 -1
  94. package/dist/ai-control-CcD4hh3y.js +0 -119
  95. package/dist/ai-control-CcD4hh3y.js.map +0 -1
  96. package/dist/ai.js.map +0 -1
  97. package/dist/client-BAEABRZB.d.ts.map +0 -1
  98. package/dist/index.js.map +0 -1
  99. package/dist/server-CjJSGcF7.js.map +0 -1
  100. package/dist/store-redis-notify-D2EI6gwX.js.map +0 -1
  101. package/dist/telemetry-CpeclqB2.d.ts.map +0 -1
@@ -0,0 +1,348 @@
1
+ import type { UIMessage, UIMessageChunk } from 'ai'
2
+ import type {
3
+ ActiveGenerationProjection,
4
+ ApprovalRequestedPayload,
5
+ ApprovalRespondedPayload,
6
+ ToolCalledPayload,
7
+ ToolResultPayload,
8
+ ToolActivity,
9
+ } from './ai.ts'
10
+ import { type UIMessageCursor, projectUIMessageStream } from './ai-projector.ts'
11
+ import {
12
+ flattenProgressBatches,
13
+ appendProgressBatch,
14
+ progressBatches,
15
+ type ProgressBatches,
16
+ } from './ai-progress-batches.ts'
17
+
18
+ export type MessageProjection<M extends UIMessage> = Omit<
19
+ ActiveGenerationProjection<M>,
20
+ 'baseTools'
21
+ >
22
+
23
+ const isRecord = (value: unknown): value is Record<string, unknown> =>
24
+ typeof value === 'object' && value !== null && !Array.isArray(value)
25
+
26
+ export const upsertMessage = <M extends UIMessage>(
27
+ messages: M[],
28
+ message: M,
29
+ ): M[] => {
30
+ const index = messages.findIndex((candidate) => candidate.id === message.id)
31
+ if (index === -1) return [...messages, message]
32
+ const next = [...messages]
33
+ next[index] = message
34
+ return next
35
+ }
36
+
37
+ export const upsertResponse = <M extends UIMessage>(options: {
38
+ messages: M[]
39
+ message: M
40
+ inputId: string
41
+ }): M[] => {
42
+ const { messages, message, inputId } = options
43
+ if (messages.some((item) => item.id === message.id))
44
+ return upsertMessage(messages, message)
45
+ const inputIndex = messages.findIndex((item) => item.id === inputId)
46
+ if (inputIndex === -1) return [...messages, message]
47
+ return [
48
+ ...messages.slice(0, inputIndex + 1),
49
+ message,
50
+ ...messages.slice(inputIndex + 1),
51
+ ]
52
+ }
53
+
54
+ export const answerApproval = <M extends UIMessage>(
55
+ messages: M[],
56
+ response: ApprovalRespondedPayload,
57
+ ): M[] =>
58
+ messages.map((message) => {
59
+ if (message.id !== response.messageId) return message
60
+ let changed = false
61
+ const parts = message.parts.map((part) => {
62
+ const candidate = part as unknown
63
+ if (
64
+ !isRecord(candidate) ||
65
+ candidate['state'] !== 'approval-requested' ||
66
+ !isRecord(candidate['approval']) ||
67
+ candidate['approval']['id'] !== response.approvalId
68
+ ) {
69
+ return part
70
+ }
71
+ changed = true
72
+ return {
73
+ ...part,
74
+ state: 'approval-responded',
75
+ approval: {
76
+ ...candidate['approval'],
77
+ approved: response.approved,
78
+ ...(response.reason === undefined ? {} : { reason: response.reason }),
79
+ },
80
+ } as typeof part
81
+ })
82
+ return changed ? ({ ...message, parts } as M) : message
83
+ })
84
+
85
+ export const approvalRequestedChunk = (
86
+ payload: ApprovalRequestedPayload,
87
+ ): UIMessageChunk => ({
88
+ type: 'tool-approval-request',
89
+ approvalId: payload.approvalId,
90
+ toolCallId: payload.toolCallId,
91
+ ...(payload.isAutomatic === undefined
92
+ ? {}
93
+ : { isAutomatic: payload.isAutomatic }),
94
+ ...(payload.signature === undefined ? {} : { signature: payload.signature }),
95
+ })
96
+
97
+ export const approvalRespondedChunk = (
98
+ payload: ApprovalRespondedPayload,
99
+ ): UIMessageChunk => ({
100
+ type: 'tool-approval-response',
101
+ approvalId: payload.approvalId,
102
+ approved: payload.approved,
103
+ ...(payload.reason === undefined ? {} : { reason: payload.reason }),
104
+ })
105
+
106
+ export const toolResultChunk = (payload: ToolResultPayload): UIMessageChunk => {
107
+ if (payload.phase === 'input') {
108
+ return {
109
+ type: 'tool-input-error',
110
+ toolCallId: payload.toolCallId,
111
+ toolName: payload.toolName ?? 'unknown',
112
+ input: payload.rawInput ?? payload.input,
113
+ errorText: payload.error ?? 'Invalid tool input',
114
+ ...(payload.dynamic === undefined ? {} : { dynamic: payload.dynamic }),
115
+ ...(payload.providerExecuted === undefined
116
+ ? {}
117
+ : { providerExecuted: payload.providerExecuted }),
118
+ ...(payload.providerMetadata === undefined
119
+ ? {}
120
+ : { providerMetadata: payload.providerMetadata }),
121
+ ...(payload.toolMetadata === undefined
122
+ ? {}
123
+ : { toolMetadata: payload.toolMetadata }),
124
+ } as UIMessageChunk
125
+ }
126
+ if (payload.denied === true) {
127
+ return {
128
+ type: 'tool-output-denied',
129
+ toolCallId: payload.toolCallId,
130
+ } as UIMessageChunk
131
+ }
132
+ if (payload.error !== undefined) {
133
+ return {
134
+ type: 'tool-output-error',
135
+ toolCallId: payload.toolCallId,
136
+ errorText: payload.error,
137
+ ...(payload.dynamic === undefined ? {} : { dynamic: payload.dynamic }),
138
+ ...(payload.providerExecuted === undefined
139
+ ? {}
140
+ : { providerExecuted: payload.providerExecuted }),
141
+ ...(payload.providerMetadata === undefined
142
+ ? {}
143
+ : { providerMetadata: payload.providerMetadata }),
144
+ ...(payload.toolMetadata === undefined
145
+ ? {}
146
+ : { toolMetadata: payload.toolMetadata }),
147
+ } as UIMessageChunk
148
+ }
149
+ return {
150
+ type: 'tool-output-available',
151
+ toolCallId: payload.toolCallId,
152
+ output: payload.output,
153
+ ...(payload.preliminary === undefined
154
+ ? {}
155
+ : { preliminary: payload.preliminary }),
156
+ ...(payload.dynamic === undefined ? {} : { dynamic: payload.dynamic }),
157
+ ...(payload.providerExecuted === undefined
158
+ ? {}
159
+ : { providerExecuted: payload.providerExecuted }),
160
+ ...(payload.providerMetadata === undefined
161
+ ? {}
162
+ : { providerMetadata: payload.providerMetadata }),
163
+ ...(payload.toolMetadata === undefined
164
+ ? {}
165
+ : { toolMetadata: payload.toolMetadata }),
166
+ } as UIMessageChunk
167
+ }
168
+
169
+ export const projectedMessageStream = <M extends UIMessage>(
170
+ projection: MessageProjection<M>,
171
+ ): { message: M; cursor: UIMessageCursor } => {
172
+ const projectionEvents = flattenProgressBatches(projection.batches)
173
+ for (const event of projection.toolEvents) {
174
+ if (event.type === 'ai.tool.result') {
175
+ projectionEvents.push({
176
+ index: event.index,
177
+ chunks: [toolResultChunk(event.payload)],
178
+ })
179
+ }
180
+ }
181
+ for (const event of projection.approvalEvents) {
182
+ projectionEvents.push({
183
+ index: event.index,
184
+ chunks: [
185
+ event.type === 'ai.approval.requested'
186
+ ? approvalRequestedChunk(event.payload)
187
+ : approvalRespondedChunk(event.payload),
188
+ ],
189
+ })
190
+ }
191
+ return projectUIMessageStream({
192
+ messageId: projection.responseMessageId,
193
+ ...(projection.baseMessage === undefined
194
+ ? {}
195
+ : { base: projection.baseMessage }),
196
+ chunks: projectionEvents
197
+ .toSorted((left, right) => left.index - right.index)
198
+ .flatMap((event) => event.chunks),
199
+ })
200
+ }
201
+
202
+ export const projectedMessage = <M extends UIMessage>(
203
+ projection: MessageProjection<M>,
204
+ ): M => projectedMessageStream(projection).message
205
+
206
+ export const reduceToolActivity = (
207
+ tools: ToolActivity[],
208
+ event:
209
+ | { type: 'ai.tool.called'; payload: ToolCalledPayload }
210
+ | { type: 'ai.tool.result'; payload: ToolResultPayload },
211
+ ): ToolActivity[] => {
212
+ if (event.type === 'ai.tool.called') {
213
+ const payload = event.payload
214
+ const { providerMetadata, ...called } = payload
215
+ const activity: ToolActivity = {
216
+ ...called,
217
+ ...(providerMetadata === undefined
218
+ ? {}
219
+ : { callProviderMetadata: providerMetadata }),
220
+ status: 'running',
221
+ }
222
+ return [
223
+ ...tools.filter(
224
+ (candidate) =>
225
+ candidate.generationId !== payload.generationId ||
226
+ candidate.toolCallId !== payload.toolCallId,
227
+ ),
228
+ activity,
229
+ ]
230
+ }
231
+
232
+ const payload = event.payload
233
+ const existing = tools.find(
234
+ (candidate) =>
235
+ candidate.generationId === payload.generationId &&
236
+ candidate.toolCallId === payload.toolCallId,
237
+ )
238
+ const toolName = payload.toolName ?? existing?.toolName
239
+ const input = payload.input === undefined ? existing?.input : payload.input
240
+ const dynamic = payload.dynamic ?? existing?.dynamic
241
+ const providerExecuted =
242
+ payload.providerExecuted ?? existing?.providerExecuted
243
+ const toolMetadata = payload.toolMetadata ?? existing?.toolMetadata
244
+ const resultProviderMetadata =
245
+ payload.providerMetadata ?? existing?.resultProviderMetadata
246
+ const activity: ToolActivity = {
247
+ requestId: payload.requestId,
248
+ messageId: payload.messageId,
249
+ generationId: payload.generationId,
250
+ toolCallId: payload.toolCallId,
251
+ ...(toolName === undefined ? {} : { toolName }),
252
+ ...(input === undefined ? {} : { input }),
253
+ ...(payload.rawInput === undefined ? {} : { rawInput: payload.rawInput }),
254
+ ...(payload.output === undefined ? {} : { output: payload.output }),
255
+ ...(payload.error === undefined ? {} : { error: payload.error }),
256
+ ...(payload.preliminary === undefined
257
+ ? {}
258
+ : { preliminary: payload.preliminary }),
259
+ ...(payload.phase === undefined ? {} : { phase: payload.phase }),
260
+ ...(dynamic === undefined ? {} : { dynamic }),
261
+ ...(providerExecuted === undefined ? {} : { providerExecuted }),
262
+ ...(existing?.callProviderMetadata === undefined
263
+ ? {}
264
+ : { callProviderMetadata: existing.callProviderMetadata }),
265
+ ...(resultProviderMetadata === undefined ? {} : { resultProviderMetadata }),
266
+ ...(toolMetadata === undefined ? {} : { toolMetadata }),
267
+ ...(existing?.title === undefined ? {} : { title: existing.title }),
268
+ status: payload.denied
269
+ ? 'denied'
270
+ : payload.error
271
+ ? 'failed'
272
+ : payload.preliminary
273
+ ? 'running'
274
+ : 'completed',
275
+ }
276
+ return [
277
+ ...tools.filter(
278
+ (candidate) =>
279
+ candidate.generationId !== payload.generationId ||
280
+ candidate.toolCallId !== payload.toolCallId,
281
+ ),
282
+ activity,
283
+ ]
284
+ }
285
+
286
+ export const advanceProjection = <M extends UIMessage>({
287
+ projection,
288
+ base,
289
+ chunks,
290
+ index,
291
+ }: {
292
+ projection: MessageProjection<M>
293
+ base: M | undefined
294
+ chunks: UIMessageChunk[]
295
+ index: number
296
+ }): {
297
+ message: M
298
+ batches: ProgressBatches
299
+ cursor: NonNullable<MessageProjection<M>['cursor']>
300
+ } => {
301
+ const ordered = index > (projection.batches.tail.at(-1)?.index ?? -1)
302
+ const batches = ordered
303
+ ? appendProgressBatch({
304
+ batches: projection.batches,
305
+ batch: { index, chunks },
306
+ })
307
+ : progressBatches(
308
+ [
309
+ ...flattenProgressBatches(projection.batches).filter(
310
+ (batch) => batch.index !== index,
311
+ ),
312
+ { index, chunks },
313
+ ].toSorted((left, right) => left.index - right.index),
314
+ )
315
+ const incremental =
316
+ base !== undefined &&
317
+ projection.cursor != null &&
318
+ index > projection.cursor.throughIndex
319
+ const projected = incremental
320
+ ? projectUIMessageStream({
321
+ messageId: projection.responseMessageId,
322
+ base,
323
+ cursor: projection.cursor!.stream,
324
+ chunks,
325
+ })
326
+ : projectedMessageStream({ ...projection, batches })
327
+ return {
328
+ message: projected.message,
329
+ batches,
330
+ cursor: {
331
+ throughIndex: incremental
332
+ ? index
333
+ : Math.max(
334
+ index,
335
+ batches.tail.at(-1)?.index ?? 0,
336
+ projection.toolEvents.reduce(
337
+ (latest, event) => Math.max(latest, event.index),
338
+ 0,
339
+ ),
340
+ projection.approvalEvents.reduce(
341
+ (latest, event) => Math.max(latest, event.index),
342
+ 0,
343
+ ),
344
+ ),
345
+ stream: projected.cursor,
346
+ },
347
+ }
348
+ }