sessions

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Jul 9, 2026 License: MIT Imports: 20 Imported by: 0

Documentation

Index

Constants

View Source
const (
	MetadataFile = "metadata.json"
	EventsFile   = "events.jsonl"
)
View Source
const CheckpointsDir = "checkpoints"

CheckpointsDir is the per-session subdirectory holding content-addressed blobs.

Variables

This section is empty.

Functions

func CheckpointsEnabled

func CheckpointsEnabled() bool

CheckpointsEnabled reports whether checkpoint capture is enabled (default on; disabled with ZERO_CHECKPOINTS=off).

func DefaultRoot

func DefaultRoot(env map[string]string) string

func FormatExecPrompt

func FormatExecPrompt(prompt string, prepared PreparedExec) string

func IsResumableKind

func IsResumableKind(kind SessionKind) bool

IsResumableKind reports whether a session kind represents a standalone, user-resumable conversation rather than an agent sub-run. Regular ("") and user fork sessions are resumable; child (specialist/sub-agent) and spec draft/impl sessions are not — each agent task and /spec run creates one, so listing them in the resume picker floods it with non-conversation entries.

func ValidSessionID

func ValidSessionID(sessionID string) bool

Types

type AppendEventInput

type AppendEventInput struct {
	Type    EventType
	Payload any
}

type CheckpointFile

type CheckpointFile struct {
	Path    string `json:"path"`
	Blob    string `json:"blob,omitempty"`    // sha256 of prior content, "" if absent/skipped
	Absent  bool   `json:"absent,omitempty"`  // file did not exist before (restore -> delete)
	Skipped bool   `json:"skipped,omitempty"` // exceeded size cap; not recoverable
	Bytes   int    `json:"bytes,omitempty"`
	Mode    uint32 `json:"mode,omitempty"` // unix permission bits of the prior file; 0 if unknown (restore preserves existing mode)
}

CheckpointFile records the before-mutation state of one workspace file.

type CheckpointPayload

type CheckpointPayload struct {
	Tool  string           `json:"tool"`
	Files []CheckpointFile `json:"files"`
}

CheckpointPayload is the payload of an EventSessionCheckpoint event. It indexes the before-state blobs captured for one mutating tool call.

type ChildInput

type ChildInput struct {
	SessionID string
	Title     string
	Cwd       string
	ModelID   string
	Provider  string
	Tag       string
	Depth     int
	AgentName string
	TaskID    string
	Prompt    string
}

type CompactionOptions

type CompactionOptions struct {
	PreserveLast   int
	MaxPromptChars int
}

type CompactionPayload

type CompactionPayload struct {
	Summary                  string     `json:"summary"`
	PreserveLast             int        `json:"preserveLast"`
	CompactableCount         int        `json:"compactableCount"`
	PreservedCount           int        `json:"preservedCount"`
	CompactedThroughEventID  string     `json:"compactedThroughEventId,omitempty"`
	CompactedThroughSequence int        `json:"compactedThroughSequence,omitempty"`
	CompactableEvents        []EventRef `json:"compactableEvents,omitempty"`
	PreservedEvents          []EventRef `json:"preservedEvents,omitempty"`
	PromptChars              int        `json:"promptChars,omitempty"`
	Truncated                bool       `json:"truncated,omitempty"`
}

CompactionPayload is the payload of an EventCompaction event. It records the summary that should replace compacted-away events during replay.

func CompactionPayloadFromPlan

func CompactionPayloadFromPlan(summary string, plan CompactionPlan) (CompactionPayload, error)

type CompactionPlan

type CompactionPlan struct {
	SessionID         string     `json:"sessionId"`
	PreserveLast      int        `json:"preserveLast"`
	CompactableCount  int        `json:"compactableCount"`
	PreservedCount    int        `json:"preservedCount"`
	CompactableEvents []EventRef `json:"compactableEvents"`
	PreservedEvents   []EventRef `json:"preservedEvents"`
	SummaryPrompt     string     `json:"summaryPrompt"`
	PromptChars       int        `json:"promptChars"`
	Truncated         bool       `json:"truncated,omitempty"`
}

type CreateInput

type CreateInput struct {
	SessionID           string
	SessionKind         SessionKind
	Title               string
	Cwd                 string
	ModelID             string
	Provider            string
	Tag                 string
	Depth               int
	ParentSessionID     string
	RootSessionID       string
	AgentName           string
	TaskID              string
	ForkedFromEventID   string
	ForkedFromSequence  int
	SpawnedFromEventID  string
	SpawnedFromSequence int
	SpecID              string
	SpecFilePath        string
	SpecStatus          SpecStatus
	SpecDraftModelID    string
	SpecDraftReasoning  string
	SpecUserComment     string
	SpecRejectReason    string
	SpecSourceSessionID string
	SpecImplSessionID   string
}

type EnsureSpecImplementationInput

type EnsureSpecImplementationInput struct {
	Title               string
	Cwd                 string
	ModelID             string
	Provider            string
	SpecID              string
	SpecFilePath        string
	SpecDraftModelID    string
	SpecDraftReasoning  string
	SpecUserComment     string
	SpecSourceSessionID string
	RootSessionID       string
	Prompt              string
}

type Event

type Event struct {
	ID        string          `json:"id"`
	SessionID string          `json:"sessionId"`
	Sequence  int             `json:"sequence"`
	Type      EventType       `json:"type"`
	CreatedAt string          `json:"createdAt"`
	Payload   json.RawMessage `json:"payload,omitempty"`
}

func RehydrateEvents

func RehydrateEvents(events []Event) ([]Event, error)

func ReplayEvents

func ReplayEvents(events []Event) ([]Event, error)

type EventRef

type EventRef struct {
	ID        string    `json:"id"`
	Sequence  int       `json:"sequence"`
	Type      EventType `json:"type"`
	CreatedAt string    `json:"createdAt,omitempty"`
}

type EventType

type EventType string
const (
	EventMessage            EventType = "message"
	EventToolCall           EventType = "tool_call"
	EventPermission         EventType = "permission"
	EventPermissionRequest  EventType = "permission_request"
	EventPermissionDecision EventType = "permission_decision"
	EventToolResult         EventType = "tool_result"
	EventProviderUsage      EventType = "provider_usage"
	EventUsage              EventType = EventProviderUsage
	EventError              EventType = "error"
	EventSessionCheckpoint  EventType = "session_checkpoint"
	EventSessionRewind      EventType = "session_rewind"
	EventCompaction         EventType = "session_compaction"
	EventSessionFork        EventType = "session_fork"
	EventSessionChild       EventType = "session_child"
	EventSpecialistStart    EventType = "specialist_start"
	EventSpecialistStop     EventType = "specialist_stop"
	EventSpecDraft          EventType = "spec_draft"
	EventSpecApproved       EventType = "spec_approved"
	EventSpecRejected       EventType = "spec_rejected"
)

type ExecError

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

func (ExecError) Error

func (err ExecError) Error() string

type ExecMode

type ExecMode string
const (
	ModeNew    ExecMode = "new"
	ModeResume ExecMode = "resume"
	ModeFork   ExecMode = "fork"
)

type ForkInput

type ForkInput struct {
	SessionID string
	Title     string
	Cwd       string
	ModelID   string
	Provider  string
}

type Metadata

type Metadata struct {
	SessionID           string      `json:"sessionId"`
	SessionKind         SessionKind `json:"sessionKind,omitempty"`
	Title               string      `json:"title,omitempty"`
	Cwd                 string      `json:"cwd,omitempty"`
	ModelID             string      `json:"modelId,omitempty"`
	Provider            string      `json:"provider,omitempty"`
	Tag                 string      `json:"tag,omitempty"`
	Depth               int         `json:"depth,omitempty"`
	ParentSessionID     string      `json:"parentSessionId,omitempty"`
	RootSessionID       string      `json:"rootSessionId,omitempty"`
	AgentName           string      `json:"agentName,omitempty"`
	TaskID              string      `json:"taskId,omitempty"`
	ForkedFromEventID   string      `json:"forkedFromEventId,omitempty"`
	ForkedFromSequence  int         `json:"forkedFromSequence,omitempty"`
	SpawnedFromEventID  string      `json:"spawnedFromEventId,omitempty"`
	SpawnedFromSequence int         `json:"spawnedFromSequence,omitempty"`
	SpecID              string      `json:"specId,omitempty"`
	SpecFilePath        string      `json:"specFilePath,omitempty"`
	SpecStatus          SpecStatus  `json:"specStatus,omitempty"`
	SpecDraftModelID    string      `json:"specDraftModelId,omitempty"`
	SpecDraftReasoning  string      `json:"specDraftReasoning,omitempty"`
	SpecUserComment     string      `json:"specUserComment,omitempty"`
	SpecRejectReason    string      `json:"specRejectReason,omitempty"`
	SpecSourceSessionID string      `json:"specSourceSessionId,omitempty"`
	SpecImplSessionID   string      `json:"specImplSessionId,omitempty"`
	CreatedAt           string      `json:"createdAt"`
	UpdatedAt           string      `json:"updatedAt"`
	EventCount          int         `json:"eventCount"`
	LastEventType       EventType   `json:"lastEventType,omitempty"`
}

type PrepareExecOptions

type PrepareExecOptions struct {
	Store            *Store
	SessionID        string
	Title            string
	Cwd              string
	ModelID          string
	Provider         string
	Tag              string
	Depth            int
	CallingSessionID string
	CallingToolUseID string
	AgentName        string
	TaskID           string
	Resume           string
	ResumeLatest     bool
	Fork             string
}

type PreparedExec

type PreparedExec struct {
	Mode          ExecMode
	Session       Metadata
	ContextEvents []Event
	Store         *Store
}

func PrepareExec

func PrepareExec(options PrepareExecOptions) (PreparedExec, error)

type RecordCompactionInput

type RecordCompactionInput struct {
	Plan    CompactionPlan
	Summary string
}

type RecordSpecInput

type RecordSpecInput struct {
	SpecID              string
	SpecFilePath        string
	SpecStatus          SpecStatus
	SpecDraftModelID    string
	SpecDraftReasoning  string
	SpecUserComment     string
	SpecRejectReason    string
	SpecSourceSessionID string
	SpecImplSessionID   string
}

type RestoreReport

type RestoreReport struct {
	TargetSequence int      `json:"targetSequence"`
	FilesRestored  int      `json:"filesRestored"`
	FilesDeleted   int      `json:"filesDeleted"`
	Skipped        []string `json:"skipped,omitempty"` // paths whose before-state was not recoverable
}

RestoreReport summarizes a workspace restore.

type RewindMarker

type RewindMarker struct {
	TargetSequence int           `json:"targetSequence"`
	Report         RestoreReport `json:"report"`
}

RewindMarker is the payload of the EventSessionRewind event appended after a rewind.

type RewindOptions

type RewindOptions struct {
	TargetSequence int
	TargetEventID  string
	KeepTarget     bool
}

type RewindPlan

type RewindPlan struct {
	SessionID       string     `json:"sessionId"`
	TargetSequence  int        `json:"targetSequence"`
	TargetEventID   string     `json:"targetEventId"`
	KeepTarget      bool       `json:"keepTarget"`
	KeptCount       int        `json:"keptCount"`
	DroppedCount    int        `json:"droppedCount"`
	LastKeptEventID string     `json:"lastKeptEventId,omitempty"`
	KeptEvents      []EventRef `json:"keptEvents"`
	DroppedEvents   []EventRef `json:"droppedEvents"`
}

type SessionKind

type SessionKind string
const (
	SessionKindFork      SessionKind = "fork"
	SessionKindChild     SessionKind = "child"
	SessionKindSpecDraft SessionKind = "spec-draft"
	SessionKindSpecImpl  SessionKind = "spec-impl"
)

type SpecStatus

type SpecStatus string
const (
	SpecStatusDraft    SpecStatus = "draft"
	SpecStatusApproved SpecStatus = "approved"
	SpecStatusRejected SpecStatus = "rejected"
)

type Store

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

func NewStore

func NewStore(options StoreOptions) *Store

func (*Store) AppendEvent

func (store *Store) AppendEvent(sessionID string, input AppendEventInput) (Event, error)

func (*Store) AppendEventUnlessExists

func (store *Store) AppendEventUnlessExists(sessionID string, input AppendEventInput, exists func([]Event) bool) (Event, bool, error)

AppendEventUnlessExists appends input only when exists(currentEvents) is false, performing the existence check and the append atomically under the session lock (in-process mutex + cross-process file lock). It is the safe primitive for "record once" accounting: a plain ReadEvents-then-AppendEvent has a check-then-act race where two callers — even in separate processes — both see the event absent and each append a duplicate. Returns appended=false when the predicate already matched. A nil predicate always appends.

func (*Store) AppendEvents added in v0.2.0

func (store *Store) AppendEvents(sessionID string, inputs []AppendEventInput) ([]Event, error)

AppendEvents appends a batch of events atomically under one session lock and durability pass. The returned events have contiguous sequences in input order. An empty batch is a no-op and does not rewrite metadata.

func (*Store) ApplyRewind

func (store *Store) ApplyRewind(sessionID, workspaceRoot string, targetSeq int) (RestoreReport, error)

ApplyRewind performs a full safe rewind: restore workspace files to targetSeq, truncate the event log, prune now-orphaned blobs, and append an EventSessionRewind marker. Returns the restore report.

func (*Store) CaptureToolCheckpoint

func (store *Store) CaptureToolCheckpoint(sessionID, workspaceRoot, tool string, paths []string) (Event, error)

CaptureToolCheckpoint snapshots the current (before-mutation) content of each path and records an EventSessionCheckpoint indexing the blobs. Capture is best-effort: an unreadable file is recorded as skipped rather than failing the caller. Returns the appended event (or a zero Event if there was nothing to do).

func (*Store) Create

func (store *Store) Create(input CreateInput) (Metadata, error)

func (*Store) CreateChild

func (store *Store) CreateChild(parentSessionID string, input ChildInput) (Metadata, error)

func (*Store) EnsureSpecImplementation

func (store *Store) EnsureSpecImplementation(input EnsureSpecImplementationInput) (Metadata, []Event, error)

func (*Store) FindSpecImplementation

func (store *Store) FindSpecImplementation(specID string, sourceSessionID string) (*Metadata, error)

func (*Store) Fork

func (store *Store) Fork(parentSessionID string, input ForkInput) (Metadata, error)

func (*Store) Get

func (store *Store) Get(sessionID string) (*Metadata, error)

func (*Store) Latest

func (store *Store) Latest() (*Metadata, error)

func (*Store) LatestResumable

func (store *Store) LatestResumable() (*Metadata, error)

LatestResumable returns the most-recently-updated resumable session, or nil when none exist. Used by `/resume latest` so it lands on a real conversation instead of the newest child/spec sub-run.

func (*Store) Lineage

func (store *Store) Lineage(sessionID string) ([]Metadata, error)

func (*Store) List

func (store *Store) List() ([]Metadata, error)

func (*Store) ListChildren

func (store *Store) ListChildren(parentSessionID string) ([]Metadata, error)

func (*Store) ListResumable

func (store *Store) ListResumable() ([]Metadata, error)

ListResumable returns only the sessions a user can resume as standalone conversations (see IsResumableKind), newest-first like List.

func (*Store) PlanCompaction

func (store *Store) PlanCompaction(sessionID string, options CompactionOptions) (CompactionPlan, error)

func (*Store) PlanRewind

func (store *Store) PlanRewind(sessionID string, options RewindOptions) (RewindPlan, error)

func (*Store) ReadEvents

func (store *Store) ReadEvents(sessionID string) ([]Event, error)

func (*Store) ReadRehydratedEvents

func (store *Store) ReadRehydratedEvents(sessionID string) ([]Event, error)

func (*Store) ReadReplayEvents

func (store *Store) ReadReplayEvents(sessionID string) ([]Event, error)

func (*Store) RecordCompaction

func (store *Store) RecordCompaction(sessionID string, input RecordCompactionInput) (Event, error)

func (*Store) RecordSpec

func (store *Store) RecordSpec(sessionID string, input RecordSpecInput) (Metadata, Event, error)

func (*Store) RestoreToSequence

func (store *Store) RestoreToSequence(sessionID, workspaceRoot string, targetSeq int) (RestoreReport, error)

RestoreToSequence reverts workspace files to their state at targetSeq by applying the before-snapshots of every checkpoint after the target, newest-first (so the snapshot closest to the target wins). It does not modify the event log.

func (*Store) SnapshotForCheckpoint

func (store *Store) SnapshotForCheckpoint(sessionID, workspaceRoot, tool string, paths []string) (CheckpointPayload, bool)

SnapshotForCheckpoint reads and stores the before-mutation blobs for paths and returns the checkpoint payload WITHOUT appending an event.

CONTRACT: the returned payload references blobs that are ORPHAN-VULNERABLE — a concurrent pruneOrphanBlobs/ApplyRewind can delete them — until the caller appends an EventSessionCheckpoint carrying this payload. The caller therefore MUST record that event promptly, including on cancellation paths. Prefer CaptureToolCheckpoint, which writes the blobs AND appends the event atomically under one session lock; use SnapshotForCheckpoint only when the event must be batched IN ORDER with other session events (the TUI captures before each mutating tool, batches the checkpoint with the run's other events to preserve recorded ordering, and flushes them at end-of-run and on cancel).

Returns ok=false when there is nothing to record (disabled, no paths, or no capturable files).

func (*Store) Tree

func (store *Store) Tree(rootSessionID string) (TreeNode, error)

func (*Store) TruncateEvents

func (store *Store) TruncateEvents(sessionID string, keepThroughSequence int) error

TruncateEvents atomically rewrites events.jsonl keeping only events with Sequence <= keepThroughSequence, and updates metadata EventCount.

func (*Store) UpdateTitle

func (store *Store) UpdateTitle(sessionID string, title string) (Metadata, error)

UpdateTitle replaces a session's Title and returns the updated metadata. It is serialized under the same per-session lock as AppendEvent and re-reads the latest metadata under that lock before rewriting, so a concurrent append can't clobber the new title (nor the title clobber a concurrent append's event count/timestamp). UpdatedAt is deliberately left untouched: a retitle is not activity, so it must not reorder the session in the resumable list. A blank title is rejected so a failed model generation can never erase a useful first-message title, and an unchanged title is a no-op (no rewrite/fsync).

type StoreOptions

type StoreOptions struct {
	RootDir string
	Now     func() time.Time
	Env     map[string]string
}

type TreeNode

type TreeNode struct {
	Session  Metadata   `json:"session"`
	Children []TreeNode `json:"children"`
}

Jump to

Keyboard shortcuts

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