import { tool } from "../tool.js"; import { investigationNodeAnnotations } from "../../src/product/investigation-node-state.js"; // OpenCode owns a separate Zod version. Native parameters are described here; // product schemas revalidate progress and presentation at execution boundaries. const text = tool.schema.string().trim().min(1); 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").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), spec: tool.schema.object({ root: tool.schema.string(), elements: tool.schema.record(tool.schema.string(), tool.schema.object({ type: tool.schema.string(), props: tool.schema.record(tool.schema.string(), tool.schema.unknown()), children: tool.schema.array(tool.schema.string()).max(48), }).strict()), }).strict(), }).strict(), 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), }; const reportFields = { title: text, current_conclusion: text, conclusion_boundary: text, evidence_refs: tool.schema.array(text).default([]), 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 taskPublicationGraphProgressSchema = tool.schema.object({ summary: text.max(20000), graph_update: tool.schema.object({ title: text.max(200).nullish(), nodes: tool.schema.array(tool.schema.object({ node_id: tool.schema.string().regex(/^[A-Za-z_][A-Za-z0-9_-]{0,79}$/).describe("Stable task-wide id. Keep this id when updating a step; use a new id for a retry of a failed attempt."), label: text, detail: text.max(2000).nullish().describe("Actual result or failure reason; preserve the failed attempt."), status: tool.schema.enum(investigationNodeAnnotations).nullish().describe("Optional saved reference annotation. recorded means a finding exists, not business completion; reference is neutral. No running state or live task synchronization."), evidence_refs: tool.schema.array(text).default([]), action_refs: tool.schema.array(tool.schema.string().uuid()).default([]), map_targets: tool.schema.array(tool.schema.object({ source_id: layer, feature_id: identifier }).strict()).max(100).default([]), }).strict()).max(100).default([]), edges: tool.schema.array(tool.schema.object({ source: tool.schema.string().regex(/^[A-Za-z_][A-Za-z0-9_-]{0,79}$/), target: tool.schema.string().regex(/^[A-Za-z_][A-Za-z0-9_-]{0,79}$/), label: text.max(200).nullish(), }).strict()).max(200).default([]), }).strict().optional().describe("Incremental nodes and explicit relationships. The Server retains prior paths and generates Mermaid; omit for a text-only update."), }).strict(); import { createViewSchemas } from "../../src/frontendAction/command.js"; export const taskPublicationCommandSchema = createViewSchemas(tool.schema as unknown as Parameters[0], true).command as unknown as ReturnType;