@tea-agent/loop-agent 0.41.1-beta.0 → 0.41.1
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 +47 -267
- package/dist/application/dag/generate-task-dag.js +19 -75
- package/dist/application/task-lifecycle/advance.js +5 -24
- package/dist/application/task-lifecycle/observe.js +17 -171
- package/dist/application/task-lifecycle/plan-transitions.js +7 -42
- package/dist/application/task-lifecycle/recommendations.js +2 -13
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +0 -7
- package/dist/cli/program.js +0 -5
- package/dist/commands/client-recovery.js +0 -3
- package/dist/commands/init.js +1 -27
- package/dist/commands/task-advance.js +0 -19
- package/dist/executors/dag-pi-executor.js +231 -3784
- package/dist/executors/pi-executor.js +4 -15
- package/dist/executors/shell-executor.js +217 -945
- package/dist/executors/shell-write-guard.js +0 -7
- package/dist/shared/dag-failure-category.js +0 -12
- package/dist/shared/openspec-spec.js +4 -70
- package/dist/shared/operator/capabilities.js +0 -7
- package/dist/task/config-types.js +7 -34
- package/dist/task/contract/adopt.js +0 -4
- package/dist/task/contract/import-revision.js +0 -4
- package/dist/task/contract/project.js +0 -3
- package/dist/task/contract/schema.js +1 -2
- package/dist/task/frontend-project-capability.js +20 -203
- package/dist/task/runtime.js +2 -5
- package/dist/task/source-prepare/build-draft.js +3 -3
- package/dist/task/source-prepare/fragment-inventory.js +9 -15
- package/dist/task/source-prepare/prepare.js +1 -89
- package/dist/task/source-prepare/semantic-intake.js +2 -6
- package/dist/task/source-references.js +1 -22
- package/dist/worker/console/chat/chat-ui-policy.js +4 -2
- package/dist/worker/console/chat/pi-runtime.js +176 -9
- package/dist/worker/console/chat/repo-browser.js +10 -3
- package/dist/worker/console/chat/routes.js +42 -22
- package/dist/worker/console/chat/semantic-activity.js +12 -11
- package/dist/worker/console/chat/session-stats.js +96 -0
- package/dist/worker/console/chat/session-store.js +117 -70
- package/dist/worker/console/chat/shortcuts.js +1 -1
- package/dist/worker/console/chat/turn-process.js +139 -60
- package/dist/worker/console/index.js +0 -3
- package/dist/worker/console/operation-store.js +6 -226
- package/dist/worker/console/operator-actions.js +7 -169
- package/dist/worker/console/pi-plugins.js +3 -1
- package/dist/worker/console/server.js +0 -3
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-Bho4we4c.js → abnfDiagram-N423BO3Z-D7oU983t.js} +1 -1
- package/dist/worker/console/static/assets/{arc-TBDkTeK0.js → arc-BrKSRdFS.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-BF_fZrn2.js → architectureDiagram-T3A2C74G-CiXV7L56.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-CCnPgUe2.js → blockDiagram-VBNYF7ZC-D_Hs_gIG.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-BCYZJLAT.js → c4Diagram-5PPSVZJV-DbM-Lqqh.js} +1 -1
- package/dist/worker/console/static/assets/channel-D1xajGpe.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-D8K6adhU.js → chunk-2GRJ4B5K-D1M8uqJw.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-DkT1Q78b.js → chunk-2Q5K7J3B-4D3RFHT6.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-DuhvpIr7.js → chunk-5RXB4S5H-CdwpY_n9.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-BnK6dC49.js → chunk-5VM5RSS4-BLeLZ9PG.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DPz__z7r.js → chunk-6Q2QTUOP-CeRNEMvc.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-0kkppM2j.js → chunk-GF5L2VYU-CjkHP8Vy.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-BB41iK5t.js → chunk-JWPE2WC7-B54ZUQY-.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-CiVrxKHU.js → chunk-KBJHAD2P-4bKaMVmx.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-C2h9Xje1.js → chunk-RYQCIY6F-Cyjfznu_.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-3iOSHdbM.js → chunk-XXDRQBXY-CDN-zGFV.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BmuGTfRr.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BmuGTfRr.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-BN3A7tkk.js → cose-bilkent-JH36ORCC-HxJmz411.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-CTuBy0Or.js → cynefin-VYW2F7L2-DtSt5K2w.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-wb6tUlMN.js → cynefinDiagram-MW4NZA55-VtkSpRmG.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-GP1Ivx8B.js → dagre-VZM6K2ZE-Bj4DT4du.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-ejrcK6L0.js → diagram-7IWD3JNH-Bpuk6BmP.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-YFkCCvUU.js → diagram-B4RE2ZJO-BHYGv_yO.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-B4rrvIaL.js → diagram-LBJQPF4R-DEx3WwnT.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-C-YIZcUf.js → diagram-Q27KOJAE-Cj0dS5e8.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-BPF5wcL1.js → diagram-UB23O5K3-B97h8l21.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-CU5SzXjO.js → ebnfDiagram-BXEA7PRR-DMSFAhBd.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-CpL414DE.js → erDiagram-JOGREHBK-DhVOCbt6.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-MrhLzNau.js → flowDiagram-UKHOOZJN-GlakITWL.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-Bg4qFuUo.js → ganttDiagram-PKOTCBZU-VxPpu9eQ.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-ColxSXLR.js → gitGraphDiagram-DS77QQ5N-Bs_e5DRT.js} +1 -1
- package/dist/worker/console/static/assets/index-BLzcAykx.js +451 -0
- package/dist/worker/console/static/assets/index-Cs9JUSPs.css +1 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-Czvtm6gc.js → infoDiagram-6WML65LV-BXYuU4Uw.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-B7Xp8HyJ.js → ishikawaDiagram-WSZJBQD7-C34erCbU.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-DWYZcvph.js → journeyDiagram-NVQOT4AX-DZCAiOU3.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-KTLZRUjx.js → kanban-definition-27J2QSJJ-C9gaTdUm.js} +1 -1
- package/dist/worker/console/static/assets/{linear-_Exn7bfl.js → linear-CvI0Z17U.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-Cxg8kX8V.js → mermaid.core-DMMSIx7j.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-oKrlo2Bt.js → mindmap-definition-FAOFIHXS-BKKP0EO6.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-XAeteiNd.js → pegDiagram-VL7TDLO6-C3LiFbl5.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-BTf1K-lp.js → pieDiagram-7S7Q4E2Y-Bb3Om2Dz.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-C0IlY0Jf.js → quadrantDiagram-CIZ2JOQS-DNthMjzc.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-BVbKtZhW.js → railroadDiagram-AXF67PYL-miFsr4Tv.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-C5e4JFcr.js → requirementDiagram-LRYGKXZP-yXUncmn6.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-Cz30hc29.js → sankeyDiagram-W5VNT64P-DLeWKLak.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-DS33n6d0.js → sequenceDiagram-SI44F4Z6-CI3he1R3.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-46w7v-9T.js → sizeCapture-X5ZJPWSS-B7jfDkS5.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-pbcD5SsZ.js → stateDiagram-OKZ733FA-BmrZod7J.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-D9ykKEWP.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-DuivYBSj.js → swimlanes-SLNWSIFB-CDOlHVSY.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-BvPOXALT.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-Cb9VZVIl.js → timeline-definition-Z64GVDOM-Cz-XX1gy.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-D2-mfOgR.js → vennDiagram-T6HMQDX7-caV4cI8C.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-DeS2aC76.js → wardleyDiagram-T6FBY63Y-dImPlnIO.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-WGsrFjxH.js → xychartDiagram-ELKLHX3M-CtRYVJcn.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/app/useRunProgress.js +9 -2
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +19 -8
- package/dist/worker/console/static-src/operator-chat/dag-progress-link.js +22 -0
- package/dist/worker/console/static-src/operator-chat/open-preview-in-browser.js +15 -7
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +2 -0
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +6 -0
- package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +28 -3
- package/dist/worker/console/static-src/pages/tasks/run-panel-progress.js +106 -0
- package/dist/worker/console/workspace-context.js +1 -111
- package/dist/worker/observability/read-model.js +4 -0
- package/dist/worker/observe/static/dag-node-purpose.js +0 -5
- package/dist/worker/observe/static/views/dag-inspector.js +2 -7
- package/dist/worker/observe/static/views/session-timeline.js +46 -15
- package/dist/workflows/dag/backend-test-result-contract.js +1 -1
- package/dist/workflows/dag/contract-validator-registrations.js +2 -1
- package/dist/workflows/dag/frontend-implementation-contract.js +162 -932
- package/dist/workflows/dag/frontend-plan-render.js +1 -2
- package/dist/workflows/dag/frontend-prewrite-gate.js +349 -255
- package/dist/workflows/dag/frontend-recovery-plan.js +10 -17
- package/dist/workflows/dag/frontend-recovery-run.js +20 -33
- package/dist/workflows/dag/frontend-repair.js +432 -1
- package/dist/workflows/dag/frontend-review-context.js +15 -261
- package/dist/workflows/dag/frontend-verification-trace.js +24 -249
- package/dist/workflows/dag/frontend-worktree-diff.js +17 -250
- package/dist/workflows/dag/init-hybrid.js +494 -685
- package/dist/workflows/dag/interrupt-request.js +0 -7
- package/dist/workflows/dag/node-execution.js +4 -732
- package/dist/workflows/dag/prompt.js +1 -34
- package/dist/workflows/dag/report.js +1 -37
- package/dist/workflows/dag/rerun-feedback.js +1 -256
- package/dist/workflows/dag/rerun-plan.js +2 -10
- package/dist/workflows/dag/rerun-run.js +0 -23
- package/dist/workflows/dag/rerun-task.js +0 -29
- package/dist/workflows/dag/retry-policy.js +104 -214
- package/dist/workflows/dag/runner.js +122 -429
- package/dist/workflows/dag/scheduler.js +16 -114
- package/dist/workflows/dag/types.js +12 -239
- package/dist/workflows/dag/validate.js +12 -28
- package/docs/init-surface.manifest.json +12 -30
- package/docs/skills/vetted-skill-registry.md +2 -4
- package/docs/templates/README.md +0 -2
- package/docs/templates/agent-dag-report.schema.json +2 -8
- package/docs/templates/agent-dag.schema.json +1 -1
- package/docs/templates/frontend-implementation-contract.schema.json +1 -4
- package/harness.json +3 -3
- package/package.json +1 -1
- package/skills/frontend-bounded-implement/SKILL.md +14 -15
- package/skills/frontend-design-review/SKILL.md +41 -22
- package/skills/frontend-implementation/SKILL.md +52 -0
- package/skills/frontend-implementation/references/code-standards.md +33 -0
- package/skills/frontend-implementation/references/design-spec.md +56 -0
- package/skills/frontend-implementation/references/node-contracts.md +31 -0
- package/skills/frontend-review/SKILL.md +15 -20
- package/skills/frontend-review/references/review-findings.md +7 -6
- package/skills/frontend-verification/SKILL.md +1 -1
- package/skills/loop-agent/references/command-reference.md +0 -1
- package/skills/loop-agent/references/hybrid-dag.md +2 -2
- package/dist/commands/dag-follow-up.js +0 -138
- package/dist/executors/pi-read-budget-policy.js +0 -239
- package/dist/worker/console/frontend-human-decision-adapter.js +0 -19
- package/dist/worker/console/frontend-split-operation-adapter.js +0 -20
- package/dist/worker/console/static/assets/channel-bnXW0U3C.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-rZpq_twR.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-rZpq_twR.js +0 -1
- package/dist/worker/console/static/assets/index-D3CC4eUz.css +0 -1
- package/dist/worker/console/static/assets/index-DLt4ZFvD.js +0 -437
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-zBMYiUyi.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-d_rms-5i.js +0 -8
- package/dist/worker/console/static-src/active-run-badge.js +0 -17
- package/dist/worker/materialize/frontend-split-task-materializer.js +0 -72
- package/dist/workflows/dag/dag-retry-schema.js +0 -138
- package/dist/workflows/dag/frontend-closeout.js +0 -221
- package/dist/workflows/dag/frontend-design-policy.js +0 -400
- package/dist/workflows/dag/frontend-human-decision.js +0 -182
- package/dist/workflows/dag/frontend-provider-capability-matrix.js +0 -159
- package/dist/workflows/dag/frontend-read-budgets.js +0 -12
- package/dist/workflows/dag/frontend-recovery-capsule.js +0 -455
- package/dist/workflows/dag/frontend-recovery-controller.js +0 -202
- package/dist/workflows/dag/frontend-recovery-lineage.js +0 -178
- package/dist/workflows/dag/frontend-review-findings.js +0 -270
- package/dist/workflows/dag/frontend-shadow-dual-write.js +0 -914
- package/dist/workflows/dag/frontend-shape-capsule-store.js +0 -191
- package/dist/workflows/dag/frontend-shape-facts.js +0 -409
- package/dist/workflows/dag/frontend-shape.js +0 -427
- package/dist/workflows/dag/frontend-source-fidelity-ledger.js +0 -108
- package/dist/workflows/dag/frontend-split-application-service.js +0 -203
- package/dist/workflows/dag/frontend-split-orchestrator.js +0 -899
- package/dist/workflows/dag/frontend-typed-event-store.js +0 -452
- package/dist/workflows/dag/frontend-typed-event-transaction.js +0 -180
- package/dist/workflows/dag/frontend-writer-admission.js +0 -285
- package/dist/workflows/dag/frontend-writer-status.js +0 -256
- package/dist/workflows/dag/recovery-lease.js +0 -80
- package/docs/templates/frontend-implementation-dag.json +0 -89
- package/docs/templates/spec-registry.schema.json +0 -45
- package/skills/frontend-bounded-implement/references/code-standards.md +0 -19
- package/skills/frontend-contract/SKILL.md +0 -23
- package/skills/frontend-contract/references/contract-protocol.md +0 -34
- package/skills/frontend-plan/SKILL.md +0 -26
- package/skills/frontend-plan/references/decision-contract.md +0 -37
- package/skills/frontend-plan/references/design-decisions.md +0 -17
- package/skills/frontend-scout/SKILL.md +0 -25
- package/skills/frontend-scout/references/design-evidence.md +0 -16
- package/skills/frontend-scout/references/scout-evidence.md +0 -23
|
@@ -35,8 +35,25 @@ function toProgressNode(node) {
|
|
|
35
35
|
startedAt: typeof node.startedAt === "string" ? node.startedAt : undefined,
|
|
36
36
|
durationMs: typeof node.durationMs === "number" ? node.durationMs : undefined,
|
|
37
37
|
errorPreview: typeof node.errorPreview === "string" ? node.errorPreview : undefined,
|
|
38
|
+
lastMeaningfulProgressAt: typeof node.lastMeaningfulProgressAt === "string"
|
|
39
|
+
? node.lastMeaningfulProgressAt
|
|
40
|
+
: undefined,
|
|
41
|
+
livenessStatus: typeof node.livenessStatus === "string" ? node.livenessStatus : undefined,
|
|
38
42
|
};
|
|
39
43
|
}
|
|
44
|
+
function latestIso(values) {
|
|
45
|
+
let latest = null;
|
|
46
|
+
for (const iso of values) {
|
|
47
|
+
if (!iso)
|
|
48
|
+
continue;
|
|
49
|
+
const time = Date.parse(iso);
|
|
50
|
+
if (!Number.isFinite(time))
|
|
51
|
+
continue;
|
|
52
|
+
if (!latest || time > latest.time)
|
|
53
|
+
latest = { iso, time };
|
|
54
|
+
}
|
|
55
|
+
return latest?.iso ?? null;
|
|
56
|
+
}
|
|
40
57
|
function elapsedSince(iso, now) {
|
|
41
58
|
if (!iso)
|
|
42
59
|
return null;
|
|
@@ -58,6 +75,8 @@ export function summarizeRunProgress(dag, now = Date.now()) {
|
|
|
58
75
|
stalled: false,
|
|
59
76
|
currentNodeElapsedMs: null,
|
|
60
77
|
runElapsedMs: null,
|
|
78
|
+
lastMeaningfulProgressAt: null,
|
|
79
|
+
observedAtMs: now,
|
|
61
80
|
};
|
|
62
81
|
}
|
|
63
82
|
const rawNodes = Array.isArray(dag.nodes) ? dag.nodes : [];
|
|
@@ -86,6 +105,8 @@ export function summarizeRunProgress(dag, now = Date.now()) {
|
|
|
86
105
|
runElapsedMs: terminal
|
|
87
106
|
? (runDurationMs ?? elapsedSince(runStartedAt, now))
|
|
88
107
|
: elapsedSince(runStartedAt, now),
|
|
108
|
+
lastMeaningfulProgressAt: latestIso(nodes.map((node) => node.lastMeaningfulProgressAt)),
|
|
109
|
+
observedAtMs: now,
|
|
89
110
|
};
|
|
90
111
|
}
|
|
91
112
|
const TERMINAL_RUN_PROGRESS_STATUSES = new Set([
|
|
@@ -108,3 +129,88 @@ export function formatDurationMs(ms) {
|
|
|
108
129
|
return `${minutes} 分 ${seconds} 秒`;
|
|
109
130
|
return `${seconds} 秒`;
|
|
110
131
|
}
|
|
132
|
+
function friendlyRunPhase(node) {
|
|
133
|
+
if (!node)
|
|
134
|
+
return "正在等待下一阶段";
|
|
135
|
+
const searchable = `${node.nodeId} ${node.label}`.toLowerCase();
|
|
136
|
+
if (/contract|boundary|intake/.test(searchable))
|
|
137
|
+
return "正在确认执行边界";
|
|
138
|
+
if (/scout|explore|research/.test(searchable))
|
|
139
|
+
return "正在梳理影响范围";
|
|
140
|
+
if (/plan|design/.test(searchable))
|
|
141
|
+
return "正在规划实施步骤";
|
|
142
|
+
if (/implement|repair|fix|write/.test(searchable))
|
|
143
|
+
return "正在实现本次修改";
|
|
144
|
+
if (/verify|test|lint|review|check/.test(searchable))
|
|
145
|
+
return "正在验证本次修改";
|
|
146
|
+
if (/closeout|handoff|report/.test(searchable))
|
|
147
|
+
return "正在整理执行结果";
|
|
148
|
+
if (node.label && node.label !== node.nodeId)
|
|
149
|
+
return `正在进行${node.label}`;
|
|
150
|
+
return "正在推进当前阶段";
|
|
151
|
+
}
|
|
152
|
+
function formatMeaningfulAge(iso, now) {
|
|
153
|
+
if (!iso)
|
|
154
|
+
return "等待首个执行进展";
|
|
155
|
+
const time = Date.parse(iso);
|
|
156
|
+
if (!Number.isFinite(time))
|
|
157
|
+
return "等待首个执行进展";
|
|
158
|
+
const seconds = Math.max(0, Math.floor((now - time) / 1000));
|
|
159
|
+
if (seconds < 60)
|
|
160
|
+
return `最近进展 ${seconds} 秒前`;
|
|
161
|
+
const minutes = Math.floor(seconds / 60);
|
|
162
|
+
if (minutes < 60)
|
|
163
|
+
return `最近进展 ${minutes} 分钟前`;
|
|
164
|
+
return `最近进展 ${Math.floor(minutes / 60)} 小时前`;
|
|
165
|
+
}
|
|
166
|
+
/** Human-facing copy shared by the task panel and Operator Chat progress row. */
|
|
167
|
+
export function presentRunProgress(summary, now = Date.now()) {
|
|
168
|
+
if (!summary || !summary.effectiveStatus) {
|
|
169
|
+
return {
|
|
170
|
+
kind: "pending",
|
|
171
|
+
title: "DAG 正在启动",
|
|
172
|
+
detail: "正在等待首个执行进展",
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
const status = summary.effectiveStatus;
|
|
176
|
+
const liveElapsed = summary.runElapsedMs == null
|
|
177
|
+
? null
|
|
178
|
+
: summary.runElapsedMs +
|
|
179
|
+
(summary.terminal ? 0 : Math.max(0, now - summary.observedAtMs));
|
|
180
|
+
const elapsed = formatDurationMs(liveElapsed);
|
|
181
|
+
const progressDetail = `${friendlyRunPhase(summary.running)} · 已完成 ${summary.finished}/${summary.total} 步 · ${formatMeaningfulAge(summary.lastMeaningfulProgressAt, now)}`;
|
|
182
|
+
if (status === "running") {
|
|
183
|
+
return { kind: "progress", title: `DAG 执行中 · 已运行 ${elapsed}`, detail: progressDetail };
|
|
184
|
+
}
|
|
185
|
+
if (status === "running-quiet") {
|
|
186
|
+
return { kind: "quiet", title: `DAG 仍在执行 · 已运行 ${elapsed}`, detail: progressDetail };
|
|
187
|
+
}
|
|
188
|
+
if (status === "running-suspected-stall") {
|
|
189
|
+
return { kind: "warning", title: `执行可能停滞 · 已运行 ${elapsed}`, detail: progressDetail };
|
|
190
|
+
}
|
|
191
|
+
if (["needs-attention", "paused", "interrupted"].includes(status)) {
|
|
192
|
+
return { kind: "warning", title: "DAG 需要处理", detail: progressDetail };
|
|
193
|
+
}
|
|
194
|
+
if (status === "finished") {
|
|
195
|
+
return {
|
|
196
|
+
kind: "success",
|
|
197
|
+
title: `DAG 已完成 · 总用时 ${elapsed}`,
|
|
198
|
+
detail: `已完成 ${summary.finished}/${summary.total} 步 · ${formatMeaningfulAge(summary.lastMeaningfulProgressAt, now)}`,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
if (status === "partial_failed" || status === "failed") {
|
|
202
|
+
return {
|
|
203
|
+
kind: "error",
|
|
204
|
+
title: `DAG 未完成 · 总用时 ${elapsed}`,
|
|
205
|
+
detail: `已完成 ${summary.finished}/${summary.total} 步 · ${formatMeaningfulAge(summary.lastMeaningfulProgressAt, now)}`,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
if (status === "pending") {
|
|
209
|
+
return { kind: "pending", title: "DAG 正在启动", detail: progressDetail };
|
|
210
|
+
}
|
|
211
|
+
return {
|
|
212
|
+
kind: "warning",
|
|
213
|
+
title: `DAG 已结束 · 总用时 ${elapsed}`,
|
|
214
|
+
detail: `已完成 ${summary.finished}/${summary.total} 步`,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
@@ -19,10 +19,9 @@
|
|
|
19
19
|
*/
|
|
20
20
|
import { randomBytes } from "node:crypto";
|
|
21
21
|
import { existsSync } from "node:fs";
|
|
22
|
-
import { rm } from "node:fs/promises";
|
|
23
22
|
import path from "node:path";
|
|
24
23
|
import { LoopAgentClient } from "../loop-agent/loop-agent-client.js";
|
|
25
|
-
import { openConsoleAppData
|
|
24
|
+
import { openConsoleAppData } from "./app-data.js";
|
|
26
25
|
import { DagConfirmationStore } from "./dag-confirmation.js";
|
|
27
26
|
import { DagExecutionReceiptStore } from "./dag-execution-receipt.js";
|
|
28
27
|
import { DraftStore } from "./draft-store.js";
|
|
@@ -39,9 +38,6 @@ import { GoalRoundDriver } from "./chat/goal-round-driver.js";
|
|
|
39
38
|
import { driveGoalRound, recoverDeferredTurns } from "./chat/routes.js";
|
|
40
39
|
import { createOperationEventStore, } from "./operation-sse.js";
|
|
41
40
|
import { OperationStore } from "./operation-store.js";
|
|
42
|
-
import { createFrontendSplitOperationAdapter, } from "./frontend-split-operation-adapter.js";
|
|
43
|
-
import { createFrontendHumanDecisionAdapter } from "./frontend-human-decision-adapter.js";
|
|
44
|
-
import { FrontendSplitTaskMaterializer, } from "../materialize/frontend-split-task-materializer.js";
|
|
45
41
|
import { probePiReadiness, } from "./pi-readiness.js";
|
|
46
42
|
import { resolveSiblingLoopAgentBin } from "./sibling-controller.js";
|
|
47
43
|
import { NightAuxTicker } from "./night-aux-ticker.js";
|
|
@@ -77,106 +73,6 @@ export async function createWorkspaceContext(record, deps) {
|
|
|
77
73
|
const events = createOperationEventStore({
|
|
78
74
|
persistenceDir: path.join(appData.operations, "events"),
|
|
79
75
|
});
|
|
80
|
-
// Frontend split runtime is repo-scoped: materialization, durable schedule
|
|
81
|
-
// intents, and aggregate closeout must use this workspace's app-data and
|
|
82
|
-
// OperationStore rather than the process startup workspace.
|
|
83
|
-
const splitScheduleDir = path.join(appData.root, "frontend-split-schedule");
|
|
84
|
-
const splitAuditDir = path.join(appData.root, "frontend-split-audit");
|
|
85
|
-
const splitAggregateDir = path.join(appData.root, "frontend-split-aggregate");
|
|
86
|
-
const splitMaterializationBackend = {
|
|
87
|
-
async ensureTask({ taskId }) {
|
|
88
|
-
return { taskId, complete: true };
|
|
89
|
-
},
|
|
90
|
-
async ensureOperation({ taskId, operationId, payloadDigest, request }) {
|
|
91
|
-
const action = request.kind === "child"
|
|
92
|
-
? "frontendSplitChild"
|
|
93
|
-
: "frontendSplitIntegration";
|
|
94
|
-
const { operation } = await operations.ensureSplitMaterializedOperation({
|
|
95
|
-
operationId,
|
|
96
|
-
taskId,
|
|
97
|
-
action,
|
|
98
|
-
actionParams: request,
|
|
99
|
-
payloadHash: payloadDigest,
|
|
100
|
-
});
|
|
101
|
-
return {
|
|
102
|
-
taskId: operation.taskId ?? taskId,
|
|
103
|
-
operationId: operation.operationId,
|
|
104
|
-
complete: true,
|
|
105
|
-
};
|
|
106
|
-
},
|
|
107
|
-
};
|
|
108
|
-
const materializer = new FrontendSplitTaskMaterializer(splitMaterializationBackend);
|
|
109
|
-
const schedule = async (input) => {
|
|
110
|
-
const fact = {
|
|
111
|
-
schemaVersion: 1,
|
|
112
|
-
kind: "frontend-split-schedule-intent",
|
|
113
|
-
operationId: input.operationId,
|
|
114
|
-
generation: input.generation,
|
|
115
|
-
bindingDigest: input.bindingDigest,
|
|
116
|
-
recordedAt: new Date().toISOString(),
|
|
117
|
-
};
|
|
118
|
-
const name = `${sha256Json({
|
|
119
|
-
operationId: input.operationId,
|
|
120
|
-
generation: input.generation,
|
|
121
|
-
bindingDigest: input.bindingDigest,
|
|
122
|
-
})}.json`;
|
|
123
|
-
await writeSecureJson(path.join(splitScheduleDir, name), fact);
|
|
124
|
-
};
|
|
125
|
-
const auditStale = async (input) => {
|
|
126
|
-
const fact = {
|
|
127
|
-
schemaVersion: 1,
|
|
128
|
-
kind: "frontend-split-stale-audit",
|
|
129
|
-
operationId: input.operationId,
|
|
130
|
-
reason: input.reason,
|
|
131
|
-
fact: input.fact,
|
|
132
|
-
recordedAt: new Date().toISOString(),
|
|
133
|
-
};
|
|
134
|
-
await writeSecureJson(path.join(splitAuditDir, `${sha256Json(fact)}.json`), fact);
|
|
135
|
-
};
|
|
136
|
-
const prepareAggregate = async ({ operationId, aggregate, }) => {
|
|
137
|
-
const before = await operations.get(operationId);
|
|
138
|
-
if (!before) {
|
|
139
|
-
throw new Error(`cannot prepare aggregate: parent operation not found: ${operationId}`);
|
|
140
|
-
}
|
|
141
|
-
const aggregateFile = path.join(splitAggregateDir, `${aggregate.digest}.json`);
|
|
142
|
-
let parentRecordPath = "";
|
|
143
|
-
let wroteAggregate = false;
|
|
144
|
-
let wroteParent = false;
|
|
145
|
-
return {
|
|
146
|
-
committedRef: {
|
|
147
|
-
ref: aggregateFile,
|
|
148
|
-
digest: aggregate.digest,
|
|
149
|
-
commitState: "committed",
|
|
150
|
-
},
|
|
151
|
-
async commit(input) {
|
|
152
|
-
parentRecordPath = input.parentRecordPath;
|
|
153
|
-
await writeSecureJson(aggregateFile, {
|
|
154
|
-
schemaVersion: 1,
|
|
155
|
-
kind: "frontend-split-aggregate-closeout",
|
|
156
|
-
operationId,
|
|
157
|
-
aggregate,
|
|
158
|
-
});
|
|
159
|
-
wroteAggregate = true;
|
|
160
|
-
await writeSecureJson(input.parentRecordPath, input.parentRecord);
|
|
161
|
-
wroteParent = true;
|
|
162
|
-
},
|
|
163
|
-
async rollback() {
|
|
164
|
-
if (wroteAggregate)
|
|
165
|
-
await rm(aggregateFile, { force: true });
|
|
166
|
-
if (wroteParent && parentRecordPath) {
|
|
167
|
-
await writeSecureJson(parentRecordPath, before);
|
|
168
|
-
}
|
|
169
|
-
},
|
|
170
|
-
};
|
|
171
|
-
};
|
|
172
|
-
const splitOrchestrator = createFrontendSplitOperationAdapter({
|
|
173
|
-
store: operations,
|
|
174
|
-
materializer,
|
|
175
|
-
schedule,
|
|
176
|
-
auditStale,
|
|
177
|
-
prepareAggregate,
|
|
178
|
-
});
|
|
179
|
-
const humanDecisionAdapter = createFrontendHumanDecisionAdapter(operations);
|
|
180
76
|
const drafts = new DraftStore(appData);
|
|
181
77
|
const confirmations = new DagConfirmationStore(appData, deps.bootToken.confirmationToken);
|
|
182
78
|
const dagExecutionReceipts = new DagExecutionReceiptStore(appData);
|
|
@@ -201,9 +97,6 @@ export async function createWorkspaceContext(record, deps) {
|
|
|
201
97
|
client,
|
|
202
98
|
operations,
|
|
203
99
|
events,
|
|
204
|
-
splitOrchestrator,
|
|
205
|
-
humanDecisionAdapter,
|
|
206
|
-
materializer,
|
|
207
100
|
drafts,
|
|
208
101
|
confirmations,
|
|
209
102
|
dagExecutionReceipts,
|
|
@@ -299,9 +192,6 @@ export async function createWorkspaceContext(record, deps) {
|
|
|
299
192
|
client,
|
|
300
193
|
operations,
|
|
301
194
|
events,
|
|
302
|
-
splitOrchestrator,
|
|
303
|
-
humanDecisionAdapter,
|
|
304
|
-
materializer,
|
|
305
195
|
drafts,
|
|
306
196
|
confirmations,
|
|
307
197
|
dagExecutionReceipts,
|
|
@@ -2362,6 +2362,8 @@ function parseDagNodes(parsed, rankByNode, modelByNode) {
|
|
|
2362
2362
|
durationMs: readNumber(node, "durationMs"),
|
|
2363
2363
|
startedAt: readString(node, "startedAt"),
|
|
2364
2364
|
finishedAt: readString(node, "finishedAt"),
|
|
2365
|
+
lastMeaningfulProgressAt: readString(node, "lastMeaningfulProgressAt"),
|
|
2366
|
+
livenessStatus: readString(node, "livenessStatus"),
|
|
2365
2367
|
...(outputPreview ? { outputPreview } : {}),
|
|
2366
2368
|
...(errorPreview ? { errorPreview } : {}),
|
|
2367
2369
|
...(attempts && attempts.length > 0
|
|
@@ -2407,6 +2409,8 @@ function parseDagNodes(parsed, rankByNode, modelByNode) {
|
|
|
2407
2409
|
durationMs: readNumber(node, "durationMs"),
|
|
2408
2410
|
startedAt: readString(node, "startedAt"),
|
|
2409
2411
|
finishedAt: readString(node, "finishedAt"),
|
|
2412
|
+
lastMeaningfulProgressAt: readString(node, "lastMeaningfulProgressAt"),
|
|
2413
|
+
livenessStatus: readString(node, "livenessStatus"),
|
|
2410
2414
|
...(outputPreview ? { outputPreview } : {}),
|
|
2411
2415
|
...(errorPreview ? { errorPreview } : {}),
|
|
2412
2416
|
...(attempts && attempts.length > 0
|
|
@@ -39,25 +39,20 @@ const NODE_PURPOSE_BY_ID = {
|
|
|
39
39
|
"closeout-summary-pi": "把验证与交付事实写成简短交接摘要。",
|
|
40
40
|
|
|
41
41
|
"frontend-contract-pi": "把前端需求整理成可追踪契约:页面、状态、验收与边界。",
|
|
42
|
-
"frontend-contract-import-shell": "导入并固化前端实现契约,供后续设计、写入与验证节点使用。",
|
|
43
42
|
"frontend-scout-pi": "只读侦察前端结构、组件与接口用法,不改文件。",
|
|
44
43
|
"frontend-plan-pi": "选定实现与 Mock/API 策略,输出可执行的前端实现契约补丁。",
|
|
45
|
-
"frontend-design-policy-shell": "确定前端设计与 Mock/API 策略,冻结后续实现必须遵守的规则。",
|
|
46
44
|
"frontend-design-review-pi": "审查前端计划是否可写:通过则沿用原计划,否则要求修订。",
|
|
47
45
|
"frontend-plan-revision-pi": "按设计审查意见修订前端契约,不扩大范围。",
|
|
48
46
|
"frontend-final-design-review-pi": "复核修订后的前端计划,只有通过才能授权写入。",
|
|
49
47
|
"frontend-prewrite-gate-shell": "写入前门禁:核对契约、覆盖与 Mock 策略,通过才允许改前端。",
|
|
50
|
-
"frontend-writer-admission-shell": "核对契约、设计策略与 writeSet,满足准入条件后才允许 writer 写入。",
|
|
51
48
|
"frontend-lint-baseline-shell": "记录写入前的 lint 基线,供实现后对比,不阻断写入。",
|
|
52
49
|
"frontend-implement-pi": "按已批准契约改前端代码、状态与交互,留在 writeSet 内。",
|
|
53
50
|
"frontend-verify-assess-shell": "跑静态/行为/Mock 验证并评估能否有界修复。",
|
|
54
|
-
"frontend-verify-shell": "执行冻结的前端验证命令,产出可复查的通过或失败证据。",
|
|
55
51
|
"frontend-repair-pi": "只修评估为可修复的前端失败,不重开需求。",
|
|
56
52
|
"frontend-reverify-shell": "修复后用同一套冻结命令复验。",
|
|
57
53
|
"frontend-review-context-shell": "打包真实 diff 与有效验证证据,供审查节点使用。",
|
|
58
54
|
"frontend-review-pi": "对照契约审查前端实现与证据,给出通过或需修订。",
|
|
59
55
|
"frontend-review-gate-shell": "前端审查门禁:结论为通过才进入收口。",
|
|
60
|
-
"frontend-closeout-shell": "汇总实现、验证与遗留事项,形成确定性的前端收口事实。",
|
|
61
56
|
"frontend-closeout-pi": "汇总前端交付状态(含真实联调是否仍待完成)。",
|
|
62
57
|
"frontend-mock-assess-pi": "评估项目是否具备授权的 Mock 验证能力。",
|
|
63
58
|
"frontend-mock-contract-gate-shell": "Mock 契约门禁:策略与验证命令不一致则阻断。",
|
|
@@ -49,6 +49,7 @@ import {
|
|
|
49
49
|
formatSessionEventTime,
|
|
50
50
|
renderSessionTimeline,
|
|
51
51
|
buildPageStyledDoc,
|
|
52
|
+
fetchPageStylesheetText,
|
|
52
53
|
} from "./session-timeline.js";
|
|
53
54
|
import { createCustomSelect } from "../custom-select.js";
|
|
54
55
|
|
|
@@ -2366,13 +2367,7 @@ function appendProcessExportButton(content, body, meta) {
|
|
|
2366
2367
|
const btn = el("button", "btn btn-secondary", "导出 HTML");
|
|
2367
2368
|
btn.type = "button";
|
|
2368
2369
|
btn.addEventListener("click", async () => {
|
|
2369
|
-
|
|
2370
|
-
try {
|
|
2371
|
-
const res = await fetch("/inspect/styles.css", { cache: "no-cache" });
|
|
2372
|
-
if (res.ok) cssText = await res.text();
|
|
2373
|
-
} catch {
|
|
2374
|
-
/* 忽略:导出仍会用内联兜底样式 */
|
|
2375
|
-
}
|
|
2370
|
+
const cssText = await fetchPageStylesheetText();
|
|
2376
2371
|
// 复用页面真实渲染 DOM,保证导出排版与页面一致。
|
|
2377
2372
|
const clone = content.cloneNode(true);
|
|
2378
2373
|
const bar = clone.querySelector(".process-timeline-export-bar");
|
|
@@ -51,6 +51,27 @@ function downloadTextFile(filename, text) {
|
|
|
51
51
|
URL.revokeObjectURL(url);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
/** 抓取页面真实加载的样式表原文(导出内联用)。
|
|
55
|
+
* 不猜路径:直接取当前文档 <link rel="stylesheet"> 的 href(相对当前页面解析),
|
|
56
|
+
* 反代/子路径部署下也和页面渲染用的是同一份 CSS;fetch 失败时返回 "",
|
|
57
|
+
* 调用方回退 EXPORT_FALLBACK_CSS。 */
|
|
58
|
+
export async function fetchPageStylesheetText() {
|
|
59
|
+
try {
|
|
60
|
+
const links = [...document.querySelectorAll('link[rel="stylesheet"]')];
|
|
61
|
+
const texts = [];
|
|
62
|
+
for (const link of links) {
|
|
63
|
+
const href = link.getAttribute("href");
|
|
64
|
+
if (!href) continue;
|
|
65
|
+
const url = new URL(href, document.baseURI).href;
|
|
66
|
+
const res = await fetch(url, { cache: "no-cache" });
|
|
67
|
+
if (res.ok) texts.push(await res.text());
|
|
68
|
+
}
|
|
69
|
+
return texts.join("\n");
|
|
70
|
+
} catch {
|
|
71
|
+
return "";
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
54
75
|
/** 兜底样式:仅在无法抓取页面 /inspect/styles.css 时使用(与页面 process-timeline 排版对齐)。 */
|
|
55
76
|
const EXPORT_FALLBACK_CSS = `
|
|
56
77
|
:root{--canvas:#f7f7f4;--surface:#ffffff;--canvas-soft:#fafaf7;--surface-muted:#f0efe9;--ink:#26251e;--body:#5a5852;--muted:#807d72;--muted-soft:#a09c92;--hairline:#e6e5e0;--hairline-strong:#cfcdc4;--orange:#f54e00;--orange-active:#d04200;--blue:#3f6ea4;--green:#1f8a65;--amber:#a66a00;--red:#c72d53;--lavender:#e4daf1;--radius-sm:6px;--space-2:8px;--space-3:12px;--space-5:20px}
|
|
@@ -103,19 +124,31 @@ body{margin:0;font-family:-apple-system,"Segoe UI",Roboto,"PingFang SC","Microso
|
|
|
103
124
|
.markdown-output p,.markdown-output blockquote,.markdown-output ul,.markdown-output ol{margin:0;overflow-wrap:anywhere}
|
|
104
125
|
.markdown-output ul,.markdown-output ol{padding-left:22px}
|
|
105
126
|
.markdown-output blockquote{padding-left:12px}
|
|
127
|
+
.btn{display:inline-flex;align-items:center;gap:6px;min-height:28px;padding:0 12px;border:1px solid var(--hairline-strong);border-radius:var(--radius-sm);background:var(--surface);color:var(--body);font-size:12px;font-weight:600;cursor:pointer;user-select:none}
|
|
128
|
+
.btn-secondary{background:var(--surface-muted)}
|
|
129
|
+
.btn:hover{border-color:var(--orange);color:var(--orange-active)}
|
|
106
130
|
`;
|
|
107
131
|
|
|
108
132
|
const EXPORT_SHELL_CSS = `
|
|
109
|
-
body
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
133
|
+
/* 导出文档变量基础:页面样式把这些变量定义在 body[data-shell]/.inspect-workspace-root
|
|
134
|
+
* 下,独立导出的 HTML 既无该属性也无该包裹,变量会整体失效——深色系统的画布
|
|
135
|
+
* 兜底为黑底,叠加黑字后正文不可读。导出文档自带变量定义并强制 light 配色。 */
|
|
136
|
+
:root{color-scheme:light}
|
|
137
|
+
body{margin:0;font-family:-apple-system,"Segoe UI",Roboto,"PingFang SC","Microsoft YaHei",sans-serif;background:var(--canvas,#f7f7f4)}
|
|
138
|
+
.export-shell{max-width:1000px;margin:0 auto;padding:24px 28px;background:var(--surface,#ffffff);min-height:100vh;box-shadow:0 0 0 1px var(--hairline,#e6e5e0)}
|
|
139
|
+
.export-head h1{font-size:15px;color:var(--ink,#26251e);margin:0;font-weight:700}
|
|
140
|
+
.export-sub{color:var(--muted,#807d72);font-size:12px;margin:4px 0 16px}
|
|
113
141
|
.export-controls{display:flex;justify-content:flex-start;margin:0 0 12px}
|
|
114
|
-
.export-toggle{min-height:28px;padding:0 12px;border:1px solid var(--hairline-strong);border-radius:var(--radius-sm);background:var(--surface);color:var(--body);font-size:11px;font-weight:600;cursor:pointer;user-select:none}
|
|
115
|
-
.export-toggle:hover,.export-toggle:focus-visible{border-color:var(--orange);color:var(--orange-active);outline:none}
|
|
142
|
+
.export-toggle{min-height:28px;padding:0 12px;border:1px solid var(--hairline-strong,#cfcdc4);border-radius:var(--radius-sm,6px);background:var(--surface,#ffffff);color:var(--body,#5a5852);font-size:11px;font-weight:600;cursor:pointer;user-select:none}
|
|
143
|
+
.export-toggle:hover,.export-toggle:focus-visible{border-color:var(--orange,#f54e00);color:var(--orange-active,#d04200);outline:none}
|
|
116
144
|
body.hide-protocol .process-timeline-item.process-timeline-protocol{display:none}
|
|
117
145
|
`;
|
|
118
146
|
|
|
147
|
+
/** 导出文档自带的变量基础:与页面 styles.css 的 light 主题保持一致。
|
|
148
|
+
* 即使 fetch 到了页面 CSS,其变量也挂在 body[data-shell=…]/.inspect-workspace-root
|
|
149
|
+
* 选择器下而不生效,必须在导出文档内重新定义。 */
|
|
150
|
+
const EXPORT_ROOT_VARS_CSS = `:root{--canvas:#f7f7f4;--surface:#ffffff;--canvas-soft:#fafaf7;--surface-muted:#f0efe9;--surface-ink:#26251e;--ink:#26251e;--body:#5a5852;--muted:#807d72;--muted-soft:#a09c92;--hairline:#e6e5e0;--hairline-strong:#cfcdc4;--orange:#f54e00;--orange-active:#d04200;--blue:#3f6ea4;--green:#1f8a65;--amber:#a66a00;--red:#c72d53;--lavender:#e4daf1;--mint:#d8ead5;--peach:#f1d8cb;--gold:#d8b166;--radius-sm:6px;--radius-md:8px;--radius-lg:12px;--space-1:4px;--space-2:8px;--space-3:12px;--space-4:16px;--space-5:20px;--space-6:24px;--space-8:32px;--space-10:40px;--space-12:48px}`;
|
|
151
|
+
|
|
119
152
|
/**
|
|
120
153
|
* 把页面渲染出的时间线 innerHTML 封装成自包含 HTML。
|
|
121
154
|
* @param {string} bodyHTML 页面渲染的 .process-timeline 等真实 DOM 序列化结果
|
|
@@ -126,14 +159,18 @@ export function buildPageStyledDoc(bodyHTML, meta = {}, cssText = "") {
|
|
|
126
159
|
const now = new Date().toLocaleString();
|
|
127
160
|
const title = meta.nodeId ? `节点执行过程 · ${meta.nodeId}` : "节点执行过程";
|
|
128
161
|
const style = cssText.trim()
|
|
129
|
-
? `${cssText}\n\n/* 导出外壳 */\n${EXPORT_SHELL_CSS}`
|
|
162
|
+
? `${EXPORT_ROOT_VARS_CSS}\n${cssText}\n\n/* 导出外壳 */\n${EXPORT_SHELL_CSS}`
|
|
130
163
|
: `${EXPORT_FALLBACK_CSS}\n${EXPORT_SHELL_CSS}`;
|
|
131
164
|
// 有协议行时,在左上角提供一个「展开/隐藏原始协议事件」按钮(与页面一致),默认隐藏。
|
|
132
165
|
const hasProtocol = bodyHTML.includes("process-timeline-protocol");
|
|
133
166
|
const controls = hasProtocol
|
|
134
167
|
? `<div class="export-controls"><button type="button" id="exportToggleProtocol" class="export-toggle">展开原始协议事件</button></div><script>(function(){var b=document.body,t=document.getElementById('exportToggleProtocol'),on=false;function apply(){b.classList.toggle('hide-protocol',!on);t.textContent=on?'隐藏原始协议事件':'展开原始协议事件';}if(t){t.addEventListener('click',function(){on=!on;apply();});apply();}})();</script>`
|
|
135
168
|
: "";
|
|
136
|
-
|
|
169
|
+
// body 带 data-shell="observe-console":内联的页面 styles.css 把全部设计令牌
|
|
170
|
+
// (--canvas/--surface/--hairline-strong …) 与部分规则 (body[data-shell] …) 定义在
|
|
171
|
+
// 该作用域下,导出文档带上属性后令牌与前缀规则原生命中,与页面渲染一致。
|
|
172
|
+
// EXPORT_ROOT_VARS_CSS 仍保留:fetch 页面 CSS 失败的兜底路径令牌不缺。
|
|
173
|
+
return `<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>${escapeHtml(title)}</title><style>${style}</style></head><body data-shell="observe-console"><div class="export-shell"><div class="export-head"><h1>${escapeHtml(title)}</h1></div><p class="export-sub">DAG ${escapeHtml(meta.dagRunId ?? "")} · 导出于 ${escapeHtml(now)}</p>${controls}<div class="export-timeline">${bodyHTML}</div></div></body></html>`;
|
|
137
174
|
}
|
|
138
175
|
|
|
139
176
|
export function sessionEventResult(event) {
|
|
@@ -851,13 +888,7 @@ export function renderSessionTimeline(content, nodeId, options = {}) {
|
|
|
851
888
|
const exportBtn = el("button", "btn btn-secondary", "导出 HTML");
|
|
852
889
|
exportBtn.type = "button";
|
|
853
890
|
exportBtn.addEventListener("click", async () => {
|
|
854
|
-
|
|
855
|
-
try {
|
|
856
|
-
const res = await fetch("/inspect/styles.css", { cache: "no-cache" });
|
|
857
|
-
if (res.ok) cssText = await res.text();
|
|
858
|
-
} catch {
|
|
859
|
-
/* 忽略:导出仍会用内联兜底样式 */
|
|
860
|
-
}
|
|
891
|
+
const cssText = await fetchPageStylesheetText();
|
|
861
892
|
// 复用页面真实渲染(含原始协议事件),保证导出的排版与页面一致。
|
|
862
893
|
const prevShow = uiState.sessionTimelineShowProtocol;
|
|
863
894
|
uiState.sessionTimelineShowProtocol = true;
|
|
@@ -934,7 +934,7 @@ export async function materializeBackendTestResultFromPytestHtml(input) {
|
|
|
934
934
|
outcome = "command-error";
|
|
935
935
|
}
|
|
936
936
|
const commandSummary = input.commandSummary ??
|
|
937
|
-
`PYTHONUTF8=1 PYTHONIOENCODING=utf-8 PYTHONDONTWRITEBYTECODE=1 python -
|
|
937
|
+
`PYTHONUTF8=1 PYTHONIOENCODING=utf-8 PYTHONDONTWRITEBYTECODE=1 python reports/backend-test-pytest-launcher.py reports/backend-test-pytest-targets.txt ${htmlRelativePath} -> pytest.main(-v -p no:cacheprovider --self-contained-html)`;
|
|
938
938
|
assertNoSecrets("commandSummary", commandSummary);
|
|
939
939
|
const result = backendTestResultContractSchema.parse({
|
|
940
940
|
schemaVersion: 1,
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* its schemaId in the contract output registry. Imported once by the node
|
|
4
4
|
* executor; keeps per-contract imports out of the executor.
|
|
5
5
|
*/
|
|
6
|
-
import { validateFrontendContractNodeOutput, validateFrontendPlanPatchNodeOutput, } from "./frontend-implementation-contract.js";
|
|
6
|
+
import { validateFrontendContractNodeOutput, validateFrontendPlanPatchNodeOutput, validateFrontendRevisionPatchNodeOutput, } from "./frontend-implementation-contract.js";
|
|
7
7
|
import { registerStructuredContractValidator } from "./contract-output-registry.js";
|
|
8
8
|
registerStructuredContractValidator("frontend-implementation-contract-v1", validateFrontendContractNodeOutput);
|
|
9
9
|
registerStructuredContractValidator("frontend-implementation-contract-plan-patch-v1", validateFrontendPlanPatchNodeOutput);
|
|
10
|
+
registerStructuredContractValidator("frontend-implementation-contract-revision-patch-v1", validateFrontendRevisionPatchNodeOutput);
|