@tea-agent/loop-agent 0.24.11-beta.0 → 0.25.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 +53 -4
- package/README.md +4 -4
- package/dist/application/dag/generate-task-dag.js +25 -8
- package/dist/commands/dag-init-hybrid.js +3 -2
- package/dist/commands/delegate.js +11 -11
- package/dist/commands/init.js +29 -14
- package/dist/commands/instructions.js +1 -2
- package/dist/executors/dag-pi-executor.js +74 -13
- package/dist/executors/pi-executor.js +25 -7
- package/dist/executors/pi-prompt-transport.js +198 -0
- package/dist/executors/pi-sdk-executor.js +1 -1
- package/dist/executors/process-tree.js +33 -0
- package/dist/executors/shell-executor.js +362 -24
- package/dist/executors/shell-verification.js +4 -2
- package/dist/infrastructure/harness/task-store.js +31 -0
- package/dist/shared/operator/capabilities.js +6 -0
- package/dist/task/read-model.js +3 -7
- package/dist/task/runtime.js +12 -3
- package/dist/verification/maven/cache.js +142 -0
- package/dist/verification/maven/index.js +120 -0
- package/dist/verification/maven/plan-commands.js +421 -0
- package/dist/verification/maven/pom-static.js +136 -0
- package/dist/verification/maven/scope-resolve.js +153 -0
- package/dist/verification/maven/stale.js +130 -0
- package/dist/verification/maven/types.js +23 -0
- package/dist/verification/maven/workspace-graph.js +322 -0
- package/dist/worker/cli.js +236 -21
- package/dist/worker/delivery/final-verification.js +12 -0
- package/dist/worker/delivery/git-transaction.js +32 -7
- package/dist/worker/delivery/package.js +9 -5
- package/dist/worker/delivery/verification-bundle.js +26 -5
- package/dist/worker/feature/advance.js +301 -0
- package/dist/worker/feature/doctor.js +223 -0
- package/dist/worker/feature/next-action.js +11 -3
- package/dist/worker/feature/scaffold.js +798 -0
- package/dist/worker/observe/dag-run-artifacts.js +90 -0
- package/dist/worker/observe/node-input.js +444 -0
- package/dist/worker/observe/routes.js +17 -0
- package/dist/worker/observe/static/api.js +9 -0
- package/dist/worker/observe/static/constants.js +9 -0
- package/dist/worker/observe/static/state.js +14 -0
- package/dist/worker/observe/static/styles.css +74 -0
- package/dist/worker/observe/static/views/dag-inspector.js +371 -15
- package/dist/worker/outcomes/projector.js +5 -1
- package/dist/worker/run-task/run-task.js +18 -2
- package/dist/worker/runner/run-ready.js +41 -1
- package/dist/workflows/dag/backend-test-markdown-workflow.js +300 -9
- package/dist/workflows/dag/backend-test-result-contract.js +103 -0
- package/dist/workflows/dag/failure-category.js +5 -1
- package/dist/workflows/dag/frontend-implementation-contract.js +4 -0
- package/dist/workflows/dag/frontend-prewrite-gate.js +0 -17
- package/dist/workflows/dag/frontend-test-result-contract.js +106 -41
- package/dist/workflows/dag/init-hybrid.js +224 -39
- package/dist/workflows/dag/node-execution.js +3 -2
- package/dist/workflows/dag/reconcile-run.js +24 -0
- package/dist/workflows/dag/types.js +57 -0
- package/dist/workflows/dag/validate.js +19 -2
- package/dist/workflows/loop/actions/dag-action.js +2 -1
- package/docs/architecture/dag-execution.md +5 -1
- package/docs/architecture/runtime-boundaries.md +11 -1
- package/docs/architecture/worker-and-feature.md +2 -0
- package/docs/init-surface.manifest.json +3 -0
- package/docs/templates/backend-test-dag.json +3 -3
- package/docs/templates/product-line/README.md +17 -1
- package/docs/templates/product-line/feature-scaffold-batch.example.yaml +31 -0
- package/docs/templates/product-line/scaffold-samples/README.md +36 -0
- package/docs/templates/product-line/scaffold-samples/backend-only/acceptance.yaml +13 -0
- package/docs/templates/product-line/scaffold-samples/backend-only/design.md +14 -0
- package/docs/templates/product-line/scaffold-samples/backend-only/feature.yaml +3 -0
- package/docs/templates/product-line/scaffold-samples/backend-only/requirement.md +6 -0
- package/docs/templates/product-line/scaffold-samples/backend-only/tasks/BE-IMPL-001.yaml +83 -0
- package/docs/templates/product-line/scaffold-samples/backend-only/tasks/task-graph.yaml +14 -0
- package/docs/templates/product-line/scaffold-samples/fe-with-api/acceptance.yaml +15 -0
- package/docs/templates/product-line/scaffold-samples/fe-with-api/design.md +18 -0
- package/docs/templates/product-line/scaffold-samples/fe-with-api/feature.yaml +3 -0
- package/docs/templates/product-line/scaffold-samples/fe-with-api/requirement.md +6 -0
- package/docs/templates/product-line/scaffold-samples/fe-with-api/tasks/BE-IMPL-001.yaml +84 -0
- package/docs/templates/product-line/scaffold-samples/fe-with-api/tasks/CONTRACT-001.yaml +84 -0
- package/docs/templates/product-line/scaffold-samples/fe-with-api/tasks/FE-IMPL-001.yaml +86 -0
- package/docs/templates/product-line/scaffold-samples/fe-with-api/tasks/task-graph.yaml +40 -0
- package/docs/templates/product-line/scaffold-samples/frontend-only/acceptance.yaml +13 -0
- package/docs/templates/product-line/scaffold-samples/frontend-only/design.md +14 -0
- package/docs/templates/product-line/scaffold-samples/frontend-only/feature.yaml +3 -0
- package/docs/templates/product-line/scaffold-samples/frontend-only/requirement.md +6 -0
- package/docs/templates/product-line/scaffold-samples/frontend-only/tasks/FE-IMPL-001.yaml +85 -0
- package/docs/templates/product-line/scaffold-samples/frontend-only/tasks/task-graph.yaml +14 -0
- package/examples/l5-report-coms-process-definition.html +322 -0
- package/package.json +1 -1
- package/skills/agent-worker/references/agent-worker-operator.md +84 -0
- package/skills/frontend-implementation/references/node-contracts.md +2 -2
- package/skills/loop-agent/SKILL.md +14 -16
- package/skills/loop-agent/references/command-reference.md +33 -12
- package/skills/loop-agent/references/harness-policy.md +28 -6
- package/skills/loop-agent/references/hybrid-dag.md +8 -7
- package/skills/loop-agent/references/post-implementation-and-patterns.md +19 -9
- package/skills/loop-agent/references/source-and-plan-practice.md +161 -0
- package/skills/loop-agent/references/task-workflow.md +13 -5
|
@@ -2010,6 +2010,80 @@ body.is-resizing-dag-graph {
|
|
|
2010
2010
|
.spec-evidence-section { padding-bottom: var(--space-4); border-bottom: 1px solid var(--hairline); }
|
|
2011
2011
|
.spec-evidence-section + .spec-evidence-section { margin-top: var(--space-4); }
|
|
2012
2012
|
.spec-evidence-section h4 { margin: 0 0 var(--space-2); color: var(--ink); font-size: 12px; font-weight: 700; }
|
|
2013
|
+
|
|
2014
|
+
.node-input-root { display: flex; flex-direction: column; gap: var(--space-3); }
|
|
2015
|
+
.node-input-semantics {
|
|
2016
|
+
margin: 0;
|
|
2017
|
+
font-size: 12px;
|
|
2018
|
+
font-weight: 600;
|
|
2019
|
+
color: var(--muted);
|
|
2020
|
+
}
|
|
2021
|
+
.node-input-section {
|
|
2022
|
+
padding-bottom: var(--space-3);
|
|
2023
|
+
border-bottom: 1px solid var(--hairline);
|
|
2024
|
+
}
|
|
2025
|
+
.node-input-section h4 {
|
|
2026
|
+
margin: 0 0 var(--space-2);
|
|
2027
|
+
color: var(--ink);
|
|
2028
|
+
font-size: 12px;
|
|
2029
|
+
font-weight: 700;
|
|
2030
|
+
}
|
|
2031
|
+
.node-input-section h5 {
|
|
2032
|
+
margin: var(--space-2) 0;
|
|
2033
|
+
font-size: 12px;
|
|
2034
|
+
font-weight: 600;
|
|
2035
|
+
}
|
|
2036
|
+
.node-input-summary {
|
|
2037
|
+
display: grid;
|
|
2038
|
+
grid-template-columns: max-content 1fr;
|
|
2039
|
+
gap: 4px 12px;
|
|
2040
|
+
margin: 0;
|
|
2041
|
+
font-size: 12px;
|
|
2042
|
+
}
|
|
2043
|
+
.node-input-summary dt {
|
|
2044
|
+
margin: 0;
|
|
2045
|
+
color: var(--muted);
|
|
2046
|
+
font-weight: 600;
|
|
2047
|
+
}
|
|
2048
|
+
.node-input-summary dd {
|
|
2049
|
+
margin: 0;
|
|
2050
|
+
color: var(--ink);
|
|
2051
|
+
word-break: break-word;
|
|
2052
|
+
}
|
|
2053
|
+
.node-input-inherited {
|
|
2054
|
+
display: inline-block;
|
|
2055
|
+
margin-left: 4px;
|
|
2056
|
+
padding: 0 6px;
|
|
2057
|
+
border-radius: 999px;
|
|
2058
|
+
background: var(--surface-2, #f3f1ec);
|
|
2059
|
+
color: var(--muted);
|
|
2060
|
+
font-size: 11px;
|
|
2061
|
+
font-weight: 600;
|
|
2062
|
+
}
|
|
2063
|
+
.node-input-code-list,
|
|
2064
|
+
.node-input-command-list {
|
|
2065
|
+
margin: var(--space-2) 0 0;
|
|
2066
|
+
padding-left: 1.2rem;
|
|
2067
|
+
}
|
|
2068
|
+
.node-input-command-list pre {
|
|
2069
|
+
margin: 4px 0;
|
|
2070
|
+
padding: 8px;
|
|
2071
|
+
overflow: auto;
|
|
2072
|
+
background: var(--surface-2, #f3f1ec);
|
|
2073
|
+
border-radius: 6px;
|
|
2074
|
+
font-size: 12px;
|
|
2075
|
+
}
|
|
2076
|
+
.node-input-truncated,
|
|
2077
|
+
.node-input-warning {
|
|
2078
|
+
margin: var(--space-2) 0 0;
|
|
2079
|
+
font-size: 12px;
|
|
2080
|
+
color: var(--muted);
|
|
2081
|
+
}
|
|
2082
|
+
.node-input-error {
|
|
2083
|
+
margin: 0;
|
|
2084
|
+
color: var(--danger, #b42318);
|
|
2085
|
+
font-size: 13px;
|
|
2086
|
+
}
|
|
2013
2087
|
.spec-evidence-summary { margin: var(--space-2) 0 0; color: var(--body); font-size: 12px; line-height: 1.6; }
|
|
2014
2088
|
.spec-evidence-list { display: grid; gap: 6px; margin: var(--space-2) 0 0; padding: 0; list-style: none; }
|
|
2015
2089
|
.spec-evidence-list li { display: flex; align-items: flex-start; gap: 6px; color: var(--body); font-size: 12px; line-height: 1.5; overflow-wrap: anywhere; }
|
|
@@ -10,12 +10,13 @@ import {
|
|
|
10
10
|
UI_TEXT,
|
|
11
11
|
} from "../constants.js";
|
|
12
12
|
import { clearNode, el } from "../dom.js";
|
|
13
|
-
import { fetchJson, fetchSpecEvidenceFile } from "../api.js";
|
|
13
|
+
import { fetchJson, fetchSpecEvidenceFile, fetchDagNodeInput } from "../api.js";
|
|
14
14
|
import { badge } from "../format.js";
|
|
15
15
|
import { renderMarkdown } from "../markdown-render.js";
|
|
16
16
|
import {
|
|
17
17
|
uiState,
|
|
18
18
|
makeSessionEventIdentity,
|
|
19
|
+
makeDagNodeInputIdentity,
|
|
19
20
|
openSpecEvidenceDetail,
|
|
20
21
|
closeSpecEvidenceDetail,
|
|
21
22
|
updateDagTimelineViewportState,
|
|
@@ -50,9 +51,13 @@ export function selectDagNode(dagRunId, nodeId) {
|
|
|
50
51
|
// into node B's spec-evidence panel (AC-007 regression guard).
|
|
51
52
|
uiState.specEvidenceDetail = null;
|
|
52
53
|
uiState.specEvidenceListEvidence = null;
|
|
54
|
+
uiState.dagNodeInputData = null;
|
|
55
|
+
uiState.dagNodeInputError = null;
|
|
56
|
+
uiState.dagNodeInputLoading = false;
|
|
53
57
|
// Bump the session-event identity token so any in-flight response for the
|
|
54
58
|
// previous node is rejected before it can mutate the current node cache.
|
|
55
59
|
uiState.sessionEventIdentity = makeSessionEventIdentity(dagRunId, nodeId);
|
|
60
|
+
uiState.dagNodeInputIdentity = makeDagNodeInputIdentity(dagRunId, nodeId);
|
|
56
61
|
}
|
|
57
62
|
void import("./dag.js").then((m) => m.renderDagDetail(dagRunId, false));
|
|
58
63
|
}
|
|
@@ -530,16 +535,337 @@ function renderSpecEvidenceDetail(content, dagRunId, nodeId) {
|
|
|
530
535
|
content.appendChild(wrapper);
|
|
531
536
|
}
|
|
532
537
|
|
|
538
|
+
function sourceLabel(source) {
|
|
539
|
+
if (source === "defaults") return "继承默认值";
|
|
540
|
+
if (source === "schema-default") return "schema 默认";
|
|
541
|
+
if (source === "merged") return "合并";
|
|
542
|
+
return null;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
function appendMetaRow(dl, label, value, source) {
|
|
546
|
+
if (value == null || value === "") return;
|
|
547
|
+
const dt = el("dt", null, label);
|
|
548
|
+
const dd = el("dd", null, String(value));
|
|
549
|
+
const inherited = sourceLabel(source);
|
|
550
|
+
if (inherited) {
|
|
551
|
+
dd.appendChild(document.createTextNode(" "));
|
|
552
|
+
dd.appendChild(el("span", "node-input-inherited", inherited));
|
|
553
|
+
}
|
|
554
|
+
dl.appendChild(dt);
|
|
555
|
+
dl.appendChild(dd);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
function appendCodeList(parent, title, values) {
|
|
559
|
+
if (!values || values.length === 0) return;
|
|
560
|
+
const section = el("section", "node-input-section");
|
|
561
|
+
section.appendChild(el("h4", null, title));
|
|
562
|
+
const list = el("ul", "node-input-code-list");
|
|
563
|
+
for (const value of values) {
|
|
564
|
+
const item = document.createElement("li");
|
|
565
|
+
const code = document.createElement("code");
|
|
566
|
+
code.textContent = String(value);
|
|
567
|
+
item.appendChild(code);
|
|
568
|
+
list.appendChild(item);
|
|
569
|
+
}
|
|
570
|
+
section.appendChild(list);
|
|
571
|
+
parent.appendChild(section);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
function renderDagNodeInputDetail(content, data) {
|
|
575
|
+
const wrap = el("div", "node-input-root");
|
|
576
|
+
wrap.appendChild(
|
|
577
|
+
el("p", "node-input-semantics", UI_TEXT.inputFrozenLabel),
|
|
578
|
+
);
|
|
579
|
+
|
|
580
|
+
if (data.summary) {
|
|
581
|
+
const section = el("section", "node-input-section");
|
|
582
|
+
section.appendChild(el("h4", null, "摘要"));
|
|
583
|
+
const dl = el("dl", "node-input-summary");
|
|
584
|
+
appendMetaRow(
|
|
585
|
+
dl,
|
|
586
|
+
"executor",
|
|
587
|
+
data.summary.executor,
|
|
588
|
+
data.summary.executorSource,
|
|
589
|
+
);
|
|
590
|
+
appendMetaRow(dl, "role", data.summary.role, null);
|
|
591
|
+
appendMetaRow(dl, "complexity", data.summary.complexity, null);
|
|
592
|
+
appendMetaRow(
|
|
593
|
+
dl,
|
|
594
|
+
"writePolicy",
|
|
595
|
+
data.summary.writePolicy,
|
|
596
|
+
data.summary.writePolicySource,
|
|
597
|
+
);
|
|
598
|
+
appendMetaRow(dl, "toolProfile", data.summary.toolProfile, null);
|
|
599
|
+
section.appendChild(dl);
|
|
600
|
+
wrap.appendChild(section);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
if (data.dependency) {
|
|
604
|
+
const section = el("section", "node-input-section");
|
|
605
|
+
section.appendChild(el("h4", null, "依赖"));
|
|
606
|
+
const dl = el("dl", "node-input-summary");
|
|
607
|
+
appendMetaRow(
|
|
608
|
+
dl,
|
|
609
|
+
"depends_on",
|
|
610
|
+
(data.dependency.dependsOn ?? []).join(", ") || "(none)",
|
|
611
|
+
null,
|
|
612
|
+
);
|
|
613
|
+
if (data.dependency.dependsPolicy) {
|
|
614
|
+
appendMetaRow(dl, "dependsPolicy", data.dependency.dependsPolicy, null);
|
|
615
|
+
}
|
|
616
|
+
if ((data.dependency.failureAwareDependsOn ?? []).length > 0) {
|
|
617
|
+
appendMetaRow(
|
|
618
|
+
dl,
|
|
619
|
+
"failureAwareDependsOn",
|
|
620
|
+
data.dependency.failureAwareDependsOn.join(", "),
|
|
621
|
+
null,
|
|
622
|
+
);
|
|
623
|
+
}
|
|
624
|
+
if (data.dependency.runIf) {
|
|
625
|
+
appendMetaRow(dl, "runIf", data.dependency.runIf, null);
|
|
626
|
+
}
|
|
627
|
+
section.appendChild(dl);
|
|
628
|
+
wrap.appendChild(section);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
if (data.prompt) {
|
|
632
|
+
const section = el("section", "node-input-section");
|
|
633
|
+
section.appendChild(el("h4", null, "任务正文"));
|
|
634
|
+
if (data.prompt.source?.path) {
|
|
635
|
+
const meta = el("p", "muted");
|
|
636
|
+
meta.textContent = `source: ${data.prompt.source.path}${
|
|
637
|
+
data.prompt.source.sha256Prefix
|
|
638
|
+
? ` (sha256 ${data.prompt.source.sha256Prefix}…)`
|
|
639
|
+
: ""
|
|
640
|
+
}`;
|
|
641
|
+
section.appendChild(meta);
|
|
642
|
+
}
|
|
643
|
+
section.appendChild(renderMarkdown(data.prompt.text ?? ""));
|
|
644
|
+
if (data.prompt.truncated) {
|
|
645
|
+
section.appendChild(
|
|
646
|
+
el(
|
|
647
|
+
"p",
|
|
648
|
+
"node-input-truncated",
|
|
649
|
+
`已截断,仅显示前 ${data.prompt.maxBytes ?? 0} 字节(UTF-8)。`,
|
|
650
|
+
),
|
|
651
|
+
);
|
|
652
|
+
}
|
|
653
|
+
wrap.appendChild(section);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
if (data.executorInput) {
|
|
657
|
+
const section = el("section", "node-input-section");
|
|
658
|
+
section.appendChild(el("h4", null, "执行器输入摘要"));
|
|
659
|
+
const dl = el("dl", "node-input-summary");
|
|
660
|
+
appendMetaRow(dl, "kind", data.executorInput.kind, null);
|
|
661
|
+
if (data.executorInput.modelHint) {
|
|
662
|
+
appendMetaRow(dl, "modelHint", data.executorInput.modelHint, null);
|
|
663
|
+
}
|
|
664
|
+
section.appendChild(dl);
|
|
665
|
+
appendCodeList(section, "skills", data.executorInput.skills);
|
|
666
|
+
if (data.executorInput.shell) {
|
|
667
|
+
const shell = data.executorInput.shell;
|
|
668
|
+
const shellDl = el("dl", "node-input-summary");
|
|
669
|
+
appendMetaRow(shellDl, "preset", shell.preset, null);
|
|
670
|
+
appendMetaRow(shellDl, "cwd", shell.cwd, null);
|
|
671
|
+
appendMetaRow(shellDl, "timeoutMs", shell.timeoutMs, null);
|
|
672
|
+
appendMetaRow(
|
|
673
|
+
shellDl,
|
|
674
|
+
"nonZeroExitPolicy",
|
|
675
|
+
shell.nonZeroExitPolicy,
|
|
676
|
+
null,
|
|
677
|
+
);
|
|
678
|
+
appendMetaRow(
|
|
679
|
+
shellDl,
|
|
680
|
+
"gates",
|
|
681
|
+
(shell.gates ?? []).join(", ") || "(none)",
|
|
682
|
+
null,
|
|
683
|
+
);
|
|
684
|
+
section.appendChild(shellDl);
|
|
685
|
+
if ((shell.commands ?? []).length > 0) {
|
|
686
|
+
const list = el("ol", "node-input-command-list");
|
|
687
|
+
for (const command of shell.commands) {
|
|
688
|
+
const item = document.createElement("li");
|
|
689
|
+
const pre = document.createElement("pre");
|
|
690
|
+
const code = document.createElement("code");
|
|
691
|
+
code.textContent = String(command);
|
|
692
|
+
pre.appendChild(code);
|
|
693
|
+
item.appendChild(pre);
|
|
694
|
+
list.appendChild(item);
|
|
695
|
+
}
|
|
696
|
+
section.appendChild(list);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
if (data.executorInput.static) {
|
|
700
|
+
const staticBlock = data.executorInput.static;
|
|
701
|
+
const staticDl = el("dl", "node-input-summary");
|
|
702
|
+
appendMetaRow(staticDl, "status", staticBlock.status, null);
|
|
703
|
+
section.appendChild(staticDl);
|
|
704
|
+
section.appendChild(renderMarkdown(staticBlock.resultPreview ?? ""));
|
|
705
|
+
if (staticBlock.truncated) {
|
|
706
|
+
section.appendChild(
|
|
707
|
+
el(
|
|
708
|
+
"p",
|
|
709
|
+
"node-input-truncated",
|
|
710
|
+
`已截断,仅显示前 ${staticBlock.maxBytes ?? 0} 字节(UTF-8)。`,
|
|
711
|
+
),
|
|
712
|
+
);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
wrap.appendChild(section);
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
if (data.boundaries) {
|
|
719
|
+
const section = el("section", "node-input-section");
|
|
720
|
+
section.appendChild(el("h4", null, "边界与输出契约"));
|
|
721
|
+
const dl = el("dl", "node-input-summary");
|
|
722
|
+
appendMetaRow(dl, "outputMode", data.boundaries.outputMode, null);
|
|
723
|
+
appendMetaRow(
|
|
724
|
+
dl,
|
|
725
|
+
"firstProtocolLine",
|
|
726
|
+
data.boundaries.firstProtocolLine,
|
|
727
|
+
null,
|
|
728
|
+
);
|
|
729
|
+
section.appendChild(dl);
|
|
730
|
+
if (data.boundaries.outputContract) {
|
|
731
|
+
section.appendChild(el("h5", null, "outputContract"));
|
|
732
|
+
section.appendChild(renderMarkdown(data.boundaries.outputContract));
|
|
733
|
+
}
|
|
734
|
+
appendCodeList(section, "allowedPaths", data.boundaries.allowedPaths);
|
|
735
|
+
appendCodeList(section, "forbiddenPaths", data.boundaries.forbiddenPaths);
|
|
736
|
+
appendCodeList(section, "writeSet", data.boundaries.writeSet);
|
|
737
|
+
wrap.appendChild(section);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
if (data.promptFingerprint?.exists) {
|
|
741
|
+
const section = el("section", "node-input-section");
|
|
742
|
+
section.appendChild(el("h4", null, "Assembled prompt 指纹"));
|
|
743
|
+
const dl = el("dl", "node-input-summary");
|
|
744
|
+
appendMetaRow(
|
|
745
|
+
dl,
|
|
746
|
+
"sha256",
|
|
747
|
+
data.promptFingerprint.sha256Prefix ?? "(parse failed)",
|
|
748
|
+
null,
|
|
749
|
+
);
|
|
750
|
+
appendMetaRow(
|
|
751
|
+
dl,
|
|
752
|
+
"lengthChars",
|
|
753
|
+
data.promptFingerprint.lengthChars,
|
|
754
|
+
null,
|
|
755
|
+
);
|
|
756
|
+
appendMetaRow(dl, "artifact", data.promptFingerprint.artifact, null);
|
|
757
|
+
section.appendChild(dl);
|
|
758
|
+
section.appendChild(
|
|
759
|
+
el("p", "muted", UI_TEXT.inputFingerprintNote),
|
|
760
|
+
);
|
|
761
|
+
wrap.appendChild(section);
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
if ((data.warnings ?? []).length > 0) {
|
|
765
|
+
const section = el("section", "node-input-section");
|
|
766
|
+
section.appendChild(el("h4", null, "警告"));
|
|
767
|
+
for (const warning of data.warnings) {
|
|
768
|
+
section.appendChild(el("p", "node-input-warning", String(warning)));
|
|
769
|
+
}
|
|
770
|
+
wrap.appendChild(section);
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
content.appendChild(wrap);
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
function unavailableMessage(reason) {
|
|
777
|
+
if (reason === "unsupported-dynamic-node") {
|
|
778
|
+
return UI_TEXT.inputDynamicUnsupported;
|
|
779
|
+
}
|
|
780
|
+
if (reason === "run-spec-unavailable") {
|
|
781
|
+
return UI_TEXT.inputRunSpecUnavailable;
|
|
782
|
+
}
|
|
783
|
+
if (reason === "task-not-found") {
|
|
784
|
+
return UI_TEXT.inputUnavailable;
|
|
785
|
+
}
|
|
786
|
+
return UI_TEXT.noInput;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
async function renderDagNodeInput(content, dagRunId, node) {
|
|
790
|
+
const nodeId = node.nodeId;
|
|
791
|
+
const identity = makeDagNodeInputIdentity(dagRunId, nodeId);
|
|
792
|
+
uiState.dagNodeInputIdentity = identity;
|
|
793
|
+
uiState.dagNodeInputLoading = true;
|
|
794
|
+
uiState.dagNodeInputError = null;
|
|
795
|
+
|
|
796
|
+
clearNode(content);
|
|
797
|
+
const loading = el("p", "muted", UI_TEXT.inputLoading);
|
|
798
|
+
loading.setAttribute("role", "status");
|
|
799
|
+
loading.setAttribute("aria-live", "polite");
|
|
800
|
+
content.appendChild(loading);
|
|
801
|
+
|
|
802
|
+
const result = await fetchDagNodeInput(dagRunId, nodeId);
|
|
803
|
+
if (
|
|
804
|
+
uiState.dagNodeInputIdentity !== identity ||
|
|
805
|
+
uiState.selectedDagNodeId !== nodeId ||
|
|
806
|
+
uiState.currentDagRunId !== dagRunId ||
|
|
807
|
+
uiState.dagInspectorTab !== "input" ||
|
|
808
|
+
!content.isConnected ||
|
|
809
|
+
content.dataset.dagNodeId !== String(nodeId) ||
|
|
810
|
+
content.dataset.dagRunId !== String(dagRunId)
|
|
811
|
+
) {
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
uiState.dagNodeInputLoading = false;
|
|
816
|
+
clearNode(content);
|
|
817
|
+
|
|
818
|
+
if (!result.ok) {
|
|
819
|
+
const message =
|
|
820
|
+
result.body?.error ||
|
|
821
|
+
(result.status === 0
|
|
822
|
+
? UI_TEXT.inputError
|
|
823
|
+
: `${UI_TEXT.inputError}(HTTP ${result.status})`);
|
|
824
|
+
uiState.dagNodeInputError = message;
|
|
825
|
+
uiState.dagNodeInputData = null;
|
|
826
|
+
const error = el("p", "node-input-error", message);
|
|
827
|
+
error.setAttribute("role", "alert");
|
|
828
|
+
content.appendChild(error);
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
const data = result.body;
|
|
833
|
+
uiState.dagNodeInputData = data;
|
|
834
|
+
uiState.dagNodeInputError = null;
|
|
835
|
+
if (!data?.available) {
|
|
836
|
+
const empty = el(
|
|
837
|
+
"p",
|
|
838
|
+
"empty",
|
|
839
|
+
unavailableMessage(data?.availabilityReason),
|
|
840
|
+
);
|
|
841
|
+
empty.setAttribute("role", "status");
|
|
842
|
+
content.appendChild(empty);
|
|
843
|
+
if ((data?.warnings ?? []).length > 0) {
|
|
844
|
+
for (const warning of data.warnings) {
|
|
845
|
+
content.appendChild(el("p", "node-input-warning", String(warning)));
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
return;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
renderDagNodeInputDetail(content, data);
|
|
852
|
+
}
|
|
853
|
+
|
|
533
854
|
function fillDagInspectorContent(content, dagRunId, node) {
|
|
534
855
|
const previousRenderedNode = content.dataset.dagNodeId ?? "";
|
|
535
|
-
const
|
|
856
|
+
const previousRenderedRun = content.dataset.dagRunId ?? "";
|
|
857
|
+
const nodeChanged =
|
|
858
|
+
previousRenderedNode !== String(node.nodeId ?? "") ||
|
|
859
|
+
previousRenderedRun !== String(dagRunId ?? "");
|
|
536
860
|
if (nodeChanged) {
|
|
537
861
|
// Drop any open spec-evidence preview + cached list so a stale A detail
|
|
538
862
|
// cannot survive into node B (AC-007 regression guard).
|
|
539
863
|
uiState.specEvidenceDetail = null;
|
|
540
864
|
uiState.specEvidenceListEvidence = null;
|
|
865
|
+
uiState.dagNodeInputData = null;
|
|
866
|
+
uiState.dagNodeInputError = null;
|
|
541
867
|
}
|
|
542
|
-
const activeTab = ["timeline", "spec-evidence"].includes(
|
|
868
|
+
const activeTab = ["input", "timeline", "spec-evidence"].includes(
|
|
543
869
|
uiState.dagInspectorTab,
|
|
544
870
|
)
|
|
545
871
|
? uiState.dagInspectorTab
|
|
@@ -548,12 +874,15 @@ function renderSpecEvidenceDetail(content, dagRunId, nodeId) {
|
|
|
548
874
|
content.setAttribute("role", "tabpanel");
|
|
549
875
|
content.setAttribute("aria-labelledby", `dag-inspector-tab-${activeTab}`);
|
|
550
876
|
content.dataset.dagNodeId = String(node.nodeId ?? "");
|
|
877
|
+
content.dataset.dagRunId = String(dagRunId ?? "");
|
|
551
878
|
// Drop timeline-only classes when switching tabs so output layout stays clean.
|
|
552
879
|
content.className = "dag-inspector-content";
|
|
553
880
|
if (activeTab === "spec-evidence") {
|
|
554
881
|
void renderSpecEvidence(content, dagRunId, node.nodeId);
|
|
555
882
|
} else if (activeTab === "timeline") {
|
|
556
883
|
renderSessionTimeline(content, node.nodeId);
|
|
884
|
+
} else if (activeTab === "input") {
|
|
885
|
+
void renderDagNodeInput(content, dagRunId, node);
|
|
557
886
|
} else if (!node.outputPreview && !node.errorPreview) {
|
|
558
887
|
content.appendChild(el("p", "empty", UI_TEXT.noOutput));
|
|
559
888
|
} else {
|
|
@@ -592,6 +921,7 @@ function buildDagInspectorHeader(dagRunId, node) {
|
|
|
592
921
|
header.appendChild(controls);
|
|
593
922
|
|
|
594
923
|
const tabDefs = [
|
|
924
|
+
["input", "节点输入", "ri-login-box-line"],
|
|
595
925
|
["output", "节点输出", "ri-file-text-line"],
|
|
596
926
|
["timeline", "执行过程", "ri-route-line"],
|
|
597
927
|
["spec-evidence", "规范证据", "ri-book-open-line"],
|
|
@@ -660,6 +990,17 @@ function syncDagInspectorHeader(panel, dagRunId, node) {
|
|
|
660
990
|
}
|
|
661
991
|
|
|
662
992
|
function dagInspectorSignature(dagRunId, node) {
|
|
993
|
+
const inputRevision =
|
|
994
|
+
uiState.dagInspectorTab === "input"
|
|
995
|
+
? [
|
|
996
|
+
uiState.dagNodeInputIdentity,
|
|
997
|
+
uiState.dagNodeInputLoading,
|
|
998
|
+
uiState.dagNodeInputError ?? "",
|
|
999
|
+
uiState.dagNodeInputData?.availabilityReason ?? "",
|
|
1000
|
+
uiState.dagNodeInputData?.promptFingerprint?.sha256Prefix ?? "",
|
|
1001
|
+
node.status ?? "",
|
|
1002
|
+
]
|
|
1003
|
+
: null;
|
|
663
1004
|
return JSON.stringify([
|
|
664
1005
|
dagRunId,
|
|
665
1006
|
node.nodeId,
|
|
@@ -667,6 +1008,7 @@ function dagInspectorSignature(dagRunId, node) {
|
|
|
667
1008
|
node.outputPreview ?? "",
|
|
668
1009
|
node.errorPreview ?? "",
|
|
669
1010
|
uiState.dagInspectorTab === "timeline" ? uiState.sessionEventOffset : null,
|
|
1011
|
+
inputRevision,
|
|
670
1012
|
]);
|
|
671
1013
|
}
|
|
672
1014
|
|
|
@@ -702,17 +1044,30 @@ export function renderDagInspector(dagRunId, node, initial = true) {
|
|
|
702
1044
|
syncDagInspectorHeader(panel, dagRunId, node);
|
|
703
1045
|
if (panel.dataset.contentSignature === nextContentSignature) return;
|
|
704
1046
|
const previousNode = existingContent.dataset.dagNodeId ?? "";
|
|
705
|
-
const
|
|
706
|
-
const
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
1047
|
+
const previousRun = existingContent.dataset.dagRunId ?? "";
|
|
1048
|
+
const nodeChanged =
|
|
1049
|
+
previousNode !== String(node.nodeId ?? "") ||
|
|
1050
|
+
previousRun !== String(dagRunId ?? "");
|
|
1051
|
+
const activeTab = ["input", "timeline", "spec-evidence"].includes(
|
|
1052
|
+
uiState.dagInspectorTab,
|
|
1053
|
+
)
|
|
1054
|
+
? uiState.dagInspectorTab
|
|
1055
|
+
: "output";
|
|
1056
|
+
const isInputTab = activeTab === "input";
|
|
1057
|
+
const followLatest = isInputTab
|
|
1058
|
+
? false
|
|
1059
|
+
: nodeChanged ||
|
|
1060
|
+
computeFollowLatest(
|
|
1061
|
+
existingContent.scrollTop,
|
|
1062
|
+
existingContent.scrollHeight,
|
|
1063
|
+
existingContent.clientHeight,
|
|
1064
|
+
);
|
|
1065
|
+
const regionKey = `dag:${dagRunId}:${node.nodeId}:${isInputTab ? "input" : "output"}`;
|
|
1066
|
+
const viewportUpdater = isInputTab
|
|
1067
|
+
? "dagNodeInputViewportState"
|
|
1068
|
+
: "dagNodeOutputViewportState";
|
|
1069
|
+
uiState[viewportUpdater] = updateDagOutputViewportState(
|
|
1070
|
+
uiState[viewportUpdater],
|
|
716
1071
|
regionKey,
|
|
717
1072
|
existingContent.scrollLeft,
|
|
718
1073
|
existingContent.scrollTop,
|
|
@@ -729,7 +1084,8 @@ export function renderDagInspector(dagRunId, node, initial = true) {
|
|
|
729
1084
|
if (followLatest) {
|
|
730
1085
|
existingContent.scrollTop = existingContent.scrollHeight;
|
|
731
1086
|
} else {
|
|
732
|
-
|
|
1087
|
+
const saved = uiState[viewportUpdater];
|
|
1088
|
+
existingContent.scrollTop = nodeChanged ? 0 : (saved?.scrollTop ?? 0);
|
|
733
1089
|
}
|
|
734
1090
|
for (const [index, pre] of [
|
|
735
1091
|
...existingContent.querySelectorAll("pre"),
|
|
@@ -111,8 +111,12 @@ export async function projectOutcome(input) {
|
|
|
111
111
|
if (artifact.sha256 && artifact.sha256 !== recomputed) {
|
|
112
112
|
return contractError(`artifact sha256 mismatch for ${artifact.path}: declared=${artifact.sha256} actual=${recomputed}`);
|
|
113
113
|
}
|
|
114
|
+
// Persist portable repo-relative paths only (Delivery verifyArtifactRefs rejects abs paths).
|
|
115
|
+
const relativePath = path
|
|
116
|
+
.relative(path.resolve(input.repoRoot), resolved)
|
|
117
|
+
.replace(/\\/g, "/");
|
|
114
118
|
validatedArtifacts.push({
|
|
115
|
-
path:
|
|
119
|
+
path: relativePath,
|
|
116
120
|
sha256: recomputed,
|
|
117
121
|
...(artifact.kind ? { kind: artifact.kind } : {}),
|
|
118
122
|
...(artifact.schemaId ? { schemaId: artifact.schemaId } : {}),
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { writeTextAtomic } from "../../infrastructure/harness/atomic-write.js";
|
|
5
|
+
import { getTaskPaths } from "../../task/runtime.js";
|
|
4
6
|
import { formatDuration, noopProgressReporter } from "../progress-reporter.js";
|
|
5
7
|
import { redactForPreview, truncatePreview } from "../observability/events.js";
|
|
6
8
|
import { controllerIdentityExpectationFailure, resolveControllerIdentity, } from "../loop-agent/loop-agent-client.js";
|
|
@@ -104,6 +106,13 @@ export async function runTaskSpec(options) {
|
|
|
104
106
|
], true, undefined, eventCtx);
|
|
105
107
|
});
|
|
106
108
|
progress.step(`dag validated in ${formatDuration(Date.now() - validateStartedAt)}`);
|
|
109
|
+
const canonicalDraftPath = getTaskPaths(options.repoRoot, materializeManifest.harnessTaskId).dagDraftPath;
|
|
110
|
+
await runObservedStep(eventCtx, "publish-canonical-dag-draft", async () => {
|
|
111
|
+
const dagBytes = await readFile(dagPath, "utf-8");
|
|
112
|
+
await writeTextAtomic(canonicalDraftPath, dagBytes, {
|
|
113
|
+
repoRoot: options.repoRoot,
|
|
114
|
+
});
|
|
115
|
+
});
|
|
107
116
|
const runDagStep = "run-dag";
|
|
108
117
|
const runDagStartedAt = Date.now();
|
|
109
118
|
const dagEventsPath = path.join(getTaskPoolRoot(options.repoRoot), "observability", "runs", workerRunId, "dag-events.jsonl");
|
|
@@ -330,7 +339,7 @@ export function buildWorkerRunId(businessId, now) {
|
|
|
330
339
|
.slice(0, 6);
|
|
331
340
|
return `wr-${date}-${businessId}-${hash}`;
|
|
332
341
|
}
|
|
333
|
-
async function runRequiredCommand(repoRoot, client, artifactName, args, expectJson = true, timeoutMs,
|
|
342
|
+
async function runRequiredCommand(repoRoot, client, artifactName, args, expectJson = true, timeoutMs, _eventCtx) {
|
|
334
343
|
const result = await client.run(args, {
|
|
335
344
|
cwd: repoRoot,
|
|
336
345
|
artifactName,
|
|
@@ -743,7 +752,14 @@ function strictModelsArgs(piModel) {
|
|
|
743
752
|
*/
|
|
744
753
|
export async function applyPiModelOverride(dagPath, piModel) {
|
|
745
754
|
const raw = await readFile(dagPath, "utf-8");
|
|
746
|
-
|
|
755
|
+
let spec;
|
|
756
|
+
try {
|
|
757
|
+
spec = JSON.parse(raw);
|
|
758
|
+
}
|
|
759
|
+
catch (error) {
|
|
760
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
761
|
+
throw new Error(`invalid generated DAG JSON at ${dagPath}: ${message}`);
|
|
762
|
+
}
|
|
747
763
|
spec.executorModels = {
|
|
748
764
|
pi: {
|
|
749
765
|
LOW: piModel,
|
|
@@ -304,11 +304,51 @@ export async function runReadyTasks(options) {
|
|
|
304
304
|
if (result.status !== "succeeded") {
|
|
305
305
|
await options.onTaskFinalized?.({ status: result.status, taskSpec, workerRunId: result.workerRunId, runRecordPath: result.runRecordPath });
|
|
306
306
|
}
|
|
307
|
+
const recordErrorMessage = errorMessage(error);
|
|
308
|
+
// Always project a Pool run fact for record-error so operators can
|
|
309
|
+
// mark-failed / retry without hand-editing Running state.
|
|
310
|
+
try {
|
|
311
|
+
await recordTaskPoolRun({
|
|
312
|
+
repoRoot: options.repoRoot,
|
|
313
|
+
run: {
|
|
314
|
+
schemaVersion: 1,
|
|
315
|
+
batchRunId,
|
|
316
|
+
workerRunId: result.workerRunId,
|
|
317
|
+
taskId: result.businessId,
|
|
318
|
+
featureId: taskSpec.feature_id,
|
|
319
|
+
status: "run-error",
|
|
320
|
+
recordedAt: new Date().toISOString(),
|
|
321
|
+
error: recordErrorMessage,
|
|
322
|
+
runRecordPath: result.runRecordPath,
|
|
323
|
+
...(result.workflow ? { workflow: result.workflow } : {}),
|
|
324
|
+
...(controllerIdentity ? { controllerIdentity } : {}),
|
|
325
|
+
...(retryOfWorkerRunId ? { retryOfWorkerRunId } : {}),
|
|
326
|
+
},
|
|
327
|
+
});
|
|
328
|
+
await writeTaskPoolState(options.repoRoot, {
|
|
329
|
+
schemaVersion: 2,
|
|
330
|
+
featureId: taskSpec.feature_id,
|
|
331
|
+
taskId,
|
|
332
|
+
status: "Failed",
|
|
333
|
+
updatedAt: new Date().toISOString(),
|
|
334
|
+
workerRunId: result.workerRunId,
|
|
335
|
+
lastRunRecordPath: result.runRecordPath,
|
|
336
|
+
failure: {
|
|
337
|
+
category: "EnvFailure",
|
|
338
|
+
recommendedFollowUpKind: "manual-review",
|
|
339
|
+
derivedFollowUpTaskId: `${taskId}-record-error-review`,
|
|
340
|
+
source: "report-decision",
|
|
341
|
+
},
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
catch {
|
|
345
|
+
// Best-effort; batch still reports record-error.
|
|
346
|
+
}
|
|
307
347
|
tasks.push({
|
|
308
348
|
taskId,
|
|
309
349
|
workerRunId: result.workerRunId,
|
|
310
350
|
status: "record-error",
|
|
311
|
-
error:
|
|
351
|
+
error: recordErrorMessage,
|
|
312
352
|
runRecordPath: result.runRecordPath,
|
|
313
353
|
});
|
|
314
354
|
break;
|