@bolloon/bolloon-agent 0.2.12 → 0.2.13
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 +24 -8
- package/dist/llm/llm-judgment-client.js +102 -102
- package/dist/llm/pi-ai.js +137 -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,738 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* routes-judgments.ts — judgments / self-improve / permission-mode 路由 (2026-07-06 抽出)
|
|
3
|
+
*
|
|
4
|
+
* 从 src/web/server.ts 抽出 (~750 行).
|
|
5
|
+
* 不依赖 createWebServer 闭包状态 — 所有依赖从外部注入 (currentChannelId 走 query / body).
|
|
6
|
+
*
|
|
7
|
+
* 注册路由:
|
|
8
|
+
* POST /api/judgments
|
|
9
|
+
* GET /api/judgments
|
|
10
|
+
* POST /api/judgments/distill-from-conversation
|
|
11
|
+
* POST /api/judgments/detect-and-distill
|
|
12
|
+
* POST /api/judgments/resolve-usage
|
|
13
|
+
* GET /api/judgments/violations
|
|
14
|
+
* GET /api/judgments/adaptive-suggestions
|
|
15
|
+
* POST /api/judgments/adaptive-apply
|
|
16
|
+
* GET /api/judgments/evolution-log
|
|
17
|
+
* GET /api/judgments/causal/correlation
|
|
18
|
+
* GET /api/judgments/causal/intervention
|
|
19
|
+
* POST /api/judgments/causal/counterfactual
|
|
20
|
+
* GET /api/judgments/causal/audit-log
|
|
21
|
+
* POST /api/judgments/import
|
|
22
|
+
* PATCH /api/judgments/:id
|
|
23
|
+
* DELETE /api/judgments/:id
|
|
24
|
+
* POST /api/judgments/batch-delete
|
|
25
|
+
* POST /api/judgments/auto-delegate
|
|
26
|
+
*/
|
|
27
|
+
import * as path from 'path';
|
|
28
|
+
import * as os from 'os';
|
|
29
|
+
import * as fs from 'fs/promises';
|
|
30
|
+
import { loadChannels, loadSession } from './server-storage.js';
|
|
31
|
+
export function registerJudgmentsRoutes(app) {
|
|
32
|
+
// ==================== Judgments (v1 核心: 让我能记录判断) ====================
|
|
33
|
+
// POST /api/judgments — 记录一个判断
|
|
34
|
+
// GET /api/judgments — 列出所有判断 (新→旧)
|
|
35
|
+
// 存储: ~/.bolloon/human-values/judgments.json (human-value-store)
|
|
36
|
+
// 极简版: 只记录 decision + reason; 其它字段可选
|
|
37
|
+
app.post('/api/judgments', async (req, res) => {
|
|
38
|
+
try {
|
|
39
|
+
const { decision, reason, context } = req.body;
|
|
40
|
+
if (!decision || typeof decision !== 'string' || !decision.trim()) {
|
|
41
|
+
return res.status(400).json({ error: 'decision required' });
|
|
42
|
+
}
|
|
43
|
+
const { storeHumanJudgment, initializeValueStore } = await import('../pi-ecosystem-judgment/human-value-store.js');
|
|
44
|
+
await initializeValueStore();
|
|
45
|
+
const j = await storeHumanJudgment({
|
|
46
|
+
decision: decision.trim(),
|
|
47
|
+
decision_type: 'approve',
|
|
48
|
+
reasons: reason ? [reason.trim()] : [],
|
|
49
|
+
values_derived: [],
|
|
50
|
+
context: {
|
|
51
|
+
domain: context?.domain || 'general',
|
|
52
|
+
complexity: 'moderate',
|
|
53
|
+
stakes: context?.stakes || 'medium',
|
|
54
|
+
time_pressure: 'low',
|
|
55
|
+
},
|
|
56
|
+
metadata: {
|
|
57
|
+
source: 'explicit',
|
|
58
|
+
confidence: 0.8,
|
|
59
|
+
revisable: true,
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
res.json({ ok: true, judgment: j });
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
console.error('[judgments] POST failed:', err);
|
|
66
|
+
res.status(500).json({ error: err.message });
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
app.get('/api/judgments', async (req, res) => {
|
|
70
|
+
try {
|
|
71
|
+
const { listJudgmentsByStatus, initializeValueStore } = await import('../pi-ecosystem-judgment/human-value-store.js');
|
|
72
|
+
await initializeValueStore();
|
|
73
|
+
const status = (typeof req.query.status === 'string' ? req.query.status : 'all');
|
|
74
|
+
const all = await listJudgmentsByStatus(status);
|
|
75
|
+
all.sort((a, b) => (b.timestamp || '').localeCompare(a.timestamp || ''));
|
|
76
|
+
res.json({ count: all.length, status, judgments: all });
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
console.error('[judgments] GET failed:', err);
|
|
80
|
+
res.status(500).json({ error: err.message });
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
// 2026-07-06: 清测试灌水数据 — dry (报告) + run (实际写盘) 两端点
|
|
84
|
+
app.get('/api/judgments/cleanup-dry', async (_req, res) => {
|
|
85
|
+
try {
|
|
86
|
+
const { dryCleanup } = await import('../pi-ecosystem-judgment/cleanup.js');
|
|
87
|
+
const r = await dryCleanup();
|
|
88
|
+
res.json({ ok: true, ...r });
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
console.error('[judgments] cleanup-dry failed:', err);
|
|
92
|
+
res.status(500).json({ error: err.message });
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
app.post('/api/judgments/cleanup', async (_req, res) => {
|
|
96
|
+
try {
|
|
97
|
+
const { runCleanup } = await import('../pi-ecosystem-judgment/cleanup.js');
|
|
98
|
+
const r = await runCleanup();
|
|
99
|
+
res.json({ ok: true, ...r });
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
console.error('[judgments] cleanup failed:', err);
|
|
103
|
+
res.status(500).json({ error: err.message });
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
// 蒸馏 B 触发 (人类点按钮) — 同步执行演化对齐
|
|
107
|
+
app.post('/api/judgments/distill-from-conversation', async (req, res) => {
|
|
108
|
+
try {
|
|
109
|
+
const { channelId, messageId, recentTurns } = req.body;
|
|
110
|
+
if (!channelId) {
|
|
111
|
+
return res.status(400).json({ error: 'channelId required' });
|
|
112
|
+
}
|
|
113
|
+
// 取 channel 最近的对话
|
|
114
|
+
const channels = await loadChannels();
|
|
115
|
+
const channel = channels.find((c) => c.id === channelId);
|
|
116
|
+
if (!channel)
|
|
117
|
+
return res.status(404).json({ error: 'channel not found' });
|
|
118
|
+
const currentSessionId = channel.currentSessionId;
|
|
119
|
+
if (!currentSessionId) {
|
|
120
|
+
return res.status(400).json({ error: 'no active session in channel' });
|
|
121
|
+
}
|
|
122
|
+
const session = await loadSession(channelId, currentSessionId);
|
|
123
|
+
if (!session)
|
|
124
|
+
return res.status(404).json({ error: 'session not found' });
|
|
125
|
+
// 取最近 N 轮 (默认 10), 转成 DistillTurn 格式
|
|
126
|
+
const limit = Math.min(Math.max(recentTurns ?? 10, 2), 30);
|
|
127
|
+
const turns = session.messages.slice(-limit).map((m) => ({
|
|
128
|
+
role: (m.type === 'user' ? 'human' : 'agent'),
|
|
129
|
+
content: m.content,
|
|
130
|
+
}));
|
|
131
|
+
const { distillAndStoreFromChannel } = await import('../pi-ecosystem-judgment/human-value-pipeline.js');
|
|
132
|
+
const result = await distillAndStoreFromChannel(turns, { channelId });
|
|
133
|
+
res.json({
|
|
134
|
+
ok: true,
|
|
135
|
+
triggered: result.triggered,
|
|
136
|
+
reason: result.reason,
|
|
137
|
+
judgment: result.judgment,
|
|
138
|
+
evolved: result.evolved,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
console.error('[judgments] distill-from-conversation failed:', err);
|
|
143
|
+
res.status(500).json({ error: err.message });
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
// 蒸馏 D 触发 (AI 被动) — 后台异步,不阻塞 HTTP 响应
|
|
147
|
+
app.post('/api/judgments/detect-and-distill', async (req, res) => {
|
|
148
|
+
try {
|
|
149
|
+
const { channelId, turns } = req.body;
|
|
150
|
+
// 先立即返回 202, 不等 LLM
|
|
151
|
+
res.status(202).json({ ok: true, queued: true });
|
|
152
|
+
if (!channelId || !Array.isArray(turns) || turns.length === 0) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
// 异步处理 (不 await, 不阻塞响应)
|
|
156
|
+
setImmediate(async () => {
|
|
157
|
+
try {
|
|
158
|
+
const { detectAndDistillFromChannel } = await import('../pi-ecosystem-judgment/human-value-pipeline.js');
|
|
159
|
+
const result = await detectAndDistillFromChannel(turns, { channelId });
|
|
160
|
+
if (result.triggered) {
|
|
161
|
+
console.log(`[D-hook] ${channelId}: ${result.reason}`, result.evolved);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
catch (err) {
|
|
165
|
+
console.warn('[D-hook] background failed:', err);
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
catch (err) {
|
|
170
|
+
console.error('[judgments] detect-and-distill failed:', err);
|
|
171
|
+
res.status(500).json({ error: err.message });
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
// 判断力使用回溯 (P0.5): 给定 judgmentIds, 反查对应的 decision 文本
|
|
175
|
+
// 用途: UI 上"这条 AI 回复引用了哪些原则"
|
|
176
|
+
app.post('/api/judgments/resolve-usage', async (req, res) => {
|
|
177
|
+
try {
|
|
178
|
+
const { ids } = req.body;
|
|
179
|
+
if (!Array.isArray(ids) || ids.length === 0) {
|
|
180
|
+
return res.json({ items: [] });
|
|
181
|
+
}
|
|
182
|
+
const { loadAllJudgments } = await import('../pi-ecosystem-judgment/human-value-store.js');
|
|
183
|
+
const all = await loadAllJudgments();
|
|
184
|
+
const byId = new Map(all.map((j) => [j.id, j]));
|
|
185
|
+
const items = ids
|
|
186
|
+
.map((id) => byId.get(id))
|
|
187
|
+
.filter((j) => Boolean(j))
|
|
188
|
+
.map((j) => ({
|
|
189
|
+
id: j.id,
|
|
190
|
+
decision: j.decision,
|
|
191
|
+
status: j.status ?? 'active',
|
|
192
|
+
timestamp: j.timestamp,
|
|
193
|
+
}));
|
|
194
|
+
res.json({ items });
|
|
195
|
+
}
|
|
196
|
+
catch (err) {
|
|
197
|
+
console.error('[judgments] resolve-usage failed:', err);
|
|
198
|
+
res.status(500).json({ error: err.message });
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
// 判断力违规日志 (P3 UI): 读 violations.jsonl
|
|
202
|
+
app.get('/api/judgments/violations', async (req, res) => {
|
|
203
|
+
try {
|
|
204
|
+
const { getRecentViolations } = await import('../pi-ecosystem-judgment/monitor-gate.js');
|
|
205
|
+
const limit = Math.min(Math.max(parseInt(String(req.query.limit ?? '20'), 10) || 20, 1), 200);
|
|
206
|
+
const items = await getRecentViolations(limit);
|
|
207
|
+
res.json({ count: items.length, items });
|
|
208
|
+
}
|
|
209
|
+
catch (err) {
|
|
210
|
+
console.error('[judgments] violations failed:', err);
|
|
211
|
+
res.status(500).json({ error: err.message });
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
// 类 B 自适应扫描: 读 judgments.json + usage.jsonl, 给出 stale/rising/unused 建议
|
|
215
|
+
// ?force=1 跳过 24h 缓存
|
|
216
|
+
app.get('/api/judgments/adaptive-suggestions', async (req, res) => {
|
|
217
|
+
try {
|
|
218
|
+
const { getCachedScan } = await import('../pi-ecosystem-judgment/adaptive-scan.js');
|
|
219
|
+
const force = String(req.query.force ?? '') === '1';
|
|
220
|
+
const result = await getCachedScan(force);
|
|
221
|
+
res.json(result);
|
|
222
|
+
}
|
|
223
|
+
catch (err) {
|
|
224
|
+
console.error('[judgments] adaptive-scan failed:', err);
|
|
225
|
+
res.status(500).json({ error: err.message });
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
// Bootstrap Context 调试视图: 返出完整 BolloonContext
|
|
229
|
+
app.get('/api/bolloon/context', async (req, res) => {
|
|
230
|
+
try {
|
|
231
|
+
const { getCachedBolloonContext } = await import('../pi-ecosystem-judgment/human-value-pipeline.js');
|
|
232
|
+
const force = String(req.query.force ?? '') === '1';
|
|
233
|
+
const ctx = await getCachedBolloonContext({ cwd: process.cwd() }, force);
|
|
234
|
+
res.json(ctx);
|
|
235
|
+
}
|
|
236
|
+
catch (err) {
|
|
237
|
+
console.error('[bolloon] context failed:', err);
|
|
238
|
+
res.status(500).json({ error: err.message });
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
// 阶段 B: 周报 (weekly-report.ts 产物) — 仅 API 读取, 不做 UI tab
|
|
242
|
+
// GET /api/reports → { files: ['2026-W24.md', ...] }
|
|
243
|
+
// GET /api/reports/2026-W24 → { week, content }
|
|
244
|
+
app.get('/api/reports', async (_req, res) => {
|
|
245
|
+
try {
|
|
246
|
+
const dir = path.join(os.homedir(), '.bolloon', 'reports');
|
|
247
|
+
try {
|
|
248
|
+
const entries = await fs.readdir(dir);
|
|
249
|
+
const files = entries
|
|
250
|
+
.filter((f) => f.endsWith('.md'))
|
|
251
|
+
.sort()
|
|
252
|
+
.reverse(); // 新的在前
|
|
253
|
+
res.json({ dir, files });
|
|
254
|
+
}
|
|
255
|
+
catch {
|
|
256
|
+
res.json({ dir, files: [] });
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
catch (err) {
|
|
260
|
+
res.status(500).json({ error: err.message });
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
app.get('/api/reports/:week', async (req, res) => {
|
|
264
|
+
try {
|
|
265
|
+
const week = req.params.week;
|
|
266
|
+
// 严格校验, 防路径穿越
|
|
267
|
+
if (!/^\d{4}-W\d{1,2}$/.test(week)) {
|
|
268
|
+
return res.status(400).json({ error: 'week must match YYYY-Www' });
|
|
269
|
+
}
|
|
270
|
+
const file = path.join(os.homedir(), '.bolloon', 'reports', `${week}.md`);
|
|
271
|
+
try {
|
|
272
|
+
const content = await fs.readFile(file, 'utf-8');
|
|
273
|
+
res.json({ week, content, length: content.length });
|
|
274
|
+
}
|
|
275
|
+
catch {
|
|
276
|
+
res.status(404).json({ error: 'not found', week });
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
catch (err) {
|
|
280
|
+
res.status(500).json({ error: err.message });
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
// 阶段 C 护栏 5: auto-evolve baseline 管理 (无 UI, 仅 API)
|
|
284
|
+
// GET /api/auto-evolve/baselines → 列出所有 baseline tag
|
|
285
|
+
// GET /api/auto-evolve/baselines/:tag/diff → 看某 baseline 的 diff 摘要
|
|
286
|
+
// POST /api/auto-evolve/rollback {tag} → 回滚到指定 baseline
|
|
287
|
+
app.get('/api/auto-evolve/baselines', async (_req, res) => {
|
|
288
|
+
try {
|
|
289
|
+
const { execFile } = await import('child_process');
|
|
290
|
+
const { promisify } = await import('util');
|
|
291
|
+
const pExec = promisify(execFile);
|
|
292
|
+
const { stdout } = await pExec('git', [
|
|
293
|
+
'tag', '-l', 'auto-evolve-baseline-*', '--format=%(refname:short)|%(contents)|%(objectname:short)|%(taggerdate:iso)',
|
|
294
|
+
], { cwd: process.cwd() });
|
|
295
|
+
const tags = stdout.trim().split('\n').filter(Boolean).map((line) => {
|
|
296
|
+
const [tag, msg, sha, date] = line.split('|');
|
|
297
|
+
return { tag, message: msg || '', sha, date };
|
|
298
|
+
});
|
|
299
|
+
res.json({ tags, count: tags.length });
|
|
300
|
+
}
|
|
301
|
+
catch (err) {
|
|
302
|
+
res.status(500).json({ error: err.message });
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
app.get('/api/auto-evolve/baselines/:tag/diff', async (req, res) => {
|
|
306
|
+
try {
|
|
307
|
+
const { execFile } = await import('child_process');
|
|
308
|
+
const { promisify } = await import('util');
|
|
309
|
+
const pExec = promisify(execFile);
|
|
310
|
+
const tag = req.params.tag;
|
|
311
|
+
if (!/^auto-evolve-baseline-[\w-]+$/.test(tag)) {
|
|
312
|
+
return res.status(400).json({ error: 'tag must match auto-evolve-baseline-*' });
|
|
313
|
+
}
|
|
314
|
+
const { stdout } = await pExec('git', ['show', '--stat', '--no-color', tag], { cwd: process.cwd() });
|
|
315
|
+
res.json({ tag, diff: stdout.slice(0, 5000) }); // 限长 5KB
|
|
316
|
+
}
|
|
317
|
+
catch (err) {
|
|
318
|
+
res.status(500).json({ error: err.message });
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
// Bootstrap Context → 拼好的 system prompt 片段 (供调试看注入效果)
|
|
322
|
+
app.get('/api/bolloon/context/system-prompt', async (req, res) => {
|
|
323
|
+
try {
|
|
324
|
+
const { getCachedBolloonContext } = await import('../pi-ecosystem-judgment/human-value-pipeline.js');
|
|
325
|
+
const { formatContextForSystemPrompt } = await import('../bootstrap/project-context.js');
|
|
326
|
+
const ctx = await getCachedBolloonContext({ cwd: process.cwd() });
|
|
327
|
+
const systemAddition = formatContextForSystemPrompt(ctx, {
|
|
328
|
+
maxChars: parseInt(String(req.query.max ?? '4000'), 10) || 4000,
|
|
329
|
+
});
|
|
330
|
+
res.json({ systemAddition, length: systemAddition.length, truncated: systemAddition.includes('截断模式') });
|
|
331
|
+
}
|
|
332
|
+
catch (err) {
|
|
333
|
+
console.error('[bolloon] context/system-prompt failed:', err);
|
|
334
|
+
res.status(500).json({ error: err.message });
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
// ============================================================
|
|
338
|
+
// system-prompt health (P-Action 2 — Harness Gardening)
|
|
339
|
+
// 返回每层 lifecycle 状态: ok | stale | overdue-review | missing-frontmatter | dynamic
|
|
340
|
+
// query: ?activeOnly=1 → 只返回当前 context 激活的层
|
|
341
|
+
// ============================================================
|
|
342
|
+
app.get('/api/prompt/health', async (req, res) => {
|
|
343
|
+
try {
|
|
344
|
+
const { listLayers } = await import('../llm/system-prompt/registry.js');
|
|
345
|
+
const { evaluateLayers, markActive } = await import('../llm/system-prompt/health.js');
|
|
346
|
+
const all = listLayers();
|
|
347
|
+
const baseReport = evaluateLayers(all);
|
|
348
|
+
// 如果 query 里有 activeOnly, 跑一次 assembleSystemPrompt 拿激活列表
|
|
349
|
+
if (String(req.query.activeOnly ?? '') === '1') {
|
|
350
|
+
const { assembleSystemPrompt } = await import('../llm/system-prompt/registry.js');
|
|
351
|
+
const channel = String(req.query.channel ?? 'local');
|
|
352
|
+
const role = req.query.role;
|
|
353
|
+
const tool = req.query.tool;
|
|
354
|
+
try {
|
|
355
|
+
const r = await assembleSystemPrompt({ channel, role, tool });
|
|
356
|
+
const activeIds = new Set(r.layerIds);
|
|
357
|
+
res.json(markActive(baseReport, activeIds));
|
|
358
|
+
}
|
|
359
|
+
catch (err) {
|
|
360
|
+
console.warn('[prompt-health] assembleSystemPrompt failed (silent, returning base report):', err);
|
|
361
|
+
res.json(baseReport);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
else {
|
|
365
|
+
res.json(baseReport);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
catch (err) {
|
|
369
|
+
console.error('[prompt-health] failed:', err);
|
|
370
|
+
res.status(500).json({ error: err.message });
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
// 自适应接受/拒绝: 写 evolution.jsonl 留痕, 接受时同时 patch judgments.json
|
|
374
|
+
// body: { action: 'accept'|'reject'|'revert', suggestion, appliedPatch? }
|
|
375
|
+
// query: ?auto=1 → 类 B 自动路径, 受 auto-evolve-policy 网关保护
|
|
376
|
+
// 缺省 → 用户在 UI 手动触发, 不查开关 (避免阻塞用户)
|
|
377
|
+
app.post('/api/judgments/adaptive-apply', async (req, res) => {
|
|
378
|
+
try {
|
|
379
|
+
const isAuto = req.query.auto === '1' || req.query.auto === 'true';
|
|
380
|
+
const { action, suggestion, appliedPatch } = req.body;
|
|
381
|
+
if (!action || !suggestion?.judgmentId) {
|
|
382
|
+
return res.status(400).json({ error: 'action and suggestion.judgmentId required' });
|
|
383
|
+
}
|
|
384
|
+
const { updateJudgmentStatus } = await import('../pi-ecosystem-judgment/human-value-store.js');
|
|
385
|
+
const { logEvolution } = await import('../pi-ecosystem-judgment/adaptive-scan.js');
|
|
386
|
+
// accept 时: 真正改库
|
|
387
|
+
if (action === 'accept') {
|
|
388
|
+
// 阶段 A: 自动路径需先过 auto-evolve-policy 网关
|
|
389
|
+
if (isAuto) {
|
|
390
|
+
const { requireDataLayerAutoEvolve } = await import('../utils/auto-evolve-policy.js');
|
|
391
|
+
try {
|
|
392
|
+
await requireDataLayerAutoEvolve('adaptive-apply.auto.deprecate');
|
|
393
|
+
}
|
|
394
|
+
catch (err) {
|
|
395
|
+
return res.status(423).json({
|
|
396
|
+
error: 'data-layer-auto-evolve-disabled',
|
|
397
|
+
message: err.message,
|
|
398
|
+
hint: '设 BOLLOON_AUTO_EVOLVE_DATA=1 或在 self-improve-policy.json 加 dataLayerAutoEvolve: true',
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
if (suggestion.action === 'deprecate') {
|
|
403
|
+
// 标记 superseded (语义: 不再用, 但保留可回滚)
|
|
404
|
+
await updateJudgmentStatus(suggestion.judgmentId, 'superseded', {
|
|
405
|
+
evolutionReason: 'merged', // 借 merged 字段表达"被自适应废弃"
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
else if (suggestion.action === 'boost') {
|
|
409
|
+
// boost: 用户手动接受后, 不改库本身 (weight 在 getRelevantValues 里动态算),
|
|
410
|
+
// 但写 evolution 留痕, 未来可以基于此调整算法
|
|
411
|
+
// 当前不直接改库, 仅留痕
|
|
412
|
+
}
|
|
413
|
+
// 'review' 类不需要自动改库, 仅 log 接受
|
|
414
|
+
}
|
|
415
|
+
await logEvolution({
|
|
416
|
+
ts: new Date().toISOString(),
|
|
417
|
+
action,
|
|
418
|
+
suggestion: suggestion,
|
|
419
|
+
appliedPatch,
|
|
420
|
+
});
|
|
421
|
+
res.json({ ok: true });
|
|
422
|
+
}
|
|
423
|
+
catch (err) {
|
|
424
|
+
console.error('[judgments] adaptive-apply failed:', err);
|
|
425
|
+
res.status(500).json({ error: err.message });
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
// 演化日志 (audit / 一键回滚源)
|
|
429
|
+
app.get('/api/judgments/evolution-log', async (req, res) => {
|
|
430
|
+
try {
|
|
431
|
+
const { readEvolutionLog } = await import('../pi-ecosystem-judgment/adaptive-scan.js');
|
|
432
|
+
const limit = Math.min(Math.max(parseInt(String(req.query.limit ?? '50'), 10) || 50, 1), 200);
|
|
433
|
+
const items = await readEvolutionLog(limit);
|
|
434
|
+
res.json({ count: items.length, items });
|
|
435
|
+
}
|
|
436
|
+
catch (err) {
|
|
437
|
+
console.error('[judgments] evolution-log failed:', err);
|
|
438
|
+
res.status(500).json({ error: err.message });
|
|
439
|
+
}
|
|
440
|
+
});
|
|
441
|
+
// 阶段 2: Causal-judge 4 个 endpoint
|
|
442
|
+
app.get('/api/judgments/causal/correlation', async (req, res) => {
|
|
443
|
+
try {
|
|
444
|
+
const { runCorrelationAnalysis } = await import('../pi-ecosystem-judgment/human-value-pipeline.js');
|
|
445
|
+
const topN = Math.min(Math.max(parseInt(String(req.query.topN ?? '5'), 10) || 5, 1), 50);
|
|
446
|
+
const useLLM = String(req.query.useLLM ?? '1') !== '0';
|
|
447
|
+
const items = await runCorrelationAnalysis({ topN, useLLM });
|
|
448
|
+
res.json({ count: items.length, items });
|
|
449
|
+
}
|
|
450
|
+
catch (err) {
|
|
451
|
+
console.error('[causal] correlation failed:', err);
|
|
452
|
+
res.status(500).json({ error: err.message });
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
app.get('/api/judgments/causal/intervention', async (req, res) => {
|
|
456
|
+
try {
|
|
457
|
+
const { runIntervention } = await import('../pi-ecosystem-judgment/human-value-pipeline.js');
|
|
458
|
+
const { judgmentId, scenario } = req.query;
|
|
459
|
+
if (!judgmentId)
|
|
460
|
+
return res.status(400).json({ error: 'judgmentId required' });
|
|
461
|
+
const result = await runIntervention(judgmentId, { scenarioContext: scenario });
|
|
462
|
+
res.json(result);
|
|
463
|
+
}
|
|
464
|
+
catch (err) {
|
|
465
|
+
console.error('[causal] intervention failed:', err);
|
|
466
|
+
res.status(500).json({ error: err.message });
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
app.post('/api/judgments/causal/counterfactual', async (req, res) => {
|
|
470
|
+
try {
|
|
471
|
+
const { runCounterfactualAudit } = await import('../pi-ecosystem-judgment/human-value-pipeline.js');
|
|
472
|
+
const { userInput, aiReply, violatedPrinciples } = req.body;
|
|
473
|
+
if (!userInput || !aiReply) {
|
|
474
|
+
return res.status(400).json({ error: 'userInput and aiReply required' });
|
|
475
|
+
}
|
|
476
|
+
const audit = await runCounterfactualAudit({
|
|
477
|
+
userInput,
|
|
478
|
+
aiReply,
|
|
479
|
+
violatedPrinciples: violatedPrinciples ?? [],
|
|
480
|
+
});
|
|
481
|
+
res.json(audit);
|
|
482
|
+
}
|
|
483
|
+
catch (err) {
|
|
484
|
+
console.error('[causal] counterfactual failed:', err);
|
|
485
|
+
res.status(500).json({ error: err.message });
|
|
486
|
+
}
|
|
487
|
+
});
|
|
488
|
+
app.get('/api/judgments/causal/audit-log', async (req, res) => {
|
|
489
|
+
try {
|
|
490
|
+
const { readCounterfactualLog } = await import('../pi-ecosystem-judgment/human-value-pipeline.js');
|
|
491
|
+
const limit = Math.min(Math.max(parseInt(String(req.query.limit ?? '20'), 10) || 20, 1), 200);
|
|
492
|
+
const items = await readCounterfactualLog(limit);
|
|
493
|
+
res.json({ count: items.length, items });
|
|
494
|
+
}
|
|
495
|
+
catch (err) {
|
|
496
|
+
console.error('[causal] audit-log failed:', err);
|
|
497
|
+
res.status(500).json({ error: err.message });
|
|
498
|
+
}
|
|
499
|
+
});
|
|
500
|
+
// 导入判断: 接受 { filename, content (base64), context }.
|
|
501
|
+
// 支持 .json / .yaml / .yml / .md / .txt / .html. 完全离线解析, 不调 LLM.
|
|
502
|
+
// 解析规则:
|
|
503
|
+
// - .json: 顶层数组 [{decision, reason?, context?}, ...] 或 {judgments: [...]} 或 {items: [...]}
|
|
504
|
+
// - .yaml/.yml: 期望顶层数组 (用 js-yaml); 不支持复杂结构
|
|
505
|
+
// - .md/.txt/.html: 每一段 (按空行分隔) 算一条判断, 首行非空 = decision, 整段 = content
|
|
506
|
+
// 如果首行是 markdown 标题 (# ...) 则去掉 #, 整段去掉首行后作 reason
|
|
507
|
+
app.post('/api/judgments/import', async (req, res) => {
|
|
508
|
+
try {
|
|
509
|
+
const { filename, content, context } = req.body;
|
|
510
|
+
if (!filename || !content) {
|
|
511
|
+
return res.status(400).json({ error: 'filename and content (base64) required' });
|
|
512
|
+
}
|
|
513
|
+
let raw;
|
|
514
|
+
try {
|
|
515
|
+
raw = Buffer.from(content, 'base64').toString('utf-8');
|
|
516
|
+
}
|
|
517
|
+
catch {
|
|
518
|
+
return res.status(400).json({ error: 'content is not valid base64' });
|
|
519
|
+
}
|
|
520
|
+
const lower = filename.toLowerCase();
|
|
521
|
+
let items = [];
|
|
522
|
+
if (lower.endsWith('.json')) {
|
|
523
|
+
try {
|
|
524
|
+
const parsed = JSON.parse(raw);
|
|
525
|
+
const arr = Array.isArray(parsed) ? parsed
|
|
526
|
+
: Array.isArray(parsed?.judgments) ? parsed.judgments
|
|
527
|
+
: Array.isArray(parsed?.items) ? parsed.items
|
|
528
|
+
: null;
|
|
529
|
+
if (!arr)
|
|
530
|
+
return res.status(400).json({ error: 'JSON must be an array, or {judgments:[]}/{items:[]}' });
|
|
531
|
+
for (const it of arr) {
|
|
532
|
+
if (it && typeof it.decision === 'string' && it.decision.trim()) {
|
|
533
|
+
items.push({ decision: it.decision.trim(), reason: it.reason, context: it.context });
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
catch (e) {
|
|
538
|
+
return res.status(400).json({ error: 'JSON parse failed: ' + e.message });
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
else if (lower.endsWith('.yaml') || lower.endsWith('.yml')) {
|
|
542
|
+
try {
|
|
543
|
+
const yaml = (await import('js-yaml')).default;
|
|
544
|
+
const parsed = yaml.load(raw);
|
|
545
|
+
if (!Array.isArray(parsed))
|
|
546
|
+
return res.status(400).json({ error: 'YAML must be a top-level array' });
|
|
547
|
+
for (const it of parsed) {
|
|
548
|
+
if (it && typeof it.decision === 'string' && it.decision.trim()) {
|
|
549
|
+
items.push({ decision: it.decision.trim(), reason: it.reason, context: it.context });
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
catch (e) {
|
|
554
|
+
return res.status(400).json({ error: 'YAML parse failed: ' + e.message });
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
else if (lower.endsWith('.md') || lower.endsWith('.txt') || lower.endsWith('.html') || lower.endsWith('.htm')) {
|
|
558
|
+
// 通用纯文本: 按空行分段, 每段是一条判断
|
|
559
|
+
// 对 .html 先剥掉标签, 但保留段落分隔
|
|
560
|
+
let text = raw;
|
|
561
|
+
if (lower.endsWith('.html') || lower.endsWith('.htm')) {
|
|
562
|
+
text = text.replace(/<script[\s\S]*?<\/script>/gi, '')
|
|
563
|
+
.replace(/<style[\s\S]*?<\/style>/gi, '')
|
|
564
|
+
// 块级标签 -> 双换行 (保留段落分隔)
|
|
565
|
+
.replace(/<\/?(p|div|h[1-6]|li|tr|br)[^>]*>/gi, '\n\n')
|
|
566
|
+
.replace(/<[^>]+>/g, ' ')
|
|
567
|
+
.replace(/ /g, ' ').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
568
|
+
}
|
|
569
|
+
const blocks = text.split(/\n\s*\n/).map(b => b.trim()).filter(b => b.length > 0);
|
|
570
|
+
for (const block of blocks) {
|
|
571
|
+
const lines = block.split('\n').map(l => l.trim()).filter(l => l.length > 0);
|
|
572
|
+
if (lines.length === 0)
|
|
573
|
+
continue;
|
|
574
|
+
let decision = lines[0];
|
|
575
|
+
// 如果首行是 markdown 标题, 去掉 # 前缀
|
|
576
|
+
decision = decision.replace(/^#+\s*/, '');
|
|
577
|
+
// 如果整段就是一个短句 (没有换行), 直接当 decision
|
|
578
|
+
const reason = lines.length > 1 ? lines.slice(1).join(' ').trim() || undefined : undefined;
|
|
579
|
+
if (decision)
|
|
580
|
+
items.push({ decision, reason });
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
else {
|
|
584
|
+
return res.status(400).json({ error: 'unsupported file type (use .json .yaml .yml .md .txt .html)' });
|
|
585
|
+
}
|
|
586
|
+
if (items.length === 0) {
|
|
587
|
+
return res.status(400).json({ error: 'no parseable judgments found in file' });
|
|
588
|
+
}
|
|
589
|
+
const { storeHumanJudgment, initializeValueStore } = await import('../pi-ecosystem-judgment/human-value-store.js');
|
|
590
|
+
await initializeValueStore();
|
|
591
|
+
const imported = [];
|
|
592
|
+
const errors = [];
|
|
593
|
+
for (let i = 0; i < items.length; i++) {
|
|
594
|
+
try {
|
|
595
|
+
const it = items[i];
|
|
596
|
+
const j = await storeHumanJudgment({
|
|
597
|
+
decision: it.decision,
|
|
598
|
+
decision_type: 'approve',
|
|
599
|
+
reasons: it.reason ? [String(it.reason)] : [],
|
|
600
|
+
values_derived: [],
|
|
601
|
+
context: {
|
|
602
|
+
domain: it.context?.domain || context?.domain || 'general',
|
|
603
|
+
complexity: 'moderate',
|
|
604
|
+
stakes: it.context?.stakes || context?.stakes || 'medium',
|
|
605
|
+
time_pressure: 'low',
|
|
606
|
+
},
|
|
607
|
+
metadata: {
|
|
608
|
+
source: 'explicit',
|
|
609
|
+
confidence: 0.8,
|
|
610
|
+
revisable: true,
|
|
611
|
+
},
|
|
612
|
+
});
|
|
613
|
+
imported.push(j);
|
|
614
|
+
}
|
|
615
|
+
catch (e) {
|
|
616
|
+
errors.push(`#${i + 1} (${items[i].decision.substring(0, 30)}): ${e.message}`);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
res.json({ ok: true, imported: imported.length, failed: errors.length, errors: errors.slice(0, 5), judgments: imported });
|
|
620
|
+
}
|
|
621
|
+
catch (err) {
|
|
622
|
+
console.error('[judgments] import failed:', err);
|
|
623
|
+
res.status(500).json({ error: err.message });
|
|
624
|
+
}
|
|
625
|
+
});
|
|
626
|
+
// 修改判断 (手动编辑 decision / reasons / context / values_derived)
|
|
627
|
+
app.patch('/api/judgments/:id', async (req, res) => {
|
|
628
|
+
try {
|
|
629
|
+
const { id } = req.params;
|
|
630
|
+
const { updateJudgment, initializeValueStore } = await import('../pi-ecosystem-judgment/human-value-store.js');
|
|
631
|
+
await initializeValueStore();
|
|
632
|
+
const updated = await updateJudgment(id, req.body || {});
|
|
633
|
+
if (!updated)
|
|
634
|
+
return res.status(404).json({ error: 'judgment not found' });
|
|
635
|
+
res.json({ ok: true, judgment: updated });
|
|
636
|
+
}
|
|
637
|
+
catch (err) {
|
|
638
|
+
console.error('[judgments] PATCH failed:', err);
|
|
639
|
+
res.status(500).json({ error: err.message });
|
|
640
|
+
}
|
|
641
|
+
});
|
|
642
|
+
// 删除判断
|
|
643
|
+
app.delete('/api/judgments/:id', async (req, res) => {
|
|
644
|
+
try {
|
|
645
|
+
const { id } = req.params;
|
|
646
|
+
const { deleteJudgment, initializeValueStore } = await import('../pi-ecosystem-judgment/human-value-store.js');
|
|
647
|
+
await initializeValueStore();
|
|
648
|
+
const ok = await deleteJudgment(id);
|
|
649
|
+
if (!ok)
|
|
650
|
+
return res.status(404).json({ error: 'judgment not found' });
|
|
651
|
+
res.json({ ok: true });
|
|
652
|
+
}
|
|
653
|
+
catch (err) {
|
|
654
|
+
console.error('[judgments] DELETE failed:', err);
|
|
655
|
+
res.status(500).json({ error: err.message });
|
|
656
|
+
}
|
|
657
|
+
});
|
|
658
|
+
// 批量删除: { ids: ['hv-xxx', ...] } → { ok, deleted, notFound }
|
|
659
|
+
app.post('/api/judgments/batch-delete', async (req, res) => {
|
|
660
|
+
try {
|
|
661
|
+
const ids = (req.body && Array.isArray(req.body.ids)) ? req.body.ids : null;
|
|
662
|
+
if (!ids)
|
|
663
|
+
return res.status(400).json({ error: 'ids array required' });
|
|
664
|
+
const { deleteJudgment, initializeValueStore } = await import('../pi-ecosystem-judgment/human-value-store.js');
|
|
665
|
+
await initializeValueStore();
|
|
666
|
+
const idStrs = ids.filter((x) => typeof x === 'string' && x.length > 0);
|
|
667
|
+
let deleted = 0;
|
|
668
|
+
const notFound = [];
|
|
669
|
+
for (const id of idStrs) {
|
|
670
|
+
const ok = await deleteJudgment(id);
|
|
671
|
+
if (ok)
|
|
672
|
+
deleted++;
|
|
673
|
+
else
|
|
674
|
+
notFound.push(id);
|
|
675
|
+
}
|
|
676
|
+
res.json({ ok: true, deleted, notFound });
|
|
677
|
+
}
|
|
678
|
+
catch (err) {
|
|
679
|
+
console.error('[judgments] batch-delete failed:', err);
|
|
680
|
+
res.status(500).json({ error: err.message });
|
|
681
|
+
}
|
|
682
|
+
});
|
|
683
|
+
// AI 自动委派: 根据新判断的 capability / context 找最匹配的远端 agent, 委派任务
|
|
684
|
+
// 由前端在 POST /api/judgments 成功后调用 (fire-and-forget)
|
|
685
|
+
// 出参: { matched, targetAgent, response | skipped, reason }
|
|
686
|
+
app.post('/api/judgments/auto-delegate', async (req, res) => {
|
|
687
|
+
try {
|
|
688
|
+
const { judgmentId, capability, instruction } = req.body;
|
|
689
|
+
if (!judgmentId && !capability) {
|
|
690
|
+
return res.status(400).json({ error: 'judgmentId or capability required' });
|
|
691
|
+
}
|
|
692
|
+
const cap = capability || 'general';
|
|
693
|
+
// 用 agent-manifest-protocol 里的 pickAgent (内存) — 走本节点已经缓存的远端 manifest
|
|
694
|
+
const manifestMod = await import('../agents/agent-manifest-protocol.js');
|
|
695
|
+
const picked = manifestMod.pickAgent(cap);
|
|
696
|
+
if (!picked) {
|
|
697
|
+
return res.json({ ok: true, matched: false, reason: 'no remote agent matches capability' });
|
|
698
|
+
}
|
|
699
|
+
// 命中后, 用 iroh delegate transport 真正发过去
|
|
700
|
+
// 注: irohDelegateTransport.sendToNode 走的是 sendToNode(publicKey, frame, timeoutMs)
|
|
701
|
+
// irohTransport 的 sendMessage 不等回包, 所以委托是 fire-and-forget
|
|
702
|
+
// 想等回包需要新接口. 这里先把 "找得到目标 + 发送成功" 作为成功.
|
|
703
|
+
// TODO: 接入 requestResponse 等待远端 agent_response
|
|
704
|
+
try {
|
|
705
|
+
const idMod = await import('../network/iroh-integration.js');
|
|
706
|
+
const integ = idMod.getIrohIntegration();
|
|
707
|
+
if (!integ || !integ.getNodeId()) {
|
|
708
|
+
return res.json({ ok: true, matched: true, targetAgent: picked.agent, sent: false, reason: 'iroh not initialized' });
|
|
709
|
+
}
|
|
710
|
+
// 用 pickAgent 选出来的 agent 关联的 irohNodeId (有的话), 没有就跳到本地自处理
|
|
711
|
+
const targetIrohNodeId = picked.agent.irohNodeId;
|
|
712
|
+
if (!targetIrohNodeId) {
|
|
713
|
+
return res.json({ ok: true, matched: true, targetAgent: picked.agent, sent: false, reason: 'target agent has no irohNodeId (peer identity not bound)' });
|
|
714
|
+
}
|
|
715
|
+
const ok = await integ.sendTo(targetIrohNodeId, 'agent_delegate', new TextEncoder().encode(JSON.stringify({
|
|
716
|
+
type: 'agent_delegate',
|
|
717
|
+
payload: {
|
|
718
|
+
capability: cap,
|
|
719
|
+
instruction: instruction || `请执行我的判断: ${judgmentId}`,
|
|
720
|
+
fromAgentId: 'local-judgment',
|
|
721
|
+
},
|
|
722
|
+
ts: Date.now(),
|
|
723
|
+
fromDid: '',
|
|
724
|
+
})));
|
|
725
|
+
res.json({ ok: true, matched: true, targetAgent: picked.agent, sent: ok });
|
|
726
|
+
}
|
|
727
|
+
catch (e) {
|
|
728
|
+
res.json({ ok: true, matched: true, targetAgent: picked.agent, sent: false, error: e.message });
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
catch (err) {
|
|
732
|
+
console.error('[judgments] auto-delegate failed:', err);
|
|
733
|
+
res.status(500).json({ error: err.message });
|
|
734
|
+
}
|
|
735
|
+
});
|
|
736
|
+
// (判断的 UI 已合并到主页面 header 的盾牌按钮 + modal, 不再走独立路由)
|
|
737
|
+
// (启动 watchdog + health monitor 在主 server.ts createWebServer 末尾, 不在这里)
|
|
738
|
+
}
|