@sema-agent/core
Version:
Stateless, task-oriented AI agent core
110 lines (109 loc) • 6.93 kB
TypeScript
import { Type } from "typebox";
import type { TaskAccess } from "../core/task-registry.js";
import { type RosterEntry } from "./roster-store.js";
import { type PeerDirectory, type PeerPresence, type PeerSessionRecord } from "./peer-directory.js";
import type { SubagentTranscriptTier } from "../core/wiring-manifest.js";
import { formatAgo, type AgentState, type AgentStateSource } from "../core/agent-state.js";
import type { CheckpointSeat } from "../core/checkpoint-seat.js";
export declare const LIST_AGENTS_TOOL_NAME = "ListAgents";
export declare const LIST_AGENTS_TOOL_ALIAS = "ListPeers";
/** CC `maxResultSizeChars` for this tool. */
export declare const LIST_AGENTS_MAX_RESULT_CHARS = 10000;
/** The default freshness window (24 h) — one source, the peer lane's knob table. */
export declare const PEER_LISTING_FRESH_WINDOW_MS: number;
/**
* THE freshness predicate: a peer row is listed by default iff it is live AND (its host says it is
* working — `tempo` active/blocked — OR it changed within the window). One rule over rendering
* visibility only: refs are minted and names resolve over the FULL set, so a row hidden here is still
* addressable by name. Dead and stale rows are what `includeAll` adds.
*/
export declare function isFreshPeerRow(r: Pick<PeerSessionRecord, "liveness" | "tempo" | "updatedAt">, now: number, windowMs?: number): boolean;
/** `Ns` / `Nm` / `Nh` / `Nd` — a bounded age word for a listing row (one definition, shared with the agent-state clauses). */
export { formatAgo };
/** The last two path segments of a working directory (what a person recognizes a project by). */
export declare function cwdTail(cwd: string): string;
export interface ListAgentsToolOptions {
/** The peer-session directory. Absent ⇒ no peer rows (the two agent lanes still list). */
peerDirectory?: PeerDirectory;
/** This session's own identity — excluded from the peer listing, included in ref disambiguation. */
self: {
sessionId: string;
scope: string;
};
/** The live registry + this caller's access axes (same axes the SendMessage mount reads). */
registry?: {
list(access: TaskAccess, opts?: {
limit?: number;
}): Array<{
task_id: string;
type: string;
status: string;
description?: string;
}>;
getAccessibleTask(id: string, access: TaskAccess): {
name?: string;
status: string;
} | undefined;
/** The live background-agent row's state facts ({@link import("../core/task-registry.js").TaskRegistry.agentStateSource}).
* Absent on a registry that does not offer it ⇒ agent rows carry no `agentState` and no state columns. */
agentStateSource?(id: string, access: TaskAccess): AgentStateSource | undefined;
};
access?: TaskAccess;
/** design/147 S3a parity — a delegated child's TRUSTED parent view (owner = the parent's task id,
* sessionId = the parent's session), so the siblings SendMessage can reach by name are listed too.
* Unioned with {@link access}, de-duplicated by id; the scope axis is never widened. */
parentAccess?: TaskAccess;
/** The durable roster (names recorded by earlier processes). Rows are filtered by the caller's
* {@link access} with the store's own predicate (`entryAccessible`) BEFORE anything is listed — a
* shared roster holds other owners'/scopes' names, and a listing is a disclosure face. */
roster?: {
list(): RosterEntry[] | Promise<RosterEntry[]>;
};
/** The resolved freshness window ({@link isFreshPeerRow}); absent ⇒ {@link PEER_LISTING_FRESH_WINDOW_MS}. */
freshWindowMs?: number;
/** The continuation reach of the SendMessage mounted beside this tool (`sendMessageContinuationTier` —
* the same answer from the same seats, so the two faces cannot disagree): what a recorded agent's label
* may promise. `full` = the record and the conversation survive a restart; `rows` = the record survives,
* the conversation does not; `none` = only the recorded name. Absent (a direct mount that did not say) ⇒
* the `none` wording — a label never promises more than the mount can show. */
continuationTier?: SubagentTranscriptTier;
/** The run's checkpoint seat — where a PARKED agent row's approval card is read from (the same seat the
* Agent tool parks children into). Absent ⇒ the card reads `unreadable`. */
checkpointSeat?: CheckpointSeat;
/** The clock (tests). */
now?: () => number;
}
/** The store seats the listing reads agent rows' facts through, as one bundle a mount helper passes on unchanged. */
export type ListAgentsReadSeats = Pick<ListAgentsToolOptions, "checkpointSeat">;
/** One rendered row. `kind` names the lane; `address` is what SendMessage accepts. */
export interface ListAgentsRow {
kind: "peer_session" | "background_agent" | "roster";
name: string;
/** Peer rows only: the listing-minted ref. */
ref?: string;
/** Peer rows, live: when the row last changed (rendered `active <N> ago`). */
lastActiveAt?: number;
/** Peer rows, dead: when the host marked it (rendered `offline <N> ago`); a dead row with no stamp renders `offline`. */
offlineAt?: number;
/** Peer rows: the last two segments of the session's working directory, when the row carries one. */
cwd?: string;
/** Peer rows: the session's self-reported presence, when it reported any. */
presence?: PeerPresence;
/** What SendMessage accepts: on both AGENT lanes the registry `task_id` VERBATIM (no prefixing or
* re-minting), never the child's session id — a different domain, tabled in INTEGRATION-CORE §1.6a.
* Peer rows carry the `session.<id>` box handle instead. */
address: string;
/** What the row IS, right now. Both lanes are a table lookup over the word the row carries
* ({@link PEER_ROW_STATE} / {@link AGENT_ROW_STATE}), and `unknown` answers whatever the table cannot
* — on the peer lane the common case, since no host writes the activity word. `finished` means
* COMPLETED and nothing else: a model applies the continuable rule to that word. */
state: "busy" | "idle" | "offline" | "running" | "parked" | "finished" | "failed" | "killed" | "cancelled" | "unknown";
/** Agent lanes with a live registry row: the agent's current state
* ({@link import("../core/agent-state.js").readAgentState}, the one reading AgentTranscript and TaskOutput
* render too); {@link state} is this status word through the lane table. Absent on peer rows and on a
* roster row with no live row. */
agentState?: AgentState;
}
export declare function createListAgentsTool(opts: ListAgentsToolOptions): import("../core/types.js").AgentTool<Type.TObject<{
includeAll: Type.TOptional<Type.TBoolean>;
}>, unknown>;