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:
2026-10-19 18:46:58 +08:00
parent 2c5bf15cce
commit ec23d44b02
131 changed files with 6663 additions and 1204 deletions
+19 -9
View File
@@ -1,3 +1,4 @@
import { responsesFixture } from "./responses-fixture";
import type { Page } from "@playwright/test";
type MockAgentApiOptions = {
@@ -19,7 +20,7 @@ export async function mockAgentApi(page: Page, options: MockAgentApiOptions = {}
icon: "bolt" as const
};
const triggeredSessions = new Map<string, { sessionId: string; triggerId: string }>();
await page.route("**/api/v1/agent/chat/**", async (route) => {
await page.route("http://127.0.0.1:8787/api/v1/**", async (route) => {
if (route.request().method() === "OPTIONS") {
await route.fulfill({
status: 204,
@@ -34,6 +35,18 @@ export async function mockAgentApi(page: Page, options: MockAgentApiOptions = {}
const pathname = new URL(route.request().url()).pathname;
if (pathname.endsWith("/suggestions") && route.request().method() === "POST") {
await route.fulfill({ headers: corsHeaders, json: {
suggestions: [
{ question: "分析当前管网需要优先核对的压力监测点。", activity_id: null },
{ question: "检查近期供水任务的证据与待确认事项。", activity_id: null },
{ question: "解读近期分析成果并列出进一步验证的问题。", activity_id: null },
],
generated_at: "2026-10-09T07:00:00Z",
} });
return;
}
const collaborationMatch = pathname.match(/\/task\/([^/]+)\/collaboration\/session$/);
if (collaborationMatch && route.request().method() === "POST") {
await route.fulfill({
@@ -99,24 +112,21 @@ export async function mockAgentApi(page: Page, options: MockAgentApiOptions = {}
return;
}
if (pathname.endsWith("/stream")) {
if (pathname.endsWith("/responses")) {
await route.fulfill({
status: 200,
headers: {
...corsHeaders,
"Cache-Control": "no-cache",
"Content-Type": "text/event-stream",
"X-Vercel-AI-UI-Message-Stream": "v1"
"Content-Type": "text/event-stream"
},
body: [
body: responsesFixture([
{ type: "start", messageId: "assistant-browser-test" },
{ type: "text-start", id: "answer" },
{ type: "text-delta", id: "answer", delta: "已收到工况汇总请求。" },
{ type: "text-end", id: "answer" },
{ type: "finish", finishReason: "stop" }
]
.map((part) => `data: ${JSON.stringify(part)}\n\n`)
.join("")
])
});
return;
}
@@ -161,6 +171,6 @@ export async function mockAgentApi(page: Page, options: MockAgentApiOptions = {}
return;
}
await route.fulfill({ headers: corsHeaders, json: {} });
await route.fallback();
});
}