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,71 @@
1
+ $ErrorActionPreference = "Continue"
2
+
3
+ $root = Split-Path -Parent $PSScriptRoot
4
+
5
+ $py = Join-Path $root ".venv\Scripts\python.exe"
6
+ if (-not (Test-Path $py)) {
7
+ Write-Host "[1] WARN: .venv missing, fallback to PATH python (see README quick start)"
8
+ $py = (Get-Command python -ErrorAction SilentlyContinue).Source
9
+ }
10
+ if (-not $py) { $py = "python" }
11
+ Write-Host "[1] python = $py"
12
+
13
+ Write-Host "[2] Find LISTENING on :8000"
14
+ $targetPid = $null
15
+ $netstatLines = netstat -ano | Select-String ":8000"
16
+ foreach ($line in $netstatLines) {
17
+ $s = $line.ToString()
18
+ if ($s -match 'LISTENING\s+(\d+)$') {
19
+ $targetPid = $Matches[1]
20
+ break
21
+ }
22
+ }
23
+
24
+ if ($targetPid) {
25
+ Write-Host " -> Kill PID $targetPid"
26
+ taskkill /F /PID $targetPid | Out-Null
27
+ Start-Sleep -Seconds 2
28
+ } else {
29
+ Write-Host " -> No LISTEN on :8000 (may already be down)"
30
+ }
31
+
32
+ $logDir = Join-Path $root "foxclaw_data"
33
+ if (-not (Test-Path $logDir)) { New-Item -ItemType Directory -Path $logDir | Out-Null }
34
+ $outLog = Join-Path $logDir "uvicorn.log"
35
+ $errLog = Join-Path $logDir "uvicorn.err.log"
36
+ "" | Out-File -FilePath $outLog -Encoding utf8
37
+ "" | Out-File -FilePath $errLog -Encoding utf8
38
+ Write-Host "[3] Start service (hidden window, --reload)"
39
+ Write-Host " stdout -> $outLog"
40
+ Write-Host " stderr -> $errLog"
41
+ $uviArgs = @(
42
+ "-m", "uvicorn", "foxclaw.api:app",
43
+ "--port", "8000",
44
+ "--reload",
45
+ "--reload-dir", (Join-Path $root "foxclaw")
46
+ )
47
+ Start-Process $py -ArgumentList $uviArgs `
48
+ -WorkingDirectory $root `
49
+ -WindowStyle Hidden `
50
+ -RedirectStandardOutput $outLog `
51
+ -RedirectStandardError $errLog | Out-Null
52
+ Write-Host " -> Started, polling /stats up to 30s"
53
+
54
+ $ready = $false
55
+ for ($i=0; $i -lt 15; $i++) {
56
+ try {
57
+ $s = Invoke-RestMethod http://127.0.0.1:8000/stats -TimeoutSec 2 -UseBasicParsing
58
+ if ($s) {
59
+ $ready = $true
60
+ Write-Host ("[4] OK: vector_size={0} bm25={1} state={2} old_pid={3}" -f `
61
+ $s.vector_size, $s.bm25_size, $s.index_build.index_state, $targetPid)
62
+ break
63
+ }
64
+ } catch {}
65
+ Start-Sleep -Seconds 2
66
+ }
67
+
68
+ if (-not $ready) {
69
+ Write-Host "[4] FAIL: service did not respond in 30s"
70
+ exit 1
71
+ }
@@ -0,0 +1,190 @@
1
+ #!/usr/bin/env python3
2
+ """P0-3 共享层写入策略 —— 端到端验收工具
3
+
4
+ 它解决什么问题
5
+ --------------
6
+ P0-3 定义了「谁能往共享层写什么」的策略表(foxclaw/core/shared_write.py),但它默认跑在
7
+ audit 模式 —— 只记录、不拦截。切到 enforce 之前必须先回答一个问题:
8
+
9
+ 这份策略表在**真实调用路径**上,到底会不会误判?
10
+
11
+ 单测(tests/test_shared_write.py)用 Mock 后端验证了策略函数本身;本工具补的是端到端
12
+ 那一层:走**真实 Foxclaw 装配**(真实 HMRAdapter + 真实 EventSpine + 真实 Agent),
13
+ 用 8 处**真实写入点的原样参数**跑一遍,确认零误伤;再用越界探针确认真的拦得住。
14
+
15
+ 用法
16
+ ----
17
+ python tools/shared_write_audit.py # 验收(默认 audit 模式)
18
+ python tools/shared_write_audit.py --mode enforce # 验证 enforce 的拦截与放行
19
+ python tools/shared_write_audit.py --keep # 保留临时数据目录(排查用)
20
+
21
+ 退出码:0 = 全部符合预期;1 = 有偏离(详见输出)
22
+
23
+ 数据安全:默认在临时目录建实例,不碰 ./foxclaw_data;不加载项目 .env(本工具不 import
24
+ foxclaw.api,而 load_dotenv 只发生在那里)。
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ import argparse
30
+ import os
31
+ import shutil
32
+ import sys
33
+ import tempfile
34
+ from pathlib import Path
35
+
36
+ sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
37
+
38
+ os.environ.setdefault("HKC_EMBEDDING", "stub")
39
+
40
+ for _s in (sys.stdout, sys.stderr):
41
+ try:
42
+ _s.reconfigure(encoding="utf-8")
43
+ except Exception:
44
+ pass
45
+
46
+ _FAILS: list[str] = []
47
+ _TOTAL = 0
48
+
49
+
50
+ def check(cond: bool, msg: str) -> bool:
51
+ global _TOTAL
52
+ _TOTAL += 1
53
+ print(f" {'✓' if cond else '✗'} {msg}")
54
+ if not cond:
55
+ _FAILS.append(msg)
56
+ return bool(cond)
57
+
58
+
59
+ _WHO_ANY = ("supervisor", "finance", "office", "admin", "assistant")
60
+ LEGITIMATE = [
61
+ ("supervisor", "decision", ["dispatch", "multi_step", "to:finance"], "多步记录", "主管多步分发"),
62
+ ("supervisor", "decision", ["dispatch", "parallel", "to:office"], "并行记录", "主管并行分发"),
63
+ ("supervisor", "decision", [], "主管介绍", "主管自答(元问题)"),
64
+ ("supervisor", "decision", [], "主管自答", "主管自答(正面对话)"),
65
+ ("supervisor", "decision", ["dispatch", "to:finance"], "调度记录", "主管单步调度"),
66
+ ("supervisor", "decision", [], "主管处理", "主管兜底自处理"),
67
+ ("__ANY__", "preference", ["preference"], "用户偏好", "偏好固化(任一 Agent)"),
68
+ ("system", "execution", ["truth_pipeline"], "真值", "真值管线沉淀"),
69
+ ]
70
+
71
+ INTRUSIONS = [
72
+ ("office", "decision", ["finance.transfer"], "财务应给办公转账1000元", "办公→财务职责域"),
73
+ ("office", "decision", ["itinerary.plan"], "把周三会议改到周五", "办公→行政职责域"),
74
+ ("assistant", "decision", ["dispatch"], "任务已派发给财务", "助手冒充主管分发"),
75
+ ("finance", "concept", ["learning.notes"], "RAG 学习笔记", "财务→办公学习域"),
76
+ ("stranger", "decision", ["misc"], "未登记写入方", "未登记 Agent"),
77
+ ]
78
+
79
+
80
+ def _emit(hmr, agent_id, mem_type, tags, content):
81
+ """按真实路径发起一次共享层写入(_store_memory 最终就是 ingest)。"""
82
+ hmr.ingest(content, type=mem_type, agent_id=agent_id, tags=tags, shared=True)
83
+
84
+
85
+ def audit_case(hmr, agent_id, mem_type, tags, content,
86
+ expect_allowed: bool) -> None:
87
+ """跑一笔写入,断言判定与预期一致 —— 从**真链路**上取判定,不直接调 policy。
88
+
89
+ 判定来源是审计事件(audit/warn 不拦截,只能从事件读 allowed;enforce 下被抛异常
90
+ 同样以 allowed=False 记在事件里)。按事件数**增量**取本次那条,避免某次写入未产生
91
+ 事件时误读上一次的结果 —— 那会让该断言变成"永远通过"的假断言。
92
+ """
93
+ from foxclaw.core.shared_write import SharedWriteDenied
94
+ spine = hmr.shared_write_spine
95
+ before = len(spine.history("memory.shared.write", 100000))
96
+ try:
97
+ _emit(hmr, agent_id, mem_type, tags, content)
98
+ denied = False
99
+ except SharedWriteDenied:
100
+ denied = True
101
+ new = spine.history("memory.shared.write", 100000)[before:]
102
+ got = None if not new else new[-1]["payload"]["allowed"]
103
+ if denied:
104
+ got = False
105
+ check(got == expect_allowed,
106
+ f"{agent_id:<10} type={mem_type:<10} tags={tags or '[]'} → "
107
+ f"{'允许' if got else '拒绝'}(预期{'允许' if expect_allowed else '拒绝'})")
108
+
109
+
110
+ def main() -> int:
111
+ ap = argparse.ArgumentParser(description="P0-3 共享层写入策略端到端验收")
112
+ ap.add_argument("--mode", choices=["audit", "warn", "enforce"], default="audit",
113
+ help="策略模式(默认 audit)。enforce 会真拦截,用于验证拦截行为")
114
+ ap.add_argument("--keep", action="store_true", help="保留临时数据目录")
115
+ args = ap.parse_args()
116
+
117
+ os.environ["FOXCLAW_SHARED_WRITE_MODE"] = args.mode
118
+ data_dir = tempfile.mkdtemp(prefix="foxclaw_swa_")
119
+
120
+ print("=" * 66)
121
+ print(f"P0-3 共享层写入策略 · 端到端验收 [模式={args.mode}]")
122
+ print("=" * 66)
123
+
124
+ try:
125
+ from foxclaw import Foxclaw
126
+ osi = Foxclaw(data_dir=data_dir, shadow_rounds=5)
127
+
128
+ print("\n【1】装配自检")
129
+ pol = getattr(osi.hmr, "shared_write_policy", None)
130
+ spine = getattr(osi.hmr, "shared_write_spine", None)
131
+ check(pol is not None, "策略已挂到记忆后端 (hmr.shared_write_policy)")
132
+ check(spine is not None, "审计出口已挂 (hmr.shared_write_spine)")
133
+ check(pol is not None and pol.mode == args.mode,
134
+ f"生效模式 = {getattr(pol, 'mode', None)!r}(预期 {args.mode!r})")
135
+ check(spine is osi.event_spine,
136
+ "审计出口指向全系统唯一脊柱(与 /api/bus/history 同源)")
137
+ if _FAILS:
138
+ print("\n装配不完整,后续验收无意义。中止。")
139
+ return 1
140
+
141
+ print("\n【2】真实写入点零误伤(8 处调用点的原样参数)")
142
+ for agent_id, mem_type, tags, title, why in LEGITIMATE:
143
+ if agent_id == "__ANY__":
144
+ for who in _WHO_ANY:
145
+ audit_case(osi.hmr, who, mem_type, tags, f"{title}:示例内容", True)
146
+ continue
147
+ audit_case(osi.hmr, agent_id, mem_type, tags, f"{title}:示例内容", True)
148
+
149
+ print("\n【3】越界探针(跨 Agent 注入通道)")
150
+ for agent_id, mem_type, tags, content, why in INTRUSIONS:
151
+ audit_case(osi.hmr, agent_id, mem_type, tags, content, False)
152
+
153
+ print("\n【4】审计事件汇总(等价于 /api/bus/history?channel=memory.shared.write)")
154
+ evs = osi.event_spine.history("memory.shared.write", 500)
155
+ allowed_n = sum(1 for e in evs if e["payload"]["allowed"])
156
+ denied_n = len(evs) - allowed_n
157
+ print(f" 共 {len(evs)} 条:允许 {allowed_n} / 拒绝 {denied_n}")
158
+ for e in evs:
159
+ p = e["payload"]
160
+ tag = "OK " if p["allowed"] else "DENY"
161
+ print(f" {tag} {p['agent_id']:<10} {p['type']:<10} "
162
+ f"sha={p['content_sha']} {p['reason']}")
163
+ check(len(evs) > 0, "审计事件确实产生(链路口径通)")
164
+
165
+ print("\n" + "=" * 66)
166
+ if _FAILS:
167
+ print(f"✗ 验收未通过:{len(_FAILS)}/{_TOTAL} 项偏离预期")
168
+ for f in _FAILS:
169
+ print(f" - {f}")
170
+ print("\n切 enforce 前必须先修正上述偏离,否则会误伤正常链路。")
171
+ return 1
172
+ print(f"✓ 验收通过:{_TOTAL}/{_TOTAL} 项符合预期")
173
+ if args.mode != "enforce":
174
+ print("\n下一步(灰度推进):")
175
+ print(" ① 先切 warn 观察真实流量: FOXCLAW_SHARED_WRITE_MODE=warn")
176
+ print(" —— 越界写入会被打出告警,但**不拦截**,不影响任何现有功能。")
177
+ print(" ② 观察期内查审计事件,逐条判读有无「合法的被拒」:")
178
+ print(" curl -H \"X-API-Key: $FOXCLAW_API_KEY\" \\")
179
+ print(" 'http://127.0.0.1:8000/api/bus/history?channel=memory.shared.write&limit=200'")
180
+ print(" ③ 确认零误伤后再切实拦截: FOXCLAW_SHARED_WRITE_MODE=enforce")
181
+ return 0
182
+ finally:
183
+ if args.keep:
184
+ print(f"\n[数据目录保留] {data_dir}")
185
+ else:
186
+ shutil.rmtree(data_dir, ignore_errors=True)
187
+
188
+
189
+ if __name__ == "__main__":
190
+ raise SystemExit(main())
@@ -0,0 +1,28 @@
1
+ # 单文件体积棘轮基线:列此处的文件已超 15KB,允许存在但不得再增长;
2
+ # 拆到 <=15KB 后运行 `python tools/size_check.py --update` 自动移出。
3
+ # 2026-11-09 冻结时共 25 个。
4
+ foxclaw/core/model_router.py
5
+ foxclaw/agents/base.py
6
+ foxclaw/agents/impl/supervisor.py
7
+ foxclaw/skills/__init__.py
8
+ foxclaw/__init__.py
9
+ foxclaw/finance/nlparse.py
10
+ foxclaw/pipeline/cognitive_pipeline.py
11
+ foxclaw/finance/runtime.py
12
+ foxclaw/core/memory_adapter.py
13
+ foxclaw/core/vec_recall_adapter.py
14
+ foxclaw/harness/harness.py
15
+ foxclaw/core/settings.py
16
+ foxclaw/agents/mixins/tool_exec.py
17
+ foxclaw/kernel/sentinel.py
18
+ foxclaw/api/routes/llm_admin.py
19
+ foxclaw/harness/_policy.py
20
+ foxclaw/api/routes/ahs.py
21
+ foxclaw/finance/reasoning.py
22
+ foxclaw/skills/pipeline.py
23
+ foxclaw/agents/impl/office.py
24
+ foxclaw/learning/runtime.py
25
+ foxclaw/knowledge/__init__.py
26
+ foxclaw/core/recovery.py
27
+ foxclaw/core/tracing.py
28
+ foxclaw/finance/policy.py
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env python3
2
+ """单文件体积棘轮门: foxclaw/**/*.py 不得超过 15KB。
3
+
4
+ 为什么需要
5
+ ----------
6
+ REFACTOR_PLAN 要求单文件 <= 15KB(便于 LLM 整体读写、降低 diff 冲突面),
7
+ 但 2026-11 实测有 25 个文件超限,最大 foxclaw/core/model_router.py 58KB ——
8
+ 计划里没有执行机制,所以"要求"一直没落地。
9
+
10
+ 一次性拆 25 个文件是高风险的伪重构(82 个测试虽能兜底,但收益/风险不成比例)。
11
+ 本脚本采用棘轮:现有超限文件进白名单冻结,**新增超限立即失败**,已拆小的从白名单
12
+ 自动移出 —— 债务不再增长,同时每轮有机会顺手拆一个。
13
+
14
+ 用法
15
+ ----
16
+ python tools/size_check.py # CI:校验
17
+ python tools/size_check.py --update # 只允许把已合规的路径移出白名单
18
+ python tools/size_check.py --list # 打印当前超限清单
19
+ """
20
+ from __future__ import annotations
21
+
22
+ import sys
23
+ from pathlib import Path
24
+
25
+ ROOT = Path(__file__).resolve().parent.parent
26
+ TARGET = ROOT / "foxclaw"
27
+ BASELINE = ROOT / "tools" / "size_baseline.txt"
28
+ LIMIT = 15 * 1024
29
+
30
+ for _s in (sys.stdout, sys.stderr):
31
+ try:
32
+ _s.reconfigure(encoding="utf-8", errors="replace") # type: ignore[union-attr]
33
+ except Exception:
34
+ pass
35
+
36
+
37
+ def rel(p: Path) -> str:
38
+ return p.relative_to(ROOT).as_posix()
39
+
40
+
41
+ def scan() -> dict[str, int]:
42
+ return {rel(p): p.stat().st_size for p in TARGET.rglob("*.py") if p.stat().st_size > LIMIT}
43
+
44
+
45
+ def read_baseline() -> set[str]:
46
+ if not BASELINE.exists():
47
+ return set()
48
+ return {
49
+ line.strip() for line in BASELINE.read_text(encoding="utf-8").splitlines()
50
+ if line.strip() and not line.startswith("#")
51
+ }
52
+
53
+
54
+ def main() -> int:
55
+ argv = sys.argv[1:]
56
+ over = scan()
57
+
58
+ if "--list" in argv:
59
+ for path, size in sorted(over.items(), key=lambda kv: -kv[1]):
60
+ print(f"{size / 1024:7.1f}KB {path}")
61
+ print(f"共 {len(over)} 个文件超过 {LIMIT // 1024}KB")
62
+ return 0
63
+
64
+ allowed = read_baseline()
65
+ if "--update" in argv:
66
+ new = sorted(set(over) - allowed)
67
+ if new:
68
+ print("✗ 拒绝更新白名单:以下文件是新超限的,必须拆分而不是加进白名单:")
69
+ for path in new:
70
+ print(f" {over[path] / 1024:7.1f}KB {path}")
71
+ return 1
72
+ kept = sorted(set(over) & allowed)
73
+ BASELINE.write_text(
74
+ "# 单文件体积棘轮基线:列此处的文件已超 15KB,允许存在但不得再增长;\n"
75
+ "# 拆到 <=15KB 后运行 `python tools/size_check.py --update` 自动移出。\n"
76
+ + "".join(f"{p}\n" for p in kept),
77
+ encoding="utf-8",
78
+ )
79
+ print(f"✓ 白名单已更新为 {len(kept)} 项")
80
+ return 0
81
+
82
+ new = sorted(set(over) - allowed)
83
+ shrunk = sorted(allowed - set(over))
84
+
85
+ if shrunk:
86
+ print(f"ℹ 以下 {len(shrunk)} 个文件已降到 {LIMIT // 1024}KB 以内,可从白名单移出"
87
+ f"(运行 --update):")
88
+ for path in shrunk:
89
+ print(f" {path}")
90
+
91
+ if new:
92
+ print(f"✗ {len(new)} 个新文件超过 {LIMIT // 1024}KB,必须拆分:")
93
+ for path in new:
94
+ print(f" {over[path] / 1024:7.1f}KB {path}")
95
+ return 1
96
+
97
+ print(f"✓ 通过:超限文件 {len(over)} 个,均在冻结白名单内(未新增)")
98
+ return 0
99
+
100
+
101
+ if __name__ == "__main__":
102
+ sys.exit(main())
@@ -0,0 +1,228 @@
1
+ """剥离代码里的解释性注释。
2
+
3
+ 用法:
4
+ python tools/strip_comments.py --dry 只统计,不改文件
5
+ python tools/strip_comments.py 真正改写
6
+ python tools/strip_comments.py a.py b.js 只处理指定文件
7
+
8
+ 不能一刀切:下面这些注释被删会改变 lint / 类型检查 / 解释器行为,必须原样保留。
9
+ #!... shebang
10
+ # -*- coding -*- 编码声明
11
+ # noqa ... ruff 抑制
12
+ # type: ... mypy 注解(含 `x = [] # type: list[int]`)
13
+ # pragma / fmt: / isort: / pyright: / pylint: / nosec / coverage:
14
+ docstring 不动 —— 它是运行时对象,不是注释。
15
+
16
+ vendor/ 是内嵌第三方,不改;frontend/v3 是构建产物,由 vite 重新生成。
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import io
22
+ import re
23
+ import sys
24
+ import tokenize
25
+ from pathlib import Path
26
+
27
+ ROOT = Path(__file__).resolve().parent.parent
28
+
29
+ SKIP_PARTS = {".git", "node_modules", "__pycache__", ".venv", "venv", "vendor",
30
+ "foxclaw_data", "test_data", ".pytest_cache", ".ruff_cache", ".mypy_cache",
31
+ "build", "dist", "htmlcov"}
32
+ # 生成副本:npm/python 由 npm/build.cjs 从仓库根复制,frontend/v3 由 vite 构建
33
+ SKIP_PREFIX = {"frontend/v3/", "npm/python/"}
34
+ EXTS = {".py", ".js", ".vue", ".ps1"}
35
+
36
+ DIRECTIVE = re.compile(
37
+ r"^#\s*(?:!|-\*-|noqa|type:|pragma|fmt:|isort:|pyright:|pylint:|ruff:|nosec|"
38
+ r"coverage:|mypy:|noinspection|coding[:=])",
39
+ re.I,
40
+ )
41
+
42
+ MAX_BLANK = 2
43
+
44
+
45
+ def targets() -> list[Path]:
46
+ out = []
47
+ for p in ROOT.rglob("*"):
48
+ if p.suffix not in EXTS or not p.is_file():
49
+ continue
50
+ rel = p.relative_to(ROOT).as_posix()
51
+ if SKIP_PARTS & set(p.relative_to(ROOT).parts):
52
+ continue
53
+ if any(rel.startswith(x) for x in SKIP_PREFIX):
54
+ continue
55
+ if rel == "tools/strip_comments.py":
56
+ continue
57
+ out.append(p)
58
+ return sorted(out)
59
+
60
+
61
+ def collapse_blank(lines: list[str], limit: int = MAX_BLANK) -> list[str]:
62
+ out: list[str] = []
63
+ run = 0
64
+ for ln in lines:
65
+ if ln.strip() == "":
66
+ run += 1
67
+ if run > limit:
68
+ continue
69
+ else:
70
+ run = 0
71
+ out.append(ln)
72
+ return out
73
+
74
+
75
+ def strip_py(text: str) -> tuple[str, int]:
76
+ try:
77
+ toks = list(tokenize.generate_tokens(io.StringIO(text).readline))
78
+ except (tokenize.TokenError, IndentationError, SyntaxError):
79
+ return text, 0
80
+
81
+ lines = text.splitlines(keepends=True)
82
+ drops: dict[int, int | None] = {}
83
+ for t in toks:
84
+ if t.type != tokenize.COMMENT:
85
+ continue
86
+ row, col = t.start
87
+ if DIRECTIVE.match(t.string):
88
+ continue
89
+ line = lines[row - 1] if row - 1 < len(lines) else ""
90
+ head = line[:col]
91
+ drops[row] = None if head.strip() == "" else col
92
+
93
+ if not drops:
94
+ return text, 0
95
+
96
+ out: list[str] = []
97
+ for idx, line in enumerate(lines, 1):
98
+ if idx not in drops:
99
+ out.append(line)
100
+ continue
101
+ col = drops[idx]
102
+ if col is None:
103
+ continue
104
+ body = line[:col].rstrip()
105
+ eol = line[len(line.rstrip("\r\n")):]
106
+ out.append(body + eol)
107
+ out = collapse_blank(out)
108
+ return "".join(out), len(drops)
109
+
110
+
111
+ def _tpl_open(s: str) -> bool:
112
+ n = 0
113
+ i = 0
114
+ while i < len(s):
115
+ if s[i] == "\\":
116
+ i += 2
117
+ continue
118
+ if s[i] == "`":
119
+ n += 1
120
+ i += 1
121
+ return n % 2 == 1
122
+
123
+
124
+ def strip_js(text: str) -> tuple[str, int]:
125
+ """只删独占整行的注释,行尾注释一律不动。
126
+
127
+ 按位置切不行:正则字面量里的 \/\/ 和字符串里的 // 都会被当成注释起点 ——
128
+ markdown.js 的 /^https?:\/\//i 就被切掉半个函数。整行判定是安全的:
129
+ 行首出现 // 或 /* 时,它只能是注释。
130
+ """
131
+ lines = text.splitlines(keepends=True)
132
+ out: list[str] = []
133
+ removed = 0
134
+ in_block = ""
135
+ in_tpl = False
136
+ for line in lines:
137
+ body = line.rstrip("\r\n")
138
+ s = body.strip()
139
+ if in_block:
140
+ removed += 1
141
+ if (in_block == "<!--" and "-->" in s) or (in_block == "/*" and "*/" in s):
142
+ in_block = ""
143
+ continue
144
+ if in_tpl:
145
+ if _tpl_open(s):
146
+ in_tpl = False
147
+ out.append(line)
148
+ continue
149
+ if s.startswith("<!--"):
150
+ removed += 1
151
+ if "-->" not in s[4:]:
152
+ in_block = "<!--"
153
+ continue
154
+ if s.startswith("/*"):
155
+ removed += 1
156
+ if "*/" not in s[2:]:
157
+ in_block = "/*"
158
+ continue
159
+ if s.startswith("//"):
160
+ removed += 1
161
+ continue
162
+ if _tpl_open(body):
163
+ in_tpl = True
164
+ out.append(line)
165
+ return "".join(collapse_blank(out, 1)), removed
166
+
167
+
168
+ def strip_ps(text: str) -> tuple[str, int]:
169
+ """PowerShell 的注释符是 # 和 <# #>。只删独占行的,行尾的不碰 —— 拆字符串不值当。"""
170
+ out: list[str] = []
171
+ removed = 0
172
+ in_block = False
173
+ for line in text.splitlines(keepends=True):
174
+ stripped = line.strip()
175
+ if in_block:
176
+ removed += 1
177
+ if "#>" in stripped:
178
+ in_block = False
179
+ continue
180
+ if stripped.startswith("<#"):
181
+ removed += 1
182
+ if "#>" not in stripped[2:]:
183
+ in_block = True
184
+ continue
185
+ if stripped.startswith("#") and not re.match(r"#\s*requires\b", stripped, re.I):
186
+ removed += 1
187
+ continue
188
+ out.append(line)
189
+ return "".join(collapse_blank(out, 1)), removed
190
+
191
+
192
+ def main(argv: list[str]) -> int:
193
+ dry = "--dry" in argv
194
+ picked = [Path(a) for a in argv if not a.startswith("-")]
195
+ files = picked or targets()
196
+ total_files = 0
197
+ total_lines = 0
198
+ for p in files:
199
+ if not p.is_absolute():
200
+ p = ROOT / p
201
+ raw = p.read_bytes()
202
+ text = raw.decode("utf-8")
203
+ if p.suffix == ".py":
204
+ new, n = strip_py(text)
205
+ elif p.suffix == ".ps1":
206
+ new, n = strip_ps(text)
207
+ else:
208
+ new, n = strip_js(text)
209
+ if n == 0 or new == text:
210
+ continue
211
+ if p.suffix == ".py":
212
+ try:
213
+ compile(new, str(p), "exec")
214
+ except SyntaxError as e:
215
+ print(f"SKIP {p.relative_to(ROOT)}: 删后语法错误 {e}")
216
+ continue
217
+ total_files += 1
218
+ total_lines += n
219
+ if dry:
220
+ print(f"{n:5d} {p.relative_to(ROOT).as_posix()}")
221
+ else:
222
+ p.write_bytes(new.encode("utf-8"))
223
+ print(f"\n{'[dry] ' if dry else ''}files={total_files} comment_lines={total_lines}")
224
+ return 0
225
+
226
+
227
+ if __name__ == "__main__":
228
+ raise SystemExit(main(sys.argv[1:]))
@@ -0,0 +1,2 @@
1
+ @echo off
2
+ python "%~dp0verify_env.py"
@@ -0,0 +1,25 @@
1
+ import os
2
+ from pathlib import Path
3
+
4
+ from dotenv import load_dotenv
5
+
6
+ p = str(Path(__file__).resolve().parents[1] / ".env")
7
+ try:
8
+ load_dotenv(p)
9
+ print("DOTENV_OK")
10
+ except Exception as e:
11
+ print(f"DOTENV_FAIL: {type(e).__name__}: {e}")
12
+ raise SystemExit(1)
13
+
14
+ for k in ("HKC_EMBEDDING_MODEL", "HF_HUB_OFFLINE", "FOXCLAW_API_KEY",
15
+ "FOXCLAW_PROVIDER", "FOXCLAW_DATA_DIR", "FOXCLAW_MCP_SERVER",
16
+ "FOXCLAW_MODEL", "HKC_LLM_PROVIDER", "HKC_LLM_MODEL"):
17
+ v = os.environ.get(k, "<UNSET>")
18
+ if k.endswith("API_KEY") and v not in ("<UNSET>", ""):
19
+ v = v[:8] + "..." + v[-4:] if len(v) > 12 else "<short>"
20
+ print(f" {k} = {v}")
21
+
22
+ n = sum(1 for k in os.environ if k.startswith((
23
+ "HKC_", "FOXCLAW_", "HF_", "ANTHROPIC_", "OPENAI_", "GOOGLE_",
24
+ "DASHSCOPE_", "LOCAL_", "AMAP_", "BAIDU_", "MINIMAX_", "DEEPSEEK_")))
25
+ print(f"TOTAL_PROJECT_KEYS={n}")
@@ -0,0 +1,37 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Hestia OS Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ ---
24
+
25
+ ## About This License
26
+
27
+ HMR v1 (Hestia Memory Runtime) Core is released under the MIT License.
28
+
29
+ This means:
30
+ ✅ You can use it freely in commercial projects
31
+ ✅ You can modify it
32
+ ✅ You can distribute it
33
+ ✅ You must include the license and copyright
34
+
35
+ The only requirement: include this license text in your distribution.
36
+
37
+ For enterprise features and extended support, see Hestia Enterprise License.