llmist 1.0.0 → 1.2.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.
@@ -1446,247 +1446,242 @@ var init_hook_validators = __esm({
1446
1446
  }
1447
1447
  });
1448
1448
 
1449
- // src/gadgets/exceptions.ts
1450
- var BreakLoopException, HumanInputException, TimeoutException;
1451
- var init_exceptions = __esm({
1452
- "src/gadgets/exceptions.ts"() {
1453
- "use strict";
1454
- BreakLoopException = class extends Error {
1455
- constructor(message) {
1456
- super(message ?? "Agent loop terminated by gadget");
1457
- this.name = "BreakLoopException";
1458
- }
1459
- };
1460
- HumanInputException = class extends Error {
1461
- question;
1462
- constructor(question) {
1463
- super(`Human input required: ${question}`);
1464
- this.name = "HumanInputException";
1465
- this.question = question;
1466
- }
1467
- };
1468
- TimeoutException = class extends Error {
1469
- timeoutMs;
1470
- gadgetName;
1471
- constructor(gadgetName, timeoutMs) {
1472
- super(`Gadget '${gadgetName}' execution exceeded timeout of ${timeoutMs}ms`);
1473
- this.name = "TimeoutException";
1474
- this.gadgetName = gadgetName;
1475
- this.timeoutMs = timeoutMs;
1476
- }
1477
- };
1449
+ // src/gadgets/schema-introspector.ts
1450
+ function getDef(schema) {
1451
+ return schema._def;
1452
+ }
1453
+ function getTypeName(schema) {
1454
+ const def = getDef(schema);
1455
+ return def?.type ?? def?.typeName;
1456
+ }
1457
+ function getShape(schema) {
1458
+ const def = getDef(schema);
1459
+ if (typeof def?.shape === "function") {
1460
+ return def.shape();
1478
1461
  }
1479
- });
1480
-
1481
- // src/gadgets/executor.ts
1482
- var GadgetExecutor;
1483
- var init_executor = __esm({
1484
- "src/gadgets/executor.ts"() {
1462
+ return def?.shape;
1463
+ }
1464
+ var SchemaIntrospector;
1465
+ var init_schema_introspector = __esm({
1466
+ "src/gadgets/schema-introspector.ts"() {
1485
1467
  "use strict";
1486
- init_logger();
1487
- init_exceptions();
1488
- GadgetExecutor = class {
1489
- constructor(registry, onHumanInputRequired, logger, defaultGadgetTimeoutMs) {
1490
- this.registry = registry;
1491
- this.onHumanInputRequired = onHumanInputRequired;
1492
- this.defaultGadgetTimeoutMs = defaultGadgetTimeoutMs;
1493
- this.logger = logger ?? createLogger({ name: "llmist:executor" });
1468
+ SchemaIntrospector = class {
1469
+ schema;
1470
+ cache = /* @__PURE__ */ new Map();
1471
+ constructor(schema) {
1472
+ this.schema = schema;
1494
1473
  }
1495
- logger;
1496
1474
  /**
1497
- * Creates a promise that rejects with a TimeoutException after the specified timeout.
1475
+ * Get the expected type at a JSON pointer path.
1476
+ *
1477
+ * @param pointer - JSON pointer path without leading / (e.g., "config/timeout", "items/0")
1478
+ * @returns Type hint for coercion decision
1498
1479
  */
1499
- createTimeoutPromise(gadgetName, timeoutMs) {
1500
- return new Promise((_, reject) => {
1501
- setTimeout(() => {
1502
- reject(new TimeoutException(gadgetName, timeoutMs));
1503
- }, timeoutMs);
1504
- });
1480
+ getTypeAtPath(pointer) {
1481
+ const cached = this.cache.get(pointer);
1482
+ if (cached !== void 0) {
1483
+ return cached;
1484
+ }
1485
+ const result = this.resolveTypeAtPath(pointer);
1486
+ this.cache.set(pointer, result);
1487
+ return result;
1505
1488
  }
1506
- // Execute a gadget call asynchronously
1507
- async execute(call) {
1508
- const startTime = Date.now();
1509
- this.logger.debug("Executing gadget", {
1510
- gadgetName: call.gadgetName,
1511
- invocationId: call.invocationId,
1512
- parameters: call.parameters
1513
- });
1514
- const rawParameters = call.parameters ?? {};
1515
- let validatedParameters = rawParameters;
1516
- try {
1517
- const gadget = this.registry.get(call.gadgetName);
1518
- if (!gadget) {
1519
- this.logger.error("Gadget not found", { gadgetName: call.gadgetName });
1520
- return {
1521
- gadgetName: call.gadgetName,
1522
- invocationId: call.invocationId,
1523
- parameters: call.parameters ?? {},
1524
- error: `Gadget '${call.gadgetName}' not found in registry`,
1525
- executionTimeMs: Date.now() - startTime
1526
- };
1527
- }
1528
- if (call.parseError || !call.parameters) {
1529
- this.logger.error("Gadget parameter parse error", {
1530
- gadgetName: call.gadgetName,
1531
- parseError: call.parseError,
1532
- rawParameters: call.parametersRaw
1533
- });
1534
- return {
1535
- gadgetName: call.gadgetName,
1536
- invocationId: call.invocationId,
1537
- parameters: {},
1538
- error: call.parseError ?? "Failed to parse parameters",
1539
- executionTimeMs: Date.now() - startTime
1540
- };
1541
- }
1542
- if (gadget.parameterSchema) {
1543
- const validationResult = gadget.parameterSchema.safeParse(rawParameters);
1544
- if (!validationResult.success) {
1545
- const formattedIssues = validationResult.error.issues.map((issue) => {
1546
- const path = issue.path.join(".") || "root";
1547
- return `${path}: ${issue.message}`;
1548
- }).join("; ");
1549
- const validationError = `Invalid parameters: ${formattedIssues}`;
1550
- this.logger.error("Gadget parameter validation failed", {
1551
- gadgetName: call.gadgetName,
1552
- error: validationError
1553
- });
1554
- return {
1555
- gadgetName: call.gadgetName,
1556
- invocationId: call.invocationId,
1557
- parameters: rawParameters,
1558
- error: validationError,
1559
- executionTimeMs: Date.now() - startTime
1560
- };
1489
+ /**
1490
+ * Internal method to resolve type at path without caching.
1491
+ */
1492
+ resolveTypeAtPath(pointer) {
1493
+ if (!pointer) {
1494
+ return this.getBaseType(this.schema);
1495
+ }
1496
+ const segments = pointer.split("/");
1497
+ let current = this.schema;
1498
+ for (const segment of segments) {
1499
+ current = this.unwrapSchema(current);
1500
+ const typeName = getTypeName(current);
1501
+ if (typeName === "object" || typeName === "ZodObject") {
1502
+ const shape = getShape(current);
1503
+ if (!shape || !(segment in shape)) {
1504
+ return "unknown";
1561
1505
  }
1562
- validatedParameters = validationResult.data;
1563
- }
1564
- const timeoutMs = gadget.timeoutMs ?? this.defaultGadgetTimeoutMs;
1565
- let result;
1566
- if (timeoutMs && timeoutMs > 0) {
1567
- this.logger.debug("Executing gadget with timeout", {
1568
- gadgetName: call.gadgetName,
1569
- timeoutMs
1570
- });
1571
- result = await Promise.race([
1572
- Promise.resolve(gadget.execute(validatedParameters)),
1573
- this.createTimeoutPromise(call.gadgetName, timeoutMs)
1574
- ]);
1506
+ current = shape[segment];
1507
+ } else if (typeName === "array" || typeName === "ZodArray") {
1508
+ if (!/^\d+$/.test(segment)) {
1509
+ return "unknown";
1510
+ }
1511
+ const def = getDef(current);
1512
+ const elementType = def?.element ?? def?.type;
1513
+ if (!elementType) {
1514
+ return "unknown";
1515
+ }
1516
+ current = elementType;
1517
+ } else if (typeName === "tuple" || typeName === "ZodTuple") {
1518
+ if (!/^\d+$/.test(segment)) {
1519
+ return "unknown";
1520
+ }
1521
+ const index = parseInt(segment, 10);
1522
+ const def = getDef(current);
1523
+ const items = def?.items;
1524
+ if (!items || index >= items.length) {
1525
+ return "unknown";
1526
+ }
1527
+ current = items[index];
1528
+ } else if (typeName === "record" || typeName === "ZodRecord") {
1529
+ const def = getDef(current);
1530
+ const valueType = def?.valueType;
1531
+ if (!valueType) {
1532
+ return "unknown";
1533
+ }
1534
+ current = valueType;
1575
1535
  } else {
1576
- result = await Promise.resolve(gadget.execute(validatedParameters));
1536
+ return "unknown";
1577
1537
  }
1578
- const executionTimeMs = Date.now() - startTime;
1579
- this.logger.info("Gadget executed successfully", {
1580
- gadgetName: call.gadgetName,
1581
- invocationId: call.invocationId,
1582
- executionTimeMs
1583
- });
1584
- this.logger.debug("Gadget result", {
1585
- gadgetName: call.gadgetName,
1586
- invocationId: call.invocationId,
1587
- parameters: validatedParameters,
1588
- result,
1589
- executionTimeMs
1590
- });
1591
- return {
1592
- gadgetName: call.gadgetName,
1593
- invocationId: call.invocationId,
1594
- parameters: validatedParameters,
1595
- result,
1596
- executionTimeMs
1597
- };
1598
- } catch (error) {
1599
- if (error instanceof BreakLoopException) {
1600
- this.logger.info("Gadget requested loop termination", {
1601
- gadgetName: call.gadgetName,
1602
- message: error.message
1603
- });
1604
- return {
1605
- gadgetName: call.gadgetName,
1606
- invocationId: call.invocationId,
1607
- parameters: validatedParameters,
1608
- result: error.message,
1609
- breaksLoop: true,
1610
- executionTimeMs: Date.now() - startTime
1611
- };
1538
+ }
1539
+ return this.getBaseType(current);
1540
+ }
1541
+ /**
1542
+ * Unwrap schema modifiers (optional, default, nullable, branded, etc.)
1543
+ * to get to the underlying type.
1544
+ */
1545
+ unwrapSchema(schema) {
1546
+ let current = schema;
1547
+ let iterations = 0;
1548
+ const maxIterations = 20;
1549
+ while (iterations < maxIterations) {
1550
+ const typeName = getTypeName(current);
1551
+ const wrapperTypes = [
1552
+ "optional",
1553
+ "nullable",
1554
+ "default",
1555
+ "catch",
1556
+ "branded",
1557
+ "readonly",
1558
+ "pipeline",
1559
+ "ZodOptional",
1560
+ "ZodNullable",
1561
+ "ZodDefault",
1562
+ "ZodCatch",
1563
+ "ZodBranded",
1564
+ "ZodReadonly",
1565
+ "ZodPipeline"
1566
+ ];
1567
+ if (typeName && wrapperTypes.includes(typeName)) {
1568
+ const def = getDef(current);
1569
+ const inner = def?.innerType ?? def?.in ?? def?.type;
1570
+ if (!inner || inner === current) break;
1571
+ current = inner;
1572
+ iterations++;
1573
+ continue;
1612
1574
  }
1613
- if (error instanceof TimeoutException) {
1614
- this.logger.error("Gadget execution timed out", {
1615
- gadgetName: call.gadgetName,
1616
- timeoutMs: error.timeoutMs,
1617
- executionTimeMs: Date.now() - startTime
1618
- });
1619
- return {
1620
- gadgetName: call.gadgetName,
1621
- invocationId: call.invocationId,
1622
- parameters: validatedParameters,
1623
- error: error.message,
1624
- executionTimeMs: Date.now() - startTime
1625
- };
1575
+ break;
1576
+ }
1577
+ return current;
1578
+ }
1579
+ /**
1580
+ * Get the primitive type hint from an unwrapped schema.
1581
+ */
1582
+ getBaseType(schema) {
1583
+ const unwrapped = this.unwrapSchema(schema);
1584
+ const typeName = getTypeName(unwrapped);
1585
+ switch (typeName) {
1586
+ // Primitive types
1587
+ case "string":
1588
+ case "ZodString":
1589
+ return "string";
1590
+ case "number":
1591
+ case "ZodNumber":
1592
+ case "bigint":
1593
+ case "ZodBigInt":
1594
+ return "number";
1595
+ case "boolean":
1596
+ case "ZodBoolean":
1597
+ return "boolean";
1598
+ // Literal types - check the literal value type
1599
+ case "literal":
1600
+ case "ZodLiteral": {
1601
+ const def = getDef(unwrapped);
1602
+ const values = def?.values;
1603
+ const value = values?.[0] ?? def?.value;
1604
+ if (typeof value === "string") return "string";
1605
+ if (typeof value === "number" || typeof value === "bigint")
1606
+ return "number";
1607
+ if (typeof value === "boolean") return "boolean";
1608
+ return "unknown";
1626
1609
  }
1627
- if (error instanceof HumanInputException) {
1628
- this.logger.info("Gadget requested human input", {
1629
- gadgetName: call.gadgetName,
1630
- question: error.question
1631
- });
1632
- if (this.onHumanInputRequired) {
1633
- try {
1634
- const answer = await this.onHumanInputRequired(error.question);
1635
- this.logger.debug("Human input received", {
1636
- gadgetName: call.gadgetName,
1637
- answerLength: answer.length
1638
- });
1639
- return {
1640
- gadgetName: call.gadgetName,
1641
- invocationId: call.invocationId,
1642
- parameters: validatedParameters,
1643
- result: answer,
1644
- executionTimeMs: Date.now() - startTime
1645
- };
1646
- } catch (inputError) {
1647
- this.logger.error("Human input callback error", {
1648
- gadgetName: call.gadgetName,
1649
- error: inputError instanceof Error ? inputError.message : String(inputError)
1650
- });
1651
- return {
1652
- gadgetName: call.gadgetName,
1653
- invocationId: call.invocationId,
1654
- parameters: validatedParameters,
1655
- error: inputError instanceof Error ? inputError.message : String(inputError),
1656
- executionTimeMs: Date.now() - startTime
1657
- };
1658
- }
1659
- }
1660
- this.logger.warn("Human input required but no callback provided", {
1661
- gadgetName: call.gadgetName
1662
- });
1663
- return {
1664
- gadgetName: call.gadgetName,
1665
- invocationId: call.invocationId,
1666
- parameters: validatedParameters,
1667
- error: "Human input required but not available (stdin is not interactive)",
1668
- executionTimeMs: Date.now() - startTime
1669
- };
1610
+ // Enum - always string keys
1611
+ case "enum":
1612
+ case "ZodEnum":
1613
+ case "nativeEnum":
1614
+ case "ZodNativeEnum":
1615
+ return "string";
1616
+ // Union - return 'unknown' to let auto-coercion decide
1617
+ // Since multiple types are valid, we can't definitively say what the LLM intended
1618
+ // Auto-coercion will handle common cases (numbers, booleans) appropriately
1619
+ case "union":
1620
+ case "ZodUnion":
1621
+ return "unknown";
1622
+ // Discriminated union - complex, return unknown
1623
+ case "discriminatedUnion":
1624
+ case "ZodDiscriminatedUnion":
1625
+ return "unknown";
1626
+ // Intersection - check both sides
1627
+ case "intersection":
1628
+ case "ZodIntersection": {
1629
+ const def = getDef(unwrapped);
1630
+ const left = def?.left;
1631
+ const right = def?.right;
1632
+ if (!left || !right) return "unknown";
1633
+ const leftType = this.getBaseType(left);
1634
+ const rightType = this.getBaseType(right);
1635
+ if (leftType === rightType) return leftType;
1636
+ if (leftType === "string" || rightType === "string") return "string";
1637
+ return "unknown";
1670
1638
  }
1671
- const executionTimeMs = Date.now() - startTime;
1672
- this.logger.error("Gadget execution failed", {
1673
- gadgetName: call.gadgetName,
1674
- error: error instanceof Error ? error.message : String(error),
1675
- executionTimeMs
1676
- });
1677
- return {
1678
- gadgetName: call.gadgetName,
1679
- invocationId: call.invocationId,
1680
- parameters: validatedParameters,
1681
- error: error instanceof Error ? error.message : String(error),
1682
- executionTimeMs
1683
- };
1639
+ // Effects/transforms - return unknown to let Zod handle it
1640
+ case "effects":
1641
+ case "ZodEffects":
1642
+ return "unknown";
1643
+ // Lazy - can't resolve without evaluating
1644
+ case "lazy":
1645
+ case "ZodLazy":
1646
+ return "unknown";
1647
+ // Complex types - return unknown
1648
+ case "object":
1649
+ case "ZodObject":
1650
+ case "array":
1651
+ case "ZodArray":
1652
+ case "tuple":
1653
+ case "ZodTuple":
1654
+ case "record":
1655
+ case "ZodRecord":
1656
+ case "map":
1657
+ case "ZodMap":
1658
+ case "set":
1659
+ case "ZodSet":
1660
+ case "function":
1661
+ case "ZodFunction":
1662
+ case "promise":
1663
+ case "ZodPromise":
1664
+ case "date":
1665
+ case "ZodDate":
1666
+ return "unknown";
1667
+ // Unknown/any/never/void/undefined/null
1668
+ case "unknown":
1669
+ case "ZodUnknown":
1670
+ case "any":
1671
+ case "ZodAny":
1672
+ case "never":
1673
+ case "ZodNever":
1674
+ case "void":
1675
+ case "ZodVoid":
1676
+ case "undefined":
1677
+ case "ZodUndefined":
1678
+ case "null":
1679
+ case "ZodNull":
1680
+ return "unknown";
1681
+ default:
1682
+ return "unknown";
1684
1683
  }
1685
1684
  }
1686
- // Execute multiple gadget calls in parallel
1687
- async executeAll(calls) {
1688
- return Promise.all(calls.map((call) => this.execute(call)));
1689
- }
1690
1685
  };
1691
1686
  }
1692
1687
  });
@@ -1696,6 +1691,7 @@ function parseBlockParams(content, options) {
1696
1691
  const argPrefix = options?.argPrefix ?? GADGET_ARG_PREFIX;
1697
1692
  const result = {};
1698
1693
  const seenPointers = /* @__PURE__ */ new Set();
1694
+ const introspector = options?.schema ? new SchemaIntrospector(options.schema) : void 0;
1699
1695
  const parts = content.split(argPrefix);
1700
1696
  for (let i = 1; i < parts.length; i++) {
1701
1697
  const part = parts[i];
@@ -1707,7 +1703,7 @@ function parseBlockParams(content, options) {
1707
1703
  throw new Error(`Duplicate pointer: ${pointer2}`);
1708
1704
  }
1709
1705
  seenPointers.add(pointer2);
1710
- setByPointer(result, pointer2, "");
1706
+ setByPointer(result, pointer2, "", introspector);
1711
1707
  }
1712
1708
  continue;
1713
1709
  }
@@ -1723,15 +1719,30 @@ function parseBlockParams(content, options) {
1723
1719
  throw new Error(`Duplicate pointer: ${pointer}`);
1724
1720
  }
1725
1721
  seenPointers.add(pointer);
1726
- setByPointer(result, pointer, value);
1722
+ setByPointer(result, pointer, value, introspector);
1727
1723
  }
1728
1724
  return result;
1729
1725
  }
1730
- function coerceValue(value) {
1726
+ function coerceValue(value, expectedType) {
1731
1727
  if (value.includes("\n")) {
1732
1728
  return value;
1733
1729
  }
1734
1730
  const trimmed = value.trim();
1731
+ if (expectedType === "string") {
1732
+ return value;
1733
+ }
1734
+ if (expectedType === "boolean") {
1735
+ if (trimmed === "true") return true;
1736
+ if (trimmed === "false") return false;
1737
+ return value;
1738
+ }
1739
+ if (expectedType === "number") {
1740
+ const num = Number(trimmed);
1741
+ if (!isNaN(num) && isFinite(num) && trimmed !== "") {
1742
+ return num;
1743
+ }
1744
+ return value;
1745
+ }
1735
1746
  if (trimmed === "true") return true;
1736
1747
  if (trimmed === "false") return false;
1737
1748
  if (trimmed !== "" && /^-?\d+(\.\d+)?$/.test(trimmed)) {
@@ -1742,7 +1753,7 @@ function coerceValue(value) {
1742
1753
  }
1743
1754
  return value;
1744
1755
  }
1745
- function setByPointer(obj, pointer, value) {
1756
+ function setByPointer(obj, pointer, value, introspector) {
1746
1757
  const segments = pointer.split("/");
1747
1758
  let current = obj;
1748
1759
  for (let i = 0; i < segments.length - 1; i++) {
@@ -1770,7 +1781,8 @@ function setByPointer(obj, pointer, value) {
1770
1781
  }
1771
1782
  }
1772
1783
  const lastSegment = segments[segments.length - 1];
1773
- const coercedValue = coerceValue(value);
1784
+ const expectedType = introspector?.getTypeAtPath(pointer);
1785
+ const coercedValue = coerceValue(value, expectedType);
1774
1786
  if (Array.isArray(current)) {
1775
1787
  const index = parseInt(lastSegment, 10);
1776
1788
  if (isNaN(index) || index < 0) {
@@ -1783,11 +1795,127 @@ function setByPointer(obj, pointer, value) {
1783
1795
  } else {
1784
1796
  current[lastSegment] = coercedValue;
1785
1797
  }
1786
- }
1787
- var init_block_params = __esm({
1788
- "src/gadgets/block-params.ts"() {
1798
+ }
1799
+ var init_block_params = __esm({
1800
+ "src/gadgets/block-params.ts"() {
1801
+ "use strict";
1802
+ init_constants();
1803
+ init_schema_introspector();
1804
+ }
1805
+ });
1806
+
1807
+ // src/gadgets/error-formatter.ts
1808
+ var GadgetErrorFormatter;
1809
+ var init_error_formatter = __esm({
1810
+ "src/gadgets/error-formatter.ts"() {
1811
+ "use strict";
1812
+ init_constants();
1813
+ GadgetErrorFormatter = class {
1814
+ argPrefix;
1815
+ startPrefix;
1816
+ endPrefix;
1817
+ constructor(options = {}) {
1818
+ this.argPrefix = options.argPrefix ?? GADGET_ARG_PREFIX;
1819
+ this.startPrefix = options.startPrefix ?? GADGET_START_PREFIX;
1820
+ this.endPrefix = options.endPrefix ?? GADGET_END_PREFIX;
1821
+ }
1822
+ /**
1823
+ * Format a Zod validation error with full gadget instructions.
1824
+ *
1825
+ * @param gadgetName - Name of the gadget that was called
1826
+ * @param zodError - The Zod validation error
1827
+ * @param gadget - The gadget instance (for generating instructions)
1828
+ * @returns Formatted error message with usage instructions
1829
+ */
1830
+ formatValidationError(gadgetName, zodError, gadget) {
1831
+ const parts = [];
1832
+ parts.push(`Error: Invalid parameters for '${gadgetName}':`);
1833
+ for (const issue of zodError.issues) {
1834
+ const path = issue.path.join(".") || "root";
1835
+ parts.push(` - ${path}: ${issue.message}`);
1836
+ }
1837
+ parts.push("");
1838
+ parts.push("Gadget Usage:");
1839
+ parts.push(gadget.getInstruction(this.argPrefix));
1840
+ return parts.join("\n");
1841
+ }
1842
+ /**
1843
+ * Format a parse error with block format reference.
1844
+ *
1845
+ * @param gadgetName - Name of the gadget that was called
1846
+ * @param parseError - The parse error message
1847
+ * @param gadget - The gadget instance if found (for generating instructions)
1848
+ * @returns Formatted error message with format reference
1849
+ */
1850
+ formatParseError(gadgetName, parseError, gadget) {
1851
+ const parts = [];
1852
+ parts.push(`Error: Failed to parse parameters for '${gadgetName}':`);
1853
+ parts.push(` ${parseError}`);
1854
+ if (gadget) {
1855
+ parts.push("");
1856
+ parts.push("Gadget Usage:");
1857
+ parts.push(gadget.getInstruction(this.argPrefix));
1858
+ }
1859
+ parts.push("");
1860
+ parts.push("Block Format Reference:");
1861
+ parts.push(` ${this.startPrefix}${gadgetName}`);
1862
+ parts.push(` ${this.argPrefix}parameterName`);
1863
+ parts.push(" parameter value here");
1864
+ parts.push(` ${this.endPrefix}`);
1865
+ return parts.join("\n");
1866
+ }
1867
+ /**
1868
+ * Format a registry error (gadget not found) with available gadgets list.
1869
+ *
1870
+ * @param gadgetName - Name of the gadget that was not found
1871
+ * @param availableGadgets - List of available gadget names
1872
+ * @returns Formatted error message with available gadgets
1873
+ */
1874
+ formatRegistryError(gadgetName, availableGadgets) {
1875
+ const parts = [];
1876
+ parts.push(`Error: Gadget '${gadgetName}' not found.`);
1877
+ if (availableGadgets.length > 0) {
1878
+ parts.push("");
1879
+ parts.push(`Available gadgets: ${availableGadgets.join(", ")}`);
1880
+ } else {
1881
+ parts.push("");
1882
+ parts.push("No gadgets are currently registered.");
1883
+ }
1884
+ return parts.join("\n");
1885
+ }
1886
+ };
1887
+ }
1888
+ });
1889
+
1890
+ // src/gadgets/exceptions.ts
1891
+ var BreakLoopException, HumanInputException, TimeoutException;
1892
+ var init_exceptions = __esm({
1893
+ "src/gadgets/exceptions.ts"() {
1789
1894
  "use strict";
1790
- init_constants();
1895
+ BreakLoopException = class extends Error {
1896
+ constructor(message) {
1897
+ super(message ?? "Agent loop terminated by gadget");
1898
+ this.name = "BreakLoopException";
1899
+ }
1900
+ };
1901
+ HumanInputException = class extends Error {
1902
+ question;
1903
+ constructor(question) {
1904
+ super(`Human input required: ${question}`);
1905
+ this.name = "HumanInputException";
1906
+ this.question = question;
1907
+ }
1908
+ };
1909
+ TimeoutException = class extends Error {
1910
+ timeoutMs;
1911
+ gadgetName;
1912
+ constructor(gadgetName, timeoutMs) {
1913
+ super(`Gadget '${gadgetName}' execution exceeded timeout of ${timeoutMs}ms`);
1914
+ this.name = "TimeoutException";
1915
+ this.gadgetName = gadgetName;
1916
+ this.timeoutMs = timeoutMs;
1917
+ }
1918
+ };
1791
1919
  }
1792
1920
  });
1793
1921
 
@@ -1838,17 +1966,12 @@ var init_parser = __esm({
1838
1966
  return { actualName: gadgetName, invocationId: `gadget_${++globalInvocationCounter}` };
1839
1967
  }
1840
1968
  /**
1841
- * Truncate verbose parse errors to avoid context overflow.
1842
- * Keeps first meaningful line and limits total length.
1969
+ * Extract the error message from a parse error.
1970
+ * Preserves full message since the error formatter adds contextual help
1971
+ * that benefits from precise, detailed error information.
1843
1972
  */
1844
- truncateParseError(error, format) {
1845
- const message = error instanceof Error ? error.message : String(error);
1846
- const firstLine = message.split("\n")[0];
1847
- const maxLen = 200;
1848
- if (firstLine.length <= maxLen) {
1849
- return firstLine;
1850
- }
1851
- return `${firstLine.slice(0, maxLen)}... (${message.length} chars total)`;
1973
+ extractParseError(error) {
1974
+ return error instanceof Error ? error.message : String(error);
1852
1975
  }
1853
1976
  /**
1854
1977
  * Parse parameter string using block format
@@ -1858,7 +1981,7 @@ var init_parser = __esm({
1858
1981
  try {
1859
1982
  return { parameters: parseBlockParams(cleaned, { argPrefix: this.argPrefix }) };
1860
1983
  } catch (error) {
1861
- return { parseError: this.truncateParseError(error, "block") };
1984
+ return { parseError: this.extractParseError(error) };
1862
1985
  }
1863
1986
  }
1864
1987
  // Feed a chunk of text and get parsed events
@@ -1973,6 +2096,283 @@ var init_parser = __esm({
1973
2096
  }
1974
2097
  });
1975
2098
 
2099
+ // src/gadgets/executor.ts
2100
+ var GadgetExecutor;
2101
+ var init_executor = __esm({
2102
+ "src/gadgets/executor.ts"() {
2103
+ "use strict";
2104
+ init_constants();
2105
+ init_logger();
2106
+ init_block_params();
2107
+ init_error_formatter();
2108
+ init_exceptions();
2109
+ init_parser();
2110
+ GadgetExecutor = class {
2111
+ constructor(registry, onHumanInputRequired, logger, defaultGadgetTimeoutMs, errorFormatterOptions) {
2112
+ this.registry = registry;
2113
+ this.onHumanInputRequired = onHumanInputRequired;
2114
+ this.defaultGadgetTimeoutMs = defaultGadgetTimeoutMs;
2115
+ this.logger = logger ?? createLogger({ name: "llmist:executor" });
2116
+ this.errorFormatter = new GadgetErrorFormatter(errorFormatterOptions);
2117
+ this.argPrefix = errorFormatterOptions?.argPrefix ?? GADGET_ARG_PREFIX;
2118
+ }
2119
+ logger;
2120
+ errorFormatter;
2121
+ argPrefix;
2122
+ /**
2123
+ * Creates a promise that rejects with a TimeoutException after the specified timeout.
2124
+ */
2125
+ createTimeoutPromise(gadgetName, timeoutMs) {
2126
+ return new Promise((_, reject) => {
2127
+ setTimeout(() => {
2128
+ reject(new TimeoutException(gadgetName, timeoutMs));
2129
+ }, timeoutMs);
2130
+ });
2131
+ }
2132
+ // Execute a gadget call asynchronously
2133
+ async execute(call) {
2134
+ const startTime = Date.now();
2135
+ this.logger.debug("Executing gadget", {
2136
+ gadgetName: call.gadgetName,
2137
+ invocationId: call.invocationId,
2138
+ parameters: call.parameters
2139
+ });
2140
+ const rawParameters = call.parameters ?? {};
2141
+ let validatedParameters = rawParameters;
2142
+ try {
2143
+ const gadget = this.registry.get(call.gadgetName);
2144
+ if (!gadget) {
2145
+ this.logger.error("Gadget not found", { gadgetName: call.gadgetName });
2146
+ const availableGadgets = this.registry.getNames();
2147
+ return {
2148
+ gadgetName: call.gadgetName,
2149
+ invocationId: call.invocationId,
2150
+ parameters: call.parameters ?? {},
2151
+ error: this.errorFormatter.formatRegistryError(call.gadgetName, availableGadgets),
2152
+ executionTimeMs: Date.now() - startTime
2153
+ };
2154
+ }
2155
+ if (call.parseError || !call.parameters) {
2156
+ this.logger.error("Gadget parameter parse error", {
2157
+ gadgetName: call.gadgetName,
2158
+ parseError: call.parseError,
2159
+ rawParameters: call.parametersRaw
2160
+ });
2161
+ const parseErrorMessage = call.parseError ?? "Failed to parse parameters";
2162
+ return {
2163
+ gadgetName: call.gadgetName,
2164
+ invocationId: call.invocationId,
2165
+ parameters: {},
2166
+ error: this.errorFormatter.formatParseError(call.gadgetName, parseErrorMessage, gadget),
2167
+ executionTimeMs: Date.now() - startTime
2168
+ };
2169
+ }
2170
+ let schemaAwareParameters = rawParameters;
2171
+ const hasBlockFormat = call.parametersRaw?.includes(this.argPrefix);
2172
+ if (gadget.parameterSchema && hasBlockFormat) {
2173
+ try {
2174
+ const cleanedRaw = stripMarkdownFences(call.parametersRaw);
2175
+ const initialParse = parseBlockParams(cleanedRaw, { argPrefix: this.argPrefix });
2176
+ const parametersWereModified = !this.deepEquals(rawParameters, initialParse);
2177
+ if (parametersWereModified) {
2178
+ this.logger.debug("Parameters modified by interceptor, skipping re-parse", {
2179
+ gadgetName: call.gadgetName
2180
+ });
2181
+ schemaAwareParameters = rawParameters;
2182
+ } else {
2183
+ schemaAwareParameters = parseBlockParams(cleanedRaw, {
2184
+ argPrefix: this.argPrefix,
2185
+ schema: gadget.parameterSchema
2186
+ });
2187
+ this.logger.debug("Re-parsed parameters with schema", {
2188
+ gadgetName: call.gadgetName,
2189
+ original: rawParameters,
2190
+ schemaAware: schemaAwareParameters
2191
+ });
2192
+ }
2193
+ } catch (error) {
2194
+ this.logger.warn("Schema-aware re-parsing failed, using original parameters", {
2195
+ gadgetName: call.gadgetName,
2196
+ error: error instanceof Error ? error.message : String(error)
2197
+ });
2198
+ schemaAwareParameters = rawParameters;
2199
+ }
2200
+ }
2201
+ if (gadget.parameterSchema) {
2202
+ const validationResult = gadget.parameterSchema.safeParse(schemaAwareParameters);
2203
+ if (!validationResult.success) {
2204
+ const validationError = this.errorFormatter.formatValidationError(
2205
+ call.gadgetName,
2206
+ validationResult.error,
2207
+ gadget
2208
+ );
2209
+ this.logger.error("Gadget parameter validation failed", {
2210
+ gadgetName: call.gadgetName,
2211
+ issueCount: validationResult.error.issues.length
2212
+ });
2213
+ return {
2214
+ gadgetName: call.gadgetName,
2215
+ invocationId: call.invocationId,
2216
+ parameters: schemaAwareParameters,
2217
+ error: validationError,
2218
+ executionTimeMs: Date.now() - startTime
2219
+ };
2220
+ }
2221
+ validatedParameters = validationResult.data;
2222
+ } else {
2223
+ validatedParameters = schemaAwareParameters;
2224
+ }
2225
+ const timeoutMs = gadget.timeoutMs ?? this.defaultGadgetTimeoutMs;
2226
+ let result;
2227
+ if (timeoutMs && timeoutMs > 0) {
2228
+ this.logger.debug("Executing gadget with timeout", {
2229
+ gadgetName: call.gadgetName,
2230
+ timeoutMs
2231
+ });
2232
+ result = await Promise.race([
2233
+ Promise.resolve(gadget.execute(validatedParameters)),
2234
+ this.createTimeoutPromise(call.gadgetName, timeoutMs)
2235
+ ]);
2236
+ } else {
2237
+ result = await Promise.resolve(gadget.execute(validatedParameters));
2238
+ }
2239
+ const executionTimeMs = Date.now() - startTime;
2240
+ this.logger.info("Gadget executed successfully", {
2241
+ gadgetName: call.gadgetName,
2242
+ invocationId: call.invocationId,
2243
+ executionTimeMs
2244
+ });
2245
+ this.logger.debug("Gadget result", {
2246
+ gadgetName: call.gadgetName,
2247
+ invocationId: call.invocationId,
2248
+ parameters: validatedParameters,
2249
+ result,
2250
+ executionTimeMs
2251
+ });
2252
+ return {
2253
+ gadgetName: call.gadgetName,
2254
+ invocationId: call.invocationId,
2255
+ parameters: validatedParameters,
2256
+ result,
2257
+ executionTimeMs
2258
+ };
2259
+ } catch (error) {
2260
+ if (error instanceof BreakLoopException) {
2261
+ this.logger.info("Gadget requested loop termination", {
2262
+ gadgetName: call.gadgetName,
2263
+ message: error.message
2264
+ });
2265
+ return {
2266
+ gadgetName: call.gadgetName,
2267
+ invocationId: call.invocationId,
2268
+ parameters: validatedParameters,
2269
+ result: error.message,
2270
+ breaksLoop: true,
2271
+ executionTimeMs: Date.now() - startTime
2272
+ };
2273
+ }
2274
+ if (error instanceof TimeoutException) {
2275
+ this.logger.error("Gadget execution timed out", {
2276
+ gadgetName: call.gadgetName,
2277
+ timeoutMs: error.timeoutMs,
2278
+ executionTimeMs: Date.now() - startTime
2279
+ });
2280
+ return {
2281
+ gadgetName: call.gadgetName,
2282
+ invocationId: call.invocationId,
2283
+ parameters: validatedParameters,
2284
+ error: error.message,
2285
+ executionTimeMs: Date.now() - startTime
2286
+ };
2287
+ }
2288
+ if (error instanceof HumanInputException) {
2289
+ this.logger.info("Gadget requested human input", {
2290
+ gadgetName: call.gadgetName,
2291
+ question: error.question
2292
+ });
2293
+ if (this.onHumanInputRequired) {
2294
+ try {
2295
+ const answer = await this.onHumanInputRequired(error.question);
2296
+ this.logger.debug("Human input received", {
2297
+ gadgetName: call.gadgetName,
2298
+ answerLength: answer.length
2299
+ });
2300
+ return {
2301
+ gadgetName: call.gadgetName,
2302
+ invocationId: call.invocationId,
2303
+ parameters: validatedParameters,
2304
+ result: answer,
2305
+ executionTimeMs: Date.now() - startTime
2306
+ };
2307
+ } catch (inputError) {
2308
+ this.logger.error("Human input callback error", {
2309
+ gadgetName: call.gadgetName,
2310
+ error: inputError instanceof Error ? inputError.message : String(inputError)
2311
+ });
2312
+ return {
2313
+ gadgetName: call.gadgetName,
2314
+ invocationId: call.invocationId,
2315
+ parameters: validatedParameters,
2316
+ error: inputError instanceof Error ? inputError.message : String(inputError),
2317
+ executionTimeMs: Date.now() - startTime
2318
+ };
2319
+ }
2320
+ }
2321
+ this.logger.warn("Human input required but no callback provided", {
2322
+ gadgetName: call.gadgetName
2323
+ });
2324
+ return {
2325
+ gadgetName: call.gadgetName,
2326
+ invocationId: call.invocationId,
2327
+ parameters: validatedParameters,
2328
+ error: "Human input required but not available (stdin is not interactive)",
2329
+ executionTimeMs: Date.now() - startTime
2330
+ };
2331
+ }
2332
+ const executionTimeMs = Date.now() - startTime;
2333
+ this.logger.error("Gadget execution failed", {
2334
+ gadgetName: call.gadgetName,
2335
+ error: error instanceof Error ? error.message : String(error),
2336
+ executionTimeMs
2337
+ });
2338
+ return {
2339
+ gadgetName: call.gadgetName,
2340
+ invocationId: call.invocationId,
2341
+ parameters: validatedParameters,
2342
+ error: error instanceof Error ? error.message : String(error),
2343
+ executionTimeMs
2344
+ };
2345
+ }
2346
+ }
2347
+ // Execute multiple gadget calls in parallel
2348
+ async executeAll(calls) {
2349
+ return Promise.all(calls.map((call) => this.execute(call)));
2350
+ }
2351
+ /**
2352
+ * Deep equality check for objects/arrays.
2353
+ * Used to detect if parameters were modified by an interceptor.
2354
+ */
2355
+ deepEquals(a, b) {
2356
+ if (a === b) return true;
2357
+ if (a === null || b === null) return a === b;
2358
+ if (typeof a !== typeof b) return false;
2359
+ if (typeof a !== "object") return a === b;
2360
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
2361
+ if (Array.isArray(a) && Array.isArray(b)) {
2362
+ if (a.length !== b.length) return false;
2363
+ return a.every((val, i) => this.deepEquals(val, b[i]));
2364
+ }
2365
+ const aObj = a;
2366
+ const bObj = b;
2367
+ const aKeys = Object.keys(aObj);
2368
+ const bKeys = Object.keys(bObj);
2369
+ if (aKeys.length !== bKeys.length) return false;
2370
+ return aKeys.every((key) => this.deepEquals(aObj[key], bObj[key]));
2371
+ }
2372
+ };
2373
+ }
2374
+ });
2375
+
1976
2376
  // src/agent/stream-processor.ts
1977
2377
  var StreamProcessor;
1978
2378
  var init_stream_processor = __esm({
@@ -2010,7 +2410,8 @@ var init_stream_processor = __esm({
2010
2410
  options.registry,
2011
2411
  options.onHumanInputRequired,
2012
2412
  this.logger.getSubLogger({ name: "executor" }),
2013
- options.defaultGadgetTimeoutMs
2413
+ options.defaultGadgetTimeoutMs,
2414
+ { argPrefix: options.gadgetArgPrefix }
2014
2415
  );
2015
2416
  }
2016
2417
  /**