Help us shape the future of Go! Take the 2026 Go Developer Survey

memory

package
v1.33.4 Latest Latest
Warning

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

Go to latest
Published: Oct 27, 2026 License: Apache-2.0 Imports: 19 Imported by: 0

Documentation

Overview

Package memory exposes the identity-scoped administrative surface of Harbor's cumulative session memory. Runtime execution and these operations share the same StateStore-backed owner in internal/memory/session. There is no pair-only history store or background summary engine. Identity is mandatory.

Index

Constants

View Source
const DefaultDriver = "inmem"

DefaultDriver is the production driver name. The SQL drivers (SQLite + Postgres) registers additional names.

View Source
const EventTypeMemoryCallerBlockAdmitted events.EventType = "memory.caller_block_admitted"

EventTypeMemoryCallerBlockAdmitted is emitted once per run that admits caller-supplied content into its read-only external-memory tier. A Console that cannot tell caller-asserted memory from runtime-retrieved memory can audit neither, so the admission is announced over the Protocol.

It carries a SIZE and never CONTENT — see CallerBlockAdmittedPayload.

It fires at admission, which happens before planning, so the event lands whether or not the run subsequently succeeds.

View Source
const EventTypeMemoryHealthChanged events.EventType = "memory.health_changed"

EventTypeMemoryHealthChanged is emitted on every `Health` FSM transition under `rolling_summary`. Subscribers (Console, audit pipeline) render the transition; SREs alert on `degraded` duration. RFC §6.6.

The observable health-transition emit is the explicit exception to AGENTS.md §13's "no silent degradation" rule — degraded mode IS the observable failure path, and the emit makes it observable (and therefore not silent).

View Source
const EventTypeMemoryIdentityRejected events.EventType = "memory.identity_rejected"

EventTypeMemoryIdentityRejected is emitted on the events bus when a `MemoryStore` method is called with a missing identity triple (fail-closed contract). The store ALSO returns `ErrIdentityRequired`; the bus emit makes the rejection observable from the Console / audit pipeline.

Registered via `events.RegisterEventType` from this package's `init()` so `Publish` accepts the type without `ErrUnknownEventType`.

View Source
const EventTypeMemoryItemDeleted events.EventType = "memory.item_deleted"

EventTypeMemoryItemDeleted is emitted on the events bus when an admin evicts a memory turn through the `memory.delete` Protocol method ( ). The audit trail for the eviction. SafePayload — the hashed key only, never any record value bytes.

View Source
const EventTypeMemoryItemPut events.EventType = "memory.item_put"

EventTypeMemoryItemPut is emitted on the events bus when an admin adds a memory turn through the `memory.put` Protocol method It is the audit trail for the mutation: the Console / audit pipeline observes who added what (by key) and when. SafePayload by construction — it carries the deterministic (hashed) turn key only, never the operator-supplied turn text.

View Source
const EventTypeMemoryRecoveryDropped events.EventType = "memory.recovery_dropped"

EventTypeMemoryRecoveryDropped is emitted when the `rolling_summary` recovery backlog overflows `RecoveryBacklogMax` and the executor drops the oldest queued batch to make room. Per the bounded recovery loop.

Variables

View Source
var (
	// ErrInvalidInspection rejects malformed items returned by a memory driver.
	ErrInvalidInspection = errors.New("memory: invalid inspection")
	// ErrNotFound means no current authorized item has the requested key.
	ErrNotFound = errors.New("memory: record not found")

	// ErrIdentityRequired — a method was called with a
	// `Quadruple` whose tenant, user, or session was empty.
	// The fail-closed gate
	ErrIdentityRequired = errors.New("memory: identity triple incomplete")

	// ErrUnknownDriver — `Open` was asked for a driver name no
	// registered factory handles. The wrapped message lists the
	// registered names.
	ErrUnknownDriver = errors.New("memory: unknown driver")

	// ErrStoreClosed — a method was called after `Close`.
	ErrStoreClosed = errors.New("memory: store is closed")

	// ErrStrategyNotImplemented rejects a removed or unknown strategy.
	ErrStrategyNotImplemented = errors.New("memory: unsupported strategy")

	// ErrInvalidHealthTransition — a strategy executor attempted a
	// `Health` transition outside the documented FSM
	// (`healthy ↔ retry ↔ degraded ↔ recovering`). Fail loudly: an
	// invalid transition is a programming error, not a recoverable
	// state.
	ErrInvalidHealthTransition = errors.New("memory: invalid health transition")
)

Sentinel errors. Callers compare via `errors.Is`.

Functions

func EmitHealthChanged

func EmitHealthChanged(ctx context.Context, bus events.EventBus, id identity.Quadruple, prior, next Health, reason string) error

EmitHealthChanged publishes the `memory.health_changed` event on `bus`. The transition is validated against the documented `Health` FSM (see `ValidateHealthTransition`); an invalid transition returns wrapped `ErrInvalidHealthTransition` and does NOT publish — fail loudly, never silently coerce an illegal transition into a silent no-op (AGENTS.md §13).

The `reason` string is a short static label ("summarizer_failed", "retries_exhausted", "recovery_loop_drained", etc.) that subscribers / SREs can filter on. It MUST NOT carry caller-controlled bytes.

Identity is required — the event's `Identity` field carries the executor's per-key triple so subscribers can scope the transition to a session.

Self-loops (prior == next) ARE valid transitions per `ValidateHealthTransition`; this helper still emits the event so downstream observability can count "stayed healthy across summarisation success" — but it is the caller's choice to skip the self-loop emit if the cost is undesirable.

func EmitIdentityRejected

func EmitIdentityRejected(ctx context.Context, bus events.EventBus, q identity.Quadruple, operation string) error

EmitIdentityRejected publishes the `memory.identity_rejected` event on `bus` and returns the wrapped sentinel error. Drivers call this from every method that detected an identity-rejection so the audit emit path is consistent across implementations.

The bus's `ValidateEvent` requires a fully-populated identity triple; the partial / empty triple the caller supplied is preserved where present and substituted with the `missingIdentitySentinel` string elsewhere, so the rejection event itself is bus-publishable. The audit-visible payload's `Reason` field names the component(s) that were actually missing.

`operation` is the rejected method name ("AddTurn", "GetLLMContext", etc.). The reason string is computed from whichever components were missing on the supplied quadruple.

Returns the wrapped `ErrIdentityRequired` for the caller to surface. If Publish fails, the wrapped error names both the rejection cause and the bus failure — callers MUST NOT silently drop either.

func EmitRecoveryDropped

func EmitRecoveryDropped(ctx context.Context, bus events.EventBus, id identity.Quadruple, reason string) error

EmitRecoveryDropped publishes the `memory.recovery_dropped` event on `bus`. Used by the `rolling_summary` recovery loop when the backlog overflows `RecoveryBacklogMax` and the oldest queued batch is dropped to make room (bounded recovery loop).

Identity is required.

func IsSourceReference added in v1.32.0

func IsSourceReference(id string) bool

IsSourceReference identifies the reserved, source-bound memory reference namespace. It must never fall through to ordinary blob lookup or presigning.

func Register

func Register(name string, factory Factory)

Register installs a driver factory under `name`. Drivers self- register from their package `init()`; `cmd/harbor` blank-imports the production driver to trigger registration. Per AGENTS.md §4.4.

Re-registering the same name panics — the registration model is write-once-at-init and a duplicate signals a build mis-config.

func RegisteredDrivers

func RegisteredDrivers() []string

RegisteredDrivers returns a sorted list of driver names. Useful for boot-log emission ("memory drivers available: inmem") and for surfacing in error messages.

func ResolveSourceReference added in v1.32.0

func ResolveSourceReference(ctx context.Context, store MemoryStore, id identity.Quadruple, name string) (artifacts.ArtifactRef, []byte, error)

ResolveSourceReference rechecks current committed memory on every read. Expiry, erasure, compaction or deletion that removes the source makes the reference unavailable; a changed value cannot be served under the old name. Read failures remain failures, never an instruction to retry an old action.

func SourceReference added in v1.32.0

func SourceReference(id identity.Quadruple, item Item) (artifacts.ArtifactRef, error)

SourceReference names the exact bytes of one committed memory item without persisting a second copy. Scope, source key, content and expiry all participate in the name. Consumers resolve it through the bounded artifacts.get surface; it is not a blob in ArtifactStore and cannot be presigned independently.

func ValidateHealthTransition

func ValidateHealthTransition(prior, next Health) error

ValidateHealthTransition returns nil when `(prior → next)` is a legal `Health` FSM edge. Invalid transitions return wrapped `ErrInvalidHealthTransition` — fail loudly per AGENTS.md §5; an invalid transition is a programming error in the calling executor, not a recoverable state.

The empty `Health{}` (zero value) is treated as `HealthHealthy` for both sides — a freshly-constructed executor implicitly starts healthy.

func ValidateIdentity

func ValidateIdentity(q identity.Quadruple) error

ValidateIdentity returns wrapped `ErrIdentityRequired` when any of (tenant, user, session) is empty. Empty `RunID` is acceptable. Drivers call this at the boundary before any I/O.

func ValidateStrategy added in v1.32.0

func ValidateStrategy(strategy Strategy) error

ValidateStrategy rejects removed and unknown memory strategies at construction. An omitted strategy selects cumulative rolling memory, like the YAML default.

func WithStore

func WithStore(ctx context.Context, store MemoryStore) context.Context

WithStore attaches the store to ctx for downstream handlers.

Types

type Access added in v1.32.0

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

Access binds driver operations to the cumulative owner and holds no session state.

func NewAccess added in v1.32.0

func NewAccess(cfg ConfigSnapshot, deps Deps) *Access

NewAccess shares runtime dependencies without opening storage or background work.

func (*Access) Delete added in v1.32.0

func (a *Access) Delete(ctx context.Context, id identity.Quadruple, key string) (int, error)

Delete invalidates affected cumulative context through the same owner as execution.

func (*Access) Inspect added in v1.32.0

func (a *Access) Inspect(ctx context.Context, id identity.Quadruple) (Inspection, error)

Inspect reads committed, unexpired memory. Disabled memory never reads history.

func (*Access) Put added in v1.32.0

Put commits an operator note through the execution owner's conditional write.

type CallerBlockAdmittedPayload added in v1.25.0

type CallerBlockAdmittedPayload struct {
	events.SafeSealed `json:"-"`

	// Bytes is the size of the admitted document as it arrived on the
	// wire. It is the only caller-influenced value on the payload.
	Bytes int `json:"bytes"`
	// Tier is the prompt tier the content was admitted into — the
	// runtime-owned wrapper name, always the read-only external-memory
	// tier.
	Tier string `json:"tier"`
	// Key is the fixed runtime-owned map key the content was composed
	// under within that tier.
	Key string `json:"key"`
}

CallerBlockAdmittedPayload is the audit payload for EventTypeMemoryCallerBlockAdmitted. SafePayload BY CONSTRUCTION, and the construction is the point: the only caller-derived quantity on it is a byte COUNT. `Tier` and `Key` are runtime-owned constants, not caller input — the caller names no key.

No fragment of the admitted content appears here, and none may ever be added: the payload is caller-controlled bytes that would require redaction, and the audit trail records THAT an admission happened and how large it was, never what it said (CLAUDE.md §7 rules 6-7).

type ConfigSnapshot

type ConfigSnapshot struct {
	Driver        string
	DSN           string
	MigrationMode sqlmigrate.Mode
	Strategy      Strategy
	BudgetTokens  int
	RecentTurns   int
}

ConfigSnapshot is the strict subset of `config.MemoryConfig` the memory package consumes. Keeping a snapshot decouples drivers from the config package's type evolution. Callers (typically `cmd/harbor/main.go`'s bootstrap or a test wiring helper) translate `config.MemoryConfig` → `ConfigSnapshot` at the seam.

`DSN` is consumed by the SQLite + Postgres drivers; the InMem driver ignores it. Validation of "DSN required for persistent drivers" lives at the config layer (`validateMemory` in `internal/config/validate.go`) and at the driver constructor itself — fail-loudly twice so a misconfiguration surfaces early.

RecentTurns bounds detailed recent execution evidence. Zero selects twenty.

func SnapshotFromConfig added in v1.3.0

func SnapshotFromConfig(cfg config.MemoryConfig) ConfigSnapshot

SnapshotFromConfig projects the operator-facing `config.MemoryConfig` block onto the memory package's decoupled ConfigSnapshot. Every config field maps 1:1; the field-parity test in from_config_test.go fails the build when a new config field lands without a projection (or an explicit exclusion naming why).

type ConversationTurn

type ConversationTurn struct {
	UserMessage       string
	AssistantResponse string
}

ConversationTurn is an administrator-supplied conversational note. Runtime turns are recorded through the cumulative execution owner, not Put.

type Deps

type Deps struct {
	State state.StateStore
	Bus   events.EventBus
	// Redactor and RetentionTTL are the same execution-memory dependencies
	// supplied by runtime composition. Put refuses a missing redactor.
	Redactor     audit.Redactor
	RetentionTTL time.Duration
}

Deps carries the runtime dependencies a memory driver needs.

The `State` field is mandatory (typed wrapper writes opaque bytes through the generic surface). The `Bus` field is mandatory so identity-rejection emits land on the audit pipeline. Drivers MUST NOT accept missing deps silently; the registry rejects an `Open` call whose Deps omits either with a wrapped error.

type Factory

type Factory func(cfg ConfigSnapshot, deps Deps) (MemoryStore, error)

Factory builds a `MemoryStore` from a `ConfigSnapshot` + `Deps`. Drivers expose one `Factory` each via `init()` → `Register`.

type Health

type Health string

Health enumerates the memory subsystem health states.

only produces `HealthHealthy`. A later phase will drive the full FSM (`healthy → retry → degraded → recovering → healthy`) for `rolling_summary` failures.

const (
	// HealthHealthy — operating normally.
	HealthHealthy Health = "healthy"
	// HealthRetry — last summarisation attempt failed; will retry
	// next opportunity. Reserved for a later phase.
	HealthRetry Health = "retry"
	// HealthDegraded — retry budget exhausted; falling back to
	// truncation semantics and queueing recovery. Reserved for
	HealthDegraded Health = "degraded"
	// HealthRecovering — recovery loop is running; will return to
	// healthy on success. Reserved for a later phase.
	HealthRecovering Health = "recovering"
)

Health values.

type HealthChangedPayload

type HealthChangedPayload struct {
	events.SafeSealed
	PriorHealth Health
	NewHealth   Health
	Reason      string
}

HealthChangedPayload reports a `Health` FSM transition. SafePayload by construction — `PriorHealth` + `NewHealth` are bounded enumerable strings; `Reason` is a short static string indicating the transition cause ("summarizer_failed", "retries_exhausted", "recovery_loop_drained", etc.). No caller-controlled bytes survive on the payload.

Subscribers MAY admin-scope-filter to fan-in cross-tenant health transitions for fleet-level alerting.

type Inspection added in v1.32.0

type Inspection struct {
	Strategy        Strategy
	Items           []Item
	Summary         string
	RecentTurns     int
	EstimatedTokens int
	Health          Health
}

Inspection is a bounded read of committed execution memory, never another transcript.

type Item added in v1.32.0

type Item = sessionmemory.Item

Item is an immutable, source-keyed projection of committed session memory.

type MemoryIdentityRejectedPayload

type MemoryIdentityRejectedPayload struct {
	events.SafeSealed
	Operation string
	Reason    string
}

MemoryIdentityRejectedPayload reports a missing-identity rejection. SafePayload by construction — both fields are bounded enumerable strings (the operation name + a static reason); no caller-controlled bytes survive on the payload.

`Operation` is the rejected method name ("AddTurn", "GetLLMContext", etc.). `Reason` is a short static string indicating which component was missing ("tenant_id empty" / "user_id empty" / "session_id empty" / "tenant_id and user_id empty", etc.).

The Event's `Identity` field carries whatever the caller supplied (zeroed or partial); the bus's `ValidateEvent` would normally reject empty-triple events, so the bus publisher substitutes the missing components with a `"<missing>"` sentinel so the rejection event itself is bus-publishable. Subscribers MAY admin-scope- filter to fan-in cross-tenant rejections.

type MemoryMutationPayload added in v1.3.0

type MemoryMutationPayload struct {
	events.SafeSealed
	Operation string
	Key       string
}

MemoryMutationPayload is the audit payload for the `memory.item_put` / `memory.item_deleted` events. SafePayload by construction: `Operation` is a bounded enumerable string ("put" / "delete"); `Key` is the deterministic content-addressed turn key (a sha256 prefix), never operator-supplied bytes. The record value text is NEVER carried here — it is caller-controlled and would require redaction; the audit trail records THAT a mutation happened, by key, not its bytes.

type MemoryStore

type MemoryStore interface {
	// Inspect reads the authoritative committed memory projection. It exposes
	// neither active journals nor a second persisted transcript.
	Inspect(ctx context.Context, id identity.Quadruple) (Inspection, error)
	// Put appends an operator note and returns its actual committed key.
	Put(ctx context.Context, id identity.Quadruple, turn ConversationTurn) (string, error)
	// Delete atomically removes the named item and fences derived context.
	Delete(ctx context.Context, id identity.Quadruple, key string) (int, error)

	// Close releases driver resources. Idempotent. After Close,
	// every method returns `ErrStoreClosed`.
	Close(ctx context.Context) error
}

MemoryStore is Harbor's mandatory memory interface. A single surface; every V1 driver (inmem here, sqlite + postgres at Postgres) implements every method. No `Supports*` ceremony per AGENTS.md §4.4.

Identity-mandatory contract:

  • Every method validates the identity `Quadruple` at the boundary. Empty tenant / user / session returns wrapped `ErrIdentityRequired` AND emits one `memory.identity_rejected` event on the bus. Empty `RunID` is accepted (memory is session-scoped).

Concurrent-reuse contract:

  • One instance is safe to share across N concurrent goroutines. Mutable state is internally synchronised; per- call state lives in `ctx` and the supplied `Quadruple`, never on the driver.

func From

func From(ctx context.Context) (MemoryStore, bool)

From returns the `MemoryStore` in ctx and a presence bool. Use when absence is recoverable.

func MustFrom

func MustFrom(ctx context.Context) MemoryStore

MustFrom returns the `MemoryStore` in ctx; panics with `ErrStoreClosed` (used as the sentinel for "no store configured") when none is present. Use in handler/runtime paths where a store is mandatory.

func Open

func Open(_ context.Context, cfg ConfigSnapshot, deps Deps) (MemoryStore, error)

Open returns the `MemoryStore` built by the factory whose name matches `cfg.Driver` (defaults to `DefaultDriver` when empty).

Deps are validated: a missing StateStore or EventBus returns a wrapped error before the factory runs — fail loudly, never silently degrade.

func OpenDriver

func OpenDriver(name string, cfg ConfigSnapshot, deps Deps) (MemoryStore, error)

OpenDriver opens a specific driver by name; useful for tests that want to exercise the registry against a non-default driver.

type RecoveryDroppedPayload

type RecoveryDroppedPayload struct {
	events.SafeSealed
	Reason string
}

RecoveryDroppedPayload reports a recovery-backlog overflow drop. SafePayload by construction — `Reason` is a short static string ("backlog_overflow"); no caller-controlled bytes.

type Strategy

type Strategy string

Strategy declares the memory shape the store applies.

const (
	// StrategyNone explicitly disables session memory.
	StrategyNone Strategy = "none"
	// StrategyRollingSummary retains cumulative context and recent execution detail.
	StrategyRollingSummary Strategy = "rolling_summary"
)

Strategy values.

Directories

Path Synopsis
Package conformancetest applies one cumulative-memory administrative contract to every driver.
Package conformancetest applies one cumulative-memory administrative contract to every driver.
drivers
inmem
Package inmem provides the inmem driver for cumulative session-memory access.
Package inmem provides the inmem driver for cumulative session-memory access.
postgres
Package postgres provides the postgres driver for cumulative session-memory access.
Package postgres provides the postgres driver for cumulative session-memory access.
sqlite
Package sqlite provides the sqlite driver for cumulative session-memory access.
Package sqlite provides the sqlite driver for cumulative session-memory access.
Administrative memory mutations use the same owner as inspection and execution.
Administrative memory mutations use the same owner as inspection and execution.
Package session owns cumulative short-term execution memory.
Package session owns cumulative short-term execution memory.

Jump to

Keyboard shortcuts

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