ltcai 11.5.2 → 11.6.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 (439) hide show
  1. package/README.md +91 -148
  2. package/bin/ltcai.js +234 -24
  3. package/docs/CHANGELOG.md +75 -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 +21 -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-CToZnOHz.js} +1 -1
  96. package/static/app/assets/{AdminConsole-cf4npybT.js → AdminConsole-D5kbrBu8.js} +1 -1
  97. package/static/app/assets/{Brain-3VCSHFcn.js → Brain-C6zCdv4S.js} +1 -1
  98. package/static/app/assets/{BrainHome-Qm8eaztx.js → BrainHome-D4n3EDUt.js} +1 -1
  99. package/static/app/assets/{BrainSignals-DS9BtKOW.js → BrainSignals-CzeZ-vQ9.js} +1 -1
  100. package/static/app/assets/{Capture-DiQ219jW.js → Capture-DPcyTczq.js} +1 -1
  101. package/static/app/assets/{Chronicle-BGvuAchH.js → Chronicle-D3Nx-oWS.js} +1 -1
  102. package/static/app/assets/{CommandPalette-Bqhm0Urn.js → CommandPalette-DPPvpp28.js} +1 -1
  103. package/static/app/assets/{Library-BV6NnF0a.js → Library-Co2qTu3I.js} +1 -1
  104. package/static/app/assets/{LivingBrain-GzenJchP.js → LivingBrain-Bsmzwy8_.js} +1 -1
  105. package/static/app/assets/{ProductFlow-DEP6-vML.js → ProductFlow-CBLdmesT.js} +1 -1
  106. package/static/app/assets/{ReviewCard-CNZ7XjWG.js → ReviewCard-CfR6DLs7.js} +1 -1
  107. package/static/app/assets/{System-CieofHQa.js → System-Ce-P2YL6.js} +1 -1
  108. package/static/app/assets/arrow-left-DD5jFGHV.js +1 -0
  109. package/static/app/assets/{bot-B_K1Tdmw.js → bot-C5-XIww2.js} +1 -1
  110. package/static/app/assets/{brain-DWyaV1L1.js → brain-CFRJUvdX.js} +1 -1
  111. package/static/app/assets/{button-aTn4s84A.js → button-DI1KcQP-.js} +1 -1
  112. package/static/app/assets/circle-check-CY20XMCh.js +1 -0
  113. package/static/app/assets/{circle-pause-xKgeGXkT.js → circle-pause-C8HHyood.js} +1 -1
  114. package/static/app/assets/{circle-play-DkT6tYPX.js → circle-play-Cufc235E.js} +1 -1
  115. package/static/app/assets/{cpu-85xYObUC.js → cpu-DW2_JVsv.js} +1 -1
  116. package/static/app/assets/{download-B5Fm7YXo.js → download-gbdKTBb8.js} +1 -1
  117. package/static/app/assets/{folder-open-kk2Xa52u.js → folder-open-C9ydyX33.js} +1 -1
  118. package/static/app/assets/{hard-drive-DkA3zBW_.js → hard-drive-BxNMzBM3.js} +1 -1
  119. package/static/app/assets/index-BovSWRmQ.css +2 -0
  120. package/static/app/assets/{index-BMPdTmlY.js → index-q9XyDY7A.js} +3 -3
  121. package/static/app/assets/{input-B0nRf2jO.js → input-GLD94Y_K.js} +1 -1
  122. package/static/app/assets/{link-2-Dwb4gnTc.js → link-2-B4Y0gz1S.js} +1 -1
  123. package/static/app/assets/{permissionCopy-CQDUBrOZ.js → permissionCopy-CxrV0fvU.js} +1 -1
  124. package/static/app/assets/{primitives-SNp0LRJz.js → primitives-2ANdAsu2.js} +1 -1
  125. package/static/app/assets/search-DTxtjQ07.js +1 -0
  126. package/static/app/assets/{share-2-BsrxFglO.js → share-2-DxArfiuI.js} +1 -1
  127. package/static/app/assets/{shield-alert-5BStfp2_.js → shield-alert-CgftuFFs.js} +1 -1
  128. package/static/app/assets/{textarea-Cg8IUA-k.js → textarea-BHVBAoCr.js} +1 -1
  129. package/static/app/assets/{useFocusTrap-CYKvE46M.js → useFocusTrap-Dzq2j_OG.js} +1 -1
  130. package/static/app/assets/{useMutation-CSn9t1op.js → useMutation-BcqfMLNq.js} +1 -1
  131. package/static/app/assets/{useQuery-CY2OI2uy.js → useQuery-DBzLKCOv.js} +1 -1
  132. package/static/app/assets/{utils-Ddol2RWD.js → utils-B-Aah1bO.js} +1 -1
  133. package/static/app/assets/{workspace-BqDwOz_p.js → workspace-B6fFhoko.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/arrow-left-kfsrk0mv.js +0 -1
  437. package/static/app/assets/circle-check-qqLug9nU.js +0 -1
  438. package/static/app/assets/index-DxmOfNRi.css +0 -2
  439. package/static/app/assets/search-BcHqkjoy.js +0 -1
@@ -1,75 +0,0 @@
1
- """Memory System — typed, durable memory records on the brain substrate.
2
-
3
- Decision and Experience records become first-class graph nodes through the
4
- unified ingestion pipeline (provenance + hooks), instead of markdown dumps
5
- with swallowed errors. Episodic memory is the conversation store; semantic
6
- memory is the workspace MEMORY_KINDS records — this module adds the typed
7
- record kinds the schema always had but never populated.
8
-
9
- Only REAL events become memories: simulation runs are rejected at this
10
- boundary (the run record's own mode field is checked — fabricated artifacts
11
- must never enter the brain as experience).
12
- """
13
-
14
- from __future__ import annotations
15
-
16
- from typing import Any, Dict, Optional
17
-
18
- from .ingestion import IngestionItem
19
-
20
-
21
- class BrainMemory:
22
- """Writes Decision / Experience records through the ingestion pipeline."""
23
-
24
- def __init__(self, ingestion_pipeline: Any):
25
- self._pipeline = ingestion_pipeline
26
-
27
- def available(self) -> bool:
28
- return self._pipeline is not None and self._pipeline.available()
29
-
30
- def record_experience(
31
- self,
32
- title: str,
33
- detail: str = "",
34
- *,
35
- run: Optional[Dict[str, Any]] = None,
36
- user_email: Optional[str] = None,
37
- workspace_id: Optional[str] = None,
38
- metadata: Optional[Dict[str, Any]] = None,
39
- ) -> Dict[str, Any]:
40
- """Persist a completed run/action as an Experience node.
41
-
42
- ``run`` is the persisted run record; simulated runs are refused —
43
- a simulation is replay scaffolding, not something that happened.
44
- """
45
- if run is not None and run.get("mode", "simulation") == "simulation":
46
- return {
47
- "status": "rejected",
48
- "detail": "simulation runs are not experiences and never enter the brain",
49
- }
50
- if not str(title or "").strip():
51
- raise ValueError("an experience needs a title")
52
- run_meta = {}
53
- if run is not None:
54
- run_meta = {
55
- "run_id": run.get("id"),
56
- "agent_id": run.get("agent_id"),
57
- "run_status": run.get("status"),
58
- "mode": run.get("mode"),
59
- "retries": run.get("retries"),
60
- }
61
- result = self._pipeline.ingest(
62
- IngestionItem(
63
- source_type="experience",
64
- title=title.strip(),
65
- text=detail,
66
- owner=user_email,
67
- workspace_id=workspace_id,
68
- metadata={**run_meta, **(metadata or {})},
69
- ),
70
- user_email=user_email,
71
- )
72
- return result.as_dict()
73
-
74
-
75
- __all__ = ["BrainMemory"]
@@ -1,90 +0,0 @@
1
- """Knowledge Graph portability — local export / import / backup / restore.
2
-
3
- The Knowledge Graph is the user's durable asset, so it must be portable without
4
- any cloud service. Two complementary mechanisms, both fully local:
5
-
6
- * **Logical export/import** (JSON): nodes/edges/chunks/sources/provenance with a
7
- versioned header (schema + projection + embed-dim). Vectors are not in the
8
- artifact; the importer re-embeds with its own embedder and reports the
9
- resulting index state under ``result["index"]`` (``degraded: true`` means the
10
- content landed but recall is lexical-only until a rebuild succeeds). That is
11
- what makes it portable across machines.
12
- * **Binary backup/restore** (ZIP): a faithful snapshot of the SQLite DB (incl.
13
- vector embeddings) plus the blob directory, integrity-checked, for
14
- same-machine recovery.
15
-
16
- Split into cohesive submodules in v11.3.0 (no behaviour change): ``constants``
17
- (formats + gates), ``fsops`` (atomic swaps), ``bundles`` (subgraph shaping),
18
- ``sharing`` and ``backups`` (the two halves of the service), ``service`` (the
19
- composed class). This module re-exports every name the single file exposed, so
20
- ``lattice_brain.portability.X`` keeps working.
21
- """
22
-
23
- from __future__ import annotations
24
-
25
- # The single file had no ``__all__``, so its public surface was "every module
26
- # global". The redundant-alias form reproduces exactly that surface and marks
27
- # each name as a deliberate re-export rather than a leftover import.
28
- #
29
- # Stubbing note: rebinding one of these *here* changes only this module's name.
30
- # The submodule that calls it holds its own reference, so a test standing in for
31
- # ``_stamp`` patches ``lattice_brain.portability.fsops``, for ``_sha256_file``
32
- # or ``SQLiteToPostgresMigrator`` ``…portability.backups``, and for
33
- # ``load_recipient_identity`` ``…portability.sharing``.
34
- from ..archive import KDF_ITERATIONS as KDF_ITERATIONS
35
- from ..archive import BrainArchivePaths as BrainArchivePaths
36
- from ..archive import EncryptedBrainArchive as EncryptedBrainArchive
37
- from ..archive import _derive_key as _derive_key
38
- from ..gates import FeatureGate as FeatureGate
39
- from ..quiet import quiet as quiet
40
- from ..sealed_box import SEALED_BOX_ALGORITHM as SEALED_BOX_ALGORITHM
41
- from ..sealed_box import public_key_fingerprint as public_key_fingerprint
42
- from ..sealed_box import seal as seal
43
- from ..storage import DockerPostgresWizard as DockerPostgresWizard
44
- from ..storage import PostgresEngine as PostgresEngine
45
- from ..storage import SQLiteToPostgresMigrator as SQLiteToPostgresMigrator
46
- from ..utils import utc_now_iso as utc_now_iso
47
- from .backups import KGPortabilityBackupMixin as KGPortabilityBackupMixin
48
- from .backups import _sha256_file as _sha256_file
49
- from .bundles import _canonical_digest as _canonical_digest
50
- from .bundles import _expand_neighbors as _expand_neighbors
51
- from .bundles import _node_source_type as _node_source_type
52
- from .bundles import _redact_node as _redact_node
53
- from .bundles import _redact_provenance_row as _redact_provenance_row
54
- from .bundles import _scope_node as _scope_node
55
- from .bundles import _select_node_ids as _select_node_ids
56
- from .bundles import _shared_node_summary as _shared_node_summary
57
- from .bundles import _strip_fields as _strip_fields
58
- from .constants import _NEIGHBOR_EXCLUDED_TYPES as _NEIGHBOR_EXCLUDED_TYPES
59
- from .constants import _OPEN_REVIEW_STATUSES as _OPEN_REVIEW_STATUSES
60
- from .constants import _PAYLOAD_KEYS as _PAYLOAD_KEYS
61
- from .constants import _REDACTED_FIELDS as _REDACTED_FIELDS
62
- from .constants import _TRUTHY as _TRUTHY
63
- from .constants import BACKUP_FORMAT as BACKUP_FORMAT
64
- from .constants import BRAIN_NETWORK_DISABLED_DETAIL as BRAIN_NETWORK_DISABLED_DETAIL
65
- from .constants import BRAIN_NETWORK_ENV as BRAIN_NETWORK_ENV
66
- from .constants import BRAIN_NETWORK_GATE as BRAIN_NETWORK_GATE
67
- from .constants import ENCRYPTION_MODES as ENCRYPTION_MODES
68
- from .constants import FORMAT as FORMAT
69
- from .constants import FORMAT_VERSION as FORMAT_VERSION
70
- from .constants import SUBGRAPH_ARCHIVE_FORMAT as SUBGRAPH_ARCHIVE_FORMAT
71
- from .constants import SUBGRAPH_FORMAT as SUBGRAPH_FORMAT
72
- from .constants import SUBGRAPH_FORMAT_VERSION as SUBGRAPH_FORMAT_VERSION
73
- from .constants import SUBGRAPH_PROPOSAL_CAP as SUBGRAPH_PROPOSAL_CAP
74
- from .constants import SUBGRAPH_REVIEW_KIND as SUBGRAPH_REVIEW_KIND
75
- from .constants import SUBGRAPH_REVIEW_SOURCE as SUBGRAPH_REVIEW_SOURCE
76
- from .constants import BrainNetworkDisabled as BrainNetworkDisabled
77
- from .constants import _stamp as _stamp
78
- from .constants import brain_network_enabled as brain_network_enabled
79
- from .constants import require_brain_network as require_brain_network
80
- from .fsops import _checkpoint_sqlite as _checkpoint_sqlite
81
- from .fsops import _pre_restore_backup_dir as _pre_restore_backup_dir
82
- from .fsops import _replace_sqlite_atomically as _replace_sqlite_atomically
83
- from .fsops import _replace_tree_with_backup as _replace_tree_with_backup
84
- from .fsops import _restore_sibling as _restore_sibling
85
- from .fsops import _rollback_sqlite_from_backup as _rollback_sqlite_from_backup
86
- from .fsops import _safe_zip_names as _safe_zip_names
87
- from .fsops import _sqlite_siblings as _sqlite_siblings
88
- from .service import KGPortabilityService as KGPortabilityService
89
- from .sharing import KGPortabilitySharingMixin as KGPortabilitySharingMixin
90
- from .sharing import load_recipient_identity as load_recipient_identity
@@ -1,42 +0,0 @@
1
- """The seam the two portability mixins share.
2
-
3
- ``KGPortabilityService`` is assembled from :class:`KGPortabilitySharingMixin`
4
- and :class:`KGPortabilityBackupMixin`. Both read constructor state they do not
5
- own and both call the availability guard the service defines. That contract
6
- existed as an unwritten convention; this module writes it down.
7
-
8
- Typing-only, exactly like :mod:`lattice_brain.graph._kg_contract`: at runtime
9
- the mixins alias it to ``object``, so the MRO and every method resolution stay
10
- byte-for-byte what the single-file class had. Adding a cross-mixin call without
11
- declaring it here is a type error.
12
- """
13
-
14
- from __future__ import annotations
15
-
16
- from pathlib import Path
17
- from typing import Any
18
-
19
-
20
- class PortabilityCore:
21
- """What either portability mixin may assume about ``self``.
22
-
23
- Never instantiated and never inherited at runtime — see the module
24
- docstring. Members are declared, not implemented: the implementation lives
25
- in ``service.py`` (state and availability) or in whichever mixin owns it.
26
- """
27
-
28
- # ── State owned by KGPortabilityService.__init__ ─────────────────────────
29
- _kg: Any
30
- _data_dir: Path
31
- _enable: bool
32
- _exports_dir: Path
33
- _identity: Any
34
- _recipient: Any
35
- _recipient_loaded: bool
36
-
37
- # ── service.py: the availability guard every write door calls ────────────
38
- def available(self) -> bool:
39
- raise NotImplementedError
40
-
41
- def _require(self) -> None:
42
- raise NotImplementedError
@@ -1,338 +0,0 @@
1
- """Binary backup/restore, the encrypted archive, and the storage status reads.
2
-
3
- The half of :class:`~lattice_brain.portability.KGPortabilityService` that moves
4
- *files*: a ZIP snapshot of the database plus blobs, the passphrase-encrypted
5
- ``.latticebrain`` archive, and the status/migration surfaces that sit on top of
6
- them (a Postgres migration refuses to start without a verified backup).
7
-
8
- Integrity hashing (``_sha256_file``) and the Postgres migrator are looked up as
9
- this module's globals, so a test standing in for either patches *this* module.
10
- """
11
-
12
- from __future__ import annotations
13
-
14
- import json
15
- import logging
16
- import tempfile
17
- import zipfile
18
- from pathlib import Path
19
- from typing import Any, Dict, Optional
20
-
21
- from ..archive import BrainArchivePaths, EncryptedBrainArchive
22
- from ..storage import (
23
- DockerPostgresWizard,
24
- PostgresEngine,
25
- SQLiteToPostgresMigrator,
26
- )
27
- from ..utils import sha256_file as _sha256_file
28
- from ..utils import utc_now_iso
29
- from ._contract import PortabilityCore as _Core
30
- from .constants import BACKUP_FORMAT, FORMAT_VERSION, _stamp
31
- from .fsops import (
32
- _pre_restore_backup_dir,
33
- _replace_sqlite_atomically,
34
- _replace_tree_with_backup,
35
- _rollback_sqlite_from_backup,
36
- _safe_zip_names,
37
- )
38
-
39
-
40
- class KGPortabilityBackupMixin(_Core):
41
- """Backup / restore / storage status. Mixed into ``KGPortabilityService``."""
42
-
43
- # ── binary backup / restore ──────────────────────────────────────────────
44
- def backup(self, dest_path=None) -> Dict[str, Any]:
45
- self._require()
46
- self._exports_dir.mkdir(parents=True, exist_ok=True)
47
- dest = Path(dest_path) if dest_path else self._exports_dir / f"kg-backup-{_stamp()}.zip"
48
- with tempfile.TemporaryDirectory() as tmp_s:
49
- tmp = Path(tmp_s)
50
- db_copy = tmp / "knowledge_graph.sqlite"
51
- self._kg.backup_database(db_copy)
52
- manifest = {
53
- "format": BACKUP_FORMAT,
54
- "format_version": FORMAT_VERSION,
55
- **self._kg.schema_versions(),
56
- "created_at": utc_now_iso(),
57
- "db_sha256": _sha256_file(db_copy),
58
- "has_blobs": Path(self._kg.blob_dir).exists(),
59
- }
60
- with zipfile.ZipFile(dest, "w", zipfile.ZIP_DEFLATED) as zf:
61
- zf.write(db_copy, "knowledge_graph.sqlite")
62
- zf.writestr("manifest.json", json.dumps(manifest, ensure_ascii=False, indent=2))
63
- blob_dir = Path(self._kg.blob_dir)
64
- if blob_dir.exists():
65
- for f in blob_dir.rglob("*"):
66
- if f.is_file():
67
- zf.write(f, f"blobs/{f.relative_to(blob_dir)}")
68
- return {"path": str(dest), "bytes": dest.stat().st_size, "manifest": manifest}
69
-
70
- def restore(
71
- self,
72
- archive_path,
73
- *,
74
- verify: bool = True,
75
- dry_run: bool = False,
76
- confirm: bool = False,
77
- ) -> Dict[str, Any]:
78
- self._require()
79
- archive = Path(archive_path)
80
- if not archive.exists():
81
- raise FileNotFoundError(f"Backup archive not found: {archive}")
82
- if not dry_run and not confirm:
83
- raise ValueError("Explicit confirmation is required before restoring a Knowledge Graph backup.")
84
- with zipfile.ZipFile(archive) as zf:
85
- names = zf.namelist()
86
- _safe_zip_names(names)
87
- if "knowledge_graph.sqlite" not in names:
88
- raise ValueError("Archive is missing knowledge_graph.sqlite.")
89
- manifest = json.loads(zf.read("manifest.json")) if "manifest.json" in names else {}
90
- with tempfile.TemporaryDirectory() as tmp_s:
91
- tmp = Path(tmp_s)
92
- zf.extractall(tmp)
93
- db_src = tmp / "knowledge_graph.sqlite"
94
- if verify and manifest.get("db_sha256"):
95
- if _sha256_file(db_src) != manifest["db_sha256"]:
96
- raise ValueError("Backup integrity check failed (db sha256 mismatch).")
97
- if dry_run:
98
- return {
99
- "restored": False,
100
- "dry_run": True,
101
- "verified": True,
102
- "manifest": manifest,
103
- "planned": {
104
- "database": str(self._kg.db_path),
105
- "blobs": str(self._kg.blob_dir),
106
- "archive": str(archive),
107
- },
108
- }
109
- db_dest = Path(self._kg.db_path)
110
- blob_dest = Path(self._kg.blob_dir)
111
- backup_dir = _pre_restore_backup_dir(db_dest)
112
- try:
113
- _replace_sqlite_atomically(db_src, db_dest, backup_dir)
114
- blob_src = tmp / "blobs"
115
- _replace_tree_with_backup(blob_src if blob_src.exists() else None, blob_dest, backup_dir)
116
- except Exception:
117
- # The rollback is best-effort recovery; an I/O slip inside
118
- # it must never mask the restore failure being reported.
119
- try:
120
- _rollback_sqlite_from_backup(db_dest, backup_dir)
121
- except OSError as rollback_exc:
122
- logging.warning(
123
- "pre-restore rollback incomplete (backup kept at %s): %s",
124
- backup_dir, rollback_exc,
125
- )
126
- raise
127
- stats = self._kg.stats()
128
- return {
129
- "restored": True,
130
- "manifest": manifest,
131
- "pre_restore_backup": str(backup_dir),
132
- "nodes": sum(stats.get("nodes", {}).values()),
133
- }
134
-
135
- def verify_backup(self, archive_path) -> Dict[str, Any]:
136
- archive = Path(archive_path)
137
- if not archive.exists():
138
- return {"ok": False, "path": str(archive), "errors": [f"Backup archive not found: {archive}"]}
139
- try:
140
- with zipfile.ZipFile(archive) as zf:
141
- names = zf.namelist()
142
- _safe_zip_names(names)
143
- if "knowledge_graph.sqlite" not in names:
144
- raise ValueError("Archive is missing knowledge_graph.sqlite.")
145
- manifest = json.loads(zf.read("manifest.json")) if "manifest.json" in names else {}
146
- with tempfile.TemporaryDirectory() as tmp_s:
147
- tmp = Path(tmp_s)
148
- zf.extract("knowledge_graph.sqlite", tmp)
149
- db_src = tmp / "knowledge_graph.sqlite"
150
- if manifest.get("db_sha256") and _sha256_file(db_src) != manifest["db_sha256"]:
151
- raise ValueError("Backup integrity check failed (db sha256 mismatch).")
152
- return {"ok": True, "path": str(archive), "manifest": manifest, "errors": []}
153
- except (ValueError, zipfile.BadZipFile, OSError, json.JSONDecodeError) as exc:
154
- return {"ok": False, "path": str(archive), "errors": [str(exc)]}
155
-
156
- # ── encrypted .latticebrain archive ───────────────────────────────────
157
- def encrypted_archive(self, dest_path=None, *, passphrase: str) -> Dict[str, Any]:
158
- self._require()
159
- self._exports_dir.mkdir(parents=True, exist_ok=True)
160
- dest = Path(dest_path) if dest_path else self._exports_dir / f"brain-{_stamp()}.latticebrain"
161
- metadata = {
162
- "storage": self.storage_status().get("active", {}),
163
- "snapshot": self.snapshot_metadata(),
164
- "device_identity": self._identity.describe() if self._identity is not None else {},
165
- "provenance": {"exported_at": utc_now_iso(), "source": "kg-portability"},
166
- }
167
- archive = EncryptedBrainArchive(
168
- BrainArchivePaths(
169
- db_path=Path(self._kg.db_path),
170
- blob_dir=Path(self._kg.blob_dir),
171
- data_dir=self._data_dir,
172
- metadata=metadata,
173
- )
174
- )
175
- return archive.create(dest, passphrase=passphrase)
176
-
177
- def inspect_encrypted_archive(self, archive_path, *, passphrase: Optional[str] = None) -> Dict[str, Any]:
178
- archive = EncryptedBrainArchive(
179
- BrainArchivePaths(
180
- db_path=Path(self._kg.db_path),
181
- blob_dir=Path(self._kg.blob_dir),
182
- data_dir=self._data_dir,
183
- )
184
- )
185
- return archive.inspect(Path(archive_path), passphrase=passphrase)
186
-
187
- def verify_encrypted_archive(self, archive_path, *, passphrase: str) -> Dict[str, Any]:
188
- archive = EncryptedBrainArchive(
189
- BrainArchivePaths(
190
- db_path=Path(self._kg.db_path),
191
- blob_dir=Path(self._kg.blob_dir),
192
- data_dir=self._data_dir,
193
- )
194
- )
195
- return archive.verify(Path(archive_path), passphrase=passphrase)
196
-
197
- def restore_encrypted_archive(
198
- self,
199
- archive_path,
200
- *,
201
- passphrase: str,
202
- dry_run: bool = False,
203
- confirm: bool = False,
204
- ) -> Dict[str, Any]:
205
- self._require()
206
- archive = EncryptedBrainArchive(
207
- BrainArchivePaths(
208
- db_path=Path(self._kg.db_path),
209
- blob_dir=Path(self._kg.blob_dir),
210
- data_dir=self._data_dir,
211
- )
212
- )
213
- return archive.restore(
214
- Path(archive_path),
215
- passphrase=passphrase,
216
- target=BrainArchivePaths(
217
- db_path=Path(self._kg.db_path),
218
- blob_dir=Path(self._kg.blob_dir),
219
- data_dir=self._data_dir,
220
- ),
221
- dry_run=dry_run,
222
- confirm=confirm,
223
- )
224
-
225
- def import_encrypted_archive(
226
- self,
227
- archive_path,
228
- *,
229
- passphrase: str,
230
- dry_run: bool = False,
231
- confirm: bool = False,
232
- ) -> Dict[str, Any]:
233
- result = self.restore_encrypted_archive(
234
- archive_path,
235
- passphrase=passphrase,
236
- dry_run=dry_run,
237
- confirm=confirm,
238
- )
239
- result["operation"] = "import"
240
- return result
241
-
242
- # ── status surface ───────────────────────────────────────────────────────
243
- def snapshot_metadata(self) -> Dict[str, Any]:
244
- if not self.available():
245
- return {"available": False}
246
- return {
247
- "available": True,
248
- **self._kg.schema_versions(),
249
- "stats": self._kg.stats(),
250
- "provenance": self._kg.provenance_stats(),
251
- "storage": (
252
- self._kg.storage_engine.capabilities().as_dict()
253
- if getattr(self._kg, "storage_engine", None) is not None
254
- else {"engine": "sqlite", "available": True}
255
- ),
256
- }
257
-
258
- def storage_status(self) -> Dict[str, Any]:
259
- if not self.available():
260
- return {"available": False}
261
- return {
262
- "available": True,
263
- "active": (
264
- self._kg.storage_engine.capabilities().as_dict()
265
- if getattr(self._kg, "storage_engine", None) is not None
266
- else {"engine": "sqlite", "available": True}
267
- ),
268
- "postgres": PostgresEngine("", schema="lattice_brain").capabilities().as_dict(),
269
- "backup_health": self.backup_health(),
270
- }
271
-
272
- def backup_health(self) -> Dict[str, Any]:
273
- self._exports_dir.mkdir(parents=True, exist_ok=True)
274
- backups = sorted(
275
- [
276
- p for p in self._exports_dir.glob("*")
277
- if p.is_file() and (p.suffix == ".zip" or p.suffix == ".latticebrain")
278
- ],
279
- key=lambda p: p.stat().st_mtime,
280
- reverse=True,
281
- )
282
- latest = backups[0] if backups else None
283
- return {
284
- "available": True,
285
- "directory": str(self._exports_dir),
286
- "count": len(backups),
287
- "latest": str(latest) if latest else None,
288
- "latest_bytes": latest.stat().st_size if latest else 0,
289
- "encrypted_archives": sum(1 for p in backups if p.suffix == ".latticebrain"),
290
- "zip_backups": sum(1 for p in backups if p.suffix == ".zip"),
291
- }
292
-
293
- def postgres_docker_setup(
294
- self,
295
- *,
296
- consent: bool,
297
- dry_run: bool = False,
298
- port: int = 5432,
299
- ) -> Dict[str, Any]:
300
- wizard = DockerPostgresWizard(self._data_dir / "postgres", port=port)
301
- return wizard.start(consent=consent, dry_run=dry_run)
302
-
303
- def migrate_sqlite_to_postgres(
304
- self,
305
- *,
306
- dsn: str,
307
- schema: str = "lattice_brain",
308
- dry_run: bool = True,
309
- ) -> Dict[str, Any]:
310
- self._require()
311
- if not dsn:
312
- raise ValueError("Postgres DSN is required for SQLite to Postgres migration.")
313
- migrator = SQLiteToPostgresMigrator(
314
- Path(self._kg.db_path),
315
- PostgresEngine(dsn, schema=schema),
316
- )
317
- if dry_run:
318
- return migrator.migrate(dry_run=True)
319
- backup = self.backup()
320
- verification = self.verify_backup(backup["path"])
321
- if not verification.get("ok"):
322
- raise RuntimeError(
323
- "Pre-migration backup verification failed; Postgres migration was not started: "
324
- + "; ".join(verification.get("errors") or [])
325
- )
326
- result = migrator.migrate(dry_run=False)
327
- result["pre_migration_backup"] = {
328
- "path": backup["path"],
329
- "verified": True,
330
- "manifest": backup.get("manifest"),
331
- }
332
- return result
333
-
334
- def recent_ingestions(self, *, limit: int = 50, source_type: Optional[str] = None) -> Dict[str, Any]:
335
- """Recent provenance records (newest first) for the ingestion-sources UI."""
336
- if not self.available():
337
- return {"items": [], "count": 0}
338
- return self._kg.list_provenance(limit=limit, source_type=source_type)
@@ -1,136 +0,0 @@
1
- """Pure shaping of a shared subgraph bundle: select, expand, redact, digest.
2
-
3
- None of these touch a store or a file. They decide *what* travels — which
4
- nodes the selection names, which neighbours a one-hop expansion may admit, and
5
- which sender-identifying fields are stripped on the way out — and they pin the
6
- result with the digest the signature covers.
7
- """
8
-
9
- from __future__ import annotations
10
-
11
- import hashlib
12
- import json
13
- from typing import Any, Dict, List, Optional, Set
14
-
15
- from .constants import _NEIGHBOR_EXCLUDED_TYPES, _REDACTED_FIELDS
16
-
17
-
18
- def _canonical_digest(payload: Dict[str, Any]) -> str:
19
- """sha256 over the canonical JSON of a bundle payload.
20
-
21
- Pinned in the signed header so the Ed25519 signature covers the contents,
22
- not just the manifest describing them.
23
- """
24
- canonical = json.dumps(payload, sort_keys=True, ensure_ascii=False).encode("utf-8")
25
- return hashlib.sha256(canonical).hexdigest()
26
-
27
-
28
- def _node_source_type(node: Dict[str, Any]) -> str:
29
- try:
30
- metadata = json.loads(node.get("metadata_json") or "{}")
31
- except (TypeError, ValueError):
32
- return ""
33
- if not isinstance(metadata, dict):
34
- return ""
35
- return str(metadata.get("source_type") or "").strip().lower()
36
-
37
-
38
- def _select_node_ids(nodes: List[Dict[str, Any]], selection: Dict[str, Any]) -> Set[str]:
39
- wanted_ids = set(selection["node_ids"])
40
- wanted_types = set(selection["node_types"])
41
- wanted_sources = set(selection["source_types"])
42
- keep: Set[str] = set()
43
- for node in nodes:
44
- node_id = str(node.get("id"))
45
- if node_id in wanted_ids:
46
- keep.add(node_id)
47
- continue
48
- if wanted_types and str(node.get("type") or "").strip().lower() in wanted_types:
49
- keep.add(node_id)
50
- continue
51
- if wanted_sources and _node_source_type(node) in wanted_sources:
52
- keep.add(node_id)
53
- return keep
54
-
55
-
56
- def _expand_neighbors(
57
- keep: Set[str], edges: List[Dict[str, Any]], known: Dict[str, str]
58
- ) -> Set[str]:
59
- """One hop out from the selection, in both directions, within the scope.
60
-
61
- ``Person`` and ``Source`` neighbours are never pulled in implicitly: a
62
- Person node's label *is* an email address and a Source node's summary *is*
63
- a local file path, so "share this decision and what it touches" would
64
- quietly ship the sender's identity and directory layout. Naming one of
65
- those node ids explicitly still exports it — that is a decision, not a
66
- side effect.
67
- """
68
- expanded = set(keep)
69
-
70
- def _admit(candidate: str) -> None:
71
- if candidate in known and known[candidate] not in _NEIGHBOR_EXCLUDED_TYPES:
72
- expanded.add(candidate)
73
-
74
- for edge in edges:
75
- source = str(edge.get("from_node"))
76
- target = str(edge.get("to_node"))
77
- if source in keep:
78
- _admit(target)
79
- if target in keep:
80
- _admit(source)
81
- return expanded
82
-
83
-
84
- def _strip_fields(raw_json: Optional[str]) -> Optional[str]:
85
- if not raw_json:
86
- return raw_json
87
- try:
88
- parsed = json.loads(raw_json)
89
- except (TypeError, ValueError):
90
- return raw_json
91
- if not isinstance(parsed, dict):
92
- return raw_json
93
- cleaned = {k: v for k, v in parsed.items() if k not in _REDACTED_FIELDS}
94
- return json.dumps(cleaned, ensure_ascii=False, sort_keys=True)
95
-
96
-
97
- def _redact_node(node: Dict[str, Any]) -> Dict[str, Any]:
98
- redacted = dict(node)
99
- redacted["metadata_json"] = _strip_fields(node.get("metadata_json"))
100
- redacted["raw_json"] = _strip_fields(node.get("raw_json"))
101
- return redacted
102
-
103
-
104
- def _redact_provenance_row(row: Dict[str, Any]) -> Dict[str, Any]:
105
- redacted = dict(row)
106
- redacted["owner"] = None
107
- redacted["source_uri"] = None
108
- redacted["permissions_json"] = "{}"
109
- redacted["metadata_json"] = _strip_fields(row.get("metadata_json"))
110
- return redacted
111
-
112
-
113
- def _scope_node(node: Dict[str, Any], workspace_id: str) -> Dict[str, Any]:
114
- """Stamp the accepting workspace onto an incoming node.
115
-
116
- Received knowledge belongs to the workspace that accepted it. Without this
117
- the projection would file it as legacy-global — machine-shared — which is a
118
- wider scope than the reviewer chose.
119
- """
120
- scoped = dict(node)
121
- try:
122
- metadata = json.loads(scoped.get("metadata_json") or "{}")
123
- except (TypeError, ValueError):
124
- metadata = {}
125
- if not isinstance(metadata, dict):
126
- metadata = {}
127
- metadata["workspace_id"] = workspace_id
128
- scoped["metadata_json"] = json.dumps(metadata, ensure_ascii=False, sort_keys=True)
129
- return scoped
130
-
131
-
132
- def _shared_node_summary(node: Dict[str, Any], verdict: Dict[str, Any]) -> str:
133
- body = str(node.get("summary") or "").strip()
134
- origin = verdict.get("origin") or "unknown device"
135
- prefix = f"[{origin}] "
136
- return (prefix + body)[:500] if body else prefix.strip()