@bolloon/bolloon-agent 0.3.38 → 0.3.41

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 (213) hide show
  1. package/README.md +135 -135
  2. package/bin/bolloon-cli.cjs +183 -183
  3. package/bin/bolloon.cjs +0 -0
  4. package/bin/bolloon.js +157 -0
  5. package/dist/agents/constraint-layer.js +19 -19
  6. package/dist/agents/judgment-protocol.js +14 -14
  7. package/dist/agents/loop-review.js +58 -0
  8. package/dist/agents/pi-sdk-types.js +12 -12
  9. package/dist/agents/pi-sdk.js +113 -89
  10. package/dist/agents/workflow-pivot-loop.js +2 -2
  11. package/dist/bollharness/src/scripts/deploy-guard.js +8 -8
  12. package/dist/bollharness-integration/context-router-judgment.js +4 -4
  13. package/dist/bollharness-integration/context-router.js +292 -292
  14. package/dist/bollharness-integration/gate-state-machine.js +13 -13
  15. package/dist/bollharness-integration/llm-judgment-engine.js +134 -134
  16. package/dist/bollharness-integration/skill-adapter.js +21 -21
  17. package/dist/bootstrap/bootstrap.js +19 -1
  18. package/dist/bootstrap/chat-archiver.js +14 -14
  19. package/dist/bootstrap/context-os.js +21 -21
  20. package/dist/bootstrap/memory-compressor.js +13 -13
  21. package/dist/cli/mention-data.js +2 -1
  22. package/dist/cli-entry.js +36 -36
  23. package/dist/constraint-runtime/setup.js +1 -1
  24. package/dist/constraint-runtime/tools/PolymarketSDK/cancelOrder.d.ts +3 -13
  25. package/dist/constraint-runtime/tools/PolymarketSDK/cancelOrder.js +4 -16
  26. package/dist/constraint-runtime/tools/PolymarketSDK/createOrder.d.ts +3 -24
  27. package/dist/constraint-runtime/tools/PolymarketSDK/createOrder.js +4 -31
  28. package/dist/constraint-runtime/tools/PolymarketSDK/getMarket.d.ts +0 -1
  29. package/dist/constraint-runtime/tools/PolymarketSDK/getMarket.js +3 -14
  30. package/dist/constraint-runtime/tools/PolymarketSDK/getOrders.d.ts +2 -11
  31. package/dist/constraint-runtime/tools/PolymarketSDK/getOrders.js +4 -23
  32. package/dist/constraint-runtime/tools/PolymarketSDK/listMarkets.d.ts +0 -4
  33. package/dist/constraint-runtime/tools/PolymarketSDK/listMarkets.js +2 -22
  34. package/dist/context-compaction/auto-compact.js +7 -7
  35. package/dist/electron/first-run.js +54 -54
  36. package/dist/electron-build/electron/first-run.js +54 -54
  37. package/dist/electron-preload.js.map +1 -1
  38. package/dist/electron.js.map +1 -1
  39. package/dist/index.js +112 -112
  40. package/dist/llm/llm-judgment-client.js +102 -102
  41. package/dist/llm/pi-ai.js +48 -48
  42. package/dist/llm/system-prompt/layers/channel/human-async.md +41 -41
  43. package/dist/llm/system-prompt/layers/channel/local.md +14 -14
  44. package/dist/llm/system-prompt/layers/channel/p2p-agent.md +18 -18
  45. package/dist/llm/system-prompt/layers/channel/p2p-peer-sync.md +51 -51
  46. package/dist/llm/system-prompt/layers/channel/p2p-proactive.md +43 -43
  47. package/dist/llm/system-prompt/layers/channel/p2p-visitor.md +19 -19
  48. package/dist/llm/system-prompt/layers/channel/session-handoff.md +61 -61
  49. package/dist/llm/system-prompt/layers/core/artifacts_storage.md +89 -89
  50. package/dist/llm/system-prompt/layers/core/evenhandedness.md +21 -21
  51. package/dist/llm/system-prompt/layers/core/external-engagement.md +73 -73
  52. package/dist/llm/system-prompt/layers/core/hibs_reminders.md +15 -15
  53. package/dist/llm/system-prompt/layers/core/identity.md +48 -48
  54. package/dist/llm/system-prompt/layers/core/knowledge.md +17 -17
  55. package/dist/llm/system-prompt/layers/core/memory_system.md +12 -12
  56. package/dist/llm/system-prompt/layers/core/network_filesystem.md +28 -28
  57. package/dist/llm/system-prompt/layers/core/refusal.md +37 -37
  58. package/dist/llm/system-prompt/layers/core/tone.md +31 -31
  59. package/dist/llm/system-prompt/layers/core/tools.thin.md +13 -13
  60. package/dist/llm/system-prompt/layers/core/wellbeing.md +41 -41
  61. package/dist/llm/system-prompt/layers/role/architect.md +20 -20
  62. package/dist/llm/system-prompt/layers/role/expert.md +19 -19
  63. package/dist/llm/system-prompt/layers/role/implementer.md +15 -15
  64. package/dist/llm/system-prompt/layers/role/security.md +15 -15
  65. package/dist/llm/system-prompt/layers/tool/artifacts.md +72 -72
  66. package/dist/llm/system-prompt/layers/tool/bash.md +25 -25
  67. package/dist/llm/system-prompt/layers/tool/goal_handoff.md +77 -77
  68. package/dist/llm/system-prompt/layers/tool/hibs_api.md +171 -171
  69. package/dist/llm/system-prompt/layers/tool/image_search.md +70 -70
  70. package/dist/llm/system-prompt/layers/tool/manifest.md +89 -89
  71. package/dist/llm/system-prompt/layers/tool/mcp_apps.md +53 -53
  72. package/dist/llm/system-prompt/layers/tool/p2p_request.md +61 -61
  73. package/dist/llm/system-prompt/layers/tool/web_search.md +83 -83
  74. package/dist/llm/tool-manifest/ask_user_input.js +1 -1
  75. package/dist/llm/tool-manifest/bash.js +3 -3
  76. package/dist/llm/tool-manifest/create_file.js +4 -4
  77. package/dist/llm/tool-manifest/fetch_sports_data.js +3 -3
  78. package/dist/llm/tool-manifest/image_search.js +2 -2
  79. package/dist/llm/tool-manifest/index.js +0 -59
  80. package/dist/llm/tool-manifest/mcp.js +2 -2
  81. package/dist/llm/tool-manifest/message_compose.js +3 -3
  82. package/dist/llm/tool-manifest/places.js +2 -2
  83. package/dist/llm/tool-manifest/present_files.js +1 -1
  84. package/dist/llm/tool-manifest/recipe.js +2 -2
  85. package/dist/llm/tool-manifest/str_replace.js +5 -5
  86. package/dist/llm/tool-manifest/view.js +3 -3
  87. package/dist/llm/tool-manifest/weather.js +4 -4
  88. package/dist/llm/tool-manifest/web.js +4 -4
  89. package/dist/migration/external-agent-migrator.js +405 -0
  90. package/dist/pi-ecosystem-colony/index.js +365 -0
  91. package/dist/pi-ecosystem-judgment/causal-judge.js +51 -51
  92. package/dist/pi-ecosystem-judgment/distill-prompt.js +42 -42
  93. package/dist/pi-ecosystem-judgment/distillation.js +14 -14
  94. package/dist/pi-ecosystem-judgment/monitor-gate.js +28 -28
  95. package/dist/pi-ecosystem-judgment/value-injection.js +102 -102
  96. package/dist/security/context-router-tool.js +15 -15
  97. package/dist/social/ant-colony/AdaptiveHeartbeat.js +101 -0
  98. package/dist/social/ant-colony/PheromoneEngine.js +227 -0
  99. package/dist/social/ant-colony/index.js +6 -0
  100. package/dist/social/ant-colony/types.js +24 -0
  101. package/dist/test/ai-judgment-test.js +80 -0
  102. package/dist/test/bollharness-integration.test.js +318 -0
  103. package/dist/test/channel-agent-multi-dialogue.js +205 -0
  104. package/dist/test/channel-heartbeat-agent-test.js +201 -0
  105. package/dist/test/constraint-layer.test.js +164 -0
  106. package/dist/test/diap-identity-test.js +172 -0
  107. package/dist/test/diap-quick-test.js +62 -0
  108. package/dist/test/global-shared-context.test.js +315 -0
  109. package/dist/test/harness-judgment-injection.test.js +246 -0
  110. package/dist/test/harness-workflow-integrator-test.js +228 -0
  111. package/dist/test/human-value-store.test.js +243 -0
  112. package/dist/test/hybrid-integration-test.js +118 -0
  113. package/dist/test/hybrid-messenger-verify.js +55 -0
  114. package/dist/test/iroh-bistream-debug.js +38 -0
  115. package/dist/test/iroh-communication.test.js +66 -0
  116. package/dist/test/iroh-debug-test.js +57 -0
  117. package/dist/test/iroh-diap-test.js +71 -0
  118. package/dist/test/iroh-direct-connect.js +55 -0
  119. package/dist/test/iroh-e2e-fixed.js +89 -0
  120. package/dist/test/iroh-e2e-same-process.js +63 -0
  121. package/dist/test/iroh-e2e.js +66 -0
  122. package/dist/test/iroh-final-e2e.js +72 -0
  123. package/dist/test/iroh-relay-test.js +37 -0
  124. package/dist/test/iroh-simple-test.js +41 -0
  125. package/dist/test/iroh-transport-verify.js +54 -0
  126. package/dist/test/iroh-transport.test.js +37 -0
  127. package/dist/test/iroh-two-nodes.js +70 -0
  128. package/dist/test/iroh-verify.js +44 -0
  129. package/dist/test/judgment-decision.test.js +219 -0
  130. package/dist/test/llm-judgment-integration.test.js +220 -0
  131. package/dist/test/p2p-agent-complex-dialogue.js +385 -0
  132. package/dist/test/p2p-agent-dialogue.js +341 -0
  133. package/dist/test/p2p-agent-full-bidirectional.js +510 -0
  134. package/dist/test/p2p-agent-harness-flow.js +437 -0
  135. package/dist/test/p2p-agent-harness-single.js +143 -0
  136. package/dist/test/p2p-ai-dialogue-test.js +318 -0
  137. package/dist/test/p2p-cid-connect-test.js +195 -0
  138. package/dist/test/p2p-connect-receiver.js +69 -0
  139. package/dist/test/p2p-doc-transfer.js +110 -0
  140. package/dist/test/p2p-identity-page-test.js +77 -0
  141. package/dist/test/p2p-iroh-test.js +171 -0
  142. package/dist/test/p2p-minimal-test.js +241 -0
  143. package/dist/test/p2p-node-1.js +148 -0
  144. package/dist/test/p2p-node-2.js +148 -0
  145. package/dist/test/p2p-server.js +281 -0
  146. package/dist/test/p2p-two-nodes-test.js +438 -0
  147. package/dist/test/pi-sdk.test.js +44 -0
  148. package/dist/test/set-persona.js +40 -0
  149. package/dist/test/simple.test.js +9 -0
  150. package/dist/test/storage-integration.test.js +150 -0
  151. package/dist/test/subagent-manager.test.js +276 -0
  152. package/dist/test/test-gate-flow.test.js +81 -0
  153. package/dist/test/workflow-engine.test.js +87 -0
  154. package/dist/test/workflow-pivot-loop.test.js +246 -0
  155. package/dist/web/api-config.html +779 -779
  156. package/dist/web/client-hearth.js +10 -10
  157. package/dist/web/components/wallet-viem.mjs +118 -118
  158. package/dist/web/index.html +424 -424
  159. package/dist/web/manifest.json +20 -20
  160. package/dist/web/server.js +13 -13
  161. package/dist/web/style.css +4914 -4914
  162. package/package.json +193 -193
  163. package/scripts/build-cli.js +215 -215
  164. package/scripts/build-web.ts +130 -130
  165. package/scripts/postinstall.js +152 -152
  166. package/bin/bolloon-daemon.sh +0 -207
  167. package/dist/constraint-runtime/reference_data/archive_surface_snapshot.json +0 -63
  168. package/dist/constraint-runtime/reference_data/commands_snapshot.json +0 -1037
  169. package/dist/constraint-runtime/reference_data/subsystems/assistant.json +0 -8
  170. package/dist/constraint-runtime/reference_data/subsystems/bootstrap.json +0 -8
  171. package/dist/constraint-runtime/reference_data/subsystems/bridge.json +0 -32
  172. package/dist/constraint-runtime/reference_data/subsystems/buddy.json +0 -13
  173. package/dist/constraint-runtime/reference_data/subsystems/cli.json +0 -26
  174. package/dist/constraint-runtime/reference_data/subsystems/components.json +0 -32
  175. package/dist/constraint-runtime/reference_data/subsystems/constants.json +0 -28
  176. package/dist/constraint-runtime/reference_data/subsystems/coordinator.json +0 -8
  177. package/dist/constraint-runtime/reference_data/subsystems/entrypoints.json +0 -15
  178. package/dist/constraint-runtime/reference_data/subsystems/hooks.json +0 -32
  179. package/dist/constraint-runtime/reference_data/subsystems/keybindings.json +0 -21
  180. package/dist/constraint-runtime/reference_data/subsystems/memdir.json +0 -15
  181. package/dist/constraint-runtime/reference_data/subsystems/migrations.json +0 -18
  182. package/dist/constraint-runtime/reference_data/subsystems/moreright.json +0 -8
  183. package/dist/constraint-runtime/reference_data/subsystems/native_ts.json +0 -11
  184. package/dist/constraint-runtime/reference_data/subsystems/outputStyles.json +0 -8
  185. package/dist/constraint-runtime/reference_data/subsystems/plugins.json +0 -9
  186. package/dist/constraint-runtime/reference_data/subsystems/remote.json +0 -11
  187. package/dist/constraint-runtime/reference_data/subsystems/schemas.json +0 -8
  188. package/dist/constraint-runtime/reference_data/subsystems/screens.json +0 -10
  189. package/dist/constraint-runtime/reference_data/subsystems/server.json +0 -10
  190. package/dist/constraint-runtime/reference_data/subsystems/services.json +0 -32
  191. package/dist/constraint-runtime/reference_data/subsystems/skills.json +0 -27
  192. package/dist/constraint-runtime/reference_data/subsystems/state.json +0 -13
  193. package/dist/constraint-runtime/reference_data/subsystems/types.json +0 -18
  194. package/dist/constraint-runtime/reference_data/subsystems/upstreamproxy.json +0 -9
  195. package/dist/constraint-runtime/reference_data/subsystems/utils.json +0 -32
  196. package/dist/constraint-runtime/reference_data/subsystems/vim.json +0 -12
  197. package/dist/constraint-runtime/reference_data/subsystems/voice.json +0 -8
  198. package/dist/constraint-runtime/reference_data/tools_snapshot.json +0 -922
  199. package/dist/constraint-runtime/tools/PolymarketSDK/clobShared.d.ts +0 -50
  200. package/dist/constraint-runtime/tools/PolymarketSDK/clobShared.js +0 -78
  201. package/dist/electron/config.js.map +0 -1
  202. package/dist/electron/dialogs.js.map +0 -1
  203. package/dist/electron/first-run.js.map +0 -1
  204. package/dist/electron/ipc.js.map +0 -1
  205. package/dist/electron/logger.js.map +0 -1
  206. package/dist/electron/main.js.map +0 -1
  207. package/dist/electron/menu.js.map +0 -1
  208. package/dist/electron/paths.js.map +0 -1
  209. package/dist/electron/server.js.map +0 -1
  210. package/dist/electron/tray.js.map +0 -1
  211. package/dist/electron/window.js.map +0 -1
  212. package/dist/utils/auto-update.js.map +0 -1
  213. package/dist/web/client.js.map +0 -7
@@ -48,65 +48,6 @@ 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
- }
110
51
  export function formatForPrompt(tools) {
111
52
  const list = tools ?? ALL;
112
53
  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
  };
@@ -0,0 +1,405 @@
1
+ /**
2
+ * migration/external-agent-migrator.ts — 把本机 OpenClaw / Hermes 的智能体数据
3
+ * 按相同格式整理进 Bolloon 系统路径, 实现"无缝兼容" (性格 / 记忆 / 技能 / 文档).
4
+ *
5
+ * 动机 (2026-08-08, v0.3.39):
6
+ * - 用户在本机用 OpenClaw / Hermes 设计了智能体 (人格文档 + 技能 + 记忆 + 文档).
7
+ * - Bolloon 初始化加载时, 希望把这些"别人的系统"的数据按 Bolloon 的既有格式
8
+ * (persona 6 文件 / SKILL.md / memory sessions / context-os 资产) 迁移进来,
9
+ * 这样 Bolloon 能直接加载同一套性格 / 记忆 / 技能, 无缝兼容.
10
+ * - 隐式处理: 启动时静默跑, 失败不影响主流程; 完成后通告给用户 (见 report).
11
+ *
12
+ * 异构布局 (2026-08-08 v0.3.40):
13
+ * - OpenClaw 平铺在 ~/.openclaw/workspace/ (SOUL/IDENTITY/USER/AGENTS/TOOLS/MEMORY.md +
14
+ * skills/<name>/ + memory/*.md)
15
+ * - Hermes 根在 %LOCALAPPDATA%\hermes (Windows, 兜底 ~/.hermes), persona 分布在
16
+ * SOUL.md(根) + memories/{USER,MEMORY}.md, skills 是 skills/<分类>/<技能>/SKILL.md
17
+ * 两级嵌套 (235 个). 迁移时展平并以 <分类>-<技能> 命名避免重名冲突.
18
+ *
19
+ * 幂等: 每个源落一份 manifest (~/.bolloon/migration/<source>.json),
20
+ * 记录已迁移的文件 hash; 未变化则跳过, 已存在则覆盖源文档 (文档允许演进),
21
+ * 技能只复制新增/变化的 (不删除目标已存在但源已删的, 保守).
22
+ *
23
+ * 安全: 不读取/复制 API key 类文件 (credentials/identity/*auth*), 不碰 secret.
24
+ *
25
+ * 设计范式: 与 external-engines/discovery.ts 一致 — 纯函数 + 可注入 deps, 便于单测.
26
+ */
27
+ import * as fs from 'fs/promises';
28
+ import * as os from 'os';
29
+ import * as path from 'path';
30
+ import * as crypto from 'crypto';
31
+ import { sanitizeAgentId } from '../bootstrap/persona-loader.js';
32
+ // ============================================================
33
+ // 默认 deps (真实 IO)
34
+ // ============================================================
35
+ function realReadFile(p) {
36
+ return fs.readFile(p, 'utf-8').then((c) => c).catch(() => undefined);
37
+ }
38
+ function realReaddir(dir) {
39
+ return fs.readdir(dir).then((c) => c).catch(() => undefined);
40
+ }
41
+ function realStat(p) {
42
+ return fs.stat(p).then((s) => ({ isDirectory: s.isDirectory(), isFile: s.isFile(), mtimeMs: s.mtimeMs })).catch(() => undefined);
43
+ }
44
+ function realExists(p) {
45
+ return fs.access(p).then(() => true).catch(() => false);
46
+ }
47
+ export function defaultDeps() {
48
+ const home = os.homedir();
49
+ return {
50
+ home,
51
+ platform: os.platform(),
52
+ localAppData: (typeof process !== 'undefined' && process.env && (process.env.LOCALAPPDATA || process.env.ProgramData))
53
+ ? (process.env.LOCALAPPDATA || process.env.ProgramData)
54
+ : path.join(home, 'AppData', 'Local'),
55
+ readFile: realReadFile,
56
+ readdir: realReaddir,
57
+ stat: realStat,
58
+ mkdir: async (p) => { await fs.mkdir(p, { recursive: true }); },
59
+ copyFile: (f, t) => fs.copyFile(f, t),
60
+ writeFile: (p, d) => fs.writeFile(p, d, 'utf-8'),
61
+ exists: realExists,
62
+ };
63
+ }
64
+ // ============================================================
65
+ // 纯函数: 目录布局 + hash
66
+ // ============================================================
67
+ /** 各源默认根目录: openclaw 在 home/.openclaw; hermes 兜底 home/.hermes (真实见 candidates) */
68
+ export function sourceRootPath(source, home) {
69
+ return source === 'openclaw'
70
+ ? path.join(home, '.openclaw')
71
+ : path.join(home, '.hermes');
72
+ }
73
+ /**
74
+ * 各源全部候选根路径 (按优先级, 遍历时取第一个存在者).
75
+ *
76
+ * 覆盖三大平台的实际安装位置:
77
+ * OpenClaw: 主目录 ~/.openclaw (三平台一致), 兜底 ~/.config/openclaw
78
+ * Hermes :
79
+ * - win32 %LOCALAPPDATA%\hermes → ~/.hermes
80
+ * - darwin ~/Library/Application Support/hermes → ~/.hermes
81
+ * - linux ~/.local/share/hermes → ~/.config/hermes → ~/.hermes
82
+ */
83
+ export function sourceRootCandidates(source, deps) {
84
+ const home = deps.home;
85
+ const platform = deps.platform || 'linux';
86
+ if (source === 'openclaw') {
87
+ return [
88
+ path.join(home, '.openclaw'),
89
+ path.join(home, '.config', 'openclaw'),
90
+ ];
91
+ }
92
+ const candidates = [];
93
+ if (platform === 'win32') {
94
+ if (deps.localAppData)
95
+ candidates.push(path.join(deps.localAppData, 'hermes'));
96
+ }
97
+ else if (platform === 'darwin') {
98
+ candidates.push(path.join(home, 'Library', 'Application Support', 'hermes'));
99
+ }
100
+ else {
101
+ candidates.push(path.join(home, '.local', 'share', 'hermes'));
102
+ candidates.push(path.join(home, '.config', 'hermes'));
103
+ }
104
+ candidates.push(path.join(home, '.hermes'));
105
+ return candidates;
106
+ }
107
+ /** workspace 路径 (openclaw 用 workspace/, hermes 平铺在根) */
108
+ export function workspacePath(source, sourceRoot) {
109
+ return source === 'openclaw'
110
+ ? path.join(sourceRoot, 'workspace')
111
+ : sourceRoot;
112
+ }
113
+ /** 计算文件内容 hash (幂等用) */
114
+ export function sha1(content) {
115
+ return crypto.createHash('sha1').update(content).digest('hex');
116
+ }
117
+ /**
118
+ * 内容级脱敏: 抹掉明敏凭据, 防止迁移产物把真实的 Bearer token / API key /
119
+ * MT5 会话标识 / 长随机串 带进 Bolloon 落盘. 迁移只搬运"知识", 不搬运"秘密".
120
+ *
121
+ * 处理模式:
122
+ * - Authorization / Bearer <token>
123
+ * - token: / api_key: / access_token: / secret: 等 key 声明后的随机串
124
+ * - 形如 sk-... / ghp_... / AKIA... 的 platform token
125
+ * - 一行冒号后跟 20+ 位 base64/hex 随机串 ("MT5 data: D0E8...", "token: Ab...")
126
+ *
127
+ * 保留中文句子与结构说明, 只替换被判定为凭据的 token 片段.
128
+ */
129
+ export function redactSecrets(text) {
130
+ const REDACTED = '***REDACTED***';
131
+ let out = text;
132
+ // 0. Bearer <JWT/base64> / Authorization: Bearer ... — 最优先, 连同包头一起抹
133
+ out = out.replace(/\bBearer\s+[A-Za-z0-9_./\-=+]{12,}/gi, REDACTED);
134
+ out = out.replace(/\bAuthorization\s*[:=]\s*(?:Bearer\s+)?[A-Za-z0-9_./\-=+]{12,}/gi, REDACTED);
135
+ // 1. explicit key=value 声明 (token/apiKey/access_token/key/secret/password)
136
+ out = out.replace(/\b(token|api[_-]?key|access[_-]?token|secret|password)\b\s*[:=]\s*["']?[A-Za-z0-9_./+\-]{8,}["']?/gi, (_m, k) => `${k}: ${REDACTED}`);
137
+ // 2. platform 前缀 token (sk- / sk-proj- / sk-ant- / ghp_ / AKIA / xoxb-)
138
+ out = out.replace(/\b(sk-[A-Za-z0-9_]{8,}|sk-proj-[A-Za-z0-9_-]{8,}|sk-ant-[A-Za-z0-9_-]{8,}|ghp_[A-Za-z0-9]{20,}|AKIA[0-9A-Z]{16}|xox[bp]-[A-Za-z0-9-]{8,})\b/g, REDACTED);
139
+ // 3. "标签: <20+位随机串>" 结构 — 冒号后跟长的 alnum token (MT5 data: D0E8...).
140
+ // 保守: 只匹配不含 `/`(避开 URL/路径) 且不含域名点 的纯 token 骨架.
141
+ out = out.replace(/([A-Za-z][A-Za-z0-9 _-]{0,20})\s*[::]\s*([A-Za-z0-9_+\-]{20,})\b(?![A-Za-z0-9])/g, (_m, name) => `${name}: ${REDACTED}`);
142
+ // 4. 宽松: 独立长串 24+ (base64) — 前后为空白/标点/行首行尾, 避开 URL 与中文段.
143
+ // 保守: 不含 `/` 与 `.`, 防误伤 URL/域名/路径.
144
+ out = out.replace(/(^|[\s(([::\])])[A-Za-z0-9_+=]{22,}(?=[\s))\].,,。;;::§]|$)/gm, (_m, prefix) => `${prefix}${REDACTED}`);
145
+ return out;
146
+ }
147
+ /** bolloon 目标根 (默认 ~/.bolloon, 可注入 override) */
148
+ function bolloonRoot(home) {
149
+ return path.join(home, '.bolloon');
150
+ }
151
+ // ============================================================
152
+ // 单文件迁移 helper
153
+ // ============================================================
154
+ async function copyIfNeeded(deps, from, to, manifest, redact = false) {
155
+ const content = await deps.readFile(from);
156
+ if (content === undefined)
157
+ return false;
158
+ const contentOut = redact ? redactSecrets(content) : content;
159
+ const hash = sha1(contentOut);
160
+ if (manifest.get(to) === hash)
161
+ return false; // 未变化, 跳过
162
+ await deps.mkdir(path.dirname(to));
163
+ await deps.writeFile(to, contentOut);
164
+ manifest.set(to, hash);
165
+ return true;
166
+ }
167
+ async function copyDirIfNeeded(deps, srcDir, destDir, manifest) {
168
+ const copied = [];
169
+ const entries = await deps.readdir(srcDir);
170
+ if (!entries)
171
+ return copied;
172
+ for (const name of entries) {
173
+ const s = path.join(srcDir, name);
174
+ const st = await deps.stat(s);
175
+ if (!st)
176
+ continue;
177
+ if (st.isDirectory) {
178
+ copied.push(...await copyDirIfNeeded(deps, s, path.join(destDir, name), manifest));
179
+ }
180
+ else if (st.isFile) {
181
+ const to = path.join(destDir, name);
182
+ if (await copyIfNeeded(deps, s, to, manifest))
183
+ copied.push(name);
184
+ }
185
+ }
186
+ return copied;
187
+ }
188
+ // ============================================================
189
+ // 主迁移
190
+ // ============================================================
191
+ /** 探测某源是否安装: 返回选中的根目录 or null. */
192
+ export async function detectSource(deps, source) {
193
+ for (const root of sourceRootCandidates(source, deps)) {
194
+ const st = await deps.stat(root);
195
+ if (st?.isDirectory)
196
+ return root;
197
+ }
198
+ return null;
199
+ }
200
+ const OPENCLAW_PERSONA = [
201
+ { src: 'SOUL.md', toName: 'soul.md' },
202
+ { src: 'IDENTITY.md', toName: 'identity.md' },
203
+ { src: 'USER.md', toName: 'user.md' },
204
+ { src: 'AGENTS.md', toName: 'agent.md' },
205
+ { src: 'TOOLS.md', toName: 'project.md' },
206
+ { src: 'MEMORY.md', toName: 'wiki.md' },
207
+ ];
208
+ const HERMES_PERSONA = [
209
+ { src: 'SOUL.md', toName: 'soul.md' },
210
+ { src: 'memories/USER.md', toName: 'user.md' },
211
+ { src: 'memories/MEMORY.md', toName: 'wiki.md' },
212
+ ];
213
+ /**
214
+ * 迁移单个源的全部数据到 Bolloon.
215
+ * 返回 report; 源不存在 → migrated=false 且不抛错 (静默).
216
+ */
217
+ export async function migrateExternalAgent(source, deps = defaultDeps()) {
218
+ const report = {
219
+ source,
220
+ sourceRoot: sourceRootPath(source, deps.home),
221
+ migratedAt: new Date().toISOString(),
222
+ migrated: false,
223
+ personaAgentId: '',
224
+ persona: [],
225
+ skillsCopied: [],
226
+ memoryCopied: [],
227
+ docsCopied: [],
228
+ entries: [],
229
+ errors: [],
230
+ };
231
+ try {
232
+ const root = await detectSource(deps, source);
233
+ if (!root) {
234
+ report.migrated = false;
235
+ return report; // 未安装, 静默
236
+ }
237
+ report.sourceRoot = root;
238
+ const ws = workspacePath(source, root);
239
+ const wsStat = await deps.stat(ws);
240
+ if (!wsStat?.isDirectory) {
241
+ report.migrated = false;
242
+ report.errors.push(`workspace 目录不存在: ${ws}`);
243
+ return report;
244
+ }
245
+ // agentId: 用源名 + 目录名 (openclaw main agent) 兜底
246
+ const rawAgent = source === 'openclaw' ? 'main' : 'hermes-main';
247
+ const agentId = sanitizeAgentId(`ext-${source}-${rawAgent}`);
248
+ report.personaAgentId = agentId;
249
+ const bRoot = bolloonRoot(deps.home);
250
+ const personaDir = path.join(bRoot, 'persona', agentId);
251
+ const skillsRoot = path.join(bRoot, 'skills');
252
+ const memoryRoot = path.join(bRoot, 'memory', agentId, 'sessions');
253
+ const docsRoot = path.join(bRoot, 'context-os', '04-Projects', `${source}-docs`);
254
+ // manifest (幂等)
255
+ const manifestFile = path.join(bRoot, 'migration', `${source}.json`);
256
+ const manifest = new Map();
257
+ const prev = await deps.readFile(manifestFile);
258
+ if (prev) {
259
+ try {
260
+ const obj = JSON.parse(prev);
261
+ if (obj && typeof obj === 'object') {
262
+ for (const [k, v] of Object.entries(obj))
263
+ manifest.set(k, String(v));
264
+ }
265
+ }
266
+ catch { /* 损坏忽略, 从头迁 */ }
267
+ }
268
+ await deps.mkdir(path.join(bRoot, 'migration'));
269
+ // 1. persona (per-source spec) — 含敏感 token, 内容级脱敏后写入
270
+ const personaSpec = source === 'openclaw' ? OPENCLAW_PERSONA : HERMES_PERSONA;
271
+ for (const { src, toName } of personaSpec) {
272
+ const from = path.join(ws, src);
273
+ const to = path.join(personaDir, toName);
274
+ if (await copyIfNeeded(deps, from, to, manifest, true)) {
275
+ report.persona.push(toName);
276
+ report.entries.push({ from, to, kind: 'persona' });
277
+ }
278
+ }
279
+ // 2. skills → ~/.bolloon/skills/<name>/
280
+ const skillsSrc = path.join(ws, 'skills');
281
+ const skillDirs = await deps.readdir(skillsSrc);
282
+ if (skillDirs) {
283
+ // OpenClaw: skills/<name>/SKILL.md 一层, 直接落盘 <name>.
284
+ // Hermes: skills/<分类>/<技能>/SKILL.md 两层, 逐 <技能> 递归找 SKILL.md,
285
+ // 落盘 <分类>-<技能> 展平, 避免跨分类重名.
286
+ for (const entryName of skillDirs) {
287
+ if (entryName.startsWith('.'))
288
+ continue;
289
+ const entry = path.join(skillsSrc, entryName);
290
+ const st = await deps.stat(entry);
291
+ if (!st?.isDirectory)
292
+ continue;
293
+ if (source === 'hermes') {
294
+ // 分类下的每个技能目录 → 目标 <分类>-<技能>
295
+ const cat = entryName;
296
+ const subDirs = await deps.readdir(entry);
297
+ if (!subDirs)
298
+ continue;
299
+ for (const skillName of subDirs) {
300
+ if (skillName.startsWith('.'))
301
+ continue;
302
+ const skillDir = path.join(entry, skillName);
303
+ const sst = await deps.stat(skillDir);
304
+ if (!sst?.isDirectory)
305
+ continue;
306
+ // 该分类下菊 不一定有 SKILL.md → 跳过
307
+ const hasSkill = await deps.exists(path.join(skillDir, 'SKILL.md'));
308
+ if (!hasSkill)
309
+ continue;
310
+ const targetName = `${cat}-${skillName}`;
311
+ const destDir = path.join(skillsRoot, targetName);
312
+ const copied = await copyDirIfNeeded(deps, skillDir, destDir, manifest);
313
+ if (copied.length > 0) {
314
+ report.skillsCopied.push(targetName);
315
+ report.entries.push({ from: skillDir, to: destDir, kind: 'skill' });
316
+ }
317
+ }
318
+ }
319
+ else {
320
+ const destDir = path.join(skillsRoot, entryName);
321
+ const copied = await copyDirIfNeeded(deps, entry, destDir, manifest);
322
+ if (copied.length > 0) {
323
+ report.skillsCopied.push(entryName);
324
+ report.entries.push({ from: entry, to: destDir, kind: 'skill' });
325
+ }
326
+ }
327
+ }
328
+ }
329
+ // 3. memory: openclaw workspace/memory/*.md → sessions/; hermes 无独立 memory 目录
330
+ const memSrc = path.join(ws, 'memory');
331
+ const memFiles = await deps.readdir(memSrc);
332
+ if (memFiles) {
333
+ let idx = 0;
334
+ for (const f of memFiles) {
335
+ if (!f.endsWith('.md'))
336
+ continue;
337
+ const from = path.join(memSrc, f);
338
+ const to = path.join(memoryRoot, `${idx + 1}-${f}`);
339
+ if (await copyIfNeeded(deps, from, to, manifest, true)) {
340
+ report.memoryCopied.push(f);
341
+ report.entries.push({ from, to, kind: 'memory' });
342
+ }
343
+ idx++;
344
+ }
345
+ }
346
+ // 4. docs: workspace 根其他 .md → context-os/04-Projects/<source>-docs/
347
+ const wsFiles = await deps.readdir(ws);
348
+ if (wsFiles) {
349
+ const excluded = personaSpec.map((p) => p.toName);
350
+ for (const f of wsFiles) {
351
+ if (!f.endsWith('.md'))
352
+ continue;
353
+ if (excluded.includes(f))
354
+ continue;
355
+ const from = path.join(ws, f);
356
+ const st = await deps.stat(from);
357
+ if (!st?.isFile)
358
+ continue;
359
+ const to = path.join(docsRoot, f);
360
+ if (await copyIfNeeded(deps, from, to, manifest)) {
361
+ report.docsCopied.push(f);
362
+ report.entries.push({ from, to, kind: 'docs' });
363
+ }
364
+ }
365
+ }
366
+ // 写回 manifest
367
+ await deps.writeFile(manifestFile, JSON.stringify(Object.fromEntries(manifest), null, 2));
368
+ report.migrated = true;
369
+ return report;
370
+ }
371
+ catch (err) {
372
+ report.errors.push(err.message?.slice(0, 300) || String(err));
373
+ return report;
374
+ }
375
+ }
376
+ /** 一次性迁移所有已安装源 (openclaw + hermes). 隐式, 静默. */
377
+ export async function migrateAllExternalAgents(deps = defaultDeps()) {
378
+ const sources = ['openclaw', 'hermes'];
379
+ const out = [];
380
+ for (const s of sources) {
381
+ const r = await migrateExternalAgent(s, deps);
382
+ if (r.migrated)
383
+ out.push(r);
384
+ }
385
+ return out;
386
+ }
387
+ /** 把迁移结果格式化成给用户看的中文通告 (纯函数, 可单测) */
388
+ export function formatMigrationNotices(reports) {
389
+ const lines = [];
390
+ for (const r of reports) {
391
+ if (!r.migrated)
392
+ continue;
393
+ const parts = [];
394
+ if (r.persona.length)
395
+ parts.push(`性格 ${r.persona.length} 份`);
396
+ if (r.skillsCopied.length)
397
+ parts.push(`技能 ${r.skillsCopied.length} 个`);
398
+ if (r.memoryCopied.length)
399
+ parts.push(`记忆 ${r.memoryCopied.length} 条`);
400
+ if (r.docsCopied.length)
401
+ parts.push(`文档 ${r.docsCopied.length} 份`);
402
+ lines.push(`[迁移:${r.source}] 外部智能体数据已并入 Bolloon (agentId=${r.personaAgentId}): ${parts.join(', ') || '已就绪'}${r.errors.length ? ` (${r.errors.length} 个错误)` : ''}`);
403
+ }
404
+ return lines;
405
+ }