Skip to content

refactor(ai-memory): align MemoryScope to shared Scope (threadId) from #980 #990

Description

@tombeckenham

Summary

@tanstack/ai already defines a shared identity type for subsystems that persist or recall per-conversation data:

// packages/ai/src/scope.ts (#980)
interface Scope {
  threadId: string   // required — same as ChatMiddlewareContext.threadId
  userId?: string
  tenantId?: string
  namespace?: string // reserved
}

#980 explicitly says persistence and memory should share one vocabulary and that subsystems must not invent a second name (sessionId, conversationId, …) for the conversation key.

#541 (memory middleware) landed after #980 but did not adopt it. Memory still ships:

// packages/ai-memory
interface MemoryScope {
  sessionId: string
  userId?: string
}

Docs bridge at the app boundary (sessionId: session.threadId) instead of using Scope directly.

Why this matters

  • Two names for one conceptthreadId (chat / persistence) vs sessionId (memory) increases wiring friction and docs drift.
  • Incomplete multi-tenant story — shared Scope has tenantId / namespace; MemoryScope does not.
  • feat(persistence): server persistence + client browser-refresh durability #984 (persistence) and memory will keep diverging on identity unless both center on Scope.threadId + server-derived userId/tenantId.

Proposed work

  1. Adopt Scope as the memory isolation type

    • Prefer export type MemoryScope = Scope (or drop MemoryScope and use Scope everywhere), imported from @tanstack/ai.
    • Conversation key becomes threadId, not sessionId.
  2. Migrate call sites

    • Middleware scope option: static or (ctx) => Scope.
    • Adapters (in-memory, redis, mem0, honcho, hindsight, …): key/store paths use scope.threadId.
    • Redis (and any) key encoding: treat threadId as the conversation segment; keep escaping so : in ids cannot collide.
  3. Docs / skills

    • docs/memory/* and packages/ai-memory/skills/*: replace sessionId examples with threadId / Scope.
    • Cross-link persistence + memory to the same identity section (server-derived userId/tenantId, never trust client alone).
  4. Compat (if needed for any pre-release consumers)

    • Short deprecation window: accept { sessionId } at the edge and map to { threadId: sessionId } with a console warning, then remove.
    • Or hard cut if memory is still 0.x / unreleased enough that a break is fine — call that out in the PR.
  5. Tests

    • Contract suite + provider tests: assert isolation by threadId (+ userId / tenantId where relevant).
    • E2E / panel memory routes: use threadId consistently with chat.

Non-goals

  • Changing the recall / save adapter verbs.
  • Forcing persistence store methods to take a full Scope object in the same PR (can stay bare threadId + host-side auth; still document Scope as the identity model).

Acceptance criteria

  • No public sessionId on memory’s isolation type (or only via deprecated alias).
  • Memory adapters and middleware use Scope / threadId from @tanstack/ai.
  • Docs and skills no longer teach MemoryScope.sessionId as the primary key.
  • Unit + contract tests green; memory e2e/panel paths updated if they hard-code sessionId.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions