@orkestrel/tool 0.0.12 → 0.0.14

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 CHANGED
@@ -1,16 +1,14 @@
1
1
  # @orkestrel/tool
2
2
 
3
- The tool runtime for the `@orkestrel` line.
4
-
5
- A tool is a callable function described by a JSON Schema: a name, an optional description, an
6
- optional parameter schema, and the handler that runs it. That is the whole idea — a tool is an
7
- API call whose shape is data, so whoever calls it can discover it, present it, and invoke it
8
- without knowing anything about the code behind it. This package ships that shape and the
9
- registry around it: definitions to advertise, calls to dispatch, results to correlate, and
10
- per-call error isolation so one bad tool never takes down the run.
11
-
12
- Nothing here is model-specific. An agent loop, an MCP bridge, and plain application code are all
13
- just callers.
3
+ > The tool runtime for the `@orkestrel` line: a `Tool` binding an advertised JSON Schema
4
+ > definition to its handler, a `ToolManager` registry that advertises those definitions and
5
+ > executes calls with per-call error isolation, and the correlated `ToolCall` and `ToolResult`
6
+ > pair that travels between a caller and the registry.
7
+
8
+ Build a tool with the `createTool` function, register it in a registry from the
9
+ `createToolManager` function, hand `definitions()` to whatever chooses the call, and pass the
10
+ call you get back to `execute`. Nothing here is model-specific an agent loop, an MCP bridge,
11
+ and plain application code are all callers.
14
12
 
15
13
  ## Install
16
14
 
@@ -1,16 +1,48 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  let _orkestrel_contract = require("@orkestrel/contract");
3
+ //#region src/core/helpers.ts
4
+ /**
5
+ * Projects a tool onto the plain definition advertised to a caller, advertising an
6
+ * authored `summary` in place of the full description and carrying the parameter schema
7
+ * by reference.
8
+ *
9
+ * @remarks
10
+ * The projection is a fresh object carrying `name`, then `description` only when the
11
+ * tool authored a summary or a description, then `parameters` only when the tool
12
+ * authored a schema. The full `description` stays on the tool for direct lookup, and
13
+ * the definition is never a live handle on the tool's handler.
14
+ *
15
+ * @param tool - The tool to project
16
+ * @returns A fresh definition carrying only the fields the tool authored
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * import { Tool, toolToDefinition } from '@orkestrel/tool'
21
+ *
22
+ * const echo = new Tool({ name: 'echo', summary: 'Echo a value.', execute: (args) => args.value })
23
+ * toolToDefinition(echo) // { name: 'echo', description: 'Echo a value.' }
24
+ * ```
25
+ */
26
+ function toolToDefinition(tool) {
27
+ const definition = { name: tool.name };
28
+ const description = tool.summary ?? tool.description;
29
+ if (description !== void 0) definition.description = description;
30
+ if (tool.parameters !== void 0) definition.parameters = tool.parameters;
31
+ return definition;
32
+ }
33
+ //#endregion
3
34
  //#region src/core/validators.ts
4
35
  /**
5
- * Determine whether an unknown value is structurally a {@link ToolCall}.
36
+ * Determines whether an unknown value is structurally a {@link ToolCall}, staying total
37
+ * for malformed and adversarial input.
6
38
  *
7
39
  * @remarks
8
- * This total guard accepts a plain record with string `id` and `name` fields and a
40
+ * The accepted shape is a plain record with string `id` and `name` fields and a
9
41
  * plain-record `arguments` field. Optional caller context remains opaque and is not
10
- * read or verified. Adversarial values return `false`.
42
+ * read or verified.
11
43
  *
12
44
  * @param value - The value to test
13
- * @returns `true` when the value has the complete tool-call shape
45
+ * @returns True if the value has the complete tool-call shape; false otherwise
14
46
  *
15
47
  * @example
16
48
  * ```ts
@@ -26,7 +58,7 @@ function isToolCall(value) {
26
58
  //#endregion
27
59
  //#region src/core/tools/Tool.ts
28
60
  /**
29
- * An executable tool definition bound to a handler.
61
+ * Binds an executable tool definition to a handler.
30
62
  *
31
63
  * @remarks
32
64
  * Schema fields, arguments, and present caller context are forwarded by reference.
@@ -69,14 +101,15 @@ var Tool = class {
69
101
  //#endregion
70
102
  //#region src/core/tools/ToolManager.ts
71
103
  /**
72
- * An insertion-ordered tool registry with per-call error isolation.
104
+ * Represents an insertion-ordered tool registry with per-call error isolation.
73
105
  *
74
106
  * @remarks
75
107
  * A repeated name overwrites the registered tool without changing its insertion
76
108
  * position. Definitions advertise `summary` in place of `description` when present.
77
- * Unknown names and handler throws resolve to error results; batch execution preserves
78
- * input order and never fails as a whole because of an individual call. Optional
79
- * consumer-asserted caller context is forwarded without verification.
109
+ * Unknown names and handler throws resolve to error results; a call whose `id` or `name`
110
+ * accessor throws when read makes its call, and the batch holding it, reject. Batch
111
+ * execution preserves input order and isolates each call whose members are plain
112
+ * values. Optional consumer-asserted caller context is forwarded without verification.
80
113
  *
81
114
  * @example
82
115
  * ```ts
@@ -110,7 +143,7 @@ var ToolManager = class {
110
143
  return [...this.#tools.values()];
111
144
  }
112
145
  definitions() {
113
- return [...this.#tools.values()].map((tool) => this.#definition(tool));
146
+ return [...this.#tools.values()].map((tool) => toolToDefinition(tool));
114
147
  }
115
148
  execute(call) {
116
149
  if ((0, _orkestrel_contract.isArray)(call)) return Promise.all(call.map((one) => this.#run(one)));
@@ -118,8 +151,8 @@ var ToolManager = class {
118
151
  }
119
152
  remove(names) {
120
153
  if ((0, _orkestrel_contract.isArray)(names)) {
121
- let removed = false;
122
- for (const name of names) if (this.#tools.delete(name)) removed = true;
154
+ let removed = true;
155
+ for (const name of names) if (!this.#tools.delete(name)) removed = false;
123
156
  return removed;
124
157
  }
125
158
  return this.#tools.delete(names);
@@ -154,30 +187,34 @@ var ToolManager = class {
154
187
  };
155
188
  }
156
189
  }
157
- #definition(tool) {
158
- const definition = { name: tool.name };
159
- const description = tool.summary ?? tool.description;
160
- if (description !== void 0) definition.description = description;
161
- if (tool.parameters !== void 0) definition.parameters = tool.parameters;
162
- return definition;
163
- }
164
190
  };
165
191
  //#endregion
166
192
  //#region src/core/factories.ts
167
193
  /**
168
- * Create an executable tool.
194
+ * Creates an executable tool bound to the supplied handler, returned as a
195
+ * `ToolInterface` so a call site holds the published contract rather than the `Tool`
196
+ * class.
169
197
  *
170
198
  * @param options - The advertised definition and execution handler
171
199
  * @returns A tool bound to the supplied handler
172
200
  *
173
- * @example
201
+ * @example Anatomy of a tool
174
202
  * ```ts
175
203
  * import { createTool } from '@orkestrel/tool'
176
204
  *
177
205
  * const add = createTool({
178
206
  * name: 'add',
179
- * description: 'Add two numbers',
180
- * execute: (args) => Number(args.a) + Number(args.b),
207
+ * description: 'Add two numeric values and return their sum. Both operands are required.',
208
+ * summary: 'Add two numbers.',
209
+ * parameters: {
210
+ * type: 'object',
211
+ * properties: {
212
+ * left: { type: 'number' },
213
+ * right: { type: 'number' },
214
+ * },
215
+ * required: ['left', 'right'],
216
+ * },
217
+ * execute: (args) => Number(args.left) + Number(args.right),
181
218
  * })
182
219
  * ```
183
220
  */
@@ -185,10 +222,11 @@ function createTool(options) {
185
222
  return new Tool(options);
186
223
  }
187
224
  /**
188
- * Create an empty tool registry.
225
+ * Creates an empty registry that advertises definitions and executes calls with
226
+ * per-call error isolation, returned as a `ToolManagerInterface` so a caller holds the
227
+ * published contract rather than the `ToolManager` class.
189
228
  *
190
- * @returns A registry that advertises definitions and executes calls with per-call
191
- * error isolation
229
+ * @returns A registry bound to no tools
192
230
  *
193
231
  * @example
194
232
  * ```ts
@@ -212,5 +250,6 @@ exports.ToolManager = ToolManager;
212
250
  exports.createTool = createTool;
213
251
  exports.createToolManager = createToolManager;
214
252
  exports.isToolCall = isToolCall;
253
+ exports.toolToDefinition = toolToDefinition;
215
254
 
216
255
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["#execute","#tools","#definition","#run"],"sources":["../../../src/core/validators.ts","../../../src/core/tools/Tool.ts","../../../src/core/tools/ToolManager.ts","../../../src/core/factories.ts"],"sourcesContent":["import type { ToolCall } from './types.js'\nimport { holds, isRecord, isString } from '@orkestrel/contract'\n\n/**\n * Determine 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` when the value has the complete tool-call shape\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 * An executable tool definition bound 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'\n\n/**\n * 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; batch execution preserves\n * input order and never fails as a whole because of an individual call. Optional\n * 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) => this.#definition(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 = false\n\t\t\tfor (const name of names) {\n\t\t\t\tif (this.#tools.delete(name)) removed = true\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\t#definition(tool: ToolInterface): ToolDefinition {\n\t\tconst definition: {\n\t\t\tname: string\n\t\t\tdescription?: string\n\t\t\tparameters?: Readonly<Record<string, unknown>>\n\t\t} = {\n\t\t\tname: tool.name,\n\t\t}\n\t\tconst description = tool.summary ?? tool.description\n\t\tif (description !== undefined) definition.description = description\n\t\tif (tool.parameters !== undefined) definition.parameters = tool.parameters\n\t\treturn definition\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 * Create 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 * Create 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":"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"}
1
+ {"version":3,"file":"index.cjs","names":[],"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, advertising an\n * authored `summary` in place of the full description and carrying the parameter schema\n * by reference.\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. The full `description` stays on the tool for direct lookup, and\n * 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 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}, 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. Optional caller context remains opaque and is not\n * 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 { 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 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 * @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(): ToolManagerInterface {\n\treturn new ToolManager()\n}\n"],"mappings":"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"}
@@ -1,30 +1,42 @@
1
- import { Failure } from '@orkestrel/contract';
2
- import { Success } from '@orkestrel/contract';
1
+ import type { Failure } from '@orkestrel/contract';
2
+ import type { Success } from '@orkestrel/contract';
3
3
 
4
4
  /**
5
- * Create an executable tool.
5
+ * Creates an executable tool bound to the supplied handler, returned as a
6
+ * `ToolInterface` so a call site holds the published contract rather than the `Tool`
7
+ * class.
6
8
  *
7
9
  * @param options - The advertised definition and execution handler
8
10
  * @returns A tool bound to the supplied handler
9
11
  *
10
- * @example
12
+ * @example Anatomy of a tool
11
13
  * ```ts
12
14
  * import { createTool } from '@orkestrel/tool'
13
15
  *
14
16
  * const add = createTool({
15
17
  * name: 'add',
16
- * description: 'Add two numbers',
17
- * execute: (args) => Number(args.a) + Number(args.b),
18
+ * description: 'Add two numeric values and return their sum. Both operands are required.',
19
+ * summary: 'Add two numbers.',
20
+ * parameters: {
21
+ * type: 'object',
22
+ * properties: {
23
+ * left: { type: 'number' },
24
+ * right: { type: 'number' },
25
+ * },
26
+ * required: ['left', 'right'],
27
+ * },
28
+ * execute: (args) => Number(args.left) + Number(args.right),
18
29
  * })
19
30
  * ```
20
31
  */
21
32
  export declare function createTool(options: ToolOptions): ToolInterface;
22
33
 
23
34
  /**
24
- * Create an empty tool registry.
35
+ * Creates an empty registry that advertises definitions and executes calls with
36
+ * per-call error isolation, returned as a `ToolManagerInterface` so a caller holds the
37
+ * published contract rather than the `ToolManager` class.
25
38
  *
26
- * @returns A registry that advertises definitions and executes calls with per-call
27
- * error isolation
39
+ * @returns A registry bound to no tools
28
40
  *
29
41
  * @example
30
42
  * ```ts
@@ -42,15 +54,16 @@ export declare function createTool(options: ToolOptions): ToolInterface;
42
54
  export declare function createToolManager(): ToolManagerInterface;
43
55
 
44
56
  /**
45
- * Determine whether an unknown value is structurally a {@link ToolCall}.
57
+ * Determines whether an unknown value is structurally a {@link ToolCall}, staying total
58
+ * for malformed and adversarial input.
46
59
  *
47
60
  * @remarks
48
- * This total guard accepts a plain record with string `id` and `name` fields and a
61
+ * The accepted shape is a plain record with string `id` and `name` fields and a
49
62
  * plain-record `arguments` field. Optional caller context remains opaque and is not
50
- * read or verified. Adversarial values return `false`.
63
+ * read or verified.
51
64
  *
52
65
  * @param value - The value to test
53
- * @returns `true` when the value has the complete tool-call shape
66
+ * @returns True if the value has the complete tool-call shape; false otherwise
54
67
  *
55
68
  * @example
56
69
  * ```ts
@@ -63,7 +76,7 @@ export declare function createToolManager(): ToolManagerInterface;
63
76
  export declare function isToolCall(value: unknown): value is ToolCall;
64
77
 
65
78
  /**
66
- * An executable tool definition bound to a handler.
79
+ * Binds an executable tool definition to a handler.
67
80
  *
68
81
  * @remarks
69
82
  * Schema fields, arguments, and present caller context are forwarded by reference.
@@ -96,7 +109,7 @@ export declare class Tool implements ToolInterface {
96
109
  }
97
110
 
98
111
  /**
99
- * A tool call issued by a caller.
112
+ * Describes one request to run a named tool.
100
113
  *
101
114
  * @remarks
102
115
  * `id` correlates the call with its later {@link ToolResult}. `arguments` is the
@@ -105,33 +118,33 @@ export declare class Tool implements ToolInterface {
105
118
  * every trust decision.
106
119
  */
107
120
  export declare interface ToolCall {
108
- /** The identifier that correlates this call with its result. */
121
+ /** Correlates this call with its result. */
109
122
  readonly id: string;
110
- /** The name of the tool to execute. */
123
+ /** Selects the tool to execute. */
111
124
  readonly name: string;
112
- /** The caller-supplied arguments record. */
125
+ /** Carries the record the caller supplied. */
113
126
  readonly arguments: Readonly<Record<string, unknown>>;
114
- /** Consumer-asserted caller context, forwarded without verification. */
127
+ /** Carries consumer-asserted context, forwarded without verification. */
115
128
  readonly caller?: unknown;
116
129
  }
117
130
 
118
131
  /**
119
- * A tool definition advertised to a caller.
132
+ * Describes a tool as advertised to a caller.
120
133
  *
121
134
  * @remarks
122
135
  * `parameters` is an open JSON Schema record describing the arguments the tool accepts.
123
136
  */
124
137
  export declare interface ToolDefinition {
125
- /** The name a caller uses to select the tool. */
138
+ /** Identifies the tool a caller selects. */
126
139
  readonly name: string;
127
- /** A description of the tool's behavior. */
140
+ /** Describes the tool's behavior. */
128
141
  readonly description?: string;
129
- /** The JSON Schema for the tool's arguments. */
142
+ /** Holds the JSON Schema for the tool's arguments. */
130
143
  readonly parameters?: Readonly<Record<string, unknown>>;
131
144
  }
132
145
 
133
146
  /**
134
- * The failed outcome of executing a {@link ToolCall}.
147
+ * Reports the failed outcome of executing a {@link ToolCall}.
135
148
  *
136
149
  * @remarks
137
150
  * `error` is the failure message: an unknown tool name, an `Error`'s message, or
@@ -140,24 +153,31 @@ export declare interface ToolDefinition {
140
153
  * `tool.execute(args)` in its own `try`/`catch`.
141
154
  */
142
155
  export declare interface ToolFailure extends Failure<string> {
143
- /** The identifier of the corresponding call. */
156
+ /** Identifies the corresponding call. */
144
157
  readonly id: string;
145
- /** The name of the called tool. */
158
+ /** Identifies the called tool. */
146
159
  readonly name: string;
147
160
  }
148
161
 
149
162
  /**
150
- * An executable tool: its advertised definition plus its local handler.
163
+ * Represents an executable tool: its advertised definition plus its local handler.
151
164
  *
152
165
  * @remarks
153
166
  * `summary`, when present, is advertised in place of the full `description` by a
154
167
  * {@link ToolManagerInterface}. The full description remains available on the tool.
155
168
  */
156
169
  export declare interface ToolInterface extends ToolDefinition {
157
- /** A concise description to advertise in place of the full description. */
170
+ /** Holds a concise description to advertise in place of the full description. */
158
171
  readonly summary?: string;
159
172
  /**
160
- * Execute the tool.
173
+ * Runs the tool's handler with the caller-supplied arguments and any consumer-asserted
174
+ * caller context.
175
+ *
176
+ * @remarks
177
+ * Failures are not contained here: a synchronous throw propagates and an
178
+ * asynchronous rejection rejects. {@link ToolManagerInterface.execute} is where a
179
+ * call becomes a result. The registry omits `caller` from the invocation when the
180
+ * call carries none, so a handler reading its own arity sees one argument.
161
181
  *
162
182
  * @param args - The caller-supplied arguments record
163
183
  * @param caller - Optional consumer-asserted caller context, forwarded without verification
@@ -167,14 +187,15 @@ export declare interface ToolInterface extends ToolDefinition {
167
187
  }
168
188
 
169
189
  /**
170
- * An insertion-ordered tool registry with per-call error isolation.
190
+ * Represents an insertion-ordered tool registry with per-call error isolation.
171
191
  *
172
192
  * @remarks
173
193
  * A repeated name overwrites the registered tool without changing its insertion
174
194
  * position. Definitions advertise `summary` in place of `description` when present.
175
- * Unknown names and handler throws resolve to error results; batch execution preserves
176
- * input order and never fails as a whole because of an individual call. Optional
177
- * consumer-asserted caller context is forwarded without verification.
195
+ * Unknown names and handler throws resolve to error results; a call whose `id` or `name`
196
+ * accessor throws when read makes its call, and the batch holding it, reject. Batch
197
+ * execution preserves input order and isolates each call whose members are plain
198
+ * values. Optional consumer-asserted caller context is forwarded without verification.
178
199
  *
179
200
  * @example
180
201
  * ```ts
@@ -205,47 +226,49 @@ export declare class ToolManager implements ToolManagerInterface {
205
226
  }
206
227
 
207
228
  /**
208
- * A registry of executable tools with per-call error isolation.
229
+ * Represents a registry of executable tools with per-call error isolation.
209
230
  *
210
231
  * @remarks
211
232
  * Tools are keyed by name in insertion order. Adding an existing name overwrites its
212
- * value without changing its position. Every call resolves to a {@link ToolResult};
213
- * missing tools and thrown handlers become error results. Batch execution preserves
214
- * input order and isolates each call.
233
+ * value without changing its position. Every call whose members are plain values
234
+ * resolves to a {@link ToolResult}; missing tools and thrown handlers become error
235
+ * results, and a call whose `id` or `name` accessor throws when read makes `execute`
236
+ * reject instead. Batch execution preserves input order.
215
237
  */
216
238
  export declare interface ToolManagerInterface {
217
- /** The number of registered tools. */
239
+ /** Reports how many tools are registered. */
218
240
  readonly count: number;
219
241
  /**
220
- * Register one tool.
242
+ * Registers one tool.
221
243
  *
222
244
  * @param tool - The tool to register
223
245
  * @returns Nothing
224
246
  */
225
247
  add(tool: ToolInterface): void;
226
248
  /**
227
- * Register a batch of tools.
249
+ * Registers a batch of tools.
228
250
  *
229
251
  * @param tools - The tools to register
230
252
  * @returns Nothing
231
253
  */
232
254
  add(tools: readonly ToolInterface[]): void;
233
255
  /**
234
- * Find one registered tool by name.
256
+ * Finds one registered tool by name.
235
257
  *
236
258
  * @param name - The registered tool name
237
- * @returns The tool when found, otherwise `undefined`
259
+ * @returns The exact registered instance when found, otherwise `undefined`
238
260
  */
239
261
  tool(name: string): ToolInterface | undefined;
240
262
  /**
241
- * List the registered tools in insertion order.
263
+ * Lists the registered tools in insertion order.
242
264
  *
243
265
  * @returns A new readonly array of registered tools
244
266
  */
245
267
  tools(): readonly ToolInterface[];
246
268
  /**
247
- * List the definitions advertised to a caller.
269
+ * Lists the definitions advertised to a caller.
248
270
  *
271
+ * @remarks
249
272
  * The projected `description` is the tool's `summary` when one was authored,
250
273
  * advertised in place of the full description. The full text stays on the tool
251
274
  * for direct lookup.
@@ -254,35 +277,35 @@ export declare interface ToolManagerInterface {
254
277
  */
255
278
  definitions(): readonly ToolDefinition[];
256
279
  /**
257
- * Execute one call with error isolation.
280
+ * Executes one call with error isolation.
258
281
  *
259
282
  * @param call - The tool call to execute, including optional caller context
260
283
  * @returns The correlated result
261
284
  */
262
285
  execute(call: ToolCall): Promise<ToolResult>;
263
286
  /**
264
- * Execute a batch of calls with per-call error isolation.
287
+ * Executes a batch of calls with per-call error isolation.
265
288
  *
266
289
  * @param calls - The tool calls to execute, including optional caller context
267
290
  * @returns The correlated results in input order
268
291
  */
269
292
  execute(calls: readonly ToolCall[]): Promise<readonly ToolResult[]>;
270
293
  /**
271
- * Remove one registered tool.
294
+ * Removes one registered tool.
272
295
  *
273
296
  * @param name - The tool name to remove
274
- * @returns Whether the tool was present
297
+ * @returns True if the tool was present; false otherwise
275
298
  */
276
299
  remove(name: string): boolean;
277
300
  /**
278
- * Remove a batch of registered tools.
301
+ * Removes a batch of registered tools.
279
302
  *
280
303
  * @param names - The tool names to remove
281
- * @returns Whether any named tool was present
304
+ * @returns True if every named tool was present; false otherwise
282
305
  */
283
306
  remove(names: readonly string[]): boolean;
284
307
  /**
285
- * Remove every registered tool.
308
+ * Removes every registered tool.
286
309
  *
287
310
  * @returns Nothing
288
311
  */
@@ -290,7 +313,7 @@ export declare interface ToolManagerInterface {
290
313
  }
291
314
 
292
315
  /**
293
- * Options for creating an executable tool.
316
+ * Configures an executable tool.
294
317
  *
295
318
  * @remarks
296
319
  * `name` identifies the tool, `description` and `parameters` define what is advertised
@@ -299,38 +322,64 @@ export declare interface ToolManagerInterface {
299
322
  * context. This package forwards that context without verification.
300
323
  */
301
324
  export declare interface ToolOptions {
302
- /** The name a caller uses to select the tool. */
325
+ /** Identifies the tool a caller selects. */
303
326
  readonly name: string;
304
- /** The full description of the tool's behavior. */
327
+ /** Describes the tool's behavior in full. */
305
328
  readonly description?: string;
306
- /** A concise description to advertise in place of the full description. */
329
+ /** Holds a concise description to advertise in place of the full description. */
307
330
  readonly summary?: string;
308
- /** The JSON Schema for the tool's arguments. */
331
+ /** Holds the JSON Schema for the tool's arguments. */
309
332
  readonly parameters?: Readonly<Record<string, unknown>>;
310
- /** The handler that receives arguments and optional unverified caller context. */
333
+ /** Handles the arguments and optional unverified caller context. */
311
334
  readonly execute: (args: Readonly<Record<string, unknown>>, caller?: unknown) => Promise<unknown> | unknown;
312
335
  }
313
336
 
314
337
  /**
315
- * The outcome of executing a {@link ToolCall}.
338
+ * Represents the outcome of executing a {@link ToolCall}.
316
339
  *
317
340
  * @remarks
318
- * Always a result and never a throw. Narrow on `success`.
341
+ * Always a result and never a throw for a call whose members are plain values. A call
342
+ * whose `id` or `name` accessor throws when read makes `execute` reject instead, because
343
+ * no correlated result can be built without them. Narrow on `success`.
319
344
  */
320
345
  export declare type ToolResult = ToolSuccess | ToolFailure;
321
346
 
322
347
  /**
323
- * The successful outcome of executing a {@link ToolCall}.
348
+ * Reports the successful outcome of executing a {@link ToolCall}.
324
349
  *
325
350
  * @remarks
326
351
  * `value` is whatever the handler returned — including `undefined`, `null`, `0`,
327
352
  * `''`, or `false`. A present value never implies a meaningful one.
328
353
  */
329
354
  export declare interface ToolSuccess extends Success<unknown> {
330
- /** The identifier of the corresponding call. */
355
+ /** Identifies the corresponding call. */
331
356
  readonly id: string;
332
- /** The name of the called tool. */
357
+ /** Identifies the called tool. */
333
358
  readonly name: string;
334
359
  }
335
360
 
361
+ /**
362
+ * Projects a tool onto the plain definition advertised to a caller, advertising an
363
+ * authored `summary` in place of the full description and carrying the parameter schema
364
+ * by reference.
365
+ *
366
+ * @remarks
367
+ * The projection is a fresh object carrying `name`, then `description` only when the
368
+ * tool authored a summary or a description, then `parameters` only when the tool
369
+ * authored a schema. The full `description` stays on the tool for direct lookup, and
370
+ * the definition is never a live handle on the tool's handler.
371
+ *
372
+ * @param tool - The tool to project
373
+ * @returns A fresh definition carrying only the fields the tool authored
374
+ *
375
+ * @example
376
+ * ```ts
377
+ * import { Tool, toolToDefinition } from '@orkestrel/tool'
378
+ *
379
+ * const echo = new Tool({ name: 'echo', summary: 'Echo a value.', execute: (args) => args.value })
380
+ * toolToDefinition(echo) // { name: 'echo', description: 'Echo a value.' }
381
+ * ```
382
+ */
383
+ export declare function toolToDefinition(tool: ToolInterface): ToolDefinition;
384
+
336
385
  export { }
@@ -1,30 +1,42 @@
1
- import { Failure } from '@orkestrel/contract';
2
- import { Success } from '@orkestrel/contract';
1
+ import type { Failure } from '@orkestrel/contract';
2
+ import type { Success } from '@orkestrel/contract';
3
3
 
4
4
  /**
5
- * Create an executable tool.
5
+ * Creates an executable tool bound to the supplied handler, returned as a
6
+ * `ToolInterface` so a call site holds the published contract rather than the `Tool`
7
+ * class.
6
8
  *
7
9
  * @param options - The advertised definition and execution handler
8
10
  * @returns A tool bound to the supplied handler
9
11
  *
10
- * @example
12
+ * @example Anatomy of a tool
11
13
  * ```ts
12
14
  * import { createTool } from '@orkestrel/tool'
13
15
  *
14
16
  * const add = createTool({
15
17
  * name: 'add',
16
- * description: 'Add two numbers',
17
- * execute: (args) => Number(args.a) + Number(args.b),
18
+ * description: 'Add two numeric values and return their sum. Both operands are required.',
19
+ * summary: 'Add two numbers.',
20
+ * parameters: {
21
+ * type: 'object',
22
+ * properties: {
23
+ * left: { type: 'number' },
24
+ * right: { type: 'number' },
25
+ * },
26
+ * required: ['left', 'right'],
27
+ * },
28
+ * execute: (args) => Number(args.left) + Number(args.right),
18
29
  * })
19
30
  * ```
20
31
  */
21
32
  export declare function createTool(options: ToolOptions): ToolInterface;
22
33
 
23
34
  /**
24
- * Create an empty tool registry.
35
+ * Creates an empty registry that advertises definitions and executes calls with
36
+ * per-call error isolation, returned as a `ToolManagerInterface` so a caller holds the
37
+ * published contract rather than the `ToolManager` class.
25
38
  *
26
- * @returns A registry that advertises definitions and executes calls with per-call
27
- * error isolation
39
+ * @returns A registry bound to no tools
28
40
  *
29
41
  * @example
30
42
  * ```ts
@@ -42,15 +54,16 @@ export declare function createTool(options: ToolOptions): ToolInterface;
42
54
  export declare function createToolManager(): ToolManagerInterface;
43
55
 
44
56
  /**
45
- * Determine whether an unknown value is structurally a {@link ToolCall}.
57
+ * Determines whether an unknown value is structurally a {@link ToolCall}, staying total
58
+ * for malformed and adversarial input.
46
59
  *
47
60
  * @remarks
48
- * This total guard accepts a plain record with string `id` and `name` fields and a
61
+ * The accepted shape is a plain record with string `id` and `name` fields and a
49
62
  * plain-record `arguments` field. Optional caller context remains opaque and is not
50
- * read or verified. Adversarial values return `false`.
63
+ * read or verified.
51
64
  *
52
65
  * @param value - The value to test
53
- * @returns `true` when the value has the complete tool-call shape
66
+ * @returns True if the value has the complete tool-call shape; false otherwise
54
67
  *
55
68
  * @example
56
69
  * ```ts
@@ -63,7 +76,7 @@ export declare function createToolManager(): ToolManagerInterface;
63
76
  export declare function isToolCall(value: unknown): value is ToolCall;
64
77
 
65
78
  /**
66
- * An executable tool definition bound to a handler.
79
+ * Binds an executable tool definition to a handler.
67
80
  *
68
81
  * @remarks
69
82
  * Schema fields, arguments, and present caller context are forwarded by reference.
@@ -96,7 +109,7 @@ export declare class Tool implements ToolInterface {
96
109
  }
97
110
 
98
111
  /**
99
- * A tool call issued by a caller.
112
+ * Describes one request to run a named tool.
100
113
  *
101
114
  * @remarks
102
115
  * `id` correlates the call with its later {@link ToolResult}. `arguments` is the
@@ -105,33 +118,33 @@ export declare class Tool implements ToolInterface {
105
118
  * every trust decision.
106
119
  */
107
120
  export declare interface ToolCall {
108
- /** The identifier that correlates this call with its result. */
121
+ /** Correlates this call with its result. */
109
122
  readonly id: string;
110
- /** The name of the tool to execute. */
123
+ /** Selects the tool to execute. */
111
124
  readonly name: string;
112
- /** The caller-supplied arguments record. */
125
+ /** Carries the record the caller supplied. */
113
126
  readonly arguments: Readonly<Record<string, unknown>>;
114
- /** Consumer-asserted caller context, forwarded without verification. */
127
+ /** Carries consumer-asserted context, forwarded without verification. */
115
128
  readonly caller?: unknown;
116
129
  }
117
130
 
118
131
  /**
119
- * A tool definition advertised to a caller.
132
+ * Describes a tool as advertised to a caller.
120
133
  *
121
134
  * @remarks
122
135
  * `parameters` is an open JSON Schema record describing the arguments the tool accepts.
123
136
  */
124
137
  export declare interface ToolDefinition {
125
- /** The name a caller uses to select the tool. */
138
+ /** Identifies the tool a caller selects. */
126
139
  readonly name: string;
127
- /** A description of the tool's behavior. */
140
+ /** Describes the tool's behavior. */
128
141
  readonly description?: string;
129
- /** The JSON Schema for the tool's arguments. */
142
+ /** Holds the JSON Schema for the tool's arguments. */
130
143
  readonly parameters?: Readonly<Record<string, unknown>>;
131
144
  }
132
145
 
133
146
  /**
134
- * The failed outcome of executing a {@link ToolCall}.
147
+ * Reports the failed outcome of executing a {@link ToolCall}.
135
148
  *
136
149
  * @remarks
137
150
  * `error` is the failure message: an unknown tool name, an `Error`'s message, or
@@ -140,24 +153,31 @@ export declare interface ToolDefinition {
140
153
  * `tool.execute(args)` in its own `try`/`catch`.
141
154
  */
142
155
  export declare interface ToolFailure extends Failure<string> {
143
- /** The identifier of the corresponding call. */
156
+ /** Identifies the corresponding call. */
144
157
  readonly id: string;
145
- /** The name of the called tool. */
158
+ /** Identifies the called tool. */
146
159
  readonly name: string;
147
160
  }
148
161
 
149
162
  /**
150
- * An executable tool: its advertised definition plus its local handler.
163
+ * Represents an executable tool: its advertised definition plus its local handler.
151
164
  *
152
165
  * @remarks
153
166
  * `summary`, when present, is advertised in place of the full `description` by a
154
167
  * {@link ToolManagerInterface}. The full description remains available on the tool.
155
168
  */
156
169
  export declare interface ToolInterface extends ToolDefinition {
157
- /** A concise description to advertise in place of the full description. */
170
+ /** Holds a concise description to advertise in place of the full description. */
158
171
  readonly summary?: string;
159
172
  /**
160
- * Execute the tool.
173
+ * Runs the tool's handler with the caller-supplied arguments and any consumer-asserted
174
+ * caller context.
175
+ *
176
+ * @remarks
177
+ * Failures are not contained here: a synchronous throw propagates and an
178
+ * asynchronous rejection rejects. {@link ToolManagerInterface.execute} is where a
179
+ * call becomes a result. The registry omits `caller` from the invocation when the
180
+ * call carries none, so a handler reading its own arity sees one argument.
161
181
  *
162
182
  * @param args - The caller-supplied arguments record
163
183
  * @param caller - Optional consumer-asserted caller context, forwarded without verification
@@ -167,14 +187,15 @@ export declare interface ToolInterface extends ToolDefinition {
167
187
  }
168
188
 
169
189
  /**
170
- * An insertion-ordered tool registry with per-call error isolation.
190
+ * Represents an insertion-ordered tool registry with per-call error isolation.
171
191
  *
172
192
  * @remarks
173
193
  * A repeated name overwrites the registered tool without changing its insertion
174
194
  * position. Definitions advertise `summary` in place of `description` when present.
175
- * Unknown names and handler throws resolve to error results; batch execution preserves
176
- * input order and never fails as a whole because of an individual call. Optional
177
- * consumer-asserted caller context is forwarded without verification.
195
+ * Unknown names and handler throws resolve to error results; a call whose `id` or `name`
196
+ * accessor throws when read makes its call, and the batch holding it, reject. Batch
197
+ * execution preserves input order and isolates each call whose members are plain
198
+ * values. Optional consumer-asserted caller context is forwarded without verification.
178
199
  *
179
200
  * @example
180
201
  * ```ts
@@ -205,47 +226,49 @@ export declare class ToolManager implements ToolManagerInterface {
205
226
  }
206
227
 
207
228
  /**
208
- * A registry of executable tools with per-call error isolation.
229
+ * Represents a registry of executable tools with per-call error isolation.
209
230
  *
210
231
  * @remarks
211
232
  * Tools are keyed by name in insertion order. Adding an existing name overwrites its
212
- * value without changing its position. Every call resolves to a {@link ToolResult};
213
- * missing tools and thrown handlers become error results. Batch execution preserves
214
- * input order and isolates each call.
233
+ * value without changing its position. Every call whose members are plain values
234
+ * resolves to a {@link ToolResult}; missing tools and thrown handlers become error
235
+ * results, and a call whose `id` or `name` accessor throws when read makes `execute`
236
+ * reject instead. Batch execution preserves input order.
215
237
  */
216
238
  export declare interface ToolManagerInterface {
217
- /** The number of registered tools. */
239
+ /** Reports how many tools are registered. */
218
240
  readonly count: number;
219
241
  /**
220
- * Register one tool.
242
+ * Registers one tool.
221
243
  *
222
244
  * @param tool - The tool to register
223
245
  * @returns Nothing
224
246
  */
225
247
  add(tool: ToolInterface): void;
226
248
  /**
227
- * Register a batch of tools.
249
+ * Registers a batch of tools.
228
250
  *
229
251
  * @param tools - The tools to register
230
252
  * @returns Nothing
231
253
  */
232
254
  add(tools: readonly ToolInterface[]): void;
233
255
  /**
234
- * Find one registered tool by name.
256
+ * Finds one registered tool by name.
235
257
  *
236
258
  * @param name - The registered tool name
237
- * @returns The tool when found, otherwise `undefined`
259
+ * @returns The exact registered instance when found, otherwise `undefined`
238
260
  */
239
261
  tool(name: string): ToolInterface | undefined;
240
262
  /**
241
- * List the registered tools in insertion order.
263
+ * Lists the registered tools in insertion order.
242
264
  *
243
265
  * @returns A new readonly array of registered tools
244
266
  */
245
267
  tools(): readonly ToolInterface[];
246
268
  /**
247
- * List the definitions advertised to a caller.
269
+ * Lists the definitions advertised to a caller.
248
270
  *
271
+ * @remarks
249
272
  * The projected `description` is the tool's `summary` when one was authored,
250
273
  * advertised in place of the full description. The full text stays on the tool
251
274
  * for direct lookup.
@@ -254,35 +277,35 @@ export declare interface ToolManagerInterface {
254
277
  */
255
278
  definitions(): readonly ToolDefinition[];
256
279
  /**
257
- * Execute one call with error isolation.
280
+ * Executes one call with error isolation.
258
281
  *
259
282
  * @param call - The tool call to execute, including optional caller context
260
283
  * @returns The correlated result
261
284
  */
262
285
  execute(call: ToolCall): Promise<ToolResult>;
263
286
  /**
264
- * Execute a batch of calls with per-call error isolation.
287
+ * Executes a batch of calls with per-call error isolation.
265
288
  *
266
289
  * @param calls - The tool calls to execute, including optional caller context
267
290
  * @returns The correlated results in input order
268
291
  */
269
292
  execute(calls: readonly ToolCall[]): Promise<readonly ToolResult[]>;
270
293
  /**
271
- * Remove one registered tool.
294
+ * Removes one registered tool.
272
295
  *
273
296
  * @param name - The tool name to remove
274
- * @returns Whether the tool was present
297
+ * @returns True if the tool was present; false otherwise
275
298
  */
276
299
  remove(name: string): boolean;
277
300
  /**
278
- * Remove a batch of registered tools.
301
+ * Removes a batch of registered tools.
279
302
  *
280
303
  * @param names - The tool names to remove
281
- * @returns Whether any named tool was present
304
+ * @returns True if every named tool was present; false otherwise
282
305
  */
283
306
  remove(names: readonly string[]): boolean;
284
307
  /**
285
- * Remove every registered tool.
308
+ * Removes every registered tool.
286
309
  *
287
310
  * @returns Nothing
288
311
  */
@@ -290,7 +313,7 @@ export declare interface ToolManagerInterface {
290
313
  }
291
314
 
292
315
  /**
293
- * Options for creating an executable tool.
316
+ * Configures an executable tool.
294
317
  *
295
318
  * @remarks
296
319
  * `name` identifies the tool, `description` and `parameters` define what is advertised
@@ -299,38 +322,64 @@ export declare interface ToolManagerInterface {
299
322
  * context. This package forwards that context without verification.
300
323
  */
301
324
  export declare interface ToolOptions {
302
- /** The name a caller uses to select the tool. */
325
+ /** Identifies the tool a caller selects. */
303
326
  readonly name: string;
304
- /** The full description of the tool's behavior. */
327
+ /** Describes the tool's behavior in full. */
305
328
  readonly description?: string;
306
- /** A concise description to advertise in place of the full description. */
329
+ /** Holds a concise description to advertise in place of the full description. */
307
330
  readonly summary?: string;
308
- /** The JSON Schema for the tool's arguments. */
331
+ /** Holds the JSON Schema for the tool's arguments. */
309
332
  readonly parameters?: Readonly<Record<string, unknown>>;
310
- /** The handler that receives arguments and optional unverified caller context. */
333
+ /** Handles the arguments and optional unverified caller context. */
311
334
  readonly execute: (args: Readonly<Record<string, unknown>>, caller?: unknown) => Promise<unknown> | unknown;
312
335
  }
313
336
 
314
337
  /**
315
- * The outcome of executing a {@link ToolCall}.
338
+ * Represents the outcome of executing a {@link ToolCall}.
316
339
  *
317
340
  * @remarks
318
- * Always a result and never a throw. Narrow on `success`.
341
+ * Always a result and never a throw for a call whose members are plain values. A call
342
+ * whose `id` or `name` accessor throws when read makes `execute` reject instead, because
343
+ * no correlated result can be built without them. Narrow on `success`.
319
344
  */
320
345
  export declare type ToolResult = ToolSuccess | ToolFailure;
321
346
 
322
347
  /**
323
- * The successful outcome of executing a {@link ToolCall}.
348
+ * Reports the successful outcome of executing a {@link ToolCall}.
324
349
  *
325
350
  * @remarks
326
351
  * `value` is whatever the handler returned — including `undefined`, `null`, `0`,
327
352
  * `''`, or `false`. A present value never implies a meaningful one.
328
353
  */
329
354
  export declare interface ToolSuccess extends Success<unknown> {
330
- /** The identifier of the corresponding call. */
355
+ /** Identifies the corresponding call. */
331
356
  readonly id: string;
332
- /** The name of the called tool. */
357
+ /** Identifies the called tool. */
333
358
  readonly name: string;
334
359
  }
335
360
 
361
+ /**
362
+ * Projects a tool onto the plain definition advertised to a caller, advertising an
363
+ * authored `summary` in place of the full description and carrying the parameter schema
364
+ * by reference.
365
+ *
366
+ * @remarks
367
+ * The projection is a fresh object carrying `name`, then `description` only when the
368
+ * tool authored a summary or a description, then `parameters` only when the tool
369
+ * authored a schema. The full `description` stays on the tool for direct lookup, and
370
+ * the definition is never a live handle on the tool's handler.
371
+ *
372
+ * @param tool - The tool to project
373
+ * @returns A fresh definition carrying only the fields the tool authored
374
+ *
375
+ * @example
376
+ * ```ts
377
+ * import { Tool, toolToDefinition } from '@orkestrel/tool'
378
+ *
379
+ * const echo = new Tool({ name: 'echo', summary: 'Echo a value.', execute: (args) => args.value })
380
+ * toolToDefinition(echo) // { name: 'echo', description: 'Echo a value.' }
381
+ * ```
382
+ */
383
+ export declare function toolToDefinition(tool: ToolInterface): ToolDefinition;
384
+
336
385
  export { }
@@ -1,15 +1,47 @@
1
1
  import { attempt, holds, isArray, isRecord, isString } from "@orkestrel/contract";
2
+ //#region src/core/helpers.ts
3
+ /**
4
+ * Projects a tool onto the plain definition advertised to a caller, advertising an
5
+ * authored `summary` in place of the full description and carrying the parameter schema
6
+ * by reference.
7
+ *
8
+ * @remarks
9
+ * The projection is a fresh object carrying `name`, then `description` only when the
10
+ * tool authored a summary or a description, then `parameters` only when the tool
11
+ * authored a schema. The full `description` stays on the tool for direct lookup, and
12
+ * the definition is never a live handle on the tool's handler.
13
+ *
14
+ * @param tool - The tool to project
15
+ * @returns A fresh definition carrying only the fields the tool authored
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * import { Tool, toolToDefinition } from '@orkestrel/tool'
20
+ *
21
+ * const echo = new Tool({ name: 'echo', summary: 'Echo a value.', execute: (args) => args.value })
22
+ * toolToDefinition(echo) // { name: 'echo', description: 'Echo a value.' }
23
+ * ```
24
+ */
25
+ function toolToDefinition(tool) {
26
+ const definition = { name: tool.name };
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;
31
+ }
32
+ //#endregion
2
33
  //#region src/core/validators.ts
3
34
  /**
4
- * Determine whether an unknown value is structurally a {@link ToolCall}.
35
+ * Determines whether an unknown value is structurally a {@link ToolCall}, staying total
36
+ * for malformed and adversarial input.
5
37
  *
6
38
  * @remarks
7
- * This total guard accepts a plain record with string `id` and `name` fields and a
39
+ * The accepted shape is a plain record with string `id` and `name` fields and a
8
40
  * plain-record `arguments` field. Optional caller context remains opaque and is not
9
- * read or verified. Adversarial values return `false`.
41
+ * read or verified.
10
42
  *
11
43
  * @param value - The value to test
12
- * @returns `true` when the value has the complete tool-call shape
44
+ * @returns True if the value has the complete tool-call shape; false otherwise
13
45
  *
14
46
  * @example
15
47
  * ```ts
@@ -25,7 +57,7 @@ function isToolCall(value) {
25
57
  //#endregion
26
58
  //#region src/core/tools/Tool.ts
27
59
  /**
28
- * An executable tool definition bound to a handler.
60
+ * Binds an executable tool definition to a handler.
29
61
  *
30
62
  * @remarks
31
63
  * Schema fields, arguments, and present caller context are forwarded by reference.
@@ -68,14 +100,15 @@ var Tool = class {
68
100
  //#endregion
69
101
  //#region src/core/tools/ToolManager.ts
70
102
  /**
71
- * An insertion-ordered tool registry with per-call error isolation.
103
+ * Represents an insertion-ordered tool registry with per-call error isolation.
72
104
  *
73
105
  * @remarks
74
106
  * A repeated name overwrites the registered tool without changing its insertion
75
107
  * position. Definitions advertise `summary` in place of `description` when present.
76
- * Unknown names and handler throws resolve to error results; batch execution preserves
77
- * input order and never fails as a whole because of an individual call. Optional
78
- * consumer-asserted caller context is forwarded without verification.
108
+ * Unknown names and handler throws resolve to error results; a call whose `id` or `name`
109
+ * accessor throws when read makes its call, and the batch holding it, reject. Batch
110
+ * execution preserves input order and isolates each call whose members are plain
111
+ * values. Optional consumer-asserted caller context is forwarded without verification.
79
112
  *
80
113
  * @example
81
114
  * ```ts
@@ -109,7 +142,7 @@ var ToolManager = class {
109
142
  return [...this.#tools.values()];
110
143
  }
111
144
  definitions() {
112
- return [...this.#tools.values()].map((tool) => this.#definition(tool));
145
+ return [...this.#tools.values()].map((tool) => toolToDefinition(tool));
113
146
  }
114
147
  execute(call) {
115
148
  if (isArray(call)) return Promise.all(call.map((one) => this.#run(one)));
@@ -117,8 +150,8 @@ var ToolManager = class {
117
150
  }
118
151
  remove(names) {
119
152
  if (isArray(names)) {
120
- let removed = false;
121
- for (const name of names) if (this.#tools.delete(name)) removed = true;
153
+ let removed = true;
154
+ for (const name of names) if (!this.#tools.delete(name)) removed = false;
122
155
  return removed;
123
156
  }
124
157
  return this.#tools.delete(names);
@@ -153,30 +186,34 @@ var ToolManager = class {
153
186
  };
154
187
  }
155
188
  }
156
- #definition(tool) {
157
- const definition = { name: tool.name };
158
- const description = tool.summary ?? tool.description;
159
- if (description !== void 0) definition.description = description;
160
- if (tool.parameters !== void 0) definition.parameters = tool.parameters;
161
- return definition;
162
- }
163
189
  };
164
190
  //#endregion
165
191
  //#region src/core/factories.ts
166
192
  /**
167
- * Create an executable tool.
193
+ * Creates an executable tool bound to the supplied handler, returned as a
194
+ * `ToolInterface` so a call site holds the published contract rather than the `Tool`
195
+ * class.
168
196
  *
169
197
  * @param options - The advertised definition and execution handler
170
198
  * @returns A tool bound to the supplied handler
171
199
  *
172
- * @example
200
+ * @example Anatomy of a tool
173
201
  * ```ts
174
202
  * import { createTool } from '@orkestrel/tool'
175
203
  *
176
204
  * const add = createTool({
177
205
  * name: 'add',
178
- * description: 'Add two numbers',
179
- * execute: (args) => Number(args.a) + Number(args.b),
206
+ * description: 'Add two numeric values and return their sum. Both operands are required.',
207
+ * summary: 'Add two numbers.',
208
+ * parameters: {
209
+ * type: 'object',
210
+ * properties: {
211
+ * left: { type: 'number' },
212
+ * right: { type: 'number' },
213
+ * },
214
+ * required: ['left', 'right'],
215
+ * },
216
+ * execute: (args) => Number(args.left) + Number(args.right),
180
217
  * })
181
218
  * ```
182
219
  */
@@ -184,10 +221,11 @@ function createTool(options) {
184
221
  return new Tool(options);
185
222
  }
186
223
  /**
187
- * Create an empty tool registry.
224
+ * Creates an empty registry that advertises definitions and executes calls with
225
+ * per-call error isolation, returned as a `ToolManagerInterface` so a caller holds the
226
+ * published contract rather than the `ToolManager` class.
188
227
  *
189
- * @returns A registry that advertises definitions and executes calls with per-call
190
- * error isolation
228
+ * @returns A registry bound to no tools
191
229
  *
192
230
  * @example
193
231
  * ```ts
@@ -206,6 +244,6 @@ function createToolManager() {
206
244
  return new ToolManager();
207
245
  }
208
246
  //#endregion
209
- export { Tool, ToolManager, createTool, createToolManager, isToolCall };
247
+ export { Tool, ToolManager, createTool, createToolManager, isToolCall, toolToDefinition };
210
248
 
211
249
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["#execute","#tools","#definition","#run"],"sources":["../../../src/core/validators.ts","../../../src/core/tools/Tool.ts","../../../src/core/tools/ToolManager.ts","../../../src/core/factories.ts"],"sourcesContent":["import type { ToolCall } from './types.js'\nimport { holds, isRecord, isString } from '@orkestrel/contract'\n\n/**\n * Determine 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` when the value has the complete tool-call shape\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 * An executable tool definition bound 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'\n\n/**\n * 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; batch execution preserves\n * input order and never fails as a whole because of an individual call. Optional\n * 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) => this.#definition(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 = false\n\t\t\tfor (const name of names) {\n\t\t\t\tif (this.#tools.delete(name)) removed = true\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\t#definition(tool: ToolInterface): ToolDefinition {\n\t\tconst definition: {\n\t\t\tname: string\n\t\t\tdescription?: string\n\t\t\tparameters?: Readonly<Record<string, unknown>>\n\t\t} = {\n\t\t\tname: tool.name,\n\t\t}\n\t\tconst description = tool.summary ?? tool.description\n\t\tif (description !== undefined) definition.description = description\n\t\tif (tool.parameters !== undefined) definition.parameters = tool.parameters\n\t\treturn definition\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 * Create 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 * Create 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":"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"}
1
+ {"version":3,"file":"index.js","names":[],"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, advertising an\n * authored `summary` in place of the full description and carrying the parameter schema\n * by reference.\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. The full `description` stays on the tool for direct lookup, and\n * 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 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}, 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. Optional caller context remains opaque and is not\n * 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 { 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 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 * @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(): ToolManagerInterface {\n\treturn new ToolManager()\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orkestrel/tool",
3
- "version": "0.0.12",
3
+ "version": "0.0.14",
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,40 +45,43 @@
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 --deny-warnings .",
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",
52
52
  "format": "oxfmt --config .oxfmtrc.json --write .",
53
53
  "format:check": "oxfmt --config .oxfmtrc.json --check .",
54
54
  "lint:check": "oxlint --config .oxlintrc.json --deny-warnings .",
55
- "test": "npm run test:src && npm run test:policy && npm run test:config && npm run test:guides",
55
+ "test": "npm run test:src && npm run test:policy && npm run test:config && npm run test:setup && npm run test:guides",
56
56
  "test:src": "vitest run --config vite.config.ts --no-cache --reporter=dot --project src:core",
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": "vitest run --config vite.config.ts --reporter=dot --project 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",
64
- "prepublishOnly": "npm run format:check && npm run lint:check && npm run check && npm run build && npm test"
64
+ "prepublishOnly": "npm run format:check && npm run lint:check && npm run check && npm run build && npm test && npm run test:distribution -- --mode release",
65
+ "test:distribution": "vitest run --config vite.config.ts --no-cache --reporter=dot --project distribution",
66
+ "test:probe": "vitest run --config vite.config.ts --no-cache --reporter=verbose --project probe",
67
+ "test:bench": "vitest bench --config vite.config.ts --no-cache --project probe",
68
+ "prepack": "npm run build",
69
+ "test:setup": "vitest run --config vite.config.ts --no-cache --reporter=dot --project setup"
65
70
  },
66
71
  "dependencies": {
67
- "@orkestrel/contract": "^0.0.13"
72
+ "@orkestrel/contract": "^0.0.17"
68
73
  },
69
74
  "devDependencies": {
70
75
  "@microsoft/api-extractor": "^7.59.0",
71
- "@orkestrel/guide": "^0.0.12",
72
- "@orkestrel/probe": "^0.0.3",
73
- "@orkestrel/scaffold": "^0.0.49",
74
- "@orkestrel/test": "^0.0.11",
75
- "@types/node": "^26.2.0",
76
- "@vitest/browser-playwright": "^4.1.11",
77
- "oxfmt": "^0.64.0",
78
- "oxlint": "^1.79.0",
76
+ "@orkestrel/guide": "^0.0.17",
77
+ "@orkestrel/probe": "^0.0.12",
78
+ "@orkestrel/scaffold": "^0.0.63",
79
+ "@orkestrel/test": "^0.0.14",
80
+ "@types/node": "^26.4.1",
81
+ "oxfmt": "^0.66.0",
82
+ "oxlint": "^1.81.0",
79
83
  "typescript": "^6.0.3",
80
84
  "vite": "^8.2.2",
81
- "vite-plugin-dts": "^5.0.3",
82
85
  "vitest": "^4.1.11"
83
86
  },
84
87
  "engines": {