@adhdev/session-host-core 1.0.28-rc.17 → 1.0.28-rc.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +59 -2
- package/dist/index.d.ts +59 -2
- package/dist/index.js +75 -19
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +70 -19
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +8 -1
- package/src/instance-key.ts +108 -0
- package/src/ipc.ts +26 -4
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/types.ts","../src/termination.ts","../src/buffer.ts","../src/defaults.ts","../src/runtime-labels.ts","../src/registry.ts","../src/ipc.ts","../src/spawn-env.ts","../src/control-plane.ts"],"sourcesContent":["export type {\n AcquireWritePayload,\n AttachSessionPayload,\n ClearSessionBufferPayload,\n CreateSessionPayload,\n DetachSessionPayload,\n ForceDetachClientPayload,\n GetHostDiagnosticsPayload,\n GetSnapshotPayload,\n GetTerminalSnapshotPayload,\n PruneDuplicateSessionsPayload,\n ReleaseWritePayload,\n ResumeSessionPayload,\n RestartSessionPayload,\n ResizeSessionPayload,\n SendSignalPayload,\n SendInputPayload,\n UpdateSessionMetaPayload,\n SessionAttachedClient,\n SessionBufferSnapshot,\n SessionClientType,\n SessionHostDiagnostics,\n SessionHostDuplicateSessionGroup,\n SessionHostCategory,\n SessionHostSurfaceKind,\n SessionHostEvent,\n SessionHostEventEnvelope,\n SessionHostLogEntry,\n SessionHostPruneDuplicatesResult,\n SessionHostRecord,\n SessionHostRequest,\n SessionHostRequestEnvelope,\n SessionHostRequestTrace,\n SessionHostRequestType,\n SessionHostResponse,\n SessionHostResponseEnvelope,\n SessionHostRuntimeTransition,\n SessionTerminalSnapshot,\n SessionTerminalState,\n SessionLaunchCommand,\n SessionLifecycle,\n SessionOwnerType,\n SessionTransport,\n SessionTermination,\n SessionHostWireEnvelope,\n SessionWriteOwner,\n StopSessionPayload,\n} from './types.js';\n\nexport { SESSION_HOST_SUPPORTED_REQUEST_TYPES } from './types.js';\nexport { classifyTermination } from './termination.js';\n\nexport { DEFAULT_SESSION_RING_BUFFER_MAX_BYTES, SessionRingBuffer } from './buffer.js';\nexport type { SessionRingBufferOptions } from './buffer.js';\nexport { SessionHostRegistry } from './registry.js';\nexport {\n buildRuntimeDisplayName,\n buildRuntimeKey,\n formatRuntimeOwner,\n getSessionHostRecoveryLabel,\n getSessionHostSurfaceKind,\n getWorkspaceLabel,\n isSessionHostLiveRuntime,\n isSessionHostRecoverySnapshot,\n partitionSessionHostDiagnosticsSessions,\n partitionSessionHostRecords,\n resolveAttachableRuntimeRecord,\n resolveRuntimeRecord,\n} from './runtime-labels.js';\nexport type { SessionHostSurfaceRecordLike } from './runtime-labels.js';\nexport {\n SessionHostClient,\n createLineParser,\n createResponseEnvelope,\n getDefaultSessionHostEndpoint,\n writeEnvelope,\n} from './ipc.js';\nexport type { SessionHostClientOptions, SessionHostEndpoint } from './ipc.js';\nexport {\n DEFAULT_SESSION_HOST_COLS,\n DEFAULT_SESSION_HOST_ROWS,\n resolveSessionHostCols,\n resolveSessionHostRows,\n} from './defaults.js';\nexport {\n sanitizeSpawnEnv,\n applyTerminalColorEnv,\n ensureNodePtySpawnHelperPermissions,\n} from './spawn-env.js';\nexport { createSessionHostControlPlane } from './control-plane.js';\nexport type { SessionHostControlPlane, SessionHostControlTransport } from './control-plane.js';\n","export type SessionTransport = 'pty';\n\nexport type SessionHostCategory = 'cli' | 'acp' | 'shell';\n\nexport type SessionLifecycle = 'starting' | 'running' | 'stopping' | 'stopped' | 'failed' | 'interrupted';\n\nexport type SessionClientType = 'daemon' | 'web' | 'local-terminal';\n\nexport type SessionOwnerType = 'agent' | 'user';\n\nexport interface SessionLaunchCommand {\n command: string;\n args: string[];\n env?: Record<string, string>;\n}\n\nexport interface SessionWriteOwner {\n clientId: string;\n ownerType: SessionOwnerType;\n acquiredAt: number;\n}\n\nexport interface SessionAttachedClient {\n clientId: string;\n type: SessionClientType;\n readOnly: boolean;\n attachedAt: number;\n lastSeenAt: number;\n}\n\nexport interface SessionBufferSnapshot {\n seq: number;\n text: string;\n truncated: boolean;\n cols?: number;\n rows?: number;\n}\n\nexport interface SessionTerminalState {\n cursor: { row: number; col: number };\n altScreen: boolean;\n pasteMode: boolean;\n rawMode: boolean;\n scrollRegion: { top: number; bot: number };\n cols: number;\n rows: number;\n}\n\nexport interface SessionTerminalSnapshot {\n text: string;\n state: SessionTerminalState;\n}\n\nexport interface SessionBufferState {\n scrollbackBytes: number;\n snapshotSeq: number;\n}\n\nexport type SessionHostSurfaceKind = 'live_runtime' | 'recovery_snapshot' | 'inactive_record';\n\n/**\n * Compact, secret-free record of how a session's PTY process terminated.\n *\n * Preserves the nullable/unknown exitCode and signal exactly as observed from\n * the first node-pty `onExit` — an `exitCode` of `null` means \"unknown\" (the\n * process was terminated by a signal, or the exit code was otherwise not\n * reported) and MUST NOT be collapsed to 0. Persisted as a tombstone so\n * termination diagnostics remain inspectable after the live runtime and its\n * transport adapter are torn down.\n */\nexport interface SessionTermination {\n /** Exit code exactly as observed; `null` = unknown / signal-terminated. Never fabricated to 0. */\n exitCode: number | null;\n /** Signal number that terminated the process, if any; `null` when not signalled. */\n signal: number | null;\n /** Classification derived from (exitCode, signal): clean exit vs. failure vs. unknown. */\n reason: 'exit' | 'signal' | 'failed' | 'unknown';\n /** Lifecycle recorded for this termination (`stopped` | `failed`). */\n lifecycle: Extract<SessionLifecycle, 'stopped' | 'failed'>;\n /** Wall-clock time the termination was observed. */\n terminatedAt: number;\n /** OS pid of the terminated process, if it was known. */\n osPid?: number;\n /** Lifecycle the session was in immediately before termination. */\n previousLifecycle?: SessionLifecycle;\n /** Last time output was observed from the session, if known. */\n lastOutputAt?: number;\n /** Whether the termination followed an explicit stop/delete request, and which. */\n requestedStop?: 'stop' | 'delete' | 'restart' | 'prune';\n}\n\nexport interface SessionHostRecord {\n sessionId: string;\n runtimeKey: string;\n displayName: string;\n workspaceLabel: string;\n transport: SessionTransport;\n providerType: string;\n category: SessionHostCategory;\n workspace: string;\n launchCommand: SessionLaunchCommand;\n osPid?: number;\n createdAt: number;\n startedAt?: number;\n lastActivityAt: number;\n lifecycle: SessionLifecycle;\n surfaceKind?: SessionHostSurfaceKind;\n writeOwner: SessionWriteOwner | null;\n attachedClients: SessionAttachedClient[];\n buffer: SessionBufferState;\n meta: Record<string, unknown>;\n /** Termination tombstone, stamped when the PTY process exits. Retained for post-mortem inspection. */\n termination?: SessionTermination;\n}\n\nexport interface CreateSessionPayload {\n sessionId?: string;\n runtimeKey?: string;\n displayName?: string;\n providerType: string;\n category: SessionHostCategory;\n workspace: string;\n launchCommand: SessionLaunchCommand;\n cols?: number;\n rows?: number;\n clientId?: string;\n clientType?: SessionClientType;\n meta?: Record<string, unknown>;\n}\n\nexport interface AttachSessionPayload {\n sessionId: string;\n clientId: string;\n clientType: SessionClientType;\n readOnly?: boolean;\n}\n\nexport interface DetachSessionPayload {\n sessionId: string;\n clientId: string;\n}\n\nexport interface SendInputPayload {\n sessionId: string;\n clientId: string;\n data: string;\n}\n\nexport interface ResizeSessionPayload {\n sessionId: string;\n cols: number;\n rows: number;\n}\n\nexport interface StopSessionPayload {\n sessionId: string;\n}\n\nexport interface DeleteSessionPayload {\n sessionId: string;\n /** Stop a live runtime before deleting the persisted record. */\n force?: boolean;\n}\n\nexport interface ResumeSessionPayload {\n sessionId: string;\n}\n\nexport interface AcquireWritePayload {\n sessionId: string;\n clientId: string;\n ownerType: SessionOwnerType;\n force?: boolean;\n}\n\nexport interface ReleaseWritePayload {\n sessionId: string;\n clientId: string;\n}\n\nexport interface GetSnapshotPayload {\n sessionId: string;\n sinceSeq?: number;\n}\n\nexport interface GetTerminalSnapshotPayload {\n sessionId: string;\n}\n\nexport interface ClearSessionBufferPayload {\n sessionId: string;\n}\n\nexport interface UpdateSessionMetaPayload {\n sessionId: string;\n meta: Record<string, unknown>;\n replace?: boolean;\n}\n\nexport interface GetHostDiagnosticsPayload {\n includeSessions?: boolean;\n limit?: number;\n}\n\nexport interface ForceDetachClientPayload {\n sessionId: string;\n clientId: string;\n}\n\nexport interface SendSignalPayload {\n sessionId: string;\n signal: string;\n}\n\nexport interface RestartSessionPayload {\n sessionId: string;\n}\n\nexport interface PruneDuplicateSessionsPayload {\n providerType?: string;\n workspace?: string;\n dryRun?: boolean;\n}\n\nexport interface SessionHostDuplicateSessionGroup {\n bindingKey: string;\n providerType: string;\n workspace: string;\n providerSessionId: string;\n keptSessionId: string;\n prunedSessionIds: string[];\n}\n\nexport interface SessionHostPruneDuplicatesResult {\n duplicateGroupCount: number;\n keptSessionIds: string[];\n prunedSessionIds: string[];\n groups: SessionHostDuplicateSessionGroup[];\n}\n\nexport interface SessionHostLogEntry {\n timestamp: number;\n level: 'debug' | 'info' | 'warn' | 'error';\n message: string;\n sessionId?: string;\n data?: Record<string, unknown>;\n}\n\nexport interface SessionHostRequestTrace {\n timestamp: number;\n requestId: string;\n type: SessionHostRequest['type'];\n sessionId?: string;\n clientId?: string;\n success: boolean;\n durationMs: number;\n error?: string;\n}\n\nexport interface SessionHostRuntimeTransition {\n timestamp: number;\n sessionId: string;\n action: string;\n lifecycle?: SessionLifecycle;\n detail?: string;\n success?: boolean;\n error?: string;\n}\n\nexport interface SessionHostDiagnostics {\n hostStartedAt: number;\n endpoint: string;\n runtimeCount: number;\n supportedRequestTypes?: SessionHostRequestType[];\n sessions?: SessionHostRecord[];\n liveRuntimes?: SessionHostRecord[];\n recoverySnapshots?: SessionHostRecord[];\n inactiveRecords?: SessionHostRecord[];\n recentLogs: SessionHostLogEntry[];\n recentRequests: SessionHostRequestTrace[];\n recentTransitions: SessionHostRuntimeTransition[];\n}\n\nexport type SessionHostRequest =\n | { type: 'create_session'; payload: CreateSessionPayload }\n | { type: 'attach_session'; payload: AttachSessionPayload }\n | { type: 'detach_session'; payload: DetachSessionPayload }\n | { type: 'send_input'; payload: SendInputPayload }\n | { type: 'resize_session'; payload: ResizeSessionPayload }\n | { type: 'stop_session'; payload: StopSessionPayload }\n | { type: 'delete_session'; payload: DeleteSessionPayload }\n | { type: 'resume_session'; payload: ResumeSessionPayload }\n | { type: 'acquire_write'; payload: AcquireWritePayload }\n | { type: 'release_write'; payload: ReleaseWritePayload }\n | { type: 'get_snapshot'; payload: GetSnapshotPayload }\n | { type: 'get_terminal_snapshot'; payload: GetTerminalSnapshotPayload }\n | { type: 'clear_session_buffer'; payload: ClearSessionBufferPayload }\n | { type: 'update_session_meta'; payload: UpdateSessionMetaPayload }\n | { type: 'get_host_diagnostics'; payload?: GetHostDiagnosticsPayload }\n | { type: 'force_detach_client'; payload: ForceDetachClientPayload }\n | { type: 'send_signal'; payload: SendSignalPayload }\n | { type: 'restart_session'; payload: RestartSessionPayload }\n | { type: 'prune_duplicate_sessions'; payload?: PruneDuplicateSessionsPayload }\n | { type: 'list_sessions'; payload?: {} };\n\nexport type SessionHostRequestType = SessionHostRequest['type'];\n\nexport const SESSION_HOST_SUPPORTED_REQUEST_TYPES: readonly SessionHostRequestType[] = [\n 'create_session',\n 'attach_session',\n 'detach_session',\n 'send_input',\n 'resize_session',\n 'stop_session',\n 'delete_session',\n 'resume_session',\n 'acquire_write',\n 'release_write',\n 'get_snapshot',\n 'get_terminal_snapshot',\n 'clear_session_buffer',\n 'update_session_meta',\n 'get_host_diagnostics',\n 'force_detach_client',\n 'send_signal',\n 'restart_session',\n 'prune_duplicate_sessions',\n 'list_sessions',\n];\n\nexport interface SessionHostResponse<T = unknown> {\n success: boolean;\n result?: T;\n error?: string;\n}\n\nexport type SessionHostEvent =\n | { type: 'session_created'; sessionId: string; record: SessionHostRecord }\n | { type: 'session_started'; sessionId: string; pid?: number }\n | { type: 'session_resumed'; sessionId: string; pid?: number }\n | { type: 'session_output'; sessionId: string; seq: number; data: string }\n | { type: 'session_cleared'; sessionId: string }\n | { type: 'session_exit'; sessionId: string; exitCode: number | null; signal?: number | null; termination?: SessionTermination }\n | { type: 'session_stopped'; sessionId: string }\n | { type: 'session_deleted'; sessionId: string }\n | { type: 'session_resized'; sessionId: string; cols: number; rows: number }\n | { type: 'write_owner_changed'; sessionId: string; owner: SessionWriteOwner | null }\n | { type: 'client_attached'; sessionId: string; client: SessionAttachedClient }\n | { type: 'client_detached'; sessionId: string; clientId: string }\n | { type: 'host_log'; entry: SessionHostLogEntry }\n | { type: 'request_trace'; trace: SessionHostRequestTrace }\n | { type: 'runtime_transition'; transition: SessionHostRuntimeTransition };\n\nexport interface SessionHostRequestEnvelope {\n kind: 'request';\n requestId: string;\n request: SessionHostRequest;\n}\n\nexport interface SessionHostResponseEnvelope {\n kind: 'response';\n requestId: string;\n response: SessionHostResponse;\n}\n\nexport interface SessionHostEventEnvelope {\n kind: 'event';\n event: SessionHostEvent;\n}\n\nexport type SessionHostWireEnvelope =\n | SessionHostRequestEnvelope\n | SessionHostResponseEnvelope\n | SessionHostEventEnvelope;\n","import type { SessionLifecycle, SessionTermination } from './types.js';\n\nexport interface ClassifyTerminationInput {\n exitCode: number | null;\n signal?: number | null;\n osPid?: number;\n previousLifecycle?: SessionLifecycle;\n lastOutputAt?: number;\n requestedStop?: SessionTermination['requestedStop'];\n terminatedAt: number;\n}\n\n/**\n * Classify a PTY termination from its raw (exitCode, signal) pair.\n *\n * Invariants:\n * - A `null` exitCode is UNKNOWN, never a clean exit — it MUST NOT collapse to\n * exit 0. This is the regression guard for the old `exitCode ?? 0` behavior.\n * - A non-null, non-zero exitCode or any signal is a failure.\n * - Only an explicit exit code of 0 (with no signal) is a clean stop.\n *\n * The returned lifecycle is `stopped` only for a clean exit 0; every other\n * case (nonzero, signalled, or unknown) is `failed` so that an unknown\n * termination stays distinguishable from a successful one.\n */\nexport function classifyTermination(input: ClassifyTerminationInput): SessionTermination {\n const { exitCode, terminatedAt } = input;\n const signal = input.signal ?? null;\n\n let reason: SessionTermination['reason'];\n let lifecycle: SessionTermination['lifecycle'];\n\n if (signal !== null && signal !== 0) {\n reason = 'signal';\n lifecycle = 'failed';\n } else if (exitCode === null) {\n reason = 'unknown';\n lifecycle = 'failed';\n } else if (exitCode === 0) {\n reason = 'exit';\n lifecycle = 'stopped';\n } else {\n reason = 'failed';\n lifecycle = 'failed';\n }\n\n const termination: SessionTermination = {\n exitCode,\n signal,\n reason,\n lifecycle,\n terminatedAt,\n };\n if (typeof input.osPid === 'number') termination.osPid = input.osPid;\n if (input.previousLifecycle) termination.previousLifecycle = input.previousLifecycle;\n if (typeof input.lastOutputAt === 'number') termination.lastOutputAt = input.lastOutputAt;\n if (input.requestedStop) termination.requestedStop = input.requestedStop;\n return termination;\n}\n","import type { SessionBufferSnapshot } from './types.js';\n\nexport interface SessionRingBufferOptions {\n maxBytes?: number;\n}\n\n// Manual \"Load older terminal output\" replays this raw ring buffer into the\n// browser terminal. 512KiB was too small for long Claude/Codex conversations:\n// by the time the user scrolled to the top and clicked the loader, the output\n// they were trying to recover was often already trimmed.\nexport const DEFAULT_SESSION_RING_BUFFER_MAX_BYTES = 4 * 1024 * 1024;\n\nexport class SessionRingBuffer {\n private maxBytes: number;\n private chunks: { seq: number; data: string; bytes: number }[] = [];\n private nextSeq = 1;\n private totalBytes = 0;\n\n constructor(options: SessionRingBufferOptions = {}) {\n this.maxBytes = options.maxBytes ?? DEFAULT_SESSION_RING_BUFFER_MAX_BYTES;\n }\n\n append(data: string): number {\n const normalized = typeof data === 'string' ? data : String(data ?? '');\n const bytes = Buffer.byteLength(normalized, 'utf8');\n const seq = this.nextSeq++;\n\n this.chunks.push({ seq, data: normalized, bytes });\n this.totalBytes += bytes;\n this.trim();\n return seq;\n }\n\n snapshot(sinceSeq?: number): SessionBufferSnapshot {\n const relevant = typeof sinceSeq === 'number'\n ? this.chunks.filter(chunk => chunk.seq > sinceSeq)\n : this.chunks;\n\n const text = relevant.map(chunk => chunk.data).join('');\n const truncated = !!this.chunks[0] && typeof sinceSeq === 'number' && sinceSeq < this.chunks[0].seq - 1;\n\n return {\n seq: this.nextSeq - 1,\n text,\n truncated,\n };\n }\n\n getState(): { scrollbackBytes: number; snapshotSeq: number } {\n return {\n scrollbackBytes: this.totalBytes,\n snapshotSeq: this.nextSeq - 1,\n };\n }\n\n clear(): void {\n this.chunks = [];\n this.totalBytes = 0;\n this.nextSeq = 1;\n }\n\n restore(snapshot: { seq: number; text: string }): void {\n this.clear();\n const text = String(snapshot.text || '');\n if (!text) {\n this.nextSeq = Math.max(1, Number(snapshot.seq || 0) + 1);\n return;\n }\n const bytes = Buffer.byteLength(text, 'utf8');\n const seq = Math.max(1, Number(snapshot.seq || 1));\n this.chunks = [{ seq, data: text, bytes }];\n this.totalBytes = bytes;\n this.nextSeq = seq + 1;\n this.trim();\n }\n\n private trim(): void {\n while (this.totalBytes > this.maxBytes && this.chunks.length > 1) {\n const removed = this.chunks.shift();\n if (!removed) break;\n this.totalBytes -= removed.bytes;\n }\n }\n}\n","export const DEFAULT_SESSION_HOST_COLS = 80;\nexport const DEFAULT_SESSION_HOST_ROWS = 32;\n\nfunction normalizeSessionHostDimension(value: number | undefined, fallback: number): number {\n if (typeof value !== 'number' || !Number.isFinite(value)) return fallback;\n const rounded = Math.floor(value);\n return rounded > 0 ? rounded : fallback;\n}\n\nexport function resolveSessionHostCols(value: number | undefined): number {\n return normalizeSessionHostDimension(value, DEFAULT_SESSION_HOST_COLS);\n}\n\nexport function resolveSessionHostRows(value: number | undefined): number {\n return normalizeSessionHostDimension(value, DEFAULT_SESSION_HOST_ROWS);\n}\n","import * as path from 'path';\nimport type { CreateSessionPayload, SessionHostRecord, SessionHostSurfaceKind } from './types.js';\n\nfunction normalizeSlug(input: string): string {\n return input\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 48);\n}\n\nfunction normalizeValue(input: string): string {\n return input.trim().toLowerCase();\n}\n\nexport function getWorkspaceLabel(workspace: string): string {\n const trimmed = workspace.trim();\n if (!trimmed) return 'workspace';\n const normalized = trimmed.replace(/[\\\\/]+$/, '');\n const base = path.basename(normalized);\n return base || normalized;\n}\n\nexport function buildRuntimeDisplayName(payload: Pick<CreateSessionPayload, 'displayName' | 'providerType' | 'workspace'>): string {\n const explicit = payload.displayName?.trim();\n if (explicit) return explicit;\n const workspaceLabel = getWorkspaceLabel(payload.workspace);\n const providerLabel = payload.providerType.trim() || 'runtime';\n return `${providerLabel} @ ${workspaceLabel}`;\n}\n\nexport function buildRuntimeKey(\n payload: Pick<CreateSessionPayload, 'runtimeKey' | 'displayName' | 'providerType' | 'workspace'>,\n existingKeys: Iterable<string>,\n): string {\n const requested = payload.runtimeKey?.trim();\n const existing = new Set(Array.from(existingKeys, (key) => key.toLowerCase()));\n const displayName = buildRuntimeDisplayName(payload);\n const baseKey = normalizeSlug(requested || displayName || getWorkspaceLabel(payload.workspace) || payload.providerType || 'runtime') || 'runtime';\n if (!existing.has(baseKey)) return baseKey;\n\n let suffix = 2;\n let candidate = `${baseKey}-${suffix}`;\n while (existing.has(candidate)) {\n suffix += 1;\n candidate = `${baseKey}-${suffix}`;\n }\n return candidate;\n}\n\nexport interface SessionHostSurfaceRecordLike {\n lifecycle?: string | null;\n surfaceKind?: SessionHostSurfaceKind | null;\n meta?: Record<string, unknown> | null;\n}\n\nconst LIVE_LIFECYCLES = new Set(['starting', 'running', 'stopping', 'interrupted']);\n\nexport function isSessionHostLiveRuntime(record: SessionHostSurfaceRecordLike | null | undefined): boolean {\n if (!record) return false;\n if (record.surfaceKind === 'live_runtime') return true;\n if (record.surfaceKind === 'recovery_snapshot' || record.surfaceKind === 'inactive_record') return false;\n const lifecycle = String(record.lifecycle || '').trim();\n return LIVE_LIFECYCLES.has(lifecycle);\n}\n\nexport function getSessionHostRecoveryLabel(meta: Record<string, unknown> | null | undefined): string | null {\n const recoveryState = typeof meta?.runtimeRecoveryState === 'string'\n ? String(meta.runtimeRecoveryState).trim()\n : '';\n if (!recoveryState) return null;\n if (recoveryState === 'auto_resumed') return 'restored after restart';\n if (recoveryState === 'resume_failed') return 'restore failed';\n if (recoveryState === 'host_restart_interrupted') return 'host restart interrupted';\n if (recoveryState === 'orphan_snapshot') return 'snapshot recovered';\n return recoveryState.replace(/_/g, ' ');\n}\n\nexport function isSessionHostRecoverySnapshot(record: SessionHostSurfaceRecordLike | null | undefined): boolean {\n if (!record) return false;\n if (record.surfaceKind === 'recovery_snapshot') return true;\n if (record.surfaceKind === 'live_runtime' || record.surfaceKind === 'inactive_record') return false;\n if (isSessionHostLiveRuntime(record)) return false;\n\n const lifecycle = String(record.lifecycle || '').trim();\n if (lifecycle && lifecycle !== 'stopped' && lifecycle !== 'failed') {\n return false;\n }\n\n const meta = record.meta || undefined;\n if (meta?.restoredFromStorage === true) return true;\n return getSessionHostRecoveryLabel(meta) !== null;\n}\n\nexport function getSessionHostSurfaceKind(record: SessionHostSurfaceRecordLike | null | undefined): SessionHostSurfaceKind {\n if (record?.surfaceKind === 'live_runtime' || record?.surfaceKind === 'recovery_snapshot' || record?.surfaceKind === 'inactive_record') {\n return record.surfaceKind;\n }\n if (isSessionHostLiveRuntime(record)) return 'live_runtime';\n if (isSessionHostRecoverySnapshot(record)) return 'recovery_snapshot';\n return 'inactive_record';\n}\n\nexport function partitionSessionHostRecords<T extends SessionHostSurfaceRecordLike>(records: T[]): {\n liveRuntimes: T[];\n recoverySnapshots: T[];\n inactiveRecords: T[];\n} {\n const liveRuntimes: T[] = [];\n const recoverySnapshots: T[] = [];\n const inactiveRecords: T[] = [];\n\n for (const record of records) {\n const kind = getSessionHostSurfaceKind(record);\n if (kind === 'live_runtime') {\n liveRuntimes.push(record);\n } else if (kind === 'recovery_snapshot') {\n recoverySnapshots.push(record);\n } else {\n inactiveRecords.push(record);\n }\n }\n\n return {\n liveRuntimes,\n recoverySnapshots,\n inactiveRecords,\n };\n}\n\nexport function partitionSessionHostDiagnosticsSessions(\n records: SessionHostRecord[] | null | undefined,\n): ReturnType<typeof partitionSessionHostRecords<SessionHostRecord>> {\n return partitionSessionHostRecords(records || []);\n}\n\nexport function resolveAttachableRuntimeRecord(records: SessionHostRecord[], identifier: string): SessionHostRecord {\n const record = resolveRuntimeRecord(records, identifier);\n const surfaceKind = getSessionHostSurfaceKind(record);\n if (surfaceKind === 'live_runtime') {\n return record;\n }\n if (surfaceKind === 'recovery_snapshot') {\n throw new Error(`Runtime ${record.runtimeKey} is a recovery snapshot, not a live attach target. Resume or recover it first.`);\n }\n throw new Error(`Runtime ${record.runtimeKey} is ${record.lifecycle}, not a live attach target.`);\n}\n\nfunction uniqueMatch(records: SessionHostRecord[], predicate: (record: SessionHostRecord) => boolean): SessionHostRecord | null {\n const matches = records.filter(predicate);\n if (matches.length === 1) return matches[0] || null;\n if (matches.length === 0) return null;\n const labels = matches.map((record) => `${record.runtimeKey} (${record.sessionId})`).join(', ');\n throw new Error(`Ambiguous runtime target. Matches: ${labels}`);\n}\n\nexport function resolveRuntimeRecord(records: SessionHostRecord[], identifier: string): SessionHostRecord {\n const target = identifier.trim();\n if (!target) {\n throw new Error('Runtime target is required');\n }\n\n const exact = uniqueMatch(records, (record) =>\n record.sessionId === target ||\n normalizeValue(record.runtimeKey) === normalizeValue(target) ||\n normalizeValue(record.displayName) === normalizeValue(target),\n );\n if (exact) return exact;\n\n const prefix = uniqueMatch(records, (record) =>\n record.sessionId.startsWith(target) ||\n normalizeValue(record.runtimeKey).startsWith(normalizeValue(target)),\n );\n if (prefix) return prefix;\n\n throw new Error(`Unknown runtime target: ${target}`);\n}\n\nexport function formatRuntimeOwner(record: Pick<SessionHostRecord, 'writeOwner'>): string {\n if (!record.writeOwner) return 'none';\n return `${record.writeOwner.ownerType}:${record.writeOwner.clientId}`;\n}\n","function generateUUID(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {\n const r = Math.random() * 16 | 0;\n const v = c === 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n}\nimport type {\n AcquireWritePayload,\n AttachSessionPayload,\n CreateSessionPayload,\n DetachSessionPayload,\n ReleaseWritePayload,\n SessionAttachedClient,\n SessionHostRecord,\n SessionTermination,\n} from './types.js';\nimport { SessionRingBuffer } from './buffer.js';\nimport { resolveSessionHostCols, resolveSessionHostRows } from './defaults.js';\nimport { buildRuntimeDisplayName, buildRuntimeKey, getWorkspaceLabel } from './runtime-labels.js';\n\ninterface SessionRuntimeState {\n record: SessionHostRecord;\n buffer: SessionRingBuffer;\n}\n\nexport class SessionHostRegistry {\n private sessions = new Map<string, SessionRuntimeState>();\n\n createSession(payload: CreateSessionPayload): SessionHostRecord {\n const sessionId = payload.sessionId || generateUUID();\n if (this.sessions.has(sessionId)) {\n throw new Error(`Session already exists: ${sessionId}`);\n }\n const now = Date.now();\n const initialClient = payload.clientId\n ? [{\n clientId: payload.clientId,\n type: payload.clientType || 'daemon',\n readOnly: false,\n attachedAt: now,\n lastSeenAt: now,\n } satisfies SessionAttachedClient]\n : [];\n\n const record: SessionHostRecord = {\n sessionId,\n runtimeKey: buildRuntimeKey(\n payload,\n Array.from(this.sessions.values(), (state) => state.record.runtimeKey),\n ),\n displayName: buildRuntimeDisplayName(payload),\n workspaceLabel: getWorkspaceLabel(payload.workspace),\n transport: 'pty',\n providerType: payload.providerType,\n category: payload.category,\n workspace: payload.workspace,\n launchCommand: payload.launchCommand,\n createdAt: now,\n lastActivityAt: now,\n lifecycle: 'starting',\n writeOwner: null,\n attachedClients: initialClient,\n buffer: {\n scrollbackBytes: 0,\n snapshotSeq: 0,\n },\n meta: payload.meta || {},\n };\n\n record.meta = {\n sessionHostCols: resolveSessionHostCols(payload.cols),\n sessionHostRows: resolveSessionHostRows(payload.rows),\n ...record.meta,\n };\n\n this.sessions.set(sessionId, {\n record,\n buffer: new SessionRingBuffer(),\n });\n\n return this.cloneRecord(record);\n }\n\n restoreSession(record: SessionHostRecord, snapshot?: { seq: number; text: string } | null): SessionHostRecord {\n const cloned = this.cloneRecord(record);\n this.sessions.set(cloned.sessionId, {\n record: cloned,\n buffer: (() => {\n const buffer = new SessionRingBuffer();\n if (snapshot) buffer.restore(snapshot);\n return buffer;\n })(),\n });\n return this.cloneRecord(cloned);\n }\n\n listSessions(): SessionHostRecord[] {\n return Array.from(this.sessions.values())\n .map(state => this.cloneRecord(state.record))\n .sort((a, b) => b.lastActivityAt - a.lastActivityAt);\n }\n\n getSession(sessionId: string): SessionHostRecord | null {\n const state = this.sessions.get(sessionId);\n return state ? this.cloneRecord(state.record) : null;\n }\n\n attachClient(payload: AttachSessionPayload): SessionHostRecord {\n const state = this.requireSession(payload.sessionId);\n const now = Date.now();\n let removedDaemonOwner = false;\n\n if (payload.clientType === 'daemon') {\n const staleDaemonClientIds = state.record.attachedClients\n .filter(client => client.type === 'daemon' && client.clientId !== payload.clientId)\n .map(client => client.clientId);\n if (staleDaemonClientIds.length > 0) {\n state.record.attachedClients = state.record.attachedClients.filter(\n client => !(client.type === 'daemon' && client.clientId !== payload.clientId),\n );\n if (state.record.writeOwner && staleDaemonClientIds.includes(state.record.writeOwner.clientId)) {\n removedDaemonOwner = true;\n }\n }\n }\n\n const existing = state.record.attachedClients.find(client => client.clientId === payload.clientId);\n\n if (existing) {\n existing.type = payload.clientType;\n existing.readOnly = !!payload.readOnly;\n existing.lastSeenAt = now;\n } else {\n state.record.attachedClients.push({\n clientId: payload.clientId,\n type: payload.clientType,\n readOnly: !!payload.readOnly,\n attachedAt: now,\n lastSeenAt: now,\n });\n }\n\n if (removedDaemonOwner) {\n state.record.writeOwner = null;\n }\n\n state.record.lastActivityAt = now;\n return this.cloneRecord(state.record);\n }\n\n detachClient(payload: DetachSessionPayload): SessionHostRecord {\n const state = this.requireSession(payload.sessionId);\n state.record.attachedClients = state.record.attachedClients.filter(client => client.clientId !== payload.clientId);\n if (state.record.writeOwner?.clientId === payload.clientId) {\n state.record.writeOwner = null;\n }\n state.record.lastActivityAt = Date.now();\n return this.cloneRecord(state.record);\n }\n\n acquireWrite(payload: AcquireWritePayload): SessionHostRecord {\n const state = this.requireSession(payload.sessionId);\n if (state.record.writeOwner && state.record.writeOwner.clientId !== payload.clientId && !payload.force) {\n throw new Error(`Write owned by ${state.record.writeOwner.clientId}`);\n }\n const attachedClient = state.record.attachedClients.find(client => client.clientId === payload.clientId);\n if (attachedClient) {\n attachedClient.readOnly = false;\n attachedClient.lastSeenAt = Date.now();\n }\n state.record.writeOwner = {\n clientId: payload.clientId,\n ownerType: payload.ownerType,\n acquiredAt: Date.now(),\n };\n state.record.lastActivityAt = Date.now();\n return this.cloneRecord(state.record);\n }\n\n releaseWrite(payload: ReleaseWritePayload): SessionHostRecord {\n const state = this.requireSession(payload.sessionId);\n const attachedClient = state.record.attachedClients.find(client => client.clientId === payload.clientId);\n if (attachedClient) {\n attachedClient.readOnly = false;\n attachedClient.lastSeenAt = Date.now();\n }\n if (state.record.writeOwner?.clientId === payload.clientId) {\n state.record.writeOwner = null;\n }\n state.record.lastActivityAt = Date.now();\n return this.cloneRecord(state.record);\n }\n\n appendOutput(sessionId: string, data: string): { record: SessionHostRecord; seq: number } {\n const state = this.requireSession(sessionId);\n const seq = state.buffer.append(data);\n state.record.buffer = state.buffer.getState();\n state.record.lastActivityAt = Date.now();\n return { record: this.cloneRecord(state.record), seq };\n }\n\n getSnapshot(sessionId: string, sinceSeq?: number) {\n const state = this.requireSession(sessionId);\n state.record.buffer = state.buffer.getState();\n return state.buffer.snapshot(sinceSeq);\n }\n\n clearBuffer(sessionId: string): SessionHostRecord {\n const state = this.requireSession(sessionId);\n state.buffer.clear();\n state.record.buffer = state.buffer.getState();\n state.record.lastActivityAt = Date.now();\n return this.cloneRecord(state.record);\n }\n\n updateSessionMeta(sessionId: string, meta: Record<string, unknown>, replace = false): SessionHostRecord {\n const state = this.requireSession(sessionId);\n state.record.meta = replace\n ? { ...meta }\n : {\n ...(state.record.meta || {}),\n ...meta,\n };\n state.record.lastActivityAt = Date.now();\n return this.cloneRecord(state.record);\n }\n\n markStarted(sessionId: string, pid?: number): SessionHostRecord {\n const state = this.requireSession(sessionId);\n state.record.lifecycle = 'running';\n state.record.startedAt = state.record.startedAt || Date.now();\n if (typeof pid === 'number') state.record.osPid = pid;\n state.record.lastActivityAt = Date.now();\n return this.cloneRecord(state.record);\n }\n\n markStopped(sessionId: string, lifecycle: 'stopped' | 'failed' = 'stopped', termination?: SessionTermination): SessionHostRecord {\n const state = this.requireSession(sessionId);\n state.record.lifecycle = lifecycle;\n state.record.lastActivityAt = Date.now();\n if (termination) state.record.termination = termination;\n return this.cloneRecord(state.record);\n }\n\n setLifecycle(sessionId: string, lifecycle: 'starting' | 'running' | 'stopping' | 'stopped' | 'failed' | 'interrupted'): SessionHostRecord {\n const state = this.requireSession(sessionId);\n state.record.lifecycle = lifecycle;\n state.record.lastActivityAt = Date.now();\n return this.cloneRecord(state.record);\n }\n\n deleteSession(sessionId: string): boolean {\n return this.sessions.delete(sessionId);\n }\n\n private requireSession(sessionId: string): SessionRuntimeState {\n const state = this.sessions.get(sessionId);\n if (!state) throw new Error(`Unknown session: ${sessionId}`);\n return state;\n }\n\n private cloneRecord(record: SessionHostRecord): SessionHostRecord {\n return {\n ...record,\n launchCommand: {\n ...record.launchCommand,\n args: [...record.launchCommand.args],\n env: record.launchCommand.env ? { ...record.launchCommand.env } : undefined,\n },\n writeOwner: record.writeOwner ? { ...record.writeOwner } : null,\n attachedClients: record.attachedClients.map(client => ({ ...client })),\n buffer: { ...record.buffer },\n meta: { ...record.meta },\n };\n }\n}\n","import * as os from 'os';\nimport * as path from 'path';\nimport * as net from 'net';\nimport type {\n SessionHostEvent,\n SessionHostRequest,\n SessionHostRequestEnvelope,\n SessionHostResponse,\n SessionHostResponseEnvelope,\n SessionHostWireEnvelope,\n} from './types.js';\n\nfunction generateUUID(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {\n const r = Math.random() * 16 | 0;\n const v = c === 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n}\n\nexport interface SessionHostEndpoint {\n kind: 'unix' | 'pipe';\n path: string;\n}\n\nexport function getDefaultSessionHostEndpoint(appName = 'adhdev'): SessionHostEndpoint {\n if (process.platform === 'win32') {\n return {\n kind: 'pipe',\n path: `\\\\\\\\.\\\\pipe\\\\${appName}-session-host`,\n };\n }\n\n return {\n kind: 'unix',\n path: path.join(os.tmpdir(), `${appName}-session-host.sock`),\n };\n}\n\nfunction serializeEnvelope(envelope: SessionHostWireEnvelope): string {\n return `${JSON.stringify(envelope)}\\n`;\n}\n\nfunction createLineParser(onEnvelope: (envelope: SessionHostWireEnvelope) => void) {\n let buffer = '';\n return (chunk: Buffer | string) => {\n buffer += chunk.toString();\n let newlineIndex = buffer.indexOf('\\n');\n while (newlineIndex >= 0) {\n const rawLine = buffer.slice(0, newlineIndex).trim();\n buffer = buffer.slice(newlineIndex + 1);\n if (rawLine) {\n onEnvelope(JSON.parse(rawLine) as SessionHostWireEnvelope);\n }\n newlineIndex = buffer.indexOf('\\n');\n }\n };\n}\n\nexport interface SessionHostClientOptions {\n endpoint?: SessionHostEndpoint;\n appName?: string;\n}\n\nexport class SessionHostClient {\n readonly endpoint: SessionHostEndpoint;\n\n private socket: net.Socket | null = null;\n private requestWaiters = new Map<string, { resolve: (value: SessionHostResponse) => void; reject: (error: Error) => void }>();\n private eventListeners = new Set<(event: SessionHostEvent) => void>();\n\n constructor(options: SessionHostClientOptions = {}) {\n this.endpoint = options.endpoint || getDefaultSessionHostEndpoint(options.appName || 'adhdev');\n }\n\n async connect(): Promise<void> {\n if (this.socket && !this.socket.destroyed) return;\n // Cleanup stale socket reference left after error/disconnect\n if (this.socket) {\n try { this.socket.destroy(); } catch { /* noop */ }\n this.socket = null;\n }\n\n const socket = net.createConnection(this.endpoint.path);\n this.socket = socket;\n\n socket.on('data', createLineParser((envelope) => {\n if (envelope.kind === 'response') {\n const waiter = this.requestWaiters.get(envelope.requestId);\n if (waiter) {\n this.requestWaiters.delete(envelope.requestId);\n waiter.resolve(envelope.response);\n }\n return;\n }\n\n if (envelope.kind === 'event') {\n for (const listener of this.eventListeners) listener(envelope.event);\n }\n }));\n\n socket.on('error', (error) => {\n for (const waiter of this.requestWaiters.values()) {\n waiter.reject(error);\n }\n this.requestWaiters.clear();\n // Clear the dead socket reference so the next connect() creates a fresh connection\n // instead of skipping reconnection on the `!this.socket.destroyed` guard.\n if (this.socket === socket) {\n this.socket = null;\n }\n try { socket.destroy(); } catch { /* noop */ }\n });\n\n await new Promise<void>((resolve, reject) => {\n socket.once('connect', () => resolve());\n socket.once('error', reject);\n });\n }\n\n onEvent(listener: (event: SessionHostEvent) => void): () => void {\n this.eventListeners.add(listener);\n return () => {\n this.eventListeners.delete(listener);\n };\n }\n\n async request<T = unknown>(request: SessionHostRequest): Promise<SessionHostResponse<T>> {\n await this.connect();\n if (!this.socket) throw new Error('Session host socket unavailable');\n\n const requestId = generateUUID();\n const envelope: SessionHostRequestEnvelope = {\n kind: 'request',\n requestId,\n request,\n };\n\n const response = await new Promise<SessionHostResponse>((resolve, reject) => {\n const timeout = setTimeout(() => {\n this.requestWaiters.delete(requestId);\n reject(new Error(`Session host request timed out after 30s (${request.type})`));\n }, 30_000);\n this.requestWaiters.set(requestId, {\n resolve: (value) => { clearTimeout(timeout); resolve(value); },\n reject: (error) => { clearTimeout(timeout); reject(error); },\n });\n this.socket?.write(serializeEnvelope(envelope));\n });\n\n return response as SessionHostResponse<T>;\n }\n\n async close(): Promise<void> {\n if (!this.socket) return;\n const socket = this.socket;\n this.socket = null;\n for (const waiter of this.requestWaiters.values()) {\n waiter.reject(new Error('Session host client closed'));\n }\n this.requestWaiters.clear();\n await new Promise<void>((resolve) => {\n let settled = false;\n const done = () => {\n if (settled) return;\n settled = true;\n resolve();\n };\n socket.once('close', done);\n socket.end();\n socket.destroy();\n setTimeout(done, 50);\n });\n }\n}\n\nexport function createResponseEnvelope(requestId: string, response: SessionHostResponse): SessionHostResponseEnvelope {\n return {\n kind: 'response',\n requestId,\n response,\n };\n}\n\nexport function writeEnvelope(socket: Pick<net.Socket, 'write'>, envelope: SessionHostWireEnvelope): void {\n socket.write(serializeEnvelope(envelope));\n}\n\nexport { createLineParser };\n","/**\n * Shared PTY spawn environment utilities.\n *\n * Centralises npm/pnpm/yarn env variable stripping, terminal colour env\n * injection, and node-pty spawn-helper permission fixing.\n *\n * Used by daemon-core (provider-cli-adapter), session-host-daemon (runtime),\n * and daemon-cloud (session-host).\n */\n\nimport * as os from 'os';\nimport * as path from 'path';\n\n/**\n * Strip package-manager injected environment variables that can interfere\n * with child CLI processes and apply terminal colour defaults.\n */\nexport function sanitizeSpawnEnv(\n baseEnv: NodeJS.ProcessEnv,\n overrides?: Record<string, string>,\n): Record<string, string> {\n const env: Record<string, string> = {};\n const source = { ...baseEnv, ...(overrides || {}) } as NodeJS.ProcessEnv;\n\n for (const [key, value] of Object.entries(source)) {\n if (typeof value !== 'string') continue;\n env[key] = value;\n }\n\n for (const key of Object.keys(env)) {\n if (\n key === 'INIT_CWD'\n || key === 'npm_command'\n || key === 'npm_execpath'\n || key === 'npm_node_execpath'\n || key.startsWith('npm_')\n || key.startsWith('npm_config_')\n || key.startsWith('npm_package_')\n || key.startsWith('npm_lifecycle_')\n || key.startsWith('PNPM_')\n || key.startsWith('YARN_')\n || key.startsWith('BUN_')\n || key.startsWith('VSCODE_')\n || key.startsWith('ELECTRON_')\n ) {\n delete env[key];\n }\n }\n\n // Do not leak parent Codex session controls into child CLIs. Thread identity\n // breaks providerSessionId discovery/reconnect semantics, while the parent's\n // network-disabled sandbox flag prevents coordinator MCP children from\n // connecting back to the local ADHDev daemon.\n delete env.CODEX_THREAD_ID;\n delete env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE;\n delete env.CODEX_SANDBOX_NETWORK_DISABLED;\n delete env.NO_COLOR;\n delete env.COLOR;\n\n // Do not leak a parent Claude Code session identity into spawned claude-cli\n // children. CLAUDE_CODE_CHILD_SESSION makes a spawned claude run as a nested\n // child that does NOT persist its ~/.claude/projects transcript, so the\n // native-source history reader finds no file and the live dashboard renders\n // empty. This bites when the daemon itself was launched from inside a Claude\n // Code session (the markers are inherited and forwarded to every child).\n // Scoped to win32 for now: the macOS/Linux path works today and we want to\n // keep the blast radius small. Safe to make unconditional later — a\n // daemon-spawned claude should always be a fresh top-level session.\n if (process.platform === 'win32') {\n delete env.CLAUDECODE;\n delete env.CLAUDE_CODE_CHILD_SESSION;\n delete env.CLAUDE_CODE_ENTRYPOINT;\n delete env.CLAUDE_CODE_SESSION_ID;\n delete env.CLAUDE_CODE_EXECPATH;\n }\n\n applyTerminalColorEnv(env);\n return env;\n}\n\n/**\n * Apply preferred terminal colour environment variables.\n * Ensures TERM is set to xterm-256color and enables colour on Windows.\n */\nexport function applyTerminalColorEnv(env: Record<string, string>): void {\n if (env.NO_COLOR) return;\n\n if (!env.TERM || env.TERM === 'xterm-color') {\n env.TERM = 'xterm-256color';\n }\n if (!env.COLORTERM) env.COLORTERM = 'truecolor';\n\n if (process.platform === 'win32') {\n if (!env.FORCE_COLOR) env.FORCE_COLOR = '1';\n if (!env.CLICOLOR) env.CLICOLOR = '1';\n }\n}\n\n/**\n * Ensure node-pty's spawn-helper binary has execute permissions.\n *\n * npm's default umask can strip +x from the prebuilt spawn-helper on macOS/Linux,\n * causing EACCES when node-pty tries to fork. Best-effort fix.\n *\n * @param logFn Optional log callback for reporting the fix.\n */\nexport function ensureNodePtySpawnHelperPermissions(\n logFn?: (msg: string) => void,\n): void {\n if (os.platform() === 'win32') return;\n try {\n const fs = require('fs');\n const ptyDir = path.resolve(path.dirname(require.resolve('node-pty')), '..');\n const platformArch = `${os.platform()}-${os.arch()}`;\n const helper = path.join(ptyDir, 'prebuilds', platformArch, 'spawn-helper');\n if (fs.existsSync(helper)) {\n const stat = fs.statSync(helper);\n if (!(stat.mode & 0o111)) {\n fs.chmodSync(helper, stat.mode | 0o755);\n logFn?.(`Fixed spawn-helper permissions: ${helper}`);\n }\n }\n } catch {\n // best-effort: node-pty still works on most installs without this\n }\n}\n","import type {\n AcquireWritePayload,\n GetHostDiagnosticsPayload,\n PruneDuplicateSessionsPayload,\n ReleaseWritePayload,\n SessionHostDiagnostics,\n SessionHostPruneDuplicatesResult,\n SessionHostRecord,\n SessionHostRequestType,\n} from './types.js';\n\n/**\n * The 11-method session-host control-plane surface shared by the cloud and\n * standalone daemons. Both daemons used to carry a byte-for-byte copy of this\n * dispatch table plus the identical throw strings; this is the single source of\n * truth for that mapping. The wire `type` strings and error text here are the\n * daemon IPC contract and MUST match what the session-host daemon accepts.\n */\nexport interface SessionHostControlPlane {\n getDiagnostics(payload?: GetHostDiagnosticsPayload): Promise<SessionHostDiagnostics>;\n listSessions(): Promise<SessionHostRecord[]>;\n stopSession(sessionId: string): Promise<SessionHostRecord | null>;\n deleteSession(sessionId: string, opts?: { force?: boolean }): Promise<SessionHostRecord | null>;\n resumeSession(sessionId: string): Promise<SessionHostRecord | null>;\n restartSession(sessionId: string): Promise<SessionHostRecord | null>;\n sendSignal(sessionId: string, signal: string): Promise<SessionHostRecord | null>;\n forceDetachClient(sessionId: string, clientId: string): Promise<SessionHostRecord | null>;\n pruneDuplicateSessions(payload?: PruneDuplicateSessionsPayload): Promise<SessionHostPruneDuplicatesResult>;\n acquireWrite(payload: AcquireWritePayload): Promise<SessionHostRecord | null>;\n releaseWrite(payload: ReleaseWritePayload): Promise<SessionHostRecord | null>;\n}\n\n/**\n * Minimal request transport the control-plane factory needs. Each daemon injects\n * its own implementation:\n * - cloud reuses a single persistent SessionHostClient (with reconnect); its\n * `request` calls `ensureConnected()` then the shared client.\n * - standalone opens a fresh client per request and closes it in `finally`.\n *\n * The factory below owns ONLY the type/payload dispatch table; the connection\n * lifecycle and success/error unwrapping stay in each daemon's transport so the\n * observable behavior of both paths is preserved exactly.\n */\nexport interface SessionHostControlTransport {\n request<T>(type: SessionHostRequestType, payload: Record<string, unknown>): Promise<T>;\n}\n\n/**\n * Build the shared 11-method control-plane over an injected request transport.\n * The (type string, payload shape) pairs are verbatim what both daemons emitted\n * previously — do not reword them without matching the session-host daemon's\n * request handlers.\n */\nexport function createSessionHostControlPlane(\n transport: SessionHostControlTransport,\n): SessionHostControlPlane {\n return {\n getDiagnostics(payload: GetHostDiagnosticsPayload = {}): Promise<SessionHostDiagnostics> {\n return transport.request<SessionHostDiagnostics>('get_host_diagnostics', payload as Record<string, unknown>);\n },\n listSessions(): Promise<SessionHostRecord[]> {\n return transport.request<SessionHostRecord[]>('list_sessions', {});\n },\n stopSession(sessionId: string): Promise<SessionHostRecord | null> {\n return transport.request<SessionHostRecord | null>('stop_session', { sessionId });\n },\n deleteSession(sessionId: string, opts: { force?: boolean } = {}): Promise<SessionHostRecord | null> {\n return transport.request<SessionHostRecord | null>('delete_session', { sessionId, force: opts.force === true });\n },\n resumeSession(sessionId: string): Promise<SessionHostRecord | null> {\n return transport.request<SessionHostRecord | null>('resume_session', { sessionId });\n },\n restartSession(sessionId: string): Promise<SessionHostRecord | null> {\n return transport.request<SessionHostRecord | null>('restart_session', { sessionId });\n },\n sendSignal(sessionId: string, signal: string): Promise<SessionHostRecord | null> {\n return transport.request<SessionHostRecord | null>('send_signal', { sessionId, signal });\n },\n forceDetachClient(sessionId: string, clientId: string): Promise<SessionHostRecord | null> {\n return transport.request<SessionHostRecord | null>('force_detach_client', { sessionId, clientId });\n },\n pruneDuplicateSessions(payload: PruneDuplicateSessionsPayload = {}): Promise<SessionHostPruneDuplicatesResult> {\n return transport.request<SessionHostPruneDuplicatesResult>('prune_duplicate_sessions', payload as Record<string, unknown>);\n },\n acquireWrite(payload: AcquireWritePayload): Promise<SessionHostRecord | null> {\n return transport.request<SessionHostRecord | null>('acquire_write', payload as unknown as Record<string, unknown>);\n },\n releaseWrite(payload: ReleaseWritePayload): Promise<SessionHostRecord | null> {\n return transport.request<SessionHostRecord | null>('release_write', payload as unknown as Record<string, unknown>);\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACmTO,IAAM,uCAA0E;AAAA,EACrF;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;;;AC/SO,SAAS,oBAAoB,OAAqD;AACvF,QAAM,EAAE,UAAU,aAAa,IAAI;AACnC,QAAM,SAAS,MAAM,UAAU;AAE/B,MAAI;AACJ,MAAI;AAEJ,MAAI,WAAW,QAAQ,WAAW,GAAG;AACnC,aAAS;AACT,gBAAY;AAAA,EACd,WAAW,aAAa,MAAM;AAC5B,aAAS;AACT,gBAAY;AAAA,EACd,WAAW,aAAa,GAAG;AACzB,aAAS;AACT,gBAAY;AAAA,EACd,OAAO;AACL,aAAS;AACT,gBAAY;AAAA,EACd;AAEA,QAAM,cAAkC;AAAA,IACtC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,MAAI,OAAO,MAAM,UAAU,SAAU,aAAY,QAAQ,MAAM;AAC/D,MAAI,MAAM,kBAAmB,aAAY,oBAAoB,MAAM;AACnE,MAAI,OAAO,MAAM,iBAAiB,SAAU,aAAY,eAAe,MAAM;AAC7E,MAAI,MAAM,cAAe,aAAY,gBAAgB,MAAM;AAC3D,SAAO;AACT;;;AChDO,IAAM,wCAAwC,IAAI,OAAO;AAEzD,IAAM,oBAAN,MAAwB;AAAA,EACrB;AAAA,EACA,SAAyD,CAAC;AAAA,EAC1D,UAAU;AAAA,EACV,aAAa;AAAA,EAErB,YAAY,UAAoC,CAAC,GAAG;AAClD,SAAK,WAAW,QAAQ,YAAY;AAAA,EACtC;AAAA,EAEA,OAAO,MAAsB;AAC3B,UAAM,aAAa,OAAO,SAAS,WAAW,OAAO,OAAO,QAAQ,EAAE;AACtE,UAAM,QAAQ,OAAO,WAAW,YAAY,MAAM;AAClD,UAAM,MAAM,KAAK;AAEjB,SAAK,OAAO,KAAK,EAAE,KAAK,MAAM,YAAY,MAAM,CAAC;AACjD,SAAK,cAAc;AACnB,SAAK,KAAK;AACV,WAAO;AAAA,EACT;AAAA,EAEA,SAAS,UAA0C;AACjD,UAAM,WAAW,OAAO,aAAa,WACjC,KAAK,OAAO,OAAO,WAAS,MAAM,MAAM,QAAQ,IAChD,KAAK;AAET,UAAM,OAAO,SAAS,IAAI,WAAS,MAAM,IAAI,EAAE,KAAK,EAAE;AACtD,UAAM,YAAY,CAAC,CAAC,KAAK,OAAO,CAAC,KAAK,OAAO,aAAa,YAAY,WAAW,KAAK,OAAO,CAAC,EAAE,MAAM;AAEtG,WAAO;AAAA,MACL,KAAK,KAAK,UAAU;AAAA,MACpB;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,WAA6D;AAC3D,WAAO;AAAA,MACL,iBAAiB,KAAK;AAAA,MACtB,aAAa,KAAK,UAAU;AAAA,IAC9B;AAAA,EACF;AAAA,EAEA,QAAc;AACZ,SAAK,SAAS,CAAC;AACf,SAAK,aAAa;AAClB,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,QAAQ,UAA+C;AACrD,SAAK,MAAM;AACX,UAAM,OAAO,OAAO,SAAS,QAAQ,EAAE;AACvC,QAAI,CAAC,MAAM;AACT,WAAK,UAAU,KAAK,IAAI,GAAG,OAAO,SAAS,OAAO,CAAC,IAAI,CAAC;AACxD;AAAA,IACF;AACA,UAAM,QAAQ,OAAO,WAAW,MAAM,MAAM;AAC5C,UAAM,MAAM,KAAK,IAAI,GAAG,OAAO,SAAS,OAAO,CAAC,CAAC;AACjD,SAAK,SAAS,CAAC,EAAE,KAAK,MAAM,MAAM,MAAM,CAAC;AACzC,SAAK,aAAa;AAClB,SAAK,UAAU,MAAM;AACrB,SAAK,KAAK;AAAA,EACZ;AAAA,EAEQ,OAAa;AACnB,WAAO,KAAK,aAAa,KAAK,YAAY,KAAK,OAAO,SAAS,GAAG;AAChE,YAAM,UAAU,KAAK,OAAO,MAAM;AAClC,UAAI,CAAC,QAAS;AACd,WAAK,cAAc,QAAQ;AAAA,IAC7B;AAAA,EACF;AACF;;;ACnFO,IAAM,4BAA4B;AAClC,IAAM,4BAA4B;AAEzC,SAAS,8BAA8B,OAA2B,UAA0B;AAC1F,MAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO;AACjE,QAAM,UAAU,KAAK,MAAM,KAAK;AAChC,SAAO,UAAU,IAAI,UAAU;AACjC;AAEO,SAAS,uBAAuB,OAAmC;AACxE,SAAO,8BAA8B,OAAO,yBAAyB;AACvE;AAEO,SAAS,uBAAuB,OAAmC;AACxE,SAAO,8BAA8B,OAAO,yBAAyB;AACvE;;;ACfA,WAAsB;AAGtB,SAAS,cAAc,OAAuB;AAC5C,SAAO,MACJ,KAAK,EACL,YAAY,EACZ,QAAQ,eAAe,GAAG,EAC1B,QAAQ,YAAY,EAAE,EACtB,MAAM,GAAG,EAAE;AAChB;AAEA,SAAS,eAAe,OAAuB;AAC7C,SAAO,MAAM,KAAK,EAAE,YAAY;AAClC;AAEO,SAAS,kBAAkB,WAA2B;AAC3D,QAAM,UAAU,UAAU,KAAK;AAC/B,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,aAAa,QAAQ,QAAQ,WAAW,EAAE;AAChD,QAAM,OAAY,cAAS,UAAU;AACrC,SAAO,QAAQ;AACjB;AAEO,SAAS,wBAAwB,SAA2F;AACjI,QAAM,WAAW,QAAQ,aAAa,KAAK;AAC3C,MAAI,SAAU,QAAO;AACrB,QAAM,iBAAiB,kBAAkB,QAAQ,SAAS;AAC1D,QAAM,gBAAgB,QAAQ,aAAa,KAAK,KAAK;AACrD,SAAO,GAAG,aAAa,MAAM,cAAc;AAC7C;AAEO,SAAS,gBACd,SACA,cACQ;AACR,QAAM,YAAY,QAAQ,YAAY,KAAK;AAC3C,QAAM,WAAW,IAAI,IAAI,MAAM,KAAK,cAAc,CAAC,QAAQ,IAAI,YAAY,CAAC,CAAC;AAC7E,QAAM,cAAc,wBAAwB,OAAO;AACnD,QAAM,UAAU,cAAc,aAAa,eAAe,kBAAkB,QAAQ,SAAS,KAAK,QAAQ,gBAAgB,SAAS,KAAK;AACxI,MAAI,CAAC,SAAS,IAAI,OAAO,EAAG,QAAO;AAEnC,MAAI,SAAS;AACb,MAAI,YAAY,GAAG,OAAO,IAAI,MAAM;AACpC,SAAO,SAAS,IAAI,SAAS,GAAG;AAC9B,cAAU;AACV,gBAAY,GAAG,OAAO,IAAI,MAAM;AAAA,EAClC;AACA,SAAO;AACT;AAQA,IAAM,kBAAkB,oBAAI,IAAI,CAAC,YAAY,WAAW,YAAY,aAAa,CAAC;AAE3E,SAAS,yBAAyB,QAAkE;AACzG,MAAI,CAAC,OAAQ,QAAO;AACpB,MAAI,OAAO,gBAAgB,eAAgB,QAAO;AAClD,MAAI,OAAO,gBAAgB,uBAAuB,OAAO,gBAAgB,kBAAmB,QAAO;AACnG,QAAM,YAAY,OAAO,OAAO,aAAa,EAAE,EAAE,KAAK;AACtD,SAAO,gBAAgB,IAAI,SAAS;AACtC;AAEO,SAAS,4BAA4B,MAAiE;AAC3G,QAAM,gBAAgB,OAAO,MAAM,yBAAyB,WACxD,OAAO,KAAK,oBAAoB,EAAE,KAAK,IACvC;AACJ,MAAI,CAAC,cAAe,QAAO;AAC3B,MAAI,kBAAkB,eAAgB,QAAO;AAC7C,MAAI,kBAAkB,gBAAiB,QAAO;AAC9C,MAAI,kBAAkB,2BAA4B,QAAO;AACzD,MAAI,kBAAkB,kBAAmB,QAAO;AAChD,SAAO,cAAc,QAAQ,MAAM,GAAG;AACxC;AAEO,SAAS,8BAA8B,QAAkE;AAC9G,MAAI,CAAC,OAAQ,QAAO;AACpB,MAAI,OAAO,gBAAgB,oBAAqB,QAAO;AACvD,MAAI,OAAO,gBAAgB,kBAAkB,OAAO,gBAAgB,kBAAmB,QAAO;AAC9F,MAAI,yBAAyB,MAAM,EAAG,QAAO;AAE7C,QAAM,YAAY,OAAO,OAAO,aAAa,EAAE,EAAE,KAAK;AACtD,MAAI,aAAa,cAAc,aAAa,cAAc,UAAU;AAClE,WAAO;AAAA,EACT;AAEA,QAAM,OAAO,OAAO,QAAQ;AAC5B,MAAI,MAAM,wBAAwB,KAAM,QAAO;AAC/C,SAAO,4BAA4B,IAAI,MAAM;AAC/C;AAEO,SAAS,0BAA0B,QAAiF;AACzH,MAAI,QAAQ,gBAAgB,kBAAkB,QAAQ,gBAAgB,uBAAuB,QAAQ,gBAAgB,mBAAmB;AACtI,WAAO,OAAO;AAAA,EAChB;AACA,MAAI,yBAAyB,MAAM,EAAG,QAAO;AAC7C,MAAI,8BAA8B,MAAM,EAAG,QAAO;AAClD,SAAO;AACT;AAEO,SAAS,4BAAoE,SAIlF;AACA,QAAM,eAAoB,CAAC;AAC3B,QAAM,oBAAyB,CAAC;AAChC,QAAM,kBAAuB,CAAC;AAE9B,aAAW,UAAU,SAAS;AAC5B,UAAM,OAAO,0BAA0B,MAAM;AAC7C,QAAI,SAAS,gBAAgB;AAC3B,mBAAa,KAAK,MAAM;AAAA,IAC1B,WAAW,SAAS,qBAAqB;AACvC,wBAAkB,KAAK,MAAM;AAAA,IAC/B,OAAO;AACL,sBAAgB,KAAK,MAAM;AAAA,IAC7B;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEO,SAAS,wCACd,SACmE;AACnE,SAAO,4BAA4B,WAAW,CAAC,CAAC;AAClD;AAEO,SAAS,+BAA+B,SAA8B,YAAuC;AAClH,QAAM,SAAS,qBAAqB,SAAS,UAAU;AACvD,QAAM,cAAc,0BAA0B,MAAM;AACpD,MAAI,gBAAgB,gBAAgB;AAClC,WAAO;AAAA,EACT;AACA,MAAI,gBAAgB,qBAAqB;AACvC,UAAM,IAAI,MAAM,WAAW,OAAO,UAAU,gFAAgF;AAAA,EAC9H;AACA,QAAM,IAAI,MAAM,WAAW,OAAO,UAAU,OAAO,OAAO,SAAS,6BAA6B;AAClG;AAEA,SAAS,YAAY,SAA8B,WAA6E;AAC9H,QAAM,UAAU,QAAQ,OAAO,SAAS;AACxC,MAAI,QAAQ,WAAW,EAAG,QAAO,QAAQ,CAAC,KAAK;AAC/C,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,QAAM,SAAS,QAAQ,IAAI,CAAC,WAAW,GAAG,OAAO,UAAU,KAAK,OAAO,SAAS,GAAG,EAAE,KAAK,IAAI;AAC9F,QAAM,IAAI,MAAM,sCAAsC,MAAM,EAAE;AAChE;AAEO,SAAS,qBAAqB,SAA8B,YAAuC;AACxG,QAAM,SAAS,WAAW,KAAK;AAC/B,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,4BAA4B;AAAA,EAC9C;AAEA,QAAM,QAAQ;AAAA,IAAY;AAAA,IAAS,CAAC,WAClC,OAAO,cAAc,UACrB,eAAe,OAAO,UAAU,MAAM,eAAe,MAAM,KAC3D,eAAe,OAAO,WAAW,MAAM,eAAe,MAAM;AAAA,EAC9D;AACA,MAAI,MAAO,QAAO;AAElB,QAAM,SAAS;AAAA,IAAY;AAAA,IAAS,CAAC,WACnC,OAAO,UAAU,WAAW,MAAM,KAClC,eAAe,OAAO,UAAU,EAAE,WAAW,eAAe,MAAM,CAAC;AAAA,EACrE;AACA,MAAI,OAAQ,QAAO;AAEnB,QAAM,IAAI,MAAM,2BAA2B,MAAM,EAAE;AACrD;AAEO,SAAS,mBAAmB,QAAuD;AACxF,MAAI,CAAC,OAAO,WAAY,QAAO;AAC/B,SAAO,GAAG,OAAO,WAAW,SAAS,IAAI,OAAO,WAAW,QAAQ;AACrE;;;ACtLA,SAAS,eAAuB;AAC9B,MAAI,OAAO,WAAW,eAAe,OAAO,YAAY;AACtD,WAAO,OAAO,WAAW;AAAA,EAC3B;AACA,SAAO,uCAAuC,QAAQ,SAAS,OAAK;AAClE,UAAM,IAAI,KAAK,OAAO,IAAI,KAAK;AAC/B,UAAM,IAAI,MAAM,MAAM,IAAK,IAAI,IAAM;AACrC,WAAO,EAAE,SAAS,EAAE;AAAA,EACtB,CAAC;AACH;AAoBO,IAAM,sBAAN,MAA0B;AAAA,EACvB,WAAW,oBAAI,IAAiC;AAAA,EAExD,cAAc,SAAkD;AAC9D,UAAM,YAAY,QAAQ,aAAa,aAAa;AACpD,QAAI,KAAK,SAAS,IAAI,SAAS,GAAG;AAChC,YAAM,IAAI,MAAM,2BAA2B,SAAS,EAAE;AAAA,IACxD;AACA,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,gBAAgB,QAAQ,WAC1B,CAAC;AAAA,MACC,UAAU,QAAQ;AAAA,MAClB,MAAM,QAAQ,cAAc;AAAA,MAC5B,UAAU;AAAA,MACV,YAAY;AAAA,MACZ,YAAY;AAAA,IACd,CAAiC,IACjC,CAAC;AAEL,UAAM,SAA4B;AAAA,MAChC;AAAA,MACA,YAAY;AAAA,QACV;AAAA,QACA,MAAM,KAAK,KAAK,SAAS,OAAO,GAAG,CAAC,UAAU,MAAM,OAAO,UAAU;AAAA,MACvE;AAAA,MACA,aAAa,wBAAwB,OAAO;AAAA,MAC5C,gBAAgB,kBAAkB,QAAQ,SAAS;AAAA,MACnD,WAAW;AAAA,MACX,cAAc,QAAQ;AAAA,MACtB,UAAU,QAAQ;AAAA,MAClB,WAAW,QAAQ;AAAA,MACnB,eAAe,QAAQ;AAAA,MACvB,WAAW;AAAA,MACX,gBAAgB;AAAA,MAChB,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,iBAAiB;AAAA,MACjB,QAAQ;AAAA,QACN,iBAAiB;AAAA,QACjB,aAAa;AAAA,MACf;AAAA,MACA,MAAM,QAAQ,QAAQ,CAAC;AAAA,IACzB;AAEA,WAAO,OAAO;AAAA,MACZ,iBAAiB,uBAAuB,QAAQ,IAAI;AAAA,MACpD,iBAAiB,uBAAuB,QAAQ,IAAI;AAAA,MACpD,GAAG,OAAO;AAAA,IACZ;AAEA,SAAK,SAAS,IAAI,WAAW;AAAA,MAC3B;AAAA,MACA,QAAQ,IAAI,kBAAkB;AAAA,IAChC,CAAC;AAED,WAAO,KAAK,YAAY,MAAM;AAAA,EAChC;AAAA,EAEA,eAAe,QAA2B,UAAoE;AAC5G,UAAM,SAAS,KAAK,YAAY,MAAM;AACtC,SAAK,SAAS,IAAI,OAAO,WAAW;AAAA,MAClC,QAAQ;AAAA,MACR,SAAS,MAAM;AACb,cAAM,SAAS,IAAI,kBAAkB;AACrC,YAAI,SAAU,QAAO,QAAQ,QAAQ;AACrC,eAAO;AAAA,MACT,GAAG;AAAA,IACL,CAAC;AACD,WAAO,KAAK,YAAY,MAAM;AAAA,EAChC;AAAA,EAEA,eAAoC;AAClC,WAAO,MAAM,KAAK,KAAK,SAAS,OAAO,CAAC,EACrC,IAAI,WAAS,KAAK,YAAY,MAAM,MAAM,CAAC,EAC3C,KAAK,CAAC,GAAG,MAAM,EAAE,iBAAiB,EAAE,cAAc;AAAA,EACvD;AAAA,EAEA,WAAW,WAA6C;AACtD,UAAM,QAAQ,KAAK,SAAS,IAAI,SAAS;AACzC,WAAO,QAAQ,KAAK,YAAY,MAAM,MAAM,IAAI;AAAA,EAClD;AAAA,EAEA,aAAa,SAAkD;AAC7D,UAAM,QAAQ,KAAK,eAAe,QAAQ,SAAS;AACnD,UAAM,MAAM,KAAK,IAAI;AACrB,QAAI,qBAAqB;AAEzB,QAAI,QAAQ,eAAe,UAAU;AACnC,YAAM,uBAAuB,MAAM,OAAO,gBACvC,OAAO,YAAU,OAAO,SAAS,YAAY,OAAO,aAAa,QAAQ,QAAQ,EACjF,IAAI,YAAU,OAAO,QAAQ;AAChC,UAAI,qBAAqB,SAAS,GAAG;AACnC,cAAM,OAAO,kBAAkB,MAAM,OAAO,gBAAgB;AAAA,UAC1D,YAAU,EAAE,OAAO,SAAS,YAAY,OAAO,aAAa,QAAQ;AAAA,QACtE;AACA,YAAI,MAAM,OAAO,cAAc,qBAAqB,SAAS,MAAM,OAAO,WAAW,QAAQ,GAAG;AAC9F,+BAAqB;AAAA,QACvB;AAAA,MACF;AAAA,IACF;AAEA,UAAM,WAAW,MAAM,OAAO,gBAAgB,KAAK,YAAU,OAAO,aAAa,QAAQ,QAAQ;AAEjG,QAAI,UAAU;AACZ,eAAS,OAAO,QAAQ;AACxB,eAAS,WAAW,CAAC,CAAC,QAAQ;AAC9B,eAAS,aAAa;AAAA,IACxB,OAAO;AACL,YAAM,OAAO,gBAAgB,KAAK;AAAA,QAChC,UAAU,QAAQ;AAAA,QAClB,MAAM,QAAQ;AAAA,QACd,UAAU,CAAC,CAAC,QAAQ;AAAA,QACpB,YAAY;AAAA,QACZ,YAAY;AAAA,MACd,CAAC;AAAA,IACH;AAEA,QAAI,oBAAoB;AACtB,YAAM,OAAO,aAAa;AAAA,IAC5B;AAEA,UAAM,OAAO,iBAAiB;AAC9B,WAAO,KAAK,YAAY,MAAM,MAAM;AAAA,EACtC;AAAA,EAEA,aAAa,SAAkD;AAC7D,UAAM,QAAQ,KAAK,eAAe,QAAQ,SAAS;AACnD,UAAM,OAAO,kBAAkB,MAAM,OAAO,gBAAgB,OAAO,YAAU,OAAO,aAAa,QAAQ,QAAQ;AACjH,QAAI,MAAM,OAAO,YAAY,aAAa,QAAQ,UAAU;AAC1D,YAAM,OAAO,aAAa;AAAA,IAC5B;AACA,UAAM,OAAO,iBAAiB,KAAK,IAAI;AACvC,WAAO,KAAK,YAAY,MAAM,MAAM;AAAA,EACtC;AAAA,EAEA,aAAa,SAAiD;AAC5D,UAAM,QAAQ,KAAK,eAAe,QAAQ,SAAS;AACnD,QAAI,MAAM,OAAO,cAAc,MAAM,OAAO,WAAW,aAAa,QAAQ,YAAY,CAAC,QAAQ,OAAO;AACtG,YAAM,IAAI,MAAM,kBAAkB,MAAM,OAAO,WAAW,QAAQ,EAAE;AAAA,IACtE;AACA,UAAM,iBAAiB,MAAM,OAAO,gBAAgB,KAAK,YAAU,OAAO,aAAa,QAAQ,QAAQ;AACvG,QAAI,gBAAgB;AAClB,qBAAe,WAAW;AAC1B,qBAAe,aAAa,KAAK,IAAI;AAAA,IACvC;AACA,UAAM,OAAO,aAAa;AAAA,MACxB,UAAU,QAAQ;AAAA,MAClB,WAAW,QAAQ;AAAA,MACnB,YAAY,KAAK,IAAI;AAAA,IACvB;AACA,UAAM,OAAO,iBAAiB,KAAK,IAAI;AACvC,WAAO,KAAK,YAAY,MAAM,MAAM;AAAA,EACtC;AAAA,EAEA,aAAa,SAAiD;AAC5D,UAAM,QAAQ,KAAK,eAAe,QAAQ,SAAS;AACnD,UAAM,iBAAiB,MAAM,OAAO,gBAAgB,KAAK,YAAU,OAAO,aAAa,QAAQ,QAAQ;AACvG,QAAI,gBAAgB;AAClB,qBAAe,WAAW;AAC1B,qBAAe,aAAa,KAAK,IAAI;AAAA,IACvC;AACA,QAAI,MAAM,OAAO,YAAY,aAAa,QAAQ,UAAU;AAC1D,YAAM,OAAO,aAAa;AAAA,IAC5B;AACA,UAAM,OAAO,iBAAiB,KAAK,IAAI;AACvC,WAAO,KAAK,YAAY,MAAM,MAAM;AAAA,EACtC;AAAA,EAEA,aAAa,WAAmB,MAA0D;AACxF,UAAM,QAAQ,KAAK,eAAe,SAAS;AAC3C,UAAM,MAAM,MAAM,OAAO,OAAO,IAAI;AACpC,UAAM,OAAO,SAAS,MAAM,OAAO,SAAS;AAC5C,UAAM,OAAO,iBAAiB,KAAK,IAAI;AACvC,WAAO,EAAE,QAAQ,KAAK,YAAY,MAAM,MAAM,GAAG,IAAI;AAAA,EACvD;AAAA,EAEA,YAAY,WAAmB,UAAmB;AAChD,UAAM,QAAQ,KAAK,eAAe,SAAS;AAC3C,UAAM,OAAO,SAAS,MAAM,OAAO,SAAS;AAC5C,WAAO,MAAM,OAAO,SAAS,QAAQ;AAAA,EACvC;AAAA,EAEA,YAAY,WAAsC;AAChD,UAAM,QAAQ,KAAK,eAAe,SAAS;AAC3C,UAAM,OAAO,MAAM;AACnB,UAAM,OAAO,SAAS,MAAM,OAAO,SAAS;AAC5C,UAAM,OAAO,iBAAiB,KAAK,IAAI;AACvC,WAAO,KAAK,YAAY,MAAM,MAAM;AAAA,EACtC;AAAA,EAEA,kBAAkB,WAAmB,MAA+B,UAAU,OAA0B;AACtG,UAAM,QAAQ,KAAK,eAAe,SAAS;AAC3C,UAAM,OAAO,OAAO,UAChB,EAAE,GAAG,KAAK,IACV;AAAA,MACE,GAAI,MAAM,OAAO,QAAQ,CAAC;AAAA,MAC1B,GAAG;AAAA,IACL;AACJ,UAAM,OAAO,iBAAiB,KAAK,IAAI;AACvC,WAAO,KAAK,YAAY,MAAM,MAAM;AAAA,EACtC;AAAA,EAEA,YAAY,WAAmB,KAAiC;AAC9D,UAAM,QAAQ,KAAK,eAAe,SAAS;AAC3C,UAAM,OAAO,YAAY;AACzB,UAAM,OAAO,YAAY,MAAM,OAAO,aAAa,KAAK,IAAI;AAC5D,QAAI,OAAO,QAAQ,SAAU,OAAM,OAAO,QAAQ;AAClD,UAAM,OAAO,iBAAiB,KAAK,IAAI;AACvC,WAAO,KAAK,YAAY,MAAM,MAAM;AAAA,EACtC;AAAA,EAEA,YAAY,WAAmB,YAAkC,WAAW,aAAqD;AAC/H,UAAM,QAAQ,KAAK,eAAe,SAAS;AAC3C,UAAM,OAAO,YAAY;AACzB,UAAM,OAAO,iBAAiB,KAAK,IAAI;AACvC,QAAI,YAAa,OAAM,OAAO,cAAc;AAC5C,WAAO,KAAK,YAAY,MAAM,MAAM;AAAA,EACtC;AAAA,EAEA,aAAa,WAAmB,WAA0G;AACxI,UAAM,QAAQ,KAAK,eAAe,SAAS;AAC3C,UAAM,OAAO,YAAY;AACzB,UAAM,OAAO,iBAAiB,KAAK,IAAI;AACvC,WAAO,KAAK,YAAY,MAAM,MAAM;AAAA,EACtC;AAAA,EAEA,cAAc,WAA4B;AACxC,WAAO,KAAK,SAAS,OAAO,SAAS;AAAA,EACvC;AAAA,EAEQ,eAAe,WAAwC;AAC7D,UAAM,QAAQ,KAAK,SAAS,IAAI,SAAS;AACzC,QAAI,CAAC,MAAO,OAAM,IAAI,MAAM,oBAAoB,SAAS,EAAE;AAC3D,WAAO;AAAA,EACT;AAAA,EAEQ,YAAY,QAA8C;AAChE,WAAO;AAAA,MACL,GAAG;AAAA,MACH,eAAe;AAAA,QACb,GAAG,OAAO;AAAA,QACV,MAAM,CAAC,GAAG,OAAO,cAAc,IAAI;AAAA,QACnC,KAAK,OAAO,cAAc,MAAM,EAAE,GAAG,OAAO,cAAc,IAAI,IAAI;AAAA,MACpE;AAAA,MACA,YAAY,OAAO,aAAa,EAAE,GAAG,OAAO,WAAW,IAAI;AAAA,MAC3D,iBAAiB,OAAO,gBAAgB,IAAI,aAAW,EAAE,GAAG,OAAO,EAAE;AAAA,MACrE,QAAQ,EAAE,GAAG,OAAO,OAAO;AAAA,MAC3B,MAAM,EAAE,GAAG,OAAO,KAAK;AAAA,IACzB;AAAA,EACF;AACF;;;ACvRA,SAAoB;AACpB,IAAAA,QAAsB;AACtB,UAAqB;AAUrB,SAASC,gBAAuB;AAC9B,MAAI,OAAO,WAAW,eAAe,OAAO,YAAY;AACtD,WAAO,OAAO,WAAW;AAAA,EAC3B;AACA,SAAO,uCAAuC,QAAQ,SAAS,OAAK;AAClE,UAAM,IAAI,KAAK,OAAO,IAAI,KAAK;AAC/B,UAAM,IAAI,MAAM,MAAM,IAAK,IAAI,IAAM;AACrC,WAAO,EAAE,SAAS,EAAE;AAAA,EACtB,CAAC;AACH;AAOO,SAAS,8BAA8B,UAAU,UAA+B;AACrF,MAAI,QAAQ,aAAa,SAAS;AAChC,WAAO;AAAA,MACL,MAAM;AAAA,MACN,MAAM,gBAAgB,OAAO;AAAA,IAC/B;AAAA,EACF;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,MAAW,WAAQ,UAAO,GAAG,GAAG,OAAO,oBAAoB;AAAA,EAC7D;AACF;AAEA,SAAS,kBAAkB,UAA2C;AACpE,SAAO,GAAG,KAAK,UAAU,QAAQ,CAAC;AAAA;AACpC;AAEA,SAAS,iBAAiB,YAAyD;AACjF,MAAI,SAAS;AACb,SAAO,CAAC,UAA2B;AACjC,cAAU,MAAM,SAAS;AACzB,QAAI,eAAe,OAAO,QAAQ,IAAI;AACtC,WAAO,gBAAgB,GAAG;AACxB,YAAM,UAAU,OAAO,MAAM,GAAG,YAAY,EAAE,KAAK;AACnD,eAAS,OAAO,MAAM,eAAe,CAAC;AACtC,UAAI,SAAS;AACX,mBAAW,KAAK,MAAM,OAAO,CAA4B;AAAA,MAC3D;AACA,qBAAe,OAAO,QAAQ,IAAI;AAAA,IACpC;AAAA,EACF;AACF;AAOO,IAAM,oBAAN,MAAwB;AAAA,EACpB;AAAA,EAED,SAA4B;AAAA,EAC5B,iBAAiB,oBAAI,IAA+F;AAAA,EACpH,iBAAiB,oBAAI,IAAuC;AAAA,EAEpE,YAAY,UAAoC,CAAC,GAAG;AAClD,SAAK,WAAW,QAAQ,YAAY,8BAA8B,QAAQ,WAAW,QAAQ;AAAA,EAC/F;AAAA,EAEA,MAAM,UAAyB;AAC7B,QAAI,KAAK,UAAU,CAAC,KAAK,OAAO,UAAW;AAE3C,QAAI,KAAK,QAAQ;AACf,UAAI;AAAE,aAAK,OAAO,QAAQ;AAAA,MAAG,QAAQ;AAAA,MAAa;AAClD,WAAK,SAAS;AAAA,IAChB;AAEA,UAAM,SAAa,qBAAiB,KAAK,SAAS,IAAI;AACtD,SAAK,SAAS;AAEd,WAAO,GAAG,QAAQ,iBAAiB,CAAC,aAAa;AAC/C,UAAI,SAAS,SAAS,YAAY;AAChC,cAAM,SAAS,KAAK,eAAe,IAAI,SAAS,SAAS;AACzD,YAAI,QAAQ;AACV,eAAK,eAAe,OAAO,SAAS,SAAS;AAC7C,iBAAO,QAAQ,SAAS,QAAQ;AAAA,QAClC;AACA;AAAA,MACF;AAEA,UAAI,SAAS,SAAS,SAAS;AAC7B,mBAAW,YAAY,KAAK,eAAgB,UAAS,SAAS,KAAK;AAAA,MACrE;AAAA,IACF,CAAC,CAAC;AAEF,WAAO,GAAG,SAAS,CAAC,UAAU;AAC5B,iBAAW,UAAU,KAAK,eAAe,OAAO,GAAG;AACjD,eAAO,OAAO,KAAK;AAAA,MACrB;AACA,WAAK,eAAe,MAAM;AAG1B,UAAI,KAAK,WAAW,QAAQ;AAC1B,aAAK,SAAS;AAAA,MAChB;AACA,UAAI;AAAE,eAAO,QAAQ;AAAA,MAAG,QAAQ;AAAA,MAAa;AAAA,IAC/C,CAAC;AAED,UAAM,IAAI,QAAc,CAACC,UAAS,WAAW;AAC3C,aAAO,KAAK,WAAW,MAAMA,SAAQ,CAAC;AACtC,aAAO,KAAK,SAAS,MAAM;AAAA,IAC7B,CAAC;AAAA,EACH;AAAA,EAEA,QAAQ,UAAyD;AAC/D,SAAK,eAAe,IAAI,QAAQ;AAChC,WAAO,MAAM;AACX,WAAK,eAAe,OAAO,QAAQ;AAAA,IACrC;AAAA,EACF;AAAA,EAEA,MAAM,QAAqB,SAA8D;AACvF,UAAM,KAAK,QAAQ;AACnB,QAAI,CAAC,KAAK,OAAQ,OAAM,IAAI,MAAM,iCAAiC;AAEnE,UAAM,YAAYD,cAAa;AAC/B,UAAM,WAAuC;AAAA,MAC3C,MAAM;AAAA,MACN;AAAA,MACA;AAAA,IACF;AAEA,UAAM,WAAW,MAAM,IAAI,QAA6B,CAACC,UAAS,WAAW;AAC3E,YAAM,UAAU,WAAW,MAAM;AAC/B,aAAK,eAAe,OAAO,SAAS;AACpC,eAAO,IAAI,MAAM,6CAA6C,QAAQ,IAAI,GAAG,CAAC;AAAA,MAChF,GAAG,GAAM;AACT,WAAK,eAAe,IAAI,WAAW;AAAA,QACjC,SAAS,CAAC,UAAU;AAAE,uBAAa,OAAO;AAAG,UAAAA,SAAQ,KAAK;AAAA,QAAG;AAAA,QAC7D,QAAQ,CAAC,UAAU;AAAE,uBAAa,OAAO;AAAG,iBAAO,KAAK;AAAA,QAAG;AAAA,MAC7D,CAAC;AACD,WAAK,QAAQ,MAAM,kBAAkB,QAAQ,CAAC;AAAA,IAChD,CAAC;AAED,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,QAAuB;AAC3B,QAAI,CAAC,KAAK,OAAQ;AAClB,UAAM,SAAS,KAAK;AACpB,SAAK,SAAS;AACd,eAAW,UAAU,KAAK,eAAe,OAAO,GAAG;AACjD,aAAO,OAAO,IAAI,MAAM,4BAA4B,CAAC;AAAA,IACvD;AACA,SAAK,eAAe,MAAM;AAC1B,UAAM,IAAI,QAAc,CAACA,aAAY;AACnC,UAAI,UAAU;AACd,YAAM,OAAO,MAAM;AACjB,YAAI,QAAS;AACb,kBAAU;AACV,QAAAA,SAAQ;AAAA,MACV;AACA,aAAO,KAAK,SAAS,IAAI;AACzB,aAAO,IAAI;AACX,aAAO,QAAQ;AACf,iBAAW,MAAM,EAAE;AAAA,IACrB,CAAC;AAAA,EACH;AACF;AAEO,SAAS,uBAAuB,WAAmB,UAA4D;AACpH,SAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA,IACA;AAAA,EACF;AACF;AAEO,SAAS,cAAc,QAAmC,UAAyC;AACxG,SAAO,MAAM,kBAAkB,QAAQ,CAAC;AAC1C;;;ACnLA,IAAAC,MAAoB;AACpB,IAAAC,QAAsB;AAMf,SAAS,iBACZ,SACA,WACsB;AACtB,QAAM,MAA8B,CAAC;AACrC,QAAM,SAAS,EAAE,GAAG,SAAS,GAAI,aAAa,CAAC,EAAG;AAElD,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AAC/C,QAAI,OAAO,UAAU,SAAU;AAC/B,QAAI,GAAG,IAAI;AAAA,EACf;AAEA,aAAW,OAAO,OAAO,KAAK,GAAG,GAAG;AAChC,QACI,QAAQ,cACL,QAAQ,iBACR,QAAQ,kBACR,QAAQ,uBACR,IAAI,WAAW,MAAM,KACrB,IAAI,WAAW,aAAa,KAC5B,IAAI,WAAW,cAAc,KAC7B,IAAI,WAAW,gBAAgB,KAC/B,IAAI,WAAW,OAAO,KACtB,IAAI,WAAW,OAAO,KACtB,IAAI,WAAW,MAAM,KACrB,IAAI,WAAW,SAAS,KACxB,IAAI,WAAW,WAAW,GAC/B;AACE,aAAO,IAAI,GAAG;AAAA,IAClB;AAAA,EACJ;AAMA,SAAO,IAAI;AACX,SAAO,IAAI;AACX,SAAO,IAAI;AACX,SAAO,IAAI;AACX,SAAO,IAAI;AAWX,MAAI,QAAQ,aAAa,SAAS;AAC9B,WAAO,IAAI;AACX,WAAO,IAAI;AACX,WAAO,IAAI;AACX,WAAO,IAAI;AACX,WAAO,IAAI;AAAA,EACf;AAEA,wBAAsB,GAAG;AACzB,SAAO;AACX;AAMO,SAAS,sBAAsB,KAAmC;AACrE,MAAI,IAAI,SAAU;AAElB,MAAI,CAAC,IAAI,QAAQ,IAAI,SAAS,eAAe;AACzC,QAAI,OAAO;AAAA,EACf;AACA,MAAI,CAAC,IAAI,UAAW,KAAI,YAAY;AAEpC,MAAI,QAAQ,aAAa,SAAS;AAC9B,QAAI,CAAC,IAAI,YAAa,KAAI,cAAc;AACxC,QAAI,CAAC,IAAI,SAAU,KAAI,WAAW;AAAA,EACtC;AACJ;AAUO,SAAS,oCACZ,OACI;AACJ,MAAO,aAAS,MAAM,QAAS;AAC/B,MAAI;AACA,UAAM,KAAK,QAAQ,IAAI;AACvB,UAAM,SAAc,cAAa,cAAQ,gBAAgB,UAAU,CAAC,GAAG,IAAI;AAC3E,UAAM,eAAe,GAAM,aAAS,CAAC,IAAO,SAAK,CAAC;AAClD,UAAM,SAAc,WAAK,QAAQ,aAAa,cAAc,cAAc;AAC1E,QAAI,GAAG,WAAW,MAAM,GAAG;AACvB,YAAM,OAAO,GAAG,SAAS,MAAM;AAC/B,UAAI,EAAE,KAAK,OAAO,KAAQ;AACtB,WAAG,UAAU,QAAQ,KAAK,OAAO,GAAK;AACtC,gBAAQ,mCAAmC,MAAM,EAAE;AAAA,MACvD;AAAA,IACJ;AAAA,EACJ,QAAQ;AAAA,EAER;AACJ;;;ACxEO,SAAS,8BACd,WACyB;AACzB,SAAO;AAAA,IACL,eAAe,UAAqC,CAAC,GAAoC;AACvF,aAAO,UAAU,QAAgC,wBAAwB,OAAkC;AAAA,IAC7G;AAAA,IACA,eAA6C;AAC3C,aAAO,UAAU,QAA6B,iBAAiB,CAAC,CAAC;AAAA,IACnE;AAAA,IACA,YAAY,WAAsD;AAChE,aAAO,UAAU,QAAkC,gBAAgB,EAAE,UAAU,CAAC;AAAA,IAClF;AAAA,IACA,cAAc,WAAmB,OAA4B,CAAC,GAAsC;AAClG,aAAO,UAAU,QAAkC,kBAAkB,EAAE,WAAW,OAAO,KAAK,UAAU,KAAK,CAAC;AAAA,IAChH;AAAA,IACA,cAAc,WAAsD;AAClE,aAAO,UAAU,QAAkC,kBAAkB,EAAE,UAAU,CAAC;AAAA,IACpF;AAAA,IACA,eAAe,WAAsD;AACnE,aAAO,UAAU,QAAkC,mBAAmB,EAAE,UAAU,CAAC;AAAA,IACrF;AAAA,IACA,WAAW,WAAmB,QAAmD;AAC/E,aAAO,UAAU,QAAkC,eAAe,EAAE,WAAW,OAAO,CAAC;AAAA,IACzF;AAAA,IACA,kBAAkB,WAAmB,UAAqD;AACxF,aAAO,UAAU,QAAkC,uBAAuB,EAAE,WAAW,SAAS,CAAC;AAAA,IACnG;AAAA,IACA,uBAAuB,UAAyC,CAAC,GAA8C;AAC7G,aAAO,UAAU,QAA0C,4BAA4B,OAAkC;AAAA,IAC3H;AAAA,IACA,aAAa,SAAiE;AAC5E,aAAO,UAAU,QAAkC,iBAAiB,OAA6C;AAAA,IACnH;AAAA,IACA,aAAa,SAAiE;AAC5E,aAAO,UAAU,QAAkC,iBAAiB,OAA6C;AAAA,IACnH;AAAA,EACF;AACF;","names":["path","generateUUID","resolve","os","path"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/types.ts","../src/termination.ts","../src/buffer.ts","../src/defaults.ts","../src/runtime-labels.ts","../src/registry.ts","../src/ipc.ts","../src/instance-key.ts","../src/spawn-env.ts","../src/control-plane.ts"],"sourcesContent":["export type {\n AcquireWritePayload,\n AttachSessionPayload,\n ClearSessionBufferPayload,\n CreateSessionPayload,\n DetachSessionPayload,\n ForceDetachClientPayload,\n GetHostDiagnosticsPayload,\n GetSnapshotPayload,\n GetTerminalSnapshotPayload,\n PruneDuplicateSessionsPayload,\n ReleaseWritePayload,\n ResumeSessionPayload,\n RestartSessionPayload,\n ResizeSessionPayload,\n SendSignalPayload,\n SendInputPayload,\n UpdateSessionMetaPayload,\n SessionAttachedClient,\n SessionBufferSnapshot,\n SessionClientType,\n SessionHostDiagnostics,\n SessionHostDuplicateSessionGroup,\n SessionHostCategory,\n SessionHostSurfaceKind,\n SessionHostEvent,\n SessionHostEventEnvelope,\n SessionHostLogEntry,\n SessionHostPruneDuplicatesResult,\n SessionHostRecord,\n SessionHostRequest,\n SessionHostRequestEnvelope,\n SessionHostRequestTrace,\n SessionHostRequestType,\n SessionHostResponse,\n SessionHostResponseEnvelope,\n SessionHostRuntimeTransition,\n SessionTerminalSnapshot,\n SessionTerminalState,\n SessionLaunchCommand,\n SessionLifecycle,\n SessionOwnerType,\n SessionTransport,\n SessionTermination,\n SessionHostWireEnvelope,\n SessionWriteOwner,\n StopSessionPayload,\n} from './types.js';\n\nexport { SESSION_HOST_SUPPORTED_REQUEST_TYPES } from './types.js';\nexport { classifyTermination } from './termination.js';\n\nexport { DEFAULT_SESSION_RING_BUFFER_MAX_BYTES, SessionRingBuffer } from './buffer.js';\nexport type { SessionRingBufferOptions } from './buffer.js';\nexport { SessionHostRegistry } from './registry.js';\nexport {\n buildRuntimeDisplayName,\n buildRuntimeKey,\n formatRuntimeOwner,\n getSessionHostRecoveryLabel,\n getSessionHostSurfaceKind,\n getWorkspaceLabel,\n isSessionHostLiveRuntime,\n isSessionHostRecoverySnapshot,\n partitionSessionHostDiagnosticsSessions,\n partitionSessionHostRecords,\n resolveAttachableRuntimeRecord,\n resolveRuntimeRecord,\n} from './runtime-labels.js';\nexport type { SessionHostSurfaceRecordLike } from './runtime-labels.js';\nexport {\n SessionHostClient,\n createLineParser,\n createResponseEnvelope,\n getDefaultSessionHostEndpoint,\n writeEnvelope,\n} from './ipc.js';\nexport type { SessionHostClientOptions, SessionHostEndpoint, SessionHostEndpointOptions } from './ipc.js';\nexport {\n DEFAULT_CONFIG_DIR_NAME,\n canonicalizeInstancePath,\n isDefaultInstanceConfigDir,\n resolveInstanceConfigDir,\n resolveSessionHostIpcKey,\n} from './instance-key.js';\nexport {\n DEFAULT_SESSION_HOST_COLS,\n DEFAULT_SESSION_HOST_ROWS,\n resolveSessionHostCols,\n resolveSessionHostRows,\n} from './defaults.js';\nexport {\n sanitizeSpawnEnv,\n applyTerminalColorEnv,\n ensureNodePtySpawnHelperPermissions,\n} from './spawn-env.js';\nexport { createSessionHostControlPlane } from './control-plane.js';\nexport type { SessionHostControlPlane, SessionHostControlTransport } from './control-plane.js';\n","export type SessionTransport = 'pty';\n\nexport type SessionHostCategory = 'cli' | 'acp' | 'shell';\n\nexport type SessionLifecycle = 'starting' | 'running' | 'stopping' | 'stopped' | 'failed' | 'interrupted';\n\nexport type SessionClientType = 'daemon' | 'web' | 'local-terminal';\n\nexport type SessionOwnerType = 'agent' | 'user';\n\nexport interface SessionLaunchCommand {\n command: string;\n args: string[];\n env?: Record<string, string>;\n}\n\nexport interface SessionWriteOwner {\n clientId: string;\n ownerType: SessionOwnerType;\n acquiredAt: number;\n}\n\nexport interface SessionAttachedClient {\n clientId: string;\n type: SessionClientType;\n readOnly: boolean;\n attachedAt: number;\n lastSeenAt: number;\n}\n\nexport interface SessionBufferSnapshot {\n seq: number;\n text: string;\n truncated: boolean;\n cols?: number;\n rows?: number;\n}\n\nexport interface SessionTerminalState {\n cursor: { row: number; col: number };\n altScreen: boolean;\n pasteMode: boolean;\n rawMode: boolean;\n scrollRegion: { top: number; bot: number };\n cols: number;\n rows: number;\n}\n\nexport interface SessionTerminalSnapshot {\n text: string;\n state: SessionTerminalState;\n}\n\nexport interface SessionBufferState {\n scrollbackBytes: number;\n snapshotSeq: number;\n}\n\nexport type SessionHostSurfaceKind = 'live_runtime' | 'recovery_snapshot' | 'inactive_record';\n\n/**\n * Compact, secret-free record of how a session's PTY process terminated.\n *\n * Preserves the nullable/unknown exitCode and signal exactly as observed from\n * the first node-pty `onExit` — an `exitCode` of `null` means \"unknown\" (the\n * process was terminated by a signal, or the exit code was otherwise not\n * reported) and MUST NOT be collapsed to 0. Persisted as a tombstone so\n * termination diagnostics remain inspectable after the live runtime and its\n * transport adapter are torn down.\n */\nexport interface SessionTermination {\n /** Exit code exactly as observed; `null` = unknown / signal-terminated. Never fabricated to 0. */\n exitCode: number | null;\n /** Signal number that terminated the process, if any; `null` when not signalled. */\n signal: number | null;\n /** Classification derived from (exitCode, signal): clean exit vs. failure vs. unknown. */\n reason: 'exit' | 'signal' | 'failed' | 'unknown';\n /** Lifecycle recorded for this termination (`stopped` | `failed`). */\n lifecycle: Extract<SessionLifecycle, 'stopped' | 'failed'>;\n /** Wall-clock time the termination was observed. */\n terminatedAt: number;\n /** OS pid of the terminated process, if it was known. */\n osPid?: number;\n /** Lifecycle the session was in immediately before termination. */\n previousLifecycle?: SessionLifecycle;\n /** Last time output was observed from the session, if known. */\n lastOutputAt?: number;\n /** Whether the termination followed an explicit stop/delete request, and which. */\n requestedStop?: 'stop' | 'delete' | 'restart' | 'prune';\n}\n\nexport interface SessionHostRecord {\n sessionId: string;\n runtimeKey: string;\n displayName: string;\n workspaceLabel: string;\n transport: SessionTransport;\n providerType: string;\n category: SessionHostCategory;\n workspace: string;\n launchCommand: SessionLaunchCommand;\n osPid?: number;\n createdAt: number;\n startedAt?: number;\n lastActivityAt: number;\n lifecycle: SessionLifecycle;\n surfaceKind?: SessionHostSurfaceKind;\n writeOwner: SessionWriteOwner | null;\n attachedClients: SessionAttachedClient[];\n buffer: SessionBufferState;\n meta: Record<string, unknown>;\n /** Termination tombstone, stamped when the PTY process exits. Retained for post-mortem inspection. */\n termination?: SessionTermination;\n}\n\nexport interface CreateSessionPayload {\n sessionId?: string;\n runtimeKey?: string;\n displayName?: string;\n providerType: string;\n category: SessionHostCategory;\n workspace: string;\n launchCommand: SessionLaunchCommand;\n cols?: number;\n rows?: number;\n clientId?: string;\n clientType?: SessionClientType;\n meta?: Record<string, unknown>;\n}\n\nexport interface AttachSessionPayload {\n sessionId: string;\n clientId: string;\n clientType: SessionClientType;\n readOnly?: boolean;\n}\n\nexport interface DetachSessionPayload {\n sessionId: string;\n clientId: string;\n}\n\nexport interface SendInputPayload {\n sessionId: string;\n clientId: string;\n data: string;\n}\n\nexport interface ResizeSessionPayload {\n sessionId: string;\n cols: number;\n rows: number;\n}\n\nexport interface StopSessionPayload {\n sessionId: string;\n}\n\nexport interface DeleteSessionPayload {\n sessionId: string;\n /** Stop a live runtime before deleting the persisted record. */\n force?: boolean;\n}\n\nexport interface ResumeSessionPayload {\n sessionId: string;\n}\n\nexport interface AcquireWritePayload {\n sessionId: string;\n clientId: string;\n ownerType: SessionOwnerType;\n force?: boolean;\n}\n\nexport interface ReleaseWritePayload {\n sessionId: string;\n clientId: string;\n}\n\nexport interface GetSnapshotPayload {\n sessionId: string;\n sinceSeq?: number;\n}\n\nexport interface GetTerminalSnapshotPayload {\n sessionId: string;\n}\n\nexport interface ClearSessionBufferPayload {\n sessionId: string;\n}\n\nexport interface UpdateSessionMetaPayload {\n sessionId: string;\n meta: Record<string, unknown>;\n replace?: boolean;\n}\n\nexport interface GetHostDiagnosticsPayload {\n includeSessions?: boolean;\n limit?: number;\n}\n\nexport interface ForceDetachClientPayload {\n sessionId: string;\n clientId: string;\n}\n\nexport interface SendSignalPayload {\n sessionId: string;\n signal: string;\n}\n\nexport interface RestartSessionPayload {\n sessionId: string;\n}\n\nexport interface PruneDuplicateSessionsPayload {\n providerType?: string;\n workspace?: string;\n dryRun?: boolean;\n}\n\nexport interface SessionHostDuplicateSessionGroup {\n bindingKey: string;\n providerType: string;\n workspace: string;\n providerSessionId: string;\n keptSessionId: string;\n prunedSessionIds: string[];\n}\n\nexport interface SessionHostPruneDuplicatesResult {\n duplicateGroupCount: number;\n keptSessionIds: string[];\n prunedSessionIds: string[];\n groups: SessionHostDuplicateSessionGroup[];\n}\n\nexport interface SessionHostLogEntry {\n timestamp: number;\n level: 'debug' | 'info' | 'warn' | 'error';\n message: string;\n sessionId?: string;\n data?: Record<string, unknown>;\n}\n\nexport interface SessionHostRequestTrace {\n timestamp: number;\n requestId: string;\n type: SessionHostRequest['type'];\n sessionId?: string;\n clientId?: string;\n success: boolean;\n durationMs: number;\n error?: string;\n}\n\nexport interface SessionHostRuntimeTransition {\n timestamp: number;\n sessionId: string;\n action: string;\n lifecycle?: SessionLifecycle;\n detail?: string;\n success?: boolean;\n error?: string;\n}\n\nexport interface SessionHostDiagnostics {\n hostStartedAt: number;\n endpoint: string;\n runtimeCount: number;\n supportedRequestTypes?: SessionHostRequestType[];\n sessions?: SessionHostRecord[];\n liveRuntimes?: SessionHostRecord[];\n recoverySnapshots?: SessionHostRecord[];\n inactiveRecords?: SessionHostRecord[];\n recentLogs: SessionHostLogEntry[];\n recentRequests: SessionHostRequestTrace[];\n recentTransitions: SessionHostRuntimeTransition[];\n}\n\nexport type SessionHostRequest =\n | { type: 'create_session'; payload: CreateSessionPayload }\n | { type: 'attach_session'; payload: AttachSessionPayload }\n | { type: 'detach_session'; payload: DetachSessionPayload }\n | { type: 'send_input'; payload: SendInputPayload }\n | { type: 'resize_session'; payload: ResizeSessionPayload }\n | { type: 'stop_session'; payload: StopSessionPayload }\n | { type: 'delete_session'; payload: DeleteSessionPayload }\n | { type: 'resume_session'; payload: ResumeSessionPayload }\n | { type: 'acquire_write'; payload: AcquireWritePayload }\n | { type: 'release_write'; payload: ReleaseWritePayload }\n | { type: 'get_snapshot'; payload: GetSnapshotPayload }\n | { type: 'get_terminal_snapshot'; payload: GetTerminalSnapshotPayload }\n | { type: 'clear_session_buffer'; payload: ClearSessionBufferPayload }\n | { type: 'update_session_meta'; payload: UpdateSessionMetaPayload }\n | { type: 'get_host_diagnostics'; payload?: GetHostDiagnosticsPayload }\n | { type: 'force_detach_client'; payload: ForceDetachClientPayload }\n | { type: 'send_signal'; payload: SendSignalPayload }\n | { type: 'restart_session'; payload: RestartSessionPayload }\n | { type: 'prune_duplicate_sessions'; payload?: PruneDuplicateSessionsPayload }\n | { type: 'list_sessions'; payload?: {} };\n\nexport type SessionHostRequestType = SessionHostRequest['type'];\n\nexport const SESSION_HOST_SUPPORTED_REQUEST_TYPES: readonly SessionHostRequestType[] = [\n 'create_session',\n 'attach_session',\n 'detach_session',\n 'send_input',\n 'resize_session',\n 'stop_session',\n 'delete_session',\n 'resume_session',\n 'acquire_write',\n 'release_write',\n 'get_snapshot',\n 'get_terminal_snapshot',\n 'clear_session_buffer',\n 'update_session_meta',\n 'get_host_diagnostics',\n 'force_detach_client',\n 'send_signal',\n 'restart_session',\n 'prune_duplicate_sessions',\n 'list_sessions',\n];\n\nexport interface SessionHostResponse<T = unknown> {\n success: boolean;\n result?: T;\n error?: string;\n}\n\nexport type SessionHostEvent =\n | { type: 'session_created'; sessionId: string; record: SessionHostRecord }\n | { type: 'session_started'; sessionId: string; pid?: number }\n | { type: 'session_resumed'; sessionId: string; pid?: number }\n | { type: 'session_output'; sessionId: string; seq: number; data: string }\n | { type: 'session_cleared'; sessionId: string }\n | { type: 'session_exit'; sessionId: string; exitCode: number | null; signal?: number | null; termination?: SessionTermination }\n | { type: 'session_stopped'; sessionId: string }\n | { type: 'session_deleted'; sessionId: string }\n | { type: 'session_resized'; sessionId: string; cols: number; rows: number }\n | { type: 'write_owner_changed'; sessionId: string; owner: SessionWriteOwner | null }\n | { type: 'client_attached'; sessionId: string; client: SessionAttachedClient }\n | { type: 'client_detached'; sessionId: string; clientId: string }\n | { type: 'host_log'; entry: SessionHostLogEntry }\n | { type: 'request_trace'; trace: SessionHostRequestTrace }\n | { type: 'runtime_transition'; transition: SessionHostRuntimeTransition };\n\nexport interface SessionHostRequestEnvelope {\n kind: 'request';\n requestId: string;\n request: SessionHostRequest;\n}\n\nexport interface SessionHostResponseEnvelope {\n kind: 'response';\n requestId: string;\n response: SessionHostResponse;\n}\n\nexport interface SessionHostEventEnvelope {\n kind: 'event';\n event: SessionHostEvent;\n}\n\nexport type SessionHostWireEnvelope =\n | SessionHostRequestEnvelope\n | SessionHostResponseEnvelope\n | SessionHostEventEnvelope;\n","import type { SessionLifecycle, SessionTermination } from './types.js';\n\nexport interface ClassifyTerminationInput {\n exitCode: number | null;\n signal?: number | null;\n osPid?: number;\n previousLifecycle?: SessionLifecycle;\n lastOutputAt?: number;\n requestedStop?: SessionTermination['requestedStop'];\n terminatedAt: number;\n}\n\n/**\n * Classify a PTY termination from its raw (exitCode, signal) pair.\n *\n * Invariants:\n * - A `null` exitCode is UNKNOWN, never a clean exit — it MUST NOT collapse to\n * exit 0. This is the regression guard for the old `exitCode ?? 0` behavior.\n * - A non-null, non-zero exitCode or any signal is a failure.\n * - Only an explicit exit code of 0 (with no signal) is a clean stop.\n *\n * The returned lifecycle is `stopped` only for a clean exit 0; every other\n * case (nonzero, signalled, or unknown) is `failed` so that an unknown\n * termination stays distinguishable from a successful one.\n */\nexport function classifyTermination(input: ClassifyTerminationInput): SessionTermination {\n const { exitCode, terminatedAt } = input;\n const signal = input.signal ?? null;\n\n let reason: SessionTermination['reason'];\n let lifecycle: SessionTermination['lifecycle'];\n\n if (signal !== null && signal !== 0) {\n reason = 'signal';\n lifecycle = 'failed';\n } else if (exitCode === null) {\n reason = 'unknown';\n lifecycle = 'failed';\n } else if (exitCode === 0) {\n reason = 'exit';\n lifecycle = 'stopped';\n } else {\n reason = 'failed';\n lifecycle = 'failed';\n }\n\n const termination: SessionTermination = {\n exitCode,\n signal,\n reason,\n lifecycle,\n terminatedAt,\n };\n if (typeof input.osPid === 'number') termination.osPid = input.osPid;\n if (input.previousLifecycle) termination.previousLifecycle = input.previousLifecycle;\n if (typeof input.lastOutputAt === 'number') termination.lastOutputAt = input.lastOutputAt;\n if (input.requestedStop) termination.requestedStop = input.requestedStop;\n return termination;\n}\n","import type { SessionBufferSnapshot } from './types.js';\n\nexport interface SessionRingBufferOptions {\n maxBytes?: number;\n}\n\n// Manual \"Load older terminal output\" replays this raw ring buffer into the\n// browser terminal. 512KiB was too small for long Claude/Codex conversations:\n// by the time the user scrolled to the top and clicked the loader, the output\n// they were trying to recover was often already trimmed.\nexport const DEFAULT_SESSION_RING_BUFFER_MAX_BYTES = 4 * 1024 * 1024;\n\nexport class SessionRingBuffer {\n private maxBytes: number;\n private chunks: { seq: number; data: string; bytes: number }[] = [];\n private nextSeq = 1;\n private totalBytes = 0;\n\n constructor(options: SessionRingBufferOptions = {}) {\n this.maxBytes = options.maxBytes ?? DEFAULT_SESSION_RING_BUFFER_MAX_BYTES;\n }\n\n append(data: string): number {\n const normalized = typeof data === 'string' ? data : String(data ?? '');\n const bytes = Buffer.byteLength(normalized, 'utf8');\n const seq = this.nextSeq++;\n\n this.chunks.push({ seq, data: normalized, bytes });\n this.totalBytes += bytes;\n this.trim();\n return seq;\n }\n\n snapshot(sinceSeq?: number): SessionBufferSnapshot {\n const relevant = typeof sinceSeq === 'number'\n ? this.chunks.filter(chunk => chunk.seq > sinceSeq)\n : this.chunks;\n\n const text = relevant.map(chunk => chunk.data).join('');\n const truncated = !!this.chunks[0] && typeof sinceSeq === 'number' && sinceSeq < this.chunks[0].seq - 1;\n\n return {\n seq: this.nextSeq - 1,\n text,\n truncated,\n };\n }\n\n getState(): { scrollbackBytes: number; snapshotSeq: number } {\n return {\n scrollbackBytes: this.totalBytes,\n snapshotSeq: this.nextSeq - 1,\n };\n }\n\n clear(): void {\n this.chunks = [];\n this.totalBytes = 0;\n this.nextSeq = 1;\n }\n\n restore(snapshot: { seq: number; text: string }): void {\n this.clear();\n const text = String(snapshot.text || '');\n if (!text) {\n this.nextSeq = Math.max(1, Number(snapshot.seq || 0) + 1);\n return;\n }\n const bytes = Buffer.byteLength(text, 'utf8');\n const seq = Math.max(1, Number(snapshot.seq || 1));\n this.chunks = [{ seq, data: text, bytes }];\n this.totalBytes = bytes;\n this.nextSeq = seq + 1;\n this.trim();\n }\n\n private trim(): void {\n while (this.totalBytes > this.maxBytes && this.chunks.length > 1) {\n const removed = this.chunks.shift();\n if (!removed) break;\n this.totalBytes -= removed.bytes;\n }\n }\n}\n","export const DEFAULT_SESSION_HOST_COLS = 80;\nexport const DEFAULT_SESSION_HOST_ROWS = 32;\n\nfunction normalizeSessionHostDimension(value: number | undefined, fallback: number): number {\n if (typeof value !== 'number' || !Number.isFinite(value)) return fallback;\n const rounded = Math.floor(value);\n return rounded > 0 ? rounded : fallback;\n}\n\nexport function resolveSessionHostCols(value: number | undefined): number {\n return normalizeSessionHostDimension(value, DEFAULT_SESSION_HOST_COLS);\n}\n\nexport function resolveSessionHostRows(value: number | undefined): number {\n return normalizeSessionHostDimension(value, DEFAULT_SESSION_HOST_ROWS);\n}\n","import * as path from 'path';\nimport type { CreateSessionPayload, SessionHostRecord, SessionHostSurfaceKind } from './types.js';\n\nfunction normalizeSlug(input: string): string {\n return input\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 48);\n}\n\nfunction normalizeValue(input: string): string {\n return input.trim().toLowerCase();\n}\n\nexport function getWorkspaceLabel(workspace: string): string {\n const trimmed = workspace.trim();\n if (!trimmed) return 'workspace';\n const normalized = trimmed.replace(/[\\\\/]+$/, '');\n const base = path.basename(normalized);\n return base || normalized;\n}\n\nexport function buildRuntimeDisplayName(payload: Pick<CreateSessionPayload, 'displayName' | 'providerType' | 'workspace'>): string {\n const explicit = payload.displayName?.trim();\n if (explicit) return explicit;\n const workspaceLabel = getWorkspaceLabel(payload.workspace);\n const providerLabel = payload.providerType.trim() || 'runtime';\n return `${providerLabel} @ ${workspaceLabel}`;\n}\n\nexport function buildRuntimeKey(\n payload: Pick<CreateSessionPayload, 'runtimeKey' | 'displayName' | 'providerType' | 'workspace'>,\n existingKeys: Iterable<string>,\n): string {\n const requested = payload.runtimeKey?.trim();\n const existing = new Set(Array.from(existingKeys, (key) => key.toLowerCase()));\n const displayName = buildRuntimeDisplayName(payload);\n const baseKey = normalizeSlug(requested || displayName || getWorkspaceLabel(payload.workspace) || payload.providerType || 'runtime') || 'runtime';\n if (!existing.has(baseKey)) return baseKey;\n\n let suffix = 2;\n let candidate = `${baseKey}-${suffix}`;\n while (existing.has(candidate)) {\n suffix += 1;\n candidate = `${baseKey}-${suffix}`;\n }\n return candidate;\n}\n\nexport interface SessionHostSurfaceRecordLike {\n lifecycle?: string | null;\n surfaceKind?: SessionHostSurfaceKind | null;\n meta?: Record<string, unknown> | null;\n}\n\nconst LIVE_LIFECYCLES = new Set(['starting', 'running', 'stopping', 'interrupted']);\n\nexport function isSessionHostLiveRuntime(record: SessionHostSurfaceRecordLike | null | undefined): boolean {\n if (!record) return false;\n if (record.surfaceKind === 'live_runtime') return true;\n if (record.surfaceKind === 'recovery_snapshot' || record.surfaceKind === 'inactive_record') return false;\n const lifecycle = String(record.lifecycle || '').trim();\n return LIVE_LIFECYCLES.has(lifecycle);\n}\n\nexport function getSessionHostRecoveryLabel(meta: Record<string, unknown> | null | undefined): string | null {\n const recoveryState = typeof meta?.runtimeRecoveryState === 'string'\n ? String(meta.runtimeRecoveryState).trim()\n : '';\n if (!recoveryState) return null;\n if (recoveryState === 'auto_resumed') return 'restored after restart';\n if (recoveryState === 'resume_failed') return 'restore failed';\n if (recoveryState === 'host_restart_interrupted') return 'host restart interrupted';\n if (recoveryState === 'orphan_snapshot') return 'snapshot recovered';\n return recoveryState.replace(/_/g, ' ');\n}\n\nexport function isSessionHostRecoverySnapshot(record: SessionHostSurfaceRecordLike | null | undefined): boolean {\n if (!record) return false;\n if (record.surfaceKind === 'recovery_snapshot') return true;\n if (record.surfaceKind === 'live_runtime' || record.surfaceKind === 'inactive_record') return false;\n if (isSessionHostLiveRuntime(record)) return false;\n\n const lifecycle = String(record.lifecycle || '').trim();\n if (lifecycle && lifecycle !== 'stopped' && lifecycle !== 'failed') {\n return false;\n }\n\n const meta = record.meta || undefined;\n if (meta?.restoredFromStorage === true) return true;\n return getSessionHostRecoveryLabel(meta) !== null;\n}\n\nexport function getSessionHostSurfaceKind(record: SessionHostSurfaceRecordLike | null | undefined): SessionHostSurfaceKind {\n if (record?.surfaceKind === 'live_runtime' || record?.surfaceKind === 'recovery_snapshot' || record?.surfaceKind === 'inactive_record') {\n return record.surfaceKind;\n }\n if (isSessionHostLiveRuntime(record)) return 'live_runtime';\n if (isSessionHostRecoverySnapshot(record)) return 'recovery_snapshot';\n return 'inactive_record';\n}\n\nexport function partitionSessionHostRecords<T extends SessionHostSurfaceRecordLike>(records: T[]): {\n liveRuntimes: T[];\n recoverySnapshots: T[];\n inactiveRecords: T[];\n} {\n const liveRuntimes: T[] = [];\n const recoverySnapshots: T[] = [];\n const inactiveRecords: T[] = [];\n\n for (const record of records) {\n const kind = getSessionHostSurfaceKind(record);\n if (kind === 'live_runtime') {\n liveRuntimes.push(record);\n } else if (kind === 'recovery_snapshot') {\n recoverySnapshots.push(record);\n } else {\n inactiveRecords.push(record);\n }\n }\n\n return {\n liveRuntimes,\n recoverySnapshots,\n inactiveRecords,\n };\n}\n\nexport function partitionSessionHostDiagnosticsSessions(\n records: SessionHostRecord[] | null | undefined,\n): ReturnType<typeof partitionSessionHostRecords<SessionHostRecord>> {\n return partitionSessionHostRecords(records || []);\n}\n\nexport function resolveAttachableRuntimeRecord(records: SessionHostRecord[], identifier: string): SessionHostRecord {\n const record = resolveRuntimeRecord(records, identifier);\n const surfaceKind = getSessionHostSurfaceKind(record);\n if (surfaceKind === 'live_runtime') {\n return record;\n }\n if (surfaceKind === 'recovery_snapshot') {\n throw new Error(`Runtime ${record.runtimeKey} is a recovery snapshot, not a live attach target. Resume or recover it first.`);\n }\n throw new Error(`Runtime ${record.runtimeKey} is ${record.lifecycle}, not a live attach target.`);\n}\n\nfunction uniqueMatch(records: SessionHostRecord[], predicate: (record: SessionHostRecord) => boolean): SessionHostRecord | null {\n const matches = records.filter(predicate);\n if (matches.length === 1) return matches[0] || null;\n if (matches.length === 0) return null;\n const labels = matches.map((record) => `${record.runtimeKey} (${record.sessionId})`).join(', ');\n throw new Error(`Ambiguous runtime target. Matches: ${labels}`);\n}\n\nexport function resolveRuntimeRecord(records: SessionHostRecord[], identifier: string): SessionHostRecord {\n const target = identifier.trim();\n if (!target) {\n throw new Error('Runtime target is required');\n }\n\n const exact = uniqueMatch(records, (record) =>\n record.sessionId === target ||\n normalizeValue(record.runtimeKey) === normalizeValue(target) ||\n normalizeValue(record.displayName) === normalizeValue(target),\n );\n if (exact) return exact;\n\n const prefix = uniqueMatch(records, (record) =>\n record.sessionId.startsWith(target) ||\n normalizeValue(record.runtimeKey).startsWith(normalizeValue(target)),\n );\n if (prefix) return prefix;\n\n throw new Error(`Unknown runtime target: ${target}`);\n}\n\nexport function formatRuntimeOwner(record: Pick<SessionHostRecord, 'writeOwner'>): string {\n if (!record.writeOwner) return 'none';\n return `${record.writeOwner.ownerType}:${record.writeOwner.clientId}`;\n}\n","function generateUUID(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {\n const r = Math.random() * 16 | 0;\n const v = c === 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n}\nimport type {\n AcquireWritePayload,\n AttachSessionPayload,\n CreateSessionPayload,\n DetachSessionPayload,\n ReleaseWritePayload,\n SessionAttachedClient,\n SessionHostRecord,\n SessionTermination,\n} from './types.js';\nimport { SessionRingBuffer } from './buffer.js';\nimport { resolveSessionHostCols, resolveSessionHostRows } from './defaults.js';\nimport { buildRuntimeDisplayName, buildRuntimeKey, getWorkspaceLabel } from './runtime-labels.js';\n\ninterface SessionRuntimeState {\n record: SessionHostRecord;\n buffer: SessionRingBuffer;\n}\n\nexport class SessionHostRegistry {\n private sessions = new Map<string, SessionRuntimeState>();\n\n createSession(payload: CreateSessionPayload): SessionHostRecord {\n const sessionId = payload.sessionId || generateUUID();\n if (this.sessions.has(sessionId)) {\n throw new Error(`Session already exists: ${sessionId}`);\n }\n const now = Date.now();\n const initialClient = payload.clientId\n ? [{\n clientId: payload.clientId,\n type: payload.clientType || 'daemon',\n readOnly: false,\n attachedAt: now,\n lastSeenAt: now,\n } satisfies SessionAttachedClient]\n : [];\n\n const record: SessionHostRecord = {\n sessionId,\n runtimeKey: buildRuntimeKey(\n payload,\n Array.from(this.sessions.values(), (state) => state.record.runtimeKey),\n ),\n displayName: buildRuntimeDisplayName(payload),\n workspaceLabel: getWorkspaceLabel(payload.workspace),\n transport: 'pty',\n providerType: payload.providerType,\n category: payload.category,\n workspace: payload.workspace,\n launchCommand: payload.launchCommand,\n createdAt: now,\n lastActivityAt: now,\n lifecycle: 'starting',\n writeOwner: null,\n attachedClients: initialClient,\n buffer: {\n scrollbackBytes: 0,\n snapshotSeq: 0,\n },\n meta: payload.meta || {},\n };\n\n record.meta = {\n sessionHostCols: resolveSessionHostCols(payload.cols),\n sessionHostRows: resolveSessionHostRows(payload.rows),\n ...record.meta,\n };\n\n this.sessions.set(sessionId, {\n record,\n buffer: new SessionRingBuffer(),\n });\n\n return this.cloneRecord(record);\n }\n\n restoreSession(record: SessionHostRecord, snapshot?: { seq: number; text: string } | null): SessionHostRecord {\n const cloned = this.cloneRecord(record);\n this.sessions.set(cloned.sessionId, {\n record: cloned,\n buffer: (() => {\n const buffer = new SessionRingBuffer();\n if (snapshot) buffer.restore(snapshot);\n return buffer;\n })(),\n });\n return this.cloneRecord(cloned);\n }\n\n listSessions(): SessionHostRecord[] {\n return Array.from(this.sessions.values())\n .map(state => this.cloneRecord(state.record))\n .sort((a, b) => b.lastActivityAt - a.lastActivityAt);\n }\n\n getSession(sessionId: string): SessionHostRecord | null {\n const state = this.sessions.get(sessionId);\n return state ? this.cloneRecord(state.record) : null;\n }\n\n attachClient(payload: AttachSessionPayload): SessionHostRecord {\n const state = this.requireSession(payload.sessionId);\n const now = Date.now();\n let removedDaemonOwner = false;\n\n if (payload.clientType === 'daemon') {\n const staleDaemonClientIds = state.record.attachedClients\n .filter(client => client.type === 'daemon' && client.clientId !== payload.clientId)\n .map(client => client.clientId);\n if (staleDaemonClientIds.length > 0) {\n state.record.attachedClients = state.record.attachedClients.filter(\n client => !(client.type === 'daemon' && client.clientId !== payload.clientId),\n );\n if (state.record.writeOwner && staleDaemonClientIds.includes(state.record.writeOwner.clientId)) {\n removedDaemonOwner = true;\n }\n }\n }\n\n const existing = state.record.attachedClients.find(client => client.clientId === payload.clientId);\n\n if (existing) {\n existing.type = payload.clientType;\n existing.readOnly = !!payload.readOnly;\n existing.lastSeenAt = now;\n } else {\n state.record.attachedClients.push({\n clientId: payload.clientId,\n type: payload.clientType,\n readOnly: !!payload.readOnly,\n attachedAt: now,\n lastSeenAt: now,\n });\n }\n\n if (removedDaemonOwner) {\n state.record.writeOwner = null;\n }\n\n state.record.lastActivityAt = now;\n return this.cloneRecord(state.record);\n }\n\n detachClient(payload: DetachSessionPayload): SessionHostRecord {\n const state = this.requireSession(payload.sessionId);\n state.record.attachedClients = state.record.attachedClients.filter(client => client.clientId !== payload.clientId);\n if (state.record.writeOwner?.clientId === payload.clientId) {\n state.record.writeOwner = null;\n }\n state.record.lastActivityAt = Date.now();\n return this.cloneRecord(state.record);\n }\n\n acquireWrite(payload: AcquireWritePayload): SessionHostRecord {\n const state = this.requireSession(payload.sessionId);\n if (state.record.writeOwner && state.record.writeOwner.clientId !== payload.clientId && !payload.force) {\n throw new Error(`Write owned by ${state.record.writeOwner.clientId}`);\n }\n const attachedClient = state.record.attachedClients.find(client => client.clientId === payload.clientId);\n if (attachedClient) {\n attachedClient.readOnly = false;\n attachedClient.lastSeenAt = Date.now();\n }\n state.record.writeOwner = {\n clientId: payload.clientId,\n ownerType: payload.ownerType,\n acquiredAt: Date.now(),\n };\n state.record.lastActivityAt = Date.now();\n return this.cloneRecord(state.record);\n }\n\n releaseWrite(payload: ReleaseWritePayload): SessionHostRecord {\n const state = this.requireSession(payload.sessionId);\n const attachedClient = state.record.attachedClients.find(client => client.clientId === payload.clientId);\n if (attachedClient) {\n attachedClient.readOnly = false;\n attachedClient.lastSeenAt = Date.now();\n }\n if (state.record.writeOwner?.clientId === payload.clientId) {\n state.record.writeOwner = null;\n }\n state.record.lastActivityAt = Date.now();\n return this.cloneRecord(state.record);\n }\n\n appendOutput(sessionId: string, data: string): { record: SessionHostRecord; seq: number } {\n const state = this.requireSession(sessionId);\n const seq = state.buffer.append(data);\n state.record.buffer = state.buffer.getState();\n state.record.lastActivityAt = Date.now();\n return { record: this.cloneRecord(state.record), seq };\n }\n\n getSnapshot(sessionId: string, sinceSeq?: number) {\n const state = this.requireSession(sessionId);\n state.record.buffer = state.buffer.getState();\n return state.buffer.snapshot(sinceSeq);\n }\n\n clearBuffer(sessionId: string): SessionHostRecord {\n const state = this.requireSession(sessionId);\n state.buffer.clear();\n state.record.buffer = state.buffer.getState();\n state.record.lastActivityAt = Date.now();\n return this.cloneRecord(state.record);\n }\n\n updateSessionMeta(sessionId: string, meta: Record<string, unknown>, replace = false): SessionHostRecord {\n const state = this.requireSession(sessionId);\n state.record.meta = replace\n ? { ...meta }\n : {\n ...(state.record.meta || {}),\n ...meta,\n };\n state.record.lastActivityAt = Date.now();\n return this.cloneRecord(state.record);\n }\n\n markStarted(sessionId: string, pid?: number): SessionHostRecord {\n const state = this.requireSession(sessionId);\n state.record.lifecycle = 'running';\n state.record.startedAt = state.record.startedAt || Date.now();\n if (typeof pid === 'number') state.record.osPid = pid;\n state.record.lastActivityAt = Date.now();\n return this.cloneRecord(state.record);\n }\n\n markStopped(sessionId: string, lifecycle: 'stopped' | 'failed' = 'stopped', termination?: SessionTermination): SessionHostRecord {\n const state = this.requireSession(sessionId);\n state.record.lifecycle = lifecycle;\n state.record.lastActivityAt = Date.now();\n if (termination) state.record.termination = termination;\n return this.cloneRecord(state.record);\n }\n\n setLifecycle(sessionId: string, lifecycle: 'starting' | 'running' | 'stopping' | 'stopped' | 'failed' | 'interrupted'): SessionHostRecord {\n const state = this.requireSession(sessionId);\n state.record.lifecycle = lifecycle;\n state.record.lastActivityAt = Date.now();\n return this.cloneRecord(state.record);\n }\n\n deleteSession(sessionId: string): boolean {\n return this.sessions.delete(sessionId);\n }\n\n private requireSession(sessionId: string): SessionRuntimeState {\n const state = this.sessions.get(sessionId);\n if (!state) throw new Error(`Unknown session: ${sessionId}`);\n return state;\n }\n\n private cloneRecord(record: SessionHostRecord): SessionHostRecord {\n return {\n ...record,\n launchCommand: {\n ...record.launchCommand,\n args: [...record.launchCommand.args],\n env: record.launchCommand.env ? { ...record.launchCommand.env } : undefined,\n },\n writeOwner: record.writeOwner ? { ...record.writeOwner } : null,\n attachedClients: record.attachedClients.map(client => ({ ...client })),\n buffer: { ...record.buffer },\n meta: { ...record.meta },\n };\n }\n}\n","import * as os from 'os';\nimport * as path from 'path';\nimport * as net from 'net';\nimport type {\n SessionHostEvent,\n SessionHostRequest,\n SessionHostRequestEnvelope,\n SessionHostResponse,\n SessionHostResponseEnvelope,\n SessionHostWireEnvelope,\n} from './types.js';\n\nfunction generateUUID(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {\n const r = Math.random() * 16 | 0;\n const v = c === 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n}\n\nexport interface SessionHostEndpoint {\n kind: 'unix' | 'pipe';\n path: string;\n}\n\nexport interface SessionHostEndpointOptions {\n /**\n * Instance IPC namespace key (see instance-key.ts). '' / omitted yields the\n * legacy default-instance endpoint; a non-empty key suffixes the endpoint so\n * each config-dir instance gets its own socket/pipe.\n */\n ipcKey?: string;\n /** Test seam: override the platform the endpoint shape is derived for. */\n platform?: NodeJS.Platform;\n}\n\nexport function getDefaultSessionHostEndpoint(\n appName = 'adhdev',\n options: SessionHostEndpointOptions = {},\n): SessionHostEndpoint {\n const rawKey = typeof options.ipcKey === 'string' ? options.ipcKey.trim() : '';\n // Fail closed on a malformed key: silently dropping it would collapse the\n // endpoint back onto the default instance's namespace — a cross-instance leak.\n if (rawKey && !/^[0-9a-f]{12}$/.test(rawKey)) {\n throw new Error(`Invalid session-host ipcKey \"${rawKey}\" — expected '' or a 12-hex-char instance key`);\n }\n const suffix = rawKey ? `-${rawKey}` : '';\n const platform = options.platform || process.platform;\n if (platform === 'win32') {\n return {\n kind: 'pipe',\n path: `\\\\\\\\.\\\\pipe\\\\${appName}-session-host${suffix}`,\n };\n }\n\n return {\n kind: 'unix',\n path: path.join(os.tmpdir(), `${appName}-session-host${suffix}.sock`),\n };\n}\n\nfunction serializeEnvelope(envelope: SessionHostWireEnvelope): string {\n return `${JSON.stringify(envelope)}\\n`;\n}\n\nfunction createLineParser(onEnvelope: (envelope: SessionHostWireEnvelope) => void) {\n let buffer = '';\n return (chunk: Buffer | string) => {\n buffer += chunk.toString();\n let newlineIndex = buffer.indexOf('\\n');\n while (newlineIndex >= 0) {\n const rawLine = buffer.slice(0, newlineIndex).trim();\n buffer = buffer.slice(newlineIndex + 1);\n if (rawLine) {\n onEnvelope(JSON.parse(rawLine) as SessionHostWireEnvelope);\n }\n newlineIndex = buffer.indexOf('\\n');\n }\n };\n}\n\nexport interface SessionHostClientOptions {\n endpoint?: SessionHostEndpoint;\n appName?: string;\n}\n\nexport class SessionHostClient {\n readonly endpoint: SessionHostEndpoint;\n\n private socket: net.Socket | null = null;\n private requestWaiters = new Map<string, { resolve: (value: SessionHostResponse) => void; reject: (error: Error) => void }>();\n private eventListeners = new Set<(event: SessionHostEvent) => void>();\n\n constructor(options: SessionHostClientOptions = {}) {\n this.endpoint = options.endpoint || getDefaultSessionHostEndpoint(options.appName || 'adhdev');\n }\n\n async connect(): Promise<void> {\n if (this.socket && !this.socket.destroyed) return;\n // Cleanup stale socket reference left after error/disconnect\n if (this.socket) {\n try { this.socket.destroy(); } catch { /* noop */ }\n this.socket = null;\n }\n\n const socket = net.createConnection(this.endpoint.path);\n this.socket = socket;\n\n socket.on('data', createLineParser((envelope) => {\n if (envelope.kind === 'response') {\n const waiter = this.requestWaiters.get(envelope.requestId);\n if (waiter) {\n this.requestWaiters.delete(envelope.requestId);\n waiter.resolve(envelope.response);\n }\n return;\n }\n\n if (envelope.kind === 'event') {\n for (const listener of this.eventListeners) listener(envelope.event);\n }\n }));\n\n socket.on('error', (error) => {\n for (const waiter of this.requestWaiters.values()) {\n waiter.reject(error);\n }\n this.requestWaiters.clear();\n // Clear the dead socket reference so the next connect() creates a fresh connection\n // instead of skipping reconnection on the `!this.socket.destroyed` guard.\n if (this.socket === socket) {\n this.socket = null;\n }\n try { socket.destroy(); } catch { /* noop */ }\n });\n\n await new Promise<void>((resolve, reject) => {\n socket.once('connect', () => resolve());\n socket.once('error', reject);\n });\n }\n\n onEvent(listener: (event: SessionHostEvent) => void): () => void {\n this.eventListeners.add(listener);\n return () => {\n this.eventListeners.delete(listener);\n };\n }\n\n async request<T = unknown>(request: SessionHostRequest): Promise<SessionHostResponse<T>> {\n await this.connect();\n if (!this.socket) throw new Error('Session host socket unavailable');\n\n const requestId = generateUUID();\n const envelope: SessionHostRequestEnvelope = {\n kind: 'request',\n requestId,\n request,\n };\n\n const response = await new Promise<SessionHostResponse>((resolve, reject) => {\n const timeout = setTimeout(() => {\n this.requestWaiters.delete(requestId);\n reject(new Error(`Session host request timed out after 30s (${request.type})`));\n }, 30_000);\n this.requestWaiters.set(requestId, {\n resolve: (value) => { clearTimeout(timeout); resolve(value); },\n reject: (error) => { clearTimeout(timeout); reject(error); },\n });\n this.socket?.write(serializeEnvelope(envelope));\n });\n\n return response as SessionHostResponse<T>;\n }\n\n async close(): Promise<void> {\n if (!this.socket) return;\n const socket = this.socket;\n this.socket = null;\n for (const waiter of this.requestWaiters.values()) {\n waiter.reject(new Error('Session host client closed'));\n }\n this.requestWaiters.clear();\n await new Promise<void>((resolve) => {\n let settled = false;\n const done = () => {\n if (settled) return;\n settled = true;\n resolve();\n };\n socket.once('close', done);\n socket.end();\n socket.destroy();\n setTimeout(done, 50);\n });\n }\n}\n\nexport function createResponseEnvelope(requestId: string, response: SessionHostResponse): SessionHostResponseEnvelope {\n return {\n kind: 'response',\n requestId,\n response,\n };\n}\n\nexport function writeEnvelope(socket: Pick<net.Socket, 'write'>, envelope: SessionHostWireEnvelope): void {\n socket.write(serializeEnvelope(envelope));\n}\n\nexport { createLineParser };\n","import * as crypto from 'crypto';\nimport * as fs from 'fs';\nimport * as os from 'os';\nimport * as path from 'path';\n\n/**\n * Instance identity derivation for the session-host IPC namespace.\n *\n * One machine can host several ADHDev installs (stable `~/.adhdev`, preview\n * `~/.adhdev-preview`, standalone `~/.adhdev-standalone`, or any explicit\n * ADHDEV_CONFIG_DIR). Each install is an *instance*: it owns its config dir\n * and everything mutable under it. The session-host socket/pipe must be\n * namespaced by that identity so two simultaneous instances can never attach\n * to, kill, or rebind each other's session host.\n *\n * This module is the single derivation rule, shared by:\n * - the parent daemons (via @adhdev/daemon-core's InstanceContext),\n * - the session-host-daemon child process (which only sees env vars),\n * - CLI attach/list clients.\n *\n * All three compute the endpoint from the same inputs (appName +\n * ADHDEV_CONFIG_DIR), so they always agree without extra plumbing.\n *\n * Compatibility contract: the DEFAULT instance (config dir canonicalizes to\n * `<home>/.adhdev`) yields an EMPTY ipc key, so its socket/pipe path stays\n * byte-for-byte identical to the pre-instance layout.\n */\n\nexport const DEFAULT_CONFIG_DIR_NAME = '.adhdev';\n\n/**\n * Resolve the instance config dir from the environment: ADHDEV_CONFIG_DIR\n * when set (trimmed), else `<homeDir>/.adhdev`. Mirrors daemon-core's\n * getConfigDir() source rule without the mkdir side effect.\n */\nexport function resolveInstanceConfigDir(\n env: NodeJS.ProcessEnv = process.env,\n homeDir: string = os.homedir(),\n): string {\n const override = typeof env.ADHDEV_CONFIG_DIR === 'string' ? env.ADHDEV_CONFIG_DIR.trim() : '';\n return override || path.join(homeDir, DEFAULT_CONFIG_DIR_NAME);\n}\n\n/**\n * Realpath the longest existing prefix of `resolved` and re-append the\n * non-existing remainder. Unlike a plain realpathSync, this is deterministic\n * whether or not the leaf exists yet (a config dir realpath'd only after its\n * first mkdir must not change the derived ipc key between processes).\n */\nfunction realpathLongestExistingPrefix(resolved: string): string {\n let current = resolved;\n const remainder: string[] = [];\n // Bounded walk: a path can have at most its own segment count of parents.\n for (let depth = 0; depth < 256; depth++) {\n try {\n const real = fs.realpathSync.native(current);\n return remainder.length === 0 ? real : path.join(real, ...remainder);\n } catch {\n const parent = path.dirname(current);\n if (parent === current) return resolved;\n remainder.unshift(path.basename(current));\n current = parent;\n }\n }\n return resolved;\n}\n\n/**\n * Canonical form of an instance path for identity comparison/hashing:\n * absolute, symlink-resolved (longest existing prefix), no trailing\n * separator, case-folded on win32.\n */\nexport function canonicalizeInstancePath(\n dir: string,\n platform: NodeJS.Platform = process.platform,\n): string {\n const resolved = path.resolve(dir);\n const real = realpathLongestExistingPrefix(resolved).replace(/[\\\\/]+$/, '');\n return platform === 'win32' ? real.toLowerCase() : real;\n}\n\n/** True when `configDir` is canonically the default `<homeDir>/.adhdev`. */\nexport function isDefaultInstanceConfigDir(\n configDir: string,\n homeDir: string = os.homedir(),\n platform: NodeJS.Platform = process.platform,\n): boolean {\n return canonicalizeInstancePath(configDir, platform)\n === canonicalizeInstancePath(path.join(homeDir, DEFAULT_CONFIG_DIR_NAME), platform);\n}\n\n/**\n * Derive the session-host IPC namespace key for an instance config dir.\n *\n * '' for the default instance (legacy-compatible endpoint), otherwise a\n * 12-hex-char sha256 prefix of the canonical config dir — stable across\n * processes/machines for the same dir, collision-resistant between dirs,\n * and insensitive to symlink/spelling differences of the same dir.\n */\nexport function resolveSessionHostIpcKey(\n configDir: string,\n homeDir: string = os.homedir(),\n platform: NodeJS.Platform = process.platform,\n): string {\n if (isDefaultInstanceConfigDir(configDir, homeDir, platform)) return '';\n const canonical = canonicalizeInstancePath(configDir, platform);\n return crypto.createHash('sha256').update(canonical, 'utf8').digest('hex').slice(0, 12);\n}\n","/**\n * Shared PTY spawn environment utilities.\n *\n * Centralises npm/pnpm/yarn env variable stripping, terminal colour env\n * injection, and node-pty spawn-helper permission fixing.\n *\n * Used by daemon-core (provider-cli-adapter), session-host-daemon (runtime),\n * and daemon-cloud (session-host).\n */\n\nimport * as os from 'os';\nimport * as path from 'path';\n\n/**\n * Strip package-manager injected environment variables that can interfere\n * with child CLI processes and apply terminal colour defaults.\n */\nexport function sanitizeSpawnEnv(\n baseEnv: NodeJS.ProcessEnv,\n overrides?: Record<string, string>,\n): Record<string, string> {\n const env: Record<string, string> = {};\n const source = { ...baseEnv, ...(overrides || {}) } as NodeJS.ProcessEnv;\n\n for (const [key, value] of Object.entries(source)) {\n if (typeof value !== 'string') continue;\n env[key] = value;\n }\n\n for (const key of Object.keys(env)) {\n if (\n key === 'INIT_CWD'\n || key === 'npm_command'\n || key === 'npm_execpath'\n || key === 'npm_node_execpath'\n || key.startsWith('npm_')\n || key.startsWith('npm_config_')\n || key.startsWith('npm_package_')\n || key.startsWith('npm_lifecycle_')\n || key.startsWith('PNPM_')\n || key.startsWith('YARN_')\n || key.startsWith('BUN_')\n || key.startsWith('VSCODE_')\n || key.startsWith('ELECTRON_')\n ) {\n delete env[key];\n }\n }\n\n // Do not leak parent Codex session controls into child CLIs. Thread identity\n // breaks providerSessionId discovery/reconnect semantics, while the parent's\n // network-disabled sandbox flag prevents coordinator MCP children from\n // connecting back to the local ADHDev daemon.\n delete env.CODEX_THREAD_ID;\n delete env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE;\n delete env.CODEX_SANDBOX_NETWORK_DISABLED;\n delete env.NO_COLOR;\n delete env.COLOR;\n\n // Do not leak a parent Claude Code session identity into spawned claude-cli\n // children. CLAUDE_CODE_CHILD_SESSION makes a spawned claude run as a nested\n // child that does NOT persist its ~/.claude/projects transcript, so the\n // native-source history reader finds no file and the live dashboard renders\n // empty. This bites when the daemon itself was launched from inside a Claude\n // Code session (the markers are inherited and forwarded to every child).\n // Scoped to win32 for now: the macOS/Linux path works today and we want to\n // keep the blast radius small. Safe to make unconditional later — a\n // daemon-spawned claude should always be a fresh top-level session.\n if (process.platform === 'win32') {\n delete env.CLAUDECODE;\n delete env.CLAUDE_CODE_CHILD_SESSION;\n delete env.CLAUDE_CODE_ENTRYPOINT;\n delete env.CLAUDE_CODE_SESSION_ID;\n delete env.CLAUDE_CODE_EXECPATH;\n }\n\n applyTerminalColorEnv(env);\n return env;\n}\n\n/**\n * Apply preferred terminal colour environment variables.\n * Ensures TERM is set to xterm-256color and enables colour on Windows.\n */\nexport function applyTerminalColorEnv(env: Record<string, string>): void {\n if (env.NO_COLOR) return;\n\n if (!env.TERM || env.TERM === 'xterm-color') {\n env.TERM = 'xterm-256color';\n }\n if (!env.COLORTERM) env.COLORTERM = 'truecolor';\n\n if (process.platform === 'win32') {\n if (!env.FORCE_COLOR) env.FORCE_COLOR = '1';\n if (!env.CLICOLOR) env.CLICOLOR = '1';\n }\n}\n\n/**\n * Ensure node-pty's spawn-helper binary has execute permissions.\n *\n * npm's default umask can strip +x from the prebuilt spawn-helper on macOS/Linux,\n * causing EACCES when node-pty tries to fork. Best-effort fix.\n *\n * @param logFn Optional log callback for reporting the fix.\n */\nexport function ensureNodePtySpawnHelperPermissions(\n logFn?: (msg: string) => void,\n): void {\n if (os.platform() === 'win32') return;\n try {\n const fs = require('fs');\n const ptyDir = path.resolve(path.dirname(require.resolve('node-pty')), '..');\n const platformArch = `${os.platform()}-${os.arch()}`;\n const helper = path.join(ptyDir, 'prebuilds', platformArch, 'spawn-helper');\n if (fs.existsSync(helper)) {\n const stat = fs.statSync(helper);\n if (!(stat.mode & 0o111)) {\n fs.chmodSync(helper, stat.mode | 0o755);\n logFn?.(`Fixed spawn-helper permissions: ${helper}`);\n }\n }\n } catch {\n // best-effort: node-pty still works on most installs without this\n }\n}\n","import type {\n AcquireWritePayload,\n GetHostDiagnosticsPayload,\n PruneDuplicateSessionsPayload,\n ReleaseWritePayload,\n SessionHostDiagnostics,\n SessionHostPruneDuplicatesResult,\n SessionHostRecord,\n SessionHostRequestType,\n} from './types.js';\n\n/**\n * The 11-method session-host control-plane surface shared by the cloud and\n * standalone daemons. Both daemons used to carry a byte-for-byte copy of this\n * dispatch table plus the identical throw strings; this is the single source of\n * truth for that mapping. The wire `type` strings and error text here are the\n * daemon IPC contract and MUST match what the session-host daemon accepts.\n */\nexport interface SessionHostControlPlane {\n getDiagnostics(payload?: GetHostDiagnosticsPayload): Promise<SessionHostDiagnostics>;\n listSessions(): Promise<SessionHostRecord[]>;\n stopSession(sessionId: string): Promise<SessionHostRecord | null>;\n deleteSession(sessionId: string, opts?: { force?: boolean }): Promise<SessionHostRecord | null>;\n resumeSession(sessionId: string): Promise<SessionHostRecord | null>;\n restartSession(sessionId: string): Promise<SessionHostRecord | null>;\n sendSignal(sessionId: string, signal: string): Promise<SessionHostRecord | null>;\n forceDetachClient(sessionId: string, clientId: string): Promise<SessionHostRecord | null>;\n pruneDuplicateSessions(payload?: PruneDuplicateSessionsPayload): Promise<SessionHostPruneDuplicatesResult>;\n acquireWrite(payload: AcquireWritePayload): Promise<SessionHostRecord | null>;\n releaseWrite(payload: ReleaseWritePayload): Promise<SessionHostRecord | null>;\n}\n\n/**\n * Minimal request transport the control-plane factory needs. Each daemon injects\n * its own implementation:\n * - cloud reuses a single persistent SessionHostClient (with reconnect); its\n * `request` calls `ensureConnected()` then the shared client.\n * - standalone opens a fresh client per request and closes it in `finally`.\n *\n * The factory below owns ONLY the type/payload dispatch table; the connection\n * lifecycle and success/error unwrapping stay in each daemon's transport so the\n * observable behavior of both paths is preserved exactly.\n */\nexport interface SessionHostControlTransport {\n request<T>(type: SessionHostRequestType, payload: Record<string, unknown>): Promise<T>;\n}\n\n/**\n * Build the shared 11-method control-plane over an injected request transport.\n * The (type string, payload shape) pairs are verbatim what both daemons emitted\n * previously — do not reword them without matching the session-host daemon's\n * request handlers.\n */\nexport function createSessionHostControlPlane(\n transport: SessionHostControlTransport,\n): SessionHostControlPlane {\n return {\n getDiagnostics(payload: GetHostDiagnosticsPayload = {}): Promise<SessionHostDiagnostics> {\n return transport.request<SessionHostDiagnostics>('get_host_diagnostics', payload as Record<string, unknown>);\n },\n listSessions(): Promise<SessionHostRecord[]> {\n return transport.request<SessionHostRecord[]>('list_sessions', {});\n },\n stopSession(sessionId: string): Promise<SessionHostRecord | null> {\n return transport.request<SessionHostRecord | null>('stop_session', { sessionId });\n },\n deleteSession(sessionId: string, opts: { force?: boolean } = {}): Promise<SessionHostRecord | null> {\n return transport.request<SessionHostRecord | null>('delete_session', { sessionId, force: opts.force === true });\n },\n resumeSession(sessionId: string): Promise<SessionHostRecord | null> {\n return transport.request<SessionHostRecord | null>('resume_session', { sessionId });\n },\n restartSession(sessionId: string): Promise<SessionHostRecord | null> {\n return transport.request<SessionHostRecord | null>('restart_session', { sessionId });\n },\n sendSignal(sessionId: string, signal: string): Promise<SessionHostRecord | null> {\n return transport.request<SessionHostRecord | null>('send_signal', { sessionId, signal });\n },\n forceDetachClient(sessionId: string, clientId: string): Promise<SessionHostRecord | null> {\n return transport.request<SessionHostRecord | null>('force_detach_client', { sessionId, clientId });\n },\n pruneDuplicateSessions(payload: PruneDuplicateSessionsPayload = {}): Promise<SessionHostPruneDuplicatesResult> {\n return transport.request<SessionHostPruneDuplicatesResult>('prune_duplicate_sessions', payload as Record<string, unknown>);\n },\n acquireWrite(payload: AcquireWritePayload): Promise<SessionHostRecord | null> {\n return transport.request<SessionHostRecord | null>('acquire_write', payload as unknown as Record<string, unknown>);\n },\n releaseWrite(payload: ReleaseWritePayload): Promise<SessionHostRecord | null> {\n return transport.request<SessionHostRecord | null>('release_write', payload as unknown as Record<string, unknown>);\n },\n };\n}\n"],"mappings":"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","names":["path","generateUUID","platform","resolve","crypto","os","path","platform","os","path","fs"]}
|
package/dist/index.mjs
CHANGED
|
@@ -519,16 +519,22 @@ function generateUUID2() {
|
|
|
519
519
|
return v.toString(16);
|
|
520
520
|
});
|
|
521
521
|
}
|
|
522
|
-
function getDefaultSessionHostEndpoint(appName = "adhdev") {
|
|
523
|
-
|
|
522
|
+
function getDefaultSessionHostEndpoint(appName = "adhdev", options = {}) {
|
|
523
|
+
const rawKey = typeof options.ipcKey === "string" ? options.ipcKey.trim() : "";
|
|
524
|
+
if (rawKey && !/^[0-9a-f]{12}$/.test(rawKey)) {
|
|
525
|
+
throw new Error(`Invalid session-host ipcKey "${rawKey}" \u2014 expected '' or a 12-hex-char instance key`);
|
|
526
|
+
}
|
|
527
|
+
const suffix = rawKey ? `-${rawKey}` : "";
|
|
528
|
+
const platform2 = options.platform || process.platform;
|
|
529
|
+
if (platform2 === "win32") {
|
|
524
530
|
return {
|
|
525
531
|
kind: "pipe",
|
|
526
|
-
path: `\\\\.\\pipe\\${appName}-session-host`
|
|
532
|
+
path: `\\\\.\\pipe\\${appName}-session-host${suffix}`
|
|
527
533
|
};
|
|
528
534
|
}
|
|
529
535
|
return {
|
|
530
536
|
kind: "unix",
|
|
531
|
-
path: path2.join(os.tmpdir(), `${appName}-session-host.sock`)
|
|
537
|
+
path: path2.join(os.tmpdir(), `${appName}-session-host${suffix}.sock`)
|
|
532
538
|
};
|
|
533
539
|
}
|
|
534
540
|
function serializeEnvelope(envelope) {
|
|
@@ -595,8 +601,8 @@ var SessionHostClient = class {
|
|
|
595
601
|
} catch {
|
|
596
602
|
}
|
|
597
603
|
});
|
|
598
|
-
await new Promise((
|
|
599
|
-
socket.once("connect", () =>
|
|
604
|
+
await new Promise((resolve3, reject) => {
|
|
605
|
+
socket.once("connect", () => resolve3());
|
|
600
606
|
socket.once("error", reject);
|
|
601
607
|
});
|
|
602
608
|
}
|
|
@@ -615,7 +621,7 @@ var SessionHostClient = class {
|
|
|
615
621
|
requestId,
|
|
616
622
|
request
|
|
617
623
|
};
|
|
618
|
-
const response = await new Promise((
|
|
624
|
+
const response = await new Promise((resolve3, reject) => {
|
|
619
625
|
const timeout = setTimeout(() => {
|
|
620
626
|
this.requestWaiters.delete(requestId);
|
|
621
627
|
reject(new Error(`Session host request timed out after 30s (${request.type})`));
|
|
@@ -623,7 +629,7 @@ var SessionHostClient = class {
|
|
|
623
629
|
this.requestWaiters.set(requestId, {
|
|
624
630
|
resolve: (value) => {
|
|
625
631
|
clearTimeout(timeout);
|
|
626
|
-
|
|
632
|
+
resolve3(value);
|
|
627
633
|
},
|
|
628
634
|
reject: (error) => {
|
|
629
635
|
clearTimeout(timeout);
|
|
@@ -642,12 +648,12 @@ var SessionHostClient = class {
|
|
|
642
648
|
waiter.reject(new Error("Session host client closed"));
|
|
643
649
|
}
|
|
644
650
|
this.requestWaiters.clear();
|
|
645
|
-
await new Promise((
|
|
651
|
+
await new Promise((resolve3) => {
|
|
646
652
|
let settled = false;
|
|
647
653
|
const done = () => {
|
|
648
654
|
if (settled) return;
|
|
649
655
|
settled = true;
|
|
650
|
-
|
|
656
|
+
resolve3();
|
|
651
657
|
};
|
|
652
658
|
socket.once("close", done);
|
|
653
659
|
socket.end();
|
|
@@ -667,9 +673,49 @@ function writeEnvelope(socket, envelope) {
|
|
|
667
673
|
socket.write(serializeEnvelope(envelope));
|
|
668
674
|
}
|
|
669
675
|
|
|
670
|
-
// src/
|
|
676
|
+
// src/instance-key.ts
|
|
677
|
+
import * as crypto2 from "crypto";
|
|
678
|
+
import * as fs from "fs";
|
|
671
679
|
import * as os2 from "os";
|
|
672
680
|
import * as path3 from "path";
|
|
681
|
+
var DEFAULT_CONFIG_DIR_NAME = ".adhdev";
|
|
682
|
+
function resolveInstanceConfigDir(env = process.env, homeDir = os2.homedir()) {
|
|
683
|
+
const override = typeof env.ADHDEV_CONFIG_DIR === "string" ? env.ADHDEV_CONFIG_DIR.trim() : "";
|
|
684
|
+
return override || path3.join(homeDir, DEFAULT_CONFIG_DIR_NAME);
|
|
685
|
+
}
|
|
686
|
+
function realpathLongestExistingPrefix(resolved) {
|
|
687
|
+
let current = resolved;
|
|
688
|
+
const remainder = [];
|
|
689
|
+
for (let depth = 0; depth < 256; depth++) {
|
|
690
|
+
try {
|
|
691
|
+
const real = fs.realpathSync.native(current);
|
|
692
|
+
return remainder.length === 0 ? real : path3.join(real, ...remainder);
|
|
693
|
+
} catch {
|
|
694
|
+
const parent = path3.dirname(current);
|
|
695
|
+
if (parent === current) return resolved;
|
|
696
|
+
remainder.unshift(path3.basename(current));
|
|
697
|
+
current = parent;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
return resolved;
|
|
701
|
+
}
|
|
702
|
+
function canonicalizeInstancePath(dir, platform2 = process.platform) {
|
|
703
|
+
const resolved = path3.resolve(dir);
|
|
704
|
+
const real = realpathLongestExistingPrefix(resolved).replace(/[\\/]+$/, "");
|
|
705
|
+
return platform2 === "win32" ? real.toLowerCase() : real;
|
|
706
|
+
}
|
|
707
|
+
function isDefaultInstanceConfigDir(configDir, homeDir = os2.homedir(), platform2 = process.platform) {
|
|
708
|
+
return canonicalizeInstancePath(configDir, platform2) === canonicalizeInstancePath(path3.join(homeDir, DEFAULT_CONFIG_DIR_NAME), platform2);
|
|
709
|
+
}
|
|
710
|
+
function resolveSessionHostIpcKey(configDir, homeDir = os2.homedir(), platform2 = process.platform) {
|
|
711
|
+
if (isDefaultInstanceConfigDir(configDir, homeDir, platform2)) return "";
|
|
712
|
+
const canonical = canonicalizeInstancePath(configDir, platform2);
|
|
713
|
+
return crypto2.createHash("sha256").update(canonical, "utf8").digest("hex").slice(0, 12);
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
// src/spawn-env.ts
|
|
717
|
+
import * as os3 from "os";
|
|
718
|
+
import * as path4 from "path";
|
|
673
719
|
function sanitizeSpawnEnv(baseEnv, overrides) {
|
|
674
720
|
const env = {};
|
|
675
721
|
const source = { ...baseEnv, ...overrides || {} };
|
|
@@ -709,16 +755,16 @@ function applyTerminalColorEnv(env) {
|
|
|
709
755
|
}
|
|
710
756
|
}
|
|
711
757
|
function ensureNodePtySpawnHelperPermissions(logFn) {
|
|
712
|
-
if (
|
|
758
|
+
if (os3.platform() === "win32") return;
|
|
713
759
|
try {
|
|
714
|
-
const
|
|
715
|
-
const ptyDir =
|
|
716
|
-
const platformArch = `${
|
|
717
|
-
const helper =
|
|
718
|
-
if (
|
|
719
|
-
const stat =
|
|
760
|
+
const fs2 = __require("fs");
|
|
761
|
+
const ptyDir = path4.resolve(path4.dirname(__require.resolve("node-pty")), "..");
|
|
762
|
+
const platformArch = `${os3.platform()}-${os3.arch()}`;
|
|
763
|
+
const helper = path4.join(ptyDir, "prebuilds", platformArch, "spawn-helper");
|
|
764
|
+
if (fs2.existsSync(helper)) {
|
|
765
|
+
const stat = fs2.statSync(helper);
|
|
720
766
|
if (!(stat.mode & 73)) {
|
|
721
|
-
|
|
767
|
+
fs2.chmodSync(helper, stat.mode | 493);
|
|
722
768
|
logFn?.(`Fixed spawn-helper permissions: ${helper}`);
|
|
723
769
|
}
|
|
724
770
|
}
|
|
@@ -765,6 +811,7 @@ function createSessionHostControlPlane(transport) {
|
|
|
765
811
|
};
|
|
766
812
|
}
|
|
767
813
|
export {
|
|
814
|
+
DEFAULT_CONFIG_DIR_NAME,
|
|
768
815
|
DEFAULT_SESSION_HOST_COLS,
|
|
769
816
|
DEFAULT_SESSION_HOST_ROWS,
|
|
770
817
|
DEFAULT_SESSION_RING_BUFFER_MAX_BYTES,
|
|
@@ -775,6 +822,7 @@ export {
|
|
|
775
822
|
applyTerminalColorEnv,
|
|
776
823
|
buildRuntimeDisplayName,
|
|
777
824
|
buildRuntimeKey,
|
|
825
|
+
canonicalizeInstancePath,
|
|
778
826
|
classifyTermination,
|
|
779
827
|
createLineParser,
|
|
780
828
|
createResponseEnvelope,
|
|
@@ -785,13 +833,16 @@ export {
|
|
|
785
833
|
getSessionHostRecoveryLabel,
|
|
786
834
|
getSessionHostSurfaceKind,
|
|
787
835
|
getWorkspaceLabel,
|
|
836
|
+
isDefaultInstanceConfigDir,
|
|
788
837
|
isSessionHostLiveRuntime,
|
|
789
838
|
isSessionHostRecoverySnapshot,
|
|
790
839
|
partitionSessionHostDiagnosticsSessions,
|
|
791
840
|
partitionSessionHostRecords,
|
|
792
841
|
resolveAttachableRuntimeRecord,
|
|
842
|
+
resolveInstanceConfigDir,
|
|
793
843
|
resolveRuntimeRecord,
|
|
794
844
|
resolveSessionHostCols,
|
|
845
|
+
resolveSessionHostIpcKey,
|
|
795
846
|
resolveSessionHostRows,
|
|
796
847
|
sanitizeSpawnEnv,
|
|
797
848
|
writeEnvelope
|