experimental-a2 0.0.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 (68) hide show
  1. package/CHANGELOG.md +128 -0
  2. package/dist/ai-server.browser.d.ts +1 -0
  3. package/dist/ai-server.browser.js +4 -0
  4. package/dist/ai-server.d.ts +65 -0
  5. package/dist/ai-server.js +494 -0
  6. package/dist/ai.d.ts +282 -0
  7. package/dist/ai.js +922 -0
  8. package/dist/cache-indexeddb.d.ts +1 -0
  9. package/dist/cache-indexeddb.js +0 -0
  10. package/dist/client.d.ts +90 -0
  11. package/dist/client.js +410 -0
  12. package/dist/contract-B0kAXoaL.js +60 -0
  13. package/dist/contract-DL8btVd9.d.ts +161 -0
  14. package/dist/devtools-server.browser.d.ts +1 -0
  15. package/dist/devtools-server.browser.js +4 -0
  16. package/dist/devtools-server.d.ts +22 -0
  17. package/dist/devtools-server.js +1087 -0
  18. package/dist/errors-BJRMd-h6.js +23 -0
  19. package/dist/errors-xL_JTXsY.d.ts +20 -0
  20. package/dist/http.d.ts +44 -0
  21. package/dist/http.js +119 -0
  22. package/dist/index.d.ts +5 -0
  23. package/dist/index.js +3 -0
  24. package/dist/inspection-E7qbD0Xj.js +10 -0
  25. package/dist/internal-Dm8Ejnud.js +36 -0
  26. package/dist/log-Dg1I8NRr.d.ts +245 -0
  27. package/dist/log-memory.d.ts +11 -0
  28. package/dist/log-memory.js +345 -0
  29. package/dist/log-polling-RO7kclzR.js +83 -0
  30. package/dist/log-postgres.d.ts +40 -0
  31. package/dist/log-postgres.js +628 -0
  32. package/dist/log-redis.d.ts +31 -0
  33. package/dist/log-redis.js +711 -0
  34. package/dist/log-sqlite.d.ts +17 -0
  35. package/dist/log-sqlite.js +450 -0
  36. package/dist/log-yJbXUf72.js +5 -0
  37. package/dist/otel.d.ts +12 -0
  38. package/dist/otel.js +41 -0
  39. package/dist/react.d.ts +54 -0
  40. package/dist/react.js +85 -0
  41. package/dist/recovery-vercel.d.ts +60 -0
  42. package/dist/recovery-vercel.js +120 -0
  43. package/dist/retryable-lazy-DZWmHpii.js +19 -0
  44. package/dist/server-DYsnKTTy.js +780 -0
  45. package/dist/server.browser.d.ts +1 -0
  46. package/dist/server.browser.js +11 -0
  47. package/dist/server.d.ts +136 -0
  48. package/dist/server.js +2 -0
  49. package/dist/telemetry-C78al20p.d.ts +32 -0
  50. package/dist/validate-XKT4FSNn.js +28 -0
  51. package/dist/wire-2QpU1EtJ.js +62 -0
  52. package/docs/01-quickstart.mdx +214 -0
  53. package/docs/concepts/01-contracts.mdx +138 -0
  54. package/docs/concepts/02-handlers.mdx +146 -0
  55. package/docs/concepts/03-durability.mdx +230 -0
  56. package/docs/concepts/04-state.mdx +133 -0
  57. package/docs/guides/01-timers.mdx +85 -0
  58. package/docs/guides/02-cancellation.mdx +107 -0
  59. package/docs/guides/03-react.mdx +234 -0
  60. package/docs/guides/04-local-first.mdx +88 -0
  61. package/docs/guides/05-production.mdx +179 -0
  62. package/docs/guides/06-ai-agents.mdx +659 -0
  63. package/docs/guides/07-devtools.mdx +101 -0
  64. package/docs/guides/08-application-data.mdx +114 -0
  65. package/docs/index.mdx +282 -0
  66. package/docs/reference/01-api.mdx +637 -0
  67. package/docs/reference/02-errors.mdx +77 -0
  68. package/package.json +111 -0
package/dist/ai.js ADDED
@@ -0,0 +1,922 @@
1
+ import { t as assertSyncSchema } from "./validate-XKT4FSNn.js";
2
+ import { t as contract } from "./contract-B0kAXoaL.js";
3
+ //#region src/ai-projector.ts
4
+ const isRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
5
+ const cloneMessage = (messageId, base) => structuredClone(base ?? {
6
+ id: messageId,
7
+ role: "assistant",
8
+ parts: []
9
+ });
10
+ const currentStepParts = (parts) => {
11
+ let start = parts.length - 1;
12
+ while (start >= 0 && parts[start]?.["type"] !== "step-start") start -= 1;
13
+ return parts.slice(start + 1);
14
+ };
15
+ const isToolPart = (part) => typeof part["toolCallId"] === "string" && (part["type"] === "dynamic-tool" || typeof part["type"] === "string" && part["type"].startsWith("tool-"));
16
+ const findToolPart = (parts, toolCallId) => {
17
+ const current = currentStepParts(parts).find((part) => isToolPart(part) && part["toolCallId"] === toolCallId);
18
+ if (current) return current;
19
+ return parts.findLast((part) => isToolPart(part) && part["toolCallId"] === toolCallId);
20
+ };
21
+ const findApprovalPart = (parts, approvalId) => parts.find((part) => {
22
+ const approval = part["approval"];
23
+ return isToolPart(part) && isRecord$1(approval) && approval["id"] === approvalId;
24
+ });
25
+ const setOptional = (target, key, value) => {
26
+ if (value !== void 0) target[key] = value;
27
+ };
28
+ const setToolPart = (options) => {
29
+ let part = findToolPart(options.parts, options.toolCallId);
30
+ if (!part) {
31
+ part = {
32
+ type: options.dynamic ? "dynamic-tool" : `tool-${options.toolName}`,
33
+ ...options.dynamic ? { toolName: options.toolName } : {},
34
+ toolCallId: options.toolCallId
35
+ };
36
+ options.parts.push(part);
37
+ }
38
+ part["state"] = options.state;
39
+ part["input"] = options.input;
40
+ delete part["output"];
41
+ delete part["rawInput"];
42
+ delete part["errorText"];
43
+ delete part["preliminary"];
44
+ setOptional(part, "rawInput", options.rawInput);
45
+ setOptional(part, "output", options.output);
46
+ setOptional(part, "errorText", options.errorText);
47
+ setOptional(part, "preliminary", options.preliminary);
48
+ setOptional(part, "providerExecuted", options.providerExecuted);
49
+ setOptional(part, "title", options.title);
50
+ setOptional(part, "toolMetadata", options.toolMetadata);
51
+ if (options.dynamic) part["toolName"] = options.toolName;
52
+ if (options.providerMetadata !== void 0) part[options.state === "output-available" || options.state === "output-error" ? "resultProviderMetadata" : "callProviderMetadata"] = options.providerMetadata;
53
+ return part;
54
+ };
55
+ const mergeMetadata = (current, update) => {
56
+ if (update === void 0 || update === null) return current;
57
+ return isRecord$1(current) && isRecord$1(update) ? {
58
+ ...current,
59
+ ...update
60
+ } : update;
61
+ };
62
+ const parseCompleteJSON = (value) => {
63
+ try {
64
+ return JSON.parse(value);
65
+ } catch {
66
+ return;
67
+ }
68
+ };
69
+ /** Internal synchronous counterpart of the AI SDK UI message stream fold. */
70
+ function projectUIMessage(options) {
71
+ const message = cloneMessage(options.messageId, options.base);
72
+ const record = message;
73
+ const parts = message.parts;
74
+ const activeText = /* @__PURE__ */ new Map();
75
+ const activeReasoning = /* @__PURE__ */ new Map();
76
+ const partialTools = /* @__PURE__ */ new Map();
77
+ for (const chunk of options.chunks) switch (chunk.type) {
78
+ case "text-start": {
79
+ const part = {
80
+ type: "text",
81
+ text: "",
82
+ state: "streaming",
83
+ ...chunk.providerMetadata === void 0 ? {} : { providerMetadata: chunk.providerMetadata }
84
+ };
85
+ activeText.set(chunk.id, part);
86
+ parts.push(part);
87
+ break;
88
+ }
89
+ case "text-delta": {
90
+ let part = activeText.get(chunk.id);
91
+ if (!part) {
92
+ part = {
93
+ type: "text",
94
+ text: "",
95
+ state: "streaming"
96
+ };
97
+ activeText.set(chunk.id, part);
98
+ parts.push(part);
99
+ }
100
+ part["text"] = `${typeof part["text"] === "string" ? part["text"] : ""}${chunk.delta}`;
101
+ setOptional(part, "providerMetadata", chunk.providerMetadata);
102
+ break;
103
+ }
104
+ case "text-end": {
105
+ const part = activeText.get(chunk.id);
106
+ if (part) {
107
+ part["state"] = "done";
108
+ setOptional(part, "providerMetadata", chunk.providerMetadata);
109
+ activeText.delete(chunk.id);
110
+ }
111
+ break;
112
+ }
113
+ case "reasoning-start": {
114
+ const part = {
115
+ type: "reasoning",
116
+ text: "",
117
+ state: "streaming",
118
+ ...chunk.providerMetadata === void 0 ? {} : { providerMetadata: chunk.providerMetadata }
119
+ };
120
+ activeReasoning.set(chunk.id, part);
121
+ parts.push(part);
122
+ break;
123
+ }
124
+ case "reasoning-delta": {
125
+ let part = activeReasoning.get(chunk.id);
126
+ if (!part) {
127
+ part = {
128
+ type: "reasoning",
129
+ text: "",
130
+ state: "streaming"
131
+ };
132
+ activeReasoning.set(chunk.id, part);
133
+ parts.push(part);
134
+ }
135
+ part["text"] = `${typeof part["text"] === "string" ? part["text"] : ""}${chunk.delta}`;
136
+ setOptional(part, "providerMetadata", chunk.providerMetadata);
137
+ break;
138
+ }
139
+ case "reasoning-end": {
140
+ const part = activeReasoning.get(chunk.id);
141
+ if (part) {
142
+ part["state"] = "done";
143
+ setOptional(part, "providerMetadata", chunk.providerMetadata);
144
+ activeReasoning.delete(chunk.id);
145
+ }
146
+ break;
147
+ }
148
+ case "custom":
149
+ parts.push({
150
+ type: "custom",
151
+ kind: chunk.kind,
152
+ ...chunk.providerMetadata === void 0 ? {} : { providerMetadata: chunk.providerMetadata }
153
+ });
154
+ break;
155
+ case "file":
156
+ case "reasoning-file":
157
+ parts.push({
158
+ type: chunk.type,
159
+ mediaType: chunk.mediaType,
160
+ url: chunk.url,
161
+ ...chunk.providerMetadata === void 0 ? {} : { providerMetadata: chunk.providerMetadata }
162
+ });
163
+ break;
164
+ case "source-url":
165
+ parts.push({
166
+ type: chunk.type,
167
+ sourceId: chunk.sourceId,
168
+ url: chunk.url,
169
+ ...chunk.title === void 0 ? {} : { title: chunk.title },
170
+ ...chunk.providerMetadata === void 0 ? {} : { providerMetadata: chunk.providerMetadata }
171
+ });
172
+ break;
173
+ case "source-document":
174
+ parts.push({
175
+ type: chunk.type,
176
+ sourceId: chunk.sourceId,
177
+ mediaType: chunk.mediaType,
178
+ title: chunk.title,
179
+ ...chunk.filename === void 0 ? {} : { filename: chunk.filename },
180
+ ...chunk.providerMetadata === void 0 ? {} : { providerMetadata: chunk.providerMetadata }
181
+ });
182
+ break;
183
+ case "tool-input-start":
184
+ partialTools.set(chunk.toolCallId, {
185
+ text: "",
186
+ toolName: chunk.toolName,
187
+ dynamic: chunk.dynamic === true,
188
+ ...chunk.providerExecuted === void 0 ? {} : { providerExecuted: chunk.providerExecuted },
189
+ ...chunk.providerMetadata === void 0 ? {} : { providerMetadata: chunk.providerMetadata },
190
+ ...chunk.toolMetadata === void 0 ? {} : { toolMetadata: chunk.toolMetadata },
191
+ ...chunk.title === void 0 ? {} : { title: chunk.title }
192
+ });
193
+ setToolPart({
194
+ parts,
195
+ toolCallId: chunk.toolCallId,
196
+ toolName: chunk.toolName,
197
+ dynamic: chunk.dynamic === true,
198
+ state: "input-streaming",
199
+ providerExecuted: chunk.providerExecuted,
200
+ providerMetadata: chunk.providerMetadata,
201
+ toolMetadata: chunk.toolMetadata,
202
+ title: chunk.title
203
+ });
204
+ break;
205
+ case "tool-input-delta": {
206
+ const partial = partialTools.get(chunk.toolCallId);
207
+ if (!partial) break;
208
+ partial.text += chunk.inputTextDelta;
209
+ setToolPart({
210
+ parts,
211
+ toolCallId: chunk.toolCallId,
212
+ toolName: partial.toolName,
213
+ dynamic: partial.dynamic,
214
+ state: "input-streaming",
215
+ input: parseCompleteJSON(partial.text),
216
+ providerExecuted: partial.providerExecuted,
217
+ providerMetadata: partial.providerMetadata,
218
+ toolMetadata: partial.toolMetadata,
219
+ title: partial.title
220
+ });
221
+ break;
222
+ }
223
+ case "tool-input-available":
224
+ setToolPart({
225
+ parts,
226
+ toolCallId: chunk.toolCallId,
227
+ toolName: chunk.toolName,
228
+ dynamic: chunk.dynamic === true,
229
+ state: "input-available",
230
+ input: chunk.input,
231
+ providerExecuted: chunk.providerExecuted,
232
+ providerMetadata: chunk.providerMetadata,
233
+ toolMetadata: chunk.toolMetadata,
234
+ title: chunk.title
235
+ });
236
+ partialTools.delete(chunk.toolCallId);
237
+ break;
238
+ case "tool-input-error": {
239
+ const dynamic = findToolPart(parts, chunk.toolCallId)?.["type"] === "dynamic-tool" || chunk.dynamic;
240
+ setToolPart({
241
+ parts,
242
+ toolCallId: chunk.toolCallId,
243
+ toolName: chunk.toolName,
244
+ dynamic: dynamic === true,
245
+ state: "output-error",
246
+ input: dynamic ? chunk.input : void 0,
247
+ rawInput: dynamic ? void 0 : chunk.input,
248
+ errorText: chunk.errorText,
249
+ providerExecuted: chunk.providerExecuted,
250
+ providerMetadata: chunk.providerMetadata,
251
+ toolMetadata: chunk.toolMetadata,
252
+ title: chunk.title
253
+ });
254
+ partialTools.delete(chunk.toolCallId);
255
+ break;
256
+ }
257
+ case "tool-approval-request": {
258
+ const part = findToolPart(parts, chunk.toolCallId);
259
+ if (!part) break;
260
+ part["state"] = "approval-requested";
261
+ part["approval"] = {
262
+ id: chunk.approvalId,
263
+ ...chunk.isAutomatic === true ? { isAutomatic: true } : {},
264
+ ...chunk.signature === void 0 ? {} : { signature: chunk.signature }
265
+ };
266
+ break;
267
+ }
268
+ case "tool-approval-response": {
269
+ const part = findApprovalPart(parts, chunk.approvalId);
270
+ if (!part) break;
271
+ const approval = isRecord$1(part["approval"]) ? part["approval"] : {};
272
+ part["state"] = "approval-responded";
273
+ part["approval"] = {
274
+ id: chunk.approvalId,
275
+ approved: chunk.approved,
276
+ ...chunk.reason === void 0 ? {} : { reason: chunk.reason },
277
+ ...approval["isAutomatic"] === true ? { isAutomatic: true } : {},
278
+ ...typeof approval["signature"] === "string" ? { signature: approval["signature"] } : {}
279
+ };
280
+ setOptional(part, "providerExecuted", chunk.providerExecuted);
281
+ setOptional(part, "callProviderMetadata", chunk.providerMetadata);
282
+ break;
283
+ }
284
+ case "tool-output-available": {
285
+ const part = findToolPart(parts, chunk.toolCallId);
286
+ if (!part) break;
287
+ setToolPart({
288
+ parts,
289
+ toolCallId: chunk.toolCallId,
290
+ toolName: part["type"] === "dynamic-tool" && typeof part["toolName"] === "string" ? part["toolName"] : String(part["type"]).slice(5),
291
+ dynamic: part["type"] === "dynamic-tool",
292
+ state: "output-available",
293
+ input: part["input"],
294
+ output: chunk.output,
295
+ preliminary: chunk.preliminary,
296
+ providerExecuted: chunk.providerExecuted,
297
+ providerMetadata: chunk.providerMetadata,
298
+ toolMetadata: chunk.toolMetadata ?? part["toolMetadata"],
299
+ title: typeof part["title"] === "string" ? part["title"] : void 0
300
+ });
301
+ break;
302
+ }
303
+ case "tool-output-error": {
304
+ const part = findToolPart(parts, chunk.toolCallId);
305
+ if (!part) break;
306
+ setToolPart({
307
+ parts,
308
+ toolCallId: chunk.toolCallId,
309
+ toolName: part["type"] === "dynamic-tool" && typeof part["toolName"] === "string" ? part["toolName"] : String(part["type"]).slice(5),
310
+ dynamic: part["type"] === "dynamic-tool",
311
+ state: "output-error",
312
+ input: part["input"],
313
+ rawInput: part["rawInput"],
314
+ errorText: chunk.errorText,
315
+ providerExecuted: chunk.providerExecuted,
316
+ providerMetadata: chunk.providerMetadata,
317
+ toolMetadata: chunk.toolMetadata ?? part["toolMetadata"],
318
+ title: typeof part["title"] === "string" ? part["title"] : void 0
319
+ });
320
+ break;
321
+ }
322
+ case "tool-output-denied": {
323
+ const part = findToolPart(parts, chunk.toolCallId);
324
+ if (part) part["state"] = "output-denied";
325
+ break;
326
+ }
327
+ case "start-step":
328
+ parts.push({ type: "step-start" });
329
+ break;
330
+ case "finish-step":
331
+ activeText.clear();
332
+ activeReasoning.clear();
333
+ break;
334
+ case "start":
335
+ if (chunk.messageId !== void 0) record["id"] = chunk.messageId;
336
+ if (chunk.messageMetadata !== void 0 && chunk.messageMetadata !== null) record["metadata"] = mergeMetadata(record["metadata"], chunk.messageMetadata);
337
+ break;
338
+ case "finish":
339
+ if (chunk.messageMetadata !== void 0 && chunk.messageMetadata !== null) record["metadata"] = mergeMetadata(record["metadata"], chunk.messageMetadata);
340
+ break;
341
+ case "message-metadata":
342
+ if (chunk.messageMetadata !== void 0 && chunk.messageMetadata !== null) record["metadata"] = mergeMetadata(record["metadata"], chunk.messageMetadata);
343
+ break;
344
+ case "abort":
345
+ case "error": break;
346
+ default: {
347
+ if (!chunk.type.startsWith("data-") || chunk.transient) break;
348
+ const existing = chunk.id === void 0 ? void 0 : parts.find((part) => part["type"] === chunk.type && part["id"] === chunk.id);
349
+ if (existing) existing["data"] = chunk.data;
350
+ else parts.push({
351
+ type: chunk.type,
352
+ ...chunk.id === void 0 ? {} : { id: chunk.id },
353
+ data: chunk.data
354
+ });
355
+ }
356
+ }
357
+ return message;
358
+ }
359
+ function interruptUIMessage(message, toolError = "Tool execution was interrupted.") {
360
+ const next = structuredClone(message);
361
+ for (const value of next.parts) {
362
+ if (value["type"] === "text" || value["type"] === "reasoning") {
363
+ if (value["state"] === "streaming") value["state"] = "done";
364
+ continue;
365
+ }
366
+ if (!isToolPart(value)) continue;
367
+ const state = value["state"];
368
+ if (state === "approval-responded") {
369
+ const approval = value["approval"];
370
+ if (isRecord$1(approval) && approval["approved"] === false) {
371
+ value["state"] = "output-denied";
372
+ continue;
373
+ }
374
+ }
375
+ if (state === "output-available" && value["preliminary"] === true) {
376
+ delete value["output"];
377
+ delete value["preliminary"];
378
+ value["state"] = "output-error";
379
+ value["errorText"] = toolError;
380
+ continue;
381
+ }
382
+ if (state === "input-streaming" || state === "input-available" || state === "approval-requested" || state === "approval-responded") {
383
+ if (state === "approval-requested") delete value["approval"];
384
+ value["state"] = "output-error";
385
+ value["errorText"] = toolError;
386
+ }
387
+ }
388
+ return next;
389
+ }
390
+ //#endregion
391
+ //#region src/ai.ts
392
+ const issue = (message) => ({ issues: [{ message }] });
393
+ const schema = (label, parse) => ({ "~standard": {
394
+ version: 1,
395
+ vendor: "a2",
396
+ validate(value) {
397
+ try {
398
+ return parse(value);
399
+ } catch (error) {
400
+ return issue(`${label}: ${error instanceof Error ? error.message : String(error)}`);
401
+ }
402
+ }
403
+ } });
404
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
405
+ const isJSONCompatible = (value, seen = /* @__PURE__ */ new Set()) => {
406
+ if (value === null || typeof value === "string" || typeof value === "boolean") return true;
407
+ if (typeof value === "number") return Number.isFinite(value);
408
+ if (typeof value === "undefined") return true;
409
+ if (typeof value !== "object") return false;
410
+ if (seen.has(value)) return false;
411
+ seen.add(value);
412
+ const valid = Array.isArray(value) ? value.every((item) => isJSONCompatible(item, seen)) : Object.getPrototypeOf(value) === Object.prototype && Object.values(value).every((item) => isJSONCompatible(item, seen));
413
+ seen.delete(value);
414
+ return valid;
415
+ };
416
+ const jsonObject = (label, check) => schema(label, (value) => {
417
+ if (!isRecord(value) || !isJSONCompatible(value) || !check(value)) return issue(`invalid ${label}`);
418
+ return { value };
419
+ });
420
+ const stringField = (value, key) => typeof value[key] === "string" && value[key].length > 0;
421
+ const optionalStringField = (value, key) => value[key] === void 0 || typeof value[key] === "string";
422
+ const messageValue = (value) => isRecord(value) && typeof value["id"] === "string" && (value["role"] === "user" || value["role"] === "assistant" || value["role"] === "system") && Array.isArray(value["parts"]) && value["parts"].every((part) => isRecord(part) && typeof part["type"] === "string") && isJSONCompatible(value);
423
+ const validateMessage = (value, messageSchema) => {
424
+ if (!messageValue(value)) return issue("invalid AI SDK UIMessage");
425
+ if (!messageSchema) return { value };
426
+ const result = messageSchema["~standard"].validate(value);
427
+ if (result instanceof Promise) throw new TypeError("the AI message schema must validate synchronously");
428
+ return result;
429
+ };
430
+ const messagePayloadSchema = (messageSchema) => schema("ai.message.created", (value) => {
431
+ if (!isRecord(value)) return issue("invalid ai.message.created payload");
432
+ const message = validateMessage(value["message"], messageSchema);
433
+ if (message.issues) return { issues: message.issues };
434
+ return { value: { message: message.value } };
435
+ });
436
+ const progressPayloadSchema = schema("ai.generation.progress", (value) => {
437
+ if (!isRecord(value) || !isJSONCompatible(value) || !stringField(value, "requestId") || !stringField(value, "messageId") || !stringField(value, "generationId") || !stringField(value, "responseMessageId") || typeof value["sequence"] !== "number" || !Number.isInteger(value["sequence"]) || value["sequence"] < 0 || !Array.isArray(value["chunks"]) || !value["chunks"].every((chunk) => isRecord(chunk) && typeof chunk["type"] === "string")) return issue("invalid ai.generation.progress payload");
438
+ return { value };
439
+ });
440
+ const completedPayloadSchema = schema("ai.generation.completed", (value) => {
441
+ if (!isRecord(value) || !isJSONCompatible(value) || !stringField(value, "requestId") || !stringField(value, "messageId") || !stringField(value, "generationId") || !stringField(value, "responseMessageId") || !optionalStringField(value, "finishReason")) return issue("invalid ai.generation.completed payload");
442
+ return { value };
443
+ });
444
+ const compactionPayloadSchema = (messageSchema) => schema("ai.compaction.completed", (value) => {
445
+ if (!isRecord(value) || !isJSONCompatible(value) || !stringField(value, "generationId") || !stringField(value, "throughMessageId") || !Array.isArray(value["messages"])) return issue("invalid ai.compaction.completed payload");
446
+ const messages = [];
447
+ for (const candidate of value["messages"]) {
448
+ const result = validateMessage(candidate, messageSchema);
449
+ if (result.issues) return { issues: result.issues };
450
+ messages.push(result.value);
451
+ }
452
+ return { value: {
453
+ generationId: value["generationId"],
454
+ throughMessageId: value["throughMessageId"],
455
+ messages
456
+ } };
457
+ });
458
+ function createEvents(options) {
459
+ const messageSchema = options?.messageSchema;
460
+ if (messageSchema) assertSyncSchema(messageSchema, "the AI message schema");
461
+ return {
462
+ "ai.session.created": jsonObject("ai.session.created", () => true),
463
+ "ai.session.closed": jsonObject("ai.session.closed", (value) => optionalStringField(value, "reason")),
464
+ "ai.message.created": messagePayloadSchema(messageSchema),
465
+ "ai.message.completed": jsonObject("ai.message.completed", (value) => stringField(value, "messageId")),
466
+ "ai.message.interrupted": jsonObject("ai.message.interrupted", (value) => stringField(value, "messageId") && optionalStringField(value, "generationId") && optionalStringField(value, "reason") && (value["lastSeenIndex"] === void 0 || typeof value["lastSeenIndex"] === "number" && Number.isInteger(value["lastSeenIndex"]) && value["lastSeenIndex"] >= 0)),
467
+ "ai.generation.requested": jsonObject("ai.generation.requested", (value) => stringField(value, "messageId") && optionalStringField(value, "responseMessageId") && (value["reason"] === "message" || value["reason"] === "approval" || value["reason"] === "input" || value["reason"] === "retry")),
468
+ "ai.generation.started": jsonObject("ai.generation.started", (value) => stringField(value, "requestId") && stringField(value, "messageId") && stringField(value, "generationId") && stringField(value, "responseMessageId") && typeof value["attempt"] === "number" && Number.isInteger(value["attempt"]) && value["attempt"] > 0 && stringField(value, "model")),
469
+ "ai.generation.progress": progressPayloadSchema,
470
+ "ai.generation.completed": completedPayloadSchema,
471
+ "ai.generation.failed": jsonObject("ai.generation.failed", (value) => stringField(value, "requestId") && stringField(value, "messageId") && stringField(value, "generationId") && stringField(value, "responseMessageId") && stringField(value, "error") && (value["superseded"] === void 0 || typeof value["superseded"] === "boolean")),
472
+ "ai.tool.called": jsonObject("ai.tool.called", (value) => stringField(value, "requestId") && stringField(value, "messageId") && stringField(value, "generationId") && stringField(value, "toolCallId") && stringField(value, "toolName") && (value["dynamic"] === void 0 || typeof value["dynamic"] === "boolean") && (value["providerExecuted"] === void 0 || typeof value["providerExecuted"] === "boolean") && optionalStringField(value, "title")),
473
+ "ai.tool.result": jsonObject("ai.tool.result", (value) => stringField(value, "requestId") && stringField(value, "messageId") && stringField(value, "generationId") && stringField(value, "toolCallId") && optionalStringField(value, "toolName") && optionalStringField(value, "error") && (value["denied"] === void 0 || typeof value["denied"] === "boolean") && (value["preliminary"] === void 0 || typeof value["preliminary"] === "boolean") && (value["phase"] === void 0 || value["phase"] === "input" || value["phase"] === "execution") && (value["dynamic"] === void 0 || typeof value["dynamic"] === "boolean") && (value["providerExecuted"] === void 0 || typeof value["providerExecuted"] === "boolean")),
474
+ "ai.approval.requested": jsonObject("ai.approval.requested", (value) => stringField(value, "messageId") && stringField(value, "generationId") && stringField(value, "approvalId") && stringField(value, "toolCallId") && (value["isAutomatic"] === void 0 || typeof value["isAutomatic"] === "boolean") && optionalStringField(value, "signature")),
475
+ "ai.approval.responded": jsonObject("ai.approval.responded", (value) => stringField(value, "messageId") && stringField(value, "approvalId") && typeof value["approved"] === "boolean" && optionalStringField(value, "reason")),
476
+ "ai.input.requested": jsonObject("ai.input.requested", (value) => stringField(value, "messageId") && optionalStringField(value, "generationId") && stringField(value, "inputId") && stringField(value, "name")),
477
+ "ai.input.responded": jsonObject("ai.input.responded", (value) => stringField(value, "messageId") && stringField(value, "inputId") && stringField(value, "name")),
478
+ "ai.compaction.requested": jsonObject("ai.compaction.requested", (value) => stringField(value, "generationId") && stringField(value, "throughMessageId")),
479
+ "ai.compaction.completed": compactionPayloadSchema(messageSchema)
480
+ };
481
+ }
482
+ const events = createEvents();
483
+ const initialState = () => ({
484
+ session: { created: false },
485
+ messages: [],
486
+ status: "idle",
487
+ activeGeneration: null,
488
+ activeProjection: null,
489
+ pendingApprovals: [],
490
+ pendingInputs: [],
491
+ tools: [],
492
+ compaction: null,
493
+ usage: [],
494
+ error: null
495
+ });
496
+ const upsertMessage = (messages, message) => {
497
+ const index = messages.findIndex((candidate) => candidate.id === message.id);
498
+ if (index === -1) return [...messages, message];
499
+ const next = [...messages];
500
+ next[index] = message;
501
+ return next;
502
+ };
503
+ const answerApproval = (messages, response) => messages.map((message) => {
504
+ if (message.id !== response.messageId) return message;
505
+ let changed = false;
506
+ const parts = message.parts.map((part) => {
507
+ const candidate = part;
508
+ if (!isRecord(candidate) || candidate["state"] !== "approval-requested" || !isRecord(candidate["approval"]) || candidate["approval"]["id"] !== response.approvalId) return part;
509
+ changed = true;
510
+ return {
511
+ ...part,
512
+ state: "approval-responded",
513
+ approval: {
514
+ ...candidate["approval"],
515
+ approved: response.approved,
516
+ ...response.reason === void 0 ? {} : { reason: response.reason }
517
+ }
518
+ };
519
+ });
520
+ return changed ? {
521
+ ...message,
522
+ parts
523
+ } : message;
524
+ });
525
+ const waitingStatus = (state) => state.pendingApprovals.length > 0 || state.pendingInputs.length > 0 ? "waiting" : "idle";
526
+ const projectedMessage = (projection) => projectUIMessage({
527
+ messageId: projection.responseMessageId,
528
+ ...projection.baseMessage === void 0 ? {} : { base: projection.baseMessage },
529
+ chunks: projection.batches.flatMap((batch) => batch.chunks)
530
+ });
531
+ const reduceToolActivity = (tools, event) => {
532
+ if (event.type === "ai.tool.called") {
533
+ const payload = event.payload;
534
+ const { providerMetadata, ...called } = payload;
535
+ const tool = {
536
+ ...called,
537
+ ...providerMetadata === void 0 ? {} : { callProviderMetadata: providerMetadata },
538
+ status: "running"
539
+ };
540
+ return [...tools.filter((candidate) => candidate.toolCallId !== payload.toolCallId), tool];
541
+ }
542
+ const payload = event.payload;
543
+ const existing = tools.find((candidate) => candidate.toolCallId === payload.toolCallId);
544
+ const toolName = payload.toolName ?? existing?.toolName;
545
+ const input = payload.input === void 0 ? existing?.input : payload.input;
546
+ const dynamic = payload.dynamic ?? existing?.dynamic;
547
+ const providerExecuted = payload.providerExecuted ?? existing?.providerExecuted;
548
+ const toolMetadata = payload.toolMetadata ?? existing?.toolMetadata;
549
+ const resultProviderMetadata = payload.providerMetadata ?? existing?.resultProviderMetadata;
550
+ const tool = {
551
+ requestId: payload.requestId,
552
+ messageId: payload.messageId,
553
+ generationId: payload.generationId,
554
+ toolCallId: payload.toolCallId,
555
+ ...toolName === void 0 ? {} : { toolName },
556
+ ...input === void 0 ? {} : { input },
557
+ ...payload.rawInput === void 0 ? {} : { rawInput: payload.rawInput },
558
+ ...payload.output === void 0 ? {} : { output: payload.output },
559
+ ...payload.error === void 0 ? {} : { error: payload.error },
560
+ ...payload.preliminary === void 0 ? {} : { preliminary: payload.preliminary },
561
+ ...payload.phase === void 0 ? {} : { phase: payload.phase },
562
+ ...dynamic === void 0 ? {} : { dynamic },
563
+ ...providerExecuted === void 0 ? {} : { providerExecuted },
564
+ ...existing?.callProviderMetadata === void 0 ? {} : { callProviderMetadata: existing.callProviderMetadata },
565
+ ...resultProviderMetadata === void 0 ? {} : { resultProviderMetadata },
566
+ ...toolMetadata === void 0 ? {} : { toolMetadata },
567
+ ...existing?.title === void 0 ? {} : { title: existing.title },
568
+ status: payload.denied ? "denied" : payload.error ? "failed" : payload.preliminary ? "running" : "completed"
569
+ };
570
+ return [...tools.filter((candidate) => candidate.toolCallId !== payload.toolCallId), tool];
571
+ };
572
+ const projectedTools = (projection) => projection.toolEvents.reduce((tools, event) => reduceToolActivity(tools, event), projection.baseTools);
573
+ const stopGenerationTools = (tools, generationId, responseMessageId, error) => tools.map((tool) => (tool.generationId === generationId || tool.messageId === responseMessageId) && tool.status === "running" ? {
574
+ ...tool,
575
+ status: "failed",
576
+ error,
577
+ preliminary: false
578
+ } : tool);
579
+ /** Pure projection of the built-in AI protocol. Unknown extension events are ignored. */
580
+ function reduceAIState(state, event) {
581
+ if (state.status === "closed") return state;
582
+ switch (event.type) {
583
+ case "ai.session.created": {
584
+ const payload = event.payload;
585
+ return {
586
+ ...state,
587
+ session: {
588
+ created: true,
589
+ ...payload.metadata === void 0 ? state.session.metadata === void 0 ? {} : { metadata: state.session.metadata } : { metadata: payload.metadata }
590
+ }
591
+ };
592
+ }
593
+ case "ai.session.closed": return {
594
+ ...state,
595
+ status: "closed",
596
+ activeGeneration: null,
597
+ activeProjection: null
598
+ };
599
+ case "ai.message.created": {
600
+ const { message } = event.payload;
601
+ return {
602
+ ...state,
603
+ messages: upsertMessage(state.messages, message)
604
+ };
605
+ }
606
+ case "ai.generation.requested": {
607
+ const payload = event.payload;
608
+ return {
609
+ ...state,
610
+ messages: payload.reason === "retry" && payload.responseMessageId !== void 0 ? state.messages.filter((message) => message.id !== payload.responseMessageId) : state.messages,
611
+ status: "generating",
612
+ error: null
613
+ };
614
+ }
615
+ case "ai.generation.started": {
616
+ const payload = event.payload;
617
+ const baseMessage = state.messages.find((message) => message.id === payload.responseMessageId);
618
+ return {
619
+ ...state,
620
+ status: "generating",
621
+ activeGeneration: payload,
622
+ activeProjection: {
623
+ generationId: payload.generationId,
624
+ responseMessageId: payload.responseMessageId,
625
+ ...baseMessage === void 0 ? {} : { baseMessage },
626
+ batches: [],
627
+ baseTools: state.tools,
628
+ toolEvents: []
629
+ },
630
+ error: null
631
+ };
632
+ }
633
+ case "ai.generation.progress": {
634
+ const payload = event.payload;
635
+ const activeProjection = state.activeProjection;
636
+ if (state.activeGeneration?.generationId !== payload.generationId || activeProjection?.generationId !== payload.generationId) return state;
637
+ const projection = {
638
+ ...activeProjection,
639
+ batches: [...activeProjection.batches.filter((batch) => batch.index !== event.index), {
640
+ index: event.index,
641
+ chunks: payload.chunks
642
+ }].toSorted((left, right) => left.index - right.index)
643
+ };
644
+ return {
645
+ ...state,
646
+ messages: upsertMessage(state.messages, projectedMessage(projection)),
647
+ activeProjection: projection,
648
+ status: "generating"
649
+ };
650
+ }
651
+ case "ai.generation.completed": {
652
+ const payload = event.payload;
653
+ if (state.activeGeneration?.generationId !== payload.generationId) return state;
654
+ const usage = payload.usage ? [...state.usage.filter((entry) => entry.generationId !== payload.generationId), {
655
+ generationId: payload.generationId,
656
+ usage: payload.usage
657
+ }] : state.usage;
658
+ const next = {
659
+ ...state,
660
+ activeGeneration: null,
661
+ activeProjection: null,
662
+ usage
663
+ };
664
+ return {
665
+ ...next,
666
+ status: waitingStatus(next)
667
+ };
668
+ }
669
+ case "ai.generation.failed": {
670
+ const payload = event.payload;
671
+ if (state.activeGeneration?.generationId !== payload.generationId) return state;
672
+ if (payload.superseded === true) return {
673
+ ...state,
674
+ messages: state.messages.filter((message) => message.id !== payload.responseMessageId),
675
+ tools: state.tools.filter((tool) => tool.generationId !== payload.generationId),
676
+ activeGeneration: null,
677
+ activeProjection: null
678
+ };
679
+ const projection = state.activeProjection;
680
+ const shouldProject = projection?.generationId === payload.generationId && (projection.baseMessage !== void 0 || projection.batches.length > 0);
681
+ return {
682
+ ...state,
683
+ messages: shouldProject ? upsertMessage(state.messages, interruptUIMessage(projectedMessage(projection), `Generation failed before tool completion: ${payload.error}`)) : state.messages,
684
+ status: "failed",
685
+ activeGeneration: null,
686
+ activeProjection: null,
687
+ pendingApprovals: state.pendingApprovals.filter((approval) => approval.generationId !== payload.generationId),
688
+ tools: stopGenerationTools(state.tools, payload.generationId, payload.responseMessageId, payload.error),
689
+ error: payload.error
690
+ };
691
+ }
692
+ case "ai.message.interrupted": {
693
+ const payload = event.payload;
694
+ const active = state.activeGeneration;
695
+ if (active === null || (payload.generationId === void 0 ? payload.messageId !== active.responseMessageId : payload.generationId !== active.generationId)) return state;
696
+ const projection = state.activeProjection;
697
+ const cutoff = payload.lastSeenIndex ?? Number.POSITIVE_INFINITY;
698
+ const retained = projection?.generationId === active.generationId ? {
699
+ ...projection,
700
+ batches: projection.batches.filter((batch) => batch.index <= cutoff),
701
+ toolEvents: projection.toolEvents.filter((toolEvent) => toolEvent.index <= cutoff)
702
+ } : null;
703
+ const shouldProject = retained !== null && (retained.baseMessage !== void 0 || retained.batches.length > 0);
704
+ const next = {
705
+ ...state,
706
+ messages: shouldProject ? upsertMessage(state.messages, interruptUIMessage(projectedMessage(retained))) : state.messages,
707
+ activeGeneration: null,
708
+ activeProjection: null,
709
+ pendingApprovals: state.pendingApprovals.filter((approval) => approval.generationId !== active.generationId),
710
+ tools: stopGenerationTools(retained === null ? state.tools : projectedTools(retained), active.generationId, active.responseMessageId, "Tool execution was interrupted.")
711
+ };
712
+ return {
713
+ ...next,
714
+ status: waitingStatus(next)
715
+ };
716
+ }
717
+ case "ai.tool.called": {
718
+ const payload = event.payload;
719
+ const projection = state.activeProjection;
720
+ if (projection?.generationId !== payload.generationId) return state;
721
+ const activeProjection = {
722
+ ...projection,
723
+ toolEvents: [...projection.toolEvents, {
724
+ index: event.index,
725
+ type: "ai.tool.called",
726
+ payload
727
+ }]
728
+ };
729
+ return {
730
+ ...state,
731
+ activeProjection,
732
+ tools: projectedTools(activeProjection)
733
+ };
734
+ }
735
+ case "ai.tool.result": {
736
+ const payload = event.payload;
737
+ const projection = state.activeProjection;
738
+ if (projection?.generationId !== payload.generationId) return state;
739
+ const activeProjection = {
740
+ ...projection,
741
+ toolEvents: [...projection.toolEvents, {
742
+ index: event.index,
743
+ type: "ai.tool.result",
744
+ payload
745
+ }]
746
+ };
747
+ return {
748
+ ...state,
749
+ activeProjection,
750
+ tools: projectedTools(activeProjection)
751
+ };
752
+ }
753
+ case "ai.approval.requested": {
754
+ const payload = event.payload;
755
+ return {
756
+ ...state,
757
+ pendingApprovals: [...state.pendingApprovals.filter((approval) => approval.approvalId !== payload.approvalId), payload]
758
+ };
759
+ }
760
+ case "ai.approval.responded": {
761
+ const payload = event.payload;
762
+ const next = {
763
+ ...state,
764
+ messages: answerApproval(state.messages, payload),
765
+ pendingApprovals: state.pendingApprovals.filter((approval) => approval.approvalId !== payload.approvalId)
766
+ };
767
+ return {
768
+ ...next,
769
+ status: waitingStatus(next)
770
+ };
771
+ }
772
+ case "ai.input.requested": {
773
+ const payload = event.payload;
774
+ return {
775
+ ...state,
776
+ pendingInputs: [...state.pendingInputs.filter((input) => input.inputId !== payload.inputId), payload],
777
+ status: "waiting"
778
+ };
779
+ }
780
+ case "ai.input.responded": {
781
+ const payload = event.payload;
782
+ const next = {
783
+ ...state,
784
+ pendingInputs: state.pendingInputs.filter((input) => input.inputId !== payload.inputId)
785
+ };
786
+ return {
787
+ ...next,
788
+ status: waitingStatus(next)
789
+ };
790
+ }
791
+ case "ai.compaction.requested": {
792
+ const payload = event.payload;
793
+ return {
794
+ ...state,
795
+ compaction: {
796
+ status: "running",
797
+ ...payload
798
+ }
799
+ };
800
+ }
801
+ case "ai.compaction.completed": {
802
+ const payload = event.payload;
803
+ return {
804
+ ...state,
805
+ compaction: {
806
+ status: "completed",
807
+ ...payload
808
+ }
809
+ };
810
+ }
811
+ default: return state;
812
+ }
813
+ }
814
+ /** Derive AI SDK UI messages directly from an A2 event history. */
815
+ function deriveUIMessages(history) {
816
+ let state = initialState();
817
+ for (const event of history) state = reduceAIState(state, event);
818
+ return state.messages;
819
+ }
820
+ /** Build the standard AI state projection for any contract containing the protocol. */
821
+ function createReducer(options) {
822
+ return options.contract.reducer({
823
+ name: options.name ?? "a2.ai.state.v2",
824
+ initialState: initialState()
825
+ }).fold((state, event) => reduceAIState(state, event));
826
+ }
827
+ const generationRequest = (options) => ({
828
+ type: "ai.generation.requested",
829
+ id: options.id,
830
+ payload: {
831
+ messageId: options.messageId,
832
+ reason: options.reason,
833
+ ...options.responseMessageId === void 0 ? {} : { responseMessageId: options.responseMessageId }
834
+ }
835
+ });
836
+ const seedMessage = (message) => [{
837
+ type: "ai.message.created",
838
+ id: `ai.message:${message.id}`,
839
+ payload: { message }
840
+ }];
841
+ /** Pure append inputs for the built-in AI protocol. */
842
+ const inputs = {
843
+ message(message) {
844
+ const messageInputs = seedMessage(message);
845
+ if (message.role !== "user") return messageInputs;
846
+ return [...messageInputs, generationRequest({
847
+ id: `ai.generate:message:${message.id}`,
848
+ messageId: message.id,
849
+ reason: "message"
850
+ })];
851
+ },
852
+ seed: seedMessage,
853
+ approval(response) {
854
+ return [{
855
+ type: "ai.approval.responded",
856
+ id: `ai.approval:${response.approvalId}:response`,
857
+ payload: response
858
+ }, generationRequest({
859
+ id: `ai.generate:approval:${response.approvalId}`,
860
+ messageId: response.messageId,
861
+ reason: "approval"
862
+ })];
863
+ },
864
+ input(response) {
865
+ return [{
866
+ type: "ai.input.responded",
867
+ id: `ai.input:${response.inputId}:response`,
868
+ payload: response
869
+ }, generationRequest({
870
+ id: `ai.generate:input:${response.inputId}`,
871
+ messageId: response.messageId,
872
+ reason: "input"
873
+ })];
874
+ },
875
+ requestInput(request) {
876
+ return [{
877
+ type: "ai.input.requested",
878
+ id: `ai.input:${request.inputId}:request`,
879
+ payload: request
880
+ }];
881
+ },
882
+ retry(retryOptions) {
883
+ return [generationRequest({
884
+ id: `ai.generate:retry:${retryOptions.retryId}`,
885
+ messageId: retryOptions.messageId,
886
+ responseMessageId: retryOptions.responseMessageId,
887
+ reason: "retry"
888
+ })];
889
+ },
890
+ interrupt(interruption) {
891
+ return [{
892
+ type: "ai.message.interrupted",
893
+ id: `ai.interrupt:${interruption.generationId ?? interruption.messageId}`,
894
+ payload: interruption
895
+ }];
896
+ }
897
+ };
898
+ /**
899
+ * Define an agent: built-in AI protocol + application events and one state
900
+ * reducer. The result is isomorphic.
901
+ */
902
+ function agent(options) {
903
+ const builtIns = createEvents(options.messageSchema === void 0 ? void 0 : { messageSchema: options.messageSchema });
904
+ const extensions = options.events ?? {};
905
+ for (const key of Object.keys(extensions)) if (Object.hasOwn(builtIns, key)) throw new TypeError(`agent event '${key}' conflicts with a built-in event`);
906
+ const agentContract = contract({
907
+ name: options.name,
908
+ events: {
909
+ ...builtIns,
910
+ ...extensions
911
+ }
912
+ });
913
+ return {
914
+ contract: agentContract,
915
+ reducer: agentContract.reducer({
916
+ name: options.reducerName ?? "a2.ai.state.v2",
917
+ initialState: initialState()
918
+ }).fold((state, event) => reduceAIState(state, event))
919
+ };
920
+ }
921
+ //#endregion
922
+ export { agent, createEvents, createReducer, deriveUIMessages, events, inputs, reduceAIState };