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/dist/index.cjs CHANGED
@@ -192,7 +192,9 @@ var init_input_content = __esm({
192
192
  // WAV (RIFF)
193
193
  { bytes: [82, 73, 70, 70], mimeType: "audio/wav" },
194
194
  // WebM
195
- { bytes: [26, 69, 223, 163], mimeType: "audio/webm" }
195
+ { bytes: [26, 69, 223, 163], mimeType: "audio/webm" },
196
+ // FLAC (fLaC)
197
+ { bytes: [102, 76, 97, 67], mimeType: "audio/flac" }
196
198
  ];
197
199
  }
198
200
  });
@@ -564,13 +566,13 @@ var init_prompt_config = __esm({
564
566
  });
565
567
 
566
568
  // src/core/messages.ts
567
- function normalizeContent(content) {
569
+ function normalizeMessageContent(content) {
568
570
  if (typeof content === "string") {
569
571
  return [{ type: "text", text: content }];
570
572
  }
571
573
  return content;
572
574
  }
573
- function extractText(content) {
575
+ function extractMessageText(content) {
574
576
  if (typeof content === "string") {
575
577
  return content;
576
578
  }
@@ -930,7 +932,17 @@ Produces: { "items": ["first", "second"] }`);
930
932
  this.messages.push({ role: "user", content: parts });
931
933
  return this;
932
934
  }
933
- addGadgetCall(gadget, parameters, result) {
935
+ /**
936
+ * Record a gadget execution result in the message history.
937
+ * Creates an assistant message with the gadget invocation and a user message with the result.
938
+ *
939
+ * @param gadget - Name of the gadget that was executed
940
+ * @param parameters - Parameters that were passed to the gadget
941
+ * @param result - Text result from the gadget execution
942
+ * @param media - Optional media outputs from the gadget
943
+ * @param mediaIds - Optional IDs for the media outputs
944
+ */
945
+ addGadgetCallResult(gadget, parameters, result, media, mediaIds) {
934
946
  const paramStr = this.formatBlockParameters(parameters, "");
935
947
  this.messages.push({
936
948
  role: "assistant",
@@ -938,10 +950,25 @@ Produces: { "items": ["first", "second"] }`);
938
950
  ${paramStr}
939
951
  ${this.endPrefix}`
940
952
  });
941
- this.messages.push({
942
- role: "user",
943
- content: `Result: ${result}`
944
- });
953
+ if (media && media.length > 0 && mediaIds && mediaIds.length > 0) {
954
+ const idRefs = media.map((m, i) => `[Media: ${mediaIds[i]} (${m.kind})]`).join("\n");
955
+ const textWithIds = `Result: ${result}
956
+ ${idRefs}`;
957
+ const parts = [text(textWithIds)];
958
+ for (const item of media) {
959
+ if (item.kind === "image") {
960
+ parts.push(imageFromBase64(item.data, item.mimeType));
961
+ } else if (item.kind === "audio") {
962
+ parts.push(audioFromBase64(item.data, item.mimeType));
963
+ }
964
+ }
965
+ this.messages.push({ role: "user", content: parts });
966
+ } else {
967
+ this.messages.push({
968
+ role: "user",
969
+ content: `Result: ${result}`
970
+ });
971
+ }
945
972
  return this;
946
973
  }
947
974
  /**
@@ -978,22 +1005,226 @@ ${this.endPrefix}`
978
1005
  }
979
1006
  });
980
1007
 
1008
+ // src/gadgets/media-store.ts
1009
+ function getLlmistTmpDir() {
1010
+ return (0, import_node_path.join)((0, import_node_os.homedir)(), ".llmist", "tmp");
1011
+ }
1012
+ var import_node_crypto, import_promises, import_node_os, import_node_path, MIME_TO_EXTENSION, MediaStore;
1013
+ var init_media_store = __esm({
1014
+ "src/gadgets/media-store.ts"() {
1015
+ "use strict";
1016
+ import_node_crypto = require("crypto");
1017
+ import_promises = require("fs/promises");
1018
+ import_node_os = require("os");
1019
+ import_node_path = require("path");
1020
+ MIME_TO_EXTENSION = {
1021
+ // Images
1022
+ "image/png": ".png",
1023
+ "image/jpeg": ".jpg",
1024
+ "image/gif": ".gif",
1025
+ "image/webp": ".webp",
1026
+ "image/svg+xml": ".svg",
1027
+ "image/bmp": ".bmp",
1028
+ "image/tiff": ".tiff",
1029
+ // Audio
1030
+ "audio/mp3": ".mp3",
1031
+ "audio/mpeg": ".mp3",
1032
+ "audio/wav": ".wav",
1033
+ "audio/webm": ".webm",
1034
+ "audio/ogg": ".ogg",
1035
+ "audio/flac": ".flac",
1036
+ "audio/aac": ".aac",
1037
+ // Video
1038
+ "video/mp4": ".mp4",
1039
+ "video/webm": ".webm",
1040
+ "video/ogg": ".ogv",
1041
+ "video/quicktime": ".mov",
1042
+ "video/x-msvideo": ".avi",
1043
+ // Documents
1044
+ "application/pdf": ".pdf",
1045
+ "application/json": ".json",
1046
+ "text/plain": ".txt",
1047
+ "text/html": ".html",
1048
+ "text/css": ".css",
1049
+ "text/javascript": ".js"
1050
+ };
1051
+ MediaStore = class {
1052
+ items = /* @__PURE__ */ new Map();
1053
+ outputDir;
1054
+ counter = 0;
1055
+ initialized = false;
1056
+ /**
1057
+ * Create a new MediaStore.
1058
+ *
1059
+ * @param sessionId - Optional session ID for the output directory.
1060
+ * If not provided, a random ID is generated.
1061
+ */
1062
+ constructor(sessionId) {
1063
+ const id = sessionId ?? (0, import_node_crypto.randomBytes)(8).toString("hex");
1064
+ this.outputDir = (0, import_node_path.join)(getLlmistTmpDir(), `media-${id}`);
1065
+ }
1066
+ /**
1067
+ * Get the output directory path.
1068
+ */
1069
+ getOutputDir() {
1070
+ return this.outputDir;
1071
+ }
1072
+ /**
1073
+ * Ensure the output directory exists.
1074
+ * @throws Error if directory creation fails
1075
+ */
1076
+ async ensureDir() {
1077
+ if (this.initialized) return;
1078
+ try {
1079
+ await (0, import_promises.mkdir)(this.outputDir, { recursive: true });
1080
+ this.initialized = true;
1081
+ } catch (error) {
1082
+ throw new Error(
1083
+ `MediaStore: Failed to create directory ${this.outputDir}: ${error instanceof Error ? error.message : String(error)}`
1084
+ );
1085
+ }
1086
+ }
1087
+ /**
1088
+ * Generate a unique media ID.
1089
+ * Format: "media_" + 6 random alphanumeric characters
1090
+ */
1091
+ generateId() {
1092
+ const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
1093
+ let id = "media_";
1094
+ const bytes = (0, import_node_crypto.randomBytes)(6);
1095
+ for (let i = 0; i < 6; i++) {
1096
+ id += chars[bytes[i] % chars.length];
1097
+ }
1098
+ return id;
1099
+ }
1100
+ /**
1101
+ * Get file extension from MIME type.
1102
+ */
1103
+ getExtension(mimeType) {
1104
+ return MIME_TO_EXTENSION[mimeType] ?? ".bin";
1105
+ }
1106
+ /**
1107
+ * Store media and return stored metadata with ID.
1108
+ *
1109
+ * @param media - The media output from a gadget
1110
+ * @param gadgetName - Name of the gadget that created this media
1111
+ * @returns Stored media information including generated ID
1112
+ * @throws Error if file write fails
1113
+ */
1114
+ async store(media, gadgetName) {
1115
+ await this.ensureDir();
1116
+ const id = this.generateId();
1117
+ const ext = this.getExtension(media.mimeType);
1118
+ const filename = media.fileName ?? `${gadgetName}_${String(++this.counter).padStart(3, "0")}${ext}`;
1119
+ const filePath = (0, import_node_path.join)(this.outputDir, filename);
1120
+ const buffer = Buffer.from(media.data, "base64");
1121
+ try {
1122
+ await (0, import_promises.writeFile)(filePath, buffer);
1123
+ } catch (error) {
1124
+ throw new Error(
1125
+ `MediaStore: Failed to write media file ${filePath}: ${error instanceof Error ? error.message : String(error)}`
1126
+ );
1127
+ }
1128
+ const stored = {
1129
+ id,
1130
+ kind: media.kind,
1131
+ path: filePath,
1132
+ mimeType: media.mimeType,
1133
+ sizeBytes: buffer.length,
1134
+ description: media.description,
1135
+ metadata: media.metadata,
1136
+ gadgetName,
1137
+ createdAt: /* @__PURE__ */ new Date()
1138
+ };
1139
+ this.items.set(id, stored);
1140
+ return stored;
1141
+ }
1142
+ /**
1143
+ * Get stored media by ID.
1144
+ *
1145
+ * @param id - The media ID (e.g., "media_a1b2c3")
1146
+ * @returns The stored media or undefined if not found
1147
+ */
1148
+ get(id) {
1149
+ return this.items.get(id);
1150
+ }
1151
+ /**
1152
+ * Get the actual file path for a media ID.
1153
+ * Convenience method for gadgets that need the raw path.
1154
+ *
1155
+ * @param id - The media ID
1156
+ * @returns The file path or undefined if not found
1157
+ */
1158
+ getPath(id) {
1159
+ return this.items.get(id)?.path;
1160
+ }
1161
+ /**
1162
+ * List all stored media, optionally filtered by kind.
1163
+ *
1164
+ * @param kind - Optional media kind to filter by
1165
+ * @returns Array of stored media items
1166
+ */
1167
+ list(kind) {
1168
+ const all = Array.from(this.items.values());
1169
+ if (kind) {
1170
+ return all.filter((item) => item.kind === kind);
1171
+ }
1172
+ return all;
1173
+ }
1174
+ /**
1175
+ * Get the count of stored media items.
1176
+ */
1177
+ get size() {
1178
+ return this.items.size;
1179
+ }
1180
+ /**
1181
+ * Check if a media ID exists.
1182
+ */
1183
+ has(id) {
1184
+ return this.items.has(id);
1185
+ }
1186
+ /**
1187
+ * Clear in-memory store without deleting files.
1188
+ * Resets the counter but leaves files on disk.
1189
+ */
1190
+ clear() {
1191
+ this.items.clear();
1192
+ this.counter = 0;
1193
+ }
1194
+ /**
1195
+ * Delete all stored files and clear memory.
1196
+ * Removes the entire session directory.
1197
+ */
1198
+ async cleanup() {
1199
+ if (this.initialized) {
1200
+ try {
1201
+ await (0, import_promises.rm)(this.outputDir, { recursive: true, force: true });
1202
+ } catch {
1203
+ }
1204
+ this.initialized = false;
1205
+ }
1206
+ this.clear();
1207
+ }
1208
+ };
1209
+ }
1210
+ });
1211
+
981
1212
  // src/gadgets/exceptions.ts
982
- var BreakLoopException, HumanInputException, TimeoutException, AbortError;
1213
+ var TaskCompletionSignal, HumanInputRequiredException, TimeoutException, AbortException;
983
1214
  var init_exceptions = __esm({
984
1215
  "src/gadgets/exceptions.ts"() {
985
1216
  "use strict";
986
- BreakLoopException = class extends Error {
1217
+ TaskCompletionSignal = class extends Error {
987
1218
  constructor(message) {
988
1219
  super(message ?? "Agent loop terminated by gadget");
989
- this.name = "BreakLoopException";
1220
+ this.name = "TaskCompletionSignal";
990
1221
  }
991
1222
  };
992
- HumanInputException = class extends Error {
1223
+ HumanInputRequiredException = class extends Error {
993
1224
  question;
994
1225
  constructor(question) {
995
1226
  super(`Human input required: ${question}`);
996
- this.name = "HumanInputException";
1227
+ this.name = "HumanInputRequiredException";
997
1228
  this.question = question;
998
1229
  }
999
1230
  };
@@ -1007,10 +1238,10 @@ var init_exceptions = __esm({
1007
1238
  this.timeoutMs = timeoutMs;
1008
1239
  }
1009
1240
  };
1010
- AbortError = class extends Error {
1241
+ AbortException = class extends Error {
1011
1242
  constructor(message) {
1012
1243
  super(message || "Gadget execution was aborted");
1013
- this.name = "AbortError";
1244
+ this.name = "AbortException";
1014
1245
  }
1015
1246
  };
1016
1247
  }
@@ -1050,7 +1281,7 @@ function createLogger(options = {}) {
1050
1281
  let finalType = defaultType;
1051
1282
  if (envLogFile) {
1052
1283
  try {
1053
- (0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(envLogFile), { recursive: true });
1284
+ (0, import_node_fs.mkdirSync)((0, import_node_path2.dirname)(envLogFile), { recursive: true });
1054
1285
  const flags = logReset ? "w" : "a";
1055
1286
  logFileStream = (0, import_node_fs.createWriteStream)(envLogFile, { flags });
1056
1287
  finalType = "hidden";
@@ -1075,12 +1306,12 @@ function createLogger(options = {}) {
1075
1306
  }
1076
1307
  return logger;
1077
1308
  }
1078
- var import_node_fs, import_node_path, import_tslog, LEVEL_NAME_TO_ID, defaultLogger;
1309
+ var import_node_fs, import_node_path2, import_tslog, LEVEL_NAME_TO_ID, defaultLogger;
1079
1310
  var init_logger = __esm({
1080
1311
  "src/logging/logger.ts"() {
1081
1312
  "use strict";
1082
1313
  import_node_fs = require("fs");
1083
- import_node_path = require("path");
1314
+ import_node_path2 = require("path");
1084
1315
  import_tslog = require("tslog");
1085
1316
  LEVEL_NAME_TO_ID = {
1086
1317
  silly: 0,
@@ -1179,7 +1410,7 @@ var init_schema_to_json = __esm({
1179
1410
  });
1180
1411
 
1181
1412
  // src/gadgets/gadget.ts
1182
- function formatParamsAsBlock(params, prefix = "", argPrefix = GADGET_ARG_PREFIX) {
1413
+ function formatParamsForBlockExample(params, prefix = "", argPrefix = GADGET_ARG_PREFIX) {
1183
1414
  const lines = [];
1184
1415
  for (const [key, value] of Object.entries(params)) {
1185
1416
  const fullPath = prefix ? `${prefix}/${key}` : key;
@@ -1187,14 +1418,14 @@ function formatParamsAsBlock(params, prefix = "", argPrefix = GADGET_ARG_PREFIX)
1187
1418
  value.forEach((item, index) => {
1188
1419
  const itemPath = `${fullPath}/${index}`;
1189
1420
  if (typeof item === "object" && item !== null) {
1190
- lines.push(formatParamsAsBlock(item, itemPath, argPrefix));
1421
+ lines.push(formatParamsForBlockExample(item, itemPath, argPrefix));
1191
1422
  } else {
1192
1423
  lines.push(`${argPrefix}${itemPath}`);
1193
1424
  lines.push(String(item));
1194
1425
  }
1195
1426
  });
1196
1427
  } else if (typeof value === "object" && value !== null) {
1197
- lines.push(formatParamsAsBlock(value, fullPath, argPrefix));
1428
+ lines.push(formatParamsForBlockExample(value, fullPath, argPrefix));
1198
1429
  } else {
1199
1430
  lines.push(`${argPrefix}${fullPath}`);
1200
1431
  lines.push(String(value));
@@ -1283,7 +1514,7 @@ function formatSchemaAsPlainText(schema, indent = "", atRoot = true) {
1283
1514
  }
1284
1515
  return lines.join("\n");
1285
1516
  }
1286
- var BaseGadget;
1517
+ var AbstractGadget;
1287
1518
  var init_gadget = __esm({
1288
1519
  "src/gadgets/gadget.ts"() {
1289
1520
  "use strict";
@@ -1291,7 +1522,7 @@ var init_gadget = __esm({
1291
1522
  init_exceptions();
1292
1523
  init_schema_to_json();
1293
1524
  init_schema_validator();
1294
- BaseGadget = class {
1525
+ AbstractGadget = class {
1295
1526
  /**
1296
1527
  * The name of the gadget. Used for identification when LLM calls it.
1297
1528
  * If not provided, defaults to the class name.
@@ -1319,14 +1550,14 @@ var init_gadget = __esm({
1319
1550
  */
1320
1551
  examples;
1321
1552
  /**
1322
- * Throws an AbortError if the execution has been aborted.
1553
+ * Throws an AbortException if the execution has been aborted.
1323
1554
  *
1324
1555
  * Call this at key checkpoints in long-running gadgets to allow early exit
1325
1556
  * when the gadget has been cancelled (e.g., due to timeout). This enables
1326
1557
  * resource cleanup and prevents unnecessary work after cancellation.
1327
1558
  *
1328
1559
  * @param ctx - The execution context containing the abort signal
1329
- * @throws AbortError if ctx.signal.aborted is true
1560
+ * @throws AbortException if ctx.signal.aborted is true
1330
1561
  *
1331
1562
  * @example
1332
1563
  * ```typescript
@@ -1351,7 +1582,7 @@ var init_gadget = __esm({
1351
1582
  */
1352
1583
  throwIfAborted(ctx) {
1353
1584
  if (ctx?.signal?.aborted) {
1354
- throw new AbortError();
1585
+ throw new AbortException();
1355
1586
  }
1356
1587
  }
1357
1588
  /**
@@ -1492,7 +1723,9 @@ var init_gadget = __esm({
1492
1723
  parts.push(`# ${example.comment}`);
1493
1724
  }
1494
1725
  parts.push(`${effectiveStartPrefix}${gadgetName}`);
1495
- parts.push(formatParamsAsBlock(example.params, "", effectiveArgPrefix));
1726
+ parts.push(
1727
+ formatParamsForBlockExample(example.params, "", effectiveArgPrefix)
1728
+ );
1496
1729
  parts.push(effectiveEndPrefix);
1497
1730
  if (example.output !== void 0) {
1498
1731
  parts.push("");
@@ -1509,7 +1742,7 @@ var init_gadget = __esm({
1509
1742
 
1510
1743
  // src/gadgets/create-gadget.ts
1511
1744
  function createGadget(config) {
1512
- class DynamicGadget extends BaseGadget {
1745
+ class DynamicGadget extends AbstractGadget {
1513
1746
  name = config.name;
1514
1747
  description = config.description;
1515
1748
  parameterSchema = config.schema;
@@ -1694,6 +1927,18 @@ var init_output_viewer = __esm({
1694
1927
  }
1695
1928
  });
1696
1929
 
1930
+ // src/agent/agent-internal-key.ts
1931
+ function isValidAgentKey(key) {
1932
+ return key === AGENT_INTERNAL_KEY;
1933
+ }
1934
+ var AGENT_INTERNAL_KEY;
1935
+ var init_agent_internal_key = __esm({
1936
+ "src/agent/agent-internal-key.ts"() {
1937
+ "use strict";
1938
+ AGENT_INTERNAL_KEY = Symbol("AGENT_INTERNAL_KEY");
1939
+ }
1940
+ });
1941
+
1697
1942
  // src/agent/compaction/config.ts
1698
1943
  function resolveCompactionConfig(config = {}) {
1699
1944
  const trigger = config.triggerThresholdPercent ?? DEFAULT_COMPACTION_CONFIG.triggerThresholdPercent;
@@ -1941,9 +2186,9 @@ var init_hybrid = __esm({
1941
2186
  var init_strategies = __esm({
1942
2187
  "src/agent/compaction/strategies/index.ts"() {
1943
2188
  "use strict";
2189
+ init_hybrid();
1944
2190
  init_sliding_window();
1945
2191
  init_summarization();
1946
- init_hybrid();
1947
2192
  }
1948
2193
  });
1949
2194
 
@@ -2105,98 +2350,6 @@ var init_manager = __esm({
2105
2350
  }
2106
2351
  });
2107
2352
 
2108
- // src/agent/gadget-output-store.ts
2109
- var import_node_crypto, GadgetOutputStore;
2110
- var init_gadget_output_store = __esm({
2111
- "src/agent/gadget-output-store.ts"() {
2112
- "use strict";
2113
- import_node_crypto = require("crypto");
2114
- GadgetOutputStore = class {
2115
- outputs = /* @__PURE__ */ new Map();
2116
- /**
2117
- * Store a gadget output and return its ID.
2118
- *
2119
- * @param gadgetName - Name of the gadget that produced the output
2120
- * @param content - Full output content to store
2121
- * @returns Generated ID for retrieving the output later
2122
- */
2123
- store(gadgetName, content) {
2124
- const id = this.generateId(gadgetName);
2125
- const encoder = new TextEncoder();
2126
- const stored = {
2127
- id,
2128
- gadgetName,
2129
- content,
2130
- byteSize: encoder.encode(content).length,
2131
- lineCount: content.split("\n").length,
2132
- timestamp: /* @__PURE__ */ new Date()
2133
- };
2134
- this.outputs.set(id, stored);
2135
- return id;
2136
- }
2137
- /**
2138
- * Retrieve a stored output by ID.
2139
- *
2140
- * @param id - The output ID (e.g., "Search_d34db33f")
2141
- * @returns The stored output or undefined if not found
2142
- */
2143
- get(id) {
2144
- return this.outputs.get(id);
2145
- }
2146
- /**
2147
- * Check if an output exists.
2148
- *
2149
- * @param id - The output ID to check
2150
- * @returns True if the output exists
2151
- */
2152
- has(id) {
2153
- return this.outputs.has(id);
2154
- }
2155
- /**
2156
- * Get all stored output IDs.
2157
- *
2158
- * @returns Array of output IDs
2159
- */
2160
- getIds() {
2161
- return Array.from(this.outputs.keys());
2162
- }
2163
- /**
2164
- * Get the number of stored outputs.
2165
- */
2166
- get size() {
2167
- return this.outputs.size;
2168
- }
2169
- /**
2170
- * Clear all stored outputs.
2171
- * Called when the agent run completes.
2172
- */
2173
- clear() {
2174
- this.outputs.clear();
2175
- }
2176
- /**
2177
- * Generate a unique ID for a stored output.
2178
- * Format: {GadgetName}_{8 hex chars}
2179
- */
2180
- generateId(gadgetName) {
2181
- const hex = (0, import_node_crypto.randomBytes)(4).toString("hex");
2182
- return `${gadgetName}_${hex}`;
2183
- }
2184
- };
2185
- }
2186
- });
2187
-
2188
- // src/agent/agent-internal-key.ts
2189
- function isValidAgentKey(key) {
2190
- return key === AGENT_INTERNAL_KEY;
2191
- }
2192
- var AGENT_INTERNAL_KEY;
2193
- var init_agent_internal_key = __esm({
2194
- "src/agent/agent-internal-key.ts"() {
2195
- "use strict";
2196
- AGENT_INTERNAL_KEY = Symbol("AGENT_INTERNAL_KEY");
2197
- }
2198
- });
2199
-
2200
2353
  // src/agent/conversation-manager.ts
2201
2354
  var ConversationManager;
2202
2355
  var init_conversation_manager = __esm({
@@ -2227,8 +2380,8 @@ var init_conversation_manager = __esm({
2227
2380
  addAssistantMessage(content) {
2228
2381
  this.historyBuilder.addAssistant(content);
2229
2382
  }
2230
- addGadgetCall(gadgetName, parameters, result) {
2231
- this.historyBuilder.addGadgetCall(gadgetName, parameters, result);
2383
+ addGadgetCallResult(gadgetName, parameters, result, media, mediaIds) {
2384
+ this.historyBuilder.addGadgetCallResult(gadgetName, parameters, result, media, mediaIds);
2232
2385
  }
2233
2386
  getMessages() {
2234
2387
  return [...this.baseMessages, ...this.initialMessages, ...this.historyBuilder.build()];
@@ -2248,7 +2401,7 @@ var init_conversation_manager = __esm({
2248
2401
  if (msg.role === "user") {
2249
2402
  this.historyBuilder.addUser(msg.content);
2250
2403
  } else if (msg.role === "assistant") {
2251
- this.historyBuilder.addAssistant(extractText(msg.content));
2404
+ this.historyBuilder.addAssistant(extractMessageText(msg.content));
2252
2405
  }
2253
2406
  }
2254
2407
  }
@@ -2342,6 +2495,86 @@ var init_event_handlers = __esm({
2342
2495
  }
2343
2496
  });
2344
2497
 
2498
+ // src/agent/gadget-output-store.ts
2499
+ var import_node_crypto2, GadgetOutputStore;
2500
+ var init_gadget_output_store = __esm({
2501
+ "src/agent/gadget-output-store.ts"() {
2502
+ "use strict";
2503
+ import_node_crypto2 = require("crypto");
2504
+ GadgetOutputStore = class {
2505
+ outputs = /* @__PURE__ */ new Map();
2506
+ /**
2507
+ * Store a gadget output and return its ID.
2508
+ *
2509
+ * @param gadgetName - Name of the gadget that produced the output
2510
+ * @param content - Full output content to store
2511
+ * @returns Generated ID for retrieving the output later
2512
+ */
2513
+ store(gadgetName, content) {
2514
+ const id = this.generateId(gadgetName);
2515
+ const encoder = new TextEncoder();
2516
+ const stored = {
2517
+ id,
2518
+ gadgetName,
2519
+ content,
2520
+ byteSize: encoder.encode(content).length,
2521
+ lineCount: content.split("\n").length,
2522
+ timestamp: /* @__PURE__ */ new Date()
2523
+ };
2524
+ this.outputs.set(id, stored);
2525
+ return id;
2526
+ }
2527
+ /**
2528
+ * Retrieve a stored output by ID.
2529
+ *
2530
+ * @param id - The output ID (e.g., "Search_d34db33f")
2531
+ * @returns The stored output or undefined if not found
2532
+ */
2533
+ get(id) {
2534
+ return this.outputs.get(id);
2535
+ }
2536
+ /**
2537
+ * Check if an output exists.
2538
+ *
2539
+ * @param id - The output ID to check
2540
+ * @returns True if the output exists
2541
+ */
2542
+ has(id) {
2543
+ return this.outputs.has(id);
2544
+ }
2545
+ /**
2546
+ * Get all stored output IDs.
2547
+ *
2548
+ * @returns Array of output IDs
2549
+ */
2550
+ getIds() {
2551
+ return Array.from(this.outputs.keys());
2552
+ }
2553
+ /**
2554
+ * Get the number of stored outputs.
2555
+ */
2556
+ get size() {
2557
+ return this.outputs.size;
2558
+ }
2559
+ /**
2560
+ * Clear all stored outputs.
2561
+ * Called when the agent run completes.
2562
+ */
2563
+ clear() {
2564
+ this.outputs.clear();
2565
+ }
2566
+ /**
2567
+ * Generate a unique ID for a stored output.
2568
+ * Format: {GadgetName}_{8 hex chars}
2569
+ */
2570
+ generateId(gadgetName) {
2571
+ const hex = (0, import_node_crypto2.randomBytes)(4).toString("hex");
2572
+ return `${gadgetName}_${hex}`;
2573
+ }
2574
+ };
2575
+ }
2576
+ });
2577
+
2345
2578
  // src/agent/hook-validators.ts
2346
2579
  function validateBeforeLLMCallAction(action) {
2347
2580
  if (!action || typeof action !== "object" || !("action" in action)) {
@@ -2652,8 +2885,7 @@ var init_schema_introspector = __esm({
2652
2885
  const values = def?.values;
2653
2886
  const value = values?.[0] ?? def?.value;
2654
2887
  if (typeof value === "string") return "string";
2655
- if (typeof value === "number" || typeof value === "bigint")
2656
- return "number";
2888
+ if (typeof value === "number" || typeof value === "bigint") return "number";
2657
2889
  if (typeof value === "boolean") return "boolean";
2658
2890
  return "unknown";
2659
2891
  }
@@ -2925,7 +3157,13 @@ var init_cost_reporting_client = __esm({
2925
3157
  cacheCreationInputTokens = chunk.usage.cacheCreationInputTokens ?? 0;
2926
3158
  }
2927
3159
  }
2928
- this.reportCostFromUsage(model, inputTokens, outputTokens, cachedInputTokens, cacheCreationInputTokens);
3160
+ this.reportCostFromUsage(
3161
+ model,
3162
+ inputTokens,
3163
+ outputTokens,
3164
+ cachedInputTokens,
3165
+ cacheCreationInputTokens
3166
+ );
2929
3167
  return result;
2930
3168
  }
2931
3169
  /**
@@ -2965,7 +3203,13 @@ var init_cost_reporting_client = __esm({
2965
3203
  }
2966
3204
  }
2967
3205
  } finally {
2968
- this.reportCostFromUsage(model, inputTokens, outputTokens, cachedInputTokens, cacheCreationInputTokens);
3206
+ this.reportCostFromUsage(
3207
+ model,
3208
+ inputTokens,
3209
+ outputTokens,
3210
+ cachedInputTokens,
3211
+ cacheCreationInputTokens
3212
+ );
2969
3213
  }
2970
3214
  }
2971
3215
  /**
@@ -3003,7 +3247,13 @@ var init_cost_reporting_client = __esm({
3003
3247
  }
3004
3248
  } finally {
3005
3249
  if (inputTokens > 0 || outputTokens > 0) {
3006
- reportCostFromUsage(model, inputTokens, outputTokens, cachedInputTokens, cacheCreationInputTokens);
3250
+ reportCostFromUsage(
3251
+ model,
3252
+ inputTokens,
3253
+ outputTokens,
3254
+ cachedInputTokens,
3255
+ cacheCreationInputTokens
3256
+ );
3007
3257
  }
3008
3258
  }
3009
3259
  }
@@ -3031,12 +3281,12 @@ var init_cost_reporting_client = __esm({
3031
3281
  });
3032
3282
 
3033
3283
  // src/gadgets/error-formatter.ts
3034
- var GadgetErrorFormatter;
3284
+ var GadgetExecutionErrorFormatter;
3035
3285
  var init_error_formatter = __esm({
3036
3286
  "src/gadgets/error-formatter.ts"() {
3037
3287
  "use strict";
3038
3288
  init_constants();
3039
- GadgetErrorFormatter = class {
3289
+ GadgetExecutionErrorFormatter = class {
3040
3290
  argPrefix;
3041
3291
  startPrefix;
3042
3292
  endPrefix;
@@ -3122,16 +3372,16 @@ function stripMarkdownFences(content) {
3122
3372
  cleaned = cleaned.replace(closingFence, "");
3123
3373
  return cleaned.trim();
3124
3374
  }
3125
- var globalInvocationCounter, StreamParser;
3375
+ var globalInvocationCounter, GadgetCallParser;
3126
3376
  var init_parser = __esm({
3127
3377
  "src/gadgets/parser.ts"() {
3128
3378
  "use strict";
3129
3379
  init_constants();
3130
3380
  init_block_params();
3131
3381
  globalInvocationCounter = 0;
3132
- StreamParser = class {
3382
+ GadgetCallParser = class {
3133
3383
  buffer = "";
3134
- lastReportedTextLength = 0;
3384
+ lastEmittedTextOffset = 0;
3135
3385
  startPrefix;
3136
3386
  endPrefix;
3137
3387
  argPrefix;
@@ -3140,16 +3390,20 @@ var init_parser = __esm({
3140
3390
  this.endPrefix = options.endPrefix ?? GADGET_END_PREFIX;
3141
3391
  this.argPrefix = options.argPrefix ?? GADGET_ARG_PREFIX;
3142
3392
  }
3143
- takeTextUntil(index) {
3144
- if (index <= this.lastReportedTextLength) {
3393
+ /**
3394
+ * Extract and consume text up to the given index.
3395
+ * Returns undefined if no meaningful text to emit.
3396
+ */
3397
+ extractTextSegment(index) {
3398
+ if (index <= this.lastEmittedTextOffset) {
3145
3399
  return void 0;
3146
3400
  }
3147
- const segment = this.buffer.slice(this.lastReportedTextLength, index);
3148
- this.lastReportedTextLength = index;
3401
+ const segment = this.buffer.slice(this.lastEmittedTextOffset, index);
3402
+ this.lastEmittedTextOffset = index;
3149
3403
  return segment.trim().length > 0 ? segment : void 0;
3150
3404
  }
3151
3405
  /**
3152
- * Parse gadget name with optional invocation ID and dependencies.
3406
+ * Parse gadget invocation metadata from the header line.
3153
3407
  *
3154
3408
  * Supported formats:
3155
3409
  * - `GadgetName` - Auto-generate ID, no dependencies
@@ -3158,24 +3412,24 @@ var init_parser = __esm({
3158
3412
  *
3159
3413
  * Dependencies must be comma-separated invocation IDs.
3160
3414
  */
3161
- parseGadgetName(gadgetName) {
3162
- const parts = gadgetName.split(":");
3415
+ parseInvocationMetadata(headerLine) {
3416
+ const parts = headerLine.split(":");
3163
3417
  if (parts.length === 1) {
3164
3418
  return {
3165
- actualName: parts[0],
3419
+ gadgetName: parts[0],
3166
3420
  invocationId: `gadget_${++globalInvocationCounter}`,
3167
3421
  dependencies: []
3168
3422
  };
3169
3423
  } else if (parts.length === 2) {
3170
3424
  return {
3171
- actualName: parts[0],
3425
+ gadgetName: parts[0],
3172
3426
  invocationId: parts[1].trim(),
3173
3427
  dependencies: []
3174
3428
  };
3175
3429
  } else {
3176
3430
  const deps = parts[2].split(",").map((d) => d.trim()).filter((d) => d.length > 0);
3177
3431
  return {
3178
- actualName: parts[0],
3432
+ gadgetName: parts[0],
3179
3433
  invocationId: parts[1].trim(),
3180
3434
  dependencies: deps
3181
3435
  };
@@ -3207,15 +3461,15 @@ var init_parser = __esm({
3207
3461
  while (true) {
3208
3462
  const partStartIndex = this.buffer.indexOf(this.startPrefix, startIndex);
3209
3463
  if (partStartIndex === -1) break;
3210
- const textBefore = this.takeTextUntil(partStartIndex);
3464
+ const textBefore = this.extractTextSegment(partStartIndex);
3211
3465
  if (textBefore !== void 0) {
3212
3466
  yield { type: "text", content: textBefore };
3213
3467
  }
3214
3468
  const metadataStartIndex = partStartIndex + this.startPrefix.length;
3215
3469
  const metadataEndIndex = this.buffer.indexOf("\n", metadataStartIndex);
3216
3470
  if (metadataEndIndex === -1) break;
3217
- const gadgetName = this.buffer.substring(metadataStartIndex, metadataEndIndex).trim();
3218
- const { actualName: actualGadgetName, invocationId, dependencies } = this.parseGadgetName(gadgetName);
3471
+ const headerLine = this.buffer.substring(metadataStartIndex, metadataEndIndex).trim();
3472
+ const { gadgetName, invocationId, dependencies } = this.parseInvocationMetadata(headerLine);
3219
3473
  const contentStartIndex = metadataEndIndex + 1;
3220
3474
  let partEndIndex;
3221
3475
  let endMarkerLength = 0;
@@ -3235,7 +3489,7 @@ var init_parser = __esm({
3235
3489
  yield {
3236
3490
  type: "gadget_call",
3237
3491
  call: {
3238
- gadgetName: actualGadgetName,
3492
+ gadgetName,
3239
3493
  invocationId,
3240
3494
  parametersRaw,
3241
3495
  parameters,
@@ -3244,33 +3498,33 @@ var init_parser = __esm({
3244
3498
  }
3245
3499
  };
3246
3500
  startIndex = partEndIndex + endMarkerLength;
3247
- this.lastReportedTextLength = startIndex;
3501
+ this.lastEmittedTextOffset = startIndex;
3248
3502
  }
3249
3503
  if (startIndex > 0) {
3250
3504
  this.buffer = this.buffer.substring(startIndex);
3251
- this.lastReportedTextLength = 0;
3505
+ this.lastEmittedTextOffset = 0;
3252
3506
  }
3253
3507
  }
3254
3508
  // Finalize parsing and return remaining text or incomplete gadgets
3255
3509
  *finalize() {
3256
- const startIndex = this.buffer.indexOf(this.startPrefix, this.lastReportedTextLength);
3510
+ const startIndex = this.buffer.indexOf(this.startPrefix, this.lastEmittedTextOffset);
3257
3511
  if (startIndex !== -1) {
3258
- const textBefore = this.takeTextUntil(startIndex);
3512
+ const textBefore = this.extractTextSegment(startIndex);
3259
3513
  if (textBefore !== void 0) {
3260
3514
  yield { type: "text", content: textBefore };
3261
3515
  }
3262
3516
  const metadataStartIndex = startIndex + this.startPrefix.length;
3263
3517
  const metadataEndIndex = this.buffer.indexOf("\n", metadataStartIndex);
3264
3518
  if (metadataEndIndex !== -1) {
3265
- const gadgetName = this.buffer.substring(metadataStartIndex, metadataEndIndex).trim();
3266
- const { actualName: actualGadgetName, invocationId, dependencies } = this.parseGadgetName(gadgetName);
3519
+ const headerLine = this.buffer.substring(metadataStartIndex, metadataEndIndex).trim();
3520
+ const { gadgetName, invocationId, dependencies } = this.parseInvocationMetadata(headerLine);
3267
3521
  const contentStartIndex = metadataEndIndex + 1;
3268
3522
  const parametersRaw = this.buffer.substring(contentStartIndex).trim();
3269
3523
  const { parameters, parseError } = this.parseParameters(parametersRaw);
3270
3524
  yield {
3271
3525
  type: "gadget_call",
3272
3526
  call: {
3273
- gadgetName: actualGadgetName,
3527
+ gadgetName,
3274
3528
  invocationId,
3275
3529
  parametersRaw,
3276
3530
  parameters,
@@ -3281,7 +3535,7 @@ var init_parser = __esm({
3281
3535
  return;
3282
3536
  }
3283
3537
  }
3284
- const remainingText = this.takeTextUntil(this.buffer.length);
3538
+ const remainingText = this.extractTextSegment(this.buffer.length);
3285
3539
  if (remainingText !== void 0) {
3286
3540
  yield { type: "text", content: remainingText };
3287
3541
  }
@@ -3289,7 +3543,7 @@ var init_parser = __esm({
3289
3543
  // Reset parser state (note: global invocation counter is NOT reset to ensure unique IDs)
3290
3544
  reset() {
3291
3545
  this.buffer = "";
3292
- this.lastReportedTextLength = 0;
3546
+ this.lastEmittedTextOffset = 0;
3293
3547
  }
3294
3548
  };
3295
3549
  }
@@ -3308,13 +3562,16 @@ var init_executor = __esm({
3308
3562
  init_exceptions();
3309
3563
  init_parser();
3310
3564
  GadgetExecutor = class {
3311
- constructor(registry, onHumanInputRequired, logger, defaultGadgetTimeoutMs, errorFormatterOptions, client) {
3565
+ constructor(registry, requestHumanInput, logger, defaultGadgetTimeoutMs, errorFormatterOptions, client, mediaStore, agentConfig, subagentConfig) {
3312
3566
  this.registry = registry;
3313
- this.onHumanInputRequired = onHumanInputRequired;
3567
+ this.requestHumanInput = requestHumanInput;
3314
3568
  this.defaultGadgetTimeoutMs = defaultGadgetTimeoutMs;
3315
3569
  this.client = client;
3570
+ this.mediaStore = mediaStore;
3571
+ this.agentConfig = agentConfig;
3572
+ this.subagentConfig = subagentConfig;
3316
3573
  this.logger = logger ?? createLogger({ name: "llmist:executor" });
3317
- this.errorFormatter = new GadgetErrorFormatter(errorFormatterOptions);
3574
+ this.errorFormatter = new GadgetExecutionErrorFormatter(errorFormatterOptions);
3318
3575
  this.argPrefix = errorFormatterOptions?.argPrefix ?? GADGET_ARG_PREFIX;
3319
3576
  }
3320
3577
  logger;
@@ -3334,13 +3591,17 @@ var init_executor = __esm({
3334
3591
  });
3335
3592
  }
3336
3593
  /**
3337
- * Normalizes gadget execute result to consistent format.
3338
- * Handles both string returns (backwards compat) and object returns with cost.
3594
+ * Unify gadget execute result to consistent internal format.
3595
+ * Handles string returns (backwards compat), object returns with cost,
3596
+ * and object returns with media.
3339
3597
  */
3340
- normalizeExecuteResult(raw) {
3598
+ unifyExecuteResult(raw) {
3341
3599
  if (typeof raw === "string") {
3342
3600
  return { result: raw, cost: 0 };
3343
3601
  }
3602
+ if ("media" in raw && raw.media) {
3603
+ return { result: raw.result, media: raw.media, cost: raw.cost ?? 0 };
3604
+ }
3344
3605
  return { result: raw.result, cost: raw.cost ?? 0 };
3345
3606
  }
3346
3607
  // Execute a gadget call asynchronously
@@ -3452,7 +3713,9 @@ var init_executor = __esm({
3452
3713
  const ctx = {
3453
3714
  reportCost,
3454
3715
  llmist: this.client ? new CostReportingLLMistWrapper(this.client, reportCost) : void 0,
3455
- signal: abortController.signal
3716
+ signal: abortController.signal,
3717
+ agentConfig: this.agentConfig,
3718
+ subagentConfig: this.subagentConfig
3456
3719
  };
3457
3720
  let rawResult;
3458
3721
  if (timeoutMs && timeoutMs > 0) {
@@ -3467,8 +3730,21 @@ var init_executor = __esm({
3467
3730
  } else {
3468
3731
  rawResult = await Promise.resolve(gadget.execute(validatedParameters, ctx));
3469
3732
  }
3470
- const { result, cost: returnCost } = this.normalizeExecuteResult(rawResult);
3733
+ const { result, media, cost: returnCost } = this.unifyExecuteResult(rawResult);
3471
3734
  const totalCost = callbackCost + returnCost;
3735
+ let mediaIds;
3736
+ let storedMedia;
3737
+ if (media && media.length > 0 && this.mediaStore) {
3738
+ storedMedia = await Promise.all(
3739
+ media.map((item) => this.mediaStore.store(item, call.gadgetName))
3740
+ );
3741
+ mediaIds = storedMedia.map((m) => m.id);
3742
+ this.logger.debug("Stored media outputs", {
3743
+ gadgetName: call.gadgetName,
3744
+ mediaIds,
3745
+ count: media.length
3746
+ });
3747
+ }
3472
3748
  const executionTimeMs = Date.now() - startTime;
3473
3749
  this.logger.info("Gadget executed successfully", {
3474
3750
  gadgetName: call.gadgetName,
@@ -3476,7 +3752,8 @@ var init_executor = __esm({
3476
3752
  executionTimeMs,
3477
3753
  cost: totalCost > 0 ? totalCost : void 0,
3478
3754
  callbackCost: callbackCost > 0 ? callbackCost : void 0,
3479
- returnCost: returnCost > 0 ? returnCost : void 0
3755
+ returnCost: returnCost > 0 ? returnCost : void 0,
3756
+ mediaCount: media?.length
3480
3757
  });
3481
3758
  this.logger.debug("Gadget result", {
3482
3759
  gadgetName: call.gadgetName,
@@ -3484,7 +3761,8 @@ var init_executor = __esm({
3484
3761
  parameters: validatedParameters,
3485
3762
  result,
3486
3763
  cost: totalCost,
3487
- executionTimeMs
3764
+ executionTimeMs,
3765
+ mediaIds
3488
3766
  });
3489
3767
  return {
3490
3768
  gadgetName: call.gadgetName,
@@ -3492,10 +3770,13 @@ var init_executor = __esm({
3492
3770
  parameters: validatedParameters,
3493
3771
  result,
3494
3772
  executionTimeMs,
3495
- cost: totalCost
3773
+ cost: totalCost,
3774
+ media,
3775
+ mediaIds,
3776
+ storedMedia
3496
3777
  };
3497
3778
  } catch (error) {
3498
- if (error instanceof BreakLoopException) {
3779
+ if (error instanceof TaskCompletionSignal) {
3499
3780
  this.logger.info("Gadget requested loop termination", {
3500
3781
  gadgetName: call.gadgetName,
3501
3782
  message: error.message
@@ -3523,7 +3804,7 @@ var init_executor = __esm({
3523
3804
  executionTimeMs: Date.now() - startTime
3524
3805
  };
3525
3806
  }
3526
- if (error instanceof AbortError) {
3807
+ if (error instanceof AbortException) {
3527
3808
  this.logger.info("Gadget execution was aborted", {
3528
3809
  gadgetName: call.gadgetName,
3529
3810
  executionTimeMs: Date.now() - startTime
@@ -3536,14 +3817,14 @@ var init_executor = __esm({
3536
3817
  executionTimeMs: Date.now() - startTime
3537
3818
  };
3538
3819
  }
3539
- if (error instanceof HumanInputException) {
3820
+ if (error instanceof HumanInputRequiredException) {
3540
3821
  this.logger.info("Gadget requested human input", {
3541
3822
  gadgetName: call.gadgetName,
3542
3823
  question: error.question
3543
3824
  });
3544
- if (this.onHumanInputRequired) {
3825
+ if (this.requestHumanInput) {
3545
3826
  try {
3546
- const answer = await this.onHumanInputRequired(error.question);
3827
+ const answer = await this.requestHumanInput(error.question);
3547
3828
  this.logger.debug("Human input received", {
3548
3829
  gadgetName: call.gadgetName,
3549
3830
  answerLength: answer.length
@@ -3641,13 +3922,13 @@ var init_stream_processor = __esm({
3641
3922
  parser;
3642
3923
  executor;
3643
3924
  stopOnGadgetError;
3644
- shouldContinueAfterError;
3645
- accumulatedText = "";
3646
- shouldStopExecution = false;
3925
+ canRecoverFromGadgetError;
3926
+ responseText = "";
3927
+ executionHalted = false;
3647
3928
  observerFailureCount = 0;
3648
3929
  // Dependency tracking for gadget execution DAG
3649
3930
  /** Gadgets waiting for their dependencies to complete */
3650
- pendingGadgets = /* @__PURE__ */ new Map();
3931
+ gadgetsAwaitingDependencies = /* @__PURE__ */ new Map();
3651
3932
  /** Completed gadget results, keyed by invocation ID */
3652
3933
  completedResults = /* @__PURE__ */ new Map();
3653
3934
  /** Invocation IDs of gadgets that have failed (error or skipped due to dependency) */
@@ -3658,19 +3939,22 @@ var init_stream_processor = __esm({
3658
3939
  this.hooks = options.hooks ?? {};
3659
3940
  this.logger = options.logger ?? createLogger({ name: "llmist:stream-processor" });
3660
3941
  this.stopOnGadgetError = options.stopOnGadgetError ?? true;
3661
- this.shouldContinueAfterError = options.shouldContinueAfterError;
3662
- this.parser = new StreamParser({
3942
+ this.canRecoverFromGadgetError = options.canRecoverFromGadgetError;
3943
+ this.parser = new GadgetCallParser({
3663
3944
  startPrefix: options.gadgetStartPrefix,
3664
3945
  endPrefix: options.gadgetEndPrefix,
3665
3946
  argPrefix: options.gadgetArgPrefix
3666
3947
  });
3667
3948
  this.executor = new GadgetExecutor(
3668
3949
  options.registry,
3669
- options.onHumanInputRequired,
3950
+ options.requestHumanInput,
3670
3951
  this.logger.getSubLogger({ name: "executor" }),
3671
3952
  options.defaultGadgetTimeoutMs,
3672
3953
  { argPrefix: options.gadgetArgPrefix },
3673
- options.client
3954
+ options.client,
3955
+ options.mediaStore,
3956
+ options.agentConfig,
3957
+ options.subagentConfig
3674
3958
  );
3675
3959
  }
3676
3960
  /**
@@ -3691,7 +3975,7 @@ var init_stream_processor = __esm({
3691
3975
  if (this.hooks.interceptors?.interceptRawChunk) {
3692
3976
  const context = {
3693
3977
  iteration: this.iteration,
3694
- accumulatedText: this.accumulatedText,
3978
+ accumulatedText: this.responseText,
3695
3979
  logger: this.logger
3696
3980
  };
3697
3981
  const intercepted = this.hooks.interceptors.interceptRawChunk(processedChunk, context);
@@ -3702,7 +3986,7 @@ var init_stream_processor = __esm({
3702
3986
  }
3703
3987
  }
3704
3988
  if (processedChunk) {
3705
- this.accumulatedText += processedChunk;
3989
+ this.responseText += processedChunk;
3706
3990
  }
3707
3991
  }
3708
3992
  if (this.hooks.observers?.onStreamChunk && (processedChunk || chunk.usage)) {
@@ -3711,7 +3995,7 @@ var init_stream_processor = __esm({
3711
3995
  const context = {
3712
3996
  iteration: this.iteration,
3713
3997
  rawChunk: processedChunk,
3714
- accumulatedText: this.accumulatedText,
3998
+ accumulatedText: this.responseText,
3715
3999
  usage,
3716
4000
  logger: this.logger
3717
4001
  };
@@ -3734,12 +4018,12 @@ var init_stream_processor = __esm({
3734
4018
  }
3735
4019
  }
3736
4020
  }
3737
- if (this.shouldStopExecution) {
4021
+ if (this.executionHalted) {
3738
4022
  this.logger.info("Breaking from LLM stream due to gadget error");
3739
4023
  break;
3740
4024
  }
3741
4025
  }
3742
- if (!this.shouldStopExecution) {
4026
+ if (!this.executionHalted) {
3743
4027
  for (const event of this.parser.finalize()) {
3744
4028
  const processedEvents = await this.processEvent(event);
3745
4029
  outputs.push(...processedEvents);
@@ -3763,11 +4047,11 @@ var init_stream_processor = __esm({
3763
4047
  }
3764
4048
  }
3765
4049
  }
3766
- let finalMessage = this.accumulatedText;
4050
+ let finalMessage = this.responseText;
3767
4051
  if (this.hooks.interceptors?.interceptAssistantMessage) {
3768
4052
  const context = {
3769
4053
  iteration: this.iteration,
3770
- rawResponse: this.accumulatedText,
4054
+ rawResponse: this.responseText,
3771
4055
  logger: this.logger
3772
4056
  };
3773
4057
  finalMessage = this.hooks.interceptors.interceptAssistantMessage(finalMessage, context);
@@ -3778,7 +4062,7 @@ var init_stream_processor = __esm({
3778
4062
  didExecuteGadgets,
3779
4063
  finishReason,
3780
4064
  usage,
3781
- rawResponse: this.accumulatedText,
4065
+ rawResponse: this.responseText,
3782
4066
  finalMessage
3783
4067
  };
3784
4068
  }
@@ -3801,7 +4085,7 @@ var init_stream_processor = __esm({
3801
4085
  if (this.hooks.interceptors?.interceptTextChunk) {
3802
4086
  const context = {
3803
4087
  iteration: this.iteration,
3804
- accumulatedText: this.accumulatedText,
4088
+ accumulatedText: this.responseText,
3805
4089
  logger: this.logger
3806
4090
  };
3807
4091
  const intercepted = this.hooks.interceptors.interceptTextChunk(content, context);
@@ -3820,7 +4104,7 @@ var init_stream_processor = __esm({
3820
4104
  * After each execution, pending gadgets are checked to see if they can now run.
3821
4105
  */
3822
4106
  async processGadgetCall(call) {
3823
- if (this.shouldStopExecution) {
4107
+ if (this.executionHalted) {
3824
4108
  this.logger.debug("Skipping gadget execution due to previous error", {
3825
4109
  gadgetName: call.gadgetName
3826
4110
  });
@@ -3859,7 +4143,7 @@ var init_stream_processor = __esm({
3859
4143
  invocationId: call.invocationId,
3860
4144
  waitingOn: unsatisfied
3861
4145
  });
3862
- this.pendingGadgets.set(call.invocationId, call);
4146
+ this.gadgetsAwaitingDependencies.set(call.invocationId, call);
3863
4147
  return events;
3864
4148
  }
3865
4149
  }
@@ -3881,14 +4165,14 @@ var init_stream_processor = __esm({
3881
4165
  error: call.parseError,
3882
4166
  rawParameters: call.parametersRaw
3883
4167
  });
3884
- const shouldContinue = await this.checkContinueAfterError(
4168
+ const shouldContinue = await this.checkCanRecoverFromError(
3885
4169
  call.parseError,
3886
4170
  call.gadgetName,
3887
4171
  "parse",
3888
4172
  call.parameters
3889
4173
  );
3890
4174
  if (!shouldContinue) {
3891
- this.shouldStopExecution = true;
4175
+ this.executionHalted = true;
3892
4176
  }
3893
4177
  }
3894
4178
  let parameters = call.parameters ?? {};
@@ -4012,14 +4296,14 @@ var init_stream_processor = __esm({
4012
4296
  events.push({ type: "gadget_result", result });
4013
4297
  if (result.error) {
4014
4298
  const errorType = this.determineErrorType(call, result);
4015
- const shouldContinue = await this.checkContinueAfterError(
4299
+ const shouldContinue = await this.checkCanRecoverFromError(
4016
4300
  result.error,
4017
4301
  result.gadgetName,
4018
4302
  errorType,
4019
4303
  result.parameters
4020
4304
  );
4021
4305
  if (!shouldContinue) {
4022
- this.shouldStopExecution = true;
4306
+ this.executionHalted = true;
4023
4307
  }
4024
4308
  }
4025
4309
  return events;
@@ -4106,11 +4390,11 @@ var init_stream_processor = __esm({
4106
4390
  async processPendingGadgets() {
4107
4391
  const events = [];
4108
4392
  let progress = true;
4109
- while (progress && this.pendingGadgets.size > 0) {
4393
+ while (progress && this.gadgetsAwaitingDependencies.size > 0) {
4110
4394
  progress = false;
4111
4395
  const readyToExecute = [];
4112
4396
  const readyToSkip = [];
4113
- for (const [invocationId, call] of this.pendingGadgets) {
4397
+ for (const [invocationId, call] of this.gadgetsAwaitingDependencies) {
4114
4398
  const failedDep = call.dependencies.find((dep) => this.failedInvocations.has(dep));
4115
4399
  if (failedDep) {
4116
4400
  readyToSkip.push({ call, failedDep });
@@ -4122,7 +4406,7 @@ var init_stream_processor = __esm({
4122
4406
  }
4123
4407
  }
4124
4408
  for (const { call, failedDep } of readyToSkip) {
4125
- this.pendingGadgets.delete(call.invocationId);
4409
+ this.gadgetsAwaitingDependencies.delete(call.invocationId);
4126
4410
  const skipEvents = await this.handleFailedDependency(call, failedDep);
4127
4411
  events.push(...skipEvents);
4128
4412
  progress = true;
@@ -4133,7 +4417,7 @@ var init_stream_processor = __esm({
4133
4417
  invocationIds: readyToExecute.map((c) => c.invocationId)
4134
4418
  });
4135
4419
  for (const call of readyToExecute) {
4136
- this.pendingGadgets.delete(call.invocationId);
4420
+ this.gadgetsAwaitingDependencies.delete(call.invocationId);
4137
4421
  }
4138
4422
  const executePromises = readyToExecute.map((call) => this.executeGadgetWithHooks(call));
4139
4423
  const results = await Promise.all(executePromises);
@@ -4143,9 +4427,9 @@ var init_stream_processor = __esm({
4143
4427
  progress = true;
4144
4428
  }
4145
4429
  }
4146
- if (this.pendingGadgets.size > 0) {
4147
- const pendingIds = new Set(this.pendingGadgets.keys());
4148
- for (const [invocationId, call] of this.pendingGadgets) {
4430
+ if (this.gadgetsAwaitingDependencies.size > 0) {
4431
+ const pendingIds = new Set(this.gadgetsAwaitingDependencies.keys());
4432
+ for (const [invocationId, call] of this.gadgetsAwaitingDependencies) {
4149
4433
  const missingDeps = call.dependencies.filter((dep) => !this.completedResults.has(dep));
4150
4434
  const circularDeps = missingDeps.filter((dep) => pendingIds.has(dep));
4151
4435
  const trulyMissingDeps = missingDeps.filter((dep) => !pendingIds.has(dep));
@@ -4176,7 +4460,7 @@ var init_stream_processor = __esm({
4176
4460
  };
4177
4461
  events.push(skipEvent);
4178
4462
  }
4179
- this.pendingGadgets.clear();
4463
+ this.gadgetsAwaitingDependencies.clear();
4180
4464
  }
4181
4465
  return events;
4182
4466
  }
@@ -4206,19 +4490,19 @@ var init_stream_processor = __esm({
4206
4490
  );
4207
4491
  }
4208
4492
  /**
4209
- * Check if execution should continue after an error.
4493
+ * Check if execution can recover from an error.
4210
4494
  *
4211
4495
  * Returns true if we should continue processing subsequent gadgets, false if we should stop.
4212
4496
  *
4213
4497
  * Logic:
4214
- * - If custom shouldContinueAfterError is provided, use it
4498
+ * - If custom canRecoverFromGadgetError is provided, use it
4215
4499
  * - Otherwise, use stopOnGadgetError config:
4216
4500
  * - stopOnGadgetError=true → return false (stop execution)
4217
4501
  * - stopOnGadgetError=false → return true (continue execution)
4218
4502
  */
4219
- async checkContinueAfterError(error, gadgetName, errorType, parameters) {
4220
- if (this.shouldContinueAfterError) {
4221
- return await this.shouldContinueAfterError({
4503
+ async checkCanRecoverFromError(error, gadgetName, errorType, parameters) {
4504
+ if (this.canRecoverFromGadgetError) {
4505
+ return await this.canRecoverFromGadgetError({
4222
4506
  error,
4223
4507
  gadgetName,
4224
4508
  errorType,
@@ -4259,13 +4543,14 @@ var init_agent = __esm({
4259
4543
  init_constants();
4260
4544
  init_messages();
4261
4545
  init_model_shortcuts();
4546
+ init_media_store();
4262
4547
  init_output_viewer();
4263
4548
  init_logger();
4264
- init_manager();
4265
- init_gadget_output_store();
4266
4549
  init_agent_internal_key();
4550
+ init_manager();
4267
4551
  init_conversation_manager();
4268
4552
  init_event_handlers();
4553
+ init_gadget_output_store();
4269
4554
  init_hook_validators();
4270
4555
  init_stream_processor();
4271
4556
  Agent = class {
@@ -4280,22 +4565,27 @@ var init_agent = __esm({
4280
4565
  gadgetStartPrefix;
4281
4566
  gadgetEndPrefix;
4282
4567
  gadgetArgPrefix;
4283
- onHumanInputRequired;
4568
+ requestHumanInput;
4284
4569
  textOnlyHandler;
4285
4570
  textWithGadgetsHandler;
4286
4571
  stopOnGadgetError;
4287
- shouldContinueAfterError;
4572
+ canRecoverFromGadgetError;
4288
4573
  defaultGadgetTimeoutMs;
4289
4574
  defaultMaxTokens;
4290
- userPromptProvided;
4575
+ hasUserPrompt;
4291
4576
  // Gadget output limiting
4292
4577
  outputStore;
4293
4578
  outputLimitEnabled;
4294
4579
  outputLimitCharLimit;
4295
4580
  // Context compaction
4296
4581
  compactionManager;
4582
+ // Media storage (for gadgets returning images, audio, etc.)
4583
+ mediaStore;
4297
4584
  // Cancellation
4298
4585
  signal;
4586
+ // Subagent configuration
4587
+ agentContextConfig;
4588
+ subagentConfig;
4299
4589
  /**
4300
4590
  * Creates a new Agent instance.
4301
4591
  * @internal This constructor is private. Use LLMist.createAgent() or AgentBuilder instead.
@@ -4315,15 +4605,16 @@ var init_agent = __esm({
4315
4605
  this.gadgetStartPrefix = options.gadgetStartPrefix;
4316
4606
  this.gadgetEndPrefix = options.gadgetEndPrefix;
4317
4607
  this.gadgetArgPrefix = options.gadgetArgPrefix;
4318
- this.onHumanInputRequired = options.onHumanInputRequired;
4608
+ this.requestHumanInput = options.requestHumanInput;
4319
4609
  this.textOnlyHandler = options.textOnlyHandler ?? "terminate";
4320
4610
  this.textWithGadgetsHandler = options.textWithGadgetsHandler;
4321
4611
  this.stopOnGadgetError = options.stopOnGadgetError ?? true;
4322
- this.shouldContinueAfterError = options.shouldContinueAfterError;
4612
+ this.canRecoverFromGadgetError = options.canRecoverFromGadgetError;
4323
4613
  this.defaultGadgetTimeoutMs = options.defaultGadgetTimeoutMs;
4324
4614
  this.defaultMaxTokens = this.resolveMaxTokensFromCatalog(options.model);
4325
4615
  this.outputLimitEnabled = options.gadgetOutputLimit ?? DEFAULT_GADGET_OUTPUT_LIMIT;
4326
4616
  this.outputStore = new GadgetOutputStore();
4617
+ this.mediaStore = new MediaStore();
4327
4618
  const limitPercent = options.gadgetOutputLimitPercent ?? DEFAULT_GADGET_OUTPUT_LIMIT_PERCENT;
4328
4619
  const limits = this.client.modelRegistry.getModelLimits(this.model);
4329
4620
  const contextWindow = limits?.contextWindow ?? FALLBACK_CONTEXT_WINDOW;
@@ -4334,7 +4625,7 @@ var init_agent = __esm({
4334
4625
  createGadgetOutputViewer(this.outputStore, this.outputLimitCharLimit)
4335
4626
  );
4336
4627
  }
4337
- this.hooks = this.mergeOutputLimiterHook(options.hooks);
4628
+ this.hooks = this.chainOutputLimiterWithUserHooks(options.hooks);
4338
4629
  const baseBuilder = new LLMMessageBuilder(options.promptConfig);
4339
4630
  if (options.systemPrompt) {
4340
4631
  baseBuilder.addSystem(options.systemPrompt);
@@ -4354,7 +4645,7 @@ var init_agent = __esm({
4354
4645
  endPrefix: options.gadgetEndPrefix,
4355
4646
  argPrefix: options.gadgetArgPrefix
4356
4647
  });
4357
- this.userPromptProvided = !!options.userPrompt;
4648
+ this.hasUserPrompt = !!options.userPrompt;
4358
4649
  if (options.userPrompt) {
4359
4650
  this.conversation.addUserMessage(options.userPrompt);
4360
4651
  }
@@ -4367,6 +4658,11 @@ var init_agent = __esm({
4367
4658
  );
4368
4659
  }
4369
4660
  this.signal = options.signal;
4661
+ this.agentContextConfig = {
4662
+ model: this.model,
4663
+ temperature: this.temperature
4664
+ };
4665
+ this.subagentConfig = options.subagentConfig;
4370
4666
  }
4371
4667
  /**
4372
4668
  * Get the gadget registry for this agent.
@@ -4389,6 +4685,36 @@ var init_agent = __esm({
4389
4685
  getRegistry() {
4390
4686
  return this.registry;
4391
4687
  }
4688
+ /**
4689
+ * Get the media store for this agent session.
4690
+ *
4691
+ * The media store holds all media outputs (images, audio, etc.) produced by gadgets
4692
+ * during this agent's execution. Use this to:
4693
+ * - Access stored media files by ID
4694
+ * - List all stored media
4695
+ * - Clean up temporary files after execution
4696
+ *
4697
+ * @returns The MediaStore instance for this agent
4698
+ *
4699
+ * @example
4700
+ * ```typescript
4701
+ * const agent = new AgentBuilder()
4702
+ * .withModel("sonnet")
4703
+ * .build();
4704
+ *
4705
+ * // After execution, access stored media
4706
+ * const store = agent.getMediaStore();
4707
+ * for (const media of store.list()) {
4708
+ * console.log(`${media.id}: ${media.path}`);
4709
+ * }
4710
+ *
4711
+ * // Clean up when done
4712
+ * await store.cleanup();
4713
+ * ```
4714
+ */
4715
+ getMediaStore() {
4716
+ return this.mediaStore;
4717
+ }
4392
4718
  /**
4393
4719
  * Manually trigger context compaction.
4394
4720
  *
@@ -4443,7 +4769,7 @@ var init_agent = __esm({
4443
4769
  * @throws {Error} If no user prompt was provided (when using build() without ask())
4444
4770
  */
4445
4771
  async *run() {
4446
- if (!this.userPromptProvided) {
4772
+ if (!this.hasUserPrompt) {
4447
4773
  throw new Error(
4448
4774
  "No user prompt provided. Use .ask(prompt) instead of .build(), or call agent.run() after providing a prompt."
4449
4775
  );
@@ -4560,11 +4886,14 @@ var init_agent = __esm({
4560
4886
  gadgetArgPrefix: this.gadgetArgPrefix,
4561
4887
  hooks: this.hooks,
4562
4888
  logger: this.logger.getSubLogger({ name: "stream-processor" }),
4563
- onHumanInputRequired: this.onHumanInputRequired,
4889
+ requestHumanInput: this.requestHumanInput,
4564
4890
  stopOnGadgetError: this.stopOnGadgetError,
4565
- shouldContinueAfterError: this.shouldContinueAfterError,
4891
+ canRecoverFromGadgetError: this.canRecoverFromGadgetError,
4566
4892
  defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
4567
- client: this.client
4893
+ client: this.client,
4894
+ mediaStore: this.mediaStore,
4895
+ agentConfig: this.agentContextConfig,
4896
+ subagentConfig: this.subagentConfig
4568
4897
  });
4569
4898
  const result = await processor.process(stream2);
4570
4899
  for (const output of result.outputs) {
@@ -4617,19 +4946,21 @@ var init_agent = __esm({
4617
4946
  if (msg.role === "user") {
4618
4947
  this.conversation.addUserMessage(msg.content);
4619
4948
  } else if (msg.role === "assistant") {
4620
- this.conversation.addAssistantMessage(extractText(msg.content));
4949
+ this.conversation.addAssistantMessage(extractMessageText(msg.content));
4621
4950
  } else if (msg.role === "system") {
4622
- this.conversation.addUserMessage(`[System] ${extractText(msg.content)}`);
4951
+ this.conversation.addUserMessage(`[System] ${extractMessageText(msg.content)}`);
4623
4952
  }
4624
4953
  }
4625
4954
  }
4626
4955
  }
4627
4956
  if (result.didExecuteGadgets) {
4628
4957
  if (this.textWithGadgetsHandler) {
4629
- const textContent = result.outputs.filter((output) => output.type === "text").map((output) => output.content).join("");
4958
+ const textContent = result.outputs.filter(
4959
+ (output) => output.type === "text"
4960
+ ).map((output) => output.content).join("");
4630
4961
  if (textContent.trim()) {
4631
4962
  const { gadgetName, parameterMapping, resultMapping } = this.textWithGadgetsHandler;
4632
- this.conversation.addGadgetCall(
4963
+ this.conversation.addGadgetCallResult(
4633
4964
  gadgetName,
4634
4965
  parameterMapping(textContent),
4635
4966
  resultMapping ? resultMapping(textContent) : textContent
@@ -4639,16 +4970,18 @@ var init_agent = __esm({
4639
4970
  for (const output of result.outputs) {
4640
4971
  if (output.type === "gadget_result") {
4641
4972
  const gadgetResult = output.result;
4642
- this.conversation.addGadgetCall(
4973
+ this.conversation.addGadgetCallResult(
4643
4974
  gadgetResult.gadgetName,
4644
4975
  gadgetResult.parameters,
4645
- gadgetResult.error ?? gadgetResult.result ?? ""
4976
+ gadgetResult.error ?? gadgetResult.result ?? "",
4977
+ gadgetResult.media,
4978
+ gadgetResult.mediaIds
4646
4979
  );
4647
4980
  }
4648
4981
  }
4649
4982
  } else {
4650
4983
  if (finalMessage.trim()) {
4651
- this.conversation.addGadgetCall(
4984
+ this.conversation.addGadgetCallResult(
4652
4985
  "TellUser",
4653
4986
  { message: finalMessage, done: false, type: "info" },
4654
4987
  `\u2139\uFE0F ${finalMessage}`
@@ -4774,10 +5107,10 @@ var init_agent = __esm({
4774
5107
  return this.client.modelRegistry.getModelLimits(unprefixedModelId)?.maxOutputTokens;
4775
5108
  }
4776
5109
  /**
4777
- * Merge the output limiter interceptor into user-provided hooks.
5110
+ * Chain the output limiter interceptor with user-provided hooks.
4778
5111
  * The limiter runs first, then chains to any user interceptor.
4779
5112
  */
4780
- mergeOutputLimiterHook(userHooks) {
5113
+ chainOutputLimiterWithUserHooks(userHooks) {
4781
5114
  if (!this.outputLimitEnabled) {
4782
5115
  return userHooks ?? {};
4783
5116
  }
@@ -5131,9 +5464,9 @@ var init_base_provider = __esm({
5131
5464
  */
5132
5465
  async *stream(options, descriptor, spec) {
5133
5466
  const preparedMessages = this.prepareMessages(options.messages);
5134
- const payload = this.buildRequestPayload(options, descriptor, spec, preparedMessages);
5467
+ const payload = this.buildApiRequest(options, descriptor, spec, preparedMessages);
5135
5468
  const rawStream = await this.executeStreamRequest(payload, options.signal);
5136
- yield* this.wrapStream(rawStream);
5469
+ yield* this.normalizeProviderStream(rawStream);
5137
5470
  }
5138
5471
  /**
5139
5472
  * Prepare messages for the request.
@@ -5233,11 +5566,11 @@ var init_anthropic = __esm({
5233
5566
  "Anthropic does not support speech generation. Use OpenAI (TTS) or Google Gemini (TTS) instead."
5234
5567
  );
5235
5568
  }
5236
- buildRequestPayload(options, descriptor, spec, messages) {
5569
+ buildApiRequest(options, descriptor, spec, messages) {
5237
5570
  const systemMessages = messages.filter((message) => message.role === "system");
5238
5571
  const system = systemMessages.length > 0 ? systemMessages.map((m, index) => ({
5239
5572
  type: "text",
5240
- text: extractText(m.content),
5573
+ text: extractMessageText(m.content),
5241
5574
  // Add cache_control to the LAST system message block
5242
5575
  ...index === systemMessages.length - 1 ? { cache_control: { type: "ephemeral" } } : {}
5243
5576
  })) : void 0;
@@ -5274,7 +5607,7 @@ var init_anthropic = __esm({
5274
5607
  * Handles text, images (base64 only), and applies cache_control.
5275
5608
  */
5276
5609
  convertToAnthropicContent(content, addCacheControl) {
5277
- const parts = normalizeContent(content);
5610
+ const parts = normalizeMessageContent(content);
5278
5611
  return parts.map((part, index) => {
5279
5612
  const isLastPart = index === parts.length - 1;
5280
5613
  const cacheControl = addCacheControl && isLastPart ? { cache_control: { type: "ephemeral" } } : {};
@@ -5320,7 +5653,7 @@ var init_anthropic = __esm({
5320
5653
  const stream2 = await client.messages.create(payload, signal ? { signal } : void 0);
5321
5654
  return stream2;
5322
5655
  }
5323
- async *wrapStream(iterable) {
5656
+ async *normalizeProviderStream(iterable) {
5324
5657
  const stream2 = iterable;
5325
5658
  let inputTokens = 0;
5326
5659
  let cachedInputTokens = 0;
@@ -5397,7 +5730,7 @@ var init_anthropic = __esm({
5397
5730
  async countTokens(messages, descriptor, _spec) {
5398
5731
  const client = this.client;
5399
5732
  const systemMessages = messages.filter((message) => message.role === "system");
5400
- const system = systemMessages.length > 0 ? systemMessages.map((m) => extractText(m.content)).join("\n\n") : void 0;
5733
+ const system = systemMessages.length > 0 ? systemMessages.map((m) => extractMessageText(m.content)).join("\n\n") : void 0;
5401
5734
  const conversation = messages.filter(
5402
5735
  (message) => message.role !== "system"
5403
5736
  ).map((message) => ({
@@ -5419,7 +5752,7 @@ var init_anthropic = __esm({
5419
5752
  let totalChars = 0;
5420
5753
  let imageCount = 0;
5421
5754
  for (const msg of messages) {
5422
- const parts = normalizeContent(msg.content);
5755
+ const parts = normalizeMessageContent(msg.content);
5423
5756
  for (const part of parts) {
5424
5757
  if (part.type === "text") {
5425
5758
  totalChars += part.text.length;
@@ -6110,7 +6443,7 @@ var init_gemini = __esm({
6110
6443
  format: spec?.defaultFormat ?? "wav"
6111
6444
  };
6112
6445
  }
6113
- buildRequestPayload(options, descriptor, _spec, messages) {
6446
+ buildApiRequest(options, descriptor, _spec, messages) {
6114
6447
  const contents = this.convertMessagesToContents(messages);
6115
6448
  const generationConfig = this.buildGenerationConfig(options);
6116
6449
  const config = {
@@ -6162,7 +6495,7 @@ var init_gemini = __esm({
6162
6495
  if (message.role === "system") {
6163
6496
  expandedMessages.push({
6164
6497
  role: "user",
6165
- content: extractText(message.content)
6498
+ content: extractMessageText(message.content)
6166
6499
  });
6167
6500
  expandedMessages.push({
6168
6501
  role: "assistant",
@@ -6212,7 +6545,7 @@ var init_gemini = __esm({
6212
6545
  * Handles text, images, and audio (Gemini supports all three).
6213
6546
  */
6214
6547
  convertToGeminiParts(content) {
6215
- const parts = normalizeContent(content);
6548
+ const parts = normalizeMessageContent(content);
6216
6549
  return parts.map((part) => {
6217
6550
  if (part.type === "text") {
6218
6551
  return { text: part.text };
@@ -6257,10 +6590,10 @@ var init_gemini = __esm({
6257
6590
  }
6258
6591
  return Object.keys(config).length > 0 ? config : null;
6259
6592
  }
6260
- async *wrapStream(iterable) {
6593
+ async *normalizeProviderStream(iterable) {
6261
6594
  const stream2 = iterable;
6262
6595
  for await (const chunk of stream2) {
6263
- const text3 = this.extractText(chunk);
6596
+ const text3 = this.extractMessageText(chunk);
6264
6597
  if (text3) {
6265
6598
  yield { text: text3, rawEvent: chunk };
6266
6599
  }
@@ -6271,7 +6604,7 @@ var init_gemini = __esm({
6271
6604
  }
6272
6605
  }
6273
6606
  }
6274
- extractText(chunk) {
6607
+ extractMessageText(chunk) {
6275
6608
  if (!chunk?.candidates) {
6276
6609
  return "";
6277
6610
  }
@@ -6336,7 +6669,7 @@ var init_gemini = __esm({
6336
6669
  let totalChars = 0;
6337
6670
  let mediaCount = 0;
6338
6671
  for (const msg of messages) {
6339
- const parts = normalizeContent(msg.content);
6672
+ const parts = normalizeMessageContent(msg.content);
6340
6673
  for (const part of parts) {
6341
6674
  if (part.type === "text") {
6342
6675
  totalChars += part.text.length;
@@ -6857,14 +7190,7 @@ var OPENAI_TTS_VOICES, OPENAI_TTS_EXTENDED_VOICES, OPENAI_TTS_FORMATS, openaiSpe
6857
7190
  var init_openai_speech_models = __esm({
6858
7191
  "src/providers/openai-speech-models.ts"() {
6859
7192
  "use strict";
6860
- OPENAI_TTS_VOICES = [
6861
- "alloy",
6862
- "echo",
6863
- "fable",
6864
- "onyx",
6865
- "nova",
6866
- "shimmer"
6867
- ];
7193
+ OPENAI_TTS_VOICES = ["alloy", "echo", "fable", "onyx", "nova", "shimmer"];
6868
7194
  OPENAI_TTS_EXTENDED_VOICES = [
6869
7195
  ...OPENAI_TTS_VOICES,
6870
7196
  "ash",
@@ -7089,7 +7415,7 @@ var init_openai = __esm({
7089
7415
  format
7090
7416
  };
7091
7417
  }
7092
- buildRequestPayload(options, descriptor, spec, messages) {
7418
+ buildApiRequest(options, descriptor, spec, messages) {
7093
7419
  const { maxTokens, temperature, topP, stopSequences, extra } = options;
7094
7420
  const supportsTemperature = spec?.metadata?.supportsTemperature !== false;
7095
7421
  const shouldIncludeTemperature = typeof temperature === "number" && supportsTemperature;
@@ -7124,7 +7450,7 @@ var init_openai = __esm({
7124
7450
  ...message.name ? { name: message.name } : {}
7125
7451
  };
7126
7452
  }
7127
- const textContent = typeof message.content === "string" ? message.content : extractText(message.content);
7453
+ const textContent = typeof message.content === "string" ? message.content : extractMessageText(message.content);
7128
7454
  if (role === "system") {
7129
7455
  return {
7130
7456
  role: "system",
@@ -7184,7 +7510,7 @@ var init_openai = __esm({
7184
7510
  const stream2 = await client.chat.completions.create(payload, signal ? { signal } : void 0);
7185
7511
  return stream2;
7186
7512
  }
7187
- async *wrapStream(iterable) {
7513
+ async *normalizeProviderStream(iterable) {
7188
7514
  const stream2 = iterable;
7189
7515
  for await (const chunk of stream2) {
7190
7516
  const text3 = chunk.choices.map((choice) => choice.delta?.content ?? "").join("");
@@ -7242,9 +7568,9 @@ var init_openai = __esm({
7242
7568
  tokenCount += OPENAI_MESSAGE_OVERHEAD_TOKENS;
7243
7569
  const roleText = ROLE_MAP[message.role];
7244
7570
  tokenCount += encoding.encode(roleText).length;
7245
- const textContent = extractText(message.content);
7571
+ const textContent = extractMessageText(message.content);
7246
7572
  tokenCount += encoding.encode(textContent).length;
7247
- const parts = normalizeContent(message.content);
7573
+ const parts = normalizeMessageContent(message.content);
7248
7574
  for (const part of parts) {
7249
7575
  if (part.type === "image") {
7250
7576
  imageCount++;
@@ -7269,7 +7595,7 @@ var init_openai = __esm({
7269
7595
  let totalChars = 0;
7270
7596
  let imageCount = 0;
7271
7597
  for (const msg of messages) {
7272
- const parts = normalizeContent(msg.content);
7598
+ const parts = normalizeMessageContent(msg.content);
7273
7599
  for (const part of parts) {
7274
7600
  if (part.type === "text") {
7275
7601
  totalChars += part.text.length;
@@ -7562,9 +7888,7 @@ var init_image = __esm({
7562
7888
  return this.findImageAdapter(modelId) !== void 0;
7563
7889
  }
7564
7890
  findImageAdapter(modelId) {
7565
- return this.adapters.find(
7566
- (adapter) => adapter.supportsImageGeneration?.(modelId) ?? false
7567
- );
7891
+ return this.adapters.find((adapter) => adapter.supportsImageGeneration?.(modelId) ?? false);
7568
7892
  }
7569
7893
  };
7570
7894
  }
@@ -7616,9 +7940,7 @@ var init_speech = __esm({
7616
7940
  return this.findSpeechAdapter(modelId) !== void 0;
7617
7941
  }
7618
7942
  findSpeechAdapter(modelId) {
7619
- return this.adapters.find(
7620
- (adapter) => adapter.supportsSpeechGeneration?.(modelId) ?? false
7621
- );
7943
+ return this.adapters.find((adapter) => adapter.supportsSpeechGeneration?.(modelId) ?? false);
7622
7944
  }
7623
7945
  };
7624
7946
  }
@@ -7729,11 +8051,7 @@ var init_vision = __esm({
7729
8051
  if (!parsed) {
7730
8052
  throw new Error("Invalid data URL format");
7731
8053
  }
7732
- builder.addUserWithImage(
7733
- options.prompt,
7734
- parsed.data,
7735
- parsed.mimeType
7736
- );
8054
+ builder.addUserWithImage(options.prompt, parsed.data, parsed.mimeType);
7737
8055
  } else {
7738
8056
  const buffer = Buffer.from(options.image, "base64");
7739
8057
  builder.addUserWithImage(options.prompt, buffer, options.mimeType);
@@ -8142,20 +8460,21 @@ var init_builder = __esm({
8142
8460
  promptConfig;
8143
8461
  gadgets = [];
8144
8462
  initialMessages = [];
8145
- onHumanInputRequired;
8463
+ requestHumanInput;
8146
8464
  gadgetStartPrefix;
8147
8465
  gadgetEndPrefix;
8148
8466
  gadgetArgPrefix;
8149
8467
  textOnlyHandler;
8150
8468
  textWithGadgetsHandler;
8151
8469
  stopOnGadgetError;
8152
- shouldContinueAfterError;
8470
+ canRecoverFromGadgetError;
8153
8471
  defaultGadgetTimeoutMs;
8154
8472
  gadgetOutputLimit;
8155
8473
  gadgetOutputLimitPercent;
8156
8474
  compactionConfig;
8157
8475
  signal;
8158
8476
  trailingMessage;
8477
+ subagentConfig;
8159
8478
  constructor(client) {
8160
8479
  this.client = client;
8161
8480
  }
@@ -8246,13 +8565,13 @@ var init_builder = __esm({
8246
8565
  *
8247
8566
  * @example
8248
8567
  * ```typescript
8249
- * .withPromptConfig({
8568
+ * .withPromptTemplateConfig({
8250
8569
  * mainInstruction: "Use the gadget markers below:",
8251
8570
  * rules: ["Always use markers", "Never use function calling"]
8252
8571
  * })
8253
8572
  * ```
8254
8573
  */
8255
- withPromptConfig(config) {
8574
+ withPromptTemplateConfig(config) {
8256
8575
  this.promptConfig = config;
8257
8576
  return this;
8258
8577
  }
@@ -8332,7 +8651,7 @@ var init_builder = __esm({
8332
8651
  * ```
8333
8652
  */
8334
8653
  onHumanInput(handler) {
8335
- this.onHumanInputRequired = handler;
8654
+ this.requestHumanInput = handler;
8336
8655
  return this;
8337
8656
  }
8338
8657
  /**
@@ -8467,9 +8786,9 @@ var init_builder = __esm({
8467
8786
  * Provides fine-grained control over whether to continue after different types of errors.
8468
8787
  * Overrides `stopOnGadgetError` when provided.
8469
8788
  *
8470
- * **Note:** This builder method configures the underlying `shouldContinueAfterError` option
8789
+ * **Note:** This builder method configures the underlying `canRecoverFromGadgetError` option
8471
8790
  * in `AgentOptions`. The method is named `withErrorHandler` for better developer experience,
8472
- * but maps to the `shouldContinueAfterError` property internally.
8791
+ * but maps to the `canRecoverFromGadgetError` property internally.
8473
8792
  *
8474
8793
  * @param handler - Function that decides whether to continue after an error.
8475
8794
  * Return `true` to continue execution, `false` to stop.
@@ -8490,7 +8809,7 @@ var init_builder = __esm({
8490
8809
  * ```
8491
8810
  */
8492
8811
  withErrorHandler(handler) {
8493
- this.shouldContinueAfterError = handler;
8812
+ this.canRecoverFromGadgetError = handler;
8494
8813
  return this;
8495
8814
  }
8496
8815
  /**
@@ -8631,6 +8950,27 @@ var init_builder = __esm({
8631
8950
  this.signal = signal;
8632
8951
  return this;
8633
8952
  }
8953
+ /**
8954
+ * Set subagent configuration overrides.
8955
+ *
8956
+ * Subagent gadgets (like BrowseWeb) can read these settings from ExecutionContext
8957
+ * to inherit model and other options from the CLI configuration.
8958
+ *
8959
+ * @param config - Subagent configuration map keyed by gadget name
8960
+ * @returns This builder for chaining
8961
+ *
8962
+ * @example
8963
+ * ```typescript
8964
+ * .withSubagentConfig({
8965
+ * BrowseWeb: { model: "inherit", maxIterations: 20, headless: true },
8966
+ * CodeAnalyzer: { model: "sonnet", maxIterations: 10 }
8967
+ * })
8968
+ * ```
8969
+ */
8970
+ withSubagentConfig(config) {
8971
+ this.subagentConfig = config;
8972
+ return this;
8973
+ }
8634
8974
  /**
8635
8975
  * Add an ephemeral trailing message that appears at the end of each LLM request.
8636
8976
  *
@@ -8795,19 +9135,20 @@ ${endPrefix}`
8795
9135
  hooks: this.composeHooks(),
8796
9136
  promptConfig: this.promptConfig,
8797
9137
  initialMessages: this.initialMessages,
8798
- onHumanInputRequired: this.onHumanInputRequired,
9138
+ requestHumanInput: this.requestHumanInput,
8799
9139
  gadgetStartPrefix: this.gadgetStartPrefix,
8800
9140
  gadgetEndPrefix: this.gadgetEndPrefix,
8801
9141
  gadgetArgPrefix: this.gadgetArgPrefix,
8802
9142
  textOnlyHandler: this.textOnlyHandler,
8803
9143
  textWithGadgetsHandler: this.textWithGadgetsHandler,
8804
9144
  stopOnGadgetError: this.stopOnGadgetError,
8805
- shouldContinueAfterError: this.shouldContinueAfterError,
9145
+ canRecoverFromGadgetError: this.canRecoverFromGadgetError,
8806
9146
  defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
8807
9147
  gadgetOutputLimit: this.gadgetOutputLimit,
8808
9148
  gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
8809
9149
  compactionConfig: this.compactionConfig,
8810
- signal: this.signal
9150
+ signal: this.signal,
9151
+ subagentConfig: this.subagentConfig
8811
9152
  };
8812
9153
  }
8813
9154
  ask(userPrompt) {
@@ -8976,19 +9317,20 @@ ${endPrefix}`
8976
9317
  hooks: this.composeHooks(),
8977
9318
  promptConfig: this.promptConfig,
8978
9319
  initialMessages: this.initialMessages,
8979
- onHumanInputRequired: this.onHumanInputRequired,
9320
+ requestHumanInput: this.requestHumanInput,
8980
9321
  gadgetStartPrefix: this.gadgetStartPrefix,
8981
9322
  gadgetEndPrefix: this.gadgetEndPrefix,
8982
9323
  gadgetArgPrefix: this.gadgetArgPrefix,
8983
9324
  textOnlyHandler: this.textOnlyHandler,
8984
9325
  textWithGadgetsHandler: this.textWithGadgetsHandler,
8985
9326
  stopOnGadgetError: this.stopOnGadgetError,
8986
- shouldContinueAfterError: this.shouldContinueAfterError,
9327
+ canRecoverFromGadgetError: this.canRecoverFromGadgetError,
8987
9328
  defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
8988
9329
  gadgetOutputLimit: this.gadgetOutputLimit,
8989
9330
  gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
8990
9331
  compactionConfig: this.compactionConfig,
8991
- signal: this.signal
9332
+ signal: this.signal,
9333
+ subagentConfig: this.subagentConfig
8992
9334
  };
8993
9335
  return new Agent(AGENT_INTERNAL_KEY, options);
8994
9336
  }
@@ -8999,11 +9341,10 @@ ${endPrefix}`
8999
9341
  // src/index.ts
9000
9342
  var index_exports = {};
9001
9343
  __export(index_exports, {
9002
- AbortError: () => AbortError,
9344
+ AbortException: () => AbortException,
9345
+ AbstractGadget: () => AbstractGadget,
9003
9346
  AgentBuilder: () => AgentBuilder,
9004
9347
  AnthropicMessagesProvider: () => AnthropicMessagesProvider,
9005
- BaseGadget: () => BaseGadget,
9006
- BreakLoopException: () => BreakLoopException,
9007
9348
  CompactionManager: () => CompactionManager,
9008
9349
  ConversationManager: () => ConversationManager,
9009
9350
  DEFAULT_COMPACTION_CONFIG: () => DEFAULT_COMPACTION_CONFIG,
@@ -9011,16 +9352,18 @@ __export(index_exports, {
9011
9352
  DEFAULT_PROMPTS: () => DEFAULT_PROMPTS,
9012
9353
  DEFAULT_SUMMARIZATION_PROMPT: () => DEFAULT_SUMMARIZATION_PROMPT,
9013
9354
  Gadget: () => Gadget,
9355
+ GadgetCallParser: () => GadgetCallParser,
9014
9356
  GadgetExecutor: () => GadgetExecutor,
9015
9357
  GadgetOutputStore: () => GadgetOutputStore,
9016
9358
  GadgetRegistry: () => GadgetRegistry,
9017
9359
  GeminiGenerativeProvider: () => GeminiGenerativeProvider,
9018
9360
  HookPresets: () => HookPresets,
9019
- HumanInputException: () => HumanInputException,
9361
+ HumanInputRequiredException: () => HumanInputRequiredException,
9020
9362
  HybridStrategy: () => HybridStrategy,
9021
9363
  LLMMessageBuilder: () => LLMMessageBuilder,
9022
9364
  LLMist: () => LLMist,
9023
9365
  MODEL_ALIASES: () => MODEL_ALIASES,
9366
+ MediaStore: () => MediaStore,
9024
9367
  MockBuilder: () => MockBuilder,
9025
9368
  MockManager: () => MockManager,
9026
9369
  MockProviderAdapter: () => MockProviderAdapter,
@@ -9028,9 +9371,10 @@ __export(index_exports, {
9028
9371
  ModelRegistry: () => ModelRegistry,
9029
9372
  OpenAIChatProvider: () => OpenAIChatProvider,
9030
9373
  SlidingWindowStrategy: () => SlidingWindowStrategy,
9031
- StreamParser: () => StreamParser,
9032
9374
  StreamProcessor: () => StreamProcessor,
9033
9375
  SummarizationStrategy: () => SummarizationStrategy,
9376
+ TaskCompletionSignal: () => TaskCompletionSignal,
9377
+ TimeoutException: () => TimeoutException,
9034
9378
  audioFromBase64: () => audioFromBase64,
9035
9379
  audioFromBuffer: () => audioFromBuffer,
9036
9380
  collectEvents: () => collectEvents,
@@ -9042,6 +9386,7 @@ __export(index_exports, {
9042
9386
  createGeminiProviderFromEnv: () => createGeminiProviderFromEnv,
9043
9387
  createHints: () => createHints,
9044
9388
  createLogger: () => createLogger,
9389
+ createMediaOutput: () => createMediaOutput,
9045
9390
  createMockAdapter: () => createMockAdapter,
9046
9391
  createMockClient: () => createMockClient,
9047
9392
  createMockStream: () => createMockStream,
@@ -9051,7 +9396,7 @@ __export(index_exports, {
9051
9396
  detectAudioMimeType: () => detectAudioMimeType,
9052
9397
  detectImageMimeType: () => detectImageMimeType,
9053
9398
  discoverProviderAdapters: () => discoverProviderAdapters,
9054
- extractText: () => extractText,
9399
+ extractMessageText: () => extractMessageText,
9055
9400
  getMockManager: () => getMockManager,
9056
9401
  getModelId: () => getModelId,
9057
9402
  getProvider: () => getProvider,
@@ -9065,13 +9410,18 @@ __export(index_exports, {
9065
9410
  isTextPart: () => isTextPart,
9066
9411
  iterationProgressHint: () => iterationProgressHint,
9067
9412
  mockLLM: () => mockLLM,
9068
- normalizeContent: () => normalizeContent,
9413
+ normalizeMessageContent: () => normalizeMessageContent,
9069
9414
  parallelGadgetHint: () => parallelGadgetHint,
9070
9415
  parseDataUrl: () => parseDataUrl,
9071
9416
  resolveHintTemplate: () => resolveHintTemplate,
9072
9417
  resolveModel: () => resolveModel,
9073
9418
  resolvePromptTemplate: () => resolvePromptTemplate,
9074
9419
  resolveRulesTemplate: () => resolveRulesTemplate,
9420
+ resultWithAudio: () => resultWithAudio,
9421
+ resultWithFile: () => resultWithFile,
9422
+ resultWithImage: () => resultWithImage,
9423
+ resultWithImages: () => resultWithImages,
9424
+ resultWithMedia: () => resultWithMedia,
9075
9425
  runWithHandlers: () => runWithHandlers,
9076
9426
  stream: () => stream,
9077
9427
  text: () => text,
@@ -9625,9 +9975,7 @@ var HookPresets = class _HookPresets {
9625
9975
  console.log(
9626
9976
  `\u{1F5DC}\uFE0F Compaction (${ctx.event.strategy}): ${ctx.event.tokensBefore} \u2192 ${ctx.event.tokensAfter} tokens (saved ${saved}, ${percent}%)`
9627
9977
  );
9628
- console.log(
9629
- ` Messages: ${ctx.event.messagesBefore} \u2192 ${ctx.event.messagesAfter}`
9630
- );
9978
+ console.log(` Messages: ${ctx.event.messagesBefore} \u2192 ${ctx.event.messagesAfter}`);
9631
9979
  if (ctx.stats.totalCompactions > 1) {
9632
9980
  console.log(
9633
9981
  ` Cumulative: ${ctx.stats.totalCompactions} compactions, ${ctx.stats.totalTokensSaved} tokens saved`
@@ -9862,16 +10210,15 @@ var HookPresets = class _HookPresets {
9862
10210
  }
9863
10211
  };
9864
10212
 
9865
- // src/agent/index.ts
9866
- init_conversation_manager();
9867
- init_stream_processor();
9868
- init_gadget_output_store();
9869
-
9870
10213
  // src/agent/compaction/index.ts
9871
10214
  init_config();
9872
- init_strategy();
9873
- init_strategies();
9874
10215
  init_manager();
10216
+ init_strategies();
10217
+ init_strategy();
10218
+
10219
+ // src/agent/index.ts
10220
+ init_conversation_manager();
10221
+ init_gadget_output_store();
9875
10222
 
9876
10223
  // src/agent/hints.ts
9877
10224
  init_prompt_config();
@@ -9895,11 +10242,7 @@ function iterationProgressHint(options) {
9895
10242
  maxIterations,
9896
10243
  remaining
9897
10244
  };
9898
- let hint = resolveHintTemplate(
9899
- template,
9900
- DEFAULT_HINTS.iterationProgressHint,
9901
- hintContext
9902
- );
10245
+ let hint = resolveHintTemplate(template, DEFAULT_HINTS.iterationProgressHint, hintContext);
9903
10246
  if (showUrgency && progress >= 0.8) {
9904
10247
  hint += " \u26A0\uFE0F Running low on iterations - focus on completing the task.";
9905
10248
  }
@@ -9974,6 +10317,9 @@ function createHints(config) {
9974
10317
  return HookPresets.merge(...hooksToMerge);
9975
10318
  }
9976
10319
 
10320
+ // src/agent/index.ts
10321
+ init_stream_processor();
10322
+
9977
10323
  // src/index.ts
9978
10324
  init_client();
9979
10325
  init_input_content();
@@ -9984,17 +10330,17 @@ init_options();
9984
10330
  init_prompt_config();
9985
10331
  init_quick_methods();
9986
10332
  init_create_gadget();
9987
- init_output_viewer();
9988
10333
  init_exceptions();
9989
10334
  init_executor();
9990
10335
  init_gadget();
10336
+ init_output_viewer();
9991
10337
  init_parser();
9992
10338
  init_registry();
9993
10339
 
9994
10340
  // src/gadgets/typed-gadget.ts
9995
10341
  init_gadget();
9996
10342
  function Gadget(config) {
9997
- class GadgetBase extends BaseGadget {
10343
+ class GadgetBase extends AbstractGadget {
9998
10344
  description = config.description;
9999
10345
  parameterSchema = config.schema;
10000
10346
  name = config.name;
@@ -10012,6 +10358,119 @@ function Gadget(config) {
10012
10358
  return GadgetBase;
10013
10359
  }
10014
10360
 
10361
+ // src/gadgets/helpers.ts
10362
+ init_input_content();
10363
+ function createMediaOutput(kind, data, mimeType, options) {
10364
+ const buffer = data instanceof Buffer ? data : Buffer.from(data);
10365
+ return {
10366
+ kind,
10367
+ data: buffer.toString("base64"),
10368
+ mimeType,
10369
+ description: options?.description,
10370
+ metadata: options?.metadata,
10371
+ fileName: options?.fileName
10372
+ };
10373
+ }
10374
+ function resultWithMedia(result, media, cost) {
10375
+ if (media.length === 0) {
10376
+ throw new Error("resultWithMedia: media array cannot be empty");
10377
+ }
10378
+ return {
10379
+ result,
10380
+ media,
10381
+ cost
10382
+ };
10383
+ }
10384
+ function resultWithImage(result, imageData, options) {
10385
+ const buffer = imageData instanceof Buffer ? imageData : Buffer.from(imageData);
10386
+ const mimeType = options?.mimeType ?? detectImageMimeType(buffer);
10387
+ if (!mimeType) {
10388
+ throw new Error(
10389
+ "Could not detect image MIME type. Please provide mimeType explicitly in options."
10390
+ );
10391
+ }
10392
+ return {
10393
+ result,
10394
+ media: [
10395
+ {
10396
+ kind: "image",
10397
+ data: buffer.toString("base64"),
10398
+ mimeType,
10399
+ description: options?.description,
10400
+ metadata: options?.metadata,
10401
+ fileName: options?.fileName
10402
+ }
10403
+ ],
10404
+ cost: options?.cost
10405
+ };
10406
+ }
10407
+ function resultWithImages(result, images, cost) {
10408
+ if (images.length === 0) {
10409
+ throw new Error("resultWithImages: images array cannot be empty");
10410
+ }
10411
+ const media = images.map((img, index) => {
10412
+ const buffer = img.data instanceof Buffer ? img.data : Buffer.from(img.data);
10413
+ const mimeType = img.mimeType ?? detectImageMimeType(buffer);
10414
+ if (!mimeType) {
10415
+ throw new Error(
10416
+ `Could not detect MIME type for image at index ${index}. Please provide mimeType explicitly.`
10417
+ );
10418
+ }
10419
+ return {
10420
+ kind: "image",
10421
+ data: buffer.toString("base64"),
10422
+ mimeType,
10423
+ description: img.description,
10424
+ metadata: img.metadata,
10425
+ fileName: img.fileName
10426
+ };
10427
+ });
10428
+ return { result, media, cost };
10429
+ }
10430
+ function resultWithAudio(result, audioData, options) {
10431
+ const buffer = audioData instanceof Buffer ? audioData : Buffer.from(audioData);
10432
+ const mimeType = options?.mimeType ?? detectAudioMimeType(buffer);
10433
+ if (!mimeType) {
10434
+ throw new Error(
10435
+ "Could not detect audio MIME type. Please provide mimeType explicitly in options."
10436
+ );
10437
+ }
10438
+ const metadata = options?.durationMs ? { durationMs: options.durationMs } : void 0;
10439
+ return {
10440
+ result,
10441
+ media: [
10442
+ {
10443
+ kind: "audio",
10444
+ data: buffer.toString("base64"),
10445
+ mimeType,
10446
+ description: options?.description,
10447
+ metadata,
10448
+ fileName: options?.fileName
10449
+ }
10450
+ ],
10451
+ cost: options?.cost
10452
+ };
10453
+ }
10454
+ function resultWithFile(result, fileData, mimeType, options) {
10455
+ const buffer = fileData instanceof Buffer ? fileData : Buffer.from(fileData);
10456
+ return {
10457
+ result,
10458
+ media: [
10459
+ {
10460
+ kind: "file",
10461
+ data: buffer.toString("base64"),
10462
+ mimeType,
10463
+ description: options?.description,
10464
+ fileName: options?.fileName
10465
+ }
10466
+ ],
10467
+ cost: options?.cost
10468
+ };
10469
+ }
10470
+
10471
+ // src/index.ts
10472
+ init_media_store();
10473
+
10015
10474
  // src/gadgets/validation.ts
10016
10475
  function validateAndApplyDefaults(schema, params) {
10017
10476
  const result = schema.safeParse(params);
@@ -10049,6 +10508,9 @@ init_discovery();
10049
10508
  init_gemini();
10050
10509
  init_openai();
10051
10510
 
10511
+ // src/testing/cli-helpers.ts
10512
+ var import_node_stream = require("stream");
10513
+
10052
10514
  // src/testing/mock-manager.ts
10053
10515
  init_logger();
10054
10516
  var MockManager = class _MockManager {
@@ -10601,7 +11063,9 @@ var MockBuilder = class {
10601
11063
  */
10602
11064
  whenMessageContains(text3) {
10603
11065
  this.matchers.push(
10604
- (ctx) => ctx.messages.some((msg) => extractText(msg.content).toLowerCase().includes(text3.toLowerCase()))
11066
+ (ctx) => ctx.messages.some(
11067
+ (msg) => extractMessageText(msg.content).toLowerCase().includes(text3.toLowerCase())
11068
+ )
10605
11069
  );
10606
11070
  return this;
10607
11071
  }
@@ -10615,7 +11079,7 @@ var MockBuilder = class {
10615
11079
  this.matchers.push((ctx) => {
10616
11080
  const lastMsg = ctx.messages[ctx.messages.length - 1];
10617
11081
  if (!lastMsg) return false;
10618
- return extractText(lastMsg.content).toLowerCase().includes(text3.toLowerCase());
11082
+ return extractMessageText(lastMsg.content).toLowerCase().includes(text3.toLowerCase());
10619
11083
  });
10620
11084
  return this;
10621
11085
  }
@@ -10626,7 +11090,7 @@ var MockBuilder = class {
10626
11090
  * mockLLM().whenMessageMatches(/calculate \d+/)
10627
11091
  */
10628
11092
  whenMessageMatches(regex) {
10629
- this.matchers.push((ctx) => ctx.messages.some((msg) => regex.test(extractText(msg.content))));
11093
+ this.matchers.push((ctx) => ctx.messages.some((msg) => regex.test(extractMessageText(msg.content))));
10630
11094
  return this;
10631
11095
  }
10632
11096
  /**
@@ -10638,7 +11102,7 @@ var MockBuilder = class {
10638
11102
  whenRoleContains(role, text3) {
10639
11103
  this.matchers.push(
10640
11104
  (ctx) => ctx.messages.some(
10641
- (msg) => msg.role === role && extractText(msg.content).toLowerCase().includes(text3.toLowerCase())
11105
+ (msg) => msg.role === role && extractMessageText(msg.content).toLowerCase().includes(text3.toLowerCase())
10642
11106
  )
10643
11107
  );
10644
11108
  return this;
@@ -11041,16 +11505,12 @@ function createMockClient(options) {
11041
11505
 
11042
11506
  // src/testing/mock-gadget.ts
11043
11507
  init_gadget();
11044
-
11045
- // src/testing/cli-helpers.ts
11046
- var import_node_stream = require("stream");
11047
11508
  // Annotate the CommonJS export names for ESM import in node:
11048
11509
  0 && (module.exports = {
11049
- AbortError,
11510
+ AbortException,
11511
+ AbstractGadget,
11050
11512
  AgentBuilder,
11051
11513
  AnthropicMessagesProvider,
11052
- BaseGadget,
11053
- BreakLoopException,
11054
11514
  CompactionManager,
11055
11515
  ConversationManager,
11056
11516
  DEFAULT_COMPACTION_CONFIG,
@@ -11058,16 +11518,18 @@ var import_node_stream = require("stream");
11058
11518
  DEFAULT_PROMPTS,
11059
11519
  DEFAULT_SUMMARIZATION_PROMPT,
11060
11520
  Gadget,
11521
+ GadgetCallParser,
11061
11522
  GadgetExecutor,
11062
11523
  GadgetOutputStore,
11063
11524
  GadgetRegistry,
11064
11525
  GeminiGenerativeProvider,
11065
11526
  HookPresets,
11066
- HumanInputException,
11527
+ HumanInputRequiredException,
11067
11528
  HybridStrategy,
11068
11529
  LLMMessageBuilder,
11069
11530
  LLMist,
11070
11531
  MODEL_ALIASES,
11532
+ MediaStore,
11071
11533
  MockBuilder,
11072
11534
  MockManager,
11073
11535
  MockProviderAdapter,
@@ -11075,9 +11537,10 @@ var import_node_stream = require("stream");
11075
11537
  ModelRegistry,
11076
11538
  OpenAIChatProvider,
11077
11539
  SlidingWindowStrategy,
11078
- StreamParser,
11079
11540
  StreamProcessor,
11080
11541
  SummarizationStrategy,
11542
+ TaskCompletionSignal,
11543
+ TimeoutException,
11081
11544
  audioFromBase64,
11082
11545
  audioFromBuffer,
11083
11546
  collectEvents,
@@ -11089,6 +11552,7 @@ var import_node_stream = require("stream");
11089
11552
  createGeminiProviderFromEnv,
11090
11553
  createHints,
11091
11554
  createLogger,
11555
+ createMediaOutput,
11092
11556
  createMockAdapter,
11093
11557
  createMockClient,
11094
11558
  createMockStream,
@@ -11098,7 +11562,7 @@ var import_node_stream = require("stream");
11098
11562
  detectAudioMimeType,
11099
11563
  detectImageMimeType,
11100
11564
  discoverProviderAdapters,
11101
- extractText,
11565
+ extractMessageText,
11102
11566
  getMockManager,
11103
11567
  getModelId,
11104
11568
  getProvider,
@@ -11112,13 +11576,18 @@ var import_node_stream = require("stream");
11112
11576
  isTextPart,
11113
11577
  iterationProgressHint,
11114
11578
  mockLLM,
11115
- normalizeContent,
11579
+ normalizeMessageContent,
11116
11580
  parallelGadgetHint,
11117
11581
  parseDataUrl,
11118
11582
  resolveHintTemplate,
11119
11583
  resolveModel,
11120
11584
  resolvePromptTemplate,
11121
11585
  resolveRulesTemplate,
11586
+ resultWithAudio,
11587
+ resultWithFile,
11588
+ resultWithImage,
11589
+ resultWithImages,
11590
+ resultWithMedia,
11122
11591
  runWithHandlers,
11123
11592
  stream,
11124
11593
  text,