import assert from 'node:assert/strict'; import { EventEmitter } from 'node:events'; import test from 'node:test'; import { loadConfig } from '../src/config.js'; import { AgentBusListener, type AgentBusSocket, type AgentBusTaskGateway, createTaskResultFrame, createTaskProgressFrame, isInboundAgentBusTask, parseAgentBusFrame, sessionFromReadyFrame, taskAttachmentReferences, taskResultStatus, taskResultText } from '../src/agentbus.js'; import { mergeRuntimeChannelStatuses, type PublicAgentBusChannel } from '../src/agentbus-channels.js'; import type { AgentBusDelivery, PublicTask } from '../src/task-service.js'; function makeTask(status: string, overrides: Partial = {}): PublicTask { return { organization_id: 'org-1', task_id: 'TASK-1', source: 'manual', channel_id: null, channel_name: null, raw_text: '测试指令', conversation_id: 'conversation-1', agent_session: { status: 'active', turn_no: 1, recovery_count: 0, session_id_present: false }, input_request: null, important_message: null, operation: null, parse_response: null, result: null, summary: null, status, stage: 'parse', message: '任务状态已更新。', error: '', failure: null, success_receipt: null, error_summary: null, handoff_status: '', confirmation_mode: 'manual', confirmed_at: null, created_at: new Date().toISOString(), updated_at: new Date().toISOString(), last_event_id: null, events: [], ...overrides }; } class FakeSocket { readyState = 0; readonly sent: Record[] = []; private readonly handlers = new Map void>>(); on(event: string, listener: (...args: any[]) => void): this { const listeners = this.handlers.get(event) || []; listeners.push(listener); this.handlers.set(event, listeners); return this; } emit(event: string, ...args: any[]): void { for (const listener of this.handlers.get(event) || []) listener(...args); } send(data: string, callback?: (error?: Error) => void): void { this.sent.push(JSON.parse(data) as Record); callback?.(); } close(): void { this.readyState = 3; this.emit('close', 1000, Buffer.alloc(0)); } } function testConfig() { return loadConfig({ NODE_ENV: 'test', FIELD_ENCRYPTION_KEY: Buffer.alloc(32, 4).toString('base64'), AGENTBUS_ENABLED: 'true', AGENTBUS_WS_URL: 'wss://mesh.nianxx.cn/ws', AGENTBUS_WS_TOKEN: 'test-ws-token', AGENTBUS_BOT_ADDRESS: 'bot:test:listener', AGENTBUS_WS_RECONNECT_DELAY: '1ms', AGENTBUS_TASK_TIMEOUT_MS: '1000', AGENTBUS_LOG_PAYLOADS: 'true' }); } function publicChannelFixture(overrides: Partial = {}): PublicAgentBusChannel { return { id: 'channel-1', organization_id: 'org-1', channel_type: 'agentbus', display_name: '外部用户 A', external_user_ref: 'external-user-a', agentbus_bot_address: 'bot:test:listener', enabled: true, status: 'disabled', key_configured: true, last_connected_at: null, last_error: null, created_at: new Date().toISOString(), updated_at: new Date().toISOString(), ...overrides }; } test('AgentBus configuration stays disabled until connection fields are supplied', () => { const config = loadConfig({ NODE_ENV: 'test', FIELD_ENCRYPTION_KEY: Buffer.alloc(32, 5).toString('base64') }); assert.equal(config.agentBusEnabled, false); assert.equal(config.AGENTBUS_WS_RECONNECT_DELAY, 5_000); assert.equal(config.AGENTBUS_LOG_PAYLOADS, false); }); test('AgentBus auto configuration rejects partial credentials', () => { assert.throws(() => loadConfig({ NODE_ENV: 'test', FIELD_ENCRYPTION_KEY: Buffer.alloc(32, 6).toString('base64'), AGENTBUS_WS_TOKEN: 'partial-token' }), /AGENTBUS_WS_URL/); }); test('database-managed channels may use the global WebSocket URL without a global key', () => { const config = loadConfig({ NODE_ENV: 'test', FIELD_ENCRYPTION_KEY: Buffer.alloc(32, 7).toString('base64'), AGENTBUS_ENABLED: 'true', AGENTBUS_WS_URL: 'wss://mesh.nianxx.cn/ws' }); assert.equal(config.agentBusEnabled, true); assert.equal(config.AGENTBUS_WS_TOKEN, undefined); }); test('channel directory prefers the current listener over stale persisted disabled status', () => { const channel = publicChannelFixture({ status: 'disabled', last_error: '旧进程状态' }); const connected = mergeRuntimeChannelStatuses([channel], [{ channel_id: channel.id, enabled: true, connected: true, session_ready: true }]); assert.equal(connected[0].status, 'connected'); assert.equal(connected[0].last_error, null); const connecting = mergeRuntimeChannelStatuses([channel], [{ channel_id: channel.id, enabled: true, connected: false, session_ready: false }]); assert.equal(connecting[0].status, 'connecting'); const disabled = mergeRuntimeChannelStatuses( [publicChannelFixture({ enabled: false, status: 'disabled' })], [{ channel_id: channel.id, enabled: true, connected: true, session_ready: true }] ); assert.equal(disabled[0].status, 'disabled'); }); test('AgentBus protocol helpers preserve reply routing fields', () => { const inbound = parseAgentBusFrame(JSON.stringify({ id: 'channel-event-1', type: 'event', from: 'channel:wechat:user-1', conversation_id: 'conversation-1', payload: { text: '你好', reply_policy: { progress: true } } })); assert.ok(inbound); assert.equal(isInboundAgentBusTask(inbound), true); const session = { id: 'session-1', epoch: 3, address: 'bot:test:listener' }; const ready = sessionFromReadyFrame(parseAgentBusFrame(JSON.stringify({ id: 'ready-1', type: 'event', session_id: session.id, epoch: session.epoch, to: session.address, payload: { event: 'session.ready' } }))!); assert.deepEqual(ready, session); const progress = createTaskProgressFrame(inbound, session, '处理中', 'progress-1'); assert.equal(progress.reply_to, 'channel-event-1'); assert.equal(progress.conversation_id, 'conversation-1'); assert.equal((progress.payload as Record).event, 'task.progress'); const result = createTaskResultFrame(inbound, session, 'completed', '已完成', 'result-1'); assert.equal(result.from, session.address); assert.equal(result.to, inbound.from); assert.equal(result.session_id, session.id); assert.equal(result.epoch, session.epoch); assert.equal(result.reply_to, inbound.id); assert.equal((result.payload as Record).event, 'task.result'); const important = { kind: 'awaiting_user_input' as const, text: '请补充房型分配。', recorded_at: new Date().toISOString() }; const resultWithImportantMessage = createTaskResultFrame( inbound, session, 'completed', important.text, 'result-important-1', important ); assert.deepEqual((resultWithImportantMessage.payload as Record).important_message, { kind: 'awaiting_user_input', text: '请补充房型分配。' }); const detailedSuccess = { kind: 'success' as const, text: '创建完成。\n团号:LW-260907A-B', recorded_at: new Date().toISOString(), success_receipt: { recorded_at: new Date().toISOString(), receipt: { group_number: 'LW-260907A-B', verification_status: 'split_parent_completed' } } }; const channelResult = createTaskResultFrame( inbound, session, 'completed', '创建完成。\n团号:LW-260907A-B', 'result-channel-summary-1', detailedSuccess ); assert.deepEqual((channelResult.payload as Record).important_message, { kind: 'success', text: '创建完成。\n团号:LW-260907A-B' }); assert.doesNotMatch(JSON.stringify(channelResult.payload), /success_receipt|verification_status|split_parent_completed/); const directFileResult = createTaskResultFrame( inbound, session, 'completed', '团队文件已准备', 'result-file-1', detailedSuccess, [{ id: 'artifact-direct-1', name: 'team.docx', content_type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', size: 3, inline: false, url: 'https://files.example.test/team.docx', sha256: 'a'.repeat(64) }] ); assert.deepEqual((directFileResult.payload as Record).attachments, [{ id: 'artifact-direct-1', name: 'team.docx', content_type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', size: 3, inline: false, url: 'https://files.example.test/team.docx', sha256: 'a'.repeat(64) }]); const insecureFileResult = createTaskResultFrame( inbound, session, 'completed', '团队文件已准备', 'result-insecure-file-1', { kind: 'success', text: '团队文件已准备', recorded_at: new Date().toISOString(), attachments: [{ name: 'team.docx', download_url: 'http://127.0.0.1:8786/file.docx' }] } ); assert.equal((insecureFileResult.payload as Record).attachments, undefined); const senderOnly = parseAgentBusFrame(JSON.stringify({ id: 'channel-event-without-conversation-1', type: 'event', from: 'channel:wechat:user-2', payload: { text: '补充信息' } })); assert.ok(senderOnly); const fallbackProgress = createTaskProgressFrame(senderOnly, session, '处理中', 'progress-fallback-1'); assert.equal(fallbackProgress.conversation_id, 'agentbus:channel:wechat:user-2'); }); test('AgentBus result text uses the unified important message and preserves the confirmation gate', () => { const needsInput = makeTask('awaiting_user_input', { input_request: { missing_fields: ['data.room_counts'], questions: [{ field: 'data.room_counts', prompt: '请补充房型分配。' }], captured_facts: {} }, important_message: { kind: 'awaiting_user_input', text: '请补充房型分配,并说明是否一单一房。', recorded_at: new Date().toISOString(), input_request: { missing_fields: ['data.room_counts'], questions: [{ field: 'data.room_counts', prompt: '请补充房型分配。' }], captured_facts: {} } } }); assert.match(taskResultText(needsInput), /是否一单一房/); assert.equal(taskResultStatus(needsInput), 'completed'); const completed = makeTask('completed', { message: '散拼-创建母团计划已提交并完成回查。', important_message: { kind: 'success', text: '散拼-创建母团计划已提交并完成回查。\n团号:LW-260907A-B', recorded_at: new Date().toISOString(), success_receipt: { recorded_at: new Date().toISOString(), receipt: { group_numbers: ['LW-260907A-B', 'LW-260915A-A'], room_counts: { SGL: 1, TWN: 8 }, verification_status: 'split_parent_completed' } } } }); assert.equal( taskResultText(completed), '散拼-创建母团计划已提交并完成回查。\n团号:LW-260907A-B' ); assert.doesNotMatch(taskResultText(completed), /最终回执|verification_status|split_parent_completed/); const awaitingConfirmation = makeTask('awaiting_confirmation'); assert.equal(taskResultText(awaitingConfirmation), '已受理,正在处理。'); assert.equal(taskResultStatus(awaitingConfirmation), 'completed'); const failed = makeTask('parse_failed', { failure: { error_code: 'blocked', failure_stage: 'parse', failure_source: 'agent', failure_message: '资料不完整', agent_returned: true, plugin_dispatch_started: false, erp_write_started: false, no_plugin_dispatch: true, no_erp_write: true, validation_errors: [] } }); assert.equal(taskResultStatus(failed), 'failed'); assert.match(taskResultText(failed), /资料不完整/); const blocked = makeTask('blocked', { important_message: { kind: 'error', text: '插件执行阻断:客户候选未唯一解析,已阻断 ERP 写入。;GetProduct ajax request was not observed; product_after_effect: expected exactly one existing LTJT option, found 0; Required fields still blank: customer, product.', recorded_at: new Date().toISOString() } }); assert.equal( taskResultText(blocked), '插件执行阻断:客户候选未唯一解析,已阻断 ERP 写入。' ); assert.doesNotMatch(taskResultText(blocked), /GetProduct|product_after_effect|Required fields/); const erpFailure = makeTask('blocked', { error_summary: { recorded_at: new Date().toISOString(), error_code: 'erp_business_rule_blocked', stage: 'lifecycle_live_submit', source: 'erp', message: '此团还有【应收团款】账,不能取消!', validation_errors: [] }, important_message: { kind: 'error', text: '此团还有【应收团款】账,不能取消!', recorded_at: new Date().toISOString(), error_summary: { recorded_at: new Date().toISOString(), error_code: 'erp_business_rule_blocked', stage: 'lifecycle_live_submit', source: 'erp', message: '此团还有【应收团款】账,不能取消!', validation_errors: [] } } }); assert.equal(taskResultStatus(erpFailure), 'failed'); assert.equal(taskResultText(erpFailure), '此团还有【应收团款】账,不能取消!'); }); test('AgentBus attachment references omit archived visitor XLS and keep only XLSX delivery', () => { const task = makeTask('completed', { result: { report: { status: 'export_source_completed', artifacts: [ { artifact_id: 'artifact-visitor-xls', file_name: 'Visitor.xls', content_type: 'application/vnd.ms-excel', bytes: 9377, storage_backend: 'oss', download_url: 'https://files.example.test/Visitor.xls', agentbus_visible: false }, { artifact_id: 'artifact-visitor-xlsx', file_name: 'Visitor.xlsx', content_type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', bytes: 5211, storage_backend: 'oss', download_url: 'https://files.example.test/Visitor.xlsx', agentbus_visible: true } ] } } }); assert.deepEqual(taskAttachmentReferences(task), [{ artifact_id: 'artifact-visitor-xlsx', name: 'Visitor.xlsx', content_type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', size: 5211, url: 'https://files.example.test/Visitor.xlsx', storage_backend: 'oss' }]); }); test('AgentBus listener connects with the documented Authorization header and returns one final result', async () => { const config = testConfig(); const socket = new FakeSocket(); let capturedUrl = ''; let capturedHeaders: Record = {}; let currentTask = makeTask('parse_queued'); const events = new EventEmitter(); const received: Array> = []; let capturedContext: Record = {}; const logs: Array<{ level: string; metadata: Record; message?: string }> = []; const tasks: AgentBusTaskGateway = { events, async ingestMessage(context, input) { capturedContext = context as unknown as Record; received.push(input as unknown as Record); return { task: currentTask, attached: false, created: true }; }, async getTask() { return currentTask; } }; const listener = new AgentBusListener({ config, tasks, organizationId: 'org-1', scheduleParseQueue: async () => { currentTask = makeTask('awaiting_confirmation'); events.emit('task', { id: 1, organization_id: 'org-1', task_id: currentTask.task_id, status: currentTask.status, stage: 'parse', message: currentTask.message, payload: {}, created_at: new Date().toISOString() }); await new Promise((resolve) => setTimeout(resolve, 10)); currentTask = makeTask('confirmed', { confirmation_mode: 'automatic', confirmed_at: new Date().toISOString(), handoff_status: 'awaiting_handoff', message: '指令解析完成,已进入全自动化 ERP 执行。' }); events.emit('task', { id: 2, organization_id: 'org-1', task_id: currentTask.task_id, status: currentTask.status, stage: 'automation', message: currentTask.message, payload: {}, created_at: new Date().toISOString() }); await new Promise((resolve) => setTimeout(resolve, 10)); currentTask = makeTask('completed', { message: '散拼-创建母团计划已提交并完成回查。', important_message: { kind: 'success', text: '散拼-创建母团计划已提交并完成回查。\n团号:LW-260907A-B', recorded_at: new Date().toISOString(), success_receipt: { recorded_at: new Date().toISOString(), receipt: { group_number: 'LW-260907A-B', verification_status: 'split_parent_completed' } } } }); events.emit('task', { id: 3, organization_id: 'org-1', task_id: currentTask.task_id, status: currentTask.status, stage: 'browser_execution', message: currentTask.message, payload: {}, created_at: new Date().toISOString() }); }, socketFactory: (url, options) => { capturedUrl = url; capturedHeaders = options.headers; return socket as unknown as AgentBusSocket; }, logger: { info(metadata, message) { logs.push({ level: 'info', metadata, message }); }, warn(metadata, message) { logs.push({ level: 'warn', metadata, message }); }, error(metadata, message) { logs.push({ level: 'error', metadata, message }); } } }); listener.start(); assert.match(capturedUrl, /[?&]ready=1/); assert.equal(capturedHeaders.Authorization, 'Bearer test-ws-token'); socket.readyState = 1; socket.emit('open'); socket.emit('message', JSON.stringify({ id: 'ready-1', type: 'event', session_id: 'session-1', epoch: 1, to: 'bot:test:listener', payload: { event: 'session.ready' } })); socket.emit('message', JSON.stringify({ id: 'ignored-frame-1', type: 'message', from: 'channel:wechat:user-1', payload: { text: '格式不符合监听契约' } })); socket.emit('message', JSON.stringify({ id: 'channel-event-1', type: 'event', from: 'channel:wechat:user-1', payload: { text: '创建一个新团', reply_policy: { progress: true } } })); await new Promise((resolve) => setTimeout(resolve, 50)); assert.equal(received.length, 1); assert.equal(capturedContext.source, 'agentbus'); assert.equal(received[0].conversationId, 'agentbus:channel:wechat:user-1'); assert.equal(received[0].idempotencyKey, 'agentbus:channel-event-1'); assert.equal(socket.sent.length, 2); assert.equal((socket.sent[0].payload as Record).event, 'task.progress'); assert.equal((socket.sent[0].payload as Record).status, 'accepted'); assert.equal((socket.sent[0].payload as Record).text, '已受理,正在处理。'); assert.equal((socket.sent[1].payload as Record).event, 'task.result'); assert.equal(socket.sent[1].reply_to, 'channel-event-1'); assert.match(String((socket.sent[1].payload as Record).text), /散拼-创建母团计划已提交并完成回查/); assert.match(String((socket.sent[1].payload as Record).text), /LW-260907A-B/); assert.doesNotMatch(String((socket.sent[1].payload as Record).text), /最终回执|verification_status|split_parent_completed/); assert.deepEqual((socket.sent[1].payload as Record).important_message, { kind: 'success', text: '散拼-创建母团计划已提交并完成回查。\n团号:LW-260907A-B' }); assert.ok(logs.some((entry) => entry.metadata.agentbus_event === 'listener_starting')); assert.ok(logs.some((entry) => entry.metadata.agentbus_event === 'session_ready')); assert.ok(logs.some((entry) => entry.metadata.agentbus_event === 'frame_ignored' && entry.metadata.ignore_reason === 'unexpected_type:message')); assert.ok(logs.some((entry) => entry.metadata.agentbus_event === 'frame_received' && entry.metadata.frame_id === 'channel-event-1')); assert.ok(logs.some((entry) => entry.metadata.agentbus_event === 'frame_received' && entry.metadata.text_preview === '创建一个新团')); assert.ok(logs.some((entry) => entry.metadata.agentbus_event === 'task_ingested' && entry.metadata.task_id === 'TASK-1')); assert.ok(logs.some((entry) => entry.metadata.agentbus_event === 'outbound_progress_sent')); assert.ok(logs.some((entry) => entry.metadata.agentbus_event === 'outbound_result_sent')); assert.doesNotMatch(JSON.stringify(logs), /test-ws-token/); listener.stop(); }); test('listener reload stop does not overwrite the channel status as disabled', () => { const statuses: string[] = []; const listener = new AgentBusListener({ config: testConfig(), tasks: {} as AgentBusTaskGateway, organizationId: 'org-1', scheduleParseQueue: async () => {}, onStatusChange: (status) => statuses.push(status) }); listener.stop(false); assert.deepEqual(statuses, []); listener.stop(); assert.deepEqual(statuses, ['disabled']); }); test('listener shutdown carries its session epoch for stale-writer protection', () => { const socket = new FakeSocket(); const changes: Array<{ status: string; epoch: number | null }> = []; const listener = new AgentBusListener({ config: testConfig(), tasks: {} as AgentBusTaskGateway, organizationId: 'org-1', scheduleParseQueue: async () => {}, socketFactory: () => socket as unknown as AgentBusSocket, onStatusChange: (status, _error, epoch) => changes.push({ status, epoch }) }); listener.start(); socket.readyState = 1; socket.emit('message', JSON.stringify({ id: 'ready-epoch-1', type: 'event', session_id: 'session-epoch-1', epoch: 7, to: 'bot:test:listener', payload: { event: 'session.ready' } })); listener.stop(); assert.deepEqual(changes.at(-1), { status: 'disabled', epoch: 7 }); }); test('durable channel listener persists route and resends accepted/result deliveries', async () => { const config = testConfig(); const socket = new FakeSocket(); const events = new EventEmitter(); let currentTask = makeTask('parse_queued', { task_id: 'TASK-DURABLE', channel_id: 'channel-1', channel_name: '外部用户 A' }); let acceptedCreated = false; let resultEnqueued = false; const deliveryState = { acceptedDelivered: false, resultDelivered: false, failures: [] as string[] }; let resultPayload: Record = {}; const accepted: AgentBusDelivery = { id: 'delivery-accepted', channel_id: 'channel-1', task_id: 'task-row-1', inbound_frame_id: 'channel-durable-1', inbound_from: 'channel:external:user-a', conversation_id: 'conversation-durable-1', delivery_kind: 'accepted', payload: { event: 'task.progress', status: 'accepted', text: '已受理,正在处理。' }, attempt_count: 1 }; const result: AgentBusDelivery = { ...accepted, id: 'delivery-result', delivery_kind: 'result', payload: resultPayload }; let capturedHeaders: Record = {}; let receivedContext: Record = {}; let receivedInput: Record = {}; const tasks: AgentBusTaskGateway & { deliveryState: typeof deliveryState } = { deliveryState, events, async ingestMessage(context, input) { acceptedCreated = true; receivedContext = context as unknown as Record; receivedInput = input as unknown as Record; return { task: currentTask, attached: false, created: true }; }, async getTask() { return currentTask; }, async listAgentBusFinalizationCandidates() { if (acceptedCreated && deliveryState.acceptedDelivered && !resultEnqueued && currentTask.status === 'completed') { return [{ channel_id: 'channel-1', task_id: currentTask.task_id, inbound_frame_id: accepted.inbound_frame_id }]; } return []; }, async enqueueAgentBusResult(_channelId, _inboundFrameId, payload) { resultPayload = payload; result.payload = resultPayload; resultEnqueued = true; }, async claimAgentBusDeliveries() { if (acceptedCreated && !deliveryState.acceptedDelivered) return [accepted]; if (resultEnqueued && !deliveryState.resultDelivered) return [result]; return []; }, async markAgentBusDeliveryDelivered(this: { deliveryState: typeof deliveryState }, deliveryId) { if (deliveryId === accepted.id) this.deliveryState.acceptedDelivered = true; if (deliveryId === result.id) this.deliveryState.resultDelivered = true; }, async markAgentBusDeliveryFailed(this: { deliveryState: typeof deliveryState }, _deliveryId, errorMessage) { this.deliveryState.failures.push(errorMessage); }, async releaseAgentBusDeliveries() {} }; const listener = new AgentBusListener({ config, tasks, organizationId: 'org-1', scheduleParseQueue: async () => { currentTask = makeTask('completed', { task_id: 'TASK-DURABLE', channel_id: 'channel-1', channel_name: '外部用户 A', message: '任务已完成。', result: { report: { status: 'export_source_completed', artifacts: [ { artifact_id: 'artifact-durable-source-xls', file_name: 'Visitor.xls', content_type: 'application/vnd.ms-excel', bytes: 3, sha256: 'b'.repeat(64), storage_backend: 'oss', download_url: 'https://files.example.test/Visitor.xls', agentbus_visible: false }, { artifact_id: 'artifact-durable-1', file_name: 'Visitor.xlsx', content_type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', bytes: 4, sha256: 'a'.repeat(64), storage_backend: 'oss', download_url: 'https://files.example.test/Visitor.xlsx', agentbus_visible: true } ] } }, important_message: { kind: 'success', text: '任务已完成。', recorded_at: new Date().toISOString() } }); }, socketFactory: (_url, options) => { capturedHeaders = options.headers; return socket as unknown as AgentBusSocket; }, channel: { id: 'channel-1', displayName: '外部用户 A', wsUrl: 'wss://mesh.nianxx.cn/ws', wsToken: 'channel-ws-token', botAddress: 'bot:channel-a:listener' } }); listener.start(); assert.equal(capturedHeaders.Authorization, 'Bearer channel-ws-token'); socket.readyState = 1; socket.emit('open'); socket.emit('message', JSON.stringify({ id: 'ready-durable-1', type: 'event', session_id: 'session-durable-1', epoch: 4, to: 'bot:channel-a:listener', payload: { event: 'session.ready' } })); socket.emit('message', JSON.stringify({ id: 'channel-durable-1', type: 'event', from: 'channel:external:user-a', conversation_id: 'conversation-durable-1', payload: { text: '创建一个新团' } })); const deadline = Date.now() + 3_000; while (Date.now() < deadline && !deliveryState.resultDelivered) { await new Promise((resolve) => setTimeout(resolve, 50)); } assert.equal(receivedContext.channelId, 'channel-1'); assert.equal(receivedInput.channelId, 'channel-1'); assert.deepEqual(receivedInput.agentBusRoute, { inboundFrameId: 'channel-durable-1', inboundFrom: 'channel:external:user-a', conversationId: 'conversation-durable-1' }); assert.deepEqual(resultPayload.attachments, [{ artifact_id: 'artifact-durable-1', name: 'Visitor.xlsx', content_type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', size: 4, url: 'https://files.example.test/Visitor.xlsx', sha256: 'a'.repeat(64), storage_backend: 'oss' }]); assert.doesNotMatch(JSON.stringify(resultPayload), /content_base64|download_url/); assert.equal(socket.sent.length, 2); assert.equal(socket.sent[0].id, 'bot-accepted-delivery-accepted'); assert.equal((socket.sent[0].payload as Record).event, 'task.progress'); assert.equal(socket.sent[1].id, 'bot-result-delivery-result'); assert.equal(socket.sent[1].reply_to, 'channel-durable-1'); assert.equal((socket.sent[1].payload as Record).event, 'task.result'); assert.equal((socket.sent[1].payload as Record).status, 'completed'); assert.deepEqual((socket.sent[1].payload as Record).attachments, [{ id: 'artifact-durable-1', name: 'Visitor.xlsx', content_type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', size: 4, inline: false, url: 'https://files.example.test/Visitor.xlsx', sha256: 'a'.repeat(64) }]); assert.equal(deliveryState.acceptedDelivered, true); assert.equal(deliveryState.resultDelivered, true); assert.deepEqual(deliveryState.failures, []); listener.stop(); });