'use strict'; 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 output = () => ({ content: '已收到,我先按当前信息整理下一步。', confidence: 0.96, intent: '信息确认', reason: '恢复测试固定输出。', requiresHuman: false, profileUpdates: {}, tasks: [], alerts: [], citations: [], toolTrace: [], }); async function main() { const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'qiwei-generation-recovery-')); const db = new AgentWorkbenchDb(path.join(dir, 'workbench.db'), { globalPaused: false, defaultMode: 'autopilot' }); let calls = 0; const sent = []; const service = new AgentWorkbenchService({ db, agent: { modelClient: { isConfigured: () => true }, async run() { calls += 1; if (calls === 1) throw new Error('fixture fatal generation failure'); return output(); }, }, qiwei: { isConfigured: () => true, async sendText(contactId, content) { sent.push({ contactId, content }); return { isSendSuccess: true, msgServerId: `recovery-out-${sent.length}` }; }, }, config: { accountKey: 'generation-recovery-fixture', agent: { provider: 'fixture', model: 'fixture', apiKey: 'fixture', generationRetryAttempts: 1, generationRetryBaseMs: 0, qualityPassScore: 60, generationRecovery: { pendingAgeMs: 5000, retryCooldownMs: 5000, maxAgeMs: 3600000 }, }, qiwei: { allowedSenders: ['recovery-contact'] }, memory: { enabled: false }, }, }); try { const queued = await service.ingestInbound({ externalId: 'generation-recovery-in-1', contactId: 'recovery-contact', contactName: '恢复测试', content: '请介绍一下服务', }, { awaitGeneration: false, allowAutoSend: true }); assert.equal(queued.status, 'generation_queued'); for (let i = 0; i < 50 && !db.listAudit(100).some(item => item.action === 'agent_failed'); i += 1) { await new Promise(resolve => setTimeout(resolve, 5)); } const conversation = db.getConversationByContactId('recovery-contact'); const inbound = db.getLatestInbound(conversation.id); assert.equal(inbound.status, 'generation_failed', '后台失败必须留下可恢复状态'); assert.equal(sent.length, 0); const recovered = await service.recoverPendingGenerations('smoke:recovery'); assert.equal(recovered.recovered, 1, '下一轮恢复必须认领并处理失败入站'); assert.equal(sent.length, 1, '恢复成功后必须只发送一次'); assert.equal(db.getMessage(inbound.id).status, 'reply_sent'); assert.equal(db.listAudit(100).some(item => item.action === 'agent_generation_recovery_attempt'), true); assert.equal(db.listAudit(100).some(item => item.action === 'agent_generation_recovery_completed'), true); process.stdout.write(JSON.stringify({ status: 'passed', checks: [ '异步生成失败持久化为 generation_failed', '下一轮恢复扫描重新生成并自动发送', '恢复链路保持单次出站与审计记录', ] }, null, 2) + '\n'); } finally { service.stopBackgroundWorkers(); db.close(); fs.rmSync(dir, { recursive: true, force: true }); } } main().catch(error => { process.stderr.write(JSON.stringify({ status: 'failed', message: error.message, stack: error.stack }, null, 2) + '\n'); process.exitCode = 1; });