/** * 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 { ArtifactStore } from '../artifacts/types.js'; import type { SkillGraphDeclaredMap } from './agent/skillGraphDeclared.js'; import type { AppliedRecipe } from '../recipes/types.js'; import type { WindowStrategy } from './agent/window/strategy.js'; import { type CheckInBuilderOptions } from './checkin.js'; import type { MemoryDefinition } from '../memory/define.types.js'; import type { MemoryIdentity } from '../memory/identity/types.js'; import type { SelfExplainBinding } from '../lib/trace-toolpack/selfExplain.js'; import type { ResolvedEvidenceGate } from './agent/evidence/types.js'; import type { Injection, InjectionContext } from '../lib/injection-engine/types.js'; import type { CursorMove, EntryScoring, TurnRoutingPlan } 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 type { ResolvedOutputEnforcement } from './agent/outputEnforcement.js'; import { RunnerBase } from './RunnerBase.js'; import type { ToolRegistryEntry } from './tools.js'; import type { ToolProvider } from '../tool-providers/types.js'; import type { AgentArtifactsOptions, AgentInput, AgentOptions, AgentOutput, AgentRecordingsOptions, AgentState, ObserverDeliveryOptions, RunConfig, RunConfigContext, RunConfigFn, WriteProvenanceMode } from './agent/types.js'; import type { MessageMiddleware, ToolMiddleware } from './agent/middleware/types.js'; import { AgentBuilder } from './agent/AgentBuilder.js'; export type { SkillGraphOptions } from './agent/AgentBuilder.js'; export type { ProviderChoice, EscalationPolicy } from './agent/skillBrains.js'; import type { ThinkingHandler } from '../thinking/types.js'; export { AgentBuilder }; export type { AgentArtifactsOptions, AgentInput, AgentOptions, AgentOutput, AgentRecordingsOptions, ObserverDeliveryOptions, RunConfig, RunConfigContext, RunConfigFn, WriteProvenanceMode, }; /** * `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; /** * The hosting CONVERSATION this run belongs to (9.4.0) — forwarded onto * every emitted event's `EventMeta.sessionId`, and from there into whatever * an observability strategy ships (the CloudWatch/AgentCore adapters * serialize the whole envelope, so it arrives without their knowing). * * `runId` is per run; a session spans many. Without this, a shipped event * stream can answer "what happened in this run?" and not "what happened in * this conversation?", which is the question a session-oriented host is * built around. `standingAgent` sets this for you from the request's own * session id, on both `run()` and `resume()`. * * **It also decides the memory namespace when you named no identity * (9.10.0):** a run with a session and no `identity` scopes its memory to * `{ conversationId: sessionId }`, because a hosting session IS a * conversation. An `identity` you pass always wins. See * {@link AgentInput.identity} for the full ladder. * * Omit it for an unhosted run. It is never derived, guessed, or defaulted to * the runId: an absent session and an invented one are different facts. */ sessionId?: string; /** * Who this run is for — the same tuple as `run({ identity })`, reachable * from the doors whose input is a stored conversation rather than a * message bag: `resumeOnError(checkpoint, { identity })` and * `followUp(message, { identity })` (9.2.0). * * Before this existed, `resumeOnError` could not carry an identity at all, * so every continued turn silently re-namespaced its memory under a fresh * runId. Omitted, the conversation's own stored `identity` is used; given, * it wins. On `run()` this is a second spelling of `run({ identity })` and * the one on the input wins, since that is where the caller looked first. */ identity?: MemoryIdentity; } 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?; /** The `to` end of every edge the mounted graph declares — which skills the graph * WIRES. Empty for a graph-less agent. Read only by `openSkillIds()`. */ private readonly skillGraphEdgeTargets; /** Skill-graph cursor resolver that also reports WHICH CLAUSE won * (`graph.explainNextSkill`, 8.5.0). Threaded to the Injection Engine, which * stamps the result on `context.evaluated` as `cursorMove`. Optional — a graph * built before it existed falls back to `nextSkill` and emits no `cursorMove`. */ private readonly skillGraphExplainNextSkill?; /** Skill-graph suppression reporter (`graph.supersededEntries`, 8.15.0) — the * conditional entries whose `when` matched while the cursor was elsewhere. * Threaded to the Injection Engine, which stamps them on `context.evaluated` as * `supersededIds`. Optional — a graph built before it existed routes identically * and simply emits no `supersededIds`. */ private readonly skillGraphSupersededEntries?; /** Is the mounted graph a decision `tree()`? Derived at build time from * `graph.nodes` (a tree is the only shape with `predicate` nodes) — no new * public field on `SkillGraph`. Read for ONE thing: the read_skill gate's * refusal has to explain that a tree has no cursor to jump (8.5.0). */ private readonly skillGraphIsTree; /** The turn-start cascade wiring (SG-C, 9.17.0) — the graph's routing plan, * the mount's posture/continuity, and the node-id set droppedResume checks * against. Absent (every graph without the new options) → RouteTurn never * mounts, the gate never postures, seed never restores a cursor, and the * checkpoint shape is byte-identical. */ /** The folded per-skill brains + escalation + decider (9.19.0) — * validated at `AgentBuilder.build()`. Undefined = no brain anywhere: * callLLM, the gate, seed and RouteTurn wire nothing new. */ private readonly skillBrains?; /** * The evidence gate (9.35.0) — `.namesAndNumbersFromEvidence()`, resolved by * the builder. Undefined for every agent that did not ask for it, and that * undefined is the whole zero-cost guarantee: no decider change, no branch, * no scope key, no event. */ private readonly evidenceGate?; /** * `.limitsTravelWithTheAnswer()` (this release) — whether the run's declared * coverage is folded into the final answer. False for every agent that did * not ask for it; the RECORDING half of the coverage primitives does not * consult it. */ private readonly limitsTravelWithTheAnswerValue; private readonly skillGraphCascade?; /** Side channel for the conversation's inherited skill cursor (SG-C) — * stashed by `applyContinuation`, consumed-and-cleared by seed. */ private pendingResumeSkillCursor?; private readonly pricingTable?; /** Normalized at construction: a bare number is `{ usd, onExceed: 'warn' }`. */ private readonly costBudget?; /** Per-slot character budgets (8.11.0). Absent keys keep the slot default. */ private readonly contextBudget?; private readonly permissionChecker?; private readonly toolArgValidation?; /** The opt-in tool-result ceiling in characters (9.11.0). Absent → results * are never measured. See {@link AgentOptions.maxToolResultChars}. */ private readonly maxToolResultChars?; /** 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; /** Per-run config resolver from `.configure()`. Undefined for every agent * that never called it — and the chart is then built exactly as before, * with no scope writes and no scope reads added. */ private readonly runConfigFn?; /** The agent's one window strategy. Undefined = no window stage, loop * target unchanged, run byte-identical to an agent without it. */ private readonly windowStrategy?; /** The tool-dispatch chain (`.toolMiddleware()`), in declaration order. * Empty for every agent that never called it — and then the dispatch loop * never walks a chain, never writes the ledger key, and never emits. */ private readonly toolMiddleware; /** The message chain (`.messageMiddleware()`), in declaration order. Empty * keeps seed synchronous and prepare-final untouched. */ private readonly messageMiddleware; /** The instrument the window stage reads mid-run (adapter-reported usage + * per-message provenance). Only ever created alongside a strategy. */ private readonly compactionMeterHandle?; /** 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; /** Per-write read provenance (#P1) — forwarded to the internal executor. * Default `'off'` (footprintjs's own): recordings stay byte-identical * unless a consumer opts in. See AgentOptions.writeProvenance. */ private readonly writeProvenance; private readonly credentialProvider?; /** The claim-check store (9.21.0). When set, every tool's `ctx.artifacts` * is this store bound to the run's scope. See AgentOptions.artifacts. */ private readonly artifactStore?; /** The placement threshold (9.22.0) — the operator's ref-ing dial from the * object form of AgentOptions.artifacts. Only ever set beside a store. */ private readonly artifactPlacement?; /** Recordings-as-artifacts (9.26.0) — the operator's dial from the object * form of AgentOptions.artifacts. Only ever set beside a store; absent * means no recorder is ever attached and no run is ever recorded. */ private readonly artifactRecordings?; /** The repeated-call nudge (9.26.0) — `false` only when the operator turned * it off. See AgentOptions.repeatedCallNudge. */ private readonly repeatedCallNudge?; /** The out-of-budget wrap-up (9.56.0) — `false` only when the operator * turned it off. See AgentOptions.wrapUpAtMaxIterations. */ private readonly wrapUpAtMaxIterations?; /** The last-tool-result pin (9.57.0) — set only when the operator named a * value other than the default 2. See AgentOptions.keepLastToolResults. */ private readonly keepLastToolResults?; /** See AgentOptions.integrityPosture (9.60.0). Default 'observe'. */ private readonly integrityPosture; /** * The per-run disposition ledger, shared with the check sites by * REFERENCE through build-time closures (the ProviderToolCache pattern — * plumbing, never scope state). `run()` resets it; the run boundary * files its report and clears it. */ private readonly integrityLedgerHolder; /** Set at chart build: whether any tool in the FULL declared catalog * (static registry + skill-carried tools) declared `argumentsFrom`. */ private integrityDanglingPresent; /** See AgentOptions.noticeEmptyLookups (9.77.0). Default false — absent is * byte-identical, save for the registered not-applicable ledger row. */ private readonly noticeEmptyLookups; /** See AgentOptions.noticePriorTurnEvidence (9.83.0). Default false — * absent is byte-identical, save for the registered not-applicable ledger * row. */ private readonly noticePriorTurnEvidence; /** Set at chart build: whether any tool in the FULL declared catalog * declared `resultColumns` (9.78.0) — the other half of the column-type * contract's arming. */ private integrityColumnsPresent; /** See AgentOptions.checkColumnTypes (9.78.0). Default 'off' — absent is * byte-identical, save for the registered not-applicable ledger rows. */ private readonly checkColumnTypes; /** See AgentOptions.externalGrounds (9.72.0). Absent = door closed, * byte-identical behavior. */ private readonly externalGrounds?; /** What a run does when a declared credential needs 3LO consent (8.6.0). * Default `'pause'`. See AgentOptions.onAuthorizationRequired. */ private readonly onAuthorizationRequired; /** * Consent blocks outstanding in THIS run, keyed by service — the * `'tell-model'` honesty ledger, and the only place the authorization URL * lives between the block and the caller. * * It is a plain instance field rather than a scope key on purpose: tracked * state is the commit log, which is the snapshot, the narrative and every * recording, and this value is a bearer capability. Cleared at the top of * every `run()` / `resume()` so one run can never raise on another's block, * and cleared per service the moment that credential is issued. */ private readonly consentOutstanding; /** 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?; /** * What the LOOP enforces about the output (7.26) — resolved by the builder * when `.outputSchema()` was given `retries` or a `'tool-forced'` strategy. * Undefined otherwise, and undefined is the whole of the byte-identical * path: no branch is mounted, the decider is the function it always was, * and the request is the one 7.25 sent. */ private readonly outputEnforcement?; /** * 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?; /** Its sibling for the folded spans. A restored conversation that dropped * them would carry summaries nobody could unpack — the evidence would be * destroyed by the act of continuing, which is the one thing retention * exists to prevent. Cleared on first read, exactly like the history. */ private pendingResumeFolded?; /** The last completed run's final answer — see `checkpoint()` for why it is * kept here rather than read back from the recording. Undefined after a run * that failed or paused. */ private lastRunAnswer?; /** The id the CONSUMER chose, or undefined when they took the default. * `this.id` cannot answer that question — it is `'agent'` either way — and * the stored-conversation fingerprint refuses only on ids somebody picked * (see `AgentRunCheckpoint.agent`). */ private readonly explicitId?; /** The identity the caller gave the last run, or undefined when they gave * none. Only an EXPLICIT identity is carried onto `checkpoint()`: the * default is derived from a runId, and storing that would pin a whole * conversation to the id of the one run that started it. */ private lastRunIdentity?; /** How long ONE tool teardown may take before the runner stops waiting. * See `AgentOptions.toolTeardownTimeoutMs`. */ private readonly toolTeardownTimeoutMs; /** The run in flight, by id — the whole of the one-turn-at-a-time guard. * Set before the executor is built and cleared in `finally`, so a run that * throws does not leave the agent permanently refusing. */ private inFlightRunId?; /** The question a person still owes this agent an answer to. Set when a run * ends paused, cleared by `resume()`, `abandonPause()`, or a run that * completes. Read by the `run()` guard — see `PendingQuestionError`. */ private pendingQuestion?; /** The `.selfExplain()` binding, when the builder mounted one. Held so * `canExplain()` can answer the same question the trace tools answer, from * the same fact. Undefined on every agent that never called `.selfExplain()`. */ private selfExplainBinding?; /** * 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; /** The recipes `.recipe()` applied, in declaration order. Held for ONE * purpose: the run manifest's `recipes` rows, so a recording can say which * composition produced the agent that answered. Undefined — never `[]` — on * every agent built without one, which is what keeps the manifest of an * agent that uses no recipes byte-identical to the one it emitted before * they existed. */ private readonly appliedRecipes?; /** The DECLARED skill map (9.50.0) — nodes + edges verbatim, captured by * `AgentBuilder.skillGraph()`. Filed once per run as * `agentfootprint.skill.graph_declared`; undefined = no graph, or a graph * that could not state its map (the event then never fires). */ private readonly skillGraphDeclared?; /** The maps kernel's plan (9.58.0) — present only when built with `.maps()`. */ private readonly mapsPlan?; /** `.claims()` (9.61.0) — the declared claim contract, or undefined. */ private readonly claimContract?; /** `AgentOptions.recordSystemPrompt` (9.50.0) — OFF by default. When true, * every `stream.llm_start` carries the assembled system prompt verbatim as * `systemPromptText`. */ private readonly recordSystemPromptValue; /** `AgentOptions.recordReceipt` (9.88.0) — ON by default. `false` declines * the mint at `callLLM`; nothing else about the run changes. */ private readonly recordReceiptValue; 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, runConfigFn?: RunConfigFn, windowStrategy?: WindowStrategy, toolMiddleware?: readonly ToolMiddleware[], messageMiddleware?: readonly MessageMiddleware[], outputEnforcement?: ResolvedOutputEnforcement, skillGraphEdgeTargets?: readonly string[], skillGraphExplainNextSkill?: (ctx: InjectionContext) => CursorMove, skillGraphIsTree?: boolean, skillGraphSupersededEntries?: (ctx: InjectionContext) => readonly string[], skillGraphCascade?: { readonly turnRouting?: TurnRoutingPlan; readonly strictness: 'assist' | 'guard' | 'rails'; readonly continuity: 'turn' | 'conversation'; readonly nodeIds: ReadonlySet; }, skillBrains?: import('./agent/skillBrains.js').FoldedSkillBrains, evidenceGate?: ResolvedEvidenceGate, limitsTravelWithTheAnswer?: boolean, recipes?: readonly AppliedRecipe[], skillGraphDeclared?: SkillGraphDeclaredMap, mapsPlan?: import('../maps/engagement/types.js').EngagementPlan, claimContract?: readonly import('../integrity/unsupported-claim/check.js').DeclaredClaim[]); 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 artifact store this agent was built with, or `undefined` when none * was attached (9.23.0). * * For COMPOSERS that resolve refs on the agent's behalf — the hosting * layer's `artifact-head` / `artifact-get` wire operations redeem a * screen's claim tickets against exactly this store. It is the store, not * a scope-bound capability: whoever calls it owns composing the resolution * scope (the hosting layer composes the requesting session's identity, the * same tuple the run's own tools resolved under). Tools never touch this — * `ctx.artifacts` is already bound to the run's scope, and that remains * their only door. */ getArtifactStore(): ArtifactStore | undefined; /** * Start recording this run — or do nothing at all (9.26.0). * * Zero-cost when unused is not a claim here, it is the control flow: with * `recordings` unset this returns `undefined` before touching the agent, so * no listener is subscribed, no boundary recorder is attached, and the run * is byte-identical to every earlier release. * * It is deliberately the SAME `recordRun` a consumer would call by hand. * Nothing about this feature is a second recording implementation — the * three connections that a hand-rolled version gets wrong (attach, * subscribe, getCommitCount) are wired in exactly one place in this package, * and this is a caller of it. */ private startRunRecording; /** * File the finished recording into the artifact store. * * ── When ──────────────────────────────────────────────────────────────── * After the answer is composed, and only for a run that COMPLETED. A pause * is not a finished run (the turn continues, and the resume mints its own); * a throw never reaches here at all. * * ── Why it is awaited ─────────────────────────────────────────────────── * The answer is final before this begins and this cannot change it — but * `run()` does return after the write rather than before, and that is a * choice rather than an oversight. A fire-and-forget write is a recording * lost whenever the process exits with the reply, which is precisely the * serverless deployment that wants recordings most. The cost is one store * write per turn, stated on the option. * * ── Why it can never fail the run ─────────────────────────────────────── * A full store, an unserializable snapshot, a bucket that 500s — none of * them are facts about the ANSWER, which is already correct and already * paid for. Turning "your recording was not filed" into "your request * failed" would be the library deciding that its observability matters more * than the user's turn. So every failure degrades to today's path: the * answer is returned unchanged and the reason lands on the record as * `agentfootprint.artifacts.refused`, where a sink can count it. * * The recording is FROZEN before the mint, so it can never contain the * `artifacts.minted` event describing itself. */ private fileRunRecording; /** One refusal fact for a recording that could not be filed. The message is * the store's own, which never carries a payload — only what went wrong. */ private reportRecordingRefused; /** * The run's artifact scope, read from the finished run's own state. * * The SAME tuple `ctx.artifacts` bound during the run (`scope.runIdentity`, * composed by seed from the caller's identity or derived from the session). * Read rather than recomposed: a second derivation here could disagree with * the one the run's tools used, and a recording filed in a different scope * from the artifacts it describes is a recording nobody can find. */ private runArtifactScope; /** * 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. * * `undefined` until a run has STARTED. After that it is the most recent * run's snapshot — including across multiple turns of the same instance. * * **It is LIVE during a run, not a completed-runs-only view.** The executor * is assigned at run start, so calling this from an event listener, a tool, * or any other mid-run vantage point returns the IN-FLIGHT run, partially * filled. That is deliberate (Lens scrubs a running agent through it), and * it is why `.selfExplain()` captures at the terminal flush instead of * resolving through this: evidence that is supposed to describe a FINISHED * turn cannot be read from a getter that also answers about an unfinished * one. */ 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; /** * Corrective re-asks the agent's LAST run paid for, read off its own ledger. * * `undefined` when there is no last run to read — `parseOutputAsync` accepts * any string, including one that never came from this agent, and reporting * `0` for "I do not know" would be an invented fact in an event payload. */ private lastRunRetriesSpent; /** * 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 | string, options?: AgentRunOptions): Promise; /** * Answer one turn. * * **`run()` is ONE turn, and it starts a new conversation every time.** The * chart seeds its history from this call's `message` alone, so a second * `run()` on the same agent does not continue the first: the model is shown * one user message and will honestly tell your user it has not spoken to * them before. That is deliberate — a primitive that quietly accumulated * state across calls could never be used for one-shot work, and a hidden * transcript is the most expensive thing an agent can carry. * * To continue a conversation, name it: * * - `agent.followUp(message)` — continue THIS agent's own last completed * run. The one-liner, and what most callers want. * - `run({ message, continueFrom })` — continue a conversation you are * holding: `agent.checkpoint()` from an earlier turn, persisted anywhere * and handed back. Works across a restart, a deploy, or a different * machine, and is what `standingAgent` uses per session. * * Passing the same `identity.conversationId` to two `run()` calls does NOT * continue anything — see {@link AgentInput.identity}. What a registered * memory adds is *recall* of prior turns into the system-prompt slot, which * is a different thing from the conversation itself. * * Two refusals guard the per-instance state this agent keeps; both replace * behavior that used to succeed while quietly being wrong (9.2.0): * {@link RunInFlightError} when a run is already in flight, and * {@link PendingQuestionError} when the last run paused to ask a person * something that nobody has answered. * * @example One turn, then a follow-up * ```ts * await agent.run({ message: 'Book me a table for two.' }); * await agent.followUp('Make it three.'); // remembers the table * ``` */ run(input: AgentInput | string, options?: AgentRunOptions): Promise; /** * Continue this agent's own last completed conversation. * * The one-liner for turn two and after. `run()` is one turn and starts a new * conversation each time (see {@link Agent.run}); this reads the * conversation off the last completed run, appends `message` as the next * user turn, and runs from there — so the model sees what was actually said. * * Sugar over `run({ message, continueFrom: this.checkpoint() })` and nothing * more: one restoration path, so the convenience cannot drift from the * mechanism. Reach for `run({ continueFrom })` directly when the * conversation comes from somewhere other than this instance's last run — a * store, another process, a different machine. * * Refuses rather than guessing: {@link NoConversationError} when this agent * has no completed run to continue (a "follow-up" that quietly became a * first turn would be exactly the confusion this door exists to remove), * and — through `run()` — {@link PendingQuestionError} when the last run * paused to ask a person something, because a pause has its own door: * `resume(checkpoint, decision)`. * * The conversation grows every turn and nothing here trims it; bounding what * the model is shown is `.window()` / `.compaction()` / `.memory()`, not a * silent cap on the way through. * * @example * ```ts * await agent.run({ message: 'Book me a table for two.' }); * await agent.followUp('Make it three.'); * await agent.followUp('And move it to 8pm.'); * ``` */ followUp(message: string, options?: AgentRunOptions): Promise; /** * Drop the question this agent's last run paused to ask, on the record. * * A paused run is waiting on a person. Sending a different message while one * is outstanding is refused ({@link PendingQuestionError}) because silently * discarding a pending question makes a consent gate something any later * message can walk around. When the question really is being dropped — * the user changed the subject, the session timed out, the approval is no * longer wanted — say so with this, and the next `run()` proceeds. * * Returns what was dropped (`undefined` when nothing was pending), so a * caller can log or audit the abandonment rather than perform it blind. It * does not touch the paused run's checkpoint: if you still hold that, it * remains resumable. */ abandonPause(): { readonly toolName?: string; readonly toolCallId?: string; readonly question?: string; } | undefined; /** * Whether {@link Agent.selfExplain}'s why-questions have a run to answer * from right now. * * `false` for two different reasons, both honest: this agent was not built * with `.selfExplain()`, or it was and no turn has completed yet (evidence * binds at the END of a run, never to the one in flight). Either way there * is nothing to explain, which is what a caller routing a why-question needs * to know before it routes. * * The model is told the same thing by the same fact — the trace tools answer * "No completed run is available yet" and the skill body says to say so * plainly. This is that answer, for the program. */ canExplain(): boolean; /** * 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; /** * Which identity a continued turn runs under: the caller's if they named * one, otherwise the conversation's own. * * @internal */ private identityFor; /** * 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; /** * Fire `'run'`-scoped tool teardown — IF this run really ended. * * **Not on `finally`, and that is the whole point.** `finally` runs on every * exit including a pause, and a pause exits TWO ways: a returned * `RunnerPauseOutcome` and a thrown `PauseSignal`. A check-in on a * code-interpreter call pauses the run so a person can approve the code — * tearing the sandbox down there destroys the exact state the resume needs, * and it fails QUIETLY, as a resumed run that "just re-ran everything". * Both shapes are discriminated here and both are skipped. * * An error IS a terminal: the run is over, nobody is coming back, and a * sandbox held by a run that crashed is the clearest kind of leak. Only a * pause survives. * * Fired for the TURN, not for `currentRunContext.runId` — `resume()` mints a * fresh run id, so a pause and its resume are one turn across two runs, and * filtering on the id would leave everything a paused turn opened alive * forever. See `ToolSessionTier.fireRun`. * * @param outcome what `run()`/`resume()` is about to return, or about to throw. */ private endRunToolSessions; /** * The conversation this agent's LAST completed run leaves behind, packed as * the same `AgentRunCheckpoint` that `resumeOnError(...)` accepts. Store it, * hand it back next turn, and the agent continues where it left off — across * a restart, a deploy, or a different machine. * * Returns `undefined` before any run has completed. * * **Read from the run's own recording, not from a second copy.** The history * comes from `getLastSnapshot().sharedState.history` — the state the run * actually committed — cloned on the way out so a persistence layer can never * mutate the live heap. The final assistant turn is appended from the answer * `run()` returned, because nothing ever writes it back into `history`: the * loop appends assistant turns only when they carry tool calls, and the turn * that ends the run carries none. An agent that stored this conversation * without that append would drop its own reply every turn and answer the next * one having forgotten what it just said. * * Adds no events, no scope writes and no capture: every recording is * byte-identical to an agent that never calls this. * * After a run that **paused**, this is the conversation as of the pause, with * no answer appended — a pause is unfinished work, and pause/resume has its * own carrier (`FlowchartCheckpoint`) that holds engine state this shape * cannot. * * The conversation grows every turn and nothing here trims it. Bounding what * the model is shown is the memory subsystem's job (`.memory(...)`), not a * silent cap applied on the way to storage. * * @example * ```ts * await agent.run({ message: 'Book me a table for two.' }); * const conversation = agent.checkpoint(); // persist anywhere * // …a restart later, on a fresh Agent: * await agent.resumeOnError({ * ...conversation, * history: [...conversation.history, { role: 'user', content: 'Make it three.' }], * originalInput: { message: 'Make it three.' }, * }); * ``` */ checkpoint(): AgentRunCheckpoint | undefined; /** * The graph cursor a conversation carrier stores — the run's final * committed `currentSkillId`, read from the SNAPSHOT (the state the run * actually committed; both chart shapes map the advanced cursor onto it * every iteration), and only when the mounted graph declared * `continuity: 'conversation'`. * * ONE reader for BOTH carriers — `checkpoint()` (the conversation door * `followUp()` walks through) and the crash checkpoint * `RunCheckpointError` carries — the `foldedSpansOf`/`conversationOwner` * rule: a conversation must not keep its place on one path and silently * lose it on the other. * * @internal */ private continuityCursorOf; /** * The folded spans this run has committed, cloned on the way out. * * One reader for both carriers — `checkpoint()` and the crash checkpoint * `RunCheckpointError` carries — so a conversation cannot keep its spans on * one path and silently lose them on the other. The clone is the same * promise `checkpoint()` makes about history: a persistence layer never gets * a reference into the live heap. * * @internal */ private foldedSpansOf; /** * The two owner facts every conversation carrier stamps — who the run was * for, and which agent ran it (9.2.0). * * One reader for `checkpoint()` and the crash checkpoint, the same rule * `foldedSpansOf` follows: a fact kept on one carrier and lost on the other * is worse than a fact kept on neither. Both are absent unless the caller * chose them, which is what keeps the fingerprint refusal narrow and the * default `conversationId` out of storage. * * @internal */ private conversationOwner; /** * Restore a stored conversation onto the side channel `seed` reads. * * THE one restoration path — `run({ continueFrom })` and `resumeOnError()` * both come through here, so the conversation door and the error door cannot * disagree about what continuing means. It checks the agent fingerprint, * restores history + folded spans, and adopts the conversation's identity so * the continued turn writes its memory where the earlier turns are. * * `appendMessage` is the difference between the two callers, and it is the * whole difference. Continuing a conversation ADDS this turn's user message * to the stored history; resuming after an error does NOT, because there the * message is already the last user turn in that history and appending it * would ask the same question twice. * * @internal */ private applyContinuation; /** One turn at a time — see `RunInFlightError`. @internal */ private assertNotRunning; /** A person's unanswered question outranks a new message — see * `PendingQuestionError`. @internal */ private assertNoPendingQuestion; /** * Remember (or forget) the question this run ended on. * * A paused outcome sets it; anything else clears it, because a run that * reached an answer has no outstanding question by definition. Reads the * same `pauseData` fields `standingAgent.describePause` reads — the tool * name and question the dispatch loop stamped — and invents nothing. * * @internal */ private recordPendingQuestion; /** * Hand the `.selfExplain()` binding to the agent that owns it. * * Called once by `AgentBuilder.build()`, immediately after `bindTo`. The * binding stays the tool provider's to read; the Agent holds it only so * `canExplain()` answers from the same fact the trace tools answer from, * rather than from a second guess about whether a run has completed. * * @internal */ bindSelfExplain(binding: SelfExplainBinding): void; /** * Refuse, at run start, any declared messages-slot role this provider * cannot carry inside its message list (7.21, D2). * * Run start rather than build time because the answer depends on the * provider — and a decorated provider (`withFallback`, `withRetry`) is only * the thing it is once composed. Run start rather than delivery time * because a declaration that can never work should fail on the first call, * not three iterations into a paid run. The delivery stage re-checks each * message anyway, which is what catches roles that only exist at run time * (a hand-built memory-recall subflow's formatted output). * * Called from the ONE place `run()` and `resume()` share, so no entry point * can slip past it. */ /** * The OPEN skills — the ones `read_skill` may reach from anywhere, whatever the * graph's cursor says (8.4.0). Two clauses, both load-bearing: * * • `trigger.kind === 'llm-activated'` — the trigger that reads * `activatedInjectionIds`, which is the ONLY thing a `read_skill` call writes. * It is exactly "read_skill can really activate this", so admitting anything * else (a hand-built `rule` injection, say) would replace one lie with another: * the tool would answer "activated" and nothing would activate. * • the graph declares no incoming edge to it — a bare model edge `.route(a, m)` * is a declared, drawn, `from`-gated affordance ("from a, the model may hop to * m"), and opening every such target would silently globalize it. What is left * is skills the graph never mentions at all. * * That covers three shapes that were all dead before: `.selfExplain()`'s debug * skill under a graph, a `.skill()`/`.skills()` registration beside a graph, and a * skill listed in `skills[]` and wired to nothing (whose own check-up warning says * "it can only be reached by the model via read_skill" — true again now). * * An open pick ACTIVATES but never moves the cursor — see the tool-calls gate. * Computed once per chart build; the injection list is fixed at construction. */ /** * The identity facts this run hands `tool.execute` (9.7.0). * * Read through an ACCESSOR from the chart (see `ToolCallsHandlerDeps.currentRun`) * because the chart is built once and this changes every run. * * `identity` is `lastRunIdentity` — what the CALLER passed — and deliberately * NOT `scope.runIdentity`, which is always populated and defaults to * `{ conversationId: '' }` (or, on a session-bound run since 9.10.0, * to `{ conversationId: sessionId }`). Handing a tool a synthesized * conversation as "the identity" would let it key an isolated session on a * fiction, and would make "absent" unrepresentable at exactly the layer that * most needs to see it. The session-derived namespace is synthesized too, and * is withheld here for that reason — `sessionId` beside it is the fact the * transport really delivered. */ private toolRunFacts; /** * The teardown tier, built on FIRST registration. * * An agent whose tools never hold a session never allocates one, and its * terminals stay a single `undefined` check. */ private toolSessions; /** * Turn one TEARDOWN report into a typed `agentfootprint.tools.session_*` event. * * Only the two closing events come through here. A start and a reuse happen * inside `tool.execute`, where the dispatch loop still holds the scope, so * those ride the ordinary emit channel and carry the stage they really * happened in. These two fire after the run's last stage committed, and this * is the one place that has to answer "from where?" without a stage to point * at. * * **Built with `buildEventMeta`, never `minimalMeta()`.** `minimalMeta()` * hardcodes `runId: 'consumer-scope'`, and a teardown event stamped that way * cannot be joined to the run that OPENED the session — the exact * unjoinability 9.4.0 spent a release fixing for credential events. So the * meta comes from `currentRunContext`, with a STATED pseudo-stage, the same * move as the `'#paused'` stamp at the pause boundary. */ private emitToolSessionReport; private openSkillIds; /** * The per-iteration `read_skill` offer builder — or `undefined` to leave the tool * exactly as it has always been (8.5.0). * * `read_skill` enumerated every registered skill while the gate admitted only * `reachableSkills(cursor) ∪ open`, so under a graph the model was handed ids it * would be refused, every iteration, and could spend a whole run re-asking. The * OFFER is rebuilt here from the same two functions the gate itself calls — one * source of truth, so the menu cannot drift from the verdict. * * Two guards: * * • no graph AND no per-role skill visibility → `undefined`. A plain * `read_skill` agent has no cursor and no gate; every registered skill * really is reachable, and the tool keeps its byte-identical description. * • `reactMode: 'classic'` → `undefined` for the GRAPH menu, plus a dev-mode * warning. Classic composes the tools slot on turn 1 ONLY (see the Context * selector's `includeStatic`), so a cursor-scoped menu would freeze at the * cold-start cursor and keep advertising it for the rest of the run — a * worse lie than the honest full catalog. `.selfExplain()` refuses under * classic for exactly this caching reason; here the full catalog is a * correct fallback, so this warns instead of refusing. * * Per-role VISIBILITY (9.11.0) survives classic, and that is not an * inconsistency: a cursor moves every iteration, but who is asking does not * change inside one run. A filter computed on turn 1 is still exactly right * on turn 9. */ private readSkillOfferFor; /** * Does the configured checker ask to decide which skills this caller sees? * (9.11.0) * * Absence is NO — see `PermissionChecker.governs`. This is the ONE switch: * false and the menu, the resolver and the activation gate are all inert, so * an agent with a checker that predates 9.11.0 composes the same prompt it * always did. */ private governsSkillVisibility; /** * Which skills the caller's role may NOT see, asked once per iteration * (9.11.0). * * Per iteration rather than per run because the checker is a port: a * hub-backed one can legitimately answer differently as a grant is revoked * mid-conversation, and caching the first answer would keep a withdrawn skill * on the menu for the rest of the run. The cost is one `check()` per skill per * iteration, paid only by agents that opted in. * * A skill is hidden when the checker returns anything other than `'allow'` / * `'gate_open'` — and when the checker THROWS, which is the fail-closed half: * a policy that did not answer did not say yes, and the same sentence governs * the tool gate. */ private hiddenSkillIdsNow; private assertDeliverableRoles; /** * Refuse, at run start, a `'tool-forced'` output strategy on a provider * that does not put a forced tool choice on its wire (7.26). * * Run start rather than build time for the reason `assertDeliverableRoles` * gives one method up: a decorated provider (`withFallback`, `withRetry`, * a breaker) is only the thing it is once composed, and `withFallback` * publishes the AND of its pair. * * Refusal rather than a quiet fall back to `'instruct'`, because the two * are not interchangeable: one constrains the shape at generation, the * other asks for it in prose. An agent that silently got the second while * its config said the first would be a promise the recording could not * even show was broken. */ private assertForcedToolChoiceSupported; private createExecutor; /** * File the run-configuration manifest (9.41.0) — which adapters and * strategies this run is about to use, stamped with the runId every other * event of the run already carries. * * **Why it lives in `createExecutor` and not in `run()`.** `run()` and * `resume()` both come through here, and both mint a fresh runId (a resumed * run is a new run to every consumer joining on `meta.runId`, so a resume * with no manifest would be a run whose arm nobody can name). One funnel is * also how the next entry point cannot forget — the `beginIngress` lesson. * * **Why a direct dispatch rather than `typedEmit`.** There is no stage: the * chart has not started. So it is built with `buildEventMeta` and a STATED * pseudo-stage, exactly like the tool-teardown reports at the other end of * the run — never `minimalMeta()`, whose hardcoded `runId: 'consumer-scope'` * would make the one event whose whole job is to BE joinable the one event * that cannot be joined. * * **Why it is gated on a listener.** Every typed event in this library is: * the dispatcher drops what nobody subscribed to, and `EmitBridge` does the * same upstream. The gate is what keeps an unwatched agent at one map * lookup per run. It also means the manifest is not "always on" but "always * there when anything is watching" — including `recordRun`, which subscribes * with `'*'` before the run starts, so every recording carries one. */ /** Fresh per-run ledger — see AgentOptions.integrityPosture (9.60.0). */ private beginIntegrityLedger; /** * Dev posture only: throw {@link CheckerDeadError} on a run whose * registered checkers demonstrably never ran, or whose canary went * uncaught. Called on the SUCCESS path before the recording files, so the * failure is the run's result, never a masked afterthought. */ private assertIntegrityAlive; /** * Did this run reach work a registered check should have seen? * * MEASURED, not asserted — and measured from a signal the integrity code * does not write. `llmLatestContent` is committed by the LLM stage's own * core path on every completed call, so its presence proves a call * happened; its absence proves the run died or paused before one, and a * checker that filed nothing THERE is not rot, it is a run that never got * started. Deriving this from the checks' own encounter counts would be * circular: an unhooked check would report "no work existed" and silence * the very alarm it should be tripping. */ private integrityWorkExisted; /** * File the run's disposition rows as ONE `integrity.disposition` event and * clear the ledger. On the finally path of both run doors — every exit, * before the recording stops. Listener-gated like every typed event, and * deliberately throw-proof: accounting must never change a run's outcome. */ private fileIntegrityDisposition; private emitRunManifest; /** * File the DECLARED skill map (9.50.0) — `agentfootprint.skill.graph_declared`, * once per run, right after the run-configuration manifest. * * Same funnel, same dispatch discipline, same listener gate as the manifest * (see `emitRunManifest` above): `run()` and `resume()` both come through * `createExecutor`, both mint a fresh runId, and a resumed run's consumers * deserve the topology under the runId they are joining on. The payload is * the map `AgentBuilder.skillGraph()` projected at mount — the author's * nodes and edges VERBATIM, never inferred from runtime hops — so a * recording carries the complete declared topology rather than the * fired-edges lower bound that `context.evaluated.routing[]` names. * * No graph, or a graph that could not state its map ⇒ no event — absent, * never guessed. */ private emitSkillGraphDeclared; /** * 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; /** * Did the last turn stop because a LIMIT cut it short — and if so, which? * * `undefined` on every normal finish, including a turn that used its whole * `maxIterations` budget and then genuinely finished. It is set only when * the model was still asking for tools and the run refused to run them: * `maxIterations` was reached, or a `costBudget: { onExceed: 'halt' }` was * crossed. * * ## Why this is a method and not part of the answer * * `run()` resolves to a bare string. There is nowhere in a string to write * "…and three tool calls never ran", which is the same wall 8.6.0 hit with * an outstanding credential consent — and there the turn raises, because * handing back a plausible answer for work a tool never did is a silent * success. This is not that. A limit you configured firing is the limit * working, and the answer is sometimes real (a model can return content AND * tool calls). So it does not raise; it records, in committed state, where * it is provable after the fact — `getLastSnapshot().sharedState.stoppedEarly` * is the same value, and this is the short way to it. * * When the answer came back EMPTY the library also warns once on the * console, because an empty string reaching a user is indistinguishable * from a bug. * * @example * ```ts * const answer = await agent.run({ message: 'audit every log file' }); * const cut = agent.stoppedEarly(); * if (cut) { * console.log(`stopped at iteration ${cut.iteration}: ${cut.reason}`); * console.log(`${cut.pendingToolCalls} tool call(s) never ran`); * } * ``` */ stoppedEarly(): AgentState['stoppedEarly']; /** * Did the last turn's answer FAIL this agent's `outputSchema` — and how (8.18.0)? * * `undefined` when the answer satisfied the contract, and on any agent with * no `.outputSchema()`. Set on every run whose final answer was judged and * rejected, including the default `retries: 0` case where the first answer is * the only one there was. * * ## Why a method, when `runTyped()` already throws * * Because `run()` does not, and `run()` is what a server, a queue worker and * `standingAgent` call. Before this existed, that caller received a string * that violated a contract they had declared, with nothing anywhere saying * so: the retries were billed, the ledger row was written under `retries > 0` * and absent under `retries: 0`, and the answer looked exactly like a good * one. `runTyped()` still throws `OutputSchemaError` — that is the caller * ASKING to be raised at, and it is unchanged. * * `brokenBy` is the case worth a dashboard: the model's answer PASSED and one * of your own `act({ output })` rules rewrote it into one that fails. The run * stops re-asking when that happens — a deterministic rule breaks the next * answer identically, so the retries would be bought for nothing. * * @example * ```ts * const answer = await agent.run({ message: 'summarise ticket 91' }); * const unmet = agent.outputContractUnmet(); * if (unmet) { * log.warn({ stage: unmet.stage, error: unmet.error, brokenBy: unmet.brokenBy }); * return safeDefault; // …rather than shipping `answer` as typed data * } * ``` */ outputContractUnmet(): AgentState['outputContractUnmet']; /** * Did the last turn's answer state names or numbers that appear in NO tool * result (9.35.0)? * * `undefined` when every value was grounded — and on any agent without * `.namesAndNumbersFromEvidence()`. Set on a turn that shipped flagged * (`'assist'` / `'guard'`) and on one that was refused (`'rails'`, where * `run()` also raised `UnsupportedValuesError`, so this is what a caller * reads in the `catch`). * * `revised: true` means the model was asked once to correct the values and * they survived that turn — the fact worth alerting on, because it is a * model that cannot ground its own claims rather than one that slipped. * * Remember what the verdict does NOT say: values were invented. It cannot * tell you whether a claim built from real values is true. * * @example * ```ts * const answer = await agent.run({ message: 'which port is down?' }); * const bad = agent.unsupportedValues(); * if (bad) log.warn({ values: bad.values.map((v) => v.value), revised: bad.revised }); * ``` */ unsupportedValues(): AgentState['unsupportedValues']; private finalizeResult; private buildChart; }