protocol

package
v1.1.6 Latest Latest
Warning

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

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

Documentation

Overview

Package protocol composes the read-only Console-memory-page Protocol surface (Phase 73j / D-118) 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 D-025-safe.

The projection model

The shipped `memory.MemoryStore` interface (Phases 23–25) 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 (D-001 / D-033)

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 (D-033) — this package does not re-emit; it relies on the shipped driver-layer emit and never masks the rejection.

Heavy values bypass via artifacts (D-026)

`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 Phase 73 / 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 (D-026)")

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` (D-026 / 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 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 (D-001). The heavy-value bypass (D-026) 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 (D-001): 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 (D-001): 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).

Types

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 (D-026). 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 configured heavy-content byte size
	// (cfg.Artifacts.HeavyOutputThresholdBytes). 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 configured heavy-content byte size
	// (cfg.Artifacts.HeavyOutputThresholdBytes). It is the single
	// classification point for the per-row HeavyContent flag (D-026);
	// `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.

Jump to

Keyboard shortcuts

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