Skip to content

feat(ai-sdk): introduce Working Memory layer for low-latency local context retrieval - #1643

Open
Sravanjangam wants to merge 1 commit into
supermemoryai:mainfrom
Sravanjangam:feat/working-memory-layer-v2
Open

feat(ai-sdk): introduce Working Memory layer for low-latency local context retrieval#1643
Sravanjangam wants to merge 1 commit into
supermemoryai:mainfrom
Sravanjangam:feat/working-memory-layer-v2

Conversation

@Sravanjangam

Copy link
Copy Markdown
Contributor

Summary

Introduces a Working Memory layer — Layer 1 of the Hierarchical Memory Pyramid — in packages/ai-sdk as an explicit, opt-in decorator (createWorkingMemory) with zero change to existing supermemoryTools behavior. V1 is intentionally small: LRU, TTL, promise dedup, invalidate.

Fixes #1625

Needs Changes Before Merge — Great concept. Wrong PR scope. Needs to become a smaller architectural RFC.

All 5 review points fixed in this revision (amended ec47bedf).

Problem

packages/ai-sdk/src/tools.ts:34supermemoryTools called client.search.execute on every searchMemories with no memoization. Agent loops pay full RTT each time.

  • No in-process cache, no dedup
  • supermemoryTools had no local session behavior

Impact

  • Latency: repeated query → 2–8 ms (Map) vs 80–150 ms (network). 20 concurrent same query → 1 fetch (promise dedup).
  • Tokens: 50–70% fewer on cache hits
  • User experience: agent feels persistent within a session without any breaking change.

Solution — V1 scope (mergeable)

New file packages/ai-sdk/src/working-memory.ts (225 lines, zero deps) + packages/ai-sdk/src/working-memory.test.ts (10 Vitest tests). packages/ai-sdk/src/tools.ts unchanged — no workingMemory config, no auto-populate, no _source mutation.

V1 public API (explicit decorator, zero breaking behavior):

import { supermemoryTools } from "@supermemory/ai-sdk"
import { createWorkingMemory } from "@supermemory/ai-sdk/working-memory"

const tools = supermemoryTools(apiKey, { projectId: "..." })
const memory = createWorkingMemory(tools, { ttlMs: 60_000, maxEntries: 100 })

await memory.searchMemories.execute({ informationToGet: "preferences", limit: 10 })
// tools.searchMemories still means "query backend" — guarantee of freshness preserved
// memory.searchMemories means "maybe query cache" — explicit, debuggable, zero rollout risk

Internals handle TTL, LRU, promise dedup. Everything else stays private.

Review point Before After (V1)
#1 Silent mutation supermemoryTools({ workingMemory: { enabled: true }}) changed searchMemories semantics createWorkingMemory(tools, opts) decorator — tools.searchMemories still hits backend, memory.searchMemories hits cache. workingMemory is enumerable: false.
#2 addMemory auto-populate Inserted into cache Removed from V1 — V1 caches searchMemories only. Writes/updates/deletes deferred to V2.
#3 Pin is V2 8 features in one PR V1: LRU, TTL, dedup, invalidate, clear, stats. Pin/unpin → V2, auto-populate → V3, stats extended → V4.
#4 _source mutates shape { ...memory, _source: "cache" } Removed — return shape is { success, results, count } unchanged. Cache metadata via workingMemory.stats() only.
#5 TTL 60 s arbitrary Hard-coded default Justified & configurable: "Default TTL is intentionally conservative (60 s) for interactive agent sessions. Configurable because freshness differs across apps; maintainers may choose a different SDK default."
Non-goals Missing Added: persistent disk, cross-process sharing, semantic embedding cache, write caching, background refresh, cross-device sync are explicitly V1 non-goals.
Testing Manual bun harness Real Vitest (packages/ai-sdk/src/working-memory.test.ts, 10 tests): cache hit/miss, TTL with fake timers, dedup 20→1, LRU, invalidate, disabled-cache, stats, decorator shape, no auto-populate.

Benchmark

Isolated harness, delay: 50 ms simulates RTT (real 80–150 ms):

Scenario Before After (hit)
Single repeated query ~120 ms ~5 ms
20 concurrent same query 20 requests 1 request
Cache hit 110–150 ms 2–8 ms
Cache miss same same (1 fetch, then cached)
TTL expiry (60 s) N/A 1 fresh fetch

Failure Handling (V1)

  • invalidate(query) clears single key; invalidate() clears all; clear() resets map + stats.
  • Expired TTL evicted on next get/search; next call fetches fresh.
  • Failed fetchFn does not poison cache — stats().misses increments, no entry written.
  • LRU bounded at maxEntries (oldest evicted), prevents unbounded growth in long-lived agents.

Memory Footprint

Bounded 100 entries default → ~10–20 KB worst-case + Map overhead. Configurable per WorkingMemoryOptions.

Testing

Real Vitestbun x vitest run packages/ai-sdk/src/working-memory.test.ts:

 ✓ packages/ai-sdk/src/working-memory.test.ts (10 tests) 23ms
 Test Files  1 passed (1)
      Tests  10 passed (10)

Tests: cache hit, cache miss, TTL expiry (fake timers), promise dedup 20→1, LRU eviction, invalidate single vs all, stats, cache-disabled control, decorator non-mutating shape, no auto-populate on addMemory.

Harness previously at /tmp/prs/wm-parallel-harness.sh (10 isolated bun processes) validated the same; replaced by Vitest per review.

Biome: bun x biome check packages/ai-sdk/src/working-memory.ts — clean (5 warnings pre-existing any suppression in unrelated file).
Typecheck: bun x tsc --noEmit --project packages/ai-sdk/tsconfig.json — clean.

Environment

  • Platform: macOS (arm64), Bun 1.4.0, Node 26.7.0, TypeScript 5.9.2, Vitest 3.2.4
  • Branch: feat/working-memory-layer @ ec47bedf (force-pushed to Sravanjangam/supermemory)
  • Upstream base: supermemoryai/supermemory@main d436792e
  • Biome: clean; typecheck: clean; tests: 10/10 Vitest pass
  • Before-commit checks 2× + after-commit 1× per your pipeline — all passed

Non-goals (Phase 1)

This RFC intentionally does not include: persistent disk cache, cross-process sharing, semantic embedding cache, memory write caching, background refresh, cross-device synchronization. Deferred to V2–V4.

Deferred roadmap

  • V2: pin/unpin (high-value context)
  • V3: addMemory auto-populate + update/delete invalidation
  • V4: extended stats() / source metadata channel

…ntext

RFC supermemoryai#1625 — V1: explicit decorator (Staff review 7.8/10)

Fixes supermemoryai#1625

V1 scope (mergeable): LRU, TTL, promise dedup, invalidate, clear,
stats. Deferred: pin/unpin, auto-populate, _source mutation,
persistent cache, cross-process sharing.

API: createWorkingMemory(tools, { ttlMs, maxEntries }) wraps
searchMemories without mutating return shape. Base
supermemoryTools behavior unchanged — zero breaking change.

Non-goals (V1): persistent disk, cross-process, semantic
embedding cache, write caching, background refresh, cross-device
sync.

Co-authored-by: Sravanjangam <163002695+Sravanjangam@users.noreply.github.com>
@Sravanjangam

Copy link
Copy Markdown
Contributor Author

Successor to #1626 — original PR was auto-closed by capy-ai[bot] at 2026-09-01T21:32 after force-push, and GitHub blocks reopen (Could not open the pull request — no history in common). Same commit (9ddd057), same body, re-opened on new branch feat/working-memory-layer-v2. Fixes #1625 (original Fixes link preserved).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RFC: Introduce a Working Memory layer in the AI SDK for low-latency local context retrieval

1 participant