experimental-a2 0.0.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 +128 -0
- package/dist/ai-server.browser.d.ts +1 -0
- package/dist/ai-server.browser.js +4 -0
- package/dist/ai-server.d.ts +65 -0
- package/dist/ai-server.js +494 -0
- package/dist/ai.d.ts +282 -0
- package/dist/ai.js +922 -0
- package/dist/cache-indexeddb.d.ts +1 -0
- package/dist/cache-indexeddb.js +0 -0
- package/dist/client.d.ts +90 -0
- package/dist/client.js +410 -0
- package/dist/contract-B0kAXoaL.js +60 -0
- package/dist/contract-DL8btVd9.d.ts +161 -0
- package/dist/devtools-server.browser.d.ts +1 -0
- package/dist/devtools-server.browser.js +4 -0
- package/dist/devtools-server.d.ts +22 -0
- package/dist/devtools-server.js +1087 -0
- package/dist/errors-BJRMd-h6.js +23 -0
- package/dist/errors-xL_JTXsY.d.ts +20 -0
- package/dist/http.d.ts +44 -0
- package/dist/http.js +119 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +3 -0
- package/dist/inspection-E7qbD0Xj.js +10 -0
- package/dist/internal-Dm8Ejnud.js +36 -0
- package/dist/log-Dg1I8NRr.d.ts +245 -0
- package/dist/log-memory.d.ts +11 -0
- package/dist/log-memory.js +345 -0
- package/dist/log-polling-RO7kclzR.js +83 -0
- package/dist/log-postgres.d.ts +40 -0
- package/dist/log-postgres.js +628 -0
- package/dist/log-redis.d.ts +31 -0
- package/dist/log-redis.js +711 -0
- package/dist/log-sqlite.d.ts +17 -0
- package/dist/log-sqlite.js +450 -0
- package/dist/log-yJbXUf72.js +5 -0
- package/dist/otel.d.ts +12 -0
- package/dist/otel.js +41 -0
- package/dist/react.d.ts +54 -0
- package/dist/react.js +85 -0
- package/dist/recovery-vercel.d.ts +60 -0
- package/dist/recovery-vercel.js +120 -0
- package/dist/retryable-lazy-DZWmHpii.js +19 -0
- package/dist/server-DYsnKTTy.js +780 -0
- package/dist/server.browser.d.ts +1 -0
- package/dist/server.browser.js +11 -0
- package/dist/server.d.ts +136 -0
- package/dist/server.js +2 -0
- package/dist/telemetry-C78al20p.d.ts +32 -0
- package/dist/validate-XKT4FSNn.js +28 -0
- package/dist/wire-2QpU1EtJ.js +62 -0
- package/docs/01-quickstart.mdx +214 -0
- package/docs/concepts/01-contracts.mdx +138 -0
- package/docs/concepts/02-handlers.mdx +146 -0
- package/docs/concepts/03-durability.mdx +230 -0
- package/docs/concepts/04-state.mdx +133 -0
- package/docs/guides/01-timers.mdx +85 -0
- package/docs/guides/02-cancellation.mdx +107 -0
- package/docs/guides/03-react.mdx +234 -0
- package/docs/guides/04-local-first.mdx +88 -0
- package/docs/guides/05-production.mdx +179 -0
- package/docs/guides/06-ai-agents.mdx +659 -0
- package/docs/guides/07-devtools.mdx +101 -0
- package/docs/guides/08-application-data.mdx +114 -0
- package/docs/index.mdx +282 -0
- package/docs/reference/01-api.mdx +637 -0
- package/docs/reference/02-errors.mdx +77 -0
- package/package.json +111 -0
package/dist/ai.js
ADDED
|
@@ -0,0 +1,922 @@
|
|
|
1
|
+
import { t as assertSyncSchema } from "./validate-XKT4FSNn.js";
|
|
2
|
+
import { t as contract } from "./contract-B0kAXoaL.js";
|
|
3
|
+
//#region src/ai-projector.ts
|
|
4
|
+
const isRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5
|
+
const cloneMessage = (messageId, base) => structuredClone(base ?? {
|
|
6
|
+
id: messageId,
|
|
7
|
+
role: "assistant",
|
|
8
|
+
parts: []
|
|
9
|
+
});
|
|
10
|
+
const currentStepParts = (parts) => {
|
|
11
|
+
let start = parts.length - 1;
|
|
12
|
+
while (start >= 0 && parts[start]?.["type"] !== "step-start") start -= 1;
|
|
13
|
+
return parts.slice(start + 1);
|
|
14
|
+
};
|
|
15
|
+
const isToolPart = (part) => typeof part["toolCallId"] === "string" && (part["type"] === "dynamic-tool" || typeof part["type"] === "string" && part["type"].startsWith("tool-"));
|
|
16
|
+
const findToolPart = (parts, toolCallId) => {
|
|
17
|
+
const current = currentStepParts(parts).find((part) => isToolPart(part) && part["toolCallId"] === toolCallId);
|
|
18
|
+
if (current) return current;
|
|
19
|
+
return parts.findLast((part) => isToolPart(part) && part["toolCallId"] === toolCallId);
|
|
20
|
+
};
|
|
21
|
+
const findApprovalPart = (parts, approvalId) => parts.find((part) => {
|
|
22
|
+
const approval = part["approval"];
|
|
23
|
+
return isToolPart(part) && isRecord$1(approval) && approval["id"] === approvalId;
|
|
24
|
+
});
|
|
25
|
+
const setOptional = (target, key, value) => {
|
|
26
|
+
if (value !== void 0) target[key] = value;
|
|
27
|
+
};
|
|
28
|
+
const setToolPart = (options) => {
|
|
29
|
+
let part = findToolPart(options.parts, options.toolCallId);
|
|
30
|
+
if (!part) {
|
|
31
|
+
part = {
|
|
32
|
+
type: options.dynamic ? "dynamic-tool" : `tool-${options.toolName}`,
|
|
33
|
+
...options.dynamic ? { toolName: options.toolName } : {},
|
|
34
|
+
toolCallId: options.toolCallId
|
|
35
|
+
};
|
|
36
|
+
options.parts.push(part);
|
|
37
|
+
}
|
|
38
|
+
part["state"] = options.state;
|
|
39
|
+
part["input"] = options.input;
|
|
40
|
+
delete part["output"];
|
|
41
|
+
delete part["rawInput"];
|
|
42
|
+
delete part["errorText"];
|
|
43
|
+
delete part["preliminary"];
|
|
44
|
+
setOptional(part, "rawInput", options.rawInput);
|
|
45
|
+
setOptional(part, "output", options.output);
|
|
46
|
+
setOptional(part, "errorText", options.errorText);
|
|
47
|
+
setOptional(part, "preliminary", options.preliminary);
|
|
48
|
+
setOptional(part, "providerExecuted", options.providerExecuted);
|
|
49
|
+
setOptional(part, "title", options.title);
|
|
50
|
+
setOptional(part, "toolMetadata", options.toolMetadata);
|
|
51
|
+
if (options.dynamic) part["toolName"] = options.toolName;
|
|
52
|
+
if (options.providerMetadata !== void 0) part[options.state === "output-available" || options.state === "output-error" ? "resultProviderMetadata" : "callProviderMetadata"] = options.providerMetadata;
|
|
53
|
+
return part;
|
|
54
|
+
};
|
|
55
|
+
const mergeMetadata = (current, update) => {
|
|
56
|
+
if (update === void 0 || update === null) return current;
|
|
57
|
+
return isRecord$1(current) && isRecord$1(update) ? {
|
|
58
|
+
...current,
|
|
59
|
+
...update
|
|
60
|
+
} : update;
|
|
61
|
+
};
|
|
62
|
+
const parseCompleteJSON = (value) => {
|
|
63
|
+
try {
|
|
64
|
+
return JSON.parse(value);
|
|
65
|
+
} catch {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
/** Internal synchronous counterpart of the AI SDK UI message stream fold. */
|
|
70
|
+
function projectUIMessage(options) {
|
|
71
|
+
const message = cloneMessage(options.messageId, options.base);
|
|
72
|
+
const record = message;
|
|
73
|
+
const parts = message.parts;
|
|
74
|
+
const activeText = /* @__PURE__ */ new Map();
|
|
75
|
+
const activeReasoning = /* @__PURE__ */ new Map();
|
|
76
|
+
const partialTools = /* @__PURE__ */ new Map();
|
|
77
|
+
for (const chunk of options.chunks) switch (chunk.type) {
|
|
78
|
+
case "text-start": {
|
|
79
|
+
const part = {
|
|
80
|
+
type: "text",
|
|
81
|
+
text: "",
|
|
82
|
+
state: "streaming",
|
|
83
|
+
...chunk.providerMetadata === void 0 ? {} : { providerMetadata: chunk.providerMetadata }
|
|
84
|
+
};
|
|
85
|
+
activeText.set(chunk.id, part);
|
|
86
|
+
parts.push(part);
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
case "text-delta": {
|
|
90
|
+
let part = activeText.get(chunk.id);
|
|
91
|
+
if (!part) {
|
|
92
|
+
part = {
|
|
93
|
+
type: "text",
|
|
94
|
+
text: "",
|
|
95
|
+
state: "streaming"
|
|
96
|
+
};
|
|
97
|
+
activeText.set(chunk.id, part);
|
|
98
|
+
parts.push(part);
|
|
99
|
+
}
|
|
100
|
+
part["text"] = `${typeof part["text"] === "string" ? part["text"] : ""}${chunk.delta}`;
|
|
101
|
+
setOptional(part, "providerMetadata", chunk.providerMetadata);
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
case "text-end": {
|
|
105
|
+
const part = activeText.get(chunk.id);
|
|
106
|
+
if (part) {
|
|
107
|
+
part["state"] = "done";
|
|
108
|
+
setOptional(part, "providerMetadata", chunk.providerMetadata);
|
|
109
|
+
activeText.delete(chunk.id);
|
|
110
|
+
}
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
case "reasoning-start": {
|
|
114
|
+
const part = {
|
|
115
|
+
type: "reasoning",
|
|
116
|
+
text: "",
|
|
117
|
+
state: "streaming",
|
|
118
|
+
...chunk.providerMetadata === void 0 ? {} : { providerMetadata: chunk.providerMetadata }
|
|
119
|
+
};
|
|
120
|
+
activeReasoning.set(chunk.id, part);
|
|
121
|
+
parts.push(part);
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
case "reasoning-delta": {
|
|
125
|
+
let part = activeReasoning.get(chunk.id);
|
|
126
|
+
if (!part) {
|
|
127
|
+
part = {
|
|
128
|
+
type: "reasoning",
|
|
129
|
+
text: "",
|
|
130
|
+
state: "streaming"
|
|
131
|
+
};
|
|
132
|
+
activeReasoning.set(chunk.id, part);
|
|
133
|
+
parts.push(part);
|
|
134
|
+
}
|
|
135
|
+
part["text"] = `${typeof part["text"] === "string" ? part["text"] : ""}${chunk.delta}`;
|
|
136
|
+
setOptional(part, "providerMetadata", chunk.providerMetadata);
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
case "reasoning-end": {
|
|
140
|
+
const part = activeReasoning.get(chunk.id);
|
|
141
|
+
if (part) {
|
|
142
|
+
part["state"] = "done";
|
|
143
|
+
setOptional(part, "providerMetadata", chunk.providerMetadata);
|
|
144
|
+
activeReasoning.delete(chunk.id);
|
|
145
|
+
}
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
case "custom":
|
|
149
|
+
parts.push({
|
|
150
|
+
type: "custom",
|
|
151
|
+
kind: chunk.kind,
|
|
152
|
+
...chunk.providerMetadata === void 0 ? {} : { providerMetadata: chunk.providerMetadata }
|
|
153
|
+
});
|
|
154
|
+
break;
|
|
155
|
+
case "file":
|
|
156
|
+
case "reasoning-file":
|
|
157
|
+
parts.push({
|
|
158
|
+
type: chunk.type,
|
|
159
|
+
mediaType: chunk.mediaType,
|
|
160
|
+
url: chunk.url,
|
|
161
|
+
...chunk.providerMetadata === void 0 ? {} : { providerMetadata: chunk.providerMetadata }
|
|
162
|
+
});
|
|
163
|
+
break;
|
|
164
|
+
case "source-url":
|
|
165
|
+
parts.push({
|
|
166
|
+
type: chunk.type,
|
|
167
|
+
sourceId: chunk.sourceId,
|
|
168
|
+
url: chunk.url,
|
|
169
|
+
...chunk.title === void 0 ? {} : { title: chunk.title },
|
|
170
|
+
...chunk.providerMetadata === void 0 ? {} : { providerMetadata: chunk.providerMetadata }
|
|
171
|
+
});
|
|
172
|
+
break;
|
|
173
|
+
case "source-document":
|
|
174
|
+
parts.push({
|
|
175
|
+
type: chunk.type,
|
|
176
|
+
sourceId: chunk.sourceId,
|
|
177
|
+
mediaType: chunk.mediaType,
|
|
178
|
+
title: chunk.title,
|
|
179
|
+
...chunk.filename === void 0 ? {} : { filename: chunk.filename },
|
|
180
|
+
...chunk.providerMetadata === void 0 ? {} : { providerMetadata: chunk.providerMetadata }
|
|
181
|
+
});
|
|
182
|
+
break;
|
|
183
|
+
case "tool-input-start":
|
|
184
|
+
partialTools.set(chunk.toolCallId, {
|
|
185
|
+
text: "",
|
|
186
|
+
toolName: chunk.toolName,
|
|
187
|
+
dynamic: chunk.dynamic === true,
|
|
188
|
+
...chunk.providerExecuted === void 0 ? {} : { providerExecuted: chunk.providerExecuted },
|
|
189
|
+
...chunk.providerMetadata === void 0 ? {} : { providerMetadata: chunk.providerMetadata },
|
|
190
|
+
...chunk.toolMetadata === void 0 ? {} : { toolMetadata: chunk.toolMetadata },
|
|
191
|
+
...chunk.title === void 0 ? {} : { title: chunk.title }
|
|
192
|
+
});
|
|
193
|
+
setToolPart({
|
|
194
|
+
parts,
|
|
195
|
+
toolCallId: chunk.toolCallId,
|
|
196
|
+
toolName: chunk.toolName,
|
|
197
|
+
dynamic: chunk.dynamic === true,
|
|
198
|
+
state: "input-streaming",
|
|
199
|
+
providerExecuted: chunk.providerExecuted,
|
|
200
|
+
providerMetadata: chunk.providerMetadata,
|
|
201
|
+
toolMetadata: chunk.toolMetadata,
|
|
202
|
+
title: chunk.title
|
|
203
|
+
});
|
|
204
|
+
break;
|
|
205
|
+
case "tool-input-delta": {
|
|
206
|
+
const partial = partialTools.get(chunk.toolCallId);
|
|
207
|
+
if (!partial) break;
|
|
208
|
+
partial.text += chunk.inputTextDelta;
|
|
209
|
+
setToolPart({
|
|
210
|
+
parts,
|
|
211
|
+
toolCallId: chunk.toolCallId,
|
|
212
|
+
toolName: partial.toolName,
|
|
213
|
+
dynamic: partial.dynamic,
|
|
214
|
+
state: "input-streaming",
|
|
215
|
+
input: parseCompleteJSON(partial.text),
|
|
216
|
+
providerExecuted: partial.providerExecuted,
|
|
217
|
+
providerMetadata: partial.providerMetadata,
|
|
218
|
+
toolMetadata: partial.toolMetadata,
|
|
219
|
+
title: partial.title
|
|
220
|
+
});
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
case "tool-input-available":
|
|
224
|
+
setToolPart({
|
|
225
|
+
parts,
|
|
226
|
+
toolCallId: chunk.toolCallId,
|
|
227
|
+
toolName: chunk.toolName,
|
|
228
|
+
dynamic: chunk.dynamic === true,
|
|
229
|
+
state: "input-available",
|
|
230
|
+
input: chunk.input,
|
|
231
|
+
providerExecuted: chunk.providerExecuted,
|
|
232
|
+
providerMetadata: chunk.providerMetadata,
|
|
233
|
+
toolMetadata: chunk.toolMetadata,
|
|
234
|
+
title: chunk.title
|
|
235
|
+
});
|
|
236
|
+
partialTools.delete(chunk.toolCallId);
|
|
237
|
+
break;
|
|
238
|
+
case "tool-input-error": {
|
|
239
|
+
const dynamic = findToolPart(parts, chunk.toolCallId)?.["type"] === "dynamic-tool" || chunk.dynamic;
|
|
240
|
+
setToolPart({
|
|
241
|
+
parts,
|
|
242
|
+
toolCallId: chunk.toolCallId,
|
|
243
|
+
toolName: chunk.toolName,
|
|
244
|
+
dynamic: dynamic === true,
|
|
245
|
+
state: "output-error",
|
|
246
|
+
input: dynamic ? chunk.input : void 0,
|
|
247
|
+
rawInput: dynamic ? void 0 : chunk.input,
|
|
248
|
+
errorText: chunk.errorText,
|
|
249
|
+
providerExecuted: chunk.providerExecuted,
|
|
250
|
+
providerMetadata: chunk.providerMetadata,
|
|
251
|
+
toolMetadata: chunk.toolMetadata,
|
|
252
|
+
title: chunk.title
|
|
253
|
+
});
|
|
254
|
+
partialTools.delete(chunk.toolCallId);
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
257
|
+
case "tool-approval-request": {
|
|
258
|
+
const part = findToolPart(parts, chunk.toolCallId);
|
|
259
|
+
if (!part) break;
|
|
260
|
+
part["state"] = "approval-requested";
|
|
261
|
+
part["approval"] = {
|
|
262
|
+
id: chunk.approvalId,
|
|
263
|
+
...chunk.isAutomatic === true ? { isAutomatic: true } : {},
|
|
264
|
+
...chunk.signature === void 0 ? {} : { signature: chunk.signature }
|
|
265
|
+
};
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
case "tool-approval-response": {
|
|
269
|
+
const part = findApprovalPart(parts, chunk.approvalId);
|
|
270
|
+
if (!part) break;
|
|
271
|
+
const approval = isRecord$1(part["approval"]) ? part["approval"] : {};
|
|
272
|
+
part["state"] = "approval-responded";
|
|
273
|
+
part["approval"] = {
|
|
274
|
+
id: chunk.approvalId,
|
|
275
|
+
approved: chunk.approved,
|
|
276
|
+
...chunk.reason === void 0 ? {} : { reason: chunk.reason },
|
|
277
|
+
...approval["isAutomatic"] === true ? { isAutomatic: true } : {},
|
|
278
|
+
...typeof approval["signature"] === "string" ? { signature: approval["signature"] } : {}
|
|
279
|
+
};
|
|
280
|
+
setOptional(part, "providerExecuted", chunk.providerExecuted);
|
|
281
|
+
setOptional(part, "callProviderMetadata", chunk.providerMetadata);
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
case "tool-output-available": {
|
|
285
|
+
const part = findToolPart(parts, chunk.toolCallId);
|
|
286
|
+
if (!part) break;
|
|
287
|
+
setToolPart({
|
|
288
|
+
parts,
|
|
289
|
+
toolCallId: chunk.toolCallId,
|
|
290
|
+
toolName: part["type"] === "dynamic-tool" && typeof part["toolName"] === "string" ? part["toolName"] : String(part["type"]).slice(5),
|
|
291
|
+
dynamic: part["type"] === "dynamic-tool",
|
|
292
|
+
state: "output-available",
|
|
293
|
+
input: part["input"],
|
|
294
|
+
output: chunk.output,
|
|
295
|
+
preliminary: chunk.preliminary,
|
|
296
|
+
providerExecuted: chunk.providerExecuted,
|
|
297
|
+
providerMetadata: chunk.providerMetadata,
|
|
298
|
+
toolMetadata: chunk.toolMetadata ?? part["toolMetadata"],
|
|
299
|
+
title: typeof part["title"] === "string" ? part["title"] : void 0
|
|
300
|
+
});
|
|
301
|
+
break;
|
|
302
|
+
}
|
|
303
|
+
case "tool-output-error": {
|
|
304
|
+
const part = findToolPart(parts, chunk.toolCallId);
|
|
305
|
+
if (!part) break;
|
|
306
|
+
setToolPart({
|
|
307
|
+
parts,
|
|
308
|
+
toolCallId: chunk.toolCallId,
|
|
309
|
+
toolName: part["type"] === "dynamic-tool" && typeof part["toolName"] === "string" ? part["toolName"] : String(part["type"]).slice(5),
|
|
310
|
+
dynamic: part["type"] === "dynamic-tool",
|
|
311
|
+
state: "output-error",
|
|
312
|
+
input: part["input"],
|
|
313
|
+
rawInput: part["rawInput"],
|
|
314
|
+
errorText: chunk.errorText,
|
|
315
|
+
providerExecuted: chunk.providerExecuted,
|
|
316
|
+
providerMetadata: chunk.providerMetadata,
|
|
317
|
+
toolMetadata: chunk.toolMetadata ?? part["toolMetadata"],
|
|
318
|
+
title: typeof part["title"] === "string" ? part["title"] : void 0
|
|
319
|
+
});
|
|
320
|
+
break;
|
|
321
|
+
}
|
|
322
|
+
case "tool-output-denied": {
|
|
323
|
+
const part = findToolPart(parts, chunk.toolCallId);
|
|
324
|
+
if (part) part["state"] = "output-denied";
|
|
325
|
+
break;
|
|
326
|
+
}
|
|
327
|
+
case "start-step":
|
|
328
|
+
parts.push({ type: "step-start" });
|
|
329
|
+
break;
|
|
330
|
+
case "finish-step":
|
|
331
|
+
activeText.clear();
|
|
332
|
+
activeReasoning.clear();
|
|
333
|
+
break;
|
|
334
|
+
case "start":
|
|
335
|
+
if (chunk.messageId !== void 0) record["id"] = chunk.messageId;
|
|
336
|
+
if (chunk.messageMetadata !== void 0 && chunk.messageMetadata !== null) record["metadata"] = mergeMetadata(record["metadata"], chunk.messageMetadata);
|
|
337
|
+
break;
|
|
338
|
+
case "finish":
|
|
339
|
+
if (chunk.messageMetadata !== void 0 && chunk.messageMetadata !== null) record["metadata"] = mergeMetadata(record["metadata"], chunk.messageMetadata);
|
|
340
|
+
break;
|
|
341
|
+
case "message-metadata":
|
|
342
|
+
if (chunk.messageMetadata !== void 0 && chunk.messageMetadata !== null) record["metadata"] = mergeMetadata(record["metadata"], chunk.messageMetadata);
|
|
343
|
+
break;
|
|
344
|
+
case "abort":
|
|
345
|
+
case "error": break;
|
|
346
|
+
default: {
|
|
347
|
+
if (!chunk.type.startsWith("data-") || chunk.transient) break;
|
|
348
|
+
const existing = chunk.id === void 0 ? void 0 : parts.find((part) => part["type"] === chunk.type && part["id"] === chunk.id);
|
|
349
|
+
if (existing) existing["data"] = chunk.data;
|
|
350
|
+
else parts.push({
|
|
351
|
+
type: chunk.type,
|
|
352
|
+
...chunk.id === void 0 ? {} : { id: chunk.id },
|
|
353
|
+
data: chunk.data
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
return message;
|
|
358
|
+
}
|
|
359
|
+
function interruptUIMessage(message, toolError = "Tool execution was interrupted.") {
|
|
360
|
+
const next = structuredClone(message);
|
|
361
|
+
for (const value of next.parts) {
|
|
362
|
+
if (value["type"] === "text" || value["type"] === "reasoning") {
|
|
363
|
+
if (value["state"] === "streaming") value["state"] = "done";
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
if (!isToolPart(value)) continue;
|
|
367
|
+
const state = value["state"];
|
|
368
|
+
if (state === "approval-responded") {
|
|
369
|
+
const approval = value["approval"];
|
|
370
|
+
if (isRecord$1(approval) && approval["approved"] === false) {
|
|
371
|
+
value["state"] = "output-denied";
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
if (state === "output-available" && value["preliminary"] === true) {
|
|
376
|
+
delete value["output"];
|
|
377
|
+
delete value["preliminary"];
|
|
378
|
+
value["state"] = "output-error";
|
|
379
|
+
value["errorText"] = toolError;
|
|
380
|
+
continue;
|
|
381
|
+
}
|
|
382
|
+
if (state === "input-streaming" || state === "input-available" || state === "approval-requested" || state === "approval-responded") {
|
|
383
|
+
if (state === "approval-requested") delete value["approval"];
|
|
384
|
+
value["state"] = "output-error";
|
|
385
|
+
value["errorText"] = toolError;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
return next;
|
|
389
|
+
}
|
|
390
|
+
//#endregion
|
|
391
|
+
//#region src/ai.ts
|
|
392
|
+
const issue = (message) => ({ issues: [{ message }] });
|
|
393
|
+
const schema = (label, parse) => ({ "~standard": {
|
|
394
|
+
version: 1,
|
|
395
|
+
vendor: "a2",
|
|
396
|
+
validate(value) {
|
|
397
|
+
try {
|
|
398
|
+
return parse(value);
|
|
399
|
+
} catch (error) {
|
|
400
|
+
return issue(`${label}: ${error instanceof Error ? error.message : String(error)}`);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
} });
|
|
404
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
405
|
+
const isJSONCompatible = (value, seen = /* @__PURE__ */ new Set()) => {
|
|
406
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return true;
|
|
407
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
408
|
+
if (typeof value === "undefined") return true;
|
|
409
|
+
if (typeof value !== "object") return false;
|
|
410
|
+
if (seen.has(value)) return false;
|
|
411
|
+
seen.add(value);
|
|
412
|
+
const valid = Array.isArray(value) ? value.every((item) => isJSONCompatible(item, seen)) : Object.getPrototypeOf(value) === Object.prototype && Object.values(value).every((item) => isJSONCompatible(item, seen));
|
|
413
|
+
seen.delete(value);
|
|
414
|
+
return valid;
|
|
415
|
+
};
|
|
416
|
+
const jsonObject = (label, check) => schema(label, (value) => {
|
|
417
|
+
if (!isRecord(value) || !isJSONCompatible(value) || !check(value)) return issue(`invalid ${label}`);
|
|
418
|
+
return { value };
|
|
419
|
+
});
|
|
420
|
+
const stringField = (value, key) => typeof value[key] === "string" && value[key].length > 0;
|
|
421
|
+
const optionalStringField = (value, key) => value[key] === void 0 || typeof value[key] === "string";
|
|
422
|
+
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);
|
|
423
|
+
const validateMessage = (value, messageSchema) => {
|
|
424
|
+
if (!messageValue(value)) return issue("invalid AI SDK UIMessage");
|
|
425
|
+
if (!messageSchema) return { value };
|
|
426
|
+
const result = messageSchema["~standard"].validate(value);
|
|
427
|
+
if (result instanceof Promise) throw new TypeError("the AI message schema must validate synchronously");
|
|
428
|
+
return result;
|
|
429
|
+
};
|
|
430
|
+
const messagePayloadSchema = (messageSchema) => schema("ai.message.created", (value) => {
|
|
431
|
+
if (!isRecord(value)) return issue("invalid ai.message.created payload");
|
|
432
|
+
const message = validateMessage(value["message"], messageSchema);
|
|
433
|
+
if (message.issues) return { issues: message.issues };
|
|
434
|
+
return { value: { message: message.value } };
|
|
435
|
+
});
|
|
436
|
+
const progressPayloadSchema = schema("ai.generation.progress", (value) => {
|
|
437
|
+
if (!isRecord(value) || !isJSONCompatible(value) || !stringField(value, "requestId") || !stringField(value, "messageId") || !stringField(value, "generationId") || !stringField(value, "responseMessageId") || typeof value["sequence"] !== "number" || !Number.isInteger(value["sequence"]) || value["sequence"] < 0 || !Array.isArray(value["chunks"]) || !value["chunks"].every((chunk) => isRecord(chunk) && typeof chunk["type"] === "string")) return issue("invalid ai.generation.progress payload");
|
|
438
|
+
return { value };
|
|
439
|
+
});
|
|
440
|
+
const completedPayloadSchema = schema("ai.generation.completed", (value) => {
|
|
441
|
+
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");
|
|
442
|
+
return { value };
|
|
443
|
+
});
|
|
444
|
+
const compactionPayloadSchema = (messageSchema) => schema("ai.compaction.completed", (value) => {
|
|
445
|
+
if (!isRecord(value) || !isJSONCompatible(value) || !stringField(value, "generationId") || !stringField(value, "throughMessageId") || !Array.isArray(value["messages"])) return issue("invalid ai.compaction.completed payload");
|
|
446
|
+
const messages = [];
|
|
447
|
+
for (const candidate of value["messages"]) {
|
|
448
|
+
const result = validateMessage(candidate, messageSchema);
|
|
449
|
+
if (result.issues) return { issues: result.issues };
|
|
450
|
+
messages.push(result.value);
|
|
451
|
+
}
|
|
452
|
+
return { value: {
|
|
453
|
+
generationId: value["generationId"],
|
|
454
|
+
throughMessageId: value["throughMessageId"],
|
|
455
|
+
messages
|
|
456
|
+
} };
|
|
457
|
+
});
|
|
458
|
+
function createEvents(options) {
|
|
459
|
+
const messageSchema = options?.messageSchema;
|
|
460
|
+
if (messageSchema) assertSyncSchema(messageSchema, "the AI message schema");
|
|
461
|
+
return {
|
|
462
|
+
"ai.session.created": jsonObject("ai.session.created", () => true),
|
|
463
|
+
"ai.session.closed": jsonObject("ai.session.closed", (value) => optionalStringField(value, "reason")),
|
|
464
|
+
"ai.message.created": messagePayloadSchema(messageSchema),
|
|
465
|
+
"ai.message.completed": jsonObject("ai.message.completed", (value) => stringField(value, "messageId")),
|
|
466
|
+
"ai.message.interrupted": jsonObject("ai.message.interrupted", (value) => stringField(value, "messageId") && optionalStringField(value, "generationId") && optionalStringField(value, "reason") && (value["lastSeenIndex"] === void 0 || typeof value["lastSeenIndex"] === "number" && Number.isInteger(value["lastSeenIndex"]) && value["lastSeenIndex"] >= 0)),
|
|
467
|
+
"ai.generation.requested": jsonObject("ai.generation.requested", (value) => stringField(value, "messageId") && optionalStringField(value, "responseMessageId") && (value["reason"] === "message" || value["reason"] === "approval" || value["reason"] === "input" || value["reason"] === "retry")),
|
|
468
|
+
"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")),
|
|
469
|
+
"ai.generation.progress": progressPayloadSchema,
|
|
470
|
+
"ai.generation.completed": completedPayloadSchema,
|
|
471
|
+
"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")),
|
|
472
|
+
"ai.tool.called": jsonObject("ai.tool.called", (value) => stringField(value, "requestId") && stringField(value, "messageId") && stringField(value, "generationId") && stringField(value, "toolCallId") && stringField(value, "toolName") && (value["dynamic"] === void 0 || typeof value["dynamic"] === "boolean") && (value["providerExecuted"] === void 0 || typeof value["providerExecuted"] === "boolean") && optionalStringField(value, "title")),
|
|
473
|
+
"ai.tool.result": jsonObject("ai.tool.result", (value) => stringField(value, "requestId") && stringField(value, "messageId") && stringField(value, "generationId") && stringField(value, "toolCallId") && optionalStringField(value, "toolName") && optionalStringField(value, "error") && (value["denied"] === void 0 || typeof value["denied"] === "boolean") && (value["preliminary"] === void 0 || typeof value["preliminary"] === "boolean") && (value["phase"] === void 0 || value["phase"] === "input" || value["phase"] === "execution") && (value["dynamic"] === void 0 || typeof value["dynamic"] === "boolean") && (value["providerExecuted"] === void 0 || typeof value["providerExecuted"] === "boolean")),
|
|
474
|
+
"ai.approval.requested": jsonObject("ai.approval.requested", (value) => stringField(value, "messageId") && stringField(value, "generationId") && stringField(value, "approvalId") && stringField(value, "toolCallId") && (value["isAutomatic"] === void 0 || typeof value["isAutomatic"] === "boolean") && optionalStringField(value, "signature")),
|
|
475
|
+
"ai.approval.responded": jsonObject("ai.approval.responded", (value) => stringField(value, "messageId") && stringField(value, "approvalId") && typeof value["approved"] === "boolean" && optionalStringField(value, "reason")),
|
|
476
|
+
"ai.input.requested": jsonObject("ai.input.requested", (value) => stringField(value, "messageId") && optionalStringField(value, "generationId") && stringField(value, "inputId") && stringField(value, "name")),
|
|
477
|
+
"ai.input.responded": jsonObject("ai.input.responded", (value) => stringField(value, "messageId") && stringField(value, "inputId") && stringField(value, "name")),
|
|
478
|
+
"ai.compaction.requested": jsonObject("ai.compaction.requested", (value) => stringField(value, "generationId") && stringField(value, "throughMessageId")),
|
|
479
|
+
"ai.compaction.completed": compactionPayloadSchema(messageSchema)
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
const events = createEvents();
|
|
483
|
+
const initialState = () => ({
|
|
484
|
+
session: { created: false },
|
|
485
|
+
messages: [],
|
|
486
|
+
status: "idle",
|
|
487
|
+
activeGeneration: null,
|
|
488
|
+
activeProjection: null,
|
|
489
|
+
pendingApprovals: [],
|
|
490
|
+
pendingInputs: [],
|
|
491
|
+
tools: [],
|
|
492
|
+
compaction: null,
|
|
493
|
+
usage: [],
|
|
494
|
+
error: null
|
|
495
|
+
});
|
|
496
|
+
const upsertMessage = (messages, message) => {
|
|
497
|
+
const index = messages.findIndex((candidate) => candidate.id === message.id);
|
|
498
|
+
if (index === -1) return [...messages, message];
|
|
499
|
+
const next = [...messages];
|
|
500
|
+
next[index] = message;
|
|
501
|
+
return next;
|
|
502
|
+
};
|
|
503
|
+
const answerApproval = (messages, response) => messages.map((message) => {
|
|
504
|
+
if (message.id !== response.messageId) return message;
|
|
505
|
+
let changed = false;
|
|
506
|
+
const parts = message.parts.map((part) => {
|
|
507
|
+
const candidate = part;
|
|
508
|
+
if (!isRecord(candidate) || candidate["state"] !== "approval-requested" || !isRecord(candidate["approval"]) || candidate["approval"]["id"] !== response.approvalId) return part;
|
|
509
|
+
changed = true;
|
|
510
|
+
return {
|
|
511
|
+
...part,
|
|
512
|
+
state: "approval-responded",
|
|
513
|
+
approval: {
|
|
514
|
+
...candidate["approval"],
|
|
515
|
+
approved: response.approved,
|
|
516
|
+
...response.reason === void 0 ? {} : { reason: response.reason }
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
});
|
|
520
|
+
return changed ? {
|
|
521
|
+
...message,
|
|
522
|
+
parts
|
|
523
|
+
} : message;
|
|
524
|
+
});
|
|
525
|
+
const waitingStatus = (state) => state.pendingApprovals.length > 0 || state.pendingInputs.length > 0 ? "waiting" : "idle";
|
|
526
|
+
const projectedMessage = (projection) => projectUIMessage({
|
|
527
|
+
messageId: projection.responseMessageId,
|
|
528
|
+
...projection.baseMessage === void 0 ? {} : { base: projection.baseMessage },
|
|
529
|
+
chunks: projection.batches.flatMap((batch) => batch.chunks)
|
|
530
|
+
});
|
|
531
|
+
const reduceToolActivity = (tools, event) => {
|
|
532
|
+
if (event.type === "ai.tool.called") {
|
|
533
|
+
const payload = event.payload;
|
|
534
|
+
const { providerMetadata, ...called } = payload;
|
|
535
|
+
const tool = {
|
|
536
|
+
...called,
|
|
537
|
+
...providerMetadata === void 0 ? {} : { callProviderMetadata: providerMetadata },
|
|
538
|
+
status: "running"
|
|
539
|
+
};
|
|
540
|
+
return [...tools.filter((candidate) => candidate.toolCallId !== payload.toolCallId), tool];
|
|
541
|
+
}
|
|
542
|
+
const payload = event.payload;
|
|
543
|
+
const existing = tools.find((candidate) => candidate.toolCallId === payload.toolCallId);
|
|
544
|
+
const toolName = payload.toolName ?? existing?.toolName;
|
|
545
|
+
const input = payload.input === void 0 ? existing?.input : payload.input;
|
|
546
|
+
const dynamic = payload.dynamic ?? existing?.dynamic;
|
|
547
|
+
const providerExecuted = payload.providerExecuted ?? existing?.providerExecuted;
|
|
548
|
+
const toolMetadata = payload.toolMetadata ?? existing?.toolMetadata;
|
|
549
|
+
const resultProviderMetadata = payload.providerMetadata ?? existing?.resultProviderMetadata;
|
|
550
|
+
const tool = {
|
|
551
|
+
requestId: payload.requestId,
|
|
552
|
+
messageId: payload.messageId,
|
|
553
|
+
generationId: payload.generationId,
|
|
554
|
+
toolCallId: payload.toolCallId,
|
|
555
|
+
...toolName === void 0 ? {} : { toolName },
|
|
556
|
+
...input === void 0 ? {} : { input },
|
|
557
|
+
...payload.rawInput === void 0 ? {} : { rawInput: payload.rawInput },
|
|
558
|
+
...payload.output === void 0 ? {} : { output: payload.output },
|
|
559
|
+
...payload.error === void 0 ? {} : { error: payload.error },
|
|
560
|
+
...payload.preliminary === void 0 ? {} : { preliminary: payload.preliminary },
|
|
561
|
+
...payload.phase === void 0 ? {} : { phase: payload.phase },
|
|
562
|
+
...dynamic === void 0 ? {} : { dynamic },
|
|
563
|
+
...providerExecuted === void 0 ? {} : { providerExecuted },
|
|
564
|
+
...existing?.callProviderMetadata === void 0 ? {} : { callProviderMetadata: existing.callProviderMetadata },
|
|
565
|
+
...resultProviderMetadata === void 0 ? {} : { resultProviderMetadata },
|
|
566
|
+
...toolMetadata === void 0 ? {} : { toolMetadata },
|
|
567
|
+
...existing?.title === void 0 ? {} : { title: existing.title },
|
|
568
|
+
status: payload.denied ? "denied" : payload.error ? "failed" : payload.preliminary ? "running" : "completed"
|
|
569
|
+
};
|
|
570
|
+
return [...tools.filter((candidate) => candidate.toolCallId !== payload.toolCallId), tool];
|
|
571
|
+
};
|
|
572
|
+
const projectedTools = (projection) => projection.toolEvents.reduce((tools, event) => reduceToolActivity(tools, event), projection.baseTools);
|
|
573
|
+
const stopGenerationTools = (tools, generationId, responseMessageId, error) => tools.map((tool) => (tool.generationId === generationId || tool.messageId === responseMessageId) && tool.status === "running" ? {
|
|
574
|
+
...tool,
|
|
575
|
+
status: "failed",
|
|
576
|
+
error,
|
|
577
|
+
preliminary: false
|
|
578
|
+
} : tool);
|
|
579
|
+
/** Pure projection of the built-in AI protocol. Unknown extension events are ignored. */
|
|
580
|
+
function reduceAIState(state, event) {
|
|
581
|
+
if (state.status === "closed") return state;
|
|
582
|
+
switch (event.type) {
|
|
583
|
+
case "ai.session.created": {
|
|
584
|
+
const payload = event.payload;
|
|
585
|
+
return {
|
|
586
|
+
...state,
|
|
587
|
+
session: {
|
|
588
|
+
created: true,
|
|
589
|
+
...payload.metadata === void 0 ? state.session.metadata === void 0 ? {} : { metadata: state.session.metadata } : { metadata: payload.metadata }
|
|
590
|
+
}
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
case "ai.session.closed": return {
|
|
594
|
+
...state,
|
|
595
|
+
status: "closed",
|
|
596
|
+
activeGeneration: null,
|
|
597
|
+
activeProjection: null
|
|
598
|
+
};
|
|
599
|
+
case "ai.message.created": {
|
|
600
|
+
const { message } = event.payload;
|
|
601
|
+
return {
|
|
602
|
+
...state,
|
|
603
|
+
messages: upsertMessage(state.messages, message)
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
case "ai.generation.requested": {
|
|
607
|
+
const payload = event.payload;
|
|
608
|
+
return {
|
|
609
|
+
...state,
|
|
610
|
+
messages: payload.reason === "retry" && payload.responseMessageId !== void 0 ? state.messages.filter((message) => message.id !== payload.responseMessageId) : state.messages,
|
|
611
|
+
status: "generating",
|
|
612
|
+
error: null
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
case "ai.generation.started": {
|
|
616
|
+
const payload = event.payload;
|
|
617
|
+
const baseMessage = state.messages.find((message) => message.id === payload.responseMessageId);
|
|
618
|
+
return {
|
|
619
|
+
...state,
|
|
620
|
+
status: "generating",
|
|
621
|
+
activeGeneration: payload,
|
|
622
|
+
activeProjection: {
|
|
623
|
+
generationId: payload.generationId,
|
|
624
|
+
responseMessageId: payload.responseMessageId,
|
|
625
|
+
...baseMessage === void 0 ? {} : { baseMessage },
|
|
626
|
+
batches: [],
|
|
627
|
+
baseTools: state.tools,
|
|
628
|
+
toolEvents: []
|
|
629
|
+
},
|
|
630
|
+
error: null
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
case "ai.generation.progress": {
|
|
634
|
+
const payload = event.payload;
|
|
635
|
+
const activeProjection = state.activeProjection;
|
|
636
|
+
if (state.activeGeneration?.generationId !== payload.generationId || activeProjection?.generationId !== payload.generationId) return state;
|
|
637
|
+
const projection = {
|
|
638
|
+
...activeProjection,
|
|
639
|
+
batches: [...activeProjection.batches.filter((batch) => batch.index !== event.index), {
|
|
640
|
+
index: event.index,
|
|
641
|
+
chunks: payload.chunks
|
|
642
|
+
}].toSorted((left, right) => left.index - right.index)
|
|
643
|
+
};
|
|
644
|
+
return {
|
|
645
|
+
...state,
|
|
646
|
+
messages: upsertMessage(state.messages, projectedMessage(projection)),
|
|
647
|
+
activeProjection: projection,
|
|
648
|
+
status: "generating"
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
case "ai.generation.completed": {
|
|
652
|
+
const payload = event.payload;
|
|
653
|
+
if (state.activeGeneration?.generationId !== payload.generationId) return state;
|
|
654
|
+
const usage = payload.usage ? [...state.usage.filter((entry) => entry.generationId !== payload.generationId), {
|
|
655
|
+
generationId: payload.generationId,
|
|
656
|
+
usage: payload.usage
|
|
657
|
+
}] : state.usage;
|
|
658
|
+
const next = {
|
|
659
|
+
...state,
|
|
660
|
+
activeGeneration: null,
|
|
661
|
+
activeProjection: null,
|
|
662
|
+
usage
|
|
663
|
+
};
|
|
664
|
+
return {
|
|
665
|
+
...next,
|
|
666
|
+
status: waitingStatus(next)
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
case "ai.generation.failed": {
|
|
670
|
+
const payload = event.payload;
|
|
671
|
+
if (state.activeGeneration?.generationId !== payload.generationId) return state;
|
|
672
|
+
if (payload.superseded === true) return {
|
|
673
|
+
...state,
|
|
674
|
+
messages: state.messages.filter((message) => message.id !== payload.responseMessageId),
|
|
675
|
+
tools: state.tools.filter((tool) => tool.generationId !== payload.generationId),
|
|
676
|
+
activeGeneration: null,
|
|
677
|
+
activeProjection: null
|
|
678
|
+
};
|
|
679
|
+
const projection = state.activeProjection;
|
|
680
|
+
const shouldProject = projection?.generationId === payload.generationId && (projection.baseMessage !== void 0 || projection.batches.length > 0);
|
|
681
|
+
return {
|
|
682
|
+
...state,
|
|
683
|
+
messages: shouldProject ? upsertMessage(state.messages, interruptUIMessage(projectedMessage(projection), `Generation failed before tool completion: ${payload.error}`)) : state.messages,
|
|
684
|
+
status: "failed",
|
|
685
|
+
activeGeneration: null,
|
|
686
|
+
activeProjection: null,
|
|
687
|
+
pendingApprovals: state.pendingApprovals.filter((approval) => approval.generationId !== payload.generationId),
|
|
688
|
+
tools: stopGenerationTools(state.tools, payload.generationId, payload.responseMessageId, payload.error),
|
|
689
|
+
error: payload.error
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
case "ai.message.interrupted": {
|
|
693
|
+
const payload = event.payload;
|
|
694
|
+
const active = state.activeGeneration;
|
|
695
|
+
if (active === null || (payload.generationId === void 0 ? payload.messageId !== active.responseMessageId : payload.generationId !== active.generationId)) return state;
|
|
696
|
+
const projection = state.activeProjection;
|
|
697
|
+
const cutoff = payload.lastSeenIndex ?? Number.POSITIVE_INFINITY;
|
|
698
|
+
const retained = projection?.generationId === active.generationId ? {
|
|
699
|
+
...projection,
|
|
700
|
+
batches: projection.batches.filter((batch) => batch.index <= cutoff),
|
|
701
|
+
toolEvents: projection.toolEvents.filter((toolEvent) => toolEvent.index <= cutoff)
|
|
702
|
+
} : null;
|
|
703
|
+
const shouldProject = retained !== null && (retained.baseMessage !== void 0 || retained.batches.length > 0);
|
|
704
|
+
const next = {
|
|
705
|
+
...state,
|
|
706
|
+
messages: shouldProject ? upsertMessage(state.messages, interruptUIMessage(projectedMessage(retained))) : state.messages,
|
|
707
|
+
activeGeneration: null,
|
|
708
|
+
activeProjection: null,
|
|
709
|
+
pendingApprovals: state.pendingApprovals.filter((approval) => approval.generationId !== active.generationId),
|
|
710
|
+
tools: stopGenerationTools(retained === null ? state.tools : projectedTools(retained), active.generationId, active.responseMessageId, "Tool execution was interrupted.")
|
|
711
|
+
};
|
|
712
|
+
return {
|
|
713
|
+
...next,
|
|
714
|
+
status: waitingStatus(next)
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
case "ai.tool.called": {
|
|
718
|
+
const payload = event.payload;
|
|
719
|
+
const projection = state.activeProjection;
|
|
720
|
+
if (projection?.generationId !== payload.generationId) return state;
|
|
721
|
+
const activeProjection = {
|
|
722
|
+
...projection,
|
|
723
|
+
toolEvents: [...projection.toolEvents, {
|
|
724
|
+
index: event.index,
|
|
725
|
+
type: "ai.tool.called",
|
|
726
|
+
payload
|
|
727
|
+
}]
|
|
728
|
+
};
|
|
729
|
+
return {
|
|
730
|
+
...state,
|
|
731
|
+
activeProjection,
|
|
732
|
+
tools: projectedTools(activeProjection)
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
case "ai.tool.result": {
|
|
736
|
+
const payload = event.payload;
|
|
737
|
+
const projection = state.activeProjection;
|
|
738
|
+
if (projection?.generationId !== payload.generationId) return state;
|
|
739
|
+
const activeProjection = {
|
|
740
|
+
...projection,
|
|
741
|
+
toolEvents: [...projection.toolEvents, {
|
|
742
|
+
index: event.index,
|
|
743
|
+
type: "ai.tool.result",
|
|
744
|
+
payload
|
|
745
|
+
}]
|
|
746
|
+
};
|
|
747
|
+
return {
|
|
748
|
+
...state,
|
|
749
|
+
activeProjection,
|
|
750
|
+
tools: projectedTools(activeProjection)
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
case "ai.approval.requested": {
|
|
754
|
+
const payload = event.payload;
|
|
755
|
+
return {
|
|
756
|
+
...state,
|
|
757
|
+
pendingApprovals: [...state.pendingApprovals.filter((approval) => approval.approvalId !== payload.approvalId), payload]
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
case "ai.approval.responded": {
|
|
761
|
+
const payload = event.payload;
|
|
762
|
+
const next = {
|
|
763
|
+
...state,
|
|
764
|
+
messages: answerApproval(state.messages, payload),
|
|
765
|
+
pendingApprovals: state.pendingApprovals.filter((approval) => approval.approvalId !== payload.approvalId)
|
|
766
|
+
};
|
|
767
|
+
return {
|
|
768
|
+
...next,
|
|
769
|
+
status: waitingStatus(next)
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
case "ai.input.requested": {
|
|
773
|
+
const payload = event.payload;
|
|
774
|
+
return {
|
|
775
|
+
...state,
|
|
776
|
+
pendingInputs: [...state.pendingInputs.filter((input) => input.inputId !== payload.inputId), payload],
|
|
777
|
+
status: "waiting"
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
case "ai.input.responded": {
|
|
781
|
+
const payload = event.payload;
|
|
782
|
+
const next = {
|
|
783
|
+
...state,
|
|
784
|
+
pendingInputs: state.pendingInputs.filter((input) => input.inputId !== payload.inputId)
|
|
785
|
+
};
|
|
786
|
+
return {
|
|
787
|
+
...next,
|
|
788
|
+
status: waitingStatus(next)
|
|
789
|
+
};
|
|
790
|
+
}
|
|
791
|
+
case "ai.compaction.requested": {
|
|
792
|
+
const payload = event.payload;
|
|
793
|
+
return {
|
|
794
|
+
...state,
|
|
795
|
+
compaction: {
|
|
796
|
+
status: "running",
|
|
797
|
+
...payload
|
|
798
|
+
}
|
|
799
|
+
};
|
|
800
|
+
}
|
|
801
|
+
case "ai.compaction.completed": {
|
|
802
|
+
const payload = event.payload;
|
|
803
|
+
return {
|
|
804
|
+
...state,
|
|
805
|
+
compaction: {
|
|
806
|
+
status: "completed",
|
|
807
|
+
...payload
|
|
808
|
+
}
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
default: return state;
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
/** Derive AI SDK UI messages directly from an A2 event history. */
|
|
815
|
+
function deriveUIMessages(history) {
|
|
816
|
+
let state = initialState();
|
|
817
|
+
for (const event of history) state = reduceAIState(state, event);
|
|
818
|
+
return state.messages;
|
|
819
|
+
}
|
|
820
|
+
/** Build the standard AI state projection for any contract containing the protocol. */
|
|
821
|
+
function createReducer(options) {
|
|
822
|
+
return options.contract.reducer({
|
|
823
|
+
name: options.name ?? "a2.ai.state.v2",
|
|
824
|
+
initialState: initialState()
|
|
825
|
+
}).fold((state, event) => reduceAIState(state, event));
|
|
826
|
+
}
|
|
827
|
+
const generationRequest = (options) => ({
|
|
828
|
+
type: "ai.generation.requested",
|
|
829
|
+
id: options.id,
|
|
830
|
+
payload: {
|
|
831
|
+
messageId: options.messageId,
|
|
832
|
+
reason: options.reason,
|
|
833
|
+
...options.responseMessageId === void 0 ? {} : { responseMessageId: options.responseMessageId }
|
|
834
|
+
}
|
|
835
|
+
});
|
|
836
|
+
const seedMessage = (message) => [{
|
|
837
|
+
type: "ai.message.created",
|
|
838
|
+
id: `ai.message:${message.id}`,
|
|
839
|
+
payload: { message }
|
|
840
|
+
}];
|
|
841
|
+
/** Pure append inputs for the built-in AI protocol. */
|
|
842
|
+
const inputs = {
|
|
843
|
+
message(message) {
|
|
844
|
+
const messageInputs = seedMessage(message);
|
|
845
|
+
if (message.role !== "user") return messageInputs;
|
|
846
|
+
return [...messageInputs, generationRequest({
|
|
847
|
+
id: `ai.generate:message:${message.id}`,
|
|
848
|
+
messageId: message.id,
|
|
849
|
+
reason: "message"
|
|
850
|
+
})];
|
|
851
|
+
},
|
|
852
|
+
seed: seedMessage,
|
|
853
|
+
approval(response) {
|
|
854
|
+
return [{
|
|
855
|
+
type: "ai.approval.responded",
|
|
856
|
+
id: `ai.approval:${response.approvalId}:response`,
|
|
857
|
+
payload: response
|
|
858
|
+
}, generationRequest({
|
|
859
|
+
id: `ai.generate:approval:${response.approvalId}`,
|
|
860
|
+
messageId: response.messageId,
|
|
861
|
+
reason: "approval"
|
|
862
|
+
})];
|
|
863
|
+
},
|
|
864
|
+
input(response) {
|
|
865
|
+
return [{
|
|
866
|
+
type: "ai.input.responded",
|
|
867
|
+
id: `ai.input:${response.inputId}:response`,
|
|
868
|
+
payload: response
|
|
869
|
+
}, generationRequest({
|
|
870
|
+
id: `ai.generate:input:${response.inputId}`,
|
|
871
|
+
messageId: response.messageId,
|
|
872
|
+
reason: "input"
|
|
873
|
+
})];
|
|
874
|
+
},
|
|
875
|
+
requestInput(request) {
|
|
876
|
+
return [{
|
|
877
|
+
type: "ai.input.requested",
|
|
878
|
+
id: `ai.input:${request.inputId}:request`,
|
|
879
|
+
payload: request
|
|
880
|
+
}];
|
|
881
|
+
},
|
|
882
|
+
retry(retryOptions) {
|
|
883
|
+
return [generationRequest({
|
|
884
|
+
id: `ai.generate:retry:${retryOptions.retryId}`,
|
|
885
|
+
messageId: retryOptions.messageId,
|
|
886
|
+
responseMessageId: retryOptions.responseMessageId,
|
|
887
|
+
reason: "retry"
|
|
888
|
+
})];
|
|
889
|
+
},
|
|
890
|
+
interrupt(interruption) {
|
|
891
|
+
return [{
|
|
892
|
+
type: "ai.message.interrupted",
|
|
893
|
+
id: `ai.interrupt:${interruption.generationId ?? interruption.messageId}`,
|
|
894
|
+
payload: interruption
|
|
895
|
+
}];
|
|
896
|
+
}
|
|
897
|
+
};
|
|
898
|
+
/**
|
|
899
|
+
* Define an agent: built-in AI protocol + application events and one state
|
|
900
|
+
* reducer. The result is isomorphic.
|
|
901
|
+
*/
|
|
902
|
+
function agent(options) {
|
|
903
|
+
const builtIns = createEvents(options.messageSchema === void 0 ? void 0 : { messageSchema: options.messageSchema });
|
|
904
|
+
const extensions = options.events ?? {};
|
|
905
|
+
for (const key of Object.keys(extensions)) if (Object.hasOwn(builtIns, key)) throw new TypeError(`agent event '${key}' conflicts with a built-in event`);
|
|
906
|
+
const agentContract = contract({
|
|
907
|
+
name: options.name,
|
|
908
|
+
events: {
|
|
909
|
+
...builtIns,
|
|
910
|
+
...extensions
|
|
911
|
+
}
|
|
912
|
+
});
|
|
913
|
+
return {
|
|
914
|
+
contract: agentContract,
|
|
915
|
+
reducer: agentContract.reducer({
|
|
916
|
+
name: options.reducerName ?? "a2.ai.state.v2",
|
|
917
|
+
initialState: initialState()
|
|
918
|
+
}).fold((state, event) => reduceAIState(state, event))
|
|
919
|
+
};
|
|
920
|
+
}
|
|
921
|
+
//#endregion
|
|
922
|
+
export { agent, createEvents, createReducer, deriveUIMessages, events, inputs, reduceAIState };
|