import { type CanonicalKeysResult } from "../utils/parameterAliases.js"; import { type Run } from "./asyncContext.js"; import type { Capability } from "../host/host.js"; import type { CreatedTool } from "./createdTool.js"; export declare const UNSET: unique symbol; export type FuncParam = { name: string; hasDefault: boolean; defaultValue: unknown; variadic: boolean; /** * True when the parameter's declared type is (or contains) a function type * — e.g. `block: () => void`, a union with a function arm, or a variadic * whose element type is a function. Set by the codegen from the static * `isFunctionTyped` predicate; consumed by `validateToolForLLM`. * * Defaults to false on legacy/handcrafted FuncParam values so the runtime * backstop fails open (i.e. doesn't block valid tools). */ isFunctionTyped?: boolean; /** * False when the parameter's declared type does NOT accept Result values — * i.e. it is not `Result`/`Result<...>`, not explicit `any`, and not a * union containing either. Set by codegen from the static * `paramAcceptsFailure` predicate (lib/typeChecker/utils.ts); consumed by * the failure-propagation check in invoke(). * * Absent on legacy/handcrafted FuncParam values, and absence fails OPEN * (the param accepts failures) — same convention as `isFunctionTyped`. */ acceptsResult?: boolean; /** * Other names a caller from outside the program may use for this * parameter, from `def fetch(baseUrl | url: string)`. Only a call whose * descriptor sets `forgiveAliases` gets them renamed; for everyone else * an alias is an unknown argument. Absent when there are none. See * `docs/dev/language/parameter-aliases.md`. */ aliases?: string[]; boundValue?: unknown; isBound?: boolean; }; export type CallType = { type: "positional"; args: unknown[]; } | { type: "named"; positionalArgs: unknown[]; namedArgs: Record; /** Trailing-block argument from `f(name: val) as { ... }` syntax. * * In the positional case the block is simply appended to `args` * (it's always the next positional slot), so no separate field * is needed. In the named case earlier params may be filled by * name and intermediate params may need UNSET padding, so the * block must be bound by NAME to the last non-variadic * parameter — appending it as positional[N] would mis-fill * parameter N. resolveNamed synthesizes it into namedArgs under * the last param's name so the rest of fill logic treats it * uniformly. Without this, `f(name: val) as { ... }` silently * dropped the block. */ blockArg?: unknown; /** Set by a caller whose arguments come from outside the program: a * model calling a tool, an HTTP body, the command line. Then an * alias key is renamed to its parameter's name. A call compiled * from Agency source never sets it, so there an alias is an unknown * argument, whether or not the type checker ran. */ forgiveAliases?: boolean; }; export type ToolDefinition = { name: string; description: string; schema: unknown; }; /** Markers carried on a registered tool. Inline shape so the runtime * stays decoupled from the compiler's AST types. */ export type ToolMarkers = { destructive?: boolean; idempotent?: boolean; /** Called as a tool, this function continues the caller's conversation. * Read by the tool loop in prompt.ts. */ handoff?: boolean; /** Declared `decorator def`. Carried for completeness; the runtime does * nothing with it. */ decorator?: boolean; }; /** * The body of an `AgencyFunction`. Its first parameter is the run it is * called under, and the function's own arguments follow: * * fn: (run, name: string, age: number) => `${name} is ${age}` * * This holds for every body, whether the compiler wrote it or a person did. * A body that needs nothing from the run still declares the parameter, or * declares none at all. A body that puts one of its own typed parameters * first does not compile. */ export type AgencyFunctionBody = (run: Run, ...args: any[]) => any; /** Builds the zod schema of what a call returns. */ export type ReturnSchema = () => unknown; export type AgencyFunctionOpts = { name: string; module: string; fn: AgencyFunctionBody; params: FuncParam[]; toolDefinition: ToolDefinition | null; /** Makes the zod schema of the value a call returns: the declared return * type, or the success type inside a `Result`. A thunk, so nothing is * built at module load. Absent for a def that is not exported or declares * no return type, or whose return type holds a function or a `regex` * (which JSON Schema cannot express) or names a type its module cannot * reach at run time (#1371). */ returnSchema?: ReturnSchema | null; exported?: boolean; markers?: ToolMarkers; isPreapproved?: boolean; registeredName?: string; /** What the function's own `@capabilities` tag names. Checked against the * run's host on every call, before the body. Empty when untagged. */ capabilities?: Capability[]; /** Definition and registered executor for a callable built at runtime. */ created?: CreatedTool; /** The JSON Schema each `.withParamSchema` call gave a parameter, by * name, kept so a checkpoint can restore it. Never zod: zod does not * serialize. */ paramSchemas?: Record; /** The absolute path of the `.agency` file this function was defined in, * resolved when its compiled module loaded. Null for a function with no * source file, such as a block, a builtin, or code compiled for a * subprocess. */ sourceFile?: string | null; }; export declare class AgencyFunction { #private; /** The marker `isAgencyFunction` checks. An own enumerable property, so a * raw JSON copy of a function still reads as one and the registry can * rebuild it from `name` and `module` (`FunctionRefReviver`). Defined in * the constructor as non-writable and non-configurable, with the label. */ readonly __agencyFunction: true; /** The label: the name, module, registered name, and source file that * `toolRefs` turns into an import line. Own data properties that cannot * be reassigned or redefined, so code that holds a function cannot point * it at another file or name. */ readonly name: string; readonly module: string; readonly params: FuncParam[]; readonly toolDefinition: ToolDefinition | null; readonly returnSchema: ReturnSchema | null; private readonly _fn; private readonly _unboundParams; private readonly _nonVariadicUnbound; private readonly _hasVariadic; /** Whether any unbound parameter has aliases. Almost none do, so a * named call skips the alias pass when this is false. */ private readonly _hasAliases; private readonly _isBound; private readonly _checksFailures; readonly exported: boolean; readonly markers: ToolMarkers; readonly capabilities: Capability[]; private readonly _isPreapproved; /** The key this function's registered ancestor is stored under in the * function registry. `.rename()` changes `name` but not this, and the * other derivations carry it forward — `.partial()` and `.preapprove()` * directly, `.describe()` via `withToolDefinition()` — so a serialized * ref to any derived copy can find its registry entry again. Equal to * `name` for functions never renamed. */ readonly registeredName: string; readonly created?: CreatedTool; readonly paramSchemas: Record; readonly sourceFile: string | null; constructor(opts: AgencyFunctionOpts); get isPreapproved(): boolean; get description(): string; get boundArgs(): { indices: number[]; values: unknown[]; originalParams: FuncParam[]; } | null; withToolDefinition(toolDefinition: ToolDefinition | null): AgencyFunction; getOriginalParams(): FuncParam[]; getUnboundParams(): FuncParam[]; /** The full resolved argument list received by a body, by canonical * parameter name. Omitted defaults stay absent; an explicit undefined * is still present. A variadic already occupies one array slot. */ namedArguments(resolved: readonly unknown[]): Record; invoke(outerRun: Run, descriptor: CallType): Promise; /** Argument resolution, failure propagation, and the body, under a run * whose host has already been checked. */ private run; /** * The value of every unbound parameter for a call that passes `record` * by name and nothing by position: the record's value, UNSET for an * absent defaulted parameter, [] for an absent variadic. `keep` holds the * current values of the parameters a record cannot carry (the function's * block-typed ones), by name; a record that names one of them is refused. * Throws what a call throws: an unknown name, a missing required * argument. Null and undefined are the empty record; any other * non-object is refused. * * This is how a decorator's block rebinds the decorated function's * parameters (lib/runtime/rebind.ts). That function's module-level * constant is never partially applied, so `_unboundParams` is all of its * parameters. */ bindRecord(record: unknown, keep?: Record): Record; partial(bindings: Record): AgencyFunction; preapprove(): AgencyFunction; describe(description: string): AgencyFunction; /** * Return a copy of this function with a new name. The name is BOTH what the * LLM sees as the tool name and what tool-call dispatch matches against * (`prompt.ts` looks up the handler by `fn.name`), so `name` and * `toolDefinition.name` are updated together. * * `.partial()` and `.describe()` deliberately preserve the base name, so * deriving several tools from one function (e.g. `read.partial(dir)` as * `skillsDir` does) produces several tools that all share that base name. * Passing such a list to `llm({ tools })` is rejected by providers that * require unique tool names (Anthropic returns a 400). `.rename(...)` gives * each derived tool a distinct name. */ /** * Return a copy whose `name` parameter is described to the model by * `jsonSchema`, a JSON Schema document, instead of the schema its * declaration gave it. For a parameter whose type is only known at run * time, such as a saved tool's request. The parameter must be unbound, * and the other parameters keep the schemas they have. */ withParamSchema(name: string, jsonSchema: unknown): AgencyFunction; rename(newName: string): AgencyFunction; private mergeWithBound; /** * The arguments with alias keys renamed to their parameter's name, the * list of renames, and the conflict message if two keys name one * parameter. Never throws, and idempotent: the tool loop calls it before * the invoke (for the repeat key) and `resolveNamed` calls it again. */ canonicalizeNamedArgs(namedArgs: Record): CanonicalKeysResult; private resolveArgs; /** The one place that decides what an alias key means for a caller. A * forgiven caller gets the rename, or the conflict if two keys name one * parameter. Any other caller gets an error naming the parameter, so * Agency code cannot use an alias even with the type checker off. */ private namedArgsWithoutAliases; private resolvePositional; private resolveNamed; /** * Runtime backstop invoked once per tool by `runPrompt` immediately before * issuing the LLM request. Throws if any required function-typed param is * unbound — duplicating (intentionally) the type checker's check at the * `llm(...)` site so dynamically-assembled tool arrays (`tools: [...base, * x]`) are also covered. The error wording uses the same builder as the * compile-time diagnostic so users see one consistent message. */ validateForLLM(): void; /** Whether `value` reads as a function: an instance, or a raw copy of * one that the registry can rebuild. This is the runtime's own check. */ static isAgencyFunction(value: unknown): value is AgencyFunction; /** Whether `value` is an instance this runtime built. Nothing made from * data passes: not an object literal, a JSON value, or a copy of an * instance's properties. `toolRefs` uses this, since a tool's label is * written into an import. */ static isBranded(value: unknown): value is AgencyFunction; static create(opts: AgencyFunctionOpts, registry: Record): AgencyFunction; }