@bolloon/bolloon-agent 0.4.5 → 0.4.6
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/README.md +135 -135
- package/bin/bolloon-cli.cjs +183 -183
- package/bin/bolloon.cjs +0 -0
- package/bin/bolloon.js +157 -0
- package/dist/agents/constraint-layer.js +19 -19
- package/dist/agents/judgment-protocol.js +14 -14
- package/dist/agents/pi-sdk-tools.js +62 -45
- package/dist/agents/pi-sdk-types.js +12 -12
- package/dist/agents/pi-sdk.js +103 -90
- package/dist/agents/skill-organizer.js +19 -19
- package/dist/agents/skill-writer.js +15 -3
- package/dist/agents/workflow-pivot-loop.js +13 -3
- package/dist/bollharness/src/scripts/deploy-guard.js +8 -8
- package/dist/bollharness-integration/context-router-judgment.js +4 -4
- package/dist/bollharness-integration/context-router.js +292 -292
- package/dist/bollharness-integration/gate-state-machine.js +13 -13
- package/dist/bollharness-integration/llm-judgment-engine.js +134 -134
- package/dist/bollharness-integration/skill-adapter.js +21 -21
- package/dist/bootstrap/chat-archiver.js +14 -14
- package/dist/bootstrap/context-os.js +24 -24
- package/dist/bootstrap/memory-compressor.js +14 -14
- package/dist/cli/ink-app.js +18 -0
- package/dist/cli-entry.js +36 -36
- package/dist/context-compaction/auto-compact.js +7 -7
- package/dist/electron/first-run.js +54 -54
- package/dist/electron-build/electron/first-run.js +54 -54
- package/dist/electron-preload.js.map +1 -1
- package/dist/electron.js.map +1 -1
- package/dist/index.js +222 -138
- package/dist/llm/llm-judgment-client.js +102 -102
- package/dist/llm/pi-ai.js +48 -48
- package/dist/llm/system-prompt/layers/channel/human-async.md +41 -41
- package/dist/llm/system-prompt/layers/channel/local.md +14 -14
- package/dist/llm/system-prompt/layers/channel/p2p-agent.md +18 -18
- package/dist/llm/system-prompt/layers/channel/p2p-peer-sync.md +51 -51
- package/dist/llm/system-prompt/layers/channel/p2p-proactive.md +43 -43
- package/dist/llm/system-prompt/layers/channel/p2p-visitor.md +19 -19
- package/dist/llm/system-prompt/layers/channel/session-handoff.md +61 -61
- package/dist/llm/system-prompt/layers/core/artifacts_storage.md +89 -89
- package/dist/llm/system-prompt/layers/core/evenhandedness.md +21 -21
- package/dist/llm/system-prompt/layers/core/external-engagement.md +73 -73
- package/dist/llm/system-prompt/layers/core/hibs_reminders.md +15 -15
- package/dist/llm/system-prompt/layers/core/identity.md +48 -48
- package/dist/llm/system-prompt/layers/core/knowledge.md +17 -17
- package/dist/llm/system-prompt/layers/core/memory_system.md +12 -12
- package/dist/llm/system-prompt/layers/core/network_filesystem.md +28 -28
- package/dist/llm/system-prompt/layers/core/refusal.md +37 -37
- package/dist/llm/system-prompt/layers/core/tone.md +31 -31
- package/dist/llm/system-prompt/layers/core/tools.thin.md +13 -13
- package/dist/llm/system-prompt/layers/core/wellbeing.md +41 -41
- package/dist/llm/system-prompt/layers/role/architect.md +20 -20
- package/dist/llm/system-prompt/layers/role/expert.md +19 -19
- package/dist/llm/system-prompt/layers/role/implementer.md +15 -15
- package/dist/llm/system-prompt/layers/role/security.md +15 -15
- package/dist/llm/system-prompt/layers/tool/artifacts.md +72 -72
- package/dist/llm/system-prompt/layers/tool/bash.md +25 -25
- package/dist/llm/system-prompt/layers/tool/goal_handoff.md +77 -77
- package/dist/llm/system-prompt/layers/tool/hibs_api.md +171 -171
- package/dist/llm/system-prompt/layers/tool/image_search.md +70 -70
- package/dist/llm/system-prompt/layers/tool/manifest.md +89 -89
- package/dist/llm/system-prompt/layers/tool/mcp_apps.md +53 -53
- package/dist/llm/system-prompt/layers/tool/p2p_request.md +61 -61
- package/dist/llm/system-prompt/layers/tool/web_search.md +83 -83
- package/dist/llm/tool-manifest/ask_user_input.js +1 -1
- package/dist/llm/tool-manifest/bash.js +3 -3
- package/dist/llm/tool-manifest/create_file.js +4 -4
- package/dist/llm/tool-manifest/fetch_sports_data.js +3 -3
- package/dist/llm/tool-manifest/image_search.js +2 -2
- package/dist/llm/tool-manifest/index.js +0 -59
- package/dist/llm/tool-manifest/mcp.js +2 -2
- package/dist/llm/tool-manifest/message_compose.js +3 -3
- package/dist/llm/tool-manifest/places.js +2 -2
- package/dist/llm/tool-manifest/present_files.js +1 -1
- package/dist/llm/tool-manifest/recipe.js +2 -2
- package/dist/llm/tool-manifest/str_replace.js +5 -5
- package/dist/llm/tool-manifest/view.js +3 -3
- package/dist/llm/tool-manifest/weather.js +4 -4
- package/dist/llm/tool-manifest/web.js +4 -4
- package/dist/orbitdb/agent-tools.js +139 -0
- package/dist/orbitdb/kanban-store.js +259 -0
- package/dist/orbitdb/task-store.js +112 -0
- package/dist/pi-ecosystem-colony/index.js +365 -0
- package/dist/pi-ecosystem-judgment/causal-judge.js +51 -51
- package/dist/pi-ecosystem-judgment/distill-prompt.js +42 -42
- package/dist/pi-ecosystem-judgment/distillation.js +14 -14
- package/dist/pi-ecosystem-judgment/monitor-gate.js +28 -28
- package/dist/pi-ecosystem-judgment/value-injection.js +102 -102
- package/dist/security/context-router-tool.js +15 -15
- package/dist/security/tool-gate.js +2 -0
- package/dist/social/ant-colony/AdaptiveHeartbeat.js +101 -0
- package/dist/social/ant-colony/PheromoneEngine.js +227 -0
- package/dist/social/ant-colony/index.js +6 -0
- package/dist/social/ant-colony/types.js +24 -0
- package/dist/storage/trajectory.js +101 -0
- package/dist/test/ai-judgment-test.js +80 -0
- package/dist/test/bollharness-integration.test.js +318 -0
- package/dist/test/channel-agent-multi-dialogue.js +205 -0
- package/dist/test/channel-heartbeat-agent-test.js +201 -0
- package/dist/test/constraint-layer.test.js +164 -0
- package/dist/test/diap-identity-test.js +172 -0
- package/dist/test/diap-quick-test.js +62 -0
- package/dist/test/global-shared-context.test.js +315 -0
- package/dist/test/harness-judgment-injection.test.js +246 -0
- package/dist/test/harness-workflow-integrator-test.js +228 -0
- package/dist/test/human-value-store.test.js +243 -0
- package/dist/test/hybrid-integration-test.js +118 -0
- package/dist/test/hybrid-messenger-verify.js +55 -0
- package/dist/test/iroh-bistream-debug.js +38 -0
- package/dist/test/iroh-communication.test.js +66 -0
- package/dist/test/iroh-debug-test.js +57 -0
- package/dist/test/iroh-diap-test.js +71 -0
- package/dist/test/iroh-direct-connect.js +55 -0
- package/dist/test/iroh-e2e-fixed.js +89 -0
- package/dist/test/iroh-e2e-same-process.js +63 -0
- package/dist/test/iroh-e2e.js +66 -0
- package/dist/test/iroh-final-e2e.js +72 -0
- package/dist/test/iroh-relay-test.js +37 -0
- package/dist/test/iroh-simple-test.js +41 -0
- package/dist/test/iroh-transport-verify.js +54 -0
- package/dist/test/iroh-transport.test.js +37 -0
- package/dist/test/iroh-two-nodes.js +70 -0
- package/dist/test/iroh-verify.js +44 -0
- package/dist/test/judgment-decision.test.js +219 -0
- package/dist/test/llm-judgment-integration.test.js +220 -0
- package/dist/test/p2p-agent-complex-dialogue.js +385 -0
- package/dist/test/p2p-agent-dialogue.js +341 -0
- package/dist/test/p2p-agent-full-bidirectional.js +510 -0
- package/dist/test/p2p-agent-harness-flow.js +437 -0
- package/dist/test/p2p-agent-harness-single.js +143 -0
- package/dist/test/p2p-ai-dialogue-test.js +318 -0
- package/dist/test/p2p-cid-connect-test.js +195 -0
- package/dist/test/p2p-connect-receiver.js +69 -0
- package/dist/test/p2p-doc-transfer.js +110 -0
- package/dist/test/p2p-identity-page-test.js +77 -0
- package/dist/test/p2p-iroh-test.js +171 -0
- package/dist/test/p2p-minimal-test.js +241 -0
- package/dist/test/p2p-node-1.js +148 -0
- package/dist/test/p2p-node-2.js +148 -0
- package/dist/test/p2p-server.js +281 -0
- package/dist/test/p2p-two-nodes-test.js +438 -0
- package/dist/test/pi-sdk.test.js +44 -0
- package/dist/test/set-persona.js +40 -0
- package/dist/test/simple.test.js +9 -0
- package/dist/test/storage-integration.test.js +150 -0
- package/dist/test/subagent-manager.test.js +276 -0
- package/dist/test/test-gate-flow.test.js +81 -0
- package/dist/test/workflow-engine.test.js +87 -0
- package/dist/test/workflow-pivot-loop.test.js +246 -0
- package/dist/web/api-config.html +779 -779
- package/dist/web/client-hearth.js +10 -10
- package/dist/web/components/wallet-viem.mjs +118 -118
- package/dist/web/index.html +472 -472
- package/dist/web/manifest.json +20 -20
- package/dist/web/server-storage.js +29 -0
- package/dist/web/server.js +30 -14
- package/dist/web/style.css +4946 -4946
- package/package.json +195 -195
- package/scripts/build-cli.js +215 -215
- package/scripts/build-web.ts +130 -130
- package/scripts/postinstall.js +152 -152
- package/bin/bolloon-daemon.sh +0 -207
- package/dist/constraint-runtime/reference_data/archive_surface_snapshot.json +0 -63
- package/dist/constraint-runtime/reference_data/commands_snapshot.json +0 -1037
- package/dist/constraint-runtime/reference_data/subsystems/assistant.json +0 -8
- package/dist/constraint-runtime/reference_data/subsystems/bootstrap.json +0 -8
- package/dist/constraint-runtime/reference_data/subsystems/bridge.json +0 -32
- package/dist/constraint-runtime/reference_data/subsystems/buddy.json +0 -13
- package/dist/constraint-runtime/reference_data/subsystems/cli.json +0 -26
- package/dist/constraint-runtime/reference_data/subsystems/components.json +0 -32
- package/dist/constraint-runtime/reference_data/subsystems/constants.json +0 -28
- package/dist/constraint-runtime/reference_data/subsystems/coordinator.json +0 -8
- package/dist/constraint-runtime/reference_data/subsystems/entrypoints.json +0 -15
- package/dist/constraint-runtime/reference_data/subsystems/hooks.json +0 -32
- package/dist/constraint-runtime/reference_data/subsystems/keybindings.json +0 -21
- package/dist/constraint-runtime/reference_data/subsystems/memdir.json +0 -15
- package/dist/constraint-runtime/reference_data/subsystems/migrations.json +0 -18
- package/dist/constraint-runtime/reference_data/subsystems/moreright.json +0 -8
- package/dist/constraint-runtime/reference_data/subsystems/native_ts.json +0 -11
- package/dist/constraint-runtime/reference_data/subsystems/outputStyles.json +0 -8
- package/dist/constraint-runtime/reference_data/subsystems/plugins.json +0 -9
- package/dist/constraint-runtime/reference_data/subsystems/remote.json +0 -11
- package/dist/constraint-runtime/reference_data/subsystems/schemas.json +0 -8
- package/dist/constraint-runtime/reference_data/subsystems/screens.json +0 -10
- package/dist/constraint-runtime/reference_data/subsystems/server.json +0 -10
- package/dist/constraint-runtime/reference_data/subsystems/services.json +0 -32
- package/dist/constraint-runtime/reference_data/subsystems/skills.json +0 -27
- package/dist/constraint-runtime/reference_data/subsystems/state.json +0 -13
- package/dist/constraint-runtime/reference_data/subsystems/types.json +0 -18
- package/dist/constraint-runtime/reference_data/subsystems/upstreamproxy.json +0 -9
- package/dist/constraint-runtime/reference_data/subsystems/utils.json +0 -32
- package/dist/constraint-runtime/reference_data/subsystems/vim.json +0 -12
- package/dist/constraint-runtime/reference_data/subsystems/voice.json +0 -8
- package/dist/constraint-runtime/reference_data/tools_snapshot.json +0 -922
- package/dist/electron/config.js.map +0 -1
- package/dist/electron/dialogs.js.map +0 -1
- package/dist/electron/first-run.js.map +0 -1
- package/dist/electron/ipc.js.map +0 -1
- package/dist/electron/logger.js.map +0 -1
- package/dist/electron/main.js.map +0 -1
- package/dist/electron/menu.js.map +0 -1
- package/dist/electron/paths.js.map +0 -1
- package/dist/electron/server.js.map +0 -1
- package/dist/electron/tray.js.map +0 -1
- package/dist/electron/window.js.map +0 -1
- package/dist/utils/auto-update.js.map +0 -1
- package/dist/web/client.js.map +0 -7
package/dist/web/client.js.map
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/web/ui/step-timeline.ts", "../../src/agents/parse-tool-call.ts", "../../src/agents/chat-segmenter.ts", "../../src/web/ui/message-renderer.ts", "../../src/web/util/safe-name.ts", "../../src/web/client.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * step-timeline.ts \u2014 \u6C14\u6CE1\u5185 4 \u72B6\u6001\u6B65\u9AA4\u6761 (2026-06-15)\n *\n * \u804C\u8D23 (\u5355\u4E00):\n * - \u7ED9\u6BCF\u6761 AI \u6D88\u606F\u6302\u4E00\u4E2A\u53EF\u6298\u53E0/\u5C55\u5F00\u7684\u6B65\u9AA4\u6761\n * - \u72B6\u6001\u673A: idle \u25CB / active \u25CF / done \uD83D\uDFE2 / error \uD83D\uDD34\n * - \u6458\u8981\u6761 + \u8BE6\u60C5\u533A\u5171\u7528\u540C\u4E00\u4EFD Step[] \u6570\u636E\n * - \u6D41\u5F0F\u671F\u95F4\u5728 streaming \u5143\u7D20\u5185, finalize \u65F6\u642C\u5230\u6B63\u5F0F message\n * - \u6298\u53E0\u72B6\u6001\u6309 channel \u6301\u4E45\u5316 (localStorage)\n *\n * \u72B6\u6001 (\u6A21\u5757\u79C1\u6709, \u6BCF\u4E2A message \u4E00\u4E2A\u5B9E\u4F8B):\n * - steps: Step[]\n * - currentIndex: number\n * - expanded: boolean (\u53D7 localStorage \u5F71\u54CD)\n *\n * \u8F93\u5165 (\u4ECE message-renderer \u8C03\u7528):\n * - createEmptyStepTimeline(channelId) -> HTMLElement\n * - mountStepTimeline(messageEl, channelId) // \u5DF2\u6709 message \u4E0A\u6302 timeline\n * - pushStepToTimeline(timelineEl, type, data) // \u8BA2\u9605 step_start/done/error\n * - migrateStepTimeline(fromEl, toEl) // finalize \u65F6\u642C\u5BB6\n *\n * \u8F93\u51FA (DOM):\n * - .step-timeline / .step-timeline-summary / .step-timeline-body\n * - .step-dot (\u6458\u8981\u6761\u5706\u70B9) / .step-timeline-node (\u8BE6\u60C5\u533A\u8282\u70B9)\n */\n\n// ---------------------------------------------------------------------------\n// \u7C7B\u578B\n// ---------------------------------------------------------------------------\nexport type StepStatus = 'idle' | 'active' | 'done' | 'error';\nexport interface Step {\n name: string;\n status: StepStatus;\n args?: Record<string, unknown>;\n output?: string;\n error?: string;\n /** \u8BE5 step \u5BF9\u5E94\u7684\u4E8B\u4EF6 type (\u4F9B\u8C03\u8BD5/\u6269\u5C55) */\n eventType?: 'step_start' | 'step_done' | 'step_error';\n}\n\nconst MAX_VISIBLE_NODES = 8;\nconst STORAGE_PREFIX = 'bolloon.stepTimeline.expanded.';\n\n// ---------------------------------------------------------------------------\n// \u6A21\u5757\u79C1\u6709\u72B6\u6001 (\u6309 timeline \u5143\u7D20\u5B9E\u4F8B\u5316)\n// \u7528 WeakMap \u907F\u514D\u5FAA\u73AF\u5F15\u7528\u6CC4\u6F0F\n// ---------------------------------------------------------------------------\ninterface TimelineState {\n steps: Step[];\n currentIndex: number;\n expanded: boolean;\n channelId: string | null;\n showAll: boolean; // \"+N \u66F4\u591A\" \u5C55\u5F00\n}\nconst stateMap = new WeakMap<HTMLElement, TimelineState>();\n\n// ---------------------------------------------------------------------------\n// \u5DE5\u5177: \u6301\u4E45\u5316\n// ---------------------------------------------------------------------------\nfunction readExpanded(channelId: string | null): boolean {\n if (!channelId) return false;\n try {\n return localStorage.getItem(STORAGE_PREFIX + channelId) === '1';\n } catch { return false; }\n}\nfunction writeExpanded(channelId: string | null, expanded: boolean): void {\n if (!channelId) return;\n try {\n localStorage.setItem(STORAGE_PREFIX + channelId, expanded ? '1' : '0');\n } catch { /* localStorage \u5199\u5931\u8D25 (\u9690\u79C1\u6A21\u5F0F), \u9759\u9ED8 */ }\n}\n\n// ---------------------------------------------------------------------------\n// \u5DE5\u5177: \u6458\u8981\u6761 title\n// ---------------------------------------------------------------------------\nfunction computeTitle(steps: Step[]): string {\n if (steps.length === 0) return '\u6267\u884C\u6B65\u9AA4';\n // \u4F18\u5148 active, \u5176\u6B21\u6700\u540E\u4E00\u4E2A error\n const activeIdx = steps.findIndex(s => s.status === 'active');\n if (activeIdx >= 0) return `\u25CF \u6267\u884C\u4E2D \u00B7 ${steps[activeIdx].name}`;\n const lastErrorIdx = (() => {\n for (let i = steps.length - 1; i >= 0; i--) {\n if (steps[i].status === 'error') return i;\n }\n return -1;\n })();\n if (lastErrorIdx >= 0) return `\u2717 \u5931\u8D25 \u00B7 ${steps[lastErrorIdx].name}`;\n return `\u2713 \u5DF2\u5B8C\u6210 \u00B7 ${steps.length} \u6B65`;\n}\n\n// ---------------------------------------------------------------------------\n// \u6838\u5FC3: \u6E32\u67D3 (\u6458\u8981\u6761 + \u8BE6\u60C5\u533A, \u5171\u7528 steps \u72B6\u6001)\n// ---------------------------------------------------------------------------\nfunction render(timelineEl: HTMLElement): void {\n const state = stateMap.get(timelineEl);\n if (!state) return;\n const { steps, expanded, showAll } = state;\n\n const titleEl = timelineEl.querySelector('[data-current-tool]') as HTMLElement | null;\n if (titleEl) titleEl.textContent = computeTitle(steps);\n\n // \u6458\u8981\u6761 dots\n const dotsEl = timelineEl.querySelector('[data-dots]') as HTMLElement | null;\n if (dotsEl) {\n // diff: \u590D\u7528\u73B0\u6709 dot, \u591A\u4F59 remove, \u7F3A\u5C11 append\n const existing = Array.from(dotsEl.children) as HTMLElement[];\n for (let i = 0; i < steps.length; i++) {\n const step = steps[i];\n let dot = existing[i];\n if (!dot) {\n dot = document.createElement('span');\n dot.className = 'step-dot';\n dot.setAttribute('data-index', String(i));\n dotsEl.appendChild(dot);\n }\n dot.setAttribute('data-status', step.status);\n dot.setAttribute('title', `${step.name} \u00B7 ${step.status}`);\n }\n while (dotsEl.children.length > steps.length) {\n dotsEl.removeChild(dotsEl.lastChild!);\n }\n }\n\n // \u8BE6\u60C5\u533A list\n const listEl = timelineEl.querySelector('[data-list]') as HTMLElement | null;\n if (listEl) {\n const hiddenCount = Math.max(0, steps.length - MAX_VISIBLE_NODES);\n const visibleSteps = showAll ? steps : steps.slice(-MAX_VISIBLE_NODES);\n const existing = Array.from(listEl.children) as HTMLElement[];\n\n let htmlIdx = 0;\n // \"+N \u66F4\u591A\" \u6298\u53E0\u884C (\u4EC5\u5728 showAll=false \u4E14\u6709 hidden \u65F6)\n if (!showAll && hiddenCount > 0) {\n let moreBtn = existing[htmlIdx] as HTMLElement | undefined;\n if (!moreBtn || !moreBtn.classList.contains('step-timeline-more')) {\n moreBtn = document.createElement('li');\n moreBtn.className = 'step-timeline-more';\n moreBtn.setAttribute('role', 'button');\n moreBtn.textContent = `+ ${hiddenCount} \u66F4\u591A`;\n moreBtn.onclick = () => {\n state.showAll = true;\n render(timelineEl);\n };\n if (existing[htmlIdx] && existing[htmlIdx] !== moreBtn) {\n listEl.replaceChild(moreBtn, existing[htmlIdx]);\n } else {\n listEl.appendChild(moreBtn);\n }\n } else {\n moreBtn.textContent = `+ ${hiddenCount} \u66F4\u591A`;\n }\n htmlIdx++;\n }\n\n for (let i = 0; i < visibleSteps.length; i++) {\n const step = visibleSteps[i];\n const realIndex = showAll ? i : (steps.length - visibleSteps.length + i);\n let node = existing[htmlIdx];\n if (!node || !node.classList.contains('step-timeline-node')) {\n node = document.createElement('li');\n node.className = 'step-timeline-node';\n node.setAttribute('data-index', String(realIndex));\n const marker = document.createElement('span');\n marker.className = 'step-timeline-marker';\n const label = document.createElement('span');\n label.className = 'step-timeline-label';\n const args = document.createElement('span');\n args.className = 'step-timeline-args';\n node.appendChild(marker);\n node.appendChild(label);\n node.appendChild(args);\n if (existing[htmlIdx] && existing[htmlIdx] !== node) {\n listEl.replaceChild(node, existing[htmlIdx]);\n } else {\n listEl.appendChild(node);\n }\n } else {\n node.setAttribute('data-index', String(realIndex));\n }\n node.setAttribute('data-status', step.status);\n const label = node.querySelector('.step-timeline-label') as HTMLElement;\n if (label) label.textContent = step.name;\n const argsEl = node.querySelector('.step-timeline-args') as HTMLElement;\n if (argsEl) {\n const argStr = step.args && Object.keys(step.args).length > 0\n ? JSON.stringify(step.args).slice(0, 60)\n : '';\n argsEl.textContent = argStr;\n argsEl.style.display = argStr ? '' : 'none';\n }\n htmlIdx++;\n }\n\n // \u6E05\u7406\u591A\u4F59 li\n while (listEl.children.length > htmlIdx) {\n listEl.removeChild(listEl.lastChild!);\n }\n }\n\n // \u6298\u53E0\u6001: \u7A7A\u72B6\u6001\u663E\u793A\u5360\u4F4D, \u6709\u8282\u70B9\u5C31\u4FDD\u6301\n if (steps.length === 0) {\n timelineEl.setAttribute('data-empty', 'true');\n } else {\n timelineEl.removeAttribute('data-empty');\n }\n // \u6298\u53E0 class\n const body = timelineEl.querySelector('[data-body]') as HTMLElement | null;\n if (body) {\n if (expanded) {\n body.style.maxHeight = body.scrollHeight + 'px';\n setTimeout(() => {\n // \u52A8\u753B\u7ED3\u675F\u540E\u6E05\u6389, \u8BA9\u5185\u5BB9\u81EA\u9002\u5E94 (\u65B0 step \u8FDB\u6765\u65F6\u80FD\u6491\u5F00)\n if (state.expanded) body.style.maxHeight = '';\n }, 300);\n } else {\n body.style.maxHeight = '0';\n }\n }\n const arrow = timelineEl.querySelector('.step-timeline-arrow') as HTMLElement | null;\n if (arrow) arrow.style.transform = expanded ? 'rotate(180deg)' : 'rotate(0deg)';\n\n // \u81EA\u52A8\u6EDA\u5230\u5E95 (\u65B0\u8282\u70B9\u8FDB\u6765\u65F6, \u8BE6\u60C5\u533A\u8DDF\u5230\u5E95)\n if (expanded && listEl) {\n listEl.scrollTop = listEl.scrollHeight;\n }\n}\n\n// ---------------------------------------------------------------------------\n// \u516C\u5171: \u521B\u5EFA\u4E00\u4E2A\u7A7A timeline (AI \u6D88\u606F\u521B\u5EFA\u65F6\u6302\u7684\u5360\u4F4D)\n// ---------------------------------------------------------------------------\nexport function createEmptyStepTimeline(channelId: string | null = null): HTMLElement {\n const root = document.createElement('div');\n root.className = 'step-timeline';\n root.setAttribute('data-step-timeline', '');\n root.setAttribute('data-empty', 'true');\n\n // \u6458\u8981\u6761\n const summary = document.createElement('div');\n summary.className = 'step-timeline-summary';\n summary.setAttribute('data-summary', '');\n const titleSpan = document.createElement('span');\n titleSpan.className = 'step-timeline-title';\n const titleContent = document.createElement('span');\n titleContent.setAttribute('data-current-tool', '');\n titleContent.textContent = '\u6267\u884C\u6B65\u9AA4';\n titleSpan.appendChild(titleContent);\n const dots = document.createElement('div');\n dots.className = 'step-timeline-dots';\n dots.setAttribute('data-dots', '');\n const arrow = document.createElement('span');\n arrow.className = 'step-timeline-arrow';\n arrow.textContent = '\u25BE';\n summary.appendChild(titleSpan);\n summary.appendChild(dots);\n summary.appendChild(arrow);\n summary.onclick = () => {\n const state = stateMap.get(root);\n if (!state) return;\n state.expanded = !state.expanded;\n writeExpanded(state.channelId, state.expanded);\n render(root);\n };\n\n // \u8BE6\u60C5\u533A\n const body = document.createElement('div');\n body.className = 'step-timeline-body';\n body.setAttribute('data-body', '');\n const list = document.createElement('ul');\n list.className = 'step-timeline-list';\n list.setAttribute('data-list', '');\n body.appendChild(list);\n\n root.appendChild(summary);\n root.appendChild(body);\n\n // \u521D\u59CB\u6298\u53E0\u6001\n const initialExpanded = readExpanded(channelId);\n stateMap.set(root, {\n steps: [],\n currentIndex: -1,\n expanded: initialExpanded,\n channelId: channelId || null,\n showAll: false,\n });\n render(root);\n return root;\n}\n\n// ---------------------------------------------------------------------------\n// \u516C\u5171: \u628A timeline \u6302\u5230 message \u4E0A (\u521D\u59CB\u5316\u6216\u8865\u6302)\n// ---------------------------------------------------------------------------\nexport function mountStepTimeline(messageEl: HTMLElement, channelId: string | null): HTMLElement {\n // \u5DF2\u7ECF\u6302\u8FC7 \u2192 \u590D\u7528\n const existing = messageEl.querySelector('[data-step-timeline]') as HTMLElement | null;\n if (existing) {\n const state = stateMap.get(existing);\n if (state && channelId && !state.channelId) state.channelId = channelId;\n return existing;\n }\n // \u627E\u63D2\u5165\u70B9: \u5728 .bubble \u4E4B\u540E, .message-actions \u4E4B\u524D\n const bubble = messageEl.querySelector('.bubble');\n const actions = messageEl.querySelector('.message-actions');\n const timeline = createEmptyStepTimeline(channelId);\n if (actions && actions.parentNode === messageEl) {\n messageEl.insertBefore(timeline, actions);\n } else if (bubble && bubble.parentNode === messageEl) {\n bubble.parentNode.insertBefore(timeline, bubble.nextSibling);\n } else {\n messageEl.appendChild(timeline);\n }\n return timeline;\n}\n\n// ---------------------------------------------------------------------------\n// \u516C\u5171: \u63A8\u4E00\u4E2A step \u4E8B\u4EF6 (start/done/error)\n// ---------------------------------------------------------------------------\nexport function pushStepToTimeline(\n timelineEl: HTMLElement,\n eventType: 'step_start' | 'step_done' | 'step_error',\n data: { tool: string; args?: Record<string, unknown>; success?: boolean; output?: string; error?: string }\n): void {\n const state = stateMap.get(timelineEl);\n if (!state) return;\n const { steps } = state;\n\n if (eventType === 'step_start') {\n state.currentIndex = steps.length;\n steps.push({\n name: data.tool,\n status: 'active',\n args: data.args,\n eventType,\n });\n } else {\n // step_done / step_error \u2014 \u627E\u5F53\u524D active \u8282\u70B9\u5173\u95ED\n // \u4F18\u5148\u5339\u914D currentIndex, \u5176\u6B21\u56DE\u9000\u5230\u6700\u540E\u4E00\u4E2A active\n let targetIdx = state.currentIndex;\n if (targetIdx < 0 || !steps[targetIdx] || steps[targetIdx].name !== data.tool) {\n // \u515C\u5E95: \u627E\u6700\u540E\u4E00\u4E2A\u540C\u540D active\n for (let i = steps.length - 1; i >= 0; i--) {\n if (steps[i].name === data.tool && steps[i].status === 'active') {\n targetIdx = i;\n break;\n }\n }\n }\n if (targetIdx < 0) {\n // \u6CA1\u6709 active \u8282\u70B9\u5339\u914D, \u5F53\u4F5C\u65B0 error \u8282\u70B9\n targetIdx = steps.length;\n steps.push({ name: data.tool, status: 'error', eventType });\n }\n if (eventType === 'step_done') {\n steps[targetIdx].status = data.success === false ? 'error' : 'done';\n if (data.output) steps[targetIdx].output = data.output;\n if (data.error) steps[targetIdx].error = data.error;\n } else {\n steps[targetIdx].status = 'error';\n if (data.error) steps[targetIdx].error = data.error;\n }\n steps[targetIdx].eventType = eventType;\n state.currentIndex = -1;\n }\n render(timelineEl);\n}\n\n// ---------------------------------------------------------------------------\n// \u516C\u5171: finalize \u65F6\u628A timeline \u5143\u7D20\u4ECE from \u642C\u5230 to (\u907F\u514D\u91CD\u6E32)\n// \u63A5\u53D7 timelineEl \u672C\u8EAB\u6216 fromEl \u5185\u90E8\u7684 timeline\n// ---------------------------------------------------------------------------\nexport function migrateStepTimeline(fromEl: HTMLElement, toEl: HTMLElement): void {\n const fromTimeline = fromEl.querySelector('[data-step-timeline]') as HTMLElement | null;\n const toTimeline = toEl.querySelector('[data-step-timeline]') as HTMLElement | null;\n if (!fromTimeline) return;\n // \u79FB\u9664 toEl \u7684\u5360\u4F4D, \u642C fromEl \u7684\u8FC7\u6765\n if (toTimeline && toTimeline !== fromTimeline) {\n toTimeline.remove();\n }\n if (toTimeline !== fromTimeline) {\n toEl.appendChild(fromTimeline);\n }\n // \u91CD\u6E32\u4E00\u6B21 (channelId \u6CE8\u5165)\n const state = stateMap.get(fromTimeline);\n if (state) {\n render(fromTimeline);\n }\n}\n\n// ---------------------------------------------------------------------------\n// \u516C\u5171: \u62FF\u5230\u4E00\u4E2A message \u5185\u7684 timeline (\u65E0\u5219\u8FD4\u56DE null)\n// ---------------------------------------------------------------------------\nexport function getStepTimeline(messageEl: HTMLElement | null): HTMLElement | null {\n if (!messageEl) return null;\n return messageEl.querySelector('[data-step-timeline]') as HTMLElement | null;\n}\n", "/**\n * parseToolCall \u2014 \u4ECE LLM \u8F93\u51FA (minimax / Hermes / Qwen / GLM / Anthropic \u7B49)\n * \u4E2D\u63D0\u53D6 tool_call { name, args }.\n *\n * 2026-06-30 \u62BD\u51FA: \u539F\u5148\u4F5C\u4E3A PiAgentSession \u7684 private \u65B9\u6CD5\u85CF\u5728 4522 \u884C\u7C7B\u91CC,\n * \u6D4B\u8BD5\u53EA\u80FD\u590D\u523B\u903B\u8F91\u4E0D\u80FD import \u771F\u6E90 = \u6D4B\u8BD5\u901A\u8FC7 \u2260 \u5B9E\u9645\u5DE5\u4F5C. \u73B0\u5728\u62BD\u51FA\u4E3A\u7EAF\u51FD\u6570,\n * \u63A5\u53D7 ctx.tools (\u53EF\u7528\u5DE5\u5177\u540D\u96C6\u5408) + \u53EF\u9009 ctx.resolveAlias (\u522B\u540D \u2192 \u771F\u5B9E\u5DE5\u5177\u540D).\n *\n * \u7528\u6CD5:\n * import { parseToolCall } from './parse-tool-call';\n * const r = parseToolCall(content, { tools: new Set(['shell_exec', 'read_file']) });\n * if (r) tool.execute(r.args);\n */\n\nexport interface ParseContext {\n /** \u5F53\u524D\u4F1A\u8BDD\u53EF\u7528\u7684\u5DE5\u5177\u540D\u96C6\u5408. \u7528\u4E8E resolveToolName. */\n tools: Set<string>;\n /** \u53EF\u9009: \u522B\u540D \u2192 \u6807\u51C6\u5DE5\u5177\u540D. \u4E0D\u63D0\u4F9B\u65F6, \u53EA\u7528 tools.has() \u7CBE\u786E\u5339\u914D. */\n resolveAlias?: (name: string) => string | null;\n}\n\nexport interface ToolCall {\n name: string;\n args: Record<string, string>;\n}\n\nconst SHELL_KEYWORDS = ['git', 'npx', 'npm', 'tsx', 'tsc', 'vitest', 'node', 'mkdir', 'touch', 'ls', 'echo', 'cat', 'head', 'tail', 'wc', 'pwd', 'date'];\n\n/**\n * \u9ED8\u8BA4\u7684 alias resolver \u2014 Claude Code \u98CE\u683C \u2192 bolloon \u5DE5\u5177\u540D\n * \u5F53 ctx.resolveAlias \u7F3A\u5931\u65F6\u4F7F\u7528.\n */\nfunction defaultResolveAlias(name: string, tools: Set<string>): string | null {\n if (tools.has(name)) return name;\n const lower = name.toLowerCase();\n const aliasMap: Record<string, string> = {\n read: 'read_file',\n edit: 'edit_file',\n write: 'write_file',\n rm: 'delete_file',\n mv: 'move_file',\n bash: 'shell_exec',\n shell: 'shell_exec',\n sh: 'shell_exec',\n cat: 'read_file',\n test: 'vitest_run',\n vitest: 'vitest_run',\n typecheck: 'tsc_check',\n tsc: 'tsc_check',\n log: 'git_log',\n show: 'git_show',\n diff: 'git_diff',\n commit: 'git_commit',\n push: 'git_push',\n branch: 'git_branch',\n checkout: 'git_branch',\n stash: 'git_stash',\n todo_write: 'create_task',\n todowrite: 'create_task',\n task: 'create_task',\n };\n const aliased = aliasMap[lower];\n if (aliased && tools.has(aliased)) return aliased;\n if (tools.has(lower)) return lower;\n return null;\n}\n\n/** \u62C6\u5206 args.command: \"git status\" \u2192 { command: \"git\", args: \"status\" } */\nfunction autoSplitCommand(args: Record<string, string>): void {\n if (typeof args.command === 'string' && args.command.includes(' ') && !args.args) {\n const parts = args.command.split(/\\s+/);\n args.command = parts[0];\n args.args = parts.slice(1).join(' ');\n }\n}\n\n/** \u7528 resolver \u89E3\u6790\u5DE5\u5177\u540D \u2014 \u63A5\u53D7\u4EFB\u610F resolver (\u9ED8\u8BA4\u6216\u81EA\u5B9A\u4E49). */\nfunction resolve(ctx: ParseContext, name: string): string | null {\n if (ctx.resolveAlias) return ctx.resolveAlias(name);\n return defaultResolveAlias(name, ctx.tools);\n}\n\nexport function parseToolCall(content: string, ctx: ParseContext): ToolCall | null {\n if (!content) return null;\n\n // === 0. \u5265\u79BB \u601D\u8003\u5757 \u2014 \u5FC5\u987B\u5148\u505A, \u5426\u5219\u65E7 regex `<(\\w+)>...</\\1>` \u4F1A\u5148\u5339\u914D \u60F3\u60F3\u6807\u7B7E ===\n const strippedContent = content.replace(/<think[\\s\\S]*?<\\/think/g, '');\n\n // === 1. JSON function-call (OpenAI / Anthropic / Minimax-style) ===\n const jsonPatterns = [\n // markdown json code block + OpenAI \u5757, \u540C\u65F6\u5339\u914D arguments/input \u5B57\u6BB5\n /(?:```(?:json|json5)?\\s*\\n?)?\\{[\\s\\S]*?\"name\"\\s*:\\s*[\"'](\\w+)[\"']\\s*,\\s*[\"']?(?:arguments|input)[\"']?\\s*:\\s*(\\{[\\s\\S]*?\\})\\s*\\}/,\n ];\n for (const p of jsonPatterns) {\n const m = content.match(p);\n if (m) {\n const name = m[1];\n let args: Record<string, string> = {};\n try {\n const parsed = JSON.parse(m[2]);\n if (parsed && typeof parsed === 'object') {\n args = Object.fromEntries(Object.entries(parsed).map(([k, v]) => [k, String(v)]));\n }\n } catch {\n /* \u89E3\u6790\u5931\u8D25\u4FDD\u6301 args={} */\n }\n autoSplitCommand(args);\n const resolved = resolve(ctx, name);\n if (resolved) {\n return { name: resolved, args };\n }\n }\n }\n\n // === 2. <tools:call name=\"X\">...</tools:call> \u5D4C\u5957\u683C\u5F0F ===\n const toolsCallMatch = content.match(/<tools:call\\s+name=[\"'](\\w+)[\"']>([\\s\\S]*?)<\\/tools:call>/);\n if (toolsCallMatch) {\n const name = toolsCallMatch[1];\n const inner = toolsCallMatch[2];\n const args: Record<string, string> = {};\n const argTags = inner.matchAll(/<tools:call\\s+name=[\"'](\\w+)[\"']>([\\s\\S]*?)<\\/tools:call>/g);\n for (const m of argTags) {\n args[m[1]] = m[2].trim();\n }\n const resolved = resolve(ctx, name);\n if (resolved) {\n return { name: resolved, args };\n }\n // \u515C\u5E95: \u5916\u5C42 tool name \u4E0D\u5728 tools, \u4F46\u5185\u5C42\u6709 command, \u63A8\u65AD shell_exec\n if (args.command) {\n const cmdFirst = args.command.split(/\\s+/)[0];\n if (SHELL_KEYWORDS.includes(cmdFirst)) {\n return { name: 'shell_exec', args };\n }\n }\n }\n\n // === 3. XML \u683C\u5F0F (\u9646\u7EED\u68C0\u6D4B\u591A\u4E2A pattern) ===\n const patterns = [\n // minimax/Hermes \u81EA\u95ED\u5408 XML \u683C\u5F0F <invoke name=\"X\">...</invoke>\n new RegExp('<invoke\\\\s+name=[\"\\']([\\\\w]+)[\"\\']>([\\\\s\\\\S]*?)</invoke>'),\n // <function_calls> \u5305\u88F9\n new RegExp('<function_calls>[\\\\s\\\\S]*?<invoke\\\\s+name=[\"\\']([\\\\w]+)[\"\\']>([\\\\s\\\\S]*?)</invoke>[\\\\s\\\\S]*?</function_calls>'),\n // <function_calls><tool name=\"X\"><param name=\"Y\">value</param></tool></function_calls>\n new RegExp('<function_calls>[\\\\s\\\\S]*?<tool\\\\s+name=[\"\\']([\\\\w]+)[\"\\']>([\\\\s\\\\S]*?)</tool>[\\\\s\\\\S]*?</function_calls>'),\n /\u8C03\u7528\u5DE5\u5177[\uFF1A:]\\s*(\\w+)\\s*\\(([^)]*)\\)/,\n /\u4F7F\u7528\u5DE5\u5177[\uFF1A:]\\s*(\\w+)\\s*\\(([^)]*)\\)/,\n /tool[_\\w]*[\uFF1A:]\\s*(\\w+)\\s*\\(([^)]*)\\)/i,\n /(\\w+)\\s*\\(\\s*([^)]*)\\s*\\)/,\n // \u5BF9\u8C61\u5B57\u9762\u91CF\u683C\u5F0F {tool => \"get_identity\", args => {...}}\n /\\{\\s*tool\\s*=>\\s*[\"'](\\w+)[\"']\\s*(?:,\\s*args\\s*=>\\s*(\\{[\\s\\S]*?\\}))?\\s*\\}/,\n // tool => \"get_identity\" (\u65E0 args \u5305\u88F9)\n /\\btool\\s*=>\\s*[\"'](\\w+)[\"']/,\n // [TOOL_CALL] \u5757\u5185 JSON \u5F62\u5F0F {\"name\": \"x\", \"args\": {...}}\n /\\[TOOL_CALL\\][\\s\\S]*?\\{\\s*\"name\"\\s*:\\s*\"(\\w+)\"\\s*,\\s*\"args\"\\s*:\\s*(\\{[\\s\\S]*?\\})/i,\n // \"tool_name {json_args}\" \u5F62\u5F0F (\u5355\u884C, \u65E0 name \u5B57\u6BB5)\n /(?:^|\\n)(\\w+)\\s+(\\{[\\s\\S]*?\\})(?=\\n|$)/,\n // XML \u683C\u5F0F <tool_name>...<arg>value</arg>...</tool_name>\n /<(\\w+)>([\\s\\S]*?)<\\/\\1>/,\n ];\n\n for (const pattern of patterns) {\n const match = strippedContent.match(pattern);\n if (!match) continue;\n const name = match[1];\n let args: Record<string, string> = {};\n const rawArgs = match[2] || '';\n\n if (rawArgs && rawArgs.trim().startsWith('{')) {\n // JSON \u5F62\u5F0F, \u5C1D\u8BD5\u89E3\u6790\n try {\n const parsed = JSON.parse(rawArgs);\n if (parsed && typeof parsed === 'object') {\n args = Object.fromEntries(Object.entries(parsed).map(([k, v]) => [k, String(v)]));\n }\n } catch {\n // \u89E3\u6790\u5931\u8D25\u5C31\u5F53\u5B57\u7B26\u4E32\u5904\u7406\n const argPairs = rawArgs.split(',').map(s => s.trim()).filter(Boolean);\n for (const pair of argPairs) {\n const [key, ...valueParts] = pair.split(':').map(s => s.trim().replace(/['\"]/g, ''));\n if (key) args[key] = valueParts.join(':') || '';\n }\n }\n } else if (rawArgs && /<[\\w]/.test(rawArgs) && /<\\/\\w+>/.test(rawArgs)) {\n // 2026-06-30 \u4FEE: \u68C0\u6D4B XML \u5B50\u6807\u7B7E (\u542B\u5E26\u5C5E\u6027\u7684 <parameter name=\"X\"> \u5F62\u5F0F)\n // \u539F\u6587\u7528 /<\\w+>[\\s\\S]*<\\/\\w+>/ \u68C0\u6D4B, \u4F46\u662F <parameter name=\"command\"> \u5F62\u5F0F\n // \u9996\u6807\u7B7E\u662F <parameter, \u4E0D\u662F\u7B80\u5355\u7684 <command \u6240\u4EE5\u539F\u68C0\u6D4B\u4F1A false,\n // \u8DEF\u7531\u9519\u8BEF\u8D70 key:value \u8DEF\u5F84. \u8FD9\u91CC\u653E\u5BBD\u5230\u4EFB\u4F55 <...>...content...</...>\n const paramRe = /<parameter\\s+name=[\"'](\\w+)[\"']>([\\s\\S]*?)<\\/parameter>/g;\n const paramReShort = /<param\\s+name=[\"'](\\w+)[\"']>([\\s\\S]*?)<\\/param>/g;\n let pMatch: RegExpExecArray | null;\n while ((pMatch = paramRe.exec(rawArgs)) !== null) {\n const argName = pMatch[1];\n const argValue = pMatch[2].trim();\n if (argName && argValue) {\n args[argName] = argValue;\n }\n }\n if (Object.keys(args).length === 0) {\n let sMatch: RegExpExecArray | null;\n paramReShort.lastIndex = 0;\n while ((sMatch = paramReShort.exec(rawArgs)) !== null) {\n const argName = sMatch[1];\n const argValue = sMatch[2].trim().replace(/^[\"']|[\"']$/g, '');\n if (argName && argValue) {\n args[argName] = argValue;\n }\n }\n }\n if (Object.keys(args).length === 0) {\n const xmlArgPattern = /<(\\w+)>([\\s\\S]*?)<\\/\\1>/g;\n let xmlMatch: RegExpExecArray | null;\n while ((xmlMatch = xmlArgPattern.exec(rawArgs)) !== null) {\n const argName = xmlMatch[1];\n const argValue = xmlMatch[2].trim();\n if (argName && argValue) {\n args[argName] = argValue;\n }\n }\n }\n } else if (rawArgs) {\n // \u5F62\u53C2\u4E32 key: value\n const argPairs = rawArgs.split(',').map(s => s.trim()).filter(Boolean);\n for (const pair of argPairs) {\n const [key, ...valueParts] = pair.split(':').map(s => s.trim().replace(/['\"]/g, ''));\n if (key) args[key] = valueParts.join(':') || '';\n }\n }\n\n // \u540D\u5B57 resolve: \u4F18\u5148 this.tools.has, \u7136\u540E alias resolver\n const resolved = ctx.tools.has(name) ? name : resolve(ctx, name);\n if (resolved) {\n autoSplitCommand(args);\n return { name: resolved, args };\n }\n // \u540D\u5B57\u672A\u8BC6\u522B\u4F46 rawArgs \u542B <command> \u5B50\u6807\u7B7E \u2192 \u8DF3\u51FA, \u8D70 fallback \u6309 command \u63A8\u65AD shell_exec\n if (rawArgs && /<\\w+>[\\s\\S]*<\\/\\w+>/.test(strippedContent)) {\n break;\n }\n }\n\n // === 4. Fallback: \u4EFB\u610F <tag><command>...</command></tag> \u6309 cmdFirst \u63A8\u65AD shell_exec ===\n const xmlTagMatch = strippedContent.match(/<(\\w+)>([\\s\\S]*?)<\\/\\1>/);\n if (xmlTagMatch) {\n const outerTag = xmlTagMatch[1];\n const inner = xmlTagMatch[2];\n if (!resolve(ctx, outerTag)) {\n const cmdMatch = inner.match(/<command>([\\s\\S]*?)<\\/command>/);\n if (cmdMatch) {\n const cmd = cmdMatch[1].trim();\n const cmdFirst = cmd.split(/\\s+/)[0];\n if (SHELL_KEYWORDS.includes(cmdFirst)) {\n const args: Record<string, string> = {};\n const remaining = cmd.slice(cmdFirst.length).trim();\n if (remaining) {\n args.command = cmdFirst;\n args.args = remaining;\n } else {\n args.command = cmd;\n }\n const argsM = inner.match(/<args>([\\s\\S]*?)<\\/args>/);\n if (argsM && argsM[1].trim()) {\n args.args = argsM[1].trim();\n }\n return { name: 'shell_exec', args };\n }\n }\n }\n }\n return null;\n}\n\n/**\n * isFinalResponse \u2014 \u4E34\u65F6\u5224\u65AD LLM \u662F\u5426\u5DF2\u5B8C\u7ED3 (\u542B <final gen>)\n * 2026-06-19/06-30 \u4FEE: \u5148\u5265\u79BB think, \u907F\u514D\u601D\u8003\u5757\u4E2D\u63D0\u53CA <final gen> \u8BEF\u6740\n * \u542B\u53EF\u89E3\u6790 tool_call \u5219\u4F18\u5148\u6267\u884C\u5DE5\u5177, \u4E0D\u7B97 final\n */\nexport function isFinalResponse(content: string, ctx?: ParseContext): boolean {\n const stripped = content.replace(/<think[\\s\\S]*?<\\/think/g, '');\n if (!stripped.includes('<final gen>')) return false;\n // \u7528\u9ED8\u8BA4 ctx (\u65E0\u9700 tool \u540D\u89E3\u6790, \u53EA\u770B\u80FD\u5426 parse \u51FA\u6765) - \u4F46 isFinalResponse \u4E0D\u9700\u8981 tools \u5217\u8868\u4E0A\u4E0B\u6587.\n // \u5982\u679C\u6CA1\u6709\u4F20 ctx, \u4F7F\u7528 empty set (\u53EA\u89E3\u6790\u4E0D resolve).\n if (ctx && parseToolCall(stripped, ctx)) return false;\n if (!ctx) {\n // \u8BD5\u63A2\u6027 parse (\u4E0D resolve name) \u2014 \u5982\u679C\u81F3\u5C11\u80FD\u63D0\u53D6 name+args \u6A21\u5F0F, \u5C31\u4E0D\u662F final.\n // \u8FD9\u91CC\u7C97\u7565\u68C0\u6D4B: stripped \u4E2D\u542B json \"name\": \u6216 <invoke name= \u6216 <function_calls> \u4EFB\u4E00\n if (/\\{[\\s\\S]*?\"name\"\\s*:/.test(stripped)) return false;\n if (/<invoke\\s+name=/.test(stripped)) return false;\n if (/<function_calls>/.test(stripped)) return false;\n }\n return true;\n}\n\n/** extractFinalAnswer: \u53BB\u9664 <final gen> \u6807\u8BB0 + tool call \u566A\u58F0. */\nexport function extractFinalAnswer(content: string): string {\n const marker = '<final gen>';\n const markerIndex = content.indexOf(marker);\n if (markerIndex !== -1) {\n const after = content.substring(markerIndex + marker.length).trim();\n if (after) {\n content = after;\n } else {\n content = content.substring(0, markerIndex).trim();\n }\n }\n let cleaned = content\n .replace(/\u8C03\u7528\u5DE5\u5177[\uFF1A:]\\s*\\w+\\s*\\([^)]*\\)/g, '')\n .replace(/\u4F7F\u7528\u5DE5\u5177[\uFF1A:]\\s*\\w+\\s*\\([^)]*\\)/g, '')\n .replace(/tool[_\\w]*[\uFF1A:]\\s*\\w+\\s*\\([^)]*\\)/gi, '')\n .trim();\n return cleaned;\n}\n", "/**\n * chat-segmenter \u2014 \u628A LLM \u539F\u59CB\u8F93\u51FA\u5207\u6210\u7ED3\u6784\u5316 segments (\u524D\u540E\u7AEF\u5206\u79BB\u6838\u5FC3)\n *\n * 2026-07-01 (v0.2.6): \u62BD\u5230\u72EC\u7ACB\u6A21\u5757. \u76EE\u6807: \u524D\u7AEF\u53EA\u6E32\u67D3, \u4E0D\u505A\u6B63\u5219.\n *\n * \u8BBE\u8BA1:\n * \u540E\u7AEF\u8DD1\u5B8C LLM \u4E00\u8F6E\u540E, \u8C03 segmentChatReply() \u628A reply \u5207\u6210:\n * [{type: 'think', content: '...'}, {type: 'text', content: '...'}, ...]\n * \u7136\u540E\u5E8F\u5217\u5316\u53D1\u7ED9\u524D\u7AEF, \u524D\u7AEF\u6309 type \u6E32\u67D3 \u2014 \u4E0D\u77E5\u9053\u4EFB\u4F55 <invoke> / <function_calls>\n * /<tool_call> / {tool:...} \u8FD9\u4E9B LLM \u8F93\u51FA\u683C\u5F0F\u7EC6\u8282.\n *\n * \u4E0E parse-tool-call.ts \u7684\u533A\u522B:\n * - parse-tool-call: \u627E **\u7B2C\u4E00\u4E2A** tool_call, \u63D0\u53D6 name+args (\u7528\u4E8E dispatch)\n * - chat-segmenter: \u5207**\u6574\u6BB5** reply \u4E3A\u5C55\u793A segments, **\u5B8C\u5168\u53BB\u6389** tool_call \u6807\u8BB0\n * \u590D\u7528 parse-tool-call \u7684 tool_call \u68C0\u6D4B\u903B\u8F91\u4FDD\u8BC1\u4E00\u81F4\u6027\n *\n * \u8FD4\u56DE\u7C7B\u578B: ChatSegment[] \u5E8F\u5217\u5316 JSON \u7ED9\u524D\u7AEF:\n * {type: 'think'|'text'|'env_details'|'tool_call'|'final', content?: string, tool?: {...}}\n *\n * \u987A\u5E8F\u6309 LLM \u8F93\u51FA\u6D41\u6392, \u4E0D\u91CD\u6392.\n */\n\nimport { parseToolCall } from './parse-tool-call.js';\n\nexport type ChatSegmentType = 'think' | 'text' | 'env_details' | 'tool_call' | 'final';\n\nexport interface ChatSegment {\n type: ChatSegmentType;\n /** text/think/env_details/final \u7528 */\n content?: string;\n /** tool_call \u7528 \u2014 \u540E\u7AEF\u770B, \u524D\u7AEF\u7528\u6765\u6253\"\u5DF2\u53D1\"\u6253\u52FE */\n tool?: {\n name: string;\n args: Record<string, string>;\n };\n}\n\nexport interface SegmenterContext {\n /** \u5DF2\u77E5 tool names \u2014 \u7528\u4E8E\u6807\u8BB0 tool_call segment \u7684\u5408\u6CD5\u6027 (\u8DDF parse-tool-call \u4E00\u81F4) */\n knownToolNames: Set<string>;\n}\n\n/** \u5207\u5206 LLM \u8F93\u51FA. */\nexport function segmentChatReply(reply: string, ctx: SegmenterContext): ChatSegment[] {\n if (!reply) return [];\n const segments: ChatSegment[] = [];\n let remaining = reply;\n\n // === 1. \u542F\u53D1\u5F0F: \u5F00\u5934\"\u8BA9\u6211.../First I'll.../I should...\" \u53E5\u5B50\u8FDB think \u6BB5 ===\n // LLM \u5076\u5C14\u4E0D\u5199 <think> \u6807\u7B7E\u4F46\u76F4\u63A5\u51FA\u601D\u8003. \u542F\u53D1\u5F0F: \u6587\u672C\u9996\u53E5\u4EE5\u8FD9\u4E9B\u6A21\u5F0F\u5F00\u5934 \u2192 think\n // \u5FC5\u987B\u5728 step 1 (\u663E\u5F0F <think> \u5207\u5206) \u4E4B\u524D \u2014 \u56E0\u4E3A\u663E\u5F0F\u5207\u5206 push \u5B8C\u4F1A\u53CD\u5E8F\n if (!remaining.startsWith('<think>')) {\n remaining = extractLeadingThinking(remaining, segments);\n }\n\n // === 2. \u5207 <think>...</think> (\u5185\u5BB9\u4FDD\u7559, wrap \u6210 think segment) ===\n remaining = extractAndPush('think', remaining, segments, /<think>([\\s\\S]*?)<\\/think>/g);\n\n // === 3. \u5207 <environment_details>...</environment_details> ===\n remaining = extractAndPush('env_details', remaining, segments, /<environment_details>([\\s\\S]*?)<\\/environment_details>/g);\n\n // === 4. \u5207 tool_call \u6807\u8BB0 (\u6838\u5FC3: \u5B8C\u5168\u53BB\u6389, \u4E0D\u8BA9\u524D\u7AEF\u770B\u5230) ===\n // 2026-07-01 \u4FEE: \u8FD9\u6B65\u5FC5\u987B\u5728 final \u4E4B\u524D \u2014 final \u6807\u8BB0\u4E4B\u524D\u7684 tool_call \u662F\n // \"LLM \u5728 final \u4E4B\u524D\u8FD8\u5728\u8C03\u5DE5\u5177\", \u8FD9\u90E8\u5206 content \u5E94\u8FDB text (\u4E2D\u95F4\u8FC7\u7A0B), \u4E0D\u5E94\u8FDB final.\n remaining = stripToolCallMarkers(remaining, segments, ctx);\n\n // === 5. \u8FC7\u6EE4 LLM \u586B\u5145\u8BCD (\"\u597D\u4E86\"/\"\u5B8C\u6210\"/\"\u53EF\u4EE5\" \u7B49\u5355\u53E5 text \u4E0D\u4E0A\u5C4F) ===\n if (remaining.trim()) {\n remaining = filterFillerText(remaining);\n }\n\n // === 6. final \u4E4B\u524D\u7684 text \u6BB5 (\u4E2D\u95F4\u5BF9\u8BDD) push \u2014 \u5FC5\u987B\u5728 final push \u4E4B\u524D ===\n // \u6CE8\u610F: \u8FD9\u65F6 remaining \u8FD8\u542B <final gen> \u6807\u8BB0 + \u6807\u8BB0\u540E\u5185\u5BB9. \u5207 final \u524D\u5148\u628A <final gen> \u4E4B\u540E\u5185\u5BB9\u6E05\u6389\n // \u5B9E\u9645: \u5207 final \u540E\u518D\u5207 text \u66F4\u6E05\u695A\n const finalIdx = remaining.indexOf('<final gen>');\n let beforeFinalText = remaining;\n if (finalIdx >= 0) {\n beforeFinalText = remaining.substring(0, finalIdx);\n }\n\n const textContent = beforeFinalText.trim();\n if (textContent) {\n segments.push({ type: 'text', content: textContent });\n }\n\n // === 7. \u5207 <final gen>...</final gen> (\u6C38\u8FDC\u6700\u540E push, \u6E32\u67D3\u65F6\u5728\u6700\u540E) ===\n if (finalIdx >= 0) {\n const afterFinal = remaining.substring(finalIdx + '<final gen>'.length).trim();\n if (afterFinal) {\n segments.push({ type: 'final', content: afterFinal });\n }\n }\n\n return segments;\n}\n\n/**\n * \u4FEE extractAndPush \u7684 reverse \u5220\u9664 bug (2026-07-01)\n * \u4E4B\u524D: \u7528 mutate \u540E\u7684 next \u7B97\u5207\u7247, idx \u9519\u4F4D \u2192 \u591A match \u65F6\u5220\u9664\u9519\n * \u73B0\u5728: \u7528 lastIndex \u7D2F\u52A0\u5728\u539F\u6587\u4E0A\u6807\u8BB0\u5220\u9664\u533A, \u6700\u540E\u4E00\u6B21\u6027 slice\n */\nfunction extractAndPush(\n type: ChatSegmentType,\n text: string,\n out: ChatSegment[],\n re: RegExp\n): string {\n const matches: Array<{ start: number; end: number; content: string }> = [];\n const localRe = new RegExp(re.source, re.flags);\n let m: RegExpExecArray | null;\n while ((m = localRe.exec(text)) !== null) {\n const content = m[1] ? m[1].trim() : '';\n matches.push({\n start: m.index,\n end: m.index + m[0].length,\n content,\n });\n if (m[0].length === 0) localRe.lastIndex++;\n }\n if (matches.length === 0) return text;\n\n // push \u6B63\u5411 (\u4FDD\u987A\u5E8F)\n for (const m of matches) {\n if (m.content) out.push({ type, content: m.content });\n }\n\n // \u5220\u9664: \u5728\u539F\u6587\u4E0A\u57FA\u4E8E start/end \u5207\u7247, \u4E0D\u5728 mutate \u540E\u7B97 idx\n let next = '';\n let cursor = 0;\n for (const m of matches) {\n next += text.substring(cursor, m.start);\n cursor = m.end;\n }\n next += text.substring(cursor);\n return next;\n}\n\n/**\n * \u542F\u53D1\u5F0F: \u63D0\u53D6\u5F00\u5934\u7684\"\u601D\u8003\"\u53E5\u5B50 (2026-07-01 \u65B0\u589E)\n * \u6A21\u5F0F: \u6587\u672C\u5F00\u5934\u9047\u5230\u8FD9\u4E9B\u8D77\u624B\u5F0F, \u6574\u53E5 (\u5230\u4E0B\u4E00\u4E2A \\n \u6216 . \u6216 \u53E5\u53F7) \u5207\u5230 think \u6BB5\n * \u4F8B\u5B50:\n * \"\u8BA9\u6211\u5148\u60F3\u60F3\u7528\u6237\u7684\u9700\u6C42\" + \u6B63\u6587 \u2192 think(\u8BA9\u6211\u5148\u60F3\u60F3\u7528\u6237\u7684\u9700\u6C42) + \u6B63\u6587\n * \"First I'll check the project structure\" \u2192 think(...)\n * \u4E0D\u547D\u4E2D \u2192 \u6574\u6BB5\u4FDD\u7559, \u8D70 text\n */\nfunction extractLeadingThinking(text: string, out: ChatSegment[]): string {\n // \u6A21\u5F0F 1: \u663E\u5F0F/\u9690\u5F0F\u601D\u8003\u53E5\u9996 (\u4E00\u53E5\u5230\u7B2C\u4E00\u4E2A \\n \u6216 \\.\\?\\! \u7ED3\u675F)\n // \"\u8BA9\u6211...\" / \"\u6211\u5148...\" / \"\u5148\u6765...\" / \"\u63A5\u4E0B\u6765...\" / \"First, ...\" / \"I'll ...\"\n // \u6574\u53E5\u8FDB think, \u540E\u7EED content \u8FDB text\n // \u6A21\u5F0F 2: \u6574\u884C\u90FD\u662F\u601D\u8003 (\u4E00\u884C \\n \u95F4\u9694)\n const firstLineEnd = text.indexOf('\\n');\n const firstLine = firstLineEnd === -1 ? text : text.substring(0, firstLineEnd);\n const rest = firstLineEnd === -1 ? '' : text.substring(firstLineEnd + 1);\n\n // \u4E2D\u6587/\u82F1\u6587\u601D\u8003\u8D77\u624B\u8BCD\n const thinkingStartRe = /^(\u8BA9\u6211|\u6211\u5148|\u6211\u5E94\u8BE5|\u5148\u6765|\u5148|\u63A5\u4E0B\u6765|\u597D\u7684[,\uFF0C]?\\s*\u6211|\u8BA9\u6211\u5148|\u5148\u770B\u770B|\u8BA9\u6211\u770B\u770B|\u601D\u8003|\u8003\u8651)/;\n const enThinkingStartRe = /^(Let me|I'll|I will|First,|Next,|Now,|So,|Alright[,.]\\s+(?:let me|I'll|I will))/i;\n\n if (firstLine.trim().length === 0) {\n return text; // \u7A7A\u5F00\u5934\u4E0D\u52A8\n }\n\n // \u5355\u884C\u542F\u53D1\u5F0F: \u6574\u884C < 120 \u5B57\u7B26 + \u8D77\u59CB match \u2192 \u6574\u884C\u8FDB think\n if (firstLine.length <= 120 && (thinkingStartRe.test(firstLine) || enThinkingStartRe.test(firstLine))) {\n out.push({ type: 'think', content: firstLine.trim() });\n return rest;\n }\n\n // \u591A\u884C\u542F\u53D1\u5F0F: \u7B2C\u4E00\u6BB5 (\u5230\u7B2C\u4E00\u4E2A\u7A7A\u884C) \u662F\u601D\u8003\n if (firstLine.length <= 80 && (thinkingStartRe.test(firstLine) || enThinkingStartRe.test(firstLine))) {\n out.push({ type: 'think', content: firstLine.trim() });\n return rest;\n }\n\n return text;\n}\n\n/**\n * \u8FC7\u6EE4 LLM \u586B\u5145\u8BCD (2026-07-01 \u65B0\u589E)\n * \u5355\u72EC\u7684\"\u597D\u4E86\"/\"\u5B8C\u6210\"/\"\u53EF\u4EE5\"/\"\u7B54\u5B8C\u4E86\"\u7B49\u5355\u53E5\u4E0D\u663E\u793A\u5728\u6C14\u6CE1\n * \u914D\u5408 segmentChatReply \u6B65\u9AA4 6\n */\nfunction filterFillerText(text: string): string {\n // \u6574\u884C = filler \u8BCD \u2192 \u6574\u884C\u4E22\n const lines = text.split('\\n');\n const filtered: string[] = [];\n for (const line of lines) {\n const t = line.trim();\n if (!t) continue;\n // \u5339\u914D: \u6574\u884C = \u586B\u5145\u8BCD (\u53EF\u5E26\u6807\u70B9)\n if (/^(\u597D|\u597D\u4E86|\u597D\u7684|\u5B8C\u6210|\u5B8C\u6210\u4E86|\u4EFB\u52A1\u5B8C\u6210|\u53EF\u4EE5|\u53EF\u4EE5\u4E86|\u7B54\u5B8C\u4E86|\u8BF4\u5B8C\u4E86|\u5C31\u8FD9\u6837|\u5B8C\u4E86|done|ok|OK|Okay|okay|alright|fine|let me check|\u6211\u6765)\\.?$/i.test(t)) {\n continue;\n }\n filtered.push(line);\n }\n return filtered.join('\\n').trim();\n}\n\n/** \u63D0\u53D6\u6B63\u5219\u5339\u914D\u7684 group(1) \u5185\u5BB9, \u5305\u88C5\u4E3A segment; \u539F\u6587\u4F4D\u7F6E\u53BB\u6389 */\nfunction stripToolCallMarkers(\n text: string,\n out: ChatSegment[],\n ctx: SegmenterContext\n): string {\n // 1. \u89E3\u6790 [TOOL_CALL]...[/TOOL_CALL]\n let result = text.replace(/\\[TOOL_CALL\\][\\s\\S]*?\\[\\/TOOL_CALL\\]/g, (m) => {\n const parsed = parseToolCall(m, { tools: ctx.knownToolNames });\n if (parsed) out.push({ type: 'tool_call', tool: { name: parsed.name, args: parsed.args } });\n return '';\n });\n\n // 2.<tool_call>...</tool_call> (OpenAI Hermes)\n result = result.replace(/<tool_call>[\\s\\S]*?<\\/tool_call>/gi, (m) => {\n const parsed = parseToolCall(m, { tools: ctx.knownToolNames });\n if (parsed) out.push({ type: 'tool_call', tool: { name: parsed.name, args: parsed.args } });\n return '';\n });\n\n // 3. <invoke name=\"X\">...</invoke> (minimax/Hermes)\n result = result.replace(/<invoke\\s+name=[\"']([\\w]+)[\"']>([\\s\\S]*?)<\\/invoke>/g, (_m, name, inner) => {\n if (ctx.knownToolNames.has(name)) {\n const args = extractSimpleArgs(inner);\n out.push({ type: 'tool_call', tool: { name, args } });\n }\n return '';\n });\n\n // 4. <function_calls>...</function_calls> \u6574\u5757\n result = result.replace(/<function_calls>[\\s\\S]*?<\\/function_calls>/g, (m) => {\n const parsed = parseToolCall(m, { tools: ctx.knownToolNames });\n if (parsed) out.push({ type: 'tool_call', tool: { name: parsed.name, args: parsed.args } });\n return '';\n });\n\n // 5. JSON \u5F62\u5F0F {\"name\": \"X\", \"arguments\": {...}}\n result = result.replace(\n /\\{\\s*\"name\"\\s*:\\s*\"([\\w]+)\"\\s*,\\s*\"(?:arguments|input|args|params)\"\\s*:\\s*(\\{[\\s\\S]*?\\})\\s*\\}/g,\n (_m, name, argsJson) => {\n if (!ctx.knownToolNames.has(name)) return '';\n let args: Record<string, string> = {};\n try {\n const parsed = JSON.parse(argsJson);\n if (parsed && typeof parsed === 'object') {\n args = Object.fromEntries(Object.entries(parsed).map(([k, v]) => [k, String(v)]));\n }\n } catch {}\n out.push({ type: 'tool_call', tool: { name, args } });\n return '';\n }\n );\n\n // 6. tool => \"X\", args => {...} (Perplexity-style \u5185\u90E8)\n result = result.replace(/\\{\\s*tool\\s*=>\\s*[\"']([\\w]+)[\"']\\s*(?:,\\s*args\\s*=>\\s*(\\{[\\s\\S]*?\\}))?\\s*\\}/g, (_m, name, argsJson) => {\n if (!ctx.knownToolNames.has(name)) return '';\n let args: Record<string, string> = {};\n if (argsJson) {\n try {\n const parsed = JSON.parse(argsJson);\n if (parsed && typeof parsed === 'object') {\n args = Object.fromEntries(Object.entries(parsed).map(([k, v]) => [k, String(v)]));\n }\n } catch {}\n }\n out.push({ type: 'tool_call', tool: { name, args } });\n return '';\n });\n\n // 7. [Function calling]...[/Function calling] \u65E7 bolloon\n result = result.replace(/\\[Function[^\\]]*\\]\\s*/g, '');\n\n // 8. (2026-07-01) \u672A\u95ED\u5408\u7684 tool_call \u8D77\u59CB\u6807\u7B7E (LLM \u5076\u8F93\u51FA\u622A\u65AD)\n // \u4F8B\u5B50: \"<tool_call><invoke name=\"shell_exec\"><command>ls</command></invoke>\" \u7F3A\u5C11 </tool_call>\n // \u4E0A\u9762 1-7 \u6B65\u7684\u6B63\u5219\u90FD\u8981\u6C42\u5B8C\u6574\u95ED\u5408\u5BF9, \u4E0D\u95ED\u5408\u4F1A\u7559\u6B8B\u6587.\n // \u4FEE\u6CD5: \u626B\u5230\u5B64\u7ACB\u8D77\u59CB\u6807\u7B7E (\u540E\u9762\u6CA1\u6709\u5339\u914D\u7684\u95ED\u5408) \u2192 \u6574\u6BB5\u5220\u5230 \\n\\n \u6216 end\n result = stripUnclosedToolCallTags(result);\n\n return result;\n}\n\n/**\n * \u5220\u672A\u95ED\u5408\u7684 tool_call \u8D77\u59CB\u6807\u7B7E + \u540E\u9762\u5230\u6BB5\u7ED3\u675F (2026-07-01)\n * \u4F8B\u5B50: \"<tool_call><invoke name=\"shell_exec\"><command>ls</command></invoke>\"\n * \u2192 \u68C0\u67E5\u6BCF\u4E2A\u8D77\u59CB\u6807\u7B7E (<tool_call>, <invoke, <function_calls, [TOOL_CALL])\n * \u2192 \u5982\u679C\u6CA1\u627E\u5230\u5BF9\u5E94\u95ED\u5408, \u5220\u6574\u6BB5\n */\nfunction stripUnclosedToolCallTags(text: string): string {\n const startTags: Array<{ tag: string; closeTag: string; openRe: RegExp }> = [\n { tag: '<tool_call>', closeTag: '</tool_call>', openRe: /<tool_call>/g },\n { tag: '<invoke ', closeTag: '</invoke>', openRe: /<invoke\\s+name=[\"']([\\w]+)[\"']>/g },\n { tag: '<function_calls>', closeTag: '</function_calls>', openRe: /<function_calls>/g },\n { tag: '[TOOL_CALL]', closeTag: '[/TOOL_CALL]', openRe: /\\[TOOL_CALL\\]/g },\n ];\n let result = text;\n for (const { tag, closeTag, openRe } of startTags) {\n let m: RegExpExecArray | null;\n while ((m = openRe.exec(result)) !== null) {\n // \u68C0\u67E5 startIdx \u4E4B\u540E\u662F\u5426\u8FD8\u6709 closeTag\n const tail = result.substring(m.index);\n const closeIdx = tail.indexOf(closeTag);\n if (closeIdx === -1) {\n // \u672A\u95ED\u5408 \u2014 \u5220\u8D77\u59CB\u6807\u7B7E + \u4E4B\u540E\u6240\u6709\u5185\u5BB9 (\u5230 \\n\\n \u6216 end)\n const nextBlank = result.indexOf('\\n\\n', m.index);\n const endIdx = nextBlank === -1 ? result.length : nextBlank;\n result = result.substring(0, m.index) + result.substring(endIdx);\n break; // \u91CD\u65B0\u5F00\u59CB\u672C tag \u626B\u63CF\n }\n }\n }\n return result;\n}\n\n/** \u4ECE <invoke>...</invoke> inner XML \u62BD <command> / <args> / <parameter> \u7B80\u6613 args */\nfunction extractSimpleArgs(inner: string): Record<string, string> {\n const args: Record<string, string> = {};\n // <parameter name=\"X\">value</parameter>\n const paramRe = /<parameter\\s+name=[\"'](\\w+)[\"']>([\\s\\S]*?)<\\/parameter>/g;\n let m: RegExpExecArray | null;\n while ((m = paramRe.exec(inner)) !== null) {\n args[m[1]] = m[2].trim();\n }\n // <param name=\"X\">value</param>\n if (Object.keys(args).length === 0) {\n const shortRe = /<param\\s+name=[\"'](\\w+)[\"']>([\\s\\S]*?)<\\/param>/g;\n while ((m = shortRe.exec(inner)) !== null) {\n args[m[1]] = m[2].trim().replace(/^[\"']|[\"']$/g, '');\n }\n }\n // <command>X</command> + <args>Y</args>\n if (Object.keys(args).length === 0) {\n const cmdM = inner.match(/<command>([\\s\\S]*?)<\\/command>/);\n const argsM = inner.match(/<args>([\\s\\S]*?)<\\/args>/);\n if (cmdM) {\n const cmd = cmdM[1].trim();\n // auto-split (\u8DDF parse-tool-call \u4E00\u81F4: \u542B\u7A7A\u683C \u2192 split)\n if (cmd.includes(' ') && !argsM) {\n const parts = cmd.split(/\\s+/);\n args.command = parts[0];\n args.args = parts.slice(1).join(' ');\n } else {\n args.command = cmd;\n }\n }\n if (argsM) args.args = argsM[1].trim();\n }\n return args;\n}\n", "/**\n * message-renderer.ts \u2014 \u5BF9\u8BDD\u663E\u793A UI (2026-06-15 \u4ECE client.js \u62C6\u51FA, .js \u2192 .ts)\n *\n * \u804C\u8D23 (\u5355\u4E00):\n * - \u628A user / AI \u5185\u5BB9\u6E32\u67D3\u6210 DOM \u6C14\u6CE1 (\u542B marked.parse \u6E32\u67D3)\n * - \u5904\u7406\u6D41\u5F0F token \u7D2F\u79EF (textNode \u589E\u91CF, O(1), \u4E0D\u91CD\u6392)\n * - finalize \u6D41\u5F0F\u6D88\u606F\u4E3A\u6B63\u5F0F AI \u6C14\u6CE1\n * - \u6298\u53E0 think / environment_details \u5757\n * - \u590D\u5236 / \u91CD\u65B0\u56DE\u7B54 / \u84B8\u998F\u4E3A\u5224\u65AD \u6309\u94AE\n * - \u6302\u8F7D step-timeline (2026-06-15) \u6B65\u9AA4\u72B6\u6001\u6761\u5230\u6BCF\u6761 AI \u6D88\u606F\u5185\n *\n * \u72B6\u6001 (\u672C\u6A21\u5757\u79C1\u6709):\n * - streamingMessageEl / streamingTextNode / streamingText\n * - lastUserCommand / lastAiContent (\u53BB\u91CD)\n *\n * \u4F9D\u8D56 (import):\n * - \u6D4F\u89C8\u5668 API (document, marked, fetch, navigator)\n * - ./step-timeline (4 \u72B6\u6001\u6B65\u9AA4\u6761\u6A21\u5757)\n *\n * \u4E0D\u4F9D\u8D56 (\u96F6\u4E1A\u52A1 import, \u9632\u5FAA\u73AF):\n * - \u4E0D import client.js\n * - \u4E0D import \u4EFB\u4F55\u4E1A\u52A1\u6A21\u5757\n *\n * \u8F93\u5165 (\u4ECE client.js \u8C03\u7528):\n * - addMessage(content, type, save, container, usedIds, ctx)\n * - handleStreamTokenEvent({ streamType, content }, ctx)\n * - finalizeTimelineAsMessage(ctx)\n * - handleStepEvent({ type: 'step_start'|'step_done'|'step_error', ... }, ctx)\n * - escapeHtml(s)\n * - getMessagesContainerForCurrent(currentChannelId, messagesContainers, messagesEl)\n *\n * \u8F93\u51FA (DOM):\n * - .message / .bubble / .think-container / .env-container\n * - .message-streaming (\u6D41\u5F0F\u671F\u95F4, finalize \u65F6\u79FB\u9664)\n * - .message-actions (\u590D\u5236/\u91CD\u65B0\u56DE\u7B54/\u84B8\u998F\u6309\u94AE)\n * - .used-judgments-link (P0.5 \u53CD\u5411\u5F15\u7528)\n * - .step-timeline (\u6C14\u6CE1\u5185\u6B65\u9AA4\u72B6\u6001\u6761, \u7531 ./step-timeline \u6E32\u67D3)\n */\n\n// ---------------------------------------------------------------------------\n// \u7C7B\u578B\u5B9A\u4E49\n// ---------------------------------------------------------------------------\nimport {\n mountStepTimeline,\n pushStepToTimeline,\n migrateStepTimeline,\n getStepTimeline,\n} from './step-timeline.js';\n// 2026-07-01 (v0.2.6): \u5171\u4EAB\u540E\u7AEF\u5207 LLM \u8F93\u51FA. \u6D88\u9664 <invoke>/<function_calls>/<tool_call>\n// \u7B49\u5404\u79CD LLM \u683C\u5F0F\u5728\u524D\u7AEF\u6C14\u6CE1\u91CC\u51FA\u73B0\u7684 bug.\nimport { segmentChatReply } from '../../agents/chat-segmenter.js';\nexport type MessageType = 'user' | 'ai' | 'system' | 'error';\n\nexport interface StreamTokenEvent {\n streamType: 'token' | 'thinking';\n content: string;\n}\n\nexport interface RendererCtx {\n messagesEl: HTMLElement | null;\n messagesContainers: Map<string, HTMLElement>;\n currentChannelId: string | null;\n /** 2026-07-01 (v0.2.6): \u6CE8\u518C\u5230 client \u7684\u5DE5\u5177\u540D\u96C6\u5408 \u2014 segmenter \u7528\u5B83\u51B3\u5B9A tool_call \u6BB5\u662F\u5426\u4FDD\u7559 */\n knownToolNames?: Set<string>;\n /** tool_call segment \u6E32\u67D3\u65F6\u56DE\u8C03 (\u5916\u5C42\u6302\u5230 step-timeline) */\n toolCallCallback?: (tool: { name: string; args: Record<string, string> }, hostEl: HTMLElement) => void;\n lastUsedJudgmentIds?: string[];\n /** \u5F15\u7528 client.js \u51FD\u6570, \u907F\u514D\u5FAA\u73AF import */\n openJudgmentsModalWithFilter?: (ids: string[]) => void;\n workflowDisplayEl?: HTMLElement | null;\n /**\n * B-3 (2026-06-15) 3 \u72B6\u6001\u673A\u56DE\u8C03:\n * - setTimelineState('streaming'): \u9996\u4E2A token \u6765\u65F6\u5207\u5230 streaming (\u84DD\u5FBD)\n * - setTimelineState('done'): finalize \u65F6\u5207\u5230 done (\u7EFF\u5FBD), 1.5s \u540E\u81EA\u52A8 hide\n * \u8D70 ctx \u6CE8\u5165\u800C\u975E\u76F4\u63A5 import, \u4FDD\u6301 message-renderer \u96F6\u4E1A\u52A1\u4F9D\u8D56.\n */\n setTimelineState?: (state: 'idle' | 'loading' | 'streaming' | 'done') => void;\n}\n\nexport interface UsedJudgmentsLink {\n length: number;\n ids: string[];\n}\n\n// ---------------------------------------------------------------------------\n// \u6A21\u5757\u79C1\u6709\u72B6\u6001\n// ---------------------------------------------------------------------------\nlet streamingMessageEl: HTMLElement | null = null;\nlet streamingTextNode: Text | null = null;\nlet streamingText = '';\nlet lastUserCommand = '';\nlet lastAiContent = '';\n\n/**\n * 2026-06-17: \u6D41\u5F0F\u72B6\u6001\u67E5\u8BE2 \u2014 \u5BA2\u6237\u7AEF\u7528\u5B83\u5728\u6536\u5230 server `ai` \u4E8B\u4EF6\u65F6\u5224\u65AD\u662F\u5426\u9700\u8981\u8DF3\u8FC7 addMessage,\n * \u907F\u514D\u548C\u540E\u7EED `done` \u2192 finalizeTimelineAsMessage \u4EA7\u751F\u53CC\u6C14\u6CE1.\n * \u6D41\u5F0F\u8FC7\u7A0B\u4E2D (streamingText > 0) server \u63A8 `ai(content=fullResponse)` \u65F6\u8DF3\u8FC7,\n * \u771F\u6B63\u6E32\u67D3\u8D70 `done` \u89E6\u53D1\u7684 finalizeTimelineAsMessage.\n */\nexport function hasStreamingText(): boolean {\n return streamingText.length > 0;\n}\n\n// 2026-07-06: SSE \u91CD\u8FDE\u6062\u590D\u7528 \u2014 \u628A streamingText \u66FF\u6362\u6210 server \u7ED9\u7684 fullContent\n// \u7136\u540E caller \u8C03 finalizeTimelineAsMessage \u628A\u5B83\u843D\u5B9A\u4E3A\u6B63\u5F0F\u6C14\u6CE1\nexport function replaceStreamingText(fullContent: string): void {\n if (!streamingTextNode || !streamingMessageEl) {\n // \u8FD8\u6CA1\u542F\u52A8\u6D41\u5F0F\u5143\u7D20 \u2014 \u4E0D\u52A8, \u8BA9 caller \u81EA\u5DF1 addMessage\n return;\n }\n streamingTextNode.nodeValue = String(fullContent || '');\n streamingText = String(fullContent || '');\n}\n\n// 2026-07-06: SSE \u91CD\u8FDE\u65F6 server \u8BF4\"\u8FD8\u5728\u751F\u6210\", \u628A\u73B0\u6709\u7684 streamingMessageEl \u586B\u4E0A partialText\n// \u8BA9\u7528\u6237\u770B\u5230 AI \u5728\u751F\u6210\u4E2D\u7684\u72B6\u6001, \u4E0D\u4F1A\u5361\u6B7B\nexport function injectRecoveredText(partialText: string, ctx: RendererCtx = { messagesEl: null, messagesContainers: new Map(), currentChannelId: null }): void {\n if (streamingMessageEl && streamingTextNode) {\n streamingTextNode.nodeValue = String(partialText || '');\n streamingText = String(partialText || '');\n return;\n }\n // \u8FD8\u6CA1\u6D41\u5F0F\u5143\u7D20 \u2014 \u521B\u5EFA\u4E00\u4E2A\u7A7A\u6D41\u5F0F\u5BB9\u5668, \u7136\u540E\u628A partialText \u704C\u8FDB\u53BB, \u7528\u6237\u770B\u5230\"AI \u5728\u601D\u8003\"\n handleStreamTokenEvent(\n {\n type: 'token',\n streamType: 'token',\n content: String(partialText || ''),\n delta: String(partialText || ''),\n } as any,\n ctx\n );\n}\n\n// \u6EDA\u52A8\u9650\u9891 (60ms 16fps, \u51CF reflow)\nlet scrollToBottomTimer: ReturnType<typeof setTimeout> | null = null;\nfunction scheduleScrollToBottom(container: HTMLElement | null): void {\n if (!container) return;\n if (scrollToBottomTimer) return;\n scrollToBottomTimer = setTimeout(() => {\n container.scrollTop = container.scrollHeight;\n scrollToBottomTimer = null;\n }, 60);\n}\n\n// ---------------------------------------------------------------------------\n// \u5BB9\u5668\u9009\u62E9 (\u4E3B\u5165\u53E3\u6216 per-channel \u5BB9\u5668)\n// ---------------------------------------------------------------------------\nexport function getMessagesContainerForCurrent(\n currentChannelId: string | null,\n messagesContainers: Map<string, HTMLElement>,\n messagesEl: HTMLElement | null\n): HTMLElement | null {\n if (currentChannelId && messagesContainers.get(currentChannelId)) {\n return messagesContainers.get(currentChannelId) || null;\n }\n return messagesEl;\n}\n\n// ---------------------------------------------------------------------------\n// HTML escape (\u4F9B think / env \u6298\u53E0\u5757\u7528)\n// ---------------------------------------------------------------------------\nexport function escapeHtml(s: unknown): string {\n return String(s ?? '').replace(/[&<>\"']/g, (c) => ({\n '&': '&', '<': '<', '>': '>', '\"': '"', \"'\": '''\n }[c] as string));\n}\n\n// ---------------------------------------------------------------------------\n// \u4E3B\u5165\u53E3: \u6E32\u67D3\u4E00\u6761\u6D88\u606F\u6C14\u6CE1 (user / ai / system)\n// ---------------------------------------------------------------------------\nexport function addMessage(\n content: string,\n type: MessageType,\n save: boolean = true,\n container?: HTMLElement | null,\n usedJudgmentIds: string[] = [],\n ctx: RendererCtx = { messagesEl: null, messagesContainers: new Map(), currentChannelId: null }\n): void {\n const messagesEl = ctx.messagesEl || (typeof document !== 'undefined' ? document.getElementById('messages') : null);\n const messagesContainers = ctx.messagesContainers || new Map<string, HTMLElement>();\n const currentChannelId = ctx.currentChannelId;\n const msgContainer = container || getMessagesContainerForCurrent(currentChannelId, messagesContainers, messagesEl);\n\n // \u5185\u5B58\u4FDD\u62A4: \u5355\u4E2A channel \u5BB9\u5668\u8D85\u8FC7 200 \u6761, \u65E7\u4F1A\u8BDD\u52A0\u8F7D\u65F6\u6DD8\u6C70\u6700\u65E7\n if (!save && msgContainer && msgContainer.children.length > 200) {\n const toRemove = msgContainer.children.length - 200;\n for (let i = 0; i < toRemove; i++) {\n const first = msgContainer.firstElementChild;\n if (first) msgContainer.removeChild(first);\n }\n }\n\n // \u53BB\u91CD (save=true \u65F6)\n if (save) {\n const lastContent = type === 'user' ? lastUserCommand : lastAiContent;\n if (lastContent && content === lastContent) {\n console.log(`[addMessage] \u8DF3\u8FC7\u91CD\u590D\u7684 ${type} \u6D88\u606F`);\n return;\n }\n if (type === 'user') lastUserCommand = content;\n else lastAiContent = content;\n }\n\n const div = document.createElement('div');\n div.className = `message message-${type}`;\n\n // 2026-07-01 (v0.2.6 \u524D\u540E\u7AEF\u5206\u79BB): \u6539\u7528 chat-segmenter \u7EAF\u51FD\u6570\u5207 LLM \u8F93\u51FA.\n // \u4E4B\u524D 8 \u884C\u6B63\u5219\u6F0F minimax <invoke> / Qwen function_calls / <tool_call> / {tool:..}.\n // \u5355\u4E00\u6765\u6E90: src/agents/chat-segmenter.ts (server + client \u5171\u4EAB).\n // knownToolNames: \u7528 ctx \u4F20\u5165\u7684\u6CE8\u518C\u8868, fallback \u7A7A\u96C6 (\u4E0D\u8BC6\u522B tool_call \u5C31 strip \u4E0D\u663E\u793A).\n const knownToolNames = (ctx && ctx.knownToolNames) || new Set<string>();\n const segments = segmentChatReply(content, { knownToolNames });\n\n // \u6CA1\u6709\u53EF\u663E\u793A\u7684 segment, \u4E0D\u4E0A\u5C4F\n if (segments.length === 0) {\n return;\n }\n\n // \u6E32\u67D3\u5404 segment (\u6309 type \u8D70\u4E0D\u540C\u5BB9\u5668)\n let thinkContainer: HTMLElement | null = null;\n let renderedAny = false;\n for (const seg of segments) {\n if (seg.type === 'think' && seg.content) {\n thinkContainer = buildThinkContainer(seg.content);\n div.appendChild(thinkContainer);\n renderedAny = true;\n } else if (seg.type === 'env_details' && seg.content) {\n div.appendChild(buildEnvContainer(seg.content));\n renderedAny = true;\n } else if (seg.type === 'text' && seg.content) {\n if (thinkContainer) div.appendChild(thinkContainer);\n thinkContainer = null;\n div.appendChild(buildBubble(seg.content, type));\n renderedAny = true;\n } else if (seg.type === 'final' && seg.content) {\n if (thinkContainer) div.appendChild(thinkContainer);\n thinkContainer = null;\n // final \u6BB5\u6E32\u67D3\u4E3A\u7279\u6B8A\u6C14\u6CE1 (\u9876\u90E8\u6709\u6807\u8BB0, \u8868\u793A LLM \u663E\u5F0F\u7EC8\u6B62)\n const finalEl = buildBubble(seg.content, type);\n finalEl.classList.add('bubble-final');\n div.appendChild(finalEl);\n renderedAny = true;\n } else if (seg.type === 'tool_call' && seg.tool) {\n // tool_call segment \u4E0D\u6E32\u67D3\u6587\u5B57 \u2014 \u8D70 step-timeline (\u6B65\u9AA4\u72B6\u6001\u6761)\n // \u8FD9\u91CC\u53EA\u8BB0\u5F55\u5230 ctx \u8BA9\u5916\u5C42\u5728 addMessage \u540E\u6302\u5230 timeline\n if (ctx && ctx.toolCallCallback) {\n ctx.toolCallCallback(seg.tool, div);\n }\n renderedAny = true; // \u5373\u4F7F\u6CA1\u6587\u5B57, tool_call \u4E5F\u7B97\"\u6709\u610F\u4E49\"\n }\n }\n if (!renderedAny) {\n return; // \u6CA1\u6709\u6E32\u67D3\u4EFB\u4F55\u4E1C\u897F, \u4E0D\u4E0A\u5C4F\n }\n\n // \u7EAF\u6587\u672C\u7528\u4E8E\u590D\u5236\u6309\u94AE \u2014 \u73B0\u5728\u4ECE segments \u62FC, \u4E0D\u518D\u4F9D\u8D56 cleanContent \u53D8\u91CF\n const rawContent = segments\n .filter(s => s.type === 'text' || s.type === 'final')\n .map(s => s.content || '')\n .join('\\n');\n\n // \u65F6\u95F4\n const time = document.createElement('div');\n time.className = 'time';\n time.textContent = new Date().toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' });\n\n // AI \u6D88\u606F\u64CD\u4F5C\u6309\u94AE\n if (type === 'ai') {\n div.appendChild(buildMessageActions(div, rawContent, ctx));\n }\n\n // P0.5 \u53CD\u5411\u5F15\u7528\n if (type === 'ai' && Array.isArray(usedJudgmentIds) && usedJudgmentIds.length > 0) {\n const link = document.createElement('a');\n link.className = 'used-judgments-link';\n link.textContent = `\uD83D\uDCCE \u53C2\u8003 ${usedJudgmentIds.length} \u6761\u539F\u5219`;\n link.onclick = (e) => {\n e.preventDefault();\n if (typeof ctx.openJudgmentsModalWithFilter === 'function') {\n ctx.openJudgmentsModalWithFilter(usedJudgmentIds);\n }\n };\n div.appendChild(link);\n }\n\n // 2026-06-15: \u6BCF\u6761 AI \u6D88\u606F\u6302\u4E00\u4E2A\u7A7A step-timeline \u5360\u4F4D (\u7528\u6237\u51B3\u7B56)\n // \u540E\u7EED step_start/step_done \u4E8B\u4EF6\u4F1A\u901A\u8FC7 handleStepEvent \u63A8\u5165\n if (type === 'ai' && msgContainer) {\n mountStepTimeline(div, currentChannelId);\n }\n\n div.appendChild(time);\n if (msgContainer) {\n msgContainer.appendChild(div);\n scheduleScrollToBottom(msgContainer);\n }\n}\n\n// ---------------------------------------------------------------------------\n// \u79C1\u6709: think \u6298\u53E0\u5757\n// ---------------------------------------------------------------------------\nfunction buildThinkContainer(thinkContent: string): HTMLElement {\n const container = document.createElement('div');\n container.className = 'think-container';\n const toggle = document.createElement('div');\n toggle.className = 'think-toggle';\n toggle.innerHTML = '\uD83D\uDCAD \u601D\u8003\u8FC7\u7A0B <span class=\"think-arrow\">\u25B8</span>';\n toggle.onclick = function() {\n const details = container.querySelector('.think-content') as HTMLElement | null;\n const arrow = toggle.querySelector('.think-arrow') as HTMLElement | null;\n if (!details || !arrow) return;\n if (details.style.display === 'none') {\n details.style.display = 'block';\n arrow.textContent = '\u25BE';\n } else {\n details.style.display = 'none';\n arrow.textContent = '\u25B8';\n }\n };\n const content = document.createElement('div');\n content.className = 'think-content';\n content.style.display = 'none';\n content.innerHTML = `<pre>${escapeHtml(thinkContent)}</pre>`;\n container.appendChild(toggle);\n container.appendChild(content);\n return container;\n}\n\n// ---------------------------------------------------------------------------\n// \u79C1\u6709: environment_details \u6298\u53E0\u5757\n// ---------------------------------------------------------------------------\nfunction buildEnvContainer(envDetails: string): HTMLElement {\n const container = document.createElement('div');\n container.className = 'env-container';\n const toggle = document.createElement('div');\n toggle.className = 'env-toggle';\n toggle.innerHTML = '\u2699\uFE0F \u73AF\u5883\u4FE1\u606F <span class=\"env-arrow\">\u25B8</span>';\n toggle.onclick = function() {\n const details = container.querySelector('.environment-details') as HTMLElement | null;\n const arrow = toggle.querySelector('.env-arrow') as HTMLElement | null;\n if (!details || !arrow) return;\n if (details.style.display === 'none') {\n details.style.display = 'block';\n arrow.textContent = '\u25BE';\n } else {\n details.style.display = 'none';\n arrow.textContent = '\u25B8';\n }\n };\n const content = document.createElement('div');\n content.className = 'environment-details';\n content.style.display = 'none';\n content.innerHTML = `<pre>${escapeHtml(envDetails)}</pre>`;\n container.appendChild(toggle);\n container.appendChild(content);\n return container;\n}\n\n// ---------------------------------------------------------------------------\n// \u79C1\u6709: \u6C14\u6CE1 (marked.parse \u6E32\u67D3)\n// ---------------------------------------------------------------------------\nfunction buildBubble(text: string, type: MessageType): HTMLElement {\n const bubble = document.createElement('div');\n bubble.className = `bubble bubble-${type}`;\n // \u5B89\u5168\u964D\u7EA7: CDN \u52A0\u8F7D\u5931\u8D25\u65F6 marked \u662F escape \u7248\u672C, \u8FD9\u91CC\u4E0D\u9700\u8981\u4E8C\u6B21 escape\n // window.marked \u5728 ts \u7C7B\u578B\u4E0A\u4E0D\u4E00\u5B9A\u6709, \u7528 any \u515C\u5E95\n const marked = (window as any).marked;\n bubble.innerHTML = marked ? marked.parse(text) : escapeHtml(text);\n return bubble;\n}\n\n// ---------------------------------------------------------------------------\n// \u79C1\u6709: \u6D88\u606F\u64CD\u4F5C\u6309\u94AE (\u590D\u5236 / \u91CD\u65B0\u56DE\u7B54 / \u84B8\u998F\u4E3A\u5224\u65AD)\n// ---------------------------------------------------------------------------\nfunction buildMessageActions(div: HTMLElement, rawContent: string, ctx: RendererCtx): HTMLElement {\n const actions = document.createElement('div');\n actions.className = 'message-actions';\n\n // \u590D\u5236\n const copyBtn = document.createElement('button');\n copyBtn.className = 'action-btn copy-btn';\n copyBtn.innerHTML = copyIcon() + ' \u590D\u5236';\n copyBtn.title = '\u590D\u5236\u6D88\u606F';\n copyBtn.onclick = () => {\n navigator.clipboard.writeText(rawContent).then(() => {\n copyBtn.innerHTML = checkIcon() + ' \u5DF2\u590D\u5236';\n setTimeout(() => { copyBtn.innerHTML = copyIcon() + ' \u590D\u5236'; }, 2000);\n });\n };\n actions.appendChild(copyBtn);\n\n // \u84B8\u998F\u4E3A\u5224\u65AD\n const saveJudgmentBtn = document.createElement('button');\n saveJudgmentBtn.className = 'action-btn save-as-judgment';\n saveJudgmentBtn.title = 'AI \u84B8\u998F\u4E3A 30-80 \u5B57\u5224\u65AD\u529B + \u81EA\u52A8\u6F14\u5316\u5BF9\u9F50';\n saveJudgmentBtn.setAttribute('data-decision', rawContent.substring(0, 800));\n if (ctx.currentChannelId) saveJudgmentBtn.setAttribute('data-channel-id', ctx.currentChannelId);\n saveJudgmentBtn.innerHTML = shieldIcon() + ' \u84B8\u998F\u4E3A\u5224\u65AD';\n actions.appendChild(saveJudgmentBtn);\n\n // \u91CD\u65B0\u56DE\u7B54\n const regenBtn = document.createElement('button');\n regenBtn.className = 'action-btn regenerate-btn';\n regenBtn.innerHTML = refreshIcon(false) + ' \u91CD\u65B0\u56DE\u7B54';\n regenBtn.title = '\u91CD\u65B0\u751F\u6210\u56DE\u590D';\n regenBtn.onclick = () => {\n regenBtn.innerHTML = refreshIcon(true) + ' \u751F\u6210\u4E2D...';\n regenBtn.disabled = true;\n const messages = div.parentElement?.querySelectorAll('.message') || [];\n let lastUserMsg = '';\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i];\n if (msg.classList.contains('message-user')) {\n const bubble = msg.querySelector('.bubble') as HTMLElement | null;\n if (bubble) { lastUserMsg = bubble.textContent || bubble.innerText || ''; break; }\n }\n }\n fetch('/regenerate', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ channelId: ctx.currentChannelId, userMessage: lastUserMsg })\n }).then(res => {\n if (!res.ok) throw new Error('regenerate failed');\n }).catch(err => {\n console.error('\u91CD\u65B0\u751F\u6210\u5931\u8D25:', err);\n regenBtn.innerHTML = refreshIcon(false) + ' \u5931\u8D25';\n setTimeout(() => {\n regenBtn.innerHTML = refreshIcon(false) + ' \u91CD\u65B0\u56DE\u7B54';\n regenBtn.disabled = false;\n }, 2000);\n });\n };\n actions.appendChild(regenBtn);\n return actions;\n}\n\nfunction copyIcon(): string { return '<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"></rect><path d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"></path></svg>'; }\nfunction checkIcon(): string { return '<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><polyline points=\"20 6 9 17 4 12\"></polyline></svg>'; }\nfunction shieldIcon(): string { return '<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M12 2L4 6v6c0 5 3.5 9.5 8 10 4.5-.5 8-5 8-10V6l-8-4z\"></path><path d=\"M9 12l2 2 4-4\"></path></svg>'; }\nfunction refreshIcon(spin: boolean = false): string { const cls = spin ? ' class=\"spin\"' : ''; return `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"${cls}><path d=\"M21 2v6h-6M3 12a9 9 0 0 1 15-6.7L21 8M3 22v-6h6M21 12a9 9 0 0 1-15 6.7L3 16\"></path></svg>`; }\n\n// ---------------------------------------------------------------------------\n// \u6D41\u5F0F token \u5904\u7406 (textNode \u589E\u91CF, \u4E0D\u91CD\u6392)\n// ---------------------------------------------------------------------------\nexport function handleStreamTokenEvent(data: StreamTokenEvent, ctx: RendererCtx = { messagesEl: null, messagesContainers: new Map(), currentChannelId: null }): void {\n const messagesEl = ctx.messagesEl || (typeof document !== 'undefined' ? document.getElementById('messages') : null);\n const messagesContainers = ctx.messagesContainers || new Map<string, HTMLElement>();\n const currentChannelId = ctx.currentChannelId;\n const container = getMessagesContainerForCurrent(currentChannelId, messagesContainers, messagesEl);\n if (!container) return;\n const delta = data.content || '';\n if (!delta) return;\n\n if (!streamingMessageEl || !streamingMessageEl.isConnected) {\n // \u65B0\u5EFA\u6D41\u5F0F\u6D88\u606F\n streamingMessageEl = document.createElement('div');\n streamingMessageEl.className = 'message message-ai message-streaming';\n streamingTextNode = document.createTextNode('');\n streamingMessageEl.appendChild(streamingTextNode);\n streamingText = '';\n // 2026-06-15: \u6D41\u5F0F\u671F\u95F4\u4E5F\u6302\u4E00\u4E2A\u7A7A step-timeline \u5360\u4F4D \u2014 step_start/done/error \u4E8B\u4EF6\n // \u8D70 handleStepEvent, getStepTimeline \u627E\u5230\u8FD9\u4E2A\u6D41\u5F0F\u5143\u7D20\u5185\u7684 timeline \u63A8\u5165\n mountStepTimeline(streamingMessageEl, currentChannelId);\n container.appendChild(streamingMessageEl);\n // B-3: \u9996\u4E2A token \u6765\u65F6\u5207\u72B6\u6001\u5230 streaming (\u84DD\u5FBD), \u63D0\u793A\u7528\u6237 panel \u5728\u5DE5\u4F5C\n if (typeof ctx.setTimelineState === 'function') {\n ctx.setTimelineState('streaming');\n }\n scheduleScrollToBottom(container);\n }\n if (streamingTextNode) streamingTextNode.appendData(delta);\n streamingText += delta;\n scheduleScrollToBottom(container);\n}\n\n// ---------------------------------------------------------------------------\n// \u6D41\u5F0F\u6D88\u606F finalize (done \u4E8B\u4EF6): \u79FB\u9664\u6D41\u5F0F\u5143\u7D20, \u8D70 addMessage \u8D70 marked.parse\n// ---------------------------------------------------------------------------\nexport function finalizeTimelineAsMessage(ctx: RendererCtx = { messagesEl: null, messagesContainers: new Map(), currentChannelId: null }): void {\n const messagesEl = ctx.messagesEl || (typeof document !== 'undefined' ? document.getElementById('messages') : null);\n const messagesContainers = ctx.messagesContainers || new Map<string, HTMLElement>();\n const currentChannelId = ctx.currentChannelId;\n const container = getMessagesContainerForCurrent(currentChannelId, messagesContainers, messagesEl);\n if (streamingText.trim().length > 0) {\n // 2026-06-15: finalize \u65F6\u628A streaming \u5185\u7684 step-timeline \u6574\u4F53\u642C\u5230\u65B0\u5EFA\u7684\u6B63\u5F0F AI message \u5185\n // addMessage \u4F1A\u5EFA\u4E00\u4E2A\u65B0 timeline \u5360\u4F4D, \u5148\u8BB0\u4E0B streaming \u7684\u5F15\u7528, addMessage \u540E\u518D\u8FC1\u79FB\n // \u907F\u514D\u8282\u70B9\u4ECE 0 \u91CD\u6E32 (10+ \u6B65\u7684\u4EFB\u52A1, \u91CD\u6E32\u95EA\u70C1\u4F1A\u5F88\u5389\u5BB3)\n const oldStreamingEl = streamingMessageEl;\n if (oldStreamingEl && oldStreamingEl.parentNode) {\n oldStreamingEl.parentNode.removeChild(oldStreamingEl);\n }\n addMessage(streamingText, 'ai', true, container, ctx.lastUsedJudgmentIds || [], ctx);\n if (oldStreamingEl && container) {\n // \u627E\u521A addMessage \u521B\u5EFA\u7684\u6700\u540E\u4E00\u6761 ai message\n const newAiMsg = container.querySelector('.message-ai:last-of-type') as HTMLElement | null;\n if (newAiMsg && newAiMsg !== oldStreamingEl) {\n migrateStepTimeline(oldStreamingEl, newAiMsg);\n }\n }\n }\n // \u91CD\u7F6E\u6D41\u5F0F\u72B6\u6001\n streamingMessageEl = null;\n streamingTextNode = null;\n streamingText = '';\n // B-3: finalize \u65F6\u5207 done \u72B6\u6001 (\u7EFF\u5FBD), hide \u5EF6\u8FDF\u7531 client.js \u7684 hideTimelinePanel \u7EDF\u4E00\u7BA1\n if (typeof ctx.setTimelineState === 'function') {\n ctx.setTimelineState('done');\n }\n}\n\n// ---------------------------------------------------------------------------\n// \u7528\u6237\u547D\u4EE4\u88C5\u9970\u6761 (showUserCommand) \u2014 2026-06-16 \u6574\u6BB5\u5220\u9664, SSE \u5DF2\u8D70 addMessage \u8DEF\u5F84\n// 2026-06-11 \u4FEE: \u4E0D\u518D\u8D70 showUserCommand (\u203A \u88C5\u9970\u6761) \u8DEF\u5F84 \u2014 \u4F1A\u548C user bubble \u53CC\u91CD\u663E\u793A\n\n// ---------------------------------------------------------------------------\n// 2026-06-15: step-timeline \u4E8B\u4EF6\u5165\u53E3 (server \u63A8 step_start/step_done/step_error)\n// \u627E\u5230\u5F53\u524D\u6B63\u5728\u6D41\u5F0F / \u6700\u540E\u4E00\u6761 AI \u6D88\u606F\u7684 timeline \u5BB9\u5668, \u63A8\u5165\n// ---------------------------------------------------------------------------\nexport interface StepEvent {\n type: 'step_start' | 'step_done' | 'step_error';\n tool?: string;\n content?: string;\n success?: boolean;\n output?: string;\n error?: string;\n args?: Record<string, unknown>;\n}\n\nexport function handleStepEvent(data: StepEvent, ctx: RendererCtx = { messagesEl: null, messagesContainers: new Map(), currentChannelId: null }): void {\n const messagesEl = ctx.messagesEl || (typeof document !== 'undefined' ? document.getElementById('messages') : null);\n const messagesContainers = ctx.messagesContainers || new Map<string, HTMLElement>();\n const currentChannelId = ctx.currentChannelId;\n const container = getMessagesContainerForCurrent(currentChannelId, messagesContainers, messagesEl);\n if (!container) return;\n if (!data || !data.type) return;\n\n // 1. \u4F18\u5148\u7528\u6B63\u5728\u6D41\u5F0F\u7684\u5143\u7D20 (\u6D41\u5F0F\u671F\u95F4\u80FD\u5373\u65F6\u770B\u5230)\n let target: HTMLElement | null = streamingMessageEl && streamingMessageEl.isConnected\n ? streamingMessageEl\n : null;\n // 2. \u5426\u5219\u7528\u6700\u540E\u4E00\u6761 AI message\n if (!target) {\n const aiMsgs = container.querySelectorAll('.message-ai');\n if (aiMsgs.length === 0) return;\n target = aiMsgs[aiMsgs.length - 1] as HTMLElement;\n }\n if (!target) return;\n\n const timeline = getStepTimeline(target);\n if (!timeline) return;\n\n pushStepToTimeline(timeline, data.type, {\n tool: data.tool || 'unknown',\n args: data.args,\n success: data.success,\n output: data.output,\n error: data.error,\n });\n}\n\n// ---------------------------------------------------------------------------\n// \u91CD\u7F6E\u6A21\u5757\u72B6\u6001 (\u5207\u9891\u9053\u65F6\u8C03\u7528)\n// ---------------------------------------------------------------------------\nexport function resetRendererState(): void {\n streamingMessageEl = null;\n streamingTextNode = null;\n streamingText = '';\n lastUserCommand = '';\n lastAiContent = '';\n if (scrollToBottomTimer) {\n clearTimeout(scrollToBottomTimer);\n scrollToBottomTimer = null;\n }\n}\n\nexport const MessageRenderer = {\n addMessage,\n handleStreamTokenEvent,\n finalizeTimelineAsMessage,\n handleStepEvent,\n escapeHtml,\n getMessagesContainerForCurrent,\n resetRendererState,\n};\n\ndeclare global {\n interface Window {\n MR?: typeof MessageRenderer;\n }\n}\n\nif (typeof window !== 'undefined') {\n window.MR = MessageRenderer;\n}\n", "/**\n * safe-name.ts \u2014 \u901A\u7528 channel / peer / agent \u540D\u515C\u5E95 (2026-07-06)\n *\n * \u6D4F\u89C8\u5668\u7AEF helper. \u9632\u6B62 name=undefined/null/'undefined'/'null' \u5B57\u4E32\u65F6\n * UI \u6E32\u67D3\u5B57\u9762\u91CF \"undefined\" / \"null\". \u7528\u4E8E\u6240\u6709\u5C55\u793A\u7528\u6237\u7ED9\u8D77\u540D\u5B57\u7684\u4F4D\u7F6E.\n *\n * \u7528\u6CD5:\n * import { safeName, safeChannelName, safePeerName } from './safe-name.js';\n * safeChannelName(ch.name); // '(\u672A\u547D\u540D)' \u9ED8\u8BA4\n * safeChannelName(ch.name, '...'); // \u81EA\u5B9A\u4E49 fallback\n * safePeerName(peer.info?.name); // 'Unknown' \u9ED8\u8BA4 (\u82F1\u6587\u573A\u666F)\n */\nexport interface SafeNameOptions {\n fallback?: string;\n /** \u89C6\u4E3A\u65E0\u6548\u7684\u5B57\u9762\u91CF\u96C6\u5408 (\u9ED8\u8BA4 ['undefined','null','NaN','']) */\n invalidLiterals?: string[];\n}\n\n/**\n * \u901A\u7528\u540D\u515C\u5E95. fallback \u9ED8\u8BA4 '(\u672A\u547D\u540D)'. \u4E2D\u6587\u573A\u666F.\n */\nexport function safeChannelName(input: unknown, fallback?: string): string {\n return safeNameInternal(input, fallback ?? '(\u672A\u547D\u540D)', ['undefined', 'null', 'NaN']);\n}\n\n/**\n * \u82F1\u6587\u573A\u666F\u515C\u5E95, \u9ED8\u8BA4 'Unknown'.\n */\nexport function safePeerName(input: unknown, fallback?: string): string {\n return safeNameInternal(input, fallback ?? 'Unknown', ['undefined', 'null', 'NaN']);\n}\n\n/**\n * \u515C\u5E95\u4EFB\u610F\u5B57\u6BB5, \u7531 caller \u6307\u5B9A\u9ED8\u8BA4 + \u65E0\u6548\u5B57\u9762\u91CF\u96C6\u5408.\n */\nexport function safeName(input: unknown, fallback: string, invalidLiterals: string[] = ['undefined', 'null', 'NaN']): string {\n return safeNameInternal(input, fallback, invalidLiterals);\n}\n\nfunction safeNameInternal(input: unknown, fallback: string, invalidLiterals: string[]): string {\n if (input === undefined || input === null) return fallback;\n if (typeof input === 'number' && Number.isNaN(input)) return fallback;\n const s = String(input).trim();\n if (!s) return fallback;\n for (const lit of invalidLiterals) {\n if (s === lit) return fallback;\n }\n return s;\n}", "\uFEFF// @ts-nocheck\n// marked \u5E93\u53EF\u80FD\u4ECE CDN \u52A0\u8F7D\u5931\u8D25, \u8FD9\u91CC\u505A\u5B89\u5168\u964D\u7EA7 (\u907F\u514D ReferenceError \u8BA9 addMessage \u6574\u4F53\u5D29\u6E83)\nif (typeof marked === 'undefined') {\n window.marked = { parse: (text) => String(text).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\\n/g, '<br>') };\n}\n\n// 2026-06-17: \u7EC8\u7AEF\u9759\u9ED8 \u2014 \u524D\u7AEF console.log \u8D70 proxy, \u9ED8\u8BA4\u4E0D\u6253 SSE \u63A5\u6536 spam.\n// \u4EC5 DevTools console \u4FA7\u9759\u9ED8, \u4E0D\u5F71\u54CD\u7EC8\u7AEF;\u6D4F\u89C8\u5668\u8C03\u8BD5\u4ECD\u53EF\u8BBE BOLLOON_VERBOSE=1 \u8D70 build \u5D4C\u5165.\n// console.error \u4ECD\u8D70\u539F\u751F (\u5F02\u5E38\u8DEF\u5F84\u53EF\u89C1).\n(function installConsoleProxy() {\n const VERBOSE = (typeof process !== 'undefined' && process.env && process.env.BOLLOON_VERBOSE === '1');\n const SUPPRESSED = ['[SSE]', '[broadcast]'];\n const orig = console.log.bind(console);\n console.log = (...args) => {\n if (VERBOSE) return orig(...args);\n const first = args[0];\n if (typeof first === 'string') {\n for (const p of SUPPRESSED) {\n if (first.startsWith(p)) return;\n }\n }\n return orig(...args);\n };\n})();\n\n// 2026-06-15: \u62C6\u51FA message-renderer \u6A21\u5757 (TS, ESM \u7F16\u8BD1).\n// \u6D4F\u89C8\u5668\u4FA7: <script type=\"module\"> \u52A0\u8F7D, \u6A21\u5757\u4E3B\u52A8\u6302\u5230 window.MR\n// \u5BA2\u6237\u7AEF\u9876\u5C42\u76F4\u63A5\u62FF window.MR (ESM deferred \u4F46 client.js \u9876\u5C42\u53EA\u53D6 ref, \u5B9E\u9645\u8C03\u7528\u5EF6\u540E\u5230 DOMContentLoaded)\n// tsx \u8DD1\u6D4B\u8BD5: \u8D70 require() \u540C\u540D\u62FF\nlet MR = {};\ntry { if (typeof require !== 'undefined') MR = require('./ui/message-renderer.js') || {}; } catch (e) { /* \u6D4F\u89C8\u5668\u6CA1 require, \u8D70 window.MR */ }\nfunction _getMR() {\n if (MR && MR.addMessage) return MR;\n if (typeof window !== 'undefined' && window.MR) return window.MR;\n return {};\n}\nconst MR_addMessage = (...args) => _getMR().addMessage?.(...args);\nconst MR_handleStreamTokenEvent = (...args) => _getMR().handleStreamTokenEvent?.(...args);\nconst MR_finalizeTimelineAsMessage = (...args) => _getMR().finalizeTimelineAsMessage?.(...args);\nconst MR_handleStepEvent = (...args) => _getMR().handleStepEvent?.(...args);\nconst MR_getMessagesContainerForCurrent = (...args) => _getMR().getMessagesContainerForCurrent?.(...args);\nconst MR_escapeHtml = (s) => _getMR().escapeHtml?.(s);\n// 2026-06-17: \u6D41\u5F0F\u72B6\u6001\u67E5\u8BE2 \u2014 'ai' \u4E8B\u4EF6\u7528\u5B83\u5728\u53CC\u6C14\u6CE1\u7ADE\u6001\u4E0B\u51B3\u5B9A\u662F\u5426\u8DF3\u8FC7 addMessage\nconst MR_hasStreamingText = () => _getMR().hasStreamingText?.() ?? false;\nconst MR_resetRendererState = () => _getMR().resetRendererState?.();\n// 2026-07-06: SSE \u91CD\u8FDE\u6062\u590D\u7528 \u2014 \u7528 server \u7ED9\u7684\u5B8C\u6574\u5185\u5BB9\u66FF\u6362\u6D41\u5F0F\u7D2F\u79EF, \u7136\u540E finalize\nconst MR_replaceStreamingText = (text: string) => _getMR().replaceStreamingText?.(text);\nconst MR_injectRecoveredText = (text: string, ctx?: any) => _getMR().injectRecoveredText?.(text, ctx ?? getRendererCtx());\n\n// ctx \u5BF9\u8C61: \u628A\u5168\u5C40\u72B6\u6001\u6253\u5305, \u907F\u514D\u786C\u5F15\u7528 client.js \u9876\u5C42 let\nfunction getRendererCtx() {\n return {\n messagesEl,\n messagesContainers,\n currentChannelId,\n lastUsedJudgmentIds,\n openJudgmentsModalWithFilter, // \u5F15\u7528 client.js \u51FD\u6570, \u901A\u8FC7\u53C2\u6570\u6CE8\u5165\u907F\u514D\u5FAA\u73AF import\n };\n}\n\n// 2026-06-16: \u5B9E\u9645\u5165\u53E3\u662F\u5E95\u90E8\u9876\u5C42\u8C03\u7528\u7684 init().\n// \u771F\u5B9E init \u6D41\u7A0B: DOMContentLoaded \u2192 \u6A21\u5757\u9876\u5C42\u540C\u6B65\u6267\u884C \u2192 \u6CE8\u518C\u6240\u6709 addEventListener \u2192 fire-and-forget init()\n// init() \u5185\u90E8: loadTheme \u2192 loadChannels \u2192 checkApiConfig \u2192 selectChannel \u2192 connect (SSE)\n// \u4E4B\u524D\u7684 ensureMRLoaded() \u662F\u6B7B\u4EE3\u7801\u6B8B\u7559 (init \u4E0D await \u5B83, \u4E5F\u6CA1\u4EBA\u8C03\u7528) \u2014 2026-06-16 \u5220\u9664.\n\nconst messagesEl = document.getElementById('messages');\nconst input = document.getElementById('input');\nconst sendBtn = document.getElementById('send');\nconst sidebar = document.getElementById('sidebar');\n// 2026-06-16 \u65B0\u589E: \u5FAA\u73AF\u8FDB\u5EA6 status bar (\u6E32\u67D3 tool='loop'|'compactor'|'recovery' \u7684 status \u4E8B\u4EF6)\nconst loopStatusBar = document.getElementById('loop-status-bar') as HTMLElement | null;\nconst loopStatusText = document.getElementById('loop-status-text');\nconst loopStatusMeta = document.getElementById('loop-status-meta');\n\n// \u628A status \u4E8B\u4EF6\u8DEF\u7531\u5230 status bar; \u53EA\u5173\u5FC3 system \u7EA7 (loop/compactor/recovery), \u5176\u4ED6\u9759\u9ED8\nconst LOOP_STATUS_TOOLS = new Set(['loop', 'compactor', 'recovery', 'system']);\n// 2026-06-16: \u4E09\u6001\u673A \u2014 loading (spinner+\u6587\u672C) / retrying (spinner + \"\u81EA\u52A8\u91CD\u8BD5\u4E2D X/N\") / done (\u68C0\u67E5\u6309\u94AE)\n// \u91CD\u8BD5\u662F server \u7AEF\u81EA\u52A8\u7684 (pi-sdk.ts promptStream \u5305 retry \u5FAA\u73AF), \u4E0D\u66B4\u9732\u6309\u94AE\u7ED9\u7528\u6237.\nlet loopBarState: 'loading' | 'retrying' | 'done' = 'loading';\nlet loopBarLastSummary: string = '';\nfunction renderLoopStatusBar(tool: string | undefined, content: string | undefined): void {\n if (!loopStatusBar || !loopStatusText) return;\n const t = String(tool || '').toLowerCase();\n if (!LOOP_STATUS_TOOLS.has(t)) {\n // \u975E system \u7EA7 status (\u4F8B\u5982 tool='shell_exec' \u5DE5\u5177\u6267\u884C\u6D88\u606F) \u2014 \u4ECD\u8D70 console.log, \u4E0D\u753B UI\n console.log('[SSE] status (tool=' + t + ', ignored by UI):', content?.slice(0, 80));\n return;\n }\n // 2026-06-16: \u62BD\u53D6 retry \u4FE1\u606F \u2014 \"\u21BB \u81EA\u52A8\u91CD\u8BD5 loop 1/3\" / \"\u26D4 loop \u81EA\u52A8\u91CD\u8BD5 3 \u6B21\u540E\u4ECD\u5931\u8D25\"\n // \u7528 meta \u663E\u793A, \u4E0D\u653E\u5230\u4E3B\u6587\u672C\u91CC\n const retryMatch = String(content || '').match(/\u81EA\u52A8\u91CD\u8BD5(?: loop)?\\s+(\\d+)\\/(\\d+)/);\n const retryFinal = /\u81EA\u52A8\u91CD\u8BD5\\s+\\d+\\s*\u6B21\u540E\u4ECD\u5931\u8D25/.test(String(content || ''));\n\n loopStatusBar.hidden = false;\n // \u4E3B\u6587\u672C\u53BB\u6389 emoji \u548C retry \u524D\u7F00, \u53EA\u7559\u5E72\u51C0\u7684\u63CF\u8FF0\n let mainText = String(content || '')\n .replace(/^[\\u{1F000}-\\u{1FFFF}\\u{2600}-\\u{27BF}]\\s*/u, '')\n .replace(/^\u21BB\\s*/, '')\n .replace(/^\u26D4\\s*/, '')\n .replace(/^\u26A0\uFE0F\\s*/, '')\n .slice(0, 200);\n loopStatusText.textContent = mainText;\n\n // retry \u6001: spinner + \"\u81EA\u52A8\u91CD\u8BD5\u4E2D X/N\" badge (\u6A59\u8272)\n if (retryMatch) {\n loopBarState = 'retrying';\n const retryEl = document.getElementById('loop-status-retry');\n if (retryEl) {\n retryEl.hidden = false;\n retryEl.textContent = `\u81EA\u52A8\u91CD\u8BD5 ${retryMatch[1]}/${retryMatch[2]}`;\n }\n } else if (retryFinal) {\n // \u6700\u7EC8\u5931\u8D25: \u8FDB\u5165 done \u6001, \u8BA9\u7528\u6237\u70B9\"\u68C0\u67E5\"\u770B\u9519\u8BEF\u8BE6\u60C5 (\u66FF\u4EE3\u624B\u52A8\u91CD\u8BD5\u6309\u94AE)\n loopBarState = 'done';\n const retryEl = document.getElementById('loop-status-retry');\n if (retryEl) retryEl.hidden = true;\n } else {\n // \u6B63\u5E38 status (loop/compactor/recovery/system) \u2192 loading \u6001\n if (loopBarState !== 'loading') loopBarState = 'loading';\n const retryEl = document.getElementById('loop-status-retry');\n if (retryEl) retryEl.hidden = true;\n }\n applyLoopBarState();\n}\n\n// 2026-06-16: \u5B8C\u6210\u6001\u7531 SSE `done` \u4E8B\u4EF6\u89E6\u53D1; \u4E0D\u4F9D\u8D56 system \u63A8 \"\u2705 \u5B8C\u6210\"\nfunction markLoopBarDone(summary?: string): void {\n loopBarState = 'done';\n if (summary) loopBarLastSummary = summary;\n applyLoopBarState();\n}\n\nfunction applyLoopBarState(): void {\n if (!loopStatusBar) return;\n loopStatusBar.dataset.state = loopBarState;\n const checkBtn = document.getElementById('loop-status-check') as HTMLButtonElement | null;\n if (checkBtn) checkBtn.hidden = loopBarState !== 'done';\n}\nfunction hideLoopStatusBar(): void {\n if (!loopStatusBar) return;\n loopStatusBar.hidden = true;\n loopBarState = 'loading';\n loopBarLastSummary = '';\n const retryEl = document.getElementById('loop-status-retry');\n if (retryEl) retryEl.hidden = true;\n applyLoopBarState();\n}\n\n// 2026-06-16: \u5B8C\u6210\u540E\u68C0\u67E5 \u2014 GET /api/loop/inspect \u62C9\u5FAA\u73AF\u4EA7\u51FA\u7684\u5DE5\u4F5C\u8BB0\u5FC6/\u5DE5\u5177\u7ED3\u679C, \u5F39 modal\nasync function inspectLoopResult(): Promise<void> {\n const checkBtn = document.getElementById('loop-status-check') as HTMLButtonElement | null;\n if (checkBtn) {\n checkBtn.disabled = true;\n checkBtn.textContent = '\u23F3 \u52A0\u8F7D...';\n }\n try {\n const r = await fetch(`/api/loop/inspect?channelId=${encodeURIComponent(currentChannelId || '')}`);\n const j = await r.json().catch(() => ({}));\n openLoopInspectModal(j);\n } catch (err) {\n console.error('[inspect] error:', err);\n if (typeof showSimpleToast === 'function') showSimpleToast('\u2717 \u68C0\u67E5\u5931\u8D25');\n } finally {\n if (checkBtn) {\n checkBtn.disabled = false;\n checkBtn.textContent = '\u2713 \u68C0\u67E5';\n }\n }\n}\n\n// 2026-06-16: \u68C0\u67E5\u7ED3\u679C modal \u2014 \u5217\u5FAA\u73AF\u4EA7\u51FA\u7684\u5DE5\u5177\u7ED3\u679C / \u538B\u7F29\u6458\u8981 / \u6700\u7EC8\u56DE\u590D\nfunction openLoopInspectModal(data: { summary?: string; steps?: Array<{ name: string; status: string; durationMs?: number; output?: string }>; finalReply?: string; tokens?: { input?: number; output?: number }; error?: string }): void {\n const existing = document.getElementById('loop-inspect-modal');\n if (existing) existing.remove();\n\n const modal = document.createElement('div');\n modal.id = 'loop-inspect-modal';\n modal.className = 'modal active';\n modal.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center;z-index:1000;';\n\n const panel = document.createElement('div');\n panel.className = 'modal-panel';\n panel.style.cssText = 'background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:20px;max-width:720px;width:90%;max-height:80vh;overflow:auto;position:relative;';\n\n const title = document.createElement('h3');\n title.textContent = '\uD83D\uDD0D \u5FAA\u73AF\u68C0\u67E5';\n title.style.cssText = 'margin:0 0 12px;font-size:16px;';\n panel.appendChild(title);\n\n const close = document.createElement('button');\n close.textContent = '\u00D7';\n close.style.cssText = 'position:absolute;top:8px;right:12px;background:transparent;border:0;font-size:24px;cursor:pointer;color:var(--text-secondary);';\n close.onclick = () => modal.remove();\n panel.appendChild(close);\n\n if (data.error) {\n const e = document.createElement('div');\n e.style.cssText = 'padding:8px 12px;background:rgba(239,68,68,0.12);color:var(--error,#ef4444);border-radius:4px;margin-bottom:12px;font-size:13px;';\n e.textContent = '\u26A0\uFE0F ' + data.error;\n panel.appendChild(e);\n }\n\n if (data.summary) {\n const s = document.createElement('div');\n s.style.cssText = 'padding:8px 12px;background:var(--bg-tertiary);border-radius:4px;margin-bottom:12px;font-size:13px;';\n s.textContent = data.summary;\n panel.appendChild(s);\n }\n\n if (data.tokens && (data.tokens.input || data.tokens.output)) {\n const t = document.createElement('div');\n t.style.cssText = 'font-size:12px;color:var(--text-muted);margin-bottom:12px;';\n t.textContent = `token: input ${data.tokens.input || 0} \u00B7 output ${data.tokens.output || 0}`;\n panel.appendChild(t);\n }\n\n if (Array.isArray(data.steps) && data.steps.length > 0) {\n const h = document.createElement('div');\n h.textContent = `\u6B65\u9AA4 (${data.steps.length})`;\n h.style.cssText = 'font-weight:600;margin-bottom:8px;';\n panel.appendChild(h);\n for (const step of data.steps) {\n const row = document.createElement('div');\n row.style.cssText = 'padding:6px 10px;margin-bottom:4px;background:var(--bg-secondary);border-left:3px solid var(--accent);border-radius:3px;font-size:12px;';\n const icon = step.status === 'ok' || step.status === 'completed' ? '\u2713' : step.status === 'error' || step.status === 'failed' ? '\u2717' : '\u25CB';\n const dur = step.durationMs ? ` (${(step.durationMs / 1000).toFixed(1)}s)` : '';\n row.innerHTML = `<b>${icon} ${escapeHtml(step.name)}</b>${dur}`;\n if (step.output) {\n const pre = document.createElement('pre');\n pre.style.cssText = 'margin:4px 0 0;padding:6px;background:var(--bg);border-radius:3px;font-size:11px;white-space:pre-wrap;word-break:break-word;max-height:120px;overflow:auto;';\n pre.textContent = String(step.output).slice(0, 800);\n row.appendChild(pre);\n }\n panel.appendChild(row);\n }\n }\n\n if (data.finalReply) {\n const h = document.createElement('div');\n h.textContent = '\u6700\u7EC8\u56DE\u590D';\n h.style.cssText = 'font-weight:600;margin:12px 0 8px;';\n panel.appendChild(h);\n const r = document.createElement('div');\n r.style.cssText = 'padding:8px 12px;background:var(--bg-secondary);border-radius:4px;font-size:13px;white-space:pre-wrap;word-break:break-word;';\n r.textContent = data.finalReply;\n panel.appendChild(r);\n }\n\n if (!data.error && !data.summary && (!data.steps || data.steps.length === 0) && !data.finalReply) {\n const empty = document.createElement('div');\n empty.style.cssText = 'text-align:center;padding:24px;color:var(--text-muted);font-size:13px;';\n empty.textContent = '\u65E0\u5FAA\u73AF\u4EA7\u51FA (\u53EF\u80FD\u5DF2 abort, \u6216\u6CA1\u4EA7\u751F step)';\n panel.appendChild(empty);\n }\n\n modal.appendChild(panel);\n modal.onclick = (e) => { if (e.target === modal) modal.remove(); };\n document.body.appendChild(modal);\n}\nconst sidebarToggle = document.getElementById('sidebar-toggle');\nconst themeToggle = document.getElementById('theme-toggle');\nconst channelList = document.getElementById('channel-list');\nconst newChannelBtn = document.getElementById('new-channel-btn');\nconst newChannelInput = document.getElementById('new-channel-input');\nconst channelNameEl = document.getElementById('channel-name');\n\nlet eventSources = new Map(); // channelId -> EventSource\nlet currentChannelId = null;\nlet currentAgentId = '';\nlet channels = [];\nlet remoteChannels = []; // v3: \u8FDC\u7AEF channel UI \u5143\u6570\u636E (\u6309 peer \u5206\u7EC4)\nlet isSidebarCollapsed = false;\nlet reconnectAttempts = new Map(); // channelId -> attempts\nlet reconnectTimers = new Map(); // channelId -> timer\nlet heartbeatTimers = new Map(); // channelId -> setInterval handle (\u9632\u6B62\u6CC4\u6F0F)\nlet lastUserCommand = ''; // \u9632\u6B62\u7528\u6237\u6D88\u606F\u91CD\u590D\u663E\u793A\n// 2026-07-06: SSE \u91CD\u8FDE\u6062\u590D\u7528 \u2014 \u6BCF\u4E2A channel \u8BB0\u6536\u5230\u7684\u6700\u5927 seq + \u5DF2\u6E32\u67D3\u7684 msgId \u96C6\u5408\nconst lastKnownSeq: Map<string, number> = new Map(); // channelId -> max seq\nconst lastSeenMsgIds: Map<string, string[]> = new Map(); // channelId -> msgId[] (\u6700\u8FD1 100)\n\n// 2026-06-10: P2P peer-group \u6298\u53E0\u72B6\u6001\u6301\u4E45\u5316 (\u8DE8\u5237\u65B0)\n// key = bolloon.p2p.collapsedPeers, value = JSON array of publicKey hex\nconst COLLAPSED_PEERS_KEY = 'bolloon.p2p.collapsedPeers';\nconst SEEN_PEERS_KEY = 'bolloon.p2p.seenPeers';\nlet collapsedPeers = (function loadCollapsed() {\n try {\n const raw = localStorage.getItem(COLLAPSED_PEERS_KEY);\n return new Set(raw ? JSON.parse(raw) : []);\n } catch { return new Set(); }\n})();\nlet seenPeers = (function loadSeen() {\n try {\n const raw = localStorage.getItem(SEEN_PEERS_KEY);\n return new Set(raw ? JSON.parse(raw) : []);\n } catch { return new Set(); }\n})();\nfunction saveCollapsedPeers() {\n try { localStorage.setItem(COLLAPSED_PEERS_KEY, JSON.stringify([...collapsedPeers])); } catch {}\n}\nfunction saveSeenPeers() {\n try { localStorage.setItem(SEEN_PEERS_KEY, JSON.stringify([...seenPeers])); } catch {}\n}\nfunction togglePeerCollapsed(peerPk) {\n if (collapsedPeers.has(peerPk)) {\n collapsedPeers.delete(peerPk);\n } else {\n collapsedPeers.add(peerPk);\n }\n saveCollapsedPeers();\n renderRemoteChannels();\n // 2026-06-10: \u901A\u77E5 header \u5207\u6362\u6309\u94AE\u540C\u6B65\u56FE\u6807\n if (typeof window.__syncP2PToggleAllBtn === 'function') window.__syncP2PToggleAllBtn();\n}\n// 2026-06-10: \u4E00\u952E\u5C55\u5F00/\u6298\u53E0\u6240\u6709 P2P peer (header \u6309\u94AE\u8C03\u7528)\nfunction expandAllPeers() {\n // \u4ECE remoteChannels + knownPeers \u6536\u96C6\u6240\u6709 publicKey\n const allPks = new Set([\n ...knownPeers.map(p => p.publicKey),\n ...remoteChannels.map(g => g.peerId)\n ]);\n for (const pk of allPks) collapsedPeers.delete(pk);\n saveCollapsedPeers();\n renderRemoteChannels();\n if (typeof window.__syncP2PToggleAllBtn === 'function') window.__syncP2PToggleAllBtn();\n}\nfunction collapseAllPeers() {\n const allPks = new Set([\n ...knownPeers.map(p => p.publicKey),\n ...remoteChannels.map(g => g.peerId)\n ]);\n for (const pk of allPks) collapsedPeers.add(pk);\n saveCollapsedPeers();\n renderRemoteChannels();\n if (typeof window.__syncP2PToggleAllBtn === 'function') window.__syncP2PToggleAllBtn();\n}\nlet lastAiContent = ''; // \u9632\u6B62 AI \u6D88\u606F\u91CD\u590D\u663E\u793A\nlet messagesContainers = new Map(); // channelId -> messages container div\nlet sessionMessages = new Map(); // channelId:sessionId -> messages array\nlet currentSessionId = null; // \u5F53\u524D\u663E\u793A\u7684 session ID\nlet expandedAgents = new Set(); // \u5F53\u524D\u5C55\u5F00\u7684 agent(channel) id \u96C6\u5408\n\nfunction generateId() {\n return crypto.randomUUID();\n}\n\nfunction applyTheme(theme) {\n document.documentElement.setAttribute('data-theme', theme);\n}\n\nasync function loadTheme() {\n try {\n const res = await fetch('/theme');\n const data = await res.json();\n applyTheme(data.theme);\n if (data.agentId) {\n currentAgentId = data.agentId;\n }\n return data;\n } catch {\n applyTheme('dark');\n return { theme: 'dark', agentId: '' };\n }\n}\n\nasync function saveTheme(theme, agentId) {\n try {\n await fetch('/theme', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ theme, agentId })\n });\n } catch (err) {\n console.error('Failed to save theme:', err);\n }\n}\n\nfunction toggleTheme() {\n const current = document.documentElement.getAttribute('data-theme') || 'dark';\n const next = current === 'dark' ? 'light' : 'dark';\n applyTheme(next);\n saveTheme(next, currentAgentId);\n}\n\nfunction toggleSidebar() {\n isSidebarCollapsed = !isSidebarCollapsed;\n\n if (isSidebarCollapsed) {\n sidebar.classList.add('collapsed');\n } else {\n sidebar.classList.remove('collapsed');\n }\n}\n\nfunction expandSidebar() {\n isSidebarCollapsed = false;\n sidebar.classList.remove('collapsed');\n}\n\nasync function loadChannels() {\n try {\n const res = await fetch('/channels');\n channels = await res.json();\n console.log('[\u52A0\u8F7D\u9891\u9053] \u4ECE\u670D\u52A1\u5668\u83B7\u53D6\u5230', channels.length, '\u4E2A\u9891\u9053');\n channels.forEach((ch, i) => {\n console.log(` [${i}] ${ch.name} - did: \"${ch.did}\"`);\n });\n // 2026-06-11: \u5168\u90E8\u9ED8\u8BA4\u4E0D\u5C55\u5F00 (\u7528\u6237\u9700\u8981\u624B\u52A8\u70B9 caret \u5C55\u5F00 session \u5217\u8868)\n // \u4E4B\u524D\u9ED8\u8BA4\u5C55\u5F00\u7B2C\u4E00\u4E2A\u4F1A\u55A7\u5BBE\u593A\u4E3B, \u7528\u6237\u770B\u4E0D\u5230\u5B8C\u6574 channel \u5217\u8868\n renderChannels();\n } catch (err) {\n console.error('[\u52A0\u8F7D\u9891\u9053] \u5931\u8D25:', err);\n }\n}\n\n// v3: \u5168\u5C40 SSE \u76D1\u542C (p2p-global channel) - \u63A5\u6536\u8FDC\u7AEF chat.reply \u7B49\u4E8B\u4EF6\nlet v3GlobalEventSource = null;\nfunction startV3GlobalSSE() {\n if (v3GlobalEventSource) return;\n try {\n v3GlobalEventSource = new EventSource('/events?channelId=p2p-global');\n v3GlobalEventSource.onmessage = (e) => {\n try {\n const msg = JSON.parse(e.data);\n if (msg.type === 'remote-chat-reply') {\n // 2026-06-10: \u590D\u7528\u672C\u5730 addMessage \u6E32\u67D3 \u2014 \u81EA\u52A8 marked + \u5265 think/env + \u4E3B\u9898\u6837\u5F0F\n // \u4E4B\u524D\u662F textContent \u786C\u7F16\u7801\u7070\u5E95, \u8DDF Step 3 \u91CD\u5199\u7684 modal \u98CE\u683C\u4E0D\u4E00\u81F4,\n // \u800C\u4E14 SSE \u5F02\u6B65\u56DE\u5230\u65F6 modal \u53EF\u80FD\u5DF2\u88AB\u5207\u5230 thinking \u5360\u6EE1, \u7528\u6237\u770B\u4E0D\u5230 reply.\n const log = document.getElementById('rcm-log');\n const thinkingEl = document.getElementById('rcm-thinking');\n if (thinkingEl) thinkingEl.style.display = 'none'; // \u601D\u8003\u7ED3\u675F, \u9690\u85CF\n // \u4E5F\u6E05\u6389 \"\u5BF9\u65B9\u6B63\u5728\u601D\u8003...\" \u884C (\u6D41\u5F0F token \u7559\u4E0B\u7684)\n const liveThinking = document.getElementById('rcm-thinking-live');\n if (liveThinking) liveThinking.remove();\n if (log) {\n if (msg.error) {\n // \u9519\u8BEF\u7528 sysmsg \u6837\u5F0F (\u8DDF modal \u98CE\u683C\u4E00\u81F4)\n const errEl = document.createElement('div');\n errEl.className = 'remote-chat-sysmsg remote-chat-sysmsg-error';\n errEl.textContent = `\u274C \u5BF9\u65B9\u56DE\u590D\u51FA\u9519: ${msg.error}`;\n log.appendChild(errEl);\n } else {\n // \u8D70\u672C\u5730 addMessage, \u8DDF\u4E3B\u804A\u5929\u6846\u5B8C\u5168\u4E00\u81F4 (marked + think/env \u6298\u53E0 + \u4E3B\u9898\u8272)\n const prefix = `\uD83E\uDD16 \u8FDC\u7AEF AI \u56DE\u590D\\n\\n`;\n addMessage(prefix + (msg.text || '(\u7A7A\u56DE\u590D)'), 'ai', false, log);\n }\n log.scrollTop = log.scrollHeight;\n } else {\n // modal \u6CA1\u5F00 \u2192 \u7528\u53F3\u4E0B toast \u63D0\u793A\u7528\u6237\"\u5BF9\u65B9\u56DE\u4E86, \u6253\u5F00\u804A\u5929\u770B\"\n if (typeof showSimpleToast === 'function') {\n const preview = (msg.text || '').slice(0, 50);\n showSimpleToast(`\uD83D\uDCAC \u8FDC\u7AEF channel \u6709\u65B0\u56DE\u590D: ${preview}${msg.text && msg.text.length > 50 ? '\u2026' : ''}`);\n }\n }\n } else if (msg.type === 'remote-chat-thinking') {\n // v3 \u65B0\u589E: B \u7AEF\u5B9E\u65F6\u663E\u793A A \u8282\u70B9\u7684\u601D\u8003\u8FC7\u7A0B\n const phase = msg.phase;\n const log = document.getElementById('rcm-log');\n if (!log) return;\n\n if (phase === 'start') {\n // \u5934\u90E8\u63D2\u5165\"\u5224\u65AD\u529B\u4F9D\u636E\"\u533A\u5757 (\u53EA\u7B2C\u4E00\u6B21)\n const judgments = msg.usedJudgments || { bound: [], candidates: [] };\n const judgmentBlock = document.createElement('div');\n judgmentBlock.className = 'rcm-judgment-block';\n judgmentBlock.style.cssText = 'margin:6px 0;padding:8px 10px;background:#fef3c7;border-left:3px solid #f59e0b;border-radius:4px;font-size:12px;';\n let jh = '<div style=\"font-weight:600;color:#92400e;margin-bottom:4px;\">\uD83D\uDEE1\uFE0F \u5BF9\u65B9\u4F7F\u7528\u7684\u5224\u65AD\u529B (\u6765\u81EA ta \u7684 channel)</div>';\n if (judgments.bound && judgments.bound.length > 0) {\n jh += '<div style=\"color:#78350f;margin-bottom:4px;\"><b>\u786C\u7ED1\u5B9A</b> (\u5FC5\u987B\u9075\u5FAA):</div>';\n for (const j of judgments.bound) {\n jh += `<div style=\"margin:2px 0;padding-left:8px;\">\u2022 <b>${escapeHtml((j.decision || '').slice(0, 80))}</b>${j.reasons && j.reasons.length ? '<br><span style=\"color:#92400e;font-size:11px;\">\u7406\u7531: ' + escapeHtml(j.reasons.join('; ').slice(0, 80)) + '</span>' : ''}</div>`;\n }\n }\n if (judgments.candidates && judgments.candidates.length > 0) {\n jh += `<div style=\"color:#78350f;margin-top:4px;\"><b>\u5019\u9009\u6C60</b> (${judgments.candidates.length} \u6761, LLM \u81EA\u9009)</div>`;\n }\n log.appendChild(judgmentBlock);\n // \"\u601D\u8003\u4E2D\" \u533A\u5757\n const thinkingEl = document.createElement('div');\n thinkingEl.id = 'rcm-thinking-live';\n thinkingEl.style.cssText = 'margin:6px 0;padding:8px 10px;background:#ede9fe;border-left:3px solid #8b5cf6;border-radius:4px;font-size:12px;color:#5b21b6;font-style:italic;';\n thinkingEl.textContent = '\uD83D\uDCAD \u5BF9\u65B9\u6B63\u5728\u601D\u8003...';\n log.appendChild(thinkingEl);\n log.scrollTop = log.scrollHeight;\n } else if (phase === 'token') {\n // \u5B9E\u65F6\u66F4\u65B0\u601D\u8003\u4E2D\u7684 partial\n const thinkingEl = document.getElementById('rcm-thinking-live');\n if (thinkingEl) {\n thinkingEl.textContent = '\uD83D\uDCAD \u5BF9\u65B9\u6B63\u5728\u601D\u8003: ' + (msg.partial || '').slice(-200);\n log.scrollTop = log.scrollHeight;\n }\n }\n } else if (msg.type === 'cross-mention-received') {\n // v3 \u65B0\u589E: A \u8282\u70B9\u4E0A, \u67D0\u4E2A channel \u7684 LLM @-mention \u4E86\u53E6\u4E00\u4E2A channel, SSE \u63A8\u8FC7\u6765\n // \u5728\u6240\u6709\u6253\u5F00\u7684 chat modal \u4E0A\u663E\u793A\"AI \u8DE8\u6E20\u9053 @-mention\" \u63D0\u793A\n const allModals = document.querySelectorAll('.rcm-mention-toast, [id^=\"rcm-log\"]');\n for (const log of allModals) {\n if (!log.id) continue;\n const toast = document.createElement('div');\n toast.style.cssText = 'margin:6px 0;padding:8px 10px;background:#fce7f3;border-left:3px solid #ec4899;border-radius:4px;font-size:12px;color:#831843;';\n const fromTxt = msg.source === 'ai-mention-remote' ? `\u8FDC\u7AEF\u8282\u70B9 ${(msg.fromPublicKey || '').substring(0, 8)}\u2026 \u7684 ${msg.originChannelName}` : `${msg.originChannelName} (\u672C\u5730)`;\n toast.innerHTML = `\uD83D\uDCE1 <b>${fromTxt}</b> @-mention \u2192 \u5F53\u524D channel: <i>${escapeHtml((msg.text || '').slice(0, 100))}</i>${msg.text && msg.text.length > 100 ? '\u2026' : ''}`;\n log.appendChild(toast);\n log.scrollTop = log.scrollHeight;\n }\n } else if (msg.type === 'remote-channel-update') {\n // v3 \u65B0\u589E: \u8FDC\u7AEF\u8282\u70B9\u53D1\u6765\u65B0\u5206\u4EAB / \u5220\u9664 / \u6539\u540D, \u7ACB\u5373\u66F4\u65B0\u672C\u5730 cache\n const peerId = msg.peerId;\n const channels = msg.channels || [];\n const peerName = msg.peerName || null; // 2026-06-10: \u540C\u6B65\u63A5\u6536\u5BF9\u65B9\u540D\u5B57\n let group = remoteChannels.find(g => g.peerId === peerId);\n if (!group) {\n group = { peerId, channels: [], peerName: peerName || ('peer-' + peerId.substring(0, 8)) };\n remoteChannels.push(group);\n } else if (peerName) {\n group.peerName = peerName; // \u66F4\u65B0\u540D\u5B57\n }\n group.channels = channels;\n // 2026-06-10: \u5982\u679C\u5BF9\u9762\u544A\u77E5\u540D\u5B57, \u540C\u6B65\u5237\u65B0 knownPeers \u5217\u8868, \u907F\u514D\u964C\u751F peer \u72B6\u6001\n if (peerName && !knownPeers.find(p => p.publicKey === peerId)) {\n knownPeers.push({\n publicKey: peerId,\n name: peerName,\n addedAt: new Date().toISOString(),\n lastConnectedAt: new Date().toISOString(),\n });\n console.log(`[v3] \u8FDC\u7AEF ${peerId.substring(0,12)}... \u81EA\u62A5\u540D\u5B57 = ${peerName}, \u5DF2\u52A0\u5230 knownPeers`);\n }\n renderRemoteChannels();\n console.log(`[v3] \u6536\u5230\u8FDC\u7AEF ${peerId.substring(0,12)}... \u7684 ${channels.length} \u4E2A channel \u66F4\u65B0 (name=${peerName || '?'})`);\n } else if (msg.type === 'friend-request') {\n // v3 \u65B0\u589E: \u6536\u5230\u597D\u53CB\u7533\u8BF7\n showFriendRequestModal(msg);\n } else if (msg.type === 'friend-request-ack') {\n // 2026-06-10: \u6536\u5230\u5BF9\u65B9 ack, \u7ED9\u53D1\u9001\u65B9\u63D0\u793A\"\u5DF2\u9001\u8FBE\"\n const pending = window.__pendingFriendRequests;\n if (pending && msg.requestId && pending.has(msg.requestId)) {\n const { name } = pending.get(msg.requestId);\n pending.delete(msg.requestId);\n console.log(`[v3-friend] \u2705 ack \u6536\u5230: ${name} \u5DF2\u6536\u5230\u597D\u53CB\u7533\u8BF7`);\n // \u7B80\u77ED toast (\u53F3\u4E0B\u89D2), \u4E0D\u963B\u585E\n showSimpleToast(`\uD83D\uDCEC ${name} \u5DF2\u6536\u5230\u4F60\u7684\u597D\u53CB\u7533\u8BF7, \u7B49\u5BF9\u65B9\u63A5\u53D7`);\n }\n }\n } catch (err) {\n console.error('[v3] \u5168\u5C40 SSE \u89E3\u6790\u5931\u8D25:', err);\n }\n };\n v3GlobalEventSource.onerror = (e) => {\n console.warn('[v3] \u5168\u5C40 SSE \u9519\u8BEF');\n };\n } catch (err) {\n console.error('[v3] \u542F\u52A8\u5168\u5C40 SSE \u5931\u8D25:', err);\n }\n}\n\nasync function createChannel(name) {\n if (!name.trim()) return;\n try {\n const res = await fetch('/channels', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ name: name.trim(), agentId: currentAgentId })\n });\n const channel = await res.json();\n console.log('[\u521B\u5EFA\u9891\u9053] \u670D\u52A1\u5668\u8FD4\u56DE:', channel);\n console.log('[\u521B\u5EFA\u9891\u9053] DID:', channel.did, 'CID:', channel.cid);\n\n // \u7ACB\u5373\u6DFB\u52A0\u9891\u9053\u5E76\u5207\u6362\uFF08\u4E0D\u7B49\u5F85 DID\uFF09\n channels.push(channel);\n renderChannels();\n selectChannel(channel.id);\n if (newChannelInput) newChannelInput.value = '';\n\n // \u540E\u53F0\u66F4\u65B0 DID\uFF08\u5982\u679C\u8FD8\u6CA1\u6709\u7684\u8BDD\uFF09\n if (!channel.did || channel.did === 'undefined') {\n console.log('[\u521B\u5EFA\u9891\u9053] \u540E\u53F0\u751F\u6210 DID...');\n // \u590D\u7528\u5168\u5C40 channelRefreshTimer, \u628A\u591A\u4E2A\u5237\u65B0\u8BF7\u6C42\u5408\u5E76\u6210\u4E00\u4E2A 1.5s \u540E\u7684\u8BF7\u6C42\n scheduleChannelsRefresh();\n }\n } catch (err) {\n console.error('Failed to create channel:', err);\n }\n}\n\nasync function deleteChannel(channelId, e) {\n e.stopPropagation();\n if (!confirm('\u786E\u5B9A\u8981\u5220\u9664\u8BE5\u667A\u80FD\u4F53\u53CA\u5176\u6240\u6709\u4F1A\u8BDD\u5417\uFF1F\u6B64\u64CD\u4F5C\u4E0D\u53EF\u64A4\u9500\u3002')) return;\n try {\n await fetch(`/channels/${channelId}`, { method: 'DELETE' });\n channels = channels.filter(c => c.id !== channelId);\n expandedAgents.delete(channelId);\n\n // \u91CA\u653E\u6D4F\u89C8\u5668\u4FA7\u5F15\u7528 + DOM, \u907F\u514D\u957F\u65F6\u95F4\u4F7F\u7528\u540E\u5185\u5B58\u7D2F\u79EF\n cleanupChannelState(channelId);\n\n if (currentChannelId === channelId) {\n currentChannelId = channels[0]?.id || null;\n currentSessionId = null;\n if (currentChannelId) {\n const ch = channels.find(c => c.id === currentChannelId);\n if (channelNameEl) channelNameEl.textContent = safeChannelName(ch?.name, 'Bolloon Agent');\n await selectChannel(currentChannelId);\n } else {\n messagesEl.innerHTML = '';\n if (channelNameEl) channelNameEl.textContent = 'Bolloon Agent';\n }\n }\n renderChannels();\n } catch (err) {\n console.error('Failed to delete channel:', err);\n }\n}\n\n/** \u91CA\u653E\u4E00\u4E2A channel \u5728\u6D4F\u89C8\u5668\u4FA7\u5360\u7528\u7684\u6240\u6709\u8D44\u6E90 (DOM \u5BB9\u5668, SSE, \u7F13\u5B58 session \u6D88\u606F) */\nfunction cleanupChannelState(channelId) {\n // 1. SSE \u8FDE\u63A5\n if (eventSources.has(channelId)) {\n try { eventSources.get(channelId).close(); } catch {}\n eventSources.delete(channelId);\n }\n // 2. \u5FC3\u8DF3 + \u91CD\u8FDE timer\n if (heartbeatTimers.has(channelId)) {\n clearInterval(heartbeatTimers.get(channelId));\n heartbeatTimers.delete(channelId);\n }\n if (reconnectTimers.has(channelId)) {\n clearTimeout(reconnectTimers.get(channelId));\n reconnectTimers.delete(channelId);\n }\n reconnectAttempts.delete(channelId);\n // 3. \u6D88\u606F\u5BB9\u5668 DOM \u2014 \u771F\u4ECE #messages \u91CC\u79FB\u9664, \u4E0D\u53EA\u662F\u9690\u85CF\n const container = messagesContainers.get(channelId);\n if (container && container.parentNode) {\n container.parentNode.removeChild(container);\n }\n messagesContainers.delete(channelId);\n // 4. \u7F13\u5B58\u7684\u6240\u6709 session \u6D88\u606F (\u6309 channel:session \u7D22\u5F15)\n const prefix = `${channelId}:`;\n for (const key of sessionMessages.keys()) {\n if (key === channelId || key.startsWith(prefix)) {\n sessionMessages.delete(key);\n }\n }\n}\n\nasync function createNewSession() {\n if (!currentChannelId) {\n console.log('[\u65B0\u4F1A\u8BDD] \u6CA1\u6709\u9009\u4E2D\u7684\u9891\u9053');\n return;\n }\n try {\n // \u4FDD\u5B58\u5F53\u524D session \u7684\u6D88\u606F\n saveCurrentSessionMessages();\n\n const res = await fetch(`/channels/${currentChannelId}/sessions`, {\n method: 'POST'\n });\n const data = await res.json();\n console.log('[\u65B0\u4F1A\u8BDD] \u521B\u5EFA\u6210\u529F:', data);\n\n // \u66F4\u65B0\u672C\u5730\u9891\u9053\u6570\u636E\n const channel = channels.find(c => c.id === currentChannelId);\n if (channel) {\n if (!channel.sessions) channel.sessions = [];\n channel.sessions.push(data.session);\n channel.currentSessionId = data.currentSessionId;\n }\n\n // \u5207\u6362\u5230\u65B0 session\n currentSessionId = data.currentSessionId;\n\n // \u6E05\u7A7A\u5BB9\u5668\u5E76\u52A0\u8F7D\u65B0 session\n const container = messagesContainers.get(currentChannelId);\n if (container) {\n container.innerHTML = '';\n showChannelView(currentChannelId);\n addMessage('\u4F60\u597D\uFF01\u65B0\u4F1A\u8BDD\u5DF2\u5F00\u59CB\uFF0C\u6709\u4EC0\u4E48\u6211\u53EF\u4EE5\u5E2E\u4F60\u7684\u5417\uFF1F', 'ai', false, container);\n }\n\n // \u5C55\u5F00\u5F53\u524D\u667A\u80FD\u4F53\uFF0C\u5237\u65B0\u4FA7\u8FB9\u680F\u8BA9\u65B0\u4F1A\u8BDD\u663E\u793A\u51FA\u6765\n expandedAgents.add(currentChannelId);\n renderChannels();\n\n console.log('[\u65B0\u4F1A\u8BDD] \u5DF2\u5207\u6362\u5230:', data.currentSessionId);\n } catch (err) {\n console.error('Failed to create new session:', err);\n }\n}\n\nasync function createNewSessionForChannel(channelId, e) {\n if (e) e.stopPropagation();\n if (!channelId) return;\n\n // \u7ED9\u81EA\u5DF1\u521B\u5EFA\uFF1A\u590D\u7528\u7EDF\u4E00\u7684 createNewSession\n if (channelId === currentChannelId) {\n if (currentSessionId) saveCurrentSessionMessages();\n await createNewSession();\n return;\n }\n\n // \u7ED9\u522B\u7684\u667A\u80FD\u4F53\u521B\u5EFA\uFF1A\u540E\u7AEF\u5EFA\u597D\u540E\u76F4\u63A5 re-fetch \u4E00\u6B21\u4FDD\u6301\u672C\u5730\u4E0E\u540E\u7AEF\u4E00\u81F4\n try {\n const res = await fetch(`/channels/${channelId}/sessions`, { method: 'POST' });\n if (!res.ok) throw new Error('create session failed');\n const data = await res.json();\n const channel = channels.find(c => c.id === channelId);\n if (channel) {\n if (!channel.sessions) channel.sessions = [];\n channel.sessions.push(data.session);\n channel.currentSessionId = data.currentSessionId;\n }\n expandedAgents.add(channelId);\n renderChannels();\n } catch (err) {\n console.error('Failed to create new session:', err);\n }\n}\n\nasync function switchSession(channelId, sessionId, e) {\n if (e) e.stopPropagation();\n if (!channelId || !sessionId) return;\n if (channelId === currentChannelId && sessionId === currentSessionId) return;\n\n // \u5148\u4FDD\u5B58\u5F53\u524D session \u7684\u672C\u5730\u6D88\u606F\n if (currentChannelId && currentSessionId) {\n saveCurrentSessionMessages();\n }\n\n try {\n const res = await fetch(`/channels/${channelId}/sessions/${sessionId}/switch`, { method: 'POST' });\n if (!res.ok) throw new Error('switch failed');\n const channel = channels.find(c => c.id === channelId);\n if (channel) {\n channel.currentSessionId = sessionId;\n await saveChannels();\n }\n\n // \u5207\u6362\u5230\u76EE\u6807 agent + session\n await selectChannel(channelId, sessionId);\n renderChannels();\n } catch (err) {\n console.error('Failed to switch session:', err);\n }\n}\n\nasync function deleteSession(channelId, sessionId, e) {\n if (e) e.stopPropagation();\n if (!confirm('\u786E\u5B9A\u8981\u5220\u9664\u8BE5\u4F1A\u8BDD\u5417\uFF1F\u6B64\u64CD\u4F5C\u4E0D\u53EF\u64A4\u9500\u3002')) return;\n try {\n const res = await fetch(`/channels/${channelId}/sessions/${sessionId}`, { method: 'DELETE' });\n if (!res.ok) {\n const err = await res.json().catch(() => ({}));\n alert(err.error || '\u5220\u9664\u5931\u8D25');\n return;\n }\n const data = await res.json();\n const channel = channels.find(c => c.id === channelId);\n if (channel) {\n if (channel.sessions) {\n channel.sessions = channel.sessions.filter(s => s.id !== sessionId);\n }\n if (data.currentSessionId) {\n channel.currentSessionId = data.currentSessionId;\n }\n }\n\n // \u5982\u679C\u5220\u7684\u662F\u5F53\u524D\u6253\u5F00\u7684\u4F1A\u8BDD\uFF0C\u5207\u6362\u5230\u65B0\u7684\u5F53\u524D\u4F1A\u8BDD\n if (channelId === currentChannelId && sessionId === currentSessionId) {\n if (data.currentSessionId) {\n currentSessionId = data.currentSessionId;\n const container = messagesContainers.get(channelId);\n if (container) container.innerHTML = '';\n await loadSession(channelId);\n }\n }\n renderChannels();\n } catch (err) {\n console.error('Failed to delete session:', err);\n }\n}\n\nlet _saveSessionMessagesDirty = false;\nlet _saveSessionMessagesTimer = null;\nfunction saveCurrentSessionMessages() {\n if (!currentChannelId || !currentSessionId) return;\n // \u5185\u5B58\u4FDD\u62A4: \u591A\u6B21\u5FEB\u901F\u8C03\u7528\u5408\u5E76\u6210\u4E00\u4E2A, \u907F\u514D\u5728\u5207\u4F1A\u8BDD\u65F6\u53CD\u590D .textContent \u8BFB DOM\n // (\u6BCF\u4E2A textContent \u4F1A\u5E8F\u5217\u5316\u6574\u68F5\u5B50\u6811, 200 \u6761\u6D88\u606F = \u51E0\u767E MB \u4E34\u65F6\u5B57\u7B26\u4E32)\n _saveSessionMessagesDirty = true;\n if (_saveSessionMessagesTimer) return;\n _saveSessionMessagesTimer = setTimeout(() => {\n _saveSessionMessagesTimer = null;\n if (!_saveSessionMessagesDirty) return;\n _saveSessionMessagesDirty = false;\n if (!currentChannelId || !currentSessionId) return;\n const container = messagesContainers.get(currentChannelId);\n if (!container) return;\n const messages = Array.from(container.querySelectorAll('.message')).map(msg => ({\n type: msg.classList.contains('message-user') ? 'user' : 'ai',\n content: msg.querySelector('.message-content')?.textContent || ''\n }));\n if (messages.length > 0) {\n sessionMessages.set(`${currentChannelId}:${currentSessionId}`, messages);\n }\n }, 50);\n}\n\nasync function saveChannels() {\n // \u7B80\u5355\u5730 re-fetch\uFF0C\u4FDD\u6301\u672C\u5730 channels \u4E0E\u670D\u52A1\u7AEF\u4E00\u81F4\n // \u6539\u6210\u8D70 scheduleChannelsRefresh, \u591A\u4E2A\u8C03\u7528\u5408\u5E76\u6210\u4E00\u4E2A\u8BF7\u6C42 \u2014 \u51CF\u5C11\u5185\u5B58\u5CF0\u503C\u548C\u540E\u7AEF\u538B\u529B\n scheduleChannelsRefresh();\n await new Promise(r => setTimeout(r, 600));\n}\n\nlet channelRefreshTimer = null;\nlet channelRefreshInFlight = null;\nfunction scheduleChannelsRefresh() {\n if (channelRefreshTimer) return;\n channelRefreshTimer = setTimeout(async () => {\n channelRefreshTimer = null;\n if (channelRefreshInFlight) return channelRefreshInFlight;\n channelRefreshInFlight = (async () => {\n try {\n const res = await fetch('/channels');\n if (res.ok) {\n const fresh = await res.json();\n channels = fresh;\n renderChannels();\n }\n } catch (err) {\n console.error('Failed to re-fetch channels:', err);\n } finally {\n channelRefreshInFlight = null;\n }\n })();\n return channelRefreshInFlight;\n }, 800);\n}\n\nfunction toggleAgentExpand(channelId, e) {\n if (e) e.stopPropagation();\n if (expandedAgents.has(channelId)) {\n expandedAgents.delete(channelId);\n } else {\n expandedAgents.add(channelId);\n }\n renderChannels();\n}\n\n/**\n * 2026-06-11 \u6027\u80FD\u4F18\u5316: \u5207 channel \u65F6\u7528\u8F7B\u91CF patch, \u4E0D\u91CD\u5EFA\u6574\u4E2A sidebar \u5217\u8868\n * \u53EA\u66F4\u65B0: (1) active class (2) \u5F53\u524D session label + count (3) expanded \u72B6\u6001\n * \u907F\u514D\u6BCF\u6B21\u5207 channel \u90FD innerHTML='' + \u91CD\u5EFA ~10 \u4E2A channel \u8282\u70B9\n */\nfunction renderChannelsLite(activeChannelId, activeSessionId) {\n if (!channelList) return;\n // 1. \u66F4\u65B0\u6240\u6709 .agent-row \u7684 active class\n channelList.querySelectorAll('.agent-row').forEach(row => {\n const li = row.closest('.agent-group');\n const chId = li?.dataset.channelId;\n row.classList.toggle('active', chId === activeChannelId);\n });\n // 2. \u5F53\u524D channel \u7684\u5C55\u5F00\u72B6\u6001: \u5F3A\u5236\u5C55\u5F00, \u5176\u4ED6\u4E0D\u52A8\n if (activeChannelId) expandedAgents.add(activeChannelId);\n // 3. \u5F53\u524D channel \u884C\u5C55\u5F00 + \u53EA\u5207 session-item \u7684 active class (\u4E0D\u518D innerHTML \u91CD\u6E32!)\n // \u539F\u56E0: \u91CD\u6E32 innerHTML \u4F1A\u6E05\u6389\u539F\u59CB renderChannels \u7ED1\u7684 session-item click handler,\n // \u5373\u4F7F\u8865\u7ED1\u4E5F\u4F1A\u56E0\u4E3A lite HTML \u7ED3\u6784 (.session-dot + .session-msg-count) \u8DDF\u539F\u59CB\u4E0D\u540C\n // \u5BFC\u81F4\"\u7B2C 1 \u6B21\u70B9\u4E0D\u52A8 (\u539F\u59CB), \u7B2C 2 \u6B21\u70B9\u624D\u80FD\u7528 (lite)\" \u73B0\u8C61\n // \u4FEE\u6CD5: \u5B8C\u5168\u4E0D\u52A8 session-list DOM, \u53EA toggle .active\n const activeLi = channelList.querySelector(`.agent-group[data-channel-id=\"${activeChannelId}\"]`);\n if (activeLi) {\n activeLi.classList.add('expanded');\n // \u53EA\u5207 active class, \u4E0D\u52A8 innerHTML (\u907F\u514D\u6E05\u6389\u539F\u59CB click handler)\n const ch = channels.find(c => c.id === activeChannelId);\n // 2026-06-11: \u539F\u59CB renderChannels \u5DF2\u7ECF\u7ED9 session-item \u52A0\u4E86 data-session-id (line 791),\n // \u8FD9\u91CC\u5148\u6E05\u7A7A\u6240\u6709 .active \u518D\u8BBE\u65B0\u7684, \u907F\u514D\u591A\u4E2A active \u5171\u5B58 (\u56E0\u4E3A renderChannels \u521D\u59CB DOM\n // \u4E0A\u4F1A\u6709\u4E00\u4E2A active \u6807\u8BB0\u65E7 session, \u65B0\u5207 session \u5BB9\u6613\u51FA\u73B0\u4E24\u4E2A active)\n activeLi.querySelectorAll('.session-item').forEach(sessLi => {\n const sessId = sessLi.dataset.sessionId;\n const shouldBeActive = sessId === activeSessionId;\n sessLi.classList.toggle('active', shouldBeActive);\n });\n // \u66F4\u65B0\u9876\u90E8 current session label\n if (ch) {\n const currentSess = Array.isArray(ch.sessions) ? ch.sessions.find(s => s.id === activeSessionId) : null;\n const labelEl = activeLi.querySelector('.agent-current-session');\n if (labelEl) {\n labelEl.textContent = currentSess ? '\u00B7 ' + formatSessionName(currentSess) : '';\n }\n }\n }\n}\n\nfunction renderChannels() {\n if (!channelList) return;\n channelList.innerHTML = '';\n\n const fragment = document.createDocumentFragment();\n\n // \u6EDA\u52A8\u53EF\u89C1\u6027\u76D1\u542C\u53EA\u7ED1\u5B9A\u4E00\u6B21 (channelList \u662F\u540C\u4E00\u4E2A DOM \u8282\u70B9,\n // renderChannels \u6BCF\u6B21\u6E05\u7A7A innerHTML \u90FD\u4F1A\u91CD\u6E32, \u4E0D\u80FD\u91CD\u590D addEventListener)\n if (!channelList._scrollListenersBound) {\n const onUserScroll = () => {\n channelList.classList.add('is-scrolling');\n if (channelList._scrollIdleTimer) clearTimeout(channelList._scrollIdleTimer);\n channelList._scrollIdleTimer = setTimeout(() => {\n channelList.classList.remove('is-scrolling');\n }, 1200);\n };\n channelList.addEventListener('wheel', onUserScroll, { passive: true });\n channelList.addEventListener('touchmove', onUserScroll, { passive: true });\n channelList.addEventListener('keydown', (ev) => {\n if (['ArrowUp', 'ArrowDown', 'PageUp', 'PageDown', 'Home', 'End'].includes(ev.key)) {\n onUserScroll();\n }\n });\n channelList._scrollListenersBound = true;\n }\n\n channels.forEach(ch => {\n const li = document.createElement('li');\n const isExpanded = expandedAgents.has(ch.id);\n li.className = `agent-group ${isExpanded ? 'expanded' : ''}`;\n li.dataset.channelId = ch.id;\n\n // --- \u667A\u80FD\u4F53\u884C ---\n const row = document.createElement('div');\n row.className = `agent-row ${ch.id === currentChannelId ? 'active' : ''}`;\n\n // \u627E\u5230\u5F53\u524D\u667A\u80FD\u4F53\uFF08\u5982\u679C\u5B83\u662F\u6FC0\u6D3B\u7684\uFF09\u7684\u5F53\u524D session\n const currentSess = (ch.id === currentChannelId && Array.isArray(ch.sessions))\n ? ch.sessions.find(s => s.id === ch.currentSessionId)\n : null;\n const currentSessLabel = currentSess ? formatSessionName(currentSess) : '';\n const sessionCount = Array.isArray(ch.sessions) ? ch.sessions.length : 0;\n\n // 2026-06-10: \u9690\u85CF channel \u884C\u53F3\u4FA7\u7684\u52CB\u7AE0 (\u94B1\u5305 / \u5DE5\u5177) \u2014 UI \u7B80\u6D01\n const walletBadge = '';\n const toolsBadge = '';\n\n row.innerHTML = `\n <svg class=\"agent-caret\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <polyline points=\"9 18 15 12 9 6\"></polyline>\n </svg>\n <div class=\"channel-icon\">\uD83D\uDCAC</div>\n <span class=\"channel-name\" title=\"${escapeHtml(safeChannelName(ch.name, ''))}\">${escapeHtml(safeChannelName(ch.name))}</span>\n <span class=\"agent-row-meta\">\n ${walletBadge}\n ${toolsBadge}\n ${sessionCount > 1 ? `<span class=\"agent-session-count\" title=\"${sessionCount} \u4E2A\u4F1A\u8BDD\">${sessionCount}</span>` : ''}\n ${currentSessLabel ? `<span class=\"agent-current-session\" title=\"\u5F53\u524D\u4F1A\u8BDD\uFF1A${escapeHtml(currentSessLabel)}\">\u00B7 ${escapeHtml(currentSessLabel)}</span>` : ''}\n <button class=\"agent-config-btn\" title=\"\u914D\u7F6E\u667A\u80FD\u4F53 (\u94B1\u5305 / \u5DE5\u5177)\">\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"3\"></circle>\n <path d=\"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z\"></path>\n </svg>\n </button>\n <button class=\"channel-delete\" title=\"\u5220\u9664\u667A\u80FD\u4F53\">\u00D7</button>\n </span>\n `;\n\n // \u884C\u70B9\u51FB\uFF1A\u5207\u6362\u5C55\u5F00\uFF1B\u70B9\u51FB\u540D\u5B57/\u56FE\u6807\u533A\u57DF\u5219\u5207\u5230\u8BE5\u667A\u80FD\u4F53\n row.addEventListener('click', (ev) => {\n // \u5982\u679C\u70B9\u5728\u5220\u9664/\u914D\u7F6E\u6309\u94AE\u4E0A, \u5355\u72EC\u5904\u7406\n if (ev.target.closest('.channel-delete')\n || ev.target.closest('.agent-config-btn')) return;\n if (ev.target.closest('.agent-caret')) {\n toggleAgentExpand(ch.id, ev);\n return;\n }\n toggleAgentExpand(ch.id, ev);\n if (ch.id !== currentChannelId) {\n expandSidebar();\n selectChannel(ch.id);\n }\n });\n\n // \u667A\u80FD\u4F53\u5220\u9664\n row.querySelector('.channel-delete').addEventListener('click', (ev) => deleteChannel(ch.id, ev));\n // \u914D\u7F6E\u6309\u94AE: \u6253\u5F00\u540C\u4E00\u4E2A modal \u7F16\u8F91\u5DF2\u6709\u667A\u80FD\u4F53\n row.querySelector('.agent-config-btn').addEventListener('click', (ev) => {\n ev.stopPropagation();\n openAgentAddModal(ch);\n });\n\n li.appendChild(row);\n\n // --- Session \u5217\u8868\uFF08\u4EC5\u5C55\u5F00\u65F6\u6E32\u67D3 DOM\uFF09---\n const sessionUl = document.createElement('ul');\n sessionUl.className = 'session-list';\n if (isExpanded) {\n // \"\u65B0\u5EFA\u4F1A\u8BDD\" \u6309\u94AE \u2014 \u653E\u5728 session \u5217\u8868\u6700\u524D\u9762, \u59CB\u7EC8\u53EF\u89C1\n const newSessLi = document.createElement('li');\n newSessLi.className = 'session-new-item';\n newSessLi.setAttribute('role', 'button');\n newSessLi.setAttribute('tabindex', '0');\n newSessLi.title = '\u65B0\u5EFA\u4F1A\u8BDD';\n newSessLi.innerHTML = `\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <line x1=\"12\" y1=\"5\" x2=\"12\" y2=\"19\"></line>\n <line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"></line>\n </svg>\n <span>\u65B0\u5EFA\u4F1A\u8BDD</span>\n `;\n const onNewSession = (ev) => {\n ev.stopPropagation();\n createNewSessionForChannel(ch.id, ev);\n };\n newSessLi.addEventListener('click', onNewSession);\n newSessLi.addEventListener('keydown', (ev) => {\n if (ev.key === 'Enter' || ev.key === ' ') {\n ev.preventDefault();\n onNewSession(ev);\n }\n });\n sessionUl.appendChild(newSessLi);\n\n const sessions = Array.isArray(ch.sessions) ? ch.sessions : [];\n sessions.forEach(sess => {\n const sessLi = document.createElement('li');\n const isActive = ch.id === currentChannelId && sess.id === ch.currentSessionId;\n sessLi.className = `session-item ${isActive ? 'active' : ''}`;\n sessLi.dataset.sessionId = sess.id; // 2026-06-11: \u7ED9 session-item \u52A0\u4E0A data-session-id, renderChannelsLite \u624D\u80FD toggle active class\n sessLi.innerHTML = `\n <span class=\"session-name\" title=\"${escapeHtml(formatSessionName(sess))}\">${escapeHtml(formatSessionName(sess))}</span>\n <button class=\"session-delete\" title=\"\u5220\u9664\u4F1A\u8BDD\">\u00D7</button>\n `;\n sessLi.addEventListener('click', (ev) => {\n if (ev.target.closest('.session-delete')) return;\n switchSession(ch.id, sess.id, ev);\n });\n sessLi.querySelector('.session-delete').addEventListener('click', (ev) => deleteSession(ch.id, sess.id, ev));\n sessionUl.appendChild(sessLi);\n });\n }\n li.appendChild(sessionUl);\n\n fragment.appendChild(li);\n });\n\n channelList.appendChild(fragment);\n\n // header \u94B1\u5305\u5FBD\u7AE0\u8BA1\u6570: \u53EA\u5728 channels \u53D8\u5316\u65F6\u5237\u65B0, \u907F\u514D\u6BCF\u6B21 renderChannels \u90FD\u91CD\u7B97\n refreshWalletBadge();\n\n // \u628A\u5F53\u524D\u6FC0\u6D3B\u7684 channel \u5E73\u6ED1\u6EDA\u5230\u89C6\u53E3\u5185 \u2014 \u7528\u6237\u5207\u6362\u540E\u4E0D\u4F1A\u770B\u4E0D\u5230\n // \u53EA\u5728\u975E\u7528\u6237\u4E3B\u52A8\u6EDA\u52A8\u72B6\u6001\u4E0B\u6267\u884C, \u907F\u514D\u4E0E\u6B63\u5728\u8FDB\u884C\u7684\u6EDA\u52A8\u51B2\u7A81\n if (currentChannelId) {\n requestAnimationFrame(() => scrollActiveChannelIntoView(false));\n }\n}\n\n/** \u628A\u5F53\u524D\u6FC0\u6D3B\u7684 channel \u6EDA\u5230\u4FA7\u8FB9\u680F\u89C6\u53E3\u5185 */\nfunction scrollActiveChannelIntoView(smooth = true) {\n if (!channelList || !currentChannelId) return;\n const active = channelList.querySelector(`.agent-group[data-channel-id=\"${currentChannelId}\"]`);\n if (!active) return;\n const listRect = channelList.getBoundingClientRect();\n const itemRect = active.getBoundingClientRect();\n const margin = 24; // \u89C6\u53E3\u4E0A\u4E0B\u5404\u7559 24px\n if (itemRect.top < listRect.top + margin) {\n channelList.scrollBy({ top: itemRect.top - listRect.top - margin, behavior: smooth ? 'smooth' : 'auto' });\n } else if (itemRect.bottom > listRect.bottom - margin) {\n channelList.scrollBy({ top: itemRect.bottom - listRect.bottom + margin, behavior: smooth ? 'smooth' : 'auto' });\n }\n}\n\nfunction formatSessionName(sess) {\n if (!sess) return '\u65B0\u4F1A\u8BDD';\n if (sess.preview && sess.preview.trim()) return sess.preview.trim();\n const id = sess.id || '';\n return id ? `\u4F1A\u8BDD ${id.slice(-6)}` : '\u65B0\u4F1A\u8BDD';\n}\n\n// 2026-06-15: escapeHtml \u5DF2\u8FC1\u5230 ui/message-renderer.js\nconst escapeHtml = MR_escapeHtml || ((s) => String(s ?? '').replace(/[&<>\"']/g, (c) => ({\n '&': '&', '<': '<', '>': '>', '\"': '"', \"'\": '''\n}[c])));\n\n// 2026-07-06: \u901A\u7528 channel name \u515C\u5E95 \u2014 \u9632\u6B62 name=undefined/null/'undefined' \u5B57\u4E32\n// \u65F6 UI \u51FA\u73B0 \"undefined\" \u5B57\u9762\u91CF. \u59D4\u6258 util/safe-name.ts (\u6709\u5355\u6D4B\u8986\u76D6).\nimport { safeChannelName } from './util/safe-name.js';\n\nfunction ensureMessageContainer(channelId) {\n if (!messagesContainers.has(channelId)) {\n const container = document.createElement('div');\n container.className = 'channel-messages';\n container.id = `channel-messages-${channelId}`;\n container.style.display = 'none';\n messagesEl.appendChild(container);\n messagesContainers.set(channelId, container);\n }\n return messagesContainers.get(channelId);\n}\n\nfunction showChannelView(channelId) {\n // Hide all channel message containers (\u4E0D\u8981 innerHTML='' \u9500\u6BC1, \u4FDD\u7559\u4EE5\u4FBF\u5FEB\u901F\u5207\u6362)\n messagesContainers.forEach((container, cid) => {\n container.style.display = 'none';\n });\n // Show the selected channel's container\n const container = messagesContainers.get(channelId);\n if (container) {\n container.style.display = 'block';\n }\n}\n\nasync function selectChannel(channelId, targetSessionId = null) {\n console.log('[selectChannel] \u5F00\u59CB\u5207\u6362\u5230:', channelId, 'targetSession:', targetSessionId);\n\n // \u7ACB\u5373\u66F4\u65B0\u5F53\u524D\u9891\u9053 ID\n currentChannelId = channelId;\n reconnectAttempts.set(channelId, 0);\n\n // v3: \u76FE\u724C\u5F39\u7A97\u6253\u5F00\u65F6, \u5207 channel \u8981\u5237\u5217\u8868 (tab \u6807\u9898 + \u5DF2\u7ED1/\u672A\u7ED1 \u5206\u7EC4)\n if (typeof judgmentsModal !== 'undefined' && judgmentsModal && judgmentsModal.classList.contains('active')) {\n if (typeof lastJudgmentsCache !== 'undefined') renderJudgments(lastJudgmentsCache);\n }\n\n // \u627E\u5230\u5F53\u524D\u9891\u9053\u548C session\n const channel = channels.find(c => c.id === channelId);\n if (channel) {\n if (channelNameEl) channelNameEl.textContent = safeChannelName(channel.name);\n currentSessionId = targetSessionId || channel.currentSessionId || 'default';\n if (targetSessionId) {\n channel.currentSessionId = targetSessionId;\n }\n // \u81EA\u52A8\u5C55\u5F00\u5F53\u524D\u667A\u80FD\u4F53\u7684\u4F1A\u8BDD\u5217\u8868\uFF0C\u8BA9\u7528\u6237\u80FD\u5207\u6362\u4F1A\u8BDD\n expandedAgents.add(channelId);\n console.log('[selectChannel] \u9891\u9053:', channel.name, 'session:', currentSessionId);\n }\n\n // 2026-06-11 \u63D0\u901F: \u5207 channel \u65F6 sidebar \u6E32\u67D3\u964D\u7EA7 \u2014 \u53EA\u66F4\u65B0 active \u6837\u5F0F, \u4E0D\u91CD\u6E32\u6574\u5217\u8868\n // renderChannels() \u4ECD\u7136\u8981\u8C03 (current session label \u7B49\u53EF\u80FD\u53D8\u4E86), \u4F46\u52A0\u4E00\u5C42\u5224\u65AD: \u5982\u679C\u53EA\u662F\u5207 channel (\u6CA1\u589E\u5220), \u8D70 patch \u8DEF\u5F84\n const t0 = performance.now();\n renderChannelsLite(channelId, currentSessionId);\n console.log(`[selectChannel] renderChannelsLite \u8017\u65F6 ${(performance.now() - t0).toFixed(1)}ms`);\n\n // \u786E\u4FDD\u8BE5\u9891\u9053\u6709\u6D88\u606F\u5BB9\u5668\n const container = ensureMessageContainer(channelId);\n\n // \u5207\u6362\u5230\u8BE5\u9891\u9053\u7684\u89C6\u56FE\n showChannelView(channelId);\n\n // \u5982\u679C\u8FD8\u6CA1\u6709 SSE \u8FDE\u63A5\uFF0C\u5EFA\u7ACB\u8FDE\u63A5\n if (!eventSources.has(channelId)) {\n console.log('[selectChannel] \u5EFA\u7ACB SSE \u8FDE\u63A5');\n connect(channelId);\n }\n\n // \u76F4\u63A5\u4ECE server \u62C9 session \u6D88\u606F (container \u8DE8 session \u5171\u4EAB, \u5148\u6E05\u7A7A\u518D\u52A0\u8F7D)\n container.innerHTML = '';\n try {\n const res = await fetch(`/sessions/${channelId}?sessionId=${encodeURIComponent(currentSessionId)}`);\n const session = await res.json();\n const msgs = session.messages || [];\n if (msgs.length > 0) {\n // 2026-06-11 \u63D0\u901F: \u7528 DocumentFragment \u4E00\u6B21\u6027 append \u907F\u514D\u591A\u6B21 reflow\n const frag = document.createDocumentFragment();\n const tmpContainer = document.createElement('div');\n tmpContainer.style.display = 'none';\n for (const msg of msgs) {\n addMessage(msg.content, msg.type, false, tmpContainer, msg.metadata?.usedJudgmentIds || []);\n }\n while (tmpContainer.firstChild) {\n frag.appendChild(tmpContainer.firstChild);\n }\n container.appendChild(frag);\n } else {\n addMessage('\u4F60\u597D\uFF01\u6211\u662F Bolloon Agent\u3002\u6709\u4EC0\u4E48\u6211\u53EF\u4EE5\u5E2E\u4F60\u7684\u5417\uFF1F', 'ai', false, container);\n }\n } catch (err) {\n console.error('[selectChannel] \u52A0\u8F7D session \u5931\u8D25:', err);\n addMessage('\u4F60\u597D\uFF01\u6211\u662F Bolloon Agent\u3002\u6709\u4EC0\u4E48\u6211\u53EF\u4EE5\u5E2E\u4F60\u7684\u5417\uFF1F', 'ai', false, container);\n }\n}\n\nasync function loadSession(channelId, sessionId = null) {\n const container = messagesContainers.get(channelId);\n if (!container) return;\n const targetSessionId = sessionId || currentSessionId || 'default';\n try {\n const res = await fetch(`/sessions/${channelId}?sessionId=${encodeURIComponent(targetSessionId)}`);\n const session = await res.json();\n container.innerHTML = '';\n if (session.messages && session.messages.length > 0) {\n session.messages.forEach(msg => {\n addMessage(msg.content, msg.type, false, container, msg.metadata?.usedJudgmentIds || []);\n });\n } else {\n addMessage('\u4F60\u597D\uFF01\u6211\u662F Bolloon Agent\u3002\u6709\u4EC0\u4E48\u6211\u53EF\u4EE5\u5E2E\u4F60\u7684\u5417\uFF1F', 'ai', false, container);\n }\n } catch (err) {\n console.error('Failed to load session:', err);\n container.innerHTML = '';\n addMessage('\u4F60\u597D\uFF01\u6211\u662F Bolloon Agent\u3002\u6709\u4EC0\u4E48\u6211\u53EF\u4EE5\u5E2E\u4F60\u7684\u5417\uFF1F', 'ai', false, container);\n }\n}\n\n// 2026-06-15: addMessage \u59D4\u6258\u7ED9 ui/message-renderer.js, \u5BA2\u6237\u7AEF\u4EE3\u7801\u539F\u4F4D\u8C03\u7528\u540C\u540D\u51FD\u6570, \u4E0D\u611F\u77E5\u62C6\u5206\nfunction addMessage(content, type, save = true, container, usedJudgmentIds = []) {\n return MR_addMessage(content, type, save, container, usedJudgmentIds, getRendererCtx());\n}\n\n// 2026-06-15: stream / done \u4E8B\u4EF6\u4E5F\u59D4\u6258\u7ED9 ui/message-renderer.js.\n// \u4FDD\u7559\u540C\u540D wrapper, \u8BA9 SSE \u5206\u53D1\u4EE3\u7801\u4E0D\u611F\u77E5\u6A21\u5757\u62C6\u5206\u3002\nfunction handleStreamTokenEvent(data) {\n return MR_handleStreamTokenEvent(data, getRendererCtx());\n}\n\nfunction finalizeTimelineAsMessage() {\n return MR_finalizeTimelineAsMessage(getRendererCtx());\n}\n\nfunction handleStepEvent(data) {\n return MR_handleStepEvent(data, getRendererCtx());\n}\n\n\n// ============================================================\n// 2026-06-15: \u65E7 timeline panel + 3 \u72B6\u6001\u673A + workflowDisplayEl \u5168\u90E8\u5220\u9664\n// \u65B0\u7EC4\u4EF6: step-timeline (\u6C14\u6CE1\u5185 4 \u72B6\u6001\u6B65\u9AA4\u6761, \u89C1 src/web/ui/step-timeline.ts)\n// ============================================================\n\nlet lastUsedJudgmentIds = []; // \u7528\u4E8E finalizeTimelineAsMessage \u7ED9 addMessage \u7B2C 5 \u53C2\n\n// ============================================================================\n// 2026-06-15: self_improve SSE handler \u2014 \u4E4B\u524D server \u63A8 self_improve_triggered\n// / self_improve_result \u4F46 client \u5B8C\u5168\u6CA1\u6CE8\u518C, \u6D88\u606F\u5C31\u4E22\u4E86 (Bug 2).\n// \u4FEE: \u5373\u65F6 render \u5361\u7247\u5230 messages \u5BB9\u5668\u5185 (Bug 4), \u7528\u4E3B\u9898\u8272 var(--accent/--success/--warning) (Bug 3).\n// \u5931\u8D25\u91CD\u8BD5: \u5361\u7247\u7684 retry \u6309\u94AE \u2192 POST /self-improve \u518D\u89E6\u53D1.\n// ============================================================================\nlet selfImproveCardSeq = 0;\nfunction getMessagesContainerForCurrent() {\n if (currentChannelId && messagesContainers.get(currentChannelId)) {\n return messagesContainers.get(currentChannelId);\n }\n return messagesEl; // fallback: \u4E3B messages\n}\n\nfunction makeSelfImproveCard(data) {\n const seq = ++selfImproveCardSeq;\n const id = `self-improve-card-${seq}`;\n // \u7528\u4E3B\u9898\u8272 var(--accent/--success/--warning) \u4E0D\u5199\u6B7B (Bug 3)\n const card = document.createElement('div');\n card.className = 'self-improve-card';\n card.id = id;\n card.dataset.seq = String(seq);\n card.style.cssText = 'margin:8px 12px;padding:10px 12px;border:1px solid var(--accent);border-left:3px solid var(--accent);border-radius:6px;background:var(--bg-hover);color:var(--text);font-size:12px;line-height:1.5;';\n card.innerHTML = `\n <div class=\"self-improve-header\" style=\"display:flex;align-items:center;gap:8px;cursor:pointer;user-select:none;\">\n <span class=\"self-improve-caret\" style=\"font-size:10px;color:var(--text-muted);\">\u25BE</span>\n <span class=\"self-improve-title\" style=\"flex:1;font-weight:600;color:var(--accent);\"></span>\n <span class=\"self-improve-status\" style=\"font-size:10px;color:var(--text-muted);\"></span>\n </div>\n <div class=\"self-improve-body\" style=\"margin-top:6px;display:none;color:var(--text-muted);white-space:pre-wrap;word-break:break-word;\"></div>\n `;\n // \u6298\u53E0 (Bug 4: \u5361\u7247\u5185\u81EA\u5E26\u6298\u53E0, \u8DDF\u5BF9\u8BDD\u6D88\u606F\u540C\u4E00\u5BB9\u5668)\n const header = card.querySelector('.self-improve-header');\n const body = card.querySelector('.self-improve-body');\n const caret = card.querySelector('.self-improve-caret');\n header.addEventListener('click', () => {\n const collapsed = body.style.display === 'none';\n body.style.display = collapsed ? 'block' : 'none';\n caret.style.transform = collapsed ? 'rotate(0deg)' : 'rotate(-90deg)';\n });\n return card;\n}\n\nfunction handleSelfImproveTriggered(data) {\n const container = getMessagesContainerForCurrent();\n if (!container) return;\n const card = makeSelfImproveCard(data);\n card.querySelector('.self-improve-title').textContent =\n `\uD83E\uDDE0 \u81EA\u8FED\u4EE3\u89E6\u53D1 \u00B7 ${data.eventKind || 'unknown'}`;\n card.querySelector('.self-improve-status').textContent =\n new Date(data.ts || Date.now()).toLocaleTimeString();\n const body = card.querySelector('.self-improve-body');\n body.textContent = JSON.stringify({\n eventKind: data.eventKind,\n details: data.details,\n goal: data.goal,\n }, null, 2);\n container.appendChild(card);\n card.scrollIntoView({ block: 'end', behavior: 'smooth' });\n}\n\nfunction handleSelfImproveResult(data) {\n const container = getMessagesContainerForCurrent();\n if (!container) return;\n const card = makeSelfImproveCard(data);\n const ok = !!data.success;\n card.style.borderColor = ok ? 'var(--success)' : 'var(--warning)';\n card.style.borderLeftColor = ok ? 'var(--success)' : 'var(--warning)';\n card.querySelector('.self-improve-title').textContent =\n `${ok ? '\u2705' : '\u26A0\uFE0F'} \u81EA\u8FED\u4EE3\u5B8C\u6210 \u00B7 ${ok ? '\u6210\u529F' : '\u5931\u8D25'}`;\n card.querySelector('.self-improve-status').textContent =\n new Date(data.ts || Date.now()).toLocaleTimeString();\n const body = card.querySelector('.self-improve-body');\n body.textContent = (ok ? (data.output || '') : (data.error || '')) || '(no output)';\n // \u5931\u8D25 \u2192 \u52A0 retry \u6309\u94AE (Bug 2 \u91CD\u8BD5\u673A\u5236)\n if (!ok) {\n const btn = document.createElement('button');\n btn.textContent = '\uD83D\uDD01 \u91CD\u8BD5';\n btn.style.cssText = 'margin-top:6px;padding:4px 10px;background:var(--accent);color:var(--bg-main);border:none;border-radius:4px;cursor:pointer;font-size:11px;';\n btn.onclick = async () => {\n btn.disabled = true;\n btn.textContent = '\u23F3 \u91CD\u8BD5\u4E2D...';\n try {\n const r = await fetch('/self-improve', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ reason: 'user retry from UI card' }),\n });\n if (!r.ok) throw new Error(`HTTP ${r.status}`);\n btn.textContent = '\u2713 \u5DF2\u91CD\u8BD5';\n } catch (err) {\n btn.disabled = false;\n btn.textContent = '\uD83D\uDD01 \u91CD\u8BD5 (\u5931\u8D25)';\n body.textContent += `\\n[retry error] ${(err && err.message) || err}`;\n }\n };\n body.appendChild(btn);\n }\n container.appendChild(card);\n card.scrollIntoView({ block: 'end', behavior: 'smooth' });\n}\n\n\n\n// ============================================================\n// 2026-06-15: \u53D1\u9001\u6309\u94AE \u2194 \u7EC8\u6B62\u6309\u94AE \u72B6\u6001\u673A\n// idle: sendMessage \u5165\u53E3, \u98DE\u673A\u56FE\u6807\n// abort: \u6D41\u5F0F\u671F\u95F4, \u25A2 \u56FE\u6807 + \u7EA2\u8FB9\u6846, click \u8C03 abortCurrentRun\n// aborting: \u5DF2\u70B9\u7EC8\u6B62, \u7B49\u5F85 server \u53CD\u9988, \u534A\u900F\u660E\n// done/error \u4E8B\u4EF6\u89E6\u53D1\u65F6\u56DE\u5230 idle\n// ============================================================\nfunction setSendMode(mode) {\n if (!sendBtn) return;\n sendBtn.dataset.state = mode;\n sendBtn.title = mode === 'abort' ? '\u23F9 \u7EC8\u6B62\u5F53\u524D\u751F\u6210 (Esc)' : '\u53D1\u9001 (Enter)';\n // \u5207 svg \u663E\u793A\n const sendIcon = sendBtn.querySelector('[data-mode=\"send\"]');\n const abortIcon = sendBtn.querySelector('[data-mode=\"abort\"]');\n if (sendIcon) sendIcon.style.display = mode === 'idle' ? '' : 'none';\n if (abortIcon) abortIcon.style.display = mode === 'idle' ? 'none' : '';\n}\n\nasync function abortCurrentRun() {\n if (sendBtn && sendBtn.dataset.state === 'aborting') return; // \u9632\u53CC\u51FB\n setSendMode('aborting');\n try {\n const r = await fetch('/api/chat/abort', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ channelId: currentChannelId || '' }),\n });\n const j = await r.json().catch(() => ({}));\n if (j.aborted) {\n if (typeof showSimpleToast === 'function') showSimpleToast('\u2713 \u5DF2\u7EC8\u6B62');\n } else {\n if (typeof showSimpleToast === 'function') showSimpleToast('\u25CB \u5F53\u524D\u65E0\u8FD0\u884C\u4E2D');\n }\n } catch (err) {\n console.error('[abort] error:', err);\n if (typeof showSimpleToast === 'function') showSimpleToast('\u2717 \u7EC8\u6B62\u5931\u8D25');\n }\n // 1.5s \u540E\u56DE\u5230 idle (server \u63A8 done/error \u65F6\u4F1A\u7ACB\u5373\u518D\u5207, \u8FD9\u91CC\u53EA\u662F\u515C\u5E95)\n setTimeout(() => {\n if (sendBtn && sendBtn.dataset.state === 'aborting') setSendMode('idle');\n }, 1500);\n}\n\n\n\nfunction connect(channelId) {\n const targetChannelId = channelId || currentChannelId;\n if (!targetChannelId) return;\n\n // \u6E05\u9664\u8BE5\u9891\u9053\u7684\u91CD\u8FDE\u5B9A\u65F6\u5668\n if (reconnectTimers.has(targetChannelId)) {\n clearTimeout(reconnectTimers.get(targetChannelId));\n reconnectTimers.delete(targetChannelId);\n }\n\n // \u6E05\u9664\u8BE5\u9891\u9053\u7684\u5FC3\u8DF3\u5B9A\u65F6\u5668 (\u9632\u6B62\u591A\u6B21\u8C03\u7528 connect \u5BFC\u81F4 setInterval \u7D2F\u79EF)\n if (heartbeatTimers.has(targetChannelId)) {\n clearInterval(heartbeatTimers.get(targetChannelId));\n heartbeatTimers.delete(targetChannelId);\n }\n\n // \u5173\u95ED\u8BE5\u9891\u9053\u7684\u65E7\u8FDE\u63A5\n if (eventSources.has(targetChannelId)) {\n eventSources.get(targetChannelId).close();\n eventSources.delete(targetChannelId);\n }\n\n const sseUrl = `/events?channelId=${encodeURIComponent(targetChannelId)}`;\n console.log('[connect] \u521B\u5EFA SSE \u8FDE\u63A5:', sseUrl);\n\n const eventSource = new EventSource(sseUrl);\n eventSources.set(targetChannelId, eventSource);\n\n if (!reconnectAttempts.has(targetChannelId)) {\n reconnectAttempts.set(targetChannelId, 0);\n }\n\n // 2026-07-06: \u8BB0\u5FC6\"\u6BCF\u4E2A channel \u6536\u5230\u7684\u6700\u5927 seq + \u5DF2\u6E32\u67D3 msgId\", \u91CD\u8FDE\u540E\u80FD\u68C0\u6D4B\u65AD\u7EBF\u671F\u95F4\u4E22\u7684\u4E8B\u4EF6\n let localMaxSeq: number = lastKnownSeq.get(targetChannelId) || 0;\n const seenMsgIds: Set<string> = new Set(lastSeenMsgIds.get(targetChannelId) || []);\n\n eventSource.onopen = () => {\n console.log('[SSE] \u5DF2\u8FDE\u63A5 channelId:', targetChannelId);\n reconnectAttempts.set(targetChannelId, 0);\n // \u91CD\u8FDE\u540E\u4E3B\u52A8\u8C03\u7528 /api/chat/resume, \u62FF\u56DE\u65AD\u7EBF\u671F\u95F4\u6F0F\u7684\u4E8B\u4EF6 (\u5C24\u5176\u662F type: ai \u6536\u5C3E + done)\n // \u9632\u6B62\"\u6C14\u6CE1\u4E3A\u7A7A\" + \"\u5361\u5728 streaming\"\u7684\u6839\u672C\u95EE\u9898\n (async () => {\n try {\n const resp = await fetch('/api/chat/resume', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n channelId: targetChannelId,\n sessionId: currentSessionId || 'default',\n afterSeq: localMaxSeq,\n }),\n });\n const data = await resp.json();\n if (data?.ok && (data.missedSome || data.stillRunning)) {\n console.log('[SSE-resume] \u6062\u590D channel=', targetChannelId, 'curSeq=', data.currentSeq, 'recovered=', data.recoveredMessages?.length || 0, 'stillRunning=', !!data.stillRunning);\n // \u628A server \u7ED9\u7684\u6062\u590D\u5305, \u7ECF\u8FC7 onmessage \u540C\u6837\u7684\u8DEF\u7531\n for (const msg of (data.recoveredMessages || [])) {\n if (msg.msgId && seenMsgIds.has(msg.msgId)) continue;\n if (msg.msgId) {\n seenMsgIds.add(msg.msgId);\n lastSeenMsgIds.set(targetChannelId, Array.from(seenMsgIds).slice(-100));\n }\n const container = messagesContainers.get(targetChannelId) || messagesEl;\n if (msg.type === 'ai') {\n if (!MR_hasStreamingText()) {\n addMessage(msg.content, 'ai', true, container, lastUsedJudgmentIds || []);\n } else {\n // \u5982\u679C\u8FD8\u5728 streaming, \u5F3A\u5236\u628A streamingText \u66FF\u6362\u4E3A recovered content, \u7136\u540E finalize\n MR_replaceStreamingText?.(msg.content);\n MR_finalizeTimelineAsMessage(getRendererCtx());\n }\n } else if (msg.type === 'user') {\n if (msg.source === 'remote' || msg.source === 'local') {\n addMessage(msg.content, 'user', true, container);\n }\n }\n }\n // \u5982\u679C server \u8BF4\u8FD8\u5728 running, \u5C31\u5F53 abort \u72B6\u6001\u5904\u7406 (\u524D\u7AEF\u53EF\u4EE5\u91CD\u65B0\u6E32\u67D3\"AI \u601D\u8003\u4E2D\")\n if (data.stillRunning && !MR_hasStreamingText() && data.partialText) {\n // \u89E6\u53D1 streaming \u7684\"\u6062\u590D\" \u2014 \u6A21\u62DF\u4E00\u4E2A token \u56DE\u8C03\u628A partialText \u6CE8\u5165, \u7136\u540E finalize\n try {\n MR_injectRecoveredText?.(data.partialText);\n } catch (e) { console.warn('[SSE-resume] inject \u5931\u8D25:', e); }\n }\n }\n // \u540C\u6B65 currentSeq \u7ED9\u540E\u7EED\u4E8B\u4EF6\u7528\n if (typeof data?.currentSeq === 'number') {\n localMaxSeq = Math.max(localMaxSeq, data.currentSeq);\n lastKnownSeq.set(targetChannelId, localMaxSeq);\n }\n } catch (e) {\n console.warn('[SSE-resume] \u8BF7\u6C42\u5931\u8D25:', e);\n }\n })();\n };\n\n // \u5FC3\u8DF3\u8D85\u65F6: 2026-06-16 \u6536\u7D27\u5230 30s (\u914D\u5408 server \u7AEF 30s ping).\n // \u4E4B\u524D 60s \u5728 mobile/sleep \u5524\u9192\u540E\u8BEF\u5224; \u540E\u7AEF ping \u5DF2\u6539\u4E3A data: {\"type\":\"ping\"}, onmessage \u4F1A\u91CD\u7F6E lastEventTime.\n // \u8986\u76D6\u7F51\u7EDC\u534A\u5F00 / \u6D4F\u89C8\u5668\u6CA1\u89E6\u53D1 onerror \u7684\u60C5\u51B5\n let lastEventTime = Date.now();\n const heartbeatTimer = setInterval(() => {\n if (!eventSources.has(targetChannelId)) {\n clearInterval(heartbeatTimer);\n return;\n }\n if (Date.now() - lastEventTime > 30000) {\n console.warn('[SSE] 30s \u65E0\u6570\u636E, \u5F3A\u5236\u91CD\u5EFA\u8FDE\u63A5:', targetChannelId);\n clearInterval(heartbeatTimer);\n try { eventSource.close(); } catch {}\n eventSources.delete(targetChannelId);\n // \u9000\u907F\u91CD\u8FDE (\u6709\u4E0A\u9650)\n const attempts = (reconnectAttempts.get(targetChannelId) || 0) + 1;\n reconnectAttempts.set(targetChannelId, attempts);\n const delay = Math.min(1000 * Math.pow(2, attempts - 1), 15000);\n const timer = setTimeout(() => connect(targetChannelId), delay);\n reconnectTimers.set(targetChannelId, timer);\n }\n }, 10000);\n\n // onerror: \u4E0D\u8981\u624B\u52A8 close \u2014 \u6D4F\u89C8\u5668 EventSource \u4F1A\u81EA\u52A8\u91CD\u8FDE\n // \u6211\u4EEC\u53EA\u9700\u5728 readyState \u6C38\u4E45 CLOSED \u65F6 (\u7F55\u89C1) \u624D\u4ECB\u5165\n eventSource.onerror = () => {\n console.warn('[SSE] \u9519\u8BEF, \u6D4F\u89C8\u5668\u81EA\u52A8\u91CD\u8FDE\u4E2D:', targetChannelId, 'readyState=', eventSource.readyState);\n if (eventSource.readyState === EventSource.CLOSED) {\n // \u6D4F\u89C8\u5668\u653E\u5F03\u91CD\u8FDE, \u6211\u4EEC\u63A5\u624B\n clearInterval(heartbeatTimer);\n eventSources.delete(targetChannelId);\n const attempts = (reconnectAttempts.get(targetChannelId) || 0) + 1;\n reconnectAttempts.set(targetChannelId, attempts);\n const delay = Math.min(1000 * Math.pow(2, attempts - 1), 15000);\n const timer = setTimeout(() => connect(targetChannelId), delay);\n reconnectTimers.set(targetChannelId, timer);\n }\n };\n\n eventSource.onmessage = (e) => {\n lastEventTime = Date.now();\n try {\n const data = JSON.parse(e.data);\n // 2026-06-16: server ping \u8D70 data: {\"type\":\"ping\"}, onmessage \u6536\u5230\u540E\u91CD\u7F6E lastEventTime \u540E\u7ACB\u5373\u8FD4\u56DE.\n // \u4E0D\u8D70\u901A\u7528\u8DEF\u7531 \u2014 \u6D41\u5F0F\u5143\u7D20\u3001\u6D41\u5F0F\u5BB9\u5668\u3001step-timeline \u90FD\u4E0D\u52A8, \u907F\u514D reset \u8BEF\u6E05.\n if (data && data.type === 'ping') {\n return;\n }\n // 2026-07-06: msgId \u53BB\u91CD + seq \u8DDF\u8E2A (\u8BA9 SSE \u91CD\u8FDE\u540E\u80FD\u7CBE\u786E\u8865\u5305)\n if (data?.msgId) {\n if (seenMsgIds.has(data.msgId)) {\n return; // \u5DF2\u6E32\u67D3\u8FC7 \u2014 \u8DF3\u8FC7 (\u9632 dup)\n }\n seenMsgIds.add(data.msgId);\n lastSeenMsgIds.set(targetChannelId, Array.from(seenMsgIds).slice(-100));\n }\n if (typeof data?.seq === 'number') {\n if (data.seq > localMaxSeq) localMaxSeq = data.seq;\n lastKnownSeq.set(targetChannelId, localMaxSeq);\n }\n const msgChannelId = data.channelId || targetChannelId;\n console.log('[SSE] \u6536\u5230\u6D88\u606F:', data.type, 'channelId:', msgChannelId, 'msgId:', data.msgId);\n\n // \u8DEF\u7531\u6D88\u606F\u5230\u6B63\u786E\u7684\u9891\u9053\n // \u53EA\u6709 envelope.channelId \u5B58\u5728\u4E14\u4E0E\u76EE\u6807\u4E0D\u540C\u65F6\u624D\u4E22\u5F03 (\u7A7A/undefined \u89C6\u4E3A\u5E7F\u64AD\u7ED9\u81EA\u5DF1)\n if (msgChannelId && msgChannelId !== targetChannelId) {\n console.log('[SSE] \u5FFD\u7565\u975E\u76EE\u6807\u9891\u9053\u6D88\u606F');\n return;\n }\n\n // \u4F7F\u7528\u6B63\u786E\u7684\u6D88\u606F\u5BB9\u5668\n const container = messagesContainers.get(msgChannelId) || messagesEl;\n\n if (data.type === 'user') {\n // 2026-06-11 \u4FEE: \u4E0D\u518D\u8D70 showUserCommand (\u203A \u88C5\u9970\u6761) \u8DEF\u5F84, \u56E0\u4E3A:\n // 1. sendMessage \u5DF2\u7ECF\u5728\u5BA2\u6237\u7AEF addMessage(text, 'user', true) \u6E32\u67D3\u6210 .bubble-user \u6C14\u6CE1\n // 2. SSE \u63A8 user \u53C8\u8C03 showUserCommand \u2192 \u540C\u65F6\u51FA\u73B0\u6C14\u6CE1 + \u88C5\u9970\u6761 (\u53CC\u91CD\u663E\u793A)\n // 3. \u7B2C\u4E8C\u6B21\u5207 channel \u65F6, showUserCommand \u4F1A remove \u5DF2\u6709 .message-user \u5143\u7D20 (line 1477),\n // \u4F46 .bubble-user class \u4E0D\u662F .message-user \u2192 \u6B8B\u7559\u88C5\u9970\u6761, \u8868\u73B0\"\u6A21\u5F0F\u53D8\u4E86\"\n // \u6539\u6CD5: SSE \u6536\u5230 user \u540E, \u8DF3\u8FC7\u663E\u793A (lastUserCommand \u5DF2\u7ECF\u5339\u914D, addMessage(save=true) \u5185\u90E8\u53BB\u91CD)\n // \u4F46\u8981\u786E\u4FDD lastUserCommand \u5DF2\u7ECF\u8BBE\u8FC7 \u2014 sendMessage \u8C03 addMessage(true) \u65F6\u4F1A\u8BBE\n // \u8FDC\u7AEF user (source === 'remote') \u4E0D\u4F1A\u88AB sendMessage \u6E32\u67D3, \u9700\u8981\u8D70 addMessage \u4E00\u6B21\n if (data.source === 'remote') {\n // \u8FDC\u7AEF\u8BBF\u5BA2 (B \u901A\u8FC7 P2P \u53D1\u6765\u7684), sendMessage \u6CA1\u6E32\u67D3\u5B83, \u8FD9\u91CC\u8865\u4E0A\u6C14\u6CE1\n addMessage(data.content, 'user', true, container);\n }\n // \u672C\u5730 user \u5DF2\u7ECF\u7531 sendMessage \u6E32\u67D3 + \u53BB\u91CD, \u8FD9\u91CC\u4E0D\u518D\u663E\u793A\n } else if (data.type === 'ai') {\n // 2026-06-17: \u9632\u53CC\u6C14\u6CE1 \u2014 \u6D41\u5F0F\u8FC7\u7A0B\u4E2D streamingText \u5DF2\u7D2F\u79EF\u540C\u4E00\u4EFD fullResponse,\n // server `ai` \u4E8B\u4EF6\u643A\u5E26\u7684 content \u4E0E\u4E4B\u91CD\u590D, \u8DF3\u8FC7;\u771F\u6B63\u6E32\u67D3\u8D70 done \u2192 finalizeTimelineAsMessage.\n // \u975E\u6D41\u5F0F\u573A\u666F (\u8FDC\u7AEF P2P \u76F4\u63A8 / \u6D41\u5F0F\u524D chat \u5F02\u5E38\u4E2D\u65AD) streamingText \u4E3A\u7A7A, \u6B63\u5E38 addMessage.\n if (!MR_hasStreamingText()) {\n addMessage(data.content, 'ai', true, container, lastUsedJudgmentIds);\n }\n } else if (data.type === 'stream') {\n if (data.streamType === 'thinking' || data.streamType === 'token') {\n handleStreamTokenEvent(data);\n }\n } else if (data.type === 'regenerating') {\n // \u5220\u65E7\u7684\u6700\u540E\u4E00\u6761 AI \u6D88\u606F, \u51C6\u5907\u91CD\u65B0\u751F\u6210\n const messages = container.querySelectorAll('.message-ai');\n if (messages.length > 0) {\n const lastAiMsg = messages[messages.length - 1];\n lastAiMsg.remove();\n }\n // \u91CD\u65B0\u8FDB\u5165 abort \u6A21\u5F0F (\u65B0\u4E00\u6B21\u751F\u6210\u5F00\u59CB)\n setSendMode('abort');\n } else if (data.type === 'status') {\n // 2026-06-16: status \u4E8B\u4EF6\u6E32\u67D3\u5230 system \u7EA7 status bar (tool=loop/compactor/recovery)\n // \u65E7 step_timeline \u72B6\u6001\u673A\u8D70 step_* \u4E8B\u4EF6, \u4E0D\u53D7\u5F71\u54CD\n renderLoopStatusBar(data.tool, data.content);\n } else if (data.type === 'step_start' || data.type === 'step_done' || data.type === 'step_error') {\n // 2026-06-15: \u6B65\u9AA4\u72B6\u6001\u673A\u4E8B\u4EF6 \u2014 \u63A8\u7ED9 message-renderer \u7684 step-timeline\n handleStepEvent(data);\n } else if (data.type === 'done') {\n // AI \u56DE\u590D\u751F\u6210\u5B8C, \u4ECE\u6D41\u5F0F\u5143\u7D20\u642C token \u6587\u672C\u5230\u6B63\u5F0F\u6D88\u606F\n finalizeTimelineAsMessage();\n // 2026-06-16: \u9690\u85CF\u5FAA\u73AF\u8FDB\u5EA6 status bar\n hideLoopStatusBar();\n // 2026-06-15: \u5207\u56DE idle \u6A21\u5F0F (\u7528\u6237\u53EF\u53D1\u4E0B\u4E00\u6761)\n setSendMode('idle');\n } else if (data.type === 'renamed') {\n const channel = channels.find(c => c.id === data.channelId);\n if (channel) {\n channel.name = data.newName;\n renderChannels();\n if (currentChannelId === data.channelId && channelNameEl) {\n channelNameEl.textContent = safeChannelName(data.newName);\n }\n }\n } else if (data.type === 'error') {\n // 2026-06-15: \u9519\u8BEF\u4E0D\u518D addMessage \u6210 AI \u6C14\u6CE1 \u2014 server \u63A8 error \u65F6, stream \u7684\n // \u90E8\u5206 token \u5DF2\u7ECF\u88AB\u6D41\u5F0F\u5143\u7D20\u6536\u4E0B, finalizeTimelineAsMessage \u4E5F\u4F1A\u88AB done \u89E6\u53D1\n // \u518D\u6E32\u67D3\u4E00\u6B21, \u8DDF\u9519\u8BEF\u6587\u672C\u6DF7\u5728\u4E00\u8D77\u5C31\u662F\u7528\u6237\u770B\u5230\u7684\"\u591A\u4E86\u4E00\u4E2A\u6C14\u6CE1 + \u534A\u4E2A system context\"\n // \u6539: error \u8D70 toast \u63D0\u793A, \u7559\u7ED9\u6D41\u5F0F finalize \u5904\u7406\u6B8B\u4F59\n if (typeof showSimpleToast === 'function') {\n const msg = String(data.content || '').slice(0, 200);\n showSimpleToast('\u26A0\uFE0F ' + msg);\n } else {\n console.error('[SSE] error:', data.content);\n }\n // 2026-06-16: error \u89E6\u53D1\u65F6\u4E5F\u9690\u85CF status bar (\u4EFB\u52A1\u4E2D\u6B62)\n hideLoopStatusBar();\n setSendMode('idle');\n } else if (data.type === 'task_status' || data.type === 'workflow_step' || data.type === 'workflow_loop') {\n // 2026-06-16: \u65E7\u5DE5\u4F5C\u6D41\u4E8B\u4EF6, \u4E0D\u518D\u5355\u72EC\u753B \u2014 server \u4ECD\u53EF\u63A8, \u5BA2\u6237\u7AEF\u4EC5 log\n // \u7279\u522B: server \u7AEF token/thinking \u6D41\u4F1A\u540C\u65F6\u63A8\u4E00\u4EFD workflow_step (title=\"AI \u601D\u8003\"),\n // \u8DDF\u524D\u7AEF message-renderer \u7684 think \u6298\u53E0\u5757\u91CD\u590D, \u8FD9\u91CC\u7EDF\u4E00\u4E22\u5F03.\n // \u5F00\u53D1\u8005\u6A21\u5F0F\u60F3\u770B\u539F\u59CB\u4E8B\u4EF6, \u6253\u5F00 console.log \u8FC7\u6EE4 \"[SSE] workflow\"\n if (data.type === 'workflow_step' && (data.step === 'AI \u601D\u8003' || data.step === '\u5F00\u59CB\u601D\u8003')) {\n return;\n }\n console.log('[SSE] workflow (deprecated for UI):', data.type, data.content?.slice(0, 80));\n } else if (data.type === 'phase') {\n // phase \u4E8B\u4EF6 (\u6CE8\u5165\u95E8 / D \u89E6\u53D1) \u4ECD\u53EF\u63A8, \u5BA2\u6237\u7AEF\u4EC5 log\n console.log('[SSE] phase (no UI):', data.phase);\n } else if (data.type === 'queue_update') {\n // \u961F\u5217\u4E8B\u4EF6\u65E0 UI \u5165\u53E3, \u4EC5 log\n console.log('[SSE] queue_update (no UI):', data.queueLength);\n } else if (data.type === 'used_judgments' && Array.isArray(data.usedIds)) {\n // \u6CE8\u5165\u95E8\u56DE\u4F20: \u4FDD\u5B58 usedIds, finalizeTimelineAsMessage \u65F6\u7ED9 addMessage\n lastUsedJudgmentIds = data.usedIds;\n } else if (data.type === 'self_improve_triggered') {\n // 2026-06-15: \u5373\u65F6 render (\u4E0D\u518D\u4E22\u6D88\u606F, \u4FEE Bug 2)\n handleSelfImproveTriggered(data);\n } else if (data.type === 'self_improve_result') {\n // 2026-06-15: \u5373\u65F6 render + \u5931\u8D25 retry \u6309\u94AE (\u4FEE Bug 2/3)\n handleSelfImproveResult(data);\n }\n } catch (parseErr) {\n console.error('[SSE] \u89E3\u6790\u9519\u8BEF', parseErr);\n }\n };\n}\n\nasync function sendMessage() {\n const text = input.value.trim();\n if (!text) return;\n\n // 2026-06-11: \u7ACB\u5373\u628A\u7528\u6237\u6D88\u606F\u6E32\u67D3\u6210\u6C14\u6CE1\u4E0A\u5C4F (\u8D70 .bubble-user, \u8DDF\u672C\u5730\u804A\u5929\u4E00\u81F4)\n // \u4E4B\u524D\u53EA\u9760 SSE `type: user` \u56DE\u8C03\u663E\u793A, \u4F46 addMessage(user) \u9ED8\u8BA4 save=true \u8D70\u53BB\u91CD, \u5BB9\u6613\u8DDF SSE \u4E8C\u6B21\u663E\u793A\u51B2\u7A81/\u4E22\u5931\n // \u73B0\u5728: sendMessage \u81EA\u5DF1\u4E0A\u5C4F, SSE `user` \u56DE\u8C03\u6765\u65F6\u56E0\u4E3A lastUserCommand \u5DF2\u5339\u914D, \u81EA\u52A8\u8DF3\u8FC7 \u2192 \u4E0D\u91CD\u590D\n const container = messagesContainers.get(currentChannelId) || messagesEl;\n addMessage(text, 'user', true, container);\n // \u6EDA\u52A8\u5230\u5E95\n if (container) container.scrollTop = container.scrollHeight;\n\n input.value = '';\n // 2026-06-15: \u5207\u5230 abort \u6A21\u5F0F, \u7528\u6237\u53EF\u70B9\u6309\u94AE\u6216\u6309 Esc \u7EC8\u6B62\n setSendMode('abort');\n\n // \u7ACB\u5373\u628A\u7528\u6237\u6D88\u606F\u843D\u76D8, \u907F\u514D\u5207\u8D70\u518D\u5207\u56DE\u65F6\u4E22\u5931\n persistLastMessageToServer('user', text);\n\n // \u83B7\u53D6\u5F53\u524D\u9891\u9053\u7684 DID\n const channel = channels.find(c => c.id === currentChannelId);\n const channelDid = channel?.did || '';\n console.log('[\u53D1\u9001\u6D88\u606F] \u9891\u9053 DID:', channelDid);\n\n try {\n const res = await fetch('/message', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n text,\n channelId: currentChannelId,\n channelDid\n })\n });\n\n if (!res.ok) {\n addMessage('\u53D1\u9001\u5931\u8D25', 'ai');\n setSendMode('idle');\n }\n } catch (err) {\n addMessage('\u8FDE\u63A5\u9519\u8BEF', 'ai');\n console.error('Send error', err);\n setSendMode('idle');\n }\n}\n\n// \u4E3B\u52A8\u843D\u76D8: \u628A\u5F53\u524D channelId/sessionId \u6700\u540E\u4E00\u6761\u6D88\u606F PATCH \u5230 server\n// fire-and-forget, \u5931\u8D25\u53EA\u6253\u65E5\u5FD7, \u4E0D\u5F71\u54CD UI\nfunction persistLastMessageToServer(type, content) {\n if (!currentChannelId || !currentSessionId) return;\n fetch(`/sessions/${currentChannelId}/${currentSessionId}`, {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n message: { type, content, timestamp: new Date().toISOString() }\n })\n }).catch(err => {\n console.warn('[persist] \u843D\u76D8\u5931\u8D25:', err);\n });\n}\n\n// 2026-06-15: sendBtn click \u5206\u53D1 \u2014 \u770B data-state \u51B3\u5B9A send \u8FD8\u662F abort\nsendBtn.addEventListener('click', () => {\n if (sendBtn.dataset.state === 'abort' || sendBtn.dataset.state === 'aborting') {\n abortCurrentRun();\n } else {\n sendMessage();\n }\n});\ninput.addEventListener('keydown', (e) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n if (sendBtn.dataset.state === 'abort' || sendBtn.dataset.state === 'aborting') {\n // \u6D41\u5F0F\u671F\u95F4\u6309 Enter \u4E5F\u8D70\u7EC8\u6B62 (\u8DDF\u6309\u94AE\u4E00\u81F4)\n abortCurrentRun();\n } else {\n sendMessage();\n }\n } else if (e.key === 'Escape' && (sendBtn.dataset.state === 'abort' || sendBtn.dataset.state === 'aborting')) {\n // 2026-06-15: Esc \u7EC8\u6B62 \u2014 Claude Code \u98CE\u683C\n e.preventDefault();\n abortCurrentRun();\n }\n});\n\n// ============ v3 \u65B0\u589E: @-mention \u5355\u9009\u81EA\u52A8\u8865\u5168 (\u4E3B\u804A\u5929\u6846 #input) ============\nlet mentionChannels = []; // { id, name, source: 'local'|'remote', ownerPublicKey? }\nlet mentionDropdownEl = null;\nlet mentionHighlightIdx = -1;\nlet mentionQuery = null;\nlet mentionAnchor = -1; // @ \u5B57\u7B26\u7684\u7EDD\u5BF9\u4F4D\u7F6E (\u56FA\u5B9A, \u76F4\u5230 dropdown \u5173\u95ED)\nlet mentionBlockEnd = -1; // \u63D2\u5165\u533A\u5757\u7684\u7EC8\u70B9 (\u5355\u9009\u6A21\u5F0F\u4E0B = anchor + 1 + query)\nlet mentionDocMousedownBound = false; // \u9632\u6B62\u91CD\u590D\u6CE8\u518C document mousedown\n\nfunction ensureMentionDocMousedown() {\n if (mentionDocMousedownBound) return;\n mentionDocMousedownBound = true;\n document.addEventListener('mousedown', (e) => {\n if (mentionDropdownEl && !mentionDropdownEl.contains(e.target) && e.target !== input) {\n closeMentionDropdown();\n }\n });\n}\n\nasync function refreshMentionChannels() {\n try {\n const res = await fetch('/channels');\n const local = res.ok ? await res.json() : [];\n const r2 = await fetch('/api/remote-channels');\n const remoteData = r2.ok ? await r2.json() : { peers: [] };\n const remote = [];\n for (const p of (remoteData.peers || [])) {\n for (const c of (p.channels || [])) {\n remote.push({ id: c.id, name: safeChannelName(c.name, '(\u8FDC\u7AEF\u672A\u547D\u540D)'), source: 'remote', ownerPublicKey: p.peerId });\n }\n }\n mentionChannels = [\n ...(Array.isArray(local) ? local.map(c => ({ id: c.id, name: c.name, source: 'local' })) : []),\n ...remote\n ];\n } catch (err) {\n console.warn('[mention] \u52A0\u8F7D\u6E20\u9053\u5217\u8868\u5931\u8D25:', err);\n }\n}\n\nfunction closeMentionDropdown() {\n if (mentionDropdownEl) { mentionDropdownEl.remove(); mentionDropdownEl = null; }\n mentionHighlightIdx = -1;\n mentionQuery = null;\n mentionAnchor = -1;\n mentionBlockEnd = -1;\n // \u4E0D\u91CD\u7F6E mentionDocMousedownBound \u2014 \u76D1\u542C\u5668\u662F\u7A7A\u64CD\u4F5C (mentionDropdownEl === null) \u7559\u7740\u65E0\u59A8, \u907F\u514D\u91CD\u590D\u7ED1\n}\n\nfunction getCurrentMentionQuery() {\n const pos = input.selectionStart || input.value.length;\n const before = input.value.slice(0, pos);\n const m = before.match(/@([\u4E00-\u9FA5A-Za-z0-9_\\-]{0,30})$/);\n return m ? { query: m[1], anchor: pos - m[0].length } : null;\n}\n\nfunction renderMentionDropdown(items) {\n if (!mentionDropdownEl) {\n mentionDropdownEl = document.createElement('div');\n mentionDropdownEl.id = 'mention-dropdown';\n mentionDropdownEl.style.cssText = 'position:fixed;background:#fff;border:1px solid #d1d5db;border-radius:6px;box-shadow:0 4px 16px rgba(0,0,0,0.15);max-height:240px;overflow-y:auto;z-index:10000;font-size:13px;min-width:240px;';\n document.body.appendChild(mentionDropdownEl);\n ensureMentionDocMousedown();\n }\n // v3 \u7B80\u5316: \u5355\u9009 + \u7ACB\u5373\u586B\u5165\u8F93\u5165\u6846\n const headerHtml = `<div style=\"padding:6px 10px;background:#f9fafb;border-bottom:1px solid #e5e7eb;font-size:11px;color:#6b7280;display:flex;justify-content:space-between;align-items:center;\">\n <span>\uD83D\uDCA1 \u70B9\u51FB\u6216\u56DE\u8F66\u9009\u4E2D \u2192 \u81EA\u52A8\u586B\u5165\u8F93\u5165\u6846</span>\n <span style=\"color:#9ca3af;\">\u2191\u2193 \u79FB\u52A8</span>\n </div>`;\n\n if (items.length === 0) {\n mentionDropdownEl.innerHTML = headerHtml + '<div style=\"padding:10px 12px;color:#6b7280;font-size:12px;\">\u6CA1\u6709\u5339\u914D\u7684\u6E20\u9053</div>';\n } else {\n const rows = items.map((c, i) => {\n const isLocal = c.source === 'local';\n const tag = isLocal ? '\uD83C\uDFE0 \u672C\u5730' : '\uD83C\uDF10 \u8FDC\u7AEF';\n const owner = !isLocal && c.ownerPublicKey ? ` <span style=\"color:#9ca3af;font-size:11px;\">(${c.ownerPublicKey.substring(0, 8)}\u2026)</span>` : '';\n // \u6D45\u84DD = \u952E\u76D8\u9AD8\u4EAE, \u767D = \u666E\u901A\n const bg = i === mentionHighlightIdx ? '#eff6ff' : '#fff';\n const borderLeft = i === mentionHighlightIdx ? '3px solid #93c5fd' : '3px solid transparent';\n return `<div class=\"mention-item\" data-idx=\"${i}\" data-channel-id=\"${escapeHtml(c.id)}\" data-channel-name=\"${escapeHtml(safeChannelName(c.name, ''))}\" style=\"padding:8px 12px;cursor:pointer;background:${bg};border-bottom:1px solid #f3f4f6;display:flex;align-items:center;gap:8px;border-left:${borderLeft};\">\n <span style=\"font-size:10px;color:${isLocal ? '#059669' : '#2563eb'};background:${isLocal ? '#d1fae5' : '#dbeafe'};padding:1px 6px;border-radius:3px;white-space:nowrap;\">${tag}</span>\n <span style=\"flex:1;\">${escapeHtml(safeChannelName(c.name))}</span>${owner}\n </div>`;\n }).join('');\n mentionDropdownEl.innerHTML = headerHtml + rows;\n mentionDropdownEl.querySelectorAll('.mention-item').forEach((el) => {\n const idx = parseInt(el.getAttribute('data-idx'));\n el.onclick = () => {\n // \u5355\u51FB \u2192 \u7ACB\u5373\u586B\u5165\u8F93\u5165\u6846 + \u5173\u95ED dropdown\n applyMention(items[idx]);\n };\n // v3 \u5173\u952E\u4FEE\u590D: mouseenter \u53EA\u66F4\u65B0\u9AD8\u4EAE, \u4E0D\u91CD\u5EFA dropdown \u2014 \u5426\u5219\u7528\u6237\u5B9E\u9645\u70B9\u51FB\u7684 element \u88AB\u9500\u6BC1,\n // click \u4E8B\u4EF6\u843D\u5230\u65B0 element, \u4F46\u5B9E\u9645\u89E6\u53D1\u7684\u662F\u65B0 element \u7684 onclick (\u7A7A), \u800C\u4E0D\u662F\u88AB\u9500\u6BC1\u524D\u90A3\u4E2A\n el.onmouseenter = () => {\n if (mentionHighlightIdx === idx) return;\n mentionHighlightIdx = idx;\n // \u53EA\u66F4\u65B0\u80CC\u666F\u8272 + \u5DE6\u8FB9\u6846, \u4E0D\u91CD\u5EFA innerHTML\n const itemEls = mentionDropdownEl.querySelectorAll('.mention-item');\n itemEls.forEach((ie, ii) => {\n const isHi = ii === idx;\n ie.style.background = isHi ? '#eff6ff' : '#fff';\n ie.style.borderLeft = isHi ? '3px solid #93c5fd' : '3px solid transparent';\n });\n };\n });\n }\n const rect = input.getBoundingClientRect();\n mentionDropdownEl.style.left = rect.left + 'px';\n mentionDropdownEl.style.top = 'auto';\n mentionDropdownEl.style.bottom = (window.innerHeight - rect.top + 4) + 'px';\n}\n\n/** v3 \u5355\u9009: \u628A @xxx \u66FF\u6362\u4E3A @\u6E20\u9053\u540D + \u7A7A\u683C, \u5173\u95ED dropdown, \u5149\u6807\u653E\u7A7A\u683C\u540E */\nfunction applyMention(channel) {\n const anchor = mentionAnchor;\n const blockEnd = mentionBlockEnd >= 0 ? mentionBlockEnd : (anchor + 1 + (mentionQuery || '').length);\n if (anchor < 0 || anchor > input.value.length || input.value[anchor] !== '@') {\n closeMentionDropdown();\n return;\n }\n const before = input.value.slice(0, anchor); // \u542B @\n const after = input.value.slice(blockEnd); // query \u4E4B\u540E (\u53EF\u80FD\u7528\u6237\u5DF2\u8F93\u5165\u6B63\u6587)\n const insert = `@${safeChannelName(channel.name)} `;\n input.value = before + insert + after;\n const newPos = before.length + insert.length;\n input.focus();\n input.setSelectionRange(newPos, newPos);\n closeMentionDropdown();\n}\n\nfunction updateMentionDropdown() {\n // 2026-06-10 \u4FEE: \u6570\u7EC4\u7A7A\u65F6\u4E3B\u52A8\u5237\u4E00\u6B21, \u4E0D\u518D\u9759\u9ED8 return\n // \u4E4B\u524D `if (!mentionChannels.length) return` \u5BFC\u81F4\u521D\u59CB\u5316 0-8s \u7A97\u53E3\u6309 @ \u770B\u4E0D\u5230\u4EFB\u4F55 item\n if (!mentionChannels.length) {\n refreshMentionChannels().then(() => {\n // \u62C9\u5B8C\u518D\u91CD\u8BD5\u4E00\u6B21 (\u5F02\u6B65, \u4E0D\u963B\u585E\u5F53\u524D\u952E\u51FB)\n if (mentionChannels.length) updateMentionDropdown();\n });\n return;\n }\n const m = getCurrentMentionQuery();\n if (!m) { closeMentionDropdown(); return; }\n // \u53EA\u5728 dropdown \u521A\u6253\u5F00\u65F6\u8BBE\u7F6E anchor (blockEnd \u8DDF\u7740 insert \u8D70)\n if (mentionAnchor === -1) {\n mentionAnchor = m.anchor;\n mentionBlockEnd = m.anchor + 1 + (m.query || '').length;\n // dropdown \u9996\u6B21\u6253\u5F00 \u2192 \u5F3A\u5236\u5237\u4E00\u6B21, \u4FDD\u8BC1 remote \u5217\u8868\u6700\u65B0\n refreshMentionChannels();\n }\nmentionQuery = m.query;\n const q = m.query.toLowerCase();\n const items = mentionChannels.filter(c => safeChannelName(c.name).toLowerCase().includes(q)).slice(0, 8);\n mentionHighlightIdx = items.length > 0 ? 0 : -1;\n renderMentionDropdown(items);\n}\n\ninput.addEventListener('input', () => {\n updateMentionDropdown();\n});\ninput.addEventListener('keydown', (e) => {\n if (!mentionDropdownEl) return;\n const items = mentionDropdownEl.querySelectorAll('.mention-item');\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n if (items.length === 0) return;\n mentionHighlightIdx = (mentionHighlightIdx + 1) % items.length;\n const q = (mentionQuery || '').toLowerCase();\n const filtered = mentionChannels.filter(c => safeChannelName(c.name).toLowerCase().includes(q)).slice(0, 8);\n renderMentionDropdown(filtered);\n } else if (e.key === 'ArrowUp') {\n e.preventDefault();\n if (items.length === 0) return;\n mentionHighlightIdx = (mentionHighlightIdx - 1 + items.length) % items.length;\n const q = (mentionQuery || '').toLowerCase();\n const filtered = mentionChannels.filter(c => safeChannelName(c.name).toLowerCase().includes(q)).slice(0, 8);\n renderMentionDropdown(filtered);\n } else if (e.key === 'Enter' || e.key === 'Tab') {\n // \u5355\u9009: Enter/Tab \u7ACB\u5373\u586B\u5165 + \u5173\u95ED dropdown\n if (items.length > 0) {\n e.preventDefault();\n e.stopPropagation();\n const q = (mentionQuery || '').toLowerCase();\n const filtered = mentionChannels.filter(c => safeChannelName(c.name).toLowerCase().includes(q)).slice(0, 8);\n const cur = filtered[mentionHighlightIdx];\n if (cur) applyMention(cur);\n }\n } else if (e.key === 'Escape') {\n e.preventDefault();\n closeMentionDropdown();\n }\n}, true); // capture phase, \u5148\u4E8E sendMessage \u90A3\u4E2A keydown\n\n// \u521D\u59CB\u5316\nrefreshMentionChannels();\n// \u5B9A\u65F6\u5237\u65B0 (channel \u5217\u8868\u53EF\u80FD\u53D8\u5316)\nsetInterval(refreshMentionChannels, 5000);\n// \u8FDC\u7AEF channel \u5217\u8868\u53D8\u5316\u65F6\u4E5F\u5237\u65B0 (loadRemoteChannels \u662F function declaration, \u4E0D\u80FD\u91CD\u65B0\u8D4B\u503C)\n// \u7528 setInterval \u515C\u5E95: \u6BCF 5s \u5237\u4E00\u6B21 (\u5DF2\u7ECF\u6709\u5B9A\u65F6\u5668, \u8FD9\u91CC\u4E0D\u91CD\u590D)\n// \u5B9E\u9645\u4E0A refreshMentionChannels() \u5DF2\u7ECF\u5728 setInterval \u91CC\u8DD1\u4E86\n\n// v3 \u65B0\u589E: \u901A\u7528\u7248 @-autocomplete (\u4EFB\u610F input \u5143\u7D20\u90FD\u80FD\u6302, \u6BD4\u5982 B \u7AEF\u7684 #rcm-input)\nfunction setupMentionAutocomplete(inputEl) {\n if (!inputEl || inputEl.__mentionBound) return;\n inputEl.__mentionBound = true;\n let localQuery = null;\n let localAnchor = -1; // @ \u5B57\u7B26\u7684\u7EDD\u5BF9\u4F4D\u7F6E (\u56FA\u5B9A, \u76F4\u5230 dropdown \u5173\u95ED)\n let localBlockEnd = -1; // \u63D2\u5165\u533A\u5757\u7684\u7EC8\u70B9\n let localHighlight = -1;\n\n function closeLocal() {\n if (inputEl.__mentionDD) { inputEl.__mentionDD.remove(); inputEl.__mentionDD = null; }\n localHighlight = -1; localQuery = null; localAnchor = -1; localBlockEnd = -1;\n }\n\n function detectQuery() {\n const pos = inputEl.selectionStart || inputEl.value.length;\n const before = inputEl.value.slice(0, pos);\n const m = before.match(/@([\u4E00-\u9FA5A-Za-z0-9_\\-]{0,30})$/);\n return m ? { query: m[1], anchor: pos - m[0].length } : null;\n }\n\n // v3 \u5355\u9009: \u70B9\u51FB / Enter \u7ACB\u5373\u586B\u5165\u8F93\u5165\u6846 + \u5173\u95ED dropdown\n function applyLocal(channel) {\n const anchor = localAnchor;\n const blockEnd = localBlockEnd >= 0 ? localBlockEnd : (anchor + 1 + (localQuery || '').length);\n if (anchor < 0 || anchor > inputEl.value.length || inputEl.value[anchor] !== '@') {\n closeLocal();\n return;\n }\n const before = inputEl.value.slice(0, anchor); // \u542B @\n const after = inputEl.value.slice(blockEnd);\n const insert = `@${safeChannelName(channel.name)} `;\n inputEl.value = before + insert + after;\n const newPos = before.length + insert.length;\n inputEl.focus();\n inputEl.setSelectionRange(newPos, newPos);\n closeLocal();\n }\n\n function renderLocal(items) {\n if (!inputEl.__mentionDD) {\n inputEl.__mentionDD = document.createElement('div');\n inputEl.__mentionDD.style.cssText = 'position:fixed;background:#fff;border:1px solid #d1d5db;border-radius:6px;box-shadow:0 4px 16px rgba(0,0,0,0.15);max-height:240px;overflow-y:auto;z-index:10001;font-size:13px;min-width:240px;';\n document.body.appendChild(inputEl.__mentionDD);\n }\n const headerHtml = `<div style=\"padding:6px 10px;background:#f9fafb;border-bottom:1px solid #e5e7eb;font-size:11px;color:#6b7280;display:flex;justify-content:space-between;align-items:center;\">\n <span>\uD83D\uDCA1 \u70B9\u51FB\u6216\u56DE\u8F66\u9009\u4E2D \u2192 \u81EA\u52A8\u586B\u5165\u8F93\u5165\u6846</span>\n <span style=\"color:#9ca3af;\">\u2191\u2193 \u79FB\u52A8</span>\n </div>`;\n if (items.length === 0) {\n inputEl.__mentionDD.innerHTML = headerHtml + '<div style=\"padding:10px 12px;color:#6b7280;font-size:12px;\">\u6CA1\u6709\u5339\u914D\u7684\u6E20\u9053</div>';\n } else {\n inputEl.__mentionDD.innerHTML = headerHtml + items.map((c, i) => {\n const isLocal = c.source === 'local';\n const tag = isLocal ? '\uD83C\uDFE0 \u672C\u5730' : '\uD83C\uDF10 \u8FDC\u7AEF';\n const owner = !isLocal && c.ownerPublicKey ? ` <span style=\"color:#9ca3af;font-size:11px;\">(${c.ownerPublicKey.substring(0, 8)}\u2026)</span>` : '';\n const bg = i === localHighlight ? '#eff6ff' : '#fff';\n const borderLeft = i === localHighlight ? '3px solid #93c5fd' : '3px solid transparent';\nreturn `<div class=\"mention-item\" data-idx=\"${i}\" data-channel-id=\"${escapeHtml(c.id)}\" data-channel-name=\"${escapeHtml(safeChannelName(c.name, ''))}\" style=\"padding:8px 12px;cursor:pointer;background:${bg};border-bottom:1px solid #f3f4f6;display:flex;align-items:center;gap:8px;border-left:${borderLeft};\">\n <span style=\"font-size:10px;color:${isLocal ? '#059669' : '#2563eb'};background:${isLocal ? '#d1fae5' : '#dbeafe'};padding:1px 6px;border-radius:3px;white-space:nowrap;\">${tag}</span>\n <span style=\"flex:1;\">${escapeHtml(safeChannelName(c.name))}</span>${owner}\n </div>`;\n }).join('');\n inputEl.__mentionDD.querySelectorAll('.mention-item').forEach((el) => {\n const idx = parseInt(el.getAttribute('data-idx'));\n el.onclick = () => applyLocal(items[idx]);\n // v3 \u5173\u952E\u4FEE\u590D: mouseenter \u53EA\u66F4\u65B0\u9AD8\u4EAE, \u4E0D\u91CD\u5EFA dropdown (\u540C\u4E3B input)\n el.onmouseenter = () => {\n if (localHighlight === idx) return;\n localHighlight = idx;\n const itemEls = inputEl.__mentionDD.querySelectorAll('.mention-item');\n itemEls.forEach((ie, ii) => {\n const isHi = ii === idx;\n ie.style.background = isHi ? '#eff6ff' : '#fff';\n ie.style.borderLeft = isHi ? '3px solid #93c5fd' : '3px solid transparent';\n });\n };\n });\n }\n const rect = inputEl.getBoundingClientRect();\n inputEl.__mentionDD.style.left = rect.left + 'px';\n inputEl.__mentionDD.style.top = 'auto';\n inputEl.__mentionDD.style.bottom = (window.innerHeight - rect.top + 4) + 'px';\n }\n\n function update() {\n // 2026-06-10 \u4FEE: \u4E0E\u4E3B\u8F93\u5165\u6846\u540C\u6B65 \u2014 \u6570\u7EC4\u7A7A\u65F6\u4E3B\u52A8\u5237\u65B0, \u9996\u6B21\u6253\u5F00 dropdown \u5F3A\u5236\u5237\u65B0\n if (!mentionChannels.length) {\n refreshMentionChannels().then(() => {\n if (mentionChannels.length) update();\n });\n return;\n }\n const m = detectQuery();\n if (!m) { closeLocal(); return; }\n if (localAnchor === -1) {\n localAnchor = m.anchor;\n localBlockEnd = m.anchor + 1 + (m.query || '').length;\n // dropdown \u9996\u6B21\u6253\u5F00 \u2192 \u5F3A\u5236\u5237\u4E00\u6B21\u4FDD\u8BC1 remote \u6700\u65B0\n refreshMentionChannels();\n }\n localQuery = m.query;\n const q = m.query.toLowerCase();\n const items = mentionChannels.filter(c => safeChannelName(c.name).toLowerCase().includes(q)).slice(0, 8);\n localHighlight = items.length > 0 ? 0 : -1;\n renderLocal(items);\n }\n\n inputEl.addEventListener('input', update);\n inputEl.addEventListener('keydown', (e) => {\n if (!inputEl.__mentionDD) return;\n const items = inputEl.__mentionDD.querySelectorAll('.mention-item');\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n if (items.length === 0) return;\n localHighlight = (localHighlight + 1) % items.length;\n const q = (localQuery || '').toLowerCase();\n renderLocal(mentionChannels.filter(c => safeChannelName(c.name).toLowerCase().includes(q)).slice(0, 8));\n } else if (e.key === 'ArrowUp') {\n e.preventDefault();\n if (items.length === 0) return;\n localHighlight = (localHighlight - 1 + items.length) % items.length;\n const q = (localQuery || '').toLowerCase();\n renderLocal(mentionChannels.filter(c => safeChannelName(c.name).toLowerCase().includes(q)).slice(0, 8));\n } else if (e.key === 'Enter' || e.key === 'Tab') {\n if (items.length > 0) {\n e.preventDefault();\n e.stopPropagation();\n const q = (localQuery || '').toLowerCase();\n const filtered = mentionChannels.filter(c => safeChannelName(c.name).toLowerCase().includes(q)).slice(0, 8);\n const cur = filtered[localHighlight];\n if (cur) applyLocal(cur);\n }\n } else if (e.key === 'Escape') {\n e.preventDefault();\n closeLocal();\n }\n }, true);\n}\n\n// \u62D6\u62FD\u843D\u70B9: \u628A\u5224\u65AD\u5E93\u91CC\u7684\u5224\u65AD\u62D6\u5230\u8F93\u5165\u6846, \u76F4\u63A5\u4F5C\u4E3A\u6307\u4EE4\u53D1\u7ED9 AI (\u8D70\"\u4EE3\u6211\u51B3\u5B9A\"\u8DEF\u5F84).\n// \u7528\u6237\u62D6\u8FDB\u6765\u540E\u8F93\u5165\u6846\u88AB\u9884\u586B, \u70B9\u53D1\u9001\u5C31\u628A\u8FD9\u6761\u5224\u65AD\u4F5C\u4E3A\u6307\u4EE4\u4EA4\u7ED9\u5F53\u524D agent.\nconst inputArea = document.querySelector('.input-area');\nif (input && inputArea) {\n const onDragOver = (e) => {\n if (e.dataTransfer && Array.from(e.dataTransfer.types || []).includes('application/x-bolloon-judgment')) {\n e.preventDefault();\n e.dataTransfer.dropEffect = 'copy';\n inputArea.classList.add('drop-target');\n }\n };\n const onDragLeave = (e) => {\n if (e.target === inputArea || !inputArea.contains(e.relatedTarget)) {\n inputArea.classList.remove('drop-target');\n }\n };\n const onDrop = (e) => {\n inputArea.classList.remove('drop-target');\n const raw = e.dataTransfer.getData('application/x-bolloon-judgment');\n if (!raw) return;\n e.preventDefault();\n try {\n const { id, decision } = JSON.parse(raw);\n // \u9884\u586B\u8F93\u5165\u6846: \u7528\u6237\u53EF\u6539, \u7136\u540E\u53D1\u51FA\u53BB AI \u5C31\u77E5\u9053\"\u6309\u8FD9\u6761\u5224\u65AD\u505A\"\n const prefix = input.value.trim() ? input.value.trim() + '\\n' : '';\n input.value = `${prefix}\u6309\u6211\u7684\u5224\u65AD #${id?.substring(0, 8) || ''} \u6267\u884C: ${decision}`;\n input.focus();\n // \u89C6\u89C9\u63D0\u793A\n input.style.transition = 'box-shadow 0.3s';\n input.style.boxShadow = '0 0 0 2px #2563eb';\n setTimeout(() => { input.style.boxShadow = ''; }, 800);\n } catch {}\n };\n inputArea.addEventListener('dragover', onDragOver);\n inputArea.addEventListener('dragleave', onDragLeave);\n inputArea.addEventListener('drop', onDrop);\n}\n\nif (themeToggle) {\n themeToggle.addEventListener('click', toggleTheme);\n}\n\nconst apiConfigBtn = document.getElementById('api-config-btn');\nif (apiConfigBtn) {\n apiConfigBtn.addEventListener('click', () => {\n window.location.href = '/api-config';\n });\n}\n\n// \u94B1\u5305\u7BA1\u7406\u6309\u94AE\nconst walletBtn = document.getElementById('wallet-btn');\nconst walletBadge = document.getElementById('wallet-badge');\nif (walletBtn) {\n walletBtn.addEventListener('click', openWalletModal);\n}\n/** \u5237\u65B0 header \u94B1\u5305\u5FBD\u7AE0: \u7EDF\u8BA1\u5DF2\u7ED1\u5B9A\u94B1\u5305\u7684\u667A\u80FD\u4F53\u6570 */\nfunction refreshWalletBadge() {\n if (!walletBadge) return;\n const count = channels.filter(c => c.walletAddress).length;\n if (count > 0) {\n walletBadge.textContent = String(count);\n walletBadge.style.display = '';\n } else {\n walletBadge.style.display = 'none';\n }\n}\n\nif (sidebarToggle) {\n sidebarToggle.addEventListener('click', toggleSidebar);\n}\n\nif (newChannelBtn) {\n newChannelBtn.addEventListener('click', () => {\n createChannel('\u667A\u80FD\u4F53');\n });\n}\n\nif (newChannelInput) {\n newChannelInput.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n createChannel(newChannelInput.value);\n }\n });\n}\n\nasync function checkApiConfig() {\n try {\n const res = await fetch('/api/llm-config');\n const config = await res.json();\n\n // \u68C0\u67E5\u662F\u5426\u6709\u4F9B\u5E94\u5546\u5DF2\u914D\u7F6E\n const hasConfigured = Object.values(config.providers).some(p => p.enabled && p.apiKey);\n\n if (!hasConfigured) {\n // \u663E\u793A API \u914D\u7F6E\u63D0\u793A\n const hint = document.createElement('div');\n hint.className = 'api-config-hint';\n hint.innerHTML = `\n <div class=\"hint-icon\">\u26A0\uFE0F</div>\n <div class=\"hint-text\">\n <strong>API \u672A\u914D\u7F6E</strong><br>\n \u8BF7\u5148\u914D\u7F6E AI \u6A21\u578B\u624D\u80FD\u5F00\u59CB\u5BF9\u8BDD\n </div>\n <button class=\"hint-btn\" id=\"api-config-hint-btn\">\u524D\u5F80\u914D\u7F6E</button>\n `;\n document.body.appendChild(hint);\n\n // \u7ED1\u5B9A\u4E8B\u4EF6\u5904\u7406\u5668\uFF08\u907F\u514D\u5185\u8054 onclick\uFF09\n const hintBtn = document.getElementById('api-config-hint-btn');\n if (hintBtn) {\n hintBtn.addEventListener('click', () => {\n window.location.href = '/api-config';\n });\n }\n }\n } catch (err) {\n console.error('Failed to check API config:', err);\n }\n}\n\nasync function init() {\n const themeData = await loadTheme();\n currentAgentId = themeData.agentId || `agent_${generateId().substring(0, 8)}`;\n\n if (!themeData.agentId) {\n await saveTheme(themeData.theme, currentAgentId);\n }\n\n await loadChannels();\n await checkApiConfig();\n\n if (channels.length > 0) {\n await selectChannel(channels[0].id);\n } else {\n await createChannel('\u9ED8\u8BA4\u4F1A\u8BDD');\n }\n}\n\n// P2P Network Modal - React Version\nconst p2pNetworkBtn = document.getElementById('p2p-network-btn');\n\n// \u6253\u5F00 P2P Modal (\u4F7F\u7528 React)\nif (p2pNetworkBtn) {\n p2pNetworkBtn.addEventListener('click', () => {\n if (typeof window.showP2PModal === 'function') {\n window.showP2PModal();\n }\n });\n}\n\n// ==================== Judgments (v1 \u6781\u7B80) ====================\nconst judgmentsModal = document.getElementById('judgments-modal');\nconst judgmentsBtn = document.getElementById('judgments-btn');\nconst judgmentsModalClose = document.getElementById('judgments-modal-close');\nconst judgmentDecision = document.getElementById('judgment-decision');\nconst judgmentReason = document.getElementById('judgment-reason');\nconst judgmentDomain = document.getElementById('judgment-domain');\nconst judgmentStakes = document.getElementById('judgment-stakes');\nconst judgmentSubmitBtn = document.getElementById('judgment-submit-btn');\nconst judgmentError = document.getElementById('judgment-error');\nconst judgmentsList = document.getElementById('judgments-list');\nconst judgmentsBadge = document.getElementById('judgments-badge');\n\nlet judgmentsLoaded = false;\n\nfunction showJudgmentsModal() {\n if (judgmentsModal) judgmentsModal.classList.add('active');\n if (!judgmentsLoaded) loadJudgments();\n else renderJudgments(lastJudgmentsCache); // \u6253\u5F00\u65F6\u6309\u5F53\u524D channel / tab \u91CD\u6E32\n}\n\nfunction switchJudgmentTab(tab) {\n currentJudgmentTab = tab;\n // 2026-06-16: \u6837\u5F0F\u5207 active \u6539\u7528 CSS class, \u4E0D\u518D\u5199 style.borderBottomColor\n document.querySelectorAll('.judgment-tab').forEach(btn => {\n btn.classList.toggle('active', btn.dataset.tab === tab);\n });\n renderJudgments(lastJudgmentsCache);\n}\n\nfunction switchStatusFilter(status) {\n currentStatusFilter = status;\n // 2026-06-16: \u6837\u5F0F\u5207 active \u6539\u7528 CSS class, \u4E0D\u518D\u5199 style.background\n document.querySelectorAll('.judgment-status-tab').forEach(btn => {\n btn.classList.toggle('active', btn.dataset.status === status);\n });\n loadJudgments();\n}\n\n/**\n * P0.5: \u6253\u5F00\u5224\u65AD\u529B modal \u5E76 filter \u5230\u6307\u5B9A ids\n * - \u8C03 openJudgmentsModal() + \u7B49 loadJudgments() \u5B8C\u6210\n * - \u7136\u540E\u7528 ids filter lastJudgmentsCache\n */\nfunction openJudgmentsModalWithFilter(ids) {\n if (!Array.isArray(ids) || ids.length === 0) return;\n if (typeof openJudgmentsModal === 'function') {\n openJudgmentsModal();\n } else if (judgmentsModal) {\n judgmentsModal.classList.add('active');\n }\n // \u7B49 loadJudgments \u5B8C\u6210 (\u5B83\u4F1A await fetch \u7136\u540E renderJudgments)\n setTimeout(() => {\n if (typeof lastJudgmentsCache === 'undefined') return;\n lastJudgmentsCache = (lastJudgmentsCache || []).filter((j) => ids.includes(j.id));\n if (typeof renderJudgments === 'function') {\n renderJudgments(lastJudgmentsCache);\n }\n }, 150);\n}\n\nfunction hideJudgmentsModal() {\n if (judgmentsModal) judgmentsModal.classList.remove('active');\n}\n\nlet currentJudgmentTab = 'channel'; // 'channel' | 'global'\nlet currentStatusFilter = 'all'; // 'all' | 'active' | 'superseded' | 'violations'\nlet lastJudgmentsCache = []; // \u6700\u8FD1\u4E00\u6B21 loadJudgments \u62FF\u5230\u7684\u539F\u59CB\u5217\u8868, \u5207 tab / \u5207 channel \u65F6\u590D\u7528\n\n/**\n * v3 \u91CD\u505A: \u6E32\u67D3\u5224\u65AD\u529B\u5217\u8868 (\u53D7 tab + \u5F53\u524D channel \u5F71\u54CD)\n * tab = 'channel': \u62C6\u4E3A\"\u5DF2\u7ED1\u5B9A\" + \"\u672A\u7ED1\u5B9A\"\u4E24\u7EC4, \u6BCF\u6761\u5E26 + / \u00D7 \u6309\u94AE\n * tab = 'global': \u5168\u90E8 judgment \u5217\u8868, \u65E0 + / \u00D7 \u6309\u94AE\n * \u5982\u679C\u6CA1\u9009 channel, 'channel' tab \u81EA\u52A8\u663E\u793A\u63D0\u793A + \u5168\u90E8 judgment\n */\nfunction renderJudgments(items) {\n if (!judgmentsList) return;\n const all = items || [];\n const titleEl = document.getElementById('judgments-list-title');\n const chNameEl = document.getElementById('judgments-tab-channel-name');\n const currentCh = currentChannelId\n ? channels.find(c => c.id === currentChannelId)\n : null;\n\n if (chNameEl) {\n chNameEl.textContent = currentCh ? `(${safeChannelName(currentCh.name)})` : '(\u672A\u9009)';\n }\n\n if (all.length === 0) {\n judgmentsList.innerHTML = '<div class=\"task-empty\">\u8FD8\u6CA1\u6709\u5224\u65AD, \u5728\u4E0A\u9762\u8BB0\u5F55\u7B2C\u4E00\u6761\u5427</div>';\n if (titleEl) titleEl.textContent = '\u672C channel \u7684\u5224\u65AD\u529B';\n return;\n }\n\n if (currentJudgmentTab === 'global') {\n // \u5168\u5C40 tab: \u5168\u90E8 judgment, \u7B80\u5355\u5217\u8868\n if (titleEl) titleEl.textContent = `\u5168\u5C40\u5224\u65AD\u529B (${all.length} \u6761)`;\n judgmentsList.innerHTML = renderJudgmentItems(all, { showBindToggle: false });\n return;\n }\n\n // channel tab: \u5FC5\u987B\u6709 channel\n if (!currentCh) {\n if (titleEl) titleEl.textContent = '\u672C channel \u7684\u5224\u65AD\u529B';\n judgmentsList.innerHTML = `\n <div style=\"padding:24px 12px;text-align:center;color:#6b7280;font-size:13px;\">\n \u8BF7\u5148\u5728\u5DE6\u4FA7\u9009\u4E2D\u4E00\u4E2A channel,<br>\u7136\u540E\u8FD9\u91CC\u4F1A\u663E\u793A\u5DF2\u7ED1\u5B9A\u548C\u53EF\u52A0\u5165\u7684\u5224\u65AD\u529B\u3002\n </div>\n `;\n return;\n }\n\n const boundIds = new Set(\n Array.isArray(currentCh.bound_judgment_ids) ? currentCh.bound_judgment_ids : []\n );\n const bound = all.filter(j => boundIds.has(j.id));\n const unbound = all.filter(j => !boundIds.has(j.id));\n\n if (titleEl) titleEl.textContent = `${safeChannelName(currentCh.name)} \u7684\u5224\u65AD\u529B (\u5DF2\u7ED1 ${bound.length} / \u5171 ${all.length})`;\n\n let html = '';\n if (bound.length > 0) {\n html += `<div style=\"font-size:11px;color:#6b7280;text-transform:uppercase;letter-spacing:0.5px;padding:8px 4px 4px;\">\u5DF2\u7ED1\u5B9A (${bound.length})</div>`;\n html += renderJudgmentItems(bound, { showBindToggle: true, isBound: true });\n }\n if (unbound.length > 0) {\n html += `<div style=\"font-size:11px;color:#6b7280;text-transform:uppercase;letter-spacing:0.5px;padding:14px 4px 4px;\">\u672A\u7ED1\u5B9A (${unbound.length})</div>`;\n html += renderJudgmentItems(unbound, { showBindToggle: true, isBound: false });\n }\n judgmentsList.innerHTML = html;\n}\n\nfunction renderJudgmentItems(items, opts) {\n const { showBindToggle, isBound } = opts || {};\n return items.map(j => {\n const reason = (j.reasons && j.reasons[0]) ? escapeHtml(j.reasons[0]) : '';\n const domain = (j.context && j.context.domain) ? escapeHtml(j.context.domain) : 'general';\n const stakes = (j.context && j.context.stakes) ? escapeHtml(j.context.stakes) : 'medium';\n const isSuperseded = j.status === 'superseded';\n const isRejected = j.status === 'rejected';\n const dimmedStyle = isSuperseded || isRejected\n ? 'opacity:0.55;background:#f3f4f6;'\n : '';\n const statusTag = isSuperseded\n ? `<span style=\"display:inline-block;background:#fef3c7;color:#92400e;font-size:10px;padding:1px 6px;border-radius:3px;margin-left:6px;\" title=\"\u5DF2\u88AB\u65B0\u5224\u65AD\u529B\u6F14\u5316\u66FF\u4EE3\">\u5DF2\u8FC7\u65F6</span>`\n : isRejected\n ? `<span style=\"display:inline-block;background:#fee2e2;color:#991b1b;font-size:10px;padding:1px 6px;border-radius:3px;margin-left:6px;\">\u5DF2\u62D2\u7EDD</span>`\n : '';\n const evolveNote = isSuperseded && j.supersededBy\n ? `<div style=\"font-size:10px;color:#6b7280;margin-top:2px;\">\u88AB\u65B0\u6761\u66FF\u4EE3 \u00B7 ${escapeHtml(j.evolutionReason || 'merged')} \u00B7 ${escapeHtml(j.evolvedAt || '').substring(0,10)}</div>`\n : '';\n const bindBtn = showBindToggle\n ? isBound\n ? `<button class=\"judgment-toggle-btn\" data-id=\"${escapeHtml(j.id)}\" data-action=\"unbind\" title=\"\u4ECE\u5F53\u524D channel \u79FB\u9664\" style=\"background:none;border:1px solid #fca5a5;color:#b91c1c;padding:1px 8px;border-radius:3px;cursor:pointer;font-size:11px;\">\u00D7 \u79FB\u9664</button>`\n : `<button class=\"judgment-toggle-btn\" data-id=\"${escapeHtml(j.id)}\" data-action=\"bind\" title=\"\u52A0\u8FDB\u5F53\u524D channel\" style=\"background:none;border:1px solid #6b7280;color:#6b7280;padding:1px 8px;border-radius:3px;cursor:pointer;font-size:11px;\">+ \u52A0\u5165</button>`\n : '';\n return `\n <div class=\"task-item completed judgment-row\"\n data-judgment-id=\"${escapeHtml(j.id)}\"\n draggable=\"true\"\n style=\"cursor:grab;${dimmedStyle}\">\n <div class=\"task-item-header\">\n <label class=\"judgment-checkbox\" style=\"display:flex;align-items:center;cursor:pointer;margin-right:8px;\" onclick=\"event.stopPropagation();\">\n <input type=\"checkbox\" class=\"judgment-select-cb\" data-id=\"${escapeHtml(j.id)}\" style=\"cursor:pointer;\" onclick=\"event.stopPropagation();\">\n </label>\n <div class=\"task-item-title\">\n <span class=\"judgment-decision\">${escapeHtml(j.decision)}</span>${statusTag}\n </div>\n <span class=\"task-item-status completed\">${stakes}</span>\n </div>\n ${reason ? `<div class=\"task-item-desc\" style=\"color:#555;font-size:13px;margin-top:4px;\">\u7406\u7531: ${reason}</div>` : ''}\n ${evolveNote}\n <div class=\"task-item-meta\" style=\"color:#999;font-size:11px;margin-top:4px;display:flex;justify-content:space-between;align-items:center;\">\n <span>${domain} \u00B7 ${escapeHtml(j.timestamp)} \u00B7 ${escapeHtml(j.id)}</span>\n <span style=\"display:flex;gap:4px;\">\n ${bindBtn}\n <button class=\"judgment-edit-btn\" data-id=\"${escapeHtml(j.id)}\" title=\"\u7F16\u8F91\u5224\u65AD\" style=\"background:none;border:1px solid #d1d5db;color:#374151;padding:1px 8px;border-radius:3px;cursor:pointer;font-size:11px;\">\u7F16\u8F91</button>\n <button class=\"judgment-del-btn\" data-id=\"${escapeHtml(j.id)}\" title=\"\u5220\u9664\u5224\u65AD\" style=\"background:none;border:1px solid #fca5a5;color:#b91c1c;padding:1px 8px;border-radius:3px;cursor:pointer;font-size:11px;\">\u5220\u9664</button>\n </span>\n </div>\n </div>\n `;\n }).join('');\n}\n\nasync function loadJudgments() {\n if (!judgmentsList) return;\n try {\n // P3: \u8FDD\u89C4\u8BB0\u5F55\u8D70\u5355\u72EC API\n if (currentStatusFilter === 'violations') {\n const res = await fetch('/api/judgments/violations?limit=50');\n if (!res.ok) throw new Error('HTTP ' + res.status);\n const data = await res.json();\n renderViolations(data.items || []);\n judgmentsLoaded = true;\n return;\n }\n\n // \u7C7B B: \u81EA\u9002\u5E94\u626B\u63CF\u5EFA\u8BAE\n if (currentStatusFilter === 'adaptive') {\n const res = await fetch('/api/judgments/adaptive-suggestions');\n if (!res.ok) throw new Error('HTTP ' + res.status);\n const data = await res.json();\n renderAdaptiveSuggestions(data);\n judgmentsLoaded = true;\n return;\n }\n\n // \u9636\u6BB5 2: causal-judge \u56E0\u679C\u5206\u6790\n if (currentStatusFilter === 'causal') {\n const res = await fetch('/api/judgments/causal/correlation?topN=10');\n if (!res.ok) throw new Error('HTTP ' + res.status);\n const data = await res.json();\n renderCausalAnalysis(data.items || []);\n judgmentsLoaded = true;\n return;\n }\n\n const res = await fetch('/api/judgments?status=' + encodeURIComponent(currentStatusFilter));\n if (!res.ok) throw new Error('HTTP ' + res.status);\n const data = await res.json();\n lastJudgmentsCache = data.judgments || [];\n renderJudgments(lastJudgmentsCache);\n if (judgmentsBadge) {\n // \u5FBD\u7AE0\u6C38\u8FDC\u663E\u793A active \u6570\u91CF; \u4E0D\u8DDF filter \u53D8\n // \u5F53 filter=active \u65F6, \u4E3B fetch \u5DF2\u7ECF\u8FD4\u56DE\u7684\u5C31\u662F active \u5217\u8868, \u76F4\u63A5\u7528 data.count\n // \u5F53 filter=all/superseded \u65F6, \u4ECE\u4E3B\u5217\u8868\u672C\u5730\u6570 active \u6761 (status ?? 'active' \u517C\u5BB9\u8001\u6570\u636E)\n let activeCount;\n if (currentStatusFilter === 'active') {\n activeCount = data.count;\n } else {\n activeCount = lastJudgmentsCache.filter((j) => (j.status ?? 'active') === 'active').length;\n }\n if (activeCount > 0) {\n judgmentsBadge.textContent = activeCount;\n judgmentsBadge.style.display = '';\n } else {\n judgmentsBadge.style.display = 'none';\n }\n }\n judgmentsLoaded = true;\n } catch (e) {\n if (judgmentsList) judgmentsList.innerHTML = '<div class=\"task-empty\">\u52A0\u8F7D\u5931\u8D25: ' + escapeHtml(e.message) + '</div>';\n }\n}\n\n/**\n * P3 \u6E32\u67D3\u8FDD\u89C4\u8BB0\u5F55 (\u4E0E renderJudgments \u540C\u4F4D\u7F6E, \u4F46\u5185\u5BB9\u4E0D\u540C)\n */\nfunction renderViolations(items) {\n if (!judgmentsList) return;\n if (!items || items.length === 0) {\n judgmentsList.innerHTML = '<div class=\"task-empty\">\u6682\u65E0\u8FDD\u89C4\u8BB0\u5F55 (AI \u56DE\u590D\u672A\u8FDD\u53CD\u6CE8\u5165\u539F\u5219).</div>';\n return;\n }\n judgmentsList.innerHTML = items.map((v) => {\n const ts = escapeHtml((v.ts || '').substring(0, 19).replace('T', ' '));\n const userPrev = escapeHtml(v.userInputPreview || '');\n const aiPrev = escapeHtml(v.aiReplyPreview || '');\n const principles = (v.result?.violatedPrinciples || []).map((p) =>\n `<div style=\"margin-top:3px;padding:4px 8px;background:#fef2f2;border-radius:3px;\">\n <span style=\"color:#dc2626;\">\u26A0</span> <strong>${escapeHtml(p.principle || '')}</strong>\n <span style=\"color:#991b1b;\">\u2014 ${escapeHtml(p.reason || '')}</span>\n </div>`\n ).join('');\n return `\n <div class=\"task-item\" style=\"border-left:3px solid #dc2626;padding:8px 12px;background:#fffbfb;\">\n <div style=\"font-size:11px;color:#6b7280;margin-bottom:4px;\">${ts} \u00B7 confidence=${escapeHtml(String(v.result?.confidence ?? 0))}</div>\n <div style=\"font-size:12px;color:#1f2937;\"><strong>\u7528\u6237:</strong> ${userPrev}</div>\n <div style=\"font-size:12px;color:#1f2937;margin-top:2px;\"><strong>AI:</strong> ${aiPrev}</div>\n <div style=\"margin-top:6px;\">${principles}</div>\n </div>\n `;\n }).join('');\n}\n\n/**\n * \u7C7B B \u81EA\u9002\u5E94\u5EFA\u8BAE\u6E32\u67D3\n * - rising (\u7EFF\u8272 boost \u6807\u8BB0): 7 \u5929\u4F7F\u7528\u7387\u9AD8\u4E8E 30 \u5929\u5747\u503C\u7684 1.5 \u500D\n * - stale (\u9EC4\u8272 deprecate \u6807\u8BB0): 90 \u5929\u672A\u7528 + \u603B\u4F7F\u7528 < 3\n * - unused (\u7070\u8272 review \u6807\u8BB0): 30 \u5929\u672A\u7528 + \u603B\u4F7F\u7528 < 5\n * \u6BCF\u6761\u5E26 \"\u2713 \u63A5\u53D7\" / \"\u2717 \u62D2\u7EDD\" \u6309\u94AE, \u63A5\u53D7\u4F1A\u771F\u6539\u5E93, \u62D2\u7EDD\u53EA\u7559\u75D5\n */\nfunction renderAdaptiveSuggestions(data) {\n if (!judgmentsList) return;\n const { judgmentsTotal, usageEntriesScanned, suggestions, scannedAt } = data;\n const ts = escapeHtml((scannedAt || '').substring(0, 19).replace('T', ' '));\n\n if (!suggestions || suggestions.length === 0) {\n judgmentsList.innerHTML = `\n <div class=\"task-empty\">\uD83D\uDCCA \u81EA\u9002\u5E94\u626B\u63CF: \u65E0\u5EFA\u8BAE\n <div style=\"margin-top:8px;font-size:11px;color:#6b7280;\">\u626B\u4E86 ${judgmentsTotal} \u6761\u539F\u5219, ${usageEntriesScanned} \u6761\u4F7F\u7528\u8BB0\u5F55, \u90FD\u633A\u5065\u5EB7.</div>\n <div style=\"margin-top:4px;font-size:11px;color:#6b7280;\">\u626B\u63CF\u4E8E ${ts}</div>\n </div>`;\n return;\n }\n\n const KIND_STYLE = {\n rising: { color: '#059669', bg: '#ecfdf5', label: '\u2191 rising', action: 'boost' },\n stale: { color: '#92400e', bg: '#fef3c7', label: '\u23F0 stale', action: 'deprecate' },\n unused: { color: '#6b7280', bg: '#f3f4f6', label: '\uD83D\uDC40 unused', action: 'review' },\n };\n\n const header = `\n <div style=\"padding:8px 12px;background:#f9fafb;border-radius:4px;margin-bottom:8px;font-size:11px;color:#374151;\">\n \uD83D\uDCCA \u626B\u63CF\u4E8E ${ts} \u00B7 ${judgmentsTotal} \u6761\u539F\u5219 \u00B7 ${usageEntriesScanned} \u6761\u4F7F\u7528\u8BB0\u5F55 \u00B7 <strong>${suggestions.length}</strong> \u6761\u5EFA\u8BAE\n <button class=\"rescan-btn\" style=\"margin-left:8px;background:none;border:1px solid #6b7280;color:#374151;padding:1px 8px;border-radius:3px;cursor:pointer;font-size:11px;\">\uD83D\uDD04 \u91CD\u65B0\u626B\u63CF</button>\n </div>\n `;\n\n const rows = suggestions.map((s) => {\n const style = KIND_STYLE[s.kind] || KIND_STYLE.unused;\n const m = s.metrics || {};\n return `\n <div class=\"task-item\" data-suggestion-key=\"${escapeHtml(s.key)}\"\n style=\"border-left:3px solid ${style.color};padding:8px 12px;background:${style.bg};margin-bottom:6px;\">\n <div style=\"display:flex;align-items:center;gap:6px;margin-bottom:4px;\">\n <span style=\"color:${style.color};font-weight:600;font-size:12px;\">${style.label}</span>\n <span style=\"font-size:11px;color:#6b7280;\">${s.action === 'boost' ? '\u5EFA\u8BAE\u52A0\u6743' : s.action === 'deprecate' ? '\u5EFA\u8BAE\u5E9F\u5F03' : '\u5EFA\u8BAE\u5BA1\u89C6'}</span>\n </div>\n <div style=\"font-size:12px;color:#1f2937;margin-bottom:4px;\"><strong>${escapeHtml(s.decision)}</strong></div>\n <div style=\"font-size:11px;color:#6b7280;margin-bottom:6px;\">${escapeHtml(s.reason)}</div>\n <div style=\"font-size:11px;color:#9ca3af;margin-bottom:6px;\">\n 7\u5929 ${m.usage7d || 0} \u00B7 30\u5929 ${m.usage30d || 0} \u00B7 \u5171 ${m.totalUsage || 0} \u00B7 \u4E0A\u6B21\u7528 ${m.daysSinceLastUse || 0} \u5929\u524D\n </div>\n <div style=\"display:flex;gap:6px;\">\n <button class=\"adaptive-accept\" data-key=\"${escapeHtml(s.key)}\" data-id=\"${escapeHtml(s.judgmentId)}\" data-action-kind=\"${escapeHtml(s.action)}\"\n style=\"background:#059669;color:#fff;border:none;padding:2px 10px;border-radius:3px;cursor:pointer;font-size:11px;\">\u2713 \u63A5\u53D7</button>\n <button class=\"adaptive-reject\" data-key=\"${escapeHtml(s.key)}\" data-id=\"${escapeHtml(s.judgmentId)}\" data-action-kind=\"${escapeHtml(s.action)}\"\n style=\"background:none;border:1px solid #d1d5db;color:#6b7280;padding:2px 10px;border-radius:3px;cursor:pointer;font-size:11px;\">\u2717 \u62D2\u7EDD</button>\n </div>\n </div>\n `;\n }).join('');\n\n judgmentsList.innerHTML = header + rows;\n\n // \u7ED1\u5B9A\u6309\u94AE\n const rescanBtn = judgmentsList.querySelector('.rescan-btn');\n if (rescanBtn) {\n rescanBtn.onclick = async () => {\n rescanBtn.disabled = true;\n rescanBtn.textContent = '\uD83D\uDD04 \u626B\u63CF\u4E2D...';\n try {\n const r = await fetch('/api/judgments/adaptive-suggestions?force=1');\n if (r.ok) renderAdaptiveSuggestions(await r.json());\n } catch (err) {\n console.error('[adaptive] rescan failed:', err);\n } finally {\n rescanBtn.disabled = false;\n rescanBtn.textContent = '\uD83D\uDD04 \u91CD\u65B0\u626B\u63CF';\n }\n };\n }\n judgmentsList.querySelectorAll('.adaptive-accept').forEach((btn) => {\n btn.onclick = () => applyAdaptiveSuggestion(btn.dataset.key, btn.dataset.id, btn.dataset.actionKind, 'accept');\n });\n judgmentsList.querySelectorAll('.adaptive-reject').forEach((btn) => {\n btn.onclick = () => applyAdaptiveSuggestion(btn.dataset.key, btn.dataset.id, btn.dataset.actionKind, 'reject');\n });\n}\n\nasync function applyAdaptiveSuggestion(key, judgmentId, actionKind, decision) {\n const row = judgmentsList?.querySelector(`[data-suggestion-key=\"${key}\"]`);\n if (row) row.style.opacity = '0.5';\n try {\n const res = await fetch('/api/judgments/adaptive-apply', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n action: decision,\n suggestion: {\n key,\n judgmentId,\n kind: actionKind,\n action: actionKind,\n decision: '',\n reason: '',\n metrics: {},\n scannedAt: new Date().toISOString(),\n },\n }),\n });\n if (!res.ok) throw new Error('HTTP ' + res.status);\n // \u89C6\u89C9\u53CD\u9988: \u9690\u85CF\u8BE5\u884C\n if (row) row.style.display = 'none';\n } catch (err) {\n if (row) row.style.opacity = '';\n console.error('[adaptive] apply failed:', err);\n alert('\u64CD\u4F5C\u5931\u8D25: ' + (err && err.message || 'unknown'));\n }\n}\n\n// ============================================================\n// \u9636\u6BB5 2: causal-judge \u6E32\u67D3\n// ============================================================\n\n/**\n * \u6E32\u67D3\u5173\u8054\u5206\u6790 (top 5 \u4E92\u4FE1\u606F\u5BF9)\n * - \u663E\u793A\u6BCF\u5BF9: judgmentA \u2194 judgmentB + \u4E92\u4FE1\u606F + co-occurrence + \u56E0\u679C\u65B9\u5411\n * - \u6BCF\u6761 judgment \u65C1\u52A0\"\uD83D\uDD2C \u8DD1 do-calculus\"\u6309\u94AE, \u5F02\u6B65\u663E\u793A causalEffect\n */\nfunction renderCausalAnalysis(items) {\n if (!judgmentsList) return;\n if (!items || items.length === 0) {\n judgmentsList.innerHTML = `\n <div class=\"task-empty\">\uD83D\uDD0D \u56E0\u679C\u5206\u6790: \u65E0\u9AD8\u5173\u8054\u5BF9\n <div style=\"margin-top:8px;font-size:11px;color:#6b7280;\">usage \u6570\u636E\u4E0D\u8DB3 (\u81F3\u5C11 3 \u6761\u540C\u73B0), \u6216 LLM \u4E0D\u53EF\u7528. \u591A\u7528 bolloon \u4E00\u6BB5\u65F6\u95F4\u540E\u91CD\u8BD5.</div>\n </div>`;\n return;\n }\n\n const rows = items.map((p, idx) => `\n <div class=\"task-item\" data-causal-idx=\"${idx}\" data-judgment-a=\"${escapeHtml(p.judgmentA)}\" data-judgment-b=\"${escapeHtml(p.judgmentB)}\"\n style=\"border-left:3px solid #7c3aed;padding:8px 12px;background:#faf5ff;margin-bottom:6px;\">\n <div style=\"display:flex;align-items:center;gap:6px;margin-bottom:4px;\">\n <span style=\"color:#7c3aed;font-weight:600;font-size:12px;\">${escapeHtml(p.causalDirection)}</span>\n <span style=\"font-size:11px;color:#6b7280;\">MI=${p.mutualInfo} \u00B7 co=${p.coOccurrence}</span>\n </div>\n <div style=\"font-size:11px;color:#374151;margin-bottom:4px;\">${escapeHtml(p.explanation || '(\u65E0 LLM \u89E3\u91CA)')}</div>\n <div style=\"font-size:10px;color:#9ca3af;\">A: ${escapeHtml(p.judgmentA)} \u2194 B: ${escapeHtml(p.judgmentB)}</div>\n <div style=\"margin-top:6px;display:flex;gap:6px;\">\n <button class=\"causal-intervention-a\" data-jid=\"${escapeHtml(p.judgmentA)}\"\n style=\"background:#7c3aed;color:#fff;border:none;padding:2px 10px;border-radius:3px;cursor:pointer;font-size:11px;\">\uD83D\uDD2C do(A)</button>\n <button class=\"causal-intervention-b\" data-jid=\"${escapeHtml(p.judgmentB)}\"\n style=\"background:#7c3aed;color:#fff;border:none;padding:2px 10px;border-radius:3px;cursor:pointer;font-size:11px;\">\uD83D\uDD2C do(B)</button>\n </div>\n <div class=\"causal-result\" data-jid=\"\" style=\"display:none;margin-top:6px;padding:6px;background:#f3e8ff;border-radius:3px;font-size:11px;\"></div>\n </div>\n `).join('');\n\n judgmentsList.innerHTML = `\n <div style=\"padding:8px 12px;background:#f9fafb;border-radius:4px;margin-bottom:8px;font-size:11px;color:#374151;\">\n \uD83D\uDD0D \u5173\u8054\u5206\u6790 (top ${items.length} \u4E92\u4FE1\u606F\u5BF9) \u00B7 <span style=\"color:#7c3aed;\">LLM \u63A8\u65AD\u65B9\u5411</span>\n <button class=\"causal-refresh\" style=\"margin-left:8px;background:none;border:1px solid #7c3aed;color:#7c3aed;padding:1px 8px;border-radius:3px;cursor:pointer;font-size:11px;\">\uD83D\uDD04 \u91CD\u65B0\u8DD1</button>\n </div>\n ${rows}\n `;\n\n // \u6309\u94AE: \u91CD\u65B0\u8DD1\n const refresh = judgmentsList.querySelector('.causal-refresh');\n if (refresh) {\n refresh.onclick = async () => {\n refresh.disabled = true;\n refresh.textContent = '\uD83D\uDD04 \u8DD1\u4E2D...';\n try {\n const r = await fetch('/api/judgments/causal/correlation?topN=10');\n if (r.ok) renderCausalAnalysis((await r.json()).items || []);\n } finally {\n refresh.disabled = false;\n refresh.textContent = '\uD83D\uDD04 \u91CD\u65B0\u8DD1';\n }\n };\n }\n\n // \u6309\u94AE: \u8DD1 do-calculus\n judgmentsList.querySelectorAll('.causal-intervention-a, .causal-intervention-b').forEach((btn) => {\n btn.onclick = async () => {\n const jid = btn.getAttribute('data-jid');\n const resultDiv = btn.closest('.task-item')?.querySelector('.causal-result');\n if (!resultDiv) return;\n resultDiv.style.display = 'block';\n resultDiv.textContent = '\uD83D\uDD2C \u8DD1 do-calculus (LLM \u6A21\u62DF\u53CD\u4E8B\u5B9E)...';\n btn.disabled = true;\n try {\n const r = await fetch(`/api/judgments/causal/intervention?judgmentId=${encodeURIComponent(jid)}`);\n if (!r.ok) throw new Error('HTTP ' + r.status);\n const data = await r.json();\n const effect = data.causalEffect;\n const sign = effect > 0 ? '+' : '';\n const color = Math.abs(effect) > 0.5 ? '#dc2626' : Math.abs(effect) > 0.2 ? '#d97706' : '#059669';\n resultDiv.innerHTML = `\n <div style=\"color:${color};font-weight:600;\">do-calculus: causalEffect = ${sign}${effect} (${data.marginalContribution})</div>\n <div style=\"color:#374151;margin-top:4px;\">${escapeHtml(data.reasoning)}</div>\n <div style=\"color:#9ca3af;margin-top:4px;\">confidence=${data.confidence}</div>\n `;\n } catch (err) {\n resultDiv.innerHTML = `<div style=\"color:#dc2626;\">\u5931\u8D25: ${escapeHtml(err.message)}</div>`;\n } finally {\n btn.disabled = false;\n }\n };\n });\n}\n\n/** \u628A judgment id \u52A0\u8FDB / \u79FB\u51FA\u5F53\u524D channel.bound_judgment_ids, \u7136\u540E\u5237\u65B0\u4E24\u8FB9 UI */\nasync function toggleChannelJudgment(judgmentId, action) {\n if (!currentChannelId) {\n showJudgmentError('\u8BF7\u5148\u9009\u4E2D\u4E00\u4E2A channel');\n return;\n }\n const ch = channels.find(c => c.id === currentChannelId);\n if (!ch) return;\n const set = new Set(Array.isArray(ch.bound_judgment_ids) ? ch.bound_judgment_ids : []);\n if (action === 'bind') set.add(judgmentId);\n else set.delete(judgmentId);\n const next = Array.from(set);\n try {\n const res = await fetch(`/channels/${currentChannelId}`, {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ bound_judgment_ids: next })\n });\n if (!res.ok) {\n const err = await res.json().catch(() => ({}));\n throw new Error(err.error || `HTTP ${res.status}`);\n }\n const updated = await res.json();\n const idx = channels.findIndex(c => c.id === currentChannelId);\n if (idx >= 0) channels[idx] = updated;\n // \u5F39\u7A97\u5F00\u7740\u5C31\u5237\u65B0, \u5173\u7740\u5C31\u8DF3\u8FC7\n if (judgmentsModal && judgmentsModal.classList.contains('active')) {\n renderJudgments(lastJudgmentsCache);\n }\n } catch (err) {\n showJudgmentError('\u7ED1\u5B9A\u5931\u8D25: ' + err.message);\n }\n}\n\n// \u5217\u8868\u5185\u7F16\u8F91/\u5220\u9664 + \u62D6\u62FD \u2014 \u4E8B\u4EF6\u59D4\u6258\nif (judgmentsList) {\n judgmentsList.addEventListener('click', async (e) => {\n const editBtn = e.target.closest && e.target.closest('.judgment-edit-btn');\n const delBtn = e.target.closest && e.target.closest('.judgment-del-btn');\n const toggleBtn = e.target.closest && e.target.closest('.judgment-toggle-btn');\n if (editBtn) {\n const id = editBtn.getAttribute('data-id');\n await editJudgment(id);\n } else if (delBtn) {\n const id = delBtn.getAttribute('data-id');\n if (!confirm('\u786E\u5B9A\u5220\u9664\u8FD9\u6761\u5224\u65AD?')) return;\n try {\n const res = await fetch('/api/judgments/' + encodeURIComponent(id), { method: 'DELETE' });\n const out = await res.json();\n if (!out.ok) throw new Error(out.error || 'delete failed');\n await loadJudgments();\n } catch (err) {\n showJudgmentError('\u5220\u9664\u5931\u8D25: ' + err.message);\n }\n } else if (toggleBtn) {\n const id = toggleBtn.getAttribute('data-id');\n const action = toggleBtn.getAttribute('data-action');\n await toggleChannelJudgment(id, action);\n }\n });\n\n // tab \u5207\u6362\n document.querySelectorAll('.judgment-tab').forEach(btn => {\n btn.addEventListener('click', () => switchJudgmentTab(btn.dataset.tab));\n });\n\n // status \u8FC7\u6EE4\n document.querySelectorAll('.judgment-status-tab').forEach(btn => {\n btn.addEventListener('click', () => switchStatusFilter(btn.dataset.status));\n });\n\n // \u62D6\u62FD: \u6BCF\u6761 judgment \u662F drag source, dataTransfer \u88C5 decision text\n judgmentsList.addEventListener('dragstart', (e) => {\n const row = e.target.closest && e.target.closest('.judgment-row');\n if (!row) return;\n const decision = row.querySelector('.judgment-decision')?.textContent || '';\n const id = row.getAttribute('data-judgment-id') || '';\n e.dataTransfer.effectAllowed = 'copy';\n e.dataTransfer.setData('text/plain', decision);\n e.dataTransfer.setData('application/x-bolloon-judgment', JSON.stringify({ id, decision }));\n });\n\n // \u591A\u9009 checkbox \u53D8\u5316 \u2192 \u66F4\u65B0\u5DE5\u5177\u680F\n judgmentsList.addEventListener('change', (e) => {\n if (e.target.classList && e.target.classList.contains('judgment-select-cb')) {\n updateBulkDeleteToolbar();\n }\n });\n}\n\n// \u6279\u91CF\u9009\u62E9\u5DE5\u5177\u680F: \u5168\u9009 / \u8BA1\u6570 / \u542F\u7528\u5220\u9664\u6309\u94AE\nconst judgmentSelectAll = document.getElementById('judgment-select-all');\nconst judgmentSelectedCount = document.getElementById('judgment-selected-count');\nconst judgmentBulkDeleteBtn = document.getElementById('judgment-bulk-delete-btn');\n\nfunction getSelectedJudgmentIds() {\n if (!judgmentsList) return [];\n return Array.from(judgmentsList.querySelectorAll('.judgment-select-cb'))\n .filter(cb => cb.checked)\n .map(cb => cb.getAttribute('data-id'))\n .filter(Boolean);\n}\n\nfunction updateBulkDeleteToolbar() {\n const ids = getSelectedJudgmentIds();\n if (judgmentSelectedCount) judgmentSelectedCount.textContent = `\u5DF2\u9009 ${ids.length}`;\n if (judgmentBulkDeleteBtn) {\n judgmentBulkDeleteBtn.disabled = ids.length === 0;\n judgmentBulkDeleteBtn.style.opacity = ids.length === 0 ? '0.5' : '1';\n judgmentBulkDeleteBtn.style.cursor = ids.length === 0 ? 'not-allowed' : 'pointer';\n }\n // \u5168\u9009 checkbox \u7684 indeterminate / checked \u72B6\u6001\u540C\u6B65\n if (judgmentSelectAll && judgmentsList) {\n const all = judgmentsList.querySelectorAll('.judgment-select-cb');\n const checked = Array.from(all).filter(cb => cb.checked);\n judgmentSelectAll.checked = all.length > 0 && checked.length === all.length;\n judgmentSelectAll.indeterminate = checked.length > 0 && checked.length < all.length;\n }\n}\n\nif (judgmentSelectAll) {\n judgmentSelectAll.addEventListener('change', (e) => {\n if (!judgmentsList) return;\n const checked = e.target.checked;\n judgmentsList.querySelectorAll('.judgment-select-cb').forEach(cb => { cb.checked = checked; });\n updateBulkDeleteToolbar();\n });\n}\n\nif (judgmentBulkDeleteBtn) {\n judgmentBulkDeleteBtn.addEventListener('click', async () => {\n const ids = getSelectedJudgmentIds();\n if (ids.length === 0) return;\n if (!confirm(`\u786E\u5B9A\u5220\u9664\u9009\u4E2D\u7684 ${ids.length} \u6761\u5224\u65AD? \u6B64\u64CD\u4F5C\u4E0D\u53EF\u64A4\u9500.`)) return;\n judgmentBulkDeleteBtn.disabled = true;\n try {\n const res = await fetch('/api/judgments/batch-delete', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ ids }),\n });\n const out = await res.json();\n if (!out.ok) throw new Error(out.error || 'failed');\n showJudgmentOk(`\u2713 \u6279\u91CF\u5220\u9664 ${out.deleted} \u6761${out.notFound?.length ? ` (${out.notFound.length} \u6761\u672A\u627E\u5230)` : ''}`);\n await loadJudgments();\n } catch (err) {\n showJudgmentError('\u6279\u91CF\u5220\u9664\u5931\u8D25: ' + err.message);\n } finally {\n if (judgmentBulkDeleteBtn) judgmentBulkDeleteBtn.disabled = false;\n }\n });\n}\n\nasync function editJudgment(id) {\n // \u7B80\u5355\u505A\u6CD5: \u7528 prompt \u5F39 3 \u4E2A\u5B57\u6BB5. \u60F3\u8981\u66F4\u597D\u7684\u4F53\u9A8C\u5C31\u7528 inline editor, \u4F46 v1 \u4E0D\u5FC5.\n const all = await (await fetch('/api/judgments')).json();\n const j = (all.judgments || []).find(x => x.id === id);\n if (!j) { showJudgmentError('\u627E\u4E0D\u5230\u8BE5\u5224\u65AD (\u53EF\u80FD\u5DF2\u5220\u9664)'); return; }\n const newDecision = prompt('\u4FEE\u6539\u5224\u65AD (decision):', j.decision);\n if (newDecision === null) return;\n const newReason = prompt('\u4FEE\u6539\u7406\u7531 (reason, \u7559\u7A7A\u4E0D\u6539):', (j.reasons && j.reasons[0]) || '');\n const newStakes = prompt('\u4FEE\u6539\u98CE\u9669 (low/medium/high/critical):', (j.context && j.context.stakes) || 'medium');\n const patch = {\n decision: newDecision.trim() || j.decision,\n reasons: newReason !== null ? [newReason.trim()].filter(Boolean) : j.reasons,\n context: newStakes ? { ...(j.context || {}), stakes: newStakes } : j.context,\n };\n try {\n const res = await fetch('/api/judgments/' + encodeURIComponent(id), {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(patch),\n });\n const out = await res.json();\n if (!out.ok) throw new Error(out.error || 'update failed');\n showJudgmentOk('\u2713 \u5DF2\u66F4\u65B0');\n await loadJudgments();\n } catch (err) {\n showJudgmentError('\u66F4\u65B0\u5931\u8D25: ' + err.message);\n }\n}\n\nasync function submitJudgment() {\n if (!judgmentSubmitBtn) return;\n const decision = (judgmentDecision?.value || '').trim();\n const reason = (judgmentReason?.value || '').trim();\n if (!decision) {\n if (judgmentError) { judgmentError.textContent = '\u5224\u65AD\u4E0D\u80FD\u4E3A\u7A7A'; judgmentError.style.display = ''; }\n return;\n }\n judgmentSubmitBtn.disabled = true;\n if (judgmentError) judgmentError.style.display = 'none';\n try {\n const res = await fetch('/api/judgments', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n decision,\n reason: reason || undefined,\n context: { domain: judgmentDomain?.value, stakes: judgmentStakes?.value },\n }),\n });\n const out = await res.json();\n if (!out.ok) throw new Error(out.error || 'unknown');\n if (judgmentDecision) judgmentDecision.value = '';\n if (judgmentReason) judgmentReason.value = '';\n await loadJudgments();\n\n // AI \u81EA\u52A8\u59D4\u6D3E: fire-and-forget. \u6839\u636E domain \u627E\u5339\u914D\u7684\u8FDC\u7AEF agent, \u89E6\u53D1 agent_delegate \u534F\u8BAE.\n // \u5931\u8D25\u4E5F\u4E0D\u5F71\u54CD\u672C\u6B21\u8BB0\u5F55.\n try {\n const del = await fetch('/api/judgments/auto-delegate', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n judgmentId: out.judgment.id,\n capability: judgmentDomain?.value || 'general',\n instruction: `\u6267\u884C\u5224\u65AD: ${out.judgment.decision}` + (reason ? ` (\u7406\u7531: ${reason})` : ''),\n }),\n });\n const delOut = await del.json();\n if (delOut.matched && delOut.sent) {\n showJudgmentOk(`\u2713 \u5DF2\u8BB0\u5F55\u5E76\u81EA\u52A8\u59D4\u6D3E\u7ED9 ${delOut.targetAgent.name}`);\n } else if (delOut.matched) {\n showJudgmentOk(`\u2713 \u5DF2\u8BB0\u5F55 (\u5339\u914D\u5230 ${delOut.targetAgent.name}, \u4F46 ${delOut.reason || '\u672A\u53D1\u9001'})`);\n } else {\n showJudgmentOk('\u2713 \u5DF2\u8BB0\u5F55 (\u672C\u5730, \u65E0\u5339\u914D\u8FDC\u7AEF agent)');\n }\n } catch (e) {\n console.warn('[judgments] auto-delegate fire failed:', e);\n }\n } catch (e) {\n if (judgmentError) { judgmentError.textContent = '\u8BB0\u5F55\u5931\u8D25: ' + e.message; judgmentError.style.display = ''; }\n } finally {\n judgmentSubmitBtn.disabled = false;\n }\n}\n\nif (judgmentsBtn) judgmentsBtn.addEventListener('click', showJudgmentsModal);\nif (judgmentsModalClose) judgmentsModalClose.addEventListener('click', hideJudgmentsModal);\nif (judgmentsModal) {\n judgmentsModal.addEventListener('click', (e) => {\n if (e.target === judgmentsModal) hideJudgmentsModal();\n });\n}\n\n// --- \u5BFC\u5165\u6587\u4EF6 (.json / .yaml / .md / .txt / .html) ---\nconst judgmentImportBtn = document.getElementById('judgment-import-btn');\nconst judgmentImportFile = document.getElementById('judgment-import-file');\n\nfunction showJudgmentError(msg) {\n if (!judgmentError) return;\n judgmentError.textContent = msg;\n judgmentError.style.display = '';\n judgmentError.style.color = '#b91c1c';\n}\nfunction showJudgmentOk(msg) {\n if (!judgmentError) return;\n judgmentError.textContent = msg;\n judgmentError.style.display = '';\n judgmentError.style.color = '#15803d';\n}\n\nfunction fileToBase64(file) {\n return new Promise((resolve, reject) => {\n const r = new FileReader();\n r.onload = () => {\n // result is \"data:<mime>;base64,<payload>\" \u2014 strip prefix\n const s = String(r.result || '');\n const idx = s.indexOf(',');\n resolve(idx >= 0 ? s.substring(idx + 1) : s);\n };\n r.onerror = () => reject(r.error || new Error('read failed'));\n r.readAsDataURL(file);\n });\n}\n\nasync function importJudgmentFile(file) {\n if (!file) return;\n if (judgmentImportBtn) judgmentImportBtn.disabled = true;\n try {\n const content = await fileToBase64(file);\n const res = await fetch('/api/judgments/import', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ filename: file.name, content }),\n });\n const out = await res.json();\n if (!out.ok) throw new Error(out.error || 'import failed');\n showJudgmentOk(`\u2713 \u5BFC\u5165 ${out.imported} \u6761${out.failed ? `, ${out.failed} \u6761\u5931\u8D25` : ''}`);\n await loadJudgments();\n } catch (e) {\n showJudgmentError('\u5BFC\u5165\u5931\u8D25: ' + e.message);\n } finally {\n if (judgmentImportBtn) judgmentImportBtn.disabled = false;\n if (judgmentImportFile) judgmentImportFile.value = '';\n }\n}\n\nif (judgmentImportBtn) {\n judgmentImportBtn.addEventListener('click', () => {\n if (judgmentImportFile) judgmentImportFile.click();\n });\n}\nif (judgmentImportFile) {\n judgmentImportFile.addEventListener('change', (e) => {\n const f = e.target.files && e.target.files[0];\n if (f) importJudgmentFile(f);\n });\n}\n\n// --- \u4ECE\u5BF9\u8BDD\u91CC \"\u84B8\u998F\u4E3A\u5224\u65AD\": \u4E8B\u4EF6\u59D4\u6258\u5230\u6D88\u606F\u5BB9\u5668, \u5339\u914D .save-as-judgment ---\n// \u4E24\u6761\u8DEF\u5F84:\n// 1. \u6709 data-channel-id \u2192 \u8C03 /api/judgments/distill-from-conversation (B \u89E6\u53D1, AI \u84B8\u998F + \u6F14\u5316\u5BF9\u9F50)\n// 2. \u6CA1\u6709 channelId (\u8001\u6309\u94AE / \u5386\u53F2\u6570\u636E) \u2192 fallback \u5230\u8001 /api/judgments (\u76F4\u5B58)\ndocument.addEventListener('click', async (e) => {\n const btn = e.target.closest && e.target.closest('.save-as-judgment');\n if (!btn) return;\n e.preventDefault();\n e.stopPropagation();\n\n const channelId = btn.getAttribute('data-channel-id');\n const decision = (btn.getAttribute('data-decision') || '').trim();\n\n if (channelId) {\n btn.classList.add('loading');\n btn.disabled = true;\n try {\n const res = await fetch('/api/judgments/distill-from-conversation', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ channelId }),\n });\n const out = await res.json();\n if (!res.ok) throw new Error(out.error || 'HTTP ' + res.status);\n\n if (!out.triggered) {\n btn.classList.remove('loading');\n btn.disabled = false;\n btn.title = '\u84B8\u998F\u5931\u8D25: ' + (out.reason || '\u65E0\u5185\u5BB9');\n return;\n }\n\n const j = out.judgment;\n const ev = out.evolved || { merged: 0, superseded: 0 };\n btn.classList.remove('loading');\n btn.classList.add('saved');\n btn.title = '\u5DF2\u84B8\u998F\u4E3A\u5224\u65AD';\n\n // inline \u786E\u8BA4\u5F39\u6846 (\u5728\u6309\u94AE\u4E0B\u65B9\u51FA\u73B0, 5 \u79D2\u540E\u81EA\u52A8\u6D88\u5931)\n showDistillConfirm(btn, {\n value: j.decision,\n evidence: (j.reasons && j.reasons[0]) || '',\n merged: ev.merged,\n superseded: ev.superseded,\n onEdit: async (newText) => {\n try {\n await fetch('/api/judgments/' + encodeURIComponent(j.id), {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ decision: newText }),\n });\n } catch (err) {\n console.error('[judgments] edit failed:', err);\n }\n },\n onReject: async () => {\n try {\n await fetch('/api/judgments/' + encodeURIComponent(j.id), {\n method: 'DELETE',\n });\n } catch (err) {\n console.error('[judgments] reject failed:', err);\n }\n },\n });\n\n // \u5237\u65B0\u5224\u65AD\u529B\u5E93\u7F13\u5B58\n setTimeout(() => loadJudgments(), 100);\n } catch (err) {\n console.error('[judgments] distill-from-chat failed:', err);\n btn.classList.remove('loading');\n btn.disabled = false;\n btn.title = '\u84B8\u998F\u5931\u8D25: ' + err.message;\n }\n return;\n }\n\n // \u8001\u8DEF\u5F84 fallback (\u6CA1\u6709 channelId, \u76F4\u63A5\u5B58\u539F\u6587)\n if (!decision) return;\n try {\n const res = await fetch('/api/judgments', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ decision, reason: '\u4ECE\u5BF9\u8BDD\u4FDD\u5B58' }),\n });\n const out = await res.json();\n if (!out.ok) throw new Error(out.error || 'failed');\n btn.classList.add('saved');\n btn.title = '\u5DF2\u5B58\u4E3A\u5224\u65AD';\n } catch (err) {\n console.error('[judgments] save-from-chat failed:', err);\n btn.title = '\u4FDD\u5B58\u5931\u8D25: ' + err.message;\n }\n});\n\n/**\n * inline \u84B8\u998F\u786E\u8BA4\u5F39\u6846 \u2014 \u5728\u6309\u94AE\u4E0B\u65B9\u51FA\u73B0, \u663E\u793A\u51DD\u7EC3\u7ED3\u679C + \u6F14\u5316\u7ED3\u679C\n * 5 \u79D2\u540E\u81EA\u52A8\u6D88\u5931, \u7528\u6237\u53EF\u70B9 \"\u7F16\u8F91\" / \"\u62D2\u7EDD\"\n */\nfunction showDistillConfirm(btn, opts) {\n const { value, evidence, merged, superseded, onEdit, onReject } = opts;\n const old = document.getElementById('distill-confirm-popup');\n if (old) old.remove();\n\n const popup = document.createElement('div');\n popup.id = 'distill-confirm-popup';\n popup.style.cssText = `\n position:absolute; z-index:1000;\n background:#fff; border:1px solid #d1d5db; border-radius:6px;\n box-shadow:0 4px 12px rgba(0,0,0,0.1);\n padding:10px 12px; min-width:280px; max-width:380px;\n font-size:13px; color:#1f2937;\n `;\n let evolveNote = '';\n if (merged > 0 || superseded > 0) {\n evolveNote = `<div style=\"font-size:11px;color:#059669;margin-top:6px;\">\u2713 \u6F14\u5316\u5BF9\u9F50: ${merged} \u6761\u5DF2\u5408\u5E76${superseded > 0 ? `, ${superseded} \u6761\u5DF2\u6DD8\u6C70` : ''}</div>`;\n }\n popup.innerHTML = `\n <div style=\"font-weight:600;margin-bottom:4px;\">\u5DF2\u84B8\u998F\u4E3A\u5224\u65AD\u529B</div>\n <div style=\"background:#f9fafb;padding:6px 8px;border-radius:4px;line-height:1.4;\">${escapeHtml(value)}</div>\n ${evidence ? `<div style=\"font-size:11px;color:#6b7280;margin-top:4px;\">\u8BC1\u636E: ${escapeHtml(evidence)}</div>` : ''}\n ${evolveNote}\n <div style=\"display:flex;gap:6px;margin-top:8px;\">\n <button class=\"dc-edit\" style=\"background:none;border:1px solid #d1d5db;color:#374151;padding:2px 10px;border-radius:3px;cursor:pointer;font-size:11px;\">\u7F16\u8F91</button>\n <button class=\"dc-reject\" style=\"background:none;border:1px solid #fca5a5;color:#b91c1c;padding:2px 10px;border-radius:3px;cursor:pointer;font-size:11px;\">\u62D2\u7EDD</button>\n <button class=\"dc-close\" style=\"margin-left:auto;background:none;border:none;color:#6b7280;cursor:pointer;font-size:14px;\">\u00D7</button>\n </div>\n `;\n\n // \u5B9A\u4F4D\n const rect = btn.getBoundingClientRect();\n popup.style.top = (window.scrollY + rect.bottom + 4) + 'px';\n popup.style.left = (window.scrollX + rect.left) + 'px';\n document.body.appendChild(popup);\n\n // \u7ED1\u5B9A\u6309\u94AE\n popup.querySelector('.dc-edit').onclick = () => {\n const newText = prompt('\u7F16\u8F91\u5224\u65AD\u529B:', value);\n if (newText && newText.trim() && onEdit) onEdit(newText.trim());\n popup.remove();\n };\n popup.querySelector('.dc-reject').onclick = () => {\n if (onReject) onReject();\n popup.remove();\n };\n popup.querySelector('.dc-close').onclick = () => popup.remove();\n setTimeout(() => popup.remove(), 5000);\n}\nif (judgmentSubmitBtn) judgmentSubmitBtn.addEventListener('click', submitJudgment);\n\n// \u542F\u52A8\u65F6\u62C9\u4E00\u6B21, \u8BA9\u5FBD\u7AE0\u663E\u793A\u603B\u6570 (\u4E0D\u6253\u5F00 modal \u4E5F\u80FD\u770B\u5230)\nloadJudgments();\n// \u540E\u53F0\u5B9A\u671F\u5237\u65B0 (\u4E0E modal \u6253\u5F00/\u5173\u95ED\u65E0\u5173, \u4EFB\u4F55\u65F6\u5019\u90FD\u4FDD\u6301\u5FBD\u7AE0\u65B0\u9C9C)\nsetInterval(loadJudgments, 10000);\n\n// ============================================================================\n// v3: P2P \u597D\u53CB (known peers) + \u6536\u5230\u7684\u5206\u4EAB\n// ============================================================================\n\nlet knownPeers = []; // { name, publicKey, lastConnectedAt, addedAt }\n\nasync function loadRemoteChannels() {\n try {\n // 1) \u62C9 known peers (\u597D\u53CB\u5217\u8868)\n const res = await fetch('/api/p2p-peers');\n if (res.ok) {\n const data = await res.json();\n knownPeers = Array.isArray(data.peers) ? data.peers : [];\n }\n // 2) 2026-06-10 \u4FEE: \u540C\u65F6\u62C9 /api/remote-channels, \u515C\u5E95 SSE \u63A8\u9001\u6F0F\u6389\u7684\u60C5\u51B5\n // (\u9875\u9762\u5237\u65B0\u540E remoteChannels[] = [], \u5FC5\u987B\u4E3B\u52A8\u62C9\u4E00\u6B21\u624D\u6709\u6570\u636E)\n const r2 = await fetch('/api/remote-channels');\n if (r2.ok) {\n const data2 = await r2.json();\n const peers = Array.isArray(data2.peers) ? data2.peers : [];\n // \u5408\u5E76\u5230 remoteChannels[]: \u6309 peerId \u8986\u76D6\n for (const p of peers) {\n let group = remoteChannels.find(g => g.peerId === p.peerId);\n if (!group) {\n group = { peerId: p.peerId, channels: [], peerName: ('peer-' + p.peerId.substring(0, 8)) };\n remoteChannels.push(group);\n }\n group.channels = p.channels || [];\n }\n }\n renderRemoteChannels();\n // 3) \u8FDC\u7AEF\u6570\u636E\u53EF\u80FD\u53D8\u5316, \u540C\u6B65 @-mention \u5217\u8868\n if (typeof refreshMentionChannels === 'function') {\n refreshMentionChannels();\n }\n } catch (err) {\n console.error('[v3] loadRemoteChannels \u5931\u8D25:', err);\n }\n}\n\nfunction renderRemoteChannels() {\n const list = document.getElementById('remote-channel-list');\n if (!list) return;\n\n // \u6309 peerId \u5206\u7EC4 channels\n const channelsByPeer = {};\n for (const p of remoteChannels) {\n channelsByPeer[p.peerId] = p.channels || [];\n }\n\n // 2026-06-10 \u4FEE: \u4E4B\u524D UI \u53EA\u6E32\u67D3 knownPeers, \u4F46\u5BF9\u9762 publicKey \u53EF\u80FD\u8DDF\u672C\u673A known_peers \u4E0D\u5339\u914D\n // (\u4F8B\u5982\u5BF9\u9762\u91CD\u542F / \u6362 role / \u7B2C\u4E00\u6B21\u76F8\u8FDE\u8FD8\u6CA1\u52A0\u4E3A\u597D\u53CB), \u5BFC\u81F4 remoteChannels \u91CC\u6709\u6570\u636E UI \u5374\u7A7A\u767D.\n // \u4FEE\u6CD5: \u628A remoteChannels \u91CC\u7684 \"\u964C\u751F peer\" (\u4E0D\u5728 known_peers \u91CC) \u4E5F\u6E32\u67D3\u51FA\u6765, \u6807\u8BB0\u4E3A\u672A\u52A0\u597D\u53CB.\n const knownPks = new Set(knownPeers.map(p => p.publicKey));\n const strangerPeers = remoteChannels\n .filter(p => !knownPks.has(p.peerId))\n .map(p => ({\n publicKey: p.peerId,\n name: p.peerName || ('\u672A\u6388\u6743 ' + p.peerId.substring(0, 8)),\n lastConnectedAt: null,\n _isStranger: true\n }));\n const allPeers = [...knownPeers, ...strangerPeers];\n\n if (allPeers.length === 0) {\n list.innerHTML = '<li style=\"color:var(--text-muted);font-size:11px;padding:8px 4px;text-align:center;\">(\u6682\u65E0\u597D\u53CB, \u70B9 + \u6DFB\u52A0)</li>';\n return;\n }\n\n const html = allPeers.map(peer => {\n const peerChannels = channelsByPeer[peer.publicKey] || [];\n const lastConn = peer.lastConnectedAt\n ? new Date(peer.lastConnectedAt).toLocaleDateString()\n : (peer._isStranger ? '\u964C\u751F peer' : '\u4ECE\u672A\u8FDE\u63A5');\n const strangerStyle = peer._isStranger ? 'border:1px dashed var(--border-light);' : '';\n const strangerIcon = peer._isStranger ? '\u2754' : '\uD83D\uDC64';\n // 2026-06-11: \u6298\u53E0\u903B\u8F91 (\u5168\u4E0D\u5C55\u5F00)\n // - \u6240\u6709 peer \u9996\u6B21\u89C1\u90FD\u9ED8\u8BA4 *\u6298\u53E0* (\u5305\u62EC known_peers \u7B2C\u4E00\u4E2A) \u2014 \u7528\u6237\u4E00\u8FDB\u6765\u770B\u5230\u5B8C\u6574 peer \u5217\u8868\n // - \u6807\u9898\u680F\u53F3\u4FA7 \"X ch\" \u63D0\u793A\u6709\u5185\u5BB9, \u7528\u6237\u70B9 caret \u5C55\u5F00\n // - \u5DF2\u89C1\u8FC7: \u6CBF\u7528 collapsedPeers (\u7528\u6237\u4E0A\u6B21\u9009\u62E9)\n // - \"\u5168\u90E8\u5C55\u5F00/\u6298\u53E0\" \u6309\u94AE\u5728 P2P header (id=p2p-toggle-all-btn)\n if (!seenPeers.has(peer.publicKey)) {\n seenPeers.add(peer.publicKey);\n collapsedPeers.add(peer.publicKey); // \u5168\u90E8\u9ED8\u8BA4\u6298\u53E0\n saveSeenPeers();\n saveCollapsedPeers();\n }\n const isCollapsed = collapsedPeers.has(peer.publicKey);\n const caretChar = '\u25BE'; // CSS rotate -90deg \u5904\u7406\u6298\u53E0\u6001\n return `\n <li class=\"remote-peer-group ${isCollapsed ? 'collapsed' : ''}\" style=\"margin-bottom:10px;${strangerStyle}\">\n <div class=\"remote-peer-header\" data-peer-name=\"${escapeHtml(peer.name)}\" data-peer-pk=\"${escapeHtml(peer.publicKey)}\"\n style=\"display:flex;align-items:center;gap:6px;padding:6px 8px;background:var(--bg-hover);border-radius:4px;cursor:pointer;\">\n <button class=\"peer-caret-btn\" data-toggle-peer=\"${escapeHtml(peer.publicKey)}\" title=\"\u6298\u53E0/\u5C55\u5F00\"\n style=\"background:var(--bg-active);border:1px solid var(--border);color:var(--text);cursor:pointer;width:22px;height:22px;border-radius:4px;font-size:12px;line-height:1;padding:0;display:flex;align-items:center;justify-content:center;flex:0 0 auto;\">${caretChar}</button>\n <span style=\"font-size:13px;\">${strangerIcon}</span>\n <span style=\"flex:1;font-size:12px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;\" title=\"${escapeHtml(peer.publicKey)}\">${escapeHtml(peer.name)}</span>\n <span style=\"font-size:9px;color:var(--text-muted);\">${peerChannels.length > 0 ? `${peerChannels.length} ch \u00B7 ` : ''}${lastConn}</span>\n <button class=\"peer-share-btn\" title=\"\u5206\u4EAB channel \u7ED9 ${escapeHtml(peer.name)}\"\n style=\"background:transparent;border:1px solid var(--border);color:var(--text);cursor:pointer;width:22px;height:22px;border-radius:4px;font-size:12px;line-height:1;padding:0;display:flex;align-items:center;justify-content:center;flex:0 0 auto;\">\uD83D\uDCE4</button>\n </div>\n <div class=\"remote-peer-channels\" style=\"margin-top:4px;margin-left:8px;\">\n ${peerChannels.length === 0\n ? '<div style=\"font-size:10px;color:var(--text-muted);padding:2px 4px;\">(\u5BF9\u65B9\u8FD8\u6CA1\u5206\u4EAB channel \u7ED9\u4F60)</div>'\n : peerChannels.map(c => `\n <div class=\"remote-channel-row\" data-peer-id=\"${escapeHtml(peer.publicKey)}\" data-channel-id=\"${escapeHtml(c.id)}\"\n style=\"display:flex;align-items:center;gap:6px;padding:4px 6px;cursor:pointer;border-radius:4px;font-size:12px;\">\n <span>\uD83E\uDD16</span>\n <span style=\"flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;\" title=\"${escapeHtml(safeChannelName(c.name, ''))}\">${escapeHtml(safeChannelName(c.name))}</span>\n </div>\n `).join('')\n }\n </div>\n </li>\n `;\n }).join('');\n list.innerHTML = html;\n\n // 2026-06-10: \u6298\u53E0\u6309\u94AE\u70B9\u51FB \u2192 \u5207\u6298\u53E0 (stopPropagation \u9632\u6B62\u5192\u6CE1\u89E6\u53D1 header \u7684\u5206\u4EAB modal)\n list.querySelectorAll('.peer-caret-btn[data-toggle-peer]').forEach(btn => {\n btn.addEventListener('click', (e) => {\n e.stopPropagation();\n const pk = btn.getAttribute('data-toggle-peer');\n togglePeerCollapsed(pk);\n });\n });\n\n // \u7ED1\u5B9A: \u70B9\u51FB channel \u2192 \u5F39\u804A\u5929\u7A97\u53E3\n list.querySelectorAll('.remote-channel-row').forEach(row => {\n row.addEventListener('click', () => {\n const peerId = row.dataset.peerId;\n const channelId = row.dataset.channelId;\n const channelName = row.querySelector('span[title]')?.getAttribute('title') || channelId;\n console.log('[v3] \u70B9\u51FB\u8FDC\u7AEF channel:', peerId.substring(0,12), channelId);\n openRemoteChannelChat(peerId, channelId, channelName);\n });\n });\n // \u7ED1\u5B9A: \u70B9\u51FB peer \u5934\u90E8 \u2192 \u5F39\u5206\u4EAB modal (\u8BA9 A \u51B3\u5B9A\u5206\u4EAB\u672C\u673A\u54EA\u4E9B channel \u7ED9\u8FD9\u4E2A peer)\n // 2026-06-15 \u4FEE\u6B63: \u6574\u5757 click \u6539\u6210\u663E\u5F0F \"\uD83D\uDCE4 \u5206\u4EAB\" \u6309\u94AE\u89E6\u53D1, \u907F\u514D caret \u6298\u53E0\u4E0E\u5206\u4EAB modal \u51B2\u7A81\n list.querySelectorAll('.remote-peer-header').forEach(row => {\n const shareBtn = row.querySelector('.peer-share-btn');\n if (shareBtn) {\n shareBtn.addEventListener('click', (e) => {\n e.stopPropagation();\n const peerName = row.dataset.peerName;\n const peerPk = row.dataset.peerPk;\n openShareToPeerModal(peerName, peerPk);\n });\n }\n });\n\n // 2026-06-10: \u6BCF\u4E2A peer \u5934\u90E8\u53CC\u51FB \u2192 \u6539\u540D\u5B57 / \u6539\u5907\u6CE8\n list.querySelectorAll('.remote-peer-header').forEach(row => {\n row.addEventListener('dblclick', (e) => {\n if (e.target.closest('.peer-caret-btn')) return;\n const peerName = row.dataset.peerName;\n const peerPk = row.dataset.peerPk;\n openEditPeerModal(peerName, peerPk);\n });\n });\n\n // 2026-06-10: \u6E32\u67D3\u5B8C\u6210\u540E\u540C\u6B65 header \u5207\u6362\u6309\u94AE\u56FE\u6807\n if (typeof window.__syncP2PToggleAllBtn === 'function') window.__syncP2PToggleAllBtn();\n}\n\n/** v3: \u6539 peer \u540D\u5B57 / \u5907\u6CE8 modal (\u6301\u4E45\u5316\u5230 known_peers.json) */\nasync function openEditPeerModal(peerName, peerPublicKey) {\n document.getElementById('edit-peer-modal')?.remove();\n // \u5148\u8BFB known_peers \u62FF\u5230\u73B0\u6709 notes\n let currentNotes = '';\n let currentName = peerName;\n try {\n const r = await fetch('/api/p2p-peers');\n if (r.ok) {\n const d = await r.json();\n const entry = (d.peers || []).find(p => p.publicKey === peerPublicKey);\n if (entry) {\n currentName = entry.name || peerName;\n currentNotes = entry.notes || '';\n }\n }\n } catch {}\n const html = `\n <div id=\"edit-peer-modal\" class=\"friend-req-overlay\">\n <div class=\"friend-req-shell\" style=\"width:520px;\">\n <div class=\"friend-req-header\">\n <span style=\"font-size:18px;\">\u270F\uFE0F</span>\n <div style=\"flex:1;min-width:0;\">\n <div class=\"friend-req-title\">\u7F16\u8F91\u597D\u53CB</div>\n <div class=\"friend-req-meta\">publicKey: ${escapeHtml(peerPublicKey.substring(0,16))}\u2026</div>\n </div>\n </div>\n <div class=\"friend-req-body\">\n <label style=\"display:block;margin-bottom:6px;font-size:12px;color:var(--text-secondary);\">\u663E\u793A\u540D\u5B57</label>\n <input id=\"epm-name\" type=\"text\" value=\"${escapeHtml(currentName)}\"\n style=\"width:100%;padding:8px 10px;border:1px solid var(--border);border-radius:4px;background:var(--bg-main);color:var(--text);font-family:inherit;font-size:13px;box-sizing:border-box;margin-bottom:12px;\">\n <label style=\"display:block;margin-bottom:6px;font-size:12px;color:var(--text-secondary);\">\u5907\u6CE8 (\u81EA\u7531\u6587\u672C, \u4F8B\u5982\u5408\u4F5C\u9886\u57DF / \u600E\u4E48\u8BA4\u8BC6\u7684)</label>\n <textarea id=\"epm-notes\" rows=\"4\" placeholder=\"\u4F8B\u5982: 2026-06 \u5408\u4F5C LLM \u4EE3\u53D1\u9A8C\u8BC1\"\n style=\"width:100%;padding:8px 10px;border:1px solid var(--border);border-radius:4px;background:var(--bg-main);color:var(--text);font-family:inherit;font-size:13px;box-sizing:border-box;resize:vertical;\">${escapeHtml(currentNotes)}</textarea>\n </div>\n <div class=\"friend-req-actions\">\n <button id=\"epm-cancel\" class=\"friend-req-btn-deny\">\u53D6\u6D88</button>\n <button id=\"epm-save\" class=\"friend-req-btn-accept\">\u4FDD\u5B58</button>\n </div>\n </div>\n </div>\n `;\n document.body.insertAdjacentHTML('beforeend', html);\n const close = () => document.getElementById('edit-peer-modal')?.remove();\n document.getElementById('epm-cancel').onclick = close;\n document.getElementById('epm-save').onclick = async () => {\n const newName = document.getElementById('epm-name').value.trim() || currentName;\n const newNotes = document.getElementById('epm-notes').value;\n try {\n const r = await fetch(`/api/p2p-peers/${encodeURIComponent(peerName)}`, {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ name: newName, notes: newNotes })\n });\n const data = await r.json();\n if (!r.ok) throw new Error(data.error || 'save failed');\n console.log('[v3] \u6539 peer \u6210\u529F:', newName, '\u5907\u6CE8:', newNotes);\n showSimpleToast(`\u2705 \u5DF2\u4FDD\u5B58 ${newName}`);\n close();\n // \u91CD\u65B0\u62C9 known_peers + \u8FDC\u7A0B channels \u91CD\u65B0\u6E32\u67D3\n const r2 = await fetch('/api/p2p-peers');\n if (r2.ok) {\n const d2 = await r2.json();\n knownPeers = Array.isArray(d2.peers) ? d2.peers : [];\n }\n renderRemoteChannels();\n } catch (err) {\n console.error('[v3] \u4FDD\u5B58 peer \u5931\u8D25:', err);\n alert('\u4FDD\u5B58\u5931\u8D25: ' + (err.message || err));\n }\n };\n}\n\n/** v3: \u5206\u4EAB channel \u7ED9\u6307\u5B9A peer \u7684 modal (A \u4FA7\u7528) */\n/** v3: \u5206\u4EAB channel \u7ED9\u6307\u5B9A peer \u7684 modal (A \u4FA7\u7528) \u2014 2026-06-11 \u6539\u7528 Step 3 \u98CE\u683C class */\nasync function openShareToPeerModal(peerName, peerPublicKey) {\n document.getElementById('share-to-peer-modal')?.remove();\n let allChannels = [];\n try {\n const res = await fetch('/channels');\n if (res.ok) allChannels = await res.json();\n } catch (err) { console.error('openShareToPeerModal:', err); }\n const rows = allChannels.length === 0\n ? '<div class=\"share-modal-empty\">\u8FD8\u6CA1\u6709 channel</div>'\n : allChannels.map(ch => {\n const isShared = Array.isArray(ch.shared_with_peers) && ch.shared_with_peers.includes(peerPublicKey);\n return `\n <label class=\"share-modal-row\">\n <input type=\"checkbox\" data-cid=\"${escapeHtml(ch.id)}\" ${isShared ? 'checked' : ''} class=\"share-modal-cb\">\n <div class=\"share-modal-row-info\">\n <div class=\"share-modal-row-name\">${escapeHtml(ch.name || '(\u672A\u547D\u540D)')}</div>\n <div class=\"share-modal-row-meta\">\n ${isShared ? '\u2713 \u5DF2\u5206\u4EAB' : '\u672A\u5206\u4EAB'} \u00B7 ${escapeHtml(ch.id.slice(0, 24))}\u2026\n </div>\n </div>\n </label>\n `;\n }).join('');\n const html = `\n <div id=\"share-to-peer-modal\" class=\"friend-req-overlay\">\n <div class=\"friend-req-shell share-modal-shell\">\n <div class=\"friend-req-header\">\n <span style=\"font-size:18px;\">\uD83D\uDCE4</span>\n <div style=\"flex:1;min-width:0;\">\n <div class=\"friend-req-title\">\u5206\u4EAB channel \u7ED9 ${escapeHtml(peerName)}</div>\n <div class=\"friend-req-meta\">${escapeHtml(peerPublicKey.substring(0,16))}\u2026</div>\n </div>\n <button id=\"spm-close\" class=\"friend-req-btn-close\">\u00D7</button>\n </div>\n <div class=\"share-modal-hint\">\u52FE\u9009\u8981\u5206\u4EAB\u7684 channel, \u5BF9\u65B9\u624D\u80FD\u770B\u5230</div>\n <div id=\"spm-list\" class=\"share-modal-list\">${rows}</div>\n <div class=\"friend-req-actions\">\n <button id=\"spm-cancel\" class=\"friend-req-btn-deny\">\u53D6\u6D88</button>\n <button id=\"spm-save\" class=\"friend-req-btn-accept\">\u4FDD\u5B58\u5206\u4EAB</button>\n </div>\n </div>\n </div>\n `;\n document.body.insertAdjacentHTML('beforeend', html);\n const overlay = document.getElementById('share-to-peer-modal');\n\n // \u5173\u95ED\u51FD\u6570 \u2014 \u96C6\u4E2D\u5904\u7406 (ESC / backdrop / \u00D7 / \u53D6\u6D88 \u5171\u7528)\n const closeModal = () => {\n overlay.remove();\n document.removeEventListener('keydown', onEsc);\n };\n\n // ESC \u5173\u95ED\n const onEsc = (e) => {\n if (e.key === 'Escape') closeModal();\n };\n document.addEventListener('keydown', onEsc);\n\n // \u00D7 \u5173\u95ED\n document.getElementById('spm-close').onclick = closeModal;\n // \u53D6\u6D88\u5173\u95ED\n document.getElementById('spm-cancel').onclick = closeModal;\n // \u70B9\u51FB overlay \u80CC\u666F\u5173\u95ED (\u70B9\u5230 shell \u4E0D\u5173)\n overlay.onclick = (e) => {\n if (e.target === overlay) closeModal();\n };\n document.getElementById('spm-save').onclick = async () => {\n const checkedIds = [...overlay.querySelectorAll('input[type=checkbox][data-cid]:checked')].map(el => el.dataset.cid);\n // \u5BF9\u6BCF\u4E2A channel \u5355\u72EC PATCH \u2014 \u8BBE shared_with_peers \u4E3A checked \u5217\u8868\n let ok = 0, fail = 0;\n for (const ch of allChannels) {\n const shouldShare = checkedIds.includes(ch.id);\n const wasShared = Array.isArray(ch.shared_with_peers) && ch.shared_with_peers.includes(peerPublicKey);\n if (shouldShare === wasShared) continue;\n const newList = (ch.shared_with_peers || []).filter((p) => p !== peerPublicKey);\n if (shouldShare) newList.push(peerPublicKey);\n try {\n const res = await fetch(`/channels/${encodeURIComponent(ch.id)}`, {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ shared_with_peers: newList })\n });\n if (res.ok) ok++; else fail++;\n } catch { fail++; }\n }\n showSimpleToast(`\u5206\u4EAB\u66F4\u65B0\u5B8C\u6210: \u6210\u529F ${ok}, \u5931\u8D25 ${fail}`, ok > 0 ? 'info' : (fail > 0 ? 'error' : 'info'));\n overlay.remove();\n };\n}\n\n/** v3: \u8DDF\u8FDC\u7AEF channel \u804A\u5929\u7684\u7B80\u6613\u5F39\u7A97\n * 2026-06-10 \u91CD\u5199: UI \u5B8C\u5168\u5BF9\u9F50\u672C\u5730\u804A\u5929 (\u590D\u7528 addMessage / .messages / .bubble \u6574\u5957\u6837\u5F0F),\n * marked.parse + cleanThink + cleanEnv \u81EA\u52A8\u751F\u6548, \u4E0D\u518D\u88F8\u6587\u672C.\n */\nfunction openRemoteChannelChat(peerPublicKey, channelId, channelName) {\n // \u79FB\u9664\u5DF2\u6709 modal\n document.getElementById('remote-chat-modal')?.remove();\n const html = `\n <div id=\"remote-chat-modal\" class=\"remote-chat-overlay\">\n <div class=\"remote-chat-shell\">\n <div class=\"remote-chat-header\">\n <div style=\"flex:1;min-width:0;\">\n <div class=\"remote-chat-title\">\uD83C\uDF10 \u8DDF ${escapeHtml(channelName)} \u804A\u5929</div>\n <div class=\"remote-chat-meta\">\u8FDC\u7AEF peer: ${escapeHtml(peerPublicKey.substring(0,16))}\u2026 \u00B7 ${escapeHtml(channelId)}</div>\n </div>\n <button id=\"rcm-refresh-history\" title=\"\u91CD\u65B0\u62C9\u5386\u53F2\" class=\"remote-chat-btn-secondary\">\u21BB \u5386\u53F2</button>\n <button id=\"rcm-close\" class=\"remote-chat-btn-close\">\u00D7</button>\n </div>\n <div id=\"rcm-thinking\" class=\"remote-chat-thinking\" style=\"display:none;\">\n \uD83D\uDCE5 \u6B63\u5728\u4ECE\u8FDC\u7AEF\u62C9\u5386\u53F2 + \u5224\u65AD\u529B\u2026\n </div>\n <div id=\"rcm-log\" class=\"messages remote-chat-log\"></div>\n <div class=\"remote-chat-input-row\">\n <input id=\"rcm-input\" type=\"text\" placeholder=\"\u8F93\u5165\u6D88\u606F, \u53D1\u9001\u5230\u8FDC\u7AEF channel...\" class=\"remote-chat-input\">\n <button id=\"rcm-send\" class=\"remote-chat-btn-send\">\u53D1\u9001</button>\n </div>\n </div>\n </div>\n `;\n document.body.insertAdjacentHTML('beforeend', html);\n\n const log = document.getElementById('rcm-log');\n const inputEl = document.getElementById('rcm-input');\n const sendBtn = document.getElementById('rcm-send');\n const thinkingEl = document.getElementById('rcm-thinking');\n let historyRefreshTimer = null;\n document.getElementById('rcm-close').onclick = () => {\n if (historyRefreshTimer) { clearInterval(historyRefreshTimer); historyRefreshTimer = null; }\n document.getElementById('remote-chat-modal').remove();\n };\n document.getElementById('rcm-refresh-history').onclick = () => loadHistory(false);\n\n // 2026-06-10 \u6539: \u76F4\u63A5\u590D\u7528\u672C\u5730 addMessage, \u81EA\u52A8\u83B7\u5F97 marked + think \u6298\u53E0 + env \u6298\u53E0 + \u4E3B\u9898\u53D8\u91CF\n const append = (text, role) => {\n addMessage(text, role === 'user' ? 'user' : 'ai', false, log);\n log.scrollTop = log.scrollHeight;\n };\n\n // \u7CFB\u7EDF\u63D0\u793A\u7528\u66F4\u8F7B\u91CF\u7684\u6837\u5F0F (\u4E0D\u8D70 addMessage, \u907F\u514D\u88AB\u5F53\u804A\u5929\u8BB0\u5F55\u88C1\u526A)\n const appendSystem = (text, kind = 'info') => {\n const el = document.createElement('div');\n el.className = `remote-chat-sysmsg remote-chat-sysmsg-${kind}`;\n el.textContent = text;\n log.appendChild(el);\n log.scrollTop = log.scrollHeight;\n };\n\n // v3 \u65B0\u589E: \u62C9 A \u7AEF\u7684 channel \u5386\u53F2 (\u542B messages + judgments)\n async function loadHistory(isSilent) {\n if (!document.getElementById('remote-chat-modal')) return; // modal \u5DF2\u5173\u95ED\n\n if (isSilent) {\n try {\n const res = await fetch(`/api/remote-channels/chat-history?targetPublicKey=${encodeURIComponent(peerPublicKey)}&channelId=${encodeURIComponent(channelId)}`);\n if (!res.ok || !document.getElementById('remote-chat-modal')) return;\n const data = await res.json();\n const newMsgs = data.messages || [];\n const oldCount = log.querySelectorAll('.message').length;\n if (newMsgs.length === oldCount) return;\n const scrollWasAtBottom = log.scrollTop + log.clientHeight >= log.scrollHeight - 30;\n renderHistory(data);\n if (scrollWasAtBottom) {\n setTimeout(() => { log.scrollTop = log.scrollHeight; }, 50);\n }\n } catch (_) { /* \u9759\u9ED8\u5931\u8D25 */ }\n return;\n }\n\n thinkingEl.style.display = 'block';\n log.innerHTML = '';\n try {\n const res = await fetch(`/api/remote-channels/chat-history?targetPublicKey=${encodeURIComponent(peerPublicKey)}&channelId=${encodeURIComponent(channelId)}`);\n const data = await res.json();\n if (!res.ok) {\n appendSystem(`\u62C9\u53D6\u5931\u8D25: ${data.error || 'unknown'}`, 'error');\n thinkingEl.style.display = 'none';\n return;\n }\n renderHistory(data);\n } catch (err) {\n appendSystem(`\u62C9\u53D6\u5F02\u5E38: ${err.message}`, 'error');\n } finally {\n thinkingEl.style.display = 'none';\n }\n }\n\n function renderHistory(data) {\n log.innerHTML = '';\n\n // 1. \u663E\u793A judgment \u4F9D\u636E (header) \u2014 \u4FDD\u7559, \u4F46\u7528 class \u5316\u6837\u5F0F\n const judgments = data.judgments || { bound: [], candidates: [] };\n if (judgments.bound && judgments.bound.length > 0) {\n const jh = document.createElement('div');\n jh.className = 'remote-chat-judgments';\n let h = `<div class=\"remote-chat-judgments-title\">\uD83D\uDEE1\uFE0F \u5BF9\u65B9 channel \u7ED1\u5B9A\u7684\u5224\u65AD\u529B (${judgments.bound.length} \u6761\u786C\u7EA6\u675F)</div>`;\n for (const j of judgments.bound) {\n h += `<div class=\"remote-chat-judgment-item\">\u2022 <b>${escapeHtml((j.decision || '').slice(0, 100))}</b>${j.domain ? `<span class=\"remote-chat-judgment-tag\"> [${escapeHtml(j.domain)}${j.stakes ? '/' + escapeHtml(j.stakes) : ''}]</span>` : ''}${j.reasons && j.reasons.length ? '<br><span class=\"remote-chat-judgment-reason\">\u7406\u7531: ' + escapeHtml(j.reasons.join('; ').slice(0, 100)) + '</span>' : ''}</div>`;\n }\n if (judgments.candidates && judgments.candidates.length > 0) {\n h += `<div class=\"remote-chat-judgments-foot\">+ ${judgments.candidates.length} \u6761\u5019\u9009\u5224\u65AD\u529B (LLM \u53EF\u81EA\u9009\u53C2\u8003)</div>`;\n }\n jh.innerHTML = h;\n log.appendChild(jh);\n }\n\n // 2. \u663E\u793A\u5386\u53F2 messages \u2014 \u5B8C\u5168\u590D\u7528\u672C\u5730 addMessage \u6E32\u67D3\n const msgs = data.messages || [];\n if (msgs.length === 0) {\n appendSystem('\u8FD8\u6CA1\u6709\u5386\u53F2\u6D88\u606F, \u5728\u4E0B\u9762\u53D1\u7B2C\u4E00\u6761\u5427', 'info');\n } else {\n for (const m of msgs) {\n // \u8FDC\u7AEF owner \u7684 user \u6D88\u606F vs \u8FDC\u7AEF\u8BBF\u5BA2 (B) \u7684 user \u6D88\u606F vs A \u7684 LLM \u56DE\u590D\n // \u5168\u90E8\u8D70 addMessage, \u8BA9 marked/think/env \u81EA\u52A8\u5904\u7406. \u6765\u6E90\u7528\u4E00\u4E2A\u5C0F prefix \u6807\u8BB0.\n const type = m.type === 'user' ? 'user' : 'ai';\n let prefix = '';\n if (m.type === 'user') {\n if (m.source === 'remote') {\n prefix = `\uD83C\uDF10 \u8FDC\u7AEF\u8BBF\u5BA2${m.fromPublicKey ? ' (' + m.fromPublicKey.substring(0, 8) + '\u2026)' : ''}\\n\\n`;\n } else {\n prefix = `\uD83D\uDC64 A (\u5185\u90E8 owner)\\n\\n`;\n }\n } else {\n prefix = `\uD83E\uDD16 A \u7684 LLM\\n\\n`;\n }\n addMessage(prefix + (m.content || ''), type, false, log);\n }\n setTimeout(() => { log.scrollTop = log.scrollHeight; }, 50);\n }\n }\n\n const doSend = async () => {\n const text = inputEl.value.trim();\n if (!text) return;\n append(text, 'user');\n inputEl.value = '';\n sendBtn.disabled = true;\n sendBtn.textContent = '...';\n try {\n const res = await fetch('/api/remote-channels/chat-send', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ targetPublicKey: peerPublicKey, channelId, text })\n });\n const data = await res.json();\n if (!res.ok) throw new Error(data.error || 'send failed');\n // \u4E0D\u518D appendSystem('\u5DF2\u53D1\u9001...') \u2014\u2014 \u7528\u6237\u770B\u5230\u81EA\u5DF1\u6D88\u606F\u5DF2\u4E0A\u5C4F\u5C31\u77E5\u9053, \u7CFB\u7EDF\u63D0\u793A\u662F\u566A\u97F3\n } catch (err) {\n appendSystem('\u53D1\u9001\u5931\u8D25: ' + (err.message || err), 'error');\n } finally {\n sendBtn.disabled = false;\n sendBtn.textContent = '\u53D1\u9001';\n }\n };\n sendBtn.onclick = doSend;\n inputEl.onkeydown = (e) => { if (e.key === 'Enter') doSend(); };\n // v3 \u65B0\u589E: B \u7AEF\u8FDC\u7AEF chat \u4E5F\u652F\u6301 @-autocomplete\n setupMentionAutocomplete(inputEl);\n inputEl.focus();\n startV3GlobalSSE();\n\n // \u6253\u5F00\u65F6\u7ACB\u5373\u62C9\u5386\u53F2\n loadHistory(false);\n\n // \u6BCF 15 \u79D2\u81EA\u52A8\u9759\u9ED8\u5237\u65B0, \u540C\u6B65\u8FDC\u7AEF owner \u6216\u5176\u4ED6\u8BBF\u5BA2\u7684\u65B0\u6D88\u606F\n historyRefreshTimer = setInterval(() => loadHistory(true), 15000);\n}\n\n// Phase 3: \u6211\u7684 ID \u6309\u94AE \u2192 \u771F modal (\u907F\u514D confirm \u5728\u67D0\u4E9B\u73AF\u5883\u88AB\u7981\u7528)\nconst showMyIdBtn = document.getElementById('show-my-p2p-id-btn');\nif (showMyIdBtn) {\n showMyIdBtn.addEventListener('click', async (e) => {\n e.stopPropagation();\n // \u79FB\u9664\u5DF2\u6709 modal\n document.getElementById('my-p2p-id-modal')?.remove();\n // \u7ACB\u5373\u5F39\u51FA loading \u72B6\u6001 modal\n const html = `\n <div id=\"my-p2p-id-modal\" style=\"position:fixed;inset:0;background:rgba(0,0,0,0.5);z-index:10003;display:flex;align-items:center;justify-content:center;\">\n <div style=\"background:#fff;border-radius:8px;width:480px;max-width:92vw;display:flex;flex-direction:column;box-shadow:0 10px 40px rgba(0,0,0,0.2);\">\n <div style=\"padding:14px 18px;border-bottom:1px solid #e5e7eb;display:flex;align-items:center;justify-content:space-between;\">\n <div style=\"font-size:15px;font-weight:600;\">\uD83E\uDEAA \u6211\u7684 P2P \u8EAB\u4EFD</div>\n <button id=\"mpim-close\" style=\"background:none;border:none;font-size:20px;color:#6b7280;cursor:pointer;\">\u00D7</button>\n </div>\n <div id=\"mpim-body\" style=\"padding:16px 18px;\">\n <div style=\"color:#6b7280;font-size:13px;margin-bottom:10px;\">\u6B63\u5728\u83B7\u53D6 publicKey\u2026</div>\n </div>\n </div>\n </div>\n `;\n document.body.insertAdjacentHTML('beforeend', html);\n document.getElementById('mpim-close').onclick = () => document.getElementById('my-p2p-id-modal').remove();\n\n try {\n const res = await fetch('/api/p2p-publickey');\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n const data = await res.json();\n const pk = data.publicKey || '';\n const body = document.getElementById('mpim-body');\n if (!pk || pk.length !== 64) {\n body.innerHTML = `<div style=\"color:#b91c1c;font-size:13px;\">\u2717 P2PDirect \u8FD8\u6CA1\u542F\u52A8, \u5237\u65B0\u9875\u9762\u7A0D\u540E\u518D\u8BD5</div>`;\n return;\n }\n body.innerHTML = `\n <div style=\"font-size:12px;color:#6b7280;margin-bottom:8px;\">\u628A\u4E0B\u9762\u8FD9\u4E32\u53D1\u7ED9\u597D\u53CB, \u597D\u53CB\u5728 P2P \u597D\u53CB\u533A\u70B9 \"+ \u597D\u53CB\" \u7C98\u8D34\u5373\u53EF\u52A0\u4F60:</div>\n <div style=\"display:flex;gap:6px;align-items:center;margin-bottom:12px;\">\n <code id=\"mpim-pk\" style=\"flex:1;padding:8px 10px;background:#f3f4f6;border:1px solid #d1d5db;border-radius:4px;font-family:monospace;font-size:11px;word-break:break-all;line-height:1.4;\">${escapeHtml(pk)}</code>\n <button id=\"mpim-copy\" style=\"padding:8px 14px;background:#2563eb;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:13px;white-space:nowrap;\">\uD83D\uDCCB \u590D\u5236</button>\n </div>\n <div id=\"mpim-status\" style=\"font-size:12px;color:#059669;min-height:16px;\"></div>\n <div style=\"margin-top:14px;padding-top:12px;border-top:1px solid #e5e7eb;font-size:11px;color:#6b7280;\">\n \uD83D\uDCA1 \u540C\u4E00\u4E2A role \u91CD\u542F\u540E publicKey \u4E0D\u4F1A\u53D8, \u597D\u53CB\u4E0D\u9700\u8981\u91CD\u65B0\u52A0\u4F60.\n </div>\n `;\n document.getElementById('mpim-copy').onclick = async () => {\n const statusEl = document.getElementById('mpim-status');\n try {\n await navigator.clipboard.writeText(pk);\n statusEl.textContent = '\u2713 \u5DF2\u590D\u5236\u5230\u526A\u8D34\u677F';\n } catch {\n const ta = document.createElement('textarea');\n ta.value = pk;\n ta.style.position = 'fixed';\n ta.style.opacity = '0';\n document.body.appendChild(ta);\n ta.select();\n try { document.execCommand('copy'); statusEl.textContent = '\u2713 \u5DF2\u590D\u5236 (fallback)'; }\n catch { statusEl.textContent = '\u2717 \u590D\u5236\u5931\u8D25, \u8BF7\u624B\u52A8\u9009\u4E2D\u590D\u5236'; }\n document.body.removeChild(ta);\n }\n };\n } catch (err) {\n const body = document.getElementById('mpim-body');\n if (body) body.innerHTML = `<div style=\"color:#b91c1c;font-size:13px;\">\u2717 \u83B7\u53D6\u5931\u8D25: ${escapeHtml(err.message || String(err))}</div>`;\n }\n });\n}\n\n// Phase 3 \u91CD\u505A: + \u6DFB\u52A0\u597D\u53CB\u6309\u94AE \u2192 \u5F39\u7A97\u8F93\u5165 publicKey + name, \u540C\u65F6 joinPeer\nconst addPeerBtn = document.getElementById('add-p2p-peer-btn');\nif (addPeerBtn) {\n addPeerBtn.addEventListener('click', async (e) => {\n e.stopPropagation();\n const name = prompt('\u7ED9\u8FD9\u4E2A P2P \u597D\u53CB\u8D77\u4E2A\u540D\u5B57 (\u5982: \u540C\u4E8B-\u5F20\u78CA)');\n if (!name) return;\n const publicKey = prompt('\u7C98\u8D34\u5BF9\u65B9\u7684 P2PDirect publicKey (64 \u5B57\u7B26 hex):\\n\\n\u83B7\u53D6\u65B9\u5F0F: \u5BF9\u65B9\u5728 http://localhost:54188/api/p2p-publickey');\n if (!publicKey) return;\n if (publicKey.length !== 64) {\n alert('publicKey \u957F\u5EA6\u4E0D\u5BF9, \u5E94\u8BE5\u662F 64 \u5B57\u7B26 hex');\n return;\n }\n try {\n // v3 \u65B0\u589E: \u6539\u7528 friend-request RPC \u2014 \u4E0D\u5149 joinPeer, \u8FD8\u53D1\u7533\u8BF7\u5230\u5BF9\u65B9\n // \u5BF9\u65B9\u4F1A\u6536\u5230 SSE friend-request \u4E8B\u4EF6, \u5F39\u4E00\u4E2A\u7533\u8BF7 modal\n const res = await fetch('/api/friend-request', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ targetPublicKey: publicKey, name, message: '\u60F3\u52A0\u4F60\u4E3A P2P \u597D\u53CB, \u5171\u4EAB channel \u534F\u4F5C' })\n });\n const data = await res.json();\n if (res.status === 502) {\n // 2026-06-10: \u533A\u5206\"\u5BF9\u65B9\u4E0D\u5728\u7EBF\"\u548C\"\u5199\u5931\u8D25\" \u2014 \u8BA9\u7528\u6237\u77E5\u9053\u662F\u5426\u9700\u8981\u91CD\u8BD5\n const reason = data.code === 'NO_CONN' ? '\u5BF9\u65B9\u672A\u5728\u7EBF\u6216 P2P \u63E1\u624B\u8D85\u65F6' : '\u5199\u5165 P2P \u901A\u9053\u5931\u8D25';\n alert(`\u597D\u53CB\u7533\u8BF7\u53D1\u9001\u5931\u8D25: ${reason}\\n\\n\u672C\u5730\u5DF2\u8BB0\u4F4F\u5BF9\u65B9 publicKey (${publicKey.substring(0,8)}...), \u7B49\u5BF9\u65B9\u4E0A\u7EBF\u540E\u53EF\u5728 P2P \u9762\u677F\u624B\u52A8\u91CD\u8BD5.`);\n await loadRemoteChannels();\n return;\n }\n if (!res.ok) throw new Error(data.error || 'connect failed');\n // \u6210\u529F \u2014 \u4F46\u4E0D\u963B\u585E\u5730\u7B49 ack (ack \u7ECF SSE 'friend-request-ack' \u63A8\u56DE, \u7531 v3GlobalEventSource \u5904\u7406)\n window.__pendingFriendRequests = window.__pendingFriendRequests || new Map();\n if (data.requestId) {\n window.__pendingFriendRequests.set(data.requestId, { name, publicKey, at: Date.now() });\n // 8s \u540E\u8FD8\u6CA1 ack \u2192 \u63D0\u793A\u7528\u6237\u5BF9\u65B9\u53EF\u80FD\u8DD1\u65E7\u7248 (\u65E0 ack \u534F\u8BAE)\n setTimeout(() => {\n if (window.__pendingFriendRequests.has(data.requestId)) {\n window.__pendingFriendRequests.delete(data.requestId);\n console.warn(`[v3-friend] \u7533\u8BF7\u8D85\u65F6\u672A\u6536\u5230 ack (requestId=${data.requestId.substring(0,8)})`);\n showSimpleToast(`\u26A0\uFE0F \u5BF9\u65B9\u672A\u786E\u8BA4\u6536\u5230 (\u53EF\u80FD\u662F\u65E7\u7248\u5BA2\u6237\u7AEF, \u7533\u8BF7\u5DF2\u53D1\u51FA\u4F46\u65E0\u6CD5\u9A8C\u8BC1)`, 'warn');\n }\n }, 8000);\n }\n alert(`\u5DF2\u53D1\u9001\u597D\u53CB\u7533\u8BF7\u7ED9 ${name} (${publicKey.substring(0, 12)}...)\\n\u5BF9\u65B9\u6536\u5230\u540E\u81EA\u5DF1\u7AEF\u5F39\u7533\u8BF7 modal, \u63A5\u53D7\u540E\u4F1A\u51FA\u73B0\u5728 P2P \u597D\u53CB\u533A.`);\n await loadRemoteChannels();\n } catch (err) {\n alert('\u7533\u8BF7\u5931\u8D25: ' + (err.message || err));\n }\n });\n}\n\n/**\n * v3 \u65B0\u589E: \u6536\u5230\u597D\u53CB\u7533\u8BF7\u65F6, \u5F39\u4E00\u4E2A modal \u8BA9\u7528\u6237\u63A5\u53D7\u6216\u62D2\u7EDD\n */\nfunction showFriendRequestModal(req) {\n // \u79FB\u9664\u5DF2\u6709 modal\n document.getElementById('friend-request-modal')?.remove();\n // 2026-06-10: \u540C Step 3 \u8FDC\u7AEF chat modal \u4E00\u6837, \u6539\u7528 class + CSS \u53D8\u91CF, \u8DDF\u672C\u5730\u98CE\u683C\u7EDF\u4E00\n const html = `\n <div id=\"friend-request-modal\" class=\"friend-req-overlay\">\n <div class=\"friend-req-shell\">\n <div class=\"friend-req-header\">\n <span style=\"font-size:20px;\">\uD83E\uDD1D</span>\n <div style=\"flex:1;min-width:0;\">\n <div class=\"friend-req-title\">\u597D\u53CB\u7533\u8BF7</div>\n <div class=\"friend-req-meta\">\u6765\u81EA ${escapeHtml(req.fromName)} (${escapeHtml(req.fromPublicKey.substring(0, 16))}\u2026)</div>\n </div>\n </div>\n <div class=\"friend-req-body\">\n <p style=\"margin:0 0 8px;\">${escapeHtml(req.message || '\u60F3\u52A0\u4F60\u4E3A P2P \u597D\u53CB')}</p>\n <p style=\"margin:0;color:var(--text-muted);font-size:11px;\">\u63A5\u53D7\u540E: \u53CC\u65B9\u4E92\u52A0\u597D\u53CB, \u5BF9\u65B9\u5206\u4EAB\u7684 channel \u4F1A\u81EA\u52A8\u51FA\u73B0\u5728 P2P \u597D\u53CB\u533A.</p>\n </div>\n <div class=\"friend-req-actions\">\n <button id=\"frm-deny\" class=\"friend-req-btn-deny\">\u62D2\u7EDD</button>\n <button id=\"frm-accept\" class=\"friend-req-btn-accept\">\u63A5\u53D7</button>\n </div>\n </div>\n </div>\n `;\n document.body.insertAdjacentHTML('beforeend', html);\n const close = () => document.getElementById('friend-request-modal')?.remove();\n document.getElementById('frm-deny').onclick = close;\n document.getElementById('frm-accept').onclick = async () => {\n close();\n try {\n const res = await fetch('/api/friend-accept', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ fromPublicKey: req.fromPublicKey, name: req.fromName })\n });\n const data = await res.json();\n if (!res.ok) throw new Error(data.error || 'accept failed');\n console.log('[v3-friend] \u63A5\u53D7\u4E86\u597D\u53CB\u7533\u8BF7:', req.fromName);\n // \u7ACB\u523B\u62C9\u4E00\u6B21 \u2014 \u5BF9\u65B9\u521A accept, ta \u7684 channel \u5217\u8868\u4F1A\u88AB\u63A8\u5230\u6211\u4EEC\u8FD9\n setTimeout(loadRemoteChannels, 1000);\n showSimpleToast(`\u2705 \u5DF2\u63A5\u53D7 ${req.fromName} \u7684\u597D\u53CB\u7533\u8BF7`);\n } catch (err) {\n console.error('[v3-friend] accept \u5931\u8D25:', err);\n alert('\u63A5\u53D7\u5931\u8D25: ' + (err.message || err));\n }\n };\n}\n\n/**\n * 2026-06-10: \u7B80\u5355\u7684\u53F3\u4E0B toast, 3s \u81EA\u52A8\u6D88\u5931. \u7528\u4E8E ack / \u63A5\u53D7\u597D\u53CB \u7B49\u975E\u963B\u585E\u53CD\u9988\n */\nfunction showSimpleToast(text, kind = 'info') {\n const containerId = 'simple-toast-container';\n let container = document.getElementById(containerId);\n if (!container) {\n container = document.createElement('div');\n container.id = containerId;\n container.style.cssText = 'position:fixed;right:16px;bottom:16px;z-index:10005;display:flex;flex-direction:column;gap:8px;max-width:320px;';\n document.body.appendChild(container);\n }\n const el = document.createElement('div');\n el.className = `simple-toast simple-toast-${kind}`;\n el.style.cssText = `background:var(--bg-sidebar);color:var(--text);border:1px solid var(--border);padding:10px 14px;border-radius:6px;font-size:12px;box-shadow:0 4px 16px rgba(0,0,0,0.3);font-family:inherit;animation:toast-in .2s ease-out;`;\n el.textContent = text;\n container.appendChild(el);\n setTimeout(() => {\n el.style.transition = 'opacity .3s, transform .3s';\n el.style.opacity = '0';\n el.style.transform = 'translateX(20px)';\n setTimeout(() => el.remove(), 320);\n }, 3000);\n}\n\n// 2026-06-10: P2P \u5168\u90E8\u5C55\u5F00/\u6298\u53E0\u5207\u6362\u6309\u94AE (\u5355\u6309\u94AE, \u6839\u636E\u5F53\u524D\u591A\u6570\u6001\u5207\u6362)\nconst p2pToggleAllBtn = document.getElementById('p2p-toggle-all-btn');\nif (p2pToggleAllBtn) {\n // \u540C\u6B65\u56FE\u6807/\u6587\u5B57: \u591A\u6570 peer \u6298\u53E0 \u2192 \u663E\u793A \"\u229E \u5C55\u5F00\"; \u591A\u6570\u5C55\u5F00 \u2192 \u663E\u793A \"\u229F \u6298\u53E0\"\n function syncToggleAllBtn() {\n const allPks = new Set([\n ...knownPeers.map(p => p.publicKey),\n ...remoteChannels.map(g => g.peerId)\n ]);\n if (allPks.size === 0) {\n p2pToggleAllBtn.textContent = '\u229E \u5C55\u5F00';\n p2pToggleAllBtn.title = '\u5207\u6362\u5168\u90E8\u5C55\u5F00/\u6298\u53E0';\n return;\n }\n let collapsedCount = 0;\n for (const pk of allPks) if (collapsedPeers.has(pk)) collapsedCount++;\n const majorityCollapsed = collapsedCount >= allPks.size / 2;\n if (majorityCollapsed) {\n p2pToggleAllBtn.textContent = '\u229E \u5C55\u5F00';\n p2pToggleAllBtn.title = '\u70B9\u51FB\u5C55\u5F00\u6240\u6709 P2P \u597D\u53CB';\n } else {\n p2pToggleAllBtn.textContent = '\u229F \u6298\u53E0';\n p2pToggleAllBtn.title = '\u70B9\u51FB\u6298\u53E0\u6240\u6709 P2P \u597D\u53CB';\n }\n }\n p2pToggleAllBtn.addEventListener('click', (e) => {\n e.stopPropagation();\n const allPks = new Set([\n ...knownPeers.map(p => p.publicKey),\n ...remoteChannels.map(g => g.peerId)\n ]);\n if (allPks.size === 0) return;\n // \u591A\u6570\u6298\u53E0 \u2192 \u5168\u5C55\u5F00; \u5426\u5219\u5168\u6298\u53E0\n let collapsedCount = 0;\n for (const pk of allPks) if (collapsedPeers.has(pk)) collapsedCount++;\n const majorityCollapsed = collapsedCount >= allPks.size / 2;\n if (majorityCollapsed) {\n expandAllPeers();\n } else {\n collapseAllPeers();\n }\n syncToggleAllBtn();\n });\n // \u66B4\u9732\u7ED9 renderRemoteChannels \u6E32\u67D3\u540E\u8C03\u7528 (\u4FDD\u6301\u56FE\u6807\u8DDF\u5B9E\u9645\u72B6\u6001\u4E00\u81F4)\n window.__syncP2PToggleAllBtn = syncToggleAllBtn;\n syncToggleAllBtn(); // \u9996\u6B21\u540C\u6B65\n}\n\n// v3 \u53CC\u5411\u5237\u65B0: \u4E3B\u52A8\u5411\u6240\u6709\u597D\u53CB\u53D1 agent.meta.list, \u62FF\u5230 ta \u4EEC\u5206\u4EAB\u7ED9\u6211\u7684 channel\nconst refreshSharedBtn = document.getElementById('refresh-shared-btn');\nif (refreshSharedBtn) {\n refreshSharedBtn.addEventListener('click', async (e) => {\n e.stopPropagation();\n const originalText = refreshSharedBtn.textContent;\n refreshSharedBtn.disabled = true;\n refreshSharedBtn.textContent = '...';\n try {\n const res = await fetch('/api/remote-channels/refresh', { method: 'POST' });\n const data = await res.json();\n if (!res.ok) throw new Error(data.error || 'refresh failed');\n // \u7B49 1.5s \u8BA9 RPC \u56DE\u590D\u56DE\u6765 (\u5411\u6240\u6709 peer \u5E7F\u64AD)\n await new Promise(r => setTimeout(r, 1500));\n await loadRemoteChannels();\n console.log(`[v3] \u53CC\u5411\u5237\u65B0: \u5411 ${data.peerCount || 0} \u4E2A\u597D\u53CB\u53D1 list \u8BF7\u6C42`);\n } catch (err) {\n alert('\u5237\u65B0\u5931\u8D25: ' + (err.message || err));\n } finally {\n refreshSharedBtn.disabled = false;\n refreshSharedBtn.textContent = originalText;\n }\n });\n}\n\n// \u542F\u52A8\u65F6\u62C9\u4E00\u6B21 + \u5B9A\u671F\u8F6E\u8BE2 (SSE \u63A5\u6536 P2P reply \u540E\u4E5F\u4F1A\u66F4\u65B0)\nloadRemoteChannels();\nsetInterval(loadRemoteChannels, 8000);\n// \u5168\u5C40 SSE \u2014 \u63A5\u6536 remote-channel-update / remote-chat-reply / friend-request\nstartV3GlobalSSE();\n\n// ============ v3: \u6298\u53E0 + \u62D6\u62FD\u5206\u9694\u7EBF ============\n\n// \u7ED9\u672C\u5730/\u8FDC\u7AEF section \u52A0 flex \u4FEE\u9970\u7C7B (CSS variable \u9A71\u52A8\u6BD4\u4F8B)\nconst localSection = document.querySelector('.sidebar-section'); // \u7B2C\u4E00\u4E2A section = \u672C\u5730 channel\nconst remoteSection = document.getElementById('remote-agents-section');\nif (localSection) localSection.classList.add('local-flex');\nif (remoteSection) remoteSection.classList.add('remote-flex');\n\n// \u6298\u53E0: \u70B9 header \u5207\u6362 collapsed \u7C7B\nconst remoteHeader = document.getElementById('remote-agents-header');\nif (remoteHeader && remoteSection) {\n remoteHeader.addEventListener('click', (e) => {\n // \u963B\u6B62\u5237\u65B0\u6309\u94AE\u7684\u4E8B\u4EF6\u5192\u6CE1\u5728 refreshRemoteBtn \u91CC\u5DF2\u5904\u7406\n remoteSection.classList.toggle('collapsed');\n });\n}\n\n// \u62D6\u62FD\u5206\u9694\u7EBF: \u9F20\u6807\u6309\u4E0B\u5F00\u59CB\u62D6, mousemove \u6539 --local-flex / --remote-flex, mouseup \u7ED3\u675F\nconst splitHandle = document.getElementById('sidebar-split-handle');\nif (splitHandle && localSection && remoteSection) {\n // \u521D\u59CB\u5316\u7B49\u5206\n const updateFlexVars = (localRatio, remoteRatio) => {\n localSection.style.setProperty('--local-flex', String(localRatio));\n remoteSection.style.setProperty('--remote-flex', String(remoteRatio));\n };\n updateFlexVars(1, 1);\n\n let isDragging = false;\n let dragStartY = 0;\n let startLocalFlex = 1;\n let startRemoteFlex = 1;\n let sidebarHeight = 0;\n\n splitHandle.addEventListener('mousedown', (e) => {\n isDragging = true;\n splitHandle.classList.add('dragging');\n dragStartY = e.clientY;\n // \u8BFB\u5F53\u524D CSS variable \u62FF\u771F\u5B9E flex \u503C\n const lf = parseFloat(getComputedStyle(localSection).getPropertyValue('--local-flex')) || 1;\n const rf = parseFloat(getComputedStyle(remoteSection).getPropertyValue('--remote-flex')) || 1;\n startLocalFlex = lf;\n startRemoteFlex = rf;\n // \u7236\u5BB9\u5668\u53EF\u7528\u9AD8\u5EA6 = sidebar-section \u603B\u548C (\u672C\u5730+\u8FDC\u7AEF+handle)\n const sidebar = document.querySelector('.sidebar');\n if (sidebar) sidebarHeight = sidebar.clientHeight;\n e.preventDefault();\n document.body.style.cursor = 'ns-resize';\n });\n\n document.addEventListener('mousemove', (e) => {\n if (!isDragging) return;\n const deltaY = e.clientY - dragStartY;\n if (sidebarHeight <= 0) return;\n // deltaY \u6B63 = \u9F20\u6807\u4E0B\u79FB = \u62C9\u5927\u672C\u5730 / \u7F29\u5C0F\u8FDC\u7AEF\n // \u8F6C\u6362: 1 \u50CF\u7D20 \u2248 sidebarHeight \u4E2D 0.005 \u7684\u6BD4\u4F8B\n const deltaRatio = deltaY / sidebarHeight * 4; // 4 \u500D\u7075\u654F\n let newLocal = Math.max(0.1, startLocalFlex + deltaRatio);\n let newRemote = Math.max(0.1, startRemoteFlex - deltaRatio);\n updateFlexVars(newLocal, newRemote);\n });\n\n document.addEventListener('mouseup', () => {\n if (!isDragging) return;\n isDragging = false;\n splitHandle.classList.remove('dragging');\n document.body.style.cursor = '';\n });\n\n // \u53CC\u51FB\u5206\u9694\u7EBF = \u91CD\u7F6E\u4E3A\u7B49\u5206\n splitHandle.addEventListener('dblclick', () => {\n updateFlexVars(1, 1);\n });\n}\n\n// 2026-06-16 \u4FEE\u590D: wallet modal \u76F8\u5173 const \u5728\u4E4B\u524D\u7684\u6B7B\u4EE3\u7801\u6E05\u7406\u4E2D\u8BEF\u5220\n// (Task \u5957\u4EF6 L2145-2419 \u7528 Python \u884C\u53F7\u5220\u9664\u65F6\u6CE2\u53CA\u4E86\u94B1\u5305\u6A21\u5757\u9876\u90E8\u58F0\u660E)\n// \u8865\u56DE: walletModal / walletBindBtn / walletGenerateBtn / walletAutoTools /\n// walletUnbindBtn / walletNewInfo / walletListEl / walletBindAddress\nconst walletModal = document.getElementById('wallet-modal');\nconst walletModalClose = document.getElementById('wallet-modal-close');\nconst walletBindAddress = document.getElementById('wallet-bind-address');\nconst walletGenerateBtn = document.getElementById('wallet-generate-btn');\nconst walletAutoTools = document.getElementById('wallet-auto-tools');\nconst walletBindBtn = document.getElementById('wallet-bind-btn');\nconst walletUnbindBtn = document.getElementById('wallet-unbind-btn');\nconst walletNewInfo = document.getElementById('wallet-new-info');\nconst walletListEl = document.getElementById('wallet-list');\n\n/** \u672C\u6B21\u4F1A\u8BDD\u751F\u6210\u7684\u79C1\u94A5/\u52A9\u8BB0\u8BCD, \u4EC5\u7528\u4E8E\u672C\u5730\u7B7E\u540D, \u6C38\u4E0D\u4E0A\u4F20 */\nlet walletModalPendingSecret = null;\nlet walletModalPendingMnemonic = null;\n\nif (walletModalClose) {\n walletModalClose.addEventListener('click', closeWalletModal);\n}\n\nif (walletBindBtn) {\n walletBindBtn.addEventListener('click', async () => {\n if (!currentChannelId) {\n alert('\u8BF7\u5148\u5728\u4FA7\u8FB9\u680F\u9009\u62E9\u4E00\u4E2A\u667A\u80FD\u4F53');\n return;\n }\n const address = (walletBindAddress.value || '').trim();\n if (!address) {\n alert('\u8BF7\u8F93\u5165\u94B1\u5305\u5730\u5740\u6216\u70B9\u51FB\u300C\u751F\u6210\u300D');\n return;\n }\n const ch = channels.find(c => c.id === currentChannelId);\n const did = ch?.did || '';\n if (!did || did === 'undefined' || did === 'null') {\n alert('\u5F53\u524D\u667A\u80FD\u4F53\u8FD8\u6CA1\u6709\u751F\u6210 DID, \u8BF7\u7A0D\u7B49\u51E0\u79D2\u540E\u91CD\u8BD5');\n return;\n }\n\n // \u670D\u52A1\u7AEF\u4F1A\u7528 recoverMessage \u6821\u9A8C\u7B7E\u540D, \u56E0\u6B64\u5FC5\u987B\u7528\u672C\u4F1A\u8BDD\u751F\u6210\u7684\u79C1\u94A5\u7B7E\u540D\n // (\u5DF2\u7ED1\u8FC7\u7684\u94B1\u5305\u91CD\u65B0\u7B7E\u540D\u4E5F\u4F1A\u8FC7, \u56E0\u4E3A challenge \u91CC\u6709 channelId + DID)\n if (!walletModalPendingSecret) {\n alert('\u8BF7\u5148\u5728\u300C\u94B1\u5305\u7BA1\u7406\u300D\u9762\u677F\u70B9\u51FB\u300C\u751F\u6210\u300D\u6216\u5BFC\u5165\u79C1\u94A5, \u4E34\u65F6\u79C1\u94A5\u4EC5\u5728\u672C\u4F1A\u8BDD\u4FDD\u7559');\n return;\n }\n let challenge;\n try {\n challenge = await signDIDChallengeAsync(walletModalPendingSecret, did, currentChannelId);\n } catch (err) {\n alert('\u7B7E\u540D\u5931\u8D25: ' + err.message);\n return;\n }\n if (challenge.address.toLowerCase() !== address.toLowerCase()) {\n alert(`\u7B7E\u540D\u5730\u5740 ${challenge.address} \u4E0E\u8F93\u5165\u5730\u5740 ${address} \u4E0D\u4E00\u81F4, \u62D2\u7EDD\u7ED1\u5B9A`);\n return;\n }\n\n try {\n const res = await fetch(`/channels/${currentChannelId}/bind-wallet`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n walletAddress: challenge.address,\n signature: challenge.signature,\n message: challenge.message,\n did: challenge.did,\n autoInvokeTools: !!walletAutoTools.checked\n })\n });\n if (!res.ok) {\n const err = await res.json().catch(() => ({}));\n throw new Error(err.error || 'bind failed');\n }\n const updated = await res.json();\n const idx = channels.findIndex(c => c.id === currentChannelId);\n if (idx >= 0) channels[idx] = updated;\n renderChannels();\n renderWalletList();\n walletModalPendingSecret = null;\n walletModalPendingMnemonic = null;\n walletNewInfo.style.display = 'block';\n walletNewInfo.innerHTML =\n '\u2705 \u7ED1\u5B9A\u6210\u529F<br>' +\n '<strong>\u5730\u5740:</strong> <code>' + escapeHtml(updated.walletAddress) + '</code><br>' +\n '<strong>\u7B7E\u540D DID:</strong> <code>' + escapeHtml(did) + '</code><br>' +\n '<small style=\"color:#9c9;\">\u670D\u52A1\u7AEF\u5DF2\u7528 recoverMessage \u6821\u9A8C\u7B7E\u540D, \u8BC1\u660E\u4F60\u6301\u6709\u8BE5\u94B1\u5305\u79C1\u94A5\u3002</small>';\n } catch (err) {\n alert('\u7ED1\u5B9A\u5931\u8D25: ' + err.message);\n }\n });\n}\n\nif (walletUnbindBtn) {\n walletUnbindBtn.addEventListener('click', async () => {\n if (!currentChannelId) {\n alert('\u8BF7\u5148\u9009\u62E9\u4E00\u4E2A\u667A\u80FD\u4F53');\n return;\n }\n if (!confirm('\u89E3\u7ED1\u5F53\u524D\u667A\u80FD\u4F53\u7684\u94B1\u5305?')) return;\n try {\n const res = await fetch(`/channels/${currentChannelId}`, {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ walletAddress: null })\n });\n if (!res.ok) throw new Error('unbind failed');\n const updated = await res.json();\n const idx = channels.findIndex(c => c.id === currentChannelId);\n if (idx >= 0) channels[idx] = updated;\n walletBindAddress.value = '';\n renderChannels();\n renderWalletList();\n } catch (err) {\n alert('\u89E3\u7ED1\u5931\u8D25: ' + err.message);\n }\n });\n}\n\n/** 2026-06-16 \u4FEE\u590D: openWalletModal \u4E4B\u524D\u5728 git \u5386\u53F2\u91CC\u88AB\u8BEF\u5220 (L2018 \u5F15\u7528\u4F46\u672A\u5B9A\u4E49)\n * - \u8868\u73B0: \u70B9\u51FB header \u94B1\u5305\u6309\u94AE \u2192 ReferenceError \u2192 init() \u4E4B\u540E\u7684\u4EE3\u7801\u4E0D\u6267\u884C\n * - \u5F71\u54CD: sidebar channel / session \u5168\u90E8\u4E0D\u6E32\u67D3, \u7528\u6237\u770B\u5230\"\u6309\u94AE\u6CA1\u53CD\u5E94 / channel \u6D88\u5931\"\n * \u4FEE: \u8865\u56DE openWalletModal + closeWalletModal */\nfunction openWalletModal() {\n if (walletModal) {\n walletModal.classList.add('active');\n if (currentChannelId && walletBindAddress && channels.find(c => c.id === currentChannelId)) {\n const ch = channels.find(c => c.id === currentChannelId);\n walletBindAddress.value = ch?.walletAddress || '';\n }\n renderWalletList();\n }\n}\nfunction closeWalletModal() {\n if (walletModal) walletModal.classList.remove('active');\n}\n\n/** \u6E32\u67D3\"\u6240\u6709\u5DF2\u7ED1\u5B9A\u94B1\u5305\"\u5217\u8868 */\nfunction renderWalletList() {\n if (!walletListEl) return;\n const bound = channels.filter(c => c.walletAddress);\n if (bound.length === 0) {\n walletListEl.innerHTML = '<div class=\"wallet-empty\">\u6682\u672A\u7ED1\u5B9A\u94B1\u5305</div>';\n return;\n }\n // \u7528 DocumentFragment \u907F\u514D\u591A\u6B21 reflow\n const frag = document.createDocumentFragment();\n bound.forEach(ch => {\n const isActive = ch.id === currentChannelId;\n const chain = detectChain(ch.walletAddress);\n const row = document.createElement('div');\n row.className = 'wallet-row' + (isActive ? ' is-active' : '');\n row.innerHTML = `\n <span class=\"wallet-chain\">${escapeHtml(chain)}</span>\n <div class=\"wallet-info\">\n <span class=\"wallet-agent\" title=\"${escapeHtml(ch.name || '')}\">${escapeHtml(ch.name || '(\u672A\u547D\u540D)')}</span>\n <span class=\"wallet-address\" title=\"${escapeHtml(ch.walletAddress)}\">${escapeHtml(ch.walletAddress)}</span>\n </div>\n <div class=\"wallet-actions\">\n <button class=\"wallet-mini-btn\" data-action=\"copy\" data-addr=\"${escapeHtml(ch.walletAddress)}\" title=\"\u590D\u5236\u5730\u5740\">\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\"></rect>\n <path d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"></path>\n </svg>\n </button>\n <button class=\"wallet-mini-btn\" data-action=\"goto\" data-id=\"${escapeHtml(ch.id)}\" title=\"\u5207\u6362\u5230\u8BE5\u667A\u80FD\u4F53\">\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M5 12h14M12 5l7 7-7 7\"></path>\n </svg>\n </button>\n <button class=\"wallet-mini-btn\" data-action=\"unbind\" data-id=\"${escapeHtml(ch.id)}\" title=\"\u89E3\u7ED1\">\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>\n </svg>\n </button>\n </div>\n `;\n frag.appendChild(row);\n });\n walletListEl.innerHTML = '';\n walletListEl.appendChild(frag);\n\n // \u4E8B\u4EF6\u59D4\u6258: \u4E00\u6B21\u7ED1\u5B9A\u5904\u7406\u4E09\u4E2A\u52A8\u4F5C\n walletListEl.onclick = async (ev) => {\n const btn = ev.target.closest('button[data-action]');\n if (!btn) return;\n const action = btn.dataset.action;\n if (action === 'copy') {\n try {\n await navigator.clipboard.writeText(btn.dataset.addr);\n btn.style.background = 'var(--accent)';\n btn.style.color = 'var(--bg)';\n setTimeout(() => { btn.style.background = ''; btn.style.color = ''; }, 800);\n } catch {}\n } else if (action === 'goto') {\n closeWalletModal();\n selectChannel(btn.dataset.id);\n } else if (action === 'unbind') {\n if (!confirm('\u89E3\u7ED1\u8BE5\u667A\u80FD\u4F53\u7684\u94B1\u5305?')) return;\n try {\n const res = await fetch(`/channels/${btn.dataset.id}`, {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ walletAddress: null })\n });\n if (!res.ok) throw new Error('unbind failed');\n const updated = await res.json();\n const idx = channels.findIndex(c => c.id === btn.dataset.id);\n if (idx >= 0) channels[idx] = updated;\n renderChannels();\n renderWalletList();\n if (btn.dataset.id === currentChannelId) walletBindAddress.value = '';\n } catch (err) {\n alert('\u89E3\u7ED1\u5931\u8D25: ' + err.message);\n }\n }\n };\n}\n\nfunction detectChain(addr) {\n if (!addr) return '?';\n if (/^0x[0-9a-fA-F]{40}$/.test(addr)) return 'EVM';\n if (/^0x[0-9a-fA-F]{64}$/.test(addr)) return 'SUI';\n if (/^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(addr)) return 'SOL';\n return '?';\n}\n\n// \u542F\u52A8\u5E94\u7528\ninit();\n\n// =====================================================\n// \u667A\u80FD\u4F53\u76EE\u5F55\uFF1A\u94B1\u5305\u6CE8\u518C + \u81EA\u52A8\u5DE5\u5177\u8C03\u7528 (2026-06-11: catalog-add-btn \u5DF2\u5220\u9664)\n// =====================================================\nconst agentAddModal = document.getElementById('agent-add-modal');\nconst agentAddTitle = document.getElementById('agent-add-title');\nconst agentAddModalClose = document.getElementById('agent-add-modal-close');\nconst agentAddName = document.getElementById('agent-add-name');\nconst agentAddWallet = document.getElementById('agent-add-wallet');\nconst agentAddAutoTools = document.getElementById('agent-add-auto-tools');\nconst agentAddConfirmBtn = document.getElementById('agent-add-confirm-btn');\nconst agentAddCancelBtn = document.getElementById('agent-add-cancel-btn');\nconst agentAddWalletInfo = document.getElementById('agent-add-wallet-info');\nconst agentGenerateWalletBtn = document.getElementById('agent-generate-wallet-btn');\n\n/** \u5BA2\u6237\u7AEF\u53EA\u4E3A\u63D0\u793A, \u4E0D\u5411\u670D\u52A1\u7AEF\u53D1\u9001\u79C1\u94A5 */\nlet pendingWalletSecret = null; // \u672C\u4F1A\u8BDD\u5F85\u7ED1\u5B9A\u7684\u79C1\u94A5, \u4EC5\u6D4F\u89C8\u5668\u5185\u5B58\nlet pendingWalletMnemonic = null; // \u672C\u4F1A\u8BDD\u5F85\u7ED1\u5B9A\u7684\u52A9\u8BB0\u8BCD, \u4EC5\u6D4F\u89C8\u5668\u5185\u5B58\n\nfunction openAgentAddModal(existingChannel) {\n if (!agentAddModal) return;\n if (existingChannel) {\n agentAddTitle.textContent = '\u914D\u7F6E\u667A\u80FD\u4F53\uFF1A' + existingChannel.name;\n agentAddName.value = existingChannel.name || '';\n agentAddName.readOnly = false; // v3: \u540D\u5B57\u6539\u6210\u53EF\u7F16\u8F91 (PATCH \u652F\u6301\u66F4\u65B0 name)\n agentAddName.placeholder = '\u8F93\u5165\u65B0\u540D\u79F0';\n agentAddWallet.value = existingChannel.walletAddress || '';\n agentAddAutoTools.checked = !!existingChannel.autoInvokeTools;\n agentAddConfirmBtn.dataset.mode = 'update';\n agentAddConfirmBtn.dataset.channelId = existingChannel.id;\n agentAddConfirmBtn.dataset.originalName = existingChannel.name || '';\n } else {\n agentAddTitle.textContent = '\u6DFB\u52A0\u667A\u80FD\u4F53';\n agentAddName.value = '';\n agentAddName.readOnly = false;\n agentAddName.placeholder = '\u4F8B\u5982: \u4EA4\u6613\u52A9\u624B';\n agentAddWallet.value = '';\n agentAddAutoTools.checked = true;\n agentAddConfirmBtn.dataset.mode = 'create';\n delete agentAddConfirmBtn.dataset.channelId;\n delete agentAddConfirmBtn.dataset.originalName;\n }\n agentAddWalletInfo.style.display = 'none';\n agentAddWalletInfo.innerHTML = '';\n pendingWalletSecret = null;\n agentAddModal.classList.add('active');\n}\n\nfunction closeAgentAddModal() {\n if (!agentAddModal) return;\n agentAddModal.classList.remove('active');\n pendingWalletSecret = null;\n}\n\n/** \u751F\u6210\u771F\u5B9E EVM \u94B1\u5305: BIP-39 \u52A9\u8BB0\u8BCD + secp256k1 \u79C1\u94A5 + EIP-55 \u6821\u9A8C\u548C\u5730\u5740\n * \u901A\u8FC7 window.WalletViem (\u6765\u81EA components/wallet-viem.mjs, viem \u9A71\u52A8) \u8C03\u7528.\n * \u5931\u8D25\u65F6\u964D\u7EA7\u5230\u65E7\u7684\u6F14\u793A\u6A21\u5F0F, \u4F46 UI \u4F1A\u63D0\u793A\u7528\u6237.\n */\nasync function generateRealWalletAsync() {\n if (!window.WalletViem) {\n throw new Error('\u94B1\u5305\u6A21\u5757\u5C1A\u672A\u52A0\u8F7D, \u8BF7\u7A0D\u540E\u91CD\u8BD5');\n }\n return window.WalletViem.generateRealWallet();\n}\n\nasync function importRealWalletByPrivateKey(privateKeyHex) {\n if (!window.WalletViem) {\n throw new Error('\u94B1\u5305\u6A21\u5757\u5C1A\u672A\u52A0\u8F7D, \u8BF7\u7A0D\u540E\u91CD\u8BD5');\n }\n return window.WalletViem.importEVMWallet(privateKeyHex);\n}\n\nasync function signDIDChallengeAsync(privateKeyHex, did, channelId) {\n if (!window.WalletViem) {\n throw new Error('\u94B1\u5305\u6A21\u5757\u5C1A\u672A\u52A0\u8F7D, \u8BF7\u7A0D\u540E\u91CD\u8BD5');\n }\n return window.WalletViem.signDIDChallenge(privateKeyHex, did, channelId);\n}\n\nfunction formatWalletInfoHtml({ address, privateKey, mnemonic }) {\n const parts = [\n '\u2713 \u5DF2\u751F\u6210\u771F\u5B9E EVM \u94B1\u5305 (BIP-39 + secp256k1 + EIP-55)',\n '<strong>\u5730\u5740:</strong> <code>' + escapeHtml(address) + '</code>',\n ];\n if (mnemonic) {\n parts.push(\n '<strong>\u52A9\u8BB0\u8BCD (12 \u8BCD, \u8BF7\u6284\u5199\u4FDD\u5B58):</strong>',\n '<code style=\"color:#fc6;word-break:break-all;\">' + escapeHtml(mnemonic) + '</code>'\n );\n }\n parts.push(\n '<strong>\u79C1\u94A5 (0x + 32 \u5B57\u8282):</strong>',\n '<code style=\"color:#f88;word-break:break-all;\">' + escapeHtml(privateKey) + '</code>',\n '<small style=\"color:#f88;\">\u26A0 \u52A9\u8BB0\u8BCD + \u79C1\u94A5\u5747\u4EC5\u5728\u672C\u6D4F\u89C8\u5668\u5185\u5B58, \u5173\u95ED\u9875\u9762\u540E\u65E0\u6CD5\u627E\u56DE\u3002</small>',\n '<small style=\"color:#999;\">\u7B7E\u540D\u7ED1\u5B9A\u5230 channel DID (EIP-191 personal_sign) \u4F1A\u53D1\u9001\u5230\u670D\u52A1\u7AEF, \u7528\u4E8E\u8BC1\u660E\u94B1\u5305\u6240\u6709\u6743\u3002</small>'\n );\n return parts.join('<br>');\n}\n\nif (agentGenerateWalletBtn) {\n agentGenerateWalletBtn.addEventListener('click', async () => {\n agentAddWalletInfo.style.display = 'block';\n agentAddWalletInfo.innerHTML = '\u23F3 \u6B63\u5728\u751F\u6210\u771F\u5B9E EVM \u94B1\u5305...';\n try {\n const wallet = await generateRealWalletAsync();\n agentAddWallet.value = wallet.address;\n pendingWalletSecret = wallet.privateKey;\n pendingWalletMnemonic = wallet.mnemonic;\n agentAddWalletInfo.innerHTML = formatWalletInfoHtml(wallet);\n } catch (err) {\n agentAddWalletInfo.innerHTML = '\u2717 \u751F\u6210\u94B1\u5305\u5931\u8D25: ' + escapeHtml(err.message);\n }\n });\n}\n\nif (agentAddModalClose) agentAddModalClose.addEventListener('click', closeAgentAddModal);\nif (agentAddCancelBtn) agentAddCancelBtn.addEventListener('click', closeAgentAddModal);\n\nif (agentAddConfirmBtn) {\n agentAddConfirmBtn.addEventListener('click', async () => {\n const mode = agentAddConfirmBtn.dataset.mode || 'create';\n const name = (agentAddName.value || '').trim();\n if (!name && mode === 'create') {\n alert('\u8BF7\u8F93\u5165\u667A\u80FD\u4F53\u540D\u79F0');\n return;\n }\n const walletAddress = (agentAddWallet.value || '').trim();\n const autoInvokeTools = !!agentAddAutoTools.checked;\n\n try {\n if (mode === 'create') {\n const res = await fetch('/channels', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n name,\n agentId: currentAgentId,\n walletAddress: walletAddress || undefined,\n autoInvokeTools\n })\n });\n if (!res.ok) throw new Error('create failed');\n const channel = await res.json();\n channels.push(channel);\n renderChannels();\n selectChannel(channel.id);\n } else {\n // update\n const channelId = agentAddConfirmBtn.dataset.channelId;\n const originalName = agentAddConfirmBtn.dataset.originalName || '';\n // v3 \u65B0\u589E: \u540D\u5B57\u6539\u4E86\u624D\u53D1 (\u6CA1\u6539\u5C31\u4E0D\u53D1, \u4FDD\u6301\u539F\u72B6)\n const body = { walletAddress: walletAddress || null, autoInvokeTools };\n if (name && name !== originalName) {\n body.name = name;\n }\n const res = await fetch(`/channels/${channelId}`, {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body)\n });\n if (!res.ok) throw new Error('update failed');\n const updated = await res.json();\n const idx = channels.findIndex(c => c.id === channelId);\n if (idx >= 0) channels[idx] = updated;\n renderChannels();\n }\n closeAgentAddModal();\n } catch (err) {\n console.error('Failed to save agent:', err);\n alert('\u4FDD\u5B58\u5931\u8D25: ' + err.message);\n }\n });\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["label", "m", "currentChannelId", "messagesContainers", "messagesEl", "marked", "input", "input", "addMessage", "escapeHtml", "channels", "walletBadge", "container", "handleStreamTokenEvent", "finalizeTimelineAsMessage", "handleStepEvent", "getMessagesContainerForCurrent", "res", "data", "resolve", "sendBtn", "syncToggleAllBtn", "sidebar", "escapeHtml"]
|
|
7
|
-
}
|