Captured Event Envelope
Every event captured by Codaph uses a canonical envelope structure defined in src/lib/core-types.ts:
interface CapturedEventEnvelope {
eventId : string ; // Unique event identifier (SHA-256 hash)
source : AgentSource ; // Event origin (codex_sdk, claude_code_history, etc.)
repoId : string ; // Project identifier
actorId : string | null ; // Contributor identifier
sessionId : string ; // Session identifier
threadId : string | null ; // Thread/conversation identifier
ts : string ; // ISO 8601 timestamp
eventType : string ; // Event type (e.g., 'thread.message.created')
payload : Record < string , unknown >; // Event-specific payload
reasoningAvailability : ReasoningAvailability ; // 'full' | 'partial' | 'unavailable'
}
Event ID Generation
Event IDs are deterministic hashes to enable deduplication:
function createEventId ( input : {
source : AgentSource ;
threadId : string | null ;
sequence : number ;
eventType : string ;
ts : string ;
}) : string {
const raw = [
input . source ,
input . threadId ?? 'no-thread' ,
String ( input . sequence ),
input . eventType ,
input . ts ,
]. join ( '|' );
return createHash ( 'sha256' ). update ( raw ). digest ( 'hex' ). slice ( 0 , 24 );
}
The 24-character event ID ensures uniqueness while keeping the index compact.
Agent Sources
Events can originate from multiple sources:
type AgentSource =
| "codex_sdk" // Live Codex SDK streaming
| "codex_exec" // codex exec --json output
| "codex_history" // ~/.codex/sessions import
| "claude_code_history" // Claude Code transcript import
| "gemini_cli_history" ; // Gemini CLI transcript import
Live Capture
Exec Wrapper
History Import
Source : codex_sdkEvents captured from live Codex SDK runs using CodexSdkAdapter: codaph run "Add authentication"
Captures events in real-time as the agent executes. Source : codex_execEvents parsed from codex exec --json output: codaph exec "Refactor auth module"
Spawns codex as a subprocess and parses JSONL output. Sources : codex_history, claude_code_history, gemini_cli_historyEvents imported from local agent history: codaph push --providers codex,claude-code,gemini
Scans ~/.codex/sessions, Claude transcripts, and Gemini CLI history.
Reasoning Availability
Indicates whether agent reasoning is included in the event:
type ReasoningAvailability = 'full' | 'partial' | 'unavailable' ;
full : Complete reasoning text available in payload
partial : Reasoning metadata present but content missing
unavailable : No reasoning data in this event
The local mirror stores events in JSONL (JSON Lines) format under .codaph/:
Directory Structure
.codaph/
├── manifest.json # Segment registry with checksums
├── sparse-index.jsonl # Session → segment mapping
├── eventid-index.jsonl # Event ID deduplication index
├── sync-lock.json # Sync operation lock
├── project.json # Project configuration
└── segments/
├── <session-id-1>.jsonl
├── <session-id-2>.jsonl
└── ...
Manifest Structure
From src/lib/mirror-jsonl.ts, the manifest tracks all segments:
{
"schema" : "codaph.mirror.v2" ,
"segments" : [
{
"sessionId" : "abc123..." ,
"segment" : "abc123.jsonl" ,
"checksum" : "sha256:..." ,
"eventCount" : 42 ,
"firstTs" : "2024-03-01T10:00:00Z" ,
"lastTs" : "2024-03-01T10:15:00Z"
}
]
}
Enables fast session lookups without scanning all segments:
{ "sessionId" : "abc123" , "segment" : "abc123.jsonl" , "eventCount" : 42 }
{ "sessionId" : "def456" , "segment" : "def456.jsonl" , "eventCount" : 18 }
Event ID Index
Tracks all event IDs for deduplication:
{ "eventId" : "a1b2c3d4e5f6g7h8i9j0k1l2" , "ts" : "2024-03-01T10:00:00Z" }
{ "eventId" : "b2c3d4e5f6g7h8i9j0k1l2m3" , "ts" : "2024-03-01T10:00:01Z" }
Event Segment Example
Each segment contains one or more events as JSONL:
{ "eventId" : "a1b2c3..." , "source" : "codex_sdk" , "repoId" : "owner/repo" , "actorId" : "alice" , "sessionId" : "abc123" , "threadId" : "thread_1" , "ts" : "2024-03-01T10:00:00Z" , "eventType" : "thread.message.created" , "payload" :{ "item" :{ "type" : "message" , "text" : "Add authentication" }}, "reasoningAvailability" : "unavailable" }
{ "eventId" : "b2c3d4..." , "source" : "codex_sdk" , "repoId" : "owner/repo" , "actorId" : "alice" , "sessionId" : "abc123" , "threadId" : "thread_1" , "ts" : "2024-03-01T10:00:05Z" , "eventType" : "thread.item.created" , "payload" :{ "item" :{ "type" : "reasoning" , "text" : "I'll add JWT-based authentication..." }}, "reasoningAvailability" : "full" }
Mubit Integration
Codaph writes events to Mubit for shared memory and semantic query.
Run Scopes
From src/lib/memory-mubit.ts, Mubit supports two run scopes:
Run ID : codaph:<projectId>One shared run across all contributors and sessions: const runId = mubitRunIdForProject ( 'owner/repo' );
// => 'codaph:owner/repo'
Best for : Team collaboration, shared memoryRun ID : codaph:<projectId>:<sessionId>Separate run per captured session: const runId = mubitRunIdForSession ( 'owner/repo' , 'abc123' );
// => 'codaph:owner/repo:abc123'
Best for : Isolated experimentation, per-session analysis
Event Ingestion
Events are written to Mubit’s control API as codaph_event activities:
const memory = new MubitMemoryEngine ({
apiKey: process . env . MUBIT_API_KEY ,
projectId: 'owner/repo' ,
actorId: 'alice' ,
runScope: 'project'
});
await memory . writeEvent ({
eventId: 'a1b2c3...' ,
source: 'codex_sdk' ,
repoId: 'owner/repo' ,
actorId: 'alice' ,
sessionId: 'abc123' ,
threadId: 'thread_1' ,
ts: '2024-03-01T10:00:00Z' ,
eventType: 'thread.message.created' ,
payload: { item: { type: 'message' , text: 'Add auth' } },
reasoningAvailability: 'unavailable'
});
Semantic Query
Mubit provides semantic search over captured events:
codaph mubit query "what changed in authentication?" --session abc123
Optionally uses OpenAI for synthesis over Mubit evidence:
const response = await memory . query ({
question: 'what changed in authentication?' ,
sessionId: 'abc123' ,
agentModel: 'gpt-4.1-mini' // Optional OpenAI synthesis
});
Deduplication Strategy
Codeph uses a two-phase deduplication approach:
Local deduplication
Check event ID index before appending to mirror const appendResult = await mirror . appendEvent ( event );
if ( appendResult . deduplicated ) {
// Event already exists in local mirror
return ;
}
Mubit deduplication
Mubit’s backend deduplicates based on event ID and run ID Server-side dedup prevents duplicate memory writes even if multiple contributors push the same history.
During bulk sync (codaph push), the pipeline can retry Mubit writes for locally-deduped events to ensure cloud sync is complete.
Timeline Filters
Query the local mirror with filters:
interface TimelineFilter {
repoId : string ;
sessionId ?: string ;
threadId ?: string ;
actorId ?: string ; // Filter by contributor
from ?: string ; // ISO 8601 timestamp
to ?: string ; // ISO 8601 timestamp
itemType ?: string ; // Filter by payload item type
}
Example:
const timeline = await query . getTimeline ({
repoId: 'owner/repo' ,
sessionId: 'abc123' ,
actorId: 'alice' ,
from: '2024-03-01T00:00:00Z'
});
See Also
Architecture Dual-store design and data flow
Core Types API TypeScript type definitions
Mirror JSONL API Local storage implementation
Mubit Memory API Cloud memory engine