/** * Memory event payload types — wire format for `memory-delta-v1.md`. * * The event-log substrate stores every motebit state change as an * `EventLogEntry` with a free-form `payload: Record`. * That's correct for the storage layer, but too loose for the wire * contract that crosses devices during sync and crosses federation peers * once federation ships. This file pins the payload shape per event * type for every memory-shaped event that the `@motebit/memory-graph` * package emits. * * Every type named here is exported from `@motebit/protocol` and * referenced by a `### X.Y — Name` section under a `#### Wire format * (foundation law)` block in `spec/memory-delta-v1.md`, so * `check-spec-coverage` (invariant #9) keeps the spec and the types in * lockstep. The implementing package declaration lives in * `packages/memory-graph/package.json`'s `motebit.implements` array, * enforced by `check-spec-impl-coverage` (invariant #31). * * Sensitivity handling is a wire-layer concern. Payloads that may * carry sensitive user content (today: `MemoryFormedPayload`) MUST * include `sensitivity`; relays MAY redact content above a consented * threshold before forwarding, replacing `content` with the sentinel * string "[REDACTED]" and flipping `redacted: true`. The reference * implementation in `services/relay/src/sync-routes.ts:redactSensitiveEvents` * is conformant. */ import type { SensitivityLevel } from "./index.js"; import type { MemorySource } from "./memory-source.js"; /** Reserved — declared in EventType for forward compatibility, no * emitter today. Consumers must accept events of this type but MUST * NOT assume a payload shape until this spec adds one. */ export type MemoryDecayedPayload = Record; /** Emitted when a new memory node is formed. Carries content + the * declared sensitivity of that content so downstream sync / relay * layers can apply the redaction policy before the payload crosses * the wire to a peer device. */ export interface MemoryFormedPayload { /** Stable identifier of the new memory node. */ readonly node_id: string; /** Textual content. MAY be replaced with "[REDACTED]" over the wire * when `sensitivity` exceeds the sync policy's threshold. */ readonly content: string; /** Classification used by relays and sync engines to decide whether * to forward the content or strip it. */ readonly sensitivity: SensitivityLevel; /** Provenance — who contributed the remembered fact. Emitter-authored * by the FORMING CODE PATH (never the model, never a peer's * self-declaration; `docs/doctrine/memory-provenance.md`). Absent ⇒ * formed before provenance tracking; consumers validate inbound * values with `isMemorySource` and degrade unknown vocabulary to * absent — never to a trusted tier, never rejecting the event. */ readonly source?: MemorySource; /** Present only on the wire when content has been redacted by the * sync path. Absent on the emitter's original event. */ readonly redacted?: true; /** Original sensitivity level retained after redaction so receiving * peers can still honor policy without seeing the content. */ readonly redacted_sensitivity?: SensitivityLevel; /** Discriminates WHY `content` is the `"[REDACTED]"` sentinel. * Absent ⇒ sync-forwarder *sensitivity* redaction (`redacted: true` + * `redacted_sensitivity`): the original still exists on the emitter and * MAY be re-requested. `"deleted"` ⇒ a *deletion tombstone* propagated * by the forget path (`EventStoreAdapter.redactMemoryContent`): the * content is gone for good and a conforming consumer MUST NOT re-form a * node from it. The two mechanisms both blank `content`; this field is * the only thing that tells them apart. `docs/doctrine/retention-policy.md`. */ readonly redacted_reason?: "deleted"; /** Validity-time start (Unix ms) — when the asserted fact became true * in the world, which MAY predate the recording timestamp (backdated * memory). Absent ⇒ the wrapping event's recording time. Bi-temporal * validity, spec/memory-delta-v1.md §3.5. */ readonly valid_from?: number; /** Validity-time end (Unix ms), or `null` for an open interval (still * true). A later `memory_consolidated` supersession closes it. */ readonly valid_until?: number | null; } /** Emitted when an existing memory node is read for recall, reflection, * or audit. Used by consolidation and housekeeping to track which * nodes are "live" vs "cold". */ export interface MemoryAccessedPayload { readonly node_id: string; } /** Emitted when a memory node is pinned or unpinned. Pinned nodes are * exempt from decay-based pruning and from phantom-certainty flagging * in the reflection audit. */ export interface MemoryPinnedPayload { readonly node_id: string; /** `true` when the node is now pinned, `false` when unpinned. */ readonly pinned: boolean; } /** Emitted when a memory node is deleted — either explicitly by the * user, by housekeeping after decay drops below the prune threshold, * or as the subsumed half of a consolidation. The event is * append-only; storage does NOT remove the original `memory_formed` * event because the history is the ledger. */ export interface MemoryDeletedPayload { readonly node_id: string; } /** Emitted when consolidation merges, supersedes, or rejects a * candidate memory. The action taxonomy below mirrors * `@motebit/memory-graph/consolidation.ts`'s `ConsolidationDecision`. */ export interface MemoryConsolidatedPayload { /** The action taken by the consolidation decision. */ readonly action: "merge" | "supersede" | "reject" | "accept"; /** When `action === "merge"` or `"supersede"`, the id of the node * being merged into / superseded. Null otherwise. */ readonly existing_node_id: string | null; /** When `action === "accept"` or `"merge"`, the id of the newly-formed * node. Null on reject/supersede-in-place. */ readonly new_node_id: string | null; /** Human-readable rationale from the consolidation decider. Free * text; consumers MUST NOT parse it semantically. */ readonly reason: string; /** Present on `"supersede"` actions: the validity-time (Unix ms) at * which the superseded (`existing_node_id`) belief ceased to hold. A * conforming consumer sets `valid_until` on that node to this value. * Bi-temporal validity, spec/memory-delta-v1.md §3.5. */ readonly superseded_valid_until?: number | null; } /** Emitted when the ai-core loop detects sensitivity patterns in a * user turn that should have been tagged. The event carries a * bounded slice of the turn text so reflection can revisit the * missed tagging later. Distinct from the reflection audit's * phantom/conflict/near-death categorization — that is a compute, * not an event. */ export interface MemoryAuditPayload { /** Memory-worthy pattern labels the ai-core heuristic detected in the * turn but the model didn't tag — each is a `