@bolloon/bolloon-agent 0.2.12 → 0.2.14
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 +495 -467
- package/bin/bolloon-cli.cjs +183 -183
- package/bin/bolloon-daemon.sh +207 -0
- package/bin/bolloon.cjs +0 -0
- package/bin/ipfs +0 -0
- package/dist/agents/agent-manifest-protocol.js +52 -0
- package/dist/agents/chat-segmenter.js +298 -0
- package/dist/agents/constraint-layer.js +19 -19
- package/dist/agents/judgment-protocol.js +479 -0
- package/dist/agents/parse-tool-call.js +304 -0
- package/dist/agents/peer-manifest-loader.js +210 -0
- package/dist/agents/pi-sdk-session-factory.js +110 -0
- package/dist/agents/pi-sdk-session-manager.js +297 -0
- package/dist/agents/pi-sdk-tools.js +1288 -0
- package/dist/agents/pi-sdk-types.js +13 -0
- package/dist/agents/pi-sdk.js +553 -1306
- package/dist/agents/react-loop.js +120 -0
- package/dist/agents/session-store.js +171 -0
- package/dist/agents/shell-guard.js +2 -2
- package/dist/agents/tool-registry.js +136 -0
- package/dist/agents/workflow-pivot-loop.js +200 -22
- package/dist/bollharness/src/scripts/context_router.js +7 -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 +26 -13
- package/dist/bollharness-integration/integration.js +71 -0
- package/dist/bollharness-integration/llm-judgment-engine.js +134 -134
- package/dist/bollharness-integration/skill-adapter.js +57 -127
- package/dist/bootstrap/chat-archiver.js +276 -0
- package/dist/bootstrap/context-collector.js +6 -3
- package/dist/bootstrap/event-log.js +160 -0
- package/dist/bootstrap/lifecycle-hooks.js +15 -1
- package/dist/bootstrap/memory-compressor.js +170 -0
- package/dist/bootstrap/persona-loader.js +94 -0
- package/dist/bootstrap/project-state.js +159 -0
- package/dist/bootstrap/remote-mirror.js +97 -0
- package/dist/bootstrap/session-window.js +121 -0
- package/dist/bootstrap/vector-index.js +248 -0
- package/dist/cli/loading-tui.js +40 -0
- package/dist/cli-entry.js +41 -37
- package/dist/context-compaction/auto-compact.js +7 -7
- package/dist/electron/config.js +21 -0
- package/dist/electron/config.js.map +1 -0
- package/dist/electron/dialogs.js +108 -0
- package/dist/electron/dialogs.js.map +1 -0
- package/dist/electron/first-run.js +170 -0
- package/dist/electron/first-run.js.map +1 -0
- package/dist/electron/ipc.js +20 -0
- package/dist/electron/ipc.js.map +1 -0
- package/dist/electron/logger.js +114 -0
- package/dist/electron/logger.js.map +1 -0
- package/dist/electron/main.js +63 -0
- package/dist/electron/main.js.map +1 -0
- package/dist/electron/menu.js +145 -0
- package/dist/electron/menu.js.map +1 -0
- package/dist/electron/paths.js +75 -0
- package/dist/electron/paths.js.map +1 -0
- package/dist/electron/server.js +119 -0
- package/dist/electron/server.js.map +1 -0
- package/dist/electron/tray.js +111 -0
- package/dist/electron/tray.js.map +1 -0
- package/dist/electron/window.js +111 -0
- package/dist/electron/window.js.map +1 -0
- package/dist/electron-preload.js +26 -6
- package/dist/electron-preload.js.map +1 -1
- package/dist/electron.js +6 -168
- package/dist/electron.js.map +1 -1
- package/dist/index.js +323 -266
- package/dist/llm/config-store.js +29 -8
- package/dist/llm/llm-judgment-client.js +102 -102
- package/dist/llm/pi-ai.js +139 -85
- 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/network/local-inbox-bus.js +73 -0
- package/dist/network/p2p-outbox.js +161 -0
- package/dist/network/peer-fs.js +420 -0
- package/dist/network/peer-resource-bridge.js +216 -0
- package/dist/pi-ecosystem-judgment/causal-judge.js +51 -51
- package/dist/pi-ecosystem-judgment/cleanup.js +126 -0
- package/dist/pi-ecosystem-judgment/distill-prompt.js +42 -42
- package/dist/pi-ecosystem-judgment/distillation.js +14 -14
- package/dist/pi-ecosystem-judgment/human-value-store.js +45 -11
- 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 +11 -0
- package/dist/web/api-config.html +520 -520
- package/dist/web/client-loop-status.js +220 -0
- package/dist/web/client.js +22 -5
- package/dist/web/client.js.map +7 -0
- package/dist/web/components/wallet-viem.mjs +118 -118
- package/dist/web/index.html +375 -375
- package/dist/web/input-validator.js +103 -0
- package/dist/web/manifest.json +20 -20
- package/dist/web/routes-judgments.js +738 -0
- package/dist/web/routes-llm-config.js +279 -0
- package/dist/web/routes-tasks.js +222 -0
- package/dist/web/server-sse.js +121 -0
- package/dist/web/server-storage.js +154 -0
- package/dist/web/server-types.js +15 -0
- package/dist/web/server-v3-p2p.js +224 -0
- package/dist/web/server.js +1070 -1307
- package/dist/web/style.css +4742 -4742
- package/dist/web/util/safe-name.js +32 -0
- package/package.json +162 -160
- package/scripts/build-cli.js +215 -215
- package/scripts/build-web.ts +130 -130
- package/scripts/postinstall.js +152 -152
- package/bin/bolloon.js +0 -157
- package/dist/constraint-runtime/src/_archive_helper.js +0 -9
- package/dist/constraint-runtime/src/agent/coordinator.js +0 -48
- package/dist/constraint-runtime/src/agent/index.js +0 -1
- package/dist/constraint-runtime/src/assistant/index.js +0 -12
- package/dist/constraint-runtime/src/bootstrap/index.js +0 -12
- package/dist/constraint-runtime/src/bootstrap_graph.js +0 -13
- package/dist/constraint-runtime/src/bridge/index.js +0 -12
- package/dist/constraint-runtime/src/buddy/index.js +0 -12
- package/dist/constraint-runtime/src/cli/index.js +0 -12
- package/dist/constraint-runtime/src/command_graph.js +0 -10
- package/dist/constraint-runtime/src/commands.js +0 -60
- package/dist/constraint-runtime/src/components/index.js +0 -12
- package/dist/constraint-runtime/src/constants/index.js +0 -12
- package/dist/constraint-runtime/src/constraint/budget.js +0 -22
- package/dist/constraint-runtime/src/constraint/index.js +0 -2
- package/dist/constraint-runtime/src/constraint/permission.js +0 -20
- package/dist/constraint-runtime/src/context.js +0 -30
- package/dist/constraint-runtime/src/coordinator/index.js +0 -12
- package/dist/constraint-runtime/src/cost_hook.js +0 -4
- package/dist/constraint-runtime/src/cost_tracker.js +0 -8
- package/dist/constraint-runtime/src/deferred_init.js +0 -10
- package/dist/constraint-runtime/src/direct_modes.js +0 -6
- package/dist/constraint-runtime/src/dynamic-tool-loader.js +0 -85
- package/dist/constraint-runtime/src/entrypoints/index.js +0 -12
- package/dist/constraint-runtime/src/execution_registry.js +0 -44
- package/dist/constraint-runtime/src/history.js +0 -9
- package/dist/constraint-runtime/src/hooks/index.js +0 -12
- package/dist/constraint-runtime/src/index.js +0 -26
- package/dist/constraint-runtime/src/ink.js +0 -4
- package/dist/constraint-runtime/src/keybindings/index.js +0 -12
- package/dist/constraint-runtime/src/memdir/index.js +0 -12
- package/dist/constraint-runtime/src/migrations/index.js +0 -12
- package/dist/constraint-runtime/src/models.js +0 -1
- package/dist/constraint-runtime/src/moreright/index.js +0 -12
- package/dist/constraint-runtime/src/native_ts/index.js +0 -12
- package/dist/constraint-runtime/src/output_styles/index.js +0 -12
- package/dist/constraint-runtime/src/parity_audit.js +0 -12
- package/dist/constraint-runtime/src/plugins/index.js +0 -12
- package/dist/constraint-runtime/src/port_manifest.js +0 -11
- package/dist/constraint-runtime/src/prefetch.js +0 -9
- package/dist/constraint-runtime/src/query.js +0 -1
- package/dist/constraint-runtime/src/remote/index.js +0 -12
- package/dist/constraint-runtime/src/remote_runtime.js +0 -9
- package/dist/constraint-runtime/src/runtime/index.js +0 -1
- package/dist/constraint-runtime/src/runtime/session.js +0 -35
- package/dist/constraint-runtime/src/schemas/index.js +0 -12
- package/dist/constraint-runtime/src/screens/index.js +0 -12
- package/dist/constraint-runtime/src/server/index.js +0 -12
- package/dist/constraint-runtime/src/services/index.js +0 -12
- package/dist/constraint-runtime/src/session_store.js +0 -22
- package/dist/constraint-runtime/src/setup.js +0 -30
- package/dist/constraint-runtime/src/skills/index.js +0 -1
- package/dist/constraint-runtime/src/skills/skill-registry.js +0 -28
- package/dist/constraint-runtime/src/state/index.js +0 -12
- package/dist/constraint-runtime/src/system_init.js +0 -20
- package/dist/constraint-runtime/src/thinking/engine.js +0 -42
- package/dist/constraint-runtime/src/thinking/index.js +0 -1
- package/dist/constraint-runtime/src/tool_pool.js +0 -8
- package/dist/constraint-runtime/src/tools/OpenCLI/execAdapter.js +0 -7
- package/dist/constraint-runtime/src/tools/OpenCLI/listAdapters.js +0 -7
- package/dist/constraint-runtime/src/tools/OpenCLI/runCommand.js +0 -7
- package/dist/constraint-runtime/src/tools/SafeSDK/confirmTransaction.js +0 -6
- package/dist/constraint-runtime/src/tools/SafeSDK/createTransaction.js +0 -8
- package/dist/constraint-runtime/src/tools/SafeSDK/executeTransaction.js +0 -6
- package/dist/constraint-runtime/src/tools/SafeSDK/getBalance.js +0 -6
- package/dist/constraint-runtime/src/tools/SafeSDK/getPendingTransactions.js +0 -6
- package/dist/constraint-runtime/src/tools/SafeSDK/proposeTransaction.js +0 -6
- package/dist/constraint-runtime/src/tools.js +0 -80
- package/dist/constraint-runtime/src/transcript.js +0 -19
- package/dist/constraint-runtime/src/types/index.js +0 -12
- package/dist/constraint-runtime/src/upstream_proxy/index.js +0 -12
- package/dist/constraint-runtime/src/utils/index.js +0 -12
- package/dist/constraint-runtime/src/vim/index.js +0 -12
- package/dist/constraint-runtime/src/voice/index.js +0 -12
- package/dist/constraint-runtime/tests/agent.test.js +0 -16
- package/dist/constraint-runtime/tests/constraint.test.js +0 -41
- package/dist/constraint-runtime/tests/skill.test.js +0 -19
- package/dist/constraint-runtime/tests/thinking.test.js +0 -22
- 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/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
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* routes-llm-config.ts — LLM/Video/Audio 配置路由 (2026-07-06 抽出)
|
|
3
|
+
*
|
|
4
|
+
* 从 src/web/server.ts 抽出 (~230 行).
|
|
5
|
+
* 包含 /api/llm-config/* /api/video-config/* /api/audio-config/* /api/ai-parse
|
|
6
|
+
*/
|
|
7
|
+
import { llmConfigStore } from '../llm/config-store.js';
|
|
8
|
+
import { videoConfigStore } from '../llm/video-config-store.js';
|
|
9
|
+
import { audioConfigStore } from '../llm/audio-config-store.js';
|
|
10
|
+
import { initMinimax, getMinimax } from '../constraints/index.js';
|
|
11
|
+
export function registerLlmConfigRoutes(app) {
|
|
12
|
+
// ==================== LLM 配置 API ====================
|
|
13
|
+
// 获取所有 LLM 配置
|
|
14
|
+
app.get('/api/llm-config', async (req, res) => {
|
|
15
|
+
try {
|
|
16
|
+
const config = await llmConfigStore.getConfig();
|
|
17
|
+
const providerInfo = llmConfigStore.getAllProviderInfo();
|
|
18
|
+
// 隐藏 API Key
|
|
19
|
+
const safeConfig = {
|
|
20
|
+
...config,
|
|
21
|
+
providers: Object.fromEntries(Object.entries(config.providers).map(([key, val]) => [
|
|
22
|
+
key,
|
|
23
|
+
{ ...val, apiKey: val.apiKey ? '***' + val.apiKey.slice(-4) : '' }
|
|
24
|
+
])),
|
|
25
|
+
providerInfo
|
|
26
|
+
};
|
|
27
|
+
res.json(safeConfig);
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
res.status(500).json({ error: err.message });
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
// 更新 LLM 配置
|
|
34
|
+
app.post('/api/llm-config', async (req, res) => {
|
|
35
|
+
try {
|
|
36
|
+
const { provider, config } = req.body;
|
|
37
|
+
if (!provider || !config) {
|
|
38
|
+
return res.status(400).json({ error: 'provider and config required' });
|
|
39
|
+
}
|
|
40
|
+
// 如果前端发的是掩码(***xxx),从当前配置里取真实 key
|
|
41
|
+
const currentConfig = await llmConfigStore.getProvider(provider);
|
|
42
|
+
if (currentConfig && config.apiKey && config.apiKey.startsWith('***')) {
|
|
43
|
+
config.apiKey = currentConfig.apiKey;
|
|
44
|
+
}
|
|
45
|
+
await llmConfigStore.updateProvider(provider, config);
|
|
46
|
+
// 如果是活跃供应商,重新初始化 Pi SDK
|
|
47
|
+
const currentActive = await llmConfigStore.getActiveProvider();
|
|
48
|
+
if (provider === currentActive) {
|
|
49
|
+
const newConfig = await llmConfigStore.getActiveProviderConfig();
|
|
50
|
+
if (newConfig) {
|
|
51
|
+
initMinimax({
|
|
52
|
+
provider,
|
|
53
|
+
apiKey: newConfig.apiKey || undefined,
|
|
54
|
+
baseUrl: newConfig.baseUrl || undefined,
|
|
55
|
+
model: newConfig.model || undefined
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
res.json({ ok: true });
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
res.status(500).json({ error: err.message });
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
// 设置活跃供应商
|
|
66
|
+
app.post('/api/llm-provider', async (req, res) => {
|
|
67
|
+
try {
|
|
68
|
+
const { provider } = req.body;
|
|
69
|
+
if (!provider) {
|
|
70
|
+
return res.status(400).json({ error: 'provider required' });
|
|
71
|
+
}
|
|
72
|
+
await llmConfigStore.setActiveProvider(provider);
|
|
73
|
+
// 重新初始化 Pi SDK
|
|
74
|
+
const config = await llmConfigStore.getActiveProviderConfig();
|
|
75
|
+
if (config) {
|
|
76
|
+
initMinimax({
|
|
77
|
+
provider: provider,
|
|
78
|
+
apiKey: config.apiKey || undefined,
|
|
79
|
+
baseUrl: config.baseUrl || undefined,
|
|
80
|
+
model: config.model || undefined
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
res.json({ ok: true, provider });
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
res.status(500).json({ error: err.message });
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
// 测试供应商连接
|
|
90
|
+
app.post('/api/llm-test', async (req, res) => {
|
|
91
|
+
try {
|
|
92
|
+
const { provider } = req.body;
|
|
93
|
+
if (!provider) {
|
|
94
|
+
return res.status(400).json({ error: 'provider required' });
|
|
95
|
+
}
|
|
96
|
+
const result = await llmConfigStore.testProvider(provider);
|
|
97
|
+
res.json(result);
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
res.status(500).json({ error: err.message });
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
// ==================== 视频生成配置 (Seedance 等) ====================
|
|
104
|
+
// 获取视频生成配置
|
|
105
|
+
app.get('/api/video-config', async (req, res) => {
|
|
106
|
+
try {
|
|
107
|
+
const config = await videoConfigStore.getConfig();
|
|
108
|
+
const providerInfo = videoConfigStore.getAllProviderInfo();
|
|
109
|
+
// 脱敏:不返回 apiKey 明文
|
|
110
|
+
const masked = Object.fromEntries(Object.entries(config.providers).map(([key, val]) => [
|
|
111
|
+
key,
|
|
112
|
+
{ ...val, apiKey: val.apiKey ? '***' + val.apiKey.slice(-4) : '' }
|
|
113
|
+
]));
|
|
114
|
+
res.json({
|
|
115
|
+
activeProvider: config.activeProvider,
|
|
116
|
+
providers: masked,
|
|
117
|
+
providerInfo
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
res.status(500).json({ error: err.message });
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
// 更新视频供应商配置
|
|
125
|
+
app.post('/api/video-config', async (req, res) => {
|
|
126
|
+
try {
|
|
127
|
+
const { provider, config } = req.body;
|
|
128
|
+
if (!provider || !config) {
|
|
129
|
+
return res.status(400).json({ error: 'provider and config required' });
|
|
130
|
+
}
|
|
131
|
+
// 如果前端发的是掩码(***xxx),从当前配置里取真实 key
|
|
132
|
+
const currentConfig = await videoConfigStore.getProvider(provider);
|
|
133
|
+
if (currentConfig && config.apiKey && config.apiKey.startsWith('***')) {
|
|
134
|
+
config.apiKey = currentConfig.apiKey;
|
|
135
|
+
}
|
|
136
|
+
await videoConfigStore.updateProvider(provider, config);
|
|
137
|
+
res.json({ ok: true });
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
res.status(500).json({ error: err.message });
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
// 测试视频供应商连接
|
|
144
|
+
app.post('/api/video-test', async (req, res) => {
|
|
145
|
+
try {
|
|
146
|
+
const { provider } = req.body;
|
|
147
|
+
if (!provider) {
|
|
148
|
+
return res.status(400).json({ error: 'provider required' });
|
|
149
|
+
}
|
|
150
|
+
const result = await videoConfigStore.testProvider(provider);
|
|
151
|
+
res.json(result);
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
res.status(500).json({ error: err.message });
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
// ==================== 音频生成配置 (TTS / Music) ====================
|
|
158
|
+
// 获取音频配置
|
|
159
|
+
app.get('/api/audio-config', async (req, res) => {
|
|
160
|
+
try {
|
|
161
|
+
const config = await audioConfigStore.getConfig();
|
|
162
|
+
const providerInfo = audioConfigStore.getAllProviderInfo();
|
|
163
|
+
const masked = Object.fromEntries(Object.entries(config.providers).map(([key, val]) => [
|
|
164
|
+
key,
|
|
165
|
+
{ ...val, apiKey: val.apiKey ? '***' + val.apiKey.slice(-4) : '' }
|
|
166
|
+
]));
|
|
167
|
+
res.json({
|
|
168
|
+
activeProvider: config.activeProvider,
|
|
169
|
+
providers: masked,
|
|
170
|
+
providerInfo
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
catch (err) {
|
|
174
|
+
res.status(500).json({ error: err.message });
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
// 更新音频供应商配置
|
|
178
|
+
app.post('/api/audio-config', async (req, res) => {
|
|
179
|
+
try {
|
|
180
|
+
const { provider, config } = req.body;
|
|
181
|
+
if (!provider || !config) {
|
|
182
|
+
return res.status(400).json({ error: 'provider and config required' });
|
|
183
|
+
}
|
|
184
|
+
// 掩码回写真实 key
|
|
185
|
+
const currentConfig = await audioConfigStore.getProvider(provider);
|
|
186
|
+
if (currentConfig && config.apiKey && config.apiKey.startsWith('***')) {
|
|
187
|
+
config.apiKey = currentConfig.apiKey;
|
|
188
|
+
}
|
|
189
|
+
await audioConfigStore.updateProvider(provider, config);
|
|
190
|
+
res.json({ ok: true });
|
|
191
|
+
}
|
|
192
|
+
catch (err) {
|
|
193
|
+
res.status(500).json({ error: err.message });
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
// 测试音频供应商连接
|
|
197
|
+
app.post('/api/audio-test', async (req, res) => {
|
|
198
|
+
try {
|
|
199
|
+
const { provider } = req.body;
|
|
200
|
+
if (!provider) {
|
|
201
|
+
return res.status(400).json({ error: 'provider required' });
|
|
202
|
+
}
|
|
203
|
+
const result = await audioConfigStore.testProvider(provider);
|
|
204
|
+
res.json(result);
|
|
205
|
+
}
|
|
206
|
+
catch (err) {
|
|
207
|
+
res.status(500).json({ error: err.message });
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
// 统一 AI 解析入口:CLI / 接收方节点 调这里完成 LLM + judgment + harness
|
|
211
|
+
// 入参: { text, mimeType, fileName, fromNodeId, source }
|
|
212
|
+
// 出参: { summary, qualityScore, judgmentId?, gateArtifact? }
|
|
213
|
+
app.post('/api/ai-parse', async (req, res) => {
|
|
214
|
+
try {
|
|
215
|
+
const { text, mimeType, fileName, fromNodeId, source } = req.body || {};
|
|
216
|
+
if (!text || !fileName) {
|
|
217
|
+
return res.status(400).json({ error: 'text and fileName required' });
|
|
218
|
+
}
|
|
219
|
+
const truncated = text.length > 6000 ? text.substring(0, 6000) + '...[截断]' : text;
|
|
220
|
+
const prompt = `请分析以下 ${mimeType || 'text'} 文档,并给出 (1) 一句话中文摘要 (2) 三个关键要点 (3) 质量评分(0-1)。\n\n文件名: ${fileName}\n\n内容:\n${truncated}`;
|
|
221
|
+
// 1. LLM 解析
|
|
222
|
+
const llm = getMinimax();
|
|
223
|
+
const t0 = Date.now();
|
|
224
|
+
const llmResult = await llm.summarize(prompt);
|
|
225
|
+
const dt = Date.now() - t0;
|
|
226
|
+
const out = {
|
|
227
|
+
ok: true,
|
|
228
|
+
summary: llmResult.summary,
|
|
229
|
+
qualityScore: llmResult.qualityScore,
|
|
230
|
+
latencyMs: dt,
|
|
231
|
+
mimeType: mimeType || 'text/plain',
|
|
232
|
+
fileName,
|
|
233
|
+
};
|
|
234
|
+
// 2. 蒸馏为 judgment (异步,失败不影响主返回)
|
|
235
|
+
try {
|
|
236
|
+
const judgmentMod = await import('../pi-ecosystem-judgment/index.js');
|
|
237
|
+
await judgmentMod.initializeJudgmentStore();
|
|
238
|
+
const j = await judgmentMod.createJudgment({
|
|
239
|
+
type: 'trajectory',
|
|
240
|
+
content: `AI 解析 ${fileName}: ${llmResult.summary.slice(0, 200)}`,
|
|
241
|
+
source: 'agent',
|
|
242
|
+
confidence: Math.min(1, llmResult.qualityScore),
|
|
243
|
+
context: `ai-parse:${mimeType || 'text'}:${source || 'p2p'}`,
|
|
244
|
+
evidence: {
|
|
245
|
+
trajectory: [{
|
|
246
|
+
timestamp: new Date().toISOString(),
|
|
247
|
+
action: `parse:${fileName}`,
|
|
248
|
+
outcome: `score=${llmResult.qualityScore.toFixed(2)}`,
|
|
249
|
+
approved: true,
|
|
250
|
+
}],
|
|
251
|
+
},
|
|
252
|
+
});
|
|
253
|
+
out.judgmentId = j.id;
|
|
254
|
+
}
|
|
255
|
+
catch (e) {
|
|
256
|
+
out.judgmentError = e.message;
|
|
257
|
+
}
|
|
258
|
+
// 3. 在 harness 落产物 (异步,失败不影响)
|
|
259
|
+
try {
|
|
260
|
+
const harnessMod = await import('../bollharness-integration/index.js');
|
|
261
|
+
const gate = new harnessMod.GateStateMachine();
|
|
262
|
+
gate.submitArtifact(`ai-parse:${fileName}`, {
|
|
263
|
+
summary: llmResult.summary,
|
|
264
|
+
score: llmResult.qualityScore,
|
|
265
|
+
fromNodeId: fromNodeId || null,
|
|
266
|
+
parsedAt: Date.now(),
|
|
267
|
+
});
|
|
268
|
+
out.gateArtifact = `ai-parse:${fileName}`;
|
|
269
|
+
}
|
|
270
|
+
catch (e) {
|
|
271
|
+
out.gateError = e.message;
|
|
272
|
+
}
|
|
273
|
+
res.json(out);
|
|
274
|
+
}
|
|
275
|
+
catch (err) {
|
|
276
|
+
res.status(500).json({ error: err.message });
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* routes-tasks.ts — 任务队列 CRUD 路由 (2026-07-06 抽出)
|
|
3
|
+
*
|
|
4
|
+
* 从 src/web/server.ts 抽出 (~160 行).
|
|
5
|
+
* 包含 /api/tasks/* (GET, POST, PATCH, DELETE, execute, execute-next)
|
|
6
|
+
*/
|
|
7
|
+
import { loadTaskQueue, saveTaskQueue, isTaskExecuting } from './server-storage.js';
|
|
8
|
+
import { documentReader } from '../documents/reader.js';
|
|
9
|
+
export function registerTaskRoutes(app, opts) {
|
|
10
|
+
const { broadcast, getAgentForChannel } = opts;
|
|
11
|
+
// 获取所有任务
|
|
12
|
+
app.get('/api/tasks', async (req, res) => {
|
|
13
|
+
try {
|
|
14
|
+
const tasks = await loadTaskQueue();
|
|
15
|
+
res.json(tasks);
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
res.status(500).json({ error: err.message });
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
// 创建新任务
|
|
22
|
+
app.post('/api/tasks', async (req, res) => {
|
|
23
|
+
try {
|
|
24
|
+
const { type, title, description, steps } = req.body;
|
|
25
|
+
if (!type || !title) {
|
|
26
|
+
return res.status(400).json({ error: 'type and title required' });
|
|
27
|
+
}
|
|
28
|
+
const tasks = await loadTaskQueue();
|
|
29
|
+
const task = {
|
|
30
|
+
id: `task_${Date.now()}_${Math.random().toString(36).substring(2, 8)}`,
|
|
31
|
+
type,
|
|
32
|
+
title,
|
|
33
|
+
description,
|
|
34
|
+
status: 'pending',
|
|
35
|
+
progress: 0,
|
|
36
|
+
createdAt: new Date().toISOString(),
|
|
37
|
+
updatedAt: new Date().toISOString(),
|
|
38
|
+
steps: steps?.map((s, i) => ({
|
|
39
|
+
id: `step_${i}`,
|
|
40
|
+
name: s,
|
|
41
|
+
status: 'pending'
|
|
42
|
+
}))
|
|
43
|
+
};
|
|
44
|
+
tasks.push(task);
|
|
45
|
+
await saveTaskQueue(tasks);
|
|
46
|
+
res.json(task);
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
res.status(500).json({ error: err.message });
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
// 获取单个任务
|
|
53
|
+
app.get('/api/tasks/:taskId', async (req, res) => {
|
|
54
|
+
try {
|
|
55
|
+
const { taskId } = req.params;
|
|
56
|
+
const tasks = await loadTaskQueue();
|
|
57
|
+
const task = tasks.find(t => t.id === taskId);
|
|
58
|
+
if (!task) {
|
|
59
|
+
return res.status(404).json({ error: 'Task not found' });
|
|
60
|
+
}
|
|
61
|
+
res.json(task);
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
res.status(500).json({ error: err.message });
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
// 更新任务
|
|
68
|
+
app.patch('/api/tasks/:taskId', async (req, res) => {
|
|
69
|
+
try {
|
|
70
|
+
const { taskId } = req.params;
|
|
71
|
+
const { status, currentStep } = req.body;
|
|
72
|
+
const tasks = await loadTaskQueue();
|
|
73
|
+
const taskIndex = tasks.findIndex(t => t.id === taskId);
|
|
74
|
+
if (taskIndex === -1) {
|
|
75
|
+
return res.status(404).json({ error: 'Task not found' });
|
|
76
|
+
}
|
|
77
|
+
if (status) {
|
|
78
|
+
tasks[taskIndex].status = status;
|
|
79
|
+
}
|
|
80
|
+
if (currentStep !== undefined) {
|
|
81
|
+
tasks[taskIndex].currentStep = currentStep;
|
|
82
|
+
}
|
|
83
|
+
tasks[taskIndex].updatedAt = new Date().toISOString();
|
|
84
|
+
await saveTaskQueue(tasks);
|
|
85
|
+
res.json(tasks[taskIndex]);
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
res.status(500).json({ error: err.message });
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
// 删除任务
|
|
92
|
+
app.delete('/api/tasks/:taskId', async (req, res) => {
|
|
93
|
+
try {
|
|
94
|
+
const { taskId } = req.params;
|
|
95
|
+
const tasks = await loadTaskQueue();
|
|
96
|
+
const filtered = tasks.filter(t => t.id !== taskId);
|
|
97
|
+
await saveTaskQueue(filtered);
|
|
98
|
+
res.json({ ok: true });
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
res.status(500).json({ error: err.message });
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
// 执行任务(自动执行下一步)
|
|
105
|
+
app.post('/api/tasks/:taskId/execute', async (req, res) => {
|
|
106
|
+
try {
|
|
107
|
+
const { taskId } = req.params;
|
|
108
|
+
const { channelId } = req.body;
|
|
109
|
+
if (!channelId) {
|
|
110
|
+
return res.status(400).json({ error: 'channelId required' });
|
|
111
|
+
}
|
|
112
|
+
const tasks = await loadTaskQueue();
|
|
113
|
+
const task = tasks.find(t => t.id === taskId);
|
|
114
|
+
if (!task) {
|
|
115
|
+
return res.status(404).json({ error: 'Task not found' });
|
|
116
|
+
}
|
|
117
|
+
if (isTaskExecuting()) {
|
|
118
|
+
return res.status(409).json({ error: 'Another task is currently executing' });
|
|
119
|
+
}
|
|
120
|
+
// 异步执行任务
|
|
121
|
+
executeTask(task, channelId, getAgentForChannel, broadcast);
|
|
122
|
+
res.json({ ok: true, taskId: task.id });
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
res.status(500).json({ error: err.message });
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
// 执行下一个待处理任务
|
|
129
|
+
app.post('/api/tasks/execute-next', async (req, res) => {
|
|
130
|
+
try {
|
|
131
|
+
const { channelId } = req.body;
|
|
132
|
+
if (!channelId) {
|
|
133
|
+
return res.status(400).json({ error: 'channelId required' });
|
|
134
|
+
}
|
|
135
|
+
const tasks = await loadTaskQueue();
|
|
136
|
+
const nextTask = tasks.find(t => t.status === 'pending');
|
|
137
|
+
if (!nextTask) {
|
|
138
|
+
return res.json({ ok: false, message: 'No pending tasks' });
|
|
139
|
+
}
|
|
140
|
+
if (isTaskExecuting()) {
|
|
141
|
+
return res.status(409).json({ error: 'Another task is currently executing' });
|
|
142
|
+
}
|
|
143
|
+
// 异步执行任务
|
|
144
|
+
executeTask(nextTask, channelId, getAgentForChannel, broadcast);
|
|
145
|
+
res.json({ ok: true, taskId: nextTask.id });
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
res.status(500).json({ error: err.message });
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
// ==================== Task Execution ====================
|
|
153
|
+
async function executeTask(task, channelId, getAgentForChannel, broadcast) {
|
|
154
|
+
// 使用 server-storage 的 startTaskExecution 做锁
|
|
155
|
+
const { startTaskExecution, endTaskExecution } = await import('./server-storage.js');
|
|
156
|
+
if (!startTaskExecution(task.id))
|
|
157
|
+
return;
|
|
158
|
+
const agent = await getAgentForChannel(channelId);
|
|
159
|
+
const tasks = await loadTaskQueue();
|
|
160
|
+
const taskIndex = tasks.findIndex(t => t.id === task.id);
|
|
161
|
+
if (taskIndex >= 0) {
|
|
162
|
+
tasks[taskIndex].status = 'running';
|
|
163
|
+
tasks[taskIndex].updatedAt = new Date().toISOString();
|
|
164
|
+
await saveTaskQueue(tasks);
|
|
165
|
+
}
|
|
166
|
+
broadcast({ type: 'task_status', taskId: task.id, status: 'running', progress: 0 }, channelId);
|
|
167
|
+
try {
|
|
168
|
+
let result = '';
|
|
169
|
+
switch (task.type) {
|
|
170
|
+
case 'chat':
|
|
171
|
+
if (task.description) {
|
|
172
|
+
broadcast({ type: 'status', content: `执行任务: ${task.title}` }, channelId);
|
|
173
|
+
result = await agent.prompt(task.description);
|
|
174
|
+
}
|
|
175
|
+
break;
|
|
176
|
+
case 'read':
|
|
177
|
+
if (task.description) {
|
|
178
|
+
broadcast({ type: 'status', content: `读取文档: ${task.description}` }, channelId);
|
|
179
|
+
const content = await documentReader.read(task.description);
|
|
180
|
+
result = `📄 文档读取完成\n\n${content.text.substring(0, 500)}${content.text.length > 500 ? '...' : ''}`;
|
|
181
|
+
}
|
|
182
|
+
break;
|
|
183
|
+
case 'summarize':
|
|
184
|
+
if (task.description) {
|
|
185
|
+
broadcast({ type: 'status', content: `总结文档: ${task.description}` }, channelId);
|
|
186
|
+
const content = await documentReader.read(task.description);
|
|
187
|
+
const { getMinimax } = await import('../constraints/index.js');
|
|
188
|
+
const llm = getMinimax();
|
|
189
|
+
const summary = await llm.summarize(content.text);
|
|
190
|
+
result = `📝 文档总结:\n\n${summary.summary}`;
|
|
191
|
+
}
|
|
192
|
+
break;
|
|
193
|
+
default:
|
|
194
|
+
result = '未知任务类型';
|
|
195
|
+
}
|
|
196
|
+
// 更新任务状态
|
|
197
|
+
const tasks = await loadTaskQueue();
|
|
198
|
+
const idx = tasks.findIndex(t => t.id === task.id);
|
|
199
|
+
if (idx >= 0) {
|
|
200
|
+
tasks[idx].status = 'completed';
|
|
201
|
+
tasks[idx].progress = 100;
|
|
202
|
+
tasks[idx].result = result;
|
|
203
|
+
tasks[idx].updatedAt = new Date().toISOString();
|
|
204
|
+
await saveTaskQueue(tasks);
|
|
205
|
+
}
|
|
206
|
+
broadcast({ type: 'task_status', taskId: task.id, status: 'completed', progress: 100, result }, channelId);
|
|
207
|
+
broadcast({ type: 'ai', content: result }, channelId);
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
const tasks = await loadTaskQueue();
|
|
211
|
+
const idx = tasks.findIndex(t => t.id === task.id);
|
|
212
|
+
if (idx >= 0) {
|
|
213
|
+
tasks[idx].status = 'failed';
|
|
214
|
+
tasks[idx].error = error.message;
|
|
215
|
+
tasks[idx].updatedAt = new Date().toISOString();
|
|
216
|
+
await saveTaskQueue(tasks);
|
|
217
|
+
}
|
|
218
|
+
broadcast({ type: 'task_status', taskId: task.id, status: 'failed', error: error.message }, channelId);
|
|
219
|
+
broadcast({ type: 'error', content: `任务执行失败: ${error.message}` }, channelId);
|
|
220
|
+
}
|
|
221
|
+
endTaskExecution();
|
|
222
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* server-sse.ts — server.ts 拆出的 SSE 实时推送层
|
|
3
|
+
*
|
|
4
|
+
* 包含:
|
|
5
|
+
* - sseClients: 客户端连接 Set
|
|
6
|
+
* - broadcast(): 推 SSE 事件 + 加 seq/msgId envelope
|
|
7
|
+
* - nextEventSeq / nextMsgId: 用于客户端断线重连 resume
|
|
8
|
+
* - installChatBusHook / installSelfImproveHook: 桥接 chat event bus / self-improve bus → SSE
|
|
9
|
+
*
|
|
10
|
+
* 从 src/web/server.ts 抽出 (2026-07-06).
|
|
11
|
+
* SSEClient type 来自 ./server-types.ts.
|
|
12
|
+
*/
|
|
13
|
+
import * as crypto from 'crypto';
|
|
14
|
+
const sseClients = new Set();
|
|
15
|
+
export function getSseClients() {
|
|
16
|
+
return sseClients;
|
|
17
|
+
}
|
|
18
|
+
export function addSseClient(client) {
|
|
19
|
+
sseClients.add(client);
|
|
20
|
+
}
|
|
21
|
+
export function removeSseClient(client) {
|
|
22
|
+
sseClients.delete(client);
|
|
23
|
+
}
|
|
24
|
+
// 2026-07-06: 每个 channelId 维护递增 sequence + msgId, 让前端能去重 + 重连后 resume
|
|
25
|
+
const channelEventSeq = new Map();
|
|
26
|
+
const channelMsgIds = new Map(); // channelId -> 上一条 msgId (uuid)
|
|
27
|
+
export function nextEventSeq(channelId) {
|
|
28
|
+
if (!channelId)
|
|
29
|
+
return 0;
|
|
30
|
+
const cur = channelEventSeq.get(channelId) || 0;
|
|
31
|
+
const next = cur + 1;
|
|
32
|
+
channelEventSeq.set(channelId, next);
|
|
33
|
+
return next;
|
|
34
|
+
}
|
|
35
|
+
export function nextMsgId(channelId) {
|
|
36
|
+
const id = `msg_${Date.now()}_${crypto.randomBytes(4).toString('hex')}`;
|
|
37
|
+
if (channelId)
|
|
38
|
+
channelMsgIds.set(channelId, id);
|
|
39
|
+
return id;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* 给 watchdog 喂活动, 让 broadcast() / 模块级业务函数能埋点喂活动
|
|
43
|
+
* 之前在 createWebServer 闭包内, 闭包外的 broadcast() 拿不到 → 误判 30min 无活动 → 自杀.
|
|
44
|
+
*/
|
|
45
|
+
let watchdogRef = null;
|
|
46
|
+
export function setWatchdogRef(ref) {
|
|
47
|
+
watchdogRef = ref;
|
|
48
|
+
}
|
|
49
|
+
export function broadcast(data, channelId) {
|
|
50
|
+
watchdogRef?.recordActivity?.();
|
|
51
|
+
const seq = nextEventSeq(channelId);
|
|
52
|
+
const msgId = (data.type === 'ai' || data.type === 'user')
|
|
53
|
+
? nextMsgId(channelId)
|
|
54
|
+
: `evt_${Date.now()}_${crypto.randomBytes(4).toString('hex')}`;
|
|
55
|
+
const envelope = { ...data, channelId, seq, msgId };
|
|
56
|
+
const message = `data: ${JSON.stringify(envelope)}\n\n`;
|
|
57
|
+
console.log(`[broadcast] type=${data.type}, channelId=${channelId}, seq=${seq}, msgId=${msgId}, clients=${sseClients.size}`);
|
|
58
|
+
for (const client of sseClients) {
|
|
59
|
+
if (!channelId || client.channelId === channelId) {
|
|
60
|
+
try {
|
|
61
|
+
client.res.write(message);
|
|
62
|
+
}
|
|
63
|
+
catch (e) {
|
|
64
|
+
console.error(`[broadcast] 写入失败:`, e.message);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// ============================================================================
|
|
70
|
+
// Chat 事件总线 -> SSE 桥 (供前端 inbox UI 用)
|
|
71
|
+
// ============================================================================
|
|
72
|
+
let chatBusHookInstalled = false;
|
|
73
|
+
export async function installChatBusHook() {
|
|
74
|
+
if (chatBusHookInstalled)
|
|
75
|
+
return;
|
|
76
|
+
chatBusHookInstalled = true;
|
|
77
|
+
try {
|
|
78
|
+
const { chatEventBus } = await import('../agents/p2p-chat-tools.js');
|
|
79
|
+
chatEventBus.on('chat', (ev) => {
|
|
80
|
+
broadcast({ type: 'chat_event', chatKind: ev.kind, payload: ev }, undefined);
|
|
81
|
+
});
|
|
82
|
+
console.log('[chat-bus] SSE bridge installed');
|
|
83
|
+
}
|
|
84
|
+
catch (e) {
|
|
85
|
+
console.warn('[chat-bus] install failed:', e.message);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// ============================================================================
|
|
89
|
+
// Self-Improve Bus -> SSE 桥 (供前端 / 用户看到自改触发)
|
|
90
|
+
// ============================================================================
|
|
91
|
+
let selfImproveHookInstalled = false;
|
|
92
|
+
export async function installSelfImproveHook() {
|
|
93
|
+
if (selfImproveHookInstalled)
|
|
94
|
+
return;
|
|
95
|
+
selfImproveHookInstalled = true;
|
|
96
|
+
try {
|
|
97
|
+
const { onSelfImproveTrigger } = await import('../heartbeat/self-improve-bus.js');
|
|
98
|
+
const { runSelfImproveLoop } = await import('../agents/pi-sdk.js');
|
|
99
|
+
onSelfImproveTrigger(async (event, goal) => {
|
|
100
|
+
broadcast({
|
|
101
|
+
type: 'self_improve_triggered',
|
|
102
|
+
eventKind: event.kind,
|
|
103
|
+
details: event.details,
|
|
104
|
+
goal,
|
|
105
|
+
ts: Date.now()
|
|
106
|
+
}, undefined);
|
|
107
|
+
const result = await runSelfImproveLoop(goal);
|
|
108
|
+
broadcast({
|
|
109
|
+
type: 'self_improve_result',
|
|
110
|
+
success: result.success,
|
|
111
|
+
output: result.output,
|
|
112
|
+
error: result.error,
|
|
113
|
+
ts: Date.now()
|
|
114
|
+
}, undefined);
|
|
115
|
+
});
|
|
116
|
+
console.log('[self-improve] SSE bridge installed');
|
|
117
|
+
}
|
|
118
|
+
catch (e) {
|
|
119
|
+
console.warn('[self-improve] install failed:', e.message);
|
|
120
|
+
}
|
|
121
|
+
}
|