llmist 2.5.0 → 2.6.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/dist/{chunk-YHS2DYXP.js → chunk-364PEMVT.js} +947 -649
- package/dist/chunk-364PEMVT.js.map +1 -0
- package/dist/{chunk-IHSZUAYN.js → chunk-4IHLIYW5.js} +134 -18
- package/dist/chunk-4IHLIYW5.js.map +1 -0
- package/dist/cli.cjs +3345 -2981
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +2758 -2708
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +584 -163
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +659 -454
- package/dist/index.d.ts +659 -454
- package/dist/index.js +16 -2
- package/dist/{mock-stream-ga4KIiwX.d.cts → mock-stream-Jgg5u6Uf.d.cts} +262 -7
- package/dist/{mock-stream-ga4KIiwX.d.ts → mock-stream-Jgg5u6Uf.d.ts} +262 -7
- package/dist/testing/index.cjs +932 -624
- package/dist/testing/index.cjs.map +1 -1
- package/dist/testing/index.d.cts +343 -343
- package/dist/testing/index.d.ts +343 -343
- 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
package/dist/testing/index.cjs
CHANGED
|
@@ -134,6 +134,12 @@ function isAudioPart(part) {
|
|
|
134
134
|
function text(content) {
|
|
135
135
|
return { type: "text", text: content };
|
|
136
136
|
}
|
|
137
|
+
function imageFromBase64(data, mediaType) {
|
|
138
|
+
return {
|
|
139
|
+
type: "image",
|
|
140
|
+
source: { type: "base64", mediaType, data }
|
|
141
|
+
};
|
|
142
|
+
}
|
|
137
143
|
function imageFromUrl(url) {
|
|
138
144
|
return {
|
|
139
145
|
type: "image",
|
|
@@ -194,6 +200,12 @@ function toBase64(data) {
|
|
|
194
200
|
}
|
|
195
201
|
return Buffer.from(data).toString("base64");
|
|
196
202
|
}
|
|
203
|
+
function audioFromBase64(data, mediaType) {
|
|
204
|
+
return {
|
|
205
|
+
type: "audio",
|
|
206
|
+
source: { type: "base64", mediaType, data }
|
|
207
|
+
};
|
|
208
|
+
}
|
|
197
209
|
function audioFromBuffer(buffer, mediaType) {
|
|
198
210
|
const detectedType = mediaType ?? detectAudioMimeType(buffer);
|
|
199
211
|
if (!detectedType) {
|
|
@@ -240,7 +252,9 @@ var init_input_content = __esm({
|
|
|
240
252
|
// WAV (RIFF)
|
|
241
253
|
{ bytes: [82, 73, 70, 70], mimeType: "audio/wav" },
|
|
242
254
|
// WebM
|
|
243
|
-
{ bytes: [26, 69, 223, 163], mimeType: "audio/webm" }
|
|
255
|
+
{ bytes: [26, 69, 223, 163], mimeType: "audio/webm" },
|
|
256
|
+
// FLAC (fLaC)
|
|
257
|
+
{ bytes: [102, 76, 97, 67], mimeType: "audio/flac" }
|
|
244
258
|
];
|
|
245
259
|
}
|
|
246
260
|
});
|
|
@@ -653,7 +667,7 @@ Produces: { "items": ["first", "second"] }`);
|
|
|
653
667
|
this.messages.push({ role: "user", content: parts });
|
|
654
668
|
return this;
|
|
655
669
|
}
|
|
656
|
-
addGadgetCall(gadget, parameters, result) {
|
|
670
|
+
addGadgetCall(gadget, parameters, result, media, mediaIds) {
|
|
657
671
|
const paramStr = this.formatBlockParameters(parameters, "");
|
|
658
672
|
this.messages.push({
|
|
659
673
|
role: "assistant",
|
|
@@ -661,10 +675,25 @@ Produces: { "items": ["first", "second"] }`);
|
|
|
661
675
|
${paramStr}
|
|
662
676
|
${this.endPrefix}`
|
|
663
677
|
});
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
678
|
+
if (media && media.length > 0 && mediaIds && mediaIds.length > 0) {
|
|
679
|
+
const idRefs = media.map((m, i) => `[Media: ${mediaIds[i]} (${m.kind})]`).join("\n");
|
|
680
|
+
const textWithIds = `Result: ${result}
|
|
681
|
+
${idRefs}`;
|
|
682
|
+
const parts = [text(textWithIds)];
|
|
683
|
+
for (const item of media) {
|
|
684
|
+
if (item.kind === "image") {
|
|
685
|
+
parts.push(imageFromBase64(item.data, item.mimeType));
|
|
686
|
+
} else if (item.kind === "audio") {
|
|
687
|
+
parts.push(audioFromBase64(item.data, item.mimeType));
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
this.messages.push({ role: "user", content: parts });
|
|
691
|
+
} else {
|
|
692
|
+
this.messages.push({
|
|
693
|
+
role: "user",
|
|
694
|
+
content: `Result: ${result}`
|
|
695
|
+
});
|
|
696
|
+
}
|
|
668
697
|
return this;
|
|
669
698
|
}
|
|
670
699
|
/**
|
|
@@ -998,6 +1027,210 @@ var init_registry = __esm({
|
|
|
998
1027
|
}
|
|
999
1028
|
});
|
|
1000
1029
|
|
|
1030
|
+
// src/gadgets/media-store.ts
|
|
1031
|
+
function getLlmistTmpDir() {
|
|
1032
|
+
return (0, import_node_path2.join)((0, import_node_os.homedir)(), ".llmist", "tmp");
|
|
1033
|
+
}
|
|
1034
|
+
var import_node_crypto, import_promises, import_node_os, import_node_path2, MIME_TO_EXTENSION, MediaStore;
|
|
1035
|
+
var init_media_store = __esm({
|
|
1036
|
+
"src/gadgets/media-store.ts"() {
|
|
1037
|
+
"use strict";
|
|
1038
|
+
import_node_crypto = require("crypto");
|
|
1039
|
+
import_promises = require("fs/promises");
|
|
1040
|
+
import_node_os = require("os");
|
|
1041
|
+
import_node_path2 = require("path");
|
|
1042
|
+
MIME_TO_EXTENSION = {
|
|
1043
|
+
// Images
|
|
1044
|
+
"image/png": ".png",
|
|
1045
|
+
"image/jpeg": ".jpg",
|
|
1046
|
+
"image/gif": ".gif",
|
|
1047
|
+
"image/webp": ".webp",
|
|
1048
|
+
"image/svg+xml": ".svg",
|
|
1049
|
+
"image/bmp": ".bmp",
|
|
1050
|
+
"image/tiff": ".tiff",
|
|
1051
|
+
// Audio
|
|
1052
|
+
"audio/mp3": ".mp3",
|
|
1053
|
+
"audio/mpeg": ".mp3",
|
|
1054
|
+
"audio/wav": ".wav",
|
|
1055
|
+
"audio/webm": ".webm",
|
|
1056
|
+
"audio/ogg": ".ogg",
|
|
1057
|
+
"audio/flac": ".flac",
|
|
1058
|
+
"audio/aac": ".aac",
|
|
1059
|
+
// Video
|
|
1060
|
+
"video/mp4": ".mp4",
|
|
1061
|
+
"video/webm": ".webm",
|
|
1062
|
+
"video/ogg": ".ogv",
|
|
1063
|
+
"video/quicktime": ".mov",
|
|
1064
|
+
"video/x-msvideo": ".avi",
|
|
1065
|
+
// Documents
|
|
1066
|
+
"application/pdf": ".pdf",
|
|
1067
|
+
"application/json": ".json",
|
|
1068
|
+
"text/plain": ".txt",
|
|
1069
|
+
"text/html": ".html",
|
|
1070
|
+
"text/css": ".css",
|
|
1071
|
+
"text/javascript": ".js"
|
|
1072
|
+
};
|
|
1073
|
+
MediaStore = class {
|
|
1074
|
+
items = /* @__PURE__ */ new Map();
|
|
1075
|
+
outputDir;
|
|
1076
|
+
counter = 0;
|
|
1077
|
+
initialized = false;
|
|
1078
|
+
/**
|
|
1079
|
+
* Create a new MediaStore.
|
|
1080
|
+
*
|
|
1081
|
+
* @param sessionId - Optional session ID for the output directory.
|
|
1082
|
+
* If not provided, a random ID is generated.
|
|
1083
|
+
*/
|
|
1084
|
+
constructor(sessionId) {
|
|
1085
|
+
const id = sessionId ?? (0, import_node_crypto.randomBytes)(8).toString("hex");
|
|
1086
|
+
this.outputDir = (0, import_node_path2.join)(getLlmistTmpDir(), `media-${id}`);
|
|
1087
|
+
}
|
|
1088
|
+
/**
|
|
1089
|
+
* Get the output directory path.
|
|
1090
|
+
*/
|
|
1091
|
+
getOutputDir() {
|
|
1092
|
+
return this.outputDir;
|
|
1093
|
+
}
|
|
1094
|
+
/**
|
|
1095
|
+
* Ensure the output directory exists.
|
|
1096
|
+
* @throws Error if directory creation fails
|
|
1097
|
+
*/
|
|
1098
|
+
async ensureDir() {
|
|
1099
|
+
if (this.initialized) return;
|
|
1100
|
+
try {
|
|
1101
|
+
await (0, import_promises.mkdir)(this.outputDir, { recursive: true });
|
|
1102
|
+
this.initialized = true;
|
|
1103
|
+
} catch (error) {
|
|
1104
|
+
throw new Error(
|
|
1105
|
+
`MediaStore: Failed to create directory ${this.outputDir}: ${error instanceof Error ? error.message : String(error)}`
|
|
1106
|
+
);
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
/**
|
|
1110
|
+
* Generate a unique media ID.
|
|
1111
|
+
* Format: "media_" + 6 random alphanumeric characters
|
|
1112
|
+
*/
|
|
1113
|
+
generateId() {
|
|
1114
|
+
const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
1115
|
+
let id = "media_";
|
|
1116
|
+
const bytes = (0, import_node_crypto.randomBytes)(6);
|
|
1117
|
+
for (let i = 0; i < 6; i++) {
|
|
1118
|
+
id += chars[bytes[i] % chars.length];
|
|
1119
|
+
}
|
|
1120
|
+
return id;
|
|
1121
|
+
}
|
|
1122
|
+
/**
|
|
1123
|
+
* Get file extension from MIME type.
|
|
1124
|
+
*/
|
|
1125
|
+
getExtension(mimeType) {
|
|
1126
|
+
return MIME_TO_EXTENSION[mimeType] ?? ".bin";
|
|
1127
|
+
}
|
|
1128
|
+
/**
|
|
1129
|
+
* Store media and return stored metadata with ID.
|
|
1130
|
+
*
|
|
1131
|
+
* @param media - The media output from a gadget
|
|
1132
|
+
* @param gadgetName - Name of the gadget that created this media
|
|
1133
|
+
* @returns Stored media information including generated ID
|
|
1134
|
+
* @throws Error if file write fails
|
|
1135
|
+
*/
|
|
1136
|
+
async store(media, gadgetName) {
|
|
1137
|
+
await this.ensureDir();
|
|
1138
|
+
const id = this.generateId();
|
|
1139
|
+
const ext = this.getExtension(media.mimeType);
|
|
1140
|
+
const filename = media.fileName ?? `${gadgetName}_${String(++this.counter).padStart(3, "0")}${ext}`;
|
|
1141
|
+
const filePath = (0, import_node_path2.join)(this.outputDir, filename);
|
|
1142
|
+
const buffer = Buffer.from(media.data, "base64");
|
|
1143
|
+
try {
|
|
1144
|
+
await (0, import_promises.writeFile)(filePath, buffer);
|
|
1145
|
+
} catch (error) {
|
|
1146
|
+
throw new Error(
|
|
1147
|
+
`MediaStore: Failed to write media file ${filePath}: ${error instanceof Error ? error.message : String(error)}`
|
|
1148
|
+
);
|
|
1149
|
+
}
|
|
1150
|
+
const stored = {
|
|
1151
|
+
id,
|
|
1152
|
+
kind: media.kind,
|
|
1153
|
+
path: filePath,
|
|
1154
|
+
mimeType: media.mimeType,
|
|
1155
|
+
sizeBytes: buffer.length,
|
|
1156
|
+
description: media.description,
|
|
1157
|
+
metadata: media.metadata,
|
|
1158
|
+
gadgetName,
|
|
1159
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
1160
|
+
};
|
|
1161
|
+
this.items.set(id, stored);
|
|
1162
|
+
return stored;
|
|
1163
|
+
}
|
|
1164
|
+
/**
|
|
1165
|
+
* Get stored media by ID.
|
|
1166
|
+
*
|
|
1167
|
+
* @param id - The media ID (e.g., "media_a1b2c3")
|
|
1168
|
+
* @returns The stored media or undefined if not found
|
|
1169
|
+
*/
|
|
1170
|
+
get(id) {
|
|
1171
|
+
return this.items.get(id);
|
|
1172
|
+
}
|
|
1173
|
+
/**
|
|
1174
|
+
* Get the actual file path for a media ID.
|
|
1175
|
+
* Convenience method for gadgets that need the raw path.
|
|
1176
|
+
*
|
|
1177
|
+
* @param id - The media ID
|
|
1178
|
+
* @returns The file path or undefined if not found
|
|
1179
|
+
*/
|
|
1180
|
+
getPath(id) {
|
|
1181
|
+
return this.items.get(id)?.path;
|
|
1182
|
+
}
|
|
1183
|
+
/**
|
|
1184
|
+
* List all stored media, optionally filtered by kind.
|
|
1185
|
+
*
|
|
1186
|
+
* @param kind - Optional media kind to filter by
|
|
1187
|
+
* @returns Array of stored media items
|
|
1188
|
+
*/
|
|
1189
|
+
list(kind) {
|
|
1190
|
+
const all = Array.from(this.items.values());
|
|
1191
|
+
if (kind) {
|
|
1192
|
+
return all.filter((item) => item.kind === kind);
|
|
1193
|
+
}
|
|
1194
|
+
return all;
|
|
1195
|
+
}
|
|
1196
|
+
/**
|
|
1197
|
+
* Get the count of stored media items.
|
|
1198
|
+
*/
|
|
1199
|
+
get size() {
|
|
1200
|
+
return this.items.size;
|
|
1201
|
+
}
|
|
1202
|
+
/**
|
|
1203
|
+
* Check if a media ID exists.
|
|
1204
|
+
*/
|
|
1205
|
+
has(id) {
|
|
1206
|
+
return this.items.has(id);
|
|
1207
|
+
}
|
|
1208
|
+
/**
|
|
1209
|
+
* Clear in-memory store without deleting files.
|
|
1210
|
+
* Resets the counter but leaves files on disk.
|
|
1211
|
+
*/
|
|
1212
|
+
clear() {
|
|
1213
|
+
this.items.clear();
|
|
1214
|
+
this.counter = 0;
|
|
1215
|
+
}
|
|
1216
|
+
/**
|
|
1217
|
+
* Delete all stored files and clear memory.
|
|
1218
|
+
* Removes the entire session directory.
|
|
1219
|
+
*/
|
|
1220
|
+
async cleanup() {
|
|
1221
|
+
if (this.initialized) {
|
|
1222
|
+
try {
|
|
1223
|
+
await (0, import_promises.rm)(this.outputDir, { recursive: true, force: true });
|
|
1224
|
+
} catch {
|
|
1225
|
+
}
|
|
1226
|
+
this.initialized = false;
|
|
1227
|
+
}
|
|
1228
|
+
this.clear();
|
|
1229
|
+
}
|
|
1230
|
+
};
|
|
1231
|
+
}
|
|
1232
|
+
});
|
|
1233
|
+
|
|
1001
1234
|
// src/gadgets/exceptions.ts
|
|
1002
1235
|
var BreakLoopException, HumanInputException, TimeoutException, AbortError;
|
|
1003
1236
|
var init_exceptions = __esm({
|
|
@@ -1433,7 +1666,9 @@ var init_gadget = __esm({
|
|
|
1433
1666
|
parts.push(`# ${example.comment}`);
|
|
1434
1667
|
}
|
|
1435
1668
|
parts.push(`${effectiveStartPrefix}${gadgetName}`);
|
|
1436
|
-
parts.push(
|
|
1669
|
+
parts.push(
|
|
1670
|
+
formatParamsAsBlock(example.params, "", effectiveArgPrefix)
|
|
1671
|
+
);
|
|
1437
1672
|
parts.push(effectiveEndPrefix);
|
|
1438
1673
|
if (example.output !== void 0) {
|
|
1439
1674
|
parts.push("");
|
|
@@ -1635,6 +1870,18 @@ var init_output_viewer = __esm({
|
|
|
1635
1870
|
}
|
|
1636
1871
|
});
|
|
1637
1872
|
|
|
1873
|
+
// src/agent/agent-internal-key.ts
|
|
1874
|
+
function isValidAgentKey(key) {
|
|
1875
|
+
return key === AGENT_INTERNAL_KEY;
|
|
1876
|
+
}
|
|
1877
|
+
var AGENT_INTERNAL_KEY;
|
|
1878
|
+
var init_agent_internal_key = __esm({
|
|
1879
|
+
"src/agent/agent-internal-key.ts"() {
|
|
1880
|
+
"use strict";
|
|
1881
|
+
AGENT_INTERNAL_KEY = Symbol("AGENT_INTERNAL_KEY");
|
|
1882
|
+
}
|
|
1883
|
+
});
|
|
1884
|
+
|
|
1638
1885
|
// src/agent/compaction/config.ts
|
|
1639
1886
|
function resolveCompactionConfig(config = {}) {
|
|
1640
1887
|
const trigger = config.triggerThresholdPercent ?? DEFAULT_COMPACTION_CONFIG.triggerThresholdPercent;
|
|
@@ -1882,9 +2129,9 @@ var init_hybrid = __esm({
|
|
|
1882
2129
|
var init_strategies = __esm({
|
|
1883
2130
|
"src/agent/compaction/strategies/index.ts"() {
|
|
1884
2131
|
"use strict";
|
|
2132
|
+
init_hybrid();
|
|
1885
2133
|
init_sliding_window();
|
|
1886
2134
|
init_summarization();
|
|
1887
|
-
init_hybrid();
|
|
1888
2135
|
}
|
|
1889
2136
|
});
|
|
1890
2137
|
|
|
@@ -2046,98 +2293,6 @@ var init_manager = __esm({
|
|
|
2046
2293
|
}
|
|
2047
2294
|
});
|
|
2048
2295
|
|
|
2049
|
-
// src/agent/gadget-output-store.ts
|
|
2050
|
-
var import_node_crypto, GadgetOutputStore;
|
|
2051
|
-
var init_gadget_output_store = __esm({
|
|
2052
|
-
"src/agent/gadget-output-store.ts"() {
|
|
2053
|
-
"use strict";
|
|
2054
|
-
import_node_crypto = require("crypto");
|
|
2055
|
-
GadgetOutputStore = class {
|
|
2056
|
-
outputs = /* @__PURE__ */ new Map();
|
|
2057
|
-
/**
|
|
2058
|
-
* Store a gadget output and return its ID.
|
|
2059
|
-
*
|
|
2060
|
-
* @param gadgetName - Name of the gadget that produced the output
|
|
2061
|
-
* @param content - Full output content to store
|
|
2062
|
-
* @returns Generated ID for retrieving the output later
|
|
2063
|
-
*/
|
|
2064
|
-
store(gadgetName, content) {
|
|
2065
|
-
const id = this.generateId(gadgetName);
|
|
2066
|
-
const encoder = new TextEncoder();
|
|
2067
|
-
const stored = {
|
|
2068
|
-
id,
|
|
2069
|
-
gadgetName,
|
|
2070
|
-
content,
|
|
2071
|
-
byteSize: encoder.encode(content).length,
|
|
2072
|
-
lineCount: content.split("\n").length,
|
|
2073
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
2074
|
-
};
|
|
2075
|
-
this.outputs.set(id, stored);
|
|
2076
|
-
return id;
|
|
2077
|
-
}
|
|
2078
|
-
/**
|
|
2079
|
-
* Retrieve a stored output by ID.
|
|
2080
|
-
*
|
|
2081
|
-
* @param id - The output ID (e.g., "Search_d34db33f")
|
|
2082
|
-
* @returns The stored output or undefined if not found
|
|
2083
|
-
*/
|
|
2084
|
-
get(id) {
|
|
2085
|
-
return this.outputs.get(id);
|
|
2086
|
-
}
|
|
2087
|
-
/**
|
|
2088
|
-
* Check if an output exists.
|
|
2089
|
-
*
|
|
2090
|
-
* @param id - The output ID to check
|
|
2091
|
-
* @returns True if the output exists
|
|
2092
|
-
*/
|
|
2093
|
-
has(id) {
|
|
2094
|
-
return this.outputs.has(id);
|
|
2095
|
-
}
|
|
2096
|
-
/**
|
|
2097
|
-
* Get all stored output IDs.
|
|
2098
|
-
*
|
|
2099
|
-
* @returns Array of output IDs
|
|
2100
|
-
*/
|
|
2101
|
-
getIds() {
|
|
2102
|
-
return Array.from(this.outputs.keys());
|
|
2103
|
-
}
|
|
2104
|
-
/**
|
|
2105
|
-
* Get the number of stored outputs.
|
|
2106
|
-
*/
|
|
2107
|
-
get size() {
|
|
2108
|
-
return this.outputs.size;
|
|
2109
|
-
}
|
|
2110
|
-
/**
|
|
2111
|
-
* Clear all stored outputs.
|
|
2112
|
-
* Called when the agent run completes.
|
|
2113
|
-
*/
|
|
2114
|
-
clear() {
|
|
2115
|
-
this.outputs.clear();
|
|
2116
|
-
}
|
|
2117
|
-
/**
|
|
2118
|
-
* Generate a unique ID for a stored output.
|
|
2119
|
-
* Format: {GadgetName}_{8 hex chars}
|
|
2120
|
-
*/
|
|
2121
|
-
generateId(gadgetName) {
|
|
2122
|
-
const hex = (0, import_node_crypto.randomBytes)(4).toString("hex");
|
|
2123
|
-
return `${gadgetName}_${hex}`;
|
|
2124
|
-
}
|
|
2125
|
-
};
|
|
2126
|
-
}
|
|
2127
|
-
});
|
|
2128
|
-
|
|
2129
|
-
// src/agent/agent-internal-key.ts
|
|
2130
|
-
function isValidAgentKey(key) {
|
|
2131
|
-
return key === AGENT_INTERNAL_KEY;
|
|
2132
|
-
}
|
|
2133
|
-
var AGENT_INTERNAL_KEY;
|
|
2134
|
-
var init_agent_internal_key = __esm({
|
|
2135
|
-
"src/agent/agent-internal-key.ts"() {
|
|
2136
|
-
"use strict";
|
|
2137
|
-
AGENT_INTERNAL_KEY = Symbol("AGENT_INTERNAL_KEY");
|
|
2138
|
-
}
|
|
2139
|
-
});
|
|
2140
|
-
|
|
2141
2296
|
// src/agent/conversation-manager.ts
|
|
2142
2297
|
var ConversationManager;
|
|
2143
2298
|
var init_conversation_manager = __esm({
|
|
@@ -2168,8 +2323,8 @@ var init_conversation_manager = __esm({
|
|
|
2168
2323
|
addAssistantMessage(content) {
|
|
2169
2324
|
this.historyBuilder.addAssistant(content);
|
|
2170
2325
|
}
|
|
2171
|
-
addGadgetCall(gadgetName, parameters, result) {
|
|
2172
|
-
this.historyBuilder.addGadgetCall(gadgetName, parameters, result);
|
|
2326
|
+
addGadgetCall(gadgetName, parameters, result, media, mediaIds) {
|
|
2327
|
+
this.historyBuilder.addGadgetCall(gadgetName, parameters, result, media, mediaIds);
|
|
2173
2328
|
}
|
|
2174
2329
|
getMessages() {
|
|
2175
2330
|
return [...this.baseMessages, ...this.initialMessages, ...this.historyBuilder.build()];
|
|
@@ -2253,62 +2408,142 @@ var init_event_handlers = __esm({
|
|
|
2253
2408
|
}
|
|
2254
2409
|
});
|
|
2255
2410
|
|
|
2256
|
-
// src/agent/
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
}
|
|
2311
|
-
|
|
2411
|
+
// src/agent/gadget-output-store.ts
|
|
2412
|
+
var import_node_crypto2, GadgetOutputStore;
|
|
2413
|
+
var init_gadget_output_store = __esm({
|
|
2414
|
+
"src/agent/gadget-output-store.ts"() {
|
|
2415
|
+
"use strict";
|
|
2416
|
+
import_node_crypto2 = require("crypto");
|
|
2417
|
+
GadgetOutputStore = class {
|
|
2418
|
+
outputs = /* @__PURE__ */ new Map();
|
|
2419
|
+
/**
|
|
2420
|
+
* Store a gadget output and return its ID.
|
|
2421
|
+
*
|
|
2422
|
+
* @param gadgetName - Name of the gadget that produced the output
|
|
2423
|
+
* @param content - Full output content to store
|
|
2424
|
+
* @returns Generated ID for retrieving the output later
|
|
2425
|
+
*/
|
|
2426
|
+
store(gadgetName, content) {
|
|
2427
|
+
const id = this.generateId(gadgetName);
|
|
2428
|
+
const encoder = new TextEncoder();
|
|
2429
|
+
const stored = {
|
|
2430
|
+
id,
|
|
2431
|
+
gadgetName,
|
|
2432
|
+
content,
|
|
2433
|
+
byteSize: encoder.encode(content).length,
|
|
2434
|
+
lineCount: content.split("\n").length,
|
|
2435
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
2436
|
+
};
|
|
2437
|
+
this.outputs.set(id, stored);
|
|
2438
|
+
return id;
|
|
2439
|
+
}
|
|
2440
|
+
/**
|
|
2441
|
+
* Retrieve a stored output by ID.
|
|
2442
|
+
*
|
|
2443
|
+
* @param id - The output ID (e.g., "Search_d34db33f")
|
|
2444
|
+
* @returns The stored output or undefined if not found
|
|
2445
|
+
*/
|
|
2446
|
+
get(id) {
|
|
2447
|
+
return this.outputs.get(id);
|
|
2448
|
+
}
|
|
2449
|
+
/**
|
|
2450
|
+
* Check if an output exists.
|
|
2451
|
+
*
|
|
2452
|
+
* @param id - The output ID to check
|
|
2453
|
+
* @returns True if the output exists
|
|
2454
|
+
*/
|
|
2455
|
+
has(id) {
|
|
2456
|
+
return this.outputs.has(id);
|
|
2457
|
+
}
|
|
2458
|
+
/**
|
|
2459
|
+
* Get all stored output IDs.
|
|
2460
|
+
*
|
|
2461
|
+
* @returns Array of output IDs
|
|
2462
|
+
*/
|
|
2463
|
+
getIds() {
|
|
2464
|
+
return Array.from(this.outputs.keys());
|
|
2465
|
+
}
|
|
2466
|
+
/**
|
|
2467
|
+
* Get the number of stored outputs.
|
|
2468
|
+
*/
|
|
2469
|
+
get size() {
|
|
2470
|
+
return this.outputs.size;
|
|
2471
|
+
}
|
|
2472
|
+
/**
|
|
2473
|
+
* Clear all stored outputs.
|
|
2474
|
+
* Called when the agent run completes.
|
|
2475
|
+
*/
|
|
2476
|
+
clear() {
|
|
2477
|
+
this.outputs.clear();
|
|
2478
|
+
}
|
|
2479
|
+
/**
|
|
2480
|
+
* Generate a unique ID for a stored output.
|
|
2481
|
+
* Format: {GadgetName}_{8 hex chars}
|
|
2482
|
+
*/
|
|
2483
|
+
generateId(gadgetName) {
|
|
2484
|
+
const hex = (0, import_node_crypto2.randomBytes)(4).toString("hex");
|
|
2485
|
+
return `${gadgetName}_${hex}`;
|
|
2486
|
+
}
|
|
2487
|
+
};
|
|
2488
|
+
}
|
|
2489
|
+
});
|
|
2490
|
+
|
|
2491
|
+
// src/agent/hook-validators.ts
|
|
2492
|
+
function validateBeforeLLMCallAction(action) {
|
|
2493
|
+
if (!action || typeof action !== "object" || !("action" in action)) {
|
|
2494
|
+
throw new HookValidationError(
|
|
2495
|
+
"beforeLLMCall",
|
|
2496
|
+
"Must return an action object with an 'action' field"
|
|
2497
|
+
);
|
|
2498
|
+
}
|
|
2499
|
+
const actionType = action.action;
|
|
2500
|
+
if (actionType !== "proceed" && actionType !== "skip") {
|
|
2501
|
+
throw new HookValidationError(
|
|
2502
|
+
"beforeLLMCall",
|
|
2503
|
+
`Invalid action type: ${actionType}. Must be 'proceed' or 'skip'`
|
|
2504
|
+
);
|
|
2505
|
+
}
|
|
2506
|
+
if (actionType === "skip" && !action.syntheticResponse) {
|
|
2507
|
+
throw new HookValidationError(
|
|
2508
|
+
"beforeLLMCall",
|
|
2509
|
+
"When action is 'skip', syntheticResponse is required"
|
|
2510
|
+
);
|
|
2511
|
+
}
|
|
2512
|
+
}
|
|
2513
|
+
function validateAfterLLMCallAction(action) {
|
|
2514
|
+
if (!action || typeof action !== "object" || !("action" in action)) {
|
|
2515
|
+
throw new HookValidationError(
|
|
2516
|
+
"afterLLMCall",
|
|
2517
|
+
"Must return an action object with an 'action' field"
|
|
2518
|
+
);
|
|
2519
|
+
}
|
|
2520
|
+
const actionType = action.action;
|
|
2521
|
+
const validActions = ["continue", "append_messages", "modify_and_continue", "append_and_modify"];
|
|
2522
|
+
if (!validActions.includes(actionType)) {
|
|
2523
|
+
throw new HookValidationError(
|
|
2524
|
+
"afterLLMCall",
|
|
2525
|
+
`Invalid action type: ${actionType}. Must be one of: ${validActions.join(", ")}`
|
|
2526
|
+
);
|
|
2527
|
+
}
|
|
2528
|
+
if (actionType === "append_messages" || actionType === "append_and_modify") {
|
|
2529
|
+
if (!("messages" in action) || !action.messages || !Array.isArray(action.messages)) {
|
|
2530
|
+
throw new HookValidationError(
|
|
2531
|
+
"afterLLMCall",
|
|
2532
|
+
`When action is '${actionType}', messages array is required`
|
|
2533
|
+
);
|
|
2534
|
+
}
|
|
2535
|
+
if (action.messages.length === 0) {
|
|
2536
|
+
throw new HookValidationError(
|
|
2537
|
+
"afterLLMCall",
|
|
2538
|
+
`When action is '${actionType}', messages array must not be empty`
|
|
2539
|
+
);
|
|
2540
|
+
}
|
|
2541
|
+
for (let i = 0; i < action.messages.length; i++) {
|
|
2542
|
+
const msg = action.messages[i];
|
|
2543
|
+
if (!msg || typeof msg !== "object") {
|
|
2544
|
+
throw new HookValidationError("afterLLMCall", `Message at index ${i} must be an object`);
|
|
2545
|
+
}
|
|
2546
|
+
if (!msg.role || !msg.content) {
|
|
2312
2547
|
throw new HookValidationError(
|
|
2313
2548
|
"afterLLMCall",
|
|
2314
2549
|
`Message at index ${i} must have 'role' and 'content' fields`
|
|
@@ -2563,8 +2798,7 @@ var init_schema_introspector = __esm({
|
|
|
2563
2798
|
const values = def?.values;
|
|
2564
2799
|
const value = values?.[0] ?? def?.value;
|
|
2565
2800
|
if (typeof value === "string") return "string";
|
|
2566
|
-
if (typeof value === "number" || typeof value === "bigint")
|
|
2567
|
-
return "number";
|
|
2801
|
+
if (typeof value === "number" || typeof value === "bigint") return "number";
|
|
2568
2802
|
if (typeof value === "boolean") return "boolean";
|
|
2569
2803
|
return "unknown";
|
|
2570
2804
|
}
|
|
@@ -2836,7 +3070,13 @@ var init_cost_reporting_client = __esm({
|
|
|
2836
3070
|
cacheCreationInputTokens = chunk.usage.cacheCreationInputTokens ?? 0;
|
|
2837
3071
|
}
|
|
2838
3072
|
}
|
|
2839
|
-
this.reportCostFromUsage(
|
|
3073
|
+
this.reportCostFromUsage(
|
|
3074
|
+
model,
|
|
3075
|
+
inputTokens,
|
|
3076
|
+
outputTokens,
|
|
3077
|
+
cachedInputTokens,
|
|
3078
|
+
cacheCreationInputTokens
|
|
3079
|
+
);
|
|
2840
3080
|
return result;
|
|
2841
3081
|
}
|
|
2842
3082
|
/**
|
|
@@ -2876,7 +3116,13 @@ var init_cost_reporting_client = __esm({
|
|
|
2876
3116
|
}
|
|
2877
3117
|
}
|
|
2878
3118
|
} finally {
|
|
2879
|
-
this.reportCostFromUsage(
|
|
3119
|
+
this.reportCostFromUsage(
|
|
3120
|
+
model,
|
|
3121
|
+
inputTokens,
|
|
3122
|
+
outputTokens,
|
|
3123
|
+
cachedInputTokens,
|
|
3124
|
+
cacheCreationInputTokens
|
|
3125
|
+
);
|
|
2880
3126
|
}
|
|
2881
3127
|
}
|
|
2882
3128
|
/**
|
|
@@ -2914,7 +3160,13 @@ var init_cost_reporting_client = __esm({
|
|
|
2914
3160
|
}
|
|
2915
3161
|
} finally {
|
|
2916
3162
|
if (inputTokens > 0 || outputTokens > 0) {
|
|
2917
|
-
reportCostFromUsage(
|
|
3163
|
+
reportCostFromUsage(
|
|
3164
|
+
model,
|
|
3165
|
+
inputTokens,
|
|
3166
|
+
outputTokens,
|
|
3167
|
+
cachedInputTokens,
|
|
3168
|
+
cacheCreationInputTokens
|
|
3169
|
+
);
|
|
2918
3170
|
}
|
|
2919
3171
|
}
|
|
2920
3172
|
}
|
|
@@ -3126,7 +3378,11 @@ var init_parser = __esm({
|
|
|
3126
3378
|
const metadataEndIndex = this.buffer.indexOf("\n", metadataStartIndex);
|
|
3127
3379
|
if (metadataEndIndex === -1) break;
|
|
3128
3380
|
const gadgetName = this.buffer.substring(metadataStartIndex, metadataEndIndex).trim();
|
|
3129
|
-
const {
|
|
3381
|
+
const {
|
|
3382
|
+
actualName: actualGadgetName,
|
|
3383
|
+
invocationId,
|
|
3384
|
+
dependencies
|
|
3385
|
+
} = this.parseGadgetName(gadgetName);
|
|
3130
3386
|
const contentStartIndex = metadataEndIndex + 1;
|
|
3131
3387
|
let partEndIndex;
|
|
3132
3388
|
let endMarkerLength = 0;
|
|
@@ -3174,7 +3430,11 @@ var init_parser = __esm({
|
|
|
3174
3430
|
const metadataEndIndex = this.buffer.indexOf("\n", metadataStartIndex);
|
|
3175
3431
|
if (metadataEndIndex !== -1) {
|
|
3176
3432
|
const gadgetName = this.buffer.substring(metadataStartIndex, metadataEndIndex).trim();
|
|
3177
|
-
const {
|
|
3433
|
+
const {
|
|
3434
|
+
actualName: actualGadgetName,
|
|
3435
|
+
invocationId,
|
|
3436
|
+
dependencies
|
|
3437
|
+
} = this.parseGadgetName(gadgetName);
|
|
3178
3438
|
const contentStartIndex = metadataEndIndex + 1;
|
|
3179
3439
|
const parametersRaw = this.buffer.substring(contentStartIndex).trim();
|
|
3180
3440
|
const { parameters, parseError } = this.parseParameters(parametersRaw);
|
|
@@ -3219,11 +3479,12 @@ var init_executor = __esm({
|
|
|
3219
3479
|
init_exceptions();
|
|
3220
3480
|
init_parser();
|
|
3221
3481
|
GadgetExecutor = class {
|
|
3222
|
-
constructor(registry, onHumanInputRequired, logger, defaultGadgetTimeoutMs, errorFormatterOptions, client) {
|
|
3482
|
+
constructor(registry, onHumanInputRequired, logger, defaultGadgetTimeoutMs, errorFormatterOptions, client, mediaStore) {
|
|
3223
3483
|
this.registry = registry;
|
|
3224
3484
|
this.onHumanInputRequired = onHumanInputRequired;
|
|
3225
3485
|
this.defaultGadgetTimeoutMs = defaultGadgetTimeoutMs;
|
|
3226
3486
|
this.client = client;
|
|
3487
|
+
this.mediaStore = mediaStore;
|
|
3227
3488
|
this.logger = logger ?? createLogger({ name: "llmist:executor" });
|
|
3228
3489
|
this.errorFormatter = new GadgetErrorFormatter(errorFormatterOptions);
|
|
3229
3490
|
this.argPrefix = errorFormatterOptions?.argPrefix ?? GADGET_ARG_PREFIX;
|
|
@@ -3246,12 +3507,16 @@ var init_executor = __esm({
|
|
|
3246
3507
|
}
|
|
3247
3508
|
/**
|
|
3248
3509
|
* Normalizes gadget execute result to consistent format.
|
|
3249
|
-
* Handles
|
|
3510
|
+
* Handles string returns (backwards compat), object returns with cost,
|
|
3511
|
+
* and object returns with media.
|
|
3250
3512
|
*/
|
|
3251
3513
|
normalizeExecuteResult(raw) {
|
|
3252
3514
|
if (typeof raw === "string") {
|
|
3253
3515
|
return { result: raw, cost: 0 };
|
|
3254
3516
|
}
|
|
3517
|
+
if ("media" in raw && raw.media) {
|
|
3518
|
+
return { result: raw.result, media: raw.media, cost: raw.cost ?? 0 };
|
|
3519
|
+
}
|
|
3255
3520
|
return { result: raw.result, cost: raw.cost ?? 0 };
|
|
3256
3521
|
}
|
|
3257
3522
|
// Execute a gadget call asynchronously
|
|
@@ -3378,8 +3643,21 @@ var init_executor = __esm({
|
|
|
3378
3643
|
} else {
|
|
3379
3644
|
rawResult = await Promise.resolve(gadget.execute(validatedParameters, ctx));
|
|
3380
3645
|
}
|
|
3381
|
-
const { result, cost: returnCost } = this.normalizeExecuteResult(rawResult);
|
|
3646
|
+
const { result, media, cost: returnCost } = this.normalizeExecuteResult(rawResult);
|
|
3382
3647
|
const totalCost = callbackCost + returnCost;
|
|
3648
|
+
let mediaIds;
|
|
3649
|
+
let storedMedia;
|
|
3650
|
+
if (media && media.length > 0 && this.mediaStore) {
|
|
3651
|
+
storedMedia = await Promise.all(
|
|
3652
|
+
media.map((item) => this.mediaStore.store(item, call.gadgetName))
|
|
3653
|
+
);
|
|
3654
|
+
mediaIds = storedMedia.map((m) => m.id);
|
|
3655
|
+
this.logger.debug("Stored media outputs", {
|
|
3656
|
+
gadgetName: call.gadgetName,
|
|
3657
|
+
mediaIds,
|
|
3658
|
+
count: media.length
|
|
3659
|
+
});
|
|
3660
|
+
}
|
|
3383
3661
|
const executionTimeMs = Date.now() - startTime;
|
|
3384
3662
|
this.logger.info("Gadget executed successfully", {
|
|
3385
3663
|
gadgetName: call.gadgetName,
|
|
@@ -3387,7 +3665,8 @@ var init_executor = __esm({
|
|
|
3387
3665
|
executionTimeMs,
|
|
3388
3666
|
cost: totalCost > 0 ? totalCost : void 0,
|
|
3389
3667
|
callbackCost: callbackCost > 0 ? callbackCost : void 0,
|
|
3390
|
-
returnCost: returnCost > 0 ? returnCost : void 0
|
|
3668
|
+
returnCost: returnCost > 0 ? returnCost : void 0,
|
|
3669
|
+
mediaCount: media?.length
|
|
3391
3670
|
});
|
|
3392
3671
|
this.logger.debug("Gadget result", {
|
|
3393
3672
|
gadgetName: call.gadgetName,
|
|
@@ -3395,7 +3674,8 @@ var init_executor = __esm({
|
|
|
3395
3674
|
parameters: validatedParameters,
|
|
3396
3675
|
result,
|
|
3397
3676
|
cost: totalCost,
|
|
3398
|
-
executionTimeMs
|
|
3677
|
+
executionTimeMs,
|
|
3678
|
+
mediaIds
|
|
3399
3679
|
});
|
|
3400
3680
|
return {
|
|
3401
3681
|
gadgetName: call.gadgetName,
|
|
@@ -3403,7 +3683,10 @@ var init_executor = __esm({
|
|
|
3403
3683
|
parameters: validatedParameters,
|
|
3404
3684
|
result,
|
|
3405
3685
|
executionTimeMs,
|
|
3406
|
-
cost: totalCost
|
|
3686
|
+
cost: totalCost,
|
|
3687
|
+
media,
|
|
3688
|
+
mediaIds,
|
|
3689
|
+
storedMedia
|
|
3407
3690
|
};
|
|
3408
3691
|
} catch (error) {
|
|
3409
3692
|
if (error instanceof BreakLoopException) {
|
|
@@ -3581,7 +3864,8 @@ var init_stream_processor = __esm({
|
|
|
3581
3864
|
this.logger.getSubLogger({ name: "executor" }),
|
|
3582
3865
|
options.defaultGadgetTimeoutMs,
|
|
3583
3866
|
{ argPrefix: options.gadgetArgPrefix },
|
|
3584
|
-
options.client
|
|
3867
|
+
options.client,
|
|
3868
|
+
options.mediaStore
|
|
3585
3869
|
);
|
|
3586
3870
|
}
|
|
3587
3871
|
/**
|
|
@@ -4170,13 +4454,14 @@ var init_agent = __esm({
|
|
|
4170
4454
|
init_constants();
|
|
4171
4455
|
init_messages();
|
|
4172
4456
|
init_model_shortcuts();
|
|
4457
|
+
init_media_store();
|
|
4173
4458
|
init_output_viewer();
|
|
4174
4459
|
init_logger();
|
|
4175
|
-
init_manager();
|
|
4176
|
-
init_gadget_output_store();
|
|
4177
4460
|
init_agent_internal_key();
|
|
4461
|
+
init_manager();
|
|
4178
4462
|
init_conversation_manager();
|
|
4179
4463
|
init_event_handlers();
|
|
4464
|
+
init_gadget_output_store();
|
|
4180
4465
|
init_hook_validators();
|
|
4181
4466
|
init_stream_processor();
|
|
4182
4467
|
Agent = class {
|
|
@@ -4205,6 +4490,8 @@ var init_agent = __esm({
|
|
|
4205
4490
|
outputLimitCharLimit;
|
|
4206
4491
|
// Context compaction
|
|
4207
4492
|
compactionManager;
|
|
4493
|
+
// Media storage (for gadgets returning images, audio, etc.)
|
|
4494
|
+
mediaStore;
|
|
4208
4495
|
// Cancellation
|
|
4209
4496
|
signal;
|
|
4210
4497
|
/**
|
|
@@ -4235,6 +4522,7 @@ var init_agent = __esm({
|
|
|
4235
4522
|
this.defaultMaxTokens = this.resolveMaxTokensFromCatalog(options.model);
|
|
4236
4523
|
this.outputLimitEnabled = options.gadgetOutputLimit ?? DEFAULT_GADGET_OUTPUT_LIMIT;
|
|
4237
4524
|
this.outputStore = new GadgetOutputStore();
|
|
4525
|
+
this.mediaStore = new MediaStore();
|
|
4238
4526
|
const limitPercent = options.gadgetOutputLimitPercent ?? DEFAULT_GADGET_OUTPUT_LIMIT_PERCENT;
|
|
4239
4527
|
const limits = this.client.modelRegistry.getModelLimits(this.model);
|
|
4240
4528
|
const contextWindow = limits?.contextWindow ?? FALLBACK_CONTEXT_WINDOW;
|
|
@@ -4300,6 +4588,36 @@ var init_agent = __esm({
|
|
|
4300
4588
|
getRegistry() {
|
|
4301
4589
|
return this.registry;
|
|
4302
4590
|
}
|
|
4591
|
+
/**
|
|
4592
|
+
* Get the media store for this agent session.
|
|
4593
|
+
*
|
|
4594
|
+
* The media store holds all media outputs (images, audio, etc.) produced by gadgets
|
|
4595
|
+
* during this agent's execution. Use this to:
|
|
4596
|
+
* - Access stored media files by ID
|
|
4597
|
+
* - List all stored media
|
|
4598
|
+
* - Clean up temporary files after execution
|
|
4599
|
+
*
|
|
4600
|
+
* @returns The MediaStore instance for this agent
|
|
4601
|
+
*
|
|
4602
|
+
* @example
|
|
4603
|
+
* ```typescript
|
|
4604
|
+
* const agent = new AgentBuilder()
|
|
4605
|
+
* .withModel("sonnet")
|
|
4606
|
+
* .build();
|
|
4607
|
+
*
|
|
4608
|
+
* // After execution, access stored media
|
|
4609
|
+
* const store = agent.getMediaStore();
|
|
4610
|
+
* for (const media of store.list()) {
|
|
4611
|
+
* console.log(`${media.id}: ${media.path}`);
|
|
4612
|
+
* }
|
|
4613
|
+
*
|
|
4614
|
+
* // Clean up when done
|
|
4615
|
+
* await store.cleanup();
|
|
4616
|
+
* ```
|
|
4617
|
+
*/
|
|
4618
|
+
getMediaStore() {
|
|
4619
|
+
return this.mediaStore;
|
|
4620
|
+
}
|
|
4303
4621
|
/**
|
|
4304
4622
|
* Manually trigger context compaction.
|
|
4305
4623
|
*
|
|
@@ -4475,7 +4793,8 @@ var init_agent = __esm({
|
|
|
4475
4793
|
stopOnGadgetError: this.stopOnGadgetError,
|
|
4476
4794
|
shouldContinueAfterError: this.shouldContinueAfterError,
|
|
4477
4795
|
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
4478
|
-
client: this.client
|
|
4796
|
+
client: this.client,
|
|
4797
|
+
mediaStore: this.mediaStore
|
|
4479
4798
|
});
|
|
4480
4799
|
const result = await processor.process(stream2);
|
|
4481
4800
|
for (const output of result.outputs) {
|
|
@@ -4537,7 +4856,9 @@ var init_agent = __esm({
|
|
|
4537
4856
|
}
|
|
4538
4857
|
if (result.didExecuteGadgets) {
|
|
4539
4858
|
if (this.textWithGadgetsHandler) {
|
|
4540
|
-
const textContent = result.outputs.filter(
|
|
4859
|
+
const textContent = result.outputs.filter(
|
|
4860
|
+
(output) => output.type === "text"
|
|
4861
|
+
).map((output) => output.content).join("");
|
|
4541
4862
|
if (textContent.trim()) {
|
|
4542
4863
|
const { gadgetName, parameterMapping, resultMapping } = this.textWithGadgetsHandler;
|
|
4543
4864
|
this.conversation.addGadgetCall(
|
|
@@ -4553,7 +4874,9 @@ var init_agent = __esm({
|
|
|
4553
4874
|
this.conversation.addGadgetCall(
|
|
4554
4875
|
gadgetResult.gadgetName,
|
|
4555
4876
|
gadgetResult.parameters,
|
|
4556
|
-
gadgetResult.error ?? gadgetResult.result ?? ""
|
|
4877
|
+
gadgetResult.error ?? gadgetResult.result ?? "",
|
|
4878
|
+
gadgetResult.media,
|
|
4879
|
+
gadgetResult.mediaIds
|
|
4557
4880
|
);
|
|
4558
4881
|
}
|
|
4559
4882
|
}
|
|
@@ -7645,14 +7968,7 @@ var OPENAI_TTS_VOICES, OPENAI_TTS_EXTENDED_VOICES, OPENAI_TTS_FORMATS, openaiSpe
|
|
|
7645
7968
|
var init_openai_speech_models = __esm({
|
|
7646
7969
|
"src/providers/openai-speech-models.ts"() {
|
|
7647
7970
|
"use strict";
|
|
7648
|
-
OPENAI_TTS_VOICES = [
|
|
7649
|
-
"alloy",
|
|
7650
|
-
"echo",
|
|
7651
|
-
"fable",
|
|
7652
|
-
"onyx",
|
|
7653
|
-
"nova",
|
|
7654
|
-
"shimmer"
|
|
7655
|
-
];
|
|
7971
|
+
OPENAI_TTS_VOICES = ["alloy", "echo", "fable", "onyx", "nova", "shimmer"];
|
|
7656
7972
|
OPENAI_TTS_EXTENDED_VOICES = [
|
|
7657
7973
|
...OPENAI_TTS_VOICES,
|
|
7658
7974
|
"ash",
|
|
@@ -8350,9 +8666,7 @@ var init_image = __esm({
|
|
|
8350
8666
|
return this.findImageAdapter(modelId) !== void 0;
|
|
8351
8667
|
}
|
|
8352
8668
|
findImageAdapter(modelId) {
|
|
8353
|
-
return this.adapters.find(
|
|
8354
|
-
(adapter) => adapter.supportsImageGeneration?.(modelId) ?? false
|
|
8355
|
-
);
|
|
8669
|
+
return this.adapters.find((adapter) => adapter.supportsImageGeneration?.(modelId) ?? false);
|
|
8356
8670
|
}
|
|
8357
8671
|
};
|
|
8358
8672
|
}
|
|
@@ -8404,9 +8718,7 @@ var init_speech = __esm({
|
|
|
8404
8718
|
return this.findSpeechAdapter(modelId) !== void 0;
|
|
8405
8719
|
}
|
|
8406
8720
|
findSpeechAdapter(modelId) {
|
|
8407
|
-
return this.adapters.find(
|
|
8408
|
-
(adapter) => adapter.supportsSpeechGeneration?.(modelId) ?? false
|
|
8409
|
-
);
|
|
8721
|
+
return this.adapters.find((adapter) => adapter.supportsSpeechGeneration?.(modelId) ?? false);
|
|
8410
8722
|
}
|
|
8411
8723
|
};
|
|
8412
8724
|
}
|
|
@@ -8517,11 +8829,7 @@ var init_vision = __esm({
|
|
|
8517
8829
|
if (!parsed) {
|
|
8518
8830
|
throw new Error("Invalid data URL format");
|
|
8519
8831
|
}
|
|
8520
|
-
builder.addUserWithImage(
|
|
8521
|
-
options.prompt,
|
|
8522
|
-
parsed.data,
|
|
8523
|
-
parsed.mimeType
|
|
8524
|
-
);
|
|
8832
|
+
builder.addUserWithImage(options.prompt, parsed.data, parsed.mimeType);
|
|
8525
8833
|
} else {
|
|
8526
8834
|
const buffer = Buffer.from(options.image, "base64");
|
|
8527
8835
|
builder.addUserWithImage(options.prompt, buffer, options.mimeType);
|
|
@@ -8952,52 +9260,277 @@ __export(testing_exports, {
|
|
|
8952
9260
|
});
|
|
8953
9261
|
module.exports = __toCommonJS(testing_exports);
|
|
8954
9262
|
|
|
8955
|
-
// src/
|
|
8956
|
-
|
|
8957
|
-
|
|
8958
|
-
|
|
8959
|
-
|
|
8960
|
-
|
|
8961
|
-
|
|
8962
|
-
};
|
|
8963
|
-
}
|
|
8964
|
-
const issues = result.error.issues.map((issue) => ({
|
|
8965
|
-
path: issue.path.join(".") || "root",
|
|
8966
|
-
message: issue.message
|
|
8967
|
-
}));
|
|
8968
|
-
const formattedError = `Invalid parameters: ${issues.map((i) => `${i.path}: ${i.message}`).join("; ")}`;
|
|
9263
|
+
// src/testing/cli-helpers.ts
|
|
9264
|
+
var import_node_stream = require("stream");
|
|
9265
|
+
function createTestEnvironment(options = {}) {
|
|
9266
|
+
const stdin = createMockReadable(options.stdin);
|
|
9267
|
+
const stdout = new import_node_stream.PassThrough();
|
|
9268
|
+
const stderr = new import_node_stream.PassThrough();
|
|
9269
|
+
let exitCode;
|
|
8969
9270
|
return {
|
|
8970
|
-
|
|
8971
|
-
|
|
8972
|
-
|
|
9271
|
+
stdin,
|
|
9272
|
+
stdout,
|
|
9273
|
+
stderr,
|
|
9274
|
+
isTTY: options.isTTY ?? false,
|
|
9275
|
+
argv: options.argv ?? ["node", "llmist"],
|
|
9276
|
+
env: { ...filterDefinedEnv(process.env), ...options.env },
|
|
9277
|
+
get exitCode() {
|
|
9278
|
+
return exitCode;
|
|
9279
|
+
},
|
|
9280
|
+
setExitCode: (code) => {
|
|
9281
|
+
exitCode = code;
|
|
9282
|
+
}
|
|
8973
9283
|
};
|
|
8974
9284
|
}
|
|
8975
|
-
function
|
|
8976
|
-
if (!
|
|
8977
|
-
|
|
8978
|
-
|
|
8979
|
-
|
|
8980
|
-
|
|
9285
|
+
function createMockReadable(input) {
|
|
9286
|
+
if (!input) {
|
|
9287
|
+
const stream3 = new import_node_stream.Readable({ read() {
|
|
9288
|
+
} });
|
|
9289
|
+
stream3.push(null);
|
|
9290
|
+
return stream3;
|
|
8981
9291
|
}
|
|
8982
|
-
|
|
9292
|
+
const content = Array.isArray(input) ? `${input.join("\n")}
|
|
9293
|
+
` : input;
|
|
9294
|
+
const stream2 = new import_node_stream.Readable({ read() {
|
|
9295
|
+
} });
|
|
9296
|
+
stream2.push(content);
|
|
9297
|
+
stream2.push(null);
|
|
9298
|
+
return stream2;
|
|
8983
9299
|
}
|
|
8984
|
-
|
|
8985
|
-
|
|
8986
|
-
|
|
8987
|
-
|
|
8988
|
-
|
|
8989
|
-
|
|
8990
|
-
if (!validationResult.success) {
|
|
8991
|
-
return {
|
|
8992
|
-
error: validationResult.error,
|
|
8993
|
-
validatedParams: params
|
|
8994
|
-
};
|
|
9300
|
+
function createMockWritable() {
|
|
9301
|
+
const chunks = [];
|
|
9302
|
+
const stream2 = new import_node_stream.Writable({
|
|
9303
|
+
write(chunk, _encoding, callback) {
|
|
9304
|
+
chunks.push(Buffer.from(chunk));
|
|
9305
|
+
callback();
|
|
8995
9306
|
}
|
|
8996
|
-
|
|
8997
|
-
|
|
8998
|
-
|
|
8999
|
-
|
|
9000
|
-
|
|
9307
|
+
});
|
|
9308
|
+
stream2.getData = () => Buffer.concat(chunks).toString("utf8");
|
|
9309
|
+
return stream2;
|
|
9310
|
+
}
|
|
9311
|
+
async function collectOutput(stream2, timeout = 5e3) {
|
|
9312
|
+
return new Promise((resolve, reject) => {
|
|
9313
|
+
const chunks = [];
|
|
9314
|
+
const timeoutId = setTimeout(() => {
|
|
9315
|
+
resolve(Buffer.concat(chunks).toString("utf8"));
|
|
9316
|
+
}, timeout);
|
|
9317
|
+
stream2.on("data", (chunk) => {
|
|
9318
|
+
chunks.push(Buffer.from(chunk));
|
|
9319
|
+
});
|
|
9320
|
+
stream2.on("end", () => {
|
|
9321
|
+
clearTimeout(timeoutId);
|
|
9322
|
+
resolve(Buffer.concat(chunks).toString("utf8"));
|
|
9323
|
+
});
|
|
9324
|
+
stream2.on("error", (err) => {
|
|
9325
|
+
clearTimeout(timeoutId);
|
|
9326
|
+
reject(err);
|
|
9327
|
+
});
|
|
9328
|
+
});
|
|
9329
|
+
}
|
|
9330
|
+
function getBufferedOutput(stream2) {
|
|
9331
|
+
const chunks = [];
|
|
9332
|
+
for (; ; ) {
|
|
9333
|
+
const chunk = stream2.read();
|
|
9334
|
+
if (chunk === null) break;
|
|
9335
|
+
chunks.push(chunk);
|
|
9336
|
+
}
|
|
9337
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
9338
|
+
}
|
|
9339
|
+
function createMockPrompt(responses) {
|
|
9340
|
+
let index = 0;
|
|
9341
|
+
return async (_question) => {
|
|
9342
|
+
if (index >= responses.length) {
|
|
9343
|
+
throw new Error(`Mock prompt exhausted: no response for question ${index + 1}`);
|
|
9344
|
+
}
|
|
9345
|
+
return responses[index++];
|
|
9346
|
+
};
|
|
9347
|
+
}
|
|
9348
|
+
var MockPromptRecorder = class {
|
|
9349
|
+
responses;
|
|
9350
|
+
index = 0;
|
|
9351
|
+
questions = [];
|
|
9352
|
+
constructor(responses) {
|
|
9353
|
+
this.responses = responses;
|
|
9354
|
+
}
|
|
9355
|
+
/**
|
|
9356
|
+
* The prompt function to use in tests.
|
|
9357
|
+
*/
|
|
9358
|
+
prompt = async (question) => {
|
|
9359
|
+
this.questions.push(question);
|
|
9360
|
+
if (this.index >= this.responses.length) {
|
|
9361
|
+
throw new Error(`Mock prompt exhausted after ${this.index} questions`);
|
|
9362
|
+
}
|
|
9363
|
+
return this.responses[this.index++];
|
|
9364
|
+
};
|
|
9365
|
+
/**
|
|
9366
|
+
* Get all questions that were asked.
|
|
9367
|
+
*/
|
|
9368
|
+
getQuestions() {
|
|
9369
|
+
return [...this.questions];
|
|
9370
|
+
}
|
|
9371
|
+
/**
|
|
9372
|
+
* Get the number of questions asked.
|
|
9373
|
+
*/
|
|
9374
|
+
getQuestionCount() {
|
|
9375
|
+
return this.questions.length;
|
|
9376
|
+
}
|
|
9377
|
+
/**
|
|
9378
|
+
* Reset the recorder state.
|
|
9379
|
+
*/
|
|
9380
|
+
reset(newResponses) {
|
|
9381
|
+
this.index = 0;
|
|
9382
|
+
this.questions = [];
|
|
9383
|
+
if (newResponses) {
|
|
9384
|
+
this.responses = newResponses;
|
|
9385
|
+
}
|
|
9386
|
+
}
|
|
9387
|
+
};
|
|
9388
|
+
async function waitFor(condition, timeout = 5e3, interval = 50) {
|
|
9389
|
+
const startTime = Date.now();
|
|
9390
|
+
while (!condition()) {
|
|
9391
|
+
if (Date.now() - startTime > timeout) {
|
|
9392
|
+
throw new Error(`waitFor timed out after ${timeout}ms`);
|
|
9393
|
+
}
|
|
9394
|
+
await sleep(interval);
|
|
9395
|
+
}
|
|
9396
|
+
}
|
|
9397
|
+
function sleep(ms) {
|
|
9398
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
9399
|
+
}
|
|
9400
|
+
function filterDefinedEnv(env) {
|
|
9401
|
+
const result = {};
|
|
9402
|
+
for (const [key, value] of Object.entries(env)) {
|
|
9403
|
+
if (value !== void 0) {
|
|
9404
|
+
result[key] = value;
|
|
9405
|
+
}
|
|
9406
|
+
}
|
|
9407
|
+
return result;
|
|
9408
|
+
}
|
|
9409
|
+
|
|
9410
|
+
// src/testing/conversation-fixtures.ts
|
|
9411
|
+
function createConversation(turnCount, options) {
|
|
9412
|
+
const messages = [];
|
|
9413
|
+
const userPrefix = options?.userPrefix ?? "User message";
|
|
9414
|
+
const assistantPrefix = options?.assistantPrefix ?? "Assistant response";
|
|
9415
|
+
const contentLength = options?.contentLength ?? 100;
|
|
9416
|
+
for (let i = 0; i < turnCount; i++) {
|
|
9417
|
+
const padding = " ".repeat(Math.max(0, contentLength - 30));
|
|
9418
|
+
messages.push({
|
|
9419
|
+
role: "user",
|
|
9420
|
+
content: `${userPrefix} ${i + 1}: This is turn ${i + 1} of the conversation.${padding}`
|
|
9421
|
+
});
|
|
9422
|
+
messages.push({
|
|
9423
|
+
role: "assistant",
|
|
9424
|
+
content: `${assistantPrefix} ${i + 1}: I acknowledge turn ${i + 1}.${padding}`
|
|
9425
|
+
});
|
|
9426
|
+
}
|
|
9427
|
+
return messages;
|
|
9428
|
+
}
|
|
9429
|
+
function createConversationWithGadgets(turnCount, gadgetCallsPerTurn = 1, options) {
|
|
9430
|
+
const messages = [];
|
|
9431
|
+
const gadgetNames = options?.gadgetNames ?? ["search", "calculate", "read"];
|
|
9432
|
+
const contentLength = options?.contentLength ?? 50;
|
|
9433
|
+
let gadgetIndex = 0;
|
|
9434
|
+
for (let turn = 0; turn < turnCount; turn++) {
|
|
9435
|
+
messages.push({
|
|
9436
|
+
role: "user",
|
|
9437
|
+
content: `User request ${turn + 1}${"x".repeat(contentLength)}`
|
|
9438
|
+
});
|
|
9439
|
+
for (let g = 0; g < gadgetCallsPerTurn; g++) {
|
|
9440
|
+
const gadgetName = gadgetNames[gadgetIndex % gadgetNames.length];
|
|
9441
|
+
gadgetIndex++;
|
|
9442
|
+
messages.push({
|
|
9443
|
+
role: "assistant",
|
|
9444
|
+
content: `!!!GADGET_START:${gadgetName}
|
|
9445
|
+
!!!ARG:query
|
|
9446
|
+
test query ${turn}-${g}
|
|
9447
|
+
!!!GADGET_END`
|
|
9448
|
+
});
|
|
9449
|
+
messages.push({
|
|
9450
|
+
role: "user",
|
|
9451
|
+
content: `Result: Gadget ${gadgetName} returned result for query ${turn}-${g}`
|
|
9452
|
+
});
|
|
9453
|
+
}
|
|
9454
|
+
messages.push({
|
|
9455
|
+
role: "assistant",
|
|
9456
|
+
content: `Final response for turn ${turn + 1}${"y".repeat(contentLength)}`
|
|
9457
|
+
});
|
|
9458
|
+
}
|
|
9459
|
+
return messages;
|
|
9460
|
+
}
|
|
9461
|
+
function estimateTokens(messages) {
|
|
9462
|
+
return Math.ceil(messages.reduce((sum, msg) => sum + (msg.content?.length ?? 0), 0) / 4);
|
|
9463
|
+
}
|
|
9464
|
+
function createUserMessage(content) {
|
|
9465
|
+
return { role: "user", content };
|
|
9466
|
+
}
|
|
9467
|
+
function createAssistantMessage(content) {
|
|
9468
|
+
return { role: "assistant", content };
|
|
9469
|
+
}
|
|
9470
|
+
function createSystemMessage(content) {
|
|
9471
|
+
return { role: "system", content };
|
|
9472
|
+
}
|
|
9473
|
+
function createMinimalConversation() {
|
|
9474
|
+
return [
|
|
9475
|
+
{ role: "user", content: "Hello" },
|
|
9476
|
+
{ role: "assistant", content: "Hi there!" }
|
|
9477
|
+
];
|
|
9478
|
+
}
|
|
9479
|
+
function createLargeConversation(targetTokens, options) {
|
|
9480
|
+
const tokensPerTurn = options?.tokensPerTurn ?? 200;
|
|
9481
|
+
const turnsNeeded = Math.ceil(targetTokens / tokensPerTurn);
|
|
9482
|
+
const charsPerMessage = Math.floor(tokensPerTurn * 4 / 2);
|
|
9483
|
+
return createConversation(turnsNeeded, {
|
|
9484
|
+
contentLength: charsPerMessage
|
|
9485
|
+
});
|
|
9486
|
+
}
|
|
9487
|
+
|
|
9488
|
+
// src/gadgets/validation.ts
|
|
9489
|
+
function validateAndApplyDefaults(schema, params) {
|
|
9490
|
+
const result = schema.safeParse(params);
|
|
9491
|
+
if (result.success) {
|
|
9492
|
+
return {
|
|
9493
|
+
success: true,
|
|
9494
|
+
data: result.data
|
|
9495
|
+
};
|
|
9496
|
+
}
|
|
9497
|
+
const issues = result.error.issues.map((issue) => ({
|
|
9498
|
+
path: issue.path.join(".") || "root",
|
|
9499
|
+
message: issue.message
|
|
9500
|
+
}));
|
|
9501
|
+
const formattedError = `Invalid parameters: ${issues.map((i) => `${i.path}: ${i.message}`).join("; ")}`;
|
|
9502
|
+
return {
|
|
9503
|
+
success: false,
|
|
9504
|
+
error: formattedError,
|
|
9505
|
+
issues
|
|
9506
|
+
};
|
|
9507
|
+
}
|
|
9508
|
+
function validateGadgetParams(gadget, params) {
|
|
9509
|
+
if (!gadget.parameterSchema) {
|
|
9510
|
+
return {
|
|
9511
|
+
success: true,
|
|
9512
|
+
data: params
|
|
9513
|
+
};
|
|
9514
|
+
}
|
|
9515
|
+
return validateAndApplyDefaults(gadget.parameterSchema, params);
|
|
9516
|
+
}
|
|
9517
|
+
|
|
9518
|
+
// src/testing/gadget-testing.ts
|
|
9519
|
+
async function testGadget(gadget, params, options) {
|
|
9520
|
+
let validatedParams = params;
|
|
9521
|
+
if (!options?.skipValidation) {
|
|
9522
|
+
const validationResult = validateGadgetParams(gadget, params);
|
|
9523
|
+
if (!validationResult.success) {
|
|
9524
|
+
return {
|
|
9525
|
+
error: validationResult.error,
|
|
9526
|
+
validatedParams: params
|
|
9527
|
+
};
|
|
9528
|
+
}
|
|
9529
|
+
validatedParams = validationResult.data;
|
|
9530
|
+
}
|
|
9531
|
+
try {
|
|
9532
|
+
const rawResult = await Promise.resolve(gadget.execute(validatedParams));
|
|
9533
|
+
if (typeof rawResult === "string") {
|
|
9001
9534
|
return {
|
|
9002
9535
|
result: rawResult,
|
|
9003
9536
|
validatedParams,
|
|
@@ -9189,7 +9722,7 @@ function getMockManager(options) {
|
|
|
9189
9722
|
|
|
9190
9723
|
// src/testing/mock-stream.ts
|
|
9191
9724
|
init_constants();
|
|
9192
|
-
function
|
|
9725
|
+
function sleep2(ms) {
|
|
9193
9726
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
9194
9727
|
}
|
|
9195
9728
|
function generateInvocationId() {
|
|
@@ -9270,7 +9803,7 @@ ${blockParams}${GADGET_END_PREFIX}`;
|
|
|
9270
9803
|
}
|
|
9271
9804
|
async function* createMockStream(response) {
|
|
9272
9805
|
if (response.delayMs) {
|
|
9273
|
-
await
|
|
9806
|
+
await sleep2(response.delayMs);
|
|
9274
9807
|
}
|
|
9275
9808
|
const streamDelay = response.streamDelayMs ?? 0;
|
|
9276
9809
|
let fullText = response.text ?? "";
|
|
@@ -9295,7 +9828,7 @@ async function* createMockStream(response) {
|
|
|
9295
9828
|
}
|
|
9296
9829
|
yield chunk;
|
|
9297
9830
|
if (streamDelay > 0 && !isLast) {
|
|
9298
|
-
await
|
|
9831
|
+
await sleep2(streamDelay);
|
|
9299
9832
|
}
|
|
9300
9833
|
}
|
|
9301
9834
|
} else {
|
|
@@ -9572,7 +10105,9 @@ var MockBuilder = class {
|
|
|
9572
10105
|
*/
|
|
9573
10106
|
whenMessageContains(text3) {
|
|
9574
10107
|
this.matchers.push(
|
|
9575
|
-
(ctx) => ctx.messages.some(
|
|
10108
|
+
(ctx) => ctx.messages.some(
|
|
10109
|
+
(msg) => extractText(msg.content).toLowerCase().includes(text3.toLowerCase())
|
|
10110
|
+
)
|
|
9576
10111
|
);
|
|
9577
10112
|
return this;
|
|
9578
10113
|
}
|
|
@@ -10010,32 +10545,161 @@ function createMockClient(options) {
|
|
|
10010
10545
|
});
|
|
10011
10546
|
}
|
|
10012
10547
|
|
|
10013
|
-
// src/testing/mock-
|
|
10014
|
-
|
|
10015
|
-
|
|
10016
|
-
|
|
10017
|
-
|
|
10018
|
-
|
|
10019
|
-
|
|
10020
|
-
|
|
10021
|
-
|
|
10022
|
-
|
|
10023
|
-
|
|
10024
|
-
|
|
10025
|
-
|
|
10026
|
-
|
|
10027
|
-
|
|
10028
|
-
|
|
10029
|
-
|
|
10030
|
-
|
|
10031
|
-
this.
|
|
10032
|
-
this.
|
|
10033
|
-
|
|
10034
|
-
|
|
10035
|
-
|
|
10036
|
-
|
|
10037
|
-
|
|
10038
|
-
|
|
10548
|
+
// src/testing/mock-conversation.ts
|
|
10549
|
+
var MockConversationManager = class {
|
|
10550
|
+
history;
|
|
10551
|
+
baseMessages;
|
|
10552
|
+
replacementHistory;
|
|
10553
|
+
replaceHistoryCallCount = 0;
|
|
10554
|
+
addedMessages = [];
|
|
10555
|
+
constructor(history = [], baseMessages = []) {
|
|
10556
|
+
this.history = [...history];
|
|
10557
|
+
this.baseMessages = [...baseMessages];
|
|
10558
|
+
}
|
|
10559
|
+
addUserMessage(content) {
|
|
10560
|
+
const msg = { role: "user", content };
|
|
10561
|
+
this.history.push(msg);
|
|
10562
|
+
this.addedMessages.push(msg);
|
|
10563
|
+
}
|
|
10564
|
+
addAssistantMessage(content) {
|
|
10565
|
+
const msg = { role: "assistant", content };
|
|
10566
|
+
this.history.push(msg);
|
|
10567
|
+
this.addedMessages.push(msg);
|
|
10568
|
+
}
|
|
10569
|
+
addGadgetCall(gadgetName, parameters, result) {
|
|
10570
|
+
const assistantMsg = {
|
|
10571
|
+
role: "assistant",
|
|
10572
|
+
content: `!!!GADGET_START:${gadgetName}
|
|
10573
|
+
${JSON.stringify(parameters)}
|
|
10574
|
+
!!!GADGET_END`
|
|
10575
|
+
};
|
|
10576
|
+
const resultMsg = {
|
|
10577
|
+
role: "user",
|
|
10578
|
+
content: `Result: ${result}`
|
|
10579
|
+
};
|
|
10580
|
+
this.history.push(assistantMsg);
|
|
10581
|
+
this.history.push(resultMsg);
|
|
10582
|
+
this.addedMessages.push(assistantMsg);
|
|
10583
|
+
this.addedMessages.push(resultMsg);
|
|
10584
|
+
}
|
|
10585
|
+
getMessages() {
|
|
10586
|
+
return [...this.baseMessages, ...this.history];
|
|
10587
|
+
}
|
|
10588
|
+
getHistoryMessages() {
|
|
10589
|
+
return [...this.history];
|
|
10590
|
+
}
|
|
10591
|
+
getBaseMessages() {
|
|
10592
|
+
return [...this.baseMessages];
|
|
10593
|
+
}
|
|
10594
|
+
replaceHistory(newHistory) {
|
|
10595
|
+
this.replacementHistory = [...newHistory];
|
|
10596
|
+
this.history = [...newHistory];
|
|
10597
|
+
this.replaceHistoryCallCount++;
|
|
10598
|
+
}
|
|
10599
|
+
// ============================================
|
|
10600
|
+
// Test Helper Methods
|
|
10601
|
+
// ============================================
|
|
10602
|
+
/**
|
|
10603
|
+
* Check if replaceHistory was called.
|
|
10604
|
+
*/
|
|
10605
|
+
wasReplaceHistoryCalled() {
|
|
10606
|
+
return this.replaceHistoryCallCount > 0;
|
|
10607
|
+
}
|
|
10608
|
+
/**
|
|
10609
|
+
* Get the number of times replaceHistory was called.
|
|
10610
|
+
*/
|
|
10611
|
+
getReplaceHistoryCallCount() {
|
|
10612
|
+
return this.replaceHistoryCallCount;
|
|
10613
|
+
}
|
|
10614
|
+
/**
|
|
10615
|
+
* Get the most recent history passed to replaceHistory.
|
|
10616
|
+
* Returns undefined if replaceHistory was never called.
|
|
10617
|
+
*/
|
|
10618
|
+
getReplacementHistory() {
|
|
10619
|
+
return this.replacementHistory;
|
|
10620
|
+
}
|
|
10621
|
+
/**
|
|
10622
|
+
* Get all messages that were added via add* methods.
|
|
10623
|
+
*/
|
|
10624
|
+
getAddedMessages() {
|
|
10625
|
+
return [...this.addedMessages];
|
|
10626
|
+
}
|
|
10627
|
+
/**
|
|
10628
|
+
* Reset all tracking state while preserving the conversation.
|
|
10629
|
+
*/
|
|
10630
|
+
resetTracking() {
|
|
10631
|
+
this.replacementHistory = void 0;
|
|
10632
|
+
this.replaceHistoryCallCount = 0;
|
|
10633
|
+
this.addedMessages = [];
|
|
10634
|
+
}
|
|
10635
|
+
/**
|
|
10636
|
+
* Completely reset the mock to initial state.
|
|
10637
|
+
* Note: baseMessages cannot be changed after construction.
|
|
10638
|
+
*/
|
|
10639
|
+
reset(history = []) {
|
|
10640
|
+
this.history = [...history];
|
|
10641
|
+
this.resetTracking();
|
|
10642
|
+
}
|
|
10643
|
+
/**
|
|
10644
|
+
* Set the history directly (for test setup).
|
|
10645
|
+
*/
|
|
10646
|
+
setHistory(messages) {
|
|
10647
|
+
this.history = [...messages];
|
|
10648
|
+
}
|
|
10649
|
+
/**
|
|
10650
|
+
* Get the current history length.
|
|
10651
|
+
*/
|
|
10652
|
+
getHistoryLength() {
|
|
10653
|
+
return this.history.length;
|
|
10654
|
+
}
|
|
10655
|
+
/**
|
|
10656
|
+
* Get total message count (base + history).
|
|
10657
|
+
*/
|
|
10658
|
+
getTotalMessageCount() {
|
|
10659
|
+
return this.baseMessages.length + this.history.length;
|
|
10660
|
+
}
|
|
10661
|
+
};
|
|
10662
|
+
function createMockConversationManager(turnCount, baseMessages = []) {
|
|
10663
|
+
const history = [];
|
|
10664
|
+
for (let i = 0; i < turnCount; i++) {
|
|
10665
|
+
history.push({
|
|
10666
|
+
role: "user",
|
|
10667
|
+
content: `User message ${i + 1}: This is turn ${i + 1} of the conversation.`
|
|
10668
|
+
});
|
|
10669
|
+
history.push({
|
|
10670
|
+
role: "assistant",
|
|
10671
|
+
content: `Assistant response ${i + 1}: I acknowledge turn ${i + 1}.`
|
|
10672
|
+
});
|
|
10673
|
+
}
|
|
10674
|
+
return new MockConversationManager(history, baseMessages);
|
|
10675
|
+
}
|
|
10676
|
+
|
|
10677
|
+
// src/testing/mock-gadget.ts
|
|
10678
|
+
init_gadget();
|
|
10679
|
+
var MockGadgetImpl = class extends BaseGadget {
|
|
10680
|
+
name;
|
|
10681
|
+
description;
|
|
10682
|
+
parameterSchema;
|
|
10683
|
+
timeoutMs;
|
|
10684
|
+
calls = [];
|
|
10685
|
+
resultValue;
|
|
10686
|
+
resultFn;
|
|
10687
|
+
errorToThrow;
|
|
10688
|
+
delayMs;
|
|
10689
|
+
shouldTrackCalls;
|
|
10690
|
+
constructor(config) {
|
|
10691
|
+
super();
|
|
10692
|
+
this.name = config.name;
|
|
10693
|
+
this.description = config.description ?? `Mock gadget: ${config.name}`;
|
|
10694
|
+
this.parameterSchema = config.schema;
|
|
10695
|
+
this.resultValue = config.result;
|
|
10696
|
+
this.resultFn = config.resultFn;
|
|
10697
|
+
this.delayMs = config.delayMs ?? 0;
|
|
10698
|
+
this.shouldTrackCalls = config.trackCalls ?? true;
|
|
10699
|
+
this.timeoutMs = config.timeoutMs;
|
|
10700
|
+
if (config.error) {
|
|
10701
|
+
this.errorToThrow = typeof config.error === "string" ? new Error(config.error) : config.error;
|
|
10702
|
+
}
|
|
10039
10703
|
}
|
|
10040
10704
|
async execute(params) {
|
|
10041
10705
|
if (this.shouldTrackCalls) {
|
|
@@ -10169,7 +10833,7 @@ function createTestStream(chunks) {
|
|
|
10169
10833
|
function createTextStream(text3, options) {
|
|
10170
10834
|
return async function* () {
|
|
10171
10835
|
if (options?.delayMs) {
|
|
10172
|
-
await
|
|
10836
|
+
await sleep3(options.delayMs);
|
|
10173
10837
|
}
|
|
10174
10838
|
const chunkSize = options?.chunkSize ?? text3.length;
|
|
10175
10839
|
const chunks = [];
|
|
@@ -10191,7 +10855,7 @@ function createTextStream(text3, options) {
|
|
|
10191
10855
|
}
|
|
10192
10856
|
yield chunk;
|
|
10193
10857
|
if (options?.chunkDelayMs && !isLast) {
|
|
10194
|
-
await
|
|
10858
|
+
await sleep3(options.chunkDelayMs);
|
|
10195
10859
|
}
|
|
10196
10860
|
}
|
|
10197
10861
|
}();
|
|
@@ -10229,365 +10893,9 @@ function createErrorStream(chunksBeforeError, error) {
|
|
|
10229
10893
|
throw error;
|
|
10230
10894
|
}();
|
|
10231
10895
|
}
|
|
10232
|
-
function sleep2(ms) {
|
|
10233
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
10234
|
-
}
|
|
10235
|
-
|
|
10236
|
-
// src/testing/conversation-fixtures.ts
|
|
10237
|
-
function createConversation(turnCount, options) {
|
|
10238
|
-
const messages = [];
|
|
10239
|
-
const userPrefix = options?.userPrefix ?? "User message";
|
|
10240
|
-
const assistantPrefix = options?.assistantPrefix ?? "Assistant response";
|
|
10241
|
-
const contentLength = options?.contentLength ?? 100;
|
|
10242
|
-
for (let i = 0; i < turnCount; i++) {
|
|
10243
|
-
const padding = " ".repeat(Math.max(0, contentLength - 30));
|
|
10244
|
-
messages.push({
|
|
10245
|
-
role: "user",
|
|
10246
|
-
content: `${userPrefix} ${i + 1}: This is turn ${i + 1} of the conversation.${padding}`
|
|
10247
|
-
});
|
|
10248
|
-
messages.push({
|
|
10249
|
-
role: "assistant",
|
|
10250
|
-
content: `${assistantPrefix} ${i + 1}: I acknowledge turn ${i + 1}.${padding}`
|
|
10251
|
-
});
|
|
10252
|
-
}
|
|
10253
|
-
return messages;
|
|
10254
|
-
}
|
|
10255
|
-
function createConversationWithGadgets(turnCount, gadgetCallsPerTurn = 1, options) {
|
|
10256
|
-
const messages = [];
|
|
10257
|
-
const gadgetNames = options?.gadgetNames ?? ["search", "calculate", "read"];
|
|
10258
|
-
const contentLength = options?.contentLength ?? 50;
|
|
10259
|
-
let gadgetIndex = 0;
|
|
10260
|
-
for (let turn = 0; turn < turnCount; turn++) {
|
|
10261
|
-
messages.push({
|
|
10262
|
-
role: "user",
|
|
10263
|
-
content: `User request ${turn + 1}${"x".repeat(contentLength)}`
|
|
10264
|
-
});
|
|
10265
|
-
for (let g = 0; g < gadgetCallsPerTurn; g++) {
|
|
10266
|
-
const gadgetName = gadgetNames[gadgetIndex % gadgetNames.length];
|
|
10267
|
-
gadgetIndex++;
|
|
10268
|
-
messages.push({
|
|
10269
|
-
role: "assistant",
|
|
10270
|
-
content: `!!!GADGET_START:${gadgetName}
|
|
10271
|
-
!!!ARG:query
|
|
10272
|
-
test query ${turn}-${g}
|
|
10273
|
-
!!!GADGET_END`
|
|
10274
|
-
});
|
|
10275
|
-
messages.push({
|
|
10276
|
-
role: "user",
|
|
10277
|
-
content: `Result: Gadget ${gadgetName} returned result for query ${turn}-${g}`
|
|
10278
|
-
});
|
|
10279
|
-
}
|
|
10280
|
-
messages.push({
|
|
10281
|
-
role: "assistant",
|
|
10282
|
-
content: `Final response for turn ${turn + 1}${"y".repeat(contentLength)}`
|
|
10283
|
-
});
|
|
10284
|
-
}
|
|
10285
|
-
return messages;
|
|
10286
|
-
}
|
|
10287
|
-
function estimateTokens(messages) {
|
|
10288
|
-
return Math.ceil(
|
|
10289
|
-
messages.reduce((sum, msg) => sum + (msg.content?.length ?? 0), 0) / 4
|
|
10290
|
-
);
|
|
10291
|
-
}
|
|
10292
|
-
function createUserMessage(content) {
|
|
10293
|
-
return { role: "user", content };
|
|
10294
|
-
}
|
|
10295
|
-
function createAssistantMessage(content) {
|
|
10296
|
-
return { role: "assistant", content };
|
|
10297
|
-
}
|
|
10298
|
-
function createSystemMessage(content) {
|
|
10299
|
-
return { role: "system", content };
|
|
10300
|
-
}
|
|
10301
|
-
function createMinimalConversation() {
|
|
10302
|
-
return [
|
|
10303
|
-
{ role: "user", content: "Hello" },
|
|
10304
|
-
{ role: "assistant", content: "Hi there!" }
|
|
10305
|
-
];
|
|
10306
|
-
}
|
|
10307
|
-
function createLargeConversation(targetTokens, options) {
|
|
10308
|
-
const tokensPerTurn = options?.tokensPerTurn ?? 200;
|
|
10309
|
-
const turnsNeeded = Math.ceil(targetTokens / tokensPerTurn);
|
|
10310
|
-
const charsPerMessage = Math.floor(tokensPerTurn * 4 / 2);
|
|
10311
|
-
return createConversation(turnsNeeded, {
|
|
10312
|
-
contentLength: charsPerMessage
|
|
10313
|
-
});
|
|
10314
|
-
}
|
|
10315
|
-
|
|
10316
|
-
// src/testing/mock-conversation.ts
|
|
10317
|
-
var MockConversationManager = class {
|
|
10318
|
-
history;
|
|
10319
|
-
baseMessages;
|
|
10320
|
-
replacementHistory;
|
|
10321
|
-
replaceHistoryCallCount = 0;
|
|
10322
|
-
addedMessages = [];
|
|
10323
|
-
constructor(history = [], baseMessages = []) {
|
|
10324
|
-
this.history = [...history];
|
|
10325
|
-
this.baseMessages = [...baseMessages];
|
|
10326
|
-
}
|
|
10327
|
-
addUserMessage(content) {
|
|
10328
|
-
const msg = { role: "user", content };
|
|
10329
|
-
this.history.push(msg);
|
|
10330
|
-
this.addedMessages.push(msg);
|
|
10331
|
-
}
|
|
10332
|
-
addAssistantMessage(content) {
|
|
10333
|
-
const msg = { role: "assistant", content };
|
|
10334
|
-
this.history.push(msg);
|
|
10335
|
-
this.addedMessages.push(msg);
|
|
10336
|
-
}
|
|
10337
|
-
addGadgetCall(gadgetName, parameters, result) {
|
|
10338
|
-
const assistantMsg = {
|
|
10339
|
-
role: "assistant",
|
|
10340
|
-
content: `!!!GADGET_START:${gadgetName}
|
|
10341
|
-
${JSON.stringify(parameters)}
|
|
10342
|
-
!!!GADGET_END`
|
|
10343
|
-
};
|
|
10344
|
-
const resultMsg = {
|
|
10345
|
-
role: "user",
|
|
10346
|
-
content: `Result: ${result}`
|
|
10347
|
-
};
|
|
10348
|
-
this.history.push(assistantMsg);
|
|
10349
|
-
this.history.push(resultMsg);
|
|
10350
|
-
this.addedMessages.push(assistantMsg);
|
|
10351
|
-
this.addedMessages.push(resultMsg);
|
|
10352
|
-
}
|
|
10353
|
-
getMessages() {
|
|
10354
|
-
return [...this.baseMessages, ...this.history];
|
|
10355
|
-
}
|
|
10356
|
-
getHistoryMessages() {
|
|
10357
|
-
return [...this.history];
|
|
10358
|
-
}
|
|
10359
|
-
getBaseMessages() {
|
|
10360
|
-
return [...this.baseMessages];
|
|
10361
|
-
}
|
|
10362
|
-
replaceHistory(newHistory) {
|
|
10363
|
-
this.replacementHistory = [...newHistory];
|
|
10364
|
-
this.history = [...newHistory];
|
|
10365
|
-
this.replaceHistoryCallCount++;
|
|
10366
|
-
}
|
|
10367
|
-
// ============================================
|
|
10368
|
-
// Test Helper Methods
|
|
10369
|
-
// ============================================
|
|
10370
|
-
/**
|
|
10371
|
-
* Check if replaceHistory was called.
|
|
10372
|
-
*/
|
|
10373
|
-
wasReplaceHistoryCalled() {
|
|
10374
|
-
return this.replaceHistoryCallCount > 0;
|
|
10375
|
-
}
|
|
10376
|
-
/**
|
|
10377
|
-
* Get the number of times replaceHistory was called.
|
|
10378
|
-
*/
|
|
10379
|
-
getReplaceHistoryCallCount() {
|
|
10380
|
-
return this.replaceHistoryCallCount;
|
|
10381
|
-
}
|
|
10382
|
-
/**
|
|
10383
|
-
* Get the most recent history passed to replaceHistory.
|
|
10384
|
-
* Returns undefined if replaceHistory was never called.
|
|
10385
|
-
*/
|
|
10386
|
-
getReplacementHistory() {
|
|
10387
|
-
return this.replacementHistory;
|
|
10388
|
-
}
|
|
10389
|
-
/**
|
|
10390
|
-
* Get all messages that were added via add* methods.
|
|
10391
|
-
*/
|
|
10392
|
-
getAddedMessages() {
|
|
10393
|
-
return [...this.addedMessages];
|
|
10394
|
-
}
|
|
10395
|
-
/**
|
|
10396
|
-
* Reset all tracking state while preserving the conversation.
|
|
10397
|
-
*/
|
|
10398
|
-
resetTracking() {
|
|
10399
|
-
this.replacementHistory = void 0;
|
|
10400
|
-
this.replaceHistoryCallCount = 0;
|
|
10401
|
-
this.addedMessages = [];
|
|
10402
|
-
}
|
|
10403
|
-
/**
|
|
10404
|
-
* Completely reset the mock to initial state.
|
|
10405
|
-
* Note: baseMessages cannot be changed after construction.
|
|
10406
|
-
*/
|
|
10407
|
-
reset(history = []) {
|
|
10408
|
-
this.history = [...history];
|
|
10409
|
-
this.resetTracking();
|
|
10410
|
-
}
|
|
10411
|
-
/**
|
|
10412
|
-
* Set the history directly (for test setup).
|
|
10413
|
-
*/
|
|
10414
|
-
setHistory(messages) {
|
|
10415
|
-
this.history = [...messages];
|
|
10416
|
-
}
|
|
10417
|
-
/**
|
|
10418
|
-
* Get the current history length.
|
|
10419
|
-
*/
|
|
10420
|
-
getHistoryLength() {
|
|
10421
|
-
return this.history.length;
|
|
10422
|
-
}
|
|
10423
|
-
/**
|
|
10424
|
-
* Get total message count (base + history).
|
|
10425
|
-
*/
|
|
10426
|
-
getTotalMessageCount() {
|
|
10427
|
-
return this.baseMessages.length + this.history.length;
|
|
10428
|
-
}
|
|
10429
|
-
};
|
|
10430
|
-
function createMockConversationManager(turnCount, baseMessages = []) {
|
|
10431
|
-
const history = [];
|
|
10432
|
-
for (let i = 0; i < turnCount; i++) {
|
|
10433
|
-
history.push({
|
|
10434
|
-
role: "user",
|
|
10435
|
-
content: `User message ${i + 1}: This is turn ${i + 1} of the conversation.`
|
|
10436
|
-
});
|
|
10437
|
-
history.push({
|
|
10438
|
-
role: "assistant",
|
|
10439
|
-
content: `Assistant response ${i + 1}: I acknowledge turn ${i + 1}.`
|
|
10440
|
-
});
|
|
10441
|
-
}
|
|
10442
|
-
return new MockConversationManager(history, baseMessages);
|
|
10443
|
-
}
|
|
10444
|
-
|
|
10445
|
-
// src/testing/cli-helpers.ts
|
|
10446
|
-
var import_node_stream = require("stream");
|
|
10447
|
-
function createTestEnvironment(options = {}) {
|
|
10448
|
-
const stdin = createMockReadable(options.stdin);
|
|
10449
|
-
const stdout = new import_node_stream.PassThrough();
|
|
10450
|
-
const stderr = new import_node_stream.PassThrough();
|
|
10451
|
-
let exitCode;
|
|
10452
|
-
return {
|
|
10453
|
-
stdin,
|
|
10454
|
-
stdout,
|
|
10455
|
-
stderr,
|
|
10456
|
-
isTTY: options.isTTY ?? false,
|
|
10457
|
-
argv: options.argv ?? ["node", "llmist"],
|
|
10458
|
-
env: { ...filterDefinedEnv(process.env), ...options.env },
|
|
10459
|
-
get exitCode() {
|
|
10460
|
-
return exitCode;
|
|
10461
|
-
},
|
|
10462
|
-
setExitCode: (code) => {
|
|
10463
|
-
exitCode = code;
|
|
10464
|
-
}
|
|
10465
|
-
};
|
|
10466
|
-
}
|
|
10467
|
-
function createMockReadable(input) {
|
|
10468
|
-
if (!input) {
|
|
10469
|
-
const stream3 = new import_node_stream.Readable({ read() {
|
|
10470
|
-
} });
|
|
10471
|
-
stream3.push(null);
|
|
10472
|
-
return stream3;
|
|
10473
|
-
}
|
|
10474
|
-
const content = Array.isArray(input) ? `${input.join("\n")}
|
|
10475
|
-
` : input;
|
|
10476
|
-
const stream2 = new import_node_stream.Readable({ read() {
|
|
10477
|
-
} });
|
|
10478
|
-
stream2.push(content);
|
|
10479
|
-
stream2.push(null);
|
|
10480
|
-
return stream2;
|
|
10481
|
-
}
|
|
10482
|
-
function createMockWritable() {
|
|
10483
|
-
const chunks = [];
|
|
10484
|
-
const stream2 = new import_node_stream.Writable({
|
|
10485
|
-
write(chunk, _encoding, callback) {
|
|
10486
|
-
chunks.push(Buffer.from(chunk));
|
|
10487
|
-
callback();
|
|
10488
|
-
}
|
|
10489
|
-
});
|
|
10490
|
-
stream2.getData = () => Buffer.concat(chunks).toString("utf8");
|
|
10491
|
-
return stream2;
|
|
10492
|
-
}
|
|
10493
|
-
async function collectOutput(stream2, timeout = 5e3) {
|
|
10494
|
-
return new Promise((resolve, reject) => {
|
|
10495
|
-
const chunks = [];
|
|
10496
|
-
const timeoutId = setTimeout(() => {
|
|
10497
|
-
resolve(Buffer.concat(chunks).toString("utf8"));
|
|
10498
|
-
}, timeout);
|
|
10499
|
-
stream2.on("data", (chunk) => {
|
|
10500
|
-
chunks.push(Buffer.from(chunk));
|
|
10501
|
-
});
|
|
10502
|
-
stream2.on("end", () => {
|
|
10503
|
-
clearTimeout(timeoutId);
|
|
10504
|
-
resolve(Buffer.concat(chunks).toString("utf8"));
|
|
10505
|
-
});
|
|
10506
|
-
stream2.on("error", (err) => {
|
|
10507
|
-
clearTimeout(timeoutId);
|
|
10508
|
-
reject(err);
|
|
10509
|
-
});
|
|
10510
|
-
});
|
|
10511
|
-
}
|
|
10512
|
-
function getBufferedOutput(stream2) {
|
|
10513
|
-
const chunks = [];
|
|
10514
|
-
for (; ; ) {
|
|
10515
|
-
const chunk = stream2.read();
|
|
10516
|
-
if (chunk === null) break;
|
|
10517
|
-
chunks.push(chunk);
|
|
10518
|
-
}
|
|
10519
|
-
return Buffer.concat(chunks).toString("utf8");
|
|
10520
|
-
}
|
|
10521
|
-
function createMockPrompt(responses) {
|
|
10522
|
-
let index = 0;
|
|
10523
|
-
return async (_question) => {
|
|
10524
|
-
if (index >= responses.length) {
|
|
10525
|
-
throw new Error(`Mock prompt exhausted: no response for question ${index + 1}`);
|
|
10526
|
-
}
|
|
10527
|
-
return responses[index++];
|
|
10528
|
-
};
|
|
10529
|
-
}
|
|
10530
|
-
var MockPromptRecorder = class {
|
|
10531
|
-
responses;
|
|
10532
|
-
index = 0;
|
|
10533
|
-
questions = [];
|
|
10534
|
-
constructor(responses) {
|
|
10535
|
-
this.responses = responses;
|
|
10536
|
-
}
|
|
10537
|
-
/**
|
|
10538
|
-
* The prompt function to use in tests.
|
|
10539
|
-
*/
|
|
10540
|
-
prompt = async (question) => {
|
|
10541
|
-
this.questions.push(question);
|
|
10542
|
-
if (this.index >= this.responses.length) {
|
|
10543
|
-
throw new Error(`Mock prompt exhausted after ${this.index} questions`);
|
|
10544
|
-
}
|
|
10545
|
-
return this.responses[this.index++];
|
|
10546
|
-
};
|
|
10547
|
-
/**
|
|
10548
|
-
* Get all questions that were asked.
|
|
10549
|
-
*/
|
|
10550
|
-
getQuestions() {
|
|
10551
|
-
return [...this.questions];
|
|
10552
|
-
}
|
|
10553
|
-
/**
|
|
10554
|
-
* Get the number of questions asked.
|
|
10555
|
-
*/
|
|
10556
|
-
getQuestionCount() {
|
|
10557
|
-
return this.questions.length;
|
|
10558
|
-
}
|
|
10559
|
-
/**
|
|
10560
|
-
* Reset the recorder state.
|
|
10561
|
-
*/
|
|
10562
|
-
reset(newResponses) {
|
|
10563
|
-
this.index = 0;
|
|
10564
|
-
this.questions = [];
|
|
10565
|
-
if (newResponses) {
|
|
10566
|
-
this.responses = newResponses;
|
|
10567
|
-
}
|
|
10568
|
-
}
|
|
10569
|
-
};
|
|
10570
|
-
async function waitFor(condition, timeout = 5e3, interval = 50) {
|
|
10571
|
-
const startTime = Date.now();
|
|
10572
|
-
while (!condition()) {
|
|
10573
|
-
if (Date.now() - startTime > timeout) {
|
|
10574
|
-
throw new Error(`waitFor timed out after ${timeout}ms`);
|
|
10575
|
-
}
|
|
10576
|
-
await sleep3(interval);
|
|
10577
|
-
}
|
|
10578
|
-
}
|
|
10579
10896
|
function sleep3(ms) {
|
|
10580
10897
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
10581
10898
|
}
|
|
10582
|
-
function filterDefinedEnv(env) {
|
|
10583
|
-
const result = {};
|
|
10584
|
-
for (const [key, value] of Object.entries(env)) {
|
|
10585
|
-
if (value !== void 0) {
|
|
10586
|
-
result[key] = value;
|
|
10587
|
-
}
|
|
10588
|
-
}
|
|
10589
|
-
return result;
|
|
10590
|
-
}
|
|
10591
10899
|
// Annotate the CommonJS export names for ESM import in node:
|
|
10592
10900
|
0 && (module.exports = {
|
|
10593
10901
|
MockBuilder,
|