llmist 17.3.0 → 17.5.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.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";
@@ -2432,126 +1767,6 @@ var init_media_store = __esm({
2432
1767
  }
2433
1768
  });
2434
1769
 
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
2545
- };
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
- }
2553
- });
2554
-
2555
1770
  // src/agent/agent-internal-key.ts
2556
1771
  function isValidAgentKey(key) {
2557
1772
  return key === AGENT_INTERNAL_KEY;
@@ -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) {
@@ -3994,666 +3219,83 @@ var init_llm_call_lifecycle = __esm({
3994
3219
  resolveCachingConfig() {
3995
3220
  if (this.caching !== void 0) return this.caching;
3996
3221
  return { enabled: true };
3997
- }
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
- * ```
3222
+ }
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
  });
@@ -5461,7 +4103,7 @@ var init_activation = __esm({
5461
4103
  });
5462
4104
 
5463
4105
  // src/skills/load-skill-gadget.ts
5464
- import { z as z4 } from "zod";
4106
+ import { z as z2 } from "zod";
5465
4107
  function createLoadSkillGadget(registry) {
5466
4108
  const summaries = registry.getMetadataSummaries();
5467
4109
  const skillNames = registry.getModelInvocable().map((s) => s.name);
@@ -5473,9 +4115,9 @@ function createLoadSkillGadget(registry) {
5473
4115
  return createGadget({
5474
4116
  name: LOAD_SKILL_GADGET_NAME,
5475
4117
  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)")
4118
+ schema: z2.object({
4119
+ skill: z2.enum(skillNames).describe("Name of the skill to load"),
4120
+ arguments: z2.string().optional().describe("Arguments for the skill (e.g., a filename, issue number, or search query)")
5479
4121
  }),
5480
4122
  execute: async ({ skill: skillName, arguments: args }) => {
5481
4123
  const skill = registry.get(skillName);
@@ -12910,7 +11552,7 @@ __export(client_exports, {
12910
11552
  LLMist: () => LLMist
12911
11553
  });
12912
11554
  var LLMist;
12913
- var init_client = __esm({
11555
+ var init_client2 = __esm({
12914
11556
  "src/core/client.ts"() {
12915
11557
  "use strict";
12916
11558
  init_builder();
@@ -13224,6 +11866,7 @@ var init_builder = __esm({
13224
11866
  subagents;
13225
11867
  policies;
13226
11868
  skills;
11869
+ mcp;
13227
11870
  constructor(client) {
13228
11871
  this.core = { client, initialMessages: [] };
13229
11872
  this.gadgets = { gadgets: [] };
@@ -13231,6 +11874,7 @@ var init_builder = __esm({
13231
11874
  this.subagents = {};
13232
11875
  this.policies = {};
13233
11876
  this.skills = { preActivated: [], skillDirs: [] };
11877
+ this.mcp = { servers: [] };
13234
11878
  }
13235
11879
  /** Set the model to use. Supports aliases like "sonnet", "flash". */
13236
11880
  withModel(model) {
@@ -13277,6 +11921,45 @@ var init_builder = __esm({
13277
11921
  this.gadgets.gadgets.push(...gadgets);
13278
11922
  return this;
13279
11923
  }
11924
+ /**
11925
+ * Attach a Model Context Protocol (MCP) server.
11926
+ *
11927
+ * The agent connects to the server lazily at the start of `run()`,
11928
+ * discovers its tools, and registers them as native gadgets so the LLM
11929
+ * can call them through the standard streaming block format.
11930
+ *
11931
+ * Calling this multiple times accumulates servers. Tools across servers
11932
+ * are merged into a single registry; in plan 1, conflicting tool names
11933
+ * raise a registration warning. Plan 2 introduces deterministic
11934
+ * `<server>__<tool>` prefixing for collisions.
11935
+ *
11936
+ * STDIO commands are gated by an allowlist (see allowlist.ts) — pass
11937
+ * `trust: true` on the spec to opt in for non-allowlisted binaries.
11938
+ *
11939
+ * Zero-overhead invariant: if you never call this method, the MCP
11940
+ * runtime module is never loaded. Agents without MCP pay nothing.
11941
+ *
11942
+ * @example
11943
+ * ```typescript
11944
+ * const agent = LLMist.createAgent()
11945
+ * .withModel("sonnet")
11946
+ * .withMcpServer({
11947
+ * name: "filesystem",
11948
+ * transport: "stdio",
11949
+ * command: "npx",
11950
+ * args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"],
11951
+ * })
11952
+ * .ask("list files in /tmp");
11953
+ * ```
11954
+ */
11955
+ withMcpServer(spec) {
11956
+ this.mcp.servers.push(spec);
11957
+ return this;
11958
+ }
11959
+ /** Inspect the configured MCP server specs. Useful for tests. */
11960
+ getMcpServerSpecs() {
11961
+ return this.mcp.servers;
11962
+ }
13280
11963
  /** Add conversation history messages. */
13281
11964
  withHistory(messages) {
13282
11965
  this.core.initialMessages.push(...normalizeHistory(messages));
@@ -13538,7 +12221,7 @@ ${resolved}`);
13538
12221
  }
13539
12222
  buildAgentOptions(userPrompt) {
13540
12223
  if (!this.core.client) {
13541
- const { LLMist: LLMistClass } = (init_client(), __toCommonJS(client_exports));
12224
+ const { LLMist: LLMistClass } = (init_client2(), __toCommonJS(client_exports));
13542
12225
  this.core.client = new LLMistClass();
13543
12226
  }
13544
12227
  const registry = GadgetRegistry.from(this.gadgets.gadgets);
@@ -13597,7 +12280,8 @@ ${preActivatedBlock}` : preActivatedBlock;
13597
12280
  parentObservers: this.subagents.parentObservers
13598
12281
  },
13599
12282
  sharedRateLimitTracker: this.subagents.sharedRateLimitTracker,
13600
- sharedRetryConfig: this.retry.sharedRetryConfig
12283
+ sharedRetryConfig: this.retry.sharedRetryConfig,
12284
+ mcpSpecs: this.mcp.servers.length > 0 ? [...this.mcp.servers] : void 0
13601
12285
  };
13602
12286
  }
13603
12287
  /** Create agent and start with a user prompt. */
@@ -14521,7 +13205,7 @@ var init_typed_gadget = __esm({
14521
13205
 
14522
13206
  // src/gadgets/executor.ts
14523
13207
  import equal from "fast-deep-equal";
14524
- import { z as z5 } from "zod";
13208
+ import { z as z3 } from "zod";
14525
13209
  function getHostExportsInternal() {
14526
13210
  return {
14527
13211
  AgentBuilder,
@@ -14529,7 +13213,7 @@ function getHostExportsInternal() {
14529
13213
  createGadget,
14530
13214
  ExecutionTree,
14531
13215
  LLMist,
14532
- z: z5
13216
+ z: z3
14533
13217
  };
14534
13218
  }
14535
13219
  var GadgetExecutor;
@@ -14538,7 +13222,7 @@ var init_executor = __esm({
14538
13222
  "use strict";
14539
13223
  init_builder();
14540
13224
  init_hook_utils();
14541
- init_client();
13225
+ init_client2();
14542
13226
  init_constants();
14543
13227
  init_execution_tree();
14544
13228
  init_logger();
@@ -16669,6 +15353,10 @@ var init_agent = __esm({
16669
15353
  streamProcessorFactory;
16670
15354
  // LLM call lifecycle helper (encapsulates prepareLLMCall, completeLLMCall, notifyLLMError)
16671
15355
  llmCallLifecycle;
15356
+ // MCP integration — populated only when mcpSpecs were provided.
15357
+ mcpSpecs;
15358
+ mcpLifecycle = null;
15359
+ mcpDiscoveredPrompts = [];
16672
15360
  /**
16673
15361
  * Creates a new Agent instance.
16674
15362
  * @internal This constructor is private. Use LLMist.createAgent() or AgentBuilder instead.
@@ -16740,6 +15428,7 @@ var init_agent = __esm({
16740
15428
  );
16741
15429
  }
16742
15430
  this.signal = options.signal;
15431
+ this.mcpSpecs = options.mcpSpecs ?? [];
16743
15432
  this.reasoning = options.reasoning;
16744
15433
  this.caching = options.caching;
16745
15434
  this.retryConfig = options.sharedRetryConfig ?? resolveRetryConfig(options.retryConfig);
@@ -16999,6 +15688,18 @@ var init_agent = __esm({
16999
15688
  );
17000
15689
  }
17001
15690
  const unsubscribeBridge = bridgeTreeToHooks(this.tree, this.hooks, this.logger);
15691
+ if (this.mcpSpecs.length > 0) {
15692
+ const { setupMcpServers } = await import("./runtime-GKQ6QIQP.js");
15693
+ this.mcpLifecycle = await setupMcpServers({
15694
+ specs: this.mcpSpecs,
15695
+ registry: this.registry,
15696
+ conversation: this.conversation,
15697
+ prefixConfig: this.prefixConfig,
15698
+ systemPrompt: this.conversation.getBaseMessages()[0]?.role === "system" ? this.conversation.getBaseMessages()[0].content : void 0,
15699
+ logger: this.logger,
15700
+ onPromptDiscovered: (skill) => this.mcpDiscoveredPrompts.push(skill)
15701
+ });
15702
+ }
17002
15703
  let currentIteration = 0;
17003
15704
  this.logger.info("Starting agent loop", {
17004
15705
  model: this.model,
@@ -17198,6 +15899,14 @@ var init_agent = __esm({
17198
15899
  }
17199
15900
  }
17200
15901
  unsubscribeBridge();
15902
+ if (this.mcpLifecycle) {
15903
+ try {
15904
+ await this.mcpLifecycle.closeAll();
15905
+ } catch (err) {
15906
+ this.logger.debug("MCP lifecycle teardown error (suppressed):", err);
15907
+ }
15908
+ this.mcpLifecycle = null;
15909
+ }
17201
15910
  }
17202
15911
  }
17203
15912
  /**
@@ -17389,7 +16098,7 @@ init_builder();
17389
16098
  init_event_handlers();
17390
16099
  init_file_logging();
17391
16100
  init_hook_presets();
17392
- import { z as z6 } from "zod";
16101
+ import { z as z4 } from "zod";
17393
16102
 
17394
16103
  // src/agent/compaction/index.ts
17395
16104
  init_config();
@@ -17502,7 +16211,7 @@ function createHints(config) {
17502
16211
  init_stream_processor();
17503
16212
 
17504
16213
  // src/index.ts
17505
- init_client();
16214
+ init_client2();
17506
16215
  init_constants();
17507
16216
 
17508
16217
  // src/core/errors.ts
@@ -17567,140 +16276,254 @@ init_create_gadget();
17567
16276
  init_exceptions();
17568
16277
  init_executor();
17569
16278
  init_gadget();
16279
+ init_helpers();
16280
+ init_output_viewer();
16281
+ init_parser2();
16282
+ init_registry();
16283
+ init_typed_gadget();
17570
16284
 
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);
16285
+ // src/mcp/index.ts
16286
+ init_allowlist();
16287
+ init_client();
16288
+ init_errors();
16289
+
16290
+ // src/mcp/gadget-exporter.ts
16291
+ init_schema_to_json();
16292
+
16293
+ // src/gadgets/validation.ts
16294
+ function validateAndApplyDefaults(schema, params) {
16295
+ const result = schema.safeParse(params);
16296
+ if (result.success) {
16297
+ return {
16298
+ success: true,
16299
+ data: result.data
16300
+ };
16301
+ }
16302
+ const issues = result.error.issues.map((issue) => ({
16303
+ path: issue.path.join(".") || "root",
16304
+ message: issue.message
16305
+ }));
16306
+ const formattedError = `Invalid parameters: ${issues.map((i) => `${i.path}: ${i.message}`).join("; ")}`;
17593
16307
  return {
17594
- kind,
17595
- data: buffer.toString("base64"),
17596
- mimeType,
17597
- description: options?.description,
17598
- metadata: options?.metadata,
17599
- fileName: options?.fileName
16308
+ success: false,
16309
+ error: formattedError,
16310
+ issues
17600
16311
  };
17601
16312
  }
17602
- function resultWithMedia(result, media, cost) {
17603
- if (media.length === 0) {
17604
- throw new Error("resultWithMedia: media array cannot be empty");
16313
+ function validateGadgetParams(gadget, params) {
16314
+ if (!gadget.parameterSchema) {
16315
+ return {
16316
+ success: true,
16317
+ data: params
16318
+ };
17605
16319
  }
17606
- return {
17607
- result,
17608
- media,
17609
- cost
17610
- };
16320
+ return validateAndApplyDefaults(gadget.parameterSchema, params);
17611
16321
  }
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
- );
16322
+
16323
+ // src/mcp/gadget-exporter.ts
16324
+ function gadgetToMcpTool(gadget) {
16325
+ const description = gadget.description && gadget.description.length > 0 ? gadget.description : `Native llmist gadget "${gadget.name ?? "unnamed"}"`;
16326
+ let inputSchema;
16327
+ if (gadget.parameterSchema) {
16328
+ inputSchema = schemaToJSONSchema(gadget.parameterSchema);
16329
+ } else {
16330
+ inputSchema = { type: "object", properties: {} };
17619
16331
  }
17620
16332
  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
16333
+ name: gadget.name ?? "unnamed-gadget",
16334
+ description,
16335
+ inputSchema
17633
16336
  };
17634
16337
  }
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
- );
16338
+ function gadgetResultToMcpContent(ret) {
16339
+ if (typeof ret === "string") {
16340
+ return [{ type: "text", text: ret }];
16341
+ }
16342
+ if (ret && typeof ret === "object" && "result" in ret) {
16343
+ const r = ret;
16344
+ const blocks = [];
16345
+ if (typeof r.result === "string") {
16346
+ blocks.push({ type: "text", text: r.result });
16347
+ } else {
16348
+ blocks.push({ type: "text", text: JSON.stringify(r.result) });
16349
+ }
16350
+ if (r.media) {
16351
+ for (const m of r.media) {
16352
+ if (m.kind === "image" || m.kind === "audio") {
16353
+ blocks.push({
16354
+ type: m.kind,
16355
+ data: m.data,
16356
+ mimeType: m.mimeType
16357
+ });
16358
+ }
16359
+ }
16360
+ }
16361
+ return blocks;
16362
+ }
16363
+ return [{ type: "text", text: JSON.stringify(ret) }];
16364
+ }
16365
+ async function runGadgetForMcp(gadget, rawParams) {
16366
+ if (gadget.parameterSchema) {
16367
+ const validation = validateAndApplyDefaults(
16368
+ gadget.parameterSchema,
16369
+ rawParams ?? {}
16370
+ );
16371
+ if (!validation.success) {
16372
+ return {
16373
+ isError: true,
16374
+ content: [
16375
+ {
16376
+ type: "text",
16377
+ text: `Invalid arguments for gadget "${gadget.name}": ${validation.error}`
16378
+ }
16379
+ ]
16380
+ };
17646
16381
  }
16382
+ rawParams = validation.data;
16383
+ }
16384
+ try {
16385
+ const result = await gadget.execute(rawParams);
17647
16386
  return {
17648
- kind: "image",
17649
- data: buffer.toString("base64"),
17650
- mimeType,
17651
- description: img.description,
17652
- metadata: img.metadata,
17653
- fileName: img.fileName
16387
+ content: gadgetResultToMcpContent(result)
17654
16388
  };
17655
- });
17656
- return { result, media, cost };
16389
+ } catch (err) {
16390
+ return {
16391
+ isError: true,
16392
+ content: [
16393
+ {
16394
+ type: "text",
16395
+ text: `Gadget "${gadget.name}" failed: ${err.message}`
16396
+ }
16397
+ ]
16398
+ };
16399
+ }
17657
16400
  }
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
- );
16401
+
16402
+ // src/mcp/index.ts
16403
+ init_json_schema_to_zod();
16404
+ init_lifecycle();
16405
+
16406
+ // src/mcp/skill-exporter.ts
16407
+ function skillToMcpPrompt(skill) {
16408
+ const description = skill.description && skill.description.length > 0 ? skill.description : `Native llmist skill "${skill.name}"`;
16409
+ const args = [];
16410
+ if (skill.metadata.argumentHint) {
16411
+ args.push({
16412
+ name: "arguments",
16413
+ description: skill.metadata.argumentHint,
16414
+ required: false
16415
+ });
17665
16416
  }
17666
- const metadata = options?.durationMs ? { durationMs: options.durationMs } : void 0;
17667
16417
  return {
17668
- result,
17669
- media: [
16418
+ name: skill.name,
16419
+ description,
16420
+ ...args.length > 0 ? { arguments: args } : {}
16421
+ };
16422
+ }
16423
+ async function renderSkillForMcpPrompt(skill, args) {
16424
+ const argString = typeof args.arguments === "string" ? args.arguments : Object.values(args).filter((v) => typeof v === "string").join(" ");
16425
+ const activation = await skill.activate({
16426
+ arguments: argString || void 0
16427
+ });
16428
+ return {
16429
+ description: skill.description,
16430
+ messages: [
17670
16431
  {
17671
- kind: "audio",
17672
- data: buffer.toString("base64"),
17673
- mimeType,
17674
- description: options?.description,
17675
- metadata,
17676
- fileName: options?.fileName
16432
+ role: "user",
16433
+ content: { type: "text", text: activation.resolvedInstructions }
17677
16434
  }
17678
- ],
17679
- cost: options?.cost
16435
+ ]
17680
16436
  };
17681
16437
  }
17682
- function resultWithFile(result, fileData, mimeType, options) {
17683
- const buffer = fileData instanceof Buffer ? fileData : Buffer.from(fileData);
16438
+
16439
+ // src/mcp/server.ts
16440
+ var DEFAULT_SERVER_INFO = { name: "llmist", version: "0.0.0" };
16441
+ function createMcpServer(opts) {
16442
+ const { gadgets, skills } = opts;
16443
+ const hasTools = gadgets.getAll().length > 0;
16444
+ const hasPrompts = !!skills && skills.size > 0;
16445
+ const capabilities = {};
16446
+ if (hasTools) capabilities.tools = {};
16447
+ if (hasPrompts) capabilities.prompts = {};
16448
+ let sdkServer = null;
16449
+ let running = false;
16450
+ async function ensureServer() {
16451
+ if (sdkServer) return sdkServer;
16452
+ const [serverMod, typesMod] = await Promise.all([
16453
+ import("@modelcontextprotocol/sdk/server/index.js"),
16454
+ import("@modelcontextprotocol/sdk/types.js")
16455
+ ]);
16456
+ const ServerClass = serverMod.Server;
16457
+ const server = new ServerClass(opts.serverInfo ?? DEFAULT_SERVER_INFO, {
16458
+ capabilities
16459
+ });
16460
+ if (hasTools) {
16461
+ server.setRequestHandler(typesMod.ListToolsRequestSchema, async () => ({
16462
+ tools: gadgets.getAll().map(gadgetToMcpTool)
16463
+ }));
16464
+ server.setRequestHandler(
16465
+ typesMod.CallToolRequestSchema,
16466
+ async (req) => {
16467
+ const gadget = gadgets.get(req.params.name);
16468
+ if (!gadget) {
16469
+ return {
16470
+ isError: true,
16471
+ content: [
16472
+ {
16473
+ type: "text",
16474
+ text: `Unknown tool "${req.params.name}". Call tools/list first.`
16475
+ }
16476
+ ]
16477
+ };
16478
+ }
16479
+ return runGadgetForMcp(gadget, req.params.arguments ?? {});
16480
+ }
16481
+ );
16482
+ }
16483
+ if (hasPrompts && skills) {
16484
+ server.setRequestHandler(typesMod.ListPromptsRequestSchema, async () => ({
16485
+ prompts: Array.from(skills.getAll()).map(skillToMcpPrompt)
16486
+ }));
16487
+ server.setRequestHandler(
16488
+ typesMod.GetPromptRequestSchema,
16489
+ async (req) => {
16490
+ const skill = skills.get(req.params.name);
16491
+ if (!skill) {
16492
+ throw new Error(`Unknown prompt "${req.params.name}"`);
16493
+ }
16494
+ const result = await renderSkillForMcpPrompt(skill, req.params.arguments ?? {});
16495
+ return result;
16496
+ }
16497
+ );
16498
+ }
16499
+ sdkServer = server;
16500
+ return server;
16501
+ }
17684
16502
  return {
17685
- result,
17686
- media: [
17687
- {
17688
- kind: "file",
17689
- data: buffer.toString("base64"),
17690
- mimeType,
17691
- description: options?.description,
17692
- fileName: options?.fileName
16503
+ get running() {
16504
+ return running;
16505
+ },
16506
+ async connect(transport) {
16507
+ const server = await ensureServer();
16508
+ await server.connect(transport);
16509
+ running = true;
16510
+ },
16511
+ async stop() {
16512
+ if (!sdkServer) return;
16513
+ try {
16514
+ await sdkServer.close();
16515
+ } catch {
17693
16516
  }
17694
- ],
17695
- cost: options?.cost
16517
+ sdkServer = null;
16518
+ running = false;
16519
+ }
17696
16520
  };
17697
16521
  }
17698
16522
 
16523
+ // src/mcp/index.ts
16524
+ init_tool_adapter();
16525
+
17699
16526
  // src/index.ts
17700
- init_output_viewer();
17701
- init_parser2();
17702
- init_registry();
17703
- init_typed_gadget();
17704
16527
  init_constants2();
17705
16528
 
17706
16529
  // src/utils/config-resolver.ts
@@ -17809,38 +16632,6 @@ function hasHostExports(ctx) {
17809
16632
  init_media_store();
17810
16633
  init_schema_to_json();
17811
16634
  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
16635
  init_logger();
17845
16636
 
17846
16637
  // src/package/manifest.ts
@@ -18138,6 +16929,7 @@ export {
18138
16929
  ConversationManager,
18139
16930
  DEFAULT_COMPACTION_CONFIG,
18140
16931
  DEFAULT_HINTS,
16932
+ DEFAULT_MCP_COMMAND_ALLOWLIST,
18141
16933
  DEFAULT_PROMPTS,
18142
16934
  DEFAULT_RATE_LIMIT_CONFIG,
18143
16935
  DEFAULT_RETRY_CONFIG,
@@ -18157,10 +16949,17 @@ export {
18157
16949
  HuggingFaceProvider,
18158
16950
  HumanInputRequiredException,
18159
16951
  HybridStrategy,
16952
+ JsonSchemaConversionError,
18160
16953
  LLMMessageBuilder,
18161
16954
  LLMist,
18162
16955
  LOAD_SKILL_GADGET_NAME,
18163
16956
  MODEL_ALIASES,
16957
+ McpClient,
16958
+ McpConnectError,
16959
+ McpError,
16960
+ McpLifecycle,
16961
+ McpToolCallError,
16962
+ McpUntrustedCommandError,
18164
16963
  MediaStore,
18165
16964
  ModelIdentifierParser,
18166
16965
  ModelRegistry,
@@ -18176,6 +16975,7 @@ export {
18176
16975
  SummarizationStrategy,
18177
16976
  TaskCompletionSignal,
18178
16977
  TimeoutException,
16978
+ assertCommandAllowed,
18179
16979
  audioFromBase64,
18180
16980
  audioFromBuffer,
18181
16981
  collectEvents,
@@ -18190,6 +16990,7 @@ export {
18190
16990
  createHuggingFaceProviderFromEnv,
18191
16991
  createLoadSkillGadget,
18192
16992
  createLogger,
16993
+ createMcpServer,
18193
16994
  createMediaOutput,
18194
16995
  createOpenAIProviderFromEnv,
18195
16996
  createOpenRouterProviderFromEnv,
@@ -18212,7 +17013,9 @@ export {
18212
17013
  formatLLMError,
18213
17014
  formatLlmRequest,
18214
17015
  gadgetError,
17016
+ gadgetResultToMcpContent,
18215
17017
  gadgetSuccess,
17018
+ gadgetToMcpTool,
18216
17019
  getErrorMessage,
18217
17020
  getHostExports2 as getHostExports,
18218
17021
  getModelId,
@@ -18240,9 +17043,11 @@ export {
18240
17043
  isSubagentEvent,
18241
17044
  isTextPart,
18242
17045
  iterationProgressHint,
17046
+ jsonSchemaToZod,
18243
17047
  listPresets,
18244
17048
  listSubagents,
18245
17049
  loadSkillsFromDirectory,
17050
+ mcpToolToGadget,
18246
17051
  normalizeMessageContent,
18247
17052
  parallelGadgetHint,
18248
17053
  parseDataUrl,
@@ -18253,6 +17058,7 @@ export {
18253
17058
  parseSkillContent,
18254
17059
  parseSkillFile,
18255
17060
  randomDelay,
17061
+ renderSkillForMcpPrompt,
18256
17062
  resetFileLoggingState,
18257
17063
  resolveConfig,
18258
17064
  resolveHintTemplate,
@@ -18270,9 +17076,11 @@ export {
18270
17076
  resultWithImage,
18271
17077
  resultWithImages,
18272
17078
  resultWithMedia,
17079
+ runGadgetForMcp,
18273
17080
  runWithHandlers,
18274
17081
  scanResources,
18275
17082
  schemaToJSONSchema,
17083
+ skillToMcpPrompt,
18276
17084
  stream,
18277
17085
  stripProviderPrefix,
18278
17086
  substituteArguments,
@@ -18288,6 +17096,6 @@ export {
18288
17096
  withErrorHandling,
18289
17097
  withRetry,
18290
17098
  withTimeout,
18291
- z6 as z
17099
+ z4 as z
18292
17100
  };
18293
17101
  //# sourceMappingURL=index.js.map