import type { AgentMessage } from "@oh-my-pi/pi-agent-core"; import type { ImageContent, MessageAttribution, ServiceTier, TextContent } from "@oh-my-pi/pi-ai"; export const CURRENT_SESSION_VERSION = 3; export const EPHEMERAL_MODEL_CHANGE_ROLE = "fallback"; export interface SessionHeader { type: "session"; version?: number; // v1 sessions don't have this id: string; title?: string; // Auto-generated title from first message titleSource?: "auto" | "user"; timestamp: string; cwd: string; parentSession?: string; } export interface NewSessionOptions { parentSession?: string; /** Skip flushing the current session and delete it instead of saving. */ drop?: boolean; } 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 | null; /** * The user-configured selector at the time of this change: `"auto"` when auto * mode was active, otherwise the concrete level. Absent on entries written * before auto-mode persistence existed; readers fall back to `thinkingLevel`. */ configured?: string | null; } export interface ModelChangeEntry extends SessionEntryBase { type: "model_change"; /** Model in "provider/modelId" format */ model: string; /** Role: "default", "smol", "slow", etc. Undefined treated as "default" */ role?: string; } export interface ServiceTierChangeEntry extends SessionEntryBase { type: "service_tier_change"; serviceTier: ServiceTier | null; } export interface CompactionEntry extends SessionEntryBase { type: "compaction"; summary: string; shortSummary?: string; firstKeptEntryId: string; tokensBefore: number; /** Extension-specific data (e.g., ArtifactIndex, version markers for structured compaction) */ details?: T; /** Hook-provided data to persist across compaction */ preserveData?: Record; /** True if generated by an extension, undefined/false if pi-generated (backward compatible) */ fromExtension?: boolean; } export interface BranchSummaryEntry extends SessionEntryBase { type: "branch_summary"; fromId: string; summary: string; /** Extension-specific data (not sent to LLM) */ details?: T; /** True if generated by an extension, false if pi-generated */ fromExtension?: 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 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; } /** TTSR injection entry - tracks which time-traveling rules have been injected this session. */ export interface TtsrInjectionEntry extends SessionEntryBase { type: "ttsr_injection"; /** Names of rules that were injected */ injectedRules: string[]; } /** Persisted MCP discovery selection state for a session branch. */ export interface MCPToolSelectionEntry extends SessionEntryBase { type: "mcp_tool_selection"; /** MCP tool names selected for visibility in discovery mode. */ selectedToolNames: string[]; } /** Session init entry - captures initial context for subagent sessions (debugging/replay). */ export interface SessionInitEntry extends SessionEntryBase { type: "session_init"; /** Full system prompt sent to the model */ systemPrompt: string; /** Initial task/user message */ task: string; /** Tools available to the agent */ tools: string[]; /** Output schema if structured output was requested */ outputSchema?: unknown; /** Spawn allowlist the subagent ran with ("" = none, "*" = any, else CSV); absent on pre-spawns files. */ spawns?: string; /** The agent's `readSummarize` setting (`false` = read summarization disabled); absent uses the session default. */ readSummarize?: boolean; } /** Mode change entry - tracks agent mode transitions (e.g. plan mode). */ export interface ModeChangeEntry extends SessionEntryBase { type: "mode_change"; /** Current mode name, or "none" when exiting a mode */ mode: string; /** Optional mode-specific data (e.g. plan file path) */ data?: Record; } /** * 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 participates in LLM context through convertToLlm(). * 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 extends SessionEntryBase { type: "custom_message"; customType: string; content: string | (TextContent | ImageContent)[]; details?: T; display: boolean; /** Who initiated this message for billing/attribution semantics. */ attribution?: MessageAttribution; } /** Session entry - has id/parentId for tree structure (returned by "read" methods in SessionManager) */ export type SessionEntry = | SessionMessageEntry | ThinkingLevelChangeEntry | ModelChangeEntry | ServiceTierChangeEntry | CompactionEntry | BranchSummaryEntry | CustomEntry | CustomMessageEntry | LabelEntry | TtsrInjectionEntry | MCPToolSelectionEntry | SessionInitEntry | ModeChangeEntry; /** 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; } export interface UsageStatistics { input: number; output: number; cacheRead: number; cacheWrite: number; premiumRequests: number; cost: number; }