llmist 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -362,6 +362,7 @@ var init_prompt_config = __esm({
362
362
  criticalUsage: "INVOKE gadgets using the markers - do not describe what you want to do.",
363
363
  formatDescriptionYaml: "Parameters in YAML format (one per line)",
364
364
  formatDescriptionJson: "Parameters in JSON format (valid JSON object)",
365
+ formatDescriptionToml: "Parameters in TOML format (key = value pairs, use triple-quotes for multiline)",
365
366
  rules: () => [
366
367
  "Output ONLY plain text with the exact markers - never use function/tool calling",
367
368
  "You can invoke multiple gadgets in a single response",
@@ -369,6 +370,7 @@ var init_prompt_config = __esm({
369
370
  ],
370
371
  schemaLabelJson: "\n\nInput Schema (JSON):",
371
372
  schemaLabelYaml: "\n\nInput Schema (YAML):",
373
+ schemaLabelToml: "\n\nInput Schema (TOML):",
372
374
  customExamples: null
373
375
  };
374
376
  }
@@ -399,6 +401,15 @@ var init_messages = __esm({
399
401
  constructor(promptConfig) {
400
402
  this.promptConfig = promptConfig ?? {};
401
403
  }
404
+ /**
405
+ * Set custom prefixes for gadget markers.
406
+ * Used to configure history builder to match system prompt markers.
407
+ */
408
+ withPrefixes(startPrefix, endPrefix) {
409
+ this.startPrefix = startPrefix;
410
+ this.endPrefix = endPrefix;
411
+ return this;
412
+ }
402
413
  addSystem(content, metadata) {
403
414
  this.messages.push({ role: "system", content, metadata });
404
415
  return this;
@@ -436,7 +447,14 @@ var init_messages = __esm({
436
447
  for (const gadget of gadgets) {
437
448
  const gadgetName = gadget.name ?? gadget.constructor.name;
438
449
  const instruction = gadget.getInstruction(parameterFormat);
439
- const schemaMarker = parameterFormat === "yaml" ? "\n\nInput Schema (YAML):" : "\n\nInput Schema (JSON):";
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];
440
458
  const schemaIndex = instruction.indexOf(schemaMarker);
441
459
  const description = (schemaIndex !== -1 ? instruction.substring(0, schemaIndex) : instruction).trim();
442
460
  const schema = schemaIndex !== -1 ? instruction.substring(schemaIndex + schemaMarker.length).trim() : "";
@@ -456,15 +474,26 @@ ${schema}`);
456
474
  }
457
475
  buildUsageSection(parameterFormat, context) {
458
476
  const parts = [];
459
- const formatDescription = parameterFormat === "yaml" ? resolvePromptTemplate(
460
- this.promptConfig.formatDescriptionYaml,
461
- DEFAULT_PROMPTS.formatDescriptionYaml,
462
- context
463
- ) : resolvePromptTemplate(
464
- this.promptConfig.formatDescriptionJson,
465
- DEFAULT_PROMPTS.formatDescriptionJson,
466
- context
467
- );
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);
468
497
  parts.push("\n\nHOW TO INVOKE GADGETS");
469
498
  parts.push("\n=====================\n");
470
499
  const criticalUsage = resolvePromptTemplate(
@@ -492,38 +521,110 @@ CRITICAL: ${criticalUsage}
492
521
  return this.promptConfig.customExamples(context);
493
522
  }
494
523
  const parts = [];
495
- const singleExample = parameterFormat === "yaml" ? `${this.startPrefix}translate
524
+ const singleExamples = {
525
+ yaml: `${this.startPrefix}translate
496
526
  from: English
497
527
  to: Polish
498
- content: Paris is the capital of France.
499
- ${this.endPrefix}` : `${this.startPrefix}translate
500
- {"from": "English", "to": "Polish", "content": "Paris is the capital of France."}
501
- ${this.endPrefix}`;
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
+ };
502
542
  parts.push(`
503
543
 
504
544
  EXAMPLE (Single Gadget):
505
545
 
506
- ${singleExample}`);
507
- const multipleExample = parameterFormat === "yaml" ? `${this.startPrefix}translate
546
+ ${singleExamples[parameterFormat]}`);
547
+ const multipleExamples = {
548
+ yaml: `${this.startPrefix}translate
508
549
  from: English
509
550
  to: Polish
510
- content: Paris is the capital of France.
551
+ content: "Paris is the capital of France: a beautiful city."
511
552
  ${this.endPrefix}
512
553
  ${this.startPrefix}analyze
513
554
  type: economic_analysis
514
555
  matter: "Polish Economy"
515
- question: Polish arms exports 2025.
516
- ${this.endPrefix}` : `${this.startPrefix}translate
517
- {"from": "English", "to": "Polish", "content": "Paris is the capital of France."}
556
+ question: |
557
+ Analyze the following:
558
+ - Polish arms exports 2025
559
+ - Economic implications
560
+ ${this.endPrefix}`,
561
+ json: `${this.startPrefix}translate
562
+ {"from": "English", "to": "Polish", "content": "Paris is the capital of France: a beautiful city."}
563
+ ${this.endPrefix}
564
+ ${this.startPrefix}analyze
565
+ {"type": "economic_analysis", "matter": "Polish Economy", "question": "Analyze the following: Polish arms exports 2025, economic implications"}
566
+ ${this.endPrefix}`,
567
+ toml: `${this.startPrefix}translate
568
+ from = "English"
569
+ to = "Polish"
570
+ content = "Paris is the capital of France: a beautiful city."
571
+ ${this.endPrefix}
572
+ ${this.startPrefix}analyze
573
+ type = "economic_analysis"
574
+ matter = "Polish Economy"
575
+ question = """
576
+ Analyze the following:
577
+ - Polish arms exports 2025
578
+ - Economic implications
579
+ """
580
+ ${this.endPrefix}`,
581
+ auto: `${this.startPrefix}translate
582
+ {"from": "English", "to": "Polish", "content": "Paris is the capital of France: a beautiful city."}
518
583
  ${this.endPrefix}
519
584
  ${this.startPrefix}analyze
520
- {"type": "economic_analysis", "matter": "Polish Economy", "question": "Polish arms exports 2025."}
521
- ${this.endPrefix}`;
585
+ {"type": "economic_analysis", "matter": "Polish Economy", "question": "Analyze the following: Polish arms exports 2025, economic implications"}
586
+ ${this.endPrefix}`
587
+ };
522
588
  parts.push(`
523
589
 
524
590
  EXAMPLE (Multiple Gadgets):
525
591
 
526
- ${multipleExample}`);
592
+ ${multipleExamples[parameterFormat]}`);
593
+ if (parameterFormat === "yaml") {
594
+ parts.push(`
595
+
596
+ YAML MULTILINE SYNTAX:
597
+ For string values with special characters (colons, dashes, quotes) or multiple lines,
598
+ use the pipe (|) syntax. ALL content lines MUST be indented with 2 spaces:
599
+
600
+ CORRECT - all lines indented:
601
+ question: |
602
+ Which option do you prefer?
603
+ - Option A: fast processing
604
+ - Option B: thorough analysis
605
+ Please choose one.
606
+
607
+ WRONG - inconsistent indentation breaks YAML:
608
+ question: |
609
+ Which option do you prefer?
610
+ - Option A: fast
611
+ Please choose one. <-- ERROR: not indented, breaks out of the block`);
612
+ } else if (parameterFormat === "toml") {
613
+ parts.push(`
614
+
615
+ TOML MULTILINE SYNTAX:
616
+ For string values with multiple lines or special characters, use triple-quotes ("""):
617
+
618
+ filePath = "README.md"
619
+ content = """
620
+ # Project Title
621
+
622
+ This content can contain:
623
+ - Markdown lists
624
+ - Special characters: # : -
625
+ - Multiple paragraphs
626
+ """`);
627
+ }
527
628
  return parts.join("");
528
629
  }
529
630
  buildRulesSection(context) {
@@ -567,6 +668,16 @@ ${this.endPrefix}`
567
668
  return `${key}: ${JSON.stringify(value)}`;
568
669
  }).join("\n");
569
670
  }
671
+ if (format === "toml") {
672
+ return Object.entries(parameters).map(([key, value]) => {
673
+ if (typeof value === "string" && value.includes("\n")) {
674
+ return `${key} = """
675
+ ${value}
676
+ """`;
677
+ }
678
+ return `${key} = ${JSON.stringify(value)}`;
679
+ }).join("\n");
680
+ }
570
681
  return JSON.stringify(parameters);
571
682
  }
572
683
  build() {
@@ -656,11 +767,14 @@ var init_conversation_manager = __esm({
656
767
  initialMessages;
657
768
  historyBuilder;
658
769
  parameterFormat;
659
- constructor(baseMessages, initialMessages, parameterFormat = "json") {
770
+ constructor(baseMessages, initialMessages, options = {}) {
660
771
  this.baseMessages = baseMessages;
661
772
  this.initialMessages = initialMessages;
662
- this.parameterFormat = parameterFormat;
773
+ this.parameterFormat = options.parameterFormat ?? "json";
663
774
  this.historyBuilder = new LLMMessageBuilder();
775
+ if (options.startPrefix && options.endPrefix) {
776
+ this.historyBuilder.withPrefixes(options.startPrefix, options.endPrefix);
777
+ }
664
778
  }
665
779
  addUserMessage(content) {
666
780
  this.historyBuilder.addUser(content);
@@ -1008,6 +1122,104 @@ var init_executor = __esm({
1008
1122
 
1009
1123
  // src/gadgets/parser.ts
1010
1124
  import * as yaml from "js-yaml";
1125
+ import { load as parseToml } from "js-toml";
1126
+ function preprocessYaml(yamlStr) {
1127
+ const lines = yamlStr.split("\n");
1128
+ const result = [];
1129
+ let i = 0;
1130
+ while (i < lines.length) {
1131
+ const line = lines[i];
1132
+ const match = line.match(/^(\s*)([\w-]+):\s+(.+)$/);
1133
+ if (match) {
1134
+ const [, indent, key, value] = match;
1135
+ if (value === "|" || value === ">" || value === "|-" || value === ">-") {
1136
+ result.push(line);
1137
+ i++;
1138
+ const keyIndentLen2 = indent.length;
1139
+ const blockLines = [];
1140
+ let minContentIndent = Infinity;
1141
+ while (i < lines.length) {
1142
+ const blockLine = lines[i];
1143
+ const blockIndentMatch = blockLine.match(/^(\s*)/);
1144
+ const blockIndentLen = blockIndentMatch ? blockIndentMatch[1].length : 0;
1145
+ if (blockLine.trim() === "") {
1146
+ blockLines.push({ content: "", originalIndent: 0 });
1147
+ i++;
1148
+ continue;
1149
+ }
1150
+ if (blockIndentLen > keyIndentLen2) {
1151
+ const content = blockLine.substring(blockIndentLen);
1152
+ blockLines.push({ content, originalIndent: blockIndentLen });
1153
+ if (content.trim().length > 0) {
1154
+ minContentIndent = Math.min(minContentIndent, blockIndentLen);
1155
+ }
1156
+ i++;
1157
+ } else {
1158
+ break;
1159
+ }
1160
+ }
1161
+ const targetIndent = keyIndentLen2 + 2;
1162
+ for (const blockLine of blockLines) {
1163
+ if (blockLine.content === "") {
1164
+ result.push("");
1165
+ } else {
1166
+ result.push(" ".repeat(targetIndent) + blockLine.content);
1167
+ }
1168
+ }
1169
+ continue;
1170
+ }
1171
+ if (value.startsWith('"') || value.startsWith("'") || value === "true" || value === "false" || /^-?\d+(\.\d+)?$/.test(value)) {
1172
+ result.push(line);
1173
+ i++;
1174
+ continue;
1175
+ }
1176
+ const keyIndentLen = indent.length;
1177
+ const continuationLines = [];
1178
+ let j = i + 1;
1179
+ while (j < lines.length) {
1180
+ const nextLine = lines[j];
1181
+ if (nextLine.trim() === "") {
1182
+ continuationLines.push(nextLine);
1183
+ j++;
1184
+ continue;
1185
+ }
1186
+ const nextIndentMatch = nextLine.match(/^(\s*)/);
1187
+ const nextIndentLen = nextIndentMatch ? nextIndentMatch[1].length : 0;
1188
+ if (nextIndentLen > keyIndentLen) {
1189
+ continuationLines.push(nextLine);
1190
+ j++;
1191
+ } else {
1192
+ break;
1193
+ }
1194
+ }
1195
+ if (continuationLines.length > 0 && continuationLines.some((l) => l.trim().length > 0)) {
1196
+ result.push(`${indent}${key}: |`);
1197
+ result.push(`${indent} ${value}`);
1198
+ for (const contLine of continuationLines) {
1199
+ if (contLine.trim() === "") {
1200
+ result.push("");
1201
+ } else {
1202
+ const contIndentMatch = contLine.match(/^(\s*)/);
1203
+ const contIndent = contIndentMatch ? contIndentMatch[1] : "";
1204
+ const contContent = contLine.substring(contIndent.length);
1205
+ result.push(`${indent} ${contContent}`);
1206
+ }
1207
+ }
1208
+ i = j;
1209
+ continue;
1210
+ }
1211
+ if (value.includes(": ") || value.endsWith(":")) {
1212
+ const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
1213
+ result.push(`${indent}${key}: "${escaped}"`);
1214
+ i++;
1215
+ continue;
1216
+ }
1217
+ }
1218
+ result.push(line);
1219
+ i++;
1220
+ }
1221
+ return result.join("\n");
1222
+ }
1011
1223
  var globalInvocationCounter, StreamParser;
1012
1224
  var init_parser = __esm({
1013
1225
  "src/gadgets/parser.ts"() {
@@ -1033,6 +1245,17 @@ var init_parser = __esm({
1033
1245
  this.lastReportedTextLength = index;
1034
1246
  return segment.trim().length > 0 ? segment : void 0;
1035
1247
  }
1248
+ /**
1249
+ * Parse gadget name, handling both old format (name:invocationId) and new format (just name).
1250
+ * For new format, generates a unique invocation ID.
1251
+ */
1252
+ parseGadgetName(gadgetName) {
1253
+ if (gadgetName.includes(":")) {
1254
+ const parts = gadgetName.split(":");
1255
+ return { actualName: parts[0], invocationId: parts[1] };
1256
+ }
1257
+ return { actualName: gadgetName, invocationId: `gadget_${++globalInvocationCounter}` };
1258
+ }
1036
1259
  /**
1037
1260
  * Parse parameter string according to configured format
1038
1261
  */
@@ -1046,20 +1269,31 @@ var init_parser = __esm({
1046
1269
  }
1047
1270
  if (this.parameterFormat === "yaml") {
1048
1271
  try {
1049
- return { parameters: yaml.load(raw) };
1272
+ return { parameters: yaml.load(preprocessYaml(raw)) };
1050
1273
  } catch (error) {
1051
1274
  return { parseError: error instanceof Error ? error.message : "Failed to parse YAML" };
1052
1275
  }
1053
1276
  }
1277
+ if (this.parameterFormat === "toml") {
1278
+ try {
1279
+ return { parameters: parseToml(raw) };
1280
+ } catch (error) {
1281
+ return { parseError: error instanceof Error ? error.message : "Failed to parse TOML" };
1282
+ }
1283
+ }
1054
1284
  try {
1055
1285
  return { parameters: JSON.parse(raw) };
1056
1286
  } catch {
1057
1287
  try {
1058
- return { parameters: yaml.load(raw) };
1059
- } catch (error) {
1060
- return {
1061
- parseError: error instanceof Error ? error.message : "Failed to parse as JSON or YAML"
1062
- };
1288
+ return { parameters: parseToml(raw) };
1289
+ } catch {
1290
+ try {
1291
+ return { parameters: yaml.load(preprocessYaml(raw)) };
1292
+ } catch (error) {
1293
+ return {
1294
+ parseError: error instanceof Error ? error.message : "Failed to parse as JSON, TOML, or YAML"
1295
+ };
1296
+ }
1063
1297
  }
1064
1298
  }
1065
1299
  }
@@ -1078,16 +1312,7 @@ var init_parser = __esm({
1078
1312
  const metadataEndIndex = this.buffer.indexOf("\n", metadataStartIndex);
1079
1313
  if (metadataEndIndex === -1) break;
1080
1314
  const gadgetName = this.buffer.substring(metadataStartIndex, metadataEndIndex).trim();
1081
- let invocationId;
1082
- let actualGadgetName;
1083
- if (gadgetName.includes(":")) {
1084
- const parts = gadgetName.split(":");
1085
- actualGadgetName = parts[0];
1086
- invocationId = parts[1];
1087
- } else {
1088
- actualGadgetName = gadgetName;
1089
- invocationId = `gadget_${++globalInvocationCounter}`;
1090
- }
1315
+ const { actualName: actualGadgetName, invocationId } = this.parseGadgetName(gadgetName);
1091
1316
  const contentStartIndex = metadataEndIndex + 1;
1092
1317
  let partEndIndex;
1093
1318
  let endMarkerLength = 0;
@@ -1097,23 +1322,29 @@ var init_parser = __esm({
1097
1322
  if (partEndIndex === -1) break;
1098
1323
  endMarkerLength = oldEndMarker.length;
1099
1324
  } else {
1100
- partEndIndex = contentStartIndex;
1325
+ const nextStartPos = this.buffer.indexOf(this.startPrefix, contentStartIndex);
1326
+ let validEndPos = -1;
1327
+ let searchPos = contentStartIndex;
1101
1328
  while (true) {
1102
- const endPos = this.buffer.indexOf(this.endPrefix, partEndIndex);
1103
- if (endPos === -1) {
1104
- partEndIndex = -1;
1105
- break;
1106
- }
1329
+ const endPos = this.buffer.indexOf(this.endPrefix, searchPos);
1330
+ if (endPos === -1) break;
1107
1331
  const afterEnd = this.buffer.substring(endPos + this.endPrefix.length);
1108
1332
  if (afterEnd.startsWith("\n") || afterEnd.startsWith("\r") || afterEnd.startsWith(this.startPrefix) || afterEnd.length === 0) {
1109
- partEndIndex = endPos;
1110
- endMarkerLength = this.endPrefix.length;
1333
+ validEndPos = endPos;
1111
1334
  break;
1112
1335
  } else {
1113
- partEndIndex = endPos + this.endPrefix.length;
1336
+ searchPos = endPos + this.endPrefix.length;
1114
1337
  }
1115
1338
  }
1116
- if (partEndIndex === -1) break;
1339
+ if (nextStartPos !== -1 && (validEndPos === -1 || nextStartPos < validEndPos)) {
1340
+ partEndIndex = nextStartPos;
1341
+ endMarkerLength = 0;
1342
+ } else if (validEndPos !== -1) {
1343
+ partEndIndex = validEndPos;
1344
+ endMarkerLength = this.endPrefix.length;
1345
+ } else {
1346
+ break;
1347
+ }
1117
1348
  }
1118
1349
  const parametersRaw = this.buffer.substring(contentStartIndex, partEndIndex).trim();
1119
1350
  const { parameters, parseError } = this.parseParameters(parametersRaw);
@@ -1136,8 +1367,35 @@ var init_parser = __esm({
1136
1367
  this.lastReportedTextLength = 0;
1137
1368
  }
1138
1369
  }
1139
- // Finalize parsing and return remaining text
1370
+ // Finalize parsing and return remaining text or incomplete gadgets
1140
1371
  *finalize() {
1372
+ const startIndex = this.buffer.indexOf(this.startPrefix, this.lastReportedTextLength);
1373
+ if (startIndex !== -1) {
1374
+ const textBefore = this.takeTextUntil(startIndex);
1375
+ if (textBefore !== void 0) {
1376
+ yield { type: "text", content: textBefore };
1377
+ }
1378
+ const metadataStartIndex = startIndex + this.startPrefix.length;
1379
+ const metadataEndIndex = this.buffer.indexOf("\n", metadataStartIndex);
1380
+ if (metadataEndIndex !== -1) {
1381
+ const gadgetName = this.buffer.substring(metadataStartIndex, metadataEndIndex).trim();
1382
+ const { actualName: actualGadgetName, invocationId } = this.parseGadgetName(gadgetName);
1383
+ const contentStartIndex = metadataEndIndex + 1;
1384
+ const parametersRaw = this.buffer.substring(contentStartIndex).trim();
1385
+ const { parameters, parseError } = this.parseParameters(parametersRaw);
1386
+ yield {
1387
+ type: "gadget_call",
1388
+ call: {
1389
+ gadgetName: actualGadgetName,
1390
+ invocationId,
1391
+ parametersYaml: parametersRaw,
1392
+ parameters,
1393
+ parseError
1394
+ }
1395
+ };
1396
+ return;
1397
+ }
1398
+ }
1141
1399
  const remainingText = this.takeTextUntil(this.buffer.length);
1142
1400
  if (remainingText !== void 0) {
1143
1401
  yield { type: "text", content: remainingText };
@@ -2382,10 +2640,11 @@ var init_gemini = __esm({
2382
2640
  return GEMINI_MODELS;
2383
2641
  }
2384
2642
  buildRequestPayload(options, descriptor, _spec, messages) {
2385
- const { systemInstruction, contents } = this.extractSystemAndContents(messages);
2643
+ const contents = this.convertMessagesToContents(messages);
2386
2644
  const generationConfig = this.buildGenerationConfig(options);
2387
2645
  const config = {
2388
- ...systemInstruction ? { systemInstruction: systemInstruction.parts.map((p) => p.text).join("\n") } : {},
2646
+ // Note: systemInstruction removed - it doesn't work with countTokens()
2647
+ // System messages are now included in contents as user+model exchanges
2389
2648
  ...generationConfig ? { ...generationConfig } : {},
2390
2649
  // Explicitly disable function calling to prevent UNEXPECTED_TOOL_CALL errors
2391
2650
  toolConfig: {
@@ -2406,31 +2665,37 @@ var init_gemini = __esm({
2406
2665
  const streamResponse = await client.models.generateContentStream(payload);
2407
2666
  return streamResponse;
2408
2667
  }
2409
- extractSystemAndContents(messages) {
2410
- const firstSystemIndex = messages.findIndex((message) => message.role === "system");
2411
- if (firstSystemIndex === -1) {
2412
- return {
2413
- systemInstruction: null,
2414
- contents: this.mergeConsecutiveMessages(messages)
2415
- };
2668
+ /**
2669
+ * Convert LLM messages to Gemini contents format.
2670
+ *
2671
+ * For Gemini, we convert system messages to user+model exchanges instead of
2672
+ * using systemInstruction, because:
2673
+ * 1. systemInstruction doesn't work with countTokens() API
2674
+ * 2. This approach gives perfect token counting accuracy (0% error)
2675
+ * 3. The model receives and follows system instructions identically
2676
+ *
2677
+ * System message: "You are a helpful assistant"
2678
+ * Becomes:
2679
+ * - User: "You are a helpful assistant"
2680
+ * - Model: "Understood."
2681
+ */
2682
+ convertMessagesToContents(messages) {
2683
+ const expandedMessages = [];
2684
+ for (const message of messages) {
2685
+ if (message.role === "system") {
2686
+ expandedMessages.push({
2687
+ role: "user",
2688
+ content: message.content
2689
+ });
2690
+ expandedMessages.push({
2691
+ role: "assistant",
2692
+ content: "Understood."
2693
+ });
2694
+ } else {
2695
+ expandedMessages.push(message);
2696
+ }
2416
2697
  }
2417
- let systemBlockEnd = firstSystemIndex;
2418
- while (systemBlockEnd < messages.length && messages[systemBlockEnd].role === "system") {
2419
- systemBlockEnd++;
2420
- }
2421
- const systemMessages = messages.slice(firstSystemIndex, systemBlockEnd);
2422
- const nonSystemMessages = [
2423
- ...messages.slice(0, firstSystemIndex),
2424
- ...messages.slice(systemBlockEnd)
2425
- ];
2426
- const systemInstruction = {
2427
- role: "system",
2428
- parts: systemMessages.map((message) => ({ text: message.content }))
2429
- };
2430
- return {
2431
- systemInstruction,
2432
- contents: this.mergeConsecutiveMessages(nonSystemMessages)
2433
- };
2698
+ return this.mergeConsecutiveMessages(expandedMessages);
2434
2699
  }
2435
2700
  mergeConsecutiveMessages(messages) {
2436
2701
  if (messages.length === 0) {
@@ -2519,8 +2784,8 @@ var init_gemini = __esm({
2519
2784
  *
2520
2785
  * This method provides accurate token estimation for Gemini models by:
2521
2786
  * - Using the SDK's countTokens() method
2522
- * - Properly extracting and handling system instructions
2523
- * - Transforming messages to Gemini's expected format
2787
+ * - Converting system messages to user+model exchanges (same as in generation)
2788
+ * - This gives perfect token counting accuracy (0% error vs actual usage)
2524
2789
  *
2525
2790
  * @param messages - The messages to count tokens for
2526
2791
  * @param descriptor - Model descriptor containing the model name
@@ -2539,16 +2804,14 @@ var init_gemini = __esm({
2539
2804
  */
2540
2805
  async countTokens(messages, descriptor, _spec) {
2541
2806
  const client = this.client;
2542
- const { systemInstruction, contents } = this.extractSystemAndContents(messages);
2543
- const request = {
2544
- model: descriptor.name,
2545
- contents: this.convertContentsForNewSDK(contents)
2546
- };
2547
- if (systemInstruction) {
2548
- request.systemInstruction = systemInstruction.parts.map((p) => p.text).join("\n");
2549
- }
2807
+ const contents = this.convertMessagesToContents(messages);
2550
2808
  try {
2551
- const response = await client.models.countTokens(request);
2809
+ const response = await client.models.countTokens({
2810
+ model: descriptor.name,
2811
+ contents: this.convertContentsForNewSDK(contents)
2812
+ // Note: systemInstruction not used - it's not supported by countTokens()
2813
+ // and would cause a 2100% token counting error
2814
+ });
2552
2815
  return response.totalTokens ?? 0;
2553
2816
  } catch (error) {
2554
2817
  console.warn(
@@ -3226,11 +3489,11 @@ var init_agent = __esm({
3226
3489
  role: message.role,
3227
3490
  content: message.content
3228
3491
  }));
3229
- this.conversation = new ConversationManager(
3230
- baseMessages,
3231
- initialMessages,
3232
- this.parameterFormat
3233
- );
3492
+ this.conversation = new ConversationManager(baseMessages, initialMessages, {
3493
+ parameterFormat: this.parameterFormat,
3494
+ startPrefix: options.gadgetStartPrefix,
3495
+ endPrefix: options.gadgetEndPrefix
3496
+ });
3234
3497
  this.userPromptProvided = !!options.userPrompt;
3235
3498
  if (options.userPrompt) {
3236
3499
  this.conversation.addUserMessage(options.userPrompt);
@@ -4376,6 +4639,83 @@ function mergeDescriptions(schema, jsonSchema) {
4376
4639
 
4377
4640
  // src/gadgets/gadget.ts
4378
4641
  init_schema_validator();
4642
+ function formatYamlValue(value, indent = "") {
4643
+ if (typeof value === "string") {
4644
+ const lines = value.split("\n");
4645
+ if (lines.length === 1 && !value.includes(":") && !value.startsWith("-")) {
4646
+ return value;
4647
+ }
4648
+ const indentedLines = lines.map((line) => `${indent} ${line}`).join("\n");
4649
+ return `|
4650
+ ${indentedLines}`;
4651
+ }
4652
+ if (typeof value === "number" || typeof value === "boolean") {
4653
+ return String(value);
4654
+ }
4655
+ if (value === null || value === void 0) {
4656
+ return "null";
4657
+ }
4658
+ if (Array.isArray(value)) {
4659
+ if (value.length === 0) return "[]";
4660
+ const items = value.map((item) => `${indent}- ${formatYamlValue(item, indent + " ")}`);
4661
+ return "\n" + items.join("\n");
4662
+ }
4663
+ if (typeof value === "object") {
4664
+ const entries = Object.entries(value);
4665
+ if (entries.length === 0) return "{}";
4666
+ const lines = entries.map(([k, v]) => {
4667
+ const formattedValue = formatYamlValue(v, indent + " ");
4668
+ if (formattedValue.startsWith("\n") || formattedValue.startsWith("|")) {
4669
+ return `${indent}${k}: ${formattedValue}`;
4670
+ }
4671
+ return `${indent}${k}: ${formattedValue}`;
4672
+ });
4673
+ return "\n" + lines.join("\n");
4674
+ }
4675
+ return yaml2.dump(value).trimEnd();
4676
+ }
4677
+ function formatParamsAsYaml(params) {
4678
+ const lines = [];
4679
+ for (const [key, value] of Object.entries(params)) {
4680
+ const formattedValue = formatYamlValue(value, "");
4681
+ if (formattedValue.startsWith("\n")) {
4682
+ lines.push(`${key}:${formattedValue}`);
4683
+ } else {
4684
+ lines.push(`${key}: ${formattedValue}`);
4685
+ }
4686
+ }
4687
+ return lines.join("\n");
4688
+ }
4689
+ function formatTomlValue(value) {
4690
+ if (typeof value === "string") {
4691
+ if (value.includes("\n")) {
4692
+ return `"""
4693
+ ${value}
4694
+ """`;
4695
+ }
4696
+ return JSON.stringify(value);
4697
+ }
4698
+ if (typeof value === "number" || typeof value === "boolean") {
4699
+ return String(value);
4700
+ }
4701
+ if (value === null || value === void 0) {
4702
+ return '""';
4703
+ }
4704
+ if (Array.isArray(value)) {
4705
+ return JSON.stringify(value);
4706
+ }
4707
+ if (typeof value === "object") {
4708
+ return JSON.stringify(value);
4709
+ }
4710
+ return JSON.stringify(value);
4711
+ }
4712
+ function formatParamsAsToml(params) {
4713
+ const lines = [];
4714
+ for (const [key, value] of Object.entries(params)) {
4715
+ lines.push(`${key} = ${formatTomlValue(value)}`);
4716
+ }
4717
+ return lines.join("\n");
4718
+ }
4379
4719
  var BaseGadget = class {
4380
4720
  /**
4381
4721
  * The name of the gadget. Used for identification when LLM calls it.
@@ -4395,6 +4735,14 @@ var BaseGadget = class {
4395
4735
  * Set to 0 or undefined to disable timeout for this gadget.
4396
4736
  */
4397
4737
  timeoutMs;
4738
+ /**
4739
+ * Optional usage examples to help LLMs understand proper invocation.
4740
+ * Examples are rendered in getInstruction() alongside the schema.
4741
+ *
4742
+ * Note: Uses broader `unknown` type to allow typed examples from subclasses
4743
+ * while maintaining runtime compatibility.
4744
+ */
4745
+ examples;
4398
4746
  /**
4399
4747
  * Auto-generated instruction text for the LLM.
4400
4748
  * Combines name, description, and parameter schema into a formatted instruction.
@@ -4407,7 +4755,7 @@ var BaseGadget = class {
4407
4755
  * Generate instruction text for the LLM with format-specific schema.
4408
4756
  * Combines name, description, and parameter schema into a formatted instruction.
4409
4757
  *
4410
- * @param format - Format for the schema representation ('json' | 'yaml' | 'auto')
4758
+ * @param format - Format for the schema representation ('json' | 'yaml' | 'toml' | 'auto')
4411
4759
  * @returns Formatted instruction string
4412
4760
  */
4413
4761
  getInstruction(format = "json") {
@@ -4422,12 +4770,38 @@ var BaseGadget = class {
4422
4770
  if (format === "json" || format === "auto") {
4423
4771
  parts.push("\n\nInput Schema (JSON):");
4424
4772
  parts.push(JSON.stringify(jsonSchema, null, 2));
4773
+ } else if (format === "toml") {
4774
+ parts.push("\n\nInput Schema (TOML):");
4775
+ parts.push(JSON.stringify(jsonSchema, null, 2));
4425
4776
  } else {
4426
4777
  const yamlSchema = yaml2.dump(jsonSchema).trimEnd();
4427
4778
  parts.push("\n\nInput Schema (YAML):");
4428
4779
  parts.push(yamlSchema);
4429
4780
  }
4430
4781
  }
4782
+ if (this.examples && this.examples.length > 0) {
4783
+ parts.push("\n\nExamples:");
4784
+ this.examples.forEach((example, index) => {
4785
+ if (index > 0) {
4786
+ parts.push("");
4787
+ }
4788
+ if (example.comment) {
4789
+ parts.push(`# ${example.comment}`);
4790
+ }
4791
+ parts.push("Input:");
4792
+ if (format === "json" || format === "auto") {
4793
+ parts.push(JSON.stringify(example.params, null, 2));
4794
+ } else if (format === "toml") {
4795
+ parts.push(formatParamsAsToml(example.params));
4796
+ } else {
4797
+ parts.push(formatParamsAsYaml(example.params));
4798
+ }
4799
+ if (example.output !== void 0) {
4800
+ parts.push("Output:");
4801
+ parts.push(example.output);
4802
+ }
4803
+ });
4804
+ }
4431
4805
  return parts.join("\n");
4432
4806
  }
4433
4807
  };
@@ -4494,4 +4868,4 @@ export {
4494
4868
  init_builder,
4495
4869
  BaseGadget
4496
4870
  };
4497
- //# sourceMappingURL=chunk-VYBRYR2S.js.map
4871
+ //# sourceMappingURL=chunk-MH4TQ5AD.js.map