import { type BrokerPeer } from '../peerBroker.js'; import { SessionError } from './errors.js'; import { type TmuxSession } from './tmux.js'; type NodePty = typeof import('node-pty'); export declare function loadPty(): Promise; export type SessionOrigin = 'spawned' | 'adopted' | 'peer'; /** One row of GET /sessions. Unknown fields are omitted, never null. */ export interface HostedSession { session_key: string; /** Omitted when the session's cwd is in no registered project. */ project_id?: string; origin: SessionOrigin; state: 'live' | 'exited'; attachable: boolean; attach_handle?: string; /** Live spawned sessions: the exact argv to attach from a terminal (ADR-0018). */ attach_argv?: string[]; claude_session_id?: string; peer_id?: string; pid?: number; tty?: string; cwd?: string; branch?: string; issue_label?: string; summary?: string; created?: string; } export { SessionError }; export declare function isUuid(v: unknown): v is string; export interface StrictProject { id: string; path: string; } /** Exact id or alias in projects.json, available on this device. Nothing else. */ export declare function resolveRegisteredProject(projectId: string): StrictProject; export declare function isWithin(child: string, parent: string): boolean; export interface ListDeps { ownedSessions?: () => Promise; userSessions?: () => Promise; ttyMap?: () => Promise>; peers?: () => Promise; ptyAvailable?: () => Promise; projects?: () => StrictProject[]; } /** Every registered project available on this device. */ export declare function availableProjects(): StrictProject[]; /** * The registered project a path belongs to: the LONGEST registered path that * contains it, so a checkout nested inside another project belongs to itself. * Undefined when nothing contains it (a session in ~/Documents/GitHub itself, * or in a repo that was never registered). */ export declare function projectForPath(path: string, projects: StrictProject[]): StrictProject | undefined; /** * The session roster. With a project id, that project's sessions only (and an * unregistered id is an error, never a fallback). Without one, every session * on the machine; `project_id` is present where the session resolves to a * registered project and omitted where it does not. */ export declare function listHostedSessions(projectId?: string, deps?: ListDeps): Promise; /** * What an attach token may be minted for: a live spawned session, or a tmux * session that is hosting a claude-peers session. Never an arbitrary tmux * session — the user's other tmux work is not HQ's to open. */ export declare function resolveAttachable(key: string): Promise; /** * launchd gives the daemon no login PATH. Find claude by absolute path, then * run it under a login shell so it gets the user's environment. */ export declare function resolveClaude(): string; /** * argv for the pane: `$SHELL -lc 'exec "$0" "$@"' `. The * claude path and arguments travel as positional parameters, so nothing is * interpolated into the shell script. */ export declare function claudeCommand(claudeArgs: string[]): string[]; export declare function sessionNameFor(projectId: string): string; export interface SpawnInput { project_id?: unknown; branch?: unknown; issue_label?: unknown; resume?: unknown; } /** * Daemon-internal spawn options. Never read from a request body: a caller * that can choose the cwd chooses where `claude` runs. */ export interface SpawnInternal { /** * Fleet restore (ADR-0018): the cwd the session was recorded in. Claude * keys transcripts by cwd, so a session started in a subdirectory resumes * only from that subdirectory. Must lie inside the registered project. */ cwd?: string; /** * A room session (ISS-0229): runs in the project's room worktree under * ~/.decibel/rooms (outside the project checkout, deliberately), with a * fixed name and the room deny rules. Set only by the room dispatcher. */ room?: { worktree: string; name: string; extraArgs: string[]; }; } export interface SpawnResult { session_key: string; claude_session_id: string; attach_argv: string[]; /** Set when the dev-channels dialog could not be confirmed (ADR-0018 D4). */ needs_attention?: { code: AttentionCode; error: string; }; } /** * The argv for attaching to an owned session from a terminal (ADR-0018). The * absolute tmux path, because launchd's PATH and a login shell's can resolve * different binaries, and a client/server version mismatch refuses to attach. * `=key` is an exact-match target. Not secret. */ export declare function attachArgv(key: string): string[]; /** * Apply launch.json's tmux options to the owned server, if it is running. * Spawns apply them too; this covers a daemon restart, where the tmux server * and its sessions outlive the daemon. A missing server or a bad launch.json * is logged, never thrown: the daemon must still boot. */ export declare function applyTmuxProfile(): Promise; /** * Scroll a session's pane through tmux copy-mode, for a client with no * scrollback of its own (HQ's xterm.js pane: tmux draws on the alternate * screen, and the owned server runs with mouse off). Never a keystroke into * the session: copy-mode is tmux's own view. * * {lines: n<0} enter copy-mode -e and scroll up |n| lines * {lines: n>0} scroll down; -e leaves copy-mode at the bottom by itself * {cancel: true} leave copy-mode * * Down and cancel do nothing when the pane is not in copy-mode. HQ's line * counter can overcount past the top of history, and it sends cancel before * the first keystroke after any scroll, so both arrive when nothing is * scrolled. tmux answers "not in a mode" then, which is the no-op. */ export declare function scrollPane(key: string, frame: { lines?: unknown; cancel?: unknown; }): Promise; export declare function spawnSession(input: SpawnInput, internal?: SpawnInternal): Promise; export type AttentionCode = 'DIALOG_UNRECOGNISED' | 'DIALOG_TIMEOUT' | 'CLAUDE_EXITED'; export declare const DIALOG_TIMEOUT_MS = 15000; /** Exact match for the dev-channels warning, cursor on "1.". */ export declare function isDevChannelsDialog(screen: string): boolean; export interface ConfirmDeps { capture?: (key: string) => Promise; isDead?: (key: string) => Promise; sendEnter?: (key: string) => Promise; sleep?: (ms: number) => Promise; now?: () => number; timeoutMs?: number; } /** Null when confirmed; the attention code and message otherwise. */ export declare function confirmDevChannels(key: string, deps?: ConfirmDeps): Promise<{ code: AttentionCode; error: string; } | null>; export type ControlAction = 'interrupt' | 'kill' | 'resume'; export declare function controlSession(key: string, action: unknown): Promise<{ ok: true; action: ControlAction; needs_attention?: { code: AttentionCode; error: string; }; }>; //# sourceMappingURL=sessions.d.ts.map