const assert = require('assert/strict'); const fs = require('fs'); const os = require('os'); const path = require('path'); const messagesDir = fs.mkdtempSync(path.join(os.tmpdir(), 'qiwei-generic-intake-messages-')); process.env.QIWEI_MESSAGES_DIR = messagesDir; const { AgentWorkbenchDb } = require('../mcp/src/core/agent-workbench-db'); const { AgentWorkbenchService } = require('../mcp/src/core/agent-workbench-service'); const { evaluatePolledMessage } = require('../mcp/src/core/agent-poller-policy'); const { __testing } = require('../mcp/src/dashboard/agent-service'); const results = []; async function check(name, fn) { await fn(); results.push({ name, status: 'passed' }); } function createContext({ accountKey = 'account-a', mode = 'allowlist_only', welcomeEnabled = false, welcomeSendMode = 'draft', sendSuccess = true } = {}) { const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'qiwei-generic-intake-')); const db = new AgentWorkbenchDb(path.join(dir, 'workbench.db'), { globalPaused: false, defaultMode: 'review', autoSendConfidence: 0.88, personalIntakeMode: mode, welcomeEnabled, welcomeText: '您好,已经收到您的消息。', welcomeSendMode, }); if (mode === 'auto_enroll_autopilot') { db.setIntakePolicy({ mode, welcomeEnabled, welcomeText: '您好,已经收到您的消息。', welcomeSendMode, autopilotConfirmation: 'v1' }); } let currentSendSuccess = sendSuccess; const sent = []; const config = { accountKey, intake: { effectiveAutopilotMode: 'auto' }, memory: { enabled: false }, agent: { apiKey: 'smoke', model: 'stub', provider: 'stub' }, qiwei: { accountKey, allowedSenders: [], manualAllowedSenders: [], autoEnrolledSenders: [], intakeAutopilotConversationMode: 'auto' }, }; const qiwei = { isConfigured: () => true, async sendText(contactId, content) { sent.push({ contactId, content }); return { isSendSuccess: currentSendSuccess }; }, }; const agent = { modelClient: { isConfigured: () => true }, async run() { return { content: '普通 Agent 回复草稿', confidence: 0.5, intent: 'general', reason: 'smoke', requiresHuman: true, profileUpdates: {}, citations: [], toolTrace: [] }; }, }; const service = new AgentWorkbenchService({ db, agent, qiwei, config }); const poller = { status: () => ({ running: false }), stop() {} }; return { dir, db, config, service, sent, target: { config: config.qiwei, db, service, qiwei }, workbenchTarget: { config, db, service, poller }, setSendSuccess(value) { currentSendSuccess = value; }, close() { service.close?.(); db.close(); fs.rmSync(dir, { recursive: true, force: true }); }, }; } function inbound(contactId, overrides = {}) { return { msgType: 0, senderId: contactId, senderName: `联系人-${contactId}`, content: '你好,我想咨询一下', timestamp: Math.floor(Date.now() / 1000), seq: Math.floor(Math.random() * 1000000), msgServerId: `message-${contactId}-${Date.now()}-${Math.random()}`, ...overrides, }; } async function main() { await check('strict mode and group policy create no onboarding records', async () => { const ctx = createContext(); try { const strict = await __testing.ingestMessageForWorkbench(ctx.target, inbound('strict-contact'), 'smoke'); assert.equal(strict.status, 'ignored_not_allowlisted'); assert.equal(ctx.db.listConversations().length, 0); assert.equal(ctx.db.listOnboardings('account-a').length, 0); const group = evaluatePolledMessage(inbound('group-member', { fromRoomId: 'room-1', chatType: 'group' }), { intakeMode: 'auto_enroll_review', allowedSenders: [] }); assert.equal(group.reason, 'group_message'); } finally { ctx.close(); } }); await check('review auto enrollment creates one welcome draft with zero sends', async () => { const ctx = createContext({ mode: 'auto_enroll_review', welcomeEnabled: true }); try { const first = await __testing.ingestMessageForWorkbench(ctx.target, inbound('review-contact'), 'smoke'); assert.equal(first.status, 'pending_review'); assert.equal(ctx.db.getConversationByContactId('review-contact').mode, 'review'); assert.equal(ctx.sent.length, 0); const onboarding = ctx.db.getOnboarding('account-a', 'review-contact'); assert.ok(onboarding.first_message_id); assert.ok(onboarding.welcome_draft_id); const restarted = { ...ctx.config.qiwei, allowedSenders: ['review-contact'], autoEnrolledSenders: ['review-contact'] }; await __testing.ingestMessageForWorkbench({ ...ctx.target, config: restarted }, inbound('review-contact', { content: '继续了解办理流程' }), 'restart'); assert.equal(ctx.db.listDrafts({ conversationId: onboarding.conversation_id }).filter(item => item.intent === 'new_contact_welcome').length, 1); assert.equal(ctx.sent.length, 0); } finally { ctx.close(); } }); await check('generic automatic intake requires confirmation and maps to auto mode', async () => { const ctx = createContext(); try { assert.throws(() => __testing.updateIntakePolicyForWorkbench(ctx, { mode: 'auto_enroll_autopilot', welcomeEnabled: true, welcomeText: '欢迎', welcomeSendMode: 'send' }), /二次确认/); const policy = __testing.updateIntakePolicyForWorkbench(ctx, { mode: 'auto_enroll_autopilot', welcomeEnabled: true, welcomeText: '欢迎', welcomeSendMode: 'send', confirmation: 'ENABLE_AUTO_ENROLL_AUTOPILOT' }, 'smoke-admin'); assert.equal(policy.autopilotConfirmed, true); assert.equal(policy.effectiveConversationMode, 'auto'); } finally { ctx.close(); } }); await check('failed welcome can be retried once and then stays sent', async () => { const ctx = createContext({ mode: 'auto_enroll_autopilot', welcomeEnabled: true, welcomeSendMode: 'send', sendSuccess: false }); try { const first = await __testing.ingestMessageForWorkbench(ctx.target, inbound('send-contact'), 'smoke'); assert.equal(first.status, 'welcome_send_failed'); assert.equal(ctx.db.getConversationByContactId('send-contact').mode, 'auto'); assert.equal(ctx.db.getOnboarding('account-a', 'send-contact').attempt_count, 1); ctx.setSendSuccess(true); assert.equal((await ctx.service.retryOnboardingWelcome('send-contact', 'smoke-admin')).status, 'welcome_sent'); assert.equal(ctx.db.getOnboarding('account-a', 'send-contact').attempt_count, 2); await assert.rejects(() => ctx.service.retryOnboardingWelcome('send-contact', 'smoke-admin'), /明确发送失败/); } finally { ctx.close(); } }); await check('two accounts keep manual, automatic and listener state isolated', async () => { const accountA = createContext({ accountKey: 'account-a' }); const accountB = createContext({ accountKey: 'account-b', mode: 'auto_enroll_review' }); try { __testing.updateAllowlistForWorkbench(accountA.workbenchTarget, { contactIds: ['contact-a'], autoStart: true }); assert.deepEqual(accountA.config.qiwei.allowedSenders, ['contact-a']); assert.deepEqual(accountB.config.qiwei.allowedSenders, []); assert.equal(accountA.db.getSetting('listener_enabled', 'false'), 'true'); assert.equal(accountB.db.getSetting('listener_enabled', 'false'), 'false'); await __testing.ingestMessageForWorkbench(accountB.target, inbound('contact-b'), 'smoke-b'); __testing.hydrateAccountAllowlist(accountA.config, accountA.db); __testing.hydrateAccountAllowlist(accountB.config, accountB.db); assert.deepEqual(accountA.config.qiwei.allowedSenders, ['contact-a']); assert.deepEqual(accountB.config.qiwei.allowedSenders, ['contact-b']); } finally { accountA.close(); accountB.close(); } }); console.log(JSON.stringify({ status: 'ok', passed: results.length, results }, null, 2)); } main() .finally(() => fs.rmSync(messagesDir, { recursive: true, force: true })) .catch(error => { console.error(error); process.exitCode = 1; });