experimental-a2 0.15.0 → 0.16.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 (101) hide show
  1. package/CHANGELOG.md +81 -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-Cai-lCbj.d.ts → ai-CrEf6p_W.d.ts} +73 -14
  9. package/dist/ai-CrEf6p_W.d.ts.map +1 -0
  10. package/dist/ai-DgOBltJ_.js +2451 -0
  11. package/dist/ai-DgOBltJ_.js.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 +788 -296
  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 +149 -48
  59. package/docs/reference/01-api.mdx +110 -28
  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 +36 -4
  74. package/src/ai-context-schema.ts +296 -0
  75. package/src/ai-context.ts +680 -0
  76. package/src/ai-control-server.ts +183 -135
  77. package/src/ai-control-state.ts +6 -0
  78. package/src/ai-control.ts +8 -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 +474 -0
  83. package/src/ai-server.ts +247 -336
  84. package/src/ai-stored-state.ts +484 -0
  85. package/src/ai.ts +255 -538
  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,2451 @@
1
+ import { n as validateSync, t as assertSyncSchema } from "./validate-XKT4FSNn.js";
2
+ import { t as clientStateProjection } from "./reducer-DEMjEY_O.js";
3
+ import { t as contract } from "./contract-CKRg_E4q.js";
4
+ import { t as idempotentId } from "./idempotent-id-BRJVeylj.js";
5
+ import { l as ambientToolScopeStorage } from "./internal-Dq2qYxou.js";
6
+ //#region src/parse-partial-json.ts
7
+ /*!
8
+ * Adapted from AI SDK 7.0.58, packages/ai/src/util/fix-json.ts.
9
+ * Source: https://github.com/vercel/ai, packages/ai/src/util/fix-json.ts.
10
+ * Modified to expose a synchronous partial parser with strict TypeScript types.
11
+ * Also preserves positive exponents in unfinished containers.
12
+ * Copyright 2023 Vercel, Inc.
13
+ * @license Apache-2.0. See src/licenses/Apache-2.0.txt.
14
+ */
15
+ function parsePartialJSON(text) {
16
+ if (text === void 0 || text.trim() === "") return void 0;
17
+ try {
18
+ return JSON.parse(text);
19
+ } catch {
20
+ try {
21
+ return JSON.parse(repairPartialJSON(text));
22
+ } catch {
23
+ return;
24
+ }
25
+ }
26
+ }
27
+ const isHexDigit = (char) => char >= "0" && char <= "9" || char >= "A" && char <= "F" || char >= "a" && char <= "f";
28
+ function repairPartialJSON(input) {
29
+ const stack = ["ROOT"];
30
+ let lastValidIndex = -1;
31
+ let literalStart = null;
32
+ let unicodeEscapeDigits = 0;
33
+ function processValueStart(char, i, swapState) {
34
+ switch (char) {
35
+ case "\"":
36
+ lastValidIndex = i;
37
+ stack.pop();
38
+ stack.push(swapState);
39
+ stack.push("INSIDE_STRING");
40
+ break;
41
+ case "f":
42
+ case "t":
43
+ case "n":
44
+ lastValidIndex = i;
45
+ literalStart = i;
46
+ stack.pop();
47
+ stack.push(swapState);
48
+ stack.push("INSIDE_LITERAL");
49
+ break;
50
+ case "-":
51
+ stack.pop();
52
+ stack.push(swapState);
53
+ stack.push("INSIDE_NUMBER");
54
+ break;
55
+ case "0":
56
+ case "1":
57
+ case "2":
58
+ case "3":
59
+ case "4":
60
+ case "5":
61
+ case "6":
62
+ case "7":
63
+ case "8":
64
+ case "9":
65
+ lastValidIndex = i;
66
+ stack.pop();
67
+ stack.push(swapState);
68
+ stack.push("INSIDE_NUMBER");
69
+ break;
70
+ case "{":
71
+ lastValidIndex = i;
72
+ stack.pop();
73
+ stack.push(swapState);
74
+ stack.push("INSIDE_OBJECT_START");
75
+ break;
76
+ case "[":
77
+ lastValidIndex = i;
78
+ stack.pop();
79
+ stack.push(swapState);
80
+ stack.push("INSIDE_ARRAY_START");
81
+ }
82
+ }
83
+ function processAfterObjectValue(char, i) {
84
+ switch (char) {
85
+ case ",":
86
+ stack.pop();
87
+ stack.push("INSIDE_OBJECT_AFTER_COMMA");
88
+ break;
89
+ case "}":
90
+ lastValidIndex = i;
91
+ stack.pop();
92
+ }
93
+ }
94
+ function processAfterArrayValue(char, i) {
95
+ switch (char) {
96
+ case ",":
97
+ stack.pop();
98
+ stack.push("INSIDE_ARRAY_AFTER_COMMA");
99
+ break;
100
+ case "]":
101
+ lastValidIndex = i;
102
+ stack.pop();
103
+ }
104
+ }
105
+ for (let i = 0; i < input.length; i++) {
106
+ const char = input[i];
107
+ switch (stack[stack.length - 1]) {
108
+ case "ROOT":
109
+ processValueStart(char, i, "FINISH");
110
+ break;
111
+ case "INSIDE_OBJECT_START":
112
+ switch (char) {
113
+ case "\"":
114
+ stack.pop();
115
+ stack.push("INSIDE_OBJECT_KEY");
116
+ break;
117
+ case "}":
118
+ lastValidIndex = i;
119
+ stack.pop();
120
+ }
121
+ break;
122
+ case "INSIDE_OBJECT_AFTER_COMMA":
123
+ switch (char) {
124
+ case "\"":
125
+ stack.pop();
126
+ stack.push("INSIDE_OBJECT_KEY");
127
+ }
128
+ break;
129
+ case "INSIDE_OBJECT_KEY":
130
+ switch (char) {
131
+ case "\"":
132
+ stack.pop();
133
+ stack.push("INSIDE_OBJECT_AFTER_KEY");
134
+ }
135
+ break;
136
+ case "INSIDE_OBJECT_AFTER_KEY":
137
+ switch (char) {
138
+ case ":":
139
+ stack.pop();
140
+ stack.push("INSIDE_OBJECT_BEFORE_VALUE");
141
+ }
142
+ break;
143
+ case "INSIDE_OBJECT_BEFORE_VALUE":
144
+ processValueStart(char, i, "INSIDE_OBJECT_AFTER_VALUE");
145
+ break;
146
+ case "INSIDE_OBJECT_AFTER_VALUE":
147
+ processAfterObjectValue(char, i);
148
+ break;
149
+ case "INSIDE_STRING":
150
+ switch (char) {
151
+ case "\"":
152
+ stack.pop();
153
+ lastValidIndex = i;
154
+ break;
155
+ case "\\":
156
+ stack.push("INSIDE_STRING_ESCAPE");
157
+ break;
158
+ default: lastValidIndex = i;
159
+ }
160
+ break;
161
+ case "INSIDE_ARRAY_START":
162
+ switch (char) {
163
+ case "]":
164
+ lastValidIndex = i;
165
+ stack.pop();
166
+ break;
167
+ default:
168
+ lastValidIndex = i;
169
+ processValueStart(char, i, "INSIDE_ARRAY_AFTER_VALUE");
170
+ }
171
+ break;
172
+ case "INSIDE_ARRAY_AFTER_VALUE":
173
+ switch (char) {
174
+ case ",":
175
+ stack.pop();
176
+ stack.push("INSIDE_ARRAY_AFTER_COMMA");
177
+ break;
178
+ case "]":
179
+ lastValidIndex = i;
180
+ stack.pop();
181
+ break;
182
+ default: lastValidIndex = i;
183
+ }
184
+ break;
185
+ case "INSIDE_ARRAY_AFTER_COMMA":
186
+ processValueStart(char, i, "INSIDE_ARRAY_AFTER_VALUE");
187
+ break;
188
+ case "INSIDE_STRING_ESCAPE":
189
+ stack.pop();
190
+ if (char === "u") {
191
+ unicodeEscapeDigits = 0;
192
+ stack.push("INSIDE_STRING_UNICODE_ESCAPE");
193
+ } else lastValidIndex = i;
194
+ break;
195
+ case "INSIDE_STRING_UNICODE_ESCAPE":
196
+ if (isHexDigit(char)) {
197
+ unicodeEscapeDigits++;
198
+ if (unicodeEscapeDigits === 4) {
199
+ stack.pop();
200
+ lastValidIndex = i;
201
+ }
202
+ }
203
+ break;
204
+ case "INSIDE_NUMBER":
205
+ switch (char) {
206
+ case "0":
207
+ case "1":
208
+ case "2":
209
+ case "3":
210
+ case "4":
211
+ case "5":
212
+ case "6":
213
+ case "7":
214
+ case "8":
215
+ case "9":
216
+ lastValidIndex = i;
217
+ break;
218
+ case "e":
219
+ case "E":
220
+ case "-":
221
+ case "+":
222
+ case ".": break;
223
+ case ",":
224
+ stack.pop();
225
+ if (stack[stack.length - 1] === "INSIDE_ARRAY_AFTER_VALUE") processAfterArrayValue(char, i);
226
+ if (stack[stack.length - 1] === "INSIDE_OBJECT_AFTER_VALUE") processAfterObjectValue(char, i);
227
+ break;
228
+ case "}":
229
+ stack.pop();
230
+ if (stack[stack.length - 1] === "INSIDE_OBJECT_AFTER_VALUE") processAfterObjectValue(char, i);
231
+ break;
232
+ case "]":
233
+ stack.pop();
234
+ if (stack[stack.length - 1] === "INSIDE_ARRAY_AFTER_VALUE") processAfterArrayValue(char, i);
235
+ break;
236
+ default: stack.pop();
237
+ }
238
+ break;
239
+ case "INSIDE_LITERAL": {
240
+ const partialLiteral = input.substring(literalStart, i + 1);
241
+ if (!"false".startsWith(partialLiteral) && !"true".startsWith(partialLiteral) && !"null".startsWith(partialLiteral)) {
242
+ stack.pop();
243
+ if (stack[stack.length - 1] === "INSIDE_OBJECT_AFTER_VALUE") processAfterObjectValue(char, i);
244
+ else if (stack[stack.length - 1] === "INSIDE_ARRAY_AFTER_VALUE") processAfterArrayValue(char, i);
245
+ } else lastValidIndex = i;
246
+ break;
247
+ }
248
+ }
249
+ }
250
+ let result = input.slice(0, lastValidIndex + 1);
251
+ for (let i = stack.length - 1; i >= 0; i--) switch (stack[i]) {
252
+ case "INSIDE_STRING":
253
+ result += "\"";
254
+ break;
255
+ case "INSIDE_OBJECT_KEY":
256
+ case "INSIDE_OBJECT_AFTER_KEY":
257
+ case "INSIDE_OBJECT_AFTER_COMMA":
258
+ case "INSIDE_OBJECT_START":
259
+ case "INSIDE_OBJECT_BEFORE_VALUE":
260
+ case "INSIDE_OBJECT_AFTER_VALUE":
261
+ result += "}";
262
+ break;
263
+ case "INSIDE_ARRAY_START":
264
+ case "INSIDE_ARRAY_AFTER_COMMA":
265
+ case "INSIDE_ARRAY_AFTER_VALUE":
266
+ result += "]";
267
+ break;
268
+ case "INSIDE_LITERAL": {
269
+ const partialLiteral = input.substring(literalStart, input.length);
270
+ if ("true".startsWith(partialLiteral)) result += "true".slice(partialLiteral.length);
271
+ else if ("false".startsWith(partialLiteral)) result += "false".slice(partialLiteral.length);
272
+ else if ("null".startsWith(partialLiteral)) result += "null".slice(partialLiteral.length);
273
+ }
274
+ }
275
+ return result;
276
+ }
277
+ //#endregion
278
+ //#region src/ai-projector.ts
279
+ const isRecord$3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
280
+ const cloneMessage = (messageId, base) => base === void 0 ? {
281
+ id: messageId,
282
+ role: "assistant",
283
+ parts: []
284
+ } : {
285
+ ...base,
286
+ id: messageId,
287
+ parts: base.parts.map((part) => ({ ...part }))
288
+ };
289
+ const currentStepParts = (parts) => {
290
+ let start = parts.length - 1;
291
+ while (start >= 0 && parts[start]?.["type"] !== "step-start") start -= 1;
292
+ return parts.slice(start + 1);
293
+ };
294
+ const isToolPart = (part) => typeof part["toolCallId"] === "string" && (part["type"] === "dynamic-tool" || typeof part["type"] === "string" && part["type"].startsWith("tool-"));
295
+ const findToolPart = (parts, toolCallId) => {
296
+ const current = currentStepParts(parts).find((part) => isToolPart(part) && part["toolCallId"] === toolCallId);
297
+ if (current) return current;
298
+ return parts.findLast((part) => isToolPart(part) && part["toolCallId"] === toolCallId);
299
+ };
300
+ const findApprovalPart = (parts, approvalId) => parts.find((part) => {
301
+ const approval = part["approval"];
302
+ return isToolPart(part) && isRecord$3(approval) && approval["id"] === approvalId;
303
+ });
304
+ const setOptional = (target, key, value) => {
305
+ if (value !== void 0) target[key] = value;
306
+ };
307
+ const setToolPart = (options) => {
308
+ let part = findToolPart(options.parts, options.toolCallId);
309
+ if (!part) {
310
+ part = {
311
+ type: options.dynamic ? "dynamic-tool" : `tool-${options.toolName}`,
312
+ ...options.dynamic ? { toolName: options.toolName } : {},
313
+ toolCallId: options.toolCallId
314
+ };
315
+ options.parts.push(part);
316
+ }
317
+ part["state"] = options.state;
318
+ part["input"] = options.input;
319
+ delete part["output"];
320
+ delete part["rawInput"];
321
+ delete part["errorText"];
322
+ delete part["preliminary"];
323
+ setOptional(part, "rawInput", options.rawInput);
324
+ setOptional(part, "output", options.output);
325
+ setOptional(part, "errorText", options.errorText);
326
+ setOptional(part, "preliminary", options.preliminary);
327
+ setOptional(part, "providerExecuted", options.providerExecuted);
328
+ setOptional(part, "title", options.title);
329
+ setOptional(part, "toolMetadata", options.toolMetadata);
330
+ if (options.dynamic) part["toolName"] = options.toolName;
331
+ if (options.providerMetadata !== void 0) part[options.state === "output-available" || options.state === "output-error" ? "resultProviderMetadata" : "callProviderMetadata"] = options.providerMetadata;
332
+ return part;
333
+ };
334
+ const mergeMetadata = (current, update) => {
335
+ if (update === void 0 || update === null) return current;
336
+ return isRecord$3(current) && isRecord$3(update) ? {
337
+ ...current,
338
+ ...update
339
+ } : update;
340
+ };
341
+ function projectUIMessage(options) {
342
+ return projectUIMessageStream(options).message;
343
+ }
344
+ function projectUIMessageStream(options) {
345
+ const message = cloneMessage(options.messageId, options.base);
346
+ const record = message;
347
+ const parts = message.parts;
348
+ const activeText = new Map(Object.entries(options.cursor?.text ?? {}).map(([id, index]) => [id, parts[index]]));
349
+ const activeReasoning = new Map(Object.entries(options.cursor?.reasoning ?? {}).map(([id, index]) => [id, parts[index]]));
350
+ const partialTools = new Map(Object.entries(options.cursor?.tools ?? {}).map(([id, partial]) => [id, { ...partial }]));
351
+ for (const chunk of options.chunks) switch (chunk.type) {
352
+ case "text-start": {
353
+ const part = {
354
+ type: "text",
355
+ text: "",
356
+ state: "streaming",
357
+ ...chunk.providerMetadata === void 0 ? {} : { providerMetadata: chunk.providerMetadata }
358
+ };
359
+ activeText.set(chunk.id, part);
360
+ parts.push(part);
361
+ break;
362
+ }
363
+ case "text-delta": {
364
+ let part = activeText.get(chunk.id);
365
+ if (!part) {
366
+ part = {
367
+ type: "text",
368
+ text: "",
369
+ state: "streaming"
370
+ };
371
+ activeText.set(chunk.id, part);
372
+ parts.push(part);
373
+ }
374
+ part["text"] = `${typeof part["text"] === "string" ? part["text"] : ""}${chunk.delta}`;
375
+ setOptional(part, "providerMetadata", chunk.providerMetadata);
376
+ break;
377
+ }
378
+ case "text-end": {
379
+ const part = activeText.get(chunk.id);
380
+ if (part) {
381
+ part["state"] = "done";
382
+ setOptional(part, "providerMetadata", chunk.providerMetadata);
383
+ activeText.delete(chunk.id);
384
+ }
385
+ break;
386
+ }
387
+ case "reasoning-start": {
388
+ const part = {
389
+ type: "reasoning",
390
+ text: "",
391
+ state: "streaming",
392
+ ...chunk.providerMetadata === void 0 ? {} : { providerMetadata: chunk.providerMetadata }
393
+ };
394
+ activeReasoning.set(chunk.id, part);
395
+ parts.push(part);
396
+ break;
397
+ }
398
+ case "reasoning-delta": {
399
+ let part = activeReasoning.get(chunk.id);
400
+ if (!part) {
401
+ part = {
402
+ type: "reasoning",
403
+ text: "",
404
+ state: "streaming"
405
+ };
406
+ activeReasoning.set(chunk.id, part);
407
+ parts.push(part);
408
+ }
409
+ part["text"] = `${typeof part["text"] === "string" ? part["text"] : ""}${chunk.delta}`;
410
+ setOptional(part, "providerMetadata", chunk.providerMetadata);
411
+ break;
412
+ }
413
+ case "reasoning-end": {
414
+ const part = activeReasoning.get(chunk.id);
415
+ if (part) {
416
+ part["state"] = "done";
417
+ setOptional(part, "providerMetadata", chunk.providerMetadata);
418
+ activeReasoning.delete(chunk.id);
419
+ }
420
+ break;
421
+ }
422
+ case "custom":
423
+ parts.push({
424
+ type: "custom",
425
+ kind: chunk.kind,
426
+ ...chunk.providerMetadata === void 0 ? {} : { providerMetadata: chunk.providerMetadata }
427
+ });
428
+ break;
429
+ case "file":
430
+ case "reasoning-file":
431
+ parts.push({
432
+ type: chunk.type,
433
+ mediaType: chunk.mediaType,
434
+ url: chunk.url,
435
+ ...chunk.providerMetadata === void 0 ? {} : { providerMetadata: chunk.providerMetadata }
436
+ });
437
+ break;
438
+ case "source-url":
439
+ parts.push({
440
+ type: chunk.type,
441
+ sourceId: chunk.sourceId,
442
+ url: chunk.url,
443
+ ...chunk.title === void 0 ? {} : { title: chunk.title },
444
+ ...chunk.providerMetadata === void 0 ? {} : { providerMetadata: chunk.providerMetadata }
445
+ });
446
+ break;
447
+ case "source-document":
448
+ parts.push({
449
+ type: chunk.type,
450
+ sourceId: chunk.sourceId,
451
+ mediaType: chunk.mediaType,
452
+ title: chunk.title,
453
+ ...chunk.filename === void 0 ? {} : { filename: chunk.filename },
454
+ ...chunk.providerMetadata === void 0 ? {} : { providerMetadata: chunk.providerMetadata }
455
+ });
456
+ break;
457
+ case "tool-input-start":
458
+ partialTools.set(chunk.toolCallId, {
459
+ text: "",
460
+ toolName: chunk.toolName,
461
+ dynamic: chunk.dynamic === true,
462
+ ...chunk.providerExecuted === void 0 ? {} : { providerExecuted: chunk.providerExecuted },
463
+ ...chunk.providerMetadata === void 0 ? {} : { providerMetadata: chunk.providerMetadata },
464
+ ...chunk.toolMetadata === void 0 ? {} : { toolMetadata: chunk.toolMetadata },
465
+ ...chunk.title === void 0 ? {} : { title: chunk.title }
466
+ });
467
+ setToolPart({
468
+ parts,
469
+ toolCallId: chunk.toolCallId,
470
+ toolName: chunk.toolName,
471
+ dynamic: chunk.dynamic === true,
472
+ state: "input-streaming",
473
+ providerExecuted: chunk.providerExecuted,
474
+ providerMetadata: chunk.providerMetadata,
475
+ toolMetadata: chunk.toolMetadata,
476
+ title: chunk.title
477
+ });
478
+ break;
479
+ case "tool-input-delta": {
480
+ const partial = partialTools.get(chunk.toolCallId);
481
+ if (!partial) break;
482
+ partial.text += chunk.inputTextDelta;
483
+ setToolPart({
484
+ parts,
485
+ toolCallId: chunk.toolCallId,
486
+ toolName: partial.toolName,
487
+ dynamic: partial.dynamic,
488
+ state: "input-streaming",
489
+ input: parsePartialJSON(partial.text),
490
+ providerExecuted: partial.providerExecuted,
491
+ providerMetadata: partial.providerMetadata,
492
+ toolMetadata: partial.toolMetadata,
493
+ title: partial.title
494
+ });
495
+ break;
496
+ }
497
+ case "tool-input-available":
498
+ setToolPart({
499
+ parts,
500
+ toolCallId: chunk.toolCallId,
501
+ toolName: chunk.toolName,
502
+ dynamic: chunk.dynamic === true,
503
+ state: "input-available",
504
+ input: chunk.input,
505
+ providerExecuted: chunk.providerExecuted,
506
+ providerMetadata: chunk.providerMetadata,
507
+ toolMetadata: chunk.toolMetadata,
508
+ title: chunk.title
509
+ });
510
+ partialTools.delete(chunk.toolCallId);
511
+ break;
512
+ case "tool-input-error": {
513
+ const dynamic = findToolPart(parts, chunk.toolCallId)?.["type"] === "dynamic-tool" || chunk.dynamic;
514
+ setToolPart({
515
+ parts,
516
+ toolCallId: chunk.toolCallId,
517
+ toolName: chunk.toolName,
518
+ dynamic: dynamic === true,
519
+ state: "output-error",
520
+ input: dynamic ? chunk.input : void 0,
521
+ rawInput: dynamic ? void 0 : chunk.input,
522
+ errorText: chunk.errorText,
523
+ providerExecuted: chunk.providerExecuted,
524
+ providerMetadata: chunk.providerMetadata,
525
+ toolMetadata: chunk.toolMetadata,
526
+ title: chunk.title
527
+ });
528
+ partialTools.delete(chunk.toolCallId);
529
+ break;
530
+ }
531
+ case "tool-approval-request": {
532
+ const part = findToolPart(parts, chunk.toolCallId);
533
+ if (!part) break;
534
+ part["state"] = "approval-requested";
535
+ part["approval"] = {
536
+ id: chunk.approvalId,
537
+ ...chunk.isAutomatic === true ? { isAutomatic: true } : {},
538
+ ...chunk.signature === void 0 ? {} : { signature: chunk.signature }
539
+ };
540
+ break;
541
+ }
542
+ case "tool-approval-response": {
543
+ const part = findApprovalPart(parts, chunk.approvalId);
544
+ if (!part) break;
545
+ const approval = isRecord$3(part["approval"]) ? part["approval"] : {};
546
+ part["state"] = "approval-responded";
547
+ part["approval"] = {
548
+ id: chunk.approvalId,
549
+ approved: chunk.approved,
550
+ ...chunk.reason === void 0 ? {} : { reason: chunk.reason },
551
+ ...approval["isAutomatic"] === true ? { isAutomatic: true } : {},
552
+ ...typeof approval["signature"] === "string" ? { signature: approval["signature"] } : {}
553
+ };
554
+ setOptional(part, "providerExecuted", chunk.providerExecuted);
555
+ setOptional(part, "callProviderMetadata", chunk.providerMetadata);
556
+ break;
557
+ }
558
+ case "tool-output-available": {
559
+ const part = findToolPart(parts, chunk.toolCallId);
560
+ if (!part) break;
561
+ setToolPart({
562
+ parts,
563
+ toolCallId: chunk.toolCallId,
564
+ toolName: part["type"] === "dynamic-tool" && typeof part["toolName"] === "string" ? part["toolName"] : String(part["type"]).slice(5),
565
+ dynamic: part["type"] === "dynamic-tool",
566
+ state: "output-available",
567
+ input: part["input"],
568
+ output: chunk.output,
569
+ preliminary: chunk.preliminary,
570
+ providerExecuted: chunk.providerExecuted,
571
+ providerMetadata: chunk.providerMetadata,
572
+ toolMetadata: chunk.toolMetadata ?? part["toolMetadata"],
573
+ title: typeof part["title"] === "string" ? part["title"] : void 0
574
+ });
575
+ break;
576
+ }
577
+ case "tool-output-error": {
578
+ const part = findToolPart(parts, chunk.toolCallId);
579
+ if (!part) break;
580
+ setToolPart({
581
+ parts,
582
+ toolCallId: chunk.toolCallId,
583
+ toolName: part["type"] === "dynamic-tool" && typeof part["toolName"] === "string" ? part["toolName"] : String(part["type"]).slice(5),
584
+ dynamic: part["type"] === "dynamic-tool",
585
+ state: "output-error",
586
+ input: part["input"],
587
+ rawInput: part["rawInput"],
588
+ errorText: chunk.errorText,
589
+ providerExecuted: chunk.providerExecuted,
590
+ providerMetadata: chunk.providerMetadata,
591
+ toolMetadata: chunk.toolMetadata ?? part["toolMetadata"],
592
+ title: typeof part["title"] === "string" ? part["title"] : void 0
593
+ });
594
+ break;
595
+ }
596
+ case "tool-output-denied": {
597
+ const part = findToolPart(parts, chunk.toolCallId);
598
+ if (part) part["state"] = "output-denied";
599
+ break;
600
+ }
601
+ case "start-step":
602
+ parts.push({ type: "step-start" });
603
+ break;
604
+ case "finish-step":
605
+ activeText.clear();
606
+ activeReasoning.clear();
607
+ break;
608
+ case "start":
609
+ if (chunk.messageMetadata !== void 0 && chunk.messageMetadata !== null) record["metadata"] = mergeMetadata(record["metadata"], chunk.messageMetadata);
610
+ break;
611
+ case "finish":
612
+ if (chunk.messageMetadata !== void 0 && chunk.messageMetadata !== null) record["metadata"] = mergeMetadata(record["metadata"], chunk.messageMetadata);
613
+ break;
614
+ case "message-metadata":
615
+ if (chunk.messageMetadata !== void 0 && chunk.messageMetadata !== null) record["metadata"] = mergeMetadata(record["metadata"], chunk.messageMetadata);
616
+ break;
617
+ case "abort":
618
+ case "error": break;
619
+ default: {
620
+ if (!chunk.type.startsWith("data-") || chunk.transient) break;
621
+ const existing = chunk.id === void 0 ? void 0 : parts.find((part) => part["type"] === chunk.type && part["id"] === chunk.id);
622
+ if (existing) existing["data"] = chunk.data;
623
+ else parts.push({
624
+ type: chunk.type,
625
+ ...chunk.id === void 0 ? {} : { id: chunk.id },
626
+ data: chunk.data
627
+ });
628
+ }
629
+ }
630
+ const indexes = new Map(parts.map((part, index) => [part, index]));
631
+ return {
632
+ message,
633
+ cursor: {
634
+ text: Object.fromEntries([...activeText].map(([id, part]) => [id, indexes.get(part)])),
635
+ reasoning: Object.fromEntries([...activeReasoning].map(([id, part]) => [id, indexes.get(part)])),
636
+ tools: Object.fromEntries(partialTools)
637
+ }
638
+ };
639
+ }
640
+ function pauseUIMessage(options) {
641
+ const next = structuredClone(options.message);
642
+ next.parts = next.parts.filter((part) => !("toolCallId" in part && !options.toolCallIds.has(part.toolCallId)));
643
+ for (const part of next.parts) if ((part.type === "text" || part.type === "reasoning") && part.state === "streaming") part.state = "done";
644
+ return next;
645
+ }
646
+ function interruptUIMessage(message, toolError = "Tool execution was interrupted.") {
647
+ const next = structuredClone(message);
648
+ next.parts = next.parts.filter((value) => {
649
+ const part = value;
650
+ return !isToolPart(part) || part["state"] !== "input-streaming";
651
+ });
652
+ for (const value of next.parts) {
653
+ if (value["type"] === "text" || value["type"] === "reasoning") {
654
+ if (value["state"] === "streaming") value["state"] = "done";
655
+ continue;
656
+ }
657
+ if (!isToolPart(value)) continue;
658
+ const state = value["state"];
659
+ if (state === "approval-responded") {
660
+ const approval = value["approval"];
661
+ if (isRecord$3(approval) && approval["approved"] === false) {
662
+ value["state"] = "output-denied";
663
+ continue;
664
+ }
665
+ }
666
+ if (state === "output-available" && value["preliminary"] === true) {
667
+ delete value["output"];
668
+ delete value["preliminary"];
669
+ value["state"] = "output-error";
670
+ value["errorText"] = toolError;
671
+ continue;
672
+ }
673
+ if (state === "input-available" || state === "approval-requested" || state === "approval-responded") {
674
+ if (state === "approval-requested") delete value["approval"];
675
+ value["state"] = "output-error";
676
+ value["errorText"] = toolError;
677
+ }
678
+ }
679
+ return next;
680
+ }
681
+ //#endregion
682
+ //#region src/ai-progress-batches.ts
683
+ const PAGE_SIZE = 64;
684
+ const appendProgressBatch = ({ batches, batch }) => {
685
+ if (batches.tail.length < PAGE_SIZE) return {
686
+ ...batches,
687
+ length: batches.length + 1,
688
+ tail: [...batches.tail, batch]
689
+ };
690
+ const blocks = [...batches.blocks];
691
+ let block = batches.tail;
692
+ let level = 0;
693
+ for (;;) {
694
+ const left = blocks[level];
695
+ if (left == null) break;
696
+ block = {
697
+ left,
698
+ right: block
699
+ };
700
+ blocks[level] = null;
701
+ level += 1;
702
+ }
703
+ blocks[level] = block;
704
+ return {
705
+ length: batches.length + 1,
706
+ tail: [batch],
707
+ blocks
708
+ };
709
+ };
710
+ const progressBatches = (items = []) => {
711
+ let batches = {
712
+ length: 0,
713
+ tail: [],
714
+ blocks: []
715
+ };
716
+ for (const batch of items) batches = appendProgressBatch({
717
+ batches,
718
+ batch
719
+ });
720
+ return batches;
721
+ };
722
+ const flattenProgressBatches = (batches) => {
723
+ const items = [];
724
+ const visit = (block) => {
725
+ if (Array.isArray(block)) items.push(...block);
726
+ else {
727
+ visit(block.left);
728
+ visit(block.right);
729
+ }
730
+ };
731
+ for (let level = batches.blocks.length - 1; level >= 0; level -= 1) {
732
+ const block = batches.blocks[level];
733
+ if (block) visit(block);
734
+ }
735
+ items.push(...batches.tail);
736
+ return items;
737
+ };
738
+ //#endregion
739
+ //#region src/ai-message-projection.ts
740
+ const isRecord$2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
741
+ const upsertMessage = (messages, message) => {
742
+ const index = messages.findIndex((candidate) => candidate.id === message.id);
743
+ if (index === -1) return [...messages, message];
744
+ const next = [...messages];
745
+ next[index] = message;
746
+ return next;
747
+ };
748
+ const upsertResponse = (options) => {
749
+ const { messages, message, inputId } = options;
750
+ if (messages.some((item) => item.id === message.id)) return upsertMessage(messages, message);
751
+ const inputIndex = messages.findIndex((item) => item.id === inputId);
752
+ if (inputIndex === -1) return [...messages, message];
753
+ return [
754
+ ...messages.slice(0, inputIndex + 1),
755
+ message,
756
+ ...messages.slice(inputIndex + 1)
757
+ ];
758
+ };
759
+ const answerApproval = (messages, response) => messages.map((message) => {
760
+ if (message.id !== response.messageId) return message;
761
+ let changed = false;
762
+ const parts = message.parts.map((part) => {
763
+ const candidate = part;
764
+ if (!isRecord$2(candidate) || candidate["state"] !== "approval-requested" || !isRecord$2(candidate["approval"]) || candidate["approval"]["id"] !== response.approvalId) return part;
765
+ changed = true;
766
+ return {
767
+ ...part,
768
+ state: "approval-responded",
769
+ approval: {
770
+ ...candidate["approval"],
771
+ approved: response.approved,
772
+ ...response.reason === void 0 ? {} : { reason: response.reason }
773
+ }
774
+ };
775
+ });
776
+ return changed ? {
777
+ ...message,
778
+ parts
779
+ } : message;
780
+ });
781
+ const approvalRequestedChunk = (payload) => ({
782
+ type: "tool-approval-request",
783
+ approvalId: payload.approvalId,
784
+ toolCallId: payload.toolCallId,
785
+ ...payload.isAutomatic === void 0 ? {} : { isAutomatic: payload.isAutomatic },
786
+ ...payload.signature === void 0 ? {} : { signature: payload.signature }
787
+ });
788
+ const approvalRespondedChunk = (payload) => ({
789
+ type: "tool-approval-response",
790
+ approvalId: payload.approvalId,
791
+ approved: payload.approved,
792
+ ...payload.reason === void 0 ? {} : { reason: payload.reason }
793
+ });
794
+ const toolResultChunk = (payload) => {
795
+ if (payload.phase === "input") return {
796
+ type: "tool-input-error",
797
+ toolCallId: payload.toolCallId,
798
+ toolName: payload.toolName ?? "unknown",
799
+ input: payload.rawInput ?? payload.input,
800
+ errorText: payload.error ?? "Invalid tool input",
801
+ ...payload.dynamic === void 0 ? {} : { dynamic: payload.dynamic },
802
+ ...payload.providerExecuted === void 0 ? {} : { providerExecuted: payload.providerExecuted },
803
+ ...payload.providerMetadata === void 0 ? {} : { providerMetadata: payload.providerMetadata },
804
+ ...payload.toolMetadata === void 0 ? {} : { toolMetadata: payload.toolMetadata }
805
+ };
806
+ if (payload.denied === true) return {
807
+ type: "tool-output-denied",
808
+ toolCallId: payload.toolCallId
809
+ };
810
+ if (payload.error !== void 0) return {
811
+ type: "tool-output-error",
812
+ toolCallId: payload.toolCallId,
813
+ errorText: payload.error,
814
+ ...payload.dynamic === void 0 ? {} : { dynamic: payload.dynamic },
815
+ ...payload.providerExecuted === void 0 ? {} : { providerExecuted: payload.providerExecuted },
816
+ ...payload.providerMetadata === void 0 ? {} : { providerMetadata: payload.providerMetadata },
817
+ ...payload.toolMetadata === void 0 ? {} : { toolMetadata: payload.toolMetadata }
818
+ };
819
+ return {
820
+ type: "tool-output-available",
821
+ toolCallId: payload.toolCallId,
822
+ output: payload.output,
823
+ ...payload.preliminary === void 0 ? {} : { preliminary: payload.preliminary },
824
+ ...payload.dynamic === void 0 ? {} : { dynamic: payload.dynamic },
825
+ ...payload.providerExecuted === void 0 ? {} : { providerExecuted: payload.providerExecuted },
826
+ ...payload.providerMetadata === void 0 ? {} : { providerMetadata: payload.providerMetadata },
827
+ ...payload.toolMetadata === void 0 ? {} : { toolMetadata: payload.toolMetadata }
828
+ };
829
+ };
830
+ const projectedMessageStream = (projection) => {
831
+ const projectionEvents = flattenProgressBatches(projection.batches);
832
+ for (const event of projection.toolEvents) if (event.type === "ai.tool.result") projectionEvents.push({
833
+ index: event.index,
834
+ chunks: [toolResultChunk(event.payload)]
835
+ });
836
+ for (const event of projection.approvalEvents) projectionEvents.push({
837
+ index: event.index,
838
+ chunks: [event.type === "ai.approval.requested" ? approvalRequestedChunk(event.payload) : approvalRespondedChunk(event.payload)]
839
+ });
840
+ return projectUIMessageStream({
841
+ messageId: projection.responseMessageId,
842
+ ...projection.baseMessage === void 0 ? {} : { base: projection.baseMessage },
843
+ chunks: projectionEvents.toSorted((left, right) => left.index - right.index).flatMap((event) => event.chunks)
844
+ });
845
+ };
846
+ const projectedMessage = (projection) => projectedMessageStream(projection).message;
847
+ const reduceToolActivity = (tools, event) => {
848
+ if (event.type === "ai.tool.called") {
849
+ const payload = event.payload;
850
+ const { providerMetadata, ...called } = payload;
851
+ const activity = {
852
+ ...called,
853
+ ...providerMetadata === void 0 ? {} : { callProviderMetadata: providerMetadata },
854
+ status: "running"
855
+ };
856
+ return [...tools.filter((candidate) => candidate.generationId !== payload.generationId || candidate.toolCallId !== payload.toolCallId), activity];
857
+ }
858
+ const payload = event.payload;
859
+ const existing = tools.find((candidate) => candidate.generationId === payload.generationId && candidate.toolCallId === payload.toolCallId);
860
+ const toolName = payload.toolName ?? existing?.toolName;
861
+ const input = payload.input === void 0 ? existing?.input : payload.input;
862
+ const dynamic = payload.dynamic ?? existing?.dynamic;
863
+ const providerExecuted = payload.providerExecuted ?? existing?.providerExecuted;
864
+ const toolMetadata = payload.toolMetadata ?? existing?.toolMetadata;
865
+ const resultProviderMetadata = payload.providerMetadata ?? existing?.resultProviderMetadata;
866
+ const activity = {
867
+ requestId: payload.requestId,
868
+ messageId: payload.messageId,
869
+ generationId: payload.generationId,
870
+ toolCallId: payload.toolCallId,
871
+ ...toolName === void 0 ? {} : { toolName },
872
+ ...input === void 0 ? {} : { input },
873
+ ...payload.rawInput === void 0 ? {} : { rawInput: payload.rawInput },
874
+ ...payload.output === void 0 ? {} : { output: payload.output },
875
+ ...payload.error === void 0 ? {} : { error: payload.error },
876
+ ...payload.preliminary === void 0 ? {} : { preliminary: payload.preliminary },
877
+ ...payload.phase === void 0 ? {} : { phase: payload.phase },
878
+ ...dynamic === void 0 ? {} : { dynamic },
879
+ ...providerExecuted === void 0 ? {} : { providerExecuted },
880
+ ...existing?.callProviderMetadata === void 0 ? {} : { callProviderMetadata: existing.callProviderMetadata },
881
+ ...resultProviderMetadata === void 0 ? {} : { resultProviderMetadata },
882
+ ...toolMetadata === void 0 ? {} : { toolMetadata },
883
+ ...existing?.title === void 0 ? {} : { title: existing.title },
884
+ status: payload.denied ? "denied" : payload.error ? "failed" : payload.preliminary ? "running" : "completed"
885
+ };
886
+ return [...tools.filter((candidate) => candidate.generationId !== payload.generationId || candidate.toolCallId !== payload.toolCallId), activity];
887
+ };
888
+ const advanceProjection = ({ projection, base, chunks, index }) => {
889
+ const batches = index > (projection.batches.tail.at(-1)?.index ?? -1) ? appendProgressBatch({
890
+ batches: projection.batches,
891
+ batch: {
892
+ index,
893
+ chunks
894
+ }
895
+ }) : progressBatches([...flattenProgressBatches(projection.batches).filter((batch) => batch.index !== index), {
896
+ index,
897
+ chunks
898
+ }].toSorted((left, right) => left.index - right.index));
899
+ const incremental = base !== void 0 && projection.cursor != null && index > projection.cursor.throughIndex;
900
+ const projected = incremental ? projectUIMessageStream({
901
+ messageId: projection.responseMessageId,
902
+ base,
903
+ cursor: projection.cursor.stream,
904
+ chunks
905
+ }) : projectedMessageStream({
906
+ ...projection,
907
+ batches
908
+ });
909
+ return {
910
+ message: projected.message,
911
+ batches,
912
+ cursor: {
913
+ throughIndex: incremental ? index : Math.max(index, batches.tail.at(-1)?.index ?? 0, projection.toolEvents.reduce((latest, event) => Math.max(latest, event.index), 0), projection.approvalEvents.reduce((latest, event) => Math.max(latest, event.index), 0)),
914
+ stream: projected.cursor
915
+ }
916
+ };
917
+ };
918
+ const projectedTools = (projection) => projection.toolEvents.reduce((tools, event) => reduceToolActivity(tools, event), projection.baseTools);
919
+ const restoreGenerationBase = (state, generationId, responseMessageId, reason) => {
920
+ const projection = state.activeProjection?.generationId === generationId ? state.activeProjection : null;
921
+ return {
922
+ ...state,
923
+ messages: projection?.baseMessage === void 0 ? state.messages.filter((message) => message.id !== responseMessageId) : upsertMessage(state.messages, projection.baseMessage),
924
+ tools: projection === null ? state.tools.filter((activity) => activity.generationId !== generationId) : projection.baseTools,
925
+ pendingApprovals: state.pendingApprovals.filter((approval) => approval.generationId !== generationId),
926
+ pendingInputs: state.pendingInputs.filter((input) => input.generationId !== generationId),
927
+ activeGeneration: null,
928
+ activeProjection: null,
929
+ terminalGenerations: {
930
+ ...state.terminalGenerations,
931
+ [generationId]: reason
932
+ },
933
+ compaction: projection === null ? state.compaction?.generationId === generationId ? null : state.compaction : projection.baseCompaction ?? null
934
+ };
935
+ };
936
+ const stopGenerationTools = (tools, generationId, responseMessageId, error) => tools.map((activity) => activity.status === "running" && (generationId === void 0 ? activity.messageId === responseMessageId : activity.generationId === generationId) ? {
937
+ ...activity,
938
+ status: "failed",
939
+ error,
940
+ preliminary: false
941
+ } : activity);
942
+ const interruptGenerationProjection = (state, generationId, responseMessageId, cutoff) => {
943
+ const projection = state.activeProjection?.generationId === generationId ? state.activeProjection : null;
944
+ if (projection === null) return null;
945
+ const retained = {
946
+ ...projection,
947
+ batches: progressBatches(flattenProgressBatches(projection.batches).filter((batch) => batch.index <= cutoff)),
948
+ toolEvents: projection.toolEvents.filter((toolEvent) => toolEvent.index <= cutoff),
949
+ approvalEvents: projection.approvalEvents.filter((approvalEvent) => approvalEvent.index <= cutoff)
950
+ };
951
+ const shouldProject = retained.baseMessage !== void 0 || retained.batches.length > 0;
952
+ const restored = restoreGenerationBase(state, generationId, responseMessageId, "interrupted");
953
+ return {
954
+ ...restored,
955
+ messages: shouldProject ? upsertMessage(state.messages, interruptUIMessage(projectedMessage(retained))) : restored.messages,
956
+ tools: stopGenerationTools(projectedTools(retained), generationId, responseMessageId, "Tool execution was interrupted."),
957
+ usage: projection.completionIndex === void 0 || projection.completionIndex <= cutoff ? restored.usage : restored.usage.filter((entry) => entry.generationId !== generationId)
958
+ };
959
+ };
960
+ //#endregion
961
+ //#region src/ai-client-state.ts
962
+ const isQueueCommand = (command) => command.action === "send" || command.action === "edit" || command.action === "move" || command.action === "remove" || command.action === "send-now" || command.action === "steer" || command.action === "pause" || command.action === "resume" || command.action === "stop";
963
+ const moveItem = ({ items, inputId, beforeId }) => {
964
+ const from = items.findIndex((item) => item.id === inputId);
965
+ if (from < 0 || inputId === beforeId || beforeId !== null && !items.some((item) => item.id === beforeId)) return items;
966
+ const next = [...items];
967
+ const [item] = next.splice(from, 1);
968
+ const to = beforeId === null ? next.length : next.findIndex((entry) => entry.id === beforeId);
969
+ next.splice(to, 0, item);
970
+ return next;
971
+ };
972
+ function projectAIClientState(state) {
973
+ const pending = state.pendingQueueCommands ?? [];
974
+ if (state.status === "closed") return state;
975
+ if (pending.length === 0 && !state.inbox.items.some((item) => item.afterStepOf !== void 0)) return state;
976
+ let messages = state.messages;
977
+ let { items, paused } = state.inbox;
978
+ let turnId = state.active?.turnId ?? null;
979
+ let starting = false;
980
+ let suspended = state.active?.phase === "paused" || state.active?.phase === "pausing";
981
+ let messageIds;
982
+ const show = (message) => {
983
+ messageIds ??= new Set(messages.map((item) => item.id));
984
+ if (messageIds.has(message.id)) return;
985
+ if (messages === state.messages) messages = state.messages.slice();
986
+ messages.push(message);
987
+ messageIds.add(message.id);
988
+ };
989
+ const admit = () => {
990
+ if (turnId !== null || paused || items.length === 0) return;
991
+ let count = 0;
992
+ for (const item of items) {
993
+ count += 1;
994
+ show(item.message);
995
+ if (item.generate) {
996
+ turnId = item.id;
997
+ starting = true;
998
+ suspended = false;
999
+ break;
1000
+ }
1001
+ }
1002
+ if (count > 0) items = items.slice(count);
1003
+ };
1004
+ for (const { command } of pending) {
1005
+ switch (command.action) {
1006
+ case "send":
1007
+ case "steer": {
1008
+ if (items.some((item) => item.id === command.message.id) || turnId === command.message.id) break;
1009
+ const item = {
1010
+ ...command.action === "steer" ? { afterStepOf: command.turnId } : {},
1011
+ id: command.message.id,
1012
+ message: command.message,
1013
+ generate: command.message.role === "user" && (command.action === "steer" || command.generate !== false)
1014
+ };
1015
+ items = command.action === "steer" ? [item, ...items] : [...items, item];
1016
+ break;
1017
+ }
1018
+ case "edit":
1019
+ items = items.map((item) => item.id === command.message.id && item.message.role === command.message.role ? {
1020
+ ...item,
1021
+ message: command.message
1022
+ } : item);
1023
+ break;
1024
+ case "move":
1025
+ items = moveItem({
1026
+ items,
1027
+ inputId: command.inputId,
1028
+ beforeId: command.beforeId
1029
+ });
1030
+ break;
1031
+ case "remove":
1032
+ items = items.filter((item) => item.id !== command.inputId);
1033
+ break;
1034
+ case "send-now": {
1035
+ if (turnId !== command.turnId) break;
1036
+ const selected = items.find((item) => item.id === command.inputId);
1037
+ if (!selected || selected.message.role !== "user") break;
1038
+ items = items.map((item) => item.id === selected.id ? {
1039
+ ...item,
1040
+ generate: true,
1041
+ ...turnId === null ? {} : { afterStepOf: turnId }
1042
+ } : item);
1043
+ items = moveItem({
1044
+ items,
1045
+ inputId: command.inputId,
1046
+ beforeId: items[0]?.id ?? null
1047
+ });
1048
+ break;
1049
+ }
1050
+ case "pause":
1051
+ paused = true;
1052
+ if (command.when === "now") suspended = true;
1053
+ break;
1054
+ case "resume":
1055
+ paused = false;
1056
+ suspended = false;
1057
+ break;
1058
+ case "stop": continue;
1059
+ }
1060
+ admit();
1061
+ }
1062
+ items = items.filter((item) => {
1063
+ if (item.afterStepOf === void 0) return true;
1064
+ show(item.message);
1065
+ return false;
1066
+ });
1067
+ const displayed = {
1068
+ ...state,
1069
+ messages,
1070
+ inbox: {
1071
+ items,
1072
+ paused
1073
+ },
1074
+ starting: starting && !suspended
1075
+ };
1076
+ const stop = pending.find(({ command }) => command.action === "stop" && command.turnId === state.active?.turnId)?.command;
1077
+ if (stop?.action !== "stop") return displayed;
1078
+ const projection = state.activeProjection;
1079
+ return {
1080
+ ...projection === null ? displayed : interruptGenerationProjection(displayed, projection.generationId, projection.responseMessageId, stop.lastSeenIndex ?? Number.POSITIVE_INFINITY) ?? displayed,
1081
+ active: null,
1082
+ status: paused ? "paused" : "idle",
1083
+ starting: false,
1084
+ activeGeneration: null,
1085
+ activeRequestId: null,
1086
+ activeRequestReason: null,
1087
+ activeRequestSourceGenerationId: null,
1088
+ activeResponseMessageId: null,
1089
+ pendingApprovals: [],
1090
+ pendingInputs: [],
1091
+ error: null
1092
+ };
1093
+ }
1094
+ //#endregion
1095
+ //#region src/ai-control-state.ts
1096
+ const container = (value) => typeof value === "object" && value !== null;
1097
+ const same = (left, right) => JSON.stringify(left) === JSON.stringify(right);
1098
+ const controlChanges = ({ before, after }) => {
1099
+ const changes = [];
1100
+ const visit = (left, right, path) => {
1101
+ if (same(left, right)) return;
1102
+ if (Array.isArray(left) && Array.isArray(right)) {
1103
+ let prefix = 0;
1104
+ while (prefix < Math.min(left.length, right.length) && same(left[prefix], right[prefix])) prefix += 1;
1105
+ let suffix = 0;
1106
+ while (suffix < Math.min(left.length, right.length) - prefix && same(left[left.length - 1 - suffix], right[right.length - 1 - suffix])) suffix += 1;
1107
+ if (left.length === right.length) for (let index = prefix; index < left.length - suffix; index += 1) visit(left[index], right[index], [...path, index]);
1108
+ else changes.push({
1109
+ path,
1110
+ splice: {
1111
+ index: prefix,
1112
+ deleteCount: left.length - prefix - suffix,
1113
+ items: right.slice(prefix, right.length - suffix)
1114
+ }
1115
+ });
1116
+ return;
1117
+ }
1118
+ if (container(left) && container(right) && !Array.isArray(left) && !Array.isArray(right)) {
1119
+ if (Object.keys(right).length === 0) {
1120
+ changes.push({
1121
+ path,
1122
+ value: right
1123
+ });
1124
+ return;
1125
+ }
1126
+ for (const key of Object.keys(left)) if (!Object.hasOwn(right, key)) changes.push({
1127
+ path: [...path, key],
1128
+ remove: true
1129
+ });
1130
+ for (const key of Object.keys(right)) visit(Object.hasOwn(left, key) ? left[key] : void 0, right[key], [...path, key]);
1131
+ return;
1132
+ }
1133
+ changes.push({
1134
+ path,
1135
+ value: right
1136
+ });
1137
+ };
1138
+ visit(before, after, []);
1139
+ return changes;
1140
+ };
1141
+ const applyControlChanges = ({ state, changes }) => {
1142
+ if (changes.length === 0) return state;
1143
+ let next = structuredClone(state);
1144
+ for (const change of changes) {
1145
+ const path = change.path;
1146
+ if (path.length === 0) {
1147
+ next = structuredClone(change.value);
1148
+ continue;
1149
+ }
1150
+ let target = next;
1151
+ for (const key of path.slice(0, -1)) {
1152
+ if (!container(target) || !Object.hasOwn(target, key)) throw new TypeError("Invalid AI control change path");
1153
+ target = target[key];
1154
+ }
1155
+ if (!container(target)) throw new TypeError("Invalid AI control change target");
1156
+ const parent = target;
1157
+ const key = path.at(-1);
1158
+ if (change.remove) delete parent[key];
1159
+ else if (change.splice) {
1160
+ const items = parent[key];
1161
+ Object.defineProperty(parent, key, {
1162
+ value: [
1163
+ ...items.slice(0, change.splice.index),
1164
+ ...structuredClone(change.splice.items),
1165
+ ...items.slice(change.splice.index + change.splice.deleteCount)
1166
+ ],
1167
+ enumerable: true,
1168
+ configurable: true,
1169
+ writable: true
1170
+ });
1171
+ } else if (change.move) {
1172
+ const items = parent[key];
1173
+ const [item] = items.splice(change.move.from, 1);
1174
+ items.splice(change.move.to, 0, item);
1175
+ } else Object.defineProperty(parent, key, {
1176
+ value: structuredClone(change.value),
1177
+ enumerable: true,
1178
+ configurable: true,
1179
+ writable: true
1180
+ });
1181
+ }
1182
+ return next;
1183
+ };
1184
+ const controlCommit = (options) => {
1185
+ const active = options.after.active;
1186
+ const response = options.after.coordinator.response;
1187
+ return {
1188
+ changes: [...options.moves, ...controlChanges({
1189
+ before: options.before,
1190
+ after: options.after
1191
+ })],
1192
+ view: {
1193
+ turnId: active?.turnId ?? null,
1194
+ version: active?.version ?? null,
1195
+ phase: active?.phase ?? null,
1196
+ paused: options.after.inbox.paused,
1197
+ closed: options.after.coordinator.closed,
1198
+ requestId: response?.activeRequestId ?? null,
1199
+ requestReason: response?.requestReason ?? null,
1200
+ sourceGenerationId: response?.sourceGenerationId ?? null,
1201
+ responseMessageId: response?.responseMessageId ?? null
1202
+ }
1203
+ };
1204
+ };
1205
+ //#endregion
1206
+ //#region src/ai-control.ts
1207
+ const initialControlState = () => ({
1208
+ pendingStops: {},
1209
+ inbox: {
1210
+ paused: false,
1211
+ items: []
1212
+ },
1213
+ active: null,
1214
+ coordinator: {
1215
+ closed: false,
1216
+ queued: []
1217
+ }
1218
+ });
1219
+ //#endregion
1220
+ //#region src/ai-id.ts
1221
+ const AI_EVENT_ID_PREFIX = "a2.ai:";
1222
+ const aiEventId = async (...parts) => `${AI_EVENT_ID_PREFIX}${await idempotentId(...parts)}`;
1223
+ //#endregion
1224
+ //#region src/ai-stored-state.ts
1225
+ const aiStateReducer = Symbol("a2.ai.stateReducer");
1226
+ const isRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1227
+ const hasKeys = (value, { required, optional = [] }) => isRecord$1(value) && required.every((key) => Object.hasOwn(value, key)) && Object.keys(value).every((key) => required.includes(key) || optional.includes(key));
1228
+ const reference = (value) => {
1229
+ if (!isRecord$1(value) || !Object.hasOwn(value, "$a2Value")) return void 0;
1230
+ const id = value["$a2Value"];
1231
+ if (typeof id !== "number" || !Number.isSafeInteger(id) || id < 0 || Object.keys(value).length !== 1) throw new TypeError("invalid AI payload reference");
1232
+ return id;
1233
+ };
1234
+ const payloadFields = /* @__PURE__ */ new Set([
1235
+ "input",
1236
+ "output",
1237
+ "rawInput",
1238
+ "metadata",
1239
+ "providerMetadata",
1240
+ "callProviderMetadata",
1241
+ "resultProviderMetadata",
1242
+ "toolMetadata",
1243
+ "data"
1244
+ ]);
1245
+ const keyFor = (value) => value === void 0 ? "undefined" : JSON.stringify(value);
1246
+ const sameValue = (left, right) => {
1247
+ if (Object.is(left, right)) return true;
1248
+ if (typeof left !== "object" || left === null || typeof right !== "object" || right === null) return false;
1249
+ const prototype = Object.getPrototypeOf(left);
1250
+ if (prototype !== Object.getPrototypeOf(right) || prototype !== Object.prototype && prototype !== Array.prototype && prototype !== null) return false;
1251
+ if (Array.isArray(left) && (!Array.isArray(right) || left.length !== right.length)) return false;
1252
+ const keys = Reflect.ownKeys(left);
1253
+ return keys.length === Reflect.ownKeys(right).length && keys.every((key) => Object.hasOwn(right, key) && sameValue(Reflect.get(left, key), Reflect.get(right, key)));
1254
+ };
1255
+ const restore = (current, undo) => {
1256
+ if (undo === void 0) return current;
1257
+ if (!isRecord$1(undo)) throw new TypeError("invalid AI recovery change");
1258
+ if ("value" in undo) {
1259
+ if (!hasKeys(undo, { required: ["value"] })) throw new TypeError("invalid AI replacement recovery");
1260
+ return undo.value;
1261
+ }
1262
+ if ("length" in undo) {
1263
+ if (!hasKeys(undo, { required: ["length", "items"] }) || !Number.isSafeInteger(undo.length) || undo.length < 0 || undo.length > 4294967295 || !isRecord$1(undo.items) || !Array.isArray(current)) throw new TypeError("invalid AI array recovery");
1264
+ const items = Object.entries(undo.items);
1265
+ let added = 0;
1266
+ for (const [key, patch] of items) {
1267
+ const index = Number(key);
1268
+ if (!Number.isSafeInteger(index) || index < 0 || index >= undo.length || String(index) !== key || patch === void 0) throw new TypeError("invalid AI array recovery index");
1269
+ if (index >= current.length) added += 1;
1270
+ }
1271
+ if (added !== Math.max(0, undo.length - current.length)) throw new TypeError("incomplete AI array recovery");
1272
+ const result = current.slice(0, undo.length);
1273
+ result.length = undo.length;
1274
+ for (const [index, patch] of items) result[Number(index)] = restore(current[Number(index)] ?? null, patch);
1275
+ return result;
1276
+ }
1277
+ if (!hasKeys(undo, { required: ["fields", "remove"] }) || !isRecord$1(undo.fields) || !Array.isArray(undo.remove) || Object.keys(undo.remove).length !== undo.remove.length || Array.from(undo.remove).some((key) => typeof key !== "string") || new Set(undo.remove).size !== undo.remove.length || !isRecord$1(current) || reference(current) !== void 0) throw new TypeError("invalid AI object recovery");
1278
+ const result = { ...current };
1279
+ for (const key of undo.remove) delete result[key];
1280
+ for (const [key, patch] of Object.entries(undo.fields)) {
1281
+ if (patch === void 0 || undo.remove.includes(key)) throw new TypeError("invalid AI object recovery field");
1282
+ Object.defineProperty(result, key, {
1283
+ value: restore(Object.hasOwn(result, key) ? result[key] : null, patch),
1284
+ enumerable: true,
1285
+ configurable: true,
1286
+ writable: true
1287
+ });
1288
+ }
1289
+ return result;
1290
+ };
1291
+ const difference = (current, before) => {
1292
+ if (current === before) return void 0;
1293
+ if (reference(current) !== void 0 || reference(before) !== void 0) return reference(current) === reference(before) ? void 0 : { value: before };
1294
+ if (Array.isArray(current) && Array.isArray(before)) {
1295
+ const items = {};
1296
+ for (const [index, value] of before.entries()) {
1297
+ const patch = index < current.length ? difference(current[index], value) : { value };
1298
+ if (patch !== void 0) items[index] = patch;
1299
+ }
1300
+ return current.length === before.length && Object.keys(items).length === 0 ? void 0 : {
1301
+ length: before.length,
1302
+ items
1303
+ };
1304
+ }
1305
+ if (isRecord$1(current) && isRecord$1(before)) {
1306
+ const fields = {};
1307
+ const remove = Object.keys(current).filter((key) => !Object.hasOwn(before, key));
1308
+ for (const [key, value] of Object.entries(before)) {
1309
+ const patch = Object.hasOwn(current, key) ? difference(current[key], value) : { value };
1310
+ if (patch !== void 0) Object.defineProperty(fields, key, {
1311
+ value: patch,
1312
+ enumerable: true,
1313
+ configurable: true,
1314
+ writable: true
1315
+ });
1316
+ }
1317
+ return remove.length === 0 && Object.keys(fields).length === 0 ? void 0 : {
1318
+ fields,
1319
+ remove
1320
+ };
1321
+ }
1322
+ return { value: before };
1323
+ };
1324
+ const storeState = (state) => {
1325
+ const packed = /* @__PURE__ */ new WeakMap();
1326
+ const values = [];
1327
+ const ids = /* @__PURE__ */ new Map();
1328
+ const contents = /* @__PURE__ */ new Map();
1329
+ const intern = (value) => {
1330
+ let id = ids.get(value);
1331
+ if (id !== void 0) return { $a2Value: id };
1332
+ const key = typeof value === "object" && value !== null ? keyFor(value) : void 0;
1333
+ const candidates = key === void 0 ? void 0 : contents.get(key);
1334
+ if (candidates !== void 0) id = candidates.find((candidate) => sameValue(values[candidate].value, value));
1335
+ if (id === void 0) {
1336
+ id = values.length;
1337
+ values.push(value === void 0 ? {} : { value });
1338
+ }
1339
+ ids.set(value, id);
1340
+ if (key !== void 0 && !candidates?.includes(id)) contents.set(key, [...candidates ?? [], id]);
1341
+ return { $a2Value: id };
1342
+ };
1343
+ const pack = (value, field) => {
1344
+ if (value === void 0 || typeof value === "string" && value.length > 80 || field !== void 0 && payloadFields.has(field) && !(field === "input" && isRecord$1(value) && "message" in value) || isRecord$1(value) && Object.hasOwn(value, "$a2Value")) return intern(value);
1345
+ if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number") return value;
1346
+ if (typeof value !== "object") throw new TypeError("AI state must contain JSON values");
1347
+ const cached = packed.get(value);
1348
+ if (cached !== void 0) return cached;
1349
+ const result = Array.isArray(value) ? value.map((item) => pack(item)) : Object.fromEntries(Object.entries(value).toSorted(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, item]) => [key, pack(item, key)]));
1350
+ packed.set(value, result);
1351
+ return result;
1352
+ };
1353
+ const saveProjection = (projection, messages, tools) => {
1354
+ const { baseMessage, baseTools, ...fields } = projection;
1355
+ const current = messages.find((message) => message.id === projection.responseMessageId);
1356
+ const message = baseMessage === void 0 ? void 0 : difference(pack(current), pack(baseMessage));
1357
+ const toolPatch = baseTools === void 0 ? void 0 : difference(pack(tools), pack(baseTools));
1358
+ return {
1359
+ fields: pack(fields),
1360
+ hasMessage: baseMessage !== void 0,
1361
+ ...message === void 0 ? {} : { message },
1362
+ ...toolPatch === void 0 ? {} : { tools: toolPatch }
1363
+ };
1364
+ };
1365
+ const { activeProjection, ...fields } = state;
1366
+ const projection = activeProjection === null ? null : saveProjection(activeProjection, state.messages, state.tools);
1367
+ return {
1368
+ version: 2,
1369
+ fields: pack(fields),
1370
+ projection,
1371
+ values
1372
+ };
1373
+ };
1374
+ const loadState = (stored) => {
1375
+ const unpack = (value) => {
1376
+ const id = reference(value);
1377
+ if (id !== void 0) {
1378
+ if (!Object.hasOwn(stored.values, id)) throw new TypeError("missing AI payload");
1379
+ return stored.values[id].value;
1380
+ }
1381
+ if (Array.isArray(value)) {
1382
+ if (Object.keys(value).length !== value.length) throw new TypeError("invalid AI stored array");
1383
+ return Array.from(value, unpack);
1384
+ }
1385
+ if (isRecord$1(value)) return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, unpack(item)]));
1386
+ if (value === null || typeof value === "boolean" || typeof value === "string" || typeof value === "number" && Number.isFinite(value)) return value;
1387
+ throw new TypeError("invalid AI stored value");
1388
+ };
1389
+ const fields = stored.fields;
1390
+ const state = unpack(stored.fields);
1391
+ const loadProjection = (saved, messages, tools) => {
1392
+ if (!hasKeys(saved, {
1393
+ required: ["fields", "hasMessage"],
1394
+ optional: ["message", "tools"]
1395
+ }) || typeof saved.hasMessage !== "boolean" || !isRecord$1(saved.fields) || "message" in saved && (!saved.hasMessage || saved.message === void 0) || "tools" in saved && saved.tools === void 0) throw new TypeError("invalid AI projection recovery");
1396
+ const projection = unpack(saved.fields);
1397
+ const index = unpack(messages).findIndex((message) => message.id === projection.responseMessageId);
1398
+ if (saved.hasMessage) {
1399
+ const current = index === -1 ? null : messages[index];
1400
+ projection.baseMessage = unpack(restore(current, saved.message));
1401
+ }
1402
+ if (tools !== void 0) projection.baseTools = unpack(restore(tools, saved.tools));
1403
+ else if (saved.tools !== void 0) throw new TypeError("missing AI recovery tools");
1404
+ return projection;
1405
+ };
1406
+ state.activeProjection = stored.projection === null ? null : loadProjection(stored.projection, fields["messages"], fields["tools"]);
1407
+ return state;
1408
+ };
1409
+ const storedAIReducer = (source) => {
1410
+ const views = /* @__PURE__ */ new WeakMap();
1411
+ const view = (state) => {
1412
+ let result = views.get(state);
1413
+ if (result === void 0) {
1414
+ result = loadState(state);
1415
+ views.set(state, result);
1416
+ }
1417
+ return result;
1418
+ };
1419
+ const defer = (next) => {
1420
+ let stored;
1421
+ const read = () => stored ??= storeState(next);
1422
+ const state = {
1423
+ version: 2,
1424
+ get fields() {
1425
+ return read().fields;
1426
+ },
1427
+ get projection() {
1428
+ return read().projection;
1429
+ },
1430
+ get values() {
1431
+ return read().values;
1432
+ }
1433
+ };
1434
+ views.set(state, next);
1435
+ return state;
1436
+ };
1437
+ return {
1438
+ ...source,
1439
+ name: `${source.name}:records-v2`,
1440
+ initialState: storeState(source.initialState),
1441
+ [aiStateReducer]: source,
1442
+ view,
1443
+ fromView: (state) => storeState(state),
1444
+ [clientStateProjection]: (state) => source[clientStateProjection]?.(view(state)) ?? view(state),
1445
+ stateSchema: { "~standard": {
1446
+ version: 1,
1447
+ vendor: "a2-ai",
1448
+ validate(value) {
1449
+ try {
1450
+ if (!hasKeys(value, { required: [
1451
+ "version",
1452
+ "fields",
1453
+ "projection",
1454
+ "values"
1455
+ ] }) || value["version"] !== 2 || !Array.isArray(value["values"]) || !isRecord$1(value["fields"]) || value["projection"] !== null && !isRecord$1(value["projection"])) return { issues: [{ message: "invalid AI stored state" }] };
1456
+ const state = value;
1457
+ if (Object.keys(state.values).length !== state.values.length) return { issues: [{ message: "invalid AI payload table" }] };
1458
+ for (const payload of state.values) if (!hasKeys(payload, {
1459
+ required: [],
1460
+ optional: ["value"]
1461
+ })) return { issues: [{ message: "invalid AI payload table" }] };
1462
+ const decoded = loadState(state);
1463
+ if (source.stateSchema) {
1464
+ const result = validateSync(source.stateSchema, decoded, "AI view stateSchema");
1465
+ if (result.issues) return result;
1466
+ return { value: storeState(result.value) };
1467
+ }
1468
+ return { value: state };
1469
+ } catch {
1470
+ return { issues: [{ message: "invalid AI stored state" }] };
1471
+ }
1472
+ }
1473
+ } },
1474
+ fold(state, event) {
1475
+ const current = view(state);
1476
+ const next = source.fold(current, event);
1477
+ if (next === current) return state;
1478
+ return defer(next);
1479
+ }
1480
+ };
1481
+ };
1482
+ //#endregion
1483
+ //#region src/ai.ts
1484
+ const issue = (message) => ({ issues: [{ message }] });
1485
+ const schema = (label, parse) => ({ "~standard": {
1486
+ version: 1,
1487
+ vendor: "a2",
1488
+ validate(value) {
1489
+ try {
1490
+ return parse(value);
1491
+ } catch (error) {
1492
+ return issue(`${label}: ${error instanceof Error ? error.message : String(error)}`);
1493
+ }
1494
+ }
1495
+ } });
1496
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1497
+ const isJSONCompatible = (value, seen = /* @__PURE__ */ new Set()) => {
1498
+ if (value === null || typeof value === "string" || typeof value === "boolean") return true;
1499
+ if (typeof value === "number") return Number.isFinite(value);
1500
+ if (typeof value === "undefined") return true;
1501
+ if (typeof value !== "object") return false;
1502
+ if (seen.has(value)) return false;
1503
+ seen.add(value);
1504
+ const valid = Array.isArray(value) ? value.every((item) => isJSONCompatible(item, seen)) : Object.getPrototypeOf(value) === Object.prototype && Object.values(value).every((item) => isJSONCompatible(item, seen));
1505
+ seen.delete(value);
1506
+ return valid;
1507
+ };
1508
+ const jsonObject = (label, check) => schema(label, (value) => {
1509
+ if (!isRecord(value) || !isJSONCompatible(value) || !check(value)) return issue(`invalid ${label}`);
1510
+ return { value };
1511
+ });
1512
+ const stringField = (value, key) => typeof value[key] === "string" && value[key].length > 0;
1513
+ const optionalIndexField = (value, key) => value[key] === void 0 || typeof value[key] === "number" && Number.isSafeInteger(value[key]) && value[key] >= 0;
1514
+ const optionalStringField = (value, key) => value[key] === void 0 || typeof value[key] === "string";
1515
+ 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);
1516
+ const validateMessage = (value, messageSchema) => {
1517
+ if (!messageValue(value)) return issue("invalid AI SDK UIMessage");
1518
+ if (!messageSchema) return { value };
1519
+ return validateSync(messageSchema, value, "the AI message");
1520
+ };
1521
+ const messagePayloadSchema = (messageSchema) => schema("ai.message.created", (value) => {
1522
+ if (!isRecord(value)) return issue("invalid ai.message.created payload");
1523
+ const message = validateMessage(value["message"], messageSchema);
1524
+ if (message.issues) return { issues: message.issues };
1525
+ const generate = value["generate"];
1526
+ if (generate !== void 0 && typeof generate !== "boolean") return issue("invalid ai.message.created generate flag");
1527
+ return { value: {
1528
+ message: message.value,
1529
+ ...generate === false ? { generate: false } : {}
1530
+ } };
1531
+ });
1532
+ const progressPayloadSchema = schema("ai.generation.progress", (value) => {
1533
+ 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");
1534
+ return { value };
1535
+ });
1536
+ const completedPayloadSchema = schema("ai.generation.completed", (value) => {
1537
+ if (!isRecord(value) || !isJSONCompatible(value) || !stringField(value, "requestId") || !stringField(value, "messageId") || !stringField(value, "generationId") || !stringField(value, "responseMessageId") || !optionalIndexField(value, "inputTokenEstimate") || !optionalStringField(value, "finishReason")) return issue("invalid ai.generation.completed payload");
1538
+ return { value };
1539
+ });
1540
+ const compactionPayloadSchema = (messageSchema) => schema("ai.compaction.completed", (value) => {
1541
+ if (!isRecord(value) || !isJSONCompatible(value) || !stringField(value, "generationId") || !stringField(value, "throughMessageId") || !optionalIndexField(value, "throughIndex") || !optionalStringField(value, "summary") || !Array.isArray(value["messages"]) || value["retainedMessageIds"] !== void 0 && (!Array.isArray(value["retainedMessageIds"]) || !value["retainedMessageIds"].every((candidate) => typeof candidate === "string"))) return issue("invalid ai.compaction.completed payload");
1542
+ const messages = [];
1543
+ for (const candidate of value["messages"]) {
1544
+ const result = validateMessage(candidate, messageSchema);
1545
+ if (result.issues) return { issues: result.issues };
1546
+ messages.push(result.value);
1547
+ }
1548
+ return { value: {
1549
+ generationId: value["generationId"],
1550
+ throughMessageId: value["throughMessageId"],
1551
+ ...value["throughIndex"] === void 0 ? {} : { throughIndex: value["throughIndex"] },
1552
+ messages,
1553
+ ...value["summary"] === void 0 ? {} : { summary: value["summary"] },
1554
+ ...value["usage"] === void 0 ? {} : { usage: value["usage"] },
1555
+ ...value["retainedMessageIds"] === void 0 ? {} : { retainedMessageIds: value["retainedMessageIds"] }
1556
+ } };
1557
+ });
1558
+ function createEvents(options) {
1559
+ const messageSchema = options?.messageSchema;
1560
+ if (messageSchema) assertSyncSchema(messageSchema, "the AI message schema");
1561
+ const definition = {
1562
+ "ai.control.requested": schema("ai.control.requested", (value) => {
1563
+ if (!isRecord(value) || !isJSONCompatible(value)) return issue("invalid AI command");
1564
+ const action = value["action"];
1565
+ const forwarded = {
1566
+ approval: ["ai.approval.responded", "response"],
1567
+ "tool-result": ["ai.tool.result", "result"],
1568
+ input: ["ai.input.responded", "response"],
1569
+ "request-input": ["ai.input.requested", "request"],
1570
+ retry: ["ai.retry.requested", "request"],
1571
+ interrupt: ["ai.message.interrupted", "request"]
1572
+ };
1573
+ if (typeof action === "string" && Object.hasOwn(forwarded, action)) {
1574
+ const [type, key] = forwarded[action];
1575
+ const payload = validateSync(definition[type], value[key], "AI command");
1576
+ if (payload.issues) return { issues: payload.issues };
1577
+ return { value: {
1578
+ action,
1579
+ [key]: payload.value
1580
+ } };
1581
+ }
1582
+ if (action === "send" || action === "edit" || action === "steer") {
1583
+ const parsed = validateMessage(value["message"], messageSchema);
1584
+ if (parsed.issues) return parsed;
1585
+ if ((action === "send" || action === "steer") && (parsed.value.id.length === 0 || parsed.value.id.startsWith("a2.ai:"))) return issue("AI input IDs must be nonempty and outside the reserved internal prefix");
1586
+ if (action === "steer" && (!stringField(value, "turnId") || !optionalIndexField(value, "lastSeenIndex")) || action === "send" && value["generate"] !== void 0 && typeof value["generate"] !== "boolean") return issue("invalid AI command");
1587
+ return { value: {
1588
+ ...value,
1589
+ message: parsed.value
1590
+ } };
1591
+ }
1592
+ if (action === "remove" && stringField(value, "inputId") || action === "send-now" && stringField(value, "inputId") && (value["turnId"] === null || stringField(value, "turnId")) && optionalIndexField(value, "lastSeenIndex") || action === "move" && stringField(value, "inputId") && (value["beforeId"] === null || stringField(value, "beforeId")) || action === "stop" && stringField(value, "turnId") && optionalIndexField(value, "lastSeenIndex") || action === "pause" && (value["when"] === "now" || value["when"] === "after-turn") || action === "resume") return { value };
1593
+ return issue("invalid AI command");
1594
+ }),
1595
+ "ai.control.committed": jsonObject("ai.control.committed", (value) => Array.isArray(value["changes"]) && isRecord(value["view"])),
1596
+ "ai.control.decided": jsonObject("ai.control.decided", (value) => stringField(value, "commandId") && (value["outcome"] === "applied" || value["outcome"] === "rejected")),
1597
+ "ai.work.reported": schema("ai.work.reported", (value) => {
1598
+ if (!isRecord(value) || !isJSONCompatible(value) || !stringField(value, "workId") || !stringField(value, "turnId") || typeof value["version"] !== "number" || !optionalIndexField(value, "version") || typeof value["attempt"] !== "number" || !Number.isSafeInteger(value["attempt"]) || value["attempt"] < 1 || typeof value["settled"] !== "boolean" || value["kind"] !== "model" && value["kind"] !== "tool" || !Array.isArray(value["events"])) return issue("invalid AI work report");
1599
+ const allowed = /* @__PURE__ */ new Set([
1600
+ "ai.generation.started",
1601
+ "ai.generation.completed",
1602
+ "ai.generation.failed",
1603
+ "ai.tool.called",
1604
+ "ai.tool.result",
1605
+ "ai.approval.requested",
1606
+ "ai.approval.responded",
1607
+ "ai.compaction.requested",
1608
+ "ai.compaction.completed",
1609
+ "ai.model.metadata.requested"
1610
+ ]);
1611
+ const events = [];
1612
+ for (const event of value["events"]) {
1613
+ if (!isRecord(event) || !stringField(event, "id") || typeof event["type"] !== "string" || !allowed.has(event["type"]) || value["kind"] === "tool" && event["type"] !== "ai.tool.result") return issue("invalid AI work fact");
1614
+ const parsed = validateSync(definition[event["type"]], event["payload"], "AI work fact");
1615
+ if (parsed.issues) return { issues: parsed.issues };
1616
+ events.push({
1617
+ id: event["id"],
1618
+ type: event["type"],
1619
+ payload: parsed.value
1620
+ });
1621
+ }
1622
+ return { value: {
1623
+ ...value,
1624
+ events
1625
+ } };
1626
+ }),
1627
+ "ai.tool.execution.requested": jsonObject("ai.tool.execution.requested", (value) => stringField(value, "turnId") && optionalIndexField(value, "version") && isRecord(value["call"]) && isRecord(value["generation"])),
1628
+ "ai.generation.paused": jsonObject("ai.generation.paused", (value) => stringField(value, "generationId") && stringField(value, "messageId")),
1629
+ "ai.session.created": jsonObject("ai.session.created", () => true),
1630
+ "ai.session.closed": jsonObject("ai.session.closed", (value) => optionalStringField(value, "reason")),
1631
+ "ai.message.created": messagePayloadSchema(messageSchema),
1632
+ "ai.message.completed": jsonObject("ai.message.completed", (value) => stringField(value, "messageId")),
1633
+ "ai.message.interrupted": jsonObject("ai.message.interrupted", (value) => stringField(value, "messageId") && (stringField(value, "generationId") && value["requestId"] === void 0 || stringField(value, "requestId") && value["generationId"] === void 0) && optionalStringField(value, "reason") && (value["lastSeenIndex"] === void 0 || typeof value["lastSeenIndex"] === "number" && Number.isInteger(value["lastSeenIndex"]) && value["lastSeenIndex"] >= 0)),
1634
+ "ai.generation.requested": jsonObject("ai.generation.requested", (value) => stringField(value, "messageId") && stringField(value, "responseMessageId") && optionalStringField(value, "sourceGenerationId") && (value["reason"] !== "tool" || stringField(value, "sourceGenerationId")) && (value["reason"] === "message" || value["reason"] === "tool" || value["reason"] === "input" || value["reason"] === "retry" || value["reason"] === "resume")),
1635
+ "ai.retry.requested": jsonObject("ai.retry.requested", (value) => stringField(value, "messageId") && stringField(value, "responseMessageId") && stringField(value, "retryId")),
1636
+ "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") && optionalIndexField(value, "promptThroughIndex")),
1637
+ "ai.generation.progress": progressPayloadSchema,
1638
+ "ai.generation.completed": completedPayloadSchema,
1639
+ "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") && (value["stepLimit"] === void 0 || typeof value["stepLimit"] === "boolean")),
1640
+ "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") && (value["supportsDeferredResults"] === void 0 || typeof value["supportsDeferredResults"] === "boolean") && optionalStringField(value, "title")),
1641
+ "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")),
1642
+ "ai.approval.requested": jsonObject("ai.approval.requested", (value) => stringField(value, "requestId") && stringField(value, "messageId") && stringField(value, "generationId") && stringField(value, "approvalId") && stringField(value, "toolCallId") && (value["isAutomatic"] === void 0 || typeof value["isAutomatic"] === "boolean") && optionalStringField(value, "signature")),
1643
+ "ai.approval.responded": jsonObject("ai.approval.responded", (value) => stringField(value, "requestId") && stringField(value, "messageId") && stringField(value, "generationId") && stringField(value, "approvalId") && typeof value["approved"] === "boolean" && optionalStringField(value, "reason")),
1644
+ "ai.input.requested": jsonObject("ai.input.requested", (value) => stringField(value, "requestId") && stringField(value, "messageId") && stringField(value, "generationId") && stringField(value, "inputId") && stringField(value, "name")),
1645
+ "ai.input.responded": jsonObject("ai.input.responded", (value) => stringField(value, "requestId") && stringField(value, "messageId") && stringField(value, "generationId") && stringField(value, "inputId") && stringField(value, "name")),
1646
+ "ai.compaction.requested": jsonObject("ai.compaction.requested", (value) => stringField(value, "generationId") && stringField(value, "throughMessageId") && optionalIndexField(value, "throughIndex") && optionalIndexField(value, "inputTokenEstimate") && optionalIndexField(value, "inputTokens") && optionalIndexField(value, "thresholdTokens")),
1647
+ "ai.compaction.completed": compactionPayloadSchema(messageSchema),
1648
+ "ai.model.metadata.requested": jsonObject("ai.model.metadata.requested", (value) => stringField(value, "modelId")),
1649
+ "ai.model.metadata.resolved": jsonObject("ai.model.metadata.resolved", (value) => stringField(value, "modelId") && (value["limits"] === null || isRecord(value["limits"]) && typeof value["limits"]["contextWindow"] === "number" && Number.isSafeInteger(value["limits"]["contextWindow"]) && value["limits"]["contextWindow"] > 0 && typeof value["limits"]["maxOutputTokens"] === "number" && Number.isSafeInteger(value["limits"]["maxOutputTokens"]) && value["limits"]["maxOutputTokens"] > 0))
1650
+ };
1651
+ return definition;
1652
+ }
1653
+ const events = createEvents();
1654
+ const initialState = () => ({
1655
+ inbox: initialControlState().inbox,
1656
+ pendingQueueCommands: [],
1657
+ active: null,
1658
+ starting: false,
1659
+ receipt: null,
1660
+ session: { created: false },
1661
+ messages: [],
1662
+ status: "idle",
1663
+ activeGeneration: null,
1664
+ activeRequestId: null,
1665
+ activeRequestReason: null,
1666
+ activeRequestSourceGenerationId: null,
1667
+ activeResponseMessageId: null,
1668
+ activeProjection: null,
1669
+ responseOwners: {},
1670
+ pendingApprovals: [],
1671
+ pendingInputs: [],
1672
+ tools: [],
1673
+ compaction: null,
1674
+ modelMetadata: {},
1675
+ usage: [],
1676
+ error: null
1677
+ });
1678
+ const projectMessageChunks = (messages, messageId, chunks) => messages.map((message) => message.id === messageId ? projectUIMessage({
1679
+ messageId,
1680
+ base: message,
1681
+ chunks
1682
+ }) : message);
1683
+ const waitingStatus = (state) => {
1684
+ if (state.pendingApprovals.length > 0 || state.pendingInputs.length > 0) return "waiting";
1685
+ return state.activeGeneration !== null || state.tools.some((activity) => activity.status === "running") ? "generating" : "idle";
1686
+ };
1687
+ const lifecycleStatus = (previous, next) => previous.status === "failed" ? "failed" : waitingStatus(next);
1688
+ const isStaleGeneration = (state, messageId, owner) => {
1689
+ const current = state.responseOwners[messageId];
1690
+ return current !== void 0 && (current.generationId !== owner.generationId || current.requestId !== owner.requestId);
1691
+ };
1692
+ const projectedResponse = (options) => {
1693
+ const message = projectedMessage(options.projection);
1694
+ return options.state.pausedGenerationIds?.[options.projection.generationId] ? pauseUIMessage({
1695
+ message,
1696
+ toolCallIds: new Set(options.state.tools.map((tool) => tool.toolCallId))
1697
+ }) : message;
1698
+ };
1699
+ const terminalGenerationReason = (state, generationId, requestId) => {
1700
+ const terminal = state.terminalGenerations?.[generationId];
1701
+ if (terminal !== void 0) return terminal;
1702
+ return requestId !== void 0 && state.terminalRequestIds?.[requestId] === true ? "interrupted" : void 0;
1703
+ };
1704
+ /** Pure projection of the built-in AI protocol. Unknown extension events are ignored. */
1705
+ function reduceAIState(state, event) {
1706
+ if (state.status === "closed") return state;
1707
+ switch (event.type) {
1708
+ case "ai.control.requested": {
1709
+ const command = event.payload;
1710
+ if (!event.id || !isQueueCommand(command)) return state;
1711
+ const pending = state.pendingQueueCommands ?? [];
1712
+ if (pending.some((item) => item.commandId === event.id)) return state;
1713
+ if ((command.action === "send" || command.action === "steer") && (state.messages.some((message) => message.id === command.message.id) || state.inbox.items.some((item) => item.id === command.message.id))) return state;
1714
+ return {
1715
+ ...state,
1716
+ pendingQueueCommands: [...pending, {
1717
+ commandId: event.id,
1718
+ command
1719
+ }]
1720
+ };
1721
+ }
1722
+ case "ai.control.committed": {
1723
+ const commit = event.payload;
1724
+ const visible = applyControlChanges({
1725
+ state: {
1726
+ inbox: state.inbox,
1727
+ active: state.active
1728
+ },
1729
+ changes: commit.changes.filter((change) => change.path[0] === "inbox" || change.path[0] === "active")
1730
+ });
1731
+ const { phase } = commit.view;
1732
+ const status = commit.view.closed ? "closed" : phase === "paused" || phase === "pausing" || commit.view.paused && phase === null ? "paused" : phase === "failed" ? "failed" : phase === "starting" || phase === "generating" ? "generating" : phase ? "waiting" : "idle";
1733
+ return {
1734
+ ...state,
1735
+ ...visible,
1736
+ status,
1737
+ activeRequestId: commit.view.requestId,
1738
+ activeRequestReason: commit.view.requestReason,
1739
+ activeRequestSourceGenerationId: commit.view.sourceGenerationId,
1740
+ activeResponseMessageId: commit.view.responseMessageId
1741
+ };
1742
+ }
1743
+ case "ai.control.decided": {
1744
+ const receipt = event.payload;
1745
+ if (receipt.work) return state;
1746
+ const pending = state.pendingQueueCommands ?? [];
1747
+ const settled = pending.find((item) => item.commandId === receipt.commandId);
1748
+ return {
1749
+ ...state,
1750
+ receipt,
1751
+ ...settled === void 0 ? {} : { pendingQueueCommands: pending.filter((item) => item.commandId !== receipt.commandId) }
1752
+ };
1753
+ }
1754
+ case "ai.generation.paused": {
1755
+ const payload = event.payload;
1756
+ const toolCallIds = new Set(state.tools.filter((tool) => tool.messageId === payload.messageId).map((tool) => tool.toolCallId));
1757
+ const projection = state.activeProjection;
1758
+ return {
1759
+ ...state,
1760
+ activeGeneration: null,
1761
+ activeProjection: projection?.generationId === payload.generationId ? {
1762
+ ...projection,
1763
+ cursor: null,
1764
+ batches: progressBatches(flattenProgressBatches(projection.batches).map((batch) => ({
1765
+ index: batch.index,
1766
+ chunks: batch.chunks.filter((chunk) => !("toolCallId" in chunk) || toolCallIds.has(chunk.toolCallId))
1767
+ })))
1768
+ } : projection,
1769
+ compaction: state.compaction?.status === "running" ? projection?.baseCompaction ?? null : state.compaction,
1770
+ messages: state.messages.map((message) => message.id === payload.messageId ? pauseUIMessage({
1771
+ message,
1772
+ toolCallIds
1773
+ }) : message),
1774
+ pausedGenerationIds: {
1775
+ ...state.pausedGenerationIds,
1776
+ [payload.generationId]: true
1777
+ }
1778
+ };
1779
+ }
1780
+ case "ai.model.metadata.requested": {
1781
+ const { modelId } = event.payload;
1782
+ if (Object.hasOwn(state.modelMetadata, modelId)) return state;
1783
+ return {
1784
+ ...state,
1785
+ modelMetadata: {
1786
+ ...state.modelMetadata,
1787
+ [modelId]: { status: "pending" }
1788
+ }
1789
+ };
1790
+ }
1791
+ case "ai.model.metadata.resolved": {
1792
+ const { modelId, limits } = event.payload;
1793
+ return {
1794
+ ...state,
1795
+ modelMetadata: {
1796
+ ...state.modelMetadata,
1797
+ [modelId]: limits === null ? { status: "unavailable" } : {
1798
+ status: "resolved",
1799
+ limits
1800
+ }
1801
+ }
1802
+ };
1803
+ }
1804
+ case "ai.session.created": {
1805
+ const payload = event.payload;
1806
+ return {
1807
+ ...state,
1808
+ session: {
1809
+ created: true,
1810
+ ...payload.metadata === void 0 ? state.session.metadata === void 0 ? {} : { metadata: state.session.metadata } : { metadata: payload.metadata }
1811
+ }
1812
+ };
1813
+ }
1814
+ case "ai.session.closed": return {
1815
+ ...state,
1816
+ active: null,
1817
+ inbox: {
1818
+ paused: true,
1819
+ items: []
1820
+ },
1821
+ pendingQueueCommands: [],
1822
+ status: "closed",
1823
+ compaction: state.compaction?.status === "running" ? state.activeProjection?.baseCompaction ?? null : state.compaction,
1824
+ activeGeneration: null,
1825
+ activeRequestId: null,
1826
+ activeRequestReason: null,
1827
+ activeRequestSourceGenerationId: null,
1828
+ activeResponseMessageId: null,
1829
+ activeProjection: null
1830
+ };
1831
+ case "ai.message.created": {
1832
+ const { message } = event.payload;
1833
+ return {
1834
+ ...state,
1835
+ messages: upsertMessage(state.messages, message),
1836
+ activeProjection: state.activeProjection?.responseMessageId === message.id ? {
1837
+ ...state.activeProjection,
1838
+ cursor: null
1839
+ } : state.activeProjection
1840
+ };
1841
+ }
1842
+ case "ai.message.completed": {
1843
+ const { messageId } = event.payload;
1844
+ const owner = state.responseOwners[messageId];
1845
+ const generationId = owner?.generationId;
1846
+ const requestId = owner?.requestId;
1847
+ if (generationId === void 0 || terminalGenerationReason(state, generationId, requestId) !== void 0) return state;
1848
+ return {
1849
+ ...state,
1850
+ activeRequestId: null,
1851
+ activeRequestReason: null,
1852
+ activeRequestSourceGenerationId: null,
1853
+ activeResponseMessageId: null,
1854
+ activeProjection: state.activeProjection?.generationId === generationId ? null : state.activeProjection,
1855
+ terminalGenerations: {
1856
+ ...state.terminalGenerations,
1857
+ [generationId]: "completed"
1858
+ },
1859
+ ...requestId === void 0 ? {} : { terminalRequestIds: {
1860
+ ...state.terminalRequestIds,
1861
+ [requestId]: true
1862
+ } }
1863
+ };
1864
+ }
1865
+ case "ai.generation.requested": {
1866
+ const payload = event.payload;
1867
+ const schedulesToolContinuation = payload.reason === "tool";
1868
+ const responseMessageId = payload.responseMessageId;
1869
+ const owner = state.responseOwners[responseMessageId];
1870
+ const ownerGenerationId = owner?.generationId;
1871
+ const ownerRequestId = owner?.requestId;
1872
+ const requestId = event.id;
1873
+ if (requestId !== void 0 && state.terminalRequestIds?.[requestId] === true) return state;
1874
+ if (!(payload.control !== void 0 && state.activeRequestId === event.id || payload.reason === "message" || (payload.reason === "tool" ? ownerGenerationId !== void 0 && payload.sourceGenerationId === ownerGenerationId && state.activeRequestId === ownerRequestId && state.status !== "failed" : payload.reason === "retry" ? state.status === "failed" : ownerGenerationId !== void 0 && payload.sourceGenerationId === ownerGenerationId && state.activeRequestId === ownerRequestId && state.status !== "failed"))) return state;
1875
+ return {
1876
+ ...payload.reason === "retry" && state.activeProjection && state.activeProjection.completionIndex === void 0 ? restoreGenerationBase(state, state.activeProjection.generationId, responseMessageId, "failed") : state,
1877
+ activeProjection: payload.reason === "retry" ? null : state.activeProjection,
1878
+ status: schedulesToolContinuation ? state.status : "generating",
1879
+ activeRequestId: requestId ?? null,
1880
+ activeRequestReason: payload.reason,
1881
+ activeRequestSourceGenerationId: payload.sourceGenerationId ?? null,
1882
+ activeResponseMessageId: responseMessageId,
1883
+ error: schedulesToolContinuation ? state.error : null
1884
+ };
1885
+ }
1886
+ case "ai.generation.started": {
1887
+ const payload = event.payload;
1888
+ if (state.terminalRequestIds?.[payload.requestId] === true || terminalGenerationReason(state, payload.generationId, payload.requestId) !== void 0) return state;
1889
+ if (state.activeRequestId !== null && state.activeRequestId !== payload.requestId) return state;
1890
+ const currentOwner = state.responseOwners[payload.responseMessageId];
1891
+ const currentTerminal = currentOwner === void 0 ? void 0 : terminalGenerationReason(state, currentOwner.generationId, currentOwner.requestId);
1892
+ if (currentTerminal === "completed" || currentTerminal === "interrupted") return state;
1893
+ if (currentOwner?.requestId === payload.requestId && currentOwner.attempt >= payload.attempt) return state;
1894
+ const activeGeneration = state.activeGeneration;
1895
+ const baseState = activeGeneration !== null && activeGeneration.requestId === payload.requestId && activeGeneration.responseMessageId === payload.responseMessageId && activeGeneration.attempt < payload.attempt && state.activeProjection?.generationId === activeGeneration.generationId ? restoreGenerationBase(state, activeGeneration.generationId, activeGeneration.responseMessageId, "superseded") : state;
1896
+ const baseMessage = baseState.messages.find((message) => message.id === payload.responseMessageId);
1897
+ return {
1898
+ ...baseState,
1899
+ status: "generating",
1900
+ activeGeneration: payload,
1901
+ activeResponseMessageId: null,
1902
+ responseOwners: {
1903
+ ...baseState.responseOwners,
1904
+ [payload.responseMessageId]: {
1905
+ generationId: payload.generationId,
1906
+ requestId: payload.requestId,
1907
+ attempt: payload.attempt
1908
+ }
1909
+ },
1910
+ activeProjection: {
1911
+ generationId: payload.generationId,
1912
+ responseMessageId: payload.responseMessageId,
1913
+ promptThroughIndex: payload.promptThroughIndex ?? event.index - 1,
1914
+ ...baseMessage === void 0 ? {} : { baseMessage },
1915
+ baseCompaction: baseState.compaction,
1916
+ batches: progressBatches(),
1917
+ baseTools: baseState.tools,
1918
+ toolEvents: [],
1919
+ approvalEvents: []
1920
+ },
1921
+ error: null
1922
+ };
1923
+ }
1924
+ case "ai.generation.progress": {
1925
+ const payload = event.payload;
1926
+ if (state.pausedGenerationIds?.[payload.generationId]) return state;
1927
+ if (terminalGenerationReason(state, payload.generationId, payload.requestId) !== void 0) return state;
1928
+ const activeProjection = state.activeProjection;
1929
+ if (state.activeGeneration?.generationId !== payload.generationId || state.activeGeneration.requestId !== payload.requestId || activeProjection?.generationId !== payload.generationId) return state;
1930
+ const progress = advanceProjection({
1931
+ projection: activeProjection,
1932
+ base: state.messages.find((message) => message.id === activeProjection.responseMessageId),
1933
+ chunks: payload.chunks,
1934
+ index: event.index
1935
+ });
1936
+ return {
1937
+ ...state,
1938
+ messages: upsertResponse({
1939
+ messages: state.messages,
1940
+ message: progress.message,
1941
+ inputId: state.activeGeneration.messageId
1942
+ }),
1943
+ activeProjection: {
1944
+ ...activeProjection,
1945
+ batches: progress.batches,
1946
+ cursor: progress.cursor
1947
+ },
1948
+ status: "generating"
1949
+ };
1950
+ }
1951
+ case "ai.generation.completed": {
1952
+ const payload = event.payload;
1953
+ if (terminalGenerationReason(state, payload.generationId, payload.requestId) !== void 0) return state;
1954
+ if (state.activeGeneration?.generationId !== payload.generationId || state.activeGeneration.requestId !== payload.requestId) return state;
1955
+ const usage = payload.usage ? [...state.usage.filter((entry) => entry.generationId !== payload.generationId), {
1956
+ generationId: payload.generationId,
1957
+ usage: payload.usage
1958
+ }] : state.usage;
1959
+ const next = {
1960
+ ...state,
1961
+ activeGeneration: null,
1962
+ activeProjection: state.activeProjection?.generationId === payload.generationId ? {
1963
+ ...state.activeProjection,
1964
+ completionIndex: event.index
1965
+ } : state.activeProjection,
1966
+ usage
1967
+ };
1968
+ return {
1969
+ ...next,
1970
+ status: waitingStatus(next)
1971
+ };
1972
+ }
1973
+ case "ai.generation.failed": {
1974
+ const payload = event.payload;
1975
+ if (terminalGenerationReason(state, payload.generationId, payload.requestId) !== void 0) return state;
1976
+ if ((state.activeGeneration?.generationId !== payload.generationId || state.activeGeneration.requestId !== payload.requestId) && !(payload.stepLimit === true && state.activeRequestReason === "tool" && state.responseOwners[payload.responseMessageId]?.requestId === payload.requestId && state.responseOwners[payload.responseMessageId]?.generationId === payload.generationId && state.activeRequestSourceGenerationId === payload.generationId && state.activeResponseMessageId === payload.responseMessageId)) return state;
1977
+ if (payload.superseded === true) return restoreGenerationBase(state, payload.generationId, payload.responseMessageId, "superseded");
1978
+ const projection = state.activeProjection?.generationId === payload.generationId ? state.activeProjection : null;
1979
+ const shouldProject = projection !== null && (projection.baseMessage !== void 0 || projection.batches.length > 0);
1980
+ return {
1981
+ ...state,
1982
+ messages: shouldProject ? upsertMessage(state.messages, interruptUIMessage(projectedMessage(projection), `Generation failed before tool completion: ${payload.error}`)) : state.messages,
1983
+ status: "failed",
1984
+ activeGeneration: null,
1985
+ activeResponseMessageId: null,
1986
+ activeProjection: projection,
1987
+ terminalGenerations: {
1988
+ ...state.terminalGenerations,
1989
+ [payload.generationId]: "failed"
1990
+ },
1991
+ terminalRequestIds: {
1992
+ ...state.terminalRequestIds,
1993
+ [payload.requestId]: true,
1994
+ ...state.activeRequestId === null ? {} : { [state.activeRequestId]: true }
1995
+ },
1996
+ compaction: projection?.generationId === payload.generationId ? projection.baseCompaction ?? null : state.compaction?.generationId === payload.generationId ? null : state.compaction,
1997
+ pendingApprovals: state.pendingApprovals.filter((approval) => approval.generationId !== payload.generationId),
1998
+ pendingInputs: state.pendingInputs.filter((input) => input.generationId !== payload.generationId),
1999
+ tools: stopGenerationTools(state.tools, payload.generationId, payload.responseMessageId, payload.error),
2000
+ error: payload.error
2001
+ };
2002
+ }
2003
+ case "ai.message.interrupted": {
2004
+ const payload = event.payload;
2005
+ const owner = state.responseOwners[payload.messageId];
2006
+ const currentGenerationId = owner?.generationId;
2007
+ const targetRequestId = payload.requestId ?? (owner?.generationId === payload.generationId ? owner?.requestId : void 0);
2008
+ const ownsRequestPhase = payload.requestId !== void 0 && state.activeGeneration === null && state.activeRequestId === payload.requestId && state.activeResponseMessageId === payload.messageId;
2009
+ if (currentGenerationId !== void 0 && !ownsRequestPhase && (payload.generationId !== void 0 ? currentGenerationId !== payload.generationId : owner?.requestId !== payload.requestId)) return state;
2010
+ if (state.activeGeneration === null && state.activeRequestId !== null && state.activeResponseMessageId === payload.messageId && targetRequestId !== state.activeRequestId) return state;
2011
+ const currentTerminal = currentGenerationId === void 0 ? void 0 : terminalGenerationReason(state, currentGenerationId, owner?.requestId);
2012
+ if (currentTerminal === "completed" || currentTerminal === "interrupted" || currentTerminal === "superseded") return state;
2013
+ const active = state.activeGeneration;
2014
+ if (active === null) {
2015
+ if (payload.requestId !== void 0 && state.activeRequestId !== null && state.activeRequestId === payload.requestId && state.activeResponseMessageId === payload.messageId) {
2016
+ const baseState = (currentGenerationId === void 0 ? null : interruptGenerationProjection(state, currentGenerationId, payload.messageId, payload.lastSeenIndex ?? Number.POSITIVE_INFINITY)) ?? state;
2017
+ return {
2018
+ ...baseState,
2019
+ status: "idle",
2020
+ activeRequestId: null,
2021
+ activeRequestReason: null,
2022
+ activeRequestSourceGenerationId: null,
2023
+ activeResponseMessageId: null,
2024
+ activeProjection: null,
2025
+ terminalRequestIds: {
2026
+ ...baseState.terminalRequestIds,
2027
+ [payload.requestId]: true
2028
+ },
2029
+ ...currentGenerationId === void 0 ? {} : { terminalGenerations: {
2030
+ ...baseState.terminalGenerations,
2031
+ [currentGenerationId]: "interrupted"
2032
+ } },
2033
+ error: null
2034
+ };
2035
+ }
2036
+ const targetGenerationId = payload.generationId ?? (currentGenerationId !== void 0 && owner?.requestId === payload.requestId ? currentGenerationId : void 0);
2037
+ if (targetGenerationId === void 0) return state;
2038
+ if (currentGenerationId !== targetGenerationId) return state;
2039
+ const matchesGeneration = (generationId) => targetGenerationId === generationId;
2040
+ const hasMessageWork = state.pendingApprovals.some((approval) => approval.messageId === payload.messageId && matchesGeneration(approval.generationId)) || state.pendingInputs.some((input) => input.messageId === payload.messageId && matchesGeneration(input.generationId)) || state.tools.some((activity) => activity.messageId === payload.messageId && activity.status === "running" && matchesGeneration(activity.generationId));
2041
+ const resolvesFailedGeneration = state.status === "failed" && currentGenerationId === targetGenerationId && (state.activeRequestId === null || state.activeRequestId === targetRequestId || state.activeRequestReason === "tool" && state.activeRequestSourceGenerationId === targetGenerationId);
2042
+ if (!hasMessageWork && !resolvesFailedGeneration && !(currentGenerationId === targetGenerationId && currentTerminal === void 0)) return state;
2043
+ const pendingRequestId = state.activeRequestId;
2044
+ const projectedState = interruptGenerationProjection(state, targetGenerationId, payload.messageId, payload.lastSeenIndex ?? Number.POSITIVE_INFINITY);
2045
+ const baseState = projectedState ?? state;
2046
+ const next = {
2047
+ ...baseState,
2048
+ activeRequestId: null,
2049
+ activeRequestReason: null,
2050
+ activeRequestSourceGenerationId: null,
2051
+ activeResponseMessageId: null,
2052
+ activeProjection: null,
2053
+ messages: projectedState === null ? state.messages.map((message) => message.id === payload.messageId ? interruptUIMessage(message) : message) : baseState.messages,
2054
+ pendingApprovals: baseState.pendingApprovals.filter((approval) => approval.messageId !== payload.messageId || !matchesGeneration(approval.generationId)),
2055
+ pendingInputs: baseState.pendingInputs.filter((input) => input.messageId !== payload.messageId || !matchesGeneration(input.generationId)),
2056
+ tools: projectedState === null ? stopGenerationTools(state.tools, targetGenerationId, payload.messageId, "Tool execution was interrupted.") : baseState.tools,
2057
+ terminalGenerations: {
2058
+ ...baseState.terminalGenerations,
2059
+ [targetGenerationId]: "interrupted"
2060
+ },
2061
+ ...pendingRequestId === null ? {} : { terminalRequestIds: {
2062
+ ...baseState.terminalRequestIds,
2063
+ [pendingRequestId]: true
2064
+ } },
2065
+ error: resolvesFailedGeneration ? null : state.error
2066
+ };
2067
+ return {
2068
+ ...next,
2069
+ status: waitingStatus(next)
2070
+ };
2071
+ }
2072
+ if (payload.messageId !== active.responseMessageId || payload.generationId !== active.generationId && payload.requestId !== active.requestId) return state;
2073
+ const cutoff = payload.lastSeenIndex ?? Number.POSITIVE_INFINITY;
2074
+ const projectedState = interruptGenerationProjection(state, active.generationId, active.responseMessageId, cutoff);
2075
+ const baseState = projectedState ?? restoreGenerationBase(state, active.generationId, active.responseMessageId, "interrupted");
2076
+ const next = {
2077
+ ...baseState,
2078
+ activeRequestId: null,
2079
+ activeRequestReason: null,
2080
+ activeRequestSourceGenerationId: null,
2081
+ activeResponseMessageId: null,
2082
+ terminalRequestIds: {
2083
+ ...baseState.terminalRequestIds,
2084
+ [active.requestId]: true
2085
+ },
2086
+ tools: projectedState === null ? stopGenerationTools(baseState.tools, active.generationId, active.responseMessageId, "Tool execution was interrupted.") : baseState.tools
2087
+ };
2088
+ return {
2089
+ ...next,
2090
+ status: lifecycleStatus(state, next)
2091
+ };
2092
+ }
2093
+ case "ai.tool.called": {
2094
+ const payload = event.payload;
2095
+ const projection = state.activeProjection;
2096
+ if (isStaleGeneration(state, payload.messageId, payload) || terminalGenerationReason(state, payload.generationId, payload.requestId) !== void 0) return state;
2097
+ if (state.tools.some((activity) => activity.generationId === payload.generationId && activity.toolCallId === payload.toolCallId && activity.status === "failed" && state.activeGeneration?.generationId !== payload.generationId)) return state;
2098
+ if (projection?.generationId !== payload.generationId) {
2099
+ const next = {
2100
+ ...state,
2101
+ tools: reduceToolActivity(state.tools, {
2102
+ type: "ai.tool.called",
2103
+ payload
2104
+ })
2105
+ };
2106
+ return {
2107
+ ...next,
2108
+ status: lifecycleStatus(state, next)
2109
+ };
2110
+ }
2111
+ const activeProjection = {
2112
+ ...projection,
2113
+ toolEvents: [...projection.toolEvents, {
2114
+ index: event.index,
2115
+ type: "ai.tool.called",
2116
+ payload
2117
+ }]
2118
+ };
2119
+ const next = {
2120
+ ...state,
2121
+ activeProjection,
2122
+ tools: projectedTools(activeProjection)
2123
+ };
2124
+ return {
2125
+ ...next,
2126
+ status: lifecycleStatus(state, next)
2127
+ };
2128
+ }
2129
+ case "ai.tool.result": {
2130
+ const payload = event.payload;
2131
+ const projection = state.activeProjection;
2132
+ const terminalReason = terminalGenerationReason(state, payload.generationId, payload.requestId);
2133
+ const completesFailedTool = terminalReason === "failed" && state.status === "failed" && payload.preliminary !== true && projection?.generationId === payload.generationId && projectedTools(projection).some((activity) => activity.generationId === payload.generationId && activity.toolCallId === payload.toolCallId && activity.status === "running");
2134
+ if (isStaleGeneration(state, payload.messageId, payload) || terminalReason !== void 0 && !completesFailedTool) return state;
2135
+ if (state.tools.some((activity) => activity.generationId === payload.generationId && activity.toolCallId === payload.toolCallId && activity.status === "failed" && state.activeGeneration?.generationId !== payload.generationId && !completesFailedTool)) return state;
2136
+ if (projection?.generationId !== payload.generationId) {
2137
+ const next = {
2138
+ ...state,
2139
+ messages: projectMessageChunks(state.messages, payload.messageId, [toolResultChunk(payload)]),
2140
+ tools: reduceToolActivity(state.tools, {
2141
+ type: "ai.tool.result",
2142
+ payload
2143
+ })
2144
+ };
2145
+ return {
2146
+ ...next,
2147
+ status: lifecycleStatus(state, next)
2148
+ };
2149
+ }
2150
+ const activeProjection = {
2151
+ ...projection,
2152
+ cursor: null,
2153
+ toolEvents: [...projection.toolEvents, {
2154
+ index: event.index,
2155
+ type: "ai.tool.result",
2156
+ payload
2157
+ }]
2158
+ };
2159
+ const projected = projectedResponse({
2160
+ state,
2161
+ projection: activeProjection
2162
+ });
2163
+ const tools = projectedTools(activeProjection);
2164
+ const failureError = state.error ?? "Generation failed.";
2165
+ const next = {
2166
+ ...state,
2167
+ messages: upsertMessage(state.messages, terminalReason === "failed" ? interruptUIMessage(projected, `Generation failed before tool completion: ${failureError}`) : projected),
2168
+ activeProjection,
2169
+ tools: terminalReason === "failed" ? stopGenerationTools(tools, payload.generationId, payload.messageId, failureError) : tools
2170
+ };
2171
+ return {
2172
+ ...next,
2173
+ status: lifecycleStatus(state, next)
2174
+ };
2175
+ }
2176
+ case "ai.approval.requested": {
2177
+ const payload = event.payload;
2178
+ const projection = state.activeProjection;
2179
+ if (isStaleGeneration(state, payload.messageId, payload) || terminalGenerationReason(state, payload.generationId, payload.requestId) !== void 0) return state;
2180
+ if (state.tools.some((activity) => activity.generationId === payload.generationId && activity.toolCallId === payload.toolCallId && activity.status === "failed" && state.activeGeneration?.generationId !== payload.generationId)) return state;
2181
+ const activeProjection = projection?.generationId === payload.generationId ? {
2182
+ ...projection,
2183
+ cursor: null,
2184
+ approvalEvents: [...projection.approvalEvents, {
2185
+ index: event.index,
2186
+ type: "ai.approval.requested",
2187
+ payload
2188
+ }]
2189
+ } : projection;
2190
+ const next = {
2191
+ ...state,
2192
+ messages: activeProjection === null || activeProjection === projection ? projectMessageChunks(state.messages, payload.messageId, [approvalRequestedChunk(payload)]) : upsertMessage(state.messages, projectedResponse({
2193
+ state,
2194
+ projection: activeProjection
2195
+ })),
2196
+ activeProjection,
2197
+ pendingApprovals: payload.isAutomatic === true ? state.pendingApprovals : [...state.pendingApprovals.filter((approval) => approval.approvalId !== payload.approvalId || approval.messageId !== payload.messageId), payload]
2198
+ };
2199
+ return {
2200
+ ...next,
2201
+ status: lifecycleStatus(state, next)
2202
+ };
2203
+ }
2204
+ case "ai.approval.responded": {
2205
+ const payload = event.payload;
2206
+ if (isStaleGeneration(state, payload.messageId, payload) || terminalGenerationReason(state, payload.generationId, payload.requestId) !== void 0) return state;
2207
+ const projection = state.activeProjection;
2208
+ const pendingRequest = state.pendingApprovals.find((candidate) => candidate.approvalId === payload.approvalId && candidate.messageId === payload.messageId && candidate.generationId === payload.generationId);
2209
+ const activeRequest = projection?.approvalEvents.findLast((candidate) => candidate.type === "ai.approval.requested" && candidate.payload.approvalId === payload.approvalId && candidate.payload.messageId === payload.messageId && candidate.payload.generationId === payload.generationId);
2210
+ if (state.status === "failed" && pendingRequest === void 0 && activeRequest === void 0) return state;
2211
+ const activeProjection = projection !== null && activeRequest !== void 0 ? {
2212
+ ...projection,
2213
+ cursor: null,
2214
+ approvalEvents: [...projection.approvalEvents, {
2215
+ index: event.index,
2216
+ type: "ai.approval.responded",
2217
+ payload
2218
+ }]
2219
+ } : projection;
2220
+ const next = {
2221
+ ...state,
2222
+ messages: activeProjection === null || activeProjection === projection ? answerApproval(state.messages, payload) : upsertMessage(state.messages, projectedResponse({
2223
+ state,
2224
+ projection: activeProjection
2225
+ })),
2226
+ activeProjection: activeProjection === null ? null : {
2227
+ ...activeProjection,
2228
+ cursor: null
2229
+ },
2230
+ pendingApprovals: state.pendingApprovals.filter((approval) => approval.approvalId !== payload.approvalId || approval.messageId !== payload.messageId || approval.generationId !== payload.generationId)
2231
+ };
2232
+ return {
2233
+ ...next,
2234
+ status: lifecycleStatus(state, next)
2235
+ };
2236
+ }
2237
+ case "ai.input.requested": {
2238
+ const payload = event.payload;
2239
+ if (isStaleGeneration(state, payload.messageId, payload) || terminalGenerationReason(state, payload.generationId, payload.requestId) !== void 0) return state;
2240
+ const next = {
2241
+ ...state,
2242
+ pendingInputs: [...state.pendingInputs.filter((input) => input.messageId !== payload.messageId || input.generationId !== payload.generationId || input.inputId !== payload.inputId), payload]
2243
+ };
2244
+ return {
2245
+ ...next,
2246
+ status: lifecycleStatus(state, next)
2247
+ };
2248
+ }
2249
+ case "ai.input.responded": {
2250
+ const payload = event.payload;
2251
+ if (isStaleGeneration(state, payload.messageId, payload) || terminalGenerationReason(state, payload.generationId, payload.requestId) !== void 0) return state;
2252
+ const next = {
2253
+ ...state,
2254
+ pendingInputs: state.pendingInputs.filter((input) => input.inputId !== payload.inputId || input.messageId !== payload.messageId || input.generationId !== payload.generationId)
2255
+ };
2256
+ return {
2257
+ ...next,
2258
+ status: lifecycleStatus(state, next)
2259
+ };
2260
+ }
2261
+ case "ai.compaction.requested": {
2262
+ const payload = event.payload;
2263
+ if (state.activeGeneration?.generationId !== payload.generationId) return state;
2264
+ return {
2265
+ ...state,
2266
+ compaction: {
2267
+ status: "running",
2268
+ ...payload
2269
+ }
2270
+ };
2271
+ }
2272
+ case "ai.compaction.completed": {
2273
+ const payload = event.payload;
2274
+ if (state.activeGeneration?.generationId !== payload.generationId || state.compaction?.status !== "running" || state.compaction.generationId !== payload.generationId) return state;
2275
+ const compaction = {
2276
+ status: "completed",
2277
+ ...payload,
2278
+ throughIndex: payload.throughIndex ?? state.activeProjection.promptThroughIndex
2279
+ };
2280
+ return {
2281
+ ...state,
2282
+ compaction,
2283
+ activeProjection: state.activeProjection && {
2284
+ ...state.activeProjection,
2285
+ baseCompaction: compaction
2286
+ }
2287
+ };
2288
+ }
2289
+ default: return state;
2290
+ }
2291
+ }
2292
+ /** Derive AI SDK UI messages directly from an A2 event history. */
2293
+ function deriveUIMessages(history) {
2294
+ let state = initialState();
2295
+ for (const event of history) state = reduceAIState(state, event);
2296
+ return state.messages;
2297
+ }
2298
+ /** Build the standard AI state projection for any contract containing the protocol. */
2299
+ function createReducer(options) {
2300
+ const reducer = options.contract.reducer({
2301
+ name: options.name ?? "a2.ai.state.v22",
2302
+ initialState: initialState(),
2303
+ ...options.stateSchema === void 0 ? {} : { stateSchema: options.stateSchema }
2304
+ }).fold(options.fold ?? ((state, event) => reduceAIState(state, event)));
2305
+ return storedAIReducer({
2306
+ ...reducer,
2307
+ [clientStateProjection]: projectAIClientState
2308
+ });
2309
+ }
2310
+ const commandInput = (payload) => [{
2311
+ type: "ai.control.requested",
2312
+ payload
2313
+ }];
2314
+ const messageInput = (message, generate) => [{
2315
+ type: "ai.control.requested",
2316
+ id: message.id,
2317
+ payload: {
2318
+ action: "send",
2319
+ message,
2320
+ ...generate === false ? { generate: false } : {}
2321
+ }
2322
+ }];
2323
+ /** Pure append inputs for the built-in AI protocol. */
2324
+ const inputs = {
2325
+ toolResult: (result) => commandInput({
2326
+ action: "tool-result",
2327
+ result
2328
+ }),
2329
+ queue: {
2330
+ remove: (options) => commandInput({
2331
+ action: "remove",
2332
+ ...options
2333
+ }),
2334
+ sendNow: (options) => commandInput({
2335
+ action: "send-now",
2336
+ ...options
2337
+ }),
2338
+ edit: (options) => commandInput({
2339
+ action: "edit",
2340
+ ...options
2341
+ }),
2342
+ move: (options) => commandInput({
2343
+ action: "move",
2344
+ ...options
2345
+ })
2346
+ },
2347
+ stop: (options) => commandInput({
2348
+ action: "stop",
2349
+ ...options
2350
+ }),
2351
+ steer: (options) => [{
2352
+ type: "ai.control.requested",
2353
+ id: options.message.id,
2354
+ payload: {
2355
+ action: "steer",
2356
+ ...options
2357
+ }
2358
+ }],
2359
+ pause: (options) => commandInput({
2360
+ action: "pause",
2361
+ ...options
2362
+ }),
2363
+ resume: () => commandInput({ action: "resume" }),
2364
+ message(message, options) {
2365
+ return messageInput(message, options?.generate);
2366
+ },
2367
+ seed(message) {
2368
+ return messageInput(message, false);
2369
+ },
2370
+ approval: (response) => [{
2371
+ ...commandInput({
2372
+ action: "approval",
2373
+ response
2374
+ })[0],
2375
+ id: `ai.approval:${encodeURIComponent(response.requestId)}:${encodeURIComponent(response.messageId)}:${encodeURIComponent(response.generationId)}:${encodeURIComponent(response.approvalId)}:response`
2376
+ }],
2377
+ input: (response) => [{
2378
+ ...commandInput({
2379
+ action: "input",
2380
+ response
2381
+ })[0],
2382
+ id: `ai.input:${encodeURIComponent(response.requestId)}:${encodeURIComponent(response.messageId)}:${encodeURIComponent(response.generationId)}:${encodeURIComponent(response.inputId)}:response`
2383
+ }],
2384
+ requestInput: (request) => [{
2385
+ ...commandInput({
2386
+ action: "request-input",
2387
+ request
2388
+ })[0],
2389
+ id: `ai.input:${encodeURIComponent(request.requestId)}:${encodeURIComponent(request.messageId)}:${encodeURIComponent(request.generationId)}:${encodeURIComponent(request.inputId)}:request`
2390
+ }],
2391
+ retry: (request) => [{
2392
+ ...commandInput({
2393
+ action: "retry",
2394
+ request
2395
+ })[0],
2396
+ id: `ai.retry:${request.retryId}`
2397
+ }],
2398
+ interrupt: (request) => [{
2399
+ ...commandInput({
2400
+ action: "interrupt",
2401
+ request
2402
+ })[0],
2403
+ id: `ai.interrupt:${request.generationId ?? request.requestId}`
2404
+ }]
2405
+ };
2406
+ /**
2407
+ * Read the durable handler context ambient to the current AI tool
2408
+ * execution. A2 runs each local tool's `execute` inside this scope, so
2409
+ * plain AI SDK `tool()` definitions reach their `event`, `attempt`,
2410
+ * `session`, and `signal` without an A2-specific wrapper. The agent (or
2411
+ * contract) argument carries the event types and is verified against
2412
+ * the executing server's contract.
2413
+ */
2414
+ function handlerContext(agentOrContract) {
2415
+ const contract = "contract" in agentOrContract ? agentOrContract.contract : agentOrContract;
2416
+ const scope = ambientToolScopeStorage()?.getStore();
2417
+ if (scope === void 0) throw new TypeError(`handlerContext('${contract.name}') requires an ambient A2 tool execution: call it inside a local tool's execute, or wrap direct tool tests with runWithHandlerContext`);
2418
+ if (scope.contract !== void 0 && scope.contract.name !== contract.name) throw new TypeError(`handlerContext('${contract.name}') was called during a tool execution for agent '${scope.contract.name}'`);
2419
+ return scope.context;
2420
+ }
2421
+ /**
2422
+ * Define an agent: built-in AI protocol + application events and one state
2423
+ * reducer. The result is isomorphic.
2424
+ */
2425
+ function agent(options) {
2426
+ const builtIns = createEvents(options.messageSchema === void 0 ? void 0 : { messageSchema: options.messageSchema });
2427
+ const extensions = options.events ?? {};
2428
+ for (const key of Object.keys(extensions)) if (Object.hasOwn(builtIns, key)) throw new TypeError(`agent event '${key}' conflicts with a built-in event`);
2429
+ const agentContract = contract({
2430
+ name: options.name,
2431
+ events: {
2432
+ ...builtIns,
2433
+ ...extensions
2434
+ }
2435
+ });
2436
+ const reducer = agentContract.reducer({
2437
+ name: options.reducerName ?? "a2.ai.state.v22",
2438
+ initialState: initialState()
2439
+ }).fold((state, event) => reduceAIState(state, event));
2440
+ return {
2441
+ contract: agentContract,
2442
+ reducer: storedAIReducer({
2443
+ ...reducer,
2444
+ [clientStateProjection]: projectAIClientState
2445
+ })
2446
+ };
2447
+ }
2448
+ //#endregion
2449
+ export { interruptUIMessage as C, progressBatches as S, projectedMessage as _, events as a, upsertResponse as b, reduceAIState as c, AI_EVENT_ID_PREFIX as d, aiEventId as f, advanceProjection as g, controlCommit as h, deriveUIMessages as i, aiStateReducer as l, applyControlChanges as m, createEvents as n, handlerContext as o, initialControlState as p, createReducer as r, inputs as s, agent as t, storedAIReducer as u, reduceToolActivity as v, pauseUIMessage as w, flattenProgressBatches as x, upsertMessage as y };
2450
+
2451
+ //# sourceMappingURL=ai-DgOBltJ_.js.map