import { AITool } from '@happyvertical/ai'; import { Logger } from '@happyvertical/logger'; import { DispatchBus, SmrtClassOptions } from '@happyvertical/smrt-core'; import { DelegationEnvelope } from './delegation.js'; import { PrincipalAuditSink, PrincipalRun } from './execute-as-principal.js'; /** Catalog slug + permission id of the standard invoke-agent tool. */ export declare const INVOKE_AGENT_TOOL_SLUG = "agents.invoke"; /** * Provider-safe function name the model receives for the invoke-agent tool. * Catalog slugs contain a `.` which some providers (OpenAI) reject in function * names; the loop resolves a call by either the slug or this name. */ export declare const INVOKE_AGENT_FUNCTION_NAME = "agents-invoke"; /** DispatchBus signal prefix a worker is invoked through in the async transport. */ export declare const AGENT_INVOKE_SIGNAL = "agent.invoke"; /** DispatchBus signal a worker emits to report completion, correlated by id. */ export declare const AGENT_COMPLETED_SIGNAL = "agent.completed"; /** * The **per-worker** signal type an async invocation is emitted on, so a * processor only ever claims invocations for the worker class it serves. * * The async transport emits `agent.invoke.` (the class rendered as a * single, provider-safe signal segment) rather than the bare `agent.invoke`. * DispatchBus `process()` claims pending rows by *subscribed signal type* before * a handler can inspect the payload, so a processor targeting worker A * (subscribed to `agent.invoke.`) can never claim a worker-B invocation * (`agent.invoke.`). A generic processor that handles every class subscribes * to the single-segment wildcard `agent.invoke.*`. */ export declare function agentInvokeSignalType(agentClass: string): string; /** * A tool executed under a {@link PrincipalRun} that is not a manifest CRUD * operation — e.g. the orchestration invoke-agent tool. It carries its own AI * definition and handler, and is offered through the conversational tool loop * alongside manifest tools, gated by the same fail-closed `allowedTools`. * * Defined here (in `@happyvertical/smrt-agents`) rather than in the chat loop so * the acyclic `chat → agents` dependency direction is preserved: the loop * imports this contract, agents produces implementations of it. */ export interface PrincipalTool { /** Tool name + permission slug, gated by the persona's `allowedTools`. */ slug: string; /** The provider tool definition offered to the model. */ aiTool: AITool; /** Execute the tool under the principal run (should re-assert its own gate). */ execute(ctx: PrincipalToolContext): Promise; } /** Context handed to a {@link PrincipalTool.execute}. */ export interface PrincipalToolContext { /** The principal run whose context bounds this execution. */ run: PrincipalRun; /** Parsed tool arguments. */ args: Record; /** The database handle for side-door operations. */ db?: SmrtClassOptions['db']; } /** A worker invocation handed to a {@link WorkerRunner}. */ export interface WorkerInvocation { /** The principal run the worker executes within (the delegated principal). */ run: PrincipalRun; /** The delegation envelope (principal, depth, correlation). */ envelope: DelegationEnvelope; /** The target worker agent class. */ agentClass: string; /** The task payload handed to the worker. */ task: Record; /** The database handle for the worker's operations. */ db?: SmrtClassOptions['db']; } /** * Performs a worker's actual work under the delegated principal. Injected so * orchestration stays decoupled from *what* a worker does (run an `Agent`, run a * nested persona conversation, call a domain method); the runner receives a * {@link PrincipalRun} already bound to the originating user's permissions. */ export type WorkerRunner = (invocation: WorkerInvocation) => Promise; /** * A worker's completion, correlated back to the invocation that produced it. */ export interface AgentCompletion { /** Correlates this completion to the invocation. */ correlationId: string; /** The worker agent class that ran. */ agentClass: string; /** The originating user the worker acted on behalf of. */ onBehalfOfUserId: string; /** Whether the worker's work succeeded. */ ok: boolean; /** The worker's result, when it succeeded. */ result?: unknown; /** The error message, when it failed. */ error?: string; } /** The outcome the invoke-agent tool returns to the conversation. */ export interface InvokeAgentResult { /** * `completed` / `failed` for an in-process (inline) invocation whose result is * surfaced in the same turn; `enqueued` for an async transport whose * completion is surfaced later via {@link surfaceAgentCompletions}. */ status: 'completed' | 'failed' | 'enqueued'; /** Correlates a later completion dispatch back to this invocation. */ correlationId: string; /** The worker agent class invoked. */ agentClass: string; /** The delegation depth of the invoked worker. */ depth: number; /** The worker's result, when it completed in-process. */ result?: unknown; /** The error message, when it failed in-process. */ error?: string; } /** A delivery handed to an {@link InvokeAgentTransport}. */ export interface InvokeAgentDelivery { /** The child delegation envelope for the worker. */ envelope: DelegationEnvelope; /** The target worker agent class. */ agentClass: string; /** The task payload for the worker. */ task: Record; /** The worker runner (used by in-process transports; ignored by async ones). */ worker: WorkerRunner; /** The database handle. */ db?: SmrtClassOptions['db']; /** DispatchBus for the correlated invoke/completion signals. */ dispatchBus?: DispatchBus; /** Audit sink forwarded to {@link executeAsPrincipal}. */ audit?: PrincipalAuditSink; /** Opt into Postgres RLS transaction wrapping. */ postgresRls?: boolean; /** Logger for the default audit sink. */ logger?: Logger; } /** * How a worker invocation is delivered: run it in-process now (inline), emit a * DispatchBus signal for a worker to process, or enqueue a job. Swapping the * transport never changes the principal-delegation or completion semantics. */ export interface InvokeAgentTransport { deliver(delivery: InvokeAgentDelivery): Promise; } /** * Run a worker as the delegated principal and report its completion. * * The worker executes inside a single {@link executeAsPrincipal} context bound * to the envelope's principal (`runAsUserId` + `tenantId`) and acting **on * behalf of** the originating user — so its authority is the originating user's * live RBAC and every action audits back to that user. On completion (success * or failure) a correlated `agent.completed` dispatch is emitted **inside** the * principal's tenant context, so it is stamped with the right tenant and the * orchestrator can surface it back into the conversation. */ export declare function executeDelegatedInvocation(options: { envelope: DelegationEnvelope; agentClass: string; task: Record; worker: WorkerRunner; db?: SmrtClassOptions['db']; dispatchBus?: DispatchBus; audit?: PrincipalAuditSink; postgresRls?: boolean; logger?: Logger; }): Promise; /** * Emit a correlated `agent.completed` dispatch for a worker's completion. */ export declare function emitAgentCompletion(dispatchBus: DispatchBus, completion: AgentCompletion): Promise; /** * Read the correlated completions for an invocation, so the orchestrator can * surface a worker's result back into the conversation on a later turn (the * async transport). Returns `[]` when nothing has completed yet. */ export declare function surfaceAgentCompletions(dispatchBus: DispatchBus, correlationId: string): Promise; /** * The default transport: run the worker in-process now and return its completion * as the tool observation, so the result is surfaced back into the conversation * in the same turn. */ export declare const inlineInvokeAgentTransport: InvokeAgentTransport; /** * An async transport that emits a correlated, **per-worker** `agent.invoke.` * DispatchBus signal for a worker to process out of band * ({@link processAgentInvocations}). The tool returns `enqueued`; the worker's * completion is surfaced later via {@link surfaceAgentCompletions}. The worker * runner is *not* used here — it is reconstructed on the processing side. * * Emitting on the per-worker signal type (see {@link agentInvokeSignalType}) * means a processor for worker A never claims an invocation targeted at worker * B, even under compete delivery. */ export declare function createDispatchInvokeTransport(dispatchBus: DispatchBus, options?: { source?: string; }): InvokeAgentTransport; /** * Process pending `agent.invoke` signals, running each worker as its delegated * principal and emitting the correlated completion. This is the worker side of * {@link createDispatchInvokeTransport}. * * Pass `agentClass` to target a single worker class — the processor subscribes * to `agent.invoke.` and can only ever claim that class's invocations, so * running one processor per worker class never cross-claims. Omit it for a * generic processor that handles every class (subscribes to the wildcard * `agent.invoke.*` and dispatches on the payload's `agentClass`). * * The envelope arrives from a (persisted, thus untrusted) dispatch payload, so * it is validated ({@link isValidDelegationEnvelope}) and its depth re-asserted * before the worker runs — a malformed or tampered envelope cannot drive the * chain past {@link MAX_DELEGATION_DEPTH}. * * @returns The number of invocations processed. */ export declare function processAgentInvocations(options: { dispatchBus: DispatchBus; subscriber: string; worker: WorkerRunner; /** Target a single worker class; omit for a handle-every-class processor. */ agentClass?: string; db?: SmrtClassOptions['db']; audit?: PrincipalAuditSink; postgresRls?: boolean; logger?: Logger; limit?: number; }): Promise; /** * Options for {@link createInvokeAgentTool}. */ export interface CreateInvokeAgentToolOptions { /** * The **current run's** delegation envelope — the orchestrator's own (depth * `0`) when building the tool for a conversation, or a worker's own envelope * when building it for a nested/further delegation. Its principal is the * ceiling every child inherits; the live run context is the source of truth * for the principal and overrides this copy. */ parentEnvelope: DelegationEnvelope; /** The worker runner used by in-process transports. */ worker: WorkerRunner; /** Database handle for the worker's operations. */ db?: SmrtClassOptions['db']; /** DispatchBus for correlated invoke/completion signals. */ dispatchBus?: DispatchBus; /** Delivery transport. Defaults to {@link inlineInvokeAgentTransport}. */ transport?: InvokeAgentTransport; /** Audit sink forwarded to {@link executeAsPrincipal}. */ audit?: PrincipalAuditSink; /** Opt into Postgres RLS transaction wrapping. */ postgresRls?: boolean; /** Logger for the default audit sink. */ logger?: Logger; /** Resolve a worker's tool ceiling from its class (e.g. its persona tools). */ resolveWorkerAllowedTools?: (agentClass: string) => string[] | undefined; /** Depth ceiling override (mainly for tests). */ maxDepth?: number; /** Override the tool description offered to the model. */ description?: string; } /** * Build the standard **invoke-agent** tool. * * Offered through the conversational tool loop and gated by the persona's * `allowedTools` (the model may only call it when `agents.invoke` is * allow-listed). Its handler: * * 1. re-asserts the fail-closed allow-list ({@link PrincipalRun.assertToolAllowed}); * 2. derives the child {@link DelegationEnvelope} with the principal taken * **from the live run context** — never from the tool arguments — so a worker * cannot widen the principal, and increments the bounded depth; * 3. delivers the invocation via the configured transport. * * The child inherits the orchestrator's principal verbatim and acts on behalf of * the same originating user, so the worker runs under the originating user's * permissions and audits back to them. */ export declare function createInvokeAgentTool(options: CreateInvokeAgentToolOptions): PrincipalTool; //# sourceMappingURL=invoke-agent.d.ts.map