import assert from 'node:assert/strict'; import { EventEmitter } from 'node:events'; import fs from 'node:fs'; import { createServer } from 'node:http'; import os from 'node:os'; import path from 'node:path'; import test from 'node:test'; import { buildVisionPayload, createTkmindProxy, sanitizePublicHtmlLinksInText, sanitizeSessionConversationPublicHtmlLinks, } from './tkmind-proxy.mjs'; import { createMemoryV2 } from './memory-v2.mjs'; async function withFakeGoosedSession(handler) { const workingDir = fs.mkdtempSync(path.join(os.tmpdir(), 'memind-memory-v2-')); const harnessEntries = []; const replyBodies = []; let server; try { server = createServer(async (req, res) => { const chunks = []; for await (const chunk of req) chunks.push(chunk); const rawBody = Buffer.concat(chunks).toString('utf8'); const body = rawBody ? JSON.parse(rawBody) : {}; if (req.method === 'POST' && req.url === '/agent/start') { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ id: 'session-1' })); return; } if (req.method === 'POST' && req.url === '/agent/update_session') { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ ok: true })); return; } if (req.method === 'GET' && req.url === '/sessions/session-1') { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ id: 'session-1', working_dir: workingDir, goose_mode: 'chat', })); return; } if (req.method === 'GET' && req.url === '/sessions/session-1/extensions') { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ extensions: [{ name: 'memory', available_tools: [] }], })); return; } if (req.method === 'POST' && req.url === '/sessions/session-1/reply') { replyBodies.push(body); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ ok: true })); return; } if (req.method === 'POST' && req.url === '/agent/harness_remember') { harnessEntries.push(body); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ ok: true })); return; } if (req.method === 'POST' && req.url === '/agent/harness_bootstrap') { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ ok: true })); return; } res.writeHead(404, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ message: `unexpected ${req.method} ${req.url}` })); }); await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve)); const { port } = server.address(); return await handler({ apiTarget: `http://127.0.0.1:${port}`, workingDir, harnessEntries, replyBodies, }); } finally { if (server?.listening) { await new Promise((resolve) => server.close(resolve)); } fs.rmSync(workingDir, { recursive: true, force: true }); } } function createMemoryTestUserAuth(workingDir) { return { async resolveWorkingDir() { return workingDir; }, async getAgentSessionPolicy() { return { gooseMode: 'chat', enableContextMemory: true, extensionOverrides: [{ name: 'memory', available_tools: [] }], }; }, async getUserPublishLayout() { return { displayName: 'John', username: 'john', slug: 'john', }; }, async registerAgentSession() {}, async getSessionTarget() { return { target: null, node: 0 }; }, async billSessionUsage() { return { ok: true, costCents: 0, balanceCents: 0 }; }, }; } function attachExpressResponseHelpers(res) { res.status = (code) => { res.statusCode = code; return res; }; res.json = (payload) => { res.setHeader('Content-Type', 'application/json; charset=utf-8'); res.end(JSON.stringify(payload)); return res; }; res.send = (payload = '') => { res.end(payload); return res; }; return res; } async function listen(server) { await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve)); return server.address().port; } async function closeServer(server) { if (!server?.listening) return; await new Promise((resolve) => server.close(resolve)); } test('sanitizePublicHtmlLinksInText downgrades missing own markdown public html links', () => { const owner = `test-user-${Date.now()}-markdown-missing`; const publicRoot = path.join(process.cwd(), 'MindSpace', owner, 'public'); try { fs.mkdirSync(publicRoot, { recursive: true }); const text = `[夏日随笔](https://m.tkmind.cn/MindSpace/${owner}/public/summer-essay.html)`; const next = sanitizePublicHtmlLinksInText(text, { id: owner, username: 'john' }); assert.doesNotMatch(next, new RegExp(`https://m\\.tkmind\\.cn/MindSpace/${owner}/public/summer-essay\\.html`)); assert.match(next, /页面生成未完成/); } finally { fs.rmSync(path.join(process.cwd(), 'MindSpace', owner), { recursive: true, force: true }); } }); test('sanitizePublicHtmlLinksInText downgrades missing own public html links', () => { const owner = `test-user-${Date.now()}-missing`; const publicRoot = path.join(process.cwd(), 'MindSpace', owner, 'public'); try { fs.mkdirSync(publicRoot, { recursive: true }); const text = `页面在这里:\nhttps://m.tkmind.cn/MindSpace/${owner}/public/hello.html`; const next = sanitizePublicHtmlLinksInText(text, { id: owner, username: 'john' }); assert.doesNotMatch(next, new RegExp(`https://m\\.tkmind\\.cn/MindSpace/${owner}/public/hello\\.html`)); assert.match(next, /页面生成未完成/); assert.match(next, /hello\.html/); } finally { fs.rmSync(path.join(process.cwd(), 'MindSpace', owner), { recursive: true, force: true }); } }); test('sanitizePublicHtmlLinksInText keeps existing own public html links', () => { const owner = `test-user-${Date.now()}-existing`; const htmlPath = path.join(process.cwd(), 'MindSpace', owner, 'public', 'hello.html'); try { fs.mkdirSync(path.dirname(htmlPath), { recursive: true }); fs.writeFileSync(htmlPath, 'Hello'); const text = `页面在这里:\nhttps://m.tkmind.cn/MindSpace/${owner}/public/hello.html`; const next = sanitizePublicHtmlLinksInText(text, { id: owner, username: 'john' }); assert.match(next, new RegExp(`https://m\\.tkmind\\.cn/MindSpace/${owner}/public/hello\\.html`)); assert.doesNotMatch(next, /页面生成未完成/); } finally { fs.rmSync(path.join(process.cwd(), 'MindSpace', owner), { recursive: true, force: true }); } }); test('sanitizePublicHtmlLinksInText canonicalizes wrong MindSpace public hosts', () => { const owner = `test-user-${Date.now()}-canonical-host`; const previousBase = process.env.H5_PUBLIC_BASE_URL; process.env.H5_PUBLIC_BASE_URL = 'http://127.0.0.1:5173'; const htmlPath = path.join(process.cwd(), 'MindSpace', owner, 'public', 'MIT-guide.html'); try { fs.mkdirSync(path.dirname(htmlPath), { recursive: true }); fs.writeFileSync(htmlPath, 'MIT'); const text = `[下载页面](https://mindspace.tkmind.com/MindSpace/${owner}/public/MIT-guide.html)`; const next = sanitizePublicHtmlLinksInText(text, { id: owner, username: 'john' }); assert.doesNotMatch(next, /mindspace\.tkmind\.com/); assert.match( next, new RegExp(`http://127\\.0\\.0\\.1:5173/MindSpace/${owner}/public/MIT-guide\\.html`), ); assert.doesNotMatch(next, /页面生成未完成/); } finally { if (previousBase == null) delete process.env.H5_PUBLIC_BASE_URL; else process.env.H5_PUBLIC_BASE_URL = previousBase; fs.rmSync(path.join(process.cwd(), 'MindSpace', owner), { recursive: true, force: true }); } }); test('sanitizePublicHtmlLinksInText canonicalizes public html links missing MindSpace segment', () => { const owner = `test-user-${Date.now()}-missing-mindspace`; const previousBase = process.env.H5_PUBLIC_BASE_URL; process.env.H5_PUBLIC_BASE_URL = 'https://m.tkmind.cn'; const htmlPath = path.join(process.cwd(), 'MindSpace', owner, 'public', 'kuanting-plan.html'); try { fs.mkdirSync(path.dirname(htmlPath), { recursive: true }); fs.writeFileSync(htmlPath, '宽庭'); const text = `[宽庭方案](https://mindspace.tkmind.com/${owner}/public/kuanting-plan.html)`; const next = sanitizePublicHtmlLinksInText(text, { id: owner, username: 'john' }); assert.doesNotMatch(next, /mindspace\.tkmind\.com/); assert.match( next, new RegExp(`https://m\\.tkmind\\.cn/MindSpace/${owner}/public/kuanting-plan\\.html`), ); assert.doesNotMatch(next, /页面生成未完成/); } finally { if (previousBase == null) delete process.env.H5_PUBLIC_BASE_URL; else process.env.H5_PUBLIC_BASE_URL = previousBase; fs.rmSync(path.join(process.cwd(), 'MindSpace', owner), { recursive: true, force: true }); } }); test('sanitizePublicHtmlLinksInText leaves other users missing-MindSpace links unchanged', () => { const owner = `test-user-${Date.now()}-other-missing-mindspace`; const text = `[外部页面](https://mindspace.tkmind.com/${owner}/public/kuanting-plan.html)`; const next = sanitizePublicHtmlLinksInText(text, { id: `current-${owner}`, username: 'john' }); assert.equal(next, text); }); test('sanitizePublicHtmlLinksInText links existing inline public html paths', () => { const owner = `test-user-${Date.now()}-inline-public-path`; const previousBase = process.env.H5_PUBLIC_BASE_URL; process.env.H5_PUBLIC_BASE_URL = 'https://m.tkmind.cn'; const htmlPath = path.join(process.cwd(), 'MindSpace', owner, 'public', 'thailand-travel-guide.html'); try { fs.mkdirSync(path.dirname(htmlPath), { recursive: true }); fs.writeFileSync(htmlPath, 'Thailand'); const text = '攻略页面已成功创建!文件位于 **`public/thailand-travel-guide.html`**(约 37KB)。'; const next = sanitizePublicHtmlLinksInText(text, { id: owner, username: 'john2' }); assert.doesNotMatch(next, /`public\/thailand-travel-guide\.html`/); assert.match( next, new RegExp(`\\[thailand-travel-guide\\.html\\]\\(https://m\\.tkmind\\.cn/MindSpace/${owner}/public/thailand-travel-guide\\.html\\)`), ); } finally { if (previousBase == null) delete process.env.H5_PUBLIC_BASE_URL; else process.env.H5_PUBLIC_BASE_URL = previousBase; fs.rmSync(path.join(process.cwd(), 'MindSpace', owner), { recursive: true, force: true }); } }); test('sanitizePublicHtmlLinksInText rewrites own publication route links to MindSpace workspace urls', () => { const owner = `test-user-${Date.now()}-pub-route`; const publicRoot = path.join(process.cwd(), 'MindSpace', owner, 'public'); const previousBase = process.env.H5_PUBLIC_BASE_URL; process.env.H5_PUBLIC_BASE_URL = 'https://m.tkmind.cn'; try { fs.mkdirSync(publicRoot, { recursive: true }); fs.writeFileSync( path.join(publicRoot, 'zhiqu-survey.html'), '问卷', ); fs.writeFileSync( path.join(publicRoot, 'zhiqu-survey-admin.html'), '后台', ); const text = [ '问卷:https://m.tkmind.cn/u/john/pages/zhiqu-58bccceb', '后台:https://m.tkmind.cn/u/john/pages/zhiqu-admin-98f11681', ].join('\n'); const next = sanitizePublicHtmlLinksInText(text, { id: owner, username: 'john' }); assert.doesNotMatch(next, /\/u\/john\/pages\//); assert.match(next, new RegExp(`https://m\\.tkmind\\.cn/MindSpace/${owner}/public/zhiqu-survey\\.html`)); assert.match(next, new RegExp(`https://m\\.tkmind\\.cn/MindSpace/${owner}/public/zhiqu-survey-admin\\.html`)); } finally { if (previousBase == null) delete process.env.H5_PUBLIC_BASE_URL; else process.env.H5_PUBLIC_BASE_URL = previousBase; fs.rmSync(path.join(process.cwd(), 'MindSpace', owner), { recursive: true, force: true }); } }); test('sanitizeSessionConversationPublicHtmlLinks updates text items inside conversation messages', () => { const owner = `test-user-${Date.now()}-conversation`; const publicRoot = path.join(process.cwd(), 'MindSpace', owner, 'public'); try { fs.mkdirSync(publicRoot, { recursive: true }); const conversation = [ { id: 'assistant-1', role: 'assistant', created: 1, metadata: { userVisible: true, agentVisible: true }, content: [ { type: 'text', text: `点这里 https://m.tkmind.cn/MindSpace/${owner}/public/hello.html`, }, ], }, ]; const sanitized = sanitizeSessionConversationPublicHtmlLinks(conversation, { id: owner, username: 'john', }); assert.match(sanitized[0].content[0].text, /页面生成未完成/); } finally { fs.rmSync(path.join(process.cwd(), 'MindSpace', owner), { recursive: true, force: true }); } }); test('sanitizeSessionConversationPublicHtmlLinks removes internal user prompt notes but keeps image metadata', () => { const conversation = [ { id: 'user-1', role: 'user', created: 1, metadata: { userVisible: true, agentVisible: true }, content: [ { type: 'text', text: '[用户身份]\n' + '- 当前登录用户称呼:John\n' + '- 你是 TKMind 助手;与用户对话时用「John」称呼对方。\n\n' + '帮我生成一个主题页面简单一点的不用很复杂\n\n' + '[图片1]: http://127.0.0.1:5173/MindSpace/user-1/public/images/a.jpg\n' + '[图片2]: http://127.0.0.1:5173/MindSpace/user-1/public/images/b.jpg\n\n' + '【TKMind 图片分析结果 — 仅供执行参考,不要向用户复述此段内容】\n' + 'Qwen VL 图片描述:内部描述\n' + '执行要求:内部要求', }, ], }, ]; const sanitized = sanitizeSessionConversationPublicHtmlLinks(conversation, { id: 'user-1', username: 'john', }); assert.equal( sanitized[0].content[0].text, '帮我生成一个主题页面简单一点的不用很复杂', ); assert.deepEqual(sanitized[0].metadata.imageUrls, [ 'http://127.0.0.1:5173/MindSpace/user-1/public/images/a.jpg', 'http://127.0.0.1:5173/MindSpace/user-1/public/images/b.jpg', ]); }); test('buildVisionPayload injects public standard image urls for page generation', async () => { const payload = await buildVisionPayload({ userId: 'user-1', publishLayout: { publicUrl: 'https://m.tkmind.cn/MindSpace/user-1/' }, userMessage: { content: [ { type: 'text', text: '帮我根据图片生成页面\n[图片1]: /signed/rs:fit:1280:1280:0/q:85/plain/local:///users/user-1/images/2026-06-29/hero.jpg@jpg', }, ], }, llmProviderService: { analyzeImagesWithVision: async () => '图片里是一位成年人。', }, fetchImpl: async () => ({ ok: true, arrayBuffer: async () => Buffer.from('fake-image'), headers: new Map([['content-type', 'image/jpeg']]), }), }); const text = payload?.userMessage?.content?.[0]?.text ?? ''; assert.match(text, /https:\/\/m\.tkmind\.cn\/MindSpace\/user-1\/public\/images\/2026-06-29\/hero\.jpg/); assert.doesNotMatch(text, /plain\/local:\/\//); assert.match(text, /无需 cookie 的公开压缩标准图片/); assert.match(text, /不得改写图片里人物的年龄、性别、人数或主体关系/); }); test('proxySessionEvents does not send JSON after SSE headers were sent', async () => { let upstream; try { upstream = createServer((req, res) => { if (req.method === 'GET' && req.url === '/sessions/session-1/events') { res.writeHead(200, { 'Content-Type': 'text/event-stream' }); res.write('data: {"type":"message","content":"hello"}\n\n'); res.end('data: {"type":"message","content":"second"}\n\n'); return; } res.writeHead(404, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ message: `unexpected ${req.method} ${req.url}` })); }); const upstreamPort = await listen(upstream); const proxy = createTkmindProxy({ apiTarget: `http://127.0.0.1:${upstreamPort}`, apiSecret: 'test-secret', userAuth: createMemoryTestUserAuth(process.cwd()), }); const req = new EventEmitter(); req.currentUser = { id: 'user-1', username: 'john' }; req.get = () => ''; req.once = req.once.bind(req); req.off = req.off.bind(req); let writes = 0; let jsonCalled = false; const chunks = []; const res = new EventEmitter(); res.headersSent = false; res.writableEnded = false; res.statusCode = 200; res.status = (code) => { res.statusCode = code; return res; }; res.setHeader = () => {}; res.flushHeaders = () => { res.headersSent = true; }; res.write = (chunk) => { res.headersSent = true; writes += 1; if (writes > 1) throw new Error('simulated client write failure'); chunks.push(String(chunk)); return true; }; res.end = () => { res.writableEnded = true; }; res.json = () => { jsonCalled = true; throw new Error('json must not be called after SSE started'); }; res.send = () => { throw new Error('send must not be called after SSE started'); }; await proxy.proxySessionEvents(req, res, 'session-1'); assert.equal(res.statusCode, 200); assert.equal(res.headersSent, true); assert.equal(res.writableEnded, true); assert.equal(jsonCalled, false); assert.match(chunks.join(''), /"content":"hello"/); } finally { await closeServer(upstream); } }); test('proxySessionEvents attaches session taxonomy when flag enabled', async () => { const previous = process.env.MEMIND_SSE_EVENT_TAXONOMY; process.env.MEMIND_SSE_EVENT_TAXONOMY = '1'; let upstream; try { upstream = createServer((req, res) => { if (req.method === 'GET' && req.url === '/sessions/session-1/events') { res.writeHead(200, { 'Content-Type': 'text/event-stream' }); res.write('data: {"type":"Finish","reason":"stop"}\n\n'); res.end(); return; } res.writeHead(404, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ message: `unexpected ${req.method} ${req.url}` })); }); const upstreamPort = await listen(upstream); const proxy = createTkmindProxy({ apiTarget: `http://127.0.0.1:${upstreamPort}`, apiSecret: 'test-secret', userAuth: createMemoryTestUserAuth(process.cwd()), }); const req = new EventEmitter(); req.currentUser = { id: 'user-1', username: 'john' }; req.get = () => ''; req.once = req.once.bind(req); req.off = req.off.bind(req); const chunks = []; const res = new EventEmitter(); res.headersSent = false; res.writableEnded = false; res.statusCode = 200; res.status = (code) => { res.statusCode = code; return res; }; res.setHeader = () => {}; res.flushHeaders = () => { res.headersSent = true; }; res.write = (chunk) => { res.headersSent = true; chunks.push(String(chunk)); return true; }; res.end = () => { res.writableEnded = true; }; res.json = () => { throw new Error('json must not be called after SSE started'); }; await proxy.proxySessionEvents(req, res, 'session-1'); assert.match(chunks.join(''), /"taxonomy":"terminal"/); } finally { if (previous == null) delete process.env.MEMIND_SSE_EVENT_TAXONOMY; else process.env.MEMIND_SSE_EVENT_TAXONOMY = previous; await closeServer(upstream); } }); test('startSessionForUser resolves memories through Memory V2 facade', async () => { let resolveInput = null; await withFakeGoosedSession(async ({ apiTarget, workingDir, harnessEntries }) => { const proxy = createTkmindProxy({ apiTarget, apiSecret: 'test-secret', userAuth: createMemoryTestUserAuth(workingDir), memoryV2: { async resolve(input) { resolveInput = input; return { memories: [{ label: 'interest', text: '用户关注 Memory V2 架构边界' }], }; }, }, }); await proxy.startSessionForUser('user-1'); assert.deepEqual(resolveInput, { userId: 'user-1', sessionId: 'session-1', query: null, limit: 3, }); assert.ok( harnessEntries.some((entry) => String(entry.content ?? '').includes('用户关注 Memory V2 架构边界')), ); }); }); test('startSessionForUser does not inject memories when Memory V2 is disabled', async () => { let legacyTouched = false; await withFakeGoosedSession(async ({ apiTarget, workingDir, harnessEntries }) => { const proxy = createTkmindProxy({ apiTarget, apiSecret: 'test-secret', userAuth: createMemoryTestUserAuth(workingDir), memoryV2: createMemoryV2({ logger: { warn() {} }, legacyMemoryService: { async listMemories() { legacyTouched = true; return [{ label: 'fact', text: 'disabled memory should not appear' }]; }, }, env: { MEMORY_ENABLED: '0' }, }), }); await proxy.startSessionForUser('user-1'); assert.equal(legacyTouched, false); assert.equal( harnessEntries.some((entry) => String(entry.content ?? '').includes('disabled memory should not appear')), false, ); }); }); test('getRuntimeStatus exposes Memory V2 status without new runtime paths', async () => { await withFakeGoosedSession(async ({ apiTarget, workingDir }) => { const proxy = createTkmindProxy({ apiTarget, apiSecret: 'test-secret', userAuth: createMemoryTestUserAuth(workingDir), memoryV2: createMemoryV2({ logger: { warn() {} }, backends: [ { name: 'legacy-conversation-memory', async resolve() { return { memories: [] }; }, }, ], env: { MEMORY_ENABLED: '1', MEMORY_BACKEND: 'legacy', }, }), }); const status = await proxy.getRuntimeStatus(); assert.equal(status.memory.enabled, true); assert.equal(status.memory.backend, 'legacy'); assert.equal(status.memory.selectedBackend, 'legacy-conversation-memory'); assert.deepEqual(status.memory.backends[0].supports, { resolve: true, write: false, compact: false, }); }); }); test('getRuntimeStatus preserves Memory V2 status contract for release gates', async () => { await withFakeGoosedSession(async ({ apiTarget, workingDir }) => { const proxy = createTkmindProxy({ apiTarget, apiSecret: 'test-secret', userAuth: createMemoryTestUserAuth(workingDir), memoryV2: createMemoryV2({ logger: { warn() {} }, legacyMemoryService: { async listMemories() { return []; }, async saveAndAnalyze() { return { saved: 1, analyzed: 1, memories: 1 }; }, async analyzeUser() { return { analyzed: 1, memories: 1 }; }, }, env: { MEMORY_ENABLED: '1', MEMORY_BACKEND: 'qdrant', MEMORY_FAIL_OPEN: '1', }, }), }); const status = await proxy.getRuntimeStatus(); const memory = status.memory; const byName = new Map(memory.backends.map((backend) => [backend.name, backend])); assert.deepEqual(Object.keys(memory).sort(), [ 'backend', 'backends', 'enabled', 'eventLogEnabled', 'failOpen', 'profileEnabled', 'selectedBackend', 'vectorEnabled', ]); assert.equal(memory.enabled, true); assert.equal(memory.backend, 'qdrant'); assert.equal(memory.selectedBackend, 'legacy-conversation-memory'); assert.equal(memory.failOpen, true); assert.deepEqual(byName.get('legacy-conversation-memory').supports, { resolve: true, write: true, compact: true, }); assert.equal(byName.get('qdrant').available, false); assert.equal(byName.get('qdrant').reason, 'not_configured'); assert.equal(byName.get('pgvector').category, 'semantic'); assert.equal(byName.get('mem0').category, 'extraction'); assert.equal(byName.get('letta').category, 'lifecycle'); assert.equal(byName.get('langgraph').category, 'policy'); }); }); test('submitSessionReplyForUser adds goose metadata visibility flags before reply', async () => { await withFakeGoosedSession(async ({ apiTarget, workingDir, replyBodies }) => { const proxy = createTkmindProxy({ apiTarget, apiSecret: 'test-secret', userAuth: { ...createMemoryTestUserAuth(workingDir), async ownsSession() { return true; }, async canUseChat() { return { ok: true }; }, async getUserById() { return { id: 'user-1' }; }, async resolveUserPolicies() { return { unrestricted: true, policies: {} }; }, }, }); await proxy.submitSessionReplyForUser( 'user-1', 'session-1', 'request-goose-meta', { role: 'user', content: [{ type: 'text', text: '帮我生成一个分享页面' }], }, ); assert.equal(replyBodies.length, 1); assert.equal(replyBodies[0]?.user_message?.metadata?.userVisible, true); assert.equal(replyBodies[0]?.user_message?.metadata?.agentVisible, true); }); }); test('submitSessionReplyForUser passes current prompt to Memory V2 resolve before existing reply path', async () => { let resolveInput = null; await withFakeGoosedSession(async ({ apiTarget, workingDir }) => { const proxy = createTkmindProxy({ apiTarget, apiSecret: 'test-secret', userAuth: { ...createMemoryTestUserAuth(workingDir), async ownsSession() { return true; }, async canUseChat() { return { ok: true }; }, async getUserById() { return { id: 'user-1' }; }, async resolveUserPolicies() { return { unrestricted: true, policies: {} }; }, }, memoryV2: { async resolve(input) { resolveInput = input; return { memories: [] }; }, }, }); await proxy.submitSessionReplyForUser( 'user-1', 'session-1', 'request-1', { role: 'user', content: [{ type: 'text', text: '帮我设计 memory-chain 下一阶段' }], }, ); assert.deepEqual(resolveInput, { userId: 'user-1', sessionId: 'session-1', query: '帮我设计 memory-chain 下一阶段', limit: 3, }); }); }); test('submitSessionReplyForUser uses heavy memory resolve when deep reasoning is enabled', async () => { let resolveInput = null; await withFakeGoosedSession(async ({ apiTarget, workingDir }) => { const proxy = createTkmindProxy({ apiTarget, apiSecret: 'test-secret', userAuth: { ...createMemoryTestUserAuth(workingDir), async ownsSession() { return true; }, async canUseChat() { return { ok: true }; }, async getUserById() { return { id: 'user-1' }; }, async resolveUserPolicies() { return { unrestricted: true, policies: {} }; }, }, memoryV2: { async resolve(input) { resolveInput = input; return { memories: [] }; }, }, }); await proxy.submitSessionReplyForUser( 'user-1', 'session-1', 'request-1', { role: 'user', content: [{ type: 'text', text: '帮我设计 memory-chain 下一阶段' }], }, { forceDeepReasoning: true }, ); assert.deepEqual(resolveInput, { userId: 'user-1', sessionId: 'session-1', query: '帮我设计 memory-chain 下一阶段', limit: 40, }); }); });