@tea-agent/loop-agent 0.39.2 → 0.40.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (215) hide show
  1. package/CHANGELOG.md +133 -92
  2. package/dist/adapters/context-transfer/optional-pi-handoff.js +31 -0
  3. package/dist/adapters/context-transfer/pi-session.js +61 -0
  4. package/dist/build-stamp.json +3 -3
  5. package/dist/cli/command-definitions.js +12 -0
  6. package/dist/cli/program.js +23 -1
  7. package/dist/commands/dag-artifact.js +284 -0
  8. package/dist/commands/dag-context.js +184 -0
  9. package/dist/commands/dag-rerun.js +206 -1
  10. package/dist/executors/dag-pi-executor.js +108 -19
  11. package/dist/executors/pi-executor.js +4 -2
  12. package/dist/executors/pi-sdk-executor.js +49 -1
  13. package/dist/executors/pi-writer-tool-policy.js +57 -1
  14. package/dist/executors/shell-executor.js +42 -2
  15. package/dist/executors/workspace-write-snapshot.js +68 -0
  16. package/dist/{worker → infrastructure}/console/app-data.js +4 -0
  17. package/dist/infrastructure/console/artifact-revision-store.js +430 -0
  18. package/dist/infrastructure/console/context-export-store.js +160 -0
  19. package/dist/infrastructure/console/dir-lock.js +132 -0
  20. package/dist/infrastructure/harness/artifact-store.js +10 -1
  21. package/dist/infrastructure/harness/atomic-write.js +35 -2
  22. package/dist/shared/context-transfer/artifact-revision.js +172 -0
  23. package/dist/shared/context-transfer.js +418 -0
  24. package/dist/shared/operator/capabilities.js +14 -0
  25. package/dist/shared/operator/safe-run-summary.js +1 -0
  26. package/dist/shared/path-safety.js +93 -0
  27. package/dist/shared/preview.js +28 -4
  28. package/dist/task/config-types.js +10 -0
  29. package/dist/worker/console/chat/chat-event-store.js +77 -7
  30. package/dist/worker/console/chat/context-panel.js +7 -0
  31. package/dist/worker/console/chat/deferred-turn.js +12 -0
  32. package/dist/worker/console/chat/pi-runtime.js +331 -18
  33. package/dist/worker/console/chat/provider-error.js +98 -0
  34. package/dist/worker/console/chat/resource-preferences-store.js +1 -1
  35. package/dist/worker/console/chat/routes.js +389 -56
  36. package/dist/worker/console/chat/session-stats.js +179 -0
  37. package/dist/worker/console/chat/session-store.js +94 -64
  38. package/dist/worker/console/chat/todos.js +115 -0
  39. package/dist/worker/console/chat/tool-preview.js +42 -0
  40. package/dist/worker/console/chat/turn-process.js +20 -55
  41. package/dist/worker/console/chat/user-questions.js +266 -0
  42. package/dist/worker/console/chat/workspace-landing.js +79 -0
  43. package/dist/worker/console/console-update-runtime.js +1 -1
  44. package/dist/worker/console/context-transfer-diagnostics.js +198 -0
  45. package/dist/worker/console/dag-confirmation.js +1 -1
  46. package/dist/worker/console/dag-execution-receipt.js +1 -1
  47. package/dist/worker/console/doctor.js +1 -1
  48. package/dist/worker/console/draft-store.js +1 -1
  49. package/dist/worker/console/human-gate-token.js +1 -1
  50. package/dist/worker/console/index.js +3 -3
  51. package/dist/worker/console/interview/assessment.js +1 -1
  52. package/dist/worker/console/interview/session.js +1 -1
  53. package/dist/worker/console/native-directory-picker.js +13 -0
  54. package/dist/worker/console/operation-runner.js +45 -4
  55. package/dist/worker/console/operation-sse.js +1 -1
  56. package/dist/worker/console/operation-wait.js +1 -1
  57. package/dist/worker/console/operator-actions.js +104 -3
  58. package/dist/worker/console/operator-surface-health.js +1 -1
  59. package/dist/worker/console/operator-user-error.js +128 -0
  60. package/dist/worker/console/routes.js +927 -8
  61. package/dist/worker/console/security.js +39 -4
  62. package/dist/worker/console/server.js +532 -187
  63. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-CTx1TrNa.js → abnfDiagram-N423BO3Z-CpWvwARc.js} +1 -1
  64. package/dist/worker/console/static/assets/{arc-B4i9cVxX.js → arc-DctXgO-g.js} +1 -1
  65. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-BAXE8_Rl.js → architectureDiagram-T3A2C74G-D4mIkoQl.js} +1 -1
  66. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-C5Xh59at.js → blockDiagram-VBNYF7ZC-ClG0--KO.js} +1 -1
  67. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-B_asOzAu.js → c4Diagram-5PPSVZJV-CwdLk78T.js} +1 -1
  68. package/dist/worker/console/static/assets/channel-C9VQZ-ru.js +1 -0
  69. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-wRO_iRNc.js → chunk-2GRJ4B5K-DKczcu-O.js} +1 -1
  70. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-ByOaSh_G.js → chunk-2Q5K7J3B-BV6F-vBI.js} +1 -1
  71. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-DjLzudan.js → chunk-5RXB4S5H-DtojVnvA.js} +1 -1
  72. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-JCNGkXYT.js → chunk-5VM5RSS4-BjDQRGnO.js} +1 -1
  73. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-BhrPXXMZ.js → chunk-6Q2QTUOP-I0fV6ngU.js} +1 -1
  74. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-TJ5JuEBT.js → chunk-GF5L2VYU-BM8C-mmC.js} +1 -1
  75. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-VVthn_mD.js → chunk-JWPE2WC7-BEK-a15e.js} +1 -1
  76. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-D60-Mny4.js → chunk-KBJHAD2P-CQsg_fSR.js} +1 -1
  77. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-zXv7aVye.js → chunk-RYQCIY6F-B-BcaUm3.js} +1 -1
  78. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-By9VQoi1.js → chunk-XXDRQBXY-D1Bu08fg.js} +1 -1
  79. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BsCkw5MF.js +1 -0
  80. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BsCkw5MF.js +1 -0
  81. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-C4zQLe-M.js → cose-bilkent-JH36ORCC-q8nwtxBR.js} +1 -1
  82. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-wh5N2ReN.js → cynefin-VYW2F7L2-CruhpURw.js} +1 -1
  83. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-Dlu5wIqs.js → cynefinDiagram-MW4NZA55-CJne6iYF.js} +1 -1
  84. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-CrDgthc6.js → dagre-VZM6K2ZE-CbeLvBU5.js} +1 -1
  85. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-zN1clg27.js → diagram-7IWD3JNH-DmCxH1Pe.js} +1 -1
  86. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-BN-o7J7-.js → diagram-B4RE2ZJO-DJ5OYhtC.js} +1 -1
  87. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-C8SuPt0d.js → diagram-LBJQPF4R-B-UN-hln.js} +1 -1
  88. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-BoizuHbZ.js → diagram-Q27KOJAE-CcEro8Ey.js} +1 -1
  89. package/dist/worker/console/static/assets/{diagram-UB23O5K3-BzUzXixG.js → diagram-UB23O5K3-BBBzDden.js} +1 -1
  90. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-Ev3lA4Cf.js → ebnfDiagram-BXEA7PRR-xKEyH-oo.js} +1 -1
  91. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-GALUzRbs.js → erDiagram-JOGREHBK-C7xl63VC.js} +1 -1
  92. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-CZJN65Sb.js → flowDiagram-UKHOOZJN-nthQQ9nR.js} +1 -1
  93. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-JLB9OESl.js → ganttDiagram-PKOTCBZU-CWN5mE33.js} +1 -1
  94. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-CcvCr4GD.js → gitGraphDiagram-DS77QQ5N-Wn86tQ6N.js} +1 -1
  95. package/dist/worker/console/static/assets/index-D-0NVyMk.css +1 -0
  96. package/dist/worker/console/static/assets/index-gwyOxQiS.js +407 -0
  97. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-HG5z7aVg.js → infoDiagram-6WML65LV-_WQymGtW.js} +1 -1
  98. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-BHEu9chR.js → ishikawaDiagram-WSZJBQD7-COBeivBm.js} +1 -1
  99. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-Cp_Up8-a.js → journeyDiagram-NVQOT4AX-CwGOJl3Q.js} +1 -1
  100. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-DvVeY-em.js → kanban-definition-27J2QSJJ-Ci0CJ5Vi.js} +1 -1
  101. package/dist/worker/console/static/assets/{linear-aGmcjgTn.js → linear-CPjMq6vN.js} +1 -1
  102. package/dist/worker/console/static/assets/{mermaid.core-Biyo-tqA.js → mermaid.core-Z0wGoe0b.js} +5 -5
  103. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-DyxY2pIb.js → mindmap-definition-FAOFIHXS-dISked0d.js} +1 -1
  104. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-CkdGuc50.js → pegDiagram-VL7TDLO6-BJxgof7Y.js} +1 -1
  105. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-NNQI9xVR.js → pieDiagram-7S7Q4E2Y-CIblU8eW.js} +1 -1
  106. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-PAJkuz_G.js → quadrantDiagram-CIZ2JOQS-vwYiqh8E.js} +1 -1
  107. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-ZwrQod0O.js → railroadDiagram-AXF67PYL-_iOsVeFm.js} +1 -1
  108. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-D3ZfrzM2.js → requirementDiagram-LRYGKXZP-Dk0MLxO_.js} +1 -1
  109. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-DDGFmWk5.js → sankeyDiagram-W5VNT64P-Dx7HtLVW.js} +1 -1
  110. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-DO6mmXTA.js → sequenceDiagram-SI44F4Z6-DRuWjlpL.js} +1 -1
  111. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-DMbCQiJt.js → sizeCapture-X5ZJPWSS-Bhy9DQAA.js} +1 -1
  112. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-Di_YUEfC.js → stateDiagram-OKZ733FA-DAx9twnU.js} +1 -1
  113. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Dmdf8cGy.js +1 -0
  114. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-Bn2PphNJ.js → swimlanes-SLNWSIFB-okMlvVrM.js} +2 -2
  115. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-W1IcAFff.js +8 -0
  116. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-DPh8pRsp.js → timeline-definition-Z64GVDOM-DXqCKe37.js} +1 -1
  117. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-GMfl26pW.js → vennDiagram-T6HMQDX7-CeIUkwBh.js} +1 -1
  118. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-DTlmVrmF.js → wardleyDiagram-T6FBY63Y-BP76BKTN.js} +1 -1
  119. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-YGBjKAS5.js → xychartDiagram-ELKLHX3M-BpVI0OpF.js} +1 -1
  120. package/dist/worker/console/static/index.html +7 -2
  121. package/dist/worker/console/static-src/app/console-types.js +29 -4
  122. package/dist/worker/console/static-src/app/useConsoleShell.js +30 -12
  123. package/dist/worker/console/static-src/app/useOperatorActions.js +36 -6
  124. package/dist/worker/console/static-src/app/useRecoveryConsole.js +10 -11
  125. package/dist/worker/console/static-src/app/useRunProgress.js +2 -1
  126. package/dist/worker/console/static-src/app/useTaskWizard.js +2 -2
  127. package/dist/worker/console/static-src/night/useNightBoard.js +8 -5
  128. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +141 -8
  129. package/dist/worker/console/static-src/operator-chat/compaction-message.js +79 -0
  130. package/dist/worker/console/static-src/operator-chat/details-dag-actions.js +122 -0
  131. package/dist/worker/console/static-src/operator-chat/refs.js +3 -0
  132. package/dist/worker/console/static-src/operator-chat/runtime-selection-labels.js +27 -0
  133. package/dist/worker/console/static-src/operator-chat/spatial-overlay.js +1 -2
  134. package/dist/worker/console/static-src/operator-chat/turn-stream-controller.js +25 -0
  135. package/dist/worker/console/static-src/operator-chat/turn-submission.js +3 -2
  136. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +181 -87
  137. package/dist/worker/console/static-src/operator-chat/useChatStream.js +159 -44
  138. package/dist/worker/console/static-src/operator-chat/useChatThread.js +177 -8
  139. package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +63 -9
  140. package/dist/worker/console/static-src/operator-chat/useWorkspaceBrowserGate.js +4 -0
  141. package/dist/worker/console/static-src/operator-chat/workspace-layout-mode.js +3 -3
  142. package/dist/worker/console/static-src/shell/useWorkspaces.js +299 -0
  143. package/dist/worker/console/static-src/shell/workspace-route.js +339 -0
  144. package/dist/worker/console/workspace-context.js +260 -0
  145. package/dist/worker/console/workspace-registry.js +214 -0
  146. package/dist/worker/continuation/worker-continuation.js +278 -0
  147. package/dist/worker/observe/health.js +1 -1
  148. package/dist/worker/observe/node-transparency.js +572 -0
  149. package/dist/worker/observe/routes.js +51 -1
  150. package/dist/worker/observe/server.js +4 -0
  151. package/dist/worker/observe/static/api.js +94 -10
  152. package/dist/worker/observe/static/app.js +18 -70
  153. package/dist/worker/observe/static/dag-context-reason-labels.d.ts +9 -0
  154. package/dist/worker/observe/static/dag-context-reason-labels.js +120 -0
  155. package/dist/worker/observe/static/index.html +1 -1
  156. package/dist/worker/observe/static/inspect-workspace.js +307 -0
  157. package/dist/worker/observe/static/operator-chrome.css +145 -20
  158. package/dist/worker/observe/static/operator-chrome.d.ts +20 -3
  159. package/dist/worker/observe/static/operator-chrome.js +330 -100
  160. package/dist/worker/observe/static/router.d.ts +46 -0
  161. package/dist/worker/observe/static/router.js +25 -1
  162. package/dist/worker/observe/static/state.d.ts +50 -0
  163. package/dist/worker/observe/static/state.js +175 -1
  164. package/dist/worker/observe/static/styles.css +1039 -128
  165. package/dist/worker/observe/static/views/dag-inspector.js +1218 -60
  166. package/dist/worker/observe/static/views/dag.js +2 -1
  167. package/dist/worker/observe/static/views/dashboard.js +6 -4
  168. package/dist/worker/observe/static/views/night.js +2 -2
  169. package/dist/worker/observe/static/views/pool.js +1 -0
  170. package/dist/worker/observe/static/views/run.js +3 -1
  171. package/dist/worker/observe/static/views/session-timeline.js +75 -10
  172. package/dist/worker/observe/static/views/task.js +1 -1
  173. package/dist/worker/pool/attempt-lease.js +97 -66
  174. package/dist/worker/pool/begin-attempt-with-lease.js +1 -0
  175. package/dist/worker/pool/reconcile.js +46 -1
  176. package/dist/worker/pool/run-store.js +2 -0
  177. package/dist/worker/pool/state-projection.js +2 -0
  178. package/dist/workflows/dag/artifact-bindings.js +149 -0
  179. package/dist/workflows/dag/artifact-revision-schema-registry.js +31 -0
  180. package/dist/workflows/dag/backend-test-pytest-collection.js +11 -10
  181. package/dist/workflows/dag/backend-test-writer-completeness.js +7 -12
  182. package/dist/workflows/dag/context-receipt.js +305 -0
  183. package/dist/workflows/dag/context-transfer/context-bundle.js +423 -0
  184. package/dist/workflows/dag/context-transfer/operator-actions.js +61 -0
  185. package/dist/workflows/dag/context-transfer/renderers.js +93 -0
  186. package/dist/workflows/dag/dynamic-runtime/map.js +1 -0
  187. package/dist/workflows/dag/init-hybrid.js +178 -47
  188. package/dist/workflows/dag/interrupt-request.js +3 -3
  189. package/dist/workflows/dag/lifecycle.js +3 -3
  190. package/dist/workflows/dag/node-execution.js +112 -27
  191. package/dist/workflows/dag/path-safety.js +1 -0
  192. package/dist/workflows/dag/prompt.js +29 -4
  193. package/dist/workflows/dag/rerun-plan.js +272 -0
  194. package/dist/workflows/dag/rerun-run.js +390 -12
  195. package/dist/workflows/dag/skill-snapshot.js +1 -0
  196. package/dist/workflows/dag/types.js +203 -8
  197. package/docs/README.md +1 -0
  198. package/docs/architecture/runtime-boundaries.md +6 -6
  199. package/docs/operations/local-development-environment.md +21 -0
  200. package/docs/templates/agent-dag.schema.json +50 -0
  201. package/docs/templates/backend-test-dag.json +265 -237
  202. package/harness.json +3 -3
  203. package/package.json +5 -1
  204. package/skills/loop-agent/references/command-reference.md +6 -0
  205. package/dist/worker/console/static/assets/channel-Dj_4Moei.js +0 -1
  206. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-C0YYAyCa.js +0 -1
  207. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-C0YYAyCa.js +0 -1
  208. package/dist/worker/console/static/assets/index-CW88Fjbw.js +0 -406
  209. package/dist/worker/console/static/assets/index-DP0ZJl39.css +0 -1
  210. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DrdDvela.js +0 -1
  211. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-DPkEAULl.js +0 -8
  212. package/dist/worker/console/static-src/operator-chat/activity-rail-presentation.js +0 -73
  213. package/dist/worker/console/static-src/operator-chat/useActivityRailTransition.js +0 -59
  214. /package/dist/{worker → infrastructure}/console/operation-store.js +0 -0
  215. /package/dist/{worker → infrastructure}/console/repo-fingerprint.js +0 -0
@@ -0,0 +1,50 @@
1
+ /** 与 state.js 对应的类型声明(供 vitest/TS 侧导入契约测试)。 */
2
+
3
+ export type ViewStateKind = "loading" | "empty" | "degraded" | "error";
4
+
5
+ export type ViewStateDescriptor = {
6
+ kind: ViewStateKind;
7
+ className: string;
8
+ role: string | null;
9
+ ariaLive: string | null;
10
+ message: string;
11
+ detail: string | null;
12
+ messageClass: string | null;
13
+ };
14
+
15
+ export const uiState: {
16
+ lastSnapshot: unknown;
17
+ specEvidenceDetail: {
18
+ source: string;
19
+ path: string;
20
+ loading: boolean;
21
+ error: string | null;
22
+ data: unknown;
23
+ triggerId: string | null;
24
+ } | null;
25
+ dagNodeExecutionOutputCache: Map<string, unknown> | null;
26
+ [key: string]: unknown;
27
+ };
28
+
29
+ export function setWorkspaceScope(workspaceId: string | null): void;
30
+ export function workspaceScope(): string | null;
31
+ export function scopedApiUrl(url: string): string;
32
+ export function workspaceRequestSignal(): AbortSignal | undefined;
33
+ export function fetchWithWorkspaceScope(
34
+ url: string,
35
+ init?: RequestInit,
36
+ ): Promise<Response>;
37
+ export function abortWorkspaceRequests(): void;
38
+ export function setWorkspaceActive(active: boolean): void;
39
+ export function stopTimers(): void;
40
+ export function pauseForegroundTimers(): void;
41
+
42
+ /** Multi-workspace atomic scope switch (2026-08-26, AC-006). */
43
+ export function applyWorkspaceScopeSwitch(
44
+ nextWorkspaceId: string | null,
45
+ routeValue?: (() => void) | string,
46
+ options?: {
47
+ route?: (hashValue: string) => void;
48
+ onTrace?: (trace: string[]) => void;
49
+ },
50
+ ): string[];
@@ -82,7 +82,9 @@ export const uiState = {
82
82
  dagNodeExecutionOutputLoading: false,
83
83
  dagNodeExecutionOutputError: null,
84
84
  dagNodeExecutionOutputKey: null,
85
- /** When true,「执行过程」展开 message_start/end 原始协议事件。默认隐藏,用户可点选展开。 */
85
+ /** Unified execution-record filter: all model/tool/process facts by default. */
86
+ executionRecordFilter: "all",
87
+ /** When true,「执行记录」展开 message_start/end 原始协议事件。默认隐藏,避免重复淹没语义记录。 */
86
88
  sessionTimelineShowProtocol: false,
87
89
  /** Open <details> keys restored across timeline re-renders. */
88
90
  sessionTimelineOpenDetails: null,
@@ -116,6 +118,8 @@ export const uiState = {
116
118
  /** Last-rendered spec-evidence list, used to restore the list synchronously. */
117
119
  specEvidenceListEvidence: null,
118
120
  pollingGeneration: 0,
121
+ /** Hosted Inspect workspace is foreground. Standalone defaults to active. */
122
+ workspaceActive: true,
119
123
  };
120
124
 
121
125
  /** Per-dagRunId graph viewport restore map (stable Map instance). */
@@ -279,16 +283,186 @@ export function isPageVisible() {
279
283
  );
280
284
  }
281
285
 
286
+ export function isWorkspaceActive() {
287
+ return uiState.workspaceActive !== false;
288
+ }
289
+
290
+ export function setWorkspaceActive(active) {
291
+ uiState.workspaceActive = Boolean(active);
292
+ }
293
+
294
+ /**
295
+ * Multi-workspace scope (2026-08-23): every Observe GET must carry the
296
+ * current workspaceId (AC-002/AC-003). The scope is synced from the parent
297
+ * Console shell hash (`?workspaceId=`) or via postMessage for hosted mode;
298
+ * standalone pages default to null and all workspace-scoped routes fail closed
299
+ * until the host or hash publishes an explicit workspace id.
300
+ */
301
+ let currentWorkspaceScope = null;
302
+ let currentWorkspaceRequestController =
303
+ typeof AbortController === "undefined" ? null : new AbortController();
304
+
305
+ export function setWorkspaceScope(workspaceId) {
306
+ currentWorkspaceScope =
307
+ typeof workspaceId === "string" && workspaceId.trim()
308
+ ? workspaceId.trim()
309
+ : null;
310
+ }
311
+
312
+ export function workspaceScope() {
313
+ return currentWorkspaceScope;
314
+ }
315
+
316
+ /** Signal shared by every workspace-scoped Observe request in the active scope. */
317
+ export function workspaceRequestSignal() {
318
+ if (
319
+ typeof AbortController !== "undefined" &&
320
+ (!currentWorkspaceRequestController ||
321
+ currentWorkspaceRequestController.signal.aborted)
322
+ ) {
323
+ currentWorkspaceRequestController = new AbortController();
324
+ }
325
+ return currentWorkspaceRequestController?.signal;
326
+ }
327
+
328
+ function combinedRequestSignal(scopeSignal, requestSignal) {
329
+ const signals = [scopeSignal, requestSignal].filter(Boolean);
330
+ if (signals.length <= 1) return { signal: signals[0], cleanup: () => {} };
331
+ const controller = new AbortController();
332
+ const abort = () => controller.abort();
333
+ for (const signal of signals) {
334
+ if (signal.aborted) abort();
335
+ else signal.addEventListener("abort", abort, { once: true });
336
+ }
337
+ return {
338
+ signal: controller.signal,
339
+ cleanup: () => {
340
+ for (const signal of signals) signal.removeEventListener("abort", abort);
341
+ },
342
+ };
343
+ }
344
+
345
+ /** Fetch through the current workspace boundary and abort on any scope swap. */
346
+ export async function fetchWithWorkspaceScope(url, init = {}) {
347
+ const { signal: requestSignal, ...rest } = init;
348
+ const combined = combinedRequestSignal(
349
+ workspaceRequestSignal(),
350
+ requestSignal,
351
+ );
352
+ try {
353
+ return await fetch(scopedApiUrl(url), {
354
+ ...rest,
355
+ ...(combined.signal ? { signal: combined.signal } : {}),
356
+ });
357
+ } finally {
358
+ combined.cleanup();
359
+ }
360
+ }
361
+
362
+ /** Abort every old-scope request and invalidate request identity guards. */
363
+ export function abortWorkspaceRequests() {
364
+ currentWorkspaceRequestController?.abort();
365
+ currentWorkspaceRequestController =
366
+ typeof AbortController === "undefined" ? null : new AbortController();
367
+ uiState.dagHistoryAbortController?.abort?.();
368
+ uiState.dagHistoryAbortController = null;
369
+ uiState.dagHistoryRequestSeq += 1;
370
+ uiState.dagHistoryPrefetchGeneration += 1;
371
+ uiState.dagNodeInputRequestSeq += 1;
372
+ uiState.dagNodeProcessRequestSeq += 1;
373
+ uiState.dagNodeExecutionOutputRequestSeq += 1;
374
+ }
375
+
376
+ /** Multi-workspace atomic scope switch (2026-08-26, AC-006).
377
+ *
378
+ * Contract order — strictly sequential, no interleaving:
379
+ * 1. abort old — stop every poll/SSE so no late request can race the swap;
380
+ * 2. clear old — drop cached run/task/selection state of the old scope;
381
+ * 3. set scope — publish the new workspaceId for scopedApiUrl;
382
+ * 4. reload new — re-route/re-render with the new scope active.
383
+ *
384
+ * Observability: each stage appends to the returned trace so tests (and
385
+ * diagnostics) can assert the exact ordering; there is no window in which a
386
+ * new route is requested with the old scope or vice versa.
387
+ */
388
+ export function applyWorkspaceScopeSwitch(
389
+ nextWorkspaceId,
390
+ routeValue,
391
+ options = {},
392
+ ) {
393
+ const trace = [];
394
+ // 1. abort old
395
+ abortWorkspaceRequests();
396
+ stopTimers();
397
+ pauseForegroundTimers();
398
+ trace.push("abort-old");
399
+ // 2. clear old
400
+ uiState.lastSnapshot = null;
401
+ uiState.specEvidenceDetail = null;
402
+ uiState.specEvidenceListEvidence = null;
403
+ clearDagNodeExecutionOutputCache();
404
+ trace.push("clear-old");
405
+ // 3. set scope
406
+ setWorkspaceScope(nextWorkspaceId);
407
+ trace.push(`set-scope:${workspaceScope() ?? ""}`);
408
+ // 4. route/reload new
409
+ if (typeof routeValue === "function") routeValue();
410
+ else if (typeof routeValue === "string") options.route?.(routeValue);
411
+ trace.push("route-new");
412
+ if (options.onTrace) options.onTrace(trace);
413
+ return trace;
414
+ }
415
+
416
+ /** Append ?workspaceId= to an observe API URL (real request path kept). */
417
+ export function scopedApiUrl(url) {
418
+ const raw = String(url ?? "");
419
+ if (!currentWorkspaceScope || !raw.startsWith("/api/")) return raw;
420
+ const qIndex = raw.indexOf("?");
421
+ if (qIndex >= 0) {
422
+ const params = new URLSearchParams(raw.slice(qIndex + 1));
423
+ if (params.has("workspaceId")) return raw;
424
+ params.set("workspaceId", currentWorkspaceScope);
425
+ return `${raw.slice(0, qIndex)}?${params.toString()}`;
426
+ }
427
+ return `${raw}?workspaceId=${encodeURIComponent(currentWorkspaceScope)}`;
428
+ }
429
+
282
430
  export function detailPollDelay(status, isVisible) {
283
431
  const normalizedStatus = (status ?? "").toLowerCase().replace(/-/g, "_");
284
432
  if (TERMINAL_RUN_STATUSES.has(normalizedStatus)) return null;
433
+ if (!isWorkspaceActive()) return null;
285
434
  return isVisible ? ACTIVE_DETAIL_POLL_MS : HIDDEN_POLL_MS;
286
435
  }
287
436
 
288
437
  export function dashboardPollDelay() {
438
+ if (!isWorkspaceActive()) return null;
289
439
  return isPageVisible() ? DASHBOARD_POLL_MS : HIDDEN_POLL_MS;
290
440
  }
291
441
 
442
+ /** Stop pausable timers without destroying route/selection/scroll. */
443
+ export function pauseForegroundTimers() {
444
+ if (uiState.dashboardTimer) {
445
+ clearTimeout(uiState.dashboardTimer);
446
+ uiState.dashboardTimer = null;
447
+ }
448
+ if (uiState.runPollTimer) {
449
+ clearTimeout(uiState.runPollTimer);
450
+ uiState.runPollTimer = null;
451
+ }
452
+ if (uiState.dagPollTimer) {
453
+ clearTimeout(uiState.dagPollTimer);
454
+ uiState.dagPollTimer = null;
455
+ }
456
+ if (uiState.poolTimer) {
457
+ clearTimeout(uiState.poolTimer);
458
+ uiState.poolTimer = null;
459
+ }
460
+ if (uiState.taskPollTimer) {
461
+ clearTimeout(uiState.taskPollTimer);
462
+ uiState.taskPollTimer = null;
463
+ }
464
+ }
465
+
292
466
  export function updateDagGraphViewportState(
293
467
  _previous,
294
468
  dagRunId,