/** * Typed tool contracts. * * A tool is an owned effect seam for an agent. The contract carries the data shape, authority, * approval, idempotency, dimensionless cost, and sensitivity metadata that every adapter needs. * Adapters call {@link executeTool}; they do not reimplement admission. * * This module is deliberately token-only in its evidence and idempotency stores. It never persists * tool arguments, results, request bodies, or business values. */ import type { RequestBudget } from "./budget.js"; import { type ExecutionPolicy, type ExecutionPolicyAdapter } from "./execution-policy.js"; import { type IdempotencyScope } from "./idempotency.js"; import { type EffectCost, type RequestLedger, type SealedEffectLedger } from "./ledger.js"; import { type InferOutput, type StandardIssue, type StandardSchemaV1 } from "./schema/standard.js"; import { type ProtoPoisoning } from "./server/proto-guard.js"; export type ToolSensitivity = "public" | "internal" | "sensitive" | "secret"; export type ToolApprovalPolicy = { readonly kind: "none"; } | { readonly kind: "required"; } | { readonly kind: "threshold"; readonly level: number; }; export type ToolApproval = { readonly granted: true; readonly level?: number; } | { readonly granted: false; readonly reason?: string; }; export interface ToolAnnotations { readonly title?: string; readonly readOnlyHint?: boolean; readonly destructiveHint?: boolean; readonly idempotentHint?: boolean; readonly openWorldHint?: boolean; } export interface ToolIdempotencyPolicy { readonly scope: Exclude; /** Return an opaque bounded key. The key must not contain a request body or secret. */ readonly key: (input: Input) => string | PromiseLike; } export interface ToolContractOptions { readonly name: string; readonly description: string; readonly input: InputSchema; readonly output: OutputSchema; readonly capability?: string; readonly approval?: ToolApprovalPolicy; readonly idempotency?: ToolIdempotencyPolicy; /** Dimensionless counters such as `{ calls: 1, ms: 20 }`; never prices. */ readonly cost?: EffectCost; readonly sensitivity?: ToolSensitivity; readonly annotations?: ToolAnnotations; readonly policy?: ExecutionPolicy; readonly execute: (input: Input, context: ToolExecutionContext) => Output | PromiseLike; } export interface ToolContract { readonly name: string; readonly description: string; readonly input: StandardSchemaV1; readonly output: StandardSchemaV1; readonly capability: string; readonly approval: ToolApprovalPolicy; readonly idempotency?: ToolIdempotencyPolicy; readonly cost?: EffectCost; readonly sensitivity: ToolSensitivity; readonly annotations: ToolAnnotations; readonly policy?: ExecutionPolicy; readonly execute: (input: Input, context: ToolExecutionContext) => Output | PromiseLike; } export interface ToolExecutionContext { readonly effectId: string; readonly signal: AbortSignal; /** Wall-clock budget shared with the parent request, when an agent supplies one. */ readonly deadline?: RequestBudget; readonly dryRun: boolean; readonly policy?: ExecutionPolicy; } export interface ToolBudget { readonly consume: (cost: EffectCost | undefined) => boolean; readonly snapshot: () => EffectCost; } export interface CreateToolBudgetOptions { readonly limits: EffectCost; readonly initial?: EffectCost; } export declare function createToolBudget(options: CreateToolBudgetOptions): ToolBudget; export interface ToolIdempotencyBeginInput { readonly namespace: string; readonly key: string; } export type ToolIdempotencyBeginResult = { readonly state: "new"; readonly reservation: string; } | { readonly state: "duplicate"; } | { readonly state: "in-flight"; } | { readonly state: "capacity"; }; export interface ToolIdempotencyStore { readonly durability?: "memory" | "durable"; begin(input: ToolIdempotencyBeginInput): ToolIdempotencyBeginResult | PromiseLike; complete(input: { readonly namespace: string; readonly key: string; readonly reservation: string; }): boolean | PromiseLike; abandon(input: { readonly namespace: string; readonly key: string; readonly reservation: string; }): boolean | PromiseLike; } export interface MemoryToolIdempotencyStoreOptions { readonly maxEntries?: number; readonly ttlMs?: number; readonly now?: () => number; } export declare class MemoryToolIdempotencyStore implements ToolIdempotencyStore { readonly durability: "memory"; private readonly entries; private readonly maxEntries; private readonly ttlMs; private readonly now; constructor(options?: MemoryToolIdempotencyStoreOptions); begin(input: ToolIdempotencyBeginInput): ToolIdempotencyBeginResult; complete(input: { readonly namespace: string; readonly key: string; readonly reservation: string; }): boolean; abandon(input: { readonly namespace: string; readonly key: string; readonly reservation: string; }): boolean; sweep(): void; } export type ToolEvidenceStage = "input" | "capability" | "policy" | "approval" | "idempotency" | "budget" | "execution" | "output"; export type ToolEvidenceOutcome = "passed" | "denied" | "failed" | "skipped" | "committed" | "dry-run"; export interface ToolEvidence { readonly seq: number; readonly stage: ToolEvidenceStage; readonly outcome: ToolEvidenceOutcome; readonly code?: string; } export interface ToolError { readonly code: "input_invalid" | "capability_denied" | "execution_policy_unsatisfied" | "approval_required" | "approval_denied" | "idempotency_store_missing" | "idempotency_durability" | "idempotency_duplicate" | "idempotency_in_flight" | "idempotency_capacity" | "budget_exceeded" | "cancelled" | "execution_failed" | "output_invalid" | "ledger_failed"; readonly stage: ToolEvidenceStage; readonly issues?: readonly StandardIssue[]; } export type ToolCallResult = { readonly ok: true; readonly output?: Output; readonly dryRun: boolean; readonly evidence: readonly ToolEvidence[]; readonly ledger: SealedEffectLedger; } | { readonly ok: false; readonly dryRun: boolean; readonly output?: undefined; readonly error: ToolError; readonly evidence: readonly ToolEvidence[]; readonly ledger: SealedEffectLedger; }; export interface ToolCallOptions { readonly effectId?: string; readonly clock?: () => number; readonly signal?: AbortSignal; /** Wall-clock budget shared with the parent request, distinct from the cost `budget`. */ readonly deadline?: RequestBudget; readonly capabilities?: readonly string[]; readonly approval?: ToolApproval; readonly budget?: ToolBudget; readonly idempotency?: ToolIdempotencyStore; readonly namespace?: string; readonly dryRun?: boolean; readonly ledger?: RequestLedger; readonly executionPolicy?: ExecutionPolicyAdapter; } export declare function defineTool(options: ToolContractOptions, InferOutput, InputSchema, OutputSchema>): ToolContract, InferOutput>; export declare function executeTool(tool: ToolContract, input: unknown, options?: ToolCallOptions): Promise>; export interface ToolHttpOptions extends Omit { readonly method?: string; /** Prototype-poisoning policy for the JSON request body - mirrors the server option (this * handler is standalone, so it carries its own). Default `"reject"`. */ readonly protoPoisoning?: ProtoPoisoning; /** Transport body cap in bytes for the JSON request body. Default {@link DEFAULT_TOOL_MAX_BYTES} * (1 MiB). `"unlimited"` removes the cap and requires a written {@link maxBytesReason}: the * mounting platform then owns the limit, and the exemption is reviewable rather than implicit. */ readonly maxBytes?: number | "unlimited"; /** Why this handler runs uncapped. Required with `maxBytes: "unlimited"`, rejected without it. */ readonly maxBytesReason?: string; } /** Default transport body cap for {@link createToolHttpHandler}: 1 MiB. */ export declare const DEFAULT_TOOL_MAX_BYTES = 1048576; /** * Mount one contract behind a Web-standard handler. The handler accepts one JSON request body. * A body carrying a poisoned key (own `__proto__`, or `constructor.prototype`) is rejected with * the same `input_invalid` result as malformed JSON. The body is capped at * {@link ToolHttpOptions.maxBytes} (1 MiB by default); over-cap answers a flat `413` and a * malformed `Content-Length` a flat `400`, before any parse. */ export declare function createToolHttpHandler(tool: ToolContract, options?: ToolHttpOptions): (request: Request) => Promise; export declare function toolHttpResult(result: ToolCallResult): Response; export declare function toolInputJsonSchema(tool: ToolContract): Record; export type ToolAdapterResult = { readonly ok: true; readonly output?: Output; readonly dryRun: boolean; readonly evidence: readonly ToolEvidence[]; } | { readonly ok: false; readonly dryRun: boolean; readonly error: ToolError; readonly evidence: readonly ToolEvidence[]; }; export interface ToolAdapter { readonly name: string; call(input: unknown, options?: ToolCallOptions): Promise; } export interface ToolConformanceResult { readonly adapter: string; readonly checks: readonly string[]; } /** Shared conformance assertions for adapters. It intentionally checks only public, token-only behavior. */ export declare function runToolContractConformance(adapter: ToolAdapter, options: { readonly input: unknown; readonly capability: string; readonly approval: ToolApproval; readonly dryRun: ToolCallOptions; }): Promise; //# sourceMappingURL=tool-contract.d.ts.map