@orkestrel/tool 0.0.13 → 0.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,30 +1,48 @@
1
- import { Failure } from '@orkestrel/contract';
2
- import { Success } from '@orkestrel/contract';
1
+ import type { ContractShape } from '@orkestrel/contract';
2
+ import type { EmitterErrorHandler } from '@orkestrel/emitter';
3
+ import type { EmitterHooks } from '@orkestrel/emitter';
4
+ import type { EmitterInterface } from '@orkestrel/emitter';
5
+ import type { Failure } from '@orkestrel/contract';
6
+ import type { Fault } from '@orkestrel/contract';
7
+ import type { Success } from '@orkestrel/contract';
3
8
 
4
9
  /**
5
- * Creates an executable tool.
10
+ * Creates an executable tool bound to the supplied handler, returned as a
11
+ * `ToolInterface` so a call site holds the published contract rather than the `Tool`
12
+ * class.
6
13
  *
7
14
  * @param options - The advertised definition and execution handler
8
15
  * @returns A tool bound to the supplied handler
9
16
  *
10
- * @example
17
+ * @example Anatomy of a tool
11
18
  * ```ts
12
19
  * import { createTool } from '@orkestrel/tool'
13
20
  *
14
21
  * const add = createTool({
15
22
  * name: 'add',
16
- * description: 'Add two numbers',
17
- * execute: (args) => Number(args.a) + Number(args.b),
23
+ * description: 'Add two numeric values and return their sum. Both operands are required.',
24
+ * summary: 'Add two numbers.',
25
+ * parameters: {
26
+ * type: 'object',
27
+ * properties: {
28
+ * left: { type: 'number' },
29
+ * right: { type: 'number' },
30
+ * },
31
+ * required: ['left', 'right'],
32
+ * },
33
+ * execute: (args) => Number(args.left) + Number(args.right),
18
34
  * })
19
35
  * ```
20
36
  */
21
37
  export declare function createTool(options: ToolOptions): ToolInterface;
22
38
 
23
39
  /**
24
- * Creates an empty tool registry.
40
+ * Creates an empty registry that advertises definitions and executes calls with
41
+ * per-call error isolation, returned as a `ToolManagerInterface` so a caller holds the
42
+ * published contract rather than the `ToolManager` class.
25
43
  *
26
- * @returns A registry that advertises definitions and executes calls with per-call
27
- * error isolation
44
+ * @param options - The initial registry listeners and listener-error handler
45
+ * @returns A registry bound to no tools
28
46
  *
29
47
  * @example
30
48
  * ```ts
@@ -39,15 +57,15 @@ export declare function createTool(options: ToolOptions): ToolInterface;
39
57
  * })
40
58
  * ```
41
59
  */
42
- export declare function createToolManager(): ToolManagerInterface;
60
+ export declare function createToolManager(options?: ToolManagerOptions): ToolManagerInterface;
43
61
 
44
62
  /**
45
- * Determines whether an unknown value is structurally a {@link ToolCall}.
63
+ * Determines whether an unknown value is structurally a {@link ToolCall}, staying total
64
+ * for malformed and adversarial input.
46
65
  *
47
66
  * @remarks
48
- * This total guard accepts a plain record with string `id` and `name` fields and a
49
- * plain-record `arguments` field. Optional caller context remains opaque and is not
50
- * read or verified. Adversarial values return `false`.
67
+ * The accepted shape is a plain record with string `id` and `name` fields and a
68
+ * plain-record `arguments` field. Extra fields are not read or verified.
51
69
  *
52
70
  * @param value - The value to test
53
71
  * @returns True if the value has the complete tool-call shape; false otherwise
@@ -62,11 +80,30 @@ export declare function createToolManager(): ToolManagerInterface;
62
80
  */
63
81
  export declare function isToolCall(value: unknown): value is ToolCall;
64
82
 
83
+ /**
84
+ * Checks whether a value is a tool error, containing hostile prototype access.
85
+ *
86
+ * @param value - The value to test
87
+ * @returns True if the value is an instance of the tool error class; false otherwise
88
+ *
89
+ * @example
90
+ * ```ts
91
+ * import { ToolError, isToolError } from '@orkestrel/tool'
92
+ *
93
+ * isToolError(new ToolError('ARGUMENTS', 'Invalid amount')) // true
94
+ * isToolError(new Error('Unrelated')) // false
95
+ * ```
96
+ */
97
+ export declare function isToolError(value: unknown): value is ToolError;
98
+
65
99
  /**
66
100
  * Binds an executable tool definition to a handler.
67
101
  *
68
102
  * @remarks
69
- * Schema fields, arguments, and present caller context are forwarded by reference.
103
+ * Advertised fields and execution context are forwarded by reference.
104
+ * A contract derives parameters at construction and refuses parse faults before the
105
+ * handler runs, then forwards the parsed arguments; without a contract, arguments
106
+ * retain their identity. Supplying a contract and parameters throws a schema conflict.
70
107
  * Caller context is consumer-asserted and is not verified. Handler failures are not
71
108
  * caught here; {@link ToolManager} owns per-call error isolation.
72
109
  *
@@ -88,21 +125,32 @@ export declare function isToolCall(value: unknown): value is ToolCall;
88
125
  export declare class Tool implements ToolInterface {
89
126
  #private;
90
127
  readonly name: string;
128
+ readonly title?: string;
91
129
  readonly description?: string;
92
130
  readonly summary?: string;
93
131
  readonly parameters?: Readonly<Record<string, unknown>>;
132
+ readonly annotations?: ToolAnnotations;
94
133
  constructor(options: ToolOptions);
95
- execute(args: Readonly<Record<string, unknown>>, caller?: unknown): Promise<unknown> | unknown;
134
+ execute(args: Readonly<Record<string, unknown>>, context: ToolContext): Promise<unknown> | unknown;
135
+ }
136
+
137
+ /** Describes the observable effects and content of a tool. */
138
+ export declare interface ToolAnnotations {
139
+ /** Reports that the tool changes no state its caller can observe. */
140
+ readonly pure?: boolean;
141
+ /** Reports that the tool's value can carry content the tool did not author. */
142
+ readonly untrusted?: boolean;
143
+ /** Reports that running the tool has a consequence a caller must confirm. */
144
+ readonly consequential?: boolean;
96
145
  }
97
146
 
98
147
  /**
99
- * Describes a call issued by a caller.
148
+ * Describes one request to run a named tool.
100
149
  *
101
150
  * @remarks
102
151
  * `id` correlates the call with its later {@link ToolResult}. `arguments` is the
103
- * caller-supplied arguments record. `caller` is optional consumer-asserted context:
104
- * this package forwards it without verification, so the tool or its policy layer owns
105
- * every trust decision.
152
+ * caller-supplied arguments record. Execution context travels separately from this
153
+ * JSON call envelope.
106
154
  */
107
155
  export declare interface ToolCall {
108
156
  /** Correlates this call with its result. */
@@ -111,7 +159,13 @@ export declare interface ToolCall {
111
159
  readonly name: string;
112
160
  /** Carries the record the caller supplied. */
113
161
  readonly arguments: Readonly<Record<string, unknown>>;
114
- /** Carries consumer-asserted context, forwarded without verification. */
162
+ }
163
+
164
+ /** Carries the signal and consumer-asserted identity for an execution. */
165
+ export declare interface ToolContext {
166
+ /** Aborts when the caller stops waiting for this call. */
167
+ readonly signal: AbortSignal;
168
+ /** Carries consumer-asserted caller identity, forwarded without verification. */
115
169
  readonly caller?: unknown;
116
170
  }
117
171
 
@@ -124,10 +178,41 @@ export declare interface ToolCall {
124
178
  export declare interface ToolDefinition {
125
179
  /** Identifies the tool a caller selects. */
126
180
  readonly name: string;
181
+ /** Holds a display title for the tool. */
182
+ readonly title?: string;
127
183
  /** Describes the tool's behavior. */
128
184
  readonly description?: string;
129
185
  /** Holds the JSON Schema for the tool's arguments. */
130
186
  readonly parameters?: Readonly<Record<string, unknown>>;
187
+ /** Describes the tool's observable effects and content. */
188
+ readonly annotations?: ToolAnnotations;
189
+ }
190
+
191
+ /**
192
+ * Reports a schema conflict or argument validation failure with a machine-readable code.
193
+ *
194
+ * @example
195
+ * ```ts
196
+ * import { ToolError } from '@orkestrel/tool'
197
+ *
198
+ * const error = new ToolError('SCHEMA', 'Choose contract or parameters')
199
+ * error.code // 'SCHEMA'
200
+ * ```
201
+ */
202
+ export declare class ToolError extends Error {
203
+ readonly name: "ToolError";
204
+ readonly code: ToolErrorCode;
205
+ readonly context?: ToolErrorContext;
206
+ constructor(code: ToolErrorCode, message: string, context?: ToolErrorContext);
207
+ }
208
+
209
+ /** Identifies a schema conflict or an argument validation failure. */
210
+ export declare type ToolErrorCode = 'SCHEMA' | 'ARGUMENTS';
211
+
212
+ /** Carries the structured faults behind an argument validation failure. */
213
+ export declare interface ToolErrorContext {
214
+ /** Holds the contract's full parse-fault report. */
215
+ readonly faults?: readonly Fault[];
131
216
  }
132
217
 
133
218
  /**
@@ -137,7 +222,7 @@ export declare interface ToolDefinition {
137
222
  * `error` is the failure message: an unknown tool name, an `Error`'s message, or
138
223
  * a String-converted throw. The registry carries no further structure. An
139
224
  * in-process caller needing a typed error calls `tools.tool(name)`, then
140
- * `tool.execute(args)` in its own `try`/`catch`.
225
+ * `tool.execute(args, context)` in its own `try`/`catch`.
141
226
  */
142
227
  export declare interface ToolFailure extends Failure<string> {
143
228
  /** Identifies the corresponding call. */
@@ -157,19 +242,25 @@ export declare interface ToolInterface extends ToolDefinition {
157
242
  /** Holds a concise description to advertise in place of the full description. */
158
243
  readonly summary?: string;
159
244
  /**
160
- * Runs the tool's handler.
245
+ * Runs the tool's handler with the caller-supplied arguments and execution context.
161
246
  *
162
247
  * @remarks
163
248
  * Failures are not contained here: a synchronous throw propagates and an
164
249
  * asynchronous rejection rejects. {@link ToolManagerInterface.execute} is where a
165
- * call becomes a result. The registry omits `caller` from the invocation when the
166
- * call carries none, so a handler reading its own arity sees one argument.
250
+ * call becomes a result. A configured contract refuses arguments with parse faults
251
+ * before the handler runs, then forwards `contract.parse(args)`, an owned,
252
+ * normalized copy in the schema's types with undeclared keys dropped; without a
253
+ * contract, the raw record is forwarded unchanged.
254
+ * Caller identity is forwarded without verification.
255
+ * The second parameter is the execution context; caller identity is `context.caller`.
256
+ * `Tool.execute` refuses nothing on an aborted signal; the manager checks the signal
257
+ * before entry, and a direct caller who passes an aborted signal gets a handler that observes it.
167
258
  *
168
259
  * @param args - The caller-supplied arguments record
169
- * @param caller - Optional consumer-asserted caller context, forwarded without verification
260
+ * @param context - The required signal and optional consumer-asserted caller identity
170
261
  * @returns The tool's synchronous or asynchronous result
171
262
  */
172
- execute(args: Readonly<Record<string, unknown>>, caller?: unknown): Promise<unknown> | unknown;
263
+ execute(args: Readonly<Record<string, unknown>>, context: ToolContext): Promise<unknown> | unknown;
173
264
  }
174
265
 
175
266
  /**
@@ -181,7 +272,13 @@ export declare interface ToolInterface extends ToolDefinition {
181
272
  * Unknown names and handler throws resolve to error results; a call whose `id` or `name`
182
273
  * accessor throws when read makes its call, and the batch holding it, reject. Batch
183
274
  * execution preserves input order and isolates each call whose members are plain
184
- * values. Optional consumer-asserted caller context is forwarded without verification.
275
+ * values. Execution context is shared across a batch and forwarded unchanged. An
276
+ * omitted context receives a non-aborted signal. A signal aborted before handler
277
+ * entry produces an error result; later cancellation is the handler's responsibility.
278
+ * Registry changes publish synchronously. Replacements publish `remove`, then `add`
279
+ * if the map still holds that exact replacement after the removal listeners return.
280
+ * Destruction clears the tools before releasing listeners. A destroyed registry
281
+ * publishes nothing, even when later additions update its tool map.
185
282
  *
186
283
  * @example
187
284
  * ```ts
@@ -198,19 +295,45 @@ export declare interface ToolInterface extends ToolDefinition {
198
295
  */
199
296
  export declare class ToolManager implements ToolManagerInterface {
200
297
  #private;
298
+ constructor(options?: ToolManagerOptions);
201
299
  get count(): number;
300
+ get emitter(): EmitterInterface<ToolManagerEventMap>;
202
301
  add(tool: ToolInterface): void;
203
302
  add(tools: readonly ToolInterface[]): void;
204
303
  tool(name: string): ToolInterface | undefined;
205
304
  tools(): readonly ToolInterface[];
206
305
  definitions(): readonly ToolDefinition[];
207
- execute(call: ToolCall): Promise<ToolResult>;
208
- execute(calls: readonly ToolCall[]): Promise<readonly ToolResult[]>;
306
+ execute(call: ToolCall, context?: ToolContext): Promise<ToolResult>;
307
+ execute(calls: readonly ToolCall[], context?: ToolContext): Promise<readonly ToolResult[]>;
209
308
  remove(name: string): boolean;
210
309
  remove(names: readonly string[]): boolean;
211
310
  clear(): void;
311
+ destroy(): void;
212
312
  }
213
313
 
314
+ /**
315
+ * Names the events a tool registry publishes.
316
+ *
317
+ * @remarks
318
+ * Each event describes the registry at the moment it is published. A listener that
319
+ * mutates the registry re-enters synchronously; its events publish before the outer
320
+ * call resumes.
321
+ */
322
+ export declare type ToolManagerEventMap = {
323
+ /** Fires after a tool is registered, with the registered instance. */
324
+ readonly add: readonly [tool: ToolInterface];
325
+ /**
326
+ * Fires after a tool is removed, with the removed instance.
327
+ *
328
+ * @remarks
329
+ * A replacement publishes this event with the replacement already installed.
330
+ * A listener must not read absence from the map to confirm a removal.
331
+ */
332
+ readonly remove: readonly [tool: ToolInterface];
333
+ /** Fires once per `clear`, with the tools it removed in registration order. */
334
+ readonly clear: readonly [tools: readonly ToolInterface[]];
335
+ };
336
+
214
337
  /**
215
338
  * Represents a registry of executable tools with per-call error isolation.
216
339
  *
@@ -219,11 +342,17 @@ export declare class ToolManager implements ToolManagerInterface {
219
342
  * value without changing its position. Every call whose members are plain values
220
343
  * resolves to a {@link ToolResult}; missing tools and thrown handlers become error
221
344
  * 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.
345
+ * reject instead. Batch execution preserves input order.
346
+ * Registry changes publish synchronously through the owned emitter. A replacement
347
+ * publishes `remove` for the previous instance, then `add` if the map still holds
348
+ * that exact replacement after the removal listeners return.
349
+ * A destroyed registry publishes nothing; later additions still update its tool map.
223
350
  */
224
351
  export declare interface ToolManagerInterface {
225
352
  /** Reports how many tools are registered. */
226
353
  readonly count: number;
354
+ /** Publishes the registry's `add`, `remove`, and `clear` events. */
355
+ readonly emitter: EmitterInterface<ToolManagerEventMap>;
227
356
  /**
228
357
  * Registers one tool.
229
358
  *
@@ -242,7 +371,7 @@ export declare interface ToolManagerInterface {
242
371
  * Finds one registered tool by name.
243
372
  *
244
373
  * @param name - The registered tool name
245
- * @returns The tool when found, otherwise `undefined`
374
+ * @returns The exact registered instance when found, otherwise `undefined`
246
375
  */
247
376
  tool(name: string): ToolInterface | undefined;
248
377
  /**
@@ -254,6 +383,7 @@ export declare interface ToolManagerInterface {
254
383
  /**
255
384
  * Lists the definitions advertised to a caller.
256
385
  *
386
+ * @remarks
257
387
  * The projected `description` is the tool's `summary` when one was authored,
258
388
  * advertised in place of the full description. The full text stays on the tool
259
389
  * for direct lookup.
@@ -264,17 +394,19 @@ export declare interface ToolManagerInterface {
264
394
  /**
265
395
  * Executes one call with error isolation.
266
396
  *
267
- * @param call - The tool call to execute, including optional caller context
397
+ * @param call - The tool call to execute
398
+ * @param context - The execution context; omission creates a non-aborted signal
268
399
  * @returns The correlated result
269
400
  */
270
- execute(call: ToolCall): Promise<ToolResult>;
401
+ execute(call: ToolCall, context?: ToolContext): Promise<ToolResult>;
271
402
  /**
272
403
  * Executes a batch of calls with per-call error isolation.
273
404
  *
274
- * @param calls - The tool calls to execute, including optional caller context
405
+ * @param calls - The tool calls to execute
406
+ * @param context - The shared execution context; omission creates a non-aborted signal
275
407
  * @returns The correlated results in input order
276
408
  */
277
- execute(calls: readonly ToolCall[]): Promise<readonly ToolResult[]>;
409
+ execute(calls: readonly ToolCall[], context?: ToolContext): Promise<readonly ToolResult[]>;
278
410
  /**
279
411
  * Removes one registered tool.
280
412
  *
@@ -295,6 +427,27 @@ export declare interface ToolManagerInterface {
295
427
  * @returns Nothing
296
428
  */
297
429
  clear(): void;
430
+ /**
431
+ * Removes every tool and releases the emitter's listeners.
432
+ *
433
+ * @remarks
434
+ * Publishes `clear` before destroying the emitter. A destroyed registry publishes
435
+ * nothing, including when a later `add` updates its tool map.
436
+ * Returns with an empty registry even if a `clear` listener adds a tool.
437
+ * An emission already underway delivers to its remaining snapshotted listeners,
438
+ * even when a listener destroys the registry before its siblings run.
439
+ *
440
+ * @returns Nothing
441
+ */
442
+ destroy(): void;
443
+ }
444
+
445
+ /** Configures a tool registry's initial listeners and error handling. */
446
+ export declare interface ToolManagerOptions {
447
+ /** Registers the initial listeners for registry changes. */
448
+ readonly on?: EmitterHooks<ToolManagerEventMap>;
449
+ /** Receives listener throws with the event name, without interrupting sibling listeners. */
450
+ readonly error?: EmitterErrorHandler;
298
451
  }
299
452
 
300
453
  /**
@@ -303,20 +456,32 @@ export declare interface ToolManagerInterface {
303
456
  * @remarks
304
457
  * `name` identifies the tool, `description` and `parameters` define what is advertised
305
458
  * to a caller, `summary` optionally replaces the advertised description, and `execute`
306
- * handles the caller-supplied arguments record plus optional consumer-asserted caller
307
- * context. This package forwards that context without verification.
459
+ * handles the caller-supplied arguments record and execution context. `contract`
460
+ * derives the advertised parameters and checks arguments with `explain`; supplying
461
+ * `parameters` alongside `contract` throws a `ToolError` with code `SCHEMA`.
308
462
  */
309
463
  export declare interface ToolOptions {
310
464
  /** Identifies the tool a caller selects. */
311
465
  readonly name: string;
466
+ /** Holds a display title for the tool. */
467
+ readonly title?: string;
312
468
  /** Describes the tool's behavior in full. */
313
469
  readonly description?: string;
314
470
  /** Holds a concise description to advertise in place of the full description. */
315
471
  readonly summary?: string;
316
472
  /** Holds the JSON Schema for the tool's arguments. */
317
473
  readonly parameters?: Readonly<Record<string, unknown>>;
318
- /** Handles the arguments and optional unverified caller context. */
319
- readonly execute: (args: Readonly<Record<string, unknown>>, caller?: unknown) => Promise<unknown> | unknown;
474
+ /** Derives parameters and validates arguments before execution. */
475
+ readonly contract?: ContractShape;
476
+ /** Describes the tool's observable effects and content. */
477
+ readonly annotations?: ToolAnnotations;
478
+ /**
479
+ * Handles the arguments and required execution context.
480
+ *
481
+ * @remarks
482
+ * The second parameter is the execution context; caller identity is `context.caller`.
483
+ */
484
+ readonly execute: (args: Readonly<Record<string, unknown>>, context: ToolContext) => Promise<unknown> | unknown;
320
485
  }
321
486
 
322
487
  /**
@@ -344,15 +509,14 @@ export declare interface ToolSuccess extends Success<unknown> {
344
509
  }
345
510
 
346
511
  /**
347
- * Projects a tool onto the plain definition advertised to a caller.
512
+ * Projects a tool onto the plain definition advertised to a caller, advertising an
513
+ * authored `summary` in place of the full description and carrying `parameters` and
514
+ * `annotations` by reference.
348
515
  *
349
516
  * @remarks
350
- * The projection is a fresh object carrying `name`, then `description` only when the
351
- * 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.
517
+ * The projection carries `name` and present `title`, `description`, `parameters`, and
518
+ * `annotations` fields in that order. The full `description` stays on the tool for
519
+ * direct lookup, and the definition is never a live handle on the tool's handler.
356
520
  *
357
521
  * @param tool - The tool to project
358
522
  * @returns A fresh definition carrying only the fields the tool authored