llmist 1.1.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,6 +1493,364 @@ var init_hook_validators = __esm({
1493
1493
  }
1494
1494
  });
1495
1495
 
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();
1508
+ }
1509
+ return def?.shape;
1510
+ }
1511
+ var SchemaIntrospector;
1512
+ var init_schema_introspector = __esm({
1513
+ "src/gadgets/schema-introspector.ts"() {
1514
+ "use strict";
1515
+ SchemaIntrospector = class {
1516
+ schema;
1517
+ cache = /* @__PURE__ */ new Map();
1518
+ constructor(schema) {
1519
+ this.schema = schema;
1520
+ }
1521
+ /**
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
1526
+ */
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;
1535
+ }
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";
1552
+ }
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;
1582
+ } else {
1583
+ return "unknown";
1584
+ }
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;
1621
+ }
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";
1656
+ }
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";
1685
+ }
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";
1730
+ }
1731
+ }
1732
+ };
1733
+ }
1734
+ });
1735
+
1736
+ // src/gadgets/block-params.ts
1737
+ function parseBlockParams(content, options) {
1738
+ const argPrefix = options?.argPrefix ?? GADGET_ARG_PREFIX;
1739
+ const result = {};
1740
+ const seenPointers = /* @__PURE__ */ new Set();
1741
+ const introspector = options?.schema ? new SchemaIntrospector(options.schema) : void 0;
1742
+ const parts = content.split(argPrefix);
1743
+ for (let i = 1; i < parts.length; i++) {
1744
+ const part = parts[i];
1745
+ const newlineIndex = part.indexOf("\n");
1746
+ if (newlineIndex === -1) {
1747
+ const pointer2 = part.trim();
1748
+ if (pointer2) {
1749
+ if (seenPointers.has(pointer2)) {
1750
+ throw new Error(`Duplicate pointer: ${pointer2}`);
1751
+ }
1752
+ seenPointers.add(pointer2);
1753
+ setByPointer(result, pointer2, "", introspector);
1754
+ }
1755
+ continue;
1756
+ }
1757
+ const pointer = part.substring(0, newlineIndex).trim();
1758
+ let value = part.substring(newlineIndex + 1);
1759
+ if (value.endsWith("\n")) {
1760
+ value = value.slice(0, -1);
1761
+ }
1762
+ if (!pointer) {
1763
+ continue;
1764
+ }
1765
+ if (seenPointers.has(pointer)) {
1766
+ throw new Error(`Duplicate pointer: ${pointer}`);
1767
+ }
1768
+ seenPointers.add(pointer);
1769
+ setByPointer(result, pointer, value, introspector);
1770
+ }
1771
+ return result;
1772
+ }
1773
+ function coerceValue(value, expectedType) {
1774
+ if (value.includes("\n")) {
1775
+ return value;
1776
+ }
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
+ }
1793
+ if (trimmed === "true") return true;
1794
+ if (trimmed === "false") return false;
1795
+ if (trimmed !== "" && /^-?\d+(\.\d+)?$/.test(trimmed)) {
1796
+ const num = Number(trimmed);
1797
+ if (!isNaN(num) && isFinite(num)) {
1798
+ return num;
1799
+ }
1800
+ }
1801
+ return value;
1802
+ }
1803
+ function setByPointer(obj, pointer, value, introspector) {
1804
+ const segments = pointer.split("/");
1805
+ let current = obj;
1806
+ for (let i = 0; i < segments.length - 1; i++) {
1807
+ const segment = segments[i];
1808
+ const nextSegment = segments[i + 1];
1809
+ const nextIsArrayIndex = /^\d+$/.test(nextSegment);
1810
+ if (Array.isArray(current)) {
1811
+ const index = parseInt(segment, 10);
1812
+ if (isNaN(index) || index < 0) {
1813
+ throw new Error(`Invalid array index: ${segment}`);
1814
+ }
1815
+ if (index > current.length) {
1816
+ throw new Error(`Array index gap: expected ${current.length}, got ${index}`);
1817
+ }
1818
+ if (current[index] === void 0) {
1819
+ current[index] = nextIsArrayIndex ? [] : {};
1820
+ }
1821
+ current = current[index];
1822
+ } else {
1823
+ const rec = current;
1824
+ if (rec[segment] === void 0) {
1825
+ rec[segment] = nextIsArrayIndex ? [] : {};
1826
+ }
1827
+ current = rec[segment];
1828
+ }
1829
+ }
1830
+ const lastSegment = segments[segments.length - 1];
1831
+ const expectedType = introspector?.getTypeAtPath(pointer);
1832
+ const coercedValue = coerceValue(value, expectedType);
1833
+ if (Array.isArray(current)) {
1834
+ const index = parseInt(lastSegment, 10);
1835
+ if (isNaN(index) || index < 0) {
1836
+ throw new Error(`Invalid array index: ${lastSegment}`);
1837
+ }
1838
+ if (index > current.length) {
1839
+ throw new Error(`Array index gap: expected ${current.length}, got ${index}`);
1840
+ }
1841
+ current[index] = coercedValue;
1842
+ } else {
1843
+ current[lastSegment] = coercedValue;
1844
+ }
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
+
1496
1854
  // src/gadgets/error-formatter.ts
1497
1855
  var GadgetErrorFormatter;
1498
1856
  var init_error_formatter = __esm({
@@ -1604,325 +1962,7 @@ var init_exceptions = __esm({
1604
1962
  this.gadgetName = gadgetName;
1605
1963
  this.timeoutMs = timeoutMs;
1606
1964
  }
1607
- };
1608
- }
1609
- });
1610
-
1611
- // src/gadgets/executor.ts
1612
- var GadgetExecutor;
1613
- var init_executor = __esm({
1614
- "src/gadgets/executor.ts"() {
1615
- "use strict";
1616
- init_logger();
1617
- init_error_formatter();
1618
- init_exceptions();
1619
- GadgetExecutor = class {
1620
- constructor(registry, onHumanInputRequired, logger, defaultGadgetTimeoutMs, errorFormatterOptions) {
1621
- this.registry = registry;
1622
- this.onHumanInputRequired = onHumanInputRequired;
1623
- this.defaultGadgetTimeoutMs = defaultGadgetTimeoutMs;
1624
- this.logger = logger ?? createLogger({ name: "llmist:executor" });
1625
- this.errorFormatter = new GadgetErrorFormatter(errorFormatterOptions);
1626
- }
1627
- logger;
1628
- errorFormatter;
1629
- /**
1630
- * Creates a promise that rejects with a TimeoutException after the specified timeout.
1631
- */
1632
- createTimeoutPromise(gadgetName, timeoutMs) {
1633
- return new Promise((_, reject) => {
1634
- setTimeout(() => {
1635
- reject(new TimeoutException(gadgetName, timeoutMs));
1636
- }, timeoutMs);
1637
- });
1638
- }
1639
- // Execute a gadget call asynchronously
1640
- async execute(call) {
1641
- const startTime = Date.now();
1642
- this.logger.debug("Executing gadget", {
1643
- gadgetName: call.gadgetName,
1644
- invocationId: call.invocationId,
1645
- parameters: call.parameters
1646
- });
1647
- const rawParameters = call.parameters ?? {};
1648
- let validatedParameters = rawParameters;
1649
- try {
1650
- const gadget = this.registry.get(call.gadgetName);
1651
- if (!gadget) {
1652
- this.logger.error("Gadget not found", { gadgetName: call.gadgetName });
1653
- const availableGadgets = this.registry.getNames();
1654
- return {
1655
- gadgetName: call.gadgetName,
1656
- invocationId: call.invocationId,
1657
- parameters: call.parameters ?? {},
1658
- error: this.errorFormatter.formatRegistryError(call.gadgetName, availableGadgets),
1659
- executionTimeMs: Date.now() - startTime
1660
- };
1661
- }
1662
- if (call.parseError || !call.parameters) {
1663
- this.logger.error("Gadget parameter parse error", {
1664
- gadgetName: call.gadgetName,
1665
- parseError: call.parseError,
1666
- rawParameters: call.parametersRaw
1667
- });
1668
- const parseErrorMessage = call.parseError ?? "Failed to parse parameters";
1669
- return {
1670
- gadgetName: call.gadgetName,
1671
- invocationId: call.invocationId,
1672
- parameters: {},
1673
- error: this.errorFormatter.formatParseError(call.gadgetName, parseErrorMessage, gadget),
1674
- executionTimeMs: Date.now() - startTime
1675
- };
1676
- }
1677
- if (gadget.parameterSchema) {
1678
- const validationResult = gadget.parameterSchema.safeParse(rawParameters);
1679
- if (!validationResult.success) {
1680
- const validationError = this.errorFormatter.formatValidationError(
1681
- call.gadgetName,
1682
- validationResult.error,
1683
- gadget
1684
- );
1685
- this.logger.error("Gadget parameter validation failed", {
1686
- gadgetName: call.gadgetName,
1687
- issueCount: validationResult.error.issues.length
1688
- });
1689
- return {
1690
- gadgetName: call.gadgetName,
1691
- invocationId: call.invocationId,
1692
- parameters: rawParameters,
1693
- error: validationError,
1694
- executionTimeMs: Date.now() - startTime
1695
- };
1696
- }
1697
- validatedParameters = validationResult.data;
1698
- }
1699
- const timeoutMs = gadget.timeoutMs ?? this.defaultGadgetTimeoutMs;
1700
- let result;
1701
- if (timeoutMs && timeoutMs > 0) {
1702
- this.logger.debug("Executing gadget with timeout", {
1703
- gadgetName: call.gadgetName,
1704
- timeoutMs
1705
- });
1706
- result = await Promise.race([
1707
- Promise.resolve(gadget.execute(validatedParameters)),
1708
- this.createTimeoutPromise(call.gadgetName, timeoutMs)
1709
- ]);
1710
- } else {
1711
- result = await Promise.resolve(gadget.execute(validatedParameters));
1712
- }
1713
- const executionTimeMs = Date.now() - startTime;
1714
- this.logger.info("Gadget executed successfully", {
1715
- gadgetName: call.gadgetName,
1716
- invocationId: call.invocationId,
1717
- executionTimeMs
1718
- });
1719
- this.logger.debug("Gadget result", {
1720
- gadgetName: call.gadgetName,
1721
- invocationId: call.invocationId,
1722
- parameters: validatedParameters,
1723
- result,
1724
- executionTimeMs
1725
- });
1726
- return {
1727
- gadgetName: call.gadgetName,
1728
- invocationId: call.invocationId,
1729
- parameters: validatedParameters,
1730
- result,
1731
- executionTimeMs
1732
- };
1733
- } catch (error) {
1734
- if (error instanceof BreakLoopException) {
1735
- this.logger.info("Gadget requested loop termination", {
1736
- gadgetName: call.gadgetName,
1737
- message: error.message
1738
- });
1739
- return {
1740
- gadgetName: call.gadgetName,
1741
- invocationId: call.invocationId,
1742
- parameters: validatedParameters,
1743
- result: error.message,
1744
- breaksLoop: true,
1745
- executionTimeMs: Date.now() - startTime
1746
- };
1747
- }
1748
- if (error instanceof TimeoutException) {
1749
- this.logger.error("Gadget execution timed out", {
1750
- gadgetName: call.gadgetName,
1751
- timeoutMs: error.timeoutMs,
1752
- executionTimeMs: Date.now() - startTime
1753
- });
1754
- return {
1755
- gadgetName: call.gadgetName,
1756
- invocationId: call.invocationId,
1757
- parameters: validatedParameters,
1758
- error: error.message,
1759
- executionTimeMs: Date.now() - startTime
1760
- };
1761
- }
1762
- if (error instanceof HumanInputException) {
1763
- this.logger.info("Gadget requested human input", {
1764
- gadgetName: call.gadgetName,
1765
- question: error.question
1766
- });
1767
- if (this.onHumanInputRequired) {
1768
- try {
1769
- const answer = await this.onHumanInputRequired(error.question);
1770
- this.logger.debug("Human input received", {
1771
- gadgetName: call.gadgetName,
1772
- answerLength: answer.length
1773
- });
1774
- return {
1775
- gadgetName: call.gadgetName,
1776
- invocationId: call.invocationId,
1777
- parameters: validatedParameters,
1778
- result: answer,
1779
- executionTimeMs: Date.now() - startTime
1780
- };
1781
- } catch (inputError) {
1782
- this.logger.error("Human input callback error", {
1783
- gadgetName: call.gadgetName,
1784
- error: inputError instanceof Error ? inputError.message : String(inputError)
1785
- });
1786
- return {
1787
- gadgetName: call.gadgetName,
1788
- invocationId: call.invocationId,
1789
- parameters: validatedParameters,
1790
- error: inputError instanceof Error ? inputError.message : String(inputError),
1791
- executionTimeMs: Date.now() - startTime
1792
- };
1793
- }
1794
- }
1795
- this.logger.warn("Human input required but no callback provided", {
1796
- gadgetName: call.gadgetName
1797
- });
1798
- return {
1799
- gadgetName: call.gadgetName,
1800
- invocationId: call.invocationId,
1801
- parameters: validatedParameters,
1802
- error: "Human input required but not available (stdin is not interactive)",
1803
- executionTimeMs: Date.now() - startTime
1804
- };
1805
- }
1806
- const executionTimeMs = Date.now() - startTime;
1807
- this.logger.error("Gadget execution failed", {
1808
- gadgetName: call.gadgetName,
1809
- error: error instanceof Error ? error.message : String(error),
1810
- executionTimeMs
1811
- });
1812
- return {
1813
- gadgetName: call.gadgetName,
1814
- invocationId: call.invocationId,
1815
- parameters: validatedParameters,
1816
- error: error instanceof Error ? error.message : String(error),
1817
- executionTimeMs
1818
- };
1819
- }
1820
- }
1821
- // Execute multiple gadget calls in parallel
1822
- async executeAll(calls) {
1823
- return Promise.all(calls.map((call) => this.execute(call)));
1824
- }
1825
- };
1826
- }
1827
- });
1828
-
1829
- // src/gadgets/block-params.ts
1830
- function parseBlockParams(content, options) {
1831
- const argPrefix = options?.argPrefix ?? GADGET_ARG_PREFIX;
1832
- const result = {};
1833
- const seenPointers = /* @__PURE__ */ new Set();
1834
- const parts = content.split(argPrefix);
1835
- for (let i = 1; i < parts.length; i++) {
1836
- const part = parts[i];
1837
- const newlineIndex = part.indexOf("\n");
1838
- if (newlineIndex === -1) {
1839
- const pointer2 = part.trim();
1840
- if (pointer2) {
1841
- if (seenPointers.has(pointer2)) {
1842
- throw new Error(`Duplicate pointer: ${pointer2}`);
1843
- }
1844
- seenPointers.add(pointer2);
1845
- setByPointer(result, pointer2, "");
1846
- }
1847
- continue;
1848
- }
1849
- const pointer = part.substring(0, newlineIndex).trim();
1850
- let value = part.substring(newlineIndex + 1);
1851
- if (value.endsWith("\n")) {
1852
- value = value.slice(0, -1);
1853
- }
1854
- if (!pointer) {
1855
- continue;
1856
- }
1857
- if (seenPointers.has(pointer)) {
1858
- throw new Error(`Duplicate pointer: ${pointer}`);
1859
- }
1860
- seenPointers.add(pointer);
1861
- setByPointer(result, pointer, value);
1862
- }
1863
- return result;
1864
- }
1865
- function coerceValue(value) {
1866
- if (value.includes("\n")) {
1867
- return value;
1868
- }
1869
- const trimmed = value.trim();
1870
- if (trimmed === "true") return true;
1871
- if (trimmed === "false") return false;
1872
- if (trimmed !== "" && /^-?\d+(\.\d+)?$/.test(trimmed)) {
1873
- const num = Number(trimmed);
1874
- if (!isNaN(num) && isFinite(num)) {
1875
- return num;
1876
- }
1877
- }
1878
- return value;
1879
- }
1880
- function setByPointer(obj, pointer, value) {
1881
- const segments = pointer.split("/");
1882
- let current = obj;
1883
- for (let i = 0; i < segments.length - 1; i++) {
1884
- const segment = segments[i];
1885
- const nextSegment = segments[i + 1];
1886
- const nextIsArrayIndex = /^\d+$/.test(nextSegment);
1887
- if (Array.isArray(current)) {
1888
- const index = parseInt(segment, 10);
1889
- if (isNaN(index) || index < 0) {
1890
- throw new Error(`Invalid array index: ${segment}`);
1891
- }
1892
- if (index > current.length) {
1893
- throw new Error(`Array index gap: expected ${current.length}, got ${index}`);
1894
- }
1895
- if (current[index] === void 0) {
1896
- current[index] = nextIsArrayIndex ? [] : {};
1897
- }
1898
- current = current[index];
1899
- } else {
1900
- const rec = current;
1901
- if (rec[segment] === void 0) {
1902
- rec[segment] = nextIsArrayIndex ? [] : {};
1903
- }
1904
- current = rec[segment];
1905
- }
1906
- }
1907
- const lastSegment = segments[segments.length - 1];
1908
- const coercedValue = coerceValue(value);
1909
- if (Array.isArray(current)) {
1910
- const index = parseInt(lastSegment, 10);
1911
- if (isNaN(index) || index < 0) {
1912
- throw new Error(`Invalid array index: ${lastSegment}`);
1913
- }
1914
- if (index > current.length) {
1915
- throw new Error(`Array index gap: expected ${current.length}, got ${index}`);
1916
- }
1917
- current[index] = coercedValue;
1918
- } else {
1919
- current[lastSegment] = coercedValue;
1920
- }
1921
- }
1922
- var init_block_params = __esm({
1923
- "src/gadgets/block-params.ts"() {
1924
- "use strict";
1925
- init_constants();
1965
+ };
1926
1966
  }
1927
1967
  });
1928
1968
 
@@ -2103,6 +2143,283 @@ var init_parser = __esm({
2103
2143
  }
2104
2144
  });
2105
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
+
2106
2423
  // src/agent/stream-processor.ts
2107
2424
  var StreamProcessor;
2108
2425
  var init_stream_processor = __esm({
@@ -2140,7 +2457,8 @@ var init_stream_processor = __esm({
2140
2457
  options.registry,
2141
2458
  options.onHumanInputRequired,
2142
2459
  this.logger.getSubLogger({ name: "executor" }),
2143
- options.defaultGadgetTimeoutMs
2460
+ options.defaultGadgetTimeoutMs,
2461
+ { argPrefix: options.gadgetArgPrefix }
2144
2462
  );
2145
2463
  }
2146
2464
  /**