feat(sdk-playground): reflect SDK-owned memory block in debug view

Update the playground to display the current deduplicated <supermemory>
replacement block produced by the SDK middleware instead of a browser-side
seen-facts delta. Add memory-dedupe helper and ignore local tsbuildinfo.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Dhravya Shah 2026-08-18 08:14:07 -07:00 committed by ved015
parent cb557f404f
commit 0fe2dabcee
2 changed files with 88 additions and 13 deletions

View file

@ -3,6 +3,7 @@ import {
type MiddlewareRuntimeConfig,
normalizeMiddlewareConfig,
} from "./middleware-config"
import { dedupeProfileForMode } from "./memory-dedupe"
export interface MemoryDebugEntry {
type:
@ -91,17 +92,6 @@ function summarizeProfile(profile: ContainerContext["profile"]) {
}
}
function selectProfileForMode(
profile: ContainerContext["profile"],
mode: "profile" | "query" | "full",
): ContainerContext["profile"] {
return {
static: mode === "query" ? [] : profile.static,
dynamic: mode === "query" ? [] : profile.dynamic,
searchResults: mode === "profile" ? [] : profile.searchResults,
}
}
function buildContextPreview(
profile: ContainerContext["profile"],
mode: "profile" | "query" | "full",
@ -182,7 +172,10 @@ export async function fetchContainerContext(
if (!apiKey) throw new Error("Supermemory API key is required")
const client = getSupermemoryClient(apiKey)
const profile = await fetchProfileContext(client, containerTag, query)
const profile = dedupeProfileForMode(
query ? "full" : "profile",
await fetchProfileContext(client, containerTag, query),
)
const docsResponse = await client.post<{
documents?: unknown[]
@ -246,7 +239,7 @@ export async function buildMiddlewareMemoryDebug(
query,
signal,
)
const selectedProfile = selectProfileForMode(profile, memoryMode)
const selectedProfile = dedupeProfileForMode(memoryMode, profile)
const summary = summarizeProfile(selectedProfile)
return [
@ -276,6 +269,12 @@ export async function buildMiddlewareMemoryDebug(
type: "context_preview",
label: "Reconstructed context preview (not middleware capture)",
preview: buildContextPreview(selectedProfile, memoryMode, query),
detail: {
totalFacts:
summary.staticCount +
summary.dynamicCount +
summary.searchResultCount,
},
},
config.addMemory === "always"
? {

View file

@ -0,0 +1,76 @@
import type { ContainerContext } from "./context-api"
type ProfileSlice = ContainerContext["profile"]
/** Normalize a fact for exact comparison within retrieved context. */
export function normalizeFactKey(text: string): string {
return text
.trim()
.replace(/^\[recent\]\s*/i, "")
.replace(/^\[\d{4}-\d{2}-\d{2}\]\s*/, "")
.trim()
.replace(/\s+/g, " ")
.toLowerCase()
}
function memoryText(item: unknown): string {
if (typeof item === "string") return item
if (item && typeof item === "object") {
const record = item as Record<string, unknown>
if (typeof record.memory === "string") return record.memory
if (typeof record.content === "string") return record.content
if (typeof record.chunk === "string") return record.chunk
}
return ""
}
/**
* Deduplicate static dynamic search (same priority as @supermemory/tools middleware).
*/
export function dedupeProfileForMode(
mode: "profile" | "query" | "full",
profile: ProfileSlice,
): ProfileSlice {
const injectsProfile = mode !== "query"
const staticItems = injectsProfile ? profile.static : []
const dynamicItems = injectsProfile ? profile.dynamic : []
const searchItems = profile.searchResults
const seen = new Set<string>()
const staticOut: unknown[] = []
const dynamicOut: unknown[] = []
const searchOut: unknown[] = []
for (const item of staticItems) {
const text = memoryText(item).trim()
if (!text) continue
const key = normalizeFactKey(text)
if (!key || seen.has(key)) continue
seen.add(key)
staticOut.push(item)
}
for (const item of dynamicItems) {
const text = memoryText(item).trim()
if (!text) continue
const key = normalizeFactKey(text)
if (!key || seen.has(key)) continue
seen.add(key)
dynamicOut.push(item)
}
for (const item of searchItems) {
const text = memoryText(item).trim()
if (!text) continue
const key = normalizeFactKey(text)
if (!key || seen.has(key)) continue
seen.add(key)
searchOut.push(item)
}
return {
static: staticOut,
dynamic: dynamicOut,
searchResults: mode === "profile" ? [] : searchOut,
}
}