@bolloon/bolloon-agent 0.3.52 → 0.4.0

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.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 +21 -0
  8. package/dist/agents/pi-sdk-tools.js +86 -2
  9. package/dist/agents/pi-sdk-types.js +12 -12
  10. package/dist/agents/pi-sdk.js +90 -90
  11. package/dist/agents/shell-guard.js +22 -1
  12. package/dist/agents/skill-organizer.js +19 -19
  13. package/dist/agents/verify-recipe.js +84 -0
  14. package/dist/agents/workflow-pivot-loop.js +101 -13
  15. package/dist/bollharness/src/scripts/deploy-guard.js +8 -8
  16. package/dist/bollharness-integration/context-router-judgment.js +4 -4
  17. package/dist/bollharness-integration/context-router.js +292 -292
  18. package/dist/bollharness-integration/gate-state-machine.js +13 -13
  19. package/dist/bollharness-integration/llm-judgment-engine.js +134 -134
  20. package/dist/bollharness-integration/skill-adapter.js +21 -21
  21. package/dist/bootstrap/chat-archiver.js +14 -14
  22. package/dist/bootstrap/context-os.js +50 -40
  23. package/dist/bootstrap/memory-compressor.js +13 -13
  24. package/dist/cli-entry.js +36 -36
  25. package/dist/context-compaction/auto-compact.js +7 -7
  26. package/dist/electron/first-run.js +54 -54
  27. package/dist/electron-build/electron/first-run.js +54 -54
  28. package/dist/electron-preload.js.map +1 -1
  29. package/dist/electron.js.map +1 -1
  30. package/dist/external-engines/delegate-handle.js +70 -0
  31. package/dist/external-engines/delegate.js +24 -3
  32. package/dist/index.js +112 -112
  33. package/dist/llm/error-lessons.js +53 -0
  34. package/dist/llm/llm-judgment-client.js +102 -102
  35. package/dist/llm/pi-ai.js +61 -48
  36. package/dist/llm/system-prompt/layers/channel/human-async.md +41 -41
  37. package/dist/llm/system-prompt/layers/channel/local.md +14 -14
  38. package/dist/llm/system-prompt/layers/channel/p2p-agent.md +18 -18
  39. package/dist/llm/system-prompt/layers/channel/p2p-peer-sync.md +51 -51
  40. package/dist/llm/system-prompt/layers/channel/p2p-proactive.md +43 -43
  41. package/dist/llm/system-prompt/layers/channel/p2p-visitor.md +19 -19
  42. package/dist/llm/system-prompt/layers/channel/session-handoff.md +61 -61
  43. package/dist/llm/system-prompt/layers/core/artifacts_storage.md +89 -89
  44. package/dist/llm/system-prompt/layers/core/evenhandedness.md +21 -21
  45. package/dist/llm/system-prompt/layers/core/external-engagement.md +73 -73
  46. package/dist/llm/system-prompt/layers/core/hibs_reminders.md +15 -15
  47. package/dist/llm/system-prompt/layers/core/identity.md +48 -48
  48. package/dist/llm/system-prompt/layers/core/knowledge.md +17 -17
  49. package/dist/llm/system-prompt/layers/core/memory_system.md +12 -12
  50. package/dist/llm/system-prompt/layers/core/network_filesystem.md +28 -28
  51. package/dist/llm/system-prompt/layers/core/refusal.md +37 -37
  52. package/dist/llm/system-prompt/layers/core/tone.md +31 -31
  53. package/dist/llm/system-prompt/layers/core/tools.thin.md +13 -13
  54. package/dist/llm/system-prompt/layers/core/wellbeing.md +41 -41
  55. package/dist/llm/system-prompt/layers/role/architect.md +20 -20
  56. package/dist/llm/system-prompt/layers/role/expert.md +19 -19
  57. package/dist/llm/system-prompt/layers/role/implementer.md +15 -15
  58. package/dist/llm/system-prompt/layers/role/security.md +15 -15
  59. package/dist/llm/system-prompt/layers/tool/artifacts.md +72 -72
  60. package/dist/llm/system-prompt/layers/tool/bash.md +25 -25
  61. package/dist/llm/system-prompt/layers/tool/goal_handoff.md +77 -77
  62. package/dist/llm/system-prompt/layers/tool/hibs_api.md +171 -171
  63. package/dist/llm/system-prompt/layers/tool/image_search.md +70 -70
  64. package/dist/llm/system-prompt/layers/tool/manifest.md +89 -89
  65. package/dist/llm/system-prompt/layers/tool/mcp_apps.md +53 -53
  66. package/dist/llm/system-prompt/layers/tool/p2p_request.md +61 -61
  67. package/dist/llm/system-prompt/layers/tool/web_search.md +83 -83
  68. package/dist/llm/system-prompt/registry.js +9 -1
  69. package/dist/llm/tool-manifest/ask_user_input.js +1 -1
  70. package/dist/llm/tool-manifest/bash.js +3 -3
  71. package/dist/llm/tool-manifest/create_file.js +4 -4
  72. package/dist/llm/tool-manifest/fetch_sports_data.js +3 -3
  73. package/dist/llm/tool-manifest/image_search.js +2 -2
  74. package/dist/llm/tool-manifest/index.js +0 -59
  75. package/dist/llm/tool-manifest/mcp.js +2 -2
  76. package/dist/llm/tool-manifest/message_compose.js +3 -3
  77. package/dist/llm/tool-manifest/places.js +2 -2
  78. package/dist/llm/tool-manifest/present_files.js +1 -1
  79. package/dist/llm/tool-manifest/recipe.js +2 -2
  80. package/dist/llm/tool-manifest/str_replace.js +5 -5
  81. package/dist/llm/tool-manifest/view.js +3 -3
  82. package/dist/llm/tool-manifest/weather.js +4 -4
  83. package/dist/llm/tool-manifest/web.js +4 -4
  84. package/dist/locales/en.js +28 -0
  85. package/dist/locales/zh.js +26 -0
  86. package/dist/pi-ecosystem-colony/index.js +365 -0
  87. package/dist/pi-ecosystem-judgment/causal-judge.js +51 -51
  88. package/dist/pi-ecosystem-judgment/distill-prompt.js +42 -42
  89. package/dist/pi-ecosystem-judgment/distillation.js +14 -14
  90. package/dist/pi-ecosystem-judgment/monitor-gate.js +28 -28
  91. package/dist/pi-ecosystem-judgment/value-injection.js +102 -102
  92. package/dist/security/context-router-tool.js +15 -15
  93. package/dist/social/ant-colony/AdaptiveHeartbeat.js +101 -0
  94. package/dist/social/ant-colony/PheromoneEngine.js +227 -0
  95. package/dist/social/ant-colony/index.js +6 -0
  96. package/dist/social/ant-colony/types.js +24 -0
  97. package/dist/storage/trajectory.js +101 -0
  98. package/dist/test/ai-judgment-test.js +80 -0
  99. package/dist/test/bollharness-integration.test.js +318 -0
  100. package/dist/test/channel-agent-multi-dialogue.js +205 -0
  101. package/dist/test/channel-heartbeat-agent-test.js +201 -0
  102. package/dist/test/constraint-layer.test.js +164 -0
  103. package/dist/test/diap-identity-test.js +172 -0
  104. package/dist/test/diap-quick-test.js +62 -0
  105. package/dist/test/global-shared-context.test.js +315 -0
  106. package/dist/test/harness-judgment-injection.test.js +246 -0
  107. package/dist/test/harness-workflow-integrator-test.js +228 -0
  108. package/dist/test/human-value-store.test.js +243 -0
  109. package/dist/test/hybrid-integration-test.js +118 -0
  110. package/dist/test/hybrid-messenger-verify.js +55 -0
  111. package/dist/test/iroh-bistream-debug.js +38 -0
  112. package/dist/test/iroh-communication.test.js +66 -0
  113. package/dist/test/iroh-debug-test.js +57 -0
  114. package/dist/test/iroh-diap-test.js +71 -0
  115. package/dist/test/iroh-direct-connect.js +55 -0
  116. package/dist/test/iroh-e2e-fixed.js +89 -0
  117. package/dist/test/iroh-e2e-same-process.js +63 -0
  118. package/dist/test/iroh-e2e.js +66 -0
  119. package/dist/test/iroh-final-e2e.js +72 -0
  120. package/dist/test/iroh-relay-test.js +37 -0
  121. package/dist/test/iroh-simple-test.js +41 -0
  122. package/dist/test/iroh-transport-verify.js +54 -0
  123. package/dist/test/iroh-transport.test.js +37 -0
  124. package/dist/test/iroh-two-nodes.js +70 -0
  125. package/dist/test/iroh-verify.js +44 -0
  126. package/dist/test/judgment-decision.test.js +219 -0
  127. package/dist/test/llm-judgment-integration.test.js +220 -0
  128. package/dist/test/p2p-agent-complex-dialogue.js +385 -0
  129. package/dist/test/p2p-agent-dialogue.js +341 -0
  130. package/dist/test/p2p-agent-full-bidirectional.js +510 -0
  131. package/dist/test/p2p-agent-harness-flow.js +437 -0
  132. package/dist/test/p2p-agent-harness-single.js +143 -0
  133. package/dist/test/p2p-ai-dialogue-test.js +318 -0
  134. package/dist/test/p2p-cid-connect-test.js +195 -0
  135. package/dist/test/p2p-connect-receiver.js +69 -0
  136. package/dist/test/p2p-doc-transfer.js +110 -0
  137. package/dist/test/p2p-identity-page-test.js +77 -0
  138. package/dist/test/p2p-iroh-test.js +171 -0
  139. package/dist/test/p2p-minimal-test.js +241 -0
  140. package/dist/test/p2p-node-1.js +148 -0
  141. package/dist/test/p2p-node-2.js +148 -0
  142. package/dist/test/p2p-server.js +281 -0
  143. package/dist/test/p2p-two-nodes-test.js +438 -0
  144. package/dist/test/pi-sdk.test.js +44 -0
  145. package/dist/test/set-persona.js +40 -0
  146. package/dist/test/simple.test.js +9 -0
  147. package/dist/test/storage-integration.test.js +150 -0
  148. package/dist/test/subagent-manager.test.js +276 -0
  149. package/dist/test/test-gate-flow.test.js +81 -0
  150. package/dist/test/workflow-engine.test.js +87 -0
  151. package/dist/test/workflow-pivot-loop.test.js +246 -0
  152. package/dist/web/api-config.html +779 -779
  153. package/dist/web/client-hearth.js +10 -10
  154. package/dist/web/components/wallet-viem.mjs +118 -118
  155. package/dist/web/delivery-ledger.js +103 -0
  156. package/dist/web/i18n.js +104 -0
  157. package/dist/web/index.html +472 -472
  158. package/dist/web/manifest.json +20 -20
  159. package/dist/web/routes-tasks.js +242 -24
  160. package/dist/web/server-storage.js +111 -0
  161. package/dist/web/server.js +55 -26
  162. package/dist/web/style.css +4946 -4946
  163. package/dist/web/task-breaker.js +18 -0
  164. package/dist/web/task-cancel.js +35 -0
  165. package/dist/web/task-deps.js +36 -0
  166. package/dist/web/task-review.js +27 -0
  167. package/dist/web/task-verify.js +60 -0
  168. package/package.json +196 -196
  169. package/scripts/build-cli.js +215 -215
  170. package/scripts/build-web.ts +130 -130
  171. package/scripts/postinstall.js +152 -152
  172. package/bin/bolloon-daemon.sh +0 -207
  173. package/dist/constraint-runtime/reference_data/archive_surface_snapshot.json +0 -63
  174. package/dist/constraint-runtime/reference_data/commands_snapshot.json +0 -1037
  175. package/dist/constraint-runtime/reference_data/subsystems/assistant.json +0 -8
  176. package/dist/constraint-runtime/reference_data/subsystems/bootstrap.json +0 -8
  177. package/dist/constraint-runtime/reference_data/subsystems/bridge.json +0 -32
  178. package/dist/constraint-runtime/reference_data/subsystems/buddy.json +0 -13
  179. package/dist/constraint-runtime/reference_data/subsystems/cli.json +0 -26
  180. package/dist/constraint-runtime/reference_data/subsystems/components.json +0 -32
  181. package/dist/constraint-runtime/reference_data/subsystems/constants.json +0 -28
  182. package/dist/constraint-runtime/reference_data/subsystems/coordinator.json +0 -8
  183. package/dist/constraint-runtime/reference_data/subsystems/entrypoints.json +0 -15
  184. package/dist/constraint-runtime/reference_data/subsystems/hooks.json +0 -32
  185. package/dist/constraint-runtime/reference_data/subsystems/keybindings.json +0 -21
  186. package/dist/constraint-runtime/reference_data/subsystems/memdir.json +0 -15
  187. package/dist/constraint-runtime/reference_data/subsystems/migrations.json +0 -18
  188. package/dist/constraint-runtime/reference_data/subsystems/moreright.json +0 -8
  189. package/dist/constraint-runtime/reference_data/subsystems/native_ts.json +0 -11
  190. package/dist/constraint-runtime/reference_data/subsystems/outputStyles.json +0 -8
  191. package/dist/constraint-runtime/reference_data/subsystems/plugins.json +0 -9
  192. package/dist/constraint-runtime/reference_data/subsystems/remote.json +0 -11
  193. package/dist/constraint-runtime/reference_data/subsystems/schemas.json +0 -8
  194. package/dist/constraint-runtime/reference_data/subsystems/screens.json +0 -10
  195. package/dist/constraint-runtime/reference_data/subsystems/server.json +0 -10
  196. package/dist/constraint-runtime/reference_data/subsystems/services.json +0 -32
  197. package/dist/constraint-runtime/reference_data/subsystems/skills.json +0 -27
  198. package/dist/constraint-runtime/reference_data/subsystems/state.json +0 -13
  199. package/dist/constraint-runtime/reference_data/subsystems/types.json +0 -18
  200. package/dist/constraint-runtime/reference_data/subsystems/upstreamproxy.json +0 -9
  201. package/dist/constraint-runtime/reference_data/subsystems/utils.json +0 -32
  202. package/dist/constraint-runtime/reference_data/subsystems/vim.json +0 -12
  203. package/dist/constraint-runtime/reference_data/subsystems/voice.json +0 -8
  204. package/dist/constraint-runtime/reference_data/tools_snapshot.json +0 -922
  205. package/dist/electron/config.js.map +0 -1
  206. package/dist/electron/dialogs.js.map +0 -1
  207. package/dist/electron/first-run.js.map +0 -1
  208. package/dist/electron/ipc.js.map +0 -1
  209. package/dist/electron/logger.js.map +0 -1
  210. package/dist/electron/main.js.map +0 -1
  211. package/dist/electron/menu.js.map +0 -1
  212. package/dist/electron/paths.js.map +0 -1
  213. package/dist/electron/server.js.map +0 -1
  214. package/dist/electron/tray.js.map +0 -1
  215. package/dist/electron/window.js.map +0 -1
  216. package/dist/utils/auto-update.js.map +0 -1
  217. package/dist/web/client.js.map +0 -7
package/bin/bolloon.js ADDED
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env node
2
+ const path = require("path");
3
+ const { spawn } = require("child_process");
4
+ const fs = require("fs");
5
+
6
+ const RESET = "\x1b[0m";
7
+ const BOLD = "\x1b[1m";
8
+ const CYAN = "\x1b[36m";
9
+ const GREEN = "\x1b[32m";
10
+ const MAGENTA = "\x1b[35m";
11
+
12
+ function log(msg, color) {
13
+ console.log((color || RESET) + msg + RESET);
14
+ }
15
+
16
+ function printBanner() {
17
+ console.log("\n" + CYAN + BOLD + [
18
+ " ╔═══════════════════════════════════════════╗",
19
+ " ║ 🤖 Bolloon Agent ║",
20
+ " ║ P2P AI Document Processor ║",
21
+ " ╚═══════════════════════════════════════════╝"
22
+ ].join("\n") + RESET + "\n");
23
+ }
24
+
25
+ function getMainEntry() {
26
+ const distDir = path.dirname(require.main.filename);
27
+ const distIndex = path.join(distDir, "index.js");
28
+ if (fs.existsSync(distIndex)) {
29
+ return distIndex;
30
+ }
31
+ return path.join(process.cwd(), "src", "index.ts");
32
+ }
33
+
34
+ function parseArgs() {
35
+ const args = process.argv.slice(2);
36
+ if (args.length === 0) {
37
+ return { mode: "gui", args: [] };
38
+ }
39
+ const first = args[0];
40
+ switch (first) {
41
+ case "-v":
42
+ case "--version":
43
+ return { mode: "version", args: [] };
44
+ case "-h":
45
+ case "--help":
46
+ return { mode: "help", args: [] };
47
+ case "-g":
48
+ case "--gui":
49
+ return { mode: "gui", args: args.slice(1) };
50
+ case "-w":
51
+ case "--web":
52
+ return { mode: "web", args: args.slice(1) };
53
+ case "-c":
54
+ case "--cli":
55
+ return { mode: "cli", args: args.slice(1) };
56
+ default:
57
+ return { mode: "passthrough", args };
58
+ }
59
+ }
60
+
61
+ async function startElectron(additionalArgs) {
62
+ try {
63
+ const electron = require("electron");
64
+ const distDir = path.dirname(require.main.filename);
65
+ let mainPath = path.join(distDir, "electron.js");
66
+ if (!fs.existsSync(mainPath)) {
67
+ mainPath = path.join(process.cwd(), "src", "electron.ts");
68
+ }
69
+ log("启动 Electron...", CYAN);
70
+ const child = spawn(electron, [mainPath, ...additionalArgs], {
71
+ stdio: "inherit",
72
+ env: { ...process.env, NODE_ENV: "development" }
73
+ });
74
+ child.on("error", (err) => {
75
+ log("Electron 启动失败: " + err.message, MAGENTA);
76
+ process.exit(1);
77
+ });
78
+ child.on("exit", (code) => process.exit(code || 0));
79
+ } catch (err) {
80
+ log("Electron 不可用,切换到 Web 模式...", CYAN);
81
+ await startWebServer(additionalArgs);
82
+ }
83
+ }
84
+
85
+ async function startWebServer(additionalArgs) {
86
+ const mainPath = getMainEntry();
87
+ const webArgs = ["--web", ...additionalArgs];
88
+ log("启动 Web 服务...", CYAN);
89
+ const child = spawn(process.execPath, [mainPath, ...webArgs], { stdio: "inherit" });
90
+ child.on("error", (err) => {
91
+ log("Web 服务启动失败: " + err.message, MAGENTA);
92
+ process.exit(1);
93
+ });
94
+ child.on("exit", (code) => process.exit(code || 0));
95
+ }
96
+
97
+ async function startCLI(additionalArgs) {
98
+ const mainPath = getMainEntry();
99
+ log("启动命令行界面...", CYAN);
100
+ const child = spawn(process.execPath, [mainPath, ...additionalArgs], { stdio: "inherit" });
101
+ child.on("error", (err) => {
102
+ log("CLI 启动失败: " + err.message, MAGENTA);
103
+ process.exit(1);
104
+ });
105
+ child.on("exit", (code) => process.exit(code || 0));
106
+ }
107
+
108
+ async function main() {
109
+ const { mode, args } = parseArgs();
110
+ switch (mode) {
111
+ case "version":
112
+ console.log("Bolloon Agent v0.1.1");
113
+ break;
114
+ case "help":
115
+ printBanner();
116
+ console.log(BOLD + "用法:" + RESET + " bolloon [选项] [命令] [参数]");
117
+ console.log(BOLD + "选项:" + RESET + " --gui, -g 启动图形界面");
118
+ console.log(" --web, -w 启动 Web UI");
119
+ console.log(" --cli, -c 启动命令行界面");
120
+ console.log(" --version, -v 显示版本");
121
+ console.log(" --help, -h 显示帮助");
122
+ console.log(BOLD + "示例:" + RESET + " bolloon # 启动图形界面");
123
+ console.log(" bolloon --web # 启动 Web UI");
124
+ console.log(" bolloon --read file # 读取文档");
125
+ break;
126
+ case "gui":
127
+ printBanner();
128
+ await startElectron(args);
129
+ break;
130
+ case "web":
131
+ printBanner();
132
+ await startWebServer(args);
133
+ break;
134
+ case "cli":
135
+ await startCLI(args);
136
+ break;
137
+ case "passthrough":
138
+ const mainPath = getMainEntry();
139
+ const child = spawn(process.execPath, [mainPath, ...args], { stdio: "inherit" });
140
+ child.on("error", (err) => {
141
+ log("执行失败: " + err.message, MAGENTA);
142
+ process.exit(1);
143
+ });
144
+ child.on("exit", (code) => process.exit(code || 0));
145
+ break;
146
+ default:
147
+ log("未知模式: " + mode, MAGENTA);
148
+ printBanner();
149
+ console.log("输入 --help 查看帮助");
150
+ process.exit(1);
151
+ }
152
+ }
153
+
154
+ main().catch((err) => {
155
+ console.error("Fatal error:", err);
156
+ process.exit(1);
157
+ });
@@ -7,25 +7,25 @@ import { ToolPermissionContext, BudgetTracker } from '@bolloon/constraint-runtim
7
7
  * System prompt for constraint layer
8
8
  * Defines boundaries and autonomous permissions
9
9
  */
10
- export const SYSTEM_PROMPT = `
11
- 你是一个文档处理Agent,在以下规则下运行:
12
-
13
- 【边界规则】
14
- 1. 只处理:txt, md, pdf, docx 格式文档
15
- 2. 不修改原始文件,只输出改进版本
16
- 3. 发送前必须记录操作日志
17
-
18
- 【自主权限】
19
- - ✅ 自主决定:摘要详细程度、chunk分块策略
20
- - ✅ 自主决定:重试次数(最多3次)
21
- - ✅ 自主决定:是否需要补充信息
22
- - ❌ 必须确认:首次向新对等节点发送文档
23
- - ❌ 必须确认:删除操作
24
-
25
- 【敏感操作拦截】
26
- if (操作 === '发送文档' && 对等节点不在已知列表) {
27
- 拦截 → 记录 → 等待确认
28
- }
10
+ export const SYSTEM_PROMPT = `
11
+ 你是一个文档处理Agent,在以下规则下运行:
12
+
13
+ 【边界规则】
14
+ 1. 只处理:txt, md, pdf, docx 格式文档
15
+ 2. 不修改原始文件,只输出改进版本
16
+ 3. 发送前必须记录操作日志
17
+
18
+ 【自主权限】
19
+ - ✅ 自主决定:摘要详细程度、chunk分块策略
20
+ - ✅ 自主决定:重试次数(最多3次)
21
+ - ✅ 自主决定:是否需要补充信息
22
+ - ❌ 必须确认:首次向新对等节点发送文档
23
+ - ❌ 必须确认:删除操作
24
+
25
+ 【敏感操作拦截】
26
+ if (操作 === '发送文档' && 对等节点不在已知列表) {
27
+ 拦截 → 记录 → 等待确认
28
+ }
29
29
  `;
30
30
  /**
31
31
  * Actions that can be executed autonomously without confirmation
@@ -148,20 +148,20 @@ export async function distillDissent(ask) {
148
148
  .slice(-10)
149
149
  .flatMap((j) => j.reasons || [])
150
150
  .slice(0, 20);
151
- const prompt = `你是用户的判断力代理. 用户发起了一个决定: "${ask.decision}"
152
- ${ask.context ? `\n补充: ${ask.context}` : ''}
153
-
154
- [用户价值画像] ${valueHint}
155
- [相关历史判断 (${relevant.length} 条)] ${relevant.slice(0, 3).map((j) => j.decision || '').join(' | ').slice(0, 300)}
156
- [历史理由样本] ${recentReasons.join(' | ').slice(0, 400) || '(无)'}
157
-
158
- 请基于上述判断力, 生成正好 3 个"反方观点" (用户应该考虑但可能忽略的风险/盲点).
159
- 要求:
160
- - 每个反方 1-2 句, 总长 < 80 字
161
- - 必须是这个特定决定的"反对声音", 不是通用建议
162
- - 第 1 个: 历史/经验类风险
163
- - 第 2 个: 关系/信任类风险
164
- - 第 3 个: 隐藏代价/时间类风险
151
+ const prompt = `你是用户的判断力代理. 用户发起了一个决定: "${ask.decision}"
152
+ ${ask.context ? `\n补充: ${ask.context}` : ''}
153
+
154
+ [用户价值画像] ${valueHint}
155
+ [相关历史判断 (${relevant.length} 条)] ${relevant.slice(0, 3).map((j) => j.decision || '').join(' | ').slice(0, 300)}
156
+ [历史理由样本] ${recentReasons.join(' | ').slice(0, 400) || '(无)'}
157
+
158
+ 请基于上述判断力, 生成正好 3 个"反方观点" (用户应该考虑但可能忽略的风险/盲点).
159
+ 要求:
160
+ - 每个反方 1-2 句, 总长 < 80 字
161
+ - 必须是这个特定决定的"反对声音", 不是通用建议
162
+ - 第 1 个: 历史/经验类风险
163
+ - 第 2 个: 关系/信任类风险
164
+ - 第 3 个: 隐藏代价/时间类风险
165
165
  - 用 JSON 数组返回: ["反方1", "反方2", "反方3"]`;
166
166
  // LLM 调用 (OpenAI 兼容, 复用 p2p-chat-tools 的方式)
167
167
  let dissents = [];
@@ -61,6 +61,7 @@ export function buildReviewHint(state, maxReviews = DEFAULT_MAX_REVIEWS) {
61
61
  `\n用户需求: ${intent.slice(0, 300)}` +
62
62
  actionLines +
63
63
  `\n已完成工具: ${tools}` +
64
+ `\n[完成契约] 宣布 <final gen> 前必须展示具体证据 (命令输出 / 文件内容 / 测试结果), 只写结论不算完成.` +
64
65
  `\n请对照需求逐条自查: 用户需求是否每一项都真正完成了? 如果还有未完成的子目标或自然衔接的后续步骤 → 继续调用工具推进 (已完成动作不要重复, 直接基于已有结果做下一步); ` +
65
66
  `如果用户需求已全部满足 → 直接输出 <final gen> 结束.` +
66
67
  `\n[重要] 不要因为做了一部分就提前结束 — 结束前逐条对照「用户需求」确认每一项都完成.`);
@@ -69,3 +70,23 @@ export function buildReviewHint(state, maxReviews = DEFAULT_MAX_REVIEWS) {
69
70
  export function shouldReviewAgain(state, maxReviews = DEFAULT_MAX_REVIEWS) {
70
71
  return decideAfterReview(state, maxReviews).kind === 'continue-review';
71
72
  }
73
+ /** 判定连续 window 次完全相同的工具调用 (循环签名) */
74
+ export function detectRepeatingCalls(recent, window = 3) {
75
+ if (recent.length < window)
76
+ return { repeating: false };
77
+ const tail = recent.slice(-window);
78
+ const first = tail[0];
79
+ const same = tail.every((c) => c.name === first.name && c.argsHash === first.argsHash);
80
+ return same ? { repeating: true, name: first.name } : { repeating: false };
81
+ }
82
+ /** 工具调用参数稳定哈希 (排序键, 顺序无关) */
83
+ export function toolCallArgsHash(args) {
84
+ try {
85
+ const obj = (args ?? {});
86
+ const keys = Object.keys(obj).sort();
87
+ return JSON.stringify(keys.map((k) => [k, obj[k]]));
88
+ }
89
+ catch {
90
+ return String(args ?? {});
91
+ }
92
+ }
@@ -490,13 +490,25 @@ export function registerBuiltinTools(ctx) {
490
490
  return { success: false, error: 'prompt 必填' };
491
491
  const cwd = args.cwd ? String(args.cwd).trim() : undefined;
492
492
  const model = args.model ? String(args.model).trim() : undefined;
493
- const result = await delegateToEngine(engine, prompt, { cwd: cwd || undefined, ...(model ? { model } : {}) });
493
+ // 2026-08-11: 委派句柄 ownerDid 绑当前 agent DID (防跨 channel 使用),
494
+ // correlationId 幂等去重 (同一 agent 重复 correlation 只应出现一次)
495
+ const ownerDid = ctx.identity?.did || 'unknown';
496
+ const correlationId = `delegate:${Date.now()}:${Math.random().toString(36).slice(2, 10)}`;
497
+ const result = await delegateToEngine(engine, prompt, {
498
+ cwd: cwd || undefined,
499
+ ...(model ? { model } : {}),
500
+ ownerDid,
501
+ correlationId,
502
+ });
494
503
  if (!result.success) {
495
504
  return { success: false, error: result.error, output: result.output };
496
505
  }
506
+ const handleLine = result.handle
507
+ ? `\n[delegate handle] contractVersion=${result.handle.contractVersion} delegateId=${result.handle.delegateId} ownerDid=${result.handle.ownerDid} correlationId=${result.handle.correlationId ?? '-'} capability=${result.handle.capability.slice(0, 16)}...`
508
+ : '';
497
509
  return {
498
510
  success: true,
499
- output: `🤖 ${engine} 执行结果 (exitCode=${result.exitCode}):\n${result.output || '(无输出)'}`
511
+ output: `🤖 ${engine} 执行结果 (exitCode=${result.exitCode}):\n${result.output || '(无输出)'}${handleLine}`
500
512
  };
501
513
  }
502
514
  });
@@ -1930,6 +1942,78 @@ export function registerBuiltinTools(ctx) {
1930
1942
  }
1931
1943
  }
1932
1944
  });
1945
+ // ============================================================
1946
+ // verify_project (2026-08-11, Hermes agent/verify recipes 模式)
1947
+ // 从项目 package.json scripts 提取验证命令依序执行 (build→typecheck→test→check→lint)
1948
+ // 完成契约 (B7) 的证据工具: 宣布完成前跑一遍, 输出命令/exit code/耗时
1949
+ // ============================================================
1950
+ ctx.tools.set('verify_project', {
1951
+ name: 'verify_project',
1952
+ description: '运行项目验证配方 (Hermes verify recipes 模式): 从 package.json scripts 自动提取 build/typecheck/test/check/lint 依序执行, 返回每步命令+exit code+耗时+输出尾部. 修改代码后、宣布完成前调用 — 提供完成契约要求的证据. cwd 选项目根 (默认当前工作目录).',
1953
+ parameters: {
1954
+ cwd: '项目根目录 (可选, 默认当前 cwd)',
1955
+ },
1956
+ execute: async (args) => {
1957
+ try {
1958
+ const { readFileSync, readdirSync } = await import('fs');
1959
+ const { extractVerifyCommands, detectPackageManager, runVerifyCommands } = await import('./verify-recipe.js');
1960
+ const cwd = String(args.cwd || process.cwd()).trim() || process.cwd();
1961
+ let pkg = {};
1962
+ try {
1963
+ pkg = JSON.parse(readFileSync(`${cwd}/package.json`, 'utf-8'));
1964
+ }
1965
+ catch {
1966
+ return { success: false, error: `${cwd}/package.json 不存在或非法 — verify_project 只支持 npm/pnpm/yarn/bun 项目` };
1967
+ }
1968
+ const scripts = (pkg.scripts ?? {});
1969
+ const pm = detectPackageManager(readdirSync(cwd).map((f) => f.toLowerCase()));
1970
+ const commands = extractVerifyCommands(scripts, pm);
1971
+ if (commands.length === 0) {
1972
+ return { success: false, error: 'package.json scripts 里没有 build/typecheck/test/check/lint 任何一步 — 无可验证' };
1973
+ }
1974
+ const r = await runVerifyCommands({ cwd, commands });
1975
+ const lines = r.steps.map((s) => ` ${s.exitCode === 0 ? '✅' : '❌'} ${s.name} (${s.command}) → exit ${s.exitCode ?? 'ERR'} ${s.durationMs}ms${s.timedOut ? ' [超时]' : ''}`);
1976
+ const failedStep = r.steps.find((s) => s.exitCode !== 0 || s.timedOut);
1977
+ return {
1978
+ success: r.allPassed,
1979
+ output: `${r.allPassed ? '✅ 验证全部通过' : '❌ 验证失败'} (${r.steps.filter((s) => s.exitCode === 0).length}/${r.steps.length} 步):\n${lines.join('\n')}${failedStep ? `\n\n失败输出尾部:\n${failedStep.outputTail.slice(-1500)}` : ''}`,
1980
+ ...(r.allPassed ? {} : { error: `验证失败于 ${failedStep?.name} (exit ${failedStep?.exitCode})` }),
1981
+ };
1982
+ }
1983
+ catch (e) {
1984
+ return { success: false, error: `verify_project 失败: ${String(e).slice(0, 200)}` };
1985
+ }
1986
+ },
1987
+ });
1988
+ ctx.tools.set('mcp_list_catalog', {
1989
+ name: 'mcp_list_catalog',
1990
+ description: '列出 Bolloon 审核过的可选 MCP 目录 (Hermes optional-mcps 模式: 默认禁用, 显式安装才生效). 想看"有哪些外部工具可以接"时用这个; 已接入的看 mcp_list_tools.',
1991
+ parameters: {},
1992
+ execute: async () => {
1993
+ try {
1994
+ // fs 读 manifests/mcp-catalog.json — 构建后 dist/agents/../../manifests = 仓库根/manifests
1995
+ const { readFileSync } = await import('fs');
1996
+ const raw = readFileSync(new URL('../../manifests/mcp-catalog.json', import.meta.url), 'utf-8');
1997
+ const catalog = JSON.parse(raw);
1998
+ const entries = (catalog?.entries ?? []);
1999
+ if (entries.length === 0) {
2000
+ return { success: true, output: '📦 MCP 目录为空. 新增条目: manifests/mcp-catalog.json (实测可用才收录).' };
2001
+ }
2002
+ const lines = entries.map((e) => {
2003
+ const t = e.transport?.type ?? 'unknown';
2004
+ const addr = e.transport?.url ?? e.transport?.command ?? '';
2005
+ return ` - ${e.name} [${t}] ${addr ? '(' + addr + ')' : ''}: ${e.description?.slice(0, 70) || ''}`;
2006
+ });
2007
+ return {
2008
+ success: true,
2009
+ output: `📦 可选 MCP 目录 (${entries.length} 条, 默认禁用):\n${lines.join('\n')}\n\n安装: 把条目按 mcpServers 格式写进 ~/.mcp.json, 重启后 mcp_list_tools 可见. 版本必须精确 pin (Hermes 纪律).`,
2010
+ };
2011
+ }
2012
+ catch (e) {
2013
+ return { success: false, error: `mcp_list_catalog 失败: ${String(e).slice(0, 200)}` };
2014
+ }
2015
+ },
2016
+ });
1933
2017
  ctx.tools.set('mcp_tool', {
1934
2018
  name: 'mcp_tool',
1935
2019
  description: '调用外部 MCP 工具 (真实 stdio JSON-RPC 通信). tool = 工具名 (先用 mcp_list_tools 查看), arguments = 参数 JSON 对象.',
@@ -1,13 +1,13 @@
1
- export const TOOL_DEFINITIONS = `
2
- 可用工具:
3
- 1. read_document(path) - 读取文档内容,支持 .txt, .md, .pdf, .docx
4
- 2. summarize_document(path, context?) - 总结文档内容,可选提供上下文
5
- 3. improve_document(path, requirements) - 改进文档,需提供文件路径和改进要求
6
- 4. list_peers() - 列出已连接的对等节点
7
- 5. send_message(peer_id, message) - 向指定对等节点发送消息
8
- 6. broadcast_message(message) - 向所有对等节点广播消息
9
- 7. get_identity() - 获取当前智能体身份信息
10
- 8. set_persona(persona_json) - 更新智能体 persona,包含 name、description、personality、greeting 等
11
- 9. run_workflow(steps) - 执行预定义工作流
12
- 10. get_operation_logs() - 获取操作日志
1
+ export const TOOL_DEFINITIONS = `
2
+ 可用工具:
3
+ 1. read_document(path) - 读取文档内容,支持 .txt, .md, .pdf, .docx
4
+ 2. summarize_document(path, context?) - 总结文档内容,可选提供上下文
5
+ 3. improve_document(path, requirements) - 改进文档,需提供文件路径和改进要求
6
+ 4. list_peers() - 列出已连接的对等节点
7
+ 5. send_message(peer_id, message) - 向指定对等节点发送消息
8
+ 6. broadcast_message(message) - 向所有对等节点广播消息
9
+ 7. get_identity() - 获取当前智能体身份信息
10
+ 8. set_persona(persona_json) - 更新智能体 persona,包含 name、description、personality、greeting 等
11
+ 9. run_workflow(steps) - 执行预定义工作流
12
+ 10. get_operation_logs() - 获取操作日志
13
13
  `;
@@ -1042,38 +1042,38 @@ export class PiAgentSession {
1042
1042
  }
1043
1043
  // M2.4: persona 缓存
1044
1044
  if (!this.cachedPersonaSection && this.persona) {
1045
- this.cachedPersonaSection = `
1046
- 角色描述: ${this.persona.description || '无'}
1047
- 性格特点: ${this.persona.personality || '无'}
1048
- 问候语: ${this.persona.greeting || '无'}
1045
+ this.cachedPersonaSection = `
1046
+ 角色描述: ${this.persona.description || '无'}
1047
+ 性格特点: ${this.persona.personality || '无'}
1048
+ 问候语: ${this.persona.greeting || '无'}
1049
1049
  `;
1050
1050
  }
1051
- const systemPrompt = `${this.bootstrapAddition}你是 ${this.identity.name},基于ReAct (Reasoning + Acting)模式工作。${this.cachedPersonaSection}
1052
- 当前工作目录: ${this.cwd}
1053
- 当前身份: ${this.identity.name} (${this.identity.did})
1054
- ${this.currentIntentHint}
1055
-
1056
- ${this.getToolDefinitions()}
1057
-
1058
- 工作模式:
1059
- 1. 理解用户自然语言请求
1060
- 2. 分析需要哪些工具来完成
1061
- 3. 按顺序调用工具并观察结果
1062
- 4. 根据观察结果决定下一步
1063
- 5. 最终给出完整回答
1064
-
1065
- 重要 (一次命中要求):
1066
- - 每次只调用一个工具
1067
- - 仔细分析工具返回结果
1068
- - 当任务完成时,必须在回答末尾添加 <final gen> 标记表示结束
1069
- - 如果需要更多信息,继续调用工具
1070
-
1071
- 【工具调用格式 (严格遵守, 否则系统无法解析)】
1072
- - 你只能输出**一个**工具调用, 不要堆叠多个 invoke
1073
- - 工具调用格式: {"name":"<tool_name>","input":{"arg1":"value1"}}
1074
- - 用 markdown json code block 包裹: \`\`\`json\n{"name":"X","input":{...}}\n\`\`\`
1075
- - 工具调用前可以简短思考 (1-2 句话), 但**不要写长篇 thinking** (会撞 max_tokens)
1076
- - 工具调用后必须等结果, 不要在同一个回复里继续输出
1051
+ const systemPrompt = `${this.bootstrapAddition}你是 ${this.identity.name},基于ReAct (Reasoning + Acting)模式工作。${this.cachedPersonaSection}
1052
+ 当前工作目录: ${this.cwd}
1053
+ 当前身份: ${this.identity.name} (${this.identity.did})
1054
+ ${this.currentIntentHint}
1055
+
1056
+ ${this.getToolDefinitions()}
1057
+
1058
+ 工作模式:
1059
+ 1. 理解用户自然语言请求
1060
+ 2. 分析需要哪些工具来完成
1061
+ 3. 按顺序调用工具并观察结果
1062
+ 4. 根据观察结果决定下一步
1063
+ 5. 最终给出完整回答
1064
+
1065
+ 重要 (一次命中要求):
1066
+ - 每次只调用一个工具
1067
+ - 仔细分析工具返回结果
1068
+ - 当任务完成时,必须在回答末尾添加 <final gen> 标记表示结束
1069
+ - 如果需要更多信息,继续调用工具
1070
+
1071
+ 【工具调用格式 (严格遵守, 否则系统无法解析)】
1072
+ - 你只能输出**一个**工具调用, 不要堆叠多个 invoke
1073
+ - 工具调用格式: {"name":"<tool_name>","input":{"arg1":"value1"}}
1074
+ - 用 markdown json code block 包裹: \`\`\`json\n{"name":"X","input":{...}}\n\`\`\`
1075
+ - 工具调用前可以简短思考 (1-2 句话), 但**不要写长篇 thinking** (会撞 max_tokens)
1076
+ - 工具调用后必须等结果, 不要在同一个回复里继续输出
1077
1077
  - <final gen> 只在**真完成所有任务**时输出, 不要在工具调用前/中输出${this.judgmentGateAddition}${this.contextHintAddition}`;
1078
1078
  // 2026-06-15: 把 currentOnStream 传给 loop, 让 step-timeline 在 pivot 循环里也能 emit step_start/done
1079
1079
  // 之前 loop.execute() 不接 streamCallback, 导致 step-timeline 只能看到老 runReActLoop 路径
@@ -1265,10 +1265,10 @@ ${this.getToolDefinitions()}
1265
1265
  }
1266
1266
  // M2.4: persona section 缓存 — persona 在 loadPersona() 时一次设定, 此后不变
1267
1267
  if (!this.cachedPersonaSection && this.persona) {
1268
- this.cachedPersonaSection = `
1269
- 角色描述: ${this.persona.description || '无'}
1270
- 性格特点: ${this.persona.personality || '无'}
1271
- 问候语: ${this.persona.greeting || '无'}
1268
+ this.cachedPersonaSection = `
1269
+ 角色描述: ${this.persona.description || '无'}
1270
+ 性格特点: ${this.persona.personality || '无'}
1271
+ 问候语: ${this.persona.greeting || '无'}
1272
1272
  `;
1273
1273
  }
1274
1274
  const personaSection = this.cachedPersonaSection;
@@ -1286,26 +1286,26 @@ ${this.getToolDefinitions()}
1286
1286
  .join('\n');
1287
1287
  loopProgressSection = `\n【本轮循环进度】你已完成以下 ${loopActionLog.length} 个动作, 这是连续执行的同一轮任务:\n${actionLines}\n请基于已有结果继续推进, 不要重复执行上面已成功的动作. 全部完成后用 <final gen> 结束.\n`;
1288
1288
  }
1289
- const systemPrompt = `${this.bootstrapAddition}你是 ${this.identity.name},基于ReAct (Reasoning + Acting)模式工作。${personaSection}
1290
- 当前工作目录: ${this.cwd}
1291
- 当前身份: ${this.identity.name} (${this.identity.did})
1292
- ${refineContext}
1293
- ${this.currentIntentHint}
1294
- ${loopProgressSection}
1295
-
1296
- ${toolDefs}
1297
-
1298
- 工作模式:
1299
- 1. 理解用户自然语言请求
1300
- 2. 分析需要哪些工具来完成
1301
- 3. 按顺序调用工具并观察结果
1302
- 4. 根据观察结果决定下一步
1303
- 5. 最终给出完整回答
1304
-
1305
- 重要:
1306
- - 每次只调用一个工具
1307
- - 仔细分析工具返回结果
1308
- - 当任务完成时,必须在回答末尾添加 <final gen> 标记表示结束
1289
+ const systemPrompt = `${this.bootstrapAddition}你是 ${this.identity.name},基于ReAct (Reasoning + Acting)模式工作。${personaSection}
1290
+ 当前工作目录: ${this.cwd}
1291
+ 当前身份: ${this.identity.name} (${this.identity.did})
1292
+ ${refineContext}
1293
+ ${this.currentIntentHint}
1294
+ ${loopProgressSection}
1295
+
1296
+ ${toolDefs}
1297
+
1298
+ 工作模式:
1299
+ 1. 理解用户自然语言请求
1300
+ 2. 分析需要哪些工具来完成
1301
+ 3. 按顺序调用工具并观察结果
1302
+ 4. 根据观察结果决定下一步
1303
+ 5. 最终给出完整回答
1304
+
1305
+ 重要:
1306
+ - 每次只调用一个工具
1307
+ - 仔细分析工具返回结果
1308
+ - 当任务完成时,必须在回答末尾添加 <final gen> 标记表示结束
1309
1309
  - 如果需要更多信息,继续调用工具${this.judgmentGateAddition}${this.contextHintAddition}`;
1310
1310
  // 3 个恢复机制 (Claude Code 论文 9-step pipeline 内部):
1311
1311
  // 1. max output token 升级 (最多 3 次, 每次 maxOutputTokens 翻倍)
@@ -2376,37 +2376,37 @@ ${toolDefs}
2376
2376
  getDefaultResponse(input) {
2377
2377
  const operationsRef = PiAgentSession.getOperationsReference();
2378
2378
  if (operationsRef) {
2379
- return `收到了: "${input}"
2380
-
2381
- 我是一个判断力处理智能体,支持自然语言交互。
2382
-
2383
- 可用操作(直接说出即可):
2384
- ${this.extractOperationsFromRef(operationsRef)}
2385
-
2386
- 示例请求:
2387
- - "读取 src/index.ts 文件"
2388
- - "总结一下 README.md"
2389
- - "查看当前连接了哪些节点"
2379
+ return `收到了: "${input}"
2380
+
2381
+ 我是一个判断力处理智能体,支持自然语言交互。
2382
+
2383
+ 可用操作(直接说出即可):
2384
+ ${this.extractOperationsFromRef(operationsRef)}
2385
+
2386
+ 示例请求:
2387
+ - "读取 src/index.ts 文件"
2388
+ - "总结一下 README.md"
2389
+ - "查看当前连接了哪些节点"
2390
2390
  - "向 QmABC... 发送测试消息"`;
2391
2391
  }
2392
- return `收到了: "${input}"
2393
-
2394
- 我是一个判断力处理智能体,支持自然语言交互。
2395
-
2396
- 可用操作(直接说出即可):
2397
- - "读取 README.md" - 读取并分析文档
2398
- - "总结文档" - 总结文档内容
2399
- - "改进文档,按照X要求" - 改进文档
2400
- - "查看节点" - 查看已连接的对等节点
2401
- - "向X发送消息Y" - 向对等节点发送消息
2402
- - "广播消息X" - 广播消息到所有节点
2403
- - "查看身份" - 查看当前智能体身份
2404
- - "查看日志" - 查看最近操作日志
2405
-
2406
- 示例请求:
2407
- - "读取 src/index.ts 文件"
2408
- - "总结一下 README.md"
2409
- - "查看当前连接了哪些节点"
2392
+ return `收到了: "${input}"
2393
+
2394
+ 我是一个判断力处理智能体,支持自然语言交互。
2395
+
2396
+ 可用操作(直接说出即可):
2397
+ - "读取 README.md" - 读取并分析文档
2398
+ - "总结文档" - 总结文档内容
2399
+ - "改进文档,按照X要求" - 改进文档
2400
+ - "查看节点" - 查看已连接的对等节点
2401
+ - "向X发送消息Y" - 向对等节点发送消息
2402
+ - "广播消息X" - 广播消息到所有节点
2403
+ - "查看身份" - 查看当前智能体身份
2404
+ - "查看日志" - 查看最近操作日志
2405
+
2406
+ 示例请求:
2407
+ - "读取 src/index.ts 文件"
2408
+ - "总结一下 README.md"
2409
+ - "查看当前连接了哪些节点"
2410
2410
  - "向 QmABC... 发送测试消息"`;
2411
2411
  }
2412
2412
  extractOperationsFromRef(ref) {
@@ -2431,13 +2431,13 @@ ${this.extractOperationsFromRef(operationsRef)}
2431
2431
  }
2432
2432
  }
2433
2433
  return operationLines.length > 0 ? operationLines.join('\n') :
2434
- ` - "读取 README.md" - 读取并分析文档
2435
- - "总结文档" - 总结文档内容
2436
- - "改进文档,按照X要求" - 改进文档
2437
- - "查看节点" - 查看已连接的对等节点
2438
- - "向X发送消息Y" - 向对等节点发送消息
2439
- - "广播消息X" - 广播消息到所有节点
2440
- - "查看身份" - 查看当前智能体身份
2434
+ ` - "读取 README.md" - 读取并分析文档
2435
+ - "总结文档" - 总结文档内容
2436
+ - "改进文档,按照X要求" - 改进文档
2437
+ - "查看节点" - 查看已连接的对等节点
2438
+ - "向X发送消息Y" - 向对等节点发送消息
2439
+ - "广播消息X" - 广播消息到所有节点
2440
+ - "查看身份" - 查看当前智能体身份
2441
2441
  - "查看日志" - 查看最近操作日志`;
2442
2442
  }
2443
2443
  async suggestRename(messages) {