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
@@ -1,5 +1,6 @@
import { z } from "zod";
import { investigationReportSchema } from "./investigation-report";
import { investigationReportSchema, investigationDiagramSchema } from "./investigation-report";
import { investigationPresentationCursorSchema } from "./investigation-presentation";
import type { AccessTokenProvider } from "@/shared/auth/keycloak-auth";
import { env } from "@/shared/config/env";
import { createWorkbenchHeaders, findOwnedWorkbenchProject, getServerError } from "./project-client";
@@ -45,6 +46,7 @@ const taskSummarySchema = z.object({
workflow: z.string().min(1),
lifecycle_status: z.enum(["open", "waiting_human", "completed", "failed", "cancelled"]),
active_run: operationalTaskRunSchema.nullable(),
latest_run: operationalTaskRunSchema.nullable().optional(),
status: taskStatusSchema,
priority: taskPrioritySchema,
revision: z.number().int().positive(),
@@ -115,6 +117,14 @@ const reportRevisionSchema = z.object({
published_by: z.string().min(1),
report: reportInputSchema
}).strict();
const progressSnapshotSchema = z.object({
revision: z.number().int().positive(),
run_id: z.string().uuid(),
execution_generation: z.number().int().positive(),
published_at: workbenchDateTimeSchema,
source_session_id: z.string().min(1),
progress: z.object({ summary: z.string().trim().min(1), diagram: investigationDiagramSchema.nullable() }).strict()
}).strict();
const reportPageSchema = z.object({
items: z.array(reportRevisionSchema),
next_offset: z.number().int().nonnegative().nullable(),
@@ -212,6 +222,7 @@ const externalWorkOrderSnapshotSchema = z.preprocess((value) => {
const operationalTaskSchema = taskSummarySchema.extend({
latest_plan: planSnapshotSchema.nullable(),
latest_report: reportRevisionSchema.nullable(),
latest_progress: progressSnapshotSchema.nullish(),
evidence: z.array(evidenceSchema),
actions: z.array(actionSchema),
access_requests: z.array(accessRequestSchema).default([]),
@@ -269,7 +280,43 @@ const scheduleSchema = z.object({
}).strict();
const schedulePageSchema = z.object({ items: z.array(scheduleSchema) }).strict();
const taskDataSnapshotSchema = z.object({
source: z.enum(["workbench_runtime", "scada_devices", "scada_readings", "peripheral_points", "peripheral_work_orders", "peripheral_observations", "peripheral_test_data"]),
// Snapshot sources include product MCP queries; access-request scopes remain unchanged.
source: z.enum(["workbench_runtime", "scada_devices", "scada_readings", "peripheral_points", "peripheral_work_orders", "peripheral_observations", "peripheral_test_data",
"get_analysis_run",
"get_element_gis_properties",
"get_element_properties",
"get_model_baseline_at_time",
"get_network_options",
"get_network_schema",
"get_scada_device",
"get_sensor_placement_run",
"get_source_gis_properties",
"get_task_context",
"get_task_evidence",
"get_workbench_runtime",
"list_analysis_results",
"list_analysis_runs",
"list_elements",
"list_scada_devices",
"list_sensor_placement_runs",
"query_analysis_series",
"query_analysis_values",
"query_element_history",
"query_model_baseline_field",
"query_model_baseline_series",
"query_peripheral_data",
"query_scada_readings",
"run_burst_detection",
"run_simulation",
"run_contaminant_simulation",
"analyze_valve_closure",
"analyze_flushing",
"analyze_pressure_regulation",
"optimize_sensor_placement",
"run_burst_location",
"analyze_valve_isolation",
"run_leakage_identification",
]),
source_ref: z.string().min(1),
queried_at: workbenchDateTimeSchema,
origin: z.enum(["system_data", "synthetic"]),
@@ -286,6 +333,7 @@ export type OperationalTaskEvidence = z.infer<typeof evidenceSchema>;
export type OperationalTaskNotification = z.infer<typeof notificationSchema>;
export type OperationalTaskReport = z.infer<typeof reportRevisionSchema>;
export type OperationalTaskReportPage = z.infer<typeof reportPageSchema>;
export type OperationalTaskProgress = z.infer<typeof progressSnapshotSchema>;
export type OperationalTaskSchedule = z.infer<typeof scheduleSchema>;
export type OperationalTaskDataSnapshot = z.infer<typeof taskDataSnapshotSchema>;
export type ExternalWorkOrderSnapshot = z.infer<typeof externalWorkOrderSnapshotSchema>;
@@ -342,6 +390,12 @@ export async function fetchOperationalTaskReports(taskId: string, offset: number
));
}
export async function fetchOperationalTaskPresentationCursor(taskId: string, getAccessToken?: AccessTokenProvider, signal?: AbortSignal) {
return investigationPresentationCursorSchema.parse(await operationalTaskRequest(
`/operational-tasks/${encodeURIComponent(taskId)}/presentation-cursor`, getAccessToken, { signal }
));
}
export async function createOperationalTask(input: CreateOperationalTaskInput, getAccessToken?: AccessTokenProvider) {
return operationalTaskSchema.parse(await operationalTaskRequest(
"/operational-tasks",