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.
Files changed (368) hide show
  1. package/index.js +1 -8
  2. package/package.json +1 -1
  3. package/python/.env.example +8 -0
  4. package/python/.github/workflows/ci.yml +47 -2
  5. package/python/.github/workflows/release.yml +5 -0
  6. package/python/CHANGELOG.md +100 -0
  7. package/python/CHANGELOG_CN.md +100 -0
  8. package/python/README.md +17 -2
  9. package/python/README_CN.md +38 -20
  10. package/python/bench/bm25_audit.py +1 -14
  11. package/python/bench/bm25_regression.py +0 -1
  12. package/python/bench/bm25_stress.py +0 -4
  13. package/python/bench/cd_audit.py +0 -23
  14. package/python/bench/chaos.py +6 -8
  15. package/python/bench/e2e_real_models.py +0 -1
  16. package/python/bench/llm_retry_test.py +2 -9
  17. package/python/bench/load_test.py +5 -8
  18. package/python/bench/vector_ivf_upgrade.py +1 -7
  19. package/python/docs/CONTENT_GUARD.md +189 -0
  20. package/python/docs/CONTENT_GUARD_CN.md +189 -0
  21. package/python/docs/RELEASE.md +78 -37
  22. package/python/docs/RUN_CN.md +2 -2
  23. package/python/evals/governance/cases.py +0 -37
  24. package/python/evals/governance/run_evals.py +5 -37
  25. package/python/examples/governance_middleware_demo.py +1 -6
  26. package/python/examples/langgraph_governance_demo.py +7 -11
  27. package/python/foxclaw/__init__.py +74 -152
  28. package/python/foxclaw/agents/base.py +82 -136
  29. package/python/foxclaw/agents/impl/_helpers.py +2 -10
  30. package/python/foxclaw/agents/impl/finance.py +4 -21
  31. package/python/foxclaw/agents/impl/office.py +7 -24
  32. package/python/foxclaw/agents/impl/supervisor.py +12 -79
  33. package/python/foxclaw/agents/mixins/_context.py +0 -23
  34. package/python/foxclaw/agents/mixins/augment.py +10 -16
  35. package/python/foxclaw/agents/mixins/llm.py +7 -6
  36. package/python/foxclaw/agents/mixins/memory.py +23 -3
  37. package/python/foxclaw/agents/mixins/preference.py +58 -8
  38. package/python/foxclaw/agents/mixins/prompt.py +59 -12
  39. package/python/foxclaw/agents/mixins/skillbox.py +6 -6
  40. package/python/foxclaw/agents/mixins/tool_exec.py +16 -42
  41. package/python/foxclaw/agents/rules/finance/RULES.md +131 -0
  42. package/python/foxclaw/api/__init__.py +18 -77
  43. package/python/foxclaw/api/_state.py +43 -0
  44. package/python/foxclaw/api/bootstrap.py +114 -0
  45. package/python/foxclaw/api/middleware.py +5 -28
  46. package/python/foxclaw/api/routes/ahs.py +60 -73
  47. package/python/foxclaw/api/routes/attachments.py +5 -6
  48. package/python/foxclaw/api/routes/browser.py +7 -11
  49. package/python/foxclaw/api/routes/domain.py +18 -26
  50. package/python/foxclaw/api/routes/knowledge.py +3 -21
  51. package/python/foxclaw/api/routes/llm_admin.py +21 -33
  52. package/python/foxclaw/api/routes/memory.py +50 -12
  53. package/python/foxclaw/api/routes/orchestration.py +3 -5
  54. package/python/foxclaw/api/routes/recovery.py +4 -3
  55. package/python/foxclaw/api/routes/review.py +2 -11
  56. package/python/foxclaw/api/routes/search.py +3 -7
  57. package/python/foxclaw/api/routes/system.py +33 -49
  58. package/python/foxclaw/api/routes/task.py +31 -49
  59. package/python/foxclaw/api/routes/ws.py +2 -6
  60. package/python/foxclaw/api/spine.py +1 -4
  61. package/python/foxclaw/behavior/__init__.py +20 -0
  62. package/python/foxclaw/behavior/behavior.yaml +62 -0
  63. package/python/foxclaw/behavior/spec.py +287 -0
  64. package/python/foxclaw/browser/action/action_executor.py +1 -5
  65. package/python/foxclaw/browser/action/action_types.py +0 -1
  66. package/python/foxclaw/browser/action/url_policy.py +1 -5
  67. package/python/foxclaw/browser/adapter/web_adapter.py +1 -1
  68. package/python/foxclaw/browser/driver/playwright_driver.py +20 -16
  69. package/python/foxclaw/browser/perception/dom_extractor.py +0 -1
  70. package/python/foxclaw/browser/perception/element_indexer.py +1 -2
  71. package/python/foxclaw/browser/service.py +7 -11
  72. package/python/foxclaw/capability/embedding_service.py +4 -6
  73. package/python/foxclaw/channels/__init__.py +9 -11
  74. package/python/foxclaw/channels/discord.py +4 -13
  75. package/python/foxclaw/channels/dispatcher.py +2 -27
  76. package/python/foxclaw/channels/feishu.py +10 -20
  77. package/python/foxclaw/channels/openclaw_bridge.py +4 -9
  78. package/python/foxclaw/channels/signing.py +9 -9
  79. package/python/foxclaw/channels/telegram.py +3 -4
  80. package/python/foxclaw/channels/weixin.py +7 -17
  81. package/python/foxclaw/cli.py +42 -13
  82. package/python/foxclaw/cognitive/reasoner.py +9 -10
  83. package/python/foxclaw/cognitive/state.py +18 -24
  84. package/python/foxclaw/core/audit_chain.py +1 -3
  85. package/python/foxclaw/core/audit_sink.py +3 -3
  86. package/python/foxclaw/core/compaction.py +8 -26
  87. package/python/foxclaw/core/config.py +2 -2
  88. package/python/foxclaw/core/confirm_nonce.py +6 -12
  89. package/python/foxclaw/core/content_guard.py +341 -0
  90. package/python/foxclaw/core/envfile.py +2 -2
  91. package/python/foxclaw/core/failure_policy.py +17 -26
  92. package/python/foxclaw/core/gateway.py +6 -10
  93. package/python/foxclaw/core/governance_audit.py +3 -7
  94. package/python/foxclaw/core/idempotency.py +15 -22
  95. package/python/foxclaw/core/injection_guard.py +10 -29
  96. package/python/foxclaw/core/memory_adapter.py +53 -60
  97. package/python/foxclaw/core/metrics.py +4 -5
  98. package/python/foxclaw/core/model_router.py +74 -150
  99. package/python/foxclaw/core/paths.py +1 -1
  100. package/python/foxclaw/core/recovery.py +21 -34
  101. package/python/foxclaw/core/scheduler.py +23 -26
  102. package/python/foxclaw/core/secrets.py +0 -3
  103. package/python/foxclaw/core/sensitive_write.py +283 -0
  104. package/python/foxclaw/core/sessions.py +4 -6
  105. package/python/foxclaw/core/settings.py +16 -32
  106. package/python/foxclaw/core/shared_write.py +11 -23
  107. package/python/foxclaw/core/steering_queue.py +12 -11
  108. package/python/foxclaw/core/system_bus.py +1 -1
  109. package/python/foxclaw/core/thought_chain.py +30 -32
  110. package/python/foxclaw/core/token_meter.py +9 -13
  111. package/python/foxclaw/core/tools.py +4 -12
  112. package/python/foxclaw/core/tracing.py +25 -40
  113. package/python/foxclaw/core/truth_pipeline.py +5 -9
  114. package/python/foxclaw/core/update_check.py +149 -0
  115. package/python/foxclaw/core/vec_recall_adapter.py +11 -37
  116. package/python/foxclaw/core/verify.py +6 -14
  117. package/python/foxclaw/core/web_search.py +4 -28
  118. package/python/foxclaw/finance/evidence.py +14 -18
  119. package/python/foxclaw/finance/fx.py +1 -3
  120. package/python/foxclaw/finance/graph.py +9 -18
  121. package/python/foxclaw/finance/models.py +20 -24
  122. package/python/foxclaw/finance/nlparse.py +12 -72
  123. package/python/foxclaw/finance/policy.py +8 -36
  124. package/python/foxclaw/finance/reasoner.py +3 -11
  125. package/python/foxclaw/finance/reasoning.py +13 -34
  126. package/python/foxclaw/finance/reconcile.py +5 -10
  127. package/python/foxclaw/finance/runtime.py +19 -46
  128. package/python/foxclaw/governance/middleware.py +13 -21
  129. package/python/foxclaw/harness/_base.py +2 -8
  130. package/python/foxclaw/harness/_policy.py +19 -38
  131. package/python/foxclaw/harness/drift.py +1 -13
  132. package/python/foxclaw/harness/event_bus.py +2 -5
  133. package/python/foxclaw/harness/harness.py +9 -97
  134. package/python/foxclaw/harness/loop_detection.py +11 -18
  135. package/python/foxclaw/itinerary/amap_client.py +2 -5
  136. package/python/foxclaw/itinerary/models.py +7 -7
  137. package/python/foxclaw/itinerary/reasoner.py +1 -6
  138. package/python/foxclaw/itinerary/reasoning.py +15 -21
  139. package/python/foxclaw/itinerary/runtime.py +8 -12
  140. package/python/foxclaw/kernel/capabilities.py +5 -6
  141. package/python/foxclaw/kernel/policy_gate.py +4 -10
  142. package/python/foxclaw/kernel/process_manager.py +17 -36
  143. package/python/foxclaw/kernel/scheduler.py +3 -9
  144. package/python/foxclaw/kernel/sentinel.py +13 -23
  145. package/python/foxclaw/kernel/sentinel_client.py +12 -15
  146. package/python/foxclaw/kernel/spine.py +9 -9
  147. package/python/foxclaw/knowledge/__init__.py +4 -20
  148. package/python/foxclaw/knowledge/truth_crystallizer.py +5 -8
  149. package/python/foxclaw/learning/knowledge_graph.py +4 -9
  150. package/python/foxclaw/learning/models.py +13 -13
  151. package/python/foxclaw/learning/reasoner.py +1 -4
  152. package/python/foxclaw/learning/runtime.py +19 -31
  153. package/python/foxclaw/learning/spaced_repetition.py +2 -7
  154. package/python/foxclaw/mcp_server.py +2 -3
  155. package/python/foxclaw/orchestration/__init__.py +16 -33
  156. package/python/foxclaw/pipeline/cognitive_pipeline.py +45 -97
  157. package/python/foxclaw/pipeline/stage_meta.py +21 -0
  158. package/python/foxclaw/pipeline/supervisor_stages.py +13 -42
  159. package/python/foxclaw/planning/llm_planner.py +3 -3
  160. package/python/foxclaw/planning/planner.py +6 -11
  161. package/python/foxclaw/search/agent_port.py +7 -11
  162. package/python/foxclaw/search/evidence.py +0 -4
  163. package/python/foxclaw/search/internal_search.py +3 -5
  164. package/python/foxclaw/search/models.py +11 -13
  165. package/python/foxclaw/search/planner.py +3 -3
  166. package/python/foxclaw/search/retriever.py +1 -1
  167. package/python/foxclaw/search/runtime.py +1 -8
  168. package/python/foxclaw/skills/__init__.py +46 -100
  169. package/python/foxclaw/skills/archive.py +111 -0
  170. package/python/foxclaw/skills/fetch.py +226 -0
  171. package/python/foxclaw/skills/hooks_spec.py +21 -25
  172. package/python/foxclaw/skills/install_cmd.py +216 -0
  173. package/python/foxclaw/skills/lifecycle.py +16 -42
  174. package/python/foxclaw/skills/markdown_skill.py +10 -9
  175. package/python/foxclaw/skills/mcp_client.py +5 -11
  176. package/python/foxclaw/skills/mcp_store.py +0 -3
  177. package/python/foxclaw/skills/permission.py +13 -48
  178. package/python/foxclaw/skills/pipeline.py +21 -53
  179. package/python/foxclaw/skills/policy.py +4 -10
  180. package/python/foxclaw/skills/review.py +3 -3
  181. package/python/frontend/deck/auth.js +0 -7
  182. package/python/frontend/deck/chat_model_picker.js +0 -54
  183. package/python/frontend/deck/domain.js +0 -20
  184. package/python/frontend/deck/main.js +0 -144
  185. package/python/frontend/deck/markdown.js +0 -4
  186. package/python/frontend/deck/settings.js +1 -18
  187. package/python/frontend/deck/state.js +0 -1
  188. package/python/frontend/deck/util.js +0 -2
  189. package/python/frontend/deck/visuals.js +0 -22
  190. package/python/frontend/planet/planet.js +18 -201
  191. package/python/frontend/v3/assets/index-JKfHlb-a.css +1 -0
  192. package/python/frontend/v3/assets/index-tN90dpUH.js +34 -0
  193. package/python/frontend/v3/index.html +3 -3
  194. package/python/frontend/widget_runtime.js +0 -43
  195. package/python/frontend/widgets.js +0 -57
  196. package/python/frontend_src/index.html +1 -1
  197. package/python/frontend_src/package-lock.json +2 -2
  198. package/python/frontend_src/package.json +1 -1
  199. package/python/frontend_src/src/App.vue +3 -7
  200. package/python/frontend_src/src/api.js +0 -7
  201. package/python/frontend_src/src/components/ChatDock.vue +27 -39
  202. package/python/frontend_src/src/components/DocViewer.vue +0 -9
  203. package/python/frontend_src/src/components/Inspector.vue +0 -6
  204. package/python/frontend_src/src/components/LeftRail.vue +60 -39
  205. package/python/frontend_src/src/components/MenuPopover.vue +9 -11
  206. package/python/frontend_src/src/components/MessageStream.vue +18 -30
  207. package/python/frontend_src/src/components/ModelPicker.vue +0 -5
  208. package/python/frontend_src/src/components/QuickIngest.vue +0 -6
  209. package/python/frontend_src/src/components/Stage.vue +71 -25
  210. package/python/frontend_src/src/components/ToolMenu.vue +0 -3
  211. package/python/frontend_src/src/components/TopBar.vue +38 -10
  212. package/python/frontend_src/src/components/panels/ActivityPanel.vue +0 -2
  213. package/python/frontend_src/src/components/panels/AgentsPanel.vue +2 -5
  214. package/python/frontend_src/src/components/panels/KnowledgePanel.vue +0 -16
  215. package/python/frontend_src/src/components/panels/MemoryPanel.vue +42 -3
  216. package/python/frontend_src/src/components/panels/PanelBox.vue +6 -7
  217. package/python/frontend_src/src/components/panels/ReviewPanel.vue +0 -16
  218. package/python/frontend_src/src/components/panels/SettingsPanel.vue +142 -38
  219. package/python/frontend_src/src/components/panels/TasksPanel.vue +0 -1
  220. package/python/frontend_src/src/components/panels/ToolsPanel.vue +125 -19
  221. package/python/frontend_src/src/lib/fmt.js +0 -4
  222. package/python/frontend_src/src/lib/i18n.js +53 -0
  223. package/python/frontend_src/src/lib/locales/en.js +173 -0
  224. package/python/frontend_src/src/lib/locales/zh.js +174 -0
  225. package/python/frontend_src/src/lib/md.js +0 -14
  226. package/python/frontend_src/src/main.js +2 -0
  227. package/python/frontend_src/src/stores/app.js +17 -32
  228. package/python/frontend_src/src/stores/chat.js +0 -34
  229. package/python/frontend_src/src/stores/model.js +0 -8
  230. package/python/frontend_src/src/stores/panels.js +16 -44
  231. package/python/frontend_src/src/stores/profile.js +31 -0
  232. package/python/frontend_src/src/stores/system.js +25 -0
  233. package/python/frontend_src/vite.config.js +0 -7
  234. package/python/pyproject.toml +17 -3
  235. package/python/requirements.txt +311 -0
  236. package/python/tests/conftest.py +0 -22
  237. package/python/tests/run_all.py +4 -66
  238. package/python/tests/test_agent_lifecycle.py +4 -38
  239. package/python/tests/test_agent_rules.py +81 -0
  240. package/python/tests/test_ahs_p2.py +4 -25
  241. package/python/tests/test_anthropic_tools.py +1 -15
  242. package/python/tests/test_api.py +19 -9
  243. package/python/tests/test_api_auth.py +1 -5
  244. package/python/tests/test_attachments.py +9 -25
  245. package/python/tests/test_audit_chain.py +2 -7
  246. package/python/tests/test_audit_sink.py +0 -6
  247. package/python/tests/test_behavior_spec.py +198 -0
  248. package/python/tests/test_browser.py +2 -6
  249. package/python/tests/test_browser_request_guard.py +0 -4
  250. package/python/tests/test_capabilities.py +0 -6
  251. package/python/tests/test_channel_signing.py +2 -9
  252. package/python/tests/test_channels.py +1 -11
  253. package/python/tests/test_config_layering.py +0 -4
  254. package/python/tests/test_confirm_nonce.py +1 -12
  255. package/python/tests/test_conflict_dismiss.py +143 -0
  256. package/python/tests/test_content_guard.py +177 -0
  257. package/python/tests/test_context_compaction_wiring.py +1 -7
  258. package/python/tests/test_dispatch.py +0 -6
  259. package/python/tests/test_domain_evals.py +11 -80
  260. package/python/tests/test_domain_import.py +0 -6
  261. package/python/tests/test_domain_reasoners.py +0 -14
  262. package/python/tests/test_e2e.py +0 -17
  263. package/python/tests/test_emb_cache.py +7 -17
  264. package/python/tests/test_embedding_singleton.py +1 -3
  265. package/python/tests/test_evals_smoke.py +0 -24
  266. package/python/tests/test_event_spine.py +10 -18
  267. package/python/tests/test_evidence_semantic.py +1 -14
  268. package/python/tests/test_evidence_sse.py +3 -6
  269. package/python/tests/test_file_ingest.py +0 -11
  270. package/python/tests/test_finance.py +0 -9
  271. package/python/tests/test_finance_ledger.py +5 -12
  272. package/python/tests/test_finance_persist.py +8 -12
  273. package/python/tests/test_gateway.py +3 -22
  274. package/python/tests/test_governance.py +5 -52
  275. package/python/tests/test_governance_audit.py +4 -39
  276. package/python/tests/test_governance_middleware.py +1 -7
  277. package/python/tests/test_hkc.py +6 -24
  278. package/python/tests/test_hkc_index_build_manager.py +0 -33
  279. package/python/tests/test_hkc_manifest_reuse.py +4 -27
  280. package/python/tests/test_hkc_search_event_sync.py +3 -54
  281. package/python/tests/test_import_path_guard.py +0 -6
  282. package/python/tests/test_injection_guard.py +18 -36
  283. package/python/tests/test_integration.py +5 -29
  284. package/python/tests/test_itinerary.py +0 -3
  285. package/python/tests/test_kee_semantic.py +0 -5
  286. package/python/tests/test_kernel.py +1 -11
  287. package/python/tests/test_learning.py +3 -12
  288. package/python/tests/test_llm_admin.py +4 -19
  289. package/python/tests/test_llm_planner.py +1 -23
  290. package/python/tests/test_markdown_skills.py +3 -14
  291. package/python/tests/test_markdown_skills_openclaw.py +4 -9
  292. package/python/tests/test_mcp_connectors.py +3 -15
  293. package/python/tests/test_mcp_guardian_discovery.py +2 -6
  294. package/python/tests/test_mcp_no_memory.py +3 -3
  295. package/python/tests/test_mcp_policy_gate.py +0 -1
  296. package/python/tests/test_mcp_server.py +6 -9
  297. package/python/tests/test_memory_recall.py +1 -4
  298. package/python/tests/test_metrics.py +0 -8
  299. package/python/tests/test_multi_chain.py +1 -13
  300. package/python/tests/test_multimodal.py +5 -18
  301. package/python/tests/test_negotiation.py +3 -13
  302. package/python/tests/test_orchestration.py +1 -11
  303. package/python/tests/test_orchestration_decoupled.py +0 -6
  304. package/python/tests/test_p3_p5_hardening.py +3 -17
  305. package/python/tests/test_parallel_dispatch.py +4 -12
  306. package/python/tests/test_pipeline.py +17 -58
  307. package/python/tests/test_plan_visible.py +4 -4
  308. package/python/tests/test_planner.py +0 -7
  309. package/python/tests/test_policy_gate.py +2 -3
  310. package/python/tests/test_profile.py +67 -0
  311. package/python/tests/test_readme_claims.py +163 -0
  312. package/python/tests/test_reasoner.py +2 -17
  313. package/python/tests/test_release_check.py +2 -13
  314. package/python/tests/test_resume_checkpoint.py +7 -15
  315. package/python/tests/test_routing.py +2 -12
  316. package/python/tests/test_scheduler.py +7 -10
  317. package/python/tests/test_search.py +1 -11
  318. package/python/tests/test_secrets.py +4 -10
  319. package/python/tests/test_semantic.py +1 -10
  320. package/python/tests/test_sensitive_write.py +181 -0
  321. package/python/tests/test_sessions.py +3 -4
  322. package/python/tests/test_settings.py +2 -18
  323. package/python/tests/test_shadow_learning.py +3 -43
  324. package/python/tests/test_shared_write.py +0 -7
  325. package/python/tests/test_skill_taint_gate.py +1 -10
  326. package/python/tests/test_skills.py +2 -18
  327. package/python/tests/test_skills_fetch.py +282 -0
  328. package/python/tests/test_stats_and_events.py +2 -5
  329. package/python/tests/test_streaming.py +2 -7
  330. package/python/tests/test_supervisor_pipeline.py +4 -5
  331. package/python/tests/test_supervisor_triage.py +4 -10
  332. package/python/tests/test_taint.py +2 -7
  333. package/python/tests/test_task_stream.py +1 -12
  334. package/python/tests/test_thinking_sse.py +0 -1
  335. package/python/tests/test_tool_center.py +1 -17
  336. package/python/tests/test_tool_parallel.py +2 -2
  337. package/python/tests/test_tool_params.py +1 -5
  338. package/python/tests/test_tool_use.py +0 -13
  339. package/python/tests/test_toplevel_concurrency.py +3 -4
  340. package/python/tests/test_tracing.py +7 -41
  341. package/python/tests/test_truth_crystallizer.py +4 -6
  342. package/python/tests/test_truth_flow.py +2 -7
  343. package/python/tests/test_update_check.py +95 -0
  344. package/python/tests/test_verify_evidence.py +8 -21
  345. package/python/tests/test_version_sync.py +35 -6
  346. package/python/tests/test_web_trigger.py +0 -17
  347. package/python/tools/build_content_lexicon.py +218 -0
  348. package/python/tools/bundle_web.py +98 -0
  349. package/python/tools/check_faiss_type.py +0 -1
  350. package/python/tools/check_log.ps1 +0 -8
  351. package/python/tools/mypy_baseline.txt +12 -4
  352. package/python/tools/mypy_ratchet.py +52 -6
  353. package/python/tools/pack.py +0 -5
  354. package/python/tools/recheck_conflicts.py +156 -0
  355. package/python/tools/release_check.py +24 -15
  356. package/python/tools/restart_foxclaw.ps1 +5 -8
  357. package/python/tools/shared_write_audit.py +1 -15
  358. package/python/tools/size_check.py +1 -1
  359. package/python/tools/strip_comments.py +228 -0
  360. package/python/vendor/hkc/hkc_api/routes/ability.py +31 -0
  361. package/python/vendor/hkc/hkc_api/schemas.py +11 -0
  362. package/python/vendor/hkc/hkc_kee/kee.py +124 -3
  363. package/python/vendor/hkc/hkc_sdk/base.py +22 -0
  364. package/python/vendor/hkc/hkc_sdk/direct_client.py +10 -0
  365. package/python/vendor/hkc/hkc_sdk/http_client.py +19 -0
  366. package/python/foxclaw/api/app.py +0 -123
  367. package/python/frontend/v3/assets/index-BD7VPbMV.js +0 -33
  368. package/python/frontend/v3/assets/index-CAvnYWGH.css +0 -1
@@ -20,21 +20,20 @@ class LearningRuntime:
20
20
  """office Agent 的学习内核。"""
21
21
 
22
22
  def __init__(self, hmr=None, hkc_provider=None, persist_path=None):
23
- self.items: dict = {} # id -> KnowledgeItem(内存缓存;知识真源=HKC)
24
- self.review_states: dict = {} # item_id -> ReviewState
23
+ self.items: dict = {}
24
+ self.review_states: dict = {}
25
25
  self.sr = SpacedRepetition()
26
26
  self.hmr = hmr
27
- self._hkc_provider = hkc_provider # 惰性拿 HKC(知识真源);None=纯内存(测试用)
28
- self._lock = threading.RLock() # 跨线程写/改内存加锁(与全系统线程安全铁律一致)
27
+ self._hkc_provider = hkc_provider
28
+ self._lock = threading.RLock()
29
29
  embedder = None
30
30
  if hmr is not None and hasattr(hmr, "_hmr"):
31
31
  embedder = self._make_embedder(hmr)
32
32
  self.graph = KnowledgeGraph(embedder=embedder)
33
- # persist_path 现在只作「离线兜底队列 + 旧 learning.json 迁移源」;知识真源是 HKC。
34
33
  self._persist_path = Path(persist_path) if persist_path else None
35
- self._hkc_loaded = False # HKC 首次成功载入的门闩(惰性,见 _ensure_hkc_loaded)
36
- self._load_file() # 先把离线队列/旧文件载入内存(__init__ 时 HKC 常未就绪)
37
- self._ensure_hkc_loaded() # HKC 若已就绪则立即从真源载入;否则留待后续调用重试
34
+ self._hkc_loaded = False
35
+ self._load_file()
36
+ self._ensure_hkc_loaded()
38
37
 
39
38
  def _make_embedder(self, hmr):
40
39
  """尝试复用 HMR 的 embedding;失败则返回 None(图谱自动降级哈希)。"""
@@ -46,7 +45,6 @@ class LearningRuntime:
46
45
  pass
47
46
  return None
48
47
 
49
- # ── 存储:知识内容+笔记+复习状态 全落 HKC(真源);HKC 拿不到时落离线队列(兜底 A)──
50
48
  def _graph_store(self):
51
49
  """惰性拿 HKC 的 graph_store。拿不到(HKC 未装配/未配/异常)返回 None。"""
52
50
  if not self._hkc_provider:
@@ -126,9 +124,9 @@ class LearningRuntime:
126
124
  if self._hkc_loaded:
127
125
  return
128
126
  gs = self._graph_store()
129
- if gs is None: # HKC 还没就绪 → 下次调用再试
127
+ if gs is None:
130
128
  return
131
- try: # 1) 从 HKC 载入(内存已有的以文件版为准,不覆盖)
129
+ try:
132
130
  for ku in gs.query_all(limit=5000):
133
131
  ex = getattr(ku, "extra", {}) or {}
134
132
  if ex.get("kind") != "learning_note":
@@ -146,9 +144,9 @@ class LearningRuntime:
146
144
  except Exception:
147
145
  pass
148
146
  except Exception:
149
- return # 载入失败不置门闩,留待下次重试
147
+ return
150
148
  self._hkc_loaded = True
151
- if self._persist_path and self._persist_path.exists(): # 2) 文件残留回迁 HKC + 删文件
149
+ if self._persist_path and self._persist_path.exists():
152
150
  self._save()
153
151
 
154
152
  def _save(self):
@@ -166,11 +164,11 @@ class LearningRuntime:
166
164
  if ok:
167
165
  if self._persist_path and self._persist_path.exists():
168
166
  try:
169
- self._persist_path.unlink() # 已全部进 HKC → 删掉本地队列文件
167
+ self._persist_path.unlink()
170
168
  except Exception:
171
169
  pass
172
170
  return
173
- if self._persist_path: # 离线兜底:HKC 不可用/部分失败 → 落队列文件
171
+ if self._persist_path:
174
172
  try:
175
173
  data = {
176
174
  "items": [asdict(it) for it in items],
@@ -183,9 +181,8 @@ class LearningRuntime:
183
181
  except Exception:
184
182
  pass
185
183
 
186
- # ── 采集 + 沉淀 ───────────────────────────────────────
187
184
  def learn(self, content: str, topic: str = "general",
188
- source: str = "", tags: list = None) -> dict:
185
+ source: str = "", tags: list | None = None) -> dict:
189
186
  """采集一个知识点 → 存进 HKC(知识真源)+ 间隔重复队列 + 图谱。
190
187
  铁律:知识内容+笔记归 HKC,不进 HMR(HMR 只装 token 上下文/工作运行记忆)。"""
191
188
  self._ensure_hkc_loaded()
@@ -195,7 +192,7 @@ class LearningRuntime:
195
192
  self.items[item.id] = item
196
193
  self.review_states[item.id] = ReviewState(item_id=item.id)
197
194
  self.graph.add_item(item)
198
- self._save() # 存进 HKC(HKC 不可用则离线队列兜底,恢复后自动补进)
195
+ self._save()
199
196
  return {"item": item.to_dict(),
200
197
  "review": self.review_states[item.id].to_dict()}
201
198
 
@@ -212,7 +209,6 @@ class LearningRuntime:
212
209
  count += 1
213
210
  return count
214
211
 
215
- # ── 固化:复习 ────────────────────────────────────────
216
212
  def review(self, item_id: str, grade: int) -> dict:
217
213
  """复习一个知识点,提交回忆质量评分(0~5),更新掌握度。"""
218
214
  self._ensure_hkc_loaded()
@@ -237,7 +233,7 @@ class LearningRuntime:
237
233
  gs = self._graph_store()
238
234
  if gs is not None:
239
235
  try:
240
- gs.delete(item_id) # HKC 软删除(status='deleted')
236
+ gs.delete(item_id)
241
237
  except Exception:
242
238
  pass
243
239
  if found:
@@ -256,7 +252,7 @@ class LearningRuntime:
256
252
  return {"error": "未知知识点"}
257
253
  item.notes.append({"text": text, "ts": datetime.now().isoformat()})
258
254
  notes = list(item.notes)
259
- self._save() # 只有新增了笔记才落盘
255
+ self._save()
260
256
  return {"notes": notes}
261
257
 
262
258
  def due_today(self) -> list:
@@ -265,7 +261,6 @@ class LearningRuntime:
265
261
  return [{"item": self.items[s.item_id].to_dict(),
266
262
  "review": s.to_dict()} for s in due]
267
263
 
268
- # ── 连接:构建图谱 ────────────────────────────────────
269
264
  def build_connections(self, semantic_threshold: float = 0.55) -> dict:
270
265
  """自动构建知识连接(同主题 + 语义)。"""
271
266
  topic_edges = self.graph.auto_link_by_topic()
@@ -283,13 +278,11 @@ class LearningRuntime:
283
278
  "edge_type": etype, "weight": weight, "reason": reason})
284
279
  return out
285
280
 
286
- # ── 学习策略(对应财务守门人)─────────────────────────
287
281
  def strategy_check(self, daily_cap: int = 20) -> list:
288
282
  """学习策略裁决:复习提醒、薄弱主题、负荷预警、连接建议。"""
289
283
  verdicts = []
290
284
  states = list(self.review_states.values())
291
285
 
292
- # 到期复习
293
286
  due = self.sr.due_items(states)
294
287
  if due:
295
288
  verdicts.append(LearningVerdict(
@@ -297,7 +290,6 @@ class LearningRuntime:
297
290
  message=f"今天有 {len(due)} 个知识点待复习",
298
291
  payload={"count": len(due)}))
299
292
 
300
- # 遗忘风险(薄弱项)
301
293
  risky = self.sr.forgetting_risk(states)
302
294
  if risky:
303
295
  verdicts.append(LearningVerdict(
@@ -305,7 +297,6 @@ class LearningRuntime:
305
297
  message=f"{len(risky)} 个知识点处于高遗忘风险,建议优先复习",
306
298
  payload={"risky": risky[:5]}))
307
299
 
308
- # 负荷预警
309
300
  load = self.sr.learning_load(states)
310
301
  if load["total_due"] > daily_cap:
311
302
  verdicts.append(LearningVerdict(
@@ -313,7 +304,6 @@ class LearningRuntime:
313
304
  message=f"未来复习量 {load['total_due']} 超过每日上限 {daily_cap},建议分批",
314
305
  payload=load))
315
306
 
316
- # 孤立知识点(无连接)→ 建议建立关联
317
307
  connected = set()
318
308
  for e in self.graph.edges:
319
309
  connected.add(e.source_id)
@@ -326,14 +316,12 @@ class LearningRuntime:
326
316
  payload={"isolated_count": len(isolated)}))
327
317
  return verdicts
328
318
 
329
- # ── 综合分析(office Agent 核心输出)──────────────────
330
319
  def analyze(self) -> dict:
331
- self._ensure_hkc_loaded() # HKC 就绪后从真源载回知识(迁移删文件后重启也不丢)
320
+ self._ensure_hkc_loaded()
332
321
  states = list(self.review_states.values())
333
322
  masteries = [s.mastery for s in states]
334
323
  avg_mastery = round(sum(masteries) / len(masteries), 3) if masteries else 0.0
335
324
 
336
- # 按主题统计掌握度
337
325
  from collections import defaultdict
338
326
  topic_mastery = defaultdict(list)
339
327
  for iid, item in self.items.items():
@@ -346,7 +334,7 @@ class LearningRuntime:
346
334
  "total_items": len(self.items),
347
335
  "avg_mastery": avg_mastery,
348
336
  "topic_mastery": topics,
349
- "weakest_topic": (min(topics, key=topics.get) if topics else None),
337
+ "weakest_topic": (min(topics, key=lambda t: topics[t]) if topics else None),
350
338
  "due_today": len(self.sr.due_items(states)),
351
339
  "forgetting_risk": self.sr.forgetting_risk(states),
352
340
  "learning_load": self.sr.learning_load(states),
@@ -22,19 +22,16 @@ class SpacedRepetition:
22
22
  @staticmethod
23
23
  def review(state: ReviewState, grade: int) -> ReviewState:
24
24
  """根据本次回忆质量,更新间隔重复状态。返回更新后的 state。"""
25
- # grade 钳制到 SM-2 定义域 [0,5],防止越界输入让 EF 公式算出无意义值
26
25
  grade = max(0, min(5, int(grade)))
27
26
  now = datetime.now()
28
27
  state.total_reviews += 1
29
28
  state.last_review = now.isoformat()
30
29
 
31
30
  if grade < 3:
32
- # 没记住:重置,短期内再复习
33
31
  state.repetitions = 0
34
32
  state.interval = 1
35
33
  state.lapses += 1
36
34
  else:
37
- # 记住了:按 SM-2 推进间隔
38
35
  if state.repetitions == 0:
39
36
  state.interval = 1
40
37
  elif state.repetitions == 1:
@@ -43,11 +40,9 @@ class SpacedRepetition:
43
40
  state.interval = round(state.interval * state.ease_factor)
44
41
  state.repetitions += 1
45
42
 
46
- # 更新难度因子(SM-2 公式)
47
43
  ef = state.ease_factor + (0.1 - (5 - grade) * (0.08 + (5 - grade) * 0.02))
48
44
  state.ease_factor = max(1.3, round(ef, 3))
49
45
 
50
- # 下次复习时间
51
46
  state.due_date = (now + timedelta(days=state.interval)).isoformat()
52
47
  return state
53
48
 
@@ -79,13 +74,13 @@ class SpacedRepetition:
79
74
  def learning_load(states: list, days: int = 7) -> dict:
80
75
  """学习负荷(对应财务烧钱速度):未来 N 天的复习量。"""
81
76
  from collections import defaultdict
82
- by_day = defaultdict(int)
77
+ by_day: defaultdict[int, int] = defaultdict(int)
83
78
  for s in states:
84
79
  d = s.days_until_due
85
80
  if 0 <= d <= days:
86
81
  by_day[d] += 1
87
82
  elif d < 0:
88
- by_day[0] += 1 # 逾期的算今天
83
+ by_day[0] += 1
89
84
  total_due = sum(by_day.values())
90
85
  return {
91
86
  "window_days": days,
@@ -90,7 +90,6 @@ def build_foxclaw_mcp(os_getter, expose_agents: bool = False) -> FastMCP:
90
90
  """os_getter:无参回调,返回当前 Foxclaw 实例(惰性,启动后才有)。
91
91
  expose_agents:另开一道开关才暴露 Agent 调度(有副作用面,默认关)。"""
92
92
  mcp = FastMCP("Foxclaw Knowledge", stateless_http=True, streamable_http_path="/")
93
- # 限流(env 可配):读工具较宽松、ask_foxclaw(驱动 LLM,贵)较紧
94
93
  read_limiter = _RateLimiter(_env_int("FOXCLAW_MCP_RATE_READ", 120))
95
94
  agent_limiter = _RateLimiter(_env_int("FOXCLAW_MCP_RATE_AGENT", 20))
96
95
 
@@ -111,7 +110,7 @@ def build_foxclaw_mcp(os_getter, expose_agents: bool = False) -> FastMCP:
111
110
  "type": str(getattr(h, "ku_type", ""))}
112
111
  for h in (hits or [])]
113
112
  except Exception:
114
- return [{"error": "internal error"}] # 对外脱敏:不回传 str(e)
113
+ return [{"error": "internal error"}]
115
114
 
116
115
  @mcp.tool()
117
116
  def knowledge_get(ku_id: str) -> dict:
@@ -172,7 +171,7 @@ def build_foxclaw_mcp(os_getter, expose_agents: bool = False) -> FastMCP:
172
171
 
173
172
  def _work():
174
173
  from foxclaw.agents.base import _suppress_memory
175
- _suppress_memory.set(True) # 外部驱动的任务不写 HMR(防污染),随上下文传到 run_task
174
+ _suppress_memory.set(True)
176
175
  return os_.scheduler.run(
177
176
  lambda: _a.run(os_.run(task, agent)),
178
177
  agent_id=agent, task=task)
@@ -22,14 +22,14 @@ RequestStatus = Literal["pending", "approved", "rejected", "needs_confirmation"]
22
22
  class CrossAgentRequest:
23
23
  """跨 Agent 请求 — 编排的基本单元。
24
24
  一个 Agent 向另一个发起请求,目标 Agent 用自己的守门人裁决。"""
25
- from_agent: str # 发起方
26
- to_agent: str # 目标方
25
+ from_agent: str
26
+ to_agent: str
27
27
  request_type: RequestType
28
- payload: dict # 请求内容(金额/时间/查询等)
28
+ payload: dict
29
29
  reason: str = ""
30
30
  status: RequestStatus = "pending"
31
- verdict: str = "" # 目标 Agent 的裁决说明
32
- negotiation: list = field(default_factory=list) # 协商回合记录(每轮:裁决 + 让步)
31
+ verdict: str = ""
32
+ negotiation: list = field(default_factory=list)
33
33
  id: str = field(default_factory=lambda: f"req_{uuid.uuid4().hex[:10]}")
34
34
  created_at: str = field(default_factory=lambda: datetime.now().isoformat())
35
35
 
@@ -40,10 +40,10 @@ class CrossAgentRequest:
40
40
  @dataclass
41
41
  class GlobalSituation:
42
42
  """全局态势 — 主管的驾驶舱视图,汇总各内核关键指标。"""
43
- finance: dict = field(default_factory=dict) # 超支?守门人告警?
44
- learning: dict = field(default_factory=dict) # 待复习?薄弱主题?
45
- itinerary: dict = field(default_factory=dict) # 行程冲突?赶不及?
46
- alerts: list = field(default_factory=list) # 跨域汇总的高优先告警
43
+ finance: dict = field(default_factory=dict)
44
+ learning: dict = field(default_factory=dict)
45
+ itinerary: dict = field(default_factory=dict)
46
+ alerts: list = field(default_factory=list)
47
47
  timestamp: str = field(default_factory=lambda: datetime.now().isoformat())
48
48
 
49
49
  def to_dict(self) -> dict:
@@ -58,13 +58,8 @@ class Orchestrator:
58
58
  不再反向直接持有含主管的 agents 字典)。"""
59
59
 
60
60
  def __init__(self, caps, auditor=None):
61
- self._caps = caps # CapabilityRegistry
62
- # 【P1-3】统一治理审计(可空:standalone 无审计)。裁决事件原本**完全没有**——
63
- # 跨 Agent 委派的批准/拒绝/转人工,事前无任何可观测记录。
61
+ self._caps = caps
64
62
  self._auditor = auditor
65
- # 【深审修复·长期运行泄漏】原为普通 list、只增不减且**全库从不读**——每次跨 Agent 请求
66
- # append 一个 req → 无界增长。改有界 deque(保留最近 N 条供未来观测,符合本库 token_meter/
67
- # HookManager 的有界日志惯例);deque.append 原子 ⇒ 并发路由也安全。
68
63
  self.request_log: deque = deque(maxlen=1000)
69
64
 
70
65
  @property
@@ -72,12 +67,11 @@ class Orchestrator:
72
67
  """按名字从注册中枢解析 agents(id -> agent 实例)。"""
73
68
  return self._caps.get("agents") or {}
74
69
 
75
- # ── 全局态势汇总(P3:通用聚合,不懂任何领域内部 API)────────────
76
70
  def gather_situation(self) -> GlobalSituation:
77
71
  """通用聚合各 Agent 自报的领域态势(agent.situation()),形成驾驶舱视图。
78
72
  编排层不再直接读 finance/learning/itinerary 内核——领域知识已归属各领域 Agent。"""
79
73
  sit = GlobalSituation()
80
- alerts = []
74
+ alerts: list[dict] = []
81
75
  for agent in self.agents.values():
82
76
  try:
83
77
  s = agent.situation()
@@ -85,12 +79,10 @@ class Orchestrator:
85
79
  continue
86
80
  if not s:
87
81
  continue
88
- # 一个 Agent 可以自报**多个**领域(拓扑 5→3 后,办公学习 Agent 同时覆盖
89
- # learning 与 itinerary),故 `domains: {name: status}` 与旧的单 `domain` 都收。
90
82
  doms = s.get("domains")
91
83
  if isinstance(doms, dict) and doms:
92
84
  for d, st in doms.items():
93
- if d and hasattr(sit, d): # finance/learning/itinerary
85
+ if d and hasattr(sit, d):
94
86
  setattr(sit, d, st or {})
95
87
  else:
96
88
  domain = s.get("domain")
@@ -102,24 +94,16 @@ class Orchestrator:
102
94
  .get(a.get("severity"), 3))
103
95
  return sit
104
96
 
105
- # ── 跨 Agent 请求路由(P3:通用委托,由目标 Agent 用自己的守门人裁决)──────
106
97
  def route_request(self, req: CrossAgentRequest) -> CrossAgentRequest:
107
98
  """把请求路由给目标 Agent,由**目标 Agent**用自己的守门人裁决(编排层不懂领域)。"""
108
99
  target = self.agents.get(req.to_agent)
109
100
  if target is None:
110
- # 【P1-0】原为 status="approved"(默认放行)—— 那是 fail-open:发起方会拿到
111
- # "已批准",但**没有任何 Agent 真的处理过这个请求**。后果是实打实的,例如
112
- # 行程 Agent 向未注册的 finance 申请差旅预算,会直接拿到批准并据此下单。
113
- # 与下方「裁决异常 → 转人工确认」同属一条原则:裁决权不在编排层,凡未真正
114
- # 裁决过的,都不能算通过。改为 needs_confirmation 同样"不崩",只是不再说谎。
115
101
  req.status = "needs_confirmation"
116
102
  req.verdict = f"目标 Agent '{req.to_agent}' 不存在,无法裁决,转人工确认"
117
103
  else:
118
104
  try:
119
105
  target.handle_cross_request(req)
120
106
  except Exception as e:
121
- # 守门人裁决崩溃(如畸形 payload 让解析抛错)必须 **fail-safe**:转人工确认,
122
- # 绝不自动放行——对守门人而言,裁决失败时放行=fail-open,是安全隐患。
123
107
  req.status = "needs_confirmation"
124
108
  req.verdict = f"裁决异常,转人工确认: {e}"
125
109
  self.request_log.append(req)
@@ -143,7 +127,6 @@ class Orchestrator:
143
127
  "negotiation_round": len(req.negotiation)},
144
128
  correlation_id=req.id)
145
129
 
146
- # ── 协商闭环:被拒 → 发起方让步 → 再裁决 ──────────────────────────
147
130
  def negotiate(self, req: CrossAgentRequest, max_rounds: int = 3) -> CrossAgentRequest:
148
131
  """跑协商协议。原先裁决是**终局**:财务说预算不足→rejected→到此为止,用户只拿到
149
132
  一句"被拒绝"。现在:发起方可拿着拒绝理由降标准重来(改高铁/换酒店/减一天)。
@@ -165,17 +148,17 @@ class Orchestrator:
165
148
  "verdict": req.verdict,
166
149
  "payload": dict(req.payload)})
167
150
  if req.status != "rejected":
168
- break # 通过/待确认 → 协商结束
151
+ break
169
152
  requester = self.agents.get(req.from_agent)
170
153
  if requester is None:
171
154
  break
172
155
  try:
173
- revised = requester.concede(req, req.verdict) # 问发起方:能让吗?
156
+ revised = requester.concede(req, req.verdict)
174
157
  except Exception as e:
175
158
  req.negotiation.append({"round": rnd, "concede_error": str(e)[:120]})
176
- break # 让步逻辑出错 → 停,维持原裁决
159
+ break
177
160
  if not revised or revised == req.payload:
178
- break # 让无可让 / 无实质变化 → 停
161
+ break
179
162
  req.negotiation[-1]["conceded_to"] = dict(revised)
180
163
  req.payload = revised
181
164
  return req