@orkestrel/tool 0.0.3 → 0.0.4
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/README.md +1 -1
- package/dist/src/core/index.cjs +340 -0
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +265 -0
- package/dist/src/core/index.d.ts +265 -0
- package/dist/src/core/index.js +336 -2
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.d.cts +125 -3
- package/dist/src/server/index.d.ts +125 -3
- package/package.json +7 -5
- package/dist/src/server/constants.d.ts +0 -12
- package/dist/src/server/factories.d.ts +0 -44
- package/dist/src/server/types.d.ts +0 -60
|
@@ -673,6 +673,153 @@ export declare function createDatabaseTool(options?: DatabaseToolOptions): ToolI
|
|
|
673
673
|
*/
|
|
674
674
|
export declare function createDescribeTool(tools: ToolManagerInterface): ToolInterface;
|
|
675
675
|
|
|
676
|
+
/**
|
|
677
|
+
* Wrap one CONCRETE endpoint ({@link import('./types.js').EndpointDefinition}) as an LLM-callable
|
|
678
|
+
* `ToolInterface` — the endpoint half of the "existing API/DB → MCP tool" bridge (the other half,
|
|
679
|
+
* {@link createInferTool}, is a standalone inference utility).
|
|
680
|
+
*
|
|
681
|
+
* @remarks
|
|
682
|
+
* `parameters` is inferred ONCE at construction from `definition.samples` via
|
|
683
|
+
* `@orkestrel/contract`'s `samplesToSchema` (tuned by {@link import('./types.js').EndpointToolOptions}'s
|
|
684
|
+
* `format` / `enum`), wrapping a non-object root as `{ value: <schema> }` via `schemaToObject` —
|
|
685
|
+
* the SAME object-rooted schema is both the ADVERTISED `parameters` and, by default
|
|
686
|
+
* ({@link import('./types.js').EndpointToolOptions.validate} `true`), the ENFORCED contract:
|
|
687
|
+
* `@orkestrel/contract` 0.0.7's `schemaToShape` compiles it ONCE (via `createContract`) into a
|
|
688
|
+
* `ContractInterface` whose `.parse` runs on every call's `args` before `definition.invoke` — a
|
|
689
|
+
* NORMALIZING parse, not a strict type check: a scalar is COERCED to its inferred type where the
|
|
690
|
+
* house parsers coerce (a number to/from a numeric string, a boolean from `'1'`/`'0'`/`'true'`/
|
|
691
|
+
* `'false'`/`1`/`0`), so `definition.invoke` receives the COERCED value (e.g. `7` sent for a
|
|
692
|
+
* string slot arrives as `'7'`), not the raw call value. A call whose `args` fails to parse into
|
|
693
|
+
* a record — a required key missing, or a value not coercible to its slot's type — THROWS a
|
|
694
|
+
* typed `TOOL` {@link import('./errors.js').AgentToolError} carrying the compiled contract's
|
|
695
|
+
* structured `explain` faults, and `definition.invoke` is never called. `format` annotations are
|
|
696
|
+
* NEVER asserted, and a key outside the closed inferred schema is SILENTLY DROPPED rather than
|
|
697
|
+
* rejected (see {@link import('./types.js').EndpointToolOptions.validate}). With
|
|
698
|
+
* `validate: false`, `execute` PASSES THROUGH the model-supplied `args` to `definition.invoke`
|
|
699
|
+
* WITHOUT re-validation — the pre-0.0.7 behavior, preserved as an explicit opt-out. Either way,
|
|
700
|
+
* `invoke`'s return flows back as the tool call's plain result; a throw PROPAGATES uncaught,
|
|
701
|
+
* isolated by the `ToolManagerInterface` (`@orkestrel/agent`) into the canonical error envelope
|
|
702
|
+
* (AGENTS §14) — never caught or re-wrapped here.
|
|
703
|
+
*
|
|
704
|
+
* @param definition - The endpoint's identity, non-empty samples, and local handler (see
|
|
705
|
+
* {@link import('./types.js').EndpointDefinition})
|
|
706
|
+
* @param options - Construction-time inference tuning + the validate opt-out (see
|
|
707
|
+
* {@link import('./types.js').EndpointToolOptions})
|
|
708
|
+
* @returns A `ToolInterface` named `definition.name`
|
|
709
|
+
*
|
|
710
|
+
* @example
|
|
711
|
+
* ```ts
|
|
712
|
+
* import { createEndpointTool } from '@src/core'
|
|
713
|
+
* import { createToolManager } from '@orkestrel/agent'
|
|
714
|
+
*
|
|
715
|
+
* const tool = createEndpointTool({
|
|
716
|
+
* name: 'lookupUser',
|
|
717
|
+
* description: 'Look up a user by id.',
|
|
718
|
+
* samples: [{ id: '1', name: 'Ada' }, { id: '2', name: 'Bob' }],
|
|
719
|
+
* invoke: (args) => ({ id: args.id, name: 'Ada' }),
|
|
720
|
+
* })
|
|
721
|
+
* const tools = createToolManager()
|
|
722
|
+
* tools.add(tool)
|
|
723
|
+
*
|
|
724
|
+
* // conforming args (all required keys present) parse and reach `invoke`
|
|
725
|
+
* const result = await tools.execute({
|
|
726
|
+
* id: 'call-1',
|
|
727
|
+
* name: 'lookupUser',
|
|
728
|
+
* arguments: { id: '1', name: 'Ada' },
|
|
729
|
+
* })
|
|
730
|
+
* // result.value -> { id: '1', name: 'Ada' }
|
|
731
|
+
*
|
|
732
|
+
* // a nonconforming call (id is not coercible to the required string) is rejected before
|
|
733
|
+
* // `invoke` runs
|
|
734
|
+
* const rejected = await tools.execute({
|
|
735
|
+
* id: 'call-2',
|
|
736
|
+
* name: 'lookupUser',
|
|
737
|
+
* arguments: { id: true, name: 'Ada' },
|
|
738
|
+
* })
|
|
739
|
+
* // rejected.error -> the TOOL AgentToolError message
|
|
740
|
+
* ```
|
|
741
|
+
*/
|
|
742
|
+
export declare function createEndpointTool(definition: EndpointDefinition, options?: EndpointToolOptions): ToolInterface;
|
|
743
|
+
|
|
744
|
+
/**
|
|
745
|
+
* Build a standalone LLM-callable tool that infers a JSON Schema from example values — the
|
|
746
|
+
* utility half of the "existing API/DB → MCP tool" bridge (the other half,
|
|
747
|
+
* {@link createEndpointTool}, wraps one CONCRETE endpoint).
|
|
748
|
+
*
|
|
749
|
+
* @remarks
|
|
750
|
+
* The universal tool-handler contract (AGENTS §14): validates the call args against
|
|
751
|
+
* {@link import('./shapers.js').inferToolShape} (`samples` non-empty, `format` / `enum` optional
|
|
752
|
+
* booleans, `candidates` an optional array), infers a schema via `@orkestrel/contract`'s
|
|
753
|
+
* `samplesToSchema`, wraps a non-object root as `{ value: <schema> }` via `schemaToObject` (mirrors
|
|
754
|
+
* the tool-parameters convention every other `create*Tool` factory advertises), and RETURNS the
|
|
755
|
+
* resulting parameters record. An empty `samples` array fails `inferToolShape`'s `min: 1` bound —
|
|
756
|
+
* `contract.parse` returns `undefined` and the handler throws a typed `TOOL`
|
|
757
|
+
* {@link import('./errors.js').AgentToolError}.
|
|
758
|
+
*
|
|
759
|
+
* When `candidates` is ABSENT, the return is the bare parameters record — unchanged from before
|
|
760
|
+
* this array existed. When `candidates` is PRESENT (any array, including empty), the handler
|
|
761
|
+
* compiles a SEPARATE per-call contract from the RAW inferred schema (via `@orkestrel/contract`'s
|
|
762
|
+
* `schemaToShape`, NOT the `schemaToObject`-wrapped parameters — a bare-value sample checks a
|
|
763
|
+
* bare-value candidate) and returns `{ parameters, checks }`, one check per candidate at the same
|
|
764
|
+
* index. Every entry has a UNIFORM shape — `{ index, valid, coercible }`, with `faults` added ONLY
|
|
765
|
+
* when `valid` is `false`: `valid` is the STRICT guard verdict (`checker.is(candidate)`), the
|
|
766
|
+
* OPPOSITE of {@link createEndpointTool}'s enforcement, which coerces (`7` becomes `'7'` for a
|
|
767
|
+
* string slot) — here a conformance report answers "does this value conform AS-IS": `7` against a
|
|
768
|
+
* string slot is `valid: false`, full stop. `coercible` answers a SEPARATE question — "would the
|
|
769
|
+
* NORMALIZING parse accept this value", i.e. would {@link createEndpointTool}'s default enforcement
|
|
770
|
+
* admit it (`checker.parse(candidate) !== undefined`) — computed for every candidate regardless of
|
|
771
|
+
* `valid`; by the house parse/guard round-trip guarantee (AGENTS §14), a `valid: true` entry is
|
|
772
|
+
* ALWAYS also `coercible: true`. `@orkestrel/contract` 0.0.7's `explain` mirrors the normalizing
|
|
773
|
+
* `parse`'s leniency, not `is`'s strictness — so a strictly-invalid but coercible candidate (`7`
|
|
774
|
+
* against a string slot) yields `{ valid: false, coercible: true, faults: [] }`: EMPTY faults, since
|
|
775
|
+
* the mismatch the normalizing parse would silently fix is not one `explain` reports. `faults`
|
|
776
|
+
* therefore only ever populates for a NON-coercible mismatch — a wrong type the parse can't coerce
|
|
777
|
+
* (a boolean in a string slot), a missing required key, or an out-of-enum value — where
|
|
778
|
+
* `coercible: false`. `checker.is` / `.parse` / `.explain` are all total over JSON-safe input — a
|
|
779
|
+
* JSON-safe hostile candidate (a `__proto__`-carrying object, deeply nested data) reaches all three
|
|
780
|
+
* and yields a bounded, non-throwing per-candidate verdict; a NON-JSON-safe candidate (e.g. a
|
|
781
|
+
* throwing-getter `Proxy`) never reaches the checker at all — it fails the OUTER `args` parse
|
|
782
|
+
* against {@link import('./shapers.js').inferToolShape} and rejects the WHOLE call with the same
|
|
783
|
+
* `TOOL` {@link import('./errors.js').AgentToolError} a malformed `samples`/`format`/`enum` throws,
|
|
784
|
+
* with no per-candidate verdict produced.
|
|
785
|
+
*
|
|
786
|
+
* @param options - Advertised `name` / `description` overrides (see
|
|
787
|
+
* {@link import('./types.js').InferToolOptions})
|
|
788
|
+
* @returns A `ToolInterface` (named {@link import('./constants.js').INFER_TOOL_NAME} by default)
|
|
789
|
+
*
|
|
790
|
+
* @example
|
|
791
|
+
* ```ts
|
|
792
|
+
* import { createInferTool } from '@src/core'
|
|
793
|
+
* import { createToolManager } from '@orkestrel/agent'
|
|
794
|
+
*
|
|
795
|
+
* const tool = createInferTool()
|
|
796
|
+
* const tools = createToolManager()
|
|
797
|
+
* tools.add(tool)
|
|
798
|
+
*
|
|
799
|
+
* const result = await tools.execute({
|
|
800
|
+
* id: 'call-1',
|
|
801
|
+
* name: 'infer',
|
|
802
|
+
* arguments: { samples: [{ id: 1, name: 'Ada' }, { id: 2, name: 'Bob' }] },
|
|
803
|
+
* })
|
|
804
|
+
* // result.value -> { type: 'object', properties: { id: {...}, name: {...} }, ... }
|
|
805
|
+
*
|
|
806
|
+
* // with candidates, the result is wrapped with per-candidate verdicts
|
|
807
|
+
* const checked = await tools.execute({
|
|
808
|
+
* id: 'call-2',
|
|
809
|
+
* name: 'infer',
|
|
810
|
+
* arguments: {
|
|
811
|
+
* samples: [{ id: 1, name: 'Ada' }],
|
|
812
|
+
* candidates: [{ id: 2, name: 'Bob' }, { id: 'x', name: 'Cy' }],
|
|
813
|
+
* },
|
|
814
|
+
* })
|
|
815
|
+
* // checked.value -> { parameters: {...}, checks: [
|
|
816
|
+
* // { index: 0, valid: true, coercible: true },
|
|
817
|
+
* // { index: 1, valid: false, coercible: false, faults: [...] },
|
|
818
|
+
* // ] }
|
|
819
|
+
* ```
|
|
820
|
+
*/
|
|
821
|
+
export declare function createInferTool(options?: InferToolOptions): ToolInterface;
|
|
822
|
+
|
|
676
823
|
/**
|
|
677
824
|
* Create the in-memory {@link DefinitionStoreInterface} — a process-lifetime `Map` of database
|
|
678
825
|
* definitions, the DEFAULT store the upcoming database / relation tools will persist their
|
|
@@ -1349,6 +1496,76 @@ export declare const describeToolShape: ObjectShape<{
|
|
|
1349
1496
|
name: StringShape;
|
|
1350
1497
|
}, false>;
|
|
1351
1498
|
|
|
1499
|
+
/**
|
|
1500
|
+
* One concrete endpoint {@link import('./factories.js').createEndpointTool} wraps as an
|
|
1501
|
+
* LLM-callable `ToolInterface` — the advertised identity, a non-empty set of example values its
|
|
1502
|
+
* `parameters` are inferred from, and the local handler that runs a call.
|
|
1503
|
+
*
|
|
1504
|
+
* @remarks
|
|
1505
|
+
* `samples` MUST be non-empty — {@link import('./factories.js').createEndpointTool} throws a
|
|
1506
|
+
* typed `TOOL` {@link import('./errors.js').AgentToolError} at CONSTRUCTION when it is empty,
|
|
1507
|
+
* since an empty sample set cannot infer a schema. By DEFAULT ({@link EndpointToolOptions.validate}
|
|
1508
|
+
* `true`) `invoke` receives the PARSED, NORMALIZED args record — a copy of the model-supplied
|
|
1509
|
+
* `args` with each scalar coerced to its inferred type (e.g. a number sent for a string slot
|
|
1510
|
+
* arrives coerced to a string), checked against the same schema advertised as `parameters` — and
|
|
1511
|
+
* a call with a missing required key or a non-coercible value never reaches `invoke` at all (see
|
|
1512
|
+
* {@link EndpointToolOptions.validate}). With
|
|
1513
|
+
* `validate: false`, `invoke` receives the model-supplied `args` VERBATIM (raw passthrough, never
|
|
1514
|
+
* checked against the inferred schema). Either way `invoke`'s return flows back as the tool
|
|
1515
|
+
* call's result; a throw PROPAGATES uncaught, isolated by the `ToolManagerInterface`
|
|
1516
|
+
* (`@orkestrel/agent`) into the canonical error envelope. When `samples` are non-object values,
|
|
1517
|
+
* the advertised schema wraps them under a single required `value` property, so `invoke` receives
|
|
1518
|
+
* an `args` record of the shape `{ value: ... }` — never the bare value.
|
|
1519
|
+
*/
|
|
1520
|
+
export declare interface EndpointDefinition {
|
|
1521
|
+
readonly name: string;
|
|
1522
|
+
readonly description: string;
|
|
1523
|
+
readonly samples: readonly unknown[];
|
|
1524
|
+
readonly invoke: EndpointHandler;
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
/**
|
|
1528
|
+
* The handler {@link import('./types.js').EndpointDefinition.invoke} implements — mirrors
|
|
1529
|
+
* `@orkestrel/agent`'s `ToolOptions.execute` signature EXACTLY (same `Readonly<Record<string,
|
|
1530
|
+
* unknown>>` argument, same `Promise<unknown> | unknown` return) so
|
|
1531
|
+
* `execute: (args) => definition.invoke(args)` typechecks with zero assertions in
|
|
1532
|
+
* {@link import('./factories.js').createEndpointTool}.
|
|
1533
|
+
*/
|
|
1534
|
+
export declare type EndpointHandler = (args: Readonly<Record<string, unknown>>) => Promise<unknown> | unknown;
|
|
1535
|
+
|
|
1536
|
+
/**
|
|
1537
|
+
* Construction-time tuning for {@link import('./factories.js').createEndpointTool} — the
|
|
1538
|
+
* inferred `parameters` schema's `format` / `enum` constraints, and whether that same schema is
|
|
1539
|
+
* ENFORCED at `execute` time.
|
|
1540
|
+
*
|
|
1541
|
+
* @remarks
|
|
1542
|
+
* `format` / `enum` default to `false`, matching `@orkestrel/contract`'s own
|
|
1543
|
+
* `ValueToSchemaOptions` defaults. `validate` defaults to `true`: the schema
|
|
1544
|
+
* `createEndpointTool` advertises as `parameters` (`samplesToSchema` + `schemaToObject`) is
|
|
1545
|
+
* compiled ONCE at construction (via `@orkestrel/contract` 0.0.7's `schemaToShape`) into a
|
|
1546
|
+
* `ContractInterface` used to `parse` every call's `args` before `invoke` runs — a NORMALIZING
|
|
1547
|
+
* parse: a scalar value is COERCED to its inferred type where the house parsers coerce (a number
|
|
1548
|
+
* to/from a numeric string, a boolean from `'1'`/`'0'`/`'true'`/`'false'`/`1`/`0`), so `invoke`
|
|
1549
|
+
* receives the COERCED values (e.g. `7` sent for a string slot arrives at `invoke` as `'7'`), not
|
|
1550
|
+
* the raw call args. A call whose `args` fails to parse — a required key missing, or a value not
|
|
1551
|
+
* coercible to its slot's type — THROWS a typed `TOOL` {@link import('./errors.js').AgentToolError}
|
|
1552
|
+
* carrying the structured `explain` faults, and `invoke` is never called. Beyond that coercion,
|
|
1553
|
+
* enforcement is STRUCTURAL — required keys, `enum` membership, and numeric bounds — `format`
|
|
1554
|
+
* annotations (`email`, `date-time`, `uuid`, `uri`, ...) are NEVER asserted, mirroring
|
|
1555
|
+
* `@orkestrel/contract`'s own widening-only law for `schemaToShape`: a `format: true`-tuned
|
|
1556
|
+
* endpoint still ACCEPTS a non-conforming string in a format-tagged slot. A key NOT present in
|
|
1557
|
+
* the inferred (closed, `additionalProperties: false`) schema is NEVER a rejection either — it is
|
|
1558
|
+
* SILENTLY DROPPED before `invoke` runs (the same leniency `@orkestrel/contract`'s own `parse`
|
|
1559
|
+
* grants a closed object generally), so `invoke` may see fewer keys than the caller sent. Set
|
|
1560
|
+
* `validate: false` to restore the PRE-0.0.7 behavior exactly — `execute` passes the
|
|
1561
|
+
* model-supplied `args` straight to `invoke` UNCHANGED, unchecked and unstripped.
|
|
1562
|
+
*/
|
|
1563
|
+
export declare interface EndpointToolOptions {
|
|
1564
|
+
readonly format?: boolean;
|
|
1565
|
+
readonly enum?: boolean;
|
|
1566
|
+
readonly validate?: boolean;
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1352
1569
|
/**
|
|
1353
1570
|
* Expand the relation tool's FLAT dot-path `include` list into a live `@orkestrel/relation`
|
|
1354
1571
|
* {@link Include} tree — the pure leaf {@link import('./factories.js').createRelationTool} calls
|
|
@@ -1408,6 +1625,54 @@ export declare function expandTables(spec: TableSpec): TablesShape;
|
|
|
1408
1625
|
/** Flat dot-path relation include list, expanded via {@link import('./helpers.js').expandInclude}. */
|
|
1409
1626
|
export declare const includeShape: OptionalShape<ArrayShape<StringShape>>;
|
|
1410
1627
|
|
|
1628
|
+
export declare const INFER_TOOL_DESCRIPTION: string;
|
|
1629
|
+
|
|
1630
|
+
/**
|
|
1631
|
+
* The name {@link import('./factories.js').createInferTool} advertises by default — the key a
|
|
1632
|
+
* model calls and the `ToolManagerInterface` (`@orkestrel/agent`) registers under.
|
|
1633
|
+
*/
|
|
1634
|
+
export declare const INFER_TOOL_NAME = "infer";
|
|
1635
|
+
|
|
1636
|
+
/**
|
|
1637
|
+
* The lean {@link import('@orkestrel/agent').ToolInterface.summary} {@link import('./factories.js').createInferTool}
|
|
1638
|
+
* advertises in place of {@link INFER_TOOL_DESCRIPTION} — a `ToolManagerInterface.definitions()`
|
|
1639
|
+
* (`@orkestrel/agent`) advertises `summary ?? description`, so this one-sentence text stands in
|
|
1640
|
+
* for the full teaching description; the full text stays retrievable via
|
|
1641
|
+
* {@link import('./factories.js').createDescribeTool}.
|
|
1642
|
+
*/
|
|
1643
|
+
export declare const INFER_TOOL_SUMMARY = "Infer a JSON Schema (as advertised tool parameters) from one or more example values. Call describe('infer') for the required fields.";
|
|
1644
|
+
|
|
1645
|
+
/**
|
|
1646
|
+
* Options for {@link import('./factories.js').createInferTool} — advertised name/description
|
|
1647
|
+
* overrides only; `format` / `enum` are RUNTIME call arguments (see
|
|
1648
|
+
* {@link import('./shapers.js').inferToolShape}), not construction-time options, since a model
|
|
1649
|
+
* chooses them per call.
|
|
1650
|
+
*/
|
|
1651
|
+
export declare interface InferToolOptions {
|
|
1652
|
+
readonly name?: string;
|
|
1653
|
+
readonly description?: string;
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
/**
|
|
1657
|
+
* The shape of {@link import('./factories.js').createInferTool}'s call arguments — one or more
|
|
1658
|
+
* example `samples` to infer a JSON Schema from, plus per-call `format` / `enum` toggles and an
|
|
1659
|
+
* optional `candidates` array to check against the inferred schema.
|
|
1660
|
+
*
|
|
1661
|
+
* @remarks
|
|
1662
|
+
* `samples` requires at least one element (`min: 1`) — an empty array parses to `undefined`,
|
|
1663
|
+
* surfaced by the handler as a typed `TOOL` {@link import('./errors.js').AgentToolError}. When
|
|
1664
|
+
* `candidates` is present (any array, including empty), the handler compiles a contract from the
|
|
1665
|
+
* freshly inferred schema and checks each candidate against it with a STRICT guard (`.is`, no
|
|
1666
|
+
* coercion) — the opposite of {@link import('./factories.js').createEndpointTool}'s NORMALIZING
|
|
1667
|
+
* `.parse` enforcement.
|
|
1668
|
+
*/
|
|
1669
|
+
export declare const inferToolShape: ObjectShape<{
|
|
1670
|
+
samples: ArrayShape<JSONShape>;
|
|
1671
|
+
format: OptionalShape<BooleanShape>;
|
|
1672
|
+
enum: OptionalShape<BooleanShape>;
|
|
1673
|
+
candidates: OptionalShape<ArrayShape<JSONShape>>;
|
|
1674
|
+
}, false>;
|
|
1675
|
+
|
|
1411
1676
|
/**
|
|
1412
1677
|
* Type guard narrowing an unknown caught value to an {@link AgentToolError}.
|
|
1413
1678
|
*
|
package/dist/src/core/index.d.ts
CHANGED
|
@@ -673,6 +673,153 @@ export declare function createDatabaseTool(options?: DatabaseToolOptions): ToolI
|
|
|
673
673
|
*/
|
|
674
674
|
export declare function createDescribeTool(tools: ToolManagerInterface): ToolInterface;
|
|
675
675
|
|
|
676
|
+
/**
|
|
677
|
+
* Wrap one CONCRETE endpoint ({@link import('./types.js').EndpointDefinition}) as an LLM-callable
|
|
678
|
+
* `ToolInterface` — the endpoint half of the "existing API/DB → MCP tool" bridge (the other half,
|
|
679
|
+
* {@link createInferTool}, is a standalone inference utility).
|
|
680
|
+
*
|
|
681
|
+
* @remarks
|
|
682
|
+
* `parameters` is inferred ONCE at construction from `definition.samples` via
|
|
683
|
+
* `@orkestrel/contract`'s `samplesToSchema` (tuned by {@link import('./types.js').EndpointToolOptions}'s
|
|
684
|
+
* `format` / `enum`), wrapping a non-object root as `{ value: <schema> }` via `schemaToObject` —
|
|
685
|
+
* the SAME object-rooted schema is both the ADVERTISED `parameters` and, by default
|
|
686
|
+
* ({@link import('./types.js').EndpointToolOptions.validate} `true`), the ENFORCED contract:
|
|
687
|
+
* `@orkestrel/contract` 0.0.7's `schemaToShape` compiles it ONCE (via `createContract`) into a
|
|
688
|
+
* `ContractInterface` whose `.parse` runs on every call's `args` before `definition.invoke` — a
|
|
689
|
+
* NORMALIZING parse, not a strict type check: a scalar is COERCED to its inferred type where the
|
|
690
|
+
* house parsers coerce (a number to/from a numeric string, a boolean from `'1'`/`'0'`/`'true'`/
|
|
691
|
+
* `'false'`/`1`/`0`), so `definition.invoke` receives the COERCED value (e.g. `7` sent for a
|
|
692
|
+
* string slot arrives as `'7'`), not the raw call value. A call whose `args` fails to parse into
|
|
693
|
+
* a record — a required key missing, or a value not coercible to its slot's type — THROWS a
|
|
694
|
+
* typed `TOOL` {@link import('./errors.js').AgentToolError} carrying the compiled contract's
|
|
695
|
+
* structured `explain` faults, and `definition.invoke` is never called. `format` annotations are
|
|
696
|
+
* NEVER asserted, and a key outside the closed inferred schema is SILENTLY DROPPED rather than
|
|
697
|
+
* rejected (see {@link import('./types.js').EndpointToolOptions.validate}). With
|
|
698
|
+
* `validate: false`, `execute` PASSES THROUGH the model-supplied `args` to `definition.invoke`
|
|
699
|
+
* WITHOUT re-validation — the pre-0.0.7 behavior, preserved as an explicit opt-out. Either way,
|
|
700
|
+
* `invoke`'s return flows back as the tool call's plain result; a throw PROPAGATES uncaught,
|
|
701
|
+
* isolated by the `ToolManagerInterface` (`@orkestrel/agent`) into the canonical error envelope
|
|
702
|
+
* (AGENTS §14) — never caught or re-wrapped here.
|
|
703
|
+
*
|
|
704
|
+
* @param definition - The endpoint's identity, non-empty samples, and local handler (see
|
|
705
|
+
* {@link import('./types.js').EndpointDefinition})
|
|
706
|
+
* @param options - Construction-time inference tuning + the validate opt-out (see
|
|
707
|
+
* {@link import('./types.js').EndpointToolOptions})
|
|
708
|
+
* @returns A `ToolInterface` named `definition.name`
|
|
709
|
+
*
|
|
710
|
+
* @example
|
|
711
|
+
* ```ts
|
|
712
|
+
* import { createEndpointTool } from '@src/core'
|
|
713
|
+
* import { createToolManager } from '@orkestrel/agent'
|
|
714
|
+
*
|
|
715
|
+
* const tool = createEndpointTool({
|
|
716
|
+
* name: 'lookupUser',
|
|
717
|
+
* description: 'Look up a user by id.',
|
|
718
|
+
* samples: [{ id: '1', name: 'Ada' }, { id: '2', name: 'Bob' }],
|
|
719
|
+
* invoke: (args) => ({ id: args.id, name: 'Ada' }),
|
|
720
|
+
* })
|
|
721
|
+
* const tools = createToolManager()
|
|
722
|
+
* tools.add(tool)
|
|
723
|
+
*
|
|
724
|
+
* // conforming args (all required keys present) parse and reach `invoke`
|
|
725
|
+
* const result = await tools.execute({
|
|
726
|
+
* id: 'call-1',
|
|
727
|
+
* name: 'lookupUser',
|
|
728
|
+
* arguments: { id: '1', name: 'Ada' },
|
|
729
|
+
* })
|
|
730
|
+
* // result.value -> { id: '1', name: 'Ada' }
|
|
731
|
+
*
|
|
732
|
+
* // a nonconforming call (id is not coercible to the required string) is rejected before
|
|
733
|
+
* // `invoke` runs
|
|
734
|
+
* const rejected = await tools.execute({
|
|
735
|
+
* id: 'call-2',
|
|
736
|
+
* name: 'lookupUser',
|
|
737
|
+
* arguments: { id: true, name: 'Ada' },
|
|
738
|
+
* })
|
|
739
|
+
* // rejected.error -> the TOOL AgentToolError message
|
|
740
|
+
* ```
|
|
741
|
+
*/
|
|
742
|
+
export declare function createEndpointTool(definition: EndpointDefinition, options?: EndpointToolOptions): ToolInterface;
|
|
743
|
+
|
|
744
|
+
/**
|
|
745
|
+
* Build a standalone LLM-callable tool that infers a JSON Schema from example values — the
|
|
746
|
+
* utility half of the "existing API/DB → MCP tool" bridge (the other half,
|
|
747
|
+
* {@link createEndpointTool}, wraps one CONCRETE endpoint).
|
|
748
|
+
*
|
|
749
|
+
* @remarks
|
|
750
|
+
* The universal tool-handler contract (AGENTS §14): validates the call args against
|
|
751
|
+
* {@link import('./shapers.js').inferToolShape} (`samples` non-empty, `format` / `enum` optional
|
|
752
|
+
* booleans, `candidates` an optional array), infers a schema via `@orkestrel/contract`'s
|
|
753
|
+
* `samplesToSchema`, wraps a non-object root as `{ value: <schema> }` via `schemaToObject` (mirrors
|
|
754
|
+
* the tool-parameters convention every other `create*Tool` factory advertises), and RETURNS the
|
|
755
|
+
* resulting parameters record. An empty `samples` array fails `inferToolShape`'s `min: 1` bound —
|
|
756
|
+
* `contract.parse` returns `undefined` and the handler throws a typed `TOOL`
|
|
757
|
+
* {@link import('./errors.js').AgentToolError}.
|
|
758
|
+
*
|
|
759
|
+
* When `candidates` is ABSENT, the return is the bare parameters record — unchanged from before
|
|
760
|
+
* this array existed. When `candidates` is PRESENT (any array, including empty), the handler
|
|
761
|
+
* compiles a SEPARATE per-call contract from the RAW inferred schema (via `@orkestrel/contract`'s
|
|
762
|
+
* `schemaToShape`, NOT the `schemaToObject`-wrapped parameters — a bare-value sample checks a
|
|
763
|
+
* bare-value candidate) and returns `{ parameters, checks }`, one check per candidate at the same
|
|
764
|
+
* index. Every entry has a UNIFORM shape — `{ index, valid, coercible }`, with `faults` added ONLY
|
|
765
|
+
* when `valid` is `false`: `valid` is the STRICT guard verdict (`checker.is(candidate)`), the
|
|
766
|
+
* OPPOSITE of {@link createEndpointTool}'s enforcement, which coerces (`7` becomes `'7'` for a
|
|
767
|
+
* string slot) — here a conformance report answers "does this value conform AS-IS": `7` against a
|
|
768
|
+
* string slot is `valid: false`, full stop. `coercible` answers a SEPARATE question — "would the
|
|
769
|
+
* NORMALIZING parse accept this value", i.e. would {@link createEndpointTool}'s default enforcement
|
|
770
|
+
* admit it (`checker.parse(candidate) !== undefined`) — computed for every candidate regardless of
|
|
771
|
+
* `valid`; by the house parse/guard round-trip guarantee (AGENTS §14), a `valid: true` entry is
|
|
772
|
+
* ALWAYS also `coercible: true`. `@orkestrel/contract` 0.0.7's `explain` mirrors the normalizing
|
|
773
|
+
* `parse`'s leniency, not `is`'s strictness — so a strictly-invalid but coercible candidate (`7`
|
|
774
|
+
* against a string slot) yields `{ valid: false, coercible: true, faults: [] }`: EMPTY faults, since
|
|
775
|
+
* the mismatch the normalizing parse would silently fix is not one `explain` reports. `faults`
|
|
776
|
+
* therefore only ever populates for a NON-coercible mismatch — a wrong type the parse can't coerce
|
|
777
|
+
* (a boolean in a string slot), a missing required key, or an out-of-enum value — where
|
|
778
|
+
* `coercible: false`. `checker.is` / `.parse` / `.explain` are all total over JSON-safe input — a
|
|
779
|
+
* JSON-safe hostile candidate (a `__proto__`-carrying object, deeply nested data) reaches all three
|
|
780
|
+
* and yields a bounded, non-throwing per-candidate verdict; a NON-JSON-safe candidate (e.g. a
|
|
781
|
+
* throwing-getter `Proxy`) never reaches the checker at all — it fails the OUTER `args` parse
|
|
782
|
+
* against {@link import('./shapers.js').inferToolShape} and rejects the WHOLE call with the same
|
|
783
|
+
* `TOOL` {@link import('./errors.js').AgentToolError} a malformed `samples`/`format`/`enum` throws,
|
|
784
|
+
* with no per-candidate verdict produced.
|
|
785
|
+
*
|
|
786
|
+
* @param options - Advertised `name` / `description` overrides (see
|
|
787
|
+
* {@link import('./types.js').InferToolOptions})
|
|
788
|
+
* @returns A `ToolInterface` (named {@link import('./constants.js').INFER_TOOL_NAME} by default)
|
|
789
|
+
*
|
|
790
|
+
* @example
|
|
791
|
+
* ```ts
|
|
792
|
+
* import { createInferTool } from '@src/core'
|
|
793
|
+
* import { createToolManager } from '@orkestrel/agent'
|
|
794
|
+
*
|
|
795
|
+
* const tool = createInferTool()
|
|
796
|
+
* const tools = createToolManager()
|
|
797
|
+
* tools.add(tool)
|
|
798
|
+
*
|
|
799
|
+
* const result = await tools.execute({
|
|
800
|
+
* id: 'call-1',
|
|
801
|
+
* name: 'infer',
|
|
802
|
+
* arguments: { samples: [{ id: 1, name: 'Ada' }, { id: 2, name: 'Bob' }] },
|
|
803
|
+
* })
|
|
804
|
+
* // result.value -> { type: 'object', properties: { id: {...}, name: {...} }, ... }
|
|
805
|
+
*
|
|
806
|
+
* // with candidates, the result is wrapped with per-candidate verdicts
|
|
807
|
+
* const checked = await tools.execute({
|
|
808
|
+
* id: 'call-2',
|
|
809
|
+
* name: 'infer',
|
|
810
|
+
* arguments: {
|
|
811
|
+
* samples: [{ id: 1, name: 'Ada' }],
|
|
812
|
+
* candidates: [{ id: 2, name: 'Bob' }, { id: 'x', name: 'Cy' }],
|
|
813
|
+
* },
|
|
814
|
+
* })
|
|
815
|
+
* // checked.value -> { parameters: {...}, checks: [
|
|
816
|
+
* // { index: 0, valid: true, coercible: true },
|
|
817
|
+
* // { index: 1, valid: false, coercible: false, faults: [...] },
|
|
818
|
+
* // ] }
|
|
819
|
+
* ```
|
|
820
|
+
*/
|
|
821
|
+
export declare function createInferTool(options?: InferToolOptions): ToolInterface;
|
|
822
|
+
|
|
676
823
|
/**
|
|
677
824
|
* Create the in-memory {@link DefinitionStoreInterface} — a process-lifetime `Map` of database
|
|
678
825
|
* definitions, the DEFAULT store the upcoming database / relation tools will persist their
|
|
@@ -1349,6 +1496,76 @@ export declare const describeToolShape: ObjectShape<{
|
|
|
1349
1496
|
name: StringShape;
|
|
1350
1497
|
}, false>;
|
|
1351
1498
|
|
|
1499
|
+
/**
|
|
1500
|
+
* One concrete endpoint {@link import('./factories.js').createEndpointTool} wraps as an
|
|
1501
|
+
* LLM-callable `ToolInterface` — the advertised identity, a non-empty set of example values its
|
|
1502
|
+
* `parameters` are inferred from, and the local handler that runs a call.
|
|
1503
|
+
*
|
|
1504
|
+
* @remarks
|
|
1505
|
+
* `samples` MUST be non-empty — {@link import('./factories.js').createEndpointTool} throws a
|
|
1506
|
+
* typed `TOOL` {@link import('./errors.js').AgentToolError} at CONSTRUCTION when it is empty,
|
|
1507
|
+
* since an empty sample set cannot infer a schema. By DEFAULT ({@link EndpointToolOptions.validate}
|
|
1508
|
+
* `true`) `invoke` receives the PARSED, NORMALIZED args record — a copy of the model-supplied
|
|
1509
|
+
* `args` with each scalar coerced to its inferred type (e.g. a number sent for a string slot
|
|
1510
|
+
* arrives coerced to a string), checked against the same schema advertised as `parameters` — and
|
|
1511
|
+
* a call with a missing required key or a non-coercible value never reaches `invoke` at all (see
|
|
1512
|
+
* {@link EndpointToolOptions.validate}). With
|
|
1513
|
+
* `validate: false`, `invoke` receives the model-supplied `args` VERBATIM (raw passthrough, never
|
|
1514
|
+
* checked against the inferred schema). Either way `invoke`'s return flows back as the tool
|
|
1515
|
+
* call's result; a throw PROPAGATES uncaught, isolated by the `ToolManagerInterface`
|
|
1516
|
+
* (`@orkestrel/agent`) into the canonical error envelope. When `samples` are non-object values,
|
|
1517
|
+
* the advertised schema wraps them under a single required `value` property, so `invoke` receives
|
|
1518
|
+
* an `args` record of the shape `{ value: ... }` — never the bare value.
|
|
1519
|
+
*/
|
|
1520
|
+
export declare interface EndpointDefinition {
|
|
1521
|
+
readonly name: string;
|
|
1522
|
+
readonly description: string;
|
|
1523
|
+
readonly samples: readonly unknown[];
|
|
1524
|
+
readonly invoke: EndpointHandler;
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
/**
|
|
1528
|
+
* The handler {@link import('./types.js').EndpointDefinition.invoke} implements — mirrors
|
|
1529
|
+
* `@orkestrel/agent`'s `ToolOptions.execute` signature EXACTLY (same `Readonly<Record<string,
|
|
1530
|
+
* unknown>>` argument, same `Promise<unknown> | unknown` return) so
|
|
1531
|
+
* `execute: (args) => definition.invoke(args)` typechecks with zero assertions in
|
|
1532
|
+
* {@link import('./factories.js').createEndpointTool}.
|
|
1533
|
+
*/
|
|
1534
|
+
export declare type EndpointHandler = (args: Readonly<Record<string, unknown>>) => Promise<unknown> | unknown;
|
|
1535
|
+
|
|
1536
|
+
/**
|
|
1537
|
+
* Construction-time tuning for {@link import('./factories.js').createEndpointTool} — the
|
|
1538
|
+
* inferred `parameters` schema's `format` / `enum` constraints, and whether that same schema is
|
|
1539
|
+
* ENFORCED at `execute` time.
|
|
1540
|
+
*
|
|
1541
|
+
* @remarks
|
|
1542
|
+
* `format` / `enum` default to `false`, matching `@orkestrel/contract`'s own
|
|
1543
|
+
* `ValueToSchemaOptions` defaults. `validate` defaults to `true`: the schema
|
|
1544
|
+
* `createEndpointTool` advertises as `parameters` (`samplesToSchema` + `schemaToObject`) is
|
|
1545
|
+
* compiled ONCE at construction (via `@orkestrel/contract` 0.0.7's `schemaToShape`) into a
|
|
1546
|
+
* `ContractInterface` used to `parse` every call's `args` before `invoke` runs — a NORMALIZING
|
|
1547
|
+
* parse: a scalar value is COERCED to its inferred type where the house parsers coerce (a number
|
|
1548
|
+
* to/from a numeric string, a boolean from `'1'`/`'0'`/`'true'`/`'false'`/`1`/`0`), so `invoke`
|
|
1549
|
+
* receives the COERCED values (e.g. `7` sent for a string slot arrives at `invoke` as `'7'`), not
|
|
1550
|
+
* the raw call args. A call whose `args` fails to parse — a required key missing, or a value not
|
|
1551
|
+
* coercible to its slot's type — THROWS a typed `TOOL` {@link import('./errors.js').AgentToolError}
|
|
1552
|
+
* carrying the structured `explain` faults, and `invoke` is never called. Beyond that coercion,
|
|
1553
|
+
* enforcement is STRUCTURAL — required keys, `enum` membership, and numeric bounds — `format`
|
|
1554
|
+
* annotations (`email`, `date-time`, `uuid`, `uri`, ...) are NEVER asserted, mirroring
|
|
1555
|
+
* `@orkestrel/contract`'s own widening-only law for `schemaToShape`: a `format: true`-tuned
|
|
1556
|
+
* endpoint still ACCEPTS a non-conforming string in a format-tagged slot. A key NOT present in
|
|
1557
|
+
* the inferred (closed, `additionalProperties: false`) schema is NEVER a rejection either — it is
|
|
1558
|
+
* SILENTLY DROPPED before `invoke` runs (the same leniency `@orkestrel/contract`'s own `parse`
|
|
1559
|
+
* grants a closed object generally), so `invoke` may see fewer keys than the caller sent. Set
|
|
1560
|
+
* `validate: false` to restore the PRE-0.0.7 behavior exactly — `execute` passes the
|
|
1561
|
+
* model-supplied `args` straight to `invoke` UNCHANGED, unchecked and unstripped.
|
|
1562
|
+
*/
|
|
1563
|
+
export declare interface EndpointToolOptions {
|
|
1564
|
+
readonly format?: boolean;
|
|
1565
|
+
readonly enum?: boolean;
|
|
1566
|
+
readonly validate?: boolean;
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1352
1569
|
/**
|
|
1353
1570
|
* Expand the relation tool's FLAT dot-path `include` list into a live `@orkestrel/relation`
|
|
1354
1571
|
* {@link Include} tree — the pure leaf {@link import('./factories.js').createRelationTool} calls
|
|
@@ -1408,6 +1625,54 @@ export declare function expandTables(spec: TableSpec): TablesShape;
|
|
|
1408
1625
|
/** Flat dot-path relation include list, expanded via {@link import('./helpers.js').expandInclude}. */
|
|
1409
1626
|
export declare const includeShape: OptionalShape<ArrayShape<StringShape>>;
|
|
1410
1627
|
|
|
1628
|
+
export declare const INFER_TOOL_DESCRIPTION: string;
|
|
1629
|
+
|
|
1630
|
+
/**
|
|
1631
|
+
* The name {@link import('./factories.js').createInferTool} advertises by default — the key a
|
|
1632
|
+
* model calls and the `ToolManagerInterface` (`@orkestrel/agent`) registers under.
|
|
1633
|
+
*/
|
|
1634
|
+
export declare const INFER_TOOL_NAME = "infer";
|
|
1635
|
+
|
|
1636
|
+
/**
|
|
1637
|
+
* The lean {@link import('@orkestrel/agent').ToolInterface.summary} {@link import('./factories.js').createInferTool}
|
|
1638
|
+
* advertises in place of {@link INFER_TOOL_DESCRIPTION} — a `ToolManagerInterface.definitions()`
|
|
1639
|
+
* (`@orkestrel/agent`) advertises `summary ?? description`, so this one-sentence text stands in
|
|
1640
|
+
* for the full teaching description; the full text stays retrievable via
|
|
1641
|
+
* {@link import('./factories.js').createDescribeTool}.
|
|
1642
|
+
*/
|
|
1643
|
+
export declare const INFER_TOOL_SUMMARY = "Infer a JSON Schema (as advertised tool parameters) from one or more example values. Call describe('infer') for the required fields.";
|
|
1644
|
+
|
|
1645
|
+
/**
|
|
1646
|
+
* Options for {@link import('./factories.js').createInferTool} — advertised name/description
|
|
1647
|
+
* overrides only; `format` / `enum` are RUNTIME call arguments (see
|
|
1648
|
+
* {@link import('./shapers.js').inferToolShape}), not construction-time options, since a model
|
|
1649
|
+
* chooses them per call.
|
|
1650
|
+
*/
|
|
1651
|
+
export declare interface InferToolOptions {
|
|
1652
|
+
readonly name?: string;
|
|
1653
|
+
readonly description?: string;
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
/**
|
|
1657
|
+
* The shape of {@link import('./factories.js').createInferTool}'s call arguments — one or more
|
|
1658
|
+
* example `samples` to infer a JSON Schema from, plus per-call `format` / `enum` toggles and an
|
|
1659
|
+
* optional `candidates` array to check against the inferred schema.
|
|
1660
|
+
*
|
|
1661
|
+
* @remarks
|
|
1662
|
+
* `samples` requires at least one element (`min: 1`) — an empty array parses to `undefined`,
|
|
1663
|
+
* surfaced by the handler as a typed `TOOL` {@link import('./errors.js').AgentToolError}. When
|
|
1664
|
+
* `candidates` is present (any array, including empty), the handler compiles a contract from the
|
|
1665
|
+
* freshly inferred schema and checks each candidate against it with a STRICT guard (`.is`, no
|
|
1666
|
+
* coercion) — the opposite of {@link import('./factories.js').createEndpointTool}'s NORMALIZING
|
|
1667
|
+
* `.parse` enforcement.
|
|
1668
|
+
*/
|
|
1669
|
+
export declare const inferToolShape: ObjectShape<{
|
|
1670
|
+
samples: ArrayShape<JSONShape>;
|
|
1671
|
+
format: OptionalShape<BooleanShape>;
|
|
1672
|
+
enum: OptionalShape<BooleanShape>;
|
|
1673
|
+
candidates: OptionalShape<ArrayShape<JSONShape>>;
|
|
1674
|
+
}, false>;
|
|
1675
|
+
|
|
1411
1676
|
/**
|
|
1412
1677
|
* Type guard narrowing an unknown caught value to an {@link AgentToolError}.
|
|
1413
1678
|
*
|