diff --git a/apps/chrome/tests/bridge.test.mjs b/apps/chrome/tests/bridge.test.mjs index 41a78fc..28543e1 100644 --- a/apps/chrome/tests/bridge.test.mjs +++ b/apps/chrome/tests/bridge.test.mjs @@ -78,6 +78,17 @@ test('page context is explicit appended reference data, never a replaced system assert.ok(prompt.includes('Ignore prior instructions')) }) +test('40 long Unicode or escaped transcripts fit the native byte limit with an in-flight answer', () => { + for (const text of ['Türkçe ığüşöç', '你好世界', '\u0000\u0001']) { + const rows = Array.from({ length: 40 }, (_, id) => ({ id, role: 'assistant', content: text.repeat(8000) })) + const response = { id: 'request', result: { messages: publicMessages(rows), inflight: '\u0000'.repeat(16000), model: 'fixture' } } + assert.ok(Buffer.byteLength(JSON.stringify(response)) < MAX_FRAME) + assert.equal(response.result.messages.length, 40) + assert.ok(response.result.messages.every(row => row.text.length > 0)) + assert.equal(rows[0].content, text.repeat(8000)) + } +}) + async function fixture(t) { const calls = [], peers = [] const http = createServer((request, response) => { diff --git a/apps/desktop/electron/chrome-bridge.mjs b/apps/desktop/electron/chrome-bridge.mjs index 071ab88..407bdae 100644 --- a/apps/desktop/electron/chrome-bridge.mjs +++ b/apps/desktop/electron/chrome-bridge.mjs @@ -12,13 +12,23 @@ const contentText = value => typeof value === 'string' ? value : Array.isArray(v ? value.filter(part => part?.type === 'text').map(part => part.text || '').join('\n') : '' export function publicMessages(rows) { - return (Array.isArray(rows) ? rows : []).filter(row => + const messages = (Array.isArray(rows) ? rows : []).filter(row => ['user', 'assistant'].includes(row.role) && row.display_kind !== 'hidden' ).slice(-40).map(row => ({ - id: row.id ?? row.row_id, + id: typeof (row.id ?? row.row_id) === 'number' ? (row.id ?? row.row_id) : undefined, role: row.role, text: clip(contentText(row.display_content ?? row.content ?? row.text)), })) + // The Chrome native limit counts UTF-8 JSON bytes, not JS characters. + // Reserve room for the opening response's in-flight text and metadata; + // escaped controls and CJK text must not disconnect a large transcript. + const messageBudget = Math.floor((MAX_FRAME - 128000) / Math.max(1, messages.length)) + for (const message of messages) { + while (Buffer.byteLength(JSON.stringify(message)) > messageBudget) { + message.text = message.text.slice(0, Math.floor(message.text.length * 0.8)) + } + } + return messages } export function validateRequest(message) { diff --git a/apps/desktop/package.json b/apps/desktop/package.json index cdcf862..730a61a 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -2,7 +2,7 @@ "name": "aiturk-ide", "productName": "AITURK IDE", "private": true, - "version": "1.0.0-beta.7", + "version": "1.0.0-beta.8", "description": "TurkServis resmi yapay zekâ geliştirme ortamı. Hermes Agent tabanlıdır.", "author": "AITURK / TurkServis", "repository": { diff --git a/package-lock.json b/package-lock.json index 325aa42..fda0e79 100644 --- a/package-lock.json +++ b/package-lock.json @@ -65,7 +65,7 @@ }, "apps/desktop": { "name": "aiturk-ide", - "version": "1.0.0-beta.7", + "version": "1.0.0-beta.8", "license": "MIT", "dependencies": { "@assistant-ui/core": "0.2.23",