feat: advance browser agent integration workflows

This commit is contained in:
go0p
2026-08-06 15:11:35 +08:00
committed by go0p
parent af5a4db694
commit 0c8e1c7b69
215 changed files with 35137 additions and 5442 deletions
@@ -0,0 +1,52 @@
import { describe, expect, it } from 'vitest';
import type { BrowserRecordingEvent } from '@/types/models';
import {
boundRecordingPreviews,
recordingEventPreviewBytes,
recordingSerializedBytes,
} from './budget';
function event(id: string, preview: string): BrowserRecordingEvent {
return {
id,
sequence: Number(id),
timestamp: Number(id),
recordingId: 'recording-1',
traceId: 'trace-1',
kind: 'crypto',
operation: 'encrypt',
inputs: [{ path: '$input', fingerprint: id, encoding: 'text', byteLength: preview.length, preview }],
outputs: [],
sensitiveCaptured: true,
inputPreview: preview,
};
}
describe('browser recording budgets', () => {
it('drops oldest previews without removing event metadata or fingerprints', () => {
const bounded = boundRecordingPreviews([
event('1', 'a'.repeat(128)),
event('2', 'b'.repeat(128)),
], 300);
expect(bounded.events).toHaveLength(2);
expect(bounded.events[0]).toMatchObject({ id: '1', sensitiveCaptured: false });
expect(bounded.events[0].inputPreview).toBeUndefined();
expect(bounded.events[0].inputs[0]).toMatchObject({ fingerprint: '1' });
expect(bounded.events[1].inputPreview).toHaveLength(128);
expect(bounded.retainedBytes).toBeLessThanOrEqual(300);
expect(bounded.droppedCount).toBe(2);
});
it('counts UTF-8 bytes instead of JavaScript code units', () => {
const value = event('1', '密钥');
expect(recordingEventPreviewBytes(value)).toBe(12);
expect(recordingSerializedBytes({ value: '密钥' })).toBeGreaterThan('{"value":""}'.length);
});
it('reports unserializable data as over budget', () => {
const cyclic: Record<string, unknown> = {};
cyclic.self = cyclic;
expect(recordingSerializedBytes(cyclic)).toBe(Number.MAX_SAFE_INTEGER);
});
});