import { Registry } from "./registry";
import { ENCODE_METHOD, DECODE_METHOD } from "../utils/encoder_symbols";
import type { Media } from "./media";
import type { AdkEncodableSnapshot } from "./encodable";
import type { Schema, Description } from '@nhtio/validation';
import type { DispatchContext } from "../contracts/dispatch_context";
import type { SpooledArtifact, SpooledArtifactConstructor } from "./spooled_artifact";
/**
* A zero-arg function that returns the {@link @nhtio/adk!SpooledArtifactConstructor} the consumer should
* use when wrapping this tool's serialised output into a `ToolCall.results` field.
*
* @remarks
* Why a resolver (and not the constructor itself)? `tool.ts` participates in a module-load
* cycle with `spooled_artifact.ts` and `artifact_tool.ts` (`ArtifactTool extends Tool` closes
* the loop). Any eager value-level reference to `SpooledArtifact` in `tool.ts` would crash the
* cycle with a TDZ error. A resolver lets `tool.ts` validate "is a function" at module-load
* time and defer the actual constructor check to validate-time (which always runs after every
* module body has executed). Wrap-sites invoke `tool.artifactConstructor?.() ?? SpooledArtifact`
* to obtain the final constructor.
*/
export type ArtifactConstructorResolver = () => SpooledArtifactConstructor;
/**
* The execution function for a {@link Tool}.
*
* @remarks
* Receives the raw arguments passed to the executor, the active {@link @nhtio/adk!DispatchContext}, and the
* tool's metadata registry.
*
* Return shapes:
* - `string` / `Uint8Array` — opaque serialised output. The ADK does not persist the bytes
* itself; the consumer's executor middleware is responsible for storing them and wrapping
* them via `tool.artifactConstructor?.() ?? SpooledArtifact` when assembling the `ToolCall`
* record.
* - {@link @nhtio/adk!SpooledArtifact} — a pre-built, reader-backed result. Return this when
* the handler has streamed bytes into storage and wrapped the reader; the consumer passes it
* through without materialising or re-spooling it. Its concrete class determines the forged
* `artifact_*` query tools.
* - {@link @nhtio/adk!Media} / `Media[]` — explicit-modality silo. Bypasses
* {@link Tool.artifactConstructor} — the handler returns the final result shape directly.
* The LLM battery renders each `Media` as a provider-specific content block.
*/
export type ToolHandler = (args: unknown, ctx: DispatchContext, meta: Registry) => string | Uint8Array | SpooledArtifact | Media | Media[] | Promise;
/**
* Plain input object supplied to {@link Tool} at construction time.
*
* @typeParam A - The {@link @nhtio/adk!SpooledArtifact} subtype used to wrap this tool's results when
* the consumer assembles a `ToolCall.results` field. Defaults to {@link @nhtio/adk!SpooledArtifact}
* (plain text). Tools producing JSON output should set this to `SpooledJsonArtifact`; tools
* producing markdown should set it to `SpooledMarkdownArtifact`; consumers can also pass a
* custom subclass.
*/
export interface RawTool {
/** Unique identifier used in LLM tool definitions. Recommend lowercase snake_case. */
name: string;
/** Human-readable description passed to the model to explain what the tool does. */
description: string;
/** @nhtio/validation schema for the tool's input arguments. Annotate with `.description()`, `.note()`, `.example()` etc. to produce rich LLM tool definitions via `.describe()`. */
inputSchema: Schema;
/** Execution function. Not exposed as a public property — invoke via `executor()`. */
handler: ToolHandler;
/**
* Zero-arg resolver returning the {@link @nhtio/adk!SpooledArtifactConstructor} the consumer should use
* when wrapping this tool's serialised output into a `ToolCall.results` field. Optional —
* when omitted, wrap-sites fall back to {@link @nhtio/adk!SpooledArtifact} (plain text).
*
* @remarks
* Recommended call shape: `artifactConstructor: () => SpooledJsonArtifact`. The closure is
* the indirection that lets `tool.ts` validate this field without eagerly importing
* `SpooledArtifact` (which would crash the `tool.ts ↔ spooled_artifact.ts ↔ artifact_tool.ts`
* module-load cycle). At validate time the schema invokes the resolver and verifies its
* return value is a `SpooledArtifact`-derived constructor — wrong-shape resolvers throw
* {@link @nhtio/adk!E_INVALID_INITIAL_TOOL_VALUE}.
*
* Wrap-sites (storage batteries, scripted executors) read the constructor via
* `tool.artifactConstructor?.() ?? SpooledArtifact`.
*/
artifactConstructor?: ArtifactConstructorResolver;
/** Optional arbitrary metadata for this tool (e.g. RBAC scopes, feature flags). Defaults to `{}`. Stored in a {@link @nhtio/adk!Registry} for dot-path access. */
meta?: Record;
/**
* When `true`, marks this tool as owned by a specific {@link @nhtio/adk!DispatchContext} so that
* `ToolRegistry.pruneEphemeral()` will drop it at ctx-completion.
*
* @remarks
* The flag is advisory at the `Tool` level — registries decide what to do with it. The canonical
* producer of ephemeral tools is `SpooledArtifact.forgeTools(ctx)`, which sets this to `true`
* on every artifact-query tool it emits.
*
* @defaultValue `false`
*/
ephemeral?: boolean;
/**
* When `true`, declares that this tool's output should be treated as **trusted developer/user
* intent** rather than as untrusted third-party text when surfaced to the model.
*
* @remarks
* LLM batteries read this flag per call when rendering tool-call results. The default
* untrusted envelope (e.g. `` in the OpenAI Chat Completions battery) is the
* secure-by-default treatment for arbitrary tool output. A tool whose output is authored by the
* user or operator (Q&A tools surfacing user-authored answers, human-in-the-loop approval
* gates, feedback-collection tools, configuration tools returning developer-authored
* constants) sets this to `true` so the LLM battery routes the result through its trusted
* envelope (`` in the OpenAI Chat Completions battery).
*
* Trust is a property of the tool's output, not a property of how a particular battery is
* wired — putting the flag here means the trust signal travels with the tool wherever it is
* registered, no per-battery string lists, no name-matching to fail-open on typos.
*
* @defaultValue `false`
*/
trusted?: boolean;
/**
* Self-declared merge collision policy. Honoured by `ToolRegistry.merge` (NOT by
* `ToolRegistry.register`) when this tool collides with another of the same name.
*
* @remarks
* - `'throw'` (default): defer to the merge-level `options.onCollision`. If that is also
* `'throw'`, the merge raises `E_TOOL_ALREADY_REGISTERED`. This matches the default behaviour
* of `ToolRegistry.register`.
* - `'replace'`: this tool always wins the collision, regardless of the merge-level option.
* - `'keep'`: this tool always loses to any previously-registered tool of the same name.
*
* Forged artifact-query tools set this to `'replace'` so that merging multiple
* `Subclass.forgeTools(ctx)` outputs (whose base-method tools overlap by name) resolves
* silently — the descriptors, snapshot, and handler behaviour are interchangeable across
* subclasses, so replacement is a behavioural no-op.
*
* @defaultValue `'throw'`
*/
onCollision?: 'throw' | 'replace' | 'keep';
}
/**
* A tool definition that serves as the single source of truth for a callable tool: its name,
* description, input schema, execution handler, and the {@link @nhtio/adk!SpooledArtifact} subclass that
* wraps its serialised output.
*
* @typeParam A - The {@link @nhtio/adk!SpooledArtifact} subtype this tool's results should be wrapped in.
* Defaults to {@link @nhtio/adk!SpooledArtifact}.
*
* @remarks
* The `inputSchema` is a `@nhtio/validation` schema. It is used at runtime to validate incoming
* arguments before the handler is called, and its `.describe()` output provides all the metadata
* needed to build a provider-specific LLM tool definition — annotate the schema with
* `.description()`, `.note()`, `.example()` etc. once, and that information is available in both
* contexts without duplication.
*
* The handler is private — invoke it only through `executor(ctx)` which validates args, fires
* observability events (with a stable `callId` derived from the tool name and arguments), and
* wraps handler errors in {@link @nhtio/adk!E_TOOL_DOWNSTREAM_ERROR}. The handler returns serialised bytes
* (`string | Uint8Array`); persistence is the consumer's responsibility.
*
* `artifactConstructor` is the {@link @nhtio/adk!SpooledArtifact} subclass the consumer should use when
* wrapping the handler's output into a `ToolCall.results` field. The author declares it once
* on the tool instance; the consumer reads it when assembling persisted records.
*/
export declare class Tool {
#private;
/**
* Validator schema that accepts a {@link RawTool} object.
*
* @remarks
* Reusable fragment for any schema that needs to validate or nest a tool entry
* (e.g. `TurnRunnerConfig.tools`).
*/
static schema: import("@nhtio/validation").ObjectSchema>;
/**
* Returns `true` if `value` is a {@link Tool} instance.
*
* @param value - The value to test.
* @returns `true` when `value` is a {@link Tool} instance.
*/
static isTool(value: unknown): value is Tool;
/** The tool's unique name, as exposed to the model in the tool definition. */
readonly name: string;
/** Human/model-facing description of what the tool does. */
readonly description: string;
/** Validation schema for the tool's arguments; also drives the generated parameter definition. */
readonly inputSchema: Schema;
/** Resolver for the artifact constructor used to wrap the handler's output, if any. */
readonly artifactConstructor: ArtifactConstructorResolver | undefined;
/** Arbitrary per-tool metadata registry, passed through to the handler. */
readonly meta: Registry;
/** When `true`, the tool's results are not persisted to history (transient/one-shot). */
readonly ephemeral: boolean;
/** When `true`, the tool's output is treated as trusted content by the LLM battery's envelopes. */
readonly trusted: boolean;
/** How registration resolves a name clash in a {@link ToolRegistry}: throw, replace, or keep the existing. */
readonly onCollision: 'throw' | 'replace' | 'keep';
/**
* @param raw - The raw tool input validated against `rawToolSchema`.
* @throws {@link @nhtio/adk!E_INVALID_INITIAL_TOOL_VALUE} when `raw` does not satisfy the schema.
*/
constructor(raw: RawTool);
/**
* Validates `args` against the tool's input schema asynchronously.
*
* @remarks
* Async to support schemas with external validators (e.g. database lookups, API calls).
* A validation failure throws {@link @nhtio/adk!E_INVALID_TOOL_ARGS} — this indicates a programming error
* in the tool call loop, not a downstream failure.
*
* @param args - The arguments to validate.
* @returns The validated (and coerced) arguments.
* @throws {@link @nhtio/adk!E_INVALID_TOOL_ARGS} when `args` does not satisfy the input schema.
*/
validate(args: unknown): Promise;
/**
* Returns a bound executor function for this tool against the given turn context.
*
* @remarks
* The executor: (1) computes a stable `callId` as `sha256(canonicalStringify({tool, args}))`
* over the **raw, pre-validation args**, (2) validates `args` via {@link Tool.validate},
* (3) emits `toolExecutionStart` on the context (with the computed `callId`), (4) calls the
* handler, (5) emits `toolExecutionEnd` (with the same `callId`), (6) wraps any handler error
* in {@link @nhtio/adk!E_TOOL_DOWNSTREAM_ERROR} before re-throwing.
*
* The handler usually returns serialised bytes (`string | Uint8Array`) — persistence is then the
* consumer's concern, and {@link Tool.artifactConstructor} is the class a wrap-site uses when
* wrapping those bytes into a `ToolCall.results` field. **A handler may instead return a
* {@link @nhtio/adk!SpooledArtifact} it built itself** (streaming into storage and wrapping the
* reader), in which case wrap-sites pass it through untouched and `artifactConstructor` does not
* apply — the returned instance's own class is what artifact-tool forging reads.
*
* Pattern mirrors `Middleware.runner()` — call once per turn, reuse the returned function.
*
* @param ctx - The active turn context. Provides emit functions and turn ID.
* @returns An async function `(args) => Promise`.
* A `SpooledArtifact` is a result the HANDLER built (streamed to storage and wrapped itself); a
* wrap-site passes it through unwrapped rather than re-spooling it. `string`/`Uint8Array` are the
* opposite case — bytes the consumer must still store and wrap.
*/
executor(ctx: DispatchContext): (args: unknown) => Promise;
/**
* Returns a fully serialisable snapshot of this tool's definition.
*
* @remarks
* The `inputSchema` property is the result of calling `.describe()` on the raw schema — a plain
* object carrying all the annotation metadata (descriptions, notes, examples, types) without any
* validator functions. Use this to build provider-specific LLM tool definitions.
*
* @returns `{ name, description, inputSchema }` where `inputSchema` is the schema description.
*/
describe(): {
name: string;
description: string;
inputSchema: Description;
};
/**
* Serialise this Tool into an `@nhtio/encoder` snapshot.
*
* @remarks
* Three halves with different serialisation strategies:
*
* - **Plain config** (`name`, `description`, `meta`, flags) — emitted verbatim.
* - **`inputSchema`** — delegated to `@nhtio/validation`'s `encode()`, which captures the full schema
* description plus its library version into a compact string; decode rebuilds the live `Schema`.
* - **`handler` / `artifactConstructor`** — emitted as **functions**. The encoder's `FunctionSerializer`
* serialises them by **source text** (`fn.toString()`). This is the sharp edge: a handler that
* closes over `ctx`, service clients, or config loses those bindings on decode — only the source
* survives, and the captured variables read back as `undefined`. A `.bind()`-ed or native handler
* cannot be serialised at all and the encoder throws. Tools are rarely serialised in practice; when
* they are, write handlers that reconstruct their dependencies inside the body rather than closing
* over them.
*
* @returns A {@link RawTool}-shaped snapshot (with `inputSchema` as an encoded string).
*/
[ENCODE_METHOD](): AdkEncodableSnapshot;
/**
* Reconstruct a {@link Tool} from a {@link Tool.[ENCODE_METHOD]} snapshot.
*
* @remarks
* Rebuilds the live `Schema` via `@nhtio/validation`'s `decode()`, then re-validates through the
* constructor. The rehydrated `handler` carries only its source text — see
* {@link Tool.[ENCODE_METHOD]} for the closure caveat.
*
* @param data - The snapshot produced by {@link Tool.[ENCODE_METHOD]}.
* @returns A fully-validated {@link Tool}.
*/
static [DECODE_METHOD](data: AdkEncodableSnapshot): Tool;
}