@bolloon/bolloon-agent 0.1.36 → 0.1.37

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 (530) hide show
  1. package/dist/web/client.js +2861 -3746
  2. package/dist/web/components/p2p/index.js +226 -264
  3. package/dist/web/ui/message-renderer.js +323 -434
  4. package/dist/web/ui/step-timeline.js +255 -351
  5. package/package.json +2 -2
  6. package/.auto-evolve-calls +0 -1
  7. package/.last-auto-evolve-baseline +0 -1
  8. package/.playwright-mcp/page-2026-06-16T07-51-45-706Z.yml +0 -130
  9. package/.playwright-mcp/page-2026-06-16T07-56-44-116Z.yml +0 -131
  10. package/Bolloon.md +0 -144
  11. package/Dive-into/CITATION.cff +0 -17
  12. package/Dive-into/LICENSE +0 -25
  13. package/Dive-into/README.md +0 -598
  14. package/Dive-into/README_zh.md +0 -599
  15. package/Dive-into/assets/context.png +0 -0
  16. package/Dive-into/assets/extensibility.png +0 -0
  17. package/Dive-into/assets/iteration.png +0 -0
  18. package/Dive-into/assets/layered_architecture.png +0 -0
  19. package/Dive-into/assets/main_structure.png +0 -0
  20. package/Dive-into/assets/permission.png +0 -0
  21. package/Dive-into/assets/session_compact.png +0 -0
  22. package/Dive-into/assets/subagent.png +0 -0
  23. package/Dive-into/paper/Dive_into_Claude_Code.pdf +0 -0
  24. package/dist/agents/constraint-layer.js.map +0 -1
  25. package/dist/agents/pi-sdk.js.map +0 -1
  26. package/dist/agents/workflow-engine.js.map +0 -1
  27. package/dist/bollharness/src/index.js +0 -5
  28. package/dist/bollharness/src/scripts/checks/check_adr_plan_numbering.js +0 -6
  29. package/dist/bollharness/src/scripts/checks/check_api_types.js +0 -45
  30. package/dist/bollharness/src/scripts/checks/check_artifact_link.js +0 -146
  31. package/dist/bollharness/src/scripts/checks/check_bridge_deps.js +0 -6
  32. package/dist/bollharness/src/scripts/checks/check_bugfix_binding.js +0 -6
  33. package/dist/bollharness/src/scripts/checks/check_bugfix_binding_ci.js +0 -6
  34. package/dist/bollharness/src/scripts/checks/check_doc_file_references.js +0 -6
  35. package/dist/bollharness/src/scripts/checks/check_doc_freshness.js +0 -135
  36. package/dist/bollharness/src/scripts/checks/check_doc_links.js +0 -31
  37. package/dist/bollharness/src/scripts/checks/check_file_existence_claims.js +0 -6
  38. package/dist/bollharness/src/scripts/checks/check_fragment_integrity.js +0 -34
  39. package/dist/bollharness/src/scripts/checks/check_hook_installed.js +0 -63
  40. package/dist/bollharness/src/scripts/checks/check_issue_closure.js +0 -41
  41. package/dist/bollharness/src/scripts/checks/check_mcp_parity.js +0 -6
  42. package/dist/bollharness/src/scripts/checks/check_security.js +0 -48
  43. package/dist/bollharness/src/scripts/checks/check_skill_parity.js +0 -6
  44. package/dist/bollharness/src/scripts/checks/check_versions.js +0 -6
  45. package/dist/bollharness/src/scripts/checks/finding.js +0 -13
  46. package/dist/bollharness/src/scripts/checks/next_decision_number.js +0 -20
  47. package/dist/bollharness/src/scripts/checks/regenerate_magic_docs.js +0 -6
  48. package/dist/bollharness/src/scripts/ci/detect_rebaseline_triggers.js +0 -8
  49. package/dist/bollharness/src/scripts/ci/scan_subprocess_cfg.js +0 -8
  50. package/dist/bollharness/src/scripts/ci/scan_verify_artifacts.js +0 -8
  51. package/dist/bollharness/src/scripts/ci/scan_yaml_schema.js +0 -8
  52. package/dist/bollharness/src/scripts/context_router.js +0 -67
  53. package/dist/bollharness/src/scripts/deploy-guard.js +0 -157
  54. package/dist/bollharness/src/scripts/guard-feedback.js +0 -192
  55. package/dist/bollharness/src/scripts/guard_router.js +0 -158
  56. package/dist/bollharness/src/scripts/hooks/_hook_output.js +0 -6
  57. package/dist/bollharness/src/scripts/hooks/auto-python3.js +0 -6
  58. package/dist/bollharness/src/scripts/hooks/deploy-progress-on-session-end.js +0 -6
  59. package/dist/bollharness/src/scripts/hooks/failure-analyzer.js +0 -6
  60. package/dist/bollharness/src/scripts/hooks/gate-judgment-inject.js +0 -92
  61. package/dist/bollharness/src/scripts/hooks/gate-transition-judgment.js +0 -63
  62. package/dist/bollharness/src/scripts/hooks/inbox-ack.js +0 -6
  63. package/dist/bollharness/src/scripts/hooks/inbox-inject-on-start.js +0 -6
  64. package/dist/bollharness/src/scripts/hooks/inbox-validate.js +0 -6
  65. package/dist/bollharness/src/scripts/hooks/inbox-write-ledger.js +0 -6
  66. package/dist/bollharness/src/scripts/hooks/initializer-agent.js +0 -6
  67. package/dist/bollharness/src/scripts/hooks/loop-detection.js +0 -73
  68. package/dist/bollharness/src/scripts/hooks/owner-guard.js +0 -6
  69. package/dist/bollharness/src/scripts/hooks/precompact.js +0 -6
  70. package/dist/bollharness/src/scripts/hooks/review-agent-gatekeeper.js +0 -6
  71. package/dist/bollharness/src/scripts/hooks/risk-tracker.js +0 -108
  72. package/dist/bollharness/src/scripts/hooks/sanitize-on-read.js +0 -6
  73. package/dist/bollharness/src/scripts/hooks/session-reflection.js +0 -7
  74. package/dist/bollharness/src/scripts/hooks/session-start-magic-docs.js +0 -7
  75. package/dist/bollharness/src/scripts/hooks/session-start-reset-risk.js +0 -7
  76. package/dist/bollharness/src/scripts/hooks/session-start-toolkit-reminder.js +0 -7
  77. package/dist/bollharness/src/scripts/hooks/stop-evaluator.js +0 -157
  78. package/dist/bollharness/src/scripts/hooks/tool-call-counter.js +0 -6
  79. package/dist/bollharness/src/scripts/hooks/trace-analyzer.js +0 -10
  80. package/dist/bollharness/src/scripts/install/install-trust-token.js +0 -7
  81. package/dist/bollharness/src/scripts/install/multi_project_registry.js +0 -9
  82. package/dist/bollharness/src/scripts/install/phase2_auto.js +0 -21
  83. package/dist/bollharness/src/scripts/install/pre_commit_installer.js +0 -6
  84. package/dist/bollharness/src/scripts/install/tier_selector.js +0 -7
  85. package/dist/bollharness/src/scripts/install/transcript_miner.js +0 -7
  86. package/dist/bollharness/src/scripts/lib/claim_patterns.js +0 -10
  87. package/dist/bollharness/src/scripts/lib/sanitize_patterns.js +0 -12
  88. package/dist/bollharness/src/scripts/sanitize.js +0 -6
  89. package/dist/bollharness-integration/context-router-judgment.js.map +0 -1
  90. package/dist/bollharness-integration/context-router.js.map +0 -1
  91. package/dist/bollharness-integration/gate-state-machine.js.map +0 -1
  92. package/dist/bollharness-integration/gate-transition-hooks.js.map +0 -1
  93. package/dist/bollharness-integration/guard-checker.js.map +0 -1
  94. package/dist/bollharness-integration/index.js.map +0 -1
  95. package/dist/bollharness-integration/integration.js.map +0 -1
  96. package/dist/bollharness-integration/llm/pi-ai.d.ts +0 -60
  97. package/dist/bollharness-integration/pi-ecosystem-colony/index.d.ts +0 -153
  98. package/dist/bollharness-integration/pi-ecosystem-goals/index.d.ts +0 -136
  99. package/dist/bollharness-integration/pi-ecosystem-judgment/decision.d.ts +0 -117
  100. package/dist/bollharness-integration/pi-ecosystem-judgment/distillation.d.ts +0 -78
  101. package/dist/bollharness-integration/pi-ecosystem-judgment/index.d.ts +0 -139
  102. package/dist/bollharness-integration/pi-ecosystem-mcp/index.d.ts +0 -127
  103. package/dist/bollharness-integration/pi-ecosystem-subagents/index.d.ts +0 -95
  104. package/dist/bollharness-integration/skill-adapter.js.map +0 -1
  105. package/dist/constraint-runtime/src/_archive_helper.js +0 -9
  106. package/dist/constraint-runtime/src/agent/coordinator.js +0 -48
  107. package/dist/constraint-runtime/src/agent/index.js +0 -1
  108. package/dist/constraint-runtime/src/assistant/index.js +0 -12
  109. package/dist/constraint-runtime/src/bootstrap/index.js +0 -12
  110. package/dist/constraint-runtime/src/bootstrap_graph.js +0 -13
  111. package/dist/constraint-runtime/src/bridge/index.js +0 -12
  112. package/dist/constraint-runtime/src/buddy/index.js +0 -12
  113. package/dist/constraint-runtime/src/cli/index.js +0 -12
  114. package/dist/constraint-runtime/src/command_graph.js +0 -10
  115. package/dist/constraint-runtime/src/commands.js +0 -60
  116. package/dist/constraint-runtime/src/components/index.js +0 -12
  117. package/dist/constraint-runtime/src/constants/index.js +0 -12
  118. package/dist/constraint-runtime/src/constraint/budget.js +0 -22
  119. package/dist/constraint-runtime/src/constraint/budget.js.map +0 -1
  120. package/dist/constraint-runtime/src/constraint/index.js +0 -2
  121. package/dist/constraint-runtime/src/constraint/permission.js +0 -20
  122. package/dist/constraint-runtime/src/constraint/permission.js.map +0 -1
  123. package/dist/constraint-runtime/src/context.js +0 -30
  124. package/dist/constraint-runtime/src/coordinator/index.js +0 -12
  125. package/dist/constraint-runtime/src/cost_hook.js +0 -4
  126. package/dist/constraint-runtime/src/cost_tracker.js +0 -8
  127. package/dist/constraint-runtime/src/deferred_init.js +0 -10
  128. package/dist/constraint-runtime/src/direct_modes.js +0 -6
  129. package/dist/constraint-runtime/src/dynamic-tool-loader.js +0 -85
  130. package/dist/constraint-runtime/src/entrypoints/index.js +0 -12
  131. package/dist/constraint-runtime/src/execution_registry.js +0 -44
  132. package/dist/constraint-runtime/src/history.js +0 -9
  133. package/dist/constraint-runtime/src/hooks/index.js +0 -12
  134. package/dist/constraint-runtime/src/index.js +0 -26
  135. package/dist/constraint-runtime/src/ink.js +0 -4
  136. package/dist/constraint-runtime/src/keybindings/index.js +0 -12
  137. package/dist/constraint-runtime/src/memdir/index.js +0 -12
  138. package/dist/constraint-runtime/src/migrations/index.js +0 -12
  139. package/dist/constraint-runtime/src/models.js +0 -1
  140. package/dist/constraint-runtime/src/models.js.map +0 -1
  141. package/dist/constraint-runtime/src/moreright/index.js +0 -12
  142. package/dist/constraint-runtime/src/native_ts/index.js +0 -12
  143. package/dist/constraint-runtime/src/output_styles/index.js +0 -12
  144. package/dist/constraint-runtime/src/parity_audit.js +0 -12
  145. package/dist/constraint-runtime/src/plugins/index.js +0 -12
  146. package/dist/constraint-runtime/src/port_manifest.js +0 -11
  147. package/dist/constraint-runtime/src/prefetch.js +0 -9
  148. package/dist/constraint-runtime/src/query.js +0 -1
  149. package/dist/constraint-runtime/src/remote/index.js +0 -12
  150. package/dist/constraint-runtime/src/remote_runtime.js +0 -9
  151. package/dist/constraint-runtime/src/runtime/index.js +0 -1
  152. package/dist/constraint-runtime/src/runtime/session.js +0 -35
  153. package/dist/constraint-runtime/src/schemas/index.js +0 -12
  154. package/dist/constraint-runtime/src/screens/index.js +0 -12
  155. package/dist/constraint-runtime/src/server/index.js +0 -12
  156. package/dist/constraint-runtime/src/services/index.js +0 -12
  157. package/dist/constraint-runtime/src/session_store.js +0 -22
  158. package/dist/constraint-runtime/src/setup.js +0 -30
  159. package/dist/constraint-runtime/src/skills/index.js +0 -1
  160. package/dist/constraint-runtime/src/skills/skill-registry.js +0 -28
  161. package/dist/constraint-runtime/src/state/index.js +0 -12
  162. package/dist/constraint-runtime/src/system_init.js +0 -20
  163. package/dist/constraint-runtime/src/thinking/engine.js +0 -42
  164. package/dist/constraint-runtime/src/thinking/index.js +0 -1
  165. package/dist/constraint-runtime/src/tool_pool.js +0 -8
  166. package/dist/constraint-runtime/src/tools/OpenCLI/execAdapter.js +0 -7
  167. package/dist/constraint-runtime/src/tools/OpenCLI/listAdapters.js +0 -7
  168. package/dist/constraint-runtime/src/tools/OpenCLI/runCommand.js +0 -7
  169. package/dist/constraint-runtime/src/tools/PolymarketSDK/cancelOrder.js +0 -6
  170. package/dist/constraint-runtime/src/tools/PolymarketSDK/createOrder.js +0 -6
  171. package/dist/constraint-runtime/src/tools/PolymarketSDK/getMarket.js +0 -5
  172. package/dist/constraint-runtime/src/tools/PolymarketSDK/getOrders.js +0 -6
  173. package/dist/constraint-runtime/src/tools/PolymarketSDK/listMarkets.js +0 -4
  174. package/dist/constraint-runtime/src/tools/SafeSDK/confirmTransaction.js +0 -6
  175. package/dist/constraint-runtime/src/tools/SafeSDK/createTransaction.js +0 -8
  176. package/dist/constraint-runtime/src/tools/SafeSDK/deploySafe.js +0 -6
  177. package/dist/constraint-runtime/src/tools/SafeSDK/executeTransaction.js +0 -6
  178. package/dist/constraint-runtime/src/tools/SafeSDK/getBalance.js +0 -6
  179. package/dist/constraint-runtime/src/tools/SafeSDK/getPendingTransactions.js +0 -6
  180. package/dist/constraint-runtime/src/tools/SafeSDK/proposeTransaction.js +0 -6
  181. package/dist/constraint-runtime/src/tools/WalletTools/autoPay.js +0 -29
  182. package/dist/constraint-runtime/src/tools/WalletTools/createWallet.js +0 -10
  183. package/dist/constraint-runtime/src/tools/WalletTools/getBalance.js +0 -13
  184. package/dist/constraint-runtime/src/tools/WalletTools/importWallet.js +0 -22
  185. package/dist/constraint-runtime/src/tools/WalletTools/sendTransaction.js +0 -25
  186. package/dist/constraint-runtime/src/tools/WalletTools/signMessage.js +0 -10
  187. package/dist/constraint-runtime/src/tools/WalletTools/transferToken.js +0 -25
  188. package/dist/constraint-runtime/src/tools.js +0 -80
  189. package/dist/constraint-runtime/src/transcript.js +0 -19
  190. package/dist/constraint-runtime/src/types/index.js +0 -12
  191. package/dist/constraint-runtime/src/upstream_proxy/index.js +0 -12
  192. package/dist/constraint-runtime/src/utils/index.js +0 -12
  193. package/dist/constraint-runtime/src/vim/index.js +0 -12
  194. package/dist/constraint-runtime/src/voice/index.js +0 -12
  195. package/dist/constraints/index.js.map +0 -1
  196. package/dist/documents/reader.js.map +0 -1
  197. package/dist/llm/config-store.js.map +0 -1
  198. package/dist/llm/pi-ai.js.map +0 -1
  199. package/dist/network/agent-network.js.map +0 -1
  200. package/dist/network/iroh-integration.js.map +0 -1
  201. package/dist/network/iroh-transport.js.map +0 -1
  202. package/dist/network/p2p.js.map +0 -1
  203. package/dist/network/storage/adapters/json-adapter.js.map +0 -1
  204. package/dist/pi-ecosystem-colony/index.js.map +0 -1
  205. package/dist/pi-ecosystem-goals/index.js.map +0 -1
  206. package/dist/pi-ecosystem-judgment/decision.js.map +0 -1
  207. package/dist/pi-ecosystem-judgment/distillation.js.map +0 -1
  208. package/dist/pi-ecosystem-judgment/human-value-store.js.map +0 -1
  209. package/dist/pi-ecosystem-judgment/index.js.map +0 -1
  210. package/dist/pi-ecosystem-judgment/value-injection.js.map +0 -1
  211. package/dist/pi-ecosystem-mcp/index.js.map +0 -1
  212. package/dist/pi-ecosystem-subagents/index.js.map +0 -1
  213. package/dist/social/ant-colony/AdaptiveHeartbeat.js.map +0 -1
  214. package/dist/social/ant-colony/PheromoneEngine.js.map +0 -1
  215. package/dist/social/ant-colony/types.js.map +0 -1
  216. package/dist/social/channels/ChannelManager.js.map +0 -1
  217. package/dist/social/channels/DiapChannelBridge.js.map +0 -1
  218. package/dist/social/channels/InterestMatcher.js.map +0 -1
  219. package/dist/social/channels/types.js.map +0 -1
  220. package/dist/social/global-shared-context.js.map +0 -1
  221. package/dist/social/heartbeat.js.map +0 -1
  222. package/dist/web/components/p2p/P2PModal.js +0 -188
  223. package/dist/web/components/p2p/p2p-modal.js +0 -657
  224. package/dist/web/components/p2p/p2p-tools.js +0 -248
  225. package/lefthook.yml +0 -33
  226. package/scripts/auto-evolve-loop.ts +0 -481
  227. package/scripts/auto-evolve-oneshot.sh +0 -155
  228. package/scripts/auto-evolve-snapshot.sh +0 -136
  229. package/scripts/build-cli.js +0 -216
  230. package/scripts/build-web.ts +0 -116
  231. package/scripts/detect-schema-changes.sh +0 -48
  232. package/scripts/diff-reviewer.ts +0 -159
  233. package/scripts/postinstall.js +0 -153
  234. package/scripts/validate-system-prompt.ts +0 -142
  235. package/scripts/weekly-report.ts +0 -364
  236. package/src/agents/agent-manifest-protocol.ts +0 -117
  237. package/src/agents/constraint-layer.ts +0 -309
  238. package/src/agents/iroh-secret.ts +0 -32
  239. package/src/agents/p2p-chat-tools.ts +0 -383
  240. package/src/agents/p2p-document-tools.ts +0 -347
  241. package/src/agents/permission-mode.ts +0 -127
  242. package/src/agents/pi-sdk.ts +0 -2895
  243. package/src/agents/pre-tool-validator.ts +0 -213
  244. package/src/agents/protocol.ts +0 -398
  245. package/src/agents/shell-guard.ts +0 -417
  246. package/src/agents/shell-tool.ts +0 -103
  247. package/src/agents/skill-loader.ts +0 -202
  248. package/src/agents/subagent-manager.ts +0 -553
  249. package/src/agents/workflow-engine.ts +0 -332
  250. package/src/agents/workflow-pivot-loop.ts +0 -766
  251. package/src/bollharness/.sanitize-report.json +0 -13
  252. package/src/bollharness/CLAUDE.md +0 -73
  253. package/src/bollharness/LICENSE +0 -21
  254. package/src/bollharness/README.md +0 -143
  255. package/src/bollharness/README.zh-CN.md +0 -131
  256. package/src/bollharness/package.json +0 -20
  257. package/src/bollharness/reference/SOURCE-COMMIT.txt +0 -3
  258. package/src/bollharness/reference/boll-reference/scripts/hooks/.sanitize-report.json +0 -12
  259. package/src/bollharness/reference/boll-reference/scripts/hooks/find-boll-root.sh +0 -27
  260. package/src/bollharness/reference/boll-reference/scripts/hooks/precompact.sh +0 -57
  261. package/src/bollharness/reference/boll-reference/scripts/hooks/stop-evaluator.md +0 -57
  262. package/src/bollharness/schemas/metrics-jsonl-allowlist.json +0 -67
  263. package/src/bollharness/scripts/checks/next_decision_number.sh +0 -48
  264. package/src/bollharness/scripts/ci/count-components.sh +0 -65
  265. package/src/bollharness/scripts/context-fragments/artifact-linkage.md +0 -14
  266. package/src/bollharness/scripts/context-fragments/auth-consumers.md +0 -17
  267. package/src/bollharness/scripts/context-fragments/bridge-constitution.md +0 -13
  268. package/src/bollharness/scripts/context-fragments/catalyst-distributed.md +0 -18
  269. package/src/bollharness/scripts/context-fragments/closure-checklist.md +0 -13
  270. package/src/bollharness/scripts/context-fragments/contract-consumers.md +0 -15
  271. package/src/bollharness/scripts/context-fragments/db-shared-structures.md +0 -15
  272. package/src/bollharness/scripts/context-fragments/fixed-three-layers.md +0 -19
  273. package/src/bollharness/scripts/context-fragments/general-dev-principles.md +0 -11
  274. package/src/bollharness/scripts/context-fragments/issue-first.md +0 -8
  275. package/src/bollharness/scripts/context-fragments/mcp-parity.md +0 -16
  276. package/src/bollharness/scripts/context-fragments/pi-agent-operations.md +0 -108
  277. package/src/bollharness/scripts/context-fragments/protocol-consumers.md +0 -15
  278. package/src/bollharness/scripts/context-fragments/run-events-consumers.md +0 -15
  279. package/src/bollharness/scripts/context-fragments/scene-fidelity.md +0 -13
  280. package/src/bollharness/scripts/context-fragments/truth-source-hierarchy.md +0 -15
  281. package/src/bollharness/scripts/context-fragments/two-language.md +0 -15
  282. package/src/bollharness/scripts/context-fragments/version-sources.md +0 -14
  283. package/src/bollharness/scripts/hooks/find-project-root.sh +0 -47
  284. package/src/bollharness/scripts/hooks/inbox-poll.sh +0 -78
  285. package/src/bollharness/scripts/hooks/precompact.sh +0 -56
  286. package/src/bollharness/scripts/hooks/stop-evaluator.md +0 -83
  287. package/src/bollharness/scripts/sync-from-upstream.sh +0 -281
  288. package/src/bollharness/src/index.ts +0 -5
  289. package/src/bollharness/src/scripts/checks/check_adr_plan_numbering.ts +0 -11
  290. package/src/bollharness/src/scripts/checks/check_api_types.ts +0 -52
  291. package/src/bollharness/src/scripts/checks/check_artifact_link.ts +0 -156
  292. package/src/bollharness/src/scripts/checks/check_bridge_deps.ts +0 -11
  293. package/src/bollharness/src/scripts/checks/check_bugfix_binding.ts +0 -11
  294. package/src/bollharness/src/scripts/checks/check_bugfix_binding_ci.ts +0 -11
  295. package/src/bollharness/src/scripts/checks/check_doc_file_references.ts +0 -11
  296. package/src/bollharness/src/scripts/checks/check_doc_freshness.ts +0 -141
  297. package/src/bollharness/src/scripts/checks/check_doc_links.ts +0 -36
  298. package/src/bollharness/src/scripts/checks/check_file_existence_claims.ts +0 -11
  299. package/src/bollharness/src/scripts/checks/check_fragment_integrity.ts +0 -40
  300. package/src/bollharness/src/scripts/checks/check_hook_installed.ts +0 -70
  301. package/src/bollharness/src/scripts/checks/check_issue_closure.ts +0 -51
  302. package/src/bollharness/src/scripts/checks/check_mcp_parity.ts +0 -11
  303. package/src/bollharness/src/scripts/checks/check_security.ts +0 -54
  304. package/src/bollharness/src/scripts/checks/check_skill_parity.ts +0 -11
  305. package/src/bollharness/src/scripts/checks/check_versions.ts +0 -11
  306. package/src/bollharness/src/scripts/checks/finding.ts +0 -35
  307. package/src/bollharness/src/scripts/checks/next_decision_number.ts +0 -24
  308. package/src/bollharness/src/scripts/checks/regenerate_magic_docs.ts +0 -11
  309. package/src/bollharness/src/scripts/ci/detect_rebaseline_triggers.ts +0 -14
  310. package/src/bollharness/src/scripts/ci/scan_subprocess_cfg.ts +0 -14
  311. package/src/bollharness/src/scripts/ci/scan_verify_artifacts.ts +0 -14
  312. package/src/bollharness/src/scripts/ci/scan_yaml_schema.ts +0 -14
  313. package/src/bollharness/src/scripts/context_router.ts +0 -76
  314. package/src/bollharness/src/scripts/deploy-guard.ts +0 -182
  315. package/src/bollharness/src/scripts/guard-feedback.ts +0 -215
  316. package/src/bollharness/src/scripts/guard_router.ts +0 -194
  317. package/src/bollharness/src/scripts/hooks/_hook_output.js +0 -3
  318. package/src/bollharness/src/scripts/hooks/_hook_output.ts +0 -11
  319. package/src/bollharness/src/scripts/hooks/auto-python3.ts +0 -10
  320. package/src/bollharness/src/scripts/hooks/deploy-progress-on-session-end.ts +0 -10
  321. package/src/bollharness/src/scripts/hooks/failure-analyzer.ts +0 -10
  322. package/src/bollharness/src/scripts/hooks/gate-judgment-inject.ts +0 -111
  323. package/src/bollharness/src/scripts/hooks/gate-transition-judgment.ts +0 -74
  324. package/src/bollharness/src/scripts/hooks/inbox-ack.ts +0 -10
  325. package/src/bollharness/src/scripts/hooks/inbox-inject-on-start.ts +0 -10
  326. package/src/bollharness/src/scripts/hooks/inbox-validate.ts +0 -10
  327. package/src/bollharness/src/scripts/hooks/inbox-write-ledger.ts +0 -10
  328. package/src/bollharness/src/scripts/hooks/initializer-agent.ts +0 -10
  329. package/src/bollharness/src/scripts/hooks/loop-detection.ts +0 -83
  330. package/src/bollharness/src/scripts/hooks/owner-guard.ts +0 -10
  331. package/src/bollharness/src/scripts/hooks/precompact.ts +0 -10
  332. package/src/bollharness/src/scripts/hooks/review-agent-gatekeeper.ts +0 -10
  333. package/src/bollharness/src/scripts/hooks/risk-tracker.ts +0 -121
  334. package/src/bollharness/src/scripts/hooks/sanitize-on-read.ts +0 -10
  335. package/src/bollharness/src/scripts/hooks/session-reflection.ts +0 -12
  336. package/src/bollharness/src/scripts/hooks/session-start-magic-docs.ts +0 -12
  337. package/src/bollharness/src/scripts/hooks/session-start-reset-risk.ts +0 -12
  338. package/src/bollharness/src/scripts/hooks/session-start-toolkit-reminder.ts +0 -12
  339. package/src/bollharness/src/scripts/hooks/stop-evaluator.ts +0 -164
  340. package/src/bollharness/src/scripts/hooks/tool-call-counter.ts +0 -10
  341. package/src/bollharness/src/scripts/hooks/trace-analyzer.ts +0 -14
  342. package/src/bollharness/src/scripts/install/install-trust-token.ts +0 -13
  343. package/src/bollharness/src/scripts/install/multi_project_registry.ts +0 -13
  344. package/src/bollharness/src/scripts/install/phase2_auto.ts +0 -28
  345. package/src/bollharness/src/scripts/install/pre_commit_installer.ts +0 -10
  346. package/src/bollharness/src/scripts/install/tier_selector.ts +0 -10
  347. package/src/bollharness/src/scripts/install/transcript_miner.ts +0 -13
  348. package/src/bollharness/src/scripts/lib/claim_patterns.ts +0 -11
  349. package/src/bollharness/src/scripts/lib/sanitize_patterns.ts +0 -13
  350. package/src/bollharness/src/scripts/sanitize.ts +0 -9
  351. package/src/bollharness/templates/persona/default.json +0 -19
  352. package/src/bollharness/templates/scaffold/.gitignore.append +0 -16
  353. package/src/bollharness/templates/scaffold/CLAUDE.md +0 -89
  354. package/src/bollharness-integration/channel-judgment-engine.ts +0 -634
  355. package/src/bollharness-integration/context-chain-router.ts +0 -474
  356. package/src/bollharness-integration/context-router-judgment.ts +0 -339
  357. package/src/bollharness-integration/context-router.ts +0 -583
  358. package/src/bollharness-integration/gate-state-machine.ts +0 -444
  359. package/src/bollharness-integration/gate-transition-hooks.ts +0 -137
  360. package/src/bollharness-integration/guard-checker.ts +0 -451
  361. package/src/bollharness-integration/index.ts +0 -195
  362. package/src/bollharness-integration/integration.ts +0 -538
  363. package/src/bollharness-integration/judgment-prompts.yaml +0 -535
  364. package/src/bollharness-integration/llm-judgment-engine.ts +0 -712
  365. package/src/bollharness-integration/skill-adapter.ts +0 -646
  366. package/src/bootstrap/bootstrap.ts +0 -135
  367. package/src/bootstrap/context-collector.ts +0 -371
  368. package/src/bootstrap/context-hierarchy.ts +0 -283
  369. package/src/bootstrap/lifecycle-hooks.ts +0 -289
  370. package/src/bootstrap/project-context.ts +0 -171
  371. package/src/cli/interface.ts +0 -211
  372. package/src/cli-entry.ts +0 -304
  373. package/src/constraints/index.ts +0 -6
  374. package/src/context-compaction/auto-compact.ts +0 -153
  375. package/src/context-compaction/budget-gate.ts +0 -32
  376. package/src/context-compaction/budget-reduce.ts +0 -37
  377. package/src/context-compaction/context-collapse.ts +0 -72
  378. package/src/context-compaction/index.ts +0 -24
  379. package/src/context-compaction/microcompact.ts +0 -54
  380. package/src/context-compaction/pipeline.ts +0 -133
  381. package/src/context-compaction/snip.ts +0 -51
  382. package/src/context-compaction/token-estimator.ts +0 -36
  383. package/src/context-compaction/types.ts +0 -99
  384. package/src/documents/reader.ts +0 -85
  385. package/src/documents/store.ts +0 -252
  386. package/src/electron-preload.ts +0 -25
  387. package/src/electron.ts +0 -200
  388. package/src/heartbeat/DaemonManager.ts +0 -283
  389. package/src/heartbeat/HealthMonitor.ts +0 -317
  390. package/src/heartbeat/StartupVerifier.ts +0 -223
  391. package/src/heartbeat/Watchdog.ts +0 -208
  392. package/src/heartbeat/index.ts +0 -109
  393. package/src/heartbeat/self-improve-bus.ts +0 -110
  394. package/src/heartbeat/types.ts +0 -82
  395. package/src/index.ts +0 -1724
  396. package/src/llm/audio-config-store.ts +0 -246
  397. package/src/llm/config-store.ts +0 -400
  398. package/src/llm/llm-judgment-client.ts +0 -471
  399. package/src/llm/pi-ai.ts +0 -653
  400. package/src/llm/system-prompt/health.ts +0 -159
  401. package/src/llm/system-prompt/layers/channel/local.md +0 -14
  402. package/src/llm/system-prompt/layers/channel/p2p-agent.md +0 -18
  403. package/src/llm/system-prompt/layers/channel/p2p-visitor.md +0 -19
  404. package/src/llm/system-prompt/layers/core/artifacts_storage.md +0 -89
  405. package/src/llm/system-prompt/layers/core/evenhandedness.md +0 -21
  406. package/src/llm/system-prompt/layers/core/hibs_reminders.md +0 -15
  407. package/src/llm/system-prompt/layers/core/identity.md +0 -37
  408. package/src/llm/system-prompt/layers/core/knowledge.md +0 -17
  409. package/src/llm/system-prompt/layers/core/memory_system.md +0 -12
  410. package/src/llm/system-prompt/layers/core/network_filesystem.md +0 -28
  411. package/src/llm/system-prompt/layers/core/refusal.md +0 -37
  412. package/src/llm/system-prompt/layers/core/tone.md +0 -31
  413. package/src/llm/system-prompt/layers/core/tools.thin.md +0 -13
  414. package/src/llm/system-prompt/layers/core/wellbeing.md +0 -41
  415. package/src/llm/system-prompt/layers/role/architect.md +0 -20
  416. package/src/llm/system-prompt/layers/role/expert.md +0 -19
  417. package/src/llm/system-prompt/layers/role/implementer.md +0 -15
  418. package/src/llm/system-prompt/layers/role/security.md +0 -15
  419. package/src/llm/system-prompt/layers/tool/artifacts.md +0 -72
  420. package/src/llm/system-prompt/layers/tool/bash.md +0 -25
  421. package/src/llm/system-prompt/layers/tool/hibs_api.md +0 -171
  422. package/src/llm/system-prompt/layers/tool/image_search.md +0 -70
  423. package/src/llm/system-prompt/layers/tool/manifest.md +0 -89
  424. package/src/llm/system-prompt/layers/tool/mcp_apps.md +0 -53
  425. package/src/llm/system-prompt/layers/tool/web_search.md +0 -83
  426. package/src/llm/system-prompt/registry.ts +0 -325
  427. package/src/llm/system-prompt/strip-hibsml.ts +0 -52
  428. package/src/llm/tool-manifest/ask_user_input.ts +0 -37
  429. package/src/llm/tool-manifest/bash.ts +0 -25
  430. package/src/llm/tool-manifest/create_file.ts +0 -26
  431. package/src/llm/tool-manifest/fetch_sports_data.ts +0 -28
  432. package/src/llm/tool-manifest/image_search.ts +0 -26
  433. package/src/llm/tool-manifest/index.ts +0 -88
  434. package/src/llm/tool-manifest/mcp.ts +0 -46
  435. package/src/llm/tool-manifest/message_compose.ts +0 -33
  436. package/src/llm/tool-manifest/places.ts +0 -86
  437. package/src/llm/tool-manifest/present_files.ts +0 -23
  438. package/src/llm/tool-manifest/recipe.ts +0 -42
  439. package/src/llm/tool-manifest/recommend_apps.ts +0 -25
  440. package/src/llm/tool-manifest/str_replace.ts +0 -29
  441. package/src/llm/tool-manifest/types.ts +0 -52
  442. package/src/llm/tool-manifest/view.ts +0 -26
  443. package/src/llm/tool-manifest/weather.ts +0 -29
  444. package/src/llm/tool-manifest/web.ts +0 -54
  445. package/src/llm/video-config-store.ts +0 -257
  446. package/src/network/agent-network.ts +0 -800
  447. package/src/network/hybrid-messenger.ts +0 -199
  448. package/src/network/iroh-bootstrap.ts +0 -57
  449. package/src/network/iroh-discovery.ts +0 -208
  450. package/src/network/iroh-integration.ts +0 -158
  451. package/src/network/iroh-transport.ts +0 -637
  452. package/src/network/known-peers.ts +0 -102
  453. package/src/network/p2p-direct.ts +0 -262
  454. package/src/network/p2p-secret.ts +0 -153
  455. package/src/network/p2p.ts +0 -964
  456. package/src/network/source-intent-broadcaster.ts +0 -242
  457. package/src/network/source-intent.ts +0 -167
  458. package/src/network/storage/adapters/json-adapter.ts +0 -454
  459. package/src/network/storage/index.ts +0 -203
  460. package/src/network/storage/types.ts +0 -166
  461. package/src/security/builtin-guards.ts +0 -162
  462. package/src/security/context-router-tool.ts +0 -122
  463. package/src/security/input-scanner.ts +0 -287
  464. package/src/security/react-harness.ts +0 -177
  465. package/src/security/tool-gate.ts +0 -294
  466. package/src/social/ant-colony/index.js +0 -19
  467. package/src/social/channels/ChannelManager.ts +0 -485
  468. package/src/social/channels/DiapChannelBridge.ts +0 -501
  469. package/src/social/channels/InterestMatcher.ts +0 -189
  470. package/src/social/channels/agent-workflow-config.json +0 -214
  471. package/src/social/channels/agent-workflow-config.yaml +0 -334
  472. package/src/social/channels/channel-agent-session.ts +0 -407
  473. package/src/social/channels/channel-heartbeat-agent.ts +0 -622
  474. package/src/social/channels/diap-doc-parser.ts +0 -282
  475. package/src/social/channels/harness-workflow-integrator.ts +0 -594
  476. package/src/social/channels/index.ts +0 -22
  477. package/src/social/channels/types.ts +0 -115
  478. package/src/social/global-shared-context.ts +0 -506
  479. package/src/social/heartbeat.ts +0 -1057
  480. package/src/social/persona/enhanced-persona.ts +0 -359
  481. package/src/types.d.ts +0 -12
  482. package/src/utils/auto-evolve-policy.ts +0 -138
  483. package/src/utils/auto-update.ts +0 -482
  484. package/src/utils/clamp.ts +0 -5
  485. package/src/web/agent-delegate-server.ts +0 -148
  486. package/src/web/api-config.html +0 -520
  487. package/src/web/client.js +0 -2352
  488. package/src/web/client.ts +0 -4326
  489. package/src/web/components/p2p/P2PModal.tsx +0 -416
  490. package/src/web/components/p2p/index.ts +0 -566
  491. package/src/web/components/p2p/index.tsx +0 -297
  492. package/src/web/components/p2p/p2p-connection.ts +0 -338
  493. package/src/web/components/p2p/p2p-identity.ts +0 -92
  494. package/src/web/components/p2p/p2p-manager.ts +0 -147
  495. package/src/web/components/p2p/p2p-messages.ts +0 -219
  496. package/src/web/components/p2p/p2p-modal.ts +0 -685
  497. package/src/web/components/p2p/p2p-store-memory.ts +0 -162
  498. package/src/web/components/p2p/p2p-tools.ts +0 -315
  499. package/src/web/components/p2p/types.ts +0 -170
  500. package/src/web/components/wallet-viem.mjs +0 -118
  501. package/src/web/design.md +0 -99
  502. package/src/web/icons/apple-touch-icon.png +0 -0
  503. package/src/web/icons/favicon-16x16.png +0 -0
  504. package/src/web/icons/favicon-192x192.png +0 -0
  505. package/src/web/icons/favicon-32x32.png +0 -0
  506. package/src/web/icons/favicon-48x48.png +0 -0
  507. package/src/web/icons/favicon-512x512.png +0 -0
  508. package/src/web/icons/favicon.icns +0 -0
  509. package/src/web/icons/favicon.ico +0 -0
  510. package/src/web/icons/icon.png +0 -0
  511. package/src/web/icons/image.png +0 -0
  512. package/src/web/index.html +0 -373
  513. package/src/web/iroh-delegate-transport.ts +0 -139
  514. package/src/web/manifest.json +0 -21
  515. package/src/web/server.ts +0 -5731
  516. package/src/web/style.css +0 -4737
  517. package/src/web/ui/message-renderer.ts +0 -540
  518. package/src/web/ui/step-timeline.ts +0 -394
  519. package/staging/auto-evolve/clean-001/.review-verdict +0 -9
  520. package/staging/auto-evolve/clean-001/clean-001.patch +0 -14
  521. package/staging/auto-evolve/e2e-001/.patch-id +0 -1
  522. package/staging/auto-evolve/e2e-001/.review-verdict +0 -12
  523. package/staging/auto-evolve/e2e-001/e2e-001.patch +0 -11
  524. package/staging/auto-evolve/test-bad/.review-verdict +0 -12
  525. package/staging/auto-evolve/test-bad/test-bad.patch +0 -11
  526. package/test-results/.last-run.json +0 -6
  527. package/test-results/src-test-web-loop-status-b-5734c-atus-bar-/346/230/276/347/244/272-spinner-/344/270/215/346/230/276/347/244/272/345/276/252/347/216/257/350/256/241/346/225/260/error-context.md +0 -285
  528. package/tsconfig.cli.json +0 -16
  529. package/tsconfig.electron.json +0 -20
  530. package/tsconfig.json +0 -18
@@ -1,2895 +0,0 @@
1
- /**
2
- * Pi-SDK - Agent Session for Document Processing
3
- * Part of OpenClaw dual-layer architecture
4
- */
5
-
6
- import * as fs from 'fs/promises';
7
- import * as fsSync from 'fs';
8
- import * as os from 'os';
9
- import * as path from 'path';
10
- import { documentReader, DocumentContent } from '../documents/reader.js';
11
- import { getMinimax } from '../constraints/index.js';
12
- import { p2pNetwork } from '../network/p2p.js';
13
- import { ConstraintLayer, WorkflowContext } from './constraint-layer.js';
14
- import { WorkflowEngine, WorkflowStep, StepResult, Workflow } from './workflow-engine.js';
15
- import { DeepThinkingEngine, AgentCoordinator, type ThinkResult, type AgentResult } from '@bolloon/constraint-runtime';
16
- import { WorkflowPivotLoop, createDefaultPivotConfig, type PivotLoopConfig, type LoopResult } from './workflow-pivot-loop.js';
17
- import { p2pDocumentTools, initDocumentReceiver } from './p2p-document-tools.js';
18
- import { shellExec } from './shell-tool.js';
19
- import { getBranchPrefix, getCooldownMs } from './shell-guard.js';
20
- import {
21
- DiscoveredAgentsManager,
22
- SocialHeartbeat,
23
- createSocialHeartbeat,
24
- getSocialHeartbeat,
25
- type PersonaDoc,
26
- type DiscoveredAgent,
27
- type SessionChannel,
28
- type SessionMessage,
29
- type SocialSessionProvider
30
- } from '../social/heartbeat.js';
31
- import {
32
- GlobalSharedContextManager,
33
- createGlobalSharedContext,
34
- getGlobalSharedContext,
35
- type ActionSummary,
36
- type AgentInfo,
37
- type CooperationTask,
38
- type CooperationType,
39
- type GlobalSharedContext
40
- } from '../social/global-shared-context.js';
41
- import { Session, SkillRegistry, saveSession, loadSession, type Skill, type StoredSession } from '@bolloon/constraint-runtime';
42
- import { loadSkillsFromPaths, defaultSkillPaths, describeSkill } from './skill-loader.js';
43
-
44
- // Judgment 注入门 (P0): 在主对话 LLM 调起前自动拼入 Top 3 判断力
45
- // 失败静默, 不阻塞主对话
46
- import { injectJudgmentGate, recordJudgmentUsage } from '../pi-ecosystem-judgment/injection-gate.js';
47
- // 持续监控门 (P3): AI 回复后审计是否违反原则
48
- import { monitorAfterReply } from '../pi-ecosystem-judgment/monitor-gate.js';
49
- // Bootstrap 生命周期 hook (SessionStart / Stop / PreToolUse)
50
- import { onSessionStart, onStop, onPreToolUse } from '../pi-ecosystem-judgment/human-value-pipeline.js';
51
- import { onPostToolUse, onJudgmentInjected, onMonitorViolation } from '../bootstrap/lifecycle-hooks.js';
52
- import { budgetReduce, snip, microcompact } from '../context-compaction/index.js';
53
- // React Harness: 8-gate + 4-guard (防越权 / 防 prompt 注入)
54
- import { ReactHarness } from '../security/react-harness.js';
55
-
56
- // Pi Ecosystem Integration (lazy imports - initialized on demand)
57
- // Functions from: createGoal, getCurrentGoal, completeCurrentGoal, failCurrentGoal, getGoalStats, getQueueSummary
58
-
59
- export interface AgentSessionConfig {
60
- cwd: string;
61
- peerId?: string;
62
- identityDoc?: IdentityDoc;
63
- usePivotLoop?: boolean;
64
- pivotLoopConfig?: PivotLoopConfig;
65
- /**
66
- * Skills 加载目录列表, 后者覆盖前者同名 skill.
67
- * 留空时使用 defaultSkillPaths() 推断的默认路径
68
- * ( ~/.bolloon/skills/ → <cwd>/.bolloon/skills/ → ~/.boll/skills/ )
69
- */
70
- skillsPaths?: string[];
71
- }
72
-
73
- export interface IdentityDoc {
74
- did: string;
75
- name: string;
76
- publicKey: string;
77
- createdAt: number;
78
- peerId?: string;
79
- p2pChannel?: string;
80
- cid?: string;
81
- ipnsName?: string;
82
- walletAddress?: string;
83
- }
84
-
85
- export interface ImprovementRequest {
86
- originalPath: string;
87
- requirements: string;
88
- context?: string;
89
- }
90
-
91
- export type { WorkflowStep, StepResult, Workflow } from './workflow-engine.js';
92
- export type { ActionSummary, AgentInfo, CooperationTask, CooperationType } from '../social/global-shared-context.js';
93
-
94
- export interface PiSessionState {
95
- id: string;
96
- agentId: string;
97
- cwd: string;
98
- startedAt: string;
99
- lastActive: string;
100
- tokenUsage?: {
101
- promptTokens: number;
102
- completionTokens: number;
103
- totalTokens: number;
104
- };
105
- }
106
-
107
- export interface PiMemory {
108
- workingMemory: string[];
109
- summarizedMemory: string[];
110
- fileContext: Map<string, string>;
111
- }
112
-
113
- const SHARED_SESSION_PATH = path.join(process.env.HOME || '/tmp', '.bolloon', 'sessions');
114
- const PERSONA_PATH = path.join(process.env.HOME || '/tmp', '.bolloon', 'persona.json');
115
-
116
- export class PiSessionManager implements SocialSessionProvider {
117
- private session: Session;
118
- private state: PiSessionState;
119
- private memory: PiMemory;
120
- private persona: PersonaDoc | null = null;
121
- private channels: Map<string, SessionChannel> = new Map();
122
- private channelsPath: string;
123
- private initialized: boolean = false;
124
- private sessionDir: string;
125
- private cwd: string;
126
- private sharedContext: GlobalSharedContextManager;
127
- private agentId: string;
128
-
129
- constructor(agentId: string, cwd: string) {
130
- this.cwd = cwd;
131
- this.sessionDir = path.join(cwd, '.port_sessions');
132
- this.agentId = agentId;
133
-
134
- const sessionId = `pi-session-${Date.now()}`;
135
- this.session = new Session(sessionId);
136
-
137
- this.state = {
138
- id: sessionId,
139
- agentId,
140
- cwd,
141
- startedAt: new Date().toISOString(),
142
- lastActive: new Date().toISOString()
143
- };
144
- this.memory = {
145
- workingMemory: [],
146
- summarizedMemory: [],
147
- fileContext: new Map()
148
- };
149
- this.channelsPath = path.join(SHARED_SESSION_PATH, 'pi-channels.json');
150
- this.sharedContext = getGlobalSharedContext();
151
- }
152
-
153
- get sessionId(): string {
154
- return this.session.sessionId;
155
- }
156
-
157
- get turnCount(): number {
158
- return this.session.turnCount;
159
- }
160
-
161
- addSessionMessage(msg: string): void {
162
- this.session.addMessage(msg);
163
- this.persistSession();
164
- }
165
-
166
- getSessionHistory(): string[] {
167
- return this.session.history;
168
- }
169
-
170
- setSessionContext(key: string, value: unknown): void {
171
- this.session.setContext(key, value);
172
- this.persistSession();
173
- }
174
-
175
- getSessionContext(key: string): unknown {
176
- return this.session.getContext(key);
177
- }
178
-
179
- private persistSession(): void {
180
- try {
181
- const stored: StoredSession = {
182
- sessionId: this.session.sessionId,
183
- messages: this.session.history,
184
- inputTokens: 0,
185
- outputTokens: 0
186
- };
187
- saveSession(stored);
188
- } catch (e) {
189
- console.warn('Failed to persist session:', e);
190
- }
191
- }
192
-
193
- private loadPersistedSession(): void {
194
- try {
195
- const sessionId = this.state.id;
196
- const stored = loadSession(sessionId);
197
- for (const msg of stored.messages) {
198
- this.session.addMessage(msg);
199
- }
200
- } catch {
201
- // No persisted session found, start fresh
202
- }
203
- }
204
-
205
- async initialize(): Promise<void> {
206
- if (this.initialized) return;
207
- await fs.mkdir(SHARED_SESSION_PATH, { recursive: true });
208
- await fs.mkdir(this.sessionDir, { recursive: true });
209
- this.persona = await this.loadPersona();
210
- await this.loadChannels();
211
- this.loadPersistedSession();
212
- await this.sharedContext.initialize();
213
-
214
- await this.sharedContext.registerAgent({
215
- agentId: this.agentId,
216
- sessionId: this.sessionId,
217
- channelId: 'system',
218
- capabilities: this.persona?.capabilities || [],
219
- status: 'active',
220
- name: this.persona?.name,
221
- persona: this.persona ? {
222
- name: this.persona.name,
223
- description: this.persona.description,
224
- capabilities: this.persona.capabilities
225
- } : undefined
226
- });
227
-
228
- this.initialized = true;
229
- }
230
-
231
- private async loadPersona(): Promise<PersonaDoc | null> {
232
- try {
233
- const data = await fs.readFile(PERSONA_PATH, 'utf-8');
234
- return JSON.parse(data) as PersonaDoc;
235
- } catch {
236
- return null;
237
- }
238
- }
239
-
240
- private async loadChannels(): Promise<void> {
241
- try {
242
- const data = await fs.readFile(this.channelsPath, 'utf-8');
243
- const channelsArray: SessionChannel[] = JSON.parse(data);
244
- this.channels.clear();
245
- for (const channel of channelsArray) {
246
- this.channels.set(channel.id, channel);
247
- }
248
- } catch {
249
- this.channels.clear();
250
- }
251
- }
252
-
253
- private async saveChannels(): Promise<void> {
254
- const channelsArray = Array.from(this.channels.values());
255
- await fs.writeFile(this.channelsPath, JSON.stringify(channelsArray, null, 2));
256
- }
257
-
258
- async savePersona(persona: PersonaDoc): Promise<void> {
259
- await fs.writeFile(PERSONA_PATH, JSON.stringify(persona, null, 2));
260
- this.persona = persona;
261
- }
262
-
263
- getPersona(): PersonaDoc | null {
264
- return this.persona;
265
- }
266
-
267
- getState(): PiSessionState {
268
- return { ...this.state, lastActive: new Date().toISOString() };
269
- }
270
-
271
- getMemory(): PiMemory {
272
- return this.memory;
273
- }
274
-
275
- addToWorkingMemory(content: string): void {
276
- this.memory.workingMemory.push(content);
277
- if (this.memory.workingMemory.length > 100) {
278
- this.memory.workingMemory = this.memory.workingMemory.slice(-100);
279
- }
280
- this.state.lastActive = new Date().toISOString();
281
- }
282
-
283
- addSummarizedMemory(content: string): void {
284
- this.memory.summarizedMemory.push(content);
285
- if (this.memory.summarizedMemory.length > 50) {
286
- this.memory.summarizedMemory = this.memory.summarizedMemory.slice(-50);
287
- }
288
- }
289
-
290
- addFileContext(filePath: string, content: string): void {
291
- this.memory.fileContext.set(filePath, content);
292
- if (this.memory.fileContext.size > 20) {
293
- const entries = Array.from(this.memory.fileContext.entries());
294
- this.memory.fileContext = new Map(entries.slice(-20));
295
- }
296
- }
297
-
298
- updateTokenUsage(promptTokens: number, completionTokens: number): void {
299
- this.state.tokenUsage = {
300
- promptTokens: (this.state.tokenUsage?.promptTokens || 0) + promptTokens,
301
- completionTokens: (this.state.tokenUsage?.completionTokens || 0) + completionTokens,
302
- totalTokens: (this.state.tokenUsage?.totalTokens || 0) + promptTokens + completionTokens
303
- };
304
- }
305
-
306
- async addMessage(channelId: string, message: SessionMessage): Promise<void> {
307
- await this.initialize();
308
-
309
- if (!this.channels.has(channelId)) {
310
- this.channels.set(channelId, {
311
- id: channelId,
312
- name: channelId,
313
- messages: [],
314
- createdAt: new Date().toISOString(),
315
- updatedAt: new Date().toISOString()
316
- });
317
- }
318
-
319
- const channel = this.channels.get(channelId)!;
320
- channel.messages.push(message);
321
- channel.updatedAt = new Date().toISOString();
322
- await this.saveChannels();
323
- }
324
-
325
- async getChannelMessages(channelId: string): Promise<SessionMessage[]> {
326
- await this.initialize();
327
- return this.channels.get(channelId)?.messages || [];
328
- }
329
-
330
- async createChannel(name: string, peerInfo?: { peerId?: string; peerDid?: string; peerName?: string }, persona?: PersonaDoc): Promise<SessionChannel> {
331
- await this.initialize();
332
-
333
- const channelId = `ch_${Date.now()}_${Math.random().toString(36).substring(2, 8)}`;
334
- const channel: SessionChannel = {
335
- id: channelId,
336
- name,
337
- messages: [],
338
- createdAt: new Date().toISOString(),
339
- updatedAt: new Date().toISOString(),
340
- ...peerInfo,
341
- persona: persona || undefined
342
- };
343
-
344
- this.channels.set(channelId, channel);
345
- await this.saveChannels();
346
- return channel;
347
- }
348
-
349
- async getOrCreatePeerChannel(peerDid: string, peerName: string, persona?: PersonaDoc): Promise<SessionChannel> {
350
- await this.initialize();
351
-
352
- for (const channel of this.channels.values()) {
353
- if (channel.peerDid === peerDid) {
354
- return channel;
355
- }
356
- }
357
-
358
- return this.createChannel(`与 ${peerName} 的对话`, {
359
- peerDid,
360
- peerName
361
- }, persona);
362
- }
363
-
364
- async setChannelInfo(channelId: string, info: Partial<SessionChannel>): Promise<void> {
365
- await this.initialize();
366
- const channel = this.channels.get(channelId);
367
- if (channel) {
368
- Object.assign(channel, info, { updatedAt: new Date().toISOString() });
369
- await this.saveChannels();
370
- }
371
- }
372
-
373
- getAllChannels(): SessionChannel[] {
374
- return Array.from(this.channels.values());
375
- }
376
-
377
- getChannelPersona(channelId: string): PersonaDoc | undefined {
378
- return this.channels.get(channelId)?.persona;
379
- }
380
-
381
- async setChannelPersona(channelId: string, persona: PersonaDoc): Promise<void> {
382
- await this.initialize();
383
- const channel = this.channels.get(channelId);
384
- if (channel) {
385
- channel.persona = persona;
386
- channel.updatedAt = new Date().toISOString();
387
- await this.saveChannels();
388
- }
389
- }
390
-
391
- async addUserActionToSharedContext(content: string, importance?: number): Promise<void> {
392
- await this.initialize();
393
- await this.sharedContext.addUserAction(content, this.agentId, undefined, importance);
394
- await this.sharedContext.updateAgentStatus(this.agentId, 'active');
395
- }
396
-
397
- async addSharedKnowledge(knowledge: string): Promise<void> {
398
- await this.initialize();
399
- await this.sharedContext.addSharedKnowledge(knowledge);
400
- }
401
-
402
- async getRecentActionsSummary(count?: number): Promise<string> {
403
- return this.sharedContext.getRecentActionsSummary(count);
404
- }
405
-
406
- async getSharedKnowledge(): Promise<string[]> {
407
- return this.sharedContext.getSharedKnowledge();
408
- }
409
-
410
- async getGlobalContext(): Promise<GlobalSharedContext> {
411
- return this.sharedContext.getFullContext();
412
- }
413
-
414
- async getGlobalContextSummary(): Promise<string> {
415
- return this.sharedContext.getContextSummary();
416
- }
417
-
418
- async createCooperation(
419
- type: CooperationType,
420
- task: string,
421
- toAgentId?: string,
422
- context?: string
423
- ): Promise<CooperationTask> {
424
- await this.initialize();
425
- return this.sharedContext.createCooperation(type, this.agentId, task, toAgentId, context);
426
- }
427
-
428
- async getPendingCooperations(): Promise<CooperationTask[]> {
429
- return this.sharedContext.getPendingCooperations(this.agentId);
430
- }
431
-
432
- async updateCooperationStatus(
433
- cooperationId: string,
434
- status: 'pending' | 'in_progress' | 'done' | 'failed',
435
- result?: string
436
- ): Promise<void> {
437
- await this.sharedContext.updateCooperationStatus(cooperationId, status, result);
438
- }
439
-
440
- async getAllRegisteredAgents(): Promise<AgentInfo[]> {
441
- return this.sharedContext.getAllAgents();
442
- }
443
-
444
- async findAgentByCapability(capability: string): Promise<AgentInfo[]> {
445
- return this.sharedContext.findAgentByCapability(capability);
446
- }
447
-
448
- async getCooperation(cooperationId: string): Promise<CooperationTask | undefined> {
449
- return this.sharedContext.getCooperation(cooperationId);
450
- }
451
-
452
- async updateAgentStatusInRegistry(status: 'active' | 'idle' | 'busy'): Promise<void> {
453
- await this.sharedContext.updateAgentStatus(this.agentId, status);
454
- }
455
- }
456
-
457
- export interface Tool {
458
- name: string;
459
- description: string;
460
- parameters: Record<string, string>;
461
- execute: (args: Record<string, string>) => Promise<ToolResult>;
462
- }
463
-
464
- export interface ToolResult {
465
- success: boolean;
466
- output?: string;
467
- error?: string;
468
- }
469
-
470
- interface Message {
471
- role: 'user' | 'assistant' | 'system' | 'tool';
472
- content: string;
473
- toolCall?: {
474
- name: string;
475
- args: Record<string, string>;
476
- };
477
- toolResult?: ToolResult;
478
- }
479
-
480
- export interface StreamCallback {
481
- (event: StreamEvent): void;
482
- }
483
-
484
- export interface StreamEvent {
485
- type: 'status' | 'thinking' | 'tool' | 'token' | 'done' | 'error'
486
- | 'step_start' | 'step_done' | 'step_error';
487
- content: string;
488
- tool?: string;
489
- data?: unknown;
490
- // step_* 专用: success / output / error
491
- success?: boolean;
492
- output?: string;
493
- error?: string;
494
- args?: Record<string, unknown>;
495
- // step_* 可选: 步骤耗时 (server 端用来展示 in 状态条 + 性能分析)
496
- durationMs?: number;
497
- }
498
-
499
- const TOOL_DEFINITIONS = `
500
- 可用工具:
501
- 1. read_document(path) - 读取文档内容,支持 .txt, .md, .pdf, .docx
502
- 2. summarize_document(path, context?) - 总结文档内容,可选提供上下文
503
- 3. improve_document(path, requirements) - 改进文档,需提供文件路径和改进要求
504
- 4. list_peers() - 列出已连接的对等节点
505
- 5. send_message(peer_id, message) - 向指定对等节点发送消息
506
- 6. broadcast_message(message) - 向所有对等节点广播消息
507
- 7. get_identity() - 获取当前智能体身份信息
508
- 8. set_persona(persona_json) - 更新智能体 persona,包含 name、description、personality、greeting 等
509
- 9. run_workflow(steps) - 执行预定义工作流
510
- 10. get_operation_logs() - 获取操作日志
511
- `;
512
-
513
- export interface HeartbeatConfig {
514
- intervalMs: number;
515
- peerDiscoveryEnabled: boolean;
516
- ipnsResolveEnabled: boolean;
517
- autoSocialEnabled: boolean;
518
- greetingMessage?: string;
519
- }
520
-
521
- export interface AgentSession {
522
- prompt(input: string, options?: { onStream?: StreamCallback; signal?: AbortSignal; channelId?: string }): Promise<string>;
523
- promptStream(input: string, onStream: StreamCallback, signal?: AbortSignal, channelId?: string): Promise<string>;
524
- promptWithPivotLoop(input: string, config?: PivotLoopConfig, channelId?: string): Promise<LoopResult>;
525
- suggestRename(messages: { type: string; content: string }[]): Promise<string | null>;
526
- readDocument(filePath: string): Promise<string>;
527
- summarizeDocument(filePath: string, context?: string): Promise<{
528
- summary: string;
529
- qualityScore: number;
530
- }>;
531
- improveDocument(request: ImprovementRequest): Promise<{
532
- improved: boolean;
533
- newContent?: string;
534
- qualityScore: number;
535
- shouldAutoSend: boolean;
536
- }>;
537
- runWorkflow(workflow: WorkflowStep[]): Promise<Workflow>;
538
- getPeers(): string[];
539
- sendMessage(peerId: string, message: string): Promise<void>;
540
- broadcast(message: string): Promise<void>;
541
- getIdentity(): IdentityDoc;
542
- updateIdentity(updates: Partial<IdentityDoc>): void;
543
- setCurrentChannelId(channelId: string): void;
544
- getSessionState(): PiSessionState;
545
- getMemory(): PiMemory;
546
- getPersona(): PersonaDoc | null;
547
- setPersona(persona: PersonaDoc): Promise<void>;
548
- getDiscoveredAgents(): DiscoveredAgent[];
549
- getSocialChannels(): SessionChannel[];
550
- sendSocialMessage(channelId: string, content: string): Promise<void>;
551
- startSocialHeartbeat(config?: Partial<HeartbeatConfig>): Promise<void>;
552
- stopSocialHeartbeat(): void;
553
- addUserAction(content: string, importance?: number): Promise<void>;
554
- addSharedKnowledge(knowledge: string): Promise<void>;
555
- getRecentActionsSummary(count?: number): Promise<string>;
556
- getSharedKnowledge(): Promise<string[]>;
557
- getGlobalContextSummary(): Promise<string>;
558
- createCooperation(type: CooperationType, task: string, toAgentId?: string, context?: string): Promise<CooperationTask>;
559
- getPendingCooperations(): Promise<CooperationTask[]>;
560
- updateCooperationStatus(cooperationId: string, status: 'pending' | 'in_progress' | 'done' | 'failed', result?: string): Promise<void>;
561
- getAllRegisteredAgents(): Promise<AgentInfo[]>;
562
- findAgentByCapability(capability: string): Promise<AgentInfo[]>;
563
- archiveToHarness(): void;
564
- getHarnessContext(): string;
565
- isHarnessEnabled(): boolean;
566
- getHarness(): any;
567
- getOperationLog(): Array<{ timestamp: number; action: string; args: any; result: any; status: string }>;
568
- }
569
-
570
- class PiAgentSession implements AgentSession {
571
- private cwd: string;
572
- private peerId: string;
573
- private identity: IdentityDoc;
574
- private persona: PersonaDoc | null = null;
575
- private minimaxAvailable = false;
576
- private workflows: Map<string, Workflow> = new Map();
577
- private constraintLayer: ConstraintLayer;
578
- private workflowEngine: WorkflowEngine;
579
- private sessionManager: PiSessionManager;
580
- private agentsManager: DiscoveredAgentsManager;
581
- private socialHeartbeat: SocialHeartbeat | null = null;
582
- private messageHistory: Message[] = [];
583
- private tools: Map<string, Tool> = new Map();
584
- private skillRegistry: SkillRegistry = new SkillRegistry();
585
- // 2026-06-16 修: 父要求把 ReAct loop 上限放大到 "几乎无限", 靠自动压缩上下文 + fail-safe 兜底
586
- // 默认 10000 — 正常任务永远跑不到, 但作为防 LLM 死循环 / 防 OOM 的最后一道闸
587
- // 旧默认 100 写死导致中等复杂度任务 (10-50 个 tool call + 多步反思) 会被误杀
588
- private readonly MAX_REACT_ITERATIONS = 10_000;
589
- private readonly MAX_REFINE_ATTEMPTS = 3;
590
- private readonly QUALITY_THRESHOLD = 0.6;
591
- /** P1: 上下文溢出阈值 (单轮估算 token 数, 超过则强制终止防止 prompt-too-long) */
592
- private readonly MAX_OUTPUT_TOKEN_ESCALATION_THRESHOLD = 60_000; // 60K tokens 上限
593
- /** 2026-06-16 新增: 累计错误总数兜底 (不管是否同工具, 累计 N 次就强制退出)
594
- * 防 LLM 轮换工具名绕开 MAX_SAME_TOOL_FAILURES 的死循环攻击 */
595
- private readonly MAX_TOTAL_ERRORS = 20;
596
- /** 2026-06-16 新增: loop 内自动压缩触发阈值 (相对 60K 阈值的比例) */
597
- private readonly LOOP_COMPACT_RATIO = 0.8;
598
- /** P1: max output token 升级重试 (LLM 截断时重试, 最多 3 次) */
599
- private readonly MAX_OUTPUT_TOKEN_ESCALATION_RETRIES = 3;
600
- private thinkingEngine = new DeepThinkingEngine(3);
601
- private coordinator = new AgentCoordinator(3);
602
- private harness: any = null;
603
- private harnessEnabled = false;
604
- /** 8-gate + 4-guard 集中调度 (防越权 / 防 prompt 注入) */
605
- private reactHarness: ReactHarness = new ReactHarness();
606
- private usePivotLoop: boolean = false;
607
- private pivotLoopConfig?: PivotLoopConfig;
608
- /** P2: 当前会话的 permission mode (每次 promptStream 入口解析) */
609
- private currentPermissionMode: import('./permission-mode.js').PermissionMode = 'default';
610
- /** P1.2: Context Collapse 读时投影结果 (feature flag 开启时由 maybeAutoCompact 写入, buildContext 优先用) */
611
- private projectedHistory: Message[] | null = null;
612
-
613
- /**
614
- * Judgment 注入门临时结果: 在 prompt / promptStream / promptWithPivotLoop 入口算一次, 拼到本轮 systemPrompt 末尾
615
- * 每次调用都会重置 (避免上一轮遗留)
616
- */
617
- private judgmentGateAddition: string = '';
618
- private judgmentGateUsedIds: string[] = [];
619
-
620
- /**
621
- * 当前 onStream 引用 + abort signal (computeJudgmentGate 需要 onStream 广播 phase)
622
- * 每次 prompt / promptStream / promptWithPivotLoop 入口设置, 用完即清
623
- */
624
- private currentOnStream: StreamCallback | null = null;
625
- private currentSignal: AbortSignal | null = null;
626
- /** Bootstrap SessionStart 拼的 system prompt 片段 (用完即清) */
627
- private bootstrapAddition: string = '';
628
- /** 当前 prompt 开始时间 (供 Stop hook 计算 durationMs) */
629
- private promptStartTime: number = 0;
630
- /** 当前 channel id (由 getAgentForChannel / prompt 4 参注入, 供 hook / log 使用) */
631
- private currentChannelId: string = '';
632
-
633
- /**
634
- * 算 judgment 注入门: 失败静默, 不阻塞主对话
635
- * 期间通过 currentOnStream 广播 phase 事件, 前端可显示 "正在检索判断力..." 状态
636
- * 调用方负责用完即清 (judgmentGateAddition='')
637
- */
638
- private async computeJudgmentGate(input: string): Promise<void> {
639
- const safePhase = (phase: string, extra: Record<string, unknown> = {}) => {
640
- try {
641
- if (this.currentOnStream) {
642
- this.currentOnStream({ type: 'phase', phase, ...extra, content: '' } as any);
643
- }
644
- } catch { /* 静默 */ }
645
- };
646
-
647
- safePhase('gate_compute', { detail: '正在检索相关判断力...' });
648
- try {
649
- // P-Action 4 (2026-06-15) 路径 1 整合: 透传 maxChars=1500 (≈ 375 tokens 硬上限)
650
- // 路径 2/3 检测由 injection-gate 内部 alreadyInjectedSources 处理 (目前 assembleSystemPrompt 还没注入 value-store 标记, 所以这里不传)
651
- const gate = await injectJudgmentGate(input, {}, { maxChars: 1500 });
652
- this.judgmentGateAddition = gate.systemAddition;
653
- this.judgmentGateUsedIds = gate.usedIds;
654
- if (gate.usedIds.length > 0) {
655
- safePhase('gate_done', { usedCount: gate.usedIds.length, didInject: gate.didInject, skipReason: gate.skipReason });
656
- }
657
- } catch (err) {
658
- console.warn('[PiAgent] judgment gate failed (non-fatal):', err);
659
- this.judgmentGateAddition = '';
660
- this.judgmentGateUsedIds = [];
661
- }
662
- }
663
-
664
- private clearJudgmentGate(): void {
665
- this.judgmentGateAddition = '';
666
- this.judgmentGateUsedIds = [];
667
- }
668
-
669
- constructor(config: AgentSessionConfig) {
670
- this.cwd = config.cwd;
671
- this.peerId = config.peerId || 'local';
672
- this.identity = config.identityDoc || this.createDefaultIdentity();
673
- this.minimaxAvailable = this.checkMinimax();
674
- this.constraintLayer = new ConstraintLayer();
675
- this.workflowEngine = new WorkflowEngine(this.constraintLayer);
676
- this.sessionManager = new PiSessionManager(this.identity.did, this.cwd);
677
- this.agentsManager = new DiscoveredAgentsManager();
678
- this.usePivotLoop = config.usePivotLoop ?? false;
679
- this.pivotLoopConfig = config.pivotLoopConfig;
680
- this.initSession();
681
- initDocumentReceiver();
682
- this.registerTools();
683
- this.loadSkills(config.skillsPaths);
684
- this.initHarness();
685
- }
686
-
687
- /**
688
- * 从 SKILL.md 目录加载 skills 进 skillRegistry.
689
- *
690
- * 路径解析优先级 (后者覆盖前者同名 skill):
691
- * 1. 显式传入的 skillsPaths
692
- * 2. ~/.bolloon/skills/ 全局用户级
693
- * 3. <cwd>/.bolloon/skills/ 项目级
694
- * 4. ~/.boll/skills/ 全局 (兼容 bollharness 旧用户)
695
- * 5. <bolloon-repo>/src/bollharness/.boll/skills/ bolloon 仓库内置 skill
696
- * (bolloon 项目本身用 pi-sdk 写核心, 这 19 个 skill 视为项目级 builtin)
697
- *
698
- * 静默忽略不存在的目录.
699
- */
700
- private loadSkills(paths?: string[]): void {
701
- let resolved: string[];
702
- if (paths && paths.length > 0) {
703
- resolved = paths;
704
- } else {
705
- resolved = [
706
- ...defaultSkillPaths(os.homedir(), this.cwd),
707
- // bolloon 仓库内置 skill (相对本 npm 包的位置)
708
- this.findBolloonBuiltinSkillsPath(),
709
- ].filter((p): p is string => Boolean(p));
710
- }
711
- loadSkillsFromPaths(resolved)
712
- .then((skills) => {
713
- for (const s of skills) {
714
- if (this.skillRegistry.has(s.name)) {
715
- this.skillRegistry.unregister(s.name);
716
- }
717
- this.skillRegistry.register(s);
718
- }
719
- console.log(`[loadSkills] 已加载 ${skills.length} 个 skill: ${skills.map(describeSkill).join(' | ')}`);
720
- })
721
- .catch((err) => {
722
- console.error('[loadSkills] 加载失败:', err);
723
- });
724
- }
725
-
726
- /**
727
- * 定位 bolloon 仓库内置的 bollharness skill 目录.
728
- * 向上回溯 cwd, 找第一个包含 src/bollharness/.boll/skills 的祖先.
729
- * 找不到时返回 null (例如把 bolloon-agent 作为外部依赖安装时).
730
- */
731
- private findBolloonBuiltinSkillsPath(): string | null {
732
- let dir = this.cwd;
733
- for (let i = 0; i < 6; i++) {
734
- const candidate = path.join(dir, 'src', 'bollharness', '.boll', 'skills');
735
- try {
736
- if (fsSync.existsSync(candidate) && fsSync.statSync(candidate).isDirectory()) {
737
- return candidate;
738
- }
739
- } catch {
740
- // 忽略 stat 异常, 继续向上
741
- }
742
- const parent = path.dirname(dir);
743
- if (parent === dir) break;
744
- dir = parent;
745
- }
746
- return null;
747
- }
748
-
749
- private async initHarness(): Promise<void> {
750
- try {
751
- const { createBollharnessIntegration } = await import('../bollharness-integration/index.js');
752
- this.harness = createBollharnessIntegration();
753
- this.harnessEnabled = true;
754
- // ReactHarness 已用 bollharness, 这里也记一份以供 archive 调用
755
- this.reactHarness = new ReactHarness({ harnessEnabled: true, gateEnabled: true });
756
- } catch (e) {
757
- console.warn('[PiAgentSession] Harness initialization failed:', e);
758
- this.harnessEnabled = false;
759
- // 失败 fallback: 走纯 8-gate (不带 bollharness 的 8-gate 工作流)
760
- this.reactHarness = new ReactHarness({ harnessEnabled: false, gateEnabled: true });
761
- }
762
- }
763
-
764
- private registerTools(): void {
765
- this.tools.set('read_document', {
766
- name: 'read_document',
767
- description: '读取文档内容,支持 .txt, .md, .pdf, .docx 格式',
768
- parameters: { path: 'string' },
769
- execute: async (args) => {
770
- try {
771
- const content = await documentReader.read(args.path);
772
- return {
773
- success: true,
774
- output: `📄 ${content.metadata.filename}\n大小: ${content.metadata.size} 字节\n\n${content.text.substring(0, 1000)}${content.text.length > 1000 ? '...' : ''}`
775
- };
776
- } catch (e) {
777
- return { success: false, error: String(e) };
778
- }
779
- }
780
- });
781
-
782
- this.tools.set('summarize_document', {
783
- name: 'summarize_document',
784
- description: '总结文档内容,分析并生成摘要',
785
- parameters: { path: 'string', context: 'string' },
786
- execute: async (args) => {
787
- try {
788
- if (!this.minimaxAvailable) {
789
- return { success: false, error: 'LLM未初始化,请设置 MINIMAX_API_KEY' };
790
- }
791
- const result = await this.summarizeDocument(args.path, args.context);
792
- return {
793
- success: true,
794
- output: `📝 摘要:\n${result.summary}\n\n质量评分: ${(result.qualityScore * 10).toFixed(1)}/10`
795
- };
796
- } catch (e) {
797
- return { success: false, error: String(e) };
798
- }
799
- }
800
- });
801
-
802
- this.tools.set('improve_document', {
803
- name: 'improve_document',
804
- description: '根据要求改进文档内容',
805
- parameters: { path: 'string', requirements: 'string' },
806
- execute: async (args) => {
807
- try {
808
- if (!this.minimaxAvailable) {
809
- return { success: false, error: 'LLM未初始化,请设置 MINIMAX_API_KEY' };
810
- }
811
- const result = await this.improveDocument({
812
- originalPath: args.path,
813
- requirements: args.requirements
814
- });
815
- return {
816
- success: true,
817
- output: `✅ 改进${result.improved ? '成功' : '失败'}\n质量评分: ${(result.qualityScore * 10).toFixed(1)}/10\n${result.newContent ? '\n改进内容:\n' + result.newContent.substring(0, 500) + '...' : ''}`
818
- };
819
- } catch (e) {
820
- return { success: false, error: String(e) };
821
- }
822
- }
823
- });
824
-
825
- this.tools.set('list_peers', {
826
- name: 'list_peers',
827
- description: '列出已连接的对等节点',
828
- parameters: {},
829
- execute: async () => {
830
- const peers = p2pNetwork.getPeers();
831
- if (peers.length === 0) {
832
- return { success: true, output: '当前无连接的对等节点' };
833
- }
834
- return { success: true, output: `已连接节点 (${peers.length}):\n${peers.map(p => ` - ${p}`).join('\n')}` };
835
- }
836
- });
837
-
838
- this.tools.set('send_message', {
839
- name: 'send_message',
840
- description: '向指定对等节点发送消息',
841
- parameters: { peer_id: '对等节点ID', message: '消息内容' },
842
- execute: async (args) => {
843
- try {
844
- await p2pNetwork.sendMessage(args.peer_id, 'message', args.message);
845
- return { success: true, output: `消息已发送到 ${args.peer_id}` };
846
- } catch (e) {
847
- return { success: false, error: String(e) };
848
- }
849
- }
850
- });
851
-
852
- this.tools.set('broadcast_message', {
853
- name: 'broadcast_message',
854
- description: '向所有对等节点广播消息',
855
- parameters: { message: '消息内容' },
856
- execute: async (args) => {
857
- try {
858
- await p2pNetwork.broadcast('message', args.message);
859
- return { success: true, output: '消息已广播到所有节点' };
860
- } catch (e) {
861
- return { success: false, error: String(e) };
862
- }
863
- }
864
- });
865
-
866
- this.tools.set('get_identity', {
867
- name: 'get_identity',
868
- description: '获取当前智能体身份信息',
869
- parameters: {},
870
- execute: async () => {
871
- const id = this.getIdentity();
872
- const extraInfo = id.cid ? `\nCID: ${id.cid}` : '';
873
- const ipnsInfo = id.ipnsName ? `\nIPNS: ${id.ipnsName}` : '';
874
- return {
875
- success: true,
876
- output: `DID: ${id.did}\n名称: ${id.name}\n公钥: ${id.publicKey}${extraInfo}${ipnsInfo}\n创建时间: ${new Date(id.createdAt).toISOString()}`
877
- };
878
- }
879
- });
880
-
881
- this.tools.set('set_persona', {
882
- name: 'set_persona',
883
- description: '更新智能体的 persona 信息,包括名字、描述、性格等',
884
- parameters: { persona_json: 'Persona JSON 对象,包含 name、description、personality、greeting 等字段' },
885
- execute: async (args) => {
886
- try {
887
- const personaData = typeof args.persona_json === 'string' ? JSON.parse(args.persona_json) : args.persona_json;
888
- const now = new Date().toISOString();
889
- const newPersona: PersonaDoc = {
890
- name: personaData.name || this.identity.name,
891
- description: personaData.description || '',
892
- capabilities: personaData.capabilities || [],
893
- personality: personaData.personality || '',
894
- greeting: personaData.greeting || '',
895
- interests: personaData.interests || [],
896
- createdAt: this.persona?.createdAt || now,
897
- updatedAt: now
898
- };
899
- await this.setPersona(newPersona);
900
- this.persona = newPersona;
901
- if (newPersona.name) {
902
- this.identity.name = newPersona.name;
903
- }
904
- return { success: true, output: `Persona 已更新:\n名称: ${newPersona.name}\n描述: ${newPersona.description}\n性格: ${newPersona.personality}` };
905
- } catch (e) {
906
- return { success: false, error: `更新 persona 失败: ${String(e)}` };
907
- }
908
- }
909
- });
910
-
911
- this.tools.set('get_operation_logs', {
912
- name: 'get_operation_logs',
913
- description: '获取约束层的操作日志',
914
- parameters: {},
915
- execute: async () => {
916
- const logs = this.constraintLayer.getLogs();
917
- if (logs.length === 0) {
918
- return { success: true, output: '暂无操作日志' };
919
- }
920
- return {
921
- success: true,
922
- output: `操作日志 (${logs.length} 条):\n${logs.slice(-10).map(l => `[${new Date(l.timestamp).toISOString()}] ${l.action} - ${l.status}`).join('\n')}`
923
- };
924
- }
925
- });
926
-
927
- // 添加文件列表工具
928
- this.tools.set('list_files', {
929
- name: 'list_files',
930
- description: '列出目录中的文件',
931
- parameters: { path: '目录路径(可选,默认为当前目录)' },
932
- execute: async (args) => {
933
- try {
934
- const fs = await import('fs');
935
- const path = args.path || this.cwd;
936
- const files = fs.readdirSync(path);
937
- return {
938
- success: true,
939
- output: `📁 目录 ${path} 中的文件 (${files.length} 个):\n${files.slice(0, 20).map(f => ` - ${f}`).join('\n')}${files.length > 20 ? '\n ...' : ''}`
940
- };
941
- } catch (e) {
942
- return { success: false, error: String(e) };
943
- }
944
- }
945
- });
946
-
947
- // 添加目录读取工具(更完整的实现)
948
- this.tools.set('read_directory', {
949
- name: 'read_directory',
950
- description: '读取目录内容,返回文件列表和目录结构',
951
- parameters: { path: '目录路径(可选,默认为当前目录)' },
952
- execute: async (args) => {
953
- try {
954
- const fs = await import('fs');
955
- const pathModule = await import('path');
956
- const targetPath = args.path || this.cwd;
957
- const items = fs.readdirSync(targetPath);
958
- const result: string[] = [];
959
- for (const item of items.slice(0, 30)) {
960
- const fullPath = pathModule.join(targetPath, item);
961
- try {
962
- const stat = fs.statSync(fullPath);
963
- const type = stat.isDirectory() ? '📁' : '📄';
964
- result.push(`${type} ${item}${stat.isDirectory() ? '/' : ''}`);
965
- } catch {
966
- result.push(`📄 ${item}`);
967
- }
968
- }
969
- return {
970
- success: true,
971
- output: `📂 ${targetPath} (${items.length} 项):\n${result.join('\n')}${items.length > 30 ? '\n... 还有更多文件' : ''}`
972
- };
973
- } catch (e) {
974
- return { success: false, error: `无法读取目录: ${String(e)}` };
975
- }
976
- }
977
- });
978
-
979
- // P2P Document Tools
980
- for (const tool of p2pDocumentTools) {
981
- this.tools.set(tool.name, tool);
982
- }
983
-
984
- // Shell Exec 工具: 给 AI 跑受限的 shell 命令
985
- // **只能** 跑白名单内的命令 (git/npm/tsc/vitest/cat/...)
986
- // **不能** 改禁区路径 (见 shell-guard.ts 的 FORBIDDEN_PATH_PATTERNS)
987
- // 沙箱 cwd: .bolloon-shell-sandbox/
988
- this.tools.set('shell_exec', {
989
- name: 'shell_exec',
990
- description: '在沙箱里跑 shell 命令. 仅支持白名单内命令: git, npm, npx, tsx, tsc, vitest, cat, head, tail, ls, wc, echo, pwd, date, mkdir, touch. 禁止管道/重定向/rm -rf/sudo. 命中护栏黑名单会被拒.',
991
- parameters: { command: '可执行文件 (必填, 必须在白名单)', args: '参数数组, 逗号分隔', timeoutMs: '超时毫秒, 默认 30000' },
992
- execute: async (args) => {
993
- const cmd = String(args.command || '').trim();
994
- if (!cmd) return { success: false, error: 'command 必填' };
995
- const argList = String(args.args || '').split(',').map(s => s.trim()).filter(Boolean);
996
- const timeoutMs = Number(args.timeoutMs) || 30000;
997
-
998
- const result = await shellExec(cmd, argList, { timeoutMs });
999
- if (result.deniedByGuard) {
1000
- return { success: false, error: result.error };
1001
- }
1002
- if (!result.success) {
1003
- return { success: false, error: result.error, output: result.output };
1004
- }
1005
- return { success: true, output: result.output };
1006
- }
1007
- });
1008
-
1009
- // self_improve 工具: AI 触发自我改进循环
1010
- // **必须** 在 branchPrefix 命名的分支上工作
1011
- // 心跳事件会自动调用; 用户对话里也能手动调
1012
- this.tools.set('self_improve', {
1013
- name: 'self_improve',
1014
- description: `触发自我改进循环. AI 会在分支 ${getBranchPrefix()}<timestamp> 上工作, 跑 tsc + vitest 验证, 通过后输出分支名给用户审. 冷却期由策略文件决定. 命中护栏禁区的改动会被拒.`,
1015
- parameters: { goal: '本轮改进目标 (1 句话)' },
1016
- execute: async (args) => {
1017
- const goal = String(args.goal || '').trim();
1018
- if (!goal) return { success: false, error: 'goal 必填' };
1019
- return await runSelfImproveLoop(goal);
1020
- }
1021
- });
1022
-
1023
- // list_skills 工具: 列出当前 session 已加载的 skills
1024
- // 加载源: ~/.bolloon/skills/ → <cwd>/.bolloon/skills/ → ~/.boll/skills/
1025
- this.tools.set('list_skills', {
1026
- name: 'list_skills',
1027
- description: '列出当前 session 已加载的 skills 及其描述. Skills 是从 SKILL.md 文件加载的, 兼容 Anthropic Agent Skills 标准 frontmatter 和 bollharness 现有 frontmatter.',
1028
- parameters: {},
1029
- execute: async () => {
1030
- const skills = this.skillRegistry.list();
1031
- if (skills.length === 0) {
1032
- return {
1033
- success: true,
1034
- output: '当前 session 没有加载任何 skill. 检查 ~/.bolloon/skills/ 或项目 .bolloon/skills/ 目录.',
1035
- };
1036
- }
1037
- const lines = skills.map((s, i) => `${i + 1}. ${s.name} — ${s.description}`);
1038
- return { success: true, output: `已加载 ${skills.length} 个 skill:\n${lines.join('\n')}` };
1039
- }
1040
- });
1041
-
1042
- // use_skill 工具: 加载指定 skill 的 body 进 LLM context
1043
- // Skills 协议核心: 把 SKILL.md body 作为 Markdown 指南返回, LLM 下一轮按它执行
1044
- this.tools.set('use_skill', {
1045
- name: 'use_skill',
1046
- description: '按名称加载一个 skill, 把它的 SKILL.md body 作为 Markdown 文档返回. 调用后 LLM 会在下一轮按 skill 指南执行. 与 shell_exec / read_document 这些 "能力工具" 不同, use_skill 是 "知识注入".',
1047
- parameters: { name: 'skill 名称 (用 list_skills 查可用的)' },
1048
- execute: async (args) => {
1049
- const name = String(args.name || '').trim();
1050
- if (!name) return { success: false, error: 'name 必填' };
1051
- if (!this.skillRegistry.has(name)) {
1052
- const available = this.skillRegistry.list().map((s) => s.name).join(', ');
1053
- return { success: false, error: `skill "${name}" 未找到. 已加载: ${available || '(无)'}` };
1054
- }
1055
- try {
1056
- const body = await this.skillRegistry.execute(name, {});
1057
- return { success: true, output: body };
1058
- } catch (e) {
1059
- return { success: false, error: `执行 skill 失败: ${String(e)}` };
1060
- }
1061
- }
1062
- });
1063
- }
1064
-
1065
- private async registerP2PDocumentReceiver(): Promise<void> {
1066
- await initDocumentReceiver();
1067
- }
1068
-
1069
- private getToolDefinitions(): string {
1070
- const defs: string[] = ['可用工具:'];
1071
- for (const tool of this.tools.values()) {
1072
- const params = Object.entries(tool.parameters).map(([k, v]) => `${k}: ${v}`).join(', ');
1073
- defs.push(`- ${tool.name}(${params}) - ${tool.description}`);
1074
- }
1075
- return defs.join('\n');
1076
- }
1077
-
1078
- private async initSession(): Promise<void> {
1079
- await this.sessionManager.initialize();
1080
- await this.agentsManager.initialize();
1081
-
1082
- this.persona = this.sessionManager.getPersona();
1083
- if (this.persona?.name) {
1084
- this.identity.name = this.persona.name;
1085
- }
1086
- }
1087
-
1088
- private createDefaultIdentity(): IdentityDoc {
1089
- return {
1090
- did: `did:pi:${this.peerId.substring(0, 16)}`,
1091
- name: `Agent-${this.peerId.substring(0, 8)}`,
1092
- publicKey: this.peerId,
1093
- createdAt: Date.now()
1094
- };
1095
- }
1096
-
1097
- private checkMinimax(): boolean {
1098
- try {
1099
- getMinimax();
1100
- return true;
1101
- } catch {
1102
- return false;
1103
- }
1104
- }
1105
-
1106
- async prompt(input: string, options?: { onStream?: StreamCallback; signal?: AbortSignal; channelId?: string }): Promise<string> {
1107
- this.minimaxAvailable = this.checkMinimax();
1108
- this.currentChannelId = options?.channelId ?? this.currentChannelId;
1109
-
1110
- this.messageHistory.push({
1111
- role: 'user',
1112
- content: input
1113
- });
1114
-
1115
- if (!this.minimaxAvailable) {
1116
- const response = await this.handleFallback(input);
1117
- this.messageHistory.push({ role: 'assistant', content: response });
1118
- return response;
1119
- }
1120
-
1121
- // P0 注入门
1122
- this.currentSignal = options?.signal ?? null;
1123
- this.currentOnStream = options?.onStream ?? null;
1124
- await this.computeJudgmentGate(input);
1125
-
1126
- // P2: 解析当前 permission mode
1127
- try {
1128
- const { resolvePermissionMode } = await import('./permission-mode.js');
1129
- this.currentPermissionMode = resolvePermissionMode();
1130
- } catch (err) {
1131
- console.warn('[PiAgent] resolvePermissionMode failed (non-fatal):', err);
1132
- this.currentPermissionMode = 'default';
1133
- }
1134
-
1135
- try {
1136
- // 2026-06-16: runReActLoop 现在返回 { reply, aiFailed, aiFailureReason } — 这里只需 reply 字符串
1137
- const loopResult = await this.runReActLoop(undefined, options?.signal);
1138
- return loopResult.reply;
1139
- } finally {
1140
- if (this.judgmentGateUsedIds.length > 0) {
1141
- recordJudgmentUsage(this.judgmentGateUsedIds, { userInput: input }).catch((err) =>
1142
- console.warn('[PiAgent] recordJudgmentUsage failed:', err)
1143
- );
1144
- }
1145
- this.clearJudgmentGate();
1146
- this.currentSignal = null;
1147
- this.currentOnStream = null;
1148
- }
1149
- }
1150
-
1151
- async promptStream(input: string, onStream: StreamCallback, signal?: AbortSignal, channelId?: string): Promise<string> {
1152
- this.minimaxAvailable = this.checkMinimax();
1153
- this.currentChannelId = channelId ?? this.currentChannelId;
1154
-
1155
- this.messageHistory.push({
1156
- role: 'user',
1157
- content: input
1158
- });
1159
-
1160
- onStream({ type: 'thinking', content: '🤔 开始思考...' });
1161
-
1162
- if (!this.minimaxAvailable) {
1163
- const response = await this.handleFallback(input);
1164
- this.messageHistory.push({ role: 'assistant', content: response });
1165
- onStream({ type: 'done', content: '' });
1166
- return response;
1167
- }
1168
-
1169
- // P0 注入门: 缓存 onStream + signal, computeJudgmentGate 用 currentOnStream 广播 phase
1170
- this.currentOnStream = onStream;
1171
- this.currentSignal = signal ?? null;
1172
- await this.computeJudgmentGate(input);
1173
-
1174
- // P1.1: 异步跑 Auto-Compact (LLM 摘要, 仅在 budget 超限时触发, 失败静默)
1175
- // 复用 computeJudgmentGate 的 onStream 广播 phase, 跟 judgment 注入门风格一致
1176
- try {
1177
- await this.maybeAutoCompact(onStream, signal);
1178
- } catch (err) {
1179
- console.warn('[PiAgent] maybeAutoCompact failed (non-fatal):', err);
1180
- }
1181
-
1182
- // Bootstrap SessionStart: 收集项目 Context, 拼到 systemAddition 头部
1183
- // (失败静默, 5s 限流防止循环)
1184
- let bootstrapAddition = '';
1185
- try {
1186
- const ss = await onSessionStart({ channelId: this.currentChannelId || undefined });
1187
- bootstrapAddition = ss.systemAddition || '';
1188
- } catch (err) {
1189
- console.warn('[PiAgent] onSessionStart failed (non-fatal):', err);
1190
- }
1191
- this.bootstrapAddition = bootstrapAddition;
1192
-
1193
- // P2: 解析当前 permission mode (BootstrapOptions > env BOLLOON_PERM_MODE > default)
1194
- try {
1195
- const { resolvePermissionMode } = await import('./permission-mode.js');
1196
- this.currentPermissionMode = resolvePermissionMode();
1197
- } catch (err) {
1198
- console.warn('[PiAgent] resolvePermissionMode failed (non-fatal, using default):', err);
1199
- this.currentPermissionMode = 'default';
1200
- }
1201
-
1202
- this.promptStartTime = Date.now();
1203
-
1204
- // 2026-06-16: loop 自动重试 — runReActLoop 内部遇到 [AI 服务调用失败] sentinel 时,
1205
- // 会设 aiFailed=true 并提前 break. 这里在外层重跑整个 loop (不是单次 LLM 调用),
1206
- // 临时网络抖动 / 配额瞬时超限可自愈. 最多 3 次, 指数退避 1s/2s/4s.
1207
- // 用户看到 status bar 显示 "自动重试中 X/N" — 不暴露按钮.
1208
- const MAX_LOOP_RETRIES = 3;
1209
- let attempt = 0;
1210
- let result: string = '';
1211
- let lastAiFailureReason = '';
1212
- while (attempt <= MAX_LOOP_RETRIES) {
1213
- try {
1214
- const loopResult = await this.runReActLoop(onStream, signal);
1215
- result = loopResult.reply;
1216
- if (!loopResult.aiFailed) break; // 正常完成, 退出 retry 循环
1217
- lastAiFailureReason = loopResult.aiFailureReason || 'AI 调用失败';
1218
- } catch (err: any) {
1219
- // abort 失败: 视作"已中断", 抛错让上层用 partial 兜底
1220
- this.currentOnStream = null;
1221
- this.currentSignal = null;
1222
- throw err;
1223
- }
1224
- attempt++;
1225
- if (attempt > MAX_LOOP_RETRIES) {
1226
- console.warn(`[PiAgent] loop 自动重试 ${MAX_LOOP_RETRIES} 次后仍失败, 终止`);
1227
- if (onStream) {
1228
- onStream({ type: 'status', content: `⛔ loop 自动重试 ${MAX_LOOP_RETRIES} 次后仍失败: ${lastAiFailureReason}`, tool: 'system' });
1229
- }
1230
- result = lastAiFailureReason || 'AI 服务调用失败, 自动重试后仍不可用';
1231
- break;
1232
- }
1233
- const backoffMs = 1000 * Math.pow(2, attempt - 1); // 1s, 2s, 4s
1234
- console.log(`[PiAgent] loop 自动重试 ${attempt}/${MAX_LOOP_RETRIES}, 等待 ${backoffMs}ms`);
1235
- if (onStream) {
1236
- onStream({ type: 'status', content: `↻ 自动重试 loop ${attempt}/${MAX_LOOP_RETRIES} (${(backoffMs / 1000).toFixed(0)}s 后)`, tool: 'system' });
1237
- }
1238
- // 中途 abort 也要响应
1239
- await new Promise<void>((resolve, reject) => {
1240
- const t = setTimeout(() => {
1241
- signal?.removeEventListener?.('abort', onAbort);
1242
- resolve();
1243
- }, backoffMs);
1244
- const onAbort = () => {
1245
- clearTimeout(t);
1246
- reject(new Error('aborted during retry backoff'));
1247
- };
1248
- if (signal?.aborted) {
1249
- clearTimeout(t);
1250
- reject(new Error('aborted during retry backoff'));
1251
- return;
1252
- }
1253
- signal?.addEventListener?.('abort', onAbort, { once: true });
1254
- });
1255
- // 重试时要把这条 user message 从 history 里移除 (避免下一次 runReActLoop 又重复加入),
1256
- // 因为 messageHistory.push({role:'user'}) 在 promptStream 顶部已经做过, 重跑 runReActLoop 不会重复 push,
1257
- // 但 assistant 失败那条也别留 (留了会污染下一轮 LLM context).
1258
- // 简化: 重试前 pop 一次 assistant (如果最后一条是 assistant)
1259
- if (this.messageHistory.length > 0 && this.messageHistory[this.messageHistory.length - 1].role === 'assistant') {
1260
- this.messageHistory.pop();
1261
- }
1262
- }
1263
- onStream({ type: 'done', content: '' });
1264
-
1265
- // 回溯: 异步记录 usage (不等)
1266
- if (this.judgmentGateUsedIds.length > 0) {
1267
- recordJudgmentUsage(this.judgmentGateUsedIds, { userInput: input }).catch((err) =>
1268
- console.warn('[PiAgent] recordJudgmentUsage failed:', err)
1269
- );
1270
- // P0.5: 把 usedIds 通过 stream 事件回传给调用方 (server.ts 写到 session message)
1271
- try { onStream({ type: 'used_judgments', usedIds: this.judgmentGateUsedIds, content: '' } as any); } catch {}
1272
- }
1273
-
1274
- // P3 监控门: fire-and-forget 审计 AI 回复是否违反原则
1275
- monitorAfterReply(input, result);
1276
-
1277
- // Bootstrap Stop hook: fire-and-forget 写本次 session 摘要
1278
- const stopStartTime = this.promptStartTime || Date.now();
1279
- onStop({
1280
- channelId: this.currentChannelId || 'unknown',
1281
- durationMs: Date.now() - stopStartTime,
1282
- usedJudgmentIds: [...this.judgmentGateUsedIds],
1283
- }).catch((err) => console.warn('[PiAgent] onStop failed:', err));
1284
-
1285
- // 用完即清, 避免污染下一轮
1286
- this.clearJudgmentGate();
1287
- this.currentOnStream = null;
1288
- this.currentSignal = null;
1289
- this.bootstrapAddition = '';
1290
- this.promptStartTime = 0;
1291
-
1292
- return result;
1293
- }
1294
-
1295
- async promptWithPivotLoop(input: string, config?: PivotLoopConfig, channelId?: string): Promise<LoopResult> {
1296
- this.currentChannelId = channelId ?? this.currentChannelId;
1297
- if (!this.minimaxAvailable) {
1298
- const response = await this.handleFallback(input);
1299
- return {
1300
- success: false,
1301
- response,
1302
- iterations: 0,
1303
- toolCalls: 0,
1304
- qualityScore: 0,
1305
- exitReason: 'error',
1306
- state: {
1307
- iteration: 0,
1308
- totalTokens: 0,
1309
- toolCallsCount: 0,
1310
- consecutiveNoProgress: 0,
1311
- qualityScores: [],
1312
- pendingToolUses: [],
1313
- lastMeaningfulWork: 0
1314
- }
1315
- };
1316
- }
1317
-
1318
- const llm = getMinimax();
1319
- const loopConfig = config || this.pivotLoopConfig || createDefaultPivotConfig();
1320
- const loop = new WorkflowPivotLoop(loopConfig);
1321
-
1322
- for (const tool of this.tools.values()) {
1323
- loop.registerTool(tool);
1324
- }
1325
-
1326
- // P0 注入门: 在构造 systemPrompt 之前算一次, 拼到末尾
1327
- await this.computeJudgmentGate(input);
1328
-
1329
- const personaSection = this.persona ? `
1330
- 角色描述: ${this.persona.description || '无'}
1331
- 性格特点: ${this.persona.personality || '无'}
1332
- 问候语: ${this.persona.greeting || '无'}
1333
- ` : '';
1334
-
1335
- const systemPrompt = `${this.bootstrapAddition}你是 ${this.identity.name},基于ReAct (Reasoning + Acting)模式工作。${personaSection}
1336
- 当前工作目录: ${this.cwd}
1337
- 当前身份: ${this.identity.name} (${this.identity.did})
1338
-
1339
- ${this.getToolDefinitions()}
1340
-
1341
- 工作模式:
1342
- 1. 理解用户自然语言请求
1343
- 2. 分析需要哪些工具来完成
1344
- 3. 按顺序调用工具并观察结果
1345
- 4. 根据观察结果决定下一步
1346
- 5. 最终给出完整回答
1347
-
1348
- 重要:
1349
- - 每次只调用一个工具
1350
- - 仔细分析工具返回结果
1351
- - 当任务完成时,必须在回答末尾添加 <final gen> 标记表示结束
1352
- - 如果需要更多信息,继续调用工具${this.judgmentGateAddition}`;
1353
-
1354
- // 2026-06-15: 把 currentOnStream 传给 loop, 让 step-timeline 在 pivot 循环里也能 emit step_start/done
1355
- // 之前 loop.execute() 不接 streamCallback, 导致 step-timeline 只能看到老 runReActLoop 路径
1356
- // promptWithPivotLoop 路径 0 step events — UI 显示 timeline 但永远是空
1357
- const result = await loop.execute(input, llm, systemPrompt, this.currentOnStream ?? undefined);
1358
-
1359
- this.messageHistory.push({ role: 'user', content: input });
1360
- if (result.response) {
1361
- this.messageHistory.push({ role: 'assistant', content: result.response });
1362
- }
1363
-
1364
- // 回溯 + 清场
1365
- if (this.judgmentGateUsedIds.length > 0) {
1366
- recordJudgmentUsage(this.judgmentGateUsedIds, { userInput: input }).catch((err) =>
1367
- console.warn('[PiAgent] recordJudgmentUsage failed:', err)
1368
- );
1369
- }
1370
- this.clearJudgmentGate();
1371
-
1372
- return result;
1373
- }
1374
-
1375
- private async runReActLoop(onStream?: StreamCallback, signal?: AbortSignal): Promise<{ reply: string; aiFailed: boolean; aiFailureReason?: string }> {
1376
- const llm = getMinimax();
1377
- let iteration = 0;
1378
- let finalResponse = '';
1379
- let lastQualityScore = 0;
1380
- let refineAttempts = 0;
1381
- let consecutiveErrors = 0;
1382
- // 2026-06-16 新增: 累计错误数 (跨工具, 兜底防 LLM 轮换工具名死循环)
1383
- let totalErrors = 0;
1384
- let lastFailedTool = ''; // 跟踪最近一次失败的 tool name
1385
- let lastFailedToolCount = 0; // 最近失败工具的连续失败次数
1386
- // 2026-06-16: AI sentinel 标志 — runReActLoop 返回 aiFailed=true,
1387
- // promptStream 据此自动重跑整个 loop 最多 N 次 (不是单次 LLM 重试)
1388
- let aiFailed = false;
1389
- let aiFailureReason = '';
1390
- const MAX_CONSECUTIVE_ERRORS = 3;
1391
- const MAX_SAME_TOOL_FAILURES = 3; // 同一工具连续失败 3 次, 强制让 LLM 给出最终答案
1392
-
1393
- // 发送循环开始的事件
1394
- if (onStream) {
1395
- onStream({ type: 'status', content: '🔄 开始 ReAct 循环...', tool: 'system' });
1396
- }
1397
-
1398
- // React Harness: 循环开始 (重置 turn 计数 + 触发 harness sessionStart)
1399
- // 失败静默 (fail-open), 不阻塞主循环
1400
- try {
1401
- await this.reactHarness.onSessionStart(this.currentChannelId || undefined);
1402
- } catch (err) {
1403
- console.warn('[PiAgent] reactHarness.onSessionStart failed (non-fatal):', err);
1404
- }
1405
-
1406
- while (iteration < this.MAX_REACT_ITERATIONS) {
1407
- iteration++;
1408
-
1409
- // 停止条件 1: max turns (fail-safe 10000, 正常任务永远跑不到)
1410
- if (iteration >= this.MAX_REACT_ITERATIONS) {
1411
- console.warn(`[PiAgent] 达到最大循环数 ${this.MAX_REACT_ITERATIONS}, 强制终止 (fail-safe)`);
1412
- onStream?.({ type: 'error', content: `⏹️ 达到最大循环数 (${this.MAX_REACT_ITERATIONS}, fail-safe)`, tool: 'loop' });
1413
- finalResponse = finalResponse || '(本轮 ReAct 循环达到最大步数, 强制结束)';
1414
- break;
1415
- }
1416
-
1417
- // 停止条件 2: signal.aborted (显式 abort / 用户中断)
1418
- if (signal?.aborted) {
1419
- console.warn('[PiAgent] runReActLoop aborted by signal');
1420
- onStream?.({ type: 'error', content: '⏹️ 用户中断', tool: 'loop' });
1421
- finalResponse = finalResponse || '(用户中断)';
1422
- break;
1423
- }
1424
-
1425
- // 2026-06-16 新增: 累计错误兜底 — 跨工具, 防 LLM 轮换工具名绕过 MAX_SAME_TOOL_FAILURES
1426
- if (totalErrors >= this.MAX_TOTAL_ERRORS) {
1427
- console.warn(`[PiAgent] 累计错误 ${totalErrors} >= ${this.MAX_TOTAL_ERRORS}, 强制终止 (防死循环)`);
1428
- onStream?.({ type: 'error', content: `⛔ 累计 ${totalErrors} 次错误, 强制终止 (防止 LLM 死循环)`, tool: 'loop' });
1429
- finalResponse = finalResponse || `(本轮 ReAct 循环累计 ${totalErrors} 次错误, 强制结束。请换个思路或简化任务重试。)`;
1430
- break;
1431
- }
1432
-
1433
- // 2026-06-16 新增: loop 内自动压缩 — token 超 80% 阈值时跑一次
1434
- // compact 失败走 C 路径: 不强行 break, 让现有 60K 阈值兜底 (后面有检查)
1435
- const compactThreshold = this.MAX_OUTPUT_TOKEN_ESCALATION_THRESHOLD * this.LOOP_COMPACT_RATIO;
1436
- const estimatedTokensBefore = this.estimateHistoryTokens();
1437
- if (estimatedTokensBefore > compactThreshold) {
1438
- const tokensBeforeCompact = estimatedTokensBefore;
1439
- console.log(`[PiAgent] loop 入口 token ${tokensBeforeCompact} > ${compactThreshold}, 触发自动压缩`);
1440
- onStream?.({ type: 'status', content: `🗜️ loop 自动压缩 (token ${tokensBeforeCompact} > ${compactThreshold})`, tool: 'compactor' });
1441
- try {
1442
- await this.maybeAutoCompact(onStream, signal);
1443
- } catch (compactErr) {
1444
- // C 路径: compact 失败不 break, 让 token 阈值检查兜底
1445
- console.warn(`[PiAgent] loop 内 maybeAutoCompact 失败 (non-fatal, 继续走 token 阈值):`, compactErr);
1446
- }
1447
- }
1448
-
1449
- // 停止条件 3: context overflow (compact 后还超, 强制终止)
1450
- const estimatedTokens = this.estimateHistoryTokens();
1451
- if (estimatedTokens > this.MAX_OUTPUT_TOKEN_ESCALATION_THRESHOLD) {
1452
- console.warn(`[PiAgent] context overflow (${estimatedTokens} tokens > ${this.MAX_OUTPUT_TOKEN_ESCALATION_THRESHOLD})`);
1453
- onStream?.({ type: 'error', content: `⏹️ 上下文溢出 (${estimatedTokens} tokens, 阈值 ${this.MAX_OUTPUT_TOKEN_ESCALATION_THRESHOLD})`, tool: 'loop' });
1454
- finalResponse = finalResponse || '(本轮 ReAct 循环因上下文溢出终止)';
1455
- break;
1456
- }
1457
-
1458
- // 调试日志:显示每次循环开始
1459
- console.log(`[PiAgent] 循环 ${iteration}/${this.MAX_REACT_ITERATIONS} 开始`);
1460
- if (onStream) {
1461
- onStream({ type: 'status', content: `🔄 循环 ${iteration}/${this.MAX_REACT_ITERATIONS}`, tool: 'loop' });
1462
- }
1463
-
1464
- const context = this.buildContext();
1465
- const toolDefs = this.getToolDefinitions();
1466
-
1467
- // 动态构建 refine 上下文
1468
- let refineContext = '';
1469
- if (refineAttempts > 0 && lastQualityScore < this.QUALITY_THRESHOLD) {
1470
- refineContext = `\n【改进提示】上轮结果质量分 ${(lastQualityScore * 10).toFixed(1)}/10,请改进回答。`;
1471
- }
1472
-
1473
- // 连续错误时的额外提示
1474
- if (consecutiveErrors > 0) {
1475
- refineContext += `\n【错误提示】上轮发生 ${consecutiveErrors} 次错误,请重新分析问题或换一种方式处理。`;
1476
- }
1477
-
1478
- const personaSection = this.persona ? `
1479
- 角色描述: ${this.persona.description || '无'}
1480
- 性格特点: ${this.persona.personality || '无'}
1481
- 问候语: ${this.persona.greeting || '无'}
1482
- ` : '';
1483
-
1484
- const systemPrompt = `${this.bootstrapAddition}你是 ${this.identity.name},基于ReAct (Reasoning + Acting)模式工作。${personaSection}
1485
- 当前工作目录: ${this.cwd}
1486
- 当前身份: ${this.identity.name} (${this.identity.did})
1487
- ${refineContext}
1488
-
1489
- ${toolDefs}
1490
-
1491
- 工作模式:
1492
- 1. 理解用户自然语言请求
1493
- 2. 分析需要哪些工具来完成
1494
- 3. 按顺序调用工具并观察结果
1495
- 4. 根据观察结果决定下一步
1496
- 5. 最终给出完整回答
1497
-
1498
- 重要:
1499
- - 每次只调用一个工具
1500
- - 仔细分析工具返回结果
1501
- - 当任务完成时,必须在回答末尾添加 <final gen> 标记表示结束
1502
- - 如果需要更多信息,继续调用工具${this.judgmentGateAddition}`;
1503
-
1504
- // 3 个恢复机制 (Claude Code 论文 9-step pipeline 内部):
1505
- // 1. max output token 升级 (最多 3 次, 每次 maxOutputTokens 翻倍)
1506
- // 2. reactive compaction (prompt 估算超阈值, 跑压缩)
1507
- // 3. prompt-too-long (LLM 报错 4xxx token 错误, 跑 reactive compaction 再试 1 次)
1508
- // 失败静默: 全部重试失败 → 空 reply (上层用 no tool_use 终止)
1509
- const response = await this.callLlmWithRecovery(llm, context, systemPrompt, signal, onStream);
1510
- const reply = (response.reply || '').trim();
1511
-
1512
- // 2026-06-16: 看到 [AI 服务调用失败] sentinel → 不再立即 break,
1513
- // 而是设 aiFailed=true, 让外层 promptStream 自动重跑整个 loop 最多 N 次
1514
- // (LLM API 401 / 网络错 / 配额满时, pi-ai 返回这个 prefix;
1515
- // 自动 retry 兜底: 临时网络抖动可自愈, 真挂 N 次后才报失败)
1516
- if (reply.startsWith('[AI 服务调用失败]')) {
1517
- console.log(`[PiAgent] 收到 AI 错误 sentinel, 标记 aiFailed, 外层会自动重试整个 loop`);
1518
- aiFailed = true;
1519
- aiFailureReason = reply.length > 200 ? reply.substring(0, 200) : reply;
1520
- if (onStream) {
1521
- onStream({ type: 'status', content: `⚠️ AI 调用失败, 将自动重试整个 loop`, tool: 'system' });
1522
- }
1523
- break;
1524
- }
1525
-
1526
- console.log(`[PiAgent] LLM 回复长度: ${reply.length}, 内容预览: "${reply.substring(0, 80)}..."`);
1527
- console.log(`[PiAgent] LLM 完整回复:\n${reply}`);
1528
-
1529
- // 通知前端:收到 LLM 回复
1530
- if (onStream) {
1531
- onStream({ type: 'token', content: reply.substring(0, 100) });
1532
- }
1533
-
1534
- if (this.isFinalResponse(reply)) {
1535
- // 检查质量分数
1536
- lastQualityScore = this.estimateResponseQuality(reply);
1537
-
1538
- // 如果质量太低且还有改进机会,进入改进循环
1539
- if (lastQualityScore < this.QUALITY_THRESHOLD && refineAttempts < this.MAX_REFINE_ATTEMPTS) {
1540
- refineAttempts++;
1541
- console.log(`[PiAgent] 质量评分 ${(lastQualityScore * 10).toFixed(1)}/10 < ${(this.QUALITY_THRESHOLD * 10).toFixed(1)}/10,自动改进中 (${refineAttempts}/${this.MAX_REFINE_ATTEMPTS})`);
1542
- continue;
1543
- }
1544
-
1545
- finalResponse = this.extractFinalAnswer(reply);
1546
- break;
1547
- }
1548
-
1549
- const toolCall = this.parseToolCall(reply);
1550
- if (toolCall) {
1551
- this.messageHistory.push({
1552
- role: 'assistant',
1553
- content: reply,
1554
- toolCall
1555
- });
1556
-
1557
- // 通知前端:检测到工具调用
1558
- if (onStream) {
1559
- onStream({ type: 'tool', content: `🔧 调用工具: ${toolCall.name}`, tool: toolCall.name });
1560
- if (toolCall.args && Object.keys(toolCall.args).length > 0) {
1561
- onStream({ type: 'status', content: `📋 参数: ${JSON.stringify(toolCall.args)}`, tool: toolCall.name });
1562
- }
1563
- // 2026-06-15: step-timeline 状态机 — 开新节点
1564
- onStream({
1565
- type: 'step_start',
1566
- content: `调用 ${toolCall.name}`,
1567
- tool: toolCall.name,
1568
- args: toolCall.args || {},
1569
- });
1570
- }
1571
-
1572
- const tool = this.tools.get(toolCall.name);
1573
- if (!tool) {
1574
- consecutiveErrors++;
1575
- // 2026-06-16 新增: 未知工具也要累计 (LLM 幻觉高频场景)
1576
- totalErrors++;
1577
- const errorResult: ToolResult = { success: false, error: `未知工具: ${toolCall.name}` };
1578
- this.messageHistory.push({ role: 'tool', content: JSON.stringify(errorResult), toolResult: errorResult });
1579
- this.logToHarness(toolCall.name, toolCall.args, errorResult);
1580
- console.warn(`[PiAgent] 未知工具: ${toolCall.name} (累计 ${totalErrors}/${this.MAX_TOTAL_ERRORS}),跳过并继续`);
1581
- continue;
1582
- }
1583
-
1584
- // Bootstrap PreToolUse hook: 调工具前校验 (危险命令拦截)
1585
- // 失败静默 — hook 自身挂掉 = 放行
1586
- // P2: 透传 permissionMode (从 BootstrapOptions / env BOLLOON_PERM_MODE 解析)
1587
- let toolToExecute = tool;
1588
- try {
1589
- const pre = await onPreToolUse({
1590
- tool: toolCall.name,
1591
- args: toolCall.args || {},
1592
- permissionMode: this.currentPermissionMode,
1593
- });
1594
- if (!pre.allowed) {
1595
- const deniedResult: ToolResult = {
1596
- success: false,
1597
- error: `PreToolUse 拒绝: ${pre.reason || '未通过安全校验'}`,
1598
- };
1599
- this.messageHistory.push({
1600
- role: 'tool',
1601
- content: JSON.stringify(deniedResult),
1602
- toolResult: deniedResult,
1603
- });
1604
- this.logToHarness(toolCall.name, toolCall.args, deniedResult);
1605
- if (onStream) {
1606
- onStream({
1607
- type: 'error',
1608
- content: `🛡️ PreToolUse 拒绝 ${toolCall.name}: ${pre.reason || '安全校验失败'}`,
1609
- tool: toolCall.name,
1610
- });
1611
- // 2026-06-15: step-timeline — 拦在 PreToolUse, 标 step_error
1612
- onStream({
1613
- type: 'step_error',
1614
- content: `PreToolUse 拒绝 ${toolCall.name}`,
1615
- tool: toolCall.name,
1616
- error: pre.reason || '安全校验失败',
1617
- });
1618
- }
1619
- console.warn(`[PiAgent] PreToolUse denied ${toolCall.name}: ${pre.reason}`);
1620
- // 不调 tool.execute, 也不计 consecutiveErrors (这是用户级拒绝, 不是工具错)
1621
- continue;
1622
- }
1623
- } catch (err) {
1624
- console.warn('[PiAgent] onPreToolUse failed (non-fatal, allowing):', err);
1625
- }
1626
-
1627
- // React Harness: 8-gate + builtin-guards 校验 (在 PreToolUse 之后, 串接双层)
1628
- // 失败静默, 拒绝时不调 tool.execute
1629
- try {
1630
- const pre = await this.reactHarness.preToolCall(
1631
- toolCall.name,
1632
- toolCall.args || {},
1633
- this.currentChannelId || undefined
1634
- );
1635
- if (!pre.allowed) {
1636
- const deniedResult: ToolResult = {
1637
- success: false,
1638
- error: `Harness gate 拒绝 (${pre.details.rejectedBy}): ${pre.reason || '未通过安全校验'}`,
1639
- };
1640
- this.messageHistory.push({
1641
- role: 'tool',
1642
- content: JSON.stringify(deniedResult),
1643
- toolResult: deniedResult,
1644
- });
1645
- this.logToHarness(toolCall.name, toolCall.args, deniedResult);
1646
- if (onStream) {
1647
- onStream({
1648
- type: 'error',
1649
- content: `🛡️ Harness ${pre.details.rejectedBy} 拒绝 ${toolCall.name}: ${pre.reason || '安全校验失败'}`,
1650
- tool: toolCall.name,
1651
- });
1652
- // 2026-06-15: step-timeline — Harness gate 拒绝, 标 step_error
1653
- onStream({
1654
- type: 'step_error',
1655
- content: `Harness 拒绝 ${toolCall.name}`,
1656
- tool: toolCall.name,
1657
- error: pre.reason || '安全校验失败',
1658
- });
1659
- }
1660
- console.warn(`[PiAgent] Harness denied ${toolCall.name} (${pre.details.rejectedBy}): ${pre.reason}`);
1661
- continue;
1662
- }
1663
- } catch (err) {
1664
- console.warn('[PiAgent] reactHarness.preToolCall failed (non-fatal, allowing):', err);
1665
- }
1666
-
1667
- try {
1668
- const toolStart = Date.now();
1669
- let result = await tool.execute(toolCall.args);
1670
- const toolDurationMs = Date.now() - toolStart;
1671
- console.log(`[PiAgent] 工具 ${toolCall.name} 执行完成: success=${result.success} (${toolDurationMs}ms)`);
1672
-
1673
- // PostToolUse 审计 hook: 写 audit log, 默认 continue
1674
- try {
1675
- await onPostToolUse({
1676
- tool: toolCall.name,
1677
- args: toolCall.args || {},
1678
- result: {
1679
- success: result.success,
1680
- output: result.output?.substring(0, 500),
1681
- error: result.error,
1682
- },
1683
- durationMs: toolDurationMs,
1684
- });
1685
- } catch (postErr) {
1686
- console.warn('[PiAgent] onPostToolUse failed (non-fatal):', postErr);
1687
- }
1688
-
1689
- // Context router: 拿最近一次 preToolCall 算的 hint, 拼到 tool result messageHistory
1690
- // (LLM 下次看到 tool result 时, 能"记得"这次调用的安全约束)
1691
- const routeHint = this.reactHarness.getLastRouteHint();
1692
- if (routeHint && routeHint.systemAddition) {
1693
- this.messageHistory.push({
1694
- role: 'system',
1695
- content: `[Harness Router Hint: ${routeHint.reason}]\n${routeHint.systemAddition}`,
1696
- });
1697
- this.reactHarness.clearRouteHint();
1698
- }
1699
-
1700
- // React Harness: post-tool call (output 审计: secret leak 等)
1701
- // 拒绝时 result.output 含敏感 → 替换为 generic message, 不污染 messageHistory
1702
- try {
1703
- const post = await this.reactHarness.postToolCall(
1704
- toolCall.name,
1705
- String(result.output || ''),
1706
- this.currentChannelId || undefined
1707
- );
1708
- if (!post.allowed) {
1709
- if (onStream) {
1710
- onStream({
1711
- type: 'error',
1712
- content: `🛡️ Harness output 拒绝 ${toolCall.name}: ${post.reason || '输出含敏感信息'}`,
1713
- tool: toolCall.name,
1714
- });
1715
- }
1716
- console.warn(`[PiAgent] Harness output denied ${toolCall.name}: ${post.reason}`);
1717
- // 替换 result: success 仍保留 (tool 本身没错), 但 output 改成 generic
1718
- // 这样 LLM 下轮看 output 不会拿到秘密, 但 success 标志让它知道 "工具执行了"
1719
- result = {
1720
- ...result,
1721
- output: `[harness output gate: output 含敏感内容, 已屏蔽. 原因: ${post.reason || 'unknown'}]`,
1722
- _harnessDenied: true,
1723
- } as typeof result;
1724
- }
1725
- } catch (err) {
1726
- console.warn('[PiAgent] reactHarness.postToolCall failed (non-fatal, allowing):', err);
1727
- }
1728
-
1729
- this.messageHistory.push({ role: 'tool', content: JSON.stringify(result), toolResult: result });
1730
- this.logToHarness(toolCall.name, toolCall.args, result);
1731
-
1732
- // 通知前端工具执行结果
1733
- if (onStream) {
1734
- if (result.success) {
1735
- onStream({ type: 'status', content: `✅ ${toolCall.name} 执行成功`, tool: toolCall.name });
1736
- if (result.output) {
1737
- const outputPreview = result.output.substring(0, 200);
1738
- onStream({ type: 'tool', content: `📤 结果: ${outputPreview}${result.output.length > 200 ? '...' : ''}`, tool: toolCall.name });
1739
- }
1740
- // 2026-06-15: step-timeline 状态机 — 关闭当前节点 (成功)
1741
- onStream({
1742
- type: 'step_done',
1743
- content: `${toolCall.name} 执行成功`,
1744
- tool: toolCall.name,
1745
- success: true,
1746
- output: result.output,
1747
- });
1748
- } else {
1749
- onStream({ type: 'error', content: `❌ ${toolCall.name} 执行失败: ${result.error}`, tool: toolCall.name });
1750
- // 2026-06-15: step-timeline 状态机 — 关闭当前节点 (失败)
1751
- onStream({
1752
- type: 'step_error',
1753
- content: `${toolCall.name} 执行失败`,
1754
- tool: toolCall.name,
1755
- error: result.error,
1756
- });
1757
- }
1758
- }
1759
-
1760
- if (result.success) {
1761
- consecutiveErrors = 0; // 重置连续错误计数
1762
-
1763
- // 检查工具执行质量
1764
- lastQualityScore = this.estimateToolResultQuality(result);
1765
- if (lastQualityScore < this.QUALITY_THRESHOLD && refineAttempts < this.MAX_REFINE_ATTEMPTS) {
1766
- refineAttempts++;
1767
- console.log(`[PiAgent] 工具结果质量低,自动重试 (${refineAttempts}/${this.MAX_REFINE_ATTEMPTS})`);
1768
- } else {
1769
- console.log(`[PiAgent] 工具执行成功,质量评分: ${(lastQualityScore * 10).toFixed(1)}/10`);
1770
- }
1771
-
1772
- // 工具执行成功后,继续循环获取下一个 LLM 响应
1773
- if (onStream) {
1774
- onStream({ type: 'status', content: `🔄 工具执行完成,继续循环...`, tool: 'loop' });
1775
- }
1776
- // 不 break,继续下一次循环
1777
- } else {
1778
- consecutiveErrors++;
1779
- // 2026-06-16 新增: 累计错误 (跨工具, 兜底防 LLM 轮换工具名死循环)
1780
- totalErrors++;
1781
- // 跟踪同一工具连续失败次数
1782
- if (toolCall.name === lastFailedTool) {
1783
- lastFailedToolCount++;
1784
- } else {
1785
- lastFailedTool = toolCall.name;
1786
- lastFailedToolCount = 1;
1787
- }
1788
- console.warn(`[PiAgent] 工具 ${toolCall.name} 执行失败 (${lastFailedToolCount}/${MAX_SAME_TOOL_FAILURES}, 累计 ${totalErrors}/${this.MAX_TOTAL_ERRORS}): ${result.error}`);
1789
-
1790
- // 同一工具连续失败达到上限, 不再重试, 强制 LLM 给出最终答案
1791
- if (lastFailedToolCount >= MAX_SAME_TOOL_FAILURES) {
1792
- console.log(`[PiAgent] 工具 ${toolCall.name} 连续 ${MAX_SAME_TOOL_FAILURES} 次失败, 放弃并要求直接回答`);
1793
- this.messageHistory.push({
1794
- role: 'system',
1795
- content: `[注意] 工具 ${toolCall.name} 在这个上下文中不可用 (连续 ${MAX_SAME_TOOL_FAILURES} 次失败: ${result.error}). 请不要再次调用它, 直接用你已知的信息回答用户, 并在回答开头标记 <final gen>.`
1796
- });
1797
- lastFailedTool = '';
1798
- lastFailedToolCount = 0;
1799
- consecutiveErrors = 0;
1800
- continue; // 让 LLM 看到系统提示后再决定
1801
- }
1802
-
1803
- // 连续错误达到上限(混合不同工具), 尝试换一种方式
1804
- if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
1805
- console.log(`[PiAgent] 连续 ${MAX_CONSECUTIVE_ERRORS} 次错误,尝试换一种方式处理`);
1806
- this.messageHistory.push({
1807
- role: 'system',
1808
- content: `[注意] 前面的工具调用连续失败。请尝试其他工具或换一种方式完成用户请求, 或用 <final gen> 给出最终回答.`
1809
- });
1810
- consecutiveErrors = 0;
1811
- }
1812
- }
1813
- } catch (execError) {
1814
- consecutiveErrors++;
1815
- // 2026-06-16 新增: 异常分支也要累计
1816
- totalErrors++;
1817
- const errorResult: ToolResult = { success: false, error: String(execError) };
1818
- this.messageHistory.push({ role: 'tool', content: JSON.stringify(errorResult), toolResult: errorResult });
1819
- this.logToHarness(toolCall.name, toolCall.args, errorResult);
1820
- console.error(`[PiAgent] 工具执行异常 (累计 ${totalErrors}/${this.MAX_TOTAL_ERRORS}): ${execError}`);
1821
- }
1822
- } else {
1823
- // LLM 返回的不是 tool call 格式
1824
- this.messageHistory.push({
1825
- role: 'assistant',
1826
- content: reply
1827
- });
1828
-
1829
- // 通知前端收到非工具调用回复
1830
- if (onStream) {
1831
- onStream({ type: 'token', content: reply.substring(0, 150) });
1832
- }
1833
-
1834
- // 检查是否需要继续循环处理
1835
- // 更严格的判断:只有当回复明确表示需要更多信息时才继续
1836
- const containsToolCallIntent = reply.includes('调用工具') || reply.includes('tool(') ||
1837
- reply.includes('使用工具') || reply.includes('需要获取') || reply.includes('需要查看') ||
1838
- // 兼容 LLM 用对象字面量输出 tool call (上轮没解析成功时, 至少要继续)
1839
- reply.includes('tool =>') || reply.includes('[TOOL_CALL]') ||
1840
- // 2026-06-15 修: 兼容 LLM 用 XML 标签输出 tool call (<shell_exec>...</shell_exec>)
1841
- // 这时 parseToolCall 失败, 至少要让 loop 继续
1842
- /<\w+>[\s\S]*?<\/\w+>/.test(reply);
1843
- const hasError = ['不存在', '找不到', '无法找到', 'not found', 'does not exist',
1844
- '错误', 'error', '失败', 'failed'].some(k => reply.includes(k));
1845
- const isTooShort = reply.length < 50 && reply.length > 0;
1846
- const hasQuestion = reply.includes('?') && (reply.includes('怎么') || reply.includes('如何') || reply.includes('什么'));
1847
-
1848
- const needsMoreWork = hasError || containsToolCallIntent || isTooShort || hasQuestion;
1849
-
1850
- if (needsMoreWork && iteration < this.MAX_REACT_ITERATIONS) {
1851
- console.log(`[PiAgent] 继续循环处理 (${iteration}/${this.MAX_REACT_ITERATIONS}): needsMoreWork=${needsMoreWork}, hasError=${hasError}, containsToolCallIntent=${containsToolCallIntent}`);
1852
- if (onStream) {
1853
- onStream({ type: 'status', content: `🔄 继续处理,循环 ${iteration}...`, tool: 'loop' });
1854
- }
1855
- continue;
1856
- }
1857
-
1858
- // 否则把这个当作可能的最终回答
1859
- finalResponse = reply;
1860
- if (onStream) {
1861
- onStream({ type: 'status', content: `📝 提取最终回答,长度 ${reply.length}`, tool: 'system' });
1862
- }
1863
- break;
1864
- }
1865
- }
1866
-
1867
- if (!finalResponse) {
1868
- // 走到这里通常是 LLM 一直在调同一个不存在的工具, 没输出 <final gen>
1869
- // 把已知的失败信息也带回去, 让用户知道发生了什么
1870
- const reason = lastFailedTool
1871
- ? `(工具 ${lastFailedTool} 连续 ${MAX_SAME_TOOL_FAILURES} 次失败, 已放弃)`
1872
- : `(共 ${iteration - 1} 轮无最终输出)`;
1873
- finalResponse = `抱歉,任务未能完成 ${reason}。请换个方式提问,或明确告诉 agent 不要调用工具。`;
1874
- if (onStream) {
1875
- onStream({ type: 'error', content: `⚠️ 任务未完成: ${reason}`, tool: 'system' });
1876
- }
1877
- }
1878
-
1879
- // 通知前端循环完成
1880
- if (onStream) {
1881
- onStream({ type: 'status', content: `✅ 处理完成,共 ${iteration - 1} 次循环`, tool: 'system' });
1882
- }
1883
-
1884
- const now = new Date().toISOString();
1885
- const identityPrefix = `${this.identity.name} | bolloon 智能体
1886
- <environment_details>
1887
- Current time: ${now}
1888
- Working directory: ${this.cwd}
1889
- Workspace root folder: ${this.cwd}
1890
- </environment_details>
1891
- `;
1892
- finalResponse = identityPrefix + finalResponse;
1893
-
1894
- this.messageHistory.push({ role: 'assistant', content: finalResponse });
1895
-
1896
- // React Harness: 循环结束
1897
- try {
1898
- await this.reactHarness.onSessionEnd();
1899
- } catch (err) {
1900
- console.warn('[PiAgent] reactHarness.onSessionEnd failed (non-fatal):', err);
1901
- }
1902
-
1903
- // 2026-06-16: 暴露 aiFailed 标志 — promptStream 据此决定是否自动重试整个 loop
1904
- return { reply: finalResponse, aiFailed, aiFailureReason: aiFailureReason || undefined };
1905
- }
1906
-
1907
- async deepThink(prompt: string): Promise<{ result: ThinkResult; response: string }> {
1908
- const result = await this.thinkingEngine.think(prompt);
1909
- let response = `深度思考完成(${result.depth}层):\n\n`;
1910
- for (const step of result.steps) {
1911
- response += `第${step.step}步: ${step.thought}\n`;
1912
- if (step.reflection) {
1913
- response += ` 反思: ${step.reflection}\n`;
1914
- }
1915
- if (step.improvement) {
1916
- response += ` 改进: ${step.improvement}\n`;
1917
- }
1918
- response += '\n';
1919
- }
1920
- response += `最终输出: ${result.finalOutput}`;
1921
- return { result, response };
1922
- }
1923
-
1924
- async processDocumentsInParallel(
1925
- paths: string[],
1926
- operation: 'summarize' | 'improve',
1927
- requirements?: string
1928
- ): Promise<{ outputs: string[]; success: boolean }> {
1929
- if (paths.length === 0) {
1930
- return { outputs: [], success: true };
1931
- }
1932
-
1933
- const subtasks = paths.map((filePath, index) => ({
1934
- id: `doc-${index}`,
1935
- description: `${operation}:${filePath}${requirements ? `:${requirements}` : ''}`,
1936
- priority: index
1937
- }));
1938
-
1939
- const dispatchPrompts = subtasks.map(t => t.description);
1940
- const results = await this.coordinator.dispatch(dispatchPrompts.join(' ||| '), paths.length);
1941
-
1942
- const outputs: string[] = [];
1943
- let allSuccess = true;
1944
-
1945
- for (let i = 0; i < paths.length; i++) {
1946
- const result = results.find((r: AgentResult) => r.taskId === `task-${i}`);
1947
- if (result) {
1948
- outputs.push(result.output);
1949
- if (!result.success) allSuccess = false;
1950
- } else {
1951
- outputs.push(`No result for ${paths[i]}`);
1952
- allSuccess = false;
1953
- }
1954
- }
1955
-
1956
- return { outputs, success: allSuccess };
1957
- }
1958
-
1959
- private buildContext(): string {
1960
- // P1 接入: 同步跑前 3 层压缩 (Budget Reduction / Snip / Microcompact)
1961
- // 异步层 (Context Collapse / Auto-Compact) 在 promptStream 入口处单独跑 (用 LLM)
1962
- // 失败静默: 任何 stage 抛错 → 走老 slice(-10) 逻辑
1963
- //
1964
- // P1.2: 如果 maybeAutoCompact 算过 Context Collapse 投影, 用 this.projectedHistory (读时投影, 非破坏)
1965
- const source = this.projectedHistory ?? this.messageHistory;
1966
- const recentMessages = this.compressHistorySync(source).slice(-10);
1967
- return recentMessages.map(m => {
1968
- if (m.role === 'user') return `用户: ${m.content}`;
1969
- if (m.role === 'assistant') return `助手: ${m.content}`;
1970
- if (m.role === 'tool') {
1971
- const result = m.toolResult ? JSON.stringify(m.toolResult) : m.content;
1972
- return `工具结果: ${result}`;
1973
- }
1974
- return m.content;
1975
- }).join('\n');
1976
- }
1977
-
1978
- /**
1979
- * 估算 messageHistory 的 token 数 (4 字符 ≈ 1 token, 与 context-compaction 同步).
1980
- * 失败静默: 任何异常 → 0 (不阻塞)
1981
- */
1982
- private estimateHistoryTokens(): number {
1983
- try {
1984
- const { estimateTokens } = require('../context-compaction/index.js') as typeof import('../context-compaction/index.js');
1985
- return estimateTokens(this.messageHistory as any);
1986
- } catch {
1987
- return 0;
1988
- }
1989
- }
1990
-
1991
- /**
1992
- * 3 个恢复机制合一:
1993
- * 1. max output token 升级: 最多 3 次, 每次 maxOutputTokens 翻倍 (如果 llm.chat 支持)
1994
- * 2. reactive compaction: 估算 > 80% 阈值, 跑 sync compressHistorySync + 必要时 maybeAutoCompact
1995
- * 3. prompt-too-long: LLM 报错 4xxx token 错误, 跑 reactive compaction 再试 1 次
1996
- *
1997
- * 失败静默: 全部失败 → 返回空 reply, 让上层 no-tool_use 终止
1998
- */
1999
- private async callLlmWithRecovery(
2000
- llm: any,
2001
- context: string,
2002
- systemPrompt: string,
2003
- signal: AbortSignal | undefined,
2004
- onStream?: (chunk: any) => void
2005
- ): Promise<{ reply: string }> {
2006
- // Reactive compaction 预检: 估算 token 超 80% 阈值, 跑一次
2007
- const estimated = this.estimateHistoryTokens();
2008
- if (estimated > this.MAX_OUTPUT_TOKEN_ESCALATION_THRESHOLD * 0.8) {
2009
- console.warn(`[PiAgent] reactive compaction pre-check (${estimated} tokens > 80% threshold)`);
2010
- onStream?.({ type: 'status', content: '⚠️ reactive compaction 预检触发', tool: 'recovery' });
2011
- try {
2012
- const compacted = this.compressHistorySync(this.messageHistory);
2013
- this.messageHistory = compacted;
2014
- if (this.estimateHistoryTokens() > this.MAX_OUTPUT_TOKEN_ESCALATION_THRESHOLD * 0.8) {
2015
- await this.maybeAutoCompact(onStream, signal);
2016
- }
2017
- } catch (err) {
2018
- console.warn('[PiAgent] reactive compaction pre-check failed:', err);
2019
- }
2020
- }
2021
-
2022
- // 主调用 + 3 个恢复路径
2023
- let lastErr: any = null;
2024
- for (let attempt = 0; attempt <= this.MAX_OUTPUT_TOKEN_ESCALATION_RETRIES; attempt++) {
2025
- try {
2026
- const response = await llm.chat(context, systemPrompt, signal);
2027
- return { reply: response.reply || '' };
2028
- } catch (err: any) {
2029
- lastErr = err;
2030
- const errMsg = String(err?.message || err || '');
2031
- const isPromptTooLong = /token|too long|exceed|length|context|4000|413|429/i.test(errMsg);
2032
- if (isPromptTooLong) {
2033
- console.warn(`[PiAgent] prompt-too-long 触发 (attempt ${attempt + 1}), 跑 reactive compaction`);
2034
- onStream?.({ type: 'status', content: `⚠️ prompt-too-long 触发 (attempt ${attempt + 1}/${this.MAX_OUTPUT_TOKEN_ESCALATION_RETRIES + 1})`, tool: 'recovery' });
2035
- try {
2036
- await this.maybeAutoCompact(onStream, signal);
2037
- } catch (compactionErr) {
2038
- console.warn('[PiAgent] reactive compaction on prompt-too-long failed:', compactionErr);
2039
- }
2040
- // 重新生成 context (compressHistorySync + projected)
2041
- context = this.buildContext();
2042
- } else {
2043
- // 非 prompt-too-long 错误, 1 次重试就放弃
2044
- if (attempt === 0) {
2045
- console.warn(`[PiAgent] LLM 调用失败 (non-prompt-too-long), 1 次重试:`, err);
2046
- continue;
2047
- }
2048
- break;
2049
- }
2050
- }
2051
- }
2052
- console.warn('[PiAgent] callLlmWithRecovery 全失败 (silent):', lastErr);
2053
- return { reply: '' };
2054
- }
2055
-
2056
- /**
2057
- * 同步压缩: 跑前 3 层 (Budget Reduction / Snip / Microcompact).
2058
- * Context Collapse / Auto-Compact 是 async, 不在 buildContext 同步链里跑.
2059
- * 失败静默: 任何 stage 抛错 → 返回原 history.
2060
- */
2061
- private compressHistorySync(history: Message[]): Message[] {
2062
- try {
2063
- // context-compaction 的 Message 与 pi-sdk 的 Message 字段兼容
2064
- // 这里用 any cast 跳过 structural type 严格校验 (避免双向 import)
2065
- let h: any = history;
2066
- const r1 = budgetReduce(h);
2067
- h = r1.history;
2068
- const r2 = snip(h);
2069
- h = r2.history;
2070
- const r3 = microcompact(h);
2071
- h = r3.history;
2072
- return h as Message[];
2073
- } catch (err) {
2074
- console.warn('[PiAgent] compressHistorySync failed (silent, using original):', err);
2075
- return history;
2076
- }
2077
- }
2078
-
2079
- /**
2080
- * P1.1: 异步跑 Auto-Compact (LLM 摘要).
2081
- * 入口: promptStream 入口, 在 computeJudgmentGate 之后, onSessionStart 之前.
2082
- *
2083
- * 逻辑:
2084
- * 1. 跑完整 compactPipeline (5 层, 异步)
2085
- * 2. 第 5 层 (Auto-Compact) 需要 LLM, 通过 getMinimax().chat 注入
2086
- * 3. 如果 budgetGate 不超限, 5 层短路在前 3 层, 不会调 LLM → 零开销
2087
- * 4. 失败静默: 任何异常 → console.warn + 保留原 messageHistory
2088
- *
2089
- * onStream 广播: 跟 computeJudgmentGate 风格一致 (phase 事件供 UI timeline 显示)
2090
- */
2091
- private async maybeAutoCompact(
2092
- onStream?: (chunk: any) => void,
2093
- signal?: AbortSignal
2094
- ): Promise<void> {
2095
- if (this.messageHistory.length < 10) return; // 历史太短, 不值得压
2096
-
2097
- onStream?.({ type: 'status', content: '🗜️ 评估是否需要压缩上下文...', tool: 'compactor' });
2098
-
2099
- // 注入 LLM (用 getMinimax().chat, 与 judgment 注入门 / ReAct 循环同一来源)
2100
- // 给 Context Collapse (虚拟投影) 和 Auto-Compact (摘要) 共用
2101
- const llm = getMinimax();
2102
- const llmChat = async (systemPrompt: string, userPrompt: string): Promise<string> => {
2103
- const r = await llm.chat(userPrompt, systemPrompt, signal);
2104
- return r.reply;
2105
- };
2106
-
2107
- const { compactPipeline, isContextCollapseEnabled } = await import('../context-compaction/index.js');
2108
- const result = await compactPipeline(this.messageHistory as any, {
2109
- maxTokens: 8000,
2110
- llmChat,
2111
- collapseLlmChat: llmChat, // P1.2: Context Collapse 投影也用同一 LLM
2112
- cacheScope: this.currentChannelId || 'default',
2113
- });
2114
-
2115
- if (result.compacted && result.history.length < this.messageHistory.length) {
2116
- const saved = this.messageHistory.length - result.history.length;
2117
- const stagesApplied = result.stages.filter((s) => s.applied).map((s) => s.stage).join(' → ');
2118
- onStream?.({
2119
- type: 'status',
2120
- content: `🗜️ 上下文压缩: ${stagesApplied || 'no-op'} | 节省 ${saved} 条 (剩余 ${result.history.length}, collapse=${isContextCollapseEnabled() ? 'on' : 'off'})`,
2121
- tool: 'compactor',
2122
- });
2123
- // 关键: 第 4 层 (Context Collapse) 是读时投影 (非破坏)
2124
- // 第 5 层 (Auto-Compact) 是破坏性折叠
2125
- // 这里用 if-else 区分: collapse on → 仅 buildContext 用; collapse off → 真更新
2126
- if (isContextCollapseEnabled()) {
2127
- this.projectedHistory = result.history as Message[]; // buildContext 用
2128
- // messageHistory 不变 (非破坏)
2129
- } else {
2130
- this.messageHistory = result.history as Message[]; // 真破坏性更新
2131
- this.projectedHistory = null;
2132
- }
2133
- }
2134
- }
2135
-
2136
- private isFinalResponse(content: string): boolean {
2137
- // 只有明确输出 <final gen> 才认为是最终回答
2138
- return content.includes('<final gen>');
2139
- }
2140
-
2141
- private extractFinalAnswer(content: string): string {
2142
- // 提取 <final gen> 后的内容作为最终回答
2143
- const marker = '<final gen>';
2144
- const markerIndex = content.indexOf(marker);
2145
- if (markerIndex !== -1) {
2146
- const after = content.substring(markerIndex + marker.length).trim();
2147
- // v3 修复: 如果 <final gen> 之后是空, fallback 用 marker 之前的内容 (去掉 marker)
2148
- // 否则 LLM 写了 <final gen> 在末尾时, 用户看到空回复 + error
2149
- if (after) {
2150
- content = after;
2151
- } else {
2152
- content = content.substring(0, markerIndex).trim();
2153
- }
2154
- }
2155
- // 移除任何 tool call 标记
2156
- let cleaned = content
2157
- .replace(/调用工具[::]\s*\w+\s*\([^)]*\)/g, '')
2158
- .replace(/使用工具[::]\s*\w+\s*\([^)]*\)/g, '')
2159
- .replace(/tool[_\w]*[::]\s*\w+\s*\([^)]*\)/gi, '')
2160
- .trim();
2161
- return cleaned;
2162
- }
2163
-
2164
- private parseToolCall(content: string): { name: string; args: Record<string, string> } | null {
2165
- const patterns = [
2166
- /调用工具[::]\s*(\w+)\s*\(([^)]*)\)/,
2167
- /使用工具[::]\s*(\w+)\s*\(([^)]*)\)/,
2168
- /tool[_\w]*[::]\s*(\w+)\s*\(([^)]*)\)/i,
2169
- /(\w+)\s*\(\s*([^)]*)\s*\)/,
2170
- // 兼容 LLM 输出的对象字面量格式: {tool => "get_identity", args => {...}}
2171
- /\{\s*tool\s*=>\s*["'](\w+)["']\s*(?:,\s*args\s*=>\s*(\{[\s\S]*?\}))?\s*\}/,
2172
- // 兼容: tool => "get_identity" (无 args 包裹)
2173
- /\btool\s*=>\s*["'](\w+)["']/,
2174
- // 兼容: [TOOL_CALL] 块内 JSON 形式 {"name": "x", "args": {...}}
2175
- /\[TOOL_CALL\][\s\S]*?\{\s*"name"\s*:\s*"(\w+)"\s*,\s*"args"\s*:\s*(\{[\s\S]*?\})/i,
2176
- // 2026-06-15 修: 兼容 LLM 输出的 XML 格式 <tool_name>...<arg>value</arg>...</tool_name>
2177
- // 实际 LLM 习惯: <shell_exec>\n<command>ls</command>\n<args>["-la", "..."]</args>\n</shell_exec>
2178
- /<(\w+)>([\s\S]*?)<\/\1>/,
2179
- ];
2180
-
2181
- for (const pattern of patterns) {
2182
- const match = content.match(pattern);
2183
- if (match) {
2184
- const name = match[1];
2185
- let args: Record<string, string> = {};
2186
- const rawArgs = match[2] || '';
2187
-
2188
- if (rawArgs && rawArgs.trim().startsWith('{')) {
2189
- // JSON 形式, 尝试解析
2190
- try {
2191
- const parsed = JSON.parse(rawArgs);
2192
- if (parsed && typeof parsed === 'object') {
2193
- args = Object.fromEntries(Object.entries(parsed).map(([k, v]) => [k, String(v)]));
2194
- }
2195
- } catch {
2196
- // 解析失败就当字符串处理
2197
- const argPairs = rawArgs.split(',').map(s => s.trim()).filter(Boolean);
2198
- for (const pair of argPairs) {
2199
- const [key, ...valueParts] = pair.split(':').map(s => s.trim().replace(/['"]/g, ''));
2200
- if (key) args[key] = valueParts.join(':') || '';
2201
- }
2202
- }
2203
- } else if (rawArgs && /<\w+>[\s\S]*<\/\w+>/.test(rawArgs)) {
2204
- // 2026-06-15 修: XML 格式, 解析内嵌子标签 <argname>value</argname>
2205
- // 例: <command>ls</command>\n<args>["-la","~/.bolloon/skills"]</args>
2206
- const xmlArgPattern = /<(\w+)>([\s\S]*?)<\/\1>/g;
2207
- let xmlMatch: RegExpExecArray | null;
2208
- while ((xmlMatch = xmlArgPattern.exec(rawArgs)) !== null) {
2209
- const argName = xmlMatch[1];
2210
- const argValue = xmlMatch[2].trim();
2211
- if (argName && argValue) {
2212
- args[argName] = argValue;
2213
- }
2214
- }
2215
- } else if (rawArgs) {
2216
- // 形参串, 形如 key: value, key2: value2
2217
- const argPairs = rawArgs.split(',').map(s => s.trim()).filter(Boolean);
2218
- for (const pair of argPairs) {
2219
- const [key, ...valueParts] = pair.split(':').map(s => s.trim().replace(/['"]/g, ''));
2220
- if (key) args[key] = valueParts.join(':') || '';
2221
- }
2222
- }
2223
-
2224
- if (this.tools.has(name) || this.tools.has(name.replace(/_/g, '_'))) {
2225
- return { name, args };
2226
- }
2227
- }
2228
- }
2229
- return null;
2230
- }
2231
-
2232
- private estimateResponseQuality(response: string): number {
2233
- let score = 0.5;
2234
- if (response.length > 50) score += 0.1;
2235
- if (response.length > 200) score += 0.1;
2236
- if (response.length < 20) score -= 0.3;
2237
- if (response.includes('\n')) score += 0.1;
2238
- if (response.includes('-') || response.includes('•')) score += 0.05;
2239
- if (response.includes('```')) score += 0.1;
2240
- const conclusionWords = ['完成', '结果', '总结', '所以', '因此', '答案', '推荐'];
2241
- if (conclusionWords.some(w => response.includes(w))) score += 0.1;
2242
- if (response.includes('调用工具') || response.includes('tool(')) score -= 0.2;
2243
- return Math.max(0, Math.min(1, score));
2244
- }
2245
-
2246
- private estimateToolResultQuality(result: ToolResult): number {
2247
- let score = 0.5;
2248
- if (!result.success) return 0.2;
2249
- if (result.output) {
2250
- score += 0.2;
2251
- if (result.output.length > 50) score += 0.1;
2252
- if (result.output.length < 10) score -= 0.1;
2253
- if (result.output.includes('❌') || result.output.includes('error')) score -= 0.2;
2254
- if (result.output.includes('✅') || result.output.includes('success')) score += 0.1;
2255
- }
2256
- if (result.error) score -= 0.3;
2257
- return Math.max(0, Math.min(1, score));
2258
- }
2259
-
2260
- private async handleFallback(input: string): Promise<string> {
2261
- const lowerInput = input.toLowerCase();
2262
- const parts = input.trim().split(/\s+/);
2263
- const cmd = parts[0].toLowerCase();
2264
- const args = parts.slice(1).join(' ');
2265
-
2266
- if (cmd.includes('读取') || cmd === 'read' || cmd === 'read_document') {
2267
- if (args) return await this.readDocument(args);
2268
- }
2269
-
2270
- if (cmd.includes('总结') || cmd === 'summary' || cmd === 'summarize') {
2271
- if (args) return await this.summarizeText(args);
2272
- }
2273
-
2274
- if (cmd.includes('改进') || cmd === 'improve' || cmd === 'improve_document') {
2275
- const match = input.match(/改进[^\w]+(.+)/i) || input.match(/improve\s+(.+)/i);
2276
- if (match) {
2277
- return `改进需要LLM支持,请设置 MINIMAX_API_KEY 环境变量。\n文件: ${match[1]}`;
2278
- }
2279
- }
2280
-
2281
- if (cmd.includes('节点') || cmd === 'peers' || cmd === 'list_peers') {
2282
- return this.listPeers();
2283
- }
2284
-
2285
- if (cmd.includes('身份') || cmd === 'identity' || cmd === 'get_identity') {
2286
- return JSON.stringify(this.getIdentity(), null, 2);
2287
- }
2288
-
2289
- if (cmd.includes('日志') || cmd === 'logs') {
2290
- const logs = this.constraintLayer.getLogs();
2291
- if (logs.length === 0) return '暂无操作日志';
2292
- return logs.slice(-5).map(l => `[${new Date(l.timestamp).toISOString()}] ${l.action}`).join('\n');
2293
- }
2294
-
2295
- return this.getDefaultResponse(input);
2296
- }
2297
-
2298
- private static OPERATIONS_REFERENCE: string | null = null;
2299
-
2300
- private static getOperationsReference(): string {
2301
- if (this.OPERATIONS_REFERENCE === null) {
2302
- try {
2303
- const refPath = path.join(process.cwd(), 'src', 'bollharness', 'scripts', 'context-fragments', 'pi-agent-operations.md');
2304
- this.OPERATIONS_REFERENCE = fsSync.readFileSync(refPath, 'utf-8');
2305
- } catch {
2306
- this.OPERATIONS_REFERENCE = '';
2307
- }
2308
- }
2309
- return this.OPERATIONS_REFERENCE;
2310
- }
2311
-
2312
- private getDefaultResponse(input: string): string {
2313
- const operationsRef = PiAgentSession.getOperationsReference();
2314
-
2315
- if (operationsRef) {
2316
- return `收到了: "${input}"
2317
-
2318
- 我是一个判断力处理智能体,支持自然语言交互。
2319
-
2320
- 可用操作(直接说出即可):
2321
- ${this.extractOperationsFromRef(operationsRef)}
2322
-
2323
- 示例请求:
2324
- - "读取 src/index.ts 文件"
2325
- - "总结一下 README.md"
2326
- - "查看当前连接了哪些节点"
2327
- - "向 QmABC... 发送测试消息"`;
2328
- }
2329
-
2330
- return `收到了: "${input}"
2331
-
2332
- 我是一个判断力处理智能体,支持自然语言交互。
2333
-
2334
- 可用操作(直接说出即可):
2335
- - "读取 README.md" - 读取并分析文档
2336
- - "总结文档" - 总结文档内容
2337
- - "改进文档,按照X要求" - 改进文档
2338
- - "查看节点" - 查看已连接的对等节点
2339
- - "向X发送消息Y" - 向对等节点发送消息
2340
- - "广播消息X" - 广播消息到所有节点
2341
- - "查看身份" - 查看当前智能体身份
2342
- - "查看日志" - 查看最近操作日志
2343
-
2344
- 示例请求:
2345
- - "读取 src/index.ts 文件"
2346
- - "总结一下 README.md"
2347
- - "查看当前连接了哪些节点"
2348
- - "向 QmABC... 发送测试消息"`;
2349
- }
2350
-
2351
- private extractOperationsFromRef(ref: string): string {
2352
- const lines = ref.split('\n');
2353
- const inOperationsSection = false;
2354
- const operationLines: string[] = [];
2355
-
2356
- for (let i = 0; i < lines.length; i++) {
2357
- const line = lines[i];
2358
- if (line.startsWith('## 可用操作')) {
2359
- for (let j = i + 1; j < lines.length; j++) {
2360
- const opLine = lines[j];
2361
- if (opLine.startsWith('## ') || opLine.startsWith('#')) break;
2362
- if (opLine.includes('|') && !opLine.startsWith('|')) {
2363
- const parts = opLine.split('|').map(p => p.trim());
2364
- if (parts.length >= 3 && parts[1] && parts[2]) {
2365
- operationLines.push(` - "${parts[1]}" - ${parts[2]}`);
2366
- }
2367
- }
2368
- }
2369
- break;
2370
- }
2371
- }
2372
-
2373
- return operationLines.length > 0 ? operationLines.join('\n') :
2374
- ` - "读取 README.md" - 读取并分析文档
2375
- - "总结文档" - 总结文档内容
2376
- - "改进文档,按照X要求" - 改进文档
2377
- - "查看节点" - 查看已连接的对等节点
2378
- - "向X发送消息Y" - 向对等节点发送消息
2379
- - "广播消息X" - 广播消息到所有节点
2380
- - "查看身份" - 查看当前智能体身份
2381
- - "查看日志" - 查看最近操作日志`;
2382
- }
2383
-
2384
- async suggestRename(messages: { type: string; content: string }[]): Promise<string | null> {
2385
- if (!this.minimaxAvailable || messages.length < 2) {
2386
- return null;
2387
- }
2388
-
2389
- const conversation = messages.map(m => `${m.type === 'user' ? '用户' : '助手'}: ${m.content}`).join('\n');
2390
- const llm = getMinimax();
2391
-
2392
- try {
2393
- const response = await llm.chat(
2394
- `根据以下对话内容,为这个对话生成一个简短的名称(不超过20个字):\n\n${conversation}\n\n直接输出名称,不要其他解释。`,
2395
- '命名建议'
2396
- );
2397
-
2398
- const name = response.reply.trim();
2399
- // 拒绝错误回退串 (LLM 不可用时返回的占位文本)
2400
- if (!name) return null;
2401
- if (/^(抱歉|对不起|sorry|error|错误|失败|暂不可用|服务不可用)/i.test(name)) {
2402
- console.log(`[suggestRename] 拒绝错误回退: "${name}"`);
2403
- return null;
2404
- }
2405
- if (name.length > 20) return null;
2406
- if (name === '智能体') return null;
2407
- // 拒绝纯符号/标点
2408
- if (!/[一-鿿\w]/.test(name)) return null;
2409
- return `Agent | ${name}`;
2410
- } catch {
2411
- // ignore
2412
- }
2413
- return null;
2414
- }
2415
-
2416
- private async summarizeText(text: string): Promise<string> {
2417
- if (!this.minimaxAvailable) {
2418
- return '⚠️ LLM未初始化,请设置 MINIMAX_API_KEY 环境变量';
2419
- }
2420
- const llm = getMinimax();
2421
- const result = await llm.summarize(text);
2422
- return `📝 摘要:\n${result.summary}\n\n质量评分: ${(result.qualityScore * 10).toFixed(1)}/10`;
2423
- }
2424
-
2425
- async readDocument(filePath: string): Promise<string> {
2426
- const content = await documentReader.read(filePath);
2427
- this.sessionManager.addFileContext(filePath, content.text.substring(0, 1000));
2428
- return `📄 ${content.metadata.filename}\n大小: ${content.metadata.size} 字节\n\n${content.text.substring(0, 500)}...`;
2429
- }
2430
-
2431
- async summarizeDocument(filePath: string, context?: string): Promise<{
2432
- summary: string;
2433
- qualityScore: number;
2434
- }> {
2435
- if (!this.minimaxAvailable) {
2436
- return {
2437
- summary: '⚠️ LLM未初始化,请设置 MINIMAX_API_KEY 环境变量',
2438
- qualityScore: 0
2439
- };
2440
- }
2441
-
2442
- const content = await documentReader.read(filePath);
2443
- this.sessionManager.addFileContext(filePath, content.text.substring(0, 1000));
2444
- const llm = getMinimax();
2445
- const chunks = documentReader.chunk(content.text);
2446
- const summaries: string[] = [];
2447
- let totalQuality = 0;
2448
-
2449
- for (const chunk of chunks) {
2450
- const result = await llm.summarize(chunk, context);
2451
- summaries.push(result.summary);
2452
- totalQuality += result.qualityScore;
2453
- }
2454
-
2455
- const avgQuality = totalQuality / chunks.length;
2456
- return {
2457
- summary: summaries.join('\n\n'),
2458
- qualityScore: avgQuality
2459
- };
2460
- }
2461
-
2462
- async improveDocument(request: ImprovementRequest): Promise<{
2463
- improved: boolean;
2464
- newContent?: string;
2465
- qualityScore: number;
2466
- shouldAutoSend: boolean;
2467
- }> {
2468
- if (!this.minimaxAvailable) {
2469
- return {
2470
- improved: false,
2471
- qualityScore: 0,
2472
- shouldAutoSend: false
2473
- };
2474
- }
2475
-
2476
- const content = await documentReader.read(request.originalPath);
2477
- const llm = getMinimax();
2478
- const improvedResult = await llm.summarize(content.text + '\n\n改进要求: ' + request.requirements, request.context);
2479
- const shouldAutoSend = await llm.shouldAutoSend(improvedResult.qualityScore, 0.7);
2480
-
2481
- return {
2482
- improved: true,
2483
- newContent: improvedResult.summary,
2484
- qualityScore: improvedResult.qualityScore,
2485
- shouldAutoSend
2486
- };
2487
- }
2488
-
2489
- async runWorkflow(steps: WorkflowStep[]): Promise<Workflow> {
2490
- const context: WorkflowContext = {
2491
- peers: this.getPeers(),
2492
- logs: []
2493
- };
2494
-
2495
- const checkResult = await this.constraintLayer.checkGuardrails(context);
2496
- if (!checkResult.passed && checkResult.blocked) {
2497
- console.warn(`Guardrail blocked: ${checkResult.blocked.name}`);
2498
- }
2499
-
2500
- return this.workflowEngine.executeWorkflow(steps, context);
2501
- }
2502
-
2503
- async summarizeDocumentWorkflow(filePath: string, targetPeer?: string): Promise<Workflow> {
2504
- const steps: WorkflowStep[] = [
2505
- {
2506
- id: 'read',
2507
- type: 'read',
2508
- config: { path: filePath },
2509
- retry: { max: 3, current: 0, backoffMs: 1000 },
2510
- onFail: 'abort'
2511
- },
2512
- {
2513
- id: 'summarize',
2514
- type: 'summarize',
2515
- config: { context: `File: ${filePath}` },
2516
- retry: { max: 3, current: 0, backoffMs: 1000 },
2517
- onFail: 'skip',
2518
- guardrail: (ctx) => Promise.resolve(ctx.qualityScore !== undefined && ctx.qualityScore >= 0.5)
2519
- }
2520
- ];
2521
-
2522
- if (targetPeer) {
2523
- steps.push({
2524
- id: 'send',
2525
- type: 'send',
2526
- config: { peerId: targetPeer },
2527
- retry: { max: 2, current: 0, backoffMs: 2000 },
2528
- onFail: 'skip'
2529
- });
2530
- }
2531
-
2532
- return this.runWorkflow(steps);
2533
- }
2534
-
2535
- async improveAndSendWorkflow(filePath: string, requirements: string, targetPeer: string): Promise<Workflow> {
2536
- const steps: WorkflowStep[] = [
2537
- {
2538
- id: 'read',
2539
- type: 'read',
2540
- config: { path: filePath },
2541
- retry: { max: 3, current: 0, backoffMs: 1000 },
2542
- onFail: 'abort'
2543
- },
2544
- {
2545
- id: 'improve',
2546
- type: 'improve',
2547
- config: { requirements, context: `File: ${filePath}` },
2548
- retry: { max: 2, current: 0, backoffMs: 1500 },
2549
- onFail: 'skip'
2550
- },
2551
- {
2552
- id: 'send',
2553
- type: 'send',
2554
- config: { peerId: targetPeer, message: '改进后的文档' },
2555
- retry: { max: 2, current: 0, backoffMs: 2000 },
2556
- onFail: 'skip'
2557
- }
2558
- ];
2559
-
2560
- return this.runWorkflow(steps);
2561
- }
2562
-
2563
- getOperationLogs(): { timestamp: number; action: string; details: Record<string, unknown>; status: string }[] {
2564
- return this.constraintLayer.getLogs();
2565
- }
2566
-
2567
- private listPeers(): string {
2568
- const peers = p2pNetwork.getPeers();
2569
- if (peers.length === 0) {
2570
- return '当前无连接的对等节点';
2571
- }
2572
- return `已连接节点 (${peers.length}):\n${peers.map(p => ` - ${p}`).join('\n')}`;
2573
- }
2574
-
2575
- getPeers(): string[] {
2576
- return p2pNetwork.getPeers();
2577
- }
2578
-
2579
- async sendMessage(peerId: string, message: string): Promise<void> {
2580
- await p2pNetwork.sendMessage(peerId, 'message', message);
2581
- }
2582
-
2583
- async broadcast(message: string): Promise<void> {
2584
- await p2pNetwork.broadcast('message', message);
2585
- }
2586
-
2587
- getIdentity(): IdentityDoc {
2588
- return { ...this.identity };
2589
- }
2590
-
2591
- updateIdentity(updates: Partial<IdentityDoc>): void {
2592
- this.identity = { ...this.identity, ...updates };
2593
- }
2594
-
2595
- setCurrentChannelId(channelId: string): void {
2596
- this.currentChannelId = channelId;
2597
- }
2598
-
2599
- getSessionState(): PiSessionState {
2600
- return this.sessionManager.getState();
2601
- }
2602
-
2603
- getMemory(): PiMemory {
2604
- return this.sessionManager.getMemory();
2605
- }
2606
-
2607
- getPersona(): PersonaDoc | null {
2608
- return this.sessionManager.getPersona();
2609
- }
2610
-
2611
- async setPersona(persona: PersonaDoc): Promise<void> {
2612
- await this.sessionManager.savePersona(persona);
2613
- this.persona = persona;
2614
- if (persona.name) {
2615
- this.identity.name = persona.name;
2616
- }
2617
- }
2618
-
2619
- getDiscoveredAgents(): DiscoveredAgent[] {
2620
- return this.agentsManager.getAllAgents();
2621
- }
2622
-
2623
- getSocialChannels(): SessionChannel[] {
2624
- return this.sessionManager.getAllChannels();
2625
- }
2626
-
2627
- async sendSocialMessage(channelId: string, content: string): Promise<void> {
2628
- const message: SessionMessage = {
2629
- id: crypto.randomUUID(),
2630
- type: 'ai',
2631
- content,
2632
- sender: 'self',
2633
- timestamp: new Date().toISOString(),
2634
- agentId: this.identity.did
2635
- };
2636
-
2637
- await this.sessionManager.addMessage(channelId, message);
2638
-
2639
- const channels = this.sessionManager.getAllChannels();
2640
- const channel = channels.find(c => c.id === channelId);
2641
- if (channel?.peerDid) {
2642
- const agent = this.agentsManager.getAgent(channel.peerDid);
2643
- if (agent) {
2644
- const comm = (global as any).hyperswarmComm;
2645
- if (comm) {
2646
- const connections = comm.getConnections?.() || [];
2647
- for (const conn of connections) {
2648
- if (conn.publicKey === agent.peerId) {
2649
- const data = new TextEncoder().encode(`social|${JSON.stringify({ from: this.identity.did, message: content })}`);
2650
- comm.sendToConnection?.(conn, data);
2651
- break;
2652
- }
2653
- }
2654
- }
2655
- }
2656
- }
2657
- }
2658
-
2659
- async startSocialHeartbeat(config?: Partial<HeartbeatConfig>): Promise<void> {
2660
- if (this.socialHeartbeat) {
2661
- return;
2662
- }
2663
- this.socialHeartbeat = await createSocialHeartbeat(this.sessionManager, this.agentsManager, config);
2664
- this.socialHeartbeat.setOnAgentDiscovered((agent) => {
2665
- console.log(`[Agent] 发现新智能体: ${agent.name}`);
2666
- });
2667
- this.socialHeartbeat.setOnSocialMessage((fromDid, message, channelId) => {
2668
- console.log(`[Agent] 收到来自 ${fromDid} 的社交消息: ${message.substring(0, 50)}...`);
2669
- });
2670
- }
2671
-
2672
- stopSocialHeartbeat(): void {
2673
- if (this.socialHeartbeat) {
2674
- this.socialHeartbeat.stop();
2675
- this.socialHeartbeat = null;
2676
- }
2677
- }
2678
-
2679
- getSkillRegistry(): SkillRegistry {
2680
- return this.skillRegistry;
2681
- }
2682
-
2683
- registerSkill(skill: Skill): void {
2684
- this.skillRegistry.register(skill);
2685
- }
2686
-
2687
- async executeSkill(name: string, params: Record<string, unknown>): Promise<string> {
2688
- return this.skillRegistry.execute(name, params);
2689
- }
2690
-
2691
- async addUserAction(content: string, importance?: number): Promise<void> {
2692
- await this.sessionManager.addUserActionToSharedContext(content, importance);
2693
- }
2694
-
2695
- async addSharedKnowledge(knowledge: string): Promise<void> {
2696
- await this.sessionManager.addSharedKnowledge(knowledge);
2697
- }
2698
-
2699
- async getRecentActionsSummary(count?: number): Promise<string> {
2700
- return this.sessionManager.getRecentActionsSummary(count);
2701
- }
2702
-
2703
- async getSharedKnowledge(): Promise<string[]> {
2704
- return this.sessionManager.getSharedKnowledge();
2705
- }
2706
-
2707
- async getGlobalContextSummary(): Promise<string> {
2708
- return this.sessionManager.getGlobalContextSummary();
2709
- }
2710
-
2711
- async createCooperation(
2712
- type: CooperationType,
2713
- task: string,
2714
- toAgentId?: string,
2715
- context?: string
2716
- ): Promise<CooperationTask> {
2717
- return this.sessionManager.createCooperation(type, task, toAgentId, context);
2718
- }
2719
-
2720
- async getPendingCooperations(): Promise<CooperationTask[]> {
2721
- return this.sessionManager.getPendingCooperations();
2722
- }
2723
-
2724
- async updateCooperationStatus(
2725
- cooperationId: string,
2726
- status: 'pending' | 'in_progress' | 'done' | 'failed',
2727
- result?: string
2728
- ): Promise<void> {
2729
- return this.sessionManager.updateCooperationStatus(cooperationId, status, result);
2730
- }
2731
-
2732
- async getAllRegisteredAgents(): Promise<AgentInfo[]> {
2733
- return this.sessionManager.getAllRegisteredAgents();
2734
- }
2735
-
2736
- async findAgentByCapability(capability: string): Promise<AgentInfo[]> {
2737
- return this.sessionManager.findAgentByCapability(capability);
2738
- }
2739
-
2740
- // ==================== Harness Integration ====================
2741
-
2742
- private operationLog: Array<{ timestamp: number; action: string; args: any; result: any; status: string }> = [];
2743
-
2744
- private logToHarness(action: string, args: any, result: any): void {
2745
- if (!this.harnessEnabled || !this.harness) return;
2746
-
2747
- this.operationLog.push({
2748
- timestamp: Date.now(),
2749
- action,
2750
- args,
2751
- result,
2752
- status: result.success ? 'ok' : 'error'
2753
- });
2754
-
2755
- if (this.operationLog.length >= 10) {
2756
- this.archiveToHarness();
2757
- }
2758
- }
2759
-
2760
- archiveToHarness(): void {
2761
- if (!this.harnessEnabled || !this.harness || this.operationLog.length === 0) return;
2762
-
2763
- this.harness.archiveSession(this.operationLog);
2764
- this.operationLog = [];
2765
- }
2766
-
2767
- getHarnessContext(): string {
2768
- if (!this.harnessEnabled || !this.harness) {
2769
- return 'Harness not available';
2770
- }
2771
- return this.harness.getSessionContext();
2772
- }
2773
-
2774
- isHarnessEnabled(): boolean {
2775
- return this.harnessEnabled;
2776
- }
2777
-
2778
- getHarness(): any {
2779
- return this.harness;
2780
- }
2781
-
2782
- getOperationLog(): Array<{ timestamp: number; action: string; args: any; result: any; status: string }> {
2783
- return [...this.operationLog];
2784
- }
2785
- }
2786
-
2787
- let sessionInstance: AgentSession | null = null;
2788
- let lastIdentityDid: string | null = null;
2789
-
2790
- // 独立的 session 实例缓存(用于多 session 支持)
2791
- const independentSessions: Map<string, AgentSession> = new Map();
2792
-
2793
- export async function createAgentSession(config: AgentSessionConfig, forceNew?: boolean): Promise<AgentSession> {
2794
- const incomingDid = config.identityDoc?.did;
2795
-
2796
- // 如果有独立的 peerId (包含 :),使用它作为 key
2797
- if (config.peerId && config.peerId.includes(':')) {
2798
- const key = config.peerId;
2799
- if (!forceNew && independentSessions.has(key)) {
2800
- console.log(`[createAgentSession] 找到现有独立 session, key=${key}`);
2801
- return independentSessions.get(key)!;
2802
- }
2803
- const session = new PiAgentSession(config);
2804
- independentSessions.set(key, session);
2805
- console.log(`[createAgentSession] 创建独立 session, key=${key}, DID=${incomingDid}`);
2806
- return session;
2807
- }
2808
-
2809
- // 如果指定了 forceNew 但没有 peerId,生成带时间戳的 key
2810
- if (forceNew) {
2811
- const key = `force:${Date.now()}`;
2812
- const session = new PiAgentSession(config);
2813
- independentSessions.set(key, session);
2814
- console.log(`[createAgentSession] 创建强制新 session, key=${key}`);
2815
- return session;
2816
- }
2817
-
2818
- // 如果有新的 DID,强制重建 session
2819
- if (sessionInstance && lastIdentityDid && incomingDid && lastIdentityDid !== incomingDid) {
2820
- console.log(`[createAgentSession] DID 变化 ${lastIdentityDid} -> ${incomingDid},重建 session`);
2821
- sessionInstance = null;
2822
- }
2823
-
2824
- if (sessionInstance) {
2825
- // 检查是否需要更新 identity
2826
- const currentDid = sessionInstance.getIdentity().did;
2827
- if (incomingDid && currentDid !== incomingDid) {
2828
- console.log(`[createAgentSession] 更新 identity: ${currentDid} -> ${incomingDid}`);
2829
- sessionInstance.updateIdentity({
2830
- did: incomingDid,
2831
- name: config.identityDoc?.name || sessionInstance.getIdentity().name,
2832
- publicKey: config.identityDoc?.publicKey || '',
2833
- createdAt: Date.now()
2834
- });
2835
- }
2836
- return sessionInstance;
2837
- }
2838
-
2839
- sessionInstance = new PiAgentSession(config);
2840
- lastIdentityDid = config.identityDoc?.did || null;
2841
- console.log(`[createAgentSession] 新建 session, DID=${lastIdentityDid}`);
2842
- return sessionInstance;
2843
- }
2844
-
2845
- export function getAgentSession(): AgentSession | null {
2846
- return sessionInstance;
2847
- }
2848
-
2849
- export function resetAgentSession(): void {
2850
- sessionInstance = null;
2851
- lastIdentityDid = null;
2852
- }
2853
-
2854
- /**
2855
- * 自我改进循环: 在沙箱分支上工作, 输出结果给用户审.
2856
- *
2857
- * 不在 PiAgent 实例上的原因: 心跳回调可能没有 agent 实例, 单独函数更易复用.
2858
- *
2859
- * **关键不变量**:
2860
- * 1. AI 不能 push 到 master (shell-guard 黑名单 + git 受保护分支)
2861
- * 2. 改动必须走沙箱分支 (SELF_IMPROVE_BRANCH_PREFIX)
2862
- * 3. 6 小时冷却期 (SELF_IMPROVE_COOLDOWN_MS)
2863
- * 4. 写文件必须经过 shell_exec + 护栏检查
2864
- */
2865
- let lastSelfImproveAt: number | null = null;
2866
-
2867
- export async function runSelfImproveLoop(goal: string): Promise<{ success: boolean; output?: string; error?: string }> {
2868
- const cooldownMs = getCooldownMs();
2869
- // 1. 冷却期检查
2870
- if (lastSelfImproveAt && Date.now() - lastSelfImproveAt < cooldownMs) {
2871
- const waitHrs = Math.ceil((cooldownMs - (Date.now() - lastSelfImproveAt)) / 3600000);
2872
- return { success: false, error: `自改冷却中, 还需要约 ${waitHrs} 小时` };
2873
- }
2874
-
2875
- // 2. 选源分支 + 新分支名
2876
- const sourceBranch = 'master';
2877
- const newBranch = `${getBranchPrefix()}${Date.now()}`;
2878
-
2879
- console.log(`[self-improve] 启动自改循环, 目标: ${goal}, 新分支: ${newBranch}`);
2880
-
2881
- // 3. 创建分支
2882
- const r1 = await shellExec('git', ['checkout', sourceBranch]);
2883
- if (!r1.success) return { success: false, error: `切换到 ${sourceBranch} 失败: ${r1.error}` };
2884
-
2885
- const r2 = await shellExec('git', ['checkout', '-b', newBranch]);
2886
- if (!r2.success) return { success: false, error: `创建分支失败: ${r2.error}` };
2887
-
2888
- // 4. 走 task queue: 把"自改"作为一个 task 抛回去, AI 拿到后会用 shell_exec 改
2889
- // 护栏已经阻止所有禁区改动, 这里只负责登记
2890
- lastSelfImproveAt = Date.now();
2891
- return {
2892
- success: true,
2893
- output: `✅ 自改分支已创建: ${newBranch}\n目标: ${goal}\n\n**护栏已激活**:\n - 仅允许白名单命令 (git/npm/tsc/vitest/cat/ls/...)\n - 禁止改 src/agents/pi-sdk.ts, shell-guard.ts, src/heartbeat/, src/network/, src/pi-ecosystem-judgment/, package.json, .env 等\n - 6 小时冷却期\n\nAI 接下来会用 shell_exec 工具改源码. 完成后你会在对话里看到 diff 摘要, 手动 git diff master..${newBranch} 审, 满意再 merge.`
2894
- };
2895
- }