session

package
v1.0.1 Latest Latest
Warning

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

Go to latest
Published: Aug 21, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
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

func NewStore(dbtx db.DBTX, workspaceID *uuid.UUID) *Store

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

func (s *Store) LatestHandoff(ctx context.Context) (*db.SessionHandoff, error)

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

func (s *Store) PruneOlderThan(ctx context.Context, cutoff time.Time) (int64, 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. Global cleanup (no workspace filter) — cutoff is computed server-side by the caller (scheduler) and passed as a parameterised argument.

func (*Store) Resolve

func (s *Store) Resolve(ctx context.Context, id uuid.UUID) error

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

func (s *Store) UpdateEmbedding(ctx context.Context, embedding []byte) error

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

func (s *Store) UpdateSummary(ctx context.Context, summary string) error

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.

func (*Store) WithTx

func (s *Store) WithTx(tx pgx.Tx) *Store

WithTx returns a Store bound to tx, preserving the workspace scope.

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.

Jump to

Keyboard shortcuts

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