Documentation
¶
Index ¶
- Variables
- func ListAdapters() []string
- func Register(adapter Adapter)
- func ResetRegistry()
- type Adapter
- type BatchTransformFunc
- type ClaudeCodeAdapter
- func (a *ClaudeCodeAdapter) Detect() bool
- func (a *ClaudeCodeAdapter) FindSessionFile(agentID string, since time.Time) (string, error)
- func (a *ClaudeCodeAdapter) Name() string
- func (a *ClaudeCodeAdapter) Read(sessionPath string) ([]RawEntry, error)
- func (a *ClaudeCodeAdapter) ReadFromOffset(path string, offset int64) ([]RawEntry, int64, error)
- func (a *ClaudeCodeAdapter) ReadMetadata(sessionPath string) (*SessionMetadata, error)
- func (a *ClaudeCodeAdapter) Watch(ctx context.Context, sessionPath string) (<-chan RawEntry, error)
- type CodexAdapter
- func (a *CodexAdapter) Detect() bool
- func (a *CodexAdapter) FindSessionFile(agentID string, since time.Time) (string, error)
- func (a *CodexAdapter) Name() string
- func (a *CodexAdapter) Read(sessionPath string) ([]RawEntry, error)
- func (a *CodexAdapter) ReadFromOffset(path string, offset int64) ([]RawEntry, int64, error)
- func (a *CodexAdapter) ReadMetadata(sessionPath string) (*SessionMetadata, error)
- func (a *CodexAdapter) Watch(ctx context.Context, sessionPath string) (<-chan RawEntry, error)
- type GeminiAdapter
- func (a *GeminiAdapter) Detect() bool
- func (a *GeminiAdapter) FindSessionFile(agentID string, since time.Time) (string, error)
- func (a *GeminiAdapter) Name() string
- func (a *GeminiAdapter) Read(sessionPath string) ([]RawEntry, error)
- func (a *GeminiAdapter) ReadFromOffset(path string, offset int64) ([]RawEntry, int64, error)
- func (a *GeminiAdapter) ReadMetadata(sessionPath string) (*SessionMetadata, error)
- func (a *GeminiAdapter) Watch(ctx context.Context, sessionPath string) (<-chan RawEntry, error)
- type GenericJSONLAdapter
- func (a *GenericJSONLAdapter) Detect() bool
- func (a *GenericJSONLAdapter) FindSessionFile(_ string, _ time.Time) (string, error)
- func (a *GenericJSONLAdapter) Name() string
- func (a *GenericJSONLAdapter) Read(sessionPath string) ([]RawEntry, error)
- func (a *GenericJSONLAdapter) ReadMetadata(sessionPath string) (*SessionMetadata, error)
- func (a *GenericJSONLAdapter) Watch(_ context.Context, _ string) (<-chan RawEntry, error)
- type IncrementalReader
- type ParseLineFunc
- type RawEntry
- type SessionMetadata
- type TailWatcher
Constants ¶
This section is empty.
Variables ¶
var ( // ErrNoAdapterDetected is returned when no adapter can handle the current environment ErrNoAdapterDetected = errors.New("no adapter detected for current environment") // ErrAdapterNotFound is returned when a specific adapter is not registered ErrAdapterNotFound = errors.New("adapter not found") // ErrSessionNotFound is returned when a session file cannot be located ErrSessionNotFound = errors.New("session file not found") // ErrWatchNotSupported is returned when an adapter does not support real-time watching ErrWatchNotSupported = errors.New("watch not supported for this adapter") )
Functions ¶
func ListAdapters ¶
func ListAdapters() []string
ListAdapters returns the names of all registered adapters
func Register ¶
func Register(adapter Adapter)
Register adds an adapter to the registry Panics if an adapter with the same name is already registered
func ResetRegistry ¶
func ResetRegistry()
ResetRegistry clears all registered adapters (for testing only)
Types ¶
type Adapter ¶
type Adapter interface {
// Name returns the adapter name (e.g., "claude-code")
Name() string
// Detect checks if this adapter can handle the current environment
// Returns true if the agent's session files are present and readable
Detect() bool
// FindSessionFile locates the session file for correlation
// Called after ox agent prime to find the matching agent session
// agentID is the unique identifier written during prime
// since filters to sessions created after this time
FindSessionFile(agentID string, since time.Time) (string, error)
// Read reads all entries from a session file
// Returns entries in chronological order
Read(sessionPath string) ([]RawEntry, error)
// ReadMetadata extracts session metadata (agent version, model) from a session file.
// Returns nil if metadata cannot be determined.
ReadMetadata(sessionPath string) (*SessionMetadata, error)
// Watch monitors a session file for new entries (for real-time capture)
// The returned channel receives entries as they appear
// The channel is closed when ctx is canceled or an error occurs
Watch(ctx context.Context, sessionPath string) (<-chan RawEntry, error)
}
Adapter reads conversation data from a coding agent's session files
func DetectAdapter ¶
DetectAdapter finds the appropriate adapter for the current environment Iterates through all registered adapters and returns the first one that detects Returns ErrNoAdapterDetected if no adapter can handle the environment
func GetAdapter ¶
GetAdapter returns a specific adapter by name. Accepts canonical names ("claude-code"), display names ("Claude Code"), and shorthand ("claude"). Case-insensitive for aliases. Returns ErrAdapterNotFound if no adapter with that name is registered.
type BatchTransformFunc ¶ added in v0.6.1
BatchTransformFunc post-processes a batch of entries before they are sent through the Watch channel. Used by adapters that need cross-entry correlation (e.g. Codex merges function_call + function_call_output by CallID).
type ClaudeCodeAdapter ¶
type ClaudeCodeAdapter struct{}
ClaudeCodeAdapter reads Claude Code session files stored as JSONL in ~/.claude/projects/<project-hash>/*.jsonl
func (*ClaudeCodeAdapter) Detect ¶
func (a *ClaudeCodeAdapter) Detect() bool
Detect checks if Claude Code session files are present
func (*ClaudeCodeAdapter) FindSessionFile ¶
FindSessionFile locates the most recent session file matching criteria. It searches through all project directories for JSONL files modified after 'since', then scans for content matching the agentID (from ox agent prime output).
func (*ClaudeCodeAdapter) Name ¶
func (a *ClaudeCodeAdapter) Name() string
Name returns the adapter identifier
func (*ClaudeCodeAdapter) Read ¶
func (a *ClaudeCodeAdapter) Read(sessionPath string) ([]RawEntry, error)
Read parses all entries from a Claude Code JSONL session file
func (*ClaudeCodeAdapter) ReadFromOffset ¶ added in v0.5.0
ReadFromOffset reads new entries starting at the given byte offset. Returns the entries and the new offset position.
func (*ClaudeCodeAdapter) ReadMetadata ¶
func (a *ClaudeCodeAdapter) ReadMetadata(sessionPath string) (*SessionMetadata, error)
ReadMetadata extracts session metadata (agent version, model) from a Claude Code session. It scans the session file for version and model information from the JSONL entries.
type CodexAdapter ¶ added in v0.3.0
type CodexAdapter struct{}
CodexAdapter reads OpenAI Codex CLI session files stored as JSONL in ~/.codex/sessions/YYYY/MM/DD/rollout-TIMESTAMP-UUID.jsonl
Codex session entry types:
- session_meta: session start metadata (cwd, cli_version, session id)
- turn_context: per-turn config including model
- response_item: conversation data (messages, function calls)
- event_msg: infrastructure events (task_started, token counts) — skipped
response_item subtypes:
- type=message, role=user: user prompt (content[].input_text.text)
- type=message, role=assistant: AI response (content[].output_text.text)
- type=message, role=developer: system instructions — skipped
- type=function_call: tool invocation (name, arguments, call_id)
- type=function_call_output: tool result (errors only — success output skipped for lean recordings)
- type=reasoning: encrypted model reasoning — skipped
Plans are embedded in the session as assistant messages, not separate files.
func (*CodexAdapter) Detect ¶ added in v0.3.0
func (a *CodexAdapter) Detect() bool
Detect checks if Codex session files are present
func (*CodexAdapter) FindSessionFile ¶ added in v0.3.0
FindSessionFile locates the Codex session file for the current project. It searches recent date directories under ~/.codex/sessions/YYYY/MM/DD/, matches by cwd and optionally agentID (from ox agent prime output).
func (*CodexAdapter) Name ¶ added in v0.3.0
func (a *CodexAdapter) Name() string
Name returns the adapter identifier
func (*CodexAdapter) Read ¶ added in v0.3.0
func (a *CodexAdapter) Read(sessionPath string) ([]RawEntry, error)
Read parses all conversation entries from a Codex JSONL session file. Plans are embedded as assistant messages — no separate plan file exists.
func (*CodexAdapter) ReadFromOffset ¶ added in v0.6.1
ReadFromOffset reads new entries starting at the given byte offset.
func (*CodexAdapter) ReadMetadata ¶ added in v0.3.0
func (a *CodexAdapter) ReadMetadata(sessionPath string) (*SessionMetadata, error)
ReadMetadata extracts session metadata (CLI version and model) from a Codex session. CLI version comes from session_meta, model from the first turn_context.
func (*CodexAdapter) Watch ¶ added in v0.3.0
Watch monitors a Codex session file for new entries using fsnotify with debouncing. mergeToolEntries is applied per-batch via WithBatchTransform, so function_call and function_call_output arriving in separate batches won't be merged here. This is intentional: unmerged entries in raw.jsonl are independently useful, and cross-batch merge is applied at read-time during finalization.
type GeminiAdapter ¶ added in v0.6.1
type GeminiAdapter struct{}
GeminiAdapter reads Gemini CLI session files stored as monolithic JSON in ~/.gemini/tmp/<project_hash>/chats/session-*.json.
Unlike JSONL-based adapters (Claude Code, Codex), Gemini rewrites the entire session file on each turn. This adapter re-reads the JSON on each change and emits only new entries (delta-based).
func (*GeminiAdapter) Detect ¶ added in v0.6.1
func (a *GeminiAdapter) Detect() bool
Detect checks if Gemini CLI session files are present.
func (*GeminiAdapter) FindSessionFile ¶ added in v0.6.1
FindSessionFile locates the most recent Gemini session file. Gemini stores sessions in ~/.gemini/tmp/<project_hash>/chats/session-*.json. The project hash algorithm is not publicly documented, so we scan all subdirectories and return the most recently modified session file.
func (*GeminiAdapter) Name ¶ added in v0.6.1
func (a *GeminiAdapter) Name() string
func (*GeminiAdapter) Read ¶ added in v0.6.1
func (a *GeminiAdapter) Read(sessionPath string) ([]RawEntry, error)
Read parses all entries from a Gemini session file.
func (*GeminiAdapter) ReadFromOffset ¶ added in v0.6.1
ReadFromOffset reads new entries since the given offset. For Gemini, offset represents entry count (not byte position) since the entire JSON file is rewritten on each turn.
func (*GeminiAdapter) ReadMetadata ¶ added in v0.6.1
func (a *GeminiAdapter) ReadMetadata(sessionPath string) (*SessionMetadata, error)
ReadMetadata extracts session metadata from a Gemini session file.
func (*GeminiAdapter) Watch ¶ added in v0.6.1
Watch monitors a Gemini session file for new entries using fsnotify. Since Gemini rewrites the entire file on each turn, we re-read the full JSON and emit only entries beyond our last-seen count. Watches the parent directory to survive atomic file replacements (temp + rename).
type GenericJSONLAdapter ¶ added in v0.3.0
type GenericJSONLAdapter struct{}
GenericJSONLAdapter reads SageOx-compatible JSONL from a file that any coding agent writes to. Unlike the Claude Code adapter which reads agent-native files, this adapter reads a standardized format that agents write to via `ox agent session log` or direct JSONL writes.
func (*GenericJSONLAdapter) Detect ¶ added in v0.3.0
func (a *GenericJSONLAdapter) Detect() bool
Detect always returns false. The generic adapter is never auto-detected -- it is used via explicit GetAdapter() or alias resolution. Deep adapters (e.g. Claude Code) take priority via DetectAdapter().
func (*GenericJSONLAdapter) FindSessionFile ¶ added in v0.3.0
FindSessionFile is not supported by the generic adapter. The session file path is created by session start and passed through recording state.
func (*GenericJSONLAdapter) Name ¶ added in v0.3.0
func (a *GenericJSONLAdapter) Name() string
Name returns the adapter identifier.
func (*GenericJSONLAdapter) Read ¶ added in v0.3.0
func (a *GenericJSONLAdapter) Read(sessionPath string) ([]RawEntry, error)
Read parses all entries from a SageOx-compatible JSONL session file. It streams line-by-line, skipping header/footer lines and malformed JSON. Empty files return an empty slice (not an error).
func (*GenericJSONLAdapter) ReadMetadata ¶ added in v0.3.0
func (a *GenericJSONLAdapter) ReadMetadata(sessionPath string) (*SessionMetadata, error)
ReadMetadata extracts session metadata from the first line of a JSONL file. If the first line is a header with a metadata object, agent_version and model are extracted. Returns nil if no header or the fields are missing.
type IncrementalReader ¶ added in v0.5.0
type IncrementalReader interface {
ReadFromOffset(path string, offset int64) ([]RawEntry, int64, error)
}
IncrementalReader is an optional interface for adapters that support offset-based incremental reading (used by hook-driven recording).
type ParseLineFunc ¶ added in v0.6.1
ParseLineFunc converts a raw JSONL line into zero or more RawEntries. Each adapter provides its own implementation.
type RawEntry ¶
type RawEntry struct {
// Timestamp when this entry was created
Timestamp time.Time
// Role identifies the speaker: "user", "assistant", "system", "tool"
Role string
// Content is the message text or tool output
Content string
// ToolName is the name of the tool invoked (only for role="tool")
ToolName string
// ToolInput is the input provided to the tool (only for role="tool")
ToolInput string
// ToolOutput is the output from the tool (only for role="tool").
// Only populated for error results to keep recordings lean.
ToolOutput string
// IsError indicates the tool call failed (only for role="tool")
IsError bool
// CallID correlates function_call with function_call_output (adapter-specific)
CallID string
// Raw contains the original data for debugging and auditing
Raw json.RawMessage
}
RawEntry represents a conversation turn from any agent
type SessionMetadata ¶
type SessionMetadata struct {
// AgentVersion is the version of the coding agent (e.g., "1.0.3" for Claude Code)
AgentVersion string
// Model is the LLM model used (e.g., "claude-sonnet-4-20250514")
Model string
}
SessionMetadata contains metadata extracted from agent session files. This captures which agent and model were used for the session.
type TailWatcher ¶ added in v0.6.1
type TailWatcher struct {
// contains filtered or unexported fields
}
TailWatcher provides file-tailing with fsnotify, debounce, and offset tracking. Used by daemon for hookless agents and by adapter Watch() methods. The caller provides a parseLine function; TailWatcher handles file I/O plumbing.
func NewTailWatcher ¶ added in v0.6.1
func NewTailWatcher(path string, offset int64, parseLine ParseLineFunc) *TailWatcher
NewTailWatcher creates a TailWatcher that tails the given file starting at offset. parseLine converts each JSONL line into adapter-specific RawEntries.
func (*TailWatcher) Offset ¶ added in v0.6.1
func (tw *TailWatcher) Offset() int64
Offset returns the current read position.
func (*TailWatcher) ReadFromOffset ¶ added in v0.6.1
func (tw *TailWatcher) ReadFromOffset(offset int64) ([]RawEntry, int64, error)
ReadFromOffset reads new entries starting at the given byte offset, applying batchTransform if set. Returns entries and the new offset position.
func (*TailWatcher) Watch ¶ added in v0.6.1
func (tw *TailWatcher) Watch(ctx context.Context) (<-chan RawEntry, error)
Watch starts tailing the file and sends new entries to the returned channel. The channel is closed when ctx is canceled or the watcher encounters an error.
func (*TailWatcher) WithBatchTransform ¶ added in v0.6.1
func (tw *TailWatcher) WithBatchTransform(fn BatchTransformFunc) *TailWatcher
WithBatchTransform sets a post-processing function applied to each batch of entries before they are sent through the Watch channel or returned by ReadFromOffset. Returns the TailWatcher for chaining.