foxclaw 0.1.2 → 0.1.4

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 (368) 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/.github/workflows/release.yml +5 -0
  6. package/python/CHANGELOG.md +100 -0
  7. package/python/CHANGELOG_CN.md +100 -0
  8. package/python/README.md +17 -2
  9. package/python/README_CN.md +38 -20
  10. package/python/bench/bm25_audit.py +1 -14
  11. package/python/bench/bm25_regression.py +0 -1
  12. package/python/bench/bm25_stress.py +0 -4
  13. package/python/bench/cd_audit.py +0 -23
  14. package/python/bench/chaos.py +6 -8
  15. package/python/bench/e2e_real_models.py +0 -1
  16. package/python/bench/llm_retry_test.py +2 -9
  17. package/python/bench/load_test.py +5 -8
  18. package/python/bench/vector_ivf_upgrade.py +1 -7
  19. package/python/docs/CONTENT_GUARD.md +189 -0
  20. package/python/docs/CONTENT_GUARD_CN.md +189 -0
  21. package/python/docs/RELEASE.md +78 -37
  22. package/python/docs/RUN_CN.md +2 -2
  23. package/python/evals/governance/cases.py +0 -37
  24. package/python/evals/governance/run_evals.py +5 -37
  25. package/python/examples/governance_middleware_demo.py +1 -6
  26. package/python/examples/langgraph_governance_demo.py +7 -11
  27. package/python/foxclaw/__init__.py +74 -152
  28. package/python/foxclaw/agents/base.py +82 -136
  29. package/python/foxclaw/agents/impl/_helpers.py +2 -10
  30. package/python/foxclaw/agents/impl/finance.py +4 -21
  31. package/python/foxclaw/agents/impl/office.py +7 -24
  32. package/python/foxclaw/agents/impl/supervisor.py +12 -79
  33. package/python/foxclaw/agents/mixins/_context.py +0 -23
  34. package/python/foxclaw/agents/mixins/augment.py +10 -16
  35. package/python/foxclaw/agents/mixins/llm.py +7 -6
  36. package/python/foxclaw/agents/mixins/memory.py +23 -3
  37. package/python/foxclaw/agents/mixins/preference.py +58 -8
  38. package/python/foxclaw/agents/mixins/prompt.py +59 -12
  39. package/python/foxclaw/agents/mixins/skillbox.py +6 -6
  40. package/python/foxclaw/agents/mixins/tool_exec.py +16 -42
  41. package/python/foxclaw/agents/rules/finance/RULES.md +131 -0
  42. package/python/foxclaw/api/__init__.py +18 -77
  43. package/python/foxclaw/api/_state.py +43 -0
  44. package/python/foxclaw/api/bootstrap.py +114 -0
  45. package/python/foxclaw/api/middleware.py +5 -28
  46. package/python/foxclaw/api/routes/ahs.py +60 -73
  47. package/python/foxclaw/api/routes/attachments.py +5 -6
  48. package/python/foxclaw/api/routes/browser.py +7 -11
  49. package/python/foxclaw/api/routes/domain.py +18 -26
  50. package/python/foxclaw/api/routes/knowledge.py +3 -21
  51. package/python/foxclaw/api/routes/llm_admin.py +21 -33
  52. package/python/foxclaw/api/routes/memory.py +50 -12
  53. package/python/foxclaw/api/routes/orchestration.py +3 -5
  54. package/python/foxclaw/api/routes/recovery.py +4 -3
  55. package/python/foxclaw/api/routes/review.py +2 -11
  56. package/python/foxclaw/api/routes/search.py +3 -7
  57. package/python/foxclaw/api/routes/system.py +33 -49
  58. package/python/foxclaw/api/routes/task.py +31 -49
  59. package/python/foxclaw/api/routes/ws.py +2 -6
  60. package/python/foxclaw/api/spine.py +1 -4
  61. package/python/foxclaw/behavior/__init__.py +20 -0
  62. package/python/foxclaw/behavior/behavior.yaml +62 -0
  63. package/python/foxclaw/behavior/spec.py +287 -0
  64. package/python/foxclaw/browser/action/action_executor.py +1 -5
  65. package/python/foxclaw/browser/action/action_types.py +0 -1
  66. package/python/foxclaw/browser/action/url_policy.py +1 -5
  67. package/python/foxclaw/browser/adapter/web_adapter.py +1 -1
  68. package/python/foxclaw/browser/driver/playwright_driver.py +20 -16
  69. package/python/foxclaw/browser/perception/dom_extractor.py +0 -1
  70. package/python/foxclaw/browser/perception/element_indexer.py +1 -2
  71. package/python/foxclaw/browser/service.py +7 -11
  72. package/python/foxclaw/capability/embedding_service.py +4 -6
  73. package/python/foxclaw/channels/__init__.py +9 -11
  74. package/python/foxclaw/channels/discord.py +4 -13
  75. package/python/foxclaw/channels/dispatcher.py +2 -27
  76. package/python/foxclaw/channels/feishu.py +10 -20
  77. package/python/foxclaw/channels/openclaw_bridge.py +4 -9
  78. package/python/foxclaw/channels/signing.py +9 -9
  79. package/python/foxclaw/channels/telegram.py +3 -4
  80. package/python/foxclaw/channels/weixin.py +7 -17
  81. package/python/foxclaw/cli.py +42 -13
  82. package/python/foxclaw/cognitive/reasoner.py +9 -10
  83. package/python/foxclaw/cognitive/state.py +18 -24
  84. package/python/foxclaw/core/audit_chain.py +1 -3
  85. package/python/foxclaw/core/audit_sink.py +3 -3
  86. package/python/foxclaw/core/compaction.py +8 -26
  87. package/python/foxclaw/core/config.py +2 -2
  88. package/python/foxclaw/core/confirm_nonce.py +6 -12
  89. package/python/foxclaw/core/content_guard.py +341 -0
  90. package/python/foxclaw/core/envfile.py +2 -2
  91. package/python/foxclaw/core/failure_policy.py +17 -26
  92. package/python/foxclaw/core/gateway.py +6 -10
  93. package/python/foxclaw/core/governance_audit.py +3 -7
  94. package/python/foxclaw/core/idempotency.py +15 -22
  95. package/python/foxclaw/core/injection_guard.py +10 -29
  96. package/python/foxclaw/core/memory_adapter.py +53 -60
  97. package/python/foxclaw/core/metrics.py +4 -5
  98. package/python/foxclaw/core/model_router.py +74 -150
  99. package/python/foxclaw/core/paths.py +1 -1
  100. package/python/foxclaw/core/recovery.py +21 -34
  101. package/python/foxclaw/core/scheduler.py +23 -26
  102. package/python/foxclaw/core/secrets.py +0 -3
  103. package/python/foxclaw/core/sensitive_write.py +283 -0
  104. package/python/foxclaw/core/sessions.py +4 -6
  105. package/python/foxclaw/core/settings.py +16 -32
  106. package/python/foxclaw/core/shared_write.py +11 -23
  107. package/python/foxclaw/core/steering_queue.py +12 -11
  108. package/python/foxclaw/core/system_bus.py +1 -1
  109. package/python/foxclaw/core/thought_chain.py +30 -32
  110. package/python/foxclaw/core/token_meter.py +9 -13
  111. package/python/foxclaw/core/tools.py +4 -12
  112. package/python/foxclaw/core/tracing.py +25 -40
  113. package/python/foxclaw/core/truth_pipeline.py +5 -9
  114. package/python/foxclaw/core/update_check.py +149 -0
  115. package/python/foxclaw/core/vec_recall_adapter.py +11 -37
  116. package/python/foxclaw/core/verify.py +6 -14
  117. package/python/foxclaw/core/web_search.py +4 -28
  118. package/python/foxclaw/finance/evidence.py +14 -18
  119. package/python/foxclaw/finance/fx.py +1 -3
  120. package/python/foxclaw/finance/graph.py +9 -18
  121. package/python/foxclaw/finance/models.py +20 -24
  122. package/python/foxclaw/finance/nlparse.py +12 -72
  123. package/python/foxclaw/finance/policy.py +8 -36
  124. package/python/foxclaw/finance/reasoner.py +3 -11
  125. package/python/foxclaw/finance/reasoning.py +13 -34
  126. package/python/foxclaw/finance/reconcile.py +5 -10
  127. package/python/foxclaw/finance/runtime.py +19 -46
  128. package/python/foxclaw/governance/middleware.py +13 -21
  129. package/python/foxclaw/harness/_base.py +2 -8
  130. package/python/foxclaw/harness/_policy.py +19 -38
  131. package/python/foxclaw/harness/drift.py +1 -13
  132. package/python/foxclaw/harness/event_bus.py +2 -5
  133. package/python/foxclaw/harness/harness.py +9 -97
  134. package/python/foxclaw/harness/loop_detection.py +11 -18
  135. package/python/foxclaw/itinerary/amap_client.py +2 -5
  136. package/python/foxclaw/itinerary/models.py +7 -7
  137. package/python/foxclaw/itinerary/reasoner.py +1 -6
  138. package/python/foxclaw/itinerary/reasoning.py +15 -21
  139. package/python/foxclaw/itinerary/runtime.py +8 -12
  140. package/python/foxclaw/kernel/capabilities.py +5 -6
  141. package/python/foxclaw/kernel/policy_gate.py +4 -10
  142. package/python/foxclaw/kernel/process_manager.py +17 -36
  143. package/python/foxclaw/kernel/scheduler.py +3 -9
  144. package/python/foxclaw/kernel/sentinel.py +13 -23
  145. package/python/foxclaw/kernel/sentinel_client.py +12 -15
  146. package/python/foxclaw/kernel/spine.py +9 -9
  147. package/python/foxclaw/knowledge/__init__.py +4 -20
  148. package/python/foxclaw/knowledge/truth_crystallizer.py +5 -8
  149. package/python/foxclaw/learning/knowledge_graph.py +4 -9
  150. package/python/foxclaw/learning/models.py +13 -13
  151. package/python/foxclaw/learning/reasoner.py +1 -4
  152. package/python/foxclaw/learning/runtime.py +19 -31
  153. package/python/foxclaw/learning/spaced_repetition.py +2 -7
  154. package/python/foxclaw/mcp_server.py +2 -3
  155. package/python/foxclaw/orchestration/__init__.py +16 -33
  156. package/python/foxclaw/pipeline/cognitive_pipeline.py +45 -97
  157. package/python/foxclaw/pipeline/stage_meta.py +21 -0
  158. package/python/foxclaw/pipeline/supervisor_stages.py +13 -42
  159. package/python/foxclaw/planning/llm_planner.py +3 -3
  160. package/python/foxclaw/planning/planner.py +6 -11
  161. package/python/foxclaw/search/agent_port.py +7 -11
  162. package/python/foxclaw/search/evidence.py +0 -4
  163. package/python/foxclaw/search/internal_search.py +3 -5
  164. package/python/foxclaw/search/models.py +11 -13
  165. package/python/foxclaw/search/planner.py +3 -3
  166. package/python/foxclaw/search/retriever.py +1 -1
  167. package/python/foxclaw/search/runtime.py +1 -8
  168. package/python/foxclaw/skills/__init__.py +46 -100
  169. package/python/foxclaw/skills/archive.py +111 -0
  170. package/python/foxclaw/skills/fetch.py +226 -0
  171. package/python/foxclaw/skills/hooks_spec.py +21 -25
  172. package/python/foxclaw/skills/install_cmd.py +216 -0
  173. package/python/foxclaw/skills/lifecycle.py +16 -42
  174. package/python/foxclaw/skills/markdown_skill.py +10 -9
  175. package/python/foxclaw/skills/mcp_client.py +5 -11
  176. package/python/foxclaw/skills/mcp_store.py +0 -3
  177. package/python/foxclaw/skills/permission.py +13 -48
  178. package/python/foxclaw/skills/pipeline.py +21 -53
  179. package/python/foxclaw/skills/policy.py +4 -10
  180. package/python/foxclaw/skills/review.py +3 -3
  181. package/python/frontend/deck/auth.js +0 -7
  182. package/python/frontend/deck/chat_model_picker.js +0 -54
  183. package/python/frontend/deck/domain.js +0 -20
  184. package/python/frontend/deck/main.js +0 -144
  185. package/python/frontend/deck/markdown.js +0 -4
  186. package/python/frontend/deck/settings.js +1 -18
  187. package/python/frontend/deck/state.js +0 -1
  188. package/python/frontend/deck/util.js +0 -2
  189. package/python/frontend/deck/visuals.js +0 -22
  190. package/python/frontend/planet/planet.js +18 -201
  191. package/python/frontend/v3/assets/index-JKfHlb-a.css +1 -0
  192. package/python/frontend/v3/assets/index-tN90dpUH.js +34 -0
  193. package/python/frontend/v3/index.html +3 -3
  194. package/python/frontend/widget_runtime.js +0 -43
  195. package/python/frontend/widgets.js +0 -57
  196. package/python/frontend_src/index.html +1 -1
  197. package/python/frontend_src/package-lock.json +2 -2
  198. package/python/frontend_src/package.json +1 -1
  199. package/python/frontend_src/src/App.vue +3 -7
  200. package/python/frontend_src/src/api.js +0 -7
  201. package/python/frontend_src/src/components/ChatDock.vue +27 -39
  202. package/python/frontend_src/src/components/DocViewer.vue +0 -9
  203. package/python/frontend_src/src/components/Inspector.vue +0 -6
  204. package/python/frontend_src/src/components/LeftRail.vue +60 -39
  205. package/python/frontend_src/src/components/MenuPopover.vue +9 -11
  206. package/python/frontend_src/src/components/MessageStream.vue +18 -30
  207. package/python/frontend_src/src/components/ModelPicker.vue +0 -5
  208. package/python/frontend_src/src/components/QuickIngest.vue +0 -6
  209. package/python/frontend_src/src/components/Stage.vue +71 -25
  210. package/python/frontend_src/src/components/ToolMenu.vue +0 -3
  211. package/python/frontend_src/src/components/TopBar.vue +38 -10
  212. package/python/frontend_src/src/components/panels/ActivityPanel.vue +0 -2
  213. package/python/frontend_src/src/components/panels/AgentsPanel.vue +2 -5
  214. package/python/frontend_src/src/components/panels/KnowledgePanel.vue +0 -16
  215. package/python/frontend_src/src/components/panels/MemoryPanel.vue +42 -3
  216. package/python/frontend_src/src/components/panels/PanelBox.vue +6 -7
  217. package/python/frontend_src/src/components/panels/ReviewPanel.vue +0 -16
  218. package/python/frontend_src/src/components/panels/SettingsPanel.vue +142 -38
  219. package/python/frontend_src/src/components/panels/TasksPanel.vue +0 -1
  220. package/python/frontend_src/src/components/panels/ToolsPanel.vue +125 -19
  221. package/python/frontend_src/src/lib/fmt.js +0 -4
  222. package/python/frontend_src/src/lib/i18n.js +53 -0
  223. package/python/frontend_src/src/lib/locales/en.js +173 -0
  224. package/python/frontend_src/src/lib/locales/zh.js +174 -0
  225. package/python/frontend_src/src/lib/md.js +0 -14
  226. package/python/frontend_src/src/main.js +2 -0
  227. package/python/frontend_src/src/stores/app.js +17 -32
  228. package/python/frontend_src/src/stores/chat.js +0 -34
  229. package/python/frontend_src/src/stores/model.js +0 -8
  230. package/python/frontend_src/src/stores/panels.js +16 -44
  231. package/python/frontend_src/src/stores/profile.js +31 -0
  232. package/python/frontend_src/src/stores/system.js +25 -0
  233. package/python/frontend_src/vite.config.js +0 -7
  234. package/python/pyproject.toml +17 -3
  235. package/python/requirements.txt +311 -0
  236. package/python/tests/conftest.py +0 -22
  237. package/python/tests/run_all.py +4 -66
  238. package/python/tests/test_agent_lifecycle.py +4 -38
  239. package/python/tests/test_agent_rules.py +81 -0
  240. package/python/tests/test_ahs_p2.py +4 -25
  241. package/python/tests/test_anthropic_tools.py +1 -15
  242. package/python/tests/test_api.py +19 -9
  243. package/python/tests/test_api_auth.py +1 -5
  244. package/python/tests/test_attachments.py +9 -25
  245. package/python/tests/test_audit_chain.py +2 -7
  246. package/python/tests/test_audit_sink.py +0 -6
  247. package/python/tests/test_behavior_spec.py +198 -0
  248. package/python/tests/test_browser.py +2 -6
  249. package/python/tests/test_browser_request_guard.py +0 -4
  250. package/python/tests/test_capabilities.py +0 -6
  251. package/python/tests/test_channel_signing.py +2 -9
  252. package/python/tests/test_channels.py +1 -11
  253. package/python/tests/test_config_layering.py +0 -4
  254. package/python/tests/test_confirm_nonce.py +1 -12
  255. package/python/tests/test_conflict_dismiss.py +143 -0
  256. package/python/tests/test_content_guard.py +177 -0
  257. package/python/tests/test_context_compaction_wiring.py +1 -7
  258. package/python/tests/test_dispatch.py +0 -6
  259. package/python/tests/test_domain_evals.py +11 -80
  260. package/python/tests/test_domain_import.py +0 -6
  261. package/python/tests/test_domain_reasoners.py +0 -14
  262. package/python/tests/test_e2e.py +0 -17
  263. package/python/tests/test_emb_cache.py +7 -17
  264. package/python/tests/test_embedding_singleton.py +1 -3
  265. package/python/tests/test_evals_smoke.py +0 -24
  266. package/python/tests/test_event_spine.py +10 -18
  267. package/python/tests/test_evidence_semantic.py +1 -14
  268. package/python/tests/test_evidence_sse.py +3 -6
  269. package/python/tests/test_file_ingest.py +0 -11
  270. package/python/tests/test_finance.py +0 -9
  271. package/python/tests/test_finance_ledger.py +5 -12
  272. package/python/tests/test_finance_persist.py +8 -12
  273. package/python/tests/test_gateway.py +3 -22
  274. package/python/tests/test_governance.py +5 -52
  275. package/python/tests/test_governance_audit.py +4 -39
  276. package/python/tests/test_governance_middleware.py +1 -7
  277. package/python/tests/test_hkc.py +6 -24
  278. package/python/tests/test_hkc_index_build_manager.py +0 -33
  279. package/python/tests/test_hkc_manifest_reuse.py +4 -27
  280. package/python/tests/test_hkc_search_event_sync.py +3 -54
  281. package/python/tests/test_import_path_guard.py +0 -6
  282. package/python/tests/test_injection_guard.py +18 -36
  283. package/python/tests/test_integration.py +5 -29
  284. package/python/tests/test_itinerary.py +0 -3
  285. package/python/tests/test_kee_semantic.py +0 -5
  286. package/python/tests/test_kernel.py +1 -11
  287. package/python/tests/test_learning.py +3 -12
  288. package/python/tests/test_llm_admin.py +4 -19
  289. package/python/tests/test_llm_planner.py +1 -23
  290. package/python/tests/test_markdown_skills.py +3 -14
  291. package/python/tests/test_markdown_skills_openclaw.py +4 -9
  292. package/python/tests/test_mcp_connectors.py +3 -15
  293. package/python/tests/test_mcp_guardian_discovery.py +2 -6
  294. package/python/tests/test_mcp_no_memory.py +3 -3
  295. package/python/tests/test_mcp_policy_gate.py +0 -1
  296. package/python/tests/test_mcp_server.py +6 -9
  297. package/python/tests/test_memory_recall.py +1 -4
  298. package/python/tests/test_metrics.py +0 -8
  299. package/python/tests/test_multi_chain.py +1 -13
  300. package/python/tests/test_multimodal.py +5 -18
  301. package/python/tests/test_negotiation.py +3 -13
  302. package/python/tests/test_orchestration.py +1 -11
  303. package/python/tests/test_orchestration_decoupled.py +0 -6
  304. package/python/tests/test_p3_p5_hardening.py +3 -17
  305. package/python/tests/test_parallel_dispatch.py +4 -12
  306. package/python/tests/test_pipeline.py +17 -58
  307. package/python/tests/test_plan_visible.py +4 -4
  308. package/python/tests/test_planner.py +0 -7
  309. package/python/tests/test_policy_gate.py +2 -3
  310. package/python/tests/test_profile.py +67 -0
  311. package/python/tests/test_readme_claims.py +163 -0
  312. package/python/tests/test_reasoner.py +2 -17
  313. package/python/tests/test_release_check.py +2 -13
  314. package/python/tests/test_resume_checkpoint.py +7 -15
  315. package/python/tests/test_routing.py +2 -12
  316. package/python/tests/test_scheduler.py +7 -10
  317. package/python/tests/test_search.py +1 -11
  318. package/python/tests/test_secrets.py +4 -10
  319. package/python/tests/test_semantic.py +1 -10
  320. package/python/tests/test_sensitive_write.py +181 -0
  321. package/python/tests/test_sessions.py +3 -4
  322. package/python/tests/test_settings.py +2 -18
  323. package/python/tests/test_shadow_learning.py +3 -43
  324. package/python/tests/test_shared_write.py +0 -7
  325. package/python/tests/test_skill_taint_gate.py +1 -10
  326. package/python/tests/test_skills.py +2 -18
  327. package/python/tests/test_skills_fetch.py +282 -0
  328. package/python/tests/test_stats_and_events.py +2 -5
  329. package/python/tests/test_streaming.py +2 -7
  330. package/python/tests/test_supervisor_pipeline.py +4 -5
  331. package/python/tests/test_supervisor_triage.py +4 -10
  332. package/python/tests/test_taint.py +2 -7
  333. package/python/tests/test_task_stream.py +1 -12
  334. package/python/tests/test_thinking_sse.py +0 -1
  335. package/python/tests/test_tool_center.py +1 -17
  336. package/python/tests/test_tool_parallel.py +2 -2
  337. package/python/tests/test_tool_params.py +1 -5
  338. package/python/tests/test_tool_use.py +0 -13
  339. package/python/tests/test_toplevel_concurrency.py +3 -4
  340. package/python/tests/test_tracing.py +7 -41
  341. package/python/tests/test_truth_crystallizer.py +4 -6
  342. package/python/tests/test_truth_flow.py +2 -7
  343. package/python/tests/test_update_check.py +95 -0
  344. package/python/tests/test_verify_evidence.py +8 -21
  345. package/python/tests/test_version_sync.py +35 -6
  346. package/python/tests/test_web_trigger.py +0 -17
  347. package/python/tools/build_content_lexicon.py +218 -0
  348. package/python/tools/bundle_web.py +98 -0
  349. package/python/tools/check_faiss_type.py +0 -1
  350. package/python/tools/check_log.ps1 +0 -8
  351. package/python/tools/mypy_baseline.txt +12 -4
  352. package/python/tools/mypy_ratchet.py +52 -6
  353. package/python/tools/pack.py +0 -5
  354. package/python/tools/recheck_conflicts.py +156 -0
  355. package/python/tools/release_check.py +24 -15
  356. package/python/tools/restart_foxclaw.ps1 +5 -8
  357. package/python/tools/shared_write_audit.py +1 -15
  358. package/python/tools/size_check.py +1 -1
  359. package/python/tools/strip_comments.py +228 -0
  360. package/python/vendor/hkc/hkc_api/routes/ability.py +31 -0
  361. package/python/vendor/hkc/hkc_api/schemas.py +11 -0
  362. package/python/vendor/hkc/hkc_kee/kee.py +124 -3
  363. package/python/vendor/hkc/hkc_sdk/base.py +22 -0
  364. package/python/vendor/hkc/hkc_sdk/direct_client.py +10 -0
  365. package/python/vendor/hkc/hkc_sdk/http_client.py +19 -0
  366. package/python/foxclaw/api/app.py +0 -123
  367. package/python/frontend/v3/assets/index-BD7VPbMV.js +0 -33
  368. package/python/frontend/v3/assets/index-CAvnYWGH.css +0 -1
@@ -18,7 +18,6 @@ import os
18
18
  import re
19
19
  import secrets
20
20
 
21
- # 典型注入/越权特征(中英)。命中不拦截,只在数据块里加醒目告警,提示模型"这是数据里的指令,别执行"。
22
21
  _MARKERS = [
23
22
  r"ignore\s+(all\s+)?(the\s+)?previous\s+instructions?",
24
23
  r"disregard\s+(the\s+)?(above|previous|prior)",
@@ -42,25 +41,17 @@ def scan(text: str) -> bool:
42
41
  return bool(text) and bool(_RE.search(str(text)))
43
42
 
44
43
 
45
- # ── 输出侧护栏(纵深防御第四道:DLP)────────────────────────────────────────────
46
- # 回复返回/落库前扫一道:① 密钥/令牌等敏感串 → 脱敏;② 系统提示词被整段吐出 → 拦截。
47
- # 纯正则、零额外模型调用(适配 flash 成本)。与"输入-隔离-模型"三层凑齐四道墙。
48
-
49
- # 常见密钥/令牌形态(含 Foxclaw 实际用的百度千帆 bce-v3 AppBuilder key)。
50
44
  _SECRET_PATTERNS = [
51
- r"sk-[A-Za-z0-9]{20,}", # OpenAI 风格
52
- r"bce-v3/[A-Za-z0-9-]{6,}/[A-Za-z0-9]{6,}", # 百度千帆 AppBuilder(Foxclaw 在用)
53
- r"AKIA[0-9A-Z]{16}", # AWS Access Key
54
- r"gh[pousr]_[A-Za-z0-9]{20,}", # GitHub token
55
- r"xox[baprs]-[A-Za-z0-9-]{10,}", # Slack token
56
- r"Bearer\s+[A-Za-z0-9._\-]{20,}", # Bearer 令牌
45
+ r"sk-[A-Za-z0-9]{20,}",
46
+ r"bce-v3/[A-Za-z0-9-]{6,}/[A-Za-z0-9]{6,}",
47
+ r"AKIA[0-9A-Z]{16}",
48
+ r"gh[pousr]_[A-Za-z0-9]{20,}",
49
+ r"xox[baprs]-[A-Za-z0-9-]{10,}",
50
+ r"Bearer\s+[A-Za-z0-9._\-]{20,}",
57
51
  r"(api[_-]?key|secret|token|password|passwd)\s*[:=]\s*['\"]?[A-Za-z0-9._\-/]{12,}",
58
52
  ]
59
53
  _SECRET_RE = re.compile("|".join(_SECRET_PATTERNS), re.IGNORECASE)
60
54
 
61
- # 系统提示词泄露判定:不用"单个短语命中"(会误伤——如用户正常问"Principle 07 是什么")。
62
- # 改为**更强信号**:① 输出里出现 KERNEL 的长段**逐字串**(dump 才会逐字复现,提及概念不会);
63
- # ② 或同时出现 ≥2 个**章节标记**(无 kernel 参照时的兜底)。
64
55
  _SECTION_MARKS = ("【一、", "【二、", "【三、", "【四、", "【五、", "【六、", "【七、")
65
56
 
66
57
 
@@ -101,7 +92,6 @@ def _has_semantic_leak(text: str, kernel: str, threshold: float = 0.88,
101
92
  if model is None:
102
93
  return False
103
94
  import numpy as np
104
- # 固定 ~200 字块;向量已 normalize → 余弦 = 点积。内核块进共享缓存(只编码一次)。
105
95
  kblocks = [kernel[i:i + 200] for i in range(0, len(kernel), 200)]
106
96
  tblocks = [text[i:i + 200] for i in range(0, len(text), 200)]
107
97
  kv = encode_cached(model, name, kblocks)
@@ -165,8 +155,6 @@ def guard_output(text: str, kernel: str = "") -> tuple:
165
155
  return (text, issues)
166
156
 
167
157
 
168
- # 分隔符伪造防护:外部内容里若自带 <untrusted>/</untrusted>,会伪造边界把恶意指令挤出隔离区
169
- # (经典分隔符注入)。包裹前把内容中一切 <(/)untrusted 的尖括号中和成 ‹,使其无法充当真边界。
170
158
  _UNTRUSTED_DELIM = re.compile(r"<\s*/?\s*untrusted", re.IGNORECASE)
171
159
 
172
160
 
@@ -196,17 +184,10 @@ def wrap_untrusted(text: str, source: str = "external") -> str:
196
184
  return block + note
197
185
 
198
186
 
199
- # ── 数据污点分级(Muse「eBPF 污点追踪」的纯软件版)────────────────────────────
200
- # 对齐 Meta Muse 的安全内核:不靠"模型自觉不泄露",而靠"数据一旦来自用户/敏感源,
201
- # 就在会话上打污点;之后凡外发/副作用动作,污点达标则动态提高审批门槛(见
202
- # skills.permission 的 taint_threshold)。这里只做「来源 → 敏感度」映射;污点的
203
- # 传播与裁决在调用方(agents.base / skills.permission)。
204
- #
205
- # 敏感度分级(数值越高越敏感,单调可比):
206
- TAINT_NONE = 0 # 无外部数据(会话初始态)
207
- TAINT_EXTERNAL = 1 # 外部不可控但非个人:web_search 结果
208
- TAINT_PERSONAL = 2 # 用户个人数据:知识库(knowledge)/文件(file)
209
- TAINT_SENSITIVE = 3 # 高敏感个人数据:个人记忆(memory)
187
+ TAINT_NONE = 0
188
+ TAINT_EXTERNAL = 1
189
+ TAINT_PERSONAL = 2
190
+ TAINT_SENSITIVE = 3
210
191
 
211
192
  _TAINT_BY_SOURCE = {
212
193
  "web_search": TAINT_EXTERNAL,
@@ -20,6 +20,9 @@ import threading
20
20
  from pathlib import Path
21
21
 
22
22
  from .shared_write import guard_shared_write
23
+ from .sensitive_write import guard_sensitive_write
24
+ from types import SimpleNamespace
25
+ from typing import Any
23
26
 
24
27
 
25
28
  def _ensure_hmr_importable():
@@ -41,19 +44,18 @@ class HMRAdapter:
41
44
  若 HMR 不可用,调用方应回退到 MockHMR。
42
45
  """
43
46
 
44
- def __init__(self, storage_path: str = "./hmr_data", llm_api_key: str = None,
45
- embedding_model: str = None):
47
+ def __init__(self, storage_path: str = "./hmr_data", llm_api_key: str | None = None,
48
+ embedding_model: str | None = None):
46
49
  """【P1·share-bge-m3】新增 embedding_model 参数。
47
50
  上层(Foxclaw)通常在 HKC 已装配后再调本构造,bge-m3 已入 HKC 缓存;
48
51
  传具体模型名(如 HKC_EMBEDDING_MODEL)使 HMR 与 HKC 共享同一 SentenceTransformer 实例。
49
52
  不传则走 vendor/hmr/storage/vector_store.py 的 env 解析链(HKC_EMBEDDING_MODEL>bge-m3)。"""
50
53
  _ensure_hmr_importable()
51
- from hmr.core.hmr import HMR # 内嵌包
54
+ from hmr.core.hmr import HMR
52
55
 
53
- # 不传 None 就用 .env / HKC 的配置;只有上层显式 override 才用 _embedding_model
54
56
  import os as _os
55
57
  resolved_model = (embedding_model
56
- or _os.environ.get("EMBEDDING_MODEL") # 【P2·单 embedding 入口】最高优先级
58
+ or _os.environ.get("EMBEDDING_MODEL")
57
59
  or _os.environ.get("HKC_EMBEDDING_MODEL")
58
60
  or _os.environ.get("FOXCLAW_EMBEDDING_MODEL")
59
61
  or "all-MiniLM-L6-v2")
@@ -61,13 +63,8 @@ class HMRAdapter:
61
63
  embedding_model=resolved_model)
62
64
  self.backend = "HMR"
63
65
  self.version = getattr(self._hmr, "VERSION", "2.0.0")
64
- # 并行子 Agent 会并发 ingest/recall,底层 HMR 的向量库/记忆图并非线程安全
65
- # (向量索引并发 add+search 是未定义行为)。串行化对 HMR 的访问。叶子组件:
66
- # 锁内不回调 pm/bus,故不参与全局锁序,无死锁风险。
67
66
  self._lock = threading.RLock()
68
67
 
69
- # HMR 未给 title 时会退化成 "Concept 03:11" 这类纯时间戳(见 hmr.py 的
70
- # `title or f"{type.title()} {HH:MM}"`),在前端"记忆信息条"里一眼看不出是什么。
71
68
  _AUTO_TITLE = re.compile(r"^[A-Za-z_]+\s+\d{1,2}:\d{2}$")
72
69
 
73
70
  @staticmethod
@@ -80,23 +77,27 @@ class HMRAdapter:
80
77
  if s.startswith(pfx):
81
78
  s = s[len(pfx):].strip()
82
79
  break
83
- for sep in ("→", "->"): # 对话记忆是"问 → 答",标题取问
80
+ for sep in ("→", "->"):
84
81
  if sep in s:
85
82
  s = s.split(sep, 1)[0].strip()
86
83
  break
87
84
  s = re.split(r"[。!?!?\n\r]", s, 1)[0].strip()
88
85
  return " ".join(s.split())[:24]
89
86
 
90
- # ── 记忆摄入 ──────────────────────────────────────────
91
- def ingest(self, content: str, type: str = "concept", agent_id: str = None,
92
- title: str = None, tags: list = None, shared: bool = False, **kwargs):
87
+ def ingest(self, content: str, type: str = "concept", agent_id: str | None = None,
88
+ title: str | None = None, tags: list | None = None, shared: bool = False,
89
+ consent: bool = False, **kwargs):
93
90
  """统一摄入接口。agent_id 写入 tags 实现隔离;shared=True 则写入共享层。
94
- 未给 title 时从内容提炼一个可读标题(否则 HMR 会生成纯时间戳标题)。"""
91
+ 未给 title 时从内容提炼一个可读标题(否则 HMR 会生成纯时间戳标题)。
92
+
93
+ consent=True 表示"用户已对该条高敏感内容点头",跳过敏感闸门。唯一允许的调用方是
94
+ `FoxclawOS.approve_sensitive_write`(与 PolicyGate 的 confirmed=True 同一模式):
95
+ 别处传 True 等于绕过同意流程。"""
95
96
  meta_tags = list(tags or [])
96
97
  if agent_id:
97
98
  meta_tags.append(f"agent:{agent_id}")
98
99
  if shared:
99
- meta_tags.append("shared") # 共享记忆:所有 Agent 可召回
100
+ meta_tags.append("shared")
100
101
  metadata = {"tags": meta_tags}
101
102
  if "confidence" in kwargs:
102
103
  metadata["confidence"] = kwargs["confidence"]
@@ -105,15 +106,19 @@ class HMRAdapter:
105
106
  mtype = type if type in valid else "concept"
106
107
  if not title:
107
108
  title = self._derive_title(content) or None
108
- # 【P0-3】共享层写入闸门:必须在**落库之前**判定(见 core/shared_write.py)。
109
- # 未挂策略时返回 None 直接放行;audit 只记事件;enforce 才抛 SharedWriteDenied。
110
109
  if shared:
111
110
  guard_shared_write(self, agent_id, content, mtype, meta_tags)
111
+ if not consent:
112
+ blocked = guard_sensitive_write(self, agent_id, content, mtype)
113
+ if blocked is not None and not blocked.allowed:
114
+ # 高敏感内容不落库:进待确认队列,由用户同意后再写(见 core/sensitive_write.py)
115
+ return SimpleNamespace(id="", type=mtype, pending=True,
116
+ pending_id=blocked.pending_id, reason=blocked.reason,
117
+ categories=list(blocked.categories))
112
118
  with self._lock:
113
119
  return self._hmr.ingest(content, memory_type=mtype, title=title, metadata=metadata)
114
120
 
115
- # ── 记忆召回 ──────────────────────────────────────────
116
- def recall(self, query: str = "", agent_id: str = None, top_k: int = 5,
121
+ def recall(self, query: str = "", agent_id: str | None = None, top_k: int = 5,
117
122
  include_shared: bool = True, **kwargs):
118
123
  """统一召回接口。返回 list[dict]。
119
124
  默认召回:本 Agent 的记忆 + 共享层(shared)记忆;include_shared=False 只看自己。
@@ -153,8 +158,7 @@ class HMRAdapter:
153
158
  "tags": tags,
154
159
  "_ts": getattr(m, "created_at", None) or getattr(m, "timestamp", None) or "",
155
160
  })
156
- out.sort(key=lambda x: str(x["_ts"]), reverse=True) # 新近优先
157
- # 按内容去重(保留最新):否则同一偏好反复表达存成多条,会挤满 limit、把别的偏好挤出召回
161
+ out.sort(key=lambda x: str(x["_ts"]), reverse=True)
158
162
  deduped, seen_c = [], set()
159
163
  for x in out:
160
164
  if x["content"] in seen_c:
@@ -195,7 +199,7 @@ class HMRAdapter:
195
199
  try:
196
200
  with self._lock:
197
201
  mems = fs.list_memories()
198
- cand = [] # tag 预过滤:本 Agent 私有 + 共享
202
+ cand = []
199
203
  for m in mems:
200
204
  tags = getattr(m, "tags", []) or []
201
205
  if agent_id:
@@ -210,7 +214,7 @@ class HMRAdapter:
210
214
  scored = []
211
215
  for m, tags in cand:
212
216
  vec = vs.vectors.get(getattr(m, "id", None))
213
- if vec is None: # 少见:该记忆尚无向量 → 现算
217
+ if vec is None:
214
218
  try:
215
219
  text = getattr(m, "semantic_summary", "") or (m.content or "")[:500]
216
220
  vec = vs.embedder.embed(text)
@@ -232,7 +236,7 @@ class HMRAdapter:
232
236
  })
233
237
  return out
234
238
  except Exception:
235
- return None # 任何异常 → 回退老路径,绝不因根治优化而丢召回能力
239
+ return None
236
240
 
237
241
  def _legacy_recall(self, query, agent_id, top_k, include_shared):
238
242
  """回退路径:全局召回再按 tag 过滤(HMR 结构不可访问时用)。有完整性局限,见 recall。"""
@@ -250,7 +254,7 @@ class HMRAdapter:
250
254
  own = f"agent:{agent_id}" in tags
251
255
  shared = include_shared and ("shared" in tags)
252
256
  if not (own or shared):
253
- continue # 既不是本 Agent 的,也不是共享的 → 跳过(隔离保证)
257
+ continue
254
258
  out.append({
255
259
  "id": getattr(m, "id", ""),
256
260
  "title": getattr(m, "title", ""),
@@ -264,11 +268,6 @@ class HMRAdapter:
264
268
  break
265
269
  return out
266
270
 
267
- # ── 工作区 ────────────────────────────────────────────
268
- # 【深审修复·补齐锁覆盖】适配层自述"串行化**所有** HMR 访问"(底层向量库/记忆图非线程安全),
269
- # 但此前只有 ingest/recall 加锁;下列同样触碰 self._hmr 的方法漏了锁——与并发 ingest/recall
270
- # (并行子 Agent / 并发请求)同时跑会读到 mutation 中的向量索引/字典(未定义行为/迭代崩溃)。
271
- # 统一补 self._lock(RLock 叶子锁,锁内不回调 pm/bus,无死锁)。
272
271
  def get_workspace(self, agent_id: str):
273
272
  with self._lock:
274
273
  return self._hmr.get_workspace(agent_id)
@@ -277,7 +276,6 @@ class HMRAdapter:
277
276
  with self._lock:
278
277
  return self._hmr.save_workspace(agent_id)
279
278
 
280
- # ── 运行时状态 ────────────────────────────────────────
281
279
  def save_runtime_state(self, **kwargs):
282
280
  with self._lock:
283
281
  return self._hmr.save_runtime_state(**kwargs)
@@ -286,7 +284,6 @@ class HMRAdapter:
286
284
  with self._lock:
287
285
  return self._hmr.restore_runtime_state()
288
286
 
289
- # ── 系统状态 ──────────────────────────────────────────
290
287
  def get_system_status(self):
291
288
  try:
292
289
  with self._lock:
@@ -307,8 +304,10 @@ class HMRAdapter:
307
304
 
308
305
  def _ts(m):
309
306
  v = getattr(m, "created_at", None)
307
+ if v is None:
308
+ return 0.0
310
309
  try:
311
- return v.timestamp() if hasattr(v, "timestamp") else float(v or 0)
310
+ return v.timestamp() if hasattr(v, "timestamp") else float(v)
312
311
  except Exception:
313
312
  return 0.0
314
313
 
@@ -325,11 +324,12 @@ class HMRAdapter:
325
324
  break
326
325
  created = getattr(m, "created_at", None)
327
326
  title = (getattr(m, "title", "") or "").strip()
328
- # 历史记忆多是 "Concept 03:11" 这类时间戳标题(修复前存的)→ 展示时现推一个可读的,
329
- # 让老数据也能看懂;推不出来才退回原标题。
330
327
  if not title or self._AUTO_TITLE.match(title):
331
328
  title = self._derive_title(
332
329
  getattr(m, "content", "") or getattr(m, "semantic_summary", "")) or title
330
+ created_at = str(created or "")
331
+ if created is not None and hasattr(created, "isoformat"):
332
+ created_at = created.isoformat()
333
333
  out.append({
334
334
  "id": getattr(m, "id", ""),
335
335
  "type": getattr(m, "type", ""),
@@ -337,7 +337,7 @@ class HMRAdapter:
337
337
  "summary": (getattr(m, "semantic_summary", "") or getattr(m, "content", "") or "")[:180],
338
338
  "agent": agent,
339
339
  "shared": "shared" in tags,
340
- "created_at": created.isoformat() if hasattr(created, "isoformat") else str(created or ""),
340
+ "created_at": created_at,
341
341
  })
342
342
  return out
343
343
 
@@ -346,17 +346,11 @@ class HMRAdapter:
346
346
  total = 0
347
347
  relations = 0
348
348
  shared_count = 0
349
- by_agent = {}
349
+ by_agent: dict[str, Any] = {}
350
350
  embedding = "TF-IDF n-gram"
351
351
  try:
352
- # 序列化对 HMR 内部结构(向量库/记忆图)的读,与 ingest/recall 的写互斥,
353
- # 避免并发迭代 store.memories("dict changed size")/读 mutation 中的 faiss 索引。
354
352
  with self._lock:
355
- # 权威来源:memory_fs 里的真实记忆对象。
356
- # 历史 bug:此前从 vector_store 探测 count/size/memories 取 total——但真实记忆存在
357
- # memory_fs,vector_store 只存嵌入且无这些属性 → total 恒为 0,而 relations 却有值(109),
358
- # 自相矛盾。改为直接数 memory_fs.list_memories()(与 get_statistics 同源)。
359
- mems = []
353
+ mems: list[Any] = []
360
354
  mfs = getattr(self._hmr, "memory_fs", None)
361
355
  if mfs is not None and hasattr(mfs, "list_memories"):
362
356
  try:
@@ -364,12 +358,11 @@ class HMRAdapter:
364
358
  except Exception:
365
359
  mems = []
366
360
  total = len(mems)
367
- if not total: # 兜底:memory_fs 不可用 → 数 vector_store.vectors(dict)
361
+ if not total:
368
362
  vs0 = getattr(self._hmr, "vector_store", None)
369
363
  vecs = getattr(vs0, "vectors", None) if vs0 is not None else None
370
364
  if isinstance(vecs, dict):
371
365
  total = len(vecs)
372
- # shared / 按 Agent 分布:agent_id 编码在 tags 里的 "agent:<id>"
373
366
  for m in mems:
374
367
  tags = getattr(m, "tags", None)
375
368
  if tags is None and isinstance(m, dict):
@@ -386,7 +379,6 @@ class HMRAdapter:
386
379
  edges = getattr(graph, "edges", None)
387
380
  if edges is not None:
388
381
  relations = len(edges) if hasattr(edges, "__len__") else 0
389
- # embedding 模型探测
390
382
  emb = getattr(self._hmr, "embedding", None) or getattr(self._hmr, "embedder", None)
391
383
  if emb is not None:
392
384
  name = type(emb).__name__
@@ -423,19 +415,20 @@ class MockHMR:
423
415
  def save_workspace(self, agent_id: str):
424
416
  pass
425
417
 
426
- def ingest(self, content, type="concept", agent_id=None, shared=False, **kwargs):
427
- # 【P0-3】与 HMRAdapter 同一道闸门:Mock 后端也过策略。否则在无 HMR 的环境里
428
- # 策略形同虚设 —— 而测试与降级场景恰好都落在这一支,那是最需要行为一致的地方。
418
+ def ingest(self, content, type="concept", agent_id=None, shared=False,
419
+ consent=False, **kwargs):
429
420
  if shared:
430
421
  guard_shared_write(self, agent_id, content, type, kwargs.get("tags") or [])
431
- from types import SimpleNamespace
432
- # 注意:不能用内置 type() 构造对象——参数名 type 已覆盖内置 type。
422
+ if not consent:
423
+ blocked = guard_sensitive_write(self, agent_id, content, type)
424
+ if blocked is not None and not blocked.allowed:
425
+ return SimpleNamespace(id="", type=type, pending=True,
426
+ pending_id=blocked.pending_id, reason=blocked.reason,
427
+ categories=list(blocked.categories))
433
428
  return SimpleNamespace(id="mock_mem", type=type)
434
429
 
435
- def recall(self, query: str = "", agent_id: str = None, top_k: int = 5, **kwargs):
430
+ def recall(self, query: str = "", agent_id: str | None = None, top_k: int = 5, **kwargs):
436
431
  from types import SimpleNamespace
437
- # 返回带 memory_objects 的对象,与真实 HMR 的 RecallResult 结构一致
438
- # (HMRAdapter.recall 会读 result.memory_objects)。
439
432
  return SimpleNamespace(memory_objects=[])
440
433
 
441
434
  def save_runtime_state(self, **kwargs):
@@ -484,7 +477,7 @@ def _memory_chain_enabled() -> bool:
484
477
  return raw.lower() in ("1", "true", "yes", "on")
485
478
 
486
479
 
487
- def create_memory_backend(storage_path: str = "./hmr_data", llm_api_key: str = None):
480
+ def create_memory_backend(storage_path: str = "./hmr_data", llm_api_key: str | None = None):
488
481
  """工厂:默认 HMR;`FOXCLAW_MEMORY_BACKEND=vecrecall` 时才切 vecRecall。
489
482
 
490
483
  顺序 vecRecall → HMR → MockHMR,任一层失败退到下一层,且**明确打印**实际用的是
@@ -508,9 +501,9 @@ def create_memory_backend(storage_path: str = "./hmr_data", llm_api_key: str = N
508
501
  print(f"[Foxclaw] ⚠ FOXCLAW_MEMORY_BACKEND={choice!r} 无法识别,按 HMR 处理")
509
502
 
510
503
  try:
511
- adapter = HMRAdapter(storage_path=storage_path, llm_api_key=llm_api_key)
512
- print(f"[Foxclaw] ✓ HMR v{adapter.version} 已接入 (backend={adapter.backend})")
513
- return adapter
504
+ hmr = HMRAdapter(storage_path=storage_path, llm_api_key=llm_api_key)
505
+ print(f"[Foxclaw] ✓ HMR v{hmr.version} 已接入 (backend={hmr.backend})")
506
+ return hmr
514
507
  except Exception as e:
515
508
  print(f"[Foxclaw] ⚠ HMR 不可用 ({e.__class__.__name__}: {e}),降级 MockHMR")
516
509
  return MockHMR()
@@ -5,7 +5,7 @@ import time
5
5
  import threading
6
6
  from collections import defaultdict, deque
7
7
 
8
- _MAX_SAMPLES = 500 # 每个 key 保留最近 N 次延迟(有界,防内存涨)
8
+ _MAX_SAMPLES = 500
9
9
 
10
10
 
11
11
  class _Stat:
@@ -14,7 +14,7 @@ class _Stat:
14
14
  def __init__(self):
15
15
  self.count = 0
16
16
  self.errors = 0
17
- self.samples = deque(maxlen=_MAX_SAMPLES)
17
+ self.samples: deque[float] = deque(maxlen=_MAX_SAMPLES)
18
18
 
19
19
 
20
20
  def _pct(sorted_vals, p: float) -> float:
@@ -29,8 +29,8 @@ class MetricsCollector:
29
29
 
30
30
  def __init__(self):
31
31
  self._lock = threading.RLock()
32
- self._ep: dict = defaultdict(_Stat) # endpoint(路由模板) -> _Stat
33
- self._llm: dict = defaultdict(_Stat) # provider -> _Stat
32
+ self._ep: dict = defaultdict(_Stat)
33
+ self._llm: dict = defaultdict(_Stat)
34
34
  self._start = time.time()
35
35
 
36
36
  def record_request(self, endpoint: str, status: int, ms: float):
@@ -85,5 +85,4 @@ class MetricsCollector:
85
85
  return "\n".join(out) + "\n"
86
86
 
87
87
 
88
- # 进程级单例:中间件 + router 都往这里写,端点从这里读。
89
88
  METRICS = MetricsCollector()