feat: add project-aware agent workspace
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
export const answerUiDemoBlock = {
|
||||
version: "answer-ui@1",
|
||||
block_id: "pressure",
|
||||
fallback_text: "演练压力读数,仍待现场核验。",
|
||||
spec: {
|
||||
root: "root",
|
||||
elements: {
|
||||
root: {
|
||||
type: "Stack",
|
||||
props: { columns: "one", gap: "normal" },
|
||||
children: ["metrics", "chart", "evidence", "location", "form"]
|
||||
},
|
||||
metrics: {
|
||||
type: "Stack",
|
||||
props: { columns: "two", gap: "compact" },
|
||||
children: ["pressure", "flow"]
|
||||
},
|
||||
pressure: {
|
||||
type: "Metric",
|
||||
props: { label: "压力", value: "0.30", unit: "MPa", source: "演练数据" },
|
||||
children: []
|
||||
},
|
||||
flow: {
|
||||
type: "Metric",
|
||||
props: { label: "流量", value: "18", unit: "m³/h", source: "演练数据" },
|
||||
children: []
|
||||
},
|
||||
chart: {
|
||||
type: "Chart",
|
||||
props: {
|
||||
title: "演练监测趋势",
|
||||
chart_type: "line",
|
||||
x_data: ["08:00", "09:00"],
|
||||
series: [{ name: "压力(MPa)", data: [0.3, 0.28] }, { name: "参考压力(MPa)", data: [0.29, 0.26] }],
|
||||
source: "浏览器验收演练数据"
|
||||
},
|
||||
children: []
|
||||
},
|
||||
evidence: {
|
||||
type: "Evidence",
|
||||
props: {
|
||||
title: "查看演练证据",
|
||||
summary: "仅用于组件验收,不代表已核验现场事实。",
|
||||
source: "浏览器演练",
|
||||
evidence_id: "DEMO-001",
|
||||
observed_at: null,
|
||||
kind: "demo"
|
||||
},
|
||||
children: []
|
||||
},
|
||||
location: {
|
||||
type: "MapLocation",
|
||||
props: {
|
||||
label: "定位演练监测点",
|
||||
layer: "scada",
|
||||
feature_ids: ["DEMO-P001"],
|
||||
source: "演练监测点"
|
||||
},
|
||||
children: []
|
||||
},
|
||||
form: {
|
||||
type: "FollowUpForm",
|
||||
props: {
|
||||
title: "补充核验资料",
|
||||
description: "填写后由你主动发送给 Agent。",
|
||||
fields: [{ name: "location", label: "现场位置", kind: "text", required: true }]
|
||||
},
|
||||
children: []
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -34,6 +34,9 @@ export async function mockAgentApi(page: Page, options: MockAgentApiOptions = {}
|
||||
}
|
||||
|
||||
const pathname = new URL(route.request().url()).pathname;
|
||||
if (pathname.endsWith("/topics") && route.request().method() === "GET") {
|
||||
await route.fulfill({ headers: corsHeaders, json: { topics: [] } }); return;
|
||||
}
|
||||
|
||||
if (pathname.endsWith("/suggestions") && route.request().method() === "POST") {
|
||||
await route.fulfill({ headers: corsHeaders, json: {
|
||||
@@ -94,13 +97,14 @@ export async function mockAgentApi(page: Page, options: MockAgentApiOptions = {}
|
||||
const sessionMatch = pathname.match(/\/session\/([^/]+)$/);
|
||||
if (sessionMatch && route.request().method() === "GET") {
|
||||
const collaboration = sessionMatch[1].startsWith("collaboration-");
|
||||
const stored = options.sessions?.find((session) => (session.session_id ?? session.id) === sessionMatch[1]);
|
||||
await route.fulfill({
|
||||
headers: corsHeaders,
|
||||
json: {
|
||||
id: sessionMatch[1],
|
||||
session_id: sessionMatch[1],
|
||||
title: collaboration ? "任务协作问答" : "水质投诉事件调查",
|
||||
purpose: collaboration ? "task_collaboration" : "ordinary_chat",
|
||||
title: stored?.title ?? (collaboration ? "任务协作问答" : "水质投诉事件调查"),
|
||||
purpose: stored?.purpose ?? (collaboration ? "task_collaboration" : "ordinary_chat"),
|
||||
task_id: collaboration ? sessionMatch[1].slice("collaboration-".length) : undefined,
|
||||
status: "active",
|
||||
created_at: "2026-09-22T02:00:00Z",
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import type { Locator, TestInfo } from "@playwright/test";
|
||||
|
||||
/** Measure the actual reading surface so density changes can be compared with screenshots. */
|
||||
export async function captureReadingGeometry(workspace: Locator, testInfo: TestInfo) {
|
||||
const geometry = await workspace.evaluate((element) => {
|
||||
const scroll = element.querySelector("[data-reading-scroll]")!;
|
||||
const metrics = element.querySelector(".answer-ui-stack-comparison")!;
|
||||
const chart = element.querySelector('.answer-ui-chart [role="img"]')!;
|
||||
const bounds = scroll.getBoundingClientRect();
|
||||
return {
|
||||
viewportWidth: window.innerWidth,
|
||||
readingWidth: Math.round(bounds.width),
|
||||
metricsHeight: Math.round(metrics.getBoundingClientRect().height),
|
||||
chartOffset: Math.round(chart.getBoundingClientRect().top - bounds.top + scroll.scrollTop),
|
||||
horizontalOverflow: document.documentElement.scrollWidth > window.innerWidth,
|
||||
};
|
||||
});
|
||||
await testInfo.attach("reading-geometry", { body: JSON.stringify(geometry, null, 2), contentType: "application/json" });
|
||||
return geometry;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
// Translate existing UI scenarios into Responses wire fixtures. Production
|
||||
// clients accept Responses only; this helper belongs exclusively to tests.
|
||||
export function responseFixtureWriter() {
|
||||
export function responseFixtureWriter(conversationId?: string) {
|
||||
let sequence = 0;
|
||||
let id = "resp_browser_fixture";
|
||||
let messageId = "msg_browser_fixture";
|
||||
@@ -13,7 +13,7 @@ export function responseFixtureWriter() {
|
||||
return (chunk: Record<string, unknown>) => {
|
||||
if (chunk.type === "start") {
|
||||
messageId = String(chunk.messageId); id = `resp_${messageId}`;
|
||||
return event("response.created", { response: { id, object: "response", status: "in_progress" } })
|
||||
return event("response.created", { response: { id, object: "response", status: "in_progress", ...(conversationId ? { conversation: { id: conversationId } } : {}) } })
|
||||
+ event("response.output_item.added", { output_index: 0, item: { type: "message", role: "assistant", id: messageId, content: [] } });
|
||||
}
|
||||
if (chunk.type === "text-start") {
|
||||
|
||||
Reference in New Issue
Block a user