agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
154 lines (153 loc) • 7.98 kB
TypeScript
/**
* memberRun — WHO a composition member runs for: the run that mounted it.
*
* ── The defect this closes ───────────────────────────────────────────────────
* A composition (`Sequence`, `Parallel`, `Conditional`, `Loop`, `Workflow`,
* `Graph`) mounts each member's chart in ITS OWN executor; the member's
* `run()` never runs. So a member agent's per-run facts — the memory
* namespace, a tool's `ctx.identity`, `ctx.runId` — were read off the member
* INSTANCE, which every run of the composition shares: `'pending'` for an
* instance never run on its own (one memory for every user of the
* composition), or the last direct run's caller (another person's identity).
*
* ── The rule ─────────────────────────────────────────────────────────────────
* The run decides, and only the engine can name it. The OUTERMOST
* composition's `run()` OPENS one run — the identity its caller named, or
* none, and that run's own id — in this module's private registry, and hands
* its members only an unguessable LINK to it (`withParentRun`), nesting
* included. A member agent looks the run up by that link (`stages/seed.ts`):
* the caller's identity when one was named, else the run's own per-run
* conversation `{ conversationId: runId }` — never a value its instance
* holds, and never a run-shaped value it was handed: a `parentRun` that is not
* a live link is refused by name (`ParentRunRefusedError`). A member mounted
* where no run reaches it is refused too (`MemberIdentityError`).
*
* Per-run args are the carrier because they are the one thing two overlapping
* runs of one composition cannot share.
*
* Pattern: value object + pure helpers. Role: core, run identity.
*/
import type { MemoryIdentity } from '../memory/identity/types.js';
import type { RequestContext } from '../identity/requestContext.js';
/** The state key a runner keeps the run it serves under. */
export declare const MEMBER_RUN_KEY = "memberRun";
/** The run a member is mounted in. */
export interface ParentRun {
/** The outermost run's id — the member's `ctx.runId`, and its conversation when no identity was named. */
readonly runId: string;
/** The identity the outermost run's caller named, if any. */
readonly identity?: MemoryIdentity;
}
/**
* A run as a runner's state records it: the run, and the LINK that names it in
* this module's registry. Only the link travels to members; what a member runs
* for is looked up here, never read off what it was handed.
*/
export interface ParentRunRecord extends ParentRun {
readonly link: string;
}
/**
* A `parentRun` that is not a live link this module issued — forged, copied
* from another run, or a run already over. Refused, never used.
*/
export declare class ParentRunRefusedError extends Error {
readonly code: "ERR_PARENT_RUN_REFUSED";
constructor(door: string);
}
/**
* Open the run a top-level composition run hands its members, and register it.
* Close it with {@link closeParentRun} when the run returns (finished or paused).
*
* @throws TypeError (from `assertIdentityShape`) when `identity` is not one.
*/
export declare function openParentRun(runId: string, identity: MemoryIdentity | undefined, door: string, request?: RequestContext): ParentRunRecord;
/**
* Re-open, for a resumed leg, the run a paused composition was running — read
* off the checkpoint's own state (the checkpoint is the record of that run; a
* host keeps it server-side or signs it, as for every resume). `undefined`
* when the paused run recorded none.
*/
export declare function reopenParentRun(sharedState: unknown, door: string, request?: RequestContext): ParentRunRecord | undefined;
/** Release a run this module opened (`openParentRun` / `reopenParentRun`). */
export declare function closeParentRun(record: ParentRunRecord | undefined): void;
/**
* The request a live link's run serves, or `undefined` — for a dead link, a
* value that is not a link, or a run opened without one.
*/
export declare function requestOfLink(link: unknown): RequestContext | undefined;
/**
* The request a member's stage serves, read from the stage's own input: the
* request of the run its `parentRun` link names. A member with no link (a
* chart a caller built and runs itself) serves NO request — never the
* request of whatever run its instance is in elsewhere.
*/
export declare function memberRequestIn(scope: object): RequestContext | undefined;
/**
* A resume names a request while the run it resumes is still open under
* another — its members would serve two requests. Refused before anything runs.
*/
export declare class ResumeRequestConflictError extends Error {
readonly code: "ERR_RESUME_REQUEST_CONFLICT";
constructor(door: string);
}
/** The run a live link names, or `undefined`. */
export declare function runOfLink(link: unknown): ParentRun | undefined;
/**
* The run a runner was handed in its input, looked up by its link; `undefined`
* when the input carries none.
*
* @throws ParentRunRefusedError when a `parentRun` is present but is not a live
* link — a member never runs under a run it cannot prove.
*/
export declare function resolveParentRun(args: unknown, door: string): ParentRunRecord | undefined;
/**
* Register a runner that reads `parentRun` from its input — `Agent` and the
* compositions call this in their constructors. Every other runner is handed
* the input it always was, byte for byte.
*/
export declare function readsParentRun(runner: object): void;
/**
* A member's input with the LINK of the run it is mounted in — what every
* composition's input mapper returns — for a runner that reads it; the input
* unchanged for any other runner, or when there is no run to hand down.
*/
export declare function withParentRun<I extends object>(input: I, record: ParentRunRecord | undefined, runner: object): I | (I & {
readonly parentRun: string;
});
/**
* A composition's seed: resolve the link it was handed — its own `run()`
* opened one; a parent composition's mapper hands one down — and keep the run
* in its state, under {@link MEMBER_RUN_KEY}, for its input mappers to hand on
* (a run's input is not part of the state an input mapper reads). Written only
* when there is one.
*
* @throws ParentRunRefusedError for a link that is not live.
*/
export declare function adoptParentRun(scope: object, args: unknown, door: string): void;
/** The run a runner's state carries (`adoptParentRun`, or an agent's seed), for its input mappers. */
export declare function parentRunIn(state: unknown): ParentRunRecord | undefined;
/**
* What an inner mount of a member agent is handed of its run: the link, or —
* for a chart a caller built — the identity it was given. Nothing on the
* agent's own runs.
*/
export declare function memberRunArg(record: unknown): {
readonly parentRun?: string;
readonly memberIdentity?: MemoryIdentity;
};
/**
* The identity a member's stage runs for, read from the stage's own input:
* the run its link names, else the identity a caller-built chart handed it.
*/
export declare function memberIdentityIn(scope: object): MemoryIdentity | undefined;
/** The memory namespace a member runs under: the named identity, else the run's own conversation. */
export declare function memberIdentityOf(parentRun: ParentRun): MemoryIdentity;
/**
* A member mounted where no run reaches it — another runner's executor is
* running this agent's chart, and neither a `parentRun` nor an `identity`
* arrived in its input. Refused rather than run under its instance's last run.
*/
export declare class MemberIdentityError extends Error {
readonly code: "ERR_MEMBER_NO_IDENTITY";
constructor();
}