@tea-agent/loop-agent 0.12.0 → 0.13.0-beta.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.
- package/AGENTS.md +155 -153
- package/CHANGELOG.md +338 -265
- package/README.md +345 -298
- package/bin/agent-worker.js +22 -22
- package/bin/loop-agent.js +21 -21
- package/dist/application/dag/generate-task-dag.js +28 -28
- package/dist/application/evaluation/candidate-hash.js +75 -0
- package/dist/application/evaluation/candidate.js +52 -0
- package/dist/application/evaluation/replay.js +289 -0
- package/dist/application/evaluation/types.js +130 -0
- package/dist/cli/command-definitions.js +27 -7
- package/dist/cli/program.js +8 -4
- package/dist/commands/cursor-prompt.js +6 -6
- package/dist/commands/eval.js +235 -0
- package/dist/commands/init.js +544 -506
- package/dist/commands/knowledge.js +129 -31
- package/dist/commands/loop-benchmark.js +11 -11
- package/dist/commands/pi-reuse-benchmark.js +16 -16
- package/dist/executors/pi-sdk-executor.js +38 -24
- package/dist/executors/shell-executor.js +34 -2
- package/dist/executors/shell-presets.js +20 -0
- package/dist/executors/shell-verification.js +7 -0
- package/dist/governance/manifest-types.js +4 -0
- package/dist/infrastructure/evaluation/candidate-store.js +435 -0
- package/dist/infrastructure/evaluation/store.js +40 -0
- package/dist/sidecars/cursor-prompt/executor.js +1 -1
- package/dist/task/config-types.js +28 -1
- package/dist/task/runtime.js +27 -27
- package/dist/worker/cli.js +96 -1
- package/dist/worker/delivery/package.js +3 -3
- package/dist/worker/feature/decision-loader.js +37 -6
- package/dist/worker/feature/next-action.js +10 -2
- package/dist/worker/feature/ready-plan-projection.js +81 -0
- package/dist/worker/feature/reducer.js +2 -1
- package/dist/worker/feature/review.js +19 -2
- package/dist/worker/feature/run.js +27 -2
- package/dist/worker/follow-up/approve.js +5 -2
- package/dist/worker/follow-up/factory.js +1 -1
- package/dist/worker/observability/read-model.js +246 -41
- package/dist/worker/observe/routes.js +173 -15
- package/dist/worker/observe/spec-evidence.js +281 -0
- package/dist/worker/observe/static/api.js +46 -27
- package/dist/worker/observe/static/app.js +150 -150
- package/dist/worker/observe/static/constants.js +148 -148
- package/dist/worker/observe/static/copy.js +67 -67
- package/dist/worker/observe/static/dag-helpers.js +172 -172
- package/dist/worker/observe/static/dag-layout.d.ts +31 -31
- package/dist/worker/observe/static/dag-layout.js +83 -83
- package/dist/worker/observe/static/dag-model.js +72 -72
- package/dist/worker/observe/static/dom.js +61 -61
- package/dist/worker/observe/static/format-pool.js +67 -67
- package/dist/worker/observe/static/format.js +292 -292
- package/dist/worker/observe/static/index.html +308 -308
- package/dist/worker/observe/static/kpi.js +94 -94
- package/dist/worker/observe/static/relations.js +133 -128
- package/dist/worker/observe/static/router.js +93 -85
- package/dist/worker/observe/static/run-processing.js +148 -148
- package/dist/worker/observe/static/shell-chrome.js +68 -68
- package/dist/worker/observe/static/state.js +253 -253
- package/dist/worker/observe/static/styles.css +1902 -1890
- package/dist/worker/observe/static/views/batch.js +227 -226
- package/dist/worker/observe/static/views/dag-graph.js +172 -172
- package/dist/worker/observe/static/views/dag-inspector.js +607 -477
- package/dist/worker/observe/static/views/dag.js +362 -362
- package/dist/worker/observe/static/views/dashboard.js +445 -442
- package/dist/worker/observe/static/views/failures.js +143 -143
- package/dist/worker/observe/static/views/feature.js +492 -453
- package/dist/worker/observe/static/views/pool.js +350 -347
- package/dist/worker/observe/static/views/run.js +453 -453
- package/dist/worker/observe/static/views/session-timeline.js +205 -205
- package/dist/worker/observe/static/views/shell.js +7 -7
- package/dist/worker/observe/static/views/task.js +314 -260
- package/dist/worker/observe/static/views/timeline.js +163 -163
- package/dist/worker/pool/doctor.js +165 -0
- package/dist/worker/pool/migrate-state.js +303 -0
- package/dist/worker/pool/run-store.js +205 -17
- package/dist/worker/pool/types.js +17 -1
- package/dist/worker/pool/validation.js +100 -15
- package/dist/worker/report/morning-report.js +12 -2
- package/dist/worker/runner/run-ready.js +41 -26
- package/dist/worker/task-graph/ready-planner.js +136 -0
- package/dist/workflows/dag/backend-test-analysis-contract.js +120 -0
- package/dist/workflows/dag/canvas-observer.js +275 -275
- package/dist/workflows/dag/convergence/controller.js +16 -8
- package/dist/workflows/dag/dynamic-runtime/map.js +90 -2
- package/dist/workflows/dag/failure-routing.js +12 -1
- package/dist/workflows/dag/init-hybrid.js +2404 -360
- package/dist/workflows/dag/node-execution.js +9 -0
- package/dist/workflows/dag/prompt.js +9 -0
- package/dist/workflows/dag/report.js +35 -1
- package/dist/workflows/dag/runner.js +28 -2
- package/dist/workflows/dag/task-demand-routing.js +383 -0
- package/dist/workflows/dag/types.js +51 -13
- package/dist/workflows/dag/upstream-artifacts.js +1 -0
- package/dist/workflows/dag/validate.js +59 -1
- package/docs/README.md +106 -104
- package/docs/agent-dag-recovery-playbook.md +195 -184
- package/docs/agent-dag-runner.md +67 -67
- package/docs/architecture/README.md +26 -26
- package/docs/architecture/dag-execution.md +140 -140
- package/docs/architecture/evolution.md +54 -53
- package/docs/architecture/facts-and-state.md +71 -58
- package/docs/architecture/runtime-boundaries.md +191 -191
- package/docs/architecture/system-overview.md +93 -93
- package/docs/architecture/worker-and-feature.md +85 -81
- package/docs/cursor-prompt-sidecar.md +36 -36
- package/docs/decisions/README.md +18 -15
- package/docs/design/README.md +167 -77
- package/docs/development-principles.md +73 -73
- package/docs/exec-plans/README.md +6 -6
- package/docs/exec-plans/active/README.md +15 -9
- package/docs/exec-plans/completed/README.md +85 -73
- package/docs/feature-workflow.md +389 -261
- package/docs/harness-methodology-debugging.md +153 -153
- package/docs/harness-methodology-tdd.md +130 -130
- package/docs/harness-methodology-verification.md +27 -27
- package/docs/init-surface.manifest.json +289 -280
- package/docs/loop-agent-harness.md +142 -130
- package/docs/production-readiness.md +96 -96
- package/docs/progress/README.md +64 -54
- package/docs/reports/README.md +117 -94
- package/docs/skills/README.md +7 -7
- package/docs/skills/vetted-skill-registry.md +29 -27
- package/docs/templates/adr.md +60 -60
- package/docs/templates/agent-dag-authority-surface-audit.prompt.md +94 -94
- package/docs/templates/agent-dag-decision-envelope.schema.json +213 -213
- package/docs/templates/agent-dag-decision-gate-dogfood-report.md +117 -117
- package/docs/templates/agent-dag-decision-gate.prompt.md +246 -246
- package/docs/templates/agent-dag-process-supervisor.prompt.md +98 -98
- package/docs/templates/agent-dag-report.schema.json +473 -473
- package/docs/templates/agent-dag-review-verdict.prompt.md +68 -68
- package/docs/templates/agent-dag.base.json +190 -190
- package/docs/templates/agent-dag.final-verification.json +185 -185
- package/docs/templates/agent-dag.schema.json +411 -383
- package/docs/templates/agent-dag.supervised-implementation.json +501 -501
- package/docs/templates/backend-test-analysis.schema.json +44 -0
- package/docs/templates/backend-test-dag.generate-pytest.prompt.md +202 -139
- package/docs/templates/backend-test-dag.json +311 -276
- package/docs/templates/backend-test-dag.retrospect.prompt.md +125 -125
- package/docs/templates/backend-test-dag.review-cases.prompt.md +81 -81
- package/docs/templates/exec-plan.md +64 -64
- package/docs/templates/feature-spec.md +53 -53
- package/docs/templates/frontend-design-contract.md +42 -33
- package/docs/templates/frontend-task-constraints.md +35 -25
- package/docs/templates/frontend-task-requirement.md +70 -61
- package/docs/templates/frontend-test-dag.generate-cases.prompt.md +5 -0
- package/docs/templates/frontend-test-dag.json +23 -0
- package/docs/templates/frontend-test-dag.retrieve-context.prompt.md +3 -0
- package/docs/templates/frontend-test-dag.retrospect.prompt.md +3 -0
- package/docs/templates/frontend-test-dag.review-cases.prompt.md +3 -0
- package/docs/templates/frontend-test-dag.review-execution.prompt.md +3 -0
- package/docs/templates/harness.schema.json +221 -221
- package/docs/templates/hybrid-dag.json +188 -188
- package/docs/templates/init-evolution-review.md +35 -35
- package/docs/templates/interactive-ui-round2-experiment.md +66 -66
- package/docs/templates/knowledge-graph-bootstrap-dag.json +118 -0
- package/docs/templates/knowledge-sync-dag.json +178 -0
- package/docs/templates/knowledge-sync-draft.schema.json +71 -0
- package/docs/templates/product-line/AGENTS.md +8 -8
- package/docs/templates/product-line/README.md +9 -9
- package/docs/templates/product-line/acceptance.yaml +14 -14
- package/docs/templates/product-line/closeout.yaml +9 -9
- package/docs/templates/product-line/design.md +13 -13
- package/docs/templates/product-line/links.md +10 -10
- package/docs/templates/product-line/requirement.md +17 -17
- package/docs/templates/product-line/task-graph.yaml +15 -15
- package/docs/templates/product-line/task.yaml +64 -64
- package/docs/templates/product-line/test-plan.md +7 -7
- package/docs/templates/production-readiness-checklist.md +57 -57
- package/docs/templates/progress-log.md +17 -17
- package/docs/templates/project-start-checklist.md +9 -9
- package/docs/templates/qa-report.md +48 -48
- package/docs/templates/sprint-contract.md +29 -29
- package/docs/templates/worker-dogfood-evidence.md +80 -80
- package/docs/templates/worker-dogfood-setup.md +68 -68
- package/docs/verification-matrix.md +70 -66
- package/examples/decision-gate-agent-dag.json +177 -177
- package/examples/example-dag.json +46 -46
- package/examples/hybrid-loop-agent-dag.json +189 -189
- package/harness.json +66 -66
- package/package.json +88 -46
- package/scripts/check-product-line-docs.sh +29 -29
- package/scripts/check-task-pool-root.sh +32 -32
- package/scripts/kb-bootstrap-init-skeleton.sh +240 -0
- package/scripts/kb-graph-incremental-prepare.mjs +386 -0
- package/scripts/kb-graph-incremental-prepare.sh +5 -0
- package/scripts/kb-graph-materialize.mjs +105 -0
- package/scripts/kb-graph-materialize.sh +4 -0
- package/scripts/kb-graph-promote.mjs +164 -0
- package/scripts/kb-graph-promote.sh +4 -0
- package/scripts/kb-query.mjs +554 -0
- package/scripts/kb-query.sh +5 -0
- package/skills/agent-worker/SKILL.md +39 -37
- package/skills/agent-worker/references/agent-worker-operator.md +60 -43
- package/skills/ai-engineering-context/SKILL.md +48 -48
- package/skills/analyze-product-dependencies/SKILL.md +67 -0
- package/skills/analyze-product-dependencies/agents/openai.yaml +4 -0
- package/skills/analyze-product-dependencies/references/api-documentation-schema.md +30 -0
- package/skills/analyze-product-dependencies/references/dependency-analysis-schema.md +28 -0
- package/skills/analyze-product-dependencies/references/example.md +76 -0
- package/skills/analyze-product-dependencies/references/forward-test-cases.md +35 -0
- package/skills/analyze-product-dependencies/references/input-contract.md +11 -0
- package/skills/analyze-product-dependencies/references/scouting-rules.md +61 -0
- package/skills/analyze-product-dependencies/scripts/test-validators.mjs +267 -0
- package/skills/analyze-product-dependencies/scripts/validate-api-documentation.mjs +101 -0
- package/skills/analyze-product-dependencies/scripts/validate-dependency-analysis.mjs +142 -0
- package/skills/analyze-product-dependencies/scripts/validate-product-requirement-input.mjs +76 -0
- package/skills/analyze-product-dependencies/scripts/validation-helpers.mjs +146 -0
- package/skills/analyze-product-requirements/SKILL.md +90 -0
- package/skills/analyze-product-requirements/agents/openai.yaml +4 -0
- package/skills/analyze-product-requirements/references/acceptance-criteria.md +91 -0
- package/skills/analyze-product-requirements/references/clarification-and-knowledge.md +56 -0
- package/skills/analyze-product-requirements/references/example.md +86 -0
- package/skills/analyze-product-requirements/references/forward-test-cases.md +66 -0
- package/skills/analyze-product-requirements/references/product-analysis-schema.md +32 -0
- package/skills/analyze-product-requirements/references/product-requirement-schema.md +33 -0
- package/skills/analyze-product-requirements/references/requirement-clarification-schema.md +35 -0
- package/skills/analyze-product-requirements/scripts/test-validators.mjs +193 -0
- package/skills/analyze-product-requirements/scripts/validate-product-analysis.mjs +69 -0
- package/skills/analyze-product-requirements/scripts/validate-product-requirement.mjs +97 -0
- package/skills/analyze-product-requirements/scripts/validate-requirement-clarification.mjs +98 -0
- package/skills/analyze-product-requirements/scripts/validation-helpers.mjs +156 -0
- package/skills/code-review-core/SKILL.md +20 -20
- package/skills/codebase-scout/SKILL.md +19 -19
- package/skills/frontend-design-review/SKILL.md +66 -59
- package/skills/frontend-design-review/references/review-checklist.md +58 -37
- package/skills/frontend-implementation/SKILL.md +47 -51
- package/skills/frontend-implementation/references/code-standards.md +32 -34
- package/skills/frontend-implementation/references/design-spec.md +46 -46
- package/skills/frontend-implementation/references/node-contracts.md +76 -32
- package/skills/frontend-review/SKILL.md +59 -53
- package/skills/frontend-review/references/review-findings.md +47 -42
- package/skills/frontend-verification/SKILL.md +53 -40
- package/skills/frontend-verification/references/verification-checklist.md +68 -56
- package/skills/grill-me/SKILL.md +10 -10
- package/skills/grill-with-docs/SKILL.md +88 -88
- package/skills/grill-with-docs/adr-format.md +47 -47
- package/skills/grill-with-docs/context-format.md +60 -60
- package/skills/init-capability-evolution/SKILL.md +70 -70
- package/skills/loop-agent/SKILL.md +151 -151
- package/skills/loop-agent/references/README.md +67 -67
- package/skills/loop-agent/references/command-reference.md +505 -452
- package/skills/loop-agent/references/docs-converge.md +126 -126
- package/skills/loop-agent/references/harness-policy.md +263 -263
- package/skills/loop-agent/references/hybrid-dag.md +238 -233
- package/skills/loop-agent/references/learned/README.md +21 -21
- package/skills/loop-agent/references/long-running-loop.md +57 -57
- package/skills/loop-agent/references/model-routing.md +36 -36
- package/skills/loop-agent/references/multi-worktree.md +54 -54
- package/skills/loop-agent/references/one-shot-runs.md +85 -85
- package/skills/loop-agent/references/orchestrator-and-interventions.md +169 -169
- package/skills/loop-agent/references/pi-prompt.md +23 -23
- package/skills/loop-agent/references/pi-subagent-assisted-mode.md +84 -84
- package/skills/loop-agent/references/post-implementation-and-patterns.md +44 -44
- package/skills/loop-agent/references/task-workflow.md +89 -89
- package/skills/loop-agent/references/verification-and-failure-handling.md +139 -139
- package/skills/playwright-cli/SKILL.md +420 -0
- package/skills/playwright-cli/references/element-attributes.md +23 -0
- package/skills/playwright-cli/references/playwright-tests.md +39 -0
- package/skills/playwright-cli/references/request-mocking.md +87 -0
- package/skills/playwright-cli/references/running-code.md +241 -0
- package/skills/playwright-cli/references/session-management.md +225 -0
- package/skills/playwright-cli/references/storage-state.md +275 -0
- package/skills/playwright-cli/references/test-generation.md +433 -0
- package/skills/playwright-cli/references/tracing.md +139 -0
- package/skills/playwright-cli/references/video-recording.md +143 -0
- package/skills/playwright-cli-case-generator/SKILL.md +74 -0
- package/skills/requesting-code-review/SKILL.md +101 -101
- package/skills/requesting-code-review/code-reviewer.md +168 -168
- package/skills/systematic-debugging/CREATION-LOG.md +119 -119
- package/skills/systematic-debugging/SKILL.md +296 -296
- package/skills/systematic-debugging/condition-based-waiting-example.ts +158 -158
- package/skills/systematic-debugging/condition-based-waiting.md +115 -115
- package/skills/systematic-debugging/defense-in-depth.md +122 -122
- package/skills/systematic-debugging/find-polluter.sh +63 -63
- package/skills/systematic-debugging/root-cause-tracing.md +169 -169
- package/skills/systematic-debugging/test-academic.md +14 -14
- package/skills/systematic-debugging/test-pressure-1.md +58 -58
- package/skills/systematic-debugging/test-pressure-2.md +68 -68
- package/skills/systematic-debugging/test-pressure-3.md +69 -69
- package/skills/test-driven-development/SKILL.md +20 -20
- package/skills/using-git-worktrees/SKILL.md +215 -215
- package/skills/verification-before-completion/SKILL.md +154 -154
- package/skills/webapp-testing/SKILL.md +19 -19
|
@@ -1,253 +1,253 @@
|
|
|
1
|
-
/** Shared mutable UI state singleton (Observe UI R5). */
|
|
2
|
-
import {
|
|
3
|
-
ACTIVE_DETAIL_POLL_MS,
|
|
4
|
-
HIDDEN_POLL_MS,
|
|
5
|
-
DASHBOARD_POLL_MS,
|
|
6
|
-
TERMINAL_RUN_STATUSES,
|
|
7
|
-
OUTPUT_FOLLOW_LATEST_PX,
|
|
8
|
-
} from "./constants.js";
|
|
9
|
-
import { clearNode, el } from "./dom.js";
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Single mutable ownership bag. Import `{ uiState }` and mutate properties.
|
|
13
|
-
* Never assign through `import * as ns` live bindings (Chrome TypeError).
|
|
14
|
-
*/
|
|
15
|
-
export const uiState = {
|
|
16
|
-
dashboardTimer: null,
|
|
17
|
-
runPollTimer: null,
|
|
18
|
-
dagPollTimer: null,
|
|
19
|
-
poolTimer: null,
|
|
20
|
-
taskPollTimer: null,
|
|
21
|
-
runEventOffset: 0,
|
|
22
|
-
runEvents: [],
|
|
23
|
-
currentRunId: null,
|
|
24
|
-
currentBatchRunId: null,
|
|
25
|
-
currentDagRunId: null,
|
|
26
|
-
selectedDagNodeId: null,
|
|
27
|
-
sessionEventOffset: 0,
|
|
28
|
-
sessionEvents: [],
|
|
29
|
-
lastSnapshot: null,
|
|
30
|
-
dagInspectorOpen: false,
|
|
31
|
-
dagInspectorTab: "output",
|
|
32
|
-
dagInspectorWidth: null,
|
|
33
|
-
dagGraphViewportState: null,
|
|
34
|
-
dagTimelineViewportState: null,
|
|
35
|
-
dagNodeOutputViewportState: null,
|
|
36
|
-
runOutputViewportState: null,
|
|
37
|
-
pollingGeneration: 0,
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
/** Per-dagRunId graph viewport restore map (stable Map instance). */
|
|
41
|
-
export const dagGraphViewportStates = new Map();
|
|
42
|
-
|
|
43
|
-
export function isPageVisible() {
|
|
44
|
-
return (
|
|
45
|
-
typeof document === "undefined" || document.visibilityState !== "hidden"
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function detailPollDelay(status, isVisible) {
|
|
50
|
-
const normalizedStatus = (status ?? "").toLowerCase().replace(/-/g, "_");
|
|
51
|
-
if (TERMINAL_RUN_STATUSES.has(normalizedStatus)) return null;
|
|
52
|
-
return isVisible ? ACTIVE_DETAIL_POLL_MS : HIDDEN_POLL_MS;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export function dashboardPollDelay() {
|
|
56
|
-
return isPageVisible() ? DASHBOARD_POLL_MS : HIDDEN_POLL_MS;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export function updateDagGraphViewportState(
|
|
60
|
-
_previous,
|
|
61
|
-
dagRunId,
|
|
62
|
-
scrollLeft,
|
|
63
|
-
scrollTop,
|
|
64
|
-
) {
|
|
65
|
-
return {
|
|
66
|
-
dagRunId,
|
|
67
|
-
scrollLeft: Math.max(0, Number(scrollLeft) || 0),
|
|
68
|
-
scrollTop: Math.max(0, Number(scrollTop) || 0),
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export function captureDagGraphViewportState(previous, dagRunId, viewport) {
|
|
73
|
-
if (!viewport || viewport.dataset?.dagRunId !== dagRunId) return previous;
|
|
74
|
-
return updateDagGraphViewportState(
|
|
75
|
-
previous,
|
|
76
|
-
dagRunId,
|
|
77
|
-
viewport.scrollLeft,
|
|
78
|
-
viewport.scrollTop,
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export function updateDagTimelineViewportState(
|
|
83
|
-
_previous,
|
|
84
|
-
dagRunId,
|
|
85
|
-
nodeId,
|
|
86
|
-
scrollLeft,
|
|
87
|
-
scrollTop,
|
|
88
|
-
followLatest,
|
|
89
|
-
) {
|
|
90
|
-
return {
|
|
91
|
-
dagRunId,
|
|
92
|
-
nodeId,
|
|
93
|
-
scrollLeft: Math.max(0, Number(scrollLeft) || 0),
|
|
94
|
-
scrollTop: Math.max(0, Number(scrollTop) || 0),
|
|
95
|
-
followLatest: Boolean(followLatest),
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Unified follow-latest helper for DAG node output and Worker output scroll
|
|
100
|
-
// regions. Returns true only when the user is parked within `threshold` px of
|
|
101
|
-
// the bottom, mirroring the existing process-timeline behaviour.
|
|
102
|
-
export function computeFollowLatest(
|
|
103
|
-
scrollTop,
|
|
104
|
-
scrollHeight,
|
|
105
|
-
clientHeight,
|
|
106
|
-
threshold = OUTPUT_FOLLOW_LATEST_PX,
|
|
107
|
-
) {
|
|
108
|
-
const top = Number(scrollTop);
|
|
109
|
-
const height = Number(scrollHeight);
|
|
110
|
-
const client = Number(clientHeight);
|
|
111
|
-
if (
|
|
112
|
-
!Number.isFinite(top) ||
|
|
113
|
-
!Number.isFinite(height) ||
|
|
114
|
-
!Number.isFinite(client)
|
|
115
|
-
) {
|
|
116
|
-
return false;
|
|
117
|
-
}
|
|
118
|
-
if (height <= 0 || client <= 0) return false;
|
|
119
|
-
return height - top - client < Math.max(0, Number(threshold) || 0);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Generalised viewport-state record keyed by `regionKey`, so the same helper
|
|
123
|
-
// serves the DAG node inspector output ("dag:<runId>:<nodeId>:output") and the
|
|
124
|
-
// Worker run output ("run:<runId>:output").
|
|
125
|
-
export function updateDagOutputViewportState(
|
|
126
|
-
_previous,
|
|
127
|
-
regionKey,
|
|
128
|
-
scrollLeft,
|
|
129
|
-
scrollTop,
|
|
130
|
-
followLatest,
|
|
131
|
-
) {
|
|
132
|
-
return {
|
|
133
|
-
regionKey,
|
|
134
|
-
scrollLeft: Math.max(0, Number(scrollLeft) || 0),
|
|
135
|
-
scrollTop: Math.max(0, Number(scrollTop) || 0),
|
|
136
|
-
followLatest: Boolean(followLatest),
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// Poll-render decision helpers. Initial paints always rebuild the shell; a
|
|
141
|
-
// non-initial refresh reuses the existing viewport/inspector DOM identity so
|
|
142
|
-
// active pointer-capture targets (scrollbar / resize handle) survive.
|
|
143
|
-
export function shouldReuseDagDetailShell(initial, hasExistingViewport) {
|
|
144
|
-
return !initial && Boolean(hasExistingViewport);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
export function shouldPreserveDiagnosticsOpen(initial, existingOpen) {
|
|
148
|
-
if (initial) return false;
|
|
149
|
-
return Boolean(existingOpen);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// Detects an in-progress resize/scroll gesture so the poll renderer can defer
|
|
153
|
-
// tearing down the interactive shell until the gesture releases.
|
|
154
|
-
export function isInteractiveGestureActive(bodyClassList) {
|
|
155
|
-
const list = bodyClassList;
|
|
156
|
-
if (!list || typeof list.contains !== "function") return false;
|
|
157
|
-
return (
|
|
158
|
-
list.contains("is-resizing-dag-inspector") ||
|
|
159
|
-
list.contains("is-resizing-dag-graph")
|
|
160
|
-
);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
export function stopTimers() {
|
|
164
|
-
uiState.pollingGeneration += 1;
|
|
165
|
-
if (uiState.dashboardTimer) {
|
|
166
|
-
clearTimeout(uiState.dashboardTimer);
|
|
167
|
-
uiState.dashboardTimer = null;
|
|
168
|
-
}
|
|
169
|
-
if (uiState.runPollTimer) {
|
|
170
|
-
clearTimeout(uiState.runPollTimer);
|
|
171
|
-
uiState.runPollTimer = null;
|
|
172
|
-
}
|
|
173
|
-
if (uiState.dagPollTimer) {
|
|
174
|
-
clearTimeout(uiState.dagPollTimer);
|
|
175
|
-
uiState.dagPollTimer = null;
|
|
176
|
-
}
|
|
177
|
-
if (uiState.poolTimer) {
|
|
178
|
-
clearTimeout(uiState.poolTimer);
|
|
179
|
-
uiState.poolTimer = null;
|
|
180
|
-
}
|
|
181
|
-
if (uiState.taskPollTimer) {
|
|
182
|
-
clearTimeout(uiState.taskPollTimer);
|
|
183
|
-
uiState.taskPollTimer = null;
|
|
184
|
-
}
|
|
185
|
-
uiState.currentRunId = null;
|
|
186
|
-
uiState.currentBatchRunId = null;
|
|
187
|
-
uiState.currentDagRunId = null;
|
|
188
|
-
uiState.selectedDagNodeId = null;
|
|
189
|
-
uiState.sessionEventOffset = 0;
|
|
190
|
-
uiState.sessionEvents = [];
|
|
191
|
-
uiState.runEventOffset = 0;
|
|
192
|
-
uiState.runEvents = [];
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/** Pure descriptor for loading / empty / degraded / error (REQ-R5-05). */
|
|
196
|
-
export function viewStateDescriptor(kind, message, options = {}) {
|
|
197
|
-
const classByKind = {
|
|
198
|
-
loading: "view-state view-state-loading",
|
|
199
|
-
empty: "view-state view-state-empty empty",
|
|
200
|
-
degraded: "view-state view-state-degraded projection-fault",
|
|
201
|
-
error: "view-state view-state-error",
|
|
202
|
-
};
|
|
203
|
-
const role =
|
|
204
|
-
kind === "error" || kind === "degraded"
|
|
205
|
-
? "alert"
|
|
206
|
-
: kind === "loading"
|
|
207
|
-
? "status"
|
|
208
|
-
: null;
|
|
209
|
-
return {
|
|
210
|
-
kind,
|
|
211
|
-
className: classByKind[kind] ?? "view-state",
|
|
212
|
-
role,
|
|
213
|
-
ariaLive: kind === "loading" ? "polite" : null,
|
|
214
|
-
message: message ?? "",
|
|
215
|
-
detail: options.detail ?? null,
|
|
216
|
-
messageClass:
|
|
217
|
-
kind === "empty" ? "empty" : kind === "loading" ? "muted" : null,
|
|
218
|
-
};
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
/** Distinguishable loading / empty / degraded / error panel states (REQ-R5-05). */
|
|
222
|
-
export function renderViewState(kind, message, options = {}) {
|
|
223
|
-
const desc = viewStateDescriptor(kind, message, options);
|
|
224
|
-
const node = el("div", desc.className);
|
|
225
|
-
if (desc.role) node.setAttribute("role", desc.role);
|
|
226
|
-
if (desc.ariaLive) node.setAttribute("aria-live", desc.ariaLive);
|
|
227
|
-
node.appendChild(el("p", desc.messageClass, desc.message));
|
|
228
|
-
if (desc.detail) {
|
|
229
|
-
node.appendChild(el("p", "muted", desc.detail));
|
|
230
|
-
}
|
|
231
|
-
return node;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
export function mountViewState(container, kind, message, options) {
|
|
235
|
-
if (!container) return null;
|
|
236
|
-
clearNode(container);
|
|
237
|
-
const node = renderViewState(kind, message, options);
|
|
238
|
-
container.appendChild(node);
|
|
239
|
-
return node;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
export function setLastSnapshot(snapshot) {
|
|
243
|
-
uiState.lastSnapshot = snapshot;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
export function setPollingGeneration(value) {
|
|
247
|
-
uiState.pollingGeneration = value;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
export function bumpPollingGeneration() {
|
|
251
|
-
uiState.pollingGeneration += 1;
|
|
252
|
-
return uiState.pollingGeneration;
|
|
253
|
-
}
|
|
1
|
+
/** Shared mutable UI state singleton (Observe UI R5). */
|
|
2
|
+
import {
|
|
3
|
+
ACTIVE_DETAIL_POLL_MS,
|
|
4
|
+
HIDDEN_POLL_MS,
|
|
5
|
+
DASHBOARD_POLL_MS,
|
|
6
|
+
TERMINAL_RUN_STATUSES,
|
|
7
|
+
OUTPUT_FOLLOW_LATEST_PX,
|
|
8
|
+
} from "./constants.js";
|
|
9
|
+
import { clearNode, el } from "./dom.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Single mutable ownership bag. Import `{ uiState }` and mutate properties.
|
|
13
|
+
* Never assign through `import * as ns` live bindings (Chrome TypeError).
|
|
14
|
+
*/
|
|
15
|
+
export const uiState = {
|
|
16
|
+
dashboardTimer: null,
|
|
17
|
+
runPollTimer: null,
|
|
18
|
+
dagPollTimer: null,
|
|
19
|
+
poolTimer: null,
|
|
20
|
+
taskPollTimer: null,
|
|
21
|
+
runEventOffset: 0,
|
|
22
|
+
runEvents: [],
|
|
23
|
+
currentRunId: null,
|
|
24
|
+
currentBatchRunId: null,
|
|
25
|
+
currentDagRunId: null,
|
|
26
|
+
selectedDagNodeId: null,
|
|
27
|
+
sessionEventOffset: 0,
|
|
28
|
+
sessionEvents: [],
|
|
29
|
+
lastSnapshot: null,
|
|
30
|
+
dagInspectorOpen: false,
|
|
31
|
+
dagInspectorTab: "output",
|
|
32
|
+
dagInspectorWidth: null,
|
|
33
|
+
dagGraphViewportState: null,
|
|
34
|
+
dagTimelineViewportState: null,
|
|
35
|
+
dagNodeOutputViewportState: null,
|
|
36
|
+
runOutputViewportState: null,
|
|
37
|
+
pollingGeneration: 0,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/** Per-dagRunId graph viewport restore map (stable Map instance). */
|
|
41
|
+
export const dagGraphViewportStates = new Map();
|
|
42
|
+
|
|
43
|
+
export function isPageVisible() {
|
|
44
|
+
return (
|
|
45
|
+
typeof document === "undefined" || document.visibilityState !== "hidden"
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function detailPollDelay(status, isVisible) {
|
|
50
|
+
const normalizedStatus = (status ?? "").toLowerCase().replace(/-/g, "_");
|
|
51
|
+
if (TERMINAL_RUN_STATUSES.has(normalizedStatus)) return null;
|
|
52
|
+
return isVisible ? ACTIVE_DETAIL_POLL_MS : HIDDEN_POLL_MS;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function dashboardPollDelay() {
|
|
56
|
+
return isPageVisible() ? DASHBOARD_POLL_MS : HIDDEN_POLL_MS;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function updateDagGraphViewportState(
|
|
60
|
+
_previous,
|
|
61
|
+
dagRunId,
|
|
62
|
+
scrollLeft,
|
|
63
|
+
scrollTop,
|
|
64
|
+
) {
|
|
65
|
+
return {
|
|
66
|
+
dagRunId,
|
|
67
|
+
scrollLeft: Math.max(0, Number(scrollLeft) || 0),
|
|
68
|
+
scrollTop: Math.max(0, Number(scrollTop) || 0),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function captureDagGraphViewportState(previous, dagRunId, viewport) {
|
|
73
|
+
if (!viewport || viewport.dataset?.dagRunId !== dagRunId) return previous;
|
|
74
|
+
return updateDagGraphViewportState(
|
|
75
|
+
previous,
|
|
76
|
+
dagRunId,
|
|
77
|
+
viewport.scrollLeft,
|
|
78
|
+
viewport.scrollTop,
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function updateDagTimelineViewportState(
|
|
83
|
+
_previous,
|
|
84
|
+
dagRunId,
|
|
85
|
+
nodeId,
|
|
86
|
+
scrollLeft,
|
|
87
|
+
scrollTop,
|
|
88
|
+
followLatest,
|
|
89
|
+
) {
|
|
90
|
+
return {
|
|
91
|
+
dagRunId,
|
|
92
|
+
nodeId,
|
|
93
|
+
scrollLeft: Math.max(0, Number(scrollLeft) || 0),
|
|
94
|
+
scrollTop: Math.max(0, Number(scrollTop) || 0),
|
|
95
|
+
followLatest: Boolean(followLatest),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Unified follow-latest helper for DAG node output and Worker output scroll
|
|
100
|
+
// regions. Returns true only when the user is parked within `threshold` px of
|
|
101
|
+
// the bottom, mirroring the existing process-timeline behaviour.
|
|
102
|
+
export function computeFollowLatest(
|
|
103
|
+
scrollTop,
|
|
104
|
+
scrollHeight,
|
|
105
|
+
clientHeight,
|
|
106
|
+
threshold = OUTPUT_FOLLOW_LATEST_PX,
|
|
107
|
+
) {
|
|
108
|
+
const top = Number(scrollTop);
|
|
109
|
+
const height = Number(scrollHeight);
|
|
110
|
+
const client = Number(clientHeight);
|
|
111
|
+
if (
|
|
112
|
+
!Number.isFinite(top) ||
|
|
113
|
+
!Number.isFinite(height) ||
|
|
114
|
+
!Number.isFinite(client)
|
|
115
|
+
) {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
if (height <= 0 || client <= 0) return false;
|
|
119
|
+
return height - top - client < Math.max(0, Number(threshold) || 0);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Generalised viewport-state record keyed by `regionKey`, so the same helper
|
|
123
|
+
// serves the DAG node inspector output ("dag:<runId>:<nodeId>:output") and the
|
|
124
|
+
// Worker run output ("run:<runId>:output").
|
|
125
|
+
export function updateDagOutputViewportState(
|
|
126
|
+
_previous,
|
|
127
|
+
regionKey,
|
|
128
|
+
scrollLeft,
|
|
129
|
+
scrollTop,
|
|
130
|
+
followLatest,
|
|
131
|
+
) {
|
|
132
|
+
return {
|
|
133
|
+
regionKey,
|
|
134
|
+
scrollLeft: Math.max(0, Number(scrollLeft) || 0),
|
|
135
|
+
scrollTop: Math.max(0, Number(scrollTop) || 0),
|
|
136
|
+
followLatest: Boolean(followLatest),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Poll-render decision helpers. Initial paints always rebuild the shell; a
|
|
141
|
+
// non-initial refresh reuses the existing viewport/inspector DOM identity so
|
|
142
|
+
// active pointer-capture targets (scrollbar / resize handle) survive.
|
|
143
|
+
export function shouldReuseDagDetailShell(initial, hasExistingViewport) {
|
|
144
|
+
return !initial && Boolean(hasExistingViewport);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function shouldPreserveDiagnosticsOpen(initial, existingOpen) {
|
|
148
|
+
if (initial) return false;
|
|
149
|
+
return Boolean(existingOpen);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Detects an in-progress resize/scroll gesture so the poll renderer can defer
|
|
153
|
+
// tearing down the interactive shell until the gesture releases.
|
|
154
|
+
export function isInteractiveGestureActive(bodyClassList) {
|
|
155
|
+
const list = bodyClassList;
|
|
156
|
+
if (!list || typeof list.contains !== "function") return false;
|
|
157
|
+
return (
|
|
158
|
+
list.contains("is-resizing-dag-inspector") ||
|
|
159
|
+
list.contains("is-resizing-dag-graph")
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function stopTimers() {
|
|
164
|
+
uiState.pollingGeneration += 1;
|
|
165
|
+
if (uiState.dashboardTimer) {
|
|
166
|
+
clearTimeout(uiState.dashboardTimer);
|
|
167
|
+
uiState.dashboardTimer = null;
|
|
168
|
+
}
|
|
169
|
+
if (uiState.runPollTimer) {
|
|
170
|
+
clearTimeout(uiState.runPollTimer);
|
|
171
|
+
uiState.runPollTimer = null;
|
|
172
|
+
}
|
|
173
|
+
if (uiState.dagPollTimer) {
|
|
174
|
+
clearTimeout(uiState.dagPollTimer);
|
|
175
|
+
uiState.dagPollTimer = null;
|
|
176
|
+
}
|
|
177
|
+
if (uiState.poolTimer) {
|
|
178
|
+
clearTimeout(uiState.poolTimer);
|
|
179
|
+
uiState.poolTimer = null;
|
|
180
|
+
}
|
|
181
|
+
if (uiState.taskPollTimer) {
|
|
182
|
+
clearTimeout(uiState.taskPollTimer);
|
|
183
|
+
uiState.taskPollTimer = null;
|
|
184
|
+
}
|
|
185
|
+
uiState.currentRunId = null;
|
|
186
|
+
uiState.currentBatchRunId = null;
|
|
187
|
+
uiState.currentDagRunId = null;
|
|
188
|
+
uiState.selectedDagNodeId = null;
|
|
189
|
+
uiState.sessionEventOffset = 0;
|
|
190
|
+
uiState.sessionEvents = [];
|
|
191
|
+
uiState.runEventOffset = 0;
|
|
192
|
+
uiState.runEvents = [];
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** Pure descriptor for loading / empty / degraded / error (REQ-R5-05). */
|
|
196
|
+
export function viewStateDescriptor(kind, message, options = {}) {
|
|
197
|
+
const classByKind = {
|
|
198
|
+
loading: "view-state view-state-loading",
|
|
199
|
+
empty: "view-state view-state-empty empty",
|
|
200
|
+
degraded: "view-state view-state-degraded projection-fault",
|
|
201
|
+
error: "view-state view-state-error",
|
|
202
|
+
};
|
|
203
|
+
const role =
|
|
204
|
+
kind === "error" || kind === "degraded"
|
|
205
|
+
? "alert"
|
|
206
|
+
: kind === "loading"
|
|
207
|
+
? "status"
|
|
208
|
+
: null;
|
|
209
|
+
return {
|
|
210
|
+
kind,
|
|
211
|
+
className: classByKind[kind] ?? "view-state",
|
|
212
|
+
role,
|
|
213
|
+
ariaLive: kind === "loading" ? "polite" : null,
|
|
214
|
+
message: message ?? "",
|
|
215
|
+
detail: options.detail ?? null,
|
|
216
|
+
messageClass:
|
|
217
|
+
kind === "empty" ? "empty" : kind === "loading" ? "muted" : null,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/** Distinguishable loading / empty / degraded / error panel states (REQ-R5-05). */
|
|
222
|
+
export function renderViewState(kind, message, options = {}) {
|
|
223
|
+
const desc = viewStateDescriptor(kind, message, options);
|
|
224
|
+
const node = el("div", desc.className);
|
|
225
|
+
if (desc.role) node.setAttribute("role", desc.role);
|
|
226
|
+
if (desc.ariaLive) node.setAttribute("aria-live", desc.ariaLive);
|
|
227
|
+
node.appendChild(el("p", desc.messageClass, desc.message));
|
|
228
|
+
if (desc.detail) {
|
|
229
|
+
node.appendChild(el("p", "muted", desc.detail));
|
|
230
|
+
}
|
|
231
|
+
return node;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export function mountViewState(container, kind, message, options) {
|
|
235
|
+
if (!container) return null;
|
|
236
|
+
clearNode(container);
|
|
237
|
+
const node = renderViewState(kind, message, options);
|
|
238
|
+
container.appendChild(node);
|
|
239
|
+
return node;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export function setLastSnapshot(snapshot) {
|
|
243
|
+
uiState.lastSnapshot = snapshot;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export function setPollingGeneration(value) {
|
|
247
|
+
uiState.pollingGeneration = value;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export function bumpPollingGeneration() {
|
|
251
|
+
uiState.pollingGeneration += 1;
|
|
252
|
+
return uiState.pollingGeneration;
|
|
253
|
+
}
|