agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
94 lines • 4.05 kB
JavaScript
"use strict";
/**
* pooledClient — one client INCARNATION: the conversation it serves, the plan it
* was opened from, its leases, its own tool list, and where it is in its life.
*
* Pattern: a plain record the pool owns; no behaviour beyond its own counters.
* Role: § 5 of the design. A client is OPENING until its open settles, OPEN
* while it can serve, ENDING from the decision to end it until that
* ending's work has settled, then ENDED. It is BUSY while any lease is
* open — the library's, one per operation, or the app's — while an
* operation that waited for its open has not yet taken its lease (a
* RESERVATION), and while it opens; IDLE otherwise. A busy client is
* never evicted, closed for idleness or ended. "Used" is the last lease
* taken or released.
*
* Its `clientId` is the pool's own random id; its `generation` counts the key's
* incarnations (1 = the key's first client). A replacement is a new record with
* a new id, a new list and a count of its own: nothing of an old incarnation
* can match it.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.PooledClient = void 0;
const definitions_js_1 = require("./definitions.js");
/** A random id of the pool's own — never derived from an identity. */
function newClientId() {
return globalThis.crypto.randomUUID().replace(/-/g, '').slice(0, 16);
}
/** One client incarnation. */
class PooledClient {
key;
plan;
generation;
clientId = newClientId();
state = 'opening';
/** The ordinary `McpClient`, once opened. */
client;
/** The port back (`clientPort.ts`), once opened. */
port;
/** Open leases: one per library operation, one per app lease. */
leases = 0;
/**
* Operations waiting for its open, each holding it until it takes its lease —
* so between the open settling and that lease nothing can evict or end it.
*/
reserved = 0;
/** The last lease taken or released, on the pool's clock. */
usedAt;
/** No new lease: `terminate` named it, its session expired, or its handle was lost. */
closedToNew = false;
/** Its open ran past `initTimeoutMs`: refused to every waiter, still counted until it settles. */
abandoned = false;
/** Waiting for an ending slot and an operation slot, and why. */
deferredEnd;
/** This incarnation's own tool list. */
list = new definitions_js_1.DefinitionList();
/**
* The guard of the open, listing or ending in progress on this client — what
* a request that is not a pooled tool call is checked against and counted by.
*/
phase;
/** The open in flight, with the work it owns. */
opening;
/** How long the open took, once it settled within its deadline. */
initMs;
/** Why it left its key without an ordinary ending: its session expired, or its handle was lost. */
lost;
/** The outcome of its ending, once one started. */
endingOutcome;
/**
* The definitions context its tools are served under now. It starts as its
* plan's; the app's `share` is asked again at every use, and a changed answer
* moves it here — its connection, the plan's transport, never changes.
*/
definitions;
constructor(key, plan,
/** 1 = the key's first client, 2 = its first replacement, … */
generation, now) {
this.key = key;
this.plan = plan;
this.generation = generation;
this.usedAt = now;
this.definitions = { context: plan.context, shared: plan.shared };
}
/** Busy: a lease is open, an operation holds it between its open and its lease, or it is still opening. */
get busy() {
return this.leases > 0 || this.reserved > 0 || this.state === 'opening';
}
/** Can it serve a new lease right now? */
get serving() {
return this.state === 'open' && !this.closedToNew;
}
}
exports.PooledClient = PooledClient;
//# sourceMappingURL=pooledClient.js.map