memory

package
v0.1.2 Latest Latest
Warning

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

Go to latest
Published: Aug 23, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// PlanModeOff means no plan turn is active or pending.
	PlanModeOff = ""
	// PlanModePlanning means a read-only plan turn was requested and is
	// running (or was interrupted before completing).
	PlanModePlanning = "planning"
	// PlanModePendingApproval means a plan turn completed and the presented
	// plan awaits the user's approve/reject decision.
	PlanModePendingApproval = "pending_approval"
	// PlanModeApproved means the user approved the pending plan; the next
	// turn injects the execution directive exactly once, then clears it.
	PlanModeApproved = "approved"
)

Plan-mode lifecycle states for Thread.PlanMode.

Variables

View Source
var (
	// ErrPlanExists is returned when CreatePlan is called for a thread that
	// already owns a plan.
	ErrPlanExists = errors.New("plan already exists")
	// ErrTodoNotFound is wrapped when a todo mutation names an unknown ID.
	ErrTodoNotFound = errors.New("todo not found")
)

Functions

func ValidateTodoStatus

func ValidateTodoStatus(status TodoStatus) error

ValidateTodoStatus returns a descriptive error for an unsupported state.

Types

type AssistantToolCall

type AssistantToolCall struct {
	ID        string
	ToolName  string
	Arguments string
}

AssistantToolCall is one provider tool call to persist with an assistant message. Slice order becomes the stored tool-call sequence.

type CompressionRecord

type CompressionRecord struct {
	ID                      int64
	ThreadID                string
	Summary                 string
	FirstKeptSeq            int
	CompressedMessageCount  int
	PrunedToolOutputs       int
	BeforeTokens            int
	AfterTokens             int
	BudgetTokens            int
	SummaryModelAlias       string
	SummaryModelID          string
	SummaryPromptTokens     int
	SummaryCompletionTokens int
	SummaryTotalTokens      int
	FallbackUsed            bool
	FallbackReason          string
	CreatedAt               time.Time
}

CompressionRecord represents one persisted compression event for a thread.

type Message

type Message struct {
	ID         int       `json:"id"`
	ThreadID   string    `json:"thread_id"`
	Role       string    `json:"role"`
	Content    *string   `json:"content"`
	ToolCallID *string   `json:"tool_call_id"`
	CreatedAt  time.Time `json:"created_at"`
	Seq        int       `json:"seq"`
}

Message represents a single message in a thread.

type Plan

type Plan struct {
	ThreadID  string     `json:"thread_id"`
	Revision  int64      `json:"revision"`
	Items     []TodoItem `json:"items"`
	CreatedAt time.Time  `json:"created_at"`
	UpdatedAt time.Time  `json:"updated_at"`
}

Plan is the current durable task plan for a conversation thread. Revision is incremented once for each successful batch mutation, making snapshots safe to reconcile in a CLI or web client.

type SearchResult

type SearchResult struct {
	MessageID   int       `json:"message_id"`
	ThreadID    string    `json:"thread_id"`
	ThreadTitle *string   `json:"thread_title"`
	Role        string    `json:"role"`
	Content     string    `json:"content"`
	Snippet     string    `json:"snippet"`
	CreatedAt   time.Time `json:"created_at"`
}

SearchResult represents a single message match from FTS5 search.

type Store

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

Store wraps the SQLite database.

func Open

func Open(path string) (*Store, error)

Open opens or creates the SQLite database at path and applies the migrations embedded in the binary. This works regardless of the working directory or where the config file lives.

func OpenWithMigrations

func OpenWithMigrations(path, migrationsDir string) (*Store, error)

OpenWithMigrations opens the database and applies migrations from a directory on disk. Kept for tests that point at the repo's migrations/ folder.

func OpenWithMigrationsFS

func OpenWithMigrationsFS(path string, fsys fs.FS) (*Store, error)

OpenWithMigrationsFS opens the database and applies migrations read from fsys.

func (*Store) AppendAssistantMessageWithToolCalls

func (s *Store) AppendAssistantMessageWithToolCalls(threadID string, content *string, toolCalls []AssistantToolCall) (*Message, error)

AppendAssistantMessageWithToolCalls atomically persists an assistant message and every tool call it contains. If any tool-call insert fails (including a globally duplicate call ID), the assistant message and all earlier calls in the same group are rolled back together.

func (*Store) AppendMessage

func (s *Store) AppendMessage(threadID, role string, content *string, toolCallID *string) (*Message, error)

AppendMessage adds a message to a thread and updates the thread timestamp.

Uses BEGIN IMMEDIATE to acquire the write lock before reading, preventing the deferred-transaction lock-upgrade issue that causes SQLITE_BUSY. The UNIQUE(thread_id, seq) constraint (migration 0003) is the hard guarantee against duplicate seq values.

func (*Store) AppendToolCall

func (s *Store) AppendToolCall(messageID int, callID, toolName, arguments string, seq int) error

AppendToolCall records a tool call for an assistant message.

func (*Store) Close

func (s *Store) Close() error

Close closes the database connection.

func (*Store) CreatePlan

func (s *Store) CreatePlan(threadID string, items []TodoItem) (plan *Plan, err error)

CreatePlan creates the sole plan belonging to threadID. Empty plans are valid. Items with an empty ID receive deterministic IDs starting at "1"; supplied IDs are useful when importing a snapshot.

func (*Store) CreateThread

func (s *Store) CreateThread(title, model *string) (*Thread, error)

CreateThread creates a new thread with optional title and model.

func (*Store) CreateThreadWithWorkspace

func (s *Store) CreateThreadWithWorkspace(title, model *string, workspace string) (*Thread, error)

CreateThreadWithWorkspace creates a new thread and records the workspace directory its tools run in ("" for unknown/legacy).

func (*Store) CreateTodos

func (s *Store) CreateTodos(threadID string, items []TodoItem) (updated []TodoItem, err error)

CreateTodos appends items to a thread's plan, creating the plan on demand. Input IDs and positions must be empty: IDs are allocated atomically and positions are derived from the existing list. Empty status defaults to pending. The returned slice is the complete updated list.

func (*Store) DB

func (s *Store) DB() *sql.DB

DB exposes the underlying sql.DB for tests.

func (*Store) DeleteMessagesAfter

func (s *Store) DeleteMessagesAfter(threadID string, seq int) error

DeleteMessagesAfter removes all messages in a thread from seq onward (inclusive). It also invalidates any compression records whose first_kept_seq >= seq, because undo can remove the messages that the compression boundary references.

func (*Store) DeletePlan

func (s *Store) DeletePlan(threadID string) error

DeletePlan deletes a thread's plan and all its todos. It is idempotent.

func (*Store) DeleteThread

func (s *Store) DeleteThread(id string) error

DeleteThread removes a thread and cascades messages + tool_calls.

func (*Store) DeleteTodos

func (s *Store) DeleteTodos(threadID string, ids []string) (updated []TodoItem, err error)

DeleteTodos atomically removes the named items, compacts positions, and returns the remaining ordered list. All IDs must exist.

func (*Store) ForkThread

func (s *Store) ForkThread(srcID string) (newID string, err error)

ForkThread creates a new thread that copies the source thread's title, model, messages, assistant tool calls, and current persisted plan. Tool-call IDs are globally unique, so the fork receives fresh IDs and all copied tool-result messages are rewritten to reference them. The entire copy is one transaction: a failed fork never leaves behind a partial thread.

func (*Store) GetLatestCompression

func (s *Store) GetLatestCompression(threadID string) (*CompressionRecord, error)

GetLatestCompression returns the most recent compression record for a thread, or nil if none exists.

func (*Store) GetPlan

func (s *Store) GetPlan(threadID string) (*Plan, error)

GetPlan returns the current plan and its ordered items. A thread without a plan returns (nil, nil).

func (*Store) GetThread

func (s *Store) GetThread(id string) (*Thread, error)

GetThread returns a thread by ID.

func (*Store) GetThreadWithMessages

func (s *Store) GetThreadWithMessages(id string) (*Thread, []Message, error)

GetThreadWithMessages returns a thread with its ordered messages.

func (*Store) GetThreadWithMessagesFromSeq

func (s *Store) GetThreadWithMessagesFromSeq(threadID string, firstSeq int) (*Thread, []Message, error)

GetThreadWithMessagesFromSeq returns a thread with messages starting from firstSeq (inclusive), ordered by seq ASC. This is used to rebuild the message list after a compression record with first_kept_seq.

func (*Store) GetToolCallsForMessage

func (s *Store) GetToolCallsForMessage(messageID int) ([]ToolCall, error)

GetToolCallsForMessage returns all tool calls for a given message, ordered by seq.

func (*Store) InvalidateCompressionsAfterSeq

func (s *Store) InvalidateCompressionsAfterSeq(threadID string, seq int) error

InvalidateCompressionsAfterSeq deletes compression records whose first_kept_seq is >= the given seq. This is called by DeleteMessagesAfter because undo can remove messages at or after the kept boundary, making those compression records point to nonexistent messages.

func (*Store) ListThreads

func (s *Store) ListThreads() ([]Thread, error)

ListThreads returns all threads ordered by most recent update.

func (*Store) ListThreadsByWorkspace

func (s *Store) ListThreadsByWorkspace(workspace string, includeLegacy bool) ([]Thread, error)

ListThreadsByWorkspace returns threads whose recorded workspace matches, ordered by most recent update. When includeLegacy is true, threads with an unknown workspace ("") are included too — they predate workspace tracking and can't be attributed, so they surface in the default view only.

func (*Store) ListTodos

func (s *Store) ListTodos(threadID string) ([]TodoItem, error)

ListTodos returns a thread's todo items in stable plan order. A thread with no plan has an empty list.

func (*Store) ReplaceTodos

func (s *Store) ReplaceTodos(threadID string, items []TodoItem) (updated []TodoItem, err error)

ReplaceTodos atomically replaces a plan's complete item list. Slice order is authoritative. Existing IDs may be retained; blank IDs receive fresh, monotonic IDs. An empty slice clears the list but keeps the plan.

func (*Store) SaveCompression

func (s *Store) SaveCompression(rec CompressionRecord) error

SaveCompression persists a compression record. It sets CreatedAt to now. The caller should set FirstKeptSeq to the Seq of the first retained non-synthetic thread message after the compressed span.

func (*Store) SearchMessages

func (s *Store) SearchMessages(query string, limit int) ([]SearchResult, error)

SearchMessages performs a full-text search across all messages. Uses SQLite FTS5 with the bm25 ranking function.

NOTE: Compression summaries are intentionally excluded from FTS search. They live in the `compressions` table, not `messages`, and are not indexed into `messages_fts`. This follows the robustness spec recommendation: do not index synthetic compression summaries into user-facing search unless clearly labeled as summaries.

func (*Store) SetThreadPlanMode

func (s *Store) SetThreadPlanMode(id, mode string) error

SetThreadPlanMode sets the thread's plan-approval lifecycle state (one of the PlanMode* constants). It deliberately does not touch updated_at: a lifecycle transition is not conversation activity and must not reorder the session list.

func (*Store) SetThreadTitle

func (s *Store) SetThreadTitle(id, title string) error

SetThreadTitle unconditionally overwrites the thread title (for manual rename or regeneration).

func (*Store) SubagentTranscript

func (s *Store) SubagentTranscript(taskID string) (string, error)

SubagentTranscript renders the persisted transcript of one subagent task (migration 0004's subagent_tasks row) for agent:// reads: goal header, role sections, tool calls and results, in stored order. It resolves an unknown id as a not-found error so file_read can surface it.

func (*Store) UpdateThreadTitle

func (s *Store) UpdateThreadTitle(id, title string) error

UpdateThreadTitle sets the thread title unless it was already set (conditional for auto-title).

func (*Store) UpdateTodos

func (s *Store) UpdateTodos(threadID string, updates []TodoUpdate) (updated []TodoItem, err error)

UpdateTodos atomically applies a batch of patches and returns the complete ordered list. Every ID is checked before the first write, so an unknown ID or invalid patch leaves the plan unchanged.

type Thread

type Thread struct {
	ID        string    `json:"id"`
	Title     *string   `json:"title"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
	Model     *string   `json:"model"`
	// Workspace is the directory the thread's tools run in. Empty means
	// unknown — threads created before migration 0005 have no record.
	Workspace string `json:"workspace"`
	// PlanMode is the thread's plan-approval lifecycle state (migration 0007):
	// one of the PlanMode* constants. Empty means plan mode is off.
	PlanMode string `json:"plan_mode"`
}

Thread represents a conversation thread.

type TodoItem

type TodoItem struct {
	ID        string     `json:"id"`
	ThreadID  string     `json:"thread_id"`
	Content   string     `json:"content"`
	Status    TodoStatus `json:"status"`
	Position  int        `json:"position"`
	CreatedAt time.Time  `json:"created_at"`
	UpdatedAt time.Time  `json:"updated_at"`
}

TodoItem is one ordered step in a thread's plan. IDs are monotonic decimal strings generated per plan. Position is one-based and follows slice order.

type TodoStatus

type TodoStatus string

TodoStatus is the durable lifecycle state of one plan item.

const (
	TodoPending    TodoStatus = "pending"
	TodoInProgress TodoStatus = "in_progress"
	TodoCompleted  TodoStatus = "completed"
	TodoCancelled  TodoStatus = "cancelled"
)

func (TodoStatus) Valid

func (status TodoStatus) Valid() bool

Valid reports whether status is one of the persisted todo states.

type TodoUpdate

type TodoUpdate struct {
	ID      string      `json:"id"`
	Content *string     `json:"content,omitempty"`
	Status  *TodoStatus `json:"status,omitempty"`
}

TodoUpdate changes one existing item. A nil field is left unchanged.

type ToolCall

type ToolCall struct {
	ID        string `json:"id"`
	MessageID int    `json:"message_id"`
	ToolName  string `json:"tool_name"`
	Arguments string `json:"arguments"`
	Seq       int    `json:"seq"`
}

ToolCall represents an assistant tool invocation.

Jump to

Keyboard shortcuts

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