@bolloon/bolloon-agent 0.2.12 → 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 +22 -5
- package/dist/web/client.js.map +7 -0
- package/dist/web/components/wallet-viem.mjs +118 -118
- package/dist/web/index.html +375 -375
- 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 -4742
- 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 -130
- 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
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* server-storage.ts — server.ts 拆出的持久化层
|
|
3
|
+
*
|
|
4
|
+
* 包含:
|
|
5
|
+
* - loadChannels / saveChannels + 去重 + 写盘保护
|
|
6
|
+
* - loadSession / saveSession + 50MB 内存保护
|
|
7
|
+
* - loadTheme / saveTheme
|
|
8
|
+
* - 任务队列 (loadTaskQueue / saveTaskQueue / executeTask)
|
|
9
|
+
*
|
|
10
|
+
* 从 src/web/server.ts 抽出 (2026-07-06).
|
|
11
|
+
* Channel / Session / Task / SessionMessage / SessionSummary type 来自 ./server-types.ts.
|
|
12
|
+
*/
|
|
13
|
+
import * as fs from 'fs/promises';
|
|
14
|
+
import { CHANNELS_PATH, SESSION_CACHE_PATH, TASK_QUEUE_PATH, THEME_PATH, } from './server-types.js';
|
|
15
|
+
import { saveWindow as saveSessionWindow, loadWindow as loadSessionWindow } from '../bootstrap/session-window.js';
|
|
16
|
+
// 写盘去重: 上次写盘内容, 用于跳过幂等调用
|
|
17
|
+
let lastChannelsJson = '';
|
|
18
|
+
// 写盘保护: 任何调用 saveChannels 后更新
|
|
19
|
+
let lastChannelsWriteAt = 0;
|
|
20
|
+
export function getLastChannelsWriteAt() {
|
|
21
|
+
return lastChannelsWriteAt;
|
|
22
|
+
}
|
|
23
|
+
export async function loadChannels() {
|
|
24
|
+
try {
|
|
25
|
+
const data = await fs.readFile(CHANNELS_PATH, 'utf-8');
|
|
26
|
+
return JSON.parse(data);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export async function saveChannels(channels) {
|
|
33
|
+
// 写盘前剥掉任何遗留的 didDocument 字段, 防止历史脏数据撑大文件
|
|
34
|
+
const sanitized = channels.map(ch => {
|
|
35
|
+
const { didDocument: _omit, ...rest } = ch;
|
|
36
|
+
return rest;
|
|
37
|
+
});
|
|
38
|
+
const jsonStr = JSON.stringify(sanitized, null, 2);
|
|
39
|
+
// 写盘保护: 内容和上次完全一致就跳过, 避免 SSE ping / 重新 init 触发的无意义写盘
|
|
40
|
+
if (jsonStr === lastChannelsJson) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
lastChannelsJson = jsonStr;
|
|
44
|
+
console.log('[saveChannels] 保存频道数据, 数量:', sanitized.length);
|
|
45
|
+
console.log('[saveChannels] JSON 长度:', jsonStr.length);
|
|
46
|
+
await fs.writeFile(CHANNELS_PATH, jsonStr);
|
|
47
|
+
lastChannelsWriteAt = Date.now();
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* loadSession 加 L0 window fallback 链 (2026-07-07 P0-B):
|
|
51
|
+
* 1) full session.json (主路径)
|
|
52
|
+
* 2) full 超 50MB 拒加载 → 读 <key>.window.json
|
|
53
|
+
* 3) window 也没有 → 返回 null (前端 fallback "你好! 我是 Bolloon Agent")
|
|
54
|
+
*/
|
|
55
|
+
export async function loadSession(channelId, sessionId) {
|
|
56
|
+
const key = sessionId ? `${channelId}:${sessionId}` : channelId;
|
|
57
|
+
const sessionPath = `${SESSION_CACHE_PATH}/${key}.json`;
|
|
58
|
+
const sid = sessionId || 'default';
|
|
59
|
+
try {
|
|
60
|
+
// 内存保护: 拒绝加载过大的 session 文件 (> 50MB 视为异常, 避免 OOM)
|
|
61
|
+
const stat = await fs.stat(sessionPath);
|
|
62
|
+
if (stat.size > 50 * 1024 * 1024) {
|
|
63
|
+
console.warn(`[loadSession] session 过大 (${stat.size} bytes): ${key}, fallback to window`);
|
|
64
|
+
return await loadSessionWindowFallback(channelId, sid);
|
|
65
|
+
}
|
|
66
|
+
const data = await fs.readFile(sessionPath, 'utf-8');
|
|
67
|
+
return JSON.parse(data);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// 文件不存在 → 仍尝试 window (可能上次 session 删了, window 还在)
|
|
71
|
+
return await loadSessionWindowFallback(channelId, sid);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/** window-only fallback: 窗口消息填入 Session.messages, 其他字段填占位 */
|
|
75
|
+
async function loadSessionWindowFallback(channelId, sessionId) {
|
|
76
|
+
const win = await loadSessionWindow(channelId, sessionId);
|
|
77
|
+
if (!win || win.messages.length === 0)
|
|
78
|
+
return null;
|
|
79
|
+
console.log(`[loadSession] window fallback for ${channelId}: ${sessionId}, ${win.messages.length} msgs (${win.totalBehind} behind)`);
|
|
80
|
+
// WindowEntry → SessionMessage 映射 (id/timestamp 缺失时补占位)
|
|
81
|
+
const messages = win.messages.map((m, idx) => ({
|
|
82
|
+
id: m.id || `win-${win.lastUpdated}-${idx}`,
|
|
83
|
+
type: (m.type === 'user' || m.type === 'ai' ? m.type : 'user'),
|
|
84
|
+
content: m.content,
|
|
85
|
+
timestamp: m.timestamp || win.lastUpdated,
|
|
86
|
+
metadata: m.metadata,
|
|
87
|
+
}));
|
|
88
|
+
return {
|
|
89
|
+
channelId,
|
|
90
|
+
sessionId,
|
|
91
|
+
messages,
|
|
92
|
+
lastUpdated: win.lastUpdated,
|
|
93
|
+
_windowOnly: true,
|
|
94
|
+
_totalBehind: win.totalBehind,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
export async function saveSession(session) {
|
|
98
|
+
const key = session.sessionId ? `${session.channelId}:${session.sessionId}` : session.channelId;
|
|
99
|
+
const sessionPath = `${SESSION_CACHE_PATH}/${key}.json`;
|
|
100
|
+
await fs.writeFile(sessionPath, JSON.stringify(session, null, 2));
|
|
101
|
+
// L0 窗口联动 (2026-07-07): 同步写最近 30 条到 <key>.window.json
|
|
102
|
+
// 失败静默 — 不阻塞主对话流
|
|
103
|
+
try {
|
|
104
|
+
await saveSessionWindow(session.channelId, session.sessionId || 'default', session.messages || [], { windowSize: 30 });
|
|
105
|
+
}
|
|
106
|
+
catch (e) {
|
|
107
|
+
console.warn(`[saveSession] window write failed for ${key}: ${e?.message || e}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
export async function loadTheme() {
|
|
111
|
+
try {
|
|
112
|
+
const data = await fs.readFile(THEME_PATH, 'utf-8');
|
|
113
|
+
return JSON.parse(data);
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return { theme: 'light', agentId: '' };
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
export async function saveTheme(theme, agentId) {
|
|
120
|
+
await fs.writeFile(THEME_PATH, JSON.stringify({ theme, agentId }, null, 2));
|
|
121
|
+
}
|
|
122
|
+
// ==================== Task Queue & Workflow System ====================
|
|
123
|
+
let isExecutingTask = false;
|
|
124
|
+
let executionTaskId = null;
|
|
125
|
+
export function isTaskExecuting() {
|
|
126
|
+
return isExecutingTask;
|
|
127
|
+
}
|
|
128
|
+
export function getExecutingTaskId() {
|
|
129
|
+
return executionTaskId;
|
|
130
|
+
}
|
|
131
|
+
export async function loadTaskQueue() {
|
|
132
|
+
try {
|
|
133
|
+
const data = await fs.readFile(TASK_QUEUE_PATH, 'utf-8');
|
|
134
|
+
return JSON.parse(data);
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return [];
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
export async function saveTaskQueue(tasks) {
|
|
141
|
+
await fs.writeFile(TASK_QUEUE_PATH, JSON.stringify(tasks, null, 2));
|
|
142
|
+
}
|
|
143
|
+
/** 执行 task 的 helper 标记 (server.ts 内部 still 持有 lock) */
|
|
144
|
+
export function startTaskExecution(taskId) {
|
|
145
|
+
if (isExecutingTask)
|
|
146
|
+
return false;
|
|
147
|
+
isExecutingTask = true;
|
|
148
|
+
executionTaskId = taskId;
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
export function endTaskExecution() {
|
|
152
|
+
isExecutingTask = false;
|
|
153
|
+
executionTaskId = null;
|
|
154
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* server-types.ts — server.ts 拆出的纯类型 + 常量 + 路径
|
|
3
|
+
*
|
|
4
|
+
* 从 src/web/server.ts 抽出 (2026-07-06).
|
|
5
|
+
* 0 副作用, 0 模块级 state, 只放 type / interface / const 路径.
|
|
6
|
+
*/
|
|
7
|
+
import * as path from 'path';
|
|
8
|
+
const HOME = process.env.HOME || process.env.USERPROFILE || '/tmp';
|
|
9
|
+
export const SHARED_SESSION_PATH = path.join(HOME, '.bolloon', 'sessions');
|
|
10
|
+
export const SESSION_CACHE_PATH = path.join(SHARED_SESSION_PATH, 'cache');
|
|
11
|
+
export const CHANNELS_PATH = path.join(SHARED_SESSION_PATH, 'channels.json');
|
|
12
|
+
export const THEME_PATH = path.join(SHARED_SESSION_PATH, 'theme.json');
|
|
13
|
+
export const TASK_QUEUE_PATH = path.join(SHARED_SESSION_PATH, 'task-queue.json');
|
|
14
|
+
export const IPFS_ENDPOINT = 'http://127.0.0.1:5001';
|
|
15
|
+
export const REMOTE_CACHE_FILE = path.join(HOME, '.bolloon', 'remote-channels-cache.json');
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* server-v3-p2p.ts — server.ts 拆出的 v3 P2P 层
|
|
3
|
+
*
|
|
4
|
+
* 包含:
|
|
5
|
+
* - sanitizeChannelForPeer / isSharedWith
|
|
6
|
+
* - routeMentionsInReply (LLM 回复 @-mention 路由)
|
|
7
|
+
* - loadRemoteChannelCacheFromDisk / persistRemoteChannelCache
|
|
8
|
+
* - loadLocalSubAgents (跳过 subagent-manager 懒加载, 直接读 agents.json)
|
|
9
|
+
* - v3P2PRef (P2PDirect 引用, 让闭包外能拿到)
|
|
10
|
+
*
|
|
11
|
+
* 从 src/web/server.ts 抽出 (2026-07-06).
|
|
12
|
+
*/
|
|
13
|
+
import { REMOTE_CACHE_FILE } from './server-types.js';
|
|
14
|
+
import { loadChannels, saveSession, loadSession } from './server-storage.js';
|
|
15
|
+
import { broadcast } from './server-sse.js';
|
|
16
|
+
// v3: 远端 channel UI 元数据缓存 — key: peerId, value: sanitize 过的 channel 列表
|
|
17
|
+
// in-memory only, 进程重启清空 (judgment 内容永远不在这里)
|
|
18
|
+
const remoteChannelCache = new Map();
|
|
19
|
+
export function getRemoteChannelCache() {
|
|
20
|
+
return remoteChannelCache;
|
|
21
|
+
}
|
|
22
|
+
// v3: P2PDirect 引用 (Hyperswarm 薄包装) - 模块级, 因为 web server 闭包里不可用
|
|
23
|
+
let v3P2PRef = null;
|
|
24
|
+
export function getV3P2PRef() {
|
|
25
|
+
return v3P2PRef;
|
|
26
|
+
}
|
|
27
|
+
export function setV3P2PRef(ref) {
|
|
28
|
+
v3P2PRef = ref;
|
|
29
|
+
}
|
|
30
|
+
// 2026-07-05: 一次性 prompt 附加块 — key: channelId, value: 下一次 LLM prompt 时 prepend 的内容
|
|
31
|
+
const nextPromptHints = new Map();
|
|
32
|
+
export function getNextPromptHints() {
|
|
33
|
+
return nextPromptHints;
|
|
34
|
+
}
|
|
35
|
+
// 2026-07-05: 等待中的 history RPC (B 端 chat-history endpoint 用) — rpcId → { resolve, reject }
|
|
36
|
+
const v3PendingHistoryGets = new Map();
|
|
37
|
+
export function getV3PendingHistoryGets() {
|
|
38
|
+
return v3PendingHistoryGets;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* v3: 过滤 channel 元数据, 只返回对远端 peer 安全的字段.
|
|
42
|
+
* 关键: bound_judgment_ids / walletBinding / autoInvokeTools 内部状态不外传.
|
|
43
|
+
*/
|
|
44
|
+
export function sanitizeChannelForPeer(ch, peerPublicKey) {
|
|
45
|
+
if (peerPublicKey) {
|
|
46
|
+
const shared = Array.isArray(ch.shared_with_peers) ? ch.shared_with_peers : [];
|
|
47
|
+
if (!shared.includes(peerPublicKey)) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
id: ch.id,
|
|
53
|
+
name: ch.name,
|
|
54
|
+
did: ch.did,
|
|
55
|
+
publicKey: ch.publicKey,
|
|
56
|
+
createdAt: ch.createdAt,
|
|
57
|
+
updatedAt: ch.updatedAt,
|
|
58
|
+
hasWallet: !!ch.walletAddress,
|
|
59
|
+
share_id: ch.share_id,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/** v3 新增: 判断 channel 是否分享给 peerPublicKey */
|
|
63
|
+
export function isSharedWith(ch, peerPublicKey) {
|
|
64
|
+
const shared = Array.isArray(ch.shared_with_peers) ? ch.shared_with_peers : [];
|
|
65
|
+
return shared.includes(peerPublicKey);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* v3 新增: 解析 LLM 回复里的 @-mentions, 把消息发到目标 channel.
|
|
69
|
+
*
|
|
70
|
+
* 语法: "@渠道名 消息内容" — 渠道名匹配 local channels by name, 或 remote channels by name.
|
|
71
|
+
*/
|
|
72
|
+
export async function routeMentionsInReply(originChannelId, replyText, localChannels, remoteChannels) {
|
|
73
|
+
const results = [];
|
|
74
|
+
const regex = /@([一-龥A-Za-z0-9_\-]{1,30})\s+([^\n@]+?)(?=(?:\s*@[一-龥A-Za-z0-9_\-]{1,30}\s)|$)/g;
|
|
75
|
+
const matches = [...replyText.matchAll(regex)];
|
|
76
|
+
if (matches.length === 0)
|
|
77
|
+
return results;
|
|
78
|
+
let originChannelName = originChannelId;
|
|
79
|
+
try {
|
|
80
|
+
const chs = await loadChannels();
|
|
81
|
+
const oc = chs.find(c => c.id === originChannelId);
|
|
82
|
+
if (oc)
|
|
83
|
+
originChannelName = oc.name;
|
|
84
|
+
}
|
|
85
|
+
catch { }
|
|
86
|
+
console.log(`[v3-cross] (${originChannelName}) 解析到 ${matches.length} 个 @-mention`);
|
|
87
|
+
for (const m of matches) {
|
|
88
|
+
const targetName = m[1].trim();
|
|
89
|
+
const text = m[2].trim();
|
|
90
|
+
if (!text)
|
|
91
|
+
continue;
|
|
92
|
+
const localTarget = localChannels.find(c => c.name === targetName);
|
|
93
|
+
const remoteTarget = !localTarget ? remoteChannels.find(c => c.name === targetName) : null;
|
|
94
|
+
if (localTarget) {
|
|
95
|
+
try {
|
|
96
|
+
const existing = await loadSession(localTarget.id, 'default');
|
|
97
|
+
const session = existing || {
|
|
98
|
+
channelId: localTarget.id, sessionId: 'default', messages: [], lastUpdated: new Date().toISOString()
|
|
99
|
+
};
|
|
100
|
+
session.messages.push({
|
|
101
|
+
id: `msg-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,
|
|
102
|
+
type: 'ai',
|
|
103
|
+
content: text,
|
|
104
|
+
timestamp: new Date().toISOString(),
|
|
105
|
+
source: 'ai-mention',
|
|
106
|
+
originChannelId,
|
|
107
|
+
originChannelName
|
|
108
|
+
});
|
|
109
|
+
session.lastUpdated = new Date().toISOString();
|
|
110
|
+
await saveSession(session);
|
|
111
|
+
console.log(`[v3-cross] (${originChannelName}) @${targetName} → 本地 channel ${localTarget.id}, 存了 ${text.length} chars`);
|
|
112
|
+
broadcast({
|
|
113
|
+
type: 'cross-mention-received',
|
|
114
|
+
originChannelId, originChannelName,
|
|
115
|
+
targetChannelId: localTarget.id, targetChannelName: localTarget.name,
|
|
116
|
+
text, source: 'ai-mention'
|
|
117
|
+
}, 'broadcast');
|
|
118
|
+
results.push({ targetName, targetId: localTarget.id, source: 'local', text, status: 'sent' });
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
console.error(`[v3-cross] @${targetName} 本地存失败:`, err.message);
|
|
122
|
+
results.push({ targetName, targetId: localTarget.id, source: 'local', text, status: 'failed' });
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
else if (remoteTarget) {
|
|
126
|
+
const ownerPk = remoteTarget._ownerPublicKey;
|
|
127
|
+
if (!v3P2PRef) {
|
|
128
|
+
console.warn(`[v3-cross] P2PDirect 未启动, 跳过远端 @${targetName}`);
|
|
129
|
+
results.push({ targetName, targetId: remoteTarget.id, source: 'remote', text, status: 'failed' });
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
const rpcPayload = {
|
|
134
|
+
targetChannelId: remoteTarget.id,
|
|
135
|
+
targetChannelName: remoteTarget.name,
|
|
136
|
+
originChannelId,
|
|
137
|
+
originChannelName,
|
|
138
|
+
text,
|
|
139
|
+
fromPublicKey: v3P2PRef.getPublicKey()
|
|
140
|
+
};
|
|
141
|
+
const { sendOrQueue } = await import('../network/p2p-outbox.js');
|
|
142
|
+
const r = await sendOrQueue(ownerPk, 'agent.cross.post', rpcPayload, v3P2PRef);
|
|
143
|
+
if (r === 'SENT') {
|
|
144
|
+
console.log(`[v3-cross] (${originChannelName}) @${targetName} → 远端 peer ${ownerPk.substring(0, 12)}... (channelId=${remoteTarget.id})`);
|
|
145
|
+
results.push({ targetName, targetId: remoteTarget.id, source: 'remote', text, status: 'sent' });
|
|
146
|
+
}
|
|
147
|
+
else if (r === 'QUEUED') {
|
|
148
|
+
console.log(`[v3-cross] (${originChannelName}) @${targetName} → 远端 peer ${ownerPk.substring(0, 12)}... 已入队 (对方不在线)`);
|
|
149
|
+
results.push({ targetName, targetId: remoteTarget.id, source: 'remote', text, status: 'queued' });
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
results.push({ targetName, targetId: remoteTarget.id, source: 'remote', text, status: 'failed' });
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
catch (err) {
|
|
156
|
+
console.error(`[v3-cross] @${targetName} 远端 RPC 失败:`, err.message);
|
|
157
|
+
results.push({ targetName, targetId: remoteTarget.id, source: 'remote', text, status: 'failed' });
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
console.warn(`[v3-cross] @${targetName} 找不到匹配 channel (本地 ${localChannels.length} 个, 远端 ${remoteChannels.length} 个)`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return results;
|
|
165
|
+
}
|
|
166
|
+
// 2026-06-10: 持久化 remote channel cache 到 ~/.bolloon/remote-channels-cache.json
|
|
167
|
+
export async function loadRemoteChannelCacheFromDisk() {
|
|
168
|
+
try {
|
|
169
|
+
const { readFile } = await import('fs/promises');
|
|
170
|
+
const { existsSync } = await import('fs');
|
|
171
|
+
if (!existsSync(REMOTE_CACHE_FILE))
|
|
172
|
+
return;
|
|
173
|
+
const raw = await readFile(REMOTE_CACHE_FILE, 'utf-8');
|
|
174
|
+
const obj = JSON.parse(raw);
|
|
175
|
+
if (obj && typeof obj === 'object') {
|
|
176
|
+
for (const [pk, list] of Object.entries(obj)) {
|
|
177
|
+
if (Array.isArray(list)) {
|
|
178
|
+
remoteChannelCache.set(pk, list);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
console.log(`[v3-meta] 从磁盘恢复 ${remoteChannelCache.size} 个 peer 的 channel cache`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
catch (err) {
|
|
185
|
+
console.warn('[v3-meta] 恢复 remote channel cache 失败 (非致命):', err.message);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
export async function persistRemoteChannelCache() {
|
|
189
|
+
try {
|
|
190
|
+
const { writeFile, mkdir } = await import('fs/promises');
|
|
191
|
+
const { existsSync } = await import('fs');
|
|
192
|
+
if (!existsSync(`${process.env.HOME || '/tmp'}/.bolloon`)) {
|
|
193
|
+
await mkdir(`${process.env.HOME || '/tmp'}/.bolloon`, { recursive: true });
|
|
194
|
+
}
|
|
195
|
+
const obj = {};
|
|
196
|
+
for (const [pk, list] of remoteChannelCache.entries()) {
|
|
197
|
+
obj[pk] = list;
|
|
198
|
+
}
|
|
199
|
+
await writeFile(REMOTE_CACHE_FILE, JSON.stringify(obj, null, 2), 'utf-8');
|
|
200
|
+
}
|
|
201
|
+
catch (err) {
|
|
202
|
+
console.warn('[v3-meta] 持久化 remote channel cache 失败 (非致命):', err.message);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// 2026-07-05: 直接读 ~/.bolloon/agents/agents.json, 跳过 subagent-manager 的 lazy init
|
|
206
|
+
export async function loadLocalSubAgents() {
|
|
207
|
+
try {
|
|
208
|
+
const fsPromises = await import('fs/promises');
|
|
209
|
+
const path = await import('path');
|
|
210
|
+
const home = process.env.BOLLOON_HOME || process.env.HOME || '/tmp';
|
|
211
|
+
const file = path.join(home, '.bolloon', 'agents', 'agents.json');
|
|
212
|
+
const raw = await fsPromises.readFile(file, 'utf-8');
|
|
213
|
+
const parsed = JSON.parse(raw);
|
|
214
|
+
const arr = Array.isArray(parsed) ? parsed : [];
|
|
215
|
+
process.stderr.write(`[LOADED] ${arr.length} agents from ${file}\n`);
|
|
216
|
+
return arr;
|
|
217
|
+
}
|
|
218
|
+
catch (e) {
|
|
219
|
+
process.stderr.write(`[LOAD FAIL] ${e?.message}\n`);
|
|
220
|
+
return [];
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
// 启动时立即同步读一次 (异步, 不阻塞)
|
|
224
|
+
loadRemoteChannelCacheFromDisk();
|