Documentation
¶
Index ¶
- Variables
- type HandoffParams
- type NextAction
- type NextActionStatus
- type Store
- func (s *Store) HandoffsByRepo(ctx context.Context, repoName string, limit int) ([]db.SessionHandoff, error)
- func (s *Store) HandoffsSince(ctx context.Context, since time.Time, limit int) ([]db.SessionHandoff, error)
- func (s *Store) LatestHandoff(ctx context.Context) (*db.SessionHandoff, error)
- func (s *Store) MarkNextActionDone(ctx context.Context, handoffID uuid.UUID, step int) (*db.SessionHandoff, error)
- func (s *Store) PruneOlderThan(ctx context.Context, cutoff time.Time) (int64, error)
- func (s *Store) Resolve(ctx context.Context, id uuid.UUID) error
- func (s *Store) SearchByCosine(ctx context.Context, queryEmbedding []float32, limit int) ([]db.SessionHandoff, error)
- func (s *Store) SetHandoff(ctx context.Context, p HandoffParams) (*db.SessionHandoff, error)
- func (s *Store) UpdateEmbedding(ctx context.Context, embedding []byte) error
- func (s *Store) UpdateEmbeddingByID(ctx context.Context, id uuid.UUID, embedding []byte, providerTag string, ...) error
- func (s *Store) UpdateSummary(ctx context.Context, summary string) error
- func (s *Store) WithTx(tx pgx.Tx) *Store
- type StoreIface
Constants ¶
This section is empty.
Variables ¶
var ErrNotFound = errors.New("session: not found")
ErrNotFound is returned when no session handoff exists.
Functions ¶
This section is empty.
Types ¶
type HandoffParams ¶
type HandoffParams struct {
ProjectID *uuid.UUID
RepoName string
Intent string
ContextSummary string
NextActions []NextAction // optional; defaults to empty array
}
HandoffParams holds parameters for recording a session handoff.
type NextAction ¶
type NextAction struct {
Step int `json:"step"`
Title string `json:"title"`
Command string `json:"command,omitempty"`
Expected string `json:"expected,omitempty"`
Status NextActionStatus `json:"status"`
RefTaskID *string `json:"ref_task_id,omitempty"` // UUID string or null
}
NextAction represents a single actionable step in a session handoff. Stored as a JSONB array in Postgres and a JSON TEXT array in SQLite.
type NextActionStatus ¶
type NextActionStatus string
NextActionStatus represents the completion state of a next_action step.
const ( NextActionPending NextActionStatus = "pending" NextActionDone NextActionStatus = "done" NextActionSkipped NextActionStatus = "skipped" )
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store handles all database operations for the Session bounded context.
func NewStore ¶
NewStore returns a Store backed by the given DBTX scoped to the optional workspace. nil workspaceID = legacy unscoped mode.
func (*Store) HandoffsByRepo ¶
func (s *Store) HandoffsByRepo(ctx context.Context, repoName string, limit int) ([]db.SessionHandoff, error)
HandoffsByRepo returns recent handoffs whose repo_name matches repoName, scoped to the configured workspace, newest first. Used by the workspace repo overview to surface recent context for a single repo. Hand-written query so the sqlc surface stays untouched.
func (*Store) HandoffsSince ¶
func (s *Store) HandoffsSince(ctx context.Context, since time.Time, limit int) ([]db.SessionHandoff, error)
HandoffsSince returns handoffs created or resolved on or after since, scoped to the configured workspace. Used by the timeline aggregator.
func (*Store) LatestHandoff ¶
LatestHandoff returns the most recent unresolved handoff, or ErrNotFound.
func (*Store) MarkNextActionDone ¶
func (s *Store) MarkNextActionDone(ctx context.Context, handoffID uuid.UUID, step int) (*db.SessionHandoff, error)
MarkNextActionDone updates next_actions[step].status = "done" for the handoff identified by id, scoped to the caller's workspace_id. Returns ErrNotFound when no matching handoff exists in the workspace.
SECURITY: workspace isolation enforced — the handoff_id is validated against the configured workspace_id before any mutation.
func (*Store) PruneOlderThan ¶
PruneOlderThan hard-deletes session_handoffs rows where resolved_at IS NOT NULL and resolved_at < cutoff. Open (unresolved) handoffs are NEVER deleted regardless of age. Global cleanup (no workspace filter) — cutoff is computed server-side by the caller (scheduler) and passed as a parameterised argument.
func (*Store) Resolve ¶
Resolve marks a handoff as resolved so it will not appear in future queries.
func (*Store) SearchByCosine ¶
func (s *Store) SearchByCosine(ctx context.Context, queryEmbedding []float32, limit int) ([]db.SessionHandoff, error)
SearchByCosine returns the top-limit session handoffs whose embeddings are most similar to queryEmbedding, filtered by workspace_id. Only handoffs with non-null embeddings whose embedding_provider matches the query vector's provider are considered — this prevents cross-provider dimension mismatches (CosineSimilarity returns 0 when dimensions differ). Similarity is computed on the Go side (brute-force scan) because session_handoffs.embedding is BYTEA and not yet a pgvector column.
SECURITY: filtered by workspace_id — no cross-workspace data is returned.
func (*Store) SetHandoff ¶
func (s *Store) SetHandoff(ctx context.Context, p HandoffParams) (*db.SessionHandoff, error)
SetHandoff records a new session handoff for the next session to pick up. Returns a descriptive error wrapping sanitize.ErrTagNoise if any text field contains tool-call serialization fragments (XML tags leaked from the MCP harness), which are never valid user input.
func (*Store) UpdateEmbedding ¶
UpdateEmbedding writes the serialized embedding bytes to the most recent unresolved session handoff. Best-effort: 0 rows updated (no unresolved handoff) is not an error.
func (*Store) UpdateEmbeddingByID ¶
func (s *Store) UpdateEmbeddingByID(ctx context.Context, id uuid.UUID, embedding []byte, providerTag string, dim int) error
UpdateEmbeddingByID writes the serialized embedding bytes plus provider metadata to the session handoff with the given ID, scoped to workspace_id to prevent cross-workspace overwrites. Best-effort: 0 rows updated is not an error.
func (*Store) UpdateSummary ¶
UpdateSummary writes summary to the most recent unresolved handoff's summary_text column. It is a best-effort operation: if no unresolved handoff exists the update affects 0 rows and returns nil (not ErrNotFound), so the Stop hook is never blocked.
type StoreIface ¶
type StoreIface interface {
SetHandoff(ctx context.Context, p HandoffParams) (*db.SessionHandoff, error)
LatestHandoff(ctx context.Context) (*db.SessionHandoff, error)
Resolve(ctx context.Context, id uuid.UUID) error
// MarkNextActionDone sets next_actions[step].status = "done" for the
// handoff identified by id, scoped to the caller's workspace.
// Returns ErrNotFound when no matching handoff exists in the workspace.
MarkNextActionDone(ctx context.Context, handoffID uuid.UUID, step int) (*db.SessionHandoff, error)
// UpdateSummary writes a plain-text session summary to the most recent
// unresolved handoff's summary_text column. Used by the Stop hook after
// SummarizeSession produces a ≤500-char digest. Silently no-ops when no
// unresolved handoff exists (first-ever session, or already resolved).
UpdateSummary(ctx context.Context, summary string) error
// UpdateEmbedding writes serialized embedding bytes to the most recent
// unresolved handoff for cosine similarity recall at SessionStart.
UpdateEmbedding(ctx context.Context, embedding []byte) error
// UpdateEmbeddingByID writes serialized embedding bytes plus provider metadata
// to the handoff with the given ID. Preferred over UpdateEmbedding when the
// caller holds the specific row ID (avoids race under concurrent SetHandoff calls).
// providerTag is "gemini", "hashed", or "unknown"; dim is len(embedding)/4.
UpdateEmbeddingByID(ctx context.Context, id uuid.UUID, embedding []byte, providerTag string, dim int) error
// SearchByCosine returns the top-limit handoffs most similar to queryEmbedding.
// SECURITY: scoped to workspace_id.
SearchByCosine(ctx context.Context, queryEmbedding []float32, limit int) ([]db.SessionHandoff, error)
// HandoffsSince returns handoffs created or resolved on or after since,
// scoped to the configured workspace. Used by the timeline aggregator.
HandoffsSince(ctx context.Context, since time.Time, limit int) ([]db.SessionHandoff, error)
// HandoffsByRepo returns recent handoffs whose repo_name matches the given
// value, scoped to the configured workspace. Ordered by created_at DESC.
// Used by the workspace repo overview to surface recent context for a repo.
HandoffsByRepo(ctx context.Context, repoName string, limit int) ([]db.SessionHandoff, error)
// PruneOlderThan hard-deletes session_handoffs rows where resolved_at IS
// NOT NULL and resolved_at < cutoff. Open (unresolved) handoffs are NEVER
// deleted regardless of age — they represent a session still in progress.
// Global cleanup (no workspace filter). Called daily by the scheduler to
// enforce the 365-day TTL per backend-security-design.md §1.3.
PruneOlderThan(ctx context.Context, cutoff time.Time) (int64, error)
}
StoreIface is the backend-agnostic contract for the Session bounded context.