llmist 0.8.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -32,12 +32,13 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
32
32
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
33
33
 
34
34
  // src/core/constants.ts
35
- var GADGET_START_PREFIX, GADGET_END_PREFIX, DEFAULT_GADGET_OUTPUT_LIMIT, DEFAULT_GADGET_OUTPUT_LIMIT_PERCENT, CHARS_PER_TOKEN, FALLBACK_CONTEXT_WINDOW;
35
+ 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;
36
36
  var init_constants = __esm({
37
37
  "src/core/constants.ts"() {
38
38
  "use strict";
39
39
  GADGET_START_PREFIX = "!!!GADGET_START:";
40
40
  GADGET_END_PREFIX = "!!!GADGET_END";
41
+ GADGET_ARG_PREFIX = "!!!ARG:";
41
42
  DEFAULT_GADGET_OUTPUT_LIMIT = true;
42
43
  DEFAULT_GADGET_OUTPUT_LIMIT_PERCENT = 15;
43
44
  CHARS_PER_TOKEN = 4;
@@ -370,17 +371,12 @@ var init_prompt_config = __esm({
370
371
  "EACH MARKER MUST START WITH A NEWLINE."
371
372
  ].join("\n"),
372
373
  criticalUsage: "INVOKE gadgets using the markers - do not describe what you want to do.",
373
- formatDescriptionYaml: "Parameters in YAML format (one per line)",
374
- formatDescriptionJson: "Parameters in JSON format (valid JSON object)",
375
- formatDescriptionToml: "Parameters in TOML format (key = value pairs, use heredoc for multiline: key = <<<EOF ... EOF)",
374
+ formatDescription: (ctx) => `Parameters using ${ctx.argPrefix}name markers (value on next line(s), no escaping needed)`,
376
375
  rules: () => [
377
376
  "Output ONLY plain text with the exact markers - never use function/tool calling",
378
377
  "You can invoke multiple gadgets in a single response",
379
378
  "For dependent gadgets, invoke the first one and wait for the result"
380
379
  ],
381
- schemaLabelJson: "\n\nInput Schema (JSON):",
382
- schemaLabelYaml: "\n\nInput Schema (YAML):",
383
- schemaLabelToml: "\n\nInput Schema (TOML):",
384
380
  customExamples: null
385
381
  };
386
382
  }
@@ -397,6 +393,7 @@ var init_messages = __esm({
397
393
  messages = [];
398
394
  startPrefix = GADGET_START_PREFIX;
399
395
  endPrefix = GADGET_END_PREFIX;
396
+ argPrefix = GADGET_ARG_PREFIX;
400
397
  promptConfig;
401
398
  constructor(promptConfig) {
402
399
  this.promptConfig = promptConfig ?? {};
@@ -405,26 +402,32 @@ var init_messages = __esm({
405
402
  * Set custom prefixes for gadget markers.
406
403
  * Used to configure history builder to match system prompt markers.
407
404
  */
408
- withPrefixes(startPrefix, endPrefix) {
405
+ withPrefixes(startPrefix, endPrefix, argPrefix) {
409
406
  this.startPrefix = startPrefix;
410
407
  this.endPrefix = endPrefix;
408
+ if (argPrefix) {
409
+ this.argPrefix = argPrefix;
410
+ }
411
411
  return this;
412
412
  }
413
413
  addSystem(content, metadata) {
414
414
  this.messages.push({ role: "system", content, metadata });
415
415
  return this;
416
416
  }
417
- addGadgets(gadgets, parameterFormat = "json", options) {
417
+ addGadgets(gadgets, options) {
418
418
  if (options?.startPrefix) {
419
419
  this.startPrefix = options.startPrefix;
420
420
  }
421
421
  if (options?.endPrefix) {
422
422
  this.endPrefix = options.endPrefix;
423
423
  }
424
+ if (options?.argPrefix) {
425
+ this.argPrefix = options.argPrefix;
426
+ }
424
427
  const context = {
425
- parameterFormat,
426
428
  startPrefix: this.startPrefix,
427
429
  endPrefix: this.endPrefix,
430
+ argPrefix: this.argPrefix,
428
431
  gadgetCount: gadgets.length,
429
432
  gadgetNames: gadgets.map((g) => g.name ?? g.constructor.name)
430
433
  };
@@ -435,26 +438,19 @@ var init_messages = __esm({
435
438
  context
436
439
  );
437
440
  parts.push(mainInstruction);
438
- parts.push(this.buildGadgetsSection(gadgets, parameterFormat));
439
- parts.push(this.buildUsageSection(parameterFormat, context));
441
+ parts.push(this.buildGadgetsSection(gadgets));
442
+ parts.push(this.buildUsageSection(context));
440
443
  this.messages.push({ role: "system", content: parts.join("") });
441
444
  return this;
442
445
  }
443
- buildGadgetsSection(gadgets, parameterFormat) {
446
+ buildGadgetsSection(gadgets) {
444
447
  const parts = [];
445
448
  parts.push("\n\nAVAILABLE GADGETS");
446
449
  parts.push("\n=================\n");
447
450
  for (const gadget of gadgets) {
448
451
  const gadgetName = gadget.name ?? gadget.constructor.name;
449
- const instruction = gadget.getInstruction(parameterFormat);
450
- const schemaMarkers = {
451
- yaml: "\n\nInput Schema (YAML):",
452
- json: "\n\nInput Schema (JSON):",
453
- toml: "\n\nInput Schema (TOML):",
454
- auto: "\n\nInput Schema (JSON):"
455
- // auto defaults to JSON schema display
456
- };
457
- const schemaMarker = schemaMarkers[parameterFormat];
452
+ const instruction = gadget.getInstruction(this.argPrefix);
453
+ const schemaMarker = "\n\nInput Schema (BLOCK):";
458
454
  const schemaIndex = instruction.indexOf(schemaMarker);
459
455
  const description = (schemaIndex !== -1 ? instruction.substring(0, schemaIndex) : instruction).trim();
460
456
  const schema = schemaIndex !== -1 ? instruction.substring(schemaIndex + schemaMarker.length).trim() : "";
@@ -465,35 +461,20 @@ ${description}`);
465
461
  if (schema) {
466
462
  parts.push(`
467
463
 
468
- PARAMETERS (${parameterFormat.toUpperCase()}):
464
+ PARAMETERS (BLOCK):
469
465
  ${schema}`);
470
466
  }
471
467
  parts.push("\n\n---");
472
468
  }
473
469
  return parts.join("");
474
470
  }
475
- buildUsageSection(parameterFormat, context) {
471
+ buildUsageSection(context) {
476
472
  const parts = [];
477
- const formatDescriptionMap = {
478
- yaml: {
479
- config: this.promptConfig.formatDescriptionYaml,
480
- defaultValue: DEFAULT_PROMPTS.formatDescriptionYaml
481
- },
482
- json: {
483
- config: this.promptConfig.formatDescriptionJson,
484
- defaultValue: DEFAULT_PROMPTS.formatDescriptionJson
485
- },
486
- toml: {
487
- config: this.promptConfig.formatDescriptionToml,
488
- defaultValue: DEFAULT_PROMPTS.formatDescriptionToml
489
- },
490
- auto: {
491
- config: this.promptConfig.formatDescriptionJson,
492
- defaultValue: DEFAULT_PROMPTS.formatDescriptionJson
493
- }
494
- };
495
- const { config, defaultValue } = formatDescriptionMap[parameterFormat];
496
- const formatDescription = resolvePromptTemplate(config, defaultValue, context);
473
+ const formatDescription = resolvePromptTemplate(
474
+ this.promptConfig.formatDescription,
475
+ DEFAULT_PROMPTS.formatDescription,
476
+ context
477
+ );
497
478
  parts.push("\n\nHOW TO INVOKE GADGETS");
498
479
  parts.push("\n=====================\n");
499
480
  const criticalUsage = resolvePromptTemplate(
@@ -511,124 +492,90 @@ CRITICAL: ${criticalUsage}
511
492
  2. ${formatDescription}`);
512
493
  parts.push(`
513
494
  3. End marker: ${this.endPrefix}`);
514
- parts.push(this.buildExamplesSection(parameterFormat, context));
495
+ parts.push(this.buildExamplesSection(context));
515
496
  parts.push(this.buildRulesSection(context));
516
497
  parts.push("\n");
517
498
  return parts.join("");
518
499
  }
519
- buildExamplesSection(parameterFormat, context) {
500
+ buildExamplesSection(context) {
520
501
  if (this.promptConfig.customExamples) {
521
502
  return this.promptConfig.customExamples(context);
522
503
  }
523
504
  const parts = [];
524
- const singleExamples = {
525
- yaml: `${this.startPrefix}translate
526
- from: English
527
- to: Polish
528
- content: "Paris is the capital of France: a beautiful city."
529
- ${this.endPrefix}`,
530
- json: `${this.startPrefix}translate
531
- {"from": "English", "to": "Polish", "content": "Paris is the capital of France: a beautiful city."}
532
- ${this.endPrefix}`,
533
- toml: `${this.startPrefix}translate
534
- from = "English"
535
- to = "Polish"
536
- content = "Paris is the capital of France: a beautiful city."
537
- ${this.endPrefix}`,
538
- auto: `${this.startPrefix}translate
539
- {"from": "English", "to": "Polish", "content": "Paris is the capital of France: a beautiful city."}
540
- ${this.endPrefix}`
541
- };
505
+ const singleExample = `${this.startPrefix}translate
506
+ ${this.argPrefix}from
507
+ English
508
+ ${this.argPrefix}to
509
+ Polish
510
+ ${this.argPrefix}content
511
+ Paris is the capital of France: a beautiful city.
512
+ ${this.endPrefix}`;
542
513
  parts.push(`
543
514
 
544
515
  EXAMPLE (Single Gadget):
545
516
 
546
- ${singleExamples[parameterFormat]}`);
547
- const multipleExamples = {
548
- yaml: `${this.startPrefix}translate
549
- from: English
550
- to: Polish
551
- content: "Paris is the capital of France: a beautiful city."
552
- ${this.endPrefix}
553
- ${this.startPrefix}analyze
554
- type: economic_analysis
555
- matter: "Polish Economy"
556
- question: <<<EOF
557
- Analyze the following:
558
- - Polish arms exports 2025
559
- - Economic implications
560
- EOF
561
- ${this.endPrefix}`,
562
- json: `${this.startPrefix}translate
563
- {"from": "English", "to": "Polish", "content": "Paris is the capital of France: a beautiful city."}
564
- ${this.endPrefix}
565
- ${this.startPrefix}analyze
566
- {"type": "economic_analysis", "matter": "Polish Economy", "question": "Analyze the following: Polish arms exports 2025, economic implications"}
567
- ${this.endPrefix}`,
568
- toml: `${this.startPrefix}translate
569
- from = "English"
570
- to = "Polish"
571
- content = "Paris is the capital of France: a beautiful city."
517
+ ${singleExample}`);
518
+ const multipleExample = `${this.startPrefix}translate
519
+ ${this.argPrefix}from
520
+ English
521
+ ${this.argPrefix}to
522
+ Polish
523
+ ${this.argPrefix}content
524
+ Paris is the capital of France: a beautiful city.
572
525
  ${this.endPrefix}
573
526
  ${this.startPrefix}analyze
574
- type = "economic_analysis"
575
- matter = "Polish Economy"
576
- question = <<<EOF
527
+ ${this.argPrefix}type
528
+ economic_analysis
529
+ ${this.argPrefix}matter
530
+ Polish Economy
531
+ ${this.argPrefix}question
577
532
  Analyze the following:
578
533
  - Polish arms exports 2025
579
534
  - Economic implications
580
- EOF
581
- ${this.endPrefix}`,
582
- auto: `${this.startPrefix}translate
583
- {"from": "English", "to": "Polish", "content": "Paris is the capital of France: a beautiful city."}
584
- ${this.endPrefix}
585
- ${this.startPrefix}analyze
586
- {"type": "economic_analysis", "matter": "Polish Economy", "question": "Analyze the following: Polish arms exports 2025, economic implications"}
587
- ${this.endPrefix}`
588
- };
535
+ ${this.endPrefix}`;
589
536
  parts.push(`
590
537
 
591
538
  EXAMPLE (Multiple Gadgets):
592
539
 
593
- ${multipleExamples[parameterFormat]}`);
594
- if (parameterFormat === "yaml") {
595
- parts.push(`
596
-
597
- YAML HEREDOC SYNTAX:
598
- For string values with multiple lines, use heredoc syntax (<<<DELIMITER...DELIMITER):
540
+ ${multipleExample}`);
541
+ parts.push(`
599
542
 
600
- filePath: "README.md"
601
- content: <<<EOF
602
- # Project Title
543
+ BLOCK FORMAT SYNTAX:
544
+ Block format uses ${this.argPrefix}name markers. Values are captured verbatim until the next marker.
603
545
 
604
- This content can contain:
605
- - Markdown lists
606
- - Special characters: # : -
607
- - Multiple paragraphs
608
- EOF
546
+ ${this.argPrefix}filename
547
+ calculator.ts
548
+ ${this.argPrefix}code
549
+ class Calculator {
550
+ private history: string[] = [];
609
551
 
610
- The delimiter (EOF) can be any identifier. The closing delimiter must be on its own line.
611
- No indentation is required for the content.`);
612
- } else if (parameterFormat === "toml") {
613
- parts.push(`
614
-
615
- TOML HEREDOC SYNTAX:
616
- For string values with multiple lines, use heredoc syntax (<<<DELIMITER...DELIMITER):
552
+ add(a: number, b: number): number {
553
+ const result = a + b;
554
+ this.history.push(\`\${a} + \${b} = \${result}\`);
555
+ return result;
556
+ }
557
+ }
617
558
 
618
- filePath = "README.md"
619
- content = <<<EOF
620
- # Project Title
559
+ BLOCK FORMAT RULES:
560
+ - Each parameter starts with ${this.argPrefix}parameterName on its own line
561
+ - The value starts on the NEXT line after the marker
562
+ - Value ends when the next ${this.argPrefix} or ${this.endPrefix} appears
563
+ - NO escaping needed - write values exactly as they should appear
564
+ - Perfect for code, JSON, markdown, or any content with special characters
621
565
 
622
- This content can contain:
623
- - Markdown lists
624
- - Special characters: # : -
625
- - Multiple paragraphs
626
- EOF
566
+ NESTED OBJECTS (use / separator):
567
+ ${this.argPrefix}config/timeout
568
+ 30
569
+ ${this.argPrefix}config/retries
570
+ 3
571
+ Produces: { "config": { "timeout": "30", "retries": "3" } }
627
572
 
628
- The delimiter (EOF) can be any identifier. The closing delimiter must be on its own line.
629
- IMPORTANT: Content inside heredoc is LITERAL - do NOT escape backticks, dollar signs, or any characters.
630
- NEVER use TOML triple-quote strings ("""). ALWAYS use heredoc syntax (<<<EOF...EOF) for multiline content.`);
631
- }
573
+ ARRAYS (use numeric indices):
574
+ ${this.argPrefix}items/0
575
+ first
576
+ ${this.argPrefix}items/1
577
+ second
578
+ Produces: { "items": ["first", "second"] }`);
632
579
  return parts.join("");
633
580
  }
634
581
  buildRulesSection(context) {
@@ -649,8 +596,8 @@ NEVER use TOML triple-quote strings ("""). ALWAYS use heredoc syntax (<<<EOF...E
649
596
  this.messages.push({ role: "assistant", content, metadata });
650
597
  return this;
651
598
  }
652
- addGadgetCall(gadget, parameters, result, parameterFormat = "json") {
653
- const paramStr = this.formatParameters(parameters, parameterFormat);
599
+ addGadgetCall(gadget, parameters, result) {
600
+ const paramStr = this.formatBlockParameters(parameters, "");
654
601
  this.messages.push({
655
602
  role: "assistant",
656
603
  content: `${this.startPrefix}${gadget}
@@ -663,26 +610,32 @@ ${this.endPrefix}`
663
610
  });
664
611
  return this;
665
612
  }
666
- formatParameters(parameters, format) {
667
- if (format === "yaml") {
668
- return Object.entries(parameters).map(([key, value]) => {
669
- if (typeof value === "string") {
670
- return `${key}: ${value}`;
671
- }
672
- return `${key}: ${JSON.stringify(value)}`;
673
- }).join("\n");
674
- }
675
- if (format === "toml") {
676
- return Object.entries(parameters).map(([key, value]) => {
677
- if (typeof value === "string" && value.includes("\n")) {
678
- return `${key} = <<<EOF
679
- ${value}
680
- EOF`;
681
- }
682
- return `${key} = ${JSON.stringify(value)}`;
683
- }).join("\n");
613
+ /**
614
+ * Format parameters as Block format with JSON Pointer paths.
615
+ * Uses the configured argPrefix for consistency with system prompt.
616
+ */
617
+ formatBlockParameters(params, prefix) {
618
+ const lines = [];
619
+ for (const [key, value] of Object.entries(params)) {
620
+ const fullPath = prefix ? `${prefix}/${key}` : key;
621
+ if (Array.isArray(value)) {
622
+ value.forEach((item, index) => {
623
+ const itemPath = `${fullPath}/${index}`;
624
+ if (typeof item === "object" && item !== null) {
625
+ lines.push(this.formatBlockParameters(item, itemPath));
626
+ } else {
627
+ lines.push(`${this.argPrefix}${itemPath}`);
628
+ lines.push(String(item));
629
+ }
630
+ });
631
+ } else if (typeof value === "object" && value !== null) {
632
+ lines.push(this.formatBlockParameters(value, fullPath));
633
+ } else {
634
+ lines.push(`${this.argPrefix}${fullPath}`);
635
+ lines.push(String(value));
636
+ }
684
637
  }
685
- return JSON.stringify(parameters);
638
+ return lines.join("\n");
686
639
  }
687
640
  build() {
688
641
  return [...this.messages];
@@ -854,134 +807,72 @@ var init_schema_to_json = __esm({
854
807
  });
855
808
 
856
809
  // src/gadgets/gadget.ts
857
- function findSafeDelimiter(content) {
858
- const lines = content.split("\n");
859
- for (const delimiter of HEREDOC_DELIMITERS) {
860
- const regex = new RegExp(`^${delimiter}\\s*$`);
861
- const isUsed = lines.some((line) => regex.test(line));
862
- if (!isUsed) {
863
- return delimiter;
864
- }
865
- }
866
- let counter = 1;
867
- while (counter < 1e3) {
868
- const delimiter = `__GADGET_PARAM_${counter}__`;
869
- const regex = new RegExp(`^${delimiter}\\s*$`);
870
- const isUsed = lines.some((line) => regex.test(line));
871
- if (!isUsed) {
872
- return delimiter;
873
- }
874
- counter++;
875
- }
876
- return "HEREDOC_FALLBACK";
877
- }
878
- function formatYamlValue(value, indent = "") {
879
- if (typeof value === "string") {
880
- const lines = value.split("\n");
881
- if (lines.length === 1 && !value.includes(":") && !value.startsWith("-")) {
882
- return value;
883
- }
884
- const delimiter = findSafeDelimiter(value);
885
- return `<<<${delimiter}
886
- ${value}
887
- ${delimiter}`;
888
- }
889
- if (typeof value === "number" || typeof value === "boolean") {
890
- return String(value);
891
- }
892
- if (value === null || value === void 0) {
893
- return "null";
894
- }
895
- if (Array.isArray(value)) {
896
- if (value.length === 0) return "[]";
897
- const items = value.map((item) => `${indent}- ${formatYamlValue(item, indent + " ")}`);
898
- return "\n" + items.join("\n");
899
- }
900
- if (typeof value === "object") {
901
- const entries = Object.entries(value);
902
- if (entries.length === 0) return "{}";
903
- const lines = entries.map(([k, v]) => {
904
- const formattedValue = formatYamlValue(v, indent + " ");
905
- if (formattedValue.startsWith("\n") || formattedValue.startsWith("|")) {
906
- return `${indent}${k}: ${formattedValue}`;
907
- }
908
- return `${indent}${k}: ${formattedValue}`;
909
- });
910
- return "\n" + lines.join("\n");
911
- }
912
- return yaml.dump(value).trimEnd();
913
- }
914
- function formatParamsAsYaml(params) {
810
+ function formatParamsAsBlock(params, prefix = "", argPrefix = GADGET_ARG_PREFIX) {
915
811
  const lines = [];
916
812
  for (const [key, value] of Object.entries(params)) {
917
- const formattedValue = formatYamlValue(value, "");
918
- if (formattedValue.startsWith("\n")) {
919
- lines.push(`${key}:${formattedValue}`);
813
+ const fullPath = prefix ? `${prefix}/${key}` : key;
814
+ if (Array.isArray(value)) {
815
+ value.forEach((item, index) => {
816
+ const itemPath = `${fullPath}/${index}`;
817
+ if (typeof item === "object" && item !== null) {
818
+ lines.push(formatParamsAsBlock(item, itemPath, argPrefix));
819
+ } else {
820
+ lines.push(`${argPrefix}${itemPath}`);
821
+ lines.push(String(item));
822
+ }
823
+ });
824
+ } else if (typeof value === "object" && value !== null) {
825
+ lines.push(formatParamsAsBlock(value, fullPath, argPrefix));
920
826
  } else {
921
- lines.push(`${key}: ${formattedValue}`);
827
+ lines.push(`${argPrefix}${fullPath}`);
828
+ lines.push(String(value));
922
829
  }
923
830
  }
924
831
  return lines.join("\n");
925
832
  }
926
- function formatTomlInlineTable(obj) {
927
- const entries = Object.entries(obj).map(([k, v]) => `${k} = ${formatTomlValue(v)}`);
928
- return `{ ${entries.join(", ")} }`;
929
- }
930
- function formatTomlValue(value) {
931
- if (typeof value === "string") {
932
- if (value.includes("\n")) {
933
- const delimiter = findSafeDelimiter(value);
934
- return `<<<${delimiter}
935
- ${value}
936
- ${delimiter}`;
937
- }
938
- return JSON.stringify(value);
939
- }
940
- if (typeof value === "number" || typeof value === "boolean") {
941
- return String(value);
942
- }
943
- if (value === null || value === void 0) {
944
- return '""';
945
- }
946
- if (Array.isArray(value)) {
947
- if (value.length === 0) return "[]";
948
- const items = value.map((item) => {
949
- if (typeof item === "object" && item !== null && !Array.isArray(item)) {
950
- return formatTomlInlineTable(item);
951
- }
952
- return formatTomlValue(item);
953
- });
954
- return `[${items.join(", ")}]`;
955
- }
956
- if (typeof value === "object") {
957
- return formatTomlInlineTable(value);
958
- }
959
- return JSON.stringify(value);
960
- }
961
- function formatParamsAsToml(params) {
833
+ function formatSchemaAsPlainText(schema, indent = "") {
962
834
  const lines = [];
963
- for (const [key, value] of Object.entries(params)) {
964
- lines.push(`${key} = ${formatTomlValue(value)}`);
835
+ const properties = schema.properties || {};
836
+ const required = schema.required || [];
837
+ for (const [key, prop] of Object.entries(properties)) {
838
+ const propObj = prop;
839
+ const type = propObj.type;
840
+ const description = propObj.description;
841
+ const isRequired = required.includes(key);
842
+ const enumValues = propObj.enum;
843
+ let line = `${indent}- ${key}`;
844
+ if (type === "array") {
845
+ const items = propObj.items;
846
+ const itemType = items?.type || "any";
847
+ line += ` (array of ${itemType})`;
848
+ } else if (type === "object" && propObj.properties) {
849
+ line += " (object)";
850
+ } else {
851
+ line += ` (${type})`;
852
+ }
853
+ if (isRequired) {
854
+ line += " [required]";
855
+ }
856
+ if (description) {
857
+ line += `: ${description}`;
858
+ }
859
+ if (enumValues) {
860
+ line += ` - one of: ${enumValues.map((v) => `"${v}"`).join(", ")}`;
861
+ }
862
+ lines.push(line);
863
+ if (type === "object" && propObj.properties) {
864
+ lines.push(formatSchemaAsPlainText(propObj, indent + " "));
865
+ }
965
866
  }
966
867
  return lines.join("\n");
967
868
  }
968
- var yaml, HEREDOC_DELIMITERS, BaseGadget;
869
+ var BaseGadget;
969
870
  var init_gadget = __esm({
970
871
  "src/gadgets/gadget.ts"() {
971
872
  "use strict";
972
- yaml = __toESM(require("js-yaml"), 1);
873
+ init_constants();
973
874
  init_schema_to_json();
974
875
  init_schema_validator();
975
- HEREDOC_DELIMITERS = [
976
- "__GADGET_PARAM_EOF__",
977
- "__GADGET_PARAM_END__",
978
- "__GADGET_PARAM_DOC__",
979
- "__GADGET_PARAM_CONTENT__",
980
- "__GADGET_PARAM_TEXT__",
981
- "__GADGET_PARAM_HEREDOC__",
982
- "__GADGET_PARAM_DATA__",
983
- "__GADGET_PARAM_BLOCK__"
984
- ];
985
876
  BaseGadget = class {
986
877
  /**
987
878
  * The name of the gadget. Used for identification when LLM calls it.
@@ -1012,19 +903,19 @@ var init_gadget = __esm({
1012
903
  /**
1013
904
  * Auto-generated instruction text for the LLM.
1014
905
  * Combines name, description, and parameter schema into a formatted instruction.
1015
- * @deprecated Use getInstruction(format) instead for format-specific schemas
906
+ * @deprecated Use getInstruction() instead
1016
907
  */
1017
908
  get instruction() {
1018
- return this.getInstruction("yaml");
909
+ return this.getInstruction();
1019
910
  }
1020
911
  /**
1021
- * Generate instruction text for the LLM with format-specific schema.
912
+ * Generate instruction text for the LLM.
1022
913
  * Combines name, description, and parameter schema into a formatted instruction.
1023
914
  *
1024
- * @param format - Format for the schema representation ('json' | 'yaml' | 'toml' | 'auto')
915
+ * @param argPrefix - Optional custom argument prefix for block format examples
1025
916
  * @returns Formatted instruction string
1026
917
  */
1027
- getInstruction(format = "json") {
918
+ getInstruction(argPrefix) {
1028
919
  const parts = [];
1029
920
  parts.push(this.description);
1030
921
  if (this.parameterSchema) {
@@ -1033,20 +924,12 @@ var init_gadget = __esm({
1033
924
  const jsonSchema = schemaToJSONSchema(this.parameterSchema, {
1034
925
  target: "draft-7"
1035
926
  });
1036
- if (format === "json" || format === "auto") {
1037
- parts.push("\n\nInput Schema (JSON):");
1038
- parts.push(JSON.stringify(jsonSchema, null, 2));
1039
- } else if (format === "toml") {
1040
- parts.push("\n\nInput Schema (TOML):");
1041
- parts.push(JSON.stringify(jsonSchema, null, 2));
1042
- } else {
1043
- const yamlSchema = yaml.dump(jsonSchema).trimEnd();
1044
- parts.push("\n\nInput Schema (YAML):");
1045
- parts.push(yamlSchema);
1046
- }
927
+ parts.push("\n\nParameters:");
928
+ parts.push(formatSchemaAsPlainText(jsonSchema));
1047
929
  }
1048
930
  if (this.examples && this.examples.length > 0) {
1049
931
  parts.push("\n\nExamples:");
932
+ const effectiveArgPrefix = argPrefix ?? GADGET_ARG_PREFIX;
1050
933
  this.examples.forEach((example, index) => {
1051
934
  if (index > 0) {
1052
935
  parts.push("");
@@ -1055,13 +938,7 @@ var init_gadget = __esm({
1055
938
  parts.push(`# ${example.comment}`);
1056
939
  }
1057
940
  parts.push("Input:");
1058
- if (format === "json" || format === "auto") {
1059
- parts.push(JSON.stringify(example.params, null, 2));
1060
- } else if (format === "toml") {
1061
- parts.push(formatParamsAsToml(example.params));
1062
- } else {
1063
- parts.push(formatParamsAsYaml(example.params));
1064
- }
941
+ parts.push(formatParamsAsBlock(example.params, "", effectiveArgPrefix));
1065
942
  if (example.output !== void 0) {
1066
943
  parts.push("Output:");
1067
944
  parts.push(example.output);
@@ -1338,14 +1215,12 @@ var init_conversation_manager = __esm({
1338
1215
  baseMessages;
1339
1216
  initialMessages;
1340
1217
  historyBuilder;
1341
- parameterFormat;
1342
1218
  constructor(baseMessages, initialMessages, options = {}) {
1343
1219
  this.baseMessages = baseMessages;
1344
1220
  this.initialMessages = initialMessages;
1345
- this.parameterFormat = options.parameterFormat ?? "json";
1346
1221
  this.historyBuilder = new LLMMessageBuilder();
1347
1222
  if (options.startPrefix && options.endPrefix) {
1348
- this.historyBuilder.withPrefixes(options.startPrefix, options.endPrefix);
1223
+ this.historyBuilder.withPrefixes(options.startPrefix, options.endPrefix, options.argPrefix);
1349
1224
  }
1350
1225
  }
1351
1226
  addUserMessage(content) {
@@ -1355,7 +1230,7 @@ var init_conversation_manager = __esm({
1355
1230
  this.historyBuilder.addAssistant(content);
1356
1231
  }
1357
1232
  addGadgetCall(gadgetName, parameters, result) {
1358
- this.historyBuilder.addGadgetCall(gadgetName, parameters, result, this.parameterFormat);
1233
+ this.historyBuilder.addGadgetCall(gadgetName, parameters, result);
1359
1234
  }
1360
1235
  getMessages() {
1361
1236
  return [...this.baseMessages, ...this.initialMessages, ...this.historyBuilder.build()];
@@ -1378,7 +1253,7 @@ async function runWithHandlers(agentGenerator, handlers) {
1378
1253
  await handlers.onGadgetCall({
1379
1254
  gadgetName: event.call.gadgetName,
1380
1255
  parameters: event.call.parameters,
1381
- parametersYaml: event.call.parametersYaml
1256
+ parametersRaw: event.call.parametersRaw
1382
1257
  });
1383
1258
  }
1384
1259
  break;
@@ -1655,7 +1530,7 @@ var init_executor = __esm({
1655
1530
  this.logger.error("Gadget parameter parse error", {
1656
1531
  gadgetName: call.gadgetName,
1657
1532
  parseError: call.parseError,
1658
- rawParameters: call.parametersYaml
1533
+ rawParameters: call.parametersRaw
1659
1534
  });
1660
1535
  return {
1661
1536
  gadgetName: call.gadgetName,
@@ -1817,168 +1692,107 @@ var init_executor = __esm({
1817
1692
  }
1818
1693
  });
1819
1694
 
1820
- // src/gadgets/parser.ts
1821
- function preprocessYaml(yamlStr) {
1822
- const lines = yamlStr.split("\n");
1823
- const result = [];
1824
- let i = 0;
1825
- while (i < lines.length) {
1826
- const line = lines[i];
1827
- const heredocMatch = line.match(/^(\s*)([\w-]+):\s*<<<([A-Za-z_][A-Za-z0-9_]*)\s*$/);
1828
- if (heredocMatch) {
1829
- const [, indent, key, delimiter] = heredocMatch;
1830
- const bodyLines = [];
1831
- i++;
1832
- const closingRegex = new RegExp(`^${delimiter}\\s*$`);
1833
- while (i < lines.length && !closingRegex.test(lines[i])) {
1834
- bodyLines.push(lines[i]);
1835
- i++;
1836
- }
1837
- if (i < lines.length) {
1838
- i++;
1839
- }
1840
- result.push(`${indent}${key}: |`);
1841
- for (const bodyLine of bodyLines) {
1842
- result.push(`${indent} ${bodyLine}`);
1695
+ // src/gadgets/block-params.ts
1696
+ function parseBlockParams(content, options) {
1697
+ const argPrefix = options?.argPrefix ?? GADGET_ARG_PREFIX;
1698
+ const result = {};
1699
+ const seenPointers = /* @__PURE__ */ new Set();
1700
+ const parts = content.split(argPrefix);
1701
+ for (let i = 1; i < parts.length; i++) {
1702
+ const part = parts[i];
1703
+ const newlineIndex = part.indexOf("\n");
1704
+ if (newlineIndex === -1) {
1705
+ const pointer2 = part.trim();
1706
+ if (pointer2) {
1707
+ if (seenPointers.has(pointer2)) {
1708
+ throw new Error(`Duplicate pointer: ${pointer2}`);
1709
+ }
1710
+ seenPointers.add(pointer2);
1711
+ setByPointer(result, pointer2, "");
1843
1712
  }
1844
1713
  continue;
1845
1714
  }
1846
- const match = line.match(/^(\s*)([\w-]+):\s+(.+)$/);
1847
- if (match) {
1848
- const [, indent, key, value] = match;
1849
- if (value === "|" || value === ">" || value === "|-" || value === ">-") {
1850
- result.push(line);
1851
- i++;
1852
- const keyIndentLen2 = indent.length;
1853
- const blockLines = [];
1854
- let minContentIndent = Infinity;
1855
- while (i < lines.length) {
1856
- const blockLine = lines[i];
1857
- const blockIndentMatch = blockLine.match(/^(\s*)/);
1858
- const blockIndentLen = blockIndentMatch ? blockIndentMatch[1].length : 0;
1859
- if (blockLine.trim() === "") {
1860
- blockLines.push({ content: "", originalIndent: 0 });
1861
- i++;
1862
- continue;
1863
- }
1864
- if (blockIndentLen > keyIndentLen2) {
1865
- const content = blockLine.substring(blockIndentLen);
1866
- blockLines.push({ content, originalIndent: blockIndentLen });
1867
- if (content.trim().length > 0) {
1868
- minContentIndent = Math.min(minContentIndent, blockIndentLen);
1869
- }
1870
- i++;
1871
- } else {
1872
- break;
1873
- }
1874
- }
1875
- const targetIndent = keyIndentLen2 + 2;
1876
- for (const blockLine of blockLines) {
1877
- if (blockLine.content === "") {
1878
- result.push("");
1879
- } else {
1880
- result.push(" ".repeat(targetIndent) + blockLine.content);
1881
- }
1882
- }
1883
- continue;
1884
- }
1885
- if (value.startsWith('"') || value.startsWith("'") || value === "true" || value === "false" || /^-?\d+(\.\d+)?$/.test(value)) {
1886
- result.push(line);
1887
- i++;
1888
- continue;
1889
- }
1890
- const keyIndentLen = indent.length;
1891
- const continuationLines = [];
1892
- let j = i + 1;
1893
- while (j < lines.length) {
1894
- const nextLine = lines[j];
1895
- if (nextLine.trim() === "") {
1896
- continuationLines.push(nextLine);
1897
- j++;
1898
- continue;
1899
- }
1900
- const nextIndentMatch = nextLine.match(/^(\s*)/);
1901
- const nextIndentLen = nextIndentMatch ? nextIndentMatch[1].length : 0;
1902
- if (nextIndentLen > keyIndentLen) {
1903
- continuationLines.push(nextLine);
1904
- j++;
1905
- } else {
1906
- break;
1907
- }
1908
- }
1909
- if (continuationLines.length > 0 && continuationLines.some((l) => l.trim().length > 0)) {
1910
- result.push(`${indent}${key}: |`);
1911
- result.push(`${indent} ${value}`);
1912
- for (const contLine of continuationLines) {
1913
- if (contLine.trim() === "") {
1914
- result.push("");
1915
- } else {
1916
- const contIndentMatch = contLine.match(/^(\s*)/);
1917
- const contIndent = contIndentMatch ? contIndentMatch[1] : "";
1918
- const contContent = contLine.substring(contIndent.length);
1919
- result.push(`${indent} ${contContent}`);
1920
- }
1921
- }
1922
- i = j;
1923
- continue;
1924
- }
1925
- if (value.includes(": ") || value.endsWith(":")) {
1926
- const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
1927
- result.push(`${indent}${key}: "${escaped}"`);
1928
- i++;
1929
- continue;
1930
- }
1715
+ const pointer = part.substring(0, newlineIndex).trim();
1716
+ let value = part.substring(newlineIndex + 1);
1717
+ if (value.endsWith("\n")) {
1718
+ value = value.slice(0, -1);
1719
+ }
1720
+ if (!pointer) {
1721
+ continue;
1722
+ }
1723
+ if (seenPointers.has(pointer)) {
1724
+ throw new Error(`Duplicate pointer: ${pointer}`);
1931
1725
  }
1932
- result.push(line);
1933
- i++;
1726
+ seenPointers.add(pointer);
1727
+ setByPointer(result, pointer, value);
1934
1728
  }
1935
- return result.join("\n");
1729
+ return result;
1936
1730
  }
1937
- function unescapeHeredocContent(content) {
1938
- return content.replace(/\\`/g, "`").replace(/\\\$/g, "$").replace(/\\{/g, "{").replace(/\\}/g, "}");
1731
+ function coerceValue(value) {
1732
+ if (value.includes("\n")) {
1733
+ return value;
1734
+ }
1735
+ const trimmed = value.trim();
1736
+ if (trimmed === "true") return true;
1737
+ if (trimmed === "false") return false;
1738
+ if (trimmed !== "" && /^-?\d+(\.\d+)?$/.test(trimmed)) {
1739
+ const num = Number(trimmed);
1740
+ if (!isNaN(num) && isFinite(num)) {
1741
+ return num;
1742
+ }
1743
+ }
1744
+ return value;
1939
1745
  }
1940
- function preprocessTomlHeredoc(tomlStr) {
1941
- const lines = tomlStr.split("\n");
1942
- const result = [];
1943
- let i = 0;
1944
- const heredocStartRegex = /^(\s*)([\w-]+)\s*=\s*<<<([A-Za-z_][A-Za-z0-9_]*)\s*$/;
1945
- while (i < lines.length) {
1946
- const line = lines[i];
1947
- const match = line.match(heredocStartRegex);
1948
- if (match) {
1949
- const [, indent, key, delimiter] = match;
1950
- const bodyLines = [];
1951
- i++;
1952
- const closingRegex = new RegExp(`^${delimiter}\\s*$`);
1953
- let foundClosing = false;
1954
- while (i < lines.length) {
1955
- const bodyLine = lines[i];
1956
- if (closingRegex.test(bodyLine)) {
1957
- foundClosing = true;
1958
- i++;
1959
- break;
1960
- }
1961
- bodyLines.push(bodyLine);
1962
- i++;
1963
- }
1964
- if (bodyLines.length === 0) {
1965
- result.push(`${indent}${key} = ''''''`);
1966
- } else {
1967
- result.push(`${indent}${key} = '''`);
1968
- for (let j = 0; j < bodyLines.length - 1; j++) {
1969
- result.push(unescapeHeredocContent(bodyLines[j]));
1970
- }
1971
- result.push(`${unescapeHeredocContent(bodyLines[bodyLines.length - 1])}'''`);
1972
- }
1973
- if (!foundClosing) {
1746
+ function setByPointer(obj, pointer, value) {
1747
+ const segments = pointer.split("/");
1748
+ let current = obj;
1749
+ for (let i = 0; i < segments.length - 1; i++) {
1750
+ const segment = segments[i];
1751
+ const nextSegment = segments[i + 1];
1752
+ const nextIsArrayIndex = /^\d+$/.test(nextSegment);
1753
+ if (Array.isArray(current)) {
1754
+ const index = parseInt(segment, 10);
1755
+ if (isNaN(index) || index < 0) {
1756
+ throw new Error(`Invalid array index: ${segment}`);
1757
+ }
1758
+ if (index > current.length) {
1759
+ throw new Error(`Array index gap: expected ${current.length}, got ${index}`);
1760
+ }
1761
+ if (current[index] === void 0) {
1762
+ current[index] = nextIsArrayIndex ? [] : {};
1763
+ }
1764
+ current = current[index];
1765
+ } else {
1766
+ const rec = current;
1767
+ if (rec[segment] === void 0) {
1768
+ rec[segment] = nextIsArrayIndex ? [] : {};
1974
1769
  }
1975
- continue;
1770
+ current = rec[segment];
1771
+ }
1772
+ }
1773
+ const lastSegment = segments[segments.length - 1];
1774
+ const coercedValue = coerceValue(value);
1775
+ if (Array.isArray(current)) {
1776
+ const index = parseInt(lastSegment, 10);
1777
+ if (isNaN(index) || index < 0) {
1778
+ throw new Error(`Invalid array index: ${lastSegment}`);
1779
+ }
1780
+ if (index > current.length) {
1781
+ throw new Error(`Array index gap: expected ${current.length}, got ${index}`);
1976
1782
  }
1977
- result.push(line);
1978
- i++;
1783
+ current[index] = coercedValue;
1784
+ } else {
1785
+ current[lastSegment] = coercedValue;
1979
1786
  }
1980
- return result.join("\n");
1981
1787
  }
1788
+ var init_block_params = __esm({
1789
+ "src/gadgets/block-params.ts"() {
1790
+ "use strict";
1791
+ init_constants();
1792
+ }
1793
+ });
1794
+
1795
+ // src/gadgets/parser.ts
1982
1796
  function stripMarkdownFences(content) {
1983
1797
  let cleaned = content.trim();
1984
1798
  const openingFence = /^```(?:toml|yaml|json)?\s*\n/i;
@@ -1987,24 +1801,23 @@ function stripMarkdownFences(content) {
1987
1801
  cleaned = cleaned.replace(closingFence, "");
1988
1802
  return cleaned.trim();
1989
1803
  }
1990
- var yaml2, import_js_toml, globalInvocationCounter, StreamParser;
1804
+ var globalInvocationCounter, StreamParser;
1991
1805
  var init_parser = __esm({
1992
1806
  "src/gadgets/parser.ts"() {
1993
1807
  "use strict";
1994
- yaml2 = __toESM(require("js-yaml"), 1);
1995
- import_js_toml = require("js-toml");
1996
1808
  init_constants();
1809
+ init_block_params();
1997
1810
  globalInvocationCounter = 0;
1998
1811
  StreamParser = class {
1999
1812
  buffer = "";
2000
1813
  lastReportedTextLength = 0;
2001
1814
  startPrefix;
2002
1815
  endPrefix;
2003
- parameterFormat;
1816
+ argPrefix;
2004
1817
  constructor(options = {}) {
2005
1818
  this.startPrefix = options.startPrefix ?? GADGET_START_PREFIX;
2006
1819
  this.endPrefix = options.endPrefix ?? GADGET_END_PREFIX;
2007
- this.parameterFormat = options.parameterFormat ?? "json";
1820
+ this.argPrefix = options.argPrefix ?? GADGET_ARG_PREFIX;
2008
1821
  }
2009
1822
  takeTextUntil(index) {
2010
1823
  if (index <= this.lastReportedTextLength) {
@@ -2039,43 +1852,14 @@ var init_parser = __esm({
2039
1852
  return `${firstLine.slice(0, maxLen)}... (${message.length} chars total)`;
2040
1853
  }
2041
1854
  /**
2042
- * Parse parameter string according to configured format
1855
+ * Parse parameter string using block format
2043
1856
  */
2044
1857
  parseParameters(raw) {
2045
1858
  const cleaned = stripMarkdownFences(raw);
2046
- if (this.parameterFormat === "json") {
2047
- try {
2048
- return { parameters: JSON.parse(cleaned) };
2049
- } catch (error) {
2050
- return { parseError: this.truncateParseError(error, "JSON") };
2051
- }
2052
- }
2053
- if (this.parameterFormat === "yaml") {
2054
- try {
2055
- return { parameters: yaml2.load(preprocessYaml(cleaned)) };
2056
- } catch (error) {
2057
- return { parseError: this.truncateParseError(error, "YAML") };
2058
- }
2059
- }
2060
- if (this.parameterFormat === "toml") {
2061
- try {
2062
- return { parameters: (0, import_js_toml.load)(preprocessTomlHeredoc(cleaned)) };
2063
- } catch (error) {
2064
- return { parseError: this.truncateParseError(error, "TOML") };
2065
- }
2066
- }
2067
1859
  try {
2068
- return { parameters: JSON.parse(cleaned) };
2069
- } catch {
2070
- try {
2071
- return { parameters: (0, import_js_toml.load)(preprocessTomlHeredoc(cleaned)) };
2072
- } catch {
2073
- try {
2074
- return { parameters: yaml2.load(preprocessYaml(cleaned)) };
2075
- } catch (error) {
2076
- return { parseError: this.truncateParseError(error, "auto") };
2077
- }
2078
- }
1860
+ return { parameters: parseBlockParams(cleaned, { argPrefix: this.argPrefix }) };
1861
+ } catch (error) {
1862
+ return { parseError: this.truncateParseError(error, "block") };
2079
1863
  }
2080
1864
  }
2081
1865
  // Feed a chunk of text and get parsed events
@@ -2134,8 +1918,7 @@ var init_parser = __esm({
2134
1918
  call: {
2135
1919
  gadgetName: actualGadgetName,
2136
1920
  invocationId,
2137
- parametersYaml: parametersRaw,
2138
- // Keep property name for backward compatibility
1921
+ parametersRaw,
2139
1922
  parameters,
2140
1923
  parseError
2141
1924
  }
@@ -2169,7 +1952,7 @@ var init_parser = __esm({
2169
1952
  call: {
2170
1953
  gadgetName: actualGadgetName,
2171
1954
  invocationId,
2172
- parametersYaml: parametersRaw,
1955
+ parametersRaw,
2173
1956
  parameters,
2174
1957
  parseError
2175
1958
  }
@@ -2220,9 +2003,9 @@ var init_stream_processor = __esm({
2220
2003
  this.stopOnGadgetError = options.stopOnGadgetError ?? true;
2221
2004
  this.shouldContinueAfterError = options.shouldContinueAfterError;
2222
2005
  this.parser = new StreamParser({
2223
- parameterFormat: options.parameterFormat,
2224
2006
  startPrefix: options.gadgetStartPrefix,
2225
- endPrefix: options.gadgetEndPrefix
2007
+ endPrefix: options.gadgetEndPrefix,
2008
+ argPrefix: options.gadgetArgPrefix
2226
2009
  });
2227
2010
  this.executor = new GadgetExecutor(
2228
2011
  options.registry,
@@ -2376,7 +2159,7 @@ var init_stream_processor = __esm({
2376
2159
  this.logger.warn("Gadget has parse error", {
2377
2160
  gadgetName: call.gadgetName,
2378
2161
  error: call.parseError,
2379
- rawParameters: call.parametersYaml
2162
+ rawParameters: call.parametersRaw
2380
2163
  });
2381
2164
  const shouldContinue = await this.checkContinueAfterError(
2382
2165
  call.parseError,
@@ -2612,9 +2395,9 @@ var init_agent = __esm({
2612
2395
  hooks;
2613
2396
  conversation;
2614
2397
  registry;
2615
- parameterFormat;
2616
2398
  gadgetStartPrefix;
2617
2399
  gadgetEndPrefix;
2400
+ gadgetArgPrefix;
2618
2401
  onHumanInputRequired;
2619
2402
  textOnlyHandler;
2620
2403
  textWithGadgetsHandler;
@@ -2643,9 +2426,9 @@ var init_agent = __esm({
2643
2426
  this.temperature = options.temperature;
2644
2427
  this.logger = options.logger ?? createLogger({ name: "llmist:agent" });
2645
2428
  this.registry = options.registry;
2646
- this.parameterFormat = options.parameterFormat ?? "json";
2647
2429
  this.gadgetStartPrefix = options.gadgetStartPrefix;
2648
2430
  this.gadgetEndPrefix = options.gadgetEndPrefix;
2431
+ this.gadgetArgPrefix = options.gadgetArgPrefix;
2649
2432
  this.onHumanInputRequired = options.onHumanInputRequired;
2650
2433
  this.textOnlyHandler = options.textOnlyHandler ?? "terminate";
2651
2434
  this.textWithGadgetsHandler = options.textWithGadgetsHandler;
@@ -2667,9 +2450,10 @@ var init_agent = __esm({
2667
2450
  if (options.systemPrompt) {
2668
2451
  baseBuilder.addSystem(options.systemPrompt);
2669
2452
  }
2670
- baseBuilder.addGadgets(this.registry.getAll(), this.parameterFormat, {
2453
+ baseBuilder.addGadgets(this.registry.getAll(), {
2671
2454
  startPrefix: options.gadgetStartPrefix,
2672
- endPrefix: options.gadgetEndPrefix
2455
+ endPrefix: options.gadgetEndPrefix,
2456
+ argPrefix: options.gadgetArgPrefix
2673
2457
  });
2674
2458
  const baseMessages = baseBuilder.build();
2675
2459
  const initialMessages = (options.initialMessages ?? []).map((message) => ({
@@ -2677,9 +2461,9 @@ var init_agent = __esm({
2677
2461
  content: message.content
2678
2462
  }));
2679
2463
  this.conversation = new ConversationManager(baseMessages, initialMessages, {
2680
- parameterFormat: this.parameterFormat,
2681
2464
  startPrefix: options.gadgetStartPrefix,
2682
- endPrefix: options.gadgetEndPrefix
2465
+ endPrefix: options.gadgetEndPrefix,
2466
+ argPrefix: options.gadgetArgPrefix
2683
2467
  });
2684
2468
  this.userPromptProvided = !!options.userPrompt;
2685
2469
  if (options.userPrompt) {
@@ -2772,9 +2556,9 @@ var init_agent = __esm({
2772
2556
  const processor = new StreamProcessor({
2773
2557
  iteration: currentIteration,
2774
2558
  registry: this.registry,
2775
- parameterFormat: this.parameterFormat,
2776
2559
  gadgetStartPrefix: this.gadgetStartPrefix,
2777
2560
  gadgetEndPrefix: this.gadgetEndPrefix,
2561
+ gadgetArgPrefix: this.gadgetArgPrefix,
2778
2562
  hooks: this.hooks,
2779
2563
  logger: this.logger.getSubLogger({ name: "stream-processor" }),
2780
2564
  onHumanInputRequired: this.onHumanInputRequired,
@@ -5040,9 +4824,9 @@ var init_builder = __esm({
5040
4824
  gadgets = [];
5041
4825
  initialMessages = [];
5042
4826
  onHumanInputRequired;
5043
- parameterFormat;
5044
4827
  gadgetStartPrefix;
5045
4828
  gadgetEndPrefix;
4829
+ gadgetArgPrefix;
5046
4830
  textOnlyHandler;
5047
4831
  textWithGadgetsHandler;
5048
4832
  stopOnGadgetError;
@@ -5229,21 +5013,6 @@ var init_builder = __esm({
5229
5013
  this.onHumanInputRequired = handler;
5230
5014
  return this;
5231
5015
  }
5232
- /**
5233
- * Set the parameter format for gadget calls.
5234
- *
5235
- * @param format - Parameter format ("json" or "xml")
5236
- * @returns This builder for chaining
5237
- *
5238
- * @example
5239
- * ```typescript
5240
- * .withParameterFormat("xml")
5241
- * ```
5242
- */
5243
- withParameterFormat(format) {
5244
- this.parameterFormat = format;
5245
- return this;
5246
- }
5247
5016
  /**
5248
5017
  * Set custom gadget marker prefix.
5249
5018
  *
@@ -5274,6 +5043,21 @@ var init_builder = __esm({
5274
5043
  this.gadgetEndPrefix = suffix;
5275
5044
  return this;
5276
5045
  }
5046
+ /**
5047
+ * Set custom argument prefix for block format parameters.
5048
+ *
5049
+ * @param prefix - Custom prefix for argument markers (default: "!!!ARG:")
5050
+ * @returns This builder for chaining
5051
+ *
5052
+ * @example
5053
+ * ```typescript
5054
+ * .withGadgetArgPrefix("<<ARG>>")
5055
+ * ```
5056
+ */
5057
+ withGadgetArgPrefix(prefix) {
5058
+ this.gadgetArgPrefix = prefix;
5059
+ return this;
5060
+ }
5277
5061
  /**
5278
5062
  * Set the text-only handler strategy.
5279
5063
  *
@@ -5473,8 +5257,7 @@ var init_builder = __esm({
5473
5257
  withSyntheticGadgetCall(gadgetName, parameters, result) {
5474
5258
  const startPrefix = this.gadgetStartPrefix ?? GADGET_START_PREFIX;
5475
5259
  const endPrefix = this.gadgetEndPrefix ?? GADGET_END_PREFIX;
5476
- const format = this.parameterFormat ?? "yaml";
5477
- const paramStr = this.formatSyntheticParameters(parameters, format);
5260
+ const paramStr = this.formatBlockParameters(parameters, "");
5478
5261
  this.initialMessages.push({
5479
5262
  role: "assistant",
5480
5263
  content: `${startPrefix}${gadgetName}
@@ -5488,25 +5271,31 @@ ${endPrefix}`
5488
5271
  return this;
5489
5272
  }
5490
5273
  /**
5491
- * Format parameters for synthetic gadget calls.
5492
- * Uses heredoc for multiline string values.
5274
+ * Format parameters as block format with JSON Pointer paths.
5493
5275
  */
5494
- formatSyntheticParameters(parameters, format) {
5495
- if (format === "json" || format === "auto") {
5496
- return JSON.stringify(parameters);
5497
- }
5498
- return Object.entries(parameters).map(([key, value]) => {
5499
- if (typeof value === "string" && value.includes("\n")) {
5500
- const separator = format === "yaml" ? ":" : " =";
5501
- return `${key}${separator} <<<EOF
5502
- ${value}
5503
- EOF`;
5504
- }
5505
- if (format === "yaml") {
5506
- return typeof value === "string" ? `${key}: ${value}` : `${key}: ${JSON.stringify(value)}`;
5276
+ formatBlockParameters(params, prefix) {
5277
+ const lines = [];
5278
+ const argPrefix = this.gadgetArgPrefix ?? GADGET_ARG_PREFIX;
5279
+ for (const [key, value] of Object.entries(params)) {
5280
+ const fullPath = prefix ? `${prefix}/${key}` : key;
5281
+ if (Array.isArray(value)) {
5282
+ value.forEach((item, index) => {
5283
+ const itemPath = `${fullPath}/${index}`;
5284
+ if (typeof item === "object" && item !== null) {
5285
+ lines.push(this.formatBlockParameters(item, itemPath));
5286
+ } else {
5287
+ lines.push(`${argPrefix}${itemPath}`);
5288
+ lines.push(String(item));
5289
+ }
5290
+ });
5291
+ } else if (typeof value === "object" && value !== null) {
5292
+ lines.push(this.formatBlockParameters(value, fullPath));
5293
+ } else {
5294
+ lines.push(`${argPrefix}${fullPath}`);
5295
+ lines.push(String(value));
5507
5296
  }
5508
- return `${key} = ${JSON.stringify(value)}`;
5509
- }).join("\n");
5297
+ }
5298
+ return lines.join("\n");
5510
5299
  }
5511
5300
  /**
5512
5301
  * Build and create the agent with the given user prompt.
@@ -5546,9 +5335,9 @@ EOF`;
5546
5335
  promptConfig: this.promptConfig,
5547
5336
  initialMessages: this.initialMessages,
5548
5337
  onHumanInputRequired: this.onHumanInputRequired,
5549
- parameterFormat: this.parameterFormat,
5550
5338
  gadgetStartPrefix: this.gadgetStartPrefix,
5551
5339
  gadgetEndPrefix: this.gadgetEndPrefix,
5340
+ gadgetArgPrefix: this.gadgetArgPrefix,
5552
5341
  textOnlyHandler: this.textOnlyHandler,
5553
5342
  textWithGadgetsHandler: this.textWithGadgetsHandler,
5554
5343
  stopOnGadgetError: this.stopOnGadgetError,
@@ -5648,9 +5437,9 @@ EOF`;
5648
5437
  promptConfig: this.promptConfig,
5649
5438
  initialMessages: this.initialMessages,
5650
5439
  onHumanInputRequired: this.onHumanInputRequired,
5651
- parameterFormat: this.parameterFormat,
5652
5440
  gadgetStartPrefix: this.gadgetStartPrefix,
5653
5441
  gadgetEndPrefix: this.gadgetEndPrefix,
5442
+ gadgetArgPrefix: this.gadgetArgPrefix,
5654
5443
  textOnlyHandler: this.textOnlyHandler,
5655
5444
  textWithGadgetsHandler: this.textWithGadgetsHandler,
5656
5445
  stopOnGadgetError: this.stopOnGadgetError,
@@ -5675,7 +5464,6 @@ var COMMANDS = {
5675
5464
  };
5676
5465
  var LOG_LEVELS = ["silly", "trace", "debug", "info", "warn", "error", "fatal"];
5677
5466
  var DEFAULT_MODEL = "openai:gpt-5-nano";
5678
- var DEFAULT_PARAMETER_FORMAT = "toml";
5679
5467
  var OPTION_FLAGS = {
5680
5468
  model: "-m, --model <identifier>",
5681
5469
  systemPrompt: "-s, --system <prompt>",
@@ -5683,10 +5471,11 @@ var OPTION_FLAGS = {
5683
5471
  maxTokens: "--max-tokens <count>",
5684
5472
  maxIterations: "-i, --max-iterations <count>",
5685
5473
  gadgetModule: "-g, --gadget <module>",
5686
- parameterFormat: "--parameter-format <format>",
5687
5474
  logLevel: "--log-level <level>",
5688
5475
  logFile: "--log-file <path>",
5689
5476
  logReset: "--log-reset",
5477
+ logLlmRequests: "--log-llm-requests [dir]",
5478
+ logLlmResponses: "--log-llm-responses [dir]",
5690
5479
  noBuiltins: "--no-builtins",
5691
5480
  noBuiltinInteraction: "--no-builtin-interaction",
5692
5481
  quiet: "-q, --quiet"
@@ -5698,10 +5487,11 @@ var OPTION_DESCRIPTIONS = {
5698
5487
  maxTokens: "Maximum number of output tokens requested from the model.",
5699
5488
  maxIterations: "Maximum number of agent loop iterations before exiting.",
5700
5489
  gadgetModule: "Path or module specifier for a gadget export. Repeat to register multiple gadgets.",
5701
- parameterFormat: "Format for gadget parameter schemas: 'json', 'yaml', 'toml', or 'auto'.",
5702
5490
  logLevel: "Log level: silly, trace, debug, info, warn, error, fatal.",
5703
5491
  logFile: "Path to log file. When set, logs are written to file instead of stderr.",
5704
5492
  logReset: "Reset (truncate) the log file at session start instead of appending.",
5493
+ logLlmRequests: "Save raw LLM requests as plain text. Optional dir, defaults to ~/.llmist/logs/requests/",
5494
+ logLlmResponses: "Save raw LLM responses as plain text. Optional dir, defaults to ~/.llmist/logs/responses/",
5705
5495
  noBuiltins: "Disable built-in gadgets (AskUser, TellUser).",
5706
5496
  noBuiltinInteraction: "Disable interactive gadgets (AskUser) while keeping TellUser.",
5707
5497
  quiet: "Suppress all output except content (text and TellUser messages)."
@@ -5709,12 +5499,12 @@ var OPTION_DESCRIPTIONS = {
5709
5499
  var SUMMARY_PREFIX = "[llmist]";
5710
5500
 
5711
5501
  // src/cli/program.ts
5712
- var import_commander3 = require("commander");
5502
+ var import_commander2 = require("commander");
5713
5503
 
5714
5504
  // package.json
5715
5505
  var package_default = {
5716
5506
  name: "llmist",
5717
- version: "0.7.0",
5507
+ version: "0.8.0",
5718
5508
  description: "Universal TypeScript LLM client with streaming-first agent framework. Works with any model - no structured outputs or native tool calling required. Implements its own flexible grammar for function calling.",
5719
5509
  type: "module",
5720
5510
  main: "dist/index.cjs",
@@ -5827,7 +5617,7 @@ var package_default = {
5827
5617
  };
5828
5618
 
5829
5619
  // src/cli/agent-command.ts
5830
- var import_promises = require("readline/promises");
5620
+ var import_promises2 = require("readline/promises");
5831
5621
  var import_chalk3 = __toESM(require("chalk"), 1);
5832
5622
  init_builder();
5833
5623
  init_registry();
@@ -6016,8 +5806,33 @@ async function loadGadgets(specifiers, cwd, importer = (specifier) => import(spe
6016
5806
  return gadgets;
6017
5807
  }
6018
5808
 
6019
- // src/cli/option-helpers.ts
6020
- var import_commander2 = require("commander");
5809
+ // src/cli/llm-logging.ts
5810
+ var import_promises = require("fs/promises");
5811
+ var import_node_os = require("os");
5812
+ var import_node_path3 = require("path");
5813
+ var DEFAULT_LLM_LOG_DIR = (0, import_node_path3.join)((0, import_node_os.homedir)(), ".llmist", "logs");
5814
+ function resolveLogDir(option, subdir) {
5815
+ if (option === true) {
5816
+ return (0, import_node_path3.join)(DEFAULT_LLM_LOG_DIR, subdir);
5817
+ }
5818
+ if (typeof option === "string") {
5819
+ return option;
5820
+ }
5821
+ return void 0;
5822
+ }
5823
+ function formatLlmRequest(messages) {
5824
+ const lines = [];
5825
+ for (const msg of messages) {
5826
+ lines.push(`=== ${msg.role.toUpperCase()} ===`);
5827
+ lines.push(msg.content ?? "");
5828
+ lines.push("");
5829
+ }
5830
+ return lines.join("\n");
5831
+ }
5832
+ async function writeLogFile(dir, filename, content) {
5833
+ await (0, import_promises.mkdir)(dir, { recursive: true });
5834
+ await (0, import_promises.writeFile)((0, import_node_path3.join)(dir, filename), content, "utf-8");
5835
+ }
6021
5836
 
6022
5837
  // src/cli/utils.ts
6023
5838
  var import_chalk2 = __toESM(require("chalk"), 1);
@@ -6061,9 +5876,29 @@ function ensureMarkedConfigured() {
6061
5876
  }
6062
5877
  function renderMarkdown(text) {
6063
5878
  ensureMarkedConfigured();
6064
- const rendered = import_marked.marked.parse(text);
5879
+ let rendered = import_marked.marked.parse(text);
5880
+ rendered = rendered.replace(/\*\*(.+?)\*\*/g, (_, content) => import_chalk.default.bold(content)).replace(/(?<!\*)\*(\S[^*]*)\*(?!\*)/g, (_, content) => import_chalk.default.italic(content));
6065
5881
  return rendered.trimEnd();
6066
5882
  }
5883
+ function createRainbowSeparator() {
5884
+ const colors = [import_chalk.default.red, import_chalk.default.yellow, import_chalk.default.green, import_chalk.default.cyan, import_chalk.default.blue, import_chalk.default.magenta];
5885
+ const char = "\u2500";
5886
+ const width = process.stdout.columns || 80;
5887
+ let result = "";
5888
+ for (let i = 0; i < width; i++) {
5889
+ result += colors[i % colors.length](char);
5890
+ }
5891
+ return result;
5892
+ }
5893
+ function renderMarkdownWithSeparators(text) {
5894
+ const rendered = renderMarkdown(text);
5895
+ const separator = createRainbowSeparator();
5896
+ return `
5897
+ ${separator}
5898
+ ${rendered}
5899
+ ${separator}
5900
+ `;
5901
+ }
6067
5902
  function formatTokens(tokens) {
6068
5903
  return tokens >= 1e3 ? `${(tokens / 1e3).toFixed(1)}k` : `${tokens}`;
6069
5904
  }
@@ -6183,7 +6018,7 @@ function formatGadgetSummary(result) {
6183
6018
  const summaryLine = `${icon} ${gadgetLabel}${paramsLabel} ${import_chalk.default.dim("\u2192")} ${outputLabel} ${timeLabel}`;
6184
6019
  if (result.gadgetName === "TellUser" && result.parameters?.message) {
6185
6020
  const message = String(result.parameters.message);
6186
- const rendered = renderMarkdown(message);
6021
+ const rendered = renderMarkdownWithSeparators(message);
6187
6022
  return `${summaryLine}
6188
6023
  ${rendered}`;
6189
6024
  }
@@ -6490,7 +6325,7 @@ var StreamProgress = class {
6490
6325
  }
6491
6326
  this.isRunning = false;
6492
6327
  if (this.hasRendered) {
6493
- this.target.write("\r\x1B[K");
6328
+ this.target.write("\r\x1B[K\x1B[0G");
6494
6329
  this.hasRendered = false;
6495
6330
  }
6496
6331
  }
@@ -6583,16 +6418,6 @@ async function executeAction(action, env) {
6583
6418
  }
6584
6419
 
6585
6420
  // src/cli/option-helpers.ts
6586
- var PARAMETER_FORMAT_VALUES = ["json", "yaml", "toml", "auto"];
6587
- function parseParameterFormat(value) {
6588
- const normalized = value.toLowerCase();
6589
- if (!PARAMETER_FORMAT_VALUES.includes(normalized)) {
6590
- throw new import_commander2.InvalidArgumentError(
6591
- `Parameter format must be one of: ${PARAMETER_FORMAT_VALUES.join(", ")}`
6592
- );
6593
- }
6594
- return normalized;
6595
- }
6596
6421
  function addCompleteOptions(cmd, defaults) {
6597
6422
  return cmd.option(OPTION_FLAGS.model, OPTION_DESCRIPTIONS.model, defaults?.model ?? DEFAULT_MODEL).option(OPTION_FLAGS.systemPrompt, OPTION_DESCRIPTIONS.systemPrompt, defaults?.system).option(
6598
6423
  OPTION_FLAGS.temperature,
@@ -6604,7 +6429,7 @@ function addCompleteOptions(cmd, defaults) {
6604
6429
  OPTION_DESCRIPTIONS.maxTokens,
6605
6430
  createNumericParser({ label: "Max tokens", integer: true, min: 1 }),
6606
6431
  defaults?.["max-tokens"]
6607
- ).option(OPTION_FLAGS.quiet, OPTION_DESCRIPTIONS.quiet, defaults?.quiet);
6432
+ ).option(OPTION_FLAGS.quiet, OPTION_DESCRIPTIONS.quiet, defaults?.quiet).option(OPTION_FLAGS.logLlmRequests, OPTION_DESCRIPTIONS.logLlmRequests, defaults?.["log-llm-requests"]).option(OPTION_FLAGS.logLlmResponses, OPTION_DESCRIPTIONS.logLlmResponses, defaults?.["log-llm-responses"]);
6608
6433
  }
6609
6434
  function addAgentOptions(cmd, defaults) {
6610
6435
  const gadgetAccumulator = (value, previous = []) => [
@@ -6624,16 +6449,11 @@ function addAgentOptions(cmd, defaults) {
6624
6449
  defaults?.["max-iterations"]
6625
6450
  ).option(OPTION_FLAGS.gadgetModule, OPTION_DESCRIPTIONS.gadgetModule, gadgetAccumulator, [
6626
6451
  ...defaultGadgets
6627
- ]).option(
6628
- OPTION_FLAGS.parameterFormat,
6629
- OPTION_DESCRIPTIONS.parameterFormat,
6630
- parseParameterFormat,
6631
- defaults?.["parameter-format"] ?? DEFAULT_PARAMETER_FORMAT
6632
- ).option(OPTION_FLAGS.noBuiltins, OPTION_DESCRIPTIONS.noBuiltins, defaults?.builtins !== false).option(
6452
+ ]).option(OPTION_FLAGS.noBuiltins, OPTION_DESCRIPTIONS.noBuiltins, defaults?.builtins !== false).option(
6633
6453
  OPTION_FLAGS.noBuiltinInteraction,
6634
6454
  OPTION_DESCRIPTIONS.noBuiltinInteraction,
6635
6455
  defaults?.["builtin-interaction"] !== false
6636
- ).option(OPTION_FLAGS.quiet, OPTION_DESCRIPTIONS.quiet, defaults?.quiet);
6456
+ ).option(OPTION_FLAGS.quiet, OPTION_DESCRIPTIONS.quiet, defaults?.quiet).option(OPTION_FLAGS.logLlmRequests, OPTION_DESCRIPTIONS.logLlmRequests, defaults?.["log-llm-requests"]).option(OPTION_FLAGS.logLlmResponses, OPTION_DESCRIPTIONS.logLlmResponses, defaults?.["log-llm-responses"]);
6637
6457
  }
6638
6458
  function configToCompleteOptions(config) {
6639
6459
  const result = {};
@@ -6642,6 +6462,8 @@ function configToCompleteOptions(config) {
6642
6462
  if (config.temperature !== void 0) result.temperature = config.temperature;
6643
6463
  if (config["max-tokens"] !== void 0) result.maxTokens = config["max-tokens"];
6644
6464
  if (config.quiet !== void 0) result.quiet = config.quiet;
6465
+ if (config["log-llm-requests"] !== void 0) result.logLlmRequests = config["log-llm-requests"];
6466
+ if (config["log-llm-responses"] !== void 0) result.logLlmResponses = config["log-llm-responses"];
6645
6467
  return result;
6646
6468
  }
6647
6469
  function configToAgentOptions(config) {
@@ -6651,7 +6473,6 @@ function configToAgentOptions(config) {
6651
6473
  if (config.temperature !== void 0) result.temperature = config.temperature;
6652
6474
  if (config["max-iterations"] !== void 0) result.maxIterations = config["max-iterations"];
6653
6475
  if (config.gadget !== void 0) result.gadget = config.gadget;
6654
- if (config["parameter-format"] !== void 0) result.parameterFormat = config["parameter-format"];
6655
6476
  if (config.builtins !== void 0) result.builtins = config.builtins;
6656
6477
  if (config["builtin-interaction"] !== void 0)
6657
6478
  result.builtinInteraction = config["builtin-interaction"];
@@ -6659,13 +6480,17 @@ function configToAgentOptions(config) {
6659
6480
  result.gadgetStartPrefix = config["gadget-start-prefix"];
6660
6481
  if (config["gadget-end-prefix"] !== void 0)
6661
6482
  result.gadgetEndPrefix = config["gadget-end-prefix"];
6483
+ if (config["gadget-arg-prefix"] !== void 0)
6484
+ result.gadgetArgPrefix = config["gadget-arg-prefix"];
6662
6485
  if (config.quiet !== void 0) result.quiet = config.quiet;
6486
+ if (config["log-llm-requests"] !== void 0) result.logLlmRequests = config["log-llm-requests"];
6487
+ if (config["log-llm-responses"] !== void 0) result.logLlmResponses = config["log-llm-responses"];
6663
6488
  return result;
6664
6489
  }
6665
6490
 
6666
6491
  // src/cli/agent-command.ts
6667
6492
  async function promptApproval(env, prompt) {
6668
- const rl = (0, import_promises.createInterface)({ input: env.stdin, output: env.stderr });
6493
+ const rl = (0, import_promises2.createInterface)({ input: env.stdin, output: env.stderr });
6669
6494
  try {
6670
6495
  const answer = await rl.question(prompt);
6671
6496
  return answer.trim();
@@ -6680,10 +6505,10 @@ function createHumanInputHandler(env, progress) {
6680
6505
  }
6681
6506
  return async (question) => {
6682
6507
  progress.pause();
6683
- const rl = (0, import_promises.createInterface)({ input: env.stdin, output: env.stdout });
6508
+ const rl = (0, import_promises2.createInterface)({ input: env.stdin, output: env.stdout });
6684
6509
  try {
6685
6510
  const questionLine = question.trim() ? `
6686
- ${renderMarkdown(question.trim())}` : "";
6511
+ ${renderMarkdownWithSeparators(question.trim())}` : "";
6687
6512
  let isFirst = true;
6688
6513
  while (true) {
6689
6514
  const statsPrompt = progress.formatPrompt();
@@ -6726,6 +6551,9 @@ async function executeAgent(promptArg, options, env) {
6726
6551
  const progress = new StreamProgress(env.stderr, stderrTTY, client.modelRegistry);
6727
6552
  let usage;
6728
6553
  let iterations = 0;
6554
+ const llmRequestsDir = resolveLogDir(options.logLlmRequests, "requests");
6555
+ const llmResponsesDir = resolveLogDir(options.logLlmResponses, "responses");
6556
+ let llmCallCounter = 0;
6729
6557
  const countMessagesTokens = async (model, messages) => {
6730
6558
  try {
6731
6559
  return await client.countTokens(model, messages);
@@ -6748,12 +6576,18 @@ async function executeAgent(promptArg, options, env) {
6748
6576
  // onLLMCallStart: Start progress indicator for each LLM call
6749
6577
  // This showcases how to react to agent lifecycle events
6750
6578
  onLLMCallStart: async (context) => {
6579
+ llmCallCounter++;
6751
6580
  const inputTokens = await countMessagesTokens(
6752
6581
  context.options.model,
6753
6582
  context.options.messages
6754
6583
  );
6755
6584
  progress.startCall(context.options.model, inputTokens);
6756
6585
  progress.setInputTokens(inputTokens, false);
6586
+ if (llmRequestsDir) {
6587
+ const filename = `${Date.now()}_call_${llmCallCounter}.request.txt`;
6588
+ const content = formatLlmRequest(context.options.messages);
6589
+ await writeLogFile(llmRequestsDir, filename, content);
6590
+ }
6757
6591
  },
6758
6592
  // onStreamChunk: Real-time updates as LLM generates tokens
6759
6593
  // This enables responsive UIs that show progress during generation
@@ -6816,6 +6650,10 @@ async function executeAgent(promptArg, options, env) {
6816
6650
  `);
6817
6651
  }
6818
6652
  }
6653
+ if (llmResponsesDir) {
6654
+ const filename = `${Date.now()}_call_${llmCallCounter}.response.txt`;
6655
+ await writeLogFile(llmResponsesDir, filename, context.rawResponse);
6656
+ }
6819
6657
  }
6820
6658
  },
6821
6659
  // SHOWCASE: Controller-based approval gating for dangerous gadgets
@@ -6880,13 +6718,15 @@ Command rejected by user with message: "${response}"`
6880
6718
  if (gadgets.length > 0) {
6881
6719
  builder.withGadgets(...gadgets);
6882
6720
  }
6883
- builder.withParameterFormat(options.parameterFormat);
6884
6721
  if (options.gadgetStartPrefix) {
6885
6722
  builder.withGadgetStartPrefix(options.gadgetStartPrefix);
6886
6723
  }
6887
6724
  if (options.gadgetEndPrefix) {
6888
6725
  builder.withGadgetEndPrefix(options.gadgetEndPrefix);
6889
6726
  }
6727
+ if (options.gadgetArgPrefix) {
6728
+ builder.withGadgetArgPrefix(options.gadgetArgPrefix);
6729
+ }
6890
6730
  builder.withSyntheticGadgetCall(
6891
6731
  "TellUser",
6892
6732
  {
@@ -6903,17 +6743,25 @@ Command rejected by user with message: "${response}"`
6903
6743
  resultMapping: (text) => `\u2139\uFE0F ${text}`
6904
6744
  });
6905
6745
  const agent = builder.ask(prompt);
6746
+ let textBuffer = "";
6747
+ const flushTextBuffer = () => {
6748
+ if (textBuffer) {
6749
+ const output = options.quiet ? textBuffer : renderMarkdownWithSeparators(textBuffer);
6750
+ printer.write(output);
6751
+ textBuffer = "";
6752
+ }
6753
+ };
6906
6754
  for await (const event of agent.run()) {
6907
6755
  if (event.type === "text") {
6908
6756
  progress.pause();
6909
- printer.write(event.content);
6757
+ textBuffer += event.content;
6910
6758
  } else if (event.type === "gadget_result") {
6759
+ flushTextBuffer();
6911
6760
  progress.pause();
6912
6761
  if (options.quiet) {
6913
6762
  if (event.result.gadgetName === "TellUser" && event.result.parameters?.message) {
6914
6763
  const message = String(event.result.parameters.message);
6915
- const rendered = renderMarkdown(message);
6916
- env.stdout.write(`${rendered}
6764
+ env.stdout.write(`${message}
6917
6765
  `);
6918
6766
  }
6919
6767
  } else {
@@ -6923,6 +6771,7 @@ Command rejected by user with message: "${response}"`
6923
6771
  }
6924
6772
  }
6925
6773
  }
6774
+ flushTextBuffer();
6926
6775
  progress.complete();
6927
6776
  printer.ensureNewline();
6928
6777
  if (!options.quiet && iterations > 1) {
@@ -6961,9 +6810,18 @@ async function executeComplete(promptArg, options, env) {
6961
6810
  builder.addSystem(options.system);
6962
6811
  }
6963
6812
  builder.addUser(prompt);
6813
+ const messages = builder.build();
6814
+ const llmRequestsDir = resolveLogDir(options.logLlmRequests, "requests");
6815
+ const llmResponsesDir = resolveLogDir(options.logLlmResponses, "responses");
6816
+ const timestamp = Date.now();
6817
+ if (llmRequestsDir) {
6818
+ const filename = `${timestamp}_complete.request.txt`;
6819
+ const content = formatLlmRequest(messages);
6820
+ await writeLogFile(llmRequestsDir, filename, content);
6821
+ }
6964
6822
  const stream2 = client.stream({
6965
6823
  model,
6966
- messages: builder.build(),
6824
+ messages,
6967
6825
  temperature: options.temperature,
6968
6826
  maxTokens: options.maxTokens
6969
6827
  });
@@ -6974,7 +6832,7 @@ async function executeComplete(promptArg, options, env) {
6974
6832
  progress.startCall(model, estimatedInputTokens);
6975
6833
  let finishReason;
6976
6834
  let usage;
6977
- let totalChars = 0;
6835
+ let accumulatedResponse = "";
6978
6836
  for await (const chunk of stream2) {
6979
6837
  if (chunk.usage) {
6980
6838
  usage = chunk.usage;
@@ -6987,8 +6845,8 @@ async function executeComplete(promptArg, options, env) {
6987
6845
  }
6988
6846
  if (chunk.text) {
6989
6847
  progress.pause();
6990
- totalChars += chunk.text.length;
6991
- progress.update(totalChars);
6848
+ accumulatedResponse += chunk.text;
6849
+ progress.update(accumulatedResponse.length);
6992
6850
  printer.write(chunk.text);
6993
6851
  }
6994
6852
  if (chunk.finishReason !== void 0) {
@@ -6998,6 +6856,10 @@ async function executeComplete(promptArg, options, env) {
6998
6856
  progress.endCall(usage);
6999
6857
  progress.complete();
7000
6858
  printer.ensureNewline();
6859
+ if (llmResponsesDir) {
6860
+ const filename = `${timestamp}_complete.response.txt`;
6861
+ await writeLogFile(llmResponsesDir, filename, accumulatedResponse);
6862
+ }
7001
6863
  if (stderrTTY && !options.quiet) {
7002
6864
  const summary = renderSummary({ finishReason, usage, cost: progress.getTotalCost() });
7003
6865
  if (summary) {
@@ -7016,9 +6878,9 @@ function registerCompleteCommand(program, env, config) {
7016
6878
 
7017
6879
  // src/cli/config.ts
7018
6880
  var import_node_fs3 = require("fs");
7019
- var import_node_os = require("os");
7020
- var import_node_path3 = require("path");
7021
- var import_js_toml2 = require("js-toml");
6881
+ var import_node_os2 = require("os");
6882
+ var import_node_path4 = require("path");
6883
+ var import_js_toml = require("js-toml");
7022
6884
 
7023
6885
  // src/cli/templates.ts
7024
6886
  var import_eta = require("eta");
@@ -7112,6 +6974,8 @@ var COMPLETE_CONFIG_KEYS = /* @__PURE__ */ new Set([
7112
6974
  "log-level",
7113
6975
  "log-file",
7114
6976
  "log-reset",
6977
+ "log-llm-requests",
6978
+ "log-llm-responses",
7115
6979
  "type"
7116
6980
  // Allowed for inheritance compatibility, ignored for built-in commands
7117
6981
  ]);
@@ -7121,16 +6985,18 @@ var AGENT_CONFIG_KEYS = /* @__PURE__ */ new Set([
7121
6985
  "temperature",
7122
6986
  "max-iterations",
7123
6987
  "gadget",
7124
- "parameter-format",
7125
6988
  "builtins",
7126
6989
  "builtin-interaction",
7127
6990
  "gadget-start-prefix",
7128
6991
  "gadget-end-prefix",
6992
+ "gadget-arg-prefix",
7129
6993
  "quiet",
7130
6994
  "inherits",
7131
6995
  "log-level",
7132
6996
  "log-file",
7133
6997
  "log-reset",
6998
+ "log-llm-requests",
6999
+ "log-llm-responses",
7134
7000
  "type"
7135
7001
  // Allowed for inheritance compatibility, ignored for built-in commands
7136
7002
  ]);
@@ -7140,9 +7006,8 @@ var CUSTOM_CONFIG_KEYS = /* @__PURE__ */ new Set([
7140
7006
  "type",
7141
7007
  "description"
7142
7008
  ]);
7143
- var VALID_PARAMETER_FORMATS = ["json", "yaml", "toml", "auto"];
7144
7009
  function getConfigPath() {
7145
- return (0, import_node_path3.join)((0, import_node_os.homedir)(), ".llmist", "cli.toml");
7010
+ return (0, import_node_path4.join)((0, import_node_os2.homedir)(), ".llmist", "cli.toml");
7146
7011
  }
7147
7012
  var ConfigError = class extends Error {
7148
7013
  constructor(message, path2) {
@@ -7276,6 +7141,20 @@ function validateCompleteConfig(raw, section) {
7276
7141
  if ("quiet" in rawObj) {
7277
7142
  result.quiet = validateBoolean(rawObj.quiet, "quiet", section);
7278
7143
  }
7144
+ if ("log-llm-requests" in rawObj) {
7145
+ result["log-llm-requests"] = validateStringOrBoolean(
7146
+ rawObj["log-llm-requests"],
7147
+ "log-llm-requests",
7148
+ section
7149
+ );
7150
+ }
7151
+ if ("log-llm-responses" in rawObj) {
7152
+ result["log-llm-responses"] = validateStringOrBoolean(
7153
+ rawObj["log-llm-responses"],
7154
+ "log-llm-responses",
7155
+ section
7156
+ );
7157
+ }
7279
7158
  return result;
7280
7159
  }
7281
7160
  function validateAgentConfig(raw, section) {
@@ -7301,15 +7180,6 @@ function validateAgentConfig(raw, section) {
7301
7180
  if ("gadget" in rawObj) {
7302
7181
  result.gadget = validateStringArray(rawObj.gadget, "gadget", section);
7303
7182
  }
7304
- if ("parameter-format" in rawObj) {
7305
- const format = validateString(rawObj["parameter-format"], "parameter-format", section);
7306
- if (!VALID_PARAMETER_FORMATS.includes(format)) {
7307
- throw new ConfigError(
7308
- `[${section}].parameter-format must be one of: ${VALID_PARAMETER_FORMATS.join(", ")}`
7309
- );
7310
- }
7311
- result["parameter-format"] = format;
7312
- }
7313
7183
  if ("builtins" in rawObj) {
7314
7184
  result.builtins = validateBoolean(rawObj.builtins, "builtins", section);
7315
7185
  }
@@ -7334,11 +7204,38 @@ function validateAgentConfig(raw, section) {
7334
7204
  section
7335
7205
  );
7336
7206
  }
7207
+ if ("gadget-arg-prefix" in rawObj) {
7208
+ result["gadget-arg-prefix"] = validateString(
7209
+ rawObj["gadget-arg-prefix"],
7210
+ "gadget-arg-prefix",
7211
+ section
7212
+ );
7213
+ }
7337
7214
  if ("quiet" in rawObj) {
7338
7215
  result.quiet = validateBoolean(rawObj.quiet, "quiet", section);
7339
7216
  }
7217
+ if ("log-llm-requests" in rawObj) {
7218
+ result["log-llm-requests"] = validateStringOrBoolean(
7219
+ rawObj["log-llm-requests"],
7220
+ "log-llm-requests",
7221
+ section
7222
+ );
7223
+ }
7224
+ if ("log-llm-responses" in rawObj) {
7225
+ result["log-llm-responses"] = validateStringOrBoolean(
7226
+ rawObj["log-llm-responses"],
7227
+ "log-llm-responses",
7228
+ section
7229
+ );
7230
+ }
7340
7231
  return result;
7341
7232
  }
7233
+ function validateStringOrBoolean(value, field, section) {
7234
+ if (typeof value === "string" || typeof value === "boolean") {
7235
+ return value;
7236
+ }
7237
+ throw new ConfigError(`[${section}].${field} must be a string or boolean`);
7238
+ }
7342
7239
  function validateCustomConfig(raw, section) {
7343
7240
  if (typeof raw !== "object" || raw === null) {
7344
7241
  throw new ConfigError(`[${section}] must be a table`);
@@ -7373,15 +7270,6 @@ function validateCustomConfig(raw, section) {
7373
7270
  if ("gadget" in rawObj) {
7374
7271
  result.gadget = validateStringArray(rawObj.gadget, "gadget", section);
7375
7272
  }
7376
- if ("parameter-format" in rawObj) {
7377
- const format = validateString(rawObj["parameter-format"], "parameter-format", section);
7378
- if (!VALID_PARAMETER_FORMATS.includes(format)) {
7379
- throw new ConfigError(
7380
- `[${section}].parameter-format must be one of: ${VALID_PARAMETER_FORMATS.join(", ")}`
7381
- );
7382
- }
7383
- result["parameter-format"] = format;
7384
- }
7385
7273
  if ("builtins" in rawObj) {
7386
7274
  result.builtins = validateBoolean(rawObj.builtins, "builtins", section);
7387
7275
  }
@@ -7406,6 +7294,13 @@ function validateCustomConfig(raw, section) {
7406
7294
  section
7407
7295
  );
7408
7296
  }
7297
+ if ("gadget-arg-prefix" in rawObj) {
7298
+ result["gadget-arg-prefix"] = validateString(
7299
+ rawObj["gadget-arg-prefix"],
7300
+ "gadget-arg-prefix",
7301
+ section
7302
+ );
7303
+ }
7409
7304
  if ("max-tokens" in rawObj) {
7410
7305
  result["max-tokens"] = validateNumber(rawObj["max-tokens"], "max-tokens", section, {
7411
7306
  integer: true,
@@ -7475,7 +7370,7 @@ function loadConfig() {
7475
7370
  }
7476
7371
  let raw;
7477
7372
  try {
7478
- raw = (0, import_js_toml2.load)(content);
7373
+ raw = (0, import_js_toml.load)(content);
7479
7374
  } catch (error) {
7480
7375
  throw new ConfigError(
7481
7376
  `Invalid TOML syntax: ${error instanceof Error ? error.message : "Unknown error"}`,
@@ -7891,12 +7786,12 @@ function registerCustomCommand(program, name, config, env) {
7891
7786
  function parseLogLevel2(value) {
7892
7787
  const normalized = value.toLowerCase();
7893
7788
  if (!LOG_LEVELS.includes(normalized)) {
7894
- throw new import_commander3.InvalidArgumentError(`Log level must be one of: ${LOG_LEVELS.join(", ")}`);
7789
+ throw new import_commander2.InvalidArgumentError(`Log level must be one of: ${LOG_LEVELS.join(", ")}`);
7895
7790
  }
7896
7791
  return normalized;
7897
7792
  }
7898
7793
  function createProgram(env, config) {
7899
- const program = new import_commander3.Command();
7794
+ const program = new import_commander2.Command();
7900
7795
  program.name(CLI_NAME).description(CLI_DESCRIPTION).version(package_default.version).option(OPTION_FLAGS.logLevel, OPTION_DESCRIPTIONS.logLevel, parseLogLevel2).option(OPTION_FLAGS.logFile, OPTION_DESCRIPTIONS.logFile).option(OPTION_FLAGS.logReset, OPTION_DESCRIPTIONS.logReset).configureOutput({
7901
7796
  writeOut: (str) => env.stdout.write(str),
7902
7797
  writeErr: (str) => env.stderr.write(str)
@@ -7917,7 +7812,7 @@ async function runCLI(overrides = {}) {
7917
7812
  const opts = "env" in overrides || "config" in overrides ? overrides : { env: overrides };
7918
7813
  const config = opts.config !== void 0 ? opts.config : loadConfig();
7919
7814
  const envOverrides = opts.env ?? {};
7920
- const preParser = new import_commander3.Command();
7815
+ const preParser = new import_commander2.Command();
7921
7816
  preParser.option(OPTION_FLAGS.logLevel, OPTION_DESCRIPTIONS.logLevel, parseLogLevel2).option(OPTION_FLAGS.logFile, OPTION_DESCRIPTIONS.logFile).option(OPTION_FLAGS.logReset, OPTION_DESCRIPTIONS.logReset).allowUnknownOption().allowExcessArguments().helpOption(false);
7922
7817
  preParser.parse(process.argv);
7923
7818
  const globalOpts = preParser.opts();