session

package
v0.11.0-beta.2 Latest Latest
Warning

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

Go to latest
Published: Oct 23, 2026 License: Apache-2.0, MIT Imports: 20 Imported by: 0

Documentation

Overview

Package session — prompt stash storage.

StashStore persists draft prompts to ~/.overkill/stash.json so users can save a half-written message and restore it later.

Index

Constants

View Source
const MaxRuntimeStatusBytes = 2 * 1024 * 1024

Variables

View Source
var ErrConflict = errors.New("session: concurrent write conflict — reload before retrying")
View Source
var ErrExists = errors.New("session: already exists")
View Source
var ErrMergeDiverged = fmt.Errorf("session: merge: parent diverged past branch point")

ErrMergeDiverged is returned by Merge when the parent has accumulated messages past the child's branch point.

View Source
var ErrNotFound = errors.New("session: not found")

Functions

func DefaultStashPath

func DefaultStashPath() (string, error)

DefaultStashPath returns ~/.overkill/stash.json.

func EnforceMax

func EnforceMax(ctx context.Context, store Store, max int) (int, error)

EnforceMax retains the newest max initially top-level sessions. Sub-sessions are excluded from the snapshot and never selected for pruning; deleting a root retains the store's existing family promotion policy. Nonpositive max means unlimited. Concurrent new roots are considered on a later pass.

PostgreSQL performs selection/deletion atomically. Generic stores are scanned completely before deletion, with bounded pages and a private ID/version disk spool. Their interface cannot make concurrent writes atomic: changed or missing candidates are skipped and a later pass may still be necessary.

Types

type Brancher

type Brancher interface {
	Branch(ctx context.Context, parentID string, atTurn int) (*Session, error)
	Clone(ctx context.Context, parentID string) (*Session, error)
	Merge(ctx context.Context, childID string) (*Session, error)
}

Brancher is the optional store capability that supports tree-structured sessions. PostgresStore satisfies this.

type ExportRitual

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

func NewExportRitual

func NewExportRitual(store Store, exportPath string) *ExportRitual

func (*ExportRitual) Export

func (er *ExportRitual) Export(ctx context.Context) error

type HistoryReader

type HistoryReader interface {
	LoadMessages(ctx context.Context, id string) ([]providers.Message, error)
}

HistoryReader returns an independently owned snapshot of committed messages. It must be safe during concurrent turns, writes and deletes, and must not expose mutable in-memory agent history or uncommitted database changes. Stores without this capability retain exclusive API admission for history.

type ListOptions

type ListOptions struct {
	Folder      string
	Status      string
	ParentID    string
	Limit       int
	Offset      int
	After       time.Time
	OldestFirst bool // Stable updated_at/id ASC instead of DESC; pagination must preserve this order.
}

type PostgresStore

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

PostgresStore is a PostgreSQL-backed session store that implements Store, Brancher, and session-router operations on the shared *sql.DB.

func NewPostgresStore

func NewPostgresStore(db *sql.DB) (*PostgresStore, error)

NewPostgresStore creates a PostgresStore backed by the shared DB. The caller owns the DB lifecycle; Close() is a no-op.

func OpenPostgresStore

func OpenPostgresStore(ctx context.Context, connString string) (*PostgresStore, error)

OpenPostgresStore opens and owns its database pool. Close releases the pool. NewPostgresStore instead borrows a pool whose lifecycle belongs to its caller.

func (*PostgresStore) Bind

func (s *PostgresStore) Bind(ctx context.Context, channel, chatKey, thread, sessionID string) error

Bind locks a channel/chat/thread tuple to a session id.

func (*PostgresStore) Branch

func (s *PostgresStore) Branch(ctx context.Context, parentID string, atTurn int) (*Session, error)

Branch creates a new session forked from parentID at message-index atTurn. Loads the parent inside the transaction to prevent TOCTOU races: without this, changes to the parent between Load and BeginTx are silently lost.

func (*PostgresStore) Clone

func (s *PostgresStore) Clone(ctx context.Context, parentID string) (*Session, error)

Clone creates an independent duplicate of parentID with the full message history copied over. Loads the parent inside the transaction to prevent TOCTOU races.

func (*PostgresStore) Close

func (s *PostgresStore) Close() error

Close releases only a pool created by OpenPostgresStore. Borrowed pools remain usable by the other stores that share them.

func (*PostgresStore) Create

func (s *PostgresStore) Create(ctx context.Context, session *Session) error

Create inserts a new session. Returns ErrExists when a session with the same ID already exists.

func (*PostgresStore) Delete

func (s *PostgresStore) Delete(ctx context.Context, id string) error

Delete removes a session, all its session_router bindings, and reassigns any child sessions' ParentID to the deleted session's parent (if any). This avoids dangling ParentID references — children are promoted one level up the tree rather than being orphaned or cascade-deleted.

func (*PostgresStore) Follow

func (s *PostgresStore) Follow(ctx context.Context, channel, chatKey, target string) error

Follow sets a chat's follow target. target="tui" shadows the live TUI session; any other non-empty value pins to that session; empty clears follow mode. Channel is required to scope the follow to a specific gateway (telegram, discord, etc.) — without it different channels sharing the same chatKey would collide on follow state.

func (*PostgresStore) List

func (s *PostgresStore) List(ctx context.Context, opts ListOptions) ([]*Session, error)

List returns sessions matching the optional filters, ordered by updated_at DESC. Limit and Offset are applied server-side.

func (*PostgresStore) Load

func (s *PostgresStore) Load(ctx context.Context, id string) (*Session, error)

Load fetches a session by ID. Returns ErrNotFound if no row matches. Children are populated by a second query.

func (*PostgresStore) LoadMessages

func (s *PostgresStore) LoadMessages(ctx context.Context, id string) ([]providers.Message, error)

LoadMessages uses one ordinary MVCC read. It neither waits on a turn's row update lock nor observes that turn before commit. JSON decoding owns the returned memory independently of the agent and all other readers.

func (*PostgresStore) LoadRuntimeStatus

func (s *PostgresStore) LoadRuntimeStatus(ctx context.Context, id string) (json.RawMessage, error)

func (*PostgresStore) Merge

func (s *PostgresStore) Merge(ctx context.Context, childID string) (*Session, error)

Merge fast-forwards a child branch back into its parent. Wrapped in a transaction to prevent lost-update races: concurrent merges on the same parent are serialised by PostgreSQL row locks.

func (*PostgresStore) Recent

func (s *PostgresStore) Recent(ctx context.Context, limit int) ([]RouterRow, error)

Recent returns up to limit bindings sorted newest-first.

func (*PostgresStore) Resolve

func (s *PostgresStore) Resolve(ctx context.Context, channel, chatKey, thread, liveSessionID string) (string, error)

Resolve looks up the session id for a channel/chat/thread binding. If follow mode points to a pinned session or "tui", that takes priority. Returns the resolved session ID, or empty string if nothing is bound.

func (*PostgresStore) Save

func (s *PostgresStore) Save(ctx context.Context, session *Session) error

Save persists an updated session. The updated_at timestamp is set to NOW().

func (*PostgresStore) SaveRuntimeStatus

func (s *PostgresStore) SaveRuntimeStatus(ctx context.Context, id string, data json.RawMessage) error

func (*PostgresStore) Touch

func (s *PostgresStore) Touch(ctx context.Context, channel, chatKey, thread string) error

Touch bumps the updated_at timestamp for a binding so /sessions can sort by recency. No-op if the binding doesn't exist.

func (*PostgresStore) Unbind

func (s *PostgresStore) Unbind(ctx context.Context, channel, chatKey, thread string) error

Unbind removes a binding from the router table.

type RouterRow

type RouterRow struct {
	SessionID string
	Channel   string
	Updated   time.Time
}

RouterRow is one row returned by the router's Recent() method.

type RuntimeStatusStore

type RuntimeStatusStore interface {
	SaveRuntimeStatus(context.Context, string, json.RawMessage) error
	LoadRuntimeStatus(context.Context, string) (json.RawMessage, error)
}

RuntimeStatusStore persists actual observed evaluation/check state separately from transcript saves, so one writer cannot overwrite the other's metadata.

type Session

type Session struct {
	ID         string              `json:"id"`
	Title      string              `json:"title"`
	Folder     string              `json:"folder"`
	ParentID   string              `json:"parent_id,omitempty"`
	CreatedAt  time.Time           `json:"created_at"`
	UpdatedAt  time.Time           `json:"updated_at"`
	Model      string              `json:"model"`
	Provider   string              `json:"provider"`
	TokenCount int64               `json:"token_count"`
	CostUSD    float64             `json:"cost_usd"`
	TurnCount  int                 `json:"turn_count"`
	Metadata   map[string]string   `json:"metadata,omitempty"`
	Status     string              `json:"status"`
	Messages   []providers.Message `json:"messages,omitempty"`

	// Children lists the session IDs that branched FROM this one
	// (Phase 1.5 #3 — tree-structured sessions). Maintained by Branch().
	// Empty for leaf sessions; never nil after first branch.
	Children []string `json:"children,omitempty"`

	// BranchedAtTurn records which turn-index in the parent's history
	// was the branch point. Only meaningful when ParentID is set.
	// Zero is a valid value (branched from the very start) so we
	// distinguish "branched at turn 0" from "not a branch" by the
	// ParentID field, not by this value.
	BranchedAtTurn int `json:"branched_at_turn,omitempty"`
	// contains filtered or unexported fields
}

func NewSession

func NewSession(folder string) *Session

func (*Session) AutoTitle

func (s *Session) AutoTitle(firstMsg string)

AutoTitle sets Title from the first user message, truncated to 72 characters. Only sets when Title is empty — no overwrites.

func (*Session) IsSubSession

func (s *Session) IsSubSession() bool

type StashEntry

type StashEntry struct {
	ID      string    `json:"id"`
	Text    string    `json:"text"`
	SavedAt time.Time `json:"saved_at"`
}

StashEntry is one saved prompt.

type StashStore

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

StashStore is a file-backed list of stash entries.

func NewStashStore

func NewStashStore(path string) (*StashStore, error)

NewStashStore opens or creates a stash file at path. The directory is created if missing.

func (*StashStore) Delete

func (s *StashStore) Delete(id string) error

Delete removes the entry with the given id. Missing ids return nil.

func (*StashStore) Get

func (s *StashStore) Get(id string) (string, error)

Get returns the text of the entry with the given id.

func (*StashStore) List

func (s *StashStore) List() ([]StashEntry, error)

List returns all stash entries, newest first.

func (*StashStore) Save

func (s *StashStore) Save(text string) (string, error)

Save persists a new entry and returns its id.

type Store

type Store interface {
	Create(ctx context.Context, session *Session) error
	Load(ctx context.Context, id string) (*Session, error)
	Save(ctx context.Context, session *Session) error
	Delete(ctx context.Context, id string) error
	List(ctx context.Context, opts ListOptions) ([]*Session, error)
	Close() error
}

Jump to

Keyboard shortcuts

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