/** * Memory-flush prompt resolver. * * The raw prompt strings live in `src/memory/constants.ts` and contain a * single `{{MEMORY_PATHS_RUBRIC}}` placeholder. This module substitutes * the caller-scoped rubric (produced by `renderPathsRubric()` in * `src/memory/paths.ts`) into that placeholder at flush time, so: * * - a collaborative agent with a real caller sees all 8 paths * - an isolated/autonomous agent sees only the 4 agent-tier paths * (user-tier rows are physically omitted from the rubric the LLM * sees, so it cannot route writes there even by mistake) * * ## Why this lives in prompts/ and not memory/constants.ts * * The constants file is pure strings — no imports, no runtime work. * Injecting the rubric needs access to `renderPathsRubric()` which * needs `MemoryScope`, so the substitution has to happen one layer up. * Keeping the placeholder in constants and the substitution here means * tests of the raw prompt (no scope) and tests of the rendered prompt * (scope-aware) stay independent. */ import { DEFAULT_MEMORY_FLUSH_PROMPT, DEFAULT_MEMORY_FLUSH_SYSTEM_PROMPT, FLUSH_PROMPT_RUBRIC_PLACEHOLDER, } from '@/memory/constants'; import { renderPathsRubric } from '@/memory/paths'; import type { MemoryScope } from '@/memory/types'; export { DEFAULT_MEMORY_FLUSH_PROMPT, DEFAULT_MEMORY_FLUSH_SYSTEM_PROMPT }; /** Back-compat alias so existing callers keep working. */ export const MEMORY_FLUSH_SYSTEM_PROMPT = DEFAULT_MEMORY_FLUSH_SYSTEM_PROMPT; /** Minimum scope shape needed to render the rubric. */ export interface ResolveFlushPromptsOptions { /** * The scope the memory tools were built with. Only `userId` matters * for rubric rendering — if absent, the user-tier paths are filtered * out of the rubric the LLM sees. */ scope: Pick; } /** * Result shape returned to `runMemoryFlush`. `prompt` goes in the * HumanMessage, `systemPrompt` goes in the SystemMessage. Both already * have the rubric substituted — the caller should pass them through * verbatim. */ export interface ResolvedFlushPrompts { prompt: string; systemPrompt: string; } /** * Substitutes the paths rubric into the raw prompts for a given scope. * * Idempotent (calling it twice on already-resolved strings is a no-op * because the placeholder will have been replaced already). Safe to * call per-flush; no caching needed — the string concat is microseconds. */ export function resolveFlushPrompts( options: ResolveFlushPromptsOptions ): ResolvedFlushPrompts { const rubric = renderPathsRubric(options.scope); return { prompt: DEFAULT_MEMORY_FLUSH_PROMPT.replaceAll( FLUSH_PROMPT_RUBRIC_PLACEHOLDER, rubric ), systemPrompt: DEFAULT_MEMORY_FLUSH_SYSTEM_PROMPT.replaceAll( FLUSH_PROMPT_RUBRIC_PLACEHOLDER, rubric ), }; }