@orkestrel/tool 0.0.14 → 0.0.16

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,4 +1,9 @@
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';
1
5
  import type { Failure } from '@orkestrel/contract';
6
+ import type { Fault } from '@orkestrel/contract';
2
7
  import type { Success } from '@orkestrel/contract';
3
8
 
4
9
  /**
@@ -36,6 +41,7 @@ export declare function createTool(options: ToolOptions): ToolInterface;
36
41
  * per-call error isolation, returned as a `ToolManagerInterface` so a caller holds the
37
42
  * published contract rather than the `ToolManager` class.
38
43
  *
44
+ * @param options - The initial registry listeners and listener-error handler
39
45
  * @returns A registry bound to no tools
40
46
  *
41
47
  * @example
@@ -51,7 +57,7 @@ export declare function createTool(options: ToolOptions): ToolInterface;
51
57
  * })
52
58
  * ```
53
59
  */
54
- export declare function createToolManager(): ToolManagerInterface;
60
+ export declare function createToolManager(options?: ToolManagerOptions): ToolManagerInterface;
55
61
 
56
62
  /**
57
63
  * Determines whether an unknown value is structurally a {@link ToolCall}, staying total
@@ -59,8 +65,7 @@ export declare function createToolManager(): ToolManagerInterface;
59
65
  *
60
66
  * @remarks
61
67
  * The accepted shape is a plain record with string `id` and `name` fields and a
62
- * plain-record `arguments` field. Optional caller context remains opaque and is not
63
- * read or verified.
68
+ * plain-record `arguments` field. Extra fields are not read or verified.
64
69
  *
65
70
  * @param value - The value to test
66
71
  * @returns True if the value has the complete tool-call shape; false otherwise
@@ -75,11 +80,30 @@ export declare function createToolManager(): ToolManagerInterface;
75
80
  */
76
81
  export declare function isToolCall(value: unknown): value is ToolCall;
77
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
+
78
99
  /**
79
100
  * Binds an executable tool definition to a handler.
80
101
  *
81
102
  * @remarks
82
- * 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.
83
107
  * Caller context is consumer-asserted and is not verified. Handler failures are not
84
108
  * caught here; {@link ToolManager} owns per-call error isolation.
85
109
  *
@@ -101,11 +125,23 @@ export declare function isToolCall(value: unknown): value is ToolCall;
101
125
  export declare class Tool implements ToolInterface {
102
126
  #private;
103
127
  readonly name: string;
128
+ readonly title?: string;
104
129
  readonly description?: string;
105
130
  readonly summary?: string;
106
131
  readonly parameters?: Readonly<Record<string, unknown>>;
132
+ readonly annotations?: ToolAnnotations;
107
133
  constructor(options: ToolOptions);
108
- 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;
109
145
  }
110
146
 
111
147
  /**
@@ -113,9 +149,8 @@ export declare class Tool implements ToolInterface {
113
149
  *
114
150
  * @remarks
115
151
  * `id` correlates the call with its later {@link ToolResult}. `arguments` is the
116
- * caller-supplied arguments record. `caller` is optional consumer-asserted context:
117
- * this package forwards it without verification, so the tool or its policy layer owns
118
- * every trust decision.
152
+ * caller-supplied arguments record. Execution context travels separately from this
153
+ * JSON call envelope.
119
154
  */
120
155
  export declare interface ToolCall {
121
156
  /** Correlates this call with its result. */
@@ -124,7 +159,13 @@ export declare interface ToolCall {
124
159
  readonly name: string;
125
160
  /** Carries the record the caller supplied. */
126
161
  readonly arguments: Readonly<Record<string, unknown>>;
127
- /** 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. */
128
169
  readonly caller?: unknown;
129
170
  }
130
171
 
@@ -137,10 +178,41 @@ export declare interface ToolCall {
137
178
  export declare interface ToolDefinition {
138
179
  /** Identifies the tool a caller selects. */
139
180
  readonly name: string;
181
+ /** Holds a display title for the tool. */
182
+ readonly title?: string;
140
183
  /** Describes the tool's behavior. */
141
184
  readonly description?: string;
142
185
  /** Holds the JSON Schema for the tool's arguments. */
143
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[];
144
216
  }
145
217
 
146
218
  /**
@@ -150,7 +222,7 @@ export declare interface ToolDefinition {
150
222
  * `error` is the failure message: an unknown tool name, an `Error`'s message, or
151
223
  * a String-converted throw. The registry carries no further structure. An
152
224
  * in-process caller needing a typed error calls `tools.tool(name)`, then
153
- * `tool.execute(args)` in its own `try`/`catch`.
225
+ * `tool.execute(args, context)` in its own `try`/`catch`.
154
226
  */
155
227
  export declare interface ToolFailure extends Failure<string> {
156
228
  /** Identifies the corresponding call. */
@@ -170,20 +242,25 @@ export declare interface ToolInterface extends ToolDefinition {
170
242
  /** Holds a concise description to advertise in place of the full description. */
171
243
  readonly summary?: string;
172
244
  /**
173
- * Runs the tool's handler with the caller-supplied arguments and any consumer-asserted
174
- * caller context.
245
+ * Runs the tool's handler with the caller-supplied arguments and execution context.
175
246
  *
176
247
  * @remarks
177
248
  * Failures are not contained here: a synchronous throw propagates and an
178
249
  * 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.
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.
181
258
  *
182
259
  * @param args - The caller-supplied arguments record
183
- * @param caller - Optional consumer-asserted caller context, forwarded without verification
260
+ * @param context - The required signal and optional consumer-asserted caller identity
184
261
  * @returns The tool's synchronous or asynchronous result
185
262
  */
186
- execute(args: Readonly<Record<string, unknown>>, caller?: unknown): Promise<unknown> | unknown;
263
+ execute(args: Readonly<Record<string, unknown>>, context: ToolContext): Promise<unknown> | unknown;
187
264
  }
188
265
 
189
266
  /**
@@ -195,7 +272,13 @@ export declare interface ToolInterface extends ToolDefinition {
195
272
  * Unknown names and handler throws resolve to error results; a call whose `id` or `name`
196
273
  * accessor throws when read makes its call, and the batch holding it, reject. Batch
197
274
  * execution preserves input order and isolates each call whose members are plain
198
- * 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.
199
282
  *
200
283
  * @example
201
284
  * ```ts
@@ -212,19 +295,45 @@ export declare interface ToolInterface extends ToolDefinition {
212
295
  */
213
296
  export declare class ToolManager implements ToolManagerInterface {
214
297
  #private;
298
+ constructor(options?: ToolManagerOptions);
215
299
  get count(): number;
300
+ get emitter(): EmitterInterface<ToolManagerEventMap>;
216
301
  add(tool: ToolInterface): void;
217
302
  add(tools: readonly ToolInterface[]): void;
218
303
  tool(name: string): ToolInterface | undefined;
219
304
  tools(): readonly ToolInterface[];
220
305
  definitions(): readonly ToolDefinition[];
221
- execute(call: ToolCall): Promise<ToolResult>;
222
- 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[]>;
223
308
  remove(name: string): boolean;
224
309
  remove(names: readonly string[]): boolean;
225
310
  clear(): void;
311
+ destroy(): void;
226
312
  }
227
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
+
228
337
  /**
229
338
  * Represents a registry of executable tools with per-call error isolation.
230
339
  *
@@ -234,10 +343,16 @@ export declare class ToolManager implements ToolManagerInterface {
234
343
  * resolves to a {@link ToolResult}; missing tools and thrown handlers become error
235
344
  * results, and a call whose `id` or `name` accessor throws when read makes `execute`
236
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.
237
350
  */
238
351
  export declare interface ToolManagerInterface {
239
352
  /** Reports how many tools are registered. */
240
353
  readonly count: number;
354
+ /** Publishes the registry's `add`, `remove`, and `clear` events. */
355
+ readonly emitter: EmitterInterface<ToolManagerEventMap>;
241
356
  /**
242
357
  * Registers one tool.
243
358
  *
@@ -279,17 +394,19 @@ export declare interface ToolManagerInterface {
279
394
  /**
280
395
  * Executes one call with error isolation.
281
396
  *
282
- * @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
283
399
  * @returns The correlated result
284
400
  */
285
- execute(call: ToolCall): Promise<ToolResult>;
401
+ execute(call: ToolCall, context?: ToolContext): Promise<ToolResult>;
286
402
  /**
287
403
  * Executes a batch of calls with per-call error isolation.
288
404
  *
289
- * @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
290
407
  * @returns The correlated results in input order
291
408
  */
292
- execute(calls: readonly ToolCall[]): Promise<readonly ToolResult[]>;
409
+ execute(calls: readonly ToolCall[], context?: ToolContext): Promise<readonly ToolResult[]>;
293
410
  /**
294
411
  * Removes one registered tool.
295
412
  *
@@ -310,6 +427,27 @@ export declare interface ToolManagerInterface {
310
427
  * @returns Nothing
311
428
  */
312
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;
313
451
  }
314
452
 
315
453
  /**
@@ -318,20 +456,32 @@ export declare interface ToolManagerInterface {
318
456
  * @remarks
319
457
  * `name` identifies the tool, `description` and `parameters` define what is advertised
320
458
  * to a caller, `summary` optionally replaces the advertised description, and `execute`
321
- * handles the caller-supplied arguments record plus optional consumer-asserted caller
322
- * 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`.
323
462
  */
324
463
  export declare interface ToolOptions {
325
464
  /** Identifies the tool a caller selects. */
326
465
  readonly name: string;
466
+ /** Holds a display title for the tool. */
467
+ readonly title?: string;
327
468
  /** Describes the tool's behavior in full. */
328
469
  readonly description?: string;
329
470
  /** Holds a concise description to advertise in place of the full description. */
330
471
  readonly summary?: string;
331
472
  /** Holds the JSON Schema for the tool's arguments. */
332
473
  readonly parameters?: Readonly<Record<string, unknown>>;
333
- /** Handles the arguments and optional unverified caller context. */
334
- 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;
335
485
  }
336
486
 
337
487
  /**
@@ -360,14 +510,13 @@ export declare interface ToolSuccess extends Success<unknown> {
360
510
 
361
511
  /**
362
512
  * 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.
513
+ * authored `summary` in place of the full description and carrying `parameters` and
514
+ * `annotations` by reference.
365
515
  *
366
516
  * @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.
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.
371
520
  *
372
521
  * @param tool - The tool to project
373
522
  * @returns A fresh definition carrying only the fields the tool authored