UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

110 lines (109 loc) • 6.93 kB
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>;