Documentation
¶
Overview ¶
Package session implements the SQLite-backed conversation store.
Schema and design rationale live in docs/design.md §9. Key choices:
- pure-Go sqlite (modernc.org/sqlite) so cross-compile stays CGO-free
- one global DB at ~/.deepseek/sessions.db (not per-project)
- branching by reference: child sessions store parent_id + branch_point and replay the parent's messages up to that index instead of copying them
Migrations are applied idempotently at Open time. The current schema is v1; future bumps live in migrations.go (not yet written).
Package session implements the SQLite-backed conversation store.
Index ¶
- func Fingerprint(projectPath string) (string, error)
- type Message
- type Persister
- func (p *Persister) AppendAssistant(ctx context.Context, blocks []llm.ContentBlock, model string, usage llm.Usage) (int, error)
- func (p *Persister) AppendReceipt(ctx context.Context, kind ReceiptKind, payload json.RawMessage) (int64, error)
- func (p *Persister) AppendToolResult(ctx context.Context, toolUseID string, content string, isError bool) (int, error)
- func (p *Persister) AppendUserMessage(ctx context.Context, blocks []llm.ContentBlock) (int, error)
- func (p *Persister) PersistedMessageCount(ctx context.Context) (int, error)
- func (p *Persister) ReplaceWithCompaction(ctx context.Context, fromIdx, toIdx int, summary string) (int, error)
- func (p *Persister) SessionID() string
- func (p *Persister) SetActiveModel(ctx context.Context, model string) error
- func (p *Persister) TakeSnapshot(stepIdx int, paths []string) (int, error)
- func (p *Persister) TruncateMessages(ctx context.Context, keepCount int) (int, error)
- func (p *Persister) Undo(n int) (int, error)
- func (p *Persister) UsageSummary(ctx context.Context) (UsageSummary, error)
- type ReceiptKind
- type Session
- type Store
- func (s *Store) AppendMessage(ctx context.Context, sessionID string, m Message) (int, error)
- func (s *Store) AppendReceipt(ctx context.Context, sessionID string, kind ReceiptKind, ...) (int64, error)
- func (s *Store) Close() error
- func (s *Store) CountMessages(ctx context.Context, sessionID string) (int, error)
- func (s *Store) GetSession(ctx context.Context, id string) (Session, error)
- func (s *Store) LatestInProject(ctx context.Context, projectPath string) (Session, error)
- func (s *Store) ListAll(ctx context.Context) ([]Session, error)
- func (s *Store) ListByProject(ctx context.Context, projectPath string) ([]Session, error)
- func (s *Store) ListReceipts(ctx context.Context, sessionID string) ([]TranscriptReceipt, error)
- func (s *Store) LoadMessages(ctx context.Context, sessionID string) ([]Message, error)
- func (s *Store) MostRecentInProject(ctx context.Context, projectPath string) (Session, error)
- func (s *Store) NewBranch(ctx context.Context, parentID string, branchPoint int) (Session, error)
- func (s *Store) NewSession(ctx context.Context, projectPath, model string, duetEnabled bool) (Session, error)
- func (s *Store) Path() string
- func (s *Store) PruneOlderThan(ctx context.Context, older time.Duration) (int64, error)
- func (s *Store) ReplaceWithCompaction(ctx context.Context, sessionID string, fromIdx, toIdx int, summary string) (int, error)
- func (s *Store) Replay(ctx context.Context, sessionID string) ([]Message, error)
- func (s *Store) TouchLastUsed(ctx context.Context, id string) error
- func (s *Store) TruncateMessages(ctx context.Context, sessionID string, keepCount int) (int, error)
- func (s *Store) UpdateModel(ctx context.Context, id, model string) error
- func (s *Store) UpdateSummary(ctx context.Context, id, summary string) error
- func (s *Store) UsageSummary(ctx context.Context, sessionID string) (UsageSummary, error)
- type TranscriptReceipt
- type UsageSummary
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Fingerprint ¶
Fingerprint returns a stable FNV-1a hash of the canonical project path. Symlinks in the path are resolved so two symlinked paths produce the same fingerprint.
Types ¶
type Message ¶
type Message struct {
Idx int
Role string
Blocks []llm.ContentBlock
Content string
ReasoningContent string
ToolCalls []llm.ToolCall
ToolCallID string
Model string
Usage llm.Usage
CostYuan float64
Timestamp time.Time
}
Message is a row in the messages table.
Blocks is the v2 canonical content; when non-nil the Content/ReasoningContent/ToolCalls fields are not read. Until T-019 removes them, both representations may coexist on a single in-memory row so legacy paths keep working during migration.
type Persister ¶
type Persister struct {
// contains filtered or unexported fields
}
Persister is the concrete agent.Persister wrapping a Store + a snapshots.Manager. Live state for one session.
Construct with NewPersister. Reuse for the lifetime of the session; it's safe to call from the agent's goroutine but not from multiple goroutines concurrently (database/sql handles its own pooling, but the ID/step counters live in this struct).
func NewPersister ¶
NewPersister returns a Persister wrapping store + snaps for sessionID. snaps may be nil to disable snapshotting.
func (*Persister) AppendAssistant ¶
func (p *Persister) AppendAssistant(ctx context.Context, blocks []llm.ContentBlock, model string, usage llm.Usage) (int, error)
AppendAssistant records an assistant turn from a typed block slice (Thinking → Text → ToolUse in emission order).
func (*Persister) AppendReceipt ¶
func (p *Persister) AppendReceipt(ctx context.Context, kind ReceiptKind, payload json.RawMessage) (int64, error)
AppendReceipt adds a transcript receipt for the persister's session.
func (*Persister) AppendToolResult ¶
func (p *Persister) AppendToolResult(ctx context.Context, toolUseID string, content string, isError bool) (int, error)
AppendToolResult records the result of one tool_use as a single ToolResultBlock-bearing tool message.
func (*Persister) AppendUserMessage ¶
AppendUserMessage records a user turn from a typed block slice.
func (*Persister) PersistedMessageCount ¶
PersistedMessageCount returns the persisted body-message count, letting /undo verify in-memory↔disk index alignment before truncating disk (T3.5).
func (*Persister) ReplaceWithCompaction ¶
func (p *Persister) ReplaceWithCompaction(ctx context.Context, fromIdx, toIdx int, summary string) (int, error)
ReplaceWithCompaction forwards to Store.ReplaceWithCompaction scoped to this persister's session.
func (*Persister) SetActiveModel ¶
SetActiveModel persists a /models switch.
func (*Persister) TakeSnapshot ¶
TakeSnapshot snapshots the given paths before a mutating tool runs.
func (*Persister) TruncateMessages ¶
TruncateMessages drops persisted messages with idx >= keepCount, satisfying agent.MessageTruncator so /undo can make disk match the rewound in-memory transcript (T3.5).
func (*Persister) Undo ¶
Undo restores the last n step snapshots and returns the number of files restored. Returns 0 if no snapshots manager is configured.
func (*Persister) UsageSummary ¶ added in v0.3.4
func (p *Persister) UsageSummary(ctx context.Context) (UsageSummary, error)
UsageSummary is a convenience wrapper that calls the store method scoped to this persister's session.
type ReceiptKind ¶
type ReceiptKind string
ReceiptKind identifies the type of transcript receipt.
const ( ReceiptModelFinal ReceiptKind = "model_final" // model usage and prefix hash ReceiptRepair ReceiptKind = "repair" // repair report from tool-call pipeline ReceiptPermission ReceiptKind = "permission" // permission decision ReceiptCompaction ReceiptKind = "compaction" // compaction event ReceiptEpoch ReceiptKind = "epoch" // epoch lifecycle event )
type Session ¶
type Session struct {
ID string
ParentID string // empty if root
BranchPoint int // valid only when ParentID != ""
ProjectPath string
WorkspaceFP string // FNV-1a fingerprint of the canonical project path
Model string
DuetEnabled bool
CreatedAt time.Time
LastUsedAt time.Time
Summary string
CompactionCount int
CompactionSummary string
}
Session is a row in the sessions table.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store is the SQLite-backed session store. Safe for concurrent use (database/sql handles pooling). Open once per process.
func Open ¶
Open opens (and migrates) the store at path. The parent directory is created if missing. If path is empty, ~/.deepseek/sessions.db is used.
func (*Store) AppendMessage ¶
AppendMessage inserts a message at the next available index. The caller is expected to construct a Message in send-order; idx is auto-assigned (length of current messages).
func (*Store) AppendReceipt ¶
func (s *Store) AppendReceipt(ctx context.Context, sessionID string, kind ReceiptKind, payload json.RawMessage) (int64, error)
AppendReceipt adds a transcript receipt to the session. The seq is auto-assigned as max(seq)+1 for the session. The payload must be valid JSON.
func (*Store) CountMessages ¶
ReplaceWithCompaction atomically collapses messages in [fromIdx, toIdx) into a single synthetic assistant message containing the summary, then renumbers later messages so idx stays contiguous. The role is assistant (not system) so a session compacted under T4.3+ replays the same body-message shape the live agent uses — one system message at the head, the summary in the body. (Sessions compacted under older code persisted a role='system' summary row; LoadMessages replays that verbatim, so a legacy summary still re-enters as a mid-body system message. That is tolerated by DeepSeek; it is simply the pre-T4.3 shape, not the new guarantee.)
Snapshots in this project are filesystem-backed (no SQL table to UPDATE), so step_idx shifting for snapshots lives in snapshots.Manager rather than here — see T-217.
Returns the idx of the inserted summary row (== fromIdx). A no-op call (fromIdx == toIdx) returns fromIdx with nil error. toIdx larger than the current message count is clamped to count+1. CountMessages returns the number of persisted messages for the session. /undo reconcile (T3.5) uses it to verify the in-memory transcript is index-aligned with disk before truncating disk.
func (*Store) GetSession ¶
GetSession reads one session by ID.
func (*Store) LatestInProject ¶
LatestInProject returns the most recently used session whose workspace fingerprint matches projectPath. Unlike MostRecentInProject, this uses the canonical fingerprint so symlinked or renamed paths match.
func (*Store) ListByProject ¶
ListByProject returns all sessions for a project, newest first.
func (*Store) ListReceipts ¶
ListReceipts returns all receipts for a session in sequence order. Returns an empty slice (not nil) for sessions with no receipts.
func (*Store) LoadMessages ¶
LoadMessages returns the message list for a session in idx order. Does NOT replay parents — use Replay for that.
Blocks resolution: if the v2 blocks column is non-empty, UnmarshalBlocks restores the typed slice. Otherwise the row is pre-v2 and rebuildBlocksFromLegacy synthesizes blocks from content/reasoning/tool_calls so callers see a uniform Blocks view regardless of when the row was written.
func (*Store) MostRecentInProject ¶
MostRecentInProject returns the most recently used session for a given project path, or sql.ErrNoRows if none.
func (*Store) NewBranch ¶
NewBranch creates a child session referencing (parentID, branchPoint). Messages are NOT copied — Replay() walks parents at read time.
func (*Store) NewSession ¶
func (s *Store) NewSession(ctx context.Context, projectPath, model string, duetEnabled bool) (Session, error)
NewSession inserts a new (root) session and returns its row. Use NewBranch for child sessions.
func (*Store) PruneOlderThan ¶
PruneOlderThan deletes sessions older than the given duration. The cascade also removes their messages.
func (*Store) ReplaceWithCompaction ¶
func (*Store) Replay ¶
Replay returns the full effective message history for a session, walking parents and concatenating up to each branch_point.
Concretely, for a chain:
root (messages 0..N)
└── child A (branch_point=5, own messages → effective: root[0..4] + A[0..M])
└── child B (branch_point=3, own → effective: root[0..4] + A[0..2] + B[...])
We never copy messages, so storage stays O(unique) even for deeply branched experiment trees.
func (*Store) TouchLastUsed ¶
TouchLastUsed updates last_used_at to now. Call on every meaningful interaction so the auto-resume heuristic stays accurate.
func (*Store) TruncateMessages ¶
TruncateMessages deletes all messages with idx >= keepCount for the session. The deleted tail is contiguous from keepCount, so no renumbering is needed. Used by /undo reconcile (T3.5) to make disk match the rewound in-memory transcript. keepCount <= 0 clears all messages; returns the rows removed.
func (*Store) UpdateModel ¶
UpdateModel persists a /models switch.
func (*Store) UpdateSummary ¶
UpdateSummary persists an autogenerated short title.
func (*Store) UsageSummary ¶ added in v0.3.4
UsageSummary returns the aggregated usage from persisted assistant messages for the given session. Only rows with at least one usage token column > 0 are counted as turns.
type TranscriptReceipt ¶
type TranscriptReceipt struct {
SessionID string
Seq int64
Kind ReceiptKind
Payload json.RawMessage
}
TranscriptReceipt is an append-only entry in the transcript log.