feat(agent): support Responses steering and project-aware tools
Use the dedicated TJWATER_AGENT_URL and Responses stream protocol. Keep running turns active while users steer or queue follow-ups and edit pending delivery. Refresh the model catalog before generation and preserve approval mode selection. Integrate scoped investigation messaging/presentation, geographic place selection, current Agent composer styles and supporting workbench contracts. Validation: 77 focused Vitest checks, 21 browser cases, lint, typecheck and production build. Preserve direct Server/Agent routing and project authentication.
This commit is contained in:
@@ -102,16 +102,28 @@ test.describe("外围工单测试", () => {
|
||||
await expect(panel.getByText("工单接口测试", { exact: true })).toBeVisible();
|
||||
await expect(panel.getByText("历史监测回放", { exact: true })).toHaveCount(0);
|
||||
await expect(panel.getByText("测试数据", { exact: true })).toHaveCount(0);
|
||||
const workOrderList = page.getByLabel("工单列表");
|
||||
await expect(workOrderList).toContainText("合成通信核查");
|
||||
await expect(workOrderList).toContainText("合成压力偏差复核");
|
||||
await expect(workOrderList).toContainText("合成零流量样本确认");
|
||||
await expect(workOrderList).toContainText("待处理");
|
||||
await expect(workOrderList).toContainText("处理中");
|
||||
await expect(workOrderList).toContainText("已完成");
|
||||
await expect(workOrderList.getByText("工单报告", { exact: true })).toHaveCount(3);
|
||||
await expect(workOrderList.getByText("暂无现场反馈", { exact: true })).toHaveCount(3);
|
||||
await expect(workOrderList.getByText("活动记录", { exact: true })).toHaveCount(0);
|
||||
const appointmentList = panel.getByLabel("预约工单列表");
|
||||
const complaintList = panel.getByLabel("投诉工单列表");
|
||||
const burstList = panel.getByLabel("爆管上报工单列表");
|
||||
await expect(appointmentList).toContainText("合成通信核查");
|
||||
await expect(appointmentList).toContainText("合成压力偏差复核");
|
||||
await expect(appointmentList).toContainText("合成零流量样本确认");
|
||||
await expect(appointmentList).toContainText("待处理");
|
||||
await expect(appointmentList).toContainText("处理中");
|
||||
await expect(appointmentList).toContainText("已完成");
|
||||
await expect(appointmentList.getByText("工单报告", { exact: true })).toHaveCount(3);
|
||||
await expect(appointmentList.getByText("暂无现场反馈", { exact: true })).toHaveCount(3);
|
||||
await expect(complaintList).toContainText("水质投诉:演示小区");
|
||||
await expect(complaintList.getByRole("region", { name: "投诉工单信息" })).toContainText("发生时间");
|
||||
await expect(complaintList.getByText("工单报告", { exact: true })).toHaveCount(0);
|
||||
await expect(burstList).toContainText("爆管上报:演示路口");
|
||||
await expect(burstList.getByRole("region", { name: "爆管上报工单信息" })).toContainText("发生时间");
|
||||
await expect(complaintList).toHaveAttribute("open", "");
|
||||
await complaintList.locator("summary").click();
|
||||
await expect(complaintList).not.toHaveAttribute("open", "");
|
||||
await expect(complaintList.getByText("水质投诉:演示小区")).toBeHidden();
|
||||
await complaintList.locator("summary").click();
|
||||
await expect(complaintList.getByText("水质投诉:演示小区")).toBeVisible();
|
||||
expect(requestedPaths).toEqual(expect.arrayContaining([
|
||||
"/api/sample/v1/context",
|
||||
"/api/sample/v1/work-orders"
|
||||
@@ -257,7 +269,12 @@ test.describe("外围工单测试", () => {
|
||||
if (path.endsWith("/work-orders") && request.method() === "POST") {
|
||||
const body = request.postDataJSON();
|
||||
writes.push(body);
|
||||
const item = { ...body, work_order_id: "50000000-0000-4000-8000-000000000001", project_id: projectId, revision: 1, origin: "synthetic", created_at: now, updated_at: now, created_by: "tester", updated_by: "tester", activity: [{ sequence: 1, occurred_at: now, actor: "tester", kind: "created", title: "工单创建", detail: "预约工单已创建。" }] };
|
||||
const createdDetail = body.work_order_type === "water_quality_complaint"
|
||||
? "投诉工单已创建。"
|
||||
: body.work_order_type === "burst_report"
|
||||
? "爆管上报工单已创建。"
|
||||
: "预约工单已创建。";
|
||||
const item = { ...body, work_order_id: "50000000-0000-4000-8000-000000000001", project_id: projectId, revision: 1, origin: "synthetic", created_at: now, updated_at: now, created_by: "tester", updated_by: "tester", activity: [{ sequence: 1, occurred_at: now, actor: "tester", kind: "created", title: "工单创建", detail: createdDetail }] };
|
||||
delete item.request_id; workOrders = [item]; return route.fulfill({ status: 201, json: item });
|
||||
}
|
||||
if (path.includes("/work-orders/") && request.method() === "PATCH") {
|
||||
@@ -280,7 +297,8 @@ test.describe("外围工单测试", () => {
|
||||
await page.getByRole("button", { name: "外围样板管理" }).click();
|
||||
const panel = page.getByRole("complementary", { name: "外围工单测试" });
|
||||
await expect(panel.getByText("工单接口测试", { exact: true })).toBeVisible();
|
||||
await expect(panel.getByLabel("工单列表")).toContainText("暂无测试工单");
|
||||
await expect(panel.getByLabel("预约工单列表")).toContainText("暂无预约工单");
|
||||
await expect(panel.getByLabel("投诉工单列表")).toContainText("暂无投诉工单");
|
||||
await expect(panel.getByRole("textbox", { name: "标题" })).toBeHidden();
|
||||
await panel.getByRole("button", { name: "新增预约工单" }).click();
|
||||
let editor = panel.getByRole("region", { name: "新增预约工单" });
|
||||
@@ -302,7 +320,7 @@ test.describe("外围工单测试", () => {
|
||||
|
||||
await panel.getByRole("button", { name: "编辑工单 合成预约维修" }).click();
|
||||
editor = panel.getByRole("region", { name: "编辑工单 合成预约维修" });
|
||||
await expect(panel.getByLabel("工单列表").getByRole("region", { name: "编辑工单 合成预约维修" })).toBeVisible();
|
||||
await expect(panel.getByLabel("预约工单列表").getByRole("region", { name: "编辑工单 合成预约维修" })).toBeVisible();
|
||||
await expect(editor.locator("xpath=ancestor::article")).toContainText("合成预约维修");
|
||||
await expect(editor.getByRole("textbox", { name: "标题" })).toBeFocused();
|
||||
await expect(panel.getByRole("button", { name: "编辑工单 合成预约维修" })).toHaveCount(0);
|
||||
@@ -387,7 +405,9 @@ test.describe("外围工单测试", () => {
|
||||
await panel.getByRole("textbox", { name: "事件位置" }).fill("演示路与样板路交叉口");
|
||||
await panel.getByRole("textbox", { name: "现场描述" }).fill("路面积水并伴随压力突降");
|
||||
await panel.getByRole("button", { name: "创建事件工单" }).click();
|
||||
await expect(panel.getByText("爆管上报", { exact: true }).last()).toBeVisible();
|
||||
await expect(panel.getByText("爆管上报工单", { exact: true }).last()).toBeVisible();
|
||||
await expect(panel.getByLabel("爆管上报工单列表")).toContainText("爆管上报:演示路与样板路交叉口");
|
||||
await expect(panel.getByLabel("爆管上报工单列表")).toContainText("爆管上报工单已创建。");
|
||||
expect(workOrders[0]).toMatchObject({
|
||||
work_order_type: "burst_report",
|
||||
scenario_code: "BURST-REPORT-001",
|
||||
@@ -439,7 +459,7 @@ test.describe("外围工单测试", () => {
|
||||
page.on("request", (request) => {
|
||||
if (
|
||||
request.method() === "POST" &&
|
||||
request.url().includes("/api/v1/agent/chat/") &&
|
||||
request.url().includes("/api/v1/") &&
|
||||
request.postData()
|
||||
) {
|
||||
agentRequests.push(request.postDataJSON() as Record<string, unknown>);
|
||||
@@ -450,7 +470,7 @@ test.describe("外围工单测试", () => {
|
||||
body: `globalThis.__TJWATER_CONFIG__=${JSON.stringify({
|
||||
TJWATER_AUTH_MODE: "disabled",
|
||||
TJWATER_SERVER_API_BASE_URL: "http://server.test",
|
||||
TJWATER_AGENT_API_BASE_URL: "http://127.0.0.1:8787",
|
||||
TJWATER_AGENT_URL: "http://127.0.0.1:8787",
|
||||
TJWATER_PERIPHERAL_SAMPLE_API_BASE_URL: "http://peripheral.test"
|
||||
})}`
|
||||
}));
|
||||
@@ -643,7 +663,7 @@ test.describe("外围工单测试", () => {
|
||||
status: "open"
|
||||
});
|
||||
expect(agentRequests.filter((body) => "trigger_context" in body)).toHaveLength(0);
|
||||
await page.route("**/api/v1/agent/chat/sessions", (route) => route.fulfill({ json: { sessions: [{
|
||||
await page.route("**/api/v1/sessions", (route) => route.fulfill({ json: { sessions: [{
|
||||
id: "browser-session-1", title: "水质投诉调查", created_at: "2026-09-23T01:00:00Z", updated_at: "2026-09-23T01:00:00Z",
|
||||
trigger: { subject_id: workOrderId, workflow: "water_quality_complaint", occurred_at: "2026-09-23T01:00:00Z" }
|
||||
}] } }));
|
||||
@@ -735,7 +755,7 @@ function createDemoDataset(projectId: string) {
|
||||
}
|
||||
|
||||
function createSourceWorkOrders(projectId: string) {
|
||||
return [
|
||||
const appointments = [
|
||||
["合成通信核查", "核查监测点通信和数据上送状态", "open", "high"],
|
||||
["合成压力偏差复核", "复核压力偏差与历史回放证据", "in_progress", "urgent"],
|
||||
["合成零流量样本确认", "确认零流量样本及质量标记", "completed", "normal"]
|
||||
@@ -760,4 +780,48 @@ function createSourceWorkOrders(projectId: string) {
|
||||
created_by: "synthetic-generator",
|
||||
updated_by: "synthetic-generator"
|
||||
}));
|
||||
const events = [
|
||||
{
|
||||
work_order_id: "50000000-0000-4000-8000-000000000004",
|
||||
title: "水质投诉:演示小区",
|
||||
summary: "居民反映晨间用水有异味",
|
||||
priority: "high",
|
||||
work_order_type: "water_quality_complaint",
|
||||
location: "演示小区",
|
||||
scenario_code: "WQ-SECONDARY-SUPPLY-001",
|
||||
activity_detail: "投诉工单已创建。"
|
||||
},
|
||||
{
|
||||
work_order_id: "50000000-0000-4000-8000-000000000005",
|
||||
title: "爆管上报:演示路口",
|
||||
summary: "路面积水并伴随压力突降",
|
||||
priority: "urgent",
|
||||
work_order_type: "burst_report",
|
||||
location: "演示路口",
|
||||
scenario_code: "BURST-REPORT-001",
|
||||
activity_detail: "爆管上报工单已创建。"
|
||||
}
|
||||
].map(({ activity_detail, ...event }) => ({
|
||||
...event,
|
||||
project_id: projectId,
|
||||
revision: 1,
|
||||
status: "open",
|
||||
assignee: null,
|
||||
scheduled_at: "2026-09-17T08:30:00Z",
|
||||
feedback_requirements: [],
|
||||
activity: [{
|
||||
sequence: 1,
|
||||
occurred_at: "2026-09-17T08:30:00Z",
|
||||
actor: "synthetic-generator",
|
||||
kind: "created",
|
||||
title: "工单创建",
|
||||
detail: activity_detail
|
||||
}],
|
||||
origin: "synthetic",
|
||||
created_at: "2026-09-17T08:30:00Z",
|
||||
updated_at: "2026-09-17T08:30:00Z",
|
||||
created_by: "synthetic-generator",
|
||||
updated_by: "synthetic-generator"
|
||||
}));
|
||||
return [...appointments, ...events];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user