interface TextBlock { type: 'text'; text: string; } interface ThinkingBlock { type: 'thinking'; thinking: string; signature: string; } interface ToolUseBlock { type: 'tool_use'; id: string; name: string; input: unknown; } interface ToolResultBlock { type: 'tool_result'; tool_use_id: string; content: string | ContentBlock[]; is_error?: boolean; } interface ImageBlock { type: 'image'; source: { type: 'base64'; media_type: string; data: string; } | { type: 'url'; url: string; }; } type ContentBlock = TextBlock | ThinkingBlock | ToolUseBlock | ToolResultBlock | ImageBlock; /** Blocks valid in a user message. `thinking` and `tool_use` are assistant-only. */ type UserContentBlock = TextBlock | ToolResultBlock | ImageBlock; interface Message { role: 'user' | 'assistant'; content: string | ContentBlock[]; } interface AssistantMessagePayload { id: string; type: 'message'; role: 'assistant'; model: string; content: ContentBlock[]; stop_reason: string | null; stop_sequence: string | null; usage?: { input_tokens: number; output_tokens: number; }; } interface UserMessagePayload { role: 'user'; content: string | ContentBlock[]; } interface BaseRecordFields { uuid: string; parentUuid: string | null; sessionId: string; timestamp: string; isSidechain: boolean; cwd: string; userType: string; version: string; gitBranch: string; slug: string; entrypoint: string; } interface UserRecord extends BaseRecordFields { type: 'user'; message: UserMessagePayload; promptId?: string; permissionMode?: string; isMeta?: boolean; } interface AssistantRecord extends BaseRecordFields { type: 'assistant'; message: AssistantMessagePayload; requestId?: string; } /** * Claude Code's own injected context, written as its own record rather than as * part of a message: an `@file` expansion, a skill listing, a task reminder. * The payload shape varies by `attachment.type` and is left open — only `file` * and `edited_text_file` carry content a consumer cannot regenerate, and the * rest Claude Code rewrites every turn. * * An attachment is a link in the uuid chain, not a leaf hanging off it: it * parents to the record that came before, and the record that follows parents * to the attachment. */ interface AttachmentRecord extends BaseRecordFields { type: 'attachment'; attachment: { type: string; [key: string]: unknown; }; } interface UnknownRecord { type: string; [key: string]: unknown; } type JsonlRecord = UserRecord | AssistantRecord | AttachmentRecord | UnknownRecord; interface ToolCallSpec { name: string; input: unknown; result: string | ContentBlock[]; isError?: boolean; } /** * An attachment to emit while importing, positioned after the message at * `afterIndex` in the array handed to `importMessages`. If the repair drops that * message the attachment is not emitted, so check `session.attachments` when it * matters. An out-of-range index logs a warning and is not emitted. */ interface ImportAttachment { afterIndex: number; attachment: { type: string; [key: string]: unknown; }; } interface CreateSessionOptions { projectPath: string; claudeDir?: string; cwd?: string; gitBranch?: string; version?: string; model?: string; sessionId?: string; } interface OpenSessionOptions { sessionId: string; projectPath: string; claudeDir?: string; } declare class Session { readonly sessionId: string; readonly projectPath: string; readonly jsonlPath: string; private _records; private _pendingRecords; private _lastUuid; private _slug; private _cwd; private _version; private _gitBranch; private _model; private _fileExists; private _nextTimestamp; constructor(opts: { sessionId: string; projectPath: string; jsonlPath: string; slug?: string; cwd?: string; version?: string; gitBranch?: string; model?: string; records?: JsonlRecord[]; fileExists?: boolean; }); /** All records (existing + pending). */ get records(): readonly JsonlRecord[]; /** Only user and assistant message records. */ get messages(): readonly (UserRecord | AssistantRecord)[]; /** Only attachment records — Claude Code's injected context (`@file` * expansions, skill listings, task reminders). */ get attachments(): readonly AttachmentRecord[]; private baseFields; /** Add a user message, as plain text or content blocks. Returns its uuid. */ addUserMessage(content: string | UserContentBlock[]): string; /** * Append a record verbatim. The escape hatch for record types this library * does not model — `queue-operation`, `last-prompt`, anything a future Claude * Code release adds — and for carrying records from one session into another. * * Dangerous because nothing is synthesized or checked beyond `sessionId` and * the parent link: the shape is whatever you pass, and Claude Code will read * it back. `sessionId` is overwritten with this session's, since a record * claiming another session is never what a caller wants. The record does not * join the uuid chain, so a following message still parents to the last real * message. * * The one guard is on `parentUuid`, because that is the failure that does not * announce itself: a dangling parent makes Claude Code resume with empty * context and answer confidently from nothing. * * Carrying an *attachment* into another session should use `addAttachment` * instead: this method does not advance the chain, so an attachment appended * through it becomes a leaf the next message skips over. */ dangerousAppendRecord(record: JsonlRecord, opts?: { parentUuid?: string | null; }): void; /** * Add an attachment record. `parentUuid` defaults to the record this session * would currently chain from; pass it explicitly when re-attaching a record * carried over from another session, where the message it belongs to has been * given a new uuid. Returns the new record's uuid. * * Attachments are links in the uuid chain, not leaves hanging off it: the * record that follows one parents to the attachment, so this advances the * chain exactly as the message methods do. Measured across 605 real sessions — * 3,526 records chain through an attachment, none skip it. */ addAttachment(attachment: { type: string; [key: string]: unknown; }, opts?: { parentUuid?: string | null; }): string; /** Add an assistant message with the given content blocks. Returns its uuid. */ addAssistantMessage(content: ContentBlock[], opts?: { model?: string; stopReason?: string; }): string; /** Add a user message containing tool results. Returns its uuid. */ addToolResults(results: { toolUseId: string; content: string | ContentBlock[]; isError?: boolean; }[]): string; /** * Convenience: add a complete tool call round-trip. * Creates assistant tool_use message, user tool_result message, * and optionally a final assistant text response. */ addToolCalls(calls: ToolCallSpec[], opts?: { response?: ContentBlock[]; model?: string; }): void; /** * Import an array of Anthropic API-shaped messages, dispatching each to the * appropriate internal method based on role and content type. */ importMessages(messages: Message[], opts?: { attachments?: ImportAttachment[]; }): void; /** * Reset this session to empty state and delete any on-disk artifacts. * The sessionId and jsonlPath are preserved so subsequent writes reuse them. */ clear(): void; /** Write pending records to disk. Creates the file/directory if needed. */ save(): void; } /** Delete a session by ID without needing an open Session instance. */ declare function deleteSession(sessionId: string, projectPath: string, claudeDir?: string): void; /** Create a new empty session. */ declare function createSession(opts: CreateSessionOptions): Session; /** Open an existing session by ID. */ declare function openSession(opts: OpenSessionOptions): Session; /** Read a session from a JSONL file path. */ declare function readSession(jsonlPath: string, projectPath?: string): Session; /** Parse a JSONL string into an array of records. */ declare function parseJsonl(content: string): JsonlRecord[]; /** Parse a JSONL file from disk. */ declare function parseJsonlFile(path: string): JsonlRecord[]; /** Serialize a single record to a JSON line (no trailing newline). */ declare function serializeRecord(record: JsonlRecord): string; /** Serialize an array of records to a JSONL string. */ declare function serializeJsonl(records: JsonlRecord[]): string; /** * Repair tool_use / tool_result pairing in an Anthropic-format message array. * * Handles three cases: * 1. Assistant tool_use with no matching tool_result → injects synthetic error result * 2. Orphan tool_result with no preceding tool_use → dropped * 3. Consecutive assistant messages → flushes pending results between them */ declare function repairToolPairing(messages: Message[]): Message[]; /** * The same repair, plus provenance: `origin[i]` is the index in `messages` that * output message `i` came from, or `null` for one the repair synthesized. An * input message the repair dropped has no entry, so a caller keying anything to * input positions must handle its position being absent. */ declare function repairWithOrigin(messages: Message[]): { messages: Message[]; origin: (number | null)[]; }; declare function getClaudeDir(claudeDir?: string): string; /** * Normalize a project path the same way Claude Code does at startup: * resolve symlinks via `realpathSync`, then NFC-normalize. This is critical * because CC stores sessions under `~/.claude/projects//`, * so any caller that passes an unresolved path (e.g. `process.cwd()` from a * shell that entered via a symlink) writes to a different directory than CC * reads from. On macOS this hits often: `/tmp` → `/private/tmp`, `/var` → * `/private/var`, plus user-level symlinked project dirs. * * Falls back to NFC-normalizing the raw path if `realpathSync` throws (e.g. * the path doesn't exist yet, or EPERM on CloudStorage mounts) — matches CC's * own try/catch behavior in src/bootstrap/state.ts. */ declare function normalizeProjectPath(projectPath: string): string; /** * Convert an absolute project path to the hash CC uses for directory names. * Matches the CLI's sanitization: replace all non-alphanumeric chars with dashes, * and truncate long paths with a hash suffix. */ declare function projectPathToHash(projectPath: string): string; /** Get the project-specific directory under ~/.claude/projects/ */ declare function getProjectDir(projectPath: string, claudeDir?: string): string; /** Get the JSONL file path for a session. */ declare function getSessionPath(sessionId: string, projectPath: string, claudeDir?: string): string; export { type AssistantMessagePayload, type AssistantRecord, type AttachmentRecord, type BaseRecordFields, type ContentBlock, type CreateSessionOptions, type ImageBlock, type ImportAttachment, type JsonlRecord, type Message, type OpenSessionOptions, Session, type TextBlock, type ThinkingBlock, type ToolCallSpec, type ToolResultBlock, type ToolUseBlock, type UnknownRecord, type UserContentBlock, type UserMessagePayload, type UserRecord, createSession, deleteSession, getClaudeDir, getProjectDir, getSessionPath, normalizeProjectPath, openSession, parseJsonl, parseJsonlFile, projectPathToHash, readSession, repairToolPairing, repairWithOrigin, serializeJsonl, serializeRecord };