fix: harden report tools and guide investigation presentation
This commit is contained in:
@@ -4,7 +4,7 @@ import { taskPresentationCommandSchema } from "../../src/product/task-presentati
|
||||
import { taskPublicationCommandSchema } from "./task-publication-schema.js";
|
||||
|
||||
export default tool({
|
||||
description: "Record a bounded map, chart or evidence-backed overlay presentation command for the current task execution. 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; data may be omitted; any supplied data is validated by the Server 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.",
|
||||
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. 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,
|
||||
|
||||
@@ -1,35 +1,14 @@
|
||||
import { tool } from "../tool.js";
|
||||
import { getInternalBridgeConfig } from "../internalBridge.js";
|
||||
import { taskPublicationDiagramSchema, taskReportUiBlocksSchema } from "./task-publication-schema.js";
|
||||
import { taskPublicationReportSchema } from "./task-publication-schema.js";
|
||||
import { taskReportSchema } from "../../src/product/investigation-report.js";
|
||||
import catalog from "../../src/chat/answer-ui-catalog.json" with { type: "json" };
|
||||
|
||||
export default tool({
|
||||
description: "Publish an evidence-backed investigation report. No todo or execution plan is required. An optional Mermaid diagram explains the investigation to people; it never schedules or completes work. Every node_refs node_id must exactly reuse an id declared in the Mermaid source; never create separate reference ids for the same nodes. Before proposing actions publish an interim concluded report; continue executable necessary analysis before finalizing with actual action references. Follow the investigation instructions for stopping: concluded classifies a supported finding, not completion of all analysis. Field-verification proposals and queued calculations do not justify early finalization. If a user can supply the remaining critical input, use ask_task_user before finalizing; the host renders saved questions in the right investigation report even before any report is published. A saved pending question needs no final report. Use final insufficient_data only when queries and applicable user clarification cannot fill critical gaps and there is no supported current judgment or concrete recommendation; include blocking gaps, no recommendations or pending items. Query failures are execution errors, not proof that data is absent. The run lifecycle endpoint settles execution independently of the diagram. Optional ui_blocks express existing results with Result, object comparisons with Table, real series with Chart, location links with MapLocation. Use ask_task_user for fixed task questions; do not generate FollowUpForm to collect investigation answers, because it routes input to left-side discussion rather than the task answer endpoint. Result status must reflect actual evidence; saving a synthetic record does not prove field action completion. Result never executes actions. Stack intent/density and Section role express presentation semantics; original required props remain required. Do not supply CSS or executable props. Report blocks are final results: set phase to final or omit it for legacy compatibility; process blocks cannot be published in reports. Each block references saved report evidence, has a unique block_id and a text fallback. Maximum 8 blocks / 400 KB total. Do not duplicate the conclusion or invent data to fill a layout. Component property schemas: " + JSON.stringify(catalog.props),
|
||||
description: "Publish an evidence-backed investigation report. The host supplies fixed UI protocol versions and the default null stop_reason. For outcome=concluded omit stop_reason or use null, and keep every data_gaps.blocking false; use pending_items for external follow-up. For outcome=insufficient_data use stage=final, a nonempty stop_reason and an evidence-backed blocking gap, with no recommendations or pending_items. No todo or execution plan is required. An optional Mermaid diagram explains the investigation to people; it never schedules or completes work. Every node_refs node_id must exactly reuse an id declared in the Mermaid source; never create separate reference ids for the same nodes. Before proposing actions publish an interim concluded report; continue executable necessary analysis before finalizing with actual action references. Follow the investigation instructions for stopping: concluded classifies a supported finding, not completion of all analysis. Field-verification proposals and queued calculations do not justify early finalization. If a user can supply the remaining critical input, use ask_task_user before finalizing; the host renders saved questions in the right investigation report even before any report is published. A saved pending question needs no final report. Use final insufficient_data only when queries and applicable user clarification cannot fill critical gaps and there is no supported current judgment or concrete recommendation; include blocking gaps, no recommendations or pending items. Query failures are execution errors, not proof that data is absent. The run lifecycle endpoint settles execution independently of the diagram. Optional ui_blocks express existing results with Result, object comparisons with Table, real series with Chart, location links with MapLocation. Use ask_task_user for fixed task questions; do not generate FollowUpForm to collect investigation answers, because it routes input to left-side discussion rather than the task answer endpoint. Result status must reflect actual evidence; saving a synthetic record does not prove field action completion. Result never executes actions. Stack intent/density and Section role express presentation semantics; original required props remain required. Do not supply CSS or executable props. Report blocks are final results: set phase to final or omit it for legacy compatibility; process blocks cannot be published in reports. Each block references saved report evidence, has a unique block_id and a text fallback. Maximum 8 blocks / 400 KB total. Do not duplicate the conclusion or invent data to fill a layout. Component property schemas: " + JSON.stringify(catalog.props),
|
||||
args: {
|
||||
request_id: tool.schema.string().describe("Stable UUID idempotency key for this report revision"),
|
||||
report: tool.schema.object({
|
||||
title: tool.schema.string(),
|
||||
current_conclusion: tool.schema.string(),
|
||||
conclusion_boundary: tool.schema.string(),
|
||||
evidence_refs: tool.schema.array(tool.schema.string()),
|
||||
recommendations: tool.schema.array(tool.schema.string()),
|
||||
pending_items: tool.schema.array(tool.schema.string()),
|
||||
stage: tool.schema.enum(["interim", "final"]),
|
||||
outcome: tool.schema.enum(["concluded", "insufficient_data"]),
|
||||
stop_reason: tool.schema.string().nullable(),
|
||||
data_gaps: tool.schema.array(tool.schema.object({
|
||||
gap_id: tool.schema.string(),
|
||||
node_id: tool.schema.string().nullable().optional(),
|
||||
missing_data: tool.schema.string(),
|
||||
scope: tool.schema.string(),
|
||||
impact: tool.schema.string(),
|
||||
required_data: tool.schema.string(),
|
||||
evidence_refs: tool.schema.array(tool.schema.string()),
|
||||
blocking: tool.schema.boolean(),
|
||||
})),
|
||||
diagram: taskPublicationDiagramSchema.nullish(),
|
||||
ui_blocks: taskReportUiBlocksSchema,
|
||||
}),
|
||||
request_id: tool.schema.string().uuid().describe("Stable UUID idempotency key for this report revision"),
|
||||
report: taskPublicationReportSchema,
|
||||
evidence: tool.schema.array(tool.schema.object({
|
||||
evidence_id: tool.schema.string(),
|
||||
kind: tool.schema.literal("synthetic"),
|
||||
@@ -37,14 +16,16 @@ export default tool({
|
||||
source_label: tool.schema.string(),
|
||||
source_ref: tool.schema.string().optional(),
|
||||
observed_at: tool.schema.string(),
|
||||
})),
|
||||
})).default([]),
|
||||
},
|
||||
async execute(args, context) {
|
||||
const report = taskReportSchema.parse(args.report);
|
||||
const { baseUrl, internalToken } = getInternalBridgeConfig();
|
||||
const response = await fetch(`${baseUrl}/internal/operational-task/artifacts`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json", "x-agent-internal-token": internalToken },
|
||||
body: JSON.stringify({ session_id: context.sessionID, ...args }),
|
||||
signal: context.signal,
|
||||
body: JSON.stringify({ session_id: context.sessionID, ...args, report }),
|
||||
});
|
||||
const text = await response.text();
|
||||
if (!response.ok) throw new Error(`task report publish failed (${response.status}): ${text}`);
|
||||
|
||||
@@ -8,8 +8,9 @@ const identifier = text.max(128);
|
||||
const layer = tool.schema.enum(["junctions", "pipes", "valves", "pumps", "tanks", "reservoirs", "scada"]);
|
||||
|
||||
export const taskReportUiBlocksSchema = tool.schema.array(tool.schema.object({
|
||||
version: tool.schema.literal("answer-ui@1").optional().describe("Legacy wrapper tag; normally omit. The host supplies the canonical block version."),
|
||||
block: tool.schema.object({
|
||||
version: tool.schema.literal("answer-ui@1"),
|
||||
version: tool.schema.literal("answer-ui@1").optional().describe("Fixed protocol version supplied by the host when omitted."),
|
||||
block_id: tool.schema.string().regex(/^[a-zA-Z0-9_-]{1,80}$/),
|
||||
phase: tool.schema.literal("final").optional(),
|
||||
fallback_text: text.max(4000),
|
||||
@@ -25,9 +26,19 @@ export const taskReportUiBlocksSchema = tool.schema.array(tool.schema.object({
|
||||
evidence_refs: tool.schema.array(identifier).min(1).max(100),
|
||||
}).strict()).max(8).optional();
|
||||
|
||||
const gapFields = {
|
||||
gap_id: text,
|
||||
node_id: text.nullish(),
|
||||
missing_data: text,
|
||||
scope: text,
|
||||
impact: text,
|
||||
required_data: text,
|
||||
evidence_refs: tool.schema.array(text).min(1),
|
||||
};
|
||||
|
||||
export const taskPublicationDiagramSchema = tool.schema.object({
|
||||
title: text,
|
||||
mermaid: text.max(20000).describe("Mermaid flowchart source; no Markdown fences, initialization directives or click callbacks. Reuse the exact Mermaid node ids in node_refs"),
|
||||
mermaid: text.max(20000).describe("Mermaid flowchart structure; no Markdown fences, initialization directives or click callbacks. Reuse exact Mermaid node ids in node_refs, including class-attached declarations such as A:::stage. Supply execution states through node_refs.status; the host supplies execution colors, so no style/classDef is needed for state coloring."),
|
||||
node_refs: tool.schema.array(tool.schema.object({
|
||||
node_id: tool.schema.string().regex(/^[A-Za-z_][A-Za-z0-9_-]{0,79}$/).describe("Exact id of a node declared in the Mermaid source; do not create a separate reference id"),
|
||||
label: text,
|
||||
@@ -41,6 +52,37 @@ export const taskPublicationDiagramSchema = tool.schema.object({
|
||||
"Every node_refs node_id must be declared with the same id in the Mermaid source",
|
||||
);
|
||||
|
||||
const reportFields = {
|
||||
title: text,
|
||||
current_conclusion: text,
|
||||
conclusion_boundary: text,
|
||||
evidence_refs: tool.schema.array(text).default([]),
|
||||
diagram: taskPublicationDiagramSchema.nullish(),
|
||||
ui_blocks: taskReportUiBlocksSchema,
|
||||
};
|
||||
|
||||
export const taskPublicationReportSchema = tool.schema.discriminatedUnion("outcome", [
|
||||
tool.schema.object({
|
||||
...reportFields,
|
||||
outcome: tool.schema.literal("concluded"),
|
||||
stage: tool.schema.enum(["interim", "final"]),
|
||||
stop_reason: tool.schema.null().optional().describe("Omit for concluded reports; the host supplies null. Put external follow-up in pending_items."),
|
||||
recommendations: tool.schema.array(text).default([]),
|
||||
pending_items: tool.schema.array(text).default([]),
|
||||
data_gaps: tool.schema.array(tool.schema.object({ ...gapFields, blocking: tool.schema.literal(false) }).strict()),
|
||||
}).strict(),
|
||||
tool.schema.object({
|
||||
...reportFields,
|
||||
outcome: tool.schema.literal("insufficient_data"),
|
||||
stage: tool.schema.literal("final"),
|
||||
stop_reason: text.describe("Why verified missing data prevents a necessary judgment or concrete recommendation."),
|
||||
recommendations: tool.schema.array(text).max(0).default([]),
|
||||
pending_items: tool.schema.array(text).max(0).default([]),
|
||||
data_gaps: tool.schema.array(tool.schema.object({ ...gapFields, blocking: tool.schema.boolean() }).strict()).min(1)
|
||||
.refine((gaps) => gaps.some((gap) => gap.blocking), "insufficient_data requires at least one evidence-backed blocking gap"),
|
||||
}).strict(),
|
||||
]);
|
||||
|
||||
export const taskPublicationProgressSchema = tool.schema.object({
|
||||
summary: text.max(20000),
|
||||
diagram: taskPublicationDiagramSchema.nullable().default(null),
|
||||
|
||||
Reference in New Issue
Block a user