@bolloon/bolloon-agent 0.2.8 → 0.2.10

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 (268) hide show
  1. package/README.md +467 -467
  2. package/bin/bolloon-cli.cjs +183 -183
  3. package/bin/bolloon.cjs +0 -0
  4. package/bin/bolloon.js +157 -0
  5. package/dist/agents/agent-manifest-protocol.js +0 -52
  6. package/dist/agents/constraint-layer.js +19 -19
  7. package/dist/agents/pi-sdk.js +287 -1213
  8. package/dist/agents/shell-guard.js +2 -2
  9. package/dist/agents/workflow-pivot-loop.js +2 -81
  10. package/dist/bollharness/src/scripts/deploy-guard.js +8 -8
  11. package/dist/bollharness-integration/context-router-judgment.js +4 -4
  12. package/dist/bollharness-integration/context-router.js +292 -292
  13. package/dist/bollharness-integration/gate-state-machine.js +13 -26
  14. package/dist/bollharness-integration/integration.js +0 -71
  15. package/dist/bollharness-integration/llm-judgment-engine.js +134 -134
  16. package/dist/bollharness-integration/skill-adapter.js +127 -52
  17. package/dist/bootstrap/context-collector.js +3 -6
  18. package/dist/bootstrap/lifecycle-hooks.js +1 -15
  19. package/dist/cli-entry.js +35 -39
  20. package/dist/constraint-runtime/src/_archive_helper.js +9 -0
  21. package/dist/constraint-runtime/src/agent/coordinator.js +48 -0
  22. package/dist/constraint-runtime/src/agent/index.js +1 -0
  23. package/dist/constraint-runtime/src/assistant/index.js +12 -0
  24. package/dist/constraint-runtime/src/bootstrap/index.js +12 -0
  25. package/dist/constraint-runtime/src/bootstrap_graph.js +13 -0
  26. package/dist/constraint-runtime/src/bridge/index.js +12 -0
  27. package/dist/constraint-runtime/src/buddy/index.js +12 -0
  28. package/dist/constraint-runtime/src/cli/index.js +12 -0
  29. package/dist/constraint-runtime/src/command_graph.js +10 -0
  30. package/dist/constraint-runtime/src/commands.js +60 -0
  31. package/dist/constraint-runtime/src/components/index.js +12 -0
  32. package/dist/constraint-runtime/src/constants/index.js +12 -0
  33. package/dist/constraint-runtime/src/constraint/budget.js +22 -0
  34. package/dist/constraint-runtime/src/constraint/index.js +2 -0
  35. package/dist/constraint-runtime/src/constraint/permission.js +20 -0
  36. package/dist/constraint-runtime/src/context.js +30 -0
  37. package/dist/constraint-runtime/src/coordinator/index.js +12 -0
  38. package/dist/constraint-runtime/src/cost_hook.js +4 -0
  39. package/dist/constraint-runtime/src/cost_tracker.js +8 -0
  40. package/dist/constraint-runtime/src/deferred_init.js +10 -0
  41. package/dist/constraint-runtime/src/direct_modes.js +6 -0
  42. package/dist/constraint-runtime/src/dynamic-tool-loader.js +85 -0
  43. package/dist/constraint-runtime/src/entrypoints/index.js +12 -0
  44. package/dist/constraint-runtime/src/execution_registry.js +44 -0
  45. package/dist/constraint-runtime/src/history.js +9 -0
  46. package/dist/constraint-runtime/src/hooks/index.js +12 -0
  47. package/dist/constraint-runtime/src/index.js +26 -0
  48. package/dist/constraint-runtime/src/ink.js +4 -0
  49. package/dist/constraint-runtime/src/keybindings/index.js +12 -0
  50. package/dist/constraint-runtime/src/memdir/index.js +12 -0
  51. package/dist/constraint-runtime/src/migrations/index.js +12 -0
  52. package/dist/constraint-runtime/src/models.js +1 -0
  53. package/dist/constraint-runtime/src/moreright/index.js +12 -0
  54. package/dist/constraint-runtime/src/native_ts/index.js +12 -0
  55. package/dist/constraint-runtime/src/output_styles/index.js +12 -0
  56. package/dist/constraint-runtime/src/parity_audit.js +12 -0
  57. package/dist/constraint-runtime/src/plugins/index.js +12 -0
  58. package/dist/constraint-runtime/src/port_manifest.js +11 -0
  59. package/dist/constraint-runtime/src/prefetch.js +9 -0
  60. package/dist/constraint-runtime/src/query.js +1 -0
  61. package/dist/constraint-runtime/src/remote/index.js +12 -0
  62. package/dist/constraint-runtime/src/remote_runtime.js +9 -0
  63. package/dist/constraint-runtime/src/runtime/index.js +1 -0
  64. package/dist/constraint-runtime/src/runtime/session.js +35 -0
  65. package/dist/constraint-runtime/src/schemas/index.js +12 -0
  66. package/dist/constraint-runtime/src/screens/index.js +12 -0
  67. package/dist/constraint-runtime/src/server/index.js +12 -0
  68. package/dist/constraint-runtime/src/services/index.js +12 -0
  69. package/dist/constraint-runtime/src/session_store.js +22 -0
  70. package/dist/constraint-runtime/src/setup.js +30 -0
  71. package/dist/constraint-runtime/src/skills/index.js +1 -0
  72. package/dist/constraint-runtime/src/skills/skill-registry.js +28 -0
  73. package/dist/constraint-runtime/src/state/index.js +12 -0
  74. package/dist/constraint-runtime/src/system_init.js +20 -0
  75. package/dist/constraint-runtime/src/thinking/engine.js +42 -0
  76. package/dist/constraint-runtime/src/thinking/index.js +1 -0
  77. package/dist/constraint-runtime/src/tool_pool.js +8 -0
  78. package/dist/constraint-runtime/src/tools/OpenCLI/execAdapter.js +7 -0
  79. package/dist/constraint-runtime/src/tools/OpenCLI/listAdapters.js +7 -0
  80. package/dist/constraint-runtime/src/tools/OpenCLI/runCommand.js +7 -0
  81. package/dist/constraint-runtime/src/tools/SafeSDK/confirmTransaction.js +6 -0
  82. package/dist/constraint-runtime/src/tools/SafeSDK/createTransaction.js +8 -0
  83. package/dist/constraint-runtime/src/tools/SafeSDK/executeTransaction.js +6 -0
  84. package/dist/constraint-runtime/src/tools/SafeSDK/getBalance.js +6 -0
  85. package/dist/constraint-runtime/src/tools/SafeSDK/getPendingTransactions.js +6 -0
  86. package/dist/constraint-runtime/src/tools/SafeSDK/proposeTransaction.js +6 -0
  87. package/dist/constraint-runtime/src/tools.js +80 -0
  88. package/dist/constraint-runtime/src/transcript.js +19 -0
  89. package/dist/constraint-runtime/src/types/index.js +12 -0
  90. package/dist/constraint-runtime/src/upstream_proxy/index.js +12 -0
  91. package/dist/constraint-runtime/src/utils/index.js +12 -0
  92. package/dist/constraint-runtime/src/vim/index.js +12 -0
  93. package/dist/constraint-runtime/src/voice/index.js +12 -0
  94. package/dist/constraint-runtime/tests/agent.test.js +16 -0
  95. package/dist/constraint-runtime/tests/constraint.test.js +41 -0
  96. package/dist/constraint-runtime/tests/skill.test.js +19 -0
  97. package/dist/constraint-runtime/tests/thinking.test.js +22 -0
  98. package/dist/context-compaction/auto-compact.js +7 -7
  99. package/dist/electron-preload.js +1 -18
  100. package/dist/electron-preload.js.map +1 -1
  101. package/dist/electron.js +168 -3
  102. package/dist/electron.js.map +1 -1
  103. package/dist/index.js +116 -136
  104. package/dist/llm/config-store.js +8 -24
  105. package/dist/llm/llm-judgment-client.js +102 -102
  106. package/dist/llm/pi-ai.js +85 -126
  107. package/dist/llm/system-prompt/layers/channel/local.md +14 -14
  108. package/dist/llm/system-prompt/layers/channel/p2p-agent.md +18 -18
  109. package/dist/llm/system-prompt/layers/channel/p2p-visitor.md +19 -19
  110. package/dist/llm/system-prompt/layers/core/artifacts_storage.md +89 -89
  111. package/dist/llm/system-prompt/layers/core/evenhandedness.md +21 -21
  112. package/dist/llm/system-prompt/layers/core/hibs_reminders.md +15 -15
  113. package/dist/llm/system-prompt/layers/core/identity.md +37 -37
  114. package/dist/llm/system-prompt/layers/core/knowledge.md +17 -17
  115. package/dist/llm/system-prompt/layers/core/memory_system.md +12 -12
  116. package/dist/llm/system-prompt/layers/core/network_filesystem.md +28 -28
  117. package/dist/llm/system-prompt/layers/core/refusal.md +37 -37
  118. package/dist/llm/system-prompt/layers/core/tone.md +31 -31
  119. package/dist/llm/system-prompt/layers/core/tools.thin.md +13 -13
  120. package/dist/llm/system-prompt/layers/core/wellbeing.md +41 -41
  121. package/dist/llm/system-prompt/layers/role/architect.md +20 -20
  122. package/dist/llm/system-prompt/layers/role/expert.md +19 -19
  123. package/dist/llm/system-prompt/layers/role/implementer.md +15 -15
  124. package/dist/llm/system-prompt/layers/role/security.md +15 -15
  125. package/dist/llm/system-prompt/layers/tool/artifacts.md +72 -72
  126. package/dist/llm/system-prompt/layers/tool/bash.md +25 -25
  127. package/dist/llm/system-prompt/layers/tool/hibs_api.md +171 -171
  128. package/dist/llm/system-prompt/layers/tool/image_search.md +70 -70
  129. package/dist/llm/system-prompt/layers/tool/manifest.md +89 -89
  130. package/dist/llm/system-prompt/layers/tool/mcp_apps.md +53 -53
  131. package/dist/llm/system-prompt/layers/tool/web_search.md +83 -83
  132. package/dist/llm/tool-manifest/ask_user_input.js +1 -1
  133. package/dist/llm/tool-manifest/bash.js +3 -3
  134. package/dist/llm/tool-manifest/create_file.js +4 -4
  135. package/dist/llm/tool-manifest/fetch_sports_data.js +3 -3
  136. package/dist/llm/tool-manifest/image_search.js +2 -2
  137. package/dist/llm/tool-manifest/mcp.js +2 -2
  138. package/dist/llm/tool-manifest/message_compose.js +3 -3
  139. package/dist/llm/tool-manifest/places.js +2 -2
  140. package/dist/llm/tool-manifest/present_files.js +1 -1
  141. package/dist/llm/tool-manifest/recipe.js +2 -2
  142. package/dist/llm/tool-manifest/str_replace.js +5 -5
  143. package/dist/llm/tool-manifest/view.js +3 -3
  144. package/dist/llm/tool-manifest/weather.js +4 -4
  145. package/dist/llm/tool-manifest/web.js +4 -4
  146. package/dist/pi-ecosystem-colony/index.js +365 -0
  147. package/dist/pi-ecosystem-judgment/causal-judge.js +51 -51
  148. package/dist/pi-ecosystem-judgment/distill-prompt.js +42 -42
  149. package/dist/pi-ecosystem-judgment/distillation.js +14 -14
  150. package/dist/pi-ecosystem-judgment/monitor-gate.js +28 -28
  151. package/dist/pi-ecosystem-judgment/value-injection.js +102 -102
  152. package/dist/security/context-router-tool.js +15 -15
  153. package/dist/security/tool-gate.js +0 -11
  154. package/dist/social/ant-colony/AdaptiveHeartbeat.js +101 -0
  155. package/dist/social/ant-colony/PheromoneEngine.js +227 -0
  156. package/dist/social/ant-colony/index.js +6 -0
  157. package/dist/social/ant-colony/types.js +24 -0
  158. package/dist/test/ai-judgment-test.js +80 -0
  159. package/dist/test/bollharness-integration.test.js +318 -0
  160. package/dist/test/channel-agent-multi-dialogue.js +205 -0
  161. package/dist/test/channel-heartbeat-agent-test.js +201 -0
  162. package/dist/test/constraint-layer.test.js +164 -0
  163. package/dist/test/diap-identity-test.js +172 -0
  164. package/dist/test/diap-quick-test.js +62 -0
  165. package/dist/test/global-shared-context.test.js +315 -0
  166. package/dist/test/harness-judgment-injection.test.js +246 -0
  167. package/dist/test/harness-workflow-integrator-test.js +228 -0
  168. package/dist/test/human-value-store.test.js +243 -0
  169. package/dist/test/hybrid-integration-test.js +118 -0
  170. package/dist/test/hybrid-messenger-verify.js +55 -0
  171. package/dist/test/iroh-bistream-debug.js +38 -0
  172. package/dist/test/iroh-communication.test.js +66 -0
  173. package/dist/test/iroh-debug-test.js +57 -0
  174. package/dist/test/iroh-diap-test.js +71 -0
  175. package/dist/test/iroh-direct-connect.js +55 -0
  176. package/dist/test/iroh-e2e-fixed.js +89 -0
  177. package/dist/test/iroh-e2e-same-process.js +63 -0
  178. package/dist/test/iroh-e2e.js +66 -0
  179. package/dist/test/iroh-final-e2e.js +72 -0
  180. package/dist/test/iroh-relay-test.js +37 -0
  181. package/dist/test/iroh-simple-test.js +41 -0
  182. package/dist/test/iroh-transport-verify.js +54 -0
  183. package/dist/test/iroh-transport.test.js +37 -0
  184. package/dist/test/iroh-two-nodes.js +70 -0
  185. package/dist/test/iroh-verify.js +44 -0
  186. package/dist/test/judgment-decision.test.js +219 -0
  187. package/dist/test/llm-judgment-integration.test.js +220 -0
  188. package/dist/test/p2p-agent-complex-dialogue.js +385 -0
  189. package/dist/test/p2p-agent-dialogue.js +341 -0
  190. package/dist/test/p2p-agent-full-bidirectional.js +510 -0
  191. package/dist/test/p2p-agent-harness-flow.js +437 -0
  192. package/dist/test/p2p-agent-harness-single.js +143 -0
  193. package/dist/test/p2p-ai-dialogue-test.js +318 -0
  194. package/dist/test/p2p-cid-connect-test.js +195 -0
  195. package/dist/test/p2p-connect-receiver.js +69 -0
  196. package/dist/test/p2p-doc-transfer.js +110 -0
  197. package/dist/test/p2p-identity-page-test.js +77 -0
  198. package/dist/test/p2p-iroh-test.js +171 -0
  199. package/dist/test/p2p-minimal-test.js +241 -0
  200. package/dist/test/p2p-node-1.js +148 -0
  201. package/dist/test/p2p-node-2.js +148 -0
  202. package/dist/test/p2p-server.js +281 -0
  203. package/dist/test/p2p-two-nodes-test.js +438 -0
  204. package/dist/test/pi-sdk.test.js +44 -0
  205. package/dist/test/set-persona.js +40 -0
  206. package/dist/test/simple.test.js +9 -0
  207. package/dist/test/storage-integration.test.js +150 -0
  208. package/dist/test/subagent-manager.test.js +276 -0
  209. package/dist/test/test-gate-flow.test.js +81 -0
  210. package/dist/test/workflow-engine.test.js +87 -0
  211. package/dist/test/workflow-pivot-loop.test.js +246 -0
  212. package/dist/web/api-config.html +520 -520
  213. package/dist/web/client.js +2917 -3791
  214. package/dist/web/components/p2p/index.js +234 -276
  215. package/dist/web/components/wallet-viem.mjs +118 -118
  216. package/dist/web/index.html +374 -373
  217. package/dist/web/manifest.json +20 -20
  218. package/dist/web/server.js +23 -775
  219. package/dist/web/style.css +4737 -4737
  220. package/dist/web/ui/message-renderer.js +373 -451
  221. package/dist/web/ui/step-timeline.js +255 -351
  222. package/package.json +160 -157
  223. package/scripts/build-cli.js +216 -0
  224. package/scripts/build-web.ts +125 -0
  225. package/scripts/postinstall.js +153 -0
  226. package/bin/bolloon-daemon.sh +0 -207
  227. package/bin/ipfs +0 -0
  228. package/dist/agents/chat-segmenter.js +0 -298
  229. package/dist/agents/judgment-protocol.js +0 -479
  230. package/dist/agents/parse-tool-call.js +0 -304
  231. package/dist/agents/peer-manifest-loader.js +0 -210
  232. package/dist/agents/react-loop.js +0 -120
  233. package/dist/agents/session-store.js +0 -171
  234. package/dist/agents/tool-registry.js +0 -136
  235. package/dist/bootstrap/chat-archiver.js +0 -276
  236. package/dist/bootstrap/memory-compressor.js +0 -170
  237. package/dist/bootstrap/persona-loader.js +0 -94
  238. package/dist/electron/config.js +0 -16
  239. package/dist/electron/config.js.map +0 -1
  240. package/dist/electron/dialogs.js +0 -71
  241. package/dist/electron/dialogs.js.map +0 -1
  242. package/dist/electron/first-run.js +0 -129
  243. package/dist/electron/first-run.js.map +0 -1
  244. package/dist/electron/ipc.js +0 -16
  245. package/dist/electron/ipc.js.map +0 -1
  246. package/dist/electron/logger.js +0 -77
  247. package/dist/electron/logger.js.map +0 -1
  248. package/dist/electron/main.js +0 -60
  249. package/dist/electron/main.js.map +0 -1
  250. package/dist/electron/menu.js +0 -140
  251. package/dist/electron/menu.js.map +0 -1
  252. package/dist/electron/paths.js +0 -33
  253. package/dist/electron/paths.js.map +0 -1
  254. package/dist/electron/server.js +0 -74
  255. package/dist/electron/server.js.map +0 -1
  256. package/dist/electron/tray.js +0 -73
  257. package/dist/electron/tray.js.map +0 -1
  258. package/dist/electron/window.js +0 -69
  259. package/dist/electron/window.js.map +0 -1
  260. package/dist/network/local-inbox-bus.js +0 -73
  261. package/dist/network/p2p-outbox.js +0 -161
  262. package/dist/network/peer-fs.js +0 -420
  263. package/dist/network/peer-resource-bridge.js +0 -216
  264. package/dist/web/components/p2p/P2PModal.js +0 -188
  265. package/dist/web/components/p2p/p2p-modal.js +0 -664
  266. package/dist/web/components/p2p/p2p-tools.js +0 -248
  267. package/dist/web/input-validator.js +0 -103
  268. package/dist/web/util/safe-name.js +0 -32
@@ -1,375 +1,279 @@
1
- /**
2
- * step-timeline.ts — 气泡内 4 状态步骤条 (2026-06-15)
3
- *
4
- * 职责 (单一):
5
- * - 给每条 AI 消息挂一个可折叠/展开的步骤条
6
- * - 状态机: idle ○ / active ● / done 🟢 / error 🔴
7
- * - 摘要条 + 详情区共用同一份 Step[] 数据
8
- * - 流式期间在 streaming 元素内, finalize 时搬到正式 message
9
- * - 折叠状态按 channel 持久化 (localStorage)
10
- *
11
- * 状态 (模块私有, 每个 message 一个实例):
12
- * - steps: Step[]
13
- * - currentIndex: number
14
- * - expanded: boolean (受 localStorage 影响)
15
- *
16
- * 输入 (从 message-renderer 调用):
17
- * - createEmptyStepTimeline(channelId) -> HTMLElement
18
- * - mountStepTimeline(messageEl, channelId) // 已有 message 上挂 timeline
19
- * - pushStepToTimeline(timelineEl, type, data) // 订阅 step_start/done/error
20
- * - migrateStepTimeline(fromEl, toEl) // finalize 时搬家
21
- *
22
- * 输出 (DOM):
23
- * - .step-timeline / .step-timeline-summary / .step-timeline-body
24
- * - .step-dot (摘要条圆点) / .step-timeline-node (详情区节点)
25
- */
26
1
  const MAX_VISIBLE_NODES = 8;
27
- const STORAGE_PREFIX = 'bolloon.stepTimeline.expanded.';
28
- const stateMap = new WeakMap();
29
- // ---------------------------------------------------------------------------
30
- // 工具: 持久化
31
- // ---------------------------------------------------------------------------
2
+ const STORAGE_PREFIX = "bolloon.stepTimeline.expanded.";
3
+ const stateMap = /* @__PURE__ */ new WeakMap();
32
4
  function readExpanded(channelId) {
33
- if (!channelId)
34
- return false;
35
- try {
36
- return localStorage.getItem(STORAGE_PREFIX + channelId) === '1';
37
- }
38
- catch {
39
- return false;
40
- }
5
+ if (!channelId) return false;
6
+ try {
7
+ return localStorage.getItem(STORAGE_PREFIX + channelId) === "1";
8
+ } catch {
9
+ return false;
10
+ }
41
11
  }
42
12
  function writeExpanded(channelId, expanded) {
43
- if (!channelId)
44
- return;
45
- try {
46
- localStorage.setItem(STORAGE_PREFIX + channelId, expanded ? '1' : '0');
47
- }
48
- catch { /* localStorage 写失败 (隐私模式), 静默 */ }
13
+ if (!channelId) return;
14
+ try {
15
+ localStorage.setItem(STORAGE_PREFIX + channelId, expanded ? "1" : "0");
16
+ } catch {
17
+ }
49
18
  }
50
- // ---------------------------------------------------------------------------
51
- // 工具: 摘要条 title
52
- // ---------------------------------------------------------------------------
53
19
  function computeTitle(steps) {
54
- if (steps.length === 0)
55
- return '执行步骤';
56
- // 优先 active, 其次最后一个 error
57
- const activeIdx = steps.findIndex(s => s.status === 'active');
58
- if (activeIdx >= 0)
59
- return `● 执行中 · ${steps[activeIdx].name}`;
60
- const lastErrorIdx = (() => {
61
- for (let i = steps.length - 1; i >= 0; i--) {
62
- if (steps[i].status === 'error')
63
- return i;
64
- }
65
- return -1;
66
- })();
67
- if (lastErrorIdx >= 0)
68
- return `✗ 失败 · ${steps[lastErrorIdx].name}`;
69
- return `✓ 已完成 · ${steps.length} 步`;
20
+ if (steps.length === 0) return "\u6267\u884C\u6B65\u9AA4";
21
+ const activeIdx = steps.findIndex((s) => s.status === "active");
22
+ if (activeIdx >= 0) return `\u25CF \u6267\u884C\u4E2D \xB7 ${steps[activeIdx].name}`;
23
+ const lastErrorIdx = (() => {
24
+ for (let i = steps.length - 1; i >= 0; i--) {
25
+ if (steps[i].status === "error") return i;
26
+ }
27
+ return -1;
28
+ })();
29
+ if (lastErrorIdx >= 0) return `\u2717 \u5931\u8D25 \xB7 ${steps[lastErrorIdx].name}`;
30
+ return `\u2713 \u5DF2\u5B8C\u6210 \xB7 ${steps.length} \u6B65`;
70
31
  }
71
- // ---------------------------------------------------------------------------
72
- // 核心: 渲染 (摘要条 + 详情区, 共用 steps 状态)
73
- // ---------------------------------------------------------------------------
74
32
  function render(timelineEl) {
75
- const state = stateMap.get(timelineEl);
76
- if (!state)
77
- return;
78
- const { steps, expanded, showAll } = state;
79
- const titleEl = timelineEl.querySelector('[data-current-tool]');
80
- if (titleEl)
81
- titleEl.textContent = computeTitle(steps);
82
- // 摘要条 dots
83
- const dotsEl = timelineEl.querySelector('[data-dots]');
84
- if (dotsEl) {
85
- // diff: 复用现有 dot, 多余 remove, 缺少 append
86
- const existing = Array.from(dotsEl.children);
87
- for (let i = 0; i < steps.length; i++) {
88
- const step = steps[i];
89
- let dot = existing[i];
90
- if (!dot) {
91
- dot = document.createElement('span');
92
- dot.className = 'step-dot';
93
- dot.setAttribute('data-index', String(i));
94
- dotsEl.appendChild(dot);
95
- }
96
- dot.setAttribute('data-status', step.status);
97
- dot.setAttribute('title', `${step.name} · ${step.status}`);
98
- }
99
- while (dotsEl.children.length > steps.length) {
100
- dotsEl.removeChild(dotsEl.lastChild);
101
- }
33
+ const state = stateMap.get(timelineEl);
34
+ if (!state) return;
35
+ const { steps, expanded, showAll } = state;
36
+ const titleEl = timelineEl.querySelector("[data-current-tool]");
37
+ if (titleEl) titleEl.textContent = computeTitle(steps);
38
+ const dotsEl = timelineEl.querySelector("[data-dots]");
39
+ if (dotsEl) {
40
+ const existing = Array.from(dotsEl.children);
41
+ for (let i = 0; i < steps.length; i++) {
42
+ const step = steps[i];
43
+ let dot = existing[i];
44
+ if (!dot) {
45
+ dot = document.createElement("span");
46
+ dot.className = "step-dot";
47
+ dot.setAttribute("data-index", String(i));
48
+ dotsEl.appendChild(dot);
49
+ }
50
+ dot.setAttribute("data-status", step.status);
51
+ dot.setAttribute("title", `${step.name} \xB7 ${step.status}`);
102
52
  }
103
- // 详情区 list
104
- const listEl = timelineEl.querySelector('[data-list]');
105
- if (listEl) {
106
- const hiddenCount = Math.max(0, steps.length - MAX_VISIBLE_NODES);
107
- const visibleSteps = showAll ? steps : steps.slice(-MAX_VISIBLE_NODES);
108
- const existing = Array.from(listEl.children);
109
- let htmlIdx = 0;
110
- // "+N 更多" 折叠行 (仅在 showAll=false 且有 hidden 时)
111
- if (!showAll && hiddenCount > 0) {
112
- let moreBtn = existing[htmlIdx];
113
- if (!moreBtn || !moreBtn.classList.contains('step-timeline-more')) {
114
- moreBtn = document.createElement('li');
115
- moreBtn.className = 'step-timeline-more';
116
- moreBtn.setAttribute('role', 'button');
117
- moreBtn.textContent = `+ ${hiddenCount} 更多`;
118
- moreBtn.onclick = () => {
119
- state.showAll = true;
120
- render(timelineEl);
121
- };
122
- if (existing[htmlIdx] && existing[htmlIdx] !== moreBtn) {
123
- listEl.replaceChild(moreBtn, existing[htmlIdx]);
124
- }
125
- else {
126
- listEl.appendChild(moreBtn);
127
- }
128
- }
129
- else {
130
- moreBtn.textContent = `+ ${hiddenCount} 更多`;
131
- }
132
- htmlIdx++;
133
- }
134
- for (let i = 0; i < visibleSteps.length; i++) {
135
- const step = visibleSteps[i];
136
- const realIndex = showAll ? i : (steps.length - visibleSteps.length + i);
137
- let node = existing[htmlIdx];
138
- if (!node || !node.classList.contains('step-timeline-node')) {
139
- node = document.createElement('li');
140
- node.className = 'step-timeline-node';
141
- node.setAttribute('data-index', String(realIndex));
142
- const marker = document.createElement('span');
143
- marker.className = 'step-timeline-marker';
144
- const label = document.createElement('span');
145
- label.className = 'step-timeline-label';
146
- const args = document.createElement('span');
147
- args.className = 'step-timeline-args';
148
- node.appendChild(marker);
149
- node.appendChild(label);
150
- node.appendChild(args);
151
- if (existing[htmlIdx] && existing[htmlIdx] !== node) {
152
- listEl.replaceChild(node, existing[htmlIdx]);
153
- }
154
- else {
155
- listEl.appendChild(node);
156
- }
157
- }
158
- else {
159
- node.setAttribute('data-index', String(realIndex));
160
- }
161
- node.setAttribute('data-status', step.status);
162
- const label = node.querySelector('.step-timeline-label');
163
- if (label)
164
- label.textContent = step.name;
165
- const argsEl = node.querySelector('.step-timeline-args');
166
- if (argsEl) {
167
- const argStr = step.args && Object.keys(step.args).length > 0
168
- ? JSON.stringify(step.args).slice(0, 60)
169
- : '';
170
- argsEl.textContent = argStr;
171
- argsEl.style.display = argStr ? '' : 'none';
172
- }
173
- htmlIdx++;
174
- }
175
- // 清理多余 li
176
- while (listEl.children.length > htmlIdx) {
177
- listEl.removeChild(listEl.lastChild);
178
- }
179
- }
180
- // 折叠态: 空状态显示占位, 有节点就保持
181
- if (steps.length === 0) {
182
- timelineEl.setAttribute('data-empty', 'true');
53
+ while (dotsEl.children.length > steps.length) {
54
+ dotsEl.removeChild(dotsEl.lastChild);
183
55
  }
184
- else {
185
- timelineEl.removeAttribute('data-empty');
186
- }
187
- // 折叠 class
188
- const body = timelineEl.querySelector('[data-body]');
189
- if (body) {
190
- if (expanded) {
191
- body.style.maxHeight = body.scrollHeight + 'px';
192
- setTimeout(() => {
193
- // 动画结束后清掉, 让内容自适应 (step 进来时能撑开)
194
- if (state.expanded)
195
- body.style.maxHeight = '';
196
- }, 300);
56
+ }
57
+ const listEl = timelineEl.querySelector("[data-list]");
58
+ if (listEl) {
59
+ const hiddenCount = Math.max(0, steps.length - MAX_VISIBLE_NODES);
60
+ const visibleSteps = showAll ? steps : steps.slice(-MAX_VISIBLE_NODES);
61
+ const existing = Array.from(listEl.children);
62
+ let htmlIdx = 0;
63
+ if (!showAll && hiddenCount > 0) {
64
+ let moreBtn = existing[htmlIdx];
65
+ if (!moreBtn || !moreBtn.classList.contains("step-timeline-more")) {
66
+ moreBtn = document.createElement("li");
67
+ moreBtn.className = "step-timeline-more";
68
+ moreBtn.setAttribute("role", "button");
69
+ moreBtn.textContent = `+ ${hiddenCount} \u66F4\u591A`;
70
+ moreBtn.onclick = () => {
71
+ state.showAll = true;
72
+ render(timelineEl);
73
+ };
74
+ if (existing[htmlIdx] && existing[htmlIdx] !== moreBtn) {
75
+ listEl.replaceChild(moreBtn, existing[htmlIdx]);
76
+ } else {
77
+ listEl.appendChild(moreBtn);
197
78
  }
198
- else {
199
- body.style.maxHeight = '0';
79
+ } else {
80
+ moreBtn.textContent = `+ ${hiddenCount} \u66F4\u591A`;
81
+ }
82
+ htmlIdx++;
83
+ }
84
+ for (let i = 0; i < visibleSteps.length; i++) {
85
+ const step = visibleSteps[i];
86
+ const realIndex = showAll ? i : steps.length - visibleSteps.length + i;
87
+ let node = existing[htmlIdx];
88
+ if (!node || !node.classList.contains("step-timeline-node")) {
89
+ node = document.createElement("li");
90
+ node.className = "step-timeline-node";
91
+ node.setAttribute("data-index", String(realIndex));
92
+ const marker = document.createElement("span");
93
+ marker.className = "step-timeline-marker";
94
+ const label2 = document.createElement("span");
95
+ label2.className = "step-timeline-label";
96
+ const args = document.createElement("span");
97
+ args.className = "step-timeline-args";
98
+ node.appendChild(marker);
99
+ node.appendChild(label2);
100
+ node.appendChild(args);
101
+ if (existing[htmlIdx] && existing[htmlIdx] !== node) {
102
+ listEl.replaceChild(node, existing[htmlIdx]);
103
+ } else {
104
+ listEl.appendChild(node);
200
105
  }
106
+ } else {
107
+ node.setAttribute("data-index", String(realIndex));
108
+ }
109
+ node.setAttribute("data-status", step.status);
110
+ const label = node.querySelector(".step-timeline-label");
111
+ if (label) label.textContent = step.name;
112
+ const argsEl = node.querySelector(".step-timeline-args");
113
+ if (argsEl) {
114
+ const argStr = step.args && Object.keys(step.args).length > 0 ? JSON.stringify(step.args).slice(0, 60) : "";
115
+ argsEl.textContent = argStr;
116
+ argsEl.style.display = argStr ? "" : "none";
117
+ }
118
+ htmlIdx++;
119
+ }
120
+ while (listEl.children.length > htmlIdx) {
121
+ listEl.removeChild(listEl.lastChild);
201
122
  }
202
- const arrow = timelineEl.querySelector('.step-timeline-arrow');
203
- if (arrow)
204
- arrow.style.transform = expanded ? 'rotate(180deg)' : 'rotate(0deg)';
205
- // 自动滚到底 (新节点进来时, 详情区跟到底)
206
- if (expanded && listEl) {
207
- listEl.scrollTop = listEl.scrollHeight;
123
+ }
124
+ if (steps.length === 0) {
125
+ timelineEl.setAttribute("data-empty", "true");
126
+ } else {
127
+ timelineEl.removeAttribute("data-empty");
128
+ }
129
+ const body = timelineEl.querySelector("[data-body]");
130
+ if (body) {
131
+ if (expanded) {
132
+ body.style.maxHeight = body.scrollHeight + "px";
133
+ setTimeout(() => {
134
+ if (state.expanded) body.style.maxHeight = "";
135
+ }, 300);
136
+ } else {
137
+ body.style.maxHeight = "0";
208
138
  }
139
+ }
140
+ const arrow = timelineEl.querySelector(".step-timeline-arrow");
141
+ if (arrow) arrow.style.transform = expanded ? "rotate(180deg)" : "rotate(0deg)";
142
+ if (expanded && listEl) {
143
+ listEl.scrollTop = listEl.scrollHeight;
144
+ }
209
145
  }
210
- // ---------------------------------------------------------------------------
211
- // 公共: 创建一个空 timeline (AI 消息创建时挂的占位)
212
- // ---------------------------------------------------------------------------
213
- export function createEmptyStepTimeline(channelId = null) {
214
- const root = document.createElement('div');
215
- root.className = 'step-timeline';
216
- root.setAttribute('data-step-timeline', '');
217
- root.setAttribute('data-empty', 'true');
218
- // 摘要条
219
- const summary = document.createElement('div');
220
- summary.className = 'step-timeline-summary';
221
- summary.setAttribute('data-summary', '');
222
- const titleSpan = document.createElement('span');
223
- titleSpan.className = 'step-timeline-title';
224
- const titleContent = document.createElement('span');
225
- titleContent.setAttribute('data-current-tool', '');
226
- titleContent.textContent = '执行步骤';
227
- titleSpan.appendChild(titleContent);
228
- const dots = document.createElement('div');
229
- dots.className = 'step-timeline-dots';
230
- dots.setAttribute('data-dots', '');
231
- const arrow = document.createElement('span');
232
- arrow.className = 'step-timeline-arrow';
233
- arrow.textContent = '▾';
234
- summary.appendChild(titleSpan);
235
- summary.appendChild(dots);
236
- summary.appendChild(arrow);
237
- summary.onclick = () => {
238
- const state = stateMap.get(root);
239
- if (!state)
240
- return;
241
- state.expanded = !state.expanded;
242
- writeExpanded(state.channelId, state.expanded);
243
- render(root);
244
- };
245
- // 详情区
246
- const body = document.createElement('div');
247
- body.className = 'step-timeline-body';
248
- body.setAttribute('data-body', '');
249
- const list = document.createElement('ul');
250
- list.className = 'step-timeline-list';
251
- list.setAttribute('data-list', '');
252
- body.appendChild(list);
253
- root.appendChild(summary);
254
- root.appendChild(body);
255
- // 初始折叠态
256
- const initialExpanded = readExpanded(channelId);
257
- stateMap.set(root, {
258
- steps: [],
259
- currentIndex: -1,
260
- expanded: initialExpanded,
261
- channelId: channelId || null,
262
- showAll: false,
263
- });
146
+ function createEmptyStepTimeline(channelId = null) {
147
+ const root = document.createElement("div");
148
+ root.className = "step-timeline";
149
+ root.setAttribute("data-step-timeline", "");
150
+ root.setAttribute("data-empty", "true");
151
+ const summary = document.createElement("div");
152
+ summary.className = "step-timeline-summary";
153
+ summary.setAttribute("data-summary", "");
154
+ const titleSpan = document.createElement("span");
155
+ titleSpan.className = "step-timeline-title";
156
+ const titleContent = document.createElement("span");
157
+ titleContent.setAttribute("data-current-tool", "");
158
+ titleContent.textContent = "\u6267\u884C\u6B65\u9AA4";
159
+ titleSpan.appendChild(titleContent);
160
+ const dots = document.createElement("div");
161
+ dots.className = "step-timeline-dots";
162
+ dots.setAttribute("data-dots", "");
163
+ const arrow = document.createElement("span");
164
+ arrow.className = "step-timeline-arrow";
165
+ arrow.textContent = "\u25BE";
166
+ summary.appendChild(titleSpan);
167
+ summary.appendChild(dots);
168
+ summary.appendChild(arrow);
169
+ summary.onclick = () => {
170
+ const state = stateMap.get(root);
171
+ if (!state) return;
172
+ state.expanded = !state.expanded;
173
+ writeExpanded(state.channelId, state.expanded);
264
174
  render(root);
265
- return root;
175
+ };
176
+ const body = document.createElement("div");
177
+ body.className = "step-timeline-body";
178
+ body.setAttribute("data-body", "");
179
+ const list = document.createElement("ul");
180
+ list.className = "step-timeline-list";
181
+ list.setAttribute("data-list", "");
182
+ body.appendChild(list);
183
+ root.appendChild(summary);
184
+ root.appendChild(body);
185
+ const initialExpanded = readExpanded(channelId);
186
+ stateMap.set(root, {
187
+ steps: [],
188
+ currentIndex: -1,
189
+ expanded: initialExpanded,
190
+ channelId: channelId || null,
191
+ showAll: false
192
+ });
193
+ render(root);
194
+ return root;
266
195
  }
267
- // ---------------------------------------------------------------------------
268
- // 公共: timeline 挂到 message 上 (初始化或补挂)
269
- // ---------------------------------------------------------------------------
270
- export function mountStepTimeline(messageEl, channelId) {
271
- // 已经挂过 复用
272
- const existing = messageEl.querySelector('[data-step-timeline]');
273
- if (existing) {
274
- const state = stateMap.get(existing);
275
- if (state && channelId && !state.channelId)
276
- state.channelId = channelId;
277
- return existing;
278
- }
279
- // 找插入点: .bubble 之后, .message-actions 之前
280
- const bubble = messageEl.querySelector('.bubble');
281
- const actions = messageEl.querySelector('.message-actions');
282
- const timeline = createEmptyStepTimeline(channelId);
283
- if (actions && actions.parentNode === messageEl) {
284
- messageEl.insertBefore(timeline, actions);
285
- }
286
- else if (bubble && bubble.parentNode === messageEl) {
287
- bubble.parentNode.insertBefore(timeline, bubble.nextSibling);
288
- }
289
- else {
290
- messageEl.appendChild(timeline);
291
- }
292
- return timeline;
196
+ function mountStepTimeline(messageEl, channelId) {
197
+ const existing = messageEl.querySelector("[data-step-timeline]");
198
+ if (existing) {
199
+ const state = stateMap.get(existing);
200
+ if (state && channelId && !state.channelId) state.channelId = channelId;
201
+ return existing;
202
+ }
203
+ const bubble = messageEl.querySelector(".bubble");
204
+ const actions = messageEl.querySelector(".message-actions");
205
+ const timeline = createEmptyStepTimeline(channelId);
206
+ if (actions && actions.parentNode === messageEl) {
207
+ messageEl.insertBefore(timeline, actions);
208
+ } else if (bubble && bubble.parentNode === messageEl) {
209
+ bubble.parentNode.insertBefore(timeline, bubble.nextSibling);
210
+ } else {
211
+ messageEl.appendChild(timeline);
212
+ }
213
+ return timeline;
293
214
  }
294
- // ---------------------------------------------------------------------------
295
- // 公共: 推一个 step 事件 (start/done/error)
296
- // ---------------------------------------------------------------------------
297
- export function pushStepToTimeline(timelineEl, eventType, data) {
298
- const state = stateMap.get(timelineEl);
299
- if (!state)
300
- return;
301
- const { steps } = state;
302
- if (eventType === 'step_start') {
303
- state.currentIndex = steps.length;
304
- steps.push({
305
- name: data.tool,
306
- status: 'active',
307
- args: data.args,
308
- eventType,
309
- });
310
- }
311
- else {
312
- // step_done / step_error — 找当前 active 节点关闭
313
- // 优先匹配 currentIndex, 其次回退到最后一个 active
314
- let targetIdx = state.currentIndex;
315
- if (targetIdx < 0 || !steps[targetIdx] || steps[targetIdx].name !== data.tool) {
316
- // 兜底: 找最后一个同名 active
317
- for (let i = steps.length - 1; i >= 0; i--) {
318
- if (steps[i].name === data.tool && steps[i].status === 'active') {
319
- targetIdx = i;
320
- break;
321
- }
322
- }
323
- }
324
- if (targetIdx < 0) {
325
- // 没有 active 节点匹配, 当作新 error 节点
326
- targetIdx = steps.length;
327
- steps.push({ name: data.tool, status: 'error', eventType });
328
- }
329
- if (eventType === 'step_done') {
330
- steps[targetIdx].status = data.success === false ? 'error' : 'done';
331
- if (data.output)
332
- steps[targetIdx].output = data.output;
333
- if (data.error)
334
- steps[targetIdx].error = data.error;
335
- }
336
- else {
337
- steps[targetIdx].status = 'error';
338
- if (data.error)
339
- steps[targetIdx].error = data.error;
215
+ function pushStepToTimeline(timelineEl, eventType, data) {
216
+ const state = stateMap.get(timelineEl);
217
+ if (!state) return;
218
+ const { steps } = state;
219
+ if (eventType === "step_start") {
220
+ state.currentIndex = steps.length;
221
+ steps.push({
222
+ name: data.tool,
223
+ status: "active",
224
+ args: data.args,
225
+ eventType
226
+ });
227
+ } else {
228
+ let targetIdx = state.currentIndex;
229
+ if (targetIdx < 0 || !steps[targetIdx] || steps[targetIdx].name !== data.tool) {
230
+ for (let i = steps.length - 1; i >= 0; i--) {
231
+ if (steps[i].name === data.tool && steps[i].status === "active") {
232
+ targetIdx = i;
233
+ break;
340
234
  }
341
- steps[targetIdx].eventType = eventType;
342
- state.currentIndex = -1;
235
+ }
343
236
  }
344
- render(timelineEl);
345
- }
346
- // ---------------------------------------------------------------------------
347
- // 公共: finalize 时把 timeline 元素从 from 搬到 to (避免重渲)
348
- // 接受 timelineEl 本身或 fromEl 内部的 timeline
349
- // ---------------------------------------------------------------------------
350
- export function migrateStepTimeline(fromEl, toEl) {
351
- const fromTimeline = fromEl.querySelector('[data-step-timeline]');
352
- const toTimeline = toEl.querySelector('[data-step-timeline]');
353
- if (!fromTimeline)
354
- return;
355
- // 移除 toEl 的占位, 搬 fromEl 的过来
356
- if (toTimeline && toTimeline !== fromTimeline) {
357
- toTimeline.remove();
358
- }
359
- if (toTimeline !== fromTimeline) {
360
- toEl.appendChild(fromTimeline);
237
+ if (targetIdx < 0) {
238
+ targetIdx = steps.length;
239
+ steps.push({ name: data.tool, status: "error", eventType });
361
240
  }
362
- // 重渲一次 (channelId 注入)
363
- const state = stateMap.get(fromTimeline);
364
- if (state) {
365
- render(fromTimeline);
241
+ if (eventType === "step_done") {
242
+ steps[targetIdx].status = data.success === false ? "error" : "done";
243
+ if (data.output) steps[targetIdx].output = data.output;
244
+ if (data.error) steps[targetIdx].error = data.error;
245
+ } else {
246
+ steps[targetIdx].status = "error";
247
+ if (data.error) steps[targetIdx].error = data.error;
366
248
  }
249
+ steps[targetIdx].eventType = eventType;
250
+ state.currentIndex = -1;
251
+ }
252
+ render(timelineEl);
253
+ }
254
+ function migrateStepTimeline(fromEl, toEl) {
255
+ const fromTimeline = fromEl.querySelector("[data-step-timeline]");
256
+ const toTimeline = toEl.querySelector("[data-step-timeline]");
257
+ if (!fromTimeline) return;
258
+ if (toTimeline && toTimeline !== fromTimeline) {
259
+ toTimeline.remove();
260
+ }
261
+ if (toTimeline !== fromTimeline) {
262
+ toEl.appendChild(fromTimeline);
263
+ }
264
+ const state = stateMap.get(fromTimeline);
265
+ if (state) {
266
+ render(fromTimeline);
267
+ }
367
268
  }
368
- // ---------------------------------------------------------------------------
369
- // 公共: 拿到一个 message 内的 timeline (无则返回 null)
370
- // ---------------------------------------------------------------------------
371
- export function getStepTimeline(messageEl) {
372
- if (!messageEl)
373
- return null;
374
- return messageEl.querySelector('[data-step-timeline]');
269
+ function getStepTimeline(messageEl) {
270
+ if (!messageEl) return null;
271
+ return messageEl.querySelector("[data-step-timeline]");
375
272
  }
273
+ export {
274
+ createEmptyStepTimeline,
275
+ getStepTimeline,
276
+ migrateStepTimeline,
277
+ mountStepTimeline,
278
+ pushStepToTimeline
279
+ };