@bolloon/bolloon-agent 0.4.5 → 0.4.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +135 -135
- package/bin/bolloon-cli.cjs +183 -183
- package/bin/bolloon.cjs +0 -0
- package/bin/bolloon.js +157 -0
- package/dist/agents/constraint-layer.js +19 -19
- package/dist/agents/judgment-protocol.js +14 -14
- package/dist/agents/pi-sdk-tools.js +128 -46
- package/dist/agents/pi-sdk-types.js +12 -12
- package/dist/agents/pi-sdk.js +103 -90
- package/dist/agents/process-runner.js +105 -0
- package/dist/agents/skill-organizer.js +19 -19
- package/dist/agents/skill-writer.js +15 -3
- package/dist/agents/workflow-pivot-loop.js +13 -3
- package/dist/agents/write-staging.js +85 -0
- package/dist/bollharness/src/scripts/deploy-guard.js +8 -8
- package/dist/bollharness-integration/context-router-judgment.js +4 -4
- package/dist/bollharness-integration/context-router.js +292 -292
- package/dist/bollharness-integration/gate-state-machine.js +13 -13
- package/dist/bollharness-integration/llm-judgment-engine.js +134 -134
- package/dist/bollharness-integration/skill-adapter.js +21 -21
- package/dist/bootstrap/chat-archiver.js +14 -14
- package/dist/bootstrap/context-os.js +24 -24
- package/dist/bootstrap/memory-compressor.js +14 -14
- package/dist/cli/ink-app.js +18 -0
- package/dist/cli-entry.js +36 -36
- package/dist/context-compaction/auto-compact.js +7 -7
- package/dist/electron/first-run.js +54 -54
- package/dist/electron-build/electron/first-run.js +54 -54
- package/dist/electron-preload.js.map +1 -1
- package/dist/electron.js.map +1 -1
- package/dist/index.js +230 -137
- package/dist/llm/llm-judgment-client.js +102 -102
- package/dist/llm/pi-ai.js +48 -48
- package/dist/llm/system-prompt/layers/channel/human-async.md +41 -41
- package/dist/llm/system-prompt/layers/channel/local.md +14 -14
- package/dist/llm/system-prompt/layers/channel/p2p-agent.md +18 -18
- package/dist/llm/system-prompt/layers/channel/p2p-peer-sync.md +51 -51
- package/dist/llm/system-prompt/layers/channel/p2p-proactive.md +43 -43
- package/dist/llm/system-prompt/layers/channel/p2p-visitor.md +19 -19
- package/dist/llm/system-prompt/layers/channel/session-handoff.md +61 -61
- package/dist/llm/system-prompt/layers/core/artifacts_storage.md +89 -89
- package/dist/llm/system-prompt/layers/core/evenhandedness.md +21 -21
- package/dist/llm/system-prompt/layers/core/external-engagement.md +73 -73
- package/dist/llm/system-prompt/layers/core/hibs_reminders.md +15 -15
- package/dist/llm/system-prompt/layers/core/identity.md +48 -48
- package/dist/llm/system-prompt/layers/core/knowledge.md +17 -17
- package/dist/llm/system-prompt/layers/core/memory_system.md +12 -12
- package/dist/llm/system-prompt/layers/core/network_filesystem.md +28 -28
- package/dist/llm/system-prompt/layers/core/refusal.md +37 -37
- package/dist/llm/system-prompt/layers/core/tone.md +31 -31
- package/dist/llm/system-prompt/layers/core/tools.thin.md +13 -13
- package/dist/llm/system-prompt/layers/core/wellbeing.md +41 -41
- package/dist/llm/system-prompt/layers/role/architect.md +20 -20
- package/dist/llm/system-prompt/layers/role/expert.md +19 -19
- package/dist/llm/system-prompt/layers/role/implementer.md +15 -15
- package/dist/llm/system-prompt/layers/role/security.md +15 -15
- package/dist/llm/system-prompt/layers/tool/artifacts.md +72 -72
- package/dist/llm/system-prompt/layers/tool/bash.md +25 -25
- package/dist/llm/system-prompt/layers/tool/goal_handoff.md +77 -77
- package/dist/llm/system-prompt/layers/tool/hibs_api.md +171 -171
- package/dist/llm/system-prompt/layers/tool/image_search.md +70 -70
- package/dist/llm/system-prompt/layers/tool/manifest.md +89 -89
- package/dist/llm/system-prompt/layers/tool/mcp_apps.md +53 -53
- package/dist/llm/system-prompt/layers/tool/p2p_request.md +61 -61
- package/dist/llm/system-prompt/layers/tool/web_search.md +83 -83
- package/dist/llm/tool-manifest/ask_user_input.js +1 -1
- package/dist/llm/tool-manifest/bash.js +3 -3
- package/dist/llm/tool-manifest/create_file.js +4 -4
- package/dist/llm/tool-manifest/fetch_sports_data.js +3 -3
- package/dist/llm/tool-manifest/image_search.js +2 -2
- package/dist/llm/tool-manifest/index.js +0 -59
- package/dist/llm/tool-manifest/mcp.js +2 -2
- package/dist/llm/tool-manifest/message_compose.js +3 -3
- package/dist/llm/tool-manifest/places.js +2 -2
- package/dist/llm/tool-manifest/present_files.js +1 -1
- package/dist/llm/tool-manifest/recipe.js +2 -2
- package/dist/llm/tool-manifest/str_replace.js +5 -5
- package/dist/llm/tool-manifest/view.js +3 -3
- package/dist/llm/tool-manifest/weather.js +4 -4
- package/dist/llm/tool-manifest/web.js +4 -4
- package/dist/orbitdb/agent-tools.js +139 -0
- package/dist/orbitdb/kanban-store.js +259 -0
- package/dist/orbitdb/task-store.js +112 -0
- package/dist/pi-ecosystem-colony/index.js +365 -0
- package/dist/pi-ecosystem-judgment/causal-judge.js +51 -51
- package/dist/pi-ecosystem-judgment/distill-prompt.js +42 -42
- package/dist/pi-ecosystem-judgment/distillation.js +14 -14
- package/dist/pi-ecosystem-judgment/monitor-gate.js +28 -28
- package/dist/pi-ecosystem-judgment/value-injection.js +102 -102
- package/dist/security/context-router-tool.js +15 -15
- package/dist/security/tool-gate.js +2 -0
- package/dist/social/ant-colony/AdaptiveHeartbeat.js +101 -0
- package/dist/social/ant-colony/PheromoneEngine.js +227 -0
- package/dist/social/ant-colony/index.js +6 -0
- package/dist/social/ant-colony/types.js +24 -0
- package/dist/storage/trajectory.js +101 -0
- package/dist/test/ai-judgment-test.js +80 -0
- package/dist/test/bollharness-integration.test.js +318 -0
- package/dist/test/channel-agent-multi-dialogue.js +205 -0
- package/dist/test/channel-heartbeat-agent-test.js +201 -0
- package/dist/test/constraint-layer.test.js +164 -0
- package/dist/test/diap-identity-test.js +172 -0
- package/dist/test/diap-quick-test.js +62 -0
- package/dist/test/global-shared-context.test.js +315 -0
- package/dist/test/harness-judgment-injection.test.js +246 -0
- package/dist/test/harness-workflow-integrator-test.js +228 -0
- package/dist/test/human-value-store.test.js +243 -0
- package/dist/test/hybrid-integration-test.js +118 -0
- package/dist/test/hybrid-messenger-verify.js +55 -0
- package/dist/test/iroh-bistream-debug.js +38 -0
- package/dist/test/iroh-communication.test.js +66 -0
- package/dist/test/iroh-debug-test.js +57 -0
- package/dist/test/iroh-diap-test.js +71 -0
- package/dist/test/iroh-direct-connect.js +55 -0
- package/dist/test/iroh-e2e-fixed.js +89 -0
- package/dist/test/iroh-e2e-same-process.js +63 -0
- package/dist/test/iroh-e2e.js +66 -0
- package/dist/test/iroh-final-e2e.js +72 -0
- package/dist/test/iroh-relay-test.js +37 -0
- package/dist/test/iroh-simple-test.js +41 -0
- package/dist/test/iroh-transport-verify.js +54 -0
- package/dist/test/iroh-transport.test.js +37 -0
- package/dist/test/iroh-two-nodes.js +70 -0
- package/dist/test/iroh-verify.js +44 -0
- package/dist/test/judgment-decision.test.js +219 -0
- package/dist/test/llm-judgment-integration.test.js +220 -0
- package/dist/test/p2p-agent-complex-dialogue.js +385 -0
- package/dist/test/p2p-agent-dialogue.js +341 -0
- package/dist/test/p2p-agent-full-bidirectional.js +510 -0
- package/dist/test/p2p-agent-harness-flow.js +437 -0
- package/dist/test/p2p-agent-harness-single.js +143 -0
- package/dist/test/p2p-ai-dialogue-test.js +318 -0
- package/dist/test/p2p-cid-connect-test.js +195 -0
- package/dist/test/p2p-connect-receiver.js +69 -0
- package/dist/test/p2p-doc-transfer.js +110 -0
- package/dist/test/p2p-identity-page-test.js +77 -0
- package/dist/test/p2p-iroh-test.js +171 -0
- package/dist/test/p2p-minimal-test.js +241 -0
- package/dist/test/p2p-node-1.js +148 -0
- package/dist/test/p2p-node-2.js +148 -0
- package/dist/test/p2p-server.js +281 -0
- package/dist/test/p2p-two-nodes-test.js +438 -0
- package/dist/test/pi-sdk.test.js +44 -0
- package/dist/test/set-persona.js +40 -0
- package/dist/test/simple.test.js +9 -0
- package/dist/test/storage-integration.test.js +150 -0
- package/dist/test/subagent-manager.test.js +276 -0
- package/dist/test/test-gate-flow.test.js +81 -0
- package/dist/test/workflow-engine.test.js +87 -0
- package/dist/test/workflow-pivot-loop.test.js +246 -0
- package/dist/web/api-config.html +779 -779
- package/dist/web/client-hearth.js +10 -10
- package/dist/web/components/wallet-viem.mjs +118 -118
- package/dist/web/index.html +472 -472
- package/dist/web/manifest.json +20 -20
- package/dist/web/server-storage.js +29 -0
- package/dist/web/server.js +30 -14
- package/dist/web/style.css +4946 -4946
- package/package.json +195 -195
- package/scripts/build-cli.js +215 -215
- package/scripts/build-web.ts +130 -130
- package/scripts/postinstall.js +152 -152
- package/bin/bolloon-daemon.sh +0 -207
- package/dist/constraint-runtime/reference_data/archive_surface_snapshot.json +0 -63
- package/dist/constraint-runtime/reference_data/commands_snapshot.json +0 -1037
- package/dist/constraint-runtime/reference_data/subsystems/assistant.json +0 -8
- package/dist/constraint-runtime/reference_data/subsystems/bootstrap.json +0 -8
- package/dist/constraint-runtime/reference_data/subsystems/bridge.json +0 -32
- package/dist/constraint-runtime/reference_data/subsystems/buddy.json +0 -13
- package/dist/constraint-runtime/reference_data/subsystems/cli.json +0 -26
- package/dist/constraint-runtime/reference_data/subsystems/components.json +0 -32
- package/dist/constraint-runtime/reference_data/subsystems/constants.json +0 -28
- package/dist/constraint-runtime/reference_data/subsystems/coordinator.json +0 -8
- package/dist/constraint-runtime/reference_data/subsystems/entrypoints.json +0 -15
- package/dist/constraint-runtime/reference_data/subsystems/hooks.json +0 -32
- package/dist/constraint-runtime/reference_data/subsystems/keybindings.json +0 -21
- package/dist/constraint-runtime/reference_data/subsystems/memdir.json +0 -15
- package/dist/constraint-runtime/reference_data/subsystems/migrations.json +0 -18
- package/dist/constraint-runtime/reference_data/subsystems/moreright.json +0 -8
- package/dist/constraint-runtime/reference_data/subsystems/native_ts.json +0 -11
- package/dist/constraint-runtime/reference_data/subsystems/outputStyles.json +0 -8
- package/dist/constraint-runtime/reference_data/subsystems/plugins.json +0 -9
- package/dist/constraint-runtime/reference_data/subsystems/remote.json +0 -11
- package/dist/constraint-runtime/reference_data/subsystems/schemas.json +0 -8
- package/dist/constraint-runtime/reference_data/subsystems/screens.json +0 -10
- package/dist/constraint-runtime/reference_data/subsystems/server.json +0 -10
- package/dist/constraint-runtime/reference_data/subsystems/services.json +0 -32
- package/dist/constraint-runtime/reference_data/subsystems/skills.json +0 -27
- package/dist/constraint-runtime/reference_data/subsystems/state.json +0 -13
- package/dist/constraint-runtime/reference_data/subsystems/types.json +0 -18
- package/dist/constraint-runtime/reference_data/subsystems/upstreamproxy.json +0 -9
- package/dist/constraint-runtime/reference_data/subsystems/utils.json +0 -32
- package/dist/constraint-runtime/reference_data/subsystems/vim.json +0 -12
- package/dist/constraint-runtime/reference_data/subsystems/voice.json +0 -8
- package/dist/constraint-runtime/reference_data/tools_snapshot.json +0 -922
- package/dist/electron/config.js.map +0 -1
- package/dist/electron/dialogs.js.map +0 -1
- package/dist/electron/first-run.js.map +0 -1
- package/dist/electron/ipc.js.map +0 -1
- package/dist/electron/logger.js.map +0 -1
- package/dist/electron/main.js.map +0 -1
- package/dist/electron/menu.js.map +0 -1
- package/dist/electron/paths.js.map +0 -1
- package/dist/electron/server.js.map +0 -1
- package/dist/electron/tray.js.map +0 -1
- package/dist/electron/window.js.map +0 -1
- package/dist/utils/auto-update.js.map +0 -1
- 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 = [];
|
|
@@ -32,8 +32,58 @@ import { delegateToEngine } from '../external-engines/delegate.js';
|
|
|
32
32
|
*/
|
|
33
33
|
export const SIDE_EFFECT_TOOLS = new Set([
|
|
34
34
|
'write_file', 'edit_file', 'shell_exec', 'git_commit', 'git_push', 'git_branch',
|
|
35
|
-
'create_task', 'update_task',
|
|
35
|
+
'create_task', 'update_task', 'terminal', 'process',
|
|
36
36
|
]);
|
|
37
|
+
export async function runTerminalCommand(raw, opts = {}) {
|
|
38
|
+
const { checkTerminalCommand } = await import('./shell-guard.js');
|
|
39
|
+
const timeoutMs = opts.timeoutMs ?? 30000;
|
|
40
|
+
const list = (opts.commands && opts.commands.length > 0) ? opts.commands : [raw];
|
|
41
|
+
for (const c of list) {
|
|
42
|
+
const guard = checkTerminalCommand(String(c || '').trim());
|
|
43
|
+
if (!guard.allowed) {
|
|
44
|
+
return { success: false, error: `[terminal-guard] ${guard.reason}`, deniedByGuard: true };
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// 2026-08-12 (TaskD): 后台执行 — 长命令不阻塞对话.
|
|
48
|
+
if (opts.background) {
|
|
49
|
+
const cmdStr = list.join(' && ');
|
|
50
|
+
const { spawnBackground } = await import('./process-runner.js');
|
|
51
|
+
const session = spawnBackground(cmdStr, opts.cwd ?? process.cwd());
|
|
52
|
+
return {
|
|
53
|
+
success: true,
|
|
54
|
+
background: true,
|
|
55
|
+
sessionId: session.id,
|
|
56
|
+
cmd: cmdStr.slice(0, 200),
|
|
57
|
+
message: `已在后台启动 (${session.id}). 用 process 工具轮询/等待: process(session_id="${session.id}", action="poll"|"wait"|"kill")`,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const { exec } = await import('child_process');
|
|
61
|
+
const runOne = (cmdStr) => new Promise((resolve) => {
|
|
62
|
+
exec(cmdStr, {
|
|
63
|
+
cwd: opts.cwd ?? process.cwd(),
|
|
64
|
+
timeout: timeoutMs,
|
|
65
|
+
maxBuffer: 8 * 1024 * 1024,
|
|
66
|
+
env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
|
|
67
|
+
}, (err, stdout, stderr) => {
|
|
68
|
+
const output = ((stdout || '') + (stderr ? `\n[stderr]\n${stderr}` : '')).trim().slice(0, 8000);
|
|
69
|
+
if (err) {
|
|
70
|
+
resolve({ success: false, cmd: cmdStr.slice(0, 120), error: `exit ${err.code ?? '?'}: ${String(err.message || '').slice(0, 200)}`, output: output || undefined });
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
resolve({ success: true, cmd: cmdStr.slice(0, 120), output: output || '(无输出)' });
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
if (list.length === 1)
|
|
78
|
+
return runOne(list[0]);
|
|
79
|
+
const results = await Promise.all(list.map((c) => runOne(c)));
|
|
80
|
+
const failed = results.filter((r) => !r.success);
|
|
81
|
+
const allOutput = results.map((r) => `${r.cmd}\n${r.output || r.error || ''}`).join('\n\n---\n\n');
|
|
82
|
+
if (failed.length > 0) {
|
|
83
|
+
return { success: false, error: `${failed.length}/${list.length} 条命令失败: ${failed[0].error}`, output: allOutput, partial: results };
|
|
84
|
+
}
|
|
85
|
+
return { success: true, output: allOutput, count: list.length, parallel: true };
|
|
86
|
+
}
|
|
37
87
|
export function registerBuiltinTools(ctx) {
|
|
38
88
|
ctx.tools.set('read_document', {
|
|
39
89
|
name: 'read_document',
|
|
@@ -621,17 +671,22 @@ export function registerBuiltinTools(ctx) {
|
|
|
621
671
|
for (const tool of p2pDocumentTools) {
|
|
622
672
|
ctx.tools.set(tool.name, tool);
|
|
623
673
|
}
|
|
624
|
-
// shell_exec
|
|
674
|
+
// 2026-08-12 (Task2): shell_exec / terminal 统一走模块级 runTerminalCommand (宽松护栏 + 多命令并行).
|
|
675
|
+
// shell_exec — 2026-08-12 (Task2): 与 terminal 统一走宽松护栏 (denylist-only).
|
|
676
|
+
// 兼容旧格式 (command + args 数组), 内部转成完整命令字符串交给 runTerminal,
|
|
677
|
+
// 不再用窄白名单 → 模型不会因 "command 不在白名单" 报错.
|
|
625
678
|
ctx.tools.set('shell_exec', {
|
|
626
679
|
name: 'shell_exec',
|
|
627
|
-
description: '
|
|
628
|
-
parameters: { command: '可执行文件 (
|
|
680
|
+
description: '执行 shell 命令 (兼容模式, 参数数组或命令字符串均可). 护栏只挡高危破坏操作 (sudo/格式化/rm -rf 根目录/写 ~/.bolloon 数据). 推荐直接用 terminal 传完整命令字符串.',
|
|
681
|
+
parameters: { command: '可执行文件 或 完整命令 (必填)', args: '参数数组, 逗号分隔 (可选)', timeoutMs: '超时毫秒, 默认 30000' },
|
|
629
682
|
execute: async (args) => {
|
|
630
683
|
const cmd = String(args.command || '').trim();
|
|
631
684
|
if (!cmd)
|
|
632
685
|
return { success: false, error: 'command 必填' };
|
|
633
|
-
|
|
686
|
+
// 兼容参数数组 → 拼成命令字符串
|
|
687
|
+
let full = cmd;
|
|
634
688
|
const rawArgs = args.args;
|
|
689
|
+
let argList = [];
|
|
635
690
|
if (Array.isArray(rawArgs)) {
|
|
636
691
|
argList = rawArgs.map((s) => String(s).trim()).filter(Boolean);
|
|
637
692
|
}
|
|
@@ -640,61 +695,76 @@ export function registerBuiltinTools(ctx) {
|
|
|
640
695
|
if (trimmed.startsWith('[') && trimmed.endsWith(']')) {
|
|
641
696
|
try {
|
|
642
697
|
const parsed = JSON.parse(trimmed);
|
|
643
|
-
if (Array.isArray(parsed))
|
|
698
|
+
if (Array.isArray(parsed))
|
|
644
699
|
argList = parsed.map((s) => String(s).trim()).filter(Boolean);
|
|
645
|
-
}
|
|
646
700
|
}
|
|
647
|
-
catch { /*
|
|
701
|
+
catch { /* 非 JSON 数组 */ }
|
|
648
702
|
}
|
|
649
|
-
if (argList.length === 0)
|
|
703
|
+
if (argList.length === 0)
|
|
650
704
|
argList = trimmed.split(',').map(s => s.trim()).filter(Boolean);
|
|
651
|
-
}
|
|
652
705
|
}
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
return { success: false, error: result.error };
|
|
706
|
+
// 若 command 只是可执行文件名且带 args → 拼成 "cmd arg1 arg2"; 否则原样
|
|
707
|
+
if (argList.length > 0 && !/\s/.test(cmd) && !cmd.includes('&&') && !cmd.includes(';') && !cmd.includes('|')) {
|
|
708
|
+
full = `${cmd} ${argList.map(a => (/[\s"&|<>^()%!`]/.test(a) ? `"${a.replace(/"/g, '""')}"` : a)).join(' ')}`;
|
|
657
709
|
}
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
}
|
|
661
|
-
return { success: true, output: result.output };
|
|
710
|
+
const timeoutMs = Number(args.timeoutMs) || 30000;
|
|
711
|
+
return await runTerminalCommand(full, { timeoutMs, cwd: ctx.cwd });
|
|
662
712
|
}
|
|
663
713
|
});
|
|
664
714
|
// 2026-08-10: terminal — 灵活终端写命令 (用户要求: bolloon 自己写命令进 terminal, 少围栏).
|
|
665
|
-
//
|
|
666
|
-
//
|
|
715
|
+
// 2026-08-12 (Task2): 支持 commands 数组并行执行; 与 shell_exec 统一走 runTerminalCommand.
|
|
716
|
+
// 与 shell_exec 的区别: 直接接受完整 shell 命令字符串, 更适合模型自主写命令.
|
|
667
717
|
ctx.tools.set('terminal', {
|
|
668
718
|
name: 'terminal',
|
|
669
|
-
description: '执行完整 shell 命令 (支持管道/重定向/写文件/跑脚本). 护栏只挡高危破坏操作 (sudo/格式化/rm -rf 根目录/写 ~/.bolloon 数据), 其余灵活放行. 适合: 写 HTML 文件、跑 python/node 脚本、查系统状态、装依赖.',
|
|
670
|
-
parameters: { command: '完整 shell 命令 (必填, 如: echo "<html>" > /tmp/site/index.html && ls /tmp/site)', timeoutMs: '超时毫秒, 默认 30000' },
|
|
719
|
+
description: '执行完整 shell 命令 (支持管道/重定向/写文件/跑脚本). 护栏只挡高危破坏操作 (sudo/格式化/rm -rf 根目录/写 ~/.bolloon 数据), 其余灵活放行. 适合: 写 HTML 文件、跑 python/node 脚本、查系统状态、装依赖. 多条命令用 commands 数组并行执行. 长命令 (服务器/构建/后台任务) 设 background=true 后台执行不阻塞对话.',
|
|
720
|
+
parameters: { command: '完整 shell 命令 (必填, 如: echo "<html>" > /tmp/site/index.html && ls /tmp/site)', commands: '可选: 多条命令数组 (并行执行), 每条独立字符串', timeoutMs: '超时毫秒, 默认 30000', background: '可选: true 后台执行, 立即返回 session_id (用 process 工具 poll/wait/kill)' },
|
|
671
721
|
execute: async (args) => {
|
|
672
722
|
const raw = String(args.command || '').trim();
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
const guard = checkTerminalCommand(raw);
|
|
677
|
-
if (!guard.allowed) {
|
|
678
|
-
return { success: false, error: `[terminal-guard] ${guard.reason}`, deniedByGuard: true };
|
|
679
|
-
}
|
|
680
|
-
const { exec } = await import('child_process');
|
|
723
|
+
const commands = Array.isArray(args.commands) ? args.commands.map((c) => String(c || '').trim()).filter(Boolean) : [];
|
|
724
|
+
if (!raw && commands.length === 0)
|
|
725
|
+
return { success: false, error: 'command 或 commands 必填' };
|
|
681
726
|
const timeoutMs = Number(args.timeoutMs) || 30000;
|
|
682
|
-
return
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
727
|
+
return await runTerminalCommand(raw, { timeoutMs, cwd: ctx.cwd, commands: commands.length > 0 ? commands : undefined, background: String(args.background).toLowerCase() === 'true' });
|
|
728
|
+
}
|
|
729
|
+
});
|
|
730
|
+
// 2026-08-12 (TaskD): process — 后台进程管理 (学 hermes terminal background session + poll/wait/kill).
|
|
731
|
+
// 长命令不阻塞对话: terminal(background=true) 启动 → process 工具轮询/等待/终止.
|
|
732
|
+
ctx.tools.set('process', {
|
|
733
|
+
name: 'process',
|
|
734
|
+
description: '管理后台进程 (terminal background=true 启动的). action: poll(查状态, 不阻塞) / wait(等结束, 最多 timeoutMs) / kill(终止) / list(列全部). 长期运行命令的阻塞问题用它解决.',
|
|
735
|
+
parameters: { session_id: '后台进程 session_id (必填, poll/wait/kill 用)', action: 'poll | wait | kill | list (默认 poll)', timeoutMs: 'wait 模式等待上限毫秒, 默认 30000' },
|
|
736
|
+
execute: async (args) => {
|
|
737
|
+
const action = String(args.action || 'poll').trim().toLowerCase();
|
|
738
|
+
try {
|
|
739
|
+
const { pollSession, waitSession, killSession, listSessions, isValidSessionId } = await import('./process-runner.js');
|
|
740
|
+
if (action === 'list') {
|
|
741
|
+
const all = listSessions();
|
|
742
|
+
return { success: true, output: all.length === 0 ? '(无后台进程)' : all.map((s) => ` [${s.status}] ${s.id} ${s.cmd}`).join('\n') };
|
|
743
|
+
}
|
|
744
|
+
const sid = String(args.session_id || '').trim();
|
|
745
|
+
if (!sid)
|
|
746
|
+
return { success: false, error: 'session_id 必填' };
|
|
747
|
+
if (!isValidSessionId(sid))
|
|
748
|
+
return { success: false, error: 'session_id 非法' };
|
|
749
|
+
if (action === 'kill') {
|
|
750
|
+
const r = killSession(sid);
|
|
751
|
+
return { success: r.ok, output: r.reason ?? `已终止 ${sid}` };
|
|
752
|
+
}
|
|
753
|
+
if (action === 'wait') {
|
|
754
|
+
const t = Number(args.timeoutMs) || 30000;
|
|
755
|
+
const r = await waitSession(sid, t);
|
|
756
|
+
return { success: r.ok, output: r.session ? `${r.session.status} exit=${r.session.exitCode}${r.session.timedOut ? ' (超时)' : ''}\n${r.session.output || '(无输出)'}` : '未知 session' };
|
|
757
|
+
}
|
|
758
|
+
// poll
|
|
759
|
+
const r = pollSession(sid);
|
|
760
|
+
if (!r.ok || !r.session)
|
|
761
|
+
return { success: false, error: `未知 session ${sid}` };
|
|
762
|
+
const s = r.session;
|
|
763
|
+
return { success: true, output: `[${s.status}] exit=${s.exitCode}${s.status === 'running' ? ' (运行中)' : ''}\n${s.output || '(无输出)'}` };
|
|
764
|
+
}
|
|
765
|
+
catch (e) {
|
|
766
|
+
return { success: false, error: `process ${action} 失败: ${String(e?.message || e).slice(0, 200)}` };
|
|
767
|
+
}
|
|
698
768
|
}
|
|
699
769
|
});
|
|
700
770
|
// self_improve
|
|
@@ -729,6 +799,15 @@ export function registerBuiltinTools(ctx) {
|
|
|
729
799
|
}
|
|
730
800
|
try {
|
|
731
801
|
const absPath = path.resolve(ctx.cwd, relPath);
|
|
802
|
+
// 2026-08-12 (TaskC): 写前暂存 (准备阶段) — 记录变更前快照, 支持审计/撤销. 失败静默.
|
|
803
|
+
let before = '';
|
|
804
|
+
try {
|
|
805
|
+
before = await fs.readFile(absPath, 'utf-8');
|
|
806
|
+
}
|
|
807
|
+
catch { /* 新文件 */ }
|
|
808
|
+
const action = before.length > 0 ? 'overwrite' : 'create';
|
|
809
|
+
const { stageWrite } = await import('./write-staging.js');
|
|
810
|
+
await stageWrite(relPath, before, content, action, ctx.cwd).catch(() => { });
|
|
732
811
|
await fs.mkdir(path.dirname(absPath), { recursive: true });
|
|
733
812
|
await fs.writeFile(absPath, content, 'utf-8');
|
|
734
813
|
return { success: true, output: `✅ wrote ${relPath} (${content.length} bytes)` };
|
|
@@ -761,6 +840,9 @@ export function registerBuiltinTools(ctx) {
|
|
|
761
840
|
return { success: false, error: `old_text 在 ${relPath} 中未找到, 拒绝静默写入. 请先用 read_document 读最新内容.` };
|
|
762
841
|
}
|
|
763
842
|
const updated = original.replace(oldText, newText);
|
|
843
|
+
// 2026-08-12 (TaskC): 写前暂存 (准备阶段) — 记录变更前快照, 支持审计/撤销. 失败静默.
|
|
844
|
+
const { stageWrite } = await import('./write-staging.js');
|
|
845
|
+
await stageWrite(relPath, original, updated, 'edit', ctx.cwd).catch(() => { });
|
|
764
846
|
await fs.writeFile(absPath, updated, 'utf-8');
|
|
765
847
|
return { success: true, output: `✅ edited ${relPath} (${oldText.length} → ${newText.length} 字节)` };
|
|
766
848
|
}
|
|
@@ -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
|
`;
|