import { spawnSync } from 'child_process'; import { normalizeTmuxCapture as sharedNormalizeTmuxCapture, paneHasActiveTask as sharedPaneHasActiveTask, paneIsBootstrapping as sharedPaneIsBootstrapping, paneLooksReady as sharedPaneLooksReady } from '../scripts/tmux-hook-engine.js'; import { type ExactPaneProof } from './exact-pane.js'; export interface TeamSession { name: string; /** Frozen tmux session incarnation persisted before any detached teardown. */ tmuxSessionId?: string; tmuxSessionCreated?: string; workerCount: number; cwd: string; workerPaneIds: string[]; /** Original worker-index slots for partial recovery; null means no unresolved pane at that index. */ workerPaneIdsByIndex?: Array; /** Frozen worker pane process identities aligned with workerPaneIdsByIndex. */ workerPanePidsByIndex?: Array; /** Ambiguous split artifacts that could not be assigned to one worker slot. */ startupCleanupPanes?: Array<{ paneId: string; panePid: number | null; }>; /** Leader's own pane ID — must never be targeted by worker cleanup routines. */ leaderPaneId: string; /** Frozen leader pane process identity for startup and recovery authority. */ leaderPanePid?: number; /** Frozen leader tmux session/window incarnations for same-server sink authorization. */ leaderSessionId?: string; leaderSessionCreated?: string; leaderWindowId?: string; /** HUD pane spawned below the leader column, or null if creation failed. */ hudPaneId: string | null; /** Frozen HUD pane process identity for startup and recovery authority. */ hudPanePid?: number | null; /** Registered tmux resize hook name for the HUD pane, or null if unavailable. */ resizeHookName: string | null; /** Registered tmux resize hook target in ":" form, or null. */ resizeHookTarget: string | null; /** Team-scoped tmux pane ownership token used by shutdown safety checks. */ teamPaneOwnerId: string; /** Immutable effective argv used by attached tmux startup scripts, aligned by worker index. */ workerStartupArgs?: ReadonlyArray>; } /** Carries live tmux resources when creation cannot safely roll them back. */ export declare class CreateTeamSessionPartialError extends Error { readonly partialSession: TeamSession; readonly proofUnavailable: Array>; readonly originalError: unknown; /** Cleanup commands that failed after resources were created and must be retried. */ readonly cleanupErrors: string[]; constructor(partialSession: TeamSession, proofUnavailable: Array>, originalError: unknown, /** Cleanup commands that failed after resources were created and must be retried. */ cleanupErrors?: string[]); } export interface CreateTeamSessionOptions { /** * Stable logical leader id forwarded to HUD/hook runtime and the generic * tmux pane instance tag. Team shutdown must not rely on this value because * environment session ids can be stale when a user starts OMX from another * tmux pane in the same shell/session. */ ownerSessionId?: string | null; /** Team-scoped pane owner token used only for Team shutdown/teardown. */ teamPaneOwnerId?: string | null; } export interface RestoreStandaloneHudPaneOptions { /** Session id that prompt-submit HUD reconciliation should use to dedupe the restored HUD. */ sessionId?: string | null; /** Explicit HUD cwd override. When omitted, the live leader pane cwd is preferred over team launch cwd. */ cwd?: string | null; /** Frozen leader pane PID. When supplied, restoration never adopts a replacement pane PID. */ expectedLeaderPanePid?: number; /** Shared-session authorization recheck performed immediately before leader-targeted operations. */ assertLeaderPaneAuthorization?: () => void; /** Canonical state root for durable restored-HUD cleanup debt. */ stateRoot?: string | null; /** Canonical Team owner tag frozen with the leader for durable replay. */ expectedLeaderPaneOwnerId?: string | null; } export type TeamWorkerCli = 'codex' | 'claude' | 'gemini'; export type TeamWorkerLaunchMode = 'interactive' | 'prompt'; export interface WorkerSubmitPlan { shouldInterrupt: boolean; queueFirstRound: boolean; rounds: number; submitKeyPressesPerRound: number; allowAdaptiveRetry: boolean; } export interface WorkerProcessLaunchSpec { workerCli: TeamWorkerCli; command: string; args: string[]; env: Record; } interface TmuxPaneInfo { paneId: string; currentCommand: string; startCommand: string; } type SpawnSyncLike = typeof spawnSync; export declare function parseSplitWindowPaneId(stdout: string, expectedReceipt?: string): string | null; export type SourcePaneAuthority = { paneId: string; panePid: number; sessionName: string; sessionId: string; sessionCreated: string; windowId: string; windowIndex: string; /** Present only after the Team owner bootstrap tag has been committed. */ teamPaneOwnerId: string | null; }; /** Queue an effect in the source pane's tmux server only when its exact pane/session/window incarnation still matches. */ export declare function runSourceAuthorizedTmux(source: SourcePaneAuthority, effect: string, receipt?: string): string; /** * Replays a prior restored-HUD obligation. A live pane is killable only when * its recorded PID, HUD command identity, and tagged leader identity all * still match; PID-less and ambiguous records remain durable debt. */ export declare function reconcileRestoredHudCleanupDebtSync(cwd: string, stateRoot?: string | null): void; /** Remove restored-HUD debt only after the canonical config transaction commits the same frozen identity. */ export declare function finalizeRestoredHudCleanupDebtSync(cwd: string, paneId: string, panePid: number, stateRoot?: string | null, _requireFreshHudIdentity?: boolean): void; export declare function tagPaneTeamOwner(paneTarget: string, teamOwnerId: string, expectedPanePid?: number): void; export declare function mitigateCopyModeUnderlineArtifacts(sessionTarget: string, beforeEffect?: () => void): boolean; export declare function hasCurrentTmuxClientContext(): boolean; export declare function isMsysOrGitBash(env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform): boolean; export declare function translatePathForMsys(value: string, env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform, spawnImpl?: SpawnSyncLike): string; export declare function listPaneIds(target: string): string[]; export declare function chooseTeamLeaderPaneId(panes: TmuxPaneInfo[], preferredPaneId: string): string; export declare function sleepFractionalSeconds(seconds: number, sleepImpl?: (ms: number) => void): void; /** Capture an explicit Team worker pane only after PID-owner-PID authorization. */ export declare function captureWorkerPane(sessionName: string, workerIndex: number, workerPaneId: string, expectedPanePid: number, expectedTeamOwnerId: string, hudPaneId?: string): Promise; export declare function buildResizeHookTarget(sessionName: string, windowIndex: string): string; export declare function buildResizeHookName(teamName: string, sessionName: string, windowIndex: string, hudPaneId: string): string; export declare function buildHudPaneTarget(hudPaneId: string): string; export declare function buildRegisterResizeHookArgs(hookTarget: string, hookName: string, hudPaneId: string, heightLines?: number, expectedPanePid?: number, expectedPaneOwnerId?: string): string[]; export declare function buildUnregisterResizeHookArgs(hookTarget: string, hookName: string): string[]; export declare function buildClientAttachedReconcileHookName(teamName: string, sessionName: string, windowIndex: string, hudPaneId: string): string; export declare function buildRegisterClientAttachedReconcileArgs(hookTarget: string, hookName: string, hudPaneId: string, heightLines?: number, expectedPanePid?: number, expectedPaneOwnerId?: string): string[]; export declare function buildUnregisterClientAttachedReconcileArgs(hookTarget: string, hookName: string): string[]; export declare function unregisterResizeHook(hookTarget: string, hookName: string): boolean; export declare function buildScheduleDelayedHudResizeArgs(hudPaneId: string, delaySeconds?: number, heightLines?: number, expectedPanePid?: number, expectedPaneOwnerId?: string): string[]; export declare function buildReconcileHudResizeArgs(hudPaneId: string, heightLines?: number, expectedPanePid?: number, expectedPaneOwnerId?: string): string[]; export declare function shouldSourceTeamWorkerShellRc(env?: NodeJS.ProcessEnv): boolean; export declare function resolveTeamWorkerLaunchMode(env?: NodeJS.ProcessEnv): TeamWorkerLaunchMode; export declare function resolveTeamWorkerCli(launchArgs?: string[], env?: NodeJS.ProcessEnv): TeamWorkerCli; export declare function resolveTeamWorkerCliPlan(workerCount: number, launchArgs?: string[], env?: NodeJS.ProcessEnv): TeamWorkerCli[]; export declare function resolveTeamWorkerCliForResolvedLaunchArgs(workerIndex: number, workerCount: number, resolvedLaunchArgs: string[], env?: NodeJS.ProcessEnv): TeamWorkerCli; export declare function assertTeamWorkerCliPolicyCompatibility(workerCli: TeamWorkerCli, launchArgs: string[]): void; export declare function assertTeamWorkerLaunchPolicyInvariant(workerCli: TeamWorkerCli, launchArgs: string[]): void; export declare function translateWorkerLaunchArgsForCli(workerCli: TeamWorkerCli, args: string[], initialPrompt?: string, workerRole?: string): string[]; export declare function trustWorkerMiseConfigIfAvailable(workerCwd: string): boolean; export declare function assertTeamWorkerCliBinaryAvailable(workerCli: TeamWorkerCli, existsImpl?: (binary: string) => boolean): void; export declare function scrubTeamWorkerHudOwnershipEnv>(env: T): T; declare const CODEX_BYPASS_MDM_INCOMPATIBLE_REASON = "codex_bypass_mdm_incompatible"; export declare function classifyCodexBypassMdmIncompatibility(captured: string, workerCli: TeamWorkerCli | undefined, finalArgs: readonly string[] | undefined): typeof CODEX_BYPASS_MDM_INCOMPATIBLE_REASON | null; type AttachedTmuxWorkerStartupPlan = Readonly<{ processSpec: WorkerProcessLaunchSpec; startupArgs: ReadonlyArray; }>; export declare function buildWorkerStartupCommand(teamName: string, workerIndex: number, launchArgs?: string[], cwd?: string, extraEnv?: Record, workerCliOverride?: TeamWorkerCli, initialPrompt?: string, workerRole?: string): string; export declare function writeWorkerStartupScriptCommand(teamName: string, workerIndex: number, launchArgs?: string[], cwd?: string, extraEnv?: Record, workerCliOverride?: TeamWorkerCli, initialPrompt?: string, workerRole?: string, attachedPlan?: AttachedTmuxWorkerStartupPlan): string | null; export declare function buildWorkerProcessLaunchSpec(teamName: string, workerIndex: number, launchArgs?: string[], cwd?: string, extraEnv?: Record, workerCliOverride?: TeamWorkerCli, initialPrompt?: string, workerRole?: string): WorkerProcessLaunchSpec; export declare function sanitizeTeamName(name: string): string; /** * Detect whether the process is running inside a WSL2 environment. * WSL2 always sets WSL_DISTRO_NAME; WSL_INTEROP is also present. * Fallback: check /proc/version for the Microsoft kernel string. */ export declare function isWsl2(): boolean; /** * Detect whether the process is running on native Windows (not WSL2). * OMX requires tmux, which is unavailable on native Windows. */ export declare function isNativeWindows(): boolean; export declare function isTmuxAvailable(): boolean; export declare function createTeamSession(teamName: string, workerCount: number, cwd: string, workerLaunchArgs?: string[], workerStartups?: Array<{ cwd?: string; env?: Record; initialPrompt?: string; launchArgs?: string[]; workerCli?: TeamWorkerCli; workerRole?: string; }>, options?: CreateTeamSessionOptions): TeamSession; export declare function restoreStandaloneHudPane(leaderPaneId: string | null | undefined, cwd: string, options?: RestoreStandaloneHudPaneOptions): string | null; /** * Enable tmux mouse mode for a session so users can scroll pane content * (e.g. long agent output) with the mouse wheel instead of arrow keys. * * This helper is intentionally limited to session-scoped options so OMX * does not overwrite server-global tmux bindings/options owned by users, * oh-my-tmux, or other sessions. Returns true if the session mouse option * was set successfully, false otherwise. */ export declare function enableMouseScrolling(sessionTarget: string, beforeEffect?: () => void): boolean; export declare const paneIsBootstrapping: typeof sharedPaneIsBootstrapping; export declare const paneLooksReady: typeof sharedPaneLooksReady; export type StartupDirectTriggerSafety = { safe: true; reason: 'ready_prompt' | 'codex_viewport'; } | { safe: false; reason: 'tmux_unavailable' | 'capture_failed' | 'trust_prompt' | 'claude_bypass_prompt' | 'bootstrapping' | 'not_agent_viewport' | typeof CODEX_BYPASS_MDM_INCOMPATIBLE_REASON; }; export declare function evaluateStartupDirectTriggerSafetyCapture(captured: string, workerCli?: TeamWorkerCli, finalArgs?: readonly string[]): StartupDirectTriggerSafety; export declare function evaluateStartupDirectTriggerSafety(sessionName: string, workerIndex: number, workerPaneId?: string, workerCli?: TeamWorkerCli, finalArgs?: readonly string[]): Promise; export declare const paneHasActiveTask: typeof sharedPaneHasActiveTask; export type WorkerStartupInjectSafety = 'safe' | 'trust_prompt' | 'claude_bypass_prompt' | 'bootstrapping' | 'active_task' | 'not_ready'; export declare function classifyWorkerStartupInjectSafety(captured: string): WorkerStartupInjectSafety; export declare function checkWorkerStartupInjectSafety(sessionName: string, workerIndex: number, workerPaneId?: string): Promise<{ safe: true; reason: 'safe'; } | { safe: false; reason: Exclude; }>; /** * Worker CLI resolution contract for submit routing: * 1) explicit workerCli param from caller * 2) per-worker OMX_TEAM_WORKER_CLI_MAP entry (worker index aware) * 3) global/default OMX_TEAM_WORKER_CLI behavior */ export declare function resolveWorkerCliForSend(workerIndex: number, workerCli?: TeamWorkerCli, launchArgs?: string[], env?: NodeJS.ProcessEnv): TeamWorkerCli; export declare function buildWorkerSubmitPlan(strategy: 'auto' | 'queue' | 'interrupt', workerCli: TeamWorkerCli, paneBusyAtStart: boolean, allowAdaptiveRetry: boolean): WorkerSubmitPlan; export declare function shouldAttemptAdaptiveRetry(strategy: 'auto' | 'queue' | 'interrupt', paneBusyAtStart: boolean, allowAdaptiveRetry: boolean, latestCapture: string | null, text: string): boolean; export declare function waitForWorkerReady(sessionName: string, workerIndex: number, timeoutMs?: number, workerPaneId?: string, expectedPanePid?: number, expectedTeamOwnerId?: string, hudPaneId?: string): boolean; export declare function waitForWorkerReadyDetailedAsync(sessionName: string, workerIndex: number, timeoutMs?: number, workerPaneId?: string, expectedPanePid?: number, expectedTeamOwnerId?: string, hudPaneId?: string, incompatibility?: { workerCli?: TeamWorkerCli; finalArgs?: readonly string[]; onIncompatible: (reason: typeof CODEX_BYPASS_MDM_INCOMPATIBLE_REASON) => void; }): Promise; export declare function waitForWorkerReadyAsync(sessionName: string, workerIndex: number, timeoutMs?: number, workerPaneId?: string, expectedPanePid?: number, expectedTeamOwnerId?: string, hudPaneId?: string): Promise; /** * Detect and auto-dismiss a Codex "Trust this directory?" prompt in a worker pane. * Returns true if a trust prompt was found and dismissed, false otherwise. * Opt-out: set OMX_TEAM_AUTO_TRUST=0 to disable auto-dismissal. */ export declare function dismissTrustPromptIfPresent(sessionName: string, workerIndex: number, workerPaneId?: string, expectedPanePid?: number, expectedTeamOwnerId?: string, hudPaneId?: string): boolean; export declare const normalizeTmuxCapture: typeof sharedNormalizeTmuxCapture; export declare function sendToWorkerStdin(stdin: Pick | null | undefined, text: string): void; export declare function sendToWorker(sessionName: string, workerIndex: number, text: string, workerPaneId?: string, workerCli?: TeamWorkerCli, expectedPanePid?: number, expectedTeamOwnerId?: string, hudPaneId?: string): Promise; export declare function notifyLeaderStatus(sessionName: string, message: string): boolean; export declare function getWorkerPanePid(sessionName: string, workerIndex: number, workerPaneId?: string, expectedPanePid?: number, expectedTeamOwnerId?: string, hudPaneId?: string): number | null; export declare function isWorkerAlive(sessionName: string, workerIndex: number, workerPaneId?: string, expectedPanePid?: number, expectedTeamOwnerId?: string, hudPaneId?: string): boolean; export declare function isWorkerPaneOpen(sessionName: string, workerIndex: number, workerPaneId?: string, expectedPanePid?: number, expectedTeamOwnerId?: string, hudPaneId?: string): boolean; export declare function killWorker(sessionName: string, workerIndex: number, workerPaneId?: string, leaderPaneId?: string, expectedPanePid?: number, expectedTeamOwnerId?: string, hudPaneId?: string): Promise; export declare function killWorkerByPaneId(workerPaneId: string, expectedPanePid?: number, leaderPaneId?: string, expectedTeamOwnerId?: string, hudPaneId?: string): void; export declare function paneHasOmxInstanceTag(paneId: string | null | undefined, instanceId: string | null | undefined): boolean; export declare function paneHasOmxTeamOwnerTag(paneId: string | null | undefined, teamOwnerId: string | null | undefined): boolean; export declare function readPaneTeamOwnerTag(paneId: string | null | undefined): string | null; export type PaneTeamOwnerTagReadResult = { status: 'value'; value: string; } | { status: 'missing'; } | { status: 'error'; error: string; }; export declare function readPaneTeamOwnerTagResult(paneId: string | null | undefined): PaneTeamOwnerTagReadResult; export declare function killWorkerByPaneIdAsync(workerPaneId: string, expectedPanePid?: number, leaderPaneId?: string, expectedTeamOwnerId?: string, hudPaneId?: string): Promise; export interface PaneTeardownSummary { attemptedPaneIds: string[]; excluded: { leader: number; hud: number; invalid: number; }; provenGonePaneIds: string[]; killedPaneIds: string[]; proofUnavailable: Array>; kill: { attempted: number; succeeded: number; failed: number; failedPaneIds: string[]; }; } export interface PaneTeardownOptions { leaderPaneId?: string | null; hudPaneId?: string | null; graceMs?: number; expectedPanePids?: Readonly>; /** Revalidates caller-owned authority after the final exact PID proof and before kill-pane. */ authorizePaneKill?: (paneId: string, proof: Extract) => boolean; } export type SharedSessionShutdownTopology = { status: 'available'; livePaneIds: string[]; teamWorkerPaneIds: string[]; leaderPaneId: string | null; hudPaneIds: string[]; leaderOwnedHudPaneIds: string[]; } | { status: 'unavailable'; detail: string; }; export declare function resolveSharedSessionShutdownTopology(sessionName: string, preferredLeaderPaneId?: string | null, teamName?: string | null): SharedSessionShutdownTopology; /** * Shared pane-id-direct teardown primitive for worker pane cleanup. * A fresh exact global proof is required before each kill; proven-gone panes * are teardown-compatible while unavailable proofs remain fail-closed. */ export declare function teardownWorkerPanes(paneIds: string[], options?: PaneTeardownOptions): Promise; export declare function killWorkerPanes(paneIds: string[], leaderPaneId: string, graceMs?: number, hudPaneId?: string, expectedPanePids?: Readonly>): Promise; export type DetachedSessionIncarnation = { sessionId: string; sessionCreated: string; }; export type DetachedSessionLeaderBinding = { paneId: string; pid: number; sessionName: string; incarnation: DetachedSessionIncarnation; }; export type DetachedSessionDestroyAuthorization = DetachedSessionIncarnation & { leaderPaneId: string; leaderPanePid: number; ownerId: string; }; /** * Reads pane and session incarnation in one tmux snapshot. This prevents a * same-name replacement from combining a stale session proof with a live pane. */ export declare function queryDetachedSessionLeaderBinding(paneId: string, pid: number, sessionName: string, expected: DetachedSessionIncarnation): DetachedSessionLeaderBinding | null; export type DetachedSessionQueryResult = { status: 'absent'; } | { status: 'exact'; incarnation: DetachedSessionIncarnation; } | { status: 'replacement'; incarnation: DetachedSessionIncarnation; } | { status: 'unavailable'; detail: string; }; /** * Reads stable tmux incarnation evidence. Name-only presence is never effect * authority: malformed or unavailable evidence fails closed, and an expected * incarnation distinguishes an exact survivor from a recycled same-name session. */ export declare function queryDetachedTeamSession(sessionName: string, expected?: DetachedSessionIncarnation): DetachedSessionQueryResult; export declare function requestDetachedTeamSessionDestroy(sessionName: string, expected: DetachedSessionDestroyAuthorization): boolean; export declare function destroyTeamSession(sessionName: string): boolean; export declare function listTeamSessions(): string[] | null; /** * Notify the leader through durable mailbox state only. * * Team leaders are a coordination endpoint, not a direct tmux control target: * workers and runtime paths may message `leader-fixed` via `omx team api` * / mailbox persistence, but team code must not inject text or control keys * into the leader pane. This is the async mailbox-based replacement for * `notifyLeaderStatus()`. */ export declare function notifyLeaderMailboxAsync(teamName: string, fromWorker: string, message: string, cwd: string): Promise; export {}; //# sourceMappingURL=tmux-session.d.ts.map