import { type StateRootSource } from '../mcp/state-paths.js'; import type { PromptDiagnosticDescriptor } from './prompt-session-provenance.js'; import { type RegularFileSyncOutcome } from '../utils/file-durability.js'; /** Cross-platform process birth identity. `birth` is an exact decimal string. */ export interface ProcessIdentity { platform: NodeJS.Platform; birth: string; cmdline_hash?: string; } /** Raw observation from a native process-identity provider. No comparison logic. */ export type ProcessObservation = { kind: 'identity'; identity: ProcessIdentity; } | { kind: 'gone'; } | { kind: 'denied'; } | { kind: 'unsupported'; } | { kind: 'error'; }; /** Classification of a recorded identity against a live observation. */ export type IdentityClassification = { status: 'match'; } | { status: 'birth-mismatch'; } | { status: 'identity-unavailable'; } | { status: 'gone'; }; /** Provider interface for observing process identity. */ export interface ProcessInspectionProvider { probePid(pid: number): PidProbeResult; observeProcess(pid: number, platform: NodeJS.Platform): ProcessObservation; } export interface SessionState { session_id: string; native_session_id?: string; previous_native_session_id?: string; native_session_switched_at?: string; owner_omx_session_id?: string; owner_codex_session_id?: string; codex_session_id?: string; started_at: string; cwd: string; state_root?: string; pid: number; platform?: NodeJS.Platform; pid_start_ticks?: number; pid_cmdline?: string; /** Versioned process identity schema; absent means legacy v1. */ identity_schema_version?: 2; /** Cross-platform process identity (v2 schema). */ process_identity?: ProcessIdentity; tmux_session_name?: string; tmux_pane_id?: string; /** Private wrapper lineage evidence; native reconciliation never creates or repairs it. */ launch_lineage_token?: string; } export interface SessionPointerContext { cwd: string; baseStateDir: string; rootSource: StateRootSource; sessionPath: string; lockPath: string; } export type SessionPointerStatus = 'absent' | 'usable' | 'stale-dead' | 'identity-indeterminate' | 'malformed' | 'foreign-cwd'; export interface SessionPointerReadResult { status: SessionPointerStatus; state?: SessionState; raw?: string; } /** Classified native session-owner sidecar evidence for fail-closed consumers. */ export interface NativeSessionOwnerEvidence extends SessionPointerReadResult { } export type SessionPointerTransactionOperation = 'pointer-context-resolve' | 'state-dir-create' | 'lock-acquire' | 'lock-owner-publish' | 'pointer-read' | 'pointer-classify' | 'pointer-temp-write' | 'pointer-fsync' | 'pointer-rename' | 'owner-conflict' | 'precommit-cleanup' | 'lock-release'; type AttemptedStateRootSource = StateRootSource | 'unresolved'; type UnusableSessionPointerStatus = 'malformed' | 'foreign-cwd' | 'identity-indeterminate'; export type SessionPointerCleanupPhase = 'remove-owner-temp' | 'inspect-unpublished-lock' | 'remove-unpublished-lock' | 'remove-pointer-temp' | 'token-check' | 'rename' | 'remove-release-owner' | 'remove-release-dir'; export interface SessionPointerSecondaryFailure { operation: 'precommit-cleanup' | 'lock-release'; phase: SessionPointerCleanupPhase; ownership: 'held' | 'released' | 'uncertain'; message: string; cause?: unknown; evidencePath?: string; } export interface SessionPointerAbortBase extends Error { name: 'SessionPointerLaunchAbort'; committed: false; cwd: string; candidateSessionId?: string; canonicalSessionId?: string; reason: string; cause?: unknown; } export interface SessionPointerContextAbort extends SessionPointerAbortBase { code: 'session_pointer_context_failure'; operation: 'pointer-context-resolve'; attemptedRootSource: AttemptedStateRootSource; pointerPath?: never; lockPath?: never; rootSource?: never; } export interface ResolvedSessionPointerAbort extends SessionPointerAbortBase { code: 'session_pointer_lock_timeout' | 'session_pointer_lock_recovery_required' | 'session_pointer_unusable' | 'session_pointer_owner_conflict' | 'session_pointer_io_failure'; operation: Exclude; pointerPath: string; lockPath?: string; rootSource: StateRootSource; pointerStatus?: UnusableSessionPointerStatus; lockOwnerStatus?: 'live' | 'dead' | 'reused' | 'identity-indeterminate' | 'missing' | 'malformed'; primaryOperation?: Exclude; secondaryFailures?: readonly SessionPointerSecondaryFailure[]; } export type SessionPointerLaunchAbort = SessionPointerContextAbort | ResolvedSessionPointerAbort; export type UnsupportedDirectoryCapabilityReason = 'platform' | 'inadequate-identity' | 'stat-feature'; export type CapabilityCloseEvidence = Readonly<{ role: 'acquisition' | 'fresh-comparison' | 'original-retained'; phase: 'before-authorization' | 'post-finalization' | 'detached-pre-release'; status: 'not-needed' | 'closed' | 'failed'; error?: Readonly<{ name: string; message: string; code?: string; }>; }>; export interface EstablishmentCleanupEvidence { readonly capability: readonly CapabilityCloseEvidence[]; } export interface LifecycleCleanupEvidence extends EstablishmentCleanupEvidence { readonly comparison?: Readonly<{ status: 'not-run' | 'matched' | 'denied'; reason?: string; }>; } export interface LaunchSessionBinding { readonly context: Readonly; readonly canonicalRealpath: string; readonly directoryIdentity: Readonly<{ kind: 'supported'; dev: bigint; ino: bigint; } | { kind: 'unsupported'; reason: UnsupportedDirectoryCapabilityReason; }>; readonly canonicalSessionId: string; readonly ownerOmxSessionId?: string; readonly nativeSessionId?: string; readonly startedAt: string; readonly launchLineageToken: string; } export interface CommittedLaunchEvidence { readonly context: Readonly; readonly canonicalSessionId: string; } export declare class CommittedLaunchBlockedError extends Error { readonly secondaryFailures: readonly SessionPointerSecondaryFailure[]; readonly name = "CommittedLaunchBlockedError"; constructor(secondaryFailures: readonly SessionPointerSecondaryFailure[]); } export type LaunchEstablishment = { kind: 'precommit-aborted'; abort: SessionPointerLaunchAbort; cleanup: EstablishmentCleanupEvidence; } | { kind: 'committed-released'; binding: LaunchSessionBinding; cleanup: EstablishmentCleanupEvidence; } | { kind: 'committed-release-failed'; evidence: CommittedLaunchEvidence; error: CommittedLaunchBlockedError; lockDisposition: 'held' | 'released-with-residue' | 'uncertain'; secondaryFailures: readonly SessionPointerSecondaryFailure[]; cleanup: EstablishmentCleanupEvidence; }; export type DetachedMetadataUpdate = { kind: 'precommit-aborted'; abort: SessionPointerLaunchAbort; cleanup: EstablishmentCleanupEvidence; } | { kind: 'committed-released'; evidence: CommittedLaunchEvidence; cleanup: EstablishmentCleanupEvidence; } | { kind: 'committed-release-failed'; evidence: CommittedLaunchEvidence; error: CommittedLaunchBlockedError; lockDisposition: 'held' | 'released-with-residue' | 'uncertain'; secondaryFailures: readonly SessionPointerSecondaryFailure[]; cleanup: EstablishmentCleanupEvidence; }; export interface BoundFinalizationReport { readonly cleanup: LifecycleCleanupEvidence; readonly finalized: boolean; } /** * Convert arbitrary input into a valid session ID without exposing validator * exceptions to lifecycle or hook inputs. */ export declare function normalizeSessionId(value: unknown): string | undefined; /** Resolve the one exact pointer path used by an operation. */ export declare function resolveSessionPointerContext(cwd: string, env?: NodeJS.ProcessEnv): SessionPointerContext; export declare function isSessionPointerLaunchAbort(error: unknown): error is SessionPointerLaunchAbort; interface LinuxProcessIdentity { startTicks: number; cmdline: string | null; } export interface SessionStaleCheckOptions { platform?: NodeJS.Platform; isPidAlive?: (pid: number) => boolean; probePid?: (pid: number) => PidProbeResult; observeProcess?: (pid: number, platform: NodeJS.Platform) => ProcessObservation; readLinuxIdentity?: (pid: number) => LinuxProcessIdentity | null; } export interface SessionStartOptions { pid?: number; platform?: NodeJS.Platform; /** @internal Scoped deterministic regular-file fsync seam. */ regularFileSync?: (platform: NodeJS.Platform) => Promise; nativeSessionId?: string; previousNativeSessionId?: string; nativeSessionSwitchedAt?: string; /** * Compatibility-only metadata. Alias candidacy always comes from * process.env.OMX_SESSION_ID, never from this option. */ ownerOmxSessionId?: string; /** The caller proved the env candidate with actual tmux pane/session tags. */ ownerAliasVerified?: boolean; tmuxSessionName?: string; tmuxPaneId?: string; context?: SessionPointerContext; } /** @internal Test-only deterministic transaction seam; do not use outside session tests. */ export type PidProbeResult = 'alive' | 'dead' | 'indeterminate'; /** @internal Test-only deterministic transaction seam; do not use outside session tests. */ export interface SessionPointerFsDependencies { mkdir(path: string, options?: { recursive?: boolean; }): Promise; readdir(path: string): Promise; lstat(path: string): Promise<{ dev: number; ino: number; isDirectory(): boolean; isFile(): boolean; isSymbolicLink(): boolean; }>; readFile(path: string, encoding: 'utf8'): Promise; writeFile(path: string, data: string, options?: { mode?: number; flag?: string; }): Promise; openAndSync(path: string, platform: NodeJS.Platform, regularFileSync?: SessionStartOptions['regularFileSync']): Promise; rename(from: string, to: string): Promise; link(path: string, dest: string): Promise; unlink(path: string): Promise; rmdir(path: string): Promise; } export interface SessionPointerLockInspection { status: 'absent' | 'live' | 'dead' | 'reused' | 'identity-indeterminate' | 'missing-owner' | 'malformed' | 'ambiguous' | 'unexpected' | 'symlink' | 'io-error'; lockPath: string; evidenceSource: 'none' | 'owner.json' | 'owner-temp'; safeToRecover: boolean; } export interface SessionPointerLockRecovery extends SessionPointerLockInspection { action: 'none' | 'quarantined'; recovered: boolean; reason: string; quarantinePath?: string; } /** @internal Test-only deterministic transaction seam; do not use outside session tests. */ export interface SessionPointerTransactionDependencies { fs: SessionPointerFsDependencies; nowMs(): number; sleep(ms: number): Promise; token(): string; runtimePlatform: NodeJS.Platform; probePid(pid: number): PidProbeResult; observeProcess(pid: number, platform: NodeJS.Platform): ProcessObservation; atomicRenameNoReplace(from: string, to: string): Promise; } /** Recovery-only no-clobber rename seam. Normal lock lifecycle never uses it. */ export type RecoveryRenameNoReplaceResult = 'moved' | 'not-moved' | 'unsupported'; /** @internal Exposed only so source-module tests can verify default ESRCH handling. */ export declare function __createDefaultPidProbeForTests(killZero: (pid: number) => void): (pid: number) => PidProbeResult; export declare const defaultProcessInspectionProvider: ProcessInspectionProvider; /** @internal Source-module test harness. Always reset after a test. */ export declare function __setSessionPointerTransactionDependenciesForTests(overrides: Omit, 'fs'> & { fs?: Partial; }): void; /** @internal Source-module test harness. */ export declare function __resetSessionPointerTransactionDependenciesForTests(): void; export declare function isValidProcessIdentity(value: unknown): value is ProcessIdentity; /** * Legacy boolean stale check retained for read compatibility. The pointer * classifier below keeps indeterminate liveness distinct from definitely dead. */ export declare function isSessionStale(state: SessionState, options?: SessionStaleCheckOptions): boolean; /** Compare recorded process birth evidence with a fresh provider observation. */ export declare function classifyRecordedIdentity(recorded: Pick, runtimePlatform: NodeJS.Platform, provider: ProcessInspectionProvider, pid: number): IdentityClassification; export declare function isSessionStateAuthoritativeForCwd(state: SessionState, cwd: string): boolean; export declare function isSessionStateUsable(state: SessionState, cwd: string, options?: SessionStaleCheckOptions): boolean; /** * Classify process liveness for an already-parsed selected session pointer. * Callers that need cwd/state_root authority must validate those against the * SAME snapshot themselves; this evaluates only pid/start-tick evidence so a * single immutable snapshot can drive both decisions. */ export declare function classifySessionStateLiveness(state: SessionState): 'usable' | 'stale-dead' | 'identity-indeterminate'; /** Read and classify only context.sessionPath; no alternate root is consulted. */ export declare function readSessionPointer(context: SessionPointerContext): Promise; export declare function readSessionStateFromContext(context: SessionPointerContext): Promise; export declare function readUsableSessionStateFromContext(context: SessionPointerContext): Promise; /** Read current session state from the exact selected root. */ export declare function readSessionState(cwd: string, env?: NodeJS.ProcessEnv): Promise; export declare function readUsableSessionState(cwd: string, options?: SessionStaleCheckOptions): Promise; /** Inspect only exact, regular owner evidence in the selected pointer lock. */ export declare function inspectSessionPointerLock(cwd: string): Promise; /** Explicitly quarantine only a dead lock by moving its entire directory incarnation. */ export declare function recoverSessionPointerLock(cwd: string): Promise; /** @internal Test-only release of a held pointer lock; do not use outside session tests. */ export declare function __releasePointerLockForTests(cwd: string, token: string): Promise; /** Create a wrapper-owned canonical pointer only when the selected pointer is absent. */ export declare function writeSessionStart(cwd: string, sessionId: string, options?: SessionStartOptions): Promise; export declare class LaunchContextResolutionError extends Error { readonly cleanup: EstablishmentCleanupEvidence; readonly name = "LaunchContextResolutionError"; constructor(cleanup: EstablishmentCleanupEvidence, cause: unknown); } export declare function closeLaunchSessionBindingOnce(binding: LaunchSessionBinding, phase?: CapabilityCloseEvidence['phase']): Promise; export declare function establishLaunchSessionBinding(cwd: string, requestedSessionId: string, options?: SessionStartOptions): Promise; export declare function updateDetachedSessionMetadata(binding: LaunchSessionBinding, patch: { tmuxSessionName?: string; tmuxPaneId?: string; }): Promise; export declare function finalizeBoundOnce(binding: LaunchSessionBinding, _reason: string, postLaunchCwd?: string): Promise; export declare function writeNativeSessionOwner(cwd: string, nativeSessionId: string, options?: SessionStartOptions): Promise; /** Read and classify the exact native session-owner sidecar without consulting alternate roots. */ export declare function readNativeSessionOwnerEvidence(cwd: string, nativeSessionId: string): Promise; export declare function readNativeSessionOwner(cwd: string, nativeSessionId: string): Promise; /** * Reconcile native SessionStart only when the selected pointer is absent, stale, * or already belongs to that native session. A different live native ID is * authoritative owner evidence and must never be replaced. */ export declare function reconcileNativeSessionStart(cwd: string, nativeSessionId: string, options?: SessionStartOptions): Promise; /** * Archive first and remove an owned pointer only after that history write * succeeds. Present unusable pointer evidence is never repaired or archived. */ export declare function writeSessionEnd(cwd: string, sessionId: string, options?: Pick & { binding?: LaunchSessionBinding; postLaunchCwd?: string; }): Promise<{ comparison: LifecycleCleanupEvidence['comparison']; capability: CapabilityCloseEvidence[]; }>; /** Reset session-scoped HUD/metrics files at launch. */ export declare function resetSessionMetrics(cwd: string, sessionId?: string): Promise; /** * Append one redacted provenance rejection to the already-selected state root. * This deliberately accepts a resolved context rather than cwd, and never falls * back to the ambient root when that exact write fails. */ export declare function appendPromptSessionProvenanceRejection(context: SessionPointerContext, descriptor: PromptDiagnosticDescriptor): Promise; /** * Append a root log entry for callers that do not already own a pointer * context. Lifecycle transitions use appendToLogAtContext instead. */ export declare function appendToLog(cwd: string, entry: Record): Promise; export {}; //# sourceMappingURL=session.d.ts.map