@tea-agent/loop-agent 0.39.0-beta.2 → 0.39.0-beta.3
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/AGENTS.md +4 -1
- package/CHANGELOG.md +187 -92
- package/README.md +9 -5
- package/bin/loop-agent.js +7 -3
- package/dist/application/task-lifecycle/recommendations.js +13 -2
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +16 -5
- package/dist/cli/program.js +16 -2
- package/dist/cli/update/notifier.js +2 -2
- package/dist/cli.js +2 -1
- package/dist/commands/dag-rerun.js +55 -1
- package/dist/commands/init-model-catalog.js +464 -0
- package/dist/commands/init-upgrade.js +265 -97
- package/dist/commands/init.js +429 -28
- package/dist/commands/inspect-next.js +8 -0
- package/dist/executors/dag-pi-executor.js +1645 -29
- package/dist/executors/pi-extension-resolver.js +233 -0
- package/dist/executors/pi-playwright-cli-tool.js +51 -12
- package/dist/executors/pi-sdk-executor.js +180 -33
- package/dist/executors/shell-executor.js +1150 -171
- package/dist/executors/shell-write-guard.js +14 -0
- package/dist/shared/dag-prompt-override.js +28 -0
- package/dist/shared/openspec-spec.js +70 -4
- package/dist/shared/operator/capabilities.js +32 -0
- package/dist/shared/playwright-cli-command-policy.js +15 -0
- package/dist/shared/update/console-notifier.js +100 -0
- package/dist/{cli → shared}/update/npm-client.js +41 -8
- package/dist/{cli → shared}/update/state.js +41 -13
- package/dist/task/config-types.js +19 -5
- package/dist/task/contract/project.js +3 -0
- package/dist/task/contract/schema.js +2 -1
- package/dist/task/frontend-project-capability.js +42 -16
- package/dist/task/source-prepare/parse-intent.js +24 -4
- package/dist/task/source-prepare/prepare.js +36 -0
- package/dist/worker/cli.js +31 -1
- package/dist/worker/console/chat/pi-console-config.js +81 -10
- package/dist/worker/console/chat/routes.js +1 -0
- package/dist/worker/console/dag-execution-receipt.js +33 -0
- package/dist/worker/console/frontend-human-decision-adapter.js +19 -0
- package/dist/worker/console/frontend-split-operation-adapter.js +20 -0
- package/dist/worker/console/index.js +3 -0
- package/dist/worker/console/operation-store.js +226 -6
- package/dist/worker/console/operator-actions.js +263 -0
- package/dist/worker/console/operator-user-error.js +2 -2
- package/dist/worker/console/routes.js +43 -2
- package/dist/worker/console/security.js +63 -9
- package/dist/worker/console/server.js +151 -6
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-C4kVYweA.js → abnfDiagram-N423BO3Z-zsNyvGaH.js} +1 -1
- package/dist/worker/console/static/assets/{arc-Bj2M8iO5.js → arc-BDjZ5kE1.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-CLlXe4-6.js → architectureDiagram-T3A2C74G-BkgaujnN.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-psEp5xH0.js → blockDiagram-VBNYF7ZC-CqxOJi0j.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-DeKcOCGJ.js → c4Diagram-5PPSVZJV-C2JHLdRi.js} +1 -1
- package/dist/worker/console/static/assets/channel-BuqaGKAT.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-K0C744p1.js → chunk-2GRJ4B5K-DF_YSvOS.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-xbNw4J9-.js → chunk-2Q5K7J3B-D3Ort5Vo.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-DjLaSDUO.js → chunk-5RXB4S5H-XfAfZeUC.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-CtiPlKel.js → chunk-5VM5RSS4-SLBibhf5.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DZtolir7.js → chunk-6Q2QTUOP-Dz29Ye_f.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CPcdNeew.js → chunk-GF5L2VYU-DB2G4H8V.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-NgHc6V37.js → chunk-JWPE2WC7-CIoGkxdu.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-DQ_T-hg8.js → chunk-KBJHAD2P-CG78FGkn.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DgLsxcVP.js → chunk-RYQCIY6F-ZDHAvXPd.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-UrVoM9z1.js → chunk-XXDRQBXY-BKkkC4VA.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DNdSHjH9.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DNdSHjH9.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-CLkYvTb3.js → cose-bilkent-JH36ORCC-SposUZsO.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BwT_xKrE.js → cynefin-VYW2F7L2-BSCtQpD7.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-Bi9tuzif.js → cynefinDiagram-MW4NZA55-DgFHrw9l.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-BCIqkWBV.js → dagre-VZM6K2ZE-BVBasYbj.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-Dip6l9_Z.js → diagram-7IWD3JNH-6r_4rN_c.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-B-xkh_wK.js → diagram-B4RE2ZJO-JmuaB41P.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-DZO0kTt_.js → diagram-LBJQPF4R-xXvAY3sk.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-D6ZplNbZ.js → diagram-Q27KOJAE-D2B3lY2o.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-BPekbhoS.js → diagram-UB23O5K3-tK54tMEX.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-BtmaJpK5.js → ebnfDiagram-BXEA7PRR-CNtELzV-.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-6DS0Xc44.js → erDiagram-JOGREHBK-C1xKMKcW.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-CihnROSm.js → flowDiagram-UKHOOZJN-CZ7dlPMz.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-CZC4zOE2.js → ganttDiagram-PKOTCBZU-BqGb42Xe.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Dq1fhzGN.js → gitGraphDiagram-DS77QQ5N-D_BlXEtt.js} +1 -1
- package/dist/worker/console/static/assets/{index-DTZOKgAn.js → index-D5zGX6fG.js} +68 -68
- package/dist/worker/console/static/assets/index-DBbhESQ_.css +1 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DlSl4BGx.js → infoDiagram-6WML65LV-Vce2vmIe.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-snnzFl_U.js → ishikawaDiagram-WSZJBQD7-DDwVBAvI.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-BDE7qpMx.js → journeyDiagram-NVQOT4AX-y_Ew-wv4.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-CD2Ci04G.js → kanban-definition-27J2QSJJ-CjXJFp1K.js} +1 -1
- package/dist/worker/console/static/assets/{linear-DdnapKIH.js → linear-1itMst1Q.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-BVjAT9b8.js → mermaid.core-C-D3ZRUa.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-D0yJk-zA.js → mindmap-definition-FAOFIHXS-BiYywmZc.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-BVo9tFP-.js → pegDiagram-VL7TDLO6-l2fydNWA.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-DnOV-mZ_.js → pieDiagram-7S7Q4E2Y-Dz0hNbYW.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-CzUJo58i.js → quadrantDiagram-CIZ2JOQS-BujI_mLZ.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-Bvikrolh.js → railroadDiagram-AXF67PYL-pWe84beb.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-DtaSVCap.js → requirementDiagram-LRYGKXZP-ByYu2frg.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-C2c9A0wW.js → sankeyDiagram-W5VNT64P-CoPnilFP.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-DmXJcU7r.js → sequenceDiagram-SI44F4Z6-1Md2LnS0.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-B4GUFW92.js → sizeCapture-X5ZJPWSS-DqqqAoSH.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-Dsad2MXf.js → stateDiagram-OKZ733FA-C4sGkf--.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-B8rmop_9.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-DGq48Fbi.js → swimlanes-SLNWSIFB-ByxJrUzW.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-B0ky-kBM.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CSSi6OFf.js → timeline-definition-Z64GVDOM-D8MLgM0O.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-BqyzPrWv.js → vennDiagram-T6HMQDX7-DnBis-wZ.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-DlznVb6S.js → wardleyDiagram-T6FBY63Y-uuyYT7UO.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CfBcgJ_K.js → xychartDiagram-ELKLHX3M-BjgabhwK.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/active-run-badge.js +17 -0
- package/dist/worker/console/static-src/operator-chat/mutation-gate.js +1 -1
- package/dist/worker/loop-agent/loop-agent-client.js +7 -3
- package/dist/worker/materialize/frontend-split-task-materializer.js +72 -0
- package/dist/worker/observe/node-input.js +74 -3
- package/dist/worker/observe/node-process.js +377 -0
- package/dist/worker/observe/routes.js +36 -3
- package/dist/worker/observe/shell-handler-keys.js +34 -0
- package/dist/worker/observe/static/api.js +66 -0
- package/dist/worker/observe/static/constants.js +8 -1
- package/dist/worker/observe/static/dag-history-labels.js +4 -0
- package/dist/worker/observe/static/format.js +23 -0
- package/dist/worker/observe/static/markdown-render.js +20 -1
- package/dist/worker/observe/static/operator-chrome.js +4 -0
- package/dist/worker/observe/static/state.js +13 -2
- package/dist/worker/observe/static/styles.css +73 -0
- package/dist/worker/observe/static/views/dag-inspector.js +612 -8
- package/dist/worker/observe/static/views/dag.js +8 -1
- package/dist/worker/observe/static/views/failures.js +5 -2
- package/dist/worker/observe/static/views/session-timeline.js +78 -9
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +20 -2
- package/dist/workflows/dag/contract-output-registry.js +15 -0
- package/dist/workflows/dag/contract-validator-registrations.js +1 -2
- package/dist/workflows/dag/failure-category.js +12 -0
- package/dist/workflows/dag/frontend-closeout.js +221 -0
- package/dist/workflows/dag/frontend-design-policy.js +347 -0
- package/dist/workflows/dag/frontend-human-decision.js +182 -0
- package/dist/workflows/dag/frontend-implementation-contract.js +378 -160
- package/dist/workflows/dag/frontend-prewrite-gate.js +258 -393
- package/dist/workflows/dag/frontend-provider-capability-matrix.js +159 -0
- package/dist/workflows/dag/frontend-recovery-capsule.js +455 -0
- package/dist/workflows/dag/frontend-recovery-controller.js +201 -0
- package/dist/workflows/dag/frontend-recovery-lineage.js +178 -0
- package/dist/workflows/dag/frontend-recovery-plan.js +17 -10
- package/dist/workflows/dag/frontend-recovery-run.js +33 -20
- package/dist/workflows/dag/frontend-repair.js +7 -424
- package/dist/workflows/dag/frontend-review-context.js +195 -8
- package/dist/workflows/dag/frontend-risk.js +15 -2
- package/dist/workflows/dag/frontend-shadow-dual-write.js +665 -0
- package/dist/workflows/dag/frontend-shape-capsule-store.js +191 -0
- package/dist/workflows/dag/frontend-shape-facts.js +409 -0
- package/dist/workflows/dag/frontend-shape.js +425 -0
- package/dist/workflows/dag/frontend-split-application-service.js +203 -0
- package/dist/workflows/dag/frontend-split-orchestrator.js +899 -0
- package/dist/workflows/dag/frontend-test-case-checklist.js +26 -2
- package/dist/workflows/dag/frontend-test-case-manifest.js +5 -0
- package/dist/workflows/dag/frontend-test-case-quality.js +5 -13
- package/dist/workflows/dag/frontend-test-environment-probe.js +227 -0
- package/dist/workflows/dag/frontend-test-markdown.js +61 -0
- package/dist/workflows/dag/frontend-test-result-contract.js +160 -28
- package/dist/workflows/dag/frontend-test-standard-scenarios.js +68 -0
- package/dist/workflows/dag/frontend-typed-event-store.js +438 -0
- package/dist/workflows/dag/frontend-typed-event-transaction.js +180 -0
- package/dist/workflows/dag/frontend-verification-trace.js +52 -17
- package/dist/workflows/dag/frontend-worktree-diff.js +97 -18
- package/dist/workflows/dag/frontend-writer-admission.js +260 -0
- package/dist/workflows/dag/frontend-writer-status.js +244 -0
- package/dist/workflows/dag/init-hybrid.js +572 -500
- package/dist/workflows/dag/node-execution.js +307 -70
- package/dist/workflows/dag/prompt.js +10 -18
- package/dist/workflows/dag/rerun-plan.js +144 -5
- package/dist/workflows/dag/rerun-run.js +58 -6
- package/dist/workflows/dag/retry-policy.js +228 -4
- package/dist/workflows/dag/runner.js +314 -119
- package/dist/workflows/dag/scheduler.js +114 -16
- package/dist/workflows/dag/structured-output-repair.js +712 -0
- package/dist/workflows/dag/types.js +235 -23
- package/dist/workflows/dag/upstream-artifacts.js +0 -4
- package/dist/workflows/dag/validate.js +60 -10
- package/docs/init-surface.manifest.json +5 -0
- package/docs/operations/README.md +1 -1
- package/docs/templates/agent-dag.schema.json +44 -0
- package/docs/templates/frontend-test-dag.json +8 -10
- package/docs/templates/frontend-test-dag.retrieve-context.prompt.md +1 -1
- package/docs/templates/init-managed-agents.md +13 -3
- package/harness.json +3 -3
- package/package.json +5 -1
- package/scripts/next-info.mjs +356 -0
- package/scripts/next-publish-gate.mjs +238 -0
- package/scripts/release-source-binding.mjs +251 -0
- package/skills/codebase-scout/SKILL.md +1 -1
- package/skills/fe-test-ui-scout/SKILL.md +65 -0
- package/skills/fe-test-ui-scout/references/ledger-schema.md +62 -0
- package/skills/fe-test-ui-scout/references/recon-protocol.md +54 -0
- package/skills/frontend-implementation/SKILL.md +4 -4
- package/skills/frontend-implementation/references/design-spec.md +11 -7
- package/skills/frontend-implementation/references/node-contracts.md +20 -11
- package/skills/loop-agent/references/command-reference.md +11 -4
- package/skills/loop-agent/references/hybrid-dag.md +1 -1
- package/skills/playwright-cli/SKILL.md +22 -0
- package/skills/playwright-cli-case-generator/SKILL.md +20 -1
- package/dist/worker/console/static/assets/channel-CPF4N7pf.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-C2DwA_Y1.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-C2DwA_Y1.js +0 -1
- package/dist/worker/console/static/assets/index-Ya5FE7cD.css +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-B8FB_cNk.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-CrZYaWfx.js +0 -8
|
@@ -90,6 +90,7 @@ import {
|
|
|
90
90
|
closeDagInspector,
|
|
91
91
|
selectDagNode,
|
|
92
92
|
renderDagInspector,
|
|
93
|
+
mergeNodeProcess,
|
|
93
94
|
} from "./dag-inspector.js";
|
|
94
95
|
import { mergeSessionEvents } from "./session-timeline.js";
|
|
95
96
|
|
|
@@ -451,13 +452,19 @@ export async function renderDagDetail(dagRunId, initial = true) {
|
|
|
451
452
|
nodesEl.scrollLeft = nodesScrollLeft;
|
|
452
453
|
|
|
453
454
|
if (uiState.dagInspectorOpen && uiState.selectedDagNodeId) {
|
|
455
|
+
const selectedNode = nodes.find(
|
|
456
|
+
(node) => node.nodeId === uiState.selectedDagNodeId,
|
|
457
|
+
);
|
|
454
458
|
if (!uiState.sessionEventIdentity) {
|
|
455
459
|
uiState.sessionEventIdentity = makeSessionEventIdentity(
|
|
456
460
|
dagRunId,
|
|
457
461
|
uiState.selectedDagNodeId,
|
|
458
462
|
);
|
|
459
463
|
}
|
|
460
|
-
await
|
|
464
|
+
await Promise.all([
|
|
465
|
+
mergeSessionEvents(dagRunId, uiState.selectedDagNodeId),
|
|
466
|
+
selectedNode ? mergeNodeProcess(dagRunId, selectedNode) : Promise.resolve(),
|
|
467
|
+
]);
|
|
461
468
|
}
|
|
462
469
|
renderDagInspector(
|
|
463
470
|
dagRunId,
|
|
@@ -101,11 +101,14 @@ export async function renderFailures() {
|
|
|
101
101
|
|
|
102
102
|
if (tasks.length === 0) {
|
|
103
103
|
clearNode(listEl);
|
|
104
|
+
// 2026-08-22 (B-FIND-02): 空态文案与静态 #failures-empty / 页面标题
|
|
105
|
+
// 「异常 Task」保持一致;旧文案「当前没有失败或阻塞的 Task。」与静态
|
|
106
|
+
// HTML 及回归目录 AC-FE-INS-004 漂移,导致 frontend-test 字面 find 0 命中。
|
|
104
107
|
if (emptyEl) {
|
|
105
108
|
emptyEl.hidden = false;
|
|
106
|
-
mountViewState(emptyEl, "empty", "
|
|
109
|
+
mountViewState(emptyEl, "empty", "当前没有异常 Task");
|
|
107
110
|
} else {
|
|
108
|
-
mountViewState(listEl, "empty", "
|
|
111
|
+
mountViewState(listEl, "empty", "当前没有异常 Task");
|
|
109
112
|
}
|
|
110
113
|
return;
|
|
111
114
|
}
|
|
@@ -250,6 +250,54 @@ export function buildSessionTimelineItems(events) {
|
|
|
250
250
|
return items;
|
|
251
251
|
}
|
|
252
252
|
|
|
253
|
+
/**
|
|
254
|
+
* Split a node's shared session-events log into per-attempt segments. Every
|
|
255
|
+
* attempt appends one agent run beginning with agent_start; events recorded
|
|
256
|
+
* before the first agent_start (defensive) stay in an implicit first segment.
|
|
257
|
+
*/
|
|
258
|
+
export function splitSessionEventsByAttempt(events) {
|
|
259
|
+
const segments = [];
|
|
260
|
+
let current = null;
|
|
261
|
+
for (const event of events) {
|
|
262
|
+
if (typeof event?.type === "string" && event.type === "agent_start") {
|
|
263
|
+
current = [];
|
|
264
|
+
segments.push(current);
|
|
265
|
+
}
|
|
266
|
+
if (current === null) {
|
|
267
|
+
current = [];
|
|
268
|
+
segments.push(current);
|
|
269
|
+
}
|
|
270
|
+
current.push(event);
|
|
271
|
+
}
|
|
272
|
+
return segments;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Resolve the events visible in the「执行过程」tab for the selected execution
|
|
277
|
+
* key. attempt-N shows only that attempt's segment when the shared log carries
|
|
278
|
+
* multiple attempts; other keys (pass-N, null) keep the full stream.
|
|
279
|
+
*/
|
|
280
|
+
export function resolveTimelineVisibleEvents(events, executionKey) {
|
|
281
|
+
const match =
|
|
282
|
+
typeof executionKey === "string"
|
|
283
|
+
? /^attempt-(\d+)$/.exec(executionKey)
|
|
284
|
+
: null;
|
|
285
|
+
if (!match) return { events, note: null };
|
|
286
|
+
const segments = splitSessionEventsByAttempt(events);
|
|
287
|
+
if (segments.length <= 1) return { events, note: null };
|
|
288
|
+
const requested = Number(match[1]);
|
|
289
|
+
if (requested >= 1 && requested <= segments.length) {
|
|
290
|
+
return {
|
|
291
|
+
events: segments[requested - 1],
|
|
292
|
+
note: `已按第 ${requested} 次执行过滤 · 共 ${segments.length} 次执行`,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
return {
|
|
296
|
+
events,
|
|
297
|
+
note: `未检测到第 ${requested} 次执行的边界(共 ${segments.length} 次执行),暂显示全部事件`,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
|
|
253
301
|
/**
|
|
254
302
|
* Build rich timeline items: merge tool start/end by toolCallId, collapse
|
|
255
303
|
* protocol noise, surface turn thinking/text without dumping agent_end.messages.
|
|
@@ -439,12 +487,25 @@ function ensureOpenDetailsSet() {
|
|
|
439
487
|
return uiState.sessionTimelineOpenDetails;
|
|
440
488
|
}
|
|
441
489
|
|
|
442
|
-
function
|
|
490
|
+
function closedDetailKey(detailKey) {
|
|
491
|
+
return `${detailKey}::user-closed`;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
function bindDetailsToggle(details, detailKey, defaultOpen = false) {
|
|
443
495
|
const openSet = ensureOpenDetailsSet();
|
|
444
|
-
if (
|
|
496
|
+
if (defaultOpen) {
|
|
497
|
+
if (!openSet.has(closedDetailKey(detailKey))) details.open = true;
|
|
498
|
+
} else if (openSet.has(detailKey)) {
|
|
499
|
+
details.open = true;
|
|
500
|
+
}
|
|
445
501
|
details.addEventListener("toggle", () => {
|
|
446
|
-
if (details.open)
|
|
447
|
-
|
|
502
|
+
if (details.open) {
|
|
503
|
+
openSet.add(detailKey);
|
|
504
|
+
openSet.delete(closedDetailKey(detailKey));
|
|
505
|
+
} else {
|
|
506
|
+
openSet.delete(detailKey);
|
|
507
|
+
openSet.add(closedDetailKey(detailKey));
|
|
508
|
+
}
|
|
448
509
|
});
|
|
449
510
|
}
|
|
450
511
|
|
|
@@ -599,7 +660,7 @@ function renderProtocolCard(item) {
|
|
|
599
660
|
const details = document.createElement("details");
|
|
600
661
|
details.className = "process-timeline-details";
|
|
601
662
|
details.dataset.detailKey = item.detailKey;
|
|
602
|
-
bindDetailsToggle(details, item.detailKey);
|
|
663
|
+
bindDetailsToggle(details, item.detailKey, true);
|
|
603
664
|
const summary = document.createElement("summary");
|
|
604
665
|
summary.className = "process-timeline-summary";
|
|
605
666
|
summary.textContent = `消息收发 · ${item.protocolCount} 条协议事件`;
|
|
@@ -644,7 +705,7 @@ export async function mergeSessionEvents(dagRunId, nodeId) {
|
|
|
644
705
|
}
|
|
645
706
|
}
|
|
646
707
|
|
|
647
|
-
export function renderSessionTimeline(content, nodeId) {
|
|
708
|
+
export function renderSessionTimeline(content, nodeId, options = {}) {
|
|
648
709
|
content.dataset.dagRunId = uiState.currentDagRunId ?? "";
|
|
649
710
|
content.dataset.nodeId = nodeId ?? "";
|
|
650
711
|
content.classList.add("process-timeline-scroll");
|
|
@@ -653,6 +714,14 @@ export function renderSessionTimeline(content, nodeId) {
|
|
|
653
714
|
return;
|
|
654
715
|
}
|
|
655
716
|
|
|
717
|
+
const visible = resolveTimelineVisibleEvents(
|
|
718
|
+
uiState.sessionEvents,
|
|
719
|
+
options.executionKey,
|
|
720
|
+
);
|
|
721
|
+
if (visible.note) {
|
|
722
|
+
content.appendChild(el("p", "process-timeline-note", visible.note));
|
|
723
|
+
}
|
|
724
|
+
|
|
656
725
|
const showProtocol = Boolean(uiState.sessionTimelineShowProtocol);
|
|
657
726
|
const toolbar = el("div", "process-timeline-toolbar");
|
|
658
727
|
const protocolToggle = document.createElement("button");
|
|
@@ -668,17 +737,17 @@ export function renderSessionTimeline(content, nodeId) {
|
|
|
668
737
|
const scrollTop = parent.scrollTop;
|
|
669
738
|
const scrollLeft = parent.scrollLeft;
|
|
670
739
|
while (parent.firstChild) parent.removeChild(parent.firstChild);
|
|
671
|
-
renderSessionTimeline(parent, nodeId);
|
|
740
|
+
renderSessionTimeline(parent, nodeId, options);
|
|
672
741
|
parent.scrollTop = scrollTop;
|
|
673
742
|
parent.scrollLeft = scrollLeft;
|
|
674
743
|
});
|
|
675
744
|
toolbar.appendChild(protocolToggle);
|
|
676
745
|
content.appendChild(toolbar);
|
|
677
746
|
|
|
678
|
-
const timelineItems = buildRichTimelineItems(
|
|
747
|
+
const timelineItems = buildRichTimelineItems(visible.events, {
|
|
679
748
|
showProtocol,
|
|
680
749
|
});
|
|
681
|
-
const hasEventTimes =
|
|
750
|
+
const hasEventTimes = visible.events.some((event) =>
|
|
682
751
|
Boolean(sessionEventTimestamp(event)),
|
|
683
752
|
);
|
|
684
753
|
if (!hasEventTimes) {
|
|
@@ -1500,8 +1500,26 @@ print(json.dumps(out,ensure_ascii=True))
|
|
|
1500
1500
|
// Windows Python may expose stdin as GBK; decoding generated Chinese/emoji
|
|
1501
1501
|
// source through that locale can create surrogateescape code points and make
|
|
1502
1502
|
// ast.parse fail with "surrogates not allowed".
|
|
1503
|
-
|
|
1504
|
-
|
|
1503
|
+
// POSIX images usually expose only `python3` (modern macOS has no `python`);
|
|
1504
|
+
// Windows often has a real `python` plus a Microsoft Store `python3` stub.
|
|
1505
|
+
// A missing interpreter must not silently degrade to empty payload shapes.
|
|
1506
|
+
const interpreterCandidates = process.platform === "win32" ? ["python", "python3"] : ["python3", "python"];
|
|
1507
|
+
let result;
|
|
1508
|
+
for (const interpreter of interpreterCandidates) {
|
|
1509
|
+
const attempt = spawnSync(interpreter, ["-c", program], {
|
|
1510
|
+
input: Buffer.from(source, "utf8"),
|
|
1511
|
+
encoding: "utf8",
|
|
1512
|
+
maxBuffer: 4 * 1024 * 1024,
|
|
1513
|
+
windowsHide: true,
|
|
1514
|
+
});
|
|
1515
|
+
// Interpreter missing/unspawnable → try the next candidate. A spawned
|
|
1516
|
+
// interpreter that exits nonzero analyzed real input; do not retry then.
|
|
1517
|
+
if (attempt.error)
|
|
1518
|
+
continue;
|
|
1519
|
+
result = attempt;
|
|
1520
|
+
break;
|
|
1521
|
+
}
|
|
1522
|
+
if (!result || result.status !== 0 || !result.stdout.trim())
|
|
1505
1523
|
return new Map();
|
|
1506
1524
|
try {
|
|
1507
1525
|
const parsed = JSON.parse(result.stdout);
|
|
@@ -1,4 +1,19 @@
|
|
|
1
1
|
import { structuredContractOutputSchemaIds, } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Registry of node-level structured contract validators, keyed by the
|
|
4
|
+
* `structuredContractOutput.schemaId` declared on a DAG task.
|
|
5
|
+
*
|
|
6
|
+
* The executor looks the validator up here instead of hardcoding per-contract
|
|
7
|
+
* imports, so new contracts (backend-test manifests, coverage contracts, ...)
|
|
8
|
+
* can opt into producing-node self-validation without touching the executor.
|
|
9
|
+
*/
|
|
10
|
+
export const structuredContractValidationClassifications = [
|
|
11
|
+
"format",
|
|
12
|
+
"schema",
|
|
13
|
+
"truncated",
|
|
14
|
+
"semantic-repair",
|
|
15
|
+
"governance-blocked",
|
|
16
|
+
];
|
|
2
17
|
const validators = new Map();
|
|
3
18
|
export function registerStructuredContractValidator(schemaId, validator) {
|
|
4
19
|
if (validators.has(schemaId)) {
|
|
@@ -3,8 +3,7 @@
|
|
|
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,
|
|
6
|
+
import { validateFrontendContractNodeOutput, validateFrontendPlanPatchNodeOutput, } 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);
|
|
@@ -29,8 +29,13 @@ const RAW_TO_NORMALIZED = {
|
|
|
29
29
|
"decision-envelope-invalid": "decision-envelope",
|
|
30
30
|
"nonzero-exit": "shell-command",
|
|
31
31
|
"spawn-error": "shell-command",
|
|
32
|
+
"test-timeout": "timeout",
|
|
32
33
|
"static-error": "static-error",
|
|
33
34
|
"invalid-output": "validation",
|
|
35
|
+
"structured-output-truncated": "validation",
|
|
36
|
+
"output-too-large": "validation",
|
|
37
|
+
"structured-repair-exhausted": "validation",
|
|
38
|
+
"governance-blocked": "validation",
|
|
34
39
|
"path-violation": "validation",
|
|
35
40
|
"empty-output": "validation",
|
|
36
41
|
"verify-failure": "validation",
|
|
@@ -40,6 +45,13 @@ const RAW_TO_NORMALIZED = {
|
|
|
40
45
|
quota: "executor",
|
|
41
46
|
"rate-limit": "executor",
|
|
42
47
|
unavailable: "executor",
|
|
48
|
+
// Deterministic capability/policy refusal: missing verified launcher JS
|
|
49
|
+
// entry, capability allowlist rejection, or missing bounded write tool
|
|
50
|
+
// profile. These are environment/config defects the operator repairs
|
|
51
|
+
// before rerunning; classify as executor so recovery recommends
|
|
52
|
+
// rerun-after-fix WITHOUT forcing a human-decision gate on
|
|
53
|
+
// standaloneTaskRerun (run-failed same-task rerun).
|
|
54
|
+
"tool-policy": "executor",
|
|
43
55
|
unknown: "unknown",
|
|
44
56
|
};
|
|
45
57
|
function isFinishedStatus(status) {
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export { buildFrontendShapeTransitionCapsule, computeFrontendShapeSourceDigest, evaluateFrontendShapeEscalation, FRONTEND_SHAPE_NODE_COUNTS, frontendShapeTransitionCapsuleSchema, frontendShapeTransitionReshapeCapsuleSchema, frontendShapeTransitionSplitCapsuleSchema, parseFrontendShapeTransitionCapsule, resolveFrontendTaskShape, } from "./frontend-shape.js";
|
|
3
|
+
/**
|
|
4
|
+
* Deterministic frontend closeout facts + renderer (M7).
|
|
5
|
+
*
|
|
6
|
+
* The functions here are pure (no I/O, no model, no side effects). The closeout
|
|
7
|
+
* renderer reads only committed facts, never model summaries or node prose, so
|
|
8
|
+
* a weak conclusion (failed / not-run / baseline-debt / mock-backed / pending)
|
|
9
|
+
* can never be rewritten into a stronger one (passed / real-integrated).
|
|
10
|
+
*
|
|
11
|
+
* Canonical shape routing and transition capsules live in `frontend-shape.ts`
|
|
12
|
+
* and are re-exported here for callers that also consume closeout contracts.
|
|
13
|
+
*/
|
|
14
|
+
export const FRONTEND_CLOSEOUT_FACTS_SCHEMA_ID = "frontend-closeout-facts-v1";
|
|
15
|
+
/* ------------------------------------------------------------------ *
|
|
16
|
+
* Enums + facts schema *
|
|
17
|
+
* ------------------------------------------------------------------ */
|
|
18
|
+
export const frontendCoverageStatusSchema = z.enum([
|
|
19
|
+
"passed",
|
|
20
|
+
"failed",
|
|
21
|
+
"not-run",
|
|
22
|
+
"blocked",
|
|
23
|
+
"unavailable",
|
|
24
|
+
]);
|
|
25
|
+
export const frontendLintStatusSchema = z.enum([
|
|
26
|
+
"passed",
|
|
27
|
+
"baseline-debt",
|
|
28
|
+
"failed",
|
|
29
|
+
"unavailable",
|
|
30
|
+
]);
|
|
31
|
+
export const frontendIntegrationFactsSchema = z.enum([
|
|
32
|
+
"real-api",
|
|
33
|
+
"mock-backed",
|
|
34
|
+
"local-no-remote",
|
|
35
|
+
"unavailable",
|
|
36
|
+
]);
|
|
37
|
+
export const frontendCloseoutFactsSchema = z
|
|
38
|
+
.object({
|
|
39
|
+
schemaVersion: z.literal(1),
|
|
40
|
+
schemaId: z.literal(FRONTEND_CLOSEOUT_FACTS_SCHEMA_ID),
|
|
41
|
+
coverageMatrix: z.array(z
|
|
42
|
+
.object({
|
|
43
|
+
verificationTargetId: z.string().min(1),
|
|
44
|
+
status: frontendCoverageStatusSchema,
|
|
45
|
+
})
|
|
46
|
+
.strict()),
|
|
47
|
+
lintStatus: frontendLintStatusSchema,
|
|
48
|
+
integrationFacts: frontendIntegrationFactsSchema,
|
|
49
|
+
reviewApproval: z
|
|
50
|
+
.object({
|
|
51
|
+
typedVerdict: z
|
|
52
|
+
.enum(["approve_review", "request_review_changes"])
|
|
53
|
+
.nullable(),
|
|
54
|
+
legacyVerdict: z.enum(["pass", "request-revision"]).nullable(),
|
|
55
|
+
match: z.boolean(),
|
|
56
|
+
})
|
|
57
|
+
.strict(),
|
|
58
|
+
cumulativeDiff: z
|
|
59
|
+
.object({
|
|
60
|
+
cumulativeDigest: z.string().min(1),
|
|
61
|
+
empty: z.boolean(),
|
|
62
|
+
})
|
|
63
|
+
.strict(),
|
|
64
|
+
notRun: z
|
|
65
|
+
.object({
|
|
66
|
+
browser: z.literal("not-run"),
|
|
67
|
+
visual: z.literal("not-run"),
|
|
68
|
+
})
|
|
69
|
+
.strict(),
|
|
70
|
+
pendingRealIntegration: z.boolean(),
|
|
71
|
+
residualRisks: z.array(z.string().min(1)).optional(),
|
|
72
|
+
evidenceGaps: z.array(z.string().min(1)).optional(),
|
|
73
|
+
})
|
|
74
|
+
.strict();
|
|
75
|
+
/* ------------------------------------------------------------------ *
|
|
76
|
+
* FrontendTestHandoff (A+B AC-010) *
|
|
77
|
+
* ------------------------------------------------------------------ */
|
|
78
|
+
export const FRONTEND_TEST_HANDOFF_SCHEMA_ID = "frontend-test-handoff-v1";
|
|
79
|
+
/**
|
|
80
|
+
* A+B (AC-010): deterministic frontend-test handoff. Bound to the task id,
|
|
81
|
+
* source digest, and candidate hash; browser/visual/runtime-a11y are always
|
|
82
|
+
* `not-run` and the handoff is always `deferred-to-frontend-test`, so a weak
|
|
83
|
+
* closeout can never block implementation and can never be rewritten as
|
|
84
|
+
* passed.
|
|
85
|
+
*/
|
|
86
|
+
export const frontendTestHandoffSchema = z
|
|
87
|
+
.object({
|
|
88
|
+
schemaVersion: z.literal(1),
|
|
89
|
+
schemaId: z.literal(FRONTEND_TEST_HANDOFF_SCHEMA_ID),
|
|
90
|
+
taskId: z.string().min(1),
|
|
91
|
+
sourceDigest: z.string().regex(/^[a-f0-9]{64}$/),
|
|
92
|
+
candidateHash: z.string().regex(/^[a-f0-9]{64}$/),
|
|
93
|
+
executionStatus: z.literal("not-run"),
|
|
94
|
+
handoffStatus: z.literal("deferred-to-frontend-test"),
|
|
95
|
+
nonBlocking: z.literal(true),
|
|
96
|
+
})
|
|
97
|
+
.strict();
|
|
98
|
+
export function buildFrontendTestHandoff(input) {
|
|
99
|
+
return frontendTestHandoffSchema.parse({
|
|
100
|
+
schemaVersion: 1,
|
|
101
|
+
schemaId: FRONTEND_TEST_HANDOFF_SCHEMA_ID,
|
|
102
|
+
taskId: input.taskId,
|
|
103
|
+
sourceDigest: input.sourceDigest,
|
|
104
|
+
candidateHash: input.candidateHash,
|
|
105
|
+
executionStatus: "not-run",
|
|
106
|
+
handoffStatus: "deferred-to-frontend-test",
|
|
107
|
+
nonBlocking: true,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Deterministic, fail-closed mapping from structured evidence to integration
|
|
112
|
+
* facts. Never reads model text. `real-api` is the only value that may report
|
|
113
|
+
* real integration, and it requires both real passed evidence and satisfied
|
|
114
|
+
* required verification evidence. Anything else downgrades to pending.
|
|
115
|
+
*/
|
|
116
|
+
export function deriveFrontendIntegrationFacts(input) {
|
|
117
|
+
const requiredEvidenceSatisfied = input.verificationTargets.length > 0 &&
|
|
118
|
+
input.verificationTargets.every((target) => target.status === "ok");
|
|
119
|
+
if (input.realApiEvidence.present &&
|
|
120
|
+
input.realApiEvidence.passed &&
|
|
121
|
+
requiredEvidenceSatisfied) {
|
|
122
|
+
return "real-api";
|
|
123
|
+
}
|
|
124
|
+
// Required evidence not satisfied → never report real-integrated or
|
|
125
|
+
// mock/locally-validated; evidence is unavailable.
|
|
126
|
+
if (!requiredEvidenceSatisfied) {
|
|
127
|
+
return "unavailable";
|
|
128
|
+
}
|
|
129
|
+
if (input.mockStrategy !== "not-needed" && input.mockEvidenceBound) {
|
|
130
|
+
return "mock-backed";
|
|
131
|
+
}
|
|
132
|
+
if (input.mockStrategy === "not-needed" && !input.realApiEvidence.present) {
|
|
133
|
+
return "local-no-remote";
|
|
134
|
+
}
|
|
135
|
+
return "unavailable";
|
|
136
|
+
}
|
|
137
|
+
/* ------------------------------------------------------------------ *
|
|
138
|
+
* Deterministic closeout renderer *
|
|
139
|
+
* ------------------------------------------------------------------ */
|
|
140
|
+
function bulletList(items) {
|
|
141
|
+
if (!items || items.length === 0)
|
|
142
|
+
return "";
|
|
143
|
+
return items.map((item) => `- ${item}`).join("\n");
|
|
144
|
+
}
|
|
145
|
+
function coverageLines(facts) {
|
|
146
|
+
const lines = facts.coverageMatrix.map((entry) => `- ${entry.verificationTargetId}: ${entry.status}`);
|
|
147
|
+
return lines.length > 0 ? lines.join("\n") : "_(none)_";
|
|
148
|
+
}
|
|
149
|
+
function frontendStatus(facts) {
|
|
150
|
+
switch (facts.integrationFacts) {
|
|
151
|
+
case "real-api":
|
|
152
|
+
return "Frontend status: real-integrated";
|
|
153
|
+
case "mock-backed":
|
|
154
|
+
return "Frontend status: mock-validated";
|
|
155
|
+
case "local-no-remote":
|
|
156
|
+
return "Frontend status: locally-validated";
|
|
157
|
+
case "unavailable":
|
|
158
|
+
return "Frontend status: unverified";
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function integrationStatus(facts) {
|
|
162
|
+
return facts.integrationFacts === "real-api"
|
|
163
|
+
? "Real integration: validated"
|
|
164
|
+
: `Real integration: pending (${facts.integrationFacts})`;
|
|
165
|
+
}
|
|
166
|
+
function reviewApprovalLines(facts) {
|
|
167
|
+
const { typedVerdict, legacyVerdict, match } = facts.reviewApproval;
|
|
168
|
+
return [
|
|
169
|
+
`typed: ${typedVerdict ?? "none"}`,
|
|
170
|
+
`legacy: ${legacyVerdict ?? "none"}`,
|
|
171
|
+
`match: ${match}`,
|
|
172
|
+
].join("\n");
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Render a deterministic closeout Markdown document from committed facts only.
|
|
176
|
+
* The same facts always render byte-identical output; weak statuses are
|
|
177
|
+
* emitted verbatim and never promoted to `passed` / real-integrated.
|
|
178
|
+
*/
|
|
179
|
+
export function renderFrontendCloseout(facts) {
|
|
180
|
+
const lintLine = facts.lintStatus === "baseline-debt"
|
|
181
|
+
? `${facts.lintStatus} (explicit debt; not passed)`
|
|
182
|
+
: facts.lintStatus;
|
|
183
|
+
return [
|
|
184
|
+
"# Frontend Closeout",
|
|
185
|
+
"",
|
|
186
|
+
"## Coverage Matrix",
|
|
187
|
+
coverageLines(facts),
|
|
188
|
+
"",
|
|
189
|
+
"## Lint Status",
|
|
190
|
+
lintLine,
|
|
191
|
+
"",
|
|
192
|
+
"## Integration Facts",
|
|
193
|
+
facts.integrationFacts,
|
|
194
|
+
"",
|
|
195
|
+
"## Frontend Status",
|
|
196
|
+
frontendStatus(facts),
|
|
197
|
+
"",
|
|
198
|
+
"## Real Integration Status",
|
|
199
|
+
integrationStatus(facts),
|
|
200
|
+
"",
|
|
201
|
+
"## Real Integration Pending",
|
|
202
|
+
facts.pendingRealIntegration ? "yes" : "no",
|
|
203
|
+
"",
|
|
204
|
+
"## Review Approval",
|
|
205
|
+
reviewApprovalLines(facts),
|
|
206
|
+
"",
|
|
207
|
+
"## Cumulative Diff",
|
|
208
|
+
`digest: ${facts.cumulativeDiff.cumulativeDigest}`,
|
|
209
|
+
`empty: ${facts.cumulativeDiff.empty}`,
|
|
210
|
+
"",
|
|
211
|
+
"## Browser / Visual",
|
|
212
|
+
`Browser: ${facts.notRun.browser}`,
|
|
213
|
+
`Visual: ${facts.notRun.visual}`,
|
|
214
|
+
"",
|
|
215
|
+
"## Residual Risks",
|
|
216
|
+
bulletList(facts.residualRisks) || "_(none)_",
|
|
217
|
+
"",
|
|
218
|
+
"## Evidence Gaps",
|
|
219
|
+
bulletList(facts.evidenceGaps) || "_(none)_",
|
|
220
|
+
].join("\n") + "\n";
|
|
221
|
+
}
|