fix(agents): show only explicitly delivered chat files

This commit is contained in:
鲨鱼辣椒 committed 2026-10-12 23:43:33 +08:00
1 parent 922b09bda0
commit e78459b384
12 files changed
+429 -62

No files matched your search

+34 -3
View File
@@ -67,6 +67,7 @@ const longWord = 'an-unbroken-story-notebook-'.repeat(24);
const richReply = [
'## 一起把小发现变成故事',
'你可以先选一个最喜欢的发现,再让角色去试一试。',
'[下载故事笔记](sandbox:/故事笔记.md)',
`这个很长的名字也应该完整保留:${longWord}`,
`[查看完整故事线索](https://example.invalid/story/${longWord})`,
['| 今天的线索 | 可以接着想什么 |', '| --- | --- |',
@@ -102,7 +103,7 @@ type FixtureSeed = { threads: CloudThread[]; histories: Record<string, CloudHist
type FixtureStream = EventTarget & { readyState: number; url: string };
type FixtureState = FixtureSeed & { calls: FixtureCall[]; attachments: CloudAttachment[]; streams: FixtureStream[] };
type FixtureWindow = Window & { __cloudChatFixture: FixtureState };
type FixtureKind = 'empty' | 'history' | 'running' | 'approval' | 'thinking';
type FixtureKind = 'empty' | 'history' | 'running' | 'approval' | 'thinking' | 'delivery';
async function installFixture(page: Page, kind: FixtureKind) {
const isRunning = kind === 'running' || kind === 'thinking';
@@ -113,7 +114,14 @@ async function installFixture(page: Page, kind: FixtureKind) {
const run = isRunning ? makeRun(first, 'running') : kind === 'approval' ? makeRun(first, 'interrupted', interrupt) : null;
const seed: FixtureSeed = kind === 'empty' ? { threads: [], histories: {} } : {
threads: [first, second], histories: {
[first.thread_id]: kind === 'thinking' ? { ...makeHistory(first, false, run), messages: [] } : makeHistory(first, kind !== 'approval', run),
[first.thread_id]: kind === 'thinking' ? { ...makeHistory(first, false, run), messages: [] }
: kind === 'delivery' ? { ...makeHistory(first), messages: [
{ id: 1, role: 'user', content: '[用户提到的文件](sandbox:/user.txt)', request_id: 'delivery-user', run_id: null, created_at: date },
{ id: 2, role: 'assistant', request_id: 'delivery-answer', run_id: null, created_at: date,
content: '[下载完成的小程序](sandbox:/deliverable.py)\n\n准备时用到了 /internal.py、/call.txt、/cache.json 和 /plain.txt。\n\n`[行内示例](sandbox:/inline.py)`\n\n```markdown\n[代码示例](sandbox:/example.py)\n```',
reasoning_content: '[思考过程](sandbox:/thought.txt)',
},
] } : makeHistory(first, kind !== 'approval', run),
[second.thread_id]: makeHistory(second),
},
};
@@ -166,7 +174,8 @@ async function installFixture(page: Page, kind: FixtureKind) {
if (!state.attachments.some(item => item.file_id === attachment.file_id)) state.attachments.push(attachment);
return result({ attachments: [attachment] });
}
case 'files': return result({ files: [{ name: '故事笔记.md', is_dir: false, path: '/故事笔记.md', size: 1024 }] });
case 'files': return result({ files: ['故事笔记.md', 'deliverable.py', 'internal.py', 'call.txt', 'cache.json', 'plain.txt', 'inline.py', 'example.py', 'user.txt', 'thought.txt']
.map(name => ({ name, is_dir: false, path: '/' + name, directory_path: '/', size: 1024 })) });
case 'run': return result(Object.values(state.histories).find(item => item.run?.agent_run_id === args?.run_id)?.run);
case 'cancelRun': return result({ status: 'cancel_requested' });
case 'resume': {
@@ -258,6 +267,28 @@ test('long Agent identity and attachment entry fit on a short narrow window', as
expect(errors).toEqual([]);
});
test('the file Sheet counts only uploads and files explicitly delivered in the assistant answer', async ({ page }) => {
const errors = await openChat(page, 'delivery', 375, 640);
const entry = page.getByRole('button', { name: '附件,共 2 个文件', exact: true });
await expect(entry).toBeVisible();
await entry.click();
const panel = page.getByRole('dialog', { name: '本次对话的附件' });
const generated = panel.getByRole('region', { name: '助手生成的' });
await expect(generated.getByRole('button', { name: /^保存 / })).toHaveCount(1);
await expect(generated.getByRole('button', { name: '保存 deliverable.py', exact: true })).toBeVisible();
for (const name of ['故事笔记.md', 'internal.py', 'call.txt', 'cache.json', 'plain.txt', 'inline.py', 'example.py', 'user.txt', 'thought.txt']) {
await expect(generated.getByText(name, { exact: true })).toHaveCount(0);
}
await generated.getByRole('button', { name: '保存 deliverable.py', exact: true }).click();
await expect.poll(() => page.evaluate(() => (window as unknown as FixtureWindow).__cloudChatFixture.calls
.filter(call => call.path === '/api/cloud-agents/files/save').map(call => call.input))).toEqual([{ thread_id: 'story-first', path: '/deliverable.py' }]);
await page.keyboard.press('Escape');
await expect(entry).toBeFocused();
await expectComposerBounds(page);
expect((await operations(page)).some(item => ['submit', 'preview', 'resume', 'deleteAttachment'].includes(String(item?.operation)))).toBe(false);
expect(errors).toEqual([]);
});
for (const width of [375, 768, 1024, 1440]) {
test(`friendly empty conversation keeps the composer reachable at ${width}px`, async ({ page }) => {
const errors = await openChat(page, 'empty', width, width === 375 ? 640 : 760);