import { type Socket } from 'node:net'; import type { AgentSession, ControlFailureKind, SessionEvent, SessionSnapshot } from './types.js'; import type { OwnerChannelHandle, OwnerChannelManagementRequest } from '../owner-channel/channel.js'; import type { ScheduledLoopManagerHandle } from '../loops/manager.js'; import type { SpawnOpts } from '../spawn.js'; import type { ManagedFleetSpawnResult } from '../fleet-proxy.js'; import { type FleetAuditPresentation, type FleetCommandOutcomeClass } from '../fleet-command-audit.js'; export interface ControlRequest { version: 1 | 2 | 3; id: string; token: string; command: 'status' | 'snapshot' | 'submit_prompt' | 'respond_permission' | 'interrupt' | 'follow' | 'events_since' | 'owner_channel_manage' | 'loop_status' | 'loop_run_now' | 'loop_disable' | 'loop_enable' | 'reload_config' | 'conversation_page' | 'conversation_follow' | 'submit_prompt_v2' | 'interrupt_v2' | 'respond_permission_v2' | 'fleet_spawn' | 'fleet_audit_begin' | 'fleet_audit_present' | 'fleet_audit_finish'; text?: string; permissionId?: string; optionId?: string; since?: number; /** Existing clients omit this and remain interactive controllers. */ controller?: boolean; ownerChannel?: OwnerChannelManagementRequest; loop?: string; /** Conversation cursor: replay strictly after this durable seq. */ after?: string; limit?: number; /** Idempotency key for v3 mutations. */ commandId?: string; /** Browser-session digest recorded as the acting principal. */ actor?: string; /** Server-stamped provenance. User-controlled text cannot set this field. */ source?: 'owner_admin_console'; /** Runner generation shown with a permission card. */ sessionGeneration?: string; /** Typed managed-spawn request accepted only over the authenticated role control plane. */ spawn?: SpawnOpts; audit?: { requestId?: string; argv?: string[]; correlationId?: string; class?: FleetCommandOutcomeClass; exitCode?: number; effect?: 'not_started' | 'completed' | 'unknown'; resourceIds?: Record; presentations?: FleetAuditPresentation[]; }; } export interface ControlResponse { version: 1; id: string; ok: boolean; result?: unknown; error?: string; /** Why it failed, so the caller does not have to guess from the text. */ kind?: ControlFailureKind; } export interface RetainedEventPage { events: SessionEvent[]; snapshot: SessionSnapshot; firstSeq: number; lastSeq: number; truncated: boolean; } /** The one retained-range projection shared by polling and live-follow admission. */ export declare function retainedEventPage(session: AgentSession, since: number): RetainedEventPage; /** * One line saying what a control failure does — and does not — prove about the * agent. Only `offline` is evidence that it is gone; every other kind used to * be rendered as "is not running", which is how a busy agent got restarted. */ export declare function livenessNote(kind: ControlFailureKind, name: string): string; /** * The result taxonomy an overseer judges a role by. * * One console command is not a liveness verdict. `peek` and `send` can fail for * five distinct reasons and succeed for one, and only ONE of the six says the * agent is gone — collapsing them into "not running" is how busy agents got * restarted. This is the single definition of that vocabulary: the generated * briefing renders it, and the shipped oversee-agents skills quote it. The * per-result wording comes from `livenessNote` rather than being restated, so * the words an overseer reads in its instructions are the words the CLI prints. */ export interface OversightResult { /** What the command reported: the one success, or the failure kind. */ result: 'queued' | ControlFailureKind; /** What it proves about the agent. */ meaning: string; /** What the overseer does next. */ action: string; /** * Whether this result ALONE justifies restarting the role. True for exactly * one result. Every other one requires corroboration before touching a role * that may simply be working. */ restartJustified: boolean; } /** The taxonomy, in the order generated guidance presents it. */ export declare function oversightTaxonomy(name?: string): OversightResult[]; /** * The taxonomy as guidance lines, for a briefing or any generated document. * The shipped oversee-agents skills carry these same lines, and a test holds * them to it — so an overseer reading its briefing and an overseer reading the * skill cannot be given different rules. */ export declare function oversightTaxonomyLines(name?: string): string[]; export declare const controlSocketPath: (stateDir: string) => string; export declare const controlTokenPath: (stateDir: string) => string; /** Private, versioned JSONL control plane for CLI and future console frontends. */ export declare class RoleControlServer { private readonly session; private readonly log; private readonly server; private readonly socketPath; private readonly token; private readonly sockets; /** Interrupt idempotency: same command id returns the first receipt. */ private readonly interruptCommands; private ownerChannel?; private loopManager?; private reloadConfig?; private fleetSpawner?; private fleetAuditor?; constructor(stateDir: string, session: AgentSession, log: (line: string) => void); start(): Promise; close(): Promise; /** Attach only the already-started supervisor-owned channel client. */ setOwnerChannel(ownerChannel: OwnerChannelHandle | undefined): void; setLoopManager(loopManager: ScheduledLoopManagerHandle | undefined): void; setConfigReloader(reloadConfig: (() => Promise) | undefined): void; setFleetSpawner(fleetSpawner: ((options: SpawnOpts) => Promise) | undefined): void; setFleetAuditor(auditor: RoleControlServer['fleetAuditor']): void; private accept; private handle; /** Version and capability gate shared by every conversation v3 command. */ private requireConversation; private write; } /** * Send one control request. Every failure mode is classified: a missing token * or socket is `control-unavailable`, a silent server is `timeout`, and a * response that is not parseable JSON is `backend`. The caller never has to * infer liveness from an exception message. */ export declare function controlRequest(stateDir: string, request: Omit, timeoutMs?: number): Promise; export declare function followControl(stateDir: string, onMessage: (message: Record) => void): Promise<{ socket: Socket; send(request: Omit): void; }>; /** * Open a live conversation follow on the role's private control socket. The * first message is the initial page + snapshot; every subsequent message is * `{ version, conversationEvent }`. Closing the socket detaches the * controller-presence this connection contributed. */ export declare function followConversation(stateDir: string, after: string | undefined, onMessage: (message: Record) => void): Promise<{ socket: Socket; close(): void; }>;