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