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,128 @@
1
+ """vector 索引升级 + 性能对比:Flat vs IVF(1500 mock KU, stub backend)
2
+
3
+ 目的:验证 HKC_VECTOR_IVF_THRESHOLD 触发的 Flat → IVF 自动升级,
4
+ 并对比两种索引在 500 / 1500 mock KU 下的检索延迟。
5
+
6
+ 用法:
7
+ python bench/vector_ivf_upgrade.py
8
+
9
+ 可调参数:
10
+ HKC_VECTOR_IVF_THRESHOLD 默认 500(本脚本覆盖)
11
+ IVF_NLIST 自定义 nlist(可选)
12
+ TOTAL_KUS 总 mock KU 数,默认 1500
13
+ """
14
+ from __future__ import annotations
15
+
16
+ import os
17
+ import statistics
18
+ import sys
19
+ import tempfile
20
+ import time
21
+ from pathlib import Path
22
+ from types import SimpleNamespace
23
+
24
+ os.environ["HKC_VECTOR_IVF_THRESHOLD"] = "500"
25
+
26
+ ROOT = Path(__file__).resolve().parent.parent
27
+ sys.path.insert(0, str(ROOT))
28
+
29
+ from vendor.hkc.hkc_search.vector_search import VectorIndex
30
+ from vendor.hkc.hkc_search.embedding_backends import StubBackend
31
+
32
+
33
+ def make_ku(i: int) -> SimpleNamespace:
34
+ """构造 mock KU,字段匹配 vector_search._ku_text() 期望的 4 项: name/summary/tags/statement。"""
35
+ return SimpleNamespace(
36
+ ku_id = f"ku_{i:06d}",
37
+ name = f"entity_{i}",
38
+ summary = f"summary of entity {i} about topic {i % 50}",
39
+ tags = [f"tag_{i % 10}", f"category_{i % 5}"],
40
+ statement = f"fact {i}: something interesting about subject {i}",
41
+ )
42
+
43
+
44
+ def bench_search(idx, query: str, n: int = 100, top_k: int = 10) -> tuple[float, float, float]:
45
+ """跑 n 次 search,返 (median_ms, min_ms, max_ms)。"""
46
+ times = []
47
+ for _ in range(n):
48
+ t0 = time.perf_counter()
49
+ idx.search(query, top_k=top_k)
50
+ times.append((time.perf_counter() - t0) * 1000)
51
+ return statistics.median(times), min(times), max(times)
52
+
53
+
54
+ def main() -> None:
55
+ backend = StubBackend(dim=384)
56
+ threshold = int(os.environ["HKC_VECTOR_IVF_THRESHOLD"])
57
+
58
+ print("=" * 70)
59
+ print(f"backend : {backend.name}")
60
+ print(f"threshold : HKC_VECTOR_IVF_THRESHOLD = {threshold}")
61
+ print(f"upgrade to : IndexIVFFlat(nlist≈√N, nprobe=16)")
62
+ print("=" * 70)
63
+
64
+ N = 5000
65
+ print(f"\n构造 {N} 条 mock KU...")
66
+ kus = [make_ku(i) for i in range(N)]
67
+
68
+ idx = VectorIndex(backend=backend)
69
+ print(f"index type 初始: {type(idx._index).__name__}")
70
+
71
+ phase1_n = threshold - 1
72
+ print(f"\n[Phase 1] append {phase1_n} 条(< {threshold},应保持 Flat)...")
73
+ for ku in kus[:phase1_n]:
74
+ idx.append_ku(ku)
75
+ print(f" index type: {type(idx._index).__name__}, ntotal={idx._index.ntotal}")
76
+ med, mn, mx = bench_search(idx, "find something about topic 7")
77
+ print(f" search ×100: median={med:.2f}ms min={mn:.2f}ms max={mx:.2f}ms")
78
+
79
+ print(f"\n[Phase 2] append 第 {threshold} 条(应触发升级)...")
80
+ t0 = time.perf_counter()
81
+ idx.append_ku(kus[phase1_n])
82
+ upgrade_ms = (time.perf_counter() - t0) * 1000
83
+ print(f" append(含升级)耗时: {upgrade_ms:.1f}ms")
84
+ print(f" index type: {type(idx._index).__name__}, ntotal={idx._index.ntotal}")
85
+ if hasattr(idx._index, "nlist"):
86
+ print(f" IVF nlist={idx._index.nlist}, nprobe={idx._index.nprobe}")
87
+ med, mn, mx = bench_search(idx, "find something about topic 7")
88
+ print(f" search ×100: median={med:.2f}ms min={mn:.2f}ms max={mx:.2f}ms")
89
+
90
+ rest = N - threshold
91
+ print(f"\n[Phase 3] append {rest} 条(应直接 add 到 IVF)...")
92
+ t0 = time.perf_counter()
93
+ for ku in kus[threshold:]:
94
+ idx.append_ku(ku)
95
+ bulk_ms = (time.perf_counter() - t0) * 1000
96
+ print(f" {rest} 条 append 耗时: {bulk_ms:.1f}ms(平均 {bulk_ms/rest:.2f}ms/条)")
97
+ print(f" index type: {type(idx._index).__name__}, ntotal={idx._index.ntotal}")
98
+ med, mn, mx = bench_search(idx, "find something about topic 7")
99
+ print(f" search ×100: median={med:.2f}ms min={mn:.2f}ms max={mx:.2f}ms")
100
+
101
+ print(f"\n[Phase 4] save/load roundtrip(IVF 持久化)...")
102
+ with tempfile.TemporaryDirectory() as td:
103
+ p = str(Path(td) / "vec")
104
+ t0 = time.perf_counter()
105
+ idx.save(p)
106
+ save_ms = (time.perf_counter() - t0) * 1000
107
+ idx2 = VectorIndex(backend=backend)
108
+ t0 = time.perf_counter()
109
+ ok = idx2.load(p)
110
+ load_ms = (time.perf_counter() - t0) * 1000
111
+ n_loaded = idx2._index.ntotal if idx2._index else 0
112
+ type_loaded = type(idx2._index).__name__ if idx2._index else "None"
113
+ print(f" save: {save_ms:.1f}ms load: {load_ms:.1f}ms ok={ok}")
114
+ print(f" loaded ntotal={n_loaded}, type={type_loaded}")
115
+ if n_loaded != N:
116
+ print(f" [FAIL] 数据丢失! 期望 {N} 实际 {n_loaded}")
117
+ elif "FlatIP" in type_loaded:
118
+ print(f" [FAIL] IVF 没保存,降回 FlatIP")
119
+ else:
120
+ print(f" [OK] IVF 完整保存/加载(ntotal={n_loaded}, type={type_loaded})")
121
+
122
+ print("\n" + "=" * 70)
123
+ print("[OK] benchmark 完成")
124
+ print("=" * 70)
125
+
126
+
127
+ if __name__ == "__main__":
128
+ main()
@@ -0,0 +1,195 @@
1
+ # Foxclaw 消息平台接入说明
2
+
3
+ 把 Foxclaw 接到消息平台,让用户在微信 / 飞书 / Telegram / Discord 里直接和你的认知系统对话。
4
+
5
+ ## 架构总览
6
+
7
+ ```
8
+ 用户在某渠道发消息
9
+ → 渠道适配器 normalize 成标准消息(InboundMessage)
10
+ → ChannelDispatcher 交给主管 Agent 处理
11
+ → 主管 run_task 思考、调度子 Agent
12
+ → 回复通过同一渠道发回
13
+ ```
14
+
15
+ 三个角色各司其职:**适配器**只管收发(翻译平台格式 ↔ 标准格式),**主管**只管思考,**调度器(ChannelDispatcher)**把两者接起来。
16
+
17
+ 所有适配器都遵循同一套接口(`normalize` / `send` / `reply`),但**内部各按平台官方规范实现**——微信是长轮询、飞书是事件订阅 webhook、Telegram 是 getUpdates、Discord 是 Gateway WebSocket,互不相同。
18
+
19
+ ## 通用启动方式
20
+
21
+ ```python
22
+ from foxclaw import Foxclaw
23
+ from foxclaw.channels.dispatcher import ChannelDispatcher
24
+
25
+ os_ = Foxclaw(data_dir="./data")
26
+ dispatcher = ChannelDispatcher(os_.supervisor)
27
+
28
+ # 注册渠道(下面每个平台示例都用这个 dispatcher)
29
+ dispatcher.register_channel(adapter) # adapter 见下文各平台
30
+ ```
31
+
32
+ `register_channel` 会自动把适配器的入站消息接到主管闭环——之后该渠道收到的每条消息都会经主管处理并自动回复。
33
+
34
+ ---
35
+
36
+ ## Telegram(最易跑通,建议先用它验证)
37
+
38
+ **拿 token:** Telegram 里找 `@BotFather` → 发 `/newbot` → 按提示拿到 bot token。
39
+
40
+ ```python
41
+ from foxclaw.channels.telegram import TelegramAdapter
42
+
43
+ tg = TelegramAdapter(bot_token="123456:ABC-DEF...")
44
+ dispatcher.register_channel(tg)
45
+
46
+ # 验证 token
47
+ print(tg.verify_token().ok)
48
+
49
+ # 持续收消息(轮询):放进循环
50
+ import time
51
+ while True:
52
+ tg.poll_once() # 拉一批新消息,自动经主管处理并回复
53
+ time.sleep(1)
54
+ ```
55
+
56
+ 收发模型:**getUpdates 长轮询**,无需公网回调地址,本地直接能跑。
57
+
58
+ ---
59
+
60
+ ## 微信(iLink Bot API)
61
+
62
+ 照 Tencent 官方开源插件 [`Tencent/openclaw-weixin`](https://github.com/Tencent/openclaw-weixin) 公开的 iLink Bot API 协议实现。
63
+
64
+ **拿 token:** token 需用微信官方那套**扫码登录**获得(扫码登录在微信侧,Foxclaw 不复现登录,只复现登录后的消息收发)。可用 OpenClaw 官方 CLI 扫码:
65
+ ```bash
66
+ openclaw channels login --channel openclaw-weixin # 扫码后本地拿到 token
67
+ ```
68
+
69
+ ```python
70
+ from foxclaw.channels.weixin import WeixinAdapter
71
+
72
+ wx = WeixinAdapter(token="<扫码登录得到的 token>")
73
+ dispatcher.register_channel(wx)
74
+
75
+ # 持续收消息(长轮询)
76
+ while True:
77
+ wx.poll_once() # getUpdates 长轮询,自动处理并回复
78
+ ```
79
+
80
+ 收发模型:**getUpdates 长轮询**。回复时会自动带回 `context_token`(微信要求),已内部处理。`verify_token(ilink_user_id)` 需提供真实用户 ID 才能探测。
81
+
82
+ ---
83
+
84
+ ## 飞书 / Lark(事件订阅 webhook)
85
+
86
+ **拿凭证:** 飞书开放平台 → 创建企业自建应用 → 拿 `App ID` + `App Secret`;在「事件订阅」里配置回调地址,记下 `Verification Token` 和 `Encrypt Key`(若开启加密)。事件订阅勾选 `im.message.receive_v1`。
87
+
88
+ 飞书是**平台主动推事件到你的回调地址**,所以需要一个公网可达的 webhook 端点(FastAPI 路由)。
89
+
90
+ ```python
91
+ from foxclaw.channels.feishu import FeishuAdapter
92
+
93
+ fs = FeishuAdapter(
94
+ app_id="cli_xxx",
95
+ app_secret="xxx",
96
+ verification_token="xxx", # webhook 模式需要
97
+ encrypt_key="xxx", # 若开启加密
98
+ )
99
+ dispatcher.register_channel(fs)
100
+
101
+ # FastAPI 路由接飞书事件回调
102
+ from fastapi import FastAPI, Request
103
+ app = FastAPI()
104
+
105
+ @app.post("/feishu/events")
106
+ async def feishu_events(request: Request):
107
+ raw_body = await request.body()
108
+ payload = await request.json()
109
+ # handle_event 内部完成:URL验证 / 解密 / 验签 / 标准化 / 主管处理
110
+ return fs.handle_event(payload, headers=dict(request.headers), raw_body=raw_body)
111
+ ```
112
+
113
+ 收发模型:**事件订阅 webhook**。`handle_event` 自动处理 URL 验证(challenge)、AES 解密、签名验签。发消息用 `tenant_access_token`(自动缓存 + 过期前刷新)。
114
+
115
+ > 注意:飞书加密事件解密需要 `cryptography` 库(或 pycryptodome)。环境一般已自带 cryptography。
116
+
117
+ ---
118
+
119
+ ## Discord(Gateway WebSocket)
120
+
121
+ **拿 token:** Discord Developer Portal → 创建 Application → Bot → 拿 bot token。**务必在 Bot 设置里开启 `MESSAGE CONTENT INTENT`**(否则收不到消息文本)。把 Bot 邀请进你的服务器。
122
+
123
+ ```python
124
+ from foxclaw.channels.discord import DiscordAdapter
125
+
126
+ dc = DiscordAdapter(bot_token="...")
127
+ dispatcher.register_channel(dc)
128
+
129
+ # 验证 token(同时记录 bot 自身 ID,用于忽略自己的消息)
130
+ print(dc.verify_token().ok)
131
+
132
+ # 持续收消息(Gateway WebSocket,异步)
133
+ import asyncio
134
+ asyncio.run(dc.connect_gateway()) # 需要 websockets 库
135
+ ```
136
+
137
+ 收发模型:**Gateway WebSocket 长连接**(收) + **REST API**(发)。`connect_gateway` 实现了 HELLO→IDENTIFY→心跳→消息分发的最小闭环。生产环境也可换成成熟的 discord.py,再把消息喂给 `dc.normalize()`。
138
+
139
+ > 需要 `websockets` 库:`pip install websockets`
140
+
141
+ ---
142
+
143
+ ## 备选:对接 OpenClaw 网关
144
+
145
+ 若你已在用 OpenClaw(它接了某些渠道),可让 Foxclaw 通过 OpenClaw 网关收发,而不必自己实现那些渠道。
146
+
147
+ ```python
148
+ from foxclaw.channels.openclaw_bridge import OpenClawBridgeAdapter
149
+
150
+ bridge = OpenClawBridgeAdapter(gateway_url="http://127.0.0.1:8080", api_token="...")
151
+ dispatcher.register_channel(bridge)
152
+ ```
153
+
154
+ OpenClaw 负责渠道接入,Foxclaw 负责认知处理,两者通过 HTTP 桥接。
155
+
156
+ ---
157
+
158
+ ## 多渠道同时运行
159
+
160
+ 一个 dispatcher 可注册多个渠道,各自独立收发,共用同一个主管:
161
+
162
+ ```python
163
+ dispatcher.register_channel(tg) # Telegram(轮询)
164
+ dispatcher.register_channel(wx) # 微信(轮询)
165
+ dispatcher.register_channel(dc) # Discord(WebSocket)
166
+ # 飞书走 FastAPI webhook 路由
167
+
168
+ # 轮询类渠道各起一个循环/线程;WebSocket/webhook 类按各自方式跑
169
+ ```
170
+
171
+ **会话隔离:** 默认按 `渠道:会话:发送者` 隔离(不同渠道的对话互不串)。若想做"跨渠道同一记忆"(同一用户在飞书和微信共享上下文),可改 `dispatcher._session_key` 只用 `sender_id`。
172
+
173
+ ---
174
+
175
+ ## 依赖小结
176
+
177
+ | 渠道 | 必需依赖 | 凭证 |
178
+ |---|---|---|
179
+ | Telegram | httpx | bot token(@BotFather) |
180
+ | 微信 | httpx | 扫码登录 token |
181
+ | 飞书 | httpx, cryptography | App ID/Secret + (加密时)Encrypt Key |
182
+ | Discord | httpx, websockets | bot token + Message Content Intent |
183
+ | OpenClaw 桥 | httpx | OpenClaw 网关地址 + token |
184
+
185
+ ```bash
186
+ pip install httpx cryptography websockets
187
+ ```
188
+
189
+ ---
190
+
191
+ ## 安全提示
192
+
193
+ - **凭证不要硬编码**:token / app_secret 用环境变量或配置文件注入,别写进代码或提交到 Git。
194
+ - 飞书 webhook 建议保留**签名验签**(开启 Encrypt Key),适配器会自动验。
195
+ - 各渠道的访问控制(谁能跟 bot 对话)目前在主管侧统一处理;如需更细的白名单,可在 `dispatcher.handle` 里按 `msg.sender_id` 加判断。
@@ -0,0 +1,164 @@
1
+ # Foxclaw 运行时治理能力说明
2
+
3
+ 这组能力借鉴 OpenClaw 等成熟 Agent 系统的设计原理,用 Python 适配 Foxclaw 实现,目标是让系统在真实运行(消息重发、进程崩溃、长任务、死循环)中保持**可靠、可审计、不丢不重**。
4
+
5
+ 六项能力分两类:**已接入主路径、现在就生效**的(幂等、重启恢复、循环检测),和**基础设施、按需启用**的(上下文压缩、钩子标准化、干预队列)。
6
+
7
+ ---
8
+
9
+ ## 一、幂等守卫(Idempotency)— 已生效
10
+
11
+ **解决:** 消息平台天生会重发(网络抖动、平台重试、轮询游标边界)。没有幂等,用户会收到重复回复、同一操作被记两次。
12
+
13
+ **原理:** 给"只应发生一次"的操作算一个稳定 key,执行前**原子查重**——处理过就跳过,没处理才执行并记录。
14
+
15
+ **核心接口**(`foxclaw/core/idempotency.py`):
16
+ ```python
17
+ from foxclaw.core.idempotency import IdempotencyGuard, make_key
18
+
19
+ guard = IdempotencyGuard(ttl_seconds=3600) # 可选 persist_path 跨重启
20
+ key = make_key("message", channel, message_id, sender_id)
21
+ if guard.check_and_mark(key): # 原子:首次返回 True(执行),重复返回 False(跳过)
22
+ do_the_work()
23
+ ```
24
+
25
+ **已接入:** `ChannelDispatcher.handle` 自动按 `channel+message_id+sender` 去重;无 message_id 的消息退化为"渠道+发送者+内容+10秒时间窗"兜底。
26
+
27
+ **设计要点:**
28
+ - `check_and_mark` 在同一把锁里完成查重+标记(防并发重发都去执行,50线程压测验证恰好1个成功)
29
+ - TTL 过期清理 + 容量上限 LRU 淘汰(防无限膨胀)
30
+ - 可选落盘(原子替换写),跨进程重启仍识别"处理过了"
31
+
32
+ ---
33
+
34
+ ## 二、重启恢复(Restart Recovery)— 已生效
35
+
36
+ **解决:** 长任务(多 Agent 链、财务分析)跑到一半进程崩了/重启,任务凭空消失,记忆可能留下半完成的脏数据。
37
+
38
+ **原理:** 启动时主动识别"上次还在进行、但持有它的进程已不在"的任务,**诚实标记为中断**,而不是装无事。契合 Foxclaw"可审计、可信"的定位。
39
+
40
+ **核心接口**(`foxclaw/core/recovery.py`):
41
+ ```python
42
+ from foxclaw.core.recovery import RestartRecovery
43
+
44
+ rec = RestartRecovery(log_path="data/task_log.jsonl")
45
+ rec.task_started(task_id, description) # 任务开始(落 in_progress)
46
+ rec.task_finished(task_id, status="done") # 结束(done/failed)
47
+ interrupted = rec.scan_interrupted() # 启动时扫描:返回被中断的任务列表
48
+ ```
49
+
50
+ **已接入:** `Foxclaw` 启动时自动扫描,检测到中断任务会打印提示;主管任务经 base 模板的
51
+ 生命周期钩子(`_on_session_started`/`_on_task_settled`,P1-1)自动记录开始/结束
52
+ (正常终态标 done、未兜底异常标 failed;`run_task` 与 `run_task_stream` 两条路径都记账)。
53
+
54
+ **设计要点:**
55
+ - **boot_id 防 pid 复用**:每次启动生成唯一 ID,判据是"非本次启动的 in_progress 任务=被中断"。比单纯靠 pid 可靠——pid 会被 OS 复用,旧任务的 pid 被新进程占用会导致漏判。
56
+ - JSONL 追加写 + fsync(崩溃安全:单行 append 原子,损坏行自动跳过)
57
+ - `compact_log` 防日志无限膨胀
58
+
59
+ ---
60
+
61
+ ## 三、工具循环检测(Loop Detection)— 已生效
62
+
63
+ **解决:** 断路器管"失败",但管不了"无进展死循环"——Agent 不报错却陷入重复/横跳/轮询卡死,白烧 token。
64
+
65
+ **原理:** 监控工具调用历史,检测三种循环模式,多级阈值预警→熔断。
66
+
67
+ **核心接口**(`foxclaw/harness/loop_detection.py`):
68
+ ```python
69
+ from foxclaw.harness.loop_detection import ToolLoopDetector
70
+
71
+ det = ToolLoopDetector(warning=6, critical=12, circuit_break=20)
72
+ verdict = det.record(tool_name, args, result) # 每次工具调用后
73
+ if verdict.stuck:
74
+ print(verdict.detector, verdict.message) # repeat/ping_pong/no_progress
75
+ if det.should_abort():
76
+ abort() # 达熔断阈值
77
+ ```
78
+
79
+ **已接入:** `SkillAdapter` 每次工具调用后自动检测,卡死时在结果上附 `loop_warning` 并触发 `on_loop` 钩子。
80
+
81
+ **三种模式:** `repeat`(同调用连续重复)、`ping_pong`(两调用交替)、`no_progress`(轮询结果无变化,优先级最高,语义最精确)。
82
+
83
+ ---
84
+
85
+ ## 四、上下文压缩(Compaction)— 基础设施
86
+
87
+ **解决:** 长对话撑爆上下文窗口。
88
+
89
+ **原理:** 渐进降级压缩,绝不崩——LLM 完整摘要 → 排除超大消息的部分摘要 → 截断兜底。始终保留系统提示 + 最近 N 轮 + 关键标识符(ID/金额)。
90
+
91
+ **核心接口**(`foxclaw/core/compaction.py`):
92
+ ```python
93
+ from foxclaw.core.compaction import Compactor
94
+
95
+ c = Compactor(router=model_router, max_tokens=6000, keep_recent=4)
96
+ if c.needs_compaction(messages):
97
+ result = c.compact(messages) # result.messages 是压缩后的列表
98
+ ```
99
+
100
+ **设计要点:** 压缩后校验是否真变小(省不到 10% 就放弃,method=`skipped_ineffective`),避免系统消息过大时压缩反而变大。`router=None` 时直接走截断兜底。
101
+
102
+ ---
103
+
104
+ ## 五、生命周期钩子标准化(Hooks)— 基础设施
105
+
106
+ **解决:** 钩子点命名零散、阻断权不清晰。
107
+
108
+ **原理:** 标准生命周期钩子 + 明确的阻断权 + 向后兼容。
109
+
110
+ **标准钩子点**(`foxclaw/skills/hooks_spec.py`):
111
+ - 会话级:`session_start` → `agent_start` → `agent_end` → `session_end`
112
+ - `agent_start`(别名 **`pre_reasoning`**)/ `agent_end`(别名 **`task_complete`**)由**认知管线 `run_task`** 触发(E2);`agent_start` 可返回 `additional_context` 注入本次推理(非阻断)。`session_start/end` 由 AHS skill 编排链触发。
113
+ - 工具级:`before_tool_call`(唯一可阻断)→ `after_tool_call` / `on_error` / `on_loop`
114
+ - 消息级:`before_message_write`
115
+
116
+ ```python
117
+ from foxclaw.skills.lifecycle import HookManager
118
+ hm = HookManager()
119
+ hm.register("before_tool_call", my_hook) # 旧名 pre_skill 也兼容,自动规整
120
+ ```
121
+
122
+ **设计要点:** 只有 `before_tool_call` 有阻断权(返回 `{block:True}` 生效);旧名(pre_skill/post_skill)自动规整为标准名且互通;非法钩子名会记警告(防拼错静默失效)。
123
+
124
+ ---
125
+
126
+ ## 六、干预队列(Steering Queue)— 基础设施(为并行预留)
127
+
128
+ **解决:** 并行子 Agent 的结果如何安全回收——不丢、不重复。
129
+
130
+ **原理:** 租约机制。当前多 Agent 顺序执行用不上(直接收集即可);这是为**未来并行子 Agent** 预留的基础设施。
131
+
132
+ **核心接口**(`foxclaw/core/steering_queue.py`):
133
+ ```python
134
+ from foxclaw.core.steering_queue import SteeringQueue
135
+
136
+ q = SteeringQueue(lease_seconds=30)
137
+ q.publish(requester, source_agent, payload) # 子任务完成入队
138
+ items = q.lease(requester) # 消费者租走(不会被重复租)
139
+ q.ack(item.item_id, item.lease_id) # 确认处理完移除
140
+ # 过期未 ack 的项自动回到 pending(主任务崩溃兜底)
141
+ # 顺序场景便捷:q.drain(requester) 一次性取走(尊重租约,跳过被租用项)
142
+ ```
143
+
144
+ **设计要点:** 锁内挑选+标记(并发不重复租用);租约过期自动重入队(结果不丢);ack 校验 lease_id(防过期租约误删别人重新租走的项)。
145
+
146
+ ---
147
+
148
+ ## 测试
149
+
150
+ 全部纳入回归(`python tests/run_all.py`):
151
+ - `test_governance.py` — 六项治理能力(幂等并发、boot_id 防复用、租约不重复等)
152
+ - `test_channels.py` — 渠道适配器 + 闭环 + 幂等去重
153
+
154
+ ---
155
+
156
+ ## 设计原则小结
157
+
158
+ 这组能力反复体现几个原则,可供后续开发参考:
159
+
160
+ - **操作要校验是否真达到目的**:压缩后检查真变小了吗、幂等并发下真只执行一次吗——不假设操作一定有效。
161
+ - **静默失效最危险**:拼错钩子名、解密缺库、消息处理失败被吞——都要让问题可见(警告/last_error),而非默默不工作。
162
+ - **崩溃后不装没事**:重启恢复主动交代中断的任务,契合"可信"定位。
163
+ - **别只信 pid**:进程恢复要用 boot_id 这类不复用的标识,pid 会被 OS 复用。
164
+ - **便捷接口别破坏核心保证**:drain 要尊重租约,否则破坏"不重复"。
@@ -0,0 +1,110 @@
1
+ # 发布流程
2
+
3
+ > **当前状态(2026-11-07):发布暂缓。** 开发全部完成后再一次性发,现在只往 GitHub 推代码。
4
+ > 因此**不要**打 `v*` tag(`.github/workflows/release.yml` 由 tag 触发),也**不要**跑
5
+ > `workflow_dispatch` 的 `publish` 选项。流水线已经就位,等要发时按下面走。
6
+ >
7
+ > 另外两个 registry 的现状(查过,别再当"全新包"处理):
8
+ >
9
+ > - PyPI `foxclaw` 已有 **0.1.0**(2026-11-06 上传,不能重发,只能 yank);
10
+ > - npm `foxclaw` 已有 `0.0.0-stage` / `0.1.0` / **0.1.1**(latest),而仓库代码是 0.1.0
11
+ > —— 那个 0.1.1 是手工 `npm publish` 后没提交,git 里找不到对应版本。
12
+ >
13
+ > 所以下次真正发布时,四处版本号必须先跳到 **≥ 0.1.2**(PyPI 只需 >0.1.0,npm 需 >0.1.1,取大)。
14
+
15
+ 两条发布通道、两种产物,性质完全不同,先说清楚免得误会:
16
+
17
+ | 产物 | 命令 | 里面有什么 |
18
+ | --- | --- | --- |
19
+ | PyPI `foxclaw` | `pip install foxclaw` | **占位包**:能 import,但 `vendor/`(HKC/HMR)不入包 → 知识/记忆子系统降级为 None |
20
+ | npm `foxclaw` | `npm i foxclaw` | 占位包:`index.js` 只导出 `name` / `version` |
21
+ | GitHub Release | tag 自动 | 上面两个包的文件 + 自动生成的 release notes |
22
+
23
+ `vendor/` 不入包是设计现状(`packages.find` 只收 `foxclaw*`,HMR/HKC 以 sys.path
24
+ 内嵌,运行期按 `Path(__file__)/../../vendor` 找)。发布说明里必须写明"这是占位包",
25
+ 不要让人装完以为拿到了完整运行时。
26
+
27
+ ## 1. 本地预检(发布前必须绿)
28
+
29
+ ```bash
30
+ python tools/release_check.py --tag v0.1.0 # 四处版本号一致 + tag 对得上
31
+ python -m build # 出 dist/*.whl + *.tar.gz
32
+ cd npm && npm pack --pack-destination ../dist && cd ..
33
+ python tools/release_check.py --dist dist --npm-tgz dist/*.tgz
34
+ ```
35
+
36
+ 第二道检查会做这些事,任一 `[ERROR]` 都不许发布:
37
+
38
+ - `dist/` 里恰好一个 wheel + 一个 sdist,文件名与版本号一致;
39
+ - wheel 的 `METADATA` 里 `Name` / `Version` 与 `pyproject.toml` 一致;
40
+ - 归档内**不得**出现 `.env`(除 `.env.example`)、`docs/internal/`、`foxclaw_data/`、
41
+ `__pycache__`、嵌套压缩包、`.venv`;
42
+ - npm tgz 文件名与 `package/package.json` 的 `name@version` 一致;
43
+ - 缺 `vendor/` 只报 `[WARN]`(见上表),不会拦发布。
44
+
45
+ 版本号的唯一来源是 `pyproject.toml`,另外四处(`foxclaw/__init__.py`、
46
+ `npm/package.json`、`npm/index.js`、`frontend_src/package.json`)由
47
+ `tests/test_version_sync.py` 盯着必须相等。
48
+ 改版本时**五处一起改**,只改一处会在第一道检查里挂掉。
49
+
50
+ ## 2. 打 tag(自动构建 + 挂 Release,不外发)
51
+
52
+ ```bash
53
+ git tag v0.1.0 && git push origin v0.1.0
54
+ ```
55
+
56
+ `.github/workflows/release.yml` 的 `build` job 会:校验版本/tag → 构建 → npm pack →
57
+ 产物结构校验 → 上传 `python-dist` / `npm-tgz` 两个 artifact;`github-release` job 把它们
58
+ 挂成 Release 附件(重跑用 `--clobber` 覆盖附件,不重建 Release)。
59
+
60
+ 这一步**不会**上传到 PyPI / npm,打错了删 tag 重来即可。
61
+
62
+ ## 3. 上传到 PyPI / npm(需要人工审批)
63
+
64
+ Actions → Release → Run workflow:
65
+
66
+ - `publish`:选 `pypi` / `npm` / `both`
67
+ - `ref`:填**同一个 tag**(如 `v0.1.0`;不填会直接失败——不允许发分支 HEAD)
68
+
69
+ 然后经由 environment 审批:
70
+
71
+ | environment | 审批后执行 | 用的 secret |
72
+ | --- | --- | --- |
73
+ | `pypi` | `pypa/gh-action-pypi-publish` | `PYPI_API_TOKEN`(项目级 `pypi-`/`npt_` token) |
74
+ | `npm` | `npm publish` | `NPM_TOKEN`(granular,仅 `foxclaw` 的 publish 权限) |
75
+
76
+ 需要在仓库 Settings → Environments 里先建 `pypi`、`npm` 两个环境并配 Required reviewers,
77
+ 否则"审批门"不存在,勾了就直接发。
78
+
79
+ ## 4. 发错了怎么办
80
+
81
+ **PyPI / npm 的版本号不可撤回**。能做的只有:
82
+
83
+ - PyPI:`yank`(`twine`/网页)→ 该版本不再被新装选中,但**仍然占着号**,不能重发同号;
84
+ 修好后必须 `0.1.1`。
85
+ - npm:`npm unpublish foxclaw@0.1.0` 只在发布后 72 小时内、且无其他包依赖时可用;
86
+ 过了就只能 `npm deprecate` + 发新号。
87
+
88
+ 所以"先构建、再审批上传"不是流程洁癖,是这两个 registry 的规则逼出来的。
89
+
90
+ ## 5. 切到 OIDC(零长期密钥)
91
+
92
+ 当前用 token,因为上传已有人工审批兜底。想彻底去掉长期密钥时:
93
+
94
+ - PyPI:项目页 → Publishing → 加 GitHub publisher(owner/repo/workflow/environment
95
+ 必须与实际一致),首次发布用 "pending publisher";然后删掉 secret,给 job 加
96
+ `permissions: id-token: write`。
97
+ - npm:npm ≥ 11.5 支持 trusted publishing,同样加 `id-token: write` 并去掉 `NODE_AUTH_TOKEN`。
98
+
99
+ ## 仓库名(2026-11-08 已定)
100
+
101
+ 两个仓库,职责不同,别再混:
102
+
103
+ | 仓库 | 可见性 | 用途 |
104
+ |---|---|---|
105
+ | `snowfoxHQ/FoxClaw` | public | 对外发布。PyPI/npm 的 `homepage`/`repository` 都指它,OIDC publisher 也填它 |
106
+ | 私有开发库(名称不对外) | private | 本地 `git remote origin` 指向的开发备份 |
107
+
108
+ 公开仓库原名 `SnowFoxClaw`,已于 2026-11-08 改名为 `FoxClaw`(GitHub 对旧名自动 301,
109
+ 老链接不会 404)。切 OIDC 时 publisher 的 repo 必须写成 `FoxClaw`——不是私有开发库的名字,
110
+ 也不是小写 `foxclaw`(registry 侧对大小写不敏感,GitHub 侧 OIDC 校验按仓库真实名匹配)。
@@ -0,0 +1,75 @@
1
+ # Foxclaw Run Guide (English)
2
+
3
+ > Chinese version with more detail: [RUN_CN.md](RUN_CN.md)
4
+
5
+ ## Windows + OneDrive (most common pitfall)
6
+
7
+ If tests raise `FileNotFoundError: [WinError 2] ... test_data`, the project is under a OneDrive-managed folder (Documents/Desktop/Pictures) whose redirect placeholders Python can't create dirs in. Move it:
8
+
9
+ ```powershell
10
+ Copy-Item -Recurse "<your path>\foxclaw" "C:\foxclaw"
11
+ cd C:\foxclaw
12
+ python tests/run_all.py
13
+ ```
14
+
15
+ ## 1. Install
16
+
17
+ ```bash
18
+ pip install -e . # dependencies come from pyproject.toml (single source of truth)
19
+ # lightweight alternative (uninstalled extras degrade gracefully):
20
+ # pip install fastapi uvicorn "httpx<0.28" cryptography websockets pyyaml numpy scikit-learn rich pydantic python-dotenv
21
+ ```
22
+
23
+ `cryptography` → Feishu encrypted-event decryption; `websockets` → Discord Gateway; `httpx` → channel HTTP + web tools.
24
+
25
+ ## 2. Verify (no API key needed)
26
+
27
+ ```bash
28
+ python tests/run_all.py # full regression (all green)
29
+ python tests/test_e2e.py # cognitive runtime only
30
+ python tests/test_api.py # API layer
31
+ ```
32
+
33
+ These run without an LLM key or real HMR — proving cognition is decoupled from the model.
34
+
35
+ > **About pytest**: all 76 tests are now pytest-style (S2 migration) and need `pip install pytest`
36
+ > (or `pip install -e ".[dev]"`). When pytest is absent the runner **explicitly marks all 76 as
37
+ > "skipped"** and excludes them from the denominator — they neither vanish silently nor fake a pass.
38
+
39
+ ## 3. Start the service
40
+
41
+ ```bash
42
+ export DEEPSEEK_API_KEY=sk-... # or ANTHROPIC_API_KEY; Windows: $env:...="..."
43
+ export FOXCLAW_PROVIDER=deepseek # optional explicit provider
44
+ export FOXCLAW_DATA_DIR=./foxclaw_data
45
+ # binds 127.0.0.1 by default. Exposing to LAN requires FOXCLAW_HOST=0.0.0.0 **and** FOXCLAW_API_KEY
46
+ # (the __main__ entry refuses to start on non-loopback without a key; override with FOXCLAW_ALLOW_INSECURE=1).
47
+ python -m uvicorn foxclaw.api:app --host 127.0.0.1 --port 8000
48
+ ```
49
+
50
+ ## 4. Channels & governance
51
+
52
+ Connect WeChat / Feishu / Telegram / Discord — see [CHANNELS.md](CHANNELS.md). Runtime governance (idempotency / crash recovery / loop detection) is auto-wired — see [GOVERNANCE.md](GOVERNANCE.md).
53
+
54
+ ## 5. Embedded HMR
55
+
56
+ HMR v2.0 is embedded in `vendor/hmr/`, auto-loaded, persisting to the data dir's `hmr/` subdir. To upgrade, API-compatibly replace `vendor/hmr/`; the adapter (`foxclaw/core/memory_adapter.py`) and main body stay unchanged.
57
+
58
+ ## 6. Key environment variables
59
+
60
+ | Var | Meaning |
61
+ |-----|---------|
62
+ | `DEEPSEEK_API_KEY` / `ANTHROPIC_API_KEY` | LLM key; unset → stub |
63
+ | `FOXCLAW_PROVIDER` | explicit provider |
64
+ | `FOXCLAW_DATA_DIR` | data directory |
65
+ | `FOXCLAW_SHADOW_ROUNDS` | shadow calibration rounds |
66
+ | `FOXCLAW_EMBEDDING_MODEL` | 语义漂移 embedding 模型;中文部署设 `paraphrase-multilingual-MiniLM-L12-v2`(中文跑题硬拦率 0→1.0,见 `evals/governance/compare_embeddings.py`);默认 `all-MiniLM-L6-v2` |
67
+ | `FOXCLAW_API_KEY` | **Optional auth**: when set, `/api/*` requires `X-API-Key: <value>` (else 401); WebSocket `/ws/harness` uses `?key=<value>` instead (browsers can't set WS headers; the frontend reads it from `localStorage.foxclawApiKey`). When unset, auth is off — safe only on loopback. Frontend and `/docs` stay open. |
68
+ | `FOXCLAW_HOST` / `FOXCLAW_PORT` | Bind address / port for the `python -m foxclaw.api` entry. **Default `127.0.0.1:8000`.** |
69
+ | `FOXCLAW_ALLOW_INSECURE` | Set to `1` to let the entry start on a non-loopback host without `FOXCLAW_API_KEY` (not recommended). |
70
+ | `FOXCLAW_CORS_ORIGINS` | Comma-separated allowed origins. **Default: loopback only** (`localhost` / `127.0.0.1` / `[::1]`, any port). Set explicitly for cross-origin deploys; `*` opens it fully (not recommended). |
71
+ | `FOXCLAW_SHARED_WRITE_MODE` | Shared-memory write policy mode: **`audit` (default) publishes `memory.shared.write` events without blocking**; `warn` also logs; `enforce` actually denies (raises `SharedWriteDenied`). Default is audit because the 8 existing legitimate shared writes must validate the policy table on real traffic before enforcement is switched on. See `foxclaw/core/shared_write.py`. **Run `python tools/shared_write_audit.py` before rolling forward** — end-to-end acceptance (8 real write sites must pass untouched, 5 intrusion probes must be caught; exit code 0 required). |
72
+ | `FOXCLAW_SHARED_WRITE_AUDIT` | Shared-write audit **persistence** switch (default on): appends every `memory.shared.write` event as a JSON line to `{data_dir}/audit/shared_write.jsonl`, so real-traffic samples accumulate for the enforce decision (the spine history is a 1000-entry ring buffer that high-frequency events flush out). Set `0` to disable. How to judge: review each `allowed=false` line — if all are genuine intrusions and none is a legitimate write, switch to `enforce`. |
73
+ | `FOXCLAW_GOVERNANCE_AUDIT` | Unified **governance audit stream** switch (default on, batch 3): rulings from the Harness/Permission/Orchestration/Skill layers are recorded as `governance.decision` events (WS-visible) and persisted to `{data_dir}/audit/governance.jsonl`. Each record carries a `decision_id` (this ruling) and a `correlation_id` (cross-layer join key: a permission ruling's decision_id is attached to the skill execution audit; negotiation rounds share the req.id). Set `0` to disable (all layers skip automatically, zero overhead). |
74
+ | `FOXCLAW_SHADOW_PROMOTE_MIN_OBS` | Observations required before a shadow rule can be promoted (default 50, batch 4 P0-1): rule candidates born from BLOCK enter `shadow_rules` (**not in effect**); after N same-dimension observations they are promoted (take effect) or discarded (harm rate > 0.3, or zero impact). Before that, rules only observe and never block — the guardrail against "unverified online learning". Roll back with `harness.rollback(rule_id)`; the full trail lives in the policy YAML `history` section. |
75
+ | `FOXCLAW_SKIP_DOTENV` | Set to `1` to **skip loading the project-root `.env` entirely** — process env vars become the only source. **For tests/CI only**: a developer `.env` carrying real keys or `FOXCLAW_MCP_SERVER=1` / `FOXCLAW_API_KEY` leaks into test processes, making results machine-dependent, causing tests to hit real external APIs, and writing into real data directories. `tests/run_all.py` sets it to `1` by default. **Do not set this in local development.** |