foxclaw 0.1.2 → 0.1.4
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/index.js +1 -8
- package/package.json +1 -1
- package/python/.env.example +8 -0
- package/python/.github/workflows/ci.yml +47 -2
- package/python/.github/workflows/release.yml +5 -0
- package/python/CHANGELOG.md +100 -0
- package/python/CHANGELOG_CN.md +100 -0
- package/python/README.md +17 -2
- package/python/README_CN.md +38 -20
- package/python/bench/bm25_audit.py +1 -14
- package/python/bench/bm25_regression.py +0 -1
- package/python/bench/bm25_stress.py +0 -4
- package/python/bench/cd_audit.py +0 -23
- package/python/bench/chaos.py +6 -8
- package/python/bench/e2e_real_models.py +0 -1
- package/python/bench/llm_retry_test.py +2 -9
- package/python/bench/load_test.py +5 -8
- package/python/bench/vector_ivf_upgrade.py +1 -7
- package/python/docs/CONTENT_GUARD.md +189 -0
- package/python/docs/CONTENT_GUARD_CN.md +189 -0
- package/python/docs/RELEASE.md +78 -37
- package/python/docs/RUN_CN.md +2 -2
- package/python/evals/governance/cases.py +0 -37
- package/python/evals/governance/run_evals.py +5 -37
- package/python/examples/governance_middleware_demo.py +1 -6
- package/python/examples/langgraph_governance_demo.py +7 -11
- package/python/foxclaw/__init__.py +74 -152
- package/python/foxclaw/agents/base.py +82 -136
- package/python/foxclaw/agents/impl/_helpers.py +2 -10
- package/python/foxclaw/agents/impl/finance.py +4 -21
- package/python/foxclaw/agents/impl/office.py +7 -24
- package/python/foxclaw/agents/impl/supervisor.py +12 -79
- package/python/foxclaw/agents/mixins/_context.py +0 -23
- package/python/foxclaw/agents/mixins/augment.py +10 -16
- package/python/foxclaw/agents/mixins/llm.py +7 -6
- package/python/foxclaw/agents/mixins/memory.py +23 -3
- package/python/foxclaw/agents/mixins/preference.py +58 -8
- package/python/foxclaw/agents/mixins/prompt.py +59 -12
- package/python/foxclaw/agents/mixins/skillbox.py +6 -6
- package/python/foxclaw/agents/mixins/tool_exec.py +16 -42
- package/python/foxclaw/agents/rules/finance/RULES.md +131 -0
- package/python/foxclaw/api/__init__.py +18 -77
- package/python/foxclaw/api/_state.py +43 -0
- package/python/foxclaw/api/bootstrap.py +114 -0
- package/python/foxclaw/api/middleware.py +5 -28
- package/python/foxclaw/api/routes/ahs.py +60 -73
- package/python/foxclaw/api/routes/attachments.py +5 -6
- package/python/foxclaw/api/routes/browser.py +7 -11
- package/python/foxclaw/api/routes/domain.py +18 -26
- package/python/foxclaw/api/routes/knowledge.py +3 -21
- package/python/foxclaw/api/routes/llm_admin.py +21 -33
- package/python/foxclaw/api/routes/memory.py +50 -12
- package/python/foxclaw/api/routes/orchestration.py +3 -5
- package/python/foxclaw/api/routes/recovery.py +4 -3
- package/python/foxclaw/api/routes/review.py +2 -11
- package/python/foxclaw/api/routes/search.py +3 -7
- package/python/foxclaw/api/routes/system.py +33 -49
- package/python/foxclaw/api/routes/task.py +31 -49
- package/python/foxclaw/api/routes/ws.py +2 -6
- package/python/foxclaw/api/spine.py +1 -4
- package/python/foxclaw/behavior/__init__.py +20 -0
- package/python/foxclaw/behavior/behavior.yaml +62 -0
- package/python/foxclaw/behavior/spec.py +287 -0
- package/python/foxclaw/browser/action/action_executor.py +1 -5
- package/python/foxclaw/browser/action/action_types.py +0 -1
- package/python/foxclaw/browser/action/url_policy.py +1 -5
- package/python/foxclaw/browser/adapter/web_adapter.py +1 -1
- package/python/foxclaw/browser/driver/playwright_driver.py +20 -16
- package/python/foxclaw/browser/perception/dom_extractor.py +0 -1
- package/python/foxclaw/browser/perception/element_indexer.py +1 -2
- package/python/foxclaw/browser/service.py +7 -11
- package/python/foxclaw/capability/embedding_service.py +4 -6
- package/python/foxclaw/channels/__init__.py +9 -11
- package/python/foxclaw/channels/discord.py +4 -13
- package/python/foxclaw/channels/dispatcher.py +2 -27
- package/python/foxclaw/channels/feishu.py +10 -20
- package/python/foxclaw/channels/openclaw_bridge.py +4 -9
- package/python/foxclaw/channels/signing.py +9 -9
- package/python/foxclaw/channels/telegram.py +3 -4
- package/python/foxclaw/channels/weixin.py +7 -17
- package/python/foxclaw/cli.py +42 -13
- package/python/foxclaw/cognitive/reasoner.py +9 -10
- package/python/foxclaw/cognitive/state.py +18 -24
- package/python/foxclaw/core/audit_chain.py +1 -3
- package/python/foxclaw/core/audit_sink.py +3 -3
- package/python/foxclaw/core/compaction.py +8 -26
- package/python/foxclaw/core/config.py +2 -2
- package/python/foxclaw/core/confirm_nonce.py +6 -12
- package/python/foxclaw/core/content_guard.py +341 -0
- package/python/foxclaw/core/envfile.py +2 -2
- package/python/foxclaw/core/failure_policy.py +17 -26
- package/python/foxclaw/core/gateway.py +6 -10
- package/python/foxclaw/core/governance_audit.py +3 -7
- package/python/foxclaw/core/idempotency.py +15 -22
- package/python/foxclaw/core/injection_guard.py +10 -29
- package/python/foxclaw/core/memory_adapter.py +53 -60
- package/python/foxclaw/core/metrics.py +4 -5
- package/python/foxclaw/core/model_router.py +74 -150
- package/python/foxclaw/core/paths.py +1 -1
- package/python/foxclaw/core/recovery.py +21 -34
- package/python/foxclaw/core/scheduler.py +23 -26
- package/python/foxclaw/core/secrets.py +0 -3
- package/python/foxclaw/core/sensitive_write.py +283 -0
- package/python/foxclaw/core/sessions.py +4 -6
- package/python/foxclaw/core/settings.py +16 -32
- package/python/foxclaw/core/shared_write.py +11 -23
- package/python/foxclaw/core/steering_queue.py +12 -11
- package/python/foxclaw/core/system_bus.py +1 -1
- package/python/foxclaw/core/thought_chain.py +30 -32
- package/python/foxclaw/core/token_meter.py +9 -13
- package/python/foxclaw/core/tools.py +4 -12
- package/python/foxclaw/core/tracing.py +25 -40
- package/python/foxclaw/core/truth_pipeline.py +5 -9
- package/python/foxclaw/core/update_check.py +149 -0
- package/python/foxclaw/core/vec_recall_adapter.py +11 -37
- package/python/foxclaw/core/verify.py +6 -14
- package/python/foxclaw/core/web_search.py +4 -28
- package/python/foxclaw/finance/evidence.py +14 -18
- package/python/foxclaw/finance/fx.py +1 -3
- package/python/foxclaw/finance/graph.py +9 -18
- package/python/foxclaw/finance/models.py +20 -24
- package/python/foxclaw/finance/nlparse.py +12 -72
- package/python/foxclaw/finance/policy.py +8 -36
- package/python/foxclaw/finance/reasoner.py +3 -11
- package/python/foxclaw/finance/reasoning.py +13 -34
- package/python/foxclaw/finance/reconcile.py +5 -10
- package/python/foxclaw/finance/runtime.py +19 -46
- package/python/foxclaw/governance/middleware.py +13 -21
- package/python/foxclaw/harness/_base.py +2 -8
- package/python/foxclaw/harness/_policy.py +19 -38
- package/python/foxclaw/harness/drift.py +1 -13
- package/python/foxclaw/harness/event_bus.py +2 -5
- package/python/foxclaw/harness/harness.py +9 -97
- package/python/foxclaw/harness/loop_detection.py +11 -18
- package/python/foxclaw/itinerary/amap_client.py +2 -5
- package/python/foxclaw/itinerary/models.py +7 -7
- package/python/foxclaw/itinerary/reasoner.py +1 -6
- package/python/foxclaw/itinerary/reasoning.py +15 -21
- package/python/foxclaw/itinerary/runtime.py +8 -12
- package/python/foxclaw/kernel/capabilities.py +5 -6
- package/python/foxclaw/kernel/policy_gate.py +4 -10
- package/python/foxclaw/kernel/process_manager.py +17 -36
- package/python/foxclaw/kernel/scheduler.py +3 -9
- package/python/foxclaw/kernel/sentinel.py +13 -23
- package/python/foxclaw/kernel/sentinel_client.py +12 -15
- package/python/foxclaw/kernel/spine.py +9 -9
- package/python/foxclaw/knowledge/__init__.py +4 -20
- package/python/foxclaw/knowledge/truth_crystallizer.py +5 -8
- package/python/foxclaw/learning/knowledge_graph.py +4 -9
- package/python/foxclaw/learning/models.py +13 -13
- package/python/foxclaw/learning/reasoner.py +1 -4
- package/python/foxclaw/learning/runtime.py +19 -31
- package/python/foxclaw/learning/spaced_repetition.py +2 -7
- package/python/foxclaw/mcp_server.py +2 -3
- package/python/foxclaw/orchestration/__init__.py +16 -33
- package/python/foxclaw/pipeline/cognitive_pipeline.py +45 -97
- package/python/foxclaw/pipeline/stage_meta.py +21 -0
- package/python/foxclaw/pipeline/supervisor_stages.py +13 -42
- package/python/foxclaw/planning/llm_planner.py +3 -3
- package/python/foxclaw/planning/planner.py +6 -11
- package/python/foxclaw/search/agent_port.py +7 -11
- package/python/foxclaw/search/evidence.py +0 -4
- package/python/foxclaw/search/internal_search.py +3 -5
- package/python/foxclaw/search/models.py +11 -13
- package/python/foxclaw/search/planner.py +3 -3
- package/python/foxclaw/search/retriever.py +1 -1
- package/python/foxclaw/search/runtime.py +1 -8
- package/python/foxclaw/skills/__init__.py +46 -100
- package/python/foxclaw/skills/archive.py +111 -0
- package/python/foxclaw/skills/fetch.py +226 -0
- package/python/foxclaw/skills/hooks_spec.py +21 -25
- package/python/foxclaw/skills/install_cmd.py +216 -0
- package/python/foxclaw/skills/lifecycle.py +16 -42
- package/python/foxclaw/skills/markdown_skill.py +10 -9
- package/python/foxclaw/skills/mcp_client.py +5 -11
- package/python/foxclaw/skills/mcp_store.py +0 -3
- package/python/foxclaw/skills/permission.py +13 -48
- package/python/foxclaw/skills/pipeline.py +21 -53
- package/python/foxclaw/skills/policy.py +4 -10
- package/python/foxclaw/skills/review.py +3 -3
- package/python/frontend/deck/auth.js +0 -7
- package/python/frontend/deck/chat_model_picker.js +0 -54
- package/python/frontend/deck/domain.js +0 -20
- package/python/frontend/deck/main.js +0 -144
- package/python/frontend/deck/markdown.js +0 -4
- package/python/frontend/deck/settings.js +1 -18
- package/python/frontend/deck/state.js +0 -1
- package/python/frontend/deck/util.js +0 -2
- package/python/frontend/deck/visuals.js +0 -22
- package/python/frontend/planet/planet.js +18 -201
- package/python/frontend/v3/assets/index-JKfHlb-a.css +1 -0
- package/python/frontend/v3/assets/index-tN90dpUH.js +34 -0
- package/python/frontend/v3/index.html +3 -3
- package/python/frontend/widget_runtime.js +0 -43
- package/python/frontend/widgets.js +0 -57
- package/python/frontend_src/index.html +1 -1
- package/python/frontend_src/package-lock.json +2 -2
- package/python/frontend_src/package.json +1 -1
- package/python/frontend_src/src/App.vue +3 -7
- package/python/frontend_src/src/api.js +0 -7
- package/python/frontend_src/src/components/ChatDock.vue +27 -39
- package/python/frontend_src/src/components/DocViewer.vue +0 -9
- package/python/frontend_src/src/components/Inspector.vue +0 -6
- package/python/frontend_src/src/components/LeftRail.vue +60 -39
- package/python/frontend_src/src/components/MenuPopover.vue +9 -11
- package/python/frontend_src/src/components/MessageStream.vue +18 -30
- package/python/frontend_src/src/components/ModelPicker.vue +0 -5
- package/python/frontend_src/src/components/QuickIngest.vue +0 -6
- package/python/frontend_src/src/components/Stage.vue +71 -25
- package/python/frontend_src/src/components/ToolMenu.vue +0 -3
- package/python/frontend_src/src/components/TopBar.vue +38 -10
- package/python/frontend_src/src/components/panels/ActivityPanel.vue +0 -2
- package/python/frontend_src/src/components/panels/AgentsPanel.vue +2 -5
- package/python/frontend_src/src/components/panels/KnowledgePanel.vue +0 -16
- package/python/frontend_src/src/components/panels/MemoryPanel.vue +42 -3
- package/python/frontend_src/src/components/panels/PanelBox.vue +6 -7
- package/python/frontend_src/src/components/panels/ReviewPanel.vue +0 -16
- package/python/frontend_src/src/components/panels/SettingsPanel.vue +142 -38
- package/python/frontend_src/src/components/panels/TasksPanel.vue +0 -1
- package/python/frontend_src/src/components/panels/ToolsPanel.vue +125 -19
- package/python/frontend_src/src/lib/fmt.js +0 -4
- package/python/frontend_src/src/lib/i18n.js +53 -0
- package/python/frontend_src/src/lib/locales/en.js +173 -0
- package/python/frontend_src/src/lib/locales/zh.js +174 -0
- package/python/frontend_src/src/lib/md.js +0 -14
- package/python/frontend_src/src/main.js +2 -0
- package/python/frontend_src/src/stores/app.js +17 -32
- package/python/frontend_src/src/stores/chat.js +0 -34
- package/python/frontend_src/src/stores/model.js +0 -8
- package/python/frontend_src/src/stores/panels.js +16 -44
- package/python/frontend_src/src/stores/profile.js +31 -0
- package/python/frontend_src/src/stores/system.js +25 -0
- package/python/frontend_src/vite.config.js +0 -7
- package/python/pyproject.toml +17 -3
- package/python/requirements.txt +311 -0
- package/python/tests/conftest.py +0 -22
- package/python/tests/run_all.py +4 -66
- package/python/tests/test_agent_lifecycle.py +4 -38
- package/python/tests/test_agent_rules.py +81 -0
- package/python/tests/test_ahs_p2.py +4 -25
- package/python/tests/test_anthropic_tools.py +1 -15
- package/python/tests/test_api.py +19 -9
- package/python/tests/test_api_auth.py +1 -5
- package/python/tests/test_attachments.py +9 -25
- package/python/tests/test_audit_chain.py +2 -7
- package/python/tests/test_audit_sink.py +0 -6
- package/python/tests/test_behavior_spec.py +198 -0
- package/python/tests/test_browser.py +2 -6
- package/python/tests/test_browser_request_guard.py +0 -4
- package/python/tests/test_capabilities.py +0 -6
- package/python/tests/test_channel_signing.py +2 -9
- package/python/tests/test_channels.py +1 -11
- package/python/tests/test_config_layering.py +0 -4
- package/python/tests/test_confirm_nonce.py +1 -12
- package/python/tests/test_conflict_dismiss.py +143 -0
- package/python/tests/test_content_guard.py +177 -0
- package/python/tests/test_context_compaction_wiring.py +1 -7
- package/python/tests/test_dispatch.py +0 -6
- package/python/tests/test_domain_evals.py +11 -80
- package/python/tests/test_domain_import.py +0 -6
- package/python/tests/test_domain_reasoners.py +0 -14
- package/python/tests/test_e2e.py +0 -17
- package/python/tests/test_emb_cache.py +7 -17
- package/python/tests/test_embedding_singleton.py +1 -3
- package/python/tests/test_evals_smoke.py +0 -24
- package/python/tests/test_event_spine.py +10 -18
- package/python/tests/test_evidence_semantic.py +1 -14
- package/python/tests/test_evidence_sse.py +3 -6
- package/python/tests/test_file_ingest.py +0 -11
- package/python/tests/test_finance.py +0 -9
- package/python/tests/test_finance_ledger.py +5 -12
- package/python/tests/test_finance_persist.py +8 -12
- package/python/tests/test_gateway.py +3 -22
- package/python/tests/test_governance.py +5 -52
- package/python/tests/test_governance_audit.py +4 -39
- package/python/tests/test_governance_middleware.py +1 -7
- package/python/tests/test_hkc.py +6 -24
- package/python/tests/test_hkc_index_build_manager.py +0 -33
- package/python/tests/test_hkc_manifest_reuse.py +4 -27
- package/python/tests/test_hkc_search_event_sync.py +3 -54
- package/python/tests/test_import_path_guard.py +0 -6
- package/python/tests/test_injection_guard.py +18 -36
- package/python/tests/test_integration.py +5 -29
- package/python/tests/test_itinerary.py +0 -3
- package/python/tests/test_kee_semantic.py +0 -5
- package/python/tests/test_kernel.py +1 -11
- package/python/tests/test_learning.py +3 -12
- package/python/tests/test_llm_admin.py +4 -19
- package/python/tests/test_llm_planner.py +1 -23
- package/python/tests/test_markdown_skills.py +3 -14
- package/python/tests/test_markdown_skills_openclaw.py +4 -9
- package/python/tests/test_mcp_connectors.py +3 -15
- package/python/tests/test_mcp_guardian_discovery.py +2 -6
- package/python/tests/test_mcp_no_memory.py +3 -3
- package/python/tests/test_mcp_policy_gate.py +0 -1
- package/python/tests/test_mcp_server.py +6 -9
- package/python/tests/test_memory_recall.py +1 -4
- package/python/tests/test_metrics.py +0 -8
- package/python/tests/test_multi_chain.py +1 -13
- package/python/tests/test_multimodal.py +5 -18
- package/python/tests/test_negotiation.py +3 -13
- package/python/tests/test_orchestration.py +1 -11
- package/python/tests/test_orchestration_decoupled.py +0 -6
- package/python/tests/test_p3_p5_hardening.py +3 -17
- package/python/tests/test_parallel_dispatch.py +4 -12
- package/python/tests/test_pipeline.py +17 -58
- package/python/tests/test_plan_visible.py +4 -4
- package/python/tests/test_planner.py +0 -7
- package/python/tests/test_policy_gate.py +2 -3
- package/python/tests/test_profile.py +67 -0
- package/python/tests/test_readme_claims.py +163 -0
- package/python/tests/test_reasoner.py +2 -17
- package/python/tests/test_release_check.py +2 -13
- package/python/tests/test_resume_checkpoint.py +7 -15
- package/python/tests/test_routing.py +2 -12
- package/python/tests/test_scheduler.py +7 -10
- package/python/tests/test_search.py +1 -11
- package/python/tests/test_secrets.py +4 -10
- package/python/tests/test_semantic.py +1 -10
- package/python/tests/test_sensitive_write.py +181 -0
- package/python/tests/test_sessions.py +3 -4
- package/python/tests/test_settings.py +2 -18
- package/python/tests/test_shadow_learning.py +3 -43
- package/python/tests/test_shared_write.py +0 -7
- package/python/tests/test_skill_taint_gate.py +1 -10
- package/python/tests/test_skills.py +2 -18
- package/python/tests/test_skills_fetch.py +282 -0
- package/python/tests/test_stats_and_events.py +2 -5
- package/python/tests/test_streaming.py +2 -7
- package/python/tests/test_supervisor_pipeline.py +4 -5
- package/python/tests/test_supervisor_triage.py +4 -10
- package/python/tests/test_taint.py +2 -7
- package/python/tests/test_task_stream.py +1 -12
- package/python/tests/test_thinking_sse.py +0 -1
- package/python/tests/test_tool_center.py +1 -17
- package/python/tests/test_tool_parallel.py +2 -2
- package/python/tests/test_tool_params.py +1 -5
- package/python/tests/test_tool_use.py +0 -13
- package/python/tests/test_toplevel_concurrency.py +3 -4
- package/python/tests/test_tracing.py +7 -41
- package/python/tests/test_truth_crystallizer.py +4 -6
- package/python/tests/test_truth_flow.py +2 -7
- package/python/tests/test_update_check.py +95 -0
- package/python/tests/test_verify_evidence.py +8 -21
- package/python/tests/test_version_sync.py +35 -6
- package/python/tests/test_web_trigger.py +0 -17
- package/python/tools/build_content_lexicon.py +218 -0
- package/python/tools/bundle_web.py +98 -0
- package/python/tools/check_faiss_type.py +0 -1
- package/python/tools/check_log.ps1 +0 -8
- package/python/tools/mypy_baseline.txt +12 -4
- package/python/tools/mypy_ratchet.py +52 -6
- package/python/tools/pack.py +0 -5
- package/python/tools/recheck_conflicts.py +156 -0
- package/python/tools/release_check.py +24 -15
- package/python/tools/restart_foxclaw.ps1 +5 -8
- package/python/tools/shared_write_audit.py +1 -15
- package/python/tools/size_check.py +1 -1
- package/python/tools/strip_comments.py +228 -0
- package/python/vendor/hkc/hkc_api/routes/ability.py +31 -0
- package/python/vendor/hkc/hkc_api/schemas.py +11 -0
- package/python/vendor/hkc/hkc_kee/kee.py +124 -3
- package/python/vendor/hkc/hkc_sdk/base.py +22 -0
- package/python/vendor/hkc/hkc_sdk/direct_client.py +10 -0
- package/python/vendor/hkc/hkc_sdk/http_client.py +19 -0
- package/python/foxclaw/api/app.py +0 -123
- package/python/frontend/v3/assets/index-BD7VPbMV.js +0 -33
- package/python/frontend/v3/assets/index-CAvnYWGH.css +0 -1
|
@@ -7,31 +7,23 @@ import os
|
|
|
7
7
|
import sys
|
|
8
8
|
import threading
|
|
9
9
|
from pathlib import Path
|
|
10
|
+
from typing import TYPE_CHECKING
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
import anthropic
|
|
10
14
|
|
|
11
|
-
# 【跨平台启动修复·统一入口】Windows 控制台默认 GBK(cp936),无法编码 ✓/⚠/· 等诊断字符,
|
|
12
|
-
# 打印会抛 UnicodeEncodeError 直接崩溃。典型踩中路径:不经过 api.py 而直接
|
|
13
|
-
# `from foxclaw import Foxclaw` 时,memory_adapter 的 "✓ HMR 已接入" 就会崩(且崩在 except 里,
|
|
14
|
-
# 连降级 MockHMR 都失败)。在包入口把 stdout/stderr 统一为 UTF-8(errors=replace 兜底),
|
|
15
|
-
# 覆盖所有 import foxclaw 的路径。api.py 顶部另有一份同义历史修复,此处为统一入口;幂等无害。
|
|
16
15
|
for _stream in (sys.stdout, sys.stderr):
|
|
17
16
|
try:
|
|
18
|
-
_stream
|
|
17
|
+
_reconf = getattr(_stream, "reconfigure", None)
|
|
18
|
+
if _reconf is not None:
|
|
19
|
+
_reconf(encoding="utf-8", errors="replace")
|
|
19
20
|
except Exception:
|
|
20
21
|
pass
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
# /api/system/info 都引用这里,避免三处各写一个值再漂移(曾出现 system_info 硬编码
|
|
24
|
-
# 1.0.0 而 pyproject/app 是 0.1.0 的不一致)。
|
|
25
|
-
__version__ = "0.1.2"
|
|
23
|
+
__version__ = "0.1.4"
|
|
26
24
|
|
|
27
25
|
from .core.thought_chain import ThoughtChain
|
|
28
26
|
|
|
29
|
-
# 五个 Agent 的导入刻意**不放在模块级**,而是放进 Foxclaw.__init__(惰性)。
|
|
30
|
-
# 原因:agents→base→harness 会连带拉入 sentence-transformers/torch 等重依赖,而
|
|
31
|
-
# `import foxclaw.governance`(治理中间件,只需 harness+thought_chain)不该被迫加载整个
|
|
32
|
-
# Agent 框架。惰性化后:仅用治理内核的场景不会拉入 agents/skills/channels/HMR。
|
|
33
|
-
# (from foxclaw import Foxclaw 仍照常工作——实例化时才导入 Agent 类。)
|
|
34
|
-
|
|
35
27
|
|
|
36
28
|
class Foxclaw:
|
|
37
29
|
"""
|
|
@@ -42,14 +34,14 @@ class Foxclaw:
|
|
|
42
34
|
def __init__(
|
|
43
35
|
self,
|
|
44
36
|
data_dir: str = "./foxclaw_data",
|
|
45
|
-
anthropic_api_key: str = None,
|
|
37
|
+
anthropic_api_key: str | None = None,
|
|
46
38
|
shadow_rounds: int = 20,
|
|
47
39
|
):
|
|
48
40
|
self.data_dir = Path(data_dir).resolve()
|
|
49
41
|
os.makedirs(self.data_dir, exist_ok=True)
|
|
50
42
|
|
|
51
|
-
# Anthropic 客户端(保留兼容;模型选择实际由 ModelRouter 统一探测)
|
|
52
43
|
api_key = anthropic_api_key or os.environ.get("ANTHROPIC_API_KEY")
|
|
44
|
+
self.client: "anthropic.Anthropic | None" = None
|
|
53
45
|
if api_key:
|
|
54
46
|
try:
|
|
55
47
|
import anthropic
|
|
@@ -59,11 +51,6 @@ class Foxclaw:
|
|
|
59
51
|
else:
|
|
60
52
|
self.client = None
|
|
61
53
|
|
|
62
|
-
# HMR 认知运行时(通过适配层接入真实 HMR v2.0,失败降级 Mock)
|
|
63
|
-
# 【P1·share-bge-m3】HKC 优先装配,确保 bge-m3 已入 HKC 共享缓存,
|
|
64
|
-
# 然后 HMR 创建时复用同一 SentenceTransformer 实例,启动期不再重复加载。
|
|
65
|
-
# HKC 装配失败(MODEL 缺失/网络问题) → HMR 仍按 env 链回退到 bge-m3 或 MiniLM,
|
|
66
|
-
# 不影响 HMR 可用性。
|
|
67
54
|
try:
|
|
68
55
|
from .knowledge import init_hkc as _init_hkc_early
|
|
69
56
|
_early_hkc = _init_hkc_early(self.data_dir, api_key)
|
|
@@ -72,18 +59,11 @@ class Foxclaw:
|
|
|
72
59
|
print(f"[Foxclaw] ⚠ 启动期 HKC 预装配失败(继续,降级): {_e}")
|
|
73
60
|
|
|
74
61
|
from .core.memory_adapter import create_memory_backend
|
|
75
|
-
# 【P2-fix】create_memory_backend / HMRAdapter 都从 EMBEDDING_MODEL env 读模型名,
|
|
76
|
-
# 加载时调 get_st_model() 走 HKC 进程缓存,bge-m3 instance 共享(只 load 1 次)
|
|
77
|
-
# —— 所以这里**不再**显式传 embedding_model 参数
|
|
78
62
|
self.hmr = create_memory_backend(
|
|
79
63
|
storage_path=str(self.data_dir / "hmr"),
|
|
80
64
|
llm_api_key=api_key,
|
|
81
65
|
)
|
|
82
66
|
|
|
83
|
-
# 【P1·share-bge-m3】早期 init 只为预热 bge-m3 入 HKC 共享缓存——绝**不留**容器:
|
|
84
|
-
# 否则 HKCContainer 会被钉在 hkc_api.container._container 全局单例(由 init_hkc
|
|
85
|
-
# line 143 注册)+ HKCService 内部 _container 引用,产生泄漏,在 aclose→rmtree 时
|
|
86
|
-
# 留下 events.db/hkc.db 文件锁(PermissionError [WinError 32])。预热完立即关+清。
|
|
87
67
|
if _early_hkc is not None:
|
|
88
68
|
try:
|
|
89
69
|
_early_hkc.close()
|
|
@@ -96,59 +76,42 @@ class Foxclaw:
|
|
|
96
76
|
pass
|
|
97
77
|
_early_hkc = None
|
|
98
78
|
|
|
99
|
-
# 真正持有 HKCService 的是后续 lazy `self.hkc` 路径(line 374-375)。
|
|
100
|
-
# 此处只置 None/False,与 line 336-342 一致。
|
|
101
79
|
self._hkc_service = None
|
|
102
80
|
self._hkc_inited = False
|
|
103
81
|
|
|
104
|
-
# ThoughtChain(共享存储,各 Agent 通过 agent_id 隔离)
|
|
105
82
|
self.thought_chain = ThoughtChain(
|
|
106
83
|
db_path=str(self.data_dir / "thoughtchain.db")
|
|
107
84
|
)
|
|
108
85
|
|
|
109
|
-
# 会话存储:让对话有历史(此前无状态,刷新即丢)。文件式、线程安全。
|
|
110
86
|
from .core.sessions import SessionStore
|
|
111
87
|
self.sessions = SessionStore(self.data_dir / "sessions")
|
|
112
88
|
|
|
113
|
-
# 多模型路由(共享一个 Router,体现 LLM 解耦;自动探测可用模型)
|
|
114
89
|
from .core.model_router import ModelRouter
|
|
115
90
|
self.router = ModelRouter()
|
|
116
91
|
|
|
117
|
-
# Token 计量器(成本可观测性,运行时治理的一块)。挂到 router 上,
|
|
118
|
-
# 所有 LLM 调用经 router.complete() 自动计量,不漏一次。落盘到数据目录。
|
|
119
92
|
from .core.token_meter import TokenMeter
|
|
120
93
|
self.token_meter = TokenMeter(
|
|
121
94
|
log_path=str(self.data_dir / "token_usage.jsonl"))
|
|
122
95
|
self.router.meter = self.token_meter
|
|
123
96
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
97
|
+
from .core.content_guard import ContentGuard
|
|
98
|
+
self.content_guard = ContentGuard()
|
|
99
|
+
self.router.content_guard = self.content_guard
|
|
100
|
+
|
|
127
101
|
from .kernel import EventSpine, CapabilityRegistry, Scheduler
|
|
128
102
|
self.event_spine = EventSpine()
|
|
129
103
|
|
|
130
|
-
# 【v2 ①·微内核调度器】任务准入(并发配额)+ 记账 + task.lifecycle.* 事件。
|
|
131
|
-
# 默认无上限(behavior-preserving);FOXCLAW_MAX_CONCURRENCY 设了才背压。线程级(跨事件循环)。
|
|
132
104
|
_maxc = os.environ.get("FOXCLAW_MAX_CONCURRENCY", "").strip()
|
|
133
105
|
self.scheduler = Scheduler(
|
|
134
106
|
max_concurrency=int(_maxc) if _maxc.isdigit() else None,
|
|
135
107
|
spine=self.event_spine)
|
|
136
108
|
|
|
137
|
-
# 【P1-2·统一治理审计】governance.decision 事件流 + {data_dir}/audit/governance.jsonl
|
|
138
|
-
# 落盘。须在 PolicyGate/Orchestrator 之前建好(二者都注入它);FOXCLAW_GOVERNANCE_AUDIT=0
|
|
139
|
-
# 时 attach 返回 None,各层按 None 跳过记录(零开销降级)。
|
|
140
109
|
from .core.governance_audit import attach as _attach_gov_audit
|
|
141
110
|
self.governance_auditor = _attach_gov_audit(self.event_spine, self.data_dir)
|
|
142
111
|
|
|
143
|
-
# 【v2 ① Step4·统一准入门】把 Harness(推理漂移)与 skill-guard(动作权限)统一到
|
|
144
|
-
# 一套 Verdict 词汇 + 一个接口。evaluate 注入(避免 kernel→skills 环)。
|
|
145
|
-
# 【P3-4】auditor 注入后,每次裁决带 decision_id 并记入治理审计流(非 allow)。
|
|
146
112
|
from .kernel import PolicyGate
|
|
147
113
|
from .skills.permission import evaluate as _perm_eval
|
|
148
114
|
|
|
149
|
-
# 【P-Muse·守门人进程化】FOXCLAW_GUARDIAN=1 时,把动作裁决从「进程内 evaluate」换成
|
|
150
|
-
# 「跨进程 Sentinel」:规则表与真实凭证只存在于守门人进程,主进程(Agent)被 RCE 也
|
|
151
|
-
# 改不了规则、读不到凭证。默认 0 = 原进程内路径(behavior-preserving)。
|
|
152
115
|
action_evaluator = _perm_eval
|
|
153
116
|
self.sentinel = None
|
|
154
117
|
if os.environ.get("FOXCLAW_GUARDIAN", "0").strip() in ("1", "true", "True", "yes", "on"):
|
|
@@ -158,8 +121,6 @@ class Foxclaw:
|
|
|
158
121
|
self.policy_gate = PolicyGate(spine=self.event_spine, action_evaluator=action_evaluator,
|
|
159
122
|
auditor=self.governance_auditor)
|
|
160
123
|
|
|
161
|
-
# 【P1-3·能力注册中枢】Composition Root 的解析中枢:组件按名解析同伴,不再直接互持。
|
|
162
|
-
# 必须在 Agent 之前建好并注入,以打断 agents⇄search_runtime、Supervisor⇄Orchestrator 两处直接环。
|
|
163
124
|
self.capabilities = CapabilityRegistry()
|
|
164
125
|
self.capabilities.register("event_spine", self.event_spine)
|
|
165
126
|
self.capabilities.register("scheduler", self.scheduler)
|
|
@@ -170,32 +131,25 @@ class Foxclaw:
|
|
|
170
131
|
if self.sentinel is not None:
|
|
171
132
|
self.capabilities.register("sentinel", self.sentinel)
|
|
172
133
|
|
|
173
|
-
# 【P0-3·共享层写入策略】ingest(shared=True) 此前**零校验**:共享层是所有 Agent
|
|
174
|
-
# 都能召回的区域,于是"任何 Agent 都能往里写"就等于一条绕开守门人的跨 Agent
|
|
175
|
-
# 注入通道(典型:办公 Agent 写「财务应给办公转账」,财务下轮召回到就受影响)。
|
|
176
|
-
# 这里挂上策略与审计出口。
|
|
177
|
-
# 默认 audit:只发 memory.shared.write 事件、不拦截 —— 现有 8 处**合法**共享
|
|
178
|
-
# 写入(主管分发 6 / 偏好共享 1 / 真值沉淀 1)需要先在真实流量上验证策略表,
|
|
179
|
-
# 再切 enforce。FOXCLAW_SHARED_WRITE_MODE=warn|enforce 可切换。
|
|
180
|
-
# 挂载时机:必须在 event_spine 之后(第 80 行才建脊柱),且在 Agent 之前
|
|
181
|
-
# (下面才构造 Agent) —— 否则会漏掉装配期自身的写入。
|
|
182
134
|
from .core.shared_write import SharedWritePolicy, attach as _attach_shared_write
|
|
183
135
|
self.shared_write_policy = SharedWritePolicy()
|
|
184
136
|
_attach_shared_write(self.hmr, self.shared_write_policy,
|
|
185
137
|
spine=self.event_spine)
|
|
186
138
|
|
|
187
|
-
# 【P0-3·审计落盘】脊柱 history 是进程内环形缓冲(deque maxlen=1000,全系统共享、
|
|
188
|
-
# 不落盘)—— 高频事件会很快把 memory.shared.write 冲掉,进程退出即全丢。而切
|
|
189
|
-
# enforce 的判据是「真实流量中有没有合法写入被拒」,没落盘这个样本永远积累不起来。
|
|
190
|
-
# 把审计事件逐条 JSON 追加到 {data_dir}/audit/shared_write.jsonl;写失败静默
|
|
191
|
-
# (审计绝不影响主流程)。FOXCLAW_SHARED_WRITE_AUDIT=0 可关。
|
|
192
|
-
# 挂载时机:在 _attach_shared_write 之后、Agent 构造之前 → 装配期与运行期的
|
|
193
|
-
# 共享写入全量覆盖。
|
|
194
139
|
from .core.audit_sink import attach as _attach_shared_write_audit
|
|
195
140
|
self.shared_write_audit = _attach_shared_write_audit(
|
|
196
141
|
self.event_spine, self.data_dir)
|
|
197
142
|
|
|
198
|
-
|
|
143
|
+
from .core.sensitive_write import (
|
|
144
|
+
PENDING_FILE,
|
|
145
|
+
SensitiveWritePolicy,
|
|
146
|
+
attach as _attach_sensitive_write,
|
|
147
|
+
)
|
|
148
|
+
self.sensitive_write_policy = SensitiveWritePolicy(
|
|
149
|
+
pending_path=Path(self.data_dir) / PENDING_FILE)
|
|
150
|
+
_attach_sensitive_write(self.hmr, self.sensitive_write_policy,
|
|
151
|
+
spine=self.event_spine)
|
|
152
|
+
|
|
199
153
|
from .agents.agents import SupervisorAgent, FinanceAgent, OfficeAgent
|
|
200
154
|
agent_kwargs = dict(
|
|
201
155
|
hmr=self.hmr,
|
|
@@ -211,22 +165,18 @@ class Foxclaw:
|
|
|
211
165
|
self.finance = FinanceAgent(**agent_kwargs)
|
|
212
166
|
self.office = OfficeAgent(**agent_kwargs)
|
|
213
167
|
|
|
214
|
-
# Agent 拓扑 5 → 3:assistant/admin 已下放为指令技能(skills/general-assistant、
|
|
215
|
-
# skills/schedule-itinerary),不再常驻;行政的行程内核并入办公学习 Agent。
|
|
216
168
|
self._agents = {
|
|
217
169
|
"supervisor": self.supervisor,
|
|
218
170
|
"finance": self.finance,
|
|
219
171
|
"office": self.office,
|
|
220
172
|
}
|
|
221
|
-
self.capabilities.register("agents", self._agents)
|
|
173
|
+
self.capabilities.register("agents", self._agents)
|
|
222
174
|
|
|
223
|
-
# 给主管注入子 Agent 登记表,使其能真实调度(D 阶段)
|
|
224
175
|
self.supervisor.register_subagents({
|
|
225
176
|
"finance": self.finance,
|
|
226
177
|
"office": self.office,
|
|
227
178
|
})
|
|
228
179
|
|
|
229
|
-
# 给财务 Agent 挂载财务内核(B 阶段:记忆+推理+Policy 守门人)
|
|
230
180
|
from .finance import FinanceRuntime
|
|
231
181
|
self.finance_runtime = FinanceRuntime(
|
|
232
182
|
policy_path=str(self.data_dir / "finance_policy.yaml"),
|
|
@@ -234,143 +184,96 @@ class Foxclaw:
|
|
|
234
184
|
)
|
|
235
185
|
self.finance.attach_runtime(self.finance_runtime)
|
|
236
186
|
|
|
237
|
-
# 给办公 Agent 挂载学习内核(B 阶段:间隔重复 + 知识图谱)
|
|
238
187
|
from .learning import LearningRuntime
|
|
239
188
|
self.learning_runtime = LearningRuntime(
|
|
240
189
|
hmr=self.hmr, hkc_provider=lambda: self.hkc,
|
|
241
190
|
persist_path=str(self.data_dir / "learning.json"))
|
|
242
191
|
self.office.attach_runtime(self.learning_runtime)
|
|
243
192
|
|
|
244
|
-
# 行程内核(路线优化 + 可达性 + 守门人 + 高德接口)现在挂在办公学习 Agent 上 ——
|
|
245
|
-
# 行政 Agent 下放后,领域内核仍归"离内核最近"的 Agent,编排层不直接读它。
|
|
246
193
|
from .itinerary import ItineraryRuntime
|
|
247
194
|
self.itinerary_runtime = ItineraryRuntime(
|
|
248
195
|
amap_key=os.environ.get("AMAP_MAPS_API_KEY"), hmr=self.hmr)
|
|
249
196
|
self.office.attach_itinerary(self.itinerary_runtime)
|
|
250
197
|
|
|
251
|
-
# 给主管挂载编排引擎(全局态势 + 跨 Agent 编排)
|
|
252
|
-
# 【P1-3】编排器经注册中枢惰性解析 agents,不再直接持有含主管的 agents 字典;
|
|
253
|
-
# 裁决经 auditor 记入治理审计流(原本完全无观测)。
|
|
254
198
|
from .orchestration import Orchestrator
|
|
255
199
|
self.orchestrator = Orchestrator(self.capabilities, auditor=self.governance_auditor)
|
|
256
200
|
self.supervisor.attach_orchestrator(self.orchestrator)
|
|
257
201
|
self.capabilities.register("orchestrator", self.orchestrator)
|
|
258
202
|
|
|
259
|
-
# 给助手挂载 Skill 接入层(通用工具箱 + 对外生态兼容层)
|
|
260
203
|
from .skills import build_default_registry, SkillAdapter
|
|
261
204
|
self.skill_registry = build_default_registry()
|
|
262
205
|
from .skills.store import SkillStore
|
|
263
206
|
self.skill_store = SkillStore(self.data_dir)
|
|
264
|
-
self.skill_store.load(self.skill_registry)
|
|
265
|
-
# 关键操作(send/pay/destructive)二次确认:HTTP 入口经此签发/校验一次性 nonce。
|
|
266
|
-
# 事件循环里也走同一实例(asyncio.to_thread 复制 contextvars,不含本对象但没关系:
|
|
267
|
-
# broker 自身线程安全)。
|
|
207
|
+
self.skill_store.load(self.skill_registry)
|
|
268
208
|
from .core.confirm_nonce import ConfirmationBroker
|
|
269
209
|
self.confirm_broker = ConfirmationBroker()
|
|
270
210
|
self.skill_adapter = SkillAdapter(
|
|
271
211
|
self.skill_registry, mcp_clients={}, policy_gate=self.policy_gate,
|
|
272
212
|
sentinel=self.sentinel, confirms=self.confirm_broker)
|
|
273
|
-
# MCP 连接器(第2期):把连上的 MCP 客户端接到 SkillAdapter.mcp_clients
|
|
274
213
|
from .skills.mcp_client import McpManager
|
|
275
214
|
from .skills.mcp_store import McpStore
|
|
276
215
|
self.mcp_manager = McpManager()
|
|
277
216
|
self.mcp_store = McpStore(self.data_dir)
|
|
278
217
|
for _cfg in self.mcp_store.load():
|
|
279
|
-
self.mcp_manager.restore(_cfg)
|
|
280
|
-
# 注:磁盘配置里的 headers 只在回退路径(FOXCLAW_GUARDIAN=0)下才存在;
|
|
281
|
-
# 守门人模式落盘的是无 headers 的元数据,真凭证由守门人侧文件持有。
|
|
218
|
+
self.mcp_manager.restore(_cfg)
|
|
282
219
|
self.skill_adapter.mcp_clients = self.mcp_manager
|
|
283
|
-
# 【P-Muse·第3步】MCP server 配置(含 headers 凭证)同步给守门人:代执行时守门人持有凭证。
|
|
284
220
|
self.sync_mcp_servers_to_sentinel()
|
|
285
|
-
# 全局技能封杀策略(deny/allow kill-switch):唯一咽喉在 adapter.invoke + md match
|
|
286
221
|
from .skills.policy import SkillPolicy
|
|
287
222
|
self.skill_policy = SkillPolicy(self.data_dir)
|
|
288
223
|
self.skill_adapter.skill_policy = self.skill_policy
|
|
289
|
-
# SKILL.md 指令技能(第3期):挂到全部 agent,注入其提示词
|
|
290
224
|
from .skills.markdown_skill import MarkdownSkillRegistry
|
|
291
225
|
from .skills.markdown_store import MarkdownStore
|
|
292
226
|
self.md_skills = MarkdownSkillRegistry()
|
|
293
227
|
self.md_skills.policy = self.skill_policy
|
|
294
228
|
self.md_store = MarkdownStore(self.data_dir)
|
|
295
229
|
self.md_store.load(self.md_skills)
|
|
296
|
-
# 随包分发的内置指令技能(frontmatter builtin: true):general-assistant /
|
|
297
|
-
# schedule-itinerary —— 原 assistant / admin 两个 Agent 的"新家"。
|
|
298
|
-
# 用户已安装同 id 技能优先,内置只是缺省值;仓库不在(wheel 安装)时静默跳过。
|
|
299
230
|
from .skills.markdown_skill import load_builtin_dir
|
|
300
231
|
load_builtin_dir(self.md_skills, Path(__file__).resolve().parent.parent / "skills")
|
|
301
232
|
for _ag in self._agents.values():
|
|
302
233
|
_ag.md_skills = self.md_skills
|
|
303
|
-
# E2:统一 HookManager(skill_adapter 持有的单例)注入所有 Agent → run_task 的认知管线
|
|
304
|
-
# 能 fire pre_reasoning(agent_start)/task_complete(agent_end)钩子。全 Agent 共享同一套。
|
|
305
234
|
_ag.hooks = self.skill_adapter.hooks
|
|
306
|
-
# 技能工具箱已上移基类:三个 Agent 都能挂载并调用 skill
|
|
307
235
|
_ag.attach_skills(self.skill_registry, self.skill_adapter)
|
|
308
|
-
# 主管也持有 skill 注册表(供 planner 判断任务是否需要拆解)
|
|
309
236
|
self.supervisor.skill_registry = self.skill_registry
|
|
310
|
-
# AHS 执行链编排器(router→planner→composition,全程过 guard+breaker+hooks)
|
|
311
237
|
from .skills.pipeline import AHSPipeline
|
|
312
238
|
self.ahs_pipeline = AHSPipeline(self.skill_registry, self.skill_adapter)
|
|
313
|
-
# 系统事件总线 + 真值管线(执行→审计→记忆 自动流转)
|
|
314
239
|
from .core.system_bus import SystemBus
|
|
315
240
|
from .core.truth_pipeline import TruthPipeline
|
|
316
|
-
# 与 harness 共用同一条脊柱:系统事件与 harness 事件汇流,单一投影统一观测
|
|
317
241
|
self.system_bus = SystemBus(spine=self.event_spine)
|
|
318
242
|
self.truth_pipeline = TruthPipeline(self.system_bus, hmr=self.hmr)
|
|
319
|
-
# 【v2 ④ Step1·真值走 HKC/KEE】叠加路径:truth.appended 时后台把真值经 KDE→KEE 结晶进 HKC
|
|
320
|
-
# (HMR 共享写保留=双写)。默认关(FOXCLAW_TRUTH_TO_HKC=1 才开;KDE 有 LLM 成本)。
|
|
321
243
|
from .knowledge import TruthCrystallizer
|
|
322
244
|
self.truth_crystallizer = TruthCrystallizer(
|
|
323
245
|
hkc_provider=lambda: self.hkc,
|
|
324
246
|
enabled=os.environ.get("FOXCLAW_TRUTH_TO_HKC", "") == "1")
|
|
325
247
|
self.system_bus.subscribe("truth.appended", self.truth_crystallizer.on_truth,
|
|
326
|
-
exact=True)
|
|
327
|
-
# 进程管理器:把每个任务当进程管理(状态机 + 历史)。它是唯一 Lifecycle 后端,
|
|
328
|
-
# emit 唯一词汇 task.lifecycle.<state>(Step3)。
|
|
248
|
+
exact=True)
|
|
329
249
|
from .kernel import ProcessManager
|
|
330
250
|
self.process_manager = ProcessManager(bus=self.system_bus)
|
|
331
|
-
# 【Step3】调度器委托 pm 管生命周期:顶层任务经调度器→pm 建进程,与并行分发的子任务
|
|
332
|
-
# 共用同一状态机/同一 task.lifecycle.* 事件流(消除 process.state_changed 与 task.lifecycle 双词汇)。
|
|
333
251
|
self.scheduler.process_manager = self.process_manager
|
|
334
|
-
# 认知状态快照:聚合"系统此刻在想什么"(订阅总线自动更新)
|
|
335
252
|
from .cognitive import CognitiveStateTracker
|
|
336
253
|
self.cognitive_state = CognitiveStateTracker(
|
|
337
254
|
self.system_bus, process_manager=self.process_manager)
|
|
338
|
-
# 把总线和进程管理器交给主管
|
|
339
255
|
if "supervisor" in self._agents:
|
|
340
256
|
self._agents["supervisor"]._system_bus = self.system_bus
|
|
341
257
|
self._agents["supervisor"]._process_manager = self.process_manager
|
|
342
258
|
|
|
343
|
-
# 崩溃/重启恢复:启动时扫描上次被中断的任务
|
|
344
259
|
from .core.recovery import RestartRecovery
|
|
345
260
|
self.recovery = RestartRecovery(
|
|
346
261
|
log_path=str(self.data_dir / "task_log.jsonl"),
|
|
347
|
-
spine=self.event_spine)
|
|
348
|
-
# 恢复记账注入**所有** Agent(不只主管):此前只有主管接线,领域 Agent 跑一半
|
|
349
|
-
# 崩溃在恢复扫描里不可见,也没有步骤级 checkpoint 可供断点续跑复用。
|
|
262
|
+
spine=self.event_spine)
|
|
350
263
|
for _a in self._agents.values():
|
|
351
264
|
_a._recovery = self.recovery
|
|
352
265
|
self.interrupted_tasks = self.recovery.scan_interrupted()
|
|
353
|
-
# 【P2-4】生成恢复计划(retryable/notify_user/needs_manual),供 API 层/前端消费
|
|
354
266
|
self.recovery_plan = self.recovery.plan_recovery(self.interrupted_tasks)
|
|
355
267
|
|
|
356
|
-
# 浏览器子系统(HIR-Browser 融入):"AI 用的浏览器"。async 安全 + 可选依赖优雅降级。
|
|
357
|
-
# 构造极轻(不开线程/不 launch chromium),首次 perceive/act 时才在专用工作线程上
|
|
358
|
-
# 惰性开会话;playwright 缺失时 available()=False,路由降级而非崩溃。
|
|
359
268
|
from .browser import BrowserService
|
|
360
269
|
self.browser_service = BrowserService(
|
|
361
270
|
headless=os.environ.get("FOXCLAW_BROWSER_HEADLESS", "1") != "0")
|
|
362
271
|
|
|
363
|
-
# HKC 知识核心(vendor/hkc,同进程 DirectClient)。**惰性装配**:首次访问 self.hkc
|
|
364
|
-
# 才装配——否则每次构造 OS 都会在 data_dir/hkc 开 4 个 SQLite 句柄,现有测试
|
|
365
|
-
# rmtree(tmpdir) 时会踩 Windows WinError 32。缺依赖/装配失败时降级 None。
|
|
366
272
|
self._hkc_api_key = api_key
|
|
367
273
|
self._hkc_service = None
|
|
368
274
|
self._hkc_inited = False
|
|
369
|
-
self._hkc_lock = threading.Lock()
|
|
275
|
+
self._hkc_lock = threading.Lock()
|
|
370
276
|
|
|
371
|
-
# 认知搜索内核(HSR):统一"查 HMR 记忆 + 查 HKC 知识 + 联网搜索",五个 Agent 共享。
|
|
372
|
-
# 【P1-3】改由能力中枢惰性解析(register_lazy):首次 get 才构造,构造在 _build_search_runtime。
|
|
373
|
-
# Agent 经 self._caps.get("search_runtime") 反向取用,不再被直接注入 _search_runtime(打断直接环)。
|
|
374
277
|
self.capabilities.register_lazy("hkc", lambda: self.hkc)
|
|
375
278
|
self.capabilities.register_lazy("search_runtime", self._build_search_runtime)
|
|
376
279
|
|
|
@@ -400,8 +303,6 @@ class Foxclaw:
|
|
|
400
303
|
if not self._hkc_inited:
|
|
401
304
|
from .knowledge import init_hkc, bridge_hkc_events
|
|
402
305
|
self._hkc_service = init_hkc(self.data_dir, self._hkc_api_key)
|
|
403
|
-
# 【HKC 融合·事件面】把 HKC 内部 EventBus 桥接到统一 EventSpine,消除"事件孤岛"
|
|
404
|
-
# (知识演化对 WS/robot/history 可见)。降级/失败静默,不影响 HKC 可用性。
|
|
405
306
|
if self._hkc_service is not None:
|
|
406
307
|
bridge_hkc_events(self._hkc_service.event_bus, self.event_spine)
|
|
407
308
|
self._hkc_inited = True
|
|
@@ -420,8 +321,9 @@ class Foxclaw:
|
|
|
420
321
|
"""【P-Muse·第4步】把当前 MCP server 元数据同步给守门人(全量)。
|
|
421
322
|
主进程不再长期持有 headers,故这里只推元数据;守门人侧 headers 空则保留旧值,
|
|
422
323
|
add_server 那次由 mcp_add_server_guardian 临时带一次 headers 覆盖。"""
|
|
423
|
-
|
|
424
|
-
|
|
324
|
+
sent = getattr(self, "sentinel", None)
|
|
325
|
+
if sent is not None and getattr(self, "mcp_manager", None) is not None:
|
|
326
|
+
sent.mcp_register(self.mcp_manager.servers())
|
|
425
327
|
|
|
426
328
|
def mcp_add_server_guardian(self, cfg: dict) -> list:
|
|
427
329
|
"""【P-Muse·第4步】守门人模式下接入 MCP server 的发现流程:
|
|
@@ -429,12 +331,15 @@ class Foxclaw:
|
|
|
429
331
|
只存非敏感元数据(id/name/url/transport)。发现(list_tools)由守门人代执行。"""
|
|
430
332
|
meta = {k: cfg[k] for k in ("id", "name", "url", "transport")}
|
|
431
333
|
full = [dict(s) for s in self.mcp_manager.servers()]
|
|
432
|
-
full = [s for s in full if s.get("id") != cfg["id"]]
|
|
433
|
-
full.append(cfg)
|
|
434
|
-
self.sentinel
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
334
|
+
full = [s for s in full if s.get("id") != cfg["id"]]
|
|
335
|
+
full.append(cfg)
|
|
336
|
+
sent = self.sentinel
|
|
337
|
+
if sent is None:
|
|
338
|
+
raise RuntimeError("守门人未启用(FOXCLAW_GUARDIAN=0),无法走守门人模式接入 MCP")
|
|
339
|
+
sent.mcp_register(full)
|
|
340
|
+
tools = sent.mcp_list_tools(cfg["id"])
|
|
341
|
+
self.mcp_manager.restore(meta)
|
|
342
|
+
self.mcp_store.save(self.mcp_manager.servers())
|
|
438
343
|
return tools
|
|
439
344
|
|
|
440
345
|
@property
|
|
@@ -447,7 +352,7 @@ class Foxclaw:
|
|
|
447
352
|
由 CapabilityRegistry 首次解析 "search_runtime" 时调用并缓存。
|
|
448
353
|
Agent 侧经 self._caps.get("search_runtime") 反向取用,故此处**不再注入** Agent。"""
|
|
449
354
|
from .search import SearchRuntime, HKCReadPort, AgentSourcePort
|
|
450
|
-
hkc = self.hkc
|
|
355
|
+
hkc = self.hkc
|
|
451
356
|
kp = HKCReadPort(client=hkc.client) if (hkc and hkc.available) else None
|
|
452
357
|
ap = AgentSourcePort(
|
|
453
358
|
agents=self._agents,
|
|
@@ -463,6 +368,32 @@ class Foxclaw:
|
|
|
463
368
|
def get_agent(self, agent_id: str):
|
|
464
369
|
return self._agents.get(agent_id)
|
|
465
370
|
|
|
371
|
+
def list_sensitive_pending(self) -> list:
|
|
372
|
+
"""待用户同意的高敏感记忆写入。未挂策略时返回空列表。"""
|
|
373
|
+
policy = getattr(self, "sensitive_write_policy", None)
|
|
374
|
+
return policy.pending.list() if policy is not None else []
|
|
375
|
+
|
|
376
|
+
def approve_sensitive_write(self, pending_id: str) -> bool:
|
|
377
|
+
"""用户同意后落库。这是全仓唯一允许传 consent=True 的地方。"""
|
|
378
|
+
policy = getattr(self, "sensitive_write_policy", None)
|
|
379
|
+
if policy is None:
|
|
380
|
+
return False
|
|
381
|
+
entry = policy.pending.pop(pending_id)
|
|
382
|
+
if entry is None:
|
|
383
|
+
return False
|
|
384
|
+
self.hmr.ingest(
|
|
385
|
+
entry.get("content", ""),
|
|
386
|
+
type=entry.get("type", "concept"),
|
|
387
|
+
agent_id=entry.get("agent_id") or None,
|
|
388
|
+
consent=True,
|
|
389
|
+
)
|
|
390
|
+
return True
|
|
391
|
+
|
|
392
|
+
def reject_sensitive_write(self, pending_id: str) -> bool:
|
|
393
|
+
"""用户拒绝:丢弃待确认项,不落库。"""
|
|
394
|
+
policy = getattr(self, "sensitive_write_policy", None)
|
|
395
|
+
return bool(policy is not None and policy.pending.pop(pending_id) is not None)
|
|
396
|
+
|
|
466
397
|
def get_all_status(self) -> dict:
|
|
467
398
|
return {
|
|
468
399
|
agent_id: agent.get_status()
|
|
@@ -470,7 +401,7 @@ class Foxclaw:
|
|
|
470
401
|
}
|
|
471
402
|
|
|
472
403
|
async def run(self, task: str, agent_id: str = "supervisor",
|
|
473
|
-
context: dict = None) -> dict:
|
|
404
|
+
context: dict | None = None) -> dict:
|
|
474
405
|
"""
|
|
475
406
|
快捷运行接口
|
|
476
407
|
默认由主管 Agent 处理,它会根据任务类型决定路由
|
|
@@ -492,29 +423,20 @@ class Foxclaw:
|
|
|
492
423
|
await svc.close()
|
|
493
424
|
except Exception:
|
|
494
425
|
pass
|
|
495
|
-
# 真值结晶器:关停后台线程池(即使从未启用也安全,幂等)
|
|
496
426
|
tc = getattr(self, "truth_crystallizer", None)
|
|
497
427
|
if tc is not None:
|
|
498
428
|
try:
|
|
499
429
|
tc.close()
|
|
500
430
|
except Exception:
|
|
501
431
|
pass
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
# SQLite 连接在 CPython 引用计数下不会立即 close,残留 events.db/hkc.db 文件锁
|
|
506
|
-
# 2) hkc_api.container._container (vendor 全局单例) — foxclaw.knowledge.init_hkc
|
|
507
|
-
# 第 143 行把它注册进去,reset_container() 才会同时关 SQLite + 清引用。
|
|
508
|
-
# 不清会导致 lifespan teardown 后的 rmtree 报 PermissionError [WinError 32]。
|
|
509
|
-
hkc_was_inited = getattr(self, "_hkc_inited", False) or getattr(self, "_hkc_service", None) is not None
|
|
510
|
-
if getattr(self, "_hkc_service", None) is not None:
|
|
432
|
+
hkc_svc = getattr(self, "_hkc_service", None)
|
|
433
|
+
hkc_was_inited = getattr(self, "_hkc_inited", False) or hkc_svc is not None
|
|
434
|
+
if hkc_svc is not None:
|
|
511
435
|
try:
|
|
512
|
-
|
|
436
|
+
hkc_svc.close()
|
|
513
437
|
except Exception:
|
|
514
438
|
pass
|
|
515
439
|
self._hkc_service = None
|
|
516
|
-
# vendor 全局单例 — 即使 lazy hkc 没被访问,启动期 Foxclaw.__init__ 早期预装配
|
|
517
|
-
# (line 67-85) 已经把 container 塞进 _cm._container;aclose 必须清。
|
|
518
440
|
if hkc_was_inited:
|
|
519
441
|
try:
|
|
520
442
|
from hkc_api.container import reset_container
|