@bolloon/bolloon-agent 0.1.0

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 (808) hide show
  1. package/README.md +462 -0
  2. package/dist/agents/constraint-layer.js +211 -0
  3. package/dist/agents/constraint-layer.js.map +1 -0
  4. package/dist/agents/pi-sdk.js +1199 -0
  5. package/dist/agents/pi-sdk.js.map +1 -0
  6. package/dist/agents/protocol.js +229 -0
  7. package/dist/agents/subagent-manager.js +361 -0
  8. package/dist/agents/workflow-engine.js +222 -0
  9. package/dist/agents/workflow-engine.js.map +1 -0
  10. package/dist/bollharness-integration/bollharness-integration/context-router-judgment.d.ts +48 -0
  11. package/dist/bollharness-integration/bollharness-integration/context-router-judgment.js +261 -0
  12. package/dist/bollharness-integration/bollharness-integration/context-router.d.ts +110 -0
  13. package/dist/bollharness-integration/bollharness-integration/context-router.js +542 -0
  14. package/dist/bollharness-integration/bollharness-integration/gate-state-machine.d.ts +87 -0
  15. package/dist/bollharness-integration/bollharness-integration/gate-state-machine.js +231 -0
  16. package/dist/bollharness-integration/bollharness-integration/gate-transition-hooks.d.ts +30 -0
  17. package/dist/bollharness-integration/bollharness-integration/gate-transition-hooks.js +91 -0
  18. package/dist/bollharness-integration/bollharness-integration/guard-checker.d.ts +105 -0
  19. package/dist/bollharness-integration/bollharness-integration/guard-checker.js +353 -0
  20. package/dist/bollharness-integration/bollharness-integration/index.d.ts +66 -0
  21. package/dist/bollharness-integration/bollharness-integration/index.js +32 -0
  22. package/dist/bollharness-integration/bollharness-integration/integration.d.ts +219 -0
  23. package/dist/bollharness-integration/bollharness-integration/integration.js +420 -0
  24. package/dist/bollharness-integration/bollharness-integration/skill-adapter.d.ts +151 -0
  25. package/dist/bollharness-integration/bollharness-integration/skill-adapter.js +518 -0
  26. package/dist/bollharness-integration/context-router-judgment.js +269 -0
  27. package/dist/bollharness-integration/context-router-judgment.js.map +1 -0
  28. package/dist/bollharness-integration/context-router.js +584 -0
  29. package/dist/bollharness-integration/context-router.js.map +1 -0
  30. package/dist/bollharness-integration/gate-state-machine.js +349 -0
  31. package/dist/bollharness-integration/gate-state-machine.js.map +1 -0
  32. package/dist/bollharness-integration/gate-transition-hooks.js +136 -0
  33. package/dist/bollharness-integration/gate-transition-hooks.js.map +1 -0
  34. package/dist/bollharness-integration/guard-checker.js +400 -0
  35. package/dist/bollharness-integration/guard-checker.js.map +1 -0
  36. package/dist/bollharness-integration/index.js +142 -0
  37. package/dist/bollharness-integration/index.js.map +1 -0
  38. package/dist/bollharness-integration/integration.js +427 -0
  39. package/dist/bollharness-integration/integration.js.map +1 -0
  40. package/dist/bollharness-integration/llm/pi-ai.d.ts +60 -0
  41. package/dist/bollharness-integration/llm/pi-ai.js +397 -0
  42. package/dist/bollharness-integration/pi-ecosystem-colony/index.d.ts +153 -0
  43. package/dist/bollharness-integration/pi-ecosystem-colony/index.js +365 -0
  44. package/dist/bollharness-integration/pi-ecosystem-goals/index.d.ts +136 -0
  45. package/dist/bollharness-integration/pi-ecosystem-goals/index.js +458 -0
  46. package/dist/bollharness-integration/pi-ecosystem-judgment/decision.d.ts +117 -0
  47. package/dist/bollharness-integration/pi-ecosystem-judgment/decision.js +300 -0
  48. package/dist/bollharness-integration/pi-ecosystem-judgment/distillation.d.ts +78 -0
  49. package/dist/bollharness-integration/pi-ecosystem-judgment/distillation.js +291 -0
  50. package/dist/bollharness-integration/pi-ecosystem-judgment/index.d.ts +139 -0
  51. package/dist/bollharness-integration/pi-ecosystem-judgment/index.js +445 -0
  52. package/dist/bollharness-integration/pi-ecosystem-mcp/index.d.ts +127 -0
  53. package/dist/bollharness-integration/pi-ecosystem-mcp/index.js +331 -0
  54. package/dist/bollharness-integration/pi-ecosystem-subagents/index.d.ts +95 -0
  55. package/dist/bollharness-integration/pi-ecosystem-subagents/index.js +303 -0
  56. package/dist/bollharness-integration/skill-adapter.js +564 -0
  57. package/dist/bollharness-integration/skill-adapter.js.map +1 -0
  58. package/dist/cli/interface.js +181 -0
  59. package/dist/constraint-runtime/src/_archive_helper.js +9 -0
  60. package/dist/constraint-runtime/src/agent/coordinator.js +48 -0
  61. package/dist/constraint-runtime/src/agent/index.js +1 -0
  62. package/dist/constraint-runtime/src/assistant/index.js +12 -0
  63. package/dist/constraint-runtime/src/bootstrap/index.js +12 -0
  64. package/dist/constraint-runtime/src/bootstrap_graph.js +13 -0
  65. package/dist/constraint-runtime/src/bridge/index.js +12 -0
  66. package/dist/constraint-runtime/src/buddy/index.js +12 -0
  67. package/dist/constraint-runtime/src/cli/index.js +12 -0
  68. package/dist/constraint-runtime/src/command_graph.js +10 -0
  69. package/dist/constraint-runtime/src/commands.js +50 -0
  70. package/dist/constraint-runtime/src/components/index.js +12 -0
  71. package/dist/constraint-runtime/src/constants/index.js +12 -0
  72. package/dist/constraint-runtime/src/constraint/budget.js +27 -0
  73. package/dist/constraint-runtime/src/constraint/budget.js.map +1 -0
  74. package/dist/constraint-runtime/src/constraint/index.js +2 -0
  75. package/dist/constraint-runtime/src/constraint/permission.js +25 -0
  76. package/dist/constraint-runtime/src/constraint/permission.js.map +1 -0
  77. package/dist/constraint-runtime/src/context.js +30 -0
  78. package/dist/constraint-runtime/src/coordinator/index.js +12 -0
  79. package/dist/constraint-runtime/src/cost_hook.js +4 -0
  80. package/dist/constraint-runtime/src/cost_tracker.js +8 -0
  81. package/dist/constraint-runtime/src/deferred_init.js +10 -0
  82. package/dist/constraint-runtime/src/direct_modes.js +6 -0
  83. package/dist/constraint-runtime/src/dynamic-tool-loader.js +85 -0
  84. package/dist/constraint-runtime/src/entrypoints/index.js +12 -0
  85. package/dist/constraint-runtime/src/execution_registry.js +44 -0
  86. package/dist/constraint-runtime/src/history.js +9 -0
  87. package/dist/constraint-runtime/src/hooks/index.js +12 -0
  88. package/dist/constraint-runtime/src/index.js +26 -0
  89. package/dist/constraint-runtime/src/ink.js +4 -0
  90. package/dist/constraint-runtime/src/keybindings/index.js +12 -0
  91. package/dist/constraint-runtime/src/memdir/index.js +12 -0
  92. package/dist/constraint-runtime/src/migrations/index.js +12 -0
  93. package/dist/constraint-runtime/src/models.js +3 -0
  94. package/dist/constraint-runtime/src/models.js.map +1 -0
  95. package/dist/constraint-runtime/src/moreright/index.js +12 -0
  96. package/dist/constraint-runtime/src/native_ts/index.js +12 -0
  97. package/dist/constraint-runtime/src/output_styles/index.js +12 -0
  98. package/dist/constraint-runtime/src/parity_audit.js +12 -0
  99. package/dist/constraint-runtime/src/plugins/index.js +12 -0
  100. package/dist/constraint-runtime/src/port_manifest.js +11 -0
  101. package/dist/constraint-runtime/src/prefetch.js +9 -0
  102. package/dist/constraint-runtime/src/query.js +1 -0
  103. package/dist/constraint-runtime/src/remote/index.js +12 -0
  104. package/dist/constraint-runtime/src/remote_runtime.js +9 -0
  105. package/dist/constraint-runtime/src/runtime/index.js +1 -0
  106. package/dist/constraint-runtime/src/runtime/session.js +35 -0
  107. package/dist/constraint-runtime/src/schemas/index.js +12 -0
  108. package/dist/constraint-runtime/src/screens/index.js +12 -0
  109. package/dist/constraint-runtime/src/server/index.js +12 -0
  110. package/dist/constraint-runtime/src/services/index.js +12 -0
  111. package/dist/constraint-runtime/src/session_store.js +22 -0
  112. package/dist/constraint-runtime/src/setup.js +30 -0
  113. package/dist/constraint-runtime/src/skills/index.js +1 -0
  114. package/dist/constraint-runtime/src/skills/skill-registry.js +28 -0
  115. package/dist/constraint-runtime/src/state/index.js +12 -0
  116. package/dist/constraint-runtime/src/system_init.js +20 -0
  117. package/dist/constraint-runtime/src/thinking/engine.js +42 -0
  118. package/dist/constraint-runtime/src/thinking/index.js +1 -0
  119. package/dist/constraint-runtime/src/tool_pool.js +8 -0
  120. package/dist/constraint-runtime/src/tools/OpenCLI/execAdapter.js +7 -0
  121. package/dist/constraint-runtime/src/tools/OpenCLI/listAdapters.js +7 -0
  122. package/dist/constraint-runtime/src/tools/OpenCLI/runCommand.js +7 -0
  123. package/dist/constraint-runtime/src/tools/PolymarketSDK/cancelOrder.js +6 -0
  124. package/dist/constraint-runtime/src/tools/PolymarketSDK/createOrder.js +6 -0
  125. package/dist/constraint-runtime/src/tools/PolymarketSDK/getMarket.js +5 -0
  126. package/dist/constraint-runtime/src/tools/PolymarketSDK/getOrders.js +6 -0
  127. package/dist/constraint-runtime/src/tools/PolymarketSDK/listMarkets.js +4 -0
  128. package/dist/constraint-runtime/src/tools/SafeSDK/confirmTransaction.js +6 -0
  129. package/dist/constraint-runtime/src/tools/SafeSDK/createTransaction.js +8 -0
  130. package/dist/constraint-runtime/src/tools/SafeSDK/deploySafe.js +6 -0
  131. package/dist/constraint-runtime/src/tools/SafeSDK/executeTransaction.js +6 -0
  132. package/dist/constraint-runtime/src/tools/SafeSDK/getBalance.js +6 -0
  133. package/dist/constraint-runtime/src/tools/SafeSDK/getPendingTransactions.js +6 -0
  134. package/dist/constraint-runtime/src/tools/SafeSDK/proposeTransaction.js +6 -0
  135. package/dist/constraint-runtime/src/tools/WalletTools/autoPay.js +29 -0
  136. package/dist/constraint-runtime/src/tools/WalletTools/createWallet.js +10 -0
  137. package/dist/constraint-runtime/src/tools/WalletTools/getBalance.js +13 -0
  138. package/dist/constraint-runtime/src/tools/WalletTools/importWallet.js +22 -0
  139. package/dist/constraint-runtime/src/tools/WalletTools/sendTransaction.js +25 -0
  140. package/dist/constraint-runtime/src/tools/WalletTools/signMessage.js +10 -0
  141. package/dist/constraint-runtime/src/tools/WalletTools/transferToken.js +25 -0
  142. package/dist/constraint-runtime/src/tools.js +70 -0
  143. package/dist/constraint-runtime/src/transcript.js +19 -0
  144. package/dist/constraint-runtime/src/types/index.js +12 -0
  145. package/dist/constraint-runtime/src/upstream_proxy/index.js +12 -0
  146. package/dist/constraint-runtime/src/utils/index.js +12 -0
  147. package/dist/constraint-runtime/src/vim/index.js +12 -0
  148. package/dist/constraint-runtime/src/voice/index.js +12 -0
  149. package/dist/constraint-runtime/tests/agent.test.js +16 -0
  150. package/dist/constraint-runtime/tests/constraint.test.js +41 -0
  151. package/dist/constraint-runtime/tests/skill.test.js +19 -0
  152. package/dist/constraint-runtime/tests/thinking.test.js +22 -0
  153. package/dist/constraints/commands.js +100 -0
  154. package/dist/constraints/index.js +11 -0
  155. package/dist/constraints/index.js.map +1 -0
  156. package/dist/constraints/permissions.js +37 -0
  157. package/dist/constraints/runtime.js +135 -0
  158. package/dist/constraints/session.js +48 -0
  159. package/dist/constraints/system-init.js +51 -0
  160. package/dist/constraints/tools.js +104 -0
  161. package/dist/documents/reader.js +104 -0
  162. package/dist/documents/reader.js.map +1 -0
  163. package/dist/electron-preload.js +15 -0
  164. package/dist/electron-preload.js.map +1 -0
  165. package/dist/electron.js +206 -0
  166. package/dist/electron.js.map +1 -0
  167. package/dist/index.js +1053 -0
  168. package/dist/llm/config-store.js +301 -0
  169. package/dist/llm/config-store.js.map +1 -0
  170. package/dist/llm/minimax-provider.js +46 -0
  171. package/dist/llm/minimax.js +45 -0
  172. package/dist/llm/pi-ai.js +471 -0
  173. package/dist/llm/pi-ai.js.map +1 -0
  174. package/dist/network/agent-network.js +692 -0
  175. package/dist/network/agent-network.js.map +1 -0
  176. package/dist/network/iroh-integration.js +117 -0
  177. package/dist/network/iroh-integration.js.map +1 -0
  178. package/dist/network/iroh-transport.js +506 -0
  179. package/dist/network/iroh-transport.js.map +1 -0
  180. package/dist/network/p2p.js +864 -0
  181. package/dist/network/p2p.js.map +1 -0
  182. package/dist/network/storage/adapters/json-adapter.js +401 -0
  183. package/dist/network/storage/adapters/json-adapter.js.map +1 -0
  184. package/dist/pi-ecosystem-colony/index.js +426 -0
  185. package/dist/pi-ecosystem-colony/index.js.map +1 -0
  186. package/dist/pi-ecosystem-goals/index.js +512 -0
  187. package/dist/pi-ecosystem-goals/index.js.map +1 -0
  188. package/dist/pi-ecosystem-judgment/decision.js +318 -0
  189. package/dist/pi-ecosystem-judgment/decision.js.map +1 -0
  190. package/dist/pi-ecosystem-judgment/distillation.js +304 -0
  191. package/dist/pi-ecosystem-judgment/distillation.js.map +1 -0
  192. package/dist/pi-ecosystem-judgment/human-value-store.js +463 -0
  193. package/dist/pi-ecosystem-judgment/human-value-store.js.map +1 -0
  194. package/dist/pi-ecosystem-judgment/index.js +581 -0
  195. package/dist/pi-ecosystem-judgment/index.js.map +1 -0
  196. package/dist/pi-ecosystem-judgment/value-injection.js +592 -0
  197. package/dist/pi-ecosystem-judgment/value-injection.js.map +1 -0
  198. package/dist/pi-ecosystem-mcp/index.js +385 -0
  199. package/dist/pi-ecosystem-mcp/index.js.map +1 -0
  200. package/dist/pi-ecosystem-subagents/index.js +352 -0
  201. package/dist/pi-ecosystem-subagents/index.js.map +1 -0
  202. package/dist/runtime/context/minimax-prompt.js +178 -0
  203. package/dist/runtime/context/sys-prompt.js +1 -0
  204. package/dist/social/ant-colony/AdaptiveHeartbeat.js +106 -0
  205. package/dist/social/ant-colony/AdaptiveHeartbeat.js.map +1 -0
  206. package/dist/social/ant-colony/PheromoneEngine.js +265 -0
  207. package/dist/social/ant-colony/PheromoneEngine.js.map +1 -0
  208. package/dist/social/ant-colony/types.js +28 -0
  209. package/dist/social/ant-colony/types.js.map +1 -0
  210. package/dist/social/channels/ChannelManager.js +444 -0
  211. package/dist/social/channels/ChannelManager.js.map +1 -0
  212. package/dist/social/channels/DiapChannelBridge.js +451 -0
  213. package/dist/social/channels/DiapChannelBridge.js.map +1 -0
  214. package/dist/social/channels/InterestMatcher.js +135 -0
  215. package/dist/social/channels/InterestMatcher.js.map +1 -0
  216. package/dist/social/channels/types.js +21 -0
  217. package/dist/social/channels/types.js.map +1 -0
  218. package/dist/social/global-shared-context.js +368 -0
  219. package/dist/social/global-shared-context.js.map +1 -0
  220. package/dist/social/heartbeat.js +865 -0
  221. package/dist/social/heartbeat.js.map +1 -0
  222. package/dist/test/constraint-layer.test.js +164 -0
  223. package/dist/test/global-shared-context.test.js +315 -0
  224. package/dist/test/pi-sdk.test.js +47 -0
  225. package/dist/test/set-persona.test.js +38 -0
  226. package/dist/test/subagent-manager.test.js +276 -0
  227. package/dist/test/workflow-engine.test.js +87 -0
  228. package/dist/web/api-config.html +265 -0
  229. package/dist/web/client.js +1401 -0
  230. package/dist/web/components/p2p/index.js +357 -0
  231. package/dist/web/components/p2p/p2p-connection.js +161 -0
  232. package/dist/web/components/p2p/p2p-identity.js +78 -0
  233. package/dist/web/components/p2p/p2p-manager.js +82 -0
  234. package/dist/web/components/p2p/p2p-messages.js +186 -0
  235. package/dist/web/components/p2p/p2p-store-memory.js +129 -0
  236. package/dist/web/components/p2p/types.js +23 -0
  237. package/dist/web/index.html +192 -0
  238. package/dist/web/server.js +1647 -0
  239. package/dist/web/server.js.map +1 -0
  240. package/dist/web/style.css +3260 -0
  241. package/dist/workflows/collaboration.js +374 -0
  242. package/dist/workflows/index.js +54 -0
  243. package/docs/agent-communication.md +333 -0
  244. package/docs/plans/2026-05-15-document-agent-design.md +479 -0
  245. package/docs/plans/2026-05-15-document-agent-implementation-plan.md +792 -0
  246. package/docs/plans/2026-05-16-chat-ui-design.md +86 -0
  247. package/docs/plans/2026-05-16-constraint-runtime-design.md +106 -0
  248. package/docs/plans/2026-05-16-constraint-runtime-implementation.md +441 -0
  249. package/docs//346/225/260/345/255/246/350/276/205/345/212/251/346/231/272/350/203/275/344/275/223-/346/240/270/345/277/203/346/225/210/346/236/234/345/256/232/344/271/211.md +287 -0
  250. package/package.json +121 -0
  251. package/scripts/build-web.ts +61 -0
  252. package/src/agents/constraint-layer.ts +309 -0
  253. package/src/agents/pi-sdk.ts +1591 -0
  254. package/src/agents/protocol.ts +304 -0
  255. package/src/agents/subagent-manager.ts +553 -0
  256. package/src/agents/workflow-engine.ts +332 -0
  257. package/src/bollharness/.boll/CLAUDE.md.template +34 -0
  258. package/src/bollharness/.boll/MANIFEST.yaml +213 -0
  259. package/src/bollharness/.boll/active-review-agents/.gitkeep +0 -0
  260. package/src/bollharness/.boll/agents/review-base.yaml +108 -0
  261. package/src/bollharness/.boll/deploy-allowlist.yaml +38 -0
  262. package/src/bollharness/.boll/inbox/schema/message-v1.json +99 -0
  263. package/src/bollharness/.boll/install-staging/.gitkeep +0 -0
  264. package/src/bollharness/.boll/issue-adapter.yaml +31 -0
  265. package/src/bollharness/.boll/plugins/boll-mode-toolkit/contracts/mode-contract.md +85 -0
  266. package/src/bollharness/.boll/plugins/boll-review-toolkit/contracts/evidence-packet-schema.json +102 -0
  267. package/src/bollharness/.boll/plugins/boll-review-toolkit/contracts/review-contract.yaml +247 -0
  268. package/src/bollharness/.boll/rules/backend-routes.md +31 -0
  269. package/src/bollharness/.boll/rules/closure-semantics.md +30 -0
  270. package/src/bollharness/.boll/rules/env-vars.md +32 -0
  271. package/src/bollharness/.boll/rules/hanis-protocol.md +145 -0
  272. package/src/bollharness/.boll/rules/repo-structure.md +42 -0
  273. package/src/bollharness/.boll/rules/review-agent-isolation.md +73 -0
  274. package/src/bollharness/.boll/rules/source-of-truth.md +33 -0
  275. package/src/bollharness/.boll/settings.json +180 -0
  276. package/src/bollharness/.boll/settings.json.template +31 -0
  277. package/src/bollharness/.boll/skills/arch/SKILL.md +372 -0
  278. package/src/bollharness/.boll/skills/bug-pipeline/SKILL.md +168 -0
  279. package/src/bollharness/.boll/skills/bug-triage/SKILL.md +161 -0
  280. package/src/bollharness/.boll/skills/context-chains/SKILL.md +250 -0
  281. package/src/bollharness/.boll/skills/context-chains/context-chain-index.md +48 -0
  282. package/src/bollharness/.boll/skills/context-chains/work-type-extractors/code_change_extractor.ts +142 -0
  283. package/src/bollharness/.boll/skills/context-chains/work-type-extractors/debugging_extractor.ts +126 -0
  284. package/src/bollharness/.boll/skills/context-chains/work-type-extractors/design_extractor.ts +148 -0
  285. package/src/bollharness/.boll/skills/context-chains/work-type-extractors/planning_extractor.ts +162 -0
  286. package/src/bollharness/.boll/skills/context-chains/work-type-extractors/question_extractor.ts +116 -0
  287. package/src/bollharness/.boll/skills/context-chains/work-type-extractors/review_extractor.ts +136 -0
  288. package/src/bollharness/.boll/skills/crystal-learn/SKILL.md +93 -0
  289. package/src/bollharness/.boll/skills/crystal-learn/invariants/INV-0.md +34 -0
  290. package/src/bollharness/.boll/skills/crystal-learn/invariants/INV-1.md +34 -0
  291. package/src/bollharness/.boll/skills/crystal-learn/invariants/INV-2.md +35 -0
  292. package/src/bollharness/.boll/skills/crystal-learn/invariants/INV-3.md +34 -0
  293. package/src/bollharness/.boll/skills/crystal-learn/invariants/INV-4.md +43 -0
  294. package/src/bollharness/.boll/skills/crystal-learn/invariants/INV-5.md +34 -0
  295. package/src/bollharness/.boll/skills/crystal-learn/invariants/INV-6.md +37 -0
  296. package/src/bollharness/.boll/skills/crystal-learn/invariants/INV-7.md +46 -0
  297. package/src/bollharness/.boll/skills/guardian-fixer/PROMPT.md +415 -0
  298. package/src/bollharness/.boll/skills/guardian-fixer/SKILL.md +320 -0
  299. package/src/bollharness/.boll/skills/harness-dev/SKILL.md +93 -0
  300. package/src/bollharness/.boll/skills/harness-dev/examples/README.md +227 -0
  301. package/src/bollharness/.boll/skills/harness-dev-handoff/SKILL.md +165 -0
  302. package/src/bollharness/.boll/skills/harness-eng/SKILL.md +110 -0
  303. package/src/bollharness/.boll/skills/harness-eng-test/SKILL.md +79 -0
  304. package/src/bollharness/.boll/skills/harness-lab/SKILL.md +170 -0
  305. package/src/bollharness/.boll/skills/harness-ops/SKILL.md +57 -0
  306. package/src/bollharness/.boll/skills/harness-voice/SKILL.md +183 -0
  307. package/src/bollharness/.boll/skills/judgment/SKILL.md +115 -0
  308. package/src/bollharness/.boll/skills/lead/SKILL.md +245 -0
  309. package/src/bollharness/.boll/skills/lead/install-wow-harness.md +77 -0
  310. package/src/bollharness/.boll/skills/lead/ref-review-sop.md +91 -0
  311. package/src/bollharness/.boll/skills/lead/ref-stages.md +129 -0
  312. package/src/bollharness/.boll/skills/skill-discovery/SKILL.md +169 -0
  313. package/src/bollharness/.boll/skills/task-arch/SKILL.md +106 -0
  314. package/src/bollharness/.boll/skills/toolkit/SKILL.md +57 -0
  315. package/src/bollharness/.boll/tasks/.gitkeep +0 -0
  316. package/src/bollharness/.boll/toolkit-index.yaml +112 -0
  317. package/src/bollharness/.claude/agents/review-base.yaml +108 -0
  318. package/src/bollharness/.claude/plugins/boll-mode-toolkit/.claude-plugin/plugin.json +44 -0
  319. package/src/bollharness/.claude/plugins/boll-review-toolkit/.claude-plugin/plugin.json +24 -0
  320. package/src/bollharness/.claude/plugins/boll-review-toolkit/contracts/evidence-packet-schema.json +102 -0
  321. package/src/bollharness/.claude/plugins/boll-review-toolkit/contracts/review-contract.yaml +247 -0
  322. package/src/bollharness/.claude/settings.json +157 -0
  323. package/src/bollharness/.claude/skills/arch/SKILL.md +64 -0
  324. package/src/bollharness/.claude/skills/crystal-learn/SKILL.md +93 -0
  325. package/src/bollharness/.claude/skills/guardian-fixer/PROMPT.md +44 -0
  326. package/src/bollharness/.claude/skills/guardian-fixer/SKILL.md +324 -0
  327. package/src/bollharness/.claude/skills/harness-dev/SKILL.md +93 -0
  328. package/src/bollharness/.claude/skills/harness-dev/examples/README.md +17 -0
  329. package/src/bollharness/.claude/skills/harness-dev-handoff/SKILL.md +165 -0
  330. package/src/bollharness/.claude/skills/harness-eng/SKILL.md +183 -0
  331. package/src/bollharness/.claude/skills/harness-eng-test/SKILL.md +57 -0
  332. package/src/bollharness/.claude/skills/harness-ops/SKILL.md +57 -0
  333. package/src/bollharness/.claude/skills/harness-voice/SKILL.md +84 -0
  334. package/src/bollharness/.claude/skills/lead/INDEX.md +28 -0
  335. package/src/bollharness/.claude/skills/lead/SKILL.md +24 -0
  336. package/src/bollharness/.claude/skills/lead/install-wow-harness.md +77 -0
  337. package/src/bollharness/.claude/skills/lead/ref-review-sop.md +48 -0
  338. package/src/bollharness/.claude/skills/lead/ref-stages.md +58 -0
  339. package/src/bollharness/.claude/skills/plan-lock/SKILL.md +74 -0
  340. package/src/bollharness/.claude/skills/skill-discovery/SKILL.md +120 -0
  341. package/src/bollharness/.claude/skills/task-arch/SKILL.md +106 -0
  342. package/src/bollharness/.claude/skills/toolkit/SKILL.md +57 -0
  343. package/src/bollharness/.claude/skills/toolkit/list.sh +92 -0
  344. package/src/bollharness/.githooks/pre-commit +21 -0
  345. package/src/bollharness/.github/workflows/ci.yml +88 -0
  346. package/src/bollharness/.sanitize-report.json +13 -0
  347. package/src/bollharness/CLAUDE.md +73 -0
  348. package/src/bollharness/LICENSE +21 -0
  349. package/src/bollharness/README.md +143 -0
  350. package/src/bollharness/README.zh-CN.md +131 -0
  351. package/src/bollharness/docs/decisions/ADR-030-guard-signal-protocol-and-governance-reload.md +1076 -0
  352. package/src/bollharness/docs/decisions/ADR-038-harness-optimization-strategy.md +2039 -0
  353. package/src/bollharness/docs/decisions/ADR-041-codex-claude-code-division-of-labor.md +128 -0
  354. package/src/bollharness/docs/decisions/ADR-H1-crystal-learn-revival.md +188 -0
  355. package/src/bollharness/docs/decisions/ADR-H2-identity-isolation.md +183 -0
  356. package/src/bollharness/docs/decisions/ADR-H3-memory-scope.md +133 -0
  357. package/src/bollharness/docs/decisions/ADR-H4-prompt-governance.md +146 -0
  358. package/src/bollharness/docs/decisions/ADR-H5-gate-quantization.md +212 -0
  359. package/src/bollharness/docs/decisions/ADR-H6-state-file-health.md +211 -0
  360. package/src/bollharness/docs/decisions/ADR-H8-issue-and-doc-compliance.md +202 -0
  361. package/src/bollharness/docs/decisions/ADR-H9-mailbox.md +231 -0
  362. package/src/bollharness/docs/decisions/PLAN-H1-crystal-learn-revival.md +270 -0
  363. package/src/bollharness/docs/decisions/PLAN-H2-identity-isolation.md +291 -0
  364. package/src/bollharness/docs/decisions/PLAN-H3-memory-scope.md +228 -0
  365. package/src/bollharness/docs/decisions/PLAN-H4-prompt-governance.md +227 -0
  366. package/src/bollharness/docs/decisions/PLAN-H5-gate-quantization.md +239 -0
  367. package/src/bollharness/docs/decisions/PLAN-H6-state-file-health.md +325 -0
  368. package/src/bollharness/docs/decisions/PLAN-H8-issue-and-doc-compliance.md +242 -0
  369. package/src/bollharness/docs/decisions/PLAN-H9-mailbox.md +378 -0
  370. package/src/bollharness/docs/launch-article-en.md +276 -0
  371. package/src/bollharness/docs/launch-article-zh.md +305 -0
  372. package/src/bollharness/docs/practice.html +356 -0
  373. package/src/bollharness/docs/practice.md +82 -0
  374. package/src/bollharness/docs/research/round-1/README.md +11 -0
  375. package/src/bollharness/docs/research/round-2/README.md +11 -0
  376. package/src/bollharness/docs/research/round-3/README.md +11 -0
  377. package/src/bollharness/docs/research/round-4/README.md +11 -0
  378. package/src/bollharness/docs/research/round-5/README.md +11 -0
  379. package/src/bollharness/docs/research/round-6/README.md +11 -0
  380. package/src/bollharness/package-lock.json +48 -0
  381. package/src/bollharness/package.json +20 -0
  382. package/src/bollharness/reference/SOURCE-COMMIT.txt +3 -0
  383. package/src/bollharness/reference/boll-reference/.claude/rules/backend-routes.md +268 -0
  384. package/src/bollharness/reference/boll-reference/.claude/rules/bridge.md +20 -0
  385. package/src/bollharness/reference/boll-reference/.claude/rules/closure-semantics.md +30 -0
  386. package/src/bollharness/reference/boll-reference/.claude/rules/coaching.md +13 -0
  387. package/src/bollharness/reference/boll-reference/.claude/rules/env-vars.md +50 -0
  388. package/src/bollharness/reference/boll-reference/.claude/rules/hackathon.md +12 -0
  389. package/src/bollharness/reference/boll-reference/.claude/rules/repo-structure.md +184 -0
  390. package/src/bollharness/reference/boll-reference/.claude/rules/review-agent-isolation.md +112 -0
  391. package/src/bollharness/reference/boll-reference/.claude/rules/scenes.md +12 -0
  392. package/src/bollharness/reference/boll-reference/.claude/skills/arch/SKILL.md +551 -0
  393. package/src/bollharness/reference/boll-reference/.claude/skills/boll-animation/SKILL.md +26 -0
  394. package/src/bollharness/reference/boll-reference/.claude/skills/boll-bridge/SKILL.md +227 -0
  395. package/src/bollharness/reference/boll-reference/.claude/skills/boll-bridge/agents/openai.yaml +4 -0
  396. package/src/bollharness/reference/boll-reference/.claude/skills/boll-bridge/references/bridge-failure-taxonomy.md +142 -0
  397. package/src/bollharness/reference/boll-reference/.claude/skills/boll-bridge/references/bridge-validation-ladder.md +107 -0
  398. package/src/bollharness/reference/boll-reference/.claude/skills/boll-crystal/SKILL.md +893 -0
  399. package/src/bollharness/reference/boll-reference/.claude/skills/boll-crystal-learn/SKILL.md +89 -0
  400. package/src/bollharness/reference/boll-reference/.claude/skills/boll-dev/SKILL.md +93 -0
  401. package/src/bollharness/reference/boll-reference/.claude/skills/boll-dev/examples/README.md +209 -0
  402. package/src/bollharness/reference/boll-reference/.claude/skills/boll-dev-handoff/SKILL.md +165 -0
  403. package/src/bollharness/reference/boll-reference/.claude/skills/boll-eng/SKILL.md +110 -0
  404. package/src/bollharness/reference/boll-reference/.claude/skills/boll-eng-frontend/SKILL.md +203 -0
  405. package/src/bollharness/reference/boll-reference/.claude/skills/boll-eng-hdc/SKILL.md +27 -0
  406. package/src/bollharness/reference/boll-reference/.claude/skills/boll-eng-orchestrator/SKILL.md +28 -0
  407. package/src/bollharness/reference/boll-reference/.claude/skills/boll-eng-prompt/SKILL.md +27 -0
  408. package/src/bollharness/reference/boll-reference/.claude/skills/boll-eng-test/SKILL.md +79 -0
  409. package/src/bollharness/reference/boll-reference/.claude/skills/boll-lab/SKILL.md +372 -0
  410. package/src/bollharness/reference/boll-reference/.claude/skills/boll-run/SKILL.md +437 -0
  411. package/src/bollharness/reference/boll-reference/.claude/skills/boll-ux-appstore/SKILL.md +27 -0
  412. package/src/bollharness/reference/boll-reference/.claude/skills/boll-voice/SKILL.md +442 -0
  413. package/src/bollharness/reference/boll-reference/.claude/skills/guardian-fixer/PROMPT.md +421 -0
  414. package/src/bollharness/reference/boll-reference/.claude/skills/guardian-fixer/SKILL.md +326 -0
  415. package/src/bollharness/reference/boll-reference/.claude/skills/lead/SKILL.md +155 -0
  416. package/src/bollharness/reference/boll-reference/.claude/skills/lead/ref-review-sop.md +91 -0
  417. package/src/bollharness/reference/boll-reference/.claude/skills/lead/ref-stages.md +129 -0
  418. package/src/bollharness/reference/boll-reference/.claude/skills/nature-designer/output/skill-map-preview.png +0 -0
  419. package/src/bollharness/reference/boll-reference/.claude/skills/nature-designer/output/skill-map-v2.png +0 -0
  420. package/src/bollharness/reference/boll-reference/.claude/skills/nature-designer/output/skill-map-v3.png +0 -0
  421. package/src/bollharness/reference/boll-reference/.claude/skills/nature-designer/output/skill-map-v4.png +0 -0
  422. package/src/bollharness/reference/boll-reference/.claude/skills/plan-lock/SKILL.md +425 -0
  423. package/src/bollharness/reference/boll-reference/.claude/skills/plan-lock/ref-three-checks.md +62 -0
  424. package/src/bollharness/reference/boll-reference/.claude/skills/plan-lock/ref-wp-templates.md +78 -0
  425. package/src/bollharness/reference/boll-reference/.claude/skills/task-arch/SKILL.md +76 -0
  426. package/src/bollharness/reference/boll-reference/.claude/skills/vibedevteam-graph/SKILL.md +57 -0
  427. package/src/bollharness/reference/boll-reference/.claude/skills/vibedevteam-graph/beads-graph.sh +153 -0
  428. package/src/bollharness/reference/boll-reference/.claude/skills/vibedevteam-init/SKILL.md +52 -0
  429. package/src/bollharness/reference/boll-reference/.claude/skills/vibedevteam-init/beads-auto-link.sh +76 -0
  430. package/src/bollharness/reference/boll-reference/.claude/skills/vibedevteam-sync/SKILL.md +50 -0
  431. package/src/bollharness/reference/boll-reference/.claude/skills/vibedevteam-sync/beads-sync-proj.sh +108 -0
  432. package/src/bollharness/reference/boll-reference/docs/architecture/AGENT-PROFILE.md +151 -0
  433. package/src/bollharness/reference/boll-reference/docs/architecture/COST-STRUCTURE.md +56 -0
  434. package/src/bollharness/reference/boll-reference/docs/architecture/INDEX.md +76 -0
  435. package/src/bollharness/reference/boll-reference/docs/architecture/MODULE1-INTENT-FIELD.md +116 -0
  436. package/src/bollharness/reference/boll-reference/docs/architecture/MODULE2-CRYSTALLIZATION.md +200 -0
  437. package/src/bollharness/reference/boll-reference/docs/architecture/PRINCIPLES.md +84 -0
  438. package/src/bollharness/reference/boll-reference/docs/architecture/PROTOCOL-CORE.md +209 -0
  439. package/src/bollharness/reference/boll-reference/docs/architecture/VISION.md +181 -0
  440. package/src/bollharness/reference/boll-reference/docs/architecture/discussions/D-01-MARKET-SCENE-PROTOCOL.md +754 -0
  441. package/src/bollharness/reference/boll-reference/scripts/hooks/.sanitize-report.json +12 -0
  442. package/src/bollharness/reference/boll-reference/scripts/hooks/find-boll-root.sh +27 -0
  443. package/src/bollharness/reference/boll-reference/scripts/hooks/precompact.sh +57 -0
  444. package/src/bollharness/reference/boll-reference/scripts/hooks/stop-evaluator.md +57 -0
  445. package/src/bollharness/schemas/metrics-jsonl-allowlist.json +67 -0
  446. package/src/bollharness/scripts/checks/next_decision_number.sh +48 -0
  447. package/src/bollharness/scripts/ci/count-components.sh +65 -0
  448. package/src/bollharness/scripts/context-fragments/artifact-linkage.md +14 -0
  449. package/src/bollharness/scripts/context-fragments/auth-consumers.md +17 -0
  450. package/src/bollharness/scripts/context-fragments/bridge-constitution.md +13 -0
  451. package/src/bollharness/scripts/context-fragments/catalyst-distributed.md +18 -0
  452. package/src/bollharness/scripts/context-fragments/closure-checklist.md +13 -0
  453. package/src/bollharness/scripts/context-fragments/contract-consumers.md +15 -0
  454. package/src/bollharness/scripts/context-fragments/db-shared-structures.md +15 -0
  455. package/src/bollharness/scripts/context-fragments/fixed-three-layers.md +19 -0
  456. package/src/bollharness/scripts/context-fragments/general-dev-principles.md +11 -0
  457. package/src/bollharness/scripts/context-fragments/issue-first.md +8 -0
  458. package/src/bollharness/scripts/context-fragments/mcp-parity.md +16 -0
  459. package/src/bollharness/scripts/context-fragments/pi-agent-operations.md +74 -0
  460. package/src/bollharness/scripts/context-fragments/protocol-consumers.md +15 -0
  461. package/src/bollharness/scripts/context-fragments/run-events-consumers.md +15 -0
  462. package/src/bollharness/scripts/context-fragments/scene-fidelity.md +13 -0
  463. package/src/bollharness/scripts/context-fragments/truth-source-hierarchy.md +15 -0
  464. package/src/bollharness/scripts/context-fragments/two-language.md +15 -0
  465. package/src/bollharness/scripts/context-fragments/version-sources.md +14 -0
  466. package/src/bollharness/scripts/hooks/find-project-root.sh +47 -0
  467. package/src/bollharness/scripts/hooks/inbox-poll.sh +78 -0
  468. package/src/bollharness/scripts/hooks/precompact.sh +56 -0
  469. package/src/bollharness/scripts/hooks/stop-evaluator.md +83 -0
  470. package/src/bollharness/scripts/sync-from-upstream.sh +281 -0
  471. package/src/bollharness/src/index.ts +5 -0
  472. package/src/bollharness/src/scripts/checks/check_adr_plan_numbering.ts +11 -0
  473. package/src/bollharness/src/scripts/checks/check_api_types.ts +52 -0
  474. package/src/bollharness/src/scripts/checks/check_artifact_link.ts +156 -0
  475. package/src/bollharness/src/scripts/checks/check_bridge_deps.ts +11 -0
  476. package/src/bollharness/src/scripts/checks/check_bugfix_binding.ts +11 -0
  477. package/src/bollharness/src/scripts/checks/check_bugfix_binding_ci.ts +11 -0
  478. package/src/bollharness/src/scripts/checks/check_doc_file_references.ts +11 -0
  479. package/src/bollharness/src/scripts/checks/check_doc_freshness.ts +141 -0
  480. package/src/bollharness/src/scripts/checks/check_doc_links.ts +36 -0
  481. package/src/bollharness/src/scripts/checks/check_file_existence_claims.ts +11 -0
  482. package/src/bollharness/src/scripts/checks/check_fragment_integrity.ts +40 -0
  483. package/src/bollharness/src/scripts/checks/check_hook_installed.ts +70 -0
  484. package/src/bollharness/src/scripts/checks/check_issue_closure.ts +51 -0
  485. package/src/bollharness/src/scripts/checks/check_mcp_parity.ts +11 -0
  486. package/src/bollharness/src/scripts/checks/check_security.ts +54 -0
  487. package/src/bollharness/src/scripts/checks/check_skill_parity.ts +11 -0
  488. package/src/bollharness/src/scripts/checks/check_versions.ts +11 -0
  489. package/src/bollharness/src/scripts/checks/finding.ts +35 -0
  490. package/src/bollharness/src/scripts/checks/next_decision_number.ts +24 -0
  491. package/src/bollharness/src/scripts/checks/regenerate_magic_docs.ts +11 -0
  492. package/src/bollharness/src/scripts/ci/detect_rebaseline_triggers.ts +14 -0
  493. package/src/bollharness/src/scripts/ci/scan_subprocess_cfg.ts +14 -0
  494. package/src/bollharness/src/scripts/ci/scan_verify_artifacts.ts +14 -0
  495. package/src/bollharness/src/scripts/ci/scan_yaml_schema.ts +14 -0
  496. package/src/bollharness/src/scripts/context_router.ts +76 -0
  497. package/src/bollharness/src/scripts/deploy-guard.ts +182 -0
  498. package/src/bollharness/src/scripts/guard-feedback.ts +215 -0
  499. package/src/bollharness/src/scripts/guard_router.ts +194 -0
  500. package/src/bollharness/src/scripts/hooks/_hook_output.js +3 -0
  501. package/src/bollharness/src/scripts/hooks/_hook_output.ts +11 -0
  502. package/src/bollharness/src/scripts/hooks/auto-python3.ts +10 -0
  503. package/src/bollharness/src/scripts/hooks/deploy-progress-on-session-end.ts +10 -0
  504. package/src/bollharness/src/scripts/hooks/failure-analyzer.ts +10 -0
  505. package/src/bollharness/src/scripts/hooks/gate-judgment-inject.ts +111 -0
  506. package/src/bollharness/src/scripts/hooks/gate-transition-judgment.ts +74 -0
  507. package/src/bollharness/src/scripts/hooks/inbox-ack.ts +10 -0
  508. package/src/bollharness/src/scripts/hooks/inbox-inject-on-start.ts +10 -0
  509. package/src/bollharness/src/scripts/hooks/inbox-validate.ts +10 -0
  510. package/src/bollharness/src/scripts/hooks/inbox-write-ledger.ts +10 -0
  511. package/src/bollharness/src/scripts/hooks/initializer-agent.ts +10 -0
  512. package/src/bollharness/src/scripts/hooks/loop-detection.ts +83 -0
  513. package/src/bollharness/src/scripts/hooks/owner-guard.ts +10 -0
  514. package/src/bollharness/src/scripts/hooks/precompact.ts +10 -0
  515. package/src/bollharness/src/scripts/hooks/review-agent-gatekeeper.ts +10 -0
  516. package/src/bollharness/src/scripts/hooks/risk-tracker.ts +121 -0
  517. package/src/bollharness/src/scripts/hooks/sanitize-on-read.ts +10 -0
  518. package/src/bollharness/src/scripts/hooks/session-reflection.ts +12 -0
  519. package/src/bollharness/src/scripts/hooks/session-start-magic-docs.ts +12 -0
  520. package/src/bollharness/src/scripts/hooks/session-start-reset-risk.ts +12 -0
  521. package/src/bollharness/src/scripts/hooks/session-start-toolkit-reminder.ts +12 -0
  522. package/src/bollharness/src/scripts/hooks/stop-evaluator.ts +164 -0
  523. package/src/bollharness/src/scripts/hooks/tool-call-counter.ts +10 -0
  524. package/src/bollharness/src/scripts/hooks/trace-analyzer.ts +14 -0
  525. package/src/bollharness/src/scripts/install/install-trust-token.ts +13 -0
  526. package/src/bollharness/src/scripts/install/multi_project_registry.ts +13 -0
  527. package/src/bollharness/src/scripts/install/phase2_auto.ts +28 -0
  528. package/src/bollharness/src/scripts/install/pre_commit_installer.ts +10 -0
  529. package/src/bollharness/src/scripts/install/tier_selector.ts +10 -0
  530. package/src/bollharness/src/scripts/install/transcript_miner.ts +13 -0
  531. package/src/bollharness/src/scripts/lib/claim_patterns.ts +11 -0
  532. package/src/bollharness/src/scripts/lib/sanitize_patterns.ts +13 -0
  533. package/src/bollharness/src/scripts/sanitize.ts +9 -0
  534. package/src/bollharness/templates/persona/default.json +19 -0
  535. package/src/bollharness/templates/scaffold/.boll/guard/.gitkeep +0 -0
  536. package/src/bollharness/templates/scaffold/.boll/metrics/.gitkeep +0 -0
  537. package/src/bollharness/templates/scaffold/.boll/state/.gitkeep +0 -0
  538. package/src/bollharness/templates/scaffold/.gitignore.append +16 -0
  539. package/src/bollharness/templates/scaffold/CLAUDE.md +89 -0
  540. package/src/bollharness/templates/scaffold/docs/INDEX.md +3 -0
  541. package/src/bollharness/templates/scaffold/docs/decisions/ADR_TEMPLATE.md +38 -0
  542. package/src/bollharness/templates/scaffold/docs/decisions/PLAN_TEMPLATE.md +45 -0
  543. package/src/bollharness/templates/scaffold/docs/decisions/tasks/.gitkeep +2 -0
  544. package/src/bollharness/templates/scaffold/docs/issues/.gitkeep +0 -0
  545. package/src/bollharness/templates/scaffold/docs/issues/GUARD_ISSUE_TEMPLATE.md +35 -0
  546. package/src/bollharness/templates/scaffold/docs/issues/ISSUE_TEMPLATE.md +51 -0
  547. package/src/bollharness/tsconfig.json +26 -0
  548. package/src/bollharness-integration/channel-judgment-engine.ts +634 -0
  549. package/src/bollharness-integration/context-chain-router.ts +474 -0
  550. package/src/bollharness-integration/context-router-judgment.ts +339 -0
  551. package/src/bollharness-integration/context-router.ts +583 -0
  552. package/src/bollharness-integration/gate-state-machine.ts +444 -0
  553. package/src/bollharness-integration/gate-transition-hooks.ts +137 -0
  554. package/src/bollharness-integration/guard-checker.ts +451 -0
  555. package/src/bollharness-integration/index.ts +219 -0
  556. package/src/bollharness-integration/integration.ts +538 -0
  557. package/src/bollharness-integration/judgment-prompts.yaml +535 -0
  558. package/src/bollharness-integration/llm-judgment-engine.ts +712 -0
  559. package/src/bollharness-integration/skill-adapter.ts +646 -0
  560. package/src/cli/interface.ts +211 -0
  561. package/src/constraint-runtime/package-lock.json +48 -0
  562. package/src/constraint-runtime/package.json +34 -0
  563. package/src/constraint-runtime/src/_archive_helper.ts +16 -0
  564. package/src/constraint-runtime/src/agent/coordinator.ts +71 -0
  565. package/src/constraint-runtime/src/agent/index.ts +1 -0
  566. package/src/constraint-runtime/src/assistant/index.ts +15 -0
  567. package/src/constraint-runtime/src/bootstrap/index.ts +15 -0
  568. package/src/constraint-runtime/src/bootstrap_graph.ts +17 -0
  569. package/src/constraint-runtime/src/bridge/index.ts +15 -0
  570. package/src/constraint-runtime/src/buddy/index.ts +15 -0
  571. package/src/constraint-runtime/src/cli/index.ts +15 -0
  572. package/src/constraint-runtime/src/command_graph.ts +20 -0
  573. package/src/constraint-runtime/src/commands.ts +83 -0
  574. package/src/constraint-runtime/src/components/index.ts +15 -0
  575. package/src/constraint-runtime/src/constants/index.ts +15 -0
  576. package/src/constraint-runtime/src/constraint/budget.ts +25 -0
  577. package/src/constraint-runtime/src/constraint/index.ts +3 -0
  578. package/src/constraint-runtime/src/constraint/permission.ts +28 -0
  579. package/src/constraint-runtime/src/context.ts +45 -0
  580. package/src/constraint-runtime/src/coordinator/index.ts +15 -0
  581. package/src/constraint-runtime/src/cost_hook.ts +6 -0
  582. package/src/constraint-runtime/src/cost_tracker.ts +9 -0
  583. package/src/constraint-runtime/src/deferred_init.ts +18 -0
  584. package/src/constraint-runtime/src/direct_modes.ts +13 -0
  585. package/src/constraint-runtime/src/dynamic-tool-loader.ts +115 -0
  586. package/src/constraint-runtime/src/entrypoints/index.ts +15 -0
  587. package/src/constraint-runtime/src/execution_registry.ts +41 -0
  588. package/src/constraint-runtime/src/history.ts +16 -0
  589. package/src/constraint-runtime/src/hooks/index.ts +15 -0
  590. package/src/constraint-runtime/src/index.ts +28 -0
  591. package/src/constraint-runtime/src/ink.ts +4 -0
  592. package/src/constraint-runtime/src/keybindings/index.ts +15 -0
  593. package/src/constraint-runtime/src/memdir/index.ts +15 -0
  594. package/src/constraint-runtime/src/migrations/index.ts +15 -0
  595. package/src/constraint-runtime/src/models.ts +49 -0
  596. package/src/constraint-runtime/src/moreright/index.ts +15 -0
  597. package/src/constraint-runtime/src/native_ts/index.ts +15 -0
  598. package/src/constraint-runtime/src/output_styles/index.ts +15 -0
  599. package/src/constraint-runtime/src/parity_audit.ts +23 -0
  600. package/src/constraint-runtime/src/plugins/index.ts +15 -0
  601. package/src/constraint-runtime/src/port_manifest.ts +20 -0
  602. package/src/constraint-runtime/src/prefetch.ts +17 -0
  603. package/src/constraint-runtime/src/query.ts +7 -0
  604. package/src/constraint-runtime/src/reference_data/archive_surface_snapshot.json +63 -0
  605. package/src/constraint-runtime/src/reference_data/commands_snapshot.json +1037 -0
  606. package/src/constraint-runtime/src/reference_data/subsystems/OpenCLI.json +10 -0
  607. package/src/constraint-runtime/src/reference_data/subsystems/PolymarketSDK.json +12 -0
  608. package/src/constraint-runtime/src/reference_data/subsystems/SafeSDK.json +14 -0
  609. package/src/constraint-runtime/src/reference_data/subsystems/assistant.json +8 -0
  610. package/src/constraint-runtime/src/reference_data/subsystems/bootstrap.json +8 -0
  611. package/src/constraint-runtime/src/reference_data/subsystems/bridge.json +32 -0
  612. package/src/constraint-runtime/src/reference_data/subsystems/buddy.json +13 -0
  613. package/src/constraint-runtime/src/reference_data/subsystems/cli.json +26 -0
  614. package/src/constraint-runtime/src/reference_data/subsystems/components.json +32 -0
  615. package/src/constraint-runtime/src/reference_data/subsystems/constants.json +28 -0
  616. package/src/constraint-runtime/src/reference_data/subsystems/coordinator.json +8 -0
  617. package/src/constraint-runtime/src/reference_data/subsystems/entrypoints.json +15 -0
  618. package/src/constraint-runtime/src/reference_data/subsystems/hooks.json +32 -0
  619. package/src/constraint-runtime/src/reference_data/subsystems/keybindings.json +21 -0
  620. package/src/constraint-runtime/src/reference_data/subsystems/memdir.json +15 -0
  621. package/src/constraint-runtime/src/reference_data/subsystems/migrations.json +18 -0
  622. package/src/constraint-runtime/src/reference_data/subsystems/moreright.json +8 -0
  623. package/src/constraint-runtime/src/reference_data/subsystems/native_ts.json +11 -0
  624. package/src/constraint-runtime/src/reference_data/subsystems/outputStyles.json +8 -0
  625. package/src/constraint-runtime/src/reference_data/subsystems/plugins.json +9 -0
  626. package/src/constraint-runtime/src/reference_data/subsystems/remote.json +11 -0
  627. package/src/constraint-runtime/src/reference_data/subsystems/schemas.json +8 -0
  628. package/src/constraint-runtime/src/reference_data/subsystems/screens.json +10 -0
  629. package/src/constraint-runtime/src/reference_data/subsystems/server.json +10 -0
  630. package/src/constraint-runtime/src/reference_data/subsystems/services.json +32 -0
  631. package/src/constraint-runtime/src/reference_data/subsystems/skills.json +27 -0
  632. package/src/constraint-runtime/src/reference_data/subsystems/state.json +13 -0
  633. package/src/constraint-runtime/src/reference_data/subsystems/types.json +18 -0
  634. package/src/constraint-runtime/src/reference_data/subsystems/upstreamproxy.json +9 -0
  635. package/src/constraint-runtime/src/reference_data/subsystems/utils.json +32 -0
  636. package/src/constraint-runtime/src/reference_data/subsystems/vim.json +12 -0
  637. package/src/constraint-runtime/src/reference_data/subsystems/voice.json +8 -0
  638. package/src/constraint-runtime/src/reference_data/tools_snapshot.json +1042 -0
  639. package/src/constraint-runtime/src/remote/index.ts +15 -0
  640. package/src/constraint-runtime/src/remote_runtime.ts +17 -0
  641. package/src/constraint-runtime/src/runtime/index.ts +1 -0
  642. package/src/constraint-runtime/src/runtime/session.ts +42 -0
  643. package/src/constraint-runtime/src/schemas/index.ts +15 -0
  644. package/src/constraint-runtime/src/screens/index.ts +15 -0
  645. package/src/constraint-runtime/src/server/index.ts +15 -0
  646. package/src/constraint-runtime/src/services/index.ts +15 -0
  647. package/src/constraint-runtime/src/session_store.ts +32 -0
  648. package/src/constraint-runtime/src/setup.ts +50 -0
  649. package/src/constraint-runtime/src/skills/index.ts +1 -0
  650. package/src/constraint-runtime/src/skills/skill-registry.ts +40 -0
  651. package/src/constraint-runtime/src/state/index.ts +15 -0
  652. package/src/constraint-runtime/src/system_init.ts +21 -0
  653. package/src/constraint-runtime/src/thinking/engine.ts +61 -0
  654. package/src/constraint-runtime/src/thinking/index.ts +1 -0
  655. package/src/constraint-runtime/src/tool_pool.ts +20 -0
  656. package/src/constraint-runtime/src/tools/OpenCLI/execAdapter.ts +12 -0
  657. package/src/constraint-runtime/src/tools/OpenCLI/listAdapters.ts +12 -0
  658. package/src/constraint-runtime/src/tools/OpenCLI/runCommand.ts +13 -0
  659. package/src/constraint-runtime/src/tools/PolymarketSDK/cancelOrder.ts +10 -0
  660. package/src/constraint-runtime/src/tools/PolymarketSDK/createOrder.ts +13 -0
  661. package/src/constraint-runtime/src/tools/PolymarketSDK/getMarket.ts +14 -0
  662. package/src/constraint-runtime/src/tools/PolymarketSDK/getOrders.ts +10 -0
  663. package/src/constraint-runtime/src/tools/PolymarketSDK/listMarkets.ts +24 -0
  664. package/src/constraint-runtime/src/tools/SafeSDK/confirmTransaction.ts +13 -0
  665. package/src/constraint-runtime/src/tools/SafeSDK/createTransaction.ts +23 -0
  666. package/src/constraint-runtime/src/tools/SafeSDK/deploySafe.ts +12 -0
  667. package/src/constraint-runtime/src/tools/SafeSDK/executeTransaction.ts +12 -0
  668. package/src/constraint-runtime/src/tools/SafeSDK/getBalance.ts +10 -0
  669. package/src/constraint-runtime/src/tools/SafeSDK/getPendingTransactions.ts +10 -0
  670. package/src/constraint-runtime/src/tools/SafeSDK/proposeTransaction.ts +14 -0
  671. package/src/constraint-runtime/src/tools/WalletTools/autoPay.ts +58 -0
  672. package/src/constraint-runtime/src/tools/WalletTools/createWallet.ts +19 -0
  673. package/src/constraint-runtime/src/tools/WalletTools/getBalance.ts +28 -0
  674. package/src/constraint-runtime/src/tools/WalletTools/importWallet.ts +34 -0
  675. package/src/constraint-runtime/src/tools/WalletTools/sendTransaction.ts +50 -0
  676. package/src/constraint-runtime/src/tools/WalletTools/signMessage.ts +23 -0
  677. package/src/constraint-runtime/src/tools/WalletTools/transferToken.ts +49 -0
  678. package/src/constraint-runtime/src/tools.ts +100 -0
  679. package/src/constraint-runtime/src/transcript.ts +23 -0
  680. package/src/constraint-runtime/src/types/index.ts +15 -0
  681. package/src/constraint-runtime/src/upstream_proxy/index.ts +15 -0
  682. package/src/constraint-runtime/src/utils/index.ts +15 -0
  683. package/src/constraint-runtime/src/vim/index.ts +15 -0
  684. package/src/constraint-runtime/src/voice/index.ts +15 -0
  685. package/src/constraint-runtime/tests/agent.test.ts +20 -0
  686. package/src/constraint-runtime/tests/constraint.test.ts +47 -0
  687. package/src/constraint-runtime/tests/skill.test.ts +23 -0
  688. package/src/constraint-runtime/tests/thinking.test.ts +28 -0
  689. package/src/constraint-runtime/tsconfig.json +13 -0
  690. package/src/constraints/index.ts +6 -0
  691. package/src/documents/reader.ts +80 -0
  692. package/src/electron-preload.ts +25 -0
  693. package/src/electron.ts +200 -0
  694. package/src/index.ts +1606 -0
  695. package/src/llm/config-store.ts +312 -0
  696. package/src/llm/llm-judgment-client.ts +470 -0
  697. package/src/llm/pi-ai.ts +522 -0
  698. package/src/network/agent-network.ts +800 -0
  699. package/src/network/hybrid-messenger.ts +199 -0
  700. package/src/network/iroh-bootstrap.ts +57 -0
  701. package/src/network/iroh-discovery.ts +207 -0
  702. package/src/network/iroh-integration.ts +158 -0
  703. package/src/network/iroh-transport.ts +594 -0
  704. package/src/network/p2p.ts +963 -0
  705. package/src/network/storage/adapters/json-adapter.ts +439 -0
  706. package/src/network/storage/index.ts +187 -0
  707. package/src/network/storage/types.ts +166 -0
  708. package/src/pi-ecosystem/index.ts +453 -0
  709. package/src/pi-ecosystem-colony/index.ts +482 -0
  710. package/src/pi-ecosystem-goals/index.ts +585 -0
  711. package/src/pi-ecosystem-judgment/decision.ts +431 -0
  712. package/src/pi-ecosystem-judgment/distillation.ts +398 -0
  713. package/src/pi-ecosystem-judgment/human-value-store.ts +580 -0
  714. package/src/pi-ecosystem-judgment/index.ts +678 -0
  715. package/src/pi-ecosystem-judgment/value-injection.ts +744 -0
  716. package/src/pi-ecosystem-mcp/index.ts +427 -0
  717. package/src/pi-ecosystem-subagents/index.ts +408 -0
  718. package/src/social/ant-colony/AdaptiveHeartbeat.ts +131 -0
  719. package/src/social/ant-colony/PheromoneEngine.ts +302 -0
  720. package/src/social/ant-colony/index.ts +18 -0
  721. package/src/social/ant-colony/types.ts +94 -0
  722. package/src/social/channels/ChannelManager.ts +485 -0
  723. package/src/social/channels/DiapChannelBridge.ts +501 -0
  724. package/src/social/channels/InterestMatcher.ts +189 -0
  725. package/src/social/channels/agent-workflow-config.json +214 -0
  726. package/src/social/channels/agent-workflow-config.yaml +334 -0
  727. package/src/social/channels/channel-agent-session.ts +407 -0
  728. package/src/social/channels/channel-heartbeat-agent.ts +622 -0
  729. package/src/social/channels/diap-doc-parser.ts +282 -0
  730. package/src/social/channels/harness-workflow-integrator.ts +594 -0
  731. package/src/social/channels/index.ts +22 -0
  732. package/src/social/channels/types.ts +115 -0
  733. package/src/social/global-shared-context.ts +506 -0
  734. package/src/social/heartbeat.ts +1041 -0
  735. package/src/social/persona/enhanced-persona.ts +359 -0
  736. package/src/test/ai-judgment-test.ts +92 -0
  737. package/src/test/bollharness-integration.test.ts +398 -0
  738. package/src/test/channel-agent-multi-dialogue.ts +265 -0
  739. package/src/test/channel-heartbeat-agent-test.ts +244 -0
  740. package/src/test/constraint-layer.test.ts +191 -0
  741. package/src/test/diap-identity-test.ts +222 -0
  742. package/src/test/diap-quick-test.ts +73 -0
  743. package/src/test/global-shared-context.test.ts +393 -0
  744. package/src/test/harness-judgment-injection.test.ts +353 -0
  745. package/src/test/harness-workflow-integrator-test.ts +285 -0
  746. package/src/test/human-value-store.test.ts +316 -0
  747. package/src/test/hybrid-integration-test.ts +126 -0
  748. package/src/test/hybrid-messenger-verify.ts +68 -0
  749. package/src/test/iroh-bistream-debug.ts +50 -0
  750. package/src/test/iroh-communication.test.ts +81 -0
  751. package/src/test/iroh-debug-test.ts +69 -0
  752. package/src/test/iroh-diap-test.ts +90 -0
  753. package/src/test/iroh-direct-connect.ts +65 -0
  754. package/src/test/iroh-e2e-fixed.ts +106 -0
  755. package/src/test/iroh-e2e-same-process.ts +83 -0
  756. package/src/test/iroh-e2e.ts +83 -0
  757. package/src/test/iroh-final-e2e.ts +84 -0
  758. package/src/test/iroh-relay-test.ts +46 -0
  759. package/src/test/iroh-simple-test.ts +49 -0
  760. package/src/test/iroh-transport-verify.ts +60 -0
  761. package/src/test/iroh-transport.test.ts +47 -0
  762. package/src/test/iroh-two-nodes.ts +87 -0
  763. package/src/test/iroh-verify.ts +55 -0
  764. package/src/test/judgment-decision.test.ts +373 -0
  765. package/src/test/llm-judgment-integration.test.ts +257 -0
  766. package/src/test/p2p-agent-complex-dialogue.ts +490 -0
  767. package/src/test/p2p-agent-dialogue.ts +423 -0
  768. package/src/test/p2p-agent-full-bidirectional.ts +686 -0
  769. package/src/test/p2p-agent-harness-flow.ts +562 -0
  770. package/src/test/p2p-agent-harness-single.ts +175 -0
  771. package/src/test/p2p-ai-dialogue-test.ts +374 -0
  772. package/src/test/p2p-cid-connect-test.ts +245 -0
  773. package/src/test/p2p-connect-receiver.ts +85 -0
  774. package/src/test/p2p-iroh-test.ts +214 -0
  775. package/src/test/p2p-minimal-test.ts +264 -0
  776. package/src/test/p2p-node-1.ts +172 -0
  777. package/src/test/p2p-node-2.ts +172 -0
  778. package/src/test/p2p-server.ts +335 -0
  779. package/src/test/p2p-two-nodes-test.ts +542 -0
  780. package/src/test/pi-sdk.test.ts +47 -0
  781. package/src/test/set-persona.ts +56 -0
  782. package/src/test/simple.test.ts +11 -0
  783. package/src/test/storage-integration.test.ts +191 -0
  784. package/src/test/subagent-manager.test.ts +392 -0
  785. package/src/test/test-gate-flow.test.ts +92 -0
  786. package/src/test/workflow-engine.test.ts +101 -0
  787. package/src/web/api-config.html +265 -0
  788. package/src/web/client.js +1401 -0
  789. package/src/web/components/p2p/P2PModal.tsx +328 -0
  790. package/src/web/components/p2p/index.ts +404 -0
  791. package/src/web/components/p2p/index.tsx +297 -0
  792. package/src/web/components/p2p/p2p-connection.ts +186 -0
  793. package/src/web/components/p2p/p2p-identity.ts +92 -0
  794. package/src/web/components/p2p/p2p-manager.ts +109 -0
  795. package/src/web/components/p2p/p2p-messages.ts +219 -0
  796. package/src/web/components/p2p/p2p-modal.ts +685 -0
  797. package/src/web/components/p2p/p2p-store-memory.ts +162 -0
  798. package/src/web/components/p2p/types.ts +112 -0
  799. package/src/web/design.md +99 -0
  800. package/src/web/index.html +192 -0
  801. package/src/web/server.ts +1878 -0
  802. package/src/web/style.css +3260 -0
  803. package/src/workflows/collaboration.ts +455 -0
  804. package/src/workflows/index.ts +64 -0
  805. package/tsconfig.electron.json +20 -0
  806. package/tsconfig.json +19 -0
  807. package/vitest.config.ts +12 -0
  808. package//346/203/263/346/263/225.md +79 -0
@@ -0,0 +1,1878 @@
1
+ import express from 'express';
2
+ import { createServer } from 'http';
3
+ import { join, dirname } from 'path';
4
+ import { fileURLToPath } from 'url';
5
+ import * as fs from 'fs/promises';
6
+ import * as path from 'path';
7
+ import {
8
+ HyperswarmCommunicator,
9
+ createHyperswarmCommunicator,
10
+ createTopic,
11
+ KeyManager,
12
+ AgentAuthManager,
13
+ type P2PConnection,
14
+ } from '@diap/sdk';
15
+ import type { AgentVerificationManager } from '@diap/sdk';
16
+ import { documentReader } from '../documents/reader.js';
17
+ import { initMinimax, getMinimax } from '../constraints/index.js';
18
+ import { createAgentSession, type AgentSession, type StreamCallback, type StreamEvent } from '../agents/pi-sdk.js';
19
+ import { llmConfigStore, type ModelProvider, PROVIDER_INFO } from '../llm/config-store.js';
20
+ import { irohTransport } from '../network/iroh-transport.js';
21
+
22
+ // 前端资源路径:在打包后会通过 CommonJS require 加载,使用 import.meta.url
23
+ const __filename = fileURLToPath(import.meta.url);
24
+ const __dirname = dirname(__filename);
25
+ const webRoot = path.join(__dirname, '..', '..', 'dist', 'web');
26
+
27
+ const SHARED_SESSION_PATH = path.join(process.env.HOME || '/tmp', '.bolloon', 'sessions');
28
+ const SESSION_CACHE_PATH = path.join(SHARED_SESSION_PATH, 'cache');
29
+ const CHANNELS_PATH = path.join(SHARED_SESSION_PATH, 'channels.json');
30
+ const THEME_PATH = path.join(SHARED_SESSION_PATH, 'theme.json');
31
+ const IPFS_ENDPOINT = 'http://127.0.0.1:5001';
32
+
33
+ // iroh P2P 状态
34
+ interface IrohNodeInfo {
35
+ did: string;
36
+ cid: string;
37
+ irohNodeId: string;
38
+ name: string;
39
+ initialized: boolean;
40
+ }
41
+ let irohNodeInfo: IrohNodeInfo | null = null;
42
+ let irohInitialized = false;
43
+
44
+ interface Channel {
45
+ id: string;
46
+ name: string;
47
+ agentId: string;
48
+ did?: string;
49
+ publicKey?: string;
50
+ cid?: string;
51
+ didDocument?: any;
52
+ createdAt: string;
53
+ updatedAt: string;
54
+ }
55
+
56
+ interface SessionMessage {
57
+ id: string;
58
+ type: 'user' | 'ai';
59
+ content: string;
60
+ timestamp: string;
61
+ }
62
+
63
+ interface Session {
64
+ channelId: string;
65
+ messages: SessionMessage[];
66
+ lastUpdated: string;
67
+ }
68
+
69
+ async function ensureSessionDirs() {
70
+ await fs.mkdir(SESSION_CACHE_PATH, { recursive: true });
71
+ }
72
+
73
+ async function loadChannels(): Promise<Channel[]> {
74
+ try {
75
+ const data = await fs.readFile(CHANNELS_PATH, 'utf-8');
76
+ return JSON.parse(data);
77
+ } catch {
78
+ return [];
79
+ }
80
+ }
81
+
82
+ async function saveChannels(channels: Channel[]): Promise<void> {
83
+ const jsonStr = JSON.stringify(channels, null, 2);
84
+ console.log('[saveChannels] 保存频道数据, 数量:', channels.length);
85
+ console.log('[saveChannels] JSON 长度:', jsonStr.length);
86
+ await fs.writeFile(CHANNELS_PATH, jsonStr);
87
+
88
+ // 验证保存的内容
89
+ const verifyData = await fs.readFile(CHANNELS_PATH, 'utf-8');
90
+ const verifyChannels = JSON.parse(verifyData);
91
+ console.log('[saveChannels] 验证 - 保存了', verifyChannels.length, '个频道');
92
+ verifyChannels.forEach((ch: Channel, i: number) => {
93
+ console.log(` [${i}] ${ch.name}: did=${ch.did || '无'}`);
94
+ });
95
+ }
96
+
97
+ async function loadSession(channelId: string): Promise<Session | null> {
98
+ const sessionPath = path.join(SESSION_CACHE_PATH, `${channelId}.json`);
99
+ try {
100
+ const data = await fs.readFile(sessionPath, 'utf-8');
101
+ return JSON.parse(data);
102
+ } catch {
103
+ return null;
104
+ }
105
+ }
106
+
107
+ async function saveSession(session: Session): Promise<void> {
108
+ const sessionPath = path.join(SESSION_CACHE_PATH, `${session.channelId}.json`);
109
+ await fs.writeFile(sessionPath, JSON.stringify(session, null, 2));
110
+ }
111
+
112
+ async function loadTheme(): Promise<{ theme: 'light' | 'dark'; agentId: string }> {
113
+ try {
114
+ const data = await fs.readFile(THEME_PATH, 'utf-8');
115
+ return JSON.parse(data);
116
+ } catch {
117
+ return { theme: 'light', agentId: '' };
118
+ }
119
+ }
120
+
121
+ async function saveTheme(theme: 'light' | 'dark', agentId: string): Promise<void> {
122
+ await fs.writeFile(THEME_PATH, JSON.stringify({ theme, agentId }, null, 2));
123
+ }
124
+
125
+ // ==================== Task Queue & Workflow System ====================
126
+
127
+ const TASK_QUEUE_PATH = path.join(SHARED_SESSION_PATH, 'task-queue.json');
128
+ const WORKFLOW_STATE_PATH = path.join(SHARED_SESSION_PATH, 'workflow-state.json');
129
+
130
+ interface Task {
131
+ id: string;
132
+ type: 'read' | 'summarize' | 'improve' | 'chat' | 'workflow';
133
+ title: string;
134
+ description?: string;
135
+ status: 'pending' | 'running' | 'completed' | 'failed' | 'paused';
136
+ progress: number; // 0-100
137
+ result?: string;
138
+ error?: string;
139
+ createdAt: string;
140
+ updatedAt: string;
141
+ steps?: TaskStep[];
142
+ currentStep?: number;
143
+ }
144
+
145
+ interface TaskStep {
146
+ id: string;
147
+ name: string;
148
+ status: 'pending' | 'running' | 'completed' | 'failed';
149
+ result?: string;
150
+ }
151
+
152
+ interface WorkflowState {
153
+ channelId: string;
154
+ tasks: Task[];
155
+ lastUpdated: string;
156
+ }
157
+
158
+ async function loadTaskQueue(): Promise<Task[]> {
159
+ try {
160
+ const data = await fs.readFile(TASK_QUEUE_PATH, 'utf-8');
161
+ return JSON.parse(data);
162
+ } catch {
163
+ return [];
164
+ }
165
+ }
166
+
167
+ async function saveTaskQueue(tasks: Task[]): Promise<void> {
168
+ await fs.writeFile(TASK_QUEUE_PATH, JSON.stringify(tasks, null, 2));
169
+ }
170
+
171
+ async function loadWorkflowState(channelId: string): Promise<WorkflowState | null> {
172
+ try {
173
+ const data = await fs.readFile(WORKFLOW_STATE_PATH, 'utf-8');
174
+ const states = JSON.parse(data) as WorkflowState[];
175
+ return states.find(s => s.channelId === channelId) || null;
176
+ } catch {
177
+ return null;
178
+ }
179
+ }
180
+
181
+ async function saveWorkflowState(state: WorkflowState): Promise<void> {
182
+ try {
183
+ const data = await fs.readFile(WORKFLOW_STATE_PATH, 'utf-8');
184
+ const states = JSON.parse(data) as WorkflowState[];
185
+ const index = states.findIndex(s => s.channelId === state.channelId);
186
+ if (index >= 0) {
187
+ states[index] = state;
188
+ } else {
189
+ states.push(state);
190
+ }
191
+ await fs.writeFile(WORKFLOW_STATE_PATH, JSON.stringify(states, null, 2));
192
+ } catch {
193
+ await fs.writeFile(WORKFLOW_STATE_PATH, JSON.stringify([state], null, 2));
194
+ }
195
+ }
196
+
197
+ let isExecutingTask = false;
198
+ let executionTaskId: string | null = null;
199
+
200
+ async function executeTask(task: Task, channelId: string): Promise<void> {
201
+ if (isExecutingTask) return;
202
+ isExecutingTask = true;
203
+ executionTaskId = task.id;
204
+
205
+ const agent = await getAgentForChannel(channelId);
206
+ const tasks = await loadTaskQueue();
207
+ const taskIndex = tasks.findIndex(t => t.id === task.id);
208
+ if (taskIndex >= 0) {
209
+ tasks[taskIndex].status = 'running';
210
+ tasks[taskIndex].updatedAt = new Date().toISOString();
211
+ await saveTaskQueue(tasks);
212
+ }
213
+
214
+ broadcast({ type: 'task_status', taskId: task.id, status: 'running', progress: 0 }, channelId);
215
+
216
+ try {
217
+ let result = '';
218
+
219
+ switch (task.type) {
220
+ case 'chat':
221
+ if (task.description) {
222
+ broadcast({ type: 'status', content: `执行任务: ${task.title}` }, channelId);
223
+ result = await agent.prompt(task.description);
224
+ }
225
+ break;
226
+
227
+ case 'read':
228
+ if (task.description) {
229
+ broadcast({ type: 'status', content: `读取文档: ${task.description}` }, channelId);
230
+ const content = await documentReader.read(task.description);
231
+ result = `📄 文档读取完成\n\n${content.text.substring(0, 500)}${content.text.length > 500 ? '...' : ''}`;
232
+ }
233
+ break;
234
+
235
+ case 'summarize':
236
+ if (task.description) {
237
+ broadcast({ type: 'status', content: `总结文档: ${task.description}` }, channelId);
238
+ const content = await documentReader.read(task.description);
239
+ const llm = getMinimax();
240
+ const summary = await llm.summarize(content.text);
241
+ result = `📝 文档总结:\n\n${summary.summary}`;
242
+ }
243
+ break;
244
+
245
+ case 'workflow':
246
+ // 执行多步骤工作流
247
+ if (task.steps && task.steps.length > 0) {
248
+ let loopCount = 0;
249
+ for (let i = 0; i < task.steps.length; i++) {
250
+ // 广播循环开始
251
+ loopCount++;
252
+ broadcast({ type: 'workflow_loop', loopCount, content: `开始步骤 ${i + 1}/${task.steps.length}: ${task.steps[i].name}` }, channelId);
253
+
254
+ task.steps[i].status = 'running';
255
+ broadcast({ type: 'task_status', taskId: task.id, status: 'running', currentStep: i, totalSteps: task.steps.length }, channelId);
256
+ broadcast({ type: 'workflow_step', step: `步骤 ${i + 1}`, content: `执行中: ${task.steps[i].name}` }, channelId);
257
+
258
+ // 执行步骤 - 模拟流式输出
259
+ for (let j = 0; j < 3; j++) {
260
+ await new Promise(resolve => setTimeout(resolve, 300));
261
+ broadcast({ type: 'workflow_step', step: `步骤 ${i + 1}`, content: `执行中... (${(j + 1) * 33}%)` }, channelId);
262
+ }
263
+
264
+ task.steps[i].status = 'completed';
265
+ task.progress = Math.round(((i + 1) / task.steps.length) * 100);
266
+
267
+ broadcast({ type: 'workflow_step', step: `步骤 ${i + 1}`, content: `✅ 完成: ${task.steps[i].name}` }, channelId);
268
+ broadcast({ type: 'workflow_loop', loopCount, status: 'completed', content: `步骤 ${i + 1} 完成` }, channelId);
269
+ broadcast({ type: 'task_status', taskId: task.id, progress: task.progress }, channelId);
270
+ }
271
+ result = '✅ 工作流执行完成';
272
+ broadcast({ type: 'workflow_loop', loopCount, status: 'finished', content: result }, channelId);
273
+ }
274
+ break;
275
+
276
+ default:
277
+ result = '未知任务类型';
278
+ }
279
+
280
+ // 更新任务状态
281
+ const tasks = await loadTaskQueue();
282
+ const idx = tasks.findIndex(t => t.id === task.id);
283
+ if (idx >= 0) {
284
+ tasks[idx].status = 'completed';
285
+ tasks[idx].progress = 100;
286
+ tasks[idx].result = result;
287
+ tasks[idx].updatedAt = new Date().toISOString();
288
+ await saveTaskQueue(tasks);
289
+ }
290
+
291
+ broadcast({ type: 'task_status', taskId: task.id, status: 'completed', progress: 100, result }, channelId);
292
+ broadcast({ type: 'ai', content: result }, channelId);
293
+
294
+ } catch (error: any) {
295
+ const tasks = await loadTaskQueue();
296
+ const idx = tasks.findIndex(t => t.id === task.id);
297
+ if (idx >= 0) {
298
+ tasks[idx].status = 'failed';
299
+ tasks[idx].error = error.message;
300
+ tasks[idx].updatedAt = new Date().toISOString();
301
+ await saveTaskQueue(tasks);
302
+ }
303
+
304
+ broadcast({ type: 'task_status', taskId: task.id, status: 'failed', error: error.message }, channelId);
305
+ broadcast({ type: 'error', content: `任务执行失败: ${error.message}` }, channelId);
306
+ }
307
+
308
+ isExecutingTask = false;
309
+ executionTaskId = null;
310
+ }
311
+
312
+ interface SSEClient {
313
+ res: express.Response;
314
+ channelId?: string;
315
+ }
316
+
317
+ let sseClients: Set<SSEClient> = new Set();
318
+ let channelSessions: Map<string, AgentSession> = new Map();
319
+
320
+ async function getAgentForChannel(
321
+ channelId: string,
322
+ channelDid?: string,
323
+ channelName?: string,
324
+ channelDidDoc?: any
325
+ ): Promise<AgentSession> {
326
+ const existingSession = channelSessions.get(channelId);
327
+
328
+ // 如果已有 session,检查是否需要更新 identity
329
+ if (existingSession) {
330
+ const currentIdentity = existingSession.getIdentity();
331
+
332
+ // 如果当前 identity 没有真实 DID,或者 DID 与频道的 DID 不匹配,需要重建
333
+ let needsUpdate = !currentIdentity.did.startsWith('did:pi:') ||
334
+ (channelDid && !currentIdentity.did.includes(channelId));
335
+
336
+ if (!needsUpdate && channelDid && currentIdentity.did !== channelDid) {
337
+ needsUpdate = true;
338
+ }
339
+
340
+ if (needsUpdate && channelDid) {
341
+ // 更新现有 session 的 identity
342
+ existingSession.updateIdentity({
343
+ did: channelDid,
344
+ name: channelName || `Channel-${channelId.slice(-6)}`,
345
+ publicKey: '',
346
+ createdAt: Date.now(),
347
+ cid: channelDidDoc?.cid,
348
+ ipnsName: channelDidDoc?.ipnsName
349
+ });
350
+ console.log(`[Agent] 频道 ${channelId} 身份更新: DID = ${channelDid}`);
351
+ }
352
+ return existingSession;
353
+ }
354
+
355
+ // 构建频道的身份文档
356
+ const identityDoc = channelDid ? {
357
+ did: channelDid,
358
+ name: channelName || `Channel-${channelId.slice(-6)}`,
359
+ publicKey: '',
360
+ createdAt: Date.now(),
361
+ cid: channelDidDoc?.cid,
362
+ ipnsName: channelDidDoc?.ipnsName
363
+ } : undefined;
364
+
365
+ const session = await createAgentSession({
366
+ cwd: process.cwd(),
367
+ peerId: `channel-${channelId}`,
368
+ identityDoc
369
+ });
370
+ channelSessions.set(channelId, session);
371
+
372
+ if (channelDid) {
373
+ console.log(`[Agent] 新建频道 ${channelId} session, DID = ${channelDid}, CID = ${channelDidDoc?.cid || '无'}`);
374
+ } else {
375
+ console.log(`[Agent] 新建频道 ${channelId} session, 使用默认身份`);
376
+ }
377
+
378
+ return session;
379
+ }
380
+
381
+ export async function createWebServer(port: number = 3000) {
382
+ // 防止 P2P DHT 超时等错误导致进程崩溃
383
+ process.on('unhandledRejection', (reason, promise) => {
384
+ console.error('[警告] 未处理的 Promise 拒绝:', reason);
385
+ });
386
+
387
+ // 重置旧的 agent session,确保使用新的 LLM 配置
388
+ const { resetAgentSession } = await import('../agents/pi-sdk.js');
389
+ resetAgentSession();
390
+
391
+ // 初始化 LLM(从配置文件读取 MiniMax 配置)
392
+ initMinimax();
393
+
394
+ // ==================== P2P DIAP 身份初始化 ====================
395
+ let p2pIdentity = {
396
+ did: '',
397
+ name: '',
398
+ publicKey: '',
399
+ keypair: null as any
400
+ };
401
+ let p2pCommunicator: HyperswarmCommunicator | null = null;
402
+
403
+ try {
404
+ console.log('开始生成 P2P 身份...');
405
+
406
+ // 生成 DIAP 身份
407
+ const kp = KeyManager.generate();
408
+ console.log('KeyManager.generate() 完成, kp:', !!kp, 'kp.did:', kp?.did);
409
+ console.log('kp.publicKey:', kp?.publicKey);
410
+
411
+ const did = kp.did || 'did:unknown:123456';
412
+ console.log(`DID: ${did}`);
413
+
414
+ const username = 'web-user';
415
+ const suffix = did?.split(':').pop()?.substring(0, 4) || 'xxxx';
416
+ const name = `blln-${username}-${suffix}`;
417
+
418
+ p2pIdentity = {
419
+ did: did || '',
420
+ name,
421
+ publicKey: Buffer.from(kp.publicKey).toString('hex'),
422
+ keypair: kp
423
+ };
424
+
425
+ console.log(`P2P 身份已生成: ${p2pIdentity.did}`);
426
+
427
+ // 尝试发布 DID 到 IPFS
428
+ try {
429
+ const auth = await AgentAuthManager.newWithRemoteIpfs('http://127.0.0.1:5001', 'http://127.0.0.1:8080');
430
+ await auth.registerAgent({ name, services: [] }, kp, '');
431
+ console.log('P2P DID 已发布到 IPFS');
432
+ } catch (e) {
433
+ console.log('P2P DID 本地模式运行');
434
+ }
435
+
436
+ // 初始化 P2P 通信器
437
+ try {
438
+ const rawSeed = crypto.getRandomValues(new Uint8Array(32));
439
+ p2pCommunicator = createHyperswarmCommunicator({
440
+ server: true,
441
+ client: true,
442
+ autoConnect: true,
443
+ maxConnections: 50,
444
+ seed: rawSeed
445
+ } as any);
446
+
447
+ p2pCommunicator.on('connection', (conn: P2PConnection) => {
448
+ console.log(`P2P 连接: ${conn.publicKey.substring(0, 8)}...`);
449
+ });
450
+
451
+ p2pCommunicator.on('message', async (msg: any, conn: P2PConnection) => {
452
+ const content = new TextDecoder().decode(msg.content);
453
+ console.log(`P2P 收到消息: ${content.substring(0, 50)}...`);
454
+ // 可以在这里处理接收到的消息
455
+ broadcast({ type: 'p2p_message', from: conn.publicKey.substring(0, 8), content }, undefined);
456
+ });
457
+
458
+ await p2pCommunicator.start();
459
+ const topic = createTopic('bolloon-agent-harness') as Buffer;
460
+ await p2pCommunicator.joinTopic(topic);
461
+ console.log(`P2P 网络已就绪`);
462
+ } catch (e: any) {
463
+ console.log(`P2P 网络初始化失败: ${e.message}`);
464
+ }
465
+ } catch (e: any) {
466
+ console.log(`P2P 身份初始化失败: ${e.message}`);
467
+ }
468
+
469
+ const app = express();
470
+ const server = createServer(app);
471
+
472
+ await ensureSessionDirs();
473
+
474
+ app.use(express.json());
475
+
476
+ app.use((req, res, next) => {
477
+ res.setHeader('Access-Control-Allow-Origin', '*');
478
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS');
479
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
480
+ if (req.method === 'OPTIONS') {
481
+ return res.status(200).end();
482
+ }
483
+ next();
484
+ });
485
+
486
+ app.use(express.static(webRoot));
487
+
488
+ app.get('/', (req, res) => {
489
+ res.sendFile(join(webRoot, 'index.html'));
490
+ });
491
+
492
+ app.get('/api-config', (req, res) => {
493
+ res.sendFile(join(webRoot, 'api-config.html'));
494
+ });
495
+
496
+ app.get('/events', (req, res) => {
497
+ const channelId = req.query.channelId as string;
498
+ res.setHeader('Content-Type', 'text/event-stream');
499
+ res.setHeader('Cache-Control', 'no-cache');
500
+ res.setHeader('Connection', 'keep-alive');
501
+ res.flushHeaders();
502
+
503
+ const clientInfo = { res, channelId };
504
+ sseClients.add(clientInfo as any);
505
+
506
+ req.on('close', () => {
507
+ sseClients.delete(clientInfo as any);
508
+ });
509
+ });
510
+
511
+ app.post('/message', async (req, res) => {
512
+ const { text, channelId, channelDid } = req.body;
513
+ if (!text) {
514
+ return res.status(400).json({ error: 'No text provided' });
515
+ }
516
+
517
+ if (!channelId) {
518
+ return res.status(400).json({ error: 'No channelId provided' });
519
+ }
520
+
521
+ // 获取频道信息,包括真实 DID 和完整 DID 文档
522
+ const channels = await loadChannels();
523
+ const channel = channels.find(c => c.id === channelId);
524
+ const realChannelDid = channelDid || channel?.did || '';
525
+ const realChannelName = channel?.name || '';
526
+ const realChannelDidDoc = channel?.didDocument;
527
+
528
+ broadcast({ type: 'user', content: text }, channelId);
529
+
530
+ try {
531
+ const agent = await getAgentForChannel(channelId, realChannelDid, realChannelName, realChannelDidDoc);
532
+ let fullResponse = '';
533
+
534
+ const streamCallback: StreamCallback = (event: StreamEvent) => {
535
+ // 同时发送给流式显示和工作流显示
536
+ if (event.type === 'token' || event.type === 'thinking') {
537
+ broadcast({ type: 'stream', streamType: event.type, content: event.content }, channelId);
538
+ // 同时作为 workflow_step 显示(用于动态 loop 循环)
539
+ if (event.content) {
540
+ broadcast({ type: 'workflow_step', step: 'AI 思考', content: event.content.substring(0, 100) }, channelId);
541
+ }
542
+ } else if (event.type === 'status' || event.type === 'tool') {
543
+ broadcast({ type: 'status', tool: event.tool, content: event.content }, channelId);
544
+ broadcast({ type: 'workflow_step', step: event.tool || '状态', content: event.content }, channelId);
545
+ } else if (event.type === 'error') {
546
+ broadcast({ type: 'error', content: event.content }, channelId);
547
+ }
548
+ };
549
+
550
+ // 将真实 DID 作为上下文前缀,让 AI 使用真实的 DID 而不是自己编造的
551
+ const contextHint = realChannelDid ? `[系统上下文] 当前频道名称: ${realChannelName}, 你的真实 DID: ${realChannelDid}\n\n` : '';
552
+ fullResponse = await agent.promptStream(contextHint + text, streamCallback);
553
+
554
+ broadcast({ type: 'ai', content: fullResponse }, channelId);
555
+
556
+ const existingSession = await loadSession(channelId);
557
+ const session: Session = existingSession || { channelId, messages: [], lastUpdated: new Date().toISOString() };
558
+ session.messages.push({ id: crypto.randomUUID(), type: 'user' as const, content: text, timestamp: new Date().toISOString() });
559
+ session.messages.push({ id: crypto.randomUUID(), type: 'ai' as const, content: fullResponse, timestamp: new Date().toISOString() });
560
+ session.lastUpdated = new Date().toISOString();
561
+ await saveSession(session);
562
+
563
+ const channels = await loadChannels();
564
+ const channel = channels.find(c => c.id === channelId);
565
+ if (channel && channel.name === '智能体') {
566
+ const renameSuggestion = await agent.suggestRename(session.messages);
567
+ if (renameSuggestion) {
568
+ channel.name = renameSuggestion;
569
+ await saveChannels(channels);
570
+ broadcast({ type: 'renamed', channelId, newName: renameSuggestion }, channelId);
571
+ }
572
+ }
573
+ if (channel) {
574
+ channel.updatedAt = new Date().toISOString();
575
+ await saveChannels(channels);
576
+ }
577
+
578
+ broadcast({ type: 'done' }, channelId);
579
+ res.json({ ok: true });
580
+ } catch (err: any) {
581
+ broadcast({ type: 'error', content: err.message }, channelId);
582
+ broadcast({ type: 'done' }, channelId);
583
+ res.status(500).json({ error: err.message });
584
+ }
585
+ });
586
+
587
+ // 获取频道并确保每个频道都有 DID
588
+ async function getChannelsWithDID(): Promise<Channel[]> {
589
+ const channels = await loadChannels();
590
+ console.log(`[getChannelsWithDID] 加载了 ${channels.length} 个频道`);
591
+ let changed = false;
592
+
593
+ for (const channel of channels) {
594
+ // 检查 DID 是否有效
595
+ const didMissing = channel.did === undefined || channel.did === null || channel.did === 'undefined' || channel.did === 'null' || channel.did === '';
596
+ console.log(`[getChannelsWithDID] 频道 ${channel.name}: did=${JSON.stringify(channel.did)}, 缺失=${didMissing}`);
597
+
598
+ if (didMissing) {
599
+ console.log(`[修复频道] ${channel.name} (${channel.id}) 缺少 DID,正在生成...`);
600
+ try {
601
+ const kp = KeyManager.generate();
602
+ const generatedDid = kp.did;
603
+ console.log(`[修复频道] KeyManager.generate() 结果: kp=${!!kp}, did=${generatedDid}`);
604
+
605
+ if (generatedDid && typeof generatedDid === 'string' && generatedDid.length > 0) {
606
+ channel.did = generatedDid;
607
+ channel.publicKey = Buffer.from(kp.publicKey).toString('hex');
608
+ console.log(`[修复频道] ${channel.name} 生成了 DID: ${channel.did}`);
609
+
610
+ // 发布到 IPFS 并保存完整 DID 文档
611
+ try {
612
+ const auth = await AgentAuthManager.newWithRemoteIpfs('http://127.0.0.1:5001', 'http://127.0.0.1:8080');
613
+ const result = await auth.registerAgent({ name: channel.name, services: [] }, kp, '');
614
+ channel.cid = result.cid || '';
615
+ // 保存完整 DID 文档(用于传递给 session)
616
+ if (result.didDocument) {
617
+ channel.didDocument = result.didDocument;
618
+ }
619
+ console.log(`[修复频道] ${channel.name} CID: ${channel.cid}`);
620
+ } catch (ipfsErr) {
621
+ console.log(`[修复频道] ${channel.name} IPFS 失败`);
622
+ }
623
+ } else {
624
+ console.log(`[修复频道] ${channel.name} KeyManager 返回无效 DID`);
625
+ channel.did = `did:web:${channel.id}`;
626
+ channel.publicKey = `pk_${channel.id}`;
627
+ }
628
+ changed = true;
629
+ } catch (e) {
630
+ console.log(`[修复频道] ${channel.name} 失败: ${e}`);
631
+ }
632
+ }
633
+ }
634
+
635
+ if (changed) {
636
+ console.log('[getChannelsWithDID] 保存修改后的频道');
637
+ await saveChannels(channels);
638
+ }
639
+
640
+ return channels;
641
+ }
642
+
643
+ app.get('/channels', async (_req, res) => {
644
+ try {
645
+ console.log('[API] /channels 被调用');
646
+ const channels = await getChannelsWithDID();
647
+ console.log('[获取频道] 返回', channels.length, '个');
648
+ channels.forEach((ch, i) => {
649
+ console.log(` [${i}] ${ch.name} - did: ${ch.did || '无'} - cid: ${ch.cid || '无'}`);
650
+ });
651
+ res.json(channels);
652
+ } catch (err: any) {
653
+ console.error('[API] /channels 错误:', err);
654
+ res.status(500).json({ error: err.message });
655
+ }
656
+ });
657
+
658
+ app.post('/channels', async (req, res) => {
659
+ try {
660
+ const { name, agentId } = req.body;
661
+ console.log(`[创建频道] 收到请求: name=${name}, agentId=${agentId}`);
662
+ if (!name || !agentId) {
663
+ return res.status(400).json({ error: 'name and agentId required' });
664
+ }
665
+ const channels = await loadChannels();
666
+ const id = `ch_${Date.now()}_${Math.random().toString(36).substring(2, 8)}`;
667
+
668
+ // 先创建频道(不阻塞等待 DID 生成)
669
+ const channel: Channel = {
670
+ id,
671
+ name,
672
+ agentId,
673
+ createdAt: new Date().toISOString(),
674
+ updatedAt: new Date().toISOString(),
675
+ };
676
+
677
+ console.log(`[创建频道] 先保存频道 ID: ${id}`);
678
+ channels.push(channel);
679
+ await saveChannels(channels);
680
+ await saveSession({ channelId: id, messages: [], lastUpdated: new Date().toISOString() });
681
+ res.json(channel);
682
+
683
+ // 后台生成 DID
684
+ console.log(`[创建频道] 后台生成 DID...`);
685
+ setTimeout(async () => {
686
+ try {
687
+ const kp = KeyManager.generate();
688
+ if (kp.did) {
689
+ const allChannels = await loadChannels();
690
+ const ch = allChannels.find(c => c.id === id);
691
+ if (ch) {
692
+ ch.did = kp.did;
693
+ ch.publicKey = Buffer.from(kp.publicKey).toString('hex');
694
+ console.log(`[创建频道] DID 生成完成: ${ch.did}`);
695
+
696
+ // 发布到 IPFS
697
+ try {
698
+ const auth = await AgentAuthManager.newWithRemoteIpfs('http://127.0.0.1:5001', 'http://127.0.0.1:8080');
699
+ const result = await auth.registerAgent({ name, services: [] }, kp, '');
700
+ ch.cid = result.cid || '';
701
+ console.log(`[创建频道] CID: ${ch.cid}`);
702
+ } catch {}
703
+
704
+ await saveChannels(allChannels);
705
+ }
706
+ }
707
+ } catch (e) {
708
+ console.log(`[创建频道] ${name} 后台生成 DID 失败`);
709
+ }
710
+ }, 100);
711
+ } catch (err: any) {
712
+ console.error('[创建频道] 错误:', err);
713
+ res.status(500).json({ error: err.message });
714
+ }
715
+ });
716
+
717
+ app.delete('/channels/:channelId', async (req, res) => {
718
+ try {
719
+ const { channelId } = req.params;
720
+ const channels = await loadChannels();
721
+ const index = channels.findIndex(c => c.id === channelId);
722
+ if (index === -1) {
723
+ return res.status(404).json({ error: 'Channel not found' });
724
+ }
725
+ channels.splice(index, 1);
726
+ await saveChannels(channels);
727
+ try {
728
+ await fs.unlink(path.join(SESSION_CACHE_PATH, `${channelId}.json`));
729
+ } catch {}
730
+ res.json({ ok: true });
731
+ } catch (err: any) {
732
+ res.status(500).json({ error: err.message });
733
+ }
734
+ });
735
+
736
+ app.patch('/channels/:channelId', async (req, res) => {
737
+ try {
738
+ const { channelId } = req.params;
739
+ const { name } = req.body;
740
+ if (!name) {
741
+ return res.status(400).json({ error: 'Name required' });
742
+ }
743
+ const channels = await loadChannels();
744
+ const channel = channels.find(c => c.id === channelId);
745
+ if (!channel) {
746
+ return res.status(404).json({ error: 'Channel not found' });
747
+ }
748
+ channel.name = name;
749
+ channel.updatedAt = new Date().toISOString();
750
+ await saveChannels(channels);
751
+ res.json(channel);
752
+ } catch (err: any) {
753
+ res.status(500).json({ error: err.message });
754
+ }
755
+ });
756
+
757
+ app.get('/sessions/:channelId', async (req, res) => {
758
+ try {
759
+ const session = await loadSession(req.params.channelId);
760
+ res.json(session || { channelId: req.params.channelId, messages: [], lastUpdated: null });
761
+ } catch (err: any) {
762
+ res.status(500).json({ error: err.message });
763
+ }
764
+ });
765
+
766
+ app.get('/theme', async (req, res) => {
767
+ try {
768
+ const themeData = await loadTheme();
769
+ res.json(themeData);
770
+ } catch (err: any) {
771
+ res.json({ theme: 'light', agentId: '' });
772
+ }
773
+ });
774
+
775
+ app.post('/theme', async (req, res) => {
776
+ try {
777
+ const { theme, agentId } = req.body;
778
+ if (theme !== 'light' && theme !== 'dark') {
779
+ return res.status(400).json({ error: 'Invalid theme' });
780
+ }
781
+ await saveTheme(theme, agentId || '');
782
+ res.json({ ok: true });
783
+ } catch (err: any) {
784
+ res.status(500).json({ error: err.message });
785
+ }
786
+ });
787
+
788
+ // 重新生成回复
789
+ app.post('/regenerate', async (req, res) => {
790
+ const { channelId, userMessage } = req.body;
791
+ if (!channelId) {
792
+ return res.status(400).json({ error: 'No channelId provided' });
793
+ }
794
+ if (!userMessage) {
795
+ return res.status(400).json({ error: 'No userMessage provided' });
796
+ }
797
+
798
+ try {
799
+ const channels = await loadChannels();
800
+ const channel = channels.find(c => c.id === channelId);
801
+ const realChannelDid = channel?.did || '';
802
+ const realChannelName = channel?.name || '';
803
+ const realChannelDidDoc = channel?.didDocument;
804
+
805
+ // 通知前端开始重新生成
806
+ broadcast({ type: 'regenerating', channelId }, channelId);
807
+
808
+ const agent = await getAgentForChannel(channelId, realChannelDid, realChannelName, realChannelDidDoc);
809
+ let fullResponse = '';
810
+
811
+ const streamCallback: StreamCallback = (event: StreamEvent) => {
812
+ if (event.type === 'token' || event.type === 'thinking') {
813
+ broadcast({ type: 'stream', streamType: event.type, content: event.content }, channelId);
814
+ } else if (event.type === 'status' || event.type === 'tool') {
815
+ broadcast({ type: 'status', tool: event.tool, content: event.content }, channelId);
816
+ } else if (event.type === 'error') {
817
+ broadcast({ type: 'error', content: event.content }, channelId);
818
+ }
819
+ };
820
+
821
+ // 重新生成时只发送用户消息
822
+ fullResponse = await agent.promptStream(userMessage, streamCallback);
823
+
824
+ broadcast({ type: 'ai', content: fullResponse }, channelId);
825
+
826
+ // 更新 session
827
+ const existingSession = await loadSession(channelId);
828
+ if (existingSession && existingSession.messages.length > 0) {
829
+ // 移除最后一个 AI 消息,替换为新的
830
+ const lastAiIndex = existingSession.messages.map((m: any) => m.type).lastIndexOf('ai');
831
+ if (lastAiIndex !== -1) {
832
+ existingSession.messages = existingSession.messages.slice(0, lastAiIndex);
833
+ }
834
+ existingSession.messages.push({
835
+ id: crypto.randomUUID(),
836
+ type: 'ai' as const,
837
+ content: fullResponse,
838
+ timestamp: new Date().toISOString()
839
+ });
840
+ existingSession.lastUpdated = new Date().toISOString();
841
+ await saveSession(existingSession);
842
+ }
843
+
844
+ broadcast({ type: 'done' }, channelId);
845
+ res.json({ ok: true });
846
+ } catch (err: any) {
847
+ broadcast({ type: 'error', content: err.message }, channelId);
848
+ broadcast({ type: 'done' }, channelId);
849
+ res.status(500).json({ error: err.message });
850
+ }
851
+ });
852
+
853
+ // ==================== Task Queue API ====================
854
+
855
+ // 获取所有任务
856
+ app.get('/api/tasks', async (req, res) => {
857
+ try {
858
+ const tasks = await loadTaskQueue();
859
+ res.json(tasks);
860
+ } catch (err: any) {
861
+ res.status(500).json({ error: err.message });
862
+ }
863
+ });
864
+
865
+ // 创建新任务
866
+ app.post('/api/tasks', async (req, res) => {
867
+ try {
868
+ const { type, title, description, steps } = req.body;
869
+ if (!type || !title) {
870
+ return res.status(400).json({ error: 'type and title required' });
871
+ }
872
+
873
+ const tasks = await loadTaskQueue();
874
+ const task: Task = {
875
+ id: `task_${Date.now()}_${Math.random().toString(36).substring(2, 8)}`,
876
+ type,
877
+ title,
878
+ description,
879
+ status: 'pending',
880
+ progress: 0,
881
+ createdAt: new Date().toISOString(),
882
+ updatedAt: new Date().toISOString(),
883
+ steps: steps?.map((s: string, i: number) => ({
884
+ id: `step_${i}`,
885
+ name: s,
886
+ status: 'pending'
887
+ }))
888
+ };
889
+
890
+ tasks.push(task);
891
+ await saveTaskQueue(tasks);
892
+
893
+ res.json(task);
894
+ } catch (err: any) {
895
+ res.status(500).json({ error: err.message });
896
+ }
897
+ });
898
+
899
+ // 获取单个任务
900
+ app.get('/api/tasks/:taskId', async (req, res) => {
901
+ try {
902
+ const { taskId } = req.params;
903
+ const tasks = await loadTaskQueue();
904
+ const task = tasks.find(t => t.id === taskId);
905
+ if (!task) {
906
+ return res.status(404).json({ error: 'Task not found' });
907
+ }
908
+ res.json(task);
909
+ } catch (err: any) {
910
+ res.status(500).json({ error: err.message });
911
+ }
912
+ });
913
+
914
+ // 更新任务
915
+ app.patch('/api/tasks/:taskId', async (req, res) => {
916
+ try {
917
+ const { taskId } = req.params;
918
+ const { status, currentStep } = req.body;
919
+ const tasks = await loadTaskQueue();
920
+ const taskIndex = tasks.findIndex(t => t.id === taskId);
921
+ if (taskIndex === -1) {
922
+ return res.status(404).json({ error: 'Task not found' });
923
+ }
924
+
925
+ if (status) {
926
+ tasks[taskIndex].status = status;
927
+ }
928
+ if (currentStep !== undefined) {
929
+ tasks[taskIndex].currentStep = currentStep;
930
+ }
931
+ tasks[taskIndex].updatedAt = new Date().toISOString();
932
+
933
+ await saveTaskQueue(tasks);
934
+ res.json(tasks[taskIndex]);
935
+ } catch (err: any) {
936
+ res.status(500).json({ error: err.message });
937
+ }
938
+ });
939
+
940
+ // 删除任务
941
+ app.delete('/api/tasks/:taskId', async (req, res) => {
942
+ try {
943
+ const { taskId } = req.params;
944
+ const tasks = await loadTaskQueue();
945
+ const filtered = tasks.filter(t => t.id !== taskId);
946
+ await saveTaskQueue(filtered);
947
+ res.json({ ok: true });
948
+ } catch (err: any) {
949
+ res.status(500).json({ error: err.message });
950
+ }
951
+ });
952
+
953
+ // 执行任务(自动执行下一步)
954
+ app.post('/api/tasks/:taskId/execute', async (req, res) => {
955
+ try {
956
+ const { taskId } = req.params;
957
+ const { channelId } = req.body;
958
+ if (!channelId) {
959
+ return res.status(400).json({ error: 'channelId required' });
960
+ }
961
+
962
+ const tasks = await loadTaskQueue();
963
+ const task = tasks.find(t => t.id === taskId);
964
+ if (!task) {
965
+ return res.status(404).json({ error: 'Task not found' });
966
+ }
967
+
968
+ if (isExecutingTask) {
969
+ return res.status(409).json({ error: 'Another task is currently executing' });
970
+ }
971
+
972
+ // 异步执行任务
973
+ executeTask(task, channelId);
974
+
975
+ res.json({ ok: true, taskId: task.id });
976
+ } catch (err: any) {
977
+ res.status(500).json({ error: err.message });
978
+ }
979
+ });
980
+
981
+ // 执行下一个待处理任务
982
+ app.post('/api/tasks/execute-next', async (req, res) => {
983
+ try {
984
+ const { channelId } = req.body;
985
+ if (!channelId) {
986
+ return res.status(400).json({ error: 'channelId required' });
987
+ }
988
+
989
+ const tasks = await loadTaskQueue();
990
+ const nextTask = tasks.find(t => t.status === 'pending');
991
+
992
+ if (!nextTask) {
993
+ return res.json({ ok: false, message: 'No pending tasks' });
994
+ }
995
+
996
+ if (isExecutingTask) {
997
+ return res.status(409).json({ error: 'Another task is currently executing' });
998
+ }
999
+
1000
+ // 异步执行任务
1001
+ executeTask(nextTask, channelId);
1002
+
1003
+ res.json({ ok: true, taskId: nextTask.id });
1004
+ } catch (err: any) {
1005
+ res.status(500).json({ error: err.message });
1006
+ }
1007
+ });
1008
+
1009
+ // 创建并执行工作流
1010
+ app.post('/api/workflow', async (req, res) => {
1011
+ try {
1012
+ const { channelId, title, steps } = req.body;
1013
+ if (!channelId || !steps || !Array.isArray(steps)) {
1014
+ return res.status(400).json({ error: 'channelId and steps required' });
1015
+ }
1016
+
1017
+ const tasks = await loadTaskQueue();
1018
+ const task: Task = {
1019
+ id: `wf_${Date.now()}_${Math.random().toString(36).substring(2, 8)}`,
1020
+ type: 'workflow',
1021
+ title: title || '工作流',
1022
+ description: `包含 ${steps.length} 个步骤的工作流`,
1023
+ status: 'pending',
1024
+ progress: 0,
1025
+ createdAt: new Date().toISOString(),
1026
+ updatedAt: new Date().toISOString(),
1027
+ steps: steps.map((s: string, i: number) => ({
1028
+ id: `step_${i}`,
1029
+ name: s,
1030
+ status: 'pending'
1031
+ })),
1032
+ currentStep: 0
1033
+ };
1034
+
1035
+ tasks.push(task);
1036
+ await saveTaskQueue(tasks);
1037
+
1038
+ // 自动开始执行
1039
+ if (!isExecutingTask) {
1040
+ executeTask(task, channelId);
1041
+ }
1042
+
1043
+ res.json({ ok: true, task });
1044
+ } catch (err: any) {
1045
+ res.status(500).json({ error: err.message });
1046
+ }
1047
+ });
1048
+
1049
+ // ==================== LLM 配置 API ====================
1050
+
1051
+ // 获取所有 LLM 配置
1052
+ app.get('/api/llm-config', async (req, res) => {
1053
+ try {
1054
+ const config = await llmConfigStore.getConfig();
1055
+ const providerInfo = llmConfigStore.getAllProviderInfo();
1056
+
1057
+ // 隐藏 API Key
1058
+ const safeConfig = {
1059
+ ...config,
1060
+ providers: Object.fromEntries(
1061
+ Object.entries(config.providers).map(([key, val]) => [
1062
+ key,
1063
+ { ...val, apiKey: val.apiKey ? '***' + val.apiKey.slice(-4) : '' }
1064
+ ])
1065
+ ),
1066
+ providerInfo
1067
+ };
1068
+
1069
+ res.json(safeConfig);
1070
+ } catch (err: any) {
1071
+ res.status(500).json({ error: err.message });
1072
+ }
1073
+ });
1074
+
1075
+ // 更新 LLM 配置
1076
+ app.post('/api/llm-config', async (req, res) => {
1077
+ try {
1078
+ const { provider, config } = req.body;
1079
+
1080
+ if (!provider || !config) {
1081
+ return res.status(400).json({ error: 'provider and config required' });
1082
+ }
1083
+
1084
+ await llmConfigStore.updateProvider(provider, config);
1085
+
1086
+ // 如果是活跃供应商,重新初始化 Pi SDK
1087
+ const currentActive = await llmConfigStore.getActiveProvider();
1088
+ if (provider === currentActive) {
1089
+ const newConfig = await llmConfigStore.getActiveProviderConfig();
1090
+ if (newConfig) {
1091
+ initMinimax({
1092
+ provider,
1093
+ apiKey: newConfig.apiKey || undefined,
1094
+ baseUrl: newConfig.baseUrl || undefined,
1095
+ model: newConfig.model || undefined
1096
+ });
1097
+ }
1098
+ }
1099
+
1100
+ res.json({ ok: true });
1101
+ } catch (err: any) {
1102
+ res.status(500).json({ error: err.message });
1103
+ }
1104
+ });
1105
+
1106
+ // 设置活跃供应商
1107
+ app.post('/api/llm-provider', async (req, res) => {
1108
+ try {
1109
+ const { provider } = req.body;
1110
+
1111
+ if (!provider) {
1112
+ return res.status(400).json({ error: 'provider required' });
1113
+ }
1114
+
1115
+ await llmConfigStore.setActiveProvider(provider as ModelProvider);
1116
+
1117
+ // 重新初始化 Pi SDK
1118
+ const config = await llmConfigStore.getActiveProviderConfig();
1119
+ if (config) {
1120
+ initMinimax({
1121
+ provider: provider as ModelProvider,
1122
+ apiKey: config.apiKey || undefined,
1123
+ baseUrl: config.baseUrl || undefined,
1124
+ model: config.model || undefined
1125
+ });
1126
+ }
1127
+
1128
+ res.json({ ok: true, provider });
1129
+ } catch (err: any) {
1130
+ res.status(500).json({ error: err.message });
1131
+ }
1132
+ });
1133
+
1134
+ // 测试供应商连接
1135
+ app.post('/api/llm-test', async (req, res) => {
1136
+ try {
1137
+ const { provider } = req.body;
1138
+
1139
+ if (!provider) {
1140
+ return res.status(400).json({ error: 'provider required' });
1141
+ }
1142
+
1143
+ const result = await llmConfigStore.testProvider(provider as ModelProvider);
1144
+ res.json(result);
1145
+ } catch (err: any) {
1146
+ res.status(500).json({ error: err.message });
1147
+ }
1148
+ });
1149
+
1150
+ // ==================== P2P Network API ====================
1151
+
1152
+ // 获取当前身份
1153
+ app.get('/api/identity', async (_req, res) => {
1154
+ console.log('收到 /api/identity 请求');
1155
+ console.log('p2pIdentity.did:', p2pIdentity.did);
1156
+ try {
1157
+ res.json({
1158
+ did: p2pIdentity.did,
1159
+ name: p2pIdentity.name,
1160
+ publicKey: p2pIdentity.publicKey
1161
+ });
1162
+ } catch (err: any) {
1163
+ console.error('API identity 错误:', err);
1164
+ res.status(500).json({ error: err.message });
1165
+ }
1166
+ });
1167
+
1168
+ // 获取已连接的节点
1169
+ app.get('/api/peers', async (_req, res) => {
1170
+ try {
1171
+ if (!p2pCommunicator) {
1172
+ res.json([]);
1173
+ return;
1174
+ }
1175
+ const connections = p2pCommunicator.getConnections();
1176
+ const peers = connections.map((conn: P2PConnection) => ({
1177
+ id: conn.publicKey.substring(0, 16),
1178
+ publicKey: conn.publicKey,
1179
+ peerId: conn.publicKey
1180
+ }));
1181
+ res.json(peers);
1182
+ } catch (err: any) {
1183
+ res.status(500).json({ error: err.message });
1184
+ }
1185
+ });
1186
+
1187
+ // 获取已发现的所有节点(包括通过 CID 解析的)
1188
+ app.get('/api/discovered-peers', async (_req, res) => {
1189
+ try {
1190
+ // 从全局状态获取已发现的节点
1191
+ const discovered = (global as any).discoveredAgents || [];
1192
+ res.json(discovered);
1193
+ } catch (err: any) {
1194
+ res.status(500).json({ error: err.message });
1195
+ }
1196
+ });
1197
+
1198
+ // ==================== iroh P2P API ====================
1199
+
1200
+ // 初始化 iroh P2P(带持久化)
1201
+ app.post('/api/iroh/init', async (_req, res) => {
1202
+ try {
1203
+ if (irohInitialized && irohNodeInfo) {
1204
+ res.json({ ok: true, ...irohNodeInfo });
1205
+ return;
1206
+ }
1207
+
1208
+ console.log('[iroh API] 初始化 iroh...');
1209
+
1210
+ // 启动 iroh(启用持久化)
1211
+ await irohTransport.start(undefined, true);
1212
+ const nodeId = irohTransport.getNodeId() || '';
1213
+
1214
+ console.log(`[iroh API] iroh 节点 ID: ${nodeId.substring(0, 20)}...`);
1215
+
1216
+ // 生成 DID
1217
+ const keyPair = KeyManager.generate();
1218
+ const did = keyPair.did;
1219
+
1220
+ // 构建节点信息文档
1221
+ const nodeDoc = {
1222
+ id: did,
1223
+ name: `bolloon-web-${Date.now()}`,
1224
+ version: '1.0',
1225
+ capabilities: ['chat', 'ai', 'judgment-injection', 'web-interface'],
1226
+ interests: ['ai', 'p2p', 'judgment-system'],
1227
+ irohNodeId: nodeId,
1228
+ channels: [{ id: 'main', name: '主对话' }],
1229
+ createdAt: new Date().toISOString()
1230
+ };
1231
+
1232
+ // 发布到 IPFS
1233
+ const formData = new FormData();
1234
+ const blob = new Blob([JSON.stringify(nodeDoc)], { type: 'application/json' });
1235
+ formData.append('file', blob, 'node-info.json');
1236
+
1237
+ const ipfsRes = await fetch(`${IPFS_ENDPOINT}/api/v0/add`, {
1238
+ method: 'POST',
1239
+ body: formData
1240
+ });
1241
+ const ipfsResult = await ipfsRes.text();
1242
+ const cidMatch = ipfsResult.match(/"Hash":"([^"]+)"/);
1243
+ const cid = cidMatch ? cidMatch[1] : '';
1244
+
1245
+ irohNodeInfo = {
1246
+ did,
1247
+ cid,
1248
+ irohNodeId: nodeId,
1249
+ name: nodeDoc.name,
1250
+ initialized: true
1251
+ };
1252
+ irohInitialized = true;
1253
+
1254
+ // 设置消息处理
1255
+ irohTransport.onMessage('chat', (msg) => {
1256
+ const content = new TextDecoder().decode(msg.payload);
1257
+ console.log(`[iroh] 收到消息 from ${msg.from.substring(0, 12)}...`);
1258
+
1259
+ // 通过 SSE 广播给所有客户端
1260
+ broadcast({
1261
+ type: 'p2p_message',
1262
+ from: msg.from,
1263
+ content,
1264
+ timestamp: Date.now()
1265
+ }, 'p2p-global');
1266
+ });
1267
+
1268
+ irohTransport.onMessage('ai-dialogue', (msg) => {
1269
+ const content = new TextDecoder().decode(msg.payload);
1270
+ console.log(`[iroh] 收到 AI 对话 from ${msg.from.substring(0, 12)}...`);
1271
+
1272
+ broadcast({
1273
+ type: 'p2p_message',
1274
+ content,
1275
+ timestamp: Date.now()
1276
+ }, 'p2p-global');
1277
+ });
1278
+
1279
+ console.log(`[iroh API] 初始化完成: DID=${did}, CID=${cid}`);
1280
+
1281
+ res.json({ ok: true, ...irohNodeInfo });
1282
+ } catch (err: any) {
1283
+ console.error('[iroh API] 初始化失败:', err);
1284
+ res.status(500).json({ error: err.message });
1285
+ }
1286
+ });
1287
+
1288
+ // 获取 iroh 节点信息
1289
+ app.get('/api/iroh/info', async (_req, res) => {
1290
+ if (!irohInitialized || !irohNodeInfo) {
1291
+ res.json({ initialized: false });
1292
+ return;
1293
+ }
1294
+ res.json({
1295
+ initialized: true,
1296
+ did: irohNodeInfo.did,
1297
+ cid: irohNodeInfo.cid,
1298
+ irohNodeId: irohNodeInfo.irohNodeId,
1299
+ name: irohNodeInfo.name
1300
+ });
1301
+ });
1302
+
1303
+ // 通过 CID 连接到其他节点
1304
+ app.post('/api/iroh/connect', async (req, res) => {
1305
+ try {
1306
+ const { cid } = req.body;
1307
+
1308
+ if (!cid) {
1309
+ return res.status(400).json({ error: 'CID required' });
1310
+ }
1311
+
1312
+ if (!irohInitialized) {
1313
+ return res.status(500).json({ error: 'iroh not initialized' });
1314
+ }
1315
+
1316
+ console.log(`[iroh API] 连接到 CID: ${cid}`);
1317
+
1318
+ // 从 IPFS 获取节点信息
1319
+ const ipfsRes = await fetch(`${IPFS_ENDPOINT}/api/v0/cat?arg=${cid}`, {
1320
+ method: 'POST'
1321
+ });
1322
+ const content = await ipfsRes.text();
1323
+ const doc = JSON.parse(content);
1324
+
1325
+ if (!doc.irohNodeId) {
1326
+ return res.status(400).json({ error: '节点信息中不包含 irohNodeId' });
1327
+ }
1328
+
1329
+ const targetNodeId = doc.irohNodeId;
1330
+ console.log(`[iroh API] 目标节点: ${targetNodeId.substring(0, 20)}...`);
1331
+
1332
+ // 发送连接消息
1333
+ const message = JSON.stringify({
1334
+ type: 'hello',
1335
+ from: irohNodeInfo?.irohNodeId,
1336
+ name: irohNodeInfo?.name,
1337
+ timestamp: Date.now()
1338
+ });
1339
+
1340
+ const success = await irohTransport.sendMessage(
1341
+ targetNodeId,
1342
+ 'chat',
1343
+ new TextEncoder().encode(message)
1344
+ );
1345
+
1346
+ if (success) {
1347
+ console.log(`[iroh API] 连接成功!`);
1348
+ res.json({
1349
+ ok: true,
1350
+ targetNodeId,
1351
+ nodeName: doc.name || 'Unknown'
1352
+ });
1353
+ } else {
1354
+ console.log(`[iroh API] 连接失败(对方可能离线)`);
1355
+ res.json({
1356
+ ok: false,
1357
+ error: '连接失败,对方可能离线',
1358
+ targetNodeId
1359
+ });
1360
+ }
1361
+ } catch (err: any) {
1362
+ console.error('[iroh API] 连接错误:', err);
1363
+ res.status(500).json({ error: err.message });
1364
+ }
1365
+ });
1366
+
1367
+ // 发送消息给指定节点
1368
+ app.post('/api/iroh/send', async (req, res) => {
1369
+ try {
1370
+ const { targetNodeId, type, content } = req.body;
1371
+
1372
+ if (!targetNodeId || !content) {
1373
+ return res.status(400).json({ error: 'targetNodeId and content required' });
1374
+ }
1375
+
1376
+ if (!irohInitialized) {
1377
+ return res.status(500).json({ error: 'iroh not initialized' });
1378
+ }
1379
+
1380
+ const messageType = type || 'chat';
1381
+ const success = await irohTransport.sendMessage(
1382
+ targetNodeId,
1383
+ messageType,
1384
+ new TextEncoder().encode(content)
1385
+ );
1386
+
1387
+ res.json({ ok: success });
1388
+ } catch (err: any) {
1389
+ console.error('[iroh API] 发送消息错误:', err);
1390
+ res.status(500).json({ error: err.message });
1391
+ }
1392
+ });
1393
+
1394
+ // 获取已连接的 iroh 节点列表
1395
+ app.get('/api/iroh/peers', async (_req, res) => {
1396
+ try {
1397
+ if (!irohInitialized) {
1398
+ res.json([]);
1399
+ return;
1400
+ }
1401
+
1402
+ const peers = irohTransport.getConnectedPeers();
1403
+ res.json(peers.map((nodeId: string) => ({
1404
+ nodeId,
1405
+ shortId: nodeId.substring(0, 16)
1406
+ })));
1407
+ } catch (err: any) {
1408
+ res.status(500).json({ error: err.message });
1409
+ }
1410
+ });
1411
+
1412
+ // 获取离线消息数量
1413
+ app.get('/api/iroh/offline-count', async (_req, res) => {
1414
+ try {
1415
+ if (!irohInitialized) {
1416
+ res.json({ count: 0 });
1417
+ return;
1418
+ }
1419
+
1420
+ const count = irohTransport.getPendingOfflineCount();
1421
+ res.json({ count });
1422
+ } catch (err: any) {
1423
+ res.json({ count: 0 });
1424
+ }
1425
+ });
1426
+
1427
+ // 获取当前频道的身份信息
1428
+ app.get('/api/channel-identity/:channelId', async (req, res) => {
1429
+ try {
1430
+ const { channelId } = req.params;
1431
+ const channels = await loadChannels();
1432
+ const channel = channels.find(c => c.id === channelId);
1433
+ if (!channel) {
1434
+ return res.status(404).json({ error: 'Channel not found' });
1435
+ }
1436
+ res.json({
1437
+ did: channel.did || '',
1438
+ cid: channel.cid || '',
1439
+ publicKey: channel.publicKey || '',
1440
+ name: channel.name
1441
+ });
1442
+ } catch (err: any) {
1443
+ res.status(500).json({ error: err.message });
1444
+ }
1445
+ });
1446
+
1447
+ // 通过 DID/CID 连接远程智能体
1448
+ app.post('/api/connect', async (req, res) => {
1449
+ try {
1450
+ const { did, cid, ipnsName } = req.body;
1451
+ if (!did && !cid && !ipnsName) {
1452
+ return res.status(400).json({ error: 'DID, CID or IPNS name required' });
1453
+ }
1454
+
1455
+ console.log(`[连接] 尝试连接 DID: ${did}, CID: ${cid}, IPNS: ${ipnsName}`);
1456
+
1457
+ let doc: any = null;
1458
+
1459
+ // 1. 通过 CID 或 IPNS 解析 DiapDoc
1460
+ if (cid || ipnsName) {
1461
+ try {
1462
+ const { IpfsClient } = await import('@diap/sdk');
1463
+ const ipfs = new IpfsClient('http://127.0.0.1:5001', null);
1464
+
1465
+ let resolvedCid = cid;
1466
+ if (ipnsName) {
1467
+ resolvedCid = await ipfs.resolveIpns(ipnsName);
1468
+ }
1469
+
1470
+ if (resolvedCid) {
1471
+ const content = await ipfs.get(resolvedCid);
1472
+ doc = JSON.parse(content);
1473
+ console.log(`[连接] 解析 DiapDoc 成功: ${doc.name}`);
1474
+ }
1475
+ } catch (e) {
1476
+ console.warn(`[连接] 解析 IPFS 内容失败:`, e);
1477
+ }
1478
+ }
1479
+
1480
+ // 2. 如果有 DID,检查是否已连接
1481
+ if (did) {
1482
+ // 广播连接请求
1483
+ if (p2pCommunicator) {
1484
+ const payload = JSON.stringify({
1485
+ type: 'connect_request',
1486
+ requesterDid: did,
1487
+ targetDid: did,
1488
+ timestamp: Date.now()
1489
+ });
1490
+ // 广播到网络
1491
+ console.log(`[连接] 广播连接请求: ${did}`);
1492
+ }
1493
+ }
1494
+
1495
+ // 3. 将解析的文档添加到已发现列表
1496
+ if (doc) {
1497
+ const discovered = (global as any).discoveredAgents || [];
1498
+ const existing = discovered.findIndex((a: any) => a.did === doc.id);
1499
+ if (existing >= 0) {
1500
+ discovered[existing] = { ...discovered[existing], ...doc, lastSeen: Date.now() };
1501
+ } else {
1502
+ discovered.push({
1503
+ did: doc.id || doc.did,
1504
+ name: doc.name,
1505
+ capabilities: doc.capabilities || [],
1506
+ interests: doc.interests || [],
1507
+ channels: doc.channels || [],
1508
+ cid: cid,
1509
+ ipnsName: ipnsName,
1510
+ lastSeen: Date.now()
1511
+ });
1512
+ }
1513
+ (global as any).discoveredAgents = discovered;
1514
+
1515
+ // 广播发现事件到前端
1516
+ broadcast({ type: 'peer_discovered', peer: doc });
1517
+ }
1518
+
1519
+ res.json({
1520
+ ok: true,
1521
+ did: doc?.id || did,
1522
+ name: doc?.name,
1523
+ capabilities: doc?.capabilities || [],
1524
+ channels: doc?.channels || [],
1525
+ message: doc ? 'DiapDoc 解析成功' : '连接请求已发送'
1526
+ });
1527
+ } catch (err: any) {
1528
+ res.status(500).json({ error: err.message });
1529
+ }
1530
+ });
1531
+
1532
+ // 发送 P2P 消息
1533
+ app.post('/api/message-p2p', async (req, res) => {
1534
+ try {
1535
+ const { peerId, did, message } = req.body;
1536
+ if (!message) {
1537
+ return res.status(400).json({ error: 'message required' });
1538
+ }
1539
+
1540
+ let targetPeerId = peerId;
1541
+
1542
+ // 如果没有 peerId,通过 DID 查找
1543
+ if (!targetPeerId && did) {
1544
+ const discovered = (global as any).discoveredAgents || [];
1545
+ const peer = discovered.find((a: any) => a.did === did);
1546
+ if (peer) {
1547
+ targetPeerId = peer.peerId;
1548
+ }
1549
+ }
1550
+
1551
+ if (!targetPeerId) {
1552
+ // 如果没有 P2P 连接,将消息存储到本地队列
1553
+ const messageQueue = (global as any).messageQueue || [];
1554
+ messageQueue.push({
1555
+ did,
1556
+ message,
1557
+ timestamp: Date.now(),
1558
+ status: 'pending'
1559
+ });
1560
+ (global as any).messageQueue = messageQueue;
1561
+ res.json({ ok: true, queued: true, message: '消息已加入队列,等待对方上线' });
1562
+ return;
1563
+ }
1564
+
1565
+ // 通过 P2P 发送消息
1566
+ if (p2pCommunicator) {
1567
+ const payload = JSON.stringify({
1568
+ from: 'bolloon-web',
1569
+ content: message,
1570
+ timestamp: Date.now()
1571
+ });
1572
+
1573
+ // 找到连接并发送
1574
+ const connections = p2pCommunicator.getConnections();
1575
+ for (const conn of connections) {
1576
+ if (conn.publicKey.includes(targetPeerId) || targetPeerId.includes(conn.publicKey.substring(0, 16))) {
1577
+ conn.send(payload);
1578
+ res.json({ ok: true, sent: true });
1579
+ return;
1580
+ }
1581
+ }
1582
+ }
1583
+
1584
+ res.json({ ok: true, sent: false, message: '对方不在线,消息已加入队列' });
1585
+ } catch (err: any) {
1586
+ res.status(500).json({ error: err.message });
1587
+ }
1588
+ });
1589
+
1590
+ // 获取待接收的消息队列
1591
+ app.get('/api/peer-messages', async (_req, res) => {
1592
+ try {
1593
+ const messageQueue = (global as any).messageQueue || [];
1594
+ const pendingMessages = messageQueue.filter((m: any) => m.status === 'pending');
1595
+ res.json(pendingMessages);
1596
+ } catch (err: any) {
1597
+ res.status(500).json({ error: err.message });
1598
+ }
1599
+ });
1600
+
1601
+ // 标记消息已读
1602
+ app.post('/api/peer-messages/:messageId/read', async (req, res) => {
1603
+ try {
1604
+ const { messageId } = req.params;
1605
+ const messageQueue = (global as any).messageQueue || [];
1606
+ const msg = messageQueue.find((m: any) => m.id === messageId);
1607
+ if (msg) {
1608
+ msg.status = 'read';
1609
+ }
1610
+ res.json({ ok: true });
1611
+ } catch (err: any) {
1612
+ res.status(500).json({ error: err.message });
1613
+ }
1614
+ });
1615
+
1616
+ // ==================== P2P 连接进度 SSE ====================
1617
+
1618
+ // 连接进度流(用于实时显示解析进度)
1619
+ const connectProgressClients = new Map<string, any>();
1620
+
1621
+ app.get('/api/p2p/connect/progress', async (req, res) => {
1622
+ const sessionId = crypto.randomUUID();
1623
+
1624
+ res.setHeader('Content-Type', 'text/event-stream');
1625
+ res.setHeader('Cache-Control', 'no-cache');
1626
+ res.setHeader('Connection', 'keep-alive');
1627
+ res.write(`data: ${JSON.stringify({ type: 'start', sessionId })}\n\n`);
1628
+
1629
+ connectProgressClients.set(sessionId, res);
1630
+
1631
+ req.on('close', () => {
1632
+ connectProgressClients.delete(sessionId);
1633
+ });
1634
+ });
1635
+
1636
+ function emitConnectProgress(sessionId: string, data: any) {
1637
+ const client = connectProgressClients.get(sessionId);
1638
+ if (client) {
1639
+ client.write(`data: ${JSON.stringify(data)}\n\n`);
1640
+ }
1641
+ }
1642
+
1643
+ // 取消连接
1644
+ app.post('/api/p2p/connect/cancel', async (req, res) => {
1645
+ const { sessionId } = req.body;
1646
+ if (sessionId && connectProgressClients.has(sessionId)) {
1647
+ connectProgressClients.get(sessionId).end();
1648
+ connectProgressClients.delete(sessionId);
1649
+ }
1650
+ res.json({ ok: true });
1651
+ });
1652
+
1653
+ // ==================== P2P 连接历史 API ====================
1654
+
1655
+ const P2P_HISTORY_PATH = path.join(SHARED_SESSION_PATH, 'p2p-history.json');
1656
+
1657
+ async function loadP2PHistory() {
1658
+ try {
1659
+ const data = await fs.readFile(P2P_HISTORY_PATH, 'utf-8');
1660
+ return JSON.parse(data);
1661
+ } catch {
1662
+ return [];
1663
+ }
1664
+ }
1665
+
1666
+ async function saveP2PHistory(history: any[]) {
1667
+ await fs.writeFile(P2P_HISTORY_PATH, JSON.stringify(history, null, 2));
1668
+ }
1669
+
1670
+ // 获取连接历史
1671
+ app.get('/api/p2p/history', async (_req, res) => {
1672
+ try {
1673
+ const history = await loadP2PHistory();
1674
+ res.json(history);
1675
+ } catch (err: any) {
1676
+ res.status(500).json({ error: err.message });
1677
+ }
1678
+ });
1679
+
1680
+ // 添加到连接历史
1681
+ app.post('/api/p2p/history', async (req, res) => {
1682
+ try {
1683
+ const history = await loadP2PHistory();
1684
+ const entry = req.body;
1685
+
1686
+ // 检查是否已存在
1687
+ const existingIndex = history.findIndex((h: any) => h.did === entry.did);
1688
+ if (existingIndex >= 0) {
1689
+ history[existingIndex] = { ...history[existingIndex], ...entry, lastConnectedAt: Date.now() };
1690
+ } else {
1691
+ history.unshift({ ...entry, id: crypto.randomUUID(), lastConnectedAt: Date.now(), lastMessageAt: 0, totalMessages: 0 });
1692
+ }
1693
+
1694
+ await saveP2PHistory(history);
1695
+ res.json({ ok: true });
1696
+ } catch (err: any) {
1697
+ res.status(500).json({ error: err.message });
1698
+ }
1699
+ });
1700
+
1701
+ // 更新连接历史
1702
+ app.patch('/api/p2p/history/:id', async (req, res) => {
1703
+ try {
1704
+ const history = await loadP2PHistory();
1705
+ const { id } = req.params;
1706
+ const updates = req.body;
1707
+
1708
+ const index = history.findIndex((h: any) => h.id === id);
1709
+ if (index >= 0) {
1710
+ history[index] = { ...history[index], ...updates };
1711
+ await saveP2PHistory(history);
1712
+ }
1713
+
1714
+ res.json({ ok: true });
1715
+ } catch (err: any) {
1716
+ res.status(500).json({ error: err.message });
1717
+ }
1718
+ });
1719
+
1720
+ // 删除连接历史
1721
+ app.delete('/api/p2p/history/:id', async (req, res) => {
1722
+ try {
1723
+ const history = await loadP2PHistory();
1724
+ const { id } = req.params;
1725
+
1726
+ const filtered = history.filter((h: any) => h.id !== id);
1727
+ await saveP2PHistory(filtered);
1728
+
1729
+ res.json({ ok: true });
1730
+ } catch (err: any) {
1731
+ res.status(500).json({ error: err.message });
1732
+ }
1733
+ });
1734
+
1735
+ // ==================== P2P 偏好设置 API ====================
1736
+
1737
+ const P2P_PREFS_PATH = path.join(SHARED_SESSION_PATH, 'p2p-preferences.json');
1738
+
1739
+ async function loadP2PPreferences() {
1740
+ try {
1741
+ const data = await fs.readFile(P2P_PREFS_PATH, 'utf-8');
1742
+ return JSON.parse(data);
1743
+ } catch {
1744
+ return {
1745
+ autoReconnect: true,
1746
+ autoConnectOnStartup: true,
1747
+ preferredNodes: [],
1748
+ maxOfflineQueue: 100,
1749
+ notifications: {
1750
+ newMessage: true,
1751
+ connectionEstablished: true,
1752
+ peerWentOnline: true,
1753
+ peerWentOffline: true
1754
+ }
1755
+ };
1756
+ }
1757
+ }
1758
+
1759
+ async function saveP2PPreferences(prefs: any) {
1760
+ await fs.writeFile(P2P_PREFS_PATH, JSON.stringify(prefs, null, 2));
1761
+ }
1762
+
1763
+ app.get('/api/p2p/preferences', async (_req, res) => {
1764
+ try {
1765
+ const prefs = await loadP2PPreferences();
1766
+ res.json(prefs);
1767
+ } catch (err: any) {
1768
+ res.status(500).json({ error: err.message });
1769
+ }
1770
+ });
1771
+
1772
+ app.patch('/api/p2p/preferences', async (req, res) => {
1773
+ try {
1774
+ const current = await loadP2PPreferences();
1775
+ const updates = req.body;
1776
+ await saveP2PPreferences({ ...current, ...updates });
1777
+ res.json({ ok: true });
1778
+ } catch (err: any) {
1779
+ res.status(500).json({ error: err.message });
1780
+ }
1781
+ });
1782
+
1783
+ return new Promise<{ app: express.Express; server: typeof server }>((resolve) => {
1784
+ server.listen(port, () => {
1785
+ console.log(`Web 服务器启动完成: http://localhost:${port}`);
1786
+ console.log('服务器已监听');
1787
+ setInterval(() => {
1788
+ for (const client of sseClients) {
1789
+ client.res.write(': ping\n\n');
1790
+ }
1791
+ }, 30000);
1792
+ resolve({ app, server });
1793
+ });
1794
+ });
1795
+ }
1796
+
1797
+ function broadcast(data: object, channelId?: string) {
1798
+ const envelope = { ...data, channelId };
1799
+ const message = `data: ${JSON.stringify(envelope)}\n\n`;
1800
+ for (const client of sseClients) {
1801
+ if (!channelId || client.channelId === channelId) {
1802
+ client.res.write(message);
1803
+ }
1804
+ }
1805
+ }
1806
+
1807
+ function getUserName(): string {
1808
+ const home = process.env.HOME || process.env.USERPROFILE || '';
1809
+ const match = home.match(/\/Users\/(\w+)/);
1810
+ if (match) return match[1];
1811
+ const user = process.env.USERNAME || process.env.USER || 'user';
1812
+ return user.toLowerCase().replace(/[^a-z0-9]/g, '');
1813
+ }
1814
+
1815
+ export async function bootstrapIdentity() {
1816
+ console.log('🔐 身份生成...');
1817
+ const kp = KeyManager.generate();
1818
+ const did = kp.did;
1819
+ const username = getUserName();
1820
+ const suffix = did.split(':').pop()?.substring(0, 4);
1821
+ const name = `blln-${username}-${suffix}`;
1822
+ console.log(` DID: ${did.substring(0, 30)}...`);
1823
+ return { keypair: kp, did, name };
1824
+ }
1825
+
1826
+ export function publishDIDBackground(name: string, kp: any) {
1827
+ console.log('📝 IPNS注册(后台)...');
1828
+ let retries = 0;
1829
+
1830
+ const attempt = async () => {
1831
+ try {
1832
+ const auth = await AgentAuthManager.newWithRemoteIpfs('http://127.0.0.1:5001', 'http://127.0.0.1:8080');
1833
+ await auth.registerAgent({ name, services: [] }, kp, '');
1834
+ console.log('✅ IPNS注册成功');
1835
+ } catch (e: any) {
1836
+ retries++;
1837
+ if (retries < 10) {
1838
+ setTimeout(attempt, 60000);
1839
+ }
1840
+ }
1841
+ };
1842
+
1843
+ setTimeout(attempt, 100);
1844
+ }
1845
+
1846
+ export async function bootstrapP2P(verifier: AgentVerificationManager): Promise<HyperswarmCommunicator> {
1847
+ console.log('🌐 P2P连接...');
1848
+ const rawSeed = crypto.getRandomValues(new Uint8Array(32));
1849
+ const comm = createHyperswarmCommunicator({ server: true, client: true, autoConnect: true, maxConnections: 50, seed: rawSeed } as any);
1850
+
1851
+ await comm.start();
1852
+ const topic = createTopic('bolloon-agent-harness') as Buffer;
1853
+ await comm.joinTopic(topic);
1854
+ console.log(' P2P已就绪');
1855
+
1856
+ return comm;
1857
+ }
1858
+
1859
+ export async function openBrowser(url: string) {
1860
+ const { exec } = await import('child_process');
1861
+ const { platform } = await import('os');
1862
+ const p = platform();
1863
+
1864
+ let cmd;
1865
+ if (p === 'darwin') {
1866
+ cmd = `open ${url}`;
1867
+ } else if (p === 'win32') {
1868
+ cmd = `start ${url}`;
1869
+ } else {
1870
+ cmd = `xdg-open ${url}`;
1871
+ }
1872
+
1873
+ exec(cmd, (err) => {
1874
+ if (err) {
1875
+ console.error('打开浏览器失败:', err.message);
1876
+ }
1877
+ });
1878
+ }