@bolloon/bolloon-agent 0.3.44 → 0.3.46

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 (217) hide show
  1. package/README.md +135 -135
  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/dist/agents/agent-identity.js +17 -0
  6. package/dist/agents/constraint-layer.js +19 -19
  7. package/dist/agents/judgment-protocol.js +14 -14
  8. package/dist/agents/loop-review.js +21 -5
  9. package/dist/agents/pi-sdk-types.js +12 -12
  10. package/dist/agents/pi-sdk.js +197 -102
  11. package/dist/agents/skill-writer.js +19 -0
  12. package/dist/agents/workflow-pivot-loop.js +3 -3
  13. package/dist/bollharness/src/scripts/deploy-guard.js +8 -8
  14. package/dist/bollharness-integration/context-router-judgment.js +4 -4
  15. package/dist/bollharness-integration/context-router.js +292 -292
  16. package/dist/bollharness-integration/gate-state-machine.js +13 -13
  17. package/dist/bollharness-integration/llm-judgment-engine.js +134 -134
  18. package/dist/bollharness-integration/skill-adapter.js +21 -21
  19. package/dist/bootstrap/chat-archiver.js +14 -14
  20. package/dist/bootstrap/context-os.js +21 -21
  21. package/dist/bootstrap/memory-compressor.js +29 -13
  22. package/dist/cli-entry.js +47 -37
  23. package/dist/constraint-runtime/reference_data/archive_surface_snapshot.json +63 -0
  24. package/dist/constraint-runtime/reference_data/commands_snapshot.json +1037 -0
  25. package/dist/constraint-runtime/reference_data/subsystems/assistant.json +8 -0
  26. package/dist/constraint-runtime/reference_data/subsystems/bootstrap.json +8 -0
  27. package/dist/constraint-runtime/reference_data/subsystems/bridge.json +32 -0
  28. package/dist/constraint-runtime/reference_data/subsystems/buddy.json +13 -0
  29. package/dist/constraint-runtime/reference_data/subsystems/cli.json +26 -0
  30. package/dist/constraint-runtime/reference_data/subsystems/components.json +32 -0
  31. package/dist/constraint-runtime/reference_data/subsystems/constants.json +28 -0
  32. package/dist/constraint-runtime/reference_data/subsystems/coordinator.json +8 -0
  33. package/dist/constraint-runtime/reference_data/subsystems/entrypoints.json +15 -0
  34. package/dist/constraint-runtime/reference_data/subsystems/hooks.json +32 -0
  35. package/dist/constraint-runtime/reference_data/subsystems/keybindings.json +21 -0
  36. package/dist/constraint-runtime/reference_data/subsystems/memdir.json +15 -0
  37. package/dist/constraint-runtime/reference_data/subsystems/migrations.json +18 -0
  38. package/dist/constraint-runtime/reference_data/subsystems/moreright.json +8 -0
  39. package/dist/constraint-runtime/reference_data/subsystems/native_ts.json +11 -0
  40. package/dist/constraint-runtime/reference_data/subsystems/outputStyles.json +8 -0
  41. package/dist/constraint-runtime/reference_data/subsystems/plugins.json +9 -0
  42. package/dist/constraint-runtime/reference_data/subsystems/remote.json +11 -0
  43. package/dist/constraint-runtime/reference_data/subsystems/schemas.json +8 -0
  44. package/dist/constraint-runtime/reference_data/subsystems/screens.json +10 -0
  45. package/dist/constraint-runtime/reference_data/subsystems/server.json +10 -0
  46. package/dist/constraint-runtime/reference_data/subsystems/services.json +32 -0
  47. package/dist/constraint-runtime/reference_data/subsystems/skills.json +27 -0
  48. package/dist/constraint-runtime/reference_data/subsystems/state.json +13 -0
  49. package/dist/constraint-runtime/reference_data/subsystems/types.json +18 -0
  50. package/dist/constraint-runtime/reference_data/subsystems/upstreamproxy.json +9 -0
  51. package/dist/constraint-runtime/reference_data/subsystems/utils.json +32 -0
  52. package/dist/constraint-runtime/reference_data/subsystems/vim.json +12 -0
  53. package/dist/constraint-runtime/reference_data/subsystems/voice.json +8 -0
  54. package/dist/constraint-runtime/reference_data/tools_snapshot.json +922 -0
  55. package/dist/constraint-runtime/setup.js +1 -1
  56. package/dist/constraint-runtime/tools/PolymarketSDK/cancelOrder.d.ts +13 -3
  57. package/dist/constraint-runtime/tools/PolymarketSDK/cancelOrder.js +16 -4
  58. package/dist/constraint-runtime/tools/PolymarketSDK/clobShared.d.ts +50 -0
  59. package/dist/constraint-runtime/tools/PolymarketSDK/clobShared.js +78 -0
  60. package/dist/constraint-runtime/tools/PolymarketSDK/createOrder.d.ts +24 -3
  61. package/dist/constraint-runtime/tools/PolymarketSDK/createOrder.js +31 -4
  62. package/dist/constraint-runtime/tools/PolymarketSDK/getMarket.d.ts +1 -0
  63. package/dist/constraint-runtime/tools/PolymarketSDK/getMarket.js +14 -3
  64. package/dist/constraint-runtime/tools/PolymarketSDK/getOrders.d.ts +11 -2
  65. package/dist/constraint-runtime/tools/PolymarketSDK/getOrders.js +23 -4
  66. package/dist/constraint-runtime/tools/PolymarketSDK/listMarkets.d.ts +4 -0
  67. package/dist/constraint-runtime/tools/PolymarketSDK/listMarkets.js +22 -2
  68. package/dist/context-compaction/auto-compact.js +7 -7
  69. package/dist/electron/config.js.map +1 -0
  70. package/dist/electron/dialogs.js.map +1 -0
  71. package/dist/electron/first-run.js +54 -54
  72. package/dist/electron/first-run.js.map +1 -0
  73. package/dist/electron/ipc.js.map +1 -0
  74. package/dist/electron/logger.js.map +1 -0
  75. package/dist/electron/main.js.map +1 -0
  76. package/dist/electron/menu.js.map +1 -0
  77. package/dist/electron/paths.js.map +1 -0
  78. package/dist/electron/server.js.map +1 -0
  79. package/dist/electron/tray.js.map +1 -0
  80. package/dist/electron/window.js.map +1 -0
  81. package/dist/electron-build/electron/first-run.js +54 -54
  82. package/dist/electron-preload.js.map +1 -1
  83. package/dist/electron.js.map +1 -1
  84. package/dist/index.js +113 -113
  85. package/dist/llm/llm-judgment-client.js +102 -102
  86. package/dist/llm/pi-ai.js +48 -48
  87. package/dist/llm/system-prompt/layers/channel/human-async.md +41 -41
  88. package/dist/llm/system-prompt/layers/channel/local.md +14 -14
  89. package/dist/llm/system-prompt/layers/channel/p2p-agent.md +18 -18
  90. package/dist/llm/system-prompt/layers/channel/p2p-peer-sync.md +51 -51
  91. package/dist/llm/system-prompt/layers/channel/p2p-proactive.md +43 -43
  92. package/dist/llm/system-prompt/layers/channel/p2p-visitor.md +19 -19
  93. package/dist/llm/system-prompt/layers/channel/session-handoff.md +61 -61
  94. package/dist/llm/system-prompt/layers/core/artifacts_storage.md +89 -89
  95. package/dist/llm/system-prompt/layers/core/evenhandedness.md +21 -21
  96. package/dist/llm/system-prompt/layers/core/external-engagement.md +73 -73
  97. package/dist/llm/system-prompt/layers/core/hibs_reminders.md +15 -15
  98. package/dist/llm/system-prompt/layers/core/identity.md +48 -48
  99. package/dist/llm/system-prompt/layers/core/knowledge.md +17 -17
  100. package/dist/llm/system-prompt/layers/core/memory_system.md +12 -12
  101. package/dist/llm/system-prompt/layers/core/network_filesystem.md +28 -28
  102. package/dist/llm/system-prompt/layers/core/refusal.md +37 -37
  103. package/dist/llm/system-prompt/layers/core/tone.md +31 -31
  104. package/dist/llm/system-prompt/layers/core/tools.thin.md +13 -13
  105. package/dist/llm/system-prompt/layers/core/wellbeing.md +41 -41
  106. package/dist/llm/system-prompt/layers/role/architect.md +20 -20
  107. package/dist/llm/system-prompt/layers/role/expert.md +19 -19
  108. package/dist/llm/system-prompt/layers/role/implementer.md +15 -15
  109. package/dist/llm/system-prompt/layers/role/security.md +15 -15
  110. package/dist/llm/system-prompt/layers/tool/artifacts.md +72 -72
  111. package/dist/llm/system-prompt/layers/tool/bash.md +25 -25
  112. package/dist/llm/system-prompt/layers/tool/goal_handoff.md +77 -77
  113. package/dist/llm/system-prompt/layers/tool/hibs_api.md +171 -171
  114. package/dist/llm/system-prompt/layers/tool/image_search.md +70 -70
  115. package/dist/llm/system-prompt/layers/tool/manifest.md +89 -89
  116. package/dist/llm/system-prompt/layers/tool/mcp_apps.md +53 -53
  117. package/dist/llm/system-prompt/layers/tool/p2p_request.md +61 -61
  118. package/dist/llm/system-prompt/layers/tool/web_search.md +83 -83
  119. package/dist/llm/tool-manifest/ask_user_input.js +1 -1
  120. package/dist/llm/tool-manifest/bash.js +3 -3
  121. package/dist/llm/tool-manifest/create_file.js +4 -4
  122. package/dist/llm/tool-manifest/fetch_sports_data.js +3 -3
  123. package/dist/llm/tool-manifest/image_search.js +2 -2
  124. package/dist/llm/tool-manifest/index.js +59 -0
  125. package/dist/llm/tool-manifest/mcp.js +2 -2
  126. package/dist/llm/tool-manifest/message_compose.js +3 -3
  127. package/dist/llm/tool-manifest/places.js +2 -2
  128. package/dist/llm/tool-manifest/present_files.js +1 -1
  129. package/dist/llm/tool-manifest/recipe.js +2 -2
  130. package/dist/llm/tool-manifest/str_replace.js +5 -5
  131. package/dist/llm/tool-manifest/view.js +3 -3
  132. package/dist/llm/tool-manifest/weather.js +4 -4
  133. package/dist/llm/tool-manifest/web.js +4 -4
  134. package/dist/orbitdb/cid-database.js +41 -0
  135. package/dist/orbitdb/did-catalog-replication.js +199 -0
  136. package/dist/orbitdb/trajectory-store.js +150 -0
  137. package/dist/pi-ecosystem-judgment/causal-judge.js +51 -51
  138. package/dist/pi-ecosystem-judgment/distill-prompt.js +42 -42
  139. package/dist/pi-ecosystem-judgment/distillation.js +14 -14
  140. package/dist/pi-ecosystem-judgment/monitor-gate.js +28 -28
  141. package/dist/pi-ecosystem-judgment/value-injection.js +102 -102
  142. package/dist/security/context-router-tool.js +15 -15
  143. package/dist/storage/did-catalog-bridge.js +271 -0
  144. package/dist/utils/auto-update.js.map +1 -0
  145. package/dist/web/api-config.html +779 -779
  146. package/dist/web/client-hearth.js +10 -10
  147. package/dist/web/client.js +141 -0
  148. package/dist/web/client.js.map +7 -0
  149. package/dist/web/components/wallet-viem.mjs +118 -118
  150. package/dist/web/index.html +472 -424
  151. package/dist/web/manifest.json +20 -20
  152. package/dist/web/server.js +290 -13
  153. package/dist/web/style.css +4946 -4914
  154. package/package.json +193 -193
  155. package/scripts/build-cli.js +215 -215
  156. package/scripts/build-web.ts +130 -130
  157. package/scripts/postinstall.js +152 -152
  158. package/bin/bolloon.js +0 -157
  159. package/dist/pi-ecosystem-colony/index.js +0 -365
  160. package/dist/social/ant-colony/AdaptiveHeartbeat.js +0 -101
  161. package/dist/social/ant-colony/PheromoneEngine.js +0 -227
  162. package/dist/social/ant-colony/index.js +0 -6
  163. package/dist/social/ant-colony/types.js +0 -24
  164. package/dist/test/ai-judgment-test.js +0 -80
  165. package/dist/test/bollharness-integration.test.js +0 -318
  166. package/dist/test/channel-agent-multi-dialogue.js +0 -205
  167. package/dist/test/channel-heartbeat-agent-test.js +0 -201
  168. package/dist/test/constraint-layer.test.js +0 -164
  169. package/dist/test/diap-identity-test.js +0 -172
  170. package/dist/test/diap-quick-test.js +0 -62
  171. package/dist/test/global-shared-context.test.js +0 -315
  172. package/dist/test/harness-judgment-injection.test.js +0 -246
  173. package/dist/test/harness-workflow-integrator-test.js +0 -228
  174. package/dist/test/human-value-store.test.js +0 -243
  175. package/dist/test/hybrid-integration-test.js +0 -118
  176. package/dist/test/hybrid-messenger-verify.js +0 -55
  177. package/dist/test/iroh-bistream-debug.js +0 -38
  178. package/dist/test/iroh-communication.test.js +0 -66
  179. package/dist/test/iroh-debug-test.js +0 -57
  180. package/dist/test/iroh-diap-test.js +0 -71
  181. package/dist/test/iroh-direct-connect.js +0 -55
  182. package/dist/test/iroh-e2e-fixed.js +0 -89
  183. package/dist/test/iroh-e2e-same-process.js +0 -63
  184. package/dist/test/iroh-e2e.js +0 -66
  185. package/dist/test/iroh-final-e2e.js +0 -72
  186. package/dist/test/iroh-relay-test.js +0 -37
  187. package/dist/test/iroh-simple-test.js +0 -41
  188. package/dist/test/iroh-transport-verify.js +0 -54
  189. package/dist/test/iroh-transport.test.js +0 -37
  190. package/dist/test/iroh-two-nodes.js +0 -70
  191. package/dist/test/iroh-verify.js +0 -44
  192. package/dist/test/judgment-decision.test.js +0 -219
  193. package/dist/test/llm-judgment-integration.test.js +0 -220
  194. package/dist/test/p2p-agent-complex-dialogue.js +0 -385
  195. package/dist/test/p2p-agent-dialogue.js +0 -341
  196. package/dist/test/p2p-agent-full-bidirectional.js +0 -510
  197. package/dist/test/p2p-agent-harness-flow.js +0 -437
  198. package/dist/test/p2p-agent-harness-single.js +0 -143
  199. package/dist/test/p2p-ai-dialogue-test.js +0 -318
  200. package/dist/test/p2p-cid-connect-test.js +0 -195
  201. package/dist/test/p2p-connect-receiver.js +0 -69
  202. package/dist/test/p2p-doc-transfer.js +0 -110
  203. package/dist/test/p2p-identity-page-test.js +0 -77
  204. package/dist/test/p2p-iroh-test.js +0 -171
  205. package/dist/test/p2p-minimal-test.js +0 -241
  206. package/dist/test/p2p-node-1.js +0 -148
  207. package/dist/test/p2p-node-2.js +0 -148
  208. package/dist/test/p2p-server.js +0 -281
  209. package/dist/test/p2p-two-nodes-test.js +0 -438
  210. package/dist/test/pi-sdk.test.js +0 -44
  211. package/dist/test/set-persona.js +0 -40
  212. package/dist/test/simple.test.js +0 -9
  213. package/dist/test/storage-integration.test.js +0 -150
  214. package/dist/test/subagent-manager.test.js +0 -276
  215. package/dist/test/test-gate-flow.test.js +0 -81
  216. package/dist/test/workflow-engine.test.js +0 -87
  217. package/dist/test/workflow-pivot-loop.test.js +0 -246
@@ -48,6 +48,65 @@ export function getToolsByLayer(layerId) {
48
48
  *
49
49
  * 不包含: 完整 parameters schema (那是 PiAI 客户端在调用时读)
50
50
  */
51
+ /**
52
+ * 把 ToolManifest 转成 OpenAI function calling 格式 (tools 数组)
53
+ * 用于 native tool_choice: "auto" 模式, 让 LLM 选择调用.
54
+ *
55
+ * 注意: 递归处理嵌套参数 (type='object' 的 properties / type='array' 的 items)
56
+ */
57
+ export function formatForOpenAI(tools) {
58
+ const list = tools ?? ALL;
59
+ return list.map((t) => {
60
+ const properties = {};
61
+ const required = [];
62
+ for (const p of t.parameters) {
63
+ properties[p.name] = convertParameter(p);
64
+ if (p.required)
65
+ required.push(p.name);
66
+ }
67
+ return {
68
+ type: 'function',
69
+ function: {
70
+ name: t.id,
71
+ description: t.oneLine,
72
+ parameters: {
73
+ type: 'object',
74
+ properties,
75
+ required,
76
+ },
77
+ },
78
+ };
79
+ });
80
+ }
81
+ function convertParameter(p) {
82
+ const schema = { type: p.type === 'enum' ? 'string' : p.type };
83
+ if (p.description)
84
+ schema.description = p.description;
85
+ if (p.enumValues)
86
+ schema.enum = p.enumValues;
87
+ if (p.default !== undefined)
88
+ schema.default = p.default;
89
+ if (p.minimum !== undefined)
90
+ schema.minimum = p.minimum;
91
+ if (p.maximum !== undefined)
92
+ schema.maximum = p.maximum;
93
+ if (p.format)
94
+ schema.format = p.format;
95
+ if (p.type === 'object' && p.properties) {
96
+ schema.properties = {};
97
+ for (const sub of p.properties) {
98
+ schema.properties[sub.name] = convertParameter(sub);
99
+ }
100
+ }
101
+ if (p.type === 'array' && p.items) {
102
+ schema.items = convertParameter(p.items);
103
+ if (p.minItems !== undefined)
104
+ schema.minItems = p.minItems;
105
+ if (p.maxItems !== undefined)
106
+ schema.maxItems = p.maxItems;
107
+ }
108
+ return schema;
109
+ }
51
110
  export function formatForPrompt(tools) {
52
111
  const list = tools ?? ALL;
53
112
  const lines = [
@@ -15,7 +15,7 @@ export const search_mcp_registry = {
15
15
  parameters: [
16
16
  { name: 'keywords', type: 'array', required: true, description: '搜索关键词', minItems: 1, items: { name: 'kw', type: 'string', required: true, description: '关键词' } }
17
17
  ],
18
- callExample: `[TOOL:search_mcp_registry]
18
+ callExample: `[TOOL:search_mcp_registry]
19
19
  [P:keywords]${'${'}JSON.stringify(["calendar","schedule"])[ENDTOOL]`,
20
20
  layerId: 'tool.mcp_apps',
21
21
  };
@@ -37,7 +37,7 @@ export const suggest_connectors = {
37
37
  parameters: [
38
38
  { name: 'uuids', type: 'array', required: true, description: '从 search_mcp_registry 结果的 directoryUuid 复制', minItems: 1, items: { name: 'u', type: 'string', required: true, description: 'uuid' } }
39
39
  ],
40
- callExample: `[TOOL:suggest_connectors]
40
+ callExample: `[TOOL:suggest_connectors]
41
41
  [P:uuids]${'${'}JSON.stringify(["uuid-from-search-result"])[ENDTOOL]`,
42
42
  layerId: 'tool.mcp_apps',
43
43
  };
@@ -23,9 +23,9 @@ export const message_compose_v1 = {
23
23
  ],
24
24
  } },
25
25
  ],
26
- callExample: `[TOOL:message_compose_v1]
27
- [P:kind]email
28
- [P:summary_title]Follow-up on Q3 review
26
+ callExample: `[TOOL:message_compose_v1]
27
+ [P:kind]email
28
+ [P:summary_title]Follow-up on Q3 review
29
29
  [P:variants]${'${'}JSON.stringify([{"label":"direct","subject":"Quick follow-up","body":"..."}])[ENDTOOL]`,
30
30
  layerId: 'tool.manifest',
31
31
  };
@@ -23,7 +23,7 @@ export const places_search = {
23
23
  { name: 'location_bias_lng', type: 'number', required: false, description: '可选经度' },
24
24
  { name: 'location_bias_radius', type: 'number', required: false, description: '米 (默认 5000)', default: 5000 }
25
25
  ],
26
- callExample: `[TOOL:places_search]
26
+ callExample: `[TOOL:places_search]
27
27
  [P:queries]${'${'}JSON.stringify([{"query":"ramen restaurants Tokyo"}])[ENDTOOL]`,
28
28
  layerId: 'tool.manifest',
29
29
  };
@@ -77,7 +77,7 @@ export const places_map_display_v0 = {
77
77
  { name: 'title', type: 'string', required: false, description: '行程标题' },
78
78
  { name: 'narrative', type: 'string', required: false, description: '行程叙述' },
79
79
  ],
80
- callExample: `[TOOL:places_map_display_v0]
80
+ callExample: `[TOOL:places_map_display_v0]
81
81
  [P:locations]${'${'}JSON.stringify([{"name":"Senso-ji","latitude":35.7148,"longitude":139.7967,"place_id":"ChIJ..."}])[ENDTOOL]`,
82
82
  layerId: 'tool.manifest',
83
83
  };
@@ -15,7 +15,7 @@ export const present_files = {
15
15
  parameters: [
16
16
  { name: 'filepaths', type: 'array', required: true, description: '至少 1 个文件路径', minItems: 1, items: { name: 'path', type: 'string', required: true, description: '容器内路径' } },
17
17
  ],
18
- callExample: `[TOOL:present_files]
18
+ callExample: `[TOOL:present_files]
19
19
  [P:filepaths]${'${'}JSON.stringify(["/home/bolloon/report.md"])[ENDTOOL]`,
20
20
  layerId: 'tool.manifest',
21
21
  };
@@ -33,8 +33,8 @@ export const recipe_display_v0 = {
33
33
  } },
34
34
  { name: 'notes', type: 'string', required: false, description: '附加说明' },
35
35
  ],
36
- callExample: `[TOOL:recipe_display_v0]
37
- [P:title]Pasta
36
+ callExample: `[TOOL:recipe_display_v0]
37
+ [P:title]Pasta
38
38
  [P:ingredients]${'${'}JSON.stringify([{"id":"a1","amount":500,"name":"pasta"}])[ENDTOOL]`,
39
39
  layerId: 'tool.manifest',
40
40
  };
@@ -17,11 +17,11 @@ export const str_replace = {
17
17
  { name: 'new_str', type: 'string', required: true, description: '新字符串 (空字符串 = 删除)', default: '' },
18
18
  { name: 'path', type: 'string', required: true, description: '文件路径' },
19
19
  ],
20
- callExample: `[TOOL:str_replace]
21
- [P:description]修复 typo
22
- [P:old_str]recieve
23
- [P:new_str]receive
24
- [P:path]/home/bolloon/main.py
20
+ callExample: `[TOOL:str_replace]
21
+ [P:description]修复 typo
22
+ [P:old_str]recieve
23
+ [P:new_str]receive
24
+ [P:path]/home/bolloon/main.py
25
25
  [ENDTOOL]`,
26
26
  layerId: 'tool.manifest',
27
27
  };
@@ -16,9 +16,9 @@ export const view = {
16
16
  { name: 'path', type: 'string', required: true, description: '绝对路径' },
17
17
  { name: 'view_range', type: 'array', required: false, description: '[start, end] 1-indexed, end=-1 表示到末尾', minItems: 2, maxItems: 2, items: { name: 'r', type: 'integer', required: true, description: '行号' } }
18
18
  ],
19
- callExample: `[TOOL:view]
20
- [P:description]读 README
21
- [P:path]/home/bolloon/README.md
19
+ callExample: `[TOOL:view]
20
+ [P:description]读 README
21
+ [P:path]/home/bolloon/README.md
22
22
  [ENDTOOL]`,
23
23
  layerId: 'tool.manifest',
24
24
  };
@@ -18,10 +18,10 @@ export const weather_fetch = {
18
18
  { name: 'latitude', type: 'number', required: true, description: '纬度' },
19
19
  { name: 'longitude', type: 'number', required: true, description: '经度' },
20
20
  ],
21
- callExample: `[TOOL:weather_fetch]
22
- [P:location_name]San Francisco, CA
23
- [P:latitude]37.78
24
- [P:longitude]-122.41
21
+ callExample: `[TOOL:weather_fetch]
22
+ [P:location_name]San Francisco, CA
23
+ [P:latitude]37.78
24
+ [P:longitude]-122.41
25
25
  [ENDTOOL]`,
26
26
  layerId: 'tool.manifest',
27
27
  };
@@ -15,8 +15,8 @@ export const web_search = {
15
15
  parameters: [
16
16
  { name: 'query', type: 'string', required: true, description: '搜索查询 (1-6 词最佳)' },
17
17
  ],
18
- callExample: `[TOOL:web_search]
19
- [P:query]S&P 500 current price
18
+ callExample: `[TOOL:web_search]
19
+ [P:query]S&P 500 current price
20
20
  [ENDTOOL]`,
21
21
  layerId: 'tool.web_search',
22
22
  };
@@ -44,8 +44,8 @@ export const web_fetch = {
44
44
  { name: 'web_fetch_rate_limit_key', type: 'string', required: false, description: '速率限制键' },
45
45
  { name: 'web_fetch_rate_limit_dark_launch', type: 'boolean', required: false, description: '速率限制暗启动模式' },
46
46
  ],
47
- callExample: `[TOOL:web_fetch]
48
- [P:url]https://example.com/article
47
+ callExample: `[TOOL:web_fetch]
48
+ [P:url]https://example.com/article
49
49
  [ENDTOOL]`,
50
50
  layerId: 'tool.manifest',
51
51
  };
@@ -151,6 +151,47 @@ export class OrbitDBAdapter {
151
151
  await this.node.helia.blockstore.put(CID.parse(rec.id), dagCbor.encode(rec));
152
152
  return `bolloon-cid://${rec.id}`;
153
153
  }
154
+ /**
155
+ * 2026-08-08: 在同一 OrbitDB 实例上打开附加 store。
156
+ * events 类型 = append-only 事件流 (WAL 复制 / 轨迹流用).
157
+ * 返回的 store 是通用适配 (put=keyvalue / add=events).
158
+ */
159
+ async openStore(name, type = 'keyvalue') {
160
+ await this.ensure();
161
+ const raw = (await this._orbitdb.open(name, { type }));
162
+ const store = {
163
+ address: raw.address,
164
+ put: async (key, value) => { await raw.put(key, JSON.parse(JSON.stringify(value))); },
165
+ add: async (value) => { await raw.add(JSON.parse(JSON.stringify(value))); },
166
+ all: async () => {
167
+ const entries = (await raw.all());
168
+ // events store 的 all() 返回 { hash, payload } — 统一成 { key, value }
169
+ return entries.map(e => ({
170
+ key: String(e.key ?? e.hash ?? ''),
171
+ value: e.payload !== undefined ? e.payload : e.value,
172
+ }));
173
+ },
174
+ get: async (key) => { return (await raw.get(key)); },
175
+ onChange: (fn) => {
176
+ const on = () => { try {
177
+ fn();
178
+ }
179
+ catch { /* 回调异常忽略 */ } };
180
+ raw.events?.on?.('join', on);
181
+ raw.events?.on?.('write', on);
182
+ raw.events?.on?.('replicate', on);
183
+ return () => {
184
+ try {
185
+ raw.events?.off?.('join', on);
186
+ raw.events?.off?.('write', on);
187
+ raw.events?.off?.('replicate', on);
188
+ }
189
+ catch { /* 忽略 */ }
190
+ };
191
+ },
192
+ };
193
+ return store;
194
+ }
154
195
  async close() {
155
196
  try {
156
197
  await this._orbitdb?.stop();
@@ -0,0 +1,199 @@
1
+ /**
2
+ * did-catalog-replication.ts — 把 DID 目录的 WAL 事件流挂进 OrbitDB 自动复制 (2026-08-08)
3
+ *
4
+ * 用户需求: "把 DID catalog 挂进 OrbitDB 的自动 replication, 并用 OrbitDB 事件流驱动跨设备复制".
5
+ *
6
+ * 机制:
7
+ * 1. 打开 events store (append-only 事件流): `bolloon-did-wal-<did短名>`
8
+ * — 与 bolloon-cid-store 共享同一 helia + OrbitDB 实例 (openStore).
9
+ * 2. 发布 (publish): 本地 catalog 每有新 WAL 事件 (seq > lastPublishedSeq) → store.add(ev)
10
+ * — OrbitDB 自动把事件流同步到订阅同地址的 peers (libp2p pubsub + IPFS 块).
11
+ * 3. 拉取 (sync): 订阅 store 变更 (join/write/replicate) + 定时轮询 →
12
+ * 读 store 全量事件 → 过滤 seq > lastAppliedSeq → catalog.syncRemote(events)
13
+ * → persist → 推进 lastAppliedSeq. (LWW 合并, 幂等)
14
+ * 4. 状态: lastPublishedSeq / lastAppliedSeq / storeAddress 持久化在
15
+ * ~/.bolloon/did-catalog/<did>/replication.json, 重启续传.
16
+ *
17
+ * 失败静默: 复制是增强层 — OrbitDB 不可用 (离线/首次启动) 时 DID 目录本身照常工作,
18
+ * 网络恢复后按 seq 增量补齐 (断点续传语义).
19
+ */
20
+ import * as fs from 'fs/promises';
21
+ import * as os from 'os';
22
+ import * as path from 'path';
23
+ import { registryOpen, didDirName } from '../storage/did-catalog.js';
24
+ import { getCIDDatabase } from './cid-database.js';
25
+ const homeRoot = (h) => h || process.env.HOME || os.homedir() || '/tmp';
26
+ /** events store 名: 按 DID 确定性命名 (跨设备同名 → 同一复制流) */
27
+ export function replicationStoreName(did) {
28
+ return `bolloon-did-wal-${didDirName(did)}`;
29
+ }
30
+ function statePath(home, did) {
31
+ return path.join(homeRoot(home), '.bolloon', 'did-catalog', didDirName(did), 'replication.json');
32
+ }
33
+ async function loadState(home, did) {
34
+ try {
35
+ const raw = JSON.parse(await fs.readFile(statePath(home, did), 'utf-8'));
36
+ return {
37
+ // WAL seq 从 0 开始 → 游标默认 -1 (否则 seq=0 的首条事件永远不发布/不应用)
38
+ lastPublishedSeq: Number(raw.lastPublishedSeq) || -1,
39
+ lastAppliedSeq: Number(raw.lastAppliedSeq) || -1,
40
+ storeAddress: String(raw.storeAddress || ''),
41
+ updatedAt: Number(raw.updatedAt) || Date.now(),
42
+ };
43
+ }
44
+ catch {
45
+ return { lastPublishedSeq: -1, lastAppliedSeq: -1, storeAddress: '', updatedAt: Date.now() };
46
+ }
47
+ }
48
+ async function saveState(home, did, s) {
49
+ try {
50
+ const p = statePath(home, did);
51
+ await fs.mkdir(path.dirname(p), { recursive: true });
52
+ await fs.writeFile(p, JSON.stringify({ ...s, updatedAt: Date.now() }, null, 2), 'utf-8');
53
+ }
54
+ catch { /* 状态写失败不致命 */ }
55
+ }
56
+ /** WAL 事件是否属于本 DID (跨设备只合并本用户数据) */
57
+ function eventOfDid(e, did) {
58
+ return !e.did || e.did === did;
59
+ }
60
+ /**
61
+ * 启动 DID 目录 → OrbitDB 自动复制.
62
+ * 单例: 同一 did 只开一个复制流 (进程级 map, 复用 store).
63
+ */
64
+ export async function startDidCatalogReplication(did, opts = {}) {
65
+ const state = await loadState(opts.home, did);
66
+ const stats = { published: 0, applied: 0, merged: 0 };
67
+ let store = null;
68
+ let stopFns = [];
69
+ let timer = null;
70
+ let syncing = false;
71
+ let stopped = false;
72
+ const open = async () => {
73
+ if (store)
74
+ return store;
75
+ const db = opts.db || getCIDDatabase();
76
+ const s = await db.openStore(replicationStoreName(did), 'events');
77
+ store = s;
78
+ if (state.storeAddress && state.storeAddress !== s.address) {
79
+ // 远端地址已有记录而本地地址不同 → 以本地为准 (state 会在 publish 后刷新)
80
+ }
81
+ state.storeAddress = s.address;
82
+ await saveState(opts.home, did, state);
83
+ return s;
84
+ };
85
+ const catalog = async () => {
86
+ try {
87
+ return await registryOpen(did, opts.home ? { home: opts.home } : undefined);
88
+ }
89
+ catch {
90
+ return null;
91
+ }
92
+ };
93
+ const publishPending = async () => {
94
+ try {
95
+ const cat = await catalog();
96
+ if (!cat)
97
+ return 0;
98
+ const s = await open();
99
+ const pending = cat.walEvents.filter(e => e.seq > state.lastPublishedSeq && eventOfDid(e, did));
100
+ if (pending.length === 0)
101
+ return 0;
102
+ for (const ev of pending) {
103
+ await s.add(ev);
104
+ state.lastPublishedSeq = Math.max(state.lastPublishedSeq, ev.seq);
105
+ }
106
+ stats.published += pending.length;
107
+ await saveState(opts.home, did, state);
108
+ return pending.length;
109
+ }
110
+ catch {
111
+ return 0; // OrbitDB 不可用 → 静默, 下次轮询重试 (断点续传)
112
+ }
113
+ };
114
+ const syncNow = async () => {
115
+ if (syncing || stopped)
116
+ return { applied: 0, merged: 0 };
117
+ syncing = true;
118
+ try {
119
+ const cat = await catalog();
120
+ if (!cat)
121
+ return { applied: 0, merged: 0 };
122
+ const s = await open();
123
+ const entries = await s.all();
124
+ const events = entries
125
+ .map(e => e.value)
126
+ .filter((e) => !!e && typeof e === 'object' && typeof e.seq === 'number')
127
+ .filter(e => e.seq > state.lastAppliedSeq && eventOfDid(e, did))
128
+ .sort((a, b) => a.seq - b.seq);
129
+ if (events.length === 0)
130
+ return { applied: 0, merged: 0 };
131
+ const r = cat.syncRemote(events);
132
+ await cat.persist();
133
+ state.lastAppliedSeq = Math.max(state.lastAppliedSeq, ...events.map(e => e.seq));
134
+ stats.applied += r.applied;
135
+ stats.merged += r.merged;
136
+ await saveState(opts.home, did, state);
137
+ return r;
138
+ }
139
+ catch {
140
+ return { applied: 0, merged: 0 };
141
+ }
142
+ finally {
143
+ syncing = false;
144
+ }
145
+ };
146
+ // 订阅 store 变更 (远端 join/写) → 拉取合并 (去抖, 事件可能密集)
147
+ const unsub = await (async () => {
148
+ try {
149
+ const s = await open();
150
+ let debounce = null;
151
+ const on = () => {
152
+ if (debounce)
153
+ clearTimeout(debounce);
154
+ debounce = setTimeout(() => { syncNow().catch(() => { }); }, 500);
155
+ };
156
+ const off = s.onChange(on);
157
+ return () => { off(); if (debounce)
158
+ clearTimeout(debounce); };
159
+ }
160
+ catch {
161
+ return () => { };
162
+ }
163
+ })();
164
+ stopFns.push(unsub);
165
+ // 首次: 拉一次远端 (重启续传) + 发布本地积压
166
+ await syncNow().catch(() => { });
167
+ await publishPending().catch(() => { });
168
+ if (opts.auto !== false) {
169
+ timer = setInterval(() => {
170
+ publishPending().catch(() => { });
171
+ syncNow().catch(() => { });
172
+ }, opts.intervalMs || 30_000);
173
+ // 不阻止进程退出
174
+ timer.unref?.();
175
+ }
176
+ const stop = async () => {
177
+ stopped = true;
178
+ if (timer)
179
+ clearInterval(timer);
180
+ for (const f of stopFns) {
181
+ try {
182
+ f();
183
+ }
184
+ catch { /* 忽略 */ }
185
+ }
186
+ stopFns = [];
187
+ // 停前最后发布一次 (尽力而为)
188
+ await publishPending().catch(() => { });
189
+ };
190
+ return {
191
+ did,
192
+ storeName: replicationStoreName(did),
193
+ storeAddress: state.storeAddress,
194
+ stats,
195
+ publishPending,
196
+ syncNow,
197
+ stop,
198
+ };
199
+ }
@@ -0,0 +1,150 @@
1
+ /**
2
+ * trajectory-store.ts — 智能体运行轨迹 (trajectory) 记录 (2026-08-08)
3
+ *
4
+ * 用户需求: "把 bolloon agent 运行过程产生的轨迹保存进 orbitdb. 智能体运行的轨迹也可以落盘."
5
+ *
6
+ * 双层持久化:
7
+ * 1. 落盘: ~/.bolloon/trajectories/<runId>.json (审计/离线可读)
8
+ * 2. OrbitDB: keyvalue store `bolloon-trajectories-<did>`, key=runId
9
+ * (与 bolloon-cid-store 共享 helia/OrbitDB 实例; 跨设备复制 + 内容寻址)
10
+ *
11
+ * 轨迹内容: 一轮运行 (用户输入 → 事件/工具调用步骤 → AI 回复) 的完整回放.
12
+ * 失败静默: recorder 是增强层, 任何持久化失败都不影响 agent 主流程.
13
+ */
14
+ import * as fs from 'fs/promises';
15
+ import * as os from 'os';
16
+ import * as path from 'path';
17
+ import { getCIDDatabase } from './cid-database.js';
18
+ import { didDirName } from '../storage/did-catalog.js';
19
+ const homeRoot = (h) => h || process.env.HOME || os.homedir() || '/tmp';
20
+ export function newRunId(agentId) {
21
+ const ts = new Date().toISOString().replace(/[:.]/g, '-');
22
+ return `${ts}__${String(agentId).replace(/[^a-zA-Z0-9_-]/g, '_').slice(0, 40)}`;
23
+ }
24
+ function trajectoriesDir(home) {
25
+ return path.join(homeRoot(home), '.bolloon', 'trajectories');
26
+ }
27
+ /** 1. 落盘: ~/.bolloon/trajectories/<runId>.json */
28
+ export async function saveTrajectoryToDisk(run, home) {
29
+ const dir = trajectoriesDir(home);
30
+ await fs.mkdir(dir, { recursive: true });
31
+ const file = path.join(dir, `${run.runId}.json`);
32
+ await fs.writeFile(file, JSON.stringify(run, null, 2), 'utf-8');
33
+ return file;
34
+ }
35
+ function trajectoryStoreName(did) {
36
+ return `bolloon-trajectories-${didDirName(did)}`;
37
+ }
38
+ /** 2. OrbitDB: keyvalue store `bolloon-trajectories-<did>`, key=runId */
39
+ export async function saveTrajectoryToOrbit(run, opts = {}) {
40
+ try {
41
+ const did = run.did;
42
+ if (!did)
43
+ return false;
44
+ if (opts.orbit === false)
45
+ return false;
46
+ const db = opts.db !== undefined ? opts.db : getCIDDatabase();
47
+ if (!db)
48
+ return false;
49
+ const store = await db.openStore(trajectoryStoreName(did), 'keyvalue');
50
+ await store.put(run.runId, run);
51
+ return true;
52
+ }
53
+ catch {
54
+ return false;
55
+ }
56
+ }
57
+ /** 一步到位: 落盘 + OrbitDB (各自失败静默, 返回落盘路径) */
58
+ export async function recordTrajectory(run, opts = {}) {
59
+ let file = null;
60
+ try {
61
+ file = await saveTrajectoryToDisk(run, opts.home);
62
+ }
63
+ catch { /* 落盘失败静默 */ }
64
+ await saveTrajectoryToOrbit(run, opts).catch(() => false);
65
+ return file;
66
+ }
67
+ /** 列出一轮轨迹 (文件名 → runId, 新→旧) */
68
+ export async function listTrajectories(home, limit = 50) {
69
+ try {
70
+ const dir = trajectoriesDir(home);
71
+ const files = (await fs.readdir(dir)).filter(f => f.endsWith('.json')).sort().reverse().slice(0, limit);
72
+ return files.map(f => ({ runId: f.replace(/\.json$/, ''), file: path.join(dir, f) }));
73
+ }
74
+ catch {
75
+ return [];
76
+ }
77
+ }
78
+ /** 读一轮轨迹 */
79
+ export async function loadTrajectory(runId, home) {
80
+ try {
81
+ const raw = await fs.readFile(path.join(trajectoriesDir(home), `${runId}.json`), 'utf-8');
82
+ return JSON.parse(raw);
83
+ }
84
+ catch {
85
+ return null;
86
+ }
87
+ }
88
+ /** 内存版 recorder: 采集步骤/消息, endRun 时产出 TrajectoryRun (持久化交给 recordTrajectory) */
89
+ export class TrajectoryRecorder {
90
+ runId;
91
+ agentId;
92
+ channelId;
93
+ did;
94
+ input;
95
+ startedAt;
96
+ model;
97
+ steps = [];
98
+ messages = [];
99
+ ended = false;
100
+ constructor(opts) {
101
+ this.runId = opts.runId || newRunId(opts.agentId);
102
+ this.agentId = opts.agentId;
103
+ this.channelId = opts.channelId;
104
+ this.did = opts.did;
105
+ this.input = opts.input;
106
+ this.model = opts.model;
107
+ this.startedAt = Date.now();
108
+ }
109
+ /** 记录一个流事件 (tool/status/error/thinking/phase/done) */
110
+ recordStep(ev) {
111
+ if (this.ended)
112
+ return;
113
+ const type = String(ev.type || 'status');
114
+ const kind = (['tool', 'status', 'error', 'thinking', 'phase', 'done'].includes(type) ? type : 'status');
115
+ const name = kind === 'tool' ? String(ev.tool || 'tool') : kind;
116
+ const content = typeof ev.content === 'string' ? ev.content : '';
117
+ // 同类型连续状态压缩 (防步骤爆炸): 只保留 content 变化的
118
+ const last = this.steps[this.steps.length - 1];
119
+ if (last && last.name === name && last.kind === kind && last.content === content)
120
+ return;
121
+ this.steps.push({ name, kind, content: content.slice(0, 2000), ts: Date.now() });
122
+ }
123
+ /** 记录一条消息 (输入/中间/回复) */
124
+ recordMessage(role, content) {
125
+ if (this.ended)
126
+ return;
127
+ this.messages.push({ role, content: content.slice(0, 8000), ts: Date.now() });
128
+ }
129
+ /** 结束一轮 → 产出 TrajectoryRun (不可再 record) */
130
+ endRun(reply, status = 'ok') {
131
+ this.ended = true;
132
+ const endedAt = Date.now();
133
+ return {
134
+ runId: this.runId,
135
+ did: this.did,
136
+ agentId: this.agentId,
137
+ channelId: this.channelId,
138
+ input: this.input,
139
+ reply,
140
+ status,
141
+ model: this.model,
142
+ startedAt: this.startedAt,
143
+ endedAt,
144
+ durationMs: endedAt - this.startedAt,
145
+ steps: this.steps,
146
+ messages: this.messages,
147
+ };
148
+ }
149
+ get stepCount() { return this.steps.length; }
150
+ }