'use strict'; // Regression coverage for the relay callback boundary. The callback must // return before model generation completes, while the queued job still owns // the normal quality/delivery path. const assert = require('assert/strict'); const fs = require('fs'); const os = require('os'); const path = require('path'); const { AgentWorkbenchDb } = require('../mcp/src/core/agent-workbench-db'); const { AgentWorkbenchService } = require('../mcp/src/core/agent-workbench-service'); const wait = ms => new Promise(resolve => setTimeout(resolve, ms)); async function waitFor(check, timeoutMs = 1000) { const deadline = Date.now() + timeoutMs; while (Date.now() < deadline) { if (check()) return; await wait(10); } assert.equal(check(), true, '后台生成在限定时间内未完成'); } function createFixture(agent) { const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'qiwei-callback-generation-')); const db = new AgentWorkbenchDb(path.join(dir, 'workbench.db'), { defaultMode: 'autopilot', autoSendConfidence: 0.8, }); const sent = []; const service = new AgentWorkbenchService({ db, agent, qiwei: { isConfigured: () => true, async sendText(toId, content) { sent.push({ toId, content }); return { isSendSuccess: true, msgServerId: `callback-out-${sent.length}` }; }, }, config: { accountKey: 'callback-generation-smoke', agent: { provider: 'fixture', model: 'fixture', apiKey: 'fixture', generationRetryAttempts: 1, generationRetryBaseMs: 0, qualityPassScore: 60 }, qiwei: { allowedSenders: ['callback-contact'] }, memory: { enabled: false }, }, }); return { dir, db, service, sent }; } function output() { return { content: '已收到,我先按当前信息整理下一步。', confidence: 0.96, intent: '信息确认', reason: '异步回调测试固定输出。', requiresHuman: false, profileUpdates: {}, tasks: [], alerts: [], citations: [], toolTrace: [], }; } async function main() { let release; const generationGate = new Promise(resolve => { release = resolve; }); let calls = 0; const fixture = createFixture({ modelClient: { isConfigured: () => true }, async run() { calls += 1; await generationGate; return output(); }, }); try { const startedAt = Date.now(); const queued = await fixture.service.ingestInbound({ externalId: 'callback-async-1', contactId: 'callback-contact', contactName: '回调异步测试', content: '请介绍一下服务', }, { awaitGeneration: false, allowAutoSend: true }); assert.equal(queued.status, 'generation_queued'); assert(Date.now() - startedAt < 300, '回调不应等待模型生成'); assert.equal(fixture.sent.length, 0, '模型完成前不能发送'); await waitFor(() => calls === 1); release(); await waitFor(() => fixture.sent.length === 1); assert.equal(fixture.sent[0].toId, 'callback-contact'); assert.equal(fixture.db.listAudit(100).some(item => item.action === 'autopilot_message_sent'), true); } finally { fixture.service.stopBackgroundWorkers(); fixture.db.close(); fs.rmSync(fixture.dir, { recursive: true, force: true }); } const failed = createFixture({ modelClient: { isConfigured: () => true }, async run() { throw new Error('fixture generation contract error'); }, }); try { const queued = await failed.service.ingestInbound({ externalId: 'callback-async-failure', contactId: 'callback-contact', contactName: '回调失败测试', content: '普通问题', }, { awaitGeneration: false, allowAutoSend: true }); assert.equal(queued.status, 'generation_queued'); await waitFor(() => failed.db.listAudit(100).some(item => item.action === 'agent_failed')); assert.equal(failed.sent.length, 0, '生成失败不得发送空消息'); } finally { failed.service.stopBackgroundWorkers(); failed.db.close(); fs.rmSync(failed.dir, { recursive: true, force: true }); } process.stdout.write(JSON.stringify({ status: 'passed', checks: [ 'relay callback returns generation_queued before model completion', 'background generation preserves autopilot delivery', 'background failure is audited without crashing the callback', ] }, null, 2) + '\n'); } main().catch(error => { process.stderr.write(JSON.stringify({ status: 'failed', message: error.message, stack: error.stack }, null, 2) + '\n'); process.exitCode = 1; });