agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
102 lines (101 loc) • 5.32 kB
TypeScript
/**
* runManifest — the run-configuration manifest, composed.
*
* Pattern: Pure builder over already-resolved configuration.
* Role: Turn what an Agent HOLDS into what `agentfootprint.agent.run_configured`
* SAYS. Kept out of Agent.ts so the rule that governs it — names only,
* absence never guessed — is testable without building an agent, and
* so the one place that decides what may leave the process is one
* small file rather than a paragraph inside a 3,000-line class.
* Emits: N/A — the Agent dispatches what this returns.
*
* ## The rule this file exists to keep
*
* The manifest carries NAMES AND IDS ONLY. Not endpoints, not directories, not
* connection strings, not keys, not tenants, not principals — and not a config
* VALUE that could be any of those. It rides into every recording, every vendor
* sink and every shared trace, which is exactly where a secret must not go.
*
* That rule is why this file takes an already-narrowed input rather than the
* Agent: nothing here can reach a store's options bag even by accident, because
* a store is never passed in. Where the only handle on a component is a value
* (a directory, a URL, a table name), the component is reported as PRESENT and
* left unnamed — {@link RunManifestSources.artifacts} is the shape of that
* answer. A field the configuration did not declare is ABSENT: "the strategy
* did not say" and "the default" are different facts, and only the first one is
* true here.
*/
import type { AgentRunConfiguredPayload } from '../../events/payloads.js';
import type { MemoryDefinition } from '../../memory/define.types.js';
import type { AppliedRecipe } from '../../recipes/types.js';
/**
* What the Agent hands over — already resolved, already narrowed to names.
*
* Every member is something the Agent knows at `createExecutor()` time, before
* the first stage runs. Anything it does not know then is deliberately not on
* this type: a manifest that had to run a stage to fill itself in would be
* describing a run already in progress.
*/
export interface RunManifestSources {
readonly agentId: string;
/** `LLMProvider.name` of the effective (decorators included) provider. */
readonly providerName: string;
/** The agent's resolved default model — the one this run starts with. */
readonly model: string;
/** A `.configure()` resolver is mounted: it may replace the model per run,
* and it runs in seed, AFTER this event. */
readonly hasRunConfig: boolean;
/** Per-skill brains are mounted: they may replace provider/model per call. */
readonly hasSkillBrains: boolean;
/** The configured cache strategy cannot apply a non-never policy.
* Fixed metadata only: no prompt, policy predicate, options or credentials. */
readonly cacheUnsupported?: 'explicit' | 'default';
readonly reactMode: 'classic' | 'dynamic' | 'dynamic-grouped';
readonly memories: readonly MemoryDefinition[];
/** `WindowStrategy.name` from `.window()` / `.compaction()`. */
readonly windowStrategyName?: string;
/** A skill graph is mounted — the OBJECT's presence in the payload means
* exactly this, and each of its fields is separately optional. */
readonly skillGraph?: {
readonly routing?: 'assist' | 'guard' | 'rails';
readonly continuity?: 'turn' | 'conversation';
/** `IntentScorer.name`. Undefined for a graph with no `.classify()`. */
readonly scorerName?: string;
};
readonly evidenceGatePosture?: 'assist' | 'guard' | 'rails';
/**
* The artifacts wiring, as booleans.
*
* `ArtifactStore` declares no id, so WHICH store is unnameable — and the
* things that would identify one (a directory, a bucket, a table) are the
* values this manifest exists not to carry. `configured` is the honest
* remainder: a store IS in play. Omit the whole member when none is.
*/
readonly artifacts?: {
readonly configured: true;
readonly placement: boolean;
readonly recordings: boolean;
};
/**
* The recipes `.recipe()` applied, in declaration order.
*
* Names and versions only — the same law the rest of this file keeps. A
* recipe's `description` is prose about the composition and never travels;
* what a consumer GROUPS on is the pair, and the pair stays two fields (a
* composed `'id@version'` would be one string two different pairs could
* produce).
*
* ABSENT, not `[]`, when an agent applied none — and this is the one place
* that differs from {@link memories}, deliberately. "No memory is mounted" is
* an arm a study compares against, so it is stated. "No recipe" is the state
* of every agent written before recipes existed, and stamping an empty list
* on all of them would put new bytes in every recording, every vendor sink
* and every stored manifest for a feature nobody used.
*/
readonly recipes?: readonly AppliedRecipe[];
}
/**
* Compose the manifest. Pure: same input, same payload, no clock, no ids
* minted here (the runId the arms are grouped by is already on `meta`).
*/
export declare function buildRunManifest(sources: RunManifestSources): AgentRunConfiguredPayload;