Documentation
¶
Index ¶
- Constants
- func ApplyTransition(ctx context.Context, state *State, result TransitionResult, ...) error
- func ClearGitCommonDirCache()
- func MermaidDiagram() string
- type Action
- type ActionHandler
- type Event
- type NoOpActionHandler
- type Phase
- type PromptAttribution
- type State
- type StateStore
- func (s *StateStore) Clear(ctx context.Context, sessionID string) error
- func (s *StateStore) List(ctx context.Context) ([]*State, error)
- func (s *StateStore) Load(ctx context.Context, sessionID string) (*State, error)
- func (s *StateStore) RemoveAll() error
- func (s *StateStore) Save(ctx context.Context, state *State) error
- type TransitionContext
- type TransitionResult
Constants ¶
const ( // SessionStateDirName is the directory name for session state files within git common dir. SessionStateDirName = "entire-sessions" // StaleSessionThreshold is the duration after which an ended session is considered stale // and will be automatically deleted during load/list operations. StaleSessionThreshold = 7 * 24 * time.Hour )
Variables ¶
This section is empty.
Functions ¶
func ApplyTransition ¶ added in v0.4.5
func ApplyTransition(ctx context.Context, state *State, result TransitionResult, handler ActionHandler) error
ApplyTransition applies a TransitionResult to state: sets the new phase unconditionally, then executes all actions. Common actions (UpdateLastInteraction, ClearEndedAt) always run regardless of handler errors so that bookkeeping fields stay consistent with the new phase. Strategy-specific handler actions stop on the first error; subsequent handler actions are skipped but common actions continue. Returns the first handler error, or nil.
func ClearGitCommonDirCache ¶ added in v0.4.6
func ClearGitCommonDirCache()
ClearGitCommonDirCache clears the cached git common dir. Useful for testing when changing directories.
func MermaidDiagram ¶ added in v0.3.13
func MermaidDiagram() string
MermaidDiagram generates a Mermaid state diagram from the transition table. The diagram is derived by calling Transition() for representative context variants per phase/event, so it stays in sync with the implementation.
Types ¶
type Action ¶ added in v0.3.13
type Action int
Action represents a side effect that should be performed after a transition. The caller is responsible for executing these -- the state machine only declares them.
const ( ActionCondense Action = iota // Condense session data to permanent storage ActionCondenseIfFilesTouched // Condense only if FilesTouched is non-empty ActionDiscardIfNoFiles // Discard session if FilesTouched is empty ActionWarnStaleSession // Warn user about stale session(s) ActionClearEndedAt // Clear EndedAt timestamp (session re-entering) ActionUpdateLastInteraction // Update LastInteractionTime )
type ActionHandler ¶ added in v0.4.5
type ActionHandler interface {
HandleCondense(state *State) error
HandleCondenseIfFilesTouched(state *State) error
HandleDiscardIfNoFiles(state *State) error
HandleWarnStaleSession(state *State) error
}
ActionHandler defines strategy-specific side effects for state transitions. The compiler enforces that every strategy-specific action has a handler.
type Event ¶ added in v0.3.13
type Event int
Event represents something that happened to a session.
const ( EventTurnStart Event = iota // Agent begins working on a prompt EventTurnEnd // Agent finishes its turn EventGitCommit // A git commit was made (PostCommit hook) EventSessionStart // Session process started (SessionStart hook) EventSessionStop // Session process ended (SessionStop hook) EventCompaction // Agent compacted context mid-turn (PreCompress hook) )
type NoOpActionHandler ¶ added in v0.4.5
type NoOpActionHandler struct{}
NoOpActionHandler is a default ActionHandler where all methods are no-ops. Embed this in handler structs to only override the methods you need.
func (NoOpActionHandler) HandleCondense ¶ added in v0.4.5
func (NoOpActionHandler) HandleCondense(_ *State) error
func (NoOpActionHandler) HandleCondenseIfFilesTouched ¶ added in v0.4.5
func (NoOpActionHandler) HandleCondenseIfFilesTouched(_ *State) error
func (NoOpActionHandler) HandleDiscardIfNoFiles ¶ added in v0.4.5
func (NoOpActionHandler) HandleDiscardIfNoFiles(_ *State) error
func (NoOpActionHandler) HandleWarnStaleSession ¶ added in v0.4.5
func (NoOpActionHandler) HandleWarnStaleSession(_ *State) error
type Phase ¶ added in v0.3.13
type Phase string
Phase represents the lifecycle stage of a session.
func PhaseFromString ¶ added in v0.3.13
PhaseFromString normalizes a phase string, treating empty or unknown values as PhaseIdle for backward compatibility with pre-state-machine session files.
type PromptAttribution ¶
type PromptAttribution struct {
// CheckpointNumber is which checkpoint this was recorded before (1-indexed)
CheckpointNumber int `json:"checkpoint_number"`
// UserLinesAdded is lines added by user since the last checkpoint
UserLinesAdded int `json:"user_lines_added"`
// UserLinesRemoved is lines removed by user since the last checkpoint
UserLinesRemoved int `json:"user_lines_removed"`
// AgentLinesAdded is total agent lines added so far (base → last checkpoint).
// Always 0 for checkpoint 1 since there's no previous checkpoint to measure against.
AgentLinesAdded int `json:"agent_lines_added"`
// AgentLinesRemoved is total agent lines removed so far (base → last checkpoint).
// Always 0 for checkpoint 1 since there's no previous checkpoint to measure against.
AgentLinesRemoved int `json:"agent_lines_removed"`
// UserAddedPerFile tracks per-file user additions for accurate modification tracking.
// This enables distinguishing user self-modifications from agent modifications.
// See docs/architecture/attribution.md for details.
UserAddedPerFile map[string]int `json:"user_added_per_file,omitempty"`
}
PromptAttribution captures line-level attribution data at the start of each prompt. By recording what changed since the last checkpoint BEFORE the agent works, we can accurately separate user edits from agent contributions.
type State ¶
type State struct {
// SessionID is the unique session identifier
SessionID string `json:"session_id"`
// CLIVersion is the version of the CLI that created this session
CLIVersion string `json:"cli_version,omitempty"`
// BaseCommit tracks the current shadow branch base. Initially set to HEAD when the
// session starts, but updated on migration (pull/rebase) and after condensation.
// Used for shadow branch naming and checkpoint storage — NOT for attribution.
BaseCommit string `json:"base_commit"`
// AttributionBaseCommit is the commit used as the reference point for attribution calculations.
// Unlike BaseCommit (which tracks the shadow branch and moves with migration), this field
// preserves the original base commit so deferred condensation can correctly calculate
// agent vs human line attribution. Updated only after successful condensation.
AttributionBaseCommit string `json:"attribution_base_commit,omitempty"`
// WorktreePath is the absolute path to the worktree root
WorktreePath string `json:"worktree_path,omitempty"`
// WorktreeID is the internal git worktree identifier (empty for main worktree)
// Derived from .git/worktrees/<name>/, stable across git worktree move
WorktreeID string `json:"worktree_id,omitempty"`
// StartedAt is when the session was started
StartedAt time.Time `json:"started_at"`
// EndedAt is when the session was explicitly closed by the user.
// nil means the session is still active or was not cleanly closed.
EndedAt *time.Time `json:"ended_at,omitempty"`
// Phase is the lifecycle stage of this session (see phase.go).
// Empty means idle (backward compat with pre-state-machine files).
Phase Phase `json:"phase,omitempty"`
// TurnID is a unique identifier for the current agent turn.
// Lifecycle:
// - Generated fresh in InitializeSession at each turn start
// - Shared across all checkpoints within the same turn
// - Used to correlate related checkpoints when a turn's work spans multiple commits
// - Persists until the next InitializeSession call generates a new one
TurnID string `json:"turn_id,omitempty"`
// TurnCheckpointIDs tracks all checkpoint IDs condensed during the current turn.
// Lifecycle:
// - Set in PostCommit when a checkpoint is condensed for an ACTIVE session
// - Consumed in HandleTurnEnd to finalize all checkpoints with the full transcript
// - Cleared in HandleTurnEnd after finalization completes
// - Cleared in InitializeSession when a new prompt starts
// - Cleared when session is reset (ResetSession deletes the state file entirely)
TurnCheckpointIDs []string `json:"turn_checkpoint_ids,omitempty"`
// LastInteractionTime is updated on agent-interaction events (TurnStart,
// TurnEnd, SessionStop, Compaction) but NOT on git commit hooks.
// Used for stale session detection in "entire doctor".
LastInteractionTime *time.Time `json:"last_interaction_time,omitempty"`
// StepCount is the number of checkpoints/steps created in this session.
// JSON tag kept as "checkpoint_count" for backward compatibility with existing state files.
StepCount int `json:"checkpoint_count"`
// CheckpointTranscriptStart is the transcript line offset where the current
// checkpoint cycle began. Set to 0 at session start, updated to current
// transcript length after each condensation. Used to scope the transcript
// for checkpoint condensation: "everything since last checkpoint".
CheckpointTranscriptStart int `json:"checkpoint_transcript_start,omitempty"`
// CheckpointTranscriptSize is the byte size of the transcript at last condensation.
// Used for fast "has new content?" checks in PostCommit: compare the git blob size
// against this value without reading the full transcript content.
CheckpointTranscriptSize int64 `json:"checkpoint_transcript_size,omitempty"`
// Deprecated: CondensedTranscriptLines is replaced by CheckpointTranscriptStart.
// Kept for backward compatibility with existing state files.
// Use NormalizeAfterLoad() to migrate.
CondensedTranscriptLines int `json:"condensed_transcript_lines,omitempty"`
// UntrackedFilesAtStart tracks files that existed at session start (to preserve during rewind)
UntrackedFilesAtStart []string `json:"untracked_files_at_start,omitempty"`
// FilesTouched tracks files modified/created/deleted during this session
FilesTouched []string `json:"files_touched,omitempty"`
// LastCheckpointID is the checkpoint ID from the most recent condensation.
// Used to restore the Entire-Checkpoint trailer on amend and to identify
// sessions that have been condensed at least once. Cleared on new prompt.
LastCheckpointID id.CheckpointID `json:"last_checkpoint_id,omitempty"`
// FullyCondensed indicates this session has been condensed and has no remaining
// carry-forward files. PostCommit skips fully-condensed sessions entirely.
// Set after successful condensation when no files remain for carry-forward
// and the session phase is ENDED. Cleared on session reactivation (ENDED →
// ACTIVE via TurnStart, or ENDED → IDLE via SessionStart) by ActionClearEndedAt.
FullyCondensed bool `json:"fully_condensed,omitempty"`
// AgentType identifies the agent that created this session (e.g., "Claude Code", "Gemini CLI", "Cursor")
AgentType types.AgentType `json:"agent_type,omitempty"`
// ModelName is the LLM model used in this session (e.g., "claude-sonnet-4-20250514", "gpt-4o").
// Set from hook data when the agent provides it.
ModelName string `json:"model_name,omitempty"`
// Token usage tracking (accumulated across all checkpoints in this session)
TokenUsage *agent.TokenUsage `json:"token_usage,omitempty"`
// Hook-provided session metrics (for agents like Cursor that report via hooks)
SessionDurationMs int64 `json:"session_duration_ms,omitempty"`
SessionTurnCount int `json:"session_turn_count,omitempty"`
ContextTokens int `json:"context_tokens,omitempty"`
ContextWindowSize int `json:"context_window_size,omitempty"`
// Deprecated: TranscriptLinesAtStart is replaced by CheckpointTranscriptStart.
// Kept for backward compatibility with existing state files.
TranscriptLinesAtStart int `json:"transcript_lines_at_start,omitempty"`
// TranscriptIdentifierAtStart is the last transcript identifier when the session started.
// Used for identifier-based transcript scoping (UUID for Claude, message ID for Gemini).
TranscriptIdentifierAtStart string `json:"transcript_identifier_at_start,omitempty"`
// TranscriptPath is the path to the live transcript file (for mid-session commit detection)
TranscriptPath string `json:"transcript_path,omitempty"`
// LastPrompt is the most recent user prompt for this session (truncated for display).
// Updated on every turn start (UserPromptSubmit). JSON tag kept as "first_prompt"
// for backward compatibility with existing state files.
LastPrompt string `json:"last_prompt,omitempty"`
// PromptAttributions tracks user and agent line changes at each prompt start.
// This enables accurate attribution by capturing user edits between checkpoints.
PromptAttributions []PromptAttribution `json:"prompt_attributions,omitempty"`
// PendingPromptAttribution holds attribution calculated at prompt start (before agent runs).
// This is moved to PromptAttributions when SaveStep is called.
PendingPromptAttribution *PromptAttribution `json:"pending_prompt_attribution,omitempty"`
}
State represents the state of an active session. This is stored in .git/entire-sessions/<session-id>.json
func (*State) IsStale ¶ added in v0.4.6
IsStale returns true when a session hasn't seen interaction for longer than StaleSessionThreshold. Falls back to StartedAt when LastInteractionTime is nil (sessions created before interaction tracking was added).
func (*State) NormalizeAfterLoad ¶ added in v0.3.13
NormalizeAfterLoad applies backward-compatible migrations to state loaded from disk. Call this after deserializing a State from JSON.
type StateStore ¶
type StateStore struct {
// contains filtered or unexported fields
}
StateStore provides low-level operations for managing session state files.
StateStore is a primitive for session state persistence. It is NOT the same as the Sessions interface - it only handles state files in .git/entire-sessions/, not the full session data which includes checkpoint content.
Use StateStore directly in strategies for performance-critical state operations. Use the Sessions interface (when implemented) for high-level session management.
func NewStateStore ¶
func NewStateStore(ctx context.Context) (*StateStore, error)
NewStateStore creates a new state store. Uses the git common dir to store session state (shared across worktrees).
func NewStateStoreWithDir ¶
func NewStateStoreWithDir(stateDir string) *StateStore
NewStateStoreWithDir creates a new state store with a custom directory. This is useful for testing.
func (*StateStore) Clear ¶
func (s *StateStore) Clear(ctx context.Context, sessionID string) error
Clear removes the session state file for the given session ID.
func (*StateStore) List ¶
func (s *StateStore) List(ctx context.Context) ([]*State, error)
List returns all session states.
func (*StateStore) Load ¶
Load loads the session state for the given session ID. Returns (nil, nil) when session file doesn't exist or session is stale (not an error condition). Stale sessions (ended longer than StaleSessionThreshold ago) are automatically deleted.
func (*StateStore) RemoveAll ¶
func (s *StateStore) RemoveAll() error
RemoveAll removes the entire session state directory. This is used during uninstall to completely remove all session state.
type TransitionContext ¶ added in v0.3.13
type TransitionContext struct {
HasFilesTouched bool // len(FilesTouched) > 0
IsRebaseInProgress bool // .git/rebase-merge/ or .git/rebase-apply/ exists
}
TransitionContext provides read-only context for transitions that need to inspect session state without mutating it.
type TransitionResult ¶ added in v0.3.13
TransitionResult holds the outcome of a state machine transition.
func Transition ¶ added in v0.3.13
func Transition(current Phase, event Event, ctx TransitionContext) TransitionResult
Transition computes the next phase and required actions given the current phase and an event. This is a pure function with no side effects.
Empty or unknown phase values are normalized to PhaseIdle for backward compatibility with session state files created before phase tracking.