@tea-agent/loop-agent 0.39.0-next.27 → 0.39.0-next.29
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 +3 -0
- package/CHANGELOG.md +150 -96
- package/README.md +8 -4
- 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/init-model-catalog.js +464 -0
- package/dist/commands/init-upgrade.js +265 -97
- package/dist/commands/init.js +428 -27
- package/dist/commands/inspect-next.js +8 -0
- package/dist/executors/dag-pi-executor.js +18 -9
- package/dist/executors/pi-playwright-cli-tool.js +8 -1
- package/dist/executors/shell-executor.js +209 -13
- package/dist/shared/operator/capabilities.js +7 -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/worker/cli.js +31 -1
- package/dist/worker/console/chat/routes.js +1 -0
- package/dist/worker/console/operator-user-error.js +1 -1
- package/dist/worker/console/routes.js +3 -0
- package/dist/worker/console/security.js +53 -5
- package/dist/worker/console/server.js +10 -4
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-C4kVYweA.js → abnfDiagram-N423BO3Z-DGn9iNrd.js} +1 -1
- package/dist/worker/console/static/assets/{arc-Bj2M8iO5.js → arc-Dc0C2Nyv.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-CLlXe4-6.js → architectureDiagram-T3A2C74G-COnz_Bhe.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-psEp5xH0.js → blockDiagram-VBNYF7ZC-VhIP6vgf.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-DeKcOCGJ.js → c4Diagram-5PPSVZJV-D74lpmlb.js} +1 -1
- package/dist/worker/console/static/assets/channel-DWcZjWB3.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-K0C744p1.js → chunk-2GRJ4B5K-BxmiogQE.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-xbNw4J9-.js → chunk-2Q5K7J3B-C00Oi-2I.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-DjLaSDUO.js → chunk-5RXB4S5H-DTrT1Vze.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-CtiPlKel.js → chunk-5VM5RSS4-DdKW7-fL.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DZtolir7.js → chunk-6Q2QTUOP-D0q1WN6w.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CPcdNeew.js → chunk-GF5L2VYU-DBa14MSI.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-NgHc6V37.js → chunk-JWPE2WC7-DkP-pEox.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-DQ_T-hg8.js → chunk-KBJHAD2P-lgPogpcX.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DgLsxcVP.js → chunk-RYQCIY6F-D-apZer0.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-UrVoM9z1.js → chunk-XXDRQBXY-B1T2yAIe.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-IjoI3dqU.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-IjoI3dqU.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-CLkYvTb3.js → cose-bilkent-JH36ORCC-0P6hi3jF.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BwT_xKrE.js → cynefin-VYW2F7L2-CntzMSR8.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-Bi9tuzif.js → cynefinDiagram-MW4NZA55-C9zmXJtG.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-BCIqkWBV.js → dagre-VZM6K2ZE-DfBCZqZw.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-Dip6l9_Z.js → diagram-7IWD3JNH-DLp-bhGL.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-B-xkh_wK.js → diagram-B4RE2ZJO-BksDcwZm.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-DZO0kTt_.js → diagram-LBJQPF4R-Bdag7DYP.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-D6ZplNbZ.js → diagram-Q27KOJAE-CoY5uNbk.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-BPekbhoS.js → diagram-UB23O5K3-KcgrDXHt.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-BtmaJpK5.js → ebnfDiagram-BXEA7PRR-CikErwJb.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-6DS0Xc44.js → erDiagram-JOGREHBK-DqmwwzvZ.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-CihnROSm.js → flowDiagram-UKHOOZJN-35krh9WG.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-CZC4zOE2.js → ganttDiagram-PKOTCBZU-CvTF1gJ4.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Dq1fhzGN.js → gitGraphDiagram-DS77QQ5N-BiOWWClJ.js} +1 -1
- package/dist/worker/console/static/assets/{index-DTZOKgAn.js → index-CqWlWE4l.js} +3 -3
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DlSl4BGx.js → infoDiagram-6WML65LV-Crha5dcA.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-snnzFl_U.js → ishikawaDiagram-WSZJBQD7-DEtbscOZ.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-BDE7qpMx.js → journeyDiagram-NVQOT4AX-D2YYzx3z.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-CD2Ci04G.js → kanban-definition-27J2QSJJ-C7x18ttQ.js} +1 -1
- package/dist/worker/console/static/assets/{linear-DdnapKIH.js → linear-CCLZkz45.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-BVjAT9b8.js → mermaid.core-Dmy4lSGv.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-D0yJk-zA.js → mindmap-definition-FAOFIHXS-CmvIol59.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-BVo9tFP-.js → pegDiagram-VL7TDLO6-DVpEdirN.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-DnOV-mZ_.js → pieDiagram-7S7Q4E2Y-cKQy456v.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-CzUJo58i.js → quadrantDiagram-CIZ2JOQS-bx06I38m.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-Bvikrolh.js → railroadDiagram-AXF67PYL-BjfIVIJr.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-DtaSVCap.js → requirementDiagram-LRYGKXZP-DcuPGbKM.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-C2c9A0wW.js → sankeyDiagram-W5VNT64P-DvgVeDoV.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-DmXJcU7r.js → sequenceDiagram-SI44F4Z6-DLBG9nm-.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-B4GUFW92.js → sizeCapture-X5ZJPWSS-D33L7FBF.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-Dsad2MXf.js → stateDiagram-OKZ733FA-hvqD9TpS.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-C6MII0D1.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-DGq48Fbi.js → swimlanes-SLNWSIFB-BtjH81Mz.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-D_tk0V8D.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CSSi6OFf.js → timeline-definition-Z64GVDOM-CJ47Kraf.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-BqyzPrWv.js → vennDiagram-T6HMQDX7-COVPl-wh.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-DlznVb6S.js → wardleyDiagram-T6FBY63Y-CCoQJLUY.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CfBcgJ_K.js → xychartDiagram-ELKLHX3M-DTWJvfAd.js} +1 -1
- package/dist/worker/console/static/index.html +1 -1
- package/dist/worker/console/static-src/operator-chat/mutation-gate.js +1 -1
- package/dist/worker/observe/node-input.js +2 -0
- package/dist/worker/observe/node-process.js +377 -0
- package/dist/worker/observe/routes.js +24 -0
- package/dist/worker/observe/shell-handler-keys.js +34 -0
- package/dist/worker/observe/static/api.js +8 -0
- package/dist/worker/observe/static/constants.js +8 -1
- 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 +11 -0
- package/dist/worker/observe/static/styles.css +3 -0
- package/dist/worker/observe/static/views/dag-inspector.js +247 -7
- package/dist/worker/observe/static/views/dag.js +8 -1
- package/dist/worker/observe/static/views/session-timeline.js +60 -4
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +20 -2
- package/dist/workflows/dag/failure-category.js +8 -0
- package/dist/workflows/dag/frontend-implementation-contract.js +5 -1
- package/dist/workflows/dag/init-hybrid.js +35 -10
- package/dist/workflows/dag/node-execution.js +20 -2
- package/dist/workflows/dag/retry-policy.js +91 -0
- package/dist/workflows/dag/types.js +9 -1
- package/docs/init-surface.manifest.json +5 -0
- package/docs/operations/README.md +1 -1
- package/docs/templates/init-managed-agents.md +13 -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/loop-agent/references/command-reference.md +11 -4
- 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/stateDiagram-v2-UEYNNEHI-B8FB_cNk.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-CrZYaWfx.js +0 -8
|
@@ -11,14 +11,15 @@ import {
|
|
|
11
11
|
} from "../constants.js";
|
|
12
12
|
import { clearNode, el } from "../dom.js";
|
|
13
13
|
import { attemptFailureLabel } from "../dag-history-labels.js";
|
|
14
|
-
import { fetchJson, fetchSpecEvidenceFile, fetchDagNodeInput } from "../api.js";
|
|
15
|
-
import { badge, formatMs } from "../format.js";
|
|
16
|
-
import { renderMarkdown } from "../markdown-render.js";
|
|
14
|
+
import { fetchJson, fetchSpecEvidenceFile, fetchDagNodeInput, fetchDagNodeProcess } from "../api.js";
|
|
15
|
+
import { badge, formatMs, prettyJsonBlock } from "../format.js";
|
|
16
|
+
import { renderMarkdown, renderInspectorDocument } from "../markdown-render.js";
|
|
17
17
|
import {
|
|
18
18
|
uiState,
|
|
19
19
|
makeSessionEventIdentity,
|
|
20
20
|
makeDagNodeInputIdentity,
|
|
21
21
|
makeDagNodeExecutionOutputIdentity,
|
|
22
|
+
makeDagNodeProcessIdentity,
|
|
22
23
|
getDagNodeExecutionOutputCache,
|
|
23
24
|
setDagNodeExecutionOutputCache,
|
|
24
25
|
clearDagNodeExecutionOutputCache,
|
|
@@ -68,6 +69,10 @@ function resetInspectorNodeSelection(dagRunId, nodeId) {
|
|
|
68
69
|
uiState.dagNodeInputData = null;
|
|
69
70
|
uiState.dagNodeInputError = null;
|
|
70
71
|
uiState.dagNodeInputLoading = false;
|
|
72
|
+
uiState.dagNodeProcessData = null;
|
|
73
|
+
uiState.dagNodeProcessError = null;
|
|
74
|
+
uiState.dagNodeProcessLoading = false;
|
|
75
|
+
uiState.dagNodeProcessIdentity = null;
|
|
71
76
|
// Drop execution-output cache/identity so node A's bodies never leak into B.
|
|
72
77
|
clearDagNodeExecutionOutputCache();
|
|
73
78
|
// Bump the session-event identity token so any in-flight response for the
|
|
@@ -748,6 +753,12 @@ function renderDagNodeInputDetail(content, data) {
|
|
|
748
753
|
(shell.gates ?? []).join(", ") || "(none)",
|
|
749
754
|
null,
|
|
750
755
|
);
|
|
756
|
+
appendMetaRow(
|
|
757
|
+
shellDl,
|
|
758
|
+
"handlers",
|
|
759
|
+
(shell.handlers ?? []).join(", ") || "(none)",
|
|
760
|
+
null,
|
|
761
|
+
);
|
|
751
762
|
section.appendChild(shellDl);
|
|
752
763
|
if ((shell.commands ?? []).length > 0) {
|
|
753
764
|
const list = el("ol", "node-input-command-list");
|
|
@@ -1305,7 +1316,7 @@ function appendOutputBody(content, outputText, errorText, options = null) {
|
|
|
1305
1316
|
content.appendChild(el("p", "empty", UI_TEXT.noOutput));
|
|
1306
1317
|
return;
|
|
1307
1318
|
}
|
|
1308
|
-
if (outputText) content.appendChild(
|
|
1319
|
+
if (outputText) content.appendChild(renderInspectorDocument(outputText));
|
|
1309
1320
|
// Truncation note only for full/cached execution-output bodies (AC-003);
|
|
1310
1321
|
// preview path never invents truncated/maxBytes.
|
|
1311
1322
|
if (options?.truncated === true) {
|
|
@@ -1321,7 +1332,7 @@ function appendOutputBody(content, outputText, errorText, options = null) {
|
|
|
1321
1332
|
if (errorText) {
|
|
1322
1333
|
const error = el("section", "markdown-error");
|
|
1323
1334
|
error.appendChild(el("p", "markdown-error-title", "错误输出"));
|
|
1324
|
-
error.appendChild(
|
|
1335
|
+
error.appendChild(renderInspectorDocument(errorText));
|
|
1325
1336
|
content.appendChild(error);
|
|
1326
1337
|
}
|
|
1327
1338
|
}
|
|
@@ -1455,6 +1466,228 @@ function repaintOutputTabBody(content, dagRunId, node) {
|
|
|
1455
1466
|
restoreInspectorContentScroll(content, dagRunId, node.nodeId, "output");
|
|
1456
1467
|
}
|
|
1457
1468
|
|
|
1469
|
+
function processEmptyMessage(body) {
|
|
1470
|
+
const reason = body?.emptyReason;
|
|
1471
|
+
if (reason === "static-no-process") return UI_TEXT.staticNoProcess;
|
|
1472
|
+
if (reason === "shell-receipt-missing") return UI_TEXT.noShellProcess;
|
|
1473
|
+
if (reason === "shell-preset-pending") return UI_TEXT.noPresetProcess;
|
|
1474
|
+
if (reason === "pi-session") return UI_TEXT.noSessionEvents;
|
|
1475
|
+
return UI_TEXT.noSessionEvents;
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
function formatProcessBlock(text) {
|
|
1479
|
+
if (!text) return "";
|
|
1480
|
+
return prettyJsonBlock(text) ?? text;
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
function appendProcessPre(parent, text) {
|
|
1484
|
+
if (!text) return;
|
|
1485
|
+
const pre = el("pre", "process-timeline-code");
|
|
1486
|
+
pre.textContent = formatProcessBlock(text);
|
|
1487
|
+
parent.appendChild(pre);
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
function renderProcessCommandCard(command) {
|
|
1491
|
+
const failed = command.ok === false;
|
|
1492
|
+
const card = el(
|
|
1493
|
+
"div",
|
|
1494
|
+
`process-timeline-card process-timeline-tool-card${failed ? " is-error" : ""}`,
|
|
1495
|
+
);
|
|
1496
|
+
const details = document.createElement("details");
|
|
1497
|
+
details.className = "process-timeline-details";
|
|
1498
|
+
details.open = failed;
|
|
1499
|
+
const summary = el("summary", "process-timeline-summary");
|
|
1500
|
+
summary.appendChild(
|
|
1501
|
+
el("span", "process-timeline-summary-title", command.command || "command"),
|
|
1502
|
+
);
|
|
1503
|
+
summary.appendChild(
|
|
1504
|
+
el(
|
|
1505
|
+
"span",
|
|
1506
|
+
`process-timeline-summary-status status-${failed ? "failed" : "succeeded"}`,
|
|
1507
|
+
failed ? "失败" : command.reused ? "复用" : "成功",
|
|
1508
|
+
),
|
|
1509
|
+
);
|
|
1510
|
+
details.appendChild(summary);
|
|
1511
|
+
const body = el("div", "process-timeline-card-body");
|
|
1512
|
+
const meta = [
|
|
1513
|
+
command.exitCode != null ? `exit ${command.exitCode}` : null,
|
|
1514
|
+
command.durationMs != null ? `耗时 ${formatMs(command.durationMs)}` : null,
|
|
1515
|
+
command.failureCategory ? command.failureCategory : null,
|
|
1516
|
+
].filter(Boolean);
|
|
1517
|
+
if (meta.length > 0) {
|
|
1518
|
+
body.appendChild(el("div", "process-timeline-section-label", meta.join(" · ")));
|
|
1519
|
+
}
|
|
1520
|
+
if (command.stdout) {
|
|
1521
|
+
body.appendChild(el("div", "process-timeline-section-label", "stdout"));
|
|
1522
|
+
appendProcessPre(body, command.stdout);
|
|
1523
|
+
}
|
|
1524
|
+
if (command.stderr) {
|
|
1525
|
+
body.appendChild(el("div", "process-timeline-section-label", "stderr"));
|
|
1526
|
+
appendProcessPre(body, command.stderr);
|
|
1527
|
+
}
|
|
1528
|
+
if (command.truncated) {
|
|
1529
|
+
body.appendChild(el("p", "muted", "内容过长,已截断。"));
|
|
1530
|
+
}
|
|
1531
|
+
details.appendChild(body);
|
|
1532
|
+
card.appendChild(details);
|
|
1533
|
+
return card;
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
function renderProcessStepCard(step) {
|
|
1537
|
+
const failed = step.ok === false;
|
|
1538
|
+
const card = el(
|
|
1539
|
+
"div",
|
|
1540
|
+
`process-timeline-card process-timeline-tool-card${failed ? " is-error" : ""}`,
|
|
1541
|
+
);
|
|
1542
|
+
const details = document.createElement("details");
|
|
1543
|
+
details.className = "process-timeline-details";
|
|
1544
|
+
details.open = true;
|
|
1545
|
+
const summary = el("summary", "process-timeline-summary");
|
|
1546
|
+
summary.appendChild(
|
|
1547
|
+
el("span", "process-timeline-summary-title", step.label || step.id || "step"),
|
|
1548
|
+
);
|
|
1549
|
+
summary.appendChild(
|
|
1550
|
+
el(
|
|
1551
|
+
"span",
|
|
1552
|
+
`process-timeline-summary-status status-${failed ? "failed" : "succeeded"}`,
|
|
1553
|
+
failed ? "失败" : "成功",
|
|
1554
|
+
),
|
|
1555
|
+
);
|
|
1556
|
+
details.appendChild(summary);
|
|
1557
|
+
const body = el("div", "process-timeline-card-body");
|
|
1558
|
+
if (step.durationMs != null) {
|
|
1559
|
+
body.appendChild(
|
|
1560
|
+
el("div", "process-timeline-section-label", `耗时 ${formatMs(step.durationMs)}`),
|
|
1561
|
+
);
|
|
1562
|
+
}
|
|
1563
|
+
if (step.detail) appendProcessPre(body, step.detail);
|
|
1564
|
+
if (step.truncated) {
|
|
1565
|
+
body.appendChild(el("p", "muted", "内容过长,已截断。"));
|
|
1566
|
+
}
|
|
1567
|
+
details.appendChild(body);
|
|
1568
|
+
card.appendChild(details);
|
|
1569
|
+
return card;
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
function renderNonPiProcess(content, body) {
|
|
1573
|
+
content.classList.add("process-timeline-scroll");
|
|
1574
|
+
if (!body || body.emptyReason || ((body.commands ?? []).length === 0 && (body.steps ?? []).length === 0)) {
|
|
1575
|
+
content.appendChild(el("p", "empty", processEmptyMessage(body)));
|
|
1576
|
+
return;
|
|
1577
|
+
}
|
|
1578
|
+
const heading =
|
|
1579
|
+
body.kind === "shell-preset"
|
|
1580
|
+
? UI_TEXT.shellPresetHeading
|
|
1581
|
+
: UI_TEXT.shellCommandsHeading;
|
|
1582
|
+
if (body.handler) {
|
|
1583
|
+
content.appendChild(
|
|
1584
|
+
el("p", "process-timeline-note", `${heading} · ${body.handler}`),
|
|
1585
|
+
);
|
|
1586
|
+
}
|
|
1587
|
+
const ul = el("ul", "process-timeline process-timeline-rich");
|
|
1588
|
+
const items = (body.commands ?? []).length > 0 ? body.commands : body.steps ?? [];
|
|
1589
|
+
for (let index = 0; index < items.length; index += 1) {
|
|
1590
|
+
const item = items[index];
|
|
1591
|
+
const failed = item.ok === false;
|
|
1592
|
+
const li = el(
|
|
1593
|
+
"li",
|
|
1594
|
+
`process-timeline-item process-timeline-tool-end${failed ? " is-error" : ""}${index === 0 ? " is-first" : ""}${index === items.length - 1 ? " is-last" : ""}`,
|
|
1595
|
+
);
|
|
1596
|
+
const rail = el("span", "process-timeline-rail");
|
|
1597
|
+
rail.appendChild(el("span", "process-timeline-dot"));
|
|
1598
|
+
li.appendChild(rail);
|
|
1599
|
+
const cardBody = el("div", "process-timeline-body");
|
|
1600
|
+
cardBody.appendChild(
|
|
1601
|
+
body.kind === "shell-preset"
|
|
1602
|
+
? renderProcessStepCard(item)
|
|
1603
|
+
: renderProcessCommandCard(item),
|
|
1604
|
+
);
|
|
1605
|
+
li.appendChild(cardBody);
|
|
1606
|
+
ul.appendChild(li);
|
|
1607
|
+
}
|
|
1608
|
+
content.appendChild(ul);
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
export async function mergeNodeProcess(dagRunId, node) {
|
|
1612
|
+
if (!dagRunId || !node?.nodeId) return;
|
|
1613
|
+
const nodeId = node.nodeId;
|
|
1614
|
+
const key = effectiveExecutionKey(dagRunId, node);
|
|
1615
|
+
const identity = makeDagNodeProcessIdentity(dagRunId, nodeId, key);
|
|
1616
|
+
uiState.dagNodeProcessIdentity = identity;
|
|
1617
|
+
uiState.dagNodeProcessLoading = true;
|
|
1618
|
+
const result = await fetchDagNodeProcess(dagRunId, nodeId, key);
|
|
1619
|
+
if (uiState.dagNodeProcessIdentity !== identity) return;
|
|
1620
|
+
if (!result.ok || !result.body) {
|
|
1621
|
+
uiState.dagNodeProcessError =
|
|
1622
|
+
result.body?.error || UI_TEXT.processError;
|
|
1623
|
+
uiState.dagNodeProcessData = {
|
|
1624
|
+
schemaVersion: 1,
|
|
1625
|
+
dagRunId,
|
|
1626
|
+
nodeId,
|
|
1627
|
+
kind: "none",
|
|
1628
|
+
executor: node.executor ?? null,
|
|
1629
|
+
handler: null,
|
|
1630
|
+
emptyReason: "not-executed",
|
|
1631
|
+
key: key ?? null,
|
|
1632
|
+
commands: [],
|
|
1633
|
+
steps: [],
|
|
1634
|
+
};
|
|
1635
|
+
uiState.dagNodeProcessLoading = false;
|
|
1636
|
+
return;
|
|
1637
|
+
}
|
|
1638
|
+
uiState.dagNodeProcessError = null;
|
|
1639
|
+
uiState.dagNodeProcessData = result.body;
|
|
1640
|
+
uiState.dagNodeProcessLoading = false;
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
function renderExecutorAwareTimeline(content, dagRunId, node) {
|
|
1644
|
+
content.dataset.dagRunId = String(dagRunId ?? "");
|
|
1645
|
+
content.dataset.nodeId = String(node.nodeId ?? "");
|
|
1646
|
+
const key = effectiveExecutionKey(dagRunId, node);
|
|
1647
|
+
const cached = uiState.dagNodeProcessData;
|
|
1648
|
+
const sameNode =
|
|
1649
|
+
cached &&
|
|
1650
|
+
cached.dagRunId === dagRunId &&
|
|
1651
|
+
cached.nodeId === node.nodeId &&
|
|
1652
|
+
(cached.key ?? null) === (key ?? null);
|
|
1653
|
+
if (sameNode && uiState.dagNodeProcessError) {
|
|
1654
|
+
content.appendChild(el("p", "empty", uiState.dagNodeProcessError));
|
|
1655
|
+
return;
|
|
1656
|
+
}
|
|
1657
|
+
if (sameNode && cached.kind === "pi-session") {
|
|
1658
|
+
renderSessionTimeline(content, node.nodeId, {
|
|
1659
|
+
executionKey: effectiveExecutionKey(dagRunId, node),
|
|
1660
|
+
});
|
|
1661
|
+
return;
|
|
1662
|
+
}
|
|
1663
|
+
if (sameNode && cached.kind !== "pi-session") {
|
|
1664
|
+
renderNonPiProcess(content, cached);
|
|
1665
|
+
return;
|
|
1666
|
+
}
|
|
1667
|
+
if ((node.executor ?? "pi") === "pi") {
|
|
1668
|
+
renderSessionTimeline(content, node.nodeId, {
|
|
1669
|
+
executionKey: effectiveExecutionKey(dagRunId, node),
|
|
1670
|
+
});
|
|
1671
|
+
return;
|
|
1672
|
+
}
|
|
1673
|
+
if (uiState.dagNodeProcessError && sameNode) {
|
|
1674
|
+
content.appendChild(el("p", "empty", uiState.dagNodeProcessError));
|
|
1675
|
+
return;
|
|
1676
|
+
}
|
|
1677
|
+
const loading = el("p", "muted", UI_TEXT.processLoading);
|
|
1678
|
+
loading.setAttribute("role", "status");
|
|
1679
|
+
content.appendChild(loading);
|
|
1680
|
+
void mergeNodeProcess(dagRunId, node).then(() => {
|
|
1681
|
+
if (
|
|
1682
|
+
uiState.dagInspectorTab === "timeline" &&
|
|
1683
|
+
uiState.selectedDagNodeId === node.nodeId &&
|
|
1684
|
+
uiState.currentDagRunId === dagRunId
|
|
1685
|
+
) {
|
|
1686
|
+
renderDagInspectorForCurrent();
|
|
1687
|
+
}
|
|
1688
|
+
});
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1458
1691
|
function fillDagInspectorContent(content, dagRunId, node) {
|
|
1459
1692
|
const previousRenderedNode = content.dataset.dagNodeId ?? "";
|
|
1460
1693
|
const previousRenderedRun = content.dataset.dagRunId ?? "";
|
|
@@ -1504,7 +1737,7 @@ function fillDagInspectorContent(content, dagRunId, node) {
|
|
|
1504
1737
|
} else if (activeTab === "timeline") {
|
|
1505
1738
|
const attemptBuilt = buildAttemptBar(dagRunId, node);
|
|
1506
1739
|
if (attemptBuilt?.bar) content.appendChild(attemptBuilt.bar);
|
|
1507
|
-
|
|
1740
|
+
renderExecutorAwareTimeline(content, dagRunId, node);
|
|
1508
1741
|
} else if (activeTab === "input") {
|
|
1509
1742
|
void renderDagNodeInput(content, dagRunId, node);
|
|
1510
1743
|
} else {
|
|
@@ -1657,7 +1890,14 @@ function dagInspectorSignature(dagRunId, node) {
|
|
|
1657
1890
|
uiState.dagNodeExecutionOutputLoading ? "loading" : "idle",
|
|
1658
1891
|
]
|
|
1659
1892
|
: tab === "timeline"
|
|
1660
|
-
? [
|
|
1893
|
+
? [
|
|
1894
|
+
uiState.sessionEventOffset,
|
|
1895
|
+
uiState.dagNodeProcessData?.kind ?? "",
|
|
1896
|
+
uiState.dagNodeProcessData?.emptyReason ?? "",
|
|
1897
|
+
(uiState.dagNodeProcessData?.commands ?? []).length,
|
|
1898
|
+
(uiState.dagNodeProcessData?.steps ?? []).length,
|
|
1899
|
+
uiState.dagNodeProcessLoading ? "loading" : "idle",
|
|
1900
|
+
]
|
|
1661
1901
|
: [node.status ?? ""];
|
|
1662
1902
|
// Attempt dimension: selection identity + history shape. Poll refreshes keep
|
|
1663
1903
|
// the DOM while selection/history are unchanged; switching attempt or a
|
|
@@ -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,
|
|
@@ -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.
|
|
@@ -657,7 +705,7 @@ export async function mergeSessionEvents(dagRunId, nodeId) {
|
|
|
657
705
|
}
|
|
658
706
|
}
|
|
659
707
|
|
|
660
|
-
export function renderSessionTimeline(content, nodeId) {
|
|
708
|
+
export function renderSessionTimeline(content, nodeId, options = {}) {
|
|
661
709
|
content.dataset.dagRunId = uiState.currentDagRunId ?? "";
|
|
662
710
|
content.dataset.nodeId = nodeId ?? "";
|
|
663
711
|
content.classList.add("process-timeline-scroll");
|
|
@@ -666,6 +714,14 @@ export function renderSessionTimeline(content, nodeId) {
|
|
|
666
714
|
return;
|
|
667
715
|
}
|
|
668
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
|
+
|
|
669
725
|
const showProtocol = Boolean(uiState.sessionTimelineShowProtocol);
|
|
670
726
|
const toolbar = el("div", "process-timeline-toolbar");
|
|
671
727
|
const protocolToggle = document.createElement("button");
|
|
@@ -681,17 +737,17 @@ export function renderSessionTimeline(content, nodeId) {
|
|
|
681
737
|
const scrollTop = parent.scrollTop;
|
|
682
738
|
const scrollLeft = parent.scrollLeft;
|
|
683
739
|
while (parent.firstChild) parent.removeChild(parent.firstChild);
|
|
684
|
-
renderSessionTimeline(parent, nodeId);
|
|
740
|
+
renderSessionTimeline(parent, nodeId, options);
|
|
685
741
|
parent.scrollTop = scrollTop;
|
|
686
742
|
parent.scrollLeft = scrollLeft;
|
|
687
743
|
});
|
|
688
744
|
toolbar.appendChild(protocolToggle);
|
|
689
745
|
content.appendChild(toolbar);
|
|
690
746
|
|
|
691
|
-
const timelineItems = buildRichTimelineItems(
|
|
747
|
+
const timelineItems = buildRichTimelineItems(visible.events, {
|
|
692
748
|
showProtocol,
|
|
693
749
|
});
|
|
694
|
-
const hasEventTimes =
|
|
750
|
+
const hasEventTimes = visible.events.some((event) =>
|
|
695
751
|
Boolean(sessionEventTimestamp(event)),
|
|
696
752
|
);
|
|
697
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);
|
|
@@ -29,6 +29,7 @@ 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",
|
|
34
35
|
"structured-output-truncated": "validation",
|
|
@@ -44,6 +45,13 @@ const RAW_TO_NORMALIZED = {
|
|
|
44
45
|
quota: "executor",
|
|
45
46
|
"rate-limit": "executor",
|
|
46
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",
|
|
47
55
|
unknown: "unknown",
|
|
48
56
|
};
|
|
49
57
|
function isFinishedStatus(status) {
|
|
@@ -1723,7 +1723,11 @@ async function dropUnresolvedVerificationSymbols(input) {
|
|
|
1723
1723
|
return changed;
|
|
1724
1724
|
}
|
|
1725
1725
|
function extractSingleOpenspecCitationsBlock(text) {
|
|
1726
|
-
|
|
1726
|
+
// Line-anchored so a directly closed empty fence (no candidate rows) still
|
|
1727
|
+
// counts as the one required block instead of "found 0" retry loops.
|
|
1728
|
+
const blocks = [
|
|
1729
|
+
...text.matchAll(/^```openspec-citations[ \t]*\r?\n([\s\S]*?)^```[ \t]*$/gim),
|
|
1730
|
+
].map((match) => match[0]);
|
|
1727
1731
|
if (blocks.length !== 1) {
|
|
1728
1732
|
throw new Error(`frontend plan patch output must include exactly one fenced openspec-citations block (found ${blocks.length})`);
|
|
1729
1733
|
}
|
|
@@ -11,7 +11,7 @@ import { pathMatchesPattern } from "../../shared/git-progress.js";
|
|
|
11
11
|
import { extractTaskSourceOpenspecPaths } from "../../shared/openspec-spec.js";
|
|
12
12
|
import { BASELINE_FORBIDDEN_PATHS } from "./governance-constants.js";
|
|
13
13
|
import { buildDecisionEnvelopePromptContract } from "./decision-envelope.js";
|
|
14
|
-
import { DEFAULT_READ_ONLY_PI_RETRY_POLICY, PLANNER_OUTPUT_LIMIT_RETRY_POLICY, PROTOCOL_AWARE_PI_RETRY_POLICY, STRUCTURED_REQUIRED_PI_RETRY_POLICY, BACKEND_TEST_WRITER_COMPLETENESS_RETRY_POLICY, WRITER_TRANSPORT_RETRY_POLICY, isSafeReadOnlyPiRetryCandidate, isWriterTransportRetryCandidate, } from "./retry-policy.js";
|
|
14
|
+
import { DEFAULT_READ_ONLY_PI_RETRY_POLICY, PLANNER_OUTPUT_LIMIT_RETRY_POLICY, PROTOCOL_AWARE_PI_RETRY_POLICY, STRUCTURED_REQUIRED_PI_RETRY_POLICY, BACKEND_TEST_WRITER_COMPLETENESS_RETRY_POLICY, WRITER_TRANSPORT_RETRY_POLICY, TARGET_TEMPLATE_TRANSIENT_RETRY_PROFILE, TARGET_TEMPLATE_WRITER_TRANSPORT_RETRY_POLICY, isCanonicalFinalVerifyShellRetryCandidate, isSafeReadOnlyPiRetryCandidate, isTargetTemplateImplementPi, isWriterTransportRetryCandidate, } from "./retry-policy.js";
|
|
15
15
|
import { REVIEW_JSON_VERDICT_OUTPUT_PROTOCOL, REVIEW_VERDICT_OUTPUT_PROTOCOL, } from "./output-protocol.js";
|
|
16
16
|
import { resolveAdapter } from "../../adapters/index.js";
|
|
17
17
|
import { loadHarnessManifest } from "../../governance/harness.js";
|
|
@@ -1719,6 +1719,7 @@ export function buildStandardHybridDagFromTask(sources) {
|
|
|
1719
1719
|
forbiddenPaths,
|
|
1720
1720
|
outputContract: "Archived final shell verification stdout/stderr with exit codes; no worktree writes.",
|
|
1721
1721
|
subtask_prompt: "Run the adapter-resolved final verification commands before read-only verification review.",
|
|
1722
|
+
transientRetryProfile: TARGET_TEMPLATE_TRANSIENT_RETRY_PROFILE,
|
|
1722
1723
|
shell: {
|
|
1723
1724
|
commands: verifyShellCommands,
|
|
1724
1725
|
verifyEvidence: buildVerifyEvidence({
|
|
@@ -1839,6 +1840,7 @@ export function buildStandardHybridDagFromTask(sources) {
|
|
|
1839
1840
|
allowedPaths: implementPaths.allowedPaths,
|
|
1840
1841
|
forbiddenPaths,
|
|
1841
1842
|
writerOutcomePolicy: { type: "implementation-outcome-v1" },
|
|
1843
|
+
transientRetryProfile: TARGET_TEMPLATE_TRANSIENT_RETRY_PROFILE,
|
|
1842
1844
|
subtask_prompt: [
|
|
1843
1845
|
"Implement the approved plan with minimal focused changes.",
|
|
1844
1846
|
"Stay within writeSet. Do not write root artifacts/** unless artifacts paths are explicitly declared in writeSet.",
|
|
@@ -1898,6 +1900,7 @@ export function buildStandardHybridDagFromTask(sources) {
|
|
|
1898
1900
|
],
|
|
1899
1901
|
};
|
|
1900
1902
|
applySddEmbeddedEnhancements(spec, sources.sddEmbeddedSkills ?? new Set());
|
|
1903
|
+
stampTargetTemplateTransientRetryProfile(spec);
|
|
1901
1904
|
applyDefaultReadOnlyRetryPolicy(spec);
|
|
1902
1905
|
parseDagSpec(spec);
|
|
1903
1906
|
assertValidDagSpec(spec);
|
|
@@ -6442,7 +6445,12 @@ async function buildHybridDagForTemplate(sources, template, options = {}) {
|
|
|
6442
6445
|
spec.sourceBinding = buildDagSourceBinding(sources);
|
|
6443
6446
|
spec.taskContractBinding = taskContractBinding;
|
|
6444
6447
|
assertNoGovernanceFlagOnDisallowedTemplate(spec, template);
|
|
6445
|
-
|
|
6448
|
+
if (template === "standard-dag" ||
|
|
6449
|
+
template === "review-gated-dag" ||
|
|
6450
|
+
template === "supervised-implementation") {
|
|
6451
|
+
stampTargetTemplateTransientRetryProfile(spec);
|
|
6452
|
+
}
|
|
6453
|
+
spec = parseDagSpec(spec);
|
|
6446
6454
|
assertValidDagSpec(spec);
|
|
6447
6455
|
return spec;
|
|
6448
6456
|
}
|
|
@@ -6493,14 +6501,6 @@ export async function buildHybridDagFromTask(sources, options = {}) {
|
|
|
6493
6501
|
function cloneTask(task, patch = {}) {
|
|
6494
6502
|
return { ...task, ...patch };
|
|
6495
6503
|
}
|
|
6496
|
-
/**
|
|
6497
|
-
* Apply default Pi retry policies to generated DAG nodes:
|
|
6498
|
-
* - safe read-only planner/scout/reviewer/verifier/supervisor/closeout
|
|
6499
|
-
* - exclusive implementers get a single clean-timeout transport retry
|
|
6500
|
-
* (provider stall with zero attributed writes only)
|
|
6501
|
-
* Dynamic, shell, static, and decision-gate nodes are skipped. Idempotent:
|
|
6502
|
-
* never overwrites an explicit retryPolicy a task already declares.
|
|
6503
|
-
*/
|
|
6504
6504
|
/**
|
|
6505
6505
|
* Backend-implementation Pi extension buckets (plan 2026-08-21 D3):
|
|
6506
6506
|
* - read side: every non-writer, non-closeout Pi node gets navigation
|
|
@@ -6523,6 +6523,22 @@ function applyBackendPiExtensionBuckets(spec) {
|
|
|
6523
6523
|
task.toolProfile === "write" ? ["pi-codegraph"] : ["pi-codegraph", "pi-lens"];
|
|
6524
6524
|
}
|
|
6525
6525
|
}
|
|
6526
|
+
function stampTargetTemplateTransientRetryProfile(spec) {
|
|
6527
|
+
for (const task of spec.tasks) {
|
|
6528
|
+
if (isTargetTemplateImplementPi(task) ||
|
|
6529
|
+
isCanonicalFinalVerifyShellRetryCandidate(task)) {
|
|
6530
|
+
task.transientRetryProfile = TARGET_TEMPLATE_TRANSIENT_RETRY_PROFILE;
|
|
6531
|
+
}
|
|
6532
|
+
}
|
|
6533
|
+
}
|
|
6534
|
+
/**
|
|
6535
|
+
* Apply default Pi retry policies to generated DAG nodes:
|
|
6536
|
+
* - safe read-only planner/scout/reviewer/verifier/supervisor/closeout
|
|
6537
|
+
* - exclusive implementers get a single clean-timeout transport retry
|
|
6538
|
+
* (provider stall with zero attributed writes only)
|
|
6539
|
+
* Dynamic, shell, static, and decision-gate nodes are skipped. Idempotent:
|
|
6540
|
+
* never overwrites an explicit retryPolicy a task already declares.
|
|
6541
|
+
*/
|
|
6526
6542
|
function applyDefaultReadOnlyRetryPolicy(spec) {
|
|
6527
6543
|
for (const task of spec.tasks) {
|
|
6528
6544
|
if (task.retryPolicy !== undefined)
|
|
@@ -6535,6 +6551,10 @@ function applyDefaultReadOnlyRetryPolicy(spec) {
|
|
|
6535
6551
|
: DEFAULT_READ_ONLY_PI_RETRY_POLICY;
|
|
6536
6552
|
continue;
|
|
6537
6553
|
}
|
|
6554
|
+
if (isTargetTemplateImplementPi(task)) {
|
|
6555
|
+
task.retryPolicy = TARGET_TEMPLATE_WRITER_TRANSPORT_RETRY_POLICY;
|
|
6556
|
+
continue;
|
|
6557
|
+
}
|
|
6538
6558
|
if (isWriterTransportRetryCandidate(task)) {
|
|
6539
6559
|
task.retryPolicy = WRITER_TRANSPORT_RETRY_POLICY;
|
|
6540
6560
|
}
|
|
@@ -6795,6 +6815,7 @@ function buildReviewGatedHybridDag(standard, sources) {
|
|
|
6795
6815
|
}));
|
|
6796
6816
|
spec.tasks.splice(spec.tasks.length - 1, 0, buildReviewNode(sources), buildReviewVerdictRecoveryNode(sources), buildReviewGateNode(sources));
|
|
6797
6817
|
applySddEmbeddedEnhancements(spec, sources.sddEmbeddedSkills ?? new Set());
|
|
6818
|
+
stampTargetTemplateTransientRetryProfile(spec);
|
|
6798
6819
|
applyDefaultReadOnlyRetryPolicy(spec);
|
|
6799
6820
|
parseDagSpec(spec);
|
|
6800
6821
|
assertValidDagSpec(spec);
|
|
@@ -7126,6 +7147,9 @@ async function buildHardVerifyNode(sources) {
|
|
|
7126
7147
|
forbiddenPaths: commonForbiddenPaths(sources),
|
|
7127
7148
|
outputContract: "Archived hard verification stdout/stderr with exit codes; no worktree writes.",
|
|
7128
7149
|
subtask_prompt: "Run hard verification after repair round.",
|
|
7150
|
+
transientRetryProfile: sources.verifyCommands && commands.length > 0
|
|
7151
|
+
? TARGET_TEMPLATE_TRANSIENT_RETRY_PROFILE
|
|
7152
|
+
: undefined,
|
|
7129
7153
|
shell: {
|
|
7130
7154
|
commands,
|
|
7131
7155
|
verifyEvidence: buildVerifyEvidence({
|
|
@@ -7296,6 +7320,7 @@ async function buildSupervisedHybridDag(standard, sources) {
|
|
|
7296
7320
|
],
|
|
7297
7321
|
};
|
|
7298
7322
|
applySddEmbeddedEnhancements(spec, sources.sddEmbeddedSkills ?? new Set());
|
|
7323
|
+
stampTargetTemplateTransientRetryProfile(spec);
|
|
7299
7324
|
applyDefaultReadOnlyRetryPolicy(spec);
|
|
7300
7325
|
parseDagSpec(spec);
|
|
7301
7326
|
assertValidDagSpec(spec);
|