Skip to main content

Overview

The history sync modules import past agent sessions from local history files into the Codaph mirror and memory stores. Each provider has a dedicated sync function.

Codex History Sync

syncCodexHistory

Imports Codex session history from ~/.codex/sessions/.
async function syncCodexHistory(
  options: SyncCodexHistoryOptions
): Promise<CodexHistorySyncSummary>
options.projectPath
string
required
Project root path to match against session CWDs
options.projectPaths
string[]
Additional project paths to match (for multi-root workspaces)
options.pipeline
IngestPipeline
required
Ingest pipeline for event processing
options.repoId
string
Repository ID (auto-generated if not provided)
options.actorId
string | null
Actor ID to assign to imported events
options.codexSessionsRoot
string
Custom path to Codex sessions directory (defaults to ~/.codex/sessions)
options.mirrorRoot
string
Custom mirror root (defaults to .codaph in project)
options.onProgress
(progress: CodexHistorySyncProgress) => void
Progress callback invoked periodically during sync
scannedFiles
number
Total JSONL files scanned
matchedFiles
number
Files that matched the project path
importedEvents
number
Total events imported
importedSessions
number
Unique sessions imported

Claude Code History Sync

syncClaudeHistory

Imports Claude Code history from ~/.claude/projects/.
async function syncClaudeHistory(
  options: SyncClaudeHistoryOptions
): Promise<ClaudeHistorySyncSummary>
options.projectPath
string
required
Project root path to match
options.projectPaths
string[]
Additional project paths
options.pipeline
IngestPipeline
required
Ingest pipeline
options.repoId
string
Repository ID
options.actorId
string | null
Actor ID for events
options.claudeProjectsRoot
string
Custom Claude projects directory (defaults to ~/.claude/projects)
options.mirrorRoot
string
Mirror root directory
options.onProgress
(progress: ClaudeHistorySyncProgress) => void
Progress callback
scannedFiles
number
Total files scanned
matchedFiles
number
Files matching project
importedEvents
number
Events imported
importedSessions
number
Sessions imported

Gemini CLI History Sync

syncGeminiHistory

Imports Gemini CLI history from ~/.gemini/history/ and ~/.gemini/tmp/.
async function syncGeminiHistory(
  options: SyncGeminiHistoryOptions
): Promise<GeminiHistorySyncSummary>
options.projectPath
string
required
Project root path
options.projectPaths
string[]
Additional project paths
options.pipeline
IngestPipeline
required
Ingest pipeline
options.repoId
string
Repository ID
options.actorId
string | null
Actor ID
options.geminiHistoryRoot
string
Custom Gemini history directory (defaults to ~/.gemini/history)
options.mirrorRoot
string
Mirror root
options.onProgress
(progress: GeminiHistorySyncProgress) => void
Progress callback
scannedFiles
number
Files scanned
matchedFiles
number
Files matched
importedEvents
number
Events imported
importedSessions
number
Sessions imported

Common Patterns

Incremental Sync

All sync functions maintain state to avoid re-importing:
  • File-level tracking: Uses size and mtime to detect unchanged files
  • Line/entry cursors: Resumes from last processed line
  • State persistence: Stores sync state in .codaph/index/{repoId}/{provider}-history-sync.json

Project Matching

History files are matched against the project using:
  1. CWD extraction: Reads session working directory from history metadata
  2. Path normalization: Resolves symlinks and canonicalizes paths
  3. Multi-root support: Matches against all configured project paths

Event Projection

Each provider transforms native history format to Codaph events:
ProviderSource FormatProjected Events
Codexsession_meta, response_item, event_msgthread.started, prompt.submitted, item.completed
Claudeuser, assistant messagesprompt.submitted, item.completed (reasoning, messages)
GeminiTool calls, message pairsprompt.submitted, item.completed (messages, file changes)

Usage Example

import { syncCodexHistory } from './codex-history-sync';
import { syncClaudeHistory } from './claude-history-sync';
import { syncGeminiHistory } from './gemini-history-sync';
import { IngestPipeline } from './lib/ingest-pipeline';
import { JsonlMirror } from './lib/mirror-jsonl';

const mirror = new JsonlMirror('.codaph');
const pipeline = new IngestPipeline(mirror);

// Sync all providers
const results = await Promise.all([
  syncCodexHistory({
    projectPath: process.cwd(),
    pipeline,
    actorId: 'alice',
    onProgress: (p) => {
      console.log(`Codex: ${p.importedEvents} events`);
    },
  }),
  syncClaudeHistory({
    projectPath: process.cwd(),
    pipeline,
    actorId: 'alice',
  }),
  syncGeminiHistory({
    projectPath: process.cwd(),
    pipeline,
    actorId: 'alice',
  }),
]);

await pipeline.flush();

const [codex, claude, gemini] = results;
console.log('Imported:');
console.log(`  Codex: ${codex.importedEvents} events, ${codex.importedSessions} sessions`);
console.log(`  Claude: ${claude.importedEvents} events, ${claude.importedSessions} sessions`);
console.log(`  Gemini: ${gemini.importedEvents} events, ${gemini.importedSessions} sessions`);

Progress Tracking

import type { CodexHistorySyncProgress } from './codex-history-sync';

await syncCodexHistory({
  projectPath: '.',
  pipeline,
  onProgress: (progress: CodexHistorySyncProgress) => {
    const pct = (progress.processedFiles / progress.scannedFiles) * 100;
    console.log(`${pct.toFixed(1)}% - ${progress.currentFile}`);
    console.log(`  Line ${progress.currentLine}, Session: ${progress.currentSessionId}`);
    console.log(`  Matched: ${progress.matchedFiles}, Events: ${progress.importedEvents}`);
  },
});

Build docs developers (and LLMs) love