experimental-a2 0.13.0 → 0.14.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.
- package/CHANGELOG.md +34 -0
- package/dist/{actor-DJi3RsNu.d.ts → actor-BfQSE0KC.d.ts} +4 -4
- package/dist/{actor-DJi3RsNu.d.ts.map → actor-BfQSE0KC.d.ts.map} +1 -1
- package/dist/actor-client.d.ts +1 -1
- package/dist/actor-client.js +1 -1
- package/dist/actor-react.d.ts +3 -3
- package/dist/actor-react.js +2 -2
- package/dist/{actor-shared-DI7J5upy.js → actor-shared-B5tJfzt-.js} +2 -2
- package/dist/{actor-shared-DI7J5upy.js.map → actor-shared-B5tJfzt-.js.map} +1 -1
- package/dist/actor.d.ts +1 -1
- package/dist/actor.js +3 -3
- package/dist/ai-Cai-lCbj.d.ts +580 -0
- package/dist/ai-Cai-lCbj.d.ts.map +1 -0
- package/dist/ai-control-CcD4hh3y.js +119 -0
- package/dist/ai-control-CcD4hh3y.js.map +1 -0
- package/dist/ai-server.d.ts +6 -6
- package/dist/ai-server.d.ts.map +1 -1
- package/dist/ai-server.js +1014 -501
- package/dist/ai-server.js.map +1 -1
- package/dist/ai.d.ts +2 -365
- package/dist/ai.js +801 -80
- package/dist/ai.js.map +1 -1
- package/dist/{client-P_NNNRM-.d.ts → client-BAEABRZB.d.ts} +2 -2
- package/dist/{client-P_NNNRM-.d.ts.map → client-BAEABRZB.d.ts.map} +1 -1
- package/dist/{client-Bf6uSEAk.js → client-BYzHjkwU.js} +21 -6
- package/dist/client-BYzHjkwU.js.map +1 -0
- package/dist/client.d.ts +1 -1
- package/dist/client.js +1 -1
- package/dist/{contract-48bUMgcL.js → contract-CKRg_E4q.js} +3 -26
- package/dist/contract-CKRg_E4q.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/react.d.ts +2 -2
- package/dist/react.js +1 -1
- package/dist/{reducer-DJKWm3cp.d.ts → reducer-BcS9VDKC.d.ts} +4 -1
- package/dist/{reducer-DJKWm3cp.d.ts.map → reducer-BcS9VDKC.d.ts.map} +1 -1
- package/dist/reducer-DEMjEY_O.js +29 -0
- package/dist/reducer-DEMjEY_O.js.map +1 -0
- package/dist/scheduler-qstash.d.ts +2 -2
- package/dist/scheduler-qstash.js +1 -1
- package/dist/scheduler-vercel.d.ts +2 -2
- package/dist/scheduler-vercel.js +1 -1
- package/dist/{server-DjZZa1wr.d.ts → server-Bp5Nd1pF.d.ts} +3 -3
- package/dist/{server-DjZZa1wr.d.ts.map → server-Bp5Nd1pF.d.ts.map} +1 -1
- package/dist/{server-BeNADlCI.js → server-CjJSGcF7.js} +4 -3
- package/dist/server-CjJSGcF7.js.map +1 -0
- package/dist/server.d.ts +3 -3
- package/dist/server.js +1 -1
- package/dist/{store-DtDOWLSn.d.ts → store-D_yhNdPz.d.ts} +7 -2
- package/dist/{store-DtDOWLSn.d.ts.map → store-D_yhNdPz.d.ts.map} +1 -1
- package/dist/store-N8PXxDAS.js.map +1 -1
- package/dist/store-memory.d.ts +1 -1
- package/dist/store-postgres.d.ts +1 -1
- package/dist/store-postgres.js +19 -0
- package/dist/store-postgres.js.map +1 -1
- package/dist/store-redis-http.d.ts +1 -1
- package/dist/store-redis-http.js +1 -1
- package/dist/{store-redis-notify-BUCyXOn0.js → store-redis-notify-D2EI6gwX.js} +27 -2
- package/dist/store-redis-notify-D2EI6gwX.js.map +1 -0
- package/dist/store-redis.d.ts +1 -1
- package/dist/store-redis.js +1 -1
- package/dist/store-sqlite.d.ts +1 -1
- package/docs/guides/06-ai-agents.mdx +265 -62
- package/docs/reference/01-api.mdx +122 -27
- package/examples/playground/app/agent/[agentId]/agent-client.tsx +118 -21
- package/examples/playground/app/agent/[agentId]/agent-queue.tsx +289 -0
- package/examples/playground/app/agent/compaction-settings.test.ts +22 -6
- package/examples/playground/app/agent/model.ts +11 -2
- package/examples/playground/app/agent/server.ts +8 -2
- package/examples/playground/app/chat/[chatId]/chat-client.tsx +3 -13
- package/examples/playground/app/chat/model.ts +2 -2
- package/examples/playground/app/chat/server.ts +24 -17
- package/examples/playground/app/globals.css +179 -0
- package/examples/playground/package.json +1 -1
- package/package.json +1 -1
- package/src/ai-client-state.ts +185 -0
- package/src/ai-control-server.ts +829 -0
- package/src/ai-control-state.ts +152 -0
- package/src/ai-control.ts +139 -0
- package/src/ai-coordinator.ts +99 -32
- package/src/ai-progress-batches.ts +68 -0
- package/src/ai-projector.ts +76 -15
- package/src/ai-sdk-step.ts +0 -1
- package/src/ai-server.ts +381 -608
- package/src/ai.ts +553 -108
- package/src/client.ts +31 -9
- package/src/licenses/Apache-2.0.txt +55 -0
- package/src/parse-partial-json.ts +441 -0
- package/src/reducer.ts +6 -0
- package/src/server.ts +8 -4
- package/src/store-postgres.ts +27 -0
- package/src/store-redis-core.ts +53 -1
- package/src/store-redis-notify.ts +1 -0
- package/src/store.ts +6 -0
- package/dist/ai.d.ts.map +0 -1
- package/dist/client-Bf6uSEAk.js.map +0 -1
- package/dist/contract-48bUMgcL.js.map +0 -1
- package/dist/server-BeNADlCI.js.map +0 -1
- package/dist/store-redis-notify-BUCyXOn0.js.map +0 -1
package/dist/ai.js
CHANGED
|
@@ -1,13 +1,407 @@
|
|
|
1
1
|
import { n as validateSync, t as assertSyncSchema } from "./validate-XKT4FSNn.js";
|
|
2
|
-
import { t as
|
|
2
|
+
import { t as clientStateProjection } from "./reducer-DEMjEY_O.js";
|
|
3
|
+
import { t as contract } from "./contract-CKRg_E4q.js";
|
|
3
4
|
import { l as ambientToolScopeStorage } from "./internal-Dq2qYxou.js";
|
|
5
|
+
import { n as applyControlChanges, t as initialControlState } from "./ai-control-CcD4hh3y.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
|
+
revision: 0,
|
|
58
|
+
message: command.message,
|
|
59
|
+
generate: command.message.role === "user" && (command.action === "steer" || command.generate !== false)
|
|
60
|
+
};
|
|
61
|
+
items = command.action === "steer" ? [item, ...items] : [...items, item];
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
case "edit":
|
|
65
|
+
items = items.map((item) => item.id === command.message.id && item.revision === command.expectedRevision && item.message.role === command.message.role ? {
|
|
66
|
+
...item,
|
|
67
|
+
message: command.message
|
|
68
|
+
} : item);
|
|
69
|
+
break;
|
|
70
|
+
case "move":
|
|
71
|
+
items = moveItem({
|
|
72
|
+
items,
|
|
73
|
+
inputId: command.inputId,
|
|
74
|
+
beforeId: command.beforeId
|
|
75
|
+
});
|
|
76
|
+
break;
|
|
77
|
+
case "remove":
|
|
78
|
+
items = items.filter((item) => item.id !== command.inputId);
|
|
79
|
+
break;
|
|
80
|
+
case "send-now": {
|
|
81
|
+
if (turnId !== command.turnId) break;
|
|
82
|
+
const selected = items.find((item) => item.id === command.inputId);
|
|
83
|
+
if (!selected || selected.message.role !== "user") break;
|
|
84
|
+
items = items.map((item) => item.id === selected.id ? {
|
|
85
|
+
...item,
|
|
86
|
+
generate: true,
|
|
87
|
+
...turnId === null ? {} : { afterStepOf: turnId }
|
|
88
|
+
} : item);
|
|
89
|
+
items = moveItem({
|
|
90
|
+
items,
|
|
91
|
+
inputId: command.inputId,
|
|
92
|
+
beforeId: items[0]?.id ?? null
|
|
93
|
+
});
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
case "pause":
|
|
97
|
+
paused = true;
|
|
98
|
+
if (command.when === "now") suspended = true;
|
|
99
|
+
break;
|
|
100
|
+
case "resume":
|
|
101
|
+
paused = false;
|
|
102
|
+
suspended = false;
|
|
103
|
+
}
|
|
104
|
+
admit();
|
|
105
|
+
}
|
|
106
|
+
items = items.filter((item) => {
|
|
107
|
+
if (item.afterStepOf === void 0) return true;
|
|
108
|
+
show(item.message);
|
|
109
|
+
return false;
|
|
110
|
+
});
|
|
111
|
+
return {
|
|
112
|
+
...state,
|
|
113
|
+
messages,
|
|
114
|
+
inbox: {
|
|
115
|
+
items,
|
|
116
|
+
paused
|
|
117
|
+
},
|
|
118
|
+
starting: starting && !suspended
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
//#endregion
|
|
122
|
+
//#region src/parse-partial-json.ts
|
|
123
|
+
/*!
|
|
124
|
+
* Adapted from AI SDK 7.0.58, packages/ai/src/util/fix-json.ts.
|
|
125
|
+
* Source: https://github.com/vercel/ai, packages/ai/src/util/fix-json.ts.
|
|
126
|
+
* Modified to expose a synchronous partial parser with strict TypeScript types.
|
|
127
|
+
* Also preserves positive exponents in unfinished containers.
|
|
128
|
+
* Copyright 2023 Vercel, Inc.
|
|
129
|
+
* @license Apache-2.0. See src/licenses/Apache-2.0.txt.
|
|
130
|
+
*/
|
|
131
|
+
function parsePartialJSON(text) {
|
|
132
|
+
if (text === void 0 || text.trim() === "") return void 0;
|
|
133
|
+
try {
|
|
134
|
+
return JSON.parse(text);
|
|
135
|
+
} catch {
|
|
136
|
+
try {
|
|
137
|
+
return JSON.parse(repairPartialJSON(text));
|
|
138
|
+
} catch {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
const isHexDigit = (char) => char >= "0" && char <= "9" || char >= "A" && char <= "F" || char >= "a" && char <= "f";
|
|
144
|
+
function repairPartialJSON(input) {
|
|
145
|
+
const stack = ["ROOT"];
|
|
146
|
+
let lastValidIndex = -1;
|
|
147
|
+
let literalStart = null;
|
|
148
|
+
let unicodeEscapeDigits = 0;
|
|
149
|
+
function processValueStart(char, i, swapState) {
|
|
150
|
+
switch (char) {
|
|
151
|
+
case "\"":
|
|
152
|
+
lastValidIndex = i;
|
|
153
|
+
stack.pop();
|
|
154
|
+
stack.push(swapState);
|
|
155
|
+
stack.push("INSIDE_STRING");
|
|
156
|
+
break;
|
|
157
|
+
case "f":
|
|
158
|
+
case "t":
|
|
159
|
+
case "n":
|
|
160
|
+
lastValidIndex = i;
|
|
161
|
+
literalStart = i;
|
|
162
|
+
stack.pop();
|
|
163
|
+
stack.push(swapState);
|
|
164
|
+
stack.push("INSIDE_LITERAL");
|
|
165
|
+
break;
|
|
166
|
+
case "-":
|
|
167
|
+
stack.pop();
|
|
168
|
+
stack.push(swapState);
|
|
169
|
+
stack.push("INSIDE_NUMBER");
|
|
170
|
+
break;
|
|
171
|
+
case "0":
|
|
172
|
+
case "1":
|
|
173
|
+
case "2":
|
|
174
|
+
case "3":
|
|
175
|
+
case "4":
|
|
176
|
+
case "5":
|
|
177
|
+
case "6":
|
|
178
|
+
case "7":
|
|
179
|
+
case "8":
|
|
180
|
+
case "9":
|
|
181
|
+
lastValidIndex = i;
|
|
182
|
+
stack.pop();
|
|
183
|
+
stack.push(swapState);
|
|
184
|
+
stack.push("INSIDE_NUMBER");
|
|
185
|
+
break;
|
|
186
|
+
case "{":
|
|
187
|
+
lastValidIndex = i;
|
|
188
|
+
stack.pop();
|
|
189
|
+
stack.push(swapState);
|
|
190
|
+
stack.push("INSIDE_OBJECT_START");
|
|
191
|
+
break;
|
|
192
|
+
case "[":
|
|
193
|
+
lastValidIndex = i;
|
|
194
|
+
stack.pop();
|
|
195
|
+
stack.push(swapState);
|
|
196
|
+
stack.push("INSIDE_ARRAY_START");
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
function processAfterObjectValue(char, i) {
|
|
200
|
+
switch (char) {
|
|
201
|
+
case ",":
|
|
202
|
+
stack.pop();
|
|
203
|
+
stack.push("INSIDE_OBJECT_AFTER_COMMA");
|
|
204
|
+
break;
|
|
205
|
+
case "}":
|
|
206
|
+
lastValidIndex = i;
|
|
207
|
+
stack.pop();
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
function processAfterArrayValue(char, i) {
|
|
211
|
+
switch (char) {
|
|
212
|
+
case ",":
|
|
213
|
+
stack.pop();
|
|
214
|
+
stack.push("INSIDE_ARRAY_AFTER_COMMA");
|
|
215
|
+
break;
|
|
216
|
+
case "]":
|
|
217
|
+
lastValidIndex = i;
|
|
218
|
+
stack.pop();
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
for (let i = 0; i < input.length; i++) {
|
|
222
|
+
const char = input[i];
|
|
223
|
+
switch (stack[stack.length - 1]) {
|
|
224
|
+
case "ROOT":
|
|
225
|
+
processValueStart(char, i, "FINISH");
|
|
226
|
+
break;
|
|
227
|
+
case "INSIDE_OBJECT_START":
|
|
228
|
+
switch (char) {
|
|
229
|
+
case "\"":
|
|
230
|
+
stack.pop();
|
|
231
|
+
stack.push("INSIDE_OBJECT_KEY");
|
|
232
|
+
break;
|
|
233
|
+
case "}":
|
|
234
|
+
lastValidIndex = i;
|
|
235
|
+
stack.pop();
|
|
236
|
+
}
|
|
237
|
+
break;
|
|
238
|
+
case "INSIDE_OBJECT_AFTER_COMMA":
|
|
239
|
+
switch (char) {
|
|
240
|
+
case "\"":
|
|
241
|
+
stack.pop();
|
|
242
|
+
stack.push("INSIDE_OBJECT_KEY");
|
|
243
|
+
}
|
|
244
|
+
break;
|
|
245
|
+
case "INSIDE_OBJECT_KEY":
|
|
246
|
+
switch (char) {
|
|
247
|
+
case "\"":
|
|
248
|
+
stack.pop();
|
|
249
|
+
stack.push("INSIDE_OBJECT_AFTER_KEY");
|
|
250
|
+
}
|
|
251
|
+
break;
|
|
252
|
+
case "INSIDE_OBJECT_AFTER_KEY":
|
|
253
|
+
switch (char) {
|
|
254
|
+
case ":":
|
|
255
|
+
stack.pop();
|
|
256
|
+
stack.push("INSIDE_OBJECT_BEFORE_VALUE");
|
|
257
|
+
}
|
|
258
|
+
break;
|
|
259
|
+
case "INSIDE_OBJECT_BEFORE_VALUE":
|
|
260
|
+
processValueStart(char, i, "INSIDE_OBJECT_AFTER_VALUE");
|
|
261
|
+
break;
|
|
262
|
+
case "INSIDE_OBJECT_AFTER_VALUE":
|
|
263
|
+
processAfterObjectValue(char, i);
|
|
264
|
+
break;
|
|
265
|
+
case "INSIDE_STRING":
|
|
266
|
+
switch (char) {
|
|
267
|
+
case "\"":
|
|
268
|
+
stack.pop();
|
|
269
|
+
lastValidIndex = i;
|
|
270
|
+
break;
|
|
271
|
+
case "\\":
|
|
272
|
+
stack.push("INSIDE_STRING_ESCAPE");
|
|
273
|
+
break;
|
|
274
|
+
default: lastValidIndex = i;
|
|
275
|
+
}
|
|
276
|
+
break;
|
|
277
|
+
case "INSIDE_ARRAY_START":
|
|
278
|
+
switch (char) {
|
|
279
|
+
case "]":
|
|
280
|
+
lastValidIndex = i;
|
|
281
|
+
stack.pop();
|
|
282
|
+
break;
|
|
283
|
+
default:
|
|
284
|
+
lastValidIndex = i;
|
|
285
|
+
processValueStart(char, i, "INSIDE_ARRAY_AFTER_VALUE");
|
|
286
|
+
}
|
|
287
|
+
break;
|
|
288
|
+
case "INSIDE_ARRAY_AFTER_VALUE":
|
|
289
|
+
switch (char) {
|
|
290
|
+
case ",":
|
|
291
|
+
stack.pop();
|
|
292
|
+
stack.push("INSIDE_ARRAY_AFTER_COMMA");
|
|
293
|
+
break;
|
|
294
|
+
case "]":
|
|
295
|
+
lastValidIndex = i;
|
|
296
|
+
stack.pop();
|
|
297
|
+
break;
|
|
298
|
+
default: lastValidIndex = i;
|
|
299
|
+
}
|
|
300
|
+
break;
|
|
301
|
+
case "INSIDE_ARRAY_AFTER_COMMA":
|
|
302
|
+
processValueStart(char, i, "INSIDE_ARRAY_AFTER_VALUE");
|
|
303
|
+
break;
|
|
304
|
+
case "INSIDE_STRING_ESCAPE":
|
|
305
|
+
stack.pop();
|
|
306
|
+
if (char === "u") {
|
|
307
|
+
unicodeEscapeDigits = 0;
|
|
308
|
+
stack.push("INSIDE_STRING_UNICODE_ESCAPE");
|
|
309
|
+
} else lastValidIndex = i;
|
|
310
|
+
break;
|
|
311
|
+
case "INSIDE_STRING_UNICODE_ESCAPE":
|
|
312
|
+
if (isHexDigit(char)) {
|
|
313
|
+
unicodeEscapeDigits++;
|
|
314
|
+
if (unicodeEscapeDigits === 4) {
|
|
315
|
+
stack.pop();
|
|
316
|
+
lastValidIndex = i;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
break;
|
|
320
|
+
case "INSIDE_NUMBER":
|
|
321
|
+
switch (char) {
|
|
322
|
+
case "0":
|
|
323
|
+
case "1":
|
|
324
|
+
case "2":
|
|
325
|
+
case "3":
|
|
326
|
+
case "4":
|
|
327
|
+
case "5":
|
|
328
|
+
case "6":
|
|
329
|
+
case "7":
|
|
330
|
+
case "8":
|
|
331
|
+
case "9":
|
|
332
|
+
lastValidIndex = i;
|
|
333
|
+
break;
|
|
334
|
+
case "e":
|
|
335
|
+
case "E":
|
|
336
|
+
case "-":
|
|
337
|
+
case "+":
|
|
338
|
+
case ".": break;
|
|
339
|
+
case ",":
|
|
340
|
+
stack.pop();
|
|
341
|
+
if (stack[stack.length - 1] === "INSIDE_ARRAY_AFTER_VALUE") processAfterArrayValue(char, i);
|
|
342
|
+
if (stack[stack.length - 1] === "INSIDE_OBJECT_AFTER_VALUE") processAfterObjectValue(char, i);
|
|
343
|
+
break;
|
|
344
|
+
case "}":
|
|
345
|
+
stack.pop();
|
|
346
|
+
if (stack[stack.length - 1] === "INSIDE_OBJECT_AFTER_VALUE") processAfterObjectValue(char, i);
|
|
347
|
+
break;
|
|
348
|
+
case "]":
|
|
349
|
+
stack.pop();
|
|
350
|
+
if (stack[stack.length - 1] === "INSIDE_ARRAY_AFTER_VALUE") processAfterArrayValue(char, i);
|
|
351
|
+
break;
|
|
352
|
+
default: stack.pop();
|
|
353
|
+
}
|
|
354
|
+
break;
|
|
355
|
+
case "INSIDE_LITERAL": {
|
|
356
|
+
const partialLiteral = input.substring(literalStart, i + 1);
|
|
357
|
+
if (!"false".startsWith(partialLiteral) && !"true".startsWith(partialLiteral) && !"null".startsWith(partialLiteral)) {
|
|
358
|
+
stack.pop();
|
|
359
|
+
if (stack[stack.length - 1] === "INSIDE_OBJECT_AFTER_VALUE") processAfterObjectValue(char, i);
|
|
360
|
+
else if (stack[stack.length - 1] === "INSIDE_ARRAY_AFTER_VALUE") processAfterArrayValue(char, i);
|
|
361
|
+
} else lastValidIndex = i;
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
let result = input.slice(0, lastValidIndex + 1);
|
|
367
|
+
for (let i = stack.length - 1; i >= 0; i--) switch (stack[i]) {
|
|
368
|
+
case "INSIDE_STRING":
|
|
369
|
+
result += "\"";
|
|
370
|
+
break;
|
|
371
|
+
case "INSIDE_OBJECT_KEY":
|
|
372
|
+
case "INSIDE_OBJECT_AFTER_KEY":
|
|
373
|
+
case "INSIDE_OBJECT_AFTER_COMMA":
|
|
374
|
+
case "INSIDE_OBJECT_START":
|
|
375
|
+
case "INSIDE_OBJECT_BEFORE_VALUE":
|
|
376
|
+
case "INSIDE_OBJECT_AFTER_VALUE":
|
|
377
|
+
result += "}";
|
|
378
|
+
break;
|
|
379
|
+
case "INSIDE_ARRAY_START":
|
|
380
|
+
case "INSIDE_ARRAY_AFTER_COMMA":
|
|
381
|
+
case "INSIDE_ARRAY_AFTER_VALUE":
|
|
382
|
+
result += "]";
|
|
383
|
+
break;
|
|
384
|
+
case "INSIDE_LITERAL": {
|
|
385
|
+
const partialLiteral = input.substring(literalStart, input.length);
|
|
386
|
+
if ("true".startsWith(partialLiteral)) result += "true".slice(partialLiteral.length);
|
|
387
|
+
else if ("false".startsWith(partialLiteral)) result += "false".slice(partialLiteral.length);
|
|
388
|
+
else if ("null".startsWith(partialLiteral)) result += "null".slice(partialLiteral.length);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return result;
|
|
392
|
+
}
|
|
393
|
+
//#endregion
|
|
4
394
|
//#region src/ai-projector.ts
|
|
5
395
|
const isRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6
|
-
const cloneMessage = (messageId, base) =>
|
|
396
|
+
const cloneMessage = (messageId, base) => base === void 0 ? {
|
|
7
397
|
id: messageId,
|
|
8
398
|
role: "assistant",
|
|
9
399
|
parts: []
|
|
10
|
-
}
|
|
400
|
+
} : {
|
|
401
|
+
...base,
|
|
402
|
+
id: messageId,
|
|
403
|
+
parts: base.parts.map((part) => ({ ...part }))
|
|
404
|
+
};
|
|
11
405
|
const currentStepParts = (parts) => {
|
|
12
406
|
let start = parts.length - 1;
|
|
13
407
|
while (start >= 0 && parts[start]?.["type"] !== "step-start") start -= 1;
|
|
@@ -60,21 +454,16 @@ const mergeMetadata = (current, update) => {
|
|
|
60
454
|
...update
|
|
61
455
|
} : update;
|
|
62
456
|
};
|
|
63
|
-
const parseCompleteJSON = (value) => {
|
|
64
|
-
try {
|
|
65
|
-
return JSON.parse(value);
|
|
66
|
-
} catch {
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
/** Internal synchronous counterpart of the AI SDK UI message stream fold. */
|
|
71
457
|
function projectUIMessage(options) {
|
|
458
|
+
return projectUIMessageStream(options).message;
|
|
459
|
+
}
|
|
460
|
+
function projectUIMessageStream(options) {
|
|
72
461
|
const message = cloneMessage(options.messageId, options.base);
|
|
73
462
|
const record = message;
|
|
74
463
|
const parts = message.parts;
|
|
75
|
-
const activeText =
|
|
76
|
-
const activeReasoning =
|
|
77
|
-
const partialTools =
|
|
464
|
+
const activeText = new Map(Object.entries(options.cursor?.text ?? {}).map(([id, index]) => [id, parts[index]]));
|
|
465
|
+
const activeReasoning = new Map(Object.entries(options.cursor?.reasoning ?? {}).map(([id, index]) => [id, parts[index]]));
|
|
466
|
+
const partialTools = new Map(Object.entries(options.cursor?.tools ?? {}).map(([id, partial]) => [id, { ...partial }]));
|
|
78
467
|
for (const chunk of options.chunks) switch (chunk.type) {
|
|
79
468
|
case "text-start": {
|
|
80
469
|
const part = {
|
|
@@ -213,7 +602,7 @@ function projectUIMessage(options) {
|
|
|
213
602
|
toolName: partial.toolName,
|
|
214
603
|
dynamic: partial.dynamic,
|
|
215
604
|
state: "input-streaming",
|
|
216
|
-
input:
|
|
605
|
+
input: parsePartialJSON(partial.text),
|
|
217
606
|
providerExecuted: partial.providerExecuted,
|
|
218
607
|
providerMetadata: partial.providerMetadata,
|
|
219
608
|
toolMetadata: partial.toolMetadata,
|
|
@@ -333,7 +722,6 @@ function projectUIMessage(options) {
|
|
|
333
722
|
activeReasoning.clear();
|
|
334
723
|
break;
|
|
335
724
|
case "start":
|
|
336
|
-
if (chunk.messageId !== void 0) record["id"] = chunk.messageId;
|
|
337
725
|
if (chunk.messageMetadata !== void 0 && chunk.messageMetadata !== null) record["metadata"] = mergeMetadata(record["metadata"], chunk.messageMetadata);
|
|
338
726
|
break;
|
|
339
727
|
case "finish":
|
|
@@ -355,10 +743,28 @@ function projectUIMessage(options) {
|
|
|
355
743
|
});
|
|
356
744
|
}
|
|
357
745
|
}
|
|
358
|
-
|
|
746
|
+
const indexes = new Map(parts.map((part, index) => [part, index]));
|
|
747
|
+
return {
|
|
748
|
+
message,
|
|
749
|
+
cursor: {
|
|
750
|
+
text: Object.fromEntries([...activeText].map(([id, part]) => [id, indexes.get(part)])),
|
|
751
|
+
reasoning: Object.fromEntries([...activeReasoning].map(([id, part]) => [id, indexes.get(part)])),
|
|
752
|
+
tools: Object.fromEntries(partialTools)
|
|
753
|
+
}
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
function pauseUIMessage(options) {
|
|
757
|
+
const next = structuredClone(options.message);
|
|
758
|
+
next.parts = next.parts.filter((part) => !("toolCallId" in part && !options.toolCallIds.has(part.toolCallId)));
|
|
759
|
+
for (const part of next.parts) if ((part.type === "text" || part.type === "reasoning") && part.state === "streaming") part.state = "done";
|
|
760
|
+
return next;
|
|
359
761
|
}
|
|
360
762
|
function interruptUIMessage(message, toolError = "Tool execution was interrupted.") {
|
|
361
763
|
const next = structuredClone(message);
|
|
764
|
+
next.parts = next.parts.filter((value) => {
|
|
765
|
+
const part = value;
|
|
766
|
+
return !isToolPart(part) || part["state"] !== "input-streaming";
|
|
767
|
+
});
|
|
362
768
|
for (const value of next.parts) {
|
|
363
769
|
if (value["type"] === "text" || value["type"] === "reasoning") {
|
|
364
770
|
if (value["state"] === "streaming") value["state"] = "done";
|
|
@@ -380,7 +786,7 @@ function interruptUIMessage(message, toolError = "Tool execution was interrupted
|
|
|
380
786
|
value["errorText"] = toolError;
|
|
381
787
|
continue;
|
|
382
788
|
}
|
|
383
|
-
if (state === "input-
|
|
789
|
+
if (state === "input-available" || state === "approval-requested" || state === "approval-responded") {
|
|
384
790
|
if (state === "approval-requested") delete value["approval"];
|
|
385
791
|
value["state"] = "output-error";
|
|
386
792
|
value["errorText"] = toolError;
|
|
@@ -389,6 +795,63 @@ function interruptUIMessage(message, toolError = "Tool execution was interrupted
|
|
|
389
795
|
return next;
|
|
390
796
|
}
|
|
391
797
|
//#endregion
|
|
798
|
+
//#region src/ai-progress-batches.ts
|
|
799
|
+
const PAGE_SIZE = 64;
|
|
800
|
+
const appendProgressBatch = ({ batches, batch }) => {
|
|
801
|
+
if (batches.tail.length < PAGE_SIZE) return {
|
|
802
|
+
...batches,
|
|
803
|
+
length: batches.length + 1,
|
|
804
|
+
tail: [...batches.tail, batch]
|
|
805
|
+
};
|
|
806
|
+
const blocks = [...batches.blocks];
|
|
807
|
+
let block = batches.tail;
|
|
808
|
+
let level = 0;
|
|
809
|
+
for (;;) {
|
|
810
|
+
const left = blocks[level];
|
|
811
|
+
if (left == null) break;
|
|
812
|
+
block = {
|
|
813
|
+
left,
|
|
814
|
+
right: block
|
|
815
|
+
};
|
|
816
|
+
blocks[level] = null;
|
|
817
|
+
level += 1;
|
|
818
|
+
}
|
|
819
|
+
blocks[level] = block;
|
|
820
|
+
return {
|
|
821
|
+
length: batches.length + 1,
|
|
822
|
+
tail: [batch],
|
|
823
|
+
blocks
|
|
824
|
+
};
|
|
825
|
+
};
|
|
826
|
+
const progressBatches = (items = []) => {
|
|
827
|
+
let batches = {
|
|
828
|
+
length: 0,
|
|
829
|
+
tail: [],
|
|
830
|
+
blocks: []
|
|
831
|
+
};
|
|
832
|
+
for (const batch of items) batches = appendProgressBatch({
|
|
833
|
+
batches,
|
|
834
|
+
batch
|
|
835
|
+
});
|
|
836
|
+
return batches;
|
|
837
|
+
};
|
|
838
|
+
const flattenProgressBatches = (batches) => {
|
|
839
|
+
const items = [];
|
|
840
|
+
const visit = (block) => {
|
|
841
|
+
if (Array.isArray(block)) items.push(...block);
|
|
842
|
+
else {
|
|
843
|
+
visit(block.left);
|
|
844
|
+
visit(block.right);
|
|
845
|
+
}
|
|
846
|
+
};
|
|
847
|
+
for (let level = batches.blocks.length - 1; level >= 0; level -= 1) {
|
|
848
|
+
const block = batches.blocks[level];
|
|
849
|
+
if (block) visit(block);
|
|
850
|
+
}
|
|
851
|
+
items.push(...batches.tail);
|
|
852
|
+
return items;
|
|
853
|
+
};
|
|
854
|
+
//#endregion
|
|
392
855
|
//#region src/ai.ts
|
|
393
856
|
const issue = (message) => ({ issues: [{ message }] });
|
|
394
857
|
const schema = (label, parse) => ({ "~standard": {
|
|
@@ -467,13 +930,79 @@ const compactionPayloadSchema = (messageSchema) => schema("ai.compaction.complet
|
|
|
467
930
|
function createEvents(options) {
|
|
468
931
|
const messageSchema = options?.messageSchema;
|
|
469
932
|
if (messageSchema) assertSyncSchema(messageSchema, "the AI message schema");
|
|
470
|
-
|
|
933
|
+
const definition = {
|
|
934
|
+
"ai.control.requested": schema("ai.control.requested", (value) => {
|
|
935
|
+
if (!isRecord(value) || !isJSONCompatible(value)) return issue("invalid AI command");
|
|
936
|
+
const action = value["action"];
|
|
937
|
+
const forwarded = {
|
|
938
|
+
approval: ["ai.approval.responded", "response"],
|
|
939
|
+
"tool-result": ["ai.tool.result", "result"],
|
|
940
|
+
input: ["ai.input.responded", "response"],
|
|
941
|
+
"request-input": ["ai.input.requested", "request"],
|
|
942
|
+
retry: ["ai.retry.requested", "request"],
|
|
943
|
+
interrupt: ["ai.message.interrupted", "request"]
|
|
944
|
+
};
|
|
945
|
+
if (typeof action === "string" && Object.hasOwn(forwarded, action)) {
|
|
946
|
+
const [type, key] = forwarded[action];
|
|
947
|
+
const payload = validateSync(definition[type], value[key], "AI command");
|
|
948
|
+
if (payload.issues) return { issues: payload.issues };
|
|
949
|
+
return { value: {
|
|
950
|
+
action,
|
|
951
|
+
[key]: payload.value
|
|
952
|
+
} };
|
|
953
|
+
}
|
|
954
|
+
if (action === "send" || action === "edit" || action === "steer") {
|
|
955
|
+
const parsed = validateMessage(value["message"], messageSchema);
|
|
956
|
+
if (parsed.issues) return parsed;
|
|
957
|
+
if (action === "edit" && (!Number.isSafeInteger(value["expectedRevision"]) || Number(value["expectedRevision"]) < 0) || action === "steer" && (!stringField(value, "turnId") || !optionalIndexField(value, "lastSeenIndex")) || action === "send" && value["generate"] !== void 0 && typeof value["generate"] !== "boolean") return issue("invalid AI command");
|
|
958
|
+
return { value: {
|
|
959
|
+
...value,
|
|
960
|
+
message: parsed.value
|
|
961
|
+
} };
|
|
962
|
+
}
|
|
963
|
+
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 };
|
|
964
|
+
return issue("invalid AI command");
|
|
965
|
+
}),
|
|
966
|
+
"ai.control.committed": jsonObject("ai.control.committed", (value) => Array.isArray(value["changes"]) && isRecord(value["view"])),
|
|
967
|
+
"ai.control.decided": jsonObject("ai.control.decided", (value) => stringField(value, "commandId") && (value["outcome"] === "applied" || value["outcome"] === "rejected")),
|
|
968
|
+
"ai.work.reported": schema("ai.work.reported", (value) => {
|
|
969
|
+
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");
|
|
970
|
+
const allowed = /* @__PURE__ */ new Set([
|
|
971
|
+
"ai.generation.started",
|
|
972
|
+
"ai.generation.completed",
|
|
973
|
+
"ai.generation.failed",
|
|
974
|
+
"ai.tool.called",
|
|
975
|
+
"ai.tool.result",
|
|
976
|
+
"ai.approval.requested",
|
|
977
|
+
"ai.approval.responded",
|
|
978
|
+
"ai.compaction.requested",
|
|
979
|
+
"ai.compaction.completed",
|
|
980
|
+
"ai.model.metadata.requested"
|
|
981
|
+
]);
|
|
982
|
+
const events = [];
|
|
983
|
+
for (const event of value["events"]) {
|
|
984
|
+
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");
|
|
985
|
+
const parsed = validateSync(definition[event["type"]], event["payload"], "AI work fact");
|
|
986
|
+
if (parsed.issues) return { issues: parsed.issues };
|
|
987
|
+
events.push({
|
|
988
|
+
id: event["id"],
|
|
989
|
+
type: event["type"],
|
|
990
|
+
payload: parsed.value
|
|
991
|
+
});
|
|
992
|
+
}
|
|
993
|
+
return { value: {
|
|
994
|
+
...value,
|
|
995
|
+
events
|
|
996
|
+
} };
|
|
997
|
+
}),
|
|
998
|
+
"ai.tool.execution.requested": jsonObject("ai.tool.execution.requested", (value) => stringField(value, "turnId") && optionalIndexField(value, "version") && isRecord(value["call"]) && isRecord(value["generation"])),
|
|
999
|
+
"ai.generation.paused": jsonObject("ai.generation.paused", (value) => stringField(value, "generationId") && stringField(value, "messageId")),
|
|
471
1000
|
"ai.session.created": jsonObject("ai.session.created", () => true),
|
|
472
1001
|
"ai.session.closed": jsonObject("ai.session.closed", (value) => optionalStringField(value, "reason")),
|
|
473
1002
|
"ai.message.created": messagePayloadSchema(messageSchema),
|
|
474
1003
|
"ai.message.completed": jsonObject("ai.message.completed", (value) => stringField(value, "messageId")),
|
|
475
1004
|
"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)),
|
|
476
|
-
"ai.generation.requested": jsonObject("ai.generation.requested", (value) => stringField(value, "messageId") && optionalStringField(value, "responseMessageId") && (value["reason"] === "message" || value["reason"] === "tool" || value["reason"] === "input" || value["reason"] === "retry")),
|
|
1005
|
+
"ai.generation.requested": jsonObject("ai.generation.requested", (value) => stringField(value, "messageId") && optionalStringField(value, "responseMessageId") && (value["reason"] === "message" || value["reason"] === "tool" || value["reason"] === "input" || value["reason"] === "retry" || value["reason"] === "resume")),
|
|
477
1006
|
"ai.retry.requested": jsonObject("ai.retry.requested", (value) => stringField(value, "messageId") && stringField(value, "responseMessageId") && stringField(value, "retryId")),
|
|
478
1007
|
"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")),
|
|
479
1008
|
"ai.generation.progress": progressPayloadSchema,
|
|
@@ -490,9 +1019,15 @@ function createEvents(options) {
|
|
|
490
1019
|
"ai.model.metadata.requested": jsonObject("ai.model.metadata.requested", (value) => stringField(value, "modelId")),
|
|
491
1020
|
"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))
|
|
492
1021
|
};
|
|
1022
|
+
return definition;
|
|
493
1023
|
}
|
|
494
1024
|
const events = createEvents();
|
|
495
1025
|
const initialState = () => ({
|
|
1026
|
+
inbox: initialControlState().inbox,
|
|
1027
|
+
pendingQueueCommands: [],
|
|
1028
|
+
active: null,
|
|
1029
|
+
starting: false,
|
|
1030
|
+
receipt: null,
|
|
496
1031
|
session: { created: false },
|
|
497
1032
|
messages: [],
|
|
498
1033
|
status: "idle",
|
|
@@ -516,6 +1051,17 @@ const upsertMessage = (messages, message) => {
|
|
|
516
1051
|
next[index] = message;
|
|
517
1052
|
return next;
|
|
518
1053
|
};
|
|
1054
|
+
const upsertResponse = (options) => {
|
|
1055
|
+
const { messages, message, inputId } = options;
|
|
1056
|
+
if (messages.some((item) => item.id === message.id)) return upsertMessage(messages, message);
|
|
1057
|
+
const inputIndex = messages.findIndex((item) => item.id === inputId);
|
|
1058
|
+
if (inputIndex === -1) return [...messages, message];
|
|
1059
|
+
return [
|
|
1060
|
+
...messages.slice(0, inputIndex + 1),
|
|
1061
|
+
message,
|
|
1062
|
+
...messages.slice(inputIndex + 1)
|
|
1063
|
+
];
|
|
1064
|
+
};
|
|
519
1065
|
const answerApproval = (messages, response) => messages.map((message) => {
|
|
520
1066
|
if (message.id !== response.messageId) return message;
|
|
521
1067
|
let changed = false;
|
|
@@ -601,8 +1147,8 @@ const isStaleGeneration = (state, messageId, generationId) => {
|
|
|
601
1147
|
const current = state.responseGenerationIds[messageId];
|
|
602
1148
|
return current !== void 0 && current !== generationId;
|
|
603
1149
|
};
|
|
604
|
-
const
|
|
605
|
-
const projectionEvents =
|
|
1150
|
+
const projectedMessageStream = (projection) => {
|
|
1151
|
+
const projectionEvents = flattenProgressBatches(projection.batches);
|
|
606
1152
|
for (const event of projection.toolEvents) if (event.type === "ai.tool.result") projectionEvents.push({
|
|
607
1153
|
index: event.index,
|
|
608
1154
|
chunks: [toolResultChunk(event.payload)]
|
|
@@ -611,12 +1157,20 @@ const projectedMessage = (projection) => {
|
|
|
611
1157
|
index: event.index,
|
|
612
1158
|
chunks: [event.type === "ai.approval.requested" ? approvalRequestedChunk(event.payload) : approvalRespondedChunk(event.payload)]
|
|
613
1159
|
});
|
|
614
|
-
return
|
|
1160
|
+
return projectUIMessageStream({
|
|
615
1161
|
messageId: projection.responseMessageId,
|
|
616
1162
|
...projection.baseMessage === void 0 ? {} : { base: projection.baseMessage },
|
|
617
1163
|
chunks: projectionEvents.toSorted((left, right) => left.index - right.index).flatMap((event) => event.chunks)
|
|
618
1164
|
});
|
|
619
1165
|
};
|
|
1166
|
+
const projectedMessage = (projection) => projectedMessageStream(projection).message;
|
|
1167
|
+
const projectedResponse = (options) => {
|
|
1168
|
+
const message = projectedMessage(options.projection);
|
|
1169
|
+
return options.state.pausedGenerationIds?.[options.projection.generationId] ? pauseUIMessage({
|
|
1170
|
+
message,
|
|
1171
|
+
toolCallIds: new Set(options.state.tools.map((tool) => tool.toolCallId))
|
|
1172
|
+
}) : message;
|
|
1173
|
+
};
|
|
620
1174
|
const reduceToolActivity = (tools, event) => {
|
|
621
1175
|
if (event.type === "ai.tool.called") {
|
|
622
1176
|
const payload = event.payload;
|
|
@@ -687,7 +1241,7 @@ const interruptGenerationProjection = (state, generationId, responseMessageId, c
|
|
|
687
1241
|
if (projection === null) return null;
|
|
688
1242
|
const retained = {
|
|
689
1243
|
...projection,
|
|
690
|
-
batches: projection.batches.filter((batch) => batch.index <= cutoff),
|
|
1244
|
+
batches: progressBatches(flattenProgressBatches(projection.batches).filter((batch) => batch.index <= cutoff)),
|
|
691
1245
|
toolEvents: projection.toolEvents.filter((toolEvent) => toolEvent.index <= cutoff),
|
|
692
1246
|
approvalEvents: projection.approvalEvents.filter((approvalEvent) => approvalEvent.index <= cutoff)
|
|
693
1247
|
};
|
|
@@ -695,7 +1249,7 @@ const interruptGenerationProjection = (state, generationId, responseMessageId, c
|
|
|
695
1249
|
const restored = restoreGenerationBase(state, generationId, responseMessageId, "interrupted");
|
|
696
1250
|
return {
|
|
697
1251
|
...restored,
|
|
698
|
-
messages: shouldProject ? upsertMessage(
|
|
1252
|
+
messages: shouldProject ? upsertMessage(state.messages, interruptUIMessage(projectedMessage(retained))) : restored.messages,
|
|
699
1253
|
tools: stopGenerationTools(projectedTools(retained), generationId, responseMessageId, "Tool execution was interrupted."),
|
|
700
1254
|
usage: projection.completionIndex === void 0 || projection.completionIndex <= cutoff ? restored.usage : restored.usage.filter((entry) => entry.generationId !== generationId)
|
|
701
1255
|
};
|
|
@@ -714,6 +1268,76 @@ const terminalGenerationReason = (state, generationId) => {
|
|
|
714
1268
|
function reduceAIState(state, event) {
|
|
715
1269
|
if (state.status === "closed") return state;
|
|
716
1270
|
switch (event.type) {
|
|
1271
|
+
case "ai.control.requested": {
|
|
1272
|
+
const command = event.payload;
|
|
1273
|
+
if (!event.id || !isQueueCommand(command)) return state;
|
|
1274
|
+
const pending = state.pendingQueueCommands ?? [];
|
|
1275
|
+
if (pending.some((item) => item.commandId === event.id)) return state;
|
|
1276
|
+
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;
|
|
1277
|
+
return {
|
|
1278
|
+
...state,
|
|
1279
|
+
pendingQueueCommands: [...pending, {
|
|
1280
|
+
commandId: event.id,
|
|
1281
|
+
command
|
|
1282
|
+
}]
|
|
1283
|
+
};
|
|
1284
|
+
}
|
|
1285
|
+
case "ai.control.committed": {
|
|
1286
|
+
const commit = event.payload;
|
|
1287
|
+
const visible = applyControlChanges({
|
|
1288
|
+
state: {
|
|
1289
|
+
inbox: state.inbox,
|
|
1290
|
+
active: state.active
|
|
1291
|
+
},
|
|
1292
|
+
changes: commit.changes.filter((change) => change.path[0] === "inbox" || change.path[0] === "active")
|
|
1293
|
+
});
|
|
1294
|
+
const { phase } = commit.view;
|
|
1295
|
+
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";
|
|
1296
|
+
return {
|
|
1297
|
+
...state,
|
|
1298
|
+
...visible,
|
|
1299
|
+
status,
|
|
1300
|
+
activeRequestId: commit.view.requestId,
|
|
1301
|
+
activeResponseMessageId: commit.view.responseMessageId
|
|
1302
|
+
};
|
|
1303
|
+
}
|
|
1304
|
+
case "ai.control.decided": {
|
|
1305
|
+
const receipt = event.payload;
|
|
1306
|
+
if (receipt.work) return state;
|
|
1307
|
+
const pending = state.pendingQueueCommands ?? [];
|
|
1308
|
+
const settled = pending.find((item) => item.commandId === receipt.commandId);
|
|
1309
|
+
return {
|
|
1310
|
+
...state,
|
|
1311
|
+
receipt,
|
|
1312
|
+
...settled === void 0 ? {} : { pendingQueueCommands: pending.filter((item) => item.commandId !== receipt.commandId) }
|
|
1313
|
+
};
|
|
1314
|
+
}
|
|
1315
|
+
case "ai.generation.paused": {
|
|
1316
|
+
const payload = event.payload;
|
|
1317
|
+
const toolCallIds = new Set(state.tools.filter((tool) => tool.messageId === payload.messageId).map((tool) => tool.toolCallId));
|
|
1318
|
+
const projection = state.activeProjection;
|
|
1319
|
+
return {
|
|
1320
|
+
...state,
|
|
1321
|
+
activeGeneration: null,
|
|
1322
|
+
activeProjection: projection?.generationId === payload.generationId ? {
|
|
1323
|
+
...projection,
|
|
1324
|
+
cursor: null,
|
|
1325
|
+
batches: progressBatches(flattenProgressBatches(projection.batches).map((batch) => ({
|
|
1326
|
+
index: batch.index,
|
|
1327
|
+
chunks: batch.chunks.filter((chunk) => !("toolCallId" in chunk) || toolCallIds.has(chunk.toolCallId))
|
|
1328
|
+
})))
|
|
1329
|
+
} : projection,
|
|
1330
|
+
compaction: state.compaction?.status === "running" ? projection?.baseCompaction ?? null : state.compaction,
|
|
1331
|
+
messages: state.messages.map((message) => message.id === payload.messageId ? pauseUIMessage({
|
|
1332
|
+
message,
|
|
1333
|
+
toolCallIds
|
|
1334
|
+
}) : message),
|
|
1335
|
+
pausedGenerationIds: {
|
|
1336
|
+
...state.pausedGenerationIds,
|
|
1337
|
+
[payload.generationId]: true
|
|
1338
|
+
}
|
|
1339
|
+
};
|
|
1340
|
+
}
|
|
717
1341
|
case "ai.model.metadata.requested": {
|
|
718
1342
|
const { modelId } = event.payload;
|
|
719
1343
|
if (Object.hasOwn(state.modelMetadata, modelId)) return state;
|
|
@@ -750,6 +1374,12 @@ function reduceAIState(state, event) {
|
|
|
750
1374
|
}
|
|
751
1375
|
case "ai.session.closed": return {
|
|
752
1376
|
...state,
|
|
1377
|
+
active: null,
|
|
1378
|
+
inbox: {
|
|
1379
|
+
paused: true,
|
|
1380
|
+
items: []
|
|
1381
|
+
},
|
|
1382
|
+
pendingQueueCommands: [],
|
|
753
1383
|
status: "closed",
|
|
754
1384
|
compaction: state.compaction?.status === "running" ? state.activeProjection?.baseCompaction ?? null : state.compaction,
|
|
755
1385
|
activeGeneration: null,
|
|
@@ -761,7 +1391,11 @@ function reduceAIState(state, event) {
|
|
|
761
1391
|
const { message } = event.payload;
|
|
762
1392
|
return {
|
|
763
1393
|
...state,
|
|
764
|
-
messages: upsertMessage(state.messages, message)
|
|
1394
|
+
messages: upsertMessage(state.messages, message),
|
|
1395
|
+
activeProjection: state.activeProjection?.responseMessageId === message.id ? {
|
|
1396
|
+
...state.activeProjection,
|
|
1397
|
+
cursor: null
|
|
1398
|
+
} : state.activeProjection
|
|
765
1399
|
};
|
|
766
1400
|
}
|
|
767
1401
|
case "ai.message.completed": {
|
|
@@ -792,7 +1426,7 @@ function reduceAIState(state, event) {
|
|
|
792
1426
|
const ownerRequestId = ownerGenerationId === void 0 ? void 0 : generationRequestId(ownerGenerationId);
|
|
793
1427
|
const requestId = event.id;
|
|
794
1428
|
if (requestId !== void 0 && state.terminalRequestIds?.[requestId] === true) return state;
|
|
795
|
-
if (!(payload.reason === "message" || (payload.reason === "tool" ? ownerGenerationId !== void 0 && requestId === `ai.generate:tools:${ownerGenerationId}` && state.activeRequestId === ownerRequestId && state.status !== "failed" : payload.reason === "retry" ? state.status === "failed" : ownerGenerationId !== void 0 && requestId?.startsWith(`ai.generate:input:${encodeURIComponent(responseMessageId)}:${encodeURIComponent(ownerGenerationId)}:`) === true && state.activeRequestId === ownerRequestId && state.status !== "failed"))) return state;
|
|
1429
|
+
if (!(payload.control !== void 0 && state.activeRequestId === event.id || payload.reason === "message" || (payload.reason === "tool" ? ownerGenerationId !== void 0 && requestId === `ai.generate:tools:${ownerGenerationId}` && state.activeRequestId === ownerRequestId && state.status !== "failed" : payload.reason === "retry" ? state.status === "failed" : ownerGenerationId !== void 0 && requestId?.startsWith(`ai.generate:input:${encodeURIComponent(responseMessageId)}:${encodeURIComponent(ownerGenerationId)}:`) === true && state.activeRequestId === ownerRequestId && state.status !== "failed"))) return state;
|
|
796
1430
|
return {
|
|
797
1431
|
...state,
|
|
798
1432
|
messages: payload.reason === "retry" && payload.responseMessageId !== void 0 ? state.messages.filter((message) => message.id !== payload.responseMessageId) : state.messages,
|
|
@@ -828,9 +1462,10 @@ function reduceAIState(state, event) {
|
|
|
828
1462
|
activeProjection: {
|
|
829
1463
|
generationId: payload.generationId,
|
|
830
1464
|
responseMessageId: payload.responseMessageId,
|
|
1465
|
+
promptThroughIndex: payload.promptThroughIndex ?? event.index - 1,
|
|
831
1466
|
...baseMessage === void 0 ? {} : { baseMessage },
|
|
832
1467
|
baseCompaction: baseState.compaction,
|
|
833
|
-
batches:
|
|
1468
|
+
batches: progressBatches(),
|
|
834
1469
|
baseTools: baseState.tools,
|
|
835
1470
|
toolEvents: [],
|
|
836
1471
|
approvalEvents: []
|
|
@@ -840,19 +1475,43 @@ function reduceAIState(state, event) {
|
|
|
840
1475
|
}
|
|
841
1476
|
case "ai.generation.progress": {
|
|
842
1477
|
const payload = event.payload;
|
|
1478
|
+
if (state.pausedGenerationIds?.[payload.generationId]) return state;
|
|
843
1479
|
if (terminalGenerationReason(state, payload.generationId) !== void 0) return state;
|
|
844
1480
|
const activeProjection = state.activeProjection;
|
|
845
1481
|
if (state.activeGeneration?.generationId !== payload.generationId || activeProjection?.generationId !== payload.generationId) return state;
|
|
1482
|
+
const ordered = event.index > (activeProjection.batches.tail.at(-1)?.index ?? -1);
|
|
846
1483
|
const projection = {
|
|
847
1484
|
...activeProjection,
|
|
848
|
-
batches:
|
|
1485
|
+
batches: ordered ? appendProgressBatch({
|
|
1486
|
+
batches: activeProjection.batches,
|
|
1487
|
+
batch: {
|
|
1488
|
+
index: event.index,
|
|
1489
|
+
chunks: payload.chunks
|
|
1490
|
+
}
|
|
1491
|
+
}) : progressBatches([...flattenProgressBatches(activeProjection.batches).filter((batch) => batch.index !== event.index), {
|
|
849
1492
|
index: event.index,
|
|
850
1493
|
chunks: payload.chunks
|
|
851
|
-
}].toSorted((left, right) => left.index - right.index)
|
|
1494
|
+
}].toSorted((left, right) => left.index - right.index))
|
|
1495
|
+
};
|
|
1496
|
+
const base = state.messages.find((message) => message.id === projection.responseMessageId);
|
|
1497
|
+
const incremental = base !== void 0 && activeProjection.cursor != null && event.index > activeProjection.cursor.throughIndex;
|
|
1498
|
+
const projected = incremental ? projectUIMessageStream({
|
|
1499
|
+
messageId: projection.responseMessageId,
|
|
1500
|
+
base,
|
|
1501
|
+
cursor: activeProjection.cursor.stream,
|
|
1502
|
+
chunks: payload.chunks
|
|
1503
|
+
}) : projectedMessageStream(projection);
|
|
1504
|
+
projection.cursor = {
|
|
1505
|
+
throughIndex: incremental ? event.index : Math.max(event.index, projection.batches.tail.at(-1)?.index ?? 0, projection.toolEvents.reduce((index, entry) => Math.max(index, entry.index), 0), projection.approvalEvents.reduce((index, entry) => Math.max(index, entry.index), 0)),
|
|
1506
|
+
stream: projected.cursor
|
|
852
1507
|
};
|
|
853
1508
|
return {
|
|
854
1509
|
...state,
|
|
855
|
-
messages:
|
|
1510
|
+
messages: upsertResponse({
|
|
1511
|
+
messages: state.messages,
|
|
1512
|
+
message: projected.message,
|
|
1513
|
+
inputId: state.activeGeneration.messageId
|
|
1514
|
+
}),
|
|
856
1515
|
activeProjection: projection,
|
|
857
1516
|
status: "generating"
|
|
858
1517
|
};
|
|
@@ -1051,13 +1710,17 @@ function reduceAIState(state, event) {
|
|
|
1051
1710
|
}
|
|
1052
1711
|
const activeProjection = {
|
|
1053
1712
|
...projection,
|
|
1713
|
+
cursor: null,
|
|
1054
1714
|
toolEvents: [...projection.toolEvents, {
|
|
1055
1715
|
index: event.index,
|
|
1056
1716
|
type: "ai.tool.result",
|
|
1057
1717
|
payload
|
|
1058
1718
|
}]
|
|
1059
1719
|
};
|
|
1060
|
-
const projected =
|
|
1720
|
+
const projected = projectedResponse({
|
|
1721
|
+
state,
|
|
1722
|
+
projection: activeProjection
|
|
1723
|
+
});
|
|
1061
1724
|
const tools = projectedTools(activeProjection);
|
|
1062
1725
|
const failureError = state.error ?? "Generation failed.";
|
|
1063
1726
|
const next = {
|
|
@@ -1078,6 +1741,7 @@ function reduceAIState(state, event) {
|
|
|
1078
1741
|
if (state.tools.some((activity) => activity.generationId === payload.generationId && activity.toolCallId === payload.toolCallId && activity.status === "failed" && state.activeGeneration?.generationId !== payload.generationId)) return state;
|
|
1079
1742
|
const activeProjection = projection?.generationId === payload.generationId ? {
|
|
1080
1743
|
...projection,
|
|
1744
|
+
cursor: null,
|
|
1081
1745
|
approvalEvents: [...projection.approvalEvents, {
|
|
1082
1746
|
index: event.index,
|
|
1083
1747
|
type: "ai.approval.requested",
|
|
@@ -1086,7 +1750,10 @@ function reduceAIState(state, event) {
|
|
|
1086
1750
|
} : projection;
|
|
1087
1751
|
const next = {
|
|
1088
1752
|
...state,
|
|
1089
|
-
messages: activeProjection === null || activeProjection === projection ? projectMessageChunks(state.messages, payload.messageId, [approvalRequestedChunk(payload)]) : upsertMessage(state.messages,
|
|
1753
|
+
messages: activeProjection === null || activeProjection === projection ? projectMessageChunks(state.messages, payload.messageId, [approvalRequestedChunk(payload)]) : upsertMessage(state.messages, projectedResponse({
|
|
1754
|
+
state,
|
|
1755
|
+
projection: activeProjection
|
|
1756
|
+
})),
|
|
1090
1757
|
activeProjection,
|
|
1091
1758
|
pendingApprovals: payload.isAutomatic === true ? state.pendingApprovals : [...state.pendingApprovals.filter((approval) => approval.approvalId !== payload.approvalId || approval.messageId !== payload.messageId), payload]
|
|
1092
1759
|
};
|
|
@@ -1104,6 +1771,7 @@ function reduceAIState(state, event) {
|
|
|
1104
1771
|
if (state.status === "failed" && pendingRequest === void 0 && activeRequest === void 0) return state;
|
|
1105
1772
|
const activeProjection = projection !== null && activeRequest !== void 0 ? {
|
|
1106
1773
|
...projection,
|
|
1774
|
+
cursor: null,
|
|
1107
1775
|
approvalEvents: [...projection.approvalEvents, {
|
|
1108
1776
|
index: event.index,
|
|
1109
1777
|
type: "ai.approval.responded",
|
|
@@ -1112,8 +1780,14 @@ function reduceAIState(state, event) {
|
|
|
1112
1780
|
} : projection;
|
|
1113
1781
|
const next = {
|
|
1114
1782
|
...state,
|
|
1115
|
-
messages: activeProjection === null || activeProjection === projection ? answerApproval(state.messages, payload) : upsertMessage(state.messages,
|
|
1116
|
-
|
|
1783
|
+
messages: activeProjection === null || activeProjection === projection ? answerApproval(state.messages, payload) : upsertMessage(state.messages, projectedResponse({
|
|
1784
|
+
state,
|
|
1785
|
+
projection: activeProjection
|
|
1786
|
+
})),
|
|
1787
|
+
activeProjection: activeProjection === null ? null : {
|
|
1788
|
+
...activeProjection,
|
|
1789
|
+
cursor: null
|
|
1790
|
+
},
|
|
1117
1791
|
pendingApprovals: state.pendingApprovals.filter((approval) => approval.approvalId !== payload.approvalId || approval.messageId !== payload.messageId || approval.generationId !== payload.generationId)
|
|
1118
1792
|
};
|
|
1119
1793
|
return {
|
|
@@ -1163,7 +1837,8 @@ function reduceAIState(state, event) {
|
|
|
1163
1837
|
...state,
|
|
1164
1838
|
compaction: {
|
|
1165
1839
|
status: "completed",
|
|
1166
|
-
...payload
|
|
1840
|
+
...payload,
|
|
1841
|
+
throughIndex: payload.throughIndex ?? state.activeProjection.promptThroughIndex
|
|
1167
1842
|
}
|
|
1168
1843
|
};
|
|
1169
1844
|
}
|
|
@@ -1178,62 +1853,105 @@ function deriveUIMessages(history) {
|
|
|
1178
1853
|
}
|
|
1179
1854
|
/** Build the standard AI state projection for any contract containing the protocol. */
|
|
1180
1855
|
function createReducer(options) {
|
|
1181
|
-
return
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1856
|
+
return {
|
|
1857
|
+
...options.contract.reducer({
|
|
1858
|
+
name: options.name ?? "a2.ai.state.v17",
|
|
1859
|
+
initialState: initialState()
|
|
1860
|
+
}).fold((state, event) => reduceAIState(state, event)),
|
|
1861
|
+
[clientStateProjection]: projectAIClientState
|
|
1862
|
+
};
|
|
1185
1863
|
}
|
|
1864
|
+
const commandInput = (payload) => [{
|
|
1865
|
+
type: "ai.control.requested",
|
|
1866
|
+
payload
|
|
1867
|
+
}];
|
|
1186
1868
|
const messageInput = (message, generate) => [{
|
|
1187
|
-
type: "ai.
|
|
1869
|
+
type: "ai.control.requested",
|
|
1188
1870
|
id: `ai.message:${message.id}`,
|
|
1189
1871
|
payload: {
|
|
1872
|
+
action: "send",
|
|
1190
1873
|
message,
|
|
1191
1874
|
...generate === false ? { generate: false } : {}
|
|
1192
1875
|
}
|
|
1193
1876
|
}];
|
|
1194
1877
|
/** Pure append inputs for the built-in AI protocol. */
|
|
1195
1878
|
const inputs = {
|
|
1879
|
+
toolResult: (result) => commandInput({
|
|
1880
|
+
action: "tool-result",
|
|
1881
|
+
result
|
|
1882
|
+
}),
|
|
1883
|
+
queue: {
|
|
1884
|
+
remove: (options) => commandInput({
|
|
1885
|
+
action: "remove",
|
|
1886
|
+
...options
|
|
1887
|
+
}),
|
|
1888
|
+
sendNow: (options) => commandInput({
|
|
1889
|
+
action: "send-now",
|
|
1890
|
+
...options
|
|
1891
|
+
}),
|
|
1892
|
+
edit: (options) => commandInput({
|
|
1893
|
+
action: "edit",
|
|
1894
|
+
...options
|
|
1895
|
+
}),
|
|
1896
|
+
move: (options) => commandInput({
|
|
1897
|
+
action: "move",
|
|
1898
|
+
...options
|
|
1899
|
+
})
|
|
1900
|
+
},
|
|
1901
|
+
stop: (options) => commandInput({
|
|
1902
|
+
action: "stop",
|
|
1903
|
+
...options
|
|
1904
|
+
}),
|
|
1905
|
+
steer: (options) => commandInput({
|
|
1906
|
+
action: "steer",
|
|
1907
|
+
...options
|
|
1908
|
+
}),
|
|
1909
|
+
pause: (options) => commandInput({
|
|
1910
|
+
action: "pause",
|
|
1911
|
+
...options
|
|
1912
|
+
}),
|
|
1913
|
+
resume: () => commandInput({ action: "resume" }),
|
|
1196
1914
|
message(message, options) {
|
|
1197
1915
|
return messageInput(message, options?.generate);
|
|
1198
1916
|
},
|
|
1199
1917
|
seed(message) {
|
|
1200
1918
|
return messageInput(message, false);
|
|
1201
1919
|
},
|
|
1202
|
-
approval(response) {
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
}
|
|
1208
|
-
},
|
|
1209
|
-
input(response) {
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
}
|
|
1215
|
-
},
|
|
1216
|
-
requestInput(request) {
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
}
|
|
1222
|
-
},
|
|
1223
|
-
retry(
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
}
|
|
1229
|
-
},
|
|
1230
|
-
interrupt(
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
}
|
|
1236
|
-
}
|
|
1920
|
+
approval: (response) => [{
|
|
1921
|
+
...commandInput({
|
|
1922
|
+
action: "approval",
|
|
1923
|
+
response
|
|
1924
|
+
})[0],
|
|
1925
|
+
id: `ai.approval:${encodeURIComponent(response.messageId)}:${encodeURIComponent(response.generationId)}:${encodeURIComponent(response.approvalId)}:response`
|
|
1926
|
+
}],
|
|
1927
|
+
input: (response) => [{
|
|
1928
|
+
...commandInput({
|
|
1929
|
+
action: "input",
|
|
1930
|
+
response
|
|
1931
|
+
})[0],
|
|
1932
|
+
id: `ai.input:${encodeURIComponent(response.messageId)}:${encodeURIComponent(response.generationId)}:${encodeURIComponent(response.inputId)}:response`
|
|
1933
|
+
}],
|
|
1934
|
+
requestInput: (request) => [{
|
|
1935
|
+
...commandInput({
|
|
1936
|
+
action: "request-input",
|
|
1937
|
+
request
|
|
1938
|
+
})[0],
|
|
1939
|
+
id: `ai.input:${encodeURIComponent(request.messageId)}:${encodeURIComponent(request.generationId)}:${encodeURIComponent(request.inputId)}:request`
|
|
1940
|
+
}],
|
|
1941
|
+
retry: (request) => [{
|
|
1942
|
+
...commandInput({
|
|
1943
|
+
action: "retry",
|
|
1944
|
+
request
|
|
1945
|
+
})[0],
|
|
1946
|
+
id: `ai.retry:${request.retryId}`
|
|
1947
|
+
}],
|
|
1948
|
+
interrupt: (request) => [{
|
|
1949
|
+
...commandInput({
|
|
1950
|
+
action: "interrupt",
|
|
1951
|
+
request
|
|
1952
|
+
})[0],
|
|
1953
|
+
id: `ai.interrupt:${request.generationId ?? request.requestId}`
|
|
1954
|
+
}]
|
|
1237
1955
|
};
|
|
1238
1956
|
/**
|
|
1239
1957
|
* Read the durable handler context ambient to the current AI tool
|
|
@@ -1267,10 +1985,13 @@ function agent(options) {
|
|
|
1267
1985
|
});
|
|
1268
1986
|
return {
|
|
1269
1987
|
contract: agentContract,
|
|
1270
|
-
reducer:
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1988
|
+
reducer: {
|
|
1989
|
+
...agentContract.reducer({
|
|
1990
|
+
name: options.reducerName ?? "a2.ai.state.v17",
|
|
1991
|
+
initialState: initialState()
|
|
1992
|
+
}).fold((state, event) => reduceAIState(state, event)),
|
|
1993
|
+
[clientStateProjection]: projectAIClientState
|
|
1994
|
+
}
|
|
1274
1995
|
};
|
|
1275
1996
|
}
|
|
1276
1997
|
//#endregion
|