import { type ChildProcess, type SpawnOptions } from 'node:child_process'; import { type ChatHistoryEntry, type ChatMode, type ChatProcessInvocation, type ChatRuntimeId, type ImageAttachment } from './chat-runtime-adapters.js'; export declare function buildSandboxEnv(): NodeJS.ProcessEnv; /** * Shared spawn options for every chat process. Centralized so the three spawn * sites (initial spawn, resume, compacted respawn) stay in lock-step: a process * runs IN the workspace (`cwd`) but reads/writes its CLI config and cache under * the real user HOME via the sandbox env (see buildSandboxEnv). Drift between * sites is exactly what reintroduced GH-371/GH-399, so they must share this. */ export declare function chatSpawnOptions(workspace: string, sessionId: string): SpawnOptions; export type ChatSession = { process: ChildProcess; lastActivity: Date; workspace: string; runtime: ChatRuntimeId; providerSessionId: string | null; model?: string; history: ChatHistoryEntry[]; outputBuffer: string; conversationId?: string; stoppedByUser?: boolean; pendingCompaction?: { reason: 'proactive' | 'reactive'; summary: string; } | null; delegationHonestyWarning?: boolean; }; export type { ChatHistoryEntry, ChatProcessInvocation }; /** * Server-originated entry point for auto-resume (chat-auto-resume.ts). If the * session's turn is live, the wake is queued (delivered later by * flushPendingWake, called from the close handler of whichever turn is * currently in flight) instead of being dropped. Returns `undefined` when the * session is not known at all — there is nothing to resume into. */ export declare function queueOrStartWake(sessionId: string, wake: { message: string; conversationId?: string; }, onStarted?: (session: ChatSession) => void): { queued: boolean; } | undefined; /** Test-only visibility into the wake queue depth for a session. */ export declare function getPendingWakeCount(sessionId: string): number; export declare function hasActiveDelegatedSpawn(chatSessionId: string): boolean; export declare function generateSessionId(): string; export declare function buildChatSpawnInvocation(runtime: ChatRuntimeId, prompt: string, providerSessionId: string, model?: string, chatMode?: ChatMode): ChatProcessInvocation; export declare function buildResumeChatInvocation(runtime: ChatRuntimeId, providerSessionId: string | null, history: ChatHistoryEntry[], message: string, model?: string, images?: ImageAttachment[]): ChatProcessInvocation; export declare function spawnChat(sessionId: string, message: string, workspace: string, runtime?: ChatRuntimeId, context?: string, model?: string, chatMode?: ChatMode, images?: ImageAttachment[], conversationId?: string): ChatSession; /** * Retries a resume whose `--resume ` target no longer exists — * the underlying provider session transcript was pruned, deleted, or otherwise lost * after the conversation was saved (e.g. a restored conversation loaded after a * server restart, whose saved providerSessionId points at a transcript the CLI's * own retention has since removed). Respawns via the SAME history-replay fallback * `buildResumeInvocation` already uses when no providerSessionId was ever captured, * instead of leaving the caller to surface the raw CLI error ("No conversation * found with session ID: ...") to the user. * * Does NOT push a new history entry — the failed attempt's user message is already * the last entry in `existing.history` (resumeSession pushed it before spawning). * Returns undefined when there is no session, or no user message to retry. */ export declare function respawnResumeAfterLostTarget(sessionId: string, images?: ImageAttachment[]): ChatSession | undefined; /** * Reactive context-overflow recovery. Mints a FRESH provider session and * respawns the turn with a compacted prompt (summary + latest message) instead * of the full conversation, so a "Prompt is too long" failure is recovered * transparently rather than surfaced as a dead error. * * The caller (stream driver) assembles `prompt` from the compacted history + * rebuilt context. We reuse the normal spawn path (new provider session id), * which is exactly what GH-SA-405 persists/recovers — the new id is captured on * the session and the next save/restore carries it forward. * * Returns the respawned session, or undefined when the session is gone. */ export declare function respawnCompacted(sessionId: string, prompt: string, latestUserMessage: string, model?: string, chatMode?: ChatMode): ChatSession | undefined; export declare function getSession(sessionId: string): ChatSession | undefined; export declare function getSessionHistory(sessionId: string): ChatHistoryEntry[]; export declare function getProviderSessionId(sessionId: string): string | null; /** * Registers a session restored from persisted history WITHOUT spawning a process, * so the next /send routes through the existing resume path in spawnChat. * * Approach (chosen for lowest risk — see GH-SA-271 / GH-SA-391): * spawnChat only reads `existing.process.exitCode` to decide between resume and * respawn, and resumeSession only reads `existing.process.exitCode`. We therefore * store a minimal stub standing in for the (already-finished) ChildProcess with * `exitCode: 0`. exitCode 0 means "clean exit", so resumeSession keeps the * providerSessionId (no signal-kill ⇒ no history-replay fallback) and the * Claude adapter issues `--resume `. We do NOT touch the * internal branching of spawnChat/resumeSession. */ export declare function seedResumeSession(sessionId: string, workspace: string, runtime: ChatRuntimeId, providerSessionId: string | null, history: ChatHistoryEntry[]): void; /** * Records that the next stream for this session should announce a compaction. * The stream driver reads and clears this so the `context-compacted` event is * emitted exactly once per compacted turn. */ export declare function markCompaction(sessionId: string, reason: 'proactive' | 'reactive', summary: string): void; /** Reads and clears the pending compaction marker for a session. */ export declare function consumeCompaction(sessionId: string): { reason: 'proactive' | 'reactive'; summary: string; } | null; export declare function touchSession(sessionId: string): void; export declare function kill(sessionId: string): boolean; /** * Stops the in-flight turn for a session WITHOUT removing it from the map. * * Differs from kill()/end(): kill() SIGTERMs the process AND deletes the session, * tearing down history and the providerSessionId so the conversation cannot resume. * stopTurn() only terminates the live process — the session entry (history, * providerSessionId, runtime, workspace) stays alive so the next /send resumes the * same conversation, exactly like ChatGPT/Claude's Stop button. * * The `stoppedByUser` marker tells the stream driver's close handler to emit a clean * terminal `done` (stopped:true) for the SIGTERM exit (code 143) instead of an error. * * Returns false when there is no session or its process is not live (nothing to stop). */ export declare function stopTurn(sessionId: string): boolean; export declare function cleanup(): void; export declare function getActiveSessionCount(): number;