@orkestrel/tool 0.0.13 → 0.0.15
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 +9 -11
- package/dist/src/core/index.cjs +171 -49
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +211 -47
- package/dist/src/core/index.d.ts +211 -47
- package/dist/src/core/index.js +171 -51
- package/dist/src/core/index.js.map +1 -1
- package/package.json +14 -14
package/dist/src/core/index.js
CHANGED
|
@@ -1,43 +1,53 @@
|
|
|
1
|
-
import { attempt, holds, isArray, isRecord, isString } from "@orkestrel/contract";
|
|
2
|
-
|
|
1
|
+
import { attempt, createContract, holds, isArray, isInstance, isRecord, isString, schemaToParameters } from "@orkestrel/contract";
|
|
2
|
+
import { Emitter } from "@orkestrel/emitter";
|
|
3
|
+
//#region src/core/errors.ts
|
|
3
4
|
/**
|
|
4
|
-
*
|
|
5
|
+
* Reports a schema conflict or argument validation failure with a machine-readable code.
|
|
5
6
|
*
|
|
6
|
-
* @
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* authored a schema. An authored `summary` is advertised in place of the full
|
|
10
|
-
* `description`, which stays on the tool for direct lookup. The parameter schema is
|
|
11
|
-
* copied by reference and never cloned, so the definition is never a live handle on
|
|
12
|
-
* the tool's handler.
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { ToolError } from '@orkestrel/tool'
|
|
13
10
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
11
|
+
* const error = new ToolError('SCHEMA', 'Choose contract or parameters')
|
|
12
|
+
* error.code // 'SCHEMA'
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
var ToolError = class extends Error {
|
|
16
|
+
name = "ToolError";
|
|
17
|
+
code;
|
|
18
|
+
context;
|
|
19
|
+
constructor(code, message, context) {
|
|
20
|
+
super(message);
|
|
21
|
+
this.code = code;
|
|
22
|
+
if (context !== void 0) this.context = context;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Checks whether a value is a tool error, containing hostile prototype access.
|
|
27
|
+
*
|
|
28
|
+
* @param value - The value to test
|
|
29
|
+
* @returns True if the value is an instance of the tool error class; false otherwise
|
|
16
30
|
*
|
|
17
31
|
* @example
|
|
18
32
|
* ```ts
|
|
19
|
-
* import {
|
|
33
|
+
* import { ToolError, isToolError } from '@orkestrel/tool'
|
|
20
34
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
35
|
+
* isToolError(new ToolError('ARGUMENTS', 'Invalid amount')) // true
|
|
36
|
+
* isToolError(new Error('Unrelated')) // false
|
|
23
37
|
* ```
|
|
24
38
|
*/
|
|
25
|
-
function
|
|
26
|
-
|
|
27
|
-
const description = tool.summary ?? tool.description;
|
|
28
|
-
if (description !== void 0) definition.description = description;
|
|
29
|
-
if (tool.parameters !== void 0) definition.parameters = tool.parameters;
|
|
30
|
-
return definition;
|
|
39
|
+
function isToolError(value) {
|
|
40
|
+
return isInstance(value, ToolError);
|
|
31
41
|
}
|
|
32
42
|
//#endregion
|
|
33
43
|
//#region src/core/validators.ts
|
|
34
44
|
/**
|
|
35
|
-
* Determines whether an unknown value is structurally a {@link ToolCall}
|
|
45
|
+
* Determines whether an unknown value is structurally a {@link ToolCall}, staying total
|
|
46
|
+
* for malformed and adversarial input.
|
|
36
47
|
*
|
|
37
48
|
* @remarks
|
|
38
|
-
*
|
|
39
|
-
* plain-record `arguments` field.
|
|
40
|
-
* read or verified. Adversarial values return `false`.
|
|
49
|
+
* The accepted shape is a plain record with string `id` and `name` fields and a
|
|
50
|
+
* plain-record `arguments` field. Extra fields are not read or verified.
|
|
41
51
|
*
|
|
42
52
|
* @param value - The value to test
|
|
43
53
|
* @returns True if the value has the complete tool-call shape; false otherwise
|
|
@@ -54,12 +64,48 @@ function isToolCall(value) {
|
|
|
54
64
|
return holds(() => isRecord(value) && isString(value.id) && isString(value.name) && isRecord(value.arguments));
|
|
55
65
|
}
|
|
56
66
|
//#endregion
|
|
67
|
+
//#region src/core/helpers.ts
|
|
68
|
+
/**
|
|
69
|
+
* Projects a tool onto the plain definition advertised to a caller, advertising an
|
|
70
|
+
* authored `summary` in place of the full description and carrying `parameters` and
|
|
71
|
+
* `annotations` by reference.
|
|
72
|
+
*
|
|
73
|
+
* @remarks
|
|
74
|
+
* The projection carries `name` and present `title`, `description`, `parameters`, and
|
|
75
|
+
* `annotations` fields in that order. The full `description` stays on the tool for
|
|
76
|
+
* direct lookup, and the definition is never a live handle on the tool's handler.
|
|
77
|
+
*
|
|
78
|
+
* @param tool - The tool to project
|
|
79
|
+
* @returns A fresh definition carrying only the fields the tool authored
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* ```ts
|
|
83
|
+
* import { Tool, toolToDefinition } from '@orkestrel/tool'
|
|
84
|
+
*
|
|
85
|
+
* const echo = new Tool({ name: 'echo', summary: 'Echo a value.', execute: (args) => args.value })
|
|
86
|
+
* toolToDefinition(echo) // { name: 'echo', description: 'Echo a value.' }
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
function toolToDefinition(tool) {
|
|
90
|
+
const description = tool.summary ?? tool.description;
|
|
91
|
+
return {
|
|
92
|
+
name: tool.name,
|
|
93
|
+
...tool.title === void 0 ? {} : { title: tool.title },
|
|
94
|
+
...description === void 0 ? {} : { description },
|
|
95
|
+
...tool.parameters === void 0 ? {} : { parameters: tool.parameters },
|
|
96
|
+
...tool.annotations === void 0 ? {} : { annotations: tool.annotations }
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
//#endregion
|
|
57
100
|
//#region src/core/tools/Tool.ts
|
|
58
101
|
/**
|
|
59
102
|
* Binds an executable tool definition to a handler.
|
|
60
103
|
*
|
|
61
104
|
* @remarks
|
|
62
|
-
*
|
|
105
|
+
* Advertised fields and execution context are forwarded by reference.
|
|
106
|
+
* A contract derives parameters at construction and refuses parse faults before the
|
|
107
|
+
* handler runs, then forwards the parsed arguments; without a contract, arguments
|
|
108
|
+
* retain their identity. Supplying a contract and parameters throws a schema conflict.
|
|
63
109
|
* Caller context is consumer-asserted and is not verified. Handler failures are not
|
|
64
110
|
* caught here; {@link ToolManager} owns per-call error isolation.
|
|
65
111
|
*
|
|
@@ -79,21 +125,47 @@ function isToolCall(value) {
|
|
|
79
125
|
* ```
|
|
80
126
|
*/
|
|
81
127
|
var Tool = class {
|
|
128
|
+
#contract;
|
|
129
|
+
#execute;
|
|
82
130
|
name;
|
|
131
|
+
title;
|
|
83
132
|
description;
|
|
84
133
|
summary;
|
|
85
134
|
parameters;
|
|
86
|
-
|
|
135
|
+
annotations;
|
|
87
136
|
constructor(options) {
|
|
137
|
+
if (options.contract !== void 0 && options.parameters !== void 0) throw new ToolError("SCHEMA", "Choose contract or parameters, not both");
|
|
88
138
|
this.name = options.name;
|
|
139
|
+
if (options.title !== void 0) this.title = options.title;
|
|
89
140
|
if (options.description !== void 0) this.description = options.description;
|
|
90
141
|
if (options.summary !== void 0) this.summary = options.summary;
|
|
91
|
-
if (options.
|
|
142
|
+
if (options.annotations !== void 0) this.annotations = options.annotations;
|
|
143
|
+
if (options.contract !== void 0) {
|
|
144
|
+
this.#contract = createContract(options.contract);
|
|
145
|
+
const parameters = schemaToParameters(this.#contract.schema);
|
|
146
|
+
if (parameters !== void 0) this.parameters = parameters;
|
|
147
|
+
} else if (options.parameters !== void 0) this.parameters = options.parameters;
|
|
92
148
|
this.#execute = options.execute;
|
|
93
149
|
}
|
|
94
|
-
execute(args,
|
|
95
|
-
|
|
96
|
-
|
|
150
|
+
execute(args, context) {
|
|
151
|
+
const faults = this.#contract?.explain(args) ?? [];
|
|
152
|
+
const fault = faults[0];
|
|
153
|
+
if (fault !== void 0) {
|
|
154
|
+
let message = `${isString(fault.path) ? fault.path : fault.path.join(".")}: ${fault.reason}`;
|
|
155
|
+
if ("expected" in fault) message += `; expected ${fault.expected}`;
|
|
156
|
+
if ("received" in fault) message += `; received ${fault.received}`;
|
|
157
|
+
if ("constraint" in fault) message += `; constraint ${fault.constraint}`;
|
|
158
|
+
if ("limit" in fault && fault.limit !== void 0) message += `; limit ${fault.limit}`;
|
|
159
|
+
if ("variants" in fault) message += `; variants ${fault.variants}`;
|
|
160
|
+
if ("matched" in fault) message += `; matched ${fault.matched}`;
|
|
161
|
+
throw new ToolError("ARGUMENTS", message, { faults });
|
|
162
|
+
}
|
|
163
|
+
if (this.#contract !== void 0) {
|
|
164
|
+
const parsed = this.#contract.parse(args);
|
|
165
|
+
if (!isRecord(parsed)) throw new ToolError("ARGUMENTS", "Arguments did not parse");
|
|
166
|
+
return this.#execute(parsed, context);
|
|
167
|
+
}
|
|
168
|
+
return this.#execute(args, context);
|
|
97
169
|
}
|
|
98
170
|
};
|
|
99
171
|
//#endregion
|
|
@@ -107,7 +179,13 @@ var Tool = class {
|
|
|
107
179
|
* Unknown names and handler throws resolve to error results; a call whose `id` or `name`
|
|
108
180
|
* accessor throws when read makes its call, and the batch holding it, reject. Batch
|
|
109
181
|
* execution preserves input order and isolates each call whose members are plain
|
|
110
|
-
* values.
|
|
182
|
+
* values. Execution context is shared across a batch and forwarded unchanged. An
|
|
183
|
+
* omitted context receives a non-aborted signal. A signal aborted before handler
|
|
184
|
+
* entry produces an error result; later cancellation is the handler's responsibility.
|
|
185
|
+
* Registry changes publish synchronously. Replacements publish `remove`, then `add`
|
|
186
|
+
* if the map still holds that exact replacement after the removal listeners return.
|
|
187
|
+
* Destruction clears the tools before releasing listeners. A destroyed registry
|
|
188
|
+
* publishes nothing, even when later additions update its tool map.
|
|
111
189
|
*
|
|
112
190
|
* @example
|
|
113
191
|
* ```ts
|
|
@@ -124,15 +202,25 @@ var Tool = class {
|
|
|
124
202
|
*/
|
|
125
203
|
var ToolManager = class {
|
|
126
204
|
#tools = /* @__PURE__ */ new Map();
|
|
205
|
+
#emitter;
|
|
206
|
+
constructor(options) {
|
|
207
|
+
this.#emitter = new Emitter(options);
|
|
208
|
+
}
|
|
127
209
|
get count() {
|
|
128
210
|
return this.#tools.size;
|
|
129
211
|
}
|
|
212
|
+
get emitter() {
|
|
213
|
+
return this.#emitter;
|
|
214
|
+
}
|
|
130
215
|
add(tools) {
|
|
131
216
|
if (isArray(tools)) {
|
|
132
|
-
for (const tool of tools) this
|
|
217
|
+
for (const tool of tools) this.add(tool);
|
|
133
218
|
return;
|
|
134
219
|
}
|
|
220
|
+
const previous = this.#tools.get(tools.name);
|
|
135
221
|
this.#tools.set(tools.name, tools);
|
|
222
|
+
if (previous !== void 0) this.#emitter.emit("remove", previous);
|
|
223
|
+
if (this.#tools.get(tools.name) === tools) this.#emitter.emit("add", tools);
|
|
136
224
|
}
|
|
137
225
|
tool(name) {
|
|
138
226
|
return this.#tools.get(name);
|
|
@@ -143,22 +231,33 @@ var ToolManager = class {
|
|
|
143
231
|
definitions() {
|
|
144
232
|
return [...this.#tools.values()].map((tool) => toolToDefinition(tool));
|
|
145
233
|
}
|
|
146
|
-
execute(call) {
|
|
147
|
-
if (isArray(call)) return Promise.all(call.map((one) => this.#run(one)));
|
|
148
|
-
return this.#run(call);
|
|
234
|
+
execute(call, context = { signal: new AbortController().signal }) {
|
|
235
|
+
if (isArray(call)) return Promise.all(call.map((one) => this.#run(one, context)));
|
|
236
|
+
return this.#run(call, context);
|
|
149
237
|
}
|
|
150
238
|
remove(names) {
|
|
151
239
|
if (isArray(names)) {
|
|
152
240
|
let removed = true;
|
|
153
|
-
for (const name of names) if (!this
|
|
241
|
+
for (const name of names) if (!this.remove(name)) removed = false;
|
|
154
242
|
return removed;
|
|
155
243
|
}
|
|
156
|
-
|
|
244
|
+
const tool = this.#tools.get(names);
|
|
245
|
+
if (tool === void 0) return false;
|
|
246
|
+
this.#tools.delete(names);
|
|
247
|
+
this.#emitter.emit("remove", tool);
|
|
248
|
+
return true;
|
|
157
249
|
}
|
|
158
250
|
clear() {
|
|
251
|
+
const tools = this.tools();
|
|
252
|
+
this.#tools.clear();
|
|
253
|
+
this.#emitter.emit("clear", tools);
|
|
254
|
+
}
|
|
255
|
+
destroy() {
|
|
256
|
+
this.clear();
|
|
257
|
+
this.#emitter.destroy();
|
|
159
258
|
this.#tools.clear();
|
|
160
259
|
}
|
|
161
|
-
async #run(call) {
|
|
260
|
+
async #run(call, context) {
|
|
162
261
|
const tool = this.#tools.get(call.name);
|
|
163
262
|
if (tool === void 0) return {
|
|
164
263
|
id: call.id,
|
|
@@ -167,8 +266,16 @@ var ToolManager = class {
|
|
|
167
266
|
error: `tool not found: ${call.name}`
|
|
168
267
|
};
|
|
169
268
|
try {
|
|
170
|
-
|
|
171
|
-
|
|
269
|
+
if (context.signal.aborted) {
|
|
270
|
+
const reason = context.signal.reason;
|
|
271
|
+
return {
|
|
272
|
+
id: call.id,
|
|
273
|
+
name: call.name,
|
|
274
|
+
success: false,
|
|
275
|
+
error: reason === void 0 ? "aborted" : String(reason)
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
const value = await tool.execute(call.arguments, context);
|
|
172
279
|
return {
|
|
173
280
|
id: call.id,
|
|
174
281
|
name: call.name,
|
|
@@ -189,19 +296,30 @@ var ToolManager = class {
|
|
|
189
296
|
//#endregion
|
|
190
297
|
//#region src/core/factories.ts
|
|
191
298
|
/**
|
|
192
|
-
* Creates an executable tool
|
|
299
|
+
* Creates an executable tool bound to the supplied handler, returned as a
|
|
300
|
+
* `ToolInterface` so a call site holds the published contract rather than the `Tool`
|
|
301
|
+
* class.
|
|
193
302
|
*
|
|
194
303
|
* @param options - The advertised definition and execution handler
|
|
195
304
|
* @returns A tool bound to the supplied handler
|
|
196
305
|
*
|
|
197
|
-
* @example
|
|
306
|
+
* @example Anatomy of a tool
|
|
198
307
|
* ```ts
|
|
199
308
|
* import { createTool } from '@orkestrel/tool'
|
|
200
309
|
*
|
|
201
310
|
* const add = createTool({
|
|
202
311
|
* name: 'add',
|
|
203
|
-
* description: 'Add two
|
|
204
|
-
*
|
|
312
|
+
* description: 'Add two numeric values and return their sum. Both operands are required.',
|
|
313
|
+
* summary: 'Add two numbers.',
|
|
314
|
+
* parameters: {
|
|
315
|
+
* type: 'object',
|
|
316
|
+
* properties: {
|
|
317
|
+
* left: { type: 'number' },
|
|
318
|
+
* right: { type: 'number' },
|
|
319
|
+
* },
|
|
320
|
+
* required: ['left', 'right'],
|
|
321
|
+
* },
|
|
322
|
+
* execute: (args) => Number(args.left) + Number(args.right),
|
|
205
323
|
* })
|
|
206
324
|
* ```
|
|
207
325
|
*/
|
|
@@ -209,10 +327,12 @@ function createTool(options) {
|
|
|
209
327
|
return new Tool(options);
|
|
210
328
|
}
|
|
211
329
|
/**
|
|
212
|
-
* Creates an empty
|
|
330
|
+
* Creates an empty registry that advertises definitions and executes calls with
|
|
331
|
+
* per-call error isolation, returned as a `ToolManagerInterface` so a caller holds the
|
|
332
|
+
* published contract rather than the `ToolManager` class.
|
|
213
333
|
*
|
|
214
|
-
* @
|
|
215
|
-
*
|
|
334
|
+
* @param options - The initial registry listeners and listener-error handler
|
|
335
|
+
* @returns A registry bound to no tools
|
|
216
336
|
*
|
|
217
337
|
* @example
|
|
218
338
|
* ```ts
|
|
@@ -227,10 +347,10 @@ function createTool(options) {
|
|
|
227
347
|
* })
|
|
228
348
|
* ```
|
|
229
349
|
*/
|
|
230
|
-
function createToolManager() {
|
|
231
|
-
return new ToolManager();
|
|
350
|
+
function createToolManager(options) {
|
|
351
|
+
return new ToolManager(options);
|
|
232
352
|
}
|
|
233
353
|
//#endregion
|
|
234
|
-
export { Tool, ToolManager, createTool, createToolManager, isToolCall, toolToDefinition };
|
|
354
|
+
export { Tool, ToolError, ToolManager, createTool, createToolManager, isToolCall, isToolError, toolToDefinition };
|
|
235
355
|
|
|
236
356
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["#execute","#tools","#run"],"sources":["../../../src/core/helpers.ts","../../../src/core/validators.ts","../../../src/core/tools/Tool.ts","../../../src/core/tools/ToolManager.ts","../../../src/core/factories.ts"],"sourcesContent":["import type { ToolDefinition, ToolInterface } from './types.js'\n\n/**\n * Projects a tool onto the plain definition advertised to a caller.\n *\n * @remarks\n * The projection is a fresh object carrying `name`, then `description` only when the\n * tool authored a summary or a description, then `parameters` only when the tool\n * authored a schema. An authored `summary` is advertised in place of the full\n * `description`, which stays on the tool for direct lookup. The parameter schema is\n * copied by reference and never cloned, so the definition is never a live handle on\n * the tool's handler.\n *\n * @param tool - The tool to project\n * @returns A fresh definition carrying only the fields the tool authored\n *\n * @example\n * ```ts\n * import { Tool, toolToDefinition } from '@orkestrel/tool'\n *\n * const echo = new Tool({ name: 'echo', summary: 'Echo a value.', execute: (args) => args.value })\n * toolToDefinition(echo) // { name: 'echo', description: 'Echo a value.' }\n * ```\n */\nexport function toolToDefinition(tool: ToolInterface): ToolDefinition {\n\tconst definition: {\n\t\tname: string\n\t\tdescription?: string\n\t\tparameters?: Readonly<Record<string, unknown>>\n\t} = {\n\t\tname: tool.name,\n\t}\n\tconst description = tool.summary ?? tool.description\n\tif (description !== undefined) definition.description = description\n\tif (tool.parameters !== undefined) definition.parameters = tool.parameters\n\treturn definition\n}\n","import type { ToolCall } from './types.js'\nimport { holds, isRecord, isString } from '@orkestrel/contract'\n\n/**\n * Determines whether an unknown value is structurally a {@link ToolCall}.\n *\n * @remarks\n * This total guard accepts a plain record with string `id` and `name` fields and a\n * plain-record `arguments` field. Optional caller context remains opaque and is not\n * read or verified. Adversarial values return `false`.\n *\n * @param value - The value to test\n * @returns True if the value has the complete tool-call shape; false otherwise\n *\n * @example\n * ```ts\n * import { isToolCall } from '@orkestrel/tool'\n *\n * isToolCall({ id: '1', name: 'search', arguments: { query: 'birds' } }) // true\n * isToolCall({ id: '1', name: 'search', arguments: [] }) // false\n * ```\n */\nexport function isToolCall(value: unknown): value is ToolCall {\n\treturn holds(\n\t\t() =>\n\t\t\tisRecord(value) && isString(value.id) && isString(value.name) && isRecord(value.arguments),\n\t)\n}\n","import type { ToolInterface, ToolOptions } from '../types.js'\n\n/**\n * Binds an executable tool definition to a handler.\n *\n * @remarks\n * Schema fields, arguments, and present caller context are forwarded by reference.\n * Caller context is consumer-asserted and is not verified. Handler failures are not\n * caught here; {@link ToolManager} owns per-call error isolation.\n *\n * @example\n * ```ts\n * import { Tool } from '@orkestrel/tool'\n *\n * const tool = new Tool({\n * \tname: 'add',\n * \tdescription: 'Add two numbers',\n * \tparameters: {\n * \t\ttype: 'object',\n * \t\tproperties: { a: { type: 'number' }, b: { type: 'number' } },\n * \t},\n * \texecute: (args) => Number(args.a) + Number(args.b),\n * })\n * ```\n */\nexport class Tool implements ToolInterface {\n\treadonly name: string\n\treadonly description?: string\n\treadonly summary?: string\n\treadonly parameters?: Readonly<Record<string, unknown>>\n\treadonly #execute: (\n\t\targs: Readonly<Record<string, unknown>>,\n\t\tcaller?: unknown,\n\t) => Promise<unknown> | unknown\n\n\tconstructor(options: ToolOptions) {\n\t\tthis.name = options.name\n\t\tif (options.description !== undefined) this.description = options.description\n\t\tif (options.summary !== undefined) this.summary = options.summary\n\t\tif (options.parameters !== undefined) this.parameters = options.parameters\n\t\tthis.#execute = options.execute\n\t}\n\n\texecute(args: Readonly<Record<string, unknown>>, caller?: unknown): Promise<unknown> | unknown {\n\t\tif (caller === undefined) return this.#execute(args)\n\t\treturn this.#execute(args, caller)\n\t}\n}\n","import type {\n\tToolCall,\n\tToolDefinition,\n\tToolInterface,\n\tToolManagerInterface,\n\tToolResult,\n} from '../types.js'\nimport { attempt, isArray } from '@orkestrel/contract'\nimport { toolToDefinition } from '../helpers.js'\n\n/**\n * Represents an insertion-ordered tool registry with per-call error isolation.\n *\n * @remarks\n * A repeated name overwrites the registered tool without changing its insertion\n * position. Definitions advertise `summary` in place of `description` when present.\n * Unknown names and handler throws resolve to error results; a call whose `id` or `name`\n * accessor throws when read makes its call, and the batch holding it, reject. Batch\n * execution preserves input order and isolates each call whose members are plain\n * values. Optional consumer-asserted caller context is forwarded without verification.\n *\n * @example\n * ```ts\n * import { Tool, ToolManager } from '@orkestrel/tool'\n *\n * const tools = new ToolManager()\n * tools.add(new Tool({ name: 'add', execute: (args) => Number(args.x) + Number(args.y) }))\n * const result = await tools.execute({\n * \tid: '1',\n * \tname: 'add',\n * \targuments: { x: 1, y: 2 },\n * })\n * ```\n */\nexport class ToolManager implements ToolManagerInterface {\n\treadonly #tools = new Map<string, ToolInterface>()\n\n\tget count(): number {\n\t\treturn this.#tools.size\n\t}\n\n\tadd(tool: ToolInterface): void\n\tadd(tools: readonly ToolInterface[]): void\n\tadd(tools: ToolInterface | readonly ToolInterface[]): void {\n\t\tif (isArray(tools)) {\n\t\t\tfor (const tool of tools) this.#tools.set(tool.name, tool)\n\t\t\treturn\n\t\t}\n\t\tthis.#tools.set(tools.name, tools)\n\t}\n\n\ttool(name: string): ToolInterface | undefined {\n\t\treturn this.#tools.get(name)\n\t}\n\n\ttools(): readonly ToolInterface[] {\n\t\treturn [...this.#tools.values()]\n\t}\n\n\tdefinitions(): readonly ToolDefinition[] {\n\t\treturn [...this.#tools.values()].map((tool) => toolToDefinition(tool))\n\t}\n\n\texecute(call: ToolCall): Promise<ToolResult>\n\texecute(calls: readonly ToolCall[]): Promise<readonly ToolResult[]>\n\texecute(call: ToolCall | readonly ToolCall[]): Promise<ToolResult | readonly ToolResult[]> {\n\t\tif (isArray(call)) return Promise.all(call.map((one) => this.#run(one)))\n\t\treturn this.#run(call)\n\t}\n\n\tremove(name: string): boolean\n\tremove(names: readonly string[]): boolean\n\tremove(names: string | readonly string[]): boolean {\n\t\tif (isArray(names)) {\n\t\t\tlet removed = true\n\t\t\tfor (const name of names) {\n\t\t\t\tif (!this.#tools.delete(name)) removed = false\n\t\t\t}\n\t\t\treturn removed\n\t\t}\n\t\treturn this.#tools.delete(names)\n\t}\n\n\tclear(): void {\n\t\tthis.#tools.clear()\n\t}\n\n\tasync #run(call: ToolCall): Promise<ToolResult> {\n\t\tconst tool = this.#tools.get(call.name)\n\t\tif (tool === undefined) {\n\t\t\treturn {\n\t\t\t\tid: call.id,\n\t\t\t\tname: call.name,\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `tool not found: ${call.name}`,\n\t\t\t}\n\t\t}\n\t\ttry {\n\t\t\tconst caller = call.caller\n\t\t\tconst value = await (caller === undefined\n\t\t\t\t? tool.execute(call.arguments)\n\t\t\t\t: tool.execute(call.arguments, caller))\n\t\t\treturn { id: call.id, name: call.name, success: true, value }\n\t\t} catch (error) {\n\t\t\tconst message = attempt(() =>\n\t\t\t\terror instanceof Error ? String(error.message) : String(error),\n\t\t\t)\n\t\t\treturn {\n\t\t\t\tid: call.id,\n\t\t\t\tname: call.name,\n\t\t\t\tsuccess: false,\n\t\t\t\terror: message.success ? message.value : 'Unknown thrown value',\n\t\t\t}\n\t\t}\n\t}\n}\n","import type { ToolInterface, ToolManagerInterface, ToolOptions } from './types.js'\nimport { Tool } from './tools/Tool.js'\nimport { ToolManager } from './tools/ToolManager.js'\n\n/**\n * Creates an executable tool.\n *\n * @param options - The advertised definition and execution handler\n * @returns A tool bound to the supplied handler\n *\n * @example\n * ```ts\n * import { createTool } from '@orkestrel/tool'\n *\n * const add = createTool({\n * \tname: 'add',\n * \tdescription: 'Add two numbers',\n * \texecute: (args) => Number(args.a) + Number(args.b),\n * })\n * ```\n */\nexport function createTool(options: ToolOptions): ToolInterface {\n\treturn new Tool(options)\n}\n\n/**\n * Creates an empty tool registry.\n *\n * @returns A registry that advertises definitions and executes calls with per-call\n * error isolation\n *\n * @example\n * ```ts\n * import { createTool, createToolManager } from '@orkestrel/tool'\n *\n * const tools = createToolManager()\n * tools.add(createTool({ name: 'echo', execute: (args) => args.value }))\n * const result = await tools.execute({\n * \tid: '1',\n * \tname: 'echo',\n * \targuments: { value: 'hello' },\n * })\n * ```\n */\nexport function createToolManager(): ToolManagerInterface {\n\treturn new ToolManager()\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,iBAAiB,MAAqC;CACrE,MAAM,aAIF,EACH,MAAM,KAAK,KACZ;CACA,MAAM,cAAc,KAAK,WAAW,KAAK;CACzC,IAAI,gBAAgB,KAAA,GAAW,WAAW,cAAc;CACxD,IAAI,KAAK,eAAe,KAAA,GAAW,WAAW,aAAa,KAAK;CAChE,OAAO;AACR;;;;;;;;;;;;;;;;;;;;;;ACdA,SAAgB,WAAW,OAAmC;CAC7D,OAAO,YAEL,SAAS,KAAK,KAAK,SAAS,MAAM,EAAE,KAAK,SAAS,MAAM,IAAI,KAAK,SAAS,MAAM,SAAS,CAC3F;AACD;;;;;;;;;;;;;;;;;;;;;;;;;;ACFA,IAAa,OAAb,MAA2C;CAC1C;CACA;CACA;CACA;CACA;CAKA,YAAY,SAAsB;EACjC,KAAK,OAAO,QAAQ;EACpB,IAAI,QAAQ,gBAAgB,KAAA,GAAW,KAAK,cAAc,QAAQ;EAClE,IAAI,QAAQ,YAAY,KAAA,GAAW,KAAK,UAAU,QAAQ;EAC1D,IAAI,QAAQ,eAAe,KAAA,GAAW,KAAK,aAAa,QAAQ;EAChE,KAAKA,WAAW,QAAQ;CACzB;CAEA,QAAQ,MAAyC,QAA8C;EAC9F,IAAI,WAAW,KAAA,GAAW,OAAO,KAAKA,SAAS,IAAI;EACnD,OAAO,KAAKA,SAAS,MAAM,MAAM;CAClC;AACD;;;;;;;;;;;;;;;;;;;;;;;;;;;ACbA,IAAa,cAAb,MAAyD;CACxD,yBAAkB,IAAI,IAA2B;CAEjD,IAAI,QAAgB;EACnB,OAAO,KAAKC,OAAO;CACpB;CAIA,IAAI,OAAuD;EAC1D,IAAI,QAAQ,KAAK,GAAG;GACnB,KAAK,MAAM,QAAQ,OAAO,KAAKA,OAAO,IAAI,KAAK,MAAM,IAAI;GACzD;EACD;EACA,KAAKA,OAAO,IAAI,MAAM,MAAM,KAAK;CAClC;CAEA,KAAK,MAAyC;EAC7C,OAAO,KAAKA,OAAO,IAAI,IAAI;CAC5B;CAEA,QAAkC;EACjC,OAAO,CAAC,GAAG,KAAKA,OAAO,OAAO,CAAC;CAChC;CAEA,cAAyC;EACxC,OAAO,CAAC,GAAG,KAAKA,OAAO,OAAO,CAAC,CAAC,CAAC,KAAK,SAAS,iBAAiB,IAAI,CAAC;CACtE;CAIA,QAAQ,MAAmF;EAC1F,IAAI,QAAQ,IAAI,GAAG,OAAO,QAAQ,IAAI,KAAK,KAAK,QAAQ,KAAKC,KAAK,GAAG,CAAC,CAAC;EACvE,OAAO,KAAKA,KAAK,IAAI;CACtB;CAIA,OAAO,OAA4C;EAClD,IAAI,QAAQ,KAAK,GAAG;GACnB,IAAI,UAAU;GACd,KAAK,MAAM,QAAQ,OAClB,IAAI,CAAC,KAAKD,OAAO,OAAO,IAAI,GAAG,UAAU;GAE1C,OAAO;EACR;EACA,OAAO,KAAKA,OAAO,OAAO,KAAK;CAChC;CAEA,QAAc;EACb,KAAKA,OAAO,MAAM;CACnB;CAEA,MAAMC,KAAK,MAAqC;EAC/C,MAAM,OAAO,KAAKD,OAAO,IAAI,KAAK,IAAI;EACtC,IAAI,SAAS,KAAA,GACZ,OAAO;GACN,IAAI,KAAK;GACT,MAAM,KAAK;GACX,SAAS;GACT,OAAO,mBAAmB,KAAK;EAChC;EAED,IAAI;GACH,MAAM,SAAS,KAAK;GACpB,MAAM,QAAQ,OAAO,WAAW,KAAA,IAC7B,KAAK,QAAQ,KAAK,SAAS,IAC3B,KAAK,QAAQ,KAAK,WAAW,MAAM;GACtC,OAAO;IAAE,IAAI,KAAK;IAAI,MAAM,KAAK;IAAM,SAAS;IAAM;GAAM;EAC7D,SAAS,OAAO;GACf,MAAM,UAAU,cACf,iBAAiB,QAAQ,OAAO,MAAM,OAAO,IAAI,OAAO,KAAK,CAC9D;GACA,OAAO;IACN,IAAI,KAAK;IACT,MAAM,KAAK;IACX,SAAS;IACT,OAAO,QAAQ,UAAU,QAAQ,QAAQ;GAC1C;EACD;CACD;AACD;;;;;;;;;;;;;;;;;;;;AC9FA,SAAgB,WAAW,SAAqC;CAC/D,OAAO,IAAI,KAAK,OAAO;AACxB;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,oBAA0C;CACzD,OAAO,IAAI,YAAY;AACxB"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/core/errors.ts","../../../src/core/validators.ts","../../../src/core/helpers.ts","../../../src/core/tools/Tool.ts","../../../src/core/tools/ToolManager.ts","../../../src/core/factories.ts"],"sourcesContent":["import type { ToolErrorCode, ToolErrorContext } from './types.js'\nimport { isInstance } from '@orkestrel/contract'\n\n/**\n * Reports a schema conflict or argument validation failure with a machine-readable code.\n *\n * @example\n * ```ts\n * import { ToolError } from '@orkestrel/tool'\n *\n * const error = new ToolError('SCHEMA', 'Choose contract or parameters')\n * error.code // 'SCHEMA'\n * ```\n */\nexport class ToolError extends Error {\n\toverride readonly name = 'ToolError' as const\n\treadonly code: ToolErrorCode\n\treadonly context?: ToolErrorContext\n\n\tconstructor(code: ToolErrorCode, message: string, context?: ToolErrorContext) {\n\t\tsuper(message)\n\t\tthis.code = code\n\t\tif (context !== undefined) this.context = context\n\t}\n}\n\n/**\n * Checks whether a value is a tool error, containing hostile prototype access.\n *\n * @param value - The value to test\n * @returns True if the value is an instance of the tool error class; false otherwise\n *\n * @example\n * ```ts\n * import { ToolError, isToolError } from '@orkestrel/tool'\n *\n * isToolError(new ToolError('ARGUMENTS', 'Invalid amount')) // true\n * isToolError(new Error('Unrelated')) // false\n * ```\n */\nexport function isToolError(value: unknown): value is ToolError {\n\treturn isInstance(value, ToolError)\n}\n","import type { ToolCall } from './types.js'\nimport { holds, isRecord, isString } from '@orkestrel/contract'\n\n/**\n * Determines whether an unknown value is structurally a {@link ToolCall}, staying total\n * for malformed and adversarial input.\n *\n * @remarks\n * The accepted shape is a plain record with string `id` and `name` fields and a\n * plain-record `arguments` field. Extra fields are not read or verified.\n *\n * @param value - The value to test\n * @returns True if the value has the complete tool-call shape; false otherwise\n *\n * @example\n * ```ts\n * import { isToolCall } from '@orkestrel/tool'\n *\n * isToolCall({ id: '1', name: 'search', arguments: { query: 'birds' } }) // true\n * isToolCall({ id: '1', name: 'search', arguments: [] }) // false\n * ```\n */\nexport function isToolCall(value: unknown): value is ToolCall {\n\treturn holds(\n\t\t() =>\n\t\t\tisRecord(value) && isString(value.id) && isString(value.name) && isRecord(value.arguments),\n\t)\n}\n","import type { ToolDefinition, ToolInterface } from './types.js'\n\n/**\n * Projects a tool onto the plain definition advertised to a caller, advertising an\n * authored `summary` in place of the full description and carrying `parameters` and\n * `annotations` by reference.\n *\n * @remarks\n * The projection carries `name` and present `title`, `description`, `parameters`, and\n * `annotations` fields in that order. The full `description` stays on the tool for\n * direct lookup, and the definition is never a live handle on the tool's handler.\n *\n * @param tool - The tool to project\n * @returns A fresh definition carrying only the fields the tool authored\n *\n * @example\n * ```ts\n * import { Tool, toolToDefinition } from '@orkestrel/tool'\n *\n * const echo = new Tool({ name: 'echo', summary: 'Echo a value.', execute: (args) => args.value })\n * toolToDefinition(echo) // { name: 'echo', description: 'Echo a value.' }\n * ```\n */\nexport function toolToDefinition(tool: ToolInterface): ToolDefinition {\n\tconst description = tool.summary ?? tool.description\n\treturn {\n\t\tname: tool.name,\n\t\t...(tool.title === undefined ? {} : { title: tool.title }),\n\t\t...(description === undefined ? {} : { description }),\n\t\t...(tool.parameters === undefined ? {} : { parameters: tool.parameters }),\n\t\t...(tool.annotations === undefined ? {} : { annotations: tool.annotations }),\n\t}\n}\n","import type { ContractInterface } from '@orkestrel/contract'\nimport type { ToolAnnotations, ToolContext, ToolInterface, ToolOptions } from '../types.js'\nimport { createContract, isRecord, isString, schemaToParameters } from '@orkestrel/contract'\nimport { ToolError } from '../errors.js'\n\n/**\n * Binds an executable tool definition to a handler.\n *\n * @remarks\n * Advertised fields and execution context are forwarded by reference.\n * A contract derives parameters at construction and refuses parse faults before the\n * handler runs, then forwards the parsed arguments; without a contract, arguments\n * retain their identity. Supplying a contract and parameters throws a schema conflict.\n * Caller context is consumer-asserted and is not verified. Handler failures are not\n * caught here; {@link ToolManager} owns per-call error isolation.\n *\n * @example\n * ```ts\n * import { Tool } from '@orkestrel/tool'\n *\n * const tool = new Tool({\n * \tname: 'add',\n * \tdescription: 'Add two numbers',\n * \tparameters: {\n * \t\ttype: 'object',\n * \t\tproperties: { a: { type: 'number' }, b: { type: 'number' } },\n * \t},\n * \texecute: (args) => Number(args.a) + Number(args.b),\n * })\n * ```\n */\nexport class Tool implements ToolInterface {\n\treadonly #contract?: ContractInterface<unknown>\n\treadonly #execute: ToolOptions['execute']\n\n\treadonly name: string\n\treadonly title?: string\n\treadonly description?: string\n\treadonly summary?: string\n\treadonly parameters?: Readonly<Record<string, unknown>>\n\treadonly annotations?: ToolAnnotations\n\n\tconstructor(options: ToolOptions) {\n\t\tif (options.contract !== undefined && options.parameters !== undefined) {\n\t\t\tthrow new ToolError('SCHEMA', 'Choose contract or parameters, not both')\n\t\t}\n\t\tthis.name = options.name\n\t\tif (options.title !== undefined) this.title = options.title\n\t\tif (options.description !== undefined) this.description = options.description\n\t\tif (options.summary !== undefined) this.summary = options.summary\n\t\tif (options.annotations !== undefined) this.annotations = options.annotations\n\t\tif (options.contract !== undefined) {\n\t\t\tthis.#contract = createContract(options.contract)\n\t\t\tconst parameters = schemaToParameters(this.#contract.schema)\n\t\t\tif (parameters !== undefined) this.parameters = parameters\n\t\t} else if (options.parameters !== undefined) this.parameters = options.parameters\n\t\tthis.#execute = options.execute\n\t}\n\n\texecute(\n\t\targs: Readonly<Record<string, unknown>>,\n\t\tcontext: ToolContext,\n\t): Promise<unknown> | unknown {\n\t\tconst faults = this.#contract?.explain(args) ?? []\n\t\tconst fault = faults[0]\n\t\tif (fault !== undefined) {\n\t\t\tconst path = isString(fault.path) ? fault.path : fault.path.join('.')\n\t\t\tlet message = `${path}: ${fault.reason}`\n\t\t\tif ('expected' in fault) message += `; expected ${fault.expected}`\n\t\t\tif ('received' in fault) message += `; received ${fault.received}`\n\t\t\tif ('constraint' in fault) message += `; constraint ${fault.constraint}`\n\t\t\tif ('limit' in fault && fault.limit !== undefined) message += `; limit ${fault.limit}`\n\t\t\tif ('variants' in fault) message += `; variants ${fault.variants}`\n\t\t\tif ('matched' in fault) message += `; matched ${fault.matched}`\n\t\t\tthrow new ToolError('ARGUMENTS', message, { faults })\n\t\t}\n\t\tif (this.#contract !== undefined) {\n\t\t\tconst parsed = this.#contract.parse(args)\n\t\t\tif (!isRecord(parsed)) throw new ToolError('ARGUMENTS', 'Arguments did not parse')\n\t\t\treturn this.#execute(parsed, context)\n\t\t}\n\t\treturn this.#execute(args, context)\n\t}\n}\n","import type { EmitterInterface } from '@orkestrel/emitter'\nimport type {\n\tToolCall,\n\tToolContext,\n\tToolDefinition,\n\tToolInterface,\n\tToolManagerEventMap,\n\tToolManagerInterface,\n\tToolManagerOptions,\n\tToolResult,\n} from '../types.js'\nimport { attempt, isArray } from '@orkestrel/contract'\nimport { Emitter } from '@orkestrel/emitter'\nimport { toolToDefinition } from '../helpers.js'\n\n/**\n * Represents an insertion-ordered tool registry with per-call error isolation.\n *\n * @remarks\n * A repeated name overwrites the registered tool without changing its insertion\n * position. Definitions advertise `summary` in place of `description` when present.\n * Unknown names and handler throws resolve to error results; a call whose `id` or `name`\n * accessor throws when read makes its call, and the batch holding it, reject. Batch\n * execution preserves input order and isolates each call whose members are plain\n * values. Execution context is shared across a batch and forwarded unchanged. An\n * omitted context receives a non-aborted signal. A signal aborted before handler\n * entry produces an error result; later cancellation is the handler's responsibility.\n * Registry changes publish synchronously. Replacements publish `remove`, then `add`\n * if the map still holds that exact replacement after the removal listeners return.\n * Destruction clears the tools before releasing listeners. A destroyed registry\n * publishes nothing, even when later additions update its tool map.\n *\n * @example\n * ```ts\n * import { Tool, ToolManager } from '@orkestrel/tool'\n *\n * const tools = new ToolManager()\n * tools.add(new Tool({ name: 'add', execute: (args) => Number(args.x) + Number(args.y) }))\n * const result = await tools.execute({\n * \tid: '1',\n * \tname: 'add',\n * \targuments: { x: 1, y: 2 },\n * })\n * ```\n */\nexport class ToolManager implements ToolManagerInterface {\n\treadonly #tools = new Map<string, ToolInterface>()\n\treadonly #emitter: Emitter<ToolManagerEventMap>\n\n\tconstructor(options?: ToolManagerOptions) {\n\t\tthis.#emitter = new Emitter<ToolManagerEventMap>(options)\n\t}\n\n\tget count(): number {\n\t\treturn this.#tools.size\n\t}\n\n\tget emitter(): EmitterInterface<ToolManagerEventMap> {\n\t\treturn this.#emitter\n\t}\n\n\tadd(tool: ToolInterface): void\n\tadd(tools: readonly ToolInterface[]): void\n\tadd(tools: ToolInterface | readonly ToolInterface[]): void {\n\t\tif (isArray(tools)) {\n\t\t\tfor (const tool of tools) this.add(tool)\n\t\t\treturn\n\t\t}\n\t\tconst previous = this.#tools.get(tools.name)\n\t\tthis.#tools.set(tools.name, tools)\n\t\tif (previous !== undefined) this.#emitter.emit('remove', previous)\n\t\tif (this.#tools.get(tools.name) === tools) this.#emitter.emit('add', tools)\n\t}\n\n\ttool(name: string): ToolInterface | undefined {\n\t\treturn this.#tools.get(name)\n\t}\n\n\ttools(): readonly ToolInterface[] {\n\t\treturn [...this.#tools.values()]\n\t}\n\n\tdefinitions(): readonly ToolDefinition[] {\n\t\treturn [...this.#tools.values()].map((tool) => toolToDefinition(tool))\n\t}\n\n\texecute(call: ToolCall, context?: ToolContext): Promise<ToolResult>\n\texecute(calls: readonly ToolCall[], context?: ToolContext): Promise<readonly ToolResult[]>\n\texecute(\n\t\tcall: ToolCall | readonly ToolCall[],\n\t\tcontext: ToolContext = { signal: new AbortController().signal },\n\t): Promise<ToolResult | readonly ToolResult[]> {\n\t\tif (isArray(call)) return Promise.all(call.map((one) => this.#run(one, context)))\n\t\treturn this.#run(call, context)\n\t}\n\n\tremove(name: string): boolean\n\tremove(names: readonly string[]): boolean\n\tremove(names: string | readonly string[]): boolean {\n\t\tif (isArray(names)) {\n\t\t\tlet removed = true\n\t\t\tfor (const name of names) {\n\t\t\t\tif (!this.remove(name)) removed = false\n\t\t\t}\n\t\t\treturn removed\n\t\t}\n\t\tconst tool = this.#tools.get(names)\n\t\tif (tool === undefined) return false\n\t\tthis.#tools.delete(names)\n\t\tthis.#emitter.emit('remove', tool)\n\t\treturn true\n\t}\n\n\tclear(): void {\n\t\tconst tools = this.tools()\n\t\tthis.#tools.clear()\n\t\tthis.#emitter.emit('clear', tools)\n\t}\n\n\tdestroy(): void {\n\t\tthis.clear()\n\t\tthis.#emitter.destroy()\n\t\tthis.#tools.clear()\n\t}\n\n\tasync #run(call: ToolCall, context: ToolContext): Promise<ToolResult> {\n\t\tconst tool = this.#tools.get(call.name)\n\t\tif (tool === undefined) {\n\t\t\treturn {\n\t\t\t\tid: call.id,\n\t\t\t\tname: call.name,\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `tool not found: ${call.name}`,\n\t\t\t}\n\t\t}\n\t\ttry {\n\t\t\tif (context.signal.aborted) {\n\t\t\t\tconst reason: unknown = context.signal.reason\n\t\t\t\treturn {\n\t\t\t\t\tid: call.id,\n\t\t\t\t\tname: call.name,\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: reason === undefined ? 'aborted' : String(reason),\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst value = await tool.execute(call.arguments, context)\n\t\t\treturn { id: call.id, name: call.name, success: true, value }\n\t\t} catch (error) {\n\t\t\tconst message = attempt(() =>\n\t\t\t\terror instanceof Error ? String(error.message) : String(error),\n\t\t\t)\n\t\t\treturn {\n\t\t\t\tid: call.id,\n\t\t\t\tname: call.name,\n\t\t\t\tsuccess: false,\n\t\t\t\terror: message.success ? message.value : 'Unknown thrown value',\n\t\t\t}\n\t\t}\n\t}\n}\n","import type {\n\tToolInterface,\n\tToolManagerInterface,\n\tToolManagerOptions,\n\tToolOptions,\n} from './types.js'\nimport { Tool } from './tools/Tool.js'\nimport { ToolManager } from './tools/ToolManager.js'\n\n/**\n * Creates an executable tool bound to the supplied handler, returned as a\n * `ToolInterface` so a call site holds the published contract rather than the `Tool`\n * class.\n *\n * @param options - The advertised definition and execution handler\n * @returns A tool bound to the supplied handler\n *\n * @example Anatomy of a tool\n * ```ts\n * import { createTool } from '@orkestrel/tool'\n *\n * const add = createTool({\n * \tname: 'add',\n * \tdescription: 'Add two numeric values and return their sum. Both operands are required.',\n * \tsummary: 'Add two numbers.',\n * \tparameters: {\n * \t\ttype: 'object',\n * \t\tproperties: {\n * \t\t\tleft: { type: 'number' },\n * \t\t\tright: { type: 'number' },\n * \t\t},\n * \t\trequired: ['left', 'right'],\n * \t},\n * \texecute: (args) => Number(args.left) + Number(args.right),\n * })\n * ```\n */\nexport function createTool(options: ToolOptions): ToolInterface {\n\treturn new Tool(options)\n}\n\n/**\n * Creates an empty registry that advertises definitions and executes calls with\n * per-call error isolation, returned as a `ToolManagerInterface` so a caller holds the\n * published contract rather than the `ToolManager` class.\n *\n * @param options - The initial registry listeners and listener-error handler\n * @returns A registry bound to no tools\n *\n * @example\n * ```ts\n * import { createTool, createToolManager } from '@orkestrel/tool'\n *\n * const tools = createToolManager()\n * tools.add(createTool({ name: 'echo', execute: (args) => args.value }))\n * const result = await tools.execute({\n * \tid: '1',\n * \tname: 'echo',\n * \targuments: { value: 'hello' },\n * })\n * ```\n */\nexport function createToolManager(options?: ToolManagerOptions): ToolManagerInterface {\n\treturn new ToolManager(options)\n}\n"],"mappings":";;;;;;;;;;;;;;AAcA,IAAa,YAAb,cAA+B,MAAM;CACpC,OAAyB;CACzB;CACA;CAEA,YAAY,MAAqB,SAAiB,SAA4B;EAC7E,MAAM,OAAO;EACb,KAAK,OAAO;EACZ,IAAI,YAAY,KAAA,GAAW,KAAK,UAAU;CAC3C;AACD;;;;;;;;;;;;;;;AAgBA,SAAgB,YAAY,OAAoC;CAC/D,OAAO,WAAW,OAAO,SAAS;AACnC;;;;;;;;;;;;;;;;;;;;;;ACpBA,SAAgB,WAAW,OAAmC;CAC7D,OAAO,YAEL,SAAS,KAAK,KAAK,SAAS,MAAM,EAAE,KAAK,SAAS,MAAM,IAAI,KAAK,SAAS,MAAM,SAAS,CAC3F;AACD;;;;;;;;;;;;;;;;;;;;;;;;ACJA,SAAgB,iBAAiB,MAAqC;CACrE,MAAM,cAAc,KAAK,WAAW,KAAK;CACzC,OAAO;EACN,MAAM,KAAK;EACX,GAAI,KAAK,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,OAAO,KAAK,MAAM;EACxD,GAAI,gBAAgB,KAAA,IAAY,CAAC,IAAI,EAAE,YAAY;EACnD,GAAI,KAAK,eAAe,KAAA,IAAY,CAAC,IAAI,EAAE,YAAY,KAAK,WAAW;EACvE,GAAI,KAAK,gBAAgB,KAAA,IAAY,CAAC,IAAI,EAAE,aAAa,KAAK,YAAY;CAC3E;AACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACDA,IAAa,OAAb,MAA2C;CAC1C;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CAEA,YAAY,SAAsB;EACjC,IAAI,QAAQ,aAAa,KAAA,KAAa,QAAQ,eAAe,KAAA,GAC5D,MAAM,IAAI,UAAU,UAAU,yCAAyC;EAExE,KAAK,OAAO,QAAQ;EACpB,IAAI,QAAQ,UAAU,KAAA,GAAW,KAAK,QAAQ,QAAQ;EACtD,IAAI,QAAQ,gBAAgB,KAAA,GAAW,KAAK,cAAc,QAAQ;EAClE,IAAI,QAAQ,YAAY,KAAA,GAAW,KAAK,UAAU,QAAQ;EAC1D,IAAI,QAAQ,gBAAgB,KAAA,GAAW,KAAK,cAAc,QAAQ;EAClE,IAAI,QAAQ,aAAa,KAAA,GAAW;GACnC,KAAK,YAAY,eAAe,QAAQ,QAAQ;GAChD,MAAM,aAAa,mBAAmB,KAAK,UAAU,MAAM;GAC3D,IAAI,eAAe,KAAA,GAAW,KAAK,aAAa;EACjD,OAAO,IAAI,QAAQ,eAAe,KAAA,GAAW,KAAK,aAAa,QAAQ;EACvE,KAAK,WAAW,QAAQ;CACzB;CAEA,QACC,MACA,SAC6B;EAC7B,MAAM,SAAS,KAAK,WAAW,QAAQ,IAAI,KAAK,CAAC;EACjD,MAAM,QAAQ,OAAO;EACrB,IAAI,UAAU,KAAA,GAAW;GAExB,IAAI,UAAU,GADD,SAAS,MAAM,IAAI,IAAI,MAAM,OAAO,MAAM,KAAK,KAAK,GAAG,EAC9C,IAAI,MAAM;GAChC,IAAI,cAAc,OAAO,WAAW,cAAc,MAAM;GACxD,IAAI,cAAc,OAAO,WAAW,cAAc,MAAM;GACxD,IAAI,gBAAgB,OAAO,WAAW,gBAAgB,MAAM;GAC5D,IAAI,WAAW,SAAS,MAAM,UAAU,KAAA,GAAW,WAAW,WAAW,MAAM;GAC/E,IAAI,cAAc,OAAO,WAAW,cAAc,MAAM;GACxD,IAAI,aAAa,OAAO,WAAW,aAAa,MAAM;GACtD,MAAM,IAAI,UAAU,aAAa,SAAS,EAAE,OAAO,CAAC;EACrD;EACA,IAAI,KAAK,cAAc,KAAA,GAAW;GACjC,MAAM,SAAS,KAAK,UAAU,MAAM,IAAI;GACxC,IAAI,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI,UAAU,aAAa,yBAAyB;GACjF,OAAO,KAAK,SAAS,QAAQ,OAAO;EACrC;EACA,OAAO,KAAK,SAAS,MAAM,OAAO;CACnC;AACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACtCA,IAAa,cAAb,MAAyD;CACxD,yBAAkB,IAAI,IAA2B;CACjD;CAEA,YAAY,SAA8B;EACzC,KAAK,WAAW,IAAI,QAA6B,OAAO;CACzD;CAEA,IAAI,QAAgB;EACnB,OAAO,KAAK,OAAO;CACpB;CAEA,IAAI,UAAiD;EACpD,OAAO,KAAK;CACb;CAIA,IAAI,OAAuD;EAC1D,IAAI,QAAQ,KAAK,GAAG;GACnB,KAAK,MAAM,QAAQ,OAAO,KAAK,IAAI,IAAI;GACvC;EACD;EACA,MAAM,WAAW,KAAK,OAAO,IAAI,MAAM,IAAI;EAC3C,KAAK,OAAO,IAAI,MAAM,MAAM,KAAK;EACjC,IAAI,aAAa,KAAA,GAAW,KAAK,SAAS,KAAK,UAAU,QAAQ;EACjE,IAAI,KAAK,OAAO,IAAI,MAAM,IAAI,MAAM,OAAO,KAAK,SAAS,KAAK,OAAO,KAAK;CAC3E;CAEA,KAAK,MAAyC;EAC7C,OAAO,KAAK,OAAO,IAAI,IAAI;CAC5B;CAEA,QAAkC;EACjC,OAAO,CAAC,GAAG,KAAK,OAAO,OAAO,CAAC;CAChC;CAEA,cAAyC;EACxC,OAAO,CAAC,GAAG,KAAK,OAAO,OAAO,CAAC,CAAC,CAAC,KAAK,SAAS,iBAAiB,IAAI,CAAC;CACtE;CAIA,QACC,MACA,UAAuB,EAAE,QAAQ,IAAI,gBAAgB,CAAC,CAAC,OAAO,GAChB;EAC9C,IAAI,QAAQ,IAAI,GAAG,OAAO,QAAQ,IAAI,KAAK,KAAK,QAAQ,KAAK,KAAK,KAAK,OAAO,CAAC,CAAC;EAChF,OAAO,KAAK,KAAK,MAAM,OAAO;CAC/B;CAIA,OAAO,OAA4C;EAClD,IAAI,QAAQ,KAAK,GAAG;GACnB,IAAI,UAAU;GACd,KAAK,MAAM,QAAQ,OAClB,IAAI,CAAC,KAAK,OAAO,IAAI,GAAG,UAAU;GAEnC,OAAO;EACR;EACA,MAAM,OAAO,KAAK,OAAO,IAAI,KAAK;EAClC,IAAI,SAAS,KAAA,GAAW,OAAO;EAC/B,KAAK,OAAO,OAAO,KAAK;EACxB,KAAK,SAAS,KAAK,UAAU,IAAI;EACjC,OAAO;CACR;CAEA,QAAc;EACb,MAAM,QAAQ,KAAK,MAAM;EACzB,KAAK,OAAO,MAAM;EAClB,KAAK,SAAS,KAAK,SAAS,KAAK;CAClC;CAEA,UAAgB;EACf,KAAK,MAAM;EACX,KAAK,SAAS,QAAQ;EACtB,KAAK,OAAO,MAAM;CACnB;CAEA,MAAM,KAAK,MAAgB,SAA2C;EACrE,MAAM,OAAO,KAAK,OAAO,IAAI,KAAK,IAAI;EACtC,IAAI,SAAS,KAAA,GACZ,OAAO;GACN,IAAI,KAAK;GACT,MAAM,KAAK;GACX,SAAS;GACT,OAAO,mBAAmB,KAAK;EAChC;EAED,IAAI;GACH,IAAI,QAAQ,OAAO,SAAS;IAC3B,MAAM,SAAkB,QAAQ,OAAO;IACvC,OAAO;KACN,IAAI,KAAK;KACT,MAAM,KAAK;KACX,SAAS;KACT,OAAO,WAAW,KAAA,IAAY,YAAY,OAAO,MAAM;IACxD;GACD;GACA,MAAM,QAAQ,MAAM,KAAK,QAAQ,KAAK,WAAW,OAAO;GACxD,OAAO;IAAE,IAAI,KAAK;IAAI,MAAM,KAAK;IAAM,SAAS;IAAM;GAAM;EAC7D,SAAS,OAAO;GACf,MAAM,UAAU,cACf,iBAAiB,QAAQ,OAAO,MAAM,OAAO,IAAI,OAAO,KAAK,CAC9D;GACA,OAAO;IACN,IAAI,KAAK;IACT,MAAM,KAAK;IACX,SAAS;IACT,OAAO,QAAQ,UAAU,QAAQ,QAAQ;GAC1C;EACD;CACD;AACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC1HA,SAAgB,WAAW,SAAqC;CAC/D,OAAO,IAAI,KAAK,OAAO;AACxB;;;;;;;;;;;;;;;;;;;;;;AAuBA,SAAgB,kBAAkB,SAAoD;CACrF,OAAO,IAAI,YAAY,OAAO;AAC/B"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orkestrel/tool",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.15",
|
|
4
4
|
"description": "The tool runtime for the @orkestrel line — JSON-Schema tool definitions, calls, results, executable tools, and a registry with per-call error isolation. Part of the @orkestrel line.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
46
46
|
"copy": "node -e \"const fs=require('node:fs'),p=require('node:path'),a=process.argv[1],b=process.argv[2];fs.mkdirSync(p.dirname(b),{recursive:true});fs.cpSync(a,b,{force:true});console.log('Copied: '+a+' to '+b)\"",
|
|
47
47
|
"scaffold": "scaffold",
|
|
48
|
-
"lint": "oxlint --config .oxlintrc.json --fix
|
|
48
|
+
"lint": "oxlint --config .oxlintrc.json --fix .",
|
|
49
49
|
"check": "tsc --noEmit --project tsconfig.json && npm run check:src",
|
|
50
50
|
"check:src": "npm run check:src:core",
|
|
51
51
|
"check:src:core": "tsc --noEmit -p configs/src/tsconfig.core.json",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"test:src:core": "vitest run --config vite.config.ts --no-cache --reporter=dot --project src:core",
|
|
58
58
|
"test:policy": "vitest run --config vite.config.ts --no-cache --reporter=dot --project policy",
|
|
59
59
|
"test:config": "vitest run --config vite.config.ts --no-cache --reporter=dot --project config",
|
|
60
|
-
"test:guides": "
|
|
60
|
+
"test:guides": "node --experimental-strip-types tests/guides.test.ts",
|
|
61
61
|
"build": "npm run clean && npm run build:src",
|
|
62
62
|
"build:src": "npm run build:src:core",
|
|
63
63
|
"build:src:core": "vite build --config configs/src/vite.core.config.ts && npm run copy dist/src/core/index.d.ts dist/src/core/index.d.cts",
|
|
@@ -69,20 +69,20 @@
|
|
|
69
69
|
"test:setup": "vitest run --config vite.config.ts --no-cache --reporter=dot --project setup"
|
|
70
70
|
},
|
|
71
71
|
"dependencies": {
|
|
72
|
-
"@orkestrel/contract": "^0.0.
|
|
72
|
+
"@orkestrel/contract": "^0.0.17",
|
|
73
|
+
"@orkestrel/emitter": "^0.0.10"
|
|
73
74
|
},
|
|
74
75
|
"devDependencies": {
|
|
75
|
-
"@microsoft/api-extractor": "^7.59.
|
|
76
|
-
"@orkestrel/guide": "^0.0.
|
|
77
|
-
"@orkestrel/probe": "^0.0.
|
|
78
|
-
"@orkestrel/scaffold": "^0.0.
|
|
79
|
-
"@orkestrel/test": "^0.0.
|
|
80
|
-
"@types/node": "^26.
|
|
81
|
-
"oxfmt": "^0.
|
|
82
|
-
"oxlint": "^1.
|
|
76
|
+
"@microsoft/api-extractor": "^7.59.1",
|
|
77
|
+
"@orkestrel/guide": "^0.0.19",
|
|
78
|
+
"@orkestrel/probe": "^0.0.14",
|
|
79
|
+
"@orkestrel/scaffold": "^0.0.68",
|
|
80
|
+
"@orkestrel/test": "^0.0.14",
|
|
81
|
+
"@types/node": "^26.5.1",
|
|
82
|
+
"oxfmt": "^0.68.0",
|
|
83
|
+
"oxlint": "^1.83.0",
|
|
83
84
|
"typescript": "^6.0.3",
|
|
84
|
-
"vite": "^8.
|
|
85
|
-
"vite-plugin-dts": "^5.1.0",
|
|
85
|
+
"vite": "^8.3.0",
|
|
86
86
|
"vitest": "^4.1.11"
|
|
87
87
|
},
|
|
88
88
|
"engines": {
|