llmist 0.5.1 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -345,12 +345,16 @@ var init_registry = __esm({
345
345
  });
346
346
 
347
347
  // src/core/constants.ts
348
- var GADGET_START_PREFIX, GADGET_END_PREFIX;
348
+ var GADGET_START_PREFIX, GADGET_END_PREFIX, DEFAULT_GADGET_OUTPUT_LIMIT, DEFAULT_GADGET_OUTPUT_LIMIT_PERCENT, CHARS_PER_TOKEN, FALLBACK_CONTEXT_WINDOW;
349
349
  var init_constants = __esm({
350
350
  "src/core/constants.ts"() {
351
351
  "use strict";
352
352
  GADGET_START_PREFIX = "!!!GADGET_START:";
353
353
  GADGET_END_PREFIX = "!!!GADGET_END";
354
+ DEFAULT_GADGET_OUTPUT_LIMIT = true;
355
+ DEFAULT_GADGET_OUTPUT_LIMIT_PERCENT = 15;
356
+ CHARS_PER_TOKEN = 4;
357
+ FALLBACK_CONTEXT_WINDOW = 128e3;
354
358
  }
355
359
  });
356
360
 
@@ -384,7 +388,7 @@ var init_prompt_config = __esm({
384
388
  criticalUsage: "INVOKE gadgets using the markers - do not describe what you want to do.",
385
389
  formatDescriptionYaml: "Parameters in YAML format (one per line)",
386
390
  formatDescriptionJson: "Parameters in JSON format (valid JSON object)",
387
- formatDescriptionToml: "Parameters in TOML format (key = value pairs, use triple-quotes for multiline)",
391
+ formatDescriptionToml: "Parameters in TOML format (key = value pairs, use heredoc for multiline: key = <<<EOF ... EOF)",
388
392
  rules: () => [
389
393
  "Output ONLY plain text with the exact markers - never use function/tool calling",
390
394
  "You can invoke multiple gadgets in a single response",
@@ -565,10 +569,11 @@ ${this.endPrefix}
565
569
  ${this.startPrefix}analyze
566
570
  type: economic_analysis
567
571
  matter: "Polish Economy"
568
- question: |
569
- Analyze the following:
570
- - Polish arms exports 2025
571
- - Economic implications
572
+ question: <<<EOF
573
+ Analyze the following:
574
+ - Polish arms exports 2025
575
+ - Economic implications
576
+ EOF
572
577
  ${this.endPrefix}`,
573
578
  json: `${this.startPrefix}translate
574
579
  {"from": "English", "to": "Polish", "content": "Paris is the capital of France: a beautiful city."}
@@ -584,11 +589,11 @@ ${this.endPrefix}
584
589
  ${this.startPrefix}analyze
585
590
  type = "economic_analysis"
586
591
  matter = "Polish Economy"
587
- question = """
592
+ question = <<<EOF
588
593
  Analyze the following:
589
594
  - Polish arms exports 2025
590
595
  - Economic implications
591
- """
596
+ EOF
592
597
  ${this.endPrefix}`,
593
598
  auto: `${this.startPrefix}translate
594
599
  {"from": "English", "to": "Polish", "content": "Paris is the capital of France: a beautiful city."}
@@ -605,37 +610,38 @@ ${multipleExamples[parameterFormat]}`);
605
610
  if (parameterFormat === "yaml") {
606
611
  parts.push(`
607
612
 
608
- YAML MULTILINE SYNTAX:
609
- For string values with special characters (colons, dashes, quotes) or multiple lines,
610
- use the pipe (|) syntax. ALL content lines MUST be indented with 2 spaces:
613
+ YAML HEREDOC SYNTAX:
614
+ For string values with multiple lines, use heredoc syntax (<<<DELIMITER...DELIMITER):
615
+
616
+ filePath: "README.md"
617
+ content: <<<EOF
618
+ # Project Title
611
619
 
612
- CORRECT - all lines indented:
613
- question: |
614
- Which option do you prefer?
615
- - Option A: fast processing
616
- - Option B: thorough analysis
617
- Please choose one.
620
+ This content can contain:
621
+ - Markdown lists
622
+ - Special characters: # : -
623
+ - Multiple paragraphs
624
+ EOF
618
625
 
619
- WRONG - inconsistent indentation breaks YAML:
620
- question: |
621
- Which option do you prefer?
622
- - Option A: fast
623
- Please choose one. <-- ERROR: not indented, breaks out of the block`);
626
+ The delimiter (EOF) can be any identifier. The closing delimiter must be on its own line.
627
+ No indentation is required for the content.`);
624
628
  } else if (parameterFormat === "toml") {
625
629
  parts.push(`
626
630
 
627
- TOML MULTILINE SYNTAX:
628
- For string values with multiple lines or special characters, use triple-quotes ("""):
631
+ TOML HEREDOC SYNTAX:
632
+ For string values with multiple lines, use heredoc syntax (<<<DELIMITER...DELIMITER):
629
633
 
630
634
  filePath = "README.md"
631
- content = """
635
+ content = <<<EOF
632
636
  # Project Title
633
637
 
634
638
  This content can contain:
635
639
  - Markdown lists
636
640
  - Special characters: # : -
637
641
  - Multiple paragraphs
638
- """`);
642
+ EOF
643
+
644
+ The delimiter (EOF) can be any identifier. The closing delimiter must be on its own line.`);
639
645
  }
640
646
  return parts.join("");
641
647
  }
@@ -714,18 +720,28 @@ function parseLogLevel(value) {
714
720
  }
715
721
  return LEVEL_NAME_TO_ID[normalized];
716
722
  }
723
+ function parseEnvBoolean(value) {
724
+ if (!value) return void 0;
725
+ const normalized = value.trim().toLowerCase();
726
+ if (normalized === "true" || normalized === "1") return true;
727
+ if (normalized === "false" || normalized === "0") return false;
728
+ return void 0;
729
+ }
717
730
  function createLogger(options = {}) {
718
731
  const envMinLevel = parseLogLevel(process.env.LLMIST_LOG_LEVEL);
719
732
  const envLogFile = process.env.LLMIST_LOG_FILE?.trim() ?? "";
733
+ const envLogReset = parseEnvBoolean(process.env.LLMIST_LOG_RESET);
720
734
  const minLevel = options.minLevel ?? envMinLevel ?? 4;
721
735
  const defaultType = options.type ?? "pretty";
722
736
  const name = options.name ?? "llmist";
737
+ const logReset = options.logReset ?? envLogReset ?? false;
723
738
  let logFileStream;
724
739
  let finalType = defaultType;
725
740
  if (envLogFile) {
726
741
  try {
727
742
  (0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(envLogFile), { recursive: true });
728
- logFileStream = (0, import_node_fs.createWriteStream)(envLogFile, { flags: "a" });
743
+ const flags = logReset ? "w" : "a";
744
+ logFileStream = (0, import_node_fs.createWriteStream)(envLogFile, { flags });
729
745
  finalType = "hidden";
730
746
  } catch (error) {
731
747
  console.error("Failed to initialize LLMIST_LOG_FILE output:", error);
@@ -768,6 +784,532 @@ var init_logger = __esm({
768
784
  }
769
785
  });
770
786
 
787
+ // src/gadgets/schema-to-json.ts
788
+ function schemaToJSONSchema(schema, options) {
789
+ const jsonSchema = z2.toJSONSchema(schema, options ?? { target: "draft-7" });
790
+ const mismatches = detectDescriptionMismatch(schema, jsonSchema);
791
+ if (mismatches.length > 0) {
792
+ defaultLogger.warn(
793
+ `Zod instance mismatch detected: ${mismatches.length} description(s) lost. For best results, use: import { z } from "llmist"`
794
+ );
795
+ return mergeDescriptions(schema, jsonSchema);
796
+ }
797
+ return jsonSchema;
798
+ }
799
+ function detectDescriptionMismatch(schema, jsonSchema) {
800
+ const mismatches = [];
801
+ function checkSchema(zodSchema, json, path) {
802
+ if (!zodSchema || typeof zodSchema !== "object") return;
803
+ const def = zodSchema._def;
804
+ const jsonObj = json;
805
+ if (def?.description && !jsonObj?.description) {
806
+ mismatches.push(path || "root");
807
+ }
808
+ if (def?.typeName === "ZodObject" && def?.shape) {
809
+ const shape = typeof def.shape === "function" ? def.shape() : def.shape;
810
+ for (const [key, fieldSchema] of Object.entries(shape)) {
811
+ const properties = jsonObj?.properties;
812
+ const jsonProp = properties?.[key];
813
+ checkSchema(fieldSchema, jsonProp, path ? `${path}.${key}` : key);
814
+ }
815
+ }
816
+ if (def?.typeName === "ZodArray" && def?.type) {
817
+ checkSchema(def.type, jsonObj?.items, path ? `${path}[]` : "[]");
818
+ }
819
+ if ((def?.typeName === "ZodOptional" || def?.typeName === "ZodNullable") && def?.innerType) {
820
+ checkSchema(def.innerType, json, path);
821
+ }
822
+ if (def?.typeName === "ZodDefault" && def?.innerType) {
823
+ checkSchema(def.innerType, json, path);
824
+ }
825
+ }
826
+ checkSchema(schema, jsonSchema, "");
827
+ return mismatches;
828
+ }
829
+ function mergeDescriptions(schema, jsonSchema) {
830
+ function merge(zodSchema, json) {
831
+ if (!json || typeof json !== "object") return json;
832
+ const def = zodSchema._def;
833
+ const jsonObj = json;
834
+ const merged = { ...jsonObj };
835
+ if (def?.description && !jsonObj.description) {
836
+ merged.description = def.description;
837
+ }
838
+ if (def?.typeName === "ZodObject" && def?.shape && jsonObj.properties) {
839
+ const shape = typeof def.shape === "function" ? def.shape() : def.shape;
840
+ const properties = jsonObj.properties;
841
+ merged.properties = { ...properties };
842
+ for (const [key, fieldSchema] of Object.entries(shape)) {
843
+ if (properties[key]) {
844
+ merged.properties[key] = merge(fieldSchema, properties[key]);
845
+ }
846
+ }
847
+ }
848
+ if (def?.typeName === "ZodArray" && def?.type && jsonObj.items) {
849
+ merged.items = merge(def.type, jsonObj.items);
850
+ }
851
+ if ((def?.typeName === "ZodOptional" || def?.typeName === "ZodNullable") && def?.innerType) {
852
+ return merge(def.innerType, json);
853
+ }
854
+ if (def?.typeName === "ZodDefault" && def?.innerType) {
855
+ return merge(def.innerType, json);
856
+ }
857
+ return merged;
858
+ }
859
+ return merge(schema, jsonSchema);
860
+ }
861
+ var z2;
862
+ var init_schema_to_json = __esm({
863
+ "src/gadgets/schema-to-json.ts"() {
864
+ "use strict";
865
+ z2 = __toESM(require("zod"), 1);
866
+ init_logger();
867
+ }
868
+ });
869
+
870
+ // src/gadgets/gadget.ts
871
+ function findSafeDelimiter(content) {
872
+ const lines = content.split("\n");
873
+ for (const delimiter of HEREDOC_DELIMITERS) {
874
+ const regex = new RegExp(`^${delimiter}\\s*$`);
875
+ const isUsed = lines.some((line) => regex.test(line));
876
+ if (!isUsed) {
877
+ return delimiter;
878
+ }
879
+ }
880
+ let counter = 1;
881
+ while (counter < 1e3) {
882
+ const delimiter = `HEREDOC_${counter}`;
883
+ const regex = new RegExp(`^${delimiter}\\s*$`);
884
+ const isUsed = lines.some((line) => regex.test(line));
885
+ if (!isUsed) {
886
+ return delimiter;
887
+ }
888
+ counter++;
889
+ }
890
+ return "HEREDOC_FALLBACK";
891
+ }
892
+ function formatYamlValue(value, indent = "") {
893
+ if (typeof value === "string") {
894
+ const lines = value.split("\n");
895
+ if (lines.length === 1 && !value.includes(":") && !value.startsWith("-")) {
896
+ return value;
897
+ }
898
+ const delimiter = findSafeDelimiter(value);
899
+ return `<<<${delimiter}
900
+ ${value}
901
+ ${delimiter}`;
902
+ }
903
+ if (typeof value === "number" || typeof value === "boolean") {
904
+ return String(value);
905
+ }
906
+ if (value === null || value === void 0) {
907
+ return "null";
908
+ }
909
+ if (Array.isArray(value)) {
910
+ if (value.length === 0) return "[]";
911
+ const items = value.map((item) => `${indent}- ${formatYamlValue(item, indent + " ")}`);
912
+ return "\n" + items.join("\n");
913
+ }
914
+ if (typeof value === "object") {
915
+ const entries = Object.entries(value);
916
+ if (entries.length === 0) return "{}";
917
+ const lines = entries.map(([k, v]) => {
918
+ const formattedValue = formatYamlValue(v, indent + " ");
919
+ if (formattedValue.startsWith("\n") || formattedValue.startsWith("|")) {
920
+ return `${indent}${k}: ${formattedValue}`;
921
+ }
922
+ return `${indent}${k}: ${formattedValue}`;
923
+ });
924
+ return "\n" + lines.join("\n");
925
+ }
926
+ return yaml.dump(value).trimEnd();
927
+ }
928
+ function formatParamsAsYaml(params) {
929
+ const lines = [];
930
+ for (const [key, value] of Object.entries(params)) {
931
+ const formattedValue = formatYamlValue(value, "");
932
+ if (formattedValue.startsWith("\n")) {
933
+ lines.push(`${key}:${formattedValue}`);
934
+ } else {
935
+ lines.push(`${key}: ${formattedValue}`);
936
+ }
937
+ }
938
+ return lines.join("\n");
939
+ }
940
+ function formatTomlValue(value) {
941
+ if (typeof value === "string") {
942
+ if (value.includes("\n")) {
943
+ const delimiter = findSafeDelimiter(value);
944
+ return `<<<${delimiter}
945
+ ${value}
946
+ ${delimiter}`;
947
+ }
948
+ return JSON.stringify(value);
949
+ }
950
+ if (typeof value === "number" || typeof value === "boolean") {
951
+ return String(value);
952
+ }
953
+ if (value === null || value === void 0) {
954
+ return '""';
955
+ }
956
+ if (Array.isArray(value)) {
957
+ return JSON.stringify(value);
958
+ }
959
+ if (typeof value === "object") {
960
+ return JSON.stringify(value);
961
+ }
962
+ return JSON.stringify(value);
963
+ }
964
+ function formatParamsAsToml(params) {
965
+ const lines = [];
966
+ for (const [key, value] of Object.entries(params)) {
967
+ lines.push(`${key} = ${formatTomlValue(value)}`);
968
+ }
969
+ return lines.join("\n");
970
+ }
971
+ var yaml, HEREDOC_DELIMITERS, BaseGadget;
972
+ var init_gadget = __esm({
973
+ "src/gadgets/gadget.ts"() {
974
+ "use strict";
975
+ yaml = __toESM(require("js-yaml"), 1);
976
+ init_schema_to_json();
977
+ init_schema_validator();
978
+ HEREDOC_DELIMITERS = ["EOF", "END", "DOC", "CONTENT", "TEXT", "HEREDOC", "DATA", "BLOCK"];
979
+ BaseGadget = class {
980
+ /**
981
+ * The name of the gadget. Used for identification when LLM calls it.
982
+ * If not provided, defaults to the class name.
983
+ */
984
+ name;
985
+ /**
986
+ * Optional Zod schema describing the expected input payload. When provided,
987
+ * it will be validated before execution and transformed into a JSON Schema
988
+ * representation that is surfaced to the LLM as part of the instructions.
989
+ */
990
+ parameterSchema;
991
+ /**
992
+ * Optional timeout in milliseconds for gadget execution.
993
+ * If execution exceeds this timeout, a TimeoutException will be thrown.
994
+ * If not set, the global defaultGadgetTimeoutMs from runtime options will be used.
995
+ * Set to 0 or undefined to disable timeout for this gadget.
996
+ */
997
+ timeoutMs;
998
+ /**
999
+ * Optional usage examples to help LLMs understand proper invocation.
1000
+ * Examples are rendered in getInstruction() alongside the schema.
1001
+ *
1002
+ * Note: Uses broader `unknown` type to allow typed examples from subclasses
1003
+ * while maintaining runtime compatibility.
1004
+ */
1005
+ examples;
1006
+ /**
1007
+ * Auto-generated instruction text for the LLM.
1008
+ * Combines name, description, and parameter schema into a formatted instruction.
1009
+ * @deprecated Use getInstruction(format) instead for format-specific schemas
1010
+ */
1011
+ get instruction() {
1012
+ return this.getInstruction("yaml");
1013
+ }
1014
+ /**
1015
+ * Generate instruction text for the LLM with format-specific schema.
1016
+ * Combines name, description, and parameter schema into a formatted instruction.
1017
+ *
1018
+ * @param format - Format for the schema representation ('json' | 'yaml' | 'toml' | 'auto')
1019
+ * @returns Formatted instruction string
1020
+ */
1021
+ getInstruction(format = "json") {
1022
+ const parts = [];
1023
+ parts.push(this.description);
1024
+ if (this.parameterSchema) {
1025
+ const gadgetName = this.name ?? this.constructor.name;
1026
+ validateGadgetSchema(this.parameterSchema, gadgetName);
1027
+ const jsonSchema = schemaToJSONSchema(this.parameterSchema, {
1028
+ target: "draft-7"
1029
+ });
1030
+ if (format === "json" || format === "auto") {
1031
+ parts.push("\n\nInput Schema (JSON):");
1032
+ parts.push(JSON.stringify(jsonSchema, null, 2));
1033
+ } else if (format === "toml") {
1034
+ parts.push("\n\nInput Schema (TOML):");
1035
+ parts.push(JSON.stringify(jsonSchema, null, 2));
1036
+ } else {
1037
+ const yamlSchema = yaml.dump(jsonSchema).trimEnd();
1038
+ parts.push("\n\nInput Schema (YAML):");
1039
+ parts.push(yamlSchema);
1040
+ }
1041
+ }
1042
+ if (this.examples && this.examples.length > 0) {
1043
+ parts.push("\n\nExamples:");
1044
+ this.examples.forEach((example, index) => {
1045
+ if (index > 0) {
1046
+ parts.push("");
1047
+ }
1048
+ if (example.comment) {
1049
+ parts.push(`# ${example.comment}`);
1050
+ }
1051
+ parts.push("Input:");
1052
+ if (format === "json" || format === "auto") {
1053
+ parts.push(JSON.stringify(example.params, null, 2));
1054
+ } else if (format === "toml") {
1055
+ parts.push(formatParamsAsToml(example.params));
1056
+ } else {
1057
+ parts.push(formatParamsAsYaml(example.params));
1058
+ }
1059
+ if (example.output !== void 0) {
1060
+ parts.push("Output:");
1061
+ parts.push(example.output);
1062
+ }
1063
+ });
1064
+ }
1065
+ return parts.join("\n");
1066
+ }
1067
+ };
1068
+ }
1069
+ });
1070
+
1071
+ // src/gadgets/create-gadget.ts
1072
+ function createGadget(config) {
1073
+ class DynamicGadget extends BaseGadget {
1074
+ name = config.name;
1075
+ description = config.description;
1076
+ parameterSchema = config.schema;
1077
+ timeoutMs = config.timeoutMs;
1078
+ examples = config.examples;
1079
+ execute(params) {
1080
+ return config.execute(params);
1081
+ }
1082
+ }
1083
+ return new DynamicGadget();
1084
+ }
1085
+ var init_create_gadget = __esm({
1086
+ "src/gadgets/create-gadget.ts"() {
1087
+ "use strict";
1088
+ init_gadget();
1089
+ }
1090
+ });
1091
+
1092
+ // src/gadgets/output-viewer.ts
1093
+ function applyPattern(lines, pattern) {
1094
+ const regex = new RegExp(pattern.regex);
1095
+ if (!pattern.include) {
1096
+ return lines.filter((line) => !regex.test(line));
1097
+ }
1098
+ const matchIndices = /* @__PURE__ */ new Set();
1099
+ for (let i = 0; i < lines.length; i++) {
1100
+ if (regex.test(lines[i])) {
1101
+ const start = Math.max(0, i - pattern.before);
1102
+ const end = Math.min(lines.length - 1, i + pattern.after);
1103
+ for (let j = start; j <= end; j++) {
1104
+ matchIndices.add(j);
1105
+ }
1106
+ }
1107
+ }
1108
+ return lines.filter((_, index) => matchIndices.has(index));
1109
+ }
1110
+ function applyPatterns(lines, patterns) {
1111
+ let result = lines;
1112
+ for (const pattern of patterns) {
1113
+ result = applyPattern(result, pattern);
1114
+ }
1115
+ return result;
1116
+ }
1117
+ function applyLineLimit(lines, limit) {
1118
+ const trimmed = limit.trim();
1119
+ if (trimmed.endsWith("-") && !trimmed.startsWith("-")) {
1120
+ const n = parseInt(trimmed.slice(0, -1), 10);
1121
+ if (!isNaN(n) && n > 0) {
1122
+ return lines.slice(0, n);
1123
+ }
1124
+ }
1125
+ if (trimmed.startsWith("-") && !trimmed.includes("-", 1)) {
1126
+ const n = parseInt(trimmed, 10);
1127
+ if (!isNaN(n) && n < 0) {
1128
+ return lines.slice(n);
1129
+ }
1130
+ }
1131
+ const rangeMatch = trimmed.match(/^(\d+)-(\d+)$/);
1132
+ if (rangeMatch) {
1133
+ const start = parseInt(rangeMatch[1], 10);
1134
+ const end = parseInt(rangeMatch[2], 10);
1135
+ if (!isNaN(start) && !isNaN(end) && start > 0 && end >= start) {
1136
+ return lines.slice(start - 1, end);
1137
+ }
1138
+ }
1139
+ return lines;
1140
+ }
1141
+ function createGadgetOutputViewer(store) {
1142
+ return createGadget({
1143
+ name: "GadgetOutputViewer",
1144
+ description: "View stored output from gadgets that returned too much data. Use patterns to filter lines (like grep) and limit to control output size. Patterns are applied first in order, then the limit is applied to the result.",
1145
+ schema: import_zod.z.object({
1146
+ id: import_zod.z.string().describe("ID of the stored output (from the truncation message)"),
1147
+ patterns: import_zod.z.array(patternSchema).optional().describe(
1148
+ "Filter patterns applied in order (like piping through grep). Each pattern can include or exclude lines with optional before/after context."
1149
+ ),
1150
+ limit: import_zod.z.string().optional().describe(
1151
+ "Line range to return after filtering. Formats: '100-' (first 100), '-25' (last 25), '50-100' (lines 50-100)"
1152
+ )
1153
+ }),
1154
+ examples: [
1155
+ {
1156
+ comment: "View first 50 lines of stored output",
1157
+ params: { id: "Search_abc12345", limit: "50-" }
1158
+ },
1159
+ {
1160
+ comment: "Filter for error lines with context",
1161
+ params: {
1162
+ id: "Search_abc12345",
1163
+ patterns: [{ regex: "error|Error|ERROR", include: true, before: 2, after: 5 }]
1164
+ }
1165
+ },
1166
+ {
1167
+ comment: "Exclude blank lines, then show first 100",
1168
+ params: {
1169
+ id: "Search_abc12345",
1170
+ patterns: [{ regex: "^\\s*$", include: false, before: 0, after: 0 }],
1171
+ limit: "100-"
1172
+ }
1173
+ },
1174
+ {
1175
+ comment: "Chain filters: find TODOs, exclude tests, limit to 50 lines",
1176
+ params: {
1177
+ id: "Search_abc12345",
1178
+ patterns: [
1179
+ { regex: "TODO", include: true, before: 1, after: 1 },
1180
+ { regex: "test|spec", include: false, before: 0, after: 0 }
1181
+ ],
1182
+ limit: "50-"
1183
+ }
1184
+ }
1185
+ ],
1186
+ execute: ({ id, patterns, limit }) => {
1187
+ const stored = store.get(id);
1188
+ if (!stored) {
1189
+ return `Error: No stored output with id "${id}". Available IDs: ${store.getIds().join(", ") || "(none)"}`;
1190
+ }
1191
+ let lines = stored.content.split("\n");
1192
+ if (patterns && patterns.length > 0) {
1193
+ lines = applyPatterns(
1194
+ lines,
1195
+ patterns.map((p) => ({
1196
+ regex: p.regex,
1197
+ include: p.include ?? true,
1198
+ before: p.before ?? 0,
1199
+ after: p.after ?? 0
1200
+ }))
1201
+ );
1202
+ }
1203
+ if (limit) {
1204
+ lines = applyLineLimit(lines, limit);
1205
+ }
1206
+ const totalLines = stored.lineCount;
1207
+ const returnedLines = lines.length;
1208
+ if (returnedLines === 0) {
1209
+ return `No lines matched the filters. Original output had ${totalLines} lines.`;
1210
+ }
1211
+ const header = returnedLines < totalLines ? `[Showing ${returnedLines} of ${totalLines} lines]
1212
+ ` : `[Showing all ${totalLines} lines]
1213
+ `;
1214
+ return header + lines.join("\n");
1215
+ }
1216
+ });
1217
+ }
1218
+ var import_zod, patternSchema;
1219
+ var init_output_viewer = __esm({
1220
+ "src/gadgets/output-viewer.ts"() {
1221
+ "use strict";
1222
+ import_zod = require("zod");
1223
+ init_create_gadget();
1224
+ patternSchema = import_zod.z.object({
1225
+ regex: import_zod.z.string().describe("Regular expression to match"),
1226
+ include: import_zod.z.boolean().default(true).describe("true = keep matching lines, false = exclude matching lines"),
1227
+ before: import_zod.z.number().int().min(0).default(0).describe("Context lines before each match (like grep -B)"),
1228
+ after: import_zod.z.number().int().min(0).default(0).describe("Context lines after each match (like grep -A)")
1229
+ });
1230
+ }
1231
+ });
1232
+
1233
+ // src/agent/gadget-output-store.ts
1234
+ var import_node_crypto, GadgetOutputStore;
1235
+ var init_gadget_output_store = __esm({
1236
+ "src/agent/gadget-output-store.ts"() {
1237
+ "use strict";
1238
+ import_node_crypto = require("crypto");
1239
+ GadgetOutputStore = class {
1240
+ outputs = /* @__PURE__ */ new Map();
1241
+ /**
1242
+ * Store a gadget output and return its ID.
1243
+ *
1244
+ * @param gadgetName - Name of the gadget that produced the output
1245
+ * @param content - Full output content to store
1246
+ * @returns Generated ID for retrieving the output later
1247
+ */
1248
+ store(gadgetName, content) {
1249
+ const id = this.generateId(gadgetName);
1250
+ const encoder = new TextEncoder();
1251
+ const stored = {
1252
+ id,
1253
+ gadgetName,
1254
+ content,
1255
+ byteSize: encoder.encode(content).length,
1256
+ lineCount: content.split("\n").length,
1257
+ timestamp: /* @__PURE__ */ new Date()
1258
+ };
1259
+ this.outputs.set(id, stored);
1260
+ return id;
1261
+ }
1262
+ /**
1263
+ * Retrieve a stored output by ID.
1264
+ *
1265
+ * @param id - The output ID (e.g., "Search_d34db33f")
1266
+ * @returns The stored output or undefined if not found
1267
+ */
1268
+ get(id) {
1269
+ return this.outputs.get(id);
1270
+ }
1271
+ /**
1272
+ * Check if an output exists.
1273
+ *
1274
+ * @param id - The output ID to check
1275
+ * @returns True if the output exists
1276
+ */
1277
+ has(id) {
1278
+ return this.outputs.has(id);
1279
+ }
1280
+ /**
1281
+ * Get all stored output IDs.
1282
+ *
1283
+ * @returns Array of output IDs
1284
+ */
1285
+ getIds() {
1286
+ return Array.from(this.outputs.keys());
1287
+ }
1288
+ /**
1289
+ * Get the number of stored outputs.
1290
+ */
1291
+ get size() {
1292
+ return this.outputs.size;
1293
+ }
1294
+ /**
1295
+ * Clear all stored outputs.
1296
+ * Called when the agent run completes.
1297
+ */
1298
+ clear() {
1299
+ this.outputs.clear();
1300
+ }
1301
+ /**
1302
+ * Generate a unique ID for a stored output.
1303
+ * Format: {GadgetName}_{8 hex chars}
1304
+ */
1305
+ generateId(gadgetName) {
1306
+ const hex = (0, import_node_crypto.randomBytes)(4).toString("hex");
1307
+ return `${gadgetName}_${hex}`;
1308
+ }
1309
+ };
1310
+ }
1311
+ });
1312
+
771
1313
  // src/agent/agent-internal-key.ts
772
1314
  function isValidAgentKey(key) {
773
1315
  return key === AGENT_INTERNAL_KEY;
@@ -1305,6 +1847,25 @@ function preprocessYaml(yamlStr) {
1305
1847
  let i = 0;
1306
1848
  while (i < lines.length) {
1307
1849
  const line = lines[i];
1850
+ const heredocMatch = line.match(/^(\s*)([\w-]+):\s*<<<([A-Za-z_][A-Za-z0-9_]*)\s*$/);
1851
+ if (heredocMatch) {
1852
+ const [, indent, key, delimiter] = heredocMatch;
1853
+ const bodyLines = [];
1854
+ i++;
1855
+ const closingRegex = new RegExp(`^${delimiter}\\s*$`);
1856
+ while (i < lines.length && !closingRegex.test(lines[i])) {
1857
+ bodyLines.push(lines[i]);
1858
+ i++;
1859
+ }
1860
+ if (i < lines.length) {
1861
+ i++;
1862
+ }
1863
+ result.push(`${indent}${key}: |`);
1864
+ for (const bodyLine of bodyLines) {
1865
+ result.push(`${indent} ${bodyLine}`);
1866
+ }
1867
+ continue;
1868
+ }
1308
1869
  const match = line.match(/^(\s*)([\w-]+):\s+(.+)$/);
1309
1870
  if (match) {
1310
1871
  const [, indent, key, value] = match;
@@ -1396,11 +1957,53 @@ function preprocessYaml(yamlStr) {
1396
1957
  }
1397
1958
  return result.join("\n");
1398
1959
  }
1399
- var yaml, import_js_toml, globalInvocationCounter, StreamParser;
1960
+ function preprocessTomlHeredoc(tomlStr) {
1961
+ const lines = tomlStr.split("\n");
1962
+ const result = [];
1963
+ let i = 0;
1964
+ const heredocStartRegex = /^(\s*)([\w-]+)\s*=\s*<<<([A-Za-z_][A-Za-z0-9_]*)\s*$/;
1965
+ while (i < lines.length) {
1966
+ const line = lines[i];
1967
+ const match = line.match(heredocStartRegex);
1968
+ if (match) {
1969
+ const [, indent, key, delimiter] = match;
1970
+ const bodyLines = [];
1971
+ i++;
1972
+ const closingRegex = new RegExp(`^${delimiter}\\s*$`);
1973
+ let foundClosing = false;
1974
+ while (i < lines.length) {
1975
+ const bodyLine = lines[i];
1976
+ if (closingRegex.test(bodyLine)) {
1977
+ foundClosing = true;
1978
+ i++;
1979
+ break;
1980
+ }
1981
+ bodyLines.push(bodyLine);
1982
+ i++;
1983
+ }
1984
+ if (bodyLines.length === 0) {
1985
+ result.push(`${indent}${key} = """"""`);
1986
+ } else {
1987
+ result.push(`${indent}${key} = """`);
1988
+ for (let j = 0; j < bodyLines.length - 1; j++) {
1989
+ result.push(bodyLines[j]);
1990
+ }
1991
+ result.push(`${bodyLines[bodyLines.length - 1]}"""`);
1992
+ }
1993
+ if (!foundClosing) {
1994
+ }
1995
+ continue;
1996
+ }
1997
+ result.push(line);
1998
+ i++;
1999
+ }
2000
+ return result.join("\n");
2001
+ }
2002
+ var yaml2, import_js_toml, globalInvocationCounter, StreamParser;
1400
2003
  var init_parser = __esm({
1401
2004
  "src/gadgets/parser.ts"() {
1402
2005
  "use strict";
1403
- yaml = __toESM(require("js-yaml"), 1);
2006
+ yaml2 = __toESM(require("js-yaml"), 1);
1404
2007
  import_js_toml = require("js-toml");
1405
2008
  init_constants();
1406
2009
  globalInvocationCounter = 0;
@@ -1447,14 +2050,14 @@ var init_parser = __esm({
1447
2050
  }
1448
2051
  if (this.parameterFormat === "yaml") {
1449
2052
  try {
1450
- return { parameters: yaml.load(preprocessYaml(raw)) };
2053
+ return { parameters: yaml2.load(preprocessYaml(raw)) };
1451
2054
  } catch (error) {
1452
2055
  return { parseError: error instanceof Error ? error.message : "Failed to parse YAML" };
1453
2056
  }
1454
2057
  }
1455
2058
  if (this.parameterFormat === "toml") {
1456
2059
  try {
1457
- return { parameters: (0, import_js_toml.load)(raw) };
2060
+ return { parameters: (0, import_js_toml.load)(preprocessTomlHeredoc(raw)) };
1458
2061
  } catch (error) {
1459
2062
  return { parseError: error instanceof Error ? error.message : "Failed to parse TOML" };
1460
2063
  }
@@ -1463,10 +2066,10 @@ var init_parser = __esm({
1463
2066
  return { parameters: JSON.parse(raw) };
1464
2067
  } catch {
1465
2068
  try {
1466
- return { parameters: (0, import_js_toml.load)(raw) };
2069
+ return { parameters: (0, import_js_toml.load)(preprocessTomlHeredoc(raw)) };
1467
2070
  } catch {
1468
2071
  try {
1469
- return { parameters: yaml.load(preprocessYaml(raw)) };
2072
+ return { parameters: yaml2.load(preprocessYaml(raw)) };
1470
2073
  } catch (error) {
1471
2074
  return {
1472
2075
  parseError: error instanceof Error ? error.message : "Failed to parse as JSON, TOML, or YAML"
@@ -1988,9 +2591,12 @@ var Agent;
1988
2591
  var init_agent = __esm({
1989
2592
  "src/agent/agent.ts"() {
1990
2593
  "use strict";
2594
+ init_constants();
1991
2595
  init_messages();
1992
2596
  init_model_shortcuts();
2597
+ init_output_viewer();
1993
2598
  init_logger();
2599
+ init_gadget_output_store();
1994
2600
  init_agent_internal_key();
1995
2601
  init_conversation_manager();
1996
2602
  init_event_handlers();
@@ -2015,6 +2621,10 @@ var init_agent = __esm({
2015
2621
  defaultGadgetTimeoutMs;
2016
2622
  defaultMaxTokens;
2017
2623
  userPromptProvided;
2624
+ // Gadget output limiting
2625
+ outputStore;
2626
+ outputLimitEnabled;
2627
+ outputLimitCharLimit;
2018
2628
  /**
2019
2629
  * Creates a new Agent instance.
2020
2630
  * @internal This constructor is private. Use LLMist.createAgent() or AgentBuilder instead.
@@ -2030,7 +2640,6 @@ var init_agent = __esm({
2030
2640
  this.maxIterations = options.maxIterations ?? 10;
2031
2641
  this.temperature = options.temperature;
2032
2642
  this.logger = options.logger ?? createLogger({ name: "llmist:agent" });
2033
- this.hooks = options.hooks ?? {};
2034
2643
  this.registry = options.registry;
2035
2644
  this.parameterFormat = options.parameterFormat ?? "json";
2036
2645
  this.gadgetStartPrefix = options.gadgetStartPrefix;
@@ -2041,6 +2650,16 @@ var init_agent = __esm({
2041
2650
  this.shouldContinueAfterError = options.shouldContinueAfterError;
2042
2651
  this.defaultGadgetTimeoutMs = options.defaultGadgetTimeoutMs;
2043
2652
  this.defaultMaxTokens = this.resolveMaxTokensFromCatalog(options.model);
2653
+ this.outputLimitEnabled = options.gadgetOutputLimit ?? DEFAULT_GADGET_OUTPUT_LIMIT;
2654
+ this.outputStore = new GadgetOutputStore();
2655
+ const limitPercent = options.gadgetOutputLimitPercent ?? DEFAULT_GADGET_OUTPUT_LIMIT_PERCENT;
2656
+ const limits = this.client.modelRegistry.getModelLimits(this.model);
2657
+ const contextWindow = limits?.contextWindow ?? FALLBACK_CONTEXT_WINDOW;
2658
+ this.outputLimitCharLimit = Math.floor(contextWindow * (limitPercent / 100) * CHARS_PER_TOKEN);
2659
+ if (this.outputLimitEnabled) {
2660
+ this.registry.register("GadgetOutputViewer", createGadgetOutputViewer(this.outputStore));
2661
+ }
2662
+ this.hooks = this.mergeOutputLimiterHook(options.hooks);
2044
2663
  const baseBuilder = new LLMMessageBuilder(options.promptConfig);
2045
2664
  if (options.systemPrompt) {
2046
2665
  baseBuilder.addSystem(options.systemPrompt);
@@ -2345,6 +2964,43 @@ var init_agent = __esm({
2345
2964
  }
2346
2965
  return this.client.modelRegistry.getModelLimits(unprefixedModelId)?.maxOutputTokens;
2347
2966
  }
2967
+ /**
2968
+ * Merge the output limiter interceptor into user-provided hooks.
2969
+ * The limiter runs first, then chains to any user interceptor.
2970
+ */
2971
+ mergeOutputLimiterHook(userHooks) {
2972
+ if (!this.outputLimitEnabled) {
2973
+ return userHooks ?? {};
2974
+ }
2975
+ const limiterInterceptor = (result, ctx) => {
2976
+ if (ctx.gadgetName === "GadgetOutputViewer") {
2977
+ return result;
2978
+ }
2979
+ if (result.length > this.outputLimitCharLimit) {
2980
+ const id = this.outputStore.store(ctx.gadgetName, result);
2981
+ const lines = result.split("\n").length;
2982
+ const bytes = new TextEncoder().encode(result).length;
2983
+ this.logger.info("Gadget output exceeded limit, stored for browsing", {
2984
+ gadgetName: ctx.gadgetName,
2985
+ outputId: id,
2986
+ bytes,
2987
+ lines,
2988
+ charLimit: this.outputLimitCharLimit
2989
+ });
2990
+ return `[Gadget "${ctx.gadgetName}" returned too much data: ${bytes.toLocaleString()} bytes, ${lines.toLocaleString()} lines. Use GadgetOutputViewer with id "${id}" to read it]`;
2991
+ }
2992
+ return result;
2993
+ };
2994
+ const userInterceptor = userHooks?.interceptors?.interceptGadgetResult;
2995
+ const chainedInterceptor = userInterceptor ? (result, ctx) => userInterceptor(limiterInterceptor(result, ctx), ctx) : limiterInterceptor;
2996
+ return {
2997
+ ...userHooks,
2998
+ interceptors: {
2999
+ ...userHooks?.interceptors,
3000
+ interceptGadgetResult: chainedInterceptor
3001
+ }
3002
+ };
3003
+ }
2348
3004
  /**
2349
3005
  * Run agent with named event handlers (syntactic sugar).
2350
3006
  *
@@ -4086,6 +4742,8 @@ var init_builder = __esm({
4086
4742
  stopOnGadgetError;
4087
4743
  shouldContinueAfterError;
4088
4744
  defaultGadgetTimeoutMs;
4745
+ gadgetOutputLimit;
4746
+ gadgetOutputLimitPercent;
4089
4747
  constructor(client) {
4090
4748
  this.client = client;
4091
4749
  }
@@ -4418,6 +5076,45 @@ var init_builder = __esm({
4418
5076
  this.defaultGadgetTimeoutMs = timeoutMs;
4419
5077
  return this;
4420
5078
  }
5079
+ /**
5080
+ * Enable or disable gadget output limiting.
5081
+ *
5082
+ * When enabled, gadget outputs exceeding the configured limit are stored
5083
+ * and can be browsed using the GadgetOutputViewer gadget.
5084
+ *
5085
+ * @param enabled - Whether to enable output limiting (default: true)
5086
+ * @returns This builder for chaining
5087
+ *
5088
+ * @example
5089
+ * ```typescript
5090
+ * .withGadgetOutputLimit(false) // Disable output limiting
5091
+ * ```
5092
+ */
5093
+ withGadgetOutputLimit(enabled) {
5094
+ this.gadgetOutputLimit = enabled;
5095
+ return this;
5096
+ }
5097
+ /**
5098
+ * Set the maximum gadget output as a percentage of the model's context window.
5099
+ *
5100
+ * Outputs exceeding this limit are stored for later browsing with GadgetOutputViewer.
5101
+ *
5102
+ * @param percent - Percentage of context window (1-100, default: 15)
5103
+ * @returns This builder for chaining
5104
+ * @throws {Error} If percent is not between 1 and 100
5105
+ *
5106
+ * @example
5107
+ * ```typescript
5108
+ * .withGadgetOutputLimitPercent(25) // 25% of context window
5109
+ * ```
5110
+ */
5111
+ withGadgetOutputLimitPercent(percent) {
5112
+ if (percent < 1 || percent > 100) {
5113
+ throw new Error("Output limit percent must be between 1 and 100");
5114
+ }
5115
+ this.gadgetOutputLimitPercent = percent;
5116
+ return this;
5117
+ }
4421
5118
  /**
4422
5119
  * Build and create the agent with the given user prompt.
4423
5120
  * Returns the Agent instance ready to run.
@@ -4462,7 +5159,9 @@ var init_builder = __esm({
4462
5159
  textOnlyHandler: this.textOnlyHandler,
4463
5160
  stopOnGadgetError: this.stopOnGadgetError,
4464
5161
  shouldContinueAfterError: this.shouldContinueAfterError,
4465
- defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs
5162
+ defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
5163
+ gadgetOutputLimit: this.gadgetOutputLimit,
5164
+ gadgetOutputLimitPercent: this.gadgetOutputLimitPercent
4466
5165
  };
4467
5166
  return new Agent(AGENT_INTERNAL_KEY, options);
4468
5167
  }
@@ -4561,7 +5260,9 @@ var init_builder = __esm({
4561
5260
  textOnlyHandler: this.textOnlyHandler,
4562
5261
  stopOnGadgetError: this.stopOnGadgetError,
4563
5262
  shouldContinueAfterError: this.shouldContinueAfterError,
4564
- defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs
5263
+ defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
5264
+ gadgetOutputLimit: this.gadgetOutputLimit,
5265
+ gadgetOutputLimitPercent: this.gadgetOutputLimitPercent
4565
5266
  };
4566
5267
  return new Agent(AGENT_INTERNAL_KEY, options);
4567
5268
  }
@@ -4580,6 +5281,7 @@ __export(index_exports, {
4580
5281
  DEFAULT_PROMPTS: () => DEFAULT_PROMPTS,
4581
5282
  Gadget: () => Gadget,
4582
5283
  GadgetExecutor: () => GadgetExecutor,
5284
+ GadgetOutputStore: () => GadgetOutputStore,
4583
5285
  GadgetRegistry: () => GadgetRegistry,
4584
5286
  GeminiGenerativeProvider: () => GeminiGenerativeProvider,
4585
5287
  HookPresets: () => HookPresets,
@@ -4600,6 +5302,7 @@ __export(index_exports, {
4600
5302
  complete: () => complete,
4601
5303
  createAnthropicProviderFromEnv: () => createAnthropicProviderFromEnv,
4602
5304
  createGadget: () => createGadget,
5305
+ createGadgetOutputViewer: () => createGadgetOutputViewer,
4603
5306
  createGeminiProviderFromEnv: () => createGeminiProviderFromEnv,
4604
5307
  createLogger: () => createLogger,
4605
5308
  createMockAdapter: () => createMockAdapter,
@@ -4621,10 +5324,10 @@ __export(index_exports, {
4621
5324
  stream: () => stream,
4622
5325
  validateAndApplyDefaults: () => validateAndApplyDefaults,
4623
5326
  validateGadgetParams: () => validateGadgetParams,
4624
- z: () => import_zod.z
5327
+ z: () => import_zod2.z
4625
5328
  });
4626
5329
  module.exports = __toCommonJS(index_exports);
4627
- var import_zod = require("zod");
5330
+ var import_zod2 = require("zod");
4628
5331
  init_builder();
4629
5332
  init_event_handlers();
4630
5333
 
@@ -5356,6 +6059,7 @@ var HookPresets = class _HookPresets {
5356
6059
  // src/agent/index.ts
5357
6060
  init_conversation_manager();
5358
6061
  init_stream_processor();
6062
+ init_gadget_output_store();
5359
6063
 
5360
6064
  // src/index.ts
5361
6065
  init_client();
@@ -5365,278 +6069,16 @@ init_model_shortcuts();
5365
6069
  init_options();
5366
6070
  init_prompt_config();
5367
6071
  init_quick_methods();
5368
-
5369
- // src/gadgets/gadget.ts
5370
- var yaml2 = __toESM(require("js-yaml"), 1);
5371
-
5372
- // src/gadgets/schema-to-json.ts
5373
- var z2 = __toESM(require("zod"), 1);
5374
- init_logger();
5375
- function schemaToJSONSchema(schema, options) {
5376
- const jsonSchema = z2.toJSONSchema(schema, options ?? { target: "draft-7" });
5377
- const mismatches = detectDescriptionMismatch(schema, jsonSchema);
5378
- if (mismatches.length > 0) {
5379
- defaultLogger.warn(
5380
- `Zod instance mismatch detected: ${mismatches.length} description(s) lost. For best results, use: import { z } from "llmist"`
5381
- );
5382
- return mergeDescriptions(schema, jsonSchema);
5383
- }
5384
- return jsonSchema;
5385
- }
5386
- function detectDescriptionMismatch(schema, jsonSchema) {
5387
- const mismatches = [];
5388
- function checkSchema(zodSchema, json, path) {
5389
- if (!zodSchema || typeof zodSchema !== "object") return;
5390
- const def = zodSchema._def;
5391
- const jsonObj = json;
5392
- if (def?.description && !jsonObj?.description) {
5393
- mismatches.push(path || "root");
5394
- }
5395
- if (def?.typeName === "ZodObject" && def?.shape) {
5396
- const shape = typeof def.shape === "function" ? def.shape() : def.shape;
5397
- for (const [key, fieldSchema] of Object.entries(shape)) {
5398
- const properties = jsonObj?.properties;
5399
- const jsonProp = properties?.[key];
5400
- checkSchema(fieldSchema, jsonProp, path ? `${path}.${key}` : key);
5401
- }
5402
- }
5403
- if (def?.typeName === "ZodArray" && def?.type) {
5404
- checkSchema(def.type, jsonObj?.items, path ? `${path}[]` : "[]");
5405
- }
5406
- if ((def?.typeName === "ZodOptional" || def?.typeName === "ZodNullable") && def?.innerType) {
5407
- checkSchema(def.innerType, json, path);
5408
- }
5409
- if (def?.typeName === "ZodDefault" && def?.innerType) {
5410
- checkSchema(def.innerType, json, path);
5411
- }
5412
- }
5413
- checkSchema(schema, jsonSchema, "");
5414
- return mismatches;
5415
- }
5416
- function mergeDescriptions(schema, jsonSchema) {
5417
- function merge(zodSchema, json) {
5418
- if (!json || typeof json !== "object") return json;
5419
- const def = zodSchema._def;
5420
- const jsonObj = json;
5421
- const merged = { ...jsonObj };
5422
- if (def?.description && !jsonObj.description) {
5423
- merged.description = def.description;
5424
- }
5425
- if (def?.typeName === "ZodObject" && def?.shape && jsonObj.properties) {
5426
- const shape = typeof def.shape === "function" ? def.shape() : def.shape;
5427
- const properties = jsonObj.properties;
5428
- merged.properties = { ...properties };
5429
- for (const [key, fieldSchema] of Object.entries(shape)) {
5430
- if (properties[key]) {
5431
- merged.properties[key] = merge(fieldSchema, properties[key]);
5432
- }
5433
- }
5434
- }
5435
- if (def?.typeName === "ZodArray" && def?.type && jsonObj.items) {
5436
- merged.items = merge(def.type, jsonObj.items);
5437
- }
5438
- if ((def?.typeName === "ZodOptional" || def?.typeName === "ZodNullable") && def?.innerType) {
5439
- return merge(def.innerType, json);
5440
- }
5441
- if (def?.typeName === "ZodDefault" && def?.innerType) {
5442
- return merge(def.innerType, json);
5443
- }
5444
- return merged;
5445
- }
5446
- return merge(schema, jsonSchema);
5447
- }
5448
-
5449
- // src/gadgets/gadget.ts
5450
- init_schema_validator();
5451
- function formatYamlValue(value, indent = "") {
5452
- if (typeof value === "string") {
5453
- const lines = value.split("\n");
5454
- if (lines.length === 1 && !value.includes(":") && !value.startsWith("-")) {
5455
- return value;
5456
- }
5457
- const indentedLines = lines.map((line) => `${indent} ${line}`).join("\n");
5458
- return `|
5459
- ${indentedLines}`;
5460
- }
5461
- if (typeof value === "number" || typeof value === "boolean") {
5462
- return String(value);
5463
- }
5464
- if (value === null || value === void 0) {
5465
- return "null";
5466
- }
5467
- if (Array.isArray(value)) {
5468
- if (value.length === 0) return "[]";
5469
- const items = value.map((item) => `${indent}- ${formatYamlValue(item, indent + " ")}`);
5470
- return "\n" + items.join("\n");
5471
- }
5472
- if (typeof value === "object") {
5473
- const entries = Object.entries(value);
5474
- if (entries.length === 0) return "{}";
5475
- const lines = entries.map(([k, v]) => {
5476
- const formattedValue = formatYamlValue(v, indent + " ");
5477
- if (formattedValue.startsWith("\n") || formattedValue.startsWith("|")) {
5478
- return `${indent}${k}: ${formattedValue}`;
5479
- }
5480
- return `${indent}${k}: ${formattedValue}`;
5481
- });
5482
- return "\n" + lines.join("\n");
5483
- }
5484
- return yaml2.dump(value).trimEnd();
5485
- }
5486
- function formatParamsAsYaml(params) {
5487
- const lines = [];
5488
- for (const [key, value] of Object.entries(params)) {
5489
- const formattedValue = formatYamlValue(value, "");
5490
- if (formattedValue.startsWith("\n")) {
5491
- lines.push(`${key}:${formattedValue}`);
5492
- } else {
5493
- lines.push(`${key}: ${formattedValue}`);
5494
- }
5495
- }
5496
- return lines.join("\n");
5497
- }
5498
- function formatTomlValue(value) {
5499
- if (typeof value === "string") {
5500
- if (value.includes("\n")) {
5501
- return `"""
5502
- ${value}
5503
- """`;
5504
- }
5505
- return JSON.stringify(value);
5506
- }
5507
- if (typeof value === "number" || typeof value === "boolean") {
5508
- return String(value);
5509
- }
5510
- if (value === null || value === void 0) {
5511
- return '""';
5512
- }
5513
- if (Array.isArray(value)) {
5514
- return JSON.stringify(value);
5515
- }
5516
- if (typeof value === "object") {
5517
- return JSON.stringify(value);
5518
- }
5519
- return JSON.stringify(value);
5520
- }
5521
- function formatParamsAsToml(params) {
5522
- const lines = [];
5523
- for (const [key, value] of Object.entries(params)) {
5524
- lines.push(`${key} = ${formatTomlValue(value)}`);
5525
- }
5526
- return lines.join("\n");
5527
- }
5528
- var BaseGadget = class {
5529
- /**
5530
- * The name of the gadget. Used for identification when LLM calls it.
5531
- * If not provided, defaults to the class name.
5532
- */
5533
- name;
5534
- /**
5535
- * Optional Zod schema describing the expected input payload. When provided,
5536
- * it will be validated before execution and transformed into a JSON Schema
5537
- * representation that is surfaced to the LLM as part of the instructions.
5538
- */
5539
- parameterSchema;
5540
- /**
5541
- * Optional timeout in milliseconds for gadget execution.
5542
- * If execution exceeds this timeout, a TimeoutException will be thrown.
5543
- * If not set, the global defaultGadgetTimeoutMs from runtime options will be used.
5544
- * Set to 0 or undefined to disable timeout for this gadget.
5545
- */
5546
- timeoutMs;
5547
- /**
5548
- * Optional usage examples to help LLMs understand proper invocation.
5549
- * Examples are rendered in getInstruction() alongside the schema.
5550
- *
5551
- * Note: Uses broader `unknown` type to allow typed examples from subclasses
5552
- * while maintaining runtime compatibility.
5553
- */
5554
- examples;
5555
- /**
5556
- * Auto-generated instruction text for the LLM.
5557
- * Combines name, description, and parameter schema into a formatted instruction.
5558
- * @deprecated Use getInstruction(format) instead for format-specific schemas
5559
- */
5560
- get instruction() {
5561
- return this.getInstruction("yaml");
5562
- }
5563
- /**
5564
- * Generate instruction text for the LLM with format-specific schema.
5565
- * Combines name, description, and parameter schema into a formatted instruction.
5566
- *
5567
- * @param format - Format for the schema representation ('json' | 'yaml' | 'toml' | 'auto')
5568
- * @returns Formatted instruction string
5569
- */
5570
- getInstruction(format = "json") {
5571
- const parts = [];
5572
- parts.push(this.description);
5573
- if (this.parameterSchema) {
5574
- const gadgetName = this.name ?? this.constructor.name;
5575
- validateGadgetSchema(this.parameterSchema, gadgetName);
5576
- const jsonSchema = schemaToJSONSchema(this.parameterSchema, {
5577
- target: "draft-7"
5578
- });
5579
- if (format === "json" || format === "auto") {
5580
- parts.push("\n\nInput Schema (JSON):");
5581
- parts.push(JSON.stringify(jsonSchema, null, 2));
5582
- } else if (format === "toml") {
5583
- parts.push("\n\nInput Schema (TOML):");
5584
- parts.push(JSON.stringify(jsonSchema, null, 2));
5585
- } else {
5586
- const yamlSchema = yaml2.dump(jsonSchema).trimEnd();
5587
- parts.push("\n\nInput Schema (YAML):");
5588
- parts.push(yamlSchema);
5589
- }
5590
- }
5591
- if (this.examples && this.examples.length > 0) {
5592
- parts.push("\n\nExamples:");
5593
- this.examples.forEach((example, index) => {
5594
- if (index > 0) {
5595
- parts.push("");
5596
- }
5597
- if (example.comment) {
5598
- parts.push(`# ${example.comment}`);
5599
- }
5600
- parts.push("Input:");
5601
- if (format === "json" || format === "auto") {
5602
- parts.push(JSON.stringify(example.params, null, 2));
5603
- } else if (format === "toml") {
5604
- parts.push(formatParamsAsToml(example.params));
5605
- } else {
5606
- parts.push(formatParamsAsYaml(example.params));
5607
- }
5608
- if (example.output !== void 0) {
5609
- parts.push("Output:");
5610
- parts.push(example.output);
5611
- }
5612
- });
5613
- }
5614
- return parts.join("\n");
5615
- }
5616
- };
5617
-
5618
- // src/gadgets/create-gadget.ts
5619
- function createGadget(config) {
5620
- class DynamicGadget extends BaseGadget {
5621
- name = config.name;
5622
- description = config.description;
5623
- parameterSchema = config.schema;
5624
- timeoutMs = config.timeoutMs;
5625
- examples = config.examples;
5626
- execute(params) {
5627
- return config.execute(params);
5628
- }
5629
- }
5630
- return new DynamicGadget();
5631
- }
5632
-
5633
- // src/index.ts
6072
+ init_create_gadget();
6073
+ init_output_viewer();
5634
6074
  init_exceptions();
5635
6075
  init_executor();
6076
+ init_gadget();
5636
6077
  init_parser();
5637
6078
  init_registry();
5638
6079
 
5639
6080
  // src/gadgets/typed-gadget.ts
6081
+ init_gadget();
5640
6082
  function Gadget(config) {
5641
6083
  class GadgetBase extends BaseGadget {
5642
6084
  description = config.description;
@@ -6356,6 +6798,9 @@ function createMockClient(options) {
6356
6798
  defaultProvider: "mock"
6357
6799
  });
6358
6800
  }
6801
+
6802
+ // src/testing/mock-gadget.ts
6803
+ init_gadget();
6359
6804
  // Annotate the CommonJS export names for ESM import in node:
6360
6805
  0 && (module.exports = {
6361
6806
  AgentBuilder,
@@ -6366,6 +6811,7 @@ function createMockClient(options) {
6366
6811
  DEFAULT_PROMPTS,
6367
6812
  Gadget,
6368
6813
  GadgetExecutor,
6814
+ GadgetOutputStore,
6369
6815
  GadgetRegistry,
6370
6816
  GeminiGenerativeProvider,
6371
6817
  HookPresets,
@@ -6386,6 +6832,7 @@ function createMockClient(options) {
6386
6832
  complete,
6387
6833
  createAnthropicProviderFromEnv,
6388
6834
  createGadget,
6835
+ createGadgetOutputViewer,
6389
6836
  createGeminiProviderFromEnv,
6390
6837
  createLogger,
6391
6838
  createMockAdapter,