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