foxclaw 0.1.2 → 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 (344) hide show
  1. package/index.js +1 -8
  2. package/package.json +1 -1
  3. package/python/.env.example +8 -0
  4. package/python/.github/workflows/ci.yml +47 -2
  5. package/python/CHANGELOG.md +57 -0
  6. package/python/CHANGELOG_CN.md +57 -0
  7. package/python/README.md +11 -2
  8. package/python/README_CN.md +32 -20
  9. package/python/bench/bm25_audit.py +1 -14
  10. package/python/bench/bm25_regression.py +0 -1
  11. package/python/bench/bm25_stress.py +0 -4
  12. package/python/bench/cd_audit.py +0 -23
  13. package/python/bench/chaos.py +6 -8
  14. package/python/bench/e2e_real_models.py +0 -1
  15. package/python/bench/llm_retry_test.py +2 -9
  16. package/python/bench/load_test.py +5 -8
  17. package/python/bench/vector_ivf_upgrade.py +1 -7
  18. package/python/docs/RELEASE.md +4 -3
  19. package/python/docs/RUN_CN.md +2 -2
  20. package/python/evals/governance/cases.py +0 -37
  21. package/python/evals/governance/run_evals.py +5 -37
  22. package/python/examples/governance_middleware_demo.py +1 -6
  23. package/python/examples/langgraph_governance_demo.py +7 -11
  24. package/python/foxclaw/__init__.py +34 -152
  25. package/python/foxclaw/agents/base.py +65 -136
  26. package/python/foxclaw/agents/impl/_helpers.py +2 -10
  27. package/python/foxclaw/agents/impl/finance.py +4 -21
  28. package/python/foxclaw/agents/impl/office.py +7 -24
  29. package/python/foxclaw/agents/impl/supervisor.py +12 -79
  30. package/python/foxclaw/agents/mixins/_context.py +0 -23
  31. package/python/foxclaw/agents/mixins/augment.py +10 -16
  32. package/python/foxclaw/agents/mixins/llm.py +7 -6
  33. package/python/foxclaw/agents/mixins/memory.py +7 -1
  34. package/python/foxclaw/agents/mixins/preference.py +58 -8
  35. package/python/foxclaw/agents/mixins/prompt.py +0 -6
  36. package/python/foxclaw/agents/mixins/skillbox.py +6 -6
  37. package/python/foxclaw/agents/mixins/tool_exec.py +16 -42
  38. package/python/foxclaw/api/__init__.py +18 -77
  39. package/python/foxclaw/api/_state.py +43 -0
  40. package/python/foxclaw/api/{app.py → bootstrap.py} +5 -32
  41. package/python/foxclaw/api/middleware.py +5 -28
  42. package/python/foxclaw/api/routes/ahs.py +60 -73
  43. package/python/foxclaw/api/routes/attachments.py +5 -6
  44. package/python/foxclaw/api/routes/browser.py +7 -11
  45. package/python/foxclaw/api/routes/domain.py +18 -26
  46. package/python/foxclaw/api/routes/knowledge.py +3 -21
  47. package/python/foxclaw/api/routes/llm_admin.py +21 -33
  48. package/python/foxclaw/api/routes/memory.py +28 -12
  49. package/python/foxclaw/api/routes/orchestration.py +3 -5
  50. package/python/foxclaw/api/routes/recovery.py +4 -3
  51. package/python/foxclaw/api/routes/review.py +2 -11
  52. package/python/foxclaw/api/routes/search.py +3 -7
  53. package/python/foxclaw/api/routes/system.py +33 -49
  54. package/python/foxclaw/api/routes/task.py +31 -49
  55. package/python/foxclaw/api/routes/ws.py +2 -6
  56. package/python/foxclaw/api/spine.py +1 -4
  57. package/python/foxclaw/browser/action/action_executor.py +1 -5
  58. package/python/foxclaw/browser/action/action_types.py +0 -1
  59. package/python/foxclaw/browser/action/url_policy.py +1 -5
  60. package/python/foxclaw/browser/adapter/web_adapter.py +1 -1
  61. package/python/foxclaw/browser/driver/playwright_driver.py +20 -16
  62. package/python/foxclaw/browser/perception/dom_extractor.py +0 -1
  63. package/python/foxclaw/browser/perception/element_indexer.py +1 -2
  64. package/python/foxclaw/browser/service.py +7 -11
  65. package/python/foxclaw/capability/embedding_service.py +4 -6
  66. package/python/foxclaw/channels/__init__.py +9 -11
  67. package/python/foxclaw/channels/discord.py +4 -13
  68. package/python/foxclaw/channels/dispatcher.py +2 -27
  69. package/python/foxclaw/channels/feishu.py +10 -20
  70. package/python/foxclaw/channels/openclaw_bridge.py +4 -9
  71. package/python/foxclaw/channels/signing.py +9 -9
  72. package/python/foxclaw/channels/telegram.py +3 -4
  73. package/python/foxclaw/channels/weixin.py +7 -17
  74. package/python/foxclaw/cli.py +42 -13
  75. package/python/foxclaw/cognitive/reasoner.py +9 -10
  76. package/python/foxclaw/cognitive/state.py +18 -24
  77. package/python/foxclaw/core/audit_chain.py +1 -3
  78. package/python/foxclaw/core/audit_sink.py +3 -3
  79. package/python/foxclaw/core/compaction.py +8 -26
  80. package/python/foxclaw/core/config.py +2 -2
  81. package/python/foxclaw/core/confirm_nonce.py +6 -12
  82. package/python/foxclaw/core/envfile.py +2 -2
  83. package/python/foxclaw/core/failure_policy.py +17 -26
  84. package/python/foxclaw/core/gateway.py +6 -10
  85. package/python/foxclaw/core/governance_audit.py +3 -7
  86. package/python/foxclaw/core/idempotency.py +15 -22
  87. package/python/foxclaw/core/injection_guard.py +10 -29
  88. package/python/foxclaw/core/memory_adapter.py +30 -57
  89. package/python/foxclaw/core/metrics.py +4 -5
  90. package/python/foxclaw/core/model_router.py +36 -148
  91. package/python/foxclaw/core/paths.py +1 -1
  92. package/python/foxclaw/core/recovery.py +21 -34
  93. package/python/foxclaw/core/scheduler.py +23 -26
  94. package/python/foxclaw/core/secrets.py +0 -3
  95. package/python/foxclaw/core/sessions.py +4 -6
  96. package/python/foxclaw/core/settings.py +16 -32
  97. package/python/foxclaw/core/shared_write.py +11 -23
  98. package/python/foxclaw/core/steering_queue.py +12 -11
  99. package/python/foxclaw/core/system_bus.py +1 -1
  100. package/python/foxclaw/core/thought_chain.py +30 -32
  101. package/python/foxclaw/core/token_meter.py +9 -13
  102. package/python/foxclaw/core/tools.py +4 -12
  103. package/python/foxclaw/core/tracing.py +25 -40
  104. package/python/foxclaw/core/truth_pipeline.py +5 -9
  105. package/python/foxclaw/core/update_check.py +149 -0
  106. package/python/foxclaw/core/vec_recall_adapter.py +11 -37
  107. package/python/foxclaw/core/verify.py +6 -14
  108. package/python/foxclaw/core/web_search.py +4 -28
  109. package/python/foxclaw/finance/evidence.py +14 -18
  110. package/python/foxclaw/finance/fx.py +1 -3
  111. package/python/foxclaw/finance/graph.py +9 -18
  112. package/python/foxclaw/finance/models.py +20 -24
  113. package/python/foxclaw/finance/nlparse.py +12 -72
  114. package/python/foxclaw/finance/policy.py +8 -36
  115. package/python/foxclaw/finance/reasoner.py +3 -11
  116. package/python/foxclaw/finance/reasoning.py +13 -34
  117. package/python/foxclaw/finance/reconcile.py +5 -10
  118. package/python/foxclaw/finance/runtime.py +19 -46
  119. package/python/foxclaw/governance/middleware.py +13 -21
  120. package/python/foxclaw/harness/_base.py +2 -8
  121. package/python/foxclaw/harness/_policy.py +19 -38
  122. package/python/foxclaw/harness/drift.py +1 -13
  123. package/python/foxclaw/harness/event_bus.py +2 -5
  124. package/python/foxclaw/harness/harness.py +9 -97
  125. package/python/foxclaw/harness/loop_detection.py +11 -18
  126. package/python/foxclaw/itinerary/amap_client.py +2 -5
  127. package/python/foxclaw/itinerary/models.py +7 -7
  128. package/python/foxclaw/itinerary/reasoner.py +1 -6
  129. package/python/foxclaw/itinerary/reasoning.py +15 -21
  130. package/python/foxclaw/itinerary/runtime.py +8 -12
  131. package/python/foxclaw/kernel/capabilities.py +5 -6
  132. package/python/foxclaw/kernel/policy_gate.py +4 -10
  133. package/python/foxclaw/kernel/process_manager.py +17 -36
  134. package/python/foxclaw/kernel/scheduler.py +3 -9
  135. package/python/foxclaw/kernel/sentinel.py +13 -23
  136. package/python/foxclaw/kernel/sentinel_client.py +12 -15
  137. package/python/foxclaw/kernel/spine.py +9 -9
  138. package/python/foxclaw/knowledge/__init__.py +4 -20
  139. package/python/foxclaw/knowledge/truth_crystallizer.py +5 -8
  140. package/python/foxclaw/learning/knowledge_graph.py +4 -9
  141. package/python/foxclaw/learning/models.py +13 -13
  142. package/python/foxclaw/learning/reasoner.py +1 -4
  143. package/python/foxclaw/learning/runtime.py +19 -31
  144. package/python/foxclaw/learning/spaced_repetition.py +2 -7
  145. package/python/foxclaw/mcp_server.py +2 -3
  146. package/python/foxclaw/orchestration/__init__.py +16 -33
  147. package/python/foxclaw/pipeline/cognitive_pipeline.py +45 -97
  148. package/python/foxclaw/pipeline/stage_meta.py +21 -0
  149. package/python/foxclaw/pipeline/supervisor_stages.py +13 -42
  150. package/python/foxclaw/planning/llm_planner.py +3 -3
  151. package/python/foxclaw/planning/planner.py +6 -11
  152. package/python/foxclaw/search/agent_port.py +7 -11
  153. package/python/foxclaw/search/evidence.py +0 -4
  154. package/python/foxclaw/search/internal_search.py +3 -5
  155. package/python/foxclaw/search/models.py +11 -13
  156. package/python/foxclaw/search/planner.py +3 -3
  157. package/python/foxclaw/search/retriever.py +1 -1
  158. package/python/foxclaw/search/runtime.py +1 -8
  159. package/python/foxclaw/skills/__init__.py +46 -100
  160. package/python/foxclaw/skills/archive.py +111 -0
  161. package/python/foxclaw/skills/fetch.py +226 -0
  162. package/python/foxclaw/skills/hooks_spec.py +21 -25
  163. package/python/foxclaw/skills/install_cmd.py +216 -0
  164. package/python/foxclaw/skills/lifecycle.py +16 -42
  165. package/python/foxclaw/skills/markdown_skill.py +10 -9
  166. package/python/foxclaw/skills/mcp_client.py +5 -11
  167. package/python/foxclaw/skills/mcp_store.py +0 -3
  168. package/python/foxclaw/skills/permission.py +13 -48
  169. package/python/foxclaw/skills/pipeline.py +21 -53
  170. package/python/foxclaw/skills/policy.py +4 -10
  171. package/python/foxclaw/skills/review.py +3 -3
  172. package/python/frontend/deck/auth.js +0 -7
  173. package/python/frontend/deck/chat_model_picker.js +0 -54
  174. package/python/frontend/deck/domain.js +0 -20
  175. package/python/frontend/deck/main.js +0 -144
  176. package/python/frontend/deck/markdown.js +0 -4
  177. package/python/frontend/deck/settings.js +1 -18
  178. package/python/frontend/deck/state.js +0 -1
  179. package/python/frontend/deck/util.js +0 -2
  180. package/python/frontend/deck/visuals.js +0 -22
  181. package/python/frontend/planet/planet.js +0 -201
  182. package/python/frontend/v3/assets/index-CQSBdxk4.js +34 -0
  183. package/python/frontend/v3/assets/index-D5nIsjms.css +1 -0
  184. package/python/frontend/v3/index.html +3 -3
  185. package/python/frontend/widget_runtime.js +0 -43
  186. package/python/frontend/widgets.js +0 -57
  187. package/python/frontend_src/index.html +1 -1
  188. package/python/frontend_src/package-lock.json +2 -2
  189. package/python/frontend_src/package.json +1 -1
  190. package/python/frontend_src/src/App.vue +3 -7
  191. package/python/frontend_src/src/api.js +0 -7
  192. package/python/frontend_src/src/components/ChatDock.vue +27 -39
  193. package/python/frontend_src/src/components/DocViewer.vue +0 -9
  194. package/python/frontend_src/src/components/Inspector.vue +0 -6
  195. package/python/frontend_src/src/components/LeftRail.vue +60 -39
  196. package/python/frontend_src/src/components/MenuPopover.vue +9 -11
  197. package/python/frontend_src/src/components/MessageStream.vue +18 -30
  198. package/python/frontend_src/src/components/ModelPicker.vue +0 -5
  199. package/python/frontend_src/src/components/QuickIngest.vue +0 -6
  200. package/python/frontend_src/src/components/Stage.vue +71 -25
  201. package/python/frontend_src/src/components/ToolMenu.vue +0 -3
  202. package/python/frontend_src/src/components/TopBar.vue +38 -10
  203. package/python/frontend_src/src/components/panels/ActivityPanel.vue +0 -2
  204. package/python/frontend_src/src/components/panels/AgentsPanel.vue +2 -5
  205. package/python/frontend_src/src/components/panels/KnowledgePanel.vue +0 -16
  206. package/python/frontend_src/src/components/panels/MemoryPanel.vue +0 -1
  207. package/python/frontend_src/src/components/panels/PanelBox.vue +6 -7
  208. package/python/frontend_src/src/components/panels/ReviewPanel.vue +0 -16
  209. package/python/frontend_src/src/components/panels/SettingsPanel.vue +142 -38
  210. package/python/frontend_src/src/components/panels/TasksPanel.vue +0 -1
  211. package/python/frontend_src/src/components/panels/ToolsPanel.vue +125 -19
  212. package/python/frontend_src/src/lib/fmt.js +0 -4
  213. package/python/frontend_src/src/lib/i18n.js +53 -0
  214. package/python/frontend_src/src/lib/locales/en.js +173 -0
  215. package/python/frontend_src/src/lib/locales/zh.js +174 -0
  216. package/python/frontend_src/src/lib/md.js +0 -14
  217. package/python/frontend_src/src/main.js +2 -0
  218. package/python/frontend_src/src/stores/app.js +17 -32
  219. package/python/frontend_src/src/stores/chat.js +0 -34
  220. package/python/frontend_src/src/stores/model.js +0 -8
  221. package/python/frontend_src/src/stores/panels.js +0 -41
  222. package/python/frontend_src/src/stores/profile.js +31 -0
  223. package/python/frontend_src/src/stores/system.js +25 -0
  224. package/python/frontend_src/vite.config.js +0 -7
  225. package/python/pyproject.toml +6 -3
  226. package/python/requirements.txt +311 -0
  227. package/python/tests/conftest.py +0 -22
  228. package/python/tests/run_all.py +3 -66
  229. package/python/tests/test_agent_lifecycle.py +4 -38
  230. package/python/tests/test_ahs_p2.py +4 -25
  231. package/python/tests/test_anthropic_tools.py +1 -15
  232. package/python/tests/test_api.py +19 -9
  233. package/python/tests/test_api_auth.py +1 -5
  234. package/python/tests/test_attachments.py +9 -25
  235. package/python/tests/test_audit_chain.py +2 -7
  236. package/python/tests/test_audit_sink.py +0 -6
  237. package/python/tests/test_browser.py +2 -6
  238. package/python/tests/test_browser_request_guard.py +0 -4
  239. package/python/tests/test_capabilities.py +0 -6
  240. package/python/tests/test_channel_signing.py +2 -9
  241. package/python/tests/test_channels.py +1 -11
  242. package/python/tests/test_config_layering.py +0 -4
  243. package/python/tests/test_confirm_nonce.py +1 -12
  244. package/python/tests/test_context_compaction_wiring.py +1 -7
  245. package/python/tests/test_dispatch.py +0 -6
  246. package/python/tests/test_domain_evals.py +11 -80
  247. package/python/tests/test_domain_import.py +0 -6
  248. package/python/tests/test_domain_reasoners.py +0 -14
  249. package/python/tests/test_e2e.py +0 -17
  250. package/python/tests/test_emb_cache.py +7 -17
  251. package/python/tests/test_embedding_singleton.py +1 -3
  252. package/python/tests/test_evals_smoke.py +0 -24
  253. package/python/tests/test_event_spine.py +10 -18
  254. package/python/tests/test_evidence_semantic.py +1 -14
  255. package/python/tests/test_evidence_sse.py +3 -6
  256. package/python/tests/test_file_ingest.py +0 -11
  257. package/python/tests/test_finance.py +0 -9
  258. package/python/tests/test_finance_ledger.py +5 -12
  259. package/python/tests/test_finance_persist.py +8 -12
  260. package/python/tests/test_gateway.py +3 -22
  261. package/python/tests/test_governance.py +5 -52
  262. package/python/tests/test_governance_audit.py +4 -39
  263. package/python/tests/test_governance_middleware.py +1 -7
  264. package/python/tests/test_hkc.py +6 -24
  265. package/python/tests/test_hkc_index_build_manager.py +0 -33
  266. package/python/tests/test_hkc_manifest_reuse.py +4 -27
  267. package/python/tests/test_hkc_search_event_sync.py +3 -54
  268. package/python/tests/test_import_path_guard.py +0 -6
  269. package/python/tests/test_injection_guard.py +18 -36
  270. package/python/tests/test_integration.py +5 -29
  271. package/python/tests/test_itinerary.py +0 -3
  272. package/python/tests/test_kee_semantic.py +0 -5
  273. package/python/tests/test_kernel.py +1 -11
  274. package/python/tests/test_learning.py +3 -12
  275. package/python/tests/test_llm_admin.py +4 -19
  276. package/python/tests/test_llm_planner.py +1 -23
  277. package/python/tests/test_markdown_skills.py +3 -14
  278. package/python/tests/test_markdown_skills_openclaw.py +4 -9
  279. package/python/tests/test_mcp_connectors.py +3 -15
  280. package/python/tests/test_mcp_guardian_discovery.py +2 -6
  281. package/python/tests/test_mcp_no_memory.py +3 -3
  282. package/python/tests/test_mcp_policy_gate.py +0 -1
  283. package/python/tests/test_mcp_server.py +6 -9
  284. package/python/tests/test_memory_recall.py +1 -4
  285. package/python/tests/test_metrics.py +0 -8
  286. package/python/tests/test_multi_chain.py +1 -13
  287. package/python/tests/test_multimodal.py +5 -18
  288. package/python/tests/test_negotiation.py +3 -13
  289. package/python/tests/test_orchestration.py +1 -11
  290. package/python/tests/test_orchestration_decoupled.py +0 -6
  291. package/python/tests/test_p3_p5_hardening.py +3 -17
  292. package/python/tests/test_parallel_dispatch.py +4 -12
  293. package/python/tests/test_pipeline.py +17 -58
  294. package/python/tests/test_plan_visible.py +4 -4
  295. package/python/tests/test_planner.py +0 -7
  296. package/python/tests/test_policy_gate.py +2 -3
  297. package/python/tests/test_profile.py +67 -0
  298. package/python/tests/test_readme_claims.py +163 -0
  299. package/python/tests/test_reasoner.py +2 -17
  300. package/python/tests/test_release_check.py +2 -13
  301. package/python/tests/test_resume_checkpoint.py +7 -15
  302. package/python/tests/test_routing.py +2 -12
  303. package/python/tests/test_scheduler.py +7 -10
  304. package/python/tests/test_search.py +1 -11
  305. package/python/tests/test_secrets.py +4 -10
  306. package/python/tests/test_semantic.py +1 -10
  307. package/python/tests/test_sessions.py +3 -4
  308. package/python/tests/test_settings.py +2 -18
  309. package/python/tests/test_shadow_learning.py +3 -43
  310. package/python/tests/test_shared_write.py +0 -7
  311. package/python/tests/test_skill_taint_gate.py +1 -10
  312. package/python/tests/test_skills.py +2 -18
  313. package/python/tests/test_skills_fetch.py +282 -0
  314. package/python/tests/test_stats_and_events.py +2 -5
  315. package/python/tests/test_streaming.py +2 -7
  316. package/python/tests/test_supervisor_pipeline.py +4 -5
  317. package/python/tests/test_supervisor_triage.py +4 -10
  318. package/python/tests/test_taint.py +2 -7
  319. package/python/tests/test_task_stream.py +1 -12
  320. package/python/tests/test_thinking_sse.py +0 -1
  321. package/python/tests/test_tool_center.py +1 -17
  322. package/python/tests/test_tool_parallel.py +2 -2
  323. package/python/tests/test_tool_params.py +1 -5
  324. package/python/tests/test_tool_use.py +0 -13
  325. package/python/tests/test_toplevel_concurrency.py +3 -4
  326. package/python/tests/test_tracing.py +7 -41
  327. package/python/tests/test_truth_crystallizer.py +4 -6
  328. package/python/tests/test_truth_flow.py +2 -7
  329. package/python/tests/test_update_check.py +95 -0
  330. package/python/tests/test_verify_evidence.py +8 -21
  331. package/python/tests/test_version_sync.py +35 -6
  332. package/python/tests/test_web_trigger.py +0 -17
  333. package/python/tools/check_faiss_type.py +0 -1
  334. package/python/tools/check_log.ps1 +0 -8
  335. package/python/tools/mypy_baseline.txt +12 -4
  336. package/python/tools/mypy_ratchet.py +52 -6
  337. package/python/tools/pack.py +0 -5
  338. package/python/tools/release_check.py +7 -7
  339. package/python/tools/restart_foxclaw.ps1 +5 -8
  340. package/python/tools/shared_write_audit.py +1 -15
  341. package/python/tools/size_check.py +1 -1
  342. package/python/tools/strip_comments.py +228 -0
  343. package/python/frontend/v3/assets/index-BD7VPbMV.js +0 -33
  344. package/python/frontend/v3/assets/index-CAvnYWGH.css +0 -1
@@ -5,7 +5,6 @@ from datetime import datetime
5
5
  from ..base import CognitiveAgent
6
6
  from ._helpers import _fmt_mem
7
7
 
8
- # 分析/建议类意图:即使这句话里带了金额,也要走 LLM 给结论,而不是只回一张记账回执
9
8
  _ADVICE_WORDS = ("分析", "建议", "怎么", "如何", "多少", "预算", "超支", "优化", "规划",
10
9
  "趋势", "为什么", "要不要", "合算", "划算", "省", "控制", "总结",
11
10
  "报告", "够不够", "对比", "排名", "异常", "提醒", "合理", "占比",
@@ -39,7 +38,6 @@ def _should_autorecord(task: str) -> bool:
39
38
  return False
40
39
  if settled:
41
40
  return True
42
- # 没有完成态动词时,只有「金额 + 明确消费类目」这种报备才记(洗车 100)
43
41
  return not _wants_advice(t)
44
42
 
45
43
 
@@ -52,7 +50,6 @@ class FinanceAgent(CognitiveAgent):
52
50
  """挂载财务内核(FinanceRuntime):记忆+推理+Policy 守门人。"""
53
51
  self.fin = runtime
54
52
 
55
- # ── 编排协作(P3:财务领域态势 + 预算守门人,从编排层下沉至此)──────
56
53
  def situation(self) -> dict:
57
54
  rt = getattr(self, "fin", None)
58
55
  if rt is None:
@@ -85,8 +82,6 @@ class FinanceAgent(CognitiveAgent):
85
82
  entity=req.payload.get("entity", "差旅"),
86
83
  account=req.payload.get("account", "default"))
87
84
  bal = rt.account_balances.get(txn.account)
88
- # Step3:模糊情形的 LLM 裁决(默认关;FOXCLAW_FINANCE_LLM_ARBITER=1 才启用)。
89
- # 安全不变量:LLM 只在规则已批准的模糊带被咨询、只能 approve→confirm、出错→confirm、永不 reject。
90
85
  import os as _os
91
86
  _llm_on = _os.environ.get("FOXCLAW_FINANCE_LLM_ARBITER", "") == "1"
92
87
  rv = FinanceGatekeeperReasoner(
@@ -95,7 +90,6 @@ class FinanceAgent(CognitiveAgent):
95
90
  req.status = {"approve": "approved", "confirm": "needs_confirmation",
96
91
  "reject": "rejected"}[rv.decision]
97
92
  req.verdict = rv.rationale
98
- # 附证据链(可审计;新增字段,鸭子类型透传,不改既有行为)
99
93
  try:
100
94
  req.evidence = [e.to_dict() for e in rv.evidence]
101
95
  req.reason_source = rv.source
@@ -114,7 +108,7 @@ class FinanceAgent(CognitiveAgent):
114
108
  try:
115
109
  rec = fin.record_text(task)
116
110
  except Exception:
117
- return "" # 记账失败不阻断对话,退回普通财务回答
111
+ return ""
118
112
  if not rec.get("recorded"):
119
113
  return ""
120
114
  ack = rec.get("ack") or ""
@@ -128,16 +122,15 @@ class FinanceAgent(CognitiveAgent):
128
122
  tags=["finance", "ledger"],
129
123
  )
130
124
  except Exception:
131
- pass # 记忆写入失败不影响账已经记上这件事
125
+ pass
132
126
  return ack
133
127
 
134
- def analyze_finances(self, period: str = None, current_balance: float = None) -> dict:
128
+ def analyze_finances(self, period: str | None = None, current_balance: float | None = None) -> dict:
135
129
  """调用财务内核做综合分析,并把关键洞察写入 ThoughtChain。"""
136
130
  fin = getattr(self, "fin", None)
137
131
  if fin is None:
138
132
  return {"error": "财务内核未挂载"}
139
133
  report = fin.analyze(period=period, current_balance=current_balance)
140
- # 把守门人裁决写进推理链(受 Harness 监督)
141
134
  verdicts = report.get("gatekeeper_verdicts", [])
142
135
  self._think(
143
136
  content=f"财务分析完成:{report['transaction_count']} 笔交易,"
@@ -168,9 +161,6 @@ class FinanceAgent(CognitiveAgent):
168
161
  _recall_query = lambda self, task: f"财务 {task}"
169
162
 
170
163
  async def _execute(self, task, context, session_id, memories):
171
- # 第一线:零星花钱报备(「今天买了一件衣服花了 105 块」)。
172
- # 解析→记账→回执全走确定性规则,不让 LLM 碰金额;回执本身就是答案,
173
- # 没有分析意图时不必再等一次 LLM 往返。
174
164
  ledger_ack = self._auto_record(task)
175
165
  if ledger_ack and not _wants_advice(task):
176
166
  return ledger_ack, {}
@@ -181,14 +171,12 @@ class FinanceAgent(CognitiveAgent):
181
171
  conf=0.93,
182
172
  )
183
173
 
184
- # 构建财务上下文
185
174
  finance_context = ""
186
175
  if memories:
187
176
  finance_context = "历史财务记录:\n" + "\n".join([
188
177
  _fmt_mem(m, 100) for m in memories[:5]
189
178
  ])
190
179
 
191
- # 接入财务内核:若已挂载且有交易数据,做真实结构化分析,作为 LLM 的依据
192
180
  kernel_insight = ""
193
181
  fin = getattr(self, "fin", None)
194
182
  if fin is not None and getattr(fin, "transactions", None):
@@ -205,20 +193,16 @@ class FinanceAgent(CognitiveAgent):
205
193
  if anomalies:
206
194
  parts.append(f"· 检测到 {len(anomalies)} 笔异常支出")
207
195
  if parts:
208
- # 注:标签用产品级措辞(不带"内核"等内部实现词),避免 LLM 回复把内部
209
- # 架构词汇透给终端用户(深层实现对用户保密;多 Agent 作卖点可讲)。
210
196
  kernel_insight = ("\n\n【财务分析结果(基于真实交易,请据此回答)】\n"
211
197
  + "\n".join(parts))
212
- # 把证据链摘要沉淀到 ThoughtChain(可审计)
213
198
  self._think(
214
199
  content=f"财务内核证据链:{len(ec)} 条决策,{len(anomalies)} 异常",
215
200
  node_type="reflection", conf=0.9,
216
201
  )
217
202
  except Exception:
218
- pass # 内核分析失败不阻断,降级为纯 LLM 回答
203
+ pass
219
204
 
220
205
  if ledger_ack:
221
- # 已落账的明细必须交给 LLM:否则它会重复"确认记账",甚至自己再报一遍数字
222
206
  kernel_insight = ("\n\n【刚刚已记账(已落账,请据此回答,不要重复报账)】\n"
223
207
  + ledger_ack + kernel_insight)
224
208
 
@@ -237,7 +221,6 @@ class FinanceAgent(CognitiveAgent):
237
221
  conf=0.90,
238
222
  )
239
223
 
240
- # 记忆财务决策
241
224
  self._store_memory(
242
225
  content=f"财务分析:{task[:80]}\n结论:{response[:200]}",
243
226
  mem_type="decision",
@@ -23,8 +23,6 @@ class OfficeAgent(CognitiveAgent):
23
23
  AGENT_NAME = "办公学习 Agent"
24
24
  ALLOWED_TOOL_CATEGORIES = {"read", "write", "network", "calendar"}
25
25
 
26
- # 日程类任务的触发词:命中则按"行政"话术走(不索要 knowledge JSON)。
27
- # 与 skills/schedule-itinerary/SKILL.md 的 triggers 保持一致。
28
26
  SCHEDULE_HINTS = ("日程", "行程", "会议", "出差", "预约", "日历", "路线", "几点", "提醒")
29
27
 
30
28
  def attach_runtime(self, runtime):
@@ -38,7 +36,6 @@ class OfficeAgent(CognitiveAgent):
38
36
  """
39
37
  self.itin = runtime
40
38
 
41
- # ── 编排协作(P3:领域态势从编排层下沉至此)──────
42
39
  def situation(self) -> dict:
43
40
  """一次自报两个领域:学习 + 行程。编排层按 `domains` 聚合(缺一个不影响另一个)。"""
44
41
  return {"domains": {"learning": self._learning_status(),
@@ -83,11 +80,8 @@ class OfficeAgent(CognitiveAgent):
83
80
  return [{"domain": "itinerary", "severity": "critical", "msg": c["reason"]}
84
81
  for c in conflicts]
85
82
 
86
- # ── 行程/日程(原 AdminAgent)────────────────────────────────
87
- # 差旅预算让步:每轮降一档;低于原始的 60% 就不再让(那已不是同一趟出差了,
88
- # 硬压下去只会规划出一个没法执行的行程 —— 宁可认输转由人决定)。
89
- CONCEDE_STEP = 0.75 # 每轮降到上一轮的 75%(改高铁/换酒店/减一天的量化近似)
90
- CONCEDE_FLOOR = 0.60 # 相对**原始**申请额的下限
83
+ CONCEDE_STEP = 0.75
84
+ CONCEDE_FLOOR = 0.60
91
85
 
92
86
  def concede(self, req, verdict: str):
93
87
  """【协商闭环·发起方】差旅预算被财务守门人拒 → 降标准重报。
@@ -104,11 +98,9 @@ class OfficeAgent(CognitiveAgent):
104
98
  if amount <= 0:
105
99
  return None
106
100
  original = float(req.payload.get("_original_amount") or amount)
107
- # 降一档,但**夹到下限**而不是跌破就放弃 —— 否则步长过冲时会在下限之上提前认输
108
- # (8000→6000→(4500 破线)放弃),白白浪费 4800 这个本来能过的方案。
109
101
  new_amount = max(round(amount * self.CONCEDE_STEP), round(original * self.CONCEDE_FLOOR))
110
102
  if new_amount <= 0 or new_amount >= amount:
111
- return None # 已到下限,再降就破线 → 认输(维持 rejected,不硬闯)
103
+ return None
112
104
  p = dict(req.payload)
113
105
  p["amount"] = new_amount
114
106
  p["_original_amount"] = original
@@ -154,14 +146,12 @@ class OfficeAgent(CognitiveAgent):
154
146
  )
155
147
  return report
156
148
 
157
- # ── 学习/知识(原 OfficeAgent)────────────────────────────────
158
149
  @staticmethod
159
150
  def _strip_knowledge_json(text: str) -> str:
160
151
  """从回复中剥离 {"new_knowledge": [...]} 结构化块,只保留自然语言。"""
161
152
  if not text or "new_knowledge" not in text:
162
153
  return text
163
154
  import re
164
- # 去掉 {"new_knowledge": [ ... ]} 整块(含可能的 markdown 代码围栏)
165
155
  cleaned = re.sub(r'`{0,3}\s*\{\s*"new_knowledge".*?\}\s*\]\s*\}\s*`{0,3}',
166
156
  '', text, flags=re.DOTALL)
167
157
  return cleaned.strip()
@@ -172,8 +162,6 @@ class OfficeAgent(CognitiveAgent):
172
162
  if rt is None:
173
163
  return {"error": "学习内核未挂载"}
174
164
  report = rt.analyze()
175
- # 给前端「知识库」轮播提供知识条目明细:analyze 本身只有统计、无明细,
176
- # 否则前端读 analysis.items 拿不到 → 一直显示"知识库为空"(即便已录入)。
177
165
  try:
178
166
  with rt._lock:
179
167
  _vals = list(rt.items.values())
@@ -220,7 +208,7 @@ Hermes 自学原则(像赫尔墨斯那样):
220
208
  你的知识是活的,会生长的。"""
221
209
 
222
210
  _task_entities = lambda self, task: ["学习", "知识", "文档", "日程", "行程", "会议"]
223
- _recall_topk = 8 # 办公学习 Agent 广泛召回
211
+ _recall_topk = 8
224
212
 
225
213
  def _recall_query(self, task: str) -> str:
226
214
  """学习 + 日程两类记忆都要召回(合并后单一 Agent 覆盖两个域)。"""
@@ -244,7 +232,6 @@ Hermes 自学原则(像赫尔墨斯那样):
244
232
  f"- {_fmt_mem(m, 80)}" for m in memories[:6]
245
233
  ])
246
234
 
247
- # 接入学习内核:若已挂载且有知识点,做真实分析(待复习/掌握度)作为依据
248
235
  kernel_insight = ""
249
236
  learn_rt = getattr(self, "learn_rt", None)
250
237
  if learn_rt is not None and getattr(learn_rt, "items", None):
@@ -262,9 +249,8 @@ Hermes 自学原则(像赫尔墨斯那样):
262
249
  node_type="reflection", conf=0.9,
263
250
  )
264
251
  except Exception:
265
- pass # 内核失败不阻断,降级纯 LLM
252
+ pass
266
253
 
267
- # 接入行程内核:若已挂载且有路点,做真实规划(路线/冲突)作为 LLM 依据
268
254
  itin = getattr(self, "itin", None)
269
255
  if itin is not None and getattr(itin, "waypoints", None):
270
256
  try:
@@ -285,7 +271,7 @@ Hermes 自学原则(像赫尔墨斯那样):
285
271
  node_type="reflection", conf=0.9,
286
272
  )
287
273
  except Exception:
288
- pass # 内核失败不阻断,降级纯 LLM
274
+ pass
289
275
 
290
276
  if schedule:
291
277
  prompt = f"""行程/日程任务:{task}
@@ -310,13 +296,11 @@ Hermes 自学原则(像赫尔墨斯那样):
310
296
 
311
297
  new_knowledge_count = 0
312
298
  if not schedule:
313
- # 提取并存储新知识(括号平衡解析:正确处理内容含 []{} 的情况)
314
299
  try:
315
300
  start = response.find('{"new_knowledge"')
316
301
  if start < 0:
317
302
  start = response.find('{ "new_knowledge"')
318
303
  if start >= 0:
319
- # 从 start 起做花括号配对,找到完整 JSON 对象的结束位置
320
304
  depth, end, in_str, esc = 0, -1, False, False
321
305
  for i in range(start, len(response)):
322
306
  c = response[i]
@@ -346,9 +330,8 @@ Hermes 自学原则(像赫尔墨斯那样):
346
330
  )
347
331
  new_knowledge_count += 1
348
332
  except Exception:
349
- pass # 解析失败不影响主流程
333
+ pass
350
334
 
351
- # 从返回文本中剥离 new_knowledge JSON 块(不展示给用户)
352
335
  response = self._strip_knowledge_json(response)
353
336
 
354
337
  self._think(
@@ -8,12 +8,13 @@ from datetime import datetime
8
8
  from ..base import CognitiveAgent
9
9
  from ...planning import planner as P
10
10
  from ._helpers import _with_prior, _dispatch_failed, _extract_step_results, PRIOR_MAX_CHARS
11
+ from typing import Any, cast
11
12
 
12
13
  class SupervisorAgent(CognitiveAgent):
13
14
  AGENT_ID = "supervisor"
14
15
  AGENT_NAME = "主管 Agent"
15
16
  ALLOWED_TOOL_CATEGORIES = {"read", "write", "calendar", "finance", "network"}
16
- IS_ROUTER = True # 调度层:豁免语义漂移拦截(接收任意输入是其本职)
17
+ IS_ROUTER = True
17
18
 
18
19
  @property
19
20
  def _role_prompt(self) -> str:
@@ -37,11 +38,6 @@ class SupervisorAgent(CognitiveAgent):
37
38
  def register_subagents(self, subagents: dict):
38
39
  """注入可调度的子 Agent 登记表。"""
39
40
  self._subagents = subagents
40
- # 【深审硬化·消除懒建 race】主管单例会被并发任务共享,_scheduler/_steering_queue 原先在
41
- # _schedule/_dispatch_parallel 里 `if not hasattr` 懒建——并发下是 check-then-act race
42
- # (现因 steering 队列 batch 隔离 + scheduler 无状态而良性,但依赖此实现细节脆弱)。改在
43
- # 本方法(Foxclaw 启动时单线程调一次)**预建**,并发任务到达时属性已就绪、不再 race。
44
- # 保留下游懒建作 standalone(未 register)兜底。
45
41
  from ...core.scheduler import CognitiveScheduler
46
42
  from ...core.steering_queue import SteeringQueue
47
43
  if not hasattr(self, "_scheduler"):
@@ -72,12 +68,10 @@ class SupervisorAgent(CognitiveAgent):
72
68
  orch = getattr(self, "orch", None)
73
69
  if orch is None:
74
70
  return {"error": "编排引擎未挂载"}
75
- from foxclaw.orchestration import CrossAgentRequest
71
+ from foxclaw.orchestration import CrossAgentRequest, RequestType
76
72
  req = CrossAgentRequest(from_agent=from_agent, to_agent=to_agent,
77
- request_type=request_type, payload=payload,
73
+ request_type=cast(RequestType, request_type), payload=payload,
78
74
  reason=reason)
79
- # 走协商闭环:被拒时给发起方让步重报的机会。对未实现 concede 的 Agent 而言
80
- # 只会跑一轮 → 与原先的一次性 route_request 逐字等价(零行为变更)。
81
75
  result = orch.negotiate(req)
82
76
  rounds = len(result.negotiation) or 1
83
77
  self._think(
@@ -87,24 +81,18 @@ class SupervisorAgent(CognitiveAgent):
87
81
  )
88
82
  return result.to_dict()
89
83
 
90
- # 路由关键词表:无 LLM 时也能基于关键词分发。
91
- # 拓扑 5 → 3 后:原行政(日程/行程)关键词并入办公;"助手"那组(整理/汇总/沟通)
92
- # 不再有归属 —— 它们正是主管自己该干的兜底活,未命中 → 主管自答。
93
84
  ROUTING_KEYWORDS = {
94
85
  "finance": ["收支", "支出", "预算", "理财", "投资", "储蓄", "财务", "消费", "报销", "账"],
95
86
  "office": ["学习", "论文", "知识", "总结", "研究", "文档", "写作", "资料", "搜索", "查",
96
87
  "日程", "行程", "会议", "出差", "预约", "日历", "计划表", "提醒",
97
- # 原助手的"整理/汇总/邮件"归办公:那是内容活,主管只兜底通用与协调
98
88
  "整理", "汇总", "邮件", "纪要"],
99
89
  }
100
90
 
101
- # 子 Agent 职责描述(P0-1 LLM 规划用):喂给 submit_plan 工具做 agent 枚举约束
102
91
  SUBAGENT_DESC = {
103
92
  "finance": "财务 Agent:理财、收支、预算、风控、对账",
104
93
  "office": "办公学习 Agent:学习、知识管理、研究、写作、日程与行程安排",
105
94
  }
106
95
 
107
- # 元问题关键词:这类"系统级/自我介绍"问题由主管自己回答,不分派
108
96
  META_KEYWORDS = ["你会做什么", "你能做什么", "你是谁", "你能干", "你会干",
109
97
  "介绍一下", "介绍下", "自我介绍", "有什么功能", "能帮我做什么",
110
98
  "你都能", "怎么用", "如何使用", "你好", "在吗", "干什么",
@@ -141,18 +129,15 @@ class SupervisorAgent(CognitiveAgent):
141
129
  """
142
130
  if self._is_expense_report(task):
143
131
  return "finance"
144
- # 关键词命中计数走统一原语(negation=False,与原计数逐字一致)
145
132
  scores = {aid: P.keyword_hits(task, kws)
146
133
  for aid, kws in self.ROUTING_KEYWORDS.items()}
147
- best = max(scores, key=scores.get)
134
+ best = max(scores, key=lambda k: scores[k])
148
135
  if scores[best] > 0:
149
136
  return best
150
- # 关键词不明:用调度意图做辅助判断
151
137
  if sched is not None:
152
138
  intent = getattr(getattr(sched, "intent", None), "category", None)
153
139
  if intent in ("reasoning", "memory"):
154
- return "office" # 推理/记忆类 → 知识研究 Agent
155
- # 无明确归属 → 主管自答(dispatch 见 target 不在 subagents 即走 _dispatch_fallback)
140
+ return "office"
156
141
  return "supervisor"
157
142
 
158
143
  def _coerce_agent(self, ag: str, subtask: str, subagents: dict) -> str:
@@ -175,13 +160,6 @@ class SupervisorAgent(CognitiveAgent):
175
160
  return (any(P.keyword_hits(task, kws) for kws in self.ROUTING_KEYWORDS.values())
176
161
  or self._is_expense_report(task))
177
162
 
178
- # 【已删除 · 请勿凭记忆加回】这里原有一个 `_llm_classify(task) -> Optional[str]`:
179
- # 关键词未命中时用一次 LLM 把任务分诊到领域 Agent。它已被「无关键词命中 → 主管直接
180
- # 自答」分支取代并成为死代码(全仓库零调用点),故删除。当时的取舍:每次分诊要多一次
181
- # 约 5s 的串行 LLM 往返,正面对话不值当,于是改为主管自答。P1-1 后该分支在
182
- # pipeline/supervisor_stages.py 的 route(决策)/dispatch(执行)两个 stage 里。
183
- # 如需恢复精修,应从 git 历史取回本方法,并同时改回那两个 stage、补回对应测试
184
- # —— 只加回方法不接线等于再制造一处死代码。
185
163
 
186
164
  def _plan_task(self, task: str, sched=None):
187
165
  """用 planner 判断任务是否需要拆解(多步)。
@@ -203,7 +181,6 @@ class SupervisorAgent(CognitiveAgent):
203
181
  p = ahs_plan(task, reg)
204
182
  if len(p.tasks) <= 1:
205
183
  return None, None
206
- # 为每个子任务单独算路由(关键词为主)→ planner 拆解真正落到不同 Agent
207
184
  step_routes = [(t.name, self._route(t.name)) for t in p.tasks]
208
185
  return p, step_routes
209
186
  except Exception:
@@ -233,7 +210,7 @@ class SupervisorAgent(CognitiveAgent):
233
210
  return None
234
211
  steps = result["steps"]
235
212
  if len(steps) <= 1:
236
- return None # 单步 → 交回 _route 关键词路由,不改变单步行为
213
+ return None
237
214
  from ...skills.pipeline import Plan, PlanTask
238
215
  n = len(steps)
239
216
  parallel = result.get("flow") == "parallel"
@@ -309,8 +286,6 @@ class SupervisorAgent(CognitiveAgent):
309
286
  from ...core.scheduler import CognitiveScheduler, SystemContext
310
287
  if not hasattr(self, "_scheduler"):
311
288
  self._scheduler = CognitiveScheduler()
312
- # 系统上下文:活跃 Agent 数 = 当前持有活跃 session 的子 Agent 数
313
- # (用真实可得的 _session_id 判断忙碌,而非不存在的 is_busy 属性)
314
289
  active = sum(1 for a in subagents.values()
315
290
  if getattr(a, "_session_id", None))
316
291
  ctx = SystemContext(active_agents=active, queue_depth=0, system_load=0.1)
@@ -322,10 +297,10 @@ class SupervisorAgent(CognitiveAgent):
322
297
 
323
298
  (原先每步都只拿到同一份原始 context,等于 N 个互不相干的任务排队跑——
324
299
  「查本月餐饮花了多少,再据此定预算」的第 2 步根本拿不到第 1 步的数字。)"""
325
- pm = getattr(self, "_process_manager", None)
300
+ pm: Any = getattr(self, "_process_manager", None)
326
301
  bus = getattr(self, "_system_bus", None)
327
302
  steps_out = []
328
- prior = [] # 已完成步骤的结果,供后续步骤参考
303
+ prior: list[Any] = []
329
304
  any_blocked = False
330
305
  for idx, (subtask, ag) in enumerate(step_routes, 1):
331
306
  if ag not in subagents:
@@ -337,10 +312,6 @@ class SupervisorAgent(CognitiveAgent):
337
312
  )
338
313
  if proc:
339
314
  pm.mark_running(proc.process_id)
340
- # _no_emit:子 Agent 不自发 execution.completed,由下方主管统一发(带 schedule),避免双发
341
- # prior_steps 同时也放进 context,便于管线/Agent 按结构化方式取用
342
- # 补超时(D 类修复):与 _dispatch_single / _dispatch_parallel 三处标准一致,
343
- # 子 Agent 卡死不再让整条多步链永久挂起。
344
315
  _timeout = float(context.get("parallel_timeout", 120))
345
316
  try:
346
317
  sub_result = await asyncio.wait_for(
@@ -356,25 +327,16 @@ class SupervisorAgent(CognitiveAgent):
356
327
  sub_output = sub_result.get("result", "")
357
328
  if isinstance(sub_output, dict):
358
329
  sub_output = json.dumps(sub_output, ensure_ascii=False)
359
- # 未完成(含超时)一律视为 blocked —— 不得让超时步骤被当成
360
- # 成功结果进入 prior 链,否则后续步骤会基于"不存在的产出"继续推理。
361
330
  blocked = sub_result.get("error") in ("harness_blocked", "subagent_timeout")
362
331
  any_blocked = any_blocked or blocked
363
332
  if proc:
364
333
  (pm.mark_failed(proc.process_id, reason=sub_result.get("error") or "")
365
334
  if blocked else pm.mark_completed(proc.process_id))
366
- # 展示用的 subtask 保留**原始**文本(不含前序上下文前言,免污染 UI)
367
335
  steps_out.append({"step": idx, "subtask": subtask, "agent": ag,
368
336
  "output": str(sub_output)[:200], "blocked": blocked})
369
- if not blocked and sub_output: # 被拦截/空输出的步骤无可参考内容,不入链
370
- # 入链即截断:渲染时本就只用最近 PRIOR_MAX_STEPS 步 × PRIOR_MAX_CHARS 字符,
371
- # 存全量纯属浪费——且 prior 会被 list(prior) 复制进**每个**后续步骤的 context,
372
- # 长链 + 大输出会滚成可观的内存与拷贝开销。
337
+ if not blocked and sub_output:
373
338
  prior.append({"step": idx, "agent": ag,
374
339
  "output": str(sub_output)[:PRIOR_MAX_CHARS]})
375
- # 【P1-1】execution.completed 不再逐子任务手动 publish——改由 base 模板
376
- # `_emit_execution` 统一发一条**任务级**事件(子 Agent 均带 _no_emit=True)。
377
- # 无总线(部分测试/独立调用)→ 主管手动兜底沉淀记忆,与单步路径一致
378
340
  if bus is None and not blocked:
379
341
  self._store_memory(
380
342
  content=f"任务「{subtask}」由 {ag} 处理。要点:{str(sub_output)[:200]}",
@@ -418,18 +380,16 @@ class SupervisorAgent(CognitiveAgent):
418
380
  self._steering_queue = SteeringQueue(lease_seconds=120)
419
381
  q = self._steering_queue
420
382
  batch = f"batch_{session_id}"
421
- pm = getattr(self, "_process_manager", None)
383
+ pm: Any = getattr(self, "_process_manager", None)
422
384
  bus = getattr(self, "_system_bus", None)
423
385
  timeout = float(context.get("parallel_timeout", 120))
424
386
 
425
387
  agents_involved = sorted({ag for _, ag in step_routes})
426
- # fork 记录(串行,主管自身 think 不并发)
427
388
  self._think(
428
389
  content=f"并行 fork {len(step_routes)} 个子任务 → {agents_involved}",
429
390
  node_type="tool_intent", tool_name="fork_parallel", conf=0.9,
430
391
  )
431
392
 
432
- # 按 Agent 分组:不同组并行,同组内串行(护 per-agent 会话态)
433
393
  groups: dict[str, list] = {}
434
394
  for idx, (subtask, ag) in enumerate(step_routes):
435
395
  if ag not in subagents:
@@ -456,8 +416,6 @@ class SupervisorAgent(CognitiveAgent):
456
416
  q.publish(batch, ag, {"idx": idx, "subtask": subtask, "agent": ag,
457
417
  "output": str(out)[:200], "blocked": blocked,
458
418
  "failed": False})
459
- # 【P1-1】execution.completed 不再逐子任务手动 publish——
460
- # 改由 base 模板 `_emit_execution` 统一发任务级事件。
461
419
  except asyncio.TimeoutError:
462
420
  if proc:
463
421
  pm.mark_failed(proc.process_id, reason="timeout")
@@ -475,7 +433,6 @@ class SupervisorAgent(CognitiveAgent):
475
433
  await asyncio.gather(*[run_group(ag, items) for ag, items in groups.items()])
476
434
  wall_ms = round((time.perf_counter() - t0) * 1000, 1)
477
435
 
478
- # join:租约收集(不丢不重)→ 按 idx 还原原始顺序 → ack
479
436
  leased = q.lease(batch, max_items=len(step_routes) * 2)
480
437
  steps_out = []
481
438
  for it in leased:
@@ -485,13 +442,11 @@ class SupervisorAgent(CognitiveAgent):
485
442
 
486
443
  any_blocked = any(s.get("blocked") for s in steps_out)
487
444
  any_failed = any(s.get("failed") for s in steps_out)
488
- # join 记录(串行)
489
445
  self._think(
490
446
  content=f"并行 join:{len(steps_out)}/{len(step_routes)} 步汇聚,"
491
447
  f"墙钟 {wall_ms}ms,blocked={any_blocked},failed={any_failed}",
492
448
  node_type="reflection", conf=0.88,
493
449
  )
494
- # 无总线时兜底沉淀记忆(与顺序路径一致)
495
450
  if bus is None:
496
451
  for s in steps_out:
497
452
  if not s.get("blocked") and not s.get("failed"):
@@ -518,11 +473,6 @@ class SupervisorAgent(CognitiveAgent):
518
473
  "thought_summary": self.thought_chain.get_summary(self.AGENT_ID),
519
474
  }
520
475
 
521
- # ── 【P1-1】主管 pipeline 化:不再重写 run_task ────────────────────
522
- # 生命周期(会话/召回/返回体/Harness 兜底/hooks/流式/中断/统一 _emit_execution)
523
- # 全部走 base 的 _run_task_core;路由分发逻辑拆成 7 个 pipeline stage
524
- # (foxclaw/pipeline/supervisor_stages.py:recall→meta_check→schedule→route→plan→
525
- # dispatch→aggregate)。本类只保留:pipeline 覆盖、领域钩子、恢复记账、分发执行体。
526
476
 
527
477
  def _cognitive_pipeline(self):
528
478
  """主管的认知管线:SUPERVISOR_STAGES(路由分发),而非领域 Agent 的 DEFAULT_STAGES。"""
@@ -537,8 +487,6 @@ class SupervisorAgent(CognitiveAgent):
537
487
  """主管的目标基线实体(旧 run_task 用 _extract_entities 开会话,逐字保留)。"""
538
488
  return self._extract_entities(task)
539
489
 
540
- # 崩溃/重启恢复记账已上移到 base 生命周期钩子(领域 Agent 此前完全没接线,跑一半崩掉
541
- # 在恢复扫描里不可见);主管只是"恰好也装着 _recovery",无需再覆盖。
542
490
 
543
491
  async def _dispatch_single(self, task, target, sched, session_id,
544
492
  subagents, context, memories, plan_obj) -> dict:
@@ -546,8 +494,7 @@ class SupervisorAgent(CognitiveAgent):
546
494
  ① execution.completed 不再在此手动 publish——改由 base 模板 `_emit_execution`
547
495
  统一发一条**任务级**事件(子 Agent 调用带 _no_emit=True 仍不双发);
548
496
  ② 返回体不再带 session_id/agent/thought_summary(由 base 模板统一组装)。"""
549
- # 创建进程(任务即进程),携带调度决策 → 状态 scheduled
550
- pm = getattr(self, "_process_manager", None)
497
+ pm: Any = getattr(self, "_process_manager", None)
551
498
  proc = pm.create(task, agent_id=target, schedule=sched.to_dict()) if pm else None
552
499
 
553
500
  self._think(
@@ -556,35 +503,25 @@ class SupervisorAgent(CognitiveAgent):
556
503
  tool_name=f"dispatch_{target}",
557
504
  conf=0.92,
558
505
  )
559
- # 进程进入 running
560
506
  if proc:
561
507
  pm.mark_running(proc.process_id)
562
- # _no_emit:子 Agent 不自发,主管任务级统一 emit(见 base._emit_execution),避免双发
563
- #
564
- # 补超时(D 类修复:同文件内标准一致性)。此前只有并行路径
565
- # (_dispatch_parallel)有 asyncio.wait_for,单步/链式路径直接 await ——
566
- # 子 Agent 卡死(LLM 挂起/工具阻塞)会让主管任务**永久挂起**,且无日志
567
- # 无告警无释放。同一语义必须两套标准一致,超时值同样取 parallel_timeout。
568
508
  _timeout = float(context.get("parallel_timeout", 120))
569
509
  try:
570
510
  sub_result = await asyncio.wait_for(
571
511
  subagents[target].run_task(task, {**context, "_no_emit": True}),
572
512
  timeout=_timeout)
573
513
  except asyncio.TimeoutError:
574
- # 诚实交代:不伪造子 Agent 的产出,明确回报"超时未完成"
575
514
  _note = f"子 Agent {target} 执行超时({_timeout:g}s),已中止并如实回报未完成结果"
576
515
  if proc:
577
516
  pm.mark_failed(proc.process_id, reason="subagent_timeout")
578
517
  self._think(content=_note, node_type="decision", conf=0.95)
579
518
  sub_result = {"result": _note, "error": "subagent_timeout", "agent": target}
580
519
 
581
- # 汇聚子 Agent 结果
582
520
  sub_output = sub_result.get("result", "")
583
521
  if isinstance(sub_output, dict):
584
522
  sub_output = json.dumps(sub_output, ensure_ascii=False)
585
523
  blocked = sub_result.get("error") == "harness_blocked"
586
524
 
587
- # 进程终态:被拦截/出错→failed,否则→completed
588
525
  if proc:
589
526
  if blocked:
590
527
  pm.mark_failed(proc.process_id, reason="harness_blocked")
@@ -597,8 +534,6 @@ class SupervisorAgent(CognitiveAgent):
597
534
  conf=0.88,
598
535
  )
599
536
 
600
- # 记忆沉淀:正常由真值管线(事件驱动,经 base._emit_execution)负责;
601
- # 无总线时(如部分测试场景)主管手动兜底,避免丢记忆。
602
537
  bus = getattr(self, "_system_bus", None)
603
538
  if bus is None:
604
539
  self._store_memory(
@@ -646,11 +581,9 @@ class SupervisorAgent(CognitiveAgent):
646
581
  (因中文无大小写,原首字母逻辑对中文无效)。
647
582
  """
648
583
  keywords = []
649
- # 英文:首字母大写的词
650
584
  for word in text.split():
651
585
  if len(word) > 3 and word[0].isupper():
652
586
  keywords.append(word)
653
- # 中文:命中路由关键词的领域词(作为实体线索)
654
587
  for kws in self.ROUTING_KEYWORDS.values():
655
588
  for kw in kws:
656
589
  if kw in text and kw not in keywords:
@@ -8,45 +8,22 @@ from ...core.thought_chain import ThoughtNode
8
8
  from ...harness.harness import HarnessResult
9
9
 
10
10
 
11
- # 每任务(per asyncio-Task / per-thread)会话状态。放在 contextvar 而非实例属性,使同一
12
- # Agent 实例并发处理多个顶层任务时,各任务的会话指针互不覆盖(asyncio 每个 Task 自动持
13
- # 独立 context 副本;旧实现下 await 后的推理节点会被最后启动的会话吞掉——已实测复现)。
14
- # 写法:单个 module 级 contextvar 承载一个 dict、按"每实例 token"分槽:
15
- # ① GC 友好(只有一个 contextvar,不随实例增长);
16
- # ② 支持 supervisor→subagent 的同任务嵌套(不同实例 token 不冲突,互不覆盖);
17
- # ③ copy-on-write —— 一个任务的 set 生成新 dict,不影响其它任务。
18
11
  _agent_ctx: contextvars.ContextVar = contextvars.ContextVar(
19
12
  "foxclaw_agent_ctx", default=None)
20
13
 
21
- # 抑制记忆写入(随 to_thread/asyncio.run 上下文复制传播):MCP ask_foxclaw 驱动的外部任务
22
- # 不写入 HMR,防外部客户端灌垃圾污染你的个人记忆。默认 False(正常任务照写)。
23
14
  _suppress_memory: contextvars.ContextVar = contextvars.ContextVar(
24
15
  "foxclaw_suppress_memory", default=False)
25
16
 
26
- # act 阶段 LLM 流式回调(D1,随 to_thread/asyncio.run 上下文复制传播):run_task_stream 设它,
27
- # 承载 {"on_token": cb, "stop_event": ev};_call_llm 读到非 None 则走 router.stream 逐 token 回调
28
- # + 累积返回完整串(返回值语义不变)。默认 None = 非流式(走 router.complete,行为逐字不变)。
29
17
  _act_stream: contextvars.ContextVar = contextvars.ContextVar(
30
18
  "foxclaw_act_stream", default=None)
31
19
 
32
- # 断点续跑(P1):{步骤指纹: {"name": 工具名, "result": 上次结果}}。run_task 带
33
- # `context={"_resume_task_id": ...}` 时由 _run_task_core 从 recovery 日志装载,工具轮命中
34
- # 同指纹步骤则复用结果、不再重复执行(避免重跑联网/副作用)。默认 None = 正常执行,行为不变。
35
20
  _resume_steps: contextvars.ContextVar = contextvars.ContextVar(
36
21
  "foxclaw_resume_steps", default=None)
37
22
 
38
- # E2 推理上下文注入(随 to_thread/asyncio.run 上下文复制传播):pre_reasoning(agent_start)钩子
39
- # 返回的 additional_context 存这里,_call_llm 前插为一条 user 消息注入推理。默认 None = 不注入
40
- # (无钩子/钩子未返回上下文时行为逐字不变)。
41
23
  _reasoning_inject: contextvars.ContextVar = contextvars.ContextVar(
42
24
  "foxclaw_reasoning_inject", default=None)
43
25
 
44
26
 
45
- # 执行证据(存在会话槽里,与污点同寿命):每次工具轮把「这一步有没有凭据」记下来,
46
- # 最终由 stream_reply 一次性交给端点做 SSE `event: evidence`,前端展示给人核对。
47
- # 放会话槽而非实例属性:同一 Agent 并发两个任务时证据不得串台。
48
-
49
-
50
27
  def _evidence_reset(agent) -> None:
51
28
  tok = getattr(agent, "_ctx_token", None)
52
29
  if tok is not None: