feat: Page AI Reasonix desktop session alignment, settings IA cleanup, knowledge RAG hardening

- ACP client/session manager: Reasonix desktop live session context
- Hermes tools: knowledge_rag tool manifest and skill updates
- Browser runtime: sidebar page AI permission/profile/render/session/tree modules
- Routes: hermes_client, hermes_tools, knowledge_rag, web_shell
- Scripts: reasonix ACP wrapper, LightRAG MCP, smoke tasks 159/558/559/561/562
- Skills: mnote-knowledge-rag and mnote-lightrag-bridge SKILL.md updates
This commit is contained in:
lix-2026
2026-06-13 22:20:01 +08:00
parent 2236a053c0
commit 4a7efd4a30
30 changed files with 5321 additions and 291 deletions
@@ -163,13 +163,43 @@ async function main() {
assert(runId, "缺少 runId");
const text = await page.locator('[data-testid="wolai-page-ai-drawer"] .wolai-page-ai-message--assistant .wolai-page-ai-message-text').last().textContent({ timeout: TIMEOUT_MS });
assert.equal(String(text || '').replace(/\s+/g, '').trim(), marker);
const runtimeStrip = await page.locator('[data-page-ai-runtime-strip]').textContent({ timeout: TIMEOUT_MS });
assert(String(runtimeStrip || '').includes('reasonix'), `runtime strip 应显示 reasonix: ${runtimeStrip}`);
assert(!String(runtimeStrip || '').includes('输出中'), `完成后 runtime strip 不应残留输出中: ${runtimeStrip}`);
const rows = sqliteJson(`SELECT status FROM ai_runtime_runs WHERE run_id=${sqlQuote(runId)};`);
assert.equal(rows[0] && rows[0].status, "completed", "SQLite run status 应为 completed");
const terminalEvents = sqliteJson(`SELECT COUNT(*) AS count FROM ai_runtime_events WHERE run_id=${sqlQuote(runId)} AND event_type='run.completed';`);
assert(Number(terminalEvents[0] && terminalEvents[0].count) >= 1, "应持久化 run.completed");
const screenshotPath = path.join(OUT_DIR, "task559-terminal-status.png");
await page.screenshot({ path: screenshotPath, fullPage: true });
const result = { ok: true, baseUrl, outDir: OUT_DIR, runId, marker, screenshotPath };
await page.locator('[data-page-ai-runtime-strip]').click({ timeout: TIMEOUT_MS });
await page.waitForFunction(() => document.querySelector('[data-testid="wolai-page-ai-drawer"]')?.getAttribute('data-page-ai-page') === 'status', null, { timeout: TIMEOUT_MS });
await page.waitForFunction(() => {
const detail = document.querySelector('[data-page-ai-runtime-detail]')?.textContent || '';
const jobs = document.querySelector('[data-page-ai-jobs-list]')?.textContent || '';
const logs = document.querySelector('[data-page-ai-logs-list]')?.textContent || '';
const usage = document.querySelector('[data-page-ai-usage-detail]')?.textContent || '';
return detail.includes('runtime') && detail.includes('MCP/tools') && jobs.length > 0 && logs.length > 0 && usage.length > 0;
}, null, { timeout: TIMEOUT_MS });
const statusText = await page.locator('[data-page-ai-panel="status"]').textContent({ timeout: TIMEOUT_MS });
assert(String(statusText || '').includes('Current runtime'), '状态页应显示 Current runtime');
assert(String(statusText || '').includes('Queue'), '状态页应显示 Queue');
assert(String(statusText || '').includes('Jobs'), '状态页应显示 Jobs');
assert(String(statusText || '').includes('Logs tail'), '状态页应显示 Logs tail');
assert(String(statusText || '').includes('Usage'), '状态页应显示 Usage');
const runtimePanelScreenshotPath = path.join(OUT_DIR, "task559-runtime-panel.png");
await page.screenshot({ path: runtimePanelScreenshotPath, fullPage: true });
await page.setViewportSize({ width: 452, height: 900 });
await page.locator('[data-page-ai-tab="agent"]').first().click({ timeout: TIMEOUT_MS });
const mobileTabs = await page.locator('[data-page-ai-panel="agent"] .wolai-page-ai-settings-tabs [data-page-ai-tab]').evaluateAll((nodes) => nodes.map((node) => {
const rect = node.getBoundingClientRect();
return { text: node.textContent.trim(), box: { width: rect.width, height: rect.height } };
}));
assert.deepEqual(mobileTabs.map((tab) => tab.text), ['MNote', 'Reasonix', 'Hermes'], `移动宽度设置 tabs 应保持三层: ${JSON.stringify(mobileTabs)}`);
assert(mobileTabs.every((tab) => tab.box.width > 40 && tab.box.height > 24), `移动宽度 tabs 不应挤压不可点: ${JSON.stringify(mobileTabs)}`);
const mobileSettingsScreenshotPath = path.join(OUT_DIR, "task559-settings-mobile.png");
await page.screenshot({ path: mobileSettingsScreenshotPath, fullPage: true });
const result = { ok: true, baseUrl, outDir: OUT_DIR, runId, marker, screenshotPath, runtimePanelScreenshotPath, mobileSettingsScreenshotPath };
fs.writeFileSync(path.join(OUT_DIR, "result.json"), `${JSON.stringify(result, null, 2)}\n`, "utf8");
console.log(JSON.stringify(result, null, 2));
await context.close().catch(() => undefined);