perf(app): stabilize live session timeline updates
Virtualize SessionDetail rows behind stable presentation boundaries and apply typed incremental patches only after visible commits. Preserve reader state across live updates, handle coalesced and reordered patches, and verify the 120Hz append path with Electron tracing.
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import {
|
||||
applySessionPatch,
|
||||
createSessionPatch,
|
||||
createSessionPatchCursor,
|
||||
} from '../app/src/shared/session-patch.mjs';
|
||||
|
||||
function snapshot(overrides = {}) {
|
||||
return {
|
||||
messages: [
|
||||
{ uuid: 'message-1', timestamp: '2026-07-14T00:00:01Z', text: 'one' },
|
||||
{ uuid: 'message-2', timestamp: '2026-07-14T00:00:02Z', text: 'two' },
|
||||
],
|
||||
toolCalls: [{ id: 'call-1', message_uuid: 'message-1', name: 'exec', input_json: '"return 1"' }],
|
||||
toolResults: [{ tool_use_id: 'call-1', message_uuid: 'message-1', content: 'running', is_error: 0 }],
|
||||
subagents: [],
|
||||
workflows: [{ run_id: 'workflow-1', status: 'running', agents: [{ agent_id: 'agent-1', state: 'running' }] }],
|
||||
summaries: [],
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
test('session patch returns only appended and updated rows, then reconstructs the new snapshot', () => {
|
||||
const current = snapshot();
|
||||
const cursor = createSessionPatchCursor(current);
|
||||
const next = snapshot({
|
||||
messages: [...current.messages, { uuid: 'message-3', timestamp: '2026-07-14T00:00:03Z', text: 'three' }],
|
||||
toolResults: [{ ...current.toolResults[0], content: 'complete' }],
|
||||
});
|
||||
|
||||
const patch = createSessionPatch(next, cursor);
|
||||
|
||||
assert.deepEqual(patch.changes.messages.map(row => row.uuid), ['message-3']);
|
||||
assert.deepEqual(patch.changes.toolResults.map(row => row.tool_use_id), ['call-1']);
|
||||
assert.deepEqual(patch.changes.toolCalls, []);
|
||||
assert.deepEqual(patch.removed.messages, []);
|
||||
assert.equal(patch.positions.messages['message-3'], 2);
|
||||
assert.deepEqual(applySessionPatch(current, cursor, patch), {
|
||||
snapshot: next,
|
||||
cursor: createSessionPatchCursor(next),
|
||||
});
|
||||
});
|
||||
|
||||
test('session patch reports removals and nested workflow updates', () => {
|
||||
const current = snapshot();
|
||||
const cursor = createSessionPatchCursor(current);
|
||||
const next = snapshot({
|
||||
messages: [current.messages[1]],
|
||||
workflows: [{ run_id: 'workflow-1', status: 'complete', agents: [{ agent_id: 'agent-1', state: 'complete' }] }],
|
||||
});
|
||||
|
||||
const patch = createSessionPatch(next, cursor);
|
||||
|
||||
assert.deepEqual(patch.removed.messages, ['message-1']);
|
||||
assert.deepEqual(patch.changes.workflows, next.workflows);
|
||||
assert.deepEqual(applySessionPatch(current, cursor, patch).snapshot, next);
|
||||
});
|
||||
|
||||
test('session patch repositions existing rows when their content is unchanged', () => {
|
||||
const current = snapshot();
|
||||
const cursor = createSessionPatchCursor(current);
|
||||
const next = snapshot({
|
||||
messages: [current.messages[1], current.messages[0]],
|
||||
});
|
||||
|
||||
const patch = createSessionPatch(next, cursor);
|
||||
|
||||
assert.deepEqual(applySessionPatch(current, cursor, patch).snapshot, next);
|
||||
});
|
||||
|
||||
test('session patch cursor is compact and never carries transcript content', () => {
|
||||
const largeText = 'private transcript content '.repeat(1000);
|
||||
const current = snapshot({
|
||||
messages: [{ uuid: 'message-large', timestamp: '2026-07-14T00:00:00Z', text: largeText }],
|
||||
toolResults: [{ tool_use_id: 'call-large', message_uuid: 'message-large', content: largeText, is_error: 0 }],
|
||||
});
|
||||
|
||||
const cursor = createSessionPatchCursor(current);
|
||||
const serializedCursor = JSON.stringify(cursor);
|
||||
|
||||
assert.equal(serializedCursor.includes('private transcript content'), false);
|
||||
assert.ok(serializedCursor.length < JSON.stringify(current).length / 20);
|
||||
});
|
||||
Reference in New Issue
Block a user