@bolloon/bolloon-agent 0.4.14 → 0.4.15
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 +127 -127
- package/bin/bolloon-cli.cjs +183 -183
- package/bin/bolloon-daemon.sh +207 -0
- package/bin/bolloon.cjs +0 -0
- package/dist/agents/constraint-layer.js +19 -19
- package/dist/agents/judgment-protocol.js +14 -14
- package/dist/agents/pi-sdk-types.js +12 -12
- package/dist/agents/pi-sdk.js +101 -101
- package/dist/agents/skill-organizer.js +19 -19
- package/dist/agents/workflow-pivot-loop.js +2 -2
- package/dist/agents/x402/x402Pay.js +4 -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 +13 -13
- package/dist/cli-entry.js +36 -36
- package/dist/constraint-runtime/reference_data/archive_surface_snapshot.json +63 -0
- package/dist/constraint-runtime/reference_data/commands_snapshot.json +1037 -0
- package/dist/constraint-runtime/reference_data/subsystems/assistant.json +8 -0
- package/dist/constraint-runtime/reference_data/subsystems/bootstrap.json +8 -0
- package/dist/constraint-runtime/reference_data/subsystems/bridge.json +32 -0
- package/dist/constraint-runtime/reference_data/subsystems/buddy.json +13 -0
- package/dist/constraint-runtime/reference_data/subsystems/cli.json +26 -0
- package/dist/constraint-runtime/reference_data/subsystems/components.json +32 -0
- package/dist/constraint-runtime/reference_data/subsystems/constants.json +28 -0
- package/dist/constraint-runtime/reference_data/subsystems/coordinator.json +8 -0
- package/dist/constraint-runtime/reference_data/subsystems/entrypoints.json +15 -0
- package/dist/constraint-runtime/reference_data/subsystems/hooks.json +32 -0
- package/dist/constraint-runtime/reference_data/subsystems/keybindings.json +21 -0
- package/dist/constraint-runtime/reference_data/subsystems/memdir.json +15 -0
- package/dist/constraint-runtime/reference_data/subsystems/migrations.json +18 -0
- package/dist/constraint-runtime/reference_data/subsystems/moreright.json +8 -0
- package/dist/constraint-runtime/reference_data/subsystems/native_ts.json +11 -0
- package/dist/constraint-runtime/reference_data/subsystems/outputStyles.json +8 -0
- package/dist/constraint-runtime/reference_data/subsystems/plugins.json +9 -0
- package/dist/constraint-runtime/reference_data/subsystems/remote.json +11 -0
- package/dist/constraint-runtime/reference_data/subsystems/schemas.json +8 -0
- package/dist/constraint-runtime/reference_data/subsystems/screens.json +10 -0
- package/dist/constraint-runtime/reference_data/subsystems/server.json +10 -0
- package/dist/constraint-runtime/reference_data/subsystems/services.json +32 -0
- package/dist/constraint-runtime/reference_data/subsystems/skills.json +27 -0
- package/dist/constraint-runtime/reference_data/subsystems/state.json +13 -0
- package/dist/constraint-runtime/reference_data/subsystems/types.json +18 -0
- package/dist/constraint-runtime/reference_data/subsystems/upstreamproxy.json +9 -0
- package/dist/constraint-runtime/reference_data/subsystems/utils.json +32 -0
- package/dist/constraint-runtime/reference_data/subsystems/vim.json +12 -0
- package/dist/constraint-runtime/reference_data/subsystems/voice.json +8 -0
- package/dist/constraint-runtime/reference_data/tools_snapshot.json +922 -0
- package/dist/context-compaction/auto-compact.js +7 -7
- package/dist/electron/config.js.map +1 -0
- package/dist/electron/dialogs.js.map +1 -0
- package/dist/electron/first-run.js +54 -54
- package/dist/electron/first-run.js.map +1 -0
- package/dist/electron/ipc.js.map +1 -0
- package/dist/electron/logger.js.map +1 -0
- package/dist/electron/main.js.map +1 -0
- package/dist/electron/menu.js.map +1 -0
- package/dist/electron/paths.js.map +1 -0
- package/dist/electron/server.js.map +1 -0
- package/dist/electron/tray.js.map +1 -0
- package/dist/electron/window.js.map +1 -0
- 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 +112 -112
- 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 +59 -0
- 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/network/p2p.js +78 -4
- 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/index.js +1 -1
- 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/utils/auto-update.js.map +1 -0
- package/dist/web/a2ui-client.js +1594 -223
- package/dist/web/api-config.html +779 -779
- package/dist/web/client-hearth.js +10 -10
- package/dist/web/client.js.map +7 -0
- package/dist/web/components/wallet-viem.mjs +118 -118
- package/dist/web/index.html +502 -502
- package/dist/web/manifest.json +20 -20
- package/dist/web/mobile-agent.js +397 -0
- package/dist/web/mobile-core.js +86235 -0
- package/dist/web/mobile-data.js +346 -0
- package/dist/web/mobile-http-api.js +71 -0
- package/dist/web/mobile-p2p.js +250 -0
- package/dist/web/mobile-payments.js +80 -0
- package/dist/web/mobile-wallet.js +226 -0
- package/dist/web/mobile.css +483 -6
- package/dist/web/mobile.html +99 -50
- package/dist/web/mobile.js +1040 -187
- package/dist/web/server.js +13 -13
- package/dist/web/style.css +4946 -4946
- package/package.json +201 -198
- package/scripts/build-cli.js +215 -215
- package/scripts/build-web.ts +162 -149
- package/scripts/postinstall.js +152 -152
- package/bin/bolloon.js +0 -157
- package/dist/agents/python-exec.js +0 -115
- package/dist/pi-ecosystem-colony/index.js +0 -365
- package/dist/social/ant-colony/AdaptiveHeartbeat.js +0 -101
- package/dist/social/ant-colony/PheromoneEngine.js +0 -227
- package/dist/social/ant-colony/index.js +0 -6
- package/dist/social/ant-colony/types.js +0 -24
- package/dist/storage/trajectory.js +0 -101
- package/dist/test/ai-judgment-test.js +0 -80
- package/dist/test/bollharness-integration.test.js +0 -318
- package/dist/test/channel-agent-multi-dialogue.js +0 -205
- package/dist/test/channel-heartbeat-agent-test.js +0 -201
- package/dist/test/constraint-layer.test.js +0 -164
- package/dist/test/diap-identity-test.js +0 -172
- package/dist/test/diap-quick-test.js +0 -62
- package/dist/test/global-shared-context.test.js +0 -315
- package/dist/test/harness-judgment-injection.test.js +0 -246
- package/dist/test/harness-workflow-integrator-test.js +0 -228
- package/dist/test/human-value-store.test.js +0 -243
- package/dist/test/hybrid-integration-test.js +0 -118
- package/dist/test/hybrid-messenger-verify.js +0 -55
- package/dist/test/iroh-bistream-debug.js +0 -38
- package/dist/test/iroh-communication.test.js +0 -66
- package/dist/test/iroh-debug-test.js +0 -57
- package/dist/test/iroh-diap-test.js +0 -71
- package/dist/test/iroh-direct-connect.js +0 -55
- package/dist/test/iroh-e2e-fixed.js +0 -89
- package/dist/test/iroh-e2e-same-process.js +0 -63
- package/dist/test/iroh-e2e.js +0 -66
- package/dist/test/iroh-final-e2e.js +0 -72
- package/dist/test/iroh-relay-test.js +0 -37
- package/dist/test/iroh-simple-test.js +0 -41
- package/dist/test/iroh-transport-verify.js +0 -54
- package/dist/test/iroh-transport.test.js +0 -37
- package/dist/test/iroh-two-nodes.js +0 -70
- package/dist/test/iroh-verify.js +0 -44
- package/dist/test/judgment-decision.test.js +0 -219
- package/dist/test/llm-judgment-integration.test.js +0 -220
- package/dist/test/p2p-agent-complex-dialogue.js +0 -385
- package/dist/test/p2p-agent-dialogue.js +0 -341
- package/dist/test/p2p-agent-full-bidirectional.js +0 -510
- package/dist/test/p2p-agent-harness-flow.js +0 -437
- package/dist/test/p2p-agent-harness-single.js +0 -143
- package/dist/test/p2p-ai-dialogue-test.js +0 -318
- package/dist/test/p2p-cid-connect-test.js +0 -195
- package/dist/test/p2p-connect-receiver.js +0 -69
- package/dist/test/p2p-doc-transfer.js +0 -110
- package/dist/test/p2p-identity-page-test.js +0 -77
- package/dist/test/p2p-iroh-test.js +0 -171
- package/dist/test/p2p-minimal-test.js +0 -241
- package/dist/test/p2p-node-1.js +0 -148
- package/dist/test/p2p-node-2.js +0 -148
- package/dist/test/p2p-server.js +0 -281
- package/dist/test/p2p-two-nodes-test.js +0 -438
- package/dist/test/pi-sdk.test.js +0 -44
- package/dist/test/set-persona.js +0 -40
- package/dist/test/simple.test.js +0 -9
- package/dist/test/storage-integration.test.js +0 -150
- package/dist/test/subagent-manager.test.js +0 -276
- package/dist/test/test-gate-flow.test.js +0 -81
- package/dist/test/workflow-engine.test.js +0 -87
- package/dist/test/workflow-pivot-loop.test.js +0 -246
package/bin/bolloon.js
DELETED
|
@@ -1,157 +0,0 @@
|
|
|
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
|
-
});
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* python-exec.ts — Python 代码执行引擎 (2026-08-12, TaskP)
|
|
3
|
-
*
|
|
4
|
-
* 借鉴 hermes code_execution_tool: 让 agent 输入 Python 代码运行操作, 而非逐条 shell 命令.
|
|
5
|
-
* 设计:
|
|
6
|
-
* - 把 LLM 给的 Python 代码写到临时文件, 用 python3 子进程执行 (隔离, 不污染主进程)
|
|
7
|
-
* - 带超时 + kill (子进程跑太久强制终止)
|
|
8
|
-
* - stdout/stderr 合并捕获 + 截断 (head+tail, 显式 truncation 标记 — hermes 模式)
|
|
9
|
-
* - 依赖检测: 找不到 python3 返回明确错误
|
|
10
|
-
*
|
|
11
|
-
* 安全: 不 eval (子进程隔离); 代码里若含高危 shell (os.system("rm -rf /") 等) 走 checkTerminalCommand 预检 (最佳努力).
|
|
12
|
-
*/
|
|
13
|
-
import { spawn } from 'child_process';
|
|
14
|
-
import * as fs from 'fs/promises';
|
|
15
|
-
import * as os from 'os';
|
|
16
|
-
import * as path from 'path';
|
|
17
|
-
const MAX_OUTPUT = 16000; // 捕获输出上限 (head+tail)
|
|
18
|
-
/** 探测可用的 python 解释器 (python3 / python) */
|
|
19
|
-
async function detectPython() {
|
|
20
|
-
const candidates = process.platform === 'win32' ? ['python', 'py'] : ['python3', 'python'];
|
|
21
|
-
for (const c of candidates) {
|
|
22
|
-
try {
|
|
23
|
-
await new Promise((resolve, reject) => {
|
|
24
|
-
const p = spawn(c, ['--version'], { stdio: 'ignore', windowsHide: true });
|
|
25
|
-
p.on('close', (code) => (code === 0 ? resolve() : reject(new Error('exit ' + code))));
|
|
26
|
-
p.on('error', reject);
|
|
27
|
-
});
|
|
28
|
-
return c;
|
|
29
|
-
}
|
|
30
|
-
catch { /* 下一个 */ }
|
|
31
|
-
}
|
|
32
|
-
return null;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* 截断输出: 保留 head + tail, 中间省略, 返回截断标记 (hermes 模式).
|
|
36
|
-
*/
|
|
37
|
-
export function truncateOutput(text) {
|
|
38
|
-
if (text.length <= MAX_OUTPUT)
|
|
39
|
-
return { text, truncated: false };
|
|
40
|
-
const head = text.slice(0, 4000);
|
|
41
|
-
const tail = text.slice(-4000);
|
|
42
|
-
return { text: `${head}\n…[输出截断 ${text.length - 8000} 字节]…\n${tail}`, truncated: true };
|
|
43
|
-
}
|
|
44
|
-
/** 最佳努力: 代码里明显的高危 shell 调用预检 (防 os.system("rm -rf /") 等) */
|
|
45
|
-
function checkDangerousCode(code) {
|
|
46
|
-
const dangerous = [
|
|
47
|
-
{ re: /os\.system\s*\(\s*["']\s*rm\s+(-[a-z]*f[a-z]*\s+)?-[a-z]*r[a-z]*\s+\//, reason: '禁止递归删除根目录' },
|
|
48
|
-
{ re: /shutil\.rmtree\s*\(\s*["']\s*\//, reason: '禁止删除根目录' },
|
|
49
|
-
{ re: /os\.system\s*\(\s*["']\s*(sudo|mkfs|dd\s+if=.*of=\/dev)/, reason: '禁止高危系统命令' },
|
|
50
|
-
];
|
|
51
|
-
for (const { re, reason } of dangerous) {
|
|
52
|
-
if (re.test(code))
|
|
53
|
-
return { allowed: false, reason };
|
|
54
|
-
}
|
|
55
|
-
return { allowed: true };
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* 执行一段 Python 代码 (隔离子进程 + 超时 + 输出截断).
|
|
59
|
-
*/
|
|
60
|
-
export async function executePython(opts) {
|
|
61
|
-
const code = String(opts.code ?? '').trim();
|
|
62
|
-
if (!code)
|
|
63
|
-
return { success: false, error: 'code 必填', output: '' };
|
|
64
|
-
// 高危代码预检 (最佳努力)
|
|
65
|
-
const d = checkDangerousCode(code);
|
|
66
|
-
if (!d.allowed)
|
|
67
|
-
return { success: false, error: `[python-guard] ${d.reason}`, deniedByGuard: true, output: '' };
|
|
68
|
-
// 探测 python
|
|
69
|
-
const python = await detectPython();
|
|
70
|
-
if (!python) {
|
|
71
|
-
return { success: false, error: '未找到 python3/python 解释器, 无法执行 Python 代码', output: '' };
|
|
72
|
-
}
|
|
73
|
-
// 写临时文件
|
|
74
|
-
const tmpFile = path.join(os.tmpdir(), `bolloon-py-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.py`);
|
|
75
|
-
await fs.writeFile(tmpFile, code, 'utf-8');
|
|
76
|
-
const timeoutMs = opts.timeoutMs ?? 30000;
|
|
77
|
-
try {
|
|
78
|
-
const result = await new Promise((resolve) => {
|
|
79
|
-
const proc = spawn(python, [tmpFile], {
|
|
80
|
-
cwd: opts.cwd ?? process.cwd(),
|
|
81
|
-
env: { ...process.env, GIT_TERMINAL_PROMPT: '0', PYTHONIOENCODING: 'utf-8' },
|
|
82
|
-
windowsHide: true,
|
|
83
|
-
});
|
|
84
|
-
let stdout = '';
|
|
85
|
-
let stderr = '';
|
|
86
|
-
const timer = setTimeout(() => {
|
|
87
|
-
try {
|
|
88
|
-
proc.kill('SIGKILL');
|
|
89
|
-
}
|
|
90
|
-
catch { /* 忽略 */ }
|
|
91
|
-
resolve({ success: false, output: '', error: `Python 执行超时 (>${timeoutMs}ms), 已终止`, exitCode: -1, python });
|
|
92
|
-
}, timeoutMs);
|
|
93
|
-
proc.stdout.on('data', (d) => { stdout += d.toString(); });
|
|
94
|
-
proc.stderr.on('data', (d) => { stderr += d.toString(); });
|
|
95
|
-
proc.on('error', (e) => { clearTimeout(timer); resolve({ success: false, output: '', error: `启动失败: ${e.message}`, python }); });
|
|
96
|
-
proc.on('close', (code) => {
|
|
97
|
-
clearTimeout(timer);
|
|
98
|
-
const raw = (stdout + (stderr ? `\n[stderr]\n${stderr}` : '')).trim();
|
|
99
|
-
const { text, truncated } = truncateOutput(raw || '(无输出)');
|
|
100
|
-
resolve({
|
|
101
|
-
success: code === 0,
|
|
102
|
-
output: text,
|
|
103
|
-
stdoutTruncated: truncated,
|
|
104
|
-
exitCode: code,
|
|
105
|
-
error: code === 0 ? undefined : `exit code ${code}`,
|
|
106
|
-
python,
|
|
107
|
-
});
|
|
108
|
-
});
|
|
109
|
-
});
|
|
110
|
-
return result;
|
|
111
|
-
}
|
|
112
|
-
finally {
|
|
113
|
-
await fs.rm(tmpFile, { force: true }).catch(() => { });
|
|
114
|
-
}
|
|
115
|
-
}
|
|
@@ -1,365 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pi Ant Colony Integration for Bolloon
|
|
3
|
-
*
|
|
4
|
-
* Multi-agent collaboration system with white-box signal protocol.
|
|
5
|
-
* Based on oh-pi's ant colony system: COLONY_SIGNAL protocol for agent coordination.
|
|
6
|
-
*
|
|
7
|
-
* Signal Protocol:
|
|
8
|
-
* COLONY_SIGNAL:LAUNCHED → SCOUTING → WORKING → REVIEWING → COMPLETE
|
|
9
|
-
*
|
|
10
|
-
* Key features:
|
|
11
|
-
* - Fully visible signal protocol (no black boxes)
|
|
12
|
-
* - Per-ant turn limits, cost tracking, concurrent scheduling
|
|
13
|
-
* - White-box design: no "what is it doing in the background" anxiety
|
|
14
|
-
*/
|
|
15
|
-
import { EventEmitter } from 'events';
|
|
16
|
-
import * as fs from 'fs/promises';
|
|
17
|
-
import * as path from 'path';
|
|
18
|
-
// Colony state
|
|
19
|
-
const ants = new Map();
|
|
20
|
-
const tasks = new Map();
|
|
21
|
-
const signalHistory = [];
|
|
22
|
-
const STATE_DIR = path.join(process.env.HOME || '/tmp', '.bolloon', 'colony');
|
|
23
|
-
// Event emitter
|
|
24
|
-
class ColonyEventEmitter extends EventEmitter {
|
|
25
|
-
}
|
|
26
|
-
const colonyEvents = new ColonyEventEmitter();
|
|
27
|
-
/**
|
|
28
|
-
* Generate a unique ant ID
|
|
29
|
-
*/
|
|
30
|
-
function generateAntId() {
|
|
31
|
-
return `ant-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Register a new ant in the colony
|
|
35
|
-
*/
|
|
36
|
-
export function registerAnt(name, role, maxTurns = 10, tokenBudget = 50000) {
|
|
37
|
-
const id = generateAntId();
|
|
38
|
-
const ant = {
|
|
39
|
-
id,
|
|
40
|
-
name,
|
|
41
|
-
role,
|
|
42
|
-
signal: 'LAUNCHED',
|
|
43
|
-
turnCount: 0,
|
|
44
|
-
maxTurns,
|
|
45
|
-
tokenBudget,
|
|
46
|
-
tokensUsed: 0,
|
|
47
|
-
createdAt: new Date().toISOString(),
|
|
48
|
-
updatedAt: new Date().toISOString(),
|
|
49
|
-
};
|
|
50
|
-
ants.set(id, ant);
|
|
51
|
-
emitSignal(id, 'LAUNCHED');
|
|
52
|
-
console.log(`[Colony] Registered ant: ${name} (${role})`);
|
|
53
|
-
return ant;
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Emit a signal transition
|
|
57
|
-
*/
|
|
58
|
-
function emitSignal(antId, newSignal, details) {
|
|
59
|
-
const ant = ants.get(antId);
|
|
60
|
-
if (!ant)
|
|
61
|
-
return;
|
|
62
|
-
const event = {
|
|
63
|
-
antId,
|
|
64
|
-
antName: ant.name,
|
|
65
|
-
fromSignal: ant.signal,
|
|
66
|
-
toSignal: newSignal,
|
|
67
|
-
timestamp: new Date().toISOString(),
|
|
68
|
-
details,
|
|
69
|
-
};
|
|
70
|
-
signalHistory.push(event);
|
|
71
|
-
if (signalHistory.length > 100) {
|
|
72
|
-
signalHistory.shift();
|
|
73
|
-
}
|
|
74
|
-
ant.signal = newSignal;
|
|
75
|
-
ant.updatedAt = new Date().toISOString();
|
|
76
|
-
ants.set(antId, ant);
|
|
77
|
-
colonyEvents.emit('signal', event);
|
|
78
|
-
console.log(`[Colony] ${ant.name}: ${event.fromSignal} → ${event.toSignal}${details ? ` (${details})` : ''}`);
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Transition ant to SCOUTING
|
|
82
|
-
*/
|
|
83
|
-
export function antScouting(antId, task) {
|
|
84
|
-
const ant = ants.get(antId);
|
|
85
|
-
if (!ant)
|
|
86
|
-
return;
|
|
87
|
-
ant.task = task;
|
|
88
|
-
ants.set(antId, ant);
|
|
89
|
-
emitSignal(antId, 'SCOUTING', `Task: ${task.substring(0, 50)}...`);
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* Transition ant to WORKING
|
|
93
|
-
*/
|
|
94
|
-
export function antWorking(antId) {
|
|
95
|
-
const ant = ants.get(antId);
|
|
96
|
-
if (!ant)
|
|
97
|
-
return;
|
|
98
|
-
emitSignal(antId, 'WORKING');
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* Transition ant to REVIEWING
|
|
102
|
-
*/
|
|
103
|
-
export function antReviewing(antId) {
|
|
104
|
-
emitSignal(antId, 'REVIEWING');
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* Complete ant work
|
|
108
|
-
*/
|
|
109
|
-
export function antComplete(antId, result) {
|
|
110
|
-
const ant = ants.get(antId);
|
|
111
|
-
if (!ant)
|
|
112
|
-
return;
|
|
113
|
-
ant.result = result;
|
|
114
|
-
ants.set(antId, ant);
|
|
115
|
-
emitSignal(antId, 'COMPLETE', `Result: ${result.substring(0, 50)}...`);
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Fail ant work
|
|
119
|
-
*/
|
|
120
|
-
export function antFail(antId, error) {
|
|
121
|
-
const ant = ants.get(antId);
|
|
122
|
-
if (!ant)
|
|
123
|
-
return;
|
|
124
|
-
ant.result = error;
|
|
125
|
-
ants.set(antId, ant);
|
|
126
|
-
emitSignal(antId, 'FAILED', error);
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
* Abort ant work
|
|
130
|
-
*/
|
|
131
|
-
export function antAbort(antId, reason) {
|
|
132
|
-
emitSignal(antId, 'ABORTED', reason);
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
|
-
* Increment turn count
|
|
136
|
-
*/
|
|
137
|
-
export function antTick(antId, tokensUsed = 0) {
|
|
138
|
-
const ant = ants.get(antId);
|
|
139
|
-
if (!ant)
|
|
140
|
-
return;
|
|
141
|
-
ant.turnCount++;
|
|
142
|
-
ant.tokensUsed += tokensUsed;
|
|
143
|
-
ant.updatedAt = new Date().toISOString();
|
|
144
|
-
ants.set(antId, ant);
|
|
145
|
-
// Check if exceeded limits
|
|
146
|
-
if (ant.turnCount >= ant.maxTurns) {
|
|
147
|
-
antAbort(antId, 'Max turns exceeded');
|
|
148
|
-
}
|
|
149
|
-
else if (ant.tokensUsed >= ant.tokenBudget) {
|
|
150
|
-
antAbort(antId, 'Token budget exceeded');
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
/**
|
|
154
|
-
* Create a colony task
|
|
155
|
-
*/
|
|
156
|
-
export function createTask(description) {
|
|
157
|
-
const id = `task-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
|
|
158
|
-
const task = {
|
|
159
|
-
id,
|
|
160
|
-
description,
|
|
161
|
-
status: 'pending',
|
|
162
|
-
assignedAnts: [],
|
|
163
|
-
results: new Map(),
|
|
164
|
-
createdAt: new Date().toISOString(),
|
|
165
|
-
};
|
|
166
|
-
tasks.set(id, task);
|
|
167
|
-
console.log(`[Colony] Created task: ${id} - ${description.substring(0, 50)}...`);
|
|
168
|
-
return task;
|
|
169
|
-
}
|
|
170
|
-
/**
|
|
171
|
-
* Dispatch task to ants
|
|
172
|
-
*/
|
|
173
|
-
export function dispatchTask(taskId, antIds) {
|
|
174
|
-
const task = tasks.get(taskId);
|
|
175
|
-
if (!task)
|
|
176
|
-
return;
|
|
177
|
-
task.status = 'dispatched';
|
|
178
|
-
task.assignedAnts = antIds;
|
|
179
|
-
tasks.set(taskId, task);
|
|
180
|
-
for (const antId of antIds) {
|
|
181
|
-
const ant = ants.get(antId);
|
|
182
|
-
if (ant) {
|
|
183
|
-
antScouting(antId, task.description);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
console.log(`[Colony] Dispatched task ${taskId} to ${antIds.length} ants`);
|
|
187
|
-
}
|
|
188
|
-
/**
|
|
189
|
-
* Record ant result for a task
|
|
190
|
-
*/
|
|
191
|
-
export function recordResult(taskId, antId, result) {
|
|
192
|
-
const task = tasks.get(taskId);
|
|
193
|
-
if (!task)
|
|
194
|
-
return;
|
|
195
|
-
task.results.set(antId, result);
|
|
196
|
-
// Check if all ants completed
|
|
197
|
-
if (task.results.size === task.assignedAnts.length) {
|
|
198
|
-
task.status = 'completed';
|
|
199
|
-
task.completedAt = new Date().toISOString();
|
|
200
|
-
for (const id of task.assignedAnts) {
|
|
201
|
-
antReviewing(id);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
tasks.set(taskId, task);
|
|
205
|
-
}
|
|
206
|
-
/**
|
|
207
|
-
* Get ant by ID
|
|
208
|
-
*/
|
|
209
|
-
export function getAnt(antId) {
|
|
210
|
-
return ants.get(antId);
|
|
211
|
-
}
|
|
212
|
-
/**
|
|
213
|
-
* List all ants
|
|
214
|
-
*/
|
|
215
|
-
export function listAnts() {
|
|
216
|
-
return Array.from(ants.values());
|
|
217
|
-
}
|
|
218
|
-
/**
|
|
219
|
-
* List ants by role
|
|
220
|
-
*/
|
|
221
|
-
export function listAntsByRole(role) {
|
|
222
|
-
return Array.from(ants.values()).filter((a) => a.role === role);
|
|
223
|
-
}
|
|
224
|
-
/**
|
|
225
|
-
* Get ants by signal state
|
|
226
|
-
*/
|
|
227
|
-
export function listAntsBySignal(signal) {
|
|
228
|
-
return Array.from(ants.values()).filter((a) => a.signal === signal);
|
|
229
|
-
}
|
|
230
|
-
/**
|
|
231
|
-
* Get active ants (not complete/failed/aborted)
|
|
232
|
-
*/
|
|
233
|
-
export function getActiveAnts() {
|
|
234
|
-
return Array.from(ants.values()).filter((a) => !['COMPLETE', 'FAILED', 'ABORTED'].includes(a.signal));
|
|
235
|
-
}
|
|
236
|
-
/**
|
|
237
|
-
* Get task by ID
|
|
238
|
-
*/
|
|
239
|
-
export function getTask(taskId) {
|
|
240
|
-
return tasks.get(taskId);
|
|
241
|
-
}
|
|
242
|
-
/**
|
|
243
|
-
* List all tasks
|
|
244
|
-
*/
|
|
245
|
-
export function listTasks() {
|
|
246
|
-
return Array.from(tasks.values());
|
|
247
|
-
}
|
|
248
|
-
/**
|
|
249
|
-
* Get signal history
|
|
250
|
-
*/
|
|
251
|
-
export function getSignalHistory() {
|
|
252
|
-
return [...signalHistory];
|
|
253
|
-
}
|
|
254
|
-
/**
|
|
255
|
-
* Get colony status
|
|
256
|
-
*/
|
|
257
|
-
export function getColonyStatus() {
|
|
258
|
-
const antList = Array.from(ants.values());
|
|
259
|
-
const taskList = Array.from(tasks.values());
|
|
260
|
-
const signalDistribution = {
|
|
261
|
-
LAUNCHED: 0,
|
|
262
|
-
SCOUTING: 0,
|
|
263
|
-
WORKING: 0,
|
|
264
|
-
REVIEWING: 0,
|
|
265
|
-
COMPLETE: 0,
|
|
266
|
-
FAILED: 0,
|
|
267
|
-
ABORTED: 0,
|
|
268
|
-
};
|
|
269
|
-
for (const ant of antList) {
|
|
270
|
-
signalDistribution[ant.signal]++;
|
|
271
|
-
}
|
|
272
|
-
return {
|
|
273
|
-
antCount: antList.length,
|
|
274
|
-
activeAnts: antList.filter((a) => !['COMPLETE', 'FAILED', 'ABORTED'].includes(a.signal)).length,
|
|
275
|
-
taskCount: taskList.length,
|
|
276
|
-
activeTasks: taskList.filter((t) => t.status === 'in_progress' || t.status === 'dispatched').length,
|
|
277
|
-
signalDistribution,
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
|
-
/**
|
|
281
|
-
* Subscribe to colony events
|
|
282
|
-
*/
|
|
283
|
-
export function onColonyEvent(event, callback) {
|
|
284
|
-
colonyEvents.on(event, callback);
|
|
285
|
-
}
|
|
286
|
-
/**
|
|
287
|
-
* Unsubscribe from colony events
|
|
288
|
-
*/
|
|
289
|
-
export function offColonyEvent(event, callback) {
|
|
290
|
-
colonyEvents.off(event, callback);
|
|
291
|
-
}
|
|
292
|
-
/**
|
|
293
|
-
* Get formatted colony dump for debugging
|
|
294
|
-
*/
|
|
295
|
-
export function getColonyDump() {
|
|
296
|
-
const status = getColonyStatus();
|
|
297
|
-
const active = getActiveAnts();
|
|
298
|
-
let dump = '# Colony Status\n\n';
|
|
299
|
-
dump += `Ants: ${status.antCount} total, ${status.activeAnts} active\n`;
|
|
300
|
-
dump += `Tasks: ${status.taskCount} total, ${status.activeTasks} active\n\n`;
|
|
301
|
-
dump += '## Signal Distribution\n';
|
|
302
|
-
for (const [signal, count] of Object.entries(status.signalDistribution)) {
|
|
303
|
-
if (count > 0) {
|
|
304
|
-
dump += `- ${signal}: ${count}\n`;
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
dump += '\n## Active Ants\n';
|
|
308
|
-
for (const ant of active) {
|
|
309
|
-
dump += `- ${ant.name} (${ant.role}): ${ant.signal} [${ant.turnCount}/${ant.maxTurns} turns, ${ant.tokensUsed}/${ant.tokenBudget} tokens]\n`;
|
|
310
|
-
if (ant.task) {
|
|
311
|
-
dump += ` Task: ${ant.task.substring(0, 60)}...\n`;
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
dump += '\n## Recent Signals\n';
|
|
315
|
-
const recent = signalHistory.slice(-10);
|
|
316
|
-
for (const event of recent.reverse()) {
|
|
317
|
-
dump += `- ${event.timestamp}: ${event.antName}: ${event.fromSignal} → ${event.toSignal}\n`;
|
|
318
|
-
}
|
|
319
|
-
return dump;
|
|
320
|
-
}
|
|
321
|
-
/**
|
|
322
|
-
* Persist colony state
|
|
323
|
-
*/
|
|
324
|
-
export async function persistColony() {
|
|
325
|
-
try {
|
|
326
|
-
await fs.mkdir(STATE_DIR, { recursive: true });
|
|
327
|
-
const state = {
|
|
328
|
-
ants: Array.from(ants.values()),
|
|
329
|
-
tasks: Array.from(tasks.entries()).map(([id, t]) => ({
|
|
330
|
-
...t,
|
|
331
|
-
results: Array.from(t.results.entries()),
|
|
332
|
-
})),
|
|
333
|
-
signalHistory: signalHistory.slice(-50),
|
|
334
|
-
persistedAt: new Date().toISOString(),
|
|
335
|
-
};
|
|
336
|
-
await fs.writeFile(path.join(STATE_DIR, 'colony.json'), JSON.stringify(state, null, 2));
|
|
337
|
-
console.log('[Colony] Persisted state');
|
|
338
|
-
}
|
|
339
|
-
catch (e) {
|
|
340
|
-
console.warn('[Colony] Failed to persist:', e);
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
/**
|
|
344
|
-
* Load colony state
|
|
345
|
-
*/
|
|
346
|
-
export async function loadColony() {
|
|
347
|
-
try {
|
|
348
|
-
const data = await fs.readFile(path.join(STATE_DIR, 'colony.json'), 'utf-8');
|
|
349
|
-
const state = JSON.parse(data);
|
|
350
|
-
ants.clear();
|
|
351
|
-
for (const a of state.ants) {
|
|
352
|
-
ants.set(a.id, a);
|
|
353
|
-
}
|
|
354
|
-
tasks.clear();
|
|
355
|
-
for (const t of state.tasks) {
|
|
356
|
-
tasks.set(t.id, { ...t, results: new Map(t.results) });
|
|
357
|
-
}
|
|
358
|
-
signalHistory.length = 0;
|
|
359
|
-
signalHistory.push(...state.signalHistory);
|
|
360
|
-
console.log(`[Colony] Loaded ${ants.size} ants, ${tasks.size} tasks`);
|
|
361
|
-
}
|
|
362
|
-
catch {
|
|
363
|
-
// No persisted state
|
|
364
|
-
}
|
|
365
|
-
}
|