llmist 17.3.0 → 17.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,29 +1,87 @@
1
- var __defProp = Object.defineProperty;
2
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
- var __getOwnPropNames = Object.getOwnPropertyNames;
4
- var __hasOwnProp = Object.prototype.hasOwnProperty;
5
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
6
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
7
- }) : x)(function(x) {
8
- if (typeof require !== "undefined") return require.apply(this, arguments);
9
- throw Error('Dynamic require of "' + x + '" is not supported');
10
- });
11
- var __esm = (fn, res) => function __init() {
12
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
13
- };
14
- var __export = (target, all) => {
15
- for (var name in all)
16
- __defProp(target, name, { get: all[name], enumerable: true });
17
- };
18
- var __copyProps = (to, from, except, desc) => {
19
- if (from && typeof from === "object" || typeof from === "function") {
20
- for (let key of __getOwnPropNames(from))
21
- if (!__hasOwnProp.call(to, key) && key !== except)
22
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
23
- }
24
- return to;
25
- };
26
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
1
+ import {
2
+ AbortException,
3
+ AbstractGadget,
4
+ BudgetPricingUnavailableError,
5
+ CHARS_PER_TOKEN,
6
+ DEFAULT_GADGET_OUTPUT_LIMIT,
7
+ DEFAULT_GADGET_OUTPUT_LIMIT_PERCENT,
8
+ DEFAULT_HINTS,
9
+ DEFAULT_MCP_COMMAND_ALLOWLIST,
10
+ DEFAULT_PROMPTS,
11
+ FALLBACK_CONTEXT_WINDOW,
12
+ GADGET_ARG_PREFIX,
13
+ GADGET_END_PREFIX,
14
+ GADGET_START_PREFIX,
15
+ HumanInputRequiredException,
16
+ JsonSchemaConversionError,
17
+ LLMMessageBuilder,
18
+ McpClient,
19
+ McpConnectError,
20
+ McpError,
21
+ McpLifecycle,
22
+ McpToolCallError,
23
+ McpUntrustedCommandError,
24
+ TaskCompletionSignal,
25
+ TimeoutException,
26
+ __esm,
27
+ __export,
28
+ __require,
29
+ __toCommonJS,
30
+ assertCommandAllowed,
31
+ audioFromBase64,
32
+ audioFromBuffer,
33
+ createGadget,
34
+ createLogger,
35
+ createMediaOutput,
36
+ defaultLogger,
37
+ detectAudioMimeType,
38
+ detectImageMimeType,
39
+ extractMessageText,
40
+ gadgetError,
41
+ gadgetSuccess,
42
+ getErrorMessage,
43
+ imageFromBase64,
44
+ imageFromBuffer,
45
+ imageFromUrl,
46
+ init_allowlist,
47
+ init_client,
48
+ init_constants,
49
+ init_create_gadget,
50
+ init_errors,
51
+ init_exceptions,
52
+ init_gadget,
53
+ init_helpers,
54
+ init_input_content,
55
+ init_json_schema_to_zod,
56
+ init_lifecycle,
57
+ init_logger,
58
+ init_messages,
59
+ init_prompt_config,
60
+ init_schema_to_json,
61
+ init_schema_validator,
62
+ init_tool_adapter,
63
+ isAudioPart,
64
+ isDataUrl,
65
+ isImagePart,
66
+ isTextPart,
67
+ jsonSchemaToZod,
68
+ mcpToolToGadget,
69
+ normalizeMessageContent,
70
+ parseDataUrl,
71
+ resolveHintTemplate,
72
+ resolvePromptTemplate,
73
+ resolveRulesTemplate,
74
+ resultWithAudio,
75
+ resultWithFile,
76
+ resultWithImage,
77
+ resultWithImages,
78
+ resultWithMedia,
79
+ schemaToJSONSchema,
80
+ text,
81
+ toBase64,
82
+ validateGadgetSchema,
83
+ withErrorHandling
84
+ } from "./chunk-HM7PUGPA.js";
27
85
 
28
86
  // src/core/execution-tree-aggregator.ts
29
87
  var ExecutionTreeAggregator;
@@ -803,679 +861,6 @@ var init_execution_tree = __esm({
803
861
  }
804
862
  });
805
863
 
806
- // src/core/constants.ts
807
- var GADGET_START_PREFIX, GADGET_END_PREFIX, GADGET_ARG_PREFIX, DEFAULT_GADGET_OUTPUT_LIMIT, DEFAULT_GADGET_OUTPUT_LIMIT_PERCENT, CHARS_PER_TOKEN, FALLBACK_CONTEXT_WINDOW;
808
- var init_constants = __esm({
809
- "src/core/constants.ts"() {
810
- "use strict";
811
- GADGET_START_PREFIX = "!!!GADGET_START:";
812
- GADGET_END_PREFIX = "!!!GADGET_END";
813
- GADGET_ARG_PREFIX = "!!!ARG:";
814
- DEFAULT_GADGET_OUTPUT_LIMIT = true;
815
- DEFAULT_GADGET_OUTPUT_LIMIT_PERCENT = 15;
816
- CHARS_PER_TOKEN = 2;
817
- FALLBACK_CONTEXT_WINDOW = 128e3;
818
- }
819
- });
820
-
821
- // src/core/input-content.ts
822
- function isTextPart(part) {
823
- return part.type === "text";
824
- }
825
- function isImagePart(part) {
826
- return part.type === "image";
827
- }
828
- function isAudioPart(part) {
829
- return part.type === "audio";
830
- }
831
- function text(content) {
832
- return { type: "text", text: content };
833
- }
834
- function imageFromBase64(data, mediaType) {
835
- return {
836
- type: "image",
837
- source: { type: "base64", mediaType, data }
838
- };
839
- }
840
- function imageFromUrl(url) {
841
- return {
842
- type: "image",
843
- source: { type: "url", url }
844
- };
845
- }
846
- function detectImageMimeType(data) {
847
- const bytes = data instanceof Buffer ? data : Buffer.from(data);
848
- for (const { bytes: magic, mimeType } of IMAGE_MAGIC_BYTES) {
849
- if (bytes.length >= magic.length) {
850
- let matches = true;
851
- for (let i = 0; i < magic.length; i++) {
852
- if (bytes[i] !== magic[i]) {
853
- matches = false;
854
- break;
855
- }
856
- }
857
- if (matches) {
858
- if (mimeType === "image/webp") {
859
- if (bytes.length >= 12) {
860
- const webpMarker = bytes[8] === 87 && bytes[9] === 69 && bytes[10] === 66 && bytes[11] === 80;
861
- if (!webpMarker) continue;
862
- }
863
- }
864
- return mimeType;
865
- }
866
- }
867
- }
868
- return null;
869
- }
870
- function detectAudioMimeType(data) {
871
- const bytes = data instanceof Buffer ? data : Buffer.from(data);
872
- for (const { bytes: magic, mimeType } of AUDIO_MAGIC_BYTES) {
873
- if (bytes.length >= magic.length) {
874
- let matches = true;
875
- for (let i = 0; i < magic.length; i++) {
876
- if (bytes[i] !== magic[i]) {
877
- matches = false;
878
- break;
879
- }
880
- }
881
- if (matches) {
882
- if (mimeType === "audio/wav") {
883
- if (bytes.length >= 12) {
884
- const waveMarker = bytes[8] === 87 && bytes[9] === 65 && bytes[10] === 86 && bytes[11] === 69;
885
- if (!waveMarker) continue;
886
- }
887
- }
888
- return mimeType;
889
- }
890
- }
891
- }
892
- return null;
893
- }
894
- function toBase64(data) {
895
- if (typeof data === "string") {
896
- return data;
897
- }
898
- return Buffer.from(data).toString("base64");
899
- }
900
- function imageFromBuffer(buffer, mediaType) {
901
- const detectedType = mediaType ?? detectImageMimeType(buffer);
902
- if (!detectedType) {
903
- throw new Error(
904
- "Could not detect image MIME type. Please provide the mediaType parameter explicitly."
905
- );
906
- }
907
- return {
908
- type: "image",
909
- source: {
910
- type: "base64",
911
- mediaType: detectedType,
912
- data: toBase64(buffer)
913
- }
914
- };
915
- }
916
- function audioFromBase64(data, mediaType) {
917
- return {
918
- type: "audio",
919
- source: { type: "base64", mediaType, data }
920
- };
921
- }
922
- function audioFromBuffer(buffer, mediaType) {
923
- const detectedType = mediaType ?? detectAudioMimeType(buffer);
924
- if (!detectedType) {
925
- throw new Error(
926
- "Could not detect audio MIME type. Please provide the mediaType parameter explicitly."
927
- );
928
- }
929
- return {
930
- type: "audio",
931
- source: {
932
- type: "base64",
933
- mediaType: detectedType,
934
- data: toBase64(buffer)
935
- }
936
- };
937
- }
938
- function isDataUrl(input) {
939
- return input.startsWith("data:");
940
- }
941
- function parseDataUrl(url) {
942
- const match = url.match(/^data:([^;]+);base64,(.+)$/);
943
- if (!match) return null;
944
- return { mimeType: match[1], data: match[2] };
945
- }
946
- var IMAGE_MAGIC_BYTES, AUDIO_MAGIC_BYTES;
947
- var init_input_content = __esm({
948
- "src/core/input-content.ts"() {
949
- "use strict";
950
- IMAGE_MAGIC_BYTES = [
951
- { bytes: [255, 216, 255], mimeType: "image/jpeg" },
952
- { bytes: [137, 80, 78, 71], mimeType: "image/png" },
953
- { bytes: [71, 73, 70, 56], mimeType: "image/gif" },
954
- // WebP starts with RIFF....WEBP
955
- { bytes: [82, 73, 70, 70], mimeType: "image/webp" }
956
- ];
957
- AUDIO_MAGIC_BYTES = [
958
- // MP3 frame sync
959
- { bytes: [255, 251], mimeType: "audio/mp3" },
960
- { bytes: [255, 250], mimeType: "audio/mp3" },
961
- // ID3 tag (MP3)
962
- { bytes: [73, 68, 51], mimeType: "audio/mp3" },
963
- // OGG
964
- { bytes: [79, 103, 103, 83], mimeType: "audio/ogg" },
965
- // WAV (RIFF)
966
- { bytes: [82, 73, 70, 70], mimeType: "audio/wav" },
967
- // WebM
968
- { bytes: [26, 69, 223, 163], mimeType: "audio/webm" },
969
- // FLAC (fLaC)
970
- { bytes: [102, 76, 97, 67], mimeType: "audio/flac" }
971
- ];
972
- }
973
- });
974
-
975
- // src/core/prompt-config.ts
976
- function resolvePromptTemplate(template, defaultValue, context) {
977
- const resolved = template ?? defaultValue;
978
- return typeof resolved === "function" ? resolved(context) : resolved;
979
- }
980
- function resolveRulesTemplate(rules, context) {
981
- const resolved = rules ?? DEFAULT_PROMPTS.rules;
982
- if (Array.isArray(resolved)) {
983
- return resolved;
984
- }
985
- if (typeof resolved === "function") {
986
- const result = resolved(context);
987
- return Array.isArray(result) ? result : [result];
988
- }
989
- return [resolved];
990
- }
991
- function resolveHintTemplate(template, defaultValue, context) {
992
- const resolved = template ?? defaultValue;
993
- if (typeof resolved === "function") {
994
- return resolved(context);
995
- }
996
- return resolved.replace(/\{iteration\}/g, String(context.iteration)).replace(/\{maxIterations\}/g, String(context.maxIterations)).replace(/\{remaining\}/g, String(context.remaining));
997
- }
998
- var DEFAULT_HINTS, DEFAULT_PROMPTS;
999
- var init_prompt_config = __esm({
1000
- "src/core/prompt-config.ts"() {
1001
- "use strict";
1002
- DEFAULT_HINTS = {
1003
- parallelGadgetsHint: "Tip: You can call multiple gadgets in a single response for efficiency.",
1004
- iterationProgressHint: "[Iteration {iteration}/{maxIterations}] Plan your actions accordingly."
1005
- };
1006
- DEFAULT_PROMPTS = {
1007
- mainInstruction: [
1008
- "\u26A0\uFE0F CRITICAL: RESPOND ONLY WITH GADGET INVOCATIONS",
1009
- "DO NOT use function calling or tool calling",
1010
- "You must output the exact text markers shown below in plain text.",
1011
- "EACH MARKER MUST START WITH A NEWLINE."
1012
- ].join("\n"),
1013
- criticalUsage: "INVOKE gadgets using the markers - do not describe what you want to do.",
1014
- formatDescription: (ctx) => `Parameters using ${ctx.argPrefix}name markers (value on next line(s), no escaping needed)`,
1015
- rules: () => [
1016
- "Output ONLY plain text with the exact markers - never use function/tool calling",
1017
- "You can invoke multiple gadgets in a single response",
1018
- "Gadgets without dependencies execute immediately (in parallel if multiple)",
1019
- "Use :invocation_id:dep1,dep2 syntax when a gadget needs results from prior gadgets",
1020
- "If any dependency fails, dependent gadgets are automatically skipped"
1021
- ],
1022
- customExamples: null
1023
- };
1024
- }
1025
- });
1026
-
1027
- // src/core/messages.ts
1028
- function normalizeMessageContent(content) {
1029
- if (typeof content === "string") {
1030
- return [{ type: "text", text: content }];
1031
- }
1032
- return content;
1033
- }
1034
- function extractMessageText(content) {
1035
- if (typeof content === "string") {
1036
- return content;
1037
- }
1038
- return content.filter((part) => part.type === "text").map((part) => part.text).join("");
1039
- }
1040
- var LLMMessageBuilder;
1041
- var init_messages = __esm({
1042
- "src/core/messages.ts"() {
1043
- "use strict";
1044
- init_constants();
1045
- init_input_content();
1046
- init_prompt_config();
1047
- LLMMessageBuilder = class {
1048
- messages = [];
1049
- startPrefix = GADGET_START_PREFIX;
1050
- endPrefix = GADGET_END_PREFIX;
1051
- argPrefix = GADGET_ARG_PREFIX;
1052
- promptConfig;
1053
- constructor(promptConfig) {
1054
- this.promptConfig = promptConfig ?? {};
1055
- }
1056
- /**
1057
- * Set custom prefixes for gadget markers.
1058
- * Used to configure history builder to match system prompt markers.
1059
- */
1060
- withPrefixes(startPrefix, endPrefix, argPrefix) {
1061
- this.startPrefix = startPrefix;
1062
- this.endPrefix = endPrefix;
1063
- if (argPrefix) {
1064
- this.argPrefix = argPrefix;
1065
- }
1066
- return this;
1067
- }
1068
- addSystem(content, metadata) {
1069
- this.messages.push({ role: "system", content, metadata });
1070
- return this;
1071
- }
1072
- addGadgets(gadgets, options) {
1073
- if (options?.startPrefix) {
1074
- this.startPrefix = options.startPrefix;
1075
- }
1076
- if (options?.endPrefix) {
1077
- this.endPrefix = options.endPrefix;
1078
- }
1079
- if (options?.argPrefix) {
1080
- this.argPrefix = options.argPrefix;
1081
- }
1082
- const context = {
1083
- startPrefix: this.startPrefix,
1084
- endPrefix: this.endPrefix,
1085
- argPrefix: this.argPrefix,
1086
- gadgetCount: gadgets.length,
1087
- gadgetNames: gadgets.map((g) => g.name ?? g.constructor.name)
1088
- };
1089
- const parts = [];
1090
- const mainInstruction = resolvePromptTemplate(
1091
- this.promptConfig.mainInstruction,
1092
- DEFAULT_PROMPTS.mainInstruction,
1093
- context
1094
- );
1095
- parts.push(mainInstruction);
1096
- parts.push(this.buildGadgetsSection(gadgets));
1097
- parts.push(this.buildUsageSection(context));
1098
- this.messages.push({ role: "system", content: parts.join("") });
1099
- return this;
1100
- }
1101
- buildGadgetsSection(gadgets) {
1102
- const parts = [];
1103
- parts.push("\n\nAVAILABLE GADGETS");
1104
- parts.push("\n=================\n");
1105
- for (const gadget of gadgets) {
1106
- const gadgetName = gadget.name ?? gadget.constructor.name;
1107
- const instruction = gadget.getInstruction({
1108
- argPrefix: this.argPrefix,
1109
- startPrefix: this.startPrefix,
1110
- endPrefix: this.endPrefix
1111
- });
1112
- const schemaMarker = "\n\nInput Schema (BLOCK):";
1113
- const schemaIndex = instruction.indexOf(schemaMarker);
1114
- const description = (schemaIndex !== -1 ? instruction.substring(0, schemaIndex) : instruction).trim();
1115
- const schema = schemaIndex !== -1 ? instruction.substring(schemaIndex + schemaMarker.length).trim() : "";
1116
- parts.push(`
1117
- GADGET: ${gadgetName}`);
1118
- parts.push(`
1119
- ${description}`);
1120
- if (schema) {
1121
- parts.push(`
1122
-
1123
- PARAMETERS (BLOCK):
1124
- ${schema}`);
1125
- }
1126
- parts.push("\n\n---");
1127
- }
1128
- return parts.join("");
1129
- }
1130
- buildUsageSection(context) {
1131
- const parts = [];
1132
- const formatDescription = resolvePromptTemplate(
1133
- this.promptConfig.formatDescription,
1134
- DEFAULT_PROMPTS.formatDescription,
1135
- context
1136
- );
1137
- parts.push("\n\nHOW TO INVOKE GADGETS");
1138
- parts.push("\n=====================\n");
1139
- const criticalUsage = resolvePromptTemplate(
1140
- this.promptConfig.criticalUsage,
1141
- DEFAULT_PROMPTS.criticalUsage,
1142
- context
1143
- );
1144
- parts.push(`
1145
- CRITICAL: ${criticalUsage}
1146
- `);
1147
- parts.push("\nFORMAT:");
1148
- parts.push(`
1149
- 1. Start marker: ${this.startPrefix}gadget_name`);
1150
- parts.push(`
1151
- With ID: ${this.startPrefix}gadget_name:my_id`);
1152
- parts.push(`
1153
- With dependencies: ${this.startPrefix}gadget_name:my_id:dep1,dep2`);
1154
- parts.push(`
1155
- 2. ${formatDescription}`);
1156
- parts.push(`
1157
- 3. End marker: ${this.endPrefix}`);
1158
- parts.push(this.buildExamplesSection(context));
1159
- parts.push(this.buildRulesSection(context));
1160
- parts.push("\n");
1161
- return parts.join("");
1162
- }
1163
- buildExamplesSection(context) {
1164
- if (this.promptConfig.customExamples) {
1165
- return this.promptConfig.customExamples(context);
1166
- }
1167
- const parts = [];
1168
- const singleExample = `${this.startPrefix}translate
1169
- ${this.argPrefix}from
1170
- English
1171
- ${this.argPrefix}to
1172
- Polish
1173
- ${this.argPrefix}content
1174
- Paris is the capital of France: a beautiful city.
1175
- ${this.endPrefix}`;
1176
- parts.push(`
1177
-
1178
- EXAMPLE (Single Gadget):
1179
-
1180
- ${singleExample}`);
1181
- const multipleExample = `${this.startPrefix}translate
1182
- ${this.argPrefix}from
1183
- English
1184
- ${this.argPrefix}to
1185
- Polish
1186
- ${this.argPrefix}content
1187
- Paris is the capital of France: a beautiful city.
1188
- ${this.endPrefix}
1189
- ${this.startPrefix}analyze
1190
- ${this.argPrefix}type
1191
- economic_analysis
1192
- ${this.argPrefix}matter
1193
- Polish Economy
1194
- ${this.argPrefix}question
1195
- Analyze the following:
1196
- - Polish arms exports 2025
1197
- - Economic implications
1198
- ${this.endPrefix}`;
1199
- parts.push(`
1200
-
1201
- EXAMPLE (Multiple Gadgets):
1202
-
1203
- ${multipleExample}`);
1204
- const dependencyExample = `${this.startPrefix}fetch_data:fetch_1
1205
- ${this.argPrefix}url
1206
- https://api.example.com/users
1207
- ${this.endPrefix}
1208
- ${this.startPrefix}fetch_data:fetch_2
1209
- ${this.argPrefix}url
1210
- https://api.example.com/orders
1211
- ${this.endPrefix}
1212
- ${this.startPrefix}merge_data:merge_1:fetch_1,fetch_2
1213
- ${this.argPrefix}format
1214
- json
1215
- ${this.endPrefix}`;
1216
- parts.push(`
1217
-
1218
- EXAMPLE (With Dependencies):
1219
- merge_1 waits for fetch_1 AND fetch_2 to complete.
1220
- If either fails, merge_1 is automatically skipped.
1221
-
1222
- ${dependencyExample}`);
1223
- parts.push(`
1224
-
1225
- BLOCK FORMAT SYNTAX:
1226
- Block format uses ${this.argPrefix}name markers. Values are captured verbatim until the next marker.
1227
-
1228
- ${this.argPrefix}filename
1229
- calculator.ts
1230
- ${this.argPrefix}code
1231
- class Calculator {
1232
- private history: string[] = [];
1233
-
1234
- add(a: number, b: number): number {
1235
- const result = a + b;
1236
- this.history.push(\`\${a} + \${b} = \${result}\`);
1237
- return result;
1238
- }
1239
- }
1240
-
1241
- BLOCK FORMAT RULES:
1242
- - Each parameter starts with ${this.argPrefix}parameterName on its own line
1243
- - The value starts on the NEXT line after the marker
1244
- - Value ends when the next ${this.argPrefix} or ${this.endPrefix} appears
1245
- - NO escaping needed - write values exactly as they should appear
1246
- - Perfect for code, JSON, markdown, or any content with special characters
1247
-
1248
- NESTED OBJECTS (use / separator):
1249
- ${this.argPrefix}config/timeout
1250
- 30
1251
- ${this.argPrefix}config/retries
1252
- 3
1253
- Produces: { "config": { "timeout": "30", "retries": "3" } }
1254
-
1255
- ARRAYS (use numeric indices):
1256
- ${this.argPrefix}items/0
1257
- first
1258
- ${this.argPrefix}items/1
1259
- second
1260
- Produces: { "items": ["first", "second"] }`);
1261
- return parts.join("");
1262
- }
1263
- buildRulesSection(context) {
1264
- const parts = [];
1265
- parts.push("\n\nRULES:");
1266
- const rules = resolveRulesTemplate(this.promptConfig.rules, context);
1267
- for (const rule of rules) {
1268
- parts.push(`
1269
- - ${rule}`);
1270
- }
1271
- return parts.join("");
1272
- }
1273
- /**
1274
- * Add a user message.
1275
- * Content can be a string (text only) or an array of content parts (multimodal).
1276
- *
1277
- * @param content - Message content
1278
- * @param metadata - Optional metadata
1279
- *
1280
- * @example
1281
- * ```typescript
1282
- * // Text only
1283
- * builder.addUser("Hello!");
1284
- *
1285
- * // Multimodal
1286
- * builder.addUser([
1287
- * text("What's in this image?"),
1288
- * imageFromBuffer(imageData),
1289
- * ]);
1290
- * ```
1291
- */
1292
- addUser(content, metadata) {
1293
- this.messages.push({ role: "user", content, metadata });
1294
- return this;
1295
- }
1296
- addAssistant(content, metadata) {
1297
- this.messages.push({ role: "assistant", content, metadata });
1298
- return this;
1299
- }
1300
- /**
1301
- * Add a user message with an image attachment.
1302
- *
1303
- * @param textContent - Text prompt
1304
- * @param imageData - Image data (Buffer, Uint8Array, or base64 string)
1305
- * @param mimeType - Optional MIME type (auto-detected if not provided)
1306
- *
1307
- * @example
1308
- * ```typescript
1309
- * builder.addUserWithImage(
1310
- * "What's in this image?",
1311
- * await fs.readFile("photo.jpg"),
1312
- * "image/jpeg" // Optional - auto-detected
1313
- * );
1314
- * ```
1315
- */
1316
- addUserWithImage(textContent, imageData, mimeType) {
1317
- const imageBuffer = typeof imageData === "string" ? Buffer.from(imageData, "base64") : imageData;
1318
- const detectedMime = mimeType ?? detectImageMimeType(imageBuffer);
1319
- if (!detectedMime) {
1320
- throw new Error(
1321
- "Could not detect image MIME type. Please provide the mimeType parameter explicitly."
1322
- );
1323
- }
1324
- const content = [
1325
- text(textContent),
1326
- {
1327
- type: "image",
1328
- source: {
1329
- type: "base64",
1330
- mediaType: detectedMime,
1331
- data: toBase64(imageBuffer)
1332
- }
1333
- }
1334
- ];
1335
- this.messages.push({ role: "user", content });
1336
- return this;
1337
- }
1338
- /**
1339
- * Add a user message with an image URL (OpenAI only).
1340
- *
1341
- * @param textContent - Text prompt
1342
- * @param imageUrl - URL to the image
1343
- *
1344
- * @example
1345
- * ```typescript
1346
- * builder.addUserWithImageUrl(
1347
- * "What's in this image?",
1348
- * "https://example.com/image.jpg"
1349
- * );
1350
- * ```
1351
- */
1352
- addUserWithImageUrl(textContent, imageUrl) {
1353
- const content = [text(textContent), imageFromUrl(imageUrl)];
1354
- this.messages.push({ role: "user", content });
1355
- return this;
1356
- }
1357
- /**
1358
- * Add a user message with an audio attachment (Gemini only).
1359
- *
1360
- * @param textContent - Text prompt
1361
- * @param audioData - Audio data (Buffer, Uint8Array, or base64 string)
1362
- * @param mimeType - Optional MIME type (auto-detected if not provided)
1363
- *
1364
- * @example
1365
- * ```typescript
1366
- * builder.addUserWithAudio(
1367
- * "Transcribe this audio",
1368
- * await fs.readFile("recording.mp3"),
1369
- * "audio/mp3" // Optional - auto-detected
1370
- * );
1371
- * ```
1372
- */
1373
- addUserWithAudio(textContent, audioData, mimeType) {
1374
- const audioBuffer = typeof audioData === "string" ? Buffer.from(audioData, "base64") : audioData;
1375
- const content = [text(textContent), audioFromBuffer(audioBuffer, mimeType)];
1376
- this.messages.push({ role: "user", content });
1377
- return this;
1378
- }
1379
- /**
1380
- * Add a user message with multiple content parts.
1381
- * Provides full flexibility for complex multimodal messages.
1382
- *
1383
- * @param parts - Array of content parts
1384
- *
1385
- * @example
1386
- * ```typescript
1387
- * builder.addUserMultimodal([
1388
- * text("Compare these images:"),
1389
- * imageFromBuffer(image1),
1390
- * imageFromBuffer(image2),
1391
- * ]);
1392
- * ```
1393
- */
1394
- addUserMultimodal(parts) {
1395
- this.messages.push({ role: "user", content: parts });
1396
- return this;
1397
- }
1398
- /**
1399
- * Record a gadget execution result in the message history.
1400
- * Creates an assistant message with the gadget invocation and a user message with the result.
1401
- *
1402
- * The invocationId is shown to the LLM so it can reference previous calls when building dependencies.
1403
- *
1404
- * @param gadget - Name of the gadget that was executed
1405
- * @param parameters - Parameters that were passed to the gadget
1406
- * @param result - Text result from the gadget execution
1407
- * @param invocationId - Invocation ID (shown to LLM so it can reference for dependencies)
1408
- * @param media - Optional media outputs from the gadget
1409
- * @param mediaIds - Optional IDs for the media outputs
1410
- * @param storedMedia - Optional stored media info including file paths
1411
- */
1412
- addGadgetCallResult(gadget, parameters, result, invocationId, media, mediaIds, storedMedia) {
1413
- const paramStr = this.formatBlockParameters(parameters, "");
1414
- this.messages.push({
1415
- role: "assistant",
1416
- content: `${this.startPrefix}${gadget}:${invocationId}
1417
- ${paramStr}
1418
- ${this.endPrefix}`
1419
- });
1420
- if (media && media.length > 0 && mediaIds && mediaIds.length > 0) {
1421
- const idRefs = media.map((m, i) => {
1422
- const path3 = storedMedia?.[i]?.path;
1423
- const pathInfo = path3 ? ` \u2192 saved to: ${path3}` : "";
1424
- return `[Media: ${mediaIds[i]} (${m.kind})${pathInfo}]`;
1425
- }).join("\n");
1426
- const textWithIds = `Result (${invocationId}): ${result}
1427
- ${idRefs}`;
1428
- const parts = [text(textWithIds)];
1429
- for (const item of media) {
1430
- if (item.kind === "image") {
1431
- parts.push(imageFromBase64(item.data, item.mimeType));
1432
- } else if (item.kind === "audio") {
1433
- parts.push(audioFromBase64(item.data, item.mimeType));
1434
- }
1435
- }
1436
- this.messages.push({ role: "user", content: parts });
1437
- } else {
1438
- this.messages.push({
1439
- role: "user",
1440
- content: `Result (${invocationId}): ${result}`
1441
- });
1442
- }
1443
- return this;
1444
- }
1445
- /**
1446
- * Format parameters as Block format with JSON Pointer paths.
1447
- * Uses the configured argPrefix for consistency with system prompt.
1448
- */
1449
- formatBlockParameters(params, prefix) {
1450
- const lines = [];
1451
- for (const [key, value] of Object.entries(params)) {
1452
- const fullPath = prefix ? `${prefix}/${key}` : key;
1453
- if (Array.isArray(value)) {
1454
- value.forEach((item, index) => {
1455
- const itemPath = `${fullPath}/${index}`;
1456
- if (typeof item === "object" && item !== null) {
1457
- lines.push(this.formatBlockParameters(item, itemPath));
1458
- } else {
1459
- lines.push(`${this.argPrefix}${itemPath}`);
1460
- lines.push(String(item));
1461
- }
1462
- });
1463
- } else if (typeof value === "object" && value !== null) {
1464
- lines.push(this.formatBlockParameters(value, fullPath));
1465
- } else {
1466
- lines.push(`${this.argPrefix}${fullPath}`);
1467
- lines.push(String(value));
1468
- }
1469
- }
1470
- return lines.join("\n");
1471
- }
1472
- build() {
1473
- return [...this.messages];
1474
- }
1475
- };
1476
- }
1477
- });
1478
-
1479
864
  // src/core/model-shortcuts.ts
1480
865
  function isKnownModelPattern(model) {
1481
866
  const normalized = model.toLowerCase();
@@ -2178,56 +1563,6 @@ var init_retry = __esm({
2178
1563
  }
2179
1564
  });
2180
1565
 
2181
- // src/gadgets/exceptions.ts
2182
- var TaskCompletionSignal, HumanInputRequiredException, TimeoutException, AbortException, BudgetPricingUnavailableError;
2183
- var init_exceptions = __esm({
2184
- "src/gadgets/exceptions.ts"() {
2185
- "use strict";
2186
- TaskCompletionSignal = class extends Error {
2187
- constructor(message) {
2188
- super(message ?? "Agent loop terminated by gadget");
2189
- this.name = "TaskCompletionSignal";
2190
- }
2191
- };
2192
- HumanInputRequiredException = class extends Error {
2193
- question;
2194
- constructor(question) {
2195
- super(`Human input required: ${question}`);
2196
- this.name = "HumanInputRequiredException";
2197
- this.question = question;
2198
- }
2199
- };
2200
- TimeoutException = class extends Error {
2201
- timeoutMs;
2202
- gadgetName;
2203
- constructor(gadgetName, timeoutMs) {
2204
- super(`Gadget '${gadgetName}' execution exceeded timeout of ${timeoutMs}ms`);
2205
- this.name = "TimeoutException";
2206
- this.gadgetName = gadgetName;
2207
- this.timeoutMs = timeoutMs;
2208
- }
2209
- };
2210
- AbortException = class extends Error {
2211
- constructor(message) {
2212
- super(message || "Gadget execution was aborted");
2213
- this.name = "AbortException";
2214
- }
2215
- };
2216
- BudgetPricingUnavailableError = class extends Error {
2217
- model;
2218
- budget;
2219
- constructor(model, budget) {
2220
- super(
2221
- `Budget of $${budget.toFixed(2)} was set but model "${model}" has no valid pricing information in the model registry. Either register pricing for this model via client.modelRegistry.registerModel() or remove the budget constraint.`
2222
- );
2223
- this.name = "BudgetPricingUnavailableError";
2224
- this.model = model;
2225
- this.budget = budget;
2226
- }
2227
- };
2228
- }
2229
- });
2230
-
2231
1566
  // src/gadgets/media-store.ts
2232
1567
  import { randomBytes } from "crypto";
2233
1568
  import { mkdir, rm, writeFile } from "fs/promises";
@@ -2427,128 +1762,8 @@ var init_media_store = __esm({
2427
1762
  this.initialized = false;
2428
1763
  }
2429
1764
  this.clear();
2430
- }
2431
- };
2432
- }
2433
- });
2434
-
2435
- // src/logging/logger.ts
2436
- import { createWriteStream, mkdirSync } from "fs";
2437
- import { dirname } from "path";
2438
- import { Logger } from "tslog";
2439
- function parseLogLevel(value) {
2440
- if (!value) {
2441
- return void 0;
2442
- }
2443
- const normalized = value.trim().toLowerCase();
2444
- if (normalized === "") {
2445
- return void 0;
2446
- }
2447
- const numericLevel = Number(normalized);
2448
- if (Number.isFinite(numericLevel)) {
2449
- return Math.max(0, Math.min(6, Math.floor(numericLevel)));
2450
- }
2451
- return LEVEL_NAME_TO_ID[normalized];
2452
- }
2453
- function parseEnvBoolean(value) {
2454
- if (!value) return void 0;
2455
- const normalized = value.trim().toLowerCase();
2456
- if (normalized === "true" || normalized === "1") return true;
2457
- if (normalized === "false" || normalized === "0") return false;
2458
- return void 0;
2459
- }
2460
- function stripAnsi(str) {
2461
- return str.replace(/\x1b\[[0-9;]*m/g, "");
2462
- }
2463
- function createLogger(options = {}) {
2464
- const envMinLevel = parseLogLevel(process.env.LLMIST_LOG_LEVEL);
2465
- const envLogFile = process.env.LLMIST_LOG_FILE?.trim() ?? "";
2466
- const envLogReset = parseEnvBoolean(process.env.LLMIST_LOG_RESET);
2467
- const minLevel = options.minLevel ?? envMinLevel ?? 4;
2468
- const defaultType = options.type ?? "pretty";
2469
- const name = options.name ?? "llmist";
2470
- const logReset = options.logReset ?? envLogReset ?? false;
2471
- const envLogTee = parseEnvBoolean(process.env.LLMIST_LOG_TEE);
2472
- const teeToConsole = options.teeToConsole ?? envLogTee ?? false;
2473
- if (envLogFile && (!logFileInitialized || sharedLogFilePath !== envLogFile)) {
2474
- try {
2475
- if (sharedLogFileStream) {
2476
- sharedLogFileStream.end();
2477
- sharedLogFileStream = void 0;
2478
- }
2479
- mkdirSync(dirname(envLogFile), { recursive: true });
2480
- const flags = logReset ? "w" : "a";
2481
- sharedLogFileStream = createWriteStream(envLogFile, { flags });
2482
- sharedLogFilePath = envLogFile;
2483
- logFileInitialized = true;
2484
- writeErrorCount = 0;
2485
- writeErrorReported = false;
2486
- sharedLogFileStream.on("error", (error) => {
2487
- writeErrorCount++;
2488
- if (!writeErrorReported) {
2489
- console.error(`[llmist] Log file write error: ${error.message}`);
2490
- writeErrorReported = true;
2491
- }
2492
- if (writeErrorCount >= MAX_WRITE_ERRORS_BEFORE_DISABLE) {
2493
- console.error(
2494
- `[llmist] Too many log file errors (${writeErrorCount}), disabling file logging`
2495
- );
2496
- sharedLogFileStream?.end();
2497
- sharedLogFileStream = void 0;
2498
- }
2499
- });
2500
- } catch (error) {
2501
- console.error("Failed to initialize LLMIST_LOG_FILE output:", error);
2502
- }
2503
- }
2504
- const useFileLogging = Boolean(sharedLogFileStream);
2505
- const logger2 = new Logger({
2506
- name,
2507
- minLevel,
2508
- type: useFileLogging ? "pretty" : defaultType,
2509
- // Hide log position for file logging and non-pretty types
2510
- hideLogPositionForProduction: useFileLogging || defaultType !== "pretty",
2511
- prettyLogTemplate: LOG_TEMPLATE,
2512
- // Use overwrite to redirect tslog's formatted output to file instead of console
2513
- overwrite: useFileLogging ? {
2514
- transportFormatted: (logMetaMarkup, logArgs, _logErrors) => {
2515
- const args = logArgs.map(
2516
- (arg) => typeof arg === "string" ? arg : JSON.stringify(arg)
2517
- );
2518
- if (sharedLogFileStream) {
2519
- const meta = stripAnsi(logMetaMarkup);
2520
- const fileArgs = args.map((a) => stripAnsi(a));
2521
- sharedLogFileStream.write(`${meta}${fileArgs.join(" ")}
2522
- `);
2523
- }
2524
- if (teeToConsole) {
2525
- process.stdout.write(`${logMetaMarkup}${args.join(" ")}
2526
- `);
2527
- }
2528
- }
2529
- } : void 0
2530
- });
2531
- return logger2;
2532
- }
2533
- var LEVEL_NAME_TO_ID, sharedLogFilePath, sharedLogFileStream, logFileInitialized, writeErrorCount, writeErrorReported, MAX_WRITE_ERRORS_BEFORE_DISABLE, LOG_TEMPLATE, defaultLogger;
2534
- var init_logger = __esm({
2535
- "src/logging/logger.ts"() {
2536
- "use strict";
2537
- LEVEL_NAME_TO_ID = {
2538
- silly: 0,
2539
- trace: 1,
2540
- debug: 2,
2541
- info: 3,
2542
- warn: 4,
2543
- error: 5,
2544
- fatal: 6
1765
+ }
2545
1766
  };
2546
- logFileInitialized = false;
2547
- writeErrorCount = 0;
2548
- writeErrorReported = false;
2549
- MAX_WRITE_ERRORS_BEFORE_DISABLE = 5;
2550
- LOG_TEMPLATE = "{{yyyy}}-{{mm}}-{{dd}} {{hh}}:{{MM}}:{{ss}}:{{ms}} {{logLevelName}} [{{name}}] ";
2551
- defaultLogger = createLogger();
2552
1767
  }
2553
1768
  });
2554
1769
 
@@ -3066,6 +2281,16 @@ var init_conversation_manager = __esm({
3066
2281
  getBaseMessages() {
3067
2282
  return [...this.baseMessages, ...this.initialMessages];
3068
2283
  }
2284
+ /**
2285
+ * Replace the base (system + gadget catalog) messages.
2286
+ *
2287
+ * Used when async setup (e.g. MCP server connect-and-list) discovers
2288
+ * additional gadgets after the agent was constructed. Conversation history
2289
+ * is preserved; only the leading system block is swapped.
2290
+ */
2291
+ replaceBaseMessages(newBase) {
2292
+ this.baseMessages = newBase;
2293
+ }
3069
2294
  replaceHistory(newHistory) {
3070
2295
  this.historyBuilder = new LLMMessageBuilder();
3071
2296
  if (this.startPrefix && this.endPrefix) {
@@ -3995,665 +3220,82 @@ var init_llm_call_lifecycle = __esm({
3995
3220
  if (this.caching !== void 0) return this.caching;
3996
3221
  return { enabled: true };
3997
3222
  }
3998
- /**
3999
- * Calculate cost and complete LLM call in execution tree.
4000
- * Also records usage to rate limit tracker for proactive throttling.
4001
- */
4002
- completeLLMCallInTree(nodeId, result) {
4003
- const inputTokens = result.usage?.inputTokens ?? 0;
4004
- const outputTokens = result.usage?.outputTokens ?? 0;
4005
- if (this.rateLimitTracker) {
4006
- this.rateLimitTracker.recordUsage(inputTokens, outputTokens);
4007
- }
4008
- const llmCost = this.client.modelRegistry?.estimateCost?.(
4009
- this.model,
4010
- inputTokens,
4011
- outputTokens,
4012
- result.usage?.cachedInputTokens ?? 0,
4013
- result.usage?.cacheCreationInputTokens ?? 0,
4014
- result.usage?.reasoningTokens ?? 0
4015
- )?.totalCost;
4016
- this.tree.completeLLMCall(nodeId, {
4017
- response: result.rawResponse,
4018
- usage: result.usage,
4019
- finishReason: result.finishReason,
4020
- cost: llmCost,
4021
- thinkingContent: result.thinkingContent
4022
- });
4023
- }
4024
- /**
4025
- * Process afterLLMCall controller and return modified final message.
4026
- *
4027
- * Skips controller invocation for interrupted calls (`finishReason === "interrupted"`)
4028
- * to preserve the original Agent behavior: the `afterLLMCall` controller was never
4029
- * invoked for interrupted calls in the pre-extraction code. Skipping it here prevents
4030
- * side effects (e.g., `append_messages` mutating conversation state) during cleanup
4031
- * of an already-exited run loop.
4032
- */
4033
- async processAfterLLMCallController(iteration, llmOptions, result, gadgetCallCount) {
4034
- let finalMessage = result.finalMessage;
4035
- if (!this.hooks.controllers?.afterLLMCall || result.finishReason === "interrupted") {
4036
- return finalMessage;
4037
- }
4038
- const context = {
4039
- iteration,
4040
- maxIterations: this.maxIterations,
4041
- budget: this.budget,
4042
- totalCost: this.tree.getTotalCost(),
4043
- options: llmOptions,
4044
- finishReason: result.finishReason,
4045
- usage: result.usage,
4046
- finalMessage: result.finalMessage,
4047
- gadgetCallCount,
4048
- logger: this.logger
4049
- };
4050
- const action = await this.hooks.controllers.afterLLMCall(context);
4051
- validateAfterLLMCallAction(action);
4052
- if (action.action === "modify_and_continue" || action.action === "append_and_modify") {
4053
- finalMessage = action.modifiedMessage;
4054
- }
4055
- if (action.action === "append_messages" || action.action === "append_and_modify") {
4056
- for (const msg of action.messages) {
4057
- if (msg.role === "user") {
4058
- this.conversation.addUserMessage(msg.content);
4059
- } else if (msg.role === "assistant") {
4060
- this.conversation.addAssistantMessage(extractMessageText(msg.content));
4061
- } else if (msg.role === "system") {
4062
- this.conversation.addUserMessage(`[System] ${extractMessageText(msg.content)}`);
4063
- }
4064
- }
4065
- }
4066
- return finalMessage;
4067
- }
4068
- };
4069
- }
4070
- });
4071
-
4072
- // src/gadgets/schema-to-json.ts
4073
- import * as z from "zod";
4074
- function schemaToJSONSchema(schema, options) {
4075
- const jsonSchema = z.toJSONSchema(schema, options ?? { target: "draft-7" });
4076
- const mismatches = detectDescriptionMismatch(schema, jsonSchema);
4077
- if (mismatches.length > 0) {
4078
- defaultLogger.warn(
4079
- `Zod instance mismatch detected: ${mismatches.length} description(s) lost. For best results, use: import { z } from "llmist"`
4080
- );
4081
- return mergeDescriptions(schema, jsonSchema);
4082
- }
4083
- return jsonSchema;
4084
- }
4085
- function detectDescriptionMismatch(schema, jsonSchema) {
4086
- const mismatches = [];
4087
- function checkSchema(zodSchema, json, path3) {
4088
- if (!zodSchema || typeof zodSchema !== "object") return;
4089
- const def = zodSchema._def;
4090
- const jsonObj = json;
4091
- if (def?.description && !jsonObj?.description) {
4092
- mismatches.push(path3 || "root");
4093
- }
4094
- if (def?.typeName === "ZodObject" && def?.shape) {
4095
- const shape = typeof def.shape === "function" ? def.shape() : def.shape;
4096
- for (const [key, fieldSchema] of Object.entries(shape)) {
4097
- const properties = jsonObj?.properties;
4098
- const jsonProp = properties?.[key];
4099
- checkSchema(fieldSchema, jsonProp, path3 ? `${path3}.${key}` : key);
4100
- }
4101
- }
4102
- if (def?.typeName === "ZodArray" && def?.type) {
4103
- checkSchema(def.type, jsonObj?.items, path3 ? `${path3}[]` : "[]");
4104
- }
4105
- if ((def?.typeName === "ZodOptional" || def?.typeName === "ZodNullable") && def?.innerType) {
4106
- checkSchema(def.innerType, json, path3);
4107
- }
4108
- if (def?.typeName === "ZodDefault" && def?.innerType) {
4109
- checkSchema(def.innerType, json, path3);
4110
- }
4111
- }
4112
- checkSchema(schema, jsonSchema, "");
4113
- return mismatches;
4114
- }
4115
- function mergeDescriptions(schema, jsonSchema) {
4116
- function merge(zodSchema, json) {
4117
- if (!json || typeof json !== "object") return json;
4118
- const def = zodSchema._def;
4119
- const jsonObj = json;
4120
- const merged = { ...jsonObj };
4121
- if (def?.description && !jsonObj.description) {
4122
- merged.description = def.description;
4123
- }
4124
- if (def?.typeName === "ZodObject" && def?.shape && jsonObj.properties) {
4125
- const shape = typeof def.shape === "function" ? def.shape() : def.shape;
4126
- const properties = jsonObj.properties;
4127
- merged.properties = { ...properties };
4128
- for (const [key, fieldSchema] of Object.entries(shape)) {
4129
- if (properties[key]) {
4130
- merged.properties[key] = merge(fieldSchema, properties[key]);
4131
- }
4132
- }
4133
- }
4134
- if (def?.typeName === "ZodArray" && def?.type && jsonObj.items) {
4135
- merged.items = merge(def.type, jsonObj.items);
4136
- }
4137
- if ((def?.typeName === "ZodOptional" || def?.typeName === "ZodNullable") && def?.innerType) {
4138
- return merge(def.innerType, json);
4139
- }
4140
- if (def?.typeName === "ZodDefault" && def?.innerType) {
4141
- return merge(def.innerType, json);
4142
- }
4143
- return merged;
4144
- }
4145
- return merge(schema, jsonSchema);
4146
- }
4147
- var init_schema_to_json = __esm({
4148
- "src/gadgets/schema-to-json.ts"() {
4149
- "use strict";
4150
- init_logger();
4151
- }
4152
- });
4153
-
4154
- // src/gadgets/schema-validator.ts
4155
- import * as z2 from "zod";
4156
- function validateGadgetSchema(schema, gadgetName) {
4157
- let jsonSchema;
4158
- try {
4159
- jsonSchema = z2.toJSONSchema(schema, { target: "draft-7" });
4160
- } catch (error) {
4161
- const errorMessage = error instanceof Error ? error.message : String(error);
4162
- throw new Error(
4163
- `Gadget "${gadgetName}" has a schema that cannot be serialized to JSON Schema.
4164
- This usually happens with unsupported patterns like:
4165
- - z.record() - use z.object({}).passthrough() instead
4166
- - Complex transforms or custom refinements
4167
- - Circular references
4168
-
4169
- Original error: ${errorMessage}
4170
-
4171
- Only use schema patterns that Zod v4's native toJSONSchema() supports.`
4172
- );
4173
- }
4174
- const issues = findUnknownTypes(jsonSchema);
4175
- if (issues.length > 0) {
4176
- const fieldList = issues.join(", ");
4177
- throw new Error(
4178
- `Gadget "${gadgetName}" uses z.unknown() which produces incomplete schemas.
4179
- Problematic fields: ${fieldList}
4180
-
4181
- z.unknown() doesn't generate type information in JSON Schema, making it unclear
4182
- to the LLM what data structure to provide.
4183
-
4184
- Suggestions:
4185
- - Use z.object({}).passthrough() for flexible objects
4186
- - Use z.record(z.string()) for key-value objects with string values
4187
- - Define specific structure if possible
4188
-
4189
- Example fixes:
4190
- // \u274C Bad
4191
- content: z.unknown()
4192
-
4193
- // \u2705 Good
4194
- content: z.object({}).passthrough() // for flexible objects
4195
- content: z.record(z.string()) // for key-value objects
4196
- content: z.array(z.string()) // for arrays of strings
4197
- `
4198
- );
4199
- }
4200
- }
4201
- function findUnknownTypes(schema, path3 = []) {
4202
- const issues = [];
4203
- if (!schema || typeof schema !== "object") {
4204
- return issues;
4205
- }
4206
- if (schema.definitions) {
4207
- for (const defSchema of Object.values(schema.definitions)) {
4208
- issues.push(...findUnknownTypes(defSchema, []));
4209
- }
4210
- }
4211
- if (schema.properties) {
4212
- for (const [propName, propSchema] of Object.entries(schema.properties)) {
4213
- const propPath = [...path3, propName];
4214
- if (hasNoType(propSchema)) {
4215
- issues.push(propPath.join(".") || propName);
4216
- }
4217
- issues.push(...findUnknownTypes(propSchema, propPath));
4218
- }
4219
- }
4220
- if (schema.items) {
4221
- const itemPath = [...path3, "[]"];
4222
- if (hasNoType(schema.items)) {
4223
- issues.push(itemPath.join("."));
4224
- }
4225
- issues.push(...findUnknownTypes(schema.items, itemPath));
4226
- }
4227
- if (schema.anyOf) {
4228
- schema.anyOf.forEach((subSchema, index) => {
4229
- issues.push(...findUnknownTypes(subSchema, [...path3, `anyOf[${index}]`]));
4230
- });
4231
- }
4232
- if (schema.oneOf) {
4233
- schema.oneOf.forEach((subSchema, index) => {
4234
- issues.push(...findUnknownTypes(subSchema, [...path3, `oneOf[${index}]`]));
4235
- });
4236
- }
4237
- if (schema.allOf) {
4238
- schema.allOf.forEach((subSchema, index) => {
4239
- issues.push(...findUnknownTypes(subSchema, [...path3, `allOf[${index}]`]));
4240
- });
4241
- }
4242
- return issues;
4243
- }
4244
- function hasNoType(prop) {
4245
- if (!prop || typeof prop !== "object") {
4246
- return false;
4247
- }
4248
- const hasType = prop.type !== void 0;
4249
- const hasRef = prop.$ref !== void 0;
4250
- const hasUnion = prop.anyOf !== void 0 || prop.oneOf !== void 0 || prop.allOf !== void 0;
4251
- if (hasType || hasRef || hasUnion) {
4252
- return false;
4253
- }
4254
- const keys = Object.keys(prop);
4255
- const metadataKeys = ["description", "title", "default", "examples"];
4256
- const hasOnlyMetadata = keys.every((key) => metadataKeys.includes(key));
4257
- return hasOnlyMetadata || keys.length === 0;
4258
- }
4259
- var init_schema_validator = __esm({
4260
- "src/gadgets/schema-validator.ts"() {
4261
- "use strict";
4262
- }
4263
- });
4264
-
4265
- // src/gadgets/gadget.ts
4266
- function formatParamsForBlockExample(params, prefix = "", argPrefix = GADGET_ARG_PREFIX) {
4267
- const lines = [];
4268
- for (const [key, value] of Object.entries(params)) {
4269
- const fullPath = prefix ? `${prefix}/${key}` : key;
4270
- if (Array.isArray(value)) {
4271
- value.forEach((item, index) => {
4272
- const itemPath = `${fullPath}/${index}`;
4273
- if (typeof item === "object" && item !== null) {
4274
- lines.push(
4275
- formatParamsForBlockExample(item, itemPath, argPrefix)
4276
- );
4277
- } else {
4278
- lines.push(`${argPrefix}${itemPath}`);
4279
- lines.push(String(item));
4280
- }
4281
- });
4282
- } else if (typeof value === "object" && value !== null) {
4283
- lines.push(
4284
- formatParamsForBlockExample(value, fullPath, argPrefix)
4285
- );
4286
- } else {
4287
- lines.push(`${argPrefix}${fullPath}`);
4288
- lines.push(String(value));
4289
- }
4290
- }
4291
- return lines.join("\n");
4292
- }
4293
- function formatParamLine(key, propObj, isRequired, indent = "") {
4294
- const type = propObj.type;
4295
- const description = propObj.description;
4296
- const enumValues = propObj.enum;
4297
- let line = `${indent}- ${key}`;
4298
- if (type === "array") {
4299
- const items = propObj.items;
4300
- const itemType = items?.type || "any";
4301
- line += ` (array of ${itemType})`;
4302
- } else if (type === "object" && propObj.properties) {
4303
- line += " (object)";
4304
- } else {
4305
- line += ` (${type})`;
4306
- }
4307
- if (isRequired && indent !== "") {
4308
- line += " [required]";
4309
- }
4310
- if (description) {
4311
- line += `: ${description}`;
4312
- }
4313
- if (enumValues) {
4314
- line += ` - one of: ${enumValues.map((v) => `"${v}"`).join(", ")}`;
4315
- }
4316
- return line;
4317
- }
4318
- function formatSchemaAsPlainText(schema, indent = "", atRoot = true) {
4319
- const lines = [];
4320
- const properties = schema.properties || {};
4321
- const required = schema.required || [];
4322
- if (atRoot && indent === "") {
4323
- const requiredProps = [];
4324
- const optionalProps = [];
4325
- for (const [key, prop] of Object.entries(properties)) {
4326
- if (required.includes(key)) {
4327
- requiredProps.push([key, prop]);
4328
- } else {
4329
- optionalProps.push([key, prop]);
4330
- }
4331
- }
4332
- const reqCount = requiredProps.length;
4333
- const optCount = optionalProps.length;
4334
- if (reqCount > 0 || optCount > 0) {
4335
- const parts = [];
4336
- if (reqCount > 0) parts.push(`${reqCount} required`);
4337
- if (optCount > 0) parts.push(`${optCount} optional`);
4338
- lines.push(parts.join(", "));
4339
- lines.push("");
4340
- }
4341
- if (reqCount > 0) {
4342
- lines.push("REQUIRED Parameters:");
4343
- for (const [key, prop] of requiredProps) {
4344
- lines.push(formatParamLine(key, prop, true, ""));
4345
- const propObj = prop;
4346
- if (propObj.type === "object" && propObj.properties) {
4347
- lines.push(formatSchemaAsPlainText(propObj, " ", false));
4348
- }
4349
- }
4350
- }
4351
- if (optCount > 0) {
4352
- if (reqCount > 0) lines.push("");
4353
- lines.push("OPTIONAL Parameters:");
4354
- for (const [key, prop] of optionalProps) {
4355
- lines.push(formatParamLine(key, prop, false, ""));
4356
- const propObj = prop;
4357
- if (propObj.type === "object" && propObj.properties) {
4358
- lines.push(formatSchemaAsPlainText(propObj, " ", false));
4359
- }
4360
- }
4361
- }
4362
- return lines.join("\n");
4363
- }
4364
- for (const [key, prop] of Object.entries(properties)) {
4365
- const isRequired = required.includes(key);
4366
- lines.push(formatParamLine(key, prop, isRequired, indent));
4367
- const propObj = prop;
4368
- if (propObj.type === "object" && propObj.properties) {
4369
- lines.push(formatSchemaAsPlainText(propObj, indent + " ", false));
4370
- }
4371
- }
4372
- return lines.join("\n");
4373
- }
4374
- var AbstractGadget;
4375
- var init_gadget = __esm({
4376
- "src/gadgets/gadget.ts"() {
4377
- "use strict";
4378
- init_constants();
4379
- init_exceptions();
4380
- init_schema_to_json();
4381
- init_schema_validator();
4382
- AbstractGadget = class {
4383
- /**
4384
- * The name of the gadget. Used for identification when LLM calls it.
4385
- * If not provided, defaults to the class name.
4386
- */
4387
- name;
4388
- /**
4389
- * Optional Zod schema describing the expected input payload. When provided,
4390
- * it will be validated before execution and transformed into a JSON Schema
4391
- * representation that is surfaced to the LLM as part of the instructions.
4392
- */
4393
- parameterSchema;
4394
- /**
4395
- * Optional timeout in milliseconds for gadget execution.
4396
- * If execution exceeds this timeout, a TimeoutException will be thrown.
4397
- * If not set, the global defaultGadgetTimeoutMs from runtime options will be used.
4398
- * Set to 0 or undefined to disable timeout for this gadget.
4399
- */
4400
- timeoutMs;
4401
- /**
4402
- * Optional usage examples to help LLMs understand proper invocation.
4403
- * Examples are rendered in getInstruction() alongside the schema.
4404
- *
4405
- * Note: Uses broader `unknown` type to allow typed examples from subclasses
4406
- * while maintaining runtime compatibility.
4407
- */
4408
- examples;
4409
- /**
4410
- * Maximum number of concurrent executions allowed for this gadget.
4411
- * Use this to prevent race conditions in gadgets that modify shared state.
4412
- *
4413
- * - `1` = Sequential execution (only one instance runs at a time)
4414
- * - `0` or `undefined` = Unlimited concurrency (default)
4415
- * - `N > 1` = At most N concurrent executions
4416
- *
4417
- * This property sets a safety floor: external configuration (SubagentConfig)
4418
- * can only make concurrency MORE restrictive, never less. For example, if
4419
- * a gadget declares `maxConcurrent: 1`, external config cannot override it
4420
- * to allow parallel execution.
4421
- *
4422
- * @example
4423
- * ```typescript
4424
- * // File writer that must run sequentially to avoid race conditions
4425
- * class WriteFile extends Gadget({
4426
- * description: 'Writes content to a file',
4427
- * schema: z.object({ path: z.string(), content: z.string() }),
4428
- * maxConcurrent: 1, // Sequential - prevents race conditions
4429
- * }) {
4430
- * execute(params: this['params']) { ... }
4431
- * }
4432
- * ```
4433
- */
4434
- maxConcurrent;
4435
- /**
4436
- * If true, this gadget must execute alone — no other gadgets in the same
4437
- * LLM response can run in parallel. When an exclusive gadget arrives and
4438
- * other gadgets are already in-flight, it is deferred until they complete.
4439
- *
4440
- * Use for gadgets that terminate the agent loop (e.g., Finish), where
4441
- * sibling tool results must be visible to the LLM before the loop ends.
4442
- *
4443
- * This is a safety floor: external config cannot weaken it.
4444
- */
4445
- exclusive;
4446
- /**
4447
- * Throws an AbortException if the execution has been aborted.
4448
- *
4449
- * Call this at key checkpoints in long-running gadgets to allow early exit
4450
- * when the gadget has been cancelled (e.g., due to timeout). This enables
4451
- * resource cleanup and prevents unnecessary work after cancellation.
4452
- *
4453
- * @param ctx - The execution context containing the abort signal
4454
- * @throws AbortException if ctx.signal.aborted is true
4455
- *
4456
- * @example
4457
- * ```typescript
4458
- * class DataProcessor extends Gadget({
4459
- * description: 'Processes data in multiple steps',
4460
- * schema: z.object({ items: z.array(z.string()) }),
4461
- * }) {
4462
- * async execute(params: this['params'], ctx?: ExecutionContext): Promise<string> {
4463
- * const results: string[] = [];
4464
- *
4465
- * for (const item of params.items) {
4466
- * // Check before each expensive operation
4467
- * this.throwIfAborted(ctx);
4468
- *
4469
- * results.push(await this.processItem(item));
4470
- * }
4471
- *
4472
- * return results.join(', ');
4473
- * }
4474
- * }
4475
- * ```
4476
- */
4477
- throwIfAborted(ctx) {
4478
- if (ctx?.signal?.aborted) {
4479
- throw new AbortException();
4480
- }
4481
- }
4482
- /**
4483
- * Register a cleanup function to run when execution is aborted (timeout or cancellation).
4484
- * The cleanup function is called immediately if the signal is already aborted.
4485
- * Errors thrown by the cleanup function are silently ignored.
4486
- *
4487
- * Use this to clean up resources like browser instances, database connections,
4488
- * or child processes when the gadget is cancelled due to timeout.
4489
- *
4490
- * @param ctx - The execution context containing the abort signal
4491
- * @param cleanup - Function to run on abort (can be sync or async)
4492
- *
4493
- * @example
4494
- * ```typescript
4495
- * class BrowserGadget extends Gadget({
4496
- * description: 'Fetches web page content',
4497
- * schema: z.object({ url: z.string() }),
4498
- * }) {
4499
- * async execute(params: this['params'], ctx?: ExecutionContext): Promise<string> {
4500
- * const browser = await chromium.launch();
4501
- * this.onAbort(ctx, () => browser.close());
4502
- *
4503
- * const page = await browser.newPage();
4504
- * this.onAbort(ctx, () => page.close());
4505
- *
4506
- * await page.goto(params.url);
4507
- * const content = await page.content();
4508
- *
4509
- * await browser.close();
4510
- * return content;
4511
- * }
4512
- * }
4513
- * ```
4514
- */
4515
- onAbort(ctx, cleanup) {
4516
- if (!ctx?.signal) return;
4517
- const safeCleanup = () => {
4518
- try {
4519
- const result = cleanup();
4520
- if (result && typeof result === "object" && "catch" in result) {
4521
- result.catch(() => {
4522
- });
4523
- }
4524
- } catch {
4525
- }
4526
- };
4527
- if (ctx.signal.aborted) {
4528
- safeCleanup();
4529
- return;
4530
- }
4531
- ctx.signal.addEventListener("abort", safeCleanup, { once: true });
4532
- }
4533
- /**
4534
- * Create an AbortController linked to the execution context's signal.
4535
- * When the parent signal aborts, the returned controller also aborts with the same reason.
4536
- *
4537
- * Useful for passing abort signals to child operations like fetch() while still
4538
- * being able to abort them independently if needed.
4539
- *
4540
- * @param ctx - The execution context containing the parent abort signal
4541
- * @returns A new AbortController linked to the parent signal
4542
- *
4543
- * @example
4544
- * ```typescript
4545
- * class FetchGadget extends Gadget({
4546
- * description: 'Fetches data from URL',
4547
- * schema: z.object({ url: z.string() }),
4548
- * }) {
4549
- * async execute(params: this['params'], ctx?: ExecutionContext): Promise<string> {
4550
- * const controller = this.createLinkedAbortController(ctx);
4551
- *
4552
- * // fetch() will automatically abort when parent times out
4553
- * const response = await fetch(params.url, { signal: controller.signal });
4554
- * return response.text();
4555
- * }
4556
- * }
4557
- * ```
3223
+ /**
3224
+ * Calculate cost and complete LLM call in execution tree.
3225
+ * Also records usage to rate limit tracker for proactive throttling.
4558
3226
  */
4559
- createLinkedAbortController(ctx) {
4560
- const controller = new AbortController();
4561
- if (ctx?.signal) {
4562
- if (ctx.signal.aborted) {
4563
- controller.abort(ctx.signal.reason);
4564
- } else {
4565
- ctx.signal.addEventListener(
4566
- "abort",
4567
- () => {
4568
- controller.abort(ctx.signal.reason);
4569
- },
4570
- { once: true }
4571
- );
4572
- }
3227
+ completeLLMCallInTree(nodeId, result) {
3228
+ const inputTokens = result.usage?.inputTokens ?? 0;
3229
+ const outputTokens = result.usage?.outputTokens ?? 0;
3230
+ if (this.rateLimitTracker) {
3231
+ this.rateLimitTracker.recordUsage(inputTokens, outputTokens);
4573
3232
  }
4574
- return controller;
3233
+ const llmCost = this.client.modelRegistry?.estimateCost?.(
3234
+ this.model,
3235
+ inputTokens,
3236
+ outputTokens,
3237
+ result.usage?.cachedInputTokens ?? 0,
3238
+ result.usage?.cacheCreationInputTokens ?? 0,
3239
+ result.usage?.reasoningTokens ?? 0
3240
+ )?.totalCost;
3241
+ this.tree.completeLLMCall(nodeId, {
3242
+ response: result.rawResponse,
3243
+ usage: result.usage,
3244
+ finishReason: result.finishReason,
3245
+ cost: llmCost,
3246
+ thinkingContent: result.thinkingContent
3247
+ });
4575
3248
  }
4576
3249
  /**
4577
- * Generate instruction text for the LLM.
4578
- * Combines name, description, and parameter schema into a formatted instruction.
3250
+ * Process afterLLMCall controller and return modified final message.
4579
3251
  *
4580
- * @param optionsOrArgPrefix - Optional custom prefixes for examples, or just argPrefix string for backwards compatibility
4581
- * @returns Formatted instruction string
3252
+ * Skips controller invocation for interrupted calls (`finishReason === "interrupted"`)
3253
+ * to preserve the original Agent behavior: the `afterLLMCall` controller was never
3254
+ * invoked for interrupted calls in the pre-extraction code. Skipping it here prevents
3255
+ * side effects (e.g., `append_messages` mutating conversation state) during cleanup
3256
+ * of an already-exited run loop.
4582
3257
  */
4583
- getInstruction(optionsOrArgPrefix) {
4584
- const options = typeof optionsOrArgPrefix === "string" ? { argPrefix: optionsOrArgPrefix } : optionsOrArgPrefix;
4585
- const parts = [];
4586
- parts.push(this.description);
4587
- if (this.parameterSchema) {
4588
- const gadgetName = this.name ?? this.constructor.name;
4589
- validateGadgetSchema(this.parameterSchema, gadgetName);
4590
- const jsonSchema = schemaToJSONSchema(this.parameterSchema, {
4591
- target: "draft-7"
4592
- });
4593
- parts.push("\n\nParameters:");
4594
- parts.push(formatSchemaAsPlainText(jsonSchema));
4595
- }
4596
- if (this.examples && this.examples.length > 0) {
4597
- parts.push("\n\nExamples:");
4598
- const effectiveArgPrefix = options?.argPrefix ?? GADGET_ARG_PREFIX;
4599
- const effectiveStartPrefix = options?.startPrefix ?? GADGET_START_PREFIX;
4600
- const effectiveEndPrefix = options?.endPrefix ?? GADGET_END_PREFIX;
4601
- const gadgetName = this.name || this.constructor.name;
4602
- this.examples.forEach((example, index) => {
4603
- if (index > 0) {
4604
- parts.push("");
4605
- parts.push("---");
4606
- parts.push("");
4607
- }
4608
- if (example.comment) {
4609
- parts.push(`# ${example.comment}`);
4610
- }
4611
- parts.push(`${effectiveStartPrefix}${gadgetName}`);
4612
- parts.push(
4613
- formatParamsForBlockExample(
4614
- example.params,
4615
- "",
4616
- effectiveArgPrefix
4617
- )
4618
- );
4619
- parts.push(effectiveEndPrefix);
4620
- if (example.output !== void 0) {
4621
- parts.push("");
4622
- parts.push("Expected Output:");
4623
- parts.push(example.output);
3258
+ async processAfterLLMCallController(iteration, llmOptions, result, gadgetCallCount) {
3259
+ let finalMessage = result.finalMessage;
3260
+ if (!this.hooks.controllers?.afterLLMCall || result.finishReason === "interrupted") {
3261
+ return finalMessage;
3262
+ }
3263
+ const context = {
3264
+ iteration,
3265
+ maxIterations: this.maxIterations,
3266
+ budget: this.budget,
3267
+ totalCost: this.tree.getTotalCost(),
3268
+ options: llmOptions,
3269
+ finishReason: result.finishReason,
3270
+ usage: result.usage,
3271
+ finalMessage: result.finalMessage,
3272
+ gadgetCallCount,
3273
+ logger: this.logger
3274
+ };
3275
+ const action = await this.hooks.controllers.afterLLMCall(context);
3276
+ validateAfterLLMCallAction(action);
3277
+ if (action.action === "modify_and_continue" || action.action === "append_and_modify") {
3278
+ finalMessage = action.modifiedMessage;
3279
+ }
3280
+ if (action.action === "append_messages" || action.action === "append_and_modify") {
3281
+ for (const msg of action.messages) {
3282
+ if (msg.role === "user") {
3283
+ this.conversation.addUserMessage(msg.content);
3284
+ } else if (msg.role === "assistant") {
3285
+ this.conversation.addAssistantMessage(extractMessageText(msg.content));
3286
+ } else if (msg.role === "system") {
3287
+ this.conversation.addUserMessage(`[System] ${extractMessageText(msg.content)}`);
4624
3288
  }
4625
- });
3289
+ }
4626
3290
  }
4627
- return parts.join("\n");
3291
+ return finalMessage;
4628
3292
  }
4629
3293
  };
4630
3294
  }
4631
3295
  });
4632
3296
 
4633
- // src/gadgets/create-gadget.ts
4634
- function createGadget(config) {
4635
- class DynamicGadget extends AbstractGadget {
4636
- name = config.name;
4637
- description = config.description;
4638
- parameterSchema = config.schema;
4639
- timeoutMs = config.timeoutMs;
4640
- examples = config.examples;
4641
- maxConcurrent = config.maxConcurrent;
4642
- execute(params, ctx) {
4643
- return config.execute(params, ctx);
4644
- }
4645
- }
4646
- return new DynamicGadget();
4647
- }
4648
- var init_create_gadget = __esm({
4649
- "src/gadgets/create-gadget.ts"() {
4650
- "use strict";
4651
- init_gadget();
4652
- }
4653
- });
4654
-
4655
3297
  // src/gadgets/output-viewer.ts
4656
- import { z as z3 } from "zod";
3298
+ import { z } from "zod";
4657
3299
  function pluralize(count, singular, plural = `${singular}s`) {
4658
3300
  return count === 1 ? singular : plural;
4659
3301
  }
@@ -4775,15 +3417,15 @@ function createGadgetOutputViewer(store, maxOutputChars = DEFAULT_MAX_OUTPUT_CHA
4775
3417
  return createGadget({
4776
3418
  name: "GadgetOutputViewer",
4777
3419
  description: 'View stored output from gadgets that returned too much data. Use mode "line" for grep-like filtering and mode "character" for raw chunked browsing when the output is dense or effectively single-line. Patterns work only in line mode.',
4778
- schema: z3.object({
4779
- id: z3.string().describe("ID of the stored output (from the truncation message)"),
4780
- mode: z3.enum(["line", "character"]).default("line").describe(
3420
+ schema: z.object({
3421
+ id: z.string().describe("ID of the stored output (from the truncation message)"),
3422
+ mode: z.enum(["line", "character"]).default("line").describe(
4781
3423
  'Browse by "line" (supports patterns) or by "character" (raw windows for dense output).'
4782
3424
  ),
4783
- patterns: z3.array(patternSchema).optional().describe(
3425
+ patterns: z.array(patternSchema).optional().describe(
4784
3426
  'Line-mode filter patterns applied in order (like piping through grep). Not supported in mode "character".'
4785
3427
  ),
4786
- limit: z3.string().optional().describe(
3428
+ limit: z.string().optional().describe(
4787
3429
  `Pagination window. In mode "line" it is a line range; in mode "character" it is a character range. Formats: "100-" (first 100), "-25" (last 25), "50-100" (inclusive range).`
4788
3430
  )
4789
3431
  }),
@@ -4923,11 +3565,11 @@ var init_output_viewer = __esm({
4923
3565
  DEFAULT_MAX_OUTPUT_CHARS = 76800;
4924
3566
  CHARACTER_HINT_WINDOW = 2e3;
4925
3567
  DENSE_LINE_THRESHOLD = 4e3;
4926
- patternSchema = z3.object({
4927
- regex: z3.string().describe("Regular expression to match"),
4928
- include: z3.boolean().default(true).describe("true = keep matching lines, false = exclude matching lines"),
4929
- before: z3.number().int().min(0).default(0).describe("Context lines before each match (like grep -B)"),
4930
- after: z3.number().int().min(0).default(0).describe("Context lines after each match (like grep -A)")
3568
+ patternSchema = z.object({
3569
+ regex: z.string().describe("Regular expression to match"),
3570
+ include: z.boolean().default(true).describe("true = keep matching lines, false = exclude matching lines"),
3571
+ before: z.number().int().min(0).default(0).describe("Context lines before each match (like grep -B)"),
3572
+ after: z.number().int().min(0).default(0).describe("Context lines after each match (like grep -A)")
4931
3573
  });
4932
3574
  }
4933
3575
  });
@@ -5187,7 +3829,6 @@ var init_retry_orchestrator = __esm({
5187
3829
  for await (const event of processor.process(stream2)) {
5188
3830
  if (event.type === "stream_complete") {
5189
3831
  streamMetadata = event;
5190
- continue;
5191
3832
  }
5192
3833
  if (event.type === "llm_response_end") {
5193
3834
  this.tree.endLLMResponse(llmNodeId, {
@@ -5461,7 +4102,7 @@ var init_activation = __esm({
5461
4102
  });
5462
4103
 
5463
4104
  // src/skills/load-skill-gadget.ts
5464
- import { z as z4 } from "zod";
4105
+ import { z as z2 } from "zod";
5465
4106
  function createLoadSkillGadget(registry) {
5466
4107
  const summaries = registry.getMetadataSummaries();
5467
4108
  const skillNames = registry.getModelInvocable().map((s) => s.name);
@@ -5473,9 +4114,9 @@ function createLoadSkillGadget(registry) {
5473
4114
  return createGadget({
5474
4115
  name: LOAD_SKILL_GADGET_NAME,
5475
4116
  description,
5476
- schema: z4.object({
5477
- skill: z4.enum(skillNames).describe("Name of the skill to load"),
5478
- arguments: z4.string().optional().describe("Arguments for the skill (e.g., a filename, issue number, or search query)")
4117
+ schema: z2.object({
4118
+ skill: z2.enum(skillNames).describe("Name of the skill to load"),
4119
+ arguments: z2.string().optional().describe("Arguments for the skill (e.g., a filename, issue number, or search query)")
5479
4120
  }),
5480
4121
  execute: async ({ skill: skillName, arguments: args }) => {
5481
4122
  const skill = registry.get(skillName);
@@ -12910,7 +11551,7 @@ __export(client_exports, {
12910
11551
  LLMist: () => LLMist
12911
11552
  });
12912
11553
  var LLMist;
12913
- var init_client = __esm({
11554
+ var init_client2 = __esm({
12914
11555
  "src/core/client.ts"() {
12915
11556
  "use strict";
12916
11557
  init_builder();
@@ -13224,6 +11865,7 @@ var init_builder = __esm({
13224
11865
  subagents;
13225
11866
  policies;
13226
11867
  skills;
11868
+ mcp;
13227
11869
  constructor(client) {
13228
11870
  this.core = { client, initialMessages: [] };
13229
11871
  this.gadgets = { gadgets: [] };
@@ -13231,6 +11873,7 @@ var init_builder = __esm({
13231
11873
  this.subagents = {};
13232
11874
  this.policies = {};
13233
11875
  this.skills = { preActivated: [], skillDirs: [] };
11876
+ this.mcp = { servers: [] };
13234
11877
  }
13235
11878
  /** Set the model to use. Supports aliases like "sonnet", "flash". */
13236
11879
  withModel(model) {
@@ -13277,6 +11920,45 @@ var init_builder = __esm({
13277
11920
  this.gadgets.gadgets.push(...gadgets);
13278
11921
  return this;
13279
11922
  }
11923
+ /**
11924
+ * Attach a Model Context Protocol (MCP) server.
11925
+ *
11926
+ * The agent connects to the server lazily at the start of `run()`,
11927
+ * discovers its tools, and registers them as native gadgets so the LLM
11928
+ * can call them through the standard streaming block format.
11929
+ *
11930
+ * Calling this multiple times accumulates servers. Tools across servers
11931
+ * are merged into a single registry; in plan 1, conflicting tool names
11932
+ * raise a registration warning. Plan 2 introduces deterministic
11933
+ * `<server>__<tool>` prefixing for collisions.
11934
+ *
11935
+ * STDIO commands are gated by an allowlist (see allowlist.ts) — pass
11936
+ * `trust: true` on the spec to opt in for non-allowlisted binaries.
11937
+ *
11938
+ * Zero-overhead invariant: if you never call this method, the MCP
11939
+ * runtime module is never loaded. Agents without MCP pay nothing.
11940
+ *
11941
+ * @example
11942
+ * ```typescript
11943
+ * const agent = LLMist.createAgent()
11944
+ * .withModel("sonnet")
11945
+ * .withMcpServer({
11946
+ * name: "filesystem",
11947
+ * transport: "stdio",
11948
+ * command: "npx",
11949
+ * args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"],
11950
+ * })
11951
+ * .ask("list files in /tmp");
11952
+ * ```
11953
+ */
11954
+ withMcpServer(spec) {
11955
+ this.mcp.servers.push(spec);
11956
+ return this;
11957
+ }
11958
+ /** Inspect the configured MCP server specs. Useful for tests. */
11959
+ getMcpServerSpecs() {
11960
+ return this.mcp.servers;
11961
+ }
13280
11962
  /** Add conversation history messages. */
13281
11963
  withHistory(messages) {
13282
11964
  this.core.initialMessages.push(...normalizeHistory(messages));
@@ -13538,7 +12220,7 @@ ${resolved}`);
13538
12220
  }
13539
12221
  buildAgentOptions(userPrompt) {
13540
12222
  if (!this.core.client) {
13541
- const { LLMist: LLMistClass } = (init_client(), __toCommonJS(client_exports));
12223
+ const { LLMist: LLMistClass } = (init_client2(), __toCommonJS(client_exports));
13542
12224
  this.core.client = new LLMistClass();
13543
12225
  }
13544
12226
  const registry = GadgetRegistry.from(this.gadgets.gadgets);
@@ -13597,7 +12279,8 @@ ${preActivatedBlock}` : preActivatedBlock;
13597
12279
  parentObservers: this.subagents.parentObservers
13598
12280
  },
13599
12281
  sharedRateLimitTracker: this.subagents.sharedRateLimitTracker,
13600
- sharedRetryConfig: this.retry.sharedRetryConfig
12282
+ sharedRetryConfig: this.retry.sharedRetryConfig,
12283
+ mcpSpecs: this.mcp.servers.length > 0 ? [...this.mcp.servers] : void 0
13601
12284
  };
13602
12285
  }
13603
12286
  /** Create agent and start with a user prompt. */
@@ -14521,7 +13204,7 @@ var init_typed_gadget = __esm({
14521
13204
 
14522
13205
  // src/gadgets/executor.ts
14523
13206
  import equal from "fast-deep-equal";
14524
- import { z as z5 } from "zod";
13207
+ import { z as z3 } from "zod";
14525
13208
  function getHostExportsInternal() {
14526
13209
  return {
14527
13210
  AgentBuilder,
@@ -14529,7 +13212,7 @@ function getHostExportsInternal() {
14529
13212
  createGadget,
14530
13213
  ExecutionTree,
14531
13214
  LLMist,
14532
- z: z5
13215
+ z: z3
14533
13216
  };
14534
13217
  }
14535
13218
  var GadgetExecutor;
@@ -14538,7 +13221,7 @@ var init_executor = __esm({
14538
13221
  "use strict";
14539
13222
  init_builder();
14540
13223
  init_hook_utils();
14541
- init_client();
13224
+ init_client2();
14542
13225
  init_constants();
14543
13226
  init_execution_tree();
14544
13227
  init_logger();
@@ -16669,6 +15352,10 @@ var init_agent = __esm({
16669
15352
  streamProcessorFactory;
16670
15353
  // LLM call lifecycle helper (encapsulates prepareLLMCall, completeLLMCall, notifyLLMError)
16671
15354
  llmCallLifecycle;
15355
+ // MCP integration — populated only when mcpSpecs were provided.
15356
+ mcpSpecs;
15357
+ mcpLifecycle = null;
15358
+ mcpDiscoveredPrompts = [];
16672
15359
  /**
16673
15360
  * Creates a new Agent instance.
16674
15361
  * @internal This constructor is private. Use LLMist.createAgent() or AgentBuilder instead.
@@ -16740,6 +15427,7 @@ var init_agent = __esm({
16740
15427
  );
16741
15428
  }
16742
15429
  this.signal = options.signal;
15430
+ this.mcpSpecs = options.mcpSpecs ?? [];
16743
15431
  this.reasoning = options.reasoning;
16744
15432
  this.caching = options.caching;
16745
15433
  this.retryConfig = options.sharedRetryConfig ?? resolveRetryConfig(options.retryConfig);
@@ -16999,6 +15687,18 @@ var init_agent = __esm({
16999
15687
  );
17000
15688
  }
17001
15689
  const unsubscribeBridge = bridgeTreeToHooks(this.tree, this.hooks, this.logger);
15690
+ if (this.mcpSpecs.length > 0) {
15691
+ const { setupMcpServers } = await import("./runtime-GKQ6QIQP.js");
15692
+ this.mcpLifecycle = await setupMcpServers({
15693
+ specs: this.mcpSpecs,
15694
+ registry: this.registry,
15695
+ conversation: this.conversation,
15696
+ prefixConfig: this.prefixConfig,
15697
+ systemPrompt: this.conversation.getBaseMessages()[0]?.role === "system" ? this.conversation.getBaseMessages()[0].content : void 0,
15698
+ logger: this.logger,
15699
+ onPromptDiscovered: (skill) => this.mcpDiscoveredPrompts.push(skill)
15700
+ });
15701
+ }
17002
15702
  let currentIteration = 0;
17003
15703
  this.logger.info("Starting agent loop", {
17004
15704
  model: this.model,
@@ -17198,6 +15898,14 @@ var init_agent = __esm({
17198
15898
  }
17199
15899
  }
17200
15900
  unsubscribeBridge();
15901
+ if (this.mcpLifecycle) {
15902
+ try {
15903
+ await this.mcpLifecycle.closeAll();
15904
+ } catch (err) {
15905
+ this.logger.debug("MCP lifecycle teardown error (suppressed):", err);
15906
+ }
15907
+ this.mcpLifecycle = null;
15908
+ }
17201
15909
  }
17202
15910
  }
17203
15911
  /**
@@ -17389,7 +16097,7 @@ init_builder();
17389
16097
  init_event_handlers();
17390
16098
  init_file_logging();
17391
16099
  init_hook_presets();
17392
- import { z as z6 } from "zod";
16100
+ import { z as z4 } from "zod";
17393
16101
 
17394
16102
  // src/agent/compaction/index.ts
17395
16103
  init_config();
@@ -17502,7 +16210,7 @@ function createHints(config) {
17502
16210
  init_stream_processor();
17503
16211
 
17504
16212
  // src/index.ts
17505
- init_client();
16213
+ init_client2();
17506
16214
  init_constants();
17507
16215
 
17508
16216
  // src/core/errors.ts
@@ -17567,140 +16275,254 @@ init_create_gadget();
17567
16275
  init_exceptions();
17568
16276
  init_executor();
17569
16277
  init_gadget();
16278
+ init_helpers();
16279
+ init_output_viewer();
16280
+ init_parser2();
16281
+ init_registry();
16282
+ init_typed_gadget();
17570
16283
 
17571
- // src/gadgets/helpers.ts
17572
- init_input_content();
17573
- function gadgetSuccess(data = {}) {
17574
- return JSON.stringify({ success: true, ...data });
17575
- }
17576
- function gadgetError(message, details) {
17577
- return JSON.stringify({ error: message, ...details });
17578
- }
17579
- function getErrorMessage(error) {
17580
- return error instanceof Error ? error.message : String(error);
17581
- }
17582
- function withErrorHandling(execute) {
17583
- return async (params, ctx) => {
17584
- try {
17585
- return await execute(params, ctx);
17586
- } catch (error) {
17587
- return gadgetError(getErrorMessage(error));
17588
- }
17589
- };
17590
- }
17591
- function createMediaOutput(kind, data, mimeType, options) {
17592
- const buffer = data instanceof Buffer ? data : Buffer.from(data);
16284
+ // src/mcp/index.ts
16285
+ init_allowlist();
16286
+ init_client();
16287
+ init_errors();
16288
+
16289
+ // src/mcp/gadget-exporter.ts
16290
+ init_schema_to_json();
16291
+
16292
+ // src/gadgets/validation.ts
16293
+ function validateAndApplyDefaults(schema, params) {
16294
+ const result = schema.safeParse(params);
16295
+ if (result.success) {
16296
+ return {
16297
+ success: true,
16298
+ data: result.data
16299
+ };
16300
+ }
16301
+ const issues = result.error.issues.map((issue) => ({
16302
+ path: issue.path.join(".") || "root",
16303
+ message: issue.message
16304
+ }));
16305
+ const formattedError = `Invalid parameters: ${issues.map((i) => `${i.path}: ${i.message}`).join("; ")}`;
17593
16306
  return {
17594
- kind,
17595
- data: buffer.toString("base64"),
17596
- mimeType,
17597
- description: options?.description,
17598
- metadata: options?.metadata,
17599
- fileName: options?.fileName
16307
+ success: false,
16308
+ error: formattedError,
16309
+ issues
17600
16310
  };
17601
16311
  }
17602
- function resultWithMedia(result, media, cost) {
17603
- if (media.length === 0) {
17604
- throw new Error("resultWithMedia: media array cannot be empty");
16312
+ function validateGadgetParams(gadget, params) {
16313
+ if (!gadget.parameterSchema) {
16314
+ return {
16315
+ success: true,
16316
+ data: params
16317
+ };
17605
16318
  }
17606
- return {
17607
- result,
17608
- media,
17609
- cost
17610
- };
16319
+ return validateAndApplyDefaults(gadget.parameterSchema, params);
17611
16320
  }
17612
- function resultWithImage(result, imageData, options) {
17613
- const buffer = imageData instanceof Buffer ? imageData : Buffer.from(imageData);
17614
- const mimeType = options?.mimeType ?? detectImageMimeType(buffer);
17615
- if (!mimeType) {
17616
- throw new Error(
17617
- "Could not detect image MIME type. Please provide mimeType explicitly in options."
17618
- );
16321
+
16322
+ // src/mcp/gadget-exporter.ts
16323
+ function gadgetToMcpTool(gadget) {
16324
+ const description = gadget.description && gadget.description.length > 0 ? gadget.description : `Native llmist gadget "${gadget.name ?? "unnamed"}"`;
16325
+ let inputSchema;
16326
+ if (gadget.parameterSchema) {
16327
+ inputSchema = schemaToJSONSchema(gadget.parameterSchema);
16328
+ } else {
16329
+ inputSchema = { type: "object", properties: {} };
17619
16330
  }
17620
16331
  return {
17621
- result,
17622
- media: [
17623
- {
17624
- kind: "image",
17625
- data: buffer.toString("base64"),
17626
- mimeType,
17627
- description: options?.description,
17628
- metadata: options?.metadata,
17629
- fileName: options?.fileName
17630
- }
17631
- ],
17632
- cost: options?.cost
16332
+ name: gadget.name ?? "unnamed-gadget",
16333
+ description,
16334
+ inputSchema
17633
16335
  };
17634
16336
  }
17635
- function resultWithImages(result, images, cost) {
17636
- if (images.length === 0) {
17637
- throw new Error("resultWithImages: images array cannot be empty");
17638
- }
17639
- const media = images.map((img, index) => {
17640
- const buffer = img.data instanceof Buffer ? img.data : Buffer.from(img.data);
17641
- const mimeType = img.mimeType ?? detectImageMimeType(buffer);
17642
- if (!mimeType) {
17643
- throw new Error(
17644
- `Could not detect MIME type for image at index ${index}. Please provide mimeType explicitly.`
17645
- );
16337
+ function gadgetResultToMcpContent(ret) {
16338
+ if (typeof ret === "string") {
16339
+ return [{ type: "text", text: ret }];
16340
+ }
16341
+ if (ret && typeof ret === "object" && "result" in ret) {
16342
+ const r = ret;
16343
+ const blocks = [];
16344
+ if (typeof r.result === "string") {
16345
+ blocks.push({ type: "text", text: r.result });
16346
+ } else {
16347
+ blocks.push({ type: "text", text: JSON.stringify(r.result) });
16348
+ }
16349
+ if (r.media) {
16350
+ for (const m of r.media) {
16351
+ if (m.kind === "image" || m.kind === "audio") {
16352
+ blocks.push({
16353
+ type: m.kind,
16354
+ data: m.data,
16355
+ mimeType: m.mimeType
16356
+ });
16357
+ }
16358
+ }
16359
+ }
16360
+ return blocks;
16361
+ }
16362
+ return [{ type: "text", text: JSON.stringify(ret) }];
16363
+ }
16364
+ async function runGadgetForMcp(gadget, rawParams) {
16365
+ if (gadget.parameterSchema) {
16366
+ const validation = validateAndApplyDefaults(
16367
+ gadget.parameterSchema,
16368
+ rawParams ?? {}
16369
+ );
16370
+ if (!validation.success) {
16371
+ return {
16372
+ isError: true,
16373
+ content: [
16374
+ {
16375
+ type: "text",
16376
+ text: `Invalid arguments for gadget "${gadget.name}": ${validation.error}`
16377
+ }
16378
+ ]
16379
+ };
17646
16380
  }
16381
+ rawParams = validation.data;
16382
+ }
16383
+ try {
16384
+ const result = await gadget.execute(rawParams);
17647
16385
  return {
17648
- kind: "image",
17649
- data: buffer.toString("base64"),
17650
- mimeType,
17651
- description: img.description,
17652
- metadata: img.metadata,
17653
- fileName: img.fileName
16386
+ content: gadgetResultToMcpContent(result)
17654
16387
  };
17655
- });
17656
- return { result, media, cost };
16388
+ } catch (err) {
16389
+ return {
16390
+ isError: true,
16391
+ content: [
16392
+ {
16393
+ type: "text",
16394
+ text: `Gadget "${gadget.name}" failed: ${err.message}`
16395
+ }
16396
+ ]
16397
+ };
16398
+ }
17657
16399
  }
17658
- function resultWithAudio(result, audioData, options) {
17659
- const buffer = audioData instanceof Buffer ? audioData : Buffer.from(audioData);
17660
- const mimeType = options?.mimeType ?? detectAudioMimeType(buffer);
17661
- if (!mimeType) {
17662
- throw new Error(
17663
- "Could not detect audio MIME type. Please provide mimeType explicitly in options."
17664
- );
16400
+
16401
+ // src/mcp/index.ts
16402
+ init_json_schema_to_zod();
16403
+ init_lifecycle();
16404
+
16405
+ // src/mcp/skill-exporter.ts
16406
+ function skillToMcpPrompt(skill) {
16407
+ const description = skill.description && skill.description.length > 0 ? skill.description : `Native llmist skill "${skill.name}"`;
16408
+ const args = [];
16409
+ if (skill.metadata.argumentHint) {
16410
+ args.push({
16411
+ name: "arguments",
16412
+ description: skill.metadata.argumentHint,
16413
+ required: false
16414
+ });
17665
16415
  }
17666
- const metadata = options?.durationMs ? { durationMs: options.durationMs } : void 0;
17667
16416
  return {
17668
- result,
17669
- media: [
16417
+ name: skill.name,
16418
+ description,
16419
+ ...args.length > 0 ? { arguments: args } : {}
16420
+ };
16421
+ }
16422
+ async function renderSkillForMcpPrompt(skill, args) {
16423
+ const argString = typeof args.arguments === "string" ? args.arguments : Object.values(args).filter((v) => typeof v === "string").join(" ");
16424
+ const activation = await skill.activate({
16425
+ arguments: argString || void 0
16426
+ });
16427
+ return {
16428
+ description: skill.description,
16429
+ messages: [
17670
16430
  {
17671
- kind: "audio",
17672
- data: buffer.toString("base64"),
17673
- mimeType,
17674
- description: options?.description,
17675
- metadata,
17676
- fileName: options?.fileName
16431
+ role: "user",
16432
+ content: { type: "text", text: activation.resolvedInstructions }
17677
16433
  }
17678
- ],
17679
- cost: options?.cost
16434
+ ]
17680
16435
  };
17681
16436
  }
17682
- function resultWithFile(result, fileData, mimeType, options) {
17683
- const buffer = fileData instanceof Buffer ? fileData : Buffer.from(fileData);
16437
+
16438
+ // src/mcp/server.ts
16439
+ var DEFAULT_SERVER_INFO = { name: "llmist", version: "0.0.0" };
16440
+ function createMcpServer(opts) {
16441
+ const { gadgets, skills } = opts;
16442
+ const hasTools = gadgets.getAll().length > 0;
16443
+ const hasPrompts = !!skills && skills.size > 0;
16444
+ const capabilities = {};
16445
+ if (hasTools) capabilities.tools = {};
16446
+ if (hasPrompts) capabilities.prompts = {};
16447
+ let sdkServer = null;
16448
+ let running = false;
16449
+ async function ensureServer() {
16450
+ if (sdkServer) return sdkServer;
16451
+ const [serverMod, typesMod] = await Promise.all([
16452
+ import("@modelcontextprotocol/sdk/server/index.js"),
16453
+ import("@modelcontextprotocol/sdk/types.js")
16454
+ ]);
16455
+ const ServerClass = serverMod.Server;
16456
+ const server = new ServerClass(opts.serverInfo ?? DEFAULT_SERVER_INFO, {
16457
+ capabilities
16458
+ });
16459
+ if (hasTools) {
16460
+ server.setRequestHandler(typesMod.ListToolsRequestSchema, async () => ({
16461
+ tools: gadgets.getAll().map(gadgetToMcpTool)
16462
+ }));
16463
+ server.setRequestHandler(
16464
+ typesMod.CallToolRequestSchema,
16465
+ async (req) => {
16466
+ const gadget = gadgets.get(req.params.name);
16467
+ if (!gadget) {
16468
+ return {
16469
+ isError: true,
16470
+ content: [
16471
+ {
16472
+ type: "text",
16473
+ text: `Unknown tool "${req.params.name}". Call tools/list first.`
16474
+ }
16475
+ ]
16476
+ };
16477
+ }
16478
+ return runGadgetForMcp(gadget, req.params.arguments ?? {});
16479
+ }
16480
+ );
16481
+ }
16482
+ if (hasPrompts && skills) {
16483
+ server.setRequestHandler(typesMod.ListPromptsRequestSchema, async () => ({
16484
+ prompts: Array.from(skills.getAll()).map(skillToMcpPrompt)
16485
+ }));
16486
+ server.setRequestHandler(
16487
+ typesMod.GetPromptRequestSchema,
16488
+ async (req) => {
16489
+ const skill = skills.get(req.params.name);
16490
+ if (!skill) {
16491
+ throw new Error(`Unknown prompt "${req.params.name}"`);
16492
+ }
16493
+ const result = await renderSkillForMcpPrompt(skill, req.params.arguments ?? {});
16494
+ return result;
16495
+ }
16496
+ );
16497
+ }
16498
+ sdkServer = server;
16499
+ return server;
16500
+ }
17684
16501
  return {
17685
- result,
17686
- media: [
17687
- {
17688
- kind: "file",
17689
- data: buffer.toString("base64"),
17690
- mimeType,
17691
- description: options?.description,
17692
- fileName: options?.fileName
16502
+ get running() {
16503
+ return running;
16504
+ },
16505
+ async connect(transport) {
16506
+ const server = await ensureServer();
16507
+ await server.connect(transport);
16508
+ running = true;
16509
+ },
16510
+ async stop() {
16511
+ if (!sdkServer) return;
16512
+ try {
16513
+ await sdkServer.close();
16514
+ } catch {
17693
16515
  }
17694
- ],
17695
- cost: options?.cost
16516
+ sdkServer = null;
16517
+ running = false;
16518
+ }
17696
16519
  };
17697
16520
  }
17698
16521
 
16522
+ // src/mcp/index.ts
16523
+ init_tool_adapter();
16524
+
17699
16525
  // src/index.ts
17700
- init_output_viewer();
17701
- init_parser2();
17702
- init_registry();
17703
- init_typed_gadget();
17704
16526
  init_constants2();
17705
16527
 
17706
16528
  // src/utils/config-resolver.ts
@@ -17809,38 +16631,6 @@ function hasHostExports(ctx) {
17809
16631
  init_media_store();
17810
16632
  init_schema_to_json();
17811
16633
  init_schema_validator();
17812
-
17813
- // src/gadgets/validation.ts
17814
- function validateAndApplyDefaults(schema, params) {
17815
- const result = schema.safeParse(params);
17816
- if (result.success) {
17817
- return {
17818
- success: true,
17819
- data: result.data
17820
- };
17821
- }
17822
- const issues = result.error.issues.map((issue) => ({
17823
- path: issue.path.join(".") || "root",
17824
- message: issue.message
17825
- }));
17826
- const formattedError = `Invalid parameters: ${issues.map((i) => `${i.path}: ${i.message}`).join("; ")}`;
17827
- return {
17828
- success: false,
17829
- error: formattedError,
17830
- issues
17831
- };
17832
- }
17833
- function validateGadgetParams(gadget, params) {
17834
- if (!gadget.parameterSchema) {
17835
- return {
17836
- success: true,
17837
- data: params
17838
- };
17839
- }
17840
- return validateAndApplyDefaults(gadget.parameterSchema, params);
17841
- }
17842
-
17843
- // src/index.ts
17844
16634
  init_logger();
17845
16635
 
17846
16636
  // src/package/manifest.ts
@@ -18138,6 +16928,7 @@ export {
18138
16928
  ConversationManager,
18139
16929
  DEFAULT_COMPACTION_CONFIG,
18140
16930
  DEFAULT_HINTS,
16931
+ DEFAULT_MCP_COMMAND_ALLOWLIST,
18141
16932
  DEFAULT_PROMPTS,
18142
16933
  DEFAULT_RATE_LIMIT_CONFIG,
18143
16934
  DEFAULT_RETRY_CONFIG,
@@ -18157,10 +16948,17 @@ export {
18157
16948
  HuggingFaceProvider,
18158
16949
  HumanInputRequiredException,
18159
16950
  HybridStrategy,
16951
+ JsonSchemaConversionError,
18160
16952
  LLMMessageBuilder,
18161
16953
  LLMist,
18162
16954
  LOAD_SKILL_GADGET_NAME,
18163
16955
  MODEL_ALIASES,
16956
+ McpClient,
16957
+ McpConnectError,
16958
+ McpError,
16959
+ McpLifecycle,
16960
+ McpToolCallError,
16961
+ McpUntrustedCommandError,
18164
16962
  MediaStore,
18165
16963
  ModelIdentifierParser,
18166
16964
  ModelRegistry,
@@ -18176,6 +16974,7 @@ export {
18176
16974
  SummarizationStrategy,
18177
16975
  TaskCompletionSignal,
18178
16976
  TimeoutException,
16977
+ assertCommandAllowed,
18179
16978
  audioFromBase64,
18180
16979
  audioFromBuffer,
18181
16980
  collectEvents,
@@ -18190,6 +16989,7 @@ export {
18190
16989
  createHuggingFaceProviderFromEnv,
18191
16990
  createLoadSkillGadget,
18192
16991
  createLogger,
16992
+ createMcpServer,
18193
16993
  createMediaOutput,
18194
16994
  createOpenAIProviderFromEnv,
18195
16995
  createOpenRouterProviderFromEnv,
@@ -18212,7 +17012,9 @@ export {
18212
17012
  formatLLMError,
18213
17013
  formatLlmRequest,
18214
17014
  gadgetError,
17015
+ gadgetResultToMcpContent,
18215
17016
  gadgetSuccess,
17017
+ gadgetToMcpTool,
18216
17018
  getErrorMessage,
18217
17019
  getHostExports2 as getHostExports,
18218
17020
  getModelId,
@@ -18240,9 +17042,11 @@ export {
18240
17042
  isSubagentEvent,
18241
17043
  isTextPart,
18242
17044
  iterationProgressHint,
17045
+ jsonSchemaToZod,
18243
17046
  listPresets,
18244
17047
  listSubagents,
18245
17048
  loadSkillsFromDirectory,
17049
+ mcpToolToGadget,
18246
17050
  normalizeMessageContent,
18247
17051
  parallelGadgetHint,
18248
17052
  parseDataUrl,
@@ -18253,6 +17057,7 @@ export {
18253
17057
  parseSkillContent,
18254
17058
  parseSkillFile,
18255
17059
  randomDelay,
17060
+ renderSkillForMcpPrompt,
18256
17061
  resetFileLoggingState,
18257
17062
  resolveConfig,
18258
17063
  resolveHintTemplate,
@@ -18270,9 +17075,11 @@ export {
18270
17075
  resultWithImage,
18271
17076
  resultWithImages,
18272
17077
  resultWithMedia,
17078
+ runGadgetForMcp,
18273
17079
  runWithHandlers,
18274
17080
  scanResources,
18275
17081
  schemaToJSONSchema,
17082
+ skillToMcpPrompt,
18276
17083
  stream,
18277
17084
  stripProviderPrefix,
18278
17085
  substituteArguments,
@@ -18288,6 +17095,6 @@ export {
18288
17095
  withErrorHandling,
18289
17096
  withRetry,
18290
17097
  withTimeout,
18291
- z6 as z
17098
+ z4 as z
18292
17099
  };
18293
17100
  //# sourceMappingURL=index.js.map