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:
@@ -15,7 +15,7 @@ test("real model publishes local components, restores them and receives form inp
|
||||
test.setTimeout(300_000);
|
||||
const target = new URL(realAgentUrl!);
|
||||
expect(["127.0.0.1", "localhost"]).toContain(target.hostname);
|
||||
const base = `${target.origin}/api/v1/agent/chat`;
|
||||
const base = `${target.origin}/api/v1`;
|
||||
const health = await request.get(`${target.origin}/health`);
|
||||
expect(health.ok()).toBe(true);
|
||||
expect(await health.json()).toMatchObject({ ready: true, warmed_up: true });
|
||||
@@ -26,7 +26,7 @@ test("real model publishes local components, restores them and receives form inp
|
||||
body: `globalThis.__TJWATER_CONFIG__ = ${JSON.stringify({
|
||||
TJWATER_AUTH_MODE: "disabled",
|
||||
TJWATER_SERVER_API_BASE_URL: "https://tjwater-api.invalid",
|
||||
TJWATER_AGENT_API_BASE_URL: target.origin,
|
||||
TJWATER_AGENT_URL: target.origin,
|
||||
TJWATER_MAP_URL: "https://workbench-map.invalid/geoserver",
|
||||
TJWATER_GEOSERVER_WORKSPACE: "tjwater_next",
|
||||
TJWATER_ENABLE_MSW: "false"
|
||||
@@ -66,7 +66,7 @@ test("real model publishes local components, restores them and receives form inp
|
||||
await page.getByRole("menuitem", { name: new RegExp(requestedModelLabel) }).click();
|
||||
}
|
||||
const firstResponse = page.waitForResponse(
|
||||
(response) => response.url() === `${base}/stream` && response.request().method() === "POST"
|
||||
(response) => response.url() === `${base}/responses` && response.request().method() === "POST"
|
||||
);
|
||||
await panel.getByRole("button", { name: "发送 Agent 指令" }).click();
|
||||
const stream = await firstResponse;
|
||||
@@ -103,18 +103,18 @@ test("real model publishes local components, restores them and receives form inp
|
||||
.getByLabel("现场位置(必填)")
|
||||
.fill("演练:东侧阀门旁,未实地核验。本轮仍为功能验收,请不要写业务证据或记忆,只确认收到。");
|
||||
const secondResponse = page.waitForResponse(
|
||||
(response) => response.url() === `${base}/stream` && response.request().method() === "POST"
|
||||
(response) => response.url() === `${base}/responses` && response.request().method() === "POST"
|
||||
);
|
||||
await panel.getByRole("button", { name: "发送补充资料" }).click();
|
||||
const submitted = await secondResponse;
|
||||
const submittedBody = submitted.request().postDataJSON() as {
|
||||
messages: Array<{ role: string; parts: Array<{ type: string; text?: string }> }>;
|
||||
input: Array<{ role: string; content: Array<{ type: string; text?: string }> }>;
|
||||
};
|
||||
expect(
|
||||
submittedBody.messages
|
||||
submittedBody.input
|
||||
.filter((message) => message.role === "user")
|
||||
.at(-1)
|
||||
?.parts.some((part) => part.text?.includes("现场位置:演练:东侧阀门旁"))
|
||||
?.content.some((part) => part.text?.includes("现场位置:演练:东侧阀门旁"))
|
||||
).toBe(true);
|
||||
expect(submitted.ok()).toBe(true);
|
||||
await expect(panel.getByRole("button", { name: "发送 Agent 指令" })).toBeVisible({
|
||||
|
||||
Reference in New Issue
Block a user