@tea-agent/loop-agent 0.40.0-next.8 → 0.40.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 (150) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +21 -20
  3. package/README.md +2 -2
  4. package/dist/application/dag/generate-task-dag.js +6 -2
  5. package/dist/build-stamp.json +3 -3
  6. package/dist/cli/command-definitions.js +9 -5
  7. package/dist/cli/program.js +38 -3
  8. package/dist/commands/client-recovery.js +8 -36
  9. package/dist/commands/init-model-catalog.js +22 -0
  10. package/dist/commands/task-advance.js +23 -1
  11. package/dist/commands/task-source-prepare.js +5 -0
  12. package/dist/commands/task-status.js +1 -0
  13. package/dist/executors/dag-pi-executor.js +3 -0
  14. package/dist/executors/pi-executor.js +4 -2
  15. package/dist/executors/pi-sdk-executor.js +79 -6
  16. package/dist/executors/shell-executor.js +94 -20
  17. package/dist/governance/checks.js +1 -0
  18. package/dist/shared/operator/capabilities.js +9 -1
  19. package/dist/shared/pi-context-pressure/checkpoint.js +116 -0
  20. package/dist/shared/pi-context-pressure/compaction-policy.js +151 -0
  21. package/dist/shared/pi-context-pressure/env.js +58 -0
  22. package/dist/shared/pi-context-pressure/extension.js +100 -0
  23. package/dist/shared/pi-context-pressure/index.js +7 -0
  24. package/dist/shared/pi-context-pressure/overflow.js +252 -0
  25. package/dist/shared/pi-context-pressure/sift-bridge.js +386 -0
  26. package/dist/shared/pi-context-pressure/telemetry.js +51 -0
  27. package/dist/worker/console/chat/assistant-content.js +7 -0
  28. package/dist/worker/console/chat/chat-event-store.js +82 -11
  29. package/dist/worker/console/chat/goal-round-driver.js +72 -0
  30. package/dist/worker/console/chat/goals.js +300 -0
  31. package/dist/worker/console/chat/pi-runtime.js +285 -8
  32. package/dist/worker/console/chat/provider-error.js +2 -1
  33. package/dist/worker/console/chat/repo-browser.js +94 -12
  34. package/dist/worker/console/chat/routes.js +495 -30
  35. package/dist/worker/console/chat/session-catalog.js +48 -0
  36. package/dist/worker/console/chat/session-stats.js +96 -0
  37. package/dist/worker/console/chat/session-store.js +27 -18
  38. package/dist/worker/console/chat/shortcuts.js +7 -0
  39. package/dist/worker/console/chat/sift-bridge.js +1 -0
  40. package/dist/worker/console/chat/turn-process.js +42 -5
  41. package/dist/worker/console/dag-execution-receipt.js +20 -2
  42. package/dist/worker/console/operator-actions.js +20 -5
  43. package/dist/worker/console/operator-selection.js +6 -2
  44. package/dist/worker/console/pi-plugins.catalog.json +73658 -0
  45. package/dist/worker/console/pi-plugins.js +179 -0
  46. package/dist/worker/console/routes.js +246 -6
  47. package/dist/worker/console/server.js +173 -42
  48. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-BYBmDfPW.js → abnfDiagram-N423BO3Z-Dy71QJp3.js} +1 -1
  49. package/dist/worker/console/static/assets/{arc-BKMmxpGF.js → arc-M25fNMTE.js} +1 -1
  50. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-DJpwxr1d.js → architectureDiagram-T3A2C74G-CUWlVl1a.js} +1 -1
  51. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-BaPj_ZlY.js → blockDiagram-VBNYF7ZC-Dd0Whpx2.js} +1 -1
  52. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-B8iGmHlm.js → c4Diagram-5PPSVZJV-BdfeCkI5.js} +1 -1
  53. package/dist/worker/console/static/assets/channel-Ba0AKiEZ.js +1 -0
  54. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-DaxXQayc.js → chunk-2GRJ4B5K-D_1sUkyD.js} +1 -1
  55. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-DqQKGA7q.js → chunk-2Q5K7J3B-Cxnxf36E.js} +1 -1
  56. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-CgMKpJBB.js → chunk-5RXB4S5H-D9RG3G_x.js} +1 -1
  57. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-BpToUlaW.js → chunk-5VM5RSS4-Zz9jgjLX.js} +1 -1
  58. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-BsEWOHxv.js → chunk-6Q2QTUOP-BCggDRCH.js} +1 -1
  59. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CKGSCe71.js → chunk-GF5L2VYU-CFg-5iAS.js} +1 -1
  60. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-Bm1U5299.js → chunk-JWPE2WC7-DRQTE8x_.js} +1 -1
  61. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-BI0ov870.js → chunk-KBJHAD2P-C_Dw7uZ8.js} +1 -1
  62. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-BmnQo0iJ.js → chunk-RYQCIY6F-BjIiApsk.js} +1 -1
  63. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-BJBqa6Xc.js → chunk-XXDRQBXY-B5XQD4KG.js} +1 -1
  64. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BU3IjVEe.js +1 -0
  65. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BU3IjVEe.js +1 -0
  66. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-BuN9-7AR.js → cose-bilkent-JH36ORCC-huHqH6WQ.js} +1 -1
  67. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-Bw22jc2k.js → cynefin-VYW2F7L2-BSi6yZqP.js} +1 -1
  68. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-BHnSH8Dr.js → cynefinDiagram-MW4NZA55-Bs_Jp31y.js} +1 -1
  69. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-BoXXIegj.js → dagre-VZM6K2ZE-CYxavikT.js} +1 -1
  70. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-CU8qyPhT.js → diagram-7IWD3JNH-B6TErl71.js} +1 -1
  71. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-lrjhF0S8.js → diagram-B4RE2ZJO-CvH4J2ii.js} +1 -1
  72. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-DL0QDEpo.js → diagram-LBJQPF4R-v1NBAqXS.js} +1 -1
  73. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-DxVMJMBH.js → diagram-Q27KOJAE-CUNHVAn2.js} +1 -1
  74. package/dist/worker/console/static/assets/{diagram-UB23O5K3-CFg9FzvG.js → diagram-UB23O5K3-Dzz1gUu-.js} +1 -1
  75. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-CQgP9fTG.js → ebnfDiagram-BXEA7PRR-CCF8_LjK.js} +1 -1
  76. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-A5vQXx9U.js → erDiagram-JOGREHBK-DObdPVLa.js} +1 -1
  77. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-ytXnLyXD.js → flowDiagram-UKHOOZJN-DlN8Pxrj.js} +1 -1
  78. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-CYuksfnS.js → ganttDiagram-PKOTCBZU-BJKIPTGX.js} +1 -1
  79. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-f_jW3zgU.js → gitGraphDiagram-DS77QQ5N-XcSSnObi.js} +1 -1
  80. package/dist/worker/console/static/assets/index-CIYzS16i.css +1 -0
  81. package/dist/worker/console/static/assets/index-CS3QOTfl.js +451 -0
  82. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-C3HLE-xV.js → infoDiagram-6WML65LV-BKrRKMww.js} +1 -1
  83. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-DwpgejS6.js → ishikawaDiagram-WSZJBQD7-CN5dk-yb.js} +1 -1
  84. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-ll0YWX1y.js → journeyDiagram-NVQOT4AX-B4P4KZ-Y.js} +1 -1
  85. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-Hq3rchvU.js → kanban-definition-27J2QSJJ-BRlteP32.js} +1 -1
  86. package/dist/worker/console/static/assets/{linear-BaGjJx8z.js → linear-CG8D9fuL.js} +1 -1
  87. package/dist/worker/console/static/assets/{mermaid.core-CKGQHWX_.js → mermaid.core-BuB6P2rX.js} +5 -5
  88. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-DM1FA6Dr.js → mindmap-definition-FAOFIHXS-DYXGl_DR.js} +1 -1
  89. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-Xa9fHkE2.js → pegDiagram-VL7TDLO6-BM48yEVG.js} +1 -1
  90. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-pCd4PDqJ.js → pieDiagram-7S7Q4E2Y-mT8NL-Ow.js} +1 -1
  91. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-KqjpEBLk.js → quadrantDiagram-CIZ2JOQS-C4pZ6DH_.js} +1 -1
  92. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-DgvR7LZD.js → railroadDiagram-AXF67PYL-BKAcr70m.js} +1 -1
  93. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-Bys1UCWA.js → requirementDiagram-LRYGKXZP-CRxcKpoO.js} +1 -1
  94. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-Ms9npnZt.js → sankeyDiagram-W5VNT64P-Ds4LxQDI.js} +1 -1
  95. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-DEIWrtQZ.js → sequenceDiagram-SI44F4Z6-CPLwPf_p.js} +1 -1
  96. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-DELJ0AhZ.js → sizeCapture-X5ZJPWSS-CL65uij0.js} +1 -1
  97. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-BpHt9wKh.js → stateDiagram-OKZ733FA-CO0Mgd1W.js} +1 -1
  98. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BUewAVYS.js +1 -0
  99. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-GKRsBluT.js → swimlanes-SLNWSIFB-Cxv3TSNw.js} +2 -2
  100. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Bhe67Wjl.js +8 -0
  101. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-Cwsq1DxL.js → timeline-definition-Z64GVDOM-Dw85f9L2.js} +1 -1
  102. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-CfaqtKx0.js → vennDiagram-T6HMQDX7-B4QLCqCy.js} +1 -1
  103. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-BlEUtK_g.js → wardleyDiagram-T6FBY63Y-B_yKCWwn.js} +1 -1
  104. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CHSRXPvO.js → xychartDiagram-ELKLHX3M-nniBEGv2.js} +1 -1
  105. package/dist/worker/console/static/index.html +2 -2
  106. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +95 -25
  107. package/dist/worker/console/static-src/operator-chat/compaction-message.js +35 -1
  108. package/dist/worker/console/static-src/operator-chat/open-preview-in-browser.js +3 -1
  109. package/dist/worker/console/static-src/operator-chat/spatial-overlay.js +1 -2
  110. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +26 -19
  111. package/dist/worker/console/static-src/operator-chat/useChatStream.js +0 -11
  112. package/dist/worker/console/static-src/operator-chat/useChatThread.js +14 -1
  113. package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +34 -4
  114. package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +28 -3
  115. package/dist/worker/console/static-src/operator-chat/workspace-reorder.js +18 -0
  116. package/dist/worker/console/static-src/shell/useWorkspaces.js +150 -34
  117. package/dist/worker/console/static-src/shell/workspace-route.js +3 -1
  118. package/dist/worker/console/workspace-context.js +28 -3
  119. package/dist/worker/console/workspace-registry.js +333 -60
  120. package/dist/worker/observe/static/inspect-bootstrap.d.ts +8 -0
  121. package/dist/worker/observe/static/inspect-bootstrap.js +57 -0
  122. package/dist/worker/observe/static/inspect-workspace.js +54 -10
  123. package/dist/worker/observe/static/operator-chrome.css +23 -4
  124. package/dist/worker/observe/static/operator-chrome.js +7 -2
  125. package/dist/worker/observe/static/styles.css +87 -25
  126. package/dist/worker/observe/static/views/dag-inspector.js +42 -1
  127. package/dist/worker/observe/static/views/dags.js +7 -5
  128. package/dist/worker/observe/static/views/session-timeline.js +135 -0
  129. package/dist/workflows/dag/backend-test-case-coverage-analysis.js +162 -8
  130. package/dist/workflows/dag/backend-test-pytest-collection.js +70 -2
  131. package/dist/workflows/dag/backend-test-result-contract.js +5 -1
  132. package/dist/workflows/dag/backend-test-scenario-param.js +339 -53
  133. package/dist/workflows/dag/backend-test-writer-completeness.js +18 -12
  134. package/dist/workflows/dag/init-hybrid.js +32 -9
  135. package/dist/workflows/dag/types.js +13 -0
  136. package/docs/architecture/runtime-boundaries.md +2 -1
  137. package/docs/templates/agent-dag.schema.json +18 -0
  138. package/docs/templates/backend-test-dag.json +27 -9
  139. package/docs/templates/init-managed-agents.md +1 -1
  140. package/package.json +3 -2
  141. package/skills/loop-agent/references/command-reference.md +15 -1
  142. package/dist/worker/console/static/assets/channel-CrzAa3FA.js +0 -1
  143. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-B7QwCCb4.js +0 -1
  144. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-B7QwCCb4.js +0 -1
  145. package/dist/worker/console/static/assets/index-BB6KxwT_.css +0 -1
  146. package/dist/worker/console/static/assets/index-D_wxbadT.js +0 -407
  147. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BEeSswlv.js +0 -1
  148. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-B2m_wca8.js +0 -8
  149. package/dist/worker/console/static-src/operator-chat/activity-rail-presentation.js +0 -73
  150. package/dist/worker/console/static-src/operator-chat/useActivityRailTransition.js +0 -59
@@ -49,16 +49,18 @@
49
49
  .och-inner {
50
50
  box-sizing: border-box;
51
51
  width: 100%;
52
+ min-width: 0;
52
53
  padding-inline: 14px;
53
54
  margin: 0 auto;
54
55
  }
55
56
 
56
- /* Band 1 — 全局带:品牌 / 仓库身份 / 模式 / 对象上下文 / 状态格 */
57
+ /* Band 1 — 全局带:品牌 / 仓库身份 / 模式 / 状态格(对象上下文在下方独立行) */
57
58
  .och-global {
58
59
  position: relative;
59
60
  display: flex;
60
61
  align-items: center;
61
62
  gap: 12px;
63
+ min-width: 0;
62
64
  /* DSH 2026-08-23:约 44px 与内容同色的压缩标题栏。 */
63
65
  height: 44px;
64
66
  }
@@ -318,11 +320,21 @@
318
320
  z-index: 1;
319
321
  }
320
322
 
321
- /* 对象上下文 chip:跨模式选中连续性 + 显式返回路径 */
323
+ /* 对象上下文 chip:跨模式选中连续性 + 显式返回路径。
324
+ * 不放进 44px 全局带:≥720px 模式开关 position:fixed 居中,长 run id
325
+ * 会从左侧把「返回 恢复」顶进滑块底下。独立一行后按钮始终完整可见。 */
326
+ .och-context-row {
327
+ display: flex;
328
+ align-items: center;
329
+ min-width: 0;
330
+ padding: 0 0 8px;
331
+ }
322
332
  .och-context {
323
333
  display: inline-flex;
324
334
  align-items: center;
325
335
  gap: 8px;
336
+ min-width: 0;
337
+ max-width: 100%;
326
338
  min-height: 30px;
327
339
  padding: 0 6px 0 12px;
328
340
  border: 1px solid
@@ -331,26 +343,29 @@
331
343
  background: color-mix(in srgb, var(--och-peach) 42%, var(--och-surface));
332
344
  font-size: 12px;
333
345
  color: var(--och-ink);
334
- white-space: nowrap;
335
346
  }
336
347
  .och-context[hidden] {
337
348
  display: none;
338
349
  }
339
350
  .och-context-kind {
351
+ flex-shrink: 0;
340
352
  color: var(--och-orange-active);
341
353
  font-weight: 700;
342
354
  }
343
355
  .och-context-id {
356
+ flex: 1 1 auto;
357
+ min-width: 0;
344
358
  font-family: var(--och-mono);
345
359
  font-size: 11px;
346
360
  color: var(--och-body);
347
- max-width: 260px;
348
361
  overflow: hidden;
349
362
  text-overflow: ellipsis;
363
+ white-space: nowrap;
350
364
  }
351
365
  .och-context-return {
352
366
  display: inline-flex;
353
367
  align-items: center;
368
+ flex-shrink: 0;
354
369
  gap: 4px;
355
370
  height: 22px;
356
371
  padding: 0 9px;
@@ -360,6 +375,7 @@
360
375
  font-size: 11px;
361
376
  font-weight: 600;
362
377
  text-decoration: none;
378
+ white-space: nowrap;
363
379
  }
364
380
  .och-context-return:hover {
365
381
  background: var(--och-orange-active);
@@ -369,6 +385,7 @@
369
385
  display: inline-flex;
370
386
  align-items: center;
371
387
  justify-content: center;
388
+ flex-shrink: 0;
372
389
  width: 20px;
373
390
  height: 20px;
374
391
  padding: 0;
@@ -401,6 +418,7 @@
401
418
  font-size: 12px;
402
419
  font-variant-numeric: tabular-nums;
403
420
  white-space: nowrap;
421
+ flex-shrink: 0;
404
422
  }
405
423
  .och-fresh-dot {
406
424
  width: 7px;
@@ -425,6 +443,7 @@
425
443
  color: var(--och-body);
426
444
  text-decoration: none;
427
445
  white-space: nowrap;
446
+ flex-shrink: 0;
428
447
  transition: border-color 0.15s ease;
429
448
  }
430
449
  .och-pi:hover {
@@ -17,6 +17,7 @@ export const OPERATE_TABS = Object.freeze([
17
17
  { id: "night", label: "夜间", href: "/#/operate/night" },
18
18
  { id: "models", label: "模型与认证", href: "/#/operate/models" },
19
19
  { id: "settings", label: "设置", href: "/#/operate/settings" },
20
+ { id: "pi-plugins", label: "Pi 插件", href: "/#/operate/pi-plugins" },
20
21
  ]);
21
22
 
22
23
  export const INSPECT_TABS = Object.freeze([
@@ -653,7 +654,9 @@ export function mountOperatorChrome(root, config = {}) {
653
654
  if (model.context) {
654
655
  const chip = el("div", "och-context");
655
656
  chip.appendChild(el("span", "och-context-kind", model.context.kindLabel));
656
- chip.appendChild(el("span", "och-context-id", model.context.id));
657
+ const idEl = el("span", "och-context-id", model.context.id);
658
+ idEl.title = model.context.id;
659
+ chip.appendChild(idEl);
657
660
  const back = el("a", "och-context-return");
658
661
  back.href = model.context.returnHref;
659
662
  back.appendChild(makeIcon("arrowLeft"));
@@ -675,7 +678,9 @@ export function mountOperatorChrome(root, config = {}) {
675
678
  render();
676
679
  });
677
680
  chip.appendChild(close);
678
- global.appendChild(chip);
681
+ const row = el("div", "och-context-row");
682
+ row.appendChild(chip);
683
+ inner.appendChild(row);
679
684
  }
680
685
 
681
686
  global.appendChild(el("span", "och-spacer"));
@@ -1,4 +1,7 @@
1
- :root {
1
+ html:has(body[data-shell="observe-console"]),
2
+ body[data-shell="observe-console"],
3
+ .inspect-workspace-root {
4
+ color-scheme: light;
2
5
  --canvas: #f7f7f4;
3
6
  --canvas-soft: #fafaf7;
4
7
  --surface: #ffffff;
@@ -38,14 +41,14 @@
38
41
  --transition-base: 220ms ease;
39
42
  --shadow-panel: 0 1px 2px rgb(38 37 30 / 4%), 0 8px 24px rgb(38 37 30 / 3%);
40
43
  --shadow-soft: 0 10px 30px rgb(38 37 30 / 6%);
41
- color-scheme: light;
42
44
  }
43
45
 
44
- * {
46
+ body[data-shell="observe-console"] *,
47
+ .inspect-workspace-root * {
45
48
  box-sizing: border-box;
46
49
  }
47
50
 
48
- html {
51
+ html:has(body[data-shell="observe-console"]) {
49
52
  background:
50
53
  radial-gradient(
51
54
  1200px 480px at 8% -10%,
@@ -62,7 +65,7 @@ html {
62
65
  scroll-behavior: smooth;
63
66
  }
64
67
 
65
- body {
68
+ body[data-shell="observe-console"] {
66
69
  min-width: 320px;
67
70
  margin: 0;
68
71
  min-height: 100vh;
@@ -76,29 +79,46 @@ body {
76
79
  line-height: 1.5;
77
80
  }
78
81
 
79
- button,
80
- input,
81
- select,
82
- textarea {
82
+ body[data-shell="observe-console"] button,
83
+ body[data-shell="observe-console"] input,
84
+ body[data-shell="observe-console"] select,
85
+ body[data-shell="observe-console"] textarea,
86
+ .inspect-workspace-root button,
87
+ .inspect-workspace-root input,
88
+ .inspect-workspace-root select,
89
+ .inspect-workspace-root textarea {
83
90
  font: inherit;
84
91
  }
85
92
 
86
- a {
93
+ body[data-shell="observe-console"] a,
94
+ .inspect-workspace-root a {
87
95
  color: inherit;
88
96
  }
89
97
 
90
- code,
91
- pre,
92
- .output-block,
93
- .dag-node-label,
94
- .dag-node-meta,
95
- .dag-node-status,
96
- .dag-node-table td:nth-child(2),
97
- td:first-child,
98
- .meta-item dd,
99
- .kpi-value,
100
- .risk-total,
101
- .process-timeline-time {
98
+ body[data-shell="observe-console"] code,
99
+ body[data-shell="observe-console"] pre,
100
+ body[data-shell="observe-console"] .output-block,
101
+ body[data-shell="observe-console"] .dag-node-label,
102
+ body[data-shell="observe-console"] .dag-node-meta,
103
+ body[data-shell="observe-console"] .dag-node-status,
104
+ body[data-shell="observe-console"] .dag-node-table td:nth-child(2),
105
+ body[data-shell="observe-console"] td:first-child,
106
+ body[data-shell="observe-console"] .meta-item dd,
107
+ body[data-shell="observe-console"] .kpi-value,
108
+ body[data-shell="observe-console"] .risk-total,
109
+ body[data-shell="observe-console"] .process-timeline-time,
110
+ .inspect-workspace-root code,
111
+ .inspect-workspace-root pre,
112
+ .inspect-workspace-root .output-block,
113
+ .inspect-workspace-root .dag-node-label,
114
+ .inspect-workspace-root .dag-node-meta,
115
+ .inspect-workspace-root .dag-node-status,
116
+ .inspect-workspace-root .dag-node-table td:nth-child(2),
117
+ .inspect-workspace-root td:first-child,
118
+ .inspect-workspace-root .meta-item dd,
119
+ .inspect-workspace-root .kpi-value,
120
+ .inspect-workspace-root .risk-total,
121
+ .inspect-workspace-root .process-timeline-time {
102
122
  font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace;
103
123
  }
104
124
 
@@ -125,6 +145,30 @@ td:first-child,
125
145
  padding: var(--space-5) 0 56px;
126
146
  }
127
147
 
148
+ .inspect-workspace-root {
149
+ position: relative;
150
+ min-height: 100%;
151
+ color: var(--ink);
152
+ font-family:
153
+ system-ui, -apple-system, "Segoe UI", "PingFang SC", "Hiragino Sans GB",
154
+ "Noto Sans SC", BlinkMacSystemFont, sans-serif;
155
+ font-size: 14px;
156
+ line-height: 1.5;
157
+ }
158
+
159
+ .inspect-workspace-root .dag-inspector {
160
+ top: 8px;
161
+ right: 8px;
162
+ bottom: 8px;
163
+ width: min(var(--dag-inspector-width, 50%), calc(100% - 16px));
164
+ }
165
+
166
+ .inspect-workspace-root #app {
167
+ width: 100%;
168
+ margin: 0;
169
+ padding: var(--space-4) var(--space-5) var(--space-6);
170
+ }
171
+
128
172
  .breadcrumb {
129
173
  display: flex;
130
174
  min-height: 16px;
@@ -257,7 +301,8 @@ td:first-child,
257
301
  color: var(--body);
258
302
  }
259
303
 
260
- .panel {
304
+ body[data-shell="observe-console"] .panel,
305
+ .inspect-workspace-root .panel {
261
306
  min-width: 0;
262
307
  background: color-mix(in srgb, var(--surface) 96%, transparent);
263
308
  border: 1px solid var(--hairline);
@@ -288,7 +333,8 @@ td:first-child,
288
333
  }
289
334
 
290
335
  .section-heading,
291
- .panel h3 {
336
+ body[data-shell="observe-console"] .panel h3,
337
+ .inspect-workspace-root .panel h3 {
292
338
  margin: 0 0 var(--space-4);
293
339
  color: var(--ink);
294
340
  font-size: 14px;
@@ -1810,6 +1856,15 @@ body.is-resizing-dag-graph {
1810
1856
  color: var(--muted);
1811
1857
  font-size: 11px;
1812
1858
  }
1859
+ .process-timeline-export-bar {
1860
+ display: flex;
1861
+ justify-content: flex-end;
1862
+ padding: 2px 0 var(--space-2);
1863
+ }
1864
+ .process-timeline-export-bar .btn {
1865
+ font-size: 11px;
1866
+ padding: 3px 10px;
1867
+ }
1813
1868
  .process-timeline {
1814
1869
  display: grid;
1815
1870
  gap: 0;
@@ -2526,7 +2581,8 @@ body.is-resizing-dag-graph {
2526
2581
  flex-wrap: wrap;
2527
2582
  gap: 6px;
2528
2583
  }
2529
- .muted {
2584
+ body[data-shell="observe-console"] .muted,
2585
+ .inspect-workspace-root .muted {
2530
2586
  color: var(--muted);
2531
2587
  }
2532
2588
 
@@ -2746,6 +2802,12 @@ body.is-resizing-dag-graph {
2746
2802
  bottom: 12px;
2747
2803
  width: calc(100vw - 24px);
2748
2804
  }
2805
+ .inspect-workspace-root .dag-inspector {
2806
+ width: calc(100% - 16px);
2807
+ top: 8px;
2808
+ right: 8px;
2809
+ bottom: 8px;
2810
+ }
2749
2811
  .dag-inspector-resize-handle {
2750
2812
  display: none;
2751
2813
  }
@@ -24,6 +24,7 @@ import { renderMarkdown, renderInspectorDocument } from "../markdown-render.js";
24
24
  import { describeDagNodePurpose } from "../dag-node-purpose.js";
25
25
  import { buildPromptRestartCandidateOptions } from "../prompt-restart-candidates.js";
26
26
  import { copyText } from "../copy.js";
27
+ import { navigate } from "../router.js";
27
28
  import {
28
29
  fetchWithWorkspaceScope,
29
30
  uiState,
@@ -47,6 +48,7 @@ import {
47
48
  import {
48
49
  formatSessionEventTime,
49
50
  renderSessionTimeline,
51
+ buildPageStyledDoc,
50
52
  } from "./session-timeline.js";
51
53
  import { createCustomSelect } from "../custom-select.js";
52
54
 
@@ -1671,7 +1673,7 @@ async function renderPromptRestart(content, dagRunId, node) {
1671
1673
  open.className = "prompt-restart-submit is-secondary";
1672
1674
  open.textContent = "打开新 run";
1673
1675
  open.addEventListener("click", () => {
1674
- window.location.hash = `#/dag/${encodeURIComponent(String(newRunId))}`;
1676
+ navigate(`/dag/${encodeURIComponent(String(newRunId))}`);
1675
1677
  });
1676
1678
  row.appendChild(open);
1677
1679
  } else {
@@ -2343,6 +2345,45 @@ function renderNonPiProcess(content, body) {
2343
2345
  ul.appendChild(li);
2344
2346
  }
2345
2347
  content.appendChild(ul);
2348
+ appendProcessExportButton(content, body, body);
2349
+ }
2350
+
2351
+ function downloadTextFile(filename, text) {
2352
+ const blob = new Blob([text], { type: "text/html;charset=utf-8" });
2353
+ const url = URL.createObjectURL(blob);
2354
+ const a = document.createElement("a");
2355
+ a.href = url;
2356
+ a.download = filename;
2357
+ document.body.appendChild(a);
2358
+ a.click();
2359
+ a.remove();
2360
+ URL.revokeObjectURL(url);
2361
+ }
2362
+
2363
+ function appendProcessExportButton(content, body, meta) {
2364
+ if (!body || ((body.commands ?? []).length === 0 && (body.steps ?? []).length === 0)) return;
2365
+ const toolbar = el("div", "process-timeline-export-bar");
2366
+ const btn = el("button", "btn btn-secondary", "导出 HTML");
2367
+ btn.type = "button";
2368
+ btn.addEventListener("click", async () => {
2369
+ let cssText = "";
2370
+ try {
2371
+ const res = await fetch("/inspect/styles.css", { cache: "no-cache" });
2372
+ if (res.ok) cssText = await res.text();
2373
+ } catch {
2374
+ /* 忽略:导出仍会用内联兜底样式 */
2375
+ }
2376
+ // 复用页面真实渲染 DOM,保证导出排版与页面一致。
2377
+ const clone = content.cloneNode(true);
2378
+ const bar = clone.querySelector(".process-timeline-export-bar");
2379
+ if (bar) bar.remove();
2380
+ downloadTextFile(
2381
+ `node-process-${meta?.nodeId || "node"}-${Date.now()}.html`,
2382
+ buildPageStyledDoc(clone.innerHTML, meta ?? {}, cssText),
2383
+ );
2384
+ });
2385
+ toolbar.appendChild(btn);
2386
+ content.insertBefore(toolbar, content.firstChild);
2346
2387
  }
2347
2388
 
2348
2389
  export async function mergeNodeProcess(dagRunId, node) {
@@ -15,6 +15,7 @@ import {
15
15
  DAG_HISTORY_DEFAULT_PAGE_SIZE,
16
16
  navigate,
17
17
  parseRoute,
18
+ inspectPathToCanonicalHref,
18
19
  } from "../router.js";
19
20
  import { fetchDagRunHistory, summarizeFetchError } from "../api.js";
20
21
  import { copyText } from "../copy.js";
@@ -57,7 +58,8 @@ function currentHistoryQuery() {
57
58
  }
58
59
 
59
60
  function syncHash(page, pageSize, { replace = false } = {}) {
60
- const next = buildDagHistoryHash(page, pageSize);
61
+ const path = buildDagHistoryHash(page, pageSize);
62
+ const next = inspectPathToCanonicalHref(path).replace(/^\/#/, "") || "/inspect";
61
63
  const current = (location.hash || "").replace(/^#/, "") || "/";
62
64
  const currentPath = current.split("?")[0];
63
65
  const nextPath = next.split("?")[0];
@@ -72,15 +74,15 @@ function syncHash(page, pageSize, { replace = false } = {}) {
72
74
  location.hash = next;
73
75
  }
74
76
  }
75
- return next;
77
+ return path;
76
78
  }
77
79
  if (replace && typeof history !== "undefined" && history.replaceState) {
78
80
  const base = `${location.pathname}${location.search}`;
79
81
  history.replaceState(null, "", `${base}#${next}`);
80
- return next;
82
+ return path;
81
83
  }
82
- navigate(next);
83
- return next;
84
+ navigate(path);
85
+ return path;
84
86
  }
85
87
 
86
88
  function setControlsDisabled(disabled) {
@@ -30,6 +30,112 @@ export function sessionEventArgs(event) {
30
30
  return args && typeof args === "object" && !Array.isArray(args) ? args : {};
31
31
  }
32
32
 
33
+ function escapeHtml(text) {
34
+ return String(text ?? "")
35
+ .replaceAll("&", "&")
36
+ .replaceAll("<", "&lt;")
37
+ .replaceAll(">", "&gt;")
38
+ .replaceAll('"', "&quot;")
39
+ .replaceAll("'", "&#39;");
40
+ }
41
+
42
+ function downloadTextFile(filename, text) {
43
+ const blob = new Blob([text], { type: "text/html;charset=utf-8" });
44
+ const url = URL.createObjectURL(blob);
45
+ const a = document.createElement("a");
46
+ a.href = url;
47
+ a.download = filename;
48
+ document.body.appendChild(a);
49
+ a.click();
50
+ a.remove();
51
+ URL.revokeObjectURL(url);
52
+ }
53
+
54
+ /** 兜底样式:仅在无法抓取页面 /inspect/styles.css 时使用(与页面 process-timeline 排版对齐)。 */
55
+ const EXPORT_FALLBACK_CSS = `
56
+ :root{--canvas:#f7f7f4;--surface:#ffffff;--canvas-soft:#fafaf7;--surface-muted:#f0efe9;--ink:#26251e;--body:#5a5852;--muted:#807d72;--muted-soft:#a09c92;--hairline:#e6e5e0;--hairline-strong:#cfcdc4;--orange:#f54e00;--orange-active:#d04200;--blue:#3f6ea4;--green:#1f8a65;--amber:#a66a00;--red:#c72d53;--lavender:#e4daf1;--radius-sm:6px;--space-2:8px;--space-3:12px;--space-5:20px}
57
+ body{margin:0;font-family:-apple-system,"Segoe UI",Roboto,"PingFang SC","Microsoft YaHei",sans-serif;background:var(--canvas);color:var(--body);font-size:13px}
58
+ .process-timeline{display:grid;gap:0;margin:0;padding:var(--space-2) 0;list-style:none}
59
+ .process-timeline-item{display:grid;grid-template-columns:20px minmax(0,1fr);align-items:stretch;min-height:34px}
60
+ .process-timeline-item.has-time{grid-template-columns:20px 92px minmax(0,1fr)}
61
+ .process-timeline-rail{position:relative;display:grid;place-items:center;min-height:34px}
62
+ .process-timeline-rail::before{position:absolute;top:0;bottom:0;width:1px;background:var(--hairline-strong);content:""}
63
+ .process-timeline-item.is-first .process-timeline-rail::before{top:50%}
64
+ .process-timeline-item.is-last .process-timeline-rail::before{bottom:50%}
65
+ .process-timeline-dot{position:relative;z-index:1;width:8px;height:8px;border:2px solid var(--surface);border-radius:50%;background:var(--muted-soft)}
66
+ .process-timeline-lifecycle .process-timeline-dot{background:var(--lavender)}
67
+ .process-timeline-tool-start .process-timeline-dot{background:var(--orange)}
68
+ .process-timeline-tool-end .process-timeline-dot{background:var(--green)}
69
+ .process-timeline-assistant .process-timeline-dot{background:var(--blue)}
70
+ .process-timeline-protocol .process-timeline-dot{background:var(--muted-soft)}
71
+ .process-timeline-item.is-error .process-timeline-dot{background:var(--red)}
72
+ .process-timeline-time{padding:9px 0;color:var(--muted);font-size:10px;font-variant-numeric:tabular-nums}
73
+ .process-timeline-label{min-width:0;padding:8px 0 8px var(--space-2);color:var(--body);font-size:12px;line-height:1.5;overflow-wrap:anywhere}
74
+ .process-timeline-protocol .process-timeline-label{color:var(--muted);font-size:11px}
75
+ .process-timeline-body{min-width:0;padding:4px 0 8px var(--space-2)}
76
+ .process-timeline-card{display:grid;gap:6px;min-width:0}
77
+ .process-timeline-details{min-width:0;border:1px solid var(--hairline);border-radius:var(--radius-sm);background:var(--surface)}
78
+ .process-timeline-summary{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:8px;padding:8px 10px;cursor:pointer;list-style:none;color:var(--ink);font-size:12px;font-weight:650}
79
+ .process-timeline-summary::-webkit-details-marker{display:none}
80
+ .process-timeline-summary-title{min-width:0;overflow-wrap:anywhere}
81
+ .process-timeline-summary-status{flex-shrink:0;color:var(--muted);font-size:11px;font-weight:700}
82
+ .process-timeline-summary-status.status-succeeded{color:var(--green)}
83
+ .process-timeline-summary-status.status-failed{color:var(--red)}
84
+ .process-timeline-summary-status.status-pending{color:var(--orange)}
85
+ .process-timeline-tool-card.is-error .process-timeline-details{border-color:var(--red);background:var(--canvas-soft)}
86
+ .process-timeline-card-body{display:grid;gap:6px;padding:0 10px 10px}
87
+ .process-timeline-section-label{color:var(--muted);font-size:11px;font-weight:700}
88
+ .process-timeline-code,.process-timeline-thinking-body{margin:0;padding:8px 10px;overflow:auto;border-radius:var(--radius-sm);background:var(--canvas-soft);color:var(--body);font-family:"JetBrains Mono","SFMono-Regular",Consolas,monospace;font-size:11px;line-height:1.45;white-space:pre-wrap;overflow-wrap:anywhere}
89
+ .process-timeline-more{margin-top:2px}
90
+ .process-timeline-more>summary{cursor:pointer;color:var(--blue);font-size:11px;font-weight:650}
91
+ .process-timeline-thinking>summary{background:var(--lavender)}
92
+ .process-timeline-turn-text{display:grid;gap:6px;padding:8px 10px;border:1px solid var(--hairline);border-radius:var(--radius-sm);background:var(--surface)}
93
+ .process-timeline-turn-text .markdown-output{font-size:12px}
94
+ .process-timeline-tool-only-note{margin:0;font-size:12px}
95
+ .process-timeline-turn-tools{display:grid;gap:4px;margin:0;padding:0 0 0 1.1em;color:var(--body);font-size:12px;font-family:"JetBrains Mono","SFMono-Regular",Consolas,monospace;overflow-wrap:anywhere}
96
+ .process-timeline-turn-tools li{padding:2px 0}
97
+ .process-timeline-agent-end-note{margin:0;font-size:11px}
98
+ .markdown-output{display:grid;gap:var(--space-3);color:var(--body);font-size:13px;line-height:1.65}
99
+ .markdown-output>:first-child{margin-top:0}
100
+ .markdown-output>:last-child{margin-bottom:0}
101
+ .markdown-output h1,.markdown-output h2,.markdown-output h3,.markdown-output h4{margin:var(--space-5) 0 var(--space-2);color:var(--ink);line-height:1.3}
102
+ .markdown-output h1{font-size:21px}.markdown-output h2{font-size:18px}.markdown-output h3{font-size:16px}.markdown-output h4{font-size:14px}
103
+ .markdown-output p,.markdown-output blockquote,.markdown-output ul,.markdown-output ol{margin:0;overflow-wrap:anywhere}
104
+ .markdown-output ul,.markdown-output ol{padding-left:22px}
105
+ .markdown-output blockquote{padding-left:12px}
106
+ `;
107
+
108
+ const EXPORT_SHELL_CSS = `
109
+ body{margin:0;font-family:-apple-system,"Segoe UI",Roboto,"PingFang SC","Microsoft YaHei",sans-serif;background:var(--canvas)}
110
+ .export-shell{max-width:1000px;margin:0 auto;padding:24px 28px;background:var(--surface);min-height:100vh;box-shadow:0 0 0 1px var(--hairline)}
111
+ .export-head h1{font-size:15px;color:var(--ink);margin:0;font-weight:700}
112
+ .export-sub{color:var(--muted);font-size:12px;margin:4px 0 16px}
113
+ .export-controls{display:flex;justify-content:flex-start;margin:0 0 12px}
114
+ .export-toggle{min-height:28px;padding:0 12px;border:1px solid var(--hairline-strong);border-radius:var(--radius-sm);background:var(--surface);color:var(--body);font-size:11px;font-weight:600;cursor:pointer;user-select:none}
115
+ .export-toggle:hover,.export-toggle:focus-visible{border-color:var(--orange);color:var(--orange-active);outline:none}
116
+ body.hide-protocol .process-timeline-item.process-timeline-protocol{display:none}
117
+ `;
118
+
119
+ /**
120
+ * 把页面渲染出的时间线 innerHTML 封装成自包含 HTML。
121
+ * @param {string} bodyHTML 页面渲染的 .process-timeline 等真实 DOM 序列化结果
122
+ * @param {{nodeId?:string,dagRunId?:string}} meta
123
+ * @param {string} [cssText] 页面 /inspect/styles.css 原文;缺失时用兜底样式
124
+ */
125
+ export function buildPageStyledDoc(bodyHTML, meta = {}, cssText = "") {
126
+ const now = new Date().toLocaleString();
127
+ const title = meta.nodeId ? `节点执行过程 · ${meta.nodeId}` : "节点执行过程";
128
+ const style = cssText.trim()
129
+ ? `${cssText}\n\n/* 导出外壳 */\n${EXPORT_SHELL_CSS}`
130
+ : `${EXPORT_FALLBACK_CSS}\n${EXPORT_SHELL_CSS}`;
131
+ // 有协议行时,在左上角提供一个「展开/隐藏原始协议事件」按钮(与页面一致),默认隐藏。
132
+ const hasProtocol = bodyHTML.includes("process-timeline-protocol");
133
+ const controls = hasProtocol
134
+ ? `<div class="export-controls"><button type="button" id="exportToggleProtocol" class="export-toggle">展开原始协议事件</button></div><script>(function(){var b=document.body,t=document.getElementById('exportToggleProtocol'),on=false;function apply(){b.classList.toggle('hide-protocol',!on);t.textContent=on?'隐藏原始协议事件':'展开原始协议事件';}if(t){t.addEventListener('click',function(){on=!on;apply();});apply();}})();</script>`
135
+ : "";
136
+ return `<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>${escapeHtml(title)}</title><style>${style}</style></head><body><div class="export-shell"><div class="export-head"><h1>${escapeHtml(title)}</h1></div><p class="export-sub">DAG ${escapeHtml(meta.dagRunId ?? "")} · 导出于 ${escapeHtml(now)}</p>${controls}<div class="export-timeline">${bodyHTML}</div></div></body></html>`;
137
+ }
138
+
33
139
  export function sessionEventResult(event) {
34
140
  return normalizeSessionEventFields(event).result;
35
141
  }
@@ -742,6 +848,35 @@ export function renderSessionTimeline(content, nodeId, options = {}) {
742
848
  parent.scrollLeft = scrollLeft;
743
849
  });
744
850
  toolbar.appendChild(protocolToggle);
851
+ const exportBtn = el("button", "btn btn-secondary", "导出 HTML");
852
+ exportBtn.type = "button";
853
+ exportBtn.addEventListener("click", async () => {
854
+ let cssText = "";
855
+ try {
856
+ const res = await fetch("/inspect/styles.css", { cache: "no-cache" });
857
+ if (res.ok) cssText = await res.text();
858
+ } catch {
859
+ /* 忽略:导出仍会用内联兜底样式 */
860
+ }
861
+ // 复用页面真实渲染(含原始协议事件),保证导出的排版与页面一致。
862
+ const prevShow = uiState.sessionTimelineShowProtocol;
863
+ uiState.sessionTimelineShowProtocol = true;
864
+ const tmp = document.createElement("div");
865
+ tmp.className = "process-timeline-scroll";
866
+ renderSessionTimeline(tmp, nodeId, options);
867
+ const toolbar = tmp.querySelector(".process-timeline-toolbar");
868
+ if (toolbar) toolbar.remove();
869
+ const innerHTML = tmp.innerHTML;
870
+ uiState.sessionTimelineShowProtocol = prevShow;
871
+ downloadTextFile(
872
+ `node-process-${nodeId || "node"}-${Date.now()}.html`,
873
+ buildPageStyledDoc(innerHTML, {
874
+ nodeId,
875
+ dagRunId: uiState.currentDagRunId ?? "",
876
+ }, cssText),
877
+ );
878
+ });
879
+ toolbar.appendChild(exportBtn);
745
880
  content.appendChild(toolbar);
746
881
 
747
882
  const timelineItems = buildRichTimelineItems(visible.events, {