refactor(ai-memory): align MemoryScope to shared Scope (threadId)#991
refactor(ai-memory): align MemoryScope to shared Scope (threadId)#991tombeckenham wants to merge 9 commits into
Conversation
Replace MemoryScope.sessionId with the shared Scope type from @tanstack/ai so memory and persistence use one conversation key (threadId) plus optional userId/tenantId. Update adapters, middleware, devtools, docs, and panel/e2e call sites. Hard cut — package is still 0.x. Closes #990
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (6)
🚧 Files skipped from review as they are similar to previous changes (5)
📝 WalkthroughWalkthroughMemory isolation now uses the shared ChangesMemory scope contract and devtools
Adapter and storage migration
Panel and E2E thread integration
Documentation and release notes
Estimated code review effort: 3 (Moderate) | ~25 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
View your CI Pipeline Execution ↗ for commit 5dfc0eb
☁️ Nx Cloud last updated this comment at |
🚀 Changeset Version Preview13 package(s) bumped directly, 36 bumped as dependents. 🟥 Major bumps
🟨 Minor bumps
🟩 Patch bumps
|
@tanstack/ai
@tanstack/ai-acp
@tanstack/ai-angular
@tanstack/ai-anthropic
@tanstack/ai-bedrock
@tanstack/ai-claude-code
@tanstack/ai-client
@tanstack/ai-code-mode
@tanstack/ai-code-mode-skills
@tanstack/ai-codex
@tanstack/ai-devtools-core
@tanstack/ai-durable-stream
@tanstack/ai-elevenlabs
@tanstack/ai-event-client
@tanstack/ai-fal
@tanstack/ai-gemini
@tanstack/ai-grok
@tanstack/ai-grok-build
@tanstack/ai-groq
@tanstack/ai-isolate-cloudflare
@tanstack/ai-isolate-node
@tanstack/ai-isolate-quickjs
@tanstack/ai-mcp
@tanstack/ai-memory
@tanstack/ai-mistral
@tanstack/ai-ollama
@tanstack/ai-openai
@tanstack/ai-opencode
@tanstack/ai-openrouter
@tanstack/ai-preact
@tanstack/ai-react
@tanstack/ai-react-ui
@tanstack/ai-sandbox
@tanstack/ai-sandbox-cloudflare
@tanstack/ai-sandbox-daytona
@tanstack/ai-sandbox-docker
@tanstack/ai-sandbox-local-process
@tanstack/ai-sandbox-sprites
@tanstack/ai-sandbox-vercel
@tanstack/ai-solid
@tanstack/ai-solid-ui
@tanstack/ai-svelte
@tanstack/ai-utils
@tanstack/ai-vue
@tanstack/ai-vue-ui
@tanstack/openai-base
@tanstack/preact-ai-devtools
@tanstack/react-ai-devtools
@tanstack/solid-ai-devtools
commit: |
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (3)
docs/memory/quickstart.md (1)
141-145: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd a client consumption snippet to this quickstart.
This page covers server middleware and client stream consumption, but the client half is only described in prose. Add a minimal
useChator connection-adapter example alongside this server-side scope example.As per coding guidelines, documentation pages covering both server and client behavior must include snippets for both halves.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/memory/quickstart.md` around lines 141 - 145, Add a minimal client-side consumption snippet near the server-side scope example in the quickstart, using either useChat or the connection adapter. Keep the example focused on establishing the client connection and consuming the stream, complementing the existing server middleware example without changing it.Source: Coding guidelines
packages/ai-devtools/tests/memory-registry.test.ts (1)
109-125: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winAdd isolation cases for the remaining Scope identifiers.
This test only distinguishes
threadIdfrom the(unknown)bucket. Add same-thread cases with differentuserId,tenantId, andnamespacevalues so thread-only bucketing cannot regress unnoticed.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/ai-devtools/tests/memory-registry.test.ts` around lines 109 - 125, Expand the test case around applyMemoryEvent to add same-thread events that vary userId, tenantId, and namespace, then assert each scope identifier remains isolated in state.scopes rather than being bucketed by threadId alone. Preserve the existing `(unknown)` assertion for the missing threadId case.testing/panel/src/lib/memory-store.ts (1)
20-24: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winRename the session-named map.
lastRecallBySessionis still exported and consumed by the panel routes, although it is now keyed bythreadId. Rename it tolastRecallByThreadand update both imports/usages to complete the terminology migration.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@testing/panel/src/lib/memory-store.ts` around lines 20 - 24, Rename the exported map lastRecallBySession to lastRecallByThread in memory-store.ts, then update both panel route imports and usages to reference the new thread-based name while preserving the existing Map behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/memory/custom-adapter.md`:
- Around line 98-100: Update the custom adapter persistence example to include
tenantId throughout: add tenant_id to the INSERT columns and parameter list, and
add tenant_id to the corresponding WHERE/filter conditions. Preserve null
handling for scopes without a tenant while ensuring tenant-scoped records cannot
cross tenant boundaries.
In `@packages/ai-devtools/src/store/memory-registry.ts`:
- Around line 84-87: Preserve the complete scope identity through the DevTools
event and registry paths: in packages/ai-devtools/src/store/memory-registry.ts
lines 84-87, update memoryScopeKey to include namespace with threadId; in
packages/ai-event-client/src/index.ts lines 828-836, add optional namespace to
the carried scope type; in packages/ai-client/src/devtools.ts line 34, include
namespace in event payload scopes; in
packages/ai-devtools/src/store/memory-registry.ts lines 63-68, add namespace to
MemoryScopeState; and in lines 101-110, initialize and update that namespace
metadata alongside the existing scope fields.
In `@packages/ai-memory/src/providers/hindsight/index.ts`:
- Around line 114-117: Preserve tenant isolation across vendor-backed memory
identities: in packages/ai-memory/src/providers/hindsight/index.ts lines
114-117, update bankId to include a safely encoded tenant dimension; in
packages/ai-memory/src/providers/honcho/index.ts lines 136-138, tenant-qualify
SDK session, user-peer identities, and cache keys; in
packages/ai-memory/tests/contract.ts lines 16-17, add coverage for identical
userId/threadId values across different tenants while retaining independent
thread/user isolation cases.
In `@packages/ai-memory/tests/providers/mem0.test.ts`:
- Around line 61-67: Update the mem0 adapter’s save, recall, and loadMemories
request construction to include scope.threadId as the conversation/scenario
filter while preserving user scoping. Extend mem0.test.ts with a
same-user/different-thread case that verifies memories do not cross thread
boundaries, covering both persistence and retrieval behavior.
In `@testing/panel/src/routes/api.memory-chat.ts`:
- Line 48: Update memory-chat’s scope construction around threadId and
memory-inspect’s query handling to derive memory scope identifiers from trusted
server/session state rather than client-provided data.threadId or ?threadId.
Include both user and tenant dimensions in the shared in-memory adapter scope,
and apply the same trusted scoping consistently in
testing/panel/src/routes/api.memory-chat.ts:48 and
testing/panel/src/routes/api.memory-inspect.ts:14-19.
---
Nitpick comments:
In `@docs/memory/quickstart.md`:
- Around line 141-145: Add a minimal client-side consumption snippet near the
server-side scope example in the quickstart, using either useChat or the
connection adapter. Keep the example focused on establishing the client
connection and consuming the stream, complementing the existing server
middleware example without changing it.
In `@packages/ai-devtools/tests/memory-registry.test.ts`:
- Around line 109-125: Expand the test case around applyMemoryEvent to add
same-thread events that vary userId, tenantId, and namespace, then assert each
scope identifier remains isolated in state.scopes rather than being bucketed by
threadId alone. Preserve the existing `(unknown)` assertion for the missing
threadId case.
In `@testing/panel/src/lib/memory-store.ts`:
- Around line 20-24: Rename the exported map lastRecallBySession to
lastRecallByThread in memory-store.ts, then update both panel route imports and
usages to reference the new thread-based name while preserving the existing Map
behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 618b9bd0-763b-4641-8ffc-bbb2db32af6f
📒 Files selected for processing (35)
docs/config.jsondocs/memory/adapters.mddocs/memory/custom-adapter.mddocs/memory/operating.mddocs/memory/overview.mddocs/memory/quickstart.mdpackages/ai-client/src/devtools.tspackages/ai-client/tests/devtools.test.tspackages/ai-devtools/src/components/hooks/MemoryPanel.tsxpackages/ai-devtools/src/store/ai-context.tsxpackages/ai-devtools/src/store/memory-registry.tspackages/ai-devtools/tests/memory-registry.test.tspackages/ai-event-client/src/index.tspackages/ai-memory/skills/tanstack-ai-memory-hindsight/SKILL.mdpackages/ai-memory/skills/tanstack-ai-memory-redis/SKILL.mdpackages/ai-memory/skills/tanstack-ai-memory/SKILL.mdpackages/ai-memory/src/internal/store.tspackages/ai-memory/src/middleware.tspackages/ai-memory/src/providers/hindsight/index.tspackages/ai-memory/src/providers/honcho/index.tspackages/ai-memory/src/providers/redis/index.tspackages/ai-memory/src/types.tspackages/ai-memory/tests/contract.tspackages/ai-memory/tests/middleware.test.tspackages/ai-memory/tests/providers/honcho.test.tspackages/ai-memory/tests/providers/in-memory.test.tspackages/ai-memory/tests/providers/mem0.test.tspackages/ai-memory/tests/providers/redis.test.tstesting/e2e/src/lib/devtools-memory-store.tstesting/e2e/src/routes/api.devtools-memory.tstesting/e2e/src/routes/api.middleware-test.tstesting/panel/src/lib/memory-store.tstesting/panel/src/routes/api.memory-chat.tstesting/panel/src/routes/api.memory-inspect.tstesting/panel/src/routes/memory.tsx
Add sameScope/redis/contract isolation coverage, composite DevTools scope keys, vendor Scope field docs, and the memory-scope-threadid changeset.
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/ai-devtools/src/store/memory-registry.ts`:
- Around line 92-105: Update scopeDim, used by memoryScopeKey, so a literal
dimension value of "_" is escaped instead of being emitted as the
absent-dimension sentinel. Preserve "_" for undefined or empty values, while
ensuring literal tenantId/userId "_" produce distinct scope keys.
In `@packages/ai-memory/tests/same-scope.test.ts`:
- Around line 1-2: Move the unit test covering sameScope from the tests
directory into a *.test.ts file alongside the internal/store source, such as
store.test.ts. Preserve the existing test behavior and imports while colocating
it with the sameScope implementation.
- Around line 30-38: Update sameScope and its tests so every Scope dimension,
including omitted or undefined tenantId and userId values, is compared as an
exact match rather than treated as a wildcard. Ensure incomplete queries do not
match records with populated tenant/user dimensions, while preserving matches
for identical scopes and aligning expectations in same-scope.test.ts with the
Scope identity contract.
In `@testing/panel/src/lib/memory-store.ts`:
- Line 24: Update lastRecallByThread and its chat/inspect route callers to key
recall state by the complete collision-safe scope composite used by memory
storage, including threadId plus optional userId, tenantId, and namespace. Reuse
the existing scope-key helper or equivalent established key construction rather
than threadId alone, and ensure all reads and writes use the same key.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 1d7f9324-b7ac-446d-a67a-508a72ddada1
📒 Files selected for processing (24)
.changeset/memory-scope-threadid.md.changeset/shared-scope.mddocs/memory/adapters.mddocs/memory/overview.mdpackages/ai-devtools/src/components/hooks/MemoryPanel.tsxpackages/ai-devtools/src/store/ai-context.tsxpackages/ai-devtools/src/store/memory-registry.tspackages/ai-devtools/tests/memory-registry.test.tspackages/ai-memory/skills/tanstack-ai-memory-hindsight/SKILL.mdpackages/ai-memory/skills/tanstack-ai-memory-honcho/SKILL.mdpackages/ai-memory/skills/tanstack-ai-memory-mem0/SKILL.mdpackages/ai-memory/skills/tanstack-ai-memory-redis/SKILL.mdpackages/ai-memory/skills/tanstack-ai-memory/SKILL.mdpackages/ai-memory/src/internal/store.tspackages/ai-memory/src/providers/redis/index.tspackages/ai-memory/src/types.tspackages/ai-memory/tests/contract.tspackages/ai-memory/tests/providers/in-memory.test.tspackages/ai-memory/tests/providers/redis.test.tspackages/ai-memory/tests/same-scope.test.tstesting/panel/src/lib/memory-store.tstesting/panel/src/routes/api.memory-chat.tstesting/panel/src/routes/api.memory-inspect.tstesting/panel/src/routes/memory.tsx
🚧 Files skipped from review as they are similar to previous changes (12)
- packages/ai-devtools/src/store/ai-context.tsx
- packages/ai-memory/skills/tanstack-ai-memory-hindsight/SKILL.md
- testing/panel/src/routes/api.memory-inspect.ts
- packages/ai-memory/tests/providers/in-memory.test.ts
- testing/panel/src/routes/api.memory-chat.ts
- testing/panel/src/routes/memory.tsx
- docs/memory/overview.md
- packages/ai-memory/src/providers/redis/index.ts
- packages/ai-devtools/tests/memory-registry.test.ts
- packages/ai-memory/src/types.ts
- packages/ai-memory/src/internal/store.ts
- packages/ai-memory/skills/tanstack-ai-memory/SKILL.md
Exact-match optional dims in sameScope, tenant-qualify vendor keys, mem0 run_id=threadId, full DevTools Scope identity, panel server-trusted user/tenant, and custom-adapter tenant_id example.
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/ai-memory/skills/tanstack-ai-memory-hindsight/SKILL.md (1)
10-10: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick winUse the tenant-aware Hindsight bank-key format consistently.
Line 10 still documents
{userId}__{threadId}, which contradicts the adapter and Lines 34-35. Document{tenantId|_}__{user}__{threadId}here as well so tenant isolation is not misrepresented.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/ai-memory/skills/tanstack-ai-memory-hindsight/SKILL.md` at line 10, Update the bank-key format documented in the skill’s recall description from {userId}__{threadId} to the tenant-aware {tenantId|_}__{user}__{threadId} format, matching the adapter and the existing documentation on Lines 34-35.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/memory/adapters.md`:
- Line 144: Update the user row in the documentation table to escape the pipe
character inside the bank-key example as `{tenant\|_}`, preserving the existing
text and table structure.
In `@packages/ai-devtools/src/store/memory-registry.ts`:
- Around line 104-107: Update memoryScopeKey so missing or empty threadId values
produce a dimension-aware registry key that includes tenantId, userId, and
namespace instead of the shared “(unknown)” key. Preserve the existing escaped
threadId key for valid scopes, ensuring ensureScope() keeps events from
different identities isolated.
In `@packages/ai-memory/src/providers/hindsight/index.ts`:
- Around line 115-121: Add a shared collision-safe encoding step for tenant,
user, and thread components before composing vendor identities. In
packages/ai-memory/src/providers/hindsight/index.ts lines 115-121, update bankId
to encode each component and retain _ only for the encoded unset-tenant
sentinel; in packages/ai-memory/src/providers/honcho/index.ts lines 142-158,
apply the same encoding to session keys and tenant-qualified peer IDs,
preserving distinct identities even when values contain __ or delimiter-like
content.
In `@packages/ai-memory/src/providers/mem0/index.ts`:
- Around line 75-81: Update the Mem0 identity helpers userId(scope) and
runId(scope) to derive tenant-scoped identifiers by combining the tenant
isolation value with their existing user and thread scope values. Ensure the
same derivation is reused consistently for save, recall, inspect, and listFacts
operations so identical IDs from different tenants cannot collide.
---
Outside diff comments:
In `@packages/ai-memory/skills/tanstack-ai-memory-hindsight/SKILL.md`:
- Line 10: Update the bank-key format documented in the skill’s recall
description from {userId}__{threadId} to the tenant-aware
{tenantId|_}__{user}__{threadId} format, matching the adapter and the existing
documentation on Lines 34-35.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: ad517071-fed1-4771-b6a5-749747f1f1fe
📒 Files selected for processing (22)
docs/memory/adapters.mddocs/memory/custom-adapter.mddocs/memory/quickstart.mdpackages/ai-client/src/devtools.tspackages/ai-devtools/src/store/memory-registry.tspackages/ai-devtools/tests/memory-registry.test.tspackages/ai-event-client/src/index.tspackages/ai-memory/skills/tanstack-ai-memory-hindsight/SKILL.mdpackages/ai-memory/skills/tanstack-ai-memory-honcho/SKILL.mdpackages/ai-memory/skills/tanstack-ai-memory-mem0/SKILL.mdpackages/ai-memory/skills/tanstack-ai-memory/SKILL.mdpackages/ai-memory/src/internal/store.test.tspackages/ai-memory/src/internal/store.tspackages/ai-memory/src/providers/hindsight/index.tspackages/ai-memory/src/providers/honcho/index.tspackages/ai-memory/src/providers/mem0/index.tspackages/ai-memory/src/providers/redis/index.tspackages/ai-memory/tests/providers/mem0.test.tspackages/ai-memory/vite.config.tstesting/panel/src/lib/memory-store.tstesting/panel/src/routes/api.memory-chat.tstesting/panel/src/routes/api.memory-inspect.ts
🚧 Files skipped from review as they are similar to previous changes (10)
- testing/panel/src/routes/api.memory-inspect.ts
- packages/ai-memory/skills/tanstack-ai-memory-honcho/SKILL.md
- packages/ai-event-client/src/index.ts
- testing/panel/src/routes/api.memory-chat.ts
- packages/ai-client/src/devtools.ts
- packages/ai-memory/skills/tanstack-ai-memory/SKILL.md
- packages/ai-devtools/tests/memory-registry.test.ts
- packages/ai-memory/tests/providers/mem0.test.ts
- packages/ai-memory/src/internal/store.ts
- docs/memory/custom-adapter.md
| function bankId(scope: MemoryScope): string { | ||
| const user = options.user ?? scope.userId ?? 'demo-user' | ||
| return `${user}__${scope.sessionId}` | ||
| // Include tenant so multi-tenant deploys cannot share banks when user+thread | ||
| // collide. Unset tenant uses `_` (same placeholder convention as redis). | ||
| const tenant = | ||
| scope.tenantId != null && scope.tenantId !== '' ? scope.tenantId : '_' | ||
| return `${tenant}__${user}__${scope.threadId}` |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== locate provider files =="
fd -a 'index\.ts$' . | sed 's#^\./##' | grep -E 'packages/ai-memory/src/providers/(hindsight|honcho)/index\.ts$' || true
echo
echo "== hindsight relevant lines =="
if [ -f packages/ai-memory/src/providers/hindsight/index.ts ]; then
sed -n '1,180p' packages/ai-memory/src/providers/hindsight/index.ts | cat -n
fi
echo
echo "== honcho relevant lines =="
if [ -f packages/ai-memory/src/providers/honcho/index.ts ]; then
sed -n '1,220p' packages/ai-memory/src/providers/honcho/index.ts | cat -n
fi
echo
echo "== search for MemoryScope usage/types =="
rg -n "type MemoryScope|interface MemoryScope|MemoryScope|tenantId|threadId|userId" packages/ai-memory/src -g '*.ts' | head -200Repository: TanStack/ai
Length of output: 26216
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== types and scope helpers =="
sed -n '1,180p' packages/ai-memory/src/types.ts | cat -n
echo
sed -n '90,140p' packages/ai-memory/src/internal/store.ts | cat -n
echo
echo "== behavior: raw tenant__component key collisions =="
python3 - <<'PY'
def bank_id(tenant, user, thread):
t = tenant if tenant is not None and tenant != '' else '_'
return f"{t}__{user}__{thread}"
def session_key(tenant, thread):
t = tenant if tenant is not None and tenant != '' else '_'
return f"{t}__{thread}"
def peer_id(tenant, user):
user = user if tenant is not None and tenant != '' else user
return f"{tenant}__{user}" if tenant is not None and tenant != '' else user
def key_equal(key):
a = b = None
def set_a(scope):
nonlocal a
a = key(scope)
def set_b(scope):
nonlocal b
b = key(scope)
def check():
nonlocal a,b
if a == b:
if a[0] == tenant:
return set_a(t)
else:
return set_b(b)
return None
return key_equal
cases = [
("tenant__user__thread"),
("_", "user", "thread"),
("_", "__user", "thread"),
("_", "user", "__thread"),
("_@", "user", "thread"),
("`@_`", "user", "thread"),
("tenant__", "user", "thread"),
("tenant__", "", "thread"),
("tenant", "_user", "thread"),
]
for key in [bank_id, session_key]:
print("key:", key.__name__)
seen = {}
for vals in [(None, "u", "t"), ('', 'u', 't'), ('_', 'u', 't'), ('a_b', 'u', 't'), ('a', '_b', 't'), ('__', 'u', 't'), ('_', '_', 't'), ('a', 'b__c', 't')]:
k = key(*vals)
if k not in seen:
seen[k] = []
seen[k].append(vals)
for k, scopes in seen.items():
if len(scopes)>1:
print("collision:", repr(k), "scopes:", [(repr(x) if isinstance(x,str) else x for x in s) for s in scopes])
print("peer collisions")
payloads = [(None, "u"), ('', 'u'), ('_', 'u'), ('a', "u"), ('_', "u")]
for tenant, user in payloads:
print(repr((tenant,user)), "=>", peer_id(tenant, user))
PY
echo
echo "== redis escapeScopeValue implementation =="
rg -n -A 15 -B 5 "escapeScopeValue|scopeKey|indexKey" packages/ai-memory/src/providers/redis/index.ts packages/ai-memory/src/internal/store.tsRepository: TanStack/ai
Length of output: 9666
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== redis scope key implementation =="
sed -n '230,275p' packages/ai-memory/src/internal/store.ts | cat -n
echo
sed -n '1,80p' packages/ai-memory/src/providers/redis/index.ts | cat -n
sed -n '95,140p' packages/ai-memory/src/providers/redis/index.ts | cat -n
echo
echo "== programmatic raw-composite collision examples =="
python3 - <<'PY'
def bank_id(tenant, user=None, thread=''):
user = user if user is not None and user != '' else 'demo-user'
t = tenant if tenant is not None and tenant != '' else '_'
return f"{t}__{user}__{thread}"
def session_key(tenant, thread):
t = tenant if tenant is not None and tenant != '' else '_'
return f"{t}__{thread}"
def peer_id(tenant, user):
user = user if user is not None and user != '' else 'demo-user'
return f"{tenant}__{user}" if tenant is not None and tenant != '' else user
keys = {'bank_id': bank_id, 'session_key': session_key, 'peer_id': peer_id}
cases = {
'bank_id': [(None, 'u', 't'), ('', 'u', 't'), ('_', 'u', 't'), ('a_b', 'u', 't'), ('__', 'u', 't')],
'session_key': [(None, 't'), ('', 't'), ('_', 't'), ('a__t', 't')],
'peer_id': [(None, 'u'), ('', 'u'), ('_', 'u'), ('a', 'u'), ('__a', 'u')],
}
for name, fn in keys.items():
print(name)
by = {}
for vals in cases[name]:
try:
k = fn(*vals)
except Exception as e:
k = f"ERR:{e}"
by.setdefault(k, []).append(vals)
for k, scopes in by.items():
if len(scopes) > 1:
print(" collision ->", repr(k))
for s in scopes:
print(" ", s)
print("peer cross-tenant sentinel: ", peer_id('_', 'u'), peer_id('a', '_u'))
PY
echo
echo "== all tenant-qualified provider composite key usages =="
rg -n "tenantId|__|scope\.threadId|options\.user|scope\.userId|escapeScopeValue" packages/ai-memory/src/providers packages/ai-memory/src/types.ts packages/ai-memory/src/internal/store.tsRepository: TanStack/ai
Length of output: 10884
Use collision-safe encoding for tenant-qualified vendor identities.
Both adapters build vendor ids by concatenating tenant dimensions into raw __ keys, so distinct tenant/user/thread combinations can resolve to the same key.
packages/ai-memory/src/providers/hindsight/index.ts#L115-L121: escape/encode tenant, user, and thread before composing the bank ID; keep_only as the escaped unset sentinel.packages/ai-memory/src/providers/honcho/index.ts#L142-L158: apply the same encoding to Honcho session keys and tenant-qualified peer IDs.
📍 Affects 2 files
packages/ai-memory/src/providers/hindsight/index.ts#L115-L121(this comment)packages/ai-memory/src/providers/honcho/index.ts#L142-L158
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ai-memory/src/providers/hindsight/index.ts` around lines 115 - 121,
Add a shared collision-safe encoding step for tenant, user, and thread
components before composing vendor identities. In
packages/ai-memory/src/providers/hindsight/index.ts lines 115-121, update bankId
to encode each component and retain _ only for the encoded unset-tenant
sentinel; in packages/ai-memory/src/providers/honcho/index.ts lines 142-158,
apply the same encoding to session keys and tenant-qualified peer IDs,
preserving distinct identities even when values contain __ or delimiter-like
content.
Source: Learnings
| /** | ||
| * mem0 `run_id` — conversation/run isolation. Maps 1:1 to `scope.threadId` so | ||
| * same-user memories do not leak across threads. | ||
| */ | ||
| function runId(scope: MemoryScope): string { | ||
| return scope.threadId | ||
| } |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "## locate file"
fd -a 'index.ts$' . | sed 's#^\./##' | grep 'packages/ai-memory/src/providers/mem0/index.ts' || true
echo "## file outline"
ast-grep outline packages/ai-memory/src/providers/mem0/index.ts --view expanded || true
echo "## relevant lines"
cat -n packages/ai-memory/src/providers/mem0/index.ts | sed -n '1,220p'
echo "## search MemoryScope and tenantId references"
rg -n "interface MemoryScope|type MemoryScope|tenantId|threadId|userId|runId|Mem0|run_id|user_id" packages/ai-memory/src -SRepository: TanStack/ai
Length of output: 15363
Include tenant isolation in Mem0 identity parameters.
userId(scope) uses the supplied/user-store id directly, and runId(scope) uses only threadId. The same user or thread id from different tenants will be sent as the same user_id / run_id for Mem0 saves, recalls, and list operations, allowing cross-tenant memory leakage. Derive these identifiers from tenant isolation together with the scope values, and keep save, recall, inspect, and listFacts consistent.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ai-memory/src/providers/mem0/index.ts` around lines 75 - 81, Update
the Mem0 identity helpers userId(scope) and runId(scope) to derive tenant-scoped
identifiers by combining the tenant isolation value with their existing user and
thread scope values. Ensure the same derivation is reused consistently for save,
recall, inspect, and listFacts operations so identical IDs from different
tenants cannot collide.
Source: Learnings
Escape table pipes in adapters.md and align the hindsight skill intro with the tenant-aware bank format.
MemoryScopeLite requires threadId when present; memory:error omits scope when resolve never ran. Drop emptyScope() and DevTools unknown-bucket fakes.
Summary
MemoryScopeto the sharedScopetype from@tanstack/ai(threadIdrequired; optionaluserId/tenantId/namespace).sessionId→threadId.{tenantId|_}:{userId|_}:{threadId}(escaped); hindsight banks use{user}__{threadId}.Scope/threadIdas the isolation vocabulary (no publicsessionIdon memory scope). Hard cut —@tanstack/ai-memoryis still0.x.Closes #990
Test plan
pnpm --filter @tanstack/ai-memory test:lib(43 tests)pnpm --filter @tanstack/ai-memory test:typespnpm --filter @tanstack/ai-devtools-core test:lib+test:typespnpm --filter @tanstack/ai-clientdevtools tests + typecheckpnpm --filter @tanstack/ai-event-client test:lib+test:typesSummary by CodeRabbit
threadIdinstead ofsessionId;MemoryScopeis aligned to the sharedScopecontract (threadIdrequired,tenantId/userIdoptional,namespacereserved).memory:errormay omit scope when resolution fails.run_id.threadIdand trusted server-side scope derivation.