/** Lightweight session-directory projection for the /resume picker. */ import { SessionId, type SessionEvent, type SessionHeader } from '@deepseek-ai/dsh-session'; export interface SessionRecord { readonly header: SessionHeader; readonly live: boolean; readonly persisted: boolean; } /** Minimal write handle retained while a planned deletion touches artifacts. */ export interface SessionDeletionLease { close(): Promise; } /** Public persistence operation used to acquire the backend's write lease. */ export interface SessionDeletionPersistence { open(id: SessionId, access: 'write'): Promise; } /** * Acquire every subtree member's cross-process write lease before deleting * any artifact. A partial acquisition is rolled back, so callers either hold * the whole deletion boundary or touch nothing. */ export declare function acquireSessionDeletionLeases(persistence: SessionDeletionPersistence, ids: readonly string[]): Promise; /** Release deletion leases in reverse acquisition order. */ export declare function releaseSessionDeletionLeases(leases: readonly SessionDeletionLease[]): Promise; export interface TitleObservationResult { readonly sessionId: string; readonly status: 'fulfilled' | 'rejected'; readonly value?: { readonly title?: { readonly title?: string; readonly text?: string; }; }; } export interface SessionLogSnapshot { readonly session: SessionHeader; readonly events: SessionEvent[]; } /** Structural upstream SessionQuery surface used by the TUI. */ export interface SessionQueryService { listSessions(signal?: AbortSignal): Promise; readTitleSnapshots(ids: readonly string[], signal?: AbortSignal): Promise; readSession(id: string, signal?: AbortSignal): Promise; /** Cross-session full-text search (SQLite FTS engine; openAt may gate it). */ searchSessions(request: { query: string; limit?: number; }, exec?: { signal?: AbortSignal; }): Promise<{ items: readonly { header: SessionHeader; live: boolean; persisted: boolean; bestMatch: { snippet: string; time: number; }; }[]; }>; } export type SessionScope = 'roots' | 'all'; export type CwdScope = 'all' | 'current'; export type SessionSort = 'newest' | 'oldest'; export interface SessionDirectoryOptions { readonly sessions: SessionScope; readonly cwd: CwdScope; readonly sort: SessionSort; readonly currentCwd: string; readonly query: string; } export interface SessionRow { readonly id: string; readonly createdAt: number; /** Last-activity timestamp: artifact mtime when known, else createdAt. */ readonly updatedAt: number; readonly cwd: string; readonly workspace: string; readonly parent?: string; readonly subagent: boolean; readonly resumable: boolean; readonly live: boolean; readonly persisted: boolean; readonly preset: string; readonly title?: string; } /** True only for delegated subagents; ordinary forks also carry lineage. */ export declare function isSubagentSession(header: SessionHeader): boolean; /** * Unique header match by exact id or unique id prefix (root and subagent * headers alike); the caller applies any lineage gate. * @param headers - the persisted headers. * @param wanted - the id or id prefix. * @returns the uniquely matched header. * @throws when nothing matches or the prefix is ambiguous. */ export declare function matchSessionId(headers: readonly SessionHeader[], wanted: string): SessionHeader; /** * Unique picker-row match by exact id, unique prefix, or unique suffix. * The resume list shows `id.slice(-12)`, so `/delete` arguments are often * that tail rather than a leading prefix. */ export declare function matchSessionRow(rows: readonly SessionRow[], wanted: string): SessionRow; /** The newest persisted ROOT session pinned to this cwd, or undefined. */ export declare function newestRootForCwd(headers: readonly SessionHeader[], cwd: string): SessionHeader | undefined; /** * Filter/sort header-only records. No session log is loaded here. Sorting is * by LAST ACTIVITY (`updated` — artifact mtime when the caller resolved one, * else createdAt), matching the codex resume picker's default UpdatedAt * ordering: a session you kept talking in outranks one created later but idle. * @param records - the header-only records. * @param options - filter/sort options. * @param updated - per-session last-activity timestamps, when resolved. */ export declare function projectSessionRows(records: readonly SessionRecord[], options: SessionDirectoryOptions, updated?: ReadonlyMap): SessionRow[]; /** True when the picker query hits id, path, preset, or the displayed title. */ export declare function sessionRowMatchesQuery(row: Pick, query: string): boolean; /** Merge page-local title observations without disturbing directory order. */ export declare function mergeSessionTitles(rows: readonly SessionRow[], observations: readonly TitleObservationResult[]): SessionRow[]; /** * Encode a session id the way the JSONL backend does for its on-disk layout * (`encodeSegment`: safe units literal, everything else `~XXXX`). Used to * validate and derive session directories — a local copy of the pure upstream * contract, kept in sync with `session-persistence-jsonl/src/format.ts`. */ export declare function encodeSessionSegment(raw: string): string; /** * Encode a project cwd the way the JSONL backend groups sessions on disk * (`projectKey`: separators collapse to one `-`, everything else mirrors * `encodeSegment`, bounded to 251 chars). A local copy of the pure upstream * contract, kept in sync with `session-persistence-jsonl/src/format.ts`. */ export declare function encodeProjectKey(cwd: string): string; /** * Derive one session's artifact directory under the JSONL backend root, * mirroring the upstream `///` * layout (0.1.5 `sessionDir`/`projectDir`). * @param root - the JSONL backend's configured session root. * @param cwd - the session's pinned working directory, when the header has one. * @param id - the session id. * @returns the absolute session directory path. */ export declare function sessionDirectoryFor(root: string, cwd: string | undefined, id: string): string; /** * The canonical session-log artifact filenames the JSONL backend may create: * format v0 writes the bare `session.jsonl` name; v1+ write * `session.vN.jsonl`, each generation optionally zstd-compressed. Multiple * immutable generations may coexist in one session directory (0.1.5). The * range follows the installed session package's `SESSION_FORMAT_VERSION`, so * a future generation joins the enumeration with the dependency bump. */ export declare function sessionArtifactNames(): readonly string[]; /** True for one canonical session-log artifact filename the backend may own. */ export declare function isSessionArtifactName(name: string): boolean; /** * Guard a derived session directory before deletion (codex's scoped-path * check, adapted to the JSONL layout): the directory's base name must be * exactly `encodeSegment(id)` beneath its project grouping. * @param dir - the derived session artifact directory. * @param id - the session id the directory claims to belong to. * @returns the guarded directory, or undefined when the layout is unexpected. */ export declare function sessionArtifactDirectory(dir: string, id: string): string | undefined; /** * The JSONL backend's configured session root, when the mounted backend * exposes one. The upstream service contract dropped `locate()` in 0.1.5 * (artifact paths are backend-private; only refusal diagnostics carry them), * so the TUI derives artifact paths from the backend's public plugin config. * Backends without a JSONL-style config (or a foreign shape) yield undefined * and callers degrade: mtime sorting falls back to createdAt and /delete * refuses, exactly as before. */ export declare function jsonlSessionRoot(persistence: unknown): string | undefined; /** * Collect one session's deletion subtree: the id plus every record whose * parent chain leads to it (codex deletes subagent threads with their root). * @param records - the full directory listing. * @param id - the root session id to delete. * @returns the ids to delete, root first. */ export declare function collectDeletionSubtree(records: readonly SessionRecord[], id: string): string[]; /** One validated node of a deletion plan. */ export interface DeletionPlanNode { /** Session id to remove. */ readonly id: string; /** Distance from the deletion root (0 for the root itself). */ readonly depth: number; } /** A fully preflighted subtree deletion, or the refusal that produced none. */ export type SessionDeletionPlan = { readonly ok: true; readonly nodes: readonly DeletionPlanNode[]; } | { readonly ok: false; readonly reason: string; }; /** * Plan one session-subtree deletion with NO filesystem side effects: collect * the doomed lineage, refuse when the root or ANY member is live (a live * child would outlive its deleted parent) or missing from the listing, and * order the result children-first so the executor can never leave a deleted * parent behind surviving children. Artifact-location guards stay at the * call site; this is the pure preflight they complete. * @param records - the full directory listing. * @param id - the root session id to delete. * @returns the ordered plan, or a user-facing refusal reason. */ export declare function planSessionDeletion(records: readonly SessionRecord[], id: string): SessionDeletionPlan; /** * Codex-style relative time for session rows ("now", "5m ago", "3h ago", * "2d ago"; older than a week falls back to the local date). * @param timestamp - epoch milliseconds of the last activity. * @param now - the pinned reference clock (one value per list render). */ export declare function formatRelativeTime(timestamp: number, now: number): string;