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,186 +0,0 @@
1
- """The Memory Manager: one cross-tier report over every backing store.
2
-
3
- :meth:`manager` is the service's centre of gravity — sources, counts, sizes,
4
- health and the Brain readiness signal in a single read that the brief, the
5
- proof and the API all build on. Nothing here is estimated: a tier with no
6
- backing reports ``unavailable`` and contributes ``None``, never a zero dressed
7
- up as a measurement.
8
-
9
- ``_brain_readiness`` lives here rather than in the frontend so the growth score
10
- is auditable and the Memory Manager stays the single owner of cross-tier
11
- signals.
12
- """
13
-
14
- from __future__ import annotations
15
-
16
- from typing import Any, Dict, List, Optional
17
-
18
- from latticeai.core.timeutil import now_iso as _now
19
- from latticeai.core.workspace_os_utils import _file_size
20
-
21
- from ._contract import MemoryCore as _Core
22
- from .constants import TIERS
23
-
24
-
25
- class MemoryManagerMixin(_Core):
26
- """Sources / usage / health. Mixed into ``MemoryService``."""
27
-
28
- # ── Memory Manager: sources / usage / health ──────────────────────────
29
- def _brain_readiness(
30
- self,
31
- *,
32
- memory_count: int,
33
- concept_count: Optional[int],
34
- relationship_count: Optional[int],
35
- healthy_sources: int,
36
- ) -> Dict[str, Any]:
37
- """Summarize how ready the user's Brain is for the product UI.
38
-
39
- The frontend should render this signal instead of re-deriving it from
40
- UI fragments. Keeping it here makes the score auditable and keeps the
41
- Memory Manager as the single owner of cross-tier Brain growth signals.
42
- """
43
- concepts = max(0, int(concept_count or 0))
44
- relationships = max(0, int(relationship_count or 0))
45
- memories = max(0, int(memory_count or 0))
46
- healthy = max(0, int(healthy_sources or 0))
47
- score = min(100, round(memories * 12 + concepts * 8 + relationships * 4 + healthy * 3))
48
-
49
- if memories < 1 and concepts < 1:
50
- state = "quiet"
51
- depth = 2
52
- title_key = "brain.readiness.quiet"
53
- action_key = "brain.readiness.start"
54
- score = max(12, score)
55
- elif concepts < 3 or relationships < 2:
56
- state = "forming"
57
- depth = 3 if concepts < 3 else 4
58
- title_key = "brain.readiness.forming"
59
- action_key = "brain.readiness.grow"
60
- score = max(38, score)
61
- else:
62
- state = "alive"
63
- depth = 5
64
- title_key = "brain.readiness.alive"
65
- action_key = "brain.readiness.map"
66
- score = max(72, score)
67
-
68
- return {
69
- "score": score,
70
- "state": state,
71
- "depth": depth,
72
- "title_key": title_key,
73
- "action_key": action_key,
74
- "signals": {
75
- "memory_count": memories,
76
- "concept_count": concepts,
77
- "relationship_count": relationships,
78
- "healthy_sources": healthy,
79
- },
80
- "source": "memory_service",
81
- }
82
-
83
- def manager(self, *, user_email: Optional[str] = None, workspace_id: Optional[str] = None) -> Dict[str, Any]:
84
- ws_mem = self._workspace_memories(user_email=user_email, workspace_id=workspace_id or "personal")
85
- if workspace_id is None:
86
- project_mem = [m for m in self._all_memories() if (m.get("workspace_id") or "personal") != "personal"]
87
- else:
88
- project_mem = self._workspace_memories(user_email=user_email, workspace_id=workspace_id)
89
- snaps = self._snapshots(workspace_id=workspace_id)
90
- convs = self._scoped_conversations(user_email=user_email, workspace_id=workspace_id)
91
- kg_stats = self._kg_stats()
92
- kg_index = self._kg_index()
93
-
94
- ws_bytes = _file_size(self._data_dir / "workspace_os.json")
95
- kg_bytes = _file_size(self._data_dir / "knowledge_graph.sqlite")
96
- if self._conversation_store is not None:
97
- conv_bytes = int(getattr(self._conversation_store, "size_bytes", lambda: 0)())
98
- else:
99
- conv_bytes = _file_size(self._history_file)
100
-
101
- node_total = sum((kg_stats or {}).get("nodes", {}).values()) if kg_stats else None
102
- edge_total = sum((kg_stats or {}).get("edges", {}).values()) if kg_stats else None
103
- vector_total = None
104
- if kg_index and isinstance(kg_index.get("vector_counts"), dict):
105
- vector_total = sum(kg_index["vector_counts"].values())
106
- elif kg_index:
107
- vector_total = kg_index.get("indexed") or kg_index.get("ready")
108
-
109
- sources: List[Dict[str, Any]] = [
110
- {
111
- "id": "workspace", "type": "workspace", "label": "Workspace Memory",
112
- "count": len(ws_mem), "size_bytes": ws_bytes if ws_mem else 0,
113
- "health": "ok", "detail": "Personal workspace knowledge, by kind.",
114
- },
115
- {
116
- "id": "project", "type": "project", "label": "Project Memory",
117
- "count": len(project_mem), "size_bytes": 0,
118
- "health": "ok", "detail": "Memory scoped to organization workspaces.",
119
- },
120
- {
121
- "id": "agent", "type": "agent", "label": "Agent Memory",
122
- "count": len(snaps), "size_bytes": 0,
123
- "health": "ok", "detail": "Per-run agent memory snapshots.",
124
- },
125
- {
126
- "id": "conversation", "type": "conversation", "label": "Conversation Memory",
127
- "count": len(convs), "size_bytes": conv_bytes,
128
- "health": "ok" if (self._conversation_store is not None or self._history_file.exists()) else "empty",
129
- "detail": "Historical interaction memory from chat.",
130
- },
131
- {
132
- "id": "graph", "type": "graph", "label": "Graph Memory",
133
- "count": node_total, "size_bytes": kg_bytes,
134
- "health": "ok" if kg_stats else "unavailable",
135
- "detail": "Knowledge Graph entities and relations." if kg_stats else "Knowledge graph disabled or unavailable.",
136
- "edges": edge_total,
137
- },
138
- {
139
- "id": "vector", "type": "vector", "label": "Vector Memory",
140
- "count": vector_total, "size_bytes": 0,
141
- "health": "ok" if kg_index else "unavailable",
142
- "detail": "Local embedding vector index." if kg_index else "Vector index unavailable.",
143
- },
144
- ]
145
- total_items = sum(int(s["count"] or 0) for s in sources)
146
- total_bytes = ws_bytes + kg_bytes + conv_bytes
147
- healthy = sum(1 for s in sources if s["health"] == "ok")
148
- overall = "ok" if healthy >= 4 else "degraded" if healthy >= 1 else "unavailable"
149
- memory_ids = {m.get("id") for m in [*ws_mem, *project_mem] if m.get("id")}
150
- memory_count = len(memory_ids) + len(snaps) + len(convs)
151
- return {
152
- "sources": sources,
153
- "recent_memories": self._manager_recent_memories([*ws_mem, *project_mem], limit=8),
154
- "tiers": list(TIERS),
155
- "usage": {"total_items": total_items, "total_bytes": total_bytes, "sources": len(sources)},
156
- "brain_readiness": self._brain_readiness(
157
- memory_count=memory_count,
158
- concept_count=node_total,
159
- relationship_count=edge_total,
160
- healthy_sources=healthy,
161
- ),
162
- "health": overall,
163
- "graph_enabled": self._enable_graph,
164
- "generated_at": _now(),
165
- }
166
-
167
- @staticmethod
168
- def _manager_recent_memories(memories: List[Dict[str, Any]], *, limit: int = 8) -> List[Dict[str, Any]]:
169
- rows: List[Dict[str, Any]] = []
170
- for item in memories[: max(1, limit)]:
171
- metadata = item.get("metadata") if isinstance(item.get("metadata"), dict) else {}
172
- rows.append({
173
- "id": item.get("id") or "",
174
- "kind": item.get("kind") or "memory",
175
- "content": str(item.get("content") or "")[:320],
176
- "tags": item.get("tags") if isinstance(item.get("tags"), list) else [],
177
- "metadata": metadata,
178
- "workspace_id": item.get("workspace_id") or "personal",
179
- "created_at": item.get("created_at"),
180
- "updated_at": item.get("updated_at"),
181
- })
182
- return rows
183
-
184
- def brain_quality_summary(self, *, user_email: Optional[str] = None, workspace_id: Optional[str] = None) -> Dict[str, Any]:
185
- """Return the backend-owned Brain readiness signal for API consumers."""
186
- return self.manager(user_email=user_email, workspace_id=workspace_id)["brain_readiness"]
@@ -1,136 +0,0 @@
1
- """Brain Proof: the durable evidence behind "your Brain outlives the model".
2
-
3
- Backend-owned on purpose. The first-run Brain screen must show the stores it
4
- can actually recall from, and the model-continuity claim has to be independent
5
- of whichever LLM happens to be loaded — a UI-derived version of this would be
6
- an assertion, not a proof.
7
-
8
- The two are deliberately separated: ``capability`` is always true (the design
9
- is model-independent), while ``proven`` stays false until there is durable
10
- evidence on disk to recall from.
11
- """
12
-
13
- from __future__ import annotations
14
-
15
- from typing import Any, Dict, Optional
16
-
17
- from latticeai.core.timeutil import now_iso as _now
18
-
19
- from ._contract import MemoryCore as _Core
20
-
21
-
22
- class MemoryProofMixin(_Core):
23
- """The proof points and the query they are demonstrated with."""
24
-
25
- def brain_proof(
26
- self,
27
- *,
28
- user_email: Optional[str] = None,
29
- workspace_id: Optional[str] = None,
30
- active_model: Optional[str] = None,
31
- recall_query: str = "",
32
- limit: int = 3,
33
- ) -> Dict[str, Any]:
34
- """Return the proof points that make Brain feel model-independent.
35
-
36
- This is intentionally backend-owned instead of UI-derived: the first-run
37
- Brain screen needs to show the durable stores it can actually recall
38
- from, and the model continuity claim must be independent from whichever
39
- LLM is currently loaded.
40
- """
41
- manager = self.manager(user_email=user_email, workspace_id=workspace_id)
42
- sources = {str(source.get("id")): source for source in manager.get("sources", []) if isinstance(source, dict)}
43
- readiness = manager.get("brain_readiness") or {}
44
- conversation_count = int(sources.get("conversation", {}).get("count") or 0)
45
- workspace_count = int(sources.get("workspace", {}).get("count") or 0)
46
- graph_count = int(sources.get("graph", {}).get("count") or 0)
47
- vector_count = int(sources.get("vector", {}).get("count") or 0)
48
- query = recall_query.strip() or self._latest_recall_query(user_email=user_email, workspace_id=workspace_id)
49
- recall: Dict[str, Any] = (
50
- self.recall(query, user_email=user_email, workspace_id=workspace_id, limit=limit)
51
- if query
52
- else {"query": "", "results": [], "count": 0, "source": "live"}
53
- )
54
- recall_items = [
55
- {
56
- "id": item.get("id"),
57
- "source": item.get("source"),
58
- "title": item.get("title"),
59
- "snippet": item.get("snippet"),
60
- "score": item.get("score", 0),
61
- # Evidence explainability: why this item was recalled, so the
62
- # citation UI can show the matched terms instead of a bare score.
63
- "matched_terms": item.get("matched_terms") or [],
64
- "confidence": item.get("confidence") or "low",
65
- # v11.1.0: what kind of memory this is, and — for a picture —
66
- # the caption and inline thumbnail the Evidence panel renders.
67
- "kind": item.get("kind") or "",
68
- **({"caption": item["caption"]} if item.get("caption") else {}),
69
- **({"thumbnail": item["thumbnail"]} if item.get("thumbnail") else {}),
70
- }
71
- for item in list(recall.get("results", []))[: max(1, min(limit, 8))]
72
- ]
73
- durable_items = workspace_count + conversation_count + graph_count
74
- # Capability and proof are deliberately separated. The brain is
75
- # architecturally model-independent, so the capability is always true.
76
- # The proof stays false until there is durable evidence on disk.
77
- has_durable_evidence = durable_items > 0
78
- proven_continuity = has_durable_evidence
79
- return {
80
- "status": readiness.get("state") or "quiet",
81
- "readiness": readiness,
82
- "model_continuity": {
83
- "active_model": active_model or "",
84
- "brain_owner": "lattice_brain",
85
- # Design capability: the brain is built to outlive any model.
86
- "capability": True,
87
- # Proof: only true when durable evidence exists to recall.
88
- "survives_model_switch": proven_continuity,
89
- "proven": proven_continuity,
90
- "context_store": "workspace + conversation + graph + vector",
91
- },
92
- "proofs": {
93
- "durable_items": durable_items,
94
- "has_durable_evidence": has_durable_evidence,
95
- "workspace_memories": workspace_count,
96
- "conversations": conversation_count,
97
- "graph_concepts": graph_count,
98
- "vector_items": vector_count,
99
- "healthy_sources": readiness.get("signals", {}).get("healthy_sources", 0),
100
- },
101
- "recall": {
102
- "query": recall.get("query") or query,
103
- "items": recall_items,
104
- "count": recall.get("count", 0),
105
- },
106
- "claims": {
107
- "can_recall_user_context": bool(recall_items or durable_items > 0),
108
- # Proven, not asserted: no durable evidence means no continuity claim.
109
- "keeps_context_across_models": proven_continuity,
110
- "is_knowledge_store": bool(graph_count or vector_count or workspace_count or conversation_count),
111
- },
112
- "generated_at": _now(),
113
- }
114
-
115
- def _latest_recall_query(self, *, user_email: Optional[str], workspace_id: Optional[str]) -> str:
116
- for memory in self._workspace_memories(user_email=user_email, workspace_id=workspace_id or "personal"):
117
- content = str(memory.get("content") or "").strip()
118
- if content:
119
- return content[:96]
120
- for conversation in self._conversations():
121
- messages = conversation.get("messages") or []
122
- if not isinstance(messages, list):
123
- continue
124
- for message in reversed(messages[-8:]):
125
- if not isinstance(message, dict):
126
- continue
127
- if user_email and message.get("user_email") != user_email:
128
- continue
129
- message_workspace = message.get("workspace_id") or "personal"
130
- target_workspace = workspace_id or "personal"
131
- if message_workspace != target_workspace:
132
- continue
133
- content = str(message.get("content") or "").strip()
134
- if content:
135
- return content[:96]
136
- return ""
@@ -1,225 +0,0 @@
1
- """Unified retrieval across the memory tiers, and single-tier inspection.
2
-
3
- :meth:`recall` blends three sources into one honestly comparable ranking:
4
-
5
- * **workspace** memories, scored lexically as the fraction of query tokens
6
- present — the same scorer both text tiers use, so cross-tier ordering is real
7
- rather than an artifact of per-tier constants;
8
- * **graph** nodes from the Knowledge Graph search;
9
- * **vector** neighbours (v9.3.0 hybrid recall), which is what lets recall find
10
- knowledge phrased differently from the query. The vector tier is optional:
11
- any failure degrades recall to pure lexical instead of breaking it.
12
-
13
- A quality gate then drops zero-score rows *only* when at least one row carries
14
- real evidence, so the gate can never empty a recall. Every row reports its
15
- confidence and which evidence kinds produced it.
16
- """
17
-
18
- from __future__ import annotations
19
-
20
- from typing import Any, Dict, List, Optional
21
-
22
- from ._contract import MemoryCore as _Core
23
- from .constants import LOGGER, TIERS, WORKSPACE_KINDS, _visual_fields
24
-
25
-
26
- class MemoryRecallMixin(_Core):
27
- """Recall, tiers, and per-tier inspection. Mixed into ``MemoryService``."""
28
-
29
- def tiers(self) -> Dict[str, Any]:
30
- return {"tiers": list(TIERS), "workspace_kinds": list(WORKSPACE_KINDS)}
31
-
32
- # ── recall (unified retrieval over the memory tiers) ───────────────────
33
- def recall(
34
- self,
35
- query: str,
36
- *,
37
- user_email: Optional[str] = None,
38
- workspace_id: Optional[str] = None,
39
- limit: int = 20,
40
- ) -> Dict[str, Any]:
41
- q = str(query or "").strip()
42
- query_tokens = [tok for tok in q.lower().split() if tok]
43
-
44
- def _matched_terms(*texts: Any) -> List[str]:
45
- haystack = " ".join(str(t or "") for t in texts).lower()
46
- return [tok for tok in query_tokens if tok in haystack]
47
-
48
- def _lexical_score(matched: List[str]) -> float:
49
- # Honest, comparable relevance: fraction of query tokens present.
50
- # Both tiers share this scorer so the cross-tier ranking is real,
51
- # not an artifact of per-tier constants.
52
- if not query_tokens:
53
- return 0.0
54
- return round(len(matched) / len(query_tokens), 4)
55
-
56
- results: List[Dict[str, Any]] = []
57
-
58
- errors: List[Dict[str, str]] = []
59
- try:
60
- mem = self._store.search_memories(q, user_email=user_email, limit=limit, workspace_id=workspace_id).get("memories", [])
61
- except Exception as exc:
62
- LOGGER.exception("workspace memory search failed")
63
- errors.append({"source": "workspace", "detail": str(exc)})
64
- mem = []
65
- for m in mem:
66
- matched = _matched_terms(m.get("content"), " ".join(m.get("tags") or []), m.get("kind"))
67
- results.append({
68
- "source": "workspace",
69
- "id": m.get("id"),
70
- "title": (m.get("kind") or "memory"),
71
- "snippet": str(m.get("content") or "")[:240],
72
- "kind": m.get("kind"),
73
- "score": _lexical_score(matched),
74
- "matched_terms": matched,
75
- "tags": m.get("tags") or [],
76
- })
77
-
78
- if self._enable_graph and q:
79
- try:
80
- # KnowledgeGraph.search returns {"query": ..., "matches": [...]}.
81
- search_kwargs = (
82
- {"allowed_workspaces": {workspace_id}}
83
- if workspace_id is not None
84
- else {}
85
- )
86
- hits = self._kg.search(q, limit, **search_kwargs).get("matches", [])
87
- except Exception as exc:
88
- LOGGER.exception("knowledge graph memory search failed")
89
- errors.append({"source": "graph", "detail": str(exc)})
90
- hits = []
91
- for hit in hits[:limit]:
92
- matched = _matched_terms(hit.get("title"), hit.get("name"), hit.get("summary"), hit.get("content"))
93
- results.append({
94
- "source": "graph",
95
- "id": hit.get("id") or hit.get("node_id"),
96
- "title": hit.get("title") or hit.get("name") or "node",
97
- "snippet": str(hit.get("summary") or hit.get("content") or "")[:240],
98
- "kind": hit.get("type") or "node",
99
- "score": _lexical_score(matched),
100
- "matched_terms": matched,
101
- **_visual_fields(hit),
102
- })
103
-
104
- # v9.3.0 hybrid recall: blend semantic similarity from the vector
105
- # index into the lexical ranking. Vector evidence lets recall find
106
- # knowledge phrased differently from the query — the main lexical
107
- # blind spot. The vector tier is optional: any failure degrades recall
108
- # back to pure lexical instead of breaking it.
109
- vector_used = False
110
- if self._enable_graph and q and hasattr(self._kg, "vector_search"):
111
- try:
112
- vector_hits = list(
113
- self._kg.vector_search(q, limit=limit).get("matches", [])
114
- )
115
- # Workspace scoping is server-owned: the vector index is
116
- # global, so scoped calls must filter matches to visible
117
- # nodes before they can influence results.
118
- if workspace_id is not None and vector_hits and hasattr(self._kg, "filter_scoped_nodes"):
119
- vector_hits = self._kg.filter_scoped_nodes(
120
- vector_hits, {workspace_id}, id_key="node_id"
121
- )
122
- vector_used = True
123
- except Exception as exc:
124
- LOGGER.exception("vector recall failed; falling back to lexical")
125
- errors.append({"source": "vector", "detail": str(exc)})
126
- vector_hits = []
127
- by_node_id = {str(r.get("id")): r for r in results if r.get("source") == "graph"}
128
- for hit in vector_hits:
129
- node_id = str(hit.get("node_id") or hit.get("id") or "")
130
- similarity = round(float(hit.get("score") or 0.0), 4)
131
- if similarity <= 0:
132
- continue
133
- # Citation precision (v9.9.6): a chunk hit knows where in the
134
- # document it came from (section heading / page). Carry that
135
- # locator onto the recall row so the citation can say it.
136
- hit_metadata = hit.get("metadata")
137
- locator = (
138
- str(hit_metadata.get("locator") or "")
139
- if isinstance(hit_metadata, dict)
140
- else ""
141
- )
142
- existing = by_node_id.get(node_id)
143
- if existing is not None:
144
- existing["vector_score"] = max(existing.get("vector_score", 0.0), similarity)
145
- existing["score"] = round(
146
- max(existing.get("score", 0.0), 0.4 * existing.get("score", 0.0) + 0.6 * similarity),
147
- 4,
148
- )
149
- if locator and not existing.get("locator"):
150
- existing["locator"] = locator
151
- else:
152
- matched = _matched_terms(hit.get("title"), hit.get("summary"))
153
- row = {
154
- "source": "graph",
155
- "id": node_id or hit.get("id"),
156
- "title": hit.get("title") or "node",
157
- "snippet": str(hit.get("summary") or "")[:240],
158
- "kind": hit.get("type") or "node",
159
- "score": round(max(_lexical_score(matched), 0.6 * similarity), 4),
160
- "matched_terms": matched,
161
- "vector_score": similarity,
162
- **({"locator": locator} if locator else {}),
163
- **_visual_fields(hit),
164
- }
165
- results.append(row)
166
- if node_id:
167
- by_node_id[node_id] = row
168
-
169
- # Quality gate: when at least one result carries real evidence
170
- # (lexical term hits, or semantic similarity in hybrid mode),
171
- # zero-score rows are noise relative to it and are dropped. When
172
- # nothing scores (e.g. tokenization mismatch), everything is kept so
173
- # the tiers' own search filters still decide — the gate never empties
174
- # a recall.
175
- candidates = len(results)
176
- if query_tokens and any(r.get("score", 0) > 0 for r in results):
177
- results = [r for r in results if r.get("score", 0) > 0]
178
- for r in results:
179
- r["confidence"] = "high" if r.get("score", 0) >= 0.65 else "medium" if r.get("score", 0) >= 0.3 else "low"
180
- evidence = []
181
- if r.get("matched_terms"):
182
- evidence.append("lexical")
183
- if r.get("vector_score"):
184
- evidence.append("semantic")
185
- r["evidence_kinds"] = evidence
186
-
187
- results.sort(key=lambda r: r.get("score", 0), reverse=True)
188
- return {
189
- "query": q,
190
- "results": results[: max(1, min(limit, 100))],
191
- "count": len(results),
192
- "source": "live",
193
- "status": "degraded" if errors else "ok",
194
- "errors": errors,
195
- "quality_gate": {
196
- "candidates": candidates,
197
- "passed": len(results),
198
- "filtered": candidates - len(results),
199
- "gate": "hybrid-evidence/v2" if vector_used else "lexical-evidence/v1",
200
- },
201
- }
202
-
203
- # ── inspect a single tier ─────────────────────────────────────────────
204
- def inspect(self, source: str, *, user_email: Optional[str] = None, workspace_id: Optional[str] = None, limit: int = 50) -> Dict[str, Any]:
205
- if source == "workspace":
206
- items = self._workspace_memories(user_email=user_email, workspace_id=workspace_id or "personal")[:limit]
207
- return {"source": source, "items": items, "count": len(items)}
208
- if source == "project":
209
- if workspace_id is None:
210
- items = [m for m in self._all_memories() if (m.get("workspace_id") or "personal") != "personal"][:limit]
211
- else:
212
- items = self._workspace_memories(user_email=user_email, workspace_id=workspace_id)[:limit]
213
- return {"source": source, "items": items, "count": len(items)}
214
- if source == "agent":
215
- items = self._snapshots(workspace_id=workspace_id)[:limit]
216
- return {"source": source, "items": items, "count": len(items)}
217
- if source == "conversation":
218
- convs = self._scoped_conversations(user_email=user_email, workspace_id=workspace_id)
219
- items = [{"id": c.get("id"), "title": c.get("title") or c.get("id"), "messages": len(c.get("messages") or [])} for c in convs[:limit]]
220
- return {"source": source, "items": items, "count": len(convs)}
221
- if source == "graph":
222
- return {"source": source, "stats": self._kg_stats() or {}, "available": bool(self._kg_stats())}
223
- if source == "vector":
224
- return {"source": source, "index": self._kg_index() or {}, "available": bool(self._kg_index())}
225
- raise KeyError(source)
@@ -1,48 +0,0 @@
1
- """The composed memory service — constructor state plus the six surfaces.
2
-
3
- ``MemoryService`` owns the state every mixin reads (the workspace store, the
4
- optional Knowledge Graph, the data directory, and whichever conversation
5
- backing is wired) and inherits its behaviour from the store-read, manager,
6
- brief, proof, recall and maintenance mixins. Same public surface, same method
7
- resolution, in seven readable files instead of one twelve-hundred-line one.
8
- """
9
-
10
- from __future__ import annotations
11
-
12
- from pathlib import Path
13
- from typing import Any, Optional
14
-
15
- from .brief import MemoryBriefMixin
16
- from .maintenance import MemoryMaintenanceMixin
17
- from .manager import MemoryManagerMixin
18
- from .proof import MemoryProofMixin
19
- from .recall import MemoryRecallMixin
20
- from .stores import MemoryStoreReadsMixin
21
-
22
-
23
- class MemoryService(
24
- MemoryStoreReadsMixin,
25
- MemoryManagerMixin,
26
- MemoryBriefMixin,
27
- MemoryProofMixin,
28
- MemoryRecallMixin,
29
- MemoryMaintenanceMixin,
30
- ):
31
- def __init__(
32
- self,
33
- *,
34
- store: Any,
35
- data_dir: Path,
36
- knowledge_graph: Any = None,
37
- enable_graph: bool = True,
38
- history_file: Optional[Path] = None,
39
- conversation_store: Any = None,
40
- ):
41
- self._store = store
42
- self._kg = knowledge_graph
43
- self._enable_graph = bool(enable_graph and knowledge_graph is not None)
44
- self._data_dir = Path(data_dir)
45
- self._history_file = Path(history_file) if history_file else (self._data_dir / "chat_history.json")
46
- # v4: the durable SQLite conversation store supersedes the JSON file
47
- # as the conversation tier's backing store when provided.
48
- self._conversation_store = conversation_store