Something went wrong. Try again.
fork of prime agent with changes i want but are not upstreamable probqbly (and some fixes)
Something went wrong. Try again.
53 kB · 1483 lines
TypeScript
at main
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484import type { AgentMessage, ThinkingLevel } from "@earendil-works/pi-agent-core";import type { ImageContent, ServiceTier, TextContent, Transport } from "@earendil-works/pi-ai";import type { AgentSessionMessageDeliveryMode, AgentSessionMessageReceipt, AgentSessionMessageSafetyStatus,} from "../../core/agent-messages.js";import type { SessionActionRecoverySnapshot } from "../../core/agent-session.js";import type { AgentSessionRuntimeConfig } from "../../core/agent-session-config.js";import type { AgentSessionRuntimeMetadata } from "../../core/agent-session-runtime.js";import type { AgentAutonomousStatus } from "../../core/autonomous.js";import type { BashResult } from "../../core/bash-executor.js";import type { AgentCronJob, AgentHeartbeatDeliveryMode, AgentHeartbeatManagementAction, AgentHeartbeatUpdateAction,} from "../../core/cron-jobs.js";import type { InputSource } from "../../core/extensions/types.js";import type { AcpMcpServerConfig } from "../../core/mcp/acp-mcp-types.js";import type { CustomMessage } from "../../core/messages.js";import type { QueuedMessageLane, QueuedMessageMutation } from "../../core/session-action-store.js";import type { SessionCwdIssue } from "../../core/session-cwd.js";import type { DeleteSessionFileResult } from "../../core/session-file-actions.js";import type { SessionUsageSummary } from "../../core/usage.js";import type { AgentConnectionAgentStatus, AgentConnectionHeartbeat, AgentConnectionQueueMode, AgentConnectionResourceSnapshot, AgentConnectionRlmChildAgentSnapshot, AgentConnectionSavedSessionScope, AgentConnectionSavedSessionState, AgentConnectionScopedModel, AgentConnectionSessionContext, AgentConnectionSessionEvent, AgentConnectionSessionHeader, AgentConnectionSessionTreeNode, AgentConnectionSideQuestionEvent, AgentConnectionSideQuestionTurn, AgentConnectionState,} from "../agent-connection/types.js";import type { AgentRosterEntry } from "./agent-roster.js";import type { SessionSummary } from "./daemon-session-list.js";
/** * Local daemon JSONL protocol. * * This is the transport used by DaemonAgentConnection today, not the final * remote gateway protocol. The protocol primitives below are intentionally * JSON-serializable so a future gateway can wrap or proxy this local transport * without leaking transport details back into InteractiveMode. */
export const DAEMON_PROTOCOL_NAME = "prime-agent.daemon";export const DAEMON_PROTOCOL_VERSION = 7;export const DAEMON_COMMAND_ENVELOPE_MIN_PROTOCOL_VERSION = 7;// Revision 9 publishes persisted RLM spawn depth on passive session rows.// Revision 10 publishes persisted RLM spawn depth on all session catalog rows.// Revision 11 adds immediate get/set commands for active-session RLM max depth.// Revision 12 publishes idle-residency metadata on session summary rows.// Revision 13 narrows agent-origin reach and roster wire shapes to the nuclear family.// Revision 14 carries the client's monotonic telemetry opt-out on attach and reattach.// Revision 15 adds the mutate_queued_message command and queue_message_mutation capability.// Revision 16 adds the "stopping" workerState and stops reporting disconnected workers as "ready".// Revision 17 gates authoritative child rosters and transient owned-session recovery context.// Revision 18 adds the opt-in RLM quiescence barrier to headless completion.// Revision 19 adds daemon-held session input pauses.// Revision 20 lets cancellation target a prompt the session owns but has not started.// Revision 21 adds capability-gated, session-scoped ACP MCP server replacement.// Revision 23 lets workers query the supervisor agent roster on demand.// Revision 24 adds the capability-gated agent-roster subscription and push.// Revision 25 adds capability-gated direct worker peer transport discovery.// Revision 26 publishes own-session usage totals on session summary and saved-session rows.// Revision 27 adds structured session_recovering failure info for known-but-unaddressable sessions.// Revision 28 publishes the last recorded model on saved-session rows.// Revision 29 adds the capability-gated projected session tree.// Revision 30 lets a streaming client omit the duplicated saved-session array.// Revision 31 publishes argument completions for extension commands in the command catalog.export const DAEMON_SCHEMA_REVISION = 31;export const DAEMON_SCHEMA_ID = "protocol-7-schema-31-5f1939744f7e";
export type DaemonProtocolName = typeof DAEMON_PROTOCOL_NAME;export type DaemonProtocolVersion = number;export type DaemonCommandId = string;export type DaemonEventId = string;export type DaemonEventSequence = number;export interface DaemonEventCursor { generation: string; sequence: DaemonEventSequence;}export type DaemonClientId = string;export type DaemonClientCapability = | "attach_snapshot" | "event_sequence" | "extension_ui" | "slim_attach" | "chunked_snapshot" | "client_owned_sessions";export type DaemonPromptAdmissionCancellationStatus = "cancelled" | "owned" | "unknown";export interface DaemonPromptAdmissionCancellationResult { status: DaemonPromptAdmissionCancellationStatus;}export type DaemonServerCapability = | DaemonClientCapability | "delete_rlm_subagent" | "heartbeat_catalog" | "heartbeat_management" | "model_catalog" // The daemon honors previousTurns on start_side_question (multi-turn side // conversations). Clients must check before sending follow-up transcripts. | "side_question_transcript" // The daemon honors transient and runId on execute_bash (side-conversation // bash: never recorded into the session, and its bash_start/bash_end events // carry the transient marker and echoed runId so clients correlate runs by // identity). Clients must check before sending. | "transient_bash" | "agent_roster" | "session_input_admission" | "prompt_admission_cancellation" | "queue_message_mutation" | "authoritative_child_roster" | "owned_session_recovery_context" | "rlm_quiescence_barrier" | "session_input_pause" | "owned_prompt_cancellation" | "acp_mcp_servers" | "direct_peer_transport" // The daemon answers get_session_tree with projected tree entries when the // client asks for them. Clients must check before sending `projection`. | "session_tree_projection" // The daemon omits the duplicated final session array on list_saved_sessions // when the client asks for `streamOnly` and consumes the streamed items. | "session_list_stream_only" // The daemon publishes argumentCompletions on get_commands entries, so a client // without a local extension runner can complete extension command arguments. // Clients must check before depending on the field. | "command_argument_completions";
export type DaemonReplayStatus = "complete" | "partial" | "unavailable";
export interface DaemonProtocolInfo { name: DaemonProtocolName; version: DaemonProtocolVersion;}
export const DAEMON_PROTOCOL_INFO: DaemonProtocolInfo = { name: DAEMON_PROTOCOL_NAME, version: DAEMON_PROTOCOL_VERSION,};
export const DAEMON_DEFAULT_CLIENT_CAPABILITIES: readonly DaemonClientCapability[] = [ "attach_snapshot", "event_sequence",];
export const DAEMON_SUPPORTED_CLIENT_CAPABILITIES: readonly DaemonClientCapability[] = [ "attach_snapshot", "event_sequence", "extension_ui", "slim_attach", "chunked_snapshot", "client_owned_sessions",];
export const DAEMON_DEFAULT_SERVER_CAPABILITIES: readonly DaemonServerCapability[] = [ ...DAEMON_SUPPORTED_CLIENT_CAPABILITIES, "delete_rlm_subagent", "heartbeat_catalog", "heartbeat_management", "model_catalog", "side_question_transcript", "transient_bash", "session_input_admission", "prompt_admission_cancellation", "owned_prompt_cancellation", "queue_message_mutation", "authoritative_child_roster", "owned_session_recovery_context", "rlm_quiescence_barrier", "session_input_pause", "acp_mcp_servers", "session_tree_projection", "session_list_stream_only", "command_argument_completions",];
/** Single-use short-lived credential for one direct TUI connection to one worker process incarnation. */export interface DaemonPeerTransportTicket { purpose: "session_client"; socketPath: string; /** Filesystem identity of the worker socket at issue time; re-checked by the client before connecting. */ socketIdentity: { dev: number; ino: number }; workerInstanceId: string; activeSessionId: string; grantId: string; token: string; expiresAt: string;}
export interface DaemonRuntimeIdentity { buildId: string; executablePath: string; entrypointPath?: string; launcherPath?: string;}
export type DaemonResumeCursor = | ({ activeSessionId?: string } & DaemonEventCursor) | { activeSessionId?: string; eventSequence: DaemonEventSequence };
export interface DaemonAttachClientMetadata { clientId?: DaemonClientId; capabilities?: readonly DaemonClientCapability[]; resumeCursor?: DaemonResumeCursor; /** Opt-out-only policy. A telemetry-enabled worker must reject this attach. */ telemetryDisabled?: true; /** Fresh owner-supplied runtime context for recovering a client-owned worker. Never persisted. */ recoveryConfig?: AgentSessionRuntimeConfig;}
/** * Client-side env vars forwarded to the daemon so extensions can reach them * (e.g. HERDR_PANE_ID/HERDR_SOCKET_PATH that herdr sets per pane). The daemon * scopes these to the created session and merges them over process.env for * that session's pi.exec() subprocesses — it does not mutate the daemon's own * env. Carried on create only: attach must not rebind a session's identity, * since watchers (agents view, subagent viewers) also attach. */export interface DaemonClientEnv { env?: Record<string, string>;}
export type DaemonSessionLifecycle = "resident" | "client_owned";
export interface DaemonLaunchEnv { launchEnv?: Record<string, string>;}
/** * The allowlist of env vars a client may forward. One shared list because it * is the wire contract: clients filter before sending and the daemon * re-filters on receipt (the socket peer is untrusted). */export const DAEMON_CLIENT_ENV_KEYS = [ "HERDR_ENV", "HERDR_PANE_ID", "HERDR_SOCKET_PATH", "HERDR_TAB_ID", "HERDR_WORKSPACE_ID",] as const;
/** Collect the allowlisted env vars from the client process for the create command. */export function collectDaemonClientEnv(source: NodeJS.ProcessEnv = process.env): Record<string, string> | undefined { const env: Record<string, string> = {}; for (const key of DAEMON_CLIENT_ENV_KEYS) { const value = source[key]; if (value !== undefined) { env[key] = value; } } return Object.keys(env).length > 0 ? env : undefined;}
export function collectDaemonLaunchEnv(source: NodeJS.ProcessEnv = process.env): Record<string, string> { const env: Record<string, string> = {}; for (const [key, value] of Object.entries(source)) { if (value !== undefined && !key.startsWith("PRIME_AGENT_INTERNAL_")) { env[key] = value; } } return env;}
export interface DaemonReplayInfo { status: DaemonReplayStatus; fromSequence?: DaemonEventSequence; toSequence: DaemonEventSequence; fromCursor?: DaemonEventCursor; toCursor?: DaemonEventCursor; reason?: string;}
export interface DaemonEventMeta { id: DaemonEventId; protocol: DaemonProtocolInfo; activeSessionId?: string; sequence?: DaemonEventSequence; cursor?: DaemonEventCursor; emittedAt: string; replayed?: boolean;}
export interface DaemonCommandEnvelope<TCommand extends DaemonCommand = DaemonCommand> { type: "command"; id: DaemonCommandId; protocol: DaemonProtocolInfo; clientId?: DaemonClientId; command: TCommand;}
export type DaemonCommandWire = DaemonCommand | DaemonCommandEnvelope;
export interface DaemonEventEnvelope<TEvent extends DaemonOutbound = DaemonOutbound> { type: "event"; id: DaemonEventId; protocol: DaemonProtocolInfo; activeSessionId?: string; sequence?: DaemonEventSequence; cursor?: DaemonEventCursor; emittedAt: string; event: TEvent;}
export interface DaemonArtifactReference { id: string; sessionId: string; type: string; logicalPath: string; relativePath?: string; mimeType?: string; metadata?: Record<string, string | number | boolean | null>;}
export interface DaemonSessionSnapshot { activeSessionId: string; summary: SessionSummary; state: AgentConnectionState; messages: AgentMessage[]; sessionContext?: AgentConnectionSessionContext; sessionTree?: { tree: AgentConnectionSessionTreeNode[]; leafId: string | null }; lastEventSequence: DaemonEventSequence; lastEventCursor?: DaemonEventCursor; parent?: { activeSessionId?: string; sessionId?: string; nodeId?: string; childId?: string; }; /** Live RLM child sessions (including grandchildren) hosted by the daemon under this session. */ children?: AgentConnectionRlmChildAgentSnapshot[];}
export interface DaemonAttachResult { protocol: DaemonProtocolInfo; activeSessionId: string; /** Omitted for clients with the "slim_attach" capability; use snapshot.summary. */ state?: SessionSummary; /** Omitted for clients with the "slim_attach" capability; use snapshot.messages. */ messages?: AgentMessage[]; snapshot: DaemonSessionSnapshot; replay: DaemonReplayInfo; lastEventSequence: DaemonEventSequence; lastEventCursor?: DaemonEventCursor; snapshotStream?: { id: string; messageCount: number; targetChunkBytes: number; }; client: { id: DaemonClientId; capabilities: DaemonClientCapability[]; };}
export const DAEMON_UPDATE_RESTART_FORMAT_VERSION = 1;
export interface DaemonUpdateRestartQueue { actions: SessionActionRecoverySnapshot; nextTurn: CustomMessage[];}
export interface DaemonUpdateRestartSession { activeSessionId: string; sessionId: string; sessionFile: string; cwd: string; config: AgentSessionRuntimeConfig; runtimeMetadata?: AgentSessionRuntimeMetadata; clientEnv?: Record<string, string>; queue: DaemonUpdateRestartQueue; shouldResume: boolean; wasStreaming: boolean; wasCompacting: boolean; wasBashRunning: boolean; hadRunningRlmChildren: boolean; wasRetrying: boolean; hadAcceptedPromptInFlight: boolean;}
export interface DaemonUpdateRestartManifest { formatVersion: typeof DAEMON_UPDATE_RESTART_FORMAT_VERSION; createdAt: string; sessions: DaemonUpdateRestartSession[]; discardedActiveSessionIds?: string[];}
/** * `streamOnly: true` tells the daemon the client consumes the streamed * session_list_item messages, so the response carries `sessions: []` instead of * repeating every row. Only send it when the daemon advertises the * session_list_stream_only capability; without it the response carries the full * array as before. */export type DaemonSavedSessionListCommand = | { id?: string; type: "list_saved_sessions"; activeSessionId: string; scope: AgentConnectionSavedSessionScope; streamOnly?: true; } | { id?: string; type: "list_saved_sessions"; cwd: string; sessionDir?: string; scope: AgentConnectionSavedSessionScope; streamOnly?: true; };
export type DaemonCommand = | { id?: string; type: "list"; all?: boolean; cwd?: string; sessionDir?: string; includeClientOwned?: boolean; } | DaemonSavedSessionListCommand | { id?: string; type: "list_agent_peers"; workerToken: string } | { id?: string; type: "get_direct_worker_transport"; activeSessionId: string } | { id?: string; type: "roster_subscribe" } | { id?: string; type: "roster_unsubscribe" } | ({ id?: string; type: "create"; sessionPath?: string; continueRecent?: boolean; noSession?: boolean; name?: string; config?: AgentSessionRuntimeConfig; runtimeMetadata?: AgentSessionRuntimeMetadata; lifecycle?: DaemonSessionLifecycle; } & DaemonClientEnv & DaemonLaunchEnv) // Attach env is adopt-if-absent only: it fills identity for env-less // sessions (e.g. cron-created) but never rebinds one, since watchers // (agents view, subagent viewers) also attach. | ({ id?: string; type: "attach"; activeSessionId: string; supportsExtensionUi?: boolean; } & DaemonAttachClientMetadata & DaemonClientEnv & DaemonLaunchEnv) | ({ id?: string; type: "reattach"; activeSessionId: string; targetActiveSessionId: string; supportsExtensionUi?: boolean; } & DaemonAttachClientMetadata & DaemonClientEnv & DaemonLaunchEnv) | { id?: string; type: "detach"; activeSessionId?: string } | { id?: string; type: "complete_owned_session"; activeSessionId: string } | { id?: string; type: "promote_owned_session"; activeSessionId: string } | { id?: string; type: "kill"; activeSessionId: string } | { id?: string; type: "rename"; activeSessionId: string; name: string } | { id?: string; type: "prompt"; activeSessionId: string; message: string; content?: (TextContent | ImageContent)[]; images?: ImageContent[]; streamingBehavior?: "steer" | "followUp"; queueIfBusy?: boolean; expandPromptTemplates?: boolean; source?: InputSource; agentMessageId?: string; customMessage?: CustomMessage; /** Unique only when the caller needs cancellable pre-ownership admission. */ admissionId?: string; } | { id?: string; type: "cancel_prompt_admission"; activeSessionId: string; admissionId: string; /** Cancel session-owned work too when it has not started delivery. */ cancelOwned?: boolean; } | { id?: string; type: "prompt_and_wait"; activeSessionId: string; message: string; content?: (TextContent | ImageContent)[]; images?: ImageContent[]; streamingBehavior?: "steer" | "followUp"; queueIfBusy?: boolean; expandPromptTemplates?: boolean; source?: InputSource; /** Unique only when the caller needs cancellable pre-ownership admission. */ admissionId?: string; } | { id?: string; type: "steer"; activeSessionId: string; message: string; content?: (TextContent | ImageContent)[]; images?: ImageContent[]; queueKey?: string; expandPromptTemplates?: boolean; agentMessageId?: string; customMessage?: CustomMessage; prefixMessages?: CustomMessage[]; } | { id?: string; type: "follow_up"; activeSessionId: string; message: string; content?: (TextContent | ImageContent)[]; images?: ImageContent[]; queueKey?: string; expandPromptTemplates?: boolean; agentMessageId?: string; customMessage?: CustomMessage; prefixMessages?: CustomMessage[]; } | { id?: string; type: "restore_next_turn"; activeSessionId: string; messages: CustomMessage[] } | { id?: string; type: "restore_actions"; activeSessionId: string; snapshot: SessionActionRecoverySnapshot } | { id?: string; type: "append_custom_message"; activeSessionId: string; message: Pick<CustomMessage, "customType" | "content" | "display" | "details">; } | { id?: string; type: "resume_queue"; activeSessionId: string } | { id?: string; type: "send_message"; targetActiveSessionId: string; message: string; fromActiveSessionId?: string; /** Internal worker-origin marker; public clients remain unrestricted. */ agentOrigin?: boolean; deliveryMode?: AgentSessionMessageDeliveryMode; } | { id?: string; type: "agent_messages_status"; activeSessionId?: string } | { id?: string; type: "agent_messages_pause"; activeSessionId?: string } | { id?: string; type: "agent_messages_resume"; activeSessionId?: string } | { id?: string; type: "agent_messages_clear"; activeSessionId: string } | { id?: string; type: "abort"; activeSessionId: string } | { id?: string; type: "start_side_question"; activeSessionId: string; sideQuestionId: string; question: string; previousTurns?: AgentConnectionSideQuestionTurn[]; } | { id?: string; type: "abort_side_question"; activeSessionId: string; sideQuestionId: string } | { id?: string; type: "execute_bash"; activeSessionId: string; command: string; excludeFromContext?: boolean; transient?: boolean; runId?: string; } | { id?: string; type: "abort_bash"; activeSessionId: string } | { id?: string; type: "cancel_rlm_child"; activeSessionId: string; childId: string } | { id?: string; type: "delete_rlm_subagent"; activeSessionId: string; childId: string } | { id?: string; type: "wait_for_idle"; activeSessionId: string } | { id?: string; type: "wait_for_headless_completion"; activeSessionId: string; waitForRlmQuiescence?: boolean; } | { id?: string; type: "get_session_header"; activeSessionId: string } | { id?: string; type: "get_state"; activeSessionId: string } | { id?: string; type: "get_connection_state"; activeSessionId: string } | { id?: string; type: "get_messages"; activeSessionId: string } | { id?: string; type: "get_rlm_children"; activeSessionId: string } | { id?: string; type: "get_session_stats"; activeSessionId: string } | { id?: string; type: "get_context_tree"; activeSessionId: string } | { id?: string; type: "get_commands"; activeSessionId: string } | { id?: string; type: "get_resource_snapshot"; activeSessionId: string } | { id?: string; type: "replace_acp_mcp_servers"; activeSessionId: string; ownerId: string; servers: AcpMcpServerConfig[]; } | { id?: string; type: "get_model_catalog"; activeSessionId: string } | { id?: string; type: "get_available_models"; activeSessionId: string } | { id?: string; type: "get_queue"; activeSessionId: string } | { id?: string; type: "mutate_queued_message"; activeSessionId: string; lane: QueuedMessageLane; index: number; expectedText: string; mutation: QueuedMessageMutation; } | { id?: string; type: "clear_queue"; activeSessionId: string } | { id?: string; type: "abort_and_clear_queue"; activeSessionId: string } | { id?: string; type: "acquire_session_input_pause"; activeSessionId: string; leaseKey: string } | { id?: string; type: "release_session_input_pause"; activeSessionId: string; pauseId: string } | { id?: string; type: "cron_list"; activeSessionId?: string; includeInactive?: boolean } | { id?: string; type: "heartbeats_list"; activeSessionId?: string } | { id?: string; type: "heartbeat_manage"; activeSessionId: string; jobId: string; action: AgentHeartbeatManagementAction; } | { id?: string; type: "cron_add"; activeSessionId: string; schedule: string; prompt: string; promoteOwnedSession?: boolean; } | { id?: string; type: "cron_cancel"; activeSessionId?: string; jobId: string } | { id?: string; type: "heartbeat_get"; activeSessionId: string } | { id?: string; type: "heartbeat_set"; activeSessionId: string; schedule: string; prompt: string; deliveryMode?: AgentHeartbeatDeliveryMode; promoteOwnedSession?: boolean; } | { id?: string; type: "heartbeat_update"; activeSessionId: string; action: AgentHeartbeatUpdateAction } | { id?: string; type: "set_model"; activeSessionId: string; provider: string; modelId: string } | { id?: string; type: "cycle_model"; activeSessionId: string; direction?: "forward" | "backward" } | { id?: string; type: "set_scoped_models"; activeSessionId: string; scopedModels: AgentConnectionScopedModel[] } | { id?: string; type: "set_thinking_level"; activeSessionId: string; level: ThinkingLevel } | { id?: string; type: "set_service_tier"; activeSessionId: string; serviceTier: ServiceTier } | { id?: string; type: "cycle_thinking_level"; activeSessionId: string } | { id?: string; type: "set_transport"; activeSessionId: string; transport: Transport } | { id?: string; type: "set_steering_mode"; activeSessionId: string; mode: AgentConnectionQueueMode } | { id?: string; type: "set_follow_up_mode"; activeSessionId: string; mode: AgentConnectionQueueMode } | { id?: string; type: "set_auto_compaction"; activeSessionId: string; enabled: boolean } | { id?: string; type: "set_auto_retry"; activeSessionId: string; enabled: boolean } | { id?: string; type: "compact"; activeSessionId: string; customInstructions?: string } | { id?: string; type: "refine"; activeSessionId: string; instructions?: string; rollbackId?: string; global?: boolean; } | { id?: string; type: "abort_compaction"; activeSessionId: string } | { id?: string; type: "abort_branch_summary"; activeSessionId: string } | { id?: string; type: "abort_retry"; activeSessionId: string } | { id?: string; type: "execute_bash_and_wait"; activeSessionId: string; command: string } | { id?: string; type: "reload"; activeSessionId: string } | { id?: string; type: "new_session"; activeSessionId: string; parentSession?: string } | { id?: string; type: "switch_session"; activeSessionId: string; sessionPath: string; cwdOverride?: string } | { id?: string; type: "fork"; activeSessionId: string; entryId: string; position?: "before" | "at" } | { id?: string; type: "navigate_tree"; activeSessionId: string; targetId: string; summarize?: boolean; customInstructions?: string; replaceInstructions?: boolean; label?: string; } | { id?: string; type: "import_jsonl"; activeSessionId: string; inputPath: string; cwdOverride?: string } | { id?: string; type: "export_html"; activeSessionId: string; outputPath?: string } | { id?: string; type: "export_jsonl"; activeSessionId: string; outputPath?: string } | { id?: string; type: "set_session_name"; activeSessionId: string; name: string; workerToken?: string } | { id?: string; type: "get_rlm_max_depth_status"; activeSessionId: string } | { id?: string; type: "set_rlm_max_depth"; activeSessionId: string; maxDepth: number; global?: boolean } | { id?: string; type: "rename_saved_session"; activeSessionId?: string; sessionPath: string; name: string } | { id?: string; type: "delete_saved_session"; activeSessionId?: string; sessionPath: string } | { id?: string; type: "get_session_context"; activeSessionId: string } /** * `projection: "tree"` asks for tree-view entries (structure plus the * display, filter, and search fields) instead of full session entries. Only * send it when the daemon advertises the session_tree_projection * capability; without it the response carries `flatNodes` as before. */ | { id?: string; type: "get_session_tree"; activeSessionId: string; projection?: "tree" } | { id?: string; type: "get_user_messages_for_forking"; activeSessionId: string } | { id?: string; type: "get_last_assistant_text"; activeSessionId: string } | { id?: string; type: "get_system_prompt"; activeSessionId: string } | { id?: string; type: "get_tool_definition"; activeSessionId: string; name: string } | { id?: string; type: "set_session_entry_label"; activeSessionId: string; entryId: string; label?: string } | { id?: string; type: "extension_ui_response"; activeSessionId: string; requestId: string; response: DaemonExtensionUIResponse; } | { id?: string; type: "ack_result"; commandId: string } | { id?: string; type: "prepare_update_restart" } | { id?: string; type: "retry_worker"; activeSessionId: string } | { id?: string; type: "restart" } | { id?: string; type: "shutdown"; force?: boolean; reason?: DaemonClosingReason };
type DaemonCommandName = DaemonCommand["type"];
export interface DaemonCommandCompatibility { minProtocol: number; minSchemaRevision?: number; capability?: DaemonServerCapability;}
const LEGACY_DAEMON_COMMAND = { minProtocol: 7 } as const;const CURRENT_DAEMON_COMMAND = { minProtocol: 7 } as const;const RLM_MAX_DEPTH_COMMAND = { minProtocol: 7, minSchemaRevision: 11 } as const;const SESSION_INPUT_ADMISSION_COMMAND = { minProtocol: 7, capability: "session_input_admission",} as const;const PROMPT_ADMISSION_CANCELLATION_COMMAND = { minProtocol: 7, minSchemaRevision: 8, capability: "prompt_admission_cancellation",} as const;const OWNED_PROMPT_CANCELLATION_COMMAND = { minProtocol: 7, minSchemaRevision: 20, capability: "owned_prompt_cancellation",} as const;const CLIENT_OWNED_DAEMON_COMMAND = { minProtocol: 7, capability: "client_owned_sessions",} as const;const DELETE_RLM_SUBAGENT_COMMAND = { minProtocol: 7, capability: "delete_rlm_subagent",} as const;const FLAT_SESSION_TREE_COMMAND = { minProtocol: 7 } as const;const TELEMETRY_POLICY_COMMAND = { minProtocol: 7, minSchemaRevision: 14 } as const;const AUTHORITATIVE_CHILD_ROSTER_COMMAND = { minProtocol: 7, minSchemaRevision: 17, capability: "authoritative_child_roster",} as const;const OWNED_SESSION_RECOVERY_CONTEXT = { minProtocol: 7, minSchemaRevision: 17, capability: "owned_session_recovery_context",} as const;const RLM_QUIESCENCE_BARRIER_COMMAND = { minProtocol: 7, minSchemaRevision: 18, capability: "rlm_quiescence_barrier",} as const;const SESSION_INPUT_PAUSE_COMMAND = { minProtocol: 7, minSchemaRevision: 19, capability: "session_input_pause",} as const;const AGENT_PEER_LIST_COMMAND = { minProtocol: 7, minSchemaRevision: 23 } as const;const DIRECT_PEER_TRANSPORT_COMMAND = { minProtocol: 7, minSchemaRevision: 25, capability: "direct_peer_transport",} as const;
export const DAEMON_COMMAND_COMPATIBILITY = { ack_result: LEGACY_DAEMON_COMMAND, list: LEGACY_DAEMON_COMMAND, list_saved_sessions: LEGACY_DAEMON_COMMAND, list_agent_peers: AGENT_PEER_LIST_COMMAND, get_direct_worker_transport: DIRECT_PEER_TRANSPORT_COMMAND, create: LEGACY_DAEMON_COMMAND, attach: LEGACY_DAEMON_COMMAND, reattach: LEGACY_DAEMON_COMMAND, detach: LEGACY_DAEMON_COMMAND, complete_owned_session: CLIENT_OWNED_DAEMON_COMMAND, promote_owned_session: CLIENT_OWNED_DAEMON_COMMAND, kill: LEGACY_DAEMON_COMMAND, rename: LEGACY_DAEMON_COMMAND, prompt: SESSION_INPUT_ADMISSION_COMMAND, cancel_prompt_admission: PROMPT_ADMISSION_CANCELLATION_COMMAND, prompt_and_wait: SESSION_INPUT_ADMISSION_COMMAND, steer: SESSION_INPUT_ADMISSION_COMMAND, follow_up: SESSION_INPUT_ADMISSION_COMMAND, restore_next_turn: LEGACY_DAEMON_COMMAND, restore_actions: LEGACY_DAEMON_COMMAND, append_custom_message: LEGACY_DAEMON_COMMAND, resume_queue: SESSION_INPUT_ADMISSION_COMMAND, send_message: LEGACY_DAEMON_COMMAND, agent_messages_status: LEGACY_DAEMON_COMMAND, agent_messages_pause: LEGACY_DAEMON_COMMAND, agent_messages_resume: LEGACY_DAEMON_COMMAND, agent_messages_clear: LEGACY_DAEMON_COMMAND, abort: LEGACY_DAEMON_COMMAND, start_side_question: LEGACY_DAEMON_COMMAND, abort_side_question: LEGACY_DAEMON_COMMAND, execute_bash: LEGACY_DAEMON_COMMAND, abort_bash: LEGACY_DAEMON_COMMAND, cancel_rlm_child: LEGACY_DAEMON_COMMAND, delete_rlm_subagent: DELETE_RLM_SUBAGENT_COMMAND, wait_for_idle: LEGACY_DAEMON_COMMAND, wait_for_headless_completion: CURRENT_DAEMON_COMMAND, get_session_header: CURRENT_DAEMON_COMMAND, get_state: LEGACY_DAEMON_COMMAND, get_connection_state: LEGACY_DAEMON_COMMAND, get_messages: LEGACY_DAEMON_COMMAND, get_rlm_children: AUTHORITATIVE_CHILD_ROSTER_COMMAND, get_session_stats: LEGACY_DAEMON_COMMAND, get_context_tree: LEGACY_DAEMON_COMMAND, get_commands: LEGACY_DAEMON_COMMAND, get_resource_snapshot: LEGACY_DAEMON_COMMAND, replace_acp_mcp_servers: { minProtocol: 7, minSchemaRevision: 22, capability: "acp_mcp_servers" }, get_model_catalog: { minProtocol: 7, capability: "model_catalog" }, get_available_models: LEGACY_DAEMON_COMMAND, get_queue: LEGACY_DAEMON_COMMAND, mutate_queued_message: { minProtocol: 7, minSchemaRevision: 15, capability: "queue_message_mutation" }, clear_queue: LEGACY_DAEMON_COMMAND, abort_and_clear_queue: LEGACY_DAEMON_COMMAND, acquire_session_input_pause: SESSION_INPUT_PAUSE_COMMAND, release_session_input_pause: SESSION_INPUT_PAUSE_COMMAND, cron_list: LEGACY_DAEMON_COMMAND, heartbeats_list: { minProtocol: 7, capability: "heartbeat_catalog" }, roster_subscribe: { minProtocol: 7, capability: "agent_roster" }, roster_unsubscribe: { minProtocol: 7, capability: "agent_roster" }, heartbeat_manage: { minProtocol: 7, capability: "heartbeat_management" }, cron_add: LEGACY_DAEMON_COMMAND, cron_cancel: LEGACY_DAEMON_COMMAND, heartbeat_get: LEGACY_DAEMON_COMMAND, heartbeat_set: LEGACY_DAEMON_COMMAND, heartbeat_update: LEGACY_DAEMON_COMMAND, set_model: LEGACY_DAEMON_COMMAND, cycle_model: LEGACY_DAEMON_COMMAND, set_scoped_models: LEGACY_DAEMON_COMMAND, set_thinking_level: LEGACY_DAEMON_COMMAND, set_service_tier: LEGACY_DAEMON_COMMAND, cycle_thinking_level: LEGACY_DAEMON_COMMAND, set_transport: LEGACY_DAEMON_COMMAND, set_steering_mode: LEGACY_DAEMON_COMMAND, set_follow_up_mode: LEGACY_DAEMON_COMMAND, set_auto_compaction: LEGACY_DAEMON_COMMAND, set_auto_retry: CURRENT_DAEMON_COMMAND, compact: LEGACY_DAEMON_COMMAND, refine: LEGACY_DAEMON_COMMAND, abort_compaction: LEGACY_DAEMON_COMMAND, abort_branch_summary: LEGACY_DAEMON_COMMAND, abort_retry: LEGACY_DAEMON_COMMAND, execute_bash_and_wait: CURRENT_DAEMON_COMMAND, reload: LEGACY_DAEMON_COMMAND, new_session: LEGACY_DAEMON_COMMAND, switch_session: LEGACY_DAEMON_COMMAND, fork: LEGACY_DAEMON_COMMAND, navigate_tree: LEGACY_DAEMON_COMMAND, import_jsonl: LEGACY_DAEMON_COMMAND, export_html: LEGACY_DAEMON_COMMAND, export_jsonl: LEGACY_DAEMON_COMMAND, set_session_name: LEGACY_DAEMON_COMMAND, get_rlm_max_depth_status: RLM_MAX_DEPTH_COMMAND, set_rlm_max_depth: RLM_MAX_DEPTH_COMMAND, rename_saved_session: LEGACY_DAEMON_COMMAND, delete_saved_session: LEGACY_DAEMON_COMMAND, get_session_context: LEGACY_DAEMON_COMMAND, get_session_tree: FLAT_SESSION_TREE_COMMAND, get_user_messages_for_forking: LEGACY_DAEMON_COMMAND, get_last_assistant_text: LEGACY_DAEMON_COMMAND, get_system_prompt: LEGACY_DAEMON_COMMAND, get_tool_definition: LEGACY_DAEMON_COMMAND, set_session_entry_label: LEGACY_DAEMON_COMMAND, extension_ui_response: LEGACY_DAEMON_COMMAND, prepare_update_restart: LEGACY_DAEMON_COMMAND, retry_worker: LEGACY_DAEMON_COMMAND, restart: LEGACY_DAEMON_COMMAND, shutdown: LEGACY_DAEMON_COMMAND,} as const satisfies Record<DaemonCommandName, DaemonCommandCompatibility>;
/** * Which endpoint serves each command when a client holds both a supervisor * (control-plane) and a direct worker (session-plane) connection. Session is * the default for session-addressed commands; "control" marks supervisor * semantics: worker lifecycle, cross-worker targeting, daemon-global * registries, and names a worker resolves differently (a worker's "list" is * only its own sessions). Total over DaemonCommand["type"], so an * unclassified command is a compile error and never routes direct. */export const DAEMON_COMMAND_PLANE = { ack_result: "control", list: "control", list_saved_sessions: "control", list_agent_peers: "control", get_direct_worker_transport: "control", create: "control", attach: "session", reattach: "control", detach: "session", complete_owned_session: "control", promote_owned_session: "control", kill: "control", rename: "control", prompt: "session", cancel_prompt_admission: "session", prompt_and_wait: "session", steer: "session", follow_up: "session", restore_next_turn: "session", restore_actions: "session", append_custom_message: "session", resume_queue: "session", send_message: "control", agent_messages_status: "control", agent_messages_pause: "control", agent_messages_resume: "control", agent_messages_clear: "control", abort: "session", start_side_question: "session", abort_side_question: "session", execute_bash: "session", abort_bash: "session", cancel_rlm_child: "session", delete_rlm_subagent: "session", wait_for_idle: "session", wait_for_headless_completion: "session", get_session_header: "session", get_state: "session", get_connection_state: "session", get_messages: "session", get_rlm_children: "session", get_session_stats: "session", get_context_tree: "session", get_commands: "session", get_resource_snapshot: "session", replace_acp_mcp_servers: "session", get_model_catalog: "session", get_available_models: "session", get_queue: "session", mutate_queued_message: "session", clear_queue: "session", abort_and_clear_queue: "session", acquire_session_input_pause: "session", release_session_input_pause: "session", cron_list: "control", heartbeats_list: "control", roster_subscribe: "control", roster_unsubscribe: "control", heartbeat_manage: "control", cron_add: "control", cron_cancel: "control", heartbeat_get: "control", heartbeat_set: "control", heartbeat_update: "control", set_model: "session", cycle_model: "session", set_scoped_models: "session", set_thinking_level: "session", set_service_tier: "session", cycle_thinking_level: "session", set_transport: "session", set_steering_mode: "session", set_follow_up_mode: "session", set_auto_compaction: "session", set_auto_retry: "session", compact: "session", refine: "session", abort_compaction: "session", abort_branch_summary: "session", abort_retry: "session", execute_bash_and_wait: "session", reload: "session", new_session: "session", switch_session: "session", fork: "session", navigate_tree: "session", import_jsonl: "session", export_html: "session", export_jsonl: "session", set_session_name: "control", get_rlm_max_depth_status: "session", set_rlm_max_depth: "session", rename_saved_session: "control", delete_saved_session: "control", get_session_context: "session", get_session_tree: "session", get_user_messages_for_forking: "session", get_last_assistant_text: "session", get_system_prompt: "session", get_tool_definition: "session", set_session_entry_label: "session", extension_ui_response: "session", prepare_update_restart: "control", retry_worker: "control", restart: "control", shutdown: "control",} as const satisfies Record<DaemonCommandName, "session" | "control">;
export function isSessionPlaneDaemonCommand(type: string): boolean { return (DAEMON_COMMAND_PLANE as Record<string, "session" | "control" | undefined>)[type] === "session";}
export function getDaemonCommandCompatibilities(command: DaemonCommand): readonly DaemonCommandCompatibility[] { const requirements: DaemonCommandCompatibility[] = []; if ((command.type === "attach" || command.type === "reattach") && command.recoveryConfig !== undefined) { requirements.push(OWNED_SESSION_RECOVERY_CONTEXT); } const carriesTelemetryPolicy = ((command.type === "attach" || command.type === "reattach") && command.telemetryDisabled !== undefined) || (command.type === "create" && command.config?.telemetryDisabled !== undefined); if (carriesTelemetryPolicy) requirements.push(TELEMETRY_POLICY_COMMAND); if ((command.type === "prompt" || command.type === "prompt_and_wait") && command.admissionId !== undefined) { requirements.push(PROMPT_ADMISSION_CANCELLATION_COMMAND); } if (command.type === "wait_for_headless_completion" && command.waitForRlmQuiescence === true) { requirements.push(RLM_QUIESCENCE_BARRIER_COMMAND); } if (command.type === "cancel_prompt_admission" && command.cancelOwned === true) { requirements.push(OWNED_PROMPT_CANCELLATION_COMMAND); } return [...requirements, DAEMON_COMMAND_COMPATIBILITY[command.type]];}
export function meetsDaemonCommandCompatibility( hello: { protocol: DaemonProtocolInfo; schemaRevision?: number; serverCapabilities?: readonly DaemonServerCapability[]; }, compatibility: DaemonCommandCompatibility,): boolean { return ( hello.protocol.version >= compatibility.minProtocol && (compatibility.minSchemaRevision === undefined || (hello.schemaRevision ?? 0) >= compatibility.minSchemaRevision) && (compatibility.capability === undefined || hello.serverCapabilities?.includes(compatibility.capability) === true) );}
export type DaemonResponse = | { id?: string; type: "response"; command: string; success: true; data?: unknown } | { id?: string; type: "response"; command: string; success: false; error: string; errorInfo?: DaemonErrorInfo; };
export type DaemonErrorInfo = | { code: "missing_session_cwd"; issue: SessionCwdIssue } | { code: "session_import_file_not_found"; filePath: string } | { code: "session_already_active"; sessionPath: string; activeSessionId?: string } | { code: "session_recovering"; activeSessionId: string } | { code: "session_starting"; activeSessionId?: string } | { code: "command_result_uncertain"; clientId: DaemonClientId; commandId: DaemonCommandId };
export type DaemonSessionClosedReason = "killed" | "shutdown" | "completed" | "replaced" | "update";export type DaemonClosingReason = "shutdown" | "update";
export type DaemonExtensionUIResponse = { value: string } | { confirmed: boolean } | { cancelled: true };
export function isDaemonDialogExtensionUiRequest(method: string): boolean { return method === "select" || method === "confirm" || method === "input" || method === "editor";}
/** * True when a daemon rejected a command it does not know, i.e. the daemon * process was started from a build that predates the command. */export function isUnknownDaemonCommandError(error: unknown, command: DaemonCommand["type"]): boolean { return error instanceof Error && error.message.includes(`Unknown daemon command: ${command}`);}
export type DaemonRequestProgress = | { id?: string; type: "session_list_progress"; command: "list_saved_sessions"; activeSessionId?: string; loaded: number; total: number; } | { id?: string; type: "session_list_item"; command: "list_saved_sessions"; activeSessionId?: string; session: DaemonSavedSessionInfo; };
export interface DaemonSavedSessionInfo { path: string; id: string; cwd: string; name?: string; state?: AgentConnectionSavedSessionState; parentSessionPath?: string; rlmDepth?: number; created: string; modified: string; messageCount: number; firstMessage: string; allMessagesText: string; agentStatus?: AgentConnectionAgentStatus; usage?: SessionUsageSummary; /** Last recorded provider/model selector; absent for sessions that never ran a model. */ model?: { provider: string; modelId: string };}
export type DaemonDeleteSavedSessionResult = DeleteSessionFileResult;export type DaemonAutonomousStatus = AgentAutonomousStatus;export type DaemonBashResult = BashResult;export type DaemonSessionHeader = AgentConnectionSessionHeader;
export type DaemonResourceSnapshot = AgentConnectionResourceSnapshot;
export type DaemonCronJob = AgentCronJob;export type DaemonHeartbeat = AgentConnectionHeartbeat;export type DaemonAgentSessionMessageReceipt = AgentSessionMessageReceipt;export type DaemonAgentSessionMessageSafetyStatus = AgentSessionMessageSafetyStatus;
export type DaemonOutbound = | DaemonResponse | DaemonRequestProgress | { type: "daemon_hello"; socketPath: string; protocol: DaemonProtocolInfo; schemaId?: string; /** Monotonic wire-schema revision for field-sensitive compatibility checks. */ schemaRevision?: number; /** App version of the daemon process, used to detect stale daemons after self-update. */ appVersion?: string; runtime?: DaemonRuntimeIdentity; /** Changes whenever the public supervisor process is replaced. */ supervisorGeneration?: string; /** Diagnostic process identity for attributing supervisor replacement. */ supervisorPid?: number; /** Durable owner marker for validating update handoff fences. */ supervisorOwnerToken?: string; /** Process start identity captured when the durable owner was published. */ supervisorProcessStartId?: string; /** Normalized socket identity stored in the durable owner record. */ supervisorSocketPath?: string; clientId: DaemonClientId; serverCapabilities: readonly DaemonServerCapability[]; } | { type: "daemon_closing"; reason: DaemonClosingReason } | { type: "heartbeats_changed" } | { type: "roster_update"; changed: AgentRosterEntry[]; removed?: string[]; resync?: true } | { type: "session_event"; activeSessionId: string; event: AgentConnectionSessionEvent; meta?: DaemonEventMeta } | { type: "side_question_event"; activeSessionId: string; event: AgentConnectionSideQuestionEvent } | { type: "session_status"; activeSessionId: string; recap?: string; meta?: DaemonEventMeta } | { type: "session_replaced"; activeSessionId: string; state: AgentConnectionState; messages: AgentMessage[]; snapshotFollows?: boolean; meta?: DaemonEventMeta; } | { type: "session_resynced"; activeSessionId: string; snapshot: DaemonSessionSnapshot; meta?: DaemonEventMeta; } | { type: "session_attached"; activeSessionId: string; state: SessionSummary; messages: AgentMessage[]; snapshot?: DaemonSessionSnapshot; replay?: DaemonReplayInfo; lastEventSequence?: DaemonEventSequence; } | { type: "session_snapshot_begin"; activeSessionId: string; snapshotId: string; snapshot: Omit<DaemonSessionSnapshot, "messages">; messageCount: number; targetChunkBytes: number; purpose?: "attach" | "replacement" | "resync"; } | { type: "session_snapshot_chunk"; activeSessionId: string; snapshotId: string; index: number; messages: AgentMessage[]; } | { type: "session_snapshot_end"; activeSessionId: string; snapshotId: string; chunkCount: number; lastEventSequence: DaemonEventSequence; lastEventCursor?: DaemonEventCursor; } | { type: "session_snapshot_failed"; activeSessionId: string; snapshotId: string; error: string; } | { type: "session_detached"; activeSessionId: string } | { type: "session_closed"; activeSessionId: string; reason: DaemonSessionClosedReason; meta?: DaemonEventMeta } | { type: "extension_ui_request"; activeSessionId: string; id: string; method: string; payload: Record<string, unknown>; meta?: DaemonEventMeta; } | { type: "extension_error"; activeSessionId: string; extensionPath: string; event: string; error: string; meta?: DaemonEventMeta; };
export const DAEMON_OUTBOUND_COMPATIBILITY = { response: LEGACY_DAEMON_COMMAND, session_list_progress: LEGACY_DAEMON_COMMAND, session_list_item: LEGACY_DAEMON_COMMAND, daemon_hello: LEGACY_DAEMON_COMMAND, daemon_closing: LEGACY_DAEMON_COMMAND, heartbeats_changed: { minProtocol: 7, capability: "heartbeat_catalog" }, roster_update: { minProtocol: 7, capability: "agent_roster" }, session_event: LEGACY_DAEMON_COMMAND, side_question_event: LEGACY_DAEMON_COMMAND, session_status: LEGACY_DAEMON_COMMAND, session_replaced: LEGACY_DAEMON_COMMAND, session_resynced: LEGACY_DAEMON_COMMAND, session_attached: LEGACY_DAEMON_COMMAND, session_snapshot_begin: LEGACY_DAEMON_COMMAND, session_snapshot_chunk: LEGACY_DAEMON_COMMAND, session_snapshot_end: LEGACY_DAEMON_COMMAND, session_snapshot_failed: LEGACY_DAEMON_COMMAND, session_detached: LEGACY_DAEMON_COMMAND, session_closed: LEGACY_DAEMON_COMMAND, extension_ui_request: LEGACY_DAEMON_COMMAND, extension_error: LEGACY_DAEMON_COMMAND,} as const satisfies Record<DaemonOutbound["type"], DaemonCommandCompatibility>;
export function createDaemonCommandEnvelope<TCommand extends DaemonCommand>( command: TCommand, id: DaemonCommandId, clientId?: DaemonClientId, protocolVersion: DaemonProtocolVersion = DAEMON_PROTOCOL_VERSION,): DaemonCommandEnvelope<TCommand> { return { type: "command", id, protocol: { name: DAEMON_PROTOCOL_NAME, version: protocolVersion }, ...(clientId ? { clientId } : {}), command, };}
export function isDaemonCommandEnvelope(value: unknown): value is DaemonCommandEnvelope { if (!value || typeof value !== "object") { return false; } const candidate = value as { type?: unknown; id?: unknown; protocol?: { name?: unknown; version?: unknown }; clientId?: unknown; command?: unknown; }; return ( candidate.type === "command" && typeof candidate.id === "string" && candidate.protocol?.name === DAEMON_PROTOCOL_NAME && typeof candidate.protocol.version === "number" && candidate.protocol.version >= DAEMON_COMMAND_ENVELOPE_MIN_PROTOCOL_VERSION && candidate.protocol.version <= DAEMON_PROTOCOL_VERSION && (candidate.clientId === undefined || typeof candidate.clientId === "string") && typeof candidate.command === "object" && candidate.command !== null );}
/** * Best-effort id salvage for rejected command lines, so parse failures reach * the sender as correlatable responses instead of client-side timeouts. * Deliberately ignores everything but the id itself: whatever made the line * unparseable (rejected protocol version, missing or invalid type), the * sender still correlates the failure by id. */export function salvageDaemonCommandId(line: string): string | undefined { try { const candidate = JSON.parse(line) as { id?: unknown }; if (!candidate || typeof candidate !== "object") { return undefined; } return typeof candidate.id === "string" ? candidate.id : undefined; } catch { return undefined; }}
const READ_ONLY_DAEMON_COMMANDS: ReadonlySet<DaemonCommand["type"]> = new Set([ "ack_result", "list", "list_saved_sessions", "list_agent_peers", "get_direct_worker_transport", "attach", "reattach", "roster_subscribe", "roster_unsubscribe", "agent_messages_status", "wait_for_idle", "get_session_header", "get_state", "get_connection_state", "get_messages", "get_rlm_children", "get_session_stats", "get_context_tree", "get_commands", "get_resource_snapshot", "get_model_catalog", "get_available_models", "get_queue", "cron_list", "heartbeats_list", "heartbeat_get", "get_session_context", "get_session_tree", "get_user_messages_for_forking", "get_last_assistant_text", "get_system_prompt", "get_rlm_max_depth_status", "get_tool_definition",]);
export function isDaemonMutatingCommand(command: Pick<DaemonCommand, "type">): boolean { return !READ_ONLY_DAEMON_COMMANDS.has(command.type);}
export const UPDATE_RESTART_DRAIN_COMMANDS: ReadonlySet<DaemonCommand["type"]> = new Set([ "extension_ui_response", "abort", "abort_bash", "abort_branch_summary", "abort_compaction", "abort_retry",]);
export function createDaemonEventEnvelope<TEvent extends DaemonOutbound>( event: TEvent, meta: DaemonEventMeta,): DaemonEventEnvelope<TEvent> { return { type: "event", id: meta.id, protocol: meta.protocol, ...(meta.activeSessionId ? { activeSessionId: meta.activeSessionId } : {}), ...(meta.sequence !== undefined ? { sequence: meta.sequence } : {}), ...(meta.cursor ? { cursor: meta.cursor } : {}), emittedAt: meta.emittedAt, event, };}
export function createDaemonEventMeta( activeSessionId: string, sequence: DaemonEventSequence, emittedAt = new Date().toISOString(), generation = activeSessionId,): DaemonEventMeta { return { id: `${activeSessionId}:${sequence}`, protocol: DAEMON_PROTOCOL_INFO, activeSessionId, sequence, cursor: { generation, sequence }, emittedAt, };}
export function createDaemonReplayInfo( resumeCursor: DaemonResumeCursor | undefined, lastEventSequence: DaemonEventSequence, generation = "legacy",): DaemonReplayInfo { const toCursor = { generation, sequence: lastEventSequence }; if (!resumeCursor) { return { status: "complete", toSequence: lastEventSequence, toCursor, }; } const resumeSequence = "sequence" in resumeCursor ? resumeCursor.sequence : resumeCursor.eventSequence; const fromCursor = "generation" in resumeCursor ? { generation: resumeCursor.generation, sequence: resumeCursor.sequence } : undefined; if (fromCursor && fromCursor.generation !== generation) { return { status: "unavailable", fromSequence: resumeSequence, toSequence: lastEventSequence, fromCursor, toCursor, reason: "event_generation_changed", }; }
if (resumeSequence > lastEventSequence) { return { status: "unavailable", fromSequence: resumeSequence, toSequence: lastEventSequence, ...(fromCursor ? { fromCursor } : {}), toCursor, reason: "resume_cursor_ahead_of_session", }; }
if (resumeSequence === lastEventSequence) { return { status: "complete", fromSequence: resumeSequence, toSequence: lastEventSequence, ...(fromCursor ? { fromCursor } : {}), toCursor, }; }
return { status: "unavailable", fromSequence: resumeSequence, toSequence: lastEventSequence, ...(fromCursor ? { fromCursor } : {}), toCursor, reason: "event_replay_not_available", };}
export function success(id: string | undefined, command: DaemonCommandName, data?: unknown): DaemonResponse { return data === undefined ? { id, type: "response", command, success: true } : { id, type: "response", command, success: true, data };}
export function failure( id: string | undefined, command: string, error: unknown, errorInfo?: DaemonErrorInfo,): DaemonResponse { return { id, type: "response", command, success: false, error: error instanceof Error ? error.message : String(error), ...(errorInfo ? { errorInfo } : {}), };}