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