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,227 @@
1
+ """
2
+ hkc-kee / dedup.py
3
+ 知识级去重器 (KnowledgeDeduplicator)
4
+
5
+ KEE 作为知识身份的唯一权威,在此实现"知识级去重":
6
+ 新 KU 进入时,用 canonical identity 判断库中是否已存在同一知识。
7
+ 命中 → 合并 Evidence 到已有 KU(不新建重复 KU)
8
+ 未命中 → 登记身份键,放行
9
+
10
+ 身份判定(确定性,无向量依赖):
11
+ - 主身份 = type + 规范化 name(statement) + domain
12
+ - 别名 = LLM 抽取的 aliases(如「北平」的别名「北京」),与主名等价
13
+ 用「别名确定性匹配」取代「向量语义近重」:零模型依赖、零余弦误合并。
14
+ LLM 未填别名时自然退化为纯 exact 去重,行为不变。
15
+
16
+ 与 Crystallizer 的 light fingerprint 分工:
17
+ Crystallizer: event 级去重(避免重复消费同一事件)
18
+ KEE(这里): knowledge 级去重(避免同一知识因多来源而重复建 KU)
19
+
20
+ 架构铁律的落点:
21
+ "KEE is the sole authority for knowledge deduplication and evolution."
22
+ 身份索引由 KEE 维护(不污染 GraphStore schema),冷启动时可从全量 KU 重建。
23
+ """
24
+ from __future__ import annotations
25
+ import logging
26
+ import threading
27
+ from typing import Optional, Callable
28
+
29
+ from .fingerprint import identity_keys, _identity_name
30
+
31
+ logger = logging.getLogger(__name__)
32
+
33
+
34
+ def _clip(s: str, n: int = 40) -> str:
35
+ """截断长名,避免 Fact/Claim 的 statement 刷屏日志。"""
36
+ s = s or ""
37
+ return s if len(s) <= n else s[:n] + "…"
38
+
39
+
40
+ class KnowledgeDeduplicator:
41
+ """
42
+ 维护 canonical identity fingerprint → ku_id 索引,提供查重 + Evidence 合并。
43
+
44
+ 参数:
45
+ graph_store: 用于查 KU、写回合并后的 KU。
46
+ merge_evidence_fn: 可选的 Evidence 合并函数 (existing_ku, new_ku) -> bool。
47
+ 返回 True 表示发生了合并(有新证据加入)。
48
+ 不传则用内置默认合并(基于 supports / evidence 列表)。
49
+ """
50
+
51
+ def __init__(self, graph_store, merge_evidence_fn: Optional[Callable] = None):
52
+ self.store = graph_store
53
+ self._index: dict[str, str] = {} # identity fingerprint → ku_id
54
+ self._merge_fn = merge_evidence_fn or self._default_merge
55
+ self._rebuilt = False
56
+ # 去重在 knowledge.created 处理器里跑(摄入线程)。两个并发摄入任务会同时
57
+ # check_and_merge → 竞态漏去重/半重建索引。RLock 序列化,保证"查重+登记"原子。
58
+ self._lock = threading.RLock()
59
+
60
+ # ── 索引维护 ──────────────────────────────────────────────
61
+
62
+ def rebuild_index(self) -> int:
63
+ """从 GraphStore 全量 KU 重建身份索引(冷启动/恢复用)。返回索引条目数。"""
64
+ with self._lock:
65
+ self._index.clear()
66
+ try:
67
+ all_kus = self.store.query_all(limit=100000)
68
+ except Exception as e:
69
+ logger.warning("重建指纹索引失败,查询全量 KU 出错: %s", e)
70
+ return 0
71
+ for ku in all_kus:
72
+ if self._is_structural_source(ku):
73
+ continue # 来源层级节点(合集/文件)不进按名去重索引
74
+ for k in identity_keys(ku):
75
+ # 已存在则保留最早的(先到先得),避免后来者覆盖
76
+ self._index.setdefault(k, ku.ku_id)
77
+ self._rebuilt = True
78
+ logger.info("身份索引重建完成,共 %d 个身份键", len(self._index))
79
+ return len(self._index)
80
+
81
+ def _ensure_index(self):
82
+ with self._lock:
83
+ if not self._rebuilt:
84
+ self.rebuild_index()
85
+
86
+ # ── 核心:查重 + 合并 ─────────────────────────────────────
87
+
88
+ # 来源层级结构节点的标记 tag(与 hkc_kde/packager.py 约定一致):
89
+ # _collection → 合集(文件夹)节点
90
+ # _incol:<合集id> → 属于某合集的文件(章节)节点
91
+ @staticmethod
92
+ def _is_structural_source(ku) -> bool:
93
+ """是否为"来源层级结构节点"(合集/文件 Evidence)。这类节点身份由结构决定
94
+ (合集 find-or-create、文件按文件独立),**不参与按名去重**——否则不同文件夹里
95
+ 同名文件(README.md / 01.md 等)会被误合并,层级与 provenance 错乱。"""
96
+ if getattr(getattr(ku, "ku_type", None), "value", "") != "Evidence":
97
+ return False
98
+ return any(t == "_collection" or t.startswith("_incol:")
99
+ for t in (getattr(ku, "tags", None) or []))
100
+
101
+ def check_and_merge(self, new_ku) -> Optional[str]:
102
+ """
103
+ 对新建的 KU 做知识级查重。
104
+
105
+ 返回:
106
+ None → 未命中(是新知识),已登记身份键,调用方应正常处理该 KU
107
+ existing_id → 命中已有知识,已把新 KU 的 Evidence 合并进去;
108
+ 调用方应放弃 new_ku(不再作为独立 KU 处理)
109
+ """
110
+ # 来源层级节点(合集/文件 Evidence)不按名去重,直接放行为独立节点
111
+ if self._is_structural_source(new_ku):
112
+ return None
113
+ with self._lock: # 整个"查重 + 登记/合并"必须原子,防并发漏去重
114
+ self._ensure_index()
115
+ keys = identity_keys(new_ku)
116
+ new_id = getattr(new_ku, "ku_id", None)
117
+
118
+ # 任一身份键(主名或别名)命中已有 KU → 判为重复
119
+ existing_id = None
120
+ hit_kind = "主名" # 命中的身份键类型(主名 / 别名),供日志诊断
121
+ for i, k in enumerate(keys):
122
+ eid = self._index.get(k)
123
+ if eid and eid != new_id:
124
+ existing_id = eid
125
+ hit_kind = "主名" if i == 0 else "别名"
126
+ break
127
+
128
+ if existing_id is None:
129
+ # 确无重复 → 登记所有身份键(先到先得)并放行
130
+ for k in keys:
131
+ self._index.setdefault(k, new_id)
132
+ return None
133
+
134
+ # 命中已有 KU → 合并 Evidence
135
+ existing_ku = self.store.get(existing_id)
136
+ if existing_ku is None:
137
+ # 索引指向的 KU 已不存在(被删),用新 KU 顶替
138
+ for k in keys:
139
+ self._index[k] = new_id
140
+ return None
141
+
142
+ try:
143
+ merged = self._merge_fn(existing_ku, new_ku)
144
+ except Exception as e:
145
+ logger.warning("Evidence 合并失败 (%s ← %s): %s", existing_id, new_id, e)
146
+ merged = False
147
+
148
+ # 重定向重复 KU 的关系到 canonical KU(避免悬空),再软删除重复 KU
149
+ redirected = 0
150
+ if new_id and new_id != existing_id:
151
+ if hasattr(self.store, "redirect_relations"):
152
+ try:
153
+ redirected = self.store.redirect_relations(new_id, existing_id)
154
+ except Exception as e:
155
+ logger.warning("关系重定向失败 (%s → %s): %s", new_id, existing_id, e)
156
+
157
+ logger.info("知识级去重命中: new=%s 合并入 existing=%s "
158
+ "(键=%s, 有新证据=%s, 重定向关系=%d) new名=%r existing名=%r",
159
+ new_id, existing_id, hit_kind, merged, redirected,
160
+ _clip(_identity_name(new_ku)),
161
+ _clip(_identity_name(existing_ku)))
162
+ return existing_id
163
+
164
+ def register(self, ku) -> None:
165
+ """显式登记一个 KU 的身份键(用于已知是新知识、跳过查重的场景)。"""
166
+ with self._lock:
167
+ self._ensure_index()
168
+ kid = getattr(ku, "ku_id", None)
169
+ for k in identity_keys(ku):
170
+ self._index.setdefault(k, kid)
171
+
172
+ # ── 默认 Evidence 合并 ────────────────────────────────────
173
+
174
+ def _default_merge(self, existing_ku, new_ku) -> bool:
175
+ """
176
+ 默认合并策略:把 new_ku 的来源证据并入 existing_ku。
177
+
178
+ 合并的证据载体:
179
+ - KU 基类通用的 sources(EVD_xxx 列表,所有 KU 类型都有)
180
+ - Claim 的 supports(支持性证据 KU id 列表)
181
+ 合并后去重,并按新增证据数微调置信度(每条 +0.02,上限 0.98;
182
+ Fact 置信度固定 1.0 不动)。
183
+ """
184
+ added = 0
185
+
186
+ # 通用 sources(所有 KU 都有)
187
+ new_sources = getattr(new_ku, "sources", None)
188
+ if new_sources:
189
+ old_sources = getattr(existing_ku, "sources", None)
190
+ if old_sources is not None:
191
+ fresh = [s for s in new_sources if s not in old_sources]
192
+ old_sources.extend(fresh)
193
+ added += len(fresh)
194
+
195
+ # Claim.supports(支持性证据)
196
+ new_supports = getattr(new_ku, "supports", None)
197
+ if new_supports:
198
+ old_supports = getattr(existing_ku, "supports", None)
199
+ if old_supports is not None:
200
+ fresh = [s for s in new_supports if s not in old_supports]
201
+ old_supports.extend(fresh)
202
+ added += len(fresh)
203
+
204
+ if added > 0:
205
+ conf = getattr(existing_ku, "confidence", None)
206
+ if conf is not None and not _is_fixed_confidence(existing_ku):
207
+ # 加证据只该「抬高」置信度。原来的 min(0.98, conf+..) 在本就 1.0 的
208
+ # KU 上会把它**降**到 0.98 —— 多一条证据反而变不确定,明显是笔误。
209
+ existing_ku.confidence = max(conf, min(0.98, conf + added * 0.02))
210
+ # 只写「被合并改动的那几处」,不整行回写:existing_ku 是几微秒前读出来的
211
+ # 快照,整行 put() 会把期间别人写进 extra 的东西(如复习进度 review)按旧快照
212
+ # 覆盖掉。这里 sources/confidence 是普通列,supports 在 extra 里。
213
+ cols = {"sources": list(existing_ku.sources or []),
214
+ "confidence": existing_ku.confidence}
215
+ ext = {}
216
+ if new_supports:
217
+ ext["supports"] = list(existing_ku.supports or [])
218
+ if self.store.patch_ku(existing_ku.ku_id, columns=cols, extra=ext or None) == 0:
219
+ self.store.put(existing_ku) # 行已不在:退回插入语义
220
+
221
+ return added > 0
222
+
223
+
224
+ def _is_fixed_confidence(ku) -> bool:
225
+ """Fact 类型置信度固定为 1.0,不应被证据合并改动。"""
226
+ ku_type = getattr(getattr(ku, "ku_type", None), "value", "") or str(getattr(ku, "ku_type", ""))
227
+ return ku_type.lower() == "fact"
@@ -0,0 +1,106 @@
1
+ """
2
+ hkc-kee / fingerprint.py
3
+ 知识规范指纹 (Canonical Fingerprint)
4
+
5
+ 与 Crystallizer 的 light fingerprint 分工:
6
+
7
+ 层 指纹类型 视角 用途
8
+ ─────────────────────────────────────────────────────────
9
+ Crystallizer light(结构哈希) 事件视角 粗去重标签,不做决策
10
+ KEE canonical(语义规范) 知识视角 知识身份判定,去重权威
11
+
12
+ canonical fingerprint 回答的问题是:
13
+ "这两个 KU 在知识层面是不是同一个东西?"
14
+
15
+ 它基于 KU 的**知识身份要素**(类型 + 规范化名称 + 领域),而非原始字节。
16
+ 所以"价值投资"和"价值投资 "(尾空格)、"Value Investing"的不同记录,
17
+ 只要规范化后语义身份相同,canonical fingerprint 就相同 → 判为同一知识。
18
+
19
+ 注:这里**有意**只做规则化规范(normalize)而非向量语义 —— 它是**又快又准的 exact 快路径**,
20
+ 覆盖"同一知识的不同来源重复录入"这一主要去重场景。
21
+ 同义词/近义表达(如「北平」↔「北京」)交给**别名(aliases)确定性匹配**:LLM 抽取时
22
+ 填写的别名与主名等价,经 identity_keys 一并纳入身份指纹,无需向量模型、零误合并。
23
+ 别把向量塞进指纹,那会让每次查重都要编码。
24
+ """
25
+ from __future__ import annotations
26
+ import hashlib
27
+ import unicodedata
28
+
29
+
30
+ def normalize_name(name: str) -> str:
31
+ """
32
+ 规范化知识名称,用于身份比对。
33
+
34
+ 步骤:Unicode NFKC 归一 → 转小写 → 仅保留字母与数字(去除所有标点、
35
+ 空白、符号、连字符等)→ strip。
36
+ 目的:让"价值投资"、"价值投资 "、"价值-投资"、"Value Investing"
37
+ 规范化后落到同一规范形。
38
+
39
+ 用 Unicode 类别过滤(保留 L* 字母 / N* 数字),比手写标点字符类更稳健,
40
+ 天然覆盖中英文标点、全角半角、各种连字符。
41
+ """
42
+ if not name:
43
+ return ""
44
+ s = unicodedata.normalize("NFKC", name).lower()
45
+ out = []
46
+ for ch in s:
47
+ cat = unicodedata.category(ch)
48
+ if cat[0] in ("L", "N"): # Letter / Number
49
+ out.append(ch)
50
+ return "".join(out)
51
+
52
+
53
+ def canonical_fingerprint(
54
+ ku_type: str,
55
+ name: str,
56
+ domain: str = "",
57
+ ) -> str:
58
+ """
59
+ 计算 KU 的规范指纹。
60
+
61
+ 身份要素:类型 + 规范化名称 + 规范化领域。
62
+ 同类型、同规范名、同领域 → 同指纹 → 知识层面视为同一 KU。
63
+
64
+ 返回:sha256 十六进制(前 24 位,碰撞概率可忽略)。
65
+ """
66
+ parts = [
67
+ (ku_type or "").strip().lower(),
68
+ normalize_name(name),
69
+ normalize_name(domain),
70
+ ]
71
+ key = "\x1f".join(parts) # 用不可见分隔符避免拼接歧义
72
+ return hashlib.sha256(key.encode("utf-8")).hexdigest()[:24]
73
+
74
+
75
+ def _identity_name(ku) -> str:
76
+ """知识身份名:Fact/Claim 用完整 statement(避免截断 name 造成误合并),
77
+ 其余类型用 name/title。"""
78
+ stmt = getattr(ku, "statement", "") or ""
79
+ if stmt:
80
+ return stmt
81
+ return getattr(ku, "name", "") or getattr(ku, "title", "") or ""
82
+
83
+
84
+ def fingerprint_of_ku(ku) -> str:
85
+ """从一个 KU 对象算 canonical fingerprint(主身份,不含别名)。"""
86
+ ku_type = getattr(getattr(ku, "ku_type", None), "value", None) or str(getattr(ku, "ku_type", ""))
87
+ domain = getattr(ku, "domain", "") or ""
88
+ return canonical_fingerprint(ku_type, _identity_name(ku), domain)
89
+
90
+
91
+ def identity_keys(ku) -> list[str]:
92
+ """返回 KU 的全部身份指纹(主名 + 所有别名),供去重索引登记/查询。
93
+
94
+ 别名纳入身份:LLM 抽取的 aliases(如「北平」的别名「北京」)与主名等价,
95
+ 让「北平」和「北京」落到同一 canonical 身份 —— 用确定性别名匹配取代向量语义近重。
96
+ 无 aliases 时退化为单主名,与纯 exact 去重等价。"""
97
+ ku_type = getattr(getattr(ku, "ku_type", None), "value", None) or str(getattr(ku, "ku_type", ""))
98
+ domain = getattr(ku, "domain", "") or ""
99
+ keys = [canonical_fingerprint(ku_type, _identity_name(ku), domain)]
100
+ for alias in (getattr(ku, "aliases", None) or []):
101
+ if not alias:
102
+ continue
103
+ k = canonical_fingerprint(ku_type, alias, domain)
104
+ if k not in keys:
105
+ keys.append(k)
106
+ return keys