import { EventEmitter } from "node:events"; import { WandStorage } from "./storage.js"; import { ExecutionMode, ProcessEventHandler, SessionProvider, SessionSnapshot, SessionSource, WandConfig } from "./types.js"; import { type PermissionResolution } from "./claude-pty-bridge.js"; import { type PtyTerminalSnapshot } from "./pty-terminal-state.js"; import { type TerminalHost } from "./terminal-host.js"; import { type ClaudeHistorySession, type CodexHistorySession, type OpenCodeHistorySession, type QoderHistorySession } from "./provider-history-scanner.js"; export type { ClaudeHistorySession, CodexHistorySession, OpenCodeHistorySession, QoderHistorySession, } from "./provider-history-scanner.js"; /** Exported for focused policy tests. */ export declare function isCommandAllowedByPrefixes(command: string, allowedPrefixes: readonly string[]): boolean; export type { ProcessEvent, ProcessEventHandler } from "./types.js"; export declare class SessionInputError extends Error { readonly code: "SESSION_NOT_FOUND" | "SESSION_NOT_RUNNING" | "SESSION_NO_PTY"; readonly sessionId: string; readonly sessionStatus?: SessionSnapshot["status"] | undefined; constructor(message: string, code: "SESSION_NOT_FOUND" | "SESSION_NOT_RUNNING" | "SESSION_NO_PTY", sessionId: string, sessionStatus?: SessionSnapshot["status"] | undefined); } export declare class ProcessManager extends EventEmitter { private readonly config; private readonly storage; private readonly sessions; private readonly logger; private readonly providerHistory; /** 24h archive scan timer */ private archiveTimer; /** Per-session debounce timers for throttled persist calls */ private readonly persistDebounceTimers; /** Last persisted message state per session — used to skip redundant message writes */ private readonly lastPersistedMessageState; /** Columns that changed since the last per-session checkpoint. */ private readonly dirtySessions; /** 启动时被识别为孤儿 PTY 并标记为 exited 的旧会话数(旧服务器进程已死) */ private orphanRecoveredCount; private readonly topicCoordinator; private disposed; private readonly terminalHost; constructor(config: WandConfig, storage: WandStorage, configDir?: string, terminalHost?: TerminalHost); private makeRestoredRecord; private bindRestoredTerminal; private initializeClaudeBridge; private bindTerminalProcess; private handleTerminalExit; private handleTerminalData; on(_event: "process", listener: ProcessEventHandler): this; /** 启动时被识别为孤儿 PTY 并标记为 exited 的旧会话数量(仅用于启动摘要展示)。 */ getOrphanRecoveredCount(): number; /** Stop all live work and flush pending state before storage is closed. */ dispose(): void; private emitEvent; private cleanupOldSessions; start(command: string, cwd: string | undefined, mode: ExecutionMode, initialInput?: string, opts?: { resumedFromSessionId?: string; autoRecovered?: boolean; worktreeEnabled?: boolean; provider?: SessionProvider; model?: string; reuseId?: string; cols?: number; rows?: number; thinkingEffort?: SessionSnapshot["thinkingEffort"]; sessionSource?: SessionSource; automationId?: string; workspaceId?: string; workspaceTaskId?: string; interactiveShell?: boolean; }): Promise; list(): SessionSnapshot[]; /** Return lightweight snapshots for the session list (no output/messages). */ listSlim(): SessionSnapshot[]; hasClaudeSessionFile(cwd: string, claudeSessionId: string): boolean; listClaudeHistorySessions(): ClaudeHistorySession[]; deleteClaudeHistoryFiles(sessions: { claudeSessionId: string; cwd: string; }[]): number; listCodexHistorySessions(): CodexHistorySession[]; hasCodexSessionFile(threadId: string): boolean; deleteCodexHistoryFiles(threadIds: string[]): number; listOpenCodeHistorySessions(): OpenCodeHistorySession[]; deleteOpenCodeHistorySessions(sessionIds: string[]): number; listQoderHistorySessions(): QoderHistorySession[]; deleteQoderHistoryFiles(sessionIds: string[]): number; private captureCodexSessionId; private captureOpenCodeSessionId; private captureClaudeSessionId; get(id: string): SessionSnapshot | null; /** Return only a session owned by this manager, without the SQLite fallback used by get(). */ getOwned(id: string): SessionSnapshot | null; getPtyTranscript(id: string): string | null; /** * Set the Claude model for an existing PTY session. Persists the selection * and, when the session is live, pipes a `/model ` slash command into * the PTY so Claude Code switches on the fly. */ setSessionModel(id: string, model: string | null): SessionSnapshot; /** * Set the thinking-effort level for a PTY session. Interactive Claude supports * this through /effort; off maps to auto, which restores the model default. */ setSessionThinkingEffort(id: string, effort: SessionSnapshot["thinkingEffort"]): SessionSnapshot; /** * Switch the execution mode of a PTY session mid-flight. The already-launched * provider process keeps its original CLI flags, but wand's own permission * auto-approval (shouldAutoApprovePermissions / escalation handling) reads * record.mode, so this changes the permission posture for subsequent prompts. * Mirrors setSessionModel/setSessionThinkingEffort. */ setSessionMode(id: string, mode: ExecutionMode): SessionSnapshot; sendInput(id: string, input: string, view?: "chat" | "terminal", shortcutKey?: string, trackUserInput?: boolean): SessionSnapshot; resize(id: string, cols: number, rows: number): SessionSnapshot; getTerminalState(id: string): PtyTerminalSnapshot | null; pauseOutput(id: string): void; resumeOutput(id: string): void; /** * Finalize provider-only state while leaving the owning PTY shell alive. The * session itself remains running so terminal input can continue normally. */ private finishProviderCli; stop(id: string): SessionSnapshot; private cleanupRecord; delete(id: string): void; private deleteClaudeCache; runStartupCommands(): Promise; private snapshot; /** Lightweight snapshot for list views — omits output and messages. */ private snapshotSlim; private isPermissionBlocked; setSessionTopic(id: string, title: string, description: string): SessionSnapshot; private setSessionTopicGenerating; /** * Persist worktree merge progress through the manager that owns the live * session record. Returning null lets callers fall back to another owner (or * directly to storage for a row that is not currently loaded by a manager). */ setWorktreeMergeState(id: string, status: SessionSnapshot["worktreeMergeStatus"], info: SessionSnapshot["worktreeMergeInfo"]): SessionSnapshot | null; private maybeGenerateSessionTopic; private defaultAutonomyPolicy; resolveEscalation(id: string, requestId: string, resolution?: PermissionResolution): SessionSnapshot; approvePermission(id: string): SessionSnapshot; denyPermission(id: string): SessionSnapshot; toggleAutoApprove(id: string): SessionSnapshot; /** * Canonical permission resolution method. * All other permission methods delegate to this. * @param resolution - "approve_once", "approve_turn", or "deny" * @param requestId - Optional escalation request ID for validation */ resolvePermission(id: string, resolution: PermissionResolution, requestId?: string): SessionSnapshot; private persist; private markDirty; /** * Schedule a debounced persist call for the given record. * Multiple calls within the debounce window are coalesced into a single write. * Use this in hot paths (e.g. onData) to reduce I/O pressure. */ private schedulePersist; /** * Immediately persist any pending debounced write and clear the timer. * Use this at critical points (exit, stop, delete) to ensure no data loss. */ private flushPersist; private archiveExpiredSessions; private assertCommandAllowed; /** * @deprecated Only retained for non-Claude-CLI sessions without ptyBridge. * For Claude CLI sessions, auto-approval is handled by ClaudePtyBridge.detectPermission(). */ private autoConfirmWithRecord; /** * Handle events from ClaudePtyBridge */ private handleBridgeEvent; private mustGet; /** Start a bare interactive login shell without a provider CLI. */ startShell(cwd: string | undefined, mode: ExecutionMode, opts?: { worktreeEnabled?: boolean; cols?: number; rows?: number; sessionSource?: SessionSource; automationId?: string; workspaceId?: string; workspaceTaskId?: string; }): Promise; private shouldAutoApprovePermissions; private processCommandForMode; }