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,200 @@
1
+ """
2
+ Foxclaw — 全量回归测试运行器
3
+ 一次跑完 tests/ 下所有测试,汇总通过/失败,确认整个系统端到端健康。
4
+ 用法:python tests/run_all.py
5
+ """
6
+ import os
7
+ import shutil
8
+ import subprocess
9
+ import sys
10
+ import tempfile
11
+ import time
12
+ from pathlib import Path
13
+
14
+ for _s in (sys.stdout, sys.stderr):
15
+ try:
16
+ _s.reconfigure(encoding="utf-8", line_buffering=True)
17
+ except Exception:
18
+ pass
19
+
20
+ TESTS = [
21
+ ("test_e2e.py", "认知运行时端到端"),
22
+ ("test_api.py", "API 端点"),
23
+ ("test_integration.py", "A · 前后端实时链路"),
24
+ ("test_dispatch.py", "D · 主管调度 + 共享记忆"),
25
+ ("test_finance.py", "B · 财务内核"),
26
+ ("test_learning.py", "B · 学习内核"),
27
+ ("test_itinerary.py", "B · 行程内核"),
28
+ ("test_orchestration.py","主管编排 + 跨 Agent"),
29
+ ("test_skills.py", "助手 Skill 接入层"),
30
+ ("test_skills_fetch.py", "技能安装(ClawHub/GitHub/URL/本地包)"),
31
+ ("test_kernel.py", "内核新模块回归"),
32
+ ("test_governance.py", "运行时治理(幂等/恢复/队列/压缩/循环/钩子)"),
33
+ ("test_compaction_toolpair.py", "M5·压缩器工具消息配对(原子组切分+畸形输入安全降级)"),
34
+ ("test_tool_params.py", "M4·工具入参校验(执行侧 schema/Stage1/解析失败可见)"),
35
+ ("test_shared_write.py", "P0-3 · 共享层写入策略(audit/warn/enforce)"),
36
+ ("test_audit_sink.py", "P0-3 · 审计落盘 sink(环形缓冲外的持久样本)"),
37
+ ("test_governance_audit.py", "批次3 · 治理审计链(P1-2/P1-3/P2-0/P3-4 decision_id)"),
38
+ ("test_shadow_learning.py", "批次4 · P0-1 影子学习(shadow→promote/discard+回滚+边界)"),
39
+ ("test_channels.py", "消息渠道(四适配器+闭环+幂等去重)"),
40
+ ("test_evals_smoke.py", "M1 · 治理评测冒烟(拦截率/误报/收敛)"),
41
+ ("test_parallel_dispatch.py", "M2 · 并行子 Agent(fork-join/隔离/并发安全)"),
42
+ ("test_governance_middleware.py", "M3 · 治理中间件(框架无关/装饰器/审计)"),
43
+ ("test_memory_recall.py", "M4 · 记忆隔离召回完整性(根治)"),
44
+ ("test_toplevel_concurrency.py", "顶层任务并发隔离(contextvars)"),
45
+ ("test_api_auth.py", "API 可选鉴权(X-API-Key)"),
46
+ ("test_supervisor_triage.py", "主管分诊与自答策略(关键词直路 / 自答 / stub 兜底)"),
47
+ ("test_supervisor_pipeline.py", "批次5 · P1-1 主管 pipeline 化(八 stage/统一 emit/流式/恢复记账)"),
48
+ ("test_ahs_p2.py", "批次6 · P2层(compose pending/run_async/idempotency日志/recovery计划)+P3-2"),
49
+ ("test_p3_p5_hardening.py", "批次7 · P3收尾(isinstance/常量时间/exact/blocked短路/system归因)+P5-1/P5-3"),
50
+ ("test_browser.py", "浏览器子系统(HIR-Browser:核心逻辑/线程包装/降级)"),
51
+ ("test_hkc.py", "知识核心(HKC融入:装配/检索/H1资源修复/惰性teardown)"),
52
+ ("test_hkc_manifest_reuse.py", "P1·Stage A — manifest v2 复用(roundtrip/8步校验/bm25 0-corpus)"),
53
+ ("test_hkc_search_event_sync.py", "P1·Stage B — 事件→索引 C/U/D 五分支同步(text_hash 跳过/立即落盘)"),
54
+ ("test_hkc_index_build_manager.py", "P1·Stage C — 后台冷构建(状态机/检查点/锁/batch/取消/重启续传)"),
55
+ ("test_search.py", "认知搜索(HSR融入:编排/证据融合/迭代上限/端口降级)"),
56
+ ("test_embedding_singleton.py", "P0·EmbeddingService单例(同名模型进程内只加载一次)"),
57
+ ("test_event_spine.py", "P0·统一事件脊柱(EventSpine/SystemBus门面/harness转发/统一投影)"),
58
+ ("test_agent_lifecycle.py", "P1·任务生命周期收敛(5 run_task→1 模板 + _execute 钩子)"),
59
+ ("test_planner.py", "P1-2·规划原语统一(主管/AHS/search 三处委托一个引擎)"),
60
+ ("test_capabilities.py", "P1-3·能力中枢(Composition Root/打断两处对象环)"),
61
+ ("test_truth_flow.py", "P2·真值流全路径触发(直连任务也进真值链 + _no_emit 防双发)"),
62
+ ("test_orchestration_decoupled.py", "P3·编排层去领域硬编码(通用聚合/路由,领域归属Agent)"),
63
+ ("test_pipeline.py", "v2②·认知管线(perceive/recall/plan/route/act/verify/reflect)"),
64
+ ("test_scheduler.py", "v2①·微内核调度器(并发配额/背压/生命周期/记账)+Lifecycle统一"),
65
+ ("test_policy_gate.py", "v2①Step4·统一准入门(Harness+skill-guard→一套Verdict+审计)"),
66
+ ("test_truth_crystallizer.py", "v2④Step1·真值走HKC/KEE(默认关双写+后台结晶+订阅接线)"),
67
+ ("test_reasoner.py", "v2③Step1·领域认知化(观察→假设→证据→裁决;财务守门人形式化)"),
68
+ ("test_domain_reasoners.py", "v2③Step4·认知化推广(itinerary冲突/learning复习;规则权威+LLM只增强)"),
69
+ ("test_tool_center.py", "工具中心①·技能台真实化(安装/启停/删除/测试/持久化/SSRF审核门/治理)"),
70
+ ("test_mcp_connectors.py", "工具中心②·MCP 连接器(接入/工具成技能/审核/调用/移除/持久化)"),
71
+ ("test_markdown_skills.py", "工具中心③·SKILL.md 指令技能(上传/解析/审核/注入/持久化)"),
72
+ ("test_markdown_skills_openclaw.py", "工具中心③·OpenClaw 兼容(zip包/描述触发/编辑触发词·进程隔离防原生库崩溃)"),
73
+ ("test_mcp_server.py", "内置 MCP 服务器(Foxclaw 作提供者:只读知识+记忆暴露成 MCP 工具)"),
74
+ ("test_mcp_no_memory.py", "MCP ask_foxclaw 不污染记忆(外部任务不写 HMR,正常任务照写)"),
75
+ ("test_streaming.py", "LLM 流式输出(router.stream 分块 + /api/chat/stream SSE 逐字)"),
76
+ ("test_task_stream.py", "D1 · /api/task/stream 任务流式化(逐 stage 进度 + act token + 结果)"),
77
+ ("test_config_layering.py", "E1 · 分层配置(LayeredConfig 三层 + policy 层 deny 不可覆盖)"),
78
+ ("test_semantic.py", "真语义检索 + KEE 语义化(共享ST单例/余弦/矛盾方向;无缓存优雅跳过)"),
79
+ ("test_metrics.py", "ops 监控(请求/LLM 延迟分位+错误率 · 深度健康 · Prometheus)"),
80
+ ("test_routing.py", "任务感知模型路由(fast/smart 分档 + 跨 provider fallback 链)"),
81
+ ("test_file_ingest.py", "文件摄入打通交互层(docx/xlsx 加载器 + KDE 分派 + 上传格式门)"),
82
+ ("test_multimodal.py", "多模态对话(图片随问题发视觉模型 · 按 provider 组装 · 护栏 · stub 降级)"),
83
+ ("test_attachments.py", "多模态渲染·聊天附件(上传/下载/正文提取/护栏/LLM 注入/会话落盘)"),
84
+ ("test_gateway.py", "统一 API 网关(per-client 令牌桶+配额+空闲淘汰+429/Retry-After+启用哲学)"),
85
+ ("test_kee_semantic.py", "KEE 身份/去重(别名确定性匹配·类型隔离·Fact 完整 statement 守 exact)"),
86
+ ("test_domain_import.py", "OS 领域导入超越 csv(finance/learning/itinerary 上传 csv/xlsx/json)"),
87
+ ("test_tracing.py", "请求链路追踪(trace_id 贯穿 请求→Agent→LLM·跨线程 span·有界环存)"),
88
+ ("test_emb_cache.py", "检索/embedding 共享向量缓存(批量只编未命中·跨层共享·LRU 有界·并发)"),
89
+ ("test_multi_chain.py", "智能体层·多步跨 Agent 链式协作(后一步看得到前序结果·上下文封顶)"),
90
+ ("test_web_trigger.py", "联网触发判定(个人数据不误搜·外部话题照搜·纯时间词不触发)"),
91
+ ("test_evidence_semantic.py", "证据融合语义化(否定词边界·幻影矛盾·语义支持边·降级)"),
92
+ ("test_domain_evals.py", "领域推理评测(漏报/误报率+误报归因到规则+规则实例隔离)"),
93
+ ("test_negotiation.py", "智能体层·跨域协商闭环(被拒→让步→再裁决·有界·拒绝不降级)"),
94
+ ("test_tool_use.py", "P2·function-calling 工具化(多轮工具循环/注册表/降级)"),
95
+ ("test_injection_guard.py", "提示词注入纵深防御(检出/wrap_untrusted/内核前缀/隔离)"),
96
+ ("test_sessions.py", "会话存储(增删改查/标题提炼/路径穿越/并发追加)"),
97
+ ("test_stats_and_events.py","记忆统计 + 活动事件(hmr.stats 计数修复/chat.completed)"),
98
+ ("test_llm_planner.py", "P0-1·LLM 驱动规划器(llm_plan/replan·opt-in+fail-safe+sv_replan)"),
99
+ ("test_anthropic_tools.py", "P0-2·Anthropic tool_use 原生接入(格式转换/解析/回灌/降级)"),
100
+ ("test_taint.py", "数据污点追踪+动态审批门槛(污点分级/传播/TAINT_CONFIRM)"),
101
+ ("test_mcp_guardian_discovery.py", "MCP 发现进守门人(凭证不出主进程·代发现·序列化)"),
102
+ ("test_verify_evidence.py", "三个薄点·失败策略表/执行后核验/执行证据(重试只限只读)"),
103
+ ("test_evidence_sse.py", "执行凭据 SSE 命名事件(与正文分离·纯对话不发)"),
104
+ ("test_profile.py", "称呼(user profile)存取:写入/替换/清空/不误删偏好"),
105
+ ("test_update_check.py", "新版本检查:默认关/版本比较/24h 缓存/失败静默"),
106
+ ]
107
+
108
+
109
+ def main():
110
+ here = Path(__file__).parent
111
+ _argv = sys.argv[1:]
112
+ global TESTS
113
+ if "--list" in _argv:
114
+ print(f"共 {len(TESTS)} 项:")
115
+ for _f, _d in TESTS:
116
+ print(f" {_f:<40} {_d}")
117
+ sys.exit(0)
118
+ _only = None
119
+ if "--only" in _argv:
120
+ _i = _argv.index("--only")
121
+ _only = _argv[_i + 1] if _i + 1 < len(_argv) else None
122
+ if not _only:
123
+ print("--only 需要一个子串参数,例:python tests/run_all.py --only governance")
124
+ sys.exit(2)
125
+ _before = len(TESTS)
126
+ TESTS = [(f, d) for f, d in TESTS if _only in f or _only in d]
127
+ print(f"[filter] '{_only}' 匹配 {len(TESTS)}/{_before} 项\n")
128
+ env = dict(os.environ, FOXCLAW_SHADOW_ROUNDS="50",
129
+ HF_HUB_OFFLINE="1", TRANSFORMERS_OFFLINE="1",
130
+ PYTHONUTF8="1", PYTHONIOENCODING="utf-8",
131
+ FOXCLAW_SKIP_DOTENV="1",
132
+ DEEPSEEK_API_KEY="", ANTHROPIC_API_KEY="", OPENAI_API_KEY="",
133
+ GOOGLE_API_KEY="", DASHSCOPE_API_KEY="", FOXCLAW_API_KEY="",
134
+ FOXCLAW_MCP_SERVER="0",
135
+ FOXCLAW_PROVIDER="", FOXCLAW_MODEL="",
136
+ HKC_EMBEDDING="stub")
137
+ print("=" * 64)
138
+ print("Foxclaw · 全量回归测试")
139
+ print("=" * 64)
140
+ results = []
141
+ try:
142
+ import pytest # noqa: F401
143
+ have_pytest = True
144
+ except Exception:
145
+ have_pytest = False
146
+ if not have_pytest:
147
+ print("⚠ 未安装 pytest —— 依赖它的测试将标记为「跳过」。"
148
+ "完整回归请先 `pip install -e \".[dev]\"` 或 `pip install pytest`。\n")
149
+ for fn, desc in TESTS:
150
+ if not have_pytest:
151
+ results.append((desc, "跳过", 0.0))
152
+ print(f" ○ {desc:<28} [跳过:需 pytest]")
153
+ continue
154
+ path = here / fn
155
+ if not path.exists():
156
+ results.append((desc, "缺失", 0)); continue
157
+ t0 = time.time()
158
+ _bt = Path(tempfile.mkdtemp(prefix="foxclaw-pt-"))
159
+ try:
160
+ _cmd = [sys.executable, "-m", "pytest", str(path), "-q",
161
+ f"--basetemp={_bt}"]
162
+ r = subprocess.run(_cmd,
163
+ capture_output=True, text=True, timeout=300, env=env,
164
+ encoding="utf-8", errors="replace")
165
+ ok = r.returncode == 0
166
+ if "Traceback" in (r.stdout + r.stderr):
167
+ ok = False
168
+ _ACCESS_VIOLATION = (3221225477, -1073741819)
169
+ if not ok and r.returncode in _ACCESS_VIOLATION and \
170
+ "Traceback" not in (r.stdout + r.stderr):
171
+ r = subprocess.run(_cmd,
172
+ capture_output=True, text=True, timeout=300, env=env,
173
+ encoding="utf-8", errors="replace")
174
+ ok = r.returncode == 0 and "Traceback" not in (r.stdout + r.stderr)
175
+ if ok:
176
+ print(f" ↻ {desc} 首次 0xC0000005 原生崩溃,重试通过(已知环境性偶发)")
177
+ results.append((desc, "通过" if ok else "失败", time.time() - t0))
178
+ if not ok:
179
+ tail = (r.stdout + r.stderr).strip().splitlines()[-15:]
180
+ print(f" ✗ {desc} 失败:")
181
+ for line in tail: print(f" {line}")
182
+ (here / f"_fail_{fn}.txt").write_text(
183
+ r.stdout + r.stderr, encoding="utf-8")
184
+ except subprocess.TimeoutExpired:
185
+ results.append((desc, "超时", 300))
186
+ finally:
187
+ shutil.rmtree(_bt, ignore_errors=True)
188
+ print(f" {'✓' if results[-1][1]=='通过' else '✗'} {desc:<28} [{results[-1][1]}] {results[-1][2]:.0f}s")
189
+
190
+ print("\n" + "=" * 64)
191
+ passed = sum(1 for _, s, _ in results if s == "通过")
192
+ skipped = sum(1 for _, s, _ in results if s == "跳过")
193
+ total = len(results) - skipped
194
+ suffix = f"(另有 {skipped} 项因缺 pytest 跳过)" if skipped else ""
195
+ print(f"结果:{passed}/{total} 通过{suffix}")
196
+ print("=" * 64)
197
+ return 0 if passed == total else 1
198
+
199
+ if __name__ == "__main__":
200
+ sys.exit(main())
@@ -0,0 +1,326 @@
1
+ """
2
+ 统一任务生命周期(P1:5 个 run_task 收敛为 1)守卫测试 —— 全离线。
3
+
4
+ 覆盖:
5
+ 1. 结构收敛:4 个领域 Agent 复用 base 的模板 run_task(不各自重写),各自实现 _execute;
6
+ SupervisorAgent 作为路由器合法地保留自己的 run_task。
7
+ 2. 统一异常兜底:_execute 抛 HarnessBlockedError → 模板统一返回结构化错误(一处定义,全 Agent 生效)。
8
+ 3. 领域钩子生效:各 Agent 的 entities/recall 前缀/召回条数按覆盖生效(行为逐字保留)。
9
+ """
10
+ import os, sys, asyncio, tempfile, shutil, types
11
+ from pathlib import Path
12
+ os.environ.setdefault("HF_HUB_OFFLINE", "1")
13
+ os.environ.setdefault("TRANSFORMERS_OFFLINE", "1")
14
+ os.environ["FOXCLAW_EMBEDDING_MODEL"] = "foxclaw-nonexistent-fast"
15
+ sys.path.insert(0, str(Path(__file__).parent.parent))
16
+
17
+ from foxclaw.agents.base import CognitiveAgent, HarnessBlockedError
18
+ from foxclaw.agents.agents import (
19
+ FinanceAgent, OfficeAgent, SupervisorAgent)
20
+
21
+
22
+ def test_structural_convergence():
23
+ print("\n【1】结构收敛:全部 Agent 复用模板 run_task(主管经 P1-1 pipeline 化收敛)")
24
+ for A in (FinanceAgent, OfficeAgent):
25
+ assert A.run_task is CognitiveAgent.run_task, f"{A.__name__} 不应重写 run_task"
26
+ assert A._execute is not CognitiveAgent._execute, f"{A.__name__} 应实现 _execute"
27
+ assert SupervisorAgent.run_task is CognitiveAgent.run_task, \
28
+ "P1-1:主管不再重写 run_task(路由分发在 pipeline stage 里)"
29
+ assert SupervisorAgent._cognitive_pipeline is not CognitiveAgent._cognitive_pipeline, \
30
+ "主管应覆盖 _cognitive_pipeline 使用 SUPERVISOR_STAGES"
31
+ print(" ✓ 5 个 Agent 共用唯一 run_task 模板;主管差异收敛为 pipeline 覆盖(P1-1)")
32
+
33
+
34
+ def test_domain_hooks():
35
+ print("\n【2】领域钩子:entities / recall 前缀 / 召回条数 逐字保留")
36
+ assert FinanceAgent._task_entities(None, "x") == ["财务", "收支", "储蓄"]
37
+ assert FinanceAgent._recall_query(None, "买车") == "财务 买车"
38
+ assert OfficeAgent._recall_query(None, "开会") == "学习 知识 日程 行程 开会"
39
+ assert OfficeAgent._task_entities(None, "x") == ["学习", "知识", "文档", "日程", "行程", "会议"]
40
+ assert OfficeAgent._recall_topk == 8, "办公学习 Agent 覆盖为广泛召回 8"
41
+ assert CognitiveAgent._recall_topk == 5, "基类默认仍是 5"
42
+ print(" ✓ finance/admin/office 的 entities、recall 前缀、top_k 均按覆盖生效")
43
+
44
+
45
+ def test_unified_block_handling():
46
+ print("\n【3】统一异常兜底:_execute 抛 HarnessBlockedError → 结构化错误")
47
+ d = tempfile.mkdtemp(prefix="life_")
48
+ try:
49
+ from foxclaw import Foxclaw
50
+ os_ = Foxclaw(data_dir=d, shadow_rounds=50)
51
+
52
+ async def boom(self, task, context, session_id, memories):
53
+ raise HarnessBlockedError(types.SimpleNamespace(reason="模拟越界"), None)
54
+
55
+ os_.finance._execute = types.MethodType(boom, os_.finance)
56
+ r = asyncio.run(os_.finance.run_task("危险任务"))
57
+ assert r.get("error") == "harness_blocked", f"应兜底为 harness_blocked,实得 {r}"
58
+ assert r.get("reason") == "模拟越界" and "session_id" in r
59
+ print(f" ✓ 模板统一兜底: {r}")
60
+ asyncio.run(os_.aclose())
61
+ finally:
62
+ shutil.rmtree(d, ignore_errors=True)
63
+
64
+
65
+ def test_finance_verify_before_recommend():
66
+ """【4·P5】记忆验证优先:财务事实以**当前账本(live analyze)**为准,召回记忆仅作历史上下文。
67
+ 锁定 finance._execute 的 verify-before-recommend 设计——即便召回了**过时**记忆,权威事实源
68
+ 仍是当前交易(`fin.analyze()`),记忆被显式降级为"历史财务记录"。防未来重构把过时记忆当事实源
69
+ (呼应范式"记忆是快照,非事实")。评估结论见方案 P5:该设计已存在,本测试是把它锁死的回归护栏。"""
70
+ print("\n【4】P5 记忆验证优先:财务事实以当前账本为准、记忆仅历史上下文")
71
+ d = tempfile.mkdtemp(prefix="p5_")
72
+ os_ = None
73
+ try:
74
+ from foxclaw import Foxclaw
75
+ os_ = Foxclaw(data_dir=d, shadow_rounds=50)
76
+ fin = os_.finance
77
+ os_.finance_runtime.import_csv(
78
+ str(Path(__file__).parent.parent / "examples" / "sample_transactions.csv"))
79
+ captured = {}
80
+ async def _capture(self, task, messages):
81
+ captured["prompt"] = messages[-1]["content"]
82
+ return "(模拟回答)"
83
+ fin._call_llm_web = types.MethodType(_capture, fin)
84
+ stale = [{"content": "历史:上月预算结余 8000 元", "id": "m1"}]
85
+ asyncio.run(fin._execute("这个月还能花多少?", {}, "s1", stale))
86
+ p = captured["prompt"]
87
+ assert "基于真实交易,请据此回答" in p, "缺 live analyze() 权威事实源(verify-before-recommend 退化)"
88
+ assert "历史财务记录" in p and "8000" in p, "召回记忆应作历史上下文出现,而非当前事实"
89
+ assert p.index("历史财务记录") < p.index("基于真实交易"), \
90
+ "结构应为:历史记忆在前、当前账本权威在后(措辞上 live『请据此回答』压过历史记忆)"
91
+ print(" ✓ live analyze() 作权威源『请据此回答』;过时记忆降级为『历史财务记录』(P5 已锁定)")
92
+ finally:
93
+ if os_ is not None:
94
+ asyncio.run(os_.aclose())
95
+ shutil.rmtree(d, ignore_errors=True)
96
+
97
+
98
+ def test_run_task_stream():
99
+ """【5·D1】run_task 流式化:同步 generator 逐 stage 进度 + act 逐 token + 最终结果,且与非流式
100
+ run_task **行为一致**(共享 `_run_task_core`)。关键验证:`_act_stream` contextvar 真的跨
101
+ worker 线程 / asyncio 边界传到 `_call_llm`(否则一个 delta token 都不会有)。"""
102
+ print("\n【5】D1 run_task 流式化:stage 进度 + act token + 结果")
103
+ d = tempfile.mkdtemp(prefix="d1_")
104
+ os_ = None
105
+ try:
106
+ from foxclaw import Foxclaw
107
+ os_ = Foxclaw(data_dir=d, shadow_rounds=50)
108
+ ag = os_.office
109
+ ag.router.provider = "stub"
110
+
111
+ events = list(ag.run_task_stream("帮我总结一下今天"))
112
+ stages = [e["stage"] for e in events if "stage" in e]
113
+ deltas = [e["delta"] for e in events if "delta" in e]
114
+ results = [e["result"] for e in events if "result" in e]
115
+ assert stages == ["perceive", "recall", "plan", "route", "act", "verify", "reflect"], stages
116
+ assert len(deltas) > 0, "act 应逐 token 出字;为空说明 contextvar 没传到 _call_llm"
117
+ assert len(results) == 1, f"应恰有一个最终 result,实得 {len(results)}"
118
+ res = results[0]
119
+ assert res.get("agent") == ag.AGENT_ID and "session_id" in res and "result" in res
120
+ assert "".join(deltas) == res["result"], "流式 token 拼接应等于最终答案"
121
+
122
+ plain = asyncio.run(ag.run_task("帮我总结一下今天"))
123
+ assert "result" in plain and "delta" not in plain and plain.get("agent") == ag.AGENT_ID
124
+
125
+ import threading as _th
126
+ ev = _th.Event(); ev.set()
127
+ ev_events = list(ag.run_task_stream("这个任务会被中断", stop_event=ev))
128
+ ev_stages = [e for e in ev_events if "stage" in e]
129
+ ev_results = [e["result"] for e in ev_events if "result" in e]
130
+ assert ev_stages == [], f"预置 stop → 0 个 stage 事件,实得 {ev_stages}"
131
+ assert len(ev_results) == 1 and ev_results[0].get("interrupted") is True, ev_results
132
+
133
+ def _boom(*a, **k):
134
+ raise RuntimeError("流式炸了")
135
+ ag.router.stream = _boom
136
+ from foxclaw.agents.base import _act_stream as _AS
137
+ async def _one_call():
138
+ tok = _AS.set({"on_token": lambda c: None, "stop_event": None})
139
+ try:
140
+ return await ag._call_llm([{"role": "user", "content": "x"}])
141
+ finally:
142
+ _AS.reset(tok)
143
+ out = asyncio.run(_one_call())
144
+ assert isinstance(out, str) and out, "流式失败应 fail-safe 降级 complete(返回非空串),而非崩"
145
+
146
+ print(f" ✓ stage={len(stages)} delta={len(deltas)} result=1;token 拼接==答案;"
147
+ f"非流式不受影响;预置 stop 起步即停;流式异常 fail-safe 降级 complete")
148
+ finally:
149
+ if os_ is not None:
150
+ asyncio.run(os_.aclose())
151
+ shutil.rmtree(d, ignore_errors=True)
152
+
153
+
154
+ def test_interrupted_task_not_sunk():
155
+ """【6·B3 深审 G2】被 stop_event 中断的任务 = 未完成,绝不沉淀为已完成真值(blocked=True)。
156
+ 深审发现:_emit_execution 的 blocked 原只看 skill_status,中断任务(skill_status=None)会
157
+ blocked=False → 部分/空结果被 TruthPipeline 当完成写进 HMR 共享记忆(违反 P5/Principle07)。
158
+ 修复后 blocked 联动 ctx.interrupted。这条路径由 B3 的 /api/task/stream(断连 set stop_event)
159
+ 首次在生产可达,故 happy-path 测不到、深审才暴露。"""
160
+ print("\n【6】B3 深审 G2:中断任务不沉淀真值(blocked=True)")
161
+ d = tempfile.mkdtemp(prefix="g2_")
162
+ os_ = None
163
+ try:
164
+ import threading as _th
165
+ from foxclaw import Foxclaw
166
+ os_ = Foxclaw(data_dir=d, shadow_rounds=50)
167
+ ag = os_.supervisor
168
+ ag.router.provider = "stub"
169
+ captured = {}
170
+ orig = ag._emit_execution
171
+ def _spy(task, result, blocked, context):
172
+ captured["blocked"] = blocked
173
+ return orig(task, result, blocked=blocked, context=context)
174
+ ag._emit_execution = _spy
175
+ ev = _th.Event(); ev.set()
176
+ r = asyncio.run(ag.run_task("会被中断的任务", stop_event=ev))
177
+ assert r.get("interrupted") is True, r
178
+ assert captured.get("blocked") is True, "中断任务必须 blocked=True(不把部分/空结果当完成真值沉淀)"
179
+ captured.clear()
180
+ r2 = asyncio.run(ag.run_task("正常任务"))
181
+ assert "interrupted" not in r2 and captured.get("blocked") is False, \
182
+ "正常完成任务 blocked=False(行为逐字不变,照常沉淀真值)"
183
+ print(" ✓ 中断→blocked=True(不沉淀);正常→blocked=False(照常沉淀,行为不变)")
184
+ finally:
185
+ if os_ is not None:
186
+ asyncio.run(os_.aclose())
187
+ shutil.rmtree(d, ignore_errors=True)
188
+
189
+
190
+ def test_reasoning_lifecycle_hooks():
191
+ """【7·E2】认知管线生命周期钩子:pre_reasoning(agent_start)可注入推理上下文(前插进 LLM);
192
+ task_complete(agent_end)只读观测;默认无钩子=行为逐字不变。这两个语义名是此前从未 fire 的
193
+ 孤儿 agent_start/agent_end 的别名——E2 顺带复活了它们并给认知管线 run_task 装上钩子。"""
194
+ print("\n【7】E2 生命周期钩子:pre_reasoning 注入 + task_complete 观测")
195
+ d = tempfile.mkdtemp(prefix="e2_")
196
+ os_ = None
197
+ try:
198
+ from foxclaw import Foxclaw
199
+ os_ = Foxclaw(data_dir=d, shadow_rounds=50)
200
+ ag = os_.office
201
+ ag.router.provider = "stub"
202
+ seen = {}
203
+ def _cap_complete(system, messages, max_tokens, agent_id):
204
+ seen["messages"] = messages
205
+ return "(stub 回答)"
206
+ ag.router.complete = _cap_complete
207
+
208
+ asyncio.run(ag.run_task("基线任务"))
209
+ base_msgs = seen.get("messages", [])
210
+ assert base_msgs and "【E2注入】" not in base_msgs[0].get("content", ""), \
211
+ f"无 pre_reasoning 钩子时不应注入,实得 {base_msgs[:1]}"
212
+
213
+ completed = []
214
+ ag.hooks.register("pre_reasoning", lambda c: {"additional_context": "【E2注入】优先考虑省钱"})
215
+ ag.hooks.register("task_complete", lambda c: completed.append(c.get("result")))
216
+ asyncio.run(ag.run_task("帮我看看要不要买"))
217
+ msgs = seen.get("messages", [])
218
+ joined = " ".join(m.get("content", "") for m in msgs)
219
+ assert "【E2注入】优先考虑省钱" in joined, f"pre_reasoning 注入应进入 LLM messages,实得 {msgs}"
220
+ roles = [m.get("role") for m in msgs]
221
+ assert not any(roles[i] == roles[i + 1] == "user" for i in range(len(roles) - 1)), \
222
+ f"注入不应产生连续两条 user 消息(违反模型无关铁律),roles={roles}"
223
+ assert len(completed) == 1, f"task_complete 应触发一次并收到 result,实得 {len(completed)}"
224
+
225
+ import types
226
+ async def _boom(self, task, context, session_id, memories):
227
+ raise HarnessBlockedError(types.SimpleNamespace(reason="E2越界"), None)
228
+ ag._execute = types.MethodType(_boom, ag)
229
+ completed.clear()
230
+ rb = asyncio.run(ag.run_task("越界任务"))
231
+ assert rb.get("error") == "harness_blocked", rb
232
+ assert len(completed) == 1, "task_complete 应在 HarnessBlock 结束路径也触发(观测被拦截任务)"
233
+ print(" ✓ 无钩子不注入(behavior-preserving);pre_reasoning 注入前插;task_complete 正常+拦截路径都观测")
234
+ finally:
235
+ if os_ is not None:
236
+ asyncio.run(os_.aclose())
237
+ shutil.rmtree(d, ignore_errors=True)
238
+
239
+
240
+ def test_preference_priority_recall():
241
+ """【8·偏好优先召回】明确偏好标 tag=preference(shared 持久)+ **独立于语义 query** 优先注入。
242
+ 核心对照:偏好与当前任务**语义完全不相关**时,纯语义 recall 召不到,但偏好召回仍注入——
243
+ 修复"越用越懂你"断在召回链的洞。默认开;FOXCLAW_PREF_CAPTURE=0 关采集。"""
244
+ print("\n【8】偏好优先召回:语义不相关也注入")
245
+ d = tempfile.mkdtemp(prefix="pref_")
246
+ os_ = None
247
+ try:
248
+ from foxclaw import Foxclaw
249
+ os_ = Foxclaw(data_dir=d, shadow_rounds=50)
250
+ ag = os_.office
251
+ ag.router.provider = "stub"
252
+ seen = {}
253
+ def _cap(system, messages, max_tokens, agent_id):
254
+ seen["messages"] = messages; return "(stub)"
255
+ ag.router.complete = _cap
256
+
257
+ asyncio.run(ag.run_task("我喜欢简洁直接的回答,不要长篇大论"))
258
+ prefs = ag.hmr.recall_by_tag(agent_id="supervisor", tag="preference", limit=8)
259
+ assert any("简洁" in p.get("content", "") for p in prefs), f"偏好应带 preference tag 存下,实得 {prefs}"
260
+
261
+ seen.clear()
262
+ asyncio.run(ag.run_task("解释一下光合作用的原理"))
263
+ joined = " ".join(m.get("content", "") for m in seen.get("messages", []))
264
+ assert "用户偏好" in joined and "简洁" in joined, \
265
+ f"语义不相关任务也应注入偏好(越用越懂你),实得 {seen.get('messages')}"
266
+
267
+ before = len(ag.hmr.recall_by_tag(agent_id="assistant", tag="preference"))
268
+ asyncio.run(ag.run_task("光合作用发生在植物的哪个部位"))
269
+ after = len(ag.hmr.recall_by_tag(agent_id="assistant", tag="preference"))
270
+ assert after == before, "无偏好信号的任务不应新增偏好"
271
+
272
+ os.environ["FOXCLAW_PREF_CAPTURE"] = "0"
273
+ try:
274
+ asyncio.run(ag.run_task("我喜欢用表格展示数据"))
275
+ got = ag.hmr.recall_by_tag(agent_id="assistant", tag="preference", limit=20)
276
+ assert not any("表格" in p.get("content", "") for p in got), "关采集后不应存偏好"
277
+ finally:
278
+ os.environ.pop("FOXCLAW_PREF_CAPTURE", None)
279
+
280
+ seen_stream = []
281
+ def _cap_stream(system, messages, *a, **k):
282
+ seen_stream.append(messages); yield "(stub)"
283
+ ag.router.stream = _cap_stream
284
+ list(ag.stream_reply("随便讲点什么"))
285
+ js = " ".join(m.get("content", "") for m in (seen_stream[0] if seen_stream else []))
286
+ assert "用户偏好" in js and "简洁" in js, f"对话路径(stream_reply)也应注入偏好,实得 {seen_stream}"
287
+
288
+ for _ in range(3):
289
+ ag._store_memory("用户偏好:我喜欢简洁直接的回答,不要长篇大论",
290
+ mem_type="preference", tags=["preference"], shared=True)
291
+ got = ag.hmr.recall_by_tag(agent_id="assistant", tag="preference", limit=8)
292
+ same = [p for p in got if "简洁直接的回答" in p.get("content", "")]
293
+ assert len(same) == 1, f"重复偏好召回应去重为 1 条,实得 {len(same)}"
294
+
295
+ ag._store_memory("用户偏好:请用英文回复", mem_type="preference", tags=["preference"], shared=True)
296
+ target = [p for p in ag.list_preferences() if "英文" in p.get("content", "")]
297
+ assert target and target[0].get("id"), f"唯一偏好应可列出带 id,实得 {target}"
298
+ assert ag.delete_preference(target[0]["id"]), "删偏好应成功"
299
+ gone = [p for p in ag.list_preferences() if "英文" in p.get("content", "")]
300
+ assert not gone, "删除后该偏好不应再出现(F3:误存可清理)"
301
+ print(" ✓ 偏好存 tag;两路径注入;去重;不误存;可关;可 list/删除(F3)")
302
+ finally:
303
+ if os_ is not None:
304
+ asyncio.run(os_.aclose())
305
+ shutil.rmtree(d, ignore_errors=True)
306
+
307
+
308
+ def main():
309
+ print("=" * 60)
310
+ print("统一任务生命周期 · run_task 收敛(P1)")
311
+ print("=" * 60)
312
+ test_structural_convergence()
313
+ test_domain_hooks()
314
+ test_unified_block_handling()
315
+ test_finance_verify_before_recommend()
316
+ test_run_task_stream()
317
+ test_interrupted_task_not_sunk()
318
+ test_reasoning_lifecycle_hooks()
319
+ test_preference_priority_recall()
320
+ print("\n" + "=" * 60)
321
+ print("✓ P1 验证完成:5 run_task→1 模板 + 领域 _execute 钩子 + 统一兜底 + P5 记忆验证优先")
322
+ print("=" * 60)
323
+
324
+
325
+ if __name__ == "__main__":
326
+ main()