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,91 @@
1
+ """
2
+ Foxclaw 学习子系统 — 间隔重复引擎(固化层)
3
+ 实现 SM-2 算法(Anki 核心算法的经典版本)。
4
+ 核心思想:学习是时间连续系统——追踪每个知识点的记忆强度随时间衰减,
5
+ 在最佳时机安排复习,让"越用越熟"变成可计算的机制。
6
+ """
7
+
8
+ from datetime import datetime, timedelta
9
+ from .models import ReviewState
10
+
11
+
12
+ class SpacedRepetition:
13
+ """SM-2 间隔重复算法。
14
+
15
+ 评分 grade(0~5):
16
+ 0-2 = 没记住(算 lapse,重置间隔)
17
+ 3 = 勉强记住
18
+ 4 = 记住了
19
+ 5 = 完美回忆
20
+ """
21
+
22
+ @staticmethod
23
+ def review(state: ReviewState, grade: int) -> ReviewState:
24
+ """根据本次回忆质量,更新间隔重复状态。返回更新后的 state。"""
25
+ grade = max(0, min(5, int(grade)))
26
+ now = datetime.now()
27
+ state.total_reviews += 1
28
+ state.last_review = now.isoformat()
29
+
30
+ if grade < 3:
31
+ state.repetitions = 0
32
+ state.interval = 1
33
+ state.lapses += 1
34
+ else:
35
+ if state.repetitions == 0:
36
+ state.interval = 1
37
+ elif state.repetitions == 1:
38
+ state.interval = 6
39
+ else:
40
+ state.interval = round(state.interval * state.ease_factor)
41
+ state.repetitions += 1
42
+
43
+ ef = state.ease_factor + (0.1 - (5 - grade) * (0.08 + (5 - grade) * 0.02))
44
+ state.ease_factor = max(1.3, round(ef, 3))
45
+
46
+ state.due_date = (now + timedelta(days=state.interval)).isoformat()
47
+ return state
48
+
49
+ @staticmethod
50
+ def due_items(states: list) -> list:
51
+ """返回所有到期需复习的状态,按掌握度升序(最弱的先复习)。"""
52
+ due = [s for s in states if s.is_due]
53
+ return sorted(due, key=lambda s: s.mastery)
54
+
55
+ @staticmethod
56
+ def forgetting_risk(states: list, horizon_days: int = 3) -> list:
57
+ """遗忘风险检测(对应财务异常检测):
58
+ 即将到期且掌握度低的知识点 = 高遗忘风险。"""
59
+ risky = []
60
+ for s in states:
61
+ if s.days_until_due <= horizon_days and s.mastery < 0.5:
62
+ risky.append({
63
+ "item_id": s.item_id,
64
+ "mastery": s.mastery,
65
+ "days_until_due": s.days_until_due,
66
+ "lapses": s.lapses,
67
+ "risk": round((1 - s.mastery) * (1 if s.is_due else 0.6), 3),
68
+ "reason": f"掌握度仅 {s.mastery},"
69
+ + ("已到期" if s.is_due else f"{s.days_until_due} 天内到期"),
70
+ })
71
+ return sorted(risky, key=lambda x: -x["risk"])
72
+
73
+ @staticmethod
74
+ def learning_load(states: list, days: int = 7) -> dict:
75
+ """学习负荷(对应财务烧钱速度):未来 N 天的复习量。"""
76
+ from collections import defaultdict
77
+ by_day: defaultdict[int, int] = defaultdict(int)
78
+ for s in states:
79
+ d = s.days_until_due
80
+ if 0 <= d <= days:
81
+ by_day[d] += 1
82
+ elif d < 0:
83
+ by_day[0] += 1
84
+ total_due = sum(by_day.values())
85
+ return {
86
+ "window_days": days,
87
+ "total_due": total_due,
88
+ "daily_avg": round(total_due / days, 1),
89
+ "by_day": dict(sorted(by_day.items())),
90
+ "overdue": sum(1 for s in states if s.is_due),
91
+ }
@@ -0,0 +1,188 @@
1
+ """Foxclaw 内置 MCP 服务器:把只读知识(HKC)+记忆(HMR)暴露成 MCP 工具,
2
+ 让任何 MCP 客户端(Claude Desktop/Cursor/别的 Agent)能调用个人知识与记忆。
3
+ 默认关(FOXCLAW_MCP_SERVER=1 开),挂 /api/mcp/,受 X-API-Key 鉴权。
4
+ 安全:knowledge_*/memory_recall 只读;ask_foxclaw(需再开 FOXCLAW_MCP_AGENTS)会驱动 Agent
5
+ 并写入记忆/审计,非只读。工具名/描述中性,错误对外脱敏(不泄露内部细节/架构)。"""
6
+ from __future__ import annotations
7
+ import os
8
+ import time
9
+ import threading
10
+ from mcp.server.fastmcp import FastMCP
11
+
12
+ _MAX_TOP_K = 50
13
+ _MAX_QUERY = 2000
14
+
15
+
16
+ class _RateLimiter:
17
+ """令牌桶限流(线程安全)。防外部刷爆:读工具与 ask_foxclaw 各一桶。"""
18
+ def __init__(self, per_min: int):
19
+ self.capacity = max(1, int(per_min))
20
+ self.tokens = float(self.capacity)
21
+ self.rate = self.capacity / 60.0
22
+ self.last = time.monotonic()
23
+ self.lock = threading.Lock()
24
+
25
+ def allow(self) -> bool:
26
+ with self.lock:
27
+ now = time.monotonic()
28
+ self.tokens = min(self.capacity, self.tokens + (now - self.last) * self.rate)
29
+ self.last = now
30
+ if self.tokens >= 1:
31
+ self.tokens -= 1
32
+ return True
33
+ return False
34
+
35
+
36
+ def _env_int(name: str, default: int) -> int:
37
+ try:
38
+ return int(os.environ.get(name, str(default)))
39
+ except ValueError:
40
+ return default
41
+
42
+
43
+ def _clamp_top_k(v, default: int = 5) -> int:
44
+ try:
45
+ return max(1, min(int(v), _MAX_TOP_K))
46
+ except Exception:
47
+ return default
48
+
49
+
50
+ def _clean_query(q) -> str:
51
+ return (str(q or "")[:_MAX_QUERY])
52
+
53
+
54
+ def _external_action(sid: str):
55
+ """给「非技能动作」(如外部驱动 Agent)做个最小 skill 替身,好走同一套权限门。
56
+
57
+ 鸭子类型,只提供 permission.evaluate 真正读的字段;不 import skills 包(避免启停期的
58
+ 包级循环)。side_effect=write + external=True 是事实描述:它确实有副作用且面向外部。
59
+ """
60
+ from types import SimpleNamespace
61
+ return SimpleNamespace(id=sid, kind="mcp", params={"task": {"required": True}},
62
+ side_effect="write", external=True,
63
+ destructive=False, read_only=False)
64
+
65
+
66
+ def _policy_error(gate, rules, task: str) -> str | None:
67
+ """外部驱动动作的准入裁决。返回 None = 放行,否则返回错误码。
68
+
69
+ 为什么这里要单独判:外部客户端「绕开工具直接驱动 Agent」,若不过门,宿主配的
70
+ deny(封杀某动作)和 ask(要求人工)对它完全无效。
71
+ MCP 没有交互确认通道 → 命中 ask 规则即拒,而不是放行后等一个永远不会来的确认;
72
+ 其余常规 ask(副作用 / external)由 FOXCLAW_MCP_AGENTS=1 这道显式开关承担授权。
73
+ """
74
+ if gate is None:
75
+ return None
76
+ from foxclaw.skills.permission import ASK_RULE, MISSING_PARAMS
77
+ v = gate.check_action(_external_action("mcp:ask_foxclaw"), {"task": task},
78
+ rules=rules, mode="strict", confirmed=False)
79
+ code = getattr(v.raw, "reason_code", "") or ""
80
+ if getattr(v, "level", "") == "deny":
81
+ return "blocked"
82
+ if code == ASK_RULE:
83
+ return "needs_confirmation"
84
+ if code == MISSING_PARAMS:
85
+ return "missing arguments"
86
+ return None
87
+
88
+
89
+ def build_foxclaw_mcp(os_getter, expose_agents: bool = False) -> FastMCP:
90
+ """os_getter:无参回调,返回当前 Foxclaw 实例(惰性,启动后才有)。
91
+ expose_agents:另开一道开关才暴露 Agent 调度(有副作用面,默认关)。"""
92
+ mcp = FastMCP("Foxclaw Knowledge", stateless_http=True, streamable_http_path="/")
93
+ read_limiter = _RateLimiter(_env_int("FOXCLAW_MCP_RATE_READ", 120))
94
+ agent_limiter = _RateLimiter(_env_int("FOXCLAW_MCP_RATE_AGENT", 20))
95
+
96
+ @mcp.tool()
97
+ def knowledge_search(query: str, top_k: int = 5, domain: str = "") -> list:
98
+ """在个人知识库中检索相关知识条目(只读)。返回每条的 id/名称/摘要/类型。"""
99
+ if not read_limiter.allow():
100
+ return [{"error": "rate limited"}]
101
+ try:
102
+ hkc = getattr(os_getter(), "hkc", None)
103
+ if hkc is None or not getattr(hkc, "available", False):
104
+ return []
105
+ hits = hkc.client.search(_clean_query(query), mode="hybrid",
106
+ top_k=_clamp_top_k(top_k), domain=str(domain or "")[:120])
107
+ return [{"id": getattr(h, "ku_id", "") or getattr(h, "id", ""),
108
+ "name": getattr(h, "name", ""),
109
+ "summary": getattr(h, "summary", ""),
110
+ "type": str(getattr(h, "ku_type", ""))}
111
+ for h in (hits or [])]
112
+ except Exception:
113
+ return [{"error": "internal error"}]
114
+
115
+ @mcp.tool()
116
+ def knowledge_get(ku_id: str) -> dict:
117
+ """按 id 取一个知识条目的详情(只读)。"""
118
+ if not read_limiter.allow():
119
+ return {"error": "rate limited"}
120
+ try:
121
+ hkc = getattr(os_getter(), "hkc", None)
122
+ if hkc is None or not getattr(hkc, "available", False):
123
+ return {"error": "knowledge base unavailable"}
124
+ ku = hkc.container.graph_store.get(str(ku_id or "")[:200])
125
+ if ku is None:
126
+ return {"error": "not_found"}
127
+ return {"id": getattr(ku, "ku_id", ku_id), "name": getattr(ku, "name", ""),
128
+ "summary": getattr(ku, "summary", ""), "type": str(getattr(ku, "ku_type", "")),
129
+ "domain": getattr(ku, "domain", ""), "tags": list(getattr(ku, "tags", []) or [])}
130
+ except Exception:
131
+ return {"error": "internal error"}
132
+
133
+ @mcp.tool()
134
+ def memory_recall(query: str, top_k: int = 5) -> list:
135
+ """在个人记忆中召回相关内容(只读;仅助手层+共享层)。"""
136
+ if not read_limiter.allow():
137
+ return [{"error": "rate limited"}]
138
+ try:
139
+ hmr = getattr(os_getter(), "hmr", None)
140
+ if hmr is None:
141
+ return []
142
+ res = hmr.recall(query=_clean_query(query), agent_id="supervisor", top_k=_clamp_top_k(top_k))
143
+ return [{"id": r.get("id", ""), "content": r.get("content", ""),
144
+ "type": r.get("type", "")} for r in (res or [])]
145
+ except Exception:
146
+ return [{"error": "internal error"}]
147
+
148
+ if expose_agents:
149
+ @mcp.tool()
150
+ async def ask_foxclaw(task: str, agent: str = "supervisor") -> dict:
151
+ """把一个任务交给 Foxclaw 的认知 Agent 处理(主管默认自动分诊到财务/行政/办公/助手)。
152
+ 返回 {reply, dispatched_to}。
153
+ ⚠ 非只读:执行过程会写入个人记忆(HMR)与审计链。有副作用的**技能**调用(写/发送/支付)
154
+ 会被内部权限门拦下需确认,MCP 无法确认→不执行;但记忆写入等内部副作用仍会发生。
155
+ 因此这是"写能力"工具,仅在 FOXCLAW_MCP_AGENTS=1 且鉴权下暴露。"""
156
+ if not agent_limiter.allow():
157
+ return {"error": "rate limited"}
158
+ import asyncio as _a
159
+ os_ = os_getter()
160
+ if os_ is None:
161
+ return {"error": "not ready"}
162
+ task = _clean_query(task)
163
+ agent = str(agent or "supervisor")[:40]
164
+ if not task.strip():
165
+ return {"error": "missing arguments"}
166
+ err = _policy_error(getattr(os_, "policy_gate", None),
167
+ getattr(getattr(os_, "skill_adapter", None), "rules", None),
168
+ task)
169
+ if err:
170
+ return {"error": err}
171
+
172
+ def _work():
173
+ from foxclaw.agents.base import _suppress_memory
174
+ _suppress_memory.set(True)
175
+ return os_.scheduler.run(
176
+ lambda: _a.run(os_.run(task, agent)),
177
+ agent_id=agent, task=task)
178
+ try:
179
+ res = await _a.to_thread(_work)
180
+ except Exception:
181
+ return {"error": "internal error"}
182
+ if not isinstance(res, dict):
183
+ return {"reply": str(res), "dispatched_to": agent}
184
+ reply = (res.get("sub_result") or res.get("summary")
185
+ or res.get("reply") or res.get("error") or "")
186
+ return {"reply": str(reply), "dispatched_to": res.get("dispatched_to", agent)}
187
+
188
+ return mcp
@@ -0,0 +1,167 @@
1
+ """
2
+ Foxclaw 主管编排子系统 — 全局态势 + 跨 Agent 编排
3
+ 主管不是领域专家,而是协调者。它的两个核心能力:
4
+ 1. GlobalSituation:汇总三个内核状态,形成"驾驶舱视图"
5
+ 2. CrossAgentRequest:一个 Agent 的行为触发另一个 Agent 的约束
6
+ (典型:行程 Agent 规划出差 → 向财务 Agent 申请差旅预算 → 财务守门人审批)
7
+ """
8
+
9
+ from collections import deque
10
+ from dataclasses import dataclass, field, asdict
11
+ from datetime import datetime
12
+ from typing import Literal
13
+ import uuid
14
+
15
+
16
+ RequestType = Literal["budget_request", "schedule_request", "knowledge_request",
17
+ "info_request"]
18
+ RequestStatus = Literal["pending", "approved", "rejected", "needs_confirmation"]
19
+
20
+
21
+ @dataclass
22
+ class CrossAgentRequest:
23
+ """跨 Agent 请求 — 编排的基本单元。
24
+ 一个 Agent 向另一个发起请求,目标 Agent 用自己的守门人裁决。"""
25
+ from_agent: str
26
+ to_agent: str
27
+ request_type: RequestType
28
+ payload: dict
29
+ reason: str = ""
30
+ status: RequestStatus = "pending"
31
+ verdict: str = ""
32
+ negotiation: list = field(default_factory=list)
33
+ id: str = field(default_factory=lambda: f"req_{uuid.uuid4().hex[:10]}")
34
+ created_at: str = field(default_factory=lambda: datetime.now().isoformat())
35
+
36
+ def to_dict(self) -> dict:
37
+ return asdict(self)
38
+
39
+
40
+ @dataclass
41
+ class GlobalSituation:
42
+ """全局态势 — 主管的驾驶舱视图,汇总各内核关键指标。"""
43
+ finance: dict = field(default_factory=dict)
44
+ learning: dict = field(default_factory=dict)
45
+ itinerary: dict = field(default_factory=dict)
46
+ alerts: list = field(default_factory=list)
47
+ timestamp: str = field(default_factory=lambda: datetime.now().isoformat())
48
+
49
+ def to_dict(self) -> dict:
50
+ return asdict(self)
51
+
52
+
53
+ class Orchestrator:
54
+ """主管编排引擎:汇总态势 + 路由跨 Agent 请求到目标守门人。
55
+
56
+ 【P1-3】不再直接持有 agents 字典,而是持 CapabilityRegistry、按需惰性解析
57
+ (打断 Supervisor⇄Orchestrator 直接环:主管持有本引擎,本引擎经注册表解析 agents,
58
+ 不再反向直接持有含主管的 agents 字典)。"""
59
+
60
+ def __init__(self, caps, auditor=None):
61
+ self._caps = caps
62
+ self._auditor = auditor
63
+ self.request_log: deque = deque(maxlen=1000)
64
+
65
+ @property
66
+ def agents(self) -> dict:
67
+ """按名字从注册中枢解析 agents(id -> agent 实例)。"""
68
+ return self._caps.get("agents") or {}
69
+
70
+ def gather_situation(self) -> GlobalSituation:
71
+ """通用聚合各 Agent 自报的领域态势(agent.situation()),形成驾驶舱视图。
72
+ 编排层不再直接读 finance/learning/itinerary 内核——领域知识已归属各领域 Agent。"""
73
+ sit = GlobalSituation()
74
+ alerts: list[dict] = []
75
+ for agent in self.agents.values():
76
+ try:
77
+ s = agent.situation()
78
+ except Exception:
79
+ continue
80
+ if not s:
81
+ continue
82
+ doms = s.get("domains")
83
+ if isinstance(doms, dict) and doms:
84
+ for d, st in doms.items():
85
+ if d and hasattr(sit, d):
86
+ setattr(sit, d, st or {})
87
+ else:
88
+ domain = s.get("domain")
89
+ if domain and hasattr(sit, domain):
90
+ setattr(sit, domain, s.get("status", {}) or {})
91
+ alerts += s.get("alerts", []) or []
92
+ sit.alerts = sorted(alerts,
93
+ key=lambda a: {"critical": 0, "warn": 1, "info": 2}
94
+ .get(a.get("severity"), 3))
95
+ return sit
96
+
97
+ def route_request(self, req: CrossAgentRequest) -> CrossAgentRequest:
98
+ """把请求路由给目标 Agent,由**目标 Agent**用自己的守门人裁决(编排层不懂领域)。"""
99
+ target = self.agents.get(req.to_agent)
100
+ if target is None:
101
+ req.status = "needs_confirmation"
102
+ req.verdict = f"目标 Agent '{req.to_agent}' 不存在,无法裁决,转人工确认"
103
+ else:
104
+ try:
105
+ target.handle_cross_request(req)
106
+ except Exception as e:
107
+ req.status = "needs_confirmation"
108
+ req.verdict = f"裁决异常,转人工确认: {e}"
109
+ self.request_log.append(req)
110
+ self._audit_verdict(req)
111
+ return req
112
+
113
+ def _audit_verdict(self, req: CrossAgentRequest) -> None:
114
+ """【P1-3】把本次裁决记入统一治理审计流(governance.decision)。
115
+
116
+ correlation_id=req.id:negotiate 的多轮裁决共用同一 req.id ⇒ 协商链可 join;
117
+ 每次裁决的 decision_id 各自独立(由审计器生成)。审计失败绝不影响路由。"""
118
+ if self._auditor is None:
119
+ return
120
+ verdict = {"approved": "allow", "rejected": "deny",
121
+ "needs_confirmation": "confirm"}.get(req.status, req.status)
122
+ self._auditor.record(
123
+ layer="orchestration", actor=req.from_agent,
124
+ action=f"cross_request:{req.request_type}->{req.to_agent}",
125
+ verdict=verdict, reason=req.verdict,
126
+ detail={"status": req.status,
127
+ "negotiation_round": len(req.negotiation)},
128
+ correlation_id=req.id)
129
+
130
+ def negotiate(self, req: CrossAgentRequest, max_rounds: int = 3) -> CrossAgentRequest:
131
+ """跑协商协议。原先裁决是**终局**:财务说预算不足→rejected→到此为止,用户只拿到
132
+ 一句"被拒绝"。现在:发起方可拿着拒绝理由降标准重来(改高铁/换酒店/减一天)。
133
+
134
+ 职责边界(守 P3):**编排层只跑协议、不懂领域** ——
135
+ · 裁决权仍在**目标 Agent**的守门人(route_request 原样调用,不绕过)
136
+ · 让步逻辑仍在**发起 Agent**(agent.concede),编排层只问"你能让吗"
137
+
138
+ 安全不变量:
139
+ · **有界**:最多 max_rounds 轮,防死循环
140
+ · **必须真让步**:payload 没变化就停(否则无限重复同一请求)
141
+ · **协商失败维持 rejected,绝不降级成 needs_confirmation** ——
142
+ 那等于给用户开一个点穿守门人的口子。协商只能"找到守门人愿意批的方案",
143
+ 不能"把不批变成可以批"。
144
+ """
145
+ for rnd in range(1, max(1, max_rounds) + 1):
146
+ self.route_request(req)
147
+ req.negotiation.append({"round": rnd, "status": req.status,
148
+ "verdict": req.verdict,
149
+ "payload": dict(req.payload)})
150
+ if req.status != "rejected":
151
+ break
152
+ requester = self.agents.get(req.from_agent)
153
+ if requester is None:
154
+ break
155
+ try:
156
+ revised = requester.concede(req, req.verdict)
157
+ except Exception as e:
158
+ req.negotiation.append({"round": rnd, "concede_error": str(e)[:120]})
159
+ break
160
+ if not revised or revised == req.payload:
161
+ break
162
+ req.negotiation[-1]["conceded_to"] = dict(revised)
163
+ req.payload = revised
164
+ return req
165
+
166
+
167
+ __all__ = ["CrossAgentRequest", "GlobalSituation", "Orchestrator"]
@@ -0,0 +1,23 @@
1
+ """
2
+ foxclaw.pipeline —— 认知管线(Cognitive Pipeline, v2 ②)。
3
+
4
+ 把任务处理从"隐式直线"升级为**显式、分阶段**的认知流:
5
+ perceive → recall → plan → route → act → verify → reflect → persist
6
+ 每个 stage 是一等公民,操作共享的 PipelineContext,可插桩/替换(规则版⇄LLM版)/重放。
7
+
8
+ Step 1(抽壳):先落 recall / act 两个 stage,把 P1-1 的统一模板 run_task 委托给管线,
9
+ 行为逐字不变;后续步骤往 stages 列表里加 verify/reflect/plan/route/perceive。
10
+ """
11
+ from .cognitive_pipeline import (
12
+ PipelineContext, CognitivePipeline,
13
+ stage_perceive, stage_recall, stage_plan, stage_route, stage_act,
14
+ stage_verify, stage_reflect, DEFAULT_STAGES,
15
+ )
16
+ from .supervisor_stages import SUPERVISOR_STAGES
17
+
18
+ __all__ = [
19
+ "PipelineContext", "CognitivePipeline",
20
+ "stage_perceive", "stage_recall", "stage_plan", "stage_route", "stage_act",
21
+ "stage_verify", "stage_reflect", "DEFAULT_STAGES",
22
+ "SUPERVISOR_STAGES",
23
+ ]