foxclaw 0.1.0 → 0.1.3

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 (514) hide show
  1. package/README.md +54 -29
  2. package/bin/foxclaw.cjs +51 -0
  3. package/index.js +4 -10
  4. package/package.json +46 -38
  5. package/python/.dockerignore +35 -0
  6. package/python/.env.example +173 -0
  7. package/python/.gitattributes +46 -0
  8. package/python/.github/workflows/ci.yml +167 -0
  9. package/python/.github/workflows/release.yml +183 -0
  10. package/python/CHANGELOG.md +57 -0
  11. package/python/CHANGELOG_CN.md +57 -0
  12. package/python/Dockerfile +64 -0
  13. package/python/LICENSE +21 -0
  14. package/python/MANIFEST.in +14 -0
  15. package/python/README.md +201 -0
  16. package/python/README_CN.md +336 -0
  17. package/python/bench/bm25_audit.py +186 -0
  18. package/python/bench/bm25_regression.py +177 -0
  19. package/python/bench/bm25_stress.py +155 -0
  20. package/python/bench/cd_audit.py +215 -0
  21. package/python/bench/chaos.py +140 -0
  22. package/python/bench/concurrency.py +103 -0
  23. package/python/bench/e2e_real_models.py +94 -0
  24. package/python/bench/llm_retry_test.py +241 -0
  25. package/python/bench/load_test.py +193 -0
  26. package/python/bench/vector_ivf_upgrade.py +128 -0
  27. package/python/docs/CHANNELS.md +195 -0
  28. package/python/docs/GOVERNANCE.md +164 -0
  29. package/python/docs/RELEASE.md +110 -0
  30. package/python/docs/RUN.md +75 -0
  31. package/python/docs/RUN_CN.md +121 -0
  32. package/python/docs//351/205/215/347/275/256/346/214/207/345/215/227.md +152 -0
  33. package/python/evals/__init__.py +0 -0
  34. package/python/evals/governance/README.md +61 -0
  35. package/python/evals/governance/__init__.py +0 -0
  36. package/python/evals/governance/cases.py +115 -0
  37. package/python/evals/governance/compare_embeddings.py +94 -0
  38. package/python/evals/governance/convergence.svg +11 -0
  39. package/python/evals/governance/report.json +371 -0
  40. package/python/evals/governance/run_evals.py +368 -0
  41. package/python/examples/governance_middleware_demo.py +72 -0
  42. package/python/examples/langgraph_governance_demo.py +104 -0
  43. package/python/examples/sample_itinerary.csv +6 -0
  44. package/python/examples/sample_knowledge.csv +9 -0
  45. package/python/examples/sample_transactions.csv +14 -0
  46. package/python/foxclaw/__init__.py +408 -0
  47. package/python/foxclaw/agents/__init__.py +12 -0
  48. package/python/foxclaw/agents/agents.py +35 -0
  49. package/python/foxclaw/agents/base.py +794 -0
  50. package/python/foxclaw/agents/impl/__init__.py +1 -0
  51. package/python/foxclaw/agents/impl/_helpers.py +70 -0
  52. package/python/foxclaw/agents/impl/finance.py +231 -0
  53. package/python/foxclaw/agents/impl/office.py +350 -0
  54. package/python/foxclaw/agents/impl/supervisor.py +592 -0
  55. package/python/foxclaw/agents/mixins/_context.py +128 -0
  56. package/python/foxclaw/agents/mixins/augment.py +143 -0
  57. package/python/foxclaw/agents/mixins/llm.py +67 -0
  58. package/python/foxclaw/agents/mixins/memory.py +41 -0
  59. package/python/foxclaw/agents/mixins/preference.py +131 -0
  60. package/python/foxclaw/agents/mixins/prompt.py +71 -0
  61. package/python/foxclaw/agents/mixins/skillbox.py +58 -0
  62. package/python/foxclaw/agents/mixins/tool_exec.py +417 -0
  63. package/python/foxclaw/api/__init__.py +224 -0
  64. package/python/foxclaw/api/_state.py +43 -0
  65. package/python/foxclaw/api/bootstrap.py +96 -0
  66. package/python/foxclaw/api/middleware.py +294 -0
  67. package/python/foxclaw/api/routes/ahs.py +404 -0
  68. package/python/foxclaw/api/routes/attachments.py +116 -0
  69. package/python/foxclaw/api/routes/browser.py +35 -0
  70. package/python/foxclaw/api/routes/domain.py +306 -0
  71. package/python/foxclaw/api/routes/knowledge.py +145 -0
  72. package/python/foxclaw/api/routes/llm_admin.py +449 -0
  73. package/python/foxclaw/api/routes/memory.py +105 -0
  74. package/python/foxclaw/api/routes/orchestration.py +39 -0
  75. package/python/foxclaw/api/routes/recovery.py +44 -0
  76. package/python/foxclaw/api/routes/review.py +266 -0
  77. package/python/foxclaw/api/routes/search.py +33 -0
  78. package/python/foxclaw/api/routes/system.py +311 -0
  79. package/python/foxclaw/api/routes/task.py +263 -0
  80. package/python/foxclaw/api/routes/ws.py +64 -0
  81. package/python/foxclaw/api/spine.py +52 -0
  82. package/python/foxclaw/browser/__init__.py +13 -0
  83. package/python/foxclaw/browser/action/__init__.py +1 -0
  84. package/python/foxclaw/browser/action/action_executor.py +50 -0
  85. package/python/foxclaw/browser/action/action_types.py +44 -0
  86. package/python/foxclaw/browser/action/action_verifier.py +68 -0
  87. package/python/foxclaw/browser/action/url_policy.py +143 -0
  88. package/python/foxclaw/browser/adapter/__init__.py +1 -0
  89. package/python/foxclaw/browser/adapter/base_adapter.py +23 -0
  90. package/python/foxclaw/browser/adapter/web_adapter.py +46 -0
  91. package/python/foxclaw/browser/driver/__init__.py +1 -0
  92. package/python/foxclaw/browser/driver/base_driver.py +61 -0
  93. package/python/foxclaw/browser/driver/playwright_driver.py +170 -0
  94. package/python/foxclaw/browser/perception/__init__.py +1 -0
  95. package/python/foxclaw/browser/perception/dom_extractor.py +42 -0
  96. package/python/foxclaw/browser/perception/element_indexer.py +56 -0
  97. package/python/foxclaw/browser/perception/semantic_reducer.py +33 -0
  98. package/python/foxclaw/browser/perception/snapshot.py +29 -0
  99. package/python/foxclaw/browser/service.py +144 -0
  100. package/python/foxclaw/browser/session/__init__.py +1 -0
  101. package/python/foxclaw/browser/session/session_manager.py +57 -0
  102. package/python/foxclaw/capability/__init__.py +12 -0
  103. package/python/foxclaw/capability/embedding_service.py +65 -0
  104. package/python/foxclaw/channels/__init__.py +138 -0
  105. package/python/foxclaw/channels/discord.py +175 -0
  106. package/python/foxclaw/channels/dispatcher.py +179 -0
  107. package/python/foxclaw/channels/feishu.py +215 -0
  108. package/python/foxclaw/channels/openclaw_bridge.py +151 -0
  109. package/python/foxclaw/channels/signing.py +187 -0
  110. package/python/foxclaw/channels/telegram.py +120 -0
  111. package/python/foxclaw/channels/weixin.py +215 -0
  112. package/python/foxclaw/cli.py +183 -0
  113. package/python/foxclaw/cognitive/__init__.py +4 -0
  114. package/python/foxclaw/cognitive/reasoner.py +81 -0
  115. package/python/foxclaw/cognitive/state.py +141 -0
  116. package/python/foxclaw/core/__init__.py +9 -0
  117. package/python/foxclaw/core/audit_chain.py +131 -0
  118. package/python/foxclaw/core/audit_sink.py +100 -0
  119. package/python/foxclaw/core/compaction.py +265 -0
  120. package/python/foxclaw/core/config.py +65 -0
  121. package/python/foxclaw/core/confirm_nonce.py +151 -0
  122. package/python/foxclaw/core/dispatch.py +27 -0
  123. package/python/foxclaw/core/envfile.py +48 -0
  124. package/python/foxclaw/core/failure_policy.py +142 -0
  125. package/python/foxclaw/core/gateway.py +81 -0
  126. package/python/foxclaw/core/governance_audit.py +171 -0
  127. package/python/foxclaw/core/idempotency.py +282 -0
  128. package/python/foxclaw/core/injection_guard.py +202 -0
  129. package/python/foxclaw/core/memory_adapter.py +489 -0
  130. package/python/foxclaw/core/metrics.py +88 -0
  131. package/python/foxclaw/core/model_router.py +985 -0
  132. package/python/foxclaw/core/paths.py +103 -0
  133. package/python/foxclaw/core/recovery.py +349 -0
  134. package/python/foxclaw/core/scheduler.py +141 -0
  135. package/python/foxclaw/core/secrets.py +131 -0
  136. package/python/foxclaw/core/sessions.py +155 -0
  137. package/python/foxclaw/core/settings.py +414 -0
  138. package/python/foxclaw/core/shared_write.py +186 -0
  139. package/python/foxclaw/core/steering_queue.py +154 -0
  140. package/python/foxclaw/core/system_bus.py +84 -0
  141. package/python/foxclaw/core/thought_chain.py +316 -0
  142. package/python/foxclaw/core/token_meter.py +231 -0
  143. package/python/foxclaw/core/tools.py +164 -0
  144. package/python/foxclaw/core/tracing.py +375 -0
  145. package/python/foxclaw/core/truth_pipeline.py +110 -0
  146. package/python/foxclaw/core/update_check.py +149 -0
  147. package/python/foxclaw/core/vec_recall_adapter.py +477 -0
  148. package/python/foxclaw/core/verify.py +173 -0
  149. package/python/foxclaw/core/web_search.py +218 -0
  150. package/python/foxclaw/finance/__init__.py +15 -0
  151. package/python/foxclaw/finance/evidence.py +149 -0
  152. package/python/foxclaw/finance/fx.py +98 -0
  153. package/python/foxclaw/finance/graph.py +179 -0
  154. package/python/foxclaw/finance/models.py +146 -0
  155. package/python/foxclaw/finance/nlparse.py +515 -0
  156. package/python/foxclaw/finance/policy.py +301 -0
  157. package/python/foxclaw/finance/reasoner.py +121 -0
  158. package/python/foxclaw/finance/reasoning.py +319 -0
  159. package/python/foxclaw/finance/reconcile.py +175 -0
  160. package/python/foxclaw/finance/runtime.py +500 -0
  161. package/python/foxclaw/governance/__init__.py +24 -0
  162. package/python/foxclaw/governance/middleware.py +176 -0
  163. package/python/foxclaw/harness/__init__.py +17 -0
  164. package/python/foxclaw/harness/_base.py +66 -0
  165. package/python/foxclaw/harness/_policy.py +340 -0
  166. package/python/foxclaw/harness/drift.py +77 -0
  167. package/python/foxclaw/harness/event_bus.py +28 -0
  168. package/python/foxclaw/harness/harness.py +358 -0
  169. package/python/foxclaw/harness/loop_detection.py +145 -0
  170. package/python/foxclaw/itinerary/__init__.py +10 -0
  171. package/python/foxclaw/itinerary/amap_client.py +114 -0
  172. package/python/foxclaw/itinerary/models.py +80 -0
  173. package/python/foxclaw/itinerary/reasoner.py +96 -0
  174. package/python/foxclaw/itinerary/reasoning.py +168 -0
  175. package/python/foxclaw/itinerary/runtime.py +144 -0
  176. package/python/foxclaw/kernel/__init__.py +13 -0
  177. package/python/foxclaw/kernel/capabilities.py +72 -0
  178. package/python/foxclaw/kernel/policy_gate.py +108 -0
  179. package/python/foxclaw/kernel/process_manager.py +186 -0
  180. package/python/foxclaw/kernel/scheduler.py +125 -0
  181. package/python/foxclaw/kernel/sentinel.py +443 -0
  182. package/python/foxclaw/kernel/sentinel_client.py +243 -0
  183. package/python/foxclaw/kernel/spine.py +97 -0
  184. package/python/foxclaw/knowledge/__init__.py +360 -0
  185. package/python/foxclaw/knowledge/truth_crystallizer.py +89 -0
  186. package/python/foxclaw/learning/__init__.py +12 -0
  187. package/python/foxclaw/learning/knowledge_graph.py +144 -0
  188. package/python/foxclaw/learning/models.py +93 -0
  189. package/python/foxclaw/learning/reasoner.py +67 -0
  190. package/python/foxclaw/learning/runtime.py +351 -0
  191. package/python/foxclaw/learning/spaced_repetition.py +91 -0
  192. package/python/foxclaw/mcp_server.py +188 -0
  193. package/python/foxclaw/orchestration/__init__.py +167 -0
  194. package/python/foxclaw/pipeline/__init__.py +23 -0
  195. package/python/foxclaw/pipeline/cognitive_pipeline.py +429 -0
  196. package/python/foxclaw/pipeline/stage_meta.py +21 -0
  197. package/python/foxclaw/pipeline/supervisor_stages.py +201 -0
  198. package/python/foxclaw/planning/__init__.py +17 -0
  199. package/python/foxclaw/planning/llm_planner.py +232 -0
  200. package/python/foxclaw/planning/planner.py +94 -0
  201. package/python/foxclaw/search/__init__.py +24 -0
  202. package/python/foxclaw/search/agent_port.py +190 -0
  203. package/python/foxclaw/search/evidence.py +109 -0
  204. package/python/foxclaw/search/external_search.py +50 -0
  205. package/python/foxclaw/search/hkc_read_port.py +60 -0
  206. package/python/foxclaw/search/internal_search.py +71 -0
  207. package/python/foxclaw/search/models.py +127 -0
  208. package/python/foxclaw/search/planner.py +44 -0
  209. package/python/foxclaw/search/retriever.py +39 -0
  210. package/python/foxclaw/search/runtime.py +127 -0
  211. package/python/foxclaw/search/strategy.py +44 -0
  212. package/python/foxclaw/skills/__init__.py +559 -0
  213. package/python/foxclaw/skills/archive.py +111 -0
  214. package/python/foxclaw/skills/fetch.py +226 -0
  215. package/python/foxclaw/skills/hooks_spec.py +69 -0
  216. package/python/foxclaw/skills/install_cmd.py +216 -0
  217. package/python/foxclaw/skills/lifecycle.py +252 -0
  218. package/python/foxclaw/skills/markdown_skill.py +175 -0
  219. package/python/foxclaw/skills/markdown_store.py +39 -0
  220. package/python/foxclaw/skills/mcp_client.py +184 -0
  221. package/python/foxclaw/skills/mcp_store.py +40 -0
  222. package/python/foxclaw/skills/permission.py +145 -0
  223. package/python/foxclaw/skills/pipeline.py +354 -0
  224. package/python/foxclaw/skills/policy.py +74 -0
  225. package/python/foxclaw/skills/review.py +72 -0
  226. package/python/foxclaw/skills/store.py +57 -0
  227. package/python/frontend/assets/core_bg.jpg +0 -0
  228. package/python/frontend/deck/auth.js +25 -0
  229. package/python/frontend/deck/chat_model_picker.js +483 -0
  230. package/python/frontend/deck/domain.js +276 -0
  231. package/python/frontend/deck/main.js +1806 -0
  232. package/python/frontend/deck/markdown.js +44 -0
  233. package/python/frontend/deck/settings.js +307 -0
  234. package/python/frontend/deck/state.js +5 -0
  235. package/python/frontend/deck/util.js +4 -0
  236. package/python/frontend/deck/visuals.js +190 -0
  237. package/python/frontend/deck.css +855 -0
  238. package/python/frontend/deck.html +686 -0
  239. package/python/frontend/deck_v2.html +341 -0
  240. package/python/frontend/planet/index.html +266 -0
  241. package/python/frontend/planet/planet.css +579 -0
  242. package/python/frontend/planet/planet.js +1436 -0
  243. package/python/frontend/planet/vendor/three.core.min.js +6 -0
  244. package/python/frontend/planet/vendor/three.module.min.js +6 -0
  245. package/python/frontend/v3/assets/index-CQSBdxk4.js +34 -0
  246. package/python/frontend/v3/assets/index-D5nIsjms.css +1 -0
  247. package/python/frontend/v3/index.html +15 -0
  248. package/python/frontend/widget_runtime.js +252 -0
  249. package/python/frontend/widgets.js +639 -0
  250. package/python/frontend_src/index.html +14 -0
  251. package/python/frontend_src/package-lock.json +1376 -0
  252. package/python/frontend_src/package.json +19 -0
  253. package/python/frontend_src/src/App.vue +58 -0
  254. package/python/frontend_src/src/api.js +82 -0
  255. package/python/frontend_src/src/components/ChatDock.vue +365 -0
  256. package/python/frontend_src/src/components/ComposerMenu.vue +75 -0
  257. package/python/frontend_src/src/components/DocViewer.vue +162 -0
  258. package/python/frontend_src/src/components/Inspector.vue +235 -0
  259. package/python/frontend_src/src/components/LeftRail.vue +415 -0
  260. package/python/frontend_src/src/components/MenuPopover.vue +128 -0
  261. package/python/frontend_src/src/components/MessageStream.vue +389 -0
  262. package/python/frontend_src/src/components/ModelPicker.vue +350 -0
  263. package/python/frontend_src/src/components/QuickIngest.vue +112 -0
  264. package/python/frontend_src/src/components/Stage.vue +272 -0
  265. package/python/frontend_src/src/components/ToolMenu.vue +141 -0
  266. package/python/frontend_src/src/components/TopBar.vue +118 -0
  267. package/python/frontend_src/src/components/panels/ActivityPanel.vue +82 -0
  268. package/python/frontend_src/src/components/panels/AgentsPanel.vue +83 -0
  269. package/python/frontend_src/src/components/panels/KnowledgePanel.vue +425 -0
  270. package/python/frontend_src/src/components/panels/MemoryPanel.vue +117 -0
  271. package/python/frontend_src/src/components/panels/PanelBox.vue +50 -0
  272. package/python/frontend_src/src/components/panels/ReviewPanel.vue +292 -0
  273. package/python/frontend_src/src/components/panels/SettingsPanel.vue +358 -0
  274. package/python/frontend_src/src/components/panels/TasksPanel.vue +73 -0
  275. package/python/frontend_src/src/components/panels/ToolsPanel.vue +746 -0
  276. package/python/frontend_src/src/components/panels/UsagePanel.vue +105 -0
  277. package/python/frontend_src/src/lib/fmt.js +44 -0
  278. package/python/frontend_src/src/lib/i18n.js +53 -0
  279. package/python/frontend_src/src/lib/locales/en.js +173 -0
  280. package/python/frontend_src/src/lib/locales/zh.js +174 -0
  281. package/python/frontend_src/src/lib/md.js +190 -0
  282. package/python/frontend_src/src/main.js +9 -0
  283. package/python/frontend_src/src/stores/app.js +91 -0
  284. package/python/frontend_src/src/stores/chat.js +450 -0
  285. package/python/frontend_src/src/stores/model.js +96 -0
  286. package/python/frontend_src/src/stores/panels.js +289 -0
  287. package/python/frontend_src/src/stores/profile.js +31 -0
  288. package/python/frontend_src/src/stores/system.js +25 -0
  289. package/python/frontend_src/src/styles/base.css +138 -0
  290. package/python/frontend_src/src/styles/panel.css +117 -0
  291. package/python/frontend_src/src/styles/tokens.css +78 -0
  292. package/python/frontend_src/vite.config.js +39 -0
  293. package/python/pyproject.toml +268 -0
  294. package/python/requirements.txt +311 -0
  295. package/python/skills/README.md +123 -0
  296. package/python/skills/execution-contract/SKILL.md +30 -0
  297. package/python/skills/general-assistant/SKILL.md +34 -0
  298. package/python/skills/planner/SKILL.md +33 -0
  299. package/python/skills/schedule-itinerary/SKILL.md +35 -0
  300. package/python/skills/skill-composition/SKILL.md +31 -0
  301. package/python/skills/skill-guard/SKILL.md +67 -0
  302. package/python/skills/skill-memory/SKILL.md +50 -0
  303. package/python/skills/skill-router/SKILL.md +35 -0
  304. package/python/tests/conftest.py +54 -0
  305. package/python/tests/run_all.py +200 -0
  306. package/python/tests/test_agent_lifecycle.py +326 -0
  307. package/python/tests/test_ahs_p2.py +393 -0
  308. package/python/tests/test_anthropic_tools.py +192 -0
  309. package/python/tests/test_api.py +97 -0
  310. package/python/tests/test_api_auth.py +79 -0
  311. package/python/tests/test_api_host_guard.py +69 -0
  312. package/python/tests/test_attachments.py +151 -0
  313. package/python/tests/test_audit_chain.py +116 -0
  314. package/python/tests/test_audit_sink.py +130 -0
  315. package/python/tests/test_browser.py +149 -0
  316. package/python/tests/test_browser_request_guard.py +180 -0
  317. package/python/tests/test_capabilities.py +152 -0
  318. package/python/tests/test_channel_signing.py +185 -0
  319. package/python/tests/test_channel_trust.py +109 -0
  320. package/python/tests/test_channels.py +174 -0
  321. package/python/tests/test_cli.py +46 -0
  322. package/python/tests/test_compaction_toolpair.py +119 -0
  323. package/python/tests/test_config_layering.py +61 -0
  324. package/python/tests/test_confirm_nonce.py +186 -0
  325. package/python/tests/test_context_compaction_wiring.py +124 -0
  326. package/python/tests/test_dispatch.py +89 -0
  327. package/python/tests/test_domain_evals.py +334 -0
  328. package/python/tests/test_domain_import.py +72 -0
  329. package/python/tests/test_domain_reasoners.py +134 -0
  330. package/python/tests/test_e2e.py +161 -0
  331. package/python/tests/test_emb_cache.py +97 -0
  332. package/python/tests/test_embedding_singleton.py +87 -0
  333. package/python/tests/test_envfile.py +83 -0
  334. package/python/tests/test_evals_smoke.py +90 -0
  335. package/python/tests/test_event_spine.py +174 -0
  336. package/python/tests/test_evidence_semantic.py +88 -0
  337. package/python/tests/test_evidence_sse.py +94 -0
  338. package/python/tests/test_file_ingest.py +135 -0
  339. package/python/tests/test_finance.py +144 -0
  340. package/python/tests/test_finance_ledger.py +245 -0
  341. package/python/tests/test_finance_persist.py +174 -0
  342. package/python/tests/test_gateway.py +125 -0
  343. package/python/tests/test_governance.py +421 -0
  344. package/python/tests/test_governance_audit.py +324 -0
  345. package/python/tests/test_governance_middleware.py +137 -0
  346. package/python/tests/test_hkc.py +268 -0
  347. package/python/tests/test_hkc_index_build_manager.py +306 -0
  348. package/python/tests/test_hkc_manifest_reuse.py +294 -0
  349. package/python/tests/test_hkc_search_event_sync.py +482 -0
  350. package/python/tests/test_import_path_guard.py +155 -0
  351. package/python/tests/test_injection_guard.py +193 -0
  352. package/python/tests/test_integration.py +117 -0
  353. package/python/tests/test_itinerary.py +100 -0
  354. package/python/tests/test_kee_semantic.py +84 -0
  355. package/python/tests/test_kernel.py +212 -0
  356. package/python/tests/test_learning.py +101 -0
  357. package/python/tests/test_llm_admin.py +145 -0
  358. package/python/tests/test_llm_planner.py +344 -0
  359. package/python/tests/test_markdown_skills.py +62 -0
  360. package/python/tests/test_markdown_skills_openclaw.py +66 -0
  361. package/python/tests/test_mcp_connectors.py +97 -0
  362. package/python/tests/test_mcp_guardian_discovery.py +87 -0
  363. package/python/tests/test_mcp_no_memory.py +43 -0
  364. package/python/tests/test_mcp_policy_gate.py +76 -0
  365. package/python/tests/test_mcp_server.py +92 -0
  366. package/python/tests/test_memory_recall.py +92 -0
  367. package/python/tests/test_metrics.py +58 -0
  368. package/python/tests/test_multi_chain.py +90 -0
  369. package/python/tests/test_multimodal.py +122 -0
  370. package/python/tests/test_negotiation.py +136 -0
  371. package/python/tests/test_orchestration.py +109 -0
  372. package/python/tests/test_orchestration_decoupled.py +103 -0
  373. package/python/tests/test_p3_p5_hardening.py +324 -0
  374. package/python/tests/test_parallel_dispatch.py +207 -0
  375. package/python/tests/test_pipeline.py +492 -0
  376. package/python/tests/test_plan_visible.py +86 -0
  377. package/python/tests/test_planner.py +88 -0
  378. package/python/tests/test_policy_gate.py +108 -0
  379. package/python/tests/test_profile.py +67 -0
  380. package/python/tests/test_readme_claims.py +163 -0
  381. package/python/tests/test_reasoner.py +170 -0
  382. package/python/tests/test_release_check.py +252 -0
  383. package/python/tests/test_resume_checkpoint.py +148 -0
  384. package/python/tests/test_routing.py +85 -0
  385. package/python/tests/test_scheduler.py +138 -0
  386. package/python/tests/test_search.py +194 -0
  387. package/python/tests/test_secrets.py +153 -0
  388. package/python/tests/test_semantic.py +69 -0
  389. package/python/tests/test_sessions.py +90 -0
  390. package/python/tests/test_settings.py +265 -0
  391. package/python/tests/test_shadow_learning.py +290 -0
  392. package/python/tests/test_shared_write.py +200 -0
  393. package/python/tests/test_skill_taint_gate.py +145 -0
  394. package/python/tests/test_skills.py +117 -0
  395. package/python/tests/test_skills_fetch.py +282 -0
  396. package/python/tests/test_stats_and_events.py +162 -0
  397. package/python/tests/test_streaming.py +55 -0
  398. package/python/tests/test_supervisor_pipeline.py +176 -0
  399. package/python/tests/test_supervisor_triage.py +102 -0
  400. package/python/tests/test_taint.py +112 -0
  401. package/python/tests/test_task_stream.py +74 -0
  402. package/python/tests/test_thinking_sse.py +66 -0
  403. package/python/tests/test_thinking_stream.py +76 -0
  404. package/python/tests/test_tool_center.py +113 -0
  405. package/python/tests/test_tool_parallel.py +113 -0
  406. package/python/tests/test_tool_params.py +99 -0
  407. package/python/tests/test_tool_use.py +229 -0
  408. package/python/tests/test_toplevel_concurrency.py +116 -0
  409. package/python/tests/test_tracing.py +256 -0
  410. package/python/tests/test_truth_crystallizer.py +125 -0
  411. package/python/tests/test_truth_flow.py +88 -0
  412. package/python/tests/test_update_check.py +95 -0
  413. package/python/tests/test_url_policy.py +53 -0
  414. package/python/tests/test_verify_evidence.py +212 -0
  415. package/python/tests/test_version_sync.py +100 -0
  416. package/python/tests/test_web_trigger.py +82 -0
  417. package/python/tools/check_faiss_type.py +30 -0
  418. package/python/tools/check_log.ps1 +38 -0
  419. package/python/tools/ivf_status.py +72 -0
  420. package/python/tools/mypy_baseline.txt +13 -0
  421. package/python/tools/mypy_ratchet.py +143 -0
  422. package/python/tools/pack.py +173 -0
  423. package/python/tools/release_check.py +349 -0
  424. package/python/tools/restart_foxclaw.ps1 +71 -0
  425. package/python/tools/shared_write_audit.py +190 -0
  426. package/python/tools/size_baseline.txt +28 -0
  427. package/python/tools/size_check.py +102 -0
  428. package/python/tools/strip_comments.py +228 -0
  429. package/python/tools/verify_env.bat +2 -0
  430. package/python/tools/verify_env.py +25 -0
  431. package/python/vendor/HMR_LICENSE +37 -0
  432. package/python/vendor/HMR_README.md +65 -0
  433. package/python/vendor/hkc/LICENSE +37 -0
  434. package/python/vendor/hkc/hkc_ace/__init__.py +0 -0
  435. package/python/vendor/hkc/hkc_ace/ace.py +457 -0
  436. package/python/vendor/hkc/hkc_ace/skill_taxonomy.json +465 -0
  437. package/python/vendor/hkc/hkc_api/__init__.py +0 -0
  438. package/python/vendor/hkc/hkc_api/container.py +416 -0
  439. package/python/vendor/hkc/hkc_api/ingest_jobs.py +103 -0
  440. package/python/vendor/hkc/hkc_api/main.py +157 -0
  441. package/python/vendor/hkc/hkc_api/routes/__init__.py +0 -0
  442. package/python/vendor/hkc/hkc_api/routes/ability.py +151 -0
  443. package/python/vendor/hkc/hkc_api/routes/config.py +51 -0
  444. package/python/vendor/hkc/hkc_api/routes/knowledge.py +630 -0
  445. package/python/vendor/hkc/hkc_api/routes/search.py +88 -0
  446. package/python/vendor/hkc/hkc_api/schemas.py +153 -0
  447. package/python/vendor/hkc/hkc_api/synthesis.py +179 -0
  448. package/python/vendor/hkc/hkc_core/__init__.py +0 -0
  449. package/python/vendor/hkc/hkc_core/graph/__init__.py +0 -0
  450. package/python/vendor/hkc/hkc_core/graph/base.py +188 -0
  451. package/python/vendor/hkc/hkc_core/graph/sqlite_store.py +789 -0
  452. package/python/vendor/hkc/hkc_core/models/__init__.py +0 -0
  453. package/python/vendor/hkc/hkc_core/models/enums.py +65 -0
  454. package/python/vendor/hkc/hkc_core/models/ku.py +307 -0
  455. package/python/vendor/hkc/hkc_core/utils/__init__.py +0 -0
  456. package/python/vendor/hkc/hkc_core/utils/id_gen.py +113 -0
  457. package/python/vendor/hkc/hkc_crystallizer/__init__.py +44 -0
  458. package/python/vendor/hkc/hkc_crystallizer/adapters.py +94 -0
  459. package/python/vendor/hkc/hkc_crystallizer/candidate.py +145 -0
  460. package/python/vendor/hkc/hkc_crystallizer/crystallizer.py +179 -0
  461. package/python/vendor/hkc/hkc_crystallizer/ingress.py +90 -0
  462. package/python/vendor/hkc/hkc_kde/__init__.py +0 -0
  463. package/python/vendor/hkc/hkc_kde/chunker.py +197 -0
  464. package/python/vendor/hkc/hkc_kde/extractor.py +623 -0
  465. package/python/vendor/hkc/hkc_kde/kde.py +298 -0
  466. package/python/vendor/hkc/hkc_kde/loaders/__init__.py +0 -0
  467. package/python/vendor/hkc/hkc_kde/loaders/ebook_loader.py +153 -0
  468. package/python/vendor/hkc/hkc_kde/loaders/office_loader.py +194 -0
  469. package/python/vendor/hkc/hkc_kde/loaders/pdf_loader.py +162 -0
  470. package/python/vendor/hkc/hkc_kde/loaders/text_loader.py +157 -0
  471. package/python/vendor/hkc/hkc_kde/loaders/web_loader.py +91 -0
  472. package/python/vendor/hkc/hkc_kde/models.py +74 -0
  473. package/python/vendor/hkc/hkc_kde/packager.py +329 -0
  474. package/python/vendor/hkc/hkc_kde/relation_disc.py +151 -0
  475. package/python/vendor/hkc/hkc_kee/__init__.py +0 -0
  476. package/python/vendor/hkc/hkc_kee/dedup.py +227 -0
  477. package/python/vendor/hkc/hkc_kee/fingerprint.py +106 -0
  478. package/python/vendor/hkc/hkc_kee/kee.py +541 -0
  479. package/python/vendor/hkc/hkc_kep/__init__.py +0 -0
  480. package/python/vendor/hkc/hkc_kep/event_bus.py +205 -0
  481. package/python/vendor/hkc/hkc_sdk/__init__.py +63 -0
  482. package/python/vendor/hkc/hkc_sdk/base.py +151 -0
  483. package/python/vendor/hkc/hkc_sdk/direct_client.py +153 -0
  484. package/python/vendor/hkc/hkc_sdk/exceptions.py +39 -0
  485. package/python/vendor/hkc/hkc_sdk/http_client.py +166 -0
  486. package/python/vendor/hkc/hkc_sdk/models.py +171 -0
  487. package/python/vendor/hkc/hkc_search/__init__.py +0 -0
  488. package/python/vendor/hkc/hkc_search/bm25.py +325 -0
  489. package/python/vendor/hkc/hkc_search/embedding_backends.py +374 -0
  490. package/python/vendor/hkc/hkc_search/graph_search.py +132 -0
  491. package/python/vendor/hkc/hkc_search/hybrid.py +331 -0
  492. package/python/vendor/hkc/hkc_search/index_build_manager.py +378 -0
  493. package/python/vendor/hkc/hkc_search/vector_search.py +484 -0
  494. package/python/vendor/hmr/__init__.py +0 -0
  495. package/python/vendor/hmr/core/__init__.py +0 -0
  496. package/python/vendor/hmr/core/hmr.py +684 -0
  497. package/python/vendor/hmr/core/models.py +310 -0
  498. package/python/vendor/hmr/engines/__init__.py +0 -0
  499. package/python/vendor/hmr/engines/evolution.py +710 -0
  500. package/python/vendor/hmr/engines/jit_compiler.py +472 -0
  501. package/python/vendor/hmr/engines/lifecycle.py +264 -0
  502. package/python/vendor/hmr/engines/policy.py +625 -0
  503. package/python/vendor/hmr/engines/recall.py +260 -0
  504. package/python/vendor/hmr/engines/runtime_state.py +158 -0
  505. package/python/vendor/hmr/engines/scheduler.py +323 -0
  506. package/python/vendor/hmr/engines/semantic.py +150 -0
  507. package/python/vendor/hmr/engines/temporal.py +261 -0
  508. package/python/vendor/hmr/engines/thought_chain.py +635 -0
  509. package/python/vendor/hmr/graph/__init__.py +0 -0
  510. package/python/vendor/hmr/graph/cwg.py +338 -0
  511. package/python/vendor/hmr/graph/memory_graph.py +530 -0
  512. package/python/vendor/hmr/storage/__init__.py +0 -0
  513. package/python/vendor/hmr/storage/memory_fs.py +300 -0
  514. package/python/vendor/hmr/storage/vector_store.py +520 -0
@@ -0,0 +1,97 @@
1
+ """
2
+ EventSpine —— 全系统唯一的进程内事件脊柱(P0 第二项:统一事件总线)。
3
+
4
+ 审计结论:此前 4 套并存且不互通的发布订阅(HarnessEventBus / SystemBus /
5
+ HKC EventBus / steering_queue),WS 只桥接了 harness 一套 → truth/process 等
6
+ 系统事件根本到不了前端。EventSpine 把它们收敛成一条脊柱 + 若干"投影"(projection):
7
+ WS / CLI / robot / edge 都只是订阅脊柱的投影,不再各自接线。
8
+
9
+ 设计:
10
+ · 主题(topic)用 `domain.entity.action` 命名;subscribe 支持**前缀匹配**
11
+ (订阅 "harness" 命中 "harness.drift.blocked";订阅 "" 命中一切)。
12
+ · project(sink):sink 收到**每一条**事件(WS/robot/edge 的统一出口)。
13
+ · 并发纪律与 SystemBus 一致:锁只护 history + 订阅表快照,handler 在锁外执行,
14
+ 保证全局锁序恒为 spine→(pm/hmr),无反向嵌套 → 无死锁。
15
+ · Event 刻意提供 .channel/.payload/.to_dict(),与旧 BusMessage 同形,
16
+ 使 SystemBus 可退化为本脊柱的薄门面而不改任何既有消费者。
17
+ """
18
+ from __future__ import annotations
19
+ import threading
20
+ import time
21
+ import uuid
22
+ from collections import deque
23
+ from dataclasses import dataclass, field
24
+ from typing import Callable, Optional
25
+
26
+
27
+ @dataclass
28
+ class Event:
29
+ topic: str
30
+ payload: dict
31
+ ts: float = field(default_factory=time.time)
32
+ source: str = ""
33
+ correlation_id: str = field(default_factory=lambda: uuid.uuid4().hex[:12])
34
+
35
+ @property
36
+ def channel(self) -> str:
37
+ return self.topic
38
+
39
+ def to_dict(self) -> dict:
40
+ return {"channel": self.topic, "topic": self.topic, "payload": self.payload,
41
+ "ts": self.ts, "source": self.source, "correlation_id": self.correlation_id}
42
+
43
+
44
+ class EventSpine:
45
+ """进程内唯一事件脊柱。发布/订阅(前缀匹配) + 投影 + 有界历史。线程安全。"""
46
+
47
+ def __init__(self, history_limit: int = 1000):
48
+ self._subs: list[tuple[str, Callable]] = []
49
+ self._projections: list[Callable] = []
50
+ self._history: deque = deque(maxlen=history_limit)
51
+ self._lock = threading.RLock()
52
+
53
+ def subscribe(self, topic_prefix: str, handler: Callable):
54
+ """订阅某主题(前缀匹配)。handler(event) 在发布线程、锁外被调用。"""
55
+ with self._lock:
56
+ self._subs.append((topic_prefix or "", handler))
57
+
58
+ def project(self, sink: Callable):
59
+ """注册投影:每一条事件都会送给 sink(event)。用于 WS / robot / edge 统一出口。"""
60
+ with self._lock:
61
+ self._projections.append(sink)
62
+
63
+ def publish(self, topic: str, payload: dict | None = None, source: str = "",
64
+ correlation_id: str | None = None) -> Event:
65
+ if correlation_id:
66
+ evt = Event(topic=topic, payload=payload or {}, source=source,
67
+ correlation_id=correlation_id)
68
+ else:
69
+ evt = Event(topic=topic, payload=payload or {}, source=source)
70
+ with self._lock:
71
+ self._history.append(evt)
72
+ handlers = [h for pfx, h in self._subs if topic.startswith(pfx)]
73
+ projections = list(self._projections)
74
+ for h in handlers:
75
+ try:
76
+ h(evt)
77
+ except Exception as e:
78
+ print(f"[EventSpine] handler error on '{topic}': {e}")
79
+ for sink in projections:
80
+ try:
81
+ sink(evt)
82
+ except Exception as e:
83
+ print(f"[EventSpine] projection error on '{topic}': {e}")
84
+ return evt
85
+
86
+ def history(self, topic_prefix: Optional[str] = None, limit: int = 50) -> list:
87
+ with self._lock:
88
+ items = [e for e in self._history
89
+ if topic_prefix is None or e.topic.startswith(topic_prefix)]
90
+ return [e.to_dict() for e in items[-limit:]]
91
+
92
+ def stats(self) -> dict:
93
+ with self._lock:
94
+ return {"subscriptions": len(self._subs),
95
+ "projections": len(self._projections),
96
+ "history_size": len(self._history),
97
+ "history_limit": self._history.maxlen}
@@ -0,0 +1,360 @@
1
+ """
2
+ foxclaw.knowledge —— HKC(Foxclaw Knowledge Core)融入 Foxclaw 的知识子系统。
3
+
4
+ HKC 是成熟的 9 包子系统(KDE 抽取 / KEE 演化 / SQLite+NetworkX 图存储 / 混合检索
5
+ BM25+向量+图 / ACE 能力编译)。按 HMR 同款方式 vendor 在 `vendor/hkc/` 下,同进程
6
+ 装配、经 hkc_sdk DirectClient 调用——不发 HTTP、不起独立服务、无嵌套工程脚手架。
7
+
8
+ 设计要点:
9
+ · 可选依赖优雅降级:缺 vendor/hkc 或缺 networkx 等依赖时 available=False,不影响本体。
10
+ · 模型无关:LLM 抽取复用 Foxclaw 探测出的 provider/key,也可用 HKC_* 环境变量覆盖。
11
+ · 资源可释放:HKCService.close() 关闭全部 4 个 SQLite 连接(经修好的 HKCContainer.close)。
12
+ · 惰性装配:由 Foxclaw.hkc 属性首次访问时才装配,避免每次构造 OS 都开 db 句柄。
13
+ """
14
+ import json
15
+ import os
16
+ import sys
17
+ from pathlib import Path
18
+
19
+
20
+ def _ensure_hkc_importable() -> bool:
21
+ """让内嵌的 hkc_* 包可被导入(与 HMR 的 vendor 机制一致)。
22
+
23
+ 两种布局:
24
+ - 源码仓: vendor/hkc 在项目根下,需注入 sys.path;
25
+ - wheel 安装: hkc_* 已作为顶层包装进 site-packages,直接可导入,无需注入。
26
+ """
27
+ vendor_hkc = Path(__file__).resolve().parents[2] / "vendor" / "hkc"
28
+ if vendor_hkc.exists():
29
+ p = str(vendor_hkc)
30
+ if p not in sys.path:
31
+ sys.path.insert(0, p)
32
+ return True
33
+ try:
34
+ import hkc_api # noqa: F401
35
+ except ImportError:
36
+ return False
37
+ return True
38
+
39
+
40
+ def _to_hkc_provider(name: str) -> str:
41
+ """Foxclaw 的 provider 名 → HKC 底层只认的 4 个协议名。
42
+
43
+ (anthropic / deepseek / openai / openai-compatible)其余全是 OpenAI 协议兼容。
44
+ """
45
+ n = (name or "").strip().lower()
46
+ if n == "anthropic":
47
+ return "anthropic"
48
+ if n == "deepseek":
49
+ return "deepseek"
50
+ return "openai-compatible"
51
+
52
+
53
+ def _read_custom_model(data_dir, name):
54
+ """从 ``custom_models.json`` 取一项,解出 ``(key, base_url, model)``;取不到或 key 为空 → None。
55
+
56
+ base_url 必须和主对话走同一个规范化(缺 path 补 /v1),否则 SDK 拼出
57
+ https://api.deepseek.com/chat/completions 而主对话用 /v1/chat/completions。
58
+ """
59
+ try:
60
+ models = json.loads((data_dir / "custom_models.json").read_text(encoding="utf-8"))
61
+ except Exception:
62
+ return None
63
+ match = next((m for m in models if m.get("name") == name), None)
64
+ if not match:
65
+ return None
66
+ key = ""
67
+ from ..core.secrets import load as _load_secret
68
+ key = _load_secret(name, match.get("api_key_obf") or "")
69
+ key = key or (match.get("api_key") or "")
70
+ if not key.strip():
71
+ return None
72
+ base = match.get("base_url") or ""
73
+ try:
74
+ from ..core.model_router import normalize_base_url
75
+ base = normalize_base_url(base)
76
+ except Exception:
77
+ pass
78
+ return key.strip(), base, (match.get("model") or "").strip()
79
+
80
+
81
+ def _selected_llm():
82
+ """读前端「LLM 管理」里当前选中的模型,返回 ``(provider, key, base_url, model)`` 或 None。
83
+
84
+ 这是**用户在界面上显式做过的选择**(FOXCLAW_DATA_DIR/.current_model),比任何
85
+ env 默认值都更能代表"现在真实在用的那个模型",所以排在 LLM_* 之前。
86
+
87
+ 两个踩过的坑:
88
+ 1. preset 写的字段是 ``provider``,不是 ``name`` —— 只读 name 会让 preset 选择被
89
+ 整个忽略,HKC 于是退回 .env(里面可能躺着一份早就换掉的旧 key)。
90
+ 2. preset 的 key 名义上来自 .env,但用户往往同时在「自定义模型」里配过同一个
91
+ provider(名字相同、key 更新)。界面里填的比 .env 默认新,所以优先它。
92
+ """
93
+ try:
94
+ data_dir = Path(os.environ.get("FOXCLAW_DATA_DIR", "./foxclaw_data"))
95
+ sel_file = data_dir / ".current_model"
96
+ if not sel_file.exists():
97
+ return None
98
+ sel = json.loads(sel_file.read_text(encoding="utf-8"))
99
+ except Exception:
100
+ return None
101
+
102
+ source = (sel.get("source") or "").strip().lower()
103
+ name = (sel.get("name") or sel.get("provider") or "").strip()
104
+ model = (sel.get("model_id") or sel.get("model") or "").strip()
105
+
106
+ if source == "custom" and name:
107
+ got = _read_custom_model(data_dir, name)
108
+ if not got:
109
+ return None
110
+ key, base, custom_model = got
111
+ return _to_hkc_provider("generic"), key, base, model or custom_model
112
+
113
+ if source == "preset" and name:
114
+ got = _read_custom_model(data_dir, name)
115
+ if got:
116
+ key, base, custom_model = got
117
+ return _to_hkc_provider("generic"), key, base, model or custom_model
118
+ env_name = ""
119
+ try:
120
+ from ..core.model_router import PROVIDER_ENV
121
+ env_name = PROVIDER_ENV.get(name, "")
122
+ except Exception:
123
+ pass
124
+ key = (os.environ.get(env_name) or "").strip() if env_name else ""
125
+ return (_to_hkc_provider(name), key, "", model)
126
+
127
+ return None
128
+
129
+
130
+ def _detect_llm(api_key=None):
131
+ """【P2·单 LLM 入口】模型无关地决定 HKC 抽取用的 provider/key/base_url/model。
132
+
133
+ 优先级(由高到低),整体目标是「和主对话用同一个模型」:
134
+ 1. ``HKC_LLM_*`` —— 显式让 HKC 独立配(主对话走云、抽取走本地)
135
+ 2. **前端 LLM 管理当前选中项** —— 用户在界面上做的显式选择
136
+ 3. ``LLM_PROVIDER / LLM_API_KEY / LLM_BASE_URL / LLM_MODEL`` 单入口 env
137
+ 4. 主对话的自动探测(``ModelRouter._auto_detect``)
138
+
139
+ HKC 底层只认 4 个协议名(anthropic / deepseek / openai / openai-compatible),
140
+ 其余按 OpenAI 兼容协议映射过去。
141
+
142
+ 返回 ``(provider, key, base_url, model)``。
143
+ """
144
+ env_provider = (os.environ.get("HKC_LLM_PROVIDER") or "").strip().lower()
145
+ if env_provider in ("anthropic", "deepseek", "openai", "openai-compatible"):
146
+ key = (os.environ.get("HKC_LLM_API_KEY")
147
+ or os.environ.get("LLM_API_KEY")
148
+ or "")
149
+ base = (os.environ.get("HKC_LLM_BASE_URL")
150
+ or os.environ.get("LLM_BASE_URL")
151
+ or "")
152
+ model = (os.environ.get("HKC_LLM_MODEL")
153
+ or os.environ.get("LLM_MODEL")
154
+ or "")
155
+ return env_provider, key or api_key, base, model
156
+
157
+ selected = _selected_llm()
158
+ if selected:
159
+ provider, key, base, model = selected
160
+ return provider, (key or api_key or "").strip(), (base or "").strip(), model
161
+
162
+ provider = (os.environ.get("LLM_PROVIDER") or "").strip().lower()
163
+ key = (os.environ.get("LLM_API_KEY") or api_key or "").strip()
164
+ base = os.environ.get("LLM_BASE_URL", "").strip()
165
+ model = (os.environ.get("LLM_MODEL") or os.environ.get("HKC_LLM_MODEL") or "").strip()
166
+
167
+ provider_env = {}
168
+ if not provider:
169
+ try:
170
+ from ..core.model_router import ModelRouter, PROVIDER_ENV
171
+ provider_env = PROVIDER_ENV
172
+ detected = ModelRouter._auto_detect()
173
+ except Exception:
174
+ detected = ""
175
+ provider = detected if detected and detected not in ("stub", "local") else "generic"
176
+ if not key:
177
+ env_name = provider_env.get(provider) or ""
178
+ if env_name:
179
+ key = (os.environ.get(env_name) or "").strip()
180
+
181
+ return _to_hkc_provider(provider), key, base, model
182
+
183
+
184
+ class HKCService:
185
+ """HKC 在 Foxclaw 内的接入句柄:持有同进程容器 + hkc_sdk DirectClient,统一 close。"""
186
+
187
+ def __init__(self, container, client):
188
+ self._container = container
189
+ self.client = client
190
+
191
+ @property
192
+ def available(self) -> bool:
193
+ return self.client is not None
194
+
195
+ @property
196
+ def event_bus(self):
197
+ """HKC 内部 EventBus(或 None)。供桥接到 Foxclaw 的 EventSpine,使知识生命周期
198
+ 事件(created/merged/conflict/ability/…)对统一观测层可见。"""
199
+ return getattr(self._container, "event_bus", None)
200
+
201
+ @property
202
+ def container(self):
203
+ """同进程 HKCContainer。供 API 把 HKC 自带 REST 路由(星球用的 /knowledge/graph 等)
204
+ 的 get_container 依赖指向这同一个已装配容器(依赖倒置,不另开一套)。"""
205
+ return self._container
206
+
207
+ def stats(self) -> dict:
208
+ try:
209
+ return self._container.stats()
210
+ except Exception as e:
211
+ return {"error": str(e)}
212
+
213
+ def close(self):
214
+ """释放 HKC 全部原生资源(4 个 SQLite 连接 + 摄入线程池)。幂等。"""
215
+ if self._container is not None:
216
+ try:
217
+ self._container.close()
218
+ except Exception:
219
+ pass
220
+ self._container = None
221
+ self.client = None
222
+
223
+
224
+ def init_hkc(data_dir, api_key=None):
225
+ """同进程装配 HKC,返回 HKCService(available=True) 或 None(降级)。
226
+ embedding 默认 auto(本地 sentence-transformers 优先,无库/加载失败自动回退 stub;
227
+ 可 HKC_EMBEDDING=stub 强制关闭真语义;HKC_EMBEDDING_MODEL 指定 local/auto 的模型名,
228
+ 如 BAAI/bge-m3,留空用内置默认模型)。"""
229
+ if not _ensure_hkc_importable():
230
+ print("[Foxclaw] · HKC 未 vendor(可选):vendor/hkc 缺失,知识库降级")
231
+ return None
232
+ try:
233
+ from hkc_api.container import HKCContainer
234
+ from hkc_sdk import connect
235
+ except ImportError as e:
236
+ print(f"[Foxclaw] · HKC 依赖缺失(可选,pip install 'foxclaw[knowledge]'): {e}")
237
+ return None
238
+ try:
239
+ provider, llm_key, llm_base_url, llm_model = _detect_llm(api_key)
240
+ _emb_model = (os.environ.get("EMBEDDING_MODEL")
241
+ or os.environ.get("HKC_EMBEDDING_MODEL")) or None
242
+ container = HKCContainer(
243
+ data_dir=str(Path(data_dir) / "hkc"),
244
+ embedding_kind=os.environ.get("HKC_EMBEDDING", "auto"),
245
+ embedding_url=os.environ.get("HKC_EMBEDDING_URL", "http://localhost:8080"),
246
+ embedding_model=_emb_model,
247
+ llm_api_key=llm_key or None,
248
+ llm_provider=provider,
249
+ llm_model=llm_model or None,
250
+ llm_base_url=llm_base_url or None,
251
+ ).assemble()
252
+ from hkc_api import container as _cm
253
+ _cm._container = container
254
+ print(f"[Foxclaw] ✓ HKC 知识核心已进程内融合 (provider={provider}, "
255
+ f"base={'已配置' if llm_base_url else 'provider默认'}, "
256
+ f"key={'已配置' if llm_key else '未配置(抽取将降级)'})")
257
+ return HKCService(container, connect(container=container))
258
+ except Exception as e:
259
+ print(f"[Foxclaw] ⚠ HKC 装配失败,知识库降级为空: {e}")
260
+ return None
261
+
262
+
263
+ from .truth_crystallizer import TruthCrystallizer
264
+
265
+
266
+ _HKC_BRIDGED_EVENTS = (
267
+ "knowledge.created", "knowledge.updated", "knowledge.merged", "knowledge.deleted",
268
+ "conflict.detected", "conflict.resolved", "claim.status_changed",
269
+ "ability.created", "ability.updated", "agent.used_ability",
270
+ )
271
+
272
+
273
+ def bridge_hkc_events(hkc_event_bus, spine) -> bool:
274
+ """把 HKC 内部 EventBus 的知识生命周期事件桥接到 Foxclaw 的 **EventSpine**——
275
+ 消除审计发现的"HKC 事件孤岛"(P0-2 只桥了 harness+system,漏了 HKC)。桥接后知识
276
+ 演化(去重/合并/冲突/能力编译)对统一观测(WS/robot/history)可见,source="hkc"。
277
+
278
+ 转发在 HKC 摄入线程执行(EventBus handler 同步调用);spine 线程安全;转发失败吞掉、
279
+ 绝不影响 HKC 内部流转。返回是否接线成功。高频的 knowledge.created/updated 仍进脊柱
280
+ history,但由 WS 投影过滤(见 api._register_spine_projection),避免批量摄入泛洪前端。"""
281
+ if hkc_event_bus is None or spine is None:
282
+ return False
283
+
284
+ def _forward(evt: dict):
285
+ try:
286
+ spine.publish(evt.get("event", "knowledge.unknown"),
287
+ evt.get("payload") or {}, source="hkc",
288
+ correlation_id=evt.get("event_id"))
289
+ except Exception:
290
+ pass
291
+
292
+ ok = False
293
+ for ev in _HKC_BRIDGED_EVENTS:
294
+ try:
295
+ hkc_event_bus.subscribe(ev, _forward)
296
+ ok = True
297
+ except Exception:
298
+ pass
299
+ return ok
300
+
301
+
302
+ def _ability_triggers(ab) -> list:
303
+ """从能力生成触发词:display_name + domain + coverage 概念键(供 router 匹配)。"""
304
+ terms = [ab.display_name, ab.domain]
305
+ terms += [str(k) for k in (getattr(ab, "coverage", {}) or {}).keys()]
306
+ return [t for t in terms if t]
307
+
308
+
309
+ def _make_ability_handler(client, ab):
310
+ """能力 skill 的 handler(read、确定性、无 LLM):在能力 domain 内检索任务相关知识并返回。
311
+ 只做知识综合(无外部副作用);LLM 综述可后续叠加。检索失败 → 返回空知识,不崩。"""
312
+ def _handler(query: str = "", **_kw) -> dict:
313
+ try:
314
+ hits = client.search(query, mode="hybrid", top_k=5, domain=ab.domain)
315
+ except Exception:
316
+ hits = []
317
+ return {"ability": ab.ability_key, "domain": ab.domain,
318
+ "knowledge": [{"name": h.name, "summary": h.summary} for h in hits[:5]]}
319
+ return _handler
320
+
321
+
322
+ def register_abilities_as_skills(registry, hkc) -> list:
323
+ """【HKC 第2档 Step4·能力→SkillRegistry(最深融合)】把 HKC 中 **coverage 达标**的 ACE 能力
324
+ 注册成 SkillRegistry 里的 skill——**证据 gate**:覆盖不足的**不注册**(能力不予授予)。
325
+
326
+ 注册的 skill 为 `side_effect=read`(知识综合、无外部副作用)→ 经 SkillAdapter/PolicyGate
327
+ 统一治理、且可被认知管线 `stage_route` 路由(复用 A 方案)。返回注册的 skill id 列表。
328
+ HKC 未启用/无达标能力 → 不注册、返回 []。幂等(同 id 覆盖注册)。"""
329
+ if registry is None or hkc is None or not getattr(hkc, "available", False):
330
+ return []
331
+ from ..skills import SkillSpec
332
+ client = hkc.client
333
+ try:
334
+ keys = list(client.list_abilities())
335
+ except Exception:
336
+ return []
337
+ registered = []
338
+ for key in keys:
339
+ try:
340
+ ab = client.compile_ability(key)
341
+ except Exception:
342
+ continue
343
+ sid = f"ability:{key}"
344
+ registry.register(SkillSpec(
345
+ id=sid, name=ab.display_name or key,
346
+ description=f"知识编译能力:{ab.display_name}(domain={ab.domain},coverage 达标)",
347
+ kind="local", side_effect="read",
348
+ triggers=_ability_triggers(ab),
349
+ handler=_make_ability_handler(client, ab),
350
+ params={"query": "str"}))
351
+ registered.append(sid)
352
+ fresh = set(registered)
353
+ for spec in registry.all():
354
+ if spec.id.startswith("ability:") and spec.id not in fresh:
355
+ registry.unregister(spec.id)
356
+ return registered
357
+
358
+
359
+ __all__ = ["init_hkc", "HKCService", "TruthCrystallizer", "bridge_hkc_events",
360
+ "register_abilities_as_skills"]
@@ -0,0 +1,89 @@
1
+ """
2
+ TruthCrystallizer —— 把执行真值经 KDE→KEE 结晶进 HKC(v2 ④ Step1)。
3
+
4
+ 审计边界(P2 已厘清):HMR = 情景记忆("发生过什么");HKC = KEE 裁决的知识("什么是真的")。
5
+ 真值本质是"知识",应经 **KEE 裁决**(去重/合并/冲突,守 Principle 07)进 HKC,而非只塞 HMR。
6
+
7
+ Step1(叠加、默认关、行为不变):
8
+ · 订阅 `truth.appended`;启用且 HKC 可用时,把真值 fact 作为知识候选**后台**推给
9
+ `hkc.client.ingest_text`(KDE 抽取 → 发 knowledge.created → KEE 去重/合并入图)。
10
+ · **后台线程执行**:KDE 调 LLM,绝不能阻塞 run_task 的同步真值链(truth.appended 处理器
11
+ 只提交任务、立即返回)。
12
+ · **双写**:TruthPipeline 的 HMR 共享真值写保留不变;本结晶器只**叠加** HKC 路径。
13
+ · 默认关(FOXCLAW_TRUTH_TO_HKC=1 才开):KDE 有 LLM 成本,先 opt-in;后续步骤再翻默认。
14
+ """
15
+ from __future__ import annotations
16
+ import threading
17
+ from concurrent.futures import ThreadPoolExecutor
18
+ from typing import Any, Callable, cast, Optional
19
+
20
+
21
+ class TruthCrystallizer:
22
+ def __init__(self, hkc_provider: Callable[[], object], enabled: bool = False):
23
+ self._hkc_provider = hkc_provider
24
+ self.enabled = enabled
25
+ self._executor: Optional[ThreadPoolExecutor] = None
26
+ self._closed = False
27
+ self._lock = threading.Lock()
28
+ self._pending: list = []
29
+
30
+ def _ex(self) -> Optional[ThreadPoolExecutor]:
31
+ with self._lock:
32
+ if self._closed:
33
+ return None
34
+ if self._executor is None:
35
+ self._executor = ThreadPoolExecutor(
36
+ max_workers=1, thread_name_prefix="truth-crystallize")
37
+ return self._executor
38
+
39
+ def on_truth(self, msg) -> None:
40
+ """订阅 truth.appended 的处理器(同步、非阻塞):只提交后台结晶任务。"""
41
+ if not self.enabled:
42
+ return
43
+ p = getattr(msg, "payload", None) or {}
44
+ if p.get("blocked"):
45
+ return
46
+ fact = ((p.get("truth") or {}).get("fact") or "").strip()
47
+ if not fact:
48
+ return
49
+ domain = p.get("agent_id") or "execution"
50
+ ex = self._ex()
51
+ if ex is None:
52
+ return
53
+ fut = ex.submit(self._crystallize, fact, domain)
54
+ with self._lock:
55
+ self._pending = [f for f in self._pending if not f.done()]
56
+ self._pending.append(fut)
57
+
58
+ def _crystallize(self, fact: str, domain: str) -> bool:
59
+ """后台执行:惰性取 HKC → ingest_text(KDE→KEE)。任何失败都吞掉,不影响主流程。"""
60
+ try:
61
+ hkc = cast(Any, self._hkc_provider())
62
+ except Exception:
63
+ hkc = None
64
+ if hkc is None or not getattr(hkc, "available", False):
65
+ return False
66
+ try:
67
+ hkc.client.ingest_text(fact, source_title="真值", domain=domain)
68
+ return True
69
+ except Exception:
70
+ return False
71
+
72
+ def drain(self, timeout: float = 10.0) -> None:
73
+ """等待所有已提交的结晶任务完成(测试/优雅关停用)。"""
74
+ with self._lock:
75
+ pending = list(self._pending)
76
+ self._pending = []
77
+ for fut in pending:
78
+ try:
79
+ fut.result(timeout=timeout)
80
+ except Exception:
81
+ pass
82
+
83
+ def close(self) -> None:
84
+ self.drain()
85
+ with self._lock:
86
+ self._closed = True
87
+ ex = self._executor
88
+ if ex is not None:
89
+ ex.shutdown(wait=True)
@@ -0,0 +1,12 @@
1
+ """Foxclaw — Learning: office Agent 的学习行为操作系统内核"""
2
+ from .models import (
3
+ KnowledgeItem, ReviewState, KnowledgeEdge, LearningVerdict,
4
+ )
5
+ from .spaced_repetition import SpacedRepetition
6
+ from .knowledge_graph import KnowledgeGraph
7
+ from .runtime import LearningRuntime
8
+
9
+ __all__ = [
10
+ "KnowledgeItem", "ReviewState", "KnowledgeEdge", "LearningVerdict",
11
+ "SpacedRepetition", "KnowledgeGraph", "LearningRuntime",
12
+ ]