@tea-agent/loop-agent 0.36.4-beta.0 → 0.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +50 -68
- package/dist/build-stamp.json +3 -3
- package/dist/infrastructure/harness/atomic-write.js +1 -2
- package/dist/task/contract/project.js +4 -0
- package/dist/task/frontend-project-capability.js +12 -162
- package/dist/task/source-prepare/completeness.js +36 -0
- package/dist/task/source-prepare/parse-intent.js +67 -11
- package/dist/task/source-prepare/prepare.js +102 -5
- package/dist/worker/console/chat/chat-event-store.js +91 -0
- package/dist/worker/console/chat/pi-runtime.js +25 -0
- package/dist/worker/console/chat/routes.js +7 -2
- package/dist/worker/console/chat/semantic-activity.js +7 -1
- package/dist/worker/console/chat/session-store.js +6 -1
- package/dist/worker/console/chat/turn-process.js +119 -12
- package/dist/worker/console/operator-actions.js +18 -1
- package/dist/worker/console/static/assets/abnfDiagram-N423BO3Z-DSvF6RXQ.js +1 -0
- package/dist/worker/console/static/assets/arc-DuI4ogtJ.js +1 -0
- package/dist/worker/console/static/assets/architectureDiagram-T3A2C74G-DdBbgD4y.js +36 -0
- package/dist/worker/console/static/assets/blockDiagram-VBNYF7ZC-xQC2HELm.js +132 -0
- package/dist/worker/console/static/assets/c4Diagram-5PPSVZJV-DPMSgwIZ.js +10 -0
- package/dist/worker/console/static/assets/channel-DLVz9TYW.js +1 -0
- package/dist/worker/console/static/assets/chunk-2GRJ4B5K-WTEKTxin.js +1 -0
- package/dist/worker/console/static/assets/chunk-2Q5K7J3B-B6vZ0Pv_.js +1 -0
- package/dist/worker/console/static/assets/chunk-5RXB4S5H-BrerNAix.js +231 -0
- package/dist/worker/console/static/assets/chunk-5VM5RSS4-D_ch2aaI.js +15 -0
- package/dist/worker/console/static/assets/chunk-6Q2QTUOP-VvfacNrq.js +88 -0
- package/dist/worker/console/static/assets/chunk-GF5L2VYU-B-uGFIg2.js +206 -0
- package/dist/worker/console/static/assets/chunk-JWPE2WC7-BlysCtod.js +1 -0
- package/dist/worker/console/static/assets/chunk-KBJHAD2P-BjAUAfcT.js +1 -0
- package/dist/worker/console/static/assets/chunk-RYQCIY6F-BeZnBmb-.js +1 -0
- package/dist/worker/console/static/assets/chunk-XXDRQBXY-Bbfe3UJG.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BeAuGcO9.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BeAuGcO9.js +1 -0
- package/dist/worker/console/static/assets/cose-bilkent-JH36ORCC-BKJQnYVM.js +1 -0
- package/dist/worker/console/static/assets/cynefin-VYW2F7L2-B1wtI3hU.js +166 -0
- package/dist/worker/console/static/assets/cynefinDiagram-MW4NZA55-DCF6DBSu.js +62 -0
- package/dist/worker/console/static/assets/cytoscape.esm-yzknjiTM.js +321 -0
- package/dist/worker/console/static/assets/dagre-VZM6K2ZE-BJyNGRlP.js +4 -0
- package/dist/worker/console/static/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/dist/worker/console/static/assets/diagram-7IWD3JNH-B7j1-VsZ.js +30 -0
- package/dist/worker/console/static/assets/diagram-B4RE2ZJO-BREJ140a.js +3 -0
- package/dist/worker/console/static/assets/diagram-LBJQPF4R-CDPHmHGH.js +24 -0
- package/dist/worker/console/static/assets/diagram-Q27KOJAE-BtZLrez1.js +24 -0
- package/dist/worker/console/static/assets/diagram-UB23O5K3-mzybu8oU.js +41 -0
- package/dist/worker/console/static/assets/ebnfDiagram-BXEA7PRR-Dk3I2uO6.js +1 -0
- package/dist/worker/console/static/assets/erDiagram-JOGREHBK-BT97u_tQ.js +85 -0
- package/dist/worker/console/static/assets/flowDiagram-UKHOOZJN-CMOtYzui.js +156 -0
- package/dist/worker/console/static/assets/ganttDiagram-PKOTCBZU-DiNydkuY.js +292 -0
- package/dist/worker/console/static/assets/gitGraphDiagram-DS77QQ5N-Cwt6fLkl.js +106 -0
- package/dist/worker/console/static/assets/graph-DOmOIIwC.js +1 -0
- package/dist/worker/console/static/assets/index-DQxiso1w.js +325 -0
- package/dist/worker/console/static/assets/index-_j0Hzo69.css +1 -0
- package/dist/worker/console/static/assets/infoDiagram-6WML65LV-AsGIGKKB.js +2 -0
- package/dist/worker/console/static/assets/init-Gi6I4Gst.js +1 -0
- package/dist/worker/console/static/assets/ishikawaDiagram-WSZJBQD7-BL8ITZh3.js +70 -0
- package/dist/worker/console/static/assets/journeyDiagram-NVQOT4AX-VPKAUX8M.js +139 -0
- package/dist/worker/console/static/assets/kanban-definition-27J2QSJJ-BL56hfGp.js +89 -0
- package/dist/worker/console/static/assets/layout-D-LzfAck.js +1 -0
- package/dist/worker/console/static/assets/linear-BmSG3nIZ.js +1 -0
- package/dist/worker/console/static/assets/map-DxJ2ADlA.js +1 -0
- package/dist/worker/console/static/assets/mermaid.core-DWGK9kwP.js +308 -0
- package/dist/worker/console/static/assets/mindmap-definition-FAOFIHXS--vvKLjo0.js +96 -0
- package/dist/worker/console/static/assets/ordinal-Cboi1Yqb.js +1 -0
- package/dist/worker/console/static/assets/pegDiagram-VL7TDLO6-B41mxb2-.js +1 -0
- package/dist/worker/console/static/assets/pieDiagram-7S7Q4E2Y-DTlu77sw.js +39 -0
- package/dist/worker/console/static/assets/quadrantDiagram-CIZ2JOQS-Bk5A7alv.js +7 -0
- package/dist/worker/console/static/assets/railroadDiagram-AXF67PYL-Csf9jzQy.js +1 -0
- package/dist/worker/console/static/assets/requirementDiagram-LRYGKXZP-CeptoSNx.js +84 -0
- package/dist/worker/console/static/assets/sankeyDiagram-W5VNT64P-A-DLCLer.js +40 -0
- package/dist/worker/console/static/assets/sequenceDiagram-SI44F4Z6-22VbPdOI.js +162 -0
- package/dist/worker/console/static/assets/sizeCapture-X5ZJPWSS-DWvAyBC9.js +1 -0
- package/dist/worker/console/static/assets/stateDiagram-OKZ733FA-qICZ75yT.js +1 -0
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DzKvfHTX.js +1 -0
- package/dist/worker/console/static/assets/swimlanes-SLNWSIFB-xi7lcMyp.js +2 -0
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Cxqr_qM1.js +8 -0
- package/dist/worker/console/static/assets/timeline-definition-Z64GVDOM-Bu6Nl6O2.js +120 -0
- package/dist/worker/console/static/assets/vennDiagram-T6HMQDX7-BzMz4Bn9.js +34 -0
- package/dist/worker/console/static/assets/wardleyDiagram-T6FBY63Y-CxaJnzc5.js +78 -0
- package/dist/worker/console/static/assets/xychartDiagram-ELKLHX3M-5JK-omQK.js +7 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_AMS-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Caligraphic-Bold.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Caligraphic-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Fraktur-Bold.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Fraktur-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Main-Bold.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Main-BoldItalic.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Main-Italic.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Main-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Math-BoldItalic.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Math-Italic.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_SansSerif-Bold.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_SansSerif-Italic.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_SansSerif-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Script-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Size1-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Size2-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Size3-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Size4-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Typewriter-Regular.woff2 +0 -0
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +25 -0
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +37 -2
- package/dist/worker/observe/static/operator-chrome.js +4 -2
- package/dist/workflows/dag/failure-routing.js +4 -9
- package/dist/workflows/dag/frontend-prewrite-gate.js +8 -37
- package/dist/workflows/dag/init-hybrid.js +4 -73
- package/dist/workflows/dag/lifecycle.js +0 -4
- package/dist/workflows/dag/node-execution.js +6 -16
- package/dist/workflows/dag/report.js +0 -6
- package/dist/workflows/dag/retry-policy.js +0 -11
- package/dist/workflows/dag/runner.js +19 -3
- package/dist/workflows/dag/types.js +0 -2
- package/dist/workflows/dag/validate.js +2 -7
- package/docs/templates/frontend-design-contract.md +14 -34
- package/docs/templates/frontend-task-constraints.md +13 -33
- package/docs/templates/frontend-task-requirement.md +20 -54
- package/package.json +6 -1
- package/skills/loop-agent/references/command-reference.md +1 -0
- package/dist/worker/console/static/assets/index-D83DYAFG.css +0 -1
- package/dist/worker/console/static/assets/index-IXm7oYjL.js +0 -59
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { lstat, readFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
3
4
|
import { ContractMutationError, applyTaskContract, logicalRevisionForState, observeTaskContractState, projectConstraintsMarkdown, projectRequirementMarkdown, sha256OfCanonicalJson, validateDraftForTask, } from "../contract/index.js";
|
|
4
5
|
import { TASK_CONTRACT_DRAFT_SCHEMA_VERSION } from "../contract/constants.js";
|
|
5
6
|
import { getTaskContractPaths } from "../contract/paths.js";
|
|
@@ -28,26 +29,114 @@ async function loadExistingTaskConfig(repoRoot, taskId) {
|
|
|
28
29
|
return null;
|
|
29
30
|
}
|
|
30
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Check whether a "## References" section in a managed 需求.md matches the
|
|
34
|
+
* projector's canonical form: every entry is `- [role] <manifest-path>` and
|
|
35
|
+
* every path exists in source-manifest.json. The projector itself writes
|
|
36
|
+
* this section, so matching entries are the writer's own round-trip output
|
|
37
|
+
* and must be accepted (2026-08-16 L1). Unknown/free-form entries stay
|
|
38
|
+
* blocking — they indicate hand-edited provenance.
|
|
39
|
+
*/
|
|
40
|
+
function isProjectedReferencesSection(sectionBody, manifestPaths) {
|
|
41
|
+
const entryRe = /^- \[(?<role>[^\]]+)\] (?<ref>\S.*)$/;
|
|
42
|
+
const lines = sectionBody
|
|
43
|
+
.split(/\r?\n/)
|
|
44
|
+
.map((line) => line.trim())
|
|
45
|
+
.filter(Boolean);
|
|
46
|
+
if (lines.length === 0)
|
|
47
|
+
return true; // empty section: nothing to reject
|
|
48
|
+
return lines.every((line) => {
|
|
49
|
+
const match = entryRe.exec(line);
|
|
50
|
+
if (!match?.groups)
|
|
51
|
+
return false;
|
|
52
|
+
const ref = match.groups.ref?.replace(/\\/g, "/").trim() ?? "";
|
|
53
|
+
return manifestPaths.has(ref);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Source Resolutions canonical projector shape:
|
|
58
|
+
* `- <sourceRef>: <conflict> → <decision> (<reason>; decidedBy=<who>)`.
|
|
59
|
+
*/
|
|
60
|
+
function isProjectedSourceResolutionsSection(sectionBody) {
|
|
61
|
+
const entryRe = /^- \S[^:]*: .+ → .+ \(.+; decidedBy=.+\)$/u;
|
|
62
|
+
const lines = sectionBody
|
|
63
|
+
.split(/\r?\n/)
|
|
64
|
+
.map((line) => line.trim())
|
|
65
|
+
.filter(Boolean);
|
|
66
|
+
if (lines.length === 0)
|
|
67
|
+
return true;
|
|
68
|
+
return lines.every((line) => entryRe.test(line));
|
|
69
|
+
}
|
|
70
|
+
/** Section titles the read-back validator may inspect (static whitelist). */
|
|
71
|
+
const MANAGED_PROJECTION_SECTION_TITLES = [
|
|
72
|
+
"References",
|
|
73
|
+
"Source Resolutions",
|
|
74
|
+
];
|
|
75
|
+
/** Extract the body of the first `## <Title>` section, if present. */
|
|
76
|
+
function extractSectionBody(markdown, title) {
|
|
77
|
+
const re = new RegExp(`^##\\s+${title}\\s*$`, "im");
|
|
78
|
+
const match = re.exec(markdown);
|
|
79
|
+
if (!match)
|
|
80
|
+
return null;
|
|
81
|
+
const rest = markdown.slice(match.index + match[0].length);
|
|
82
|
+
const nextHeading = /^##\s+/m.exec(rest);
|
|
83
|
+
return nextHeading ? rest.slice(0, nextHeading.index) : rest;
|
|
84
|
+
}
|
|
85
|
+
/** Materialized paths registered in the task's source-manifest.json. */
|
|
86
|
+
async function loadSourceManifestPaths(requirementPath) {
|
|
87
|
+
const manifestPath = path.join(path.dirname(requirementPath), "source-manifest.json");
|
|
88
|
+
try {
|
|
89
|
+
const raw = JSON.parse(await readFile(manifestPath, "utf-8"));
|
|
90
|
+
return new Set((raw.documents ?? [])
|
|
91
|
+
.map((doc) => doc.materializedPath?.replace(/\\/g, "/"))
|
|
92
|
+
.filter((p) => Boolean(p)));
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return new Set();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
31
98
|
async function loadManagedProjectionDraft(input) {
|
|
32
99
|
const config = input.existingTaskConfig;
|
|
33
100
|
if (!config) {
|
|
34
101
|
return { ok: false, message: "managed task config could not be loaded" };
|
|
35
102
|
}
|
|
36
103
|
try {
|
|
37
|
-
const [
|
|
104
|
+
const [requirementRaw, constraints] = await Promise.all([
|
|
38
105
|
readFile(input.requirementPath, "utf-8"),
|
|
39
106
|
readFile(input.constraintsPath, "utf-8"),
|
|
40
107
|
]);
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
108
|
+
// L1 (2026-08-16): the requirement projector itself writes `## References`
|
|
109
|
+
// (from source-manifest) and `## Source Resolutions` sections into
|
|
110
|
+
// 需求.md. The read-back validator used to reject ANY such section —
|
|
111
|
+
// writer/reader contract drift that deadlocked every PRD import whose
|
|
112
|
+
// draft carried references. Now accept the projector's own canonical
|
|
113
|
+
// forms: References entries must all be `- [role] <path>` with paths
|
|
114
|
+
// present in source-manifest.json; Source Resolutions entries must
|
|
115
|
+
// match the projector's `- ref: conflict → decision (...)` shape.
|
|
116
|
+
// Free-form/hand-edited entries remain blocking.
|
|
117
|
+
const manifest = await loadSourceManifestPaths(input.requirementPath);
|
|
118
|
+
const referencesBody = extractSectionBody(requirementRaw, "References");
|
|
119
|
+
if (referencesBody !== null &&
|
|
120
|
+
!isProjectedReferencesSection(referencesBody, manifest)) {
|
|
121
|
+
return {
|
|
122
|
+
ok: false,
|
|
123
|
+
message: "managed requirement projection contains hand-edited or unknown References entries (not backed by source-manifest); use loop-agent task advance " +
|
|
124
|
+
input.taskId +
|
|
125
|
+
" --from-draft <reviewed-draft.json> --json",
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
const sourceResolutionsBody = extractSectionBody(requirementRaw, "Source Resolutions");
|
|
129
|
+
if (sourceResolutionsBody !== null &&
|
|
130
|
+
!isProjectedSourceResolutionsSection(sourceResolutionsBody)) {
|
|
44
131
|
return {
|
|
45
132
|
ok: false,
|
|
46
|
-
message: "managed requirement projection contains
|
|
133
|
+
message: "managed requirement projection contains hand-edited or malformed Source Resolutions entries; use loop-agent task advance " +
|
|
47
134
|
input.taskId +
|
|
48
135
|
" --from-draft <reviewed-draft.json> --json",
|
|
49
136
|
};
|
|
50
137
|
}
|
|
138
|
+
const requirementFacts = extractRequirementFactsFromMarkdown(requirementRaw);
|
|
139
|
+
const constraintFacts = extractRequirementFactsFromMarkdown(constraints);
|
|
51
140
|
if (!requirementFacts.objective?.trim()) {
|
|
52
141
|
return {
|
|
53
142
|
ok: false,
|
|
@@ -517,6 +606,14 @@ export async function prepareTaskSource(input) {
|
|
|
517
606
|
code: "MANAGED_PROJECTION_INCOMPLETE",
|
|
518
607
|
level: "blocking",
|
|
519
608
|
message: managedProjectionError,
|
|
609
|
+
selfHealing: {
|
|
610
|
+
action: "adopt-draft",
|
|
611
|
+
steps: [
|
|
612
|
+
"task status --json 读取 contract.observedCanonicalHash / observedSourceHashes,确认现有源文件状态",
|
|
613
|
+
"从 Console drafts(<app-data>/repos/<repo>/drafts/<taskId>.json)或 semantic intake 产物(artifacts/intake/semantic-draft.json 的 draft 字段)取回结构化 draft",
|
|
614
|
+
"补充缺失字段(scope/invariants 等)后:task advance --from-draft <reviewed-draft.json> --json",
|
|
615
|
+
],
|
|
616
|
+
},
|
|
520
617
|
});
|
|
521
618
|
}
|
|
522
619
|
// If --use-imported-prd and no parseable requirement and no AC from flags/text → blocking
|
|
@@ -152,6 +152,75 @@ export function projectOperationForChat(operation) {
|
|
|
152
152
|
previewHash,
|
|
153
153
|
};
|
|
154
154
|
}
|
|
155
|
+
/** Action families that leave a user-inspectable durable outcome. */
|
|
156
|
+
const ARTIFACT_EMITTING_ACTIONS = new Set([
|
|
157
|
+
"taskAdvance",
|
|
158
|
+
"taskCreate",
|
|
159
|
+
"contractApply",
|
|
160
|
+
"dagReport",
|
|
161
|
+
"taskImportPrd",
|
|
162
|
+
"importPrd",
|
|
163
|
+
]);
|
|
164
|
+
/**
|
|
165
|
+
* Project a terminal operation into an「产物」rail card (2026-08-16).
|
|
166
|
+
* Mirrors `ArtifactCardState` (schemaVersion 1) so the existing frontend
|
|
167
|
+
* parser renders it without any change. Best-effort: operations without a
|
|
168
|
+
* durable outcome (pure status reads) return undefined.
|
|
169
|
+
*/
|
|
170
|
+
function artifactFromTerminalOperation(operation, projection) {
|
|
171
|
+
if (!ARTIFACT_EMITTING_ACTIONS.has(operation.action))
|
|
172
|
+
return undefined;
|
|
173
|
+
// Failed/interrupted actions leave nothing inspectable as a deliverable.
|
|
174
|
+
if (operation.state !== "succeeded")
|
|
175
|
+
return undefined;
|
|
176
|
+
const taskId = projection.taskId;
|
|
177
|
+
const kind = taskId
|
|
178
|
+
? "contract"
|
|
179
|
+
: "operation";
|
|
180
|
+
const title = taskId ? `任务 ${taskId}` : `执行 ${operation.action}`;
|
|
181
|
+
const summary = projection.runSummary?.status
|
|
182
|
+
? `运行 ${projection.runSummary.runId} · ${projection.runSummary.status}`
|
|
183
|
+
: projection.summaryPreview || undefined;
|
|
184
|
+
const reportLines = [];
|
|
185
|
+
if (projection.runSummary) {
|
|
186
|
+
const rs = projection.runSummary;
|
|
187
|
+
reportLines.push(`运行: ${rs.runId}`);
|
|
188
|
+
reportLines.push(`状态: ${rs.status}`);
|
|
189
|
+
if (rs.verdict)
|
|
190
|
+
reportLines.push(`结论: ${rs.verdict}`);
|
|
191
|
+
if (rs.failureCategory)
|
|
192
|
+
reportLines.push(`失败归类: ${rs.failureCategory}`);
|
|
193
|
+
if (Array.isArray(rs.nodes)) {
|
|
194
|
+
for (const node of rs.nodes.slice(0, 30)) {
|
|
195
|
+
reportLines.push(`- ${node.id}: ${node.status}`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
if (projection.stdoutPreview) {
|
|
200
|
+
reportLines.push("", "--- stdout 摘要 ---", projection.stdoutPreview);
|
|
201
|
+
}
|
|
202
|
+
const previewHash = createHash("sha256")
|
|
203
|
+
.update(JSON.stringify({
|
|
204
|
+
operationId: operation.operationId,
|
|
205
|
+
state: operation.state,
|
|
206
|
+
taskId: taskId ?? null,
|
|
207
|
+
runSummary: projection.runSummary ?? null,
|
|
208
|
+
stdoutPreview: projection.stdoutPreview ?? null,
|
|
209
|
+
}))
|
|
210
|
+
.digest("hex");
|
|
211
|
+
return {
|
|
212
|
+
schemaVersion: 1,
|
|
213
|
+
kind,
|
|
214
|
+
artifactId: `operation-${operation.operationId}`,
|
|
215
|
+
title,
|
|
216
|
+
...(summary ? { summary } : {}),
|
|
217
|
+
...(reportLines.length > 0
|
|
218
|
+
? { reportText: reportLines.join("\n").slice(0, 8000) }
|
|
219
|
+
: {}),
|
|
220
|
+
inspectHref: projection.inspectHref,
|
|
221
|
+
previewHash,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
155
224
|
export function projectCompactSnapshot(value) {
|
|
156
225
|
const record = value && typeof value === "object"
|
|
157
226
|
? value
|
|
@@ -254,6 +323,7 @@ export function projectChatToolEventPayload(input) {
|
|
|
254
323
|
resultPreview: preview,
|
|
255
324
|
previewHash,
|
|
256
325
|
isError: Boolean(input.isError),
|
|
326
|
+
...(input.operationId ? { operationId: input.operationId } : {}),
|
|
257
327
|
};
|
|
258
328
|
}
|
|
259
329
|
export function projectOperationEventSummary(event) {
|
|
@@ -411,6 +481,27 @@ export function createChatOperationLinker(options) {
|
|
|
411
481
|
},
|
|
412
482
|
});
|
|
413
483
|
}
|
|
484
|
+
// Artifact projection (2026-08-16): the sidebar「产物」tab was permanently
|
|
485
|
+
// empty because nothing in the server ever emitted `artifact-ref`. On a
|
|
486
|
+
// terminal operation with a durable outcome (task created / advanced /
|
|
487
|
+
// contract applied / report produced) project one artifact card so the
|
|
488
|
+
// user can inspect the deliverable straight from the rail.
|
|
489
|
+
if (isTerminalOperationState(operation.state)) {
|
|
490
|
+
const artifact = artifactFromTerminalOperation(operation, projection);
|
|
491
|
+
if (artifact) {
|
|
492
|
+
const artifactDuplicate = options.chatEvents
|
|
493
|
+
.snapshot(input.sessionId)
|
|
494
|
+
.some((event) => event.kind === "artifact-ref" &&
|
|
495
|
+
event.data
|
|
496
|
+
.artifact?.previewHash === artifact.previewHash);
|
|
497
|
+
if (!artifactDuplicate) {
|
|
498
|
+
options.chatEvents.append(input.sessionId, input.turnId, {
|
|
499
|
+
kind: "artifact-ref",
|
|
500
|
+
data: { artifact },
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
414
505
|
if (isTerminalOperationState(operation.state)) {
|
|
415
506
|
const key = keyFor(input.sessionId, input.operationId);
|
|
416
507
|
followers.get(key)?.();
|
|
@@ -1963,6 +1963,31 @@ export class ConsolePiRuntime {
|
|
|
1963
1963
|
if (usage)
|
|
1964
1964
|
onEvent({ type: "usage", sessionId, usage });
|
|
1965
1965
|
}
|
|
1966
|
+
// Context usage rides the usage event: refresh on agent_settled so the
|
|
1967
|
+
// bar reflects the post-turn context window fill (pi-web parity).
|
|
1968
|
+
if (event.type === "agent_settled") {
|
|
1969
|
+
try {
|
|
1970
|
+
const stats = session.getSessionStats?.();
|
|
1971
|
+
const contextUsage = stats?.contextUsage;
|
|
1972
|
+
if (contextUsage) {
|
|
1973
|
+
onEvent({
|
|
1974
|
+
type: "usage",
|
|
1975
|
+
sessionId,
|
|
1976
|
+
usage: {
|
|
1977
|
+
inputTokens: 0,
|
|
1978
|
+
outputTokens: 0,
|
|
1979
|
+
totalTokens: 0,
|
|
1980
|
+
...(contextUsage.tokens === null
|
|
1981
|
+
? {}
|
|
1982
|
+
: { contextUsage }),
|
|
1983
|
+
},
|
|
1984
|
+
});
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
catch {
|
|
1988
|
+
// Stats getter is best-effort; never break the event stream.
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1966
1991
|
});
|
|
1967
1992
|
options?.signal?.addEventListener("abort", onAbort);
|
|
1968
1993
|
if (options?.signal?.aborted) {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
import { parseAssistantContent } from "./assistant-content.js";
|
|
16
16
|
import { OPERATOR_CHAT_ALLOWED_TOOLS, OPERATOR_CHAT_DENIED_TOOLS, OPERATOR_CHAT_DENIED_OPERATOR_ACTIONS, } from "./tools.js";
|
|
17
17
|
import { buildModelCallableToolSchemas } from "./tool-adapter.js";
|
|
18
|
-
import { cleanChatTitle } from "./session-store.js";
|
|
18
|
+
import { cleanChatTitle, acceptedOperationIdFromResult } from "./session-store.js";
|
|
19
19
|
import { hashChatTurnPayload, projectChatToolEventPayload, projectCompactSnapshot, } from "./chat-event-store.js";
|
|
20
20
|
import { THINKING_LEVELS, RuntimeSnapshotUnavailableError, } from "./pi-runtime.js";
|
|
21
21
|
import { ResourcePreferencesError, setSkillAutoInvocationPreference, } from "./resource-preferences-store.js";
|
|
@@ -106,7 +106,10 @@ function turnEventToStorePartial(event) {
|
|
|
106
106
|
value: event.args,
|
|
107
107
|
}),
|
|
108
108
|
};
|
|
109
|
-
case "tool_result":
|
|
109
|
+
case "tool_result": {
|
|
110
|
+
// Plan A (2026-08-16): surface the accepted-operation link on the live
|
|
111
|
+
// SSE frame so the frontend semantic event can resolve operation:<id>.
|
|
112
|
+
const resultOperationId = acceptedOperationIdFromResult(String(event.toolName ?? ""), event.result);
|
|
110
113
|
return {
|
|
111
114
|
kind: "tool_result",
|
|
112
115
|
data: projectChatToolEventPayload({
|
|
@@ -115,8 +118,10 @@ function turnEventToStorePartial(event) {
|
|
|
115
118
|
toolName: String(event.toolName ?? ""),
|
|
116
119
|
value: event.result,
|
|
117
120
|
isError: Boolean(event.isError),
|
|
121
|
+
...(resultOperationId ? { operationId: resultOperationId } : {}),
|
|
118
122
|
}),
|
|
119
123
|
};
|
|
124
|
+
}
|
|
120
125
|
case "agent_end":
|
|
121
126
|
return {
|
|
122
127
|
kind: "agent_end",
|
|
@@ -292,7 +292,13 @@ function maxWaitSeconds(args) {
|
|
|
292
292
|
return Math.max(1, Math.round(raw / 1000));
|
|
293
293
|
}
|
|
294
294
|
function activityRefForTool(tool) {
|
|
295
|
-
|
|
295
|
+
// Plan A (2026-08-16): server backfills operationId on the persisted tool
|
|
296
|
+
// entry (and live tool_result frames carry it in the result payload), so
|
|
297
|
+
// prefer the authoritative link before parsing args/result fallbacks.
|
|
298
|
+
const operationId = tool.operationId ??
|
|
299
|
+
operationIdFromArgs(tool.args) ??
|
|
300
|
+
operationIdFromArgs(tool.result) ??
|
|
301
|
+
operationIdFromArgs(tool.resultText);
|
|
296
302
|
return operationId ? `operation:${operationId}` : `tool:${tool.id}`;
|
|
297
303
|
}
|
|
298
304
|
function semanticKindForStatus(status) {
|
|
@@ -402,6 +402,11 @@ export async function runChatTurn(options) {
|
|
|
402
402
|
toolCallId: event.toolCallId,
|
|
403
403
|
toolName: event.toolName,
|
|
404
404
|
isError: event.isError,
|
|
405
|
+
// Durable tool→operation link (plan A): the restored timeline can
|
|
406
|
+
// resolve operation:<id> refs without the live operation-ref event.
|
|
407
|
+
...(acceptedOperationId
|
|
408
|
+
? { operationId: acceptedOperationId }
|
|
409
|
+
: {}),
|
|
405
410
|
}));
|
|
406
411
|
pending.push(store.recordToolInvocation(sessionId, {
|
|
407
412
|
toolCallId: event.toolCallId,
|
|
@@ -431,7 +436,7 @@ export async function runChatTurn(options) {
|
|
|
431
436
|
* huge operator-action payload (e.g. a full dagReport) cannot bloat the chat
|
|
432
437
|
* history record.
|
|
433
438
|
*/
|
|
434
|
-
function acceptedOperationIdFromResult(toolName, result) {
|
|
439
|
+
export function acceptedOperationIdFromResult(toolName, result) {
|
|
435
440
|
const candidates = [result];
|
|
436
441
|
if (result && typeof result === "object") {
|
|
437
442
|
const record = result;
|
|
@@ -70,6 +70,7 @@ export function hydrateSessionThread(raw) {
|
|
|
70
70
|
createdAt,
|
|
71
71
|
finishedAt: createdAt,
|
|
72
72
|
turnId,
|
|
73
|
+
...(item.operationId ? { operationId: item.operationId } : {}),
|
|
73
74
|
};
|
|
74
75
|
toolCalls.push(entry);
|
|
75
76
|
pendingTools.push(entry);
|
|
@@ -77,30 +78,89 @@ export function hydrateSessionThread(raw) {
|
|
|
77
78
|
}
|
|
78
79
|
return { messages, toolCalls };
|
|
79
80
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
81
|
+
/** Stable tool order: server eventSeq first, createdAt fallback, id tiebreak. */
|
|
82
|
+
function sortToolsStable(tools) {
|
|
83
|
+
return [...tools].sort((a, b) => {
|
|
84
|
+
if (a.eventSeq !== undefined &&
|
|
85
|
+
b.eventSeq !== undefined &&
|
|
86
|
+
a.eventSeq !== b.eventSeq) {
|
|
87
|
+
return a.eventSeq - b.eventSeq;
|
|
88
|
+
}
|
|
89
|
+
if (a.createdAt !== b.createdAt)
|
|
90
|
+
return a.createdAt - b.createdAt;
|
|
91
|
+
return a.id < b.id ? -1 : 1;
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Flush buffered consecutive tools into process items. Semantic operator
|
|
96
|
+
* tools stay as individual activity events; plain tools group into a
|
|
97
|
+
* `tool-group` ("并行调用 · N 项") when two or more land back to back.
|
|
98
|
+
* Thinking/text/activity boundaries break the buffer upstream.
|
|
99
|
+
*/
|
|
100
|
+
function flushToolBuffer(processItems, buffer) {
|
|
101
|
+
if (buffer.length === 0)
|
|
102
|
+
return;
|
|
103
|
+
const sorted = sortToolsStable(buffer);
|
|
104
|
+
let plainGroup = [];
|
|
105
|
+
const flushPlain = () => {
|
|
106
|
+
if (plainGroup.length === 1) {
|
|
107
|
+
processItems.push({ kind: "tool", tool: plainGroup[0] });
|
|
108
|
+
}
|
|
109
|
+
else if (plainGroup.length > 1) {
|
|
110
|
+
processItems.push({ kind: "tool-group", tools: plainGroup });
|
|
111
|
+
}
|
|
112
|
+
plainGroup = [];
|
|
113
|
+
};
|
|
114
|
+
for (const tool of sorted) {
|
|
115
|
+
const activity = semanticActivityForTool(tool);
|
|
116
|
+
if (activity) {
|
|
117
|
+
flushPlain();
|
|
118
|
+
processItems.push({ kind: "activity", activity });
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
plainGroup.push(tool);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
flushPlain();
|
|
86
125
|
}
|
|
87
126
|
function emitProcessBlocks(blocks, group, processItems, usedToolIds) {
|
|
127
|
+
let buffer = [];
|
|
88
128
|
for (const block of blocks) {
|
|
89
129
|
if (block.type === "thinking") {
|
|
130
|
+
flushToolBuffer(processItems, buffer);
|
|
131
|
+
buffer = [];
|
|
90
132
|
processItems.push({ kind: "thinking", thinking: block.thinking });
|
|
91
133
|
continue;
|
|
92
134
|
}
|
|
93
135
|
if (block.type === "text") {
|
|
94
|
-
|
|
136
|
+
flushToolBuffer(processItems, buffer);
|
|
137
|
+
buffer = [];
|
|
138
|
+
if (block.text.trim()) {
|
|
95
139
|
processItems.push({ kind: "text", text: block.text });
|
|
140
|
+
}
|
|
96
141
|
continue;
|
|
97
142
|
}
|
|
98
143
|
const tool = group.tools.find((entry) => entry.id === block.toolCallId);
|
|
99
|
-
if (tool) {
|
|
100
|
-
|
|
144
|
+
if (tool && !usedToolIds.has(tool.id)) {
|
|
145
|
+
buffer.push(tool);
|
|
101
146
|
usedToolIds.add(tool.id);
|
|
102
147
|
}
|
|
103
148
|
}
|
|
149
|
+
flushToolBuffer(processItems, buffer);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Insert tools attributed to this assistant stage but without a matching
|
|
153
|
+
* content toolCall block (live SSE tool_call frames often arrive before the
|
|
154
|
+
* assistant content does). They follow the stage's thinking/text instead of
|
|
155
|
+
* sinking to the end of the whole turn.
|
|
156
|
+
*/
|
|
157
|
+
function appendAssistantUnmatchedTools(group, assistant, processItems, usedToolIds) {
|
|
158
|
+
const unmatched = group.tools.filter((tool) => !usedToolIds.has(tool.id) && tool.assistantMessageId === assistant.id);
|
|
159
|
+
if (unmatched.length === 0)
|
|
160
|
+
return;
|
|
161
|
+
for (const tool of unmatched)
|
|
162
|
+
usedToolIds.add(tool.id);
|
|
163
|
+
flushToolBuffer(processItems, unmatched);
|
|
104
164
|
}
|
|
105
165
|
function fillProcessDetails(group) {
|
|
106
166
|
const usedToolIds = new Set();
|
|
@@ -109,14 +169,20 @@ function fillProcessDetails(group) {
|
|
|
109
169
|
const finalAssistant = group.assistants.at(-1);
|
|
110
170
|
for (const assistant of prior) {
|
|
111
171
|
emitProcessBlocks(getDisplayableAssistantBlocks(assistant.content ?? []), group, processItems, usedToolIds);
|
|
172
|
+
appendAssistantUnmatchedTools(group, assistant, processItems, usedToolIds);
|
|
112
173
|
}
|
|
113
174
|
const blocks = finalAssistant?.content ?? [];
|
|
114
175
|
const split = splitFinalAssistantBlocks(blocks, { isStreaming: group.live });
|
|
115
176
|
emitProcessBlocks(split.processBlocks, group, processItems, usedToolIds);
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
pushToolProcessItem(processItems, tool);
|
|
177
|
+
if (finalAssistant) {
|
|
178
|
+
appendAssistantUnmatchedTools(group, finalAssistant, processItems, usedToolIds);
|
|
119
179
|
}
|
|
180
|
+
// Bounded fallback for legacy restored rows with no assistant attribution:
|
|
181
|
+
// keep them at this turn's end in stable server order instead of randomly
|
|
182
|
+
// interleaving them into earlier stages.
|
|
183
|
+
const orphans = group.tools.filter((tool) => !usedToolIds.has(tool.id));
|
|
184
|
+
if (orphans.length > 0)
|
|
185
|
+
flushToolBuffer(processItems, orphans);
|
|
120
186
|
group.processItems = processItems;
|
|
121
187
|
if (!finalAssistant)
|
|
122
188
|
return;
|
|
@@ -149,6 +215,14 @@ export function buildTurnProcessGroups(input) {
|
|
|
149
215
|
current.user = message;
|
|
150
216
|
continue;
|
|
151
217
|
}
|
|
218
|
+
if (message.kind === "compaction") {
|
|
219
|
+
// Compaction is a standalone timeline event. Starting a fresh group
|
|
220
|
+
// prevents it from replacing the previous turn's assistant answer.
|
|
221
|
+
current = startGroup(`compaction-${message.id}`);
|
|
222
|
+
current.assistants.push(message);
|
|
223
|
+
current = null;
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
152
226
|
if (!current)
|
|
153
227
|
current = startGroup(`assistant-${message.id}`);
|
|
154
228
|
current.assistants.push(message);
|
|
@@ -250,3 +324,36 @@ export function processGroupSummary(tools, options) {
|
|
|
250
324
|
parts.push("运行中");
|
|
251
325
|
return { count, errorCount, runningCount, stepCount, label: parts.join(" · ") };
|
|
252
326
|
}
|
|
327
|
+
/** Summary over rendered process items (tool-group tools count individually). */
|
|
328
|
+
export function summarizeProcessItems(items) {
|
|
329
|
+
const tools = [];
|
|
330
|
+
let thinkingCount = 0;
|
|
331
|
+
let textCount = 0;
|
|
332
|
+
let activityCount = 0;
|
|
333
|
+
let activityErrorCount = 0;
|
|
334
|
+
let activityRunningCount = 0;
|
|
335
|
+
for (const item of items) {
|
|
336
|
+
if (item.kind === "thinking")
|
|
337
|
+
thinkingCount += 1;
|
|
338
|
+
else if (item.kind === "text")
|
|
339
|
+
textCount += 1;
|
|
340
|
+
else if (item.kind === "activity") {
|
|
341
|
+
activityCount += 1;
|
|
342
|
+
if (item.activity.kind === "error")
|
|
343
|
+
activityErrorCount += 1;
|
|
344
|
+
else if (item.activity.kind === "progress")
|
|
345
|
+
activityRunningCount += 1;
|
|
346
|
+
}
|
|
347
|
+
else if (item.kind === "tool")
|
|
348
|
+
tools.push(item.tool);
|
|
349
|
+
else
|
|
350
|
+
tools.push(...item.tools);
|
|
351
|
+
}
|
|
352
|
+
return processGroupSummary(tools, {
|
|
353
|
+
thinkingCount,
|
|
354
|
+
textCount,
|
|
355
|
+
activityCount,
|
|
356
|
+
activityErrorCount,
|
|
357
|
+
activityRunningCount,
|
|
358
|
+
});
|
|
359
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { access, readFile } from "node:fs/promises";
|
|
2
2
|
import { buildOperatorCapabilitiesDocument } from "../../shared/operator/capabilities.js";
|
|
3
3
|
import { buildOperatorResult, operatorFailed, operatorSucceeded, } from "../../shared/operator/envelope.js";
|
|
4
|
-
import { getTaskPaths, loadTaskConfig } from "../../task/runtime.js";
|
|
4
|
+
import { CANONICAL_TASK_ID_PATTERN, formatLocalBusinessDate, getTaskPaths, loadTaskConfig, } from "../../task/runtime.js";
|
|
5
5
|
import { diffTaskContract, validateDraftForTask, } from "../../task/contract/index.js";
|
|
6
6
|
import { stageUtf8Text } from "./app-data.js";
|
|
7
7
|
import { ALL_CONFIRMATION_CHALLENGES, hashDagBytes, } from "./dag-confirmation.js";
|
|
@@ -63,6 +63,8 @@ function str(v) {
|
|
|
63
63
|
return typeof v === "string" && v.trim() ? v.trim() : undefined;
|
|
64
64
|
}
|
|
65
65
|
const TASK_ID_PLACEHOLDER_RE = /[<>]|^(?:id|task-?id|your-task-id|todo|tbd)$/i;
|
|
66
|
+
/** Mirror of runtime GENERIC_TASK_SLUGS (not exported there); keep in sync. */
|
|
67
|
+
const GENERIC_SLUG_SET = new Set(["test", "fix", "tmp", "new-task"]);
|
|
66
68
|
const TASK_ADVANCE_ALIAS_ACTIONS = new Set(["promoteRun", "closeoutTask"]);
|
|
67
69
|
function taskIdInputError(taskId) {
|
|
68
70
|
if (!TASK_ID_PLACEHOLDER_RE.test(taskId))
|
|
@@ -1502,6 +1504,21 @@ export async function dispatchOperatorAction(ctx, req) {
|
|
|
1502
1504
|
const taskIdError = taskIdInputError(taskId);
|
|
1503
1505
|
if (taskIdError)
|
|
1504
1506
|
return invalid(action, taskIdError);
|
|
1507
|
+
// Canonical id gate at admission (2026-08-16): non-canonical ids were
|
|
1508
|
+
// accepted here (validateNewTaskId only warns) and every downstream
|
|
1509
|
+
// advance/import then flailed on the malformed task directory. Fail
|
|
1510
|
+
// fast with a concrete suggested id instead.
|
|
1511
|
+
if (!CANONICAL_TASK_ID_PATTERN.test(taskId)) {
|
|
1512
|
+
const slug = taskId
|
|
1513
|
+
.toLowerCase()
|
|
1514
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
1515
|
+
.replace(/^-+|-+$/g, "")
|
|
1516
|
+
.slice(0, 48);
|
|
1517
|
+
const suggested = slug && !GENERIC_SLUG_SET.has(slug)
|
|
1518
|
+
? `${formatLocalBusinessDate()}-${slug}`
|
|
1519
|
+
: `${formatLocalBusinessDate()}-${slug || "task"}-r2`;
|
|
1520
|
+
return invalid(action, `taskId must be canonical YYYY-MM-DD-<kebab-slug> (local business date); got "${taskId}" — suggested id: "${suggested}" (e.g. 2026-08-16-tool-preview-edge-test); retry newTask with that id`);
|
|
1521
|
+
}
|
|
1505
1522
|
return acceptOperation(ctx, {
|
|
1506
1523
|
action,
|
|
1507
1524
|
actionParams: p,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{g as p,r as u,d as a}from"./chunk-6Q2QTUOP-VvfacNrq.js";import{p as f}from"./chunk-JWPE2WC7-BlysCtod.js";import{_ as n,l as o}from"./mermaid.core-DWGK9kwP.js";import{M as c,b as d}from"./cynefin-VYW2F7L2-B1wtI3hU.js";import"./index-DQxiso1w.js";var v=d().RailroadAbnf.parser.LangiumParser,i=n(e=>{const r=e.alternatives.map(g);return r.length===1?r[0]:{type:"choice",alternatives:r}},"transformAlternation"),g=n(e=>{const r=e.elements.map(y);return r.length===1?r[0]:{type:"sequence",elements:r}},"transformConcatenation"),b=n(e=>{if(e.includes("*")){const[t,s]=e.split("*"),l=t?parseInt(t,10):0,m=s?parseInt(s,10):1/0;return{min:l,max:m}}const r=parseInt(e,10);return{min:r,max:r}},"parseRepeat"),y=n(e=>{const r=A(e.primary);if(!e.repeat)return r;const{min:t,max:s}=b(e.repeat);return t===0&&s===1?{type:"optional",element:r}:{type:"repetition",element:r,min:t,max:s}},"transformElement"),A=n(e=>{switch(e.$type){case"AbnfStringLiteral":return{type:"terminal",value:e.value};case"AbnfNumVal":return{type:"terminal",value:e.value};case"AbnfRuleName":return{type:"nonterminal",name:e.name};case"AbnfGroup":return i(e.element);case"AbnfOptionalGroup":return{type:"optional",element:i(e.element)};default:throw new Error(`Unsupported ABNF primary node: ${e.$type}`)}},"transformPrimary"),P=n(e=>({name:e.name,definition:i(e.definition)}),"transformRule"),h=n(e=>{f(e,a),e.title&&a.setTitle(e.title),e.rules.map(r=>a.addRule(P(r)))},"populateDb"),R={parse:n(e=>{a.clear(),o.debug("[ABNF Parser] Starting Langium parse");const r=v.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new c(r);const t=r.value;o.debug("[ABNF Parser] Parsed rules:",t.rules.length),h(t),o.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:a}},B={parser:R,db:a,renderer:u,styles:p};export{B as diagram};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{Y as ln,$ as an,a0 as F,a1 as q,a2 as j,a3 as un,a4 as y,a5 as tn,a6 as J,a7 as _,a8 as rn,a9 as o,aa as on,ab as sn,ac as fn}from"./mermaid.core-DWGK9kwP.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,I,D,v,A,z,a){var O=I-l,i=D-h,n=z-v,d=a-A,u=d*O-n*i;if(!(u*u<y))return u=(n*(h-A)-d*(l-v))/u,[l+u*O,h+u*i]}function U(l,h,I,D,v,A,z){var a=l-I,O=h-D,i=(z?A:-A)/J(a*a+O*O),n=i*O,d=-i*a,u=l+n,s=h+d,f=I+n,c=D+d,B=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,R=m*m+g*g,T=v-A,P=u*c-f*s,S=(g<0?-1:1)*J(on(0,T*T*R-P*P)),Y=(P*g-m*S)/R,$=(-P*m-g*S)/R,w=(P*g+m*S)/R,p=(-P*m+g*S)/R,x=Y-B,e=$-t,r=w-B,C=p-t;return x*x+e*e>r*r+C*C&&(Y=w,$=p),{cx:Y,cy:$,x01:-n,y01:-d,x11:Y*(v/T-1),y11:$*(v/T-1)}}function hn(){var l=cn,h=yn,I=j(0),D=null,v=gn,A=dn,z=mn,a=null,O=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-un,c=A.apply(this,arguments)-un,B=rn(c-f),t=c>f;if(a||(a=n=O()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(B>tn-y)a.moveTo(s*F(f),s*q(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*F(c),u*q(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,R=f,T=c,P=B,S=B,Y=z.apply(this,arguments)/2,$=Y>y&&(D?+D.apply(this,arguments):J(u*u+s*s)),w=_(rn(s-u)/2,+I.apply(this,arguments)),p=w,x=w,e,r;if($>y){var C=sn($/u*q(Y)),K=sn($/s*q(Y));(P-=C*2)>y?(C*=t?1:-1,R+=C,T-=C):(P=0,R=T=(f+c)/2),(S-=K*2)>y?(K*=t?1:-1,m+=K,g-=K):(S=0,m=g=(f+c)/2)}var G=s*F(m),H=s*q(m),L=u*F(T),M=u*q(T);if(w>y){var N=s*F(g),Q=s*q(g),V=u*F(R),W=u*q(R),E;if(B<an)if(E=pn(G,H,V,W,N,Q,L,M)){var X=G-E[0],Z=H-E[1],b=N-E[0],k=Q-E[1],nn=1/q(fn((X*b+Z*k)/(J(X*X+Z*Z)*J(b*b+k*k)))/2),en=J(E[0]*E[0]+E[1]*E[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}S>y?x>y?(e=U(V,W,G,H,s,x,t),r=U(N,Q,L,M,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(G,H),a.arc(0,0,s,m,g,!t)):a.moveTo(G,H),!(u>y)||!(P>y)?a.lineTo(L,M):p>y?(e=U(L,M,N,Q,u,-p,t),r=U(G,H,V,W,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-an/2;return[F(d)*n,q(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:j(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:j(+n),i):h},i.cornerRadius=function(n){return arguments.length?(I=typeof n=="function"?n:j(+n),i):I},i.padRadius=function(n){return arguments.length?(D=n==null?null:typeof n=="function"?n:j(+n),i):D},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:j(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:j(+n),i):A},i.padAngle=function(n){return arguments.length?(z=typeof n=="function"?n:j(+n),i):z},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};
|