experimental-a2 0.4.0 → 0.5.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 (182) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/dist/{ai-B4YhEnfw.d.ts → ai-D_PGS-JR.d.ts} +3 -2
  3. package/dist/ai-D_PGS-JR.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 +4 -3
  7. package/dist/ai-server.d.ts.map +1 -0
  8. package/dist/ai-server.js +4 -2
  9. package/dist/ai-server.js.map +1 -0
  10. package/dist/ai.d.ts +1 -1
  11. package/dist/ai.js +3 -1
  12. package/dist/ai.js.map +1 -0
  13. package/dist/cli-B3VuxoDe.js +2 -0
  14. package/dist/cli-B3VuxoDe.js.map +1 -0
  15. package/dist/cli-bin.js +2 -0
  16. package/dist/cli-bin.js.map +1 -0
  17. package/dist/cli.d.ts +2 -1
  18. package/dist/cli.d.ts.map +1 -0
  19. package/dist/{client-Bt4tAKi9.js → client-BKlyLiOU.js} +295 -85
  20. package/dist/client-BKlyLiOU.js.map +1 -0
  21. package/dist/{client-BrfDXQ8A.d.ts → client-D7mvIXrF.d.ts} +40 -4
  22. package/dist/client-D7mvIXrF.d.ts.map +1 -0
  23. package/dist/client.d.ts +2 -2
  24. package/dist/client.js +1 -1
  25. package/dist/contract-48bUMgcL.js +2 -0
  26. package/dist/contract-48bUMgcL.js.map +1 -0
  27. package/dist/contract-jIfaR085.d.ts +2 -1
  28. package/dist/contract-jIfaR085.d.ts.map +1 -0
  29. package/dist/devtools-J_jZ2vQf.d.ts +2 -1
  30. package/dist/devtools-J_jZ2vQf.d.ts.map +1 -0
  31. package/dist/devtools-kJJaORn-.js +2 -0
  32. package/dist/devtools-kJJaORn-.js.map +1 -0
  33. package/dist/devtools-server.browser.js +2 -0
  34. package/dist/devtools-server.browser.js.map +1 -0
  35. package/dist/devtools-server.d.ts +2 -1
  36. package/dist/devtools-server.d.ts.map +1 -0
  37. package/dist/devtools-server.js +2 -0
  38. package/dist/devtools-server.js.map +1 -0
  39. package/dist/errors-BQuJpe82.js +2 -0
  40. package/dist/errors-BQuJpe82.js.map +1 -0
  41. package/dist/errors-W6nwJ-fm.d.ts +2 -1
  42. package/dist/errors-W6nwJ-fm.d.ts.map +1 -0
  43. package/dist/http.d.ts +121 -72
  44. package/dist/http.d.ts.map +1 -0
  45. package/dist/http.js +503 -178
  46. package/dist/http.js.map +1 -0
  47. package/dist/idempotent-replay-DuqEkYA7.js +2 -0
  48. package/dist/idempotent-replay-DuqEkYA7.js.map +1 -0
  49. package/dist/index.d.ts +2 -2
  50. package/dist/inspection-DaxB5jM2.js +2 -0
  51. package/dist/inspection-DaxB5jM2.js.map +1 -0
  52. package/dist/{internal-aEotMzu_.js → internal-DstsI6Re.js} +3 -1
  53. package/dist/internal-DstsI6Re.js.map +1 -0
  54. package/dist/otel.d.ts +3 -2
  55. package/dist/otel.d.ts.map +1 -0
  56. package/dist/otel.js +2 -0
  57. package/dist/otel.js.map +1 -0
  58. package/dist/platform-B4TnJtWu.js +2 -0
  59. package/dist/platform-B4TnJtWu.js.map +1 -0
  60. package/dist/react.d.ts +12 -3
  61. package/dist/react.d.ts.map +1 -0
  62. package/dist/react.js +5 -1
  63. package/dist/react.js.map +1 -0
  64. package/dist/retryable-lazy-DZWmHpii.js +2 -0
  65. package/dist/retryable-lazy-DZWmHpii.js.map +1 -0
  66. package/dist/scheduler-qstash.d.ts +4 -3
  67. package/dist/scheduler-qstash.d.ts.map +1 -0
  68. package/dist/scheduler-qstash.js +4 -2
  69. package/dist/scheduler-qstash.js.map +1 -0
  70. package/dist/scheduler-task-BpzhPnRS.js +2 -0
  71. package/dist/scheduler-task-BpzhPnRS.js.map +1 -0
  72. package/dist/scheduler-vercel.d.ts +4 -3
  73. package/dist/scheduler-vercel.d.ts.map +1 -0
  74. package/dist/scheduler-vercel.js +4 -2
  75. package/dist/scheduler-vercel.js.map +1 -0
  76. package/dist/{server-YtPq7hjw.d.ts → server-DpvjhdoE.d.ts} +6 -5
  77. package/dist/server-DpvjhdoE.d.ts.map +1 -0
  78. package/dist/{server-CcNnFnoW.js → server-Duw6MVlB.js} +112 -54
  79. package/dist/server-Duw6MVlB.js.map +1 -0
  80. package/dist/server.browser.js +2 -0
  81. package/dist/server.browser.js.map +1 -0
  82. package/dist/server.d.ts +2 -2
  83. package/dist/server.js +1 -1
  84. package/dist/{store-C3sNAaBT.d.ts → store-DysUkTH3.d.ts} +10 -1
  85. package/dist/store-DysUkTH3.d.ts.map +1 -0
  86. package/dist/store-N8PXxDAS.js +2 -0
  87. package/dist/store-N8PXxDAS.js.map +1 -0
  88. package/dist/store-codec-DTG0Ftek.js +2 -0
  89. package/dist/store-codec-DTG0Ftek.js.map +1 -0
  90. package/dist/store-memory.d.ts +3 -2
  91. package/dist/store-memory.d.ts.map +1 -0
  92. package/dist/store-memory.js +19 -11
  93. package/dist/store-memory.js.map +1 -0
  94. package/dist/{store-polling-DgrrAE3d.js → store-polling-dSeLxzfb.js} +3 -1
  95. package/dist/store-polling-dSeLxzfb.js.map +1 -0
  96. package/dist/store-postgres.d.ts +3 -2
  97. package/dist/store-postgres.d.ts.map +1 -0
  98. package/dist/store-postgres.js +57 -1
  99. package/dist/store-postgres.js.map +1 -0
  100. package/dist/{store-redis-core-DWqx3F47.js → store-redis-core-BFLwz0Wj.js} +3 -1
  101. package/dist/store-redis-core-BFLwz0Wj.js.map +1 -0
  102. package/dist/store-redis-http.d.ts +3 -2
  103. package/dist/store-redis-http.d.ts.map +1 -0
  104. package/dist/store-redis-http.js +4 -2
  105. package/dist/store-redis-http.js.map +1 -0
  106. package/dist/store-redis.d.ts +3 -2
  107. package/dist/store-redis.d.ts.map +1 -0
  108. package/dist/store-redis.js +5 -3
  109. package/dist/store-redis.js.map +1 -0
  110. package/dist/store-sqlite.d.ts +3 -2
  111. package/dist/store-sqlite.d.ts.map +1 -0
  112. package/dist/store-sqlite.js +3 -1
  113. package/dist/store-sqlite.js.map +1 -0
  114. package/dist/{telemetry-BjYHTfh2.d.ts → telemetry-CpeclqB2.d.ts} +4 -3
  115. package/dist/telemetry-CpeclqB2.d.ts.map +1 -0
  116. package/dist/testing.browser.js +2 -0
  117. package/dist/testing.browser.js.map +1 -0
  118. package/dist/testing.d.ts +2 -1
  119. package/dist/testing.d.ts.map +1 -0
  120. package/dist/testing.js +2 -0
  121. package/dist/testing.js.map +1 -0
  122. package/dist/validate-XKT4FSNn.js +2 -0
  123. package/dist/validate-XKT4FSNn.js.map +1 -0
  124. package/dist/{wire-DCUZBUlT.js → wire-BFQmSJ-9.js} +77 -15
  125. package/dist/wire-BFQmSJ-9.js.map +1 -0
  126. package/docs/guides/03-react.mdx +59 -39
  127. package/docs/guides/06-ai-agents.mdx +5 -27
  128. package/docs/guides/09-presence.mdx +19 -40
  129. package/docs/guides/10-transports.mdx +49 -40
  130. package/docs/reference/01-api.mdx +107 -26
  131. package/docs/reference/02-errors.mdx +4 -2
  132. package/package.json +2 -1
  133. package/src/ai-coordinator.ts +358 -0
  134. package/src/ai-projector.ts +524 -0
  135. package/src/ai-sdk-step.ts +261 -0
  136. package/src/ai-server.browser.ts +5 -0
  137. package/src/ai-server.ts +1719 -0
  138. package/src/ai.ts +2155 -0
  139. package/src/cache-indexeddb.ts +10 -0
  140. package/src/cli-bin.ts +5 -0
  141. package/src/cli.ts +1046 -0
  142. package/src/client.ts +1826 -0
  143. package/src/contract.ts +206 -0
  144. package/src/deterministic-id.ts +72 -0
  145. package/src/devtools-app.ts +989 -0
  146. package/src/devtools-server.browser.ts +5 -0
  147. package/src/devtools-server.ts +604 -0
  148. package/src/devtools.ts +716 -0
  149. package/src/errors.ts +50 -0
  150. package/src/http.ts +394 -0
  151. package/src/idempotent-replay.ts +53 -0
  152. package/src/index.ts +37 -0
  153. package/src/inspection.ts +39 -0
  154. package/src/internal.ts +426 -0
  155. package/src/otel.ts +59 -0
  156. package/src/platform.ts +60 -0
  157. package/src/push-envelope.ts +137 -0
  158. package/src/react.ts +284 -0
  159. package/src/reducer.ts +108 -0
  160. package/src/retryable-lazy.ts +27 -0
  161. package/src/scheduler-qstash.ts +915 -0
  162. package/src/scheduler-task.ts +106 -0
  163. package/src/scheduler-vercel.ts +437 -0
  164. package/src/server.browser.ts +12 -0
  165. package/src/server.ts +2700 -0
  166. package/src/session-socket.ts +548 -0
  167. package/src/sse.ts +141 -0
  168. package/src/standard-schema.ts +77 -0
  169. package/src/store-codec.ts +10 -0
  170. package/src/store-memory.ts +788 -0
  171. package/src/store-polling.ts +102 -0
  172. package/src/store-postgres.ts +1212 -0
  173. package/src/store-redis-core.ts +1494 -0
  174. package/src/store-redis-http.ts +116 -0
  175. package/src/store-redis.ts +458 -0
  176. package/src/store-sqlite.ts +1108 -0
  177. package/src/store.ts +385 -0
  178. package/src/telemetry.ts +54 -0
  179. package/src/testing.browser.ts +5 -0
  180. package/src/testing.ts +185 -0
  181. package/src/validate.ts +39 -0
  182. package/src/wire.ts +454 -0
@@ -0,0 +1,358 @@
1
+ import type { UIMessage } from 'ai'
2
+ import type {
3
+ AIEventDefs,
4
+ ApprovalRequestedPayload,
5
+ ApprovalRespondedPayload,
6
+ GenerationCompletedPayload,
7
+ GenerationFailedPayload,
8
+ GenerationRequestedPayload,
9
+ GenerationStartedPayload,
10
+ InputRequestedPayload,
11
+ MessageCreatedPayload,
12
+ MessageInterruptedPayload,
13
+ ToolCalledPayload,
14
+ ToolResultPayload,
15
+ } from './ai.ts'
16
+ import type { Contract, ContractEvent, EventDefs } from './contract.ts'
17
+ import type { Reducer } from './reducer.ts'
18
+
19
+ export type CoordinatorCall = {
20
+ index: number
21
+ call: ToolCalledPayload
22
+ approval?: ApprovalRequestedPayload
23
+ response?: ApprovalRespondedPayload
24
+ responseIndex?: number
25
+ terminal: boolean
26
+ }
27
+
28
+ export type CoordinatorResponse = {
29
+ rootMessageId: string
30
+ responseMessageId: string
31
+ status: 'requested' | 'generating' | 'waiting' | 'failed'
32
+ activeRequestId?: string
33
+ generation?: GenerationStartedPayload
34
+ completion?: GenerationCompletedPayload
35
+ failure?: GenerationFailedPayload
36
+ calls: CoordinatorCall[]
37
+ inputs: InputRequestedPayload[]
38
+ inputResponse?: {
39
+ index: number
40
+ generationId: string
41
+ inputId: string
42
+ name: string
43
+ }
44
+ }
45
+
46
+ export type AICoordinatorState = {
47
+ closed: boolean
48
+ queued: Array<{ index: number; messageId: string; generate: boolean }>
49
+ response?: CoordinatorResponse
50
+ }
51
+
52
+ const updateResponse = (
53
+ state: AICoordinatorState,
54
+ update: (response: CoordinatorResponse) => CoordinatorResponse,
55
+ ): AICoordinatorState =>
56
+ state.response === undefined
57
+ ? state
58
+ : { ...state, response: update(state.response) }
59
+
60
+ const foldCoordinator = <M extends UIMessage, D extends EventDefs>(
61
+ state: AICoordinatorState,
62
+ event: ContractEvent<D>,
63
+ ): AICoordinatorState => {
64
+ switch (event.type) {
65
+ case 'ai.session.closed':
66
+ return { closed: true, queued: [] }
67
+ case 'ai.message.created': {
68
+ const payload = event.payload as MessageCreatedPayload<M>
69
+ const message = payload.message
70
+ if (message.role !== 'user') return state
71
+ return {
72
+ ...state,
73
+ queued: [
74
+ ...state.queued.filter((item) => item.messageId !== message.id),
75
+ {
76
+ index: event.index,
77
+ messageId: message.id,
78
+ generate: payload.generate !== false,
79
+ },
80
+ ].toSorted((left, right) => left.index - right.index),
81
+ }
82
+ }
83
+ case 'ai.generation.requested': {
84
+ const request = event.payload as GenerationRequestedPayload
85
+ if (request.reason === 'message') {
86
+ const responseMessageId =
87
+ request.responseMessageId ?? `${request.messageId}:assistant`
88
+ const requested = state.queued.find(
89
+ (item) => item.messageId === request.messageId,
90
+ )
91
+ return {
92
+ ...state,
93
+ queued:
94
+ requested === undefined
95
+ ? state.queued.filter(
96
+ (item) => item.messageId !== request.messageId,
97
+ )
98
+ : state.queued.filter((item) => item.index > requested.index),
99
+ response: {
100
+ rootMessageId: request.messageId,
101
+ responseMessageId,
102
+ status: 'requested',
103
+ activeRequestId: event.id,
104
+ calls: [],
105
+ inputs: [],
106
+ },
107
+ }
108
+ }
109
+ if (
110
+ state.response === undefined ||
111
+ request.responseMessageId !== state.response.responseMessageId
112
+ ) {
113
+ return state
114
+ }
115
+ if (
116
+ (request.reason === 'tool' &&
117
+ (!continuationReady(state) ||
118
+ event.id !==
119
+ `ai.generate:tools:${state.response.generation?.generationId}`)) ||
120
+ (request.reason === 'retry' && state.response.status !== 'failed') ||
121
+ (request.reason === 'input' &&
122
+ state.response.inputResponse === undefined)
123
+ ) {
124
+ return state
125
+ }
126
+ return updateResponse(state, (response) => ({
127
+ rootMessageId: response.rootMessageId,
128
+ responseMessageId: response.responseMessageId,
129
+ status: 'requested',
130
+ activeRequestId: event.id,
131
+ calls: [],
132
+ inputs: [],
133
+ }))
134
+ }
135
+ case 'ai.generation.started': {
136
+ const generation = event.payload as GenerationStartedPayload
137
+ if (
138
+ state.response?.responseMessageId !== generation.responseMessageId ||
139
+ state.response.activeRequestId !== generation.requestId ||
140
+ (state.response.generation?.requestId === generation.requestId &&
141
+ state.response.generation.attempt >= generation.attempt)
142
+ ) {
143
+ return state
144
+ }
145
+ return updateResponse(state, (response) => ({
146
+ rootMessageId: response.rootMessageId,
147
+ responseMessageId: response.responseMessageId,
148
+ status: 'generating',
149
+ generation,
150
+ activeRequestId: generation.requestId,
151
+ calls: [],
152
+ inputs: [],
153
+ }))
154
+ }
155
+ case 'ai.generation.completed': {
156
+ const completion = event.payload as GenerationCompletedPayload
157
+ if (
158
+ state.response?.generation?.generationId !== completion.generationId
159
+ ) {
160
+ return state
161
+ }
162
+ return updateResponse(state, (response) => ({
163
+ ...response,
164
+ status: 'waiting',
165
+ completion,
166
+ }))
167
+ }
168
+ case 'ai.generation.failed': {
169
+ const failure = event.payload as GenerationFailedPayload
170
+ const response = state.response
171
+ const ownsActiveGeneration =
172
+ response?.generation?.generationId === failure.generationId
173
+ const ownsActiveStepLimit =
174
+ failure.stepLimit === true &&
175
+ response?.activeRequestId ===
176
+ `ai.generate:tools:${failure.generationId}` &&
177
+ response.responseMessageId === failure.responseMessageId
178
+ if (!ownsActiveGeneration && !ownsActiveStepLimit) {
179
+ return state
180
+ }
181
+ return updateResponse(state, (current) => ({
182
+ ...current,
183
+ status: 'failed',
184
+ failure,
185
+ }))
186
+ }
187
+ case 'ai.message.completed': {
188
+ const messageId = (event.payload as { messageId: string }).messageId
189
+ return state.response?.responseMessageId === messageId
190
+ ? { closed: state.closed, queued: state.queued }
191
+ : state
192
+ }
193
+ case 'ai.message.interrupted': {
194
+ const interruption = event.payload as MessageInterruptedPayload
195
+ const response = state.response
196
+ return response?.responseMessageId === interruption.messageId &&
197
+ (interruption.generationId === response.generation?.generationId ||
198
+ interruption.requestId === response.activeRequestId)
199
+ ? { closed: state.closed, queued: state.queued }
200
+ : state
201
+ }
202
+ case 'ai.tool.called': {
203
+ const call = event.payload as ToolCalledPayload
204
+ if (state.response?.generation?.generationId !== call.generationId) {
205
+ return state
206
+ }
207
+ return updateResponse(state, (response) => ({
208
+ ...response,
209
+ calls: [
210
+ ...response.calls.filter(
211
+ (candidate) => candidate.call.toolCallId !== call.toolCallId,
212
+ ),
213
+ { index: event.index, call, terminal: false },
214
+ ],
215
+ }))
216
+ }
217
+ case 'ai.approval.requested': {
218
+ const approval = event.payload as ApprovalRequestedPayload
219
+ if (state.response?.generation?.generationId !== approval.generationId) {
220
+ return state
221
+ }
222
+ return updateResponse(state, (response) => ({
223
+ ...response,
224
+ calls: response.calls.map((candidate) =>
225
+ candidate.call.toolCallId === approval.toolCallId
226
+ ? { ...candidate, approval }
227
+ : candidate,
228
+ ),
229
+ }))
230
+ }
231
+ case 'ai.approval.responded': {
232
+ const approval = event.payload as ApprovalRespondedPayload
233
+ if (
234
+ state.response?.responseMessageId !== approval.messageId ||
235
+ state.response.generation?.generationId !== approval.generationId
236
+ ) {
237
+ return state
238
+ }
239
+ return updateResponse(state, (response) => ({
240
+ ...response,
241
+ calls: response.calls.map((candidate) =>
242
+ candidate.approval?.approvalId === approval.approvalId
243
+ ? { ...candidate, response: approval, responseIndex: event.index }
244
+ : candidate,
245
+ ),
246
+ }))
247
+ }
248
+ case 'ai.tool.result': {
249
+ const result = event.payload as ToolResultPayload
250
+ if (
251
+ result.preliminary === true ||
252
+ state.response?.generation?.generationId !== result.generationId
253
+ ) {
254
+ return state
255
+ }
256
+ return updateResponse(state, (response) => ({
257
+ ...response,
258
+ calls: response.calls.map((candidate) =>
259
+ candidate.call.toolCallId === result.toolCallId
260
+ ? { ...candidate, terminal: true }
261
+ : candidate,
262
+ ),
263
+ }))
264
+ }
265
+ case 'ai.input.requested': {
266
+ const input = event.payload as InputRequestedPayload
267
+ if (
268
+ state.response?.responseMessageId !== input.messageId ||
269
+ state.response.generation?.generationId !== input.generationId
270
+ ) {
271
+ return state
272
+ }
273
+ return updateResponse(state, (response) => ({
274
+ ...response,
275
+ inputs: [
276
+ ...response.inputs.filter(
277
+ (candidate) =>
278
+ candidate.messageId !== input.messageId ||
279
+ candidate.generationId !== input.generationId ||
280
+ candidate.inputId !== input.inputId,
281
+ ),
282
+ input,
283
+ ],
284
+ }))
285
+ }
286
+ case 'ai.input.responded': {
287
+ const input = event.payload as {
288
+ messageId: string
289
+ generationId: string
290
+ inputId: string
291
+ name: string
292
+ }
293
+ const requested = state.response?.inputs.find(
294
+ (candidate) =>
295
+ candidate.messageId === input.messageId &&
296
+ candidate.generationId === input.generationId &&
297
+ candidate.inputId === input.inputId &&
298
+ candidate.name === input.name,
299
+ )
300
+ if (
301
+ state.response?.responseMessageId !== input.messageId ||
302
+ state.response.generation?.generationId !== input.generationId ||
303
+ requested === undefined
304
+ ) {
305
+ return state
306
+ }
307
+ return updateResponse(state, (response) => ({
308
+ ...response,
309
+ inputs: response.inputs.filter(
310
+ (candidate) => candidate.inputId !== input.inputId,
311
+ ),
312
+ inputResponse: {
313
+ index: event.index,
314
+ generationId: input.generationId,
315
+ inputId: input.inputId,
316
+ name: requested.name,
317
+ },
318
+ }))
319
+ }
320
+ default:
321
+ return state
322
+ }
323
+ }
324
+
325
+ export const aiCoordinatorReducer = <
326
+ M extends UIMessage,
327
+ D extends AIEventDefs<M> & EventDefs,
328
+ >(
329
+ contract: Contract<D>,
330
+ ): Reducer<D, AICoordinatorState> =>
331
+ contract
332
+ .reducer({
333
+ name: 'a2.ai.coordinator.v3',
334
+ initialState: {
335
+ closed: false,
336
+ queued: [],
337
+ } as AICoordinatorState,
338
+ })
339
+ .fold((state, event) => foldCoordinator<M, D>(state, event))
340
+
341
+ export const continuationReady = (state: AICoordinatorState): boolean => {
342
+ const response = state.response
343
+ return (
344
+ !state.closed &&
345
+ response?.completion?.finishReason === 'tool-calls' &&
346
+ response.generation?.generationId === response.completion.generationId &&
347
+ response.failure === undefined &&
348
+ response.calls.length > 0 &&
349
+ response.calls.every(
350
+ (call) =>
351
+ call.terminal ||
352
+ (call.call.providerExecuted === true &&
353
+ call.call.supportsDeferredResults !== true &&
354
+ call.approval !== undefined &&
355
+ call.response !== undefined),
356
+ )
357
+ )
358
+ }