Memory

The memories table stores persistent AI memory blobs that the chat agent reads before composing a response. Each row holds a single free-text memory string scoped to either a workspace (type = 'workspace') or to a specific workspace member (type = 'user'). The entity is exclusively written by the AI Memory pipeline (MemoryService) via a fire-and-forget Cloud Task dispatcher; no user-facing PATCH route exists. Uniqueness constraints guarantee at most one workspace-scoped memory per workspace and at most one user-scoped memory per membership.

NamingValue
ObjectMemory
Resource type (JSON:API type)memory
Collection / records rootโ€” (not a records root)
REST base/v1/memories
Entity classMemory

Internal object. Not currently exposed on the public REST API. The operations below describe the intended contract.

API operations

OperationMethod & pathStatus
ListGET /v1/memories๐ŸŸก Planned
RetrieveGET /v1/memories/{id}๐ŸŸก Planned
CreatePOST /v1/memories๐ŸŸก Planned
UpdatePATCH /v1/memories/{id}๐ŸŸก Planned
DeleteDELETE /v1/memories/{id}๐ŸŸก Planned

Data model

Attributes

FieldTypeRequiredConstraintsAllowed valuesDescription
memory_id๐Ÿ”’ system โ€” UUIDโœ… YesUNIQUE; generated via gen_random_uuid() on INSERTโ€”Public identifier for this memory record. Never expose internal pk.
typeenum (memory_type_enum)โœ… YesNOT NULL; DEFAULT 'user'; native PG enum core_api.memory_type_enum; CHECK constraint: type = 'user' AND membership_pk IS NOT NULL OR type = 'workspace' AND membership_pk IS NULL'user' | 'workspace'Scope of the memory. user memories are tied to a specific workspace member (membership required); workspace memories are shared across all members of the workspace (membership must be null).
memorytextโœ… YesNOT NULL; no length cap defined in entity or migrationโ€”The full, LLM-generated memory blob. Written by MemoryService.upsertMemory() after each chat exchange extraction cycle. The chat agent reads this value before composing responses to personalize answers.
created_at๐Ÿ”’ system โ€” datetimeโœ… YesSet on INSERT via onCreate hook; never updatedโ€”Timestamp of the first memory extraction write for this scope.
updated_at๐Ÿ”’ system โ€” datetimeโœ… YesSet on INSERT (onCreate) and refreshed on every UPDATE (onUpdate) by MikroORM hookโ€”Timestamp of the most recent memory extraction write. The chat agent can use this to judge memory freshness.

Relationships

NameTypeRequiredDescription
workspaceto-one (ManyToOne)โœ… YesThe workspace this memory belongs to. Tenant boundary: every memory is scoped to exactly one workspace. FK column: workspace_pk. Target entity: Workspace.
membershipto-one (ManyToOne, nullable)โšช NoThe workspace membership (person ร— workspace) this memory belongs to when type = 'user'. Must be NULL when type = 'workspace' (enforced by CHECK constraint). FK column: membership_pk. Target entity: Membership.

System-computed

  • memory_id โ€” generated via gen_random_uuid() on INSERT
  • created_at โ€” set by MikroORM onCreate hook at INSERT time, never mutated
  • updated_at โ€” set by MikroORM onCreate hook at INSERT; refreshed by onUpdate hook on every subsequent write
  • type default โ€” defaults to MemoryType.USER ('user') at the entity level; column DEFAULT 'user' also enforced in Postgres
  • Partial unique indexes โ€” memories_workspace_unique: at most one row per workspace_pk WHERE type = 'workspace'; memories_membership_unique: at most one row per membership_pk WHERE type = 'user' โ€” enforced by MemoryRepository.upsertForWorkspace / upsertForMembership (find-or-create pattern)
  • CHECK constraint memories_type_membership_check โ€” ensures type/membership_pk coherence: user rows must have membership_pk NOT NULL; workspace rows must have membership_pk NULL
  • Whole-record upsert โ€” MemoryService.upsertMemory() writes the entire memory blob atomically on each LLM extraction cycle; there is no incremental append path

Example

{
  "data": {
    "type": "memory",
    "id": "a3f8c2d1-7e54-4b6a-9c0d-1f2e3a4b5c6d",
    "attributes": {
      "type": "user",
      "memory": "User prefers invoices sorted by due_date ascending. Frequently asks about overdue invoices and outstanding balances. Works in EUR and occasionally GBP.",
      "created_at": "2026-03-20T09:14:33.000Z",
      "updated_at": "2026-06-01T14:52:10.000Z"
    },
    "relationships": {
      "workspace": {
        "data": { "type": "workspace", "id": "ws_9f3e2a1b-4c5d-6e7f-8a9b-0c1d2e3f4a5b" }
      },
      "membership": {
        "data": { "type": "membership", "id": "ms_b1c2d3e4-f5a6-7b8c-9d0e-1f2a3b4c5d6e" }
      }
    }
  }
}
Source: /Users/maximechampoux/platform/apps/api/src/database/entities/Memory.ts ยท domain: intelligence ยท tier: Activity