@bolloon/bolloon-agent 0.2.8 → 0.2.10
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 +467 -467
- package/bin/bolloon-cli.cjs +183 -183
- package/bin/bolloon.cjs +0 -0
- package/bin/bolloon.js +157 -0
- package/dist/agents/agent-manifest-protocol.js +0 -52
- package/dist/agents/constraint-layer.js +19 -19
- package/dist/agents/pi-sdk.js +287 -1213
- package/dist/agents/shell-guard.js +2 -2
- package/dist/agents/workflow-pivot-loop.js +2 -81
- 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 -26
- package/dist/bollharness-integration/integration.js +0 -71
- package/dist/bollharness-integration/llm-judgment-engine.js +134 -134
- package/dist/bollharness-integration/skill-adapter.js +127 -52
- package/dist/bootstrap/context-collector.js +3 -6
- package/dist/bootstrap/lifecycle-hooks.js +1 -15
- package/dist/cli-entry.js +35 -39
- package/dist/constraint-runtime/src/_archive_helper.js +9 -0
- package/dist/constraint-runtime/src/agent/coordinator.js +48 -0
- package/dist/constraint-runtime/src/agent/index.js +1 -0
- package/dist/constraint-runtime/src/assistant/index.js +12 -0
- package/dist/constraint-runtime/src/bootstrap/index.js +12 -0
- package/dist/constraint-runtime/src/bootstrap_graph.js +13 -0
- package/dist/constraint-runtime/src/bridge/index.js +12 -0
- package/dist/constraint-runtime/src/buddy/index.js +12 -0
- package/dist/constraint-runtime/src/cli/index.js +12 -0
- package/dist/constraint-runtime/src/command_graph.js +10 -0
- package/dist/constraint-runtime/src/commands.js +60 -0
- package/dist/constraint-runtime/src/components/index.js +12 -0
- package/dist/constraint-runtime/src/constants/index.js +12 -0
- package/dist/constraint-runtime/src/constraint/budget.js +22 -0
- package/dist/constraint-runtime/src/constraint/index.js +2 -0
- package/dist/constraint-runtime/src/constraint/permission.js +20 -0
- package/dist/constraint-runtime/src/context.js +30 -0
- package/dist/constraint-runtime/src/coordinator/index.js +12 -0
- package/dist/constraint-runtime/src/cost_hook.js +4 -0
- package/dist/constraint-runtime/src/cost_tracker.js +8 -0
- package/dist/constraint-runtime/src/deferred_init.js +10 -0
- package/dist/constraint-runtime/src/direct_modes.js +6 -0
- package/dist/constraint-runtime/src/dynamic-tool-loader.js +85 -0
- package/dist/constraint-runtime/src/entrypoints/index.js +12 -0
- package/dist/constraint-runtime/src/execution_registry.js +44 -0
- package/dist/constraint-runtime/src/history.js +9 -0
- package/dist/constraint-runtime/src/hooks/index.js +12 -0
- package/dist/constraint-runtime/src/index.js +26 -0
- package/dist/constraint-runtime/src/ink.js +4 -0
- package/dist/constraint-runtime/src/keybindings/index.js +12 -0
- package/dist/constraint-runtime/src/memdir/index.js +12 -0
- package/dist/constraint-runtime/src/migrations/index.js +12 -0
- package/dist/constraint-runtime/src/models.js +1 -0
- package/dist/constraint-runtime/src/moreright/index.js +12 -0
- package/dist/constraint-runtime/src/native_ts/index.js +12 -0
- package/dist/constraint-runtime/src/output_styles/index.js +12 -0
- package/dist/constraint-runtime/src/parity_audit.js +12 -0
- package/dist/constraint-runtime/src/plugins/index.js +12 -0
- package/dist/constraint-runtime/src/port_manifest.js +11 -0
- package/dist/constraint-runtime/src/prefetch.js +9 -0
- package/dist/constraint-runtime/src/query.js +1 -0
- package/dist/constraint-runtime/src/remote/index.js +12 -0
- package/dist/constraint-runtime/src/remote_runtime.js +9 -0
- package/dist/constraint-runtime/src/runtime/index.js +1 -0
- package/dist/constraint-runtime/src/runtime/session.js +35 -0
- package/dist/constraint-runtime/src/schemas/index.js +12 -0
- package/dist/constraint-runtime/src/screens/index.js +12 -0
- package/dist/constraint-runtime/src/server/index.js +12 -0
- package/dist/constraint-runtime/src/services/index.js +12 -0
- package/dist/constraint-runtime/src/session_store.js +22 -0
- package/dist/constraint-runtime/src/setup.js +30 -0
- package/dist/constraint-runtime/src/skills/index.js +1 -0
- package/dist/constraint-runtime/src/skills/skill-registry.js +28 -0
- package/dist/constraint-runtime/src/state/index.js +12 -0
- package/dist/constraint-runtime/src/system_init.js +20 -0
- package/dist/constraint-runtime/src/thinking/engine.js +42 -0
- package/dist/constraint-runtime/src/thinking/index.js +1 -0
- package/dist/constraint-runtime/src/tool_pool.js +8 -0
- package/dist/constraint-runtime/src/tools/OpenCLI/execAdapter.js +7 -0
- package/dist/constraint-runtime/src/tools/OpenCLI/listAdapters.js +7 -0
- package/dist/constraint-runtime/src/tools/OpenCLI/runCommand.js +7 -0
- package/dist/constraint-runtime/src/tools/SafeSDK/confirmTransaction.js +6 -0
- package/dist/constraint-runtime/src/tools/SafeSDK/createTransaction.js +8 -0
- package/dist/constraint-runtime/src/tools/SafeSDK/executeTransaction.js +6 -0
- package/dist/constraint-runtime/src/tools/SafeSDK/getBalance.js +6 -0
- package/dist/constraint-runtime/src/tools/SafeSDK/getPendingTransactions.js +6 -0
- package/dist/constraint-runtime/src/tools/SafeSDK/proposeTransaction.js +6 -0
- package/dist/constraint-runtime/src/tools.js +80 -0
- package/dist/constraint-runtime/src/transcript.js +19 -0
- package/dist/constraint-runtime/src/types/index.js +12 -0
- package/dist/constraint-runtime/src/upstream_proxy/index.js +12 -0
- package/dist/constraint-runtime/src/utils/index.js +12 -0
- package/dist/constraint-runtime/src/vim/index.js +12 -0
- package/dist/constraint-runtime/src/voice/index.js +12 -0
- package/dist/constraint-runtime/tests/agent.test.js +16 -0
- package/dist/constraint-runtime/tests/constraint.test.js +41 -0
- package/dist/constraint-runtime/tests/skill.test.js +19 -0
- package/dist/constraint-runtime/tests/thinking.test.js +22 -0
- package/dist/context-compaction/auto-compact.js +7 -7
- package/dist/electron-preload.js +1 -18
- package/dist/electron-preload.js.map +1 -1
- package/dist/electron.js +168 -3
- package/dist/electron.js.map +1 -1
- package/dist/index.js +116 -136
- package/dist/llm/config-store.js +8 -24
- package/dist/llm/llm-judgment-client.js +102 -102
- package/dist/llm/pi-ai.js +85 -126
- 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-visitor.md +19 -19
- 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/hibs_reminders.md +15 -15
- package/dist/llm/system-prompt/layers/core/identity.md +37 -37
- 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/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/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/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/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 +0 -11
- 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/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 +520 -520
- package/dist/web/client.js +2917 -3791
- package/dist/web/components/p2p/index.js +234 -276
- package/dist/web/components/wallet-viem.mjs +118 -118
- package/dist/web/index.html +374 -373
- package/dist/web/manifest.json +20 -20
- package/dist/web/server.js +23 -775
- package/dist/web/style.css +4737 -4737
- package/dist/web/ui/message-renderer.js +373 -451
- package/dist/web/ui/step-timeline.js +255 -351
- package/package.json +160 -157
- package/scripts/build-cli.js +216 -0
- package/scripts/build-web.ts +125 -0
- package/scripts/postinstall.js +153 -0
- package/bin/bolloon-daemon.sh +0 -207
- package/bin/ipfs +0 -0
- package/dist/agents/chat-segmenter.js +0 -298
- package/dist/agents/judgment-protocol.js +0 -479
- package/dist/agents/parse-tool-call.js +0 -304
- package/dist/agents/peer-manifest-loader.js +0 -210
- package/dist/agents/react-loop.js +0 -120
- package/dist/agents/session-store.js +0 -171
- package/dist/agents/tool-registry.js +0 -136
- package/dist/bootstrap/chat-archiver.js +0 -276
- package/dist/bootstrap/memory-compressor.js +0 -170
- package/dist/bootstrap/persona-loader.js +0 -94
- package/dist/electron/config.js +0 -16
- package/dist/electron/config.js.map +0 -1
- package/dist/electron/dialogs.js +0 -71
- package/dist/electron/dialogs.js.map +0 -1
- package/dist/electron/first-run.js +0 -129
- package/dist/electron/first-run.js.map +0 -1
- package/dist/electron/ipc.js +0 -16
- package/dist/electron/ipc.js.map +0 -1
- package/dist/electron/logger.js +0 -77
- package/dist/electron/logger.js.map +0 -1
- package/dist/electron/main.js +0 -60
- package/dist/electron/main.js.map +0 -1
- package/dist/electron/menu.js +0 -140
- package/dist/electron/menu.js.map +0 -1
- package/dist/electron/paths.js +0 -33
- package/dist/electron/paths.js.map +0 -1
- package/dist/electron/server.js +0 -74
- package/dist/electron/server.js.map +0 -1
- package/dist/electron/tray.js +0 -73
- package/dist/electron/tray.js.map +0 -1
- package/dist/electron/window.js +0 -69
- package/dist/electron/window.js.map +0 -1
- package/dist/network/local-inbox-bus.js +0 -73
- package/dist/network/p2p-outbox.js +0 -161
- package/dist/network/peer-fs.js +0 -420
- package/dist/network/peer-resource-bridge.js +0 -216
- package/dist/web/components/p2p/P2PModal.js +0 -188
- package/dist/web/components/p2p/p2p-modal.js +0 -664
- package/dist/web/components/p2p/p2p-tools.js +0 -248
- package/dist/web/input-validator.js +0 -103
- package/dist/web/util/safe-name.js +0 -32
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Postinstall 脚本
|
|
3
|
+
* npm 安装后自动执行
|
|
4
|
+
*
|
|
5
|
+
* 主要任务:
|
|
6
|
+
* 1. 确保 bin 目录存在且可执行
|
|
7
|
+
* 2. 初始化本地配置
|
|
8
|
+
* 3. 检查依赖完整性
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import * as fs from 'fs';
|
|
12
|
+
import * as path from 'path';
|
|
13
|
+
import { fileURLToPath } from 'url';
|
|
14
|
+
|
|
15
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
16
|
+
const __dirname = path.dirname(__filename);
|
|
17
|
+
const rootDir = path.join(__dirname, '..');
|
|
18
|
+
|
|
19
|
+
const RESET = '\x1b[0m';
|
|
20
|
+
const GREEN = '\x1b[32m';
|
|
21
|
+
const YELLOW = '\x1b[33m';
|
|
22
|
+
const CYAN = '\x1b[36m';
|
|
23
|
+
|
|
24
|
+
function log(msg, color = RESET) {
|
|
25
|
+
console.log(color + msg + RESET);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function initUserDirs() {
|
|
29
|
+
// 创建用户数据目录
|
|
30
|
+
const home = process.env.HOME || process.env.USERPROFILE || '/tmp';
|
|
31
|
+
const bolloonDir = path.join(home, '.bolloon');
|
|
32
|
+
|
|
33
|
+
const dirs = [
|
|
34
|
+
bolloonDir,
|
|
35
|
+
path.join(bolloonDir, 'sessions'),
|
|
36
|
+
path.join(bolloonDir, 'peer-store'),
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
for (const dir of dirs) {
|
|
40
|
+
if (!fs.existsSync(dir)) {
|
|
41
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
42
|
+
log(` ✓ 创建目录: ${dir}`, GREEN);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// 初始化配置文件
|
|
47
|
+
const configPath = path.join(bolloonDir, 'config.json');
|
|
48
|
+
if (!fs.existsSync(configPath)) {
|
|
49
|
+
const defaultConfig = {
|
|
50
|
+
version: '0.1.12',
|
|
51
|
+
initializedAt: new Date().toISOString(),
|
|
52
|
+
defaults: {
|
|
53
|
+
port: 54188,
|
|
54
|
+
theme: 'dark',
|
|
55
|
+
autoConnect: true,
|
|
56
|
+
},
|
|
57
|
+
providers: {
|
|
58
|
+
minimax: { enabled: false },
|
|
59
|
+
openai: { enabled: false },
|
|
60
|
+
anthropic: { enabled: false },
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
fs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2));
|
|
64
|
+
log(` ✓ 创建配置: ${configPath}`, GREEN);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return bolloonDir;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function checkNativeDeps() {
|
|
71
|
+
// 检查必要的原生依赖
|
|
72
|
+
const nativeDeps = [
|
|
73
|
+
'libp2p',
|
|
74
|
+
'@diap/sdk',
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
let allOk = true;
|
|
78
|
+
|
|
79
|
+
for (const dep of nativeDeps) {
|
|
80
|
+
const depPath = path.join(rootDir, 'node_modules', dep);
|
|
81
|
+
if (!fs.existsSync(depPath)) {
|
|
82
|
+
log(` ⚠ 缺少依赖: ${dep}`, YELLOW);
|
|
83
|
+
allOk = false;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return allOk;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function setupPlatform() {
|
|
91
|
+
const platform = process.platform;
|
|
92
|
+
|
|
93
|
+
log(`\n 平台: ${platform}`, CYAN);
|
|
94
|
+
|
|
95
|
+
if (platform === 'win32') {
|
|
96
|
+
// Windows: 确保 .cmd 文件存在
|
|
97
|
+
const binDir = path.join(rootDir, 'bin');
|
|
98
|
+
const cmdPath = path.join(binDir, 'bolloon.cmd');
|
|
99
|
+
|
|
100
|
+
if (fs.existsSync(binDir) && !fs.existsSync(cmdPath)) {
|
|
101
|
+
const cmdContent = `@echo off
|
|
102
|
+
node "%~dp0..\\dist\\cli.js" %*
|
|
103
|
+
`;
|
|
104
|
+
fs.writeFileSync(cmdPath, cmdContent);
|
|
105
|
+
log(' ✓ Windows 入口已创建', GREEN);
|
|
106
|
+
}
|
|
107
|
+
} else {
|
|
108
|
+
// Unix/Linux/Mac: 确保 bin 文件可执行
|
|
109
|
+
const binDir = path.join(rootDir, 'bin');
|
|
110
|
+
const binPath = path.join(binDir, 'bolloon.js');
|
|
111
|
+
|
|
112
|
+
if (fs.existsSync(binPath)) {
|
|
113
|
+
try {
|
|
114
|
+
fs.chmodSync(binPath, 0o755);
|
|
115
|
+
log(' ✓ bin/bolloon.js 已设为可执行', GREEN);
|
|
116
|
+
} catch (err) {
|
|
117
|
+
log(` ⚠ 无法设置执行权限: ${err.message}`, YELLOW);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function main() {
|
|
124
|
+
console.log('\n📦 Bolloon 安装后处理...\n');
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
// 1. 初始化用户目录
|
|
128
|
+
const bolloonDir = initUserDirs();
|
|
129
|
+
log(` ✓ 用户数据目录: ${bolloonDir}`, GREEN);
|
|
130
|
+
|
|
131
|
+
// 2. 检查依赖
|
|
132
|
+
const depsOk = checkNativeDeps();
|
|
133
|
+
if (!depsOk) {
|
|
134
|
+
log('\n ⚠ 部分依赖缺失,建议运行: npm install', YELLOW);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// 3. 平台特定设置
|
|
138
|
+
setupPlatform();
|
|
139
|
+
|
|
140
|
+
console.log('\n✅ 安装完成!\n');
|
|
141
|
+
console.log(' 使用方式:');
|
|
142
|
+
console.log(' bolloon # 启动 GUI');
|
|
143
|
+
console.log(' bolloon --web # 启动 Web UI');
|
|
144
|
+
console.log(' bolloon --cli # 命令行模式');
|
|
145
|
+
console.log(' bolloon --help # 显示帮助\n');
|
|
146
|
+
console.log(` 配置文件: ${path.join(bolloonDir, 'config.json')}\n`);
|
|
147
|
+
} catch (err) {
|
|
148
|
+
console.error('\n❌ 安装后处理失败:', err.message);
|
|
149
|
+
console.error(' 这通常不影响基本功能,继续安装...\n');
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
main();
|
package/bin/bolloon-daemon.sh
DELETED
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Bolloon Daemon - 守护进程启动脚本
|
|
3
|
-
# 支持自动重启、日志轮转、PID 管理
|
|
4
|
-
|
|
5
|
-
set -e
|
|
6
|
-
|
|
7
|
-
APP_NAME="bolloon"
|
|
8
|
-
APP_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
9
|
-
PID_FILE="$HOME/.bolloon/daemon.pid"
|
|
10
|
-
LOG_FILE="$HOME/.bolloon/daemon.log"
|
|
11
|
-
MAX_RESTARTS=5
|
|
12
|
-
RESTART_DELAY=5
|
|
13
|
-
|
|
14
|
-
# 颜色输出
|
|
15
|
-
RED='\033[0;31m'
|
|
16
|
-
GREEN='\033[0;32m'
|
|
17
|
-
YELLOW='\033[1;33m'
|
|
18
|
-
NC='\033[0m' # No Color
|
|
19
|
-
|
|
20
|
-
log() {
|
|
21
|
-
echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $1"
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
warn() {
|
|
25
|
-
echo -e "${YELLOW}[$(date '+%Y-%m-%d %H:%M:%S')] WARN:${NC} $1"
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
error() {
|
|
29
|
-
echo -e "${RED}[$(date '+%Y-%m-%d %H:%M:%S')] ERROR:${NC} $1"
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
# 获取 PID
|
|
33
|
-
get_pid() {
|
|
34
|
-
if [ -f "$PID_FILE" ]; then
|
|
35
|
-
cat "$PID_FILE"
|
|
36
|
-
fi
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
# 检查进程是否运行
|
|
40
|
-
is_running() {
|
|
41
|
-
local pid=$(get_pid)
|
|
42
|
-
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
|
|
43
|
-
return 0
|
|
44
|
-
fi
|
|
45
|
-
return 1
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
# 启动守护进程
|
|
49
|
-
start() {
|
|
50
|
-
if is_running; then
|
|
51
|
-
log "Bolloon 已在运行 (PID: $(get_pid))"
|
|
52
|
-
return 0
|
|
53
|
-
fi
|
|
54
|
-
|
|
55
|
-
log "启动 Bolloon 守护进程..."
|
|
56
|
-
|
|
57
|
-
# 确保日志目录存在
|
|
58
|
-
mkdir -p "$(dirname "$LOG_FILE")"
|
|
59
|
-
|
|
60
|
-
# 启动进程
|
|
61
|
-
cd "$APP_DIR"
|
|
62
|
-
nohup npx tsx src/index.ts >> "$LOG_FILE" 2>&1 &
|
|
63
|
-
local pid=$!
|
|
64
|
-
|
|
65
|
-
# 保存 PID
|
|
66
|
-
echo "$pid" > "$PID_FILE"
|
|
67
|
-
log "Bolloon 已启动 (PID: $pid)"
|
|
68
|
-
log "日志文件: $LOG_FILE"
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
# 停止守护进程
|
|
72
|
-
stop() {
|
|
73
|
-
local pid=$(get_pid)
|
|
74
|
-
|
|
75
|
-
if [ -z "$pid" ]; then
|
|
76
|
-
warn "Bolloon 未运行"
|
|
77
|
-
return 0
|
|
78
|
-
fi
|
|
79
|
-
|
|
80
|
-
log "停止 Bolloon (PID: $pid)..."
|
|
81
|
-
|
|
82
|
-
# 发送 SIGTERM
|
|
83
|
-
kill -TERM "$pid" 2>/dev/null || true
|
|
84
|
-
|
|
85
|
-
# 等待进程退出
|
|
86
|
-
local count=0
|
|
87
|
-
while kill -0 "$pid" 2>/dev/null && [ $count -lt 10 ]; do
|
|
88
|
-
sleep 1
|
|
89
|
-
count=$((count + 1))
|
|
90
|
-
done
|
|
91
|
-
|
|
92
|
-
# 如果还在运行,强制终止
|
|
93
|
-
if kill -0 "$pid" 2>/dev/null; then
|
|
94
|
-
warn "进程未响应,发送 SIGKILL..."
|
|
95
|
-
kill -KILL "$pid" 2>/dev/null || true
|
|
96
|
-
fi
|
|
97
|
-
|
|
98
|
-
# 清理 PID 文件
|
|
99
|
-
rm -f "$PID_FILE"
|
|
100
|
-
log "Bolloon 已停止"
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
# 重启守护进程
|
|
104
|
-
restart() {
|
|
105
|
-
log "重启 Bolloon..."
|
|
106
|
-
stop
|
|
107
|
-
sleep 2
|
|
108
|
-
start
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
# 查看状态
|
|
112
|
-
status() {
|
|
113
|
-
if is_running; then
|
|
114
|
-
log "Bolloon 运行中 (PID: $(get_pid))"
|
|
115
|
-
# 显示资源使用
|
|
116
|
-
local pid=$(get_pid)
|
|
117
|
-
if [ -n "$pid" ]; then
|
|
118
|
-
ps -p "$pid" -o %cpu,%mem,etime 2>/dev/null | tail -1 || true
|
|
119
|
-
fi
|
|
120
|
-
else
|
|
121
|
-
warn "Bolloon 未运行"
|
|
122
|
-
fi
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
# 查看日志
|
|
126
|
-
logs() {
|
|
127
|
-
if [ -f "$LOG_FILE" ]; then
|
|
128
|
-
tail -100 "$LOG_FILE"
|
|
129
|
-
else
|
|
130
|
-
warn "日志文件不存在: $LOG_FILE"
|
|
131
|
-
fi
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
# 健康检查
|
|
135
|
-
health() {
|
|
136
|
-
local port=${PORT:-54188}
|
|
137
|
-
local response=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:$port/api/health" 2>/dev/null || echo "000")
|
|
138
|
-
|
|
139
|
-
if [ "$response" = "200" ]; then
|
|
140
|
-
log "健康检查通过"
|
|
141
|
-
curl -s "http://localhost:$port/api/health" 2>/dev/null | head -5
|
|
142
|
-
else
|
|
143
|
-
error "健康检查失败 (HTTP $response)"
|
|
144
|
-
return 1
|
|
145
|
-
fi
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
# 清理日志
|
|
149
|
-
clean_logs() {
|
|
150
|
-
if [ -f "$LOG_FILE" ]; then
|
|
151
|
-
local size=$(du -h "$LOG_FILE" | cut -f1)
|
|
152
|
-
log "当前日志大小: $size"
|
|
153
|
-
> "$LOG_FILE"
|
|
154
|
-
log "日志已清空"
|
|
155
|
-
fi
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
# 显示帮助
|
|
159
|
-
show_help() {
|
|
160
|
-
echo "Bolloon Daemon 守护进程管理脚本"
|
|
161
|
-
echo ""
|
|
162
|
-
echo "用法: $0 [命令]"
|
|
163
|
-
echo ""
|
|
164
|
-
echo "命令:"
|
|
165
|
-
echo " start 启动 Bolloon 守护进程"
|
|
166
|
-
echo " stop 停止 Bolloon 守护进程"
|
|
167
|
-
echo " restart 重启 Bolloon 守护进程"
|
|
168
|
-
echo " status 查看运行状态"
|
|
169
|
-
echo " logs 查看最近日志"
|
|
170
|
-
echo " health 健康检查"
|
|
171
|
-
echo " clean 清空日志"
|
|
172
|
-
echo " help 显示帮助"
|
|
173
|
-
echo ""
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
# 主逻辑
|
|
177
|
-
case "${1:-help}" in
|
|
178
|
-
start)
|
|
179
|
-
start
|
|
180
|
-
;;
|
|
181
|
-
stop)
|
|
182
|
-
stop
|
|
183
|
-
;;
|
|
184
|
-
restart)
|
|
185
|
-
restart
|
|
186
|
-
;;
|
|
187
|
-
status)
|
|
188
|
-
status
|
|
189
|
-
;;
|
|
190
|
-
logs)
|
|
191
|
-
logs
|
|
192
|
-
;;
|
|
193
|
-
health)
|
|
194
|
-
health
|
|
195
|
-
;;
|
|
196
|
-
clean)
|
|
197
|
-
clean_logs
|
|
198
|
-
;;
|
|
199
|
-
help|--help|-h)
|
|
200
|
-
show_help
|
|
201
|
-
;;
|
|
202
|
-
*)
|
|
203
|
-
error "未知命令: $1"
|
|
204
|
-
show_help
|
|
205
|
-
exit 1
|
|
206
|
-
;;
|
|
207
|
-
esac
|
package/bin/ipfs
DELETED
|
Binary file
|
|
@@ -1,298 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* chat-segmenter — 把 LLM 原始输出切成结构化 segments (前后端分离核心)
|
|
3
|
-
*
|
|
4
|
-
* 2026-07-01 (v0.2.6): 抽到独立模块. 目标: 前端只渲染, 不做正则.
|
|
5
|
-
*
|
|
6
|
-
* 设计:
|
|
7
|
-
* 后端跑完 LLM 一轮后, 调 segmentChatReply() 把 reply 切成:
|
|
8
|
-
* [{type: 'think', content: '...'}, {type: 'text', content: '...'}, ...]
|
|
9
|
-
* 然后序列化发给前端, 前端按 type 渲染 — 不知道任何 <invoke> / <function_calls>
|
|
10
|
-
* /<tool_call> / {tool:...} 这些 LLM 输出格式细节.
|
|
11
|
-
*
|
|
12
|
-
* 与 parse-tool-call.ts 的区别:
|
|
13
|
-
* - parse-tool-call: 找 **第一个** tool_call, 提取 name+args (用于 dispatch)
|
|
14
|
-
* - chat-segmenter: 切**整段** reply 为展示 segments, **完全去掉** tool_call 标记
|
|
15
|
-
* 复用 parse-tool-call 的 tool_call 检测逻辑保证一致性
|
|
16
|
-
*
|
|
17
|
-
* 返回类型: ChatSegment[] 序列化 JSON 给前端:
|
|
18
|
-
* {type: 'think'|'text'|'env_details'|'tool_call'|'final', content?: string, tool?: {...}}
|
|
19
|
-
*
|
|
20
|
-
* 顺序按 LLM 输出流排, 不重排.
|
|
21
|
-
*/
|
|
22
|
-
import { parseToolCall } from './parse-tool-call.js';
|
|
23
|
-
/** 切分 LLM 输出. */
|
|
24
|
-
export function segmentChatReply(reply, ctx) {
|
|
25
|
-
if (!reply)
|
|
26
|
-
return [];
|
|
27
|
-
const segments = [];
|
|
28
|
-
let remaining = reply;
|
|
29
|
-
// === 1. 启发式: 开头"让我.../First I'll.../I should..." 句子进 think 段 ===
|
|
30
|
-
// LLM 偶尔不写 <think> 标签但直接出思考. 启发式: 文本首句以这些模式开头 → think
|
|
31
|
-
// 必须在 step 1 (显式 <think> 切分) 之前 — 因为显式切分 push 完会反序
|
|
32
|
-
if (!remaining.startsWith('<think>')) {
|
|
33
|
-
remaining = extractLeadingThinking(remaining, segments);
|
|
34
|
-
}
|
|
35
|
-
// === 2. 切 <think>...</think> (内容保留, wrap 成 think segment) ===
|
|
36
|
-
remaining = extractAndPush('think', remaining, segments, /<think>([\s\S]*?)<\/think>/g);
|
|
37
|
-
// === 3. 切 <environment_details>...</environment_details> ===
|
|
38
|
-
remaining = extractAndPush('env_details', remaining, segments, /<environment_details>([\s\S]*?)<\/environment_details>/g);
|
|
39
|
-
// === 4. 切 tool_call 标记 (核心: 完全去掉, 不让前端看到) ===
|
|
40
|
-
// 2026-07-01 修: 这步必须在 final 之前 — final 标记之前的 tool_call 是
|
|
41
|
-
// "LLM 在 final 之前还在调工具", 这部分 content 应进 text (中间过程), 不应进 final.
|
|
42
|
-
remaining = stripToolCallMarkers(remaining, segments, ctx);
|
|
43
|
-
// === 5. 过滤 LLM 填充词 ("好了"/"完成"/"可以" 等单句 text 不上屏) ===
|
|
44
|
-
if (remaining.trim()) {
|
|
45
|
-
remaining = filterFillerText(remaining);
|
|
46
|
-
}
|
|
47
|
-
// === 6. final 之前的 text 段 (中间对话) push — 必须在 final push 之前 ===
|
|
48
|
-
// 注意: 这时 remaining 还含 <final gen> 标记 + 标记后内容. 切 final 前先把 <final gen> 之后内容清掉
|
|
49
|
-
// 实际: 切 final 后再切 text 更清楚
|
|
50
|
-
const finalIdx = remaining.indexOf('<final gen>');
|
|
51
|
-
let beforeFinalText = remaining;
|
|
52
|
-
if (finalIdx >= 0) {
|
|
53
|
-
beforeFinalText = remaining.substring(0, finalIdx);
|
|
54
|
-
}
|
|
55
|
-
const textContent = beforeFinalText.trim();
|
|
56
|
-
if (textContent) {
|
|
57
|
-
segments.push({ type: 'text', content: textContent });
|
|
58
|
-
}
|
|
59
|
-
// === 7. 切 <final gen>...</final gen> (永远最后 push, 渲染时在最后) ===
|
|
60
|
-
if (finalIdx >= 0) {
|
|
61
|
-
const afterFinal = remaining.substring(finalIdx + '<final gen>'.length).trim();
|
|
62
|
-
if (afterFinal) {
|
|
63
|
-
segments.push({ type: 'final', content: afterFinal });
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
return segments;
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* 修 extractAndPush 的 reverse 删除 bug (2026-07-01)
|
|
70
|
-
* 之前: 用 mutate 后的 next 算切片, idx 错位 → 多 match 时删除错
|
|
71
|
-
* 现在: 用 lastIndex 累加在原文上标记删除区, 最后一次性 slice
|
|
72
|
-
*/
|
|
73
|
-
function extractAndPush(type, text, out, re) {
|
|
74
|
-
const matches = [];
|
|
75
|
-
const localRe = new RegExp(re.source, re.flags);
|
|
76
|
-
let m;
|
|
77
|
-
while ((m = localRe.exec(text)) !== null) {
|
|
78
|
-
const content = m[1] ? m[1].trim() : '';
|
|
79
|
-
matches.push({
|
|
80
|
-
start: m.index,
|
|
81
|
-
end: m.index + m[0].length,
|
|
82
|
-
content,
|
|
83
|
-
});
|
|
84
|
-
if (m[0].length === 0)
|
|
85
|
-
localRe.lastIndex++;
|
|
86
|
-
}
|
|
87
|
-
if (matches.length === 0)
|
|
88
|
-
return text;
|
|
89
|
-
// push 正向 (保顺序)
|
|
90
|
-
for (const m of matches) {
|
|
91
|
-
if (m.content)
|
|
92
|
-
out.push({ type, content: m.content });
|
|
93
|
-
}
|
|
94
|
-
// 删除: 在原文上基于 start/end 切片, 不在 mutate 后算 idx
|
|
95
|
-
let next = '';
|
|
96
|
-
let cursor = 0;
|
|
97
|
-
for (const m of matches) {
|
|
98
|
-
next += text.substring(cursor, m.start);
|
|
99
|
-
cursor = m.end;
|
|
100
|
-
}
|
|
101
|
-
next += text.substring(cursor);
|
|
102
|
-
return next;
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* 启发式: 提取开头的"思考"句子 (2026-07-01 新增)
|
|
106
|
-
* 模式: 文本开头遇到这些起手式, 整句 (到下一个 \n 或 . 或 句号) 切到 think 段
|
|
107
|
-
* 例子:
|
|
108
|
-
* "让我先想想用户的需求" + 正文 → think(让我先想想用户的需求) + 正文
|
|
109
|
-
* "First I'll check the project structure" → think(...)
|
|
110
|
-
* 不命中 → 整段保留, 走 text
|
|
111
|
-
*/
|
|
112
|
-
function extractLeadingThinking(text, out) {
|
|
113
|
-
// 模式 1: 显式/隐式思考句首 (一句到第一个 \n 或 \.\?\! 结束)
|
|
114
|
-
// "让我..." / "我先..." / "先来..." / "接下来..." / "First, ..." / "I'll ..."
|
|
115
|
-
// 整句进 think, 后续 content 进 text
|
|
116
|
-
// 模式 2: 整行都是思考 (一行 \n 间隔)
|
|
117
|
-
const firstLineEnd = text.indexOf('\n');
|
|
118
|
-
const firstLine = firstLineEnd === -1 ? text : text.substring(0, firstLineEnd);
|
|
119
|
-
const rest = firstLineEnd === -1 ? '' : text.substring(firstLineEnd + 1);
|
|
120
|
-
// 中文/英文思考起手词
|
|
121
|
-
const thinkingStartRe = /^(让我|我先|我应该|先来|先|接下来|好的[,,]?\s*我|让我先|先看看|让我看看|思考|考虑)/;
|
|
122
|
-
const enThinkingStartRe = /^(Let me|I'll|I will|First,|Next,|Now,|So,|Alright[,.]\s+(?:let me|I'll|I will))/i;
|
|
123
|
-
if (firstLine.trim().length === 0) {
|
|
124
|
-
return text; // 空开头不动
|
|
125
|
-
}
|
|
126
|
-
// 单行启发式: 整行 < 120 字符 + 起始 match → 整行进 think
|
|
127
|
-
if (firstLine.length <= 120 && (thinkingStartRe.test(firstLine) || enThinkingStartRe.test(firstLine))) {
|
|
128
|
-
out.push({ type: 'think', content: firstLine.trim() });
|
|
129
|
-
return rest;
|
|
130
|
-
}
|
|
131
|
-
// 多行启发式: 第一段 (到第一个空行) 是思考
|
|
132
|
-
if (firstLine.length <= 80 && (thinkingStartRe.test(firstLine) || enThinkingStartRe.test(firstLine))) {
|
|
133
|
-
out.push({ type: 'think', content: firstLine.trim() });
|
|
134
|
-
return rest;
|
|
135
|
-
}
|
|
136
|
-
return text;
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* 过滤 LLM 填充词 (2026-07-01 新增)
|
|
140
|
-
* 单独的"好了"/"完成"/"可以"/"答完了"等单句不显示在气泡
|
|
141
|
-
* 配合 segmentChatReply 步骤 6
|
|
142
|
-
*/
|
|
143
|
-
function filterFillerText(text) {
|
|
144
|
-
// 整行 = filler 词 → 整行丢
|
|
145
|
-
const lines = text.split('\n');
|
|
146
|
-
const filtered = [];
|
|
147
|
-
for (const line of lines) {
|
|
148
|
-
const t = line.trim();
|
|
149
|
-
if (!t)
|
|
150
|
-
continue;
|
|
151
|
-
// 匹配: 整行 = 填充词 (可带标点)
|
|
152
|
-
if (/^(好|好了|好的|完成|完成了|任务完成|可以|可以了|答完了|说完了|就这样|完了|done|ok|OK|Okay|okay|alright|fine|let me check|我来)\.?$/i.test(t)) {
|
|
153
|
-
continue;
|
|
154
|
-
}
|
|
155
|
-
filtered.push(line);
|
|
156
|
-
}
|
|
157
|
-
return filtered.join('\n').trim();
|
|
158
|
-
}
|
|
159
|
-
/** 提取正则匹配的 group(1) 内容, 包装为 segment; 原文位置去掉 */
|
|
160
|
-
function stripToolCallMarkers(text, out, ctx) {
|
|
161
|
-
// 1. 解析 [TOOL_CALL]...[/TOOL_CALL]
|
|
162
|
-
let result = text.replace(/\[TOOL_CALL\][\s\S]*?\[\/TOOL_CALL\]/g, (m) => {
|
|
163
|
-
const parsed = parseToolCall(m, { tools: ctx.knownToolNames });
|
|
164
|
-
if (parsed)
|
|
165
|
-
out.push({ type: 'tool_call', tool: { name: parsed.name, args: parsed.args } });
|
|
166
|
-
return '';
|
|
167
|
-
});
|
|
168
|
-
// 2.<tool_call>...</tool_call> (OpenAI Hermes)
|
|
169
|
-
result = result.replace(/<tool_call>[\s\S]*?<\/tool_call>/gi, (m) => {
|
|
170
|
-
const parsed = parseToolCall(m, { tools: ctx.knownToolNames });
|
|
171
|
-
if (parsed)
|
|
172
|
-
out.push({ type: 'tool_call', tool: { name: parsed.name, args: parsed.args } });
|
|
173
|
-
return '';
|
|
174
|
-
});
|
|
175
|
-
// 3. <invoke name="X">...</invoke> (minimax/Hermes)
|
|
176
|
-
result = result.replace(/<invoke\s+name=["']([\w]+)["']>([\s\S]*?)<\/invoke>/g, (_m, name, inner) => {
|
|
177
|
-
if (ctx.knownToolNames.has(name)) {
|
|
178
|
-
const args = extractSimpleArgs(inner);
|
|
179
|
-
out.push({ type: 'tool_call', tool: { name, args } });
|
|
180
|
-
}
|
|
181
|
-
return '';
|
|
182
|
-
});
|
|
183
|
-
// 4. <function_calls>...</function_calls> 整块
|
|
184
|
-
result = result.replace(/<function_calls>[\s\S]*?<\/function_calls>/g, (m) => {
|
|
185
|
-
const parsed = parseToolCall(m, { tools: ctx.knownToolNames });
|
|
186
|
-
if (parsed)
|
|
187
|
-
out.push({ type: 'tool_call', tool: { name: parsed.name, args: parsed.args } });
|
|
188
|
-
return '';
|
|
189
|
-
});
|
|
190
|
-
// 5. JSON 形式 {"name": "X", "arguments": {...}}
|
|
191
|
-
result = result.replace(/\{\s*"name"\s*:\s*"([\w]+)"\s*,\s*"(?:arguments|input|args|params)"\s*:\s*(\{[\s\S]*?\})\s*\}/g, (_m, name, argsJson) => {
|
|
192
|
-
if (!ctx.knownToolNames.has(name))
|
|
193
|
-
return '';
|
|
194
|
-
let args = {};
|
|
195
|
-
try {
|
|
196
|
-
const parsed = JSON.parse(argsJson);
|
|
197
|
-
if (parsed && typeof parsed === 'object') {
|
|
198
|
-
args = Object.fromEntries(Object.entries(parsed).map(([k, v]) => [k, String(v)]));
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
catch { }
|
|
202
|
-
out.push({ type: 'tool_call', tool: { name, args } });
|
|
203
|
-
return '';
|
|
204
|
-
});
|
|
205
|
-
// 6. tool => "X", args => {...} (Perplexity-style 内部)
|
|
206
|
-
result = result.replace(/\{\s*tool\s*=>\s*["']([\w]+)["']\s*(?:,\s*args\s*=>\s*(\{[\s\S]*?\}))?\s*\}/g, (_m, name, argsJson) => {
|
|
207
|
-
if (!ctx.knownToolNames.has(name))
|
|
208
|
-
return '';
|
|
209
|
-
let args = {};
|
|
210
|
-
if (argsJson) {
|
|
211
|
-
try {
|
|
212
|
-
const parsed = JSON.parse(argsJson);
|
|
213
|
-
if (parsed && typeof parsed === 'object') {
|
|
214
|
-
args = Object.fromEntries(Object.entries(parsed).map(([k, v]) => [k, String(v)]));
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
catch { }
|
|
218
|
-
}
|
|
219
|
-
out.push({ type: 'tool_call', tool: { name, args } });
|
|
220
|
-
return '';
|
|
221
|
-
});
|
|
222
|
-
// 7. [Function calling]...[/Function calling] 旧 bolloon
|
|
223
|
-
result = result.replace(/\[Function[^\]]*\]\s*/g, '');
|
|
224
|
-
// 8. (2026-07-01) 未闭合的 tool_call 起始标签 (LLM 偶输出截断)
|
|
225
|
-
// 例子: "<tool_call><invoke name="shell_exec"><command>ls</command></invoke>" 缺少 </tool_call>
|
|
226
|
-
// 上面 1-7 步的正则都要求完整闭合对, 不闭合会留残文.
|
|
227
|
-
// 修法: 扫到孤立起始标签 (后面没有匹配的闭合) → 整段删到 \n\n 或 end
|
|
228
|
-
result = stripUnclosedToolCallTags(result);
|
|
229
|
-
return result;
|
|
230
|
-
}
|
|
231
|
-
/**
|
|
232
|
-
* 删未闭合的 tool_call 起始标签 + 后面到段结束 (2026-07-01)
|
|
233
|
-
* 例子: "<tool_call><invoke name="shell_exec"><command>ls</command></invoke>"
|
|
234
|
-
* → 检查每个起始标签 (<tool_call>, <invoke, <function_calls, [TOOL_CALL])
|
|
235
|
-
* → 如果没找到对应闭合, 删整段
|
|
236
|
-
*/
|
|
237
|
-
function stripUnclosedToolCallTags(text) {
|
|
238
|
-
const startTags = [
|
|
239
|
-
{ tag: '<tool_call>', closeTag: '</tool_call>', openRe: /<tool_call>/g },
|
|
240
|
-
{ tag: '<invoke ', closeTag: '</invoke>', openRe: /<invoke\s+name=["']([\w]+)["']>/g },
|
|
241
|
-
{ tag: '<function_calls>', closeTag: '</function_calls>', openRe: /<function_calls>/g },
|
|
242
|
-
{ tag: '[TOOL_CALL]', closeTag: '[/TOOL_CALL]', openRe: /\[TOOL_CALL\]/g },
|
|
243
|
-
];
|
|
244
|
-
let result = text;
|
|
245
|
-
for (const { tag, closeTag, openRe } of startTags) {
|
|
246
|
-
let m;
|
|
247
|
-
while ((m = openRe.exec(result)) !== null) {
|
|
248
|
-
// 检查 startIdx 之后是否还有 closeTag
|
|
249
|
-
const tail = result.substring(m.index);
|
|
250
|
-
const closeIdx = tail.indexOf(closeTag);
|
|
251
|
-
if (closeIdx === -1) {
|
|
252
|
-
// 未闭合 — 删起始标签 + 之后所有内容 (到 \n\n 或 end)
|
|
253
|
-
const nextBlank = result.indexOf('\n\n', m.index);
|
|
254
|
-
const endIdx = nextBlank === -1 ? result.length : nextBlank;
|
|
255
|
-
result = result.substring(0, m.index) + result.substring(endIdx);
|
|
256
|
-
break; // 重新开始本 tag 扫描
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
return result;
|
|
261
|
-
}
|
|
262
|
-
/** 从 <invoke>...</invoke> inner XML 抽 <command> / <args> / <parameter> 简易 args */
|
|
263
|
-
function extractSimpleArgs(inner) {
|
|
264
|
-
const args = {};
|
|
265
|
-
// <parameter name="X">value</parameter>
|
|
266
|
-
const paramRe = /<parameter\s+name=["'](\w+)["']>([\s\S]*?)<\/parameter>/g;
|
|
267
|
-
let m;
|
|
268
|
-
while ((m = paramRe.exec(inner)) !== null) {
|
|
269
|
-
args[m[1]] = m[2].trim();
|
|
270
|
-
}
|
|
271
|
-
// <param name="X">value</param>
|
|
272
|
-
if (Object.keys(args).length === 0) {
|
|
273
|
-
const shortRe = /<param\s+name=["'](\w+)["']>([\s\S]*?)<\/param>/g;
|
|
274
|
-
while ((m = shortRe.exec(inner)) !== null) {
|
|
275
|
-
args[m[1]] = m[2].trim().replace(/^["']|["']$/g, '');
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
// <command>X</command> + <args>Y</args>
|
|
279
|
-
if (Object.keys(args).length === 0) {
|
|
280
|
-
const cmdM = inner.match(/<command>([\s\S]*?)<\/command>/);
|
|
281
|
-
const argsM = inner.match(/<args>([\s\S]*?)<\/args>/);
|
|
282
|
-
if (cmdM) {
|
|
283
|
-
const cmd = cmdM[1].trim();
|
|
284
|
-
// auto-split (跟 parse-tool-call 一致: 含空格 → split)
|
|
285
|
-
if (cmd.includes(' ') && !argsM) {
|
|
286
|
-
const parts = cmd.split(/\s+/);
|
|
287
|
-
args.command = parts[0];
|
|
288
|
-
args.args = parts.slice(1).join(' ');
|
|
289
|
-
}
|
|
290
|
-
else {
|
|
291
|
-
args.command = cmd;
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
if (argsM)
|
|
295
|
-
args.args = argsM[1].trim();
|
|
296
|
-
}
|
|
297
|
-
return args;
|
|
298
|
-
}
|