@bolloon/bolloon-agent 0.2.11 → 0.2.13
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 +495 -467
- package/bin/bolloon-cli.cjs +183 -183
- package/bin/bolloon-daemon.sh +207 -0
- package/bin/bolloon.cjs +0 -0
- package/bin/ipfs +0 -0
- package/dist/agents/agent-manifest-protocol.js +52 -0
- package/dist/agents/chat-segmenter.js +298 -0
- package/dist/agents/constraint-layer.js +19 -19
- package/dist/agents/judgment-protocol.js +479 -0
- package/dist/agents/parse-tool-call.js +304 -0
- package/dist/agents/peer-manifest-loader.js +210 -0
- package/dist/agents/pi-sdk-session-factory.js +110 -0
- package/dist/agents/pi-sdk-session-manager.js +297 -0
- package/dist/agents/pi-sdk-tools.js +1288 -0
- package/dist/agents/pi-sdk-types.js +13 -0
- package/dist/agents/pi-sdk.js +553 -1306
- package/dist/agents/react-loop.js +120 -0
- package/dist/agents/session-store.js +171 -0
- package/dist/agents/shell-guard.js +2 -2
- package/dist/agents/tool-registry.js +136 -0
- package/dist/agents/workflow-pivot-loop.js +200 -22
- package/dist/bollharness/src/scripts/context_router.js +7 -0
- 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 +26 -13
- package/dist/bollharness-integration/integration.js +71 -0
- package/dist/bollharness-integration/llm-judgment-engine.js +134 -134
- package/dist/bollharness-integration/skill-adapter.js +57 -127
- package/dist/bootstrap/chat-archiver.js +276 -0
- package/dist/bootstrap/context-collector.js +6 -3
- package/dist/bootstrap/event-log.js +160 -0
- package/dist/bootstrap/lifecycle-hooks.js +15 -1
- package/dist/bootstrap/memory-compressor.js +170 -0
- package/dist/bootstrap/persona-loader.js +94 -0
- package/dist/bootstrap/project-state.js +159 -0
- package/dist/bootstrap/remote-mirror.js +97 -0
- package/dist/bootstrap/session-window.js +121 -0
- package/dist/bootstrap/vector-index.js +248 -0
- package/dist/cli/loading-tui.js +40 -0
- package/dist/cli-entry.js +41 -37
- package/dist/context-compaction/auto-compact.js +7 -7
- package/dist/electron/config.js +21 -0
- package/dist/electron/config.js.map +1 -0
- package/dist/electron/dialogs.js +108 -0
- package/dist/electron/dialogs.js.map +1 -0
- package/dist/electron/first-run.js +170 -0
- package/dist/electron/first-run.js.map +1 -0
- package/dist/electron/ipc.js +20 -0
- package/dist/electron/ipc.js.map +1 -0
- package/dist/electron/logger.js +114 -0
- package/dist/electron/logger.js.map +1 -0
- package/dist/electron/main.js +63 -0
- package/dist/electron/main.js.map +1 -0
- package/dist/electron/menu.js +145 -0
- package/dist/electron/menu.js.map +1 -0
- package/dist/electron/paths.js +75 -0
- package/dist/electron/paths.js.map +1 -0
- package/dist/electron/server.js +119 -0
- package/dist/electron/server.js.map +1 -0
- package/dist/electron/tray.js +111 -0
- package/dist/electron/tray.js.map +1 -0
- package/dist/electron/window.js +111 -0
- package/dist/electron/window.js.map +1 -0
- package/dist/electron-preload.js +26 -6
- package/dist/electron-preload.js.map +1 -1
- package/dist/electron.js +6 -168
- package/dist/electron.js.map +1 -1
- package/dist/index.js +323 -266
- package/dist/llm/config-store.js +24 -8
- package/dist/llm/llm-judgment-client.js +102 -102
- package/dist/llm/pi-ai.js +137 -85
- 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-visitor.md +19 -19
- 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/hibs_reminders.md +15 -15
- package/dist/llm/system-prompt/layers/core/identity.md +37 -37
- 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/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/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/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/network/local-inbox-bus.js +73 -0
- package/dist/network/p2p-outbox.js +161 -0
- package/dist/network/peer-fs.js +420 -0
- package/dist/network/peer-resource-bridge.js +216 -0
- package/dist/pi-ecosystem-judgment/causal-judge.js +51 -51
- package/dist/pi-ecosystem-judgment/cleanup.js +126 -0
- package/dist/pi-ecosystem-judgment/distill-prompt.js +42 -42
- package/dist/pi-ecosystem-judgment/distillation.js +14 -14
- package/dist/pi-ecosystem-judgment/human-value-store.js +45 -11
- 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 +11 -0
- package/dist/web/api-config.html +520 -520
- package/dist/web/client-loop-status.js +220 -0
- package/dist/web/client.js +1677 -244
- package/dist/web/client.js.map +7 -0
- package/dist/web/components/wallet-viem.mjs +118 -118
- package/dist/web/index.html +375 -374
- package/dist/web/input-validator.js +103 -0
- package/dist/web/manifest.json +20 -20
- package/dist/web/routes-judgments.js +738 -0
- package/dist/web/routes-llm-config.js +279 -0
- package/dist/web/routes-tasks.js +222 -0
- package/dist/web/server-sse.js +121 -0
- package/dist/web/server-storage.js +154 -0
- package/dist/web/server-types.js +15 -0
- package/dist/web/server-v3-p2p.js +224 -0
- package/dist/web/server.js +1070 -1307
- package/dist/web/style.css +4742 -4737
- package/dist/web/util/safe-name.js +32 -0
- package/package.json +162 -160
- package/scripts/build-cli.js +215 -215
- package/scripts/build-web.ts +130 -125
- package/scripts/postinstall.js +152 -152
- package/bin/bolloon.js +0 -157
- package/dist/constraint-runtime/src/_archive_helper.js +0 -9
- package/dist/constraint-runtime/src/agent/coordinator.js +0 -48
- package/dist/constraint-runtime/src/agent/index.js +0 -1
- package/dist/constraint-runtime/src/assistant/index.js +0 -12
- package/dist/constraint-runtime/src/bootstrap/index.js +0 -12
- package/dist/constraint-runtime/src/bootstrap_graph.js +0 -13
- package/dist/constraint-runtime/src/bridge/index.js +0 -12
- package/dist/constraint-runtime/src/buddy/index.js +0 -12
- package/dist/constraint-runtime/src/cli/index.js +0 -12
- package/dist/constraint-runtime/src/command_graph.js +0 -10
- package/dist/constraint-runtime/src/commands.js +0 -60
- package/dist/constraint-runtime/src/components/index.js +0 -12
- package/dist/constraint-runtime/src/constants/index.js +0 -12
- package/dist/constraint-runtime/src/constraint/budget.js +0 -22
- package/dist/constraint-runtime/src/constraint/index.js +0 -2
- package/dist/constraint-runtime/src/constraint/permission.js +0 -20
- package/dist/constraint-runtime/src/context.js +0 -30
- package/dist/constraint-runtime/src/coordinator/index.js +0 -12
- package/dist/constraint-runtime/src/cost_hook.js +0 -4
- package/dist/constraint-runtime/src/cost_tracker.js +0 -8
- package/dist/constraint-runtime/src/deferred_init.js +0 -10
- package/dist/constraint-runtime/src/direct_modes.js +0 -6
- package/dist/constraint-runtime/src/dynamic-tool-loader.js +0 -85
- package/dist/constraint-runtime/src/entrypoints/index.js +0 -12
- package/dist/constraint-runtime/src/execution_registry.js +0 -44
- package/dist/constraint-runtime/src/history.js +0 -9
- package/dist/constraint-runtime/src/hooks/index.js +0 -12
- package/dist/constraint-runtime/src/index.js +0 -26
- package/dist/constraint-runtime/src/ink.js +0 -4
- package/dist/constraint-runtime/src/keybindings/index.js +0 -12
- package/dist/constraint-runtime/src/memdir/index.js +0 -12
- package/dist/constraint-runtime/src/migrations/index.js +0 -12
- package/dist/constraint-runtime/src/models.js +0 -1
- package/dist/constraint-runtime/src/moreright/index.js +0 -12
- package/dist/constraint-runtime/src/native_ts/index.js +0 -12
- package/dist/constraint-runtime/src/output_styles/index.js +0 -12
- package/dist/constraint-runtime/src/parity_audit.js +0 -12
- package/dist/constraint-runtime/src/plugins/index.js +0 -12
- package/dist/constraint-runtime/src/port_manifest.js +0 -11
- package/dist/constraint-runtime/src/prefetch.js +0 -9
- package/dist/constraint-runtime/src/query.js +0 -1
- package/dist/constraint-runtime/src/remote/index.js +0 -12
- package/dist/constraint-runtime/src/remote_runtime.js +0 -9
- package/dist/constraint-runtime/src/runtime/index.js +0 -1
- package/dist/constraint-runtime/src/runtime/session.js +0 -35
- package/dist/constraint-runtime/src/schemas/index.js +0 -12
- package/dist/constraint-runtime/src/screens/index.js +0 -12
- package/dist/constraint-runtime/src/server/index.js +0 -12
- package/dist/constraint-runtime/src/services/index.js +0 -12
- package/dist/constraint-runtime/src/session_store.js +0 -22
- package/dist/constraint-runtime/src/setup.js +0 -30
- package/dist/constraint-runtime/src/skills/index.js +0 -1
- package/dist/constraint-runtime/src/skills/skill-registry.js +0 -28
- package/dist/constraint-runtime/src/state/index.js +0 -12
- package/dist/constraint-runtime/src/system_init.js +0 -20
- package/dist/constraint-runtime/src/thinking/engine.js +0 -42
- package/dist/constraint-runtime/src/thinking/index.js +0 -1
- package/dist/constraint-runtime/src/tool_pool.js +0 -8
- package/dist/constraint-runtime/src/tools/OpenCLI/execAdapter.js +0 -7
- package/dist/constraint-runtime/src/tools/OpenCLI/listAdapters.js +0 -7
- package/dist/constraint-runtime/src/tools/OpenCLI/runCommand.js +0 -7
- package/dist/constraint-runtime/src/tools/SafeSDK/confirmTransaction.js +0 -6
- package/dist/constraint-runtime/src/tools/SafeSDK/createTransaction.js +0 -8
- package/dist/constraint-runtime/src/tools/SafeSDK/executeTransaction.js +0 -6
- package/dist/constraint-runtime/src/tools/SafeSDK/getBalance.js +0 -6
- package/dist/constraint-runtime/src/tools/SafeSDK/getPendingTransactions.js +0 -6
- package/dist/constraint-runtime/src/tools/SafeSDK/proposeTransaction.js +0 -6
- package/dist/constraint-runtime/src/tools.js +0 -80
- package/dist/constraint-runtime/src/transcript.js +0 -19
- package/dist/constraint-runtime/src/types/index.js +0 -12
- package/dist/constraint-runtime/src/upstream_proxy/index.js +0 -12
- package/dist/constraint-runtime/src/utils/index.js +0 -12
- package/dist/constraint-runtime/src/vim/index.js +0 -12
- package/dist/constraint-runtime/src/voice/index.js +0 -12
- package/dist/constraint-runtime/tests/agent.test.js +0 -16
- package/dist/constraint-runtime/tests/constraint.test.js +0 -41
- package/dist/constraint-runtime/tests/skill.test.js +0 -19
- package/dist/constraint-runtime/tests/thinking.test.js +0 -22
- package/dist/pi-ecosystem-colony/index.js +0 -365
- package/dist/social/ant-colony/AdaptiveHeartbeat.js +0 -101
- package/dist/social/ant-colony/PheromoneEngine.js +0 -227
- package/dist/social/ant-colony/index.js +0 -6
- package/dist/social/ant-colony/types.js +0 -24
- package/dist/test/ai-judgment-test.js +0 -80
- package/dist/test/bollharness-integration.test.js +0 -318
- package/dist/test/channel-agent-multi-dialogue.js +0 -205
- package/dist/test/channel-heartbeat-agent-test.js +0 -201
- package/dist/test/constraint-layer.test.js +0 -164
- package/dist/test/diap-identity-test.js +0 -172
- package/dist/test/diap-quick-test.js +0 -62
- package/dist/test/global-shared-context.test.js +0 -315
- package/dist/test/harness-judgment-injection.test.js +0 -246
- package/dist/test/harness-workflow-integrator-test.js +0 -228
- package/dist/test/human-value-store.test.js +0 -243
- package/dist/test/hybrid-integration-test.js +0 -118
- package/dist/test/hybrid-messenger-verify.js +0 -55
- package/dist/test/iroh-bistream-debug.js +0 -38
- package/dist/test/iroh-communication.test.js +0 -66
- package/dist/test/iroh-debug-test.js +0 -57
- package/dist/test/iroh-diap-test.js +0 -71
- package/dist/test/iroh-direct-connect.js +0 -55
- package/dist/test/iroh-e2e-fixed.js +0 -89
- package/dist/test/iroh-e2e-same-process.js +0 -63
- package/dist/test/iroh-e2e.js +0 -66
- package/dist/test/iroh-final-e2e.js +0 -72
- package/dist/test/iroh-relay-test.js +0 -37
- package/dist/test/iroh-simple-test.js +0 -41
- package/dist/test/iroh-transport-verify.js +0 -54
- package/dist/test/iroh-transport.test.js +0 -37
- package/dist/test/iroh-two-nodes.js +0 -70
- package/dist/test/iroh-verify.js +0 -44
- package/dist/test/judgment-decision.test.js +0 -219
- package/dist/test/llm-judgment-integration.test.js +0 -220
- package/dist/test/p2p-agent-complex-dialogue.js +0 -385
- package/dist/test/p2p-agent-dialogue.js +0 -341
- package/dist/test/p2p-agent-full-bidirectional.js +0 -510
- package/dist/test/p2p-agent-harness-flow.js +0 -437
- package/dist/test/p2p-agent-harness-single.js +0 -143
- package/dist/test/p2p-ai-dialogue-test.js +0 -318
- package/dist/test/p2p-cid-connect-test.js +0 -195
- package/dist/test/p2p-connect-receiver.js +0 -69
- package/dist/test/p2p-doc-transfer.js +0 -110
- package/dist/test/p2p-identity-page-test.js +0 -77
- package/dist/test/p2p-iroh-test.js +0 -171
- package/dist/test/p2p-minimal-test.js +0 -241
- package/dist/test/p2p-node-1.js +0 -148
- package/dist/test/p2p-node-2.js +0 -148
- package/dist/test/p2p-server.js +0 -281
- package/dist/test/p2p-two-nodes-test.js +0 -438
- package/dist/test/pi-sdk.test.js +0 -44
- package/dist/test/set-persona.js +0 -40
- package/dist/test/simple.test.js +0 -9
- package/dist/test/storage-integration.test.js +0 -150
- package/dist/test/subagent-manager.test.js +0 -276
- package/dist/test/test-gate-flow.test.js +0 -81
- package/dist/test/workflow-engine.test.js +0 -87
- package/dist/test/workflow-pivot-loop.test.js +0 -246
package/dist/web/client.js
CHANGED
|
@@ -1,6 +1,1362 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
(() => {
|
|
3
|
-
var
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
8
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
9
|
+
}) : x)(function(x) {
|
|
10
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
11
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
12
|
+
});
|
|
13
|
+
var __esm = (fn, res) => function __init() {
|
|
14
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
15
|
+
};
|
|
16
|
+
var __export = (target, all) => {
|
|
17
|
+
for (var name in all)
|
|
18
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
19
|
+
};
|
|
20
|
+
var __copyProps = (to, from, except, desc) => {
|
|
21
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
22
|
+
for (let key of __getOwnPropNames(from))
|
|
23
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
24
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
25
|
+
}
|
|
26
|
+
return to;
|
|
27
|
+
};
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/web/ui/step-timeline.ts
|
|
31
|
+
function readExpanded(channelId) {
|
|
32
|
+
if (!channelId) return false;
|
|
33
|
+
try {
|
|
34
|
+
return localStorage.getItem(STORAGE_PREFIX + channelId) === "1";
|
|
35
|
+
} catch {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function writeExpanded(channelId, expanded) {
|
|
40
|
+
if (!channelId) return;
|
|
41
|
+
try {
|
|
42
|
+
localStorage.setItem(STORAGE_PREFIX + channelId, expanded ? "1" : "0");
|
|
43
|
+
} catch {
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function computeTitle(steps) {
|
|
47
|
+
if (steps.length === 0) return "\u6267\u884C\u6B65\u9AA4";
|
|
48
|
+
const activeIdx = steps.findIndex((s) => s.status === "active");
|
|
49
|
+
if (activeIdx >= 0) return `\u25CF \u6267\u884C\u4E2D \xB7 ${steps[activeIdx].name}`;
|
|
50
|
+
const lastErrorIdx = (() => {
|
|
51
|
+
for (let i = steps.length - 1; i >= 0; i--) {
|
|
52
|
+
if (steps[i].status === "error") return i;
|
|
53
|
+
}
|
|
54
|
+
return -1;
|
|
55
|
+
})();
|
|
56
|
+
if (lastErrorIdx >= 0) return `\u2717 \u5931\u8D25 \xB7 ${steps[lastErrorIdx].name}`;
|
|
57
|
+
return `\u2713 \u5DF2\u5B8C\u6210 \xB7 ${steps.length} \u6B65`;
|
|
58
|
+
}
|
|
59
|
+
function render(timelineEl) {
|
|
60
|
+
const state = stateMap.get(timelineEl);
|
|
61
|
+
if (!state) return;
|
|
62
|
+
const { steps, expanded, showAll } = state;
|
|
63
|
+
const titleEl = timelineEl.querySelector("[data-current-tool]");
|
|
64
|
+
if (titleEl) titleEl.textContent = computeTitle(steps);
|
|
65
|
+
const dotsEl = timelineEl.querySelector("[data-dots]");
|
|
66
|
+
if (dotsEl) {
|
|
67
|
+
const existing = Array.from(dotsEl.children);
|
|
68
|
+
for (let i = 0; i < steps.length; i++) {
|
|
69
|
+
const step = steps[i];
|
|
70
|
+
let dot = existing[i];
|
|
71
|
+
if (!dot) {
|
|
72
|
+
dot = document.createElement("span");
|
|
73
|
+
dot.className = "step-dot";
|
|
74
|
+
dot.setAttribute("data-index", String(i));
|
|
75
|
+
dotsEl.appendChild(dot);
|
|
76
|
+
}
|
|
77
|
+
dot.setAttribute("data-status", step.status);
|
|
78
|
+
dot.setAttribute("title", `${step.name} \xB7 ${step.status}`);
|
|
79
|
+
}
|
|
80
|
+
while (dotsEl.children.length > steps.length) {
|
|
81
|
+
dotsEl.removeChild(dotsEl.lastChild);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const listEl = timelineEl.querySelector("[data-list]");
|
|
85
|
+
if (listEl) {
|
|
86
|
+
const hiddenCount = Math.max(0, steps.length - MAX_VISIBLE_NODES);
|
|
87
|
+
const visibleSteps = showAll ? steps : steps.slice(-MAX_VISIBLE_NODES);
|
|
88
|
+
const existing = Array.from(listEl.children);
|
|
89
|
+
let htmlIdx = 0;
|
|
90
|
+
if (!showAll && hiddenCount > 0) {
|
|
91
|
+
let moreBtn = existing[htmlIdx];
|
|
92
|
+
if (!moreBtn || !moreBtn.classList.contains("step-timeline-more")) {
|
|
93
|
+
moreBtn = document.createElement("li");
|
|
94
|
+
moreBtn.className = "step-timeline-more";
|
|
95
|
+
moreBtn.setAttribute("role", "button");
|
|
96
|
+
moreBtn.textContent = `+ ${hiddenCount} \u66F4\u591A`;
|
|
97
|
+
moreBtn.onclick = () => {
|
|
98
|
+
state.showAll = true;
|
|
99
|
+
render(timelineEl);
|
|
100
|
+
};
|
|
101
|
+
if (existing[htmlIdx] && existing[htmlIdx] !== moreBtn) {
|
|
102
|
+
listEl.replaceChild(moreBtn, existing[htmlIdx]);
|
|
103
|
+
} else {
|
|
104
|
+
listEl.appendChild(moreBtn);
|
|
105
|
+
}
|
|
106
|
+
} else {
|
|
107
|
+
moreBtn.textContent = `+ ${hiddenCount} \u66F4\u591A`;
|
|
108
|
+
}
|
|
109
|
+
htmlIdx++;
|
|
110
|
+
}
|
|
111
|
+
for (let i = 0; i < visibleSteps.length; i++) {
|
|
112
|
+
const step = visibleSteps[i];
|
|
113
|
+
const realIndex = showAll ? i : steps.length - visibleSteps.length + i;
|
|
114
|
+
let node = existing[htmlIdx];
|
|
115
|
+
if (!node || !node.classList.contains("step-timeline-node")) {
|
|
116
|
+
node = document.createElement("li");
|
|
117
|
+
node.className = "step-timeline-node";
|
|
118
|
+
node.setAttribute("data-index", String(realIndex));
|
|
119
|
+
const marker = document.createElement("span");
|
|
120
|
+
marker.className = "step-timeline-marker";
|
|
121
|
+
const label2 = document.createElement("span");
|
|
122
|
+
label2.className = "step-timeline-label";
|
|
123
|
+
const args = document.createElement("span");
|
|
124
|
+
args.className = "step-timeline-args";
|
|
125
|
+
node.appendChild(marker);
|
|
126
|
+
node.appendChild(label2);
|
|
127
|
+
node.appendChild(args);
|
|
128
|
+
if (existing[htmlIdx] && existing[htmlIdx] !== node) {
|
|
129
|
+
listEl.replaceChild(node, existing[htmlIdx]);
|
|
130
|
+
} else {
|
|
131
|
+
listEl.appendChild(node);
|
|
132
|
+
}
|
|
133
|
+
} else {
|
|
134
|
+
node.setAttribute("data-index", String(realIndex));
|
|
135
|
+
}
|
|
136
|
+
node.setAttribute("data-status", step.status);
|
|
137
|
+
const label = node.querySelector(".step-timeline-label");
|
|
138
|
+
if (label) label.textContent = step.name;
|
|
139
|
+
const argsEl = node.querySelector(".step-timeline-args");
|
|
140
|
+
if (argsEl) {
|
|
141
|
+
const argStr = step.args && Object.keys(step.args).length > 0 ? JSON.stringify(step.args).slice(0, 60) : "";
|
|
142
|
+
argsEl.textContent = argStr;
|
|
143
|
+
argsEl.style.display = argStr ? "" : "none";
|
|
144
|
+
}
|
|
145
|
+
htmlIdx++;
|
|
146
|
+
}
|
|
147
|
+
while (listEl.children.length > htmlIdx) {
|
|
148
|
+
listEl.removeChild(listEl.lastChild);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (steps.length === 0) {
|
|
152
|
+
timelineEl.setAttribute("data-empty", "true");
|
|
153
|
+
} else {
|
|
154
|
+
timelineEl.removeAttribute("data-empty");
|
|
155
|
+
}
|
|
156
|
+
const body = timelineEl.querySelector("[data-body]");
|
|
157
|
+
if (body) {
|
|
158
|
+
if (expanded) {
|
|
159
|
+
body.style.maxHeight = body.scrollHeight + "px";
|
|
160
|
+
setTimeout(() => {
|
|
161
|
+
if (state.expanded) body.style.maxHeight = "";
|
|
162
|
+
}, 300);
|
|
163
|
+
} else {
|
|
164
|
+
body.style.maxHeight = "0";
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const arrow = timelineEl.querySelector(".step-timeline-arrow");
|
|
168
|
+
if (arrow) arrow.style.transform = expanded ? "rotate(180deg)" : "rotate(0deg)";
|
|
169
|
+
if (expanded && listEl) {
|
|
170
|
+
listEl.scrollTop = listEl.scrollHeight;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
function createEmptyStepTimeline(channelId = null) {
|
|
174
|
+
const root = document.createElement("div");
|
|
175
|
+
root.className = "step-timeline";
|
|
176
|
+
root.setAttribute("data-step-timeline", "");
|
|
177
|
+
root.setAttribute("data-empty", "true");
|
|
178
|
+
const summary = document.createElement("div");
|
|
179
|
+
summary.className = "step-timeline-summary";
|
|
180
|
+
summary.setAttribute("data-summary", "");
|
|
181
|
+
const titleSpan = document.createElement("span");
|
|
182
|
+
titleSpan.className = "step-timeline-title";
|
|
183
|
+
const titleContent = document.createElement("span");
|
|
184
|
+
titleContent.setAttribute("data-current-tool", "");
|
|
185
|
+
titleContent.textContent = "\u6267\u884C\u6B65\u9AA4";
|
|
186
|
+
titleSpan.appendChild(titleContent);
|
|
187
|
+
const dots = document.createElement("div");
|
|
188
|
+
dots.className = "step-timeline-dots";
|
|
189
|
+
dots.setAttribute("data-dots", "");
|
|
190
|
+
const arrow = document.createElement("span");
|
|
191
|
+
arrow.className = "step-timeline-arrow";
|
|
192
|
+
arrow.textContent = "\u25BE";
|
|
193
|
+
summary.appendChild(titleSpan);
|
|
194
|
+
summary.appendChild(dots);
|
|
195
|
+
summary.appendChild(arrow);
|
|
196
|
+
summary.onclick = () => {
|
|
197
|
+
const state = stateMap.get(root);
|
|
198
|
+
if (!state) return;
|
|
199
|
+
state.expanded = !state.expanded;
|
|
200
|
+
writeExpanded(state.channelId, state.expanded);
|
|
201
|
+
render(root);
|
|
202
|
+
};
|
|
203
|
+
const body = document.createElement("div");
|
|
204
|
+
body.className = "step-timeline-body";
|
|
205
|
+
body.setAttribute("data-body", "");
|
|
206
|
+
const list = document.createElement("ul");
|
|
207
|
+
list.className = "step-timeline-list";
|
|
208
|
+
list.setAttribute("data-list", "");
|
|
209
|
+
body.appendChild(list);
|
|
210
|
+
root.appendChild(summary);
|
|
211
|
+
root.appendChild(body);
|
|
212
|
+
const initialExpanded = readExpanded(channelId);
|
|
213
|
+
stateMap.set(root, {
|
|
214
|
+
steps: [],
|
|
215
|
+
currentIndex: -1,
|
|
216
|
+
expanded: initialExpanded,
|
|
217
|
+
channelId: channelId || null,
|
|
218
|
+
showAll: false
|
|
219
|
+
});
|
|
220
|
+
render(root);
|
|
221
|
+
return root;
|
|
222
|
+
}
|
|
223
|
+
function mountStepTimeline(messageEl, channelId) {
|
|
224
|
+
const existing = messageEl.querySelector("[data-step-timeline]");
|
|
225
|
+
if (existing) {
|
|
226
|
+
const state = stateMap.get(existing);
|
|
227
|
+
if (state && channelId && !state.channelId) state.channelId = channelId;
|
|
228
|
+
return existing;
|
|
229
|
+
}
|
|
230
|
+
const bubble = messageEl.querySelector(".bubble");
|
|
231
|
+
const actions = messageEl.querySelector(".message-actions");
|
|
232
|
+
const timeline = createEmptyStepTimeline(channelId);
|
|
233
|
+
if (actions && actions.parentNode === messageEl) {
|
|
234
|
+
messageEl.insertBefore(timeline, actions);
|
|
235
|
+
} else if (bubble && bubble.parentNode === messageEl) {
|
|
236
|
+
bubble.parentNode.insertBefore(timeline, bubble.nextSibling);
|
|
237
|
+
} else {
|
|
238
|
+
messageEl.appendChild(timeline);
|
|
239
|
+
}
|
|
240
|
+
return timeline;
|
|
241
|
+
}
|
|
242
|
+
function pushStepToTimeline(timelineEl, eventType, data) {
|
|
243
|
+
const state = stateMap.get(timelineEl);
|
|
244
|
+
if (!state) return;
|
|
245
|
+
const { steps } = state;
|
|
246
|
+
if (eventType === "step_start") {
|
|
247
|
+
state.currentIndex = steps.length;
|
|
248
|
+
steps.push({
|
|
249
|
+
name: data.tool,
|
|
250
|
+
status: "active",
|
|
251
|
+
args: data.args,
|
|
252
|
+
eventType
|
|
253
|
+
});
|
|
254
|
+
} else {
|
|
255
|
+
let targetIdx = state.currentIndex;
|
|
256
|
+
if (targetIdx < 0 || !steps[targetIdx] || steps[targetIdx].name !== data.tool) {
|
|
257
|
+
for (let i = steps.length - 1; i >= 0; i--) {
|
|
258
|
+
if (steps[i].name === data.tool && steps[i].status === "active") {
|
|
259
|
+
targetIdx = i;
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (targetIdx < 0) {
|
|
265
|
+
targetIdx = steps.length;
|
|
266
|
+
steps.push({ name: data.tool, status: "error", eventType });
|
|
267
|
+
}
|
|
268
|
+
if (eventType === "step_done") {
|
|
269
|
+
steps[targetIdx].status = data.success === false ? "error" : "done";
|
|
270
|
+
if (data.output) steps[targetIdx].output = data.output;
|
|
271
|
+
if (data.error) steps[targetIdx].error = data.error;
|
|
272
|
+
} else {
|
|
273
|
+
steps[targetIdx].status = "error";
|
|
274
|
+
if (data.error) steps[targetIdx].error = data.error;
|
|
275
|
+
}
|
|
276
|
+
steps[targetIdx].eventType = eventType;
|
|
277
|
+
state.currentIndex = -1;
|
|
278
|
+
}
|
|
279
|
+
render(timelineEl);
|
|
280
|
+
}
|
|
281
|
+
function migrateStepTimeline(fromEl, toEl) {
|
|
282
|
+
const fromTimeline = fromEl.querySelector("[data-step-timeline]");
|
|
283
|
+
const toTimeline = toEl.querySelector("[data-step-timeline]");
|
|
284
|
+
if (!fromTimeline) return;
|
|
285
|
+
if (toTimeline && toTimeline !== fromTimeline) {
|
|
286
|
+
toTimeline.remove();
|
|
287
|
+
}
|
|
288
|
+
if (toTimeline !== fromTimeline) {
|
|
289
|
+
toEl.appendChild(fromTimeline);
|
|
290
|
+
}
|
|
291
|
+
const state = stateMap.get(fromTimeline);
|
|
292
|
+
if (state) {
|
|
293
|
+
render(fromTimeline);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
function getStepTimeline(messageEl) {
|
|
297
|
+
if (!messageEl) return null;
|
|
298
|
+
return messageEl.querySelector("[data-step-timeline]");
|
|
299
|
+
}
|
|
300
|
+
var MAX_VISIBLE_NODES, STORAGE_PREFIX, stateMap;
|
|
301
|
+
var init_step_timeline = __esm({
|
|
302
|
+
"src/web/ui/step-timeline.ts"() {
|
|
303
|
+
"use strict";
|
|
304
|
+
MAX_VISIBLE_NODES = 8;
|
|
305
|
+
STORAGE_PREFIX = "bolloon.stepTimeline.expanded.";
|
|
306
|
+
stateMap = /* @__PURE__ */ new WeakMap();
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
// src/agents/parse-tool-call.ts
|
|
311
|
+
function defaultResolveAlias(name, tools) {
|
|
312
|
+
if (tools.has(name)) return name;
|
|
313
|
+
const lower = name.toLowerCase();
|
|
314
|
+
const aliasMap = {
|
|
315
|
+
read: "read_file",
|
|
316
|
+
edit: "edit_file",
|
|
317
|
+
write: "write_file",
|
|
318
|
+
rm: "delete_file",
|
|
319
|
+
mv: "move_file",
|
|
320
|
+
bash: "shell_exec",
|
|
321
|
+
shell: "shell_exec",
|
|
322
|
+
sh: "shell_exec",
|
|
323
|
+
cat: "read_file",
|
|
324
|
+
test: "vitest_run",
|
|
325
|
+
vitest: "vitest_run",
|
|
326
|
+
typecheck: "tsc_check",
|
|
327
|
+
tsc: "tsc_check",
|
|
328
|
+
log: "git_log",
|
|
329
|
+
show: "git_show",
|
|
330
|
+
diff: "git_diff",
|
|
331
|
+
commit: "git_commit",
|
|
332
|
+
push: "git_push",
|
|
333
|
+
branch: "git_branch",
|
|
334
|
+
checkout: "git_branch",
|
|
335
|
+
stash: "git_stash",
|
|
336
|
+
todo_write: "create_task",
|
|
337
|
+
todowrite: "create_task",
|
|
338
|
+
task: "create_task"
|
|
339
|
+
};
|
|
340
|
+
const aliased = aliasMap[lower];
|
|
341
|
+
if (aliased && tools.has(aliased)) return aliased;
|
|
342
|
+
if (tools.has(lower)) return lower;
|
|
343
|
+
return null;
|
|
344
|
+
}
|
|
345
|
+
function autoSplitCommand(args) {
|
|
346
|
+
if (typeof args.command === "string" && args.command.includes(" ") && !args.args) {
|
|
347
|
+
const parts = args.command.split(/\s+/);
|
|
348
|
+
args.command = parts[0];
|
|
349
|
+
args.args = parts.slice(1).join(" ");
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
function resolve(ctx, name) {
|
|
353
|
+
if (ctx.resolveAlias) return ctx.resolveAlias(name);
|
|
354
|
+
return defaultResolveAlias(name, ctx.tools);
|
|
355
|
+
}
|
|
356
|
+
function parseToolCall(content, ctx) {
|
|
357
|
+
if (!content) return null;
|
|
358
|
+
const strippedContent = content.replace(/<think[\s\S]*?<\/think/g, "");
|
|
359
|
+
const jsonPatterns = [
|
|
360
|
+
// markdown json code block + OpenAI 块, 同时匹配 arguments/input 字段
|
|
361
|
+
/(?:```(?:json|json5)?\s*\n?)?\{[\s\S]*?"name"\s*:\s*["'](\w+)["']\s*,\s*["']?(?:arguments|input)["']?\s*:\s*(\{[\s\S]*?\})\s*\}/
|
|
362
|
+
];
|
|
363
|
+
for (const p of jsonPatterns) {
|
|
364
|
+
const m = content.match(p);
|
|
365
|
+
if (m) {
|
|
366
|
+
const name = m[1];
|
|
367
|
+
let args = {};
|
|
368
|
+
try {
|
|
369
|
+
const parsed = JSON.parse(m[2]);
|
|
370
|
+
if (parsed && typeof parsed === "object") {
|
|
371
|
+
args = Object.fromEntries(Object.entries(parsed).map(([k, v]) => [k, String(v)]));
|
|
372
|
+
}
|
|
373
|
+
} catch {
|
|
374
|
+
}
|
|
375
|
+
autoSplitCommand(args);
|
|
376
|
+
const resolved = resolve(ctx, name);
|
|
377
|
+
if (resolved) {
|
|
378
|
+
return { name: resolved, args };
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
const toolsCallMatch = content.match(/<tools:call\s+name=["'](\w+)["']>([\s\S]*?)<\/tools:call>/);
|
|
383
|
+
if (toolsCallMatch) {
|
|
384
|
+
const name = toolsCallMatch[1];
|
|
385
|
+
const inner = toolsCallMatch[2];
|
|
386
|
+
const args = {};
|
|
387
|
+
const argTags = inner.matchAll(/<tools:call\s+name=["'](\w+)["']>([\s\S]*?)<\/tools:call>/g);
|
|
388
|
+
for (const m of argTags) {
|
|
389
|
+
args[m[1]] = m[2].trim();
|
|
390
|
+
}
|
|
391
|
+
const resolved = resolve(ctx, name);
|
|
392
|
+
if (resolved) {
|
|
393
|
+
return { name: resolved, args };
|
|
394
|
+
}
|
|
395
|
+
if (args.command) {
|
|
396
|
+
const cmdFirst = args.command.split(/\s+/)[0];
|
|
397
|
+
if (SHELL_KEYWORDS.includes(cmdFirst)) {
|
|
398
|
+
return { name: "shell_exec", args };
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
const patterns = [
|
|
403
|
+
// minimax/Hermes 自闭合 XML 格式 <invoke name="X">...</invoke>
|
|
404
|
+
new RegExp(`<invoke\\s+name=["']([\\w]+)["']>([\\s\\S]*?)</invoke>`),
|
|
405
|
+
// <function_calls> 包裹
|
|
406
|
+
new RegExp(`<function_calls>[\\s\\S]*?<invoke\\s+name=["']([\\w]+)["']>([\\s\\S]*?)</invoke>[\\s\\S]*?</function_calls>`),
|
|
407
|
+
// <function_calls><tool name="X"><param name="Y">value</param></tool></function_calls>
|
|
408
|
+
new RegExp(`<function_calls>[\\s\\S]*?<tool\\s+name=["']([\\w]+)["']>([\\s\\S]*?)</tool>[\\s\\S]*?</function_calls>`),
|
|
409
|
+
/调用工具[::]\s*(\w+)\s*\(([^)]*)\)/,
|
|
410
|
+
/使用工具[::]\s*(\w+)\s*\(([^)]*)\)/,
|
|
411
|
+
/tool[_\w]*[::]\s*(\w+)\s*\(([^)]*)\)/i,
|
|
412
|
+
/(\w+)\s*\(\s*([^)]*)\s*\)/,
|
|
413
|
+
// 对象字面量格式 {tool => "get_identity", args => {...}}
|
|
414
|
+
/\{\s*tool\s*=>\s*["'](\w+)["']\s*(?:,\s*args\s*=>\s*(\{[\s\S]*?\}))?\s*\}/,
|
|
415
|
+
// tool => "get_identity" (无 args 包裹)
|
|
416
|
+
/\btool\s*=>\s*["'](\w+)["']/,
|
|
417
|
+
// [TOOL_CALL] 块内 JSON 形式 {"name": "x", "args": {...}}
|
|
418
|
+
/\[TOOL_CALL\][\s\S]*?\{\s*"name"\s*:\s*"(\w+)"\s*,\s*"args"\s*:\s*(\{[\s\S]*?\})/i,
|
|
419
|
+
// "tool_name {json_args}" 形式 (单行, 无 name 字段)
|
|
420
|
+
/(?:^|\n)(\w+)\s+(\{[\s\S]*?\})(?=\n|$)/,
|
|
421
|
+
// XML 格式 <tool_name>...<arg>value</arg>...</tool_name>
|
|
422
|
+
/<(\w+)>([\s\S]*?)<\/\1>/
|
|
423
|
+
];
|
|
424
|
+
for (const pattern of patterns) {
|
|
425
|
+
const match = strippedContent.match(pattern);
|
|
426
|
+
if (!match) continue;
|
|
427
|
+
const name = match[1];
|
|
428
|
+
let args = {};
|
|
429
|
+
const rawArgs = match[2] || "";
|
|
430
|
+
if (rawArgs && rawArgs.trim().startsWith("{")) {
|
|
431
|
+
try {
|
|
432
|
+
const parsed = JSON.parse(rawArgs);
|
|
433
|
+
if (parsed && typeof parsed === "object") {
|
|
434
|
+
args = Object.fromEntries(Object.entries(parsed).map(([k, v]) => [k, String(v)]));
|
|
435
|
+
}
|
|
436
|
+
} catch {
|
|
437
|
+
const argPairs = rawArgs.split(",").map((s) => s.trim()).filter(Boolean);
|
|
438
|
+
for (const pair of argPairs) {
|
|
439
|
+
const [key, ...valueParts] = pair.split(":").map((s) => s.trim().replace(/['"]/g, ""));
|
|
440
|
+
if (key) args[key] = valueParts.join(":") || "";
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
} else if (rawArgs && /<[\w]/.test(rawArgs) && /<\/\w+>/.test(rawArgs)) {
|
|
444
|
+
const paramRe = /<parameter\s+name=["'](\w+)["']>([\s\S]*?)<\/parameter>/g;
|
|
445
|
+
const paramReShort = /<param\s+name=["'](\w+)["']>([\s\S]*?)<\/param>/g;
|
|
446
|
+
let pMatch;
|
|
447
|
+
while ((pMatch = paramRe.exec(rawArgs)) !== null) {
|
|
448
|
+
const argName = pMatch[1];
|
|
449
|
+
const argValue = pMatch[2].trim();
|
|
450
|
+
if (argName && argValue) {
|
|
451
|
+
args[argName] = argValue;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
if (Object.keys(args).length === 0) {
|
|
455
|
+
let sMatch;
|
|
456
|
+
paramReShort.lastIndex = 0;
|
|
457
|
+
while ((sMatch = paramReShort.exec(rawArgs)) !== null) {
|
|
458
|
+
const argName = sMatch[1];
|
|
459
|
+
const argValue = sMatch[2].trim().replace(/^["']|["']$/g, "");
|
|
460
|
+
if (argName && argValue) {
|
|
461
|
+
args[argName] = argValue;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
if (Object.keys(args).length === 0) {
|
|
466
|
+
const xmlArgPattern = /<(\w+)>([\s\S]*?)<\/\1>/g;
|
|
467
|
+
let xmlMatch;
|
|
468
|
+
while ((xmlMatch = xmlArgPattern.exec(rawArgs)) !== null) {
|
|
469
|
+
const argName = xmlMatch[1];
|
|
470
|
+
const argValue = xmlMatch[2].trim();
|
|
471
|
+
if (argName && argValue) {
|
|
472
|
+
args[argName] = argValue;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
} else if (rawArgs) {
|
|
477
|
+
const argPairs = rawArgs.split(",").map((s) => s.trim()).filter(Boolean);
|
|
478
|
+
for (const pair of argPairs) {
|
|
479
|
+
const [key, ...valueParts] = pair.split(":").map((s) => s.trim().replace(/['"]/g, ""));
|
|
480
|
+
if (key) args[key] = valueParts.join(":") || "";
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
const resolved = ctx.tools.has(name) ? name : resolve(ctx, name);
|
|
484
|
+
if (resolved) {
|
|
485
|
+
autoSplitCommand(args);
|
|
486
|
+
return { name: resolved, args };
|
|
487
|
+
}
|
|
488
|
+
if (rawArgs && /<\w+>[\s\S]*<\/\w+>/.test(strippedContent)) {
|
|
489
|
+
break;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
const xmlTagMatch = strippedContent.match(/<(\w+)>([\s\S]*?)<\/\1>/);
|
|
493
|
+
if (xmlTagMatch) {
|
|
494
|
+
const outerTag = xmlTagMatch[1];
|
|
495
|
+
const inner = xmlTagMatch[2];
|
|
496
|
+
if (!resolve(ctx, outerTag)) {
|
|
497
|
+
const cmdMatch = inner.match(/<command>([\s\S]*?)<\/command>/);
|
|
498
|
+
if (cmdMatch) {
|
|
499
|
+
const cmd = cmdMatch[1].trim();
|
|
500
|
+
const cmdFirst = cmd.split(/\s+/)[0];
|
|
501
|
+
if (SHELL_KEYWORDS.includes(cmdFirst)) {
|
|
502
|
+
const args = {};
|
|
503
|
+
const remaining = cmd.slice(cmdFirst.length).trim();
|
|
504
|
+
if (remaining) {
|
|
505
|
+
args.command = cmdFirst;
|
|
506
|
+
args.args = remaining;
|
|
507
|
+
} else {
|
|
508
|
+
args.command = cmd;
|
|
509
|
+
}
|
|
510
|
+
const argsM = inner.match(/<args>([\s\S]*?)<\/args>/);
|
|
511
|
+
if (argsM && argsM[1].trim()) {
|
|
512
|
+
args.args = argsM[1].trim();
|
|
513
|
+
}
|
|
514
|
+
return { name: "shell_exec", args };
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
return null;
|
|
520
|
+
}
|
|
521
|
+
var SHELL_KEYWORDS;
|
|
522
|
+
var init_parse_tool_call = __esm({
|
|
523
|
+
"src/agents/parse-tool-call.ts"() {
|
|
524
|
+
"use strict";
|
|
525
|
+
SHELL_KEYWORDS = ["git", "npx", "npm", "tsx", "tsc", "vitest", "node", "mkdir", "touch", "ls", "echo", "cat", "head", "tail", "wc", "pwd", "date"];
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
// src/agents/chat-segmenter.ts
|
|
530
|
+
function segmentChatReply(reply, ctx) {
|
|
531
|
+
if (!reply) return [];
|
|
532
|
+
const segments = [];
|
|
533
|
+
let remaining = reply;
|
|
534
|
+
if (!remaining.startsWith("<think>")) {
|
|
535
|
+
remaining = extractLeadingThinking(remaining, segments);
|
|
536
|
+
}
|
|
537
|
+
remaining = extractAndPush("think", remaining, segments, /<think>([\s\S]*?)<\/think>/g);
|
|
538
|
+
remaining = extractAndPush("env_details", remaining, segments, /<environment_details>([\s\S]*?)<\/environment_details>/g);
|
|
539
|
+
remaining = stripToolCallMarkers(remaining, segments, ctx);
|
|
540
|
+
if (remaining.trim()) {
|
|
541
|
+
remaining = filterFillerText(remaining);
|
|
542
|
+
}
|
|
543
|
+
const finalIdx = remaining.indexOf("<final gen>");
|
|
544
|
+
let beforeFinalText = remaining;
|
|
545
|
+
if (finalIdx >= 0) {
|
|
546
|
+
beforeFinalText = remaining.substring(0, finalIdx);
|
|
547
|
+
}
|
|
548
|
+
const textContent = beforeFinalText.trim();
|
|
549
|
+
if (textContent) {
|
|
550
|
+
segments.push({ type: "text", content: textContent });
|
|
551
|
+
}
|
|
552
|
+
if (finalIdx >= 0) {
|
|
553
|
+
const afterFinal = remaining.substring(finalIdx + "<final gen>".length).trim();
|
|
554
|
+
if (afterFinal) {
|
|
555
|
+
segments.push({ type: "final", content: afterFinal });
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
return segments;
|
|
559
|
+
}
|
|
560
|
+
function extractAndPush(type, text, out, re) {
|
|
561
|
+
const matches = [];
|
|
562
|
+
const localRe = new RegExp(re.source, re.flags);
|
|
563
|
+
let m;
|
|
564
|
+
while ((m = localRe.exec(text)) !== null) {
|
|
565
|
+
const content = m[1] ? m[1].trim() : "";
|
|
566
|
+
matches.push({
|
|
567
|
+
start: m.index,
|
|
568
|
+
end: m.index + m[0].length,
|
|
569
|
+
content
|
|
570
|
+
});
|
|
571
|
+
if (m[0].length === 0) localRe.lastIndex++;
|
|
572
|
+
}
|
|
573
|
+
if (matches.length === 0) return text;
|
|
574
|
+
for (const m2 of matches) {
|
|
575
|
+
if (m2.content) out.push({ type, content: m2.content });
|
|
576
|
+
}
|
|
577
|
+
let next = "";
|
|
578
|
+
let cursor = 0;
|
|
579
|
+
for (const m2 of matches) {
|
|
580
|
+
next += text.substring(cursor, m2.start);
|
|
581
|
+
cursor = m2.end;
|
|
582
|
+
}
|
|
583
|
+
next += text.substring(cursor);
|
|
584
|
+
return next;
|
|
585
|
+
}
|
|
586
|
+
function extractLeadingThinking(text, out) {
|
|
587
|
+
const firstLineEnd = text.indexOf("\n");
|
|
588
|
+
const firstLine = firstLineEnd === -1 ? text : text.substring(0, firstLineEnd);
|
|
589
|
+
const rest = firstLineEnd === -1 ? "" : text.substring(firstLineEnd + 1);
|
|
590
|
+
const thinkingStartRe = /^(让我|我先|我应该|先来|先|接下来|好的[,,]?\s*我|让我先|先看看|让我看看|思考|考虑)/;
|
|
591
|
+
const enThinkingStartRe = /^(Let me|I'll|I will|First,|Next,|Now,|So,|Alright[,.]\s+(?:let me|I'll|I will))/i;
|
|
592
|
+
if (firstLine.trim().length === 0) {
|
|
593
|
+
return text;
|
|
594
|
+
}
|
|
595
|
+
if (firstLine.length <= 120 && (thinkingStartRe.test(firstLine) || enThinkingStartRe.test(firstLine))) {
|
|
596
|
+
out.push({ type: "think", content: firstLine.trim() });
|
|
597
|
+
return rest;
|
|
598
|
+
}
|
|
599
|
+
if (firstLine.length <= 80 && (thinkingStartRe.test(firstLine) || enThinkingStartRe.test(firstLine))) {
|
|
600
|
+
out.push({ type: "think", content: firstLine.trim() });
|
|
601
|
+
return rest;
|
|
602
|
+
}
|
|
603
|
+
return text;
|
|
604
|
+
}
|
|
605
|
+
function filterFillerText(text) {
|
|
606
|
+
const lines = text.split("\n");
|
|
607
|
+
const filtered = [];
|
|
608
|
+
for (const line of lines) {
|
|
609
|
+
const t = line.trim();
|
|
610
|
+
if (!t) continue;
|
|
611
|
+
if (/^(好|好了|好的|完成|完成了|任务完成|可以|可以了|答完了|说完了|就这样|完了|done|ok|OK|Okay|okay|alright|fine|let me check|我来)\.?$/i.test(t)) {
|
|
612
|
+
continue;
|
|
613
|
+
}
|
|
614
|
+
filtered.push(line);
|
|
615
|
+
}
|
|
616
|
+
return filtered.join("\n").trim();
|
|
617
|
+
}
|
|
618
|
+
function stripToolCallMarkers(text, out, ctx) {
|
|
619
|
+
let result = text.replace(/\[TOOL_CALL\][\s\S]*?\[\/TOOL_CALL\]/g, (m) => {
|
|
620
|
+
const parsed = parseToolCall(m, { tools: ctx.knownToolNames });
|
|
621
|
+
if (parsed) out.push({ type: "tool_call", tool: { name: parsed.name, args: parsed.args } });
|
|
622
|
+
return "";
|
|
623
|
+
});
|
|
624
|
+
result = result.replace(/<tool_call>[\s\S]*?<\/tool_call>/gi, (m) => {
|
|
625
|
+
const parsed = parseToolCall(m, { tools: ctx.knownToolNames });
|
|
626
|
+
if (parsed) out.push({ type: "tool_call", tool: { name: parsed.name, args: parsed.args } });
|
|
627
|
+
return "";
|
|
628
|
+
});
|
|
629
|
+
result = result.replace(/<invoke\s+name=["']([\w]+)["']>([\s\S]*?)<\/invoke>/g, (_m, name, inner) => {
|
|
630
|
+
if (ctx.knownToolNames.has(name)) {
|
|
631
|
+
const args = extractSimpleArgs(inner);
|
|
632
|
+
out.push({ type: "tool_call", tool: { name, args } });
|
|
633
|
+
}
|
|
634
|
+
return "";
|
|
635
|
+
});
|
|
636
|
+
result = result.replace(/<function_calls>[\s\S]*?<\/function_calls>/g, (m) => {
|
|
637
|
+
const parsed = parseToolCall(m, { tools: ctx.knownToolNames });
|
|
638
|
+
if (parsed) out.push({ type: "tool_call", tool: { name: parsed.name, args: parsed.args } });
|
|
639
|
+
return "";
|
|
640
|
+
});
|
|
641
|
+
result = result.replace(
|
|
642
|
+
/\{\s*"name"\s*:\s*"([\w]+)"\s*,\s*"(?:arguments|input|args|params)"\s*:\s*(\{[\s\S]*?\})\s*\}/g,
|
|
643
|
+
(_m, name, argsJson) => {
|
|
644
|
+
if (!ctx.knownToolNames.has(name)) return "";
|
|
645
|
+
let args = {};
|
|
646
|
+
try {
|
|
647
|
+
const parsed = JSON.parse(argsJson);
|
|
648
|
+
if (parsed && typeof parsed === "object") {
|
|
649
|
+
args = Object.fromEntries(Object.entries(parsed).map(([k, v]) => [k, String(v)]));
|
|
650
|
+
}
|
|
651
|
+
} catch {
|
|
652
|
+
}
|
|
653
|
+
out.push({ type: "tool_call", tool: { name, args } });
|
|
654
|
+
return "";
|
|
655
|
+
}
|
|
656
|
+
);
|
|
657
|
+
result = result.replace(/\{\s*tool\s*=>\s*["']([\w]+)["']\s*(?:,\s*args\s*=>\s*(\{[\s\S]*?\}))?\s*\}/g, (_m, name, argsJson) => {
|
|
658
|
+
if (!ctx.knownToolNames.has(name)) return "";
|
|
659
|
+
let args = {};
|
|
660
|
+
if (argsJson) {
|
|
661
|
+
try {
|
|
662
|
+
const parsed = JSON.parse(argsJson);
|
|
663
|
+
if (parsed && typeof parsed === "object") {
|
|
664
|
+
args = Object.fromEntries(Object.entries(parsed).map(([k, v]) => [k, String(v)]));
|
|
665
|
+
}
|
|
666
|
+
} catch {
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
out.push({ type: "tool_call", tool: { name, args } });
|
|
670
|
+
return "";
|
|
671
|
+
});
|
|
672
|
+
result = result.replace(/\[Function[^\]]*\]\s*/g, "");
|
|
673
|
+
result = stripUnclosedToolCallTags(result);
|
|
674
|
+
return result;
|
|
675
|
+
}
|
|
676
|
+
function stripUnclosedToolCallTags(text) {
|
|
677
|
+
const startTags = [
|
|
678
|
+
{ tag: "<tool_call>", closeTag: "</tool_call>", openRe: /<tool_call>/g },
|
|
679
|
+
{ tag: "<invoke ", closeTag: "</invoke>", openRe: /<invoke\s+name=["']([\w]+)["']>/g },
|
|
680
|
+
{ tag: "<function_calls>", closeTag: "</function_calls>", openRe: /<function_calls>/g },
|
|
681
|
+
{ tag: "[TOOL_CALL]", closeTag: "[/TOOL_CALL]", openRe: /\[TOOL_CALL\]/g }
|
|
682
|
+
];
|
|
683
|
+
let result = text;
|
|
684
|
+
for (const { tag, closeTag, openRe } of startTags) {
|
|
685
|
+
let m;
|
|
686
|
+
while ((m = openRe.exec(result)) !== null) {
|
|
687
|
+
const tail = result.substring(m.index);
|
|
688
|
+
const closeIdx = tail.indexOf(closeTag);
|
|
689
|
+
if (closeIdx === -1) {
|
|
690
|
+
const nextBlank = result.indexOf("\n\n", m.index);
|
|
691
|
+
const endIdx = nextBlank === -1 ? result.length : nextBlank;
|
|
692
|
+
result = result.substring(0, m.index) + result.substring(endIdx);
|
|
693
|
+
break;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
return result;
|
|
698
|
+
}
|
|
699
|
+
function extractSimpleArgs(inner) {
|
|
700
|
+
const args = {};
|
|
701
|
+
const paramRe = /<parameter\s+name=["'](\w+)["']>([\s\S]*?)<\/parameter>/g;
|
|
702
|
+
let m;
|
|
703
|
+
while ((m = paramRe.exec(inner)) !== null) {
|
|
704
|
+
args[m[1]] = m[2].trim();
|
|
705
|
+
}
|
|
706
|
+
if (Object.keys(args).length === 0) {
|
|
707
|
+
const shortRe = /<param\s+name=["'](\w+)["']>([\s\S]*?)<\/param>/g;
|
|
708
|
+
while ((m = shortRe.exec(inner)) !== null) {
|
|
709
|
+
args[m[1]] = m[2].trim().replace(/^["']|["']$/g, "");
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
if (Object.keys(args).length === 0) {
|
|
713
|
+
const cmdM = inner.match(/<command>([\s\S]*?)<\/command>/);
|
|
714
|
+
const argsM = inner.match(/<args>([\s\S]*?)<\/args>/);
|
|
715
|
+
if (cmdM) {
|
|
716
|
+
const cmd = cmdM[1].trim();
|
|
717
|
+
if (cmd.includes(" ") && !argsM) {
|
|
718
|
+
const parts = cmd.split(/\s+/);
|
|
719
|
+
args.command = parts[0];
|
|
720
|
+
args.args = parts.slice(1).join(" ");
|
|
721
|
+
} else {
|
|
722
|
+
args.command = cmd;
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
if (argsM) args.args = argsM[1].trim();
|
|
726
|
+
}
|
|
727
|
+
return args;
|
|
728
|
+
}
|
|
729
|
+
var init_chat_segmenter = __esm({
|
|
730
|
+
"src/agents/chat-segmenter.ts"() {
|
|
731
|
+
"use strict";
|
|
732
|
+
init_parse_tool_call();
|
|
733
|
+
}
|
|
734
|
+
});
|
|
735
|
+
|
|
736
|
+
// src/web/ui/message-renderer.ts
|
|
737
|
+
var message_renderer_exports = {};
|
|
738
|
+
__export(message_renderer_exports, {
|
|
739
|
+
MessageRenderer: () => MessageRenderer,
|
|
740
|
+
addMessage: () => addMessage,
|
|
741
|
+
escapeHtml: () => escapeHtml,
|
|
742
|
+
finalizeTimelineAsMessage: () => finalizeTimelineAsMessage,
|
|
743
|
+
getMessagesContainerForCurrent: () => getMessagesContainerForCurrent,
|
|
744
|
+
handleStepEvent: () => handleStepEvent,
|
|
745
|
+
handleStreamTokenEvent: () => handleStreamTokenEvent,
|
|
746
|
+
hasStreamingText: () => hasStreamingText,
|
|
747
|
+
injectRecoveredText: () => injectRecoveredText,
|
|
748
|
+
replaceStreamingText: () => replaceStreamingText,
|
|
749
|
+
resetRendererState: () => resetRendererState
|
|
750
|
+
});
|
|
751
|
+
function hasStreamingText() {
|
|
752
|
+
return streamingText.length > 0;
|
|
753
|
+
}
|
|
754
|
+
function replaceStreamingText(fullContent) {
|
|
755
|
+
if (!streamingTextNode || !streamingMessageEl) {
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
758
|
+
streamingTextNode.nodeValue = String(fullContent || "");
|
|
759
|
+
streamingText = String(fullContent || "");
|
|
760
|
+
}
|
|
761
|
+
function injectRecoveredText(partialText, ctx = { messagesEl: null, messagesContainers: /* @__PURE__ */ new Map(), currentChannelId: null }) {
|
|
762
|
+
if (streamingMessageEl && streamingTextNode) {
|
|
763
|
+
streamingTextNode.nodeValue = String(partialText || "");
|
|
764
|
+
streamingText = String(partialText || "");
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
handleStreamTokenEvent(
|
|
768
|
+
{
|
|
769
|
+
type: "token",
|
|
770
|
+
streamType: "token",
|
|
771
|
+
content: String(partialText || ""),
|
|
772
|
+
delta: String(partialText || "")
|
|
773
|
+
},
|
|
774
|
+
ctx
|
|
775
|
+
);
|
|
776
|
+
}
|
|
777
|
+
function scheduleScrollToBottom(container) {
|
|
778
|
+
if (!container) return;
|
|
779
|
+
if (scrollToBottomTimer) return;
|
|
780
|
+
scrollToBottomTimer = setTimeout(() => {
|
|
781
|
+
container.scrollTop = container.scrollHeight;
|
|
782
|
+
scrollToBottomTimer = null;
|
|
783
|
+
}, 60);
|
|
784
|
+
}
|
|
785
|
+
function getMessagesContainerForCurrent(currentChannelId2, messagesContainers2, messagesEl2) {
|
|
786
|
+
if (currentChannelId2 && messagesContainers2.get(currentChannelId2)) {
|
|
787
|
+
return messagesContainers2.get(currentChannelId2) || null;
|
|
788
|
+
}
|
|
789
|
+
return messagesEl2;
|
|
790
|
+
}
|
|
791
|
+
function escapeHtml(s) {
|
|
792
|
+
return String(s ?? "").replace(/[&<>"']/g, (c) => ({
|
|
793
|
+
"&": "&",
|
|
794
|
+
"<": "<",
|
|
795
|
+
">": ">",
|
|
796
|
+
'"': """,
|
|
797
|
+
"'": "'"
|
|
798
|
+
})[c]);
|
|
799
|
+
}
|
|
800
|
+
function addMessage(content, type, save = true, container, usedJudgmentIds = [], ctx = { messagesEl: null, messagesContainers: /* @__PURE__ */ new Map(), currentChannelId: null }) {
|
|
801
|
+
const messagesEl2 = ctx.messagesEl || (typeof document !== "undefined" ? document.getElementById("messages") : null);
|
|
802
|
+
const messagesContainers2 = ctx.messagesContainers || /* @__PURE__ */ new Map();
|
|
803
|
+
const currentChannelId2 = ctx.currentChannelId;
|
|
804
|
+
const msgContainer = container || getMessagesContainerForCurrent(currentChannelId2, messagesContainers2, messagesEl2);
|
|
805
|
+
if (!save && msgContainer && msgContainer.children.length > 200) {
|
|
806
|
+
const toRemove = msgContainer.children.length - 200;
|
|
807
|
+
for (let i = 0; i < toRemove; i++) {
|
|
808
|
+
const first = msgContainer.firstElementChild;
|
|
809
|
+
if (first) msgContainer.removeChild(first);
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
if (save) {
|
|
813
|
+
const lastContent = type === "user" ? lastUserCommand : lastAiContent;
|
|
814
|
+
if (lastContent && content === lastContent) {
|
|
815
|
+
console.log(`[addMessage] \u8DF3\u8FC7\u91CD\u590D\u7684 ${type} \u6D88\u606F`);
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
818
|
+
if (type === "user") lastUserCommand = content;
|
|
819
|
+
else lastAiContent = content;
|
|
820
|
+
}
|
|
821
|
+
const div = document.createElement("div");
|
|
822
|
+
div.className = `message message-${type}`;
|
|
823
|
+
let cleanContent = content;
|
|
824
|
+
if (type === "ai") {
|
|
825
|
+
cleanContent = cleanContent.replace(/<think>[\s\S]*?<\/think>/g, "");
|
|
826
|
+
const finalGenIdx = cleanContent.indexOf("<final gen>");
|
|
827
|
+
if (finalGenIdx >= 0) {
|
|
828
|
+
cleanContent = cleanContent.substring(0, finalGenIdx).trim();
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
const knownToolNames = ctx && ctx.knownToolNames || /* @__PURE__ */ new Set();
|
|
832
|
+
const segments = segmentChatReply(cleanContent, { knownToolNames });
|
|
833
|
+
if (segments.length === 0) {
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
836
|
+
let thinkContainer = null;
|
|
837
|
+
let renderedAny = false;
|
|
838
|
+
for (const seg of segments) {
|
|
839
|
+
if (seg.type === "think" && seg.content) {
|
|
840
|
+
thinkContainer = buildThinkContainer(seg.content);
|
|
841
|
+
div.appendChild(thinkContainer);
|
|
842
|
+
renderedAny = true;
|
|
843
|
+
} else if (seg.type === "env_details" && seg.content) {
|
|
844
|
+
div.appendChild(buildEnvContainer(seg.content));
|
|
845
|
+
renderedAny = true;
|
|
846
|
+
} else if (seg.type === "text" && seg.content) {
|
|
847
|
+
if (thinkContainer) div.appendChild(thinkContainer);
|
|
848
|
+
thinkContainer = null;
|
|
849
|
+
div.appendChild(buildBubble(seg.content, type));
|
|
850
|
+
renderedAny = true;
|
|
851
|
+
} else if (seg.type === "final" && seg.content) {
|
|
852
|
+
if (thinkContainer) div.appendChild(thinkContainer);
|
|
853
|
+
thinkContainer = null;
|
|
854
|
+
const finalEl = buildBubble(seg.content, type);
|
|
855
|
+
finalEl.classList.add("bubble-final");
|
|
856
|
+
div.appendChild(finalEl);
|
|
857
|
+
renderedAny = true;
|
|
858
|
+
} else if (seg.type === "tool_call" && seg.tool) {
|
|
859
|
+
if (ctx && ctx.toolCallCallback) {
|
|
860
|
+
ctx.toolCallCallback(seg.tool, div);
|
|
861
|
+
}
|
|
862
|
+
renderedAny = true;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
if (!renderedAny) {
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
const rawContent = segments.filter((s) => s.type === "text" || s.type === "final").map((s) => s.content || "").join("\n");
|
|
869
|
+
const time = document.createElement("div");
|
|
870
|
+
time.className = "time";
|
|
871
|
+
time.textContent = (/* @__PURE__ */ new Date()).toLocaleTimeString("zh-CN", { hour: "2-digit", minute: "2-digit" });
|
|
872
|
+
if (type === "ai") {
|
|
873
|
+
div.appendChild(buildMessageActions(div, rawContent, ctx));
|
|
874
|
+
}
|
|
875
|
+
if (type === "ai" && Array.isArray(usedJudgmentIds) && usedJudgmentIds.length > 0) {
|
|
876
|
+
const link = document.createElement("a");
|
|
877
|
+
link.className = "used-judgments-link";
|
|
878
|
+
link.textContent = `\u{1F4CE} \u53C2\u8003 ${usedJudgmentIds.length} \u6761\u539F\u5219`;
|
|
879
|
+
link.onclick = (e) => {
|
|
880
|
+
e.preventDefault();
|
|
881
|
+
if (typeof ctx.openJudgmentsModalWithFilter === "function") {
|
|
882
|
+
ctx.openJudgmentsModalWithFilter(usedJudgmentIds);
|
|
883
|
+
}
|
|
884
|
+
};
|
|
885
|
+
div.appendChild(link);
|
|
886
|
+
}
|
|
887
|
+
if (type === "ai" && msgContainer) {
|
|
888
|
+
mountStepTimeline(div, currentChannelId2);
|
|
889
|
+
}
|
|
890
|
+
div.appendChild(time);
|
|
891
|
+
if (msgContainer) {
|
|
892
|
+
msgContainer.appendChild(div);
|
|
893
|
+
scheduleScrollToBottom(msgContainer);
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
function buildThinkContainer(thinkContent) {
|
|
897
|
+
const container = document.createElement("div");
|
|
898
|
+
container.className = "think-container";
|
|
899
|
+
const toggle = document.createElement("div");
|
|
900
|
+
toggle.className = "think-toggle";
|
|
901
|
+
toggle.innerHTML = '\u{1F4AD} \u601D\u8003\u8FC7\u7A0B <span class="think-arrow">\u25B8</span>';
|
|
902
|
+
toggle.onclick = function() {
|
|
903
|
+
const details = container.querySelector(".think-content");
|
|
904
|
+
const arrow = toggle.querySelector(".think-arrow");
|
|
905
|
+
if (!details || !arrow) return;
|
|
906
|
+
if (details.style.display === "none") {
|
|
907
|
+
details.style.display = "block";
|
|
908
|
+
arrow.textContent = "\u25BE";
|
|
909
|
+
} else {
|
|
910
|
+
details.style.display = "none";
|
|
911
|
+
arrow.textContent = "\u25B8";
|
|
912
|
+
}
|
|
913
|
+
};
|
|
914
|
+
const content = document.createElement("div");
|
|
915
|
+
content.className = "think-content";
|
|
916
|
+
content.style.display = "none";
|
|
917
|
+
content.innerHTML = `<pre>${escapeHtml(thinkContent)}</pre>`;
|
|
918
|
+
container.appendChild(toggle);
|
|
919
|
+
container.appendChild(content);
|
|
920
|
+
return container;
|
|
921
|
+
}
|
|
922
|
+
function buildEnvContainer(envDetails) {
|
|
923
|
+
const container = document.createElement("div");
|
|
924
|
+
container.className = "env-container";
|
|
925
|
+
const toggle = document.createElement("div");
|
|
926
|
+
toggle.className = "env-toggle";
|
|
927
|
+
toggle.innerHTML = '\u2699\uFE0F \u73AF\u5883\u4FE1\u606F <span class="env-arrow">\u25B8</span>';
|
|
928
|
+
toggle.onclick = function() {
|
|
929
|
+
const details = container.querySelector(".environment-details");
|
|
930
|
+
const arrow = toggle.querySelector(".env-arrow");
|
|
931
|
+
if (!details || !arrow) return;
|
|
932
|
+
if (details.style.display === "none") {
|
|
933
|
+
details.style.display = "block";
|
|
934
|
+
arrow.textContent = "\u25BE";
|
|
935
|
+
} else {
|
|
936
|
+
details.style.display = "none";
|
|
937
|
+
arrow.textContent = "\u25B8";
|
|
938
|
+
}
|
|
939
|
+
};
|
|
940
|
+
const content = document.createElement("div");
|
|
941
|
+
content.className = "environment-details";
|
|
942
|
+
content.style.display = "none";
|
|
943
|
+
content.innerHTML = `<pre>${escapeHtml(envDetails)}</pre>`;
|
|
944
|
+
container.appendChild(toggle);
|
|
945
|
+
container.appendChild(content);
|
|
946
|
+
return container;
|
|
947
|
+
}
|
|
948
|
+
function buildBubble(text, type) {
|
|
949
|
+
const bubble = document.createElement("div");
|
|
950
|
+
bubble.className = `bubble bubble-${type}`;
|
|
951
|
+
const marked2 = window.marked;
|
|
952
|
+
bubble.innerHTML = marked2 ? marked2.parse(text) : escapeHtml(text);
|
|
953
|
+
return bubble;
|
|
954
|
+
}
|
|
955
|
+
function buildMessageActions(div, rawContent, ctx) {
|
|
956
|
+
const actions = document.createElement("div");
|
|
957
|
+
actions.className = "message-actions";
|
|
958
|
+
const copyBtn = document.createElement("button");
|
|
959
|
+
copyBtn.className = "action-btn copy-btn";
|
|
960
|
+
copyBtn.innerHTML = copyIcon() + " \u590D\u5236";
|
|
961
|
+
copyBtn.title = "\u590D\u5236\u6D88\u606F";
|
|
962
|
+
copyBtn.onclick = () => {
|
|
963
|
+
navigator.clipboard.writeText(rawContent).then(() => {
|
|
964
|
+
copyBtn.innerHTML = checkIcon() + " \u5DF2\u590D\u5236";
|
|
965
|
+
setTimeout(() => {
|
|
966
|
+
copyBtn.innerHTML = copyIcon() + " \u590D\u5236";
|
|
967
|
+
}, 2e3);
|
|
968
|
+
});
|
|
969
|
+
};
|
|
970
|
+
actions.appendChild(copyBtn);
|
|
971
|
+
const saveJudgmentBtn = document.createElement("button");
|
|
972
|
+
saveJudgmentBtn.className = "action-btn save-as-judgment";
|
|
973
|
+
saveJudgmentBtn.title = "AI \u84B8\u998F\u4E3A 30-80 \u5B57\u5224\u65AD\u529B + \u81EA\u52A8\u6F14\u5316\u5BF9\u9F50";
|
|
974
|
+
saveJudgmentBtn.setAttribute("data-decision", rawContent.substring(0, 800));
|
|
975
|
+
if (ctx.currentChannelId) saveJudgmentBtn.setAttribute("data-channel-id", ctx.currentChannelId);
|
|
976
|
+
saveJudgmentBtn.innerHTML = shieldIcon() + " \u84B8\u998F\u4E3A\u5224\u65AD";
|
|
977
|
+
actions.appendChild(saveJudgmentBtn);
|
|
978
|
+
const regenBtn = document.createElement("button");
|
|
979
|
+
regenBtn.className = "action-btn regenerate-btn";
|
|
980
|
+
regenBtn.innerHTML = refreshIcon(false) + " \u91CD\u65B0\u56DE\u7B54";
|
|
981
|
+
regenBtn.title = "\u91CD\u65B0\u751F\u6210\u56DE\u590D";
|
|
982
|
+
regenBtn.onclick = () => {
|
|
983
|
+
regenBtn.innerHTML = refreshIcon(true) + " \u751F\u6210\u4E2D...";
|
|
984
|
+
regenBtn.disabled = true;
|
|
985
|
+
const messages = div.parentElement?.querySelectorAll(".message") || [];
|
|
986
|
+
let lastUserMsg = "";
|
|
987
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
988
|
+
const msg = messages[i];
|
|
989
|
+
if (msg.classList.contains("message-user")) {
|
|
990
|
+
const bubble = msg.querySelector(".bubble");
|
|
991
|
+
if (bubble) {
|
|
992
|
+
lastUserMsg = bubble.textContent || bubble.innerText || "";
|
|
993
|
+
break;
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
fetch("/regenerate", {
|
|
998
|
+
method: "POST",
|
|
999
|
+
headers: { "Content-Type": "application/json" },
|
|
1000
|
+
body: JSON.stringify({ channelId: ctx.currentChannelId, userMessage: lastUserMsg })
|
|
1001
|
+
}).then((res) => {
|
|
1002
|
+
if (!res.ok) throw new Error("regenerate failed");
|
|
1003
|
+
}).catch((err) => {
|
|
1004
|
+
console.error("\u91CD\u65B0\u751F\u6210\u5931\u8D25:", err);
|
|
1005
|
+
regenBtn.innerHTML = refreshIcon(false) + " \u5931\u8D25";
|
|
1006
|
+
setTimeout(() => {
|
|
1007
|
+
regenBtn.innerHTML = refreshIcon(false) + " \u91CD\u65B0\u56DE\u7B54";
|
|
1008
|
+
regenBtn.disabled = false;
|
|
1009
|
+
}, 2e3);
|
|
1010
|
+
});
|
|
1011
|
+
};
|
|
1012
|
+
actions.appendChild(regenBtn);
|
|
1013
|
+
return actions;
|
|
1014
|
+
}
|
|
1015
|
+
function copyIcon() {
|
|
1016
|
+
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>';
|
|
1017
|
+
}
|
|
1018
|
+
function checkIcon() {
|
|
1019
|
+
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>';
|
|
1020
|
+
}
|
|
1021
|
+
function shieldIcon() {
|
|
1022
|
+
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>';
|
|
1023
|
+
}
|
|
1024
|
+
function refreshIcon(spin = false) {
|
|
1025
|
+
const cls = spin ? ' class="spin"' : "";
|
|
1026
|
+
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>`;
|
|
1027
|
+
}
|
|
1028
|
+
function handleStreamTokenEvent(data, ctx = { messagesEl: null, messagesContainers: /* @__PURE__ */ new Map(), currentChannelId: null }) {
|
|
1029
|
+
const messagesEl2 = ctx.messagesEl || (typeof document !== "undefined" ? document.getElementById("messages") : null);
|
|
1030
|
+
const messagesContainers2 = ctx.messagesContainers || /* @__PURE__ */ new Map();
|
|
1031
|
+
const currentChannelId2 = ctx.currentChannelId;
|
|
1032
|
+
const container = getMessagesContainerForCurrent(currentChannelId2, messagesContainers2, messagesEl2);
|
|
1033
|
+
if (!container) return;
|
|
1034
|
+
const delta = data.content || "";
|
|
1035
|
+
if (!delta) return;
|
|
1036
|
+
if (!streamingMessageEl || !streamingMessageEl.isConnected) {
|
|
1037
|
+
streamingMessageEl = document.createElement("div");
|
|
1038
|
+
streamingMessageEl.className = "message message-ai message-streaming";
|
|
1039
|
+
streamingTextNode = document.createTextNode("");
|
|
1040
|
+
streamingMessageEl.appendChild(streamingTextNode);
|
|
1041
|
+
streamingText = "";
|
|
1042
|
+
mountStepTimeline(streamingMessageEl, currentChannelId2);
|
|
1043
|
+
container.appendChild(streamingMessageEl);
|
|
1044
|
+
if (typeof ctx.setTimelineState === "function") {
|
|
1045
|
+
ctx.setTimelineState("streaming");
|
|
1046
|
+
}
|
|
1047
|
+
scheduleScrollToBottom(container);
|
|
1048
|
+
}
|
|
1049
|
+
if (data.streamType === "token") {
|
|
1050
|
+
if (streamingTextNode) streamingTextNode.nodeValue = delta;
|
|
1051
|
+
streamingText = delta;
|
|
1052
|
+
} else {
|
|
1053
|
+
if (streamingTextNode) streamingTextNode.appendData(delta);
|
|
1054
|
+
streamingText += delta;
|
|
1055
|
+
}
|
|
1056
|
+
scheduleScrollToBottom(container);
|
|
1057
|
+
}
|
|
1058
|
+
function finalizeTimelineAsMessage(ctx = { messagesEl: null, messagesContainers: /* @__PURE__ */ new Map(), currentChannelId: null }) {
|
|
1059
|
+
const messagesEl2 = ctx.messagesEl || (typeof document !== "undefined" ? document.getElementById("messages") : null);
|
|
1060
|
+
const messagesContainers2 = ctx.messagesContainers || /* @__PURE__ */ new Map();
|
|
1061
|
+
const currentChannelId2 = ctx.currentChannelId;
|
|
1062
|
+
const container = getMessagesContainerForCurrent(currentChannelId2, messagesContainers2, messagesEl2);
|
|
1063
|
+
if (streamingText.trim().length > 0) {
|
|
1064
|
+
const oldStreamingEl = streamingMessageEl;
|
|
1065
|
+
if (oldStreamingEl && oldStreamingEl.parentNode) {
|
|
1066
|
+
oldStreamingEl.parentNode.removeChild(oldStreamingEl);
|
|
1067
|
+
}
|
|
1068
|
+
addMessage(streamingText, "ai", true, container, ctx.lastUsedJudgmentIds || [], ctx);
|
|
1069
|
+
if (oldStreamingEl && container) {
|
|
1070
|
+
const newAiMsg = container.querySelector(".message-ai:last-of-type");
|
|
1071
|
+
if (newAiMsg && newAiMsg !== oldStreamingEl) {
|
|
1072
|
+
migrateStepTimeline(oldStreamingEl, newAiMsg);
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
streamingMessageEl = null;
|
|
1077
|
+
streamingTextNode = null;
|
|
1078
|
+
streamingText = "";
|
|
1079
|
+
if (typeof ctx.setTimelineState === "function") {
|
|
1080
|
+
ctx.setTimelineState("done");
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
function handleStepEvent(data, ctx = { messagesEl: null, messagesContainers: /* @__PURE__ */ new Map(), currentChannelId: null }) {
|
|
1084
|
+
const messagesEl2 = ctx.messagesEl || (typeof document !== "undefined" ? document.getElementById("messages") : null);
|
|
1085
|
+
const messagesContainers2 = ctx.messagesContainers || /* @__PURE__ */ new Map();
|
|
1086
|
+
const currentChannelId2 = ctx.currentChannelId;
|
|
1087
|
+
const container = getMessagesContainerForCurrent(currentChannelId2, messagesContainers2, messagesEl2);
|
|
1088
|
+
if (!container) return;
|
|
1089
|
+
if (!data || !data.type) return;
|
|
1090
|
+
let target = streamingMessageEl && streamingMessageEl.isConnected ? streamingMessageEl : null;
|
|
1091
|
+
if (!target) {
|
|
1092
|
+
const aiMsgs = container.querySelectorAll(".message-ai");
|
|
1093
|
+
if (aiMsgs.length === 0) return;
|
|
1094
|
+
target = aiMsgs[aiMsgs.length - 1];
|
|
1095
|
+
}
|
|
1096
|
+
if (!target) return;
|
|
1097
|
+
const timeline = getStepTimeline(target);
|
|
1098
|
+
if (!timeline) return;
|
|
1099
|
+
pushStepToTimeline(timeline, data.type, {
|
|
1100
|
+
tool: data.tool || "unknown",
|
|
1101
|
+
args: data.args,
|
|
1102
|
+
success: data.success,
|
|
1103
|
+
output: data.output,
|
|
1104
|
+
error: data.error
|
|
1105
|
+
});
|
|
1106
|
+
}
|
|
1107
|
+
function resetRendererState() {
|
|
1108
|
+
streamingMessageEl = null;
|
|
1109
|
+
streamingTextNode = null;
|
|
1110
|
+
streamingText = "";
|
|
1111
|
+
lastUserCommand = "";
|
|
1112
|
+
lastAiContent = "";
|
|
1113
|
+
if (scrollToBottomTimer) {
|
|
1114
|
+
clearTimeout(scrollToBottomTimer);
|
|
1115
|
+
scrollToBottomTimer = null;
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
var streamingMessageEl, streamingTextNode, streamingText, lastUserCommand, lastAiContent, scrollToBottomTimer, MessageRenderer;
|
|
1119
|
+
var init_message_renderer = __esm({
|
|
1120
|
+
"src/web/ui/message-renderer.ts"() {
|
|
1121
|
+
"use strict";
|
|
1122
|
+
init_step_timeline();
|
|
1123
|
+
init_chat_segmenter();
|
|
1124
|
+
streamingMessageEl = null;
|
|
1125
|
+
streamingTextNode = null;
|
|
1126
|
+
streamingText = "";
|
|
1127
|
+
lastUserCommand = "";
|
|
1128
|
+
lastAiContent = "";
|
|
1129
|
+
scrollToBottomTimer = null;
|
|
1130
|
+
MessageRenderer = {
|
|
1131
|
+
addMessage,
|
|
1132
|
+
handleStreamTokenEvent,
|
|
1133
|
+
finalizeTimelineAsMessage,
|
|
1134
|
+
handleStepEvent,
|
|
1135
|
+
escapeHtml,
|
|
1136
|
+
getMessagesContainerForCurrent,
|
|
1137
|
+
resetRendererState
|
|
1138
|
+
};
|
|
1139
|
+
if (typeof window !== "undefined") {
|
|
1140
|
+
window.MR = MessageRenderer;
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
});
|
|
1144
|
+
|
|
1145
|
+
// src/web/client-loop-status.ts
|
|
1146
|
+
var client_loop_status_exports = {};
|
|
1147
|
+
__export(client_loop_status_exports, {
|
|
1148
|
+
applyLoopBarState: () => applyLoopBarState,
|
|
1149
|
+
hideLoopStatusBar: () => hideLoopStatusBar,
|
|
1150
|
+
inspectLoopResult: () => inspectLoopResult,
|
|
1151
|
+
markLoopBarDone: () => markLoopBarDone,
|
|
1152
|
+
openLoopInspectModal: () => openLoopInspectModal,
|
|
1153
|
+
renderLoopStatusBar: () => renderLoopStatusBar
|
|
1154
|
+
});
|
|
1155
|
+
function renderLoopStatusBar(tool, content) {
|
|
1156
|
+
if (!loopStatusBar || !loopStatusText) return;
|
|
1157
|
+
const t = String(tool || "").toLowerCase();
|
|
1158
|
+
if (!LOOP_STATUS_TOOLS.has(t)) {
|
|
1159
|
+
console.log("[SSE] status (tool=" + t + ", ignored by UI):", content?.slice(0, 80));
|
|
1160
|
+
return;
|
|
1161
|
+
}
|
|
1162
|
+
const retryMatch = String(content || "").match(/自动重试(?: loop)?\s+(\d+)\/(\d+)/);
|
|
1163
|
+
const retryFinal = /自动重试\s+\d+\s*次后仍失败/.test(String(content || ""));
|
|
1164
|
+
loopStatusBar.hidden = false;
|
|
1165
|
+
const isPivotLoopMsg = /🔄\s*循环\s*\d+\s*\/\s*\d+/.test(String(content || ""));
|
|
1166
|
+
let mainText;
|
|
1167
|
+
if (isPivotLoopMsg && !retryMatch) {
|
|
1168
|
+
mainText = "";
|
|
1169
|
+
} else {
|
|
1170
|
+
mainText = String(content || "").replace(/^[\u{1F000}-\u{1FFFF}\u{2600}-\u{27BF}]\s*/u, "").replace(/^↻\s*/, "").replace(/^⛔\s*/, "").replace(/^⚠️\s*/, "").slice(0, 200);
|
|
1171
|
+
}
|
|
1172
|
+
loopStatusText.textContent = mainText;
|
|
1173
|
+
if (retryMatch) {
|
|
1174
|
+
loopBarState = "retrying";
|
|
1175
|
+
const retryEl = document.getElementById("loop-status-retry");
|
|
1176
|
+
if (retryEl) {
|
|
1177
|
+
retryEl.hidden = false;
|
|
1178
|
+
retryEl.textContent = `\u81EA\u52A8\u91CD\u8BD5 ${retryMatch[1]}/${retryMatch[2]}`;
|
|
1179
|
+
}
|
|
1180
|
+
} else if (retryFinal) {
|
|
1181
|
+
loopBarState = "done";
|
|
1182
|
+
const retryEl = document.getElementById("loop-status-retry");
|
|
1183
|
+
if (retryEl) retryEl.hidden = true;
|
|
1184
|
+
} else {
|
|
1185
|
+
if (loopBarState !== "loading") loopBarState = "loading";
|
|
1186
|
+
const retryEl = document.getElementById("loop-status-retry");
|
|
1187
|
+
if (retryEl) retryEl.hidden = true;
|
|
1188
|
+
}
|
|
1189
|
+
applyLoopBarState();
|
|
1190
|
+
}
|
|
1191
|
+
function markLoopBarDone(summary) {
|
|
1192
|
+
loopBarState = "done";
|
|
1193
|
+
if (summary) loopBarLastSummary = summary;
|
|
1194
|
+
applyLoopBarState();
|
|
1195
|
+
}
|
|
1196
|
+
function applyLoopBarState() {
|
|
1197
|
+
if (!loopStatusBar) return;
|
|
1198
|
+
loopStatusBar.dataset.state = loopBarState;
|
|
1199
|
+
const checkBtn = document.getElementById("loop-status-check");
|
|
1200
|
+
if (checkBtn) checkBtn.hidden = loopBarState !== "done";
|
|
1201
|
+
}
|
|
1202
|
+
function hideLoopStatusBar() {
|
|
1203
|
+
if (!loopStatusBar) return;
|
|
1204
|
+
loopStatusBar.hidden = true;
|
|
1205
|
+
loopBarState = "loading";
|
|
1206
|
+
loopBarLastSummary = "";
|
|
1207
|
+
const retryEl = document.getElementById("loop-status-retry");
|
|
1208
|
+
if (retryEl) retryEl.hidden = true;
|
|
1209
|
+
applyLoopBarState();
|
|
1210
|
+
}
|
|
1211
|
+
async function inspectLoopResult() {
|
|
1212
|
+
const checkBtn = document.getElementById("loop-status-check");
|
|
1213
|
+
if (checkBtn) {
|
|
1214
|
+
checkBtn.disabled = true;
|
|
1215
|
+
checkBtn.textContent = "\u23F3 \u52A0\u8F7D...";
|
|
1216
|
+
}
|
|
1217
|
+
try {
|
|
1218
|
+
const channelId = window.currentChannelId || "";
|
|
1219
|
+
const r = await fetch(`/api/loop/inspect?channelId=${encodeURIComponent(channelId)}`);
|
|
1220
|
+
const j = await r.json().catch(() => ({}));
|
|
1221
|
+
openLoopInspectModal(j);
|
|
1222
|
+
} catch (err) {
|
|
1223
|
+
console.error("[inspect] error:", err);
|
|
1224
|
+
if (typeof window.showSimpleToast === "function") window.showSimpleToast("\u2717 \u68C0\u67E5\u5931\u8D25");
|
|
1225
|
+
} finally {
|
|
1226
|
+
if (checkBtn) {
|
|
1227
|
+
checkBtn.disabled = false;
|
|
1228
|
+
checkBtn.textContent = "\u2713 \u68C0\u67E5";
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
function openLoopInspectModal(data) {
|
|
1233
|
+
const existing = document.getElementById("loop-inspect-modal");
|
|
1234
|
+
if (existing) existing.remove();
|
|
1235
|
+
const modal = document.createElement("div");
|
|
1236
|
+
modal.id = "loop-inspect-modal";
|
|
1237
|
+
modal.className = "modal active";
|
|
1238
|
+
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;";
|
|
1239
|
+
const panel = document.createElement("div");
|
|
1240
|
+
panel.className = "modal-panel";
|
|
1241
|
+
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;";
|
|
1242
|
+
const title = document.createElement("h3");
|
|
1243
|
+
title.textContent = "\u{1F50D} \u5FAA\u73AF\u68C0\u67E5";
|
|
1244
|
+
title.style.cssText = "margin:0 0 12px;font-size:16px;";
|
|
1245
|
+
panel.appendChild(title);
|
|
1246
|
+
const close = document.createElement("button");
|
|
1247
|
+
close.textContent = "\xD7";
|
|
1248
|
+
close.style.cssText = "position:absolute;top:8px;right:12px;background:transparent;border:0;font-size:24px;cursor:pointer;color:var(--text-secondary);";
|
|
1249
|
+
close.onclick = () => modal.remove();
|
|
1250
|
+
panel.appendChild(close);
|
|
1251
|
+
if (data.error) {
|
|
1252
|
+
const e = document.createElement("div");
|
|
1253
|
+
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;";
|
|
1254
|
+
e.textContent = "\u26A0\uFE0F " + data.error;
|
|
1255
|
+
panel.appendChild(e);
|
|
1256
|
+
}
|
|
1257
|
+
if (data.summary) {
|
|
1258
|
+
const s = document.createElement("div");
|
|
1259
|
+
s.style.cssText = "padding:8px 12px;background:var(--bg-tertiary);border-radius:4px;margin-bottom:12px;font-size:13px;";
|
|
1260
|
+
s.textContent = data.summary;
|
|
1261
|
+
panel.appendChild(s);
|
|
1262
|
+
}
|
|
1263
|
+
if (data.tokens && (data.tokens.input || data.tokens.output)) {
|
|
1264
|
+
const t = document.createElement("div");
|
|
1265
|
+
t.style.cssText = "font-size:12px;color:var(--text-muted);margin-bottom:12px;";
|
|
1266
|
+
t.textContent = `token: input ${data.tokens.input || 0} \xB7 output ${data.tokens.output || 0}`;
|
|
1267
|
+
panel.appendChild(t);
|
|
1268
|
+
}
|
|
1269
|
+
if (Array.isArray(data.steps) && data.steps.length > 0) {
|
|
1270
|
+
const h = document.createElement("div");
|
|
1271
|
+
h.textContent = `\u6B65\u9AA4 (${data.steps.length})`;
|
|
1272
|
+
h.style.cssText = "font-weight:600;margin-bottom:8px;";
|
|
1273
|
+
panel.appendChild(h);
|
|
1274
|
+
for (const step of data.steps) {
|
|
1275
|
+
const row = document.createElement("div");
|
|
1276
|
+
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;";
|
|
1277
|
+
const icon = step.status === "ok" || step.status === "completed" ? "\u2713" : step.status === "error" || step.status === "failed" ? "\u2717" : "\u25CB";
|
|
1278
|
+
const dur = step.durationMs ? ` (${(step.durationMs / 1e3).toFixed(1)}s)` : "";
|
|
1279
|
+
row.innerHTML = `<b>${icon} ${window.escapeHtml ? window.escapeHtml(step.name) : step.name}</b>${dur}`;
|
|
1280
|
+
if (step.output) {
|
|
1281
|
+
const pre = document.createElement("pre");
|
|
1282
|
+
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;";
|
|
1283
|
+
pre.textContent = String(step.output).slice(0, 800);
|
|
1284
|
+
row.appendChild(pre);
|
|
1285
|
+
}
|
|
1286
|
+
panel.appendChild(row);
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
if (data.finalReply) {
|
|
1290
|
+
const h = document.createElement("div");
|
|
1291
|
+
h.textContent = "\u6700\u7EC8\u56DE\u590D";
|
|
1292
|
+
h.style.cssText = "font-weight:600;margin:12px 0 8px;";
|
|
1293
|
+
panel.appendChild(h);
|
|
1294
|
+
const r = document.createElement("div");
|
|
1295
|
+
r.style.cssText = "padding:8px 12px;background:var(--bg-secondary);border-radius:4px;font-size:13px;white-space:pre-wrap;word-break:break-word;";
|
|
1296
|
+
r.textContent = data.finalReply;
|
|
1297
|
+
panel.appendChild(r);
|
|
1298
|
+
}
|
|
1299
|
+
if (!data.error && !data.summary && (!data.steps || data.steps.length === 0) && !data.finalReply) {
|
|
1300
|
+
const empty = document.createElement("div");
|
|
1301
|
+
empty.style.cssText = "text-align:center;padding:24px;color:var(--text-muted);font-size:13px;";
|
|
1302
|
+
empty.textContent = "\u65E0\u5FAA\u73AF\u4EA7\u51FA (\u53EF\u80FD\u5DF2 abort, \u6216\u6CA1\u4EA7\u751F step)";
|
|
1303
|
+
panel.appendChild(empty);
|
|
1304
|
+
}
|
|
1305
|
+
modal.appendChild(panel);
|
|
1306
|
+
modal.onclick = (e) => {
|
|
1307
|
+
if (e.target === modal) modal.remove();
|
|
1308
|
+
};
|
|
1309
|
+
document.body.appendChild(modal);
|
|
1310
|
+
}
|
|
1311
|
+
var LOOP_STATUS_TOOLS, loopBarState, loopBarLastSummary, loopStatusBar, loopStatusText, loopStatusMeta, LoopStatusExports;
|
|
1312
|
+
var init_client_loop_status = __esm({
|
|
1313
|
+
"src/web/client-loop-status.ts"() {
|
|
1314
|
+
"use strict";
|
|
1315
|
+
LOOP_STATUS_TOOLS = /* @__PURE__ */ new Set(["loop", "compactor", "recovery", "system"]);
|
|
1316
|
+
loopBarState = "loading";
|
|
1317
|
+
loopBarLastSummary = "";
|
|
1318
|
+
loopStatusBar = document.getElementById("loop-status-bar");
|
|
1319
|
+
loopStatusText = document.getElementById("loop-status-text");
|
|
1320
|
+
loopStatusMeta = document.getElementById("loop-status-meta");
|
|
1321
|
+
LoopStatusExports = {
|
|
1322
|
+
renderLoopStatusBar,
|
|
1323
|
+
markLoopBarDone,
|
|
1324
|
+
applyLoopBarState,
|
|
1325
|
+
hideLoopStatusBar,
|
|
1326
|
+
inspectLoopResult,
|
|
1327
|
+
openLoopInspectModal,
|
|
1328
|
+
get loopBarState() {
|
|
1329
|
+
return loopBarState;
|
|
1330
|
+
},
|
|
1331
|
+
get loopBarLastSummary() {
|
|
1332
|
+
return loopBarLastSummary;
|
|
1333
|
+
}
|
|
1334
|
+
};
|
|
1335
|
+
if (typeof window !== "undefined") {
|
|
1336
|
+
window.LoopStatus = LoopStatusExports;
|
|
1337
|
+
}
|
|
1338
|
+
if (typeof module !== "undefined" && module.exports) {
|
|
1339
|
+
module.exports = LoopStatusExports;
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
});
|
|
1343
|
+
|
|
1344
|
+
// src/web/util/safe-name.ts
|
|
1345
|
+
function safeChannelName(input2, fallback) {
|
|
1346
|
+
return safeNameInternal(input2, fallback ?? "(\u672A\u547D\u540D)", ["undefined", "null", "NaN"]);
|
|
1347
|
+
}
|
|
1348
|
+
function safeNameInternal(input2, fallback, invalidLiterals) {
|
|
1349
|
+
if (input2 === void 0 || input2 === null) return fallback;
|
|
1350
|
+
if (typeof input2 === "number" && Number.isNaN(input2)) return fallback;
|
|
1351
|
+
const s = String(input2).trim();
|
|
1352
|
+
if (!s) return fallback;
|
|
1353
|
+
for (const lit of invalidLiterals) {
|
|
1354
|
+
if (s === lit) return fallback;
|
|
1355
|
+
}
|
|
1356
|
+
return s;
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
// src/web/client.ts
|
|
4
1360
|
if (typeof marked === "undefined") {
|
|
5
1361
|
window.marked = { parse: (text) => String(text).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/\n/g, "<br>") };
|
|
6
1362
|
}
|
|
@@ -19,9 +1375,9 @@
|
|
|
19
1375
|
return orig(...args);
|
|
20
1376
|
};
|
|
21
1377
|
})();
|
|
22
|
-
|
|
1378
|
+
var MR = {};
|
|
23
1379
|
try {
|
|
24
|
-
if (typeof
|
|
1380
|
+
if (typeof __require !== "undefined") MR = (init_message_renderer(), __toCommonJS(message_renderer_exports)) || {};
|
|
25
1381
|
} catch (e) {
|
|
26
1382
|
}
|
|
27
1383
|
function _getMR() {
|
|
@@ -29,13 +1385,13 @@
|
|
|
29
1385
|
if (typeof window !== "undefined" && window.MR) return window.MR;
|
|
30
1386
|
return {};
|
|
31
1387
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
1388
|
+
var MR_addMessage = (...args) => _getMR().addMessage?.(...args);
|
|
1389
|
+
var MR_finalizeTimelineAsMessage = (...args) => _getMR().finalizeTimelineAsMessage?.(...args);
|
|
1390
|
+
var MR_handleStepEvent = (...args) => _getMR().handleStepEvent?.(...args);
|
|
1391
|
+
var MR_escapeHtml = (s) => _getMR().escapeHtml?.(s);
|
|
1392
|
+
var MR_hasStreamingText = () => _getMR().hasStreamingText?.() ?? false;
|
|
1393
|
+
var MR_replaceStreamingText = (text) => _getMR().replaceStreamingText?.(text);
|
|
1394
|
+
var MR_injectRecoveredText = (text, ctx) => _getMR().injectRecoveredText?.(text, ctx ?? getRendererCtx());
|
|
39
1395
|
function getRendererCtx() {
|
|
40
1396
|
return {
|
|
41
1397
|
messagesEl,
|
|
@@ -46,13 +1402,13 @@
|
|
|
46
1402
|
// 引用 client.js 函数, 通过参数注入避免循环 import
|
|
47
1403
|
};
|
|
48
1404
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
1405
|
+
var messagesEl = document.getElementById("messages");
|
|
1406
|
+
var input = document.getElementById("input");
|
|
1407
|
+
var sendBtn = document.getElementById("send");
|
|
1408
|
+
var sidebar = document.getElementById("sidebar");
|
|
1409
|
+
var LS = {};
|
|
54
1410
|
try {
|
|
55
|
-
if (typeof
|
|
1411
|
+
if (typeof __require !== "undefined") LS = (init_client_loop_status(), __toCommonJS(client_loop_status_exports)) || {};
|
|
56
1412
|
} catch (e) {
|
|
57
1413
|
}
|
|
58
1414
|
function _getLS() {
|
|
@@ -60,28 +1416,28 @@
|
|
|
60
1416
|
if (typeof window !== "undefined" && window.LoopStatus) return window.LoopStatus;
|
|
61
1417
|
return {};
|
|
62
1418
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
1419
|
+
var renderLoopStatusBar2 = (...args) => _getLS().renderLoopStatusBar?.(...args);
|
|
1420
|
+
var hideLoopStatusBar2 = (...args) => _getLS().hideLoopStatusBar?.(...args);
|
|
1421
|
+
var sidebarToggle = document.getElementById("sidebar-toggle");
|
|
1422
|
+
var themeToggle = document.getElementById("theme-toggle");
|
|
1423
|
+
var channelList = document.getElementById("channel-list");
|
|
1424
|
+
var newChannelBtn = document.getElementById("new-channel-btn");
|
|
1425
|
+
var newChannelInput = document.getElementById("new-channel-input");
|
|
1426
|
+
var channelNameEl = document.getElementById("channel-name");
|
|
1427
|
+
var eventSources = /* @__PURE__ */ new Map();
|
|
1428
|
+
var currentChannelId = null;
|
|
1429
|
+
var currentAgentId = "";
|
|
1430
|
+
var channels = [];
|
|
1431
|
+
var remoteChannels = [];
|
|
1432
|
+
var isSidebarCollapsed = false;
|
|
1433
|
+
var reconnectAttempts = /* @__PURE__ */ new Map();
|
|
1434
|
+
var reconnectTimers = /* @__PURE__ */ new Map();
|
|
1435
|
+
var heartbeatTimers = /* @__PURE__ */ new Map();
|
|
1436
|
+
var lastKnownSeq = /* @__PURE__ */ new Map();
|
|
1437
|
+
var lastSeenMsgIds = /* @__PURE__ */ new Map();
|
|
1438
|
+
var COLLAPSED_PEERS_KEY = "bolloon.p2p.collapsedPeers";
|
|
1439
|
+
var SEEN_PEERS_KEY = "bolloon.p2p.seenPeers";
|
|
1440
|
+
var collapsedPeers = function loadCollapsed() {
|
|
85
1441
|
try {
|
|
86
1442
|
const raw = localStorage.getItem(COLLAPSED_PEERS_KEY);
|
|
87
1443
|
return new Set(raw ? JSON.parse(raw) : []);
|
|
@@ -89,7 +1445,7 @@
|
|
|
89
1445
|
return /* @__PURE__ */ new Set();
|
|
90
1446
|
}
|
|
91
1447
|
}();
|
|
92
|
-
|
|
1448
|
+
var seenPeers = function loadSeen() {
|
|
93
1449
|
try {
|
|
94
1450
|
const raw = localStorage.getItem(SEEN_PEERS_KEY);
|
|
95
1451
|
return new Set(raw ? JSON.parse(raw) : []);
|
|
@@ -139,10 +1495,10 @@
|
|
|
139
1495
|
renderRemoteChannels();
|
|
140
1496
|
if (typeof window.__syncP2PToggleAllBtn === "function") window.__syncP2PToggleAllBtn();
|
|
141
1497
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
1498
|
+
var messagesContainers = /* @__PURE__ */ new Map();
|
|
1499
|
+
var sessionMessages = /* @__PURE__ */ new Map();
|
|
1500
|
+
var currentSessionId = null;
|
|
1501
|
+
var expandedAgents = /* @__PURE__ */ new Set();
|
|
146
1502
|
function generateId() {
|
|
147
1503
|
return crypto.randomUUID();
|
|
148
1504
|
}
|
|
@@ -205,7 +1561,7 @@
|
|
|
205
1561
|
console.error("[\u52A0\u8F7D\u9891\u9053] \u5931\u8D25:", err);
|
|
206
1562
|
}
|
|
207
1563
|
}
|
|
208
|
-
|
|
1564
|
+
var v3GlobalEventSource = null;
|
|
209
1565
|
function startV3GlobalSSE() {
|
|
210
1566
|
if (v3GlobalEventSource) return;
|
|
211
1567
|
try {
|
|
@@ -229,7 +1585,7 @@
|
|
|
229
1585
|
const prefix = `\u{1F916} \u8FDC\u7AEF AI \u56DE\u590D
|
|
230
1586
|
|
|
231
1587
|
`;
|
|
232
|
-
|
|
1588
|
+
addMessage2(prefix + (msg.text || "(\u7A7A\u56DE\u590D)"), "ai", false, log);
|
|
233
1589
|
}
|
|
234
1590
|
log.scrollTop = log.scrollHeight;
|
|
235
1591
|
} else {
|
|
@@ -251,7 +1607,7 @@
|
|
|
251
1607
|
if (judgments.bound && judgments.bound.length > 0) {
|
|
252
1608
|
jh += '<div style="color:#78350f;margin-bottom:4px;"><b>\u786C\u7ED1\u5B9A</b> (\u5FC5\u987B\u9075\u5FAA):</div>';
|
|
253
1609
|
for (const j of judgments.bound) {
|
|
254
|
-
jh += `<div style="margin:2px 0;padding-left:8px;">\u2022 <b>${
|
|
1610
|
+
jh += `<div style="margin:2px 0;padding-left:8px;">\u2022 <b>${escapeHtml2((j.decision || "").slice(0, 80))}</b>${j.reasons && j.reasons.length ? '<br><span style="color:#92400e;font-size:11px;">\u7406\u7531: ' + escapeHtml2(j.reasons.join("; ").slice(0, 80)) + "</span>" : ""}</div>`;
|
|
255
1611
|
}
|
|
256
1612
|
}
|
|
257
1613
|
if (judgments.candidates && judgments.candidates.length > 0) {
|
|
@@ -278,7 +1634,7 @@
|
|
|
278
1634
|
const toast = document.createElement("div");
|
|
279
1635
|
toast.style.cssText = "margin:6px 0;padding:8px 10px;background:#fce7f3;border-left:3px solid #ec4899;border-radius:4px;font-size:12px;color:#831843;";
|
|
280
1636
|
const fromTxt = msg.source === "ai-mention-remote" ? `\u8FDC\u7AEF\u8282\u70B9 ${(msg.fromPublicKey || "").substring(0, 8)}\u2026 \u7684 ${msg.originChannelName}` : `${msg.originChannelName} (\u672C\u5730)`;
|
|
281
|
-
toast.innerHTML = `\u{1F4E1} <b>${fromTxt}</b> @-mention \u2192 \u5F53\u524D channel: <i>${
|
|
1637
|
+
toast.innerHTML = `\u{1F4E1} <b>${fromTxt}</b> @-mention \u2192 \u5F53\u524D channel: <i>${escapeHtml2((msg.text || "").slice(0, 100))}</i>${msg.text && msg.text.length > 100 ? "\u2026" : ""}`;
|
|
282
1638
|
log.appendChild(toast);
|
|
283
1639
|
log.scrollTop = log.scrollHeight;
|
|
284
1640
|
}
|
|
@@ -363,7 +1719,7 @@
|
|
|
363
1719
|
currentSessionId = null;
|
|
364
1720
|
if (currentChannelId) {
|
|
365
1721
|
const ch = channels.find((c) => c.id === currentChannelId);
|
|
366
|
-
if (channelNameEl) channelNameEl.textContent =
|
|
1722
|
+
if (channelNameEl) channelNameEl.textContent = safeChannelName(ch?.name, "Bolloon Agent");
|
|
367
1723
|
await selectChannel(currentChannelId);
|
|
368
1724
|
} else {
|
|
369
1725
|
messagesEl.innerHTML = "";
|
|
@@ -427,7 +1783,7 @@
|
|
|
427
1783
|
if (container) {
|
|
428
1784
|
container.innerHTML = "";
|
|
429
1785
|
showChannelView(currentChannelId);
|
|
430
|
-
|
|
1786
|
+
addMessage2("\u4F60\u597D\uFF01\u65B0\u4F1A\u8BDD\u5DF2\u5F00\u59CB\uFF0C\u6709\u4EC0\u4E48\u6211\u53EF\u4EE5\u5E2E\u4F60\u7684\u5417\uFF1F", "ai", false, container);
|
|
431
1787
|
}
|
|
432
1788
|
expandedAgents.add(currentChannelId);
|
|
433
1789
|
renderChannels();
|
|
@@ -514,8 +1870,8 @@
|
|
|
514
1870
|
console.error("Failed to delete session:", err);
|
|
515
1871
|
}
|
|
516
1872
|
}
|
|
517
|
-
|
|
518
|
-
|
|
1873
|
+
var _saveSessionMessagesDirty = false;
|
|
1874
|
+
var _saveSessionMessagesTimer = null;
|
|
519
1875
|
function saveCurrentSessionMessages() {
|
|
520
1876
|
if (!currentChannelId || !currentSessionId) return;
|
|
521
1877
|
_saveSessionMessagesDirty = true;
|
|
@@ -540,8 +1896,8 @@
|
|
|
540
1896
|
scheduleChannelsRefresh();
|
|
541
1897
|
await new Promise((r) => setTimeout(r, 600));
|
|
542
1898
|
}
|
|
543
|
-
|
|
544
|
-
|
|
1899
|
+
var channelRefreshTimer = null;
|
|
1900
|
+
var channelRefreshInFlight = null;
|
|
545
1901
|
function scheduleChannelsRefresh() {
|
|
546
1902
|
if (channelRefreshTimer) return;
|
|
547
1903
|
channelRefreshTimer = setTimeout(async () => {
|
|
@@ -637,12 +1993,12 @@
|
|
|
637
1993
|
<polyline points="9 18 15 12 9 6"></polyline>
|
|
638
1994
|
</svg>
|
|
639
1995
|
<div class="channel-icon">\u{1F4AC}</div>
|
|
640
|
-
<span class="channel-name" title="${
|
|
1996
|
+
<span class="channel-name" title="${escapeHtml2(safeChannelName(ch.name, ""))}">${escapeHtml2(safeChannelName(ch.name))}</span>
|
|
641
1997
|
<span class="agent-row-meta">
|
|
642
1998
|
${walletBadge2}
|
|
643
1999
|
${toolsBadge}
|
|
644
2000
|
${sessionCount > 1 ? `<span class="agent-session-count" title="${sessionCount} \u4E2A\u4F1A\u8BDD">${sessionCount}</span>` : ""}
|
|
645
|
-
${currentSessLabel ? `<span class="agent-current-session" title="\u5F53\u524D\u4F1A\u8BDD\uFF1A${
|
|
2001
|
+
${currentSessLabel ? `<span class="agent-current-session" title="\u5F53\u524D\u4F1A\u8BDD\uFF1A${escapeHtml2(currentSessLabel)}">\xB7 ${escapeHtml2(currentSessLabel)}</span>` : ""}
|
|
646
2002
|
<button class="agent-config-btn" title="\u914D\u7F6E\u667A\u80FD\u4F53 (\u94B1\u5305 / \u5DE5\u5177)">
|
|
647
2003
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
648
2004
|
<circle cx="12" cy="12" r="3"></circle>
|
|
@@ -704,7 +2060,7 @@
|
|
|
704
2060
|
sessLi.className = `session-item ${isActive ? "active" : ""}`;
|
|
705
2061
|
sessLi.dataset.sessionId = sess.id;
|
|
706
2062
|
sessLi.innerHTML = `
|
|
707
|
-
<span class="session-name" title="${
|
|
2063
|
+
<span class="session-name" title="${escapeHtml2(formatSessionName(sess))}">${escapeHtml2(formatSessionName(sess))}</span>
|
|
708
2064
|
<button class="session-delete" title="\u5220\u9664\u4F1A\u8BDD">\xD7</button>
|
|
709
2065
|
`;
|
|
710
2066
|
sessLi.addEventListener("click", (ev) => {
|
|
@@ -743,7 +2099,7 @@
|
|
|
743
2099
|
const id = sess.id || "";
|
|
744
2100
|
return id ? `\u4F1A\u8BDD ${id.slice(-6)}` : "\u65B0\u4F1A\u8BDD";
|
|
745
2101
|
}
|
|
746
|
-
|
|
2102
|
+
var escapeHtml2 = MR_escapeHtml || ((s) => String(s ?? "").replace(/[&<>"']/g, (c) => ({
|
|
747
2103
|
"&": "&",
|
|
748
2104
|
"<": "<",
|
|
749
2105
|
">": ">",
|
|
@@ -779,13 +2135,23 @@
|
|
|
779
2135
|
}
|
|
780
2136
|
const channel = channels.find((c) => c.id === channelId);
|
|
781
2137
|
if (channel) {
|
|
782
|
-
if (channelNameEl) channelNameEl.textContent =
|
|
2138
|
+
if (channelNameEl) channelNameEl.textContent = safeChannelName(channel.name);
|
|
783
2139
|
currentSessionId = targetSessionId || channel.currentSessionId || "default";
|
|
784
2140
|
if (targetSessionId) {
|
|
785
2141
|
channel.currentSessionId = targetSessionId;
|
|
786
2142
|
}
|
|
787
2143
|
expandedAgents.add(channelId);
|
|
788
2144
|
console.log("[selectChannel] \u9891\u9053:", channel.name, "session:", currentSessionId);
|
|
2145
|
+
} else {
|
|
2146
|
+
console.warn("[selectChannel] channel \u4E0D\u5B58\u5728:", channelId);
|
|
2147
|
+
if (channelNameEl) channelNameEl.textContent = safeChannelName("(channel \u5DF2\u5220\u9664)");
|
|
2148
|
+
currentSessionId = targetSessionId || "default";
|
|
2149
|
+
const orphanContainer = ensureMessageContainer(channelId);
|
|
2150
|
+
showChannelView(channelId);
|
|
2151
|
+
orphanContainer.innerHTML = "";
|
|
2152
|
+
appendSystem(`\u26A0\uFE0F Channel ${channelId} \u5DF2\u4E0D\u5B58\u5728, \u65E0\u6CD5\u7EE7\u7EED\u5BF9\u8BDD\u3002
|
|
2153
|
+
\u8BF7\u5237\u65B0\u9875\u9762 (F5) \u6216\u5728\u5DE6\u4FA7\u9009\u62E9\u5176\u4ED6 channel\u3002`, "error");
|
|
2154
|
+
return;
|
|
789
2155
|
}
|
|
790
2156
|
const t0 = performance.now();
|
|
791
2157
|
renderChannelsLite(channelId, currentSessionId);
|
|
@@ -806,18 +2172,18 @@
|
|
|
806
2172
|
const tmpContainer = document.createElement("div");
|
|
807
2173
|
tmpContainer.style.display = "none";
|
|
808
2174
|
for (const msg of msgs) {
|
|
809
|
-
|
|
2175
|
+
addMessage2(msg.content, msg.type, false, tmpContainer, msg.metadata?.usedJudgmentIds || []);
|
|
810
2176
|
}
|
|
811
2177
|
while (tmpContainer.firstChild) {
|
|
812
2178
|
frag.appendChild(tmpContainer.firstChild);
|
|
813
2179
|
}
|
|
814
2180
|
container.appendChild(frag);
|
|
815
2181
|
} else {
|
|
816
|
-
|
|
2182
|
+
addMessage2("\u4F60\u597D\uFF01\u6211\u662F Bolloon Agent\u3002\u6709\u4EC0\u4E48\u6211\u53EF\u4EE5\u5E2E\u4F60\u7684\u5417\uFF1F", "ai", false, container);
|
|
817
2183
|
}
|
|
818
2184
|
} catch (err) {
|
|
819
2185
|
console.error("[selectChannel] \u52A0\u8F7D session \u5931\u8D25:", err);
|
|
820
|
-
|
|
2186
|
+
addMessage2("\u4F60\u597D\uFF01\u6211\u662F Bolloon Agent\u3002\u6709\u4EC0\u4E48\u6211\u53EF\u4EE5\u5E2E\u4F60\u7684\u5417\uFF1F", "ai", false, container);
|
|
821
2187
|
}
|
|
822
2188
|
}
|
|
823
2189
|
async function loadSession(channelId, sessionId = null) {
|
|
@@ -826,33 +2192,41 @@
|
|
|
826
2192
|
const targetSessionId = sessionId || currentSessionId || "default";
|
|
827
2193
|
try {
|
|
828
2194
|
const res = await fetch(`/sessions/${channelId}?sessionId=${encodeURIComponent(targetSessionId)}`);
|
|
2195
|
+
if (res.status === 404) {
|
|
2196
|
+
const data = await res.json().catch(() => ({}));
|
|
2197
|
+
container.innerHTML = "";
|
|
2198
|
+
appendSystem(`\u26A0\uFE0F Channel ${channelId} \u5DF2\u4E0D\u5B58\u5728, \u65E0\u6CD5\u52A0\u8F7D\u5386\u53F2\u6D88\u606F\u3002
|
|
2199
|
+
${data.error || "channel not found"}`, "error");
|
|
2200
|
+
return;
|
|
2201
|
+
}
|
|
829
2202
|
const session = await res.json();
|
|
830
2203
|
container.innerHTML = "";
|
|
831
2204
|
if (session.messages && session.messages.length > 0) {
|
|
832
2205
|
session.messages.forEach((msg) => {
|
|
833
|
-
|
|
2206
|
+
addMessage2(msg.content, msg.type, false, container, msg.metadata?.usedJudgmentIds || []);
|
|
834
2207
|
});
|
|
835
2208
|
} else {
|
|
836
|
-
|
|
2209
|
+
addMessage2("\u4F60\u597D\uFF01\u6211\u662F Bolloon Agent\u3002\u6709\u4EC0\u4E48\u6211\u53EF\u4EE5\u5E2E\u4F60\u7684\u5417\uFF1F", "ai", false, container);
|
|
837
2210
|
}
|
|
838
2211
|
} catch (err) {
|
|
839
2212
|
console.error("Failed to load session:", err);
|
|
840
2213
|
container.innerHTML = "";
|
|
841
|
-
|
|
2214
|
+
addMessage2("\u4F60\u597D\uFF01\u6211\u662F Bolloon Agent\u3002\u6709\u4EC0\u4E48\u6211\u53EF\u4EE5\u5E2E\u4F60\u7684\u5417\uFF1F", "ai", false, container);
|
|
842
2215
|
}
|
|
843
2216
|
}
|
|
844
|
-
function
|
|
2217
|
+
function addMessage2(content, type, save = true, container, usedJudgmentIds = []) {
|
|
845
2218
|
return MR_addMessage(content, type, save, container, usedJudgmentIds, getRendererCtx());
|
|
846
2219
|
}
|
|
847
|
-
function
|
|
2220
|
+
function finalizeTimelineAsMessage2() {
|
|
848
2221
|
return MR_finalizeTimelineAsMessage(getRendererCtx());
|
|
849
2222
|
}
|
|
850
|
-
function
|
|
2223
|
+
function handleStepEvent2(data) {
|
|
851
2224
|
return MR_handleStepEvent(data, getRendererCtx());
|
|
852
2225
|
}
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
2226
|
+
var lastUsedJudgmentIds = [];
|
|
2227
|
+
var currentPreviewBubble = null;
|
|
2228
|
+
var selfImproveCardSeq = 0;
|
|
2229
|
+
function getMessagesContainerForCurrent2() {
|
|
856
2230
|
if (currentChannelId && messagesContainers.get(currentChannelId)) {
|
|
857
2231
|
return messagesContainers.get(currentChannelId);
|
|
858
2232
|
}
|
|
@@ -885,7 +2259,7 @@
|
|
|
885
2259
|
return card;
|
|
886
2260
|
}
|
|
887
2261
|
function handleSelfImproveTriggered(data) {
|
|
888
|
-
const container =
|
|
2262
|
+
const container = getMessagesContainerForCurrent2();
|
|
889
2263
|
if (!container) return;
|
|
890
2264
|
const card = makeSelfImproveCard(data);
|
|
891
2265
|
card.querySelector(".self-improve-title").textContent = `\u{1F9E0} \u81EA\u8FED\u4EE3\u89E6\u53D1 \xB7 ${data.eventKind || "unknown"}`;
|
|
@@ -900,7 +2274,7 @@
|
|
|
900
2274
|
card.scrollIntoView({ block: "end", behavior: "smooth" });
|
|
901
2275
|
}
|
|
902
2276
|
function handleSelfImproveResult(data) {
|
|
903
|
-
const container =
|
|
2277
|
+
const container = getMessagesContainerForCurrent2();
|
|
904
2278
|
if (!container) return;
|
|
905
2279
|
const card = makeSelfImproveCard(data);
|
|
906
2280
|
const ok = !!data.success;
|
|
@@ -1019,14 +2393,14 @@
|
|
|
1019
2393
|
const container = messagesContainers.get(targetChannelId) || messagesEl;
|
|
1020
2394
|
if (msg.type === "ai") {
|
|
1021
2395
|
if (!MR_hasStreamingText()) {
|
|
1022
|
-
|
|
2396
|
+
addMessage2(msg.content, "ai", true, container, lastUsedJudgmentIds || []);
|
|
1023
2397
|
} else {
|
|
1024
2398
|
MR_replaceStreamingText?.(msg.content);
|
|
1025
2399
|
MR_finalizeTimelineAsMessage(getRendererCtx());
|
|
1026
2400
|
}
|
|
1027
2401
|
} else if (msg.type === "user") {
|
|
1028
2402
|
if (msg.source === "remote" || msg.source === "local") {
|
|
1029
|
-
|
|
2403
|
+
addMessage2(msg.content, "user", true, container);
|
|
1030
2404
|
}
|
|
1031
2405
|
}
|
|
1032
2406
|
}
|
|
@@ -1107,10 +2481,26 @@
|
|
|
1107
2481
|
const container = messagesContainers.get(msgChannelId) || messagesEl;
|
|
1108
2482
|
if (data.type === "user") {
|
|
1109
2483
|
if (data.source === "remote") {
|
|
1110
|
-
|
|
2484
|
+
addMessage2(data.content, "user", true, container);
|
|
1111
2485
|
}
|
|
1112
2486
|
} else if (data.type === "ai") {
|
|
1113
|
-
|
|
2487
|
+
addMessage2(data.content || "", "ai", true, container, lastUsedJudgmentIds || []);
|
|
2488
|
+
if (currentPreviewBubble) {
|
|
2489
|
+
currentPreviewBubble.remove();
|
|
2490
|
+
currentPreviewBubble = null;
|
|
2491
|
+
}
|
|
2492
|
+
} else if (data.type === "reply-preview") {
|
|
2493
|
+
const previewContent = data.content || "";
|
|
2494
|
+
if (!currentPreviewBubble) {
|
|
2495
|
+
currentPreviewBubble = addMessage2(previewContent, "ai", true, container, []);
|
|
2496
|
+
if (currentPreviewBubble) {
|
|
2497
|
+
currentPreviewBubble.classList.add("preview");
|
|
2498
|
+
}
|
|
2499
|
+
} else {
|
|
2500
|
+
currentPreviewBubble.replaceWith(addMessage2(previewContent, "ai", true, container, []));
|
|
2501
|
+
const msgs = container.querySelectorAll(".message-ai.preview");
|
|
2502
|
+
currentPreviewBubble = msgs.length > 0 ? msgs[msgs.length - 1] : null;
|
|
2503
|
+
}
|
|
1114
2504
|
} else if (data.type === "stream") {
|
|
1115
2505
|
if (false) handleStreamTokenEvent(data);
|
|
1116
2506
|
} else if (data.type === "regenerating") {
|
|
@@ -1121,12 +2511,12 @@
|
|
|
1121
2511
|
}
|
|
1122
2512
|
setSendMode("abort");
|
|
1123
2513
|
} else if (data.type === "status") {
|
|
1124
|
-
|
|
2514
|
+
renderLoopStatusBar2(data.tool, data.content);
|
|
1125
2515
|
} else if (data.type === "step_start" || data.type === "step_done" || data.type === "step_error") {
|
|
1126
|
-
|
|
2516
|
+
handleStepEvent2(data);
|
|
1127
2517
|
} else if (data.type === "done") {
|
|
1128
|
-
|
|
1129
|
-
|
|
2518
|
+
finalizeTimelineAsMessage2();
|
|
2519
|
+
hideLoopStatusBar2();
|
|
1130
2520
|
setSendMode("idle");
|
|
1131
2521
|
} else if (data.type === "renamed") {
|
|
1132
2522
|
const channel = channels.find((c) => c.id === data.channelId);
|
|
@@ -1134,18 +2524,18 @@
|
|
|
1134
2524
|
channel.name = data.newName;
|
|
1135
2525
|
renderChannels();
|
|
1136
2526
|
if (currentChannelId === data.channelId && channelNameEl) {
|
|
1137
|
-
channelNameEl.textContent =
|
|
2527
|
+
channelNameEl.textContent = safeChannelName(data.newName);
|
|
1138
2528
|
}
|
|
1139
2529
|
}
|
|
1140
2530
|
} else if (data.type === "error") {
|
|
1141
2531
|
const errContent = String(data.content || "\u672A\u77E5\u9519\u8BEF");
|
|
1142
|
-
|
|
2532
|
+
addMessage2(`\u26A0\uFE0F ${errContent}`, "ai", false, container);
|
|
1143
2533
|
if (typeof showSimpleToast === "function") {
|
|
1144
2534
|
showSimpleToast("\u26A0\uFE0F " + errContent.slice(0, 200));
|
|
1145
2535
|
} else {
|
|
1146
2536
|
console.error("[SSE] error:", errContent);
|
|
1147
2537
|
}
|
|
1148
|
-
|
|
2538
|
+
hideLoopStatusBar2();
|
|
1149
2539
|
setSendMode("idle");
|
|
1150
2540
|
} else if (data.type === "task_status" || data.type === "workflow_step" || data.type === "workflow_loop") {
|
|
1151
2541
|
if (data.type === "workflow_step" && (data.step === "AI \u601D\u8003" || data.step === "\u5F00\u59CB\u601D\u8003")) {
|
|
@@ -1171,11 +2561,15 @@
|
|
|
1171
2561
|
async function sendMessage() {
|
|
1172
2562
|
const text = input.value.trim();
|
|
1173
2563
|
if (!text) return;
|
|
2564
|
+
setSendMode("abort");
|
|
1174
2565
|
const container = messagesContainers.get(currentChannelId) || messagesEl;
|
|
1175
|
-
|
|
2566
|
+
addMessage2(text, "user", true, container);
|
|
1176
2567
|
if (container) container.scrollTop = container.scrollHeight;
|
|
2568
|
+
if (currentPreviewBubble) {
|
|
2569
|
+
currentPreviewBubble.remove();
|
|
2570
|
+
currentPreviewBubble = null;
|
|
2571
|
+
}
|
|
1177
2572
|
input.value = "";
|
|
1178
|
-
setSendMode("abort");
|
|
1179
2573
|
persistLastMessageToServer("user", text);
|
|
1180
2574
|
const channel = channels.find((c) => c.id === currentChannelId);
|
|
1181
2575
|
const channelDid = channel?.did || "";
|
|
@@ -1191,11 +2585,11 @@
|
|
|
1191
2585
|
})
|
|
1192
2586
|
});
|
|
1193
2587
|
if (!res.ok) {
|
|
1194
|
-
|
|
2588
|
+
addMessage2("\u53D1\u9001\u5931\u8D25", "ai");
|
|
1195
2589
|
setSendMode("idle");
|
|
1196
2590
|
}
|
|
1197
2591
|
} catch (err) {
|
|
1198
|
-
|
|
2592
|
+
addMessage2("\u8FDE\u63A5\u9519\u8BEF", "ai");
|
|
1199
2593
|
console.error("Send error", err);
|
|
1200
2594
|
setSendMode("idle");
|
|
1201
2595
|
}
|
|
@@ -1232,13 +2626,13 @@
|
|
|
1232
2626
|
abortCurrentRun();
|
|
1233
2627
|
}
|
|
1234
2628
|
});
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
2629
|
+
var mentionChannels = [];
|
|
2630
|
+
var mentionDropdownEl = null;
|
|
2631
|
+
var mentionHighlightIdx = -1;
|
|
2632
|
+
var mentionQuery = null;
|
|
2633
|
+
var mentionAnchor = -1;
|
|
2634
|
+
var mentionBlockEnd = -1;
|
|
2635
|
+
var mentionDocMousedownBound = false;
|
|
1242
2636
|
function ensureMentionDocMousedown() {
|
|
1243
2637
|
if (mentionDocMousedownBound) return;
|
|
1244
2638
|
mentionDocMousedownBound = true;
|
|
@@ -1257,7 +2651,7 @@
|
|
|
1257
2651
|
const remote = [];
|
|
1258
2652
|
for (const p of remoteData.peers || []) {
|
|
1259
2653
|
for (const c of p.channels || []) {
|
|
1260
|
-
remote.push({ id: c.id, name:
|
|
2654
|
+
remote.push({ id: c.id, name: safeChannelName(c.name, "(\u8FDC\u7AEF\u672A\u547D\u540D)"), source: "remote", ownerPublicKey: p.peerId });
|
|
1261
2655
|
}
|
|
1262
2656
|
}
|
|
1263
2657
|
mentionChannels = [
|
|
@@ -1305,9 +2699,9 @@
|
|
|
1305
2699
|
const owner = !isLocal && c.ownerPublicKey ? ` <span style="color:#9ca3af;font-size:11px;">(${c.ownerPublicKey.substring(0, 8)}\u2026)</span>` : "";
|
|
1306
2700
|
const bg = i === mentionHighlightIdx ? "#eff6ff" : "#fff";
|
|
1307
2701
|
const borderLeft = i === mentionHighlightIdx ? "3px solid #93c5fd" : "3px solid transparent";
|
|
1308
|
-
return `<div class="mention-item" data-idx="${i}" data-channel-id="${
|
|
2702
|
+
return `<div class="mention-item" data-idx="${i}" data-channel-id="${escapeHtml2(c.id)}" data-channel-name="${escapeHtml2(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};">
|
|
1309
2703
|
<span style="font-size:10px;color:${isLocal ? "#059669" : "#2563eb"};background:${isLocal ? "#d1fae5" : "#dbeafe"};padding:1px 6px;border-radius:3px;white-space:nowrap;">${tag}</span>
|
|
1310
|
-
<span style="flex:1;">${
|
|
2704
|
+
<span style="flex:1;">${escapeHtml2(safeChannelName(c.name))}</span>${owner}
|
|
1311
2705
|
</div>`;
|
|
1312
2706
|
}).join("");
|
|
1313
2707
|
mentionDropdownEl.innerHTML = headerHtml + rows;
|
|
@@ -1342,7 +2736,7 @@
|
|
|
1342
2736
|
}
|
|
1343
2737
|
const before = input.value.slice(0, anchor);
|
|
1344
2738
|
const after = input.value.slice(blockEnd);
|
|
1345
|
-
const insert = `@${
|
|
2739
|
+
const insert = `@${safeChannelName(channel.name)} `;
|
|
1346
2740
|
input.value = before + insert + after;
|
|
1347
2741
|
const newPos = before.length + insert.length;
|
|
1348
2742
|
input.focus();
|
|
@@ -1368,7 +2762,7 @@
|
|
|
1368
2762
|
}
|
|
1369
2763
|
mentionQuery = m.query;
|
|
1370
2764
|
const q = m.query.toLowerCase();
|
|
1371
|
-
const items = mentionChannels.filter((c) =>
|
|
2765
|
+
const items = mentionChannels.filter((c) => safeChannelName(c.name).toLowerCase().includes(q)).slice(0, 8);
|
|
1372
2766
|
mentionHighlightIdx = items.length > 0 ? 0 : -1;
|
|
1373
2767
|
renderMentionDropdown(items);
|
|
1374
2768
|
}
|
|
@@ -1383,21 +2777,21 @@
|
|
|
1383
2777
|
if (items.length === 0) return;
|
|
1384
2778
|
mentionHighlightIdx = (mentionHighlightIdx + 1) % items.length;
|
|
1385
2779
|
const q = (mentionQuery || "").toLowerCase();
|
|
1386
|
-
const filtered = mentionChannels.filter((c) =>
|
|
2780
|
+
const filtered = mentionChannels.filter((c) => safeChannelName(c.name).toLowerCase().includes(q)).slice(0, 8);
|
|
1387
2781
|
renderMentionDropdown(filtered);
|
|
1388
2782
|
} else if (e.key === "ArrowUp") {
|
|
1389
2783
|
e.preventDefault();
|
|
1390
2784
|
if (items.length === 0) return;
|
|
1391
2785
|
mentionHighlightIdx = (mentionHighlightIdx - 1 + items.length) % items.length;
|
|
1392
2786
|
const q = (mentionQuery || "").toLowerCase();
|
|
1393
|
-
const filtered = mentionChannels.filter((c) =>
|
|
2787
|
+
const filtered = mentionChannels.filter((c) => safeChannelName(c.name).toLowerCase().includes(q)).slice(0, 8);
|
|
1394
2788
|
renderMentionDropdown(filtered);
|
|
1395
2789
|
} else if (e.key === "Enter" || e.key === "Tab") {
|
|
1396
2790
|
if (items.length > 0) {
|
|
1397
2791
|
e.preventDefault();
|
|
1398
2792
|
e.stopPropagation();
|
|
1399
2793
|
const q = (mentionQuery || "").toLowerCase();
|
|
1400
|
-
const filtered = mentionChannels.filter((c) =>
|
|
2794
|
+
const filtered = mentionChannels.filter((c) => safeChannelName(c.name).toLowerCase().includes(q)).slice(0, 8);
|
|
1401
2795
|
const cur = filtered[mentionHighlightIdx];
|
|
1402
2796
|
if (cur) applyMention(cur);
|
|
1403
2797
|
}
|
|
@@ -1440,7 +2834,7 @@
|
|
|
1440
2834
|
}
|
|
1441
2835
|
const before = inputEl.value.slice(0, anchor);
|
|
1442
2836
|
const after = inputEl.value.slice(blockEnd);
|
|
1443
|
-
const insert = `@${
|
|
2837
|
+
const insert = `@${safeChannelName(channel.name)} `;
|
|
1444
2838
|
inputEl.value = before + insert + after;
|
|
1445
2839
|
const newPos = before.length + insert.length;
|
|
1446
2840
|
inputEl.focus();
|
|
@@ -1466,9 +2860,9 @@
|
|
|
1466
2860
|
const owner = !isLocal && c.ownerPublicKey ? ` <span style="color:#9ca3af;font-size:11px;">(${c.ownerPublicKey.substring(0, 8)}\u2026)</span>` : "";
|
|
1467
2861
|
const bg = i === localHighlight ? "#eff6ff" : "#fff";
|
|
1468
2862
|
const borderLeft = i === localHighlight ? "3px solid #93c5fd" : "3px solid transparent";
|
|
1469
|
-
return `<div class="mention-item" data-idx="${i}" data-channel-id="${
|
|
2863
|
+
return `<div class="mention-item" data-idx="${i}" data-channel-id="${escapeHtml2(c.id)}" data-channel-name="${escapeHtml2(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};">
|
|
1470
2864
|
<span style="font-size:10px;color:${isLocal ? "#059669" : "#2563eb"};background:${isLocal ? "#d1fae5" : "#dbeafe"};padding:1px 6px;border-radius:3px;white-space:nowrap;">${tag}</span>
|
|
1471
|
-
<span style="flex:1;">${
|
|
2865
|
+
<span style="flex:1;">${escapeHtml2(safeChannelName(c.name))}</span>${owner}
|
|
1472
2866
|
</div>`;
|
|
1473
2867
|
}).join("");
|
|
1474
2868
|
inputEl.__mentionDD.querySelectorAll(".mention-item").forEach((el) => {
|
|
@@ -1510,7 +2904,7 @@
|
|
|
1510
2904
|
}
|
|
1511
2905
|
localQuery = m.query;
|
|
1512
2906
|
const q = m.query.toLowerCase();
|
|
1513
|
-
const items = mentionChannels.filter((c) =>
|
|
2907
|
+
const items = mentionChannels.filter((c) => safeChannelName(c.name).toLowerCase().includes(q)).slice(0, 8);
|
|
1514
2908
|
localHighlight = items.length > 0 ? 0 : -1;
|
|
1515
2909
|
renderLocal(items);
|
|
1516
2910
|
}
|
|
@@ -1523,19 +2917,19 @@
|
|
|
1523
2917
|
if (items.length === 0) return;
|
|
1524
2918
|
localHighlight = (localHighlight + 1) % items.length;
|
|
1525
2919
|
const q = (localQuery || "").toLowerCase();
|
|
1526
|
-
renderLocal(mentionChannels.filter((c) =>
|
|
2920
|
+
renderLocal(mentionChannels.filter((c) => safeChannelName(c.name).toLowerCase().includes(q)).slice(0, 8));
|
|
1527
2921
|
} else if (e.key === "ArrowUp") {
|
|
1528
2922
|
e.preventDefault();
|
|
1529
2923
|
if (items.length === 0) return;
|
|
1530
2924
|
localHighlight = (localHighlight - 1 + items.length) % items.length;
|
|
1531
2925
|
const q = (localQuery || "").toLowerCase();
|
|
1532
|
-
renderLocal(mentionChannels.filter((c) =>
|
|
2926
|
+
renderLocal(mentionChannels.filter((c) => safeChannelName(c.name).toLowerCase().includes(q)).slice(0, 8));
|
|
1533
2927
|
} else if (e.key === "Enter" || e.key === "Tab") {
|
|
1534
2928
|
if (items.length > 0) {
|
|
1535
2929
|
e.preventDefault();
|
|
1536
2930
|
e.stopPropagation();
|
|
1537
2931
|
const q = (localQuery || "").toLowerCase();
|
|
1538
|
-
const filtered = mentionChannels.filter((c) =>
|
|
2932
|
+
const filtered = mentionChannels.filter((c) => safeChannelName(c.name).toLowerCase().includes(q)).slice(0, 8);
|
|
1539
2933
|
const cur = filtered[localHighlight];
|
|
1540
2934
|
if (cur) applyLocal(cur);
|
|
1541
2935
|
}
|
|
@@ -1545,7 +2939,7 @@
|
|
|
1545
2939
|
}
|
|
1546
2940
|
}, true);
|
|
1547
2941
|
}
|
|
1548
|
-
|
|
2942
|
+
var inputArea = document.querySelector(".input-area");
|
|
1549
2943
|
if (input && inputArea) {
|
|
1550
2944
|
const onDragOver = (e) => {
|
|
1551
2945
|
if (e.dataTransfer && Array.from(e.dataTransfer.types || []).includes("application/x-bolloon-judgment")) {
|
|
@@ -1584,14 +2978,14 @@
|
|
|
1584
2978
|
if (themeToggle) {
|
|
1585
2979
|
themeToggle.addEventListener("click", toggleTheme);
|
|
1586
2980
|
}
|
|
1587
|
-
|
|
2981
|
+
var apiConfigBtn = document.getElementById("api-config-btn");
|
|
1588
2982
|
if (apiConfigBtn) {
|
|
1589
2983
|
apiConfigBtn.addEventListener("click", () => {
|
|
1590
2984
|
window.location.href = "/api-config";
|
|
1591
2985
|
});
|
|
1592
2986
|
}
|
|
1593
|
-
|
|
1594
|
-
|
|
2987
|
+
var walletBtn = document.getElementById("wallet-btn");
|
|
2988
|
+
var walletBadge = document.getElementById("wallet-badge");
|
|
1595
2989
|
if (walletBtn) {
|
|
1596
2990
|
walletBtn.addEventListener("click", openWalletModal);
|
|
1597
2991
|
}
|
|
@@ -1663,7 +3057,7 @@
|
|
|
1663
3057
|
await createChannel("\u9ED8\u8BA4\u4F1A\u8BDD");
|
|
1664
3058
|
}
|
|
1665
3059
|
}
|
|
1666
|
-
|
|
3060
|
+
var p2pNetworkBtn = document.getElementById("p2p-network-btn");
|
|
1667
3061
|
if (p2pNetworkBtn) {
|
|
1668
3062
|
p2pNetworkBtn.addEventListener("click", () => {
|
|
1669
3063
|
if (typeof window.showP2PModal === "function") {
|
|
@@ -1671,18 +3065,18 @@
|
|
|
1671
3065
|
}
|
|
1672
3066
|
});
|
|
1673
3067
|
}
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
3068
|
+
var judgmentsModal = document.getElementById("judgments-modal");
|
|
3069
|
+
var judgmentsBtn = document.getElementById("judgments-btn");
|
|
3070
|
+
var judgmentsModalClose = document.getElementById("judgments-modal-close");
|
|
3071
|
+
var judgmentDecision = document.getElementById("judgment-decision");
|
|
3072
|
+
var judgmentReason = document.getElementById("judgment-reason");
|
|
3073
|
+
var judgmentDomain = document.getElementById("judgment-domain");
|
|
3074
|
+
var judgmentStakes = document.getElementById("judgment-stakes");
|
|
3075
|
+
var judgmentSubmitBtn = document.getElementById("judgment-submit-btn");
|
|
3076
|
+
var judgmentError = document.getElementById("judgment-error");
|
|
3077
|
+
var judgmentsList = document.getElementById("judgments-list");
|
|
3078
|
+
var judgmentsBadge = document.getElementById("judgments-badge");
|
|
3079
|
+
var judgmentsLoaded = false;
|
|
1686
3080
|
function showJudgmentsModal() {
|
|
1687
3081
|
if (judgmentsModal) judgmentsModal.classList.add("active");
|
|
1688
3082
|
if (!judgmentsLoaded) loadJudgments();
|
|
@@ -1720,9 +3114,9 @@
|
|
|
1720
3114
|
function hideJudgmentsModal() {
|
|
1721
3115
|
if (judgmentsModal) judgmentsModal.classList.remove("active");
|
|
1722
3116
|
}
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
3117
|
+
var currentJudgmentTab = "channel";
|
|
3118
|
+
var currentStatusFilter = "all";
|
|
3119
|
+
var lastJudgmentsCache = [];
|
|
1726
3120
|
function renderJudgments(items) {
|
|
1727
3121
|
if (!judgmentsList) return;
|
|
1728
3122
|
const all = items || [];
|
|
@@ -1730,7 +3124,7 @@
|
|
|
1730
3124
|
const chNameEl = document.getElementById("judgments-tab-channel-name");
|
|
1731
3125
|
const currentCh = currentChannelId ? channels.find((c) => c.id === currentChannelId) : null;
|
|
1732
3126
|
if (chNameEl) {
|
|
1733
|
-
chNameEl.textContent = currentCh ? `(${
|
|
3127
|
+
chNameEl.textContent = currentCh ? `(${safeChannelName(currentCh.name)})` : "(\u672A\u9009)";
|
|
1734
3128
|
}
|
|
1735
3129
|
if (all.length === 0) {
|
|
1736
3130
|
judgmentsList.innerHTML = '<div class="task-empty">\u8FD8\u6CA1\u6709\u5224\u65AD, \u5728\u4E0A\u9762\u8BB0\u5F55\u7B2C\u4E00\u6761\u5427</div>';
|
|
@@ -1756,7 +3150,7 @@
|
|
|
1756
3150
|
);
|
|
1757
3151
|
const bound = all.filter((j) => boundIds.has(j.id));
|
|
1758
3152
|
const unbound = all.filter((j) => !boundIds.has(j.id));
|
|
1759
|
-
if (titleEl) titleEl.textContent = `${
|
|
3153
|
+
if (titleEl) titleEl.textContent = `${safeChannelName(currentCh.name)} \u7684\u5224\u65AD\u529B (\u5DF2\u7ED1 ${bound.length} / \u5171 ${all.length})`;
|
|
1760
3154
|
let html = "";
|
|
1761
3155
|
if (bound.length > 0) {
|
|
1762
3156
|
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>`;
|
|
@@ -1771,37 +3165,37 @@
|
|
|
1771
3165
|
function renderJudgmentItems(items, opts) {
|
|
1772
3166
|
const { showBindToggle, isBound } = opts || {};
|
|
1773
3167
|
return items.map((j) => {
|
|
1774
|
-
const reason = j.reasons && j.reasons[0] ?
|
|
1775
|
-
const domain = j.context && j.context.domain ?
|
|
1776
|
-
const stakes = j.context && j.context.stakes ?
|
|
3168
|
+
const reason = j.reasons && j.reasons[0] ? escapeHtml2(j.reasons[0]) : "";
|
|
3169
|
+
const domain = j.context && j.context.domain ? escapeHtml2(j.context.domain) : "general";
|
|
3170
|
+
const stakes = j.context && j.context.stakes ? escapeHtml2(j.context.stakes) : "medium";
|
|
1777
3171
|
const isSuperseded = j.status === "superseded";
|
|
1778
3172
|
const isRejected = j.status === "rejected";
|
|
1779
3173
|
const dimmedStyle = isSuperseded || isRejected ? "opacity:0.55;background:#f3f4f6;" : "";
|
|
1780
3174
|
const statusTag = isSuperseded ? `<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>` : isRejected ? `<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>` : "";
|
|
1781
|
-
const evolveNote = isSuperseded && j.supersededBy ? `<div style="font-size:10px;color:#6b7280;margin-top:2px;">\u88AB\u65B0\u6761\u66FF\u4EE3 \xB7 ${
|
|
1782
|
-
const bindBtn = showBindToggle ? isBound ? `<button class="judgment-toggle-btn" data-id="${
|
|
3175
|
+
const evolveNote = isSuperseded && j.supersededBy ? `<div style="font-size:10px;color:#6b7280;margin-top:2px;">\u88AB\u65B0\u6761\u66FF\u4EE3 \xB7 ${escapeHtml2(j.evolutionReason || "merged")} \xB7 ${escapeHtml2(j.evolvedAt || "").substring(0, 10)}</div>` : "";
|
|
3176
|
+
const bindBtn = showBindToggle ? isBound ? `<button class="judgment-toggle-btn" data-id="${escapeHtml2(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;">\xD7 \u79FB\u9664</button>` : `<button class="judgment-toggle-btn" data-id="${escapeHtml2(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>` : "";
|
|
1783
3177
|
return `
|
|
1784
3178
|
<div class="task-item completed judgment-row"
|
|
1785
|
-
data-judgment-id="${
|
|
3179
|
+
data-judgment-id="${escapeHtml2(j.id)}"
|
|
1786
3180
|
draggable="true"
|
|
1787
3181
|
style="cursor:grab;${dimmedStyle}">
|
|
1788
3182
|
<div class="task-item-header">
|
|
1789
3183
|
<label class="judgment-checkbox" style="display:flex;align-items:center;cursor:pointer;margin-right:8px;" onclick="event.stopPropagation();">
|
|
1790
|
-
<input type="checkbox" class="judgment-select-cb" data-id="${
|
|
3184
|
+
<input type="checkbox" class="judgment-select-cb" data-id="${escapeHtml2(j.id)}" style="cursor:pointer;" onclick="event.stopPropagation();">
|
|
1791
3185
|
</label>
|
|
1792
3186
|
<div class="task-item-title">
|
|
1793
|
-
<span class="judgment-decision">${
|
|
3187
|
+
<span class="judgment-decision">${escapeHtml2(j.decision)}</span>${statusTag}
|
|
1794
3188
|
</div>
|
|
1795
3189
|
<span class="task-item-status completed">${stakes}</span>
|
|
1796
3190
|
</div>
|
|
1797
3191
|
${reason ? `<div class="task-item-desc" style="color:#555;font-size:13px;margin-top:4px;">\u7406\u7531: ${reason}</div>` : ""}
|
|
1798
3192
|
${evolveNote}
|
|
1799
3193
|
<div class="task-item-meta" style="color:#999;font-size:11px;margin-top:4px;display:flex;justify-content:space-between;align-items:center;">
|
|
1800
|
-
<span>${domain} \xB7 ${
|
|
3194
|
+
<span>${domain} \xB7 ${escapeHtml2(j.timestamp)} \xB7 ${escapeHtml2(j.id)}</span>
|
|
1801
3195
|
<span style="display:flex;gap:4px;">
|
|
1802
3196
|
${bindBtn}
|
|
1803
|
-
<button class="judgment-edit-btn" data-id="${
|
|
1804
|
-
<button class="judgment-del-btn" data-id="${
|
|
3197
|
+
<button class="judgment-edit-btn" data-id="${escapeHtml2(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>
|
|
3198
|
+
<button class="judgment-del-btn" data-id="${escapeHtml2(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>
|
|
1805
3199
|
</span>
|
|
1806
3200
|
</div>
|
|
1807
3201
|
</div>
|
|
@@ -1856,7 +3250,7 @@
|
|
|
1856
3250
|
}
|
|
1857
3251
|
judgmentsLoaded = true;
|
|
1858
3252
|
} catch (e) {
|
|
1859
|
-
if (judgmentsList) judgmentsList.innerHTML = '<div class="task-empty">\u52A0\u8F7D\u5931\u8D25: ' +
|
|
3253
|
+
if (judgmentsList) judgmentsList.innerHTML = '<div class="task-empty">\u52A0\u8F7D\u5931\u8D25: ' + escapeHtml2(e.message) + "</div>";
|
|
1860
3254
|
}
|
|
1861
3255
|
}
|
|
1862
3256
|
function renderViolations(items) {
|
|
@@ -1866,18 +3260,18 @@
|
|
|
1866
3260
|
return;
|
|
1867
3261
|
}
|
|
1868
3262
|
judgmentsList.innerHTML = items.map((v) => {
|
|
1869
|
-
const ts =
|
|
1870
|
-
const userPrev =
|
|
1871
|
-
const aiPrev =
|
|
3263
|
+
const ts = escapeHtml2((v.ts || "").substring(0, 19).replace("T", " "));
|
|
3264
|
+
const userPrev = escapeHtml2(v.userInputPreview || "");
|
|
3265
|
+
const aiPrev = escapeHtml2(v.aiReplyPreview || "");
|
|
1872
3266
|
const principles = (v.result?.violatedPrinciples || []).map(
|
|
1873
3267
|
(p) => `<div style="margin-top:3px;padding:4px 8px;background:#fef2f2;border-radius:3px;">
|
|
1874
|
-
<span style="color:#dc2626;">\u26A0</span> <strong>${
|
|
1875
|
-
<span style="color:#991b1b;">\u2014 ${
|
|
3268
|
+
<span style="color:#dc2626;">\u26A0</span> <strong>${escapeHtml2(p.principle || "")}</strong>
|
|
3269
|
+
<span style="color:#991b1b;">\u2014 ${escapeHtml2(p.reason || "")}</span>
|
|
1876
3270
|
</div>`
|
|
1877
3271
|
).join("");
|
|
1878
3272
|
return `
|
|
1879
3273
|
<div class="task-item" style="border-left:3px solid #dc2626;padding:8px 12px;background:#fffbfb;">
|
|
1880
|
-
<div style="font-size:11px;color:#6b7280;margin-bottom:4px;">${ts} \xB7 confidence=${
|
|
3274
|
+
<div style="font-size:11px;color:#6b7280;margin-bottom:4px;">${ts} \xB7 confidence=${escapeHtml2(String(v.result?.confidence ?? 0))}</div>
|
|
1881
3275
|
<div style="font-size:12px;color:#1f2937;"><strong>\u7528\u6237:</strong> ${userPrev}</div>
|
|
1882
3276
|
<div style="font-size:12px;color:#1f2937;margin-top:2px;"><strong>AI:</strong> ${aiPrev}</div>
|
|
1883
3277
|
<div style="margin-top:6px;">${principles}</div>
|
|
@@ -1888,7 +3282,7 @@
|
|
|
1888
3282
|
function renderAdaptiveSuggestions(data) {
|
|
1889
3283
|
if (!judgmentsList) return;
|
|
1890
3284
|
const { judgmentsTotal, usageEntriesScanned, suggestions, scannedAt } = data;
|
|
1891
|
-
const ts =
|
|
3285
|
+
const ts = escapeHtml2((scannedAt || "").substring(0, 19).replace("T", " "));
|
|
1892
3286
|
if (!suggestions || suggestions.length === 0) {
|
|
1893
3287
|
judgmentsList.innerHTML = `
|
|
1894
3288
|
<div class="task-empty">\u{1F4CA} \u81EA\u9002\u5E94\u626B\u63CF: \u65E0\u5EFA\u8BAE
|
|
@@ -1912,21 +3306,21 @@
|
|
|
1912
3306
|
const style = KIND_STYLE[s.kind] || KIND_STYLE.unused;
|
|
1913
3307
|
const m = s.metrics || {};
|
|
1914
3308
|
return `
|
|
1915
|
-
<div class="task-item" data-suggestion-key="${
|
|
3309
|
+
<div class="task-item" data-suggestion-key="${escapeHtml2(s.key)}"
|
|
1916
3310
|
style="border-left:3px solid ${style.color};padding:8px 12px;background:${style.bg};margin-bottom:6px;">
|
|
1917
3311
|
<div style="display:flex;align-items:center;gap:6px;margin-bottom:4px;">
|
|
1918
3312
|
<span style="color:${style.color};font-weight:600;font-size:12px;">${style.label}</span>
|
|
1919
3313
|
<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>
|
|
1920
3314
|
</div>
|
|
1921
|
-
<div style="font-size:12px;color:#1f2937;margin-bottom:4px;"><strong>${
|
|
1922
|
-
<div style="font-size:11px;color:#6b7280;margin-bottom:6px;">${
|
|
3315
|
+
<div style="font-size:12px;color:#1f2937;margin-bottom:4px;"><strong>${escapeHtml2(s.decision)}</strong></div>
|
|
3316
|
+
<div style="font-size:11px;color:#6b7280;margin-bottom:6px;">${escapeHtml2(s.reason)}</div>
|
|
1923
3317
|
<div style="font-size:11px;color:#9ca3af;margin-bottom:6px;">
|
|
1924
3318
|
7\u5929 ${m.usage7d || 0} \xB7 30\u5929 ${m.usage30d || 0} \xB7 \u5171 ${m.totalUsage || 0} \xB7 \u4E0A\u6B21\u7528 ${m.daysSinceLastUse || 0} \u5929\u524D
|
|
1925
3319
|
</div>
|
|
1926
3320
|
<div style="display:flex;gap:6px;">
|
|
1927
|
-
<button class="adaptive-accept" data-key="${
|
|
3321
|
+
<button class="adaptive-accept" data-key="${escapeHtml2(s.key)}" data-id="${escapeHtml2(s.judgmentId)}" data-action-kind="${escapeHtml2(s.action)}"
|
|
1928
3322
|
style="background:#059669;color:#fff;border:none;padding:2px 10px;border-radius:3px;cursor:pointer;font-size:11px;">\u2713 \u63A5\u53D7</button>
|
|
1929
|
-
<button class="adaptive-reject" data-key="${
|
|
3323
|
+
<button class="adaptive-reject" data-key="${escapeHtml2(s.key)}" data-id="${escapeHtml2(s.judgmentId)}" data-action-kind="${escapeHtml2(s.action)}"
|
|
1930
3324
|
style="background:none;border:1px solid #d1d5db;color:#6b7280;padding:2px 10px;border-radius:3px;cursor:pointer;font-size:11px;">\u2717 \u62D2\u7EDD</button>
|
|
1931
3325
|
</div>
|
|
1932
3326
|
</div>
|
|
@@ -1995,18 +3389,18 @@
|
|
|
1995
3389
|
return;
|
|
1996
3390
|
}
|
|
1997
3391
|
const rows = items.map((p, idx) => `
|
|
1998
|
-
<div class="task-item" data-causal-idx="${idx}" data-judgment-a="${
|
|
3392
|
+
<div class="task-item" data-causal-idx="${idx}" data-judgment-a="${escapeHtml2(p.judgmentA)}" data-judgment-b="${escapeHtml2(p.judgmentB)}"
|
|
1999
3393
|
style="border-left:3px solid #7c3aed;padding:8px 12px;background:#faf5ff;margin-bottom:6px;">
|
|
2000
3394
|
<div style="display:flex;align-items:center;gap:6px;margin-bottom:4px;">
|
|
2001
|
-
<span style="color:#7c3aed;font-weight:600;font-size:12px;">${
|
|
3395
|
+
<span style="color:#7c3aed;font-weight:600;font-size:12px;">${escapeHtml2(p.causalDirection)}</span>
|
|
2002
3396
|
<span style="font-size:11px;color:#6b7280;">MI=${p.mutualInfo} \xB7 co=${p.coOccurrence}</span>
|
|
2003
3397
|
</div>
|
|
2004
|
-
<div style="font-size:11px;color:#374151;margin-bottom:4px;">${
|
|
2005
|
-
<div style="font-size:10px;color:#9ca3af;">A: ${
|
|
3398
|
+
<div style="font-size:11px;color:#374151;margin-bottom:4px;">${escapeHtml2(p.explanation || "(\u65E0 LLM \u89E3\u91CA)")}</div>
|
|
3399
|
+
<div style="font-size:10px;color:#9ca3af;">A: ${escapeHtml2(p.judgmentA)} \u2194 B: ${escapeHtml2(p.judgmentB)}</div>
|
|
2006
3400
|
<div style="margin-top:6px;display:flex;gap:6px;">
|
|
2007
|
-
<button class="causal-intervention-a" data-jid="${
|
|
3401
|
+
<button class="causal-intervention-a" data-jid="${escapeHtml2(p.judgmentA)}"
|
|
2008
3402
|
style="background:#7c3aed;color:#fff;border:none;padding:2px 10px;border-radius:3px;cursor:pointer;font-size:11px;">\u{1F52C} do(A)</button>
|
|
2009
|
-
<button class="causal-intervention-b" data-jid="${
|
|
3403
|
+
<button class="causal-intervention-b" data-jid="${escapeHtml2(p.judgmentB)}"
|
|
2010
3404
|
style="background:#7c3aed;color:#fff;border:none;padding:2px 10px;border-radius:3px;cursor:pointer;font-size:11px;">\u{1F52C} do(B)</button>
|
|
2011
3405
|
</div>
|
|
2012
3406
|
<div class="causal-result" data-jid="" style="display:none;margin-top:6px;padding:6px;background:#f3e8ff;border-radius:3px;font-size:11px;"></div>
|
|
@@ -2050,11 +3444,11 @@
|
|
|
2050
3444
|
const color = Math.abs(effect) > 0.5 ? "#dc2626" : Math.abs(effect) > 0.2 ? "#d97706" : "#059669";
|
|
2051
3445
|
resultDiv.innerHTML = `
|
|
2052
3446
|
<div style="color:${color};font-weight:600;">do-calculus: causalEffect = ${sign}${effect} (${data.marginalContribution})</div>
|
|
2053
|
-
<div style="color:#374151;margin-top:4px;">${
|
|
3447
|
+
<div style="color:#374151;margin-top:4px;">${escapeHtml2(data.reasoning)}</div>
|
|
2054
3448
|
<div style="color:#9ca3af;margin-top:4px;">confidence=${data.confidence}</div>
|
|
2055
3449
|
`;
|
|
2056
3450
|
} catch (err) {
|
|
2057
|
-
resultDiv.innerHTML = `<div style="color:#dc2626;">\u5931\u8D25: ${
|
|
3451
|
+
resultDiv.innerHTML = `<div style="color:#dc2626;">\u5931\u8D25: ${escapeHtml2(err.message)}</div>`;
|
|
2058
3452
|
} finally {
|
|
2059
3453
|
btn.disabled = false;
|
|
2060
3454
|
}
|
|
@@ -2138,9 +3532,9 @@
|
|
|
2138
3532
|
}
|
|
2139
3533
|
});
|
|
2140
3534
|
}
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
3535
|
+
var judgmentSelectAll = document.getElementById("judgment-select-all");
|
|
3536
|
+
var judgmentSelectedCount = document.getElementById("judgment-selected-count");
|
|
3537
|
+
var judgmentBulkDeleteBtn = document.getElementById("judgment-bulk-delete-btn");
|
|
2144
3538
|
function getSelectedJudgmentIds() {
|
|
2145
3539
|
if (!judgmentsList) return [];
|
|
2146
3540
|
return Array.from(judgmentsList.querySelectorAll(".judgment-select-cb")).filter((cb) => cb.checked).map((cb) => cb.getAttribute("data-id")).filter(Boolean);
|
|
@@ -2288,8 +3682,37 @@
|
|
|
2288
3682
|
if (e.target === judgmentsModal) hideJudgmentsModal();
|
|
2289
3683
|
});
|
|
2290
3684
|
}
|
|
2291
|
-
|
|
2292
|
-
|
|
3685
|
+
var judgmentImportBtn = document.getElementById("judgment-import-btn");
|
|
3686
|
+
var judgmentImportFile = document.getElementById("judgment-import-file");
|
|
3687
|
+
var judgmentCleanupBtn = document.getElementById("judgment-cleanup-btn");
|
|
3688
|
+
async function runCleanupJudgments(dryRun) {
|
|
3689
|
+
const url = dryRun ? "/api/judgments/cleanup-dry" : "/api/judgments/cleanup";
|
|
3690
|
+
const method = dryRun ? "GET" : "POST";
|
|
3691
|
+
if (judgmentCleanupBtn) judgmentCleanupBtn.disabled = true;
|
|
3692
|
+
const origText = judgmentCleanupBtn?.textContent;
|
|
3693
|
+
if (judgmentCleanupBtn) judgmentCleanupBtn.textContent = dryRun ? "\u{1F50D} \u626B\u63CF\u2026" : "\u2699\uFE0F \u6E05\u7406\u4E2D\u2026";
|
|
3694
|
+
try {
|
|
3695
|
+
const res = await fetch(url, { method });
|
|
3696
|
+
const json = await res.json().catch(() => ({}));
|
|
3697
|
+
if (!res.ok) {
|
|
3698
|
+
showJudgmentError("\u6E05\u7406\u5931\u8D25: " + (json.error || res.status));
|
|
3699
|
+
return;
|
|
3700
|
+
}
|
|
3701
|
+
if (dryRun) {
|
|
3702
|
+
showJudgmentOk(`\u626B\u63CF: ${json.totalBefore} \u6761 \u2192 \u4FDD\u7559 ${json.totalAfter}, \u5C06\u88AB\u8F6F\u5220\u9664 ${json.removed} \u6761 (loadAll \u6D4B\u8BD5/\u6D4B\u8BD5\u539F\u5219\u7B49\u542F\u53D1\u5F0F)`);
|
|
3703
|
+
} else {
|
|
3704
|
+
showJudgmentOk(`\u6E05\u7406\u5B8C\u6210: ${json.totalBefore} \u2192 ${json.totalAfter} \u6761 (\u8F6F\u5220\u9664 ${json.removed} \u6761)`);
|
|
3705
|
+
if (typeof loadJudgments === "function") await loadJudgments();
|
|
3706
|
+
}
|
|
3707
|
+
} catch (err) {
|
|
3708
|
+
showJudgmentError("\u6E05\u7406\u8BF7\u6C42\u5931\u8D25: " + (err?.message || err));
|
|
3709
|
+
} finally {
|
|
3710
|
+
if (judgmentCleanupBtn) {
|
|
3711
|
+
judgmentCleanupBtn.disabled = false;
|
|
3712
|
+
judgmentCleanupBtn.textContent = origText || "\u6E05\u7406\u6D4B\u8BD5\u6570\u636E";
|
|
3713
|
+
}
|
|
3714
|
+
}
|
|
3715
|
+
}
|
|
2293
3716
|
function showJudgmentError(msg) {
|
|
2294
3717
|
if (!judgmentError) return;
|
|
2295
3718
|
judgmentError.textContent = msg;
|
|
@@ -2303,12 +3726,12 @@
|
|
|
2303
3726
|
judgmentError.style.color = "#15803d";
|
|
2304
3727
|
}
|
|
2305
3728
|
function fileToBase64(file) {
|
|
2306
|
-
return new Promise((
|
|
3729
|
+
return new Promise((resolve2, reject) => {
|
|
2307
3730
|
const r = new FileReader();
|
|
2308
3731
|
r.onload = () => {
|
|
2309
3732
|
const s = String(r.result || "");
|
|
2310
3733
|
const idx = s.indexOf(",");
|
|
2311
|
-
|
|
3734
|
+
resolve2(idx >= 0 ? s.substring(idx + 1) : s);
|
|
2312
3735
|
};
|
|
2313
3736
|
r.onerror = () => reject(r.error || new Error("read failed"));
|
|
2314
3737
|
r.readAsDataURL(file);
|
|
@@ -2346,6 +3769,15 @@
|
|
|
2346
3769
|
if (f) importJudgmentFile(f);
|
|
2347
3770
|
});
|
|
2348
3771
|
}
|
|
3772
|
+
if (judgmentCleanupBtn) {
|
|
3773
|
+
judgmentCleanupBtn.addEventListener("click", async () => {
|
|
3774
|
+
if (!window.confirm("\u5C06\u4F1A\u8F6F\u5220\u9664\u6240\u6709\u300C\u6D4B\u8BD5\u704C\u6C34\u300D\u5224\u65AD\u529B (loadAll \u6D4B\u8BD5/\u6D4B\u8BD5\u539F\u5219\u7B49\u542F\u53D1\u5F0F\u5339\u914D).\n\u4E0B\u4E00\u6B65\u5C06\u5148 dry-run \u9884\u89C8, \u4E8C\u6B21\u786E\u8BA4\u518D\u771F\u6E05.")) return;
|
|
3775
|
+
const dry = await runCleanupJudgments(true);
|
|
3776
|
+
if (dry === false) return;
|
|
3777
|
+
if (!window.confirm("\u786E\u8BA4\u6E05\u7406\u5417? \u8F6F\u5220\u9664\u53EF\u8FFD\u6EAF, \u72B6\u6001\u6807\u8BB0\u4E3A rejected, \u4E0D\u5F71\u54CD\u5DF2 active \u6570\u636E.")) return;
|
|
3778
|
+
await runCleanupJudgments(false);
|
|
3779
|
+
});
|
|
3780
|
+
}
|
|
2349
3781
|
document.addEventListener("click", async (e) => {
|
|
2350
3782
|
const btn = e.target.closest && e.target.closest(".save-as-judgment");
|
|
2351
3783
|
if (!btn) return;
|
|
@@ -2445,8 +3877,8 @@
|
|
|
2445
3877
|
}
|
|
2446
3878
|
popup.innerHTML = `
|
|
2447
3879
|
<div style="font-weight:600;margin-bottom:4px;">\u5DF2\u84B8\u998F\u4E3A\u5224\u65AD\u529B</div>
|
|
2448
|
-
<div style="background:#f9fafb;padding:6px 8px;border-radius:4px;line-height:1.4;">${
|
|
2449
|
-
${evidence ? `<div style="font-size:11px;color:#6b7280;margin-top:4px;">\u8BC1\u636E: ${
|
|
3880
|
+
<div style="background:#f9fafb;padding:6px 8px;border-radius:4px;line-height:1.4;">${escapeHtml2(value)}</div>
|
|
3881
|
+
${evidence ? `<div style="font-size:11px;color:#6b7280;margin-top:4px;">\u8BC1\u636E: ${escapeHtml2(evidence)}</div>` : ""}
|
|
2450
3882
|
${evolveNote}
|
|
2451
3883
|
<div style="display:flex;gap:6px;margin-top:8px;">
|
|
2452
3884
|
<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>
|
|
@@ -2473,7 +3905,7 @@
|
|
|
2473
3905
|
if (judgmentSubmitBtn) judgmentSubmitBtn.addEventListener("click", submitJudgment);
|
|
2474
3906
|
loadJudgments();
|
|
2475
3907
|
setInterval(loadJudgments, 1e4);
|
|
2476
|
-
|
|
3908
|
+
var knownPeers = [];
|
|
2477
3909
|
async function loadRemoteChannels() {
|
|
2478
3910
|
try {
|
|
2479
3911
|
const res = await fetch("/api/p2p-peers");
|
|
@@ -2536,22 +3968,22 @@
|
|
|
2536
3968
|
const caretChar = "\u25BE";
|
|
2537
3969
|
return `
|
|
2538
3970
|
<li class="remote-peer-group ${isCollapsed ? "collapsed" : ""}" style="margin-bottom:10px;${strangerStyle}">
|
|
2539
|
-
<div class="remote-peer-header" data-peer-name="${
|
|
3971
|
+
<div class="remote-peer-header" data-peer-name="${escapeHtml2(peer.name)}" data-peer-pk="${escapeHtml2(peer.publicKey)}"
|
|
2540
3972
|
style="display:flex;align-items:center;gap:6px;padding:6px 8px;background:var(--bg-hover);border-radius:4px;cursor:pointer;">
|
|
2541
|
-
<button class="peer-caret-btn" data-toggle-peer="${
|
|
3973
|
+
<button class="peer-caret-btn" data-toggle-peer="${escapeHtml2(peer.publicKey)}" title="\u6298\u53E0/\u5C55\u5F00"
|
|
2542
3974
|
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>
|
|
2543
3975
|
<span style="font-size:13px;">${strangerIcon}</span>
|
|
2544
|
-
<span style="flex:1;font-size:12px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;" title="${
|
|
3976
|
+
<span style="flex:1;font-size:12px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;" title="${escapeHtml2(peer.publicKey)}">${escapeHtml2(peer.name)}</span>
|
|
2545
3977
|
<span style="font-size:9px;color:var(--text-muted);">${peerChannels.length > 0 ? `${peerChannels.length} ch \xB7 ` : ""}${lastConn}</span>
|
|
2546
|
-
<button class="peer-share-btn" title="\u5206\u4EAB channel \u7ED9 ${
|
|
3978
|
+
<button class="peer-share-btn" title="\u5206\u4EAB channel \u7ED9 ${escapeHtml2(peer.name)}"
|
|
2547
3979
|
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;">\u{1F4E4}</button>
|
|
2548
3980
|
</div>
|
|
2549
3981
|
<div class="remote-peer-channels" style="margin-top:4px;margin-left:8px;">
|
|
2550
3982
|
${peerChannels.length === 0 ? '<div style="font-size:10px;color:var(--text-muted);padding:2px 4px;">(\u5BF9\u65B9\u8FD8\u6CA1\u5206\u4EAB channel \u7ED9\u4F60)</div>' : peerChannels.map((c) => `
|
|
2551
|
-
<div class="remote-channel-row" data-peer-id="${
|
|
3983
|
+
<div class="remote-channel-row" data-peer-id="${escapeHtml2(peer.publicKey)}" data-channel-id="${escapeHtml2(c.id)}"
|
|
2552
3984
|
style="display:flex;align-items:center;gap:6px;padding:4px 6px;cursor:pointer;border-radius:4px;font-size:12px;">
|
|
2553
3985
|
<span>\u{1F916}</span>
|
|
2554
|
-
<span style="flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;" title="${
|
|
3986
|
+
<span style="flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;" title="${escapeHtml2(safeChannelName(c.name, ""))}">${escapeHtml2(safeChannelName(c.name))}</span>
|
|
2555
3987
|
</div>
|
|
2556
3988
|
`).join("")}
|
|
2557
3989
|
</div>
|
|
@@ -2619,16 +4051,16 @@
|
|
|
2619
4051
|
<span style="font-size:18px;">\u270F\uFE0F</span>
|
|
2620
4052
|
<div style="flex:1;min-width:0;">
|
|
2621
4053
|
<div class="friend-req-title">\u7F16\u8F91\u597D\u53CB</div>
|
|
2622
|
-
<div class="friend-req-meta">publicKey: ${
|
|
4054
|
+
<div class="friend-req-meta">publicKey: ${escapeHtml2(peerPublicKey.substring(0, 16))}\u2026</div>
|
|
2623
4055
|
</div>
|
|
2624
4056
|
</div>
|
|
2625
4057
|
<div class="friend-req-body">
|
|
2626
4058
|
<label style="display:block;margin-bottom:6px;font-size:12px;color:var(--text-secondary);">\u663E\u793A\u540D\u5B57</label>
|
|
2627
|
-
<input id="epm-name" type="text" value="${
|
|
4059
|
+
<input id="epm-name" type="text" value="${escapeHtml2(currentName)}"
|
|
2628
4060
|
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;">
|
|
2629
4061
|
<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>
|
|
2630
4062
|
<textarea id="epm-notes" rows="4" placeholder="\u4F8B\u5982: 2026-06 \u5408\u4F5C LLM \u4EE3\u53D1\u9A8C\u8BC1"
|
|
2631
|
-
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;">${
|
|
4063
|
+
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;">${escapeHtml2(currentNotes)}</textarea>
|
|
2632
4064
|
</div>
|
|
2633
4065
|
<div class="friend-req-actions">
|
|
2634
4066
|
<button id="epm-cancel" class="friend-req-btn-deny">\u53D6\u6D88</button>
|
|
@@ -2679,11 +4111,11 @@
|
|
|
2679
4111
|
const isShared = Array.isArray(ch.shared_with_peers) && ch.shared_with_peers.includes(peerPublicKey);
|
|
2680
4112
|
return `
|
|
2681
4113
|
<label class="share-modal-row">
|
|
2682
|
-
<input type="checkbox" data-cid="${
|
|
4114
|
+
<input type="checkbox" data-cid="${escapeHtml2(ch.id)}" ${isShared ? "checked" : ""} class="share-modal-cb">
|
|
2683
4115
|
<div class="share-modal-row-info">
|
|
2684
|
-
<div class="share-modal-row-name">${
|
|
4116
|
+
<div class="share-modal-row-name">${escapeHtml2(ch.name || "(\u672A\u547D\u540D)")}</div>
|
|
2685
4117
|
<div class="share-modal-row-meta">
|
|
2686
|
-
${isShared ? "\u2713 \u5DF2\u5206\u4EAB" : "\u672A\u5206\u4EAB"} \xB7 ${
|
|
4118
|
+
${isShared ? "\u2713 \u5DF2\u5206\u4EAB" : "\u672A\u5206\u4EAB"} \xB7 ${escapeHtml2(ch.id.slice(0, 24))}\u2026
|
|
2687
4119
|
</div>
|
|
2688
4120
|
</div>
|
|
2689
4121
|
</label>
|
|
@@ -2695,8 +4127,8 @@
|
|
|
2695
4127
|
<div class="friend-req-header">
|
|
2696
4128
|
<span style="font-size:18px;">\u{1F4E4}</span>
|
|
2697
4129
|
<div style="flex:1;min-width:0;">
|
|
2698
|
-
<div class="friend-req-title">\u5206\u4EAB channel \u7ED9 ${
|
|
2699
|
-
<div class="friend-req-meta">${
|
|
4130
|
+
<div class="friend-req-title">\u5206\u4EAB channel \u7ED9 ${escapeHtml2(peerName)}</div>
|
|
4131
|
+
<div class="friend-req-meta">${escapeHtml2(peerPublicKey.substring(0, 16))}\u2026</div>
|
|
2700
4132
|
</div>
|
|
2701
4133
|
<button id="spm-close" class="friend-req-btn-close">\xD7</button>
|
|
2702
4134
|
</div>
|
|
@@ -2756,8 +4188,8 @@
|
|
|
2756
4188
|
<div class="remote-chat-shell">
|
|
2757
4189
|
<div class="remote-chat-header">
|
|
2758
4190
|
<div style="flex:1;min-width:0;">
|
|
2759
|
-
<div class="remote-chat-title">\u{1F310} \u8DDF ${
|
|
2760
|
-
<div class="remote-chat-meta">\u8FDC\u7AEF peer: ${
|
|
4191
|
+
<div class="remote-chat-title">\u{1F310} \u8DDF ${escapeHtml2(channelName)} \u804A\u5929</div>
|
|
4192
|
+
<div class="remote-chat-meta">\u8FDC\u7AEF peer: ${escapeHtml2(peerPublicKey.substring(0, 16))}\u2026 \xB7 ${escapeHtml2(channelId)}</div>
|
|
2761
4193
|
</div>
|
|
2762
4194
|
<button id="rcm-refresh-history" title="\u91CD\u65B0\u62C9\u5386\u53F2" class="remote-chat-btn-secondary">\u21BB \u5386\u53F2</button>
|
|
2763
4195
|
<button id="rcm-close" class="remote-chat-btn-close">\xD7</button>
|
|
@@ -2788,10 +4220,10 @@
|
|
|
2788
4220
|
};
|
|
2789
4221
|
document.getElementById("rcm-refresh-history").onclick = () => loadHistory(false);
|
|
2790
4222
|
const append = (text, role) => {
|
|
2791
|
-
|
|
4223
|
+
addMessage2(text, role === "user" ? "user" : "ai", false, log);
|
|
2792
4224
|
log.scrollTop = log.scrollHeight;
|
|
2793
4225
|
};
|
|
2794
|
-
const
|
|
4226
|
+
const appendSystem2 = (text, kind = "info") => {
|
|
2795
4227
|
const el = document.createElement("div");
|
|
2796
4228
|
el.className = `remote-chat-sysmsg remote-chat-sysmsg-${kind}`;
|
|
2797
4229
|
el.textContent = text;
|
|
@@ -2825,13 +4257,13 @@
|
|
|
2825
4257
|
const res = await fetch(`/api/remote-channels/chat-history?targetPublicKey=${encodeURIComponent(peerPublicKey)}&channelId=${encodeURIComponent(channelId)}`);
|
|
2826
4258
|
const data = await res.json();
|
|
2827
4259
|
if (!res.ok) {
|
|
2828
|
-
|
|
4260
|
+
appendSystem2(`\u62C9\u53D6\u5931\u8D25: ${data.error || "unknown"}`, "error");
|
|
2829
4261
|
thinkingEl.style.display = "none";
|
|
2830
4262
|
return;
|
|
2831
4263
|
}
|
|
2832
4264
|
renderHistory(data);
|
|
2833
4265
|
} catch (err) {
|
|
2834
|
-
|
|
4266
|
+
appendSystem2(`\u62C9\u53D6\u5F02\u5E38: ${err.message}`, "error");
|
|
2835
4267
|
} finally {
|
|
2836
4268
|
thinkingEl.style.display = "none";
|
|
2837
4269
|
}
|
|
@@ -2844,7 +4276,7 @@
|
|
|
2844
4276
|
jh.className = "remote-chat-judgments";
|
|
2845
4277
|
let h = `<div class="remote-chat-judgments-title">\u{1F6E1}\uFE0F \u5BF9\u65B9 channel \u7ED1\u5B9A\u7684\u5224\u65AD\u529B (${judgments.bound.length} \u6761\u786C\u7EA6\u675F)</div>`;
|
|
2846
4278
|
for (const j of judgments.bound) {
|
|
2847
|
-
h += `<div class="remote-chat-judgment-item">\u2022 <b>${
|
|
4279
|
+
h += `<div class="remote-chat-judgment-item">\u2022 <b>${escapeHtml2((j.decision || "").slice(0, 100))}</b>${j.domain ? `<span class="remote-chat-judgment-tag"> [${escapeHtml2(j.domain)}${j.stakes ? "/" + escapeHtml2(j.stakes) : ""}]</span>` : ""}${j.reasons && j.reasons.length ? '<br><span class="remote-chat-judgment-reason">\u7406\u7531: ' + escapeHtml2(j.reasons.join("; ").slice(0, 100)) + "</span>" : ""}</div>`;
|
|
2848
4280
|
}
|
|
2849
4281
|
if (judgments.candidates && judgments.candidates.length > 0) {
|
|
2850
4282
|
h += `<div class="remote-chat-judgments-foot">+ ${judgments.candidates.length} \u6761\u5019\u9009\u5224\u65AD\u529B (LLM \u53EF\u81EA\u9009\u53C2\u8003)</div>`;
|
|
@@ -2854,7 +4286,7 @@
|
|
|
2854
4286
|
}
|
|
2855
4287
|
const msgs = data.messages || [];
|
|
2856
4288
|
if (msgs.length === 0) {
|
|
2857
|
-
|
|
4289
|
+
appendSystem2("\u8FD8\u6CA1\u6709\u5386\u53F2\u6D88\u606F, \u5728\u4E0B\u9762\u53D1\u7B2C\u4E00\u6761\u5427", "info");
|
|
2858
4290
|
} else {
|
|
2859
4291
|
for (const m of msgs) {
|
|
2860
4292
|
const type = m.type === "user" ? "user" : "ai";
|
|
@@ -2874,7 +4306,7 @@
|
|
|
2874
4306
|
|
|
2875
4307
|
`;
|
|
2876
4308
|
}
|
|
2877
|
-
|
|
4309
|
+
addMessage2(prefix + (m.content || ""), type, false, log);
|
|
2878
4310
|
}
|
|
2879
4311
|
setTimeout(() => {
|
|
2880
4312
|
log.scrollTop = log.scrollHeight;
|
|
@@ -2897,7 +4329,7 @@
|
|
|
2897
4329
|
const data = await res.json();
|
|
2898
4330
|
if (!res.ok) throw new Error(data.error || "send failed");
|
|
2899
4331
|
} catch (err) {
|
|
2900
|
-
|
|
4332
|
+
appendSystem2("\u53D1\u9001\u5931\u8D25: " + (err.message || err), "error");
|
|
2901
4333
|
} finally {
|
|
2902
4334
|
sendBtn2.disabled = false;
|
|
2903
4335
|
sendBtn2.textContent = "\u53D1\u9001";
|
|
@@ -2913,7 +4345,7 @@
|
|
|
2913
4345
|
loadHistory(false);
|
|
2914
4346
|
historyRefreshTimer = setInterval(() => loadHistory(true), 15e3);
|
|
2915
4347
|
}
|
|
2916
|
-
|
|
4348
|
+
var showMyIdBtn = document.getElementById("show-my-p2p-id-btn");
|
|
2917
4349
|
if (showMyIdBtn) {
|
|
2918
4350
|
showMyIdBtn.addEventListener("click", async (e) => {
|
|
2919
4351
|
e.stopPropagation();
|
|
@@ -2946,7 +4378,7 @@
|
|
|
2946
4378
|
body.innerHTML = `
|
|
2947
4379
|
<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>
|
|
2948
4380
|
<div style="display:flex;gap:6px;align-items:center;margin-bottom:12px;">
|
|
2949
|
-
<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;">${
|
|
4381
|
+
<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;">${escapeHtml2(pk)}</code>
|
|
2950
4382
|
<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;">\u{1F4CB} \u590D\u5236</button>
|
|
2951
4383
|
</div>
|
|
2952
4384
|
<div id="mpim-status" style="font-size:12px;color:#059669;min-height:16px;"></div>
|
|
@@ -2977,11 +4409,11 @@
|
|
|
2977
4409
|
};
|
|
2978
4410
|
} catch (err) {
|
|
2979
4411
|
const body = document.getElementById("mpim-body");
|
|
2980
|
-
if (body) body.innerHTML = `<div style="color:#b91c1c;font-size:13px;">\u2717 \u83B7\u53D6\u5931\u8D25: ${
|
|
4412
|
+
if (body) body.innerHTML = `<div style="color:#b91c1c;font-size:13px;">\u2717 \u83B7\u53D6\u5931\u8D25: ${escapeHtml2(err.message || String(err))}</div>`;
|
|
2981
4413
|
}
|
|
2982
4414
|
});
|
|
2983
4415
|
}
|
|
2984
|
-
|
|
4416
|
+
var addPeerBtn = document.getElementById("add-p2p-peer-btn");
|
|
2985
4417
|
if (addPeerBtn) {
|
|
2986
4418
|
addPeerBtn.addEventListener("click", async (e) => {
|
|
2987
4419
|
e.stopPropagation();
|
|
@@ -3037,11 +4469,11 @@
|
|
|
3037
4469
|
<span style="font-size:20px;">\u{1F91D}</span>
|
|
3038
4470
|
<div style="flex:1;min-width:0;">
|
|
3039
4471
|
<div class="friend-req-title">\u597D\u53CB\u7533\u8BF7</div>
|
|
3040
|
-
<div class="friend-req-meta">\u6765\u81EA ${
|
|
4472
|
+
<div class="friend-req-meta">\u6765\u81EA ${escapeHtml2(req.fromName)} (${escapeHtml2(req.fromPublicKey.substring(0, 16))}\u2026)</div>
|
|
3041
4473
|
</div>
|
|
3042
4474
|
</div>
|
|
3043
4475
|
<div class="friend-req-body">
|
|
3044
|
-
<p style="margin:0 0 8px;">${
|
|
4476
|
+
<p style="margin:0 0 8px;">${escapeHtml2(req.message || "\u60F3\u52A0\u4F60\u4E3A P2P \u597D\u53CB")}</p>
|
|
3045
4477
|
<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>
|
|
3046
4478
|
</div>
|
|
3047
4479
|
<div class="friend-req-actions">
|
|
@@ -3094,7 +4526,7 @@
|
|
|
3094
4526
|
setTimeout(() => el.remove(), 320);
|
|
3095
4527
|
}, 3e3);
|
|
3096
4528
|
}
|
|
3097
|
-
|
|
4529
|
+
var p2pToggleAllBtn = document.getElementById("p2p-toggle-all-btn");
|
|
3098
4530
|
if (p2pToggleAllBtn) {
|
|
3099
4531
|
let syncToggleAllBtn = function() {
|
|
3100
4532
|
const allPks = /* @__PURE__ */ new Set([
|
|
@@ -3117,7 +4549,7 @@
|
|
|
3117
4549
|
p2pToggleAllBtn.title = "\u70B9\u51FB\u6298\u53E0\u6240\u6709 P2P \u597D\u53CB";
|
|
3118
4550
|
}
|
|
3119
4551
|
};
|
|
3120
|
-
|
|
4552
|
+
syncToggleAllBtn2 = syncToggleAllBtn;
|
|
3121
4553
|
p2pToggleAllBtn.addEventListener("click", (e) => {
|
|
3122
4554
|
e.stopPropagation();
|
|
3123
4555
|
const allPks = /* @__PURE__ */ new Set([
|
|
@@ -3138,7 +4570,8 @@
|
|
|
3138
4570
|
window.__syncP2PToggleAllBtn = syncToggleAllBtn;
|
|
3139
4571
|
syncToggleAllBtn();
|
|
3140
4572
|
}
|
|
3141
|
-
|
|
4573
|
+
var syncToggleAllBtn2;
|
|
4574
|
+
var refreshSharedBtn = document.getElementById("refresh-shared-btn");
|
|
3142
4575
|
if (refreshSharedBtn) {
|
|
3143
4576
|
refreshSharedBtn.addEventListener("click", async (e) => {
|
|
3144
4577
|
e.stopPropagation();
|
|
@@ -3163,17 +4596,17 @@
|
|
|
3163
4596
|
loadRemoteChannels();
|
|
3164
4597
|
setInterval(loadRemoteChannels, 8e3);
|
|
3165
4598
|
startV3GlobalSSE();
|
|
3166
|
-
|
|
3167
|
-
|
|
4599
|
+
var localSection = document.querySelector(".sidebar-section");
|
|
4600
|
+
var remoteSection = document.getElementById("remote-agents-section");
|
|
3168
4601
|
if (localSection) localSection.classList.add("local-flex");
|
|
3169
4602
|
if (remoteSection) remoteSection.classList.add("remote-flex");
|
|
3170
|
-
|
|
4603
|
+
var remoteHeader = document.getElementById("remote-agents-header");
|
|
3171
4604
|
if (remoteHeader && remoteSection) {
|
|
3172
4605
|
remoteHeader.addEventListener("click", (e) => {
|
|
3173
4606
|
remoteSection.classList.toggle("collapsed");
|
|
3174
4607
|
});
|
|
3175
4608
|
}
|
|
3176
|
-
|
|
4609
|
+
var splitHandle = document.getElementById("sidebar-split-handle");
|
|
3177
4610
|
if (splitHandle && localSection && remoteSection) {
|
|
3178
4611
|
const updateFlexVars = (localRatio, remoteRatio) => {
|
|
3179
4612
|
localSection.style.setProperty("--local-flex", String(localRatio));
|
|
@@ -3217,17 +4650,17 @@
|
|
|
3217
4650
|
updateFlexVars(1, 1);
|
|
3218
4651
|
});
|
|
3219
4652
|
}
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
4653
|
+
var walletModal = document.getElementById("wallet-modal");
|
|
4654
|
+
var walletModalClose = document.getElementById("wallet-modal-close");
|
|
4655
|
+
var walletBindAddress = document.getElementById("wallet-bind-address");
|
|
4656
|
+
var walletGenerateBtn = document.getElementById("wallet-generate-btn");
|
|
4657
|
+
var walletAutoTools = document.getElementById("wallet-auto-tools");
|
|
4658
|
+
var walletBindBtn = document.getElementById("wallet-bind-btn");
|
|
4659
|
+
var walletUnbindBtn = document.getElementById("wallet-unbind-btn");
|
|
4660
|
+
var walletNewInfo = document.getElementById("wallet-new-info");
|
|
4661
|
+
var walletListEl = document.getElementById("wallet-list");
|
|
4662
|
+
var walletModalPendingSecret = null;
|
|
4663
|
+
var walletModalPendingMnemonic = null;
|
|
3231
4664
|
if (walletModalClose) {
|
|
3232
4665
|
walletModalClose.addEventListener("click", closeWalletModal);
|
|
3233
4666
|
}
|
|
@@ -3287,7 +4720,7 @@
|
|
|
3287
4720
|
walletModalPendingSecret = null;
|
|
3288
4721
|
walletModalPendingMnemonic = null;
|
|
3289
4722
|
walletNewInfo.style.display = "block";
|
|
3290
|
-
walletNewInfo.innerHTML = "\u2705 \u7ED1\u5B9A\u6210\u529F<br><strong>\u5730\u5740:</strong> <code>" +
|
|
4723
|
+
walletNewInfo.innerHTML = "\u2705 \u7ED1\u5B9A\u6210\u529F<br><strong>\u5730\u5740:</strong> <code>" + escapeHtml2(updated.walletAddress) + "</code><br><strong>\u7B7E\u540D DID:</strong> <code>" + escapeHtml2(did) + '</code><br><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>';
|
|
3291
4724
|
} catch (err) {
|
|
3292
4725
|
alert("\u7ED1\u5B9A\u5931\u8D25: " + err.message);
|
|
3293
4726
|
}
|
|
@@ -3345,24 +4778,24 @@
|
|
|
3345
4778
|
const row = document.createElement("div");
|
|
3346
4779
|
row.className = "wallet-row" + (isActive ? " is-active" : "");
|
|
3347
4780
|
row.innerHTML = `
|
|
3348
|
-
<span class="wallet-chain">${
|
|
4781
|
+
<span class="wallet-chain">${escapeHtml2(chain)}</span>
|
|
3349
4782
|
<div class="wallet-info">
|
|
3350
|
-
<span class="wallet-agent" title="${
|
|
3351
|
-
<span class="wallet-address" title="${
|
|
4783
|
+
<span class="wallet-agent" title="${escapeHtml2(ch.name || "")}">${escapeHtml2(ch.name || "(\u672A\u547D\u540D)")}</span>
|
|
4784
|
+
<span class="wallet-address" title="${escapeHtml2(ch.walletAddress)}">${escapeHtml2(ch.walletAddress)}</span>
|
|
3352
4785
|
</div>
|
|
3353
4786
|
<div class="wallet-actions">
|
|
3354
|
-
<button class="wallet-mini-btn" data-action="copy" data-addr="${
|
|
4787
|
+
<button class="wallet-mini-btn" data-action="copy" data-addr="${escapeHtml2(ch.walletAddress)}" title="\u590D\u5236\u5730\u5740">
|
|
3355
4788
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
3356
4789
|
<rect x="9" y="9" width="13" height="13" rx="2"></rect>
|
|
3357
4790
|
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
|
|
3358
4791
|
</svg>
|
|
3359
4792
|
</button>
|
|
3360
|
-
<button class="wallet-mini-btn" data-action="goto" data-id="${
|
|
4793
|
+
<button class="wallet-mini-btn" data-action="goto" data-id="${escapeHtml2(ch.id)}" title="\u5207\u6362\u5230\u8BE5\u667A\u80FD\u4F53">
|
|
3361
4794
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
3362
4795
|
<path d="M5 12h14M12 5l7 7-7 7"></path>
|
|
3363
4796
|
</svg>
|
|
3364
4797
|
</button>
|
|
3365
|
-
<button class="wallet-mini-btn" data-action="unbind" data-id="${
|
|
4798
|
+
<button class="wallet-mini-btn" data-action="unbind" data-id="${escapeHtml2(ch.id)}" title="\u89E3\u7ED1">
|
|
3366
4799
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
3367
4800
|
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
3368
4801
|
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
@@ -3421,18 +4854,18 @@
|
|
|
3421
4854
|
return "?";
|
|
3422
4855
|
}
|
|
3423
4856
|
init();
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
4857
|
+
var agentAddModal = document.getElementById("agent-add-modal");
|
|
4858
|
+
var agentAddTitle = document.getElementById("agent-add-title");
|
|
4859
|
+
var agentAddModalClose = document.getElementById("agent-add-modal-close");
|
|
4860
|
+
var agentAddName = document.getElementById("agent-add-name");
|
|
4861
|
+
var agentAddWallet = document.getElementById("agent-add-wallet");
|
|
4862
|
+
var agentAddAutoTools = document.getElementById("agent-add-auto-tools");
|
|
4863
|
+
var agentAddConfirmBtn = document.getElementById("agent-add-confirm-btn");
|
|
4864
|
+
var agentAddCancelBtn = document.getElementById("agent-add-cancel-btn");
|
|
4865
|
+
var agentAddWalletInfo = document.getElementById("agent-add-wallet-info");
|
|
4866
|
+
var agentGenerateWalletBtn = document.getElementById("agent-generate-wallet-btn");
|
|
4867
|
+
var pendingWalletSecret = null;
|
|
4868
|
+
var pendingWalletMnemonic = null;
|
|
3436
4869
|
function openAgentAddModal(existingChannel) {
|
|
3437
4870
|
if (!agentAddModal) return;
|
|
3438
4871
|
if (existingChannel) {
|
|
@@ -3481,17 +4914,17 @@
|
|
|
3481
4914
|
function formatWalletInfoHtml({ address, privateKey, mnemonic }) {
|
|
3482
4915
|
const parts = [
|
|
3483
4916
|
"\u2713 \u5DF2\u751F\u6210\u771F\u5B9E EVM \u94B1\u5305 (BIP-39 + secp256k1 + EIP-55)",
|
|
3484
|
-
"<strong>\u5730\u5740:</strong> <code>" +
|
|
4917
|
+
"<strong>\u5730\u5740:</strong> <code>" + escapeHtml2(address) + "</code>"
|
|
3485
4918
|
];
|
|
3486
4919
|
if (mnemonic) {
|
|
3487
4920
|
parts.push(
|
|
3488
4921
|
"<strong>\u52A9\u8BB0\u8BCD (12 \u8BCD, \u8BF7\u6284\u5199\u4FDD\u5B58):</strong>",
|
|
3489
|
-
'<code style="color:#fc6;word-break:break-all;">' +
|
|
4922
|
+
'<code style="color:#fc6;word-break:break-all;">' + escapeHtml2(mnemonic) + "</code>"
|
|
3490
4923
|
);
|
|
3491
4924
|
}
|
|
3492
4925
|
parts.push(
|
|
3493
4926
|
"<strong>\u79C1\u94A5 (0x + 32 \u5B57\u8282):</strong>",
|
|
3494
|
-
'<code style="color:#f88;word-break:break-all;">' +
|
|
4927
|
+
'<code style="color:#f88;word-break:break-all;">' + escapeHtml2(privateKey) + "</code>",
|
|
3495
4928
|
'<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>',
|
|
3496
4929
|
'<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>'
|
|
3497
4930
|
);
|
|
@@ -3508,7 +4941,7 @@
|
|
|
3508
4941
|
pendingWalletMnemonic = wallet.mnemonic;
|
|
3509
4942
|
agentAddWalletInfo.innerHTML = formatWalletInfoHtml(wallet);
|
|
3510
4943
|
} catch (err) {
|
|
3511
|
-
agentAddWalletInfo.innerHTML = "\u2717 \u751F\u6210\u94B1\u5305\u5931\u8D25: " +
|
|
4944
|
+
agentAddWalletInfo.innerHTML = "\u2717 \u751F\u6210\u94B1\u5305\u5931\u8D25: " + escapeHtml2(err.message);
|
|
3512
4945
|
}
|
|
3513
4946
|
});
|
|
3514
4947
|
}
|