@sema-agent/core
Version:
Stateless, task-oriented AI agent core
53 lines (52 loc) • 3.79 kB
TypeScript
/**
* The deferral CENSUS: what a leg discloses about which of its tools ship as name-only placeholders, and the
* seat that carries it to the stream. Split out of the disclosure mount because it is a different concern —
* the mount decides the request FACE, this decides what is SAID about it.
*/
import type { ToolSearchPolicy, ResolvedToolSearch } from "../tool-search-setting.js";
/**
* The HOST-facing census of this leg's deferral: which tool names ship as name-only placeholders, which the model has activated, and the policy that decided it.
* @contract tool_disclosure.snapshot — the `tool_disclosure` frame is minted ONLY here ({@link ToolDisclosureSeat.publish}), exists ONLY on a leg that defers
* at least one tool, and carries the WHOLE census each time (a consumer replaces its copy; the "what just happened" delta is this snapshot's `activated` minus
* the previous one's). `deferred` is frozen at prepare (a name never leaves it, it only appears in `activated`); both lists are sorted, so two snapshots
* compare by value. A leg that defers nothing emits no frame at all — absence is "nothing was deferred", never "the disclosure is off".
*/
export interface ToolDisclosureSnapshot {
/** Which arm of `RunnerDeps.toolSearch` decided rule ② on this leg. */
policy: ToolSearchPolicy;
/** Present iff `policy === "auto"`: the percent of the window the candidate set had to reach. */
thresholdPercent?: number;
/** Every deferred name on this leg, sorted. Non-empty (the frame does not exist otherwise). */
deferred: readonly string[];
/** The deferred names ACTIVE on this leg right now, sorted — the engine's state, not a record of commits. Empty on the leg-start census only when nothing
* seeded it; a RESUMED leg carries whatever the engine reseeded (transcript replay, compaction snapshot, checkpoint) before anything executed. */
activated: readonly string[];
}
/**
* The run-time seat the frame rides: prepare publishes the leg-start census, every activation that changes it publishes the next, and the run's event wiring
* subscribes once. Snapshots published before the sink is installed are buffered (bounded) and flushed in order, so the leg-start census can be minted at
* prepare and still arrive after the manifest frame it belongs beside. Deliberately the same shape as `ToolRosterDeltaSeat` — the two are one problem.
*/
export declare class ToolDisclosureSeat {
#private;
/** Publish a census. A snapshot equal to the last published one is DROPPED: `rematerialize` also runs on an MCP refresh, and a frame that says nothing
* changed reads as an activation that did not happen. */
publish(snapshot: ToolDisclosureSnapshot): void;
/** Install the stream sink; anything published before is flushed in order. */
subscribe(sink: (snapshot: ToolDisclosureSnapshot) => void): void;
/** The leg ended: drop the sink so a late publish never reaches a closed stream (it buffers, unread). */
unsubscribe(): void;
}
/**
* Mint a leg's census producer: the frozen deferred list, the policy it was classified under, and a seat.
* One place holds both halves, so a census can never name a policy the classification did not run under.
*
* `deferred` is frozen because every census shares the one array — a host that mutated the list it was handed
* would be editing every later frame of the leg. `activated` is filtered to the DEFERRED names: the live
* activation set is seeded from the transcript and a resume checkpoint, both of which can name an undeferred
* tool.
*/
export declare function createDeferralCensus(deferred: ReadonlySet<string>, policy: ResolvedToolSearch): {
seat: ToolDisclosureSeat;
publish: (active: ReadonlySet<string>) => void;
};