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/README.md +21 -0
- package/dist/chunk-HM7PUGPA.js +2252 -0
- package/dist/chunk-HM7PUGPA.js.map +1 -0
- package/dist/index.cjs +1416 -331
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +448 -13
- package/dist/index.d.ts +448 -13
- package/dist/index.js +486 -1678
- package/dist/index.js.map +1 -1
- package/dist/runtime-GKQ6QIQP.js +187 -0
- package/dist/runtime-GKQ6QIQP.js.map +1 -0
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -1,29 +1,87 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
4560
|
-
const
|
|
4561
|
-
|
|
4562
|
-
|
|
4563
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
4578
|
-
* Combines name, description, and parameter schema into a formatted instruction.
|
|
3250
|
+
* Process afterLLMCall controller and return modified final message.
|
|
4579
3251
|
*
|
|
4580
|
-
*
|
|
4581
|
-
*
|
|
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
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
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
|
|
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
|
|
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:
|
|
4779
|
-
id:
|
|
4780
|
-
mode:
|
|
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:
|
|
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:
|
|
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 =
|
|
4927
|
-
regex:
|
|
4928
|
-
include:
|
|
4929
|
-
before:
|
|
4930
|
-
after:
|
|
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
|
|
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:
|
|
5477
|
-
skill:
|
|
5478
|
-
arguments:
|
|
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
|
|
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 } = (
|
|
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
|
|
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:
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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/
|
|
17572
|
-
|
|
17573
|
-
|
|
17574
|
-
|
|
17575
|
-
|
|
17576
|
-
|
|
17577
|
-
|
|
17578
|
-
|
|
17579
|
-
|
|
17580
|
-
|
|
17581
|
-
|
|
17582
|
-
|
|
17583
|
-
|
|
17584
|
-
|
|
17585
|
-
|
|
17586
|
-
}
|
|
17587
|
-
|
|
17588
|
-
|
|
17589
|
-
|
|
17590
|
-
|
|
17591
|
-
|
|
17592
|
-
const
|
|
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
|
-
|
|
17595
|
-
|
|
17596
|
-
|
|
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
|
|
17603
|
-
if (
|
|
17604
|
-
|
|
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
|
-
|
|
17613
|
-
|
|
17614
|
-
|
|
17615
|
-
|
|
17616
|
-
|
|
17617
|
-
|
|
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
|
-
|
|
17622
|
-
|
|
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
|
|
17636
|
-
if (
|
|
17637
|
-
|
|
17638
|
-
}
|
|
17639
|
-
|
|
17640
|
-
const
|
|
17641
|
-
const
|
|
17642
|
-
if (
|
|
17643
|
-
|
|
17644
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
17659
|
-
|
|
17660
|
-
|
|
17661
|
-
|
|
17662
|
-
|
|
17663
|
-
|
|
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
|
-
|
|
17669
|
-
|
|
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
|
-
|
|
17672
|
-
|
|
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
|
-
|
|
17683
|
-
|
|
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
|
-
|
|
17686
|
-
|
|
17687
|
-
|
|
17688
|
-
|
|
17689
|
-
|
|
17690
|
-
|
|
17691
|
-
|
|
17692
|
-
|
|
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
|
-
|
|
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
|
-
|
|
17099
|
+
z4 as z
|
|
18292
17100
|
};
|
|
18293
17101
|
//# sourceMappingURL=index.js.map
|