diff --git a/packages/runtime/src/__tests__/fake-backend.test.ts b/packages/runtime/src/__tests__/fake-backend.test.ts new file mode 100644 index 00000000000..fc628537a3b --- /dev/null +++ b/packages/runtime/src/__tests__/fake-backend.test.ts @@ -0,0 +1,28 @@ +import assert from 'node:assert/strict'; +import { test } from 'node:test'; +import type { SessionHeader } from '@maka/core'; +import { FakeBackend } from '../fake-backend.js'; +import type { SessionStore } from '../session-manager.js'; + +test('text deltas preserve the exact completed response, including Markdown line breaks', async () => { + const backend = new FakeBackend({ + sessionId: 'session-1', + header: { model: 'fake-model' } as SessionHeader, + store: {} as SessionStore, + appendMessage: async () => {}, + }); + const deltas: string[] = []; + let completedText = ''; + + for await (const event of backend.send({ + turnId: 'turn-1', + text: '结论先行。\n\n| 名称 | 状态 |\n| --- | --- |\n| A | 完成 |', + context: [], + })) { + if (event.type === 'text_delta') deltas.push(event.text); + if (event.type === 'text_complete') completedText = event.text; + } + + assert.equal(deltas.join(''), completedText); + assert.match(completedText, /\n\n\| 名称 \| 状态 \|/); +}); diff --git a/packages/runtime/src/fake-backend.ts b/packages/runtime/src/fake-backend.ts index fcb4bfaa321..b7a07e3c208 100644 --- a/packages/runtime/src/fake-backend.ts +++ b/packages/runtime/src/fake-backend.ts @@ -26,7 +26,9 @@ export class FakeBackend implements AgentBackend { const attNames = (input.attachments ?? []).map((a) => a.name); const attLine = attNames.length > 0 ? `\nAttachments received: ${attNames.join(', ')}` : ''; const text = `Fake backend received: ${input.text}${attLine}\n\nThis proves the session stream, JSONL storage, and renderer loop are connected.`; - const chunks = text.match(/.{1,9}/g) ?? [text]; + // Every delta must concatenate to text_complete; `.` would silently drop + // line terminators and make structured Markdown reflow only at completion. + const chunks = text.match(/[\s\S]{1,9}/g) ?? [text]; for (const chunk of chunks) { if (this.stopped) {