@tea-agent/loop-agent 0.37.0 → 0.37.1-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 (192) hide show
  1. package/AGENTS.md +2 -6
  2. package/CHANGELOG.md +71 -144
  3. package/README.md +1 -1
  4. package/bin/loop-agent.js +1 -37
  5. package/dist/application/task-lifecycle/advance.js +0 -1
  6. package/dist/application/task-lifecycle/observe.js +0 -15
  7. package/dist/application/task-lifecycle/plan-transitions.js +0 -15
  8. package/dist/cli/command-definitions.js +0 -7
  9. package/dist/cli/program.js +3 -8
  10. package/dist/commands/init-upgrade.js +19 -351
  11. package/dist/commands/init.js +67 -14
  12. package/dist/commands/run-dag-progress.js +0 -14
  13. package/dist/commands/task-advance.js +3 -33
  14. package/dist/executors/dag-pi-executor.js +30 -47
  15. package/dist/executors/pi-sdk-executor.js +0 -34
  16. package/dist/executors/shell-executor.js +95 -6
  17. package/dist/shared/operator/capabilities.js +13 -200
  18. package/dist/shared/package-metadata.js +0 -42
  19. package/dist/task/contract/project.js +0 -4
  20. package/dist/task/source-prepare/completeness.js +0 -53
  21. package/dist/task/source-prepare/parse-intent.js +12 -82
  22. package/dist/task/source-prepare/prepare.js +5 -102
  23. package/dist/task/source-prepare/semantic-intake.js +23 -144
  24. package/dist/worker/console/chat/artifact-card.js +1 -8
  25. package/dist/worker/console/chat/chat-event-store.js +0 -152
  26. package/dist/worker/console/chat/human-gate-card.js +1 -9
  27. package/dist/worker/console/chat/operation-card.js +2 -71
  28. package/dist/worker/console/chat/pi-runtime.js +62 -111
  29. package/dist/worker/console/chat/routes.js +10 -43
  30. package/dist/worker/console/chat/session-store.js +1 -9
  31. package/dist/worker/console/chat/shortcuts.js +7 -9
  32. package/dist/worker/console/chat/turn-process.js +23 -204
  33. package/dist/worker/console/chat/workspace-landing.js +1 -2
  34. package/dist/worker/console/operation-runner.js +6 -155
  35. package/dist/worker/console/operator-actions.js +14 -391
  36. package/dist/worker/console/operator-user-error.js +0 -4
  37. package/dist/worker/console/recovery-cta.js +3 -50
  38. package/dist/worker/console/static/assets/index-HX1pbOyl.css +1 -0
  39. package/dist/worker/console/static/assets/index-M0BLEBfh.js +56 -0
  40. package/dist/worker/console/static/index.html +2 -2
  41. package/dist/worker/console/static-src/app/console-types.js +9 -13
  42. package/dist/worker/console/static-src/app/useOperatorActions.js +2 -4
  43. package/dist/worker/console/static-src/app/useRecoveryActions.js +56 -65
  44. package/dist/worker/console/static-src/app/useRecoveryConsole.js +1 -73
  45. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +30 -114
  46. package/dist/worker/console/static-src/operator-chat/refs.js +0 -3
  47. package/dist/worker/console/static-src/operator-chat/spatial-overlay.js +1 -2
  48. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +13 -67
  49. package/dist/worker/console/static-src/operator-chat/useChatThread.js +6 -12
  50. package/dist/worker/console/static-src/operator-chat/useComposer.js +8 -45
  51. package/dist/worker/console/static-src/operator-chat/workspace-layout-mode.js +3 -7
  52. package/dist/worker/loop-agent/loop-agent-client.js +3 -17
  53. package/dist/worker/observability/read-model.js +0 -20
  54. package/dist/worker/observe/spec-evidence.js +8 -3
  55. package/dist/worker/observe/static/operator-chrome.d.ts +0 -1
  56. package/dist/worker/observe/static/operator-chrome.js +3 -10
  57. package/dist/worker/observe/static/views/dag-inspector.js +71 -6
  58. package/dist/worker/observe/static/views/dag.js +0 -12
  59. package/dist/worker/preflight.js +1 -2
  60. package/dist/workflows/dag/backend-test-case-coverage-analysis.js +707 -37
  61. package/dist/workflows/dag/backend-test-case-manifest.js +4 -0
  62. package/dist/workflows/dag/backend-test-markdown-workflow.js +109 -7
  63. package/dist/workflows/dag/backend-test-module-stem.js +5 -0
  64. package/dist/workflows/dag/backend-test-pytest-collection.js +345 -24
  65. package/dist/workflows/dag/backend-test-scenario-param.js +890 -164
  66. package/dist/workflows/dag/backend-test-writer-completeness.js +47 -16
  67. package/dist/workflows/dag/dynamic-runtime/map.js +24 -8
  68. package/dist/workflows/dag/dynamic-runtime/shared.js +1 -9
  69. package/dist/workflows/dag/frontend-implementation-contract.js +39 -233
  70. package/dist/workflows/dag/frontend-prewrite-gate.js +61 -364
  71. package/dist/workflows/dag/frontend-repair.js +18 -219
  72. package/dist/workflows/dag/frontend-verification-trace.js +32 -47
  73. package/dist/workflows/dag/init-hybrid.js +82 -165
  74. package/dist/workflows/dag/node-execution.js +0 -89
  75. package/dist/workflows/dag/recovery-recommendation.js +0 -58
  76. package/dist/workflows/dag/runner.js +15 -333
  77. package/dist/workflows/dag/scheduler.js +3 -257
  78. package/dist/workflows/dag/types.js +9 -130
  79. package/docs/README.md +3 -3
  80. package/docs/architecture/evolution.md +67 -102
  81. package/docs/templates/backend-test-dag.json +50 -32
  82. package/docs/templates/init-managed-agents.md +2 -5
  83. package/harness.json +2 -2
  84. package/package.json +3 -9
  85. package/skills/loop-agent/SKILL.md +0 -1
  86. package/skills/loop-agent/references/command-reference.md +0 -4
  87. package/dist/build-stamp.json +0 -6
  88. package/dist/commands/dag-request-interrupt.js +0 -20
  89. package/dist/worker/console/chat/assistant-content.js +0 -121
  90. package/dist/worker/console/chat/semantic-activity.js +0 -477
  91. package/dist/worker/console/operation-run-facts.js +0 -190
  92. package/dist/worker/console/operation-wait.js +0 -355
  93. package/dist/worker/console/recovery-error-copy.js +0 -198
  94. package/dist/worker/console/static/assets/abnfDiagram-N423BO3Z-DSvF6RXQ.js +0 -1
  95. package/dist/worker/console/static/assets/arc-DuI4ogtJ.js +0 -1
  96. package/dist/worker/console/static/assets/architectureDiagram-T3A2C74G-DdBbgD4y.js +0 -36
  97. package/dist/worker/console/static/assets/blockDiagram-VBNYF7ZC-xQC2HELm.js +0 -132
  98. package/dist/worker/console/static/assets/c4Diagram-5PPSVZJV-DPMSgwIZ.js +0 -10
  99. package/dist/worker/console/static/assets/channel-DLVz9TYW.js +0 -1
  100. package/dist/worker/console/static/assets/chunk-2GRJ4B5K-WTEKTxin.js +0 -1
  101. package/dist/worker/console/static/assets/chunk-2Q5K7J3B-B6vZ0Pv_.js +0 -1
  102. package/dist/worker/console/static/assets/chunk-5RXB4S5H-BrerNAix.js +0 -231
  103. package/dist/worker/console/static/assets/chunk-5VM5RSS4-D_ch2aaI.js +0 -15
  104. package/dist/worker/console/static/assets/chunk-6Q2QTUOP-VvfacNrq.js +0 -88
  105. package/dist/worker/console/static/assets/chunk-GF5L2VYU-B-uGFIg2.js +0 -206
  106. package/dist/worker/console/static/assets/chunk-JWPE2WC7-BlysCtod.js +0 -1
  107. package/dist/worker/console/static/assets/chunk-KBJHAD2P-BjAUAfcT.js +0 -1
  108. package/dist/worker/console/static/assets/chunk-RYQCIY6F-BeZnBmb-.js +0 -1
  109. package/dist/worker/console/static/assets/chunk-XXDRQBXY-Bbfe3UJG.js +0 -1
  110. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BeAuGcO9.js +0 -1
  111. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BeAuGcO9.js +0 -1
  112. package/dist/worker/console/static/assets/cose-bilkent-JH36ORCC-BKJQnYVM.js +0 -1
  113. package/dist/worker/console/static/assets/cynefin-VYW2F7L2-B1wtI3hU.js +0 -166
  114. package/dist/worker/console/static/assets/cynefinDiagram-MW4NZA55-DCF6DBSu.js +0 -62
  115. package/dist/worker/console/static/assets/cytoscape.esm-yzknjiTM.js +0 -321
  116. package/dist/worker/console/static/assets/dagre-VZM6K2ZE-BJyNGRlP.js +0 -4
  117. package/dist/worker/console/static/assets/defaultLocale-DX6XiGOO.js +0 -1
  118. package/dist/worker/console/static/assets/diagram-7IWD3JNH-B7j1-VsZ.js +0 -30
  119. package/dist/worker/console/static/assets/diagram-B4RE2ZJO-BREJ140a.js +0 -3
  120. package/dist/worker/console/static/assets/diagram-LBJQPF4R-CDPHmHGH.js +0 -24
  121. package/dist/worker/console/static/assets/diagram-Q27KOJAE-BtZLrez1.js +0 -24
  122. package/dist/worker/console/static/assets/diagram-UB23O5K3-mzybu8oU.js +0 -41
  123. package/dist/worker/console/static/assets/ebnfDiagram-BXEA7PRR-Dk3I2uO6.js +0 -1
  124. package/dist/worker/console/static/assets/erDiagram-JOGREHBK-BT97u_tQ.js +0 -85
  125. package/dist/worker/console/static/assets/flowDiagram-UKHOOZJN-CMOtYzui.js +0 -156
  126. package/dist/worker/console/static/assets/ganttDiagram-PKOTCBZU-DiNydkuY.js +0 -292
  127. package/dist/worker/console/static/assets/gitGraphDiagram-DS77QQ5N-Cwt6fLkl.js +0 -106
  128. package/dist/worker/console/static/assets/graph-DOmOIIwC.js +0 -1
  129. package/dist/worker/console/static/assets/index-DQxiso1w.js +0 -325
  130. package/dist/worker/console/static/assets/index-_j0Hzo69.css +0 -1
  131. package/dist/worker/console/static/assets/infoDiagram-6WML65LV-AsGIGKKB.js +0 -2
  132. package/dist/worker/console/static/assets/init-Gi6I4Gst.js +0 -1
  133. package/dist/worker/console/static/assets/ishikawaDiagram-WSZJBQD7-BL8ITZh3.js +0 -70
  134. package/dist/worker/console/static/assets/journeyDiagram-NVQOT4AX-VPKAUX8M.js +0 -139
  135. package/dist/worker/console/static/assets/kanban-definition-27J2QSJJ-BL56hfGp.js +0 -89
  136. package/dist/worker/console/static/assets/layout-D-LzfAck.js +0 -1
  137. package/dist/worker/console/static/assets/linear-BmSG3nIZ.js +0 -1
  138. package/dist/worker/console/static/assets/map-DxJ2ADlA.js +0 -1
  139. package/dist/worker/console/static/assets/mermaid.core-DWGK9kwP.js +0 -308
  140. package/dist/worker/console/static/assets/mindmap-definition-FAOFIHXS--vvKLjo0.js +0 -96
  141. package/dist/worker/console/static/assets/ordinal-Cboi1Yqb.js +0 -1
  142. package/dist/worker/console/static/assets/pegDiagram-VL7TDLO6-B41mxb2-.js +0 -1
  143. package/dist/worker/console/static/assets/pieDiagram-7S7Q4E2Y-DTlu77sw.js +0 -39
  144. package/dist/worker/console/static/assets/quadrantDiagram-CIZ2JOQS-Bk5A7alv.js +0 -7
  145. package/dist/worker/console/static/assets/railroadDiagram-AXF67PYL-Csf9jzQy.js +0 -1
  146. package/dist/worker/console/static/assets/requirementDiagram-LRYGKXZP-CeptoSNx.js +0 -84
  147. package/dist/worker/console/static/assets/sankeyDiagram-W5VNT64P-A-DLCLer.js +0 -40
  148. package/dist/worker/console/static/assets/sequenceDiagram-SI44F4Z6-22VbPdOI.js +0 -162
  149. package/dist/worker/console/static/assets/sizeCapture-X5ZJPWSS-DWvAyBC9.js +0 -1
  150. package/dist/worker/console/static/assets/stateDiagram-OKZ733FA-qICZ75yT.js +0 -1
  151. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DzKvfHTX.js +0 -1
  152. package/dist/worker/console/static/assets/swimlanes-SLNWSIFB-xi7lcMyp.js +0 -2
  153. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Cxqr_qM1.js +0 -8
  154. package/dist/worker/console/static/assets/timeline-definition-Z64GVDOM-Bu6Nl6O2.js +0 -120
  155. package/dist/worker/console/static/assets/vennDiagram-T6HMQDX7-BzMz4Bn9.js +0 -34
  156. package/dist/worker/console/static/assets/wardleyDiagram-T6FBY63Y-CxaJnzc5.js +0 -78
  157. package/dist/worker/console/static/assets/xychartDiagram-ELKLHX3M-5JK-omQK.js +0 -7
  158. package/dist/worker/console/static/fonts/katex/KaTeX_AMS-Regular.woff2 +0 -0
  159. package/dist/worker/console/static/fonts/katex/KaTeX_Caligraphic-Bold.woff2 +0 -0
  160. package/dist/worker/console/static/fonts/katex/KaTeX_Caligraphic-Regular.woff2 +0 -0
  161. package/dist/worker/console/static/fonts/katex/KaTeX_Fraktur-Bold.woff2 +0 -0
  162. package/dist/worker/console/static/fonts/katex/KaTeX_Fraktur-Regular.woff2 +0 -0
  163. package/dist/worker/console/static/fonts/katex/KaTeX_Main-Bold.woff2 +0 -0
  164. package/dist/worker/console/static/fonts/katex/KaTeX_Main-BoldItalic.woff2 +0 -0
  165. package/dist/worker/console/static/fonts/katex/KaTeX_Main-Italic.woff2 +0 -0
  166. package/dist/worker/console/static/fonts/katex/KaTeX_Main-Regular.woff2 +0 -0
  167. package/dist/worker/console/static/fonts/katex/KaTeX_Math-BoldItalic.woff2 +0 -0
  168. package/dist/worker/console/static/fonts/katex/KaTeX_Math-Italic.woff2 +0 -0
  169. package/dist/worker/console/static/fonts/katex/KaTeX_SansSerif-Bold.woff2 +0 -0
  170. package/dist/worker/console/static/fonts/katex/KaTeX_SansSerif-Italic.woff2 +0 -0
  171. package/dist/worker/console/static/fonts/katex/KaTeX_SansSerif-Regular.woff2 +0 -0
  172. package/dist/worker/console/static/fonts/katex/KaTeX_Script-Regular.woff2 +0 -0
  173. package/dist/worker/console/static/fonts/katex/KaTeX_Size1-Regular.woff2 +0 -0
  174. package/dist/worker/console/static/fonts/katex/KaTeX_Size2-Regular.woff2 +0 -0
  175. package/dist/worker/console/static/fonts/katex/KaTeX_Size3-Regular.woff2 +0 -0
  176. package/dist/worker/console/static/fonts/katex/KaTeX_Size4-Regular.woff2 +0 -0
  177. package/dist/worker/console/static/fonts/katex/KaTeX_Typewriter-Regular.woff2 +0 -0
  178. package/dist/worker/console/static-src/operator-chat/activity-journey.js +0 -125
  179. package/dist/worker/console/static-src/operator-chat/activity-rail-presentation.js +0 -73
  180. package/dist/worker/console/static-src/operator-chat/activity-references.js +0 -20
  181. package/dist/worker/console/static-src/operator-chat/slash-palette-layout.js +0 -24
  182. package/dist/worker/console/static-src/operator-chat/slash-palette-nav.js +0 -141
  183. package/dist/worker/console/static-src/operator-chat/useActivityRailTransition.js +0 -59
  184. package/dist/worker/observability/interrupt-eligibility.js +0 -264
  185. package/dist/workflows/dag/contract-output-registry.js +0 -14
  186. package/dist/workflows/dag/contract-validator-registrations.js +0 -8
  187. package/dist/workflows/dag/frontend-recovery-plan.js +0 -73
  188. package/dist/workflows/dag/frontend-recovery-root-manifest.js +0 -123
  189. package/dist/workflows/dag/frontend-recovery-run.js +0 -539
  190. package/dist/workflows/dag/frontend-writer-recovery.js +0 -106
  191. package/dist/workflows/dag/frontend-writer-rollback.js +0 -821
  192. package/dist/workflows/dag/interrupt-request.js +0 -559
@@ -2,15 +2,11 @@ import { spawn } from "node:child_process";
2
2
  import { appendFileSync, realpathSync, statSync, writeFileSync } from "node:fs";
3
3
  import { mkdir, readFile, writeFile } from "node:fs/promises";
4
4
  import path from "node:path";
5
- import { computeBinarySha256, computePackageFingerprint, findPackageRoot, isJavaScriptScript, normalizeCliVersionLabel, readPackageName, readPackageVersion, resolveBinPaths, resolveCmdShim, } from "../../shared/package-metadata.js";
5
+ import { computeBinarySha256, computePackageFingerprint, findPackageRoot, isJavaScriptScript, readPackageName, readPackageVersion, resolveBinPaths, resolveCmdShim, } from "../../shared/package-metadata.js";
6
6
  import { parseCommandJson } from "./parse-json.js";
7
7
  export const DEFAULT_WORKER_COMMAND_TIMEOUT_MS = 120_000;
8
8
  export const DEFAULT_HEARTBEAT_INTERVAL_MS = 15_000;
9
9
  export const LOOP_AGENT_PACKAGE_NAME = "@tea-agent/loop-agent";
10
- /** Grace window between the SIGTERM and SIGKILL stages of a hard timeout, so a
11
- * runner can persist terminal state (see executeDagCheckpoint) before being
12
- * force-killed. */
13
- export const HARD_TIMEOUT_KILL_GRACE_MS = 10_000;
14
10
  const MAX_BUFFERED_OUTPUT_BYTES = 64 * 1024;
15
11
  /**
16
12
  * Bind evidence supplied by an orchestration layer to the identity exposed by
@@ -182,11 +178,10 @@ export class LoopAgentClient {
182
178
  if (!this._identity) {
183
179
  throw new Error("controller identity: cannot record a reported version without a pinned identity");
184
180
  }
185
- const normalized = normalizeCliVersionLabel(reportedVersion);
186
- if (normalized !== this._identity.packageVersion) {
181
+ if (reportedVersion !== this._identity.packageVersion) {
187
182
  throw new Error(`controller identity: reported version ${reportedVersion} does not match package version ${this._identity.packageVersion}`);
188
183
  }
189
- this._identity = { ...this._identity, reportedVersion: normalized };
184
+ this._identity = { ...this._identity, reportedVersion };
190
185
  return this._identity;
191
186
  }
192
187
  async run(args, options) {
@@ -386,17 +381,10 @@ function spawnCommand(input) {
386
381
  });
387
382
  };
388
383
  let hardTimeout;
389
- let killGraceTimeout;
390
384
  if (input.hardTimeoutMs !== undefined && input.hardTimeoutMs > 0) {
391
385
  hardTimeout = setTimeout(() => {
392
386
  timedOut = true;
393
- // Two-stage termination: SIGTERM first so the runner can persist a
394
- // terminal state (see executeDagCheckpoint signal handler), then
395
- // SIGKILL only after the grace window expires.
396
387
  child.kill("SIGTERM");
397
- killGraceTimeout = setTimeout(() => {
398
- child.kill("SIGKILL");
399
- }, HARD_TIMEOUT_KILL_GRACE_MS);
400
388
  }, input.hardTimeoutMs);
401
389
  }
402
390
  let heartbeatTimer;
@@ -406,8 +394,6 @@ function spawnCommand(input) {
406
394
  const clearTimers = () => {
407
395
  if (hardTimeout !== undefined)
408
396
  clearTimeout(hardTimeout);
409
- if (killGraceTimeout !== undefined)
410
- clearTimeout(killGraceTimeout);
411
397
  if (heartbeatTimer !== undefined) {
412
398
  clearInterval(heartbeatTimer);
413
399
  heartbeatTimer = undefined;
@@ -1491,7 +1491,6 @@ function mergeDagRun(existing, incoming) {
1491
1491
  failureCategory: incoming.failureCategory ?? existing.failureCategory,
1492
1492
  stateConsistent: incoming.stateConsistent ?? existing.stateConsistent,
1493
1493
  recoveryEligibility: incoming.recoveryEligibility ?? existing.recoveryEligibility,
1494
- frontendRecovery: incoming.frontendRecovery ?? existing.frontendRecovery,
1495
1494
  title: incoming.title ?? existing.title,
1496
1495
  startedAt,
1497
1496
  finishedAt,
@@ -1791,23 +1790,6 @@ async function loadFrontendLintProjection(runDir) {
1791
1790
  blockingReasons: parsed.data.blockingReasons,
1792
1791
  };
1793
1792
  }
1794
- function projectFrontendRecovery(state) {
1795
- const s = state.frontendRecoveryState;
1796
- const r = state.frontendRecoveryResult;
1797
- if (!s && !r)
1798
- return undefined;
1799
- return {
1800
- ...(s?.recoveryRootRunId ? { recoveryRootRunId: s.recoveryRootRunId } : {}),
1801
- ...(s?.parentRunId ? { parentRunId: s.parentRunId } : {}),
1802
- ...(s?.childRunId ? { childRunId: s.childRunId } : {}),
1803
- ...(s?.attemptIndex !== undefined ? { attemptIndex: s.attemptIndex } : {}),
1804
- ...(s?.continuationCount !== undefined
1805
- ? { continuationCount: s.continuationCount }
1806
- : {}),
1807
- ...(s?.phase ? { phase: s.phase } : {}),
1808
- ...(r?.outcome ? { outcome: r.outcome } : {}),
1809
- };
1810
- }
1811
1793
  async function parseDagStateFile(statePath, now) {
1812
1794
  try {
1813
1795
  if (!existsSync(statePath))
@@ -1878,7 +1860,6 @@ async function parseDagStateFile(statePath, now) {
1878
1860
  loadBackendTestProjection(runDir, nodes),
1879
1861
  loadFrontendLintProjection(runDir),
1880
1862
  ]);
1881
- const frontendRecovery = projectFrontendRecovery(state);
1882
1863
  const continuationRecord = state.continuation &&
1883
1864
  typeof state.continuation.parentRunId === "string" &&
1884
1865
  typeof state.continuation.effectiveFromNodeId === "string" &&
@@ -1937,7 +1918,6 @@ async function parseDagStateFile(statePath, now) {
1937
1918
  ...(continuation ? { continuation } : {}),
1938
1919
  ...(backendTest ? { backendTest } : {}),
1939
1920
  ...(frontendLint ? { frontendLint } : {}),
1940
- ...(frontendRecovery ? { frontendRecovery } : {}),
1941
1921
  };
1942
1922
  }
1943
1923
  catch {
@@ -359,11 +359,16 @@ export async function extractSpecEvidence(repoRoot, dagRunId, nodeId) {
359
359
  if (status === "no-evidence") {
360
360
  summaryLines.push("未观察到任何规范证据:无 skill 注入、无文件读取、无检索操作。");
361
361
  }
362
- // Openspec directory reads/searches remain in the payload for API consumers
363
- // (tests and downstream projections); the inspector no longer displays them
364
- // separately from the generic spec reads/searches.
362
+ // Separate openspec spec directory reads and searches for Dashboard display.
363
+ // Knowledge base and openspec are parallel sources.
365
364
  const openspecReads = specReads.filter((r) => isOpenspecPath(r.path));
366
365
  const openspecSearches = searches.filter((s) => isOpenspecSearch(s.query, s.path));
366
+ if (openspecReads.length > 0) {
367
+ summaryLines.push(`openspec 已读取 ${openspecReads.length} 个文件:${openspecReads.map((r) => r.path).join("、")}`);
368
+ }
369
+ if (openspecSearches.length > 0) {
370
+ summaryLines.push(`openspec 检索 ${openspecSearches.length} 次。`);
371
+ }
367
372
  return {
368
373
  dagRunId,
369
374
  nodeId,
@@ -49,7 +49,6 @@ export function parseInspectObjectView(
49
49
  export function activeInspectTabHref(hashValue: string): string | null;
50
50
  export function parseInspectContext(hashValue: string): InspectContext | null;
51
51
  export function resolveOperateHrefFromInspectHash(hashValue: string): string;
52
- export function buildOperateInterruptHref(dagRunId: string): string;
53
52
  export function buildInspectDagHref(
54
53
  dagRunId: string,
55
54
  options?: { from?: string },
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Operator Chrome — 统一壳(Operate React 与 Inspect vanilla 共用唯一实现)。
3
3
  *
4
- * 设计真源:docs/design/archive/2026-08-09-unified-operator-chrome.md
4
+ * 设计真源:docs/design/active/2026-08-09-unified-operator-chrome.md
5
5
  * - 一条 chrome 两条带:Band1 全局(品牌/仓库身份/模式分段控件/对象上下文/状态格),
6
6
  * Band2 工作区(下划线 tab,随模式切换内容)。
7
7
  * - 模式语义:操作=做,观测=看同一对象;模式切换在持有 DAG 选中时不丢对象。
@@ -115,13 +115,7 @@ export function resolveOperateHrefFromInspectHash(hashValue) {
115
115
  if (objectView?.view === "dag") {
116
116
  return `/#/recovery?dagRunId=${encodeURIComponent(objectView.id)}`;
117
117
  }
118
- // 2026-08-16: 无持有对象时默认落到对话页(与 Console 默认 landing 一致)。
119
- return "/#/chat";
120
- }
121
-
122
- /** Inspect DAG 详情 → Operate Recovery 协作中止深链(只改导航,不发 mutation)。 */
123
- export function buildOperateInterruptHref(dagRunId) {
124
- return `/#/recovery?dagRunId=${encodeURIComponent(dagRunId)}&intent=interrupt`;
118
+ return "/#/";
125
119
  }
126
120
 
127
121
  /** 操作侧 → Inspect 的 canonical 深链(可携带 from 以激活上下文 chip)。 */
@@ -191,8 +185,7 @@ export function buildOperatorChromeModel(config) {
191
185
  {
192
186
  id: "operate",
193
187
  label: "操作",
194
- // 2026-08-16: 默认落到对话页,与 Console 默认 landing(chat)一致。
195
- href: config.operateHref ?? "/#/chat",
188
+ href: config.operateHref ?? "/#/",
196
189
  active: mode === "operate",
197
190
  },
198
191
  {
@@ -360,9 +360,15 @@ async function renderSpecEvidence(content, dagRunId, nodeId, cachedEvidence) {
360
360
  evidence.sourceBinding?.requirementIds,
361
361
  (id) => el("li", null, id),
362
362
  );
363
+ const openspecReadPaths = new Set(
364
+ (evidence.openspecReads ?? []).map((entry) => entry.path),
365
+ );
366
+ const otherSpecReads = (evidence.specReads ?? []).filter(
367
+ (entry) => !openspecReadPaths.has(entry.path),
368
+ );
363
369
  appendListSection(
364
- `规范已读取(${evidence.specReads?.length ?? 0})`,
365
- evidence.specReads,
370
+ `其他已读取规范文件(${otherSpecReads.length})`,
371
+ otherSpecReads,
366
372
  (read) => {
367
373
  const item = el("li", null);
368
374
  const button = document.createElement("button");
@@ -374,7 +380,7 @@ async function renderSpecEvidence(content, dagRunId, nodeId, cachedEvidence) {
374
380
  button.setAttribute("data-read-at", read.timestamp);
375
381
  }
376
382
  button.id = `spec-evidence-btn-read-${read.path}`;
377
- const icon = el("i", "ri-book-open-line");
383
+ const icon = el("i", "ri-file-text-line");
378
384
  icon.setAttribute("aria-hidden", "true");
379
385
  button.append(icon, el("code", null, read.path));
380
386
  if (read.timestamp) {
@@ -399,10 +405,9 @@ async function renderSpecEvidence(content, dagRunId, nodeId, cachedEvidence) {
399
405
  item.appendChild(button);
400
406
  return item;
401
407
  },
402
- "unavailable:未观察到规范读取。",
403
408
  );
404
409
  appendListSection(
405
- `规范检索(${evidence.specSearches?.length ?? 0})`,
410
+ `检索操作(${evidence.specSearches?.length ?? 0})`,
406
411
  evidence.specSearches,
407
412
  (search) => {
408
413
  const item = el("li", null);
@@ -414,7 +419,6 @@ async function renderSpecEvidence(content, dagRunId, nodeId, cachedEvidence) {
414
419
  );
415
420
  return item;
416
421
  },
417
- "unavailable:未观察到规范检索。",
418
422
  );
419
423
  appendListSection(
420
424
  `知识库查询(${evidence.knowledgeBaseQueries?.length ?? 0})`,
@@ -429,6 +433,67 @@ async function renderSpecEvidence(content, dagRunId, nodeId, cachedEvidence) {
429
433
  "unavailable:未观察到知识库查询;connector 可能未配置或未调用。",
430
434
  );
431
435
 
436
+ // openspec reads are displayed independently from skill/docs reads.
437
+ appendListSection(
438
+ `openspec 已读取(${evidence.openspecReads?.length ?? 0})`,
439
+ evidence.openspecReads,
440
+ (read) => {
441
+ const item = el("li", null);
442
+ const button = document.createElement("button");
443
+ button.type = "button";
444
+ button.className = "spec-evidence-file-btn";
445
+ button.setAttribute("data-source", "read");
446
+ button.setAttribute("data-path", read.path);
447
+ if (read.timestamp) {
448
+ button.setAttribute("data-read-at", read.timestamp);
449
+ }
450
+ button.id = `spec-evidence-btn-read-${read.path}`;
451
+ const icon = el("i", "ri-book-open-line");
452
+ icon.setAttribute("aria-hidden", "true");
453
+ button.append(icon, el("code", null, read.path));
454
+ if (read.timestamp) {
455
+ button.appendChild(
456
+ el(
457
+ "span",
458
+ "spec-evidence-time",
459
+ formatSessionEventTime({ timestamp: read.timestamp }),
460
+ ),
461
+ );
462
+ }
463
+ button.addEventListener("click", () =>
464
+ onSpecEvidenceFileClick(
465
+ content,
466
+ dagRunId,
467
+ nodeId,
468
+ "read",
469
+ read.path,
470
+ button,
471
+ ),
472
+ );
473
+ item.appendChild(button);
474
+ return item;
475
+ },
476
+ evidence.openspecSearches?.length
477
+ ? "已检索但未观察到 openspec 成功读取;若生成期存在候选,预写门禁会阻断 writer。"
478
+ : "unavailable:未观察到 openspec 成功读取。",
479
+ );
480
+
481
+ appendListSection(
482
+ `openspec 检索(${evidence.openspecSearches?.length ?? 0})`,
483
+ evidence.openspecSearches,
484
+ (search) => {
485
+ const item = el("li", null);
486
+ const icon = el("i", "ri-search-line");
487
+ icon.setAttribute("aria-hidden", "true");
488
+ item.append(
489
+ icon,
490
+ document.createTextNode(` ${search.tool}: ${search.query}`),
491
+ );
492
+ return item;
493
+ },
494
+ "unavailable:未观察到 openspec 检索。",
495
+ );
496
+
432
497
  if (
433
498
  evidence.status === "source-bound" ||
434
499
  evidence.status === "spec-injected" ||
@@ -355,18 +355,6 @@ export async function renderDagDetail(dagRunId, initial = true) {
355
355
  primaryEntries.push(["状态一致性", "不一致,需要处理"]);
356
356
  if (visibility.showResume) primaryEntries.push(["允许恢复", "是"]);
357
357
  if (visibility.showReconcile) primaryEntries.push(["允许收口", "是"]);
358
- const liveStatus = String(dag.effectiveStatus ?? dag.status ?? "").toLowerCase();
359
- if (liveStatus === "running" || liveStatus === "active") {
360
- const interruptLink = el(
361
- "a",
362
- "page-action-btn",
363
- "在操作台中止此运行 →",
364
- );
365
- interruptLink.href = `/#/recovery?dagRunId=${encodeURIComponent(dag.dagRunId)}&intent=interrupt`;
366
- interruptLink.setAttribute("data-testid", "inspect-dag-interrupt-link");
367
- interruptLink.title = "只跳转到操作台 Recovery,不会在观测页执行中止";
368
- primaryEntries.push(["协作中止", interruptLink]);
369
- }
370
358
  primaryEntries.push(
371
359
  ["开始时间", formatTs(dag.startedAt)],
372
360
  ["结束时间", formatTs(dag.finishedAt)],
@@ -1,6 +1,5 @@
1
1
  import { access } from "node:fs/promises";
2
2
  import path from "node:path";
3
- import { normalizeCliVersionLabel } from "../shared/package-metadata.js";
4
3
  import { controllerIdentityExpectationFailure, resolveControllerIdentity, } from "./loop-agent/loop-agent-client.js";
5
4
  export const DEFAULT_CHECK_REPO_TIMEOUT_MS = 300_000;
6
5
  export async function preflightTargetRepo(input) {
@@ -48,7 +47,7 @@ export async function preflightTargetRepo(input) {
48
47
  controllerIdentity: identity,
49
48
  };
50
49
  }
51
- const reportedVersion = normalizeCliVersionLabel(version.stdout);
50
+ const reportedVersion = version.stdout.trim();
52
51
  let controllerIdentity = identity
53
52
  ? { ...identity, reportedVersion }
54
53
  : undefined;