Skip to content

Memory — Log + Facts

@kindgi/specs/memory.schema.json, schema version 1.3.0.

Two memory primitives: LogEntry (append-only event stream — turns, tool calls, tool results, agent internal messages) and Fact (typed, versioned records with declared retrieval hints). Views like message history, working memory, and semantic recall are queries over these two. Everything is tenant-scoped; scope is explicit and pluggable (tenant / user / org / project / thread / session). Log entries form a tamper-evident hash chain per (tenantId, runId) so any modification cascades — see LogEntry.prevHash / entryHash.

Type: LogEntry or Fact

Every memory item is scoped. Reads and writes must present a scope; the policy engine denies out-of-scope operations at the boundary.

  • tenantId (string, required)
  • userId (string)
  • orgId (string)
  • projectId (string): Project scope — whatever unit of work a pack organizes its memory around; the memory layer stays neutral about what it represents.
  • threadId (string)
  • sessionId (string)

One event in the append-only log for a run/thread/session. Immutable once written. Ordered by (sequence, timestamp). Every entry commits to a hash of the previous entry in the same (tenantId, runId) — tamper with any past entry and every subsequent entry's prevHash breaks.

  • id (string, required)
  • kind ("user-message" | "agent-message" | "system-message" | "tool-call" | "tool-result" | "internal-thought" | "retrieval" | "artifact-produced" | "event-emitted" | "event-received" | "guardrail-triggered" | "wait-suspended" | "wait-resumed", required)
  • scope (Scope, required)
  • runId (string, required): Kernel run that produced this entry.
  • actor (string): Who produced this entry (user id, agent id, tool id, or 'system').
  • timestamp (string (date-time), required)
  • sequence (integer, required): Monotonic sequence within (tenantId, runId). Enables deterministic replay ordering. Together with prevHash defines the tamper-evident chain within a run.
  • payload (object): Kind-specific payload. Consumers must tolerate unknown fields.
  • contentHash (string): Optional SHA-256 of the payload alone (independent of the chain), useful for content-addressed dedup.
  • prevHash (string, required): SHA-256 of the previous LogEntry in the same (tenantId, runId) chain. The first entry (sequence=0) uses the zero-hash 'sha256:0000000000000000000000000000000000000000000000000000000000000000'. Added in schema-version 1.2.0.
  • entryHash (string, required): SHA-256 of this entry's canonical serialization (id + kind + scope + runId + actor + timestamp + sequence + payload + prevHash). Used as the prevHash of the next entry in the chain. Added in schema-version 1.2.0.
  • causedByLogId (string): Optional link to the log entry that caused this one. Feeds the provenance DAG directly.

A typed, versioned record. Facts are the durable, retrievable memory. Two flavors are expressible via the same primitive: (A) immutable outputs the runtime wrote from an agent run — versioned by supersession, no freshness semantics; (B) cached views of external state — carry an optional source block describing where the ground truth lives and how to refresh. Views (working memory, semantic recall, cross-history search) are queries over facts. Retrieval strategy (which indexes populate for a given type) is declared by pack authors via a policy registry, not per-instance — see RetrievalPolicyRegistry in @kindgi/memory. Schema-version 1.3.0 removed the per-instance retrieve hint that used to live on this record.

  • id (string, required)
  • type (string, required): Fact type identifier. Vertical packs define their own; a few general-purpose names are conventional ('working-memory', 'user-profile', 'summary', 'entity-index').
  • scope (Scope, required)
  • version (integer, required): Monotonic version within (scope, id). New writes increment; old versions are retained until retention policy expires them.
  • createdAt (string (date-time), required)
  • updatedAt (string (date-time))
  • content (object): The fact payload — inline JSON. Optional since schema-version 1.1.0: if contentRef is set, the payload lives in blob storage and content may hold a summary or fingerprint for cheap retrieval-scoring. Rule of thumb: keep content ≤ ~64 KB inline; larger goes to blob.
  • contentRef (string): Optional (schema-version 1.1.0+): reference to a blob holding the actual bytes when they're too large to store inline. Format: 'blob://<provider>/<bucket>/<key>'. Consumers fetch it through the blob storage binding (BlobStorageBinding.get() in @kindgi/blob-binding).
  • contentHash (string)
  • size (integer): Optional (schema-version 1.1.0+): payload size in bytes. Populated by the writer; enables cheap size-based retrieval decisions without fetching the payload.
  • embeddingModel (string): For facts whose type-policy includes semantic indexing: the model used to embed. Enables re-indexing when the model changes.
  • retention (Retention)
  • source (Source): Optional (schema-version 1.1.0+): present iff this fact is a cached view of external state (Kind B) rather than immutable runtime output (Kind A). Carries freshness metadata and a refresh strategy so readers can distinguish live-cached facts from historical facts.
  • causedByLogId (array of string): Log entries that caused this fact write, in temporal order. Every fact traces back to at least one causal event. Multi-source facts (consolidation, extraction over many messages, cross-fact synthesis) list all source log ids; single-source facts list one. Widened from string → string[] in schema-version 1.2.0.
  • supersedes (string): The previous fact id/version this one supersedes.

External source of a Kind-B (cached-view) fact. Added in schema-version 1.1.0. Absent for Kind-A (immutable historical) facts.

  • kind ("http-api" | "blob" | "mcp-tool" | "external-db" | "user-input", required): Where the source of truth lives. Opaque to the memory layer; the refresh.handler knows how to fetch from it.
  • uri (string): Optional locator interpreted per kind (URL, blob ref, MCP tool id + args, JDBC URI, etc.). May be absent when the handler resolves the source internally.
  • freshness (object, required): How to tell if the cached fact is still current.
    • ttlSeconds (integer): Fact is considered stale after this many seconds past lastVerifiedAt.
    • lastVerifiedAt (string (date-time)): When the source was last confirmed to still match the cached value. Updated by refresh handlers.
    • etag (string): HTTP-style validator, if the source supports one. Refresh handlers use conditional GET semantics.
    • sourceVersion (string): Explicit source-side version identifier (e.g., document revision id, DB row transaction id). Present when the source is versioned.
  • refresh (object, required): How the cached fact gets refreshed.
    • strategy ("on-read" | "background" | "manual", required): 'on-read' = reader triggers a refresh check before returning stale results. 'background' = a scheduled kernel run walks stale facts and refreshes. 'manual' = only refresh when explicitly invoked.
    • handler (string): Tool id, agent id, or flow id that performs the refresh. Invoked by the kernel per the chosen strategy.
    • priority (integer): Higher = refresh first when a background sweep dispatches many at once.

Fact-level retention override. Tenant policy sets defaults.

  • keepUntil (string (date-time))
  • keepDays (integer)
  • legalHold (boolean): If true, retention rules cannot delete this fact. Requires explicit release via admin action.