Files
next-tjwater-agent/.opencode/tools/task-publication-schema.ts
T
jiang 874bfba907
Generic Container CI/CD / test-build-publish (push) Successful in 56s
Agent CI/CD / build-test-publish-and-deploy (push) Successful in 56s
fix: harden report tools and guide investigation presentation
2026-11-02 15:23:34 +08:00

93 lines
5.1 KiB
TypeScript

import { tool } from "../tool.js";
import { mermaidDeclaresNodeId } from "../../src/product/investigation-report.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),
};
export const taskPublicationDiagramSchema = tool.schema.object({
title: text,
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,
evidence_refs: tool.schema.array(text).default([]),
action_refs: tool.schema.array(tool.schema.string().uuid()).default([]),
status: tool.schema.enum(["pending", "running", "completed", "waiting", "waiting_human", "failed", "skipped", "cancelled"]).nullish().describe("Evidence-confirmed node state; waiting means prerequisites/external/system result, waiting_human requires manual authorization/approval/feedback. Unknown stays omitted/null; host controls colors."),
map_targets: tool.schema.array(tool.schema.object({ source_id: layer, feature_id: identifier }).strict()).max(100).default([]),
}).strict()).max(100).default([]),
}).strict().refine(
(diagram) => diagram.node_refs.every((node) => mermaidDeclaresNodeId(diagram.mermaid, node.node_id)),
"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),
}).strict();
import { createViewSchemas } from "../../src/frontendAction/command.js";
export const taskPublicationCommandSchema = createViewSchemas(tool.schema as unknown as Parameters<typeof createViewSchemas>[0], true).command as unknown as ReturnType<typeof tool.schema.object>;