memory

package
v1.3.1 Latest Latest
Warning

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

Go to latest
Published: Jun 11, 2026 License: Apache-2.0 Imports: 11 Imported by: 0

Documentation

Overview

Package memory owns Harbor's declared-policy, identity-scoped, pluggable memory subsystem.

Phase 23 lands the leaf surface:

  • The single mandatory `MemoryStore` interface every backend (inmem here, sqlite + postgres at Phase 25) implements.
  • The shared types — `Strategy`, `Health`, `ConversationTurn`, `TrajectoryDigest`, `LLMContextPatch`, `Snapshot`.
  • Sentinel errors compared via `errors.Is`.
  • The §4.4 extensibility-seam plumbing (registry + factory).
  • Ctx helpers (`WithStore` / `MustFrom` / `From`).

The interface owns the typed shape (D-027); drivers persist opaque bytes through `state.StateStore` via the typed wrapper pattern. Memory records key on `(identity.Quadruple, Kind= "memory.state")` — sessions own the wrapper layer of session records, memory owns its own.

Identity is mandatory at every method (D-001). The triple `(tenant, user, session)` MUST be fully populated; empty `RunID` is accepted (memory is session-scoped, not run-scoped, mirroring Phase 07's `state.StateStore` rule). Missing-triple operations fail closed with `ErrIdentityRequired` AND emit a `memory.identity_rejected` event on the configured `events.EventBus` so the rejection is observable — never silent (brief 04 §4.2 + AGENTS.md §5 "Fail loudly").

Phase 23 ships `Strategy = StrategyNone` only:

  • `AddTurn` is a no-op.
  • `GetLLMContext` returns an empty patch.
  • `EstimateTokens` returns 0.
  • `Flush` is a no-op.
  • `Health` returns `HealthHealthy`.
  • `Snapshot` returns an empty snapshot.
  • `Restore` accepts only an empty snapshot; non-empty is `ErrInvalidSnapshot`.

Phase 24 will activate `StrategyTruncation` and `StrategyRollingSummary`; Phase 25 will add the SQLite + Postgres drivers under the same conformance suite.

Index

Constants

View Source
const DefaultDriver = "inmem"

DefaultDriver is the Phase 23 production driver name. Phase 25 (SQLite + Postgres) registers additional names.

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. Phase 24, RFC §6.6, D-035.

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 (D-001 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 (Phase 108n / D-186). 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 (Phase 108n / D-186). 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 D-035 (bounded recovery loop).

View Source
const KindMemoryState = "memory.state"

KindMemoryState is the canonical record-kind string Harbor uses to route memory state in the persistence layer. Since Phase 25a (D-174) every driver — InMem, SQLite, Postgres — persists this kind through the injected `state.StateStore` via the shared strategy executor; the SQL drivers no longer write a private `memory_state` table on the strategy path. The constant is exported so cross-driver tests and operators can reference it.

Variables

View Source
var (
	// ErrNotFound — a load-by-key style lookup found nothing.
	// Phase 23 returns this when `Snapshot` is asked for a slot
	// the StateStore wrapper layer has never written.
	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 per D-001 + brief 04 §4.2.
	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 — `Open` (or a driver) was asked for
	// an UNKNOWN strategy name. The three canonical strategies
	// (`none` / `truncation` / `rolling_summary`) are implemented on
	// every driver via the shared strategy executor (Phase 25a / D-174);
	// this sentinel now guards an unrecognised strategy string, not a
	// phase gap. (The error text is preserved for callers that match it.)
	ErrStrategyNotImplemented = errors.New("memory: strategy not implemented at this phase")

	// ErrInvalidSnapshot — `Restore` was called with a snapshot
	// whose Strategy mismatches the driver's, or with non-empty
	// bytes against a `StrategyNone` driver. Fail loudly; never
	// silently coerce.
	ErrInvalidSnapshot = errors.New("memory: invalid snapshot for this 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 (D-001) — 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 (D-035 bounded recovery loop).

Identity is required (D-001).

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 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 WithStore

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

WithStore attaches the store to ctx for downstream handlers.

Types

type ConfigSnapshot

type ConfigSnapshot struct {
	Driver             string
	DSN                string
	Strategy           Strategy
	BudgetTokens       int
	RecoveryBacklogMax 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 (Phase 25); 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.

`RecoveryBacklogMax` is consumed by the `rolling_summary` strategy executor only; other strategies ignore the field. Default (zero) → strategy.DefaultRecoveryBacklogMax.

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
	TrajectoryDigest    *TrajectoryDigest
	ArtifactsShown      map[string]any
	ArtifactsHiddenRefs []string
	Timestamp           time.Time
}

ConversationTurn is one turn of a memory-tracked conversation. Producers (planner runtime, Phase 42+) hand turns to `AddTurn`.

`ArtifactsShown` / `ArtifactsHiddenRefs` carry the model-visible / model-hidden artifact references for this turn so memory's downstream injection logic can prune or include them per the configured strategy (Phase 24+). Phase 23 round-trips both fields through the Snapshot bytes but applies no strategy logic.

type Deps

type Deps struct {
	State      state.StateStore
	Bus        events.EventBus
	Summarizer Summarizer
}

Deps carries the runtime dependencies a memory driver needs.

The `State` field is mandatory (D-027 — 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.

The `Summarizer` field (Phase 25a, D-174) is the injectable LLM-edge callable the `rolling_summary` strategy consumes. It is OPTIONAL — required only when `cfg.Strategy == StrategyRollingSummary`, ignored by `none` / `truncation`. The registry routes it into the driver factory, which threads it into the strategy executor. A `rolling_summary` config without a `Summarizer` fails loudly at `Open` (mirroring `strategy.New`'s rejection) — never a stub fallback (AGENTS.md §13). Existing callers that construct `Deps{State, Bus}` keep compiling: the zero value is nil, valid for the non-summarising strategies.

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.

Phase 23 only produces `HealthHealthy`. Phase 24 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 Phase 24.
	HealthRetry Health = "retry"
	// HealthDegraded — retry budget exhausted; falling back to
	// truncation semantics and queueing recovery. Reserved for
	// Phase 24.
	HealthDegraded Health = "degraded"
	// HealthRecovering — recovery loop is running; will return to
	// healthy on success. Reserved for Phase 24.
	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 LLMContextPatch

type LLMContextPatch struct {
	Strategy    Strategy
	Summary     string
	RecentTurns []ConversationTurn
	Tokens      int
}

LLMContextPatch is the output `GetLLMContext` returns: the patch a planner runtime applies to its LLM call.

Strategy=none returns an empty patch; later strategies return a rolling summary text, ordered recent turns, and a token estimate the planner can compare against its context-window budget.

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 (Phase 108n / D-186). 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 {
	// AddTurn appends a conversation turn to the memory tracked
	// for `id`. Strategy=none is a no-op (returns nil); other
	// strategies will apply their shape logic (Phase 24+).
	AddTurn(ctx context.Context, id identity.Quadruple, turn ConversationTurn) error

	// GetLLMContext returns the patch a planner runtime applies to
	// its LLM call. Strategy=none returns the zero value of
	// `LLMContextPatch`.
	GetLLMContext(ctx context.Context, id identity.Quadruple) (LLMContextPatch, error)

	// EstimateTokens returns the token-estimate for the memory
	// payload `GetLLMContext` would inject right now. Strategy=
	// none returns 0.
	EstimateTokens(ctx context.Context, id identity.Quadruple) (int, error)

	// Flush drops every in-flight turn and resets the memory for
	// `id` to a clean state. Strategy=none is a no-op.
	Flush(ctx context.Context, id identity.Quadruple) error

	// Health reports the current health state for `id`'s memory.
	// Strategy=none always reports `HealthHealthy`.
	Health(ctx context.Context, id identity.Quadruple) (Health, error)

	// Snapshot exports a portable snapshot of `id`'s memory state.
	// Strategy=none returns an empty `Snapshot{Strategy: StrategyNone}`.
	Snapshot(ctx context.Context, id identity.Quadruple) (Snapshot, error)

	// Restore imports a previously-captured `Snapshot`. The
	// Snapshot's Strategy MUST match the driver's configured
	// Strategy; mismatched strategies (e.g. restoring a
	// `truncation` snapshot into a `none` store) returns
	// `ErrInvalidSnapshot`. Strategy=none accepts only empty
	// snapshots; non-empty Bytes returns `ErrInvalidSnapshot`.
	Restore(ctx context.Context, id identity.Quadruple, snap Snapshot) 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 Phase 25) implements every method. No `Supports*` ceremony per AGENTS.md §4.4.

Identity-mandatory contract (D-001):

  • 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 (D-025):

  • 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 OverflowPolicy

type OverflowPolicy string

OverflowPolicy is the buffer-overflow action a `truncation`-style strategy applies when the recent-window buffer's token total exceeds the configured `BudgetTokens`. Phase 24 ships only `OverflowDropOldest`. See D-035 for the rationale (the brief 04 §2 trio `truncate_oldest | truncate_summary | error` was narrowed to a single safe default; the `error` policy is a silent- degradation footgun and `truncate_summary` conflates strategies).

const (
	// OverflowDropOldest evicts oldest turns until the buffer's
	// token estimate fits within the budget. The only Phase 24
	// policy.
	OverflowDropOldest OverflowPolicy = "drop_oldest"
)

type Record

type Record struct {
	Strategy Strategy           `json:"strategy"`
	Turns    []ConversationTurn `json:"turns,omitempty"`
	// Summary is the rolling-summary text the `rolling_summary` strategy
	// folds compacted turns into. Added in Phase 108n (D-186) so a
	// Snapshot → drop-turn → Restore read-modify-write (the `memory.delete`
	// mutation) round-trips the summary LOSSLESSLY rather than dropping it.
	// The json tag matches the strategy's persisted `memoryStateRecord`
	// (`internal/memory/strategy`), so decoding a real snapshot into this
	// envelope and re-marshalling preserves every field. `omitempty` keeps
	// it byte-compatible with pre-108n records that never wrote a summary.
	Summary string `json:"summary,omitempty"`
}

Record is the JSON envelope every driver persists as the opaque `Snapshot.Bytes` payload. The shape is exported so tests + later driver implementations share a single source of truth (D-034 — wire envelope centralised in the `memory` package for cross-driver byte-stable Snapshot/Restore).

Phase 23 only writes empty records (Strategy=none has no mutations). Phase 24 will populate `Turns` for the truncation + rolling_summary strategies. The struct's JSON tags pin the wire format and MUST NOT change after Phase 23 merged — additive fields are fine (later strategies will append fields), but renaming an existing field would break cross-driver round-trip.

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 Snapshot

type Snapshot struct {
	Strategy Strategy
	Bytes    []byte
}

Snapshot is the export shape for `Snapshot` / `Restore`. The Strategy field round-trips so a `Restore` against a driver configured for a different Strategy fails loudly.

`Bytes` is opaque to callers; only a driver of the same Strategy can `Restore` them. Crossing driver boundaries (e.g. inmem snapshot → sqlite restore) is safe because the bytes are JSON-serialised internal records, not driver-private structures.

func (Snapshot) IsEmpty

func (s Snapshot) IsEmpty() bool

IsEmpty reports whether the snapshot is operationally empty (no strategy + no bytes). Used by `Restore` to accept the trivial-snapshot round-trip under Strategy=none.

type Strategy

type Strategy string

Strategy declares the memory shape the store applies.

Phase 23 ships `StrategyNone` operational. `StrategyTruncation` and `StrategyRollingSummary` are declared so operators can stage their config today; the registry's `Open` rejects them with `ErrStrategyNotImplemented` until Phase 24 lands.

const (
	// StrategyNone is the no-op memory shape. AddTurn is a no-op;
	// GetLLMContext returns an empty patch. Operationally the
	// "memory disabled" mode.
	StrategyNone Strategy = "none"
	// StrategyTruncation keeps a recent-turn window with budget
	// enforcement. Reserved for Phase 24.
	StrategyTruncation Strategy = "truncation"
	// StrategyRollingSummary keeps a recent-turn window plus a
	// background-summarised long-term context. Reserved for Phase 24.
	StrategyRollingSummary Strategy = "rolling_summary"
)

Strategy values.

type SummarizeRequest

type SummarizeRequest struct {
	PreviousSummary string
	Turns           []ConversationTurn
}

SummarizeRequest carries the summariser inputs. `PreviousSummary` is the prior rolling summary (empty on the first turn); `Turns` is the batch of recently-evicted turns to fold into the summary.

type SummarizeResponse

type SummarizeResponse struct {
	Summary string
}

SummarizeResponse carries the summariser output.

type Summarizer

type Summarizer interface {
	Summarize(ctx context.Context, id identity.Quadruple, req SummarizeRequest) (SummarizeResponse, error)
}

Summarizer is the injectable callable the `rolling_summary` strategy consumes. The LLM-backed implementation lands at Phase 32+; Phase 24 ships only the interface and a test-grade stub (`EchoSummarizer`, exported from `internal/memory/strategy`).

The interface intentionally mirrors brief 04 §4.1's "input `{previous_summary, turns}`, output `{summary: string}`" with a Go-idiomatic `(ctx, identity, req)` shape so the LLM-client integration phase doesn't have to invent a fresh shape.

Concurrent-reuse contract (D-025): one `Summarizer` instance is safe to share across N concurrent goroutines. Implementers MUST honour `ctx.Done()`; the executor cancels in-flight summaries on `Close`.

type TrajectoryDigest

type TrajectoryDigest struct {
	ToolsInvoked        []string
	ObservationsSummary string
	ReasoningSummary    string
	ArtifactsRefs       []string
}

TrajectoryDigest is the compact planner-side trace snapshot the memory subsystem MAY persist alongside the turn. Phase 23 does not ingest it (Strategy=none); the type ships now so Phase 24 and downstream planner phases share one definition.

Directories

Path Synopsis
Package conformancetest exposes the canonical correctness suite every `memory.MemoryStore` driver must pass.
Package conformancetest exposes the canonical correctness suite every `memory.MemoryStore` driver must pass.
drivers
inmem
Package inmem is Harbor's V1 in-memory MemoryStore driver.
Package inmem is Harbor's V1 in-memory MemoryStore driver.
postgres
Package postgres is Harbor's Postgres-backed `memory.MemoryStore` driver.
Package postgres is Harbor's Postgres-backed `memory.MemoryStore` driver.
sqlite
Package sqlite is Harbor's SQLite-backed `memory.MemoryStore` driver.
Package sqlite is Harbor's SQLite-backed `memory.MemoryStore` driver.
Package protocol — Phase 108n (D-186) additions: the strategy-trace read projection + the admin-gated mutation pair (`memory.put` / `memory.delete`).
Package protocol — Phase 108n (D-186) additions: the strategy-trace read projection + the admin-gated mutation pair (`memory.put` / `memory.delete`).
Package strategy holds the memory-strategy executors: the algorithmic core that any `memory.MemoryStore` driver delegates to.
Package strategy holds the memory-strategy executors: the algorithmic core that any `memory.MemoryStore` driver delegates to.

Jump to

Keyboard shortcuts

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