@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.
Files changed (284) hide show
  1. package/README.md +495 -467
  2. package/bin/bolloon-cli.cjs +183 -183
  3. package/bin/bolloon-daemon.sh +207 -0
  4. package/bin/bolloon.cjs +0 -0
  5. package/bin/ipfs +0 -0
  6. package/dist/agents/agent-manifest-protocol.js +52 -0
  7. package/dist/agents/chat-segmenter.js +298 -0
  8. package/dist/agents/constraint-layer.js +19 -19
  9. package/dist/agents/judgment-protocol.js +479 -0
  10. package/dist/agents/parse-tool-call.js +304 -0
  11. package/dist/agents/peer-manifest-loader.js +210 -0
  12. package/dist/agents/pi-sdk-session-factory.js +110 -0
  13. package/dist/agents/pi-sdk-session-manager.js +297 -0
  14. package/dist/agents/pi-sdk-tools.js +1288 -0
  15. package/dist/agents/pi-sdk-types.js +13 -0
  16. package/dist/agents/pi-sdk.js +553 -1306
  17. package/dist/agents/react-loop.js +120 -0
  18. package/dist/agents/session-store.js +171 -0
  19. package/dist/agents/shell-guard.js +2 -2
  20. package/dist/agents/tool-registry.js +136 -0
  21. package/dist/agents/workflow-pivot-loop.js +200 -22
  22. package/dist/bollharness/src/scripts/context_router.js +7 -0
  23. package/dist/bollharness/src/scripts/deploy-guard.js +8 -8
  24. package/dist/bollharness-integration/context-router-judgment.js +4 -4
  25. package/dist/bollharness-integration/context-router.js +292 -292
  26. package/dist/bollharness-integration/gate-state-machine.js +26 -13
  27. package/dist/bollharness-integration/integration.js +71 -0
  28. package/dist/bollharness-integration/llm-judgment-engine.js +134 -134
  29. package/dist/bollharness-integration/skill-adapter.js +57 -127
  30. package/dist/bootstrap/chat-archiver.js +276 -0
  31. package/dist/bootstrap/context-collector.js +6 -3
  32. package/dist/bootstrap/event-log.js +160 -0
  33. package/dist/bootstrap/lifecycle-hooks.js +15 -1
  34. package/dist/bootstrap/memory-compressor.js +170 -0
  35. package/dist/bootstrap/persona-loader.js +94 -0
  36. package/dist/bootstrap/project-state.js +159 -0
  37. package/dist/bootstrap/remote-mirror.js +97 -0
  38. package/dist/bootstrap/session-window.js +121 -0
  39. package/dist/bootstrap/vector-index.js +248 -0
  40. package/dist/cli/loading-tui.js +40 -0
  41. package/dist/cli-entry.js +41 -37
  42. package/dist/context-compaction/auto-compact.js +7 -7
  43. package/dist/electron/config.js +21 -0
  44. package/dist/electron/config.js.map +1 -0
  45. package/dist/electron/dialogs.js +108 -0
  46. package/dist/electron/dialogs.js.map +1 -0
  47. package/dist/electron/first-run.js +170 -0
  48. package/dist/electron/first-run.js.map +1 -0
  49. package/dist/electron/ipc.js +20 -0
  50. package/dist/electron/ipc.js.map +1 -0
  51. package/dist/electron/logger.js +114 -0
  52. package/dist/electron/logger.js.map +1 -0
  53. package/dist/electron/main.js +63 -0
  54. package/dist/electron/main.js.map +1 -0
  55. package/dist/electron/menu.js +145 -0
  56. package/dist/electron/menu.js.map +1 -0
  57. package/dist/electron/paths.js +75 -0
  58. package/dist/electron/paths.js.map +1 -0
  59. package/dist/electron/server.js +119 -0
  60. package/dist/electron/server.js.map +1 -0
  61. package/dist/electron/tray.js +111 -0
  62. package/dist/electron/tray.js.map +1 -0
  63. package/dist/electron/window.js +111 -0
  64. package/dist/electron/window.js.map +1 -0
  65. package/dist/electron-preload.js +26 -6
  66. package/dist/electron-preload.js.map +1 -1
  67. package/dist/electron.js +6 -168
  68. package/dist/electron.js.map +1 -1
  69. package/dist/index.js +323 -266
  70. package/dist/llm/config-store.js +24 -8
  71. package/dist/llm/llm-judgment-client.js +102 -102
  72. package/dist/llm/pi-ai.js +137 -85
  73. package/dist/llm/system-prompt/layers/channel/local.md +14 -14
  74. package/dist/llm/system-prompt/layers/channel/p2p-agent.md +18 -18
  75. package/dist/llm/system-prompt/layers/channel/p2p-visitor.md +19 -19
  76. package/dist/llm/system-prompt/layers/core/artifacts_storage.md +89 -89
  77. package/dist/llm/system-prompt/layers/core/evenhandedness.md +21 -21
  78. package/dist/llm/system-prompt/layers/core/hibs_reminders.md +15 -15
  79. package/dist/llm/system-prompt/layers/core/identity.md +37 -37
  80. package/dist/llm/system-prompt/layers/core/knowledge.md +17 -17
  81. package/dist/llm/system-prompt/layers/core/memory_system.md +12 -12
  82. package/dist/llm/system-prompt/layers/core/network_filesystem.md +28 -28
  83. package/dist/llm/system-prompt/layers/core/refusal.md +37 -37
  84. package/dist/llm/system-prompt/layers/core/tone.md +31 -31
  85. package/dist/llm/system-prompt/layers/core/tools.thin.md +13 -13
  86. package/dist/llm/system-prompt/layers/core/wellbeing.md +41 -41
  87. package/dist/llm/system-prompt/layers/role/architect.md +20 -20
  88. package/dist/llm/system-prompt/layers/role/expert.md +19 -19
  89. package/dist/llm/system-prompt/layers/role/implementer.md +15 -15
  90. package/dist/llm/system-prompt/layers/role/security.md +15 -15
  91. package/dist/llm/system-prompt/layers/tool/artifacts.md +72 -72
  92. package/dist/llm/system-prompt/layers/tool/bash.md +25 -25
  93. package/dist/llm/system-prompt/layers/tool/hibs_api.md +171 -171
  94. package/dist/llm/system-prompt/layers/tool/image_search.md +70 -70
  95. package/dist/llm/system-prompt/layers/tool/manifest.md +89 -89
  96. package/dist/llm/system-prompt/layers/tool/mcp_apps.md +53 -53
  97. package/dist/llm/system-prompt/layers/tool/web_search.md +83 -83
  98. package/dist/llm/tool-manifest/ask_user_input.js +1 -1
  99. package/dist/llm/tool-manifest/bash.js +3 -3
  100. package/dist/llm/tool-manifest/create_file.js +4 -4
  101. package/dist/llm/tool-manifest/fetch_sports_data.js +3 -3
  102. package/dist/llm/tool-manifest/image_search.js +2 -2
  103. package/dist/llm/tool-manifest/mcp.js +2 -2
  104. package/dist/llm/tool-manifest/message_compose.js +3 -3
  105. package/dist/llm/tool-manifest/places.js +2 -2
  106. package/dist/llm/tool-manifest/present_files.js +1 -1
  107. package/dist/llm/tool-manifest/recipe.js +2 -2
  108. package/dist/llm/tool-manifest/str_replace.js +5 -5
  109. package/dist/llm/tool-manifest/view.js +3 -3
  110. package/dist/llm/tool-manifest/weather.js +4 -4
  111. package/dist/llm/tool-manifest/web.js +4 -4
  112. package/dist/network/local-inbox-bus.js +73 -0
  113. package/dist/network/p2p-outbox.js +161 -0
  114. package/dist/network/peer-fs.js +420 -0
  115. package/dist/network/peer-resource-bridge.js +216 -0
  116. package/dist/pi-ecosystem-judgment/causal-judge.js +51 -51
  117. package/dist/pi-ecosystem-judgment/cleanup.js +126 -0
  118. package/dist/pi-ecosystem-judgment/distill-prompt.js +42 -42
  119. package/dist/pi-ecosystem-judgment/distillation.js +14 -14
  120. package/dist/pi-ecosystem-judgment/human-value-store.js +45 -11
  121. package/dist/pi-ecosystem-judgment/monitor-gate.js +28 -28
  122. package/dist/pi-ecosystem-judgment/value-injection.js +102 -102
  123. package/dist/security/context-router-tool.js +15 -15
  124. package/dist/security/tool-gate.js +11 -0
  125. package/dist/web/api-config.html +520 -520
  126. package/dist/web/client-loop-status.js +220 -0
  127. package/dist/web/client.js +22 -5
  128. package/dist/web/client.js.map +7 -0
  129. package/dist/web/components/wallet-viem.mjs +118 -118
  130. package/dist/web/index.html +375 -375
  131. package/dist/web/input-validator.js +103 -0
  132. package/dist/web/manifest.json +20 -20
  133. package/dist/web/routes-judgments.js +738 -0
  134. package/dist/web/routes-llm-config.js +279 -0
  135. package/dist/web/routes-tasks.js +222 -0
  136. package/dist/web/server-sse.js +121 -0
  137. package/dist/web/server-storage.js +154 -0
  138. package/dist/web/server-types.js +15 -0
  139. package/dist/web/server-v3-p2p.js +224 -0
  140. package/dist/web/server.js +1070 -1307
  141. package/dist/web/style.css +4742 -4742
  142. package/dist/web/util/safe-name.js +32 -0
  143. package/package.json +162 -160
  144. package/scripts/build-cli.js +215 -215
  145. package/scripts/build-web.ts +130 -130
  146. package/scripts/postinstall.js +152 -152
  147. package/bin/bolloon.js +0 -157
  148. package/dist/constraint-runtime/src/_archive_helper.js +0 -9
  149. package/dist/constraint-runtime/src/agent/coordinator.js +0 -48
  150. package/dist/constraint-runtime/src/agent/index.js +0 -1
  151. package/dist/constraint-runtime/src/assistant/index.js +0 -12
  152. package/dist/constraint-runtime/src/bootstrap/index.js +0 -12
  153. package/dist/constraint-runtime/src/bootstrap_graph.js +0 -13
  154. package/dist/constraint-runtime/src/bridge/index.js +0 -12
  155. package/dist/constraint-runtime/src/buddy/index.js +0 -12
  156. package/dist/constraint-runtime/src/cli/index.js +0 -12
  157. package/dist/constraint-runtime/src/command_graph.js +0 -10
  158. package/dist/constraint-runtime/src/commands.js +0 -60
  159. package/dist/constraint-runtime/src/components/index.js +0 -12
  160. package/dist/constraint-runtime/src/constants/index.js +0 -12
  161. package/dist/constraint-runtime/src/constraint/budget.js +0 -22
  162. package/dist/constraint-runtime/src/constraint/index.js +0 -2
  163. package/dist/constraint-runtime/src/constraint/permission.js +0 -20
  164. package/dist/constraint-runtime/src/context.js +0 -30
  165. package/dist/constraint-runtime/src/coordinator/index.js +0 -12
  166. package/dist/constraint-runtime/src/cost_hook.js +0 -4
  167. package/dist/constraint-runtime/src/cost_tracker.js +0 -8
  168. package/dist/constraint-runtime/src/deferred_init.js +0 -10
  169. package/dist/constraint-runtime/src/direct_modes.js +0 -6
  170. package/dist/constraint-runtime/src/dynamic-tool-loader.js +0 -85
  171. package/dist/constraint-runtime/src/entrypoints/index.js +0 -12
  172. package/dist/constraint-runtime/src/execution_registry.js +0 -44
  173. package/dist/constraint-runtime/src/history.js +0 -9
  174. package/dist/constraint-runtime/src/hooks/index.js +0 -12
  175. package/dist/constraint-runtime/src/index.js +0 -26
  176. package/dist/constraint-runtime/src/ink.js +0 -4
  177. package/dist/constraint-runtime/src/keybindings/index.js +0 -12
  178. package/dist/constraint-runtime/src/memdir/index.js +0 -12
  179. package/dist/constraint-runtime/src/migrations/index.js +0 -12
  180. package/dist/constraint-runtime/src/models.js +0 -1
  181. package/dist/constraint-runtime/src/moreright/index.js +0 -12
  182. package/dist/constraint-runtime/src/native_ts/index.js +0 -12
  183. package/dist/constraint-runtime/src/output_styles/index.js +0 -12
  184. package/dist/constraint-runtime/src/parity_audit.js +0 -12
  185. package/dist/constraint-runtime/src/plugins/index.js +0 -12
  186. package/dist/constraint-runtime/src/port_manifest.js +0 -11
  187. package/dist/constraint-runtime/src/prefetch.js +0 -9
  188. package/dist/constraint-runtime/src/query.js +0 -1
  189. package/dist/constraint-runtime/src/remote/index.js +0 -12
  190. package/dist/constraint-runtime/src/remote_runtime.js +0 -9
  191. package/dist/constraint-runtime/src/runtime/index.js +0 -1
  192. package/dist/constraint-runtime/src/runtime/session.js +0 -35
  193. package/dist/constraint-runtime/src/schemas/index.js +0 -12
  194. package/dist/constraint-runtime/src/screens/index.js +0 -12
  195. package/dist/constraint-runtime/src/server/index.js +0 -12
  196. package/dist/constraint-runtime/src/services/index.js +0 -12
  197. package/dist/constraint-runtime/src/session_store.js +0 -22
  198. package/dist/constraint-runtime/src/setup.js +0 -30
  199. package/dist/constraint-runtime/src/skills/index.js +0 -1
  200. package/dist/constraint-runtime/src/skills/skill-registry.js +0 -28
  201. package/dist/constraint-runtime/src/state/index.js +0 -12
  202. package/dist/constraint-runtime/src/system_init.js +0 -20
  203. package/dist/constraint-runtime/src/thinking/engine.js +0 -42
  204. package/dist/constraint-runtime/src/thinking/index.js +0 -1
  205. package/dist/constraint-runtime/src/tool_pool.js +0 -8
  206. package/dist/constraint-runtime/src/tools/OpenCLI/execAdapter.js +0 -7
  207. package/dist/constraint-runtime/src/tools/OpenCLI/listAdapters.js +0 -7
  208. package/dist/constraint-runtime/src/tools/OpenCLI/runCommand.js +0 -7
  209. package/dist/constraint-runtime/src/tools/SafeSDK/confirmTransaction.js +0 -6
  210. package/dist/constraint-runtime/src/tools/SafeSDK/createTransaction.js +0 -8
  211. package/dist/constraint-runtime/src/tools/SafeSDK/executeTransaction.js +0 -6
  212. package/dist/constraint-runtime/src/tools/SafeSDK/getBalance.js +0 -6
  213. package/dist/constraint-runtime/src/tools/SafeSDK/getPendingTransactions.js +0 -6
  214. package/dist/constraint-runtime/src/tools/SafeSDK/proposeTransaction.js +0 -6
  215. package/dist/constraint-runtime/src/tools.js +0 -80
  216. package/dist/constraint-runtime/src/transcript.js +0 -19
  217. package/dist/constraint-runtime/src/types/index.js +0 -12
  218. package/dist/constraint-runtime/src/upstream_proxy/index.js +0 -12
  219. package/dist/constraint-runtime/src/utils/index.js +0 -12
  220. package/dist/constraint-runtime/src/vim/index.js +0 -12
  221. package/dist/constraint-runtime/src/voice/index.js +0 -12
  222. package/dist/constraint-runtime/tests/agent.test.js +0 -16
  223. package/dist/constraint-runtime/tests/constraint.test.js +0 -41
  224. package/dist/constraint-runtime/tests/skill.test.js +0 -19
  225. package/dist/constraint-runtime/tests/thinking.test.js +0 -22
  226. package/dist/pi-ecosystem-colony/index.js +0 -365
  227. package/dist/social/ant-colony/AdaptiveHeartbeat.js +0 -101
  228. package/dist/social/ant-colony/PheromoneEngine.js +0 -227
  229. package/dist/social/ant-colony/index.js +0 -6
  230. package/dist/social/ant-colony/types.js +0 -24
  231. package/dist/test/ai-judgment-test.js +0 -80
  232. package/dist/test/bollharness-integration.test.js +0 -318
  233. package/dist/test/channel-agent-multi-dialogue.js +0 -205
  234. package/dist/test/channel-heartbeat-agent-test.js +0 -201
  235. package/dist/test/constraint-layer.test.js +0 -164
  236. package/dist/test/diap-identity-test.js +0 -172
  237. package/dist/test/diap-quick-test.js +0 -62
  238. package/dist/test/global-shared-context.test.js +0 -315
  239. package/dist/test/harness-judgment-injection.test.js +0 -246
  240. package/dist/test/harness-workflow-integrator-test.js +0 -228
  241. package/dist/test/human-value-store.test.js +0 -243
  242. package/dist/test/hybrid-integration-test.js +0 -118
  243. package/dist/test/hybrid-messenger-verify.js +0 -55
  244. package/dist/test/iroh-bistream-debug.js +0 -38
  245. package/dist/test/iroh-communication.test.js +0 -66
  246. package/dist/test/iroh-debug-test.js +0 -57
  247. package/dist/test/iroh-diap-test.js +0 -71
  248. package/dist/test/iroh-direct-connect.js +0 -55
  249. package/dist/test/iroh-e2e-fixed.js +0 -89
  250. package/dist/test/iroh-e2e-same-process.js +0 -63
  251. package/dist/test/iroh-e2e.js +0 -66
  252. package/dist/test/iroh-final-e2e.js +0 -72
  253. package/dist/test/iroh-relay-test.js +0 -37
  254. package/dist/test/iroh-simple-test.js +0 -41
  255. package/dist/test/iroh-transport-verify.js +0 -54
  256. package/dist/test/iroh-transport.test.js +0 -37
  257. package/dist/test/iroh-two-nodes.js +0 -70
  258. package/dist/test/iroh-verify.js +0 -44
  259. package/dist/test/judgment-decision.test.js +0 -219
  260. package/dist/test/llm-judgment-integration.test.js +0 -220
  261. package/dist/test/p2p-agent-complex-dialogue.js +0 -385
  262. package/dist/test/p2p-agent-dialogue.js +0 -341
  263. package/dist/test/p2p-agent-full-bidirectional.js +0 -510
  264. package/dist/test/p2p-agent-harness-flow.js +0 -437
  265. package/dist/test/p2p-agent-harness-single.js +0 -143
  266. package/dist/test/p2p-ai-dialogue-test.js +0 -318
  267. package/dist/test/p2p-cid-connect-test.js +0 -195
  268. package/dist/test/p2p-connect-receiver.js +0 -69
  269. package/dist/test/p2p-doc-transfer.js +0 -110
  270. package/dist/test/p2p-identity-page-test.js +0 -77
  271. package/dist/test/p2p-iroh-test.js +0 -171
  272. package/dist/test/p2p-minimal-test.js +0 -241
  273. package/dist/test/p2p-node-1.js +0 -148
  274. package/dist/test/p2p-node-2.js +0 -148
  275. package/dist/test/p2p-server.js +0 -281
  276. package/dist/test/p2p-two-nodes-test.js +0 -438
  277. package/dist/test/pi-sdk.test.js +0 -44
  278. package/dist/test/set-persona.js +0 -40
  279. package/dist/test/simple.test.js +0 -9
  280. package/dist/test/storage-integration.test.js +0 -150
  281. package/dist/test/subagent-manager.test.js +0 -276
  282. package/dist/test/test-gate-flow.test.js +0 -81
  283. package/dist/test/workflow-engine.test.js +0 -87
  284. 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();