foxclaw 0.1.0 → 0.1.2

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 (495) hide show
  1. package/README.md +54 -29
  2. package/bin/foxclaw.cjs +51 -0
  3. package/index.js +11 -10
  4. package/package.json +46 -38
  5. package/python/.dockerignore +35 -0
  6. package/python/.env.example +165 -0
  7. package/python/.gitattributes +46 -0
  8. package/python/.github/workflows/ci.yml +122 -0
  9. package/python/.github/workflows/release.yml +183 -0
  10. package/python/Dockerfile +64 -0
  11. package/python/LICENSE +21 -0
  12. package/python/MANIFEST.in +14 -0
  13. package/python/README.md +192 -0
  14. package/python/README_CN.md +324 -0
  15. package/python/bench/bm25_audit.py +199 -0
  16. package/python/bench/bm25_regression.py +178 -0
  17. package/python/bench/bm25_stress.py +159 -0
  18. package/python/bench/cd_audit.py +238 -0
  19. package/python/bench/chaos.py +142 -0
  20. package/python/bench/concurrency.py +103 -0
  21. package/python/bench/e2e_real_models.py +95 -0
  22. package/python/bench/llm_retry_test.py +248 -0
  23. package/python/bench/load_test.py +196 -0
  24. package/python/bench/vector_ivf_upgrade.py +134 -0
  25. package/python/docs/CHANNELS.md +195 -0
  26. package/python/docs/GOVERNANCE.md +164 -0
  27. package/python/docs/RELEASE.md +109 -0
  28. package/python/docs/RUN.md +75 -0
  29. package/python/docs/RUN_CN.md +121 -0
  30. package/python/docs//351/205/215/347/275/256/346/214/207/345/215/227.md +152 -0
  31. package/python/evals/__init__.py +0 -0
  32. package/python/evals/governance/README.md +61 -0
  33. package/python/evals/governance/__init__.py +0 -0
  34. package/python/evals/governance/cases.py +152 -0
  35. package/python/evals/governance/compare_embeddings.py +94 -0
  36. package/python/evals/governance/convergence.svg +11 -0
  37. package/python/evals/governance/report.json +371 -0
  38. package/python/evals/governance/run_evals.py +400 -0
  39. package/python/examples/governance_middleware_demo.py +77 -0
  40. package/python/examples/langgraph_governance_demo.py +108 -0
  41. package/python/examples/sample_itinerary.csv +6 -0
  42. package/python/examples/sample_knowledge.csv +9 -0
  43. package/python/examples/sample_transactions.csv +14 -0
  44. package/python/foxclaw/__init__.py +526 -0
  45. package/python/foxclaw/agents/__init__.py +12 -0
  46. package/python/foxclaw/agents/agents.py +35 -0
  47. package/python/foxclaw/agents/base.py +865 -0
  48. package/python/foxclaw/agents/impl/__init__.py +1 -0
  49. package/python/foxclaw/agents/impl/_helpers.py +78 -0
  50. package/python/foxclaw/agents/impl/finance.py +248 -0
  51. package/python/foxclaw/agents/impl/office.py +367 -0
  52. package/python/foxclaw/agents/impl/supervisor.py +659 -0
  53. package/python/foxclaw/agents/mixins/_context.py +151 -0
  54. package/python/foxclaw/agents/mixins/augment.py +149 -0
  55. package/python/foxclaw/agents/mixins/llm.py +66 -0
  56. package/python/foxclaw/agents/mixins/memory.py +35 -0
  57. package/python/foxclaw/agents/mixins/preference.py +81 -0
  58. package/python/foxclaw/agents/mixins/prompt.py +77 -0
  59. package/python/foxclaw/agents/mixins/skillbox.py +58 -0
  60. package/python/foxclaw/agents/mixins/tool_exec.py +443 -0
  61. package/python/foxclaw/api/__init__.py +283 -0
  62. package/python/foxclaw/api/app.py +123 -0
  63. package/python/foxclaw/api/middleware.py +317 -0
  64. package/python/foxclaw/api/routes/ahs.py +417 -0
  65. package/python/foxclaw/api/routes/attachments.py +117 -0
  66. package/python/foxclaw/api/routes/browser.py +39 -0
  67. package/python/foxclaw/api/routes/domain.py +314 -0
  68. package/python/foxclaw/api/routes/knowledge.py +163 -0
  69. package/python/foxclaw/api/routes/llm_admin.py +461 -0
  70. package/python/foxclaw/api/routes/memory.py +89 -0
  71. package/python/foxclaw/api/routes/orchestration.py +41 -0
  72. package/python/foxclaw/api/routes/recovery.py +43 -0
  73. package/python/foxclaw/api/routes/review.py +275 -0
  74. package/python/foxclaw/api/routes/search.py +37 -0
  75. package/python/foxclaw/api/routes/system.py +327 -0
  76. package/python/foxclaw/api/routes/task.py +281 -0
  77. package/python/foxclaw/api/routes/ws.py +68 -0
  78. package/python/foxclaw/api/spine.py +55 -0
  79. package/python/foxclaw/browser/__init__.py +13 -0
  80. package/python/foxclaw/browser/action/__init__.py +1 -0
  81. package/python/foxclaw/browser/action/action_executor.py +54 -0
  82. package/python/foxclaw/browser/action/action_types.py +45 -0
  83. package/python/foxclaw/browser/action/action_verifier.py +68 -0
  84. package/python/foxclaw/browser/action/url_policy.py +147 -0
  85. package/python/foxclaw/browser/adapter/__init__.py +1 -0
  86. package/python/foxclaw/browser/adapter/base_adapter.py +23 -0
  87. package/python/foxclaw/browser/adapter/web_adapter.py +46 -0
  88. package/python/foxclaw/browser/driver/__init__.py +1 -0
  89. package/python/foxclaw/browser/driver/base_driver.py +61 -0
  90. package/python/foxclaw/browser/driver/playwright_driver.py +166 -0
  91. package/python/foxclaw/browser/perception/__init__.py +1 -0
  92. package/python/foxclaw/browser/perception/dom_extractor.py +43 -0
  93. package/python/foxclaw/browser/perception/element_indexer.py +57 -0
  94. package/python/foxclaw/browser/perception/semantic_reducer.py +33 -0
  95. package/python/foxclaw/browser/perception/snapshot.py +29 -0
  96. package/python/foxclaw/browser/service.py +148 -0
  97. package/python/foxclaw/browser/session/__init__.py +1 -0
  98. package/python/foxclaw/browser/session/session_manager.py +57 -0
  99. package/python/foxclaw/capability/__init__.py +12 -0
  100. package/python/foxclaw/capability/embedding_service.py +67 -0
  101. package/python/foxclaw/channels/__init__.py +140 -0
  102. package/python/foxclaw/channels/discord.py +184 -0
  103. package/python/foxclaw/channels/dispatcher.py +204 -0
  104. package/python/foxclaw/channels/feishu.py +225 -0
  105. package/python/foxclaw/channels/openclaw_bridge.py +156 -0
  106. package/python/foxclaw/channels/signing.py +187 -0
  107. package/python/foxclaw/channels/telegram.py +121 -0
  108. package/python/foxclaw/channels/weixin.py +225 -0
  109. package/python/foxclaw/cli.py +154 -0
  110. package/python/foxclaw/cognitive/__init__.py +4 -0
  111. package/python/foxclaw/cognitive/reasoner.py +82 -0
  112. package/python/foxclaw/cognitive/state.py +147 -0
  113. package/python/foxclaw/core/__init__.py +9 -0
  114. package/python/foxclaw/core/audit_chain.py +133 -0
  115. package/python/foxclaw/core/audit_sink.py +100 -0
  116. package/python/foxclaw/core/compaction.py +283 -0
  117. package/python/foxclaw/core/config.py +65 -0
  118. package/python/foxclaw/core/confirm_nonce.py +157 -0
  119. package/python/foxclaw/core/dispatch.py +27 -0
  120. package/python/foxclaw/core/envfile.py +48 -0
  121. package/python/foxclaw/core/failure_policy.py +151 -0
  122. package/python/foxclaw/core/gateway.py +85 -0
  123. package/python/foxclaw/core/governance_audit.py +175 -0
  124. package/python/foxclaw/core/idempotency.py +289 -0
  125. package/python/foxclaw/core/injection_guard.py +221 -0
  126. package/python/foxclaw/core/memory_adapter.py +516 -0
  127. package/python/foxclaw/core/metrics.py +89 -0
  128. package/python/foxclaw/core/model_router.py +1097 -0
  129. package/python/foxclaw/core/paths.py +103 -0
  130. package/python/foxclaw/core/recovery.py +362 -0
  131. package/python/foxclaw/core/scheduler.py +144 -0
  132. package/python/foxclaw/core/secrets.py +134 -0
  133. package/python/foxclaw/core/sessions.py +157 -0
  134. package/python/foxclaw/core/settings.py +430 -0
  135. package/python/foxclaw/core/shared_write.py +198 -0
  136. package/python/foxclaw/core/steering_queue.py +153 -0
  137. package/python/foxclaw/core/system_bus.py +84 -0
  138. package/python/foxclaw/core/thought_chain.py +318 -0
  139. package/python/foxclaw/core/token_meter.py +235 -0
  140. package/python/foxclaw/core/tools.py +172 -0
  141. package/python/foxclaw/core/tracing.py +390 -0
  142. package/python/foxclaw/core/truth_pipeline.py +114 -0
  143. package/python/foxclaw/core/vec_recall_adapter.py +503 -0
  144. package/python/foxclaw/core/verify.py +181 -0
  145. package/python/foxclaw/core/web_search.py +242 -0
  146. package/python/foxclaw/finance/__init__.py +15 -0
  147. package/python/foxclaw/finance/evidence.py +153 -0
  148. package/python/foxclaw/finance/fx.py +100 -0
  149. package/python/foxclaw/finance/graph.py +188 -0
  150. package/python/foxclaw/finance/models.py +150 -0
  151. package/python/foxclaw/finance/nlparse.py +575 -0
  152. package/python/foxclaw/finance/policy.py +329 -0
  153. package/python/foxclaw/finance/reasoner.py +129 -0
  154. package/python/foxclaw/finance/reasoning.py +340 -0
  155. package/python/foxclaw/finance/reconcile.py +180 -0
  156. package/python/foxclaw/finance/runtime.py +527 -0
  157. package/python/foxclaw/governance/__init__.py +24 -0
  158. package/python/foxclaw/governance/middleware.py +184 -0
  159. package/python/foxclaw/harness/__init__.py +17 -0
  160. package/python/foxclaw/harness/_base.py +72 -0
  161. package/python/foxclaw/harness/_policy.py +359 -0
  162. package/python/foxclaw/harness/drift.py +89 -0
  163. package/python/foxclaw/harness/event_bus.py +31 -0
  164. package/python/foxclaw/harness/harness.py +446 -0
  165. package/python/foxclaw/harness/loop_detection.py +152 -0
  166. package/python/foxclaw/itinerary/__init__.py +10 -0
  167. package/python/foxclaw/itinerary/amap_client.py +117 -0
  168. package/python/foxclaw/itinerary/models.py +80 -0
  169. package/python/foxclaw/itinerary/reasoner.py +101 -0
  170. package/python/foxclaw/itinerary/reasoning.py +174 -0
  171. package/python/foxclaw/itinerary/runtime.py +148 -0
  172. package/python/foxclaw/kernel/__init__.py +13 -0
  173. package/python/foxclaw/kernel/capabilities.py +73 -0
  174. package/python/foxclaw/kernel/policy_gate.py +114 -0
  175. package/python/foxclaw/kernel/process_manager.py +205 -0
  176. package/python/foxclaw/kernel/scheduler.py +131 -0
  177. package/python/foxclaw/kernel/sentinel.py +453 -0
  178. package/python/foxclaw/kernel/sentinel_client.py +246 -0
  179. package/python/foxclaw/kernel/spine.py +97 -0
  180. package/python/foxclaw/knowledge/__init__.py +376 -0
  181. package/python/foxclaw/knowledge/truth_crystallizer.py +92 -0
  182. package/python/foxclaw/learning/__init__.py +12 -0
  183. package/python/foxclaw/learning/knowledge_graph.py +149 -0
  184. package/python/foxclaw/learning/models.py +93 -0
  185. package/python/foxclaw/learning/reasoner.py +70 -0
  186. package/python/foxclaw/learning/runtime.py +363 -0
  187. package/python/foxclaw/learning/spaced_repetition.py +96 -0
  188. package/python/foxclaw/mcp_server.py +189 -0
  189. package/python/foxclaw/orchestration/__init__.py +184 -0
  190. package/python/foxclaw/pipeline/__init__.py +23 -0
  191. package/python/foxclaw/pipeline/cognitive_pipeline.py +481 -0
  192. package/python/foxclaw/pipeline/supervisor_stages.py +230 -0
  193. package/python/foxclaw/planning/__init__.py +17 -0
  194. package/python/foxclaw/planning/llm_planner.py +232 -0
  195. package/python/foxclaw/planning/planner.py +99 -0
  196. package/python/foxclaw/search/__init__.py +24 -0
  197. package/python/foxclaw/search/agent_port.py +194 -0
  198. package/python/foxclaw/search/evidence.py +113 -0
  199. package/python/foxclaw/search/external_search.py +50 -0
  200. package/python/foxclaw/search/hkc_read_port.py +60 -0
  201. package/python/foxclaw/search/internal_search.py +73 -0
  202. package/python/foxclaw/search/models.py +129 -0
  203. package/python/foxclaw/search/planner.py +44 -0
  204. package/python/foxclaw/search/retriever.py +39 -0
  205. package/python/foxclaw/search/runtime.py +134 -0
  206. package/python/foxclaw/search/strategy.py +44 -0
  207. package/python/foxclaw/skills/__init__.py +613 -0
  208. package/python/foxclaw/skills/hooks_spec.py +73 -0
  209. package/python/foxclaw/skills/lifecycle.py +278 -0
  210. package/python/foxclaw/skills/markdown_skill.py +174 -0
  211. package/python/foxclaw/skills/markdown_store.py +39 -0
  212. package/python/foxclaw/skills/mcp_client.py +190 -0
  213. package/python/foxclaw/skills/mcp_store.py +43 -0
  214. package/python/foxclaw/skills/permission.py +180 -0
  215. package/python/foxclaw/skills/pipeline.py +386 -0
  216. package/python/foxclaw/skills/policy.py +80 -0
  217. package/python/foxclaw/skills/review.py +72 -0
  218. package/python/foxclaw/skills/store.py +57 -0
  219. package/python/frontend/assets/core_bg.jpg +0 -0
  220. package/python/frontend/deck/auth.js +32 -0
  221. package/python/frontend/deck/chat_model_picker.js +537 -0
  222. package/python/frontend/deck/domain.js +296 -0
  223. package/python/frontend/deck/main.js +1950 -0
  224. package/python/frontend/deck/markdown.js +48 -0
  225. package/python/frontend/deck/settings.js +324 -0
  226. package/python/frontend/deck/state.js +6 -0
  227. package/python/frontend/deck/util.js +6 -0
  228. package/python/frontend/deck/visuals.js +212 -0
  229. package/python/frontend/deck.css +855 -0
  230. package/python/frontend/deck.html +686 -0
  231. package/python/frontend/deck_v2.html +341 -0
  232. package/python/frontend/planet/index.html +266 -0
  233. package/python/frontend/planet/planet.css +579 -0
  234. package/python/frontend/planet/planet.js +1637 -0
  235. package/python/frontend/planet/vendor/three.core.min.js +6 -0
  236. package/python/frontend/planet/vendor/three.module.min.js +6 -0
  237. package/python/frontend/v3/assets/index-BD7VPbMV.js +33 -0
  238. package/python/frontend/v3/assets/index-CAvnYWGH.css +1 -0
  239. package/python/frontend/v3/index.html +15 -0
  240. package/python/frontend/widget_runtime.js +295 -0
  241. package/python/frontend/widgets.js +696 -0
  242. package/python/frontend_src/index.html +14 -0
  243. package/python/frontend_src/package-lock.json +1376 -0
  244. package/python/frontend_src/package.json +19 -0
  245. package/python/frontend_src/src/App.vue +62 -0
  246. package/python/frontend_src/src/api.js +89 -0
  247. package/python/frontend_src/src/components/ChatDock.vue +377 -0
  248. package/python/frontend_src/src/components/ComposerMenu.vue +75 -0
  249. package/python/frontend_src/src/components/DocViewer.vue +171 -0
  250. package/python/frontend_src/src/components/Inspector.vue +241 -0
  251. package/python/frontend_src/src/components/LeftRail.vue +394 -0
  252. package/python/frontend_src/src/components/MenuPopover.vue +130 -0
  253. package/python/frontend_src/src/components/MessageStream.vue +401 -0
  254. package/python/frontend_src/src/components/ModelPicker.vue +355 -0
  255. package/python/frontend_src/src/components/QuickIngest.vue +118 -0
  256. package/python/frontend_src/src/components/Stage.vue +226 -0
  257. package/python/frontend_src/src/components/ToolMenu.vue +144 -0
  258. package/python/frontend_src/src/components/TopBar.vue +90 -0
  259. package/python/frontend_src/src/components/panels/ActivityPanel.vue +84 -0
  260. package/python/frontend_src/src/components/panels/AgentsPanel.vue +86 -0
  261. package/python/frontend_src/src/components/panels/KnowledgePanel.vue +441 -0
  262. package/python/frontend_src/src/components/panels/MemoryPanel.vue +118 -0
  263. package/python/frontend_src/src/components/panels/PanelBox.vue +51 -0
  264. package/python/frontend_src/src/components/panels/ReviewPanel.vue +308 -0
  265. package/python/frontend_src/src/components/panels/SettingsPanel.vue +254 -0
  266. package/python/frontend_src/src/components/panels/TasksPanel.vue +74 -0
  267. package/python/frontend_src/src/components/panels/ToolsPanel.vue +640 -0
  268. package/python/frontend_src/src/components/panels/UsagePanel.vue +105 -0
  269. package/python/frontend_src/src/lib/fmt.js +48 -0
  270. package/python/frontend_src/src/lib/md.js +204 -0
  271. package/python/frontend_src/src/main.js +7 -0
  272. package/python/frontend_src/src/stores/app.js +106 -0
  273. package/python/frontend_src/src/stores/chat.js +484 -0
  274. package/python/frontend_src/src/stores/model.js +104 -0
  275. package/python/frontend_src/src/stores/panels.js +330 -0
  276. package/python/frontend_src/src/styles/base.css +138 -0
  277. package/python/frontend_src/src/styles/panel.css +117 -0
  278. package/python/frontend_src/src/styles/tokens.css +78 -0
  279. package/python/frontend_src/vite.config.js +46 -0
  280. package/python/pyproject.toml +265 -0
  281. package/python/skills/README.md +123 -0
  282. package/python/skills/execution-contract/SKILL.md +30 -0
  283. package/python/skills/general-assistant/SKILL.md +34 -0
  284. package/python/skills/planner/SKILL.md +33 -0
  285. package/python/skills/schedule-itinerary/SKILL.md +35 -0
  286. package/python/skills/skill-composition/SKILL.md +31 -0
  287. package/python/skills/skill-guard/SKILL.md +67 -0
  288. package/python/skills/skill-memory/SKILL.md +50 -0
  289. package/python/skills/skill-router/SKILL.md +35 -0
  290. package/python/tests/conftest.py +76 -0
  291. package/python/tests/run_all.py +263 -0
  292. package/python/tests/test_agent_lifecycle.py +360 -0
  293. package/python/tests/test_ahs_p2.py +414 -0
  294. package/python/tests/test_anthropic_tools.py +206 -0
  295. package/python/tests/test_api.py +87 -0
  296. package/python/tests/test_api_auth.py +83 -0
  297. package/python/tests/test_api_host_guard.py +69 -0
  298. package/python/tests/test_attachments.py +167 -0
  299. package/python/tests/test_audit_chain.py +121 -0
  300. package/python/tests/test_audit_sink.py +136 -0
  301. package/python/tests/test_browser.py +153 -0
  302. package/python/tests/test_browser_request_guard.py +184 -0
  303. package/python/tests/test_capabilities.py +158 -0
  304. package/python/tests/test_channel_signing.py +192 -0
  305. package/python/tests/test_channel_trust.py +109 -0
  306. package/python/tests/test_channels.py +184 -0
  307. package/python/tests/test_cli.py +46 -0
  308. package/python/tests/test_compaction_toolpair.py +119 -0
  309. package/python/tests/test_config_layering.py +65 -0
  310. package/python/tests/test_confirm_nonce.py +197 -0
  311. package/python/tests/test_context_compaction_wiring.py +130 -0
  312. package/python/tests/test_dispatch.py +95 -0
  313. package/python/tests/test_domain_evals.py +403 -0
  314. package/python/tests/test_domain_import.py +78 -0
  315. package/python/tests/test_domain_reasoners.py +148 -0
  316. package/python/tests/test_e2e.py +178 -0
  317. package/python/tests/test_emb_cache.py +107 -0
  318. package/python/tests/test_embedding_singleton.py +89 -0
  319. package/python/tests/test_envfile.py +83 -0
  320. package/python/tests/test_evals_smoke.py +114 -0
  321. package/python/tests/test_event_spine.py +182 -0
  322. package/python/tests/test_evidence_semantic.py +101 -0
  323. package/python/tests/test_evidence_sse.py +97 -0
  324. package/python/tests/test_file_ingest.py +146 -0
  325. package/python/tests/test_finance.py +153 -0
  326. package/python/tests/test_finance_ledger.py +252 -0
  327. package/python/tests/test_finance_persist.py +178 -0
  328. package/python/tests/test_gateway.py +144 -0
  329. package/python/tests/test_governance.py +468 -0
  330. package/python/tests/test_governance_audit.py +359 -0
  331. package/python/tests/test_governance_middleware.py +143 -0
  332. package/python/tests/test_hkc.py +286 -0
  333. package/python/tests/test_hkc_index_build_manager.py +339 -0
  334. package/python/tests/test_hkc_manifest_reuse.py +317 -0
  335. package/python/tests/test_hkc_search_event_sync.py +533 -0
  336. package/python/tests/test_import_path_guard.py +161 -0
  337. package/python/tests/test_injection_guard.py +211 -0
  338. package/python/tests/test_integration.py +141 -0
  339. package/python/tests/test_itinerary.py +103 -0
  340. package/python/tests/test_kee_semantic.py +89 -0
  341. package/python/tests/test_kernel.py +222 -0
  342. package/python/tests/test_learning.py +110 -0
  343. package/python/tests/test_llm_admin.py +160 -0
  344. package/python/tests/test_llm_planner.py +366 -0
  345. package/python/tests/test_markdown_skills.py +73 -0
  346. package/python/tests/test_markdown_skills_openclaw.py +71 -0
  347. package/python/tests/test_mcp_connectors.py +109 -0
  348. package/python/tests/test_mcp_guardian_discovery.py +91 -0
  349. package/python/tests/test_mcp_no_memory.py +43 -0
  350. package/python/tests/test_mcp_policy_gate.py +77 -0
  351. package/python/tests/test_mcp_server.py +95 -0
  352. package/python/tests/test_memory_recall.py +95 -0
  353. package/python/tests/test_metrics.py +66 -0
  354. package/python/tests/test_multi_chain.py +102 -0
  355. package/python/tests/test_multimodal.py +135 -0
  356. package/python/tests/test_negotiation.py +146 -0
  357. package/python/tests/test_orchestration.py +119 -0
  358. package/python/tests/test_orchestration_decoupled.py +109 -0
  359. package/python/tests/test_p3_p5_hardening.py +338 -0
  360. package/python/tests/test_parallel_dispatch.py +215 -0
  361. package/python/tests/test_pipeline.py +533 -0
  362. package/python/tests/test_plan_visible.py +86 -0
  363. package/python/tests/test_planner.py +95 -0
  364. package/python/tests/test_policy_gate.py +109 -0
  365. package/python/tests/test_reasoner.py +185 -0
  366. package/python/tests/test_release_check.py +263 -0
  367. package/python/tests/test_resume_checkpoint.py +156 -0
  368. package/python/tests/test_routing.py +95 -0
  369. package/python/tests/test_scheduler.py +141 -0
  370. package/python/tests/test_search.py +204 -0
  371. package/python/tests/test_secrets.py +159 -0
  372. package/python/tests/test_semantic.py +78 -0
  373. package/python/tests/test_sessions.py +91 -0
  374. package/python/tests/test_settings.py +281 -0
  375. package/python/tests/test_shadow_learning.py +330 -0
  376. package/python/tests/test_shared_write.py +207 -0
  377. package/python/tests/test_skill_taint_gate.py +154 -0
  378. package/python/tests/test_skills.py +133 -0
  379. package/python/tests/test_stats_and_events.py +165 -0
  380. package/python/tests/test_streaming.py +60 -0
  381. package/python/tests/test_supervisor_pipeline.py +177 -0
  382. package/python/tests/test_supervisor_triage.py +108 -0
  383. package/python/tests/test_taint.py +117 -0
  384. package/python/tests/test_task_stream.py +85 -0
  385. package/python/tests/test_thinking_sse.py +67 -0
  386. package/python/tests/test_thinking_stream.py +76 -0
  387. package/python/tests/test_tool_center.py +129 -0
  388. package/python/tests/test_tool_parallel.py +113 -0
  389. package/python/tests/test_tool_params.py +103 -0
  390. package/python/tests/test_tool_use.py +242 -0
  391. package/python/tests/test_toplevel_concurrency.py +117 -0
  392. package/python/tests/test_tracing.py +290 -0
  393. package/python/tests/test_truth_crystallizer.py +127 -0
  394. package/python/tests/test_truth_flow.py +93 -0
  395. package/python/tests/test_url_policy.py +53 -0
  396. package/python/tests/test_verify_evidence.py +225 -0
  397. package/python/tests/test_version_sync.py +71 -0
  398. package/python/tests/test_web_trigger.py +99 -0
  399. package/python/tools/check_faiss_type.py +31 -0
  400. package/python/tools/check_log.ps1 +46 -0
  401. package/python/tools/ivf_status.py +72 -0
  402. package/python/tools/mypy_baseline.txt +5 -0
  403. package/python/tools/mypy_ratchet.py +97 -0
  404. package/python/tools/pack.py +178 -0
  405. package/python/tools/release_check.py +349 -0
  406. package/python/tools/restart_foxclaw.ps1 +74 -0
  407. package/python/tools/shared_write_audit.py +204 -0
  408. package/python/tools/size_baseline.txt +28 -0
  409. package/python/tools/size_check.py +102 -0
  410. package/python/tools/verify_env.bat +2 -0
  411. package/python/tools/verify_env.py +25 -0
  412. package/python/vendor/HMR_LICENSE +37 -0
  413. package/python/vendor/HMR_README.md +65 -0
  414. package/python/vendor/hkc/LICENSE +37 -0
  415. package/python/vendor/hkc/hkc_ace/__init__.py +0 -0
  416. package/python/vendor/hkc/hkc_ace/ace.py +457 -0
  417. package/python/vendor/hkc/hkc_ace/skill_taxonomy.json +465 -0
  418. package/python/vendor/hkc/hkc_api/__init__.py +0 -0
  419. package/python/vendor/hkc/hkc_api/container.py +416 -0
  420. package/python/vendor/hkc/hkc_api/ingest_jobs.py +103 -0
  421. package/python/vendor/hkc/hkc_api/main.py +157 -0
  422. package/python/vendor/hkc/hkc_api/routes/__init__.py +0 -0
  423. package/python/vendor/hkc/hkc_api/routes/ability.py +151 -0
  424. package/python/vendor/hkc/hkc_api/routes/config.py +51 -0
  425. package/python/vendor/hkc/hkc_api/routes/knowledge.py +630 -0
  426. package/python/vendor/hkc/hkc_api/routes/search.py +88 -0
  427. package/python/vendor/hkc/hkc_api/schemas.py +153 -0
  428. package/python/vendor/hkc/hkc_api/synthesis.py +179 -0
  429. package/python/vendor/hkc/hkc_core/__init__.py +0 -0
  430. package/python/vendor/hkc/hkc_core/graph/__init__.py +0 -0
  431. package/python/vendor/hkc/hkc_core/graph/base.py +188 -0
  432. package/python/vendor/hkc/hkc_core/graph/sqlite_store.py +789 -0
  433. package/python/vendor/hkc/hkc_core/models/__init__.py +0 -0
  434. package/python/vendor/hkc/hkc_core/models/enums.py +65 -0
  435. package/python/vendor/hkc/hkc_core/models/ku.py +307 -0
  436. package/python/vendor/hkc/hkc_core/utils/__init__.py +0 -0
  437. package/python/vendor/hkc/hkc_core/utils/id_gen.py +113 -0
  438. package/python/vendor/hkc/hkc_crystallizer/__init__.py +44 -0
  439. package/python/vendor/hkc/hkc_crystallizer/adapters.py +94 -0
  440. package/python/vendor/hkc/hkc_crystallizer/candidate.py +145 -0
  441. package/python/vendor/hkc/hkc_crystallizer/crystallizer.py +179 -0
  442. package/python/vendor/hkc/hkc_crystallizer/ingress.py +90 -0
  443. package/python/vendor/hkc/hkc_kde/__init__.py +0 -0
  444. package/python/vendor/hkc/hkc_kde/chunker.py +197 -0
  445. package/python/vendor/hkc/hkc_kde/extractor.py +623 -0
  446. package/python/vendor/hkc/hkc_kde/kde.py +298 -0
  447. package/python/vendor/hkc/hkc_kde/loaders/__init__.py +0 -0
  448. package/python/vendor/hkc/hkc_kde/loaders/ebook_loader.py +153 -0
  449. package/python/vendor/hkc/hkc_kde/loaders/office_loader.py +194 -0
  450. package/python/vendor/hkc/hkc_kde/loaders/pdf_loader.py +162 -0
  451. package/python/vendor/hkc/hkc_kde/loaders/text_loader.py +157 -0
  452. package/python/vendor/hkc/hkc_kde/loaders/web_loader.py +91 -0
  453. package/python/vendor/hkc/hkc_kde/models.py +74 -0
  454. package/python/vendor/hkc/hkc_kde/packager.py +329 -0
  455. package/python/vendor/hkc/hkc_kde/relation_disc.py +151 -0
  456. package/python/vendor/hkc/hkc_kee/__init__.py +0 -0
  457. package/python/vendor/hkc/hkc_kee/dedup.py +227 -0
  458. package/python/vendor/hkc/hkc_kee/fingerprint.py +106 -0
  459. package/python/vendor/hkc/hkc_kee/kee.py +541 -0
  460. package/python/vendor/hkc/hkc_kep/__init__.py +0 -0
  461. package/python/vendor/hkc/hkc_kep/event_bus.py +205 -0
  462. package/python/vendor/hkc/hkc_sdk/__init__.py +63 -0
  463. package/python/vendor/hkc/hkc_sdk/base.py +151 -0
  464. package/python/vendor/hkc/hkc_sdk/direct_client.py +153 -0
  465. package/python/vendor/hkc/hkc_sdk/exceptions.py +39 -0
  466. package/python/vendor/hkc/hkc_sdk/http_client.py +166 -0
  467. package/python/vendor/hkc/hkc_sdk/models.py +171 -0
  468. package/python/vendor/hkc/hkc_search/__init__.py +0 -0
  469. package/python/vendor/hkc/hkc_search/bm25.py +325 -0
  470. package/python/vendor/hkc/hkc_search/embedding_backends.py +374 -0
  471. package/python/vendor/hkc/hkc_search/graph_search.py +132 -0
  472. package/python/vendor/hkc/hkc_search/hybrid.py +331 -0
  473. package/python/vendor/hkc/hkc_search/index_build_manager.py +378 -0
  474. package/python/vendor/hkc/hkc_search/vector_search.py +484 -0
  475. package/python/vendor/hmr/__init__.py +0 -0
  476. package/python/vendor/hmr/core/__init__.py +0 -0
  477. package/python/vendor/hmr/core/hmr.py +684 -0
  478. package/python/vendor/hmr/core/models.py +310 -0
  479. package/python/vendor/hmr/engines/__init__.py +0 -0
  480. package/python/vendor/hmr/engines/evolution.py +710 -0
  481. package/python/vendor/hmr/engines/jit_compiler.py +472 -0
  482. package/python/vendor/hmr/engines/lifecycle.py +264 -0
  483. package/python/vendor/hmr/engines/policy.py +625 -0
  484. package/python/vendor/hmr/engines/recall.py +260 -0
  485. package/python/vendor/hmr/engines/runtime_state.py +158 -0
  486. package/python/vendor/hmr/engines/scheduler.py +323 -0
  487. package/python/vendor/hmr/engines/semantic.py +150 -0
  488. package/python/vendor/hmr/engines/temporal.py +261 -0
  489. package/python/vendor/hmr/engines/thought_chain.py +635 -0
  490. package/python/vendor/hmr/graph/__init__.py +0 -0
  491. package/python/vendor/hmr/graph/cwg.py +338 -0
  492. package/python/vendor/hmr/graph/memory_graph.py +530 -0
  493. package/python/vendor/hmr/storage/__init__.py +0 -0
  494. package/python/vendor/hmr/storage/memory_fs.py +300 -0
  495. package/python/vendor/hmr/storage/vector_store.py +520 -0
@@ -0,0 +1,189 @@
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
+ # 限流(env 可配):读工具较宽松、ask_foxclaw(驱动 LLM,贵)较紧
94
+ read_limiter = _RateLimiter(_env_int("FOXCLAW_MCP_RATE_READ", 120))
95
+ agent_limiter = _RateLimiter(_env_int("FOXCLAW_MCP_RATE_AGENT", 20))
96
+
97
+ @mcp.tool()
98
+ def knowledge_search(query: str, top_k: int = 5, domain: str = "") -> list:
99
+ """在个人知识库中检索相关知识条目(只读)。返回每条的 id/名称/摘要/类型。"""
100
+ if not read_limiter.allow():
101
+ return [{"error": "rate limited"}]
102
+ try:
103
+ hkc = getattr(os_getter(), "hkc", None)
104
+ if hkc is None or not getattr(hkc, "available", False):
105
+ return []
106
+ hits = hkc.client.search(_clean_query(query), mode="hybrid",
107
+ top_k=_clamp_top_k(top_k), domain=str(domain or "")[:120])
108
+ return [{"id": getattr(h, "ku_id", "") or getattr(h, "id", ""),
109
+ "name": getattr(h, "name", ""),
110
+ "summary": getattr(h, "summary", ""),
111
+ "type": str(getattr(h, "ku_type", ""))}
112
+ for h in (hits or [])]
113
+ except Exception:
114
+ return [{"error": "internal error"}] # 对外脱敏:不回传 str(e)
115
+
116
+ @mcp.tool()
117
+ def knowledge_get(ku_id: str) -> dict:
118
+ """按 id 取一个知识条目的详情(只读)。"""
119
+ if not read_limiter.allow():
120
+ return {"error": "rate limited"}
121
+ try:
122
+ hkc = getattr(os_getter(), "hkc", None)
123
+ if hkc is None or not getattr(hkc, "available", False):
124
+ return {"error": "knowledge base unavailable"}
125
+ ku = hkc.container.graph_store.get(str(ku_id or "")[:200])
126
+ if ku is None:
127
+ return {"error": "not_found"}
128
+ return {"id": getattr(ku, "ku_id", ku_id), "name": getattr(ku, "name", ""),
129
+ "summary": getattr(ku, "summary", ""), "type": str(getattr(ku, "ku_type", "")),
130
+ "domain": getattr(ku, "domain", ""), "tags": list(getattr(ku, "tags", []) or [])}
131
+ except Exception:
132
+ return {"error": "internal error"}
133
+
134
+ @mcp.tool()
135
+ def memory_recall(query: str, top_k: int = 5) -> list:
136
+ """在个人记忆中召回相关内容(只读;仅助手层+共享层)。"""
137
+ if not read_limiter.allow():
138
+ return [{"error": "rate limited"}]
139
+ try:
140
+ hmr = getattr(os_getter(), "hmr", None)
141
+ if hmr is None:
142
+ return []
143
+ res = hmr.recall(query=_clean_query(query), agent_id="supervisor", top_k=_clamp_top_k(top_k))
144
+ return [{"id": r.get("id", ""), "content": r.get("content", ""),
145
+ "type": r.get("type", "")} for r in (res or [])]
146
+ except Exception:
147
+ return [{"error": "internal error"}]
148
+
149
+ if expose_agents:
150
+ @mcp.tool()
151
+ async def ask_foxclaw(task: str, agent: str = "supervisor") -> dict:
152
+ """把一个任务交给 Foxclaw 的认知 Agent 处理(主管默认自动分诊到财务/行政/办公/助手)。
153
+ 返回 {reply, dispatched_to}。
154
+ ⚠ 非只读:执行过程会写入个人记忆(HMR)与审计链。有副作用的**技能**调用(写/发送/支付)
155
+ 会被内部权限门拦下需确认,MCP 无法确认→不执行;但记忆写入等内部副作用仍会发生。
156
+ 因此这是"写能力"工具,仅在 FOXCLAW_MCP_AGENTS=1 且鉴权下暴露。"""
157
+ if not agent_limiter.allow():
158
+ return {"error": "rate limited"}
159
+ import asyncio as _a
160
+ os_ = os_getter()
161
+ if os_ is None:
162
+ return {"error": "not ready"}
163
+ task = _clean_query(task)
164
+ agent = str(agent or "supervisor")[:40]
165
+ if not task.strip():
166
+ return {"error": "missing arguments"}
167
+ err = _policy_error(getattr(os_, "policy_gate", None),
168
+ getattr(getattr(os_, "skill_adapter", None), "rules", None),
169
+ task)
170
+ if err:
171
+ return {"error": err}
172
+
173
+ def _work():
174
+ from foxclaw.agents.base import _suppress_memory
175
+ _suppress_memory.set(True) # 外部驱动的任务不写 HMR(防污染),随上下文传到 run_task
176
+ return os_.scheduler.run(
177
+ lambda: _a.run(os_.run(task, agent)),
178
+ agent_id=agent, task=task)
179
+ try:
180
+ res = await _a.to_thread(_work)
181
+ except Exception:
182
+ return {"error": "internal error"}
183
+ if not isinstance(res, dict):
184
+ return {"reply": str(res), "dispatched_to": agent}
185
+ reply = (res.get("sub_result") or res.get("summary")
186
+ or res.get("reply") or res.get("error") or "")
187
+ return {"reply": str(reply), "dispatched_to": res.get("dispatched_to", agent)}
188
+
189
+ return mcp
@@ -0,0 +1,184 @@
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 = "" # 目标 Agent 的裁决说明
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 # CapabilityRegistry
62
+ # 【P1-3】统一治理审计(可空:standalone 无审计)。裁决事件原本**完全没有**——
63
+ # 跨 Agent 委派的批准/拒绝/转人工,事前无任何可观测记录。
64
+ self._auditor = auditor
65
+ # 【深审修复·长期运行泄漏】原为普通 list、只增不减且**全库从不读**——每次跨 Agent 请求
66
+ # append 一个 req → 无界增长。改有界 deque(保留最近 N 条供未来观测,符合本库 token_meter/
67
+ # HookManager 的有界日志惯例);deque.append 原子 ⇒ 并发路由也安全。
68
+ self.request_log: deque = deque(maxlen=1000)
69
+
70
+ @property
71
+ def agents(self) -> dict:
72
+ """按名字从注册中枢解析 agents(id -> agent 实例)。"""
73
+ return self._caps.get("agents") or {}
74
+
75
+ # ── 全局态势汇总(P3:通用聚合,不懂任何领域内部 API)────────────
76
+ def gather_situation(self) -> GlobalSituation:
77
+ """通用聚合各 Agent 自报的领域态势(agent.situation()),形成驾驶舱视图。
78
+ 编排层不再直接读 finance/learning/itinerary 内核——领域知识已归属各领域 Agent。"""
79
+ sit = GlobalSituation()
80
+ alerts = []
81
+ for agent in self.agents.values():
82
+ try:
83
+ s = agent.situation()
84
+ except Exception:
85
+ continue
86
+ if not s:
87
+ continue
88
+ # 一个 Agent 可以自报**多个**领域(拓扑 5→3 后,办公学习 Agent 同时覆盖
89
+ # learning 与 itinerary),故 `domains: {name: status}` 与旧的单 `domain` 都收。
90
+ doms = s.get("domains")
91
+ if isinstance(doms, dict) and doms:
92
+ for d, st in doms.items():
93
+ if d and hasattr(sit, d): # finance/learning/itinerary
94
+ setattr(sit, d, st or {})
95
+ else:
96
+ domain = s.get("domain")
97
+ if domain and hasattr(sit, domain):
98
+ setattr(sit, domain, s.get("status", {}) or {})
99
+ alerts += s.get("alerts", []) or []
100
+ sit.alerts = sorted(alerts,
101
+ key=lambda a: {"critical": 0, "warn": 1, "info": 2}
102
+ .get(a.get("severity"), 3))
103
+ return sit
104
+
105
+ # ── 跨 Agent 请求路由(P3:通用委托,由目标 Agent 用自己的守门人裁决)──────
106
+ def route_request(self, req: CrossAgentRequest) -> CrossAgentRequest:
107
+ """把请求路由给目标 Agent,由**目标 Agent**用自己的守门人裁决(编排层不懂领域)。"""
108
+ target = self.agents.get(req.to_agent)
109
+ if target is None:
110
+ # 【P1-0】原为 status="approved"(默认放行)—— 那是 fail-open:发起方会拿到
111
+ # "已批准",但**没有任何 Agent 真的处理过这个请求**。后果是实打实的,例如
112
+ # 行程 Agent 向未注册的 finance 申请差旅预算,会直接拿到批准并据此下单。
113
+ # 与下方「裁决异常 → 转人工确认」同属一条原则:裁决权不在编排层,凡未真正
114
+ # 裁决过的,都不能算通过。改为 needs_confirmation 同样"不崩",只是不再说谎。
115
+ req.status = "needs_confirmation"
116
+ req.verdict = f"目标 Agent '{req.to_agent}' 不存在,无法裁决,转人工确认"
117
+ else:
118
+ try:
119
+ target.handle_cross_request(req)
120
+ except Exception as e:
121
+ # 守门人裁决崩溃(如畸形 payload 让解析抛错)必须 **fail-safe**:转人工确认,
122
+ # 绝不自动放行——对守门人而言,裁决失败时放行=fail-open,是安全隐患。
123
+ req.status = "needs_confirmation"
124
+ req.verdict = f"裁决异常,转人工确认: {e}"
125
+ self.request_log.append(req)
126
+ self._audit_verdict(req)
127
+ return req
128
+
129
+ def _audit_verdict(self, req: CrossAgentRequest) -> None:
130
+ """【P1-3】把本次裁决记入统一治理审计流(governance.decision)。
131
+
132
+ correlation_id=req.id:negotiate 的多轮裁决共用同一 req.id ⇒ 协商链可 join;
133
+ 每次裁决的 decision_id 各自独立(由审计器生成)。审计失败绝不影响路由。"""
134
+ if self._auditor is None:
135
+ return
136
+ verdict = {"approved": "allow", "rejected": "deny",
137
+ "needs_confirmation": "confirm"}.get(req.status, req.status)
138
+ self._auditor.record(
139
+ layer="orchestration", actor=req.from_agent,
140
+ action=f"cross_request:{req.request_type}->{req.to_agent}",
141
+ verdict=verdict, reason=req.verdict,
142
+ detail={"status": req.status,
143
+ "negotiation_round": len(req.negotiation)},
144
+ correlation_id=req.id)
145
+
146
+ # ── 协商闭环:被拒 → 发起方让步 → 再裁决 ──────────────────────────
147
+ def negotiate(self, req: CrossAgentRequest, max_rounds: int = 3) -> CrossAgentRequest:
148
+ """跑协商协议。原先裁决是**终局**:财务说预算不足→rejected→到此为止,用户只拿到
149
+ 一句"被拒绝"。现在:发起方可拿着拒绝理由降标准重来(改高铁/换酒店/减一天)。
150
+
151
+ 职责边界(守 P3):**编排层只跑协议、不懂领域** ——
152
+ · 裁决权仍在**目标 Agent**的守门人(route_request 原样调用,不绕过)
153
+ · 让步逻辑仍在**发起 Agent**(agent.concede),编排层只问"你能让吗"
154
+
155
+ 安全不变量:
156
+ · **有界**:最多 max_rounds 轮,防死循环
157
+ · **必须真让步**:payload 没变化就停(否则无限重复同一请求)
158
+ · **协商失败维持 rejected,绝不降级成 needs_confirmation** ——
159
+ 那等于给用户开一个点穿守门人的口子。协商只能"找到守门人愿意批的方案",
160
+ 不能"把不批变成可以批"。
161
+ """
162
+ for rnd in range(1, max(1, max_rounds) + 1):
163
+ self.route_request(req)
164
+ req.negotiation.append({"round": rnd, "status": req.status,
165
+ "verdict": req.verdict,
166
+ "payload": dict(req.payload)})
167
+ if req.status != "rejected":
168
+ break # 通过/待确认 → 协商结束
169
+ requester = self.agents.get(req.from_agent)
170
+ if requester is None:
171
+ break
172
+ try:
173
+ revised = requester.concede(req, req.verdict) # 问发起方:能让吗?
174
+ except Exception as e:
175
+ req.negotiation.append({"round": rnd, "concede_error": str(e)[:120]})
176
+ break # 让步逻辑出错 → 停,维持原裁决
177
+ if not revised or revised == req.payload:
178
+ break # 让无可让 / 无实质变化 → 停
179
+ req.negotiation[-1]["conceded_to"] = dict(revised)
180
+ req.payload = revised
181
+ return req
182
+
183
+
184
+ __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
+ ]