重构 改为标准化输出

This commit is contained in:
2026-09-01 14:10:23 +08:00
parent 7533b298f1
commit bc87bd6d2d
10151 changed files with 1709487 additions and 11776 deletions
+2
View File
@@ -440,6 +440,7 @@ button:disabled {
.cad-status-label { color: var(--ui-text-subtle); font-size: 10px; font-weight: 500; text-transform: uppercase; }
.cad-tool-name { overflow: hidden; max-width: 46%; border: 1px solid var(--ui-border-muted); border-radius: 3px; background: var(--ui-control-bg); color: var(--ui-accent-text); font: 10px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; padding: 1px 4px; text-overflow: ellipsis; white-space: nowrap; }
.cad-message-copy { margin-left: 21px; color: var(--ui-text-muted); font-size: 11px; line-height: 1.45; overflow-wrap: anywhere; }
.cad-waiting-questions { display: grid; gap: 3px; margin: 3px 0 0 21px; padding-left: 16px; color: var(--ui-text-muted); font-size: 11px; line-height: 1.45; }
.cad-event-details { margin: 4px 0 0 21px; color: var(--ui-text-subtle); font-size: 10px; }
.cad-event-details summary { cursor: pointer; width: fit-content; }
.cad-event-details pre { max-height: 180px; overflow: auto; margin: 4px 0 0; border-left: 2px solid var(--ui-border-muted); padding-left: 8px; white-space: pre-wrap; overflow-wrap: anywhere; color: var(--ui-text-muted); font: 10px/1.45 ui-monospace, SFMono-Regular, Menlo, monospace; }
@@ -462,6 +463,7 @@ button:disabled {
.json-tree { max-height: 260px; overflow: auto; margin-top: 6px; border: 1px solid var(--ui-border); border-radius: 4px; background: var(--ui-control-bg); padding: 7px; color: var(--ui-text); font: 10px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace; }
.json-tree .json-view--property { color: var(--ui-accent-text); }.json-tree .json-view--string { color: var(--ui-success-text); }.json-tree .json-view--number, .json-tree .json-view--boolean { color: var(--ui-secondary-text); }
.cad-progress.is-error .cad-message-heading, .cad-progress.is-error .cad-message-heading svg, .cad-error .cad-message-heading, .cad-error .cad-message-heading svg { color: var(--ui-error-text); }
.cad-progress.is-waiting .cad-message-heading, .cad-progress.is-waiting .cad-message-heading svg { color: var(--ui-secondary-text); }
.cad-result { margin-top: 12px; }
.cad-result-title { margin-left: 21px; color: var(--ui-accent-text); font-size: 12px; font-weight: 600; line-height: 1.45; overflow-wrap: anywhere; }
.cad-result-meta { display: flex; flex-wrap: wrap; gap: 3px 10px; margin-left: 21px; color: var(--ui-text-muted); font-size: 10px; line-height: 1.4; overflow-wrap: anywhere; }
+3 -3
View File
@@ -7,7 +7,7 @@ import { DefaultChatTransport } from "ai";
import { AlertCircle, Box, Loader2, Moon, Sun } from "lucide-react";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { activeCheckpointPreview, latestSuccessfulResult } from "@/lib/cad-artifacts";
import { normalizeCadMessages } from "@/lib/cad-messages";
import { normalizeCadMessages, restoreTaskProjection } from "@/lib/cad-messages";
import type { ViewerSelectionContext } from "@/lib/viewer-selection";
import type {
BackendConfig,
@@ -89,7 +89,7 @@ export function AgentStudio() {
setConfig(nextConfig);
setConversationId(nextConversationId);
setSelectedTaskId(taskId);
setInitialMessages(normalizeCadMessages(conversation.messages));
setInitialMessages(restoreTaskProjection(normalizeCadMessages(conversation.messages), restoredTask));
setAttachments(conversation.attachments || []);
const defaultProvider = nextConfig.providers.find((provider) => provider.id === nextConfig.default_provider) ?? nextConfig.providers[0];
setProviderId(defaultProvider?.id || "");
@@ -156,7 +156,7 @@ export function AgentStudio() {
});
}
}
if (progress.step === "requirements_document" && progress.status === "running") setTaskRunning(true);
if (progress.step === "task_started" && progress.status === "running") setTaskRunning(true);
if (progress.step === "task_terminal") setTaskRunning(progress.lifecycle === "running");
}, [conversationId, syncUrl]);
+13 -4
View File
@@ -14,13 +14,14 @@ export function CadProgressPart({ data }: { data: CadProgress }) {
const status = String(data.status || "").toLowerCase();
const isRunning = status === "running";
const isError = status === "error";
const statusLabel = isRunning ? "进行中" : isError ? "失败" : status === "success" ? "完成" : data.status;
const Icon = data.step === "tool_call" ? Wrench : data.step.includes("review") || data.step === "final_review" ? Eye : data.step === "rollback" ? RotateCcw : data.step.includes("requirements") || data.step.includes("checklist") ? FileCheck : data.step.includes("diagnostic") ? Search : isError ? AlertTriangle : Check;
const isWaiting = status === "waiting";
const statusLabel = isRunning ? "进行中" : isWaiting ? data.lifecycle === "waiting_retry" ? "等待重试" : "等待确认" : isError ? "失败" : status === "success" ? "完成" : data.status;
const Icon = data.step === "tool_call" ? Wrench : data.step.includes("review") || data.step === "final_review" ? Eye : data.step === "rollback" ? RotateCcw : data.step.includes("requirements") || data.step.includes("checklist") ? FileCheck : data.step.includes("diagnostic") ? Search : isError || isWaiting ? AlertTriangle : Check;
const evidence = data.evidence || (Array.isArray(data.review?.evidence) ? data.review.evidence.map(String) : []);
const documentTitle = data.step === "requirements_document" ? "冻结需求内容" : data.step === "completion_checklist" ? "完成清单内容" : "";
const documentTitle = data.step === "requirements_review" ? "需求合同复核" : "";
const documentMarkdown = data.step === "requirements_document" ? withoutRequirementsFilename(data.markdown || "") : data.markdown || "";
return (
<div className={`cad-message cad-progress${isError ? " is-error" : ""}`} role="status" aria-live="polite">
<div className={`cad-message cad-progress${isError ? " is-error" : ""}${isWaiting ? " is-waiting" : ""}`} role="status" aria-live="polite">
<div className="cad-message-heading">
{isRunning ? <Loader2 className="spin" size={14} aria-hidden="true" /> : <Icon size={14} aria-hidden="true" />}
<span>{data.label || data.step}</span>
@@ -28,6 +29,10 @@ export function CadProgressPart({ data }: { data: CadProgress }) {
<span className="cad-status-label">{statusLabel}</span>
</div>
{data.message ? <div className="cad-message-copy">{data.message}</div> : null}
{data.questions?.length ? <ul className="cad-waiting-questions">{data.questions.map((question, index) => <li key={`${question}-${index}`}>{question}</li>)}</ul> : null}
{data.issues?.length ? <ul className="cad-waiting-questions">{data.issues.map((issue, index) => <li key={`${issue}-${index}`}>{issue}</li>)}</ul> : null}
{data.verificationWarnings?.length ? <details className="cad-event-details" open><summary> ({data.verificationWarnings.length})</summary><ul>{data.verificationWarnings.map((warning, index) => <li key={`${warning}-${index}`}>{warning}</li>)}</ul></details> : null}
{data.appliedNormalizations?.length ? <details className="cad-event-details"><summary> ({data.appliedNormalizations.length})</summary><JsonTree data={data.appliedNormalizations} /></details> : null}
{documentMarkdown ? <details className="cad-event-details cad-document-details" open={data.step === "requirements_document"}><summary>{documentTitle || "文档内容"}</summary><MarkdownDocument>{documentMarkdown}</MarkdownDocument></details> : null}
{data.arguments ? <details className="cad-event-details"><summary></summary><JsonTree data={data.arguments} /></details> : null}
{data.result !== undefined ? <details className="cad-event-details"><summary></summary><JsonTree data={data.result} /></details> : null}
@@ -63,6 +68,8 @@ export function CadResultPart({ data }: { data: CadResult }) {
<span>{data.referenceIds.length} </span>
</div>
{data.referenceIds.length ? <div className="cad-result-notes"><strong></strong><span>{data.referenceIds.join("")}</span></div> : null}
{data.verificationWarnings?.length ? <div className="cad-result-notes"><strong></strong><span>{data.verificationWarnings.join("")}</span></div> : null}
{data.appliedNormalizations?.length ? <details className="cad-event-details"><summary> ({data.appliedNormalizations.length})</summary><JsonTree data={data.appliedNormalizations} /></details> : null}
{downloads.length ? <div className="download-row">
{downloads.map(([label, path]) => (
<a key={label} className="download-link" href={encodeArtifactUrl(data.taskId, path)} download aria-label={`下载 ${label}`}>
@@ -87,6 +94,8 @@ export function CadErrorPart({ data }: { data: CadError }) {
<div className="cad-message cad-error" role="alert">
<div className="cad-message-heading"><AlertTriangle size={14} aria-hidden="true" /><span>{stage}</span></div>
<div className="cad-message-copy">{data.message}</div>
{data.tool ? <code className="cad-tool-name">{data.tool}</code> : null}
{data.fieldErrors?.length ? <details className="cad-event-details" open><summary> ({data.fieldErrors.length})</summary><ul>{data.fieldErrors.map((error, index) => <li key={`${error.path || "field"}-${index}`}><code>{error.path || "/"}</code>{error.message ? `: ${error.message}` : ""}</li>)}</ul></details> : null}
</div>
);
}
+3
View File
@@ -30,6 +30,9 @@ function resultForRevision(task: TaskRecord, revisionId: string, checkpoint: boo
engine: current.engine || "cdsl_only",
checkpoint,
lifecycle: task.lifecycle || "completed",
verificationStatus: task.verification_status,
verificationWarnings: task.verification_warnings || [],
appliedNormalizations: task.applied_normalizations || [],
};
}
+37 -1
View File
@@ -1,4 +1,4 @@
import type { CadError, CadProgress, CadResult, CadUIMessage } from "./cad-types";
import type { CadError, CadProgress, CadResult, CadUIMessage, TaskRecord } from "./cad-types";
type AnyPart = { type?: unknown; text?: unknown; data?: unknown; id?: unknown };
type AnyMessage = { id?: unknown; role?: unknown; parts?: unknown };
@@ -50,6 +50,42 @@ export function messagesForBackend(messages: CadUIMessage[]) {
}));
}
export function restoreTaskProjection(messages: CadUIMessage[], task: TaskRecord | null): CadUIMessage[] {
if (!task?.task_id || !task.lifecycle) return messages;
const terminal = task.lifecycle !== "running";
const step = terminal ? "task_terminal" : "task_started";
const alreadyVisible = messages.some((message) => message.parts.some((part) =>
part.type === "data-cad-progress"
&& part.data.taskId === task.task_id
&& part.data.step === step
&& (!terminal || part.data.lifecycle === task.lifecycle)
));
if (alreadyVisible) return messages;
const status = task.lifecycle === "failed" ? "error"
: task.lifecycle === "waiting_for_user" || task.lifecycle === "waiting_retry" ? "waiting"
: task.lifecycle === "completed" ? "success" : "running";
const progress: CadProgress = {
step,
label: terminal ? "生成任务" : "Agent",
status,
taskId: task.task_id,
lifecycle: task.lifecycle,
message: task.message || (terminal ? "CAD 任务已停止。" : "CAD 任务正在运行。"),
questions: task.questions || [],
issues: task.issues || [],
blockerType: task.blocker_type,
userActionRequired: Boolean(task.user_action_required),
verificationStatus: task.verification_status,
verificationWarnings: task.verification_warnings || [],
appliedNormalizations: task.applied_normalizations || [],
};
return [...messages, {
id: `projection_${task.task_id}_${task.state_version || 0}`,
role: "assistant",
parts: [{ type: "data-cad-progress", id: `projection_${task.task_id}_${step}`, data: progress }],
}];
}
function stringId(value: unknown) {
return typeof value === "string" ? value : undefined;
}
+77 -31
View File
@@ -3,6 +3,7 @@ import test from "node:test";
import { backendEventToUiChunk } from "./cad-stream";
import { activeCheckpointPreview, latestSuccessfulResult } from "./cad-artifacts";
import { messagesForBackend } from "./cad-messages";
import { restoreTaskProjection } from "./cad-messages";
import type { CadUIMessage } from "./cad-types";
test("maps backend cad_result SSE into an AI SDK data part", () => {
@@ -20,53 +21,107 @@ test("keeps progressive revisions as separate data parts", () => {
assert.notEqual(first?.id, second?.id);
});
test("maps final visual repair review into a blocking progress state", () => {
test("maps final repair review into a blocking progress state", () => {
const chunk = backendEventToUiChunk({
event: "final_review",
data: { taskId: "cad_abc", review: { verdict: "repair", confidence: 0.92, evidence: ["missing round"] } },
event: "final_review", data: { taskId: "cad_abc", result: { status: "repair", evidence: ["missing round"] } },
}, "text_1");
assert.equal(chunk?.type, "data-cad-progress");
assert.deepEqual("data" in chunk! ? chunk.data : null, {
step: "final_review", label: "最终视觉复核", status: "error", message: "missing round", taskId: "cad_abc",
step: "final_review", label: "最终独立复核", status: "error", message: "", taskId: "cad_abc", result: { status: "repair", evidence: ["missing round"] },
});
});
test("maps a rejected independent candidate review into a blocking progress state", () => {
const chunk = backendEventToUiChunk({
event: "candidate_review",
data: { taskId: "cad_abc", review: { verdict: "reject", evidence: ["base is disconnected"] } },
event: "candidate_review", data: { taskId: "cad_abc", result: { status: "rejected", evidence: ["base is disconnected"] } },
}, "text_1");
assert.equal(chunk?.type, "data-cad-progress");
assert.deepEqual("data" in chunk! ? chunk.data : null, {
step: "candidate_review", label: "候选独立复核", status: "error", message: "base is disconnected", taskId: "cad_abc",
step: "candidate_review", label: "候选独立复核", status: "error", message: "", taskId: "cad_abc", result: { status: "rejected", evidence: ["base is disconnected"] },
});
});
test("maps a modeling plan revision request into a blocking progress state", () => {
test("keeps terminal schema field errors visible to the CAD error part", () => {
const chunk = backendEventToUiChunk({
event: "modeling_plan_review",
data: { taskId: "cad_abc", review: { verdict: "revise", issues: [{ message: "split unrelated finish" }] } },
event: "cad_error",
data: {
stage: "generation",
tool: "patch_requirements_draft",
message: "Author repeatedly failed the schema.",
fieldErrors: [{ path: "/patches", message: "Field required" }],
},
}, "text_1");
assert.equal(chunk?.type, "data-cad-progress");
assert.equal("data" in chunk! ? (chunk.data as { label?: string }).label : null, "计划独立复核");
assert.equal("data" in chunk! ? (chunk.data as { status?: string }).status : null, "error");
assert.equal(chunk?.type, "data-cad-error");
assert.deepEqual("data" in chunk! ? chunk.data : null, {
stage: "generation",
tool: "patch_requirements_draft",
message: "Author repeatedly failed the schema.",
fieldErrors: [{ path: "/patches", message: "Field required" }],
});
});
test("keeps a server-verified plan skip visible in the timeline", () => {
test("maps a waiting terminal into a visible user-decision state", () => {
const question = "Should the spacing be corrected to 45 degrees or should the count change?";
const chunk = backendEventToUiChunk({
event: "plan_step_skipped",
event: "task_terminal",
data: {
taskId: "cad_abc",
planStepId: "step_6",
nextPlanStepId: "",
evidenceRef: "completion_ledger:rev_008",
status: "success",
message: "The current revision already satisfies this plan step.",
lifecycle: "waiting_for_user",
message: "Requirements need a user decision.",
questions: [question],
reviewPath: "documents/requirements-review.json",
},
}, "text_1");
assert.equal(chunk?.type, "data-cad-progress");
assert.equal("data" in chunk! ? (chunk.data as { label?: string }).label : null, "计划步骤已满足");
assert.equal("data" in chunk! ? (chunk.data as { status?: string }).status : null, "success");
assert.deepEqual("data" in chunk! ? chunk.data : null, {
step: "task_terminal",
label: "生成任务",
status: "waiting",
message: "Requirements need a user decision.",
taskId: "cad_abc",
lifecycle: "waiting_for_user",
questions: [question],
reviewPath: "documents/requirements-review.json",
});
});
test("restores a complete waiting terminal from the task projection", () => {
const restored = restoreTaskProjection([], {
task_id: "cad_abc", current_revision: "", lifecycle: "waiting_for_user", phase: "WAITING_FOR_USER",
state_version: 7, message: "A decision is required.", questions: ["Four holes or six?"],
blocker_type: "requirements_ambiguity", user_action_required: true,
verification_status: "verified", verification_warnings: [], applied_normalizations: [], revisions: [],
});
const part = restored[0].parts[0];
assert.equal(part.type, "data-cad-progress");
if (part.type === "data-cad-progress") {
assert.equal(part.data.lifecycle, "waiting_for_user");
assert.deepEqual(part.data.questions, ["Four holes or six?"]);
assert.equal(part.data.userActionRequired, true);
}
});
test("keeps unmet review issues visible when requirements review cannot converge", () => {
const issue = "The counterbores need a feature-scoped front-side claim.";
const chunk = backendEventToUiChunk({
event: "task_terminal",
data: {
taskId: "cad_abc",
lifecycle: "waiting_for_user",
message: "Requirements review did not converge.",
issues: [issue],
},
}, "text_1");
assert.equal(chunk?.type, "data-cad-progress");
assert.deepEqual("data" in chunk! ? chunk.data : null, {
step: "task_terminal",
label: "生成任务",
status: "waiting",
message: "Requirements review did not converge.",
taskId: "cad_abc",
lifecycle: "waiting_for_user",
issues: [issue],
});
});
test("gives repeated tool events unique ordered parts", () => {
@@ -83,15 +138,6 @@ test("reuses an invocation id so tool completion updates its running card", () =
assert.equal(running?.id, complete?.id);
});
test("keeps frozen requirement markdown visible in the timeline", () => {
const chunk = backendEventToUiChunk({
event: "requirements_document",
data: { taskId: "cad_abc", status: "frozen", markdown: "# 冻结需求\n\n- 创建底座" },
}, "text_1", 2);
assert.equal(chunk?.type, "data-cad-progress");
assert.equal("data" in chunk! ? (chunk.data as { markdown?: string }).markdown : null, "# 冻结需求\n\n- 创建底座");
});
test("restores the latest successful task revision for the viewer", () => {
const result = latestSuccessfulResult({
task_id: "cad_abc",
+20 -7
View File
@@ -20,15 +20,15 @@ export function backendEventToUiChunk(
data: { ...item.data, sequence },
};
}
if (["requirements_document", "completion_checklist", "completion_audit", "modeling_plan", "modeling_plan_review", "plan_step_skipped", "agent_thinking", "tool_call", "candidate_result", "candidate_review", "geometry_diagnostic", "geometry_conclusion", "step_review", "checkpoint", "rollback", "final_review", "task_terminal"].includes(item.event)) {
if (["requirements_review", "action_selection", "tool_call", "candidate_result", "candidate_review", "final_review", "task_terminal"].includes(item.event)) {
const review = item.data.review && typeof item.data.review === "object"
? item.data.review as Record<string, unknown>
: null;
const lifecycle = String(item.data.lifecycle || "");
const status = item.event === "task_terminal"
? (String(item.data.lifecycle || "") === "failed" ? "error" : "success")
: (item.event === "modeling_plan_review" && String(review?.verdict || "") === "revise")
|| (item.event === "candidate_review" && String(review?.verdict || "") === "reject")
|| (item.event === "final_review" && String(review?.verdict || "") === "repair" && Number(review?.confidence || 0) >= 0.85)
? (lifecycle === "failed" ? "error" : lifecycle === "waiting_for_user" || lifecycle === "waiting_retry" ? "waiting" : "success")
: String((item.data.result as Record<string, unknown> | undefined)?.status || "") === "rejected"
|| String((item.data.result as Record<string, unknown> | undefined)?.status || "") === "repair"
? "error"
: String(item.data.status || "running");
const taskId = String(item.data.taskId || "task");
@@ -44,15 +44,25 @@ export function backendEventToUiChunk(
type: "data-cad-progress",
id: `event_${eventId}`,
data: { step: item.event, label: ({
requirements_document: "冻结需求", completion_checklist: "完成清单", completion_audit: "完成审计", modeling_plan: "建模计划", modeling_plan_review: "计划独立复核", plan_step_skipped: "计划步骤已满足", agent_thinking: "建模判断", tool_call: "建模工具", candidate_result: "候选构建", candidate_review: "候选独立复核", geometry_diagnostic: "几何诊断", geometry_conclusion: "几何结论", step_review: "步骤审查", checkpoint: "构建检查点", rollback: "回滚检查点", final_review: "最终视觉复核", task_terminal: "生成任务",
requirements_review: "需求合同独立复核", action_selection: "动作选择", tool_call: "建模工具", candidate_result: "候选构建", candidate_review: "候选独立复核", final_review: "最终独立复核", task_terminal: "生成任务",
} as Record<string, string>)[item.event], status, ...metadata, message: String(
item.data.message || item.data.reason
|| (Array.isArray(item.data.questions) ? item.data.questions.map(String).filter(Boolean).join("") : "")
|| (review?.evidence instanceof Array ? review.evidence.join("") : "")
|| (review?.issues instanceof Array ? review.issues.map((issue) => typeof issue === "object" && issue ? String((issue as Record<string, unknown>).message || "") : String(issue)).filter(Boolean).join("") : ""),
),
...(item.data.taskId ? { taskId } : {}),
...(item.data.nodeId ? { nodeId: String(item.data.nodeId) } : {}),
...(item.data.lifecycle ? { lifecycle: String(item.data.lifecycle) } : {}),
...(Array.isArray(item.data.questions) ? { questions: item.data.questions.map(String).filter(Boolean) } : {}),
...(Array.isArray(item.data.issues) ? { issues: item.data.issues.map(String).filter(Boolean) } : {}),
...(Array.isArray(item.data.unresolved) ? { unresolved: item.data.unresolved } : {}),
...(item.data.blockerType ? { blockerType: String(item.data.blockerType) } : {}),
...(typeof item.data.userActionRequired === "boolean" ? { userActionRequired: item.data.userActionRequired } : {}),
...(item.data.verificationStatus ? { verificationStatus: String(item.data.verificationStatus) } : {}),
...(Array.isArray(item.data.verificationWarnings) ? { verificationWarnings: item.data.verificationWarnings.map(String).filter(Boolean) } : {}),
...(Array.isArray(item.data.appliedNormalizations) ? { appliedNormalizations: item.data.appliedNormalizations } : {}),
...(item.data.reviewPath ? { reviewPath: String(item.data.reviewPath) } : {}),
...(item.data.timestamp ? { timestamp: String(item.data.timestamp) } : {}),
...(item.data.markdown ? { markdown: String(item.data.markdown) } : {}),
...(item.data.tool ? { tool: String(item.data.tool) } : {}),
@@ -78,7 +88,10 @@ export function backendEventToUiChunk(
return {
type: "data-cad-error",
id: `error_${Date.now()}`,
data: item.data,
data: {
...item.data,
...(Array.isArray(item.data.fieldErrors) ? { fieldErrors: item.data.fieldErrors } : {}),
},
};
}
return null;
+45 -26
View File
@@ -16,8 +16,17 @@ export type CadProgress = {
arguments?: Record<string, unknown>;
result?: unknown;
evidence?: string[];
questions?: string[];
issues?: string[];
unresolved?: Array<{ draftId?: string; reasonCode?: string; question?: string }>;
blockerType?: string;
userActionRequired?: boolean;
verificationStatus?: "verified" | "completed_with_risks" | string;
verificationWarnings?: string[];
appliedNormalizations?: Array<Record<string, unknown>>;
review?: Record<string, unknown>;
lifecycle?: "running" | "completed" | "failed" | string;
reviewPath?: string;
lifecycle?: "running" | "completed" | "failed" | "waiting_retry" | "waiting_for_user" | string;
attempt?: number;
maxAttempts?: number;
path?: string;
@@ -36,11 +45,16 @@ export type CadResult = {
engine: string;
checkpoint?: boolean;
lifecycle?: "running" | "completed" | "failed" | string;
verificationStatus?: "verified" | "completed_with_risks" | string;
verificationWarnings?: string[];
appliedNormalizations?: Array<Record<string, unknown>>;
};
export type CadError = {
stage: string;
message: string;
tool?: string;
fieldErrors?: Array<{ path?: string; message?: string }>;
};
export type CadDataParts = {
@@ -95,38 +109,43 @@ export type TaskRevision = {
};
export type TaskRecord = {
schema_version?: string;
task_id: string;
current_revision: string;
active_revision?: string;
published_revision?: string;
lifecycle?: "running" | "completed" | "failed" | string;
lifecycle?: "running" | "completed" | "failed" | "cancelled" | "waiting_retry" | "waiting_for_user" | string;
phase?: string;
state_version?: number;
active_candidate_id?: string;
active_branch_id?: string;
requirements_path?: string;
requirements_draft?: Record<string, unknown> | null;
requirements_draft_path?: string;
requirements_review?: Record<string, unknown> | null;
requirements_review_path?: string;
requirements_contract?: Record<string, unknown> | null;
requirements_contract_path?: string;
requirements_markdown?: string | null;
completion_checklist_path?: string;
completion_checklist_markdown?: string | null;
modeling_plan_path?: string;
modeling_plan_review_path?: string;
modeling_plan_version?: number;
modeling_plan_markdown?: string | null;
modeling_plan_review?: Record<string, unknown> | null;
agent_state?: {
no_progress?: number;
cycle_tool_calls?: number;
last_diagnostic?: string;
last_review?: { candidate_id?: string; path?: string; decision?: string; recorded_at?: string };
last_candidate_review?: { verdict?: "accept" | "reject" | string; batch_goal?: string; batch_goal_status?: string; evidence?: string[]; recorded_at?: string };
modeling_plan_status?: "missing" | "pending_review" | "revise" | "approved" | "stale" | string;
active_plan_step_id?: string;
plan_step_status?: Record<string, "pending" | "complete" | string>;
completion_ledger?: {
verified_revision?: string;
items?: Array<{ item?: string; status?: "complete" | "missing" | "uncertain" | string; evidence?: string }>;
};
recent_events?: Array<{ kind?: string; at?: string; message?: string; tool?: string }>;
} | null;
completion_markdown?: string | null;
claim_summary?: Array<{
requirement_id: string;
claim_id: string;
claim_kind: string;
deterministic: boolean;
status: "pass" | "pending" | "fail" | "unavailable" | string;
evidence?: Record<string, unknown>;
}>;
pending_action?: { action_id: string; working_head: string; intent: string; requirement_ids: string[]; atomic_id: string; expected_change: string; contract_hash: string } | null;
action_ledger_summary?: Array<Record<string, unknown>>;
usage?: { calls: number; prompt_tokens: number; completion_tokens: number; context_chars: number };
preview_revision?: string;
message?: string;
questions?: string[];
issues?: string[];
blocker_type?: string;
user_action_required?: boolean;
verification_status?: "verified" | "completed_with_risks" | string;
verification_warnings?: string[];
applied_normalizations?: Array<Record<string, unknown>>;
revisions: TaskRevision[];
};