@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.
Files changed (284) hide show
  1. package/README.md +495 -467
  2. package/bin/bolloon-cli.cjs +183 -183
  3. package/bin/bolloon-daemon.sh +207 -0
  4. package/bin/bolloon.cjs +0 -0
  5. package/bin/ipfs +0 -0
  6. package/dist/agents/agent-manifest-protocol.js +52 -0
  7. package/dist/agents/chat-segmenter.js +298 -0
  8. package/dist/agents/constraint-layer.js +19 -19
  9. package/dist/agents/judgment-protocol.js +479 -0
  10. package/dist/agents/parse-tool-call.js +304 -0
  11. package/dist/agents/peer-manifest-loader.js +210 -0
  12. package/dist/agents/pi-sdk-session-factory.js +110 -0
  13. package/dist/agents/pi-sdk-session-manager.js +297 -0
  14. package/dist/agents/pi-sdk-tools.js +1288 -0
  15. package/dist/agents/pi-sdk-types.js +13 -0
  16. package/dist/agents/pi-sdk.js +553 -1306
  17. package/dist/agents/react-loop.js +120 -0
  18. package/dist/agents/session-store.js +171 -0
  19. package/dist/agents/shell-guard.js +2 -2
  20. package/dist/agents/tool-registry.js +136 -0
  21. package/dist/agents/workflow-pivot-loop.js +200 -22
  22. package/dist/bollharness/src/scripts/context_router.js +7 -0
  23. package/dist/bollharness/src/scripts/deploy-guard.js +8 -8
  24. package/dist/bollharness-integration/context-router-judgment.js +4 -4
  25. package/dist/bollharness-integration/context-router.js +292 -292
  26. package/dist/bollharness-integration/gate-state-machine.js +26 -13
  27. package/dist/bollharness-integration/integration.js +71 -0
  28. package/dist/bollharness-integration/llm-judgment-engine.js +134 -134
  29. package/dist/bollharness-integration/skill-adapter.js +57 -127
  30. package/dist/bootstrap/chat-archiver.js +276 -0
  31. package/dist/bootstrap/context-collector.js +6 -3
  32. package/dist/bootstrap/event-log.js +160 -0
  33. package/dist/bootstrap/lifecycle-hooks.js +15 -1
  34. package/dist/bootstrap/memory-compressor.js +170 -0
  35. package/dist/bootstrap/persona-loader.js +94 -0
  36. package/dist/bootstrap/project-state.js +159 -0
  37. package/dist/bootstrap/remote-mirror.js +97 -0
  38. package/dist/bootstrap/session-window.js +121 -0
  39. package/dist/bootstrap/vector-index.js +248 -0
  40. package/dist/cli/loading-tui.js +40 -0
  41. package/dist/cli-entry.js +41 -37
  42. package/dist/context-compaction/auto-compact.js +7 -7
  43. package/dist/electron/config.js +21 -0
  44. package/dist/electron/config.js.map +1 -0
  45. package/dist/electron/dialogs.js +108 -0
  46. package/dist/electron/dialogs.js.map +1 -0
  47. package/dist/electron/first-run.js +170 -0
  48. package/dist/electron/first-run.js.map +1 -0
  49. package/dist/electron/ipc.js +20 -0
  50. package/dist/electron/ipc.js.map +1 -0
  51. package/dist/electron/logger.js +114 -0
  52. package/dist/electron/logger.js.map +1 -0
  53. package/dist/electron/main.js +63 -0
  54. package/dist/electron/main.js.map +1 -0
  55. package/dist/electron/menu.js +145 -0
  56. package/dist/electron/menu.js.map +1 -0
  57. package/dist/electron/paths.js +75 -0
  58. package/dist/electron/paths.js.map +1 -0
  59. package/dist/electron/server.js +119 -0
  60. package/dist/electron/server.js.map +1 -0
  61. package/dist/electron/tray.js +111 -0
  62. package/dist/electron/tray.js.map +1 -0
  63. package/dist/electron/window.js +111 -0
  64. package/dist/electron/window.js.map +1 -0
  65. package/dist/electron-preload.js +26 -6
  66. package/dist/electron-preload.js.map +1 -1
  67. package/dist/electron.js +6 -168
  68. package/dist/electron.js.map +1 -1
  69. package/dist/index.js +323 -266
  70. package/dist/llm/config-store.js +29 -8
  71. package/dist/llm/llm-judgment-client.js +102 -102
  72. package/dist/llm/pi-ai.js +139 -85
  73. package/dist/llm/system-prompt/layers/channel/local.md +14 -14
  74. package/dist/llm/system-prompt/layers/channel/p2p-agent.md +18 -18
  75. package/dist/llm/system-prompt/layers/channel/p2p-visitor.md +19 -19
  76. package/dist/llm/system-prompt/layers/core/artifacts_storage.md +89 -89
  77. package/dist/llm/system-prompt/layers/core/evenhandedness.md +21 -21
  78. package/dist/llm/system-prompt/layers/core/hibs_reminders.md +15 -15
  79. package/dist/llm/system-prompt/layers/core/identity.md +37 -37
  80. package/dist/llm/system-prompt/layers/core/knowledge.md +17 -17
  81. package/dist/llm/system-prompt/layers/core/memory_system.md +12 -12
  82. package/dist/llm/system-prompt/layers/core/network_filesystem.md +28 -28
  83. package/dist/llm/system-prompt/layers/core/refusal.md +37 -37
  84. package/dist/llm/system-prompt/layers/core/tone.md +31 -31
  85. package/dist/llm/system-prompt/layers/core/tools.thin.md +13 -13
  86. package/dist/llm/system-prompt/layers/core/wellbeing.md +41 -41
  87. package/dist/llm/system-prompt/layers/role/architect.md +20 -20
  88. package/dist/llm/system-prompt/layers/role/expert.md +19 -19
  89. package/dist/llm/system-prompt/layers/role/implementer.md +15 -15
  90. package/dist/llm/system-prompt/layers/role/security.md +15 -15
  91. package/dist/llm/system-prompt/layers/tool/artifacts.md +72 -72
  92. package/dist/llm/system-prompt/layers/tool/bash.md +25 -25
  93. package/dist/llm/system-prompt/layers/tool/hibs_api.md +171 -171
  94. package/dist/llm/system-prompt/layers/tool/image_search.md +70 -70
  95. package/dist/llm/system-prompt/layers/tool/manifest.md +89 -89
  96. package/dist/llm/system-prompt/layers/tool/mcp_apps.md +53 -53
  97. package/dist/llm/system-prompt/layers/tool/web_search.md +83 -83
  98. package/dist/llm/tool-manifest/ask_user_input.js +1 -1
  99. package/dist/llm/tool-manifest/bash.js +3 -3
  100. package/dist/llm/tool-manifest/create_file.js +4 -4
  101. package/dist/llm/tool-manifest/fetch_sports_data.js +3 -3
  102. package/dist/llm/tool-manifest/image_search.js +2 -2
  103. package/dist/llm/tool-manifest/mcp.js +2 -2
  104. package/dist/llm/tool-manifest/message_compose.js +3 -3
  105. package/dist/llm/tool-manifest/places.js +2 -2
  106. package/dist/llm/tool-manifest/present_files.js +1 -1
  107. package/dist/llm/tool-manifest/recipe.js +2 -2
  108. package/dist/llm/tool-manifest/str_replace.js +5 -5
  109. package/dist/llm/tool-manifest/view.js +3 -3
  110. package/dist/llm/tool-manifest/weather.js +4 -4
  111. package/dist/llm/tool-manifest/web.js +4 -4
  112. package/dist/network/local-inbox-bus.js +73 -0
  113. package/dist/network/p2p-outbox.js +161 -0
  114. package/dist/network/peer-fs.js +420 -0
  115. package/dist/network/peer-resource-bridge.js +216 -0
  116. package/dist/pi-ecosystem-judgment/causal-judge.js +51 -51
  117. package/dist/pi-ecosystem-judgment/cleanup.js +126 -0
  118. package/dist/pi-ecosystem-judgment/distill-prompt.js +42 -42
  119. package/dist/pi-ecosystem-judgment/distillation.js +14 -14
  120. package/dist/pi-ecosystem-judgment/human-value-store.js +45 -11
  121. package/dist/pi-ecosystem-judgment/monitor-gate.js +28 -28
  122. package/dist/pi-ecosystem-judgment/value-injection.js +102 -102
  123. package/dist/security/context-router-tool.js +15 -15
  124. package/dist/security/tool-gate.js +11 -0
  125. package/dist/web/api-config.html +520 -520
  126. package/dist/web/client-loop-status.js +220 -0
  127. package/dist/web/client.js +22 -5
  128. package/dist/web/client.js.map +7 -0
  129. package/dist/web/components/wallet-viem.mjs +118 -118
  130. package/dist/web/index.html +375 -375
  131. package/dist/web/input-validator.js +103 -0
  132. package/dist/web/manifest.json +20 -20
  133. package/dist/web/routes-judgments.js +738 -0
  134. package/dist/web/routes-llm-config.js +279 -0
  135. package/dist/web/routes-tasks.js +222 -0
  136. package/dist/web/server-sse.js +121 -0
  137. package/dist/web/server-storage.js +154 -0
  138. package/dist/web/server-types.js +15 -0
  139. package/dist/web/server-v3-p2p.js +224 -0
  140. package/dist/web/server.js +1070 -1307
  141. package/dist/web/style.css +4742 -4742
  142. package/dist/web/util/safe-name.js +32 -0
  143. package/package.json +162 -160
  144. package/scripts/build-cli.js +215 -215
  145. package/scripts/build-web.ts +130 -130
  146. package/scripts/postinstall.js +152 -152
  147. package/bin/bolloon.js +0 -157
  148. package/dist/constraint-runtime/src/_archive_helper.js +0 -9
  149. package/dist/constraint-runtime/src/agent/coordinator.js +0 -48
  150. package/dist/constraint-runtime/src/agent/index.js +0 -1
  151. package/dist/constraint-runtime/src/assistant/index.js +0 -12
  152. package/dist/constraint-runtime/src/bootstrap/index.js +0 -12
  153. package/dist/constraint-runtime/src/bootstrap_graph.js +0 -13
  154. package/dist/constraint-runtime/src/bridge/index.js +0 -12
  155. package/dist/constraint-runtime/src/buddy/index.js +0 -12
  156. package/dist/constraint-runtime/src/cli/index.js +0 -12
  157. package/dist/constraint-runtime/src/command_graph.js +0 -10
  158. package/dist/constraint-runtime/src/commands.js +0 -60
  159. package/dist/constraint-runtime/src/components/index.js +0 -12
  160. package/dist/constraint-runtime/src/constants/index.js +0 -12
  161. package/dist/constraint-runtime/src/constraint/budget.js +0 -22
  162. package/dist/constraint-runtime/src/constraint/index.js +0 -2
  163. package/dist/constraint-runtime/src/constraint/permission.js +0 -20
  164. package/dist/constraint-runtime/src/context.js +0 -30
  165. package/dist/constraint-runtime/src/coordinator/index.js +0 -12
  166. package/dist/constraint-runtime/src/cost_hook.js +0 -4
  167. package/dist/constraint-runtime/src/cost_tracker.js +0 -8
  168. package/dist/constraint-runtime/src/deferred_init.js +0 -10
  169. package/dist/constraint-runtime/src/direct_modes.js +0 -6
  170. package/dist/constraint-runtime/src/dynamic-tool-loader.js +0 -85
  171. package/dist/constraint-runtime/src/entrypoints/index.js +0 -12
  172. package/dist/constraint-runtime/src/execution_registry.js +0 -44
  173. package/dist/constraint-runtime/src/history.js +0 -9
  174. package/dist/constraint-runtime/src/hooks/index.js +0 -12
  175. package/dist/constraint-runtime/src/index.js +0 -26
  176. package/dist/constraint-runtime/src/ink.js +0 -4
  177. package/dist/constraint-runtime/src/keybindings/index.js +0 -12
  178. package/dist/constraint-runtime/src/memdir/index.js +0 -12
  179. package/dist/constraint-runtime/src/migrations/index.js +0 -12
  180. package/dist/constraint-runtime/src/models.js +0 -1
  181. package/dist/constraint-runtime/src/moreright/index.js +0 -12
  182. package/dist/constraint-runtime/src/native_ts/index.js +0 -12
  183. package/dist/constraint-runtime/src/output_styles/index.js +0 -12
  184. package/dist/constraint-runtime/src/parity_audit.js +0 -12
  185. package/dist/constraint-runtime/src/plugins/index.js +0 -12
  186. package/dist/constraint-runtime/src/port_manifest.js +0 -11
  187. package/dist/constraint-runtime/src/prefetch.js +0 -9
  188. package/dist/constraint-runtime/src/query.js +0 -1
  189. package/dist/constraint-runtime/src/remote/index.js +0 -12
  190. package/dist/constraint-runtime/src/remote_runtime.js +0 -9
  191. package/dist/constraint-runtime/src/runtime/index.js +0 -1
  192. package/dist/constraint-runtime/src/runtime/session.js +0 -35
  193. package/dist/constraint-runtime/src/schemas/index.js +0 -12
  194. package/dist/constraint-runtime/src/screens/index.js +0 -12
  195. package/dist/constraint-runtime/src/server/index.js +0 -12
  196. package/dist/constraint-runtime/src/services/index.js +0 -12
  197. package/dist/constraint-runtime/src/session_store.js +0 -22
  198. package/dist/constraint-runtime/src/setup.js +0 -30
  199. package/dist/constraint-runtime/src/skills/index.js +0 -1
  200. package/dist/constraint-runtime/src/skills/skill-registry.js +0 -28
  201. package/dist/constraint-runtime/src/state/index.js +0 -12
  202. package/dist/constraint-runtime/src/system_init.js +0 -20
  203. package/dist/constraint-runtime/src/thinking/engine.js +0 -42
  204. package/dist/constraint-runtime/src/thinking/index.js +0 -1
  205. package/dist/constraint-runtime/src/tool_pool.js +0 -8
  206. package/dist/constraint-runtime/src/tools/OpenCLI/execAdapter.js +0 -7
  207. package/dist/constraint-runtime/src/tools/OpenCLI/listAdapters.js +0 -7
  208. package/dist/constraint-runtime/src/tools/OpenCLI/runCommand.js +0 -7
  209. package/dist/constraint-runtime/src/tools/SafeSDK/confirmTransaction.js +0 -6
  210. package/dist/constraint-runtime/src/tools/SafeSDK/createTransaction.js +0 -8
  211. package/dist/constraint-runtime/src/tools/SafeSDK/executeTransaction.js +0 -6
  212. package/dist/constraint-runtime/src/tools/SafeSDK/getBalance.js +0 -6
  213. package/dist/constraint-runtime/src/tools/SafeSDK/getPendingTransactions.js +0 -6
  214. package/dist/constraint-runtime/src/tools/SafeSDK/proposeTransaction.js +0 -6
  215. package/dist/constraint-runtime/src/tools.js +0 -80
  216. package/dist/constraint-runtime/src/transcript.js +0 -19
  217. package/dist/constraint-runtime/src/types/index.js +0 -12
  218. package/dist/constraint-runtime/src/upstream_proxy/index.js +0 -12
  219. package/dist/constraint-runtime/src/utils/index.js +0 -12
  220. package/dist/constraint-runtime/src/vim/index.js +0 -12
  221. package/dist/constraint-runtime/src/voice/index.js +0 -12
  222. package/dist/constraint-runtime/tests/agent.test.js +0 -16
  223. package/dist/constraint-runtime/tests/constraint.test.js +0 -41
  224. package/dist/constraint-runtime/tests/skill.test.js +0 -19
  225. package/dist/constraint-runtime/tests/thinking.test.js +0 -22
  226. package/dist/pi-ecosystem-colony/index.js +0 -365
  227. package/dist/social/ant-colony/AdaptiveHeartbeat.js +0 -101
  228. package/dist/social/ant-colony/PheromoneEngine.js +0 -227
  229. package/dist/social/ant-colony/index.js +0 -6
  230. package/dist/social/ant-colony/types.js +0 -24
  231. package/dist/test/ai-judgment-test.js +0 -80
  232. package/dist/test/bollharness-integration.test.js +0 -318
  233. package/dist/test/channel-agent-multi-dialogue.js +0 -205
  234. package/dist/test/channel-heartbeat-agent-test.js +0 -201
  235. package/dist/test/constraint-layer.test.js +0 -164
  236. package/dist/test/diap-identity-test.js +0 -172
  237. package/dist/test/diap-quick-test.js +0 -62
  238. package/dist/test/global-shared-context.test.js +0 -315
  239. package/dist/test/harness-judgment-injection.test.js +0 -246
  240. package/dist/test/harness-workflow-integrator-test.js +0 -228
  241. package/dist/test/human-value-store.test.js +0 -243
  242. package/dist/test/hybrid-integration-test.js +0 -118
  243. package/dist/test/hybrid-messenger-verify.js +0 -55
  244. package/dist/test/iroh-bistream-debug.js +0 -38
  245. package/dist/test/iroh-communication.test.js +0 -66
  246. package/dist/test/iroh-debug-test.js +0 -57
  247. package/dist/test/iroh-diap-test.js +0 -71
  248. package/dist/test/iroh-direct-connect.js +0 -55
  249. package/dist/test/iroh-e2e-fixed.js +0 -89
  250. package/dist/test/iroh-e2e-same-process.js +0 -63
  251. package/dist/test/iroh-e2e.js +0 -66
  252. package/dist/test/iroh-final-e2e.js +0 -72
  253. package/dist/test/iroh-relay-test.js +0 -37
  254. package/dist/test/iroh-simple-test.js +0 -41
  255. package/dist/test/iroh-transport-verify.js +0 -54
  256. package/dist/test/iroh-transport.test.js +0 -37
  257. package/dist/test/iroh-two-nodes.js +0 -70
  258. package/dist/test/iroh-verify.js +0 -44
  259. package/dist/test/judgment-decision.test.js +0 -219
  260. package/dist/test/llm-judgment-integration.test.js +0 -220
  261. package/dist/test/p2p-agent-complex-dialogue.js +0 -385
  262. package/dist/test/p2p-agent-dialogue.js +0 -341
  263. package/dist/test/p2p-agent-full-bidirectional.js +0 -510
  264. package/dist/test/p2p-agent-harness-flow.js +0 -437
  265. package/dist/test/p2p-agent-harness-single.js +0 -143
  266. package/dist/test/p2p-ai-dialogue-test.js +0 -318
  267. package/dist/test/p2p-cid-connect-test.js +0 -195
  268. package/dist/test/p2p-connect-receiver.js +0 -69
  269. package/dist/test/p2p-doc-transfer.js +0 -110
  270. package/dist/test/p2p-identity-page-test.js +0 -77
  271. package/dist/test/p2p-iroh-test.js +0 -171
  272. package/dist/test/p2p-minimal-test.js +0 -241
  273. package/dist/test/p2p-node-1.js +0 -148
  274. package/dist/test/p2p-node-2.js +0 -148
  275. package/dist/test/p2p-server.js +0 -281
  276. package/dist/test/p2p-two-nodes-test.js +0 -438
  277. package/dist/test/pi-sdk.test.js +0 -44
  278. package/dist/test/set-persona.js +0 -40
  279. package/dist/test/simple.test.js +0 -9
  280. package/dist/test/storage-integration.test.js +0 -150
  281. package/dist/test/subagent-manager.test.js +0 -276
  282. package/dist/test/test-gate-flow.test.js +0 -81
  283. package/dist/test/workflow-engine.test.js +0 -87
  284. 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(/&nbsp;/g, ' ').replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/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
+ }