Documentation
¶
Overview ¶
Package notes is a per-agent, cross-session key/value store the model writes via the note_* tools. Distinct from the chat-message store: notes are durable facts the user expects the agent to remember between sessions, not conversation history. Storage shares memory.db with the message store but lives in its own `notes` table.
Index ¶
- Variables
- func RenderInContextBlock(pinned []Note) string
- func RenderPromptSection(notes []Note) string
- type Note
- type Store
- func (s *Store) Count(ctx context.Context) (int, error)
- func (s *Store) Delete(ctx context.Context, key string) error
- func (s *Store) Get(ctx context.Context, key string) (Note, error)
- func (s *Store) List(ctx context.Context) ([]Note, error)
- func (s *Store) ListInContext(ctx context.Context) ([]Note, error)
- func (s *Store) Save(ctx context.Context, key, content string, maxBytes, maxCount int) error
- func (s *Store) SetInContext(ctx context.Context, key string, inContext bool) error
Constants ¶
This section is empty.
Variables ¶
var ( ErrInvalidKey = errors.New("invalid note key") ErrNoteTooLarge = errors.New("note content exceeds size cap") ErrTooManyNotes = errors.New("note count would exceed cap") )
Sentinel errors. Wrapped with %w at every package boundary so callers can `errors.Is` to distinguish the user-fixable cases (oversize content, key collision past the cap) from genuine IO failures.
Functions ¶
func RenderInContextBlock ¶
RenderInContextBlock renders the pinned-notes section that flows into the system prompt on every step. Distinct from RenderPromptSection (the table of all notes by preview) in that each pinned note appears as a full-content `## <key>` markdown section — the model gets the whole body, not just a one-line preview, because pinned notes are facts it's expected to lean on without re-fetching.
Empty input → empty string so the caller can skip the separator.
func RenderPromptSection ¶
RenderPromptSection turns a list of notes into the `## Notes` block injected into the agent's system prompt by the PrepareStep callback. Empty input returns the empty string so the caller can skip the separator entirely.
Output shape (markdown):
## Notes | Key | Updated | Preview | |-----|---------|---------| | `k8s-cluster` | 5m ago | homelab cluster, 3 nodes | | `kubeconfig-path` | 2d ago | /kubeconfig.yaml | Notes persist across sessions. Use `note_show <key>` for full content; `note_save` / `note_delete` to mutate.
Cells are sanitised: pipes in the preview are escaped to keep the table well-formed (rare, but cheap).
Types ¶
type Note ¶
type Note struct {
Key string
Content string
Preview string
InContext bool
CreatedAt time.Time
UpdatedAt time.Time
}
Note is one stored fact. Preview is denormalised on Save so list rendering doesn't have to re-compute it across every row on every PrepareStep call. InContext, when true, marks the note for full inclusion in the agent's system prompt on every step — the model "pins" a fact it expects to reference frequently.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
func NewStore ¶
NewStore opens (or migrates) the notes table on the supplied connection. Safe to call alongside memory.NewStore on the same db — the two stores use disjoint tables.
func (*Store) Count ¶
Count returns the total number of stored notes. Cheap — used by Save to gate new-key inserts against the maxCount quota.
func (*Store) Delete ¶
Delete removes a note by key. Missing keys are silently successful — the tool layer surfaces "deleted (or already absent)" to the model regardless.
func (*Store) Get ¶
Get returns one note by key. sql.ErrNoRows is returned untouched so callers can errors.Is(err, sql.ErrNoRows) to render a "no such key" hint.
func (*Store) List ¶
List returns every note ordered by most-recently-updated first. The model uses this ordering when deciding what's still relevant — touching a note acts as an implicit "this still matters".
func (*Store) ListInContext ¶
ListInContext returns only the notes flagged in_context = 1. Used by the PrepareStep callback to render the per-step pinned block. Same row shape as List so the renderer can treat both uniformly.
func (*Store) Save ¶
Save upserts a note. Returns ErrInvalidKey if the key fails the validation regex, ErrNoteTooLarge if len(content) > maxBytes, and ErrTooManyNotes if inserting a *new* key would exceed maxCount (updates to an existing key are unaffected by the count guard).
Quotas live here, not in the tools layer, so there is one source of truth — anyone wiring a different surface (test harness, REPL, future operator RPC) gets the same enforcement for free.