import type { Readable, Writable } from "node:stream"; import { type DaemonCommandResultMap, type DaemonCommandParamsMap, type DaemonControlRequestFrame, type DaemonQueryMethod, type DaemonWireSessionMessage, type JsonObject, type JsonValue, type OpenSessionParams, type RenameSessionResult } from "./protocol.js"; import type { SdkAuthPayload } from "../types/auth.js"; import { DaemonSessionTransport, type DaemonSessionTransportOptions } from "./daemon-session-transport.js"; import type { DiagnosticsSink } from "../core/sdk-diagnostics.js"; export declare const SESSION_STORE_FILE_CHECKPOINTING_CAPABILITY = "session_store_file_checkpointing_v1"; export declare const SESSION_STORE_FORK_BARRIER_CAPABILITY = "session_store_fork_barrier_v1"; export interface DaemonConnectionOptions { input: Writable; output: Readable; requestHandler?: DaemonRequestHandler; maxFrameBytes?: number; /** * 对端为什么没了——由持有子进程的一侧回答。 * * stdout EOF 与子进程 'exit' 是两个独立事件,先后顺序不确定,而 `fail()` 是 * 「第一个失败者获胜」(`if (this.failure) return`)。若让裸的 EOF 先落定, * 退出码与 stderr 尾巴就永久丢失,宿主只拿到一句 "Daemon connection closed"—— * 这正是联调时最难排查的形态:daemon 明明在 stderr 上写清了原因。 * 所以 EOF 路径先向进程侧要一次退出上下文;要得到就用它带来的错误落定。 * 返回 undefined 表示进程确实还活着(真正的半开管道),此时才退回笼统文案。 */ describeUnexpectedClose?: () => Promise; /** * SDK 侧诊断输出。daemon 是多会话共用的单进程,一条连接上的请求必须靠 * `request_id` + `session_id` 才能归属,所以这里每个记录点都带这两个字段。 * 只打协议元数据(方法名、id、耗时、字节数),绝不打帧原文、凭据与 prompt 正文。 */ diagnostics?: DiagnosticsSink; } export interface DaemonInitializeOptions { /** Advertised in addition to the capability this SDK cannot work without. */ capabilities?: readonly string[]; timeoutMs?: number; /** * Daemon-level credentials, sent in-band on the initialize frame. This is * what unlocks the account-level query plane; Session management works * without it. Carries a plaintext credential — never log it. */ auth?: SdkAuthPayload; } export type DaemonRequestHandler = (request: DaemonControlRequestFrame) => Promise; export declare class DaemonRpcError extends Error { readonly code: string; readonly retryable: boolean; readonly details: JsonObject | undefined; constructor(options: { code: string; message: string; retryable: boolean; details?: JsonObject; }); } export declare class DaemonRequestError extends Error { readonly code: string; readonly retryable: boolean; readonly details: JsonObject | undefined; constructor(options: { code: string; message: string; retryable: boolean; details?: JsonObject; }); } export declare class DaemonConnection { private readonly input; private readonly requestHandler; private readonly pendingRequests; private readonly timedOutRequestIds; private readonly activeInboundRequestIds; private readonly sessions; private readonly closedSessionIds; /** * 每个 daemon-bound 会话 open 时携带的工作目录(sessionId → cwd)。daemon 已无 cwd * 概念,rename/delete 的磁盘定位目录只能来自请求;宿主调用 renameSession/deleteSession * 通常不再显式传 cwd,故这里记住会话自己的 cwd,发请求时自动补上。会话关闭后仍保留, * 因为「关掉后再改名/删除历史会话」是正常路径(此时 CLI 侧已无在世 cwd 可回落)。 */ private readonly sessionCwds; private readonly pendingSessionMessages; private readonly pendingSessionMessageBytes; private pendingSessionMessageFrameCount; private pendingSessionMessageByteCount; private readonly maxFrameBytes; private readonly readTask; private writeChain; private nextRequestId; private initializing; private initialized; private readonly capabilities; private stopping; private failure; private readonly failureListeners; private readonly describeUnexpectedClose; private readonly diagnostics; constructor(options: DaemonConnectionOptions); initialize(sdkVersion: string, options?: DaemonInitializeOptions): Promise; createSessionTransport(options: DaemonSessionTransportOptions): DaemonSessionTransport; supportsCapability(capability: string): boolean; onFailure(listener: (error: Error) => void): () => void; openSession(params: OpenSessionParams, transport: DaemonSessionTransport): Promise; listSessions(cwd?: string): Promise; renameSession(sessionId: string, title: string, cwd?: string): Promise; sendSessionMessage(message: DaemonWireSessionMessage): Promise; closeSession(sessionId: string): Promise; deleteSession(localSessionId: string, cwd?: string): Promise; /** * Sends one account-level query (`daemon_queries_v1`). * * Rejected with `DaemonProtocolError` when the daemon never reported the * capability, and with `DaemonRpcError` carrying the daemon's error code when * the query itself fails. The timeout is request-scoped: a slow upstream call * fails that one query, not the connection. */ query(method: Method, params: DaemonCommandParamsMap[Method]): Promise; shutdown(timeoutMs?: number): Promise; /** * EOF 落定前问一次进程侧:对端是不是已经退了、为什么退。 * 提供者自己带 grace 超时,所以这里不会无限等;它抛错或答不上来就退回笼统文案。 */ private resolveUnexpectedClose; fail(error: unknown, notifyListeners?: boolean): void; private request; private readOutput; private acceptFrame; private rememberTimedOutRequestId; private routeSessionMessage; private acceptControlResponse; private hasPendingSessionRequest; private failIfSessionMessagesAreOrphaned; private bufferPendingSessionMessage; private takePendingSessionMessages; private deletePendingSessionMessages; private clearPendingSessionMessages; private rejectLegacyInitialize; private acceptRequest; private writeInboundError; private acceptSessionUpdatedStatusEvent; private writeFrame; }