fix: 消除会话列表解析阻塞并按需加载工具详情
This commit is contained in:
@@ -25,7 +25,7 @@ class FirefoxDriver {
|
||||
}
|
||||
async start() {
|
||||
const value = await this.request('POST', '/session', { capabilities: { alwaysMatch: {
|
||||
browserName: 'firefox', 'moz:firefoxOptions': { binary: FIREFOX, args: ['-headless'] },
|
||||
browserName: 'firefox', pageLoadStrategy: 'eager', 'moz:firefoxOptions': { binary: FIREFOX, args: ['-headless'] },
|
||||
} } });
|
||||
this.id = value.sessionId;
|
||||
await this.request('POST', `/session/${this.id}/window/rect`, { width: 1280, height: 1000 });
|
||||
@@ -38,7 +38,7 @@ class FirefoxDriver {
|
||||
|
||||
async function startRelay(port, targetPort, evidence) {
|
||||
const sockets = new Set();
|
||||
const state = { dropWs: false, blockUpgrades: false, holdId: '', held: [] };
|
||||
const state = { dropWs: false, blockUpgrades: false, holdId: '', held: [], bytesPerSecond: 0 };
|
||||
const relay = http.createServer((request, response) => {
|
||||
if (CDN_FIXTURE_DIR && request.url.startsWith('/fixture-cdn/')) {
|
||||
const name = request.url.slice('/fixture-cdn/'.length);
|
||||
@@ -63,6 +63,23 @@ async function startRelay(port, targetPort, evidence) {
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!hold && isQuery && state.bytesPerSecond) {
|
||||
const chunks = [];
|
||||
result.on('data', chunk => chunks.push(chunk));
|
||||
result.on('end', () => {
|
||||
const body = Buffer.concat(chunks);
|
||||
const bytesPerTick = Math.max(1, Math.floor(state.bytesPerSecond / 20));
|
||||
let offset = 0;
|
||||
response.writeHead(result.statusCode, result.headers);
|
||||
const timer = setInterval(() => {
|
||||
const end = Math.min(body.length, offset + bytesPerTick);
|
||||
response.write(body.subarray(offset, end)); offset = end;
|
||||
if (offset === body.length) { clearInterval(timer); response.end(); }
|
||||
}, 50);
|
||||
response.on('close', () => clearInterval(timer));
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!hold) { response.writeHead(result.statusCode, result.headers); result.pipe(response); return; }
|
||||
const chunks = [];
|
||||
result.on('data', chunk => chunks.push(chunk));
|
||||
@@ -161,6 +178,36 @@ async function runBrowser(options) {
|
||||
await click(A);
|
||||
await loaded(A, 12, true);
|
||||
evidence.stages.fakeOpen = { durationMs: Date.now() - started, count: 12, runtimeVisible: true };
|
||||
await driver.execute(`window.__switchErrors=[];window.addEventListener('error',e=>__switchErrors.push(e.message));
|
||||
window.addEventListener('unhandledrejection',e=>__switchErrors.push(String(e.reason)));`);
|
||||
relay.state.bytesPerSecond = 32768;
|
||||
started = Date.now();
|
||||
await click('http-large-tools');
|
||||
await loaded('http-large-tools', 12);
|
||||
const compact = await driver.execute(`return {
|
||||
tools:document.querySelectorAll('#messages .tool-call').length,
|
||||
contents:document.querySelectorAll('#messages .tool-call-content').length,
|
||||
bytes:performance.getEntriesByType('resource').filter(x=>x.name.includes('/api/sessions/http-large-tools?')).at(-1)?.decodedBodySize,
|
||||
};`);
|
||||
assert.equal(compact.tools, 100);
|
||||
assert.equal(compact.contents, 0, '折叠工具不能提前构建详情DOM');
|
||||
assert(Date.now() - started < 8000, '32KB/s 下工具大结果不应卡住首屏');
|
||||
evidence.stages.largeTools = { ...compact, durationMs: Date.now() - started, bytesPerSecond: 32768 };
|
||||
relay.state.bytesPerSecond = 0;
|
||||
await driver.execute(`const tool=document.querySelector('#messages details.tool-call');
|
||||
const group=tool.closest('.tool-group');if(group)group.open=true;tool.querySelector('summary').click();`);
|
||||
await waitFor(() => driver.execute(`return document.querySelector('#messages details.tool-call .tool-call-content')?.textContent.includes('结果终点');`), '完整工具详情未显示');
|
||||
evidence.stages.toolDetail = { complete: true };
|
||||
relay.state.holdId = 'http-large-tools';
|
||||
await driver.execute(`document.querySelectorAll('#messages details.tool-call')[1].querySelector('summary').click();`);
|
||||
await waitFor(() => relay.state.held.length, '工具详情延迟响应未到达');
|
||||
await click(B);
|
||||
await loaded(B, 12);
|
||||
relay.release();
|
||||
await sleep(100);
|
||||
await loaded(B, 12);
|
||||
evidence.stages.detailCancellation = { previousDetailCannotOverwriteNewView: true };
|
||||
assert.deepEqual(await driver.execute('return window.__switchErrors;'), [], '切换及展开不能产生浏览器异常');
|
||||
await click(B);
|
||||
await loaded(B, 12);
|
||||
for (const count of [36, 58]) {
|
||||
@@ -201,6 +248,7 @@ async function runBrowser(options) {
|
||||
await click(A);
|
||||
await waitFor(() => relay.state.held.length, '完成间隙的 HTTP 快照未到达');
|
||||
assert.equal(JSON.parse(relay.state.held[0].body).isRunning, true);
|
||||
const runtimeDetailRef = JSON.parse(relay.state.held[0].body).runtime.toolCalls.find(tool => tool.kind === 'command_execution').detailRef;
|
||||
fs.writeFileSync(fixture.completeFlag, 'complete');
|
||||
await waitFor(async () => {
|
||||
const result = (await query(A)).payload;
|
||||
@@ -215,6 +263,9 @@ async function runBrowser(options) {
|
||||
&& value.text.includes('Codex App mock handled:') ? value : null;
|
||||
}, '读取与订阅之间完成的消息未补齐', 12000);
|
||||
assert(evidence.ws.some(msg => msg.type === 'session_subscribed' && msg.historyChanged));
|
||||
const archivedTool = await query(A, '?toolDetail=' + encodeURIComponent(JSON.stringify(runtimeDetailRef)));
|
||||
assert.equal(archivedTool.response.status, 200, '运行工具归档后,原详情引用仍须可读取');
|
||||
assert(archivedTool.payload.tool.result.includes('运行结果终点'));
|
||||
evidence.stages.completionGap = { count: completed.ids.length, streaming: false, finalVisible: true };
|
||||
await click('http-corrupt-chunk');
|
||||
await waitFor(async () => {
|
||||
@@ -224,7 +275,7 @@ async function runBrowser(options) {
|
||||
evidence.stages.readFailure = { previousViewPreserved: true, loading: false };
|
||||
assert(!evidence.ws.some(msg => msg.direction === 'client' && ['load_session', 'load_history_page', 'resume_session'].includes(msg.type)));
|
||||
fs.writeFileSync(path.join(root, 'browser-evidence.json'), JSON.stringify(evidence, null, 2));
|
||||
console.log('Firefox 通过:假 OPEN/断 WS 首屏与运行输出、HTTP 分页顺序、迟到响应隔离、切换间隙完成对账;浏览器未通过 WS 查询历史');
|
||||
console.log('Firefox 通过:32KB/s 大工具会话首屏、折叠懒建、完整详情、假 OPEN/断 WS 运行恢复、分页、迟到隔离与完成交错');
|
||||
} catch (error) {
|
||||
evidence.failure = error.message;
|
||||
evidence.state = await snapshot().catch(() => null);
|
||||
|
||||
Reference in New Issue
Block a user