experimental-a2 0.14.1 → 0.16.0

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