experimental-a2 0.12.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 +47 -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 +16 -8
- package/dist/ai-server.d.ts.map +1 -1
- package/dist/ai-server.js +1322 -475
- package/dist/ai-server.js.map +1 -1
- package/dist/ai.d.ts +2 -334
- package/dist/ai.js +838 -85
- 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 +361 -76
- package/docs/reference/01-api.mdx +159 -27
- package/examples/playground/app/agent/[agentId]/agent-client.tsx +145 -33
- package/examples/playground/app/agent/[agentId]/agent-queue.tsx +289 -0
- package/examples/playground/app/agent/[agentId]/compaction/route.ts +14 -0
- package/examples/playground/app/agent/[agentId]/compaction-event.tsx +38 -0
- package/examples/playground/app/agent/[agentId]/compaction-panel.tsx +294 -0
- package/examples/playground/app/agent/compaction-settings.test.ts +144 -0
- package/examples/playground/app/agent/compaction-settings.ts +49 -0
- package/examples/playground/app/agent/compaction-timeline.test.ts +337 -0
- package/examples/playground/app/agent/compaction-timeline.ts +198 -0
- package/examples/playground/app/agent/model.ts +56 -1
- package/examples/playground/app/agent/server.ts +9 -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 +333 -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-model-metadata.ts +108 -0
- package/src/ai-progress-batches.ts +68 -0
- package/src/ai-projector.ts +76 -15
- package/src/ai-sdk-step.ts +5 -2
- package/src/ai-server.ts +920 -638
- package/src/ai.ts +650 -110
- 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": {
|
|
@@ -419,6 +882,7 @@ const jsonObject = (label, check) => schema(label, (value) => {
|
|
|
419
882
|
return { value };
|
|
420
883
|
});
|
|
421
884
|
const stringField = (value, key) => typeof value[key] === "string" && value[key].length > 0;
|
|
885
|
+
const optionalIndexField = (value, key) => value[key] === void 0 || typeof value[key] === "number" && Number.isSafeInteger(value[key]) && value[key] >= 0;
|
|
422
886
|
const optionalStringField = (value, key) => value[key] === void 0 || typeof value[key] === "string";
|
|
423
887
|
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);
|
|
424
888
|
const validateMessage = (value, messageSchema) => {
|
|
@@ -442,11 +906,11 @@ const progressPayloadSchema = schema("ai.generation.progress", (value) => {
|
|
|
442
906
|
return { value };
|
|
443
907
|
});
|
|
444
908
|
const completedPayloadSchema = schema("ai.generation.completed", (value) => {
|
|
445
|
-
if (!isRecord(value) || !isJSONCompatible(value) || !stringField(value, "requestId") || !stringField(value, "messageId") || !stringField(value, "generationId") || !stringField(value, "responseMessageId") || !optionalStringField(value, "finishReason")) return issue("invalid ai.generation.completed payload");
|
|
909
|
+
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");
|
|
446
910
|
return { value };
|
|
447
911
|
});
|
|
448
912
|
const compactionPayloadSchema = (messageSchema) => schema("ai.compaction.completed", (value) => {
|
|
449
|
-
if (!isRecord(value) || !isJSONCompatible(value) || !stringField(value, "generationId") || !stringField(value, "throughMessageId") || !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");
|
|
913
|
+
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");
|
|
450
914
|
const messages = [];
|
|
451
915
|
for (const candidate of value["messages"]) {
|
|
452
916
|
const result = validateMessage(candidate, messageSchema);
|
|
@@ -456,22 +920,91 @@ const compactionPayloadSchema = (messageSchema) => schema("ai.compaction.complet
|
|
|
456
920
|
return { value: {
|
|
457
921
|
generationId: value["generationId"],
|
|
458
922
|
throughMessageId: value["throughMessageId"],
|
|
923
|
+
...value["throughIndex"] === void 0 ? {} : { throughIndex: value["throughIndex"] },
|
|
459
924
|
messages,
|
|
925
|
+
...value["summary"] === void 0 ? {} : { summary: value["summary"] },
|
|
926
|
+
...value["usage"] === void 0 ? {} : { usage: value["usage"] },
|
|
460
927
|
...value["retainedMessageIds"] === void 0 ? {} : { retainedMessageIds: value["retainedMessageIds"] }
|
|
461
928
|
} };
|
|
462
929
|
});
|
|
463
930
|
function createEvents(options) {
|
|
464
931
|
const messageSchema = options?.messageSchema;
|
|
465
932
|
if (messageSchema) assertSyncSchema(messageSchema, "the AI message schema");
|
|
466
|
-
|
|
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")),
|
|
467
1000
|
"ai.session.created": jsonObject("ai.session.created", () => true),
|
|
468
1001
|
"ai.session.closed": jsonObject("ai.session.closed", (value) => optionalStringField(value, "reason")),
|
|
469
1002
|
"ai.message.created": messagePayloadSchema(messageSchema),
|
|
470
1003
|
"ai.message.completed": jsonObject("ai.message.completed", (value) => stringField(value, "messageId")),
|
|
471
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)),
|
|
472
|
-
"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")),
|
|
473
1006
|
"ai.retry.requested": jsonObject("ai.retry.requested", (value) => stringField(value, "messageId") && stringField(value, "responseMessageId") && stringField(value, "retryId")),
|
|
474
|
-
"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")),
|
|
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")),
|
|
475
1008
|
"ai.generation.progress": progressPayloadSchema,
|
|
476
1009
|
"ai.generation.completed": completedPayloadSchema,
|
|
477
1010
|
"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")),
|
|
@@ -481,12 +1014,20 @@ function createEvents(options) {
|
|
|
481
1014
|
"ai.approval.responded": jsonObject("ai.approval.responded", (value) => stringField(value, "messageId") && stringField(value, "generationId") && stringField(value, "approvalId") && typeof value["approved"] === "boolean" && optionalStringField(value, "reason")),
|
|
482
1015
|
"ai.input.requested": jsonObject("ai.input.requested", (value) => stringField(value, "messageId") && stringField(value, "generationId") && stringField(value, "inputId") && stringField(value, "name")),
|
|
483
1016
|
"ai.input.responded": jsonObject("ai.input.responded", (value) => stringField(value, "messageId") && stringField(value, "generationId") && stringField(value, "inputId") && stringField(value, "name")),
|
|
484
|
-
"ai.compaction.requested": jsonObject("ai.compaction.requested", (value) => stringField(value, "generationId") && stringField(value, "throughMessageId")),
|
|
485
|
-
"ai.compaction.completed": compactionPayloadSchema(messageSchema)
|
|
1017
|
+
"ai.compaction.requested": jsonObject("ai.compaction.requested", (value) => stringField(value, "generationId") && stringField(value, "throughMessageId") && optionalIndexField(value, "throughIndex")),
|
|
1018
|
+
"ai.compaction.completed": compactionPayloadSchema(messageSchema),
|
|
1019
|
+
"ai.model.metadata.requested": jsonObject("ai.model.metadata.requested", (value) => stringField(value, "modelId")),
|
|
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))
|
|
486
1021
|
};
|
|
1022
|
+
return definition;
|
|
487
1023
|
}
|
|
488
1024
|
const events = createEvents();
|
|
489
1025
|
const initialState = () => ({
|
|
1026
|
+
inbox: initialControlState().inbox,
|
|
1027
|
+
pendingQueueCommands: [],
|
|
1028
|
+
active: null,
|
|
1029
|
+
starting: false,
|
|
1030
|
+
receipt: null,
|
|
490
1031
|
session: { created: false },
|
|
491
1032
|
messages: [],
|
|
492
1033
|
status: "idle",
|
|
@@ -499,6 +1040,7 @@ const initialState = () => ({
|
|
|
499
1040
|
pendingInputs: [],
|
|
500
1041
|
tools: [],
|
|
501
1042
|
compaction: null,
|
|
1043
|
+
modelMetadata: {},
|
|
502
1044
|
usage: [],
|
|
503
1045
|
error: null
|
|
504
1046
|
});
|
|
@@ -509,6 +1051,17 @@ const upsertMessage = (messages, message) => {
|
|
|
509
1051
|
next[index] = message;
|
|
510
1052
|
return next;
|
|
511
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
|
+
};
|
|
512
1065
|
const answerApproval = (messages, response) => messages.map((message) => {
|
|
513
1066
|
if (message.id !== response.messageId) return message;
|
|
514
1067
|
let changed = false;
|
|
@@ -594,8 +1147,8 @@ const isStaleGeneration = (state, messageId, generationId) => {
|
|
|
594
1147
|
const current = state.responseGenerationIds[messageId];
|
|
595
1148
|
return current !== void 0 && current !== generationId;
|
|
596
1149
|
};
|
|
597
|
-
const
|
|
598
|
-
const projectionEvents =
|
|
1150
|
+
const projectedMessageStream = (projection) => {
|
|
1151
|
+
const projectionEvents = flattenProgressBatches(projection.batches);
|
|
599
1152
|
for (const event of projection.toolEvents) if (event.type === "ai.tool.result") projectionEvents.push({
|
|
600
1153
|
index: event.index,
|
|
601
1154
|
chunks: [toolResultChunk(event.payload)]
|
|
@@ -604,12 +1157,20 @@ const projectedMessage = (projection) => {
|
|
|
604
1157
|
index: event.index,
|
|
605
1158
|
chunks: [event.type === "ai.approval.requested" ? approvalRequestedChunk(event.payload) : approvalRespondedChunk(event.payload)]
|
|
606
1159
|
});
|
|
607
|
-
return
|
|
1160
|
+
return projectUIMessageStream({
|
|
608
1161
|
messageId: projection.responseMessageId,
|
|
609
1162
|
...projection.baseMessage === void 0 ? {} : { base: projection.baseMessage },
|
|
610
1163
|
chunks: projectionEvents.toSorted((left, right) => left.index - right.index).flatMap((event) => event.chunks)
|
|
611
1164
|
});
|
|
612
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
|
+
};
|
|
613
1174
|
const reduceToolActivity = (tools, event) => {
|
|
614
1175
|
if (event.type === "ai.tool.called") {
|
|
615
1176
|
const payload = event.payload;
|
|
@@ -680,7 +1241,7 @@ const interruptGenerationProjection = (state, generationId, responseMessageId, c
|
|
|
680
1241
|
if (projection === null) return null;
|
|
681
1242
|
const retained = {
|
|
682
1243
|
...projection,
|
|
683
|
-
batches: projection.batches.filter((batch) => batch.index <= cutoff),
|
|
1244
|
+
batches: progressBatches(flattenProgressBatches(projection.batches).filter((batch) => batch.index <= cutoff)),
|
|
684
1245
|
toolEvents: projection.toolEvents.filter((toolEvent) => toolEvent.index <= cutoff),
|
|
685
1246
|
approvalEvents: projection.approvalEvents.filter((approvalEvent) => approvalEvent.index <= cutoff)
|
|
686
1247
|
};
|
|
@@ -688,7 +1249,7 @@ const interruptGenerationProjection = (state, generationId, responseMessageId, c
|
|
|
688
1249
|
const restored = restoreGenerationBase(state, generationId, responseMessageId, "interrupted");
|
|
689
1250
|
return {
|
|
690
1251
|
...restored,
|
|
691
|
-
messages: shouldProject ? upsertMessage(
|
|
1252
|
+
messages: shouldProject ? upsertMessage(state.messages, interruptUIMessage(projectedMessage(retained))) : restored.messages,
|
|
692
1253
|
tools: stopGenerationTools(projectedTools(retained), generationId, responseMessageId, "Tool execution was interrupted."),
|
|
693
1254
|
usage: projection.completionIndex === void 0 || projection.completionIndex <= cutoff ? restored.usage : restored.usage.filter((entry) => entry.generationId !== generationId)
|
|
694
1255
|
};
|
|
@@ -707,6 +1268,100 @@ const terminalGenerationReason = (state, generationId) => {
|
|
|
707
1268
|
function reduceAIState(state, event) {
|
|
708
1269
|
if (state.status === "closed") return state;
|
|
709
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
|
+
}
|
|
1341
|
+
case "ai.model.metadata.requested": {
|
|
1342
|
+
const { modelId } = event.payload;
|
|
1343
|
+
if (Object.hasOwn(state.modelMetadata, modelId)) return state;
|
|
1344
|
+
return {
|
|
1345
|
+
...state,
|
|
1346
|
+
modelMetadata: {
|
|
1347
|
+
...state.modelMetadata,
|
|
1348
|
+
[modelId]: { status: "pending" }
|
|
1349
|
+
}
|
|
1350
|
+
};
|
|
1351
|
+
}
|
|
1352
|
+
case "ai.model.metadata.resolved": {
|
|
1353
|
+
const { modelId, limits } = event.payload;
|
|
1354
|
+
return {
|
|
1355
|
+
...state,
|
|
1356
|
+
modelMetadata: {
|
|
1357
|
+
...state.modelMetadata,
|
|
1358
|
+
[modelId]: limits === null ? { status: "unavailable" } : {
|
|
1359
|
+
status: "resolved",
|
|
1360
|
+
limits
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
};
|
|
1364
|
+
}
|
|
710
1365
|
case "ai.session.created": {
|
|
711
1366
|
const payload = event.payload;
|
|
712
1367
|
return {
|
|
@@ -719,7 +1374,14 @@ function reduceAIState(state, event) {
|
|
|
719
1374
|
}
|
|
720
1375
|
case "ai.session.closed": return {
|
|
721
1376
|
...state,
|
|
1377
|
+
active: null,
|
|
1378
|
+
inbox: {
|
|
1379
|
+
paused: true,
|
|
1380
|
+
items: []
|
|
1381
|
+
},
|
|
1382
|
+
pendingQueueCommands: [],
|
|
722
1383
|
status: "closed",
|
|
1384
|
+
compaction: state.compaction?.status === "running" ? state.activeProjection?.baseCompaction ?? null : state.compaction,
|
|
723
1385
|
activeGeneration: null,
|
|
724
1386
|
activeRequestId: null,
|
|
725
1387
|
activeResponseMessageId: null,
|
|
@@ -729,7 +1391,11 @@ function reduceAIState(state, event) {
|
|
|
729
1391
|
const { message } = event.payload;
|
|
730
1392
|
return {
|
|
731
1393
|
...state,
|
|
732
|
-
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
|
|
733
1399
|
};
|
|
734
1400
|
}
|
|
735
1401
|
case "ai.message.completed": {
|
|
@@ -760,7 +1426,7 @@ function reduceAIState(state, event) {
|
|
|
760
1426
|
const ownerRequestId = ownerGenerationId === void 0 ? void 0 : generationRequestId(ownerGenerationId);
|
|
761
1427
|
const requestId = event.id;
|
|
762
1428
|
if (requestId !== void 0 && state.terminalRequestIds?.[requestId] === true) return state;
|
|
763
|
-
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;
|
|
764
1430
|
return {
|
|
765
1431
|
...state,
|
|
766
1432
|
messages: payload.reason === "retry" && payload.responseMessageId !== void 0 ? state.messages.filter((message) => message.id !== payload.responseMessageId) : state.messages,
|
|
@@ -796,9 +1462,10 @@ function reduceAIState(state, event) {
|
|
|
796
1462
|
activeProjection: {
|
|
797
1463
|
generationId: payload.generationId,
|
|
798
1464
|
responseMessageId: payload.responseMessageId,
|
|
1465
|
+
promptThroughIndex: payload.promptThroughIndex ?? event.index - 1,
|
|
799
1466
|
...baseMessage === void 0 ? {} : { baseMessage },
|
|
800
1467
|
baseCompaction: baseState.compaction,
|
|
801
|
-
batches:
|
|
1468
|
+
batches: progressBatches(),
|
|
802
1469
|
baseTools: baseState.tools,
|
|
803
1470
|
toolEvents: [],
|
|
804
1471
|
approvalEvents: []
|
|
@@ -808,19 +1475,43 @@ function reduceAIState(state, event) {
|
|
|
808
1475
|
}
|
|
809
1476
|
case "ai.generation.progress": {
|
|
810
1477
|
const payload = event.payload;
|
|
1478
|
+
if (state.pausedGenerationIds?.[payload.generationId]) return state;
|
|
811
1479
|
if (terminalGenerationReason(state, payload.generationId) !== void 0) return state;
|
|
812
1480
|
const activeProjection = state.activeProjection;
|
|
813
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);
|
|
814
1483
|
const projection = {
|
|
815
1484
|
...activeProjection,
|
|
816
|
-
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), {
|
|
817
1492
|
index: event.index,
|
|
818
1493
|
chunks: payload.chunks
|
|
819
|
-
}].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
|
|
820
1507
|
};
|
|
821
1508
|
return {
|
|
822
1509
|
...state,
|
|
823
|
-
messages:
|
|
1510
|
+
messages: upsertResponse({
|
|
1511
|
+
messages: state.messages,
|
|
1512
|
+
message: projected.message,
|
|
1513
|
+
inputId: state.activeGeneration.messageId
|
|
1514
|
+
}),
|
|
824
1515
|
activeProjection: projection,
|
|
825
1516
|
status: "generating"
|
|
826
1517
|
};
|
|
@@ -1019,13 +1710,17 @@ function reduceAIState(state, event) {
|
|
|
1019
1710
|
}
|
|
1020
1711
|
const activeProjection = {
|
|
1021
1712
|
...projection,
|
|
1713
|
+
cursor: null,
|
|
1022
1714
|
toolEvents: [...projection.toolEvents, {
|
|
1023
1715
|
index: event.index,
|
|
1024
1716
|
type: "ai.tool.result",
|
|
1025
1717
|
payload
|
|
1026
1718
|
}]
|
|
1027
1719
|
};
|
|
1028
|
-
const projected =
|
|
1720
|
+
const projected = projectedResponse({
|
|
1721
|
+
state,
|
|
1722
|
+
projection: activeProjection
|
|
1723
|
+
});
|
|
1029
1724
|
const tools = projectedTools(activeProjection);
|
|
1030
1725
|
const failureError = state.error ?? "Generation failed.";
|
|
1031
1726
|
const next = {
|
|
@@ -1046,6 +1741,7 @@ function reduceAIState(state, event) {
|
|
|
1046
1741
|
if (state.tools.some((activity) => activity.generationId === payload.generationId && activity.toolCallId === payload.toolCallId && activity.status === "failed" && state.activeGeneration?.generationId !== payload.generationId)) return state;
|
|
1047
1742
|
const activeProjection = projection?.generationId === payload.generationId ? {
|
|
1048
1743
|
...projection,
|
|
1744
|
+
cursor: null,
|
|
1049
1745
|
approvalEvents: [...projection.approvalEvents, {
|
|
1050
1746
|
index: event.index,
|
|
1051
1747
|
type: "ai.approval.requested",
|
|
@@ -1054,7 +1750,10 @@ function reduceAIState(state, event) {
|
|
|
1054
1750
|
} : projection;
|
|
1055
1751
|
const next = {
|
|
1056
1752
|
...state,
|
|
1057
|
-
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
|
+
})),
|
|
1058
1757
|
activeProjection,
|
|
1059
1758
|
pendingApprovals: payload.isAutomatic === true ? state.pendingApprovals : [...state.pendingApprovals.filter((approval) => approval.approvalId !== payload.approvalId || approval.messageId !== payload.messageId), payload]
|
|
1060
1759
|
};
|
|
@@ -1072,6 +1771,7 @@ function reduceAIState(state, event) {
|
|
|
1072
1771
|
if (state.status === "failed" && pendingRequest === void 0 && activeRequest === void 0) return state;
|
|
1073
1772
|
const activeProjection = projection !== null && activeRequest !== void 0 ? {
|
|
1074
1773
|
...projection,
|
|
1774
|
+
cursor: null,
|
|
1075
1775
|
approvalEvents: [...projection.approvalEvents, {
|
|
1076
1776
|
index: event.index,
|
|
1077
1777
|
type: "ai.approval.responded",
|
|
@@ -1080,8 +1780,14 @@ function reduceAIState(state, event) {
|
|
|
1080
1780
|
} : projection;
|
|
1081
1781
|
const next = {
|
|
1082
1782
|
...state,
|
|
1083
|
-
messages: activeProjection === null || activeProjection === projection ? answerApproval(state.messages, payload) : upsertMessage(state.messages,
|
|
1084
|
-
|
|
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
|
+
},
|
|
1085
1791
|
pendingApprovals: state.pendingApprovals.filter((approval) => approval.approvalId !== payload.approvalId || approval.messageId !== payload.messageId || approval.generationId !== payload.generationId)
|
|
1086
1792
|
};
|
|
1087
1793
|
return {
|
|
@@ -1131,7 +1837,8 @@ function reduceAIState(state, event) {
|
|
|
1131
1837
|
...state,
|
|
1132
1838
|
compaction: {
|
|
1133
1839
|
status: "completed",
|
|
1134
|
-
...payload
|
|
1840
|
+
...payload,
|
|
1841
|
+
throughIndex: payload.throughIndex ?? state.activeProjection.promptThroughIndex
|
|
1135
1842
|
}
|
|
1136
1843
|
};
|
|
1137
1844
|
}
|
|
@@ -1146,62 +1853,105 @@ function deriveUIMessages(history) {
|
|
|
1146
1853
|
}
|
|
1147
1854
|
/** Build the standard AI state projection for any contract containing the protocol. */
|
|
1148
1855
|
function createReducer(options) {
|
|
1149
|
-
return
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
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
|
+
};
|
|
1153
1863
|
}
|
|
1864
|
+
const commandInput = (payload) => [{
|
|
1865
|
+
type: "ai.control.requested",
|
|
1866
|
+
payload
|
|
1867
|
+
}];
|
|
1154
1868
|
const messageInput = (message, generate) => [{
|
|
1155
|
-
type: "ai.
|
|
1869
|
+
type: "ai.control.requested",
|
|
1156
1870
|
id: `ai.message:${message.id}`,
|
|
1157
1871
|
payload: {
|
|
1872
|
+
action: "send",
|
|
1158
1873
|
message,
|
|
1159
1874
|
...generate === false ? { generate: false } : {}
|
|
1160
1875
|
}
|
|
1161
1876
|
}];
|
|
1162
1877
|
/** Pure append inputs for the built-in AI protocol. */
|
|
1163
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" }),
|
|
1164
1914
|
message(message, options) {
|
|
1165
1915
|
return messageInput(message, options?.generate);
|
|
1166
1916
|
},
|
|
1167
1917
|
seed(message) {
|
|
1168
1918
|
return messageInput(message, false);
|
|
1169
1919
|
},
|
|
1170
|
-
approval(response) {
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
}
|
|
1176
|
-
},
|
|
1177
|
-
input(response) {
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
}
|
|
1183
|
-
},
|
|
1184
|
-
requestInput(request) {
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
}
|
|
1190
|
-
},
|
|
1191
|
-
retry(
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
}
|
|
1197
|
-
},
|
|
1198
|
-
interrupt(
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
}
|
|
1204
|
-
}
|
|
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
|
+
}]
|
|
1205
1955
|
};
|
|
1206
1956
|
/**
|
|
1207
1957
|
* Read the durable handler context ambient to the current AI tool
|
|
@@ -1235,10 +1985,13 @@ function agent(options) {
|
|
|
1235
1985
|
});
|
|
1236
1986
|
return {
|
|
1237
1987
|
contract: agentContract,
|
|
1238
|
-
reducer:
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
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
|
+
}
|
|
1242
1995
|
};
|
|
1243
1996
|
}
|
|
1244
1997
|
//#endregion
|