@orkestrel/tool 0.0.3 → 0.0.5
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 +529 -81
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +349 -24
- package/dist/src/core/index.d.ts +349 -24
- package/dist/src/core/index.js +524 -83
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +225 -160
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +167 -3
- package/dist/src/server/index.d.ts +167 -3
- package/dist/src/server/index.js +225 -162
- package/dist/src/server/index.js.map +1 -1
- package/package.json +23 -21
- 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
package/dist/src/core/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { arrayShape, booleanShape, createContract, integerShape, isNonEmptyString, isRecord, isString, jsonShape, literalShape, numberShape, objectShape, optionalShape, rawShape, recordShape, schemaToParameters, stringShape, unionShape } from "@orkestrel/contract";
|
|
1
|
+
import { arrayShape, booleanShape, createContract, integerShape, isNonEmptyString, isRecord, isString, jsonShape, literalShape, numberShape, objectShape, optionalShape, rawShape, recordShape, samplesToSchema, schemaToObject, schemaToParameters, schemaToShape, stringShape, unionShape } from "@orkestrel/contract";
|
|
2
2
|
import { isTerminalError } from "@orkestrel/terminal";
|
|
3
3
|
import { createDatabase, createMemoryDriver, generateUUID, isDatabaseError, shapeToColumnType } from "@orkestrel/database";
|
|
4
4
|
import { isRelationError } from "@orkestrel/relation";
|
|
@@ -424,6 +424,108 @@ var RELATION_TOOL_DESCRIPTION = [
|
|
|
424
424
|
var RELATION_TOOL_LIMIT = 1e3;
|
|
425
425
|
/** The default cap on how many `include` path segments deep a `load` / `find` call may traverse — the relation tool's default include-depth ceiling. */
|
|
426
426
|
var RELATION_TOOL_DEPTH = 3;
|
|
427
|
+
/**
|
|
428
|
+
* The name {@link import('./factories.js').createInferTool} advertises by default — the key a
|
|
429
|
+
* model calls and the `ToolManagerInterface` (`@orkestrel/agent`) registers under.
|
|
430
|
+
*/
|
|
431
|
+
var INFER_TOOL_NAME = "infer";
|
|
432
|
+
/**
|
|
433
|
+
* The lean {@link import('@orkestrel/agent').ToolInterface.summary} {@link import('./factories.js').createInferTool}
|
|
434
|
+
* advertises in place of {@link INFER_TOOL_DESCRIPTION} — a `ToolManagerInterface.definitions()`
|
|
435
|
+
* (`@orkestrel/agent`) advertises `summary ?? description`, so this one-sentence text stands in
|
|
436
|
+
* for the full teaching description; the full text stays retrievable via
|
|
437
|
+
* {@link import('./factories.js').createDescribeTool}.
|
|
438
|
+
*/
|
|
439
|
+
var INFER_TOOL_SUMMARY = "Infer a JSON Schema (as advertised tool parameters) from one or more example values. Call describe('infer') for the required fields.";
|
|
440
|
+
var INFER_TOOL_DESCRIPTION = [
|
|
441
|
+
"Infer a JSON Schema from example values, returned in the same shape a tool advertises its parameters.",
|
|
442
|
+
"",
|
|
443
|
+
"Required:",
|
|
444
|
+
" samples - an array of at least one example value to infer the schema from.",
|
|
445
|
+
"Optional:",
|
|
446
|
+
" format - infer string formats (date-time, email, ...) from the samples. Defaults to false.",
|
|
447
|
+
" enum - infer enum constraints from repeated literal values across the samples. Defaults to false.",
|
|
448
|
+
" candidates - values to check against the freshly inferred schema. When present, the result",
|
|
449
|
+
" is wrapped as { parameters, checks } instead of the bare parameters record, one",
|
|
450
|
+
" check per candidate (same index). Every check has the uniform shape",
|
|
451
|
+
" { index, valid, coercible, faults? }. `valid` is a STRICT verdict (no coercion)",
|
|
452
|
+
" — e.g. the number 7 is NOT valid against a string slot. `coercible` answers a",
|
|
453
|
+
" separate question: would the SAME value be accepted by an endpoint tool call,",
|
|
454
|
+
" whose enforcement NORMALIZES args (7 coerces to '7')? So 7 against a string slot",
|
|
455
|
+
" yields { valid: false, coercible: true, faults: [] } — a strict mismatch that",
|
|
456
|
+
" normalization would silently accept, so faults is EMPTY. `faults` only ever",
|
|
457
|
+
" populates for a non-coercible mismatch (a wrong type normalization cannot fix,",
|
|
458
|
+
" a missing required key, an out-of-enum value); checks never throw, regardless of",
|
|
459
|
+
" candidate shape.",
|
|
460
|
+
"Example (no candidates):",
|
|
461
|
+
` in: ${JSON.stringify({ samples: [{
|
|
462
|
+
id: 1,
|
|
463
|
+
name: "Ada"
|
|
464
|
+
}, {
|
|
465
|
+
id: 2,
|
|
466
|
+
name: "Bob"
|
|
467
|
+
}] })}`,
|
|
468
|
+
` out: ${JSON.stringify({
|
|
469
|
+
type: "object",
|
|
470
|
+
properties: {
|
|
471
|
+
id: { type: "integer" },
|
|
472
|
+
name: { type: "string" }
|
|
473
|
+
},
|
|
474
|
+
required: ["id", "name"],
|
|
475
|
+
additionalProperties: false
|
|
476
|
+
})}`,
|
|
477
|
+
"Example (with candidates):",
|
|
478
|
+
` in: ${JSON.stringify({
|
|
479
|
+
samples: [{
|
|
480
|
+
id: 1,
|
|
481
|
+
name: "Ada"
|
|
482
|
+
}],
|
|
483
|
+
candidates: [
|
|
484
|
+
{
|
|
485
|
+
id: 3,
|
|
486
|
+
name: "Cy"
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
id: "x",
|
|
490
|
+
name: "Cy"
|
|
491
|
+
},
|
|
492
|
+
{
|
|
493
|
+
id: 1,
|
|
494
|
+
name: 7
|
|
495
|
+
}
|
|
496
|
+
]
|
|
497
|
+
})}`,
|
|
498
|
+
` out: ${JSON.stringify({
|
|
499
|
+
parameters: {
|
|
500
|
+
type: "object",
|
|
501
|
+
properties: {
|
|
502
|
+
id: { type: "integer" },
|
|
503
|
+
name: { type: "string" }
|
|
504
|
+
},
|
|
505
|
+
required: ["id", "name"],
|
|
506
|
+
additionalProperties: false
|
|
507
|
+
},
|
|
508
|
+
checks: [
|
|
509
|
+
{
|
|
510
|
+
index: 0,
|
|
511
|
+
valid: true,
|
|
512
|
+
coercible: true
|
|
513
|
+
},
|
|
514
|
+
{
|
|
515
|
+
index: 1,
|
|
516
|
+
valid: false,
|
|
517
|
+
coercible: false,
|
|
518
|
+
faults: "<structured faults>"
|
|
519
|
+
},
|
|
520
|
+
{
|
|
521
|
+
index: 2,
|
|
522
|
+
valid: false,
|
|
523
|
+
coercible: true,
|
|
524
|
+
faults: []
|
|
525
|
+
}
|
|
526
|
+
]
|
|
527
|
+
})}`
|
|
528
|
+
].join("\n");
|
|
427
529
|
//#endregion
|
|
428
530
|
//#region src/core/errors.ts
|
|
429
531
|
/**
|
|
@@ -457,12 +559,11 @@ var RELATION_TOOL_DEPTH = 3;
|
|
|
457
559
|
*/
|
|
458
560
|
var AgentToolError = class extends Error {
|
|
459
561
|
code;
|
|
460
|
-
context;
|
|
461
562
|
constructor(code, message, context) {
|
|
462
563
|
super(message);
|
|
463
564
|
this.name = "AgentToolError";
|
|
464
565
|
this.code = code;
|
|
465
|
-
this.context = context;
|
|
566
|
+
if (context !== void 0) this.context = context;
|
|
466
567
|
}
|
|
467
568
|
};
|
|
468
569
|
/**
|
|
@@ -1105,6 +1206,28 @@ var relationToolShape = unionShape(objectShape({
|
|
|
1105
1206
|
description: "The \"through\" relation name."
|
|
1106
1207
|
})
|
|
1107
1208
|
}));
|
|
1209
|
+
/**
|
|
1210
|
+
* The shape of {@link import('./factories.js').createInferTool}'s call arguments — one or more
|
|
1211
|
+
* example `samples` to infer a JSON Schema from, plus per-call `format` / `enum` toggles and an
|
|
1212
|
+
* optional `candidates` array to check against the inferred schema.
|
|
1213
|
+
*
|
|
1214
|
+
* @remarks
|
|
1215
|
+
* `samples` requires at least one element (`min: 1`) — an empty array parses to `undefined`,
|
|
1216
|
+
* surfaced by the handler as a typed `TOOL` {@link import('./errors.js').AgentToolError}. When
|
|
1217
|
+
* `candidates` is present (any array, including empty), the handler compiles a contract from the
|
|
1218
|
+
* freshly inferred schema and checks each candidate against it with a STRICT guard (`.is`, no
|
|
1219
|
+
* coercion) — the opposite of {@link import('./factories.js').createEndpointTool}'s NORMALIZING
|
|
1220
|
+
* `.parse` enforcement.
|
|
1221
|
+
*/
|
|
1222
|
+
var inferToolShape = objectShape({
|
|
1223
|
+
samples: arrayShape(jsonShape(), {
|
|
1224
|
+
min: 1,
|
|
1225
|
+
description: "The example values to infer a JSON Schema from (at least one)."
|
|
1226
|
+
}),
|
|
1227
|
+
format: optionalShape(booleanShape({ description: "Infer string formats (date-time, email, ...) from the samples. Defaults to false." })),
|
|
1228
|
+
enum: optionalShape(booleanShape({ description: "Infer enum constraints from repeated literal values. Defaults to false." })),
|
|
1229
|
+
candidates: optionalShape(arrayShape(jsonShape(), { description: "Optional values to check against the freshly inferred schema. When present, the tool returns a per-candidate verdict (strict — no coercion) alongside the inferred parameters." }))
|
|
1230
|
+
});
|
|
1108
1231
|
//#endregion
|
|
1109
1232
|
//#region src/core/helpers.ts
|
|
1110
1233
|
/**
|
|
@@ -1419,19 +1542,6 @@ function relationToolCode(error) {
|
|
|
1419
1542
|
* ```
|
|
1420
1543
|
*/
|
|
1421
1544
|
function expandInclude(paths, depth) {
|
|
1422
|
-
function merge(base, segments) {
|
|
1423
|
-
const [head, ...rest] = segments;
|
|
1424
|
-
const existing = base[head];
|
|
1425
|
-
if (rest.length === 0) return {
|
|
1426
|
-
...base,
|
|
1427
|
-
[head]: existing === void 0 ? true : existing
|
|
1428
|
-
};
|
|
1429
|
-
const nextBase = typeof existing === "object" ? existing : {};
|
|
1430
|
-
return {
|
|
1431
|
-
...base,
|
|
1432
|
-
[head]: merge(nextBase, rest)
|
|
1433
|
-
};
|
|
1434
|
-
}
|
|
1435
1545
|
let include = {};
|
|
1436
1546
|
for (const path of paths ?? []) {
|
|
1437
1547
|
const segments = path.split(".");
|
|
@@ -1439,7 +1549,42 @@ function expandInclude(paths, depth) {
|
|
|
1439
1549
|
path,
|
|
1440
1550
|
depth
|
|
1441
1551
|
});
|
|
1442
|
-
|
|
1552
|
+
const ancestors = [];
|
|
1553
|
+
let branch = include;
|
|
1554
|
+
const last = segments.length - 1;
|
|
1555
|
+
for (let index = 0; index < last; index++) {
|
|
1556
|
+
const segment = segments[index];
|
|
1557
|
+
if (segment === void 0) throw new AgentToolError("TOOL", `malformed include path '${path}'`, {
|
|
1558
|
+
path,
|
|
1559
|
+
depth
|
|
1560
|
+
});
|
|
1561
|
+
ancestors.push(branch);
|
|
1562
|
+
const existing = branch[segment];
|
|
1563
|
+
branch = typeof existing === "object" ? existing : {};
|
|
1564
|
+
}
|
|
1565
|
+
const leaf = segments[last];
|
|
1566
|
+
if (leaf === void 0) throw new AgentToolError("TOOL", `malformed include path '${path}'`, {
|
|
1567
|
+
path,
|
|
1568
|
+
depth
|
|
1569
|
+
});
|
|
1570
|
+
const existing = branch[leaf];
|
|
1571
|
+
let merged = {
|
|
1572
|
+
...branch,
|
|
1573
|
+
[leaf]: existing === void 0 ? true : existing
|
|
1574
|
+
};
|
|
1575
|
+
for (let index = last - 1; index >= 0; index--) {
|
|
1576
|
+
const ancestor = ancestors[index];
|
|
1577
|
+
const segment = segments[index];
|
|
1578
|
+
if (ancestor === void 0 || segment === void 0) throw new AgentToolError("TOOL", `malformed include path '${path}'`, {
|
|
1579
|
+
path,
|
|
1580
|
+
depth
|
|
1581
|
+
});
|
|
1582
|
+
merged = {
|
|
1583
|
+
...ancestor,
|
|
1584
|
+
[segment]: merged
|
|
1585
|
+
};
|
|
1586
|
+
}
|
|
1587
|
+
include = merged;
|
|
1443
1588
|
}
|
|
1444
1589
|
return include;
|
|
1445
1590
|
}
|
|
@@ -1467,7 +1612,11 @@ function relationManagerOf(managers, name) {
|
|
|
1467
1612
|
return manager;
|
|
1468
1613
|
}
|
|
1469
1614
|
const names = Object.keys(managers);
|
|
1470
|
-
|
|
1615
|
+
const [single] = names;
|
|
1616
|
+
if (names.length === 1 && single !== void 0) {
|
|
1617
|
+
const manager = managers[single];
|
|
1618
|
+
if (manager !== void 0) return manager;
|
|
1619
|
+
}
|
|
1471
1620
|
throw new AgentToolError("TOOL", "no relation manager resolved for the call", { managers: names });
|
|
1472
1621
|
}
|
|
1473
1622
|
/**
|
|
@@ -1695,6 +1844,105 @@ var DatabaseDefinitionStore = class {
|
|
|
1695
1844
|
}
|
|
1696
1845
|
};
|
|
1697
1846
|
//#endregion
|
|
1847
|
+
//#region src/core/databases/DatabaseResolver.ts
|
|
1848
|
+
/**
|
|
1849
|
+
* Resolve database definitions into cached live handles for database tools.
|
|
1850
|
+
*
|
|
1851
|
+
* @example
|
|
1852
|
+
* ```ts
|
|
1853
|
+
* import { DatabaseResolver } from '@orkestrel/tool'
|
|
1854
|
+
*
|
|
1855
|
+
* const resolver = new DatabaseResolver(handles, drivers, key, store)
|
|
1856
|
+
* const database = await resolver.resolve('shop')
|
|
1857
|
+
* ```
|
|
1858
|
+
*/
|
|
1859
|
+
var DatabaseResolver = class {
|
|
1860
|
+
#handles;
|
|
1861
|
+
#drivers;
|
|
1862
|
+
#key;
|
|
1863
|
+
#store;
|
|
1864
|
+
/**
|
|
1865
|
+
* Create a database resolver over the tool's live state and optional definition store.
|
|
1866
|
+
*
|
|
1867
|
+
* @param handles - Initial live database handles cached by id
|
|
1868
|
+
* @param drivers - Driver factories keyed by definition driver name
|
|
1869
|
+
* @param key - Key generator supplied to newly created databases
|
|
1870
|
+
* @param store - Optional persistent definition store
|
|
1871
|
+
*/
|
|
1872
|
+
constructor(handles, drivers, key, store) {
|
|
1873
|
+
this.#handles = new Map(handles);
|
|
1874
|
+
this.#drivers = drivers;
|
|
1875
|
+
this.#key = key;
|
|
1876
|
+
this.#store = store;
|
|
1877
|
+
}
|
|
1878
|
+
/**
|
|
1879
|
+
* Determine whether a live database is cached by id.
|
|
1880
|
+
*
|
|
1881
|
+
* @param id - Database id
|
|
1882
|
+
* @returns Whether a live handle is cached
|
|
1883
|
+
*/
|
|
1884
|
+
has(id) {
|
|
1885
|
+
return this.#handles.has(id);
|
|
1886
|
+
}
|
|
1887
|
+
/**
|
|
1888
|
+
* Read a cached database without consulting the definition store.
|
|
1889
|
+
*
|
|
1890
|
+
* @param id - Database id
|
|
1891
|
+
* @returns The cached live database, or `undefined`
|
|
1892
|
+
*/
|
|
1893
|
+
get(id) {
|
|
1894
|
+
return this.#handles.get(id);
|
|
1895
|
+
}
|
|
1896
|
+
/**
|
|
1897
|
+
* Cache a live database by id.
|
|
1898
|
+
*
|
|
1899
|
+
* @param id - Database id
|
|
1900
|
+
* @param database - Live database handle
|
|
1901
|
+
* @returns Nothing
|
|
1902
|
+
*/
|
|
1903
|
+
set(id, database) {
|
|
1904
|
+
this.#handles.set(id, database);
|
|
1905
|
+
}
|
|
1906
|
+
/**
|
|
1907
|
+
* Remove a cached live database by id.
|
|
1908
|
+
*
|
|
1909
|
+
* @param id - Database id
|
|
1910
|
+
* @returns Nothing
|
|
1911
|
+
*/
|
|
1912
|
+
delete(id) {
|
|
1913
|
+
this.#handles.delete(id);
|
|
1914
|
+
}
|
|
1915
|
+
/**
|
|
1916
|
+
* Resolve a cached or stored database by id.
|
|
1917
|
+
*
|
|
1918
|
+
* @param id - Database definition id
|
|
1919
|
+
* @returns The cached or newly constructed live database
|
|
1920
|
+
*/
|
|
1921
|
+
async resolve(id) {
|
|
1922
|
+
const cached = this.#handles.get(id);
|
|
1923
|
+
if (cached !== void 0) return cached;
|
|
1924
|
+
if (this.#store !== void 0) {
|
|
1925
|
+
const definition = await this.#store.get(id);
|
|
1926
|
+
if (definition !== void 0) {
|
|
1927
|
+
const factory = this.#drivers[definition.driver];
|
|
1928
|
+
if (factory === void 0) throw new AgentToolError("TOOL", `unknown driver '${definition.driver}'`, {
|
|
1929
|
+
id,
|
|
1930
|
+
driver: definition.driver
|
|
1931
|
+
});
|
|
1932
|
+
const handle = createDatabase({
|
|
1933
|
+
driver: factory(),
|
|
1934
|
+
tables: expandTables(definition.tables),
|
|
1935
|
+
...definition.keys === void 0 ? {} : { keys: definition.keys },
|
|
1936
|
+
key: this.#key
|
|
1937
|
+
});
|
|
1938
|
+
this.set(id, handle);
|
|
1939
|
+
return handle;
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
throw new AgentToolError("TOOL", `unknown database '${id}'`, { id });
|
|
1943
|
+
}
|
|
1944
|
+
};
|
|
1945
|
+
//#endregion
|
|
1698
1946
|
//#region src/core/factories.ts
|
|
1699
1947
|
/**
|
|
1700
1948
|
* Wrap a registered tool as a {@link WorkflowFunction} (`@orkestrel/workflow`) — the OPT-IN
|
|
@@ -1810,7 +2058,9 @@ function createAgentFunction(agent, options) {
|
|
|
1810
2058
|
}));
|
|
1811
2059
|
}
|
|
1812
2060
|
const signal = controller.signal;
|
|
1813
|
-
const onAbort = ()
|
|
2061
|
+
const onAbort = { handleEvent() {
|
|
2062
|
+
agent.abort(signal.reason);
|
|
2063
|
+
} };
|
|
1814
2064
|
if (signal.aborted) agent.abort(signal.reason);
|
|
1815
2065
|
else signal.addEventListener("abort", onAbort, { once: true });
|
|
1816
2066
|
try {
|
|
@@ -1915,12 +2165,13 @@ function createWorkflowTool(definition, runner, options) {
|
|
|
1915
2165
|
const depth = options?.depth ?? 0;
|
|
1916
2166
|
const ancestry = options?.ancestry ?? [];
|
|
1917
2167
|
const store = options?.store;
|
|
2168
|
+
const parameters = schemaToParameters(steps.schema);
|
|
1918
2169
|
return createTool({
|
|
1919
2170
|
name: WORKFLOW_TOOL_NAME,
|
|
1920
2171
|
description: WORKFLOW_TOOL_DESCRIPTION,
|
|
1921
2172
|
summary: WORKFLOW_TOOL_SUMMARY,
|
|
1922
|
-
parameters:
|
|
1923
|
-
|
|
2173
|
+
...parameters === void 0 ? {} : { parameters },
|
|
2174
|
+
async execute(args) {
|
|
1924
2175
|
let target;
|
|
1925
2176
|
if (Object.keys(args).length === 0) target = definition;
|
|
1926
2177
|
else if (Array.isArray(args.steps)) {
|
|
@@ -2004,8 +2255,8 @@ function createWorkspaceTool(options) {
|
|
|
2004
2255
|
name: options?.name ?? "workspace",
|
|
2005
2256
|
description: options?.description ?? WORKSPACE_TOOL_DESCRIPTION,
|
|
2006
2257
|
summary: WORKSPACE_TOOL_SUMMARY,
|
|
2007
|
-
parameters,
|
|
2008
|
-
execute
|
|
2258
|
+
...parameters === void 0 ? {} : { parameters },
|
|
2259
|
+
execute(args) {
|
|
2009
2260
|
const op = contract.parse(args);
|
|
2010
2261
|
if (op === void 0) throw new WorkspaceError("TOOL", `unknown or malformed operation`, { args });
|
|
2011
2262
|
if (op.operation === "workspaces") {
|
|
@@ -2039,14 +2290,14 @@ function createWorkspaceTool(options) {
|
|
|
2039
2290
|
}));
|
|
2040
2291
|
case "has": return active?.has(op.path) ?? false;
|
|
2041
2292
|
case "search": return active?.search(op.query, {
|
|
2042
|
-
regex: op.regex,
|
|
2043
|
-
exact: op.exact,
|
|
2044
|
-
limit: op.limit
|
|
2293
|
+
...op.regex === void 0 ? {} : { regex: op.regex },
|
|
2294
|
+
...op.exact === void 0 ? {} : { exact: op.exact },
|
|
2295
|
+
...op.limit === void 0 ? {} : { limit: op.limit }
|
|
2045
2296
|
}) ?? [];
|
|
2046
2297
|
case "replace": return (active ?? manager.add()).replace(op.query, op.replacement, {
|
|
2047
|
-
regex: op.regex,
|
|
2048
|
-
exact: op.exact,
|
|
2049
|
-
limit: op.limit
|
|
2298
|
+
...op.regex === void 0 ? {} : { regex: op.regex },
|
|
2299
|
+
...op.exact === void 0 ? {} : { exact: op.exact },
|
|
2300
|
+
...op.limit === void 0 ? {} : { limit: op.limit }
|
|
2050
2301
|
});
|
|
2051
2302
|
case "write": {
|
|
2052
2303
|
const workspace = active ?? manager.add();
|
|
@@ -2146,8 +2397,8 @@ function createAgentTool(registry, options) {
|
|
|
2146
2397
|
name: options?.name ?? "agent",
|
|
2147
2398
|
description: options?.description ?? AGENT_TOOL_DESCRIPTION,
|
|
2148
2399
|
summary: AGENT_TOOL_SUMMARY,
|
|
2149
|
-
parameters,
|
|
2150
|
-
|
|
2400
|
+
...parameters === void 0 ? {} : { parameters },
|
|
2401
|
+
async execute(args) {
|
|
2151
2402
|
const call = contract.parse(args);
|
|
2152
2403
|
if (call === void 0) throw new AgentToolError("TOOL", "malformed agent-delegation call", { args });
|
|
2153
2404
|
const provider = call.provider ?? options?.provider;
|
|
@@ -2218,12 +2469,13 @@ function createAgentTool(registry, options) {
|
|
|
2218
2469
|
*/
|
|
2219
2470
|
function createDescribeTool(tools) {
|
|
2220
2471
|
const contract = createContract(describeToolShape);
|
|
2472
|
+
const parameters = schemaToParameters(contract.schema);
|
|
2221
2473
|
return createTool({
|
|
2222
2474
|
name: DESCRIBE_TOOL_NAME,
|
|
2223
2475
|
description: DESCRIBE_TOOL_DESCRIPTION,
|
|
2224
2476
|
summary: DESCRIBE_TOOL_SUMMARY,
|
|
2225
|
-
parameters:
|
|
2226
|
-
|
|
2477
|
+
...parameters === void 0 ? {} : { parameters },
|
|
2478
|
+
async execute(args) {
|
|
2227
2479
|
const call = contract.parse(args);
|
|
2228
2480
|
if (call === void 0) throw new AgentToolError("TOOL", "malformed describe call", { args });
|
|
2229
2481
|
const tool = tools.tool(call.name);
|
|
@@ -2273,8 +2525,8 @@ function createPromptTool(options) {
|
|
|
2273
2525
|
name: options.name ?? "ask",
|
|
2274
2526
|
description: options.description ?? PROMPT_TOOL_DESCRIPTION,
|
|
2275
2527
|
summary: PROMPT_TOOL_SUMMARY,
|
|
2276
|
-
parameters,
|
|
2277
|
-
|
|
2528
|
+
...parameters === void 0 ? {} : { parameters },
|
|
2529
|
+
async execute(args) {
|
|
2278
2530
|
const call = contract.parse(args);
|
|
2279
2531
|
if (call === void 0) throw new AgentToolError("TOOL", "malformed ask call", { args });
|
|
2280
2532
|
if ((call.form === "select" || call.form === "checkbox") && (call.choices ?? []).length === 0) throw new AgentToolError("TOOL", "select/checkbox requires at least one choice", {
|
|
@@ -2375,8 +2627,8 @@ function createAnswerTool(options) {
|
|
|
2375
2627
|
name: options.name ?? "answer",
|
|
2376
2628
|
description: options.description ?? ANSWER_TOOL_DESCRIPTION,
|
|
2377
2629
|
summary: ANSWER_TOOL_SUMMARY,
|
|
2378
|
-
parameters,
|
|
2379
|
-
|
|
2630
|
+
...parameters === void 0 ? {} : { parameters },
|
|
2631
|
+
async execute(args) {
|
|
2380
2632
|
const call = contract.parse(args);
|
|
2381
2633
|
if (call === void 0) throw new AgentToolError("TOOL", "malformed answer call", { args });
|
|
2382
2634
|
if (call.operation === "pending") return options.manager.pending(options.to).map((prompt) => ({
|
|
@@ -2508,40 +2760,17 @@ function createDatabaseTool(options = {}) {
|
|
|
2508
2760
|
const parameters = schemaToParameters(contract.schema);
|
|
2509
2761
|
const handles = new Map(Object.entries(options.databases ?? {}));
|
|
2510
2762
|
const definitions = /* @__PURE__ */ new Map();
|
|
2511
|
-
const drivers = options.drivers ?? { memory:
|
|
2763
|
+
const drivers = options.drivers ?? { memory: createMemoryDriver };
|
|
2512
2764
|
const key = options.key ?? generateUUID;
|
|
2513
2765
|
const cap = options.limit ?? 1e3;
|
|
2514
2766
|
const store = options.store;
|
|
2515
|
-
|
|
2516
|
-
const cached = handles.get(id);
|
|
2517
|
-
if (cached !== void 0) return cached;
|
|
2518
|
-
if (store !== void 0) {
|
|
2519
|
-
const definition = await store.get(id);
|
|
2520
|
-
if (definition !== void 0) {
|
|
2521
|
-
const factory = drivers[definition.driver];
|
|
2522
|
-
if (factory === void 0) throw new AgentToolError("TOOL", `unknown driver '${definition.driver}'`, {
|
|
2523
|
-
id,
|
|
2524
|
-
driver: definition.driver
|
|
2525
|
-
});
|
|
2526
|
-
const handle = createDatabase({
|
|
2527
|
-
driver: factory(),
|
|
2528
|
-
tables: expandTables(definition.tables),
|
|
2529
|
-
...definition.keys === void 0 ? {} : { keys: definition.keys },
|
|
2530
|
-
key
|
|
2531
|
-
});
|
|
2532
|
-
handles.set(id, handle);
|
|
2533
|
-
definitions.set(id, definition);
|
|
2534
|
-
return handle;
|
|
2535
|
-
}
|
|
2536
|
-
}
|
|
2537
|
-
throw new AgentToolError("TOOL", `unknown database '${id}'`, { id });
|
|
2538
|
-
}
|
|
2767
|
+
const resolver = store === void 0 ? new DatabaseResolver(handles, drivers, key) : new DatabaseResolver(handles, drivers, key, store);
|
|
2539
2768
|
return createTool({
|
|
2540
2769
|
name: options.name ?? "database",
|
|
2541
2770
|
description: options.description ?? DATABASE_TOOL_DESCRIPTION,
|
|
2542
2771
|
summary: DATABASE_TOOL_SUMMARY,
|
|
2543
|
-
parameters,
|
|
2544
|
-
|
|
2772
|
+
...parameters === void 0 ? {} : { parameters },
|
|
2773
|
+
async execute(args) {
|
|
2545
2774
|
const call = contract.parse(args);
|
|
2546
2775
|
if (call === void 0) throw new AgentToolError("TOOL", "malformed database call", { args });
|
|
2547
2776
|
if (options.readonly === true && DATABASE_TOOL_MUTATIONS.has(call.operation)) throw new AgentToolError("TOOL", `operation '${call.operation}' is disabled in readonly mode`, { operation: call.operation });
|
|
@@ -2549,7 +2778,7 @@ function createDatabaseTool(options = {}) {
|
|
|
2549
2778
|
try {
|
|
2550
2779
|
switch (call.operation) {
|
|
2551
2780
|
case "create": {
|
|
2552
|
-
if (
|
|
2781
|
+
if (resolver.has(call.id) || store !== void 0 && await store.get(call.id) !== void 0) throw new AgentToolError("TOOL", `database '${call.id}' already exists`, { id: call.id });
|
|
2553
2782
|
const name = call.driver ?? "memory";
|
|
2554
2783
|
const factory = drivers[name];
|
|
2555
2784
|
if (factory === void 0) throw new AgentToolError("TOOL", `unknown driver '${name}'`, {
|
|
@@ -2564,7 +2793,7 @@ function createDatabaseTool(options = {}) {
|
|
|
2564
2793
|
...keys === void 0 ? {} : { keys },
|
|
2565
2794
|
key
|
|
2566
2795
|
});
|
|
2567
|
-
|
|
2796
|
+
resolver.set(call.id, handle);
|
|
2568
2797
|
const definition = {
|
|
2569
2798
|
id: call.id,
|
|
2570
2799
|
driver: name,
|
|
@@ -2579,7 +2808,7 @@ function createDatabaseTool(options = {}) {
|
|
|
2579
2808
|
};
|
|
2580
2809
|
}
|
|
2581
2810
|
case "tables": {
|
|
2582
|
-
const handle = await resolve(call.id);
|
|
2811
|
+
const handle = await resolver.resolve(call.id);
|
|
2583
2812
|
return { tables: Object.keys(handle.export()).map((name) => {
|
|
2584
2813
|
const table = handle.table(name);
|
|
2585
2814
|
return {
|
|
@@ -2590,14 +2819,14 @@ function createDatabaseTool(options = {}) {
|
|
|
2590
2819
|
}) };
|
|
2591
2820
|
}
|
|
2592
2821
|
case "get": {
|
|
2593
|
-
const table = (await resolve(call.id)).table(call.table);
|
|
2822
|
+
const table = (await resolver.resolve(call.id)).table(call.table);
|
|
2594
2823
|
const many = Array.isArray(call.key);
|
|
2595
2824
|
const keys = Array.isArray(call.key) ? call.key : [call.key];
|
|
2596
2825
|
const rows = await table.get(keys);
|
|
2597
2826
|
return many ? { rows } : { row: rows[0] };
|
|
2598
2827
|
}
|
|
2599
2828
|
case "records": {
|
|
2600
|
-
const table = (await resolve(call.id)).table(call.table);
|
|
2829
|
+
const table = (await resolver.resolve(call.id)).table(call.table);
|
|
2601
2830
|
const { criteria: probe, limit } = clampCriteria(criteriaOf(call.criteria), cap);
|
|
2602
2831
|
const rows = await table.records(probe, read);
|
|
2603
2832
|
const truncated = rows.length > limit;
|
|
@@ -2609,24 +2838,24 @@ function createDatabaseTool(options = {}) {
|
|
|
2609
2838
|
limit
|
|
2610
2839
|
};
|
|
2611
2840
|
}
|
|
2612
|
-
case "count": return { count: await (await resolve(call.id)).table(call.table).count(criteriaOf(call.criteria), read) };
|
|
2613
|
-
case "aggregate": return { value: await (await resolve(call.id)).table(call.table).aggregate(call.function, call.column, criteriaOf(call.criteria), read) };
|
|
2841
|
+
case "count": return { count: await (await resolver.resolve(call.id)).table(call.table).count(criteriaOf(call.criteria), read) };
|
|
2842
|
+
case "aggregate": return { value: await (await resolver.resolve(call.id)).table(call.table).aggregate(call.function, call.column, criteriaOf(call.criteria), read) };
|
|
2614
2843
|
case "add": {
|
|
2615
|
-
const table = (await resolve(call.id)).table(call.table);
|
|
2844
|
+
const table = (await resolver.resolve(call.id)).table(call.table);
|
|
2616
2845
|
const many = Array.isArray(call.row);
|
|
2617
2846
|
const rows = Array.isArray(call.row) ? call.row : [call.row];
|
|
2618
2847
|
const keys = await table.add(rows, read);
|
|
2619
2848
|
return many ? { keys } : { key: keys[0] };
|
|
2620
2849
|
}
|
|
2621
2850
|
case "set": {
|
|
2622
|
-
const table = (await resolve(call.id)).table(call.table);
|
|
2851
|
+
const table = (await resolver.resolve(call.id)).table(call.table);
|
|
2623
2852
|
const many = Array.isArray(call.row);
|
|
2624
2853
|
const rows = Array.isArray(call.row) ? call.row : [call.row];
|
|
2625
2854
|
const keys = await table.set(rows, read);
|
|
2626
2855
|
return many ? { keys } : { key: keys[0] };
|
|
2627
2856
|
}
|
|
2628
2857
|
case "update": {
|
|
2629
|
-
const table = (await resolve(call.id)).table(call.table);
|
|
2858
|
+
const table = (await resolver.resolve(call.id)).table(call.table);
|
|
2630
2859
|
const changes = call.changes;
|
|
2631
2860
|
const many = Array.isArray(call.key);
|
|
2632
2861
|
const keys = Array.isArray(call.key) ? call.key : [call.key];
|
|
@@ -2634,14 +2863,14 @@ function createDatabaseTool(options = {}) {
|
|
|
2634
2863
|
return many ? { updated } : { updated: updated[0] };
|
|
2635
2864
|
}
|
|
2636
2865
|
case "remove": {
|
|
2637
|
-
const table = (await resolve(call.id)).table(call.table);
|
|
2866
|
+
const table = (await resolver.resolve(call.id)).table(call.table);
|
|
2638
2867
|
const many = Array.isArray(call.key);
|
|
2639
2868
|
const keys = Array.isArray(call.key) ? call.key : [call.key];
|
|
2640
2869
|
const removed = await table.remove(keys, read);
|
|
2641
2870
|
return many ? { removed } : { removed: removed[0] };
|
|
2642
2871
|
}
|
|
2643
2872
|
case "migrate": {
|
|
2644
|
-
const handle = await resolve(call.id);
|
|
2873
|
+
const handle = await resolver.resolve(call.id);
|
|
2645
2874
|
const previous = handle.export();
|
|
2646
2875
|
const deployed = Object.entries(previous).map(([name, table]) => tableSchema(name, table));
|
|
2647
2876
|
const tables = call.tables;
|
|
@@ -2653,8 +2882,8 @@ function createDatabaseTool(options = {}) {
|
|
|
2653
2882
|
const declared = expandTables(tables);
|
|
2654
2883
|
const migrated = handle.import(declared, Object.keys(keys).length > 0 ? keys : void 0);
|
|
2655
2884
|
const migration = await migrated.migrate(deployed, read);
|
|
2656
|
-
|
|
2657
|
-
const tracked = definitions.get(call.id);
|
|
2885
|
+
resolver.set(call.id, migrated);
|
|
2886
|
+
const tracked = definitions.get(call.id) ?? (store === void 0 ? void 0 : await store.get(call.id));
|
|
2658
2887
|
if (tracked !== void 0) {
|
|
2659
2888
|
const updated = {
|
|
2660
2889
|
id: call.id,
|
|
@@ -2668,11 +2897,11 @@ function createDatabaseTool(options = {}) {
|
|
|
2668
2897
|
return { migration };
|
|
2669
2898
|
}
|
|
2670
2899
|
case "destroy": {
|
|
2671
|
-
const cached =
|
|
2900
|
+
const cached = resolver.get(call.id);
|
|
2672
2901
|
const persisted = store !== void 0 && cached === void 0 ? await store.get(call.id) !== void 0 : false;
|
|
2673
2902
|
if (cached !== void 0) {
|
|
2674
2903
|
await cached.close();
|
|
2675
|
-
|
|
2904
|
+
resolver.delete(call.id);
|
|
2676
2905
|
}
|
|
2677
2906
|
definitions.delete(call.id);
|
|
2678
2907
|
if (store !== void 0) await store.delete(call.id);
|
|
@@ -2752,8 +2981,8 @@ function createRelationTool(options) {
|
|
|
2752
2981
|
name: options.name ?? "relation",
|
|
2753
2982
|
description: options.description ?? RELATION_TOOL_DESCRIPTION,
|
|
2754
2983
|
summary: RELATION_TOOL_SUMMARY,
|
|
2755
|
-
parameters,
|
|
2756
|
-
|
|
2984
|
+
...parameters === void 0 ? {} : { parameters },
|
|
2985
|
+
async execute(args) {
|
|
2757
2986
|
const call = contract.parse(args);
|
|
2758
2987
|
if (call === void 0) throw new AgentToolError("TOOL", "malformed relation call", { args });
|
|
2759
2988
|
try {
|
|
@@ -2819,7 +3048,219 @@ function createRelationTool(options) {
|
|
|
2819
3048
|
}
|
|
2820
3049
|
});
|
|
2821
3050
|
}
|
|
3051
|
+
/**
|
|
3052
|
+
* Build a standalone LLM-callable tool that infers a JSON Schema from example values — the
|
|
3053
|
+
* utility half of the "existing API/DB → MCP tool" bridge (the other half,
|
|
3054
|
+
* {@link createEndpointTool}, wraps one CONCRETE endpoint).
|
|
3055
|
+
*
|
|
3056
|
+
* @remarks
|
|
3057
|
+
* The universal tool-handler contract (AGENTS §14): validates the call args against
|
|
3058
|
+
* {@link import('./shapers.js').inferToolShape} (`samples` non-empty, `format` / `enum` optional
|
|
3059
|
+
* booleans, `candidates` an optional array), infers a schema via `@orkestrel/contract`'s
|
|
3060
|
+
* `samplesToSchema`, wraps a non-object root as `{ value: <schema> }` via `schemaToObject` (mirrors
|
|
3061
|
+
* the tool-parameters convention every other `create*Tool` factory advertises), and RETURNS the
|
|
3062
|
+
* resulting parameters record. An empty `samples` array fails `inferToolShape`'s `min: 1` bound —
|
|
3063
|
+
* `contract.parse` returns `undefined` and the handler throws a typed `TOOL`
|
|
3064
|
+
* {@link import('./errors.js').AgentToolError}.
|
|
3065
|
+
*
|
|
3066
|
+
* When `candidates` is ABSENT, the return is the bare parameters record — unchanged from before
|
|
3067
|
+
* this array existed. When `candidates` is PRESENT (any array, including empty), the handler
|
|
3068
|
+
* compiles a SEPARATE per-call contract from the RAW inferred schema (via `@orkestrel/contract`'s
|
|
3069
|
+
* `schemaToShape`, NOT the `schemaToObject`-wrapped parameters — a bare-value sample checks a
|
|
3070
|
+
* bare-value candidate) and returns `{ parameters, checks }`, one check per candidate at the same
|
|
3071
|
+
* index. Every entry has a UNIFORM shape — `{ index, valid, coercible }`, with `faults` added ONLY
|
|
3072
|
+
* when `valid` is `false`: `valid` is the STRICT guard verdict (`checker.is(candidate)`), the
|
|
3073
|
+
* OPPOSITE of {@link createEndpointTool}'s enforcement, which coerces (`7` becomes `'7'` for a
|
|
3074
|
+
* string slot) — here a conformance report answers "does this value conform AS-IS": `7` against a
|
|
3075
|
+
* string slot is `valid: false`, full stop. `coercible` answers a SEPARATE question — "would the
|
|
3076
|
+
* NORMALIZING parse accept this value", i.e. would {@link createEndpointTool}'s default enforcement
|
|
3077
|
+
* admit it (`checker.parse(candidate) !== undefined`) — computed for every candidate regardless of
|
|
3078
|
+
* `valid`; by the house parse/guard round-trip guarantee (AGENTS §14), a `valid: true` entry is
|
|
3079
|
+
* ALWAYS also `coercible: true`. `@orkestrel/contract` 0.0.7's `explain` mirrors the normalizing
|
|
3080
|
+
* `parse`'s leniency, not `is`'s strictness — so a strictly-invalid but coercible candidate (`7`
|
|
3081
|
+
* against a string slot) yields `{ valid: false, coercible: true, faults: [] }`: EMPTY faults, since
|
|
3082
|
+
* the mismatch the normalizing parse would silently fix is not one `explain` reports. `faults`
|
|
3083
|
+
* therefore only ever populates for a NON-coercible mismatch — a wrong type the parse can't coerce
|
|
3084
|
+
* (a boolean in a string slot), a missing required key, or an out-of-enum value — where
|
|
3085
|
+
* `coercible: false`. `checker.is` / `.parse` / `.explain` are all total over JSON-safe input — a
|
|
3086
|
+
* JSON-safe hostile candidate (a `__proto__`-carrying object, deeply nested data) reaches all three
|
|
3087
|
+
* and yields a bounded, non-throwing per-candidate verdict; a NON-JSON-safe candidate (e.g. a
|
|
3088
|
+
* throwing-getter `Proxy`) never reaches the checker at all — it fails the OUTER `args` parse
|
|
3089
|
+
* against {@link import('./shapers.js').inferToolShape} and rejects the WHOLE call with the same
|
|
3090
|
+
* `TOOL` {@link import('./errors.js').AgentToolError} a malformed `samples`/`format`/`enum` throws,
|
|
3091
|
+
* with no per-candidate verdict produced.
|
|
3092
|
+
*
|
|
3093
|
+
* @param options - Advertised `name` / `description` overrides (see
|
|
3094
|
+
* {@link import('./types.js').InferToolOptions})
|
|
3095
|
+
* @returns A `ToolInterface` (named {@link import('./constants.js').INFER_TOOL_NAME} by default)
|
|
3096
|
+
*
|
|
3097
|
+
* @example
|
|
3098
|
+
* ```ts
|
|
3099
|
+
* import { createInferTool } from '@src/core'
|
|
3100
|
+
* import { createToolManager } from '@orkestrel/agent'
|
|
3101
|
+
*
|
|
3102
|
+
* const tool = createInferTool()
|
|
3103
|
+
* const tools = createToolManager()
|
|
3104
|
+
* tools.add(tool)
|
|
3105
|
+
*
|
|
3106
|
+
* const result = await tools.execute({
|
|
3107
|
+
* id: 'call-1',
|
|
3108
|
+
* name: 'infer',
|
|
3109
|
+
* arguments: { samples: [{ id: 1, name: 'Ada' }, { id: 2, name: 'Bob' }] },
|
|
3110
|
+
* })
|
|
3111
|
+
* // result.value -> { type: 'object', properties: { id: {...}, name: {...} }, ... }
|
|
3112
|
+
*
|
|
3113
|
+
* // with candidates, the result is wrapped with per-candidate verdicts
|
|
3114
|
+
* const checked = await tools.execute({
|
|
3115
|
+
* id: 'call-2',
|
|
3116
|
+
* name: 'infer',
|
|
3117
|
+
* arguments: {
|
|
3118
|
+
* samples: [{ id: 1, name: 'Ada' }],
|
|
3119
|
+
* candidates: [{ id: 2, name: 'Bob' }, { id: 'x', name: 'Cy' }],
|
|
3120
|
+
* },
|
|
3121
|
+
* })
|
|
3122
|
+
* // checked.value -> { parameters: {...}, checks: [
|
|
3123
|
+
* // { index: 0, valid: true, coercible: true },
|
|
3124
|
+
* // { index: 1, valid: false, coercible: false, faults: [...] },
|
|
3125
|
+
* // ] }
|
|
3126
|
+
* ```
|
|
3127
|
+
*/
|
|
3128
|
+
function createInferTool(options) {
|
|
3129
|
+
const contract = createContract(inferToolShape);
|
|
3130
|
+
const parameters = schemaToParameters(contract.schema);
|
|
3131
|
+
return createTool({
|
|
3132
|
+
name: options?.name ?? "infer",
|
|
3133
|
+
description: options?.description ?? INFER_TOOL_DESCRIPTION,
|
|
3134
|
+
summary: INFER_TOOL_SUMMARY,
|
|
3135
|
+
...parameters === void 0 ? {} : { parameters },
|
|
3136
|
+
async execute(args) {
|
|
3137
|
+
const parsed = contract.parse(args);
|
|
3138
|
+
if (parsed === void 0) throw new AgentToolError("TOOL", "malformed infer arguments", { args });
|
|
3139
|
+
const schema = samplesToSchema(parsed.samples, {
|
|
3140
|
+
format: parsed.format ?? false,
|
|
3141
|
+
enum: parsed.enum ?? false
|
|
3142
|
+
});
|
|
3143
|
+
const result = schemaToParameters(schemaToObject(schema));
|
|
3144
|
+
if (result === void 0) throw new AgentToolError("TOOL", "could not infer a schema", { args });
|
|
3145
|
+
if (parsed.candidates === void 0) return result;
|
|
3146
|
+
const checker = createContract(schemaToShape(schema));
|
|
3147
|
+
return {
|
|
3148
|
+
parameters: result,
|
|
3149
|
+
checks: parsed.candidates.map((candidate, index) => {
|
|
3150
|
+
const valid = checker.is(candidate);
|
|
3151
|
+
const coercible = checker.parse(candidate) !== void 0;
|
|
3152
|
+
return valid ? {
|
|
3153
|
+
index,
|
|
3154
|
+
valid,
|
|
3155
|
+
coercible
|
|
3156
|
+
} : {
|
|
3157
|
+
index,
|
|
3158
|
+
valid,
|
|
3159
|
+
coercible,
|
|
3160
|
+
faults: checker.explain(candidate)
|
|
3161
|
+
};
|
|
3162
|
+
})
|
|
3163
|
+
};
|
|
3164
|
+
}
|
|
3165
|
+
});
|
|
3166
|
+
}
|
|
3167
|
+
/**
|
|
3168
|
+
* Wrap one CONCRETE endpoint ({@link import('./types.js').EndpointDefinition}) as an LLM-callable
|
|
3169
|
+
* `ToolInterface` — the endpoint half of the "existing API/DB → MCP tool" bridge (the other half,
|
|
3170
|
+
* {@link createInferTool}, is a standalone inference utility).
|
|
3171
|
+
*
|
|
3172
|
+
* @remarks
|
|
3173
|
+
* `parameters` is inferred ONCE at construction from `definition.samples` via
|
|
3174
|
+
* `@orkestrel/contract`'s `samplesToSchema` (tuned by {@link import('./types.js').EndpointToolOptions}'s
|
|
3175
|
+
* `format` / `enum`), wrapping a non-object root as `{ value: <schema> }` via `schemaToObject` —
|
|
3176
|
+
* the SAME object-rooted schema is both the ADVERTISED `parameters` and, by default
|
|
3177
|
+
* ({@link import('./types.js').EndpointToolOptions.validate} `true`), the ENFORCED contract:
|
|
3178
|
+
* `@orkestrel/contract` 0.0.7's `schemaToShape` compiles it ONCE (via `createContract`) into a
|
|
3179
|
+
* `ContractInterface` whose `.parse` runs on every call's `args` before `definition.invoke` — a
|
|
3180
|
+
* NORMALIZING parse, not a strict type check: a scalar is COERCED to its inferred type where the
|
|
3181
|
+
* house parsers coerce (a number to/from a numeric string, a boolean from `'1'`/`'0'`/`'true'`/
|
|
3182
|
+
* `'false'`/`1`/`0`), so `definition.invoke` receives the COERCED value (e.g. `7` sent for a
|
|
3183
|
+
* string slot arrives as `'7'`), not the raw call value. A call whose `args` fails to parse into
|
|
3184
|
+
* a record — a required key missing, or a value not coercible to its slot's type — THROWS a
|
|
3185
|
+
* typed `TOOL` {@link import('./errors.js').AgentToolError} carrying the compiled contract's
|
|
3186
|
+
* structured `explain` faults, and `definition.invoke` is never called. `format` annotations are
|
|
3187
|
+
* NEVER asserted, and a key outside the closed inferred schema is SILENTLY DROPPED rather than
|
|
3188
|
+
* rejected (see {@link import('./types.js').EndpointToolOptions.validate}). With
|
|
3189
|
+
* `validate: false`, `execute` PASSES THROUGH the model-supplied `args` to `definition.invoke`
|
|
3190
|
+
* WITHOUT re-validation — the pre-0.0.7 behavior, preserved as an explicit opt-out. Either way,
|
|
3191
|
+
* `invoke`'s return flows back as the tool call's plain result; a throw PROPAGATES uncaught,
|
|
3192
|
+
* isolated by the `ToolManagerInterface` (`@orkestrel/agent`) into the canonical error envelope
|
|
3193
|
+
* (AGENTS §14) — never caught or re-wrapped here.
|
|
3194
|
+
*
|
|
3195
|
+
* @param definition - The endpoint's identity, non-empty samples, and local handler (see
|
|
3196
|
+
* {@link import('./types.js').EndpointDefinition})
|
|
3197
|
+
* @param options - Construction-time inference tuning + the validate opt-out (see
|
|
3198
|
+
* {@link import('./types.js').EndpointToolOptions})
|
|
3199
|
+
* @returns A `ToolInterface` named `definition.name`
|
|
3200
|
+
*
|
|
3201
|
+
* @example
|
|
3202
|
+
* ```ts
|
|
3203
|
+
* import { createEndpointTool } from '@src/core'
|
|
3204
|
+
* import { createToolManager } from '@orkestrel/agent'
|
|
3205
|
+
*
|
|
3206
|
+
* const tool = createEndpointTool({
|
|
3207
|
+
* name: 'lookupUser',
|
|
3208
|
+
* description: 'Look up a user by id.',
|
|
3209
|
+
* samples: [{ id: '1', name: 'Ada' }, { id: '2', name: 'Bob' }],
|
|
3210
|
+
* invoke: (args) => ({ id: args.id, name: 'Ada' }),
|
|
3211
|
+
* })
|
|
3212
|
+
* const tools = createToolManager()
|
|
3213
|
+
* tools.add(tool)
|
|
3214
|
+
*
|
|
3215
|
+
* // conforming args (all required keys present) parse and reach `invoke`
|
|
3216
|
+
* const result = await tools.execute({
|
|
3217
|
+
* id: 'call-1',
|
|
3218
|
+
* name: 'lookupUser',
|
|
3219
|
+
* arguments: { id: '1', name: 'Ada' },
|
|
3220
|
+
* })
|
|
3221
|
+
* // result.value -> { id: '1', name: 'Ada' }
|
|
3222
|
+
*
|
|
3223
|
+
* // a nonconforming call (id is not coercible to the required string) is rejected before
|
|
3224
|
+
* // `invoke` runs
|
|
3225
|
+
* const rejected = await tools.execute({
|
|
3226
|
+
* id: 'call-2',
|
|
3227
|
+
* name: 'lookupUser',
|
|
3228
|
+
* arguments: { id: true, name: 'Ada' },
|
|
3229
|
+
* })
|
|
3230
|
+
* // rejected.error -> the TOOL AgentToolError message
|
|
3231
|
+
* ```
|
|
3232
|
+
*/
|
|
3233
|
+
function createEndpointTool(definition, options) {
|
|
3234
|
+
if (definition.samples.length === 0) throw new AgentToolError("TOOL", "endpoint requires at least one sample", { name: definition.name });
|
|
3235
|
+
const objectSchema = schemaToObject(samplesToSchema(definition.samples, {
|
|
3236
|
+
format: options?.format ?? false,
|
|
3237
|
+
enum: options?.enum ?? false
|
|
3238
|
+
}));
|
|
3239
|
+
const parameters = schemaToParameters(objectSchema);
|
|
3240
|
+
if (!(options?.validate ?? true)) return createTool({
|
|
3241
|
+
name: definition.name,
|
|
3242
|
+
description: definition.description,
|
|
3243
|
+
...parameters === void 0 ? {} : { parameters },
|
|
3244
|
+
execute(args) {
|
|
3245
|
+
return definition.invoke(args);
|
|
3246
|
+
}
|
|
3247
|
+
});
|
|
3248
|
+
const contract = createContract(schemaToShape(objectSchema));
|
|
3249
|
+
return createTool({
|
|
3250
|
+
name: definition.name,
|
|
3251
|
+
description: definition.description,
|
|
3252
|
+
...parameters === void 0 ? {} : { parameters },
|
|
3253
|
+
execute(args) {
|
|
3254
|
+
const parsed = contract.parse(args);
|
|
3255
|
+
if (parsed === void 0 || !isRecord(parsed)) throw new AgentToolError("TOOL", "malformed endpoint call arguments", {
|
|
3256
|
+
name: definition.name,
|
|
3257
|
+
faults: contract.explain(args)
|
|
3258
|
+
});
|
|
3259
|
+
return definition.invoke(parsed);
|
|
3260
|
+
}
|
|
3261
|
+
});
|
|
3262
|
+
}
|
|
2822
3263
|
//#endregion
|
|
2823
|
-
export { AGENT_TOOL_DEPTH, AGENT_TOOL_DESCRIPTION, AGENT_TOOL_NAME, AGENT_TOOL_SUMMARY, ANSWER_TOOL_DESCRIPTION, ANSWER_TOOL_NAME, ANSWER_TOOL_SUMMARY, AgentToolError, DATABASE_TOOL_DESCRIPTION, DATABASE_TOOL_LIMIT, DATABASE_TOOL_MUTATIONS, DATABASE_TOOL_NAME, DATABASE_TOOL_SUMMARY, DESCRIBE_TOOL_DESCRIPTION, DESCRIBE_TOOL_NAME, DESCRIBE_TOOL_SUMMARY, DatabaseDefinitionStore, MAX_WORKFLOW_DEPTH, MemoryDefinitionStore, PROMPT_TOOL_DESCRIPTION, PROMPT_TOOL_NAME, PROMPT_TOOL_SUMMARY, RELATION_TOOL_DEPTH, RELATION_TOOL_DESCRIPTION, RELATION_TOOL_LIMIT, RELATION_TOOL_NAME, RELATION_TOOL_SUMMARY, WORKFLOW_TOOL_DESCRIPTION, WORKFLOW_TOOL_FLAT_EXAMPLE, WORKFLOW_TOOL_NAME, WORKFLOW_TOOL_NESTED_EXAMPLE, WORKFLOW_TOOL_SUMMARY, WORKSPACE_TOOL_DESCRIPTION, WORKSPACE_TOOL_EXAMPLE, WORKSPACE_TOOL_NAME, WORKSPACE_TOOL_SUMMARY, agentTag, agentToolShape, answerToolShape, clampCriteria, coerceAnswer, columnKindShape, columnSchema, columnShape, columnSpecShape, completeDraft, completePhaseDraft, completeTaskDraft, conditionShape, createAgentFunction, createAgentTool, createAnswerTool, createDatabaseDefinitionStore, createDatabaseTool, createDescribeTool, createMemoryDefinitionStore, createPromptTool, createRelationTool, createToolFunction, createWorkflowDraftContract, createWorkflowTool, createWorkspaceTool, criteriaOf, criteriaShape, databaseToolCode, databaseToolShape, describeToolShape, expandInclude, expandSteps, expandTables, includeShape, isAgentToolError, isColumnKind, isColumnSpec, isDatabaseDefinition, keyShape, kindShape, managerShape, orderShape, phaseDraftShape, promptToolShape, relationKeyShape, relationManagerOf, relationModelOf, relationToolCode, relationToolShape, rowShape, rowsShape, singleKeyShape, stepShape, tableSchema, tableSpecShape, taskDraftShape, terminalToolCode, workflowDraftShape, workflowStepsShape, workflowTag, workflowToolSummary, workspaceToolShape };
|
|
3264
|
+
export { AGENT_TOOL_DEPTH, AGENT_TOOL_DESCRIPTION, AGENT_TOOL_NAME, AGENT_TOOL_SUMMARY, ANSWER_TOOL_DESCRIPTION, ANSWER_TOOL_NAME, ANSWER_TOOL_SUMMARY, AgentToolError, DATABASE_TOOL_DESCRIPTION, DATABASE_TOOL_LIMIT, DATABASE_TOOL_MUTATIONS, DATABASE_TOOL_NAME, DATABASE_TOOL_SUMMARY, DESCRIBE_TOOL_DESCRIPTION, DESCRIBE_TOOL_NAME, DESCRIBE_TOOL_SUMMARY, DatabaseDefinitionStore, DatabaseResolver, INFER_TOOL_DESCRIPTION, INFER_TOOL_NAME, INFER_TOOL_SUMMARY, MAX_WORKFLOW_DEPTH, MemoryDefinitionStore, PROMPT_TOOL_DESCRIPTION, PROMPT_TOOL_NAME, PROMPT_TOOL_SUMMARY, RELATION_TOOL_DEPTH, RELATION_TOOL_DESCRIPTION, RELATION_TOOL_LIMIT, RELATION_TOOL_NAME, RELATION_TOOL_SUMMARY, WORKFLOW_TOOL_DESCRIPTION, WORKFLOW_TOOL_FLAT_EXAMPLE, WORKFLOW_TOOL_NAME, WORKFLOW_TOOL_NESTED_EXAMPLE, WORKFLOW_TOOL_SUMMARY, WORKSPACE_TOOL_DESCRIPTION, WORKSPACE_TOOL_EXAMPLE, WORKSPACE_TOOL_NAME, WORKSPACE_TOOL_SUMMARY, agentTag, agentToolShape, answerToolShape, clampCriteria, coerceAnswer, columnKindShape, columnSchema, columnShape, columnSpecShape, completeDraft, completePhaseDraft, completeTaskDraft, conditionShape, createAgentFunction, createAgentTool, createAnswerTool, createDatabaseDefinitionStore, createDatabaseTool, createDescribeTool, createEndpointTool, createInferTool, createMemoryDefinitionStore, createPromptTool, createRelationTool, createToolFunction, createWorkflowDraftContract, createWorkflowTool, createWorkspaceTool, criteriaOf, criteriaShape, databaseToolCode, databaseToolShape, describeToolShape, expandInclude, expandSteps, expandTables, includeShape, inferToolShape, isAgentToolError, isColumnKind, isColumnSpec, isDatabaseDefinition, keyShape, kindShape, managerShape, orderShape, phaseDraftShape, promptToolShape, relationKeyShape, relationManagerOf, relationModelOf, relationToolCode, relationToolShape, rowShape, rowsShape, singleKeyShape, stepShape, tableSchema, tableSpecShape, taskDraftShape, terminalToolCode, workflowDraftShape, workflowStepsShape, workflowTag, workflowToolSummary, workspaceToolShape };
|
|
2824
3265
|
|
|
2825
3266
|
//# sourceMappingURL=index.js.map
|