import { describe, expect, it } from 'vitest'; import releaseManifest from '../../public/manifest/models.json'; import { EngineClientError } from '../engine'; import { applyToolExecutionToMessages, browserTabTitle, buildEngineHistory, DEFAULT_CONTEXT_SIZE, DEFAULT_MAX_TOKENS, DEFAULT_MODEL_ID, initialInferencePhase, liveInferencePhase, messagesForPersistence, modelArchitectureLabel, normalizeStoredMessages, preparingInferenceStatus, publishBackendReport, reconcileCompletionTokens, reconcileTotalTokens, reportsModelWeightProgress, requiresModelReload, shouldCachePrompt, } from './App'; import { prepareArtifactDeployment, prepareHtmlArtifact, type ToolExecution } from '../lib/agent-tools'; function engineError(code: string): EngineClientError { return new EngineClientError({ code, message: code }); } describe('App model invalidation policy', () => { it.each([ 'WEBGPU_DEVICE_LOST', 'ENGINE_WORKER_FAILED', 'MODEL_NOT_LOADED', 'MODEL_RESOURCE_EXHAUSTED', ])('requires an explicit reload after %s', (code) => { expect(requiresModelReload(engineError(code))).toBe(true); }); it('keeps an otherwise healthy loaded model after a responsive abort', () => { expect(requiresModelReload(engineError('ABORTED'))).toBe(false); }); }); describe('App release defaults', () => { it('opens on the flagship tier required by the product contract', () => { expect(DEFAULT_MODEL_ID).toBe('bonsai-27b'); }); it('shows manifest architecture instead of invented model aliases', () => { expect(modelArchitectureLabel('qwen3')).toBe('Qwen3 dense'); expect(modelArchitectureLabel('qwen35')).toBe('Qwen3.5 hybrid'); }); it('defaults both context and maximum completion to 8K', () => { expect(DEFAULT_CONTEXT_SIZE).toBe(8192); expect(DEFAULT_MAX_TOKENS).toBe(8192); expect(releaseManifest.models.every((model) => model.defaultContext === DEFAULT_CONTEXT_SIZE)) .toBe(true); }); it('keeps prompt reuse for ordinary chat but avoids recurrent-state drift in Qwen3.5 tool runs', () => { expect(shouldCachePrompt('qwen35', false)).toBe(true); expect(shouldCachePrompt('qwen35', true)).toBe(false); expect(shouldCachePrompt('qwen3', true)).toBe(true); }); it('advertises resident model state in the browser tab title', () => { expect(browserTabTitle(null, null, 'idle')).toBe('⚪ Model not loaded · Bonsai Chat'); expect(browserTabTitle(null, 'Bonsai 27B', 'loading')).toBe('⏳ Loading Bonsai 27B · Bonsai Chat'); expect(browserTabTitle('Bonsai 27B', null, 'complete')).toBe('🟢 Bonsai 27B loaded · Bonsai Chat'); expect(browserTabTitle('Bonsai 27B', null, 'streaming')).toBe('✨ Bonsai 27B generating · Bonsai Chat'); }); it('only exposes preparation before the first graph has produced progress', () => { expect(initialInferencePhase(false)).toBe('preparing'); expect(initialInferencePhase(true)).toBe('idle'); expect(liveInferencePhase('preparing', false)).toBe('preparing'); expect(liveInferencePhase('preparing', true)).toBe('prefill'); expect(preparingInferenceStatus(3.25)) .toBe('Preparing WebGPU · compiling first inference graph · 3.3 s'); }); }); describe('App model-load progress', () => { it('does not turn pinned manifest metadata reads into model-weight download state', () => { expect(reportsModelWeightProgress('manifest')).toBe(false); expect(reportsModelWeightProgress('download')).toBe(true); expect(reportsModelWeightProgress('load')).toBe(true); expect(reportsModelWeightProgress('warmup')).toBe(true); }); }); describe('App streamed completion telemetry', () => { it('uses observed stream events when final engine usage is incomplete', () => { expect(reconcileCompletionTokens(5, 64)).toBe(64); }); it('keeps the engine count when one stream event contains multiple tokens', () => { expect(reconcileCompletionTokens(12, 8)).toBe(12); }); it('fails closed to the observed count for invalid engine usage', () => { expect(reconcileCompletionTokens(Number.NaN, 7)).toBe(7); }); it('keeps context usage consistent with a reconciled completion count', () => { expect(reconcileTotalTokens(5, 5, 64)).toBe(69); expect(reconcileTotalTokens(80, 5, 64)).toBe(80); }); }); describe('App conversation persistence', () => { it('does not persist transient streamed artifact drafts', () => { expect(messagesForPersistence([{ id: 'assistant', role: 'assistant', content: '', timestamp: '10:00', toolActivity: { name: 'artifact_deploy', argumentCharacters: 120, artifactDraft: { id: 'draft', title: 'Draft', entryPath: 'index.html', files: [], updatedAt: 1, }, }, }])).toEqual([{ id: 'assistant', role: 'assistant', content: '', timestamp: '10:00', }]); }); }); describe('App backend diagnostics', () => { it('publishes the complete backend report for the browser gate without rendering it', () => { const report = { backends: ['WebGPU'], nGraphSplits: 1, opsOnCpu: 0, layersGpu: { offloaded: 65, total: 65 }, flashAttention: false, cacheTypeK: 'f16', cacheTypeV: 'f16', webgpuKvBufferBytes: 1024, webgpuTrace: ['@@WEBGPU_TRACE@@completion_end id=1 steps=65 error=0'], }; publishBackendReport(report); expect(globalThis.__bonsaiBackendReport).toEqual(report); delete globalThis.__bonsaiBackendReport; }); }); describe('App artifact identity', () => { it('moves an edited artifact to the current tool position while completing the tool run', () => { const original = prepareArtifactDeployment([ { path: 'index.html', content: '' }, { path: 'src/main.js', content: 'document.body.textContent = "old";' }, ], 'Demo', 'index.html', 'stable-artifact', 'old-runtime'); const replacement = prepareArtifactDeployment([ { path: 'index.html', content: '' }, { path: 'src/main.js', content: 'document.body.textContent = "new";' }, ], 'Demo', 'index.html', 'stable-artifact', 'new-runtime'); const execution: ToolExecution = { call: { id: 'update-call', type: 'function', function: { name: 'artifact_write', arguments: '{}' }, }, artifact: replacement, relocateArtifact: true, output: '{"ok":true}', failed: false, }; const next = applyToolExecutionToMessages([ { id: 'older', role: 'assistant', content: '', timestamp: '10:00', artifacts: [original] }, { id: 'current', role: 'assistant', content: '', timestamp: '10:01', tools: [{ id: 'update-call', name: 'artifact_write', input: '{}', state: 'running' }], }, ], 'current', 'update-call', execution); expect(next[0]?.artifacts).toEqual([]); expect(next[1]?.artifacts).toEqual([replacement]); expect(next[1]?.tools?.[0]).toMatchObject({ state: 'complete', output: '{"ok":true}', artifactId: 'stable-artifact', }); }); it('keeps a test-only artifact in place while returning diagnostics to the current turn', () => { const artifact = prepareHtmlArtifact('

tested

', 'Demo', 'stable-artifact', 'runtime'); const execution: ToolExecution = { call: { id: 'test-call', type: 'function', function: { name: 'artifact_test', arguments: '{}' } }, artifact, relocateArtifact: false, output: '{"ok":true}', failed: false, }; const next = applyToolExecutionToMessages([ { id: 'older', role: 'assistant', content: '', timestamp: '10:00', artifacts: [artifact] }, { id: 'current', role: 'assistant', content: '', timestamp: '10:01', tools: [{ id: 'test-call', name: 'artifact_test', input: '{}', state: 'running' }], }, ], 'current', 'test-call', execution); expect(next[0]?.artifacts).toEqual([artifact]); expect(next[1]?.artifacts).toBeUndefined(); expect(next[1]?.tools?.[0]).toMatchObject({ state: 'complete', artifactId: 'stable-artifact' }); }); it('attaches a newly created artifact to the current assistant message', () => { const artifact = prepareArtifactDeployment([ { path: 'index.html', content: '

new

' }, { path: 'styles/app.css', content: 'p { color: green; }' }, ], 'Demo', 'index.html', 'new-artifact', 'runtime'); const execution: ToolExecution = { call: { id: 'create-call', type: 'function', function: { name: 'artifact_deploy', arguments: '{}' } }, artifact, relocateArtifact: true, output: '{"ok":true}', failed: false, }; const next = applyToolExecutionToMessages([{ id: 'current', role: 'assistant', content: '', timestamp: '10:01', tools: [{ id: 'create-call', name: 'artifact_deploy', input: '{}', state: 'running' }], }], 'current', 'create-call', execution); expect(next[0]?.artifacts).toEqual([artifact]); expect(next[0]?.tools?.[0]?.artifactId).toBe('new-artifact'); }); }); describe('App persisted agent history', () => { it('keeps a conversation readable when an old artifact contains truncated HTML', () => { const [message] = normalizeStoredMessages([{ id: 'assistant', role: 'assistant', content: 'The deployment attempt was interrupted.', timestamp: '10:00', artifacts: [{ id: 'truncated', title: 'Snake Game', source: ' { expect(buildEngineHistory([{ id: 'assistant', role: 'assistant', content: 'I will calculate. The first result is ready. Final answer.', timestamp: '10:00', segments: [ { id: 'text-0', kind: 'text', content: 'I will calculate.' }, { id: 'tool-1', kind: 'tool', toolCallId: 'call-1', roundId: 'round-0' }, { id: 'reasoning-1', kind: 'reasoning', content: 'Checking the first result.' }, { id: 'text-1', kind: 'text', content: 'The first result is ready.' }, { id: 'tool-2', kind: 'tool', toolCallId: 'call-2', roundId: 'round-1' }, { id: 'tool-3', kind: 'tool', toolCallId: 'call-3', roundId: 'round-1' }, { id: 'reasoning-2', kind: 'reasoning', content: 'Combining results.' }, { id: 'text-2', kind: 'text', content: 'Final answer.' }, ], tools: [ { id: 'call-1', name: 'js_eval', input: '{"code":"1+1"}', output: '{"value":2}', state: 'complete' }, { id: 'call-2', name: 'js_eval', input: '{"code":"2+2"}', output: '{"value":4}', state: 'complete' }, { id: 'call-3', name: 'js_eval', input: '{"code":"3+3"}', output: '{"value":6}', state: 'complete' }, ], }], 'System')).toEqual([ { role: 'system', content: 'System' }, { role: 'assistant', content: 'I will calculate.', tool_calls: [{ id: 'call-1', type: 'function', function: { name: 'js_eval', arguments: '{"code":"1+1"}' }, }], }, { role: 'tool', content: '{"value":2}', tool_call_id: 'call-1' }, { role: 'assistant', content: 'The first result is ready.', tool_calls: [ { id: 'call-2', type: 'function', function: { name: 'js_eval', arguments: '{"code":"2+2"}' } }, { id: 'call-3', type: 'function', function: { name: 'js_eval', arguments: '{"code":"3+3"}' } }, ], }, { role: 'tool', content: '{"value":4}', tool_call_id: 'call-2' }, { role: 'tool', content: '{"value":6}', tool_call_id: 'call-3' }, { role: 'assistant', content: 'Final answer.' }, ]); }); });