26 lines
6.3 KiB
TypeScript
26 lines
6.3 KiB
TypeScript
import { tool } from "../tool.js";
|
|
import { getInternalBridgeConfig } from "../internalBridge.js";
|
|
import { taskPresentationCommandSchema } from "../../src/product/task-presentation.js";
|
|
import { taskPublicationCommandSchema } from "./task-publication-schema.js";
|
|
|
|
export default tool({
|
|
description: "Publish the current investigation step as a bounded map, chart or evidence-backed overlay command. Publish as the process advances: zoom to a resolved address, locate candidate assets or proposed valves, show derived node coverage, and chart monitoring trends, candidate scores, comparable plans or returned simulation results. Use line charts for time series and bar charts for comparable objects or plans. For SCADA monitoring charts, include chart.scada with role=current or comparison, metric=pressure or flow, unit, window_start and window_end for the full current three-hour task window. Each series must bind device_id and day_offset (0=current, 1..7=previous days). Each per-device comparison has all eight series. Omit scada.gaps: Server derives actual gaps from the referenced complete readings. evidence_refs must include all current and historical sources; unread history is not missing data. Reports declare all monitoring-table targets in scada_device_ids. Reference-only devices still require charts. Final reports and run completion reject SCADA_PRESENTATION_INCOMPLETE with missing device IDs; publish the missing charts and retry. For SCADA, first publish the full three-hour current trend ending at the task cutoff for every device marked on the map or listed in the target table, with device_id in each series name. Publish pressure and flow separately. Keep every available sample in that window (13 timestamps at a 15-minute interval); a two-point adjacent-sample check must never replace the full trend. Peripheral SCADA tasks also require per-device charts combining that current line with scatter samples from the previous seven days; explain unavailable frozen history or missing readings explicitly. Group by metric and unit; split more than 8 series by device and more than 100 slots into contiguous time chunks with distinct stable chart_ids. Keep chart.scada window_start/window_end at the full three-hour bounds in every chunk; Server unions verified slots. Keep the real sampling grid even if the cutoff is not aligned. Historical scatter uses only the final three sampling slots of the full window. If a low-frequency device has no scheduled slot, query each full current/historical window and retain one null slot at the cutoff. Explicitly account for devices with missing readings. Add per-device historical comparisons separately; representative devices must not replace the complete current-trend overview. For object switching within one metric/unit/window, keep one stable chart_id per device and time chunk, title each chart as <device_id> <shared comparison title>, and start every series name with <device_id> followed by a space. Use identical x/y axis names, x_data and current sample_times across the group, with matching current-series names after removing the device prefix. The browser groups these object datasets into one child chart, with object buttons on the right and series legends below the plot; ordinary overview and bar charts stay separate. The browser manages all charts in one parent window and preserves manual child enlargement. Only new live commands may open the parent; task loading, switching, refresh, completed results and history restoration never replay popup intent. Do not republish charts to force windows open. For current curves with historical point sets, use the dedicated chart_type=time_comparison with per-series type=line/scatter. Ordinary charts use line or bar. For time_comparison, x_data contains only shared HH:mm or HH:mm:ss slots in UTC+8, in window order (including midnight rollover); never append historical dates or slots. Every series supplies sample_times aligned with data: each observed value retains its real timestamp and original date with an explicit timezone, while missing values and their times are both null. Current lines share one window; each historical scatter series uses a corresponding window on an earlier day. Place each historical value at the matching time-of-day slot. Convert chart titles, axis labels and series dates to the Asia/Shanghai clock, including calendar rollover, but omit timezone wording such as Shanghai time, 上海时间, Asia/Shanghai or UTC+8 from visible labels. Name the time axis 时间. Keep explicit offsets in sample_times. Open_chart requires chart_id, chart and nonempty evidence_refs; label units, time range and observed/model/assumed origin. Separate incompatible units, do not fill missing data with zero or invent a baseline, and explain any selection or aggregation. Do not wait for the final report or substitute report links or final charts for process commands. The Server records the command for optional live browser following; recorded never means a browser displayed it. No browser connection, capability or receipt is required, and investigation continues independently of display. Chart data must reflect the referenced persisted evidence. Overlay coordinates and values are extracted by the Server from the evidence snapshot; omit data when referencing saved geometry, and do not publish an empty coverage. Any supplied data is validated against the evidence snapshot. Never supply invented geometry or arbitrary code. Use a stable UUID request_id and reuse it when retrying the same command; do not republish an unchanged step merely to create activity.",
|
|
args: {
|
|
request_id: tool.schema.string().uuid().describe("Stable UUID idempotency key; reuse for retries of this command"),
|
|
command: taskPublicationCommandSchema,
|
|
},
|
|
async execute(args, context) {
|
|
const command = taskPresentationCommandSchema.parse(args.command);
|
|
const { baseUrl, internalToken } = getInternalBridgeConfig();
|
|
const response = await fetch(`${baseUrl}/internal/operational-task/presentation`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json", "x-agent-internal-token": internalToken },
|
|
signal: context.signal,
|
|
body: JSON.stringify({ session_id: context.sessionID, request_id: args.request_id, command }),
|
|
});
|
|
const text = await response.text();
|
|
if (!response.ok) throw new Error(`task presentation publish failed (${response.status}): ${text}`);
|
|
return text;
|
|
},
|
|
});
|