ltcai 11.5.2 → 11.7.0

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 (443) hide show
  1. package/README.md +93 -148
  2. package/bin/ltcai.js +234 -24
  3. package/docs/CHANGELOG.md +119 -0
  4. package/docs/COMMUNITY_AND_PLUGINS.md +1 -1
  5. package/docs/DEVELOPMENT.md +8 -6
  6. package/docs/ENTERPRISE.md +2 -1
  7. package/docs/MULTI_AGENT_RUNTIME.md +12 -5
  8. package/docs/ONBOARDING.md +1 -1
  9. package/docs/OPERATIONS.md +6 -3
  10. package/docs/REALTIME_COLLABORATION.md +1 -1
  11. package/docs/TRUST_MODEL.md +1 -1
  12. package/docs/WHY_LATTICE.md +1 -1
  13. package/docs/WORKFLOW_DESIGNER.md +3 -2
  14. package/docs/kg-schema.md +1 -1
  15. package/docs/v11.6.0_ONE_DOOR_PLAN.md +170 -0
  16. package/lattice_brain/__init__.py +42 -79
  17. package/lattice_brain/graph/__init__.py +9 -24
  18. package/lattice_brain/graph/_kg_common/__init__.py +13 -24
  19. package/lattice_brain/ingestion/__init__.py +19 -58
  20. package/lattice_brain/ingestion/pipeline.py +20 -398
  21. package/lattice_brain/multimodal/__init__.py +7 -18
  22. package/lattice_brain/multimodal/images.py +6 -264
  23. package/lattice_brain/multimodal/video.py +8 -247
  24. package/lattice_brain/runtime/__init__.py +8 -79
  25. package/lattice_brain/runtime/hooks.py +22 -584
  26. package/latticeai/__init__.py +1 -1
  27. package/latticeai/api/agent_worker_seam.py +7 -80
  28. package/latticeai/api/health.py +6 -20
  29. package/latticeai/api/local_files.py +16 -613
  30. package/latticeai/api/models.py +12 -87
  31. package/latticeai/api/search.py +17 -255
  32. package/latticeai/api/tools.py +69 -750
  33. package/latticeai/api/voice_capture.py +8 -70
  34. package/latticeai/api/worker_compute.py +842 -0
  35. package/latticeai/api/worker_seams.py +216 -0
  36. package/latticeai/app_factory.py +23 -232
  37. package/latticeai/cli/entrypoint.py +36 -249
  38. package/latticeai/core/agent_permission.py +16 -91
  39. package/latticeai/core/messages.py +89 -531
  40. package/latticeai/runtime/access_runtime.py +0 -14
  41. package/latticeai/runtime/bootstrap.py +10 -21
  42. package/latticeai/runtime/brain_runtime.py +35 -43
  43. package/latticeai/runtime/build_phases/__init__.py +21 -37
  44. package/latticeai/runtime/build_phases/features.py +99 -389
  45. package/latticeai/runtime/build_phases/foundation.py +82 -392
  46. package/latticeai/runtime/build_phases/web.py +91 -408
  47. package/latticeai/runtime/build_phases/worker_profile.py +244 -0
  48. package/latticeai/runtime/lifespan_runtime.py +7 -16
  49. package/latticeai/runtime/platform_services_runtime.py +1 -15
  50. package/latticeai/runtime/runtime_context.py +13 -130
  51. package/latticeai/runtime/security_runtime.py +10 -103
  52. package/latticeai/services/architecture_readiness.py +82 -43
  53. package/latticeai/services/model_runtime/__init__.py +2 -11
  54. package/latticeai/services/model_runtime/service.py +4 -28
  55. package/latticeai/services/p_reinforce.py +10 -261
  56. package/latticeai/services/product_readiness.py +31 -38
  57. package/latticeai/services/search_service.py +37 -795
  58. package/latticeai/services/tool_dispatch.py +30 -386
  59. package/latticeai/services/voice_capture.py +13 -107
  60. package/latticeai/tools/__init__.py +22 -49
  61. package/latticeai/tools/commands.py +0 -163
  62. package/latticeai/tools/computer.py +0 -39
  63. package/latticeai/tools/documents.py +1 -134
  64. package/latticeai/tools/filesystem.py +1 -247
  65. package/latticeai/tools/knowledge.py +1 -52
  66. package/latticeai/tools/local_files.py +0 -20
  67. package/latticeai/worker_app.py +75 -0
  68. package/package.json +2 -3
  69. package/requirements.txt +0 -5
  70. package/scripts/agent_eval.py +16 -28
  71. package/scripts/brain_quality_eval.py +20 -183
  72. package/scripts/bump_version.py +5 -5
  73. package/scripts/check_current_release_docs.mjs +7 -4
  74. package/scripts/check_openapi_drift.mjs +10 -0
  75. package/scripts/check_server_i18n.mjs +2 -18
  76. package/scripts/compose_openapi.py +377 -0
  77. package/scripts/export_openapi.py +32 -4
  78. package/scripts/gen_messages_catalog_fixture.py +302 -0
  79. package/scripts/gen_openapi_fragments.py +365 -0
  80. package/scripts/gen_redact_fixture.py +196 -0
  81. package/scripts/gen_worker_allowlist_fixture.py +115 -0
  82. package/scripts/generate_agent_parity_fixtures.py +33 -14
  83. package/scripts/openapi_route_families.json +2161 -0
  84. package/scripts/release_screen_claims.json +54 -0
  85. package/scripts/run_integration_tests.mjs +134 -29
  86. package/scripts/run_sidecar_e2e.mjs +104 -11
  87. package/scripts/wheel_smoke.py +32 -22
  88. package/src-tauri/Cargo.lock +375 -9
  89. package/src-tauri/Cargo.toml +1 -1
  90. package/src-tauri/src/backend.rs +151 -45
  91. package/src-tauri/src/main.rs +18 -18
  92. package/src-tauri/src/topology.rs +58 -88
  93. package/src-tauri/tauri.conf.json +1 -2
  94. package/static/app/asset-manifest.json +41 -41
  95. package/static/app/assets/{Act-DcQizkl1.js → Act-BPcVAbOL.js} +1 -1
  96. package/static/app/assets/AdminConsole-Bw1ATQL0.js +1 -0
  97. package/static/app/assets/{Brain-3VCSHFcn.js → Brain-CT92Kos0.js} +2 -2
  98. package/static/app/assets/{BrainHome-Qm8eaztx.js → BrainHome-CFBkt1K_.js} +1 -1
  99. package/static/app/assets/{BrainSignals-DS9BtKOW.js → BrainSignals-ReLWF2H8.js} +1 -1
  100. package/static/app/assets/Capture-BsTokYkk.js +1 -0
  101. package/static/app/assets/{Chronicle-BGvuAchH.js → Chronicle-B6f0T9id.js} +1 -1
  102. package/static/app/assets/{CommandPalette-Bqhm0Urn.js → CommandPalette-CuvjTv1u.js} +1 -1
  103. package/static/app/assets/{Library-BV6NnF0a.js → Library-BGJbG9Hd.js} +1 -1
  104. package/static/app/assets/{LivingBrain-GzenJchP.js → LivingBrain-DGYK_Jsa.js} +1 -1
  105. package/static/app/assets/{ProductFlow-DEP6-vML.js → ProductFlow-DXBC6brE.js} +1 -1
  106. package/static/app/assets/{ReviewCard-CNZ7XjWG.js → ReviewCard-HXRle3qq.js} +2 -2
  107. package/static/app/assets/System-CMHSO9qM.js +1 -0
  108. package/static/app/assets/arrow-left-BfmkskWx.js +1 -0
  109. package/static/app/assets/{bot-B_K1Tdmw.js → bot-Cn8bWRuq.js} +1 -1
  110. package/static/app/assets/{brain-DWyaV1L1.js → brain-CQJberbE.js} +1 -1
  111. package/static/app/assets/{button-aTn4s84A.js → button-Ct9f2_oT.js} +1 -1
  112. package/static/app/assets/circle-check-DruOxB-4.js +1 -0
  113. package/static/app/assets/{circle-pause-xKgeGXkT.js → circle-pause-CmzC_apg.js} +1 -1
  114. package/static/app/assets/{circle-play-DkT6tYPX.js → circle-play-D8mW2aQ7.js} +1 -1
  115. package/static/app/assets/{cpu-85xYObUC.js → cpu-DZcdd0PZ.js} +1 -1
  116. package/static/app/assets/{download-B5Fm7YXo.js → download-bv1KEPGQ.js} +1 -1
  117. package/static/app/assets/{folder-open-kk2Xa52u.js → folder-open-d-Pip5gr.js} +1 -1
  118. package/static/app/assets/{hard-drive-DkA3zBW_.js → hard-drive-D20iavUb.js} +1 -1
  119. package/static/app/assets/index-D9x-kSNy.css +2 -0
  120. package/static/app/assets/{index-BMPdTmlY.js → index-Do83hDzJ.js} +3 -3
  121. package/static/app/assets/{input-B0nRf2jO.js → input-BLXVNmj1.js} +1 -1
  122. package/static/app/assets/{link-2-Dwb4gnTc.js → link-2-BPJOFlAy.js} +1 -1
  123. package/static/app/assets/{permissionCopy-CQDUBrOZ.js → permissionCopy-ChdJd493.js} +1 -1
  124. package/static/app/assets/primitives-Cv5tbZBY.js +1 -0
  125. package/static/app/assets/search-CT9aho2j.js +1 -0
  126. package/static/app/assets/{share-2-BsrxFglO.js → share-2-YNX_NtMU.js} +1 -1
  127. package/static/app/assets/{shield-alert-5BStfp2_.js → shield-alert-DuQ3zrVL.js} +1 -1
  128. package/static/app/assets/{textarea-Cg8IUA-k.js → textarea-DqwLnli4.js} +1 -1
  129. package/static/app/assets/{useFocusTrap-CYKvE46M.js → useFocusTrap-ZVI98jaW.js} +1 -1
  130. package/static/app/assets/{useMutation-CSn9t1op.js → useMutation-CVC4qv_D.js} +1 -1
  131. package/static/app/assets/{useQuery-CY2OI2uy.js → useQuery-C7BeG4HU.js} +1 -1
  132. package/static/app/assets/{utils-Ddol2RWD.js → utils-CiFtIdZq.js} +1 -1
  133. package/static/app/assets/{workspace-BqDwOz_p.js → workspace-DQz9vIId.js} +1 -1
  134. package/static/app/index.html +4 -4
  135. package/static/sw.js +1 -1
  136. package/lattice_brain/archive.py +0 -522
  137. package/lattice_brain/context.py +0 -325
  138. package/lattice_brain/conversations.py +0 -382
  139. package/lattice_brain/core.py +0 -82
  140. package/lattice_brain/graph/_kg_contract.py +0 -249
  141. package/lattice_brain/graph/curator.py +0 -676
  142. package/lattice_brain/graph/discovery.py +0 -595
  143. package/lattice_brain/graph/discovery_index/__init__.py +0 -35
  144. package/lattice_brain/graph/discovery_index/cleanup.py +0 -182
  145. package/lattice_brain/graph/discovery_index/extract.py +0 -137
  146. package/lattice_brain/graph/discovery_index/scan.py +0 -411
  147. package/lattice_brain/graph/discovery_index/upsert.py +0 -495
  148. package/lattice_brain/graph/documents.py +0 -380
  149. package/lattice_brain/graph/fusion.py +0 -395
  150. package/lattice_brain/graph/identity.py +0 -175
  151. package/lattice_brain/graph/image_vectors.py +0 -230
  152. package/lattice_brain/graph/ingest.py +0 -829
  153. package/lattice_brain/graph/network.py +0 -205
  154. package/lattice_brain/graph/proactive.py +0 -724
  155. package/lattice_brain/graph/projection/__init__.py +0 -42
  156. package/lattice_brain/graph/projection/curation.py +0 -500
  157. package/lattice_brain/graph/projection/v2_schema.py +0 -518
  158. package/lattice_brain/graph/provenance.py +0 -524
  159. package/lattice_brain/graph/rerank.py +0 -163
  160. package/lattice_brain/graph/retrieval/__init__.py +0 -54
  161. package/lattice_brain/graph/retrieval/context.py +0 -197
  162. package/lattice_brain/graph/retrieval/graph_view.py +0 -319
  163. package/lattice_brain/graph/retrieval/hybrid.py +0 -488
  164. package/lattice_brain/graph/retrieval/maintenance.py +0 -121
  165. package/lattice_brain/graph/retrieval/signals.py +0 -95
  166. package/lattice_brain/graph/retrieval_docgen.py +0 -253
  167. package/lattice_brain/graph/retrieval_policy.py +0 -180
  168. package/lattice_brain/graph/retrieval_reads.py +0 -769
  169. package/lattice_brain/graph/retrieval_vector/__init__.py +0 -42
  170. package/lattice_brain/graph/retrieval_vector/fingerprint.py +0 -97
  171. package/lattice_brain/graph/retrieval_vector/indexing.py +0 -347
  172. package/lattice_brain/graph/retrieval_vector/search.py +0 -560
  173. package/lattice_brain/graph/retrieval_vector/status.py +0 -374
  174. package/lattice_brain/graph/schema.py +0 -792
  175. package/lattice_brain/graph/store.py +0 -268
  176. package/lattice_brain/graph/vector_index/__init__.py +0 -85
  177. package/lattice_brain/graph/vector_index/base.py +0 -167
  178. package/lattice_brain/graph/vector_index/brute_force.py +0 -110
  179. package/lattice_brain/graph/vector_index/hnsw.py +0 -290
  180. package/lattice_brain/graph/vector_index/jobs.py +0 -287
  181. package/lattice_brain/graph/vector_index/quantized.py +0 -148
  182. package/lattice_brain/graph/vector_index/selector.py +0 -161
  183. package/lattice_brain/graph/write_master.py +0 -308
  184. package/lattice_brain/ingestion/_contract.py +0 -90
  185. package/lattice_brain/ingestion/folder_scan.py +0 -57
  186. package/lattice_brain/ingestion/folders.py +0 -258
  187. package/lattice_brain/ingestion/jobs_api.py +0 -107
  188. package/lattice_brain/ingestion/routing.py +0 -295
  189. package/lattice_brain/ingestion_jobs.py +0 -380
  190. package/lattice_brain/memory.py +0 -75
  191. package/lattice_brain/portability/__init__.py +0 -90
  192. package/lattice_brain/portability/_contract.py +0 -42
  193. package/lattice_brain/portability/backups.py +0 -338
  194. package/lattice_brain/portability/bundles.py +0 -136
  195. package/lattice_brain/portability/constants.py +0 -93
  196. package/lattice_brain/portability/fsops.py +0 -138
  197. package/lattice_brain/portability/service.py +0 -41
  198. package/lattice_brain/portability/sharing.py +0 -710
  199. package/lattice_brain/quality.py +0 -543
  200. package/lattice_brain/retrieval_benchmark_fixtures.py +0 -92
  201. package/lattice_brain/runtime/agent_runtime.py +0 -859
  202. package/lattice_brain/runtime/contracts.py +0 -460
  203. package/lattice_brain/runtime/multi_agent.py +0 -942
  204. package/lattice_brain/runtime/statuses.py +0 -10
  205. package/lattice_brain/sealed_box.py +0 -240
  206. package/lattice_brain/self_model.py +0 -675
  207. package/lattice_brain/sensitivity.py +0 -94
  208. package/lattice_brain/storage/__init__.py +0 -22
  209. package/lattice_brain/storage/base.py +0 -100
  210. package/lattice_brain/storage/docker.py +0 -105
  211. package/lattice_brain/storage/factory.py +0 -31
  212. package/lattice_brain/storage/migration.py +0 -191
  213. package/lattice_brain/storage/postgres.py +0 -123
  214. package/lattice_brain/storage/sqlite.py +0 -143
  215. package/lattice_brain/synthesis.py +0 -824
  216. package/lattice_brain/workflow.py +0 -497
  217. package/latticeai/api/admin.py +0 -471
  218. package/latticeai/api/agent_registry.py +0 -105
  219. package/latticeai/api/agents.py +0 -228
  220. package/latticeai/api/auth.py +0 -383
  221. package/latticeai/api/automation_intelligence.py +0 -401
  222. package/latticeai/api/brain_intelligence.py +0 -199
  223. package/latticeai/api/browser.py +0 -493
  224. package/latticeai/api/change_proposals.py +0 -89
  225. package/latticeai/api/chat.py +0 -572
  226. package/latticeai/api/chat_agent_http.py +0 -892
  227. package/latticeai/api/chat_contracts.py +0 -73
  228. package/latticeai/api/chat_documents.py +0 -276
  229. package/latticeai/api/chat_helpers.py +0 -460
  230. package/latticeai/api/chat_history.py +0 -101
  231. package/latticeai/api/chat_hybrid.py +0 -113
  232. package/latticeai/api/chat_intents.py +0 -646
  233. package/latticeai/api/chat_stream.py +0 -216
  234. package/latticeai/api/chronicle.py +0 -63
  235. package/latticeai/api/command_center.py +0 -51
  236. package/latticeai/api/computer_use.py +0 -474
  237. package/latticeai/api/evidence_actions.py +0 -48
  238. package/latticeai/api/features.py +0 -70
  239. package/latticeai/api/funnel_metrics.py +0 -31
  240. package/latticeai/api/garden.py +0 -34
  241. package/latticeai/api/hooks.py +0 -165
  242. package/latticeai/api/index_jobs.py +0 -145
  243. package/latticeai/api/invitations.py +0 -100
  244. package/latticeai/api/knowledge_graph.py +0 -536
  245. package/latticeai/api/marketplace.py +0 -105
  246. package/latticeai/api/mcp.py +0 -482
  247. package/latticeai/api/memory.py +0 -270
  248. package/latticeai/api/network.py +0 -81
  249. package/latticeai/api/network_boundary.py +0 -225
  250. package/latticeai/api/permission_mode.py +0 -61
  251. package/latticeai/api/permissions.py +0 -436
  252. package/latticeai/api/plugins.py +0 -126
  253. package/latticeai/api/portability.py +0 -391
  254. package/latticeai/api/project_sessions.py +0 -114
  255. package/latticeai/api/realtime.py +0 -118
  256. package/latticeai/api/review_queue.py +0 -364
  257. package/latticeai/api/security_dashboard.py +0 -604
  258. package/latticeai/api/setup.py +0 -319
  259. package/latticeai/api/static_routes.py +0 -354
  260. package/latticeai/api/ui_redirects.py +0 -26
  261. package/latticeai/api/workflow_designer.py +0 -394
  262. package/latticeai/api/workspace.py +0 -856
  263. package/latticeai/api/workspace_scope.py +0 -125
  264. package/latticeai/core/agent/__init__.py +0 -93
  265. package/latticeai/core/agent/_contract.py +0 -79
  266. package/latticeai/core/agent/context.py +0 -57
  267. package/latticeai/core/agent/deps.py +0 -125
  268. package/latticeai/core/agent/execution.py +0 -622
  269. package/latticeai/core/agent/planning.py +0 -145
  270. package/latticeai/core/agent/recovery.py +0 -157
  271. package/latticeai/core/agent/runtime.py +0 -210
  272. package/latticeai/core/agent/verification.py +0 -231
  273. package/latticeai/core/agent_eval.py +0 -739
  274. package/latticeai/core/agent_helpers.py +0 -493
  275. package/latticeai/core/agent_profiles.py +0 -110
  276. package/latticeai/core/agent_prompts.py +0 -171
  277. package/latticeai/core/agent_registry.py +0 -232
  278. package/latticeai/core/agent_state.py +0 -41
  279. package/latticeai/core/agent_trace.py +0 -104
  280. package/latticeai/core/artifact_ledger.py +0 -109
  281. package/latticeai/core/audit.py +0 -260
  282. package/latticeai/core/builtin_hooks.py +0 -105
  283. package/latticeai/core/context_builder.py +0 -394
  284. package/latticeai/core/document_generator.py +0 -103
  285. package/latticeai/core/enterprise.py +0 -154
  286. package/latticeai/core/enterprise_admin.py +0 -158
  287. package/latticeai/core/file_generation/__init__.py +0 -115
  288. package/latticeai/core/file_generation/bundles.py +0 -76
  289. package/latticeai/core/file_generation/extraction.py +0 -154
  290. package/latticeai/core/file_generation/inference.py +0 -235
  291. package/latticeai/core/file_generation/orchestration.py +0 -152
  292. package/latticeai/core/file_generation/prompting.py +0 -117
  293. package/latticeai/core/file_generation/repair.py +0 -114
  294. package/latticeai/core/file_generation/sanitize.py +0 -61
  295. package/latticeai/core/file_generation/validation.py +0 -201
  296. package/latticeai/core/invitations.py +0 -132
  297. package/latticeai/core/legacy_compatibility.py +0 -243
  298. package/latticeai/core/logging_safety.py +0 -46
  299. package/latticeai/core/marketplace.py +0 -293
  300. package/latticeai/core/mcp_catalog.py +0 -452
  301. package/latticeai/core/mcp_registry.py +0 -506
  302. package/latticeai/core/network_boundary.py +0 -168
  303. package/latticeai/core/oidc.py +0 -208
  304. package/latticeai/core/plugins.py +0 -432
  305. package/latticeai/core/product_hardening.py +0 -218
  306. package/latticeai/core/project_sessions.py +0 -337
  307. package/latticeai/core/realtime.py +0 -238
  308. package/latticeai/core/run_explain.py +0 -426
  309. package/latticeai/core/run_store.py +0 -252
  310. package/latticeai/core/timezones.py +0 -80
  311. package/latticeai/core/workspace_computer_memory.py +0 -84
  312. package/latticeai/core/workspace_graph_trace.py +0 -155
  313. package/latticeai/core/workspace_indexing.py +0 -102
  314. package/latticeai/core/workspace_memory.py +0 -77
  315. package/latticeai/core/workspace_onboarding.py +0 -104
  316. package/latticeai/core/workspace_os.py +0 -978
  317. package/latticeai/core/workspace_os_constants.py +0 -126
  318. package/latticeai/core/workspace_os_state.py +0 -180
  319. package/latticeai/core/workspace_os_utils.py +0 -103
  320. package/latticeai/core/workspace_permissions.py +0 -101
  321. package/latticeai/core/workspace_plugins.py +0 -97
  322. package/latticeai/core/workspace_relationships.py +0 -99
  323. package/latticeai/core/workspace_reorganization.py +0 -335
  324. package/latticeai/core/workspace_review_items.py +0 -112
  325. package/latticeai/core/workspace_runs.py +0 -726
  326. package/latticeai/core/workspace_skills.py +0 -109
  327. package/latticeai/core/workspace_snapshots.py +0 -198
  328. package/latticeai/core/workspace_timeline.py +0 -110
  329. package/latticeai/integrations/__init__.py +0 -0
  330. package/latticeai/integrations/telegram_bot/__init__.py +0 -123
  331. package/latticeai/integrations/telegram_bot/__main__.py +0 -17
  332. package/latticeai/integrations/telegram_bot/config.py +0 -86
  333. package/latticeai/integrations/telegram_bot/dispatch.py +0 -311
  334. package/latticeai/integrations/telegram_bot/flows.py +0 -478
  335. package/latticeai/integrations/telegram_bot/helpers.py +0 -322
  336. package/latticeai/integrations/telegram_bot/screens.py +0 -394
  337. package/latticeai/runtime/audit_runtime.py +0 -76
  338. package/latticeai/runtime/automation_runtime.py +0 -81
  339. package/latticeai/runtime/chat_wiring.py +0 -141
  340. package/latticeai/runtime/context_runtime.py +0 -66
  341. package/latticeai/runtime/feature_toggle_wiring.py +0 -157
  342. package/latticeai/runtime/history_runtime.py +0 -163
  343. package/latticeai/runtime/history_writer.py +0 -138
  344. package/latticeai/runtime/hooks_runtime.py +0 -77
  345. package/latticeai/runtime/model_wiring.py +0 -68
  346. package/latticeai/runtime/namespace_runtime.py +0 -163
  347. package/latticeai/runtime/network_boundary_wiring.py +0 -117
  348. package/latticeai/runtime/network_config_runtime.py +0 -56
  349. package/latticeai/runtime/permission_mode_wiring.py +0 -112
  350. package/latticeai/runtime/persistence_runtime.py +0 -159
  351. package/latticeai/runtime/platform_runtime_wiring.py +0 -89
  352. package/latticeai/runtime/review_wiring.py +0 -42
  353. package/latticeai/runtime/router_registration.py +0 -693
  354. package/latticeai/runtime/service_singletons.py +0 -55
  355. package/latticeai/runtime/sso_config_runtime.py +0 -128
  356. package/latticeai/runtime/user_key_runtime.py +0 -106
  357. package/latticeai/runtime/web_runtime.py +0 -92
  358. package/latticeai/server_app.py +0 -51
  359. package/latticeai/services/app_context.py +0 -130
  360. package/latticeai/services/automation_execution.py +0 -266
  361. package/latticeai/services/automation_intelligence.py +0 -614
  362. package/latticeai/services/brain_automation.py +0 -191
  363. package/latticeai/services/brain_intelligence/__init__.py +0 -58
  364. package/latticeai/services/brain_intelligence/_contract.py +0 -71
  365. package/latticeai/services/brain_intelligence/consistency.py +0 -193
  366. package/latticeai/services/brain_intelligence/constants.py +0 -47
  367. package/latticeai/services/brain_intelligence/digest.py +0 -258
  368. package/latticeai/services/brain_intelligence/health.py +0 -331
  369. package/latticeai/services/brain_intelligence/proposals.py +0 -259
  370. package/latticeai/services/brain_intelligence/sampling.py +0 -84
  371. package/latticeai/services/brain_intelligence/service.py +0 -48
  372. package/latticeai/services/change_proposals.py +0 -471
  373. package/latticeai/services/chat_service.py +0 -243
  374. package/latticeai/services/chronicle.py +0 -555
  375. package/latticeai/services/cloud_egress_audit.py +0 -85
  376. package/latticeai/services/cloud_extraction.py +0 -129
  377. package/latticeai/services/cloud_streaming.py +0 -268
  378. package/latticeai/services/cloud_token_guard.py +0 -84
  379. package/latticeai/services/command_center.py +0 -548
  380. package/latticeai/services/evidence_actions.py +0 -258
  381. package/latticeai/services/feature_toggles.py +0 -502
  382. package/latticeai/services/folder_watch.py +0 -520
  383. package/latticeai/services/funnel_metrics.py +0 -307
  384. package/latticeai/services/hybrid_chat.py +0 -316
  385. package/latticeai/services/hybrid_context.py +0 -228
  386. package/latticeai/services/hybrid_policy.py +0 -129
  387. package/latticeai/services/interop_bridges.py +0 -978
  388. package/latticeai/services/local_knowledge.py +0 -465
  389. package/latticeai/services/memory_service/__init__.py +0 -52
  390. package/latticeai/services/memory_service/_contract.py +0 -100
  391. package/latticeai/services/memory_service/brief.py +0 -431
  392. package/latticeai/services/memory_service/constants.py +0 -57
  393. package/latticeai/services/memory_service/maintenance.py +0 -138
  394. package/latticeai/services/memory_service/manager.py +0 -186
  395. package/latticeai/services/memory_service/proof.py +0 -136
  396. package/latticeai/services/memory_service/recall.py +0 -225
  397. package/latticeai/services/memory_service/service.py +0 -48
  398. package/latticeai/services/memory_service/stores.py +0 -110
  399. package/latticeai/services/mode_store.py +0 -132
  400. package/latticeai/services/model_recommendation.py +0 -224
  401. package/latticeai/services/model_runtime/cloud.py +0 -87
  402. package/latticeai/services/network_boundary_service.py +0 -117
  403. package/latticeai/services/obsidian_bridge.py +0 -609
  404. package/latticeai/services/openai_compatible_adapter.py +0 -101
  405. package/latticeai/services/permission_mode_service.py +0 -122
  406. package/latticeai/services/platform_runtime.py +0 -366
  407. package/latticeai/services/review_queue.py +0 -380
  408. package/latticeai/services/router_context.py +0 -59
  409. package/latticeai/services/run_executor.py +0 -387
  410. package/latticeai/services/self_model_service.py +0 -171
  411. package/latticeai/services/setup_detection.py +0 -147
  412. package/latticeai/services/triggers.py +0 -378
  413. package/latticeai/services/upload_service.py +0 -172
  414. package/latticeai/services/workspace_service.py +0 -165
  415. package/latticeai/setup/__init__.py +0 -25
  416. package/latticeai/setup/auto_setup.py +0 -846
  417. package/latticeai/setup/demo_corpus.py +0 -98
  418. package/latticeai/setup/wizard/__init__.py +0 -126
  419. package/latticeai/setup/wizard/catalog.py +0 -175
  420. package/latticeai/setup/wizard/detect.py +0 -302
  421. package/latticeai/setup/wizard/install.py +0 -348
  422. package/latticeai/setup/wizard/paths.py +0 -165
  423. package/latticeai/setup/wizard/plans.py +0 -74
  424. package/latticeai/setup/wizard/recommend.py +0 -320
  425. package/scripts/bench_agent_smoke.py +0 -409
  426. package/scripts/bench_models.py +0 -540
  427. package/scripts/bench_vector_index.py +0 -295
  428. package/scripts/funnel_soft_gate.py +0 -192
  429. package/scripts/generate_agent_loop_fixtures.py +0 -994
  430. package/scripts/generate_rust_parity_fixtures.py +0 -908
  431. package/scripts/migrate_brain_storage.py +0 -57
  432. package/scripts/parity_fixture_corpus_context.py +0 -162
  433. package/scripts/parity_fixture_corpus_docgen.py +0 -341
  434. package/scripts/profile_kg.py +0 -355
  435. package/server.py +0 -30
  436. package/static/app/assets/AdminConsole-cf4npybT.js +0 -1
  437. package/static/app/assets/Capture-DiQ219jW.js +0 -1
  438. package/static/app/assets/System-CieofHQa.js +0 -1
  439. package/static/app/assets/arrow-left-kfsrk0mv.js +0 -1
  440. package/static/app/assets/circle-check-qqLug9nU.js +0 -1
  441. package/static/app/assets/index-DxmOfNRi.css +0 -2
  442. package/static/app/assets/primitives-SNp0LRJz.js +0 -1
  443. package/static/app/assets/search-BcHqkjoy.js +0 -1
@@ -1,518 +0,0 @@
1
- """The legacy → v2 projection: schema, backfill, per-row writes, mirrors.
2
-
3
- The v2 tables and the ``kgv2_*`` reconstruction views are *derived* — the legacy
4
- ``nodes``/``edges`` tables stay authoritative — which is what makes the whole
5
- DROP → CREATE → VIEWS → BACKFILL → stamp migration safe to run in one
6
- transaction and simply retry on the next startup when it fails. The trigram FTS
7
- index lives here too: it is the other index projected off the same rows.
8
- """
9
-
10
- # ruff: noqa: F403,F405,S608
11
- from __future__ import annotations
12
-
13
- from typing import TYPE_CHECKING
14
-
15
- from ...quiet import quiet
16
- from .._kg_common import * # noqa: F401
17
-
18
- # The cross-mixin surface (`_connect`, `_upsert_node`, …) is declared in
19
- # `_kg_contract.KnowledgeGraphCore`. It is a typing-only base: at runtime this
20
- # is `object`, so the MRO of `KnowledgeGraphStore` is unchanged.
21
- if TYPE_CHECKING:
22
- from .._kg_contract import KnowledgeGraphCore as _Core
23
- else:
24
- _Core = object
25
-
26
-
27
- class KnowledgeGraphV2SchemaMixin(_Core):
28
- """Projection + FTS. Mixed into ``KnowledgeGraphProjectionMixin``."""
29
-
30
- _FTS_SQL = """
31
- CREATE VIRTUAL TABLE IF NOT EXISTS node_fts USING fts5(
32
- node_id UNINDEXED, title, summary, metadata, tokenize='trigram'
33
- );
34
- CREATE TRIGGER IF NOT EXISTS node_fts_ai AFTER INSERT ON nodes BEGIN
35
- INSERT INTO node_fts(node_id, title, summary, metadata)
36
- VALUES (new.id, new.title, COALESCE(new.summary, ''), new.metadata_json);
37
- END;
38
- CREATE TRIGGER IF NOT EXISTS node_fts_au AFTER UPDATE ON nodes BEGIN
39
- DELETE FROM node_fts WHERE node_id = old.id;
40
- INSERT INTO node_fts(node_id, title, summary, metadata)
41
- VALUES (new.id, new.title, COALESCE(new.summary, ''), new.metadata_json);
42
- END;
43
- CREATE TRIGGER IF NOT EXISTS node_fts_ad AFTER DELETE ON nodes BEGIN
44
- DELETE FROM node_fts WHERE node_id = old.id;
45
- END;
46
- """
47
-
48
- # ``type`` is reconstructed from ``legacy_type`` when there is one, because
49
- # that column carries the label a reader expects. The trap the 11.0.1
50
- # review recorded — and which 11.2.0 fixes — is that ``edges_v2.legacy_type``
51
- # is ``NOT NULL DEFAULT ''``: a natively-canonical edge is written with
52
- # ``legacy_type=''`` so its identity is effectively (source, target, type),
53
- # and ``COALESCE('', type)`` returns ``''`` because COALESCE only skips
54
- # NULL. Every canonical edge therefore read back with an **empty type**.
55
- # ``NULLIF(legacy_type, '')`` is the fix: empty means "no legacy label",
56
- # which is precisely what the write side meant by it.
57
- #
58
- # The empty string stays the write-side sentinel on purpose. SQLite treats
59
- # NULLs as distinct in a UNIQUE index, so moving to NULL would silently
60
- # disable the (source, target, type, legacy_type) dedupe and let the same
61
- # relation land twice.
62
- #
63
- # The temporal columns pass through *raw* (v11.1.0): validity is not a
64
- # label, and a COALESCE there would turn "still valid" (NULL) into a value.
65
- # NULL in, NULL out; the fallback to ``created_at`` belongs to the read
66
- # predicate (``schema.TEMPORAL_PREDICATE_SQL``), not to the view.
67
- _V2_VIEWS_SQL = """
68
- CREATE VIEW IF NOT EXISTS kgv2_nodes AS
69
- SELECT id,
70
- COALESCE(NULLIF(legacy_type, ''), type) AS type,
71
- label AS title,
72
- summary,
73
- attrs AS metadata_json,
74
- created_at, updated_at,
75
- valid_from, valid_to, superseded_by
76
- FROM nodes_v2;
77
- CREATE VIEW IF NOT EXISTS kgv2_edges AS
78
- SELECT id, source AS from_node, target AS to_node,
79
- COALESCE(NULLIF(legacy_type, ''), type) AS type,
80
- weight,
81
- metadata AS metadata_json,
82
- created_at,
83
- valid_from, valid_to, superseded_by
84
- FROM edges_v2;
85
- """
86
-
87
- def _init_fts(self) -> None:
88
- self._fts_enabled = False
89
- try:
90
- with self._connect() as conn:
91
- conn.executescript(self._FTS_SQL)
92
- fts_count = conn.execute(
93
- "SELECT count(*) AS c FROM node_fts"
94
- ).fetchone()["c"]
95
- if fts_count == 0:
96
- conn.execute(
97
- "INSERT INTO node_fts(node_id, title, summary, metadata) "
98
- "SELECT id, title, COALESCE(summary, ''), metadata_json FROM nodes"
99
- )
100
- self._fts_enabled = True
101
- except sqlite3.OperationalError as exc:
102
- # FTS5/trigram not compiled into this SQLite build. LIKE search
103
- # stays authoritative; the capability is reported, never faked.
104
- logging.info(
105
- "FTS5 trigram index unavailable (%s); keyword search uses LIKE scans.",
106
- exc,
107
- )
108
-
109
- def _fts_match_ids(
110
- self, conn: sqlite3.Connection, query: str, limit: int
111
- ) -> List[str]:
112
- """Ranked node ids for a trigram FTS query ('' on any failure)."""
113
- if not getattr(self, "_fts_enabled", False) or len(query) < 3:
114
- return []
115
- escaped = query.replace('"', '""')
116
- try:
117
- rows = conn.execute(
118
- "SELECT node_id FROM node_fts WHERE node_fts MATCH ? ORDER BY rank LIMIT ?",
119
- (f'"{escaped}"', limit),
120
- ).fetchall()
121
- except sqlite3.OperationalError:
122
- return []
123
- return [row["node_id"] for row in rows]
124
-
125
- def _init_v2_schema(self) -> None:
126
- """Initialize the normalized v2 tables + reconstruction views, migrating
127
- the projection layout when it is stale — **atomically**.
128
-
129
- The entire DROP → CREATE → VIEWS → BACKFILL → version-stamp sequence runs
130
- in a single transaction on one connection: on any failure it rolls back,
131
- leaving the prior projection untouched and the version unchanged, so the
132
- next startup simply retries. The migration only ever touches the v2
133
- tables/views and the ``projection_version`` key — never the authoritative
134
- legacy ``nodes``/``edges`` — so legacy data cannot be corrupted even if
135
- the rebuild fails midway.
136
- """
137
- if KGStoreV2 is None or _exec_script is None:
138
- return
139
- self._v2_projection_available = False
140
- try:
141
- self._backup_before_v2_flip()
142
- with self._connect() as conn:
143
- conn.execute("BEGIN")
144
- stale = self._projection_version(conn) != _PROJECTION_VERSION
145
- # Reconstruction views are non-authoritative. Recreate them on
146
- # every startup so older SQLite rename migrations cannot strand
147
- # a view against a temporary table such as edges_v2_old.
148
- for stmt in (
149
- "DROP VIEW IF EXISTS kgv2_edges",
150
- "DROP VIEW IF EXISTS kgv2_nodes",
151
- ):
152
- conn.execute(stmt)
153
- if stale:
154
- # The projection is non-authoritative; drop it so init_schema
155
- # recreates the tables with the current normalized columns.
156
- for stmt in (
157
- "DROP TABLE IF EXISTS edges_v2",
158
- "DROP TABLE IF EXISTS nodes_v2",
159
- ):
160
- conn.execute(stmt)
161
- # init_schema(conn=...) joins this transaction (no implicit commit)
162
- KGStoreV2(self.db_path).init_schema(conn=conn)
163
- _exec_script(conn, self._V2_VIEWS_SQL)
164
- self._backfill_v2_on(conn, force=stale)
165
- self._normalize_v2_legacy_types(conn)
166
- # version stamp commits together with the backfill — never stranded
167
- conn.execute(
168
- "INSERT OR REPLACE INTO kg_meta(key, value) VALUES ('projection_version', ?)",
169
- (str(_PROJECTION_VERSION),),
170
- )
171
- mastered_at = _now()
172
- conn.execute(
173
- "INSERT OR REPLACE INTO kg_meta(key, value) VALUES (?, ?)",
174
- (_KG_DB_FORMAT_KEY, str(_KG_DB_FORMAT_VERSION)),
175
- )
176
- conn.execute(
177
- "INSERT OR REPLACE INTO kg_meta(key, value) VALUES (?, COALESCE((SELECT value FROM kg_meta WHERE key=?), ?))",
178
- (_V2_WRITE_MASTER_KEY, _V2_WRITE_MASTER_KEY, mastered_at),
179
- )
180
- conn.execute(f"PRAGMA user_version={_KG_DB_FORMAT_VERSION}")
181
- conn.execute("SELECT 1 FROM kgv2_nodes LIMIT 1").fetchone()
182
- conn.execute("SELECT 1 FROM kgv2_edges LIMIT 1").fetchone()
183
- self._v2_projection_available = True
184
- except Exception as e:
185
- logging.warning("knowledge_graph: v2 schema init/backfill skipped: %s", e)
186
-
187
- def _normalize_v2_legacy_types(self, conn: sqlite3.Connection) -> Dict[str, int]:
188
- """Bring pre-11.2.0 rows onto the current ``legacy_type`` convention.
189
-
190
- The convention is: ``legacy_type`` holds the *raw* label only when it
191
- differs from the canonical type, and ``''`` (edges) / ``NULL`` (nodes)
192
- otherwise. Rows written by older builds could carry the canonical value
193
- in both columns, which is redundant and — because the dedupe key
194
- includes ``legacy_type`` — splits one relation across two rows.
195
-
196
- Idempotent by construction: a second run matches nothing. ``UPDATE OR
197
- IGNORE`` is used because collapsing a redundant row can collide with
198
- the canonical one that already exists; those survivors are counted and
199
- reported rather than deleted, since the fixed view reads both
200
- identically and deleting an edge is not a migration's business.
201
- """
202
- report = {"edges": 0, "nodes": 0, "collisions": 0}
203
- try:
204
- before = int(
205
- conn.execute(
206
- "SELECT COUNT(*) FROM edges_v2 WHERE legacy_type = type"
207
- ).fetchone()[0]
208
- or 0
209
- )
210
- conn.execute(
211
- "UPDATE OR IGNORE edges_v2 SET legacy_type='' WHERE legacy_type = type"
212
- )
213
- after = int(
214
- conn.execute(
215
- "SELECT COUNT(*) FROM edges_v2 WHERE legacy_type = type"
216
- ).fetchone()[0]
217
- or 0
218
- )
219
- report["edges"] = before - after
220
- report["collisions"] = after
221
- cursor = conn.execute(
222
- "UPDATE nodes_v2 SET legacy_type=NULL WHERE legacy_type = ''"
223
- )
224
- report["nodes"] = int(cursor.rowcount or 0)
225
- except sqlite3.Error as exc:
226
- # The projection is derived; a migration that cannot run leaves the
227
- # data exactly as it was and the fixed view still reads it right.
228
- logging.debug("knowledge_graph: legacy_type normalization skipped: %s", exc)
229
- return report
230
-
231
- def _backup_before_v2_flip(self) -> Optional[str]:
232
- """Create one local SQLite backup before the v2 write-master flip."""
233
- if not self.db_path.exists() or self.db_path.stat().st_size == 0:
234
- return None
235
- with self._connect() as conn:
236
- try:
237
- stamped = conn.execute(
238
- "SELECT value FROM kg_meta WHERE key=?", (_V2_WRITE_MASTER_KEY,)
239
- ).fetchone()
240
- except sqlite3.Error:
241
- stamped = None
242
- if stamped:
243
- return None
244
- try:
245
- rows = int(
246
- conn.execute("SELECT COUNT(*) FROM nodes").fetchone()[0] or 0
247
- )
248
- except sqlite3.Error:
249
- rows = 0
250
- if rows == 0:
251
- return None
252
- conn.execute("PRAGMA wal_checkpoint(FULL)")
253
- backup_dir = self.db_path.parent / "backups"
254
- backup_dir.mkdir(parents=True, exist_ok=True)
255
- stamp = datetime.now().strftime("%Y%m%dT%H%M%S")
256
- dest = (
257
- backup_dir / f"{self.db_path.stem}.pre-v2-write-master.{stamp}.sqlite"
258
- )
259
- conn.execute("VACUUM INTO ?", (str(dest),))
260
- return str(dest)
261
-
262
- def _projection_version(self, conn: sqlite3.Connection) -> int:
263
- """Return the stored v2 projection layout version (0 if unknown).
264
-
265
- A fresh DB (kg_meta absent) raises ``sqlite3.OperationalError`` here and
266
- is correctly treated as version 0 → rebuild. Only sqlite errors are
267
- swallowed so a real bug doesn't masquerade as a stale projection.
268
- """
269
- try:
270
- row = conn.execute(
271
- "SELECT value FROM kg_meta WHERE key='projection_version'"
272
- ).fetchone()
273
- return int(row["value"]) if row and row["value"] is not None else 0
274
- except sqlite3.Error:
275
- return 0
276
-
277
- def _backfill_v2_if_needed(self, *, force: bool = False) -> None:
278
- """Project legacy nodes/edges into v2 on a fresh transaction.
279
-
280
- Thin wrapper around :meth:`_backfill_v2_on` for callers (tests, ad-hoc
281
- re-sync) that aren't already inside the migration transaction.
282
- """
283
- try:
284
- with self._connect() as conn:
285
- self._backfill_v2_on(conn, force=force)
286
- except Exception as ex:
287
- logging.warning("knowledge_graph: v2 backfill skipped: %s", ex)
288
-
289
- def _backfill_v2_on(self, conn: sqlite3.Connection, *, force: bool = False) -> None:
290
- """Project legacy nodes/edges into the normalized v2 tables on ``conn``.
291
-
292
- Non-destructive to legacy. ``force`` rebuilds unconditionally (used after
293
- a layout migration); otherwise it only projects when v2 is empty. The v2
294
- graph is a derived projection, so clearing + rebuilding it is always safe.
295
- Idempotent: no-ops once v2 carries the current projection. Copies the
296
- legacy column values **verbatim** so the kgv2_* views are byte-faithful.
297
- """
298
- legacy_nodes = conn.execute("SELECT COUNT(*) FROM nodes").fetchone()[0]
299
- if legacy_nodes == 0:
300
- return
301
- v2_nodes = conn.execute("SELECT COUNT(*) FROM nodes_v2").fetchone()[0]
302
- if v2_nodes > 0 and not force:
303
- return # current projection already present
304
- # (re)project: clear v2 graph (not authoritative) and rebuild
305
- conn.execute("DELETE FROM edges_v2")
306
- conn.execute("DELETE FROM nodes_v2")
307
- n = e = 0
308
- for r in conn.execute(
309
- "SELECT id, type, title, summary, metadata_json, created_at, updated_at FROM nodes"
310
- ).fetchall():
311
- self._v2_project_node(
312
- conn,
313
- r["id"],
314
- r["type"],
315
- r["title"],
316
- r["summary"],
317
- r["metadata_json"],
318
- created_at=r["created_at"],
319
- updated_at=r["updated_at"],
320
- )
321
- n += 1
322
- for r in conn.execute(
323
- "SELECT id, from_node, to_node, type, weight, metadata_json, created_at FROM edges"
324
- ).fetchall():
325
- self._v2_project_edge(
326
- conn,
327
- r["from_node"],
328
- r["to_node"],
329
- r["type"],
330
- float(r["weight"] or 1.0),
331
- r["metadata_json"],
332
- edge_id=r["id"],
333
- created_at=r["created_at"],
334
- )
335
- e += 1
336
- logging.info(
337
- "knowledge_graph: projected legacy → v2 (%d nodes, %d edges)", n, e
338
- )
339
-
340
- def _v2_project_node(
341
- self,
342
- conn: sqlite3.Connection,
343
- node_id: str,
344
- node_type: str,
345
- title: str,
346
- summary: Optional[str],
347
- metadata_json: Optional[str],
348
- *,
349
- created_at: Optional[str] = None,
350
- updated_at: Optional[str] = None,
351
- owner: Optional[str] = None,
352
- workspace_id: Optional[str] = None,
353
- visibility: Optional[str] = None,
354
- strict: bool = False,
355
- ) -> None:
356
- if KGStoreV2 is None:
357
- if strict:
358
- raise RuntimeError("Knowledge Graph v2 schema is unavailable")
359
- return
360
- ts = updated_at or _now()
361
- norm_type = (
362
- NodeType.from_legacy(node_type).value if NodeType is not None else node_type
363
- )
364
- # Scope resolution: explicit param > metadata hints > legacy-global.
365
- # 'legacy' (not 'private') marks unscoped rows — the column default
366
- # must never silently privatize previously machine-shared data.
367
- meta = _safe_loads(metadata_json) if metadata_json else {}
368
- owner = owner or meta.get("user_email") or meta.get("owner") or None
369
- workspace_id = workspace_id or meta.get("workspace_id") or None
370
- visibility = visibility or ("legacy" if workspace_id is None else "workspace")
371
- try:
372
- conn.execute(
373
- """
374
- INSERT INTO nodes_v2(id, type, legacy_type, label, summary, attrs,
375
- owner_id, workspace_id, visibility,
376
- created_at, updated_at, importance_score)
377
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0.0)
378
- ON CONFLICT(id) DO UPDATE SET
379
- type=excluded.type, legacy_type=excluded.legacy_type,
380
- label=excluded.label, summary=excluded.summary,
381
- attrs=excluded.attrs, updated_at=excluded.updated_at,
382
- owner_id=COALESCE(excluded.owner_id, nodes_v2.owner_id),
383
- workspace_id=COALESCE(excluded.workspace_id, nodes_v2.workspace_id),
384
- visibility=CASE WHEN excluded.visibility != 'legacy'
385
- THEN excluded.visibility
386
- ELSE nodes_v2.visibility END
387
- """,
388
- (
389
- node_id,
390
- norm_type,
391
- node_type,
392
- title,
393
- summary,
394
- metadata_json if metadata_json is not None else "{}",
395
- owner,
396
- workspace_id,
397
- visibility,
398
- created_at or ts,
399
- ts,
400
- ),
401
- )
402
- except Exception as ex:
403
- if strict:
404
- raise
405
- logging.debug(
406
- "knowledge_graph: v2 node projection skipped (%s): %s", node_id, ex
407
- )
408
-
409
- def _v2_project_edge(
410
- self,
411
- conn: sqlite3.Connection,
412
- from_node: str,
413
- to_node: str,
414
- edge_type: str,
415
- weight: float,
416
- metadata_json: Optional[str],
417
- *,
418
- edge_id: Optional[str] = None,
419
- created_at: Optional[str] = None,
420
- strict: bool = False,
421
- legacy_type: Optional[str] = None,
422
- ) -> None:
423
- if KGStoreV2 is None:
424
- if strict:
425
- raise RuntimeError("Knowledge Graph v2 schema is unavailable")
426
- return
427
- explicit_legacy_type = legacy_type is not None
428
- leg_type = legacy_type if explicit_legacy_type else edge_type
429
- # Native canonical writes (and write-door dedupes) use legacy_type=''
430
- # so (source,target,type) is the effective key.
431
- # Import paths can pass distinct legacy_type to keep colliding legacy
432
- # labels as separate rows (lossless for old data).
433
- if leg_type and EdgeType is not None:
434
- try:
435
- if EdgeType.from_legacy(leg_type).value == leg_type:
436
- leg_type = ""
437
- except ValueError:
438
- quiet()
439
- norm_type = (
440
- EdgeType.from_legacy(edge_type).value if EdgeType is not None else edge_type
441
- )
442
- if explicit_legacy_type and leg_type:
443
- eid = f"edge:{_sha256_text(f'{from_node}|{norm_type}|{to_node}|{leg_type}')[:24]}"
444
- else:
445
- eid = edge_id or f"edge:{_sha256_text(f'{from_node}|{norm_type}|{to_node}')[:24]}"
446
- meta_str = metadata_json if metadata_json is not None else "{}"
447
- confidence = float(_safe_loads(meta_str).get("confidence", 1.0))
448
- try:
449
- conn.execute(
450
- """
451
- INSERT INTO edges_v2(id, source, target, type, legacy_type, weight,
452
- confidence, evidence, metadata, created_by, created_at)
453
- VALUES (?, ?, ?, ?, ?, ?, ?, '[]', ?, 'legacy', ?)
454
- ON CONFLICT(source, target, type, legacy_type) DO UPDATE SET
455
- weight=max(edges_v2.weight, excluded.weight),
456
- confidence=excluded.confidence,
457
- metadata=excluded.metadata
458
- """,
459
- (
460
- eid,
461
- from_node,
462
- to_node,
463
- norm_type,
464
- leg_type,
465
- float(weight),
466
- confidence,
467
- meta_str,
468
- created_at or _now(),
469
- ),
470
- )
471
- # Temporal record: every observation of this relationship is kept
472
- # (the UNIQUE upsert + weight=max alone would erase recurrence).
473
- row = conn.execute(
474
- "SELECT id FROM edges_v2 WHERE source=? AND target=? AND type=? AND legacy_type=?",
475
- (from_node, to_node, norm_type, leg_type),
476
- ).fetchone()
477
- if row is not None:
478
- conn.execute(
479
- "INSERT INTO edge_occurrences(edge_id, observed_at, weight, source) VALUES (?, ?, ?, ?)",
480
- (
481
- row["id"],
482
- created_at or _now(),
483
- float(weight),
484
- _safe_loads(meta_str).get("source"),
485
- ),
486
- )
487
- except Exception as ex:
488
- if strict:
489
- raise
490
- logging.debug(
491
- "knowledge_graph: v2 edge projection skipped (%s->%s): %s",
492
- from_node,
493
- to_node,
494
- ex,
495
- )
496
-
497
- def _v2_delete_nodes(self, conn: sqlite3.Connection, ids) -> None:
498
- """Mirror legacy node deletions into v2 (edges_v2 cascade on the FK)."""
499
- if KGStoreV2 is None:
500
- return
501
- ids = list(ids)
502
- if not ids:
503
- return
504
- ph = ",".join("?" * len(ids))
505
- try:
506
- conn.execute(f"DELETE FROM nodes_v2 WHERE id IN ({ph})", ids)
507
- except Exception as ex:
508
- logging.debug("knowledge_graph: v2 node delete mirror skipped: %s", ex)
509
-
510
- def _v2_delete_edges_from(self, conn: sqlite3.Connection, node_id: str) -> None:
511
- """Mirror a legacy ``DELETE FROM edges WHERE from_node=?`` into v2."""
512
- if KGStoreV2 is None:
513
- return
514
- try:
515
- conn.execute("DELETE FROM edges_v2 WHERE source=?", (node_id,))
516
- except Exception as ex:
517
- logging.debug("knowledge_graph: v2 edge delete mirror skipped: %s", ex)
518
-