@qilin-agent/tools 0.0.0-stage → 3.1.3

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.
@@ -0,0 +1,844 @@
1
+ /**
2
+ * Tool registry, model presentation modes, and pre/guard/around/post/result
3
+ * execution pipeline.
4
+ * @module @qilin-agent/tools
5
+ */
6
+ import { Context, Service } from '@qilin-agent/kylin';
7
+ import z from '@qilin-agent/schemastery';
8
+ import type { ScopeKey, Scoped } from '@qilin-agent/scope';
9
+ import type { ToolCallId, ContentBlock, ToolSchema } from '@qilin-agent/llm';
10
+ import { HarnessError } from '@qilin-agent/llm';
11
+ import type { Agent } from '@qilin-agent/agent';
12
+ import type { UserMessage } from '@qilin-agent/session';
13
+ import { type JsonValue } from '@qilin-agent/util-values';
14
+ import type { ToolCallView, ToolResultView } from './presentation.ts';
15
+ import type { JsonSchemaNode } from './json-schema.ts';
16
+ declare module '@qilin-agent/llm' {
17
+ interface MessageSourceMap {
18
+ /** Tool availability changes supplied by the tool registry. */
19
+ 'tool-registry': {
20
+ kind: 'tool-registry';
21
+ };
22
+ }
23
+ }
24
+ export { defineTool, valueSchemaSpecToJsonSchema, parameterSchemaSpecToJsonSchema, validateArgs, ToolArgsError, type ValueSchemaAnnotations, type StringValueSchemaSpec, type NumberValueSchemaSpec, type IntegerValueSchemaSpec, type BooleanValueSchemaSpec, type NullValueSchemaSpec, type ArrayValueSchemaSpec, type ObjectValueSchemaSpec, type JsonValueSchemaSpec, type OneOfValueSchemaSpec, type ValueSchemaSpec, type ParameterPropertySpec, type ParameterSchemaSpec, type ParameterJsonSchema, type InferValue, type InferArgs, type DefineToolOptions, } from './schema.ts';
25
+ export { assertSupportedJsonSchema, assertObjectJsonSchema, validateJsonSchemaValue, JsonSchemaError, type JsonSchemaNode, type ObjectJsonSchema, type JsonSchemaType, type JsonSchemaScalar, } from './json-schema.ts';
26
+ export type { PtcDispatchEventData, PtcDispatchStartEventData } from './types.ts';
27
+ export { CodeRunFailedError, RUN_CODE_NAME } from './ptc.ts';
28
+ export { jsonSchemaToTs, renderToolsSdk } from './ts-types.ts';
29
+ export { jsonSchemaToPy, renderToolsSdkPy } from './py-types.ts';
30
+ export { defineContentToolFixture, type ContentToolFixtureOptions } from './testing.ts';
31
+ export type { ToolCallKind, FileLocation, FileDiff, ReadFileLine, ToolCallView, GenericCallView, TerminalCallView, DiffCallView, ToolResultView, GenericResultView, TerminalResultView, DiffResultView, SearchResultView, SearchMatchesResultView, SearchPathsResultView, SearchFileMatches, SearchLineMatch, ReadResultView, WebResultView, WebSearchResultView, WebFetchResultView, WebSource, } from './presentation.ts';
32
+ declare module '@qilin-agent/kylin' {
33
+ interface Context {
34
+ tools: ToolRuntime;
35
+ }
36
+ interface Events {
37
+ /**
38
+ * Allow, deny, cancel, or ask before dispatch. `next()` delegates to allow;
39
+ * `cancel` selects the canonical pre-dispatch cancellation result, and missing
40
+ * approval support turns `ask` into denial. Async gates must observe
41
+ * `exec.signal`; the registry rechecks cancellation after they settle but
42
+ * never abandons their promise.
43
+ * Scope-filtered dispatch (`@qilin-agent/scope`): agent-scoped listeners receive only that agent's calls.
44
+ * @param exec - the pending call (name, parsed arguments, caller agent).
45
+ * @mode waterfall
46
+ */
47
+ 'tools/pre-execute'(this: Scoped<ToolRuntime>, exec: ToolExecution, next: () => Promise<PreToolDecision>): Promise<PreToolDecision>;
48
+ /**
49
+ * Around-dispatch waterfall for timeout, retry, or metrics. `next()` returns
50
+ * a normalized result; wrappers may change only `exec.signal`, while call
51
+ * identity remains immutable. The registry re-fuses the original caller
52
+ * signal before the body, so replacement cannot detach caller cancellation;
53
+ * wrappers must still restore their signal and reach quiescence.
54
+ * Scope-filtered dispatch (`@qilin-agent/scope`): agent-scoped listeners receive only that agent's calls.
55
+ * @param exec - the allowed call about to dispatch (name, parsed arguments, caller agent, signal).
56
+ * @mode waterfall
57
+ */
58
+ 'tools/execute'(this: Scoped<ToolRuntime>, exec: ToolDispatchExecution, next: () => Promise<ToolExecutionResult>): Promise<ToolExecutionResult>;
59
+ /**
60
+ * Accept, replace, enrich, or block a normalized dispatch result. `next()`
61
+ * accepts it unchanged; thrown tools still reach this waterfall as errors. Async
62
+ * listeners must observe `exec.signal`; after they settle, caller
63
+ * cancellation replaces only a successful accepted outcome with the code
64
+ * selected by whether the tool body was invoked.
65
+ * Scope-filtered dispatch (`@qilin-agent/scope`): agent-scoped listeners receive only that agent's calls.
66
+ * @param exec - the call that just ran (name, parsed arguments, caller agent).
67
+ * @param result - the dispatch outcome a listener may accept, replace, or block.
68
+ * @mode waterfall
69
+ */
70
+ 'tools/post-execute'(this: Scoped<ToolRuntime>, exec: ToolExecution, result: Readonly<ToolExecutionResult>, next: () => Promise<PostToolDecision>): Promise<PostToolDecision>;
71
+ /**
72
+ * Allow a listener to replace content in the DURABLE LOG COPY of one
73
+ * `run_code` sub-dispatch outcome before the bridge appends its
74
+ * `tool/ptc-dispatch` event. `next()` keeps the
75
+ * content unchanged; a listener may return replacement blocks (e.g. the
76
+ * spill policy's preview + locator for an oversized text result). Only the
77
+ * logged copy is affected — the program already received the complete
78
+ * value, and the model sees neither. A throwing listener is contained:
79
+ * the bridge falls back to logging the original settled content.
80
+ * Scope-filtered dispatch (`@qilin-agent/scope`): agent-scoped listeners receive only that agent's dispatches.
81
+ * @param dispatch - the parent execution, sub-call identity, and the settled content to log.
82
+ * @mode waterfall
83
+ */
84
+ 'tools/ptc-dispatch-log'(this: Scoped<ToolRuntime>, dispatch: PtcDispatchLog, next: () => Promise<ContentBlock[]>): Promise<ContentBlock[]>;
85
+ /**
86
+ * Observe the frozen, lossless-JSON final outcome. Listener failures are contained.
87
+ * Scope-filtered dispatch (`@qilin-agent/scope`): keyed by `exec.agent`.
88
+ * @param exec - the execution object that traversed the pipeline.
89
+ * @param result - a deep-frozen snapshot of the final returned result.
90
+ * @mode emit
91
+ */
92
+ 'tools/result'(this: Scoped<ToolRuntime>, exec: Readonly<ToolExecution>, result: Readonly<ToolExecutionResult>): undefined;
93
+ /**
94
+ * A tool was registered or unregistered, or a scoped restriction changed
95
+ * (the available tool set changed — possibly for one scope only). An
96
+ * UNFILTERED registry-subject notification, deliberately not scope-filtered
97
+ * dispatch: a global change concerns every agent's next assembly, so a
98
+ * scoped listener subscribing here sees every change, not just its own
99
+ * scope's.
100
+ * @mode emit
101
+ */
102
+ 'tools/change'(): void;
103
+ }
104
+ }
105
+ /** Tool-owned canonical output contract used after the body returns a JSON value. */
106
+ export interface ToolOutputDefinition {
107
+ /** Raw supported JSON Schema enforced against every successful canonical value. */
108
+ readonly schema: JsonSchemaNode;
109
+ /** Pure projection from validated arguments and value to Native/model content. */
110
+ render(args: unknown, value: JsonValue): ContentBlock[];
111
+ /** Pure replayable presentation projection, computed only for top-level calls. */
112
+ presentationMeta?(args: unknown, value: JsonValue): JsonValue;
113
+ }
114
+ /** A registered tool: its schema plus the execution function. */
115
+ export interface ToolDefinition extends ToolSchema {
116
+ /** Mandatory canonical output declaration. */
117
+ readonly output: ToolOutputDefinition;
118
+ /**
119
+ * Run one accepted call and return only its canonical lossless-JSON value.
120
+ * Async work must observe or forward `exec.signal` and settle only after its
121
+ * owned work reaches quiescence. The registry preserves caller cancellation
122
+ * through around-dispatch signal replacement and does not abandon this
123
+ * promise, but it cannot hard-kill same-process code.
124
+ * @param args - losslessly snapshotted, frozen model arguments.
125
+ * @param exec - execution identity, cancellation signal, and context deferral.
126
+ * @returns the canonical value declared by `output.schema`.
127
+ */
128
+ execute(args: unknown, exec: ToolRunContext): Promise<unknown>;
129
+ /**
130
+ * Synchronous last-mile transform for model-facing content. The registry
131
+ * snapshots this callback when execution starts and invokes it exactly once
132
+ * for every normalized outcome, including pipeline failures that bypass
133
+ * `tools/post-execute`, immediately before lossless materialization.
134
+ * Returning `undefined` preserves the content; every other result field
135
+ * remains registry-owned. The callback must be total and must not throw.
136
+ * @param exec - immutable execution identity and arguments.
137
+ * @param result - complete normalized outcome before materialization.
138
+ * @returns replacement content, or `undefined` to preserve it.
139
+ */
140
+ finalizeContent?(exec: Readonly<ToolExecution>, result: Readonly<ToolExecutionResult>): ContentBlock[] | undefined;
141
+ /**
142
+ * Cooperative tool-call timeout budget in milliseconds. Omit for no deadline.
143
+ * Enforced by `@qilin-agent/tool-call-timeout-policy` (a `tools/execute` wrapper); it
144
+ * is NEVER sent to the model — `schemas()` whitelists only name/description/
145
+ * parameters. Declaring it asserts this tool forwards `exec.signal` to a
146
+ * cooperative implementation that can reach quiescence when the signal aborts.
147
+ */
148
+ timeoutMs?: number;
149
+ /**
150
+ * Pure synchronous classifier for overlap with sibling tool calls. Only
151
+ * `true` opts in; omission, exceptions, non-`true` returns, and invalid
152
+ * `defineTool` arguments are exclusive. This metadata is never model-visible.
153
+ *
154
+ * Opted-in executions must not mutate parent-owned state. Shared state must
155
+ * tolerate concurrent dispatch; recorder races are permitted only when they
156
+ * commute or fail closed. See the
157
+ * [parallel-tool-call Agent Note](../../../../.agents/notes/implemented/feature/2026-07-10-parallel-tool-call-execution.md)
158
+ * for the full contract.
159
+ * @param args - parsed arguments; `defineTool` validates before calling.
160
+ * @returns Whether this call may join a parallel group.
161
+ */
162
+ isConcurrencySafe?(args: unknown): boolean;
163
+ /**
164
+ * Optional: how to present the PENDING state of one call in a UI, derived from
165
+ * the call's `args` (parsed arguments, `unknown` — the tool validates/narrows
166
+ * its own input). Returns a {@link ToolCallView} (a `card`-tagged render intent),
167
+ * or `undefined` (or omit the method) to fall back to a generic presentation
168
+ * (title = tool name, raw args as input). Pure and side-effect-free: a UI may
169
+ * call it during live streaming AND a session-log replay, so it must depend
170
+ * only on `args`.
171
+ */
172
+ presentCall?(args: unknown): ToolCallView | undefined;
173
+ /**
174
+ * Optional: how to present the COMPLETED state, given the same `args` and the
175
+ * durable result projection (`content`, failure state, and optional `meta`). Returns a
176
+ * {@link ToolResultView}, or `undefined` (or omit the method) to keep the
177
+ * pending title and render the raw result content. Pure and side-effect-free
178
+ * for the same replay reason.
179
+ */
180
+ presentResult?(args: unknown, result: ToolResult): ToolResultView | undefined;
181
+ }
182
+ /** The completed outcome handed to {@link ToolDefinition.presentResult}. */
183
+ export interface ToolResult {
184
+ /** The final model-facing content (or the rendered error text on failure). */
185
+ content: ContentBlock[];
186
+ /** Whether the call failed. */
187
+ isError: boolean;
188
+ /**
189
+ * The tool-private presentation payload projected by its output declaration.
190
+ * It is persisted verbatim on `tool/result` for Host presenters and Client
191
+ * renderers to narrow independently. Absent when the tool declared no
192
+ * projector or the call was nested under a composite transport.
193
+ */
194
+ meta?: JsonValue;
195
+ }
196
+ declare const toolExecutionTokenBrand: unique symbol;
197
+ /** Opaque call identity that permits correlation without exposing mutable execution state. */
198
+ export type ToolExecutionToken = symbol & {
199
+ readonly [toolExecutionTokenBrand]: true;
200
+ };
201
+ /**
202
+ * Caller-supplied description of one tool call. {@link ToolRuntime.execute}
203
+ * adds the registry-owned token to form a pipeline {@link ToolExecution};
204
+ * callers do not choose that token.
205
+ */
206
+ export interface ToolExecutionInput {
207
+ readonly callId: ToolCallId;
208
+ /**
209
+ * Root model-requested call owning this execution tree. Callers omit it for
210
+ * a root execution; nested dispatchers propagate the enclosing value.
211
+ */
212
+ readonly rootCallId?: ToolCallId;
213
+ readonly name: string;
214
+ /** Binding-time tool schema for a PTC inner call; frozen by its producer and never logged. */
215
+ readonly schema?: ToolSchema;
216
+ /** Losslessly JSON-serializable parsed arguments (tools validate their own schema). */
217
+ readonly arguments: unknown;
218
+ /** The agent on whose behalf the call runs (set by the agent loop). */
219
+ readonly agent?: Agent;
220
+ /**
221
+ * Opaque token of the enclosing transport execution, when one exists. PTC
222
+ * mode sets this on SDK sub-dispatches so commit-style observers can wait for
223
+ * the outer `run_code` outcome without receiving its live mutable execution.
224
+ * The token also marks the call as a transport sub-dispatch rather than a
225
+ * model-direct call: under `mode: 'ptc'`, only calls WITH a parent may
226
+ * execute a native tool name — a model-direct call (no parent) is denied as
227
+ * `UNKNOWN_TOOL` before the policy pipeline. See {@link ToolRuntime.execute}.
228
+ */
229
+ readonly parent?: ToolExecutionToken;
230
+ /** Required caller-owned cancellation for this invocation. */
231
+ readonly signal: AbortSignal;
232
+ }
233
+ /**
234
+ * Scheduling mode for one pending call. `parallel` may overlap with siblings;
235
+ * `exclusive` runs alone and forms an ordering barrier.
236
+ */
237
+ export type ToolExecutionMode = {
238
+ kind: 'parallel';
239
+ } | {
240
+ kind: 'exclusive';
241
+ };
242
+ /**
243
+ * One settled `run_code` sub-dispatch about to be logged, as seen by the
244
+ * `tools/ptc-dispatch-log` waterfall: the parent execution (session owner,
245
+ * outer call identity), the sub-call identity, and the outcome whose durable
246
+ * copy a listener may reshape. `content` is the RENDERED result projection
247
+ * (what a native `tool/result` would carry) — the program itself received
248
+ * the structured `value` (or just the error message on failure); only the
249
+ * `tool/ptc-dispatch` event's copy changes.
250
+ */
251
+ export interface PtcDispatchLog {
252
+ /** The outer `run_code` execution. */
253
+ readonly exec: ToolExecution;
254
+ /** The calling agent (the scope routing key and the spill owner), when the outer call has one. */
255
+ readonly agent?: Agent;
256
+ /** Opaque sub-call id; new calls use `<parent>:ptc:<n>`. */
257
+ readonly subCallId: ToolCallId;
258
+ /** The dispatched sub-tool name. */
259
+ readonly name: string;
260
+ /** Whether the sub-call settled as an error. */
261
+ readonly isError: boolean;
262
+ /** The sub-call's complete model-facing content (the settle event's default payload). */
263
+ readonly content: ContentBlock[];
264
+ }
265
+ /**
266
+ * One pending tool call inside the registry pipeline. Parsed arguments cross
267
+ * one lossless-JSON materialization boundary before policy and are deep-frozen;
268
+ * call identity, the caller signal, and the registry-assigned {@link token} are
269
+ * readonly. The registry freezes the complete object before `tools/result`
270
+ * observers run.
271
+ */
272
+ export interface ToolExecution extends ToolExecutionInput {
273
+ /** Root model-requested call, resolved for every root and nested execution. */
274
+ readonly rootCallId: ToolCallId;
275
+ /** Registry-assigned identity shared with nested calls only as their opaque `parent` token. */
276
+ readonly token: ToolExecutionToken;
277
+ }
278
+ /**
279
+ * Around-dispatch view of a {@link ToolExecution}. A `tools/execute` wrapper
280
+ * may replace the signal for its delegated lifetime, but it cannot remove it.
281
+ * The registry fuses every replacement with the captured caller signal.
282
+ */
283
+ export interface ToolDispatchExecution extends Omit<ToolExecution, 'signal'> {
284
+ /** Cancellation signal visible to the next wrapper or tool body. */
285
+ signal: AbortSignal;
286
+ }
287
+ /**
288
+ * Runtime context handed to a tool implementation after the registry has
289
+ * accepted a {@link ToolExecution}. {@link deferContext} attaches context to
290
+ * this execution's own result — a composite tool ferries nested-dispatch
291
+ * context back to the outer result, and a leaf tool may mint a fresh
292
+ * plugin-sourced instruction; the loop appends it only after the
293
+ * `tool/result`.
294
+ */
295
+ export interface ToolRunContext extends ToolExecution {
296
+ /**
297
+ * Defer one context — typically a nested-dispatch context ferried by a
298
+ * composite tool, or a fresh plugin-sourced instruction — until this tool's
299
+ * final result reaches the agent loop. Contexts retain their individual
300
+ * source and metadata and are emitted in call order.
301
+ */
302
+ deferContext(context: UserMessage): void;
303
+ /**
304
+ * Mark a successful final result as terminal for the current agent turn.
305
+ * The marker rides this execution's own result (`concludesTurn` exists only
306
+ * on {@link ToolExecutionSuccess}); a composite that dispatches nested
307
+ * calls forwards it from the nested result, exactly like
308
+ * `additionalContexts`, so only an authoritative nested success can
309
+ * conclude the enclosing run.
310
+ */
311
+ concludeTurn(): void;
312
+ }
313
+ /**
314
+ * Scheduler-only result after ordered pre-execute and guards. A `post-result`
315
+ * still receives post-execute; a `final-result` bypasses it.
316
+ * @internal
317
+ */
318
+ export type ScheduledToolPreparation = {
319
+ kind: 'dispatch';
320
+ exec: ToolRunContext;
321
+ } | {
322
+ kind: 'post-result';
323
+ exec: ToolRunContext;
324
+ result: ToolExecutionResult;
325
+ } | {
326
+ kind: 'final-result';
327
+ exec: ToolRunContext;
328
+ result: ToolExecutionResult;
329
+ };
330
+ /**
331
+ * Scheduler-only dispatch result. A `post-result` still receives post-execute;
332
+ * a `final-result` already matches {@link ToolRuntime.execute} failure semantics.
333
+ * @internal
334
+ */
335
+ export type ScheduledToolDispatch = {
336
+ kind: 'post-result';
337
+ result: ToolExecutionResult;
338
+ } | {
339
+ kind: 'final-result';
340
+ result: ToolExecutionResult;
341
+ };
342
+ /**
343
+ * Symbol-keyed scheduler view that keeps pre/post policy ordered while
344
+ * overlapping dispatch. Ordinary callers use {@link ToolRuntime.execute};
345
+ * this is not a plugin extension point.
346
+ * @internal
347
+ */
348
+ export interface ToolRuntimeScheduler {
349
+ /** Materialize input, run the ordered pre-execute/guard gate, and decide what stage follows. */
350
+ prepare(exec: ToolExecutionInput): Promise<ScheduledToolPreparation>;
351
+ /** Run only the around-dispatch/body stage. */
352
+ dispatch(exec: ToolRunContext): Promise<ScheduledToolDispatch>;
353
+ /** Run post-execute and definition-owned content finalization, then materialize and notify. */
354
+ finalize(exec: ToolRunContext, result: ToolExecutionResult): Promise<ToolExecutionResult>;
355
+ /** Run definition-owned content finalization, then materialize and notify without post-execute. */
356
+ finish(exec: ToolRunContext, result: ToolExecutionResult): ToolExecutionResult;
357
+ }
358
+ /**
359
+ * Scheduler entry point omitted from the generated named service API.
360
+ * The registry symbol is process-global (`Symbol.for`) because src-launch can
361
+ * load this package twice — the plugin entry through the profile resolution
362
+ * and bare-name imports through the built `lib` — and each copy mints its own
363
+ * `Symbol`, so a per-copy symbol makes the scheduler lookup miss.
364
+ * @internal
365
+ */
366
+ export declare const TOOL_RUNTIME_SCHEDULER: unique symbol;
367
+ /** Canonical error code for cancellation after a tool body was invoked. */
368
+ export declare const TOOL_ABORTED = "ABORTED";
369
+ /** Canonical error code for cancellation before a tool body was invoked. */
370
+ export declare const TOOL_ABORTED_BEFORE_DISPATCH = "ABORTED_BEFORE_DISPATCH";
371
+ /** Structured error metadata for a failed tool call (alongside the model-facing text). */
372
+ export interface ToolErrorInfo {
373
+ name: string;
374
+ code: string;
375
+ /** Optional raw user-facing detail; durable projections preserve it but model-facing content does not include it. */
376
+ reason?: string;
377
+ }
378
+ /** Canonical failure detail; internal routing information remains optional. */
379
+ export interface ToolFailure {
380
+ /** Human-readable failure message without the Native `Error: ` envelope. */
381
+ message: string;
382
+ /** Internal error class/code used by policy and durable diagnostics. */
383
+ info?: ToolErrorInfo;
384
+ }
385
+ /**
386
+ * Thrown (internally) when the model requests a tool that isn't registered.
387
+ * Extends {@link HarnessError} (`code: 'UNKNOWN_TOOL'`) so an unknown-tool
388
+ * failure is as routable as a tool-thrown one — retry/sandbox/replay code can
389
+ * distinguish it from a tool body's own error.
390
+ */
391
+ export declare class ToolNotFoundError extends HarnessError {
392
+ /**
393
+ * @param toolName - the name the caller asked for.
394
+ * @param reachableFrom - how the model reaches this tool instead, when the
395
+ * name IS visible and only the presentation denies calling it directly.
396
+ * Omitted for a name that is registered nowhere.
397
+ */
398
+ constructor(toolName: string, reachableFrom?: string);
399
+ }
400
+ /** Thrown when a tool body or post-policy value violates its declared output. */
401
+ export declare class ToolOutputError extends HarnessError {
402
+ /** Schema/value violations in validation order. */
403
+ readonly violations: string[];
404
+ constructor(toolName: string, violations: string[]);
405
+ }
406
+ /** Successful canonical tool execution, including its Native/model projection. */
407
+ export interface ToolExecutionSuccess {
408
+ readonly isError: false;
409
+ /** Execution-local canonical value; deliberately omitted from durable events. */
410
+ readonly value: JsonValue;
411
+ readonly content: ContentBlock[];
412
+ readonly error?: never;
413
+ readonly meta?: JsonValue;
414
+ readonly additionalContexts?: UserMessage[];
415
+ /** The agent loop stops after committing this successful result batch. */
416
+ readonly concludesTurn?: true;
417
+ }
418
+ /** Failed canonical tool execution; failures never carry a successful value. */
419
+ export interface ToolExecutionFailure {
420
+ readonly isError: true;
421
+ readonly error: ToolFailure;
422
+ readonly value?: never;
423
+ readonly content: ContentBlock[];
424
+ readonly meta?: JsonValue;
425
+ readonly additionalContexts?: UserMessage[];
426
+ readonly concludesTurn?: never;
427
+ }
428
+ /** The discriminated, execution-local outcome of one tool call. */
429
+ export type ToolExecutionResult = ToolExecutionSuccess | ToolExecutionFailure;
430
+ /**
431
+ * Pre-dispatch decision. `allow` runs the call; `deny` materializes its
432
+ * model-facing reason and optional structured error identity; `cancel` selects
433
+ * the canonical cancellation result without presenting a policy denial; `ask`
434
+ * runs only after an approval service returns `allowed-once` and otherwise
435
+ * denies; its `reason` is the audited approval reason and its optional
436
+ * `displayReason` is the localized prompt text. Input rewriting is excluded because arguments are already logged and
437
+ * presented.
438
+ */
439
+ export type PreToolDecision = {
440
+ kind: 'allow';
441
+ } | {
442
+ kind: 'deny';
443
+ reason: string;
444
+ info?: ToolErrorInfo;
445
+ } | {
446
+ kind: 'cancel';
447
+ } | {
448
+ kind: 'ask';
449
+ reason?: string;
450
+ displayReason?: {
451
+ readonly en: string;
452
+ readonly [locale: string]: string;
453
+ };
454
+ };
455
+ /**
456
+ * Post-dispatch decision: accept, replace one projection, attach context for the
457
+ * next request, or block by turning corrective feedback into an error result.
458
+ */
459
+ export type PostToolDecision = {
460
+ kind: 'accept';
461
+ content?: ContentBlock[];
462
+ value?: never;
463
+ additionalContexts?: UserMessage[];
464
+ } | {
465
+ kind: 'accept';
466
+ value: JsonValue;
467
+ content?: never;
468
+ additionalContexts?: UserMessage[];
469
+ } | {
470
+ kind: 'block';
471
+ feedback: ContentBlock[];
472
+ additionalContexts?: UserMessage[];
473
+ };
474
+ /** How the registry presents its tools to the model (see {@link Config.mode}). */
475
+ export type ToolPresentationMode = 'native' | 'ptc' | 'both';
476
+ /** Plugin config: how the registered tools are presented to the model. */
477
+ export interface Config {
478
+ /**
479
+ * Model presentation. `native` (default) sends every visible schema; `ptc`
480
+ * sends only `run_code` plus a generated SDK prompt and collapses the
481
+ * executor to the same surface (a model-direct call may only name
482
+ * `run_code`; `run_code` SDK sub-dispatches keep every visible tool); `both`
483
+ * sends both forms. PTC mode requires a `ctx.ptcRuntime` whose `language`
484
+ * has a registered SDK renderer (TypeScript or Python) and fail prompt
485
+ * assembly when it is absent or has no renderer. Under `ptc`, native names
486
+ * in `toolOrder` are invalid.
487
+ */
488
+ mode?: ToolPresentationMode;
489
+ /**
490
+ * Concurrency cap for a `run_code` program's overlapping sub-calls
491
+ * (default 10, the loop scheduler's own default). Sub-calls follow the
492
+ * native scheduling contract — only calls whose tools classify
493
+ * concurrency-safe overlap; exclusive calls form barriers — so `1`
494
+ * restores strictly serial dispatch. Must be a positive integer.
495
+ */
496
+ maxParallelSubCalls?: number;
497
+ }
498
+ /**
499
+ * Per-scope filter over global tools. Restrictions intersect and do not affect
500
+ * scoped registrations or the reserved PTC mode transport.
501
+ */
502
+ export interface ToolRestriction {
503
+ /** Global tool names that stay visible; everything else is removed. */
504
+ readonly allow?: readonly string[];
505
+ /** Global tool names removed from visibility. */
506
+ readonly deny?: readonly string[];
507
+ }
508
+ /**
509
+ * A monotonic execution guard evaluated after every `tools/pre-execute`
510
+ * listener and before the tool body. Returning a reason denies the call;
511
+ * returning `undefined` leaves it unchanged. Because guards have no allow
512
+ * result, listener ordering cannot turn a denial back into permission.
513
+ * @param execution - the identity-protected call after extensible pre-execute policy completed.
514
+ * @returns a final denial reason, or `undefined` to leave the call allowed.
515
+ */
516
+ export type ToolGuard = (execution: Readonly<ToolExecution>) => string | undefined;
517
+ /**
518
+ * Tool registry and execution pipeline. Scoped registrations shadow globals;
519
+ * one visibility resolver feeds presentation, lookup, and dispatch.
520
+ */
521
+ export declare class ToolRuntime extends Service {
522
+ static inject: string[];
523
+ static Config: z<Config>;
524
+ /** Internal staged view consumed by `qilin-agent-loop`'s parallel scheduler. */
525
+ readonly [TOOL_RUNTIME_SCHEDULER]: ToolRuntimeScheduler;
526
+ /** Context deferred by a running tool body, keyed by its scheduler-owned execution. */
527
+ private deferredContexts;
528
+ /** Executions whose tool body declared the current turn complete. */
529
+ private concludingExecutions;
530
+ /** Original caller cancellation, kept outside the wrapper-mutable execution object. */
531
+ private cancellationStates;
532
+ /** Definition-owned final content transform snapshotted before policy begins. */
533
+ private contentFinalizers;
534
+ private readonly layers;
535
+ /** Presentation for scopes that declare none; {@link presentAs} shadows it per scope. */
536
+ private readonly defaultMode;
537
+ private readonly maxParallelSubCalls;
538
+ /**
539
+ * Reserved presentation transport, kept outside the filterable registration
540
+ * layers. Built on first need rather than at construction: which agents run
541
+ * a PTC mode is no longer known when the service is constructed, and the
542
+ * transport is stateless beyond its closures over `this`.
543
+ */
544
+ private ptcTransport;
545
+ constructor(ctx: Context, config?: Config);
546
+ /**
547
+ * The prompt statement of the `ptc` executor collapse, registered wherever
548
+ * {@link sdkSection} is and rendering empty outside an effective `ptc`.
549
+ *
550
+ * Every tool contributes its own guidance section naming its tool, none of
551
+ * them qualify how that tool is reached, and they all render before the SDK.
552
+ * Without this the model reads a catalog of tools it is told to use and no
553
+ * statement that only `run_code` may be called, so it emits a native call,
554
+ * receives `UNKNOWN_TOOL` for a tool the prompt just declared, and concludes
555
+ * the deployment is inconsistent. Its order places the rule before that
556
+ * guidance rather than after it.
557
+ *
558
+ * `both` renders empty: native calls do execute there, so the rule is false.
559
+ * @returns the section registration.
560
+ */
561
+ private collapseSection;
562
+ /**
563
+ * The generated-SDK prompt section, registered globally by a PTC mode
564
+ * deployment and per scope by {@link presentAs}.
565
+ *
566
+ * The body regenerates from the CALLING scope, and renders empty for an
567
+ * agent presenting natively — an agent that opted out under a PTC mode
568
+ * deployment still sees the global registration, and an empty section is
569
+ * dropped from the rendered prompt.
570
+ * @returns the section registration.
571
+ */
572
+ private sdkSection;
573
+ /**
574
+ * The presentation one scope's agent sees: its own declaration, else the
575
+ * deployment default.
576
+ * @param scope - the calling agent, or undefined for the global view.
577
+ * @returns the resolved presentation mode.
578
+ */
579
+ private modeFor;
580
+ /**
581
+ * The reserved `run_code` transport, built on first need.
582
+ *
583
+ * It never enters the global layer: per-agent restrictions must not remove
584
+ * it, and a scoped registration must not shadow it. The visibility resolver
585
+ * appends it after resolving the filterable global/scoped capability layers,
586
+ * and only for scopes whose mode actually presents it.
587
+ * @returns the shared transport definition.
588
+ */
589
+ private requirePtcTransport;
590
+ /**
591
+ * Present the calling scope's tools in `mode` instead of the deployment
592
+ * default. Nearest scope on the chain wins, so a preset's standing
593
+ * declaration covers every agent joined under it.
594
+ *
595
+ * Scoped only, and one declaration per scope: this is how an agent preset
596
+ * composes PTC mode agents beside native ones in the same process, and a
597
+ * process-global override would be the `mode` config field instead.
598
+ * @param mode - the presentation the covered agents' models see.
599
+ * @returns the exact disposer that restores the deployment default.
600
+ */
601
+ presentAs(mode: ToolPresentationMode): () => void;
602
+ /**
603
+ * Build one scope's wire schemas and names for prompt-order validation.
604
+ * Restrictions do not make known tools invalid, but a mode collapse does.
605
+ */
606
+ private wireSchemas;
607
+ /**
608
+ * Resolve the PTC runtime or throw the actionable misconfiguration error.
609
+ * Read at use time (assembly / run_code execution), NOT via static
610
+ * `inject`: an inject entry would hold `ctx.tools` — and every tool plugin
611
+ * behind it — hostage to a PTC runtime existing even under `mode:
612
+ * 'native'`.
613
+ *
614
+ * Assembly and `run_code` execution read separately, so the language is not
615
+ * bound to a request. Harmless while one published backend exists — both
616
+ * reads return the same flavor — but a reload that swapped in a second
617
+ * language between them would hand a program written against one SDK to the
618
+ * other. Binding it is deferred until a second backend ships (the first
619
+ * point it is testable).
620
+ */
621
+ private requirePtcRuntime;
622
+ /**
623
+ * Register globally or in the calling agent scope. Scoped tools shadow
624
+ * globals; duplicates within one layer and the reserved `run_code` name fail.
625
+ * @param definition - tool schema, execution, and optional finalization/presentation callbacks.
626
+ * @returns the exact disposer that unregisters the tool.
627
+ */
628
+ register(definition: ToolDefinition): () => void;
629
+ /**
630
+ * Restrict global tools for the calling agent scope. Empty filters, unknown
631
+ * names, scope-local names, and reserved transport names fail. Restrictions
632
+ * intersect; scoped registrations remain visible.
633
+ * @param filter - global-tool mask: `allow` (keep only) and/or `deny` (remove).
634
+ * @returns the exact disposer that lifts this restriction.
635
+ */
636
+ restrict(filter: ToolRestriction): () => void;
637
+ /**
638
+ * Register a monotonic guard after the extensible `tools/pre-execute`
639
+ * waterfall. A plain-context guard applies globally; one registered through
640
+ * `agent.ctx` applies only to that agent. Any matching guard may deny by
641
+ * returning a reason, while no guard can force-allow a call another guard
642
+ * denied. The exact effect disposer is returned for ordered ownership and
643
+ * HMR cleanup.
644
+ * @param guard - synchronous check; a returned string denies the execution.
645
+ * @returns the exact disposer that unregisters the guard.
646
+ */
647
+ guard(guard: ToolGuard): () => void;
648
+ /** First monotonic denial from the global then the scope chain's guard layers, farthest first. */
649
+ private guardReason;
650
+ /**
651
+ * Resolve every registry fact one scope needs in one layer traversal. The
652
+ * visible map applies restrictions to the INHERITED surface, then the
653
+ * scope's own registrations and the reserved presentation transport; the
654
+ * other sets retain the pre-restriction facts needed by restriction and
655
+ * prompt-order validation.
656
+ *
657
+ * A restriction filters what a scope inherits — the global layer and every
658
+ * ancestor layer on its chain — and never what its OWN layer registers.
659
+ * That exemption is what a per-child capability filter has to keep intact:
660
+ * the delegation runtime registers a child's structured-output tool into the
661
+ * child's own layer, and a filter naming the capabilities the child may use
662
+ * must not strip the machinery it answers through.
663
+ *
664
+ * Reading the exempt set as "the global layer" instead of "not mine" held
665
+ * only while every model-facing tool sat in the host composition. Once
666
+ * presets moved them onto the agent plane they became an ANCESTOR
667
+ * contribution, so a child's filter silently stopped constraining anything
668
+ * it was given.
669
+ * @param scope - the viewing scope (the agent), or undefined for the global view.
670
+ * @returns the complete derived view for that scope.
671
+ */
672
+ private view;
673
+ /**
674
+ * Look up a tool as one scope sees it (scoped
675
+ * shadows global; a restricted-away global reads as absent). Presenters pass
676
+ * the calling agent so the rendered card matches the definition that
677
+ * actually executed.
678
+ * @param name - the tool name as registered.
679
+ * @param scope - the viewing scope (the agent); omitted = the global view.
680
+ * @returns the definition the scope resolves, or undefined when none is visible.
681
+ */
682
+ get(name: string, scope?: ScopeKey): ToolDefinition | undefined;
683
+ /**
684
+ * Resolve the definition that MAY EXECUTE for a call, applying the mode
685
+ * collapse at the operation boundary that owns it. The registry view
686
+ * (`get`) is presentation-agnostic; here a MODEL-DIRECT call under `ptc`
687
+ * may only name the reserved `run_code` transport, while a nested
688
+ * sub-dispatch (a `parent` token set — the `run_code` SDK calling a tool
689
+ * it bound) may call any visible tool. Denial surfaces as `UNKNOWN_TOOL`
690
+ * through the executor, matching an absent definition.
691
+ * @param name - the tool name as registered.
692
+ * @param scope - the viewing scope (the agent); omitted = the global view.
693
+ * @param nested - whether the call is a transport sub-dispatch, not a model-direct call.
694
+ * @returns the definition that may run, or undefined when the call must be rejected.
695
+ */
696
+ private resolveExecution;
697
+ /**
698
+ * Project visible definitions onto the allowlisted model-facing schema fields,
699
+ * excluding execution and presentation callbacks.
700
+ * @param scope - the viewing scope (the agent); omitted = the global view.
701
+ * @returns one deep-cloned schema per visible tool.
702
+ */
703
+ schemas(scope?: ScopeKey): ToolSchema[];
704
+ /** Project visible callable tools onto the generated PTC mode SDK contract. */
705
+ private sdkSchemas;
706
+ /** Project one definition onto the model-facing schema fields. */
707
+ private schemaOf;
708
+ /**
709
+ * Classify a pending call through the caller's visible tool definition. Only
710
+ * an exact `true` is parallel; unknown, hidden, undeclared, invalid, or
711
+ * throwing classifiers are exclusive.
712
+ * @param exec - call name, parsed arguments, and optional agent scope.
713
+ * @returns the fail-closed scheduling mode.
714
+ */
715
+ executionMode(exec: ToolExecutionInput): ToolExecutionMode;
716
+ /**
717
+ * Run the `tools/ptc-dispatch-log` waterfall over one settled sub-dispatch
718
+ * and return the content the bridge should log on `tool/ptc-dispatch`.
719
+ * Contained: when a listener throws, the method logs the original settled
720
+ * content; that failure must not fail the dispatch or omit the settle event. Private:
721
+ * the ONE consumer is the `run_code` bridge this registry constructs, which
722
+ * receives it as a capability parameter (the `requireRuntime` idiom) — the
723
+ * waterfall, not this invoker, is the public extension point.
724
+ */
725
+ private shapeDispatchLog;
726
+ /**
727
+ * Whether the `ptc` mode collapse denies a model-direct call: only the
728
+ * reserved `run_code` transport may be named. Nested sub-dispatches (a
729
+ * `parent` token set) bypass the collapse. One home for the
730
+ * security-relevant predicate, shared by {@link resolveExecution} and
731
+ * {@link createExecution} so the two can never drift apart.
732
+ *
733
+ * Resolved through {@link modeFor}, NOT `defaultMode`: an agent given `ptc`
734
+ * by an agent preset under a native deployment is the composition
735
+ * `qilin-agent-tool-presentation` exists for, and reading the deployment default would
736
+ * leave exactly that agent uncollapsed — announcing one surface while
737
+ * executing another, which is the bypass this collapse closes.
738
+ * @param name - the tool name as registered.
739
+ * @param scope - the viewing scope whose effective presentation mode applies.
740
+ * @param nested - whether the call is a transport sub-dispatch, not a model-direct call.
741
+ */
742
+ private collapses;
743
+ /**
744
+ * Execute through pre-policy, guards, around-dispatch, post-policy,
745
+ * definition-owned content finalization, and final notification. Tool and
746
+ * listener failures resolve as materialized error results; an invisible tool
747
+ * reports `UNKNOWN_TOOL`. The returned outcome is the same lossless, frozen
748
+ * snapshot final observers receive. Cancellation
749
+ * arriving after entry and before final result materialization skips a
750
+ * not-yet-started body with `ABORTED_BEFORE_DISPATCH` or replaces a
751
+ * successful started outcome with `ABORTED`; already-started work is still
752
+ * drained and may retain a tool-owned structured error.
753
+ * @param exec - the typed same-process call input. The registry assigns its
754
+ * correlation token before policy begins.
755
+ * @returns the materialized final result.
756
+ */
757
+ execute(exec: ToolExecutionInput): Promise<ToolExecutionResult>;
758
+ private completeScheduledExecution;
759
+ private createExecution;
760
+ /**
761
+ * Run the ordered pre-execute and monotonic guard stages for the scheduler.
762
+ * @param input - the caller-supplied execution input.
763
+ * @returns the prepared execution plus the next scheduler stage.
764
+ * @internal
765
+ */
766
+ private prepareScheduledExecution;
767
+ private prepareExecution;
768
+ /** Whether the original caller signal is currently aborted. */
769
+ private callerCancelled;
770
+ /** Canonical cancellation outcome selected by whether the tool body started. */
771
+ private cancellationResult;
772
+ /**
773
+ * Dispatch the registered body with the original caller signal fused back
774
+ * into any around-wrapper replacement. Cancellation never abandons the body:
775
+ * a started promise reaches quiescence before its outcome becomes `ABORTED`.
776
+ */
777
+ private dispatchToolBody;
778
+ /**
779
+ * Run around-dispatch and the tool body. Tool and unknown-tool failures still
780
+ * receive post-execute; pipeline failures are already final.
781
+ * @param exec - the prepared execution.
782
+ * @returns whether the result still needs post-execute.
783
+ * @internal
784
+ */
785
+ private dispatchScheduledExecution;
786
+ /**
787
+ * Run ordered post-execute, then apply definition-owned content finalization,
788
+ * materialize, and notify the final outcome.
789
+ * @param exec - the prepared execution.
790
+ * @param result - dispatch/pre result that still needs post-execute.
791
+ * @returns the materialized final result.
792
+ * @internal
793
+ */
794
+ private finalizeScheduledExecution;
795
+ /**
796
+ * Materialize the candidate, apply definition-owned content finalization,
797
+ * then materialize and notify the authoritative result.
798
+ * @param exec - the prepared execution.
799
+ * @param result - final result.
800
+ * @returns the materialized final result.
801
+ * @internal
802
+ */
803
+ private finishScheduledExecution;
804
+ /** Apply the snapshotted tool-owned content transform without exposing other result fields. */
805
+ private applyFinalContent;
806
+ /** Notify observers without exposing a mutation or error channel into the outcome. */
807
+ private notifyResult;
808
+ /**
809
+ * Resolve an `ask` decision to allow/deny through the approval seam. The
810
+ * seam is consumed opportunistically with `ctx.get('approval')` — a
811
+ * deployment that composes no ApprovalService keeps the historical degrade
812
+ * to deny, and an unmount mid-session degrades the same way on the next ask.
813
+ * An agent-less execution also degrades: without an agent there is no
814
+ * session to audit to and no UI to route to. Otherwise the outcome maps
815
+ * one-to-one — `allowed-once` proceeds; the three non-grants deny with
816
+ * distinct reasons so the model can tell a human "no" from an absent
817
+ * approval channel.
818
+ */
819
+ private serviceAsk;
820
+ /**
821
+ * Run the `tools/post-execute` waterfall over a dispatched `result` and apply
822
+ * its {@link PostToolDecision}: `accept` keeps the call successful (replacing
823
+ * `content` when given), `block` turns it into an `isError` whose content is
824
+ * the corrective `feedback`. Either decision may attach `additionalContexts`,
825
+ * which are ferried on the returned result for the loop's active-batch FIFO.
826
+ * Context deferred by the tool body survives an accepted result but is
827
+ * discarded when the outer call is blocked; a block exposes only context the
828
+ * blocking decision explicitly supplied.
829
+ * Runs inside `execute`'s outer try/catch (a throwing listener → isError).
830
+ */
831
+ private postExecute;
832
+ /** Registry-normalized results and the exact dispatch that validated each value. */
833
+ private readonly canonicalResults;
834
+ /** Mark one registry-normalized result as canonical only for its owning dispatch. */
835
+ private markCanonical;
836
+ /** Snapshot, validate, render, and optionally project one successful body value. */
837
+ private createSuccessResult;
838
+ /** Normalize an around-dispatch wrapper's authored result through the owning output contract. */
839
+ private normalizeDispatchResult;
840
+ /** Materialize the authoritative commit outcome once, immediately before `tools/result`. */
841
+ private materializeFinalResult;
842
+ }
843
+ export default ToolRuntime;
844
+ //# sourceMappingURL=index.d.ts.map