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
- Variables
- func EmitHealthChanged(ctx context.Context, bus events.EventBus, id identity.Quadruple, ...) error
- func EmitIdentityRejected(ctx context.Context, bus events.EventBus, q identity.Quadruple, ...) error
- func EmitRecoveryDropped(ctx context.Context, bus events.EventBus, id identity.Quadruple, reason string) error
- func IsSourceReference(id string) bool
- func Register(name string, factory Factory)
- func RegisteredDrivers() []string
- func ResolveSourceReference(ctx context.Context, store MemoryStore, id identity.Quadruple, name string) (artifacts.ArtifactRef, []byte, error)
- func SourceReference(id identity.Quadruple, item Item) (artifacts.ArtifactRef, error)
- func ValidateHealthTransition(prior, next Health) error
- func ValidateIdentity(q identity.Quadruple) error
- func ValidateStrategy(strategy Strategy) error
- func WithStore(ctx context.Context, store MemoryStore) context.Context
- type Access
- type CallerBlockAdmittedPayload
- type ConfigSnapshot
- type ConversationTurn
- type Deps
- type Factory
- type Health
- type HealthChangedPayload
- type Inspection
- type Item
- type MemoryIdentityRejectedPayload
- type MemoryMutationPayload
- type MemoryStore
- type RecoveryDroppedPayload
- type Strategy
Constants ¶
const DefaultDriver = "inmem"
DefaultDriver is the production driver name. The SQL drivers (SQLite + Postgres) registers additional names.
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.
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).
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`.
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.
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.
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 ¶
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
IsSourceReference identifies the reserved, source-bound memory reference namespace. It must never fall through to ordinary blob lookup or presigning.
func Register ¶
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
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 ¶
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 ¶
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
ValidateStrategy rejects removed and unknown memory strategies at construction. An omitted strategy selects cumulative rolling memory, like the YAML default.
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
Delete invalidates affected cumulative context through the same owner as execution.
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 ¶
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.
Source Files
¶
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. |