session

package
v0.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 15, 2026 License: MIT Imports: 33 Imported by: 0

Documentation

Overview

Package session provides shared entry parsing utilities for session recordings. These functions handle the multiple JSONL formats that session entries can take: imported format (root-level fields), standard format (nested data.*), and legacy format.

Package session provides storage and management for agent session recordings. This file implements markdown generation from stored sessions.

Package session provides storage and management for agent session recordings. Sessions are stored in the XDG cache directory under context/<repo-id>/sessions/ using a session-folder structure for organized storage of multiple output formats.

TODO(server-side): move session file creation to server-side for MVP+1. Currently client writes all session files; server should own ledger writes.

Session folder structure:

sessions/
└── <session-name>/
    ├── raw.jsonl        # unprocessed session capture
    ├── summary.md       # ai-generated summary
    ├── session.md    # markdown session
    └── session.html  # html session

Session name format: YYYY-MM-DDTHH-MM-<username>-<sessionID> Example: 2026-01-06T14-32-ryan-Ox7f3a

Package session provides subagent session capture and aggregation.

When a parent agent spawns subagents, the parent's session can capture subagent sessions and include them in the parent session's output. This enables comprehensive session capture for complex multi-agent workflows.

Subagent capture flow:

  1. Parent starts recording: `ox agent <id> session start`
  2. Subagent spawns and inherits parent session ID (via environment or flag)
  3. Subagent completes work and reports: `ox agent <id> session subagent-complete`
  4. Parent stops recording: `ox agent <id> session stop`
  5. Parent session includes references to all subagent sessions

Package session provides subagent session capture during recording stop.

When a parent session ends, this module scans the session for Task tool calls, matches them with completed subagent sessions (by timestamp correlation), and enriches the session entries with subagent references.

Output structure for Task tool entries:

{
  "type": "tool",
  "tool_name": "Task",
  "tool_input": {...},
  "tool_output": "...",
  "subagent": {
    "session_id": "abc123",
    "session_path": "sessions/2026-01-06T14-35-history-Oxdef/",
    "entry_count": 47
  }
}

Index

Constants

View Source
const (
	StopReasonStopped   = "stopped"   // user explicitly stopped via /ox-session-stop
	StopReasonCanceled  = "canceled"  // user explicitly canceled via /ox-session-abort
	StopReasonRecovered = "recovered" // recovered from orphan by daemon anti-entropy
)

StopReason constants for how a session ended.

View Source
const (
	ErrCodeNotRecording     = "NOT_RECORDING"
	ErrCodeAlreadyRecording = "ALREADY_RECORDING"
	ErrCodeNoSession        = "NO_SESSION"
	ErrCodeInvalidEntry     = "INVALID_ENTRY"
	ErrCodeStorageError     = "STORAGE_ERROR"
	ErrCodeAuthRequired     = "AUTH_REQUIRED"
)

Error codes for session operations

View Source
const (
	EntryTypeUser      = SessionEntryTypeUser
	EntryTypeAssistant = SessionEntryTypeAssistant
	EntryTypeSystem    = SessionEntryTypeSystem
	EntryTypeTool      = SessionEntryTypeTool
)

EntryType constants for backward compatibility.

View Source
const ArtifactName = "redaction"

ArtifactName is the signing artifact name for the redaction manifest.

View Source
const DefaultListWindow = 7 * 24 * time.Hour

DefaultListWindow is the default time window for listing sessions. Only sessions within this window will have meta.json read for hydration status. Use time.Duration(0) to list all sessions.

View Source
const DefaultReminderInterval = 50

DefaultReminderInterval is the default number of entries between reminders.

View Source
const HistorySchemaVersion = "1"

HistorySchemaVersion is the current version of the history capture schema.

View Source
const HistorySourcePlanningHistory = "planning_history"

HistorySourcePlanningHistory is the source marker for planning history entries.

View Source
const ManifestSchemaVersion = "1"

ManifestSchemaVersion is incremented when the manifest structure changes. Signature verification requires matching schema versions.

View Source
const SessionSchemaVersion = "1"

SessionSchemaVersion is the current session schema version. Increment when making breaking changes to the session format.

View Source
const StaleRecordingThreshold = 12 * time.Hour

StaleRecordingThreshold defines how long a recording can run before it's considered potentially stale/abandoned (12 hours).

View Source
const SummaryPromptGuidelines = `` /* 4042-byte string literal not displayed */

SummaryPromptGuidelines contains the shared guidelines for session summarization. Used by both the resummary command and should match the server-side prompt.

Variables

View Source
var (
	// ErrSessionNotFound is returned when a session file cannot be located
	ErrSessionNotFound = errors.New("session not found")

	// ErrNoSessions is returned when no sessions exist in storage
	ErrNoSessions = errors.New("no sessions found")

	// ErrNoRawSessions is returned when no raw sessions exist
	ErrNoRawSessions = errors.New("no raw sessions found")

	// ErrEmptyPath is returned when a required path argument is empty
	ErrEmptyPath = errors.New("path cannot be empty")

	// ErrEmptyFilename is returned when a required filename argument is empty
	ErrEmptyFilename = errors.New("filename cannot be empty")

	// ErrNilEntry is returned when a nil entry is passed to a write operation
	ErrNilEntry = errors.New("entry cannot be nil")

	// ErrNilData is returned when nil data is passed to a write operation
	ErrNilData = errors.New("data cannot be nil")

	// ErrNilState is returned when a nil state is passed to a save operation
	ErrNilState = errors.New("recording state cannot be nil")

	// ErrSessionNotHydrated is returned when content files are not present locally.
	// The session's meta.json exists (from git pull) but content files need to be
	// downloaded from LFS blob storage via `ox session download`.
	ErrSessionNotHydrated = errors.New("session content not available locally: run 'ox session download' to fetch")
)

sentinel errors for the session package

View Source
var (
	// ErrHistoryMissingMeta is returned when the first line is not a valid _meta entry
	ErrHistoryMissingMeta = errors.New("history missing _meta line")

	// ErrHistoryInvalidMeta is returned when _meta is missing required fields
	ErrHistoryInvalidMeta = errors.New("history _meta missing required fields")

	// ErrHistoryInvalidEntryType is returned when entry type is not recognized
	ErrHistoryInvalidEntryType = errors.New("history entry has invalid type")

	// ErrHistoryNonMonotonicSeq is returned when seq numbers are not strictly increasing
	ErrHistoryNonMonotonicSeq = errors.New("history entry seq not monotonically increasing")

	// ErrHistoryDuplicateSeq is returned when two entries have the same seq number
	ErrHistoryDuplicateSeq = errors.New("history entry has duplicate seq")

	// ErrHistoryMissingSeq is returned when an entry is missing the seq field
	ErrHistoryMissingSeq = errors.New("history entry missing seq field")

	// ErrHistoryEmptyInput is returned when input reader is nil
	ErrHistoryEmptyInput = errors.New("history input is empty")
)

History schema validation errors.

View Source
var (
	// ErrHistoryNilHistory is returned when a nil history is passed to store
	ErrHistoryNilHistory = errors.New("captured history cannot be nil")

	// ErrHistoryEmptyEntries is returned when history has no entries to store
	ErrHistoryEmptyEntries = errors.New("captured history has no entries")

	// ErrHistoryStorageFailed is returned when storage operation fails
	ErrHistoryStorageFailed = errors.New("failed to store captured history")
)

History storage errors.

View Source
var (
	// ErrNotRecording is returned when stop is called but no recording is active
	ErrNotRecording = errors.New("not currently recording")

	// ErrAlreadyRecording is returned when start is called but already recording
	ErrAlreadyRecording = errors.New("already recording a session")

	// ErrNoLedger is returned when session recording is attempted but no ledger is configured
	ErrNoLedger = errors.New("no ledger configured for this project")
)
View Source
var ValidEntryTypes = map[string]bool{
	"user":      true,
	"assistant": true,
	"system":    true,
	"tool":      true,
}

ValidEntryTypes defines the allowed entry types for history entries.

Functions

func BuildSummaryPrompt

func BuildSummaryPrompt(entries []Entry, rawPath, ledgerSessionDir string) string

BuildSummaryPrompt builds a prompt for the calling agent to generate a session summary. The agent receives this prompt in the JSON output and produces the summary itself, avoiding a server-side API call. If ledgerSessionDir is non-empty, a step is added instructing the agent to push the summary to the ledger via `ox session push-summary`.

func ClearNeedsSummaryMarker

func ClearNeedsSummaryMarker(sessionCacheDir string) error

ClearNeedsSummaryMarker removes the .needs-summary marker from a session cache directory.

func ClearRecordingState

func ClearRecordingState(projectRoot string) error

ClearRecordingState removes the recording state file from the session folder. Note: returns first-match state. Use ClearRecordingStateForAgent in agent-context code to avoid clearing another concurrent agent's recording.

func ClearRecordingStateForAgent added in v0.3.0

func ClearRecordingStateForAgent(projectRoot, agentID string) error

ClearRecordingStateForAgent removes recording state for a specific agent only. Safe for concurrent use: only touches this agent's .recording.json.

func ConsumeExplicitStop added in v0.3.0

func ConsumeExplicitStop(projectRoot, agentID string) bool

ConsumeExplicitStop checks for and removes the per-agent explicit-stop marker. Returns true if the marker existed (meaning an auto-start should be skipped). Also cleans up any legacy global marker (without agent suffix) as a migration path.

func CountSubstantiveEntries added in v0.5.0

func CountSubstantiveEntries(rawPath string) int

CountSubstantiveEntries counts lines in a raw.jsonl that are actual session entries, excluding the metadata header (first line). Returns 0 for header-only files or files that don't exist.

func EnrichEntriesWithSubagents

func EnrichEntriesWithSubagents(entries []map[string]any, matches map[int]*SubagentReference)

EnrichEntriesWithSubagents adds subagent references to session entries. Modifies entries in place, adding "subagent" field to matched Task tool calls.

func ExtractAllMermaidFromEntries

func ExtractAllMermaidFromEntries(entries []SessionEntry) []string

ExtractAllMermaidFromEntries finds all unique mermaid diagrams across entries. Returns deduplicated list of diagrams.

func ExtractContent added in v0.1.1

func ExtractContent(entry map[string]any) string

ExtractContent gets content from various field locations in an entry. Checks: content, data.content, data.message, message, text, result.

func ExtractDiagramsFromEntries

func ExtractDiagramsFromEntries(entries []map[string]any) []string

ExtractDiagramsFromEntries scans all entries for mermaid diagrams. Returns deduplicated list of diagrams found.

func ExtractEntryTimestamp added in v0.1.1

func ExtractEntryTimestamp(entry map[string]any) (time.Time, bool)

ExtractEntryTimestamp gets the timestamp from an entry, checking both "timestamp" and "ts" field names.

func ExtractEntryType added in v0.1.1

func ExtractEntryType(entry map[string]any) string

ExtractEntryType gets the type field from an entry.

func ExtractMermaidBlocks

func ExtractMermaidBlocks(content string) []string

ExtractMermaidBlocks finds all mermaid code blocks in content. Returns the raw mermaid code (without the ``` markers).

func ExtractMermaidDiagrams

func ExtractMermaidDiagrams(content string) []string

ExtractMermaidDiagrams finds all mermaid code blocks in content. Returns a slice of diagram contents (without the ```mermaid markers). Handles multiple diagrams and ignores malformed/unclosed blocks.

func FindParentSessionPath

func FindParentSessionPath(projectRoot string) string

FindParentSessionPath looks up the active recording state and returns its session path. Used by subagents to discover where to report completion. Returns empty string if no recording is active. Note: returns first-match. Use FindParentSessionPathForAgent when agent ID is known.

func FindParentSessionPathForAgent added in v0.3.0

func FindParentSessionPathForAgent(projectRoot, agentID string) string

FindParentSessionPathForAgent looks up the recording state for a specific agent and returns its session path. Used by subagents to discover parent session.

func FormatGuidanceJSON

func FormatGuidanceJSON(phase GuidancePhase, agentID string, guidance SessionGuidance, message string) ([]byte, error)

FormatGuidanceJSON formats guidance as JSON output.

func FormatGuidanceText

func FormatGuidanceText(phase GuidancePhase, agentID string, guidance SessionGuidance) string

FormatGuidanceText formats guidance as human-readable text.

func FormatHTMLGuidanceJSON

func FormatHTMLGuidanceJSON(agentID string, guidance HTMLGuidance, generated bool, htmlPath, message string) ([]byte, error)

FormatHTMLGuidanceJSON formats HTML generation guidance as JSON output.

func FormatHTMLGuidanceText

func FormatHTMLGuidanceText(agentID string, guidance HTMLGuidance) string

FormatHTMLGuidanceText formats HTML generation guidance as human-readable text.

func FormatSummarizeGuidanceJSON

func FormatSummarizeGuidanceJSON(agentID string, guidance SummarizeGuidance, summary *SummarizeOutput) ([]byte, error)

FormatSummarizeGuidanceJSON formats summarization guidance as JSON output.

func FormatSummarizeGuidanceText

func FormatSummarizeGuidanceText(agentID string, guidance SummarizeGuidance) string

FormatSummarizeGuidanceText formats summarization guidance as human-readable text.

func FormatSummaryJSON

func FormatSummaryJSON(agentID string, summary *SummarizeResponse, entryCount int, filePath, message string) ([]byte, error)

FormatSummaryJSON formats a summary as JSON output.

func FormatSummaryText

func FormatSummaryText(agentID string, summary *SummarizeResponse, entryCount int) string

FormatSummaryText formats a summary as human-readable text.

func GenerateFilename

func GenerateFilename(username, agentID string) string

GenerateFilename creates a unique filename for a session (legacy format). Format: 2026-01-05T10-30-<username>-<agentID>.jsonl The timestamp uses hour-minute precision with dashes for filesystem compatibility. Deprecated: Use GenerateSessionName for new session-folder structure.

func GenerateSessionName

func GenerateSessionName(sessionID, username string) string

GenerateSessionName creates a unique session folder name. Format: YYYY-MM-DDTHH-MM-<username>-<sessionID> Example: 2026-01-06T14-32-ryan-Ox7f3a

func GetCacheDir

func GetCacheDir() string

GetCacheDir returns the SageOx cache directory.

Path Resolution (via internal/paths package):

Default:           ~/.sageox/cache/
With OX_XDG_ENABLE: $XDG_CACHE_HOME/sageox/

See internal/paths/doc.go for architecture rationale.

func GetContextPath

func GetContextPath(repoID string) string

GetContextPath returns the full path to a repo's session context directory.

Path Resolution (via internal/paths package):

Default:           ~/.sageox/cache/sessions/<repo-id>/
With OX_XDG_ENABLE: $XDG_CACHE_HOME/sageox/sessions/<repo-id>/

See internal/paths/doc.go for architecture rationale.

func GetHistoryStoragePath

func GetHistoryStoragePath(agentID string, activeRecording bool) string

GetHistoryStoragePath determines where to store captured history. If activeRecording is true, returns path to active session's raw.jsonl. Otherwise returns path for a new prior-history.jsonl in a new session folder.

Path resolution:

  • Active recording: <session_path>/raw.jsonl
  • New session: <sessions_base>/<new_session_name>/prior-history.jsonl

func GetRecordingDuration

func GetRecordingDuration(projectRoot string) time.Duration

GetRecordingDuration returns how long the current recording has been running. Returns 0 if no recording is active.

func GetSessionName

func GetSessionName(sessionPath string) string

GetSessionName extracts the session name from a session path.

func HasMermaidBlocks

func HasMermaidBlocks(content string) bool

HasMermaidBlocks checks if content contains any mermaid code blocks.

func HasSubstantiveEntries added in v0.5.0

func HasSubstantiveEntries(rawPath string) bool

HasSubstantiveEntries returns true if a raw.jsonl file has at least one entry beyond the metadata header line. A header-only file (1 line) has no real session content and should not be uploaded or finalized.

This is the canonical check — use it everywhere instead of inline line counting.

func IsNoiseCommand

func IsNoiseCommand(cmd string) bool

IsNoiseCommand checks if a command is noise (low-value unless it fails).

func IsRecording

func IsRecording(projectRoot string) bool

IsRecording checks if a recording is active for the given project root.

func IsRecordingForAgent added in v0.3.0

func IsRecordingForAgent(projectRoot, agentID string) bool

IsRecordingForAgent checks if a specific agent has an active recording.

func IsRedactionSigned

func IsRedactionSigned() bool

IsRedactionSigned returns true if redaction signing is configured.

func IsValidEntryType

func IsValidEntryType(entryType string) bool

IsValidEntryType returns true if the given type is valid.

func LoadCustomPatterns added in v0.3.0

func LoadCustomPatterns(projectRoot string) ([]SecretPattern, []RedactParseError)

LoadCustomPatterns discovers and loads REDACT.md files from all custom levels (team, repo, user), merges them in order, and returns compiled patterns. Returns patterns plus any parse errors encountered.

func LocalSummary

func LocalSummary(entries []Entry) string

LocalSummary generates a simple local summary without API call. Used as fallback when API is unavailable. Extracts the first substantive user message as a topic hint so the summary conveys what the session was about, not just stats.

func MapEntryType added in v0.1.1

func MapEntryType(raw string) string

MapEntryType normalizes raw entry type strings to canonical CSS/display types. Returns one of: "user", "assistant", "system", "tool", "tool_result", "info".

func MapRoleToType added in v0.1.1

func MapRoleToType(role string) string

MapRoleToType converts a message role (from data.role) to a display type.

func MarkExplicitStop added in v0.3.0

func MarkExplicitStop(projectRoot, agentID string) error

MarkExplicitStop writes a per-agent breadcrumb indicating the user explicitly stopped recording. This prevents the next auto-start cycle (e.g. from /clear hook re-prime) from silently restarting the session for this specific agent.

func MatchTasksToSubagents

func MatchTasksToSubagents(
	taskCalls []TaskToolCallInfo,
	subagents []*SubagentSession,
) map[int]*SubagentReference

MatchTasksToSubagents attempts to match Task tool calls with subagent sessions. Returns a map from entry index to matched SubagentReference.

func MergeHistoryWithRecording

func MergeHistoryWithRecording(sessionPath string, history *CapturedHistory) error

MergeHistoryWithRecording merges captured history into an active recording's raw.jsonl. History entries are prepended before existing recorded entries, with seq numbers adjusted.

The merge process:

  1. Read existing raw.jsonl entries
  2. Assign seq numbers to history entries (starting from 1)
  3. Adjust existing entry seq numbers to continue after history
  4. Write merged entries back to raw.jsonl

func NewRedactorWithCustomRules added in v0.3.0

func NewRedactorWithCustomRules(projectRoot string) (*Redactor, []RedactParseError)

NewRedactorWithCustomRules creates a Redactor with default patterns plus any user-defined patterns from REDACT.md files. Custom patterns are always additive -- built-in patterns cannot be removed. Parse errors are returned for logging/display but do not prevent redactor creation.

func ParseHistoryEntry

func ParseHistoryEntry(line []byte) (*HistoryMeta, *HistoryEntry, error)

ParseHistoryEntry parses a JSONL line into a HistoryEntry or HistoryMeta.

func ParseTimestamp added in v0.1.1

func ParseTimestamp(ts string) (time.Time, bool)

ParseTimestamp parses a timestamp string in RFC3339 or RFC3339Nano format. Returns zero time and false if parsing fails.

func ProcessMermaidBlocks

func ProcessMermaidBlocks(content string) string

ProcessMermaidBlocks finds all ```mermaid blocks in content and renders them to ASCII. Returns the content with mermaid blocks replaced by their ASCII representations. If rendering fails for a block, it's left as-is with an error comment.

func RawJSONLHasData added in v0.5.0

func RawJSONLHasData(sessionPath string) bool

RawJSONLHasData checks if a raw.jsonl file exists on disk and has content. This is a filesystem-level check (size > 0), not a line-level check.

func RecordingDurationString

func RecordingDurationString(state *RecordingState) string

RecordingDurationString returns a human-readable duration string.

func RedactHistorySecrets

func RedactHistorySecrets(history *CapturedHistory) int

RedactHistorySecrets applies secret redaction to all entries in captured history using the default redactor. This is a convenience function for one-off usage. Returns the count of entries that contained secrets.

func RenderMermaidToASCII

func RenderMermaidToASCII(mermaidCode string) (string, error)

RenderMermaidToASCII renders a mermaid diagram string to ASCII art. Returns the ASCII representation or an error if rendering fails. Uses the mermaid-ascii library compiled into the binary.

func ReportSubagentComplete

func ReportSubagentComplete(opts SubagentCompleteOptions) error

ReportSubagentComplete registers a subagent's completion with the parent session. This should be called when a subagent finishes its work.

func RulesToPatterns added in v0.3.0

func RulesToPatterns(rules []RedactRule) ([]SecretPattern, []RedactParseError)

RulesToPatterns converts parsed RedactRules to SecretPatterns. For literal rules, the pattern is escaped via regexp.QuoteMeta. Invalid regex rules produce errors but don't stop conversion.

func SaveRecordingState

func SaveRecordingState(projectRoot string, state *RecordingState) error

SaveRecordingState persists recording state to the session folder.

func SessionHasHistory

func SessionHasHistory(sessionPath string) bool

SessionHasHistory checks if a session folder contains prior-history.jsonl.

func ShortenPath

func ShortenPath(path string) string

ShortenPath returns a shortened path for display. Replaces home directory with ~.

func StoreCapturedHistory

func StoreCapturedHistory(history *CapturedHistory, agentID string, activeRecording bool) (storagePath string, err error)

StoreCapturedHistory stores validated history to the appropriate location. If activeRecording is true, merges history into the active session's raw.jsonl. Otherwise creates a new session folder with the history as prior-history.jsonl.

Returns the path where history was stored.

func UpdateRecordingState

func UpdateRecordingState(projectRoot string, updateFn func(*RecordingState)) error

UpdateRecordingState updates and persists the recording state. Useful for updating entry count or last reminder sequence. Note: uses first-match state. Use UpdateRecordingStateForAgent in agent-context code.

func UpdateRecordingStateForAgent added in v0.3.0

func UpdateRecordingStateForAgent(projectRoot, agentID string, updateFn func(*RecordingState)) error

UpdateRecordingStateForAgent updates recording state for a specific agent. Safe for concurrent use: only touches this agent's .recording.json.

func ValidateHistoryEntry

func ValidateHistoryEntry(entry *HistoryEntry) error

ValidateHistoryEntry checks if a history entry is valid.

func ValidateHistoryMeta

func ValidateHistoryMeta(meta *HistoryMeta) error

ValidateHistoryMeta checks that required fields are present in the metadata.

func VerifyRedactionSignature

func VerifyRedactionSignature() *signing.VerificationResult

VerifyRedactionSignature checks that the redaction manifest hasn't been tampered with.

func WriteHistoryJSONL

func WriteHistoryJSONL(path string, history *CapturedHistory) error

WriteHistoryJSONL writes captured history to a JSONL file. Writes the _meta line first, then each entry on its own line.

func WriteNeedsSummaryMarker

func WriteNeedsSummaryMarker(sessionCacheDir, rawPath, ledgerSessionDir string) error

WriteNeedsSummaryMarker writes a .needs-summary marker to the session cache directory. This marker indicates that session stop completed but summary.json was not yet generated.

Types

type AhaMoment

type AhaMoment struct {
	Seq       int    `json:"seq"`       // message sequence number for navigation
	Role      string `json:"role"`      // user, assistant, or system
	Type      string `json:"type"`      // question, insight, decision, breakthrough, synthesis
	Highlight string `json:"highlight"` // the key text/quote from this moment
	Why       string `json:"why"`       // brief explanation of why this was important
}

AhaMoment captures a pivotal point in the conversation where key insight emerged. These moments document collaborative intelligence - the interplay between human intuition/direction and AI exploration/synthesis.

type ArtifactPaths added in v0.5.0

type ArtifactPaths struct {
	SummaryMD   string // summary.md — structured markdown summary
	SummaryJSON string // summary.json — machine-readable summary
	HTML        string // session.html — interactive HTML viewer
	SessionMD   string // session.md — full session transcript in markdown
}

ArtifactPaths holds the output paths for generated session artifacts.

func WriteSessionArtifacts added in v0.5.0

func WriteSessionArtifacts(sessionDir string, stored *StoredSession, summaryResp *SummarizeResponse, htmlGen HTMLGenerator) (*ArtifactPaths, error)

WriteSessionArtifacts generates the standard set of session artifacts from a stored session and summary response. Both the CLI stop path and daemon anti-entropy finalization call this to ensure identical output.

The summaryResp may come from LocalSummary (stats-only) or LLM (rich). Either way, the same 4 files are produced.

type CaptureOptions

type CaptureOptions struct {
	// AgentID is the current agent's ID (used if not in history metadata)
	AgentID string

	// Title is an optional title for the captured session
	Title string

	// MergeWithActive if true, merges with any active recording
	MergeWithActive bool

	// SkipRedaction if true, skips secret redaction (for testing only)
	SkipRedaction bool
}

CaptureOptions configures the capture-prior operation.

type CaptureOutput

type CaptureOutput struct {
	Success         bool              `json:"success"`
	Type            string            `json:"type"` // "session_capture_prior"
	AgentID         string            `json:"agent_id"`
	Path            string            `json:"path"`
	SessionName     string            `json:"session_name,omitempty"`
	EntryCount      int               `json:"entry_count"`
	SecretsRedacted int               `json:"secrets_redacted,omitempty"`
	TimeRange       *HistoryTimeRange `json:"time_range,omitempty"`
	Title           string            `json:"title,omitempty"`
	Message         string            `json:"message,omitempty"`
}

CaptureOutput is the JSON output format for capture-prior command.

func NewCaptureOutput

func NewCaptureOutput(result *CaptureResult) *CaptureOutput

NewCaptureOutput creates a CaptureOutput from a CaptureResult.

func (*CaptureOutput) ToJSON

func (o *CaptureOutput) ToJSON() ([]byte, error)

ToJSON marshals the output to indented JSON.

type CaptureResult

type CaptureResult struct {
	// Path where the history was stored
	Path string `json:"path"`

	// SessionName is the generated session folder name
	SessionName string `json:"session_name,omitempty"`

	// EntryCount is the number of entries captured
	EntryCount int `json:"entry_count"`

	// SecretsRedacted is the count of secrets found and redacted
	SecretsRedacted int `json:"secrets_redacted"`

	// TimeRange contains the time span of the captured history
	TimeRange *HistoryTimeRange `json:"time_range,omitempty"`

	// Title is the session title from metadata
	Title string `json:"title,omitempty"`

	// AgentID from the captured metadata
	AgentID string `json:"agent_id,omitempty"`
}

CaptureResult contains the outcome of a capture-prior operation.

func CapturePrior

func CapturePrior(reader io.Reader, opts CaptureOptions) (*CaptureResult, error)

CapturePrior reads, validates, redacts, and stores captured history from JSONL input.

The input must be valid JSONL with:

  • First line: {"_meta": {...}} with schema_version, source, agent_id
  • Subsequent lines: entries with seq, type, content

Returns the capture result with the storage path and statistics.

func CapturePriorFromFile

func CapturePriorFromFile(path string, opts CaptureOptions) (*CaptureResult, error)

CapturePriorFromFile reads captured history from a file.

type CaptureSubagentOptions

type CaptureSubagentOptions struct {
	// SessionPath is the parent session's folder path
	SessionPath string

	// SessionsBasePath is the base path where sessions are stored
	SessionsBasePath string

	// RecordingStartTime is when the parent recording started
	RecordingStartTime time.Time

	// RecordingEndTime is when the parent recording ended (optional, defaults to now)
	RecordingEndTime time.Time

	// TimeWindowBuffer is extra time to search for subagent sessions (default: 5 minutes)
	TimeWindowBuffer time.Duration
}

CaptureSubagentOptions configures subagent session capture.

func GetSubagentCaptureOptions

func GetSubagentCaptureOptions(state *RecordingState) CaptureSubagentOptions

GetSubagentCaptureOptions creates CaptureSubagentOptions from a RecordingState. This is a convenience function for the stop recording flow.

type CaptureSubagentResult

type CaptureSubagentResult struct {
	// TaskCallsFound is the number of Task tool calls detected
	TaskCallsFound int

	// SubagentsMatched is the number of subagents successfully matched
	SubagentsMatched int

	// EnrichedEntries contains entry indices that were enriched with subagent refs
	EnrichedEntries []int

	// UnmatchedTasks contains indices of Task calls that could not be matched
	UnmatchedTasks []int
}

CaptureSubagentResult contains the results of subagent capture.

func CaptureSubagentSessions

func CaptureSubagentSessions(
	entries []map[string]any,
	opts CaptureSubagentOptions,
) (*CaptureSubagentResult, error)

CaptureSubagentSessions is the main entry point for subagent capture. It detects Task tool calls in the given entries, finds matching subagent sessions, and enriches the entries with subagent references.

This should be called during the session stop flow, after reading entries but before writing the final session.

type CapturedHistory

type CapturedHistory struct {
	Meta    *HistoryMeta
	Entries []HistoryEntry
}

CapturedHistory holds the full parsed history.

func LoadHistoryFromSession

func LoadHistoryFromSession(sessionPath string) (*CapturedHistory, error)

LoadHistoryFromSession loads captured history from a session folder. Looks for prior-history.jsonl in the session directory.

func ParseHistoryFile

func ParseHistoryFile(path string) (*CapturedHistory, error)

ParseHistoryFile parses a JSONL history file from a file path.

func ValidateAndRedact

func ValidateAndRedact(reader io.Reader) (*CapturedHistory, int, error)

ValidateAndRedact validates JSONL input and applies secret redaction. This combines validation and security processing in a single call.

Returns:

  • Validated and redacted history
  • Count of secrets redacted
  • Error if validation fails

func ValidateCapturePriorInput

func ValidateCapturePriorInput(reader io.Reader) (*CapturedHistory, error)

ValidateCapturePriorInput validates input specifically for capture-prior command. This adds additional validation beyond the schema:

  • At least one entry required
  • Updates time_range in metadata if not set
  • Sets message_count in metadata

func ValidateHistoryJSONLReader

func ValidateHistoryJSONLReader(reader io.Reader) (*CapturedHistory, error)

ValidateHistoryJSONLReader reads and validates a captured history JSONL stream.

func ValidateJSONL

func ValidateJSONL(reader io.Reader) (*CapturedHistory, error)

ValidateJSONL validates JSONL input as a captured history. This is the primary entry point for validating agent-generated history.

Returns the parsed and validated history on success. Returns an error if:

  • Input is nil or empty
  • First line is not a valid _meta object
  • _meta is missing required fields (schema_version, source, agent_id)
  • Entry type is not one of: user, assistant, system, tool
  • Entry seq numbers are not monotonically increasing

Example usage:

history, err := ValidateJSONL(reader)
if err != nil {
    return fmt.Errorf("invalid history: %w", err)
}

func (*CapturedHistory) ComputeTimeRange

func (h *CapturedHistory) ComputeTimeRange() *HistoryTimeRange

ComputeTimeRange returns the earliest and latest timestamps in the history.

func (*CapturedHistory) Duration

func (h *CapturedHistory) Duration() time.Duration

Duration returns the duration of the captured history.

func (*CapturedHistory) EntryCount

func (h *CapturedHistory) EntryCount() int

EntryCount returns the number of entries in the history.

func (*CapturedHistory) GetEntriesByType

func (h *CapturedHistory) GetEntriesByType(entryType string) []HistoryEntry

GetEntriesByType returns all entries of a given type.

func (*CapturedHistory) GetPlanEntries

func (h *CapturedHistory) GetPlanEntries() []HistoryEntry

GetPlanEntries returns all entries marked as plans.

func (*CapturedHistory) HasPlanningHistory

func (h *CapturedHistory) HasPlanningHistory() bool

HasPlanningHistory returns true if any entries have planning_history source.

func (*CapturedHistory) PlanningHistoryCount

func (h *CapturedHistory) PlanningHistoryCount() int

PlanningHistoryCount returns the number of entries with planning_history source.

func (*CapturedHistory) ToSessionEntries

func (h *CapturedHistory) ToSessionEntries() []SessionEntry

ToSessionEntries converts all history entries to session entries.

type ChapterSummary added in v0.3.0

type ChapterSummary struct {
	ID         int            `json:"id"`                    // 1-based chapter number
	Title      string         `json:"title"`                 // LLM or heuristic title
	StartSeq   int            `json:"start_seq"`             // first message seq in this chapter
	EndSeq     int            `json:"end_seq"`               // last message seq in this chapter
	ToolCounts map[string]int `json:"tool_counts,omitempty"` // aggregated tool usage {"Read": 5, "Edit": 3}
	TotalTools int            `json:"total_tools"`           // total tool calls in chapter
	HasEdits   bool           `json:"has_edits"`             // true if chapter contains file modifications
}

ChapterSummary is a structured chapter for summary.json. Computed from the raw JSONL by the grouping algorithm, enriched with LLM-generated titles when available. Any tool can consume these without re-implementing the grouping logic.

type DelegationHint

type DelegationHint struct {
	Recommended     bool   `json:"recommended"`
	SubagentType    string `json:"subagent_type,omitempty"`
	ModelTier       string `json:"model_tier,omitempty"` // "fast" (haiku), "balanced" (sonnet), "reasoning" (opus)
	RunInBackground bool   `json:"run_in_background"`
	Reason          string `json:"reason"`
	Command         string `json:"cmd,omitempty"` // actionable command for agent to run
}

DelegationHint suggests how to delegate work to preserve context.

type Entry

type Entry = SessionEntry

Entry is an alias for SessionEntry for backward compatibility and convenience.

func ConvertRawEntries added in v0.5.0

func ConvertRawEntries(rawEntries []adapters.RawEntry) []Entry

ConvertRawEntries converts adapter raw entries to session entries.

func FilterForSummarization added in v0.2.0

func FilterForSummarization(entries []Entry) []Entry

FilterForSummarization removes low-value tool entries from session data before sending to the LLM summarizer. Keeps raw.jsonl complete for auditability while reducing noise in the summarization input.

Filtered out (when successful):

  • Read-only tools: Read, Glob, Grep, WebFetch, WebSearch
  • Bash commands that are noise: ls, pwd, cat, head, tail, etc.

Always kept:

  • All user and assistant messages (the human-AI dialog)
  • System messages
  • Write/Edit tool calls (actual changes)
  • Failed tool calls (important for understanding debugging)
  • Bash commands that modify state

type EntryType

type EntryType = SessionEntryType

EntryType is an alias for SessionEntryType for backward compatibility.

func MapRoleToEntryType added in v0.5.0

func MapRoleToEntryType(role string) EntryType

MapRoleToEntryType converts an adapter role string to a session EntryType.

type FileModification

type FileModification struct {
	Path   string // file path (relative or absolute)
	Action string // Created, Modified, Deleted
}

FileModification represents a file change detected in the session.

type FileSummary added in v0.3.0

type FileSummary struct {
	Path    string `json:"path"`              // shortened file path
	Added   int    `json:"added"`             // lines added
	Removed int    `json:"removed,omitempty"` // lines removed
}

FileSummary records a file modified during the session. Extracted from Edit/Write tool calls in the raw JSONL.

type GhostCleanupResult added in v0.5.0

type GhostCleanupResult struct {
	Removed int      // number of ghost sessions removed
	Names   []string // session folder names that were removed
}

GhostCleanupResult reports what was cleaned up.

func CleanupGhostSessions added in v0.5.0

func CleanupGhostSessions(projectRoot string) GhostCleanupResult

CleanupGhostSessions removes abandoned recording stubs where the parent process is dead and no meaningful data was captured. Uses PID liveness for instant detection — no time threshold needed.

Ghost = .recording.json exists + parent PID dead + no substantive raw.jsonl. Sessions with real data (raw.jsonl with entries) are NOT removed — those are orphans that need recovery, not cleanup.

Safe to call from daemon anti-entropy, doctor --fix, or session start. Uses projectRoot to find session directories via sessionsSearchPaths.

func CleanupGhostSessionsInDir added in v0.5.0

func CleanupGhostSessionsInDir(sessionsDir string) GhostCleanupResult

CleanupGhostSessionsInDir removes ghost sessions from a specific sessions directory. Used by the daemon which has a ledgerPath rather than a projectRoot.

type GuidanceOutput

type GuidanceOutput struct {
	Success  bool            `json:"success"`
	Type     string          `json:"type"` // always "session_guidance"
	Phase    GuidancePhase   `json:"phase"`
	AgentID  string          `json:"agent_id"`
	Guidance SessionGuidance `json:"guidance"`
	Message  string          `json:"message,omitempty"`
}

GuidanceOutput is the JSON output format for session guidance.

type GuidancePhase

type GuidancePhase string

GuidancePhase represents when guidance is being emitted.

const (
	GuidancePhaseStart  GuidancePhase = "start"
	GuidancePhaseStop   GuidancePhase = "stop"
	GuidancePhaseRemind GuidancePhase = "remind"
)

type HTMLGenerator added in v0.5.0

type HTMLGenerator interface {
	GenerateToFile(stored *StoredSession, outputPath string) error
	GenerateToFileWithSummary(stored *StoredSession, summary *SummarizeResponse, outputPath string) error
}

HTMLGenerator abstracts HTML generation to avoid import cycles (internal/session cannot import internal/session/html).

type HTMLGuidance

type HTMLGuidance struct {
	Instructions   []string        `json:"instructions"`
	OutputPath     string          `json:"output_path"`
	SourcePath     string          `json:"source_path"`
	Features       []string        `json:"features"`
	DelegationHint *DelegationHint `json:"delegation_hint,omitempty"`
}

HTMLGuidance contains instructions for generating HTML session viewer.

func GetHTMLGuidance

func GetHTMLGuidance(agentID, rawPath, outputPath string) HTMLGuidance

GetHTMLGuidance returns detailed guidance for generating HTML session viewer.

type HTMLGuidanceOutput

type HTMLGuidanceOutput struct {
	Success   bool         `json:"success"`
	Type      string       `json:"type"` // always "session_html_guidance"
	AgentID   string       `json:"agent_id"`
	Guidance  HTMLGuidance `json:"guidance"`
	Generated bool         `json:"generated"`
	HTMLPath  string       `json:"html_path,omitempty"`
	Message   string       `json:"message,omitempty"`
}

HTMLGuidanceOutput is the JSON output format for HTML generation guidance.

type HealthStatus

type HealthStatus struct {
	// StorageWritable indicates if local storage is writable
	StorageWritable bool

	// StoragePath is the path to local session storage
	StoragePath string

	// StorageError contains error details if storage is not writable
	StorageError string

	// RepoCloned indicates if the ledger is cloned
	RepoCloned bool

	// RepoPath is the path to the ledger
	RepoPath string

	// RepoError contains error details if ledger has issues
	RepoError string

	// IsRecordingActive indicates if a recording is currently in progress
	IsRecordingActive bool

	// Recording contains the current recording state (if active)
	Recording *RecordingState

	// IsStaleRecording indicates the active recording may be abandoned
	// (running for more than StaleRecordingThreshold)
	IsStaleRecording bool

	// StaleRecordingAge is how long the stale recording has been running
	StaleRecordingAge time.Duration

	// AllRecordings contains all active recording states found
	AllRecordings []*RecordingState

	// StaleRecordings contains recordings older than StaleRecordingThreshold
	StaleRecordings []*RecordingState

	// EmptyStaleCount is the number of stale recordings with no raw.jsonl (empty stubs)
	EmptyStaleCount int

	// ContentStaleCount is the number of stale recordings that have raw.jsonl content
	ContentStaleCount int

	// IsStopIncomplete indicates the recording was stopped but the session file was empty
	IsStopIncomplete bool

	// StopIncompleteAge is how long since the incomplete stop occurred
	StopIncompleteAge time.Duration

	// PendingCount is the number of sessions pending commit
	PendingCount int

	// SyncedWithRemote indicates if local repo is synced with remote
	SyncedWithRemote bool

	// SyncStatus describes the sync state (e.g., "ahead by 3", "behind by 1")
	SyncStatus string

	// Errors contains any errors encountered during health checks
	Errors []string
}

HealthStatus contains session system health information.

func CheckHealth

func CheckHealth(projectRoot string) *HealthStatus

CheckHealth performs a comprehensive health check of the session system. projectRoot is the current project's git root directory. If provided, ledger path is derived from it; if empty, ledger.DefaultPath() is used (cwd-based).

type HistoryEntry

type HistoryEntry struct {
	// Seq is the sequence number (monotonically increasing)
	Seq int `json:"seq"`

	// Type identifies the entry type (user, assistant, system, tool)
	Type string `json:"type"`

	// Content is the message or response content
	Content string `json:"content"`

	// Timestamp is when this entry occurred (if known)
	Timestamp time.Time `json:"ts,omitempty"`

	// Source identifies the origin of this entry
	Source string `json:"source,omitempty"` // "planning_history"

	// ToolName is the name of the tool called (for tool entries)
	ToolName string `json:"tool_name,omitempty"`

	// ToolInput is the input passed to the tool (for tool entries)
	ToolInput string `json:"tool_input,omitempty"`

	// ToolOutput is the output from the tool (for tool entries)
	ToolOutput string `json:"tool_output,omitempty"`

	// Summary is a brief summary of this entry (optional)
	Summary string `json:"summary,omitempty"`

	// IsPlan indicates this entry contains a plan or decision
	IsPlan bool `json:"is_plan,omitempty"`
}

HistoryEntry is a single entry in captured history.

func HistoryEntryFromSessionEntry

func HistoryEntryFromSessionEntry(e SessionEntry, seq int, source string) HistoryEntry

HistoryEntryFromSessionEntry creates a HistoryEntry from a SessionEntry.

func NewHistoryEntry

func NewHistoryEntry(seq int, entryType, content string) *HistoryEntry

NewHistoryEntry creates a new history entry.

func (*HistoryEntry) ToSessionEntry

func (e *HistoryEntry) ToSessionEntry() SessionEntry

ToSessionEntry converts a HistoryEntry to a SessionEntry.

type HistoryMeta

type HistoryMeta struct {
	// SchemaVersion identifies the history format version
	SchemaVersion string `json:"schema_version"`

	// CapturedAt is when this history was captured
	CapturedAt time.Time `json:"captured_at"`

	// StartedAt is when the original conversation began (if known)
	StartedAt time.Time `json:"started_at,omitempty"`

	// Source identifies how this history was generated
	Source string `json:"source"` // "agent_reconstruction"

	// AgentID is the short agent ID (e.g., "Oxa7b3")
	AgentID string `json:"agent_id"`

	// SessionID is an alias for AgentID for compatibility
	SessionID string `json:"session_id,omitempty"`

	// AgentType identifies the AI agent (e.g., "claude-code", "cursor")
	AgentType string `json:"agent_type,omitempty"`

	// SessionTitle is a human-readable title for this session
	SessionTitle string `json:"session_title,omitempty"`

	// Username is the authenticated user who created this session
	Username string `json:"username,omitempty"`

	// MessageCount is the total number of entries captured
	MessageCount int `json:"message_count,omitempty"`

	// TimeRange is the time span of captured history
	TimeRange *HistoryTimeRange `json:"time_range,omitempty"`
}

HistoryMeta is the metadata header for captured history. This is the first line of a captured history JSONL file.

func CreateCapturedHistoryMeta

func CreateCapturedHistoryMeta(agentID, agentType, source, title string) *HistoryMeta

CreateCapturedHistoryMeta creates metadata for captured history with given parameters.

func NewHistoryMeta

func NewHistoryMeta(agentID, source string) *HistoryMeta

NewHistoryMeta creates metadata for captured history.

type HistoryTimeRange

type HistoryTimeRange struct {
	Earliest time.Time `json:"earliest"`
	Latest   time.Time `json:"latest"`
}

HistoryTimeRange represents the time span of captured history.

type HistoryValidationResult

type HistoryValidationResult struct {
	Valid      bool     `json:"valid"`
	Errors     []string `json:"errors,omitempty"`
	EntryCount int      `json:"entry_count"`
	HasMeta    bool     `json:"has_meta"`
}

HistoryValidationResult contains the result of validating a history file.

func ValidateHistory

func ValidateHistory(h *CapturedHistory) *HistoryValidationResult

ValidateHistory validates a CapturedHistory and returns detailed results.

func ValidateHistoryFile

func ValidateHistoryFile(path string) (*HistoryValidationResult, error)

ValidateHistoryFile parses and validates a history file.

func ValidateHistoryJSONL

func ValidateHistoryJSONL(jsonl string) (*HistoryValidationResult, error)

ValidateHistoryJSONL validates a JSONL string as a history.

type MarkdownGenerator

type MarkdownGenerator struct {
	// contains filtered or unexported fields
}

MarkdownGenerator creates markdown sessions from stored sessions.

func NewMarkdownGenerator

func NewMarkdownGenerator() *MarkdownGenerator

NewMarkdownGenerator creates a new markdown generator.

func (*MarkdownGenerator) Generate

func (g *MarkdownGenerator) Generate(t *StoredSession) ([]byte, error)

Generate creates markdown bytes from a StoredSession.

func (*MarkdownGenerator) GenerateToFile

func (g *MarkdownGenerator) GenerateToFile(t *StoredSession, outputPath string) error

GenerateToFile writes the markdown session to a file. TODO(server-side): move to server-side for MVP+1; client should not write to ledger directly.

type MemoryStorage

type MemoryStorage struct {
	// contains filtered or unexported fields
}

MemoryStorage provides an in-memory implementation of Storage for testing. All data is stored in memory and lost when the instance is destroyed.

func NewMemoryStorage

func NewMemoryStorage() *MemoryStorage

NewMemoryStorage creates a new in-memory storage for testing.

func (*MemoryStorage) Clear

func (m *MemoryStorage) Clear()

Clear removes all sessions from memory (useful for test cleanup).

func (*MemoryStorage) Count

func (m *MemoryStorage) Count() int

Count returns the number of sessions in memory.

func (*MemoryStorage) Delete

func (m *MemoryStorage) Delete(filename string) error

Delete implements Storage.Delete for in-memory storage.

func (*MemoryStorage) Exists

func (m *MemoryStorage) Exists(filename string) bool

Exists implements Storage.Exists for in-memory storage.

func (*MemoryStorage) GetLatest

func (m *MemoryStorage) GetLatest() (*SessionInfo, error)

GetLatest implements Storage.GetLatest for in-memory storage.

func (*MemoryStorage) List

func (m *MemoryStorage) List() ([]SessionInfo, error)

List implements Storage.List for in-memory storage.

func (*MemoryStorage) Load

func (m *MemoryStorage) Load(filename string) (*StoredSession, error)

Load implements Storage.Load for in-memory storage.

func (*MemoryStorage) Save

func (m *MemoryStorage) Save(filename, sessionType string, meta *StoreMeta, entries []map[string]any) error

Save implements Storage.Save for in-memory storage.

type NeedsSummaryInfo

type NeedsSummaryInfo struct {
	CacheDir         string `json:"cache_dir"`
	RawPath          string `json:"raw_path"`
	LedgerSessionDir string `json:"ledger_session_dir"`
}

NeedsSummaryInfo describes a session that needs summary generation.

func FindSessionsNeedingSummary

func FindSessionsNeedingSummary(contextPath string) ([]NeedsSummaryInfo, error)

FindSessionsNeedingSummary scans the sessions directory under contextPath for cache session directories containing a .needs-summary marker.

type ParsedRedactFile added in v0.3.0

type ParsedRedactFile struct {
	Path   string
	Source RedactRuleSource
	Rules  []RedactRule
	Errors []RedactParseError
}

ParsedRedactFile holds all rules from a single REDACT.md file.

func ParseRedactFile added in v0.3.0

func ParseRedactFile(path string, source RedactRuleSource) (*ParsedRedactFile, error)

ParseRedactFile reads and parses a REDACT.md file. Returns nil, nil if the file does not exist. The parser is lenient: it collects errors per-line and continues parsing.

type PatternManifestEntry

type PatternManifestEntry struct {
	Name   string `json:"name"`
	Regex  string `json:"regex"`
	Redact string `json:"redact"`
}

PatternManifestEntry represents a single pattern in the manifest. This is the canonical, signable representation of a SecretPattern.

type PlanEntry

type PlanEntry struct {
	// Entry is the original session entry
	Entry SessionEntry

	// Marker indicates which plan marker was detected
	Marker PlanMarker

	// IsPlanFlag indicates whether the entry had is_plan:true metadata
	IsPlanFlag bool
}

PlanEntry represents an entry that contains plan content.

func ExtractPlan

func ExtractPlan(entries []SessionEntry) *PlanEntry

ExtractPlan finds the plan from a slice of session entries. Priority order:

  1. Entry with is_plan:true metadata (highest priority)
  2. Entry with "## Final Plan" header
  3. Entry with "## Implementation Plan" header
  4. Entry with "## Plan" header
  5. Last assistant message (fallback)

If multiple entries match the same marker type, the last one is returned. Returns nil if entries is empty or no suitable content is found.

type PlanMarker

type PlanMarker string

PlanMarker represents a type of plan marker found in content.

const (
	// PlanMarkerNone indicates no plan markers were found
	PlanMarkerNone PlanMarker = ""

	// PlanMarkerPlan indicates a "## Plan" header was found
	PlanMarkerPlan PlanMarker = "plan"

	// PlanMarkerImplementationPlan indicates a "## Implementation Plan" header was found
	PlanMarkerImplementationPlan PlanMarker = "implementation_plan"

	// PlanMarkerFinalPlan indicates a "## Final Plan" header was found
	PlanMarkerFinalPlan PlanMarker = "final_plan"

	// PlanMarkerIsPlan indicates the entry has is_plan:true metadata
	PlanMarkerIsPlan PlanMarker = "is_plan"
)

func DetectPlanMarkers

func DetectPlanMarkers(content string) PlanMarker

DetectPlanMarkers scans content for plan header markers. Returns the marker type found, or PlanMarkerNone if no markers detected. Checks line by line to find headers at the start of lines.

type QualityDisposition added in v0.5.0

type QualityDisposition string

QualityDisposition is the action to take based on a session's quality score.

const (
	// QualityUpload means the session should be pushed to the team ledger.
	QualityUpload QualityDisposition = "upload"
	// QualityLocalOnly means artifacts are kept locally but not pushed.
	QualityLocalOnly QualityDisposition = "local_only"
	// QualityDiscard means the session should be deleted entirely.
	QualityDiscard QualityDisposition = "discard"
)

func EvaluateQuality added in v0.5.0

func EvaluateQuality(score, uploadThreshold, discardThreshold float64) QualityDisposition

EvaluateQuality determines what to do with a session based on its quality score. A score of 0 means the LLM didn't provide one — default to upload (backward compat).

type RecordingState

type RecordingState struct {
	AgentID          string    `json:"agent_id"`
	StartedAt        time.Time `json:"started_at"`
	AdapterName      string    `json:"adapter_name"`
	SessionFile      string    `json:"session_file"` // source file from adapter (Claude Code JSONL)
	OutputFile       string    `json:"output_file"`  // output file being recorded
	SessionPath      string    `json:"session_path"` // path to session folder
	Title            string    `json:"title,omitempty"`
	EntryCount       int       `json:"entry_count"`
	LastReminderSeq  int       `json:"last_reminder_seq"`
	ReminderInterval int       `json:"reminder_interval"`
	FilterMode       string    `json:"filter_mode,omitempty"`    // "infra" or "all" - controls event filtering
	WorkspacePath    string    `json:"workspace_path,omitempty"` // git root / project directory
	Branch           string    `json:"branch,omitempty"`         // git branch at recording start

	// Parent-child session tracking for subagent workflows
	// When a parent spawns subagents, each subagent can report its session
	// back to the parent session for aggregation.
	ParentSessionPath string `json:"parent_session_path,omitempty"` // path to parent's session folder
	ParentAgentID     string `json:"parent_agent_id,omitempty"`     // parent's agent ID (e.g., "Oxa7b3")

	AgentType      string `json:"agent_type,omitempty"`      // original agent type for metadata: "codex", "amp", etc. Falls back to AdapterName if empty.
	StopIncomplete bool   `json:"stop_incomplete,omitempty"` // set when stop returned retry guidance (empty file)
	Model          string `json:"model,omitempty"`           // LLM model for generic adapters where ReadMetadata returns nil
	ParentPID      int    `json:"parent_pid,omitempty"`      // parent agent process ID for liveness detection
	SourceOffset   int64  `json:"source_offset,omitempty"`   // byte offset in source file for incremental reading
	Origin         string `json:"origin,omitempty"`          // session origin: "human", "subagent", "agent" (from agentx.DetectOrigin)
}

RecordingState tracks an active recording session. Stored in sessions/<session-name>/.recording.json

func LoadAllRecordingStates added in v0.3.0

func LoadAllRecordingStates(projectRoot string) ([]*RecordingState, error)

LoadAllRecordingStates returns all active recording states by searching for .recording.json in session folders. Unlike LoadRecordingState which returns only the first match, this returns all concurrent recordings (e.g., from multiple worktrees or agents).

func LoadRecordingState

func LoadRecordingState(projectRoot string) (*RecordingState, error)

LoadRecordingState loads active recording state by searching for .recording.json in session folders under the sessions directory. Returns nil, nil if no recording state exists.

func LoadRecordingStateForAgent added in v0.3.0

func LoadRecordingStateForAgent(projectRoot, agentID string) (*RecordingState, error)

LoadRecordingStateForAgent loads recording state for a specific agent. Returns nil, nil if no recording for that agent exists. Use this instead of LoadRecordingState when you have an agent ID to avoid accidentally operating on another concurrent agent's recording.

func StartRecording

func StartRecording(projectRoot string, opts StartRecordingOptions) (*RecordingState, error)

StartRecording begins a new recording session. Returns ErrAlreadyRecording if a recording is already in progress.

func StopRecording

func StopRecording(projectRoot, agentID string) (*RecordingState, error)

StopRecording ends an active recording session for a specific agent. Returns the final state and ErrNotRecording if no recording is active for this agent.

func (*RecordingState) Duration

func (r *RecordingState) Duration() time.Duration

Duration returns how long the recording has been running.

func (*RecordingState) IsAgentAlive added in v0.5.0

func (r *RecordingState) IsAgentAlive() bool

IsAgentAlive checks if the recording agent's parent process is still running. Uses kill(pid, 0) for instant liveness detection. Returns true if no PID is recorded (assume alive for backward compat).

func (*RecordingState) IsSubagent

func (r *RecordingState) IsSubagent() bool

IsSubagent returns true if this session was spawned by a parent agent. Checks both explicit parent tracking and environment-detected origin.

type RedactParseError added in v0.3.0

type RedactParseError struct {
	Path    string
	Line    int
	Message string
}

RedactParseError describes a parse error in a REDACT.md file.

func (RedactParseError) Error added in v0.3.0

func (e RedactParseError) Error() string

type RedactRule added in v0.3.0

type RedactRule struct {
	Type        string           // "literal" or "regex"
	RawPattern  string           // original text from the file
	Replacement string           // e.g. "[REDACTED_CODENAME]"
	Source      RedactRuleSource // team, repo, or user
	SourcePath  string           // absolute path to the REDACT.md file
	LineNumber  int              // line number in the source file
}

RedactRule is a single parsed rule from a REDACT.md file.

type RedactRuleSource added in v0.3.0

type RedactRuleSource string

RedactRuleSource identifies where a redaction rule originated.

const (
	RuleSourceBuiltin RedactRuleSource = "builtin"
	RuleSourceTeam    RedactRuleSource = "team"
	RuleSourceRepo    RedactRuleSource = "repo"
	RuleSourceUser    RedactRuleSource = "user"
)

type RedactSourceInfo added in v0.3.0

type RedactSourceInfo struct {
	Source  RedactRuleSource
	Path    string // empty if file not found
	Rules   []RedactRule
	Errors  []RedactParseError
	IOError error // non-nil if file could not be read (permission denied, etc.)
}

RedactSourceInfo describes a REDACT.md source with its resolved path and rules.

func DiscoverRedactSources added in v0.3.0

func DiscoverRedactSources(projectRoot string) []RedactSourceInfo

DiscoverRedactSources finds REDACT.md files at all levels and parses them. Returns source info for each level (even if the file doesn't exist, for reporting).

type RedactionManifest

type RedactionManifest struct {
	SchemaVersion string                 `json:"schema_version"`
	Patterns      []PatternManifestEntry `json:"patterns"`
}

RedactionManifest is the complete, signable manifest of all redaction patterns. The structure is designed for deterministic serialization.

func GenerateManifest

func GenerateManifest() *RedactionManifest

GenerateManifest creates a deterministic manifest from the default patterns. Patterns are sorted by name for consistent ordering.

func (*RedactionManifest) CanonicalJSON

func (m *RedactionManifest) CanonicalJSON() ([]byte, error)

CanonicalJSON returns the deterministic JSON encoding of the manifest. Uses sorted keys and no extra whitespace for consistent hashing.

func (*RedactionManifest) FindPattern

func (m *RedactionManifest) FindPattern(name string) *PatternManifestEntry

FindPattern looks up a pattern by name.

func (*RedactionManifest) Hash

func (m *RedactionManifest) Hash() ([]byte, error)

Hash returns the SHA-256 hash of the manifest's canonical JSON representation. This hash is what gets signed during release.

func (*RedactionManifest) HashHex

func (m *RedactionManifest) HashHex() (string, error)

HashHex returns the hex-encoded SHA-256 hash of the manifest.

func (*RedactionManifest) PatternCount

func (m *RedactionManifest) PatternCount() int

PatternCount returns the number of patterns in the manifest.

func (*RedactionManifest) PrettyJSON

func (m *RedactionManifest) PrettyJSON() ([]byte, error)

PrettyJSON returns human-readable JSON for display purposes.

type RedactionResult

type RedactionResult struct {
	PatternName string // which pattern matched
	Original    string // the original matched text (for logging, be careful with this)
	Position    int    // position in the string where match was found
}

RedactionResult contains details about a redaction

type Redactor

type Redactor struct {
	// contains filtered or unexported fields
}

Redactor handles secret detection and redaction

func NewRedactor

func NewRedactor() *Redactor

NewRedactor creates a new Redactor with default patterns

func NewRedactorWithPatterns

func NewRedactorWithPatterns(patterns []SecretPattern) *Redactor

NewRedactorWithPatterns creates a Redactor with custom patterns

func (*Redactor) AddPattern

func (r *Redactor) AddPattern(pattern SecretPattern)

AddPattern adds an additional pattern to the redactor

func (*Redactor) ContainsSecrets

func (r *Redactor) ContainsSecrets(input string) bool

ContainsSecrets checks if a string contains any secrets without modifying it

func (*Redactor) RedactCapturedHistory

func (r *Redactor) RedactCapturedHistory(history *CapturedHistory) (count int)

RedactCapturedHistory applies secret redaction to all entries in captured history. Returns the count of entries that contained secrets.

func (*Redactor) RedactEntries

func (r *Redactor) RedactEntries(entries []Entry) (count int)

RedactEntries redacts all entries in a slice. Returns the count of entries that contained secrets (in any field).

func (*Redactor) RedactEntry

func (r *Redactor) RedactEntry(entry *Entry) (redacted bool)

RedactEntry redacts secrets from an Entry's content. Returns true if any secrets were found and redacted.

func (*Redactor) RedactHistoryEntries

func (r *Redactor) RedactHistoryEntries(entries []HistoryEntry) (count int)

RedactHistoryEntries redacts secrets from HistoryEntry slices. Returns the count of entries that contained secrets (in any field).

func (*Redactor) RedactMap

func (r *Redactor) RedactMap(data map[string]any) bool

RedactMap redacts secrets from string values in a map. Useful for redacting raw JSON entries before storage. Returns true if any secrets were found and redacted.

func (*Redactor) RedactSlice

func (r *Redactor) RedactSlice(data []any) bool

RedactSlice redacts secrets from string values in a slice. Returns true if any secrets were found and redacted.

func (*Redactor) RedactString

func (r *Redactor) RedactString(input string) (output string, found []string)

RedactString scans and redacts secrets from a string. Returns the redacted output and a list of pattern names that matched.

func (*Redactor) RedactStringWithDetails

func (r *Redactor) RedactStringWithDetails(input string) (output string, results []RedactionResult)

RedactStringWithDetails provides detailed redaction results

func (*Redactor) RedactWithAllowlist

func (r *Redactor) RedactWithAllowlist(input string, allowlist []string) (output string, found []string)

RedactWithAllowlist redacts secrets but preserves strings in the allowlist. Useful for known-safe patterns that might trigger false positives.

func (*Redactor) ScanForSecrets

func (r *Redactor) ScanForSecrets(input string) []string

ScanForSecrets returns pattern names of all secrets found without redacting

type SageoxInsight

type SageoxInsight struct {
	Seq     int    `json:"seq"`     // message sequence number for navigation
	Topic   string `json:"topic"`   // domain/topic area (e.g., "react-patterns", "api-design")
	Insight string `json:"insight"` // what guidance was applied
	Impact  string `json:"impact"`  // the outcome or value it provided
}

SageoxInsight captures moments where SageOx guidance provided unique value. These are explicitly attributed in the conversation using phrases like "Based on SageOx guidance..." and document the product's contribution.

type SecretPattern

type SecretPattern struct {
	Name    string           // identifier for the pattern
	Pattern *regexp.Regexp   // compiled regex
	Redact  string           // replacement text, e.g., "[REDACTED_AWS_KEY]"
	Source  RedactRuleSource // origin: builtin, team, repo, or user (zero = builtin)
}

SecretPattern defines a pattern for detecting secrets

func DefaultPatterns

func DefaultPatterns() []SecretPattern

DefaultPatterns returns built-in secret patterns covering common credential types. Patterns are ordered roughly by specificity (more specific patterns first).

type Session

type Session struct {
	// Meta is session context (first line in JSONL)
	Meta *SessionMeta `json:"_meta"`

	// Entries are conversation turns (stored as separate JSONL lines)
	Entries []SessionEntry `json:"-"`
}

Session represents a complete recording session. The SessionMeta is stored as the first line of the JSONL file. Entries are stored as separate lines following the metadata.

func NewSession

func NewSession(meta *SessionMeta) *Session

NewSession creates a new session with the given metadata.

func (*Session) AddAssistantEntry

func (s *Session) AddAssistantEntry(content string)

AddAssistantEntry appends an assistant entry to the session.

func (*Session) AddEntry

func (s *Session) AddEntry(entry SessionEntry)

AddEntry appends an entry to the session.

func (*Session) AddSystemEntry

func (s *Session) AddSystemEntry(content string)

AddSystemEntry appends a system entry to the session.

func (*Session) AddToolEntry

func (s *Session) AddToolEntry(toolName, toolInput, toolOutput string)

AddToolEntry appends a tool entry to the session.

func (*Session) AddUserEntry

func (s *Session) AddUserEntry(content string)

AddUserEntry appends a user entry to the session.

func (*Session) Close

func (s *Session) Close()

Close finalizes the session by closing the metadata.

func (*Session) EntryCount

func (s *Session) EntryCount() int

EntryCount returns the number of entries in the session.

func (*Session) Footer

func (s *Session) Footer() *SessionFooter

Footer generates a footer for this session.

type SessionEntry

type SessionEntry struct {
	// Timestamp is when this entry was recorded
	Timestamp time.Time `json:"ts"`

	// Type identifies the entry type (user, assistant, system, tool)
	Type SessionEntryType `json:"type"`

	// Content is the message or response content
	Content string `json:"content"`

	// ToolName is the name of the tool called (for tool entries)
	ToolName string `json:"tool_name,omitempty"`

	// ToolInput is the input passed to the tool (for tool entries)
	ToolInput string `json:"tool_input,omitempty"`

	// ToolOutput is the output from the tool (for tool entries)
	ToolOutput string `json:"tool_output,omitempty"`

	// CoworkerName identifies the coworker or subagent that contributed to this entry.
	// This includes both team coworkers (loaded via ox coworker load) and built-in
	// Claude Code subagents (invoked via Task tool, e.g., code-reviewer, debugger).
	CoworkerName string `json:"coworker_name,omitempty"`

	// CoworkerModel is the model tier used by the coworker (e.g., sonnet, opus, haiku).
	CoworkerModel string `json:"coworker_model,omitempty"`
}

SessionEntry represents a single conversation turn in the session.

func NewAssistantEntry

func NewAssistantEntry(content string) SessionEntry

func NewAssistantSessionEntry

func NewAssistantSessionEntry(content string) SessionEntry

NewAssistantSessionEntry creates an assistant entry.

func NewCoworkerLoadEntry

func NewCoworkerLoadEntry(name, model string) SessionEntry

NewCoworkerLoadEntry creates a system entry for coworker/subagent load events. This enables metrics on which coworkers are being used in sessions.

func NewEntry

func NewEntry(entryType SessionEntryType, content string) SessionEntry

Function aliases for backward compatibility

func NewSessionEntry

func NewSessionEntry(entryType SessionEntryType, content string) SessionEntry

NewSessionEntry creates a new entry with the current timestamp.

func NewSystemEntry

func NewSystemEntry(content string) SessionEntry

func NewSystemSessionEntry

func NewSystemSessionEntry(content string) SessionEntry

NewSystemSessionEntry creates a system entry.

func NewToolEntry

func NewToolEntry(toolName, toolInput, toolOutput string) SessionEntry

func NewToolSessionEntry

func NewToolSessionEntry(toolName, toolInput, toolOutput string) SessionEntry

NewToolSessionEntry creates a new tool entry with the current timestamp.

func NewUserEntry

func NewUserEntry(content string) SessionEntry

func NewUserSessionEntry

func NewUserSessionEntry(content string) SessionEntry

NewUserSessionEntry creates a user entry.

type SessionEntryType

type SessionEntryType string

SessionEntryType represents the type of conversation turn.

const (
	// SessionEntryTypeUser is a user message or prompt
	SessionEntryTypeUser SessionEntryType = "user"

	// SessionEntryTypeAssistant is an assistant/AI response
	SessionEntryTypeAssistant SessionEntryType = "assistant"

	// SessionEntryTypeSystem is a system message (e.g., context injection)
	SessionEntryTypeSystem SessionEntryType = "system"

	// SessionEntryTypeTool is a tool call or result
	SessionEntryTypeTool SessionEntryType = "tool"
)

func (SessionEntryType) IsValid

func (e SessionEntryType) IsValid() bool

IsValid returns true if the entry type is recognized.

func (SessionEntryType) String

func (e SessionEntryType) String() string

String returns the string representation of the entry type.

type SessionError

type SessionError struct {
	Code      string `json:"code"`          // machine-readable error code (e.g., NOT_RECORDING)
	Message   string `json:"msg"`           // human-readable description
	Retryable bool   `json:"retry"`         // whether the operation can be retried
	Fix       string `json:"fix,omitempty"` // suggested fix command
}

SessionError represents a structured error for JSON output. Provides machine-readable error information for coding agents.

func NewSessionError

func NewSessionError(code, message string, retryable bool, fix string) *SessionError

NewSessionError creates a new SessionError.

func (*SessionError) Error

func (e *SessionError) Error() string

Error implements the error interface.

type SessionFilterMode

type SessionFilterMode string

SessionFilterMode represents the level of session recording. Values: "none", "all"

const (
	SessionFilterModeNone SessionFilterMode = "none" // no automatic sessions
	SessionFilterModeAll  SessionFilterMode = "all"  // all coding agent sessions
)

func (SessionFilterMode) IsValid

func (m SessionFilterMode) IsValid() bool

IsValid returns true if the mode is a recognized value.

func (SessionFilterMode) ShouldRecord

func (m SessionFilterMode) ShouldRecord() bool

ShouldRecord returns true if this mode enables any recording.

type SessionFooter

type SessionFooter struct {
	// EndedAt is when the recording session ended
	EndedAt time.Time `json:"ended_at"`

	// DurationMins is the total duration in minutes
	DurationMins int `json:"duration_minutes"`

	// EntryCount is the total number of entries recorded
	EntryCount int `json:"entry_count"`
}

SessionFooter is the last line of each session JSONL (optional). It provides summary statistics for the recording session.

func NewSessionFooter

func NewSessionFooter(startedAt time.Time, entryCount int) *SessionFooter

NewSessionFooter creates a footer with session statistics.

type SessionGuidance

type SessionGuidance struct {
	Include          []string `json:"include"`
	Exclude          []string `json:"exclude"`
	Tips             []string `json:"tips,omitempty"`
	ReminderInterval int      `json:"reminder_interval,omitempty"`

	// UserNotification is a message the agent should relay to the user.
	// This is separate from agent instructions - it's for human awareness.
	UserNotification string `json:"user_notification,omitempty"`
}

SessionGuidance contains structured guidance for session recording.

func RemindGuidance

func RemindGuidance() SessionGuidance

RemindGuidance returns condensed guidance for periodic reminders.

func StartGuidance

func StartGuidance() SessionGuidance

StartGuidance returns detailed guidance for beginning a session recording.

func StartGuidanceWithOptions

func StartGuidanceWithOptions(opts StartGuidanceOptions) SessionGuidance

StartGuidanceWithOptions returns detailed guidance with configurable options.

func StopGuidance

func StopGuidance() SessionGuidance

StopGuidance returns guidance for wrapping up a session recording.

type SessionInfo

type SessionInfo struct {
	SessionName     string              `json:"session_name,omitempty"` // session folder name (empty for legacy)
	Filename        string              `json:"filename"`               // file name only
	FilePath        string              `json:"file_path"`              // full path to file
	Type            string              `json:"type"`                   // "raw" or "events"
	Size            int64               `json:"size"`
	CreatedAt       time.Time           `json:"created_at"`
	ModTime         time.Time           `json:"mod_time"`
	HydrationStatus lfs.HydrationStatus `json:"hydration_status,omitempty"` // hydrated/dehydrated/partial
	Username        string              `json:"username,omitempty"`         // from meta.json
	Summary         string              `json:"summary,omitempty"`          // from meta.json
	Recording       bool                `json:"recording,omitempty"`        // true if session is actively being recorded
	AgentID         string              `json:"agent_id,omitempty"`         // from .recording.json when recording
	EntryCount      int                 `json:"entry_count,omitempty"`      // from .recording.json or meta.json
	IsSubagent      bool                `json:"is_subagent,omitempty"`      // true if spawned by a parent session
	ParentPID       int                 `json:"parent_pid,omitempty"`       // from .recording.json for liveness detection
	Origin          string              `json:"origin,omitempty"`           // session origin: "human", "subagent", "agent"
	HasRawData      bool                `json:"has_raw_data,omitempty"`     // true if raw.jsonl exists with content on disk
	StopReason      string              `json:"stop_reason,omitempty"`      // how session ended: "stopped", "aborted", "recovered"
}

SessionInfo contains metadata about a stored session.

type SessionMeta

type SessionMeta struct {
	// OxVersion is the version of ox that created this session
	OxVersion string `json:"ox_version"`

	// OxUsername is the authenticated SageOx username (if logged in)
	OxUsername string `json:"ox_username,omitempty"`

	// UserID is the unique user identifier from auth (if logged in)
	UserID string `json:"user_id,omitempty"`

	// SchemaVersion identifies the session format version
	SchemaVersion string `json:"schema_version"`

	// AgentType identifies the AI agent (e.g., "claude-code", "cursor", "copilot")
	AgentType string `json:"agent_type"`

	// AgentVersion is the version of the AI agent (if known)
	AgentVersion string `json:"agent_version,omitempty"`

	// Model is the LLM model used (e.g., "claude-sonnet-4-20250514")
	Model string `json:"model,omitempty"`

	// SessionID is the short agent ID (e.g., "Oxa7b3")
	SessionID string `json:"session_id"`

	// OxSID is the full session ID (e.g., "oxsid_01JEYQ9Z8X...")
	OxSID string `json:"oxsid"`

	// StartedAt is when the recording session began
	StartedAt time.Time `json:"started_at"`

	// EndedAt is when the recording session ended (set on close)
	EndedAt time.Time `json:"ended_at,omitempty"`

	// ProjectRemote is the git remote URL for provenance tracking
	ProjectRemote string `json:"project_remote,omitempty"`
}

SessionMeta is the first line of each session JSONL file. It captures context about the recording session for provenance and replay.

func NewSessionMeta

func NewSessionMeta(sessionID, oxsid, agentType, projectRemote, endpointURL string) *SessionMeta

NewSessionMeta creates metadata with current ox version and user. sessionID is the short agent ID (e.g., "Oxa7b3"). oxsid is the full session ID (e.g., "oxsid_01JEYQ9Z8X..."). agentType identifies the AI agent (e.g., "claude-code"). projectRemote is the git remote URL for provenance. endpointURL is the project endpoint for auth lookup (empty = default).

func NewSessionMetaWithVersion

func NewSessionMetaWithVersion(sessionID, oxsid, agentType, agentVersion, projectRemote, endpointURL string) *SessionMeta

NewSessionMetaWithVersion creates metadata with a specific agent version.

func (*SessionMeta) Close

func (m *SessionMeta) Close()

Close finalizes the metadata with end time.

func (*SessionMeta) Duration

func (m *SessionMeta) Duration() time.Duration

Duration returns the session duration. Returns zero duration if EndedAt is not set.

type SessionStatus added in v0.5.0

type SessionStatus string

SessionStatus represents the lifecycle state of a session.

Lifecycle:

           ┌──────────┐
           │ recording│
           └────┬─────┘
     ┌──────────┼──────────┐
     ▼          ▼          ▼
┌────────┐ ┌────────┐ ┌────────┐
│ paused │ │ ghost  │ │ orphan │
└───┬────┘ └───┬────┘ └───┬────┘
    │       (cleanup)  (finalize)
    ▼                      │
┌────────┐                 │
│ local  │◄────────────────┘
└───┬────┘
    ▼
┌──────────┐
│ uploaded  │
└──────────┘

┌──────────┐
│ canceled  │  (terminal — data discarded)
└──────────┘
const (
	StatusRecording SessionStatus = "recording" // actively being recorded, parent process alive
	StatusPaused    SessionStatus = "paused"    // user explicitly stopped recording (data preserved, not yet uploaded)
	StatusGhost     SessionStatus = "ghost"     // parent dead, no substantive data — safe to delete
	StatusOrphan    SessionStatus = "orphan"    // parent dead, has data — needs recovery/finalization
	StatusLocal     SessionStatus = "local"     // exists locally, not uploaded (may have been recovered from orphan)
	StatusUploaded  SessionStatus = "uploaded"  // committed to ledger
	StatusCanceled  SessionStatus = "canceled"  // user explicitly discarded session (terminal — data deleted)
)

func ClassifySession added in v0.5.0

func ClassifySession(info SessionInfo, isUploaded bool) SessionStatus

ClassifySession determines the lifecycle status of a session based on its metadata and whether it exists in the ledger. This is the single source of truth for session status — all display and cleanup code should use this instead of inline logic.

type SessionWriter

type SessionWriter struct {
	// contains filtered or unexported fields
}

SessionWriter handles JSONL writing with proper header/footer semantics.

func (*SessionWriter) Close

func (w *SessionWriter) Close() error

Close writes the footer and closes the file.

func (*SessionWriter) Count

func (w *SessionWriter) Count() int

Count returns the number of entries written (excluding header/footer).

func (*SessionWriter) FilePath

func (w *SessionWriter) FilePath() string

FilePath returns the path to the session file.

func (*SessionWriter) SessionName

func (w *SessionWriter) SessionName() string

SessionName returns the session folder name (empty for legacy files).

func (*SessionWriter) WriteEntry

func (w *SessionWriter) WriteEntry(entry Writable) error

WriteEntry writes a single entry to the session.

func (*SessionWriter) WriteHeader

func (w *SessionWriter) WriteHeader(meta *StoreMeta) error

WriteHeader writes the session header with metadata. Should be called once at the start of recording.

func (*SessionWriter) WriteRaw

func (w *SessionWriter) WriteRaw(data map[string]any) error

WriteRaw writes a raw entry map directly (for flexibility).

type StartGuidanceOptions

type StartGuidanceOptions struct {
	AutoStarted bool // true if session was auto-started by ox agent prime
}

StartGuidanceOptions configures the guidance returned by StartGuidance.

type StartRecordingOptions

type StartRecordingOptions struct {
	AgentID          string
	AdapterName      string
	SessionFile      string // source file from adapter (Claude Code JSONL)
	OutputFile       string // output file being recorded
	Title            string
	Username         string // username for session folder name
	RepoContextPath  string // path to repo context directory (for storing sessions)
	ReminderInterval int    // defaults to DefaultReminderInterval if 0
	FilterMode       string // "infra" or "all" - controls event filtering on stop
	WorkspacePath    string // git root / project directory
	Branch           string // git branch at recording start

	// Parent session tracking for subagent workflows
	ParentSessionPath string // path to parent's session folder (optional)
	ParentAgentID     string // parent's agent ID (optional)

	AgentType string // original agent type for metadata (e.g., "codex", "amp")
	Model     string // LLM model for generic adapters
	ParentPID int    // parent agent process ID for liveness detection
	Origin    string // session origin: "human", "subagent", "agent" (from agentx.DetectOrigin)
}

StartRecordingOptions contains options for starting a recording.

type Storage

type Storage interface {
	// Save writes a complete session with metadata and entries.
	// The filename should include the .jsonl extension.
	// The sessionType should be "raw" or "events".
	Save(filename, sessionType string, meta *StoreMeta, entries []map[string]any) error

	// Load reads a session by filename.
	// Searches both raw and events directories for file-based storage.
	Load(filename string) (*StoredSession, error)

	// List returns all session infos, sorted by date descending.
	List() ([]SessionInfo, error)

	// Delete removes a session by filename.
	Delete(filename string) error

	// Exists checks if a session with the given filename exists.
	Exists(filename string) bool

	// GetLatest returns the most recent session info.
	GetLatest() (*SessionInfo, error)
}

Storage defines the interface for session persistence. Implementations include FileStorage (default), MemoryStorage (testing), and future CloudStorage (SageOx API sync).

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store manages session storage in the ledger. Path structure: {project}_sageox_ledger/sessions/ Uses session-folder structure for organized storage of multiple output formats.

func NewStore

func NewStore(repoContextPath string) (*Store, error)

NewStore creates a store for the given ledger path. The repoContextPath should be the full path to the ledger directory (e.g., {project}_sageox_ledger/).

func (*Store) BasePath

func (s *Store) BasePath() string

BasePath returns the base path where sessions are stored.

func (*Store) CheckNeedsDownload

func (s *Store) CheckNeedsDownload(name string) string

CheckNeedsDownload returns the session name if the session exists as a stub (meta.json present but content files not hydrated). Returns empty string if the session doesn't exist or is already hydrated.

func (*Store) CreateRaw

func (s *Store) CreateRaw(sessionName string) (*SessionWriter, error)

CreateRaw creates a new raw session file and returns a writer. Uses session-folder structure: <session>/raw.jsonl The sessionName should be generated using GenerateSessionName().

func (*Store) Delete

func (s *Store) Delete(name string) error

Delete removes a session folder by name.

func (*Store) DeleteSession

func (s *Store) DeleteSession(sessionName string) error

DeleteSession removes a session folder and all its contents.

func (*Store) Exists

func (s *Store) Exists(filename string) bool

Exists implements Storage.Exists for file-based storage.

func (*Store) GetLatest

func (s *Store) GetLatest() (*SessionInfo, error)

GetLatest returns the most recent session info (from any type). Searches all sessions regardless of age.

func (*Store) GetLatestRaw

func (s *Store) GetLatestRaw() (*SessionInfo, error)

GetLatestRaw returns the most recent raw session info. Searches all sessions regardless of age.

func (*Store) GetSessionPath

func (s *Store) GetSessionPath(sessionName string) string

GetSessionPath returns the full path to a session folder.

func (*Store) IsSessionHydrated

func (s *Store) IsSessionHydrated(sessionName string) bool

IsSessionHydrated checks if content files exist locally for a session. Returns true if content files are present (authored locally or hydrated from LFS).

func (*Store) List

func (s *Store) List() ([]SessionInfo, error)

List implements Storage.List for file-based storage.

func (*Store) ListAllSessions

func (s *Store) ListAllSessions() ([]SessionInfo, error)

ListAllSessions returns all session files regardless of age. This may be slow with many sessions due to meta.json reads.

func (*Store) ListRawSessionsSince

func (s *Store) ListRawSessionsSince(since time.Time) ([]SessionInfo, error)

ListRawSessionsSince returns only raw session files created after the given time.

func (*Store) ListSessionNames

func (s *Store) ListSessionNames() ([]string, error)

ListSessionNames returns unique session folder names, sorted by date descending.

func (*Store) ListSessions

func (s *Store) ListSessions() ([]SessionInfo, error)

ListSessions returns session files from the last 7 days, sorted by date descending. Use ListSessionsSince for custom time windows or ListAllSessions for all sessions.

func (*Store) ListSessionsSince

func (s *Store) ListSessionsSince(since time.Time) ([]SessionInfo, error)

ListSessionsSince returns session files created after the given time. Pass zero time to list all sessions.

func (*Store) Load

func (s *Store) Load(filename string) (*StoredSession, error)

Load implements Storage.Load for file-based storage.

func (*Store) Prune

func (s *Store) Prune(olderThan time.Duration) (int, error)

Prune removes sessions older than the specified duration. Returns the number of sessions removed.

func (*Store) ReadLFSSessionMeta

func (s *Store) ReadLFSSessionMeta(sessionName string) (*lfs.SessionMeta, error)

ReadLFSSessionMeta reads the LFS meta.json for a session. Returns nil if the session has no meta.json (legacy or pre-LFS session).

func (*Store) ReadRawSession

func (s *Store) ReadRawSession(filename string) (*StoredSession, error)

ReadRawSession reads the raw session file from a session folder.

func (*Store) ReadSession

func (s *Store) ReadSession(name string) (*StoredSession, error)

ReadSession reads a session by session name or filename.

func (*Store) ReadSessionRaw

func (s *Store) ReadSessionRaw(sessionName string) (*StoredSession, error)

ReadSessionRaw reads the raw session from a session folder.

func (*Store) ResolveSessionName

func (s *Store) ResolveSessionName(name string) (string, error)

ResolveSessionName resolves a partial session name (e.g. agent ID suffix like "OxKMZN") to the full session directory name (e.g. "2026-01-28T18-56-ajit-sageox-ai-OxKMZN"). Returns the input unchanged if it already matches exactly or no match is found. Returns an error if multiple sessions match the suffix.

func (*Store) Save

func (s *Store) Save(filename, sessionType string, meta *StoreMeta, entries []map[string]any) error

Save implements Storage.Save for file-based storage. It creates a session file, writes header, entries, and footer.

type StoreMeta

type StoreMeta struct {
	Version      string    `json:"version"`
	CreatedAt    time.Time `json:"created_at"`
	AgentID      string    `json:"agent_id,omitempty"`
	AgentType    string    `json:"agent_type,omitempty"`
	AgentVersion string    `json:"agent_version,omitempty"` // version of the coding agent (e.g., "1.0.3")
	Model        string    `json:"model,omitempty"`         // LLM model used (e.g., "claude-sonnet-4-20250514")
	Username     string    `json:"username,omitempty"`
	RepoID       string    `json:"repo_id,omitempty"`
	OxVersion    string    `json:"ox_version,omitempty"` // version of ox that created this session
}

StoreMeta contains session storage metadata written to the header.

func ParseStoreMeta

func ParseStoreMeta(m map[string]any) *StoreMeta

ParseStoreMeta converts a map to StoreMeta struct. Supports both standard format (version, agent_id, created_at) and alternative format (schema_version, session_id, started_at).

type StoredSession

type StoredSession struct {
	Info    SessionInfo      `json:"info"`
	Meta    *StoreMeta       `json:"metadata,omitempty"`
	Entries []map[string]any `json:"entries"`
	Footer  map[string]any   `json:"footer,omitempty"`
}

StoredSession represents a read session with its entries. This is distinct from Session in secrets.go which is for redaction.

func ReadSessionFromPath

func ReadSessionFromPath(filePath string) (*StoredSession, error)

ReadSessionFromPath reads and parses a JSONL session from an arbitrary file path. This is useful for generating HTML from sessions outside the managed store location.

type SubagentCompleteOptions

type SubagentCompleteOptions struct {
	// SubagentID is the completing subagent's agent ID
	SubagentID string

	// ParentSessionPath is the path to the parent's session folder
	ParentSessionPath string

	// SessionPath is the path to the subagent's session (optional)
	SessionPath string

	// SessionName is the subagent's session folder name (optional)
	SessionName string

	// Summary describes what the subagent accomplished (optional)
	Summary string

	// DurationMs is how long the subagent ran (optional)
	DurationMs int64

	// EntryCount is the number of session entries (optional)
	EntryCount int

	// Model is the LLM model used (optional)
	Model string

	// AgentType is the coding agent type (optional)
	AgentType string
}

SubagentCompleteOptions configures subagent completion reporting.

type SubagentReference

type SubagentReference struct {
	// SessionID is the subagent's agent ID (e.g., "Oxa7b3")
	SessionID string `json:"session_id"`

	// SessionPath is the relative path to the subagent's session folder
	// Example: "sessions/2026-01-06T14-35-history-Oxdef/"
	SessionPath string `json:"session_path"`

	// EntryCount is the number of entries in the subagent's session
	EntryCount int `json:"entry_count"`

	// DurationMs is how long the subagent ran in milliseconds (optional)
	DurationMs int64 `json:"duration_ms,omitempty"`

	// Summary is a brief description of what the subagent did (optional)
	Summary string `json:"summary,omitempty"`

	// Model is the LLM model the subagent used (optional)
	Model string `json:"model,omitempty"`

	// AgentType is the coding agent type (optional)
	AgentType string `json:"agent_type,omitempty"`
}

SubagentReference represents metadata about a subagent session embedded in a Task tool entry. This is the structure that gets added to parent session entries.

type SubagentRegistry

type SubagentRegistry struct {
	// contains filtered or unexported fields
}

SubagentRegistry tracks subagent completions for a parent session. Thread-safe for concurrent subagent reporting.

func NewSubagentRegistry

func NewSubagentRegistry(sessionPath string) (*SubagentRegistry, error)

NewSubagentRegistry creates a registry for tracking subagent sessions. The sessionPath should be the parent session folder path.

func (*SubagentRegistry) Count

func (r *SubagentRegistry) Count() (int, error)

Count returns the number of registered subagent sessions.

func (*SubagentRegistry) List

func (r *SubagentRegistry) List() ([]*SubagentSession, error)

List returns all registered subagent sessions.

func (*SubagentRegistry) Register

func (r *SubagentRegistry) Register(session *SubagentSession) error

Register adds a completed subagent session to the registry. Thread-safe: multiple subagents can register concurrently.

type SubagentSession

type SubagentSession struct {
	// SubagentID is the subagent's agent ID (e.g., "Oxa7b3")
	SubagentID string `json:"subagent_id"`

	// ParentSessionID is the parent's session ID
	ParentSessionID string `json:"parent_session_id"`

	// SessionPath is the path to the subagent's raw session
	SessionPath string `json:"session_path,omitempty"`

	// SessionName is the subagent's session folder name
	SessionName string `json:"session_name,omitempty"`

	// CompletedAt is when the subagent finished
	CompletedAt time.Time `json:"completed_at"`

	// EntryCount is the number of entries in the subagent session
	EntryCount int `json:"entry_count,omitempty"`

	// Summary is a brief description of what the subagent did
	Summary string `json:"summary,omitempty"`

	// DurationMs is how long the subagent ran in milliseconds
	DurationMs int64 `json:"duration_ms,omitempty"`

	// Model is the LLM model the subagent used
	Model string `json:"model,omitempty"`

	// AgentType is the coding agent type (claude-code, cursor, etc.)
	AgentType string `json:"agent_type,omitempty"`
}

SubagentSession represents a completed subagent's session reference. These are stored in the parent session folder and aggregated on stop.

func FindSubagentSessions

func FindSubagentSessions(opts CaptureSubagentOptions) ([]*SubagentSession, error)

FindSubagentSessions searches for subagent sessions that match the given time window. It looks for sessions registered via the subagent registry and also scans for session folders that fall within the time window.

func GetSubagentSessions

func GetSubagentSessions(sessionPath string) ([]*SubagentSession, error)

GetSubagentSessions returns all subagent sessions for a session. The sessionPath should be the parent session folder path.

type SubagentSummary

type SubagentSummary struct {
	// Count is the number of subagents that completed
	Count int `json:"count"`

	// TotalEntries is the sum of all subagent session entries
	TotalEntries int `json:"total_entries"`

	// TotalDurationMs is the sum of all subagent durations
	TotalDurationMs int64 `json:"total_duration_ms"`

	// Subagents is the list of subagent references
	Subagents []*SubagentSession `json:"subagents,omitempty"`
}

SubagentSummary provides aggregated statistics about subagent work.

func SummarizeSubagents

func SummarizeSubagents(sessions []*SubagentSession) *SubagentSummary

SummarizeSubagents creates an aggregated summary of subagent work.

type SummarizeEntry

type SummarizeEntry struct {
	Type      string `json:"type"`
	Content   string `json:"content"`
	ToolName  string `json:"tool_name,omitempty"`
	Timestamp string `json:"timestamp,omitempty"`
}

SummarizeEntry is a simplified entry for summarization.

type SummarizeGuidance

type SummarizeGuidance struct {
	Instructions   []string        `json:"instructions"`
	Format         string          `json:"format"`
	SavePath       string          `json:"save_path"`
	LedgerPath     string          `json:"ledger_path"`
	DelegationHint *DelegationHint `json:"delegation_hint,omitempty"`
}

SummarizeGuidance contains instructions for the coding agent on how to create and save session summaries for context recovery.

func GetSummarizeGuidance

func GetSummarizeGuidance(agentID, contextPath string) SummarizeGuidance

GetSummarizeGuidance returns detailed guidance for creating session summaries.

type SummarizeGuidanceOutput

type SummarizeGuidanceOutput struct {
	Success  bool              `json:"success"`
	Type     string            `json:"type"` // always "session_summarize_guidance"
	AgentID  string            `json:"agent_id"`
	Guidance SummarizeGuidance `json:"guidance"`
	Summary  *SummarizeOutput  `json:"summary,omitempty"`
}

SummarizeGuidanceOutput is the JSON output format for summarize guidance.

type SummarizeOutput

type SummarizeOutput struct {
	Success       bool     `json:"success"`
	Type          string   `json:"type"` // always "session_summary"
	AgentID       string   `json:"agent_id"`
	Summary       string   `json:"summary"`
	KeyActions    []string `json:"key_actions,omitempty"`
	Outcome       string   `json:"outcome,omitempty"`
	TopicsFound   []string `json:"topics_found,omitempty"`
	FinalPlan     string   `json:"final_plan,omitempty"` // final plan/architecture from session
	Diagrams      []string `json:"diagrams,omitempty"`   // extracted mermaid diagrams
	EntryCount    int      `json:"entry_count,omitempty"`
	FilePath      string   `json:"file_path,omitempty"`
	Message       string   `json:"message,omitempty"`
	SummaryPrompt string   `json:"summary_prompt,omitempty"` // prompt for calling agent to generate summary
}

SummarizeOutput is the JSON output format for session summarization.

type SummarizeRequest

type SummarizeRequest struct {
	AgentID   string           `json:"agent_id"`
	AgentType string           `json:"agent_type"`
	Model     string           `json:"model,omitempty"`
	Entries   []SummarizeEntry `json:"entries"`
}

SummarizeRequest contains the session data to summarize.

type SummarizeResponse

type SummarizeResponse struct {
	Title          string           `json:"title"`                     // short descriptive title for the session
	Summary        string           `json:"summary"`                   // one paragraph executive summary
	KeyActions     []string         `json:"key_actions"`               // bullet points of key actions taken
	Outcome        string           `json:"outcome"`                   // success/partial/failed
	TopicsFound    []string         `json:"topics_found"`              // topics detected during session
	FinalPlan      string           `json:"final_plan,omitempty"`      // final plan/architecture from session
	Diagrams       []string         `json:"diagrams,omitempty"`        // extracted mermaid diagrams
	ChapterTitles  []string         `json:"chapter_titles,omitempty"`  // LLM-generated narrative chapter titles
	Chapters       []ChapterSummary `json:"chapters,omitempty"`        // structured chapter data (computed from JSONL)
	FilesChanged   []FileSummary    `json:"files_changed,omitempty"`   // files modified during session (computed from JSONL)
	AhaMoments     []AhaMoment      `json:"aha_moments,omitempty"`     // pivotal moments of collaborative intelligence
	SageoxInsights []SageoxInsight  `json:"sageox_insights,omitempty"` // moments where SageOx guidance provided value
	QualityScore   float64          `json:"quality_score"`             // 0.0-1.0 session value for team sharing
	ScoreReason    string           `json:"score_reason,omitempty"`    // brief explanation of the quality score
}

SummarizeResponse contains the LLM-generated summary plus computed metadata. The LLM produces: title, summary, key_actions, outcome, topics_found, chapter_titles, aha_moments, sageox_insights. The CLI computes and appends: chapters, files_changed.

func Summarize

func Summarize(entries []Entry, agentID, agentType, model, endpointURL string) (*SummarizeResponse, error)

Summarize calls the SageOx API to generate an LLM summary of a session. If endpointURL is non-empty, uses that endpoint for auth and API calls; otherwise falls back to the default endpoint. Returns nil if summarization fails (non-critical feature).

type SummaryMarkdownGenerator

type SummaryMarkdownGenerator struct{}

SummaryMarkdownGenerator generates markdown summaries from session data.

func NewSummaryMarkdownGenerator

func NewSummaryMarkdownGenerator() *SummaryMarkdownGenerator

NewSummaryMarkdownGenerator creates a new markdown generator.

func (*SummaryMarkdownGenerator) Generate

func (g *SummaryMarkdownGenerator) Generate(meta *StoreMeta, summary *SummaryView, entries []map[string]any) ([]byte, error)

Generate creates a summary markdown from metadata and summary view. The entries parameter is used to extract file modifications and diagrams.

type SummaryView

type SummaryView struct {
	Text        string   // one paragraph executive summary
	KeyActions  []string // bullet points of key actions taken
	Outcome     string   // success/partial/failed
	TopicsFound []string // topics detected during session
	FinalPlan   string   // final plan/architecture from session
	Diagrams    []string // extracted mermaid diagrams
}

SummaryView contains the LLM-generated summary content.

func SummarizeResponseToSummaryView added in v0.5.0

func SummarizeResponseToSummaryView(resp *SummarizeResponse) *SummaryView

SummarizeResponseToSummaryView converts a SummarizeResponse to the SummaryView used by the markdown generator.

type TaskInput

type TaskInput struct {
	// Description is the task description/prompt
	Description string `json:"description,omitempty"`

	// Prompt is an alternative field name for description
	Prompt string `json:"prompt,omitempty"`

	// AgentType hints at what kind of subagent to spawn
	AgentType string `json:"agent_type,omitempty"`
}

TaskInput represents the structured input to a Task tool call.

type TaskToolCallInfo

type TaskToolCallInfo struct {
	// EntryIndex is the index of this entry in the session
	EntryIndex int

	// Timestamp is when the Task tool was called
	Timestamp time.Time

	// ToolInput is the raw tool input (the task prompt)
	ToolInput string

	// ToolOutput is the raw tool output (the task result)
	ToolOutput string

	// ParsedInput contains structured task input if parseable
	ParsedInput *TaskInput
}

TaskToolCallInfo represents information extracted from a Task tool call entry.

func DetectTaskToolCalls

func DetectTaskToolCalls(entries []map[string]any) []TaskToolCallInfo

DetectTaskToolCalls scans session entries for Task tool calls. Returns a list of TaskToolCallInfo for each detected Task tool invocation.

type Writable

type Writable interface {
	// EntryType returns the type identifier for this entry (e.g., "message", "tool_call")
	EntryType() string
}

Writable is an interface for entries that can be written to a session.

Directories

Path Synopsis
Package html provides HTML session viewer generation.
Package html provides HTML session viewer generation.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL