/** * Agent — ReAct primitive (LLM + tools + iteration loop). * * Pattern: Builder (GoF) → produces a Runner backed by a footprintjs FlowChart. * Role: Layer-5 primitive (core/). Assembles the 3-slot context * pipeline + callLLM + route decider + tool-calls subflow + * loopTo. Composition nestable anywhere that accepts a Runner. * Emits: Via internal recorders: * agentfootprint.agent.turn_start / turn_end * agentfootprint.agent.iteration_start / iteration_end * agentfootprint.agent.route_decided * agentfootprint.stream.llm_start / llm_end * agentfootprint.stream.tool_start / tool_end * agentfootprint.context.* (via ContextRecorder) */ import { type CombinedNarrativeEntry, type FlowchartCheckpoint, type ObserverDrainResult, type RunOptions, type RuntimeSnapshot } from 'footprintjs'; import type { CachePolicy, CacheStrategy } from '../cache/types.js'; import type { ReliabilityConfig } from '../reliability/types.js'; import { type RunnerPauseOutcome } from './pause.js'; import { type CheckInBuilderOptions } from './checkin.js'; import type { MemoryDefinition } from '../memory/define.types.js'; import type { Injection, InjectionContext } from '../lib/injection-engine/types.js'; import type { EntryScoring } from '../lib/injection-engine/skillGraph.js'; import { type ResolvedOutputFallback } from './outputFallback.js'; import { type AgentRunCheckpoint } from './runCheckpoint.js'; import { type OutputSchemaParser } from './outputSchema.js'; import { RunnerBase } from './RunnerBase.js'; import type { ToolRegistryEntry } from './tools.js'; import type { ToolProvider } from '../tool-providers/types.js'; import type { AgentInput, AgentOptions, AgentOutput, ObserverDeliveryOptions } from './agent/types.js'; import { AgentBuilder } from './agent/AgentBuilder.js'; import type { ThinkingHandler } from '../thinking/types.js'; export { AgentBuilder }; export type { AgentInput, AgentOptions, AgentOutput, ObserverDeliveryOptions }; /** * `RunOptions` (footprintjs) + agentfootprint-domain correlation fields. * * `correlationId`/`traceId` are NOT footprintjs concepts — footprintjs's * `RunOptions.env` is an intentionally closed infra bag (signal/timeoutMs/ * traceId only; see footprintjs's `ExecutionEnv`). These two ride separately * into `Agent.currentRunContext` and from there into every emitted event's * `EventMeta` via `buildEventMeta` (`RunContext` already declares both — * `../bridge/eventMeta.ts`), so a caller can join agentfootprint's event * stream against an external system (a upstream request id, an OTEL trace, * a cross-tier why() join key) without threading it through tool args. * * `traceId` here wins over `env.traceId` when both are set; `env.traceId` * remains a fallback since footprintjs already threads it to subflows. */ export interface AgentRunOptions extends RunOptions { /** Domain correlation id — forwarded onto every emitted event's `EventMeta.correlationId`. */ correlationId?: string; /** OTEL-style trace id — forwarded onto every emitted event's `EventMeta.traceId`. Falls back to `options.env?.traceId` when unset. */ traceId?: string; } export declare class Agent extends RunnerBase { readonly name: string; readonly id: string; private readonly provider; private readonly model; private readonly temperature?; private readonly maxTokens?; private readonly maxIterations; private readonly systemPromptValue; /** * Cache policy for the base system prompt (set via * `.system(text, { cache })`). Default `'always'` — base prompt is * stable per-turn, ideal cache anchor. CacheDecision subflow reads * this when computing the SystemPrompt slot's cache markers. */ private readonly systemPromptCachePolicy; /** * Global cache kill switch from `Agent.create({ caching: 'off' })`. * Threaded into agent scope at seed-time as `scope.cachingDisabled`; * read by the CacheGate decider every iteration (highest-priority rule). */ private readonly cachingDisabledValue; /** * Provider-specific CacheStrategy. Auto-resolved from * `getDefaultCacheStrategy(provider.name)` at agent build time * unless the consumer explicitly passes one via builder option. * Phase 7+ implementations (Anthropic, OpenAI, Bedrock) register * themselves in the strategyRegistry on import. */ private readonly cacheStrategy; private readonly registry; /** * The Injection list — Skills, Steering, Instructions, Facts (and * RAG, Memory). Evaluated each iteration by the * InjectionEngine subflow; active set is filtered by slot subflows. */ private readonly injections; /** Skill-graph cursor resolver (`graph.nextSkill`), set when built via * `.skillGraph(graph)`. Plumbed into the Injection Engine so route triggers * are `from`-gated against the persisted `currentSkillId`. */ private readonly skillGraphNextSkill?; /** Skill-graph reachable-set resolver (`graph.reachableSkills`), set when built * via `.skillGraph(graph)`. Plumbed into the tool-calls handler so `read_skill` * is gated to in-graph jumps. Undefined → gate off. */ private readonly skillGraphReachable?; /** Skill-graph relevance entry scorer (`graph.scoreEntries`), set when built via * `.skillGraph(graph)` with `.entryByRelevance()`. Drives the PickEntry stage. * Undefined → no relevance entry routing (cold-start entry as before). */ private readonly skillGraphScoreEntries?; private readonly pricingTable?; private readonly costBudget?; private readonly permissionChecker?; private readonly toolArgValidation?; /** Resolved check-in config (evidence-carrying human consent). Always * present — defaults to `standard` evidence + the lexical scorer, so a tool * that declares `checkIn` works even without a `.checkIn()` builder call. */ private readonly checkInConfig; /** Snapshot read-tracking policy (#18/#14) — forwarded to the internal * executor. Agent default is `'summary'` (cheap markers), NOT * footprintjs's `'full'`. See AgentOptions.readTracking. */ private readonly readTracking; /** Commit-log value encoding (#13c-B) — forwarded to the internal * executor. Agent default is `'delta'` (append/delete verbs; growing * arrays like `history` record only their tails — lossless, linear * retained memory), NOT footprintjs's `'full'`. See * AgentOptions.commitValues. */ private readonly commitValues; private readonly credentialProvider?; /** Evidence bridge (#5) — present iff a CAUSAL memory is mounted. */ private readonly causalEvidence?; /** Observer delivery tier (RFC-001 Block 10). `'inline'` (default) is * byte-identical to pre-10 releases; `'deferred'` routes the bridge * recorders + consumer attachments through footprintjs's bounded * capture queue. See AgentOptions.observerDelivery. */ private readonly observerDelivery; /** Queue dials forwarded on every deferred attach (first attach * configures the executor's single dispatcher). */ private readonly observerDeliveryOptions?; /** * Voice config — shared by viewers (Lens, ChatThinkKit, CLI tail). * `appName` is the active actor in narration ("Chatbot called…"). * `commentaryTemplates` drives Lens's third-person panel. * `thinkingTemplates` drives chat-bubble first-person status. * Defaults to bundled English; consumer overrides via builder. */ readonly appName: string; readonly commentaryTemplates: Readonly>; readonly thinkingTemplates: Readonly>; private currentRunContext; /** * Memory subsystems registered via `.memory()`. Each definition mounts * its `read` subflow before the InjectionEngine on every turn; per-id * scope keys (`memoryInjectionKey(id)`) keep multi-memory layering * collision-free. */ private readonly memories; /** * Optional terminal contract. Set via the builder's `.outputSchema()`. * When present, `agent.runTyped()` parses + validates the final * answer against this parser. `agent.run()` keeps returning the * raw string; consumers opt into typed mode explicitly. */ private readonly outputSchemaParser?; /** * Optional 3-tier degradation for output-schema validation * failures. Set via the builder's `.outputFallback({...})`. When * present, `parseOutput()` and `runTyped()` fall through: * primary → fallback → canned (in order; canned guarantees no-throw). */ private readonly outputFallbackCfg?; /** Side-channel for `resumeOnError(...)` — when set, the seed * function restores `scope.history` from this instead of starting * fresh. Cleared on first read so subsequent runs start clean. */ private pendingResumeHistory?; /** * Optional `ToolProvider` set via the builder's `.toolProvider()`. * When present, the Tools slot subflow consults it per iteration * (Block A5 follow-up) — the provider's tools land alongside any * tools registered statically via `.tool()` / `.tools()`. The * tool-call dispatcher also consults it for per-iteration execute * lookup so dynamic chains (`gatedTools`, `skillScopedTools`) * dispatch correctly when their visible-set changes mid-turn. */ private readonly externalToolProvider?; /** * Optional rules-based reliability config (v2.11.5+). Set via the * builder's `.reliability({...})`. When present, every CallLLM * execution is wrapped in a retry/fallback/fail-fast loop driven * by `preCheck` and `postDecide` rules. Consumed by `buildCallLLMStage`. */ private readonly reliabilityConfig?; /** * Resolved ThinkingHandler (v2.14+). Auto-wired by `provider.name` * via `findThinkingHandler` UNLESS the builder explicitly set one * (or `null` to opt out). When undefined, the NormalizeThinking * sub-subflow is NOT mounted at chart build time — zero overhead * for non-thinking agents. */ private readonly thinkingHandler?; /** * v2.14+ — request-side thinking budget. When set, every LLMRequest * carries `thinking: { budget }`. AnthropicProvider translates to the * wire format. Undefined = no thinking activation (default behavior). */ private readonly thinkingBudget?; /** Threaded to footprintjs `flowChart()` so every node the Agent * builder creates is observed by these recorders at build time. Set * from `opts.structureRecorders`; undefined when consumer didn't * attach any. */ private readonly structureRecorders?; /** Per-COMPOSITION translator (L1b). Set from `opts.groupTranslator`; * undefined when consumer didn't attach one. */ private readonly agentGroupTranslator?; /** ReAct loop mode — 'dynamic' (default, re-engineer all slots each turn, * flat chart), 'classic' (engineer context once, loop→Messages only, flat * chart), or 'dynamic-grouped' (dynamic semantics + LLM turn wrapped in an * sf-llm-call subflow for richer Lens grouping). Set from `opts.reactMode`. * See AgentOptions. */ private readonly reactMode; constructor(opts: AgentOptions, systemPromptValue: string, registry: readonly ToolRegistryEntry[], voice: { readonly appName: string; readonly commentaryTemplates: Readonly>; readonly thinkingTemplates: Readonly>; }, injections?: readonly Injection[], memories?: readonly MemoryDefinition[], outputSchemaParser?: OutputSchemaParser, toolProvider?: ToolProvider, systemPromptCachePolicy?: CachePolicy, cachingDisabled?: boolean, cacheStrategy?: CacheStrategy, outputFallbackCfg?: ResolvedOutputFallback, reliabilityConfig?: ReliabilityConfig, thinkingHandlerValue?: ThinkingHandler | null, thinkingBudgetValue?: number, skillGraphNextSkill?: (ctx: InjectionContext) => string | undefined, skillGraphReachable?: (currentSkillId?: string) => readonly string[], skillGraphScoreEntries?: (ctx: InjectionContext, signal?: AbortSignal) => Promise, checkInOptions?: CheckInBuilderOptions); static create(opts: AgentOptions): AgentBuilder; /** * Cache policy for the base system prompt. Read by the CacheDecision * subflow (v2.6 Phase 4) to know how to treat the SystemPrompt slot's * cache markers. Exposed as a method (not direct field access) so * the Agent's encapsulation boundary stays clean. */ getSystemPromptCachePolicy(): CachePolicy; /** * The footprintjs `RuntimeSnapshot` from the most recent `run()` / * `resume()`. Feeds Lens's Trace tab (ExplainableShell `runtimeSnapshot` * prop) so consumers can scrub the execution timeline post-run without * threading a recorder through the call site. * * Returns `undefined` before the first run completes. Returns the * snapshot of the most recent run on every call after — including * across multiple turns of the same Agent instance. */ getLastSnapshot(): RuntimeSnapshot | undefined; /** * Structured narrative entries from the most recent run. Pairs with * `getLastSnapshot()` for ExplainableShell's `narrativeEntries` prop. * Empty array (not `undefined`) when no run has completed — matches * the prop's expected shape so consumers can wire it directly without * a defensive guard. */ getLastNarrativeEntries(): readonly CombinedNarrativeEntry[]; /** * The FlowChart compiled for the most recent run (or a freshly-built * one if no run has happened yet). Feeds ExplainableShell's `spec` * prop. Returning the cached chart matters: the spec must match what * `getLastSnapshot()` traced, otherwise the Trace view's stage tree * desyncs from the snapshot's runtime tree. */ protected getGroupTranslator(): import('./translator.js').GroupTranslator | undefined; /** Agent has no nested-runner members (tools are function executors, * not Runner instances). Slot ids + tool names live in `extra` so * Lens can render an Agent card with slot rows + a tool list without * inspecting `buildTimeStructure`. * * Memories are NOT included as members — they're an internal * mechanism, not a composition-level concept. Consumers who need * memory visibility should listen for `agentfootprint.memory.*` * events at runtime. */ protected buildUIGroupMetadata(): import('./translator.js').GroupMetadata; /** * Parse + validate a raw agent answer against the agent's * `outputSchema` parser. Throws `OutputSchemaError` on JSON parse * or schema validation failure (the rawOutput is preserved on the * error for triage). Throws a plain `Error` if the agent has no * outputSchema set. * * Use this when you need to keep `agent.run()` returning the raw * string for logging/observability and validate at a different * layer; otherwise prefer `agent.runTyped()`. */ parseOutput(raw: string): T; /** * Async sister of `parseOutput()`. When the agent is configured * with `.outputFallback({...})`, this is the version that engages * the 3-tier degradation chain on validation failure (the sync * `parseOutput` always throws on failure for back-compat). * * Without `outputFallback`, behaves identically to `parseOutput` * — returns sync-style on the happy path, throws OutputSchemaError * on validation failure. */ parseOutputAsync(raw: string): Promise; /** * Run the agent and return the schema-validated typed output. * Convenience over `parseOutputAsync(await agent.run({...}))`. * * Throws `OutputSchemaError` on parse / validation failure UNLESS * `.outputFallback({...})` is configured, in which case the * 3-tier degradation chain (primary → fallback → canned) engages. * * Throws if the agent has no outputSchema set or if the run * pauses (use `run()` directly when pauses are expected). */ runTyped(input: AgentInput, options?: AgentRunOptions): Promise; run(input: AgentInput, options?: AgentRunOptions): Promise; /** * Resume an agent run from a checkpoint produced by a prior * `RunCheckpointError`. Unlike `agent.resume()` (which takes a * `FlowchartCheckpoint` from an intentional pause), this takes * an `AgentRunCheckpoint` (conversation-history snapshot) and * replays the agent run with that history restored. * * The next iteration retries the call that originally failed — * with the latest provider state (circuit breaker may have * closed, vendor may have recovered, etc.). * * **Resume = REPLAY from the last completed iteration boundary, * not exact-state restore.** Only the conversation history is * restored; everything else re-seeds fresh: * * - **Tool re-execution / idempotency**: tool side effects from * the FAILED iteration are not in the checkpoint. The model * re-decides from the restored history and may re-issue those * tool calls — they WILL execute again (there is no built-in * toolCallId dedup). Mutating tools (payments, emails, DB * writes) must be idempotent — key on stable call content, not * `ctx.toolCallId` (a re-issued call gets a new id). * - **Fresh `runId`**: the resumed run's events carry a new * `runId`; use `checkpoint.runId` to correlate back to the * failing run. * - **Iteration counter + budget reset**: the resumed run starts * at iteration 1 with a full `maxIterations` budget * (`checkpoint.lastCompletedIteration` is diagnostic only). * Token/cost accumulators also restart at zero. * * @example * ```ts * try { * const result = await agent.run({ message: 'long task' }); * } catch (err) { * if (err instanceof RunCheckpointError) { * await checkpointStore.put(sessionId, err.checkpoint); * // hours / restart later: * const checkpoint = await checkpointStore.get(sessionId); * const result = await agent.resumeOnError(checkpoint); * } * } * ``` */ resumeOnError(checkpoint: AgentRunCheckpoint | unknown, options?: AgentRunOptions): Promise; /** * Install a per-run checkpoint tracker. Listens for the agent's * own iteration_end events on `this.dispatcher` and snapshots the * conversation history into the tracker. Returns a stop function. * * @internal */ private installCheckpointTracker; resume(checkpoint: FlowchartCheckpoint, input?: unknown, options?: AgentRunOptions): Promise; private createExecutor; /** * Flush the deferred-observer backlog of the most recent run's executor, * then await async listener completions under a deadline (RFC-001 §11 — * the serverless / graceful-shutdown pattern). Resolves immediately with * zeros before the first run or when `observerDelivery` is `'inline'` * and no recorder opted into `'deferred'` itself. * * `pending === 0` means a full drain; non-zero honestly reports * continuations still outstanding at the deadline — never silent loss. * * @example Lambda-style handler * ```ts * export const handler = async (event) => { * const reply = await agent.run({ message: event.message }); * // settle "one beat behind" observer work BEFORE the freeze: * await agent.drainObservers({ timeoutMs: 5_000 }); * return reply; * }; * ``` */ drainObservers(opts?: { timeoutMs?: number; }): Promise; private finalizeResult; private buildChart; }