experimental-a2 0.12.0 → 0.14.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 (106) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/dist/{actor-DJi3RsNu.d.ts → actor-BfQSE0KC.d.ts} +4 -4
  3. package/dist/{actor-DJi3RsNu.d.ts.map → actor-BfQSE0KC.d.ts.map} +1 -1
  4. package/dist/actor-client.d.ts +1 -1
  5. package/dist/actor-client.js +1 -1
  6. package/dist/actor-react.d.ts +3 -3
  7. package/dist/actor-react.js +2 -2
  8. package/dist/{actor-shared-DI7J5upy.js → actor-shared-B5tJfzt-.js} +2 -2
  9. package/dist/{actor-shared-DI7J5upy.js.map → actor-shared-B5tJfzt-.js.map} +1 -1
  10. package/dist/actor.d.ts +1 -1
  11. package/dist/actor.js +3 -3
  12. package/dist/ai-Cai-lCbj.d.ts +580 -0
  13. package/dist/ai-Cai-lCbj.d.ts.map +1 -0
  14. package/dist/ai-control-CcD4hh3y.js +119 -0
  15. package/dist/ai-control-CcD4hh3y.js.map +1 -0
  16. package/dist/ai-server.d.ts +16 -8
  17. package/dist/ai-server.d.ts.map +1 -1
  18. package/dist/ai-server.js +1322 -475
  19. package/dist/ai-server.js.map +1 -1
  20. package/dist/ai.d.ts +2 -334
  21. package/dist/ai.js +838 -85
  22. package/dist/ai.js.map +1 -1
  23. package/dist/{client-P_NNNRM-.d.ts → client-BAEABRZB.d.ts} +2 -2
  24. package/dist/{client-P_NNNRM-.d.ts.map → client-BAEABRZB.d.ts.map} +1 -1
  25. package/dist/{client-Bf6uSEAk.js → client-BYzHjkwU.js} +21 -6
  26. package/dist/client-BYzHjkwU.js.map +1 -0
  27. package/dist/client.d.ts +1 -1
  28. package/dist/client.js +1 -1
  29. package/dist/{contract-48bUMgcL.js → contract-CKRg_E4q.js} +3 -26
  30. package/dist/contract-CKRg_E4q.js.map +1 -0
  31. package/dist/index.d.ts +2 -2
  32. package/dist/index.js +1 -1
  33. package/dist/react.d.ts +2 -2
  34. package/dist/react.js +1 -1
  35. package/dist/{reducer-DJKWm3cp.d.ts → reducer-BcS9VDKC.d.ts} +4 -1
  36. package/dist/{reducer-DJKWm3cp.d.ts.map → reducer-BcS9VDKC.d.ts.map} +1 -1
  37. package/dist/reducer-DEMjEY_O.js +29 -0
  38. package/dist/reducer-DEMjEY_O.js.map +1 -0
  39. package/dist/scheduler-qstash.d.ts +2 -2
  40. package/dist/scheduler-qstash.js +1 -1
  41. package/dist/scheduler-vercel.d.ts +2 -2
  42. package/dist/scheduler-vercel.js +1 -1
  43. package/dist/{server-DjZZa1wr.d.ts → server-Bp5Nd1pF.d.ts} +3 -3
  44. package/dist/{server-DjZZa1wr.d.ts.map → server-Bp5Nd1pF.d.ts.map} +1 -1
  45. package/dist/{server-BeNADlCI.js → server-CjJSGcF7.js} +4 -3
  46. package/dist/server-CjJSGcF7.js.map +1 -0
  47. package/dist/server.d.ts +3 -3
  48. package/dist/server.js +1 -1
  49. package/dist/{store-DtDOWLSn.d.ts → store-D_yhNdPz.d.ts} +7 -2
  50. package/dist/{store-DtDOWLSn.d.ts.map → store-D_yhNdPz.d.ts.map} +1 -1
  51. package/dist/store-N8PXxDAS.js.map +1 -1
  52. package/dist/store-memory.d.ts +1 -1
  53. package/dist/store-postgres.d.ts +1 -1
  54. package/dist/store-postgres.js +19 -0
  55. package/dist/store-postgres.js.map +1 -1
  56. package/dist/store-redis-http.d.ts +1 -1
  57. package/dist/store-redis-http.js +1 -1
  58. package/dist/{store-redis-notify-BUCyXOn0.js → store-redis-notify-D2EI6gwX.js} +27 -2
  59. package/dist/store-redis-notify-D2EI6gwX.js.map +1 -0
  60. package/dist/store-redis.d.ts +1 -1
  61. package/dist/store-redis.js +1 -1
  62. package/dist/store-sqlite.d.ts +1 -1
  63. package/docs/guides/06-ai-agents.mdx +361 -76
  64. package/docs/reference/01-api.mdx +159 -27
  65. package/examples/playground/app/agent/[agentId]/agent-client.tsx +145 -33
  66. package/examples/playground/app/agent/[agentId]/agent-queue.tsx +289 -0
  67. package/examples/playground/app/agent/[agentId]/compaction/route.ts +14 -0
  68. package/examples/playground/app/agent/[agentId]/compaction-event.tsx +38 -0
  69. package/examples/playground/app/agent/[agentId]/compaction-panel.tsx +294 -0
  70. package/examples/playground/app/agent/compaction-settings.test.ts +144 -0
  71. package/examples/playground/app/agent/compaction-settings.ts +49 -0
  72. package/examples/playground/app/agent/compaction-timeline.test.ts +337 -0
  73. package/examples/playground/app/agent/compaction-timeline.ts +198 -0
  74. package/examples/playground/app/agent/model.ts +56 -1
  75. package/examples/playground/app/agent/server.ts +9 -2
  76. package/examples/playground/app/chat/[chatId]/chat-client.tsx +3 -13
  77. package/examples/playground/app/chat/model.ts +2 -2
  78. package/examples/playground/app/chat/server.ts +24 -17
  79. package/examples/playground/app/globals.css +333 -0
  80. package/examples/playground/package.json +1 -1
  81. package/package.json +1 -1
  82. package/src/ai-client-state.ts +185 -0
  83. package/src/ai-control-server.ts +829 -0
  84. package/src/ai-control-state.ts +152 -0
  85. package/src/ai-control.ts +139 -0
  86. package/src/ai-coordinator.ts +99 -32
  87. package/src/ai-model-metadata.ts +108 -0
  88. package/src/ai-progress-batches.ts +68 -0
  89. package/src/ai-projector.ts +76 -15
  90. package/src/ai-sdk-step.ts +5 -2
  91. package/src/ai-server.ts +920 -638
  92. package/src/ai.ts +650 -110
  93. package/src/client.ts +31 -9
  94. package/src/licenses/Apache-2.0.txt +55 -0
  95. package/src/parse-partial-json.ts +441 -0
  96. package/src/reducer.ts +6 -0
  97. package/src/server.ts +8 -4
  98. package/src/store-postgres.ts +27 -0
  99. package/src/store-redis-core.ts +53 -1
  100. package/src/store-redis-notify.ts +1 -0
  101. package/src/store.ts +6 -0
  102. package/dist/ai.d.ts.map +0 -1
  103. package/dist/client-Bf6uSEAk.js.map +0 -1
  104. package/dist/contract-48bUMgcL.js.map +0 -1
  105. package/dist/server-BeNADlCI.js.map +0 -1
  106. package/dist/store-redis-notify-BUCyXOn0.js.map +0 -1
@@ -0,0 +1,68 @@
1
+ import type { UIMessageChunk } from 'ai'
2
+
3
+ export type ProgressBatch = { index: number; chunks: UIMessageChunk[] }
4
+ type BatchBlock = ProgressBatch[] | { left: BatchBlock; right: BatchBlock }
5
+
6
+ export type ProgressBatches = {
7
+ length: number
8
+ tail: ProgressBatch[]
9
+ blocks: Array<BatchBlock | null>
10
+ }
11
+
12
+ const PAGE_SIZE = 64
13
+
14
+ export const appendProgressBatch = ({
15
+ batches,
16
+ batch,
17
+ }: {
18
+ batches: ProgressBatches
19
+ batch: ProgressBatch
20
+ }): ProgressBatches => {
21
+ if (batches.tail.length < PAGE_SIZE)
22
+ return {
23
+ ...batches,
24
+ length: batches.length + 1,
25
+ tail: [...batches.tail, batch],
26
+ }
27
+
28
+ const blocks = [...batches.blocks]
29
+ let block: BatchBlock = batches.tail
30
+ let level = 0
31
+ for (;;) {
32
+ const left = blocks[level]
33
+ if (left == null) break
34
+ block = { left, right: block }
35
+ blocks[level] = null
36
+ level += 1
37
+ }
38
+ blocks[level] = block
39
+ return { length: batches.length + 1, tail: [batch], blocks }
40
+ }
41
+
42
+ export const progressBatches = (
43
+ items: readonly ProgressBatch[] = [],
44
+ ): ProgressBatches => {
45
+ let batches: ProgressBatches = { length: 0, tail: [], blocks: [] }
46
+ for (const batch of items) batches = appendProgressBatch({ batches, batch })
47
+ return batches
48
+ }
49
+
50
+ export const flattenProgressBatches = (
51
+ batches: ProgressBatches,
52
+ ): ProgressBatch[] => {
53
+ const items: ProgressBatch[] = []
54
+ const visit = (block: BatchBlock): void => {
55
+ if (Array.isArray(block)) items.push(...block)
56
+ else {
57
+ visit(block.left)
58
+ visit(block.right)
59
+ }
60
+ }
61
+ // Level n holds 2^n completed pages; higher levels contain older batches.
62
+ for (let level = batches.blocks.length - 1; level >= 0; level -= 1) {
63
+ const block = batches.blocks[level]
64
+ if (block) visit(block)
65
+ }
66
+ items.push(...batches.tail)
67
+ return items
68
+ }
@@ -1,4 +1,5 @@
1
1
  import type { UIMessage, UIMessageChunk } from 'ai'
2
+ import { parsePartialJSON } from './parse-partial-json.ts'
2
3
 
3
4
  type MutablePart = Record<string, unknown>
4
5
 
@@ -16,7 +17,13 @@ const isRecord = (value: unknown): value is Record<string, unknown> =>
16
17
  typeof value === 'object' && value !== null && !Array.isArray(value)
17
18
 
18
19
  const cloneMessage = <M extends UIMessage>(messageId: string, base?: M): M =>
19
- structuredClone(base ?? { id: messageId, role: 'assistant', parts: [] }) as M
20
+ (base === undefined
21
+ ? { id: messageId, role: 'assistant', parts: [] }
22
+ : {
23
+ ...base,
24
+ id: messageId,
25
+ parts: base.parts.map((part) => ({ ...part })),
26
+ }) as M
20
27
 
21
28
  const currentStepParts = (parts: MutablePart[]): MutablePart[] => {
22
29
  let start = parts.length - 1
@@ -118,26 +125,47 @@ const mergeMetadata = (current: unknown, update: unknown): unknown => {
118
125
  : update
119
126
  }
120
127
 
121
- const parseCompleteJSON = (value: string): unknown => {
122
- try {
123
- return JSON.parse(value) as unknown
124
- } catch {
125
- return undefined
126
- }
128
+ export type UIMessageCursor = {
129
+ text: Record<string, number>
130
+ reasoning: Record<string, number>
131
+ tools: Record<string, PartialToolCall>
127
132
  }
128
133
 
129
- /** Internal synchronous counterpart of the AI SDK UI message stream fold. */
130
134
  export function projectUIMessage<M extends UIMessage>(options: {
131
135
  messageId: string
132
136
  base?: M
133
137
  chunks: readonly UIMessageChunk[]
134
138
  }): M {
139
+ return projectUIMessageStream(options).message
140
+ }
141
+
142
+ export function projectUIMessageStream<M extends UIMessage>(options: {
143
+ messageId: string
144
+ base?: M
145
+ cursor?: UIMessageCursor
146
+ chunks: readonly UIMessageChunk[]
147
+ }): { message: M; cursor: UIMessageCursor } {
135
148
  const message = cloneMessage(options.messageId, options.base)
136
149
  const record = message as unknown as Record<string, unknown>
137
150
  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>()
151
+ const activeText = new Map(
152
+ Object.entries(options.cursor?.text ?? {}).map(([id, index]) => [
153
+ id,
154
+ parts[index]!,
155
+ ]),
156
+ )
157
+ const activeReasoning = new Map(
158
+ Object.entries(options.cursor?.reasoning ?? {}).map(([id, index]) => [
159
+ id,
160
+ parts[index]!,
161
+ ]),
162
+ )
163
+ const partialTools = new Map(
164
+ Object.entries(options.cursor?.tools ?? {}).map(([id, partial]) => [
165
+ id,
166
+ { ...partial },
167
+ ]),
168
+ )
141
169
 
142
170
  for (const chunk of options.chunks) {
143
171
  switch (chunk.type) {
@@ -291,7 +319,7 @@ export function projectUIMessage<M extends UIMessage>(options: {
291
319
  toolName: partial.toolName,
292
320
  dynamic: partial.dynamic,
293
321
  state: 'input-streaming',
294
- input: parseCompleteJSON(partial.text),
322
+ input: parsePartialJSON(partial.text),
295
323
  providerExecuted: partial.providerExecuted,
296
324
  providerMetadata: partial.providerMetadata,
297
325
  toolMetadata: partial.toolMetadata,
@@ -424,7 +452,6 @@ export function projectUIMessage<M extends UIMessage>(options: {
424
452
  activeReasoning.clear()
425
453
  break
426
454
  case 'start':
427
- if (chunk.messageId !== undefined) record['id'] = chunk.messageId
428
455
  if (
429
456
  chunk.messageMetadata !== undefined &&
430
457
  chunk.messageMetadata !== null
@@ -480,7 +507,38 @@ export function projectUIMessage<M extends UIMessage>(options: {
480
507
  }
481
508
  }
482
509
 
483
- return message
510
+ const indexes = new Map(parts.map((part, index) => [part, index]))
511
+ return {
512
+ message,
513
+ cursor: {
514
+ text: Object.fromEntries(
515
+ [...activeText].map(([id, part]) => [id, indexes.get(part)!]),
516
+ ),
517
+ reasoning: Object.fromEntries(
518
+ [...activeReasoning].map(([id, part]) => [id, indexes.get(part)!]),
519
+ ),
520
+ tools: Object.fromEntries(partialTools),
521
+ },
522
+ }
523
+ }
524
+
525
+ export function pauseUIMessage<M extends UIMessage>(options: {
526
+ message: M
527
+ toolCallIds: ReadonlySet<string>
528
+ }): M {
529
+ const next = structuredClone(options.message)
530
+ next.parts = next.parts.filter(
531
+ (part) =>
532
+ !('toolCallId' in part && !options.toolCallIds.has(part.toolCallId)),
533
+ )
534
+ for (const part of next.parts) {
535
+ if (
536
+ (part.type === 'text' || part.type === 'reasoning') &&
537
+ part.state === 'streaming'
538
+ )
539
+ part.state = 'done'
540
+ }
541
+ return next
484
542
  }
485
543
 
486
544
  export function interruptUIMessage<M extends UIMessage>(
@@ -488,6 +546,10 @@ export function interruptUIMessage<M extends UIMessage>(
488
546
  toolError = 'Tool execution was interrupted.',
489
547
  ): M {
490
548
  const next = structuredClone(message)
549
+ next.parts = next.parts.filter((value) => {
550
+ const part = value as unknown as MutablePart
551
+ return !isToolPart(part) || part['state'] !== 'input-streaming'
552
+ })
491
553
  for (const value of next.parts as unknown as MutablePart[]) {
492
554
  if (value['type'] === 'text' || value['type'] === 'reasoning') {
493
555
  if (value['state'] === 'streaming') value['state'] = 'done'
@@ -510,7 +572,6 @@ export function interruptUIMessage<M extends UIMessage>(
510
572
  continue
511
573
  }
512
574
  if (
513
- state === 'input-streaming' ||
514
575
  state === 'input-available' ||
515
576
  state === 'approval-requested' ||
516
577
  state === 'approval-responded'
@@ -10,6 +10,7 @@ import type {
10
10
  Instructions,
11
11
  LanguageModel,
12
12
  LanguageModelUsage,
13
+ ModelMessage,
13
14
  TextStreamPart,
14
15
  ToolSet,
15
16
  UIMessage,
@@ -127,6 +128,7 @@ export type AISDKStepInput<M extends UIMessage, T extends ToolSet> = {
127
128
  model: LanguageModel
128
129
  tools: T
129
130
  messages: M[]
131
+ modelMessages?: ModelMessage[]
130
132
  responseMessageId: string
131
133
  abortSignal: AbortSignal
132
134
  instructions?: Instructions
@@ -201,7 +203,9 @@ export async function generateAISDKStep<M extends UIMessage, T extends ToolSet>(
201
203
  ): Promise<AISDKStepResult<M>> {
202
204
  const settings = safeSettings(input.settings)
203
205
  const tools = modelToolSet(input.tools)
204
- const messages = await convertToModelMessages(input.messages, { tools })
206
+ const messages =
207
+ input.modelMessages ??
208
+ (await convertToModelMessages(input.messages, { tools }))
205
209
  const streamOptions = {
206
210
  ...settings,
207
211
  model: input.model,
@@ -240,7 +244,6 @@ export async function generateAISDKStep<M extends UIMessage, T extends ToolSet>(
240
244
  stream: toUIMessageStream<T, M>({
241
245
  stream: observed,
242
246
  tools,
243
- originalMessages: input.messages,
244
247
  generateMessageId: () => input.responseMessageId,
245
248
  onError: errorMessage,
246
249
  ...(input.messageMetadata === undefined