feat: show recorded times in personal Agent chat

This commit is contained in:
鲨鱼辣椒 committed 2026-10-13 17:51:11 +08:00
1 parent a12cc4f4e9
commit 64b4a84a2d
19 files changed
+426 -39

No files matched your search

+31 -3
View File
@@ -83,7 +83,7 @@ type FixtureCall = { path: string; method: string; input: Record<string, unknown
type RecoveryFixture = { agents: CloudAgentDraft[]; pending: CloudPendingOperation[]; retry: 'fail' | 'success'; calls: FixtureCall[] };
type FixtureWindow = Window & { __recoveryFixture: RecoveryFixture };
async function installFixture(page: Page, count = 2) {
async function installFixture(page: Page, count = 2, publication = false) {
page.on('pageerror', error => { throw error; });
await page.addInitScript(({ agent, records, retryError }) => {
const scope = window as unknown as FixtureWindow;
@@ -104,7 +104,12 @@ async function installFixture(page: Page, count = 2) {
const record = state.pending.find(item => item.id === input?.id);
if (!record) throw new Error('Recovery must keep the original pending id');
if (input?.discard) { state.pending = state.pending.filter(item => item.id !== record.id); return result({ discarded: true }); }
if (state.retry === 'fail') return { ok: true, data: { status: 503, ok: false, json: { error: retryError } } };
if (state.retry === 'fail') return { ok: true, data: { status: 503, ok: false, json: { error: retryError,
...(record.operation === 'publish' ? { code: 'cloud_service_unavailable', commandOutcome: 'unknown' } : {}) } } };
if (record.operation === 'publish') {
state.pending = state.pending.filter(item => item.id !== record.id);
return result({ operation: 'publish', result: { version: 1, draft_revision: record.input.expected_revision } });
}
const created = { ...agent, slug: 'ml-' + 'b'.repeat(32), name: String(record.input.name), published_version: null };
state.agents.push(created); state.pending = state.pending.filter(item => item.id !== record.id);
return result({ operation: 'createAgent', result: created });
@@ -124,7 +129,9 @@ async function installFixture(page: Page, count = 2) {
}
},
} } });
}, { agent, records: records.slice(0, count), retryError });
}, { agent, records: publication ? [{ id: 'e2e-publish-original', operation: 'publish',
input: { slug: agent.slug, operation_id: 'e2e-publish-original', expected_revision: 1 }, created_at: records[0].created_at }]
: records.slice(0, count), retryError });
}
async function recoveryCalls(page: Page) {
@@ -192,6 +199,27 @@ test('retry keeps the same pending identity and errors remain inside the modal',
expect(await recoveryCalls(page)).toHaveLength(2);
});
test('an application reminder gives a simple action and reuses its record after a network failure', async ({ page }) => {
await page.setViewportSize({ width: 375, height: 850 }); await installFixture(page, 1, true); await page.goto(baseUrl);
await page.getByRole('button', { name: /^待处理记录/ }).click();
const dialog = page.getByRole('dialog', { name: '待处理记录', exact: true });
await expect(dialog.getByRole('heading', { name: '应用设置', exact: true })).toBeVisible();
await expect(dialog.getByText('网络可能不稳定,请再点一次「应用」。', { exact: true })).toBeVisible();
expect(await recoveryCalls(page)).toEqual([]);
const apply = dialog.getByRole('button', { name: '应用', exact: true });
await apply.click();
await expect(dialog.getByRole('alert')).toHaveText('网络可能不稳定,请再点一次「应用」。');
await expect(apply).toBeEnabled(); await expectModalFits(dialog);
await page.screenshot({ path: test.info().outputPath('application-message-375.png'), animations: 'disabled' });
await page.evaluate(() => { (window as unknown as FixtureWindow).__recoveryFixture.retry = 'success'; });
await apply.click();
await expect(dialog.getByText('设置已应用。', { exact: true })).toBeVisible();
await expect(dialog.getByText('查看结果', { exact: true })).toHaveCount(0);
expect((await recoveryCalls(page)).map(call => call.input)).toEqual([
{ id: 'e2e-publish-original', discard: false }, { id: 'e2e-publish-original', discard: false },
]);
});
test('discard is explicit and reminders do not follow into configuration, chat or creation', async ({ page }) => {
await page.setViewportSize({ width: 375, height: 850 }); await installFixture(page); await page.goto(baseUrl);
const trigger = page.getByRole('button', { name: /^创建记录/ });
+10 -3
View File
@@ -30,7 +30,7 @@ test('personal cloud Agent identity, configuration and separate published chat i
const knowledgeFile = { file_id: 'document', name: '本体(Ontology)学习路线图.pdf', size: 34000, error: null };
let limits: { request_limit_points: string | null; daily_limit_points: string | null } = { request_limit_points: null, daily_limit_points: null };
const jobs: Record<string, unknown>[] = [];
const configuration = { model: '', tools: [], knowledges: [], mcps: [], skills: [], preload_skills: [], subagents: [], tool_approval_mode: 'default', max_execution_steps: 40, max_output_tokens: 4096, max_run_seconds: 600 };
const configuration = { model: '', tools: [], knowledges: [], mcps: [], skills: [], preload_skills: [], subagents: ['historical-child'], tool_approval_mode: 'default', max_execution_steps: 40, max_output_tokens: 4096, max_run_seconds: 600 };
const result = (json: unknown) => ({ ok: true, data: { status: 200, ok: true, json } });
ipcMain.removeHandler('hostapi:fetch');
ipcMain.handle('hostapi:fetch', async (_event, request: { path?: string; method?: string; body?: string }) => {
@@ -94,7 +94,7 @@ test('personal cloud Agent identity, configuration and separate published chat i
if (body.operation === 'submit' || body.operation === 'preview') return result({ request_id: input.request_id, thread_id: 'thread-1', run_id: 'run-1', version: '1', status: 'dispatched' });
if (body.operation === 'history') return result({ thread_id: 'thread-1', next_offset: null,
queued_requests: [], schedule_proposals: [{ id: 'proposal', name: '每天整理选题', prompt: '每天给我三个适合个人创作的选题', cron_expression: '30 8 * * *', timezone: 'Asia/Shanghai' }],
messages: [{ id: 1, role: 'user', content: '先给我一些创作建议' }, { id: 2, role: 'assistant', content: '## 创作建议\n先从一个真实的观察开始。\n\n' + '记录今天的一次观察,把具体场景、你的感受和想分享的发现写下来。\n\n'.repeat(22) }],
messages: [{ id: 1, role: 'user', content: '先给我一些创作建议', created_at: '2026-10-13T09:24:30Z' }, { id: 2, role: 'assistant', created_at: '2026-10-13T09:25:40Z', content: '## 创作建议\n先从一个真实的观察开始。\n\n' + '记录今天的一次观察,把具体场景、你的感受和想分享的发现写下来。\n\n'.repeat(22) }],
run: { agent_run_id: 'run-1', request_id: 'request-1', thread_id: 'thread-1', status: 'completed', version: '1' } });
if (body.operation === 'viewed') return result({ viewed: true });
if (body.operation === 'attachments') return result({ attachments: [] });
@@ -244,6 +244,8 @@ test('personal cloud Agent identity, configuration and separate published chat i
await expect(page.getByText('给它一本手册,照着方法做。', { exact: true })).toHaveCount(0);
await expect(page.getByText('管理我的云端能力', { exact: true })).toHaveCount(0);
await expect(page.getByText('未选择的资源不会开放给此智能体。通过微信或 API 使用时也遵循本配置。', { exact: true })).toHaveCount(0);
await expect(page.getByRole('group', { name: '子智能体', exact: true })).toHaveCount(0);
await expect(page.getByRole('button', { name: '选择子智能体', exact: true })).toHaveCount(0);
await expect(page.getByRole('group', { name: /^知识库/ })).toBeVisible();
await expect(page.getByRole('heading', { name: '费用上限', exact: true })).toHaveCount(0);
await expect(page.getByRole('heading', { name: '运行限制', exact: true })).toHaveCount(0);
@@ -263,7 +265,7 @@ test('personal cloud Agent identity, configuration and separate published chat i
await expect(prompt).toHaveValue(initialPrompt);
await page.getByRole('button', { name: '保存草稿', exact: true }).click();
await expect(page.getByRole('button', { name: '已保存', exact: true })).toBeDisabled();
expect((await fixtureState()).draft).toMatchObject({ purpose: '一起梳理故事和文章', system_prompt: initialPrompt, configuration: { model: 'reasoning-model', tools: ['fixture-tool'] } });
expect((await fixtureState()).draft).toMatchObject({ purpose: '一起梳理故事和文章', system_prompt: initialPrompt, configuration: { model: 'reasoning-model', tools: ['fixture-tool'], subagents: ['historical-child'] } });
await editor.getByRole('button', { name: '我的智能体', exact: true }).click();
const card = page.getByRole('article', { name: '我的写作搭档', exact: true });
await expect(card.getByText('一起梳理故事和文章', { exact: true })).toBeVisible();
@@ -328,6 +330,11 @@ test('personal cloud Agent identity, configuration and separate published chat i
expect(await messageOperations()).toEqual(['submit']);
const userBubble = formalChat.locator('.agent-chat-message[data-role="user"]');
await expect(userBubble).toHaveText(/先给我一些创作建议/);
await expect(userBubble.locator('time')).toHaveAttribute('datetime', '2026-10-13T09:24:30.000Z');
await expect(userBubble.locator('time')).toHaveAccessibleName(/^发送时间:/);
const answerTime = formalChat.locator('.agent-chat-message[data-role="assistant"] time');
await expect(answerTime).toHaveAttribute('datetime', '2026-10-13T09:25:40.000Z');
await expect(answerTime).toHaveAccessibleName(/^回复时间:/);
const bubbleStyle = await userBubble.evaluate(element => {
const style = getComputedStyle(element);
return { background: style.backgroundColor, radius: Number.parseFloat(style.borderTopLeftRadius) };