/** * End-to-end proof: a recall block produced by the REAL injection shape reaches * the SYSTEM prompt and does NOT reach any user/message content. * * Why this file exists next to `loop.test.js` * ------------------------------------------- * `loop.test.js` asserts the pure decision rewrite (`promoteSnapshotSections`). * That is necessary but not sufficient: the bug it fixes was that the rewrite * never fired on the real injection shape, and a unit test can only catch that * if it also encodes the real shape. This file closes the loop the other way — * it mounts the actual `HologramAgentLoop` on real dsh services (`SessionStore`, * `AgentRegistry`, `LlmRuntime`, `SystemPrompt`, `ToolRuntime`, * `SessionProjectionRegistry`), drives one real turn (pre-step → request build → * LLM call), and asserts against **the request object the adapter actually * received**, not an intermediate decision. * * Two directions, both required: * E1 positive — the recall text appears in the system prompt the adapter got. * E2 negative — the recall text appears in NO message body the adapter got * (every message, every block; not just the last one). * * Only E1 fails on the broken commit; E2 is already true there because the old * code left recall in history. E2 is what this fix is *for*: it must stay true * after the move, otherwise the promotion would duplicate recall rather than * relocate it. * * No network: the "adapter" is a local `LlmAdapter` subclass that records the * request and yields one text chunk. * * @module dsh-memory/test/loop-system-e2e */ import assert from 'node:assert/strict' import { createRequire } from 'node:module' import { pathToFileURL } from 'node:url' import test from 'node:test' import { Context } from '@deepseek-ai/cordis' import { AgentRegistry } from '@deepseek-ai/dsh-agent' import { LlmAdapter, LlmRuntime, createUserMessage } from '@deepseek-ai/dsh-llm' import { SessionStore } from '@deepseek-ai/dsh-session' import { SystemPrompt } from '@deepseek-ai/dsh-system-prompt' import { ToolRuntime } from '@deepseek-ai/dsh-tools' import { HologramAgentLoop } from '../lib/loop.js' /** * Load `SessionProjectionRegistry` the way the harness does. * * It is a peer of `dsh-agent-loop`, not of this package, so a bare specifier * only resolves when the repo's `node_modules` happens to link it. Resolving * from `dsh-agent-loop`'s own location is exactly what a real dsh process does; * if neither route works there is no harness to run against, and the test skips * loudly instead of asserting on a half-mounted app. * @returns {Promise} */ async function loadProjectionRegistry() { try { return (await import('@deepseek-ai/dsh-session-projection')).SessionProjectionRegistry } catch { try { const peerRequire = createRequire(import.meta.resolve('@deepseek-ai/dsh-agent-loop')) return (await import(pathToFileURL(peerRequire.resolve('@deepseek-ai/dsh-session-projection')).href)) .SessionProjectionRegistry } catch { return null } } } /** Every text the model would read out of a message body. */ function bodyText(message) { if (!Array.isArray(message?.content)) return '' return message.content.map((block) => (typeof block?.text === 'string' ? block.text : '')).join('') } /** The system prompt text as the adapter received it. */ function systemText(request) { return request.messages .filter((message) => message.role === 'system') .map((message) => bodyText(message)) .join('\n') } /** Every non-system message body, flattened, for the negative direction. */ function historyBlocks(request) { return request.messages .filter((message) => message.role !== 'system') .map((message, index) => ({ index, role: message.role, kind: message.source?.kind, text: bodyText(message) })) } test('E1/E2: recall reaches the system prompt and no message body', async (t) => { const SessionProjectionRegistry = await loadProjectionRegistry() if (SessionProjectionRegistry === null) { t.skip('@deepseek-ai/dsh-session-projection not resolvable from this install (no harness to mount)') return } const RECALL = 'RECALLED-NOTE-4471: user prefers evidence over summaries.' /** Records the request it is called with; yields one stop-finish text chunk. */ const captured = [] class CaptureAdapter extends LlmAdapter { async *stream(request) { captured.push(request) yield { type: 'block-start', index: 0, blockType: 'text' } yield { type: 'text-delta', index: 0, text: 'acknowledged' } yield { type: 'block-end', index: 0, block: { type: 'text', text: 'acknowledged' } } yield { type: 'finish', reason: 'stop' } } } const ctx = new Context() try { // Cordis defers service publication by a microtask; each `new` below needs // the previous one visible, so yield between mounts. new SessionStore(ctx) await new Promise((resolve) => setTimeout(resolve, 0)) new AgentRegistry(ctx) new SystemPrompt(ctx, {}) new ToolRuntime(ctx) new LlmRuntime(ctx) new SessionProjectionRegistry(ctx) await new Promise((resolve) => setTimeout(resolve, 0)) // The loop under test. Mount before any agent exists: `setFactory` runs in // the constructor and the class refuses a second registration. new HologramAgentLoop(ctx, { maxParallelToolCalls: 4, agents: [] }) await new Promise((resolve) => setTimeout(resolve, 0)) ctx.llm.registerAdapter(['capture'], new CaptureAdapter()) // The REAL injection shape, reproduced byte for byte from `lib/index.js:932`: // one text block whose text IS the section text, `form: 'snapshot'`. ctx.on( 'agent/pre-step', async (payload, next) => { const decision = await next() if (decision.kind === 'reject' || payload.signal.aborted) return decision return { ...decision, messages: [ ...decision.messages, createUserMessage({ content: [{ type: 'text', text: RECALL }], source: { kind: 'memory', form: 'snapshot', sections: [{ name: 'memory', text: RECALL }] }, }), ], } }, { prepend: true }, ) const handle = await ctx.agents.create({ sessionId: 'loop-system-e2e', agentOptions: { provider: 'capture', model: 'capture-1' }, }) const agent = handle.agent agent.followup( createUserMessage({ content: [{ type: 'text', text: 'what did we decide?' }], source: { kind: 'user' } }), ) await agent.whenIdle() assert.equal(captured.length, 1, `the adapter must be called exactly once; got ${captured.length} call(s)`) const request = captured[0] // ── E1 · positive: the recall is in the system prompt ──────────────── const system = systemText(request) assert.ok( system.includes(RECALL), `E1: recall must appear in the system prompt the adapter received. system prompt was ${JSON.stringify(system)}`, ) // ── E2 · negative: the recall is in NO message body ────────────────── const history = historyBlocks(request) const leaking = history.filter((entry) => entry.text.includes(RECALL)) assert.deepEqual( leaking, [], `E2: recall must not appear in ANY non-system message; leaking messages: ${JSON.stringify(leaking)}`, ) // E2b · the loop must not duplicate it either: exactly one system message // may carry it, so a "promote but keep" bug cannot hide behind E1. const carriers = request.messages.filter((message) => bodyText(message).includes(RECALL)) assert.equal( carriers.length, 1, `E2b: exactly one message may carry the recall (a move, not a copy); got ${carriers.length}: ${JSON.stringify( carriers.map((message) => message.role), )}`, ) assert.equal(carriers[0].role, 'system', `E2b: the sole carrier must be the system message; got ${carriers[0].role}`) } finally { await ctx.fiber.dispose() } })