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.
@@ -436,6 +436,7 @@ var init_prompt_config = __esm({
436
436
  criticalUsage: "INVOKE gadgets using the markers - do not describe what you want to do.",
437
437
  formatDescriptionYaml: "Parameters in YAML format (one per line)",
438
438
  formatDescriptionJson: "Parameters in JSON format (valid JSON object)",
439
+ formatDescriptionToml: "Parameters in TOML format (key = value pairs, use triple-quotes for multiline)",
439
440
  rules: () => [
440
441
  "Output ONLY plain text with the exact markers - never use function/tool calling",
441
442
  "You can invoke multiple gadgets in a single response",
@@ -443,6 +444,7 @@ var init_prompt_config = __esm({
443
444
  ],
444
445
  schemaLabelJson: "\n\nInput Schema (JSON):",
445
446
  schemaLabelYaml: "\n\nInput Schema (YAML):",
447
+ schemaLabelToml: "\n\nInput Schema (TOML):",
446
448
  customExamples: null
447
449
  };
448
450
  }
@@ -463,6 +465,15 @@ var init_messages = __esm({
463
465
  constructor(promptConfig) {
464
466
  this.promptConfig = promptConfig ?? {};
465
467
  }
468
+ /**
469
+ * Set custom prefixes for gadget markers.
470
+ * Used to configure history builder to match system prompt markers.
471
+ */
472
+ withPrefixes(startPrefix, endPrefix) {
473
+ this.startPrefix = startPrefix;
474
+ this.endPrefix = endPrefix;
475
+ return this;
476
+ }
466
477
  addSystem(content, metadata) {
467
478
  this.messages.push({ role: "system", content, metadata });
468
479
  return this;
@@ -500,7 +511,14 @@ var init_messages = __esm({
500
511
  for (const gadget of gadgets) {
501
512
  const gadgetName = gadget.name ?? gadget.constructor.name;
502
513
  const instruction = gadget.getInstruction(parameterFormat);
503
- const schemaMarker = parameterFormat === "yaml" ? "\n\nInput Schema (YAML):" : "\n\nInput Schema (JSON):";
514
+ const schemaMarkers = {
515
+ yaml: "\n\nInput Schema (YAML):",
516
+ json: "\n\nInput Schema (JSON):",
517
+ toml: "\n\nInput Schema (TOML):",
518
+ auto: "\n\nInput Schema (JSON):"
519
+ // auto defaults to JSON schema display
520
+ };
521
+ const schemaMarker = schemaMarkers[parameterFormat];
504
522
  const schemaIndex = instruction.indexOf(schemaMarker);
505
523
  const description = (schemaIndex !== -1 ? instruction.substring(0, schemaIndex) : instruction).trim();
506
524
  const schema = schemaIndex !== -1 ? instruction.substring(schemaIndex + schemaMarker.length).trim() : "";
@@ -520,15 +538,26 @@ ${schema}`);
520
538
  }
521
539
  buildUsageSection(parameterFormat, context) {
522
540
  const parts = [];
523
- const formatDescription = parameterFormat === "yaml" ? resolvePromptTemplate(
524
- this.promptConfig.formatDescriptionYaml,
525
- DEFAULT_PROMPTS.formatDescriptionYaml,
526
- context
527
- ) : resolvePromptTemplate(
528
- this.promptConfig.formatDescriptionJson,
529
- DEFAULT_PROMPTS.formatDescriptionJson,
530
- context
531
- );
541
+ const formatDescriptionMap = {
542
+ yaml: {
543
+ config: this.promptConfig.formatDescriptionYaml,
544
+ defaultValue: DEFAULT_PROMPTS.formatDescriptionYaml
545
+ },
546
+ json: {
547
+ config: this.promptConfig.formatDescriptionJson,
548
+ defaultValue: DEFAULT_PROMPTS.formatDescriptionJson
549
+ },
550
+ toml: {
551
+ config: this.promptConfig.formatDescriptionToml,
552
+ defaultValue: DEFAULT_PROMPTS.formatDescriptionToml
553
+ },
554
+ auto: {
555
+ config: this.promptConfig.formatDescriptionJson,
556
+ defaultValue: DEFAULT_PROMPTS.formatDescriptionJson
557
+ }
558
+ };
559
+ const { config, defaultValue } = formatDescriptionMap[parameterFormat];
560
+ const formatDescription = resolvePromptTemplate(config, defaultValue, context);
532
561
  parts.push("\n\nHOW TO INVOKE GADGETS");
533
562
  parts.push("\n=====================\n");
534
563
  const criticalUsage = resolvePromptTemplate(
@@ -556,38 +585,110 @@ CRITICAL: ${criticalUsage}
556
585
  return this.promptConfig.customExamples(context);
557
586
  }
558
587
  const parts = [];
559
- const singleExample = parameterFormat === "yaml" ? `${this.startPrefix}translate
588
+ const singleExamples = {
589
+ yaml: `${this.startPrefix}translate
560
590
  from: English
561
591
  to: Polish
562
- content: Paris is the capital of France.
563
- ${this.endPrefix}` : `${this.startPrefix}translate
564
- {"from": "English", "to": "Polish", "content": "Paris is the capital of France."}
565
- ${this.endPrefix}`;
592
+ content: "Paris is the capital of France: a beautiful city."
593
+ ${this.endPrefix}`,
594
+ json: `${this.startPrefix}translate
595
+ {"from": "English", "to": "Polish", "content": "Paris is the capital of France: a beautiful city."}
596
+ ${this.endPrefix}`,
597
+ toml: `${this.startPrefix}translate
598
+ from = "English"
599
+ to = "Polish"
600
+ content = "Paris is the capital of France: a beautiful city."
601
+ ${this.endPrefix}`,
602
+ auto: `${this.startPrefix}translate
603
+ {"from": "English", "to": "Polish", "content": "Paris is the capital of France: a beautiful city."}
604
+ ${this.endPrefix}`
605
+ };
566
606
  parts.push(`
567
607
 
568
608
  EXAMPLE (Single Gadget):
569
609
 
570
- ${singleExample}`);
571
- const multipleExample = parameterFormat === "yaml" ? `${this.startPrefix}translate
610
+ ${singleExamples[parameterFormat]}`);
611
+ const multipleExamples = {
612
+ yaml: `${this.startPrefix}translate
572
613
  from: English
573
614
  to: Polish
574
- content: Paris is the capital of France.
615
+ content: "Paris is the capital of France: a beautiful city."
575
616
  ${this.endPrefix}
576
617
  ${this.startPrefix}analyze
577
618
  type: economic_analysis
578
619
  matter: "Polish Economy"
579
- question: Polish arms exports 2025.
580
- ${this.endPrefix}` : `${this.startPrefix}translate
581
- {"from": "English", "to": "Polish", "content": "Paris is the capital of France."}
620
+ question: |
621
+ Analyze the following:
622
+ - Polish arms exports 2025
623
+ - Economic implications
624
+ ${this.endPrefix}`,
625
+ json: `${this.startPrefix}translate
626
+ {"from": "English", "to": "Polish", "content": "Paris is the capital of France: a beautiful city."}
627
+ ${this.endPrefix}
628
+ ${this.startPrefix}analyze
629
+ {"type": "economic_analysis", "matter": "Polish Economy", "question": "Analyze the following: Polish arms exports 2025, economic implications"}
630
+ ${this.endPrefix}`,
631
+ toml: `${this.startPrefix}translate
632
+ from = "English"
633
+ to = "Polish"
634
+ content = "Paris is the capital of France: a beautiful city."
635
+ ${this.endPrefix}
636
+ ${this.startPrefix}analyze
637
+ type = "economic_analysis"
638
+ matter = "Polish Economy"
639
+ question = """
640
+ Analyze the following:
641
+ - Polish arms exports 2025
642
+ - Economic implications
643
+ """
644
+ ${this.endPrefix}`,
645
+ auto: `${this.startPrefix}translate
646
+ {"from": "English", "to": "Polish", "content": "Paris is the capital of France: a beautiful city."}
582
647
  ${this.endPrefix}
583
648
  ${this.startPrefix}analyze
584
- {"type": "economic_analysis", "matter": "Polish Economy", "question": "Polish arms exports 2025."}
585
- ${this.endPrefix}`;
649
+ {"type": "economic_analysis", "matter": "Polish Economy", "question": "Analyze the following: Polish arms exports 2025, economic implications"}
650
+ ${this.endPrefix}`
651
+ };
586
652
  parts.push(`
587
653
 
588
654
  EXAMPLE (Multiple Gadgets):
589
655
 
590
- ${multipleExample}`);
656
+ ${multipleExamples[parameterFormat]}`);
657
+ if (parameterFormat === "yaml") {
658
+ parts.push(`
659
+
660
+ YAML MULTILINE SYNTAX:
661
+ For string values with special characters (colons, dashes, quotes) or multiple lines,
662
+ use the pipe (|) syntax. ALL content lines MUST be indented with 2 spaces:
663
+
664
+ CORRECT - all lines indented:
665
+ question: |
666
+ Which option do you prefer?
667
+ - Option A: fast processing
668
+ - Option B: thorough analysis
669
+ Please choose one.
670
+
671
+ WRONG - inconsistent indentation breaks YAML:
672
+ question: |
673
+ Which option do you prefer?
674
+ - Option A: fast
675
+ Please choose one. <-- ERROR: not indented, breaks out of the block`);
676
+ } else if (parameterFormat === "toml") {
677
+ parts.push(`
678
+
679
+ TOML MULTILINE SYNTAX:
680
+ For string values with multiple lines or special characters, use triple-quotes ("""):
681
+
682
+ filePath = "README.md"
683
+ content = """
684
+ # Project Title
685
+
686
+ This content can contain:
687
+ - Markdown lists
688
+ - Special characters: # : -
689
+ - Multiple paragraphs
690
+ """`);
691
+ }
591
692
  return parts.join("");
592
693
  }
593
694
  buildRulesSection(context) {
@@ -631,6 +732,16 @@ ${this.endPrefix}`
631
732
  return `${key}: ${JSON.stringify(value)}`;
632
733
  }).join("\n");
633
734
  }
735
+ if (format === "toml") {
736
+ return Object.entries(parameters).map(([key, value]) => {
737
+ if (typeof value === "string" && value.includes("\n")) {
738
+ return `${key} = """
739
+ ${value}
740
+ """`;
741
+ }
742
+ return `${key} = ${JSON.stringify(value)}`;
743
+ }).join("\n");
744
+ }
634
745
  return JSON.stringify(parameters);
635
746
  }
636
747
  build() {
@@ -663,11 +774,14 @@ var init_conversation_manager = __esm({
663
774
  initialMessages;
664
775
  historyBuilder;
665
776
  parameterFormat;
666
- constructor(baseMessages, initialMessages, parameterFormat = "json") {
777
+ constructor(baseMessages, initialMessages, options = {}) {
667
778
  this.baseMessages = baseMessages;
668
779
  this.initialMessages = initialMessages;
669
- this.parameterFormat = parameterFormat;
780
+ this.parameterFormat = options.parameterFormat ?? "json";
670
781
  this.historyBuilder = new LLMMessageBuilder();
782
+ if (options.startPrefix && options.endPrefix) {
783
+ this.historyBuilder.withPrefixes(options.startPrefix, options.endPrefix);
784
+ }
671
785
  }
672
786
  addUserMessage(content) {
673
787
  this.historyBuilder.addUser(content);
@@ -1138,11 +1252,109 @@ var init_executor = __esm({
1138
1252
  });
1139
1253
 
1140
1254
  // src/gadgets/parser.ts
1141
- var yaml, globalInvocationCounter, StreamParser;
1255
+ function preprocessYaml(yamlStr) {
1256
+ const lines = yamlStr.split("\n");
1257
+ const result = [];
1258
+ let i = 0;
1259
+ while (i < lines.length) {
1260
+ const line = lines[i];
1261
+ const match = line.match(/^(\s*)([\w-]+):\s+(.+)$/);
1262
+ if (match) {
1263
+ const [, indent, key, value] = match;
1264
+ if (value === "|" || value === ">" || value === "|-" || value === ">-") {
1265
+ result.push(line);
1266
+ i++;
1267
+ const keyIndentLen2 = indent.length;
1268
+ const blockLines = [];
1269
+ let minContentIndent = Infinity;
1270
+ while (i < lines.length) {
1271
+ const blockLine = lines[i];
1272
+ const blockIndentMatch = blockLine.match(/^(\s*)/);
1273
+ const blockIndentLen = blockIndentMatch ? blockIndentMatch[1].length : 0;
1274
+ if (blockLine.trim() === "") {
1275
+ blockLines.push({ content: "", originalIndent: 0 });
1276
+ i++;
1277
+ continue;
1278
+ }
1279
+ if (blockIndentLen > keyIndentLen2) {
1280
+ const content = blockLine.substring(blockIndentLen);
1281
+ blockLines.push({ content, originalIndent: blockIndentLen });
1282
+ if (content.trim().length > 0) {
1283
+ minContentIndent = Math.min(minContentIndent, blockIndentLen);
1284
+ }
1285
+ i++;
1286
+ } else {
1287
+ break;
1288
+ }
1289
+ }
1290
+ const targetIndent = keyIndentLen2 + 2;
1291
+ for (const blockLine of blockLines) {
1292
+ if (blockLine.content === "") {
1293
+ result.push("");
1294
+ } else {
1295
+ result.push(" ".repeat(targetIndent) + blockLine.content);
1296
+ }
1297
+ }
1298
+ continue;
1299
+ }
1300
+ if (value.startsWith('"') || value.startsWith("'") || value === "true" || value === "false" || /^-?\d+(\.\d+)?$/.test(value)) {
1301
+ result.push(line);
1302
+ i++;
1303
+ continue;
1304
+ }
1305
+ const keyIndentLen = indent.length;
1306
+ const continuationLines = [];
1307
+ let j = i + 1;
1308
+ while (j < lines.length) {
1309
+ const nextLine = lines[j];
1310
+ if (nextLine.trim() === "") {
1311
+ continuationLines.push(nextLine);
1312
+ j++;
1313
+ continue;
1314
+ }
1315
+ const nextIndentMatch = nextLine.match(/^(\s*)/);
1316
+ const nextIndentLen = nextIndentMatch ? nextIndentMatch[1].length : 0;
1317
+ if (nextIndentLen > keyIndentLen) {
1318
+ continuationLines.push(nextLine);
1319
+ j++;
1320
+ } else {
1321
+ break;
1322
+ }
1323
+ }
1324
+ if (continuationLines.length > 0 && continuationLines.some((l) => l.trim().length > 0)) {
1325
+ result.push(`${indent}${key}: |`);
1326
+ result.push(`${indent} ${value}`);
1327
+ for (const contLine of continuationLines) {
1328
+ if (contLine.trim() === "") {
1329
+ result.push("");
1330
+ } else {
1331
+ const contIndentMatch = contLine.match(/^(\s*)/);
1332
+ const contIndent = contIndentMatch ? contIndentMatch[1] : "";
1333
+ const contContent = contLine.substring(contIndent.length);
1334
+ result.push(`${indent} ${contContent}`);
1335
+ }
1336
+ }
1337
+ i = j;
1338
+ continue;
1339
+ }
1340
+ if (value.includes(": ") || value.endsWith(":")) {
1341
+ const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
1342
+ result.push(`${indent}${key}: "${escaped}"`);
1343
+ i++;
1344
+ continue;
1345
+ }
1346
+ }
1347
+ result.push(line);
1348
+ i++;
1349
+ }
1350
+ return result.join("\n");
1351
+ }
1352
+ var yaml, import_js_toml, globalInvocationCounter, StreamParser;
1142
1353
  var init_parser = __esm({
1143
1354
  "src/gadgets/parser.ts"() {
1144
1355
  "use strict";
1145
1356
  yaml = __toESM(require("js-yaml"), 1);
1357
+ import_js_toml = require("js-toml");
1146
1358
  init_constants();
1147
1359
  globalInvocationCounter = 0;
1148
1360
  StreamParser = class {
@@ -1164,6 +1376,17 @@ var init_parser = __esm({
1164
1376
  this.lastReportedTextLength = index;
1165
1377
  return segment.trim().length > 0 ? segment : void 0;
1166
1378
  }
1379
+ /**
1380
+ * Parse gadget name, handling both old format (name:invocationId) and new format (just name).
1381
+ * For new format, generates a unique invocation ID.
1382
+ */
1383
+ parseGadgetName(gadgetName) {
1384
+ if (gadgetName.includes(":")) {
1385
+ const parts = gadgetName.split(":");
1386
+ return { actualName: parts[0], invocationId: parts[1] };
1387
+ }
1388
+ return { actualName: gadgetName, invocationId: `gadget_${++globalInvocationCounter}` };
1389
+ }
1167
1390
  /**
1168
1391
  * Parse parameter string according to configured format
1169
1392
  */
@@ -1177,20 +1400,31 @@ var init_parser = __esm({
1177
1400
  }
1178
1401
  if (this.parameterFormat === "yaml") {
1179
1402
  try {
1180
- return { parameters: yaml.load(raw) };
1403
+ return { parameters: yaml.load(preprocessYaml(raw)) };
1181
1404
  } catch (error) {
1182
1405
  return { parseError: error instanceof Error ? error.message : "Failed to parse YAML" };
1183
1406
  }
1184
1407
  }
1408
+ if (this.parameterFormat === "toml") {
1409
+ try {
1410
+ return { parameters: (0, import_js_toml.load)(raw) };
1411
+ } catch (error) {
1412
+ return { parseError: error instanceof Error ? error.message : "Failed to parse TOML" };
1413
+ }
1414
+ }
1185
1415
  try {
1186
1416
  return { parameters: JSON.parse(raw) };
1187
1417
  } catch {
1188
1418
  try {
1189
- return { parameters: yaml.load(raw) };
1190
- } catch (error) {
1191
- return {
1192
- parseError: error instanceof Error ? error.message : "Failed to parse as JSON or YAML"
1193
- };
1419
+ return { parameters: (0, import_js_toml.load)(raw) };
1420
+ } catch {
1421
+ try {
1422
+ return { parameters: yaml.load(preprocessYaml(raw)) };
1423
+ } catch (error) {
1424
+ return {
1425
+ parseError: error instanceof Error ? error.message : "Failed to parse as JSON, TOML, or YAML"
1426
+ };
1427
+ }
1194
1428
  }
1195
1429
  }
1196
1430
  }
@@ -1209,16 +1443,7 @@ var init_parser = __esm({
1209
1443
  const metadataEndIndex = this.buffer.indexOf("\n", metadataStartIndex);
1210
1444
  if (metadataEndIndex === -1) break;
1211
1445
  const gadgetName = this.buffer.substring(metadataStartIndex, metadataEndIndex).trim();
1212
- let invocationId;
1213
- let actualGadgetName;
1214
- if (gadgetName.includes(":")) {
1215
- const parts = gadgetName.split(":");
1216
- actualGadgetName = parts[0];
1217
- invocationId = parts[1];
1218
- } else {
1219
- actualGadgetName = gadgetName;
1220
- invocationId = `gadget_${++globalInvocationCounter}`;
1221
- }
1446
+ const { actualName: actualGadgetName, invocationId } = this.parseGadgetName(gadgetName);
1222
1447
  const contentStartIndex = metadataEndIndex + 1;
1223
1448
  let partEndIndex;
1224
1449
  let endMarkerLength = 0;
@@ -1228,23 +1453,29 @@ var init_parser = __esm({
1228
1453
  if (partEndIndex === -1) break;
1229
1454
  endMarkerLength = oldEndMarker.length;
1230
1455
  } else {
1231
- partEndIndex = contentStartIndex;
1456
+ const nextStartPos = this.buffer.indexOf(this.startPrefix, contentStartIndex);
1457
+ let validEndPos = -1;
1458
+ let searchPos = contentStartIndex;
1232
1459
  while (true) {
1233
- const endPos = this.buffer.indexOf(this.endPrefix, partEndIndex);
1234
- if (endPos === -1) {
1235
- partEndIndex = -1;
1236
- break;
1237
- }
1460
+ const endPos = this.buffer.indexOf(this.endPrefix, searchPos);
1461
+ if (endPos === -1) break;
1238
1462
  const afterEnd = this.buffer.substring(endPos + this.endPrefix.length);
1239
1463
  if (afterEnd.startsWith("\n") || afterEnd.startsWith("\r") || afterEnd.startsWith(this.startPrefix) || afterEnd.length === 0) {
1240
- partEndIndex = endPos;
1241
- endMarkerLength = this.endPrefix.length;
1464
+ validEndPos = endPos;
1242
1465
  break;
1243
1466
  } else {
1244
- partEndIndex = endPos + this.endPrefix.length;
1467
+ searchPos = endPos + this.endPrefix.length;
1245
1468
  }
1246
1469
  }
1247
- if (partEndIndex === -1) break;
1470
+ if (nextStartPos !== -1 && (validEndPos === -1 || nextStartPos < validEndPos)) {
1471
+ partEndIndex = nextStartPos;
1472
+ endMarkerLength = 0;
1473
+ } else if (validEndPos !== -1) {
1474
+ partEndIndex = validEndPos;
1475
+ endMarkerLength = this.endPrefix.length;
1476
+ } else {
1477
+ break;
1478
+ }
1248
1479
  }
1249
1480
  const parametersRaw = this.buffer.substring(contentStartIndex, partEndIndex).trim();
1250
1481
  const { parameters, parseError } = this.parseParameters(parametersRaw);
@@ -1267,8 +1498,35 @@ var init_parser = __esm({
1267
1498
  this.lastReportedTextLength = 0;
1268
1499
  }
1269
1500
  }
1270
- // Finalize parsing and return remaining text
1501
+ // Finalize parsing and return remaining text or incomplete gadgets
1271
1502
  *finalize() {
1503
+ const startIndex = this.buffer.indexOf(this.startPrefix, this.lastReportedTextLength);
1504
+ if (startIndex !== -1) {
1505
+ const textBefore = this.takeTextUntil(startIndex);
1506
+ if (textBefore !== void 0) {
1507
+ yield { type: "text", content: textBefore };
1508
+ }
1509
+ const metadataStartIndex = startIndex + this.startPrefix.length;
1510
+ const metadataEndIndex = this.buffer.indexOf("\n", metadataStartIndex);
1511
+ if (metadataEndIndex !== -1) {
1512
+ const gadgetName = this.buffer.substring(metadataStartIndex, metadataEndIndex).trim();
1513
+ const { actualName: actualGadgetName, invocationId } = this.parseGadgetName(gadgetName);
1514
+ const contentStartIndex = metadataEndIndex + 1;
1515
+ const parametersRaw = this.buffer.substring(contentStartIndex).trim();
1516
+ const { parameters, parseError } = this.parseParameters(parametersRaw);
1517
+ yield {
1518
+ type: "gadget_call",
1519
+ call: {
1520
+ gadgetName: actualGadgetName,
1521
+ invocationId,
1522
+ parametersYaml: parametersRaw,
1523
+ parameters,
1524
+ parseError
1525
+ }
1526
+ };
1527
+ return;
1528
+ }
1529
+ }
1272
1530
  const remainingText = this.takeTextUntil(this.buffer.length);
1273
1531
  if (remainingText !== void 0) {
1274
1532
  yield { type: "text", content: remainingText };
@@ -1749,11 +2007,11 @@ var init_agent = __esm({
1749
2007
  role: message.role,
1750
2008
  content: message.content
1751
2009
  }));
1752
- this.conversation = new ConversationManager(
1753
- baseMessages,
1754
- initialMessages,
1755
- this.parameterFormat
1756
- );
2010
+ this.conversation = new ConversationManager(baseMessages, initialMessages, {
2011
+ parameterFormat: this.parameterFormat,
2012
+ startPrefix: options.gadgetStartPrefix,
2013
+ endPrefix: options.gadgetEndPrefix
2014
+ });
1757
2015
  this.userPromptProvided = !!options.userPrompt;
1758
2016
  if (options.userPrompt) {
1759
2017
  this.conversation.addUserMessage(options.userPrompt);
@@ -3256,10 +3514,11 @@ var init_gemini = __esm({
3256
3514
  return GEMINI_MODELS;
3257
3515
  }
3258
3516
  buildRequestPayload(options, descriptor, _spec, messages) {
3259
- const { systemInstruction, contents } = this.extractSystemAndContents(messages);
3517
+ const contents = this.convertMessagesToContents(messages);
3260
3518
  const generationConfig = this.buildGenerationConfig(options);
3261
3519
  const config = {
3262
- ...systemInstruction ? { systemInstruction: systemInstruction.parts.map((p) => p.text).join("\n") } : {},
3520
+ // Note: systemInstruction removed - it doesn't work with countTokens()
3521
+ // System messages are now included in contents as user+model exchanges
3263
3522
  ...generationConfig ? { ...generationConfig } : {},
3264
3523
  // Explicitly disable function calling to prevent UNEXPECTED_TOOL_CALL errors
3265
3524
  toolConfig: {
@@ -3280,31 +3539,37 @@ var init_gemini = __esm({
3280
3539
  const streamResponse = await client.models.generateContentStream(payload);
3281
3540
  return streamResponse;
3282
3541
  }
3283
- extractSystemAndContents(messages) {
3284
- const firstSystemIndex = messages.findIndex((message) => message.role === "system");
3285
- if (firstSystemIndex === -1) {
3286
- return {
3287
- systemInstruction: null,
3288
- contents: this.mergeConsecutiveMessages(messages)
3289
- };
3542
+ /**
3543
+ * Convert LLM messages to Gemini contents format.
3544
+ *
3545
+ * For Gemini, we convert system messages to user+model exchanges instead of
3546
+ * using systemInstruction, because:
3547
+ * 1. systemInstruction doesn't work with countTokens() API
3548
+ * 2. This approach gives perfect token counting accuracy (0% error)
3549
+ * 3. The model receives and follows system instructions identically
3550
+ *
3551
+ * System message: "You are a helpful assistant"
3552
+ * Becomes:
3553
+ * - User: "You are a helpful assistant"
3554
+ * - Model: "Understood."
3555
+ */
3556
+ convertMessagesToContents(messages) {
3557
+ const expandedMessages = [];
3558
+ for (const message of messages) {
3559
+ if (message.role === "system") {
3560
+ expandedMessages.push({
3561
+ role: "user",
3562
+ content: message.content
3563
+ });
3564
+ expandedMessages.push({
3565
+ role: "assistant",
3566
+ content: "Understood."
3567
+ });
3568
+ } else {
3569
+ expandedMessages.push(message);
3570
+ }
3290
3571
  }
3291
- let systemBlockEnd = firstSystemIndex;
3292
- while (systemBlockEnd < messages.length && messages[systemBlockEnd].role === "system") {
3293
- systemBlockEnd++;
3294
- }
3295
- const systemMessages = messages.slice(firstSystemIndex, systemBlockEnd);
3296
- const nonSystemMessages = [
3297
- ...messages.slice(0, firstSystemIndex),
3298
- ...messages.slice(systemBlockEnd)
3299
- ];
3300
- const systemInstruction = {
3301
- role: "system",
3302
- parts: systemMessages.map((message) => ({ text: message.content }))
3303
- };
3304
- return {
3305
- systemInstruction,
3306
- contents: this.mergeConsecutiveMessages(nonSystemMessages)
3307
- };
3572
+ return this.mergeConsecutiveMessages(expandedMessages);
3308
3573
  }
3309
3574
  mergeConsecutiveMessages(messages) {
3310
3575
  if (messages.length === 0) {
@@ -3393,8 +3658,8 @@ var init_gemini = __esm({
3393
3658
  *
3394
3659
  * This method provides accurate token estimation for Gemini models by:
3395
3660
  * - Using the SDK's countTokens() method
3396
- * - Properly extracting and handling system instructions
3397
- * - Transforming messages to Gemini's expected format
3661
+ * - Converting system messages to user+model exchanges (same as in generation)
3662
+ * - This gives perfect token counting accuracy (0% error vs actual usage)
3398
3663
  *
3399
3664
  * @param messages - The messages to count tokens for
3400
3665
  * @param descriptor - Model descriptor containing the model name
@@ -3413,16 +3678,14 @@ var init_gemini = __esm({
3413
3678
  */
3414
3679
  async countTokens(messages, descriptor, _spec) {
3415
3680
  const client = this.client;
3416
- const { systemInstruction, contents } = this.extractSystemAndContents(messages);
3417
- const request = {
3418
- model: descriptor.name,
3419
- contents: this.convertContentsForNewSDK(contents)
3420
- };
3421
- if (systemInstruction) {
3422
- request.systemInstruction = systemInstruction.parts.map((p) => p.text).join("\n");
3423
- }
3681
+ const contents = this.convertMessagesToContents(messages);
3424
3682
  try {
3425
- const response = await client.models.countTokens(request);
3683
+ const response = await client.models.countTokens({
3684
+ model: descriptor.name,
3685
+ contents: this.convertContentsForNewSDK(contents)
3686
+ // Note: systemInstruction not used - it's not supported by countTokens()
3687
+ // and would cause a 2100% token counting error
3688
+ });
3426
3689
  return response.totalTokens ?? 0;
3427
3690
  } catch (error) {
3428
3691
  console.warn(
@@ -5085,6 +5348,83 @@ function mergeDescriptions(schema, jsonSchema) {
5085
5348
 
5086
5349
  // src/gadgets/gadget.ts
5087
5350
  init_schema_validator();
5351
+ function formatYamlValue(value, indent = "") {
5352
+ if (typeof value === "string") {
5353
+ const lines = value.split("\n");
5354
+ if (lines.length === 1 && !value.includes(":") && !value.startsWith("-")) {
5355
+ return value;
5356
+ }
5357
+ const indentedLines = lines.map((line) => `${indent} ${line}`).join("\n");
5358
+ return `|
5359
+ ${indentedLines}`;
5360
+ }
5361
+ if (typeof value === "number" || typeof value === "boolean") {
5362
+ return String(value);
5363
+ }
5364
+ if (value === null || value === void 0) {
5365
+ return "null";
5366
+ }
5367
+ if (Array.isArray(value)) {
5368
+ if (value.length === 0) return "[]";
5369
+ const items = value.map((item) => `${indent}- ${formatYamlValue(item, indent + " ")}`);
5370
+ return "\n" + items.join("\n");
5371
+ }
5372
+ if (typeof value === "object") {
5373
+ const entries = Object.entries(value);
5374
+ if (entries.length === 0) return "{}";
5375
+ const lines = entries.map(([k, v]) => {
5376
+ const formattedValue = formatYamlValue(v, indent + " ");
5377
+ if (formattedValue.startsWith("\n") || formattedValue.startsWith("|")) {
5378
+ return `${indent}${k}: ${formattedValue}`;
5379
+ }
5380
+ return `${indent}${k}: ${formattedValue}`;
5381
+ });
5382
+ return "\n" + lines.join("\n");
5383
+ }
5384
+ return yaml2.dump(value).trimEnd();
5385
+ }
5386
+ function formatParamsAsYaml(params) {
5387
+ const lines = [];
5388
+ for (const [key, value] of Object.entries(params)) {
5389
+ const formattedValue = formatYamlValue(value, "");
5390
+ if (formattedValue.startsWith("\n")) {
5391
+ lines.push(`${key}:${formattedValue}`);
5392
+ } else {
5393
+ lines.push(`${key}: ${formattedValue}`);
5394
+ }
5395
+ }
5396
+ return lines.join("\n");
5397
+ }
5398
+ function formatTomlValue(value) {
5399
+ if (typeof value === "string") {
5400
+ if (value.includes("\n")) {
5401
+ return `"""
5402
+ ${value}
5403
+ """`;
5404
+ }
5405
+ return JSON.stringify(value);
5406
+ }
5407
+ if (typeof value === "number" || typeof value === "boolean") {
5408
+ return String(value);
5409
+ }
5410
+ if (value === null || value === void 0) {
5411
+ return '""';
5412
+ }
5413
+ if (Array.isArray(value)) {
5414
+ return JSON.stringify(value);
5415
+ }
5416
+ if (typeof value === "object") {
5417
+ return JSON.stringify(value);
5418
+ }
5419
+ return JSON.stringify(value);
5420
+ }
5421
+ function formatParamsAsToml(params) {
5422
+ const lines = [];
5423
+ for (const [key, value] of Object.entries(params)) {
5424
+ lines.push(`${key} = ${formatTomlValue(value)}`);
5425
+ }
5426
+ return lines.join("\n");
5427
+ }
5088
5428
  var BaseGadget = class {
5089
5429
  /**
5090
5430
  * The name of the gadget. Used for identification when LLM calls it.
@@ -5104,6 +5444,14 @@ var BaseGadget = class {
5104
5444
  * Set to 0 or undefined to disable timeout for this gadget.
5105
5445
  */
5106
5446
  timeoutMs;
5447
+ /**
5448
+ * Optional usage examples to help LLMs understand proper invocation.
5449
+ * Examples are rendered in getInstruction() alongside the schema.
5450
+ *
5451
+ * Note: Uses broader `unknown` type to allow typed examples from subclasses
5452
+ * while maintaining runtime compatibility.
5453
+ */
5454
+ examples;
5107
5455
  /**
5108
5456
  * Auto-generated instruction text for the LLM.
5109
5457
  * Combines name, description, and parameter schema into a formatted instruction.
@@ -5116,7 +5464,7 @@ var BaseGadget = class {
5116
5464
  * Generate instruction text for the LLM with format-specific schema.
5117
5465
  * Combines name, description, and parameter schema into a formatted instruction.
5118
5466
  *
5119
- * @param format - Format for the schema representation ('json' | 'yaml' | 'auto')
5467
+ * @param format - Format for the schema representation ('json' | 'yaml' | 'toml' | 'auto')
5120
5468
  * @returns Formatted instruction string
5121
5469
  */
5122
5470
  getInstruction(format = "json") {
@@ -5131,12 +5479,38 @@ var BaseGadget = class {
5131
5479
  if (format === "json" || format === "auto") {
5132
5480
  parts.push("\n\nInput Schema (JSON):");
5133
5481
  parts.push(JSON.stringify(jsonSchema, null, 2));
5482
+ } else if (format === "toml") {
5483
+ parts.push("\n\nInput Schema (TOML):");
5484
+ parts.push(JSON.stringify(jsonSchema, null, 2));
5134
5485
  } else {
5135
5486
  const yamlSchema = yaml2.dump(jsonSchema).trimEnd();
5136
5487
  parts.push("\n\nInput Schema (YAML):");
5137
5488
  parts.push(yamlSchema);
5138
5489
  }
5139
5490
  }
5491
+ if (this.examples && this.examples.length > 0) {
5492
+ parts.push("\n\nExamples:");
5493
+ this.examples.forEach((example, index) => {
5494
+ if (index > 0) {
5495
+ parts.push("");
5496
+ }
5497
+ if (example.comment) {
5498
+ parts.push(`# ${example.comment}`);
5499
+ }
5500
+ parts.push("Input:");
5501
+ if (format === "json" || format === "auto") {
5502
+ parts.push(JSON.stringify(example.params, null, 2));
5503
+ } else if (format === "toml") {
5504
+ parts.push(formatParamsAsToml(example.params));
5505
+ } else {
5506
+ parts.push(formatParamsAsYaml(example.params));
5507
+ }
5508
+ if (example.output !== void 0) {
5509
+ parts.push("Output:");
5510
+ parts.push(example.output);
5511
+ }
5512
+ });
5513
+ }
5140
5514
  return parts.join("\n");
5141
5515
  }
5142
5516
  };