@orkestrel/tool 0.0.13 → 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
- 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
 
@@ -2,15 +2,15 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  let _orkestrel_contract = require("@orkestrel/contract");
3
3
  //#region src/core/helpers.ts
4
4
  /**
5
- * Projects a tool onto the plain definition advertised to a caller.
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.
6
8
  *
7
9
  * @remarks
8
10
  * The projection is a fresh object carrying `name`, then `description` only when the
9
11
  * tool authored a summary or a description, then `parameters` only when the tool
10
- * authored a schema. An authored `summary` is advertised in place of the full
11
- * `description`, which stays on the tool for direct lookup. The parameter schema is
12
- * copied by reference and never cloned, so the definition is never a live handle on
13
- * the tool's handler.
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
14
  *
15
15
  * @param tool - The tool to project
16
16
  * @returns A fresh definition carrying only the fields the tool authored
@@ -33,12 +33,13 @@ function toolToDefinition(tool) {
33
33
  //#endregion
34
34
  //#region src/core/validators.ts
35
35
  /**
36
- * Determines 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.
37
38
  *
38
39
  * @remarks
39
- * 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
40
41
  * plain-record `arguments` field. Optional caller context remains opaque and is not
41
- * read or verified. Adversarial values return `false`.
42
+ * read or verified.
42
43
  *
43
44
  * @param value - The value to test
44
45
  * @returns True if the value has the complete tool-call shape; false otherwise
@@ -190,19 +191,30 @@ var ToolManager = class {
190
191
  //#endregion
191
192
  //#region src/core/factories.ts
192
193
  /**
193
- * Creates 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.
194
197
  *
195
198
  * @param options - The advertised definition and execution handler
196
199
  * @returns A tool bound to the supplied handler
197
200
  *
198
- * @example
201
+ * @example Anatomy of a tool
199
202
  * ```ts
200
203
  * import { createTool } from '@orkestrel/tool'
201
204
  *
202
205
  * const add = createTool({
203
206
  * name: 'add',
204
- * description: 'Add two numbers',
205
- * 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),
206
218
  * })
207
219
  * ```
208
220
  */
@@ -210,10 +222,11 @@ function createTool(options) {
210
222
  return new Tool(options);
211
223
  }
212
224
  /**
213
- * Creates 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.
214
228
  *
215
- * @returns A registry that advertises definitions and executes calls with per-call
216
- * error isolation
229
+ * @returns A registry bound to no tools
217
230
  *
218
231
  * @example
219
232
  * ```ts
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","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":"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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
- * Creates 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
- * Creates 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,12 +54,13 @@ export declare function createTool(options: ToolOptions): ToolInterface;
42
54
  export declare function createToolManager(): ToolManagerInterface;
43
55
 
44
56
  /**
45
- * Determines 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
66
  * @returns True if the value has the complete tool-call shape; false otherwise
@@ -96,7 +109,7 @@ export declare class Tool implements ToolInterface {
96
109
  }
97
110
 
98
111
  /**
99
- * Describes a 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
@@ -157,7 +170,8 @@ export declare interface ToolInterface extends ToolDefinition {
157
170
  /** Holds a concise description to advertise in place of the full description. */
158
171
  readonly summary?: string;
159
172
  /**
160
- * Runs the tool's handler.
173
+ * Runs the tool's handler with the caller-supplied arguments and any consumer-asserted
174
+ * caller context.
161
175
  *
162
176
  * @remarks
163
177
  * Failures are not contained here: a synchronous throw propagates and an
@@ -219,7 +233,7 @@ export declare class ToolManager implements ToolManagerInterface {
219
233
  * value without changing its position. Every call whose members are plain values
220
234
  * resolves to a {@link ToolResult}; missing tools and thrown handlers become error
221
235
  * results, and a call whose `id` or `name` accessor throws when read makes `execute`
222
- * reject instead. Batch execution preserves input order and isolates each such call.
236
+ * reject instead. Batch execution preserves input order.
223
237
  */
224
238
  export declare interface ToolManagerInterface {
225
239
  /** Reports how many tools are registered. */
@@ -242,7 +256,7 @@ export declare interface ToolManagerInterface {
242
256
  * Finds one registered tool by name.
243
257
  *
244
258
  * @param name - The registered tool name
245
- * @returns The tool when found, otherwise `undefined`
259
+ * @returns The exact registered instance when found, otherwise `undefined`
246
260
  */
247
261
  tool(name: string): ToolInterface | undefined;
248
262
  /**
@@ -254,6 +268,7 @@ export declare interface ToolManagerInterface {
254
268
  /**
255
269
  * Lists the definitions advertised to a caller.
256
270
  *
271
+ * @remarks
257
272
  * The projected `description` is the tool's `summary` when one was authored,
258
273
  * advertised in place of the full description. The full text stays on the tool
259
274
  * for direct lookup.
@@ -344,15 +359,15 @@ export declare interface ToolSuccess extends Success<unknown> {
344
359
  }
345
360
 
346
361
  /**
347
- * Projects a tool onto the plain definition advertised to a caller.
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.
348
365
  *
349
366
  * @remarks
350
367
  * The projection is a fresh object carrying `name`, then `description` only when the
351
368
  * tool authored a summary or a description, then `parameters` only when the tool
352
- * authored a schema. An authored `summary` is advertised in place of the full
353
- * `description`, which stays on the tool for direct lookup. The parameter schema is
354
- * copied by reference and never cloned, so the definition is never a live handle on
355
- * the tool's handler.
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.
356
371
  *
357
372
  * @param tool - The tool to project
358
373
  * @returns A fresh definition carrying only the fields the tool authored
@@ -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
- * Creates 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
- * Creates 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,12 +54,13 @@ export declare function createTool(options: ToolOptions): ToolInterface;
42
54
  export declare function createToolManager(): ToolManagerInterface;
43
55
 
44
56
  /**
45
- * Determines 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
66
  * @returns True if the value has the complete tool-call shape; false otherwise
@@ -96,7 +109,7 @@ export declare class Tool implements ToolInterface {
96
109
  }
97
110
 
98
111
  /**
99
- * Describes a 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
@@ -157,7 +170,8 @@ export declare interface ToolInterface extends ToolDefinition {
157
170
  /** Holds a concise description to advertise in place of the full description. */
158
171
  readonly summary?: string;
159
172
  /**
160
- * Runs the tool's handler.
173
+ * Runs the tool's handler with the caller-supplied arguments and any consumer-asserted
174
+ * caller context.
161
175
  *
162
176
  * @remarks
163
177
  * Failures are not contained here: a synchronous throw propagates and an
@@ -219,7 +233,7 @@ export declare class ToolManager implements ToolManagerInterface {
219
233
  * value without changing its position. Every call whose members are plain values
220
234
  * resolves to a {@link ToolResult}; missing tools and thrown handlers become error
221
235
  * results, and a call whose `id` or `name` accessor throws when read makes `execute`
222
- * reject instead. Batch execution preserves input order and isolates each such call.
236
+ * reject instead. Batch execution preserves input order.
223
237
  */
224
238
  export declare interface ToolManagerInterface {
225
239
  /** Reports how many tools are registered. */
@@ -242,7 +256,7 @@ export declare interface ToolManagerInterface {
242
256
  * Finds one registered tool by name.
243
257
  *
244
258
  * @param name - The registered tool name
245
- * @returns The tool when found, otherwise `undefined`
259
+ * @returns The exact registered instance when found, otherwise `undefined`
246
260
  */
247
261
  tool(name: string): ToolInterface | undefined;
248
262
  /**
@@ -254,6 +268,7 @@ export declare interface ToolManagerInterface {
254
268
  /**
255
269
  * Lists the definitions advertised to a caller.
256
270
  *
271
+ * @remarks
257
272
  * The projected `description` is the tool's `summary` when one was authored,
258
273
  * advertised in place of the full description. The full text stays on the tool
259
274
  * for direct lookup.
@@ -344,15 +359,15 @@ export declare interface ToolSuccess extends Success<unknown> {
344
359
  }
345
360
 
346
361
  /**
347
- * Projects a tool onto the plain definition advertised to a caller.
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.
348
365
  *
349
366
  * @remarks
350
367
  * The projection is a fresh object carrying `name`, then `description` only when the
351
368
  * tool authored a summary or a description, then `parameters` only when the tool
352
- * authored a schema. An authored `summary` is advertised in place of the full
353
- * `description`, which stays on the tool for direct lookup. The parameter schema is
354
- * copied by reference and never cloned, so the definition is never a live handle on
355
- * the tool's handler.
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.
356
371
  *
357
372
  * @param tool - The tool to project
358
373
  * @returns A fresh definition carrying only the fields the tool authored
@@ -1,15 +1,15 @@
1
1
  import { attempt, holds, isArray, isRecord, isString } from "@orkestrel/contract";
2
2
  //#region src/core/helpers.ts
3
3
  /**
4
- * Projects a tool onto the plain definition advertised to a caller.
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.
5
7
  *
6
8
  * @remarks
7
9
  * The projection is a fresh object carrying `name`, then `description` only when the
8
10
  * tool authored a summary or a description, then `parameters` only when the tool
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.
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
13
  *
14
14
  * @param tool - The tool to project
15
15
  * @returns A fresh definition carrying only the fields the tool authored
@@ -32,12 +32,13 @@ function toolToDefinition(tool) {
32
32
  //#endregion
33
33
  //#region src/core/validators.ts
34
34
  /**
35
- * Determines 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.
36
37
  *
37
38
  * @remarks
38
- * 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
39
40
  * plain-record `arguments` field. Optional caller context remains opaque and is not
40
- * read or verified. Adversarial values return `false`.
41
+ * read or verified.
41
42
  *
42
43
  * @param value - The value to test
43
44
  * @returns True if the value has the complete tool-call shape; false otherwise
@@ -189,19 +190,30 @@ var ToolManager = class {
189
190
  //#endregion
190
191
  //#region src/core/factories.ts
191
192
  /**
192
- * Creates 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.
193
196
  *
194
197
  * @param options - The advertised definition and execution handler
195
198
  * @returns A tool bound to the supplied handler
196
199
  *
197
- * @example
200
+ * @example Anatomy of a tool
198
201
  * ```ts
199
202
  * import { createTool } from '@orkestrel/tool'
200
203
  *
201
204
  * const add = createTool({
202
205
  * name: 'add',
203
- * description: 'Add two numbers',
204
- * 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),
205
217
  * })
206
218
  * ```
207
219
  */
@@ -209,10 +221,11 @@ function createTool(options) {
209
221
  return new Tool(options);
210
222
  }
211
223
  /**
212
- * Creates 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.
213
227
  *
214
- * @returns A registry that advertises definitions and executes calls with per-call
215
- * error isolation
228
+ * @returns A registry bound to no tools
216
229
  *
217
230
  * @example
218
231
  * ```ts
@@ -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":"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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.13",
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,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 --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",
@@ -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": "vitest run --config vite.config.ts --no-cache --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",
@@ -69,20 +69,19 @@
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.16"
72
+ "@orkestrel/contract": "^0.0.17"
73
73
  },
74
74
  "devDependencies": {
75
75
  "@microsoft/api-extractor": "^7.59.0",
76
- "@orkestrel/guide": "^0.0.16",
77
- "@orkestrel/probe": "^0.0.11",
78
- "@orkestrel/scaffold": "^0.0.61",
79
- "@orkestrel/test": "^0.0.13",
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
80
  "@types/node": "^26.4.1",
81
81
  "oxfmt": "^0.66.0",
82
82
  "oxlint": "^1.81.0",
83
83
  "typescript": "^6.0.3",
84
84
  "vite": "^8.2.2",
85
- "vite-plugin-dts": "^5.1.0",
86
85
  "vitest": "^4.1.11"
87
86
  },
88
87
  "engines": {