Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .maka-shots/workhub-context-after.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .maka-shots/workhub-context-before.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
50 changes: 48 additions & 2 deletions apps/desktop/e2e/workhub-reconstruction.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,16 @@ test('WorkHub rebuilds Session conversation after navigating away and back', asy
}),
).toBeVisible();

const routedPrompt = `继续${sessionName},补充重复投递测试点。`;
const routedPrompt = '继续这个工作,补充重复投递测试点。';
const workHubComposer = page.locator(
'.workhub-surface .maka-composer-editor [contenteditable="true"]',
);
await workHubComposer.fill(routedPrompt);
await workHubComposer.press('Enter');
await expect(page.locator('.workhub-submitted').last()).toBeVisible();
await page.locator('.workhub-submitted > button').last().click();
await page.locator('.workhub-turn', { hasText: routedPrompt })
.locator('.workhub-submitted > button')
.click();
await expect(page.getByRole('main', { name: 'WorkHub' })).toBeHidden();

await page.getByRole('button', { name: 'WorkHub', exact: true }).click();
Expand All @@ -62,3 +64,47 @@ test('WorkHub rebuilds Session conversation after navigating away and back', asy
}),
).toBeVisible();
});

test('WorkHub handles a first natural-language correction', async ({
window: page,
}) => {
const composer = page.locator(COMPOSER_INPUT);
await composer.fill('检查支付回调重复投递时的幂等性');
await composer.press('Enter');
await expect(page.getByRole('button', { name: '重新生成' })).toHaveCount(1, {
timeout: 20_000,
});
await page.evaluate(async () => {
await window.maka.settings.updateClient({ workHub: { enabled: true } });
});
await expect(page.getByRole('main', { name: 'WorkHub' })).toBeVisible();
await page.evaluate(async () => {
await window.maka.sessions.create({ name: '登录稳定性' });
});
await expect(page.getByText('2 项工作', { exact: true })).toBeVisible();

const workHubComposer = page.locator(
'.workhub-surface .maka-composer-editor [contenteditable="true"]',
);
await workHubComposer.fill('继续这个工作,补充重复投递测试点。');
await workHubComposer.press('Enter');
const continuedTurn = page.locator('.workhub-turn', {
hasText: '继续这个工作,补充重复投递测试点。',
});
await expect(
continuedTurn.locator('.workhub-submitted-session strong'),
).toHaveText('检查支付回调重复投递时的幂等性');

await workHubComposer.fill('不是这个,换成登录稳定性,补充刷新令牌失败判定。');
await expect(
page.locator('.workhub-surface').getByRole('button', { name: '发送' }),
).toBeEnabled();
await workHubComposer.press('Enter');

await expect(page.locator('.workhub-correction-note').last()).toBeVisible();
await expect(
page.locator('.workhub-turn', {
hasText: '不是这个,换成登录稳定性,补充刷新令牌失败判定。',
}).locator('.workhub-submitted-session strong'),
).toHaveText('登录稳定性');
});
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import type { DesktopSessionSummary } from '../../preload/bridge-contract.js';
import { collectRuntimeHostSessionCatalogs } from '../../preload/runtime-host-session-catalog.js';
import {
collectRuntimeHostSessionCatalogs,
collectRuntimeHostSessionCatalogsWithCoverage,
} from '../../preload/runtime-host-session-catalog.js';

function session(id: string, activityAt: number): DesktopSessionSummary {
return { id, activityAt } as DesktopSessionSummary;
Expand All @@ -36,6 +39,16 @@ test('keeps healthy Host catalogs when another Host rejects', async () => {
assert.deepEqual(sessions.map(({ id }) => id), ['newer', 'older']);
});

test('reports exactly which Host catalogs are complete', async () => {
const catalog = await collectRuntimeHostSessionCatalogsWithCoverage([
{ hostId: 'local', sessions: Promise.resolve([session('local-session', 1)]) },
{ hostId: 'remote', sessions: Promise.reject(new Error('remote unavailable')) },
]);

assert.deepEqual(catalog.sessions.map(({ id }) => id), ['local-session']);
assert.deepEqual(catalog.completeHostIds, ['local']);
});

test('fails when every Host catalog rejects', async () => {
await assert.rejects(
collectRuntimeHostSessionCatalogs([
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -609,7 +609,7 @@ test("queues a mid-turn send as steering when the Host reports the session busy"
assert.deepEqual(submits, [
{
sessionId: "session-1",
messageId: "id-1",
messageId: "turn-1",
content: { text: "also check the tests", inlineReferences: [] },
placement: "current_turn",
},
Expand All @@ -629,6 +629,7 @@ test("queues a mid-turn send as steering when the Host reports the session busy"

test("starts the turn from the queued message when the busy race resolves idle", async () => {
const changes: unknown[] = [];
const submits: unknown[] = [];
const ipc = ipcHarness();
registerExecutionIpc(
{
Expand All @@ -641,10 +642,13 @@ test("starts the turn from the queued message when the busy race resolves idle",
"Session already has an active root Turn",
);
},
submitMessage: async () => ({
disposition: "turn_started",
turnId: "turn-9",
}),
submitMessage: async (input) => {
submits.push(input);
return {
disposition: "turn_started",
turnId: "turn-9",
};
},
}),
observer: unusedObserver(),
attachmentApprovals: createAttachmentApprovalRegistry(),
Expand All @@ -671,6 +675,12 @@ test("starts the turn from the queued message when the busy race resolves idle",
inlineReferences: [],
skillInvocation: { loaded: [], failed: [], receipts: [] },
});
assert.deepEqual(submits, [{
sessionId: "session-1",
messageId: "turn-1",
content: { text: "also check the tests", inlineReferences: [] },
placement: "current_turn",
}]);
assert.deepEqual(changes, [
{ reason: "status-change", sessionId: "session-1", turnId: "turn-9" },
]);
Expand Down
Loading