import type { SessionContextUsage } from '../core/types/agent-types.js'; export declare const REGISTRY_FILE: string; /** How a session was initiated. Orthogonal, finer-grained companion to `kind`: * - 'direct' — a user-initiated conversation (Slack/Feishu/TUI/Web direct chat) * - 'thread' — an agent session spawned by a thread step (pipeline / task-dispatch) * - 'scheduled' — a session created by a scheduled job * The UI session list shows only `origin === 'direct'`; thread/scheduled sessions are * surfaced through the Thread and Schedule views. `kind` is retained for resumable * semantics (`kind !== 'scheduled'`); origin never replaces it. */ export type SessionOrigin = 'direct' | 'thread' | 'scheduled'; /** Derive a session's origin from its kind + label. Single source of truth shared by * registerSession (default when a caller omits origin) and the migration back-fill. */ export declare function deriveSessionOrigin(kind: 'local' | 'scheduled', label: string | null | undefined): SessionOrigin; /** Resolve a record's backend-resume id (Claude `--resume` / PI `--session` target, backup file name) * with legacy fallback. A record that predates the field (`backendSessionId === undefined`) had its * tracking `sessionId` doubling as the backend id, so fall back to it; a fresh record explicitly * carries `null` (backend self-generates on the next turn) and must NOT fall back. */ export declare function effectiveBackendSessionId(rec: Pick): string | null; export interface Session { name: string; /** Stable tracking id — the UI-facing identity, minted by Cortex, never changes. Registry key, * sessions.json channel binding, session.* events, conversation-history and transcript all key on * this. Decoupled from the backend CLI's own session id (see backendSessionId). */ sessionId: string; projectId: string; channel: string; backend: string; kind: 'local' | 'scheduled'; /** How the session was initiated (direct chat / thread step / scheduled job). */ origin: SessionOrigin; createdAt: string; lastUsedAt: string; label: string | null; /** Profile name active when the session was created. Restored on !resume. */ profileName: string | null; /** Backend CLI's own session id — the resume target (Claude `--resume`, PI `--session`) and the * session-backup jsonl file name. Distinct from the tracking `sessionId`: each backend self-assigns * it (Claude generates a UUID for `--session-id`; PI assigns one at bootstrap), captured from the * turn result and stored here. Semantics: * - `undefined` (legacy record, field absent) → fall back to `sessionId` (old conflated id). * - `null` → not assigned yet (fresh session) → the backend self-generates on the next turn. * - string → the resolved backend id to resume. * Use {@link effectiveBackendSessionId} to read it (handles the legacy fallback). */ backendSessionId?: string | null; /** When the user last VIEWED this session in a client (web workbench sessions.markRead). * Unread = lastUsedAt > lastReadAt. Absent on legacy records → treated as read. */ lastReadAt?: string | null; /** The schedule (ScheduleTask.id) whose fire produced this session. Set for scheduled runs and * KEPT after a reply converts the run to a direct session (provenance for the trigger card / * schedule grouping). Null/absent for sessions with no schedule origin. */ scheduleId?: string | null; /** Latest backend-reported context occupancy. Optional for legacy/unsupported sessions. */ contextUsage?: SessionContextUsage; } export type SessionRegistryData = Record; export declare class SessionRegistryRepo { private readonly _repo; /** name → sessionId index keeping lookupSession O(1). */ private _nameIndex; /** True once the name index has been fully rebuilt from the complete registry data. * Guards against the index being treated as authoritative when it only holds entries * added incrementally by registerSession() before any full build (which would make * lookupSession() miss every session created in a previous process lifetime). */ private _indexBuilt; /** Optional callback invoked when a session is pruned. Receives the sessionId. */ private _onPruneSession; constructor(filePath?: string); private _rebuildNameIndex; /** Read registry data, performing a one-time full rebuild of the name index. * Must NOT gate on _nameIndex.size: registerSession() populates the index * incrementally, so a non-empty-but-incomplete index would otherwise never be * fully rebuilt and lookups for pre-existing sessions would silently miss. */ private _readWithIndex; /** Rebuild the name index from the current cached data (zero I/O on cache hit). */ private _syncIndex; generateSessionName(): Promise; registerSession(name: string, opts: { sessionId: string; channel: string; backend: string; kind: 'local' | 'scheduled'; origin?: SessionOrigin; projectId?: string; label?: string | null; profileName?: string | null; backendSessionId?: string | null; scheduleId?: string | null; }): Promise; /** Convert a scheduled run into a live direct session (a web reply "adopts" the run): re-point * its conduit channel, flip kind→local (resumable) and origin→direct (shows in the direct * list). scheduleId/backendSessionId are untouched — provenance and the resume target survive. * Returns the updated record, or null when the sessionId is unknown. */ convertToDirect(sessionId: string, opts: { channel: string; }): Promise; updateSession(name: string, updates: Partial>): Promise; /** Set or clear a context snapshot by the stable Cortex session id. */ updateContextUsage(sessionId: string, usage: SessionContextUsage | null): Promise; lookupSession(name: string): Promise; lookupBySessionId(sessionId: string): Promise; listRecentSessions(limit?: number): Promise; /** Return a session by sessionId, or null if not found. O(1) key lookup. */ getById(sessionId: string): Promise; /** List sessions belonging to a project, most recently used first. */ listByProject(projectId: string): Promise; /** List sessions of a given origin (direct / thread / scheduled), most recent first, * optionally scoped to a project. Drives the UI's origin-filtered session list. */ listByOrigin(origin: SessionOrigin, projectId?: string): Promise; /** List resumable (non-scheduled) sessions, optionally filtered by projectId. */ listResumable(projectId?: string): Promise; /** Touch the lastUsedAt timestamp of a session to now. No-op if sessionId not found. */ markUsed(sessionId: string): Promise; /** Stamp lastReadAt to now — the user viewed this session (unread tracking, web markRead). * No-op if sessionId not found. */ markRead(sessionId: string): Promise; /** * Remove sessions whose lastUsedAt is older than maxAgeMs from now, * and are not referenced by any executionRepo or threadStore record. * Invokes the onPruneSession callback (if set) for each removed session. * Returns the number of removed sessions. */ pruneStale(maxAgeMs: number): Promise; /** Set a callback to invoke when a session is pruned (e.g. cleanup backup files). */ setOnPruneSession(fn: ((sessionId: string) => void) | null): void; getActiveSessionName(channel: string, backend: string): Promise; /** Drop the in-memory cache so the next read() fetches from disk. Test hook. */ invalidate(): void; /** Wait for any in-flight mutate() to complete. For graceful SIGTERM drain. */ flush(): Promise; } export declare const sessionStore: SessionRegistryRepo; /** @deprecated Use `sessionStore` instead. Alias for backward compatibility. */ export declare const sessionRegistryRepo: SessionRegistryRepo;