import { LLMPurposes, LLMToolLoopStopReasons, type LLMCallTraceContext, type LLMProviders, type LLMTerminalToolOutcome, type LLMToolDefinition, type LLMToolResult } from '../LLMService.typedefs'; import { type LLMAssistanceService } from '../services'; import { type LLMLoggerInterface } from '../utilities/logger'; import { type LLMReporterInterface } from '../utilities/reporter'; import { type LLMSchemaInterface } from '../utilities/schema'; import { type LLMToolObservationOptions } from '../utilities/llmTracing'; import { type LLMCapabilityRequirement, type LLMPromptBinding } from '../client/defineLLMPrompts'; import { type LLMVariableValue } from '../client/promptSnapshot.typedefs'; import { type ResolvedPrompt } from '../client/promptRegistry.runtime'; import { type LLMAgentGroundingRequirement, type LLMCallOverrides, type LLMClientCallContext, type LLMDelegationGate, type LLMHistoryMessage, type LLMProviderServiceScope, type LLMReporterInvocation, type LLMToolResultContext, type LLMTraceContextScope } from '../client/llmClient.typedefs'; import { LLMAgent } from '../client/LLMAgent'; import { type LLMAgentEventListener, type LLMAgentFailurePolicy, type LLMAgentRunResult, type LLMAgentSubagentReportCap } from '../client/agentRun.typedefs'; type AgentAssistanceService = LLMAssistanceService | undefined>; type AgentSendResult = { text: string; data?: unknown; parseError?: string; stopReason?: LLMToolLoopStopReasons; terminalTool?: LLMTerminalToolOutcome; }; /** * How one prompt resolution should run: the model-map purpose, extra * capability requirements beyond the binding's own, and per-call overrides. */ export interface LLMPromptResolutionCall { purpose: LLMPurposes; additionalRequires?: LLMCapabilityRequirement[]; overrides?: LLMCallOverrides; } /** * The prompt-resolution and call-plumbing surface the agent runner borrows from * the client engine, so both `generate` and `runAgent` share one gate, one * credentials path, one context shape, and one error taxonomy. */ export interface LLMAgentCallServices { resolvePrompt(promptName: string, binding: LLMPromptBinding, variables: Record, call: LLMPromptResolutionCall): Promise; buildAssistanceService(scope: LLMProviderServiceScope): Promise; buildReporterContext(resolved: ResolvedPrompt, callContext: LLMClientCallContext | undefined, invocation?: LLMReporterInvocation): unknown; buildTraceContext(scope: LLMTraceContextScope): LLMCallTraceContext; toTaxonomyError(error: unknown, resolved: ResolvedPrompt, abortSignal?: AbortSignal): Error; unwrapResult(resolved: ResolvedPrompt, schema: LLMSchemaInterface | undefined, sendResult: AgentSendResult): unknown; getBinding(promptName: string): LLMPromptBinding | undefined; normalizeToolResult(context: LLMToolResultContext): LLMToolResult; withAgentObservation(name: string, input: unknown, fn: () => Promise): Promise; withToolObservation(name: string, input: unknown, fn: () => Promise, options?: LLMToolObservationOptions): Promise; } /** * A plain-object subagent with the registry generics erased: the engine * narrows the typed definition at the `runAgent` boundary, the runner only * needs the runtime shape. */ export interface LLMAgentSubagentSpec { prompt: string; name: string; description: string; variables?: Record; tools?: LLMToolDefinition[]; inputSchema?: LLMSchemaInterface; canExecute?: LLMDelegationGate; reportCap?: LLMAgentSubagentReportCap; grounding?: LLMAgentGroundingRequirement; maxToolIterations?: number; } type SubagentEntry = LLMAgentSubagentSpec | LLMAgent; export interface LLMAgentRunInput { agentName: string; promptName: string; binding: LLMPromptBinding; variables: Record; input: string; history?: LLMHistoryMessage[]; tools?: LLMToolDefinition[]; subagents?: SubagentEntry[]; maxToolIterations?: number; context?: LLMClientCallContext; /** * The Langfuse session every generation of this run belongs to. Copied onto * delegated subagent runs, so one anchor covers the whole run tree. */ sessionId?: string; traceTags?: string[]; abortSignal?: AbortSignal; overrides?: LLMCallOverrides; onEvent?: LLMAgentEventListener; failurePolicy?: LLMAgentFailurePolicy; } /** * Runs one agent: instructions resolved from the prompt registry (Langfuse is * the source of the instructions text and the model routing), the provider * assistance tool loop does the model⇄tool rounds, subagents join as tools, * and every step emits an `LLMAgentEvent` for live progress and persistence. * Langfuse observability needs no extra wiring here — the provider services * already record generation and tool observations under the active trace. */ export declare class LLMAgentRunner { private readonly services; private readonly logger; constructor(services: LLMAgentCallServices, logger: LLMLoggerInterface | undefined); run(run: LLMAgentRunInput): Promise>; private runAgentTree; /** * A terminal tool ends the turn without a model answer, so the bound schema * is deliberately not applied — there is nothing to validate, and unwrapping * would fail a legitimate outcome. Any other stop carries the model's answer * and unwraps exactly like `generate`. */ private buildRunResult; private executeRun; private runInChat; private assistInChat; private buildTools; private wrapToolWithEvents; /** * The single place that decides whether one finished invocation actually did * anything, and the only place the policies watching it are told. A refusal * is a call that did nothing and a delegation the failure policy stopped is * the opposite of a success, so neither resets that tool's streak and * neither grounds the delegation it ran in. */ private static policeToolOutcome; /** * The lifecycle of one tool invocation. The completion event depends on what * the invocation produced: a refused call reports `tool_call_denied` in place * of `tool_call_completed`, so a listener never records a side effect that * did not happen. */ private buildToolCallLifecycle; private executeGatedTool; private executeToolObservation; private resolveToolGate; /** * What a refused call hands back to the model. It names the tool and the * reason so the model can adapt instead of retrying the same call, and it * never passes through the client's normalizer — a denial is the package's * own control flow, not a tool's output. */ private buildDenial; private buildSubagentTool; private buildDelegationLifecycle; private delegateToSubagent; /** * Runs the delegated agent tree and converts the one rejection a stopped * delegation is entitled to swallow: this same call's own abort, raised as * `LLMAbortedError` once `systemicFailureController` fires. The shared * `abortSignal` can also be aborted by an unrelated sibling delegation, so a * rejection that arrives while `abortSignal.reason` happens to be a * `SystemicToolFailureError` is not, by itself, proof that this delegation * failed for that reason — network or auth errors must still propagate as * themselves. */ private runDelegatedAgent; /** * A typed delegation hands the whole argument object to the subagent as its * request, since the parent model filled a schema rather than writing a * task sentence. Without a schema the single `prompt` argument is the * request, exactly as before. */ private buildDelegationInput; /** * Typed delegation arguments join the subagent's own variables (and win over * them), so the delegated Langfuse prompt can reference what the parent model * supplied as `{{argument}}` and the validation gate covers it like any other * variable. */ private buildDelegationVariables; /** * Flattens a nested run back into the string its delegation tool returns to * the parent model. A delegation tool is never itself terminal, so a * subagent that stopped on its own terminal tool hands that tool's output * back as the delegation result and the parent's loop continues. * * A prose report is noted before it is capped, so a specialist cannot bury * the grounding warning under a report long enough to be truncated. A * structured one is noted after, because over the cap it is replaced by an * envelope that a prefix counted into the budget would cut back open — and * only for the ungrounded check, since citation checking reads prose (see * `AgentGroundingPolicy.noteUnreadCitations`). */ private stringifySubagentOutcome; private static capReportText; /** * A structured report is never cut where it stands: half a JSON document * parses as nothing while still looking structured to whatever reads it * next. Over the cap it is replaced by a valid envelope that says the report * was too long and carries an excerpt short enough to survive being escaped * into it. */ private static capStructuredReport; private normalizeSubagent; private withLifecycleEvents; /** * Emit an `agent_message` for visible assistant text the model produced * between tool rounds. Attributed to the emitting run via `agent`/`runId`, * so a subagent's narration carries the subagent's identity. Empty or * whitespace-only text is skipped — an `agent_message` always carries real * narration. */ private emitAgentMessage; private emit; private warnListenerFailure; } export {};