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
- 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 Register(name string, factory Factory)
- func RegisteredDrivers() []string
- func ValidateHealthTransition(prior, next Health) error
- func ValidateIdentity(q identity.Quadruple) error
- func WithStore(ctx context.Context, store MemoryStore) context.Context
- type ConfigSnapshot
- type ConversationTurn
- type Deps
- type Factory
- type Health
- type HealthChangedPayload
- type LLMContextPatch
- type MemoryIdentityRejectedPayload
- type MemoryMutationPayload
- type MemoryStore
- type OverflowPolicy
- type Record
- type RecoveryDroppedPayload
- type Snapshot
- type Strategy
- type SummarizeRequest
- type SummarizeResponse
- type Summarizer
- type TrajectoryDigest
Constants ¶
const DefaultDriver = "inmem"
DefaultDriver is the Phase 23 production driver name. Phase 25 (SQLite + Postgres) registers additional names.
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).
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`.
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.
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.
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).
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 ¶
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 ¶
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 ¶
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.
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 ¶
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.
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.
Source Files
¶
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. |