experimental-a2 0.16.0 → 0.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/dist/{ai-HJ9fHfYI.d.ts → ai-CrEf6p_W.d.ts} +2 -2
- package/dist/ai-CrEf6p_W.d.ts.map +1 -0
- package/dist/{ai-CrLYwNEx.js → ai-DgOBltJ_.js} +323 -303
- package/dist/ai-DgOBltJ_.js.map +1 -0
- package/dist/ai-server.d.ts +1 -1
- package/dist/ai-server.d.ts.map +1 -1
- package/dist/ai-server.js +35 -8
- package/dist/ai-server.js.map +1 -1
- package/dist/ai.d.ts +1 -1
- package/dist/ai.js +1 -1
- package/docs/guides/06-ai-agents.mdx +15 -5
- package/docs/reference/01-api.mdx +8 -3
- package/examples/playground/package.json +1 -1
- package/package.json +1 -1
- package/src/ai-client-state.ts +36 -2
- package/src/ai-control-server.ts +24 -5
- package/src/ai-control.ts +2 -0
- package/src/ai-message-projection.ts +127 -1
- package/src/ai-server.ts +15 -2
- package/src/ai.ts +6 -123
- package/dist/ai-CrLYwNEx.js.map +0 -1
- package/dist/ai-HJ9fHfYI.d.ts.map +0 -1
|
@@ -3,249 +3,6 @@ import { t as clientStateProjection } from "./reducer-DEMjEY_O.js";
|
|
|
3
3
|
import { t as contract } from "./contract-CKRg_E4q.js";
|
|
4
4
|
import { t as idempotentId } from "./idempotent-id-BRJVeylj.js";
|
|
5
5
|
import { l as ambientToolScopeStorage } from "./internal-Dq2qYxou.js";
|
|
6
|
-
//#region src/ai-client-state.ts
|
|
7
|
-
const isQueueCommand = (command) => command.action === "send" || command.action === "edit" || command.action === "move" || command.action === "remove" || command.action === "send-now" || command.action === "steer" || command.action === "pause" || command.action === "resume";
|
|
8
|
-
const moveItem = ({ items, inputId, beforeId }) => {
|
|
9
|
-
const from = items.findIndex((item) => item.id === inputId);
|
|
10
|
-
if (from < 0 || inputId === beforeId || beforeId !== null && !items.some((item) => item.id === beforeId)) return items;
|
|
11
|
-
const next = [...items];
|
|
12
|
-
const [item] = next.splice(from, 1);
|
|
13
|
-
const to = beforeId === null ? next.length : next.findIndex((entry) => entry.id === beforeId);
|
|
14
|
-
next.splice(to, 0, item);
|
|
15
|
-
return next;
|
|
16
|
-
};
|
|
17
|
-
function projectAIClientState(state) {
|
|
18
|
-
const pending = state.pendingQueueCommands ?? [];
|
|
19
|
-
if (state.status === "closed") return state;
|
|
20
|
-
if (pending.length === 0 && !state.inbox.items.some((item) => item.afterStepOf !== void 0)) return state;
|
|
21
|
-
let messages = state.messages;
|
|
22
|
-
let { items, paused } = state.inbox;
|
|
23
|
-
let turnId = state.active?.turnId ?? null;
|
|
24
|
-
let starting = false;
|
|
25
|
-
let suspended = state.active?.phase === "paused" || state.active?.phase === "pausing";
|
|
26
|
-
let messageIds;
|
|
27
|
-
const show = (message) => {
|
|
28
|
-
messageIds ??= new Set(messages.map((item) => item.id));
|
|
29
|
-
if (messageIds.has(message.id)) return;
|
|
30
|
-
if (messages === state.messages) messages = state.messages.slice();
|
|
31
|
-
messages.push(message);
|
|
32
|
-
messageIds.add(message.id);
|
|
33
|
-
};
|
|
34
|
-
const admit = () => {
|
|
35
|
-
if (turnId !== null || paused || items.length === 0) return;
|
|
36
|
-
let count = 0;
|
|
37
|
-
for (const item of items) {
|
|
38
|
-
count += 1;
|
|
39
|
-
show(item.message);
|
|
40
|
-
if (item.generate) {
|
|
41
|
-
turnId = item.id;
|
|
42
|
-
starting = true;
|
|
43
|
-
suspended = false;
|
|
44
|
-
break;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
if (count > 0) items = items.slice(count);
|
|
48
|
-
};
|
|
49
|
-
for (const { command } of pending) {
|
|
50
|
-
switch (command.action) {
|
|
51
|
-
case "send":
|
|
52
|
-
case "steer": {
|
|
53
|
-
if (items.some((item) => item.id === command.message.id) || turnId === command.message.id) break;
|
|
54
|
-
const item = {
|
|
55
|
-
...command.action === "steer" ? { afterStepOf: command.turnId } : {},
|
|
56
|
-
id: command.message.id,
|
|
57
|
-
message: command.message,
|
|
58
|
-
generate: command.message.role === "user" && (command.action === "steer" || command.generate !== false)
|
|
59
|
-
};
|
|
60
|
-
items = command.action === "steer" ? [item, ...items] : [...items, item];
|
|
61
|
-
break;
|
|
62
|
-
}
|
|
63
|
-
case "edit":
|
|
64
|
-
items = items.map((item) => item.id === command.message.id && item.message.role === command.message.role ? {
|
|
65
|
-
...item,
|
|
66
|
-
message: command.message
|
|
67
|
-
} : item);
|
|
68
|
-
break;
|
|
69
|
-
case "move":
|
|
70
|
-
items = moveItem({
|
|
71
|
-
items,
|
|
72
|
-
inputId: command.inputId,
|
|
73
|
-
beforeId: command.beforeId
|
|
74
|
-
});
|
|
75
|
-
break;
|
|
76
|
-
case "remove":
|
|
77
|
-
items = items.filter((item) => item.id !== command.inputId);
|
|
78
|
-
break;
|
|
79
|
-
case "send-now": {
|
|
80
|
-
if (turnId !== command.turnId) break;
|
|
81
|
-
const selected = items.find((item) => item.id === command.inputId);
|
|
82
|
-
if (!selected || selected.message.role !== "user") break;
|
|
83
|
-
items = items.map((item) => item.id === selected.id ? {
|
|
84
|
-
...item,
|
|
85
|
-
generate: true,
|
|
86
|
-
...turnId === null ? {} : { afterStepOf: turnId }
|
|
87
|
-
} : item);
|
|
88
|
-
items = moveItem({
|
|
89
|
-
items,
|
|
90
|
-
inputId: command.inputId,
|
|
91
|
-
beforeId: items[0]?.id ?? null
|
|
92
|
-
});
|
|
93
|
-
break;
|
|
94
|
-
}
|
|
95
|
-
case "pause":
|
|
96
|
-
paused = true;
|
|
97
|
-
if (command.when === "now") suspended = true;
|
|
98
|
-
break;
|
|
99
|
-
case "resume":
|
|
100
|
-
paused = false;
|
|
101
|
-
suspended = false;
|
|
102
|
-
}
|
|
103
|
-
admit();
|
|
104
|
-
}
|
|
105
|
-
items = items.filter((item) => {
|
|
106
|
-
if (item.afterStepOf === void 0) return true;
|
|
107
|
-
show(item.message);
|
|
108
|
-
return false;
|
|
109
|
-
});
|
|
110
|
-
return {
|
|
111
|
-
...state,
|
|
112
|
-
messages,
|
|
113
|
-
inbox: {
|
|
114
|
-
items,
|
|
115
|
-
paused
|
|
116
|
-
},
|
|
117
|
-
starting: starting && !suspended
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
//#endregion
|
|
121
|
-
//#region src/ai-control-state.ts
|
|
122
|
-
const container = (value) => typeof value === "object" && value !== null;
|
|
123
|
-
const same = (left, right) => JSON.stringify(left) === JSON.stringify(right);
|
|
124
|
-
const controlChanges = ({ before, after }) => {
|
|
125
|
-
const changes = [];
|
|
126
|
-
const visit = (left, right, path) => {
|
|
127
|
-
if (same(left, right)) return;
|
|
128
|
-
if (Array.isArray(left) && Array.isArray(right)) {
|
|
129
|
-
let prefix = 0;
|
|
130
|
-
while (prefix < Math.min(left.length, right.length) && same(left[prefix], right[prefix])) prefix += 1;
|
|
131
|
-
let suffix = 0;
|
|
132
|
-
while (suffix < Math.min(left.length, right.length) - prefix && same(left[left.length - 1 - suffix], right[right.length - 1 - suffix])) suffix += 1;
|
|
133
|
-
if (left.length === right.length) for (let index = prefix; index < left.length - suffix; index += 1) visit(left[index], right[index], [...path, index]);
|
|
134
|
-
else changes.push({
|
|
135
|
-
path,
|
|
136
|
-
splice: {
|
|
137
|
-
index: prefix,
|
|
138
|
-
deleteCount: left.length - prefix - suffix,
|
|
139
|
-
items: right.slice(prefix, right.length - suffix)
|
|
140
|
-
}
|
|
141
|
-
});
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
if (container(left) && container(right) && !Array.isArray(left) && !Array.isArray(right)) {
|
|
145
|
-
if (Object.keys(right).length === 0) {
|
|
146
|
-
changes.push({
|
|
147
|
-
path,
|
|
148
|
-
value: right
|
|
149
|
-
});
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
for (const key of Object.keys(left)) if (!Object.hasOwn(right, key)) changes.push({
|
|
153
|
-
path: [...path, key],
|
|
154
|
-
remove: true
|
|
155
|
-
});
|
|
156
|
-
for (const key of Object.keys(right)) visit(Object.hasOwn(left, key) ? left[key] : void 0, right[key], [...path, key]);
|
|
157
|
-
return;
|
|
158
|
-
}
|
|
159
|
-
changes.push({
|
|
160
|
-
path,
|
|
161
|
-
value: right
|
|
162
|
-
});
|
|
163
|
-
};
|
|
164
|
-
visit(before, after, []);
|
|
165
|
-
return changes;
|
|
166
|
-
};
|
|
167
|
-
const applyControlChanges = ({ state, changes }) => {
|
|
168
|
-
if (changes.length === 0) return state;
|
|
169
|
-
let next = structuredClone(state);
|
|
170
|
-
for (const change of changes) {
|
|
171
|
-
const path = change.path;
|
|
172
|
-
if (path.length === 0) {
|
|
173
|
-
next = structuredClone(change.value);
|
|
174
|
-
continue;
|
|
175
|
-
}
|
|
176
|
-
let target = next;
|
|
177
|
-
for (const key of path.slice(0, -1)) {
|
|
178
|
-
if (!container(target) || !Object.hasOwn(target, key)) throw new TypeError("Invalid AI control change path");
|
|
179
|
-
target = target[key];
|
|
180
|
-
}
|
|
181
|
-
if (!container(target)) throw new TypeError("Invalid AI control change target");
|
|
182
|
-
const parent = target;
|
|
183
|
-
const key = path.at(-1);
|
|
184
|
-
if (change.remove) delete parent[key];
|
|
185
|
-
else if (change.splice) {
|
|
186
|
-
const items = parent[key];
|
|
187
|
-
Object.defineProperty(parent, key, {
|
|
188
|
-
value: [
|
|
189
|
-
...items.slice(0, change.splice.index),
|
|
190
|
-
...structuredClone(change.splice.items),
|
|
191
|
-
...items.slice(change.splice.index + change.splice.deleteCount)
|
|
192
|
-
],
|
|
193
|
-
enumerable: true,
|
|
194
|
-
configurable: true,
|
|
195
|
-
writable: true
|
|
196
|
-
});
|
|
197
|
-
} else if (change.move) {
|
|
198
|
-
const items = parent[key];
|
|
199
|
-
const [item] = items.splice(change.move.from, 1);
|
|
200
|
-
items.splice(change.move.to, 0, item);
|
|
201
|
-
} else Object.defineProperty(parent, key, {
|
|
202
|
-
value: structuredClone(change.value),
|
|
203
|
-
enumerable: true,
|
|
204
|
-
configurable: true,
|
|
205
|
-
writable: true
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
return next;
|
|
209
|
-
};
|
|
210
|
-
const controlCommit = (options) => {
|
|
211
|
-
const active = options.after.active;
|
|
212
|
-
const response = options.after.coordinator.response;
|
|
213
|
-
return {
|
|
214
|
-
changes: [...options.moves, ...controlChanges({
|
|
215
|
-
before: options.before,
|
|
216
|
-
after: options.after
|
|
217
|
-
})],
|
|
218
|
-
view: {
|
|
219
|
-
turnId: active?.turnId ?? null,
|
|
220
|
-
version: active?.version ?? null,
|
|
221
|
-
phase: active?.phase ?? null,
|
|
222
|
-
paused: options.after.inbox.paused,
|
|
223
|
-
closed: options.after.coordinator.closed,
|
|
224
|
-
requestId: response?.activeRequestId ?? null,
|
|
225
|
-
requestReason: response?.requestReason ?? null,
|
|
226
|
-
sourceGenerationId: response?.sourceGenerationId ?? null,
|
|
227
|
-
responseMessageId: response?.responseMessageId ?? null
|
|
228
|
-
}
|
|
229
|
-
};
|
|
230
|
-
};
|
|
231
|
-
//#endregion
|
|
232
|
-
//#region src/ai-control.ts
|
|
233
|
-
const initialControlState = () => ({
|
|
234
|
-
inbox: {
|
|
235
|
-
paused: false,
|
|
236
|
-
items: []
|
|
237
|
-
},
|
|
238
|
-
active: null,
|
|
239
|
-
coordinator: {
|
|
240
|
-
closed: false,
|
|
241
|
-
queued: []
|
|
242
|
-
}
|
|
243
|
-
});
|
|
244
|
-
//#endregion
|
|
245
|
-
//#region src/ai-id.ts
|
|
246
|
-
const AI_EVENT_ID_PREFIX = "a2.ai:";
|
|
247
|
-
const aiEventId = async (...parts) => `${AI_EVENT_ID_PREFIX}${await idempotentId(...parts)}`;
|
|
248
|
-
//#endregion
|
|
249
6
|
//#region src/parse-partial-json.ts
|
|
250
7
|
/*!
|
|
251
8
|
* Adapted from AI SDK 7.0.58, packages/ai/src/util/fix-json.ts.
|
|
@@ -1158,20 +915,325 @@ const advanceProjection = ({ projection, base, chunks, index }) => {
|
|
|
1158
915
|
}
|
|
1159
916
|
};
|
|
1160
917
|
};
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
const
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
918
|
+
const projectedTools = (projection) => projection.toolEvents.reduce((tools, event) => reduceToolActivity(tools, event), projection.baseTools);
|
|
919
|
+
const restoreGenerationBase = (state, generationId, responseMessageId, reason) => {
|
|
920
|
+
const projection = state.activeProjection?.generationId === generationId ? state.activeProjection : null;
|
|
921
|
+
return {
|
|
922
|
+
...state,
|
|
923
|
+
messages: projection?.baseMessage === void 0 ? state.messages.filter((message) => message.id !== responseMessageId) : upsertMessage(state.messages, projection.baseMessage),
|
|
924
|
+
tools: projection === null ? state.tools.filter((activity) => activity.generationId !== generationId) : projection.baseTools,
|
|
925
|
+
pendingApprovals: state.pendingApprovals.filter((approval) => approval.generationId !== generationId),
|
|
926
|
+
pendingInputs: state.pendingInputs.filter((input) => input.generationId !== generationId),
|
|
927
|
+
activeGeneration: null,
|
|
928
|
+
activeProjection: null,
|
|
929
|
+
terminalGenerations: {
|
|
930
|
+
...state.terminalGenerations,
|
|
931
|
+
[generationId]: reason
|
|
932
|
+
},
|
|
933
|
+
compaction: projection === null ? state.compaction?.generationId === generationId ? null : state.compaction : projection.baseCompaction ?? null
|
|
934
|
+
};
|
|
935
|
+
};
|
|
936
|
+
const stopGenerationTools = (tools, generationId, responseMessageId, error) => tools.map((activity) => activity.status === "running" && (generationId === void 0 ? activity.messageId === responseMessageId : activity.generationId === generationId) ? {
|
|
937
|
+
...activity,
|
|
938
|
+
status: "failed",
|
|
939
|
+
error,
|
|
940
|
+
preliminary: false
|
|
941
|
+
} : activity);
|
|
942
|
+
const interruptGenerationProjection = (state, generationId, responseMessageId, cutoff) => {
|
|
943
|
+
const projection = state.activeProjection?.generationId === generationId ? state.activeProjection : null;
|
|
944
|
+
if (projection === null) return null;
|
|
945
|
+
const retained = {
|
|
946
|
+
...projection,
|
|
947
|
+
batches: progressBatches(flattenProgressBatches(projection.batches).filter((batch) => batch.index <= cutoff)),
|
|
948
|
+
toolEvents: projection.toolEvents.filter((toolEvent) => toolEvent.index <= cutoff),
|
|
949
|
+
approvalEvents: projection.approvalEvents.filter((approvalEvent) => approvalEvent.index <= cutoff)
|
|
950
|
+
};
|
|
951
|
+
const shouldProject = retained.baseMessage !== void 0 || retained.batches.length > 0;
|
|
952
|
+
const restored = restoreGenerationBase(state, generationId, responseMessageId, "interrupted");
|
|
953
|
+
return {
|
|
954
|
+
...restored,
|
|
955
|
+
messages: shouldProject ? upsertMessage(state.messages, interruptUIMessage(projectedMessage(retained))) : restored.messages,
|
|
956
|
+
tools: stopGenerationTools(projectedTools(retained), generationId, responseMessageId, "Tool execution was interrupted."),
|
|
957
|
+
usage: projection.completionIndex === void 0 || projection.completionIndex <= cutoff ? restored.usage : restored.usage.filter((entry) => entry.generationId !== generationId)
|
|
958
|
+
};
|
|
959
|
+
};
|
|
960
|
+
//#endregion
|
|
961
|
+
//#region src/ai-client-state.ts
|
|
962
|
+
const isQueueCommand = (command) => command.action === "send" || command.action === "edit" || command.action === "move" || command.action === "remove" || command.action === "send-now" || command.action === "steer" || command.action === "pause" || command.action === "resume" || command.action === "stop";
|
|
963
|
+
const moveItem = ({ items, inputId, beforeId }) => {
|
|
964
|
+
const from = items.findIndex((item) => item.id === inputId);
|
|
965
|
+
if (from < 0 || inputId === beforeId || beforeId !== null && !items.some((item) => item.id === beforeId)) return items;
|
|
966
|
+
const next = [...items];
|
|
967
|
+
const [item] = next.splice(from, 1);
|
|
968
|
+
const to = beforeId === null ? next.length : next.findIndex((entry) => entry.id === beforeId);
|
|
969
|
+
next.splice(to, 0, item);
|
|
970
|
+
return next;
|
|
971
|
+
};
|
|
972
|
+
function projectAIClientState(state) {
|
|
973
|
+
const pending = state.pendingQueueCommands ?? [];
|
|
974
|
+
if (state.status === "closed") return state;
|
|
975
|
+
if (pending.length === 0 && !state.inbox.items.some((item) => item.afterStepOf !== void 0)) return state;
|
|
976
|
+
let messages = state.messages;
|
|
977
|
+
let { items, paused } = state.inbox;
|
|
978
|
+
let turnId = state.active?.turnId ?? null;
|
|
979
|
+
let starting = false;
|
|
980
|
+
let suspended = state.active?.phase === "paused" || state.active?.phase === "pausing";
|
|
981
|
+
let messageIds;
|
|
982
|
+
const show = (message) => {
|
|
983
|
+
messageIds ??= new Set(messages.map((item) => item.id));
|
|
984
|
+
if (messageIds.has(message.id)) return;
|
|
985
|
+
if (messages === state.messages) messages = state.messages.slice();
|
|
986
|
+
messages.push(message);
|
|
987
|
+
messageIds.add(message.id);
|
|
988
|
+
};
|
|
989
|
+
const admit = () => {
|
|
990
|
+
if (turnId !== null || paused || items.length === 0) return;
|
|
991
|
+
let count = 0;
|
|
992
|
+
for (const item of items) {
|
|
993
|
+
count += 1;
|
|
994
|
+
show(item.message);
|
|
995
|
+
if (item.generate) {
|
|
996
|
+
turnId = item.id;
|
|
997
|
+
starting = true;
|
|
998
|
+
suspended = false;
|
|
999
|
+
break;
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
if (count > 0) items = items.slice(count);
|
|
1003
|
+
};
|
|
1004
|
+
for (const { command } of pending) {
|
|
1005
|
+
switch (command.action) {
|
|
1006
|
+
case "send":
|
|
1007
|
+
case "steer": {
|
|
1008
|
+
if (items.some((item) => item.id === command.message.id) || turnId === command.message.id) break;
|
|
1009
|
+
const item = {
|
|
1010
|
+
...command.action === "steer" ? { afterStepOf: command.turnId } : {},
|
|
1011
|
+
id: command.message.id,
|
|
1012
|
+
message: command.message,
|
|
1013
|
+
generate: command.message.role === "user" && (command.action === "steer" || command.generate !== false)
|
|
1014
|
+
};
|
|
1015
|
+
items = command.action === "steer" ? [item, ...items] : [...items, item];
|
|
1016
|
+
break;
|
|
1017
|
+
}
|
|
1018
|
+
case "edit":
|
|
1019
|
+
items = items.map((item) => item.id === command.message.id && item.message.role === command.message.role ? {
|
|
1020
|
+
...item,
|
|
1021
|
+
message: command.message
|
|
1022
|
+
} : item);
|
|
1023
|
+
break;
|
|
1024
|
+
case "move":
|
|
1025
|
+
items = moveItem({
|
|
1026
|
+
items,
|
|
1027
|
+
inputId: command.inputId,
|
|
1028
|
+
beforeId: command.beforeId
|
|
1029
|
+
});
|
|
1030
|
+
break;
|
|
1031
|
+
case "remove":
|
|
1032
|
+
items = items.filter((item) => item.id !== command.inputId);
|
|
1033
|
+
break;
|
|
1034
|
+
case "send-now": {
|
|
1035
|
+
if (turnId !== command.turnId) break;
|
|
1036
|
+
const selected = items.find((item) => item.id === command.inputId);
|
|
1037
|
+
if (!selected || selected.message.role !== "user") break;
|
|
1038
|
+
items = items.map((item) => item.id === selected.id ? {
|
|
1039
|
+
...item,
|
|
1040
|
+
generate: true,
|
|
1041
|
+
...turnId === null ? {} : { afterStepOf: turnId }
|
|
1042
|
+
} : item);
|
|
1043
|
+
items = moveItem({
|
|
1044
|
+
items,
|
|
1045
|
+
inputId: command.inputId,
|
|
1046
|
+
beforeId: items[0]?.id ?? null
|
|
1047
|
+
});
|
|
1048
|
+
break;
|
|
1049
|
+
}
|
|
1050
|
+
case "pause":
|
|
1051
|
+
paused = true;
|
|
1052
|
+
if (command.when === "now") suspended = true;
|
|
1053
|
+
break;
|
|
1054
|
+
case "resume":
|
|
1055
|
+
paused = false;
|
|
1056
|
+
suspended = false;
|
|
1057
|
+
break;
|
|
1058
|
+
case "stop": continue;
|
|
1059
|
+
}
|
|
1060
|
+
admit();
|
|
1061
|
+
}
|
|
1062
|
+
items = items.filter((item) => {
|
|
1063
|
+
if (item.afterStepOf === void 0) return true;
|
|
1064
|
+
show(item.message);
|
|
1065
|
+
return false;
|
|
1066
|
+
});
|
|
1067
|
+
const displayed = {
|
|
1068
|
+
...state,
|
|
1069
|
+
messages,
|
|
1070
|
+
inbox: {
|
|
1071
|
+
items,
|
|
1072
|
+
paused
|
|
1073
|
+
},
|
|
1074
|
+
starting: starting && !suspended
|
|
1075
|
+
};
|
|
1076
|
+
const stop = pending.find(({ command }) => command.action === "stop" && command.turnId === state.active?.turnId)?.command;
|
|
1077
|
+
if (stop?.action !== "stop") return displayed;
|
|
1078
|
+
const projection = state.activeProjection;
|
|
1079
|
+
return {
|
|
1080
|
+
...projection === null ? displayed : interruptGenerationProjection(displayed, projection.generationId, projection.responseMessageId, stop.lastSeenIndex ?? Number.POSITIVE_INFINITY) ?? displayed,
|
|
1081
|
+
active: null,
|
|
1082
|
+
status: paused ? "paused" : "idle",
|
|
1083
|
+
starting: false,
|
|
1084
|
+
activeGeneration: null,
|
|
1085
|
+
activeRequestId: null,
|
|
1086
|
+
activeRequestReason: null,
|
|
1087
|
+
activeRequestSourceGenerationId: null,
|
|
1088
|
+
activeResponseMessageId: null,
|
|
1089
|
+
pendingApprovals: [],
|
|
1090
|
+
pendingInputs: [],
|
|
1091
|
+
error: null
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
1094
|
+
//#endregion
|
|
1095
|
+
//#region src/ai-control-state.ts
|
|
1096
|
+
const container = (value) => typeof value === "object" && value !== null;
|
|
1097
|
+
const same = (left, right) => JSON.stringify(left) === JSON.stringify(right);
|
|
1098
|
+
const controlChanges = ({ before, after }) => {
|
|
1099
|
+
const changes = [];
|
|
1100
|
+
const visit = (left, right, path) => {
|
|
1101
|
+
if (same(left, right)) return;
|
|
1102
|
+
if (Array.isArray(left) && Array.isArray(right)) {
|
|
1103
|
+
let prefix = 0;
|
|
1104
|
+
while (prefix < Math.min(left.length, right.length) && same(left[prefix], right[prefix])) prefix += 1;
|
|
1105
|
+
let suffix = 0;
|
|
1106
|
+
while (suffix < Math.min(left.length, right.length) - prefix && same(left[left.length - 1 - suffix], right[right.length - 1 - suffix])) suffix += 1;
|
|
1107
|
+
if (left.length === right.length) for (let index = prefix; index < left.length - suffix; index += 1) visit(left[index], right[index], [...path, index]);
|
|
1108
|
+
else changes.push({
|
|
1109
|
+
path,
|
|
1110
|
+
splice: {
|
|
1111
|
+
index: prefix,
|
|
1112
|
+
deleteCount: left.length - prefix - suffix,
|
|
1113
|
+
items: right.slice(prefix, right.length - suffix)
|
|
1114
|
+
}
|
|
1115
|
+
});
|
|
1116
|
+
return;
|
|
1117
|
+
}
|
|
1118
|
+
if (container(left) && container(right) && !Array.isArray(left) && !Array.isArray(right)) {
|
|
1119
|
+
if (Object.keys(right).length === 0) {
|
|
1120
|
+
changes.push({
|
|
1121
|
+
path,
|
|
1122
|
+
value: right
|
|
1123
|
+
});
|
|
1124
|
+
return;
|
|
1125
|
+
}
|
|
1126
|
+
for (const key of Object.keys(left)) if (!Object.hasOwn(right, key)) changes.push({
|
|
1127
|
+
path: [...path, key],
|
|
1128
|
+
remove: true
|
|
1129
|
+
});
|
|
1130
|
+
for (const key of Object.keys(right)) visit(Object.hasOwn(left, key) ? left[key] : void 0, right[key], [...path, key]);
|
|
1131
|
+
return;
|
|
1132
|
+
}
|
|
1133
|
+
changes.push({
|
|
1134
|
+
path,
|
|
1135
|
+
value: right
|
|
1136
|
+
});
|
|
1137
|
+
};
|
|
1138
|
+
visit(before, after, []);
|
|
1139
|
+
return changes;
|
|
1140
|
+
};
|
|
1141
|
+
const applyControlChanges = ({ state, changes }) => {
|
|
1142
|
+
if (changes.length === 0) return state;
|
|
1143
|
+
let next = structuredClone(state);
|
|
1144
|
+
for (const change of changes) {
|
|
1145
|
+
const path = change.path;
|
|
1146
|
+
if (path.length === 0) {
|
|
1147
|
+
next = structuredClone(change.value);
|
|
1148
|
+
continue;
|
|
1149
|
+
}
|
|
1150
|
+
let target = next;
|
|
1151
|
+
for (const key of path.slice(0, -1)) {
|
|
1152
|
+
if (!container(target) || !Object.hasOwn(target, key)) throw new TypeError("Invalid AI control change path");
|
|
1153
|
+
target = target[key];
|
|
1154
|
+
}
|
|
1155
|
+
if (!container(target)) throw new TypeError("Invalid AI control change target");
|
|
1156
|
+
const parent = target;
|
|
1157
|
+
const key = path.at(-1);
|
|
1158
|
+
if (change.remove) delete parent[key];
|
|
1159
|
+
else if (change.splice) {
|
|
1160
|
+
const items = parent[key];
|
|
1161
|
+
Object.defineProperty(parent, key, {
|
|
1162
|
+
value: [
|
|
1163
|
+
...items.slice(0, change.splice.index),
|
|
1164
|
+
...structuredClone(change.splice.items),
|
|
1165
|
+
...items.slice(change.splice.index + change.splice.deleteCount)
|
|
1166
|
+
],
|
|
1167
|
+
enumerable: true,
|
|
1168
|
+
configurable: true,
|
|
1169
|
+
writable: true
|
|
1170
|
+
});
|
|
1171
|
+
} else if (change.move) {
|
|
1172
|
+
const items = parent[key];
|
|
1173
|
+
const [item] = items.splice(change.move.from, 1);
|
|
1174
|
+
items.splice(change.move.to, 0, item);
|
|
1175
|
+
} else Object.defineProperty(parent, key, {
|
|
1176
|
+
value: structuredClone(change.value),
|
|
1177
|
+
enumerable: true,
|
|
1178
|
+
configurable: true,
|
|
1179
|
+
writable: true
|
|
1180
|
+
});
|
|
1181
|
+
}
|
|
1182
|
+
return next;
|
|
1183
|
+
};
|
|
1184
|
+
const controlCommit = (options) => {
|
|
1185
|
+
const active = options.after.active;
|
|
1186
|
+
const response = options.after.coordinator.response;
|
|
1187
|
+
return {
|
|
1188
|
+
changes: [...options.moves, ...controlChanges({
|
|
1189
|
+
before: options.before,
|
|
1190
|
+
after: options.after
|
|
1191
|
+
})],
|
|
1192
|
+
view: {
|
|
1193
|
+
turnId: active?.turnId ?? null,
|
|
1194
|
+
version: active?.version ?? null,
|
|
1195
|
+
phase: active?.phase ?? null,
|
|
1196
|
+
paused: options.after.inbox.paused,
|
|
1197
|
+
closed: options.after.coordinator.closed,
|
|
1198
|
+
requestId: response?.activeRequestId ?? null,
|
|
1199
|
+
requestReason: response?.requestReason ?? null,
|
|
1200
|
+
sourceGenerationId: response?.sourceGenerationId ?? null,
|
|
1201
|
+
responseMessageId: response?.responseMessageId ?? null
|
|
1202
|
+
}
|
|
1203
|
+
};
|
|
1204
|
+
};
|
|
1205
|
+
//#endregion
|
|
1206
|
+
//#region src/ai-control.ts
|
|
1207
|
+
const initialControlState = () => ({
|
|
1208
|
+
pendingStops: {},
|
|
1209
|
+
inbox: {
|
|
1210
|
+
paused: false,
|
|
1211
|
+
items: []
|
|
1212
|
+
},
|
|
1213
|
+
active: null,
|
|
1214
|
+
coordinator: {
|
|
1215
|
+
closed: false,
|
|
1216
|
+
queued: []
|
|
1217
|
+
}
|
|
1218
|
+
});
|
|
1219
|
+
//#endregion
|
|
1220
|
+
//#region src/ai-id.ts
|
|
1221
|
+
const AI_EVENT_ID_PREFIX = "a2.ai:";
|
|
1222
|
+
const aiEventId = async (...parts) => `${AI_EVENT_ID_PREFIX}${await idempotentId(...parts)}`;
|
|
1223
|
+
//#endregion
|
|
1224
|
+
//#region src/ai-stored-state.ts
|
|
1225
|
+
const aiStateReducer = Symbol("a2.ai.stateReducer");
|
|
1226
|
+
const isRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1227
|
+
const hasKeys = (value, { required, optional = [] }) => isRecord$1(value) && required.every((key) => Object.hasOwn(value, key)) && Object.keys(value).every((key) => required.includes(key) || optional.includes(key));
|
|
1228
|
+
const reference = (value) => {
|
|
1229
|
+
if (!isRecord$1(value) || !Object.hasOwn(value, "$a2Value")) return void 0;
|
|
1230
|
+
const id = value["$a2Value"];
|
|
1231
|
+
if (typeof id !== "number" || !Number.isSafeInteger(id) || id < 0 || Object.keys(value).length !== 1) throw new TypeError("invalid AI payload reference");
|
|
1232
|
+
return id;
|
|
1233
|
+
};
|
|
1234
|
+
const payloadFields = /* @__PURE__ */ new Set([
|
|
1235
|
+
"input",
|
|
1236
|
+
"output",
|
|
1175
1237
|
"rawInput",
|
|
1176
1238
|
"metadata",
|
|
1177
1239
|
"providerMetadata",
|
|
@@ -1634,48 +1696,6 @@ const projectedResponse = (options) => {
|
|
|
1634
1696
|
toolCallIds: new Set(options.state.tools.map((tool) => tool.toolCallId))
|
|
1635
1697
|
}) : message;
|
|
1636
1698
|
};
|
|
1637
|
-
const projectedTools = (projection) => projection.toolEvents.reduce((tools, event) => reduceToolActivity(tools, event), projection.baseTools);
|
|
1638
|
-
const restoreGenerationBase = (state, generationId, responseMessageId, reason) => {
|
|
1639
|
-
const projection = state.activeProjection?.generationId === generationId ? state.activeProjection : null;
|
|
1640
|
-
return {
|
|
1641
|
-
...state,
|
|
1642
|
-
messages: projection?.baseMessage === void 0 ? state.messages.filter((message) => message.id !== responseMessageId) : upsertMessage(state.messages, projection.baseMessage),
|
|
1643
|
-
tools: projection === null ? state.tools.filter((activity) => activity.generationId !== generationId) : projection.baseTools,
|
|
1644
|
-
pendingApprovals: state.pendingApprovals.filter((approval) => approval.generationId !== generationId),
|
|
1645
|
-
pendingInputs: state.pendingInputs.filter((input) => input.generationId !== generationId),
|
|
1646
|
-
activeGeneration: null,
|
|
1647
|
-
activeProjection: null,
|
|
1648
|
-
terminalGenerations: {
|
|
1649
|
-
...state.terminalGenerations,
|
|
1650
|
-
[generationId]: reason
|
|
1651
|
-
},
|
|
1652
|
-
compaction: projection === null ? state.compaction?.generationId === generationId ? null : state.compaction : projection.baseCompaction ?? null
|
|
1653
|
-
};
|
|
1654
|
-
};
|
|
1655
|
-
const stopGenerationTools = (tools, generationId, responseMessageId, error) => tools.map((activity) => activity.status === "running" && (generationId === void 0 ? activity.messageId === responseMessageId : activity.generationId === generationId) ? {
|
|
1656
|
-
...activity,
|
|
1657
|
-
status: "failed",
|
|
1658
|
-
error,
|
|
1659
|
-
preliminary: false
|
|
1660
|
-
} : activity);
|
|
1661
|
-
const interruptGenerationProjection = (state, generationId, responseMessageId, cutoff) => {
|
|
1662
|
-
const projection = state.activeProjection?.generationId === generationId ? state.activeProjection : null;
|
|
1663
|
-
if (projection === null) return null;
|
|
1664
|
-
const retained = {
|
|
1665
|
-
...projection,
|
|
1666
|
-
batches: progressBatches(flattenProgressBatches(projection.batches).filter((batch) => batch.index <= cutoff)),
|
|
1667
|
-
toolEvents: projection.toolEvents.filter((toolEvent) => toolEvent.index <= cutoff),
|
|
1668
|
-
approvalEvents: projection.approvalEvents.filter((approvalEvent) => approvalEvent.index <= cutoff)
|
|
1669
|
-
};
|
|
1670
|
-
const shouldProject = retained.baseMessage !== void 0 || retained.batches.length > 0;
|
|
1671
|
-
const restored = restoreGenerationBase(state, generationId, responseMessageId, "interrupted");
|
|
1672
|
-
return {
|
|
1673
|
-
...restored,
|
|
1674
|
-
messages: shouldProject ? upsertMessage(state.messages, interruptUIMessage(projectedMessage(retained))) : restored.messages,
|
|
1675
|
-
tools: stopGenerationTools(projectedTools(retained), generationId, responseMessageId, "Tool execution was interrupted."),
|
|
1676
|
-
usage: projection.completionIndex === void 0 || projection.completionIndex <= cutoff ? restored.usage : restored.usage.filter((entry) => entry.generationId !== generationId)
|
|
1677
|
-
};
|
|
1678
|
-
};
|
|
1679
1699
|
const terminalGenerationReason = (state, generationId, requestId) => {
|
|
1680
1700
|
const terminal = state.terminalGenerations?.[generationId];
|
|
1681
1701
|
if (terminal !== void 0) return terminal;
|
|
@@ -2278,7 +2298,7 @@ function deriveUIMessages(history) {
|
|
|
2278
2298
|
/** Build the standard AI state projection for any contract containing the protocol. */
|
|
2279
2299
|
function createReducer(options) {
|
|
2280
2300
|
const reducer = options.contract.reducer({
|
|
2281
|
-
name: options.name ?? "a2.ai.state.
|
|
2301
|
+
name: options.name ?? "a2.ai.state.v22",
|
|
2282
2302
|
initialState: initialState(),
|
|
2283
2303
|
...options.stateSchema === void 0 ? {} : { stateSchema: options.stateSchema }
|
|
2284
2304
|
}).fold(options.fold ?? ((state, event) => reduceAIState(state, event)));
|
|
@@ -2414,7 +2434,7 @@ function agent(options) {
|
|
|
2414
2434
|
}
|
|
2415
2435
|
});
|
|
2416
2436
|
const reducer = agentContract.reducer({
|
|
2417
|
-
name: options.reducerName ?? "a2.ai.state.
|
|
2437
|
+
name: options.reducerName ?? "a2.ai.state.v22",
|
|
2418
2438
|
initialState: initialState()
|
|
2419
2439
|
}).fold((state, event) => reduceAIState(state, event));
|
|
2420
2440
|
return {
|
|
@@ -2426,6 +2446,6 @@ function agent(options) {
|
|
|
2426
2446
|
};
|
|
2427
2447
|
}
|
|
2428
2448
|
//#endregion
|
|
2429
|
-
export {
|
|
2449
|
+
export { interruptUIMessage as C, progressBatches as S, projectedMessage as _, events as a, upsertResponse as b, reduceAIState as c, AI_EVENT_ID_PREFIX as d, aiEventId as f, advanceProjection as g, controlCommit as h, deriveUIMessages as i, aiStateReducer as l, applyControlChanges as m, createEvents as n, handlerContext as o, initialControlState as p, createReducer as r, inputs as s, agent as t, storedAIReducer as u, reduceToolActivity as v, pauseUIMessage as w, flattenProgressBatches as x, upsertMessage as y };
|
|
2430
2450
|
|
|
2431
|
-
//# sourceMappingURL=ai-
|
|
2451
|
+
//# sourceMappingURL=ai-DgOBltJ_.js.map
|