llmist 17.3.0 → 17.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -0
- package/dist/chunk-HM7PUGPA.js +2252 -0
- package/dist/chunk-HM7PUGPA.js.map +1 -0
- package/dist/index.cjs +1415 -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 -1679
- 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";
|
|
@@ -2427,128 +1762,8 @@ var init_media_store = __esm({
|
|
|
2427
1762
|
this.initialized = false;
|
|
2428
1763
|
}
|
|
2429
1764
|
this.clear();
|
|
2430
|
-
}
|
|
2431
|
-
};
|
|
2432
|
-
}
|
|
2433
|
-
});
|
|
2434
|
-
|
|
2435
|
-
// src/logging/logger.ts
|
|
2436
|
-
import { createWriteStream, mkdirSync } from "fs";
|
|
2437
|
-
import { dirname } from "path";
|
|
2438
|
-
import { Logger } from "tslog";
|
|
2439
|
-
function parseLogLevel(value) {
|
|
2440
|
-
if (!value) {
|
|
2441
|
-
return void 0;
|
|
2442
|
-
}
|
|
2443
|
-
const normalized = value.trim().toLowerCase();
|
|
2444
|
-
if (normalized === "") {
|
|
2445
|
-
return void 0;
|
|
2446
|
-
}
|
|
2447
|
-
const numericLevel = Number(normalized);
|
|
2448
|
-
if (Number.isFinite(numericLevel)) {
|
|
2449
|
-
return Math.max(0, Math.min(6, Math.floor(numericLevel)));
|
|
2450
|
-
}
|
|
2451
|
-
return LEVEL_NAME_TO_ID[normalized];
|
|
2452
|
-
}
|
|
2453
|
-
function parseEnvBoolean(value) {
|
|
2454
|
-
if (!value) return void 0;
|
|
2455
|
-
const normalized = value.trim().toLowerCase();
|
|
2456
|
-
if (normalized === "true" || normalized === "1") return true;
|
|
2457
|
-
if (normalized === "false" || normalized === "0") return false;
|
|
2458
|
-
return void 0;
|
|
2459
|
-
}
|
|
2460
|
-
function stripAnsi(str) {
|
|
2461
|
-
return str.replace(/\x1b\[[0-9;]*m/g, "");
|
|
2462
|
-
}
|
|
2463
|
-
function createLogger(options = {}) {
|
|
2464
|
-
const envMinLevel = parseLogLevel(process.env.LLMIST_LOG_LEVEL);
|
|
2465
|
-
const envLogFile = process.env.LLMIST_LOG_FILE?.trim() ?? "";
|
|
2466
|
-
const envLogReset = parseEnvBoolean(process.env.LLMIST_LOG_RESET);
|
|
2467
|
-
const minLevel = options.minLevel ?? envMinLevel ?? 4;
|
|
2468
|
-
const defaultType = options.type ?? "pretty";
|
|
2469
|
-
const name = options.name ?? "llmist";
|
|
2470
|
-
const logReset = options.logReset ?? envLogReset ?? false;
|
|
2471
|
-
const envLogTee = parseEnvBoolean(process.env.LLMIST_LOG_TEE);
|
|
2472
|
-
const teeToConsole = options.teeToConsole ?? envLogTee ?? false;
|
|
2473
|
-
if (envLogFile && (!logFileInitialized || sharedLogFilePath !== envLogFile)) {
|
|
2474
|
-
try {
|
|
2475
|
-
if (sharedLogFileStream) {
|
|
2476
|
-
sharedLogFileStream.end();
|
|
2477
|
-
sharedLogFileStream = void 0;
|
|
2478
|
-
}
|
|
2479
|
-
mkdirSync(dirname(envLogFile), { recursive: true });
|
|
2480
|
-
const flags = logReset ? "w" : "a";
|
|
2481
|
-
sharedLogFileStream = createWriteStream(envLogFile, { flags });
|
|
2482
|
-
sharedLogFilePath = envLogFile;
|
|
2483
|
-
logFileInitialized = true;
|
|
2484
|
-
writeErrorCount = 0;
|
|
2485
|
-
writeErrorReported = false;
|
|
2486
|
-
sharedLogFileStream.on("error", (error) => {
|
|
2487
|
-
writeErrorCount++;
|
|
2488
|
-
if (!writeErrorReported) {
|
|
2489
|
-
console.error(`[llmist] Log file write error: ${error.message}`);
|
|
2490
|
-
writeErrorReported = true;
|
|
2491
|
-
}
|
|
2492
|
-
if (writeErrorCount >= MAX_WRITE_ERRORS_BEFORE_DISABLE) {
|
|
2493
|
-
console.error(
|
|
2494
|
-
`[llmist] Too many log file errors (${writeErrorCount}), disabling file logging`
|
|
2495
|
-
);
|
|
2496
|
-
sharedLogFileStream?.end();
|
|
2497
|
-
sharedLogFileStream = void 0;
|
|
2498
|
-
}
|
|
2499
|
-
});
|
|
2500
|
-
} catch (error) {
|
|
2501
|
-
console.error("Failed to initialize LLMIST_LOG_FILE output:", error);
|
|
2502
|
-
}
|
|
2503
|
-
}
|
|
2504
|
-
const useFileLogging = Boolean(sharedLogFileStream);
|
|
2505
|
-
const logger2 = new Logger({
|
|
2506
|
-
name,
|
|
2507
|
-
minLevel,
|
|
2508
|
-
type: useFileLogging ? "pretty" : defaultType,
|
|
2509
|
-
// Hide log position for file logging and non-pretty types
|
|
2510
|
-
hideLogPositionForProduction: useFileLogging || defaultType !== "pretty",
|
|
2511
|
-
prettyLogTemplate: LOG_TEMPLATE,
|
|
2512
|
-
// Use overwrite to redirect tslog's formatted output to file instead of console
|
|
2513
|
-
overwrite: useFileLogging ? {
|
|
2514
|
-
transportFormatted: (logMetaMarkup, logArgs, _logErrors) => {
|
|
2515
|
-
const args = logArgs.map(
|
|
2516
|
-
(arg) => typeof arg === "string" ? arg : JSON.stringify(arg)
|
|
2517
|
-
);
|
|
2518
|
-
if (sharedLogFileStream) {
|
|
2519
|
-
const meta = stripAnsi(logMetaMarkup);
|
|
2520
|
-
const fileArgs = args.map((a) => stripAnsi(a));
|
|
2521
|
-
sharedLogFileStream.write(`${meta}${fileArgs.join(" ")}
|
|
2522
|
-
`);
|
|
2523
|
-
}
|
|
2524
|
-
if (teeToConsole) {
|
|
2525
|
-
process.stdout.write(`${logMetaMarkup}${args.join(" ")}
|
|
2526
|
-
`);
|
|
2527
|
-
}
|
|
2528
|
-
}
|
|
2529
|
-
} : void 0
|
|
2530
|
-
});
|
|
2531
|
-
return logger2;
|
|
2532
|
-
}
|
|
2533
|
-
var LEVEL_NAME_TO_ID, sharedLogFilePath, sharedLogFileStream, logFileInitialized, writeErrorCount, writeErrorReported, MAX_WRITE_ERRORS_BEFORE_DISABLE, LOG_TEMPLATE, defaultLogger;
|
|
2534
|
-
var init_logger = __esm({
|
|
2535
|
-
"src/logging/logger.ts"() {
|
|
2536
|
-
"use strict";
|
|
2537
|
-
LEVEL_NAME_TO_ID = {
|
|
2538
|
-
silly: 0,
|
|
2539
|
-
trace: 1,
|
|
2540
|
-
debug: 2,
|
|
2541
|
-
info: 3,
|
|
2542
|
-
warn: 4,
|
|
2543
|
-
error: 5,
|
|
2544
|
-
fatal: 6
|
|
1765
|
+
}
|
|
2545
1766
|
};
|
|
2546
|
-
logFileInitialized = false;
|
|
2547
|
-
writeErrorCount = 0;
|
|
2548
|
-
writeErrorReported = false;
|
|
2549
|
-
MAX_WRITE_ERRORS_BEFORE_DISABLE = 5;
|
|
2550
|
-
LOG_TEMPLATE = "{{yyyy}}-{{mm}}-{{dd}} {{hh}}:{{MM}}:{{ss}}:{{ms}} {{logLevelName}} [{{name}}] ";
|
|
2551
|
-
defaultLogger = createLogger();
|
|
2552
1767
|
}
|
|
2553
1768
|
});
|
|
2554
1769
|
|
|
@@ -3066,6 +2281,16 @@ var init_conversation_manager = __esm({
|
|
|
3066
2281
|
getBaseMessages() {
|
|
3067
2282
|
return [...this.baseMessages, ...this.initialMessages];
|
|
3068
2283
|
}
|
|
2284
|
+
/**
|
|
2285
|
+
* Replace the base (system + gadget catalog) messages.
|
|
2286
|
+
*
|
|
2287
|
+
* Used when async setup (e.g. MCP server connect-and-list) discovers
|
|
2288
|
+
* additional gadgets after the agent was constructed. Conversation history
|
|
2289
|
+
* is preserved; only the leading system block is swapped.
|
|
2290
|
+
*/
|
|
2291
|
+
replaceBaseMessages(newBase) {
|
|
2292
|
+
this.baseMessages = newBase;
|
|
2293
|
+
}
|
|
3069
2294
|
replaceHistory(newHistory) {
|
|
3070
2295
|
this.historyBuilder = new LLMMessageBuilder();
|
|
3071
2296
|
if (this.startPrefix && this.endPrefix) {
|
|
@@ -3995,665 +3220,82 @@ var init_llm_call_lifecycle = __esm({
|
|
|
3995
3220
|
if (this.caching !== void 0) return this.caching;
|
|
3996
3221
|
return { enabled: true };
|
|
3997
3222
|
}
|
|
3998
|
-
/**
|
|
3999
|
-
* Calculate cost and complete LLM call in execution tree.
|
|
4000
|
-
* Also records usage to rate limit tracker for proactive throttling.
|
|
4001
|
-
*/
|
|
4002
|
-
completeLLMCallInTree(nodeId, result) {
|
|
4003
|
-
const inputTokens = result.usage?.inputTokens ?? 0;
|
|
4004
|
-
const outputTokens = result.usage?.outputTokens ?? 0;
|
|
4005
|
-
if (this.rateLimitTracker) {
|
|
4006
|
-
this.rateLimitTracker.recordUsage(inputTokens, outputTokens);
|
|
4007
|
-
}
|
|
4008
|
-
const llmCost = this.client.modelRegistry?.estimateCost?.(
|
|
4009
|
-
this.model,
|
|
4010
|
-
inputTokens,
|
|
4011
|
-
outputTokens,
|
|
4012
|
-
result.usage?.cachedInputTokens ?? 0,
|
|
4013
|
-
result.usage?.cacheCreationInputTokens ?? 0,
|
|
4014
|
-
result.usage?.reasoningTokens ?? 0
|
|
4015
|
-
)?.totalCost;
|
|
4016
|
-
this.tree.completeLLMCall(nodeId, {
|
|
4017
|
-
response: result.rawResponse,
|
|
4018
|
-
usage: result.usage,
|
|
4019
|
-
finishReason: result.finishReason,
|
|
4020
|
-
cost: llmCost,
|
|
4021
|
-
thinkingContent: result.thinkingContent
|
|
4022
|
-
});
|
|
4023
|
-
}
|
|
4024
|
-
/**
|
|
4025
|
-
* Process afterLLMCall controller and return modified final message.
|
|
4026
|
-
*
|
|
4027
|
-
* Skips controller invocation for interrupted calls (`finishReason === "interrupted"`)
|
|
4028
|
-
* to preserve the original Agent behavior: the `afterLLMCall` controller was never
|
|
4029
|
-
* invoked for interrupted calls in the pre-extraction code. Skipping it here prevents
|
|
4030
|
-
* side effects (e.g., `append_messages` mutating conversation state) during cleanup
|
|
4031
|
-
* of an already-exited run loop.
|
|
4032
|
-
*/
|
|
4033
|
-
async processAfterLLMCallController(iteration, llmOptions, result, gadgetCallCount) {
|
|
4034
|
-
let finalMessage = result.finalMessage;
|
|
4035
|
-
if (!this.hooks.controllers?.afterLLMCall || result.finishReason === "interrupted") {
|
|
4036
|
-
return finalMessage;
|
|
4037
|
-
}
|
|
4038
|
-
const context = {
|
|
4039
|
-
iteration,
|
|
4040
|
-
maxIterations: this.maxIterations,
|
|
4041
|
-
budget: this.budget,
|
|
4042
|
-
totalCost: this.tree.getTotalCost(),
|
|
4043
|
-
options: llmOptions,
|
|
4044
|
-
finishReason: result.finishReason,
|
|
4045
|
-
usage: result.usage,
|
|
4046
|
-
finalMessage: result.finalMessage,
|
|
4047
|
-
gadgetCallCount,
|
|
4048
|
-
logger: this.logger
|
|
4049
|
-
};
|
|
4050
|
-
const action = await this.hooks.controllers.afterLLMCall(context);
|
|
4051
|
-
validateAfterLLMCallAction(action);
|
|
4052
|
-
if (action.action === "modify_and_continue" || action.action === "append_and_modify") {
|
|
4053
|
-
finalMessage = action.modifiedMessage;
|
|
4054
|
-
}
|
|
4055
|
-
if (action.action === "append_messages" || action.action === "append_and_modify") {
|
|
4056
|
-
for (const msg of action.messages) {
|
|
4057
|
-
if (msg.role === "user") {
|
|
4058
|
-
this.conversation.addUserMessage(msg.content);
|
|
4059
|
-
} else if (msg.role === "assistant") {
|
|
4060
|
-
this.conversation.addAssistantMessage(extractMessageText(msg.content));
|
|
4061
|
-
} else if (msg.role === "system") {
|
|
4062
|
-
this.conversation.addUserMessage(`[System] ${extractMessageText(msg.content)}`);
|
|
4063
|
-
}
|
|
4064
|
-
}
|
|
4065
|
-
}
|
|
4066
|
-
return finalMessage;
|
|
4067
|
-
}
|
|
4068
|
-
};
|
|
4069
|
-
}
|
|
4070
|
-
});
|
|
4071
|
-
|
|
4072
|
-
// src/gadgets/schema-to-json.ts
|
|
4073
|
-
import * as z from "zod";
|
|
4074
|
-
function schemaToJSONSchema(schema, options) {
|
|
4075
|
-
const jsonSchema = z.toJSONSchema(schema, options ?? { target: "draft-7" });
|
|
4076
|
-
const mismatches = detectDescriptionMismatch(schema, jsonSchema);
|
|
4077
|
-
if (mismatches.length > 0) {
|
|
4078
|
-
defaultLogger.warn(
|
|
4079
|
-
`Zod instance mismatch detected: ${mismatches.length} description(s) lost. For best results, use: import { z } from "llmist"`
|
|
4080
|
-
);
|
|
4081
|
-
return mergeDescriptions(schema, jsonSchema);
|
|
4082
|
-
}
|
|
4083
|
-
return jsonSchema;
|
|
4084
|
-
}
|
|
4085
|
-
function detectDescriptionMismatch(schema, jsonSchema) {
|
|
4086
|
-
const mismatches = [];
|
|
4087
|
-
function checkSchema(zodSchema, json, path3) {
|
|
4088
|
-
if (!zodSchema || typeof zodSchema !== "object") return;
|
|
4089
|
-
const def = zodSchema._def;
|
|
4090
|
-
const jsonObj = json;
|
|
4091
|
-
if (def?.description && !jsonObj?.description) {
|
|
4092
|
-
mismatches.push(path3 || "root");
|
|
4093
|
-
}
|
|
4094
|
-
if (def?.typeName === "ZodObject" && def?.shape) {
|
|
4095
|
-
const shape = typeof def.shape === "function" ? def.shape() : def.shape;
|
|
4096
|
-
for (const [key, fieldSchema] of Object.entries(shape)) {
|
|
4097
|
-
const properties = jsonObj?.properties;
|
|
4098
|
-
const jsonProp = properties?.[key];
|
|
4099
|
-
checkSchema(fieldSchema, jsonProp, path3 ? `${path3}.${key}` : key);
|
|
4100
|
-
}
|
|
4101
|
-
}
|
|
4102
|
-
if (def?.typeName === "ZodArray" && def?.type) {
|
|
4103
|
-
checkSchema(def.type, jsonObj?.items, path3 ? `${path3}[]` : "[]");
|
|
4104
|
-
}
|
|
4105
|
-
if ((def?.typeName === "ZodOptional" || def?.typeName === "ZodNullable") && def?.innerType) {
|
|
4106
|
-
checkSchema(def.innerType, json, path3);
|
|
4107
|
-
}
|
|
4108
|
-
if (def?.typeName === "ZodDefault" && def?.innerType) {
|
|
4109
|
-
checkSchema(def.innerType, json, path3);
|
|
4110
|
-
}
|
|
4111
|
-
}
|
|
4112
|
-
checkSchema(schema, jsonSchema, "");
|
|
4113
|
-
return mismatches;
|
|
4114
|
-
}
|
|
4115
|
-
function mergeDescriptions(schema, jsonSchema) {
|
|
4116
|
-
function merge(zodSchema, json) {
|
|
4117
|
-
if (!json || typeof json !== "object") return json;
|
|
4118
|
-
const def = zodSchema._def;
|
|
4119
|
-
const jsonObj = json;
|
|
4120
|
-
const merged = { ...jsonObj };
|
|
4121
|
-
if (def?.description && !jsonObj.description) {
|
|
4122
|
-
merged.description = def.description;
|
|
4123
|
-
}
|
|
4124
|
-
if (def?.typeName === "ZodObject" && def?.shape && jsonObj.properties) {
|
|
4125
|
-
const shape = typeof def.shape === "function" ? def.shape() : def.shape;
|
|
4126
|
-
const properties = jsonObj.properties;
|
|
4127
|
-
merged.properties = { ...properties };
|
|
4128
|
-
for (const [key, fieldSchema] of Object.entries(shape)) {
|
|
4129
|
-
if (properties[key]) {
|
|
4130
|
-
merged.properties[key] = merge(fieldSchema, properties[key]);
|
|
4131
|
-
}
|
|
4132
|
-
}
|
|
4133
|
-
}
|
|
4134
|
-
if (def?.typeName === "ZodArray" && def?.type && jsonObj.items) {
|
|
4135
|
-
merged.items = merge(def.type, jsonObj.items);
|
|
4136
|
-
}
|
|
4137
|
-
if ((def?.typeName === "ZodOptional" || def?.typeName === "ZodNullable") && def?.innerType) {
|
|
4138
|
-
return merge(def.innerType, json);
|
|
4139
|
-
}
|
|
4140
|
-
if (def?.typeName === "ZodDefault" && def?.innerType) {
|
|
4141
|
-
return merge(def.innerType, json);
|
|
4142
|
-
}
|
|
4143
|
-
return merged;
|
|
4144
|
-
}
|
|
4145
|
-
return merge(schema, jsonSchema);
|
|
4146
|
-
}
|
|
4147
|
-
var init_schema_to_json = __esm({
|
|
4148
|
-
"src/gadgets/schema-to-json.ts"() {
|
|
4149
|
-
"use strict";
|
|
4150
|
-
init_logger();
|
|
4151
|
-
}
|
|
4152
|
-
});
|
|
4153
|
-
|
|
4154
|
-
// src/gadgets/schema-validator.ts
|
|
4155
|
-
import * as z2 from "zod";
|
|
4156
|
-
function validateGadgetSchema(schema, gadgetName) {
|
|
4157
|
-
let jsonSchema;
|
|
4158
|
-
try {
|
|
4159
|
-
jsonSchema = z2.toJSONSchema(schema, { target: "draft-7" });
|
|
4160
|
-
} catch (error) {
|
|
4161
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4162
|
-
throw new Error(
|
|
4163
|
-
`Gadget "${gadgetName}" has a schema that cannot be serialized to JSON Schema.
|
|
4164
|
-
This usually happens with unsupported patterns like:
|
|
4165
|
-
- z.record() - use z.object({}).passthrough() instead
|
|
4166
|
-
- Complex transforms or custom refinements
|
|
4167
|
-
- Circular references
|
|
4168
|
-
|
|
4169
|
-
Original error: ${errorMessage}
|
|
4170
|
-
|
|
4171
|
-
Only use schema patterns that Zod v4's native toJSONSchema() supports.`
|
|
4172
|
-
);
|
|
4173
|
-
}
|
|
4174
|
-
const issues = findUnknownTypes(jsonSchema);
|
|
4175
|
-
if (issues.length > 0) {
|
|
4176
|
-
const fieldList = issues.join(", ");
|
|
4177
|
-
throw new Error(
|
|
4178
|
-
`Gadget "${gadgetName}" uses z.unknown() which produces incomplete schemas.
|
|
4179
|
-
Problematic fields: ${fieldList}
|
|
4180
|
-
|
|
4181
|
-
z.unknown() doesn't generate type information in JSON Schema, making it unclear
|
|
4182
|
-
to the LLM what data structure to provide.
|
|
4183
|
-
|
|
4184
|
-
Suggestions:
|
|
4185
|
-
- Use z.object({}).passthrough() for flexible objects
|
|
4186
|
-
- Use z.record(z.string()) for key-value objects with string values
|
|
4187
|
-
- Define specific structure if possible
|
|
4188
|
-
|
|
4189
|
-
Example fixes:
|
|
4190
|
-
// \u274C Bad
|
|
4191
|
-
content: z.unknown()
|
|
4192
|
-
|
|
4193
|
-
// \u2705 Good
|
|
4194
|
-
content: z.object({}).passthrough() // for flexible objects
|
|
4195
|
-
content: z.record(z.string()) // for key-value objects
|
|
4196
|
-
content: z.array(z.string()) // for arrays of strings
|
|
4197
|
-
`
|
|
4198
|
-
);
|
|
4199
|
-
}
|
|
4200
|
-
}
|
|
4201
|
-
function findUnknownTypes(schema, path3 = []) {
|
|
4202
|
-
const issues = [];
|
|
4203
|
-
if (!schema || typeof schema !== "object") {
|
|
4204
|
-
return issues;
|
|
4205
|
-
}
|
|
4206
|
-
if (schema.definitions) {
|
|
4207
|
-
for (const defSchema of Object.values(schema.definitions)) {
|
|
4208
|
-
issues.push(...findUnknownTypes(defSchema, []));
|
|
4209
|
-
}
|
|
4210
|
-
}
|
|
4211
|
-
if (schema.properties) {
|
|
4212
|
-
for (const [propName, propSchema] of Object.entries(schema.properties)) {
|
|
4213
|
-
const propPath = [...path3, propName];
|
|
4214
|
-
if (hasNoType(propSchema)) {
|
|
4215
|
-
issues.push(propPath.join(".") || propName);
|
|
4216
|
-
}
|
|
4217
|
-
issues.push(...findUnknownTypes(propSchema, propPath));
|
|
4218
|
-
}
|
|
4219
|
-
}
|
|
4220
|
-
if (schema.items) {
|
|
4221
|
-
const itemPath = [...path3, "[]"];
|
|
4222
|
-
if (hasNoType(schema.items)) {
|
|
4223
|
-
issues.push(itemPath.join("."));
|
|
4224
|
-
}
|
|
4225
|
-
issues.push(...findUnknownTypes(schema.items, itemPath));
|
|
4226
|
-
}
|
|
4227
|
-
if (schema.anyOf) {
|
|
4228
|
-
schema.anyOf.forEach((subSchema, index) => {
|
|
4229
|
-
issues.push(...findUnknownTypes(subSchema, [...path3, `anyOf[${index}]`]));
|
|
4230
|
-
});
|
|
4231
|
-
}
|
|
4232
|
-
if (schema.oneOf) {
|
|
4233
|
-
schema.oneOf.forEach((subSchema, index) => {
|
|
4234
|
-
issues.push(...findUnknownTypes(subSchema, [...path3, `oneOf[${index}]`]));
|
|
4235
|
-
});
|
|
4236
|
-
}
|
|
4237
|
-
if (schema.allOf) {
|
|
4238
|
-
schema.allOf.forEach((subSchema, index) => {
|
|
4239
|
-
issues.push(...findUnknownTypes(subSchema, [...path3, `allOf[${index}]`]));
|
|
4240
|
-
});
|
|
4241
|
-
}
|
|
4242
|
-
return issues;
|
|
4243
|
-
}
|
|
4244
|
-
function hasNoType(prop) {
|
|
4245
|
-
if (!prop || typeof prop !== "object") {
|
|
4246
|
-
return false;
|
|
4247
|
-
}
|
|
4248
|
-
const hasType = prop.type !== void 0;
|
|
4249
|
-
const hasRef = prop.$ref !== void 0;
|
|
4250
|
-
const hasUnion = prop.anyOf !== void 0 || prop.oneOf !== void 0 || prop.allOf !== void 0;
|
|
4251
|
-
if (hasType || hasRef || hasUnion) {
|
|
4252
|
-
return false;
|
|
4253
|
-
}
|
|
4254
|
-
const keys = Object.keys(prop);
|
|
4255
|
-
const metadataKeys = ["description", "title", "default", "examples"];
|
|
4256
|
-
const hasOnlyMetadata = keys.every((key) => metadataKeys.includes(key));
|
|
4257
|
-
return hasOnlyMetadata || keys.length === 0;
|
|
4258
|
-
}
|
|
4259
|
-
var init_schema_validator = __esm({
|
|
4260
|
-
"src/gadgets/schema-validator.ts"() {
|
|
4261
|
-
"use strict";
|
|
4262
|
-
}
|
|
4263
|
-
});
|
|
4264
|
-
|
|
4265
|
-
// src/gadgets/gadget.ts
|
|
4266
|
-
function formatParamsForBlockExample(params, prefix = "", argPrefix = GADGET_ARG_PREFIX) {
|
|
4267
|
-
const lines = [];
|
|
4268
|
-
for (const [key, value] of Object.entries(params)) {
|
|
4269
|
-
const fullPath = prefix ? `${prefix}/${key}` : key;
|
|
4270
|
-
if (Array.isArray(value)) {
|
|
4271
|
-
value.forEach((item, index) => {
|
|
4272
|
-
const itemPath = `${fullPath}/${index}`;
|
|
4273
|
-
if (typeof item === "object" && item !== null) {
|
|
4274
|
-
lines.push(
|
|
4275
|
-
formatParamsForBlockExample(item, itemPath, argPrefix)
|
|
4276
|
-
);
|
|
4277
|
-
} else {
|
|
4278
|
-
lines.push(`${argPrefix}${itemPath}`);
|
|
4279
|
-
lines.push(String(item));
|
|
4280
|
-
}
|
|
4281
|
-
});
|
|
4282
|
-
} else if (typeof value === "object" && value !== null) {
|
|
4283
|
-
lines.push(
|
|
4284
|
-
formatParamsForBlockExample(value, fullPath, argPrefix)
|
|
4285
|
-
);
|
|
4286
|
-
} else {
|
|
4287
|
-
lines.push(`${argPrefix}${fullPath}`);
|
|
4288
|
-
lines.push(String(value));
|
|
4289
|
-
}
|
|
4290
|
-
}
|
|
4291
|
-
return lines.join("\n");
|
|
4292
|
-
}
|
|
4293
|
-
function formatParamLine(key, propObj, isRequired, indent = "") {
|
|
4294
|
-
const type = propObj.type;
|
|
4295
|
-
const description = propObj.description;
|
|
4296
|
-
const enumValues = propObj.enum;
|
|
4297
|
-
let line = `${indent}- ${key}`;
|
|
4298
|
-
if (type === "array") {
|
|
4299
|
-
const items = propObj.items;
|
|
4300
|
-
const itemType = items?.type || "any";
|
|
4301
|
-
line += ` (array of ${itemType})`;
|
|
4302
|
-
} else if (type === "object" && propObj.properties) {
|
|
4303
|
-
line += " (object)";
|
|
4304
|
-
} else {
|
|
4305
|
-
line += ` (${type})`;
|
|
4306
|
-
}
|
|
4307
|
-
if (isRequired && indent !== "") {
|
|
4308
|
-
line += " [required]";
|
|
4309
|
-
}
|
|
4310
|
-
if (description) {
|
|
4311
|
-
line += `: ${description}`;
|
|
4312
|
-
}
|
|
4313
|
-
if (enumValues) {
|
|
4314
|
-
line += ` - one of: ${enumValues.map((v) => `"${v}"`).join(", ")}`;
|
|
4315
|
-
}
|
|
4316
|
-
return line;
|
|
4317
|
-
}
|
|
4318
|
-
function formatSchemaAsPlainText(schema, indent = "", atRoot = true) {
|
|
4319
|
-
const lines = [];
|
|
4320
|
-
const properties = schema.properties || {};
|
|
4321
|
-
const required = schema.required || [];
|
|
4322
|
-
if (atRoot && indent === "") {
|
|
4323
|
-
const requiredProps = [];
|
|
4324
|
-
const optionalProps = [];
|
|
4325
|
-
for (const [key, prop] of Object.entries(properties)) {
|
|
4326
|
-
if (required.includes(key)) {
|
|
4327
|
-
requiredProps.push([key, prop]);
|
|
4328
|
-
} else {
|
|
4329
|
-
optionalProps.push([key, prop]);
|
|
4330
|
-
}
|
|
4331
|
-
}
|
|
4332
|
-
const reqCount = requiredProps.length;
|
|
4333
|
-
const optCount = optionalProps.length;
|
|
4334
|
-
if (reqCount > 0 || optCount > 0) {
|
|
4335
|
-
const parts = [];
|
|
4336
|
-
if (reqCount > 0) parts.push(`${reqCount} required`);
|
|
4337
|
-
if (optCount > 0) parts.push(`${optCount} optional`);
|
|
4338
|
-
lines.push(parts.join(", "));
|
|
4339
|
-
lines.push("");
|
|
4340
|
-
}
|
|
4341
|
-
if (reqCount > 0) {
|
|
4342
|
-
lines.push("REQUIRED Parameters:");
|
|
4343
|
-
for (const [key, prop] of requiredProps) {
|
|
4344
|
-
lines.push(formatParamLine(key, prop, true, ""));
|
|
4345
|
-
const propObj = prop;
|
|
4346
|
-
if (propObj.type === "object" && propObj.properties) {
|
|
4347
|
-
lines.push(formatSchemaAsPlainText(propObj, " ", false));
|
|
4348
|
-
}
|
|
4349
|
-
}
|
|
4350
|
-
}
|
|
4351
|
-
if (optCount > 0) {
|
|
4352
|
-
if (reqCount > 0) lines.push("");
|
|
4353
|
-
lines.push("OPTIONAL Parameters:");
|
|
4354
|
-
for (const [key, prop] of optionalProps) {
|
|
4355
|
-
lines.push(formatParamLine(key, prop, false, ""));
|
|
4356
|
-
const propObj = prop;
|
|
4357
|
-
if (propObj.type === "object" && propObj.properties) {
|
|
4358
|
-
lines.push(formatSchemaAsPlainText(propObj, " ", false));
|
|
4359
|
-
}
|
|
4360
|
-
}
|
|
4361
|
-
}
|
|
4362
|
-
return lines.join("\n");
|
|
4363
|
-
}
|
|
4364
|
-
for (const [key, prop] of Object.entries(properties)) {
|
|
4365
|
-
const isRequired = required.includes(key);
|
|
4366
|
-
lines.push(formatParamLine(key, prop, isRequired, indent));
|
|
4367
|
-
const propObj = prop;
|
|
4368
|
-
if (propObj.type === "object" && propObj.properties) {
|
|
4369
|
-
lines.push(formatSchemaAsPlainText(propObj, indent + " ", false));
|
|
4370
|
-
}
|
|
4371
|
-
}
|
|
4372
|
-
return lines.join("\n");
|
|
4373
|
-
}
|
|
4374
|
-
var AbstractGadget;
|
|
4375
|
-
var init_gadget = __esm({
|
|
4376
|
-
"src/gadgets/gadget.ts"() {
|
|
4377
|
-
"use strict";
|
|
4378
|
-
init_constants();
|
|
4379
|
-
init_exceptions();
|
|
4380
|
-
init_schema_to_json();
|
|
4381
|
-
init_schema_validator();
|
|
4382
|
-
AbstractGadget = class {
|
|
4383
|
-
/**
|
|
4384
|
-
* The name of the gadget. Used for identification when LLM calls it.
|
|
4385
|
-
* If not provided, defaults to the class name.
|
|
4386
|
-
*/
|
|
4387
|
-
name;
|
|
4388
|
-
/**
|
|
4389
|
-
* Optional Zod schema describing the expected input payload. When provided,
|
|
4390
|
-
* it will be validated before execution and transformed into a JSON Schema
|
|
4391
|
-
* representation that is surfaced to the LLM as part of the instructions.
|
|
4392
|
-
*/
|
|
4393
|
-
parameterSchema;
|
|
4394
|
-
/**
|
|
4395
|
-
* Optional timeout in milliseconds for gadget execution.
|
|
4396
|
-
* If execution exceeds this timeout, a TimeoutException will be thrown.
|
|
4397
|
-
* If not set, the global defaultGadgetTimeoutMs from runtime options will be used.
|
|
4398
|
-
* Set to 0 or undefined to disable timeout for this gadget.
|
|
4399
|
-
*/
|
|
4400
|
-
timeoutMs;
|
|
4401
|
-
/**
|
|
4402
|
-
* Optional usage examples to help LLMs understand proper invocation.
|
|
4403
|
-
* Examples are rendered in getInstruction() alongside the schema.
|
|
4404
|
-
*
|
|
4405
|
-
* Note: Uses broader `unknown` type to allow typed examples from subclasses
|
|
4406
|
-
* while maintaining runtime compatibility.
|
|
4407
|
-
*/
|
|
4408
|
-
examples;
|
|
4409
|
-
/**
|
|
4410
|
-
* Maximum number of concurrent executions allowed for this gadget.
|
|
4411
|
-
* Use this to prevent race conditions in gadgets that modify shared state.
|
|
4412
|
-
*
|
|
4413
|
-
* - `1` = Sequential execution (only one instance runs at a time)
|
|
4414
|
-
* - `0` or `undefined` = Unlimited concurrency (default)
|
|
4415
|
-
* - `N > 1` = At most N concurrent executions
|
|
4416
|
-
*
|
|
4417
|
-
* This property sets a safety floor: external configuration (SubagentConfig)
|
|
4418
|
-
* can only make concurrency MORE restrictive, never less. For example, if
|
|
4419
|
-
* a gadget declares `maxConcurrent: 1`, external config cannot override it
|
|
4420
|
-
* to allow parallel execution.
|
|
4421
|
-
*
|
|
4422
|
-
* @example
|
|
4423
|
-
* ```typescript
|
|
4424
|
-
* // File writer that must run sequentially to avoid race conditions
|
|
4425
|
-
* class WriteFile extends Gadget({
|
|
4426
|
-
* description: 'Writes content to a file',
|
|
4427
|
-
* schema: z.object({ path: z.string(), content: z.string() }),
|
|
4428
|
-
* maxConcurrent: 1, // Sequential - prevents race conditions
|
|
4429
|
-
* }) {
|
|
4430
|
-
* execute(params: this['params']) { ... }
|
|
4431
|
-
* }
|
|
4432
|
-
* ```
|
|
4433
|
-
*/
|
|
4434
|
-
maxConcurrent;
|
|
4435
|
-
/**
|
|
4436
|
-
* If true, this gadget must execute alone — no other gadgets in the same
|
|
4437
|
-
* LLM response can run in parallel. When an exclusive gadget arrives and
|
|
4438
|
-
* other gadgets are already in-flight, it is deferred until they complete.
|
|
4439
|
-
*
|
|
4440
|
-
* Use for gadgets that terminate the agent loop (e.g., Finish), where
|
|
4441
|
-
* sibling tool results must be visible to the LLM before the loop ends.
|
|
4442
|
-
*
|
|
4443
|
-
* This is a safety floor: external config cannot weaken it.
|
|
4444
|
-
*/
|
|
4445
|
-
exclusive;
|
|
4446
|
-
/**
|
|
4447
|
-
* Throws an AbortException if the execution has been aborted.
|
|
4448
|
-
*
|
|
4449
|
-
* Call this at key checkpoints in long-running gadgets to allow early exit
|
|
4450
|
-
* when the gadget has been cancelled (e.g., due to timeout). This enables
|
|
4451
|
-
* resource cleanup and prevents unnecessary work after cancellation.
|
|
4452
|
-
*
|
|
4453
|
-
* @param ctx - The execution context containing the abort signal
|
|
4454
|
-
* @throws AbortException if ctx.signal.aborted is true
|
|
4455
|
-
*
|
|
4456
|
-
* @example
|
|
4457
|
-
* ```typescript
|
|
4458
|
-
* class DataProcessor extends Gadget({
|
|
4459
|
-
* description: 'Processes data in multiple steps',
|
|
4460
|
-
* schema: z.object({ items: z.array(z.string()) }),
|
|
4461
|
-
* }) {
|
|
4462
|
-
* async execute(params: this['params'], ctx?: ExecutionContext): Promise<string> {
|
|
4463
|
-
* const results: string[] = [];
|
|
4464
|
-
*
|
|
4465
|
-
* for (const item of params.items) {
|
|
4466
|
-
* // Check before each expensive operation
|
|
4467
|
-
* this.throwIfAborted(ctx);
|
|
4468
|
-
*
|
|
4469
|
-
* results.push(await this.processItem(item));
|
|
4470
|
-
* }
|
|
4471
|
-
*
|
|
4472
|
-
* return results.join(', ');
|
|
4473
|
-
* }
|
|
4474
|
-
* }
|
|
4475
|
-
* ```
|
|
4476
|
-
*/
|
|
4477
|
-
throwIfAborted(ctx) {
|
|
4478
|
-
if (ctx?.signal?.aborted) {
|
|
4479
|
-
throw new AbortException();
|
|
4480
|
-
}
|
|
4481
|
-
}
|
|
4482
|
-
/**
|
|
4483
|
-
* Register a cleanup function to run when execution is aborted (timeout or cancellation).
|
|
4484
|
-
* The cleanup function is called immediately if the signal is already aborted.
|
|
4485
|
-
* Errors thrown by the cleanup function are silently ignored.
|
|
4486
|
-
*
|
|
4487
|
-
* Use this to clean up resources like browser instances, database connections,
|
|
4488
|
-
* or child processes when the gadget is cancelled due to timeout.
|
|
4489
|
-
*
|
|
4490
|
-
* @param ctx - The execution context containing the abort signal
|
|
4491
|
-
* @param cleanup - Function to run on abort (can be sync or async)
|
|
4492
|
-
*
|
|
4493
|
-
* @example
|
|
4494
|
-
* ```typescript
|
|
4495
|
-
* class BrowserGadget extends Gadget({
|
|
4496
|
-
* description: 'Fetches web page content',
|
|
4497
|
-
* schema: z.object({ url: z.string() }),
|
|
4498
|
-
* }) {
|
|
4499
|
-
* async execute(params: this['params'], ctx?: ExecutionContext): Promise<string> {
|
|
4500
|
-
* const browser = await chromium.launch();
|
|
4501
|
-
* this.onAbort(ctx, () => browser.close());
|
|
4502
|
-
*
|
|
4503
|
-
* const page = await browser.newPage();
|
|
4504
|
-
* this.onAbort(ctx, () => page.close());
|
|
4505
|
-
*
|
|
4506
|
-
* await page.goto(params.url);
|
|
4507
|
-
* const content = await page.content();
|
|
4508
|
-
*
|
|
4509
|
-
* await browser.close();
|
|
4510
|
-
* return content;
|
|
4511
|
-
* }
|
|
4512
|
-
* }
|
|
4513
|
-
* ```
|
|
4514
|
-
*/
|
|
4515
|
-
onAbort(ctx, cleanup) {
|
|
4516
|
-
if (!ctx?.signal) return;
|
|
4517
|
-
const safeCleanup = () => {
|
|
4518
|
-
try {
|
|
4519
|
-
const result = cleanup();
|
|
4520
|
-
if (result && typeof result === "object" && "catch" in result) {
|
|
4521
|
-
result.catch(() => {
|
|
4522
|
-
});
|
|
4523
|
-
}
|
|
4524
|
-
} catch {
|
|
4525
|
-
}
|
|
4526
|
-
};
|
|
4527
|
-
if (ctx.signal.aborted) {
|
|
4528
|
-
safeCleanup();
|
|
4529
|
-
return;
|
|
4530
|
-
}
|
|
4531
|
-
ctx.signal.addEventListener("abort", safeCleanup, { once: true });
|
|
4532
|
-
}
|
|
4533
|
-
/**
|
|
4534
|
-
* Create an AbortController linked to the execution context's signal.
|
|
4535
|
-
* When the parent signal aborts, the returned controller also aborts with the same reason.
|
|
4536
|
-
*
|
|
4537
|
-
* Useful for passing abort signals to child operations like fetch() while still
|
|
4538
|
-
* being able to abort them independently if needed.
|
|
4539
|
-
*
|
|
4540
|
-
* @param ctx - The execution context containing the parent abort signal
|
|
4541
|
-
* @returns A new AbortController linked to the parent signal
|
|
4542
|
-
*
|
|
4543
|
-
* @example
|
|
4544
|
-
* ```typescript
|
|
4545
|
-
* class FetchGadget extends Gadget({
|
|
4546
|
-
* description: 'Fetches data from URL',
|
|
4547
|
-
* schema: z.object({ url: z.string() }),
|
|
4548
|
-
* }) {
|
|
4549
|
-
* async execute(params: this['params'], ctx?: ExecutionContext): Promise<string> {
|
|
4550
|
-
* const controller = this.createLinkedAbortController(ctx);
|
|
4551
|
-
*
|
|
4552
|
-
* // fetch() will automatically abort when parent times out
|
|
4553
|
-
* const response = await fetch(params.url, { signal: controller.signal });
|
|
4554
|
-
* return response.text();
|
|
4555
|
-
* }
|
|
4556
|
-
* }
|
|
4557
|
-
* ```
|
|
3223
|
+
/**
|
|
3224
|
+
* Calculate cost and complete LLM call in execution tree.
|
|
3225
|
+
* Also records usage to rate limit tracker for proactive throttling.
|
|
4558
3226
|
*/
|
|
4559
|
-
|
|
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
|
});
|
|
@@ -5187,7 +3829,6 @@ var init_retry_orchestrator = __esm({
|
|
|
5187
3829
|
for await (const event of processor.process(stream2)) {
|
|
5188
3830
|
if (event.type === "stream_complete") {
|
|
5189
3831
|
streamMetadata = event;
|
|
5190
|
-
continue;
|
|
5191
3832
|
}
|
|
5192
3833
|
if (event.type === "llm_response_end") {
|
|
5193
3834
|
this.tree.endLLMResponse(llmNodeId, {
|
|
@@ -5461,7 +4102,7 @@ var init_activation = __esm({
|
|
|
5461
4102
|
});
|
|
5462
4103
|
|
|
5463
4104
|
// src/skills/load-skill-gadget.ts
|
|
5464
|
-
import { z as
|
|
4105
|
+
import { z as z2 } from "zod";
|
|
5465
4106
|
function createLoadSkillGadget(registry) {
|
|
5466
4107
|
const summaries = registry.getMetadataSummaries();
|
|
5467
4108
|
const skillNames = registry.getModelInvocable().map((s) => s.name);
|
|
@@ -5473,9 +4114,9 @@ function createLoadSkillGadget(registry) {
|
|
|
5473
4114
|
return createGadget({
|
|
5474
4115
|
name: LOAD_SKILL_GADGET_NAME,
|
|
5475
4116
|
description,
|
|
5476
|
-
schema:
|
|
5477
|
-
skill:
|
|
5478
|
-
arguments:
|
|
4117
|
+
schema: z2.object({
|
|
4118
|
+
skill: z2.enum(skillNames).describe("Name of the skill to load"),
|
|
4119
|
+
arguments: z2.string().optional().describe("Arguments for the skill (e.g., a filename, issue number, or search query)")
|
|
5479
4120
|
}),
|
|
5480
4121
|
execute: async ({ skill: skillName, arguments: args }) => {
|
|
5481
4122
|
const skill = registry.get(skillName);
|
|
@@ -12910,7 +11551,7 @@ __export(client_exports, {
|
|
|
12910
11551
|
LLMist: () => LLMist
|
|
12911
11552
|
});
|
|
12912
11553
|
var LLMist;
|
|
12913
|
-
var
|
|
11554
|
+
var init_client2 = __esm({
|
|
12914
11555
|
"src/core/client.ts"() {
|
|
12915
11556
|
"use strict";
|
|
12916
11557
|
init_builder();
|
|
@@ -13224,6 +11865,7 @@ var init_builder = __esm({
|
|
|
13224
11865
|
subagents;
|
|
13225
11866
|
policies;
|
|
13226
11867
|
skills;
|
|
11868
|
+
mcp;
|
|
13227
11869
|
constructor(client) {
|
|
13228
11870
|
this.core = { client, initialMessages: [] };
|
|
13229
11871
|
this.gadgets = { gadgets: [] };
|
|
@@ -13231,6 +11873,7 @@ var init_builder = __esm({
|
|
|
13231
11873
|
this.subagents = {};
|
|
13232
11874
|
this.policies = {};
|
|
13233
11875
|
this.skills = { preActivated: [], skillDirs: [] };
|
|
11876
|
+
this.mcp = { servers: [] };
|
|
13234
11877
|
}
|
|
13235
11878
|
/** Set the model to use. Supports aliases like "sonnet", "flash". */
|
|
13236
11879
|
withModel(model) {
|
|
@@ -13277,6 +11920,45 @@ var init_builder = __esm({
|
|
|
13277
11920
|
this.gadgets.gadgets.push(...gadgets);
|
|
13278
11921
|
return this;
|
|
13279
11922
|
}
|
|
11923
|
+
/**
|
|
11924
|
+
* Attach a Model Context Protocol (MCP) server.
|
|
11925
|
+
*
|
|
11926
|
+
* The agent connects to the server lazily at the start of `run()`,
|
|
11927
|
+
* discovers its tools, and registers them as native gadgets so the LLM
|
|
11928
|
+
* can call them through the standard streaming block format.
|
|
11929
|
+
*
|
|
11930
|
+
* Calling this multiple times accumulates servers. Tools across servers
|
|
11931
|
+
* are merged into a single registry; in plan 1, conflicting tool names
|
|
11932
|
+
* raise a registration warning. Plan 2 introduces deterministic
|
|
11933
|
+
* `<server>__<tool>` prefixing for collisions.
|
|
11934
|
+
*
|
|
11935
|
+
* STDIO commands are gated by an allowlist (see allowlist.ts) — pass
|
|
11936
|
+
* `trust: true` on the spec to opt in for non-allowlisted binaries.
|
|
11937
|
+
*
|
|
11938
|
+
* Zero-overhead invariant: if you never call this method, the MCP
|
|
11939
|
+
* runtime module is never loaded. Agents without MCP pay nothing.
|
|
11940
|
+
*
|
|
11941
|
+
* @example
|
|
11942
|
+
* ```typescript
|
|
11943
|
+
* const agent = LLMist.createAgent()
|
|
11944
|
+
* .withModel("sonnet")
|
|
11945
|
+
* .withMcpServer({
|
|
11946
|
+
* name: "filesystem",
|
|
11947
|
+
* transport: "stdio",
|
|
11948
|
+
* command: "npx",
|
|
11949
|
+
* args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"],
|
|
11950
|
+
* })
|
|
11951
|
+
* .ask("list files in /tmp");
|
|
11952
|
+
* ```
|
|
11953
|
+
*/
|
|
11954
|
+
withMcpServer(spec) {
|
|
11955
|
+
this.mcp.servers.push(spec);
|
|
11956
|
+
return this;
|
|
11957
|
+
}
|
|
11958
|
+
/** Inspect the configured MCP server specs. Useful for tests. */
|
|
11959
|
+
getMcpServerSpecs() {
|
|
11960
|
+
return this.mcp.servers;
|
|
11961
|
+
}
|
|
13280
11962
|
/** Add conversation history messages. */
|
|
13281
11963
|
withHistory(messages) {
|
|
13282
11964
|
this.core.initialMessages.push(...normalizeHistory(messages));
|
|
@@ -13538,7 +12220,7 @@ ${resolved}`);
|
|
|
13538
12220
|
}
|
|
13539
12221
|
buildAgentOptions(userPrompt) {
|
|
13540
12222
|
if (!this.core.client) {
|
|
13541
|
-
const { LLMist: LLMistClass } = (
|
|
12223
|
+
const { LLMist: LLMistClass } = (init_client2(), __toCommonJS(client_exports));
|
|
13542
12224
|
this.core.client = new LLMistClass();
|
|
13543
12225
|
}
|
|
13544
12226
|
const registry = GadgetRegistry.from(this.gadgets.gadgets);
|
|
@@ -13597,7 +12279,8 @@ ${preActivatedBlock}` : preActivatedBlock;
|
|
|
13597
12279
|
parentObservers: this.subagents.parentObservers
|
|
13598
12280
|
},
|
|
13599
12281
|
sharedRateLimitTracker: this.subagents.sharedRateLimitTracker,
|
|
13600
|
-
sharedRetryConfig: this.retry.sharedRetryConfig
|
|
12282
|
+
sharedRetryConfig: this.retry.sharedRetryConfig,
|
|
12283
|
+
mcpSpecs: this.mcp.servers.length > 0 ? [...this.mcp.servers] : void 0
|
|
13601
12284
|
};
|
|
13602
12285
|
}
|
|
13603
12286
|
/** Create agent and start with a user prompt. */
|
|
@@ -14521,7 +13204,7 @@ var init_typed_gadget = __esm({
|
|
|
14521
13204
|
|
|
14522
13205
|
// src/gadgets/executor.ts
|
|
14523
13206
|
import equal from "fast-deep-equal";
|
|
14524
|
-
import { z as
|
|
13207
|
+
import { z as z3 } from "zod";
|
|
14525
13208
|
function getHostExportsInternal() {
|
|
14526
13209
|
return {
|
|
14527
13210
|
AgentBuilder,
|
|
@@ -14529,7 +13212,7 @@ function getHostExportsInternal() {
|
|
|
14529
13212
|
createGadget,
|
|
14530
13213
|
ExecutionTree,
|
|
14531
13214
|
LLMist,
|
|
14532
|
-
z:
|
|
13215
|
+
z: z3
|
|
14533
13216
|
};
|
|
14534
13217
|
}
|
|
14535
13218
|
var GadgetExecutor;
|
|
@@ -14538,7 +13221,7 @@ var init_executor = __esm({
|
|
|
14538
13221
|
"use strict";
|
|
14539
13222
|
init_builder();
|
|
14540
13223
|
init_hook_utils();
|
|
14541
|
-
|
|
13224
|
+
init_client2();
|
|
14542
13225
|
init_constants();
|
|
14543
13226
|
init_execution_tree();
|
|
14544
13227
|
init_logger();
|
|
@@ -16669,6 +15352,10 @@ var init_agent = __esm({
|
|
|
16669
15352
|
streamProcessorFactory;
|
|
16670
15353
|
// LLM call lifecycle helper (encapsulates prepareLLMCall, completeLLMCall, notifyLLMError)
|
|
16671
15354
|
llmCallLifecycle;
|
|
15355
|
+
// MCP integration — populated only when mcpSpecs were provided.
|
|
15356
|
+
mcpSpecs;
|
|
15357
|
+
mcpLifecycle = null;
|
|
15358
|
+
mcpDiscoveredPrompts = [];
|
|
16672
15359
|
/**
|
|
16673
15360
|
* Creates a new Agent instance.
|
|
16674
15361
|
* @internal This constructor is private. Use LLMist.createAgent() or AgentBuilder instead.
|
|
@@ -16740,6 +15427,7 @@ var init_agent = __esm({
|
|
|
16740
15427
|
);
|
|
16741
15428
|
}
|
|
16742
15429
|
this.signal = options.signal;
|
|
15430
|
+
this.mcpSpecs = options.mcpSpecs ?? [];
|
|
16743
15431
|
this.reasoning = options.reasoning;
|
|
16744
15432
|
this.caching = options.caching;
|
|
16745
15433
|
this.retryConfig = options.sharedRetryConfig ?? resolveRetryConfig(options.retryConfig);
|
|
@@ -16999,6 +15687,18 @@ var init_agent = __esm({
|
|
|
16999
15687
|
);
|
|
17000
15688
|
}
|
|
17001
15689
|
const unsubscribeBridge = bridgeTreeToHooks(this.tree, this.hooks, this.logger);
|
|
15690
|
+
if (this.mcpSpecs.length > 0) {
|
|
15691
|
+
const { setupMcpServers } = await import("./runtime-GKQ6QIQP.js");
|
|
15692
|
+
this.mcpLifecycle = await setupMcpServers({
|
|
15693
|
+
specs: this.mcpSpecs,
|
|
15694
|
+
registry: this.registry,
|
|
15695
|
+
conversation: this.conversation,
|
|
15696
|
+
prefixConfig: this.prefixConfig,
|
|
15697
|
+
systemPrompt: this.conversation.getBaseMessages()[0]?.role === "system" ? this.conversation.getBaseMessages()[0].content : void 0,
|
|
15698
|
+
logger: this.logger,
|
|
15699
|
+
onPromptDiscovered: (skill) => this.mcpDiscoveredPrompts.push(skill)
|
|
15700
|
+
});
|
|
15701
|
+
}
|
|
17002
15702
|
let currentIteration = 0;
|
|
17003
15703
|
this.logger.info("Starting agent loop", {
|
|
17004
15704
|
model: this.model,
|
|
@@ -17198,6 +15898,14 @@ var init_agent = __esm({
|
|
|
17198
15898
|
}
|
|
17199
15899
|
}
|
|
17200
15900
|
unsubscribeBridge();
|
|
15901
|
+
if (this.mcpLifecycle) {
|
|
15902
|
+
try {
|
|
15903
|
+
await this.mcpLifecycle.closeAll();
|
|
15904
|
+
} catch (err) {
|
|
15905
|
+
this.logger.debug("MCP lifecycle teardown error (suppressed):", err);
|
|
15906
|
+
}
|
|
15907
|
+
this.mcpLifecycle = null;
|
|
15908
|
+
}
|
|
17201
15909
|
}
|
|
17202
15910
|
}
|
|
17203
15911
|
/**
|
|
@@ -17389,7 +16097,7 @@ init_builder();
|
|
|
17389
16097
|
init_event_handlers();
|
|
17390
16098
|
init_file_logging();
|
|
17391
16099
|
init_hook_presets();
|
|
17392
|
-
import { z as
|
|
16100
|
+
import { z as z4 } from "zod";
|
|
17393
16101
|
|
|
17394
16102
|
// src/agent/compaction/index.ts
|
|
17395
16103
|
init_config();
|
|
@@ -17502,7 +16210,7 @@ function createHints(config) {
|
|
|
17502
16210
|
init_stream_processor();
|
|
17503
16211
|
|
|
17504
16212
|
// src/index.ts
|
|
17505
|
-
|
|
16213
|
+
init_client2();
|
|
17506
16214
|
init_constants();
|
|
17507
16215
|
|
|
17508
16216
|
// src/core/errors.ts
|
|
@@ -17567,140 +16275,254 @@ init_create_gadget();
|
|
|
17567
16275
|
init_exceptions();
|
|
17568
16276
|
init_executor();
|
|
17569
16277
|
init_gadget();
|
|
16278
|
+
init_helpers();
|
|
16279
|
+
init_output_viewer();
|
|
16280
|
+
init_parser2();
|
|
16281
|
+
init_registry();
|
|
16282
|
+
init_typed_gadget();
|
|
17570
16283
|
|
|
17571
|
-
// src/
|
|
17572
|
-
|
|
17573
|
-
|
|
17574
|
-
|
|
17575
|
-
|
|
17576
|
-
|
|
17577
|
-
|
|
17578
|
-
|
|
17579
|
-
|
|
17580
|
-
|
|
17581
|
-
|
|
17582
|
-
|
|
17583
|
-
|
|
17584
|
-
|
|
17585
|
-
|
|
17586
|
-
}
|
|
17587
|
-
|
|
17588
|
-
|
|
17589
|
-
|
|
17590
|
-
|
|
17591
|
-
|
|
17592
|
-
const
|
|
16284
|
+
// src/mcp/index.ts
|
|
16285
|
+
init_allowlist();
|
|
16286
|
+
init_client();
|
|
16287
|
+
init_errors();
|
|
16288
|
+
|
|
16289
|
+
// src/mcp/gadget-exporter.ts
|
|
16290
|
+
init_schema_to_json();
|
|
16291
|
+
|
|
16292
|
+
// src/gadgets/validation.ts
|
|
16293
|
+
function validateAndApplyDefaults(schema, params) {
|
|
16294
|
+
const result = schema.safeParse(params);
|
|
16295
|
+
if (result.success) {
|
|
16296
|
+
return {
|
|
16297
|
+
success: true,
|
|
16298
|
+
data: result.data
|
|
16299
|
+
};
|
|
16300
|
+
}
|
|
16301
|
+
const issues = result.error.issues.map((issue) => ({
|
|
16302
|
+
path: issue.path.join(".") || "root",
|
|
16303
|
+
message: issue.message
|
|
16304
|
+
}));
|
|
16305
|
+
const formattedError = `Invalid parameters: ${issues.map((i) => `${i.path}: ${i.message}`).join("; ")}`;
|
|
17593
16306
|
return {
|
|
17594
|
-
|
|
17595
|
-
|
|
17596
|
-
|
|
17597
|
-
description: options?.description,
|
|
17598
|
-
metadata: options?.metadata,
|
|
17599
|
-
fileName: options?.fileName
|
|
16307
|
+
success: false,
|
|
16308
|
+
error: formattedError,
|
|
16309
|
+
issues
|
|
17600
16310
|
};
|
|
17601
16311
|
}
|
|
17602
|
-
function
|
|
17603
|
-
if (
|
|
17604
|
-
|
|
16312
|
+
function validateGadgetParams(gadget, params) {
|
|
16313
|
+
if (!gadget.parameterSchema) {
|
|
16314
|
+
return {
|
|
16315
|
+
success: true,
|
|
16316
|
+
data: params
|
|
16317
|
+
};
|
|
17605
16318
|
}
|
|
17606
|
-
return
|
|
17607
|
-
result,
|
|
17608
|
-
media,
|
|
17609
|
-
cost
|
|
17610
|
-
};
|
|
16319
|
+
return validateAndApplyDefaults(gadget.parameterSchema, params);
|
|
17611
16320
|
}
|
|
17612
|
-
|
|
17613
|
-
|
|
17614
|
-
|
|
17615
|
-
|
|
17616
|
-
|
|
17617
|
-
|
|
17618
|
-
);
|
|
16321
|
+
|
|
16322
|
+
// src/mcp/gadget-exporter.ts
|
|
16323
|
+
function gadgetToMcpTool(gadget) {
|
|
16324
|
+
const description = gadget.description && gadget.description.length > 0 ? gadget.description : `Native llmist gadget "${gadget.name ?? "unnamed"}"`;
|
|
16325
|
+
let inputSchema;
|
|
16326
|
+
if (gadget.parameterSchema) {
|
|
16327
|
+
inputSchema = schemaToJSONSchema(gadget.parameterSchema);
|
|
16328
|
+
} else {
|
|
16329
|
+
inputSchema = { type: "object", properties: {} };
|
|
17619
16330
|
}
|
|
17620
16331
|
return {
|
|
17621
|
-
|
|
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
|
|
16332
|
+
name: gadget.name ?? "unnamed-gadget",
|
|
16333
|
+
description,
|
|
16334
|
+
inputSchema
|
|
17633
16335
|
};
|
|
17634
16336
|
}
|
|
17635
|
-
function
|
|
17636
|
-
if (
|
|
17637
|
-
|
|
17638
|
-
}
|
|
17639
|
-
|
|
17640
|
-
const
|
|
17641
|
-
const
|
|
17642
|
-
if (
|
|
17643
|
-
|
|
17644
|
-
|
|
17645
|
-
);
|
|
16337
|
+
function gadgetResultToMcpContent(ret) {
|
|
16338
|
+
if (typeof ret === "string") {
|
|
16339
|
+
return [{ type: "text", text: ret }];
|
|
16340
|
+
}
|
|
16341
|
+
if (ret && typeof ret === "object" && "result" in ret) {
|
|
16342
|
+
const r = ret;
|
|
16343
|
+
const blocks = [];
|
|
16344
|
+
if (typeof r.result === "string") {
|
|
16345
|
+
blocks.push({ type: "text", text: r.result });
|
|
16346
|
+
} else {
|
|
16347
|
+
blocks.push({ type: "text", text: JSON.stringify(r.result) });
|
|
16348
|
+
}
|
|
16349
|
+
if (r.media) {
|
|
16350
|
+
for (const m of r.media) {
|
|
16351
|
+
if (m.kind === "image" || m.kind === "audio") {
|
|
16352
|
+
blocks.push({
|
|
16353
|
+
type: m.kind,
|
|
16354
|
+
data: m.data,
|
|
16355
|
+
mimeType: m.mimeType
|
|
16356
|
+
});
|
|
16357
|
+
}
|
|
16358
|
+
}
|
|
16359
|
+
}
|
|
16360
|
+
return blocks;
|
|
16361
|
+
}
|
|
16362
|
+
return [{ type: "text", text: JSON.stringify(ret) }];
|
|
16363
|
+
}
|
|
16364
|
+
async function runGadgetForMcp(gadget, rawParams) {
|
|
16365
|
+
if (gadget.parameterSchema) {
|
|
16366
|
+
const validation = validateAndApplyDefaults(
|
|
16367
|
+
gadget.parameterSchema,
|
|
16368
|
+
rawParams ?? {}
|
|
16369
|
+
);
|
|
16370
|
+
if (!validation.success) {
|
|
16371
|
+
return {
|
|
16372
|
+
isError: true,
|
|
16373
|
+
content: [
|
|
16374
|
+
{
|
|
16375
|
+
type: "text",
|
|
16376
|
+
text: `Invalid arguments for gadget "${gadget.name}": ${validation.error}`
|
|
16377
|
+
}
|
|
16378
|
+
]
|
|
16379
|
+
};
|
|
17646
16380
|
}
|
|
16381
|
+
rawParams = validation.data;
|
|
16382
|
+
}
|
|
16383
|
+
try {
|
|
16384
|
+
const result = await gadget.execute(rawParams);
|
|
17647
16385
|
return {
|
|
17648
|
-
|
|
17649
|
-
data: buffer.toString("base64"),
|
|
17650
|
-
mimeType,
|
|
17651
|
-
description: img.description,
|
|
17652
|
-
metadata: img.metadata,
|
|
17653
|
-
fileName: img.fileName
|
|
16386
|
+
content: gadgetResultToMcpContent(result)
|
|
17654
16387
|
};
|
|
17655
|
-
})
|
|
17656
|
-
|
|
16388
|
+
} catch (err) {
|
|
16389
|
+
return {
|
|
16390
|
+
isError: true,
|
|
16391
|
+
content: [
|
|
16392
|
+
{
|
|
16393
|
+
type: "text",
|
|
16394
|
+
text: `Gadget "${gadget.name}" failed: ${err.message}`
|
|
16395
|
+
}
|
|
16396
|
+
]
|
|
16397
|
+
};
|
|
16398
|
+
}
|
|
17657
16399
|
}
|
|
17658
|
-
|
|
17659
|
-
|
|
17660
|
-
|
|
17661
|
-
|
|
17662
|
-
|
|
17663
|
-
|
|
17664
|
-
|
|
16400
|
+
|
|
16401
|
+
// src/mcp/index.ts
|
|
16402
|
+
init_json_schema_to_zod();
|
|
16403
|
+
init_lifecycle();
|
|
16404
|
+
|
|
16405
|
+
// src/mcp/skill-exporter.ts
|
|
16406
|
+
function skillToMcpPrompt(skill) {
|
|
16407
|
+
const description = skill.description && skill.description.length > 0 ? skill.description : `Native llmist skill "${skill.name}"`;
|
|
16408
|
+
const args = [];
|
|
16409
|
+
if (skill.metadata.argumentHint) {
|
|
16410
|
+
args.push({
|
|
16411
|
+
name: "arguments",
|
|
16412
|
+
description: skill.metadata.argumentHint,
|
|
16413
|
+
required: false
|
|
16414
|
+
});
|
|
17665
16415
|
}
|
|
17666
|
-
const metadata = options?.durationMs ? { durationMs: options.durationMs } : void 0;
|
|
17667
16416
|
return {
|
|
17668
|
-
|
|
17669
|
-
|
|
16417
|
+
name: skill.name,
|
|
16418
|
+
description,
|
|
16419
|
+
...args.length > 0 ? { arguments: args } : {}
|
|
16420
|
+
};
|
|
16421
|
+
}
|
|
16422
|
+
async function renderSkillForMcpPrompt(skill, args) {
|
|
16423
|
+
const argString = typeof args.arguments === "string" ? args.arguments : Object.values(args).filter((v) => typeof v === "string").join(" ");
|
|
16424
|
+
const activation = await skill.activate({
|
|
16425
|
+
arguments: argString || void 0
|
|
16426
|
+
});
|
|
16427
|
+
return {
|
|
16428
|
+
description: skill.description,
|
|
16429
|
+
messages: [
|
|
17670
16430
|
{
|
|
17671
|
-
|
|
17672
|
-
|
|
17673
|
-
mimeType,
|
|
17674
|
-
description: options?.description,
|
|
17675
|
-
metadata,
|
|
17676
|
-
fileName: options?.fileName
|
|
16431
|
+
role: "user",
|
|
16432
|
+
content: { type: "text", text: activation.resolvedInstructions }
|
|
17677
16433
|
}
|
|
17678
|
-
]
|
|
17679
|
-
cost: options?.cost
|
|
16434
|
+
]
|
|
17680
16435
|
};
|
|
17681
16436
|
}
|
|
17682
|
-
|
|
17683
|
-
|
|
16437
|
+
|
|
16438
|
+
// src/mcp/server.ts
|
|
16439
|
+
var DEFAULT_SERVER_INFO = { name: "llmist", version: "0.0.0" };
|
|
16440
|
+
function createMcpServer(opts) {
|
|
16441
|
+
const { gadgets, skills } = opts;
|
|
16442
|
+
const hasTools = gadgets.getAll().length > 0;
|
|
16443
|
+
const hasPrompts = !!skills && skills.size > 0;
|
|
16444
|
+
const capabilities = {};
|
|
16445
|
+
if (hasTools) capabilities.tools = {};
|
|
16446
|
+
if (hasPrompts) capabilities.prompts = {};
|
|
16447
|
+
let sdkServer = null;
|
|
16448
|
+
let running = false;
|
|
16449
|
+
async function ensureServer() {
|
|
16450
|
+
if (sdkServer) return sdkServer;
|
|
16451
|
+
const [serverMod, typesMod] = await Promise.all([
|
|
16452
|
+
import("@modelcontextprotocol/sdk/server/index.js"),
|
|
16453
|
+
import("@modelcontextprotocol/sdk/types.js")
|
|
16454
|
+
]);
|
|
16455
|
+
const ServerClass = serverMod.Server;
|
|
16456
|
+
const server = new ServerClass(opts.serverInfo ?? DEFAULT_SERVER_INFO, {
|
|
16457
|
+
capabilities
|
|
16458
|
+
});
|
|
16459
|
+
if (hasTools) {
|
|
16460
|
+
server.setRequestHandler(typesMod.ListToolsRequestSchema, async () => ({
|
|
16461
|
+
tools: gadgets.getAll().map(gadgetToMcpTool)
|
|
16462
|
+
}));
|
|
16463
|
+
server.setRequestHandler(
|
|
16464
|
+
typesMod.CallToolRequestSchema,
|
|
16465
|
+
async (req) => {
|
|
16466
|
+
const gadget = gadgets.get(req.params.name);
|
|
16467
|
+
if (!gadget) {
|
|
16468
|
+
return {
|
|
16469
|
+
isError: true,
|
|
16470
|
+
content: [
|
|
16471
|
+
{
|
|
16472
|
+
type: "text",
|
|
16473
|
+
text: `Unknown tool "${req.params.name}". Call tools/list first.`
|
|
16474
|
+
}
|
|
16475
|
+
]
|
|
16476
|
+
};
|
|
16477
|
+
}
|
|
16478
|
+
return runGadgetForMcp(gadget, req.params.arguments ?? {});
|
|
16479
|
+
}
|
|
16480
|
+
);
|
|
16481
|
+
}
|
|
16482
|
+
if (hasPrompts && skills) {
|
|
16483
|
+
server.setRequestHandler(typesMod.ListPromptsRequestSchema, async () => ({
|
|
16484
|
+
prompts: Array.from(skills.getAll()).map(skillToMcpPrompt)
|
|
16485
|
+
}));
|
|
16486
|
+
server.setRequestHandler(
|
|
16487
|
+
typesMod.GetPromptRequestSchema,
|
|
16488
|
+
async (req) => {
|
|
16489
|
+
const skill = skills.get(req.params.name);
|
|
16490
|
+
if (!skill) {
|
|
16491
|
+
throw new Error(`Unknown prompt "${req.params.name}"`);
|
|
16492
|
+
}
|
|
16493
|
+
const result = await renderSkillForMcpPrompt(skill, req.params.arguments ?? {});
|
|
16494
|
+
return result;
|
|
16495
|
+
}
|
|
16496
|
+
);
|
|
16497
|
+
}
|
|
16498
|
+
sdkServer = server;
|
|
16499
|
+
return server;
|
|
16500
|
+
}
|
|
17684
16501
|
return {
|
|
17685
|
-
|
|
17686
|
-
|
|
17687
|
-
|
|
17688
|
-
|
|
17689
|
-
|
|
17690
|
-
|
|
17691
|
-
|
|
17692
|
-
|
|
16502
|
+
get running() {
|
|
16503
|
+
return running;
|
|
16504
|
+
},
|
|
16505
|
+
async connect(transport) {
|
|
16506
|
+
const server = await ensureServer();
|
|
16507
|
+
await server.connect(transport);
|
|
16508
|
+
running = true;
|
|
16509
|
+
},
|
|
16510
|
+
async stop() {
|
|
16511
|
+
if (!sdkServer) return;
|
|
16512
|
+
try {
|
|
16513
|
+
await sdkServer.close();
|
|
16514
|
+
} catch {
|
|
17693
16515
|
}
|
|
17694
|
-
|
|
17695
|
-
|
|
16516
|
+
sdkServer = null;
|
|
16517
|
+
running = false;
|
|
16518
|
+
}
|
|
17696
16519
|
};
|
|
17697
16520
|
}
|
|
17698
16521
|
|
|
16522
|
+
// src/mcp/index.ts
|
|
16523
|
+
init_tool_adapter();
|
|
16524
|
+
|
|
17699
16525
|
// src/index.ts
|
|
17700
|
-
init_output_viewer();
|
|
17701
|
-
init_parser2();
|
|
17702
|
-
init_registry();
|
|
17703
|
-
init_typed_gadget();
|
|
17704
16526
|
init_constants2();
|
|
17705
16527
|
|
|
17706
16528
|
// src/utils/config-resolver.ts
|
|
@@ -17809,38 +16631,6 @@ function hasHostExports(ctx) {
|
|
|
17809
16631
|
init_media_store();
|
|
17810
16632
|
init_schema_to_json();
|
|
17811
16633
|
init_schema_validator();
|
|
17812
|
-
|
|
17813
|
-
// src/gadgets/validation.ts
|
|
17814
|
-
function validateAndApplyDefaults(schema, params) {
|
|
17815
|
-
const result = schema.safeParse(params);
|
|
17816
|
-
if (result.success) {
|
|
17817
|
-
return {
|
|
17818
|
-
success: true,
|
|
17819
|
-
data: result.data
|
|
17820
|
-
};
|
|
17821
|
-
}
|
|
17822
|
-
const issues = result.error.issues.map((issue) => ({
|
|
17823
|
-
path: issue.path.join(".") || "root",
|
|
17824
|
-
message: issue.message
|
|
17825
|
-
}));
|
|
17826
|
-
const formattedError = `Invalid parameters: ${issues.map((i) => `${i.path}: ${i.message}`).join("; ")}`;
|
|
17827
|
-
return {
|
|
17828
|
-
success: false,
|
|
17829
|
-
error: formattedError,
|
|
17830
|
-
issues
|
|
17831
|
-
};
|
|
17832
|
-
}
|
|
17833
|
-
function validateGadgetParams(gadget, params) {
|
|
17834
|
-
if (!gadget.parameterSchema) {
|
|
17835
|
-
return {
|
|
17836
|
-
success: true,
|
|
17837
|
-
data: params
|
|
17838
|
-
};
|
|
17839
|
-
}
|
|
17840
|
-
return validateAndApplyDefaults(gadget.parameterSchema, params);
|
|
17841
|
-
}
|
|
17842
|
-
|
|
17843
|
-
// src/index.ts
|
|
17844
16634
|
init_logger();
|
|
17845
16635
|
|
|
17846
16636
|
// src/package/manifest.ts
|
|
@@ -18138,6 +16928,7 @@ export {
|
|
|
18138
16928
|
ConversationManager,
|
|
18139
16929
|
DEFAULT_COMPACTION_CONFIG,
|
|
18140
16930
|
DEFAULT_HINTS,
|
|
16931
|
+
DEFAULT_MCP_COMMAND_ALLOWLIST,
|
|
18141
16932
|
DEFAULT_PROMPTS,
|
|
18142
16933
|
DEFAULT_RATE_LIMIT_CONFIG,
|
|
18143
16934
|
DEFAULT_RETRY_CONFIG,
|
|
@@ -18157,10 +16948,17 @@ export {
|
|
|
18157
16948
|
HuggingFaceProvider,
|
|
18158
16949
|
HumanInputRequiredException,
|
|
18159
16950
|
HybridStrategy,
|
|
16951
|
+
JsonSchemaConversionError,
|
|
18160
16952
|
LLMMessageBuilder,
|
|
18161
16953
|
LLMist,
|
|
18162
16954
|
LOAD_SKILL_GADGET_NAME,
|
|
18163
16955
|
MODEL_ALIASES,
|
|
16956
|
+
McpClient,
|
|
16957
|
+
McpConnectError,
|
|
16958
|
+
McpError,
|
|
16959
|
+
McpLifecycle,
|
|
16960
|
+
McpToolCallError,
|
|
16961
|
+
McpUntrustedCommandError,
|
|
18164
16962
|
MediaStore,
|
|
18165
16963
|
ModelIdentifierParser,
|
|
18166
16964
|
ModelRegistry,
|
|
@@ -18176,6 +16974,7 @@ export {
|
|
|
18176
16974
|
SummarizationStrategy,
|
|
18177
16975
|
TaskCompletionSignal,
|
|
18178
16976
|
TimeoutException,
|
|
16977
|
+
assertCommandAllowed,
|
|
18179
16978
|
audioFromBase64,
|
|
18180
16979
|
audioFromBuffer,
|
|
18181
16980
|
collectEvents,
|
|
@@ -18190,6 +16989,7 @@ export {
|
|
|
18190
16989
|
createHuggingFaceProviderFromEnv,
|
|
18191
16990
|
createLoadSkillGadget,
|
|
18192
16991
|
createLogger,
|
|
16992
|
+
createMcpServer,
|
|
18193
16993
|
createMediaOutput,
|
|
18194
16994
|
createOpenAIProviderFromEnv,
|
|
18195
16995
|
createOpenRouterProviderFromEnv,
|
|
@@ -18212,7 +17012,9 @@ export {
|
|
|
18212
17012
|
formatLLMError,
|
|
18213
17013
|
formatLlmRequest,
|
|
18214
17014
|
gadgetError,
|
|
17015
|
+
gadgetResultToMcpContent,
|
|
18215
17016
|
gadgetSuccess,
|
|
17017
|
+
gadgetToMcpTool,
|
|
18216
17018
|
getErrorMessage,
|
|
18217
17019
|
getHostExports2 as getHostExports,
|
|
18218
17020
|
getModelId,
|
|
@@ -18240,9 +17042,11 @@ export {
|
|
|
18240
17042
|
isSubagentEvent,
|
|
18241
17043
|
isTextPart,
|
|
18242
17044
|
iterationProgressHint,
|
|
17045
|
+
jsonSchemaToZod,
|
|
18243
17046
|
listPresets,
|
|
18244
17047
|
listSubagents,
|
|
18245
17048
|
loadSkillsFromDirectory,
|
|
17049
|
+
mcpToolToGadget,
|
|
18246
17050
|
normalizeMessageContent,
|
|
18247
17051
|
parallelGadgetHint,
|
|
18248
17052
|
parseDataUrl,
|
|
@@ -18253,6 +17057,7 @@ export {
|
|
|
18253
17057
|
parseSkillContent,
|
|
18254
17058
|
parseSkillFile,
|
|
18255
17059
|
randomDelay,
|
|
17060
|
+
renderSkillForMcpPrompt,
|
|
18256
17061
|
resetFileLoggingState,
|
|
18257
17062
|
resolveConfig,
|
|
18258
17063
|
resolveHintTemplate,
|
|
@@ -18270,9 +17075,11 @@ export {
|
|
|
18270
17075
|
resultWithImage,
|
|
18271
17076
|
resultWithImages,
|
|
18272
17077
|
resultWithMedia,
|
|
17078
|
+
runGadgetForMcp,
|
|
18273
17079
|
runWithHandlers,
|
|
18274
17080
|
scanResources,
|
|
18275
17081
|
schemaToJSONSchema,
|
|
17082
|
+
skillToMcpPrompt,
|
|
18276
17083
|
stream,
|
|
18277
17084
|
stripProviderPrefix,
|
|
18278
17085
|
substituteArguments,
|
|
@@ -18288,6 +17095,6 @@ export {
|
|
|
18288
17095
|
withErrorHandling,
|
|
18289
17096
|
withRetry,
|
|
18290
17097
|
withTimeout,
|
|
18291
|
-
|
|
17098
|
+
z4 as z
|
|
18292
17099
|
};
|
|
18293
17100
|
//# sourceMappingURL=index.js.map
|