llmist 2.5.0 → 3.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/README.md +10 -1
- package/dist/{chunk-YHS2DYXP.js → chunk-67MMSOAT.js} +1144 -799
- package/dist/chunk-67MMSOAT.js.map +1 -0
- package/dist/{chunk-IHSZUAYN.js → chunk-NBPKLSXJ.js} +136 -20
- package/dist/chunk-NBPKLSXJ.js.map +1 -0
- package/dist/cli.cjs +3994 -3128
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +3254 -2748
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +792 -323
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +734 -481
- package/dist/index.d.ts +734 -481
- package/dist/index.js +32 -16
- package/dist/{mock-stream-ga4KIiwX.d.cts → mock-stream-COHw8h9b.d.cts} +441 -58
- package/dist/{mock-stream-ga4KIiwX.d.ts → mock-stream-COHw8h9b.d.ts} +441 -58
- package/dist/testing/index.cjs +1115 -761
- package/dist/testing/index.cjs.map +1 -1
- package/dist/testing/index.d.cts +346 -346
- package/dist/testing/index.d.ts +346 -346
- package/dist/testing/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-IHSZUAYN.js.map +0 -1
- package/dist/chunk-YHS2DYXP.js.map +0 -1
|
@@ -260,7 +260,9 @@ var init_input_content = __esm({
|
|
|
260
260
|
// WAV (RIFF)
|
|
261
261
|
{ bytes: [82, 73, 70, 70], mimeType: "audio/wav" },
|
|
262
262
|
// WebM
|
|
263
|
-
{ bytes: [26, 69, 223, 163], mimeType: "audio/webm" }
|
|
263
|
+
{ bytes: [26, 69, 223, 163], mimeType: "audio/webm" },
|
|
264
|
+
// FLAC (fLaC)
|
|
265
|
+
{ bytes: [102, 76, 97, 67], mimeType: "audio/flac" }
|
|
264
266
|
];
|
|
265
267
|
}
|
|
266
268
|
});
|
|
@@ -318,13 +320,13 @@ var init_prompt_config = __esm({
|
|
|
318
320
|
});
|
|
319
321
|
|
|
320
322
|
// src/core/messages.ts
|
|
321
|
-
function
|
|
323
|
+
function normalizeMessageContent(content) {
|
|
322
324
|
if (typeof content === "string") {
|
|
323
325
|
return [{ type: "text", text: content }];
|
|
324
326
|
}
|
|
325
327
|
return content;
|
|
326
328
|
}
|
|
327
|
-
function
|
|
329
|
+
function extractMessageText(content) {
|
|
328
330
|
if (typeof content === "string") {
|
|
329
331
|
return content;
|
|
330
332
|
}
|
|
@@ -684,7 +686,17 @@ Produces: { "items": ["first", "second"] }`);
|
|
|
684
686
|
this.messages.push({ role: "user", content: parts });
|
|
685
687
|
return this;
|
|
686
688
|
}
|
|
687
|
-
|
|
689
|
+
/**
|
|
690
|
+
* Record a gadget execution result in the message history.
|
|
691
|
+
* Creates an assistant message with the gadget invocation and a user message with the result.
|
|
692
|
+
*
|
|
693
|
+
* @param gadget - Name of the gadget that was executed
|
|
694
|
+
* @param parameters - Parameters that were passed to the gadget
|
|
695
|
+
* @param result - Text result from the gadget execution
|
|
696
|
+
* @param media - Optional media outputs from the gadget
|
|
697
|
+
* @param mediaIds - Optional IDs for the media outputs
|
|
698
|
+
*/
|
|
699
|
+
addGadgetCallResult(gadget, parameters, result, media, mediaIds) {
|
|
688
700
|
const paramStr = this.formatBlockParameters(parameters, "");
|
|
689
701
|
this.messages.push({
|
|
690
702
|
role: "assistant",
|
|
@@ -692,10 +704,25 @@ Produces: { "items": ["first", "second"] }`);
|
|
|
692
704
|
${paramStr}
|
|
693
705
|
${this.endPrefix}`
|
|
694
706
|
});
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
707
|
+
if (media && media.length > 0 && mediaIds && mediaIds.length > 0) {
|
|
708
|
+
const idRefs = media.map((m, i) => `[Media: ${mediaIds[i]} (${m.kind})]`).join("\n");
|
|
709
|
+
const textWithIds = `Result: ${result}
|
|
710
|
+
${idRefs}`;
|
|
711
|
+
const parts = [text(textWithIds)];
|
|
712
|
+
for (const item of media) {
|
|
713
|
+
if (item.kind === "image") {
|
|
714
|
+
parts.push(imageFromBase64(item.data, item.mimeType));
|
|
715
|
+
} else if (item.kind === "audio") {
|
|
716
|
+
parts.push(audioFromBase64(item.data, item.mimeType));
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
this.messages.push({ role: "user", content: parts });
|
|
720
|
+
} else {
|
|
721
|
+
this.messages.push({
|
|
722
|
+
role: "user",
|
|
723
|
+
content: `Result: ${result}`
|
|
724
|
+
});
|
|
725
|
+
}
|
|
699
726
|
return this;
|
|
700
727
|
}
|
|
701
728
|
/**
|
|
@@ -1045,22 +1072,226 @@ var init_registry = __esm({
|
|
|
1045
1072
|
}
|
|
1046
1073
|
});
|
|
1047
1074
|
|
|
1075
|
+
// src/gadgets/media-store.ts
|
|
1076
|
+
import { randomBytes } from "node:crypto";
|
|
1077
|
+
import { mkdir, rm, writeFile } from "node:fs/promises";
|
|
1078
|
+
import { homedir } from "node:os";
|
|
1079
|
+
import { join } from "node:path";
|
|
1080
|
+
function getLlmistTmpDir() {
|
|
1081
|
+
return join(homedir(), ".llmist", "tmp");
|
|
1082
|
+
}
|
|
1083
|
+
var MIME_TO_EXTENSION, MediaStore;
|
|
1084
|
+
var init_media_store = __esm({
|
|
1085
|
+
"src/gadgets/media-store.ts"() {
|
|
1086
|
+
"use strict";
|
|
1087
|
+
MIME_TO_EXTENSION = {
|
|
1088
|
+
// Images
|
|
1089
|
+
"image/png": ".png",
|
|
1090
|
+
"image/jpeg": ".jpg",
|
|
1091
|
+
"image/gif": ".gif",
|
|
1092
|
+
"image/webp": ".webp",
|
|
1093
|
+
"image/svg+xml": ".svg",
|
|
1094
|
+
"image/bmp": ".bmp",
|
|
1095
|
+
"image/tiff": ".tiff",
|
|
1096
|
+
// Audio
|
|
1097
|
+
"audio/mp3": ".mp3",
|
|
1098
|
+
"audio/mpeg": ".mp3",
|
|
1099
|
+
"audio/wav": ".wav",
|
|
1100
|
+
"audio/webm": ".webm",
|
|
1101
|
+
"audio/ogg": ".ogg",
|
|
1102
|
+
"audio/flac": ".flac",
|
|
1103
|
+
"audio/aac": ".aac",
|
|
1104
|
+
// Video
|
|
1105
|
+
"video/mp4": ".mp4",
|
|
1106
|
+
"video/webm": ".webm",
|
|
1107
|
+
"video/ogg": ".ogv",
|
|
1108
|
+
"video/quicktime": ".mov",
|
|
1109
|
+
"video/x-msvideo": ".avi",
|
|
1110
|
+
// Documents
|
|
1111
|
+
"application/pdf": ".pdf",
|
|
1112
|
+
"application/json": ".json",
|
|
1113
|
+
"text/plain": ".txt",
|
|
1114
|
+
"text/html": ".html",
|
|
1115
|
+
"text/css": ".css",
|
|
1116
|
+
"text/javascript": ".js"
|
|
1117
|
+
};
|
|
1118
|
+
MediaStore = class {
|
|
1119
|
+
items = /* @__PURE__ */ new Map();
|
|
1120
|
+
outputDir;
|
|
1121
|
+
counter = 0;
|
|
1122
|
+
initialized = false;
|
|
1123
|
+
/**
|
|
1124
|
+
* Create a new MediaStore.
|
|
1125
|
+
*
|
|
1126
|
+
* @param sessionId - Optional session ID for the output directory.
|
|
1127
|
+
* If not provided, a random ID is generated.
|
|
1128
|
+
*/
|
|
1129
|
+
constructor(sessionId) {
|
|
1130
|
+
const id = sessionId ?? randomBytes(8).toString("hex");
|
|
1131
|
+
this.outputDir = join(getLlmistTmpDir(), `media-${id}`);
|
|
1132
|
+
}
|
|
1133
|
+
/**
|
|
1134
|
+
* Get the output directory path.
|
|
1135
|
+
*/
|
|
1136
|
+
getOutputDir() {
|
|
1137
|
+
return this.outputDir;
|
|
1138
|
+
}
|
|
1139
|
+
/**
|
|
1140
|
+
* Ensure the output directory exists.
|
|
1141
|
+
* @throws Error if directory creation fails
|
|
1142
|
+
*/
|
|
1143
|
+
async ensureDir() {
|
|
1144
|
+
if (this.initialized) return;
|
|
1145
|
+
try {
|
|
1146
|
+
await mkdir(this.outputDir, { recursive: true });
|
|
1147
|
+
this.initialized = true;
|
|
1148
|
+
} catch (error) {
|
|
1149
|
+
throw new Error(
|
|
1150
|
+
`MediaStore: Failed to create directory ${this.outputDir}: ${error instanceof Error ? error.message : String(error)}`
|
|
1151
|
+
);
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
/**
|
|
1155
|
+
* Generate a unique media ID.
|
|
1156
|
+
* Format: "media_" + 6 random alphanumeric characters
|
|
1157
|
+
*/
|
|
1158
|
+
generateId() {
|
|
1159
|
+
const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
1160
|
+
let id = "media_";
|
|
1161
|
+
const bytes = randomBytes(6);
|
|
1162
|
+
for (let i = 0; i < 6; i++) {
|
|
1163
|
+
id += chars[bytes[i] % chars.length];
|
|
1164
|
+
}
|
|
1165
|
+
return id;
|
|
1166
|
+
}
|
|
1167
|
+
/**
|
|
1168
|
+
* Get file extension from MIME type.
|
|
1169
|
+
*/
|
|
1170
|
+
getExtension(mimeType) {
|
|
1171
|
+
return MIME_TO_EXTENSION[mimeType] ?? ".bin";
|
|
1172
|
+
}
|
|
1173
|
+
/**
|
|
1174
|
+
* Store media and return stored metadata with ID.
|
|
1175
|
+
*
|
|
1176
|
+
* @param media - The media output from a gadget
|
|
1177
|
+
* @param gadgetName - Name of the gadget that created this media
|
|
1178
|
+
* @returns Stored media information including generated ID
|
|
1179
|
+
* @throws Error if file write fails
|
|
1180
|
+
*/
|
|
1181
|
+
async store(media, gadgetName) {
|
|
1182
|
+
await this.ensureDir();
|
|
1183
|
+
const id = this.generateId();
|
|
1184
|
+
const ext = this.getExtension(media.mimeType);
|
|
1185
|
+
const filename = media.fileName ?? `${gadgetName}_${String(++this.counter).padStart(3, "0")}${ext}`;
|
|
1186
|
+
const filePath = join(this.outputDir, filename);
|
|
1187
|
+
const buffer = Buffer.from(media.data, "base64");
|
|
1188
|
+
try {
|
|
1189
|
+
await writeFile(filePath, buffer);
|
|
1190
|
+
} catch (error) {
|
|
1191
|
+
throw new Error(
|
|
1192
|
+
`MediaStore: Failed to write media file ${filePath}: ${error instanceof Error ? error.message : String(error)}`
|
|
1193
|
+
);
|
|
1194
|
+
}
|
|
1195
|
+
const stored = {
|
|
1196
|
+
id,
|
|
1197
|
+
kind: media.kind,
|
|
1198
|
+
path: filePath,
|
|
1199
|
+
mimeType: media.mimeType,
|
|
1200
|
+
sizeBytes: buffer.length,
|
|
1201
|
+
description: media.description,
|
|
1202
|
+
metadata: media.metadata,
|
|
1203
|
+
gadgetName,
|
|
1204
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
1205
|
+
};
|
|
1206
|
+
this.items.set(id, stored);
|
|
1207
|
+
return stored;
|
|
1208
|
+
}
|
|
1209
|
+
/**
|
|
1210
|
+
* Get stored media by ID.
|
|
1211
|
+
*
|
|
1212
|
+
* @param id - The media ID (e.g., "media_a1b2c3")
|
|
1213
|
+
* @returns The stored media or undefined if not found
|
|
1214
|
+
*/
|
|
1215
|
+
get(id) {
|
|
1216
|
+
return this.items.get(id);
|
|
1217
|
+
}
|
|
1218
|
+
/**
|
|
1219
|
+
* Get the actual file path for a media ID.
|
|
1220
|
+
* Convenience method for gadgets that need the raw path.
|
|
1221
|
+
*
|
|
1222
|
+
* @param id - The media ID
|
|
1223
|
+
* @returns The file path or undefined if not found
|
|
1224
|
+
*/
|
|
1225
|
+
getPath(id) {
|
|
1226
|
+
return this.items.get(id)?.path;
|
|
1227
|
+
}
|
|
1228
|
+
/**
|
|
1229
|
+
* List all stored media, optionally filtered by kind.
|
|
1230
|
+
*
|
|
1231
|
+
* @param kind - Optional media kind to filter by
|
|
1232
|
+
* @returns Array of stored media items
|
|
1233
|
+
*/
|
|
1234
|
+
list(kind) {
|
|
1235
|
+
const all = Array.from(this.items.values());
|
|
1236
|
+
if (kind) {
|
|
1237
|
+
return all.filter((item) => item.kind === kind);
|
|
1238
|
+
}
|
|
1239
|
+
return all;
|
|
1240
|
+
}
|
|
1241
|
+
/**
|
|
1242
|
+
* Get the count of stored media items.
|
|
1243
|
+
*/
|
|
1244
|
+
get size() {
|
|
1245
|
+
return this.items.size;
|
|
1246
|
+
}
|
|
1247
|
+
/**
|
|
1248
|
+
* Check if a media ID exists.
|
|
1249
|
+
*/
|
|
1250
|
+
has(id) {
|
|
1251
|
+
return this.items.has(id);
|
|
1252
|
+
}
|
|
1253
|
+
/**
|
|
1254
|
+
* Clear in-memory store without deleting files.
|
|
1255
|
+
* Resets the counter but leaves files on disk.
|
|
1256
|
+
*/
|
|
1257
|
+
clear() {
|
|
1258
|
+
this.items.clear();
|
|
1259
|
+
this.counter = 0;
|
|
1260
|
+
}
|
|
1261
|
+
/**
|
|
1262
|
+
* Delete all stored files and clear memory.
|
|
1263
|
+
* Removes the entire session directory.
|
|
1264
|
+
*/
|
|
1265
|
+
async cleanup() {
|
|
1266
|
+
if (this.initialized) {
|
|
1267
|
+
try {
|
|
1268
|
+
await rm(this.outputDir, { recursive: true, force: true });
|
|
1269
|
+
} catch {
|
|
1270
|
+
}
|
|
1271
|
+
this.initialized = false;
|
|
1272
|
+
}
|
|
1273
|
+
this.clear();
|
|
1274
|
+
}
|
|
1275
|
+
};
|
|
1276
|
+
}
|
|
1277
|
+
});
|
|
1278
|
+
|
|
1048
1279
|
// src/gadgets/exceptions.ts
|
|
1049
|
-
var
|
|
1280
|
+
var TaskCompletionSignal, HumanInputRequiredException, TimeoutException, AbortException;
|
|
1050
1281
|
var init_exceptions = __esm({
|
|
1051
1282
|
"src/gadgets/exceptions.ts"() {
|
|
1052
1283
|
"use strict";
|
|
1053
|
-
|
|
1284
|
+
TaskCompletionSignal = class extends Error {
|
|
1054
1285
|
constructor(message) {
|
|
1055
1286
|
super(message ?? "Agent loop terminated by gadget");
|
|
1056
|
-
this.name = "
|
|
1287
|
+
this.name = "TaskCompletionSignal";
|
|
1057
1288
|
}
|
|
1058
1289
|
};
|
|
1059
|
-
|
|
1290
|
+
HumanInputRequiredException = class extends Error {
|
|
1060
1291
|
question;
|
|
1061
1292
|
constructor(question) {
|
|
1062
1293
|
super(`Human input required: ${question}`);
|
|
1063
|
-
this.name = "
|
|
1294
|
+
this.name = "HumanInputRequiredException";
|
|
1064
1295
|
this.question = question;
|
|
1065
1296
|
}
|
|
1066
1297
|
};
|
|
@@ -1074,10 +1305,10 @@ var init_exceptions = __esm({
|
|
|
1074
1305
|
this.timeoutMs = timeoutMs;
|
|
1075
1306
|
}
|
|
1076
1307
|
};
|
|
1077
|
-
|
|
1308
|
+
AbortException = class extends Error {
|
|
1078
1309
|
constructor(message) {
|
|
1079
1310
|
super(message || "Gadget execution was aborted");
|
|
1080
|
-
this.name = "
|
|
1311
|
+
this.name = "AbortException";
|
|
1081
1312
|
}
|
|
1082
1313
|
};
|
|
1083
1314
|
}
|
|
@@ -1166,7 +1397,7 @@ var init_schema_to_json = __esm({
|
|
|
1166
1397
|
});
|
|
1167
1398
|
|
|
1168
1399
|
// src/gadgets/gadget.ts
|
|
1169
|
-
function
|
|
1400
|
+
function formatParamsForBlockExample(params, prefix = "", argPrefix = GADGET_ARG_PREFIX) {
|
|
1170
1401
|
const lines = [];
|
|
1171
1402
|
for (const [key, value] of Object.entries(params)) {
|
|
1172
1403
|
const fullPath = prefix ? `${prefix}/${key}` : key;
|
|
@@ -1174,14 +1405,14 @@ function formatParamsAsBlock(params, prefix = "", argPrefix = GADGET_ARG_PREFIX)
|
|
|
1174
1405
|
value.forEach((item, index) => {
|
|
1175
1406
|
const itemPath = `${fullPath}/${index}`;
|
|
1176
1407
|
if (typeof item === "object" && item !== null) {
|
|
1177
|
-
lines.push(
|
|
1408
|
+
lines.push(formatParamsForBlockExample(item, itemPath, argPrefix));
|
|
1178
1409
|
} else {
|
|
1179
1410
|
lines.push(`${argPrefix}${itemPath}`);
|
|
1180
1411
|
lines.push(String(item));
|
|
1181
1412
|
}
|
|
1182
1413
|
});
|
|
1183
1414
|
} else if (typeof value === "object" && value !== null) {
|
|
1184
|
-
lines.push(
|
|
1415
|
+
lines.push(formatParamsForBlockExample(value, fullPath, argPrefix));
|
|
1185
1416
|
} else {
|
|
1186
1417
|
lines.push(`${argPrefix}${fullPath}`);
|
|
1187
1418
|
lines.push(String(value));
|
|
@@ -1270,7 +1501,7 @@ function formatSchemaAsPlainText(schema, indent = "", atRoot = true) {
|
|
|
1270
1501
|
}
|
|
1271
1502
|
return lines.join("\n");
|
|
1272
1503
|
}
|
|
1273
|
-
var
|
|
1504
|
+
var AbstractGadget;
|
|
1274
1505
|
var init_gadget = __esm({
|
|
1275
1506
|
"src/gadgets/gadget.ts"() {
|
|
1276
1507
|
"use strict";
|
|
@@ -1278,7 +1509,7 @@ var init_gadget = __esm({
|
|
|
1278
1509
|
init_exceptions();
|
|
1279
1510
|
init_schema_to_json();
|
|
1280
1511
|
init_schema_validator();
|
|
1281
|
-
|
|
1512
|
+
AbstractGadget = class {
|
|
1282
1513
|
/**
|
|
1283
1514
|
* The name of the gadget. Used for identification when LLM calls it.
|
|
1284
1515
|
* If not provided, defaults to the class name.
|
|
@@ -1306,14 +1537,14 @@ var init_gadget = __esm({
|
|
|
1306
1537
|
*/
|
|
1307
1538
|
examples;
|
|
1308
1539
|
/**
|
|
1309
|
-
* Throws an
|
|
1540
|
+
* Throws an AbortException if the execution has been aborted.
|
|
1310
1541
|
*
|
|
1311
1542
|
* Call this at key checkpoints in long-running gadgets to allow early exit
|
|
1312
1543
|
* when the gadget has been cancelled (e.g., due to timeout). This enables
|
|
1313
1544
|
* resource cleanup and prevents unnecessary work after cancellation.
|
|
1314
1545
|
*
|
|
1315
1546
|
* @param ctx - The execution context containing the abort signal
|
|
1316
|
-
* @throws
|
|
1547
|
+
* @throws AbortException if ctx.signal.aborted is true
|
|
1317
1548
|
*
|
|
1318
1549
|
* @example
|
|
1319
1550
|
* ```typescript
|
|
@@ -1338,7 +1569,7 @@ var init_gadget = __esm({
|
|
|
1338
1569
|
*/
|
|
1339
1570
|
throwIfAborted(ctx) {
|
|
1340
1571
|
if (ctx?.signal?.aborted) {
|
|
1341
|
-
throw new
|
|
1572
|
+
throw new AbortException();
|
|
1342
1573
|
}
|
|
1343
1574
|
}
|
|
1344
1575
|
/**
|
|
@@ -1479,7 +1710,9 @@ var init_gadget = __esm({
|
|
|
1479
1710
|
parts.push(`# ${example.comment}`);
|
|
1480
1711
|
}
|
|
1481
1712
|
parts.push(`${effectiveStartPrefix}${gadgetName}`);
|
|
1482
|
-
parts.push(
|
|
1713
|
+
parts.push(
|
|
1714
|
+
formatParamsForBlockExample(example.params, "", effectiveArgPrefix)
|
|
1715
|
+
);
|
|
1483
1716
|
parts.push(effectiveEndPrefix);
|
|
1484
1717
|
if (example.output !== void 0) {
|
|
1485
1718
|
parts.push("");
|
|
@@ -1496,7 +1729,7 @@ var init_gadget = __esm({
|
|
|
1496
1729
|
|
|
1497
1730
|
// src/gadgets/create-gadget.ts
|
|
1498
1731
|
function createGadget(config) {
|
|
1499
|
-
class DynamicGadget extends
|
|
1732
|
+
class DynamicGadget extends AbstractGadget {
|
|
1500
1733
|
name = config.name;
|
|
1501
1734
|
description = config.description;
|
|
1502
1735
|
parameterSchema = config.schema;
|
|
@@ -1681,6 +1914,18 @@ var init_output_viewer = __esm({
|
|
|
1681
1914
|
}
|
|
1682
1915
|
});
|
|
1683
1916
|
|
|
1917
|
+
// src/agent/agent-internal-key.ts
|
|
1918
|
+
function isValidAgentKey(key) {
|
|
1919
|
+
return key === AGENT_INTERNAL_KEY;
|
|
1920
|
+
}
|
|
1921
|
+
var AGENT_INTERNAL_KEY;
|
|
1922
|
+
var init_agent_internal_key = __esm({
|
|
1923
|
+
"src/agent/agent-internal-key.ts"() {
|
|
1924
|
+
"use strict";
|
|
1925
|
+
AGENT_INTERNAL_KEY = Symbol("AGENT_INTERNAL_KEY");
|
|
1926
|
+
}
|
|
1927
|
+
});
|
|
1928
|
+
|
|
1684
1929
|
// src/agent/compaction/config.ts
|
|
1685
1930
|
function resolveCompactionConfig(config = {}) {
|
|
1686
1931
|
const trigger = config.triggerThresholdPercent ?? DEFAULT_COMPACTION_CONFIG.triggerThresholdPercent;
|
|
@@ -1928,9 +2173,9 @@ var init_hybrid = __esm({
|
|
|
1928
2173
|
var init_strategies = __esm({
|
|
1929
2174
|
"src/agent/compaction/strategies/index.ts"() {
|
|
1930
2175
|
"use strict";
|
|
2176
|
+
init_hybrid();
|
|
1931
2177
|
init_sliding_window();
|
|
1932
2178
|
init_summarization();
|
|
1933
|
-
init_hybrid();
|
|
1934
2179
|
}
|
|
1935
2180
|
});
|
|
1936
2181
|
|
|
@@ -2092,98 +2337,6 @@ var init_manager = __esm({
|
|
|
2092
2337
|
}
|
|
2093
2338
|
});
|
|
2094
2339
|
|
|
2095
|
-
// src/agent/gadget-output-store.ts
|
|
2096
|
-
import { randomBytes } from "node:crypto";
|
|
2097
|
-
var GadgetOutputStore;
|
|
2098
|
-
var init_gadget_output_store = __esm({
|
|
2099
|
-
"src/agent/gadget-output-store.ts"() {
|
|
2100
|
-
"use strict";
|
|
2101
|
-
GadgetOutputStore = class {
|
|
2102
|
-
outputs = /* @__PURE__ */ new Map();
|
|
2103
|
-
/**
|
|
2104
|
-
* Store a gadget output and return its ID.
|
|
2105
|
-
*
|
|
2106
|
-
* @param gadgetName - Name of the gadget that produced the output
|
|
2107
|
-
* @param content - Full output content to store
|
|
2108
|
-
* @returns Generated ID for retrieving the output later
|
|
2109
|
-
*/
|
|
2110
|
-
store(gadgetName, content) {
|
|
2111
|
-
const id = this.generateId(gadgetName);
|
|
2112
|
-
const encoder = new TextEncoder();
|
|
2113
|
-
const stored = {
|
|
2114
|
-
id,
|
|
2115
|
-
gadgetName,
|
|
2116
|
-
content,
|
|
2117
|
-
byteSize: encoder.encode(content).length,
|
|
2118
|
-
lineCount: content.split("\n").length,
|
|
2119
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
2120
|
-
};
|
|
2121
|
-
this.outputs.set(id, stored);
|
|
2122
|
-
return id;
|
|
2123
|
-
}
|
|
2124
|
-
/**
|
|
2125
|
-
* Retrieve a stored output by ID.
|
|
2126
|
-
*
|
|
2127
|
-
* @param id - The output ID (e.g., "Search_d34db33f")
|
|
2128
|
-
* @returns The stored output or undefined if not found
|
|
2129
|
-
*/
|
|
2130
|
-
get(id) {
|
|
2131
|
-
return this.outputs.get(id);
|
|
2132
|
-
}
|
|
2133
|
-
/**
|
|
2134
|
-
* Check if an output exists.
|
|
2135
|
-
*
|
|
2136
|
-
* @param id - The output ID to check
|
|
2137
|
-
* @returns True if the output exists
|
|
2138
|
-
*/
|
|
2139
|
-
has(id) {
|
|
2140
|
-
return this.outputs.has(id);
|
|
2141
|
-
}
|
|
2142
|
-
/**
|
|
2143
|
-
* Get all stored output IDs.
|
|
2144
|
-
*
|
|
2145
|
-
* @returns Array of output IDs
|
|
2146
|
-
*/
|
|
2147
|
-
getIds() {
|
|
2148
|
-
return Array.from(this.outputs.keys());
|
|
2149
|
-
}
|
|
2150
|
-
/**
|
|
2151
|
-
* Get the number of stored outputs.
|
|
2152
|
-
*/
|
|
2153
|
-
get size() {
|
|
2154
|
-
return this.outputs.size;
|
|
2155
|
-
}
|
|
2156
|
-
/**
|
|
2157
|
-
* Clear all stored outputs.
|
|
2158
|
-
* Called when the agent run completes.
|
|
2159
|
-
*/
|
|
2160
|
-
clear() {
|
|
2161
|
-
this.outputs.clear();
|
|
2162
|
-
}
|
|
2163
|
-
/**
|
|
2164
|
-
* Generate a unique ID for a stored output.
|
|
2165
|
-
* Format: {GadgetName}_{8 hex chars}
|
|
2166
|
-
*/
|
|
2167
|
-
generateId(gadgetName) {
|
|
2168
|
-
const hex = randomBytes(4).toString("hex");
|
|
2169
|
-
return `${gadgetName}_${hex}`;
|
|
2170
|
-
}
|
|
2171
|
-
};
|
|
2172
|
-
}
|
|
2173
|
-
});
|
|
2174
|
-
|
|
2175
|
-
// src/agent/agent-internal-key.ts
|
|
2176
|
-
function isValidAgentKey(key) {
|
|
2177
|
-
return key === AGENT_INTERNAL_KEY;
|
|
2178
|
-
}
|
|
2179
|
-
var AGENT_INTERNAL_KEY;
|
|
2180
|
-
var init_agent_internal_key = __esm({
|
|
2181
|
-
"src/agent/agent-internal-key.ts"() {
|
|
2182
|
-
"use strict";
|
|
2183
|
-
AGENT_INTERNAL_KEY = Symbol("AGENT_INTERNAL_KEY");
|
|
2184
|
-
}
|
|
2185
|
-
});
|
|
2186
|
-
|
|
2187
2340
|
// src/agent/conversation-manager.ts
|
|
2188
2341
|
var ConversationManager;
|
|
2189
2342
|
var init_conversation_manager = __esm({
|
|
@@ -2214,8 +2367,8 @@ var init_conversation_manager = __esm({
|
|
|
2214
2367
|
addAssistantMessage(content) {
|
|
2215
2368
|
this.historyBuilder.addAssistant(content);
|
|
2216
2369
|
}
|
|
2217
|
-
|
|
2218
|
-
this.historyBuilder.
|
|
2370
|
+
addGadgetCallResult(gadgetName, parameters, result, media, mediaIds) {
|
|
2371
|
+
this.historyBuilder.addGadgetCallResult(gadgetName, parameters, result, media, mediaIds);
|
|
2219
2372
|
}
|
|
2220
2373
|
getMessages() {
|
|
2221
2374
|
return [...this.baseMessages, ...this.initialMessages, ...this.historyBuilder.build()];
|
|
@@ -2235,7 +2388,7 @@ var init_conversation_manager = __esm({
|
|
|
2235
2388
|
if (msg.role === "user") {
|
|
2236
2389
|
this.historyBuilder.addUser(msg.content);
|
|
2237
2390
|
} else if (msg.role === "assistant") {
|
|
2238
|
-
this.historyBuilder.addAssistant(
|
|
2391
|
+
this.historyBuilder.addAssistant(extractMessageText(msg.content));
|
|
2239
2392
|
}
|
|
2240
2393
|
}
|
|
2241
2394
|
}
|
|
@@ -2329,67 +2482,147 @@ var init_event_handlers = __esm({
|
|
|
2329
2482
|
}
|
|
2330
2483
|
});
|
|
2331
2484
|
|
|
2332
|
-
// src/agent/
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
}
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
}
|
|
2485
|
+
// src/agent/gadget-output-store.ts
|
|
2486
|
+
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
2487
|
+
var GadgetOutputStore;
|
|
2488
|
+
var init_gadget_output_store = __esm({
|
|
2489
|
+
"src/agent/gadget-output-store.ts"() {
|
|
2490
|
+
"use strict";
|
|
2491
|
+
GadgetOutputStore = class {
|
|
2492
|
+
outputs = /* @__PURE__ */ new Map();
|
|
2493
|
+
/**
|
|
2494
|
+
* Store a gadget output and return its ID.
|
|
2495
|
+
*
|
|
2496
|
+
* @param gadgetName - Name of the gadget that produced the output
|
|
2497
|
+
* @param content - Full output content to store
|
|
2498
|
+
* @returns Generated ID for retrieving the output later
|
|
2499
|
+
*/
|
|
2500
|
+
store(gadgetName, content) {
|
|
2501
|
+
const id = this.generateId(gadgetName);
|
|
2502
|
+
const encoder = new TextEncoder();
|
|
2503
|
+
const stored = {
|
|
2504
|
+
id,
|
|
2505
|
+
gadgetName,
|
|
2506
|
+
content,
|
|
2507
|
+
byteSize: encoder.encode(content).length,
|
|
2508
|
+
lineCount: content.split("\n").length,
|
|
2509
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
2510
|
+
};
|
|
2511
|
+
this.outputs.set(id, stored);
|
|
2512
|
+
return id;
|
|
2513
|
+
}
|
|
2514
|
+
/**
|
|
2515
|
+
* Retrieve a stored output by ID.
|
|
2516
|
+
*
|
|
2517
|
+
* @param id - The output ID (e.g., "Search_d34db33f")
|
|
2518
|
+
* @returns The stored output or undefined if not found
|
|
2519
|
+
*/
|
|
2520
|
+
get(id) {
|
|
2521
|
+
return this.outputs.get(id);
|
|
2522
|
+
}
|
|
2523
|
+
/**
|
|
2524
|
+
* Check if an output exists.
|
|
2525
|
+
*
|
|
2526
|
+
* @param id - The output ID to check
|
|
2527
|
+
* @returns True if the output exists
|
|
2528
|
+
*/
|
|
2529
|
+
has(id) {
|
|
2530
|
+
return this.outputs.has(id);
|
|
2531
|
+
}
|
|
2532
|
+
/**
|
|
2533
|
+
* Get all stored output IDs.
|
|
2534
|
+
*
|
|
2535
|
+
* @returns Array of output IDs
|
|
2536
|
+
*/
|
|
2537
|
+
getIds() {
|
|
2538
|
+
return Array.from(this.outputs.keys());
|
|
2539
|
+
}
|
|
2540
|
+
/**
|
|
2541
|
+
* Get the number of stored outputs.
|
|
2542
|
+
*/
|
|
2543
|
+
get size() {
|
|
2544
|
+
return this.outputs.size;
|
|
2545
|
+
}
|
|
2546
|
+
/**
|
|
2547
|
+
* Clear all stored outputs.
|
|
2548
|
+
* Called when the agent run completes.
|
|
2549
|
+
*/
|
|
2550
|
+
clear() {
|
|
2551
|
+
this.outputs.clear();
|
|
2552
|
+
}
|
|
2553
|
+
/**
|
|
2554
|
+
* Generate a unique ID for a stored output.
|
|
2555
|
+
* Format: {GadgetName}_{8 hex chars}
|
|
2556
|
+
*/
|
|
2557
|
+
generateId(gadgetName) {
|
|
2558
|
+
const hex = randomBytes2(4).toString("hex");
|
|
2559
|
+
return `${gadgetName}_${hex}`;
|
|
2560
|
+
}
|
|
2561
|
+
};
|
|
2562
|
+
}
|
|
2563
|
+
});
|
|
2564
|
+
|
|
2565
|
+
// src/agent/hook-validators.ts
|
|
2566
|
+
function validateBeforeLLMCallAction(action) {
|
|
2567
|
+
if (!action || typeof action !== "object" || !("action" in action)) {
|
|
2568
|
+
throw new HookValidationError(
|
|
2569
|
+
"beforeLLMCall",
|
|
2570
|
+
"Must return an action object with an 'action' field"
|
|
2571
|
+
);
|
|
2572
|
+
}
|
|
2573
|
+
const actionType = action.action;
|
|
2574
|
+
if (actionType !== "proceed" && actionType !== "skip") {
|
|
2575
|
+
throw new HookValidationError(
|
|
2576
|
+
"beforeLLMCall",
|
|
2577
|
+
`Invalid action type: ${actionType}. Must be 'proceed' or 'skip'`
|
|
2578
|
+
);
|
|
2579
|
+
}
|
|
2580
|
+
if (actionType === "skip" && !action.syntheticResponse) {
|
|
2581
|
+
throw new HookValidationError(
|
|
2582
|
+
"beforeLLMCall",
|
|
2583
|
+
"When action is 'skip', syntheticResponse is required"
|
|
2584
|
+
);
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
function validateAfterLLMCallAction(action) {
|
|
2588
|
+
if (!action || typeof action !== "object" || !("action" in action)) {
|
|
2589
|
+
throw new HookValidationError(
|
|
2590
|
+
"afterLLMCall",
|
|
2591
|
+
"Must return an action object with an 'action' field"
|
|
2592
|
+
);
|
|
2593
|
+
}
|
|
2594
|
+
const actionType = action.action;
|
|
2595
|
+
const validActions = ["continue", "append_messages", "modify_and_continue", "append_and_modify"];
|
|
2596
|
+
if (!validActions.includes(actionType)) {
|
|
2597
|
+
throw new HookValidationError(
|
|
2598
|
+
"afterLLMCall",
|
|
2599
|
+
`Invalid action type: ${actionType}. Must be one of: ${validActions.join(", ")}`
|
|
2600
|
+
);
|
|
2601
|
+
}
|
|
2602
|
+
if (actionType === "append_messages" || actionType === "append_and_modify") {
|
|
2603
|
+
if (!("messages" in action) || !action.messages || !Array.isArray(action.messages)) {
|
|
2604
|
+
throw new HookValidationError(
|
|
2605
|
+
"afterLLMCall",
|
|
2606
|
+
`When action is '${actionType}', messages array is required`
|
|
2607
|
+
);
|
|
2608
|
+
}
|
|
2609
|
+
if (action.messages.length === 0) {
|
|
2610
|
+
throw new HookValidationError(
|
|
2611
|
+
"afterLLMCall",
|
|
2612
|
+
`When action is '${actionType}', messages array must not be empty`
|
|
2613
|
+
);
|
|
2614
|
+
}
|
|
2615
|
+
for (let i = 0; i < action.messages.length; i++) {
|
|
2616
|
+
const msg = action.messages[i];
|
|
2617
|
+
if (!msg || typeof msg !== "object") {
|
|
2618
|
+
throw new HookValidationError("afterLLMCall", `Message at index ${i} must be an object`);
|
|
2619
|
+
}
|
|
2620
|
+
if (!msg.role || !msg.content) {
|
|
2621
|
+
throw new HookValidationError(
|
|
2622
|
+
"afterLLMCall",
|
|
2623
|
+
`Message at index ${i} must have 'role' and 'content' fields`
|
|
2624
|
+
);
|
|
2625
|
+
}
|
|
2393
2626
|
if (!["system", "user", "assistant"].includes(msg.role)) {
|
|
2394
2627
|
throw new HookValidationError(
|
|
2395
2628
|
"afterLLMCall",
|
|
@@ -2639,8 +2872,7 @@ var init_schema_introspector = __esm({
|
|
|
2639
2872
|
const values = def?.values;
|
|
2640
2873
|
const value = values?.[0] ?? def?.value;
|
|
2641
2874
|
if (typeof value === "string") return "string";
|
|
2642
|
-
if (typeof value === "number" || typeof value === "bigint")
|
|
2643
|
-
return "number";
|
|
2875
|
+
if (typeof value === "number" || typeof value === "bigint") return "number";
|
|
2644
2876
|
if (typeof value === "boolean") return "boolean";
|
|
2645
2877
|
return "unknown";
|
|
2646
2878
|
}
|
|
@@ -2912,7 +3144,13 @@ var init_cost_reporting_client = __esm({
|
|
|
2912
3144
|
cacheCreationInputTokens = chunk.usage.cacheCreationInputTokens ?? 0;
|
|
2913
3145
|
}
|
|
2914
3146
|
}
|
|
2915
|
-
this.reportCostFromUsage(
|
|
3147
|
+
this.reportCostFromUsage(
|
|
3148
|
+
model,
|
|
3149
|
+
inputTokens,
|
|
3150
|
+
outputTokens,
|
|
3151
|
+
cachedInputTokens,
|
|
3152
|
+
cacheCreationInputTokens
|
|
3153
|
+
);
|
|
2916
3154
|
return result;
|
|
2917
3155
|
}
|
|
2918
3156
|
/**
|
|
@@ -2952,7 +3190,13 @@ var init_cost_reporting_client = __esm({
|
|
|
2952
3190
|
}
|
|
2953
3191
|
}
|
|
2954
3192
|
} finally {
|
|
2955
|
-
this.reportCostFromUsage(
|
|
3193
|
+
this.reportCostFromUsage(
|
|
3194
|
+
model,
|
|
3195
|
+
inputTokens,
|
|
3196
|
+
outputTokens,
|
|
3197
|
+
cachedInputTokens,
|
|
3198
|
+
cacheCreationInputTokens
|
|
3199
|
+
);
|
|
2956
3200
|
}
|
|
2957
3201
|
}
|
|
2958
3202
|
/**
|
|
@@ -2990,7 +3234,13 @@ var init_cost_reporting_client = __esm({
|
|
|
2990
3234
|
}
|
|
2991
3235
|
} finally {
|
|
2992
3236
|
if (inputTokens > 0 || outputTokens > 0) {
|
|
2993
|
-
reportCostFromUsage(
|
|
3237
|
+
reportCostFromUsage(
|
|
3238
|
+
model,
|
|
3239
|
+
inputTokens,
|
|
3240
|
+
outputTokens,
|
|
3241
|
+
cachedInputTokens,
|
|
3242
|
+
cacheCreationInputTokens
|
|
3243
|
+
);
|
|
2994
3244
|
}
|
|
2995
3245
|
}
|
|
2996
3246
|
}
|
|
@@ -3018,12 +3268,12 @@ var init_cost_reporting_client = __esm({
|
|
|
3018
3268
|
});
|
|
3019
3269
|
|
|
3020
3270
|
// src/gadgets/error-formatter.ts
|
|
3021
|
-
var
|
|
3271
|
+
var GadgetExecutionErrorFormatter;
|
|
3022
3272
|
var init_error_formatter = __esm({
|
|
3023
3273
|
"src/gadgets/error-formatter.ts"() {
|
|
3024
3274
|
"use strict";
|
|
3025
3275
|
init_constants();
|
|
3026
|
-
|
|
3276
|
+
GadgetExecutionErrorFormatter = class {
|
|
3027
3277
|
argPrefix;
|
|
3028
3278
|
startPrefix;
|
|
3029
3279
|
endPrefix;
|
|
@@ -3109,16 +3359,16 @@ function stripMarkdownFences(content) {
|
|
|
3109
3359
|
cleaned = cleaned.replace(closingFence, "");
|
|
3110
3360
|
return cleaned.trim();
|
|
3111
3361
|
}
|
|
3112
|
-
var globalInvocationCounter,
|
|
3362
|
+
var globalInvocationCounter, GadgetCallParser;
|
|
3113
3363
|
var init_parser = __esm({
|
|
3114
3364
|
"src/gadgets/parser.ts"() {
|
|
3115
3365
|
"use strict";
|
|
3116
3366
|
init_constants();
|
|
3117
3367
|
init_block_params();
|
|
3118
3368
|
globalInvocationCounter = 0;
|
|
3119
|
-
|
|
3369
|
+
GadgetCallParser = class {
|
|
3120
3370
|
buffer = "";
|
|
3121
|
-
|
|
3371
|
+
lastEmittedTextOffset = 0;
|
|
3122
3372
|
startPrefix;
|
|
3123
3373
|
endPrefix;
|
|
3124
3374
|
argPrefix;
|
|
@@ -3127,16 +3377,20 @@ var init_parser = __esm({
|
|
|
3127
3377
|
this.endPrefix = options.endPrefix ?? GADGET_END_PREFIX;
|
|
3128
3378
|
this.argPrefix = options.argPrefix ?? GADGET_ARG_PREFIX;
|
|
3129
3379
|
}
|
|
3130
|
-
|
|
3131
|
-
|
|
3380
|
+
/**
|
|
3381
|
+
* Extract and consume text up to the given index.
|
|
3382
|
+
* Returns undefined if no meaningful text to emit.
|
|
3383
|
+
*/
|
|
3384
|
+
extractTextSegment(index) {
|
|
3385
|
+
if (index <= this.lastEmittedTextOffset) {
|
|
3132
3386
|
return void 0;
|
|
3133
3387
|
}
|
|
3134
|
-
const segment = this.buffer.slice(this.
|
|
3135
|
-
this.
|
|
3388
|
+
const segment = this.buffer.slice(this.lastEmittedTextOffset, index);
|
|
3389
|
+
this.lastEmittedTextOffset = index;
|
|
3136
3390
|
return segment.trim().length > 0 ? segment : void 0;
|
|
3137
3391
|
}
|
|
3138
3392
|
/**
|
|
3139
|
-
* Parse gadget
|
|
3393
|
+
* Parse gadget invocation metadata from the header line.
|
|
3140
3394
|
*
|
|
3141
3395
|
* Supported formats:
|
|
3142
3396
|
* - `GadgetName` - Auto-generate ID, no dependencies
|
|
@@ -3145,24 +3399,24 @@ var init_parser = __esm({
|
|
|
3145
3399
|
*
|
|
3146
3400
|
* Dependencies must be comma-separated invocation IDs.
|
|
3147
3401
|
*/
|
|
3148
|
-
|
|
3149
|
-
const parts =
|
|
3402
|
+
parseInvocationMetadata(headerLine) {
|
|
3403
|
+
const parts = headerLine.split(":");
|
|
3150
3404
|
if (parts.length === 1) {
|
|
3151
3405
|
return {
|
|
3152
|
-
|
|
3406
|
+
gadgetName: parts[0],
|
|
3153
3407
|
invocationId: `gadget_${++globalInvocationCounter}`,
|
|
3154
3408
|
dependencies: []
|
|
3155
3409
|
};
|
|
3156
3410
|
} else if (parts.length === 2) {
|
|
3157
3411
|
return {
|
|
3158
|
-
|
|
3412
|
+
gadgetName: parts[0],
|
|
3159
3413
|
invocationId: parts[1].trim(),
|
|
3160
3414
|
dependencies: []
|
|
3161
3415
|
};
|
|
3162
3416
|
} else {
|
|
3163
3417
|
const deps = parts[2].split(",").map((d) => d.trim()).filter((d) => d.length > 0);
|
|
3164
3418
|
return {
|
|
3165
|
-
|
|
3419
|
+
gadgetName: parts[0],
|
|
3166
3420
|
invocationId: parts[1].trim(),
|
|
3167
3421
|
dependencies: deps
|
|
3168
3422
|
};
|
|
@@ -3194,15 +3448,15 @@ var init_parser = __esm({
|
|
|
3194
3448
|
while (true) {
|
|
3195
3449
|
const partStartIndex = this.buffer.indexOf(this.startPrefix, startIndex);
|
|
3196
3450
|
if (partStartIndex === -1) break;
|
|
3197
|
-
const textBefore = this.
|
|
3451
|
+
const textBefore = this.extractTextSegment(partStartIndex);
|
|
3198
3452
|
if (textBefore !== void 0) {
|
|
3199
3453
|
yield { type: "text", content: textBefore };
|
|
3200
3454
|
}
|
|
3201
3455
|
const metadataStartIndex = partStartIndex + this.startPrefix.length;
|
|
3202
3456
|
const metadataEndIndex = this.buffer.indexOf("\n", metadataStartIndex);
|
|
3203
3457
|
if (metadataEndIndex === -1) break;
|
|
3204
|
-
const
|
|
3205
|
-
const {
|
|
3458
|
+
const headerLine = this.buffer.substring(metadataStartIndex, metadataEndIndex).trim();
|
|
3459
|
+
const { gadgetName, invocationId, dependencies } = this.parseInvocationMetadata(headerLine);
|
|
3206
3460
|
const contentStartIndex = metadataEndIndex + 1;
|
|
3207
3461
|
let partEndIndex;
|
|
3208
3462
|
let endMarkerLength = 0;
|
|
@@ -3222,7 +3476,7 @@ var init_parser = __esm({
|
|
|
3222
3476
|
yield {
|
|
3223
3477
|
type: "gadget_call",
|
|
3224
3478
|
call: {
|
|
3225
|
-
gadgetName
|
|
3479
|
+
gadgetName,
|
|
3226
3480
|
invocationId,
|
|
3227
3481
|
parametersRaw,
|
|
3228
3482
|
parameters,
|
|
@@ -3231,33 +3485,33 @@ var init_parser = __esm({
|
|
|
3231
3485
|
}
|
|
3232
3486
|
};
|
|
3233
3487
|
startIndex = partEndIndex + endMarkerLength;
|
|
3234
|
-
this.
|
|
3488
|
+
this.lastEmittedTextOffset = startIndex;
|
|
3235
3489
|
}
|
|
3236
3490
|
if (startIndex > 0) {
|
|
3237
3491
|
this.buffer = this.buffer.substring(startIndex);
|
|
3238
|
-
this.
|
|
3492
|
+
this.lastEmittedTextOffset = 0;
|
|
3239
3493
|
}
|
|
3240
3494
|
}
|
|
3241
3495
|
// Finalize parsing and return remaining text or incomplete gadgets
|
|
3242
3496
|
*finalize() {
|
|
3243
|
-
const startIndex = this.buffer.indexOf(this.startPrefix, this.
|
|
3497
|
+
const startIndex = this.buffer.indexOf(this.startPrefix, this.lastEmittedTextOffset);
|
|
3244
3498
|
if (startIndex !== -1) {
|
|
3245
|
-
const textBefore = this.
|
|
3499
|
+
const textBefore = this.extractTextSegment(startIndex);
|
|
3246
3500
|
if (textBefore !== void 0) {
|
|
3247
3501
|
yield { type: "text", content: textBefore };
|
|
3248
3502
|
}
|
|
3249
3503
|
const metadataStartIndex = startIndex + this.startPrefix.length;
|
|
3250
3504
|
const metadataEndIndex = this.buffer.indexOf("\n", metadataStartIndex);
|
|
3251
3505
|
if (metadataEndIndex !== -1) {
|
|
3252
|
-
const
|
|
3253
|
-
const {
|
|
3506
|
+
const headerLine = this.buffer.substring(metadataStartIndex, metadataEndIndex).trim();
|
|
3507
|
+
const { gadgetName, invocationId, dependencies } = this.parseInvocationMetadata(headerLine);
|
|
3254
3508
|
const contentStartIndex = metadataEndIndex + 1;
|
|
3255
3509
|
const parametersRaw = this.buffer.substring(contentStartIndex).trim();
|
|
3256
3510
|
const { parameters, parseError } = this.parseParameters(parametersRaw);
|
|
3257
3511
|
yield {
|
|
3258
3512
|
type: "gadget_call",
|
|
3259
3513
|
call: {
|
|
3260
|
-
gadgetName
|
|
3514
|
+
gadgetName,
|
|
3261
3515
|
invocationId,
|
|
3262
3516
|
parametersRaw,
|
|
3263
3517
|
parameters,
|
|
@@ -3268,7 +3522,7 @@ var init_parser = __esm({
|
|
|
3268
3522
|
return;
|
|
3269
3523
|
}
|
|
3270
3524
|
}
|
|
3271
|
-
const remainingText = this.
|
|
3525
|
+
const remainingText = this.extractTextSegment(this.buffer.length);
|
|
3272
3526
|
if (remainingText !== void 0) {
|
|
3273
3527
|
yield { type: "text", content: remainingText };
|
|
3274
3528
|
}
|
|
@@ -3276,7 +3530,7 @@ var init_parser = __esm({
|
|
|
3276
3530
|
// Reset parser state (note: global invocation counter is NOT reset to ensure unique IDs)
|
|
3277
3531
|
reset() {
|
|
3278
3532
|
this.buffer = "";
|
|
3279
|
-
this.
|
|
3533
|
+
this.lastEmittedTextOffset = 0;
|
|
3280
3534
|
}
|
|
3281
3535
|
};
|
|
3282
3536
|
}
|
|
@@ -3295,13 +3549,16 @@ var init_executor = __esm({
|
|
|
3295
3549
|
init_exceptions();
|
|
3296
3550
|
init_parser();
|
|
3297
3551
|
GadgetExecutor = class {
|
|
3298
|
-
constructor(registry,
|
|
3552
|
+
constructor(registry, requestHumanInput, logger, defaultGadgetTimeoutMs, errorFormatterOptions, client, mediaStore, agentConfig, subagentConfig) {
|
|
3299
3553
|
this.registry = registry;
|
|
3300
|
-
this.
|
|
3554
|
+
this.requestHumanInput = requestHumanInput;
|
|
3301
3555
|
this.defaultGadgetTimeoutMs = defaultGadgetTimeoutMs;
|
|
3302
3556
|
this.client = client;
|
|
3557
|
+
this.mediaStore = mediaStore;
|
|
3558
|
+
this.agentConfig = agentConfig;
|
|
3559
|
+
this.subagentConfig = subagentConfig;
|
|
3303
3560
|
this.logger = logger ?? createLogger({ name: "llmist:executor" });
|
|
3304
|
-
this.errorFormatter = new
|
|
3561
|
+
this.errorFormatter = new GadgetExecutionErrorFormatter(errorFormatterOptions);
|
|
3305
3562
|
this.argPrefix = errorFormatterOptions?.argPrefix ?? GADGET_ARG_PREFIX;
|
|
3306
3563
|
}
|
|
3307
3564
|
logger;
|
|
@@ -3321,13 +3578,17 @@ var init_executor = __esm({
|
|
|
3321
3578
|
});
|
|
3322
3579
|
}
|
|
3323
3580
|
/**
|
|
3324
|
-
*
|
|
3325
|
-
* Handles
|
|
3581
|
+
* Unify gadget execute result to consistent internal format.
|
|
3582
|
+
* Handles string returns (backwards compat), object returns with cost,
|
|
3583
|
+
* and object returns with media.
|
|
3326
3584
|
*/
|
|
3327
|
-
|
|
3585
|
+
unifyExecuteResult(raw) {
|
|
3328
3586
|
if (typeof raw === "string") {
|
|
3329
3587
|
return { result: raw, cost: 0 };
|
|
3330
3588
|
}
|
|
3589
|
+
if ("media" in raw && raw.media) {
|
|
3590
|
+
return { result: raw.result, media: raw.media, cost: raw.cost ?? 0 };
|
|
3591
|
+
}
|
|
3331
3592
|
return { result: raw.result, cost: raw.cost ?? 0 };
|
|
3332
3593
|
}
|
|
3333
3594
|
// Execute a gadget call asynchronously
|
|
@@ -3439,7 +3700,9 @@ var init_executor = __esm({
|
|
|
3439
3700
|
const ctx = {
|
|
3440
3701
|
reportCost,
|
|
3441
3702
|
llmist: this.client ? new CostReportingLLMistWrapper(this.client, reportCost) : void 0,
|
|
3442
|
-
signal: abortController.signal
|
|
3703
|
+
signal: abortController.signal,
|
|
3704
|
+
agentConfig: this.agentConfig,
|
|
3705
|
+
subagentConfig: this.subagentConfig
|
|
3443
3706
|
};
|
|
3444
3707
|
let rawResult;
|
|
3445
3708
|
if (timeoutMs && timeoutMs > 0) {
|
|
@@ -3454,8 +3717,21 @@ var init_executor = __esm({
|
|
|
3454
3717
|
} else {
|
|
3455
3718
|
rawResult = await Promise.resolve(gadget.execute(validatedParameters, ctx));
|
|
3456
3719
|
}
|
|
3457
|
-
const { result, cost: returnCost } = this.
|
|
3720
|
+
const { result, media, cost: returnCost } = this.unifyExecuteResult(rawResult);
|
|
3458
3721
|
const totalCost = callbackCost + returnCost;
|
|
3722
|
+
let mediaIds;
|
|
3723
|
+
let storedMedia;
|
|
3724
|
+
if (media && media.length > 0 && this.mediaStore) {
|
|
3725
|
+
storedMedia = await Promise.all(
|
|
3726
|
+
media.map((item) => this.mediaStore.store(item, call.gadgetName))
|
|
3727
|
+
);
|
|
3728
|
+
mediaIds = storedMedia.map((m) => m.id);
|
|
3729
|
+
this.logger.debug("Stored media outputs", {
|
|
3730
|
+
gadgetName: call.gadgetName,
|
|
3731
|
+
mediaIds,
|
|
3732
|
+
count: media.length
|
|
3733
|
+
});
|
|
3734
|
+
}
|
|
3459
3735
|
const executionTimeMs = Date.now() - startTime;
|
|
3460
3736
|
this.logger.info("Gadget executed successfully", {
|
|
3461
3737
|
gadgetName: call.gadgetName,
|
|
@@ -3463,7 +3739,8 @@ var init_executor = __esm({
|
|
|
3463
3739
|
executionTimeMs,
|
|
3464
3740
|
cost: totalCost > 0 ? totalCost : void 0,
|
|
3465
3741
|
callbackCost: callbackCost > 0 ? callbackCost : void 0,
|
|
3466
|
-
returnCost: returnCost > 0 ? returnCost : void 0
|
|
3742
|
+
returnCost: returnCost > 0 ? returnCost : void 0,
|
|
3743
|
+
mediaCount: media?.length
|
|
3467
3744
|
});
|
|
3468
3745
|
this.logger.debug("Gadget result", {
|
|
3469
3746
|
gadgetName: call.gadgetName,
|
|
@@ -3471,7 +3748,8 @@ var init_executor = __esm({
|
|
|
3471
3748
|
parameters: validatedParameters,
|
|
3472
3749
|
result,
|
|
3473
3750
|
cost: totalCost,
|
|
3474
|
-
executionTimeMs
|
|
3751
|
+
executionTimeMs,
|
|
3752
|
+
mediaIds
|
|
3475
3753
|
});
|
|
3476
3754
|
return {
|
|
3477
3755
|
gadgetName: call.gadgetName,
|
|
@@ -3479,10 +3757,13 @@ var init_executor = __esm({
|
|
|
3479
3757
|
parameters: validatedParameters,
|
|
3480
3758
|
result,
|
|
3481
3759
|
executionTimeMs,
|
|
3482
|
-
cost: totalCost
|
|
3760
|
+
cost: totalCost,
|
|
3761
|
+
media,
|
|
3762
|
+
mediaIds,
|
|
3763
|
+
storedMedia
|
|
3483
3764
|
};
|
|
3484
3765
|
} catch (error) {
|
|
3485
|
-
if (error instanceof
|
|
3766
|
+
if (error instanceof TaskCompletionSignal) {
|
|
3486
3767
|
this.logger.info("Gadget requested loop termination", {
|
|
3487
3768
|
gadgetName: call.gadgetName,
|
|
3488
3769
|
message: error.message
|
|
@@ -3510,7 +3791,7 @@ var init_executor = __esm({
|
|
|
3510
3791
|
executionTimeMs: Date.now() - startTime
|
|
3511
3792
|
};
|
|
3512
3793
|
}
|
|
3513
|
-
if (error instanceof
|
|
3794
|
+
if (error instanceof AbortException) {
|
|
3514
3795
|
this.logger.info("Gadget execution was aborted", {
|
|
3515
3796
|
gadgetName: call.gadgetName,
|
|
3516
3797
|
executionTimeMs: Date.now() - startTime
|
|
@@ -3523,14 +3804,14 @@ var init_executor = __esm({
|
|
|
3523
3804
|
executionTimeMs: Date.now() - startTime
|
|
3524
3805
|
};
|
|
3525
3806
|
}
|
|
3526
|
-
if (error instanceof
|
|
3807
|
+
if (error instanceof HumanInputRequiredException) {
|
|
3527
3808
|
this.logger.info("Gadget requested human input", {
|
|
3528
3809
|
gadgetName: call.gadgetName,
|
|
3529
3810
|
question: error.question
|
|
3530
3811
|
});
|
|
3531
|
-
if (this.
|
|
3812
|
+
if (this.requestHumanInput) {
|
|
3532
3813
|
try {
|
|
3533
|
-
const answer = await this.
|
|
3814
|
+
const answer = await this.requestHumanInput(error.question);
|
|
3534
3815
|
this.logger.debug("Human input received", {
|
|
3535
3816
|
gadgetName: call.gadgetName,
|
|
3536
3817
|
answerLength: answer.length
|
|
@@ -3628,13 +3909,13 @@ var init_stream_processor = __esm({
|
|
|
3628
3909
|
parser;
|
|
3629
3910
|
executor;
|
|
3630
3911
|
stopOnGadgetError;
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3912
|
+
canRecoverFromGadgetError;
|
|
3913
|
+
responseText = "";
|
|
3914
|
+
executionHalted = false;
|
|
3634
3915
|
observerFailureCount = 0;
|
|
3635
3916
|
// Dependency tracking for gadget execution DAG
|
|
3636
3917
|
/** Gadgets waiting for their dependencies to complete */
|
|
3637
|
-
|
|
3918
|
+
gadgetsAwaitingDependencies = /* @__PURE__ */ new Map();
|
|
3638
3919
|
/** Completed gadget results, keyed by invocation ID */
|
|
3639
3920
|
completedResults = /* @__PURE__ */ new Map();
|
|
3640
3921
|
/** Invocation IDs of gadgets that have failed (error or skipped due to dependency) */
|
|
@@ -3645,19 +3926,22 @@ var init_stream_processor = __esm({
|
|
|
3645
3926
|
this.hooks = options.hooks ?? {};
|
|
3646
3927
|
this.logger = options.logger ?? createLogger({ name: "llmist:stream-processor" });
|
|
3647
3928
|
this.stopOnGadgetError = options.stopOnGadgetError ?? true;
|
|
3648
|
-
this.
|
|
3649
|
-
this.parser = new
|
|
3929
|
+
this.canRecoverFromGadgetError = options.canRecoverFromGadgetError;
|
|
3930
|
+
this.parser = new GadgetCallParser({
|
|
3650
3931
|
startPrefix: options.gadgetStartPrefix,
|
|
3651
3932
|
endPrefix: options.gadgetEndPrefix,
|
|
3652
3933
|
argPrefix: options.gadgetArgPrefix
|
|
3653
3934
|
});
|
|
3654
3935
|
this.executor = new GadgetExecutor(
|
|
3655
3936
|
options.registry,
|
|
3656
|
-
options.
|
|
3937
|
+
options.requestHumanInput,
|
|
3657
3938
|
this.logger.getSubLogger({ name: "executor" }),
|
|
3658
3939
|
options.defaultGadgetTimeoutMs,
|
|
3659
3940
|
{ argPrefix: options.gadgetArgPrefix },
|
|
3660
|
-
options.client
|
|
3941
|
+
options.client,
|
|
3942
|
+
options.mediaStore,
|
|
3943
|
+
options.agentConfig,
|
|
3944
|
+
options.subagentConfig
|
|
3661
3945
|
);
|
|
3662
3946
|
}
|
|
3663
3947
|
/**
|
|
@@ -3678,7 +3962,7 @@ var init_stream_processor = __esm({
|
|
|
3678
3962
|
if (this.hooks.interceptors?.interceptRawChunk) {
|
|
3679
3963
|
const context = {
|
|
3680
3964
|
iteration: this.iteration,
|
|
3681
|
-
accumulatedText: this.
|
|
3965
|
+
accumulatedText: this.responseText,
|
|
3682
3966
|
logger: this.logger
|
|
3683
3967
|
};
|
|
3684
3968
|
const intercepted = this.hooks.interceptors.interceptRawChunk(processedChunk, context);
|
|
@@ -3689,7 +3973,7 @@ var init_stream_processor = __esm({
|
|
|
3689
3973
|
}
|
|
3690
3974
|
}
|
|
3691
3975
|
if (processedChunk) {
|
|
3692
|
-
this.
|
|
3976
|
+
this.responseText += processedChunk;
|
|
3693
3977
|
}
|
|
3694
3978
|
}
|
|
3695
3979
|
if (this.hooks.observers?.onStreamChunk && (processedChunk || chunk.usage)) {
|
|
@@ -3698,7 +3982,7 @@ var init_stream_processor = __esm({
|
|
|
3698
3982
|
const context = {
|
|
3699
3983
|
iteration: this.iteration,
|
|
3700
3984
|
rawChunk: processedChunk,
|
|
3701
|
-
accumulatedText: this.
|
|
3985
|
+
accumulatedText: this.responseText,
|
|
3702
3986
|
usage,
|
|
3703
3987
|
logger: this.logger
|
|
3704
3988
|
};
|
|
@@ -3721,12 +4005,12 @@ var init_stream_processor = __esm({
|
|
|
3721
4005
|
}
|
|
3722
4006
|
}
|
|
3723
4007
|
}
|
|
3724
|
-
if (this.
|
|
4008
|
+
if (this.executionHalted) {
|
|
3725
4009
|
this.logger.info("Breaking from LLM stream due to gadget error");
|
|
3726
4010
|
break;
|
|
3727
4011
|
}
|
|
3728
4012
|
}
|
|
3729
|
-
if (!this.
|
|
4013
|
+
if (!this.executionHalted) {
|
|
3730
4014
|
for (const event of this.parser.finalize()) {
|
|
3731
4015
|
const processedEvents = await this.processEvent(event);
|
|
3732
4016
|
outputs.push(...processedEvents);
|
|
@@ -3750,11 +4034,11 @@ var init_stream_processor = __esm({
|
|
|
3750
4034
|
}
|
|
3751
4035
|
}
|
|
3752
4036
|
}
|
|
3753
|
-
let finalMessage = this.
|
|
4037
|
+
let finalMessage = this.responseText;
|
|
3754
4038
|
if (this.hooks.interceptors?.interceptAssistantMessage) {
|
|
3755
4039
|
const context = {
|
|
3756
4040
|
iteration: this.iteration,
|
|
3757
|
-
rawResponse: this.
|
|
4041
|
+
rawResponse: this.responseText,
|
|
3758
4042
|
logger: this.logger
|
|
3759
4043
|
};
|
|
3760
4044
|
finalMessage = this.hooks.interceptors.interceptAssistantMessage(finalMessage, context);
|
|
@@ -3765,7 +4049,7 @@ var init_stream_processor = __esm({
|
|
|
3765
4049
|
didExecuteGadgets,
|
|
3766
4050
|
finishReason,
|
|
3767
4051
|
usage,
|
|
3768
|
-
rawResponse: this.
|
|
4052
|
+
rawResponse: this.responseText,
|
|
3769
4053
|
finalMessage
|
|
3770
4054
|
};
|
|
3771
4055
|
}
|
|
@@ -3788,7 +4072,7 @@ var init_stream_processor = __esm({
|
|
|
3788
4072
|
if (this.hooks.interceptors?.interceptTextChunk) {
|
|
3789
4073
|
const context = {
|
|
3790
4074
|
iteration: this.iteration,
|
|
3791
|
-
accumulatedText: this.
|
|
4075
|
+
accumulatedText: this.responseText,
|
|
3792
4076
|
logger: this.logger
|
|
3793
4077
|
};
|
|
3794
4078
|
const intercepted = this.hooks.interceptors.interceptTextChunk(content, context);
|
|
@@ -3807,7 +4091,7 @@ var init_stream_processor = __esm({
|
|
|
3807
4091
|
* After each execution, pending gadgets are checked to see if they can now run.
|
|
3808
4092
|
*/
|
|
3809
4093
|
async processGadgetCall(call) {
|
|
3810
|
-
if (this.
|
|
4094
|
+
if (this.executionHalted) {
|
|
3811
4095
|
this.logger.debug("Skipping gadget execution due to previous error", {
|
|
3812
4096
|
gadgetName: call.gadgetName
|
|
3813
4097
|
});
|
|
@@ -3846,7 +4130,7 @@ var init_stream_processor = __esm({
|
|
|
3846
4130
|
invocationId: call.invocationId,
|
|
3847
4131
|
waitingOn: unsatisfied
|
|
3848
4132
|
});
|
|
3849
|
-
this.
|
|
4133
|
+
this.gadgetsAwaitingDependencies.set(call.invocationId, call);
|
|
3850
4134
|
return events;
|
|
3851
4135
|
}
|
|
3852
4136
|
}
|
|
@@ -3868,14 +4152,14 @@ var init_stream_processor = __esm({
|
|
|
3868
4152
|
error: call.parseError,
|
|
3869
4153
|
rawParameters: call.parametersRaw
|
|
3870
4154
|
});
|
|
3871
|
-
const shouldContinue = await this.
|
|
4155
|
+
const shouldContinue = await this.checkCanRecoverFromError(
|
|
3872
4156
|
call.parseError,
|
|
3873
4157
|
call.gadgetName,
|
|
3874
4158
|
"parse",
|
|
3875
4159
|
call.parameters
|
|
3876
4160
|
);
|
|
3877
4161
|
if (!shouldContinue) {
|
|
3878
|
-
this.
|
|
4162
|
+
this.executionHalted = true;
|
|
3879
4163
|
}
|
|
3880
4164
|
}
|
|
3881
4165
|
let parameters = call.parameters ?? {};
|
|
@@ -3999,14 +4283,14 @@ var init_stream_processor = __esm({
|
|
|
3999
4283
|
events.push({ type: "gadget_result", result });
|
|
4000
4284
|
if (result.error) {
|
|
4001
4285
|
const errorType = this.determineErrorType(call, result);
|
|
4002
|
-
const shouldContinue = await this.
|
|
4286
|
+
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4003
4287
|
result.error,
|
|
4004
4288
|
result.gadgetName,
|
|
4005
4289
|
errorType,
|
|
4006
4290
|
result.parameters
|
|
4007
4291
|
);
|
|
4008
4292
|
if (!shouldContinue) {
|
|
4009
|
-
this.
|
|
4293
|
+
this.executionHalted = true;
|
|
4010
4294
|
}
|
|
4011
4295
|
}
|
|
4012
4296
|
return events;
|
|
@@ -4093,11 +4377,11 @@ var init_stream_processor = __esm({
|
|
|
4093
4377
|
async processPendingGadgets() {
|
|
4094
4378
|
const events = [];
|
|
4095
4379
|
let progress = true;
|
|
4096
|
-
while (progress && this.
|
|
4380
|
+
while (progress && this.gadgetsAwaitingDependencies.size > 0) {
|
|
4097
4381
|
progress = false;
|
|
4098
4382
|
const readyToExecute = [];
|
|
4099
4383
|
const readyToSkip = [];
|
|
4100
|
-
for (const [invocationId, call] of this.
|
|
4384
|
+
for (const [invocationId, call] of this.gadgetsAwaitingDependencies) {
|
|
4101
4385
|
const failedDep = call.dependencies.find((dep) => this.failedInvocations.has(dep));
|
|
4102
4386
|
if (failedDep) {
|
|
4103
4387
|
readyToSkip.push({ call, failedDep });
|
|
@@ -4109,7 +4393,7 @@ var init_stream_processor = __esm({
|
|
|
4109
4393
|
}
|
|
4110
4394
|
}
|
|
4111
4395
|
for (const { call, failedDep } of readyToSkip) {
|
|
4112
|
-
this.
|
|
4396
|
+
this.gadgetsAwaitingDependencies.delete(call.invocationId);
|
|
4113
4397
|
const skipEvents = await this.handleFailedDependency(call, failedDep);
|
|
4114
4398
|
events.push(...skipEvents);
|
|
4115
4399
|
progress = true;
|
|
@@ -4120,7 +4404,7 @@ var init_stream_processor = __esm({
|
|
|
4120
4404
|
invocationIds: readyToExecute.map((c) => c.invocationId)
|
|
4121
4405
|
});
|
|
4122
4406
|
for (const call of readyToExecute) {
|
|
4123
|
-
this.
|
|
4407
|
+
this.gadgetsAwaitingDependencies.delete(call.invocationId);
|
|
4124
4408
|
}
|
|
4125
4409
|
const executePromises = readyToExecute.map((call) => this.executeGadgetWithHooks(call));
|
|
4126
4410
|
const results = await Promise.all(executePromises);
|
|
@@ -4130,9 +4414,9 @@ var init_stream_processor = __esm({
|
|
|
4130
4414
|
progress = true;
|
|
4131
4415
|
}
|
|
4132
4416
|
}
|
|
4133
|
-
if (this.
|
|
4134
|
-
const pendingIds = new Set(this.
|
|
4135
|
-
for (const [invocationId, call] of this.
|
|
4417
|
+
if (this.gadgetsAwaitingDependencies.size > 0) {
|
|
4418
|
+
const pendingIds = new Set(this.gadgetsAwaitingDependencies.keys());
|
|
4419
|
+
for (const [invocationId, call] of this.gadgetsAwaitingDependencies) {
|
|
4136
4420
|
const missingDeps = call.dependencies.filter((dep) => !this.completedResults.has(dep));
|
|
4137
4421
|
const circularDeps = missingDeps.filter((dep) => pendingIds.has(dep));
|
|
4138
4422
|
const trulyMissingDeps = missingDeps.filter((dep) => !pendingIds.has(dep));
|
|
@@ -4163,7 +4447,7 @@ var init_stream_processor = __esm({
|
|
|
4163
4447
|
};
|
|
4164
4448
|
events.push(skipEvent);
|
|
4165
4449
|
}
|
|
4166
|
-
this.
|
|
4450
|
+
this.gadgetsAwaitingDependencies.clear();
|
|
4167
4451
|
}
|
|
4168
4452
|
return events;
|
|
4169
4453
|
}
|
|
@@ -4193,19 +4477,19 @@ var init_stream_processor = __esm({
|
|
|
4193
4477
|
);
|
|
4194
4478
|
}
|
|
4195
4479
|
/**
|
|
4196
|
-
* Check if execution
|
|
4480
|
+
* Check if execution can recover from an error.
|
|
4197
4481
|
*
|
|
4198
4482
|
* Returns true if we should continue processing subsequent gadgets, false if we should stop.
|
|
4199
4483
|
*
|
|
4200
4484
|
* Logic:
|
|
4201
|
-
* - If custom
|
|
4485
|
+
* - If custom canRecoverFromGadgetError is provided, use it
|
|
4202
4486
|
* - Otherwise, use stopOnGadgetError config:
|
|
4203
4487
|
* - stopOnGadgetError=true → return false (stop execution)
|
|
4204
4488
|
* - stopOnGadgetError=false → return true (continue execution)
|
|
4205
4489
|
*/
|
|
4206
|
-
async
|
|
4207
|
-
if (this.
|
|
4208
|
-
return await this.
|
|
4490
|
+
async checkCanRecoverFromError(error, gadgetName, errorType, parameters) {
|
|
4491
|
+
if (this.canRecoverFromGadgetError) {
|
|
4492
|
+
return await this.canRecoverFromGadgetError({
|
|
4209
4493
|
error,
|
|
4210
4494
|
gadgetName,
|
|
4211
4495
|
errorType,
|
|
@@ -4246,13 +4530,14 @@ var init_agent = __esm({
|
|
|
4246
4530
|
init_constants();
|
|
4247
4531
|
init_messages();
|
|
4248
4532
|
init_model_shortcuts();
|
|
4533
|
+
init_media_store();
|
|
4249
4534
|
init_output_viewer();
|
|
4250
4535
|
init_logger();
|
|
4251
|
-
init_manager();
|
|
4252
|
-
init_gadget_output_store();
|
|
4253
4536
|
init_agent_internal_key();
|
|
4537
|
+
init_manager();
|
|
4254
4538
|
init_conversation_manager();
|
|
4255
4539
|
init_event_handlers();
|
|
4540
|
+
init_gadget_output_store();
|
|
4256
4541
|
init_hook_validators();
|
|
4257
4542
|
init_stream_processor();
|
|
4258
4543
|
Agent = class {
|
|
@@ -4267,22 +4552,27 @@ var init_agent = __esm({
|
|
|
4267
4552
|
gadgetStartPrefix;
|
|
4268
4553
|
gadgetEndPrefix;
|
|
4269
4554
|
gadgetArgPrefix;
|
|
4270
|
-
|
|
4555
|
+
requestHumanInput;
|
|
4271
4556
|
textOnlyHandler;
|
|
4272
4557
|
textWithGadgetsHandler;
|
|
4273
4558
|
stopOnGadgetError;
|
|
4274
|
-
|
|
4559
|
+
canRecoverFromGadgetError;
|
|
4275
4560
|
defaultGadgetTimeoutMs;
|
|
4276
4561
|
defaultMaxTokens;
|
|
4277
|
-
|
|
4562
|
+
hasUserPrompt;
|
|
4278
4563
|
// Gadget output limiting
|
|
4279
4564
|
outputStore;
|
|
4280
4565
|
outputLimitEnabled;
|
|
4281
4566
|
outputLimitCharLimit;
|
|
4282
4567
|
// Context compaction
|
|
4283
4568
|
compactionManager;
|
|
4569
|
+
// Media storage (for gadgets returning images, audio, etc.)
|
|
4570
|
+
mediaStore;
|
|
4284
4571
|
// Cancellation
|
|
4285
4572
|
signal;
|
|
4573
|
+
// Subagent configuration
|
|
4574
|
+
agentContextConfig;
|
|
4575
|
+
subagentConfig;
|
|
4286
4576
|
/**
|
|
4287
4577
|
* Creates a new Agent instance.
|
|
4288
4578
|
* @internal This constructor is private. Use LLMist.createAgent() or AgentBuilder instead.
|
|
@@ -4302,15 +4592,16 @@ var init_agent = __esm({
|
|
|
4302
4592
|
this.gadgetStartPrefix = options.gadgetStartPrefix;
|
|
4303
4593
|
this.gadgetEndPrefix = options.gadgetEndPrefix;
|
|
4304
4594
|
this.gadgetArgPrefix = options.gadgetArgPrefix;
|
|
4305
|
-
this.
|
|
4595
|
+
this.requestHumanInput = options.requestHumanInput;
|
|
4306
4596
|
this.textOnlyHandler = options.textOnlyHandler ?? "terminate";
|
|
4307
4597
|
this.textWithGadgetsHandler = options.textWithGadgetsHandler;
|
|
4308
4598
|
this.stopOnGadgetError = options.stopOnGadgetError ?? true;
|
|
4309
|
-
this.
|
|
4599
|
+
this.canRecoverFromGadgetError = options.canRecoverFromGadgetError;
|
|
4310
4600
|
this.defaultGadgetTimeoutMs = options.defaultGadgetTimeoutMs;
|
|
4311
4601
|
this.defaultMaxTokens = this.resolveMaxTokensFromCatalog(options.model);
|
|
4312
4602
|
this.outputLimitEnabled = options.gadgetOutputLimit ?? DEFAULT_GADGET_OUTPUT_LIMIT;
|
|
4313
4603
|
this.outputStore = new GadgetOutputStore();
|
|
4604
|
+
this.mediaStore = new MediaStore();
|
|
4314
4605
|
const limitPercent = options.gadgetOutputLimitPercent ?? DEFAULT_GADGET_OUTPUT_LIMIT_PERCENT;
|
|
4315
4606
|
const limits = this.client.modelRegistry.getModelLimits(this.model);
|
|
4316
4607
|
const contextWindow = limits?.contextWindow ?? FALLBACK_CONTEXT_WINDOW;
|
|
@@ -4321,7 +4612,7 @@ var init_agent = __esm({
|
|
|
4321
4612
|
createGadgetOutputViewer(this.outputStore, this.outputLimitCharLimit)
|
|
4322
4613
|
);
|
|
4323
4614
|
}
|
|
4324
|
-
this.hooks = this.
|
|
4615
|
+
this.hooks = this.chainOutputLimiterWithUserHooks(options.hooks);
|
|
4325
4616
|
const baseBuilder = new LLMMessageBuilder(options.promptConfig);
|
|
4326
4617
|
if (options.systemPrompt) {
|
|
4327
4618
|
baseBuilder.addSystem(options.systemPrompt);
|
|
@@ -4341,7 +4632,7 @@ var init_agent = __esm({
|
|
|
4341
4632
|
endPrefix: options.gadgetEndPrefix,
|
|
4342
4633
|
argPrefix: options.gadgetArgPrefix
|
|
4343
4634
|
});
|
|
4344
|
-
this.
|
|
4635
|
+
this.hasUserPrompt = !!options.userPrompt;
|
|
4345
4636
|
if (options.userPrompt) {
|
|
4346
4637
|
this.conversation.addUserMessage(options.userPrompt);
|
|
4347
4638
|
}
|
|
@@ -4354,6 +4645,11 @@ var init_agent = __esm({
|
|
|
4354
4645
|
);
|
|
4355
4646
|
}
|
|
4356
4647
|
this.signal = options.signal;
|
|
4648
|
+
this.agentContextConfig = {
|
|
4649
|
+
model: this.model,
|
|
4650
|
+
temperature: this.temperature
|
|
4651
|
+
};
|
|
4652
|
+
this.subagentConfig = options.subagentConfig;
|
|
4357
4653
|
}
|
|
4358
4654
|
/**
|
|
4359
4655
|
* Get the gadget registry for this agent.
|
|
@@ -4376,6 +4672,36 @@ var init_agent = __esm({
|
|
|
4376
4672
|
getRegistry() {
|
|
4377
4673
|
return this.registry;
|
|
4378
4674
|
}
|
|
4675
|
+
/**
|
|
4676
|
+
* Get the media store for this agent session.
|
|
4677
|
+
*
|
|
4678
|
+
* The media store holds all media outputs (images, audio, etc.) produced by gadgets
|
|
4679
|
+
* during this agent's execution. Use this to:
|
|
4680
|
+
* - Access stored media files by ID
|
|
4681
|
+
* - List all stored media
|
|
4682
|
+
* - Clean up temporary files after execution
|
|
4683
|
+
*
|
|
4684
|
+
* @returns The MediaStore instance for this agent
|
|
4685
|
+
*
|
|
4686
|
+
* @example
|
|
4687
|
+
* ```typescript
|
|
4688
|
+
* const agent = new AgentBuilder()
|
|
4689
|
+
* .withModel("sonnet")
|
|
4690
|
+
* .build();
|
|
4691
|
+
*
|
|
4692
|
+
* // After execution, access stored media
|
|
4693
|
+
* const store = agent.getMediaStore();
|
|
4694
|
+
* for (const media of store.list()) {
|
|
4695
|
+
* console.log(`${media.id}: ${media.path}`);
|
|
4696
|
+
* }
|
|
4697
|
+
*
|
|
4698
|
+
* // Clean up when done
|
|
4699
|
+
* await store.cleanup();
|
|
4700
|
+
* ```
|
|
4701
|
+
*/
|
|
4702
|
+
getMediaStore() {
|
|
4703
|
+
return this.mediaStore;
|
|
4704
|
+
}
|
|
4379
4705
|
/**
|
|
4380
4706
|
* Manually trigger context compaction.
|
|
4381
4707
|
*
|
|
@@ -4430,7 +4756,7 @@ var init_agent = __esm({
|
|
|
4430
4756
|
* @throws {Error} If no user prompt was provided (when using build() without ask())
|
|
4431
4757
|
*/
|
|
4432
4758
|
async *run() {
|
|
4433
|
-
if (!this.
|
|
4759
|
+
if (!this.hasUserPrompt) {
|
|
4434
4760
|
throw new Error(
|
|
4435
4761
|
"No user prompt provided. Use .ask(prompt) instead of .build(), or call agent.run() after providing a prompt."
|
|
4436
4762
|
);
|
|
@@ -4547,11 +4873,14 @@ var init_agent = __esm({
|
|
|
4547
4873
|
gadgetArgPrefix: this.gadgetArgPrefix,
|
|
4548
4874
|
hooks: this.hooks,
|
|
4549
4875
|
logger: this.logger.getSubLogger({ name: "stream-processor" }),
|
|
4550
|
-
|
|
4876
|
+
requestHumanInput: this.requestHumanInput,
|
|
4551
4877
|
stopOnGadgetError: this.stopOnGadgetError,
|
|
4552
|
-
|
|
4878
|
+
canRecoverFromGadgetError: this.canRecoverFromGadgetError,
|
|
4553
4879
|
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
4554
|
-
client: this.client
|
|
4880
|
+
client: this.client,
|
|
4881
|
+
mediaStore: this.mediaStore,
|
|
4882
|
+
agentConfig: this.agentContextConfig,
|
|
4883
|
+
subagentConfig: this.subagentConfig
|
|
4555
4884
|
});
|
|
4556
4885
|
const result = await processor.process(stream2);
|
|
4557
4886
|
for (const output of result.outputs) {
|
|
@@ -4604,19 +4933,21 @@ var init_agent = __esm({
|
|
|
4604
4933
|
if (msg.role === "user") {
|
|
4605
4934
|
this.conversation.addUserMessage(msg.content);
|
|
4606
4935
|
} else if (msg.role === "assistant") {
|
|
4607
|
-
this.conversation.addAssistantMessage(
|
|
4936
|
+
this.conversation.addAssistantMessage(extractMessageText(msg.content));
|
|
4608
4937
|
} else if (msg.role === "system") {
|
|
4609
|
-
this.conversation.addUserMessage(`[System] ${
|
|
4938
|
+
this.conversation.addUserMessage(`[System] ${extractMessageText(msg.content)}`);
|
|
4610
4939
|
}
|
|
4611
4940
|
}
|
|
4612
4941
|
}
|
|
4613
4942
|
}
|
|
4614
4943
|
if (result.didExecuteGadgets) {
|
|
4615
4944
|
if (this.textWithGadgetsHandler) {
|
|
4616
|
-
const textContent = result.outputs.filter(
|
|
4945
|
+
const textContent = result.outputs.filter(
|
|
4946
|
+
(output) => output.type === "text"
|
|
4947
|
+
).map((output) => output.content).join("");
|
|
4617
4948
|
if (textContent.trim()) {
|
|
4618
4949
|
const { gadgetName, parameterMapping, resultMapping } = this.textWithGadgetsHandler;
|
|
4619
|
-
this.conversation.
|
|
4950
|
+
this.conversation.addGadgetCallResult(
|
|
4620
4951
|
gadgetName,
|
|
4621
4952
|
parameterMapping(textContent),
|
|
4622
4953
|
resultMapping ? resultMapping(textContent) : textContent
|
|
@@ -4626,16 +4957,18 @@ var init_agent = __esm({
|
|
|
4626
4957
|
for (const output of result.outputs) {
|
|
4627
4958
|
if (output.type === "gadget_result") {
|
|
4628
4959
|
const gadgetResult = output.result;
|
|
4629
|
-
this.conversation.
|
|
4960
|
+
this.conversation.addGadgetCallResult(
|
|
4630
4961
|
gadgetResult.gadgetName,
|
|
4631
4962
|
gadgetResult.parameters,
|
|
4632
|
-
gadgetResult.error ?? gadgetResult.result ?? ""
|
|
4963
|
+
gadgetResult.error ?? gadgetResult.result ?? "",
|
|
4964
|
+
gadgetResult.media,
|
|
4965
|
+
gadgetResult.mediaIds
|
|
4633
4966
|
);
|
|
4634
4967
|
}
|
|
4635
4968
|
}
|
|
4636
4969
|
} else {
|
|
4637
4970
|
if (finalMessage.trim()) {
|
|
4638
|
-
this.conversation.
|
|
4971
|
+
this.conversation.addGadgetCallResult(
|
|
4639
4972
|
"TellUser",
|
|
4640
4973
|
{ message: finalMessage, done: false, type: "info" },
|
|
4641
4974
|
`\u2139\uFE0F ${finalMessage}`
|
|
@@ -4761,10 +5094,10 @@ var init_agent = __esm({
|
|
|
4761
5094
|
return this.client.modelRegistry.getModelLimits(unprefixedModelId)?.maxOutputTokens;
|
|
4762
5095
|
}
|
|
4763
5096
|
/**
|
|
4764
|
-
*
|
|
5097
|
+
* Chain the output limiter interceptor with user-provided hooks.
|
|
4765
5098
|
* The limiter runs first, then chains to any user interceptor.
|
|
4766
5099
|
*/
|
|
4767
|
-
|
|
5100
|
+
chainOutputLimiterWithUserHooks(userHooks) {
|
|
4768
5101
|
if (!this.outputLimitEnabled) {
|
|
4769
5102
|
return userHooks ?? {};
|
|
4770
5103
|
}
|
|
@@ -4843,20 +5176,21 @@ var init_builder = __esm({
|
|
|
4843
5176
|
promptConfig;
|
|
4844
5177
|
gadgets = [];
|
|
4845
5178
|
initialMessages = [];
|
|
4846
|
-
|
|
5179
|
+
requestHumanInput;
|
|
4847
5180
|
gadgetStartPrefix;
|
|
4848
5181
|
gadgetEndPrefix;
|
|
4849
5182
|
gadgetArgPrefix;
|
|
4850
5183
|
textOnlyHandler;
|
|
4851
5184
|
textWithGadgetsHandler;
|
|
4852
5185
|
stopOnGadgetError;
|
|
4853
|
-
|
|
5186
|
+
canRecoverFromGadgetError;
|
|
4854
5187
|
defaultGadgetTimeoutMs;
|
|
4855
5188
|
gadgetOutputLimit;
|
|
4856
5189
|
gadgetOutputLimitPercent;
|
|
4857
5190
|
compactionConfig;
|
|
4858
5191
|
signal;
|
|
4859
5192
|
trailingMessage;
|
|
5193
|
+
subagentConfig;
|
|
4860
5194
|
constructor(client) {
|
|
4861
5195
|
this.client = client;
|
|
4862
5196
|
}
|
|
@@ -4947,13 +5281,13 @@ var init_builder = __esm({
|
|
|
4947
5281
|
*
|
|
4948
5282
|
* @example
|
|
4949
5283
|
* ```typescript
|
|
4950
|
-
* .
|
|
5284
|
+
* .withPromptTemplateConfig({
|
|
4951
5285
|
* mainInstruction: "Use the gadget markers below:",
|
|
4952
5286
|
* rules: ["Always use markers", "Never use function calling"]
|
|
4953
5287
|
* })
|
|
4954
5288
|
* ```
|
|
4955
5289
|
*/
|
|
4956
|
-
|
|
5290
|
+
withPromptTemplateConfig(config) {
|
|
4957
5291
|
this.promptConfig = config;
|
|
4958
5292
|
return this;
|
|
4959
5293
|
}
|
|
@@ -5033,7 +5367,7 @@ var init_builder = __esm({
|
|
|
5033
5367
|
* ```
|
|
5034
5368
|
*/
|
|
5035
5369
|
onHumanInput(handler) {
|
|
5036
|
-
this.
|
|
5370
|
+
this.requestHumanInput = handler;
|
|
5037
5371
|
return this;
|
|
5038
5372
|
}
|
|
5039
5373
|
/**
|
|
@@ -5168,9 +5502,9 @@ var init_builder = __esm({
|
|
|
5168
5502
|
* Provides fine-grained control over whether to continue after different types of errors.
|
|
5169
5503
|
* Overrides `stopOnGadgetError` when provided.
|
|
5170
5504
|
*
|
|
5171
|
-
* **Note:** This builder method configures the underlying `
|
|
5505
|
+
* **Note:** This builder method configures the underlying `canRecoverFromGadgetError` option
|
|
5172
5506
|
* in `AgentOptions`. The method is named `withErrorHandler` for better developer experience,
|
|
5173
|
-
* but maps to the `
|
|
5507
|
+
* but maps to the `canRecoverFromGadgetError` property internally.
|
|
5174
5508
|
*
|
|
5175
5509
|
* @param handler - Function that decides whether to continue after an error.
|
|
5176
5510
|
* Return `true` to continue execution, `false` to stop.
|
|
@@ -5191,7 +5525,7 @@ var init_builder = __esm({
|
|
|
5191
5525
|
* ```
|
|
5192
5526
|
*/
|
|
5193
5527
|
withErrorHandler(handler) {
|
|
5194
|
-
this.
|
|
5528
|
+
this.canRecoverFromGadgetError = handler;
|
|
5195
5529
|
return this;
|
|
5196
5530
|
}
|
|
5197
5531
|
/**
|
|
@@ -5332,6 +5666,27 @@ var init_builder = __esm({
|
|
|
5332
5666
|
this.signal = signal;
|
|
5333
5667
|
return this;
|
|
5334
5668
|
}
|
|
5669
|
+
/**
|
|
5670
|
+
* Set subagent configuration overrides.
|
|
5671
|
+
*
|
|
5672
|
+
* Subagent gadgets (like BrowseWeb) can read these settings from ExecutionContext
|
|
5673
|
+
* to inherit model and other options from the CLI configuration.
|
|
5674
|
+
*
|
|
5675
|
+
* @param config - Subagent configuration map keyed by gadget name
|
|
5676
|
+
* @returns This builder for chaining
|
|
5677
|
+
*
|
|
5678
|
+
* @example
|
|
5679
|
+
* ```typescript
|
|
5680
|
+
* .withSubagentConfig({
|
|
5681
|
+
* BrowseWeb: { model: "inherit", maxIterations: 20, headless: true },
|
|
5682
|
+
* CodeAnalyzer: { model: "sonnet", maxIterations: 10 }
|
|
5683
|
+
* })
|
|
5684
|
+
* ```
|
|
5685
|
+
*/
|
|
5686
|
+
withSubagentConfig(config) {
|
|
5687
|
+
this.subagentConfig = config;
|
|
5688
|
+
return this;
|
|
5689
|
+
}
|
|
5335
5690
|
/**
|
|
5336
5691
|
* Add an ephemeral trailing message that appears at the end of each LLM request.
|
|
5337
5692
|
*
|
|
@@ -5496,19 +5851,20 @@ ${endPrefix}`
|
|
|
5496
5851
|
hooks: this.composeHooks(),
|
|
5497
5852
|
promptConfig: this.promptConfig,
|
|
5498
5853
|
initialMessages: this.initialMessages,
|
|
5499
|
-
|
|
5854
|
+
requestHumanInput: this.requestHumanInput,
|
|
5500
5855
|
gadgetStartPrefix: this.gadgetStartPrefix,
|
|
5501
5856
|
gadgetEndPrefix: this.gadgetEndPrefix,
|
|
5502
5857
|
gadgetArgPrefix: this.gadgetArgPrefix,
|
|
5503
5858
|
textOnlyHandler: this.textOnlyHandler,
|
|
5504
5859
|
textWithGadgetsHandler: this.textWithGadgetsHandler,
|
|
5505
5860
|
stopOnGadgetError: this.stopOnGadgetError,
|
|
5506
|
-
|
|
5861
|
+
canRecoverFromGadgetError: this.canRecoverFromGadgetError,
|
|
5507
5862
|
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
5508
5863
|
gadgetOutputLimit: this.gadgetOutputLimit,
|
|
5509
5864
|
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
5510
5865
|
compactionConfig: this.compactionConfig,
|
|
5511
|
-
signal: this.signal
|
|
5866
|
+
signal: this.signal,
|
|
5867
|
+
subagentConfig: this.subagentConfig
|
|
5512
5868
|
};
|
|
5513
5869
|
}
|
|
5514
5870
|
ask(userPrompt) {
|
|
@@ -5677,19 +6033,20 @@ ${endPrefix}`
|
|
|
5677
6033
|
hooks: this.composeHooks(),
|
|
5678
6034
|
promptConfig: this.promptConfig,
|
|
5679
6035
|
initialMessages: this.initialMessages,
|
|
5680
|
-
|
|
6036
|
+
requestHumanInput: this.requestHumanInput,
|
|
5681
6037
|
gadgetStartPrefix: this.gadgetStartPrefix,
|
|
5682
6038
|
gadgetEndPrefix: this.gadgetEndPrefix,
|
|
5683
6039
|
gadgetArgPrefix: this.gadgetArgPrefix,
|
|
5684
6040
|
textOnlyHandler: this.textOnlyHandler,
|
|
5685
6041
|
textWithGadgetsHandler: this.textWithGadgetsHandler,
|
|
5686
6042
|
stopOnGadgetError: this.stopOnGadgetError,
|
|
5687
|
-
|
|
6043
|
+
canRecoverFromGadgetError: this.canRecoverFromGadgetError,
|
|
5688
6044
|
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
5689
6045
|
gadgetOutputLimit: this.gadgetOutputLimit,
|
|
5690
6046
|
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
5691
6047
|
compactionConfig: this.compactionConfig,
|
|
5692
|
-
signal: this.signal
|
|
6048
|
+
signal: this.signal,
|
|
6049
|
+
subagentConfig: this.subagentConfig
|
|
5693
6050
|
};
|
|
5694
6051
|
return new Agent(AGENT_INTERNAL_KEY, options);
|
|
5695
6052
|
}
|
|
@@ -5995,9 +6352,9 @@ var init_base_provider = __esm({
|
|
|
5995
6352
|
*/
|
|
5996
6353
|
async *stream(options, descriptor, spec) {
|
|
5997
6354
|
const preparedMessages = this.prepareMessages(options.messages);
|
|
5998
|
-
const payload = this.
|
|
6355
|
+
const payload = this.buildApiRequest(options, descriptor, spec, preparedMessages);
|
|
5999
6356
|
const rawStream = await this.executeStreamRequest(payload, options.signal);
|
|
6000
|
-
yield* this.
|
|
6357
|
+
yield* this.normalizeProviderStream(rawStream);
|
|
6001
6358
|
}
|
|
6002
6359
|
/**
|
|
6003
6360
|
* Prepare messages for the request.
|
|
@@ -6097,11 +6454,11 @@ var init_anthropic = __esm({
|
|
|
6097
6454
|
"Anthropic does not support speech generation. Use OpenAI (TTS) or Google Gemini (TTS) instead."
|
|
6098
6455
|
);
|
|
6099
6456
|
}
|
|
6100
|
-
|
|
6457
|
+
buildApiRequest(options, descriptor, spec, messages) {
|
|
6101
6458
|
const systemMessages = messages.filter((message) => message.role === "system");
|
|
6102
6459
|
const system = systemMessages.length > 0 ? systemMessages.map((m, index) => ({
|
|
6103
6460
|
type: "text",
|
|
6104
|
-
text:
|
|
6461
|
+
text: extractMessageText(m.content),
|
|
6105
6462
|
// Add cache_control to the LAST system message block
|
|
6106
6463
|
...index === systemMessages.length - 1 ? { cache_control: { type: "ephemeral" } } : {}
|
|
6107
6464
|
})) : void 0;
|
|
@@ -6138,7 +6495,7 @@ var init_anthropic = __esm({
|
|
|
6138
6495
|
* Handles text, images (base64 only), and applies cache_control.
|
|
6139
6496
|
*/
|
|
6140
6497
|
convertToAnthropicContent(content, addCacheControl) {
|
|
6141
|
-
const parts =
|
|
6498
|
+
const parts = normalizeMessageContent(content);
|
|
6142
6499
|
return parts.map((part, index) => {
|
|
6143
6500
|
const isLastPart = index === parts.length - 1;
|
|
6144
6501
|
const cacheControl = addCacheControl && isLastPart ? { cache_control: { type: "ephemeral" } } : {};
|
|
@@ -6184,7 +6541,7 @@ var init_anthropic = __esm({
|
|
|
6184
6541
|
const stream2 = await client.messages.create(payload, signal ? { signal } : void 0);
|
|
6185
6542
|
return stream2;
|
|
6186
6543
|
}
|
|
6187
|
-
async *
|
|
6544
|
+
async *normalizeProviderStream(iterable) {
|
|
6188
6545
|
const stream2 = iterable;
|
|
6189
6546
|
let inputTokens = 0;
|
|
6190
6547
|
let cachedInputTokens = 0;
|
|
@@ -6261,7 +6618,7 @@ var init_anthropic = __esm({
|
|
|
6261
6618
|
async countTokens(messages, descriptor, _spec) {
|
|
6262
6619
|
const client = this.client;
|
|
6263
6620
|
const systemMessages = messages.filter((message) => message.role === "system");
|
|
6264
|
-
const system = systemMessages.length > 0 ? systemMessages.map((m) =>
|
|
6621
|
+
const system = systemMessages.length > 0 ? systemMessages.map((m) => extractMessageText(m.content)).join("\n\n") : void 0;
|
|
6265
6622
|
const conversation = messages.filter(
|
|
6266
6623
|
(message) => message.role !== "system"
|
|
6267
6624
|
).map((message) => ({
|
|
@@ -6283,7 +6640,7 @@ var init_anthropic = __esm({
|
|
|
6283
6640
|
let totalChars = 0;
|
|
6284
6641
|
let imageCount = 0;
|
|
6285
6642
|
for (const msg of messages) {
|
|
6286
|
-
const parts =
|
|
6643
|
+
const parts = normalizeMessageContent(msg.content);
|
|
6287
6644
|
for (const part of parts) {
|
|
6288
6645
|
if (part.type === "text") {
|
|
6289
6646
|
totalChars += part.text.length;
|
|
@@ -6974,7 +7331,7 @@ var init_gemini = __esm({
|
|
|
6974
7331
|
format: spec?.defaultFormat ?? "wav"
|
|
6975
7332
|
};
|
|
6976
7333
|
}
|
|
6977
|
-
|
|
7334
|
+
buildApiRequest(options, descriptor, _spec, messages) {
|
|
6978
7335
|
const contents = this.convertMessagesToContents(messages);
|
|
6979
7336
|
const generationConfig = this.buildGenerationConfig(options);
|
|
6980
7337
|
const config = {
|
|
@@ -7026,7 +7383,7 @@ var init_gemini = __esm({
|
|
|
7026
7383
|
if (message.role === "system") {
|
|
7027
7384
|
expandedMessages.push({
|
|
7028
7385
|
role: "user",
|
|
7029
|
-
content:
|
|
7386
|
+
content: extractMessageText(message.content)
|
|
7030
7387
|
});
|
|
7031
7388
|
expandedMessages.push({
|
|
7032
7389
|
role: "assistant",
|
|
@@ -7076,7 +7433,7 @@ var init_gemini = __esm({
|
|
|
7076
7433
|
* Handles text, images, and audio (Gemini supports all three).
|
|
7077
7434
|
*/
|
|
7078
7435
|
convertToGeminiParts(content) {
|
|
7079
|
-
const parts =
|
|
7436
|
+
const parts = normalizeMessageContent(content);
|
|
7080
7437
|
return parts.map((part) => {
|
|
7081
7438
|
if (part.type === "text") {
|
|
7082
7439
|
return { text: part.text };
|
|
@@ -7121,10 +7478,10 @@ var init_gemini = __esm({
|
|
|
7121
7478
|
}
|
|
7122
7479
|
return Object.keys(config).length > 0 ? config : null;
|
|
7123
7480
|
}
|
|
7124
|
-
async *
|
|
7481
|
+
async *normalizeProviderStream(iterable) {
|
|
7125
7482
|
const stream2 = iterable;
|
|
7126
7483
|
for await (const chunk of stream2) {
|
|
7127
|
-
const text3 = this.
|
|
7484
|
+
const text3 = this.extractMessageText(chunk);
|
|
7128
7485
|
if (text3) {
|
|
7129
7486
|
yield { text: text3, rawEvent: chunk };
|
|
7130
7487
|
}
|
|
@@ -7135,7 +7492,7 @@ var init_gemini = __esm({
|
|
|
7135
7492
|
}
|
|
7136
7493
|
}
|
|
7137
7494
|
}
|
|
7138
|
-
|
|
7495
|
+
extractMessageText(chunk) {
|
|
7139
7496
|
if (!chunk?.candidates) {
|
|
7140
7497
|
return "";
|
|
7141
7498
|
}
|
|
@@ -7200,7 +7557,7 @@ var init_gemini = __esm({
|
|
|
7200
7557
|
let totalChars = 0;
|
|
7201
7558
|
let mediaCount = 0;
|
|
7202
7559
|
for (const msg of messages) {
|
|
7203
|
-
const parts =
|
|
7560
|
+
const parts = normalizeMessageContent(msg.content);
|
|
7204
7561
|
for (const part of parts) {
|
|
7205
7562
|
if (part.type === "text") {
|
|
7206
7563
|
totalChars += part.text.length;
|
|
@@ -7721,14 +8078,7 @@ var OPENAI_TTS_VOICES, OPENAI_TTS_EXTENDED_VOICES, OPENAI_TTS_FORMATS, openaiSpe
|
|
|
7721
8078
|
var init_openai_speech_models = __esm({
|
|
7722
8079
|
"src/providers/openai-speech-models.ts"() {
|
|
7723
8080
|
"use strict";
|
|
7724
|
-
OPENAI_TTS_VOICES = [
|
|
7725
|
-
"alloy",
|
|
7726
|
-
"echo",
|
|
7727
|
-
"fable",
|
|
7728
|
-
"onyx",
|
|
7729
|
-
"nova",
|
|
7730
|
-
"shimmer"
|
|
7731
|
-
];
|
|
8081
|
+
OPENAI_TTS_VOICES = ["alloy", "echo", "fable", "onyx", "nova", "shimmer"];
|
|
7732
8082
|
OPENAI_TTS_EXTENDED_VOICES = [
|
|
7733
8083
|
...OPENAI_TTS_VOICES,
|
|
7734
8084
|
"ash",
|
|
@@ -7953,7 +8303,7 @@ var init_openai = __esm({
|
|
|
7953
8303
|
format
|
|
7954
8304
|
};
|
|
7955
8305
|
}
|
|
7956
|
-
|
|
8306
|
+
buildApiRequest(options, descriptor, spec, messages) {
|
|
7957
8307
|
const { maxTokens, temperature, topP, stopSequences, extra } = options;
|
|
7958
8308
|
const supportsTemperature = spec?.metadata?.supportsTemperature !== false;
|
|
7959
8309
|
const shouldIncludeTemperature = typeof temperature === "number" && supportsTemperature;
|
|
@@ -7988,7 +8338,7 @@ var init_openai = __esm({
|
|
|
7988
8338
|
...message.name ? { name: message.name } : {}
|
|
7989
8339
|
};
|
|
7990
8340
|
}
|
|
7991
|
-
const textContent = typeof message.content === "string" ? message.content :
|
|
8341
|
+
const textContent = typeof message.content === "string" ? message.content : extractMessageText(message.content);
|
|
7992
8342
|
if (role === "system") {
|
|
7993
8343
|
return {
|
|
7994
8344
|
role: "system",
|
|
@@ -8048,7 +8398,7 @@ var init_openai = __esm({
|
|
|
8048
8398
|
const stream2 = await client.chat.completions.create(payload, signal ? { signal } : void 0);
|
|
8049
8399
|
return stream2;
|
|
8050
8400
|
}
|
|
8051
|
-
async *
|
|
8401
|
+
async *normalizeProviderStream(iterable) {
|
|
8052
8402
|
const stream2 = iterable;
|
|
8053
8403
|
for await (const chunk of stream2) {
|
|
8054
8404
|
const text3 = chunk.choices.map((choice) => choice.delta?.content ?? "").join("");
|
|
@@ -8106,9 +8456,9 @@ var init_openai = __esm({
|
|
|
8106
8456
|
tokenCount += OPENAI_MESSAGE_OVERHEAD_TOKENS;
|
|
8107
8457
|
const roleText = ROLE_MAP[message.role];
|
|
8108
8458
|
tokenCount += encoding.encode(roleText).length;
|
|
8109
|
-
const textContent =
|
|
8459
|
+
const textContent = extractMessageText(message.content);
|
|
8110
8460
|
tokenCount += encoding.encode(textContent).length;
|
|
8111
|
-
const parts =
|
|
8461
|
+
const parts = normalizeMessageContent(message.content);
|
|
8112
8462
|
for (const part of parts) {
|
|
8113
8463
|
if (part.type === "image") {
|
|
8114
8464
|
imageCount++;
|
|
@@ -8133,7 +8483,7 @@ var init_openai = __esm({
|
|
|
8133
8483
|
let totalChars = 0;
|
|
8134
8484
|
let imageCount = 0;
|
|
8135
8485
|
for (const msg of messages) {
|
|
8136
|
-
const parts =
|
|
8486
|
+
const parts = normalizeMessageContent(msg.content);
|
|
8137
8487
|
for (const part of parts) {
|
|
8138
8488
|
if (part.type === "text") {
|
|
8139
8489
|
totalChars += part.text.length;
|
|
@@ -8426,9 +8776,7 @@ var init_image = __esm({
|
|
|
8426
8776
|
return this.findImageAdapter(modelId) !== void 0;
|
|
8427
8777
|
}
|
|
8428
8778
|
findImageAdapter(modelId) {
|
|
8429
|
-
return this.adapters.find(
|
|
8430
|
-
(adapter) => adapter.supportsImageGeneration?.(modelId) ?? false
|
|
8431
|
-
);
|
|
8779
|
+
return this.adapters.find((adapter) => adapter.supportsImageGeneration?.(modelId) ?? false);
|
|
8432
8780
|
}
|
|
8433
8781
|
};
|
|
8434
8782
|
}
|
|
@@ -8480,9 +8828,7 @@ var init_speech = __esm({
|
|
|
8480
8828
|
return this.findSpeechAdapter(modelId) !== void 0;
|
|
8481
8829
|
}
|
|
8482
8830
|
findSpeechAdapter(modelId) {
|
|
8483
|
-
return this.adapters.find(
|
|
8484
|
-
(adapter) => adapter.supportsSpeechGeneration?.(modelId) ?? false
|
|
8485
|
-
);
|
|
8831
|
+
return this.adapters.find((adapter) => adapter.supportsSpeechGeneration?.(modelId) ?? false);
|
|
8486
8832
|
}
|
|
8487
8833
|
};
|
|
8488
8834
|
}
|
|
@@ -8593,11 +8939,7 @@ var init_vision = __esm({
|
|
|
8593
8939
|
if (!parsed) {
|
|
8594
8940
|
throw new Error("Invalid data URL format");
|
|
8595
8941
|
}
|
|
8596
|
-
builder.addUserWithImage(
|
|
8597
|
-
options.prompt,
|
|
8598
|
-
parsed.data,
|
|
8599
|
-
parsed.mimeType
|
|
8600
|
-
);
|
|
8942
|
+
builder.addUserWithImage(options.prompt, parsed.data, parsed.mimeType);
|
|
8601
8943
|
} else {
|
|
8602
8944
|
const buffer = Buffer.from(options.image, "base64");
|
|
8603
8945
|
builder.addUserWithImage(options.prompt, buffer, options.mimeType);
|
|
@@ -8983,37 +9325,262 @@ var init_client = __esm({
|
|
|
8983
9325
|
}
|
|
8984
9326
|
});
|
|
8985
9327
|
|
|
8986
|
-
// src/
|
|
8987
|
-
|
|
8988
|
-
|
|
8989
|
-
|
|
8990
|
-
|
|
8991
|
-
|
|
8992
|
-
|
|
8993
|
-
};
|
|
8994
|
-
}
|
|
8995
|
-
const issues = result.error.issues.map((issue) => ({
|
|
8996
|
-
path: issue.path.join(".") || "root",
|
|
8997
|
-
message: issue.message
|
|
8998
|
-
}));
|
|
8999
|
-
const formattedError = `Invalid parameters: ${issues.map((i) => `${i.path}: ${i.message}`).join("; ")}`;
|
|
9328
|
+
// src/testing/cli-helpers.ts
|
|
9329
|
+
import { PassThrough, Readable, Writable } from "node:stream";
|
|
9330
|
+
function createTestEnvironment(options = {}) {
|
|
9331
|
+
const stdin = createMockReadable(options.stdin);
|
|
9332
|
+
const stdout = new PassThrough();
|
|
9333
|
+
const stderr = new PassThrough();
|
|
9334
|
+
let exitCode;
|
|
9000
9335
|
return {
|
|
9001
|
-
|
|
9002
|
-
|
|
9003
|
-
|
|
9336
|
+
stdin,
|
|
9337
|
+
stdout,
|
|
9338
|
+
stderr,
|
|
9339
|
+
isTTY: options.isTTY ?? false,
|
|
9340
|
+
argv: options.argv ?? ["node", "llmist"],
|
|
9341
|
+
env: { ...filterDefinedEnv(process.env), ...options.env },
|
|
9342
|
+
get exitCode() {
|
|
9343
|
+
return exitCode;
|
|
9344
|
+
},
|
|
9345
|
+
setExitCode: (code) => {
|
|
9346
|
+
exitCode = code;
|
|
9347
|
+
}
|
|
9004
9348
|
};
|
|
9005
9349
|
}
|
|
9006
|
-
function
|
|
9007
|
-
if (!
|
|
9008
|
-
|
|
9009
|
-
|
|
9010
|
-
|
|
9011
|
-
|
|
9350
|
+
function createMockReadable(input) {
|
|
9351
|
+
if (!input) {
|
|
9352
|
+
const stream3 = new Readable({ read() {
|
|
9353
|
+
} });
|
|
9354
|
+
stream3.push(null);
|
|
9355
|
+
return stream3;
|
|
9012
9356
|
}
|
|
9013
|
-
|
|
9357
|
+
const content = Array.isArray(input) ? `${input.join("\n")}
|
|
9358
|
+
` : input;
|
|
9359
|
+
const stream2 = new Readable({ read() {
|
|
9360
|
+
} });
|
|
9361
|
+
stream2.push(content);
|
|
9362
|
+
stream2.push(null);
|
|
9363
|
+
return stream2;
|
|
9014
9364
|
}
|
|
9015
|
-
|
|
9016
|
-
|
|
9365
|
+
function createMockWritable() {
|
|
9366
|
+
const chunks = [];
|
|
9367
|
+
const stream2 = new Writable({
|
|
9368
|
+
write(chunk, _encoding, callback) {
|
|
9369
|
+
chunks.push(Buffer.from(chunk));
|
|
9370
|
+
callback();
|
|
9371
|
+
}
|
|
9372
|
+
});
|
|
9373
|
+
stream2.getData = () => Buffer.concat(chunks).toString("utf8");
|
|
9374
|
+
return stream2;
|
|
9375
|
+
}
|
|
9376
|
+
async function collectOutput(stream2, timeout = 5e3) {
|
|
9377
|
+
return new Promise((resolve, reject) => {
|
|
9378
|
+
const chunks = [];
|
|
9379
|
+
const timeoutId = setTimeout(() => {
|
|
9380
|
+
resolve(Buffer.concat(chunks).toString("utf8"));
|
|
9381
|
+
}, timeout);
|
|
9382
|
+
stream2.on("data", (chunk) => {
|
|
9383
|
+
chunks.push(Buffer.from(chunk));
|
|
9384
|
+
});
|
|
9385
|
+
stream2.on("end", () => {
|
|
9386
|
+
clearTimeout(timeoutId);
|
|
9387
|
+
resolve(Buffer.concat(chunks).toString("utf8"));
|
|
9388
|
+
});
|
|
9389
|
+
stream2.on("error", (err) => {
|
|
9390
|
+
clearTimeout(timeoutId);
|
|
9391
|
+
reject(err);
|
|
9392
|
+
});
|
|
9393
|
+
});
|
|
9394
|
+
}
|
|
9395
|
+
function getBufferedOutput(stream2) {
|
|
9396
|
+
const chunks = [];
|
|
9397
|
+
for (; ; ) {
|
|
9398
|
+
const chunk = stream2.read();
|
|
9399
|
+
if (chunk === null) break;
|
|
9400
|
+
chunks.push(chunk);
|
|
9401
|
+
}
|
|
9402
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
9403
|
+
}
|
|
9404
|
+
function createMockPrompt(responses) {
|
|
9405
|
+
let index = 0;
|
|
9406
|
+
return async (_question) => {
|
|
9407
|
+
if (index >= responses.length) {
|
|
9408
|
+
throw new Error(`Mock prompt exhausted: no response for question ${index + 1}`);
|
|
9409
|
+
}
|
|
9410
|
+
return responses[index++];
|
|
9411
|
+
};
|
|
9412
|
+
}
|
|
9413
|
+
var MockPromptRecorder = class {
|
|
9414
|
+
responses;
|
|
9415
|
+
index = 0;
|
|
9416
|
+
questions = [];
|
|
9417
|
+
constructor(responses) {
|
|
9418
|
+
this.responses = responses;
|
|
9419
|
+
}
|
|
9420
|
+
/**
|
|
9421
|
+
* The prompt function to use in tests.
|
|
9422
|
+
*/
|
|
9423
|
+
prompt = async (question) => {
|
|
9424
|
+
this.questions.push(question);
|
|
9425
|
+
if (this.index >= this.responses.length) {
|
|
9426
|
+
throw new Error(`Mock prompt exhausted after ${this.index} questions`);
|
|
9427
|
+
}
|
|
9428
|
+
return this.responses[this.index++];
|
|
9429
|
+
};
|
|
9430
|
+
/**
|
|
9431
|
+
* Get all questions that were asked.
|
|
9432
|
+
*/
|
|
9433
|
+
getQuestions() {
|
|
9434
|
+
return [...this.questions];
|
|
9435
|
+
}
|
|
9436
|
+
/**
|
|
9437
|
+
* Get the number of questions asked.
|
|
9438
|
+
*/
|
|
9439
|
+
getQuestionCount() {
|
|
9440
|
+
return this.questions.length;
|
|
9441
|
+
}
|
|
9442
|
+
/**
|
|
9443
|
+
* Reset the recorder state.
|
|
9444
|
+
*/
|
|
9445
|
+
reset(newResponses) {
|
|
9446
|
+
this.index = 0;
|
|
9447
|
+
this.questions = [];
|
|
9448
|
+
if (newResponses) {
|
|
9449
|
+
this.responses = newResponses;
|
|
9450
|
+
}
|
|
9451
|
+
}
|
|
9452
|
+
};
|
|
9453
|
+
async function waitFor(condition, timeout = 5e3, interval = 50) {
|
|
9454
|
+
const startTime = Date.now();
|
|
9455
|
+
while (!condition()) {
|
|
9456
|
+
if (Date.now() - startTime > timeout) {
|
|
9457
|
+
throw new Error(`waitFor timed out after ${timeout}ms`);
|
|
9458
|
+
}
|
|
9459
|
+
await sleep(interval);
|
|
9460
|
+
}
|
|
9461
|
+
}
|
|
9462
|
+
function sleep(ms) {
|
|
9463
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
9464
|
+
}
|
|
9465
|
+
function filterDefinedEnv(env) {
|
|
9466
|
+
const result = {};
|
|
9467
|
+
for (const [key, value] of Object.entries(env)) {
|
|
9468
|
+
if (value !== void 0) {
|
|
9469
|
+
result[key] = value;
|
|
9470
|
+
}
|
|
9471
|
+
}
|
|
9472
|
+
return result;
|
|
9473
|
+
}
|
|
9474
|
+
|
|
9475
|
+
// src/testing/conversation-fixtures.ts
|
|
9476
|
+
function createConversation(turnCount, options) {
|
|
9477
|
+
const messages = [];
|
|
9478
|
+
const userPrefix = options?.userPrefix ?? "User message";
|
|
9479
|
+
const assistantPrefix = options?.assistantPrefix ?? "Assistant response";
|
|
9480
|
+
const contentLength = options?.contentLength ?? 100;
|
|
9481
|
+
for (let i = 0; i < turnCount; i++) {
|
|
9482
|
+
const padding = " ".repeat(Math.max(0, contentLength - 30));
|
|
9483
|
+
messages.push({
|
|
9484
|
+
role: "user",
|
|
9485
|
+
content: `${userPrefix} ${i + 1}: This is turn ${i + 1} of the conversation.${padding}`
|
|
9486
|
+
});
|
|
9487
|
+
messages.push({
|
|
9488
|
+
role: "assistant",
|
|
9489
|
+
content: `${assistantPrefix} ${i + 1}: I acknowledge turn ${i + 1}.${padding}`
|
|
9490
|
+
});
|
|
9491
|
+
}
|
|
9492
|
+
return messages;
|
|
9493
|
+
}
|
|
9494
|
+
function createConversationWithGadgets(turnCount, gadgetCallsPerTurn = 1, options) {
|
|
9495
|
+
const messages = [];
|
|
9496
|
+
const gadgetNames = options?.gadgetNames ?? ["search", "calculate", "read"];
|
|
9497
|
+
const contentLength = options?.contentLength ?? 50;
|
|
9498
|
+
let gadgetIndex = 0;
|
|
9499
|
+
for (let turn = 0; turn < turnCount; turn++) {
|
|
9500
|
+
messages.push({
|
|
9501
|
+
role: "user",
|
|
9502
|
+
content: `User request ${turn + 1}${"x".repeat(contentLength)}`
|
|
9503
|
+
});
|
|
9504
|
+
for (let g = 0; g < gadgetCallsPerTurn; g++) {
|
|
9505
|
+
const gadgetName = gadgetNames[gadgetIndex % gadgetNames.length];
|
|
9506
|
+
gadgetIndex++;
|
|
9507
|
+
messages.push({
|
|
9508
|
+
role: "assistant",
|
|
9509
|
+
content: `!!!GADGET_START:${gadgetName}
|
|
9510
|
+
!!!ARG:query
|
|
9511
|
+
test query ${turn}-${g}
|
|
9512
|
+
!!!GADGET_END`
|
|
9513
|
+
});
|
|
9514
|
+
messages.push({
|
|
9515
|
+
role: "user",
|
|
9516
|
+
content: `Result: Gadget ${gadgetName} returned result for query ${turn}-${g}`
|
|
9517
|
+
});
|
|
9518
|
+
}
|
|
9519
|
+
messages.push({
|
|
9520
|
+
role: "assistant",
|
|
9521
|
+
content: `Final response for turn ${turn + 1}${"y".repeat(contentLength)}`
|
|
9522
|
+
});
|
|
9523
|
+
}
|
|
9524
|
+
return messages;
|
|
9525
|
+
}
|
|
9526
|
+
function estimateTokens(messages) {
|
|
9527
|
+
return Math.ceil(messages.reduce((sum, msg) => sum + (msg.content?.length ?? 0), 0) / 4);
|
|
9528
|
+
}
|
|
9529
|
+
function createUserMessage(content) {
|
|
9530
|
+
return { role: "user", content };
|
|
9531
|
+
}
|
|
9532
|
+
function createAssistantMessage(content) {
|
|
9533
|
+
return { role: "assistant", content };
|
|
9534
|
+
}
|
|
9535
|
+
function createSystemMessage(content) {
|
|
9536
|
+
return { role: "system", content };
|
|
9537
|
+
}
|
|
9538
|
+
function createMinimalConversation() {
|
|
9539
|
+
return [
|
|
9540
|
+
{ role: "user", content: "Hello" },
|
|
9541
|
+
{ role: "assistant", content: "Hi there!" }
|
|
9542
|
+
];
|
|
9543
|
+
}
|
|
9544
|
+
function createLargeConversation(targetTokens, options) {
|
|
9545
|
+
const tokensPerTurn = options?.tokensPerTurn ?? 200;
|
|
9546
|
+
const turnsNeeded = Math.ceil(targetTokens / tokensPerTurn);
|
|
9547
|
+
const charsPerMessage = Math.floor(tokensPerTurn * 4 / 2);
|
|
9548
|
+
return createConversation(turnsNeeded, {
|
|
9549
|
+
contentLength: charsPerMessage
|
|
9550
|
+
});
|
|
9551
|
+
}
|
|
9552
|
+
|
|
9553
|
+
// src/gadgets/validation.ts
|
|
9554
|
+
function validateAndApplyDefaults(schema, params) {
|
|
9555
|
+
const result = schema.safeParse(params);
|
|
9556
|
+
if (result.success) {
|
|
9557
|
+
return {
|
|
9558
|
+
success: true,
|
|
9559
|
+
data: result.data
|
|
9560
|
+
};
|
|
9561
|
+
}
|
|
9562
|
+
const issues = result.error.issues.map((issue) => ({
|
|
9563
|
+
path: issue.path.join(".") || "root",
|
|
9564
|
+
message: issue.message
|
|
9565
|
+
}));
|
|
9566
|
+
const formattedError = `Invalid parameters: ${issues.map((i) => `${i.path}: ${i.message}`).join("; ")}`;
|
|
9567
|
+
return {
|
|
9568
|
+
success: false,
|
|
9569
|
+
error: formattedError,
|
|
9570
|
+
issues
|
|
9571
|
+
};
|
|
9572
|
+
}
|
|
9573
|
+
function validateGadgetParams(gadget, params) {
|
|
9574
|
+
if (!gadget.parameterSchema) {
|
|
9575
|
+
return {
|
|
9576
|
+
success: true,
|
|
9577
|
+
data: params
|
|
9578
|
+
};
|
|
9579
|
+
}
|
|
9580
|
+
return validateAndApplyDefaults(gadget.parameterSchema, params);
|
|
9581
|
+
}
|
|
9582
|
+
|
|
9583
|
+
// src/testing/gadget-testing.ts
|
|
9017
9584
|
async function testGadget(gadget, params, options) {
|
|
9018
9585
|
let validatedParams = params;
|
|
9019
9586
|
if (!options?.skipValidation) {
|
|
@@ -9220,7 +9787,7 @@ function getMockManager(options) {
|
|
|
9220
9787
|
|
|
9221
9788
|
// src/testing/mock-stream.ts
|
|
9222
9789
|
init_constants();
|
|
9223
|
-
function
|
|
9790
|
+
function sleep2(ms) {
|
|
9224
9791
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
9225
9792
|
}
|
|
9226
9793
|
function generateInvocationId() {
|
|
@@ -9301,7 +9868,7 @@ ${blockParams}${GADGET_END_PREFIX}`;
|
|
|
9301
9868
|
}
|
|
9302
9869
|
async function* createMockStream(response) {
|
|
9303
9870
|
if (response.delayMs) {
|
|
9304
|
-
await
|
|
9871
|
+
await sleep2(response.delayMs);
|
|
9305
9872
|
}
|
|
9306
9873
|
const streamDelay = response.streamDelayMs ?? 0;
|
|
9307
9874
|
let fullText = response.text ?? "";
|
|
@@ -9326,7 +9893,7 @@ async function* createMockStream(response) {
|
|
|
9326
9893
|
}
|
|
9327
9894
|
yield chunk;
|
|
9328
9895
|
if (streamDelay > 0 && !isLast) {
|
|
9329
|
-
await
|
|
9896
|
+
await sleep2(streamDelay);
|
|
9330
9897
|
}
|
|
9331
9898
|
}
|
|
9332
9899
|
} else {
|
|
@@ -9603,7 +10170,9 @@ var MockBuilder = class {
|
|
|
9603
10170
|
*/
|
|
9604
10171
|
whenMessageContains(text3) {
|
|
9605
10172
|
this.matchers.push(
|
|
9606
|
-
(ctx) => ctx.messages.some(
|
|
10173
|
+
(ctx) => ctx.messages.some(
|
|
10174
|
+
(msg) => extractMessageText(msg.content).toLowerCase().includes(text3.toLowerCase())
|
|
10175
|
+
)
|
|
9607
10176
|
);
|
|
9608
10177
|
return this;
|
|
9609
10178
|
}
|
|
@@ -9617,7 +10186,7 @@ var MockBuilder = class {
|
|
|
9617
10186
|
this.matchers.push((ctx) => {
|
|
9618
10187
|
const lastMsg = ctx.messages[ctx.messages.length - 1];
|
|
9619
10188
|
if (!lastMsg) return false;
|
|
9620
|
-
return
|
|
10189
|
+
return extractMessageText(lastMsg.content).toLowerCase().includes(text3.toLowerCase());
|
|
9621
10190
|
});
|
|
9622
10191
|
return this;
|
|
9623
10192
|
}
|
|
@@ -9628,7 +10197,7 @@ var MockBuilder = class {
|
|
|
9628
10197
|
* mockLLM().whenMessageMatches(/calculate \d+/)
|
|
9629
10198
|
*/
|
|
9630
10199
|
whenMessageMatches(regex) {
|
|
9631
|
-
this.matchers.push((ctx) => ctx.messages.some((msg) => regex.test(
|
|
10200
|
+
this.matchers.push((ctx) => ctx.messages.some((msg) => regex.test(extractMessageText(msg.content))));
|
|
9632
10201
|
return this;
|
|
9633
10202
|
}
|
|
9634
10203
|
/**
|
|
@@ -9640,7 +10209,7 @@ var MockBuilder = class {
|
|
|
9640
10209
|
whenRoleContains(role, text3) {
|
|
9641
10210
|
this.matchers.push(
|
|
9642
10211
|
(ctx) => ctx.messages.some(
|
|
9643
|
-
(msg) => msg.role === role &&
|
|
10212
|
+
(msg) => msg.role === role && extractMessageText(msg.content).toLowerCase().includes(text3.toLowerCase())
|
|
9644
10213
|
)
|
|
9645
10214
|
);
|
|
9646
10215
|
return this;
|
|
@@ -10041,41 +10610,170 @@ function createMockClient(options) {
|
|
|
10041
10610
|
});
|
|
10042
10611
|
}
|
|
10043
10612
|
|
|
10044
|
-
// src/testing/mock-
|
|
10045
|
-
|
|
10046
|
-
|
|
10047
|
-
|
|
10048
|
-
|
|
10049
|
-
|
|
10050
|
-
|
|
10051
|
-
|
|
10052
|
-
|
|
10053
|
-
|
|
10054
|
-
|
|
10055
|
-
|
|
10056
|
-
|
|
10057
|
-
|
|
10058
|
-
|
|
10059
|
-
|
|
10060
|
-
|
|
10061
|
-
|
|
10062
|
-
this.
|
|
10063
|
-
this.
|
|
10064
|
-
|
|
10065
|
-
|
|
10066
|
-
|
|
10067
|
-
|
|
10068
|
-
|
|
10069
|
-
|
|
10070
|
-
|
|
10071
|
-
|
|
10072
|
-
|
|
10073
|
-
|
|
10074
|
-
|
|
10075
|
-
|
|
10076
|
-
|
|
10077
|
-
|
|
10078
|
-
|
|
10613
|
+
// src/testing/mock-conversation.ts
|
|
10614
|
+
var MockConversationManager = class {
|
|
10615
|
+
history;
|
|
10616
|
+
baseMessages;
|
|
10617
|
+
replacementHistory;
|
|
10618
|
+
replaceHistoryCallCount = 0;
|
|
10619
|
+
addedMessages = [];
|
|
10620
|
+
constructor(history = [], baseMessages = []) {
|
|
10621
|
+
this.history = [...history];
|
|
10622
|
+
this.baseMessages = [...baseMessages];
|
|
10623
|
+
}
|
|
10624
|
+
addUserMessage(content) {
|
|
10625
|
+
const msg = { role: "user", content };
|
|
10626
|
+
this.history.push(msg);
|
|
10627
|
+
this.addedMessages.push(msg);
|
|
10628
|
+
}
|
|
10629
|
+
addAssistantMessage(content) {
|
|
10630
|
+
const msg = { role: "assistant", content };
|
|
10631
|
+
this.history.push(msg);
|
|
10632
|
+
this.addedMessages.push(msg);
|
|
10633
|
+
}
|
|
10634
|
+
addGadgetCallResult(gadgetName, parameters, result) {
|
|
10635
|
+
const assistantMsg = {
|
|
10636
|
+
role: "assistant",
|
|
10637
|
+
content: `!!!GADGET_START:${gadgetName}
|
|
10638
|
+
${JSON.stringify(parameters)}
|
|
10639
|
+
!!!GADGET_END`
|
|
10640
|
+
};
|
|
10641
|
+
const resultMsg = {
|
|
10642
|
+
role: "user",
|
|
10643
|
+
content: `Result: ${result}`
|
|
10644
|
+
};
|
|
10645
|
+
this.history.push(assistantMsg);
|
|
10646
|
+
this.history.push(resultMsg);
|
|
10647
|
+
this.addedMessages.push(assistantMsg);
|
|
10648
|
+
this.addedMessages.push(resultMsg);
|
|
10649
|
+
}
|
|
10650
|
+
getMessages() {
|
|
10651
|
+
return [...this.baseMessages, ...this.history];
|
|
10652
|
+
}
|
|
10653
|
+
getHistoryMessages() {
|
|
10654
|
+
return [...this.history];
|
|
10655
|
+
}
|
|
10656
|
+
getBaseMessages() {
|
|
10657
|
+
return [...this.baseMessages];
|
|
10658
|
+
}
|
|
10659
|
+
replaceHistory(newHistory) {
|
|
10660
|
+
this.replacementHistory = [...newHistory];
|
|
10661
|
+
this.history = [...newHistory];
|
|
10662
|
+
this.replaceHistoryCallCount++;
|
|
10663
|
+
}
|
|
10664
|
+
// ============================================
|
|
10665
|
+
// Test Helper Methods
|
|
10666
|
+
// ============================================
|
|
10667
|
+
/**
|
|
10668
|
+
* Check if replaceHistory was called.
|
|
10669
|
+
*/
|
|
10670
|
+
wasReplaceHistoryCalled() {
|
|
10671
|
+
return this.replaceHistoryCallCount > 0;
|
|
10672
|
+
}
|
|
10673
|
+
/**
|
|
10674
|
+
* Get the number of times replaceHistory was called.
|
|
10675
|
+
*/
|
|
10676
|
+
getReplaceHistoryCallCount() {
|
|
10677
|
+
return this.replaceHistoryCallCount;
|
|
10678
|
+
}
|
|
10679
|
+
/**
|
|
10680
|
+
* Get the most recent history passed to replaceHistory.
|
|
10681
|
+
* Returns undefined if replaceHistory was never called.
|
|
10682
|
+
*/
|
|
10683
|
+
getReplacementHistory() {
|
|
10684
|
+
return this.replacementHistory;
|
|
10685
|
+
}
|
|
10686
|
+
/**
|
|
10687
|
+
* Get all messages that were added via add* methods.
|
|
10688
|
+
*/
|
|
10689
|
+
getAddedMessages() {
|
|
10690
|
+
return [...this.addedMessages];
|
|
10691
|
+
}
|
|
10692
|
+
/**
|
|
10693
|
+
* Reset all tracking state while preserving the conversation.
|
|
10694
|
+
*/
|
|
10695
|
+
resetTracking() {
|
|
10696
|
+
this.replacementHistory = void 0;
|
|
10697
|
+
this.replaceHistoryCallCount = 0;
|
|
10698
|
+
this.addedMessages = [];
|
|
10699
|
+
}
|
|
10700
|
+
/**
|
|
10701
|
+
* Completely reset the mock to initial state.
|
|
10702
|
+
* Note: baseMessages cannot be changed after construction.
|
|
10703
|
+
*/
|
|
10704
|
+
reset(history = []) {
|
|
10705
|
+
this.history = [...history];
|
|
10706
|
+
this.resetTracking();
|
|
10707
|
+
}
|
|
10708
|
+
/**
|
|
10709
|
+
* Set the history directly (for test setup).
|
|
10710
|
+
*/
|
|
10711
|
+
setHistory(messages) {
|
|
10712
|
+
this.history = [...messages];
|
|
10713
|
+
}
|
|
10714
|
+
/**
|
|
10715
|
+
* Get the current history length.
|
|
10716
|
+
*/
|
|
10717
|
+
getHistoryLength() {
|
|
10718
|
+
return this.history.length;
|
|
10719
|
+
}
|
|
10720
|
+
/**
|
|
10721
|
+
* Get total message count (base + history).
|
|
10722
|
+
*/
|
|
10723
|
+
getTotalMessageCount() {
|
|
10724
|
+
return this.baseMessages.length + this.history.length;
|
|
10725
|
+
}
|
|
10726
|
+
};
|
|
10727
|
+
function createMockConversationManager(turnCount, baseMessages = []) {
|
|
10728
|
+
const history = [];
|
|
10729
|
+
for (let i = 0; i < turnCount; i++) {
|
|
10730
|
+
history.push({
|
|
10731
|
+
role: "user",
|
|
10732
|
+
content: `User message ${i + 1}: This is turn ${i + 1} of the conversation.`
|
|
10733
|
+
});
|
|
10734
|
+
history.push({
|
|
10735
|
+
role: "assistant",
|
|
10736
|
+
content: `Assistant response ${i + 1}: I acknowledge turn ${i + 1}.`
|
|
10737
|
+
});
|
|
10738
|
+
}
|
|
10739
|
+
return new MockConversationManager(history, baseMessages);
|
|
10740
|
+
}
|
|
10741
|
+
|
|
10742
|
+
// src/testing/mock-gadget.ts
|
|
10743
|
+
init_gadget();
|
|
10744
|
+
var MockGadgetImpl = class extends AbstractGadget {
|
|
10745
|
+
name;
|
|
10746
|
+
description;
|
|
10747
|
+
parameterSchema;
|
|
10748
|
+
timeoutMs;
|
|
10749
|
+
calls = [];
|
|
10750
|
+
resultValue;
|
|
10751
|
+
resultFn;
|
|
10752
|
+
errorToThrow;
|
|
10753
|
+
delayMs;
|
|
10754
|
+
shouldTrackCalls;
|
|
10755
|
+
constructor(config) {
|
|
10756
|
+
super();
|
|
10757
|
+
this.name = config.name;
|
|
10758
|
+
this.description = config.description ?? `Mock gadget: ${config.name}`;
|
|
10759
|
+
this.parameterSchema = config.schema;
|
|
10760
|
+
this.resultValue = config.result;
|
|
10761
|
+
this.resultFn = config.resultFn;
|
|
10762
|
+
this.delayMs = config.delayMs ?? 0;
|
|
10763
|
+
this.shouldTrackCalls = config.trackCalls ?? true;
|
|
10764
|
+
this.timeoutMs = config.timeoutMs;
|
|
10765
|
+
if (config.error) {
|
|
10766
|
+
this.errorToThrow = typeof config.error === "string" ? new Error(config.error) : config.error;
|
|
10767
|
+
}
|
|
10768
|
+
}
|
|
10769
|
+
async execute(params) {
|
|
10770
|
+
if (this.shouldTrackCalls) {
|
|
10771
|
+
this.calls.push({ params: { ...params }, timestamp: Date.now() });
|
|
10772
|
+
}
|
|
10773
|
+
if (this.delayMs > 0) {
|
|
10774
|
+
await new Promise((resolve) => setTimeout(resolve, this.delayMs));
|
|
10775
|
+
}
|
|
10776
|
+
if (this.errorToThrow) {
|
|
10079
10777
|
throw this.errorToThrow;
|
|
10080
10778
|
}
|
|
10081
10779
|
if (this.resultFn) {
|
|
@@ -10200,7 +10898,7 @@ function createTestStream(chunks) {
|
|
|
10200
10898
|
function createTextStream(text3, options) {
|
|
10201
10899
|
return async function* () {
|
|
10202
10900
|
if (options?.delayMs) {
|
|
10203
|
-
await
|
|
10901
|
+
await sleep3(options.delayMs);
|
|
10204
10902
|
}
|
|
10205
10903
|
const chunkSize = options?.chunkSize ?? text3.length;
|
|
10206
10904
|
const chunks = [];
|
|
@@ -10222,7 +10920,7 @@ function createTextStream(text3, options) {
|
|
|
10222
10920
|
}
|
|
10223
10921
|
yield chunk;
|
|
10224
10922
|
if (options?.chunkDelayMs && !isLast) {
|
|
10225
|
-
await
|
|
10923
|
+
await sleep3(options.chunkDelayMs);
|
|
10226
10924
|
}
|
|
10227
10925
|
}
|
|
10228
10926
|
}();
|
|
@@ -10260,366 +10958,10 @@ function createErrorStream(chunksBeforeError, error) {
|
|
|
10260
10958
|
throw error;
|
|
10261
10959
|
}();
|
|
10262
10960
|
}
|
|
10263
|
-
function
|
|
10961
|
+
function sleep3(ms) {
|
|
10264
10962
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
10265
10963
|
}
|
|
10266
10964
|
|
|
10267
|
-
// src/testing/conversation-fixtures.ts
|
|
10268
|
-
function createConversation(turnCount, options) {
|
|
10269
|
-
const messages = [];
|
|
10270
|
-
const userPrefix = options?.userPrefix ?? "User message";
|
|
10271
|
-
const assistantPrefix = options?.assistantPrefix ?? "Assistant response";
|
|
10272
|
-
const contentLength = options?.contentLength ?? 100;
|
|
10273
|
-
for (let i = 0; i < turnCount; i++) {
|
|
10274
|
-
const padding = " ".repeat(Math.max(0, contentLength - 30));
|
|
10275
|
-
messages.push({
|
|
10276
|
-
role: "user",
|
|
10277
|
-
content: `${userPrefix} ${i + 1}: This is turn ${i + 1} of the conversation.${padding}`
|
|
10278
|
-
});
|
|
10279
|
-
messages.push({
|
|
10280
|
-
role: "assistant",
|
|
10281
|
-
content: `${assistantPrefix} ${i + 1}: I acknowledge turn ${i + 1}.${padding}`
|
|
10282
|
-
});
|
|
10283
|
-
}
|
|
10284
|
-
return messages;
|
|
10285
|
-
}
|
|
10286
|
-
function createConversationWithGadgets(turnCount, gadgetCallsPerTurn = 1, options) {
|
|
10287
|
-
const messages = [];
|
|
10288
|
-
const gadgetNames = options?.gadgetNames ?? ["search", "calculate", "read"];
|
|
10289
|
-
const contentLength = options?.contentLength ?? 50;
|
|
10290
|
-
let gadgetIndex = 0;
|
|
10291
|
-
for (let turn = 0; turn < turnCount; turn++) {
|
|
10292
|
-
messages.push({
|
|
10293
|
-
role: "user",
|
|
10294
|
-
content: `User request ${turn + 1}${"x".repeat(contentLength)}`
|
|
10295
|
-
});
|
|
10296
|
-
for (let g = 0; g < gadgetCallsPerTurn; g++) {
|
|
10297
|
-
const gadgetName = gadgetNames[gadgetIndex % gadgetNames.length];
|
|
10298
|
-
gadgetIndex++;
|
|
10299
|
-
messages.push({
|
|
10300
|
-
role: "assistant",
|
|
10301
|
-
content: `!!!GADGET_START:${gadgetName}
|
|
10302
|
-
!!!ARG:query
|
|
10303
|
-
test query ${turn}-${g}
|
|
10304
|
-
!!!GADGET_END`
|
|
10305
|
-
});
|
|
10306
|
-
messages.push({
|
|
10307
|
-
role: "user",
|
|
10308
|
-
content: `Result: Gadget ${gadgetName} returned result for query ${turn}-${g}`
|
|
10309
|
-
});
|
|
10310
|
-
}
|
|
10311
|
-
messages.push({
|
|
10312
|
-
role: "assistant",
|
|
10313
|
-
content: `Final response for turn ${turn + 1}${"y".repeat(contentLength)}`
|
|
10314
|
-
});
|
|
10315
|
-
}
|
|
10316
|
-
return messages;
|
|
10317
|
-
}
|
|
10318
|
-
function estimateTokens(messages) {
|
|
10319
|
-
return Math.ceil(
|
|
10320
|
-
messages.reduce((sum, msg) => sum + (msg.content?.length ?? 0), 0) / 4
|
|
10321
|
-
);
|
|
10322
|
-
}
|
|
10323
|
-
function createUserMessage(content) {
|
|
10324
|
-
return { role: "user", content };
|
|
10325
|
-
}
|
|
10326
|
-
function createAssistantMessage(content) {
|
|
10327
|
-
return { role: "assistant", content };
|
|
10328
|
-
}
|
|
10329
|
-
function createSystemMessage(content) {
|
|
10330
|
-
return { role: "system", content };
|
|
10331
|
-
}
|
|
10332
|
-
function createMinimalConversation() {
|
|
10333
|
-
return [
|
|
10334
|
-
{ role: "user", content: "Hello" },
|
|
10335
|
-
{ role: "assistant", content: "Hi there!" }
|
|
10336
|
-
];
|
|
10337
|
-
}
|
|
10338
|
-
function createLargeConversation(targetTokens, options) {
|
|
10339
|
-
const tokensPerTurn = options?.tokensPerTurn ?? 200;
|
|
10340
|
-
const turnsNeeded = Math.ceil(targetTokens / tokensPerTurn);
|
|
10341
|
-
const charsPerMessage = Math.floor(tokensPerTurn * 4 / 2);
|
|
10342
|
-
return createConversation(turnsNeeded, {
|
|
10343
|
-
contentLength: charsPerMessage
|
|
10344
|
-
});
|
|
10345
|
-
}
|
|
10346
|
-
|
|
10347
|
-
// src/testing/mock-conversation.ts
|
|
10348
|
-
var MockConversationManager = class {
|
|
10349
|
-
history;
|
|
10350
|
-
baseMessages;
|
|
10351
|
-
replacementHistory;
|
|
10352
|
-
replaceHistoryCallCount = 0;
|
|
10353
|
-
addedMessages = [];
|
|
10354
|
-
constructor(history = [], baseMessages = []) {
|
|
10355
|
-
this.history = [...history];
|
|
10356
|
-
this.baseMessages = [...baseMessages];
|
|
10357
|
-
}
|
|
10358
|
-
addUserMessage(content) {
|
|
10359
|
-
const msg = { role: "user", content };
|
|
10360
|
-
this.history.push(msg);
|
|
10361
|
-
this.addedMessages.push(msg);
|
|
10362
|
-
}
|
|
10363
|
-
addAssistantMessage(content) {
|
|
10364
|
-
const msg = { role: "assistant", content };
|
|
10365
|
-
this.history.push(msg);
|
|
10366
|
-
this.addedMessages.push(msg);
|
|
10367
|
-
}
|
|
10368
|
-
addGadgetCall(gadgetName, parameters, result) {
|
|
10369
|
-
const assistantMsg = {
|
|
10370
|
-
role: "assistant",
|
|
10371
|
-
content: `!!!GADGET_START:${gadgetName}
|
|
10372
|
-
${JSON.stringify(parameters)}
|
|
10373
|
-
!!!GADGET_END`
|
|
10374
|
-
};
|
|
10375
|
-
const resultMsg = {
|
|
10376
|
-
role: "user",
|
|
10377
|
-
content: `Result: ${result}`
|
|
10378
|
-
};
|
|
10379
|
-
this.history.push(assistantMsg);
|
|
10380
|
-
this.history.push(resultMsg);
|
|
10381
|
-
this.addedMessages.push(assistantMsg);
|
|
10382
|
-
this.addedMessages.push(resultMsg);
|
|
10383
|
-
}
|
|
10384
|
-
getMessages() {
|
|
10385
|
-
return [...this.baseMessages, ...this.history];
|
|
10386
|
-
}
|
|
10387
|
-
getHistoryMessages() {
|
|
10388
|
-
return [...this.history];
|
|
10389
|
-
}
|
|
10390
|
-
getBaseMessages() {
|
|
10391
|
-
return [...this.baseMessages];
|
|
10392
|
-
}
|
|
10393
|
-
replaceHistory(newHistory) {
|
|
10394
|
-
this.replacementHistory = [...newHistory];
|
|
10395
|
-
this.history = [...newHistory];
|
|
10396
|
-
this.replaceHistoryCallCount++;
|
|
10397
|
-
}
|
|
10398
|
-
// ============================================
|
|
10399
|
-
// Test Helper Methods
|
|
10400
|
-
// ============================================
|
|
10401
|
-
/**
|
|
10402
|
-
* Check if replaceHistory was called.
|
|
10403
|
-
*/
|
|
10404
|
-
wasReplaceHistoryCalled() {
|
|
10405
|
-
return this.replaceHistoryCallCount > 0;
|
|
10406
|
-
}
|
|
10407
|
-
/**
|
|
10408
|
-
* Get the number of times replaceHistory was called.
|
|
10409
|
-
*/
|
|
10410
|
-
getReplaceHistoryCallCount() {
|
|
10411
|
-
return this.replaceHistoryCallCount;
|
|
10412
|
-
}
|
|
10413
|
-
/**
|
|
10414
|
-
* Get the most recent history passed to replaceHistory.
|
|
10415
|
-
* Returns undefined if replaceHistory was never called.
|
|
10416
|
-
*/
|
|
10417
|
-
getReplacementHistory() {
|
|
10418
|
-
return this.replacementHistory;
|
|
10419
|
-
}
|
|
10420
|
-
/**
|
|
10421
|
-
* Get all messages that were added via add* methods.
|
|
10422
|
-
*/
|
|
10423
|
-
getAddedMessages() {
|
|
10424
|
-
return [...this.addedMessages];
|
|
10425
|
-
}
|
|
10426
|
-
/**
|
|
10427
|
-
* Reset all tracking state while preserving the conversation.
|
|
10428
|
-
*/
|
|
10429
|
-
resetTracking() {
|
|
10430
|
-
this.replacementHistory = void 0;
|
|
10431
|
-
this.replaceHistoryCallCount = 0;
|
|
10432
|
-
this.addedMessages = [];
|
|
10433
|
-
}
|
|
10434
|
-
/**
|
|
10435
|
-
* Completely reset the mock to initial state.
|
|
10436
|
-
* Note: baseMessages cannot be changed after construction.
|
|
10437
|
-
*/
|
|
10438
|
-
reset(history = []) {
|
|
10439
|
-
this.history = [...history];
|
|
10440
|
-
this.resetTracking();
|
|
10441
|
-
}
|
|
10442
|
-
/**
|
|
10443
|
-
* Set the history directly (for test setup).
|
|
10444
|
-
*/
|
|
10445
|
-
setHistory(messages) {
|
|
10446
|
-
this.history = [...messages];
|
|
10447
|
-
}
|
|
10448
|
-
/**
|
|
10449
|
-
* Get the current history length.
|
|
10450
|
-
*/
|
|
10451
|
-
getHistoryLength() {
|
|
10452
|
-
return this.history.length;
|
|
10453
|
-
}
|
|
10454
|
-
/**
|
|
10455
|
-
* Get total message count (base + history).
|
|
10456
|
-
*/
|
|
10457
|
-
getTotalMessageCount() {
|
|
10458
|
-
return this.baseMessages.length + this.history.length;
|
|
10459
|
-
}
|
|
10460
|
-
};
|
|
10461
|
-
function createMockConversationManager(turnCount, baseMessages = []) {
|
|
10462
|
-
const history = [];
|
|
10463
|
-
for (let i = 0; i < turnCount; i++) {
|
|
10464
|
-
history.push({
|
|
10465
|
-
role: "user",
|
|
10466
|
-
content: `User message ${i + 1}: This is turn ${i + 1} of the conversation.`
|
|
10467
|
-
});
|
|
10468
|
-
history.push({
|
|
10469
|
-
role: "assistant",
|
|
10470
|
-
content: `Assistant response ${i + 1}: I acknowledge turn ${i + 1}.`
|
|
10471
|
-
});
|
|
10472
|
-
}
|
|
10473
|
-
return new MockConversationManager(history, baseMessages);
|
|
10474
|
-
}
|
|
10475
|
-
|
|
10476
|
-
// src/testing/cli-helpers.ts
|
|
10477
|
-
import { PassThrough, Readable, Writable } from "node:stream";
|
|
10478
|
-
function createTestEnvironment(options = {}) {
|
|
10479
|
-
const stdin = createMockReadable(options.stdin);
|
|
10480
|
-
const stdout = new PassThrough();
|
|
10481
|
-
const stderr = new PassThrough();
|
|
10482
|
-
let exitCode;
|
|
10483
|
-
return {
|
|
10484
|
-
stdin,
|
|
10485
|
-
stdout,
|
|
10486
|
-
stderr,
|
|
10487
|
-
isTTY: options.isTTY ?? false,
|
|
10488
|
-
argv: options.argv ?? ["node", "llmist"],
|
|
10489
|
-
env: { ...filterDefinedEnv(process.env), ...options.env },
|
|
10490
|
-
get exitCode() {
|
|
10491
|
-
return exitCode;
|
|
10492
|
-
},
|
|
10493
|
-
setExitCode: (code) => {
|
|
10494
|
-
exitCode = code;
|
|
10495
|
-
}
|
|
10496
|
-
};
|
|
10497
|
-
}
|
|
10498
|
-
function createMockReadable(input) {
|
|
10499
|
-
if (!input) {
|
|
10500
|
-
const stream3 = new Readable({ read() {
|
|
10501
|
-
} });
|
|
10502
|
-
stream3.push(null);
|
|
10503
|
-
return stream3;
|
|
10504
|
-
}
|
|
10505
|
-
const content = Array.isArray(input) ? `${input.join("\n")}
|
|
10506
|
-
` : input;
|
|
10507
|
-
const stream2 = new Readable({ read() {
|
|
10508
|
-
} });
|
|
10509
|
-
stream2.push(content);
|
|
10510
|
-
stream2.push(null);
|
|
10511
|
-
return stream2;
|
|
10512
|
-
}
|
|
10513
|
-
function createMockWritable() {
|
|
10514
|
-
const chunks = [];
|
|
10515
|
-
const stream2 = new Writable({
|
|
10516
|
-
write(chunk, _encoding, callback) {
|
|
10517
|
-
chunks.push(Buffer.from(chunk));
|
|
10518
|
-
callback();
|
|
10519
|
-
}
|
|
10520
|
-
});
|
|
10521
|
-
stream2.getData = () => Buffer.concat(chunks).toString("utf8");
|
|
10522
|
-
return stream2;
|
|
10523
|
-
}
|
|
10524
|
-
async function collectOutput(stream2, timeout = 5e3) {
|
|
10525
|
-
return new Promise((resolve, reject) => {
|
|
10526
|
-
const chunks = [];
|
|
10527
|
-
const timeoutId = setTimeout(() => {
|
|
10528
|
-
resolve(Buffer.concat(chunks).toString("utf8"));
|
|
10529
|
-
}, timeout);
|
|
10530
|
-
stream2.on("data", (chunk) => {
|
|
10531
|
-
chunks.push(Buffer.from(chunk));
|
|
10532
|
-
});
|
|
10533
|
-
stream2.on("end", () => {
|
|
10534
|
-
clearTimeout(timeoutId);
|
|
10535
|
-
resolve(Buffer.concat(chunks).toString("utf8"));
|
|
10536
|
-
});
|
|
10537
|
-
stream2.on("error", (err) => {
|
|
10538
|
-
clearTimeout(timeoutId);
|
|
10539
|
-
reject(err);
|
|
10540
|
-
});
|
|
10541
|
-
});
|
|
10542
|
-
}
|
|
10543
|
-
function getBufferedOutput(stream2) {
|
|
10544
|
-
const chunks = [];
|
|
10545
|
-
for (; ; ) {
|
|
10546
|
-
const chunk = stream2.read();
|
|
10547
|
-
if (chunk === null) break;
|
|
10548
|
-
chunks.push(chunk);
|
|
10549
|
-
}
|
|
10550
|
-
return Buffer.concat(chunks).toString("utf8");
|
|
10551
|
-
}
|
|
10552
|
-
function createMockPrompt(responses) {
|
|
10553
|
-
let index = 0;
|
|
10554
|
-
return async (_question) => {
|
|
10555
|
-
if (index >= responses.length) {
|
|
10556
|
-
throw new Error(`Mock prompt exhausted: no response for question ${index + 1}`);
|
|
10557
|
-
}
|
|
10558
|
-
return responses[index++];
|
|
10559
|
-
};
|
|
10560
|
-
}
|
|
10561
|
-
var MockPromptRecorder = class {
|
|
10562
|
-
responses;
|
|
10563
|
-
index = 0;
|
|
10564
|
-
questions = [];
|
|
10565
|
-
constructor(responses) {
|
|
10566
|
-
this.responses = responses;
|
|
10567
|
-
}
|
|
10568
|
-
/**
|
|
10569
|
-
* The prompt function to use in tests.
|
|
10570
|
-
*/
|
|
10571
|
-
prompt = async (question) => {
|
|
10572
|
-
this.questions.push(question);
|
|
10573
|
-
if (this.index >= this.responses.length) {
|
|
10574
|
-
throw new Error(`Mock prompt exhausted after ${this.index} questions`);
|
|
10575
|
-
}
|
|
10576
|
-
return this.responses[this.index++];
|
|
10577
|
-
};
|
|
10578
|
-
/**
|
|
10579
|
-
* Get all questions that were asked.
|
|
10580
|
-
*/
|
|
10581
|
-
getQuestions() {
|
|
10582
|
-
return [...this.questions];
|
|
10583
|
-
}
|
|
10584
|
-
/**
|
|
10585
|
-
* Get the number of questions asked.
|
|
10586
|
-
*/
|
|
10587
|
-
getQuestionCount() {
|
|
10588
|
-
return this.questions.length;
|
|
10589
|
-
}
|
|
10590
|
-
/**
|
|
10591
|
-
* Reset the recorder state.
|
|
10592
|
-
*/
|
|
10593
|
-
reset(newResponses) {
|
|
10594
|
-
this.index = 0;
|
|
10595
|
-
this.questions = [];
|
|
10596
|
-
if (newResponses) {
|
|
10597
|
-
this.responses = newResponses;
|
|
10598
|
-
}
|
|
10599
|
-
}
|
|
10600
|
-
};
|
|
10601
|
-
async function waitFor(condition, timeout = 5e3, interval = 50) {
|
|
10602
|
-
const startTime = Date.now();
|
|
10603
|
-
while (!condition()) {
|
|
10604
|
-
if (Date.now() - startTime > timeout) {
|
|
10605
|
-
throw new Error(`waitFor timed out after ${timeout}ms`);
|
|
10606
|
-
}
|
|
10607
|
-
await sleep3(interval);
|
|
10608
|
-
}
|
|
10609
|
-
}
|
|
10610
|
-
function sleep3(ms) {
|
|
10611
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
10612
|
-
}
|
|
10613
|
-
function filterDefinedEnv(env) {
|
|
10614
|
-
const result = {};
|
|
10615
|
-
for (const [key, value] of Object.entries(env)) {
|
|
10616
|
-
if (value !== void 0) {
|
|
10617
|
-
result[key] = value;
|
|
10618
|
-
}
|
|
10619
|
-
}
|
|
10620
|
-
return result;
|
|
10621
|
-
}
|
|
10622
|
-
|
|
10623
10965
|
export {
|
|
10624
10966
|
isTextPart,
|
|
10625
10967
|
isImagePart,
|
|
@@ -10652,20 +10994,23 @@ export {
|
|
|
10652
10994
|
resolveRulesTemplate,
|
|
10653
10995
|
resolveHintTemplate,
|
|
10654
10996
|
init_prompt_config,
|
|
10655
|
-
|
|
10656
|
-
|
|
10997
|
+
normalizeMessageContent,
|
|
10998
|
+
extractMessageText,
|
|
10657
10999
|
LLMMessageBuilder,
|
|
10658
11000
|
init_messages,
|
|
10659
|
-
|
|
10660
|
-
|
|
10661
|
-
|
|
11001
|
+
MediaStore,
|
|
11002
|
+
init_media_store,
|
|
11003
|
+
TaskCompletionSignal,
|
|
11004
|
+
HumanInputRequiredException,
|
|
11005
|
+
TimeoutException,
|
|
11006
|
+
AbortException,
|
|
10662
11007
|
init_exceptions,
|
|
10663
11008
|
createLogger,
|
|
10664
11009
|
defaultLogger,
|
|
10665
11010
|
init_logger,
|
|
10666
11011
|
schemaToJSONSchema,
|
|
10667
11012
|
init_schema_to_json,
|
|
10668
|
-
|
|
11013
|
+
AbstractGadget,
|
|
10669
11014
|
init_gadget,
|
|
10670
11015
|
createGadget,
|
|
10671
11016
|
init_create_gadget,
|
|
@@ -10681,15 +11026,15 @@ export {
|
|
|
10681
11026
|
init_strategies,
|
|
10682
11027
|
CompactionManager,
|
|
10683
11028
|
init_manager,
|
|
10684
|
-
GadgetOutputStore,
|
|
10685
|
-
init_gadget_output_store,
|
|
10686
11029
|
ConversationManager,
|
|
10687
11030
|
init_conversation_manager,
|
|
10688
11031
|
runWithHandlers,
|
|
10689
11032
|
collectEvents,
|
|
10690
11033
|
collectText,
|
|
10691
11034
|
init_event_handlers,
|
|
10692
|
-
|
|
11035
|
+
GadgetOutputStore,
|
|
11036
|
+
init_gadget_output_store,
|
|
11037
|
+
GadgetCallParser,
|
|
10693
11038
|
init_parser,
|
|
10694
11039
|
GadgetExecutor,
|
|
10695
11040
|
init_executor,
|
|
@@ -10721,6 +11066,22 @@ export {
|
|
|
10721
11066
|
init_builder,
|
|
10722
11067
|
validateAndApplyDefaults,
|
|
10723
11068
|
validateGadgetParams,
|
|
11069
|
+
createTestEnvironment,
|
|
11070
|
+
createMockReadable,
|
|
11071
|
+
createMockWritable,
|
|
11072
|
+
collectOutput,
|
|
11073
|
+
getBufferedOutput,
|
|
11074
|
+
createMockPrompt,
|
|
11075
|
+
MockPromptRecorder,
|
|
11076
|
+
waitFor,
|
|
11077
|
+
createConversation,
|
|
11078
|
+
createConversationWithGadgets,
|
|
11079
|
+
estimateTokens,
|
|
11080
|
+
createUserMessage,
|
|
11081
|
+
createAssistantMessage,
|
|
11082
|
+
createSystemMessage,
|
|
11083
|
+
createMinimalConversation,
|
|
11084
|
+
createLargeConversation,
|
|
10724
11085
|
testGadget,
|
|
10725
11086
|
testGadgetBatch,
|
|
10726
11087
|
MockManager,
|
|
@@ -10732,6 +11093,8 @@ export {
|
|
|
10732
11093
|
MockBuilder,
|
|
10733
11094
|
mockLLM,
|
|
10734
11095
|
createMockClient,
|
|
11096
|
+
MockConversationManager,
|
|
11097
|
+
createMockConversationManager,
|
|
10735
11098
|
createMockGadget,
|
|
10736
11099
|
MockGadgetBuilder,
|
|
10737
11100
|
mockGadget,
|
|
@@ -10741,24 +11104,6 @@ export {
|
|
|
10741
11104
|
collectStreamText,
|
|
10742
11105
|
getStreamFinalChunk,
|
|
10743
11106
|
createEmptyStream,
|
|
10744
|
-
createErrorStream
|
|
10745
|
-
createConversation,
|
|
10746
|
-
createConversationWithGadgets,
|
|
10747
|
-
estimateTokens,
|
|
10748
|
-
createUserMessage,
|
|
10749
|
-
createAssistantMessage,
|
|
10750
|
-
createSystemMessage,
|
|
10751
|
-
createMinimalConversation,
|
|
10752
|
-
createLargeConversation,
|
|
10753
|
-
MockConversationManager,
|
|
10754
|
-
createMockConversationManager,
|
|
10755
|
-
createTestEnvironment,
|
|
10756
|
-
createMockReadable,
|
|
10757
|
-
createMockWritable,
|
|
10758
|
-
collectOutput,
|
|
10759
|
-
getBufferedOutput,
|
|
10760
|
-
createMockPrompt,
|
|
10761
|
-
MockPromptRecorder,
|
|
10762
|
-
waitFor
|
|
11107
|
+
createErrorStream
|
|
10763
11108
|
};
|
|
10764
|
-
//# sourceMappingURL=chunk-
|
|
11109
|
+
//# sourceMappingURL=chunk-67MMSOAT.js.map
|