@tea-agent/loop-agent 0.7.5 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/AGENTS.md +145 -142
  2. package/CHANGELOG.md +161 -158
  3. package/README.md +206 -204
  4. package/bin/agent-worker.js +22 -22
  5. package/bin/loop-agent.js +21 -21
  6. package/dist/application/dag/args.js +9 -2
  7. package/dist/commands/init.js +518 -488
  8. package/dist/commands/loop-benchmark.js +11 -11
  9. package/dist/commands/pi-reuse-benchmark.js +16 -16
  10. package/dist/executors/cursor-executor.js +1 -1
  11. package/dist/executors/shell-executor.js +74 -8
  12. package/dist/governance/manifest-types.js +1 -1
  13. package/dist/shared/reference-context.js +48 -22
  14. package/dist/task/config-types.js +1 -1
  15. package/dist/task/runtime.js +28 -28
  16. package/dist/worker/cli.js +3 -3
  17. package/dist/worker/loop-agent/loop-agent-client.js +51 -10
  18. package/dist/worker/observability/event-store.js +2 -1
  19. package/dist/worker/observability/read-model.js +13 -11
  20. package/dist/worker/observe/paths.js +2 -2
  21. package/dist/worker/observe/routes.js +82 -23
  22. package/dist/worker/observe/server.js +8 -6
  23. package/dist/worker/observe/static/app.js +1965 -1480
  24. package/dist/worker/observe/static/dag-layout.d.ts +31 -31
  25. package/dist/worker/observe/static/dag-layout.js +83 -83
  26. package/dist/worker/observe/static/index.html +88 -63
  27. package/dist/worker/observe/static/styles.css +598 -722
  28. package/dist/worker/pool/run-store.js +7 -8
  29. package/dist/worker/run-task/run-task.js +11 -2
  30. package/dist/worker/runner/run-ready.js +1 -1
  31. package/dist/workflows/dag/canvas-observer.js +275 -275
  32. package/docs/README.md +84 -79
  33. package/docs/agent-dag-recovery-playbook.md +184 -184
  34. package/docs/agent-dag-runner.md +42 -42
  35. package/docs/architecture/runtime-boundaries.md +162 -162
  36. package/docs/cursor-executor-usage.md +25 -25
  37. package/docs/decisions/README.md +3 -3
  38. package/docs/design/README.md +49 -49
  39. package/docs/development-principles.md +73 -73
  40. package/docs/dynamic-workflow-dag-engine-roadmap.md +1749 -1749
  41. package/docs/exec-plans/README.md +6 -6
  42. package/docs/exec-plans/active/README.md +11 -11
  43. package/docs/exec-plans/completed/README.md +43 -34
  44. package/docs/feature-workflow.md +187 -187
  45. package/docs/harness-methodology-debugging.md +153 -153
  46. package/docs/harness-methodology-tdd.md +130 -130
  47. package/docs/harness-methodology-verification.md +27 -27
  48. package/docs/init-surface.manifest.json +245 -241
  49. package/docs/loop-agent-harness.md +63 -55
  50. package/docs/production-readiness.md +96 -96
  51. package/docs/progress/README.md +3 -3
  52. package/docs/reports/README.md +12 -9
  53. package/docs/skills/README.md +6 -6
  54. package/docs/skills/vetted-skill-registry.md +26 -26
  55. package/docs/templates/adr.md +60 -60
  56. package/docs/templates/agent-dag-authority-surface-audit.prompt.md +94 -94
  57. package/docs/templates/agent-dag-decision-envelope.schema.json +213 -213
  58. package/docs/templates/agent-dag-decision-gate-dogfood-report.md +117 -117
  59. package/docs/templates/agent-dag-decision-gate.prompt.md +246 -246
  60. package/docs/templates/agent-dag-process-supervisor.prompt.md +98 -98
  61. package/docs/templates/agent-dag-report.schema.json +454 -454
  62. package/docs/templates/agent-dag-review-verdict.prompt.md +68 -68
  63. package/docs/templates/agent-dag.base.json +195 -195
  64. package/docs/templates/agent-dag.final-verification.json +190 -190
  65. package/docs/templates/agent-dag.schema.json +316 -316
  66. package/docs/templates/agent-dag.supervised-implementation.json +500 -500
  67. package/docs/templates/exec-plan.md +64 -64
  68. package/docs/templates/feature-spec.md +53 -53
  69. package/docs/templates/harness.schema.json +218 -0
  70. package/docs/templates/hybrid-dag.json +193 -193
  71. package/docs/templates/init-evolution-review.md +33 -33
  72. package/docs/templates/interactive-ui-round2-experiment.md +66 -66
  73. package/docs/templates/product-line/AGENTS.md +8 -8
  74. package/docs/templates/product-line/README.md +9 -9
  75. package/docs/templates/product-line/acceptance.yaml +14 -14
  76. package/docs/templates/product-line/closeout.yaml +9 -9
  77. package/docs/templates/product-line/design.md +13 -13
  78. package/docs/templates/product-line/links.md +10 -10
  79. package/docs/templates/product-line/requirement.md +17 -17
  80. package/docs/templates/product-line/task-graph.yaml +15 -15
  81. package/docs/templates/product-line/task.yaml +65 -65
  82. package/docs/templates/product-line/test-plan.md +7 -7
  83. package/docs/templates/production-readiness-checklist.md +57 -57
  84. package/docs/templates/progress-log.md +17 -17
  85. package/docs/templates/project-start-checklist.md +9 -9
  86. package/docs/templates/qa-report.md +48 -48
  87. package/docs/templates/sprint-contract.md +29 -29
  88. package/docs/templates/worker-dogfood-evidence.md +52 -52
  89. package/docs/templates/worker-dogfood-setup.md +48 -48
  90. package/docs/verification-matrix.md +49 -49
  91. package/examples/decision-gate-agent-dag.json +123 -123
  92. package/examples/example-dag.json +51 -51
  93. package/examples/hybrid-loop-agent-dag.json +194 -194
  94. package/harness.json +73 -71
  95. package/package.json +68 -67
  96. package/scripts/check-product-line-docs.sh +22 -22
  97. package/scripts/check-task-pool-root.sh +32 -0
  98. package/skills/ai-engineering-context/SKILL.md +48 -48
  99. package/skills/code-review-core/SKILL.md +20 -20
  100. package/skills/codebase-scout/SKILL.md +19 -19
  101. package/skills/init-capability-evolution/SKILL.md +69 -69
  102. package/skills/loop-agent/SKILL.md +149 -149
  103. package/skills/loop-agent/references/README.md +67 -67
  104. package/skills/loop-agent/references/command-reference.md +432 -412
  105. package/skills/loop-agent/references/harness-policy.md +263 -263
  106. package/skills/loop-agent/references/hybrid-dag.md +216 -216
  107. package/skills/loop-agent/references/learned/README.md +21 -21
  108. package/skills/loop-agent/references/long-running-loop.md +59 -59
  109. package/skills/loop-agent/references/model-routing.md +36 -36
  110. package/skills/loop-agent/references/multi-worktree.md +54 -54
  111. package/skills/loop-agent/references/one-shot-runs.md +85 -85
  112. package/skills/loop-agent/references/orchestrator-and-interventions.md +169 -169
  113. package/skills/loop-agent/references/pi-prompt.md +23 -23
  114. package/skills/loop-agent/references/pi-subagent-assisted-mode.md +81 -81
  115. package/skills/loop-agent/references/post-implementation-and-patterns.md +44 -44
  116. package/skills/loop-agent/references/task-workflow.md +89 -89
  117. package/skills/loop-agent/references/verification-and-failure-handling.md +133 -128
  118. package/skills/requesting-code-review/SKILL.md +101 -101
  119. package/skills/requesting-code-review/code-reviewer.md +168 -168
  120. package/skills/systematic-debugging/CREATION-LOG.md +119 -119
  121. package/skills/systematic-debugging/SKILL.md +296 -296
  122. package/skills/systematic-debugging/condition-based-waiting-example.ts +158 -158
  123. package/skills/systematic-debugging/condition-based-waiting.md +115 -115
  124. package/skills/systematic-debugging/defense-in-depth.md +122 -122
  125. package/skills/systematic-debugging/find-polluter.sh +63 -63
  126. package/skills/systematic-debugging/root-cause-tracing.md +169 -169
  127. package/skills/systematic-debugging/test-academic.md +14 -14
  128. package/skills/systematic-debugging/test-pressure-1.md +58 -58
  129. package/skills/systematic-debugging/test-pressure-2.md +68 -68
  130. package/skills/systematic-debugging/test-pressure-3.md +69 -69
  131. package/skills/test-driven-development/SKILL.md +20 -20
  132. package/skills/verification-before-completion/SKILL.md +154 -154
  133. package/skills/webapp-testing/SKILL.md +19 -19
@@ -1,31 +1,31 @@
1
- export type DagLayoutNodeInput = { nodeId: string };
2
- export type DagLayoutEdgeInput = { from: string; to: string };
3
- export type DagLayoutNode = {
4
- nodeId: string;
5
- rank: number;
6
- index: number;
7
- x: number;
8
- y: number;
9
- width: number;
10
- height: number;
11
- };
12
- export type DagLayoutEdge = DagLayoutEdgeInput & { path: string };
13
- export type DagLayout = {
14
- width: number;
15
- height: number;
16
- nodes: DagLayoutNode[];
17
- edges: DagLayoutEdge[];
18
- };
19
-
20
- export function layoutDag(
21
- nodes?: DagLayoutNodeInput[],
22
- edges?: DagLayoutEdgeInput[],
23
- ranks?: string[][],
24
- options?: Partial<{
25
- nodeWidth: number;
26
- nodeHeight: number;
27
- rankGap: number;
28
- nodeGap: number;
29
- padding: number;
30
- }>,
31
- ): DagLayout;
1
+ export type DagLayoutNodeInput = { nodeId: string };
2
+ export type DagLayoutEdgeInput = { from: string; to: string };
3
+ export type DagLayoutNode = {
4
+ nodeId: string;
5
+ rank: number;
6
+ index: number;
7
+ x: number;
8
+ y: number;
9
+ width: number;
10
+ height: number;
11
+ };
12
+ export type DagLayoutEdge = DagLayoutEdgeInput & { path: string };
13
+ export type DagLayout = {
14
+ width: number;
15
+ height: number;
16
+ nodes: DagLayoutNode[];
17
+ edges: DagLayoutEdge[];
18
+ };
19
+
20
+ export function layoutDag(
21
+ nodes?: DagLayoutNodeInput[],
22
+ edges?: DagLayoutEdgeInput[],
23
+ ranks?: string[][],
24
+ options?: Partial<{
25
+ nodeWidth: number;
26
+ nodeHeight: number;
27
+ rankGap: number;
28
+ nodeGap: number;
29
+ padding: number;
30
+ }>,
31
+ ): DagLayout;
@@ -1,83 +1,83 @@
1
- const DEFAULTS = {
2
- nodeWidth: 216,
3
- nodeHeight: 92,
4
- rankGap: 104,
5
- nodeGap: 28,
6
- padding: 32,
7
- };
8
-
9
- export function layoutDag(nodes = [], edges = [], ranks = [], options = {}) {
10
- const config = { ...DEFAULTS, ...options };
11
- const nodeIds = [...new Set(nodes.map((node) => node.nodeId).filter(Boolean))].sort();
12
- if (nodeIds.length === 0) return { width: 320, height: 160, nodes: [], edges: [] };
13
- const known = new Set(nodeIds);
14
- const validEdges = edges
15
- .filter((edge) => known.has(edge.from) && known.has(edge.to) && edge.from !== edge.to)
16
- .sort((a, b) => a.from.localeCompare(b.from) || a.to.localeCompare(b.to));
17
- const layers = normalizeLayers(nodeIds, validEdges, ranks);
18
- const positioned = [];
19
- for (let rank = 0; rank < layers.length; rank++) {
20
- const ids = layers[rank];
21
- for (let index = 0; index < ids.length; index++) {
22
- positioned.push({
23
- nodeId: ids[index], rank, index,
24
- x: config.padding + rank * (config.nodeWidth + config.rankGap),
25
- y: config.padding + index * (config.nodeHeight + config.nodeGap),
26
- width: config.nodeWidth, height: config.nodeHeight,
27
- });
28
- }
29
- }
30
- const byId = new Map(positioned.map((node) => [node.nodeId, node]));
31
- const routed = validEdges.map((edge) => {
32
- const from = byId.get(edge.from);
33
- const to = byId.get(edge.to);
34
- const x1 = from.x + from.width;
35
- const y1 = from.y + from.height / 2;
36
- const x2 = to.x;
37
- const y2 = to.y + to.height / 2;
38
- const bend = Math.max(36, (x2 - x1) / 2);
39
- return { ...edge, path: `M ${x1} ${y1} C ${x1 + bend} ${y1}, ${x2 - bend} ${y2}, ${x2} ${y2}` };
40
- });
41
- const maxRows = Math.max(...layers.map((layer) => layer.length), 1);
42
- return {
43
- width: config.padding * 2 + layers.length * config.nodeWidth + (layers.length - 1) * config.rankGap,
44
- height: config.padding * 2 + maxRows * config.nodeHeight + (maxRows - 1) * config.nodeGap,
45
- nodes: positioned,
46
- edges: routed,
47
- };
48
- }
49
-
50
- function normalizeLayers(nodeIds, edges, ranks) {
51
- const known = new Set(nodeIds);
52
- const seen = new Set();
53
- const layers = [];
54
- if (Array.isArray(ranks)) {
55
- for (const rank of ranks) {
56
- if (!Array.isArray(rank)) continue;
57
- const ids = [...new Set(rank.filter((id) => known.has(id) && !seen.has(id)))].sort();
58
- ids.forEach((id) => seen.add(id));
59
- if (ids.length > 0) layers.push(ids);
60
- }
61
- }
62
- if (layers.length === 0 && edges.length > 0) {
63
- const level = new Map(nodeIds.map((id) => [id, 0]));
64
- for (let pass = 0; pass < nodeIds.length; pass++) {
65
- let changed = false;
66
- for (const edge of edges) {
67
- const next = Math.min(nodeIds.length - 1, level.get(edge.from) + 1);
68
- if (next > level.get(edge.to)) { level.set(edge.to, next); changed = true; }
69
- }
70
- if (!changed) break;
71
- }
72
- for (const id of nodeIds) {
73
- const rank = level.get(id);
74
- (layers[rank] ??= []).push(id);
75
- seen.add(id);
76
- }
77
- for (const layer of layers) layer?.sort();
78
- }
79
- for (const id of nodeIds) {
80
- if (!seen.has(id)) (layers[0] ??= []).push(id);
81
- }
82
- return layers.filter(Boolean);
83
- }
1
+ const DEFAULTS = {
2
+ nodeWidth: 216,
3
+ nodeHeight: 92,
4
+ rankGap: 104,
5
+ nodeGap: 28,
6
+ padding: 32,
7
+ };
8
+
9
+ export function layoutDag(nodes = [], edges = [], ranks = [], options = {}) {
10
+ const config = { ...DEFAULTS, ...options };
11
+ const nodeIds = [...new Set(nodes.map((node) => node.nodeId).filter(Boolean))].sort();
12
+ if (nodeIds.length === 0) return { width: 320, height: 160, nodes: [], edges: [] };
13
+ const known = new Set(nodeIds);
14
+ const validEdges = edges
15
+ .filter((edge) => known.has(edge.from) && known.has(edge.to) && edge.from !== edge.to)
16
+ .sort((a, b) => a.from.localeCompare(b.from) || a.to.localeCompare(b.to));
17
+ const layers = normalizeLayers(nodeIds, validEdges, ranks);
18
+ const positioned = [];
19
+ for (let rank = 0; rank < layers.length; rank++) {
20
+ const ids = layers[rank];
21
+ for (let index = 0; index < ids.length; index++) {
22
+ positioned.push({
23
+ nodeId: ids[index], rank, index,
24
+ x: config.padding + rank * (config.nodeWidth + config.rankGap),
25
+ y: config.padding + index * (config.nodeHeight + config.nodeGap),
26
+ width: config.nodeWidth, height: config.nodeHeight,
27
+ });
28
+ }
29
+ }
30
+ const byId = new Map(positioned.map((node) => [node.nodeId, node]));
31
+ const routed = validEdges.map((edge) => {
32
+ const from = byId.get(edge.from);
33
+ const to = byId.get(edge.to);
34
+ const x1 = from.x + from.width;
35
+ const y1 = from.y + from.height / 2;
36
+ const x2 = to.x;
37
+ const y2 = to.y + to.height / 2;
38
+ const bend = Math.max(36, (x2 - x1) / 2);
39
+ return { ...edge, path: `M ${x1} ${y1} C ${x1 + bend} ${y1}, ${x2 - bend} ${y2}, ${x2} ${y2}` };
40
+ });
41
+ const maxRows = Math.max(...layers.map((layer) => layer.length), 1);
42
+ return {
43
+ width: config.padding * 2 + layers.length * config.nodeWidth + (layers.length - 1) * config.rankGap,
44
+ height: config.padding * 2 + maxRows * config.nodeHeight + (maxRows - 1) * config.nodeGap,
45
+ nodes: positioned,
46
+ edges: routed,
47
+ };
48
+ }
49
+
50
+ function normalizeLayers(nodeIds, edges, ranks) {
51
+ const known = new Set(nodeIds);
52
+ const seen = new Set();
53
+ const layers = [];
54
+ if (Array.isArray(ranks)) {
55
+ for (const rank of ranks) {
56
+ if (!Array.isArray(rank)) continue;
57
+ const ids = [...new Set(rank.filter((id) => known.has(id) && !seen.has(id)))].sort();
58
+ ids.forEach((id) => seen.add(id));
59
+ if (ids.length > 0) layers.push(ids);
60
+ }
61
+ }
62
+ if (layers.length === 0 && edges.length > 0) {
63
+ const level = new Map(nodeIds.map((id) => [id, 0]));
64
+ for (let pass = 0; pass < nodeIds.length; pass++) {
65
+ let changed = false;
66
+ for (const edge of edges) {
67
+ const next = Math.min(nodeIds.length - 1, level.get(edge.from) + 1);
68
+ if (next > level.get(edge.to)) { level.set(edge.to, next); changed = true; }
69
+ }
70
+ if (!changed) break;
71
+ }
72
+ for (const id of nodeIds) {
73
+ const rank = level.get(id);
74
+ (layers[rank] ??= []).push(id);
75
+ seen.add(id);
76
+ }
77
+ for (const layer of layers) layer?.sort();
78
+ }
79
+ for (const id of nodeIds) {
80
+ if (!seen.has(id)) (layers[0] ??= []).push(id);
81
+ }
82
+ return layers.filter(Boolean);
83
+ }
@@ -1,63 +1,88 @@
1
- <!DOCTYPE html>
2
- <html lang="zh-CN">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>loop-agent 可观测</title>
7
- <link rel="stylesheet" href="/styles.css">
8
- </head>
9
- <body>
10
- <div id="app">
11
- <header class="site-header">
12
- <a href="#/" class="logo">loop-agent 可观测</a>
13
- <nav class="top-nav">
14
- <a href="#/" class="nav-link">总览</a>
15
- <a href="#/dags" class="nav-link">DAG</a>
16
- <a href="#/failures" class="nav-link">失败箱</a>
17
- </nav>
18
- <span id="header-refresh" class="header-refresh"></span>
19
- <nav id="breadcrumb" class="breadcrumb"></nav>
20
- </header>
21
-
22
- <section data-view="dashboard" id="view-dashboard">
23
- <h2>总览</h2>
24
- <div id="dashboard-kpi" class="kpi-grid panel"></div>
25
- <div id="dashboard-active-dags" class="panel"></div>
26
- <div id="dashboard-dags" class="panel"></div>
27
- <div id="dashboard-batches" class="panel"></div>
28
- </section>
29
-
30
- <section data-view="batch" id="view-batch" hidden>
31
- <h2>批次详情</h2>
32
- <div id="batch-meta" class="panel"></div>
33
- <div id="batch-tasks" class="panel"></div>
34
- </section>
35
-
36
- <section data-view="run" id="view-run" hidden>
37
- <h2>运行详情</h2>
38
- <div id="run-meta" class="panel"></div>
39
- <div id="run-dag-progress" class="panel"></div>
40
- <div id="run-steps" class="panel"></div>
41
- <div id="run-command" class="panel"></div>
42
- <div id="run-output" class="panel"></div>
43
- <div id="run-artifacts" class="panel"></div>
44
- </section>
45
-
46
- <section data-view="dag" id="view-dag" hidden>
47
- <h2>DAG 详情</h2>
48
- <div id="dag-meta" class="panel"></div>
49
- <div id="dag-ranks" class="panel"></div>
50
- <div id="dag-nodes" class="panel"></div>
51
- <div id="dag-timeline" class="panel"></div>
52
- <div id="dag-output" class="panel"></div>
53
- </section>
54
-
55
- <section data-view="failures" id="view-failures" hidden>
56
- <h2>失败箱</h2>
57
- <div id="failures-empty" class="panel" hidden>暂无失败或阻塞的任务。</div>
58
- <div id="failures-list" class="panel"></div>
59
- </section>
60
- </div>
61
- <script type="module" src="/app.js"></script>
62
- </body>
63
- </html>
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Loop Agent</title>
7
+ <link rel="icon" href="data:,">
8
+ <link rel="preconnect" href="https://cdn.jsdelivr.net">
9
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/remixicon@4.6.0/fonts/remixicon.css">
10
+ <link rel="stylesheet" href="/styles.css">
11
+ </head>
12
+ <body data-shell="observe-console">
13
+ <a class="skip-link" href="#main-content">跳到主内容</a>
14
+ <div id="app">
15
+ <header class="site-header">
16
+ <a href="#/" class="logo" aria-label="Loop Agent 运行看板首页">
17
+ <i class="ri-radar-line" aria-hidden="true"></i>
18
+ <span>Loop Agent</span>
19
+ </a>
20
+ <nav class="top-nav" aria-label="主导航">
21
+ <a href="#/" class="nav-link"><i class="ri-dashboard-3-line" aria-hidden="true"></i><span>总览</span></a>
22
+ <a href="#/failures" class="nav-link"><i class="ri-alarm-warning-line" aria-hidden="true"></i><span>异常 Task</span></a>
23
+ </nav>
24
+ <div class="header-meta">
25
+ <i class="ri-live-line" aria-hidden="true"></i>
26
+ <span id="header-refresh" class="header-refresh" aria-live="polite"></span>
27
+ </div>
28
+ <nav id="breadcrumb" class="breadcrumb" aria-label="面包屑"></nav>
29
+ </header>
30
+
31
+ <main id="main-content" class="dashboard-main" tabindex="-1">
32
+ <section data-view="dashboard" id="view-dashboard" class="view view-dashboard">
33
+ <div class="view-heading">
34
+ <p class="view-kicker">OBSERVE</p>
35
+ <h2>运行总览</h2>
36
+ <p>聚焦正在推进的 DAG、需要处理的风险与近期运行记录。</p>
37
+ </div>
38
+ <section id="dashboard-kpi" class="kpi-grid panel panel-kpi" aria-label="运行摘要"></section>
39
+ <div class="dashboard-workspace">
40
+ <section id="dashboard-active-dags" class="panel panel-primary"></section>
41
+ <aside id="dashboard-risk" class="panel panel-risk" aria-label="运行风险"></aside>
42
+ </div>
43
+ <div class="dashboard-secondary">
44
+ <section id="dashboard-dags" class="panel"></section>
45
+ <section id="dashboard-batches" class="panel"></section>
46
+ </div>
47
+ </section>
48
+
49
+ <section data-view="batch" id="view-batch" class="view" hidden>
50
+ <div class="view-heading"><p class="view-kicker">BATCH</p><h2>批次详情</h2></div>
51
+ <section id="batch-meta" class="panel panel-meta"></section>
52
+ <section id="batch-tasks" class="panel panel-table"></section>
53
+ </section>
54
+
55
+ <section data-view="run" id="view-run" class="view" hidden>
56
+ <div class="view-heading"><p class="view-kicker">WORKER RUN</p><h2>运行详情</h2></div>
57
+ <section id="run-meta" class="panel panel-meta"></section>
58
+ <section id="run-dag-progress" class="panel panel-progress"></section>
59
+ <div class="run-detail-workspace">
60
+ <aside id="run-steps" class="panel panel-rail"></aside>
61
+ <div class="run-evidence">
62
+ <section id="run-command" class="panel"></section>
63
+ <section id="run-output" class="panel panel-output"></section>
64
+ </div>
65
+ <aside id="run-artifacts" class="panel panel-artifacts"></aside>
66
+ </div>
67
+ </section>
68
+
69
+ <section data-view="dag" id="view-dag" class="view" hidden>
70
+ <div class="view-heading"><p class="view-kicker">AGENT DAG</p><h2>DAG 详情</h2></div>
71
+ <section id="dag-meta" class="panel panel-meta"></section>
72
+ <div class="dag-detail-workspace">
73
+ <section id="dag-ranks" class="panel panel-graph"></section>
74
+ </div>
75
+ <section id="dag-nodes" class="panel panel-table"></section>
76
+ <aside id="dag-inspector" class="panel dag-inspector" aria-label="选中节点检查器" aria-hidden="true"></aside>
77
+ </section>
78
+
79
+ <section data-view="failures" id="view-failures" class="view" hidden>
80
+ <div class="view-heading"><p class="view-kicker">ATTENTION QUEUE</p><h2>异常 Task</h2><p>按运行状态和建议跟进项集中处理需要人工介入的 Task。</p></div>
81
+ <div id="failures-empty" class="panel empty-state" hidden>当前没有异常 Task</div>
82
+ <section id="failures-list" class="panel panel-table"></section>
83
+ </section>
84
+ </main>
85
+ </div>
86
+ <script type="module" src="/app.js"></script>
87
+ </body>
88
+ </html>