Documentation
¶
Overview ¶
Package memory defines the pluggable memory provider contract.
Every memory plugin implements Provider as its core interface. Optional capabilities are discovered via type assertion at runtime, following the same pattern as github.com/CherryHQ/stella/pkg/hooks.
Index ¶
- Constants
- Variables
- func BuildPrompt(text string, opts SummarizeOptions) string
- func Dispatch(ctx context.Context, h Handler, action string, args map[string]any) (any, error)
- func EstimateTokens(text string) int
- func GroupIngestPipeline(agentID string) string
- func GroupMessageIDFromContext(ctx context.Context) string
- func GroupSeqFromContext(ctx context.Context) int64
- func MessageRole(msg ai.Message) string
- func MessageText(msg ai.Message) string
- func MessageTimestamp(msg ai.Message) time.Time
- func NeutralizeTags(s string, tags ...string) string
- func ProjectIDFromContext(ctx context.Context) string
- func ScopeUserIDFromContext(ctx context.Context) string
- func SessionIDFromContext(ctx context.Context) string
- func ToolNames() []string
- func WithChangeSource(ctx context.Context, source ChangeSource) context.Context
- func WithCurrentSpeaker(ctx context.Context, speaker CurrentSpeaker) context.Context
- func WithGroupMessageID(ctx context.Context, id string) context.Context
- func WithGroupSeq(ctx context.Context, seq int64) context.Context
- func WithGroupTurnSink(ctx context.Context, sink *GroupTurnSink) context.Context
- func WithGroupWake(ctx context.Context, wake GroupWake) context.Context
- func WithProjectID(ctx context.Context, projectID string) context.Context
- func WithSessionID(ctx context.Context, sessionID string) context.Context
- type ActionTool
- type ChangeEntry
- type ChangeSource
- type ChangelogCursor
- type ChangelogPageReader
- type ChangelogReader
- type ChangelogWriter
- type CompactionMode
- type CompactionResult
- type Compactor
- type ConstraintEntry
- type ConstraintStore
- type CurrentSpeaker
- type DeferredGroupTurn
- type DescribeResult
- type ExpandChild
- type ExpandMessage
- type ExpandResult
- type Explorer
- type Fact
- type FactStatus
- type FactStore
- type FactSubject
- type FactWrite
- type GroupCursorCommitter
- type GroupRecallResult
- type GroupRecallSource
- type GroupTurnSink
- type GroupWake
- type Handler
- type InboxAppender
- type KnowledgeUsageTracker
- type LLMSummarizer
- type ListOptions
- type MemoryReadInput
- type MemorySearchInput
- type MessageDetail
- type MessageReader
- type ProfileEntry
- type ProfileEntryStore
- type ProfileStore
- type Provider
- type Recall
- type RecallDocument
- type RecallFragment
- type RecallReference
- type RecallSearchResult
- type RecallSource
- type RecallSummaryDetail
- type ReviewHistoryReader
- type ReviewMessage
- type Reviewer
- type SearchQuery
- type SearchResult
- type SearchScope
- type Searcher
- type Session
- type SessionActivityStore
- type SessionInfo
- type SessionManager
- type SessionSnapshot
- type SessionSnapshotStore
- type SessionStats
- type SessionTurnResult
- type StaticSummarizer
- type SummarizeOptions
- type Summarizer
- type Tool
- type TxGroupCommitter
- type VersionedConstraintStore
- type VersionedFactStore
- type VersionedProfileStore
Constants ¶
const ( SummarizePolicyNormal = "normal" SummarizePolicyAggressive = "aggressive" )
SummarizePolicy constants control how aggressively content is compressed.
const ToolPrefix = "memory"
ToolPrefix is the family every generated memory tool name starts with.
Variables ¶
var ErrGroupRecallNotFound = errors.New("group recall ref not found")
ErrGroupRecallNotFound is the uniform fail-closed result for an unavailable group-history reference, including malformed, stale, foreign, and non-public rows.
var ErrInactiveSession = errors.New("session is not active")
ErrInactiveSession reports that a metadata write lost a race with archival (or the scoped session no longer exists). Callers must not retry the stale snapshot because doing so could revive lifecycle state that has moved on.
var ErrInboxAppendOutcomeUnknown = errors.New("session inbox append outcome unknown")
ErrInboxAppendOutcomeUnknown reports a commit-acknowledgement failure after the inbox claim and transcript writes were sent. The transaction may have committed; callers must resolve the pending row before promising no delivery.
var ErrInboxNotPending = errors.New("session inbox message is not pending")
ErrInboxNotPending reports that an inbox-backed input could not claim its pending row. Cancellation, prior delivery, and immutable-fact mismatch all fail closed through this one sentinel before model execution.
var ErrRotationOutcomeUnknown = errors.New("session rotation outcome unknown")
ErrRotationOutcomeUnknown reports that a rotation failed at the commit acknowledgement: the server may or may not have committed (e.g. the connection to an external PostgreSQL dropped after COMMIT was sent). Every other rotation failure is a definite rollback; this one is not, so a caller compensating for "rotation never happened" — releasing a dedup claim, auto-retrying — must treat it as possibly-happened instead.
var ErrStaleRotation = errors.New("session rotation is stale")
ErrStaleRotation reports that the session a rotation expected to replace is no longer the active one — a concurrent rotation already won. Rotation is a compare-and-rotate, so a stale caller changed nothing and must not retry.
Functions ¶
func BuildPrompt ¶
func BuildPrompt(text string, opts SummarizeOptions) string
BuildPrompt constructs the appropriate summarization prompt.
func EstimateTokens ¶
EstimateTokens returns a rough token count (~4 chars per token).
func GroupIngestPipeline ¶ added in v0.61.0
GroupIngestPipeline names the ctx_group_ingest_cursor pipeline that records one agent's last_completed_trigger_seq. The LCM committer owns movement, but the name is shared: session rotation fast-forwards the completed boundary, and the dispatcher reads it to drop restarted dispatch rows already completed.
func GroupMessageIDFromContext ¶ added in v0.65.0
GroupMessageIDFromContext extracts the durable trigger event identity.
func GroupSeqFromContext ¶ added in v0.42.0
GroupSeqFromContext extracts the triggering event-log seq from context.
func MessageRole ¶
MessageRole returns the role string for an ai.Message.
func MessageText ¶
MessageText extracts the plain text content from an ai.Message. Returns the text for user/assistant messages, or the tool result for tool messages.
func MessageTimestamp ¶
MessageTimestamp returns the timestamp of an ai.Message.
func NeutralizeTags ¶ added in v0.46.0
NeutralizeTags prevents user-derived text from opening or closing structural prompt tags while keeping the text readable for the model.
func ProjectIDFromContext ¶ added in v0.40.0
ProjectIDFromContext extracts the project ID from context.
func ScopeUserIDFromContext ¶ added in v0.61.0
ScopeUserIDFromContext returns the user_id this turn's conversation rows are keyed by. A group turn carries no user identity — runtime identity stays the group (D9) — and guest turns likewise carry no Stella user identity. Their conversations persist under their group_id or guest_id compatibility owner key. Use this for conversation-scoped reads and writes (session info, messages, summaries) only. Per-user data — soul, profile, constraints, knowledge facts — must keep resolving strictly against authz.UserIDFromContext so a group or guest turn fails closed instead of reading a person's private rows.
func SessionIDFromContext ¶
SessionIDFromContext extracts the session ID from context.
func ToolNames ¶ added in v0.66.0
func ToolNames() []string
ToolNames lists every generated tool name, for callers that gate on names.
func WithChangeSource ¶
func WithChangeSource(ctx context.Context, source ChangeSource) context.Context
WithChangeSource stores a ChangeSource in the context. This lets callers (e.g. Reflect) tag writes without changing function signatures.
func WithCurrentSpeaker ¶ added in v0.43.0
func WithCurrentSpeaker(ctx context.Context, speaker CurrentSpeaker) context.Context
WithCurrentSpeaker attaches the current-turn speaker to the context.
func WithGroupMessageID ¶ added in v0.65.0
WithGroupMessageID attaches the durable trigger event identity to a group turn. It is runtime-authored alongside GroupSeq and never comes from model input.
func WithGroupSeq ¶ added in v0.42.0
WithGroupSeq attaches the triggering event-log seq to the context.
func WithGroupTurnSink ¶ added in v0.65.0
func WithGroupTurnSink(ctx context.Context, sink *GroupTurnSink) context.Context
func WithGroupWake ¶ added in v0.65.0
WithGroupWake attaches this turn's wake reason to the context.
func WithProjectID ¶ added in v0.40.0
WithProjectID attaches a project ID to the context.
Types ¶
type ActionTool ¶ added in v0.66.0
type ActionTool = toolmeta.ActionTool
ActionTool describes one generated tool: an exact schema bound to one action.
func ActionTools ¶ added in v0.66.0
func ActionTools() []ActionTool
ActionTools lists every generated tool in a stable order.
type ChangeEntry ¶
type ChangeEntry struct {
ID string
UserID string
AgentID string
SessionID string
Scope string // "profile", "soul", "constraint", "skill", "compaction"
Action string // "create", "update", "delete", "deprecate", "compact"
Source ChangeSource
MemoryVersionBefore *int64
MemoryVersionAfter *int64
BeforeText string
AfterText string
CreatedAt string
}
ChangeEntry represents one changelog record.
type ChangeSource ¶
type ChangeSource string
ChangeSource identifies who triggered a memory write.
const ( SourceUser ChangeSource = "user" SourceAgent ChangeSource = "agent" SourceManual ChangeSource = "manual" SourceReflect ChangeSource = "reflect" SourceSystem ChangeSource = "system" )
func ChangeSourceFromContext ¶
func ChangeSourceFromContext(ctx context.Context) ChangeSource
ChangeSourceFromContext retrieves the ChangeSource from context. Returns SourceAgent if not set.
type ChangelogCursor ¶ added in v0.60.0
ChangelogCursor identifies the last logical entry in a stable changelog page.
type ChangelogPageReader ¶ added in v0.60.0
type ChangelogPageReader interface {
ReadChangelogPage(ctx context.Context, userID string, agentID string, scope string, cursor *ChangelogCursor, limit int) ([]ChangeEntry, error)
}
ChangelogPageReader is an optional capability for keyset-paginated history. ChangelogReader remains available for memory tools that only need recent rows.
type ChangelogReader ¶
type ChangelogReader interface {
// ReadChangelog returns the last N changelog entries for a given scope.
ReadChangelog(ctx context.Context, userID string, agentID string, scope string, limit int) ([]ChangeEntry, error)
}
ChangelogReader is implemented by providers that expose memory write history.
type ChangelogWriter ¶
type ChangelogWriter interface {
// WriteChangelog records a memory change.
WriteChangelog(ctx context.Context, entry ChangeEntry) error
}
ChangelogWriter is implemented by providers that record memory write history.
type CompactionMode ¶
type CompactionMode int
CompactionMode controls compaction behavior.
const ( // CompactionIncremental runs one leaf pass + one condensed pass. CompactionIncremental CompactionMode = iota // CompactionFull repeats until no more compaction is possible. CompactionFull )
func (CompactionMode) String ¶
func (m CompactionMode) String() string
String returns the human-readable name of the compaction mode.
type CompactionResult ¶
type CompactionResult struct {
LeafSummariesCreated int
CondensedSummariesCreated int
MessagesCompacted int
TokensBefore int
TokensAfter int
Duration time.Duration
}
CompactionResult reports what a compaction cycle accomplished.
type Compactor ¶
type Compactor interface {
// NeedsCompaction returns true if the session's context token count exceeds
// threshold, an absolute token count.
NeedsCompaction(ctx context.Context, session Session, threshold float64) bool
// Compact runs the compaction algorithm on the session.
// Incremental mode runs a single summarisation pass (fast, called automatically).
// Full mode runs repeated passes until no further reduction is possible
// (slow, called on demand e.g. via /compact slash command).
Compact(ctx context.Context, session Session, mode CompactionMode) (*CompactionResult, error)
}
Compactor is implemented by providers that support background compaction. The runtime calls NeedsCompaction before each chat turn and Compact when needed.
type ConstraintEntry ¶
type ConstraintEntry struct {
ID string `json:"id"`
Text string `json:"text"`
CreatedAt string `json:"created_at"`
}
ConstraintEntry represents a single user constraint.
type ConstraintStore ¶
type ConstraintStore interface {
// GetConstraints returns all active constraints for the (userID, agentID) pair.
GetConstraints(ctx context.Context, userID string, agentID string) ([]ConstraintEntry, error)
// AddConstraint adds a new constraint with a generated UUID-like ID.
// Returns the updated list of constraints.
AddConstraint(ctx context.Context, userID string, agentID string, text string) ([]ConstraintEntry, error)
// RemoveConstraint removes the constraint with the given ID.
// Returns the updated list of constraints.
RemoveConstraint(ctx context.Context, userID string, agentID string, id string) ([]ConstraintEntry, error)
}
ConstraintStore is implemented by providers that support storing user-defined hard constraints (rules the agent must always follow). Constraints are separate from Profile to give them special protection: Reflect cannot modify them; only user-initiated writes are allowed by convention.
type CurrentSpeaker ¶ added in v0.43.0
type CurrentSpeaker struct {
Platform string // source platform (telegram, feishu, web, ...)
PlatformUserID string // platform sender id / Web auth user id; lookup/audit only
DisplayName string // sender display name when available
UserID string // resolved Stella auth user id when linked; empty when unlinked
}
CurrentSpeaker carries the human who sent the current turn in a group session, where the runtime/session identity is the group (D9), not any one person.
D9 WARNING: CurrentSpeaker.UserID is a per-turn personalization target ONLY, never a runtime identity. Do not pass it to WithUserID, sandbox config, vault/token code, plugin contexts, delegate sessions, notify routing, or hook user metadata. Session/runtime ownership keeps flowing from the group id; this axis only selects whose profile the prompt and memory tool personalize.
func CurrentSpeakerFromContext ¶ added in v0.43.0
func CurrentSpeakerFromContext(ctx context.Context) (CurrentSpeaker, bool)
CurrentSpeakerFromContext extracts the current-turn speaker. The bool is false when no speaker is set (DM turns); it is true but UserID may be empty for an unlinked platform sender.
type DeferredGroupTurn ¶ added in v0.65.0
type DeferredGroupTurn struct {
Session Session
OwnRows []ai.Message
TriggerSeq int64
OriginGroupMessageID string
Complete bool
}
DeferredGroupTurn is the uncommitted group-turn state handed to the dispatcher. It is intentionally separate from ChatStream: stream rebuilding must never lose the transaction payload.
type DescribeResult ¶
type DescribeResult struct {
SummaryID string
Kind string // "leaf" or "condensed"
Depth int // 0 = leaf, 1+ = condensed
Content string // the summary text
EarliestAt *time.Time // timestamp of oldest source message
LatestAt *time.Time // timestamp of newest source message
DescendantCount int // total original messages this summary covers
ParentIDs []string // summaries that contain this one (condensed parents)
ChildIDs []string // summaries or messages this one was built from
}
DescribeResult represents summary metadata and lineage.
type ExpandChild ¶
ExpandChild is a child summary in an expand result.
type ExpandMessage ¶
ExpandMessage is a source message in an expand result.
type ExpandResult ¶
type ExpandResult struct {
SummaryID string
// For leaf summaries: the original source messages.
Messages []ExpandMessage
// For condensed summaries: the child summaries.
Children []ExpandChild
}
ExpandResult represents drill-down results from exploring a summary.
type Explorer ¶
type Explorer interface {
// Describe returns metadata and lineage for a summary ID.
Describe(ctx context.Context, summaryID string) (*DescribeResult, error)
// Expand drills into a summary:
// - For leaf summaries: returns the original source messages
// - For condensed summaries: returns the child summaries
// tokenCap limits how many tokens of content are returned.
Expand(ctx context.Context, summaryID string, tokenCap int) (*ExpandResult, error)
}
Explorer is implemented by providers that store summaries in a navigable hierarchy. It lets the agent inspect and drill into compressed history.
Note: these methods take summaryID only, not Session. Summary IDs are globally unique (e.g. "sum_a1b2c3d4") and already scoped to a conversation internally.
type Fact ¶ added in v0.60.0
type Fact struct {
ID string `json:"id"`
Subject FactSubject `json:"subject"`
Scope string `json:"scope"`
UserID string `json:"user_id"`
AgentID string `json:"agent_id"`
Content string `json:"content"`
Status FactStatus `json:"status"`
Metadata json.RawMessage `json:"metadata"`
Supersedes string `json:"supersedes,omitempty"`
Version int64 `json:"version"`
Source ChangeSource `json:"source"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Fact is the long-term memory record rendered from the facts table and serialized into fact changelog payloads.
type FactStatus ¶ added in v0.60.0
type FactStatus string
FactStatus is the authoritative lifecycle state for a fact.
const ( FactStatusActive FactStatus = "active" FactStatusDeprecated FactStatus = "deprecated" )
type FactStore ¶ added in v0.60.0
type FactStore interface {
ListActiveFacts(ctx context.Context, userID string, agentID string, subject FactSubject) ([]Fact, error)
}
FactStore is implemented by providers that can read current active facts for a user-agent pair. Profile, soul, and knowledge use different subjects.
type FactSubject ¶ added in v0.60.0
type FactSubject string
FactSubject identifies what a durable fact is about.
const ( FactSubjectUser FactSubject = "user" FactSubjectAgent FactSubject = "agent" FactSubjectWorld FactSubject = "world" )
type FactWrite ¶ added in v0.60.0
type FactWrite struct {
UserID string
AgentID string
Subject FactSubject
Content string
Metadata json.RawMessage
Supersedes string
Source ChangeSource
}
FactWrite is the input for create/update/replace fact write operations.
type GroupCursorCommitter ¶ added in v0.43.0
type GroupCursorCommitter interface {
CommitGroupCursor(ctx context.Context, session Session, triggerSeq int64) error
}
GroupCursorCommitter records a completed trigger only after its chat turn has completed successfully. Assembly is stateless; commit owns durable movement.
type GroupRecallResult ¶ added in v0.65.0
type GroupRecallResult struct {
ID string
Seq int64
ActorType string
ActorDisplayName string
Content string
Snippet string
Score float64
OccurredAt time.Time
}
GroupRecallResult is one canonical public group event found by the group-only recall lane. It deliberately excludes internal event-log fields and all media payloads: models receive just the public text projection and display snapshot.
type GroupRecallSource ¶ added in v0.65.0
type GroupRecallSource interface {
SearchGroupRecall(ctx context.Context, groupID string, triggerSeq int64, query string, limit int) ([]GroupRecallResult, error)
ReadGroupRecall(ctx context.Context, groupID string, triggerSeq int64, messageID string, tokenCap int) ([]GroupRecallResult, bool, error)
}
GroupRecallSource reads the canonical public transcript for one group turn. The caller obtains groupID and triggerSeq from trusted runtime context; model arguments never select either scope.
type GroupTurnSink ¶ added in v0.65.0
type GroupTurnSink struct {
// contains filtered or unexported fields
}
GroupTurnSink is a one-shot, non-blocking turn finalization record.
func GroupTurnSinkFrom ¶ added in v0.65.0
func GroupTurnSinkFrom(ctx context.Context) (*GroupTurnSink, bool)
func NewGroupTurnSink ¶ added in v0.65.0
func NewGroupTurnSink() *GroupTurnSink
func (*GroupTurnSink) Deliver ¶ added in v0.65.0
func (s *GroupTurnSink) Deliver(turn DeferredGroupTurn)
Deliver records the first terminal result without blocking the producer.
func (*GroupTurnSink) Result ¶ added in v0.65.0
func (s *GroupTurnSink) Result() (DeferredGroupTurn, bool)
Result returns the delivered result, and whether the producer delivered one.
type GroupWake ¶ added in v0.65.0
type GroupWake struct {
// Reason is the triage outcome that let this turn run.
Reason string
// HeldUpToSeq is set when this run follows a HOLD: a peer posted while the
// agent was drafting, and everything up to this seq is new since then.
HeldUpToSeq int64
// MentionSeq is the earliest unconsumed event that addressed this agent.
// It lets bounded context retain the reason for a coalesced wake.
MentionSeq int64
}
GroupWake is why this group turn exists. A group agent is woken by several different things -- being mentioned, a peer posting, a stalled-work nudge -- and the reply it should write differs for each. The model cannot infer this from the transcript, so the turn carries it.
It is per-turn metadata, never group content: it is rendered into the model's copy of the trigger and is not persisted to history or echoed to the group.
func GroupWakeFromContext ¶ added in v0.65.0
GroupWakeFromContext extracts this turn's wake reason, if any.
type Handler ¶ added in v0.66.0
type Handler interface {
Read(context.Context, MemoryReadInput) (any, error)
Search(context.Context, MemorySearchInput) (any, error)
}
type InboxAppender ¶ added in v0.63.3
type InboxAppender interface {
AppendInboxInput(ctx context.Context, session Session, inboxID string, msg ai.Message) error
}
InboxAppender is implemented by providers that can atomically claim a durable Session inbox row and append its input to the transcript. Callers must verify the unwrapped provider supports this capability before relying on a wrapper.
type KnowledgeUsageTracker ¶ added in v0.60.0
type KnowledgeUsageTracker interface {
TouchKnowledgeUsage(ctx context.Context, userID string, agentID string, factIDs []string) error
}
KnowledgeUsageTracker records runtime use for returned Reflect-owned world facts.
type LLMSummarizer ¶
type LLMSummarizer struct {
// Generate calls the LLM with the given prompt and returns the response text.
Generate func(ctx context.Context, prompt string) (string, error)
}
LLMSummarizer generates summaries using an LLM via a callback function.
func (*LLMSummarizer) Summarize ¶
func (s *LLMSummarizer) Summarize(ctx context.Context, text string, opts SummarizeOptions) (string, error)
Summarize generates a summary with escalation: normal -> aggressive -> deterministic fallback.
type ListOptions ¶
type ListOptions struct {
AgentID string // filter by agent (empty = all)
UserID string // filter by user (empty = all)
GuestID string // filter by guest; empty excludes guest-owned sessions
Kind string // filter by kind (empty = all)
Channel string // filter by durable channel binding (empty = all)
ProjectID string // filter by project (empty = all)
ProjectIDIsNull bool // when true, require project_id IS NULL
IncludeArchived bool
ExcludeInternal bool // hide task/delegate worker sessions from user-facing lists
Limit int // 0 = no limit
Offset int // skip first N matching results
}
ListOptions controls session listing filters.
type MemoryReadInput ¶ added in v0.66.0
type MemorySearchInput ¶ added in v0.66.0
type MessageDetail ¶ added in v0.49.0
type MessageDetail struct {
MessageID string `json:"message_id"`
Role string `json:"role"`
Content string `json:"content"`
OccurredAt time.Time `json:"occurred_at"` // when the message happened
SessionID string `json:"session_id"` // origin session for traceability
ConversationTitle string `json:"conversation_title"` // human-readable origin label (may be empty)
}
MessageDetail is the full, untruncated content of a single stored message.
type MessageReader ¶ added in v0.49.0
type MessageReader interface {
GetMessage(ctx context.Context, messageID string) (*MessageDetail, error)
}
MessageReader is implemented by providers that can return a single message in full by ID, scoped to (user_id, agent_id). It complements Searcher: search hits are truncated snippets, so the agent calls GetMessage to read a hit in full — including hits from other sessions of the same user+agent.
type ProfileEntry ¶ added in v0.42.0
type ProfileEntry struct {
ID string `json:"id"`
Text string `json:"text"`
Source string `json:"source"` // "manual" or "auto"
CreatedAt string `json:"created_at"` // RFC3339
}
ProfileEntry represents a single dated profile entry, either manually edited by the user or auto-generated by async memory ingest (D6).
type ProfileEntryStore ¶ added in v0.42.0
type ProfileEntryStore interface {
GetProfileEntries(ctx context.Context, userID string, agentID string) ([]ProfileEntry, error)
}
ProfileEntryStore is implemented by providers that support dated profile entries (D5). Auto-generated entries live alongside the manual profile blob.
type ProfileStore ¶
type ProfileStore interface {
// GetProfile returns the current user profile for the (userID, agentID) pair.
// Returns ("", nil) if no profile exists yet (not an error).
GetProfile(ctx context.Context, userID string, agentID string) (string, error)
// SetProfile overwrites the user profile for the (userID, agentID) pair.
// Callers are responsible for merging new content with existing content
// before calling SetProfile — this method always replaces, never appends.
SetProfile(ctx context.Context, userID string, agentID string, content string) error
// GetAgentSoul returns the agent soul for the (userID, agentID) pair.
// The soul defines the agent's identity, personality, and behavior as
// customised by this specific user. Returns ("", nil) if not set.
GetAgentSoul(ctx context.Context, userID string, agentID string) (string, error)
// SetAgentSoul overwrites the agent soul for the (userID, agentID) pair.
// Callers are responsible for merging; this method always replaces.
SetAgentSoul(ctx context.Context, userID string, agentID string, content string) error
}
ProfileStore is implemented by providers that support per-user-per-agent persistent memory: agent soul (identity/personality customization) and user profile (facts/context about the user).
Both are scoped to (userID, agentID) — each user can customise the agent's soul independently, and the agent maintains separate profile notes per user. Content is free-form text managed entirely by the agent. The system injects both into the system prompt at session start.
type Provider ¶
type Provider interface {
// Name returns the plugin identifier (e.g. "lcm", "simple").
// Used in logs and admin UI.
Name() string
// Bootstrap ensures the session is initialized and ready for use.
// Called once at the start of every chat turn before any Append or Assemble.
// Implementations use this to create conversation records, initialize caches,
// or establish remote connections for the session.
Bootstrap(ctx context.Context, session Session) error
// Append persists one or more messages to the session's event log.
// Ordinary sessions receive canonical image references and must reject raw
// provider image bytes. Group sessions may retain their legacy inline codec.
// Messages must be appended in the order they arrive.
// Callers pass all messages from a single turn together so implementations
// can store them in a single atomic transaction if they choose.
//
// Concurrency: implementations MUST be safe for concurrent Append calls
// on different sessions. Concurrent Append calls on the SAME session
// MUST be serialised by the implementation (see Concurrency Contract).
Append(ctx context.Context, session Session, msgs ...ai.Message) error
// Assemble builds the context window to send to the LLM.
// budget: maximum number of tokens the returned messages may consume.
// freshTail: number of recent user turns to prefer verbatim. Implementations
// may apply safety caps so oversized tails do not consume the whole budget.
// Returns messages in chronological order (oldest first).
// Older content that does not fit in the budget is either summarised
// (if the plugin supports Compactor) or omitted.
Assemble(ctx context.Context, session Session, budget, freshTail int) ([]ai.Message, error)
// Stats returns basic statistics about a session's memory state.
// Used by the admin endpoints; no model-facing tool exposes it.
// Returns zero-value stats (not an error) if the session does not exist.
Stats(ctx context.Context, session Session) (SessionStats, error)
// Close releases any resources held by the provider (DB connections, caches, etc.).
// Called on shutdown. Must be safe to call multiple times.
Close() error
}
Provider is the memory plugin contract. Every memory plugin must implement this interface. Optional capabilities are discovered via type assertion — see Capability Interfaces.
func Unwrap ¶
Unwrap returns the innermost Provider by recursively unwrapping traced layers. If p does not implement the unwrapper interface, p itself is returned.
func WithTracing ¶
WithTracing wraps a Provider to emit PostMemoryCall hooks after each operation. hooksFn is called on each operation to get the current HookSet; it may return nil. The returned Provider implements optional capability interfaces. Unsupported capabilities return sensible zero values or errors. Use Unwrap to check the inner provider directly.
The Detail field is always populated with content previews. The trace hook decides whether to emit it based on log level (see [LevelTrace]).
type Recall ¶ added in v0.66.0
type Recall struct {
// contains filtered or unexported fields
}
Recall is the memory surface both generated tools dispatch onto: the provider capabilities discovered once, the private recall lane, and the group lane. One instance is shared by memory_search and memory_read, because those capabilities belong to the provider rather than to a call.
func NewRecall ¶ added in v0.66.0
func NewRecall(provider Provider, private RecallSource, group GroupRecallSource) *Recall
NewRecall discovers the provider's capabilities once and binds both recall lanes. The group lane is selected from trusted turn context, never from tool input.
Capabilities are probed on Unwrap(provider) so a traced wrapper cannot advertise something the inner provider does not implement, but the typed references are taken from the outer provider so tracing still fires.
type RecallDocument ¶ added in v0.63.3
type RecallDocument struct {
Reference RecallReference
Content string
Role string
Authority string
OccurredAt time.Time
SessionID string
ConversationTitle string
Summary *RecallSummaryDetail
}
RecallDocument is one fully authorized conversation-memory read.
type RecallFragment ¶ added in v0.63.3
type RecallFragment struct {
Reference RecallReference
Role string
Authority string
Kind string
Depth *int
Content string
OccurredAt time.Time
}
RecallFragment is one authorized, one-level expansion item for a summary.
type RecallReference ¶ added in v0.63.3
RecallReference identifies one conversation-memory resource behind the Session policy boundary. It is an internal transport value: model-facing callers receive an opaque encoded ref and every read reauthorizes this tuple.
type RecallSearchResult ¶ added in v0.63.3
type RecallSearchResult struct {
Reference RecallReference
Content string
Score float64
OccurredAt time.Time
SessionID string
ConversationTitle string
}
RecallSearchResult is one authorized message or summary match. Source kinds stay inside the recall facade; the memory tool returns one uniform result shape and an opaque ref instead.
type RecallSource ¶ added in v0.63.3
type RecallSource interface {
SearchRecall(ctx context.Context, authority authz.Authority, agentID, query string, limit int) ([]RecallSearchResult, error)
ReadRecall(ctx context.Context, authority authz.Authority, agentID string, ref RecallReference, tokenCap int) (RecallDocument, error)
}
RecallSource is the authorized Session facade used by the model-facing memory tool. It keeps transcript policy in Session while memory presents one search/read mental model across transcripts and durable memory.
type RecallSummaryDetail ¶ added in v0.63.3
type RecallSummaryDetail struct {
Kind string
Depth int
DescendantCount int
EarliestAt *time.Time
LatestAt *time.Time
Parents []RecallReference
Children []RecallReference
Expanded []RecallFragment
}
RecallSummaryDetail preserves LCM's describe and bounded one-level expand capabilities without exposing provider actions to the model.
type ReviewHistoryReader ¶ added in v0.60.0
type ReviewHistoryReader interface {
LoadReviewHistory(ctx context.Context, sessionID string) ([]ReviewMessage, error)
}
ReviewHistoryReader returns raw session history with stable storage boundaries for background reviewers.
type ReviewMessage ¶ added in v0.60.0
ReviewMessage is a logical message with the underlying storage boundary kept intact for deterministic review watermarking. Assistant turns may span multiple storage rows, so callers must advance to LastSeq after reviewing one.
type Reviewer ¶
type Reviewer interface {
// BuildReviewContext returns a text representation of the conversation
// suitable for passing to a reviewer agent's prompt.
//
// since: if non-zero, only include content created after this time.
// The provider should include prior context (e.g. summaries from before
// this timestamp) to give the reviewer enough background.
// If zero, include all content.
//
// Returns ("", nil) if there is no content to review.
BuildReviewContext(ctx context.Context, session Session, since time.Time) (string, error)
}
Reviewer is implemented by providers that can format conversation content for review by a background agent (the reflect system).
This is deliberately a single method. Watermark tracking (which sessions have been reviewed, when) is not the memory plugin's concern — it belongs to the consumer (`internal/reflect`). The memory provider's only job is: "given a session and an optional time boundary, produce reviewable text."
type SearchQuery ¶
type SearchQuery struct {
Text string // search term (keyword or natural language depending on plugin)
Scope SearchScope // which layer of storage to search
Limit int // max results (default 20)
}
SearchQuery describes a search request.
type SearchResult ¶
type SearchResult struct {
SourceType string `json:"source_type"` // "message" or "summary"
SourceID string `json:"source_id"` // message ID or summary ID
Content string `json:"content"` // snippet of the matching content (truncated at ~500 chars)
Score float64 `json:"score"` // relevance, higher is better: pg_search BM25 for a single-scope query, RRF fusion score when messages and summaries are merged
OccurredAt time.Time `json:"occurred_at"` // when the underlying content actually happened
SessionID string `json:"session_id"` // origin session for traceability
ConversationTitle string `json:"conversation_title"` // human-readable origin label (may be empty)
}
SearchResult represents a single search hit. Results span every active session of the current (user_id, agent_id), so each hit carries provenance — which conversation it came from — plus the time the content actually occurred so the agent can weight recency.
type SearchScope ¶
type SearchScope int
SearchScope controls which storage layer to search.
const ( SearchScopeBoth SearchScope = iota // default: search everything SearchScopeMessages // raw messages only SearchScopeSummaries // summaries only )
type Searcher ¶
type Searcher interface {
Search(ctx context.Context, session Session, query SearchQuery) ([]SearchResult, error)
}
Searcher is implemented by providers that support history search.
type Session ¶
type Session struct {
ID string // unique session key (e.g. "default:cli:<uuid>:main")
AgentID string // agent this session belongs to (e.g. "default")
UserID string // internal user ID (empty for anonymous/legacy)
Channel string // originating channel (e.g. "cli", "telegram")
GroupID string // non-empty for group sessions; assembles history from event log instead of ctx_message
GuestID string // non-empty for a persistent channel guest; equals UserID
}
Session identifies the context of a single conversation. It is created by the runtime and passed to all Provider methods.
type SessionActivityStore ¶ added in v0.63.0
type SessionActivityStore interface {
MarkSessionTurnStarted(ctx context.Context, session Session) (bool, error)
MarkSessionTurnCompleted(ctx context.Context, session Session, result SessionTurnResult) (bool, error)
MarkSessionViewed(ctx context.Context, session Session) (bool, error)
}
SessionActivityStore persists the latest terminal turn result and view watermark. Working remains process-local runtime truth; unread terminal results survive navigation and refresh until the session is opened.
type SessionInfo ¶
type SessionInfo struct {
ID string
AgentID string
UserID string
GroupID string // non-empty for group sessions; runtime uses this to isolate identity surfaces
GuestID string // non-empty for a persistent channel guest; equals UserID
Channel string
Kind string // session kind: main, chat, scheduler, task
ProjectID string // set for project-scoped sessions
Title string // auto-generated from first message
CreatedAt time.Time
LastActive time.Time
LastTurnStartedAt time.Time
LastTurnCompletedAt time.Time
LastTurnResult SessionTurnResult
LastViewedAt time.Time
Archived bool
// LatestSeq is the latest persisted message sequence when supplied by a
// review-aware listing. Zero means the provider has no stable sequence.
LatestSeq int64
}
SessionInfo holds metadata about a session.
type SessionManager ¶
type SessionManager interface {
// SaveInfo creates a session or updates metadata while it remains active.
// Archived is only honored when creating a new row; lifecycle transitions
// for an existing row must use ArchiveInfo or RotateInfo.
SaveInfo(ctx context.Context, info SessionInfo) error
// ArchiveInfo atomically transitions an active session to archived. It
// reports false without writing when the scoped session is already inactive.
ArchiveInfo(ctx context.Context, info SessionInfo) (bool, error)
// RotateInfo archives expectedSessionID and creates successor as one atomic
// unit, so a durable binding is never left without an active session and a
// one-active-session-per-binding index is never violated mid-rotation.
// It returns ErrStaleRotation, with nothing persisted, when expectedSessionID
// is no longer active or no longer matches successor's binding.
RotateInfo(ctx context.Context, expectedSessionID string, successor SessionInfo) error
// TouchActiveInfo records a running turn's metadata — its last-active
// timestamp, and a title for a session that has none — against a session that
// is still active, and reports false without writing when it is not.
//
// It exists because SaveInfo cannot serve the turn path: a turn holds the
// snapshot it resolved at the start, a rotation can archive that session
// while the turn runs, and replaying the snapshot would un-archive a session
// the chat has already left. Checking first and then saving only narrows the
// race; this is the guard and the write in one statement.
TouchActiveInfo(ctx context.Context, info SessionInfo) (bool, error)
// LoadInfo retrieves metadata for a single session.
LoadInfo(ctx context.Context, sessionID string) (SessionInfo, error)
// ListInfo lists sessions matching the options.
ListInfo(ctx context.Context, opts ListOptions) ([]SessionInfo, error)
// LoadHistory returns the complete raw message history for a session
// in chronological order. Used by the Web UI viewer and export.
LoadHistory(ctx context.Context, sessionID string) ([]ai.Message, error)
}
SessionManager is implemented by providers that support session lifecycle management.
type SessionSnapshot ¶
type SessionSnapshot struct {
SessionID string
UserID string
AgentID string
Version int64
// UpdatedAt is the wall-clock time when this snapshot was last advanced.
// It remains for legacy skill-backed knowledge filtering; v1 facts use
// Version as their snapshot clock.
UpdatedAt time.Time
}
SessionSnapshot holds the frozen version for a session. Version 0 is a valid empty snapshot, not an alias for current memory.
type SessionSnapshotStore ¶
type SessionSnapshotStore interface {
// GetOrCreateSessionSnapshot returns the snapshot for the session, creating it
// if it doesn't exist. When creating, freezes the current ctx_agent_memory.version.
GetOrCreateSessionSnapshot(ctx context.Context, sessionID string, userID string, agentID string) (SessionSnapshot, error)
// AdvanceSessionSnapshot updates the snapshot version to the current
// ctx_agent_memory.version for (userID, agentID).
// Called after front-end memory writes so the current session sees the update.
AdvanceSessionSnapshot(ctx context.Context, sessionID string, userID string, agentID string) error
}
SessionSnapshotStore is implemented by providers that support session snapshots.
type SessionStats ¶
type SessionStats struct {
MessageCount int // total messages stored for this session
TokenCount int // estimated total tokens across all stored messages
SummaryCount int // number of summaries (0 for providers without Compactor)
OldestAt time.Time // timestamp of the earliest message (zero if empty)
NewestAt time.Time // timestamp of the most recent message (zero if empty)
}
SessionStats contains basic statistics about a session's memory state. Every provider can produce this since it only requires knowledge of what was stored via Append.
type SessionTurnResult ¶ added in v0.63.0
type SessionTurnResult string
const ( SessionTurnSuccess SessionTurnResult = "success" SessionTurnError SessionTurnResult = "error" SessionTurnCanceled SessionTurnResult = "canceled" )
func (SessionTurnResult) Valid ¶ added in v0.63.0
func (r SessionTurnResult) Valid() bool
type StaticSummarizer ¶
StaticSummarizer always returns a fixed response (for testing).
func (*StaticSummarizer) Summarize ¶
func (s *StaticSummarizer) Summarize(_ context.Context, _ string, _ SummarizeOptions) (string, error)
Summarize returns the static response.
type SummarizeOptions ¶
type SummarizeOptions struct {
IsCondensed bool
Depth int
Aggressive bool
Previous string // previous summary for continuity
TargetTokens int
}
SummarizeOptions controls summarization behavior.
type Summarizer ¶
type Summarizer interface {
Summarize(ctx context.Context, text string, opts SummarizeOptions) (string, error)
}
Summarizer generates summaries from content.
type Tool ¶ added in v0.66.0
type Tool struct {
// contains filtered or unexported fields
}
Tool is one generated memory tool. The name carries the action, so the provider validates arguments against an exact schema before dispatch, and identity always comes from the runtime context.
func NewTool ¶ added in v0.66.0
func NewTool(recall *Recall, spec ActionTool) *Tool
NewTool builds one memory action tool over a shared Recall.
func (*Tool) Definition ¶ added in v0.66.0
func (t *Tool) Definition() tools.Definition
type TxGroupCommitter ¶ added in v0.65.0
type TxGroupCommitter interface {
CommitGroupTurn(context.Context, *sqlc.Queries, DeferredGroupTurn) error
}
TxGroupCommitter commits a deferred turn into the dispatcher's outer tx.
type VersionedConstraintStore ¶
type VersionedConstraintStore interface {
GetConstraintsAt(ctx context.Context, userID string, agentID string, version int64) ([]ConstraintEntry, error)
}
VersionedConstraintStore is implemented by providers that can read constraints at a non-negative frozen memory version. Callers that need current state must use ConstraintStore; version 0 is the valid empty baseline before the first write, and negative versions return an error.
type VersionedFactStore ¶ added in v0.60.0
type VersionedFactStore interface {
ListActiveFactsAt(ctx context.Context, userID string, agentID string, subject FactSubject, version int64) ([]Fact, error)
}
VersionedFactStore reconstructs facts at a frozen memory version from the fact changelog. Version 0 is the valid empty baseline before the first write; callers that need current state must use FactStore.
type VersionedProfileStore ¶
type VersionedProfileStore interface {
GetProfileAt(ctx context.Context, userID string, agentID string, version int64) (string, error)
GetAgentSoulAt(ctx context.Context, userID string, agentID string, version int64) (string, error)
}
VersionedProfileStore is implemented by providers that can read profile/soul at a specific memory version (from the changelog). Every non-negative version is a frozen memory clock. Callers that need current state must use ProfileStore; version 0 is the valid empty baseline before the first write, and negative versions return an error.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package memorytest provides test doubles and conformance testing for memory.Provider implementations.
|
Package memorytest provides test doubles and conformance testing for memory.Provider implementations. |
|
Package memorywrite provides shared transactional write helpers for durable memory surfaces.
|
Package memorywrite provides shared transactional write helpers for durable memory surfaces. |
|
Package profile is the application boundary for per-(user, agent) memory: the profile blob, agent soul, hard constraints, reset, and the change history.
|
Package profile is the application boundary for per-(user, agent) memory: the profile blob, agent soul, hard constraints, reset, and the change history. |