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.44 kB
TypeScript
/**
* session — the session strategies: which AgentCore Runtime session a request
* lands in.
*
* ── The facts they rest on ───────────────────────────────────────────────────
* From the AgentCore Runtime MCP protocol contract and the `InvokeAgentRuntime`
* API reference:
*
* - The runtime routes by `Mcp-Session-Id` and always returns one. A client
* "must capture the Mcp-Session-Id returned in the response and include it
* in all subsequent requests" for microVM affinity; without it "each request
* may be routed to a new microVM".
* - `InvokeAgentRuntime` also carries `runtimeSessionId`
* (`X-Amzn-Bedrock-AgentCore-Runtime-Session-Id`, 33–256 characters), and
* the AWS SDK models it as an idempotency token — it fills a FRESH random id
* on every call that leaves it unset.
*
* ── 'per-call' (the default) ─────────────────────────────────────────────────
* Every request in a session of its own: this strategy sends a FRESH runtime
* session id with every request and no `Mcp-Session-Id`, and the
* `Mcp-Session-Id` a response carries is not handed to the MCP client, so
* nothing can carry one call's session into the next. Isolation by
* construction: a client shared by every user of a process (tools built once at
* startup — the common shape) never puts two users in one microVM. The price is
* a cold start per call, and it needs a STATELESS server (`stateless_http=True`,
* the runtime's recommended default) — a stateful one sees every request as a
* stranger.
*
* ── 'reuse' ──────────────────────────────────────────────────────────────────
* One session per CONNECTION (per `mcpClient` built on the transport), and the
* TRANSPORT owns the key. A request with no `Mcp-Session-Id` (a client's
* `initialize`) opens a connection with a fresh runtime session id; the
* `Mcp-Session-Id` the server returns for it is stored WITH that connection,
* and the client is handed an opaque handle of the transport's own in its
* place. Every later request carrying the handle is sent in that connection's
* runtime session with the server's own id. A handle the transport does not
* know — one it never issued, or one it forgot — is never sent: the request
* fails with `LocalSessionHandleLostError`, thrown from the transport's
* `fetch`. It is not a 404: no server said anything, and the runtime session
* behind a forgotten handle may still run — it can no longer be stopped from
* here, because its id went with the handle.
*
* Why a handle and not the server's id: the server's ids are unique only as far
* as the server makes them. One that numbers sessions per process hands every
* fresh microVM the same first id, and a map keyed by it would send the second
* client into the first one's microVM. A handle is ours, random, and per
* connection, so two clients on one transport are two sessions whatever the
* server returns — while ONE client is still one session for whoever calls
* through it. Build a client per conversation; never share a 'reuse' client
* between users.
*
* At most {@link MAX_CONNECTIONS} connections are remembered, least recently
* used forgotten first. A client's `DELETE` — sent by `terminate()`, never by
* `close()`, which ends only the local side — forgets its own handle first, and
* then becomes `StopRuntimeSession` for that connection's runtime session
* (`transport.ts`), answered like a server would answer the `DELETE`.
*
* Pattern: Strategy. Role: outer ring, vendor fact. Holds no credential.
*/
/** The runtime session header, as `InvokeAgentRuntime` binds `runtimeSessionId` to it. */
export declare const AGENTCORE_RUNTIME_SESSION_HEADER = "X-Amzn-Bedrock-AgentCore-Runtime-Session-Id";
/** The MCP Streamable HTTP session header. */
export declare const MCP_SESSION_HEADER = "Mcp-Session-Id";
/** Which session a request lands in. See the module note for each. */
export type AgentCoreRuntimeSession = 'reuse' | 'per-call';
/**
* What a strategy does with one request: answer it here, unsent; let it go —
* with its session headers fixed in place — and say what the MCP client may see
* of the response; or, for a `'reuse'` connection's `DELETE`, stop that
* connection's runtime session (`stop`, its runtime session id).
*/
export type SessionStep = {
readonly answer: Response;
} | {
readonly settle: (response: Response) => Response;
} | {
readonly stop: string;
};
/** One strategy, applied once per request the MCP client makes. */
export interface SessionStrategy {
/**
* @throws `LocalSessionHandleLostError` for a handle this strategy does not
* hold: nothing is sent
*/
request(headers: Headers, method: string): SessionStep;
}
/** How many connections a 'reuse' transport remembers before it forgets the least recently used. */
export declare const MAX_CONNECTIONS = 256;
/**
* Build the strategy for one transport.
*
* @throws when `kind` is not one of the two strategies.
*/
export declare function sessionStrategy(kind: AgentCoreRuntimeSession): SessionStrategy;