experimental-a2 0.13.0 → 0.14.1

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 (99) hide show
  1. package/CHANGELOG.md +43 -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 +6 -6
  17. package/dist/ai-server.d.ts.map +1 -1
  18. package/dist/ai-server.js +1053 -513
  19. package/dist/ai-server.js.map +1 -1
  20. package/dist/ai.d.ts +2 -365
  21. package/dist/ai.js +801 -80
  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 +287 -65
  64. package/docs/reference/01-api.mdx +122 -27
  65. package/examples/playground/app/agent/[agentId]/agent-client.tsx +118 -21
  66. package/examples/playground/app/agent/[agentId]/agent-queue.tsx +289 -0
  67. package/examples/playground/app/agent/compaction-settings.test.ts +22 -6
  68. package/examples/playground/app/agent/model.ts +11 -2
  69. package/examples/playground/app/agent/server.ts +8 -2
  70. package/examples/playground/app/chat/[chatId]/chat-client.tsx +3 -13
  71. package/examples/playground/app/chat/model.ts +2 -2
  72. package/examples/playground/app/chat/server.ts +24 -17
  73. package/examples/playground/app/globals.css +179 -0
  74. package/examples/playground/package.json +1 -1
  75. package/package.json +1 -1
  76. package/src/ai-client-state.ts +185 -0
  77. package/src/ai-control-server.ts +829 -0
  78. package/src/ai-control-state.ts +152 -0
  79. package/src/ai-control.ts +139 -0
  80. package/src/ai-coordinator.ts +99 -32
  81. package/src/ai-progress-batches.ts +68 -0
  82. package/src/ai-projector.ts +76 -15
  83. package/src/ai-sdk-step.ts +0 -1
  84. package/src/ai-server.ts +429 -619
  85. package/src/ai.ts +553 -108
  86. package/src/client.ts +31 -9
  87. package/src/licenses/Apache-2.0.txt +55 -0
  88. package/src/parse-partial-json.ts +441 -0
  89. package/src/reducer.ts +6 -0
  90. package/src/server.ts +8 -4
  91. package/src/store-postgres.ts +27 -0
  92. package/src/store-redis-core.ts +53 -1
  93. package/src/store-redis-notify.ts +1 -0
  94. package/src/store.ts +6 -0
  95. package/dist/ai.d.ts.map +0 -1
  96. package/dist/client-Bf6uSEAk.js.map +0 -1
  97. package/dist/contract-48bUMgcL.js.map +0 -1
  98. package/dist/server-BeNADlCI.js.map +0 -1
  99. package/dist/store-redis-notify-BUCyXOn0.js.map +0 -1
@@ -22,7 +22,7 @@
22
22
  "@vercel/sandbox": "^3.0.0",
23
23
  "ai": "^7.0.58",
24
24
  "codemirror": "^6.0.2",
25
- "experimental-a2": "0.13.0",
25
+ "experimental-a2": "0.14.1",
26
26
  "ioredis": "^5.9.0",
27
27
  "next": "^16.3.0",
28
28
  "pg": "^8.16.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "experimental-a2",
3
- "version": "0.13.0",
3
+ "version": "0.14.1",
4
4
  "description": "Durable sync and reactions for things with a lifecycle: one event log, derived state, and live client per session.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -0,0 +1,185 @@
1
+ import type { UIMessage } from 'ai'
2
+ import type { AIState } from './ai.ts'
3
+ import type { ControlCommand, InboxItem } from './ai-control.ts'
4
+
5
+ export type QueueCommand<M extends UIMessage> = Extract<
6
+ ControlCommand<M>,
7
+ {
8
+ action:
9
+ | 'send'
10
+ | 'edit'
11
+ | 'move'
12
+ | 'remove'
13
+ | 'send-now'
14
+ | 'steer'
15
+ | 'pause'
16
+ | 'resume'
17
+ }
18
+ >
19
+ export type PendingQueueCommand<M extends UIMessage> = {
20
+ commandId: string
21
+ command: QueueCommand<M>
22
+ }
23
+
24
+ export const isQueueCommand = <M extends UIMessage>(
25
+ command: ControlCommand<M>,
26
+ ): command is QueueCommand<M> =>
27
+ command.action === 'send' ||
28
+ command.action === 'edit' ||
29
+ command.action === 'move' ||
30
+ command.action === 'remove' ||
31
+ command.action === 'send-now' ||
32
+ command.action === 'steer' ||
33
+ command.action === 'pause' ||
34
+ command.action === 'resume'
35
+
36
+ const moveItem = <M extends UIMessage>({
37
+ items,
38
+ inputId,
39
+ beforeId,
40
+ }: {
41
+ items: InboxItem<M>[]
42
+ inputId: string
43
+ beforeId: string | null
44
+ }): InboxItem<M>[] => {
45
+ const from = items.findIndex((item) => item.id === inputId)
46
+ if (
47
+ from < 0 ||
48
+ inputId === beforeId ||
49
+ (beforeId !== null && !items.some((item) => item.id === beforeId))
50
+ )
51
+ return items
52
+ const next = [...items]
53
+ const [item] = next.splice(from, 1)
54
+ const to =
55
+ beforeId === null
56
+ ? next.length
57
+ : next.findIndex((entry) => entry.id === beforeId)
58
+ next.splice(to, 0, item!)
59
+ return next
60
+ }
61
+
62
+ export function projectAIClientState<M extends UIMessage>(
63
+ state: AIState<M>,
64
+ ): AIState<M> {
65
+ const pending = state.pendingQueueCommands ?? []
66
+ if (state.status === 'closed') return state
67
+ if (
68
+ pending.length === 0 &&
69
+ !state.inbox.items.some((item) => item.afterStepOf !== undefined)
70
+ )
71
+ return state
72
+ let messages = state.messages
73
+ let { items, paused } = state.inbox
74
+ let turnId = state.active?.turnId ?? null
75
+ let starting = false
76
+ let suspended =
77
+ state.active?.phase === 'paused' || state.active?.phase === 'pausing'
78
+ let messageIds: Set<string> | undefined
79
+ const show = (message: M): void => {
80
+ messageIds ??= new Set(messages.map((item) => item.id))
81
+ if (messageIds.has(message.id)) return
82
+ if (messages === state.messages) messages = state.messages.slice()
83
+ messages.push(message)
84
+ messageIds.add(message.id)
85
+ }
86
+ const admit = (): void => {
87
+ if (turnId !== null || paused || items.length === 0) return
88
+ let count = 0
89
+ for (const item of items) {
90
+ count += 1
91
+ show(item.message)
92
+ if (item.generate) {
93
+ turnId = item.id
94
+ starting = true
95
+ suspended = false
96
+ break
97
+ }
98
+ }
99
+ if (count > 0) items = items.slice(count)
100
+ }
101
+ for (const { command } of pending) {
102
+ switch (command.action) {
103
+ case 'send':
104
+ case 'steer': {
105
+ if (
106
+ items.some((item) => item.id === command.message.id) ||
107
+ turnId === command.message.id
108
+ )
109
+ break
110
+ const item = {
111
+ ...(command.action === 'steer'
112
+ ? { afterStepOf: command.turnId }
113
+ : {}),
114
+ id: command.message.id,
115
+ revision: 0,
116
+ message: command.message,
117
+ generate:
118
+ command.message.role === 'user' &&
119
+ (command.action === 'steer' || command.generate !== false),
120
+ }
121
+ items = command.action === 'steer' ? [item, ...items] : [...items, item]
122
+ break
123
+ }
124
+ case 'edit':
125
+ items = items.map((item) =>
126
+ item.id === command.message.id &&
127
+ item.revision === command.expectedRevision &&
128
+ item.message.role === command.message.role
129
+ ? { ...item, message: command.message }
130
+ : item,
131
+ )
132
+ break
133
+ case 'move':
134
+ items = moveItem({
135
+ items,
136
+ inputId: command.inputId,
137
+ beforeId: command.beforeId,
138
+ })
139
+ break
140
+ case 'remove':
141
+ items = items.filter((item) => item.id !== command.inputId)
142
+ break
143
+ case 'send-now': {
144
+ if (turnId !== command.turnId) break
145
+ const selected = items.find((item) => item.id === command.inputId)
146
+ if (!selected || selected.message.role !== 'user') break
147
+ items = items.map((item) =>
148
+ item.id === selected.id
149
+ ? {
150
+ ...item,
151
+ generate: true,
152
+ ...(turnId === null ? {} : { afterStepOf: turnId }),
153
+ }
154
+ : item,
155
+ )
156
+ items = moveItem({
157
+ items,
158
+ inputId: command.inputId,
159
+ beforeId: items[0]?.id ?? null,
160
+ })
161
+ break
162
+ }
163
+ case 'pause':
164
+ paused = true
165
+ if (command.when === 'now') suspended = true
166
+ break
167
+ case 'resume':
168
+ paused = false
169
+ suspended = false
170
+ break
171
+ }
172
+ admit()
173
+ }
174
+ items = items.filter((item) => {
175
+ if (item.afterStepOf === undefined) return true
176
+ show(item.message)
177
+ return false
178
+ })
179
+ return {
180
+ ...state,
181
+ messages,
182
+ inbox: { items, paused },
183
+ starting: starting && !suspended,
184
+ }
185
+ }