@tea-agent/loop-agent 0.13.0-alpha.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.
Files changed (262) hide show
  1. package/AGENTS.md +155 -153
  2. package/CHANGELOG.md +326 -301
  3. package/README.md +345 -326
  4. package/bin/agent-worker.js +22 -22
  5. package/bin/loop-agent.js +21 -21
  6. package/dist/application/dag/generate-task-dag.js +28 -58
  7. package/dist/application/evaluation/candidate-hash.js +75 -0
  8. package/dist/application/evaluation/candidate.js +52 -0
  9. package/dist/application/evaluation/replay.js +289 -0
  10. package/dist/application/evaluation/types.js +130 -0
  11. package/dist/cli/command-definitions.js +17 -4
  12. package/dist/cli/program.js +8 -4
  13. package/dist/commands/cursor-prompt.js +6 -6
  14. package/dist/commands/eval.js +235 -0
  15. package/dist/commands/init.js +544 -506
  16. package/dist/commands/loop-benchmark.js +11 -11
  17. package/dist/commands/pi-reuse-benchmark.js +16 -16
  18. package/dist/executors/pi-sdk-executor.js +38 -24
  19. package/dist/executors/shell-executor.js +34 -2
  20. package/dist/executors/shell-presets.js +20 -0
  21. package/dist/executors/shell-verification.js +7 -0
  22. package/dist/governance/manifest-types.js +1 -0
  23. package/dist/infrastructure/evaluation/candidate-store.js +435 -0
  24. package/dist/infrastructure/evaluation/store.js +40 -0
  25. package/dist/sidecars/cursor-prompt/executor.js +1 -1
  26. package/dist/task/config-types.js +23 -0
  27. package/dist/task/runtime.js +27 -27
  28. package/dist/worker/observe/routes.js +18 -3
  29. package/dist/worker/observe/spec-evidence.js +1 -1
  30. package/dist/worker/observe/static/api.js +46 -46
  31. package/dist/worker/observe/static/app.js +150 -150
  32. package/dist/worker/observe/static/constants.js +148 -148
  33. package/dist/worker/observe/static/copy.js +67 -67
  34. package/dist/worker/observe/static/dag-helpers.js +172 -172
  35. package/dist/worker/observe/static/dag-layout.d.ts +31 -31
  36. package/dist/worker/observe/static/dag-layout.js +83 -83
  37. package/dist/worker/observe/static/dag-model.js +72 -72
  38. package/dist/worker/observe/static/dom.js +61 -61
  39. package/dist/worker/observe/static/format-pool.js +67 -67
  40. package/dist/worker/observe/static/format.js +292 -292
  41. package/dist/worker/observe/static/index.html +308 -308
  42. package/dist/worker/observe/static/kpi.js +94 -94
  43. package/dist/worker/observe/static/relations.js +133 -133
  44. package/dist/worker/observe/static/router.js +93 -93
  45. package/dist/worker/observe/static/run-processing.js +148 -148
  46. package/dist/worker/observe/static/shell-chrome.js +68 -68
  47. package/dist/worker/observe/static/state.js +253 -253
  48. package/dist/worker/observe/static/styles.css +1902 -1902
  49. package/dist/worker/observe/static/views/batch.js +227 -227
  50. package/dist/worker/observe/static/views/dag-graph.js +172 -172
  51. package/dist/worker/observe/static/views/dag-inspector.js +607 -596
  52. package/dist/worker/observe/static/views/dag.js +362 -362
  53. package/dist/worker/observe/static/views/dashboard.js +445 -445
  54. package/dist/worker/observe/static/views/failures.js +143 -143
  55. package/dist/worker/observe/static/views/feature.js +492 -492
  56. package/dist/worker/observe/static/views/pool.js +350 -350
  57. package/dist/worker/observe/static/views/run.js +453 -453
  58. package/dist/worker/observe/static/views/session-timeline.js +205 -205
  59. package/dist/worker/observe/static/views/shell.js +7 -7
  60. package/dist/worker/observe/static/views/task.js +314 -314
  61. package/dist/worker/observe/static/views/timeline.js +163 -163
  62. package/dist/workflows/dag/backend-test-analysis-contract.js +120 -0
  63. package/dist/workflows/dag/canvas-observer.js +275 -275
  64. package/dist/workflows/dag/dynamic-runtime/map.js +90 -2
  65. package/dist/workflows/dag/init-hybrid.js +1415 -200
  66. package/dist/workflows/dag/node-execution.js +9 -0
  67. package/dist/workflows/dag/prompt.js +9 -0
  68. package/dist/workflows/dag/report.js +35 -1
  69. package/dist/workflows/dag/runner.js +28 -2
  70. package/dist/workflows/dag/task-demand-routing.js +383 -0
  71. package/dist/workflows/dag/types.js +50 -13
  72. package/dist/workflows/dag/upstream-artifacts.js +1 -0
  73. package/dist/workflows/dag/validate.js +59 -1
  74. package/docs/README.md +106 -104
  75. package/docs/agent-dag-recovery-playbook.md +195 -193
  76. package/docs/agent-dag-runner.md +67 -67
  77. package/docs/architecture/README.md +26 -26
  78. package/docs/architecture/dag-execution.md +140 -140
  79. package/docs/architecture/evolution.md +54 -54
  80. package/docs/architecture/facts-and-state.md +71 -71
  81. package/docs/architecture/runtime-boundaries.md +191 -191
  82. package/docs/architecture/system-overview.md +93 -93
  83. package/docs/architecture/worker-and-feature.md +85 -85
  84. package/docs/cursor-prompt-sidecar.md +36 -36
  85. package/docs/decisions/README.md +18 -18
  86. package/docs/design/README.md +167 -85
  87. package/docs/development-principles.md +73 -73
  88. package/docs/exec-plans/README.md +6 -6
  89. package/docs/exec-plans/active/README.md +15 -11
  90. package/docs/exec-plans/completed/README.md +85 -74
  91. package/docs/feature-workflow.md +389 -339
  92. package/docs/harness-methodology-debugging.md +153 -153
  93. package/docs/harness-methodology-tdd.md +130 -130
  94. package/docs/harness-methodology-verification.md +27 -27
  95. package/docs/init-surface.manifest.json +289 -280
  96. package/docs/loop-agent-harness.md +142 -141
  97. package/docs/production-readiness.md +96 -96
  98. package/docs/progress/README.md +64 -58
  99. package/docs/reports/README.md +117 -100
  100. package/docs/skills/README.md +7 -7
  101. package/docs/skills/vetted-skill-registry.md +29 -27
  102. package/docs/templates/adr.md +60 -60
  103. package/docs/templates/agent-dag-authority-surface-audit.prompt.md +94 -94
  104. package/docs/templates/agent-dag-decision-envelope.schema.json +213 -213
  105. package/docs/templates/agent-dag-decision-gate-dogfood-report.md +117 -117
  106. package/docs/templates/agent-dag-decision-gate.prompt.md +246 -246
  107. package/docs/templates/agent-dag-process-supervisor.prompt.md +98 -98
  108. package/docs/templates/agent-dag-report.schema.json +473 -473
  109. package/docs/templates/agent-dag-review-verdict.prompt.md +68 -68
  110. package/docs/templates/agent-dag.base.json +190 -190
  111. package/docs/templates/agent-dag.final-verification.json +185 -185
  112. package/docs/templates/agent-dag.schema.json +411 -383
  113. package/docs/templates/agent-dag.supervised-implementation.json +501 -501
  114. package/docs/templates/backend-test-analysis.schema.json +44 -0
  115. package/docs/templates/backend-test-dag.generate-pytest.prompt.md +202 -139
  116. package/docs/templates/backend-test-dag.json +311 -288
  117. package/docs/templates/backend-test-dag.retrospect.prompt.md +125 -125
  118. package/docs/templates/backend-test-dag.review-cases.prompt.md +81 -81
  119. package/docs/templates/exec-plan.md +64 -64
  120. package/docs/templates/feature-spec.md +53 -53
  121. package/docs/templates/frontend-design-contract.md +42 -33
  122. package/docs/templates/frontend-task-constraints.md +35 -25
  123. package/docs/templates/frontend-task-requirement.md +70 -61
  124. package/docs/templates/frontend-test-dag.generate-cases.prompt.md +5 -0
  125. package/docs/templates/frontend-test-dag.json +23 -0
  126. package/docs/templates/frontend-test-dag.retrieve-context.prompt.md +3 -0
  127. package/docs/templates/frontend-test-dag.retrospect.prompt.md +3 -0
  128. package/docs/templates/frontend-test-dag.review-cases.prompt.md +3 -0
  129. package/docs/templates/frontend-test-dag.review-execution.prompt.md +3 -0
  130. package/docs/templates/harness.schema.json +221 -221
  131. package/docs/templates/hybrid-dag.json +188 -188
  132. package/docs/templates/init-evolution-review.md +35 -35
  133. package/docs/templates/interactive-ui-round2-experiment.md +66 -66
  134. package/docs/templates/knowledge-graph-bootstrap-dag.json +118 -118
  135. package/docs/templates/knowledge-sync-dag.json +178 -177
  136. package/docs/templates/knowledge-sync-draft.schema.json +71 -71
  137. package/docs/templates/product-line/AGENTS.md +8 -8
  138. package/docs/templates/product-line/README.md +9 -9
  139. package/docs/templates/product-line/acceptance.yaml +14 -14
  140. package/docs/templates/product-line/closeout.yaml +9 -9
  141. package/docs/templates/product-line/design.md +13 -13
  142. package/docs/templates/product-line/links.md +10 -10
  143. package/docs/templates/product-line/requirement.md +17 -17
  144. package/docs/templates/product-line/task-graph.yaml +15 -15
  145. package/docs/templates/product-line/task.yaml +64 -64
  146. package/docs/templates/product-line/test-plan.md +7 -7
  147. package/docs/templates/production-readiness-checklist.md +57 -57
  148. package/docs/templates/progress-log.md +17 -17
  149. package/docs/templates/project-start-checklist.md +9 -9
  150. package/docs/templates/qa-report.md +48 -48
  151. package/docs/templates/sprint-contract.md +29 -29
  152. package/docs/templates/worker-dogfood-evidence.md +80 -80
  153. package/docs/templates/worker-dogfood-setup.md +68 -68
  154. package/docs/verification-matrix.md +70 -67
  155. package/examples/decision-gate-agent-dag.json +177 -177
  156. package/examples/example-dag.json +46 -46
  157. package/examples/hybrid-loop-agent-dag.json +189 -189
  158. package/harness.json +66 -66
  159. package/package.json +88 -52
  160. package/scripts/check-product-line-docs.sh +29 -29
  161. package/scripts/check-task-pool-root.sh +32 -32
  162. package/scripts/kb-bootstrap-init-skeleton.sh +240 -239
  163. package/scripts/kb-graph-incremental-prepare.mjs +386 -372
  164. package/scripts/kb-graph-incremental-prepare.sh +5 -5
  165. package/scripts/kb-graph-materialize.mjs +105 -105
  166. package/scripts/kb-graph-materialize.sh +4 -4
  167. package/scripts/kb-graph-promote.mjs +164 -153
  168. package/scripts/kb-graph-promote.sh +4 -4
  169. package/scripts/kb-query.mjs +554 -554
  170. package/scripts/kb-query.sh +5 -5
  171. package/skills/agent-worker/SKILL.md +39 -39
  172. package/skills/agent-worker/references/agent-worker-operator.md +60 -60
  173. package/skills/ai-engineering-context/SKILL.md +48 -48
  174. package/skills/analyze-product-dependencies/SKILL.md +67 -0
  175. package/skills/analyze-product-dependencies/agents/openai.yaml +4 -0
  176. package/skills/analyze-product-dependencies/references/api-documentation-schema.md +30 -0
  177. package/skills/analyze-product-dependencies/references/dependency-analysis-schema.md +28 -0
  178. package/skills/analyze-product-dependencies/references/example.md +76 -0
  179. package/skills/analyze-product-dependencies/references/forward-test-cases.md +35 -0
  180. package/skills/analyze-product-dependencies/references/input-contract.md +11 -0
  181. package/skills/analyze-product-dependencies/references/scouting-rules.md +61 -0
  182. package/skills/analyze-product-dependencies/scripts/test-validators.mjs +267 -0
  183. package/skills/analyze-product-dependencies/scripts/validate-api-documentation.mjs +101 -0
  184. package/skills/analyze-product-dependencies/scripts/validate-dependency-analysis.mjs +142 -0
  185. package/skills/analyze-product-dependencies/scripts/validate-product-requirement-input.mjs +76 -0
  186. package/skills/analyze-product-dependencies/scripts/validation-helpers.mjs +146 -0
  187. package/skills/analyze-product-requirements/SKILL.md +90 -0
  188. package/skills/analyze-product-requirements/agents/openai.yaml +4 -0
  189. package/skills/analyze-product-requirements/references/acceptance-criteria.md +91 -0
  190. package/skills/analyze-product-requirements/references/clarification-and-knowledge.md +56 -0
  191. package/skills/analyze-product-requirements/references/example.md +86 -0
  192. package/skills/analyze-product-requirements/references/forward-test-cases.md +66 -0
  193. package/skills/analyze-product-requirements/references/product-analysis-schema.md +32 -0
  194. package/skills/analyze-product-requirements/references/product-requirement-schema.md +33 -0
  195. package/skills/analyze-product-requirements/references/requirement-clarification-schema.md +35 -0
  196. package/skills/analyze-product-requirements/scripts/test-validators.mjs +193 -0
  197. package/skills/analyze-product-requirements/scripts/validate-product-analysis.mjs +69 -0
  198. package/skills/analyze-product-requirements/scripts/validate-product-requirement.mjs +97 -0
  199. package/skills/analyze-product-requirements/scripts/validate-requirement-clarification.mjs +98 -0
  200. package/skills/analyze-product-requirements/scripts/validation-helpers.mjs +156 -0
  201. package/skills/code-review-core/SKILL.md +20 -20
  202. package/skills/codebase-scout/SKILL.md +19 -19
  203. package/skills/frontend-design-review/SKILL.md +66 -61
  204. package/skills/frontend-design-review/references/review-checklist.md +58 -37
  205. package/skills/frontend-implementation/SKILL.md +45 -52
  206. package/skills/frontend-implementation/references/code-standards.md +32 -34
  207. package/skills/frontend-implementation/references/design-spec.md +46 -46
  208. package/skills/frontend-implementation/references/node-contracts.md +76 -63
  209. package/skills/frontend-review/SKILL.md +59 -53
  210. package/skills/frontend-review/references/review-findings.md +47 -42
  211. package/skills/frontend-verification/SKILL.md +53 -40
  212. package/skills/frontend-verification/references/verification-checklist.md +68 -56
  213. package/skills/grill-me/SKILL.md +10 -10
  214. package/skills/grill-with-docs/SKILL.md +88 -88
  215. package/skills/grill-with-docs/adr-format.md +47 -47
  216. package/skills/grill-with-docs/context-format.md +60 -60
  217. package/skills/init-capability-evolution/SKILL.md +70 -70
  218. package/skills/loop-agent/SKILL.md +151 -151
  219. package/skills/loop-agent/references/README.md +67 -67
  220. package/skills/loop-agent/references/command-reference.md +505 -453
  221. package/skills/loop-agent/references/docs-converge.md +126 -126
  222. package/skills/loop-agent/references/harness-policy.md +263 -263
  223. package/skills/loop-agent/references/hybrid-dag.md +238 -233
  224. package/skills/loop-agent/references/learned/README.md +21 -21
  225. package/skills/loop-agent/references/long-running-loop.md +57 -57
  226. package/skills/loop-agent/references/model-routing.md +36 -36
  227. package/skills/loop-agent/references/multi-worktree.md +54 -54
  228. package/skills/loop-agent/references/one-shot-runs.md +85 -85
  229. package/skills/loop-agent/references/orchestrator-and-interventions.md +169 -169
  230. package/skills/loop-agent/references/pi-prompt.md +23 -23
  231. package/skills/loop-agent/references/pi-subagent-assisted-mode.md +84 -84
  232. package/skills/loop-agent/references/post-implementation-and-patterns.md +44 -44
  233. package/skills/loop-agent/references/task-workflow.md +89 -89
  234. package/skills/loop-agent/references/verification-and-failure-handling.md +139 -139
  235. package/skills/playwright-cli/SKILL.md +420 -0
  236. package/skills/playwright-cli/references/element-attributes.md +23 -0
  237. package/skills/playwright-cli/references/playwright-tests.md +39 -0
  238. package/skills/playwright-cli/references/request-mocking.md +87 -0
  239. package/skills/playwright-cli/references/running-code.md +241 -0
  240. package/skills/playwright-cli/references/session-management.md +225 -0
  241. package/skills/playwright-cli/references/storage-state.md +275 -0
  242. package/skills/playwright-cli/references/test-generation.md +433 -0
  243. package/skills/playwright-cli/references/tracing.md +139 -0
  244. package/skills/playwright-cli/references/video-recording.md +143 -0
  245. package/skills/playwright-cli-case-generator/SKILL.md +74 -0
  246. package/skills/requesting-code-review/SKILL.md +101 -101
  247. package/skills/requesting-code-review/code-reviewer.md +168 -168
  248. package/skills/systematic-debugging/CREATION-LOG.md +119 -119
  249. package/skills/systematic-debugging/SKILL.md +296 -296
  250. package/skills/systematic-debugging/condition-based-waiting-example.ts +158 -158
  251. package/skills/systematic-debugging/condition-based-waiting.md +115 -115
  252. package/skills/systematic-debugging/defense-in-depth.md +122 -122
  253. package/skills/systematic-debugging/find-polluter.sh +63 -63
  254. package/skills/systematic-debugging/root-cause-tracing.md +169 -169
  255. package/skills/systematic-debugging/test-academic.md +14 -14
  256. package/skills/systematic-debugging/test-pressure-1.md +58 -58
  257. package/skills/systematic-debugging/test-pressure-2.md +68 -68
  258. package/skills/systematic-debugging/test-pressure-3.md +69 -69
  259. package/skills/test-driven-development/SKILL.md +20 -20
  260. package/skills/using-git-worktrees/SKILL.md +215 -215
  261. package/skills/verification-before-completion/SKILL.md +154 -154
  262. 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
+ }