UNPKG

agentfootprint

Version:

The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.

95 lines (94 loc) • 5.36 kB
/** * callerIdentity — WHO a resumed run is for, read back from the run it resumes. * * Seed (`stages/seed.ts · seedFrom`) writes the run's memory namespace on one of * three rungs: the caller's EXPLICIT identity; `{ conversationId: sessionId }` * for a session-bound run that named none (recorded as * `runIdentitySource: 'session'`); or the per-run default * `{ conversationId: '<runId>' }`. Only the first rung is a caller's identity — * the other two are derivations this library made, and `Agent.lastRunIdentity` * (what `checkpoint()`, `EventMeta.principal` and a tool's `ctx.identity` read) * must stay absent for them, so "absent" keeps meaning "nobody named one". * * `Agent.resume` does not re-seed: the resumed run keeps the paused run's * `runIdentity`, restored from the flowchart checkpoint. So the checkpoint is * the one carrier of "who the paused run was for" that survives a shared * instance, a pooled instance rebuilt after eviction, and a restart. This * module is the inverse of seed's rungs over that carrier. * * THE LAW: never drop an identity the caller named, never promote a derived * one. The session rung is recorded, so it is certain. The per-run default is * NOT recorded (writing a marker would change the committed keys of every * run that names nobody), and a flowchart checkpoint does not carry the paused * run's id — so it is recognised by the strongest evidence available, in order: * * 1. **The caller named exactly this identity on this instance** (`named`, the * instance's last caller identity) → it is the caller's. This is what keeps * a single-user app's `{ conversationId: 'run-1-1' }` across a bare resume. * 2. **This instance minted that conversation id as a run id** (`minted`) → * derived, for certain. * 3. **The checkpoint's own receipt names that id as its run** (the paused run's * `receipt.basis.runId`, present once it called a model) → derived, for * certain. * 4. **LAST RESORT — the SHAPE `makeRunId` mints** (`isMintedRunId`), for a * checkpoint from another instance with no receipt to match → read as * derived. The one stated edge: a caller who names exactly * `{ conversationId: 'run-<digits>-<digits>' }` and resumes it BARE loses * it whenever step 1 cannot see it — on another instance, or on the same * instance once anyone else has run in between — and no receipt matches. * The fail-closed direction: a derived namespace is never published as a * person. Pass `identity` on `resume` to keep it. * * A checkpoint names who it is for; it is not proof. A host that lets * checkpoints leave its trust boundary signs them or keeps them server-side — * see `Agent.resume`. */ import type { MemoryIdentity } from '../../memory/identity/types.js'; /** What the resuming instance knows about identities and run ids. */ export interface CallerIdentityEvidence { /** The identity a caller last NAMED on this instance, if any. */ readonly named?: MemoryIdentity; /** Run ids this instance minted (bounded). */ readonly minted?: ReadonlySet<string>; } /** * The identity the CALLER of the run whose state this is named, as a COPY, or * undefined when that run's identity was derived (session rung, per-run * default), is malformed, or the state carries none. * * @example * ```ts * // Agent.resume — a resuming call that names a DIFFERENT identity is refused: * const named = callerIdentityOf(checkpoint.sharedState, { named: last, minted }); * ``` */ export declare function callerIdentityOf(state: unknown, evidence?: CallerIdentityEvidence): MemoryIdentity | undefined; /** * The identity the paused run's memory namespace and credentials are restored * from — `scope.runIdentity` as the checkpoint carries it, whatever its source — * as a validated copy; `undefined` when absent; `'malformed'` when the * checkpoint's own fields disagree: a value that is not an identity, or a * session-rung marker (`runIdentitySource: 'session'`) on anything but exactly * `{ conversationId }` — the only shape seed's session rung writes. */ export declare function restoredIdentityOf(state: unknown): MemoryIdentity | 'malformed' | undefined; /** * The session the paused run ran under, as its own state records it: the * `runSessionId` seed writes on every session-bound run, else — for a * checkpoint written before that key existed — the session rung's * `runIdentity.conversationId`. `undefined` when neither says. */ export declare function pausedSessionOf(state: unknown): string | undefined; /** * Refuse, when a run BEGINS, an identity that is not one: not an object, a * field that is present and not a string, or no field at all. TypeScript * already forbids these; a JavaScript caller can still pass them, and without * this door the run would start, pause, and then every resume of it — bare ones * included — would refuse as a malformed checkpoint (recheck NIT 2). The * refusal lands where the mistake was made. * * @throws TypeError naming the door and the field. */ export declare function assertIdentityShape(identity: unknown, door: string): void; /** Do two identities name the same tenant, principal and conversation? */ export declare function sameIdentity(a: MemoryIdentity, b: MemoryIdentity): boolean;