import type { Message, Provider } from "@kenkaiiii/gg-ai"; import { type SessionManager } from "./session-manager.js"; /** * Parse a foreign coding-agent transcript (Claude Code, Codex, Cursor) into * GG Coder messages so the thread can be resumed here. * * `project-discovery.ts` already walks `~/.claude/projects` and * `~/.codex/sessions`, but only to recover a cwd. This module reads the message * records themselves. * * Import is lossy **by design**: anything we cannot map faithfully is dropped * and counted, never fabricated. `ForeignImportResult.dropped` reports exactly * what was lost so the caller can record it on the imported session. */ export type ForeignFormat = "claude" | "codex" | "cursor"; export interface ForeignImportDropped { /** Tool results whose call we could not pair, or whose payload was unmappable. */ toolResults: number; /** Records recognized as messages but with no usable content. */ messages: number; /** Lines that were not valid JSON or not a known record shape. */ records: number; } export interface ForeignImportResult { format: ForeignFormat; messages: Message[]; /** Working directory recorded by the source agent, when it stored one. */ cwd?: string; /** First user prompt, used as the session preview/title. */ preview?: string; /** Epoch millis of the earliest record, when the source recorded timestamps. */ startedAt?: number; dropped: ForeignImportDropped; } /** Human-readable summary of what an import discarded. */ export declare function describeDropped(dropped: ForeignImportDropped): string; /** * Identify a transcript from its records. Detection is shape-based, not * filename-based, so a transcript copied out of its home directory still * imports correctly. */ export declare function detectForeignFormat(text: string): ForeignFormat | undefined; export declare function parseForeignTranscript(text: string, format?: ForeignFormat): ForeignImportResult; /** * Claude Code writes one JSONL record per DAG node under * `~/.claude/projects//.jsonl`. Message records are * `{ type: "user"|"assistant", message: { role, content }, uuid, cwd, timestamp }` * where `content` is a string or an Anthropic content-block array. Non-message * record types (system, file-history-snapshot, mode, …) are UI state, not * conversation, and are skipped without counting as data loss. */ export declare function parseClaudeTranscript(text: string): ForeignImportResult; /** * Codex rollouts live at `~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl`. * Current records are `{ timestamp, type, payload }` where `type` is * `session_meta` / `response_item` / `event_msg` / `turn_context`; older * rollouts wrote the payload at the top level with no wrapper. * * Only `response_item` carries the model conversation. `event_msg` duplicates * it for the UI, so importing both would double every turn. */ export declare function parseCodexTranscript(text: string): ForeignImportResult; /** * Cursor exports a flat JSONL chat log: one record per turn, carrying a role * and either a plain `text`/`content` string or a content-block array. * * The part that actually matters on import is the wrapping: Cursor sends the * typed prompt inside ``, preceded by context blocks such as * `` and ``. Importing that verbatim leaks harness * scaffolding into the session title and preview, so a recognized wrapper is * stripped while a message with UNRECOGNIZED leading context is kept intact — * that text may be something the user genuinely wrote. */ export declare function parseCursorTranscript(text: string): ForeignImportResult; export interface ImportedSession { sessionId: string; sessionPath: string; cwd: string; messageCount: number; format: ForeignFormat; dropped: ForeignImportDropped; preview?: string; } export interface ImportForeignSessionOptions { /** Path to the foreign JSONL transcript. */ filePath: string; sessionManager: SessionManager; provider: Provider; model: string; /** Overrides the cwd recorded by the source agent. */ cwd?: string; /** Skips detection when the caller already knows the format. */ format?: ForeignFormat; } /** * Read a foreign transcript and write it out as a real GG Coder session, so it * resumes through the existing loader with no reader changes: a v2 * `SessionHeader` from `SessionManager.create()`, one `MessageEntry` per * message chained through `parentId`, and a final leaf pointer. * * An `import` app marker records the source and everything the import dropped — * import is lossy by definition, and a silent loss is worse than a noted one. */ export declare function importForeignSession(opts: ImportForeignSessionOptions): Promise; /** Never-throwing result shape shared by the CLI, the sidecar and the app. */ export type ImportForeignTranscriptResult = { ok: true; sessionId: string; sessionPath: string; cwd: string; format: ForeignFormat; messageCount: number; /** Human-readable summary of what the lossy import discarded. */ dropped: string; preview?: string; } | { ok: false; error: string; }; //# sourceMappingURL=foreign-session-import.d.ts.map