356 lines
16 KiB
TypeScript
356 lines
16 KiB
TypeScript
import type { AgentMessage } from "@earendil-works/pi-agent-core";
|
|
import { type ImageContent, type Message, type TextContent, type Usage } from "@earendil-works/pi-ai";
|
|
import { type BashExecutionMessage, type CustomMessage } from "./messages.ts";
|
|
export declare const CURRENT_SESSION_VERSION = 3;
|
|
export interface SessionHeader {
|
|
type: "session";
|
|
version?: number;
|
|
id: string;
|
|
timestamp: string;
|
|
cwd: string;
|
|
parentSession?: string;
|
|
}
|
|
export interface NewSessionOptions {
|
|
id?: string;
|
|
parentSession?: string;
|
|
}
|
|
export interface SessionEntryBase {
|
|
type: string;
|
|
id: string;
|
|
parentId: string | null;
|
|
timestamp: string;
|
|
}
|
|
export interface SessionMessageEntry extends SessionEntryBase {
|
|
type: "message";
|
|
message: AgentMessage;
|
|
}
|
|
export interface ThinkingLevelChangeEntry extends SessionEntryBase {
|
|
type: "thinking_level_change";
|
|
thinkingLevel: string;
|
|
}
|
|
export interface ModelChangeEntry extends SessionEntryBase {
|
|
type: "model_change";
|
|
provider: string;
|
|
modelId: string;
|
|
}
|
|
export interface CompactionEntry<T = unknown> extends SessionEntryBase {
|
|
type: "compaction";
|
|
summary: string;
|
|
firstKeptEntryId: string;
|
|
tokensBefore: number;
|
|
/** Extension-specific data (e.g., ArtifactIndex, version markers for structured compaction) */
|
|
details?: T;
|
|
/** Usage from the LLM call(s) that generated this summary, if available */
|
|
usage?: Usage;
|
|
/** True if generated by an extension, undefined/false if pi-generated (backward compatible) */
|
|
fromHook?: boolean;
|
|
}
|
|
export interface BranchSummaryEntry<T = unknown> extends SessionEntryBase {
|
|
type: "branch_summary";
|
|
fromId: string;
|
|
summary: string;
|
|
/** Extension-specific data (not sent to LLM) */
|
|
details?: T;
|
|
/** Usage from the LLM call that generated this summary, if available */
|
|
usage?: Usage;
|
|
/** True if generated by an extension, false if pi-generated */
|
|
fromHook?: boolean;
|
|
}
|
|
/**
|
|
* Custom entry for extensions to store extension-specific data in the session.
|
|
* Use customType to identify your extension's entries.
|
|
*
|
|
* Purpose: Persist extension state across session reloads. On reload, extensions can
|
|
* scan entries for their customType and reconstruct internal state.
|
|
*
|
|
* Does NOT participate in LLM context (ignored by buildSessionContext).
|
|
* For injecting content into context, see CustomMessageEntry.
|
|
*/
|
|
export interface CustomEntry<T = unknown> extends SessionEntryBase {
|
|
type: "custom";
|
|
customType: string;
|
|
data?: T;
|
|
}
|
|
/** Label entry for user-defined bookmarks/markers on entries. */
|
|
export interface LabelEntry extends SessionEntryBase {
|
|
type: "label";
|
|
targetId: string;
|
|
label: string | undefined;
|
|
}
|
|
/** Session metadata entry (e.g., user-defined display name). */
|
|
export interface SessionInfoEntry extends SessionEntryBase {
|
|
type: "session_info";
|
|
name?: string;
|
|
}
|
|
/**
|
|
* Custom message entry for extensions to inject messages into LLM context.
|
|
* Use customType to identify your extension's entries.
|
|
*
|
|
* Unlike CustomEntry, this DOES participate in LLM context.
|
|
* The content is converted to a user message in buildSessionContext().
|
|
* Use details for extension-specific metadata (not sent to LLM).
|
|
*
|
|
* display controls TUI rendering:
|
|
* - false: hidden entirely
|
|
* - true: rendered with distinct styling (different from user messages)
|
|
*/
|
|
export interface CustomMessageEntry<T = unknown> extends SessionEntryBase {
|
|
type: "custom_message";
|
|
customType: string;
|
|
content: string | (TextContent | ImageContent)[];
|
|
details?: T;
|
|
display: boolean;
|
|
}
|
|
/** Session entry - has id/parentId for tree structure (returned by "read" methods in SessionManager) */
|
|
export type SessionEntry = SessionMessageEntry | ThinkingLevelChangeEntry | ModelChangeEntry | CompactionEntry | BranchSummaryEntry | CustomEntry | CustomMessageEntry | LabelEntry | SessionInfoEntry;
|
|
/** Raw file entry (includes header) */
|
|
export type FileEntry = SessionHeader | SessionEntry;
|
|
/** Tree node for getTree() - defensive copy of session structure */
|
|
export interface SessionTreeNode {
|
|
entry: SessionEntry;
|
|
children: SessionTreeNode[];
|
|
/** Resolved label for this entry, if any */
|
|
label?: string;
|
|
/** Timestamp of the latest label change for this entry, if any */
|
|
labelTimestamp?: string;
|
|
}
|
|
export interface SessionContext {
|
|
messages: AgentMessage[];
|
|
thinkingLevel: string;
|
|
model: {
|
|
provider: string;
|
|
modelId: string;
|
|
} | null;
|
|
}
|
|
export interface SessionInfo {
|
|
path: string;
|
|
id: string;
|
|
/** Working directory where the session was started. Empty string for old sessions. */
|
|
cwd: string;
|
|
/** User-defined display name from session_info entries. */
|
|
name?: string;
|
|
/** Path to the parent session (if this session was forked). */
|
|
parentSessionPath?: string;
|
|
created: Date;
|
|
modified: Date;
|
|
messageCount: number;
|
|
firstMessage: string;
|
|
allMessagesText: string;
|
|
}
|
|
export type ReadonlySessionManager = Pick<SessionManager, "getCwd" | "getSessionDir" | "getSessionId" | "getSessionFile" | "getLeafId" | "getLeafEntry" | "getEntry" | "getLabel" | "getBranch" | "buildContextEntries" | "getHeader" | "getEntries" | "getTree" | "getSessionName">;
|
|
export declare function assertValidSessionId(id: string): void;
|
|
/** Exported for testing */
|
|
export declare function migrateSessionEntries(entries: FileEntry[]): void;
|
|
/** Exported for compaction.test.ts */
|
|
export declare function parseSessionEntries(content: string): FileEntry[];
|
|
export declare function getLatestCompactionEntry(entries: SessionEntry[]): CompactionEntry | null;
|
|
/**
|
|
* Project one selected session entry into LLM/runtime messages.
|
|
* Plain custom entries are display/state entries and do not participate in context.
|
|
*/
|
|
export declare function sessionEntryToContextMessages(entry: SessionEntry): AgentMessage[];
|
|
/**
|
|
* Build the active, compaction-aware session entry list.
|
|
*
|
|
* This follows the current leaf path. If the path contains compaction entries,
|
|
* the latest compaction is represented by the compaction entry itself, followed
|
|
* by the kept entries starting at firstKeptEntryId and all entries after the
|
|
* compaction entry. Older summarized entries are omitted.
|
|
*/
|
|
export declare function buildContextEntries(entries: SessionEntry[], leafId?: string | null, byId?: Map<string, SessionEntry>): SessionEntry[];
|
|
/**
|
|
* Build the session context from entries using tree traversal.
|
|
* If leafId is provided, walks from that entry to root.
|
|
* Handles compaction and branch summaries along the path.
|
|
*/
|
|
export declare function buildSessionContext(entries: SessionEntry[], leafId?: string | null, byId?: Map<string, SessionEntry>): SessionContext;
|
|
export declare function getDefaultSessionDir(cwd: string, agentDir?: string): string;
|
|
/** Exported for testing */
|
|
export declare function loadEntriesFromFile(filePath: string): FileEntry[];
|
|
/** Exported for testing */
|
|
export declare function findMostRecentSession(sessionDir: string, cwd?: string): string | null;
|
|
export type SessionListProgress = (loaded: number, total: number) => void;
|
|
/**
|
|
* Manages conversation sessions as append-only trees stored in JSONL files.
|
|
*
|
|
* Each session entry has an id and parentId forming a tree structure. The "leaf"
|
|
* pointer tracks the current position. Appending creates a child of the current leaf.
|
|
* Branching moves the leaf to an earlier entry, allowing new branches without
|
|
* modifying history.
|
|
*
|
|
* Use buildSessionContext() to get the resolved message list for the LLM, which
|
|
* handles compaction summaries and follows the path from root to current leaf.
|
|
*/
|
|
export declare class SessionManager {
|
|
private sessionId;
|
|
private sessionFile;
|
|
private sessionDir;
|
|
private cwd;
|
|
private persist;
|
|
private flushed;
|
|
private fileEntries;
|
|
private byId;
|
|
private labelsById;
|
|
private labelTimestampsById;
|
|
private leafId;
|
|
private constructor();
|
|
/** Switch to a different session file (used for resume and branching) */
|
|
setSessionFile(sessionFile: string): void;
|
|
private _setSessionFile;
|
|
newSession(options?: NewSessionOptions): string | undefined;
|
|
private _buildIndex;
|
|
private _rewriteFile;
|
|
isPersisted(): boolean;
|
|
getCwd(): string;
|
|
getSessionDir(): string;
|
|
usesDefaultSessionDir(): boolean;
|
|
getSessionId(): string;
|
|
getSessionFile(): string | undefined;
|
|
_persist(entry: SessionEntry): void;
|
|
private _appendEntry;
|
|
/** Append a message as child of current leaf, then advance leaf. Returns entry id.
|
|
* Does not allow writing CompactionSummaryMessage and BranchSummaryMessage directly.
|
|
* Reason: we want these to be top-level entries in the session, not message session entries,
|
|
* so it is easier to find them.
|
|
* These need to be appended via appendCompaction() and appendBranchSummary() methods.
|
|
*/
|
|
appendMessage(message: Message | CustomMessage | BashExecutionMessage): string;
|
|
/** Append a thinking level change as child of current leaf, then advance leaf. Returns entry id. */
|
|
appendThinkingLevelChange(thinkingLevel: string): string;
|
|
/** Append a model change as child of current leaf, then advance leaf. Returns entry id. */
|
|
appendModelChange(provider: string, modelId: string): string;
|
|
/** Append a compaction summary as child of current leaf, then advance leaf. Returns entry id. */
|
|
appendCompaction<T = unknown>(summary: string, firstKeptEntryId: string, tokensBefore: number, details?: T, fromHook?: boolean, usage?: Usage): string;
|
|
/** Append a custom entry (for extensions) as child of current leaf, then advance leaf. Returns entry id. */
|
|
appendCustomEntry(customType: string, data?: unknown): string;
|
|
/** Append a session info entry (e.g., display name). Returns entry id. */
|
|
appendSessionInfo(name: string): string;
|
|
/** Get the current session name from the latest session_info entry, if any. */
|
|
getSessionName(): string | undefined;
|
|
/**
|
|
* Append a custom message entry (for extensions) that participates in LLM context.
|
|
* @param customType Extension identifier for filtering on reload
|
|
* @param content Message content (string or TextContent/ImageContent array)
|
|
* @param display Whether to show in TUI (true = styled display, false = hidden)
|
|
* @param details Optional extension-specific metadata (not sent to LLM)
|
|
* @returns Entry id
|
|
*/
|
|
appendCustomMessageEntry<T = unknown>(customType: string, content: string | (TextContent | ImageContent)[], display: boolean, details?: T): string;
|
|
getLeafId(): string | null;
|
|
getLeafEntry(): SessionEntry | undefined;
|
|
getEntry(id: string): SessionEntry | undefined;
|
|
/**
|
|
* Get all direct children of an entry.
|
|
*/
|
|
getChildren(parentId: string): SessionEntry[];
|
|
/**
|
|
* Get the label for an entry, if any.
|
|
*/
|
|
getLabel(id: string): string | undefined;
|
|
/**
|
|
* Set or clear a label on an entry.
|
|
* Labels are user-defined markers for bookmarking/navigation.
|
|
* Pass undefined or empty string to clear the label.
|
|
*/
|
|
appendLabelChange(targetId: string, label: string | undefined): string;
|
|
/**
|
|
* Walk from entry to root, returning all entries in path order.
|
|
* Includes all entry types (messages, compaction, model changes, etc.).
|
|
* Use buildSessionContext() to get the resolved messages for the LLM.
|
|
*/
|
|
getBranch(fromId?: string): SessionEntry[];
|
|
/**
|
|
* Build the active, compaction-aware entry list for context/rendering.
|
|
* Uses tree traversal from current leaf.
|
|
*/
|
|
buildContextEntries(): SessionEntry[];
|
|
/**
|
|
* Build the session context (what gets sent to the LLM).
|
|
* Uses tree traversal from current leaf.
|
|
*/
|
|
buildSessionContext(): SessionContext;
|
|
/**
|
|
* Get session header.
|
|
*/
|
|
getHeader(): SessionHeader | null;
|
|
/**
|
|
* Get all session entries (excludes header). Returns a shallow copy.
|
|
* The session is append-only: use appendXXX() to add entries, branch() to
|
|
* change the leaf pointer. Entries cannot be modified or deleted.
|
|
*/
|
|
getEntries(): SessionEntry[];
|
|
/**
|
|
* Get the session as a tree structure. Returns a shallow defensive copy of all entries.
|
|
* A well-formed session has exactly one root (first entry with parentId === null).
|
|
* Orphaned entries (broken parent chain) are also returned as roots.
|
|
*/
|
|
getTree(): SessionTreeNode[];
|
|
/**
|
|
* Start a new branch from an earlier entry.
|
|
* Moves the leaf pointer to the specified entry. The next appendXXX() call
|
|
* will create a child of that entry, forming a new branch. Existing entries
|
|
* are not modified or deleted.
|
|
*/
|
|
branch(branchFromId: string): void;
|
|
/**
|
|
* Reset the leaf pointer to null (before any entries).
|
|
* The next appendXXX() call will create a new root entry (parentId = null).
|
|
* Use this when navigating to re-edit the first user message.
|
|
*/
|
|
resetLeaf(): void;
|
|
/**
|
|
* Start a new branch with a summary of the abandoned path.
|
|
* Same as branch(), but also appends a branch_summary entry that captures
|
|
* context from the abandoned conversation path.
|
|
*/
|
|
branchWithSummary(branchFromId: string | null, summary: string, details?: unknown, fromHook?: boolean, usage?: Usage): string;
|
|
/**
|
|
* Create a new session file containing only the path from root to the specified leaf.
|
|
* Useful for extracting a single conversation path from a branched session.
|
|
* Returns the new session file path, or undefined if not persisting.
|
|
*/
|
|
createBranchedSession(leafId: string): string | undefined;
|
|
/**
|
|
* Create a new session.
|
|
* @param cwd Working directory (stored in session header)
|
|
* @param sessionDir Optional session directory. If omitted, uses default (~/.pi/agent/sessions/<encoded-cwd>/).
|
|
*/
|
|
static create(cwd: string, sessionDir?: string, options?: NewSessionOptions): SessionManager;
|
|
/**
|
|
* Open a specific session file.
|
|
* @param path Path to session file
|
|
* @param sessionDir Optional session directory for /new or /branch. If omitted, derives from file's parent.
|
|
* @param cwdOverride Optional cwd override instead of the session header cwd.
|
|
*/
|
|
static open(path: string, sessionDir?: string, cwdOverride?: string): SessionManager;
|
|
/**
|
|
* Continue the most recent session, or create new if none.
|
|
* @param cwd Working directory
|
|
* @param sessionDir Optional session directory. If omitted, uses default (~/.pi/agent/sessions/<encoded-cwd>/).
|
|
*/
|
|
static continueRecent(cwd: string, sessionDir?: string): SessionManager;
|
|
/** Create an in-memory session (no file persistence) */
|
|
static inMemory(cwd?: string, options?: NewSessionOptions): SessionManager;
|
|
/**
|
|
* Fork a session from another project directory into the current project.
|
|
* Creates a new session in the target cwd with the full history from the source session.
|
|
* @param sourcePath Path to the source session file
|
|
* @param targetCwd Target working directory (where the new session will be stored)
|
|
* @param sessionDir Optional session directory. If omitted, uses default for targetCwd.
|
|
*/
|
|
static forkFrom(sourcePath: string, targetCwd: string, sessionDir?: string, options?: NewSessionOptions): SessionManager;
|
|
/**
|
|
* List all sessions for a directory.
|
|
* @param cwd Working directory (used to compute default session directory)
|
|
* @param sessionDir Optional session directory. If omitted, uses default (~/.pi/agent/sessions/<encoded-cwd>/).
|
|
* @param onProgress Optional callback for progress updates (loaded, total)
|
|
*/
|
|
static list(cwd: string, sessionDir?: string, onProgress?: SessionListProgress): Promise<SessionInfo[]>;
|
|
/**
|
|
* List all sessions across all project directories.
|
|
* @param onProgress Optional callback for progress updates (loaded, total)
|
|
*/
|
|
static listAll(onProgress?: SessionListProgress): Promise<SessionInfo[]>;
|
|
static listAll(sessionDir?: string, onProgress?: SessionListProgress): Promise<SessionInfo[]>;
|
|
}
|
|
//# sourceMappingURL=session-manager.d.ts.map
|