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

protocol

package
v1.31.8 Latest Latest
Warning

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

Go to latest
Published: Sep 14, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

Documentation

Overview

Package protocol — additions: the strategy-trace read projection + the admin-gated mutation pair (`memory.put` / `memory.delete`). Like List / Get / Health these are stateless pure functions — every dependency is passed in per call. They compose ONLY the shipped `memory.MemoryStore` interface (Snapshot / Restore / AddTurn / GetLLMContext / Health) — no new driver-seam method, so all three V1 drivers (inmem / sqlite / postgres) back them with zero per-driver work.

strategy_trace is an honest read

`StrategyTrace` projects the strategy's LIVE `GetLLMContext` + `Health` output — the rolling-summary text the strategy injects, the verbatim-turn count it keeps, the token estimate, and the health state. It is NOT a fabricated per-step "selection with rejections" (the rolling_summary strategy summarises; it does not select-and-reject candidates). An empty session projects an empty trace (CLAUDE.md §13 — no synthesised data).

The mutations are read-modify-write on the shipped interface

`Put` appends a turn via `AddTurn`. `Delete` removes ONE turn by key via a `Snapshot` → drop-the-keyed-turn → `Restore` round-trip — the Record envelope (incl. the rolling summary) is preserved losslessly (the `memory.Record.Summary` field added in 108n). Both emit an audit event on the bus (`memory.item_put` / `memory.item_deleted`, SafePayload — the hashed key only, never the turn text). Admin-gating lives at the handler edge; the service trusts a gated, identity-validated call.

Package protocol composes the read-only Console-memory-page Protocol surface on top of the shipped memory subsystem.

It exposes three pure functions — List, Get, Health — that the Protocol stream-transport handlers (`internal/protocol/transports/ stream/memory_handler.go`) call to answer the `memory.list` / `memory.get` / `memory.health` methods. The functions are stateless: every dependency is passed in per call, nothing is cached on a package-level value, and the compiled artifacts they consume (MemoryStore, ArtifactStore, the events Aggregator) are themselves safe for concurrent reuse.

The projection model

The shipped `memory.MemoryStore` interface is per-identity: it has no per-item enumeration method. It exposes `Snapshot(ctx, id)` — an opaque JSON `memory.Record{Strategy, Turns}` — and `Health(ctx, id)`. This package projects that record into the Console-page row shape: each conversation turn in a snapshot becomes one `MemoryItem` row, keyed by a deterministic per-turn key (`memTurnKey`). The rolling-summary text, when present, is NOT a separate row — it is folded into the strategy metadata. This is the honest projection: the runtime's memory state is conversation turns, and the Memory page renders them per-identity.

Identity is mandatory

Every function validates the identity quadruple before touching the store. A missing tenant / user / session fails loudly with `memory.ErrIdentityRequired`; the caller (the stream handler) maps that onto the canonical `CodeIdentityRequired` Protocol error. The driver layer ALSO emits a `memory.identity_rejected` event on the bus — this package does not re-emit; it relies on the shipped driver-layer emit and never masks the rejection.

Heavy values bypass via artifacts

`Get` mirrors the LLM-edge enforcement pass (`internal/llm/safety.go`): a record value whose byte length meets or exceeds the configured heavy-content threshold is routed through the ArtifactStore and the detail ships a `MemoryArtifactRef` instead of inline bytes. A driver that hands back raw heavy bytes that this package would otherwise inline is a leak — `Get` fails loudly with `ErrContextLeak` rather than inlining (the same posture the LLM edge takes).

No mutation surface

V1 is read-only. `memory.put` / `memory.delete` are deferred to post-V1 (page-memory.md §10); this package ships no mutation path.

Index

Constants

This section is empty.

Variables

View Source
var ErrContextLeak = errors.New("memory/protocol: heavy memory value reached the response path as raw inline bytes")

ErrContextLeak — Get materialised a memory record value whose byte length meets or exceeds the heavy-content threshold but the value reached the response path as raw inline bytes rather than an ArtifactStub. Mirrors `llm.ErrContextLeak` (CLAUDE.md §13): a heavy value MUST route through the ArtifactStore by reference; an inline heavy value is a leak and is failed loudly, never truncated.

View Source
var ErrInvalidFilter = errors.New("memory/protocol: invalid memory.list filter")

ErrInvalidFilter — a memory.list filter carried a structurally invalid value: an unknown scope / driver / strategy enum, or a negative page / page-size. The caller maps this onto `CodeInvalidRequest`. Fails loudly — a malformed filter is never a silently-dropped facet (CLAUDE.md §13).

View Source
var ErrPageOutOfRange = errors.New("memory/protocol: page/page_size out of range")

ErrPageOutOfRange — a memory.list request asked for a page-size above the documented maximum (or a negative page / page-size). Distinct from ErrInvalidFilter so the caller can render a precise message; both map onto `CodeInvalidRequest`.

Functions

func Delete added in v1.3.0

Delete evicts ONE conversation turn (by key) from the caller's session memory via a `Snapshot` → drop-the-keyed-turn → `Restore` read-modify- write. The Record envelope — including the rolling-summary text — is preserved losslessly (`memory.Record.Summary`). A key that matches no turn fails loudly with `memory.ErrNotFound` (never a silent no-op). Emits the `memory.item_deleted` audit event on success.

func Get

Get answers the `memory.get` Protocol method: it resolves a single memory record by key within the caller's identity scope and returns the full detail — metadata + post-redaction value (below the heavy-content threshold) OR a `MemoryArtifactRef` (at or above it).

Identity is mandatory. The heavy-value bypass is enforced: a record value at or above HeavyThreshold is routed through the ArtifactStore and the detail ships `ValueArtifact`; the inline `Value` is left empty. EXACTLY ONE of Value / ValueArtifact is populated. A value that somehow reached the inline path while being heavy is a leak — Get fails loudly with `ErrContextLeak` rather than inlining it (mirrors the LLM-edge enforcement in `internal/llm/safety.go`).

A key that resolves to no record returns `memory.ErrNotFound` — the caller maps it onto `CodeNotFound`.

func Health

Health answers the `memory.health` Protocol method: it returns the aggregate memory-health counters (total records / expiring-in-1h / identity-rejected-24h / recovery-dropped-24h) plus the per-scope driver mapping.

Identity is mandatory: an incomplete triple on id fails loudly with `memory.ErrIdentityRequired`. The record counters derive from the caller's per-identity snapshot; the 24-hour event counters derive from the events Aggregator (when wired); the driver mapping derives from the configured per-scope driver split.

func List

List answers the `memory.list` Protocol method: it projects the caller's per-identity memory snapshot into the Console-page row shape, applies the request's facet filters, paginates, and attaches the aggregate counters.

Identity is mandatory: an incomplete triple on id fails loudly with `memory.ErrIdentityRequired`. The cross-tenant scope gate is the caller's job (the stream handler checks `auth.HasScope` before calling List) — by the time List runs the request is authorised; List filters strictly within the supplied identities.

The filter's facets are validated up front: an unknown scope / driver / strategy enum, or a page / page-size out of range, fails loudly (ErrInvalidFilter / ErrPageOutOfRange) — never a silently dropped facet (CLAUDE.md §13).

func Put added in v1.3.0

Put appends an operator-supplied conversation turn to the caller's session memory via the shipped `AddTurn`, then emits the `memory.item_put` audit event. Returns the deterministic key of the appended turn (the value a subsequent `memory.get` resolves). Identity is validated before the store is touched; admin-gating is the handler's job.

func StrategyTrace added in v1.3.0

StrategyTrace projects how the configured memory strategy is compacting the caller's session memory right now — the strategy's live `GetLLMContext` (rolling summary + verbatim turns + token estimate) plus `Health`. Identity is validated before the store is touched; a missing triple fails loudly with `memory.ErrIdentityRequired`.

Types

type DeleteDeps added in v1.3.0

type DeleteDeps struct {
	// Store is the memory store the turn is evicted from. Mandatory.
	Store memory.MemoryStore
	// Bus is the events bus the audit event publishes on. Optional.
	Bus events.EventBus
}

DeleteDeps carries the Delete dependencies.

type GetDeps

type GetDeps struct {
	// Store is the memory subsystem the snapshot is projected from.
	Store memory.MemoryStore
	// Artifacts is the ArtifactStore heavy values (≥ HeavyThreshold)
	// are routed through. Mandatory — a nil fails loud.
	Artifacts artifacts.ArtifactStore
	// DriverName is the configured memory-driver name surfaced on the
	// returned row.
	DriverName string
	// HeavyThreshold is the inline-payload bound in bytes — the runtime
	// supplies the pinned Console inline-payload bound, NOT the
	// operator's LLM-context `artifacts.heavy_output_threshold_bytes`,
	// because the selected arm of this reply is Protocol-visible. A
	// value whose byte length meets or exceeds it routes through the
	// ArtifactStore. A non-positive threshold fails loud (a zero
	// threshold would route every value).
	HeavyThreshold int
}

GetDeps carries the dependencies Get composes over.

type HealthDeps

type HealthDeps struct {
	// Store is the memory subsystem the snapshot + health are read
	// from.
	Store memory.MemoryStore
	// Aggregator is the events Aggregator the 24-hour counters derive
	// from. Optional — see ListDeps.Aggregator.
	Aggregator *events.Aggregator
	// DriverByScope is the configured per-scope driver mapping —
	// e.g. {"session":"inmem", "tenant":"postgres"}. The caller
	// supplies it from config; the MemoryStore interface does not
	// expose a per-scope driver split. When unset, Health reports a
	// single-scope mapping for the session scope under DriverName.
	DriverByScope map[string]string
	// DriverName is the configured memory-driver name, used to seed a
	// single-scope DriverByScope mapping when DriverByScope is unset.
	DriverName string
}

HealthDeps carries the dependencies Health composes over.

type ListDeps

type ListDeps struct {
	// Store is the memory subsystem the snapshot is projected from.
	Store memory.MemoryStore
	// Aggregator is the events Aggregator the 24-hour counters derive
	// from. Optional — when nil, the IdentityRejected24h /
	// RecoveryDropped24h counters are reported as 0 (the page still
	// renders; the right-rail cards subscribe to the live event stream
	// for the real-time view). The driver-comparison + record counters
	// do not depend on it.
	Aggregator *events.Aggregator
	// DriverName is the configured memory-driver name surfaced on each
	// row (`inmem` / `sqlite` / `postgres`). The MemoryStore interface
	// does not expose it; the caller supplies it from config.
	DriverName string
	// HeavyThreshold is the inline-payload bound in bytes — the runtime
	// supplies the pinned Console inline-payload bound, NOT the
	// operator's LLM-context `artifacts.heavy_output_threshold_bytes`.
	// It is the single classification point for the per-row
	// HeavyContent flag; `memory.list` and `memory.get` MUST agree, so
	// both read the same threshold. A zero / non-positive value disables the flag (no row
	// is reported heavy) — the list still renders.
	HeavyThreshold int
}

ListDeps carries the dependencies List composes over. All are validated at the call site (the stream handler) — a nil Store fails loud rather than nil-panicking mid-projection.

type PutDeps added in v1.3.0

type PutDeps struct {
	// Store is the memory store the turn is appended to. Mandatory.
	Store memory.MemoryStore
	// Bus is the events bus the audit event publishes on. Optional — a
	// nil bus skips the emit (test wiring); production always supplies it.
	Bus events.EventBus
}

PutDeps carries the Put dependencies.

type StrategyTraceDeps added in v1.3.0

type StrategyTraceDeps struct {
	// Store is the memory store the trace projects from. Mandatory.
	Store memory.MemoryStore
}

StrategyTraceDeps carries the StrategyTrace dependencies.

Jump to

Keyboard shortcuts

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