@tea-agent/loop-agent 0.43.0-next.1 → 0.43.0-next.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +144 -9
- package/dist/application/dag/run-dag.js +5 -0
- package/dist/application/evaluation/budget.js +19 -1
- package/dist/application/task-lifecycle/advance.js +17 -4
- package/dist/application/task-lifecycle/observe.js +43 -29
- package/dist/application/task-lifecycle/plan-transitions.js +5 -4
- package/dist/build-stamp.json +3 -3
- package/dist/executors/dag-pi-executor.js +1237 -586
- package/dist/executors/pi-executor.js +22 -1
- package/dist/executors/pi-sdk-executor.js +140 -39
- package/dist/executors/shell-executor.js +8 -4
- package/dist/shared/frontend-execution-policy.js +23 -0
- package/dist/task/config-types.js +4 -0
- package/dist/task/source-prepare/ledger-reconciliation.js +2 -2
- package/dist/task/source-prepare/ledger-review.js +6 -9
- package/dist/task/source-prepare/semantic-intake.js +16 -26
- package/dist/task/source-prepare/source-fidelity-pi.js +28 -9
- package/dist/task/source-references.js +48 -23
- package/dist/worker/console/chat/assistant-content.js +23 -2
- package/dist/worker/console/chat/browser-policy.js +113 -0
- package/dist/worker/console/chat/browser-routes.js +140 -0
- package/dist/worker/console/chat/explore-tools.js +13 -0
- package/dist/worker/console/chat/pi-runtime/custom-tools/ask-user-question.js +44 -0
- package/dist/worker/console/chat/pi-runtime/custom-tools/explore-tools.js +202 -0
- package/dist/worker/console/chat/pi-runtime/custom-tools/goal-tools.js +79 -0
- package/dist/worker/console/chat/pi-runtime/custom-tools/operator-tools.js +86 -0
- package/dist/worker/console/chat/pi-runtime/custom-tools/todo-write.js +38 -0
- package/dist/worker/console/chat/pi-runtime/inspection-types.js +28 -0
- package/dist/worker/console/chat/pi-runtime/inspection.js +439 -0
- package/dist/worker/console/chat/pi-runtime/sdk-bindings.js +153 -0
- package/dist/worker/console/chat/pi-runtime/sdk-session.js +5 -0
- package/dist/worker/console/chat/pi-runtime.js +207 -1178
- package/dist/worker/console/chat/routes.js +208 -71
- package/dist/worker/console/chat/scm-routes.js +217 -0
- package/dist/worker/console/chat/scm-service.js +283 -0
- package/dist/worker/console/chat/scm-tools.js +111 -0
- package/dist/worker/console/chat/sdd-data-alignment.js +65 -9
- package/dist/worker/console/chat/session-catalog.js +32 -0
- package/dist/worker/console/chat/session-mode-view.js +49 -0
- package/dist/worker/console/chat/session-mode.js +219 -0
- package/dist/worker/console/chat/session-store.js +22 -6
- package/dist/worker/console/chat/shortcuts.js +6 -0
- package/dist/worker/console/chat/terminal-routes.js +216 -0
- package/dist/worker/console/chat/terminal-sessions.js +348 -0
- package/dist/worker/console/chat/terminal-tools.js +211 -0
- package/dist/worker/console/chat/tool-preview.js +11 -0
- package/dist/worker/console/chat/tools.js +2 -0
- package/dist/worker/console/server.js +2 -28
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-B0Q0nClf.js → abnfDiagram-N423BO3Z-CTSK0xbU.js} +1 -1
- package/dist/worker/console/static/assets/{arc-DCPjC19G.js → arc-_2MfctFP.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-CP5n9jmG.js → architectureDiagram-T3A2C74G-DsDe2wTg.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-CmaWi_Wg.js → blockDiagram-VBNYF7ZC-Dtz_779U.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-Ct5tcMfZ.js → c4Diagram-5PPSVZJV-LaULi-gw.js} +1 -1
- package/dist/worker/console/static/assets/channel-CqS4yBWT.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-C0osm1Zf.js → chunk-2GRJ4B5K-DuHBqqX8.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-CDWviED7.js → chunk-2Q5K7J3B-BZgZs1dn.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-mtSk1-j7.js → chunk-5RXB4S5H-tzwmlIO_.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-D5J9PHC7.js → chunk-5VM5RSS4-CeYrLkSn.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-Begg4WAa.js → chunk-6Q2QTUOP-C92cgilm.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-sF1AEy7T.js → chunk-GF5L2VYU-XOXpGC0F.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-BqbsWXZb.js → chunk-JWPE2WC7-CGHbJyCH.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-BKJrPcJ6.js → chunk-KBJHAD2P-Bs2-LTzf.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-CcwNMRho.js → chunk-RYQCIY6F-rN7up7E4.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-BEJi3iIs.js → chunk-XXDRQBXY-B_fWjKpR.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DVOxNOq0.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DVOxNOq0.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-DRLgTvVu.js → cose-bilkent-JH36ORCC-DE3SdJvB.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BoAVG3cs.js → cynefin-VYW2F7L2-B-4yuRAX.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-Bzol06hR.js → cynefinDiagram-MW4NZA55-TiCPFuO8.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-UmliJM77.js → dagre-VZM6K2ZE-7crzLMzA.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-BWLcJkFo.js → diagram-7IWD3JNH-Cvr-x6Fd.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-CgApOm-O.js → diagram-B4RE2ZJO-BzcRHUjH.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-Bh3RgTLs.js → diagram-LBJQPF4R-BjkAXilh.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-Dsh-D5nE.js → diagram-Q27KOJAE-BkzyATK7.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-Dkbbpcpb.js → diagram-UB23O5K3-rZuTarYP.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-3EBaA3t3.js → ebnfDiagram-BXEA7PRR-CAYENaN1.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-Bq2Dc5ok.js → erDiagram-JOGREHBK-DCEam0Uq.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-De7y55ha.js → flowDiagram-UKHOOZJN-e_dH4wVO.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-fLiDbQRh.js → ganttDiagram-PKOTCBZU-CvZa2zOK.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Du617mp1.js → gitGraphDiagram-DS77QQ5N-CbT5TuKp.js} +1 -1
- package/dist/worker/console/static/assets/index-CUPeoE8W.js +468 -0
- package/dist/worker/console/static/assets/index-SAANIK8g.css +1 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-BwmlF-XP.js → infoDiagram-6WML65LV-BWzHQa_Z.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-zsGtS59u.js → ishikawaDiagram-WSZJBQD7-BTjlovtU.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-DTTPTe3f.js → journeyDiagram-NVQOT4AX-DkvQoCc1.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-BkXg9aP-.js → kanban-definition-27J2QSJJ-CHSzGi3W.js} +1 -1
- package/dist/worker/console/static/assets/{linear-7U2ue5IE.js → linear-DZMkTCWH.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-BUuGHmWO.js → mermaid.core-BIMhty7A.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-7q6fX0Sv.js → mindmap-definition-FAOFIHXS-Dx-tyMl3.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-CkQPJN55.js → pegDiagram-VL7TDLO6-C6G34rbY.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-CCal_tfX.js → pieDiagram-7S7Q4E2Y-CHK9XOfm.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-B7bpxyAZ.js → quadrantDiagram-CIZ2JOQS-CDARkI0w.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-CJmww7D-.js → railroadDiagram-AXF67PYL-DiuUe0ZQ.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-D6ldWJEv.js → requirementDiagram-LRYGKXZP-RE61UR56.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-VGh33I9e.js → sankeyDiagram-W5VNT64P-KDNLHMEG.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-CCkDrIjU.js → sequenceDiagram-SI44F4Z6-CeVgXnD9.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-NC8f5Otb.js → sizeCapture-X5ZJPWSS-CXwGyjjG.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-_92ezZdF.js → stateDiagram-OKZ733FA-BjTfozab.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BXoYsyr2.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-BdUCGtbP.js → swimlanes-SLNWSIFB-BaXOB8GT.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-DfKliS4e.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CdD1H8ia.js → timeline-definition-Z64GVDOM-C3r2hqUU.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-Dkdk3oFo.js → vennDiagram-T6HMQDX7-MWKgl1S9.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-CKu2uPPY.js → wardleyDiagram-T6FBY63Y-C9iFcZPk.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-ClqAnuG8.js → xychartDiagram-ELKLHX3M-DqnM24Jp.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/operator-chat/active-turn-tail.js +10 -0
- package/dist/worker/console/static-src/operator-chat/chat-link.js +94 -0
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +122 -10
- package/dist/worker/console/static-src/operator-chat/compaction-message.js +7 -0
- package/dist/worker/console/static-src/operator-chat/pending-user-message.js +27 -15
- package/dist/worker/console/static-src/operator-chat/process-label.js +41 -0
- package/dist/worker/console/static-src/operator-chat/turn-group-equality.js +13 -0
- package/dist/worker/console/static-src/operator-chat/turn-process-disclosure.js +10 -0
- package/dist/worker/console/static-src/operator-chat/use-searchable-hidden.js +51 -0
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +44 -24
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +18 -2
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +41 -8
- package/dist/worker/console/static-src/operator-chat/useComposer.js +26 -11
- package/dist/worker/console/static-src/operator-chat/user-turn-anchor-equality.js +21 -0
- package/dist/worker/console/workspace-context.js +15 -1
- package/dist/worker/observe/node-transparency.js +81 -72
- package/dist/worker/observe/routes.js +20 -1
- package/dist/worker/observe/static/dag-inspector-humanize.js +3 -0
- package/dist/worker/observe/static/dom.js +20 -1
- package/dist/worker/observe/static/format-pool.d.ts +2 -0
- package/dist/worker/observe/static/format-pool.js +6 -0
- package/dist/worker/observe/static/format.js +7 -0
- package/dist/worker/observe/static/inspect-workspace.js +34 -7
- package/dist/worker/observe/static/inspector-submission.js +32 -0
- package/dist/worker/observe/static/kpi.js +1 -0
- package/dist/worker/observe/static/relations.js +2 -0
- package/dist/worker/observe/static/router.js +13 -0
- package/dist/worker/observe/static/run-processing.js +2 -0
- package/dist/worker/observe/static/shell-chrome.js +36 -3
- package/dist/worker/observe/static/state.js +35 -2
- package/dist/worker/observe/static/styles.css +263 -39
- package/dist/worker/observe/static/task-failure-labels.d.ts +4 -0
- package/dist/worker/observe/static/task-failure-labels.js +67 -0
- package/dist/worker/observe/static/task-history.js +12 -0
- package/dist/worker/observe/static/views/batch.js +6 -13
- package/dist/worker/observe/static/views/dag-graph.js +50 -3
- package/dist/worker/observe/static/views/dag-inspector.js +742 -268
- package/dist/worker/observe/static/views/dag-trajectory.js +3 -0
- package/dist/worker/observe/static/views/dag.d.ts +6 -0
- package/dist/worker/observe/static/views/dag.js +49 -11
- package/dist/worker/observe/static/views/dags.js +2 -0
- package/dist/worker/observe/static/views/dashboard.js +21 -12
- package/dist/worker/observe/static/views/failures.js +21 -11
- package/dist/worker/observe/static/views/feature.js +11 -29
- package/dist/worker/observe/static/views/pool.js +37 -28
- package/dist/worker/observe/static/views/run.js +48 -5
- package/dist/worker/observe/static/views/session-timeline.js +189 -240
- package/dist/worker/observe/static/views/task.js +81 -62
- package/dist/workflows/dag/budget-enforcement.js +53 -3
- package/dist/workflows/dag/frontend-capacity.js +9 -0
- package/dist/workflows/dag/frontend-closeout.js +1 -1
- package/dist/workflows/dag/frontend-design-policy.js +2 -1
- package/dist/workflows/dag/frontend-durable-tools.js +193 -0
- package/dist/workflows/dag/frontend-execution-groups.js +24 -0
- package/dist/workflows/dag/frontend-implementation-contract.js +13 -0
- package/dist/workflows/dag/frontend-input-projection.js +76 -0
- package/dist/workflows/dag/frontend-plan-completeness.js +186 -0
- package/dist/workflows/dag/frontend-plan-recovery-policy.js +18 -0
- package/dist/workflows/dag/frontend-plan-render.js +10 -3
- package/dist/workflows/dag/frontend-prewrite-gate.js +3 -3
- package/dist/workflows/dag/frontend-recovery-controller.js +7 -7
- package/dist/workflows/dag/frontend-recovery-lineage.js +13 -0
- package/dist/workflows/dag/frontend-recovery-run.js +4 -0
- package/dist/workflows/dag/frontend-review-context.js +2 -1
- package/dist/workflows/dag/frontend-review-scopes.js +139 -0
- package/dist/workflows/dag/frontend-session-budget.js +249 -0
- package/dist/workflows/dag/frontend-shadow-dual-write.js +20 -2
- package/dist/workflows/dag/frontend-shape-facts.js +12 -2
- package/dist/workflows/dag/frontend-shape.js +30 -1
- package/dist/workflows/dag/frontend-test-execution-evidence.js +3 -2
- package/dist/workflows/dag/frontend-typed-event-store.js +11 -0
- package/dist/workflows/dag/frontend-verification-trace.js +1 -1
- package/dist/workflows/dag/init-hybrid.js +65 -167
- package/dist/workflows/dag/node-execution.js +38 -155
- package/dist/workflows/dag/prompt.js +4 -0
- package/dist/workflows/dag/rerun-plan.js +7 -1
- package/dist/workflows/dag/runner.js +26 -1
- package/dist/workflows/dag/types.js +6 -0
- package/docs/operations/README.md +1 -0
- package/docs/templates/frontend-design-contract.md +4 -4
- package/docs/templates/frontend-implementation-contract.schema.json +34 -2
- package/docs/templates/frontend-implementation-dag.json +14 -13
- package/package.json +6 -3
- package/skills/frontend-bounded-implement/SKILL.md +3 -3
- package/skills/frontend-bounded-implement/references/code-standards.md +3 -3
- package/skills/frontend-contract/SKILL.md +2 -1
- package/skills/frontend-contract/references/contract-protocol.md +22 -4
- package/skills/frontend-design-review/SKILL.md +22 -16
- package/skills/frontend-design-review/references/review-checklist.md +7 -7
- package/skills/frontend-plan/SKILL.md +21 -2
- package/skills/frontend-plan/references/decision-contract.md +42 -23
- package/skills/frontend-review/SKILL.md +22 -29
- package/skills/frontend-review/references/review-findings.md +24 -43
- package/skills/frontend-scout/references/scout-evidence.md +4 -0
- package/dist/worker/console/static/assets/channel-DAS07MdS.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-Cwk-5jiW.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-Cwk-5jiW.js +0 -1
- package/dist/worker/console/static/assets/index-C0O48S_P.js +0 -449
- package/dist/worker/console/static/assets/index-CzKf4U8P.css +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DyJg5gzW.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-B1cw_0Uy.js +0 -8
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
/** Session event timeline helpers (Observe UI R5 + rich process view). */
|
|
2
2
|
import { UI_TEXT } from "../constants.js";
|
|
3
|
-
import { el } from "../dom.js";
|
|
3
|
+
import { el, iconButton } from "../dom.js";
|
|
4
4
|
import { formatTs, truncateText } from "../format.js";
|
|
5
5
|
import { renderMarkdown } from "../markdown-render.js";
|
|
6
6
|
import { fetchJson } from "../api.js";
|
|
7
7
|
import {
|
|
8
8
|
uiState,
|
|
9
9
|
updateDagTimelineViewportState,
|
|
10
|
+
inspectorReadingState,
|
|
11
|
+
isScrollRegionAboveBottom,
|
|
10
12
|
} from "../state.js";
|
|
11
13
|
|
|
12
14
|
/** Default visible lines for tool result preview before expand. */
|
|
@@ -17,6 +19,10 @@ function isTimelineAutoScrollEnabled() {
|
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
function scrollTimelineToLatest(content) {
|
|
22
|
+
if (content._readingState) {
|
|
23
|
+
content._readingState.followLatest = true;
|
|
24
|
+
content._readingState.seenCount = content._readingState.eventCount;
|
|
25
|
+
}
|
|
20
26
|
content.scrollLeft = 0;
|
|
21
27
|
content.scrollTop = content.scrollHeight;
|
|
22
28
|
uiState.dagTimelineViewportState = updateDagTimelineViewportState(
|
|
@@ -29,7 +35,9 @@ function scrollTimelineToLatest(content) {
|
|
|
29
35
|
);
|
|
30
36
|
}
|
|
31
37
|
|
|
32
|
-
/** Floating
|
|
38
|
+
/** Floating chat-style jump-to-bottom control for execution-record viewports.
|
|
39
|
+
* 可滚动且视口离开底部即出现(终态运行同样可用),贴底或内容不足一屏时隐藏;
|
|
40
|
+
* 点击回到底部,活跃运行同时恢复跟随。 */
|
|
33
41
|
export function createTimelineAutoScrollControl(content) {
|
|
34
42
|
const dock = el("div", "process-timeline-autoscroll-dock");
|
|
35
43
|
const button = document.createElement("button");
|
|
@@ -42,29 +50,21 @@ export function createTimelineAutoScrollControl(content) {
|
|
|
42
50
|
button.appendChild(icon);
|
|
43
51
|
|
|
44
52
|
const sync = () => {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
53
|
+
dock.hidden = !isScrollRegionAboveBottom(content);
|
|
54
|
+
if (dock.hidden) return;
|
|
55
|
+
const unread = content._readingState
|
|
56
|
+
? Math.max(0, content._readingState.eventCount - content._readingState.seenCount)
|
|
57
|
+
: 0;
|
|
58
|
+
const label = unread > 0 ? `${unread} 条新记录 · 回到底部` : "回到底部";
|
|
59
|
+
button.className = `process-timeline-autoscroll${unread > 0 ? " has-unread" : ""}`;
|
|
60
|
+
button.setAttribute("aria-label", label);
|
|
61
|
+
button.setAttribute("data-tooltip", label);
|
|
52
62
|
};
|
|
63
|
+
content._syncTimelineFollow = sync;
|
|
53
64
|
|
|
54
65
|
button.addEventListener("click", () => {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (enabled) scrollTimelineToLatest(content);
|
|
58
|
-
else {
|
|
59
|
-
uiState.dagTimelineViewportState = updateDagTimelineViewportState(
|
|
60
|
-
uiState.dagTimelineViewportState,
|
|
61
|
-
uiState.currentDagRunId,
|
|
62
|
-
content.dataset.nodeId,
|
|
63
|
-
content.scrollLeft,
|
|
64
|
-
content.scrollTop,
|
|
65
|
-
false,
|
|
66
|
-
);
|
|
67
|
-
}
|
|
66
|
+
uiState.dagTimelineAutoScrollEnabled = true;
|
|
67
|
+
scrollTimelineToLatest(content);
|
|
68
68
|
sync();
|
|
69
69
|
});
|
|
70
70
|
|
|
@@ -90,149 +90,6 @@ export function sessionEventArgs(event) {
|
|
|
90
90
|
return args && typeof args === "object" && !Array.isArray(args) ? args : {};
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
function escapeHtml(text) {
|
|
94
|
-
return String(text ?? "")
|
|
95
|
-
.replaceAll("&", "&")
|
|
96
|
-
.replaceAll("<", "<")
|
|
97
|
-
.replaceAll(">", ">")
|
|
98
|
-
.replaceAll('"', """)
|
|
99
|
-
.replaceAll("'", "'");
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function downloadTextFile(filename, text) {
|
|
103
|
-
const blob = new Blob([text], { type: "text/html;charset=utf-8" });
|
|
104
|
-
const url = URL.createObjectURL(blob);
|
|
105
|
-
const a = document.createElement("a");
|
|
106
|
-
a.href = url;
|
|
107
|
-
a.download = filename;
|
|
108
|
-
document.body.appendChild(a);
|
|
109
|
-
a.click();
|
|
110
|
-
a.remove();
|
|
111
|
-
URL.revokeObjectURL(url);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/** 抓取页面真实加载的样式表原文(导出内联用)。
|
|
115
|
-
* 不猜路径:直接取当前文档 <link rel="stylesheet"> 的 href(相对当前页面解析),
|
|
116
|
-
* 反代/子路径部署下也和页面渲染用的是同一份 CSS;fetch 失败时返回 "",
|
|
117
|
-
* 调用方回退 EXPORT_FALLBACK_CSS。 */
|
|
118
|
-
export async function fetchPageStylesheetText() {
|
|
119
|
-
try {
|
|
120
|
-
const links = [...document.querySelectorAll('link[rel="stylesheet"]')];
|
|
121
|
-
const texts = [];
|
|
122
|
-
for (const link of links) {
|
|
123
|
-
const href = link.getAttribute("href");
|
|
124
|
-
if (!href) continue;
|
|
125
|
-
const url = new URL(href, document.baseURI).href;
|
|
126
|
-
const res = await fetch(url, { cache: "no-cache" });
|
|
127
|
-
if (res.ok) texts.push(await res.text());
|
|
128
|
-
}
|
|
129
|
-
return texts.join("\n");
|
|
130
|
-
} catch {
|
|
131
|
-
return "";
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/** 兜底样式:仅在无法抓取页面 /inspect/styles.css 时使用(与页面 process-timeline 排版对齐)。 */
|
|
136
|
-
const EXPORT_FALLBACK_CSS = `
|
|
137
|
-
: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}
|
|
138
|
-
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}
|
|
139
|
-
.process-timeline{display:grid;gap:0;margin:0;padding:var(--space-2) 0;list-style:none}
|
|
140
|
-
.process-timeline-item{display:grid;grid-template-columns:20px minmax(0,1fr);align-items:stretch;min-height:34px}
|
|
141
|
-
.process-timeline-item.has-time{grid-template-columns:20px 92px minmax(0,1fr)}
|
|
142
|
-
.process-timeline-rail{position:relative;display:grid;place-items:center;min-height:34px}
|
|
143
|
-
.process-timeline-rail::before{position:absolute;top:0;bottom:0;width:1px;background:var(--hairline-strong);content:""}
|
|
144
|
-
.process-timeline-item.is-first .process-timeline-rail::before{top:50%}
|
|
145
|
-
.process-timeline-item.is-last .process-timeline-rail::before{bottom:50%}
|
|
146
|
-
.process-timeline-dot{position:relative;z-index:1;width:8px;height:8px;border:2px solid var(--surface);border-radius:50%;background:var(--muted-soft)}
|
|
147
|
-
.process-timeline-lifecycle .process-timeline-dot{background:var(--lavender)}
|
|
148
|
-
.process-timeline-tool-start .process-timeline-dot{background:var(--orange)}
|
|
149
|
-
.process-timeline-tool-end .process-timeline-dot{background:var(--green)}
|
|
150
|
-
.process-timeline-assistant .process-timeline-dot{background:var(--blue)}
|
|
151
|
-
.process-timeline-protocol .process-timeline-dot{background:var(--muted-soft)}
|
|
152
|
-
.process-timeline-item.is-error .process-timeline-dot{background:var(--red)}
|
|
153
|
-
.process-timeline-time{padding:9px 0;color:var(--muted);font-size:10px;font-variant-numeric:tabular-nums}
|
|
154
|
-
.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}
|
|
155
|
-
.process-timeline-protocol .process-timeline-label{color:var(--muted);font-size:11px}
|
|
156
|
-
.process-timeline-body{min-width:0;padding:4px 0 8px var(--space-2)}
|
|
157
|
-
.process-timeline-card{display:grid;gap:6px;min-width:0}
|
|
158
|
-
.process-timeline-details{min-width:0;border:1px solid var(--hairline);border-radius:var(--radius-sm);background:var(--surface)}
|
|
159
|
-
.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}
|
|
160
|
-
.process-timeline-summary::-webkit-details-marker{display:none}
|
|
161
|
-
.process-timeline-summary-title{min-width:0;overflow-wrap:anywhere}
|
|
162
|
-
.process-timeline-summary-status{flex-shrink:0;color:var(--muted);font-size:11px;font-weight:700}
|
|
163
|
-
.process-timeline-summary-status.status-succeeded{color:var(--green)}
|
|
164
|
-
.process-timeline-summary-status.status-failed{color:var(--red)}
|
|
165
|
-
.process-timeline-summary-status.status-pending{color:var(--orange)}
|
|
166
|
-
.process-timeline-tool-card.is-error .process-timeline-details{border-color:var(--red);background:var(--canvas-soft)}
|
|
167
|
-
.process-timeline-card-body{display:grid;gap:6px;padding:0 10px 10px}
|
|
168
|
-
.process-timeline-section-label{color:var(--muted);font-size:11px;font-weight:700}
|
|
169
|
-
.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}
|
|
170
|
-
.process-timeline-more{margin-top:2px}
|
|
171
|
-
.process-timeline-more>summary{cursor:pointer;color:var(--blue);font-size:11px;font-weight:650}
|
|
172
|
-
.process-timeline-thinking>summary{background:var(--lavender)}
|
|
173
|
-
.process-timeline-turn-text{display:grid;gap:6px;padding:8px 10px;border:1px solid var(--hairline);border-radius:var(--radius-sm);background:var(--surface)}
|
|
174
|
-
.process-timeline-turn-text .markdown-output{font-size:12px}
|
|
175
|
-
.process-timeline-tool-only-note{margin:0;font-size:12px}
|
|
176
|
-
.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}
|
|
177
|
-
.process-timeline-turn-tools li{padding:2px 0}
|
|
178
|
-
.process-timeline-agent-end-note{margin:0;font-size:11px}
|
|
179
|
-
.markdown-output{display:grid;gap:var(--space-3);color:var(--body);font-size:13px;line-height:1.65}
|
|
180
|
-
.markdown-output>:first-child{margin-top:0}
|
|
181
|
-
.markdown-output>:last-child{margin-bottom:0}
|
|
182
|
-
.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}
|
|
183
|
-
.markdown-output h1{font-size:21px}.markdown-output h2{font-size:18px}.markdown-output h3{font-size:16px}.markdown-output h4{font-size:14px}
|
|
184
|
-
.markdown-output p,.markdown-output blockquote,.markdown-output ul,.markdown-output ol{margin:0;overflow-wrap:anywhere}
|
|
185
|
-
.markdown-output ul,.markdown-output ol{padding-left:22px}
|
|
186
|
-
.markdown-output blockquote{padding-left:12px}
|
|
187
|
-
.btn{display:inline-flex;align-items:center;gap:6px;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:12px;font-weight:600;cursor:pointer;user-select:none}
|
|
188
|
-
.btn-secondary{background:var(--surface-muted)}
|
|
189
|
-
.btn:hover{border-color:var(--orange);color:var(--orange-active)}
|
|
190
|
-
`;
|
|
191
|
-
|
|
192
|
-
const EXPORT_SHELL_CSS = `
|
|
193
|
-
/* 导出文档变量基础:页面样式把这些变量定义在 body[data-shell]/.inspect-workspace-root
|
|
194
|
-
* 下,独立导出的 HTML 既无该属性也无该包裹,变量会整体失效——深色系统的画布
|
|
195
|
-
* 兜底为黑底,叠加黑字后正文不可读。导出文档自带变量定义并强制 light 配色。 */
|
|
196
|
-
:root{color-scheme:light}
|
|
197
|
-
body{margin:0;font-family:-apple-system,"Segoe UI",Roboto,"PingFang SC","Microsoft YaHei",sans-serif;background:var(--canvas,#f7f7f4)}
|
|
198
|
-
.export-shell{max-width:1000px;margin:0 auto;padding:24px 28px;background:var(--surface,#ffffff);min-height:100vh;box-shadow:0 0 0 1px var(--hairline,#e6e5e0)}
|
|
199
|
-
.export-head h1{font-size:15px;color:var(--ink,#26251e);margin:0;font-weight:700}
|
|
200
|
-
.export-sub{color:var(--muted,#807d72);font-size:12px;margin:4px 0 16px}
|
|
201
|
-
.export-controls{display:flex;justify-content:flex-start;margin:0 0 12px}
|
|
202
|
-
.export-toggle{min-height:28px;padding:0 12px;border:1px solid var(--hairline-strong,#cfcdc4);border-radius:var(--radius-sm,6px);background:var(--surface,#ffffff);color:var(--body,#5a5852);font-size:11px;font-weight:600;cursor:pointer;user-select:none}
|
|
203
|
-
.export-toggle:hover,.export-toggle:focus-visible{border-color:var(--orange,#f54e00);color:var(--orange-active,#d04200);outline:none}
|
|
204
|
-
body.hide-protocol .process-timeline-item.process-timeline-protocol{display:none}
|
|
205
|
-
`;
|
|
206
|
-
|
|
207
|
-
/** 导出文档自带的变量基础:与页面 styles.css 的 light 主题保持一致。
|
|
208
|
-
* 即使 fetch 到了页面 CSS,其变量也挂在 body[data-shell=…]/.inspect-workspace-root
|
|
209
|
-
* 选择器下而不生效,必须在导出文档内重新定义。 */
|
|
210
|
-
const EXPORT_ROOT_VARS_CSS = `:root{--canvas:#f7f7f4;--surface:#ffffff;--canvas-soft:#fafaf7;--surface-muted:#f0efe9;--surface-ink:#26251e;--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;--mint:#d8ead5;--peach:#f1d8cb;--gold:#d8b166;--radius-sm:6px;--radius-md:8px;--radius-lg:12px;--space-1:4px;--space-2:8px;--space-3:12px;--space-4:16px;--space-5:20px;--space-6:24px;--space-8:32px;--space-10:40px;--space-12:48px}`;
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* 把页面渲染出的时间线 innerHTML 封装成自包含 HTML。
|
|
214
|
-
* @param {string} bodyHTML 页面渲染的 .process-timeline 等真实 DOM 序列化结果
|
|
215
|
-
* @param {{nodeId?:string,dagRunId?:string}} meta
|
|
216
|
-
* @param {string} [cssText] 页面 /inspect/styles.css 原文;缺失时用兜底样式
|
|
217
|
-
*/
|
|
218
|
-
export function buildPageStyledDoc(bodyHTML, meta = {}, cssText = "") {
|
|
219
|
-
const now = new Date().toLocaleString();
|
|
220
|
-
const title = meta.nodeId ? `节点执行过程 · ${meta.nodeId}` : "节点执行过程";
|
|
221
|
-
const style = cssText.trim()
|
|
222
|
-
? `${EXPORT_ROOT_VARS_CSS}\n${cssText}\n\n/* 导出外壳 */\n${EXPORT_SHELL_CSS}`
|
|
223
|
-
: `${EXPORT_FALLBACK_CSS}\n${EXPORT_SHELL_CSS}`;
|
|
224
|
-
// 有协议行时,在左上角提供一个「展开/隐藏原始协议事件」按钮(与页面一致),默认隐藏。
|
|
225
|
-
const hasProtocol = bodyHTML.includes("process-timeline-protocol");
|
|
226
|
-
const controls = hasProtocol
|
|
227
|
-
? `<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>`
|
|
228
|
-
: "";
|
|
229
|
-
// body 带 data-shell="observe-console":内联的页面 styles.css 把全部设计令牌
|
|
230
|
-
// (--canvas/--surface/--hairline-strong …) 与部分规则 (body[data-shell] …) 定义在
|
|
231
|
-
// 该作用域下,导出文档带上属性后令牌与前缀规则原生命中,与页面渲染一致。
|
|
232
|
-
// EXPORT_ROOT_VARS_CSS 仍保留:fetch 页面 CSS 失败的兜底路径令牌不缺。
|
|
233
|
-
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 data-shell="observe-console"><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>`;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
93
|
export function sessionEventResult(event) {
|
|
237
94
|
return normalizeSessionEventFields(event).result;
|
|
238
95
|
}
|
|
@@ -442,6 +299,7 @@ export function buildSessionTimelineItems(events) {
|
|
|
442
299
|
while (index + 1 < events.length) {
|
|
443
300
|
const nextType = events[index + 1]?.type;
|
|
444
301
|
if (nextType !== "message_start" && nextType !== "message_end") break;
|
|
302
|
+
if (eventCategory({ kind: "protocol", event: events[index + 1] }) !== eventCategory({ kind: "protocol", event })) break;
|
|
445
303
|
count += 1;
|
|
446
304
|
index += 1;
|
|
447
305
|
}
|
|
@@ -480,13 +338,16 @@ export function splitSessionEventsByAttempt(events) {
|
|
|
480
338
|
* key. attempt-N shows only that attempt's segment when the shared log carries
|
|
481
339
|
* multiple attempts; other keys (pass-N, null) keep the full stream.
|
|
482
340
|
*/
|
|
483
|
-
export function resolveTimelineVisibleEvents(events, executionKey) {
|
|
341
|
+
export function resolveTimelineVisibleEvents(events, executionKey, expectedAttempts) {
|
|
484
342
|
const match =
|
|
485
343
|
typeof executionKey === "string"
|
|
486
344
|
? /^attempt-(\d+)$/.exec(executionKey)
|
|
487
345
|
: null;
|
|
488
346
|
if (!match) return { events, note: null };
|
|
489
347
|
const segments = splitSessionEventsByAttempt(events);
|
|
348
|
+
if (Number.isSafeInteger(expectedAttempts) && segments.length !== expectedAttempts) return {
|
|
349
|
+
events, note: "当前显示节点共享日志:日志会话边界与尝试记录不一致,无法可靠按尝试拆分;分次产出可在“产出”中查看。",
|
|
350
|
+
};
|
|
490
351
|
if (segments.length <= 1) return { events, note: null };
|
|
491
352
|
const requested = Number(match[1]);
|
|
492
353
|
if (requested >= 1 && requested <= segments.length) {
|
|
@@ -531,6 +392,7 @@ export function buildRichTimelineItems(events, options = {}) {
|
|
|
531
392
|
while (index + 1 < events.length) {
|
|
532
393
|
const nextType = events[index + 1]?.type;
|
|
533
394
|
if (nextType !== "message_start" && nextType !== "message_end") break;
|
|
395
|
+
if (eventCategory({ kind: "protocol", event: events[index + 1] }) !== eventCategory({ kind: "protocol", event })) break;
|
|
534
396
|
count += 1;
|
|
535
397
|
index += 1;
|
|
536
398
|
protocolEvents.push(normalizeSessionEventFields(events[index]));
|
|
@@ -548,6 +410,7 @@ export function buildRichTimelineItems(events, options = {}) {
|
|
|
548
410
|
kind: "protocol",
|
|
549
411
|
event,
|
|
550
412
|
protocolCount: count,
|
|
413
|
+
protocolEvents,
|
|
551
414
|
collapsed: true,
|
|
552
415
|
detailKey: `protocol-${items.length}`,
|
|
553
416
|
});
|
|
@@ -684,32 +547,28 @@ export function buildRichTimelineItems(events, options = {}) {
|
|
|
684
547
|
}
|
|
685
548
|
|
|
686
549
|
const EXECUTION_RECORD_FILTERS = Object.freeze([
|
|
687
|
-
["all", "全部"],
|
|
688
|
-
["model", "模型消息"],
|
|
689
|
-
["tools", "工具与进程"],
|
|
550
|
+
["all", "全部"], ["model", "模型消息"], ["tools", "工具与进程"], ["system", "系统事件"],
|
|
690
551
|
]);
|
|
691
|
-
|
|
692
552
|
function normalizeExecutionRecordFilter(value) {
|
|
693
|
-
return
|
|
553
|
+
return ["model", "tools", "system"].includes(value) ? value : "all";
|
|
694
554
|
}
|
|
695
|
-
|
|
696
|
-
|
|
555
|
+
export function eventCategory(item) {
|
|
556
|
+
if (item.kind === "tool") return "tools";
|
|
557
|
+
if (item.kind === "turn") return "model";
|
|
558
|
+
if (item.kind === "protocol") {
|
|
559
|
+
const role = item.event?.message?.role ?? item.event?.role;
|
|
560
|
+
return ["tool", "toolResult"].includes(role) ? "tools" : ["assistant", "user"].includes(role) ? "model" : "system";
|
|
561
|
+
}
|
|
562
|
+
return "system";
|
|
563
|
+
}
|
|
564
|
+
export function isErrorRecord(item) {
|
|
565
|
+
const event = item.event ?? {};
|
|
566
|
+
return item.isError === true || event.isError === true || event.ok === false ||
|
|
567
|
+
["failed", "error", "failure"].includes(event.status) || event.message?.stopReason === "error";
|
|
568
|
+
}
|
|
569
|
+
export function filterExecutionRecordItems(items, filter, errorsOnly = false) {
|
|
697
570
|
const normalized = normalizeExecutionRecordFilter(filter);
|
|
698
|
-
|
|
699
|
-
return items.filter((item) => {
|
|
700
|
-
const protocolEvents = Array.isArray(item.protocolEvents)
|
|
701
|
-
? item.protocolEvents
|
|
702
|
-
: [item.event];
|
|
703
|
-
const toolProtocol =
|
|
704
|
-
item.kind === "protocol" &&
|
|
705
|
-
protocolEvents.some((event) => {
|
|
706
|
-
const role = event?.message?.role ?? event?.role;
|
|
707
|
-
return role === "tool" || role === "toolResult";
|
|
708
|
-
});
|
|
709
|
-
const modelFact =
|
|
710
|
-
item.kind === "turn" || (item.kind === "protocol" && !toolProtocol);
|
|
711
|
-
return normalized === "model" ? modelFact : !modelFact;
|
|
712
|
-
});
|
|
571
|
+
return items.filter((item) => (normalized === "all" || eventCategory(item) === normalized) && (!errorsOnly || isErrorRecord(item)));
|
|
713
572
|
}
|
|
714
573
|
|
|
715
574
|
function ensureOpenDetailsSet() {
|
|
@@ -726,7 +585,7 @@ function closedDetailKey(detailKey) {
|
|
|
726
585
|
function bindDetailsToggle(details, detailKey, defaultOpen = false) {
|
|
727
586
|
const openSet = ensureOpenDetailsSet();
|
|
728
587
|
if (defaultOpen) {
|
|
729
|
-
|
|
588
|
+
details.open = !openSet.has(closedDetailKey(detailKey));
|
|
730
589
|
} else if (openSet.has(detailKey)) {
|
|
731
590
|
details.open = true;
|
|
732
591
|
}
|
|
@@ -796,9 +655,9 @@ function renderToolCard(item) {
|
|
|
796
655
|
more.dataset.detailKey = `${item.detailKey}-full`;
|
|
797
656
|
bindDetailsToggle(more, `${item.detailKey}-full`);
|
|
798
657
|
const moreSummary = document.createElement("summary");
|
|
799
|
-
moreSummary.textContent =
|
|
658
|
+
moreSummary.textContent = `展开剩余 ${totalLines - TOOL_RESULT_PREVIEW_LINES} 行(共 ${totalLines} 行)`;
|
|
800
659
|
more.appendChild(moreSummary);
|
|
801
|
-
appendPreBlock(more, "process-timeline-code", resultText);
|
|
660
|
+
appendPreBlock(more, "process-timeline-code", resultText.split("\n").slice(TOOL_RESULT_PREVIEW_LINES).join("\n"));
|
|
802
661
|
body.appendChild(more);
|
|
803
662
|
}
|
|
804
663
|
} else if (!item.pending) {
|
|
@@ -827,8 +686,7 @@ function renderTurnCard(item) {
|
|
|
827
686
|
details.className = "process-timeline-details process-timeline-thinking";
|
|
828
687
|
details.dataset.detailKey = `${item.detailKey}-thinking-${i}`;
|
|
829
688
|
// Default open for first thinking block so anomalies are visible.
|
|
830
|
-
|
|
831
|
-
bindDetailsToggle(details, `${item.detailKey}-thinking-${i}`);
|
|
689
|
+
bindDetailsToggle(details, `${item.detailKey}-thinking-${i}`, i === 0);
|
|
832
690
|
const summary = document.createElement("summary");
|
|
833
691
|
summary.className = "process-timeline-summary";
|
|
834
692
|
summary.textContent = "思考过程";
|
|
@@ -937,9 +795,45 @@ export async function mergeSessionEvents(dagRunId, nodeId) {
|
|
|
937
795
|
}
|
|
938
796
|
}
|
|
939
797
|
|
|
798
|
+
// Work on text nodes only: log contents must never become HTML.
|
|
799
|
+
export function highlightTimelineSearch(row, query) {
|
|
800
|
+
for (const mark of row.querySelectorAll("mark.process-search-highlight")) {
|
|
801
|
+
mark.replaceWith(document.createTextNode(mark.textContent));
|
|
802
|
+
}
|
|
803
|
+
row.normalize();
|
|
804
|
+
if (!query) return false;
|
|
805
|
+
const pattern = new RegExp(query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "giu");
|
|
806
|
+
const walker = document.createTreeWalker(row, 4 /* SHOW_TEXT */);
|
|
807
|
+
const nodes = [];
|
|
808
|
+
while (walker.nextNode()) nodes.push(walker.currentNode);
|
|
809
|
+
let matched = false;
|
|
810
|
+
for (const node of nodes) {
|
|
811
|
+
const text = node.textContent;
|
|
812
|
+
const parts = [];
|
|
813
|
+
let end = 0;
|
|
814
|
+
for (const match of text.matchAll(pattern)) {
|
|
815
|
+
parts.push(document.createTextNode(text.slice(end, match.index)));
|
|
816
|
+
parts.push(el("mark", "process-search-highlight", match[0]));
|
|
817
|
+
end = match.index + match[0].length;
|
|
818
|
+
}
|
|
819
|
+
if (!parts.length) continue;
|
|
820
|
+
parts.push(document.createTextNode(text.slice(end)));
|
|
821
|
+
node.replaceWith(...parts);
|
|
822
|
+
matched = true;
|
|
823
|
+
}
|
|
824
|
+
return matched;
|
|
825
|
+
}
|
|
826
|
+
|
|
940
827
|
export function renderSessionTimeline(content, nodeId, options = {}) {
|
|
941
828
|
content.dataset.dagRunId = uiState.currentDagRunId ?? "";
|
|
942
829
|
content.dataset.nodeId = nodeId ?? "";
|
|
830
|
+
content.dataset.executionKey = options.executionKey ?? content.dataset.executionKey ?? "";
|
|
831
|
+
const reading = inspectorReadingState(uiState.currentDagRunId, nodeId, "timeline", content.dataset.executionKey, uiState.dagTimelineAutoScrollEnabled !== false);
|
|
832
|
+
if (reading) {
|
|
833
|
+
content._readingState = reading;
|
|
834
|
+
uiState.dagTimelineAutoScrollEnabled = reading.followLatest;
|
|
835
|
+
uiState.sessionTimelineOpenDetails = reading.openDetails;
|
|
836
|
+
}
|
|
943
837
|
content.classList.add("process-timeline-scroll");
|
|
944
838
|
if (!nodeId || uiState.sessionEvents.length === 0) {
|
|
945
839
|
content.appendChild(el("p", "empty", UI_TEXT.noSessionEvents));
|
|
@@ -949,74 +843,116 @@ export function renderSessionTimeline(content, nodeId, options = {}) {
|
|
|
949
843
|
const visible = resolveTimelineVisibleEvents(
|
|
950
844
|
uiState.sessionEvents,
|
|
951
845
|
options.executionKey,
|
|
846
|
+
options.attemptCount,
|
|
952
847
|
);
|
|
953
848
|
if (visible.note) {
|
|
954
849
|
content.appendChild(el("p", "process-timeline-note", visible.note));
|
|
955
850
|
}
|
|
956
851
|
|
|
957
|
-
const showProtocol = Boolean(uiState.sessionTimelineShowProtocol);
|
|
852
|
+
const showProtocol = reading?.showProtocol ?? Boolean(uiState.sessionTimelineShowProtocol);
|
|
958
853
|
const activeFilter = normalizeExecutionRecordFilter(
|
|
959
|
-
uiState.executionRecordFilter,
|
|
854
|
+
reading?.filter ?? uiState.executionRecordFilter,
|
|
960
855
|
);
|
|
961
856
|
const toolbar = el("div", "process-timeline-toolbar");
|
|
962
|
-
const filterGroup = el("
|
|
963
|
-
filterGroup.setAttribute("role", "group");
|
|
857
|
+
const filterGroup = el("select", "observe-select process-timeline-filter-group");
|
|
964
858
|
filterGroup.setAttribute("aria-label", "筛选执行记录");
|
|
965
859
|
for (const [filter, label] of EXECUTION_RECORD_FILTERS) {
|
|
966
|
-
const
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
button.setAttribute("aria-pressed", String(activeFilter === filter));
|
|
970
|
-
button.textContent = label;
|
|
971
|
-
button.addEventListener("click", () => {
|
|
972
|
-
uiState.executionRecordFilter = filter;
|
|
973
|
-
rerenderSessionTimeline(content, nodeId, options);
|
|
974
|
-
});
|
|
975
|
-
filterGroup.appendChild(button);
|
|
860
|
+
const option = el("option", null, label);
|
|
861
|
+
option.value = filter;
|
|
862
|
+
filterGroup.appendChild(option);
|
|
976
863
|
}
|
|
864
|
+
filterGroup.value = activeFilter;
|
|
865
|
+
filterGroup.addEventListener("change", () => {
|
|
866
|
+
uiState.executionRecordFilter = filterGroup.value;
|
|
867
|
+
if (reading) reading.filter = filterGroup.value;
|
|
868
|
+
rerenderSessionTimeline(content, nodeId, options);
|
|
869
|
+
content.querySelector(".process-timeline-filter-group")?.focus();
|
|
870
|
+
});
|
|
977
871
|
toolbar.appendChild(filterGroup);
|
|
978
|
-
const protocolToggle =
|
|
872
|
+
const protocolToggle = iconButton(
|
|
873
|
+
showProtocol ? "隐藏原始协议事件" : "展开原始协议事件",
|
|
874
|
+
"ri-code-s-slash-line",
|
|
875
|
+
"process-timeline-protocol-toggle",
|
|
876
|
+
);
|
|
979
877
|
protocolToggle.type = "button";
|
|
980
|
-
protocolToggle.className = "process-timeline-toggle";
|
|
981
878
|
protocolToggle.setAttribute("aria-pressed", String(showProtocol));
|
|
982
|
-
protocolToggle.textContent = showProtocol
|
|
983
|
-
? "隐藏原始协议事件"
|
|
984
|
-
: "展开原始协议事件";
|
|
985
879
|
protocolToggle.addEventListener("click", () => {
|
|
986
880
|
uiState.sessionTimelineShowProtocol = !showProtocol;
|
|
881
|
+
if (reading) reading.showProtocol = !showProtocol;
|
|
987
882
|
rerenderSessionTimeline(content, nodeId, options);
|
|
883
|
+
content.querySelector(".process-timeline-protocol-toggle")?.focus({ preventScroll: true });
|
|
988
884
|
});
|
|
989
|
-
toolbar.appendChild(protocolToggle);
|
|
990
|
-
const exportBtn = el("button", "btn btn-secondary", "导出 HTML");
|
|
991
|
-
exportBtn.type = "button";
|
|
992
|
-
exportBtn.addEventListener("click", async () => {
|
|
993
|
-
const cssText = await fetchPageStylesheetText();
|
|
994
|
-
// 复用页面真实渲染(含原始协议事件),保证导出的排版与页面一致。
|
|
995
|
-
const prevShow = uiState.sessionTimelineShowProtocol;
|
|
996
|
-
uiState.sessionTimelineShowProtocol = true;
|
|
997
|
-
const tmp = document.createElement("div");
|
|
998
|
-
tmp.className = "process-timeline-scroll";
|
|
999
|
-
renderSessionTimeline(tmp, nodeId, options);
|
|
1000
|
-
const toolbar = tmp.querySelector(".process-timeline-toolbar");
|
|
1001
|
-
if (toolbar) toolbar.remove();
|
|
1002
|
-
const innerHTML = tmp.innerHTML;
|
|
1003
|
-
uiState.sessionTimelineShowProtocol = prevShow;
|
|
1004
|
-
downloadTextFile(
|
|
1005
|
-
`node-process-${nodeId || "node"}-${Date.now()}.html`,
|
|
1006
|
-
buildPageStyledDoc(innerHTML, {
|
|
1007
|
-
nodeId,
|
|
1008
|
-
dagRunId: uiState.currentDagRunId ?? "",
|
|
1009
|
-
}, cssText),
|
|
1010
|
-
);
|
|
1011
|
-
});
|
|
1012
|
-
toolbar.appendChild(exportBtn);
|
|
1013
885
|
content.appendChild(toolbar);
|
|
1014
|
-
|
|
886
|
+
if (reading) {
|
|
887
|
+
reading.eventCount = visible.events.length;
|
|
888
|
+
if (!reading.eventsInitialized || reading.followLatest) reading.seenCount = reading.eventCount;
|
|
889
|
+
reading.eventsInitialized = true;
|
|
890
|
+
content.appendChild(createTimelineAutoScrollControl(content));
|
|
891
|
+
}
|
|
1015
892
|
|
|
1016
893
|
const timelineItems = filterExecutionRecordItems(
|
|
1017
894
|
buildRichTimelineItems(visible.events, { showProtocol }),
|
|
1018
895
|
activeFilter,
|
|
896
|
+
reading?.errorsOnly,
|
|
1019
897
|
);
|
|
898
|
+
const errors = iconButton("仅看错误", "ri-error-warning-line", "process-timeline-errors");
|
|
899
|
+
errors.type = "button";
|
|
900
|
+
errors.setAttribute("aria-pressed", String(reading?.errorsOnly === true));
|
|
901
|
+
errors.addEventListener("click", () => {
|
|
902
|
+
if (reading) reading.errorsOnly = !reading.errorsOnly;
|
|
903
|
+
rerenderSessionTimeline(content, nodeId, options);
|
|
904
|
+
});
|
|
905
|
+
toolbar.appendChild(errors);
|
|
906
|
+
const search = el("input", "process-timeline-search");
|
|
907
|
+
search.type = "search";
|
|
908
|
+
search.placeholder = "搜索当前尝试…";
|
|
909
|
+
search.setAttribute("aria-label", "搜索当前尝试执行记录");
|
|
910
|
+
search.value = reading?.query ?? "";
|
|
911
|
+
const resultCount = el("span", "muted");
|
|
912
|
+
resultCount.setAttribute("role", "status");
|
|
913
|
+
let matches = [], matchIndex = -1;
|
|
914
|
+
const searchButtons = [];
|
|
915
|
+
const refreshMatches = () => {
|
|
916
|
+
const query = search.value.trim();
|
|
917
|
+
if (reading) reading.query = search.value;
|
|
918
|
+
matches = [];
|
|
919
|
+
for (const row of content.querySelectorAll(".process-timeline-item")) {
|
|
920
|
+
const matched = highlightTimelineSearch(row, query);
|
|
921
|
+
row.classList.remove("is-search-current");
|
|
922
|
+
row.classList.toggle("is-search-match", Boolean(matched));
|
|
923
|
+
if (matched) matches.push(row);
|
|
924
|
+
}
|
|
925
|
+
matchIndex = -1;
|
|
926
|
+
for (const button of searchButtons) button.disabled = matches.length === 0;
|
|
927
|
+
resultCount.textContent = query ? `${matches.length} 条匹配` : "";
|
|
928
|
+
};
|
|
929
|
+
const jump = (delta) => {
|
|
930
|
+
if (!matches.length) return;
|
|
931
|
+
matchIndex = matchIndex < 0 ? (delta < 0 ? matches.length - 1 : 0) : (matchIndex + delta + matches.length) % matches.length;
|
|
932
|
+
for (const match of matches) match.classList.remove("is-search-current");
|
|
933
|
+
if (reading) reading.followLatest = false;
|
|
934
|
+
uiState.dagTimelineAutoScrollEnabled = false;
|
|
935
|
+
const row = matches[matchIndex];
|
|
936
|
+
row.classList.add("is-search-current");
|
|
937
|
+
for (const detail of row.querySelectorAll("details")) detail.open = true;
|
|
938
|
+
(row.querySelector("mark.process-search-highlight") ?? row).scrollIntoView({ block: "center" });
|
|
939
|
+
resultCount.textContent = `${matchIndex + 1} / ${matches.length}`;
|
|
940
|
+
content._syncTimelineFollow?.();
|
|
941
|
+
};
|
|
942
|
+
search.addEventListener("input", (event) => { if (!event.isComposing) refreshMatches(); });
|
|
943
|
+
search.addEventListener("compositionend", refreshMatches);
|
|
944
|
+
search.addEventListener("keydown", (event) => { if (event.key === "Enter" && !event.isComposing) { event.preventDefault(); jump(event.shiftKey ? -1 : 1); } });
|
|
945
|
+
toolbar.appendChild(search);
|
|
946
|
+
for (const [label, delta] of [["上一处", -1], ["下一处", 1]]) {
|
|
947
|
+
const button = iconButton(label, delta < 0 ? "ri-arrow-up-s-line" : "ri-arrow-down-s-line", delta < 0 ? "process-search-previous" : "process-search-next");
|
|
948
|
+
button.type = "button";
|
|
949
|
+
button.disabled = true;
|
|
950
|
+
searchButtons.push(button);
|
|
951
|
+
button.addEventListener("click", () => jump(delta));
|
|
952
|
+
toolbar.appendChild(button);
|
|
953
|
+
}
|
|
954
|
+
toolbar.appendChild(resultCount);
|
|
955
|
+
toolbar.appendChild(protocolToggle);
|
|
1020
956
|
const hasEventTimes = visible.events.some((event) =>
|
|
1021
957
|
Boolean(sessionEventTimestamp(event)),
|
|
1022
958
|
);
|
|
@@ -1082,33 +1018,45 @@ export function renderSessionTimeline(content, nodeId, options = {}) {
|
|
|
1082
1018
|
ul.appendChild(li);
|
|
1083
1019
|
}
|
|
1084
1020
|
content.appendChild(ul);
|
|
1021
|
+
refreshMatches();
|
|
1085
1022
|
|
|
1086
1023
|
const timelineState =
|
|
1087
1024
|
uiState.dagTimelineViewportState?.dagRunId === uiState.currentDagRunId &&
|
|
1088
1025
|
uiState.dagTimelineViewportState.nodeId === nodeId
|
|
1089
1026
|
? uiState.dagTimelineViewportState
|
|
1090
1027
|
: null;
|
|
1028
|
+
if (content.dataset.timelineScrollBound !== "1") {
|
|
1029
|
+
content.dataset.timelineScrollBound = "1";
|
|
1091
1030
|
content.addEventListener(
|
|
1092
1031
|
"scroll",
|
|
1093
1032
|
() => {
|
|
1033
|
+
if (content.dataset.inspectorTab && content.dataset.inspectorTab !== "timeline") return;
|
|
1034
|
+
if (content.scrollHeight - content.clientHeight - content.scrollTop > 48) {
|
|
1035
|
+
uiState.dagTimelineAutoScrollEnabled = false;
|
|
1036
|
+
if (content._readingState) content._readingState.followLatest = false;
|
|
1037
|
+
}
|
|
1094
1038
|
uiState.dagTimelineViewportState = updateDagTimelineViewportState(
|
|
1095
1039
|
uiState.dagTimelineViewportState,
|
|
1096
1040
|
uiState.currentDagRunId,
|
|
1097
|
-
|
|
1041
|
+
content.dataset.nodeId,
|
|
1098
1042
|
content.scrollLeft,
|
|
1099
1043
|
content.scrollTop,
|
|
1100
1044
|
isTimelineAutoScrollEnabled(),
|
|
1101
1045
|
);
|
|
1046
|
+
if (content._readingState) Object.assign(content._readingState, { scrollTop: content.scrollTop, scrollLeft: content.scrollLeft });
|
|
1047
|
+
content._syncTimelineFollow?.();
|
|
1102
1048
|
},
|
|
1103
1049
|
{ passive: true },
|
|
1104
1050
|
);
|
|
1051
|
+
}
|
|
1105
1052
|
if (isTimelineAutoScrollEnabled()) {
|
|
1106
1053
|
content.scrollLeft = timelineState?.scrollLeft ?? 0;
|
|
1107
1054
|
content.scrollTop = content.scrollHeight;
|
|
1108
1055
|
} else {
|
|
1109
|
-
content.scrollLeft = timelineState
|
|
1110
|
-
content.scrollTop = timelineState
|
|
1056
|
+
content.scrollLeft = reading?.scrollLeft ?? timelineState?.scrollLeft ?? 0;
|
|
1057
|
+
content.scrollTop = reading?.scrollTop ?? timelineState?.scrollTop ?? 0;
|
|
1111
1058
|
}
|
|
1059
|
+
content._syncTimelineFollow?.();
|
|
1112
1060
|
}
|
|
1113
1061
|
|
|
1114
1062
|
function rerenderSessionTimeline(content, nodeId, options) {
|
|
@@ -1129,4 +1077,5 @@ function rerenderSessionTimeline(content, nodeId, options) {
|
|
|
1129
1077
|
content.scrollTop = scrollTop;
|
|
1130
1078
|
content.scrollLeft = scrollLeft;
|
|
1131
1079
|
}
|
|
1080
|
+
content._syncTimelineFollow?.();
|
|
1132
1081
|
}
|