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,306 @@
1
+ """领域内核端点(B 阶段三内核):财务 / 学习 / 行程 + 多格式文件导入。
2
+
3
+ 从 foxclaw/api.py 原样搬移(纯搬移,零行为变更)。共享 OS 实例经 `_api.current_os()`
4
+ 晚绑定读取;路由用 `@app` 装饰器在 import 时注册。
5
+ """
6
+
7
+ import os
8
+ import json
9
+ import asyncio
10
+
11
+ from typing import Optional
12
+
13
+ from fastapi import HTTPException, UploadFile, File
14
+ from pydantic import BaseModel
15
+
16
+ import foxclaw.api as _api
17
+ from foxclaw.core.paths import resolve_import_path
18
+ from .. import app
19
+
20
+
21
+ class ImportPayload(BaseModel):
22
+ csv_path: Optional[str] = None
23
+ rows: Optional[list] = None
24
+
25
+
26
+ def _resolve_csv_path(raw: str) -> str:
27
+ """把请求里的 csv_path 收敛到允许目录内(默认 FOXCLAW_DATA_DIR)。
28
+
29
+ 此前 csv_path 原样进 import_csv → open(),等于给 HTTP 面开放「读本机任意文件」,
30
+ 且内容会落入知识库/账本再从 /api 读回。库调用(CLI/测试/示例)不走这里:
31
+ 那是本地可信代码,本身就有文件权限,加限制只破坏用法不增加安全。
32
+ """
33
+ try:
34
+ return str(resolve_import_path(raw))
35
+ except ValueError as e:
36
+ raise HTTPException(400, f"csv_path 不允许: {e}")
37
+
38
+
39
+ @app.get("/api/finance/analysis")
40
+ async def finance_analysis(period: Optional[str] = None,
41
+ current_balance: Optional[float] = None):
42
+ """财务内核综合分析:预算/异常/订阅/烧钱/守门人裁决。"""
43
+ try:
44
+ return _api.os_instance().finance.analyze_finances(
45
+ period=period, current_balance=current_balance)
46
+ except Exception as e:
47
+ raise HTTPException(500, f"财务分析失败: {e}")
48
+
49
+
50
+ @app.post("/api/finance/import")
51
+ async def finance_import(p: ImportPayload):
52
+ """导入交易数据(CSV 路径或记录数组)。"""
53
+ rt = _api.os_instance().finance_runtime
54
+ src = _resolve_csv_path(p.csv_path) if p.csv_path else ""
55
+ try:
56
+ if src:
57
+ n = rt.import_csv(src)
58
+ elif p.rows:
59
+ from foxclaw.finance import Transaction
60
+ n = 0
61
+ for row in p.rows:
62
+ rt.add_transaction(Transaction(**row), run_gatekeeper=False, save=False)
63
+ n += 1
64
+ if n:
65
+ rt.save()
66
+ else:
67
+ return {"imported": 0, "note": "未提供数据"}
68
+ return {"imported": n, "status": rt.status()}
69
+ except Exception as e:
70
+ raise HTTPException(400, f"导入失败: {e}")
71
+
72
+
73
+ class RecordPayload(BaseModel):
74
+ text: str
75
+ account: str = "default"
76
+ dry_run: bool = False
77
+
78
+
79
+ class FxPayload(BaseModel):
80
+ currency: str
81
+ rate: float
82
+
83
+
84
+ @app.post("/api/finance/record")
85
+ async def finance_record(p: RecordPayload):
86
+ """一句话记账:「今天买了一件衣服花了 105 块」/「明天交房租 3000」。"""
87
+ rt = _api.os_instance().finance_runtime
88
+ try:
89
+ return rt.record_text(p.text, account=p.account, dry_run=p.dry_run)
90
+ except Exception as e:
91
+ raise HTTPException(400, f"记账失败: {e}")
92
+
93
+
94
+ @app.get("/api/finance/ledger")
95
+ async def finance_ledger(period: Optional[str] = None):
96
+ """记账本:按月汇总(已发生/计划、按天、按分类、按币种)。"""
97
+ rt = _api.os_instance().finance_runtime
98
+ try:
99
+ return rt.ledger(period)
100
+ except Exception as e:
101
+ raise HTTPException(500, f"记账本汇总失败: {e}")
102
+
103
+
104
+ @app.get("/api/finance/fx")
105
+ async def finance_fx():
106
+ """当前汇率表(本位币 CNY)。"""
107
+ return _api.os_instance().finance_runtime.fx.snapshot()
108
+
109
+
110
+ @app.post("/api/finance/fx")
111
+ async def finance_set_fx(p: FxPayload):
112
+ """设置某个币种的记账汇率(比如刷卡当天的实际结算价)。"""
113
+ try:
114
+ return _api.os_instance().finance_runtime.fx.set_rate(p.currency, p.rate)
115
+ except ValueError as e:
116
+ raise HTTPException(400, str(e))
117
+
118
+
119
+ @app.get("/api/learning/analysis")
120
+ async def learning_analysis():
121
+ """学习内核综合分析:掌握度/遗忘风险/知识图谱/学习策略。"""
122
+ try:
123
+ return _api.os_instance().office.analyze_learning()
124
+ except Exception as e:
125
+ raise HTTPException(500, f"学习分析失败: {e}")
126
+
127
+
128
+ @app.post("/api/learning/import")
129
+ async def learning_import(p: ImportPayload):
130
+ """导入知识点(办公学习内核)。learn() 内部已把内容存进 HKC(连续 ConceptKU,知识真源),
131
+ 不再另做桥接。"""
132
+ rt = _api.os_instance().learning_runtime
133
+ src = _resolve_csv_path(p.csv_path) if p.csv_path else ""
134
+ try:
135
+ if src:
136
+ n = rt.import_csv(src)
137
+ elif p.rows:
138
+ n = 0
139
+ for row in p.rows:
140
+ rt.learn(**row); n += 1
141
+ else:
142
+ return {"imported": 0, "note": "未提供数据"}
143
+ rt.build_connections()
144
+ return {"imported": n, "status": rt.status()}
145
+ except Exception as e:
146
+ raise HTTPException(400, f"导入失败: {e}")
147
+
148
+
149
+ @app.delete("/api/learning/item/{item_id}")
150
+ async def learning_delete(item_id: str):
151
+ """删除一个学习知识点(内存+复习状态+图谱,并落盘)。"""
152
+ rt = _api.os_instance().learning_runtime
153
+ ok = rt.delete(item_id)
154
+ return {"deleted": ok, "status": rt.status()}
155
+
156
+
157
+ class NotePayload(BaseModel):
158
+ item_id: str
159
+ note: str
160
+
161
+
162
+ @app.post("/api/learning/note")
163
+ async def learning_note(p: NotePayload):
164
+ """给一个学习知识点追加一条笔记(有内容才存;没新增不写盘)。"""
165
+ rt = _api.os_instance().learning_runtime
166
+ r = rt.add_note(p.item_id, p.note)
167
+ if "error" in r:
168
+ raise HTTPException(400, r["error"])
169
+ return r
170
+
171
+
172
+ @app.get("/api/itinerary/plan")
173
+ async def itinerary_plan(mode: str = "driving"):
174
+ """行程内核规划:路线优化/可达性/冲突/守门人 + 高德可视化数据。"""
175
+ try:
176
+ return _api.os_instance().office.plan_itinerary(mode=mode)
177
+ except Exception as e:
178
+ raise HTTPException(500, f"行程规划失败: {e}")
179
+
180
+
181
+ @app.post("/api/itinerary/import")
182
+ async def itinerary_import(p: ImportPayload):
183
+ """导入行程点。"""
184
+ rt = _api.os_instance().itinerary_runtime
185
+ src = _resolve_csv_path(p.csv_path) if p.csv_path else ""
186
+ try:
187
+ if src:
188
+ n = rt.import_csv(src)
189
+ elif p.rows:
190
+ n = 0
191
+ for row in p.rows:
192
+ rt.add_waypoint(**row); n += 1
193
+ else:
194
+ return {"imported": 0, "note": "未提供数据"}
195
+ return {"imported": n, "status": rt.status()}
196
+ except Exception as e:
197
+ raise HTTPException(400, f"导入失败: {e}")
198
+
199
+
200
+ _IMPORT_MAX_BYTES = 10 * 1024 * 1024
201
+ _IMPORT_COLUMNS = {
202
+ "finance": ["amount", "category", "entity", "account", "timestamp", "tags"],
203
+ "learning": ["content", "topic", "source", "tags"],
204
+ "itinerary": ["name", "address", "lng", "lat", "start_time", "duration_min", "city"],
205
+ }
206
+
207
+
208
+ def _parse_import_rows(filename: str, data: bytes) -> list:
209
+ """把上传的 csv / xlsx / json 统一解析成 [dict]。"""
210
+ ext = os.path.splitext(filename or "")[1].lower()
211
+ if ext == ".json":
212
+ obj = json.loads(data.decode("utf-8"))
213
+ rows = obj.get("rows") if isinstance(obj, dict) else obj
214
+ if not isinstance(rows, list):
215
+ raise ValueError("JSON 应为对象数组或 {rows:[...]}")
216
+ return [dict(r) for r in rows if isinstance(r, dict)]
217
+ if ext in (".csv", ".txt", ""):
218
+ import csv as _csv
219
+ import io as _io
220
+ text = data.decode("utf-8-sig", errors="replace")
221
+ return list(_csv.DictReader(_io.StringIO(text)))
222
+ if ext == ".xlsx":
223
+ import openpyxl as _openpyxl
224
+ import io as _io
225
+ wb = _openpyxl.load_workbook(_io.BytesIO(data), read_only=True, data_only=True)
226
+ try:
227
+ grid = list(wb.active.iter_rows(values_only=True))
228
+ finally:
229
+ wb.close()
230
+ if not grid:
231
+ return []
232
+ headers = [str(h).strip() if h is not None else "" for h in grid[0]]
233
+ out = []
234
+ for r in grid[1:]:
235
+ d = {headers[i]: ("" if v is None else v)
236
+ for i, v in enumerate(r) if i < len(headers) and headers[i]}
237
+ if any(str(x).strip() for x in d.values()):
238
+ out.append(d)
239
+ return out
240
+ raise ValueError(f"不支持的格式 {ext};支持 .csv .xlsx .json")
241
+
242
+
243
+ def _rows_to_temp_csv(rows: list, columns: list) -> str:
244
+ """把任意来源 rows 写成运行时 import_csv 认识的 CSV(tags 列表 → | 连接),返回临时路径。"""
245
+ import csv as _csv
246
+ import tempfile
247
+ fd, path = tempfile.mkstemp(suffix=".csv")
248
+ with os.fdopen(fd, "w", newline="", encoding="utf-8") as f:
249
+ w = _csv.DictWriter(f, fieldnames=columns, extrasaction="ignore")
250
+ w.writeheader()
251
+ for r in rows:
252
+ rr = dict(r)
253
+ if isinstance(rr.get("tags"), (list, tuple)):
254
+ rr["tags"] = "|".join(str(t) for t in rr["tags"])
255
+ w.writerow({c: ("" if rr.get(c) is None else rr.get(c)) for c in columns})
256
+ return path
257
+
258
+
259
+ async def _domain_import_file(domain: str, runtime, file: UploadFile):
260
+ """统一领域文件导入:解析 → 桥接 CSV → 运行时 import_csv(线程池,含阻塞 I/O)。"""
261
+ data = await file.read()
262
+ if len(data) > _IMPORT_MAX_BYTES:
263
+ raise HTTPException(413, f"文件过大(上限 {_IMPORT_MAX_BYTES // 1048576}MB)")
264
+ try:
265
+ rows = _parse_import_rows(file.filename or "", data)
266
+ except Exception as e:
267
+ raise HTTPException(400, f"解析失败: {e}")
268
+ if not rows:
269
+ return {"imported": 0, "note": "文件无有效数据行"}
270
+ tmp = _rows_to_temp_csv(rows, _IMPORT_COLUMNS[domain])
271
+ try:
272
+ n = await asyncio.to_thread(runtime.import_csv, tmp)
273
+ except Exception as e:
274
+ raise HTTPException(400, f"导入失败(检查列名是否匹配): {e}")
275
+ finally:
276
+ try:
277
+ os.unlink(tmp)
278
+ except OSError:
279
+ pass
280
+ return {"imported": n, "status": runtime.status()}
281
+
282
+
283
+ @app.post("/api/finance/import/file")
284
+ async def finance_import_file(file: UploadFile = File(...)):
285
+ """上传 csv/xlsx/json 导入交易。列:amount,category,entity,account,timestamp,tags。"""
286
+ rt = _api.os_instance().finance_runtime
287
+ return await _domain_import_file("finance", rt, file)
288
+
289
+
290
+ @app.post("/api/learning/import/file")
291
+ async def learning_import_file(file: UploadFile = File(...)):
292
+ """上传 csv/xlsx/json 导入知识点。列:content,topic,source,tags。"""
293
+ rt = _api.os_instance().learning_runtime
294
+ res = await _domain_import_file("learning", rt, file)
295
+ try:
296
+ rt.build_connections()
297
+ except Exception:
298
+ pass
299
+ return res
300
+
301
+
302
+ @app.post("/api/itinerary/import/file")
303
+ async def itinerary_import_file(file: UploadFile = File(...)):
304
+ """上传 csv/xlsx/json 导入行程点。列:name,address,lng,lat,start_time,duration_min,city。"""
305
+ rt = _api.os_instance().itinerary_runtime
306
+ return await _domain_import_file("itinerary", rt, file)
@@ -0,0 +1,145 @@
1
+ """知识核心(HKC)端点:同进程 DirectClient,惰性装配,缺依赖降级。
2
+
3
+ 从 foxclaw/api.py 原样搬移(纯搬移,零行为变更)。共享 OS 实例经 `_api.current_os()`
4
+ 晚绑定读取;路由用 `@app` 装饰器在 import 时注册。
5
+ """
6
+
7
+ import asyncio
8
+
9
+ from fastapi import HTTPException
10
+ from pydantic import BaseModel
11
+
12
+ import foxclaw.api as _api
13
+ from .. import app
14
+
15
+
16
+ class KnowledgeSearchPayload(BaseModel):
17
+ query: str
18
+ mode: str = "hybrid"
19
+ top_k: int = 10
20
+ domain: str = ""
21
+
22
+
23
+ class KnowledgeIngestPayload(BaseModel):
24
+ text: str
25
+ source_title: str = ""
26
+ domain: str = ""
27
+
28
+
29
+ def _hkc_or_none():
30
+ """惰性取 HKCService;None 表示降级(未 vendor/缺依赖/装配失败)。"""
31
+ try:
32
+ return _api.os_instance().hkc
33
+ except Exception:
34
+ return None
35
+
36
+
37
+ try:
38
+ from hkc_sdk.exceptions import HKCInsufficientCoverage as _HKCInsuffCov
39
+ except Exception:
40
+ _HKCInsuffCov = None
41
+
42
+
43
+ @app.post("/api/knowledge/search")
44
+ async def knowledge_search(p: KnowledgeSearchPayload):
45
+ """混合检索知识库。HKC 未启用或检索失败时返回空 hits(与整体降级哲学一致)。"""
46
+ hkc = _hkc_or_none()
47
+ if hkc is None or not hkc.available:
48
+ return {"available": False, "hits": []}
49
+ try:
50
+ hits = await asyncio.to_thread(
51
+ hkc.client.search, p.query, p.mode, p.top_k, p.domain)
52
+ except Exception as e:
53
+ return {"available": True, "hits": [], "error": str(e)}
54
+ return {"available": True, "hits": [
55
+ {"ku_id": h.ku_id, "score": h.score, "name": h.name,
56
+ "summary": h.summary, "mode": h.mode, "ku_type": getattr(h, "ku_type", "")}
57
+ for h in hits]}
58
+
59
+
60
+ @app.post("/api/knowledge/ingest")
61
+ async def knowledge_ingest(p: KnowledgeIngestPayload):
62
+ """摄入文本 → KDE 抽取 KU → KEE 演化(守 Principle 07)。需配置 LLM;未配则抽取降级。"""
63
+ hkc = _hkc_or_none()
64
+ if hkc is None or not hkc.available:
65
+ raise HTTPException(503, "HKC 未启用(未 vendor 或缺依赖)")
66
+ try:
67
+ res = await asyncio.to_thread(
68
+ hkc.client.ingest_text, p.text, p.source_title, p.domain)
69
+ return {"ku_count": res.ku_count, "ku_ids": res.ku_ids, "counts": res.counts}
70
+ except Exception as e:
71
+ msg = str(e).strip() or e.__class__.__name__
72
+ raise HTTPException(502, f"摄入失败:{msg}")
73
+
74
+
75
+ @app.get("/api/knowledge/recent")
76
+ async def knowledge_recent(limit: int = 10):
77
+ """最近录入的知识单元。右栏「最近知识」看前 10 条,知识库面板看全量。
78
+
79
+ 只读、无 LLM。短知识经 `/api/knowledge/ingest` 进来,长文档走知识星球(3D 图谱);
80
+ 两边落的是同一个图库,所以这里能看到全部新增 KU 而不只是「短知识」。
81
+
82
+ 为什么不复用 HKC 的 `/knowledge/graph`:那条路径的 KUResponse 不带 created_at
83
+ (schema 里没有),无法按时间倒序。这里直接读已装配容器的 graph_store 拿原始
84
+ KU 对象(带 created_at),避免为了排序去改 vendor 里的公开 schema。
85
+ """
86
+ hkc = _hkc_or_none()
87
+ if hkc is None or not hkc.available:
88
+ return {"available": False, "total": 0, "kus": []}
89
+ try:
90
+ store = hkc.container.graph_store
91
+ kus = await asyncio.to_thread(store.query_all, 4000)
92
+ except Exception as e:
93
+ return {"available": True, "total": 0, "kus": [], "error": str(e)}
94
+
95
+ kus.sort(key=lambda k: getattr(k, "created_at", "") or "", reverse=True)
96
+ n = max(1, min(int(limit or 10), 200))
97
+ return {"available": True, "total": len(kus), "kus": [
98
+ {"ku_id": ku.ku_id, "ku_type": ku.ku_type.value, "name": ku.name,
99
+ "summary": ku.summary or "", "domain": ku.domain or "",
100
+ "created_at": getattr(ku, "created_at", "") or "",
101
+ "tags": list(ku.tags or [])}
102
+ for ku in kus[:n]]}
103
+
104
+
105
+ @app.get("/api/knowledge/abilities")
106
+ async def knowledge_abilities():
107
+ """列出可编译的能力 key(来自 ACE skill_taxonomy)。只读、确定性、无 LLM。"""
108
+ hkc = _hkc_or_none()
109
+ if hkc is None or not hkc.available:
110
+ return {"available": False, "abilities": []}
111
+ try:
112
+ keys = await asyncio.to_thread(hkc.client.list_abilities)
113
+ except Exception as e:
114
+ return {"available": True, "abilities": [], "error": str(e)}
115
+ return {"available": True, "abilities": list(keys)}
116
+
117
+
118
+ @app.get("/api/knowledge/ability/{ability_key}")
119
+ async def knowledge_ability(ability_key: str):
120
+ """编译一个能力,返回 coverage/skills/workflows。**证据驱动 gate**:知识覆盖不足时
121
+ 返回 `sufficient=False` + coverage/missing_skills——这不是错误,是"知识还不足以具备该能力"
122
+ (Foxclaw 证据驱动哲学:没根据的能力不予授予)。只读、无 LLM。"""
123
+ hkc = _hkc_or_none()
124
+ if hkc is None or not hkc.available:
125
+ return {"available": False}
126
+ try:
127
+ ab = await asyncio.to_thread(hkc.client.compile_ability, ability_key)
128
+ except Exception as e:
129
+ if _HKCInsuffCov is not None:
130
+ is_insufficient = isinstance(e, _HKCInsuffCov)
131
+ else:
132
+ is_insufficient = hasattr(e, "missing_skills")
133
+ if is_insufficient:
134
+ return {"available": True, "sufficient": False,
135
+ "coverage": getattr(e, "coverage", {}),
136
+ "missing_skills": getattr(e, "missing_skills", []),
137
+ "reason": str(e)}
138
+ raise HTTPException(404, f"能力编译失败(未知 key 或图错误): {e}")
139
+ return {"available": True, "sufficient": True, "ability": {
140
+ "ability_key": ab.ability_key, "display_name": ab.display_name,
141
+ "domain": ab.domain, "coverage": ab.coverage, "version": ab.version,
142
+ "skills": [{"skill_key": s.skill_key, "display_name": s.display_name,
143
+ "coverage": s.coverage, "concept_hits": s.concept_hits}
144
+ for s in ab.skills],
145
+ "workflows": ab.workflows}}