ltcai 9.9.9 → 10.2.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 (228) hide show
  1. package/README.md +51 -38
  2. package/docs/CHANGELOG.md +197 -0
  3. package/docs/COMMUNITY_AND_PLUGINS.md +1 -1
  4. package/docs/DEVELOPMENT.md +1 -1
  5. package/docs/HYBRID_CLOUD_KG_STREAMING.md +101 -0
  6. package/docs/ONBOARDING.md +1 -1
  7. package/docs/OPERATIONS.md +1 -1
  8. package/docs/TRUST_MODEL.md +1 -1
  9. package/docs/WHY_LATTICE.md +1 -1
  10. package/docs/kg-schema.md +1 -1
  11. package/lattice_brain/__init__.py +1 -1
  12. package/lattice_brain/archive.py +5 -4
  13. package/lattice_brain/conversations.py +14 -3
  14. package/lattice_brain/embeddings.py +12 -2
  15. package/lattice_brain/graph/_kg_common.py +5 -5
  16. package/lattice_brain/graph/_kg_fsutil.py +4 -3
  17. package/lattice_brain/graph/discovery.py +4 -3
  18. package/lattice_brain/graph/discovery_index.py +0 -1
  19. package/lattice_brain/graph/documents.py +3 -2
  20. package/lattice_brain/graph/fusion.py +4 -0
  21. package/lattice_brain/graph/ingest.py +12 -2
  22. package/lattice_brain/graph/projection.py +3 -2
  23. package/lattice_brain/graph/provenance.py +5 -3
  24. package/lattice_brain/graph/rerank.py +4 -1
  25. package/lattice_brain/graph/retrieval.py +0 -1
  26. package/lattice_brain/graph/retrieval_docgen.py +0 -1
  27. package/lattice_brain/graph/retrieval_reads.py +0 -1
  28. package/lattice_brain/graph/retrieval_vector.py +0 -1
  29. package/lattice_brain/graph/schema.py +4 -3
  30. package/lattice_brain/graph/store.py +18 -4
  31. package/lattice_brain/graph/write_master.py +46 -1
  32. package/lattice_brain/ingestion.py +4 -1
  33. package/lattice_brain/portability.py +5 -2
  34. package/lattice_brain/quality.py +12 -5
  35. package/lattice_brain/quiet.py +43 -0
  36. package/lattice_brain/runtime/agent_runtime.py +12 -8
  37. package/lattice_brain/runtime/hooks.py +2 -1
  38. package/lattice_brain/runtime/multi_agent.py +2 -3
  39. package/lattice_brain/sensitivity.py +94 -0
  40. package/lattice_brain/storage/base.py +30 -2
  41. package/lattice_brain/storage/migration.py +3 -2
  42. package/lattice_brain/storage/postgres.py +2 -2
  43. package/lattice_brain/storage/sqlite.py +6 -4
  44. package/lattice_brain/workflow.py +4 -2
  45. package/latticeai/__init__.py +1 -1
  46. package/latticeai/api/admin.py +1 -1
  47. package/latticeai/api/agents.py +4 -2
  48. package/latticeai/api/auth.py +5 -1
  49. package/latticeai/api/automation_intelligence.py +2 -1
  50. package/latticeai/api/browser.py +3 -2
  51. package/latticeai/api/chat.py +28 -17
  52. package/latticeai/api/chat_agent_http.py +22 -8
  53. package/latticeai/api/chat_contracts.py +4 -0
  54. package/latticeai/api/chat_documents.py +6 -2
  55. package/latticeai/api/chat_hybrid.py +82 -0
  56. package/latticeai/api/computer_use.py +8 -3
  57. package/latticeai/api/knowledge_graph.py +1 -1
  58. package/latticeai/api/mcp.py +4 -4
  59. package/latticeai/api/models.py +5 -2
  60. package/latticeai/api/network_boundary.py +220 -0
  61. package/latticeai/api/permissions.py +0 -1
  62. package/latticeai/api/realtime.py +1 -1
  63. package/latticeai/api/security_dashboard.py +1 -1
  64. package/latticeai/api/setup.py +16 -3
  65. package/latticeai/api/static_routes.py +2 -1
  66. package/latticeai/api/tools.py +12 -8
  67. package/latticeai/api/voice_capture.py +3 -1
  68. package/latticeai/api/workflow_designer.py +1 -1
  69. package/latticeai/api/workspace.py +1 -2
  70. package/latticeai/app_factory.py +131 -78
  71. package/latticeai/cli/entrypoint.py +6 -4
  72. package/latticeai/core/agent.py +55 -495
  73. package/latticeai/core/agent_eval.py +2 -2
  74. package/latticeai/core/agent_helpers.py +493 -0
  75. package/latticeai/core/agent_prompts.py +0 -1
  76. package/latticeai/core/agent_registry.py +3 -1
  77. package/latticeai/core/agent_state.py +41 -0
  78. package/latticeai/core/audit.py +1 -1
  79. package/latticeai/core/builtin_hooks.py +2 -1
  80. package/latticeai/core/config.py +0 -1
  81. package/latticeai/core/embedding_providers.py +12 -1
  82. package/latticeai/core/file_generation.py +3 -0
  83. package/latticeai/core/invitations.py +4 -1
  84. package/latticeai/core/io_utils.py +3 -1
  85. package/latticeai/core/legacy_compatibility.py +1 -2
  86. package/latticeai/core/local_embeddings.py +12 -1
  87. package/latticeai/core/marketplace.py +1 -2
  88. package/latticeai/core/mcp_registry.py +0 -1
  89. package/latticeai/core/model_compat.py +1 -1
  90. package/latticeai/core/model_resolution.py +1 -1
  91. package/latticeai/core/network_boundary.py +168 -0
  92. package/latticeai/core/oidc.py +3 -0
  93. package/latticeai/core/permission_mode.py +6 -6
  94. package/latticeai/core/plugins.py +3 -2
  95. package/latticeai/core/policy.py +0 -1
  96. package/latticeai/core/quiet.py +84 -0
  97. package/latticeai/core/realtime.py +3 -2
  98. package/latticeai/core/run_store.py +4 -2
  99. package/latticeai/core/security.py +4 -0
  100. package/latticeai/core/users.py +5 -3
  101. package/latticeai/core/workspace_graph_trace.py +3 -0
  102. package/latticeai/core/workspace_os.py +65 -273
  103. package/latticeai/core/workspace_os_constants.py +126 -0
  104. package/latticeai/core/workspace_os_state.py +180 -0
  105. package/latticeai/core/workspace_os_utils.py +3 -1
  106. package/latticeai/core/workspace_permissions.py +1 -0
  107. package/latticeai/core/workspace_snapshots.py +3 -1
  108. package/latticeai/core/workspace_timeline.py +3 -1
  109. package/latticeai/integrations/telegram_bot.py +25 -16
  110. package/latticeai/models/router.py +6 -3
  111. package/latticeai/runtime/access_runtime.py +3 -1
  112. package/latticeai/runtime/audit_runtime.py +3 -2
  113. package/latticeai/runtime/chat_wiring.py +4 -1
  114. package/latticeai/runtime/history_runtime.py +1 -1
  115. package/latticeai/runtime/lifespan_runtime.py +3 -1
  116. package/latticeai/runtime/network_boundary_wiring.py +124 -0
  117. package/latticeai/runtime/persistence_runtime.py +3 -1
  118. package/latticeai/runtime/router_registration.py +11 -1
  119. package/latticeai/services/architecture_readiness.py +1 -2
  120. package/latticeai/services/change_proposals.py +2 -1
  121. package/latticeai/services/cloud_egress_audit.py +85 -0
  122. package/latticeai/services/cloud_extraction.py +129 -0
  123. package/latticeai/services/cloud_streaming.py +266 -0
  124. package/latticeai/services/cloud_token_guard.py +84 -0
  125. package/latticeai/services/command_center.py +2 -1
  126. package/latticeai/services/folder_watch.py +3 -0
  127. package/latticeai/services/funnel_metrics.py +3 -1
  128. package/latticeai/services/hybrid_chat.py +265 -0
  129. package/latticeai/services/hybrid_context.py +228 -0
  130. package/latticeai/services/hybrid_policy.py +178 -0
  131. package/latticeai/services/memory_service.py +1 -1
  132. package/latticeai/services/model_catalog.py +10 -1
  133. package/latticeai/services/model_engines.py +35 -14
  134. package/latticeai/services/model_loading.py +3 -2
  135. package/latticeai/services/model_runtime.py +68 -17
  136. package/latticeai/services/multimodal_streaming.py +123 -0
  137. package/latticeai/services/network_boundary_service.py +154 -0
  138. package/latticeai/services/openai_compatible_adapter.py +100 -0
  139. package/latticeai/services/p_reinforce.py +4 -0
  140. package/latticeai/services/platform_runtime.py +9 -4
  141. package/latticeai/services/process_audit.py +0 -1
  142. package/latticeai/services/product_readiness.py +1 -1
  143. package/latticeai/services/run_executor.py +3 -2
  144. package/latticeai/services/search_service.py +1 -4
  145. package/latticeai/services/setup_detection.py +2 -1
  146. package/latticeai/services/tool_dispatch.py +7 -2
  147. package/latticeai/services/upload_service.py +2 -1
  148. package/latticeai/setup/auto_setup.py +10 -7
  149. package/latticeai/setup/wizard.py +15 -11
  150. package/latticeai/tools/__init__.py +3 -3
  151. package/latticeai/tools/commands.py +7 -7
  152. package/latticeai/tools/computer.py +3 -2
  153. package/latticeai/tools/documents.py +10 -9
  154. package/latticeai/tools/filesystem.py +7 -4
  155. package/latticeai/tools/knowledge.py +2 -0
  156. package/latticeai/tools/local_files.py +1 -1
  157. package/latticeai/tools/network.py +2 -1
  158. package/package.json +6 -4
  159. package/scripts/bench_agent_smoke.py +0 -1
  160. package/scripts/bench_models.py +0 -1
  161. package/scripts/brain_quality_eval.py +7 -2
  162. package/scripts/bump_version.py +2 -1
  163. package/scripts/check_current_release_docs.mjs +1 -1
  164. package/scripts/migrate_brain_storage.py +5 -1
  165. package/scripts/profile_kg.py +2 -7
  166. package/scripts/verify_hf_model_registry.py +2 -2
  167. package/src-tauri/Cargo.lock +1 -1
  168. package/src-tauri/Cargo.toml +1 -1
  169. package/src-tauri/tauri.conf.json +1 -1
  170. package/static/app/asset-manifest.json +37 -37
  171. package/static/app/assets/Act-CbdGD-2i.js +1 -0
  172. package/static/app/assets/AdminConsole-LgCkXpnf.js +1 -0
  173. package/static/app/assets/Brain-CoPGJI1L.js +321 -0
  174. package/static/app/assets/BrainHome-gVnaxSwp.js +2 -0
  175. package/static/app/assets/BrainSignals-ChxAYHtj.js +1 -0
  176. package/static/app/assets/Capture-DBtgkHZg.js +1 -0
  177. package/static/app/assets/CommandPalette-Ovtv5I0Y.js +1 -0
  178. package/static/app/assets/Library-CBia2Fvm.js +1 -0
  179. package/static/app/assets/LivingBrain-CNz-6NSd.js +1 -0
  180. package/static/app/assets/{ProductFlow-cB3EFinZ.js → ProductFlow-ePX-Y73J.js} +1 -1
  181. package/static/app/assets/ReviewCard-C4mpvkwH.js +3 -0
  182. package/static/app/assets/System-BvWNK1zc.js +1 -0
  183. package/static/app/assets/activity-CiauPV_3.js +1 -0
  184. package/static/app/assets/{bot-DtKkLfXj.js → bot-Dwct-Q1x.js} +1 -1
  185. package/static/app/assets/brain-BKDW1F0T.js +1 -0
  186. package/static/app/assets/{button-BTxZDMFJ.js → button-CCB9Kuw7.js} +1 -1
  187. package/static/app/assets/{circle-pause--qxF9-EO.js → circle-pause-DNa8WHC5.js} +1 -1
  188. package/static/app/assets/{circle-play-89x3S6jm.js → circle-play-8jmxn5mf.js} +1 -1
  189. package/static/app/assets/{cpu-DTUI63vo.js → cpu-mxvF3V1I.js} +1 -1
  190. package/static/app/assets/{download-EJS5_O0f.js → download--SmCcx0p.js} +1 -1
  191. package/static/app/assets/{folder-open-BL9zYTnc.js → folder-open-D4Wy5roo.js} +1 -1
  192. package/static/app/assets/{hard-drive-BFnwi1Lu.js → hard-drive-BwQcSPlS.js} +1 -1
  193. package/static/app/assets/index-CQWdDU3z.css +2 -0
  194. package/static/app/assets/index-DDV2YZwM.js +10 -0
  195. package/static/app/assets/input-DFhSjmLS.js +1 -0
  196. package/static/app/assets/{network-zKa3jAwl.js → network-Bts7VO94.js} +1 -1
  197. package/static/app/assets/primitives-BuSMEJY8.js +1 -0
  198. package/static/app/assets/search-DZzxhWaQ.js +1 -0
  199. package/static/app/assets/{shield-alert-CpHeCsaF.js → shield-alert-BfTO6X_G.js} +1 -1
  200. package/static/app/assets/textarea-BjctW1oh.js +1 -0
  201. package/static/app/assets/{useFocusTrap-58TNCONM.js → useFocusTrap-CE43-LrA.js} +1 -1
  202. package/static/app/assets/{useQuery-CcVlXlBd.js → useQuery-Bv3ejLL5.js} +1 -1
  203. package/static/app/assets/{users-C0cFMj6o.js → users-CsNqLZAj.js} +1 -1
  204. package/static/app/assets/utils-KFFdVG_t.js +7 -0
  205. package/static/app/assets/workspace-wdCvdyPF.js +1 -0
  206. package/static/app/index.html +4 -4
  207. package/static/sw.js +1 -1
  208. package/static/app/assets/Act-BFbpl2_Y.js +0 -1
  209. package/static/app/assets/AdminConsole-DaZPha9Y.js +0 -1
  210. package/static/app/assets/Brain-DGrV0heZ.js +0 -321
  211. package/static/app/assets/BrainHome-Re6Zxh_W.js +0 -2
  212. package/static/app/assets/BrainSignals-BQ11jDpJ.js +0 -1
  213. package/static/app/assets/Capture-DRaf1zLH.js +0 -1
  214. package/static/app/assets/CommandPalette-DAKPm4uJ.js +0 -1
  215. package/static/app/assets/LanguageSwitcher-BgTwHoWv.js +0 -1
  216. package/static/app/assets/Library-BVtrSt12.js +0 -1
  217. package/static/app/assets/LivingBrain-7glOmK0R.js +0 -1
  218. package/static/app/assets/ReviewCard-CShhHS-w.js +0 -3
  219. package/static/app/assets/System-B4nQDy8a.js +0 -1
  220. package/static/app/assets/brain-wVV63_1z.js +0 -1
  221. package/static/app/assets/index-BFtDYI6l.js +0 -10
  222. package/static/app/assets/index-BY9bpxyx.css +0 -2
  223. package/static/app/assets/input-DEz5UjBz.js +0 -1
  224. package/static/app/assets/primitives-jNrZm2O9.js +0 -1
  225. package/static/app/assets/search-ku6-_6wY.js +0 -1
  226. package/static/app/assets/textarea-DpcQkn75.js +0 -1
  227. package/static/app/assets/utils-DYf7pqrR.js +0 -7
  228. package/static/app/assets/workspace-CjthHsLN.js +0 -1
package/README.md CHANGED
@@ -11,7 +11,7 @@
11
11
  [![CI Status](https://github.com/TaeSooPark-PTS/LatticeAI/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/TaeSooPark-PTS/LatticeAI/actions/workflows/ci.yml)
12
12
  [![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)
13
13
 
14
- ![v9.9.9 Living Brain walkthrough](output/release/v9.9.9/gifs/v9.9.9-living-brain-walkthrough.gif)
14
+ ![v10.2.0 Living Brain walkthrough](output/release/v10.2.0/gifs/v10.2.0-living-brain-walkthrough.gif)
15
15
 
16
16
  Chat, files, folders, notes, and web pages all flow into one durable knowledge
17
17
  graph on your computer. Any model — local MLX or cloud — can speak with that
@@ -24,9 +24,9 @@ memory. Nothing leaves your machine without explicit consent.
24
24
 
25
25
  | | |
26
26
  | --- | --- |
27
- | **Chat with a Brain that remembers** — every conversation grows durable, source-linked memory ![Brain Chat](output/release/v9.9.9/screenshots/04-brain-chat-home.png) | **See how knowledge connects** — a real relationship graph, not a file list ![Memory Graph](output/release/v9.9.9/screenshots/05-memory-graph.png) |
28
- | **Capture anything** — files, whole folders, notes, screenshots, web pages ![Capture](output/release/v9.9.9/screenshots/06-capture.png) | **Automate with review** — agent changes become proposals you approve first ![Review Center](output/release/v9.9.9/screenshots/12-review-center.png) |
29
- | **Pick a model in one click** — recommended local models for your hardware ![Recommended Models](output/release/v9.9.9/screenshots/02-recommended-models.png) | **Stay in control** — audit, roles, retention in a separate admin surface ![Admin Console](output/release/v9.9.9/screenshots/10-admin-console.png) |
27
+ | **Chat with a Brain that remembers** — every conversation grows durable, source-linked memory ![Brain Chat](output/release/v10.2.0/screenshots/04-brain-chat-home.png) | **See how knowledge connects** — a real relationship graph, not a file list ![Memory Graph](output/release/v10.2.0/screenshots/05-memory-graph.png) |
28
+ | **Capture anything** — files, whole folders, notes, screenshots, web pages ![Capture](output/release/v10.2.0/screenshots/06-capture.png) | **Automate with review** — agent changes become proposals you approve first ![Review Center](output/release/v10.2.0/screenshots/12-review-center.png) |
29
+ | **Pick a model in one click** — recommended local models for your hardware ![Recommended Models](output/release/v10.2.0/screenshots/02-recommended-models.png) | **Stay in control** — audit, roles, retention in a separate admin surface ![Admin Console](output/release/v10.2.0/screenshots/10-admin-console.png) |
30
30
 
31
31
  ## Why Lattice AI
32
32
 
@@ -57,48 +57,53 @@ First-run flow — wake the Brain, pick the owner, load a recommended model:
57
57
 
58
58
  | | | |
59
59
  | --- | --- | --- |
60
- | ![Login](output/release/v9.9.9/screenshots/01-login.png) | ![Model install](output/release/v9.9.9/screenshots/03-install-load-progress.png) | ![Model library](output/release/v9.9.9/screenshots/07-model-library.png) |
60
+ | ![Login](output/release/v10.2.0/screenshots/01-login.png) | ![Model install](output/release/v10.2.0/screenshots/03-install-load-progress.png) | ![Model library](output/release/v10.2.0/screenshots/07-model-library.png) |
61
61
 
62
62
  Screenshot index and capture notes:
63
- [output/release/v9.9.9/SCREENSHOT_INDEX.md](output/release/v9.9.9/SCREENSHOT_INDEX.md)
63
+ [output/release/v10.2.0/SCREENSHOT_INDEX.md](output/release/v10.2.0/SCREENSHOT_INDEX.md)
64
64
 
65
65
  ## Current Release
66
66
 
67
- The current release is **9.9.9Lean Shell**:
68
-
69
- - **No `✖` left in the parity matrix.** Every gap 9.9.6 wrote down is closed,
70
- and every remaining `—` states *why* it is a boundary
71
- ([docs/SURFACE_PARITY.md](docs/SURFACE_PARITY.md)).
72
- - **VS Code watches the loop live.** `POST /agent` streams the same
73
- `agent_step` frames the web app gets, and cited evidence turns into one-click
74
- follow-ups in the editor.
75
- - **Telegram badges its recall and reviews changes.** The server's grounding
76
- verdict is shown as-is, and `/review` approves or rejects staged proposals
77
- inline.
78
- - **The browser extension recalls, not just captures.** Ask your Brain from the
79
- popup with the same badge, and see how many runs await approval — still
80
- posting only to `127.0.0.1`.
81
- - **A knowledge garden in four beds** what arrived, what disagrees, what went
82
- stale, what the rest leans on.
83
- - **A profile for small local models.** Under 4B, the loop shortens its window,
84
- escalates sooner, and when JSON tool calls keep failing — writes the
85
- planned files directly instead of ending empty-handed.
86
- - **Folder memory state.** How much of each connected folder is really in your
87
- Brain, what failed, and the reason it failed.
88
- - **Two skills that pay off on install** meeting notes and a weekly review
89
- written only from what the Brain actually recorded.
90
- - **Voice memos** go straight into the Brain; when no local transcriber exists
91
- the memo is still stored and honestly marked not-searchable.
67
+ The current release is **10.2.0Load-Bearing Fixes**:
68
+
69
+ A full code review of 10.1.1 scored the codebase 71/100 and found two things
70
+ that were true but invisible. This release is that review's response.
71
+
72
+ - **A database connection leak that hid behind garbage collection.**
73
+ `with sqlite3.connect(...)` commits but never closes, and 70+ call sites
74
+ relied on it. Nothing showed until something held a stack frame alive — a
75
+ profiler, a logged traceback, a coverage tracer and then descriptors ran
76
+ out. Every one of those sites now closes.
77
+ - **Coverage is measurable for the first time: 71%.** It could not be run
78
+ before, because running it *was* the thing that exhausted descriptors. There
79
+ is now a floor in CI so it cannot silently fall.
80
+ - **"Sensitive memories are never sent" is finally true.** The filter was real,
81
+ correct, and wired but nothing in the product could mark a memory, and the
82
+ blocked-type list was empty, so it could never fire. You can now mark any
83
+ memory from the boundary panel, files under `.ssh`/`.aws`/`.env` and friends
84
+ are flagged automatically at ingestion, and credential-shaped node types are
85
+ blocked outright.
86
+ - **Knowledge that leaves is now redacted and recorded.** Secret-shaped text is
87
+ stripped from the outbound payload, and every send and every refusal —
88
+ writes an audit entry naming what went, how much, and where to.
89
+ - **112 silent `except: pass` handlers now say something.** Behaviour is
90
+ unchanged; each records the failure at debug level, so a genuine bug in an
91
+ optional path is no longer indistinguishable from the optional thing being
92
+ absent.
93
+ - **The lint net was widened to the bug classes that actually bit** — loop
94
+ variable capture, truncating `zip`, silent handlers — and mypy now runs on
95
+ the trust-critical modules. CI gained macOS and Python 3.14, so the machine
96
+ this is developed on and the platform it ships a `.dmg` for are both tested.
92
97
 
93
98
  Release notes: [RELEASE.md](RELEASE.md) · Full history: [docs/CHANGELOG.md](docs/CHANGELOG.md)
94
99
 
95
- Expected artifacts for 9.9.9 release must use exact filenames:
100
+ Expected artifacts for 10.2.0 release must use exact filenames:
96
101
 
97
- - `dist/ltcai-9.9.9-py3-none-any.whl`
98
- - `dist/ltcai-9.9.9.tar.gz`
99
- - `ltcai-9.9.9.tgz`
100
- - `dist/ltcai-9.9.9.vsix`
101
- - `src-tauri/target/release/bundle/dmg/Lattice AI_9.9.9_aarch64.dmg`
102
+ - `dist/ltcai-10.2.0-py3-none-any.whl`
103
+ - `dist/ltcai-10.2.0.tar.gz`
104
+ - `ltcai-10.2.0.tgz`
105
+ - `dist/ltcai-10.2.0.vsix`
106
+ - `src-tauri/target/release/bundle/dmg/Lattice AI_10.2.0_aarch64.dmg`
102
107
 
103
108
  Do not use wildcard artifact uploads. Package registry publishing remains owner-run.
104
109
 
@@ -118,18 +123,26 @@ See [ARCHITECTURE.md](ARCHITECTURE.md) for details and
118
123
 
119
124
  - External package registries are owner-published and can lag behind GitHub.
120
125
  - PostgreSQL/pgvector is optional scale/migration tooling. SQLite remains the
121
- live local Brain store in 9.9.5.
126
+ live local Brain store in 10.2.0.
122
127
  - Docker, model downloads, cloud model calls, Telegram, Brain Network, and
123
128
  update checks require explicit user action.
124
129
  - Conversation does not fabricate answers when no model is loaded. Agent and
125
130
  workflow simulation without a loaded LLM is deterministic and LLM-free (it
126
131
  does not call a model) — labeled as such, never presented as autonomous
127
132
  model success.
133
+ - Some backend-generated messages (for example the Postgres DSN notice) are
134
+ produced server-side in English and are shown as-is; server-side i18n is not
135
+ part of 10.2.0.
128
136
 
129
137
  ## Release History
130
138
 
131
139
  | Version | Theme |
132
140
  | --- | --- |
141
+ | 10.2.0 | Load-Bearing Fixes |
142
+ | 10.1.1 | Reachable Boundary |
143
+ | 10.1.0 | Hybrid Brain |
144
+ | 10.0.1 | One Source of Truth |
145
+ | 10.0.0 | Plain Language |
133
146
  | 9.9.9 | Lean Shell |
134
147
  | 9.9.8 | Autonomy Dial |
135
148
  | 9.9.7 | No Gaps Left |
package/docs/CHANGELOG.md CHANGED
@@ -4,6 +4,203 @@ The top entry is either the current unreleased main-branch work or the current
4
4
  release line. Older entries are historical and may describe behavior as it
5
5
  existed at that release.
6
6
 
7
+ ## [10.2.0] - 2026-07-29
8
+
9
+ Response to a full code review of 10.1.1 (71/100). All twelve findings.
10
+
11
+ ### Fixed
12
+ - **SQLite connections were committed but never closed** at 70+ sites.
13
+ `sqlite3.Connection.__exit__` commits or rolls back and leaves the connection
14
+ open; `KnowledgeGraphStore._connect`, `ConversationStore._connect`,
15
+ `WorkspaceOSStore._connect_state_db`, both storage engines, `portability`,
16
+ `archive`, `migration` and `users` all relied on it. Refcounting hid the leak
17
+ until something held a frame alive (profiler, logged traceback, coverage
18
+ tracer), at which point the process hit `EMFILE`. Added
19
+ `StorageEngine.session()` and made the store helpers closing context
20
+ managers; `tests/unit/test_connection_lifecycle.py` asserts it.
21
+ - **The cloud sensitivity guard could not fire.** `HARD_BLOCK_NODE_TYPES` was
22
+ an empty set and nothing in the product could set the metadata flags
23
+ `is_node_blocked_for_cloud` matches on, so "sensitive memories are never
24
+ sent" was unfalsifiable. Populated the type list, added
25
+ `lattice_brain/sensitivity.py` (path rules, stamped at ingestion), added
26
+ `KnowledgeGraphStore.set_node_sensitivity` and
27
+ `POST /api/network-boundary/node-sensitivity`, and a per-memory hold-back
28
+ control in the boundary panel.
29
+ - **Outbound cloud text was not redacted.** `redact_secret_text` ran on logs,
30
+ audit records and API previews but not on the payload that actually leaves.
31
+ - **Vector similarity truncated silently on a dimension mismatch**, producing a
32
+ plausible but meaningless score. Now raises with both dimensions named.
33
+ - **`tempfile.mktemp()` (TOCTOU race) in the Telegram bridge** replaced with
34
+ `mkstemp` at two sites.
35
+ - **Three closures captured loop variables by reference** (`chat_agent_http`,
36
+ `computer_use`, `model_runtime`). Safe today because each is invoked within
37
+ its iteration; bound explicitly so deferring the call cannot break them.
38
+ - **The cloud turn ran retrieval twice**, discarding the first result except
39
+ for its quality dict.
40
+
41
+ ### Added
42
+ - `latticeai/services/cloud_egress_audit.py` — every cloud send and every
43
+ refusal writes an audit entry *before* the provider call, naming node ids,
44
+ count, token estimate, provider and mode. Shape, never content.
45
+ - `latticeai/core/quiet.py` + `lattice_brain/quiet.py` — `quiet()` keeps the
46
+ behaviour of `except: pass` and records the exception with its file,
47
+ function and line at DEBUG. 112 handlers converted; `except: pass` is now a
48
+ lint error (S110/S112).
49
+ - Coverage measurement: `pytest-cov` in the `dev` extra, `[tool.coverage]`
50
+ config, a **70% floor** in CI. Measured **71%** — the first known figure.
51
+ - `tests/unit/test_cloud_egress_guard.py` — end-to-end proof that a flagged
52
+ memory never reaches the payload, that retrieval still carries `metadata`
53
+ (whose loss would silently disable the filter), and that egress is audited.
54
+ - mypy over 13 trust-critical modules (`npm run typecheck:python`), wired into
55
+ `npm run lint:python`.
56
+ - CI matrix: macOS runner (the platform the `.dmg` ships for, previously
57
+ untested) and Python 3.14 (the version this is developed on).
58
+
59
+ ### Changed
60
+ - ruff widened from pyflakes-only to `E4,E7,E9,F,B,S,I,SIM,RET,C901`. Every
61
+ remaining finding is either fixed or carries a written reason; complexity
62
+ ceiling set to 30 with named exemptions.
63
+ - `latticeai/core/workspace_os.py` 1354 → 1130 lines; vocabulary moved to
64
+ `workspace_os_constants.py` and state/migration to `workspace_os_state.py`,
65
+ with delegating methods so the public surface is unchanged.
66
+ - Package classifiers now advertise Python 3.13 and 3.14, matching what CI tests.
67
+
68
+ ### Known
69
+ - `app_factory._build` remains one 1,343-line function. Its sections share ~34
70
+ local bindings, so decomposition requires a runtime-context object rather
71
+ than a mechanical extraction; deferred rather than rushed, and named in the
72
+ C901 exemptions so the choice is visible.
73
+
74
+ ## [10.1.1] - 2026-07-28
75
+
76
+ ### Added
77
+ - `NetworkBoundaryPanel` (`frontend/src/components/NetworkBoundaryPanel.tsx`),
78
+ mounted in the System settings tab beside `PermissionModePanel`. Renders the
79
+ server's mode catalog, gates `cloud_allowed` behind the acknowledgement the
80
+ server requires, and exposes the hybrid write-back policy switches only while
81
+ the boundary permits cloud.
82
+ - Context preview in that panel: a question is sent to
83
+ `POST /api/network-boundary/preview` and the panel lists the actual node
84
+ titles that would leave, the token estimate, and the token guard's verdict.
85
+ Available in `local_only` too, labelled as hypothetical.
86
+ - API client bindings and types: `networkBoundary`, `setNetworkBoundary`,
87
+ `setHybridPolicy`, `previewCloudContext`; `NetworkBoundaryOption`,
88
+ `NetworkBoundaryState`, `HybridPolicy`, `CloudContextPreview`,
89
+ `CloudTokenBudget`. The `networkBoundary` fallback is `local_only`, so a
90
+ failed read never renders as if cloud were already permitted.
91
+ - 22 `system.network.*` copy keys in the `workspace` i18n namespace (ko + en).
92
+ - `/api/network-boundary*` routes in `tests/visual/mock_server.cjs` so release
93
+ evidence shows the panel working rather than an unavailable state.
94
+ - `NetworkBoundaryPanel.test.tsx` (12 tests) and two Playwright specs covering
95
+ the settings-tab route, the acknowledgement gate, and the preview.
96
+
97
+ ### Removed
98
+ - `static/app/network-boundary-panel.js` — the unmounted standalone module
99
+ 10.1.0 shipped as this feature's UI. Nothing referenced it, it was absent
100
+ from the asset manifest, and it lived in Vite's output directory, which
101
+ `npm run build:assets` wipes; it had in fact already been deleted by the
102
+ previous release's asset rebuild. The React panel replaces it.
103
+
104
+ ### Changed
105
+ - ARCHITECTURE, FEATURE_STATUS, README, and the hybrid design doc no longer
106
+ carry the "API-only surface" caveat, and describe the in-app control instead.
107
+
108
+ ## [10.1.0] - 2026-07-28
109
+
110
+ ### Added
111
+ - Local-first hybrid path: the Knowledge Graph stays on-device and cloud LLMs
112
+ are an opt-in worker. `NetworkBoundaryMode` and `MinimalContext` contracts
113
+ (`latticeai/core/network_boundary.py`, `latticeai/api/network_boundary.py`)
114
+ with a persisted dial wired via `latticeai/runtime/network_boundary_wiring.py`.
115
+ - Cloud streaming worker: `latticeai/services/cloud_streaming.py`,
116
+ `cloud_extraction.py`, `cloud_token_guard.py`, and an OpenAI-compatible stream
117
+ adapter (`openai_compatible_adapter.py`) with token budgets and guardrails.
118
+ - Hybrid chat pipeline: `latticeai/api/chat_hybrid.py` plus
119
+ `services/hybrid_chat.py`, `hybrid_context.py`, and `hybrid_policy.py`; the
120
+ `/chat` route branches through it and writes back through the Review Queue.
121
+ - Multimodal streaming contracts (`services/multimodal_streaming.py`) and a
122
+ network-boundary control panel (`static/app/network-boundary-panel.js`).
123
+ - Tests: `test_network_boundary.py`, `test_hybrid_phase2.py`,
124
+ `test_hybrid_phase3.py`.
125
+
126
+ ### Notes
127
+ - Default network boundary is `local_only`; cloud requires explicit
128
+ acknowledgement. Minimal related nodes only leave the machine; streamed
129
+ answers expand the local Brain with provenance.
130
+
131
+ ## [10.0.1] - 2026-07-28
132
+
133
+ ### Changed
134
+ - `latticeai/core/agent.py` holds only the state machine (1769 → 1326 lines).
135
+ Its pure functions move to `latticeai/core/agent_helpers.py`: action parsing
136
+ (`extract_action`, `extract_action_details`), plan normalization
137
+ (`normalize_plan`), learning filters, transcript compaction, artifact and
138
+ requirement reporting, and the `TranscriptBudget` / `PhaseBudgets` dataclasses.
139
+ - `AgentState` and `AGENT_TERMINAL_STATES` move to a new
140
+ `latticeai/core/agent_state.py`. Both `agent.py` and `agent_helpers.py` import
141
+ from it, which is what lets the helpers reference the enum instead of a
142
+ literal state string.
143
+ - `latticeai.core.agent` re-exports every moved name as the same object and
144
+ declares the set in `__all__`. No caller changed: the HTTP layer, `run_store`,
145
+ `computer_use`, `bench_agent_smoke`, `bench_models`, and eight test modules
146
+ import exactly as before.
147
+ - The home's secondary control row is visually demoted (opacity, tighter gaps,
148
+ softer divider) and gets larger touch spacing under 640px. The sticky top bar
149
+ border is softened. CSS only.
150
+
151
+ ### Fixed
152
+ - `files_written` and `artifact_checklist` matched transcript steps against the
153
+ literal `"EXECUTING"` instead of `AgentState.EXECUTING.value`. A rename of the
154
+ enum value would have made both silently return empty results with no failing
155
+ test. Both now reference the enum.
156
+
157
+ ### Verification
158
+ - pytest 1747 passed / 11 skipped; `scripts/agent_eval.py` 23/23 (100%); ruff
159
+ clean across `latticeai/`, `lattice_brain/`, `scripts/`, `tests/`.
160
+ - All 18 extracted symbols were AST-compared against their originals before the
161
+ originals were removed.
162
+
163
+ ## [10.0.0] - 2026-07-28
164
+
165
+ ### Changed
166
+ - The Brain home is four zones: the living Brain, one composer, the autonomy
167
+ dial, and capture. `BrainHomeHero` replaces the knowledge-flow canvas on the
168
+ home; the graph is reached by clicking the Brain.
169
+ - File / folder / note / web capture render inside the composer toolbar
170
+ (`BrainIngestionDock variant="inline"`), on both the home and the active
171
+ conversation. The standalone "Brain에게 가르치기" panel is removed.
172
+ - The top bar carries the language switch and the appearance toggle, so both
173
+ are reachable from every screen.
174
+ - Provenance coverage is reported as counts with an explanation
175
+ (`CoverageMeter`) instead of a bare percentage.
176
+ - Model identifiers are humanized for display (`humanizeModelId`); the catalog
177
+ display name wins when the id is known.
178
+ - Settings splits appearance (light/dark) from detail level (basic/advanced/
179
+ admin); they were one card labeled "화면 모양".
180
+ - Model list is ordered by what can be used now: loaded, ready, downloaded,
181
+ then the rest.
182
+
183
+ ### Fixed
184
+ - The folder button did nothing in a browser: `pickFolder` called the
185
+ desktop-only `selectFolder`. `frontend/src/lib/folderPicker.ts` now owns the
186
+ desktop / File System Access / file-input decision for every surface.
187
+ - The conversation header Brain rendered at 311px — `size="trace"` had no size
188
+ rule and inherited the base `clamp(220px, 28vw, 320px)`.
189
+ - The sticky composer covered the end of the newest answer; the stream now
190
+ reserves room, and nothing renders after the composer.
191
+ - `ValuePreview` printed a nested object's field names as its value
192
+ ("Runtime → Ready, Version, Execution Mode, Mode +4").
193
+ - `white-space: nowrap` on controls was inherited by descriptive text inside
194
+ them, clipping a 347px sentence to 288px in the autonomy panel.
195
+ - Markdown written by a model leaked into search-result titles and graph node
196
+ labels; `plainText` strips it.
197
+ - Duplicate `ui.field.health` keys in the shell namespace.
198
+
199
+ ### Added
200
+ - ko/en parity across memory tiers, agent roles, automation recipes and their
201
+ outputs, entity types, status badges, and backend payload field labels.
202
+ - `plainText` and `humanizeModelId` in `frontend/src/lib/utils.ts`, with tests.
203
+
7
204
  ## [9.9.9] - 2026-07-27
8
205
 
9
206
  ### Changed
@@ -1,6 +1,6 @@
1
1
  # Community And Plugins
2
2
 
3
- Current release: **9.9.9Lean Shell**.
3
+ Current release: **10.2.0Load-Bearing Fixes**.
4
4
 
5
5
  LatticeAI defines the path from a strong local-first framework (8.4.0
6
6
  action-aware baseline, 8.5.0 registry+DI hardening, 8.6.0 capture/navigation
@@ -3,7 +3,7 @@
3
3
  > **Status: canonical** — current contributor guidance, kept in sync with the
4
4
  > current release.
5
5
 
6
- Current release: **9.9.9Lean Shell**.
6
+ Current release: **10.2.0Load-Bearing Fixes**.
7
7
 
8
8
  This document is for contributors working on the local-first Digital Brain
9
9
  codebase. Product positioning and quick start stay in `README.md`; release
@@ -0,0 +1,101 @@
1
+ # Hybrid Cloud + Local Knowledge Graph Streaming
2
+
3
+ > Status: **shipped in 10.1.0**; in-app control added in **10.1.1**
4
+ > Principle: **Local Brain is the asset. Cloud is an opt-in worker.**
5
+ > Surface: `환경설정 → 내 지식이 나가는 범위`
6
+ > (`frontend/src/components/NetworkBoundaryPanel.tsx`), plus
7
+ > `/api/network-boundary` and `LATTICEAI_NETWORK_MODE`.
8
+
9
+ ## Goal
10
+
11
+ Local Knowledge Graph stays on-device. Cloud LLMs (and optional multimodal) are opt-in workers. Minimal related nodes leave the machine; streamed answers expand the local Brain with provenance and review gates.
12
+
13
+ ## Phase summary
14
+
15
+ | Phase | Branch | Delivered |
16
+ |-------|--------|-----------|
17
+ | 0 | `feature/hybrid-cloud-kg-streaming` | Architecture + core contracts |
18
+ | 1 | `…-phase1` | Dial, OpenAI adapter, hybrid helpers, basic expansion |
19
+ | 2 | `…-phase2` | `/chat` branch, token guardrails, extraction, preview API |
20
+ | **3** | `…-phase3` | **Policy, Review Queue write path, multimodal contracts, UI panel module** |
21
+
22
+ ## Phase 3 details
23
+
24
+ ### Hybrid policy (`HybridPolicyService`)
25
+ Persisted dial for:
26
+ - `blocked_node_types` / `blocked_metadata_flags` (unioned with hard circuit breakers)
27
+ - `auto_commit` (default **false**)
28
+ - `allow_multimodal` (default **false**)
29
+ - `min_extraction_confidence`
30
+
31
+ API:
32
+ ```
33
+ GET /api/network-boundary/policy
34
+ POST /api/network-boundary/policy
35
+ { "auto_commit": false, "allow_multimodal": false, "blocked_node_types": ["Secret"] }
36
+ ```
37
+
38
+ ### Review Queue write path
39
+ `CloudResponseIngestor` now:
40
+ 1. Always enqueues a `change_proposal` review item when a ReviewQueueService is bound
41
+ 2. Only auto-writes to the graph when `plan.auto_commit` is true **and** a store write API exists
42
+
43
+ Users approve cloud-derived memory growth in the existing Review Center.
44
+
45
+ ### Multimodal / video
46
+ `latticeai/services/multimodal_streaming.py`:
47
+ - Same `NetworkBoundaryMode.CLOUD_ALLOWED` gate
48
+ - Additional `allow_multimodal` policy flag
49
+ - Adapter protocol for future Runway/Luma/Veo-compatible providers
50
+
51
+ ### UI
52
+ `NetworkBoundaryPanel` (React) + `GET /api/network-boundary/ui-state`:
53
+ - Toggle local ↔ cloud (with ack)
54
+ - Transparency preview of nodes about to be sent
55
+ - Shows current policy flags
56
+
57
+ Mounted in `frontend/src/pages/System.tsx` beside `PermissionModePanel`, so it
58
+ appears on the settings tab of every install. The standalone
59
+ `static/app/network-boundary-panel.js` module 10.1.0 shipped was removed in
60
+ 10.1.1: nothing mounted it, and it lived in Vite's build output directory,
61
+ which `npm run build:assets` wipes.
62
+
63
+ ## End-to-end flow (Phase 3)
64
+
65
+ ```
66
+ User toggles cloud_allowed (UI panel or API)
67
+ → optional policy: auto_commit / multimodal / sensitivity
68
+ User sends chat message
69
+ → /chat resolves mode
70
+ → if cloud_allowed: minimal KG context + token budget check
71
+ → cloud stream (SSE)
72
+ → rich extraction (Decision/Task/Concept)
73
+ → Review Queue item (change_proposal)
74
+ → optional auto-commit when policy allows
75
+ ```
76
+
77
+ ## Configuration
78
+
79
+ ```bash
80
+ export LATTICEAI_CLOUD_API_KEY=sk-...
81
+ export LATTICEAI_CLOUD_MODEL=gpt-4o-mini
82
+ export LATTICEAI_NETWORK_MODE=local_only
83
+ export LATTICEAI_CLOUD_MAX_TOKENS_PER_TURN=2500
84
+ export LATTICEAI_CLOUD_MAX_TOKENS_PER_SESSION=50000
85
+ ```
86
+
87
+ ## Privacy (unchanged core rules)
88
+
89
+ 1. Default `local_only`
90
+ 2. Cloud requires explicit ack
91
+ 3. Only minimal compact context leaves the host
92
+ 4. Hard + user sensitivity filters
93
+ 5. Cloud-derived nodes go through Review Queue unless user opts into auto_commit
94
+ 6. Multimodal is off unless policy explicitly enables it
95
+
96
+ ## Success criteria (Phase 3)
97
+
98
+ - User can configure sensitivity and auto-commit without code changes
99
+ - Cloud KG growth appears in Review Center as actionable proposals
100
+ - Multimodal path cannot fire without both cloud mode and policy flag
101
+ - UI panel works without rebuilding the main React bundle
@@ -1,6 +1,6 @@
1
1
  # Lattice AI Onboarding
2
2
 
3
- Current release: **9.9.9Lean Shell**.
3
+ Current release: **10.2.0Load-Bearing Fixes**.
4
4
 
5
5
  The first-run goal is a five-minute path from "I opened the app" to "my Brain
6
6
  has a source, a question, and proof." This page is the product contract behind
@@ -1,4 +1,4 @@
1
- # Lattice AI — Operations Guide (v9.9.9)
1
+ # Lattice AI — Operations Guide (v10.2.0)
2
2
 
3
3
  > **Status: canonical** — kept in sync with the current release. Storage layout
4
4
  > below reflects the SQLite live Brain store and workspace scoping, not the
@@ -1,6 +1,6 @@
1
1
  # Lattice AI Trust Model
2
2
 
3
- Current release: **9.9.9Lean Shell**.
3
+ Current release: **10.2.0Load-Bearing Fixes**.
4
4
 
5
5
  Lattice AI is local-first, explicit about external communication, and honest
6
6
  when a capability is unavailable.
@@ -1,6 +1,6 @@
1
1
  # Why Lattice AI Exists
2
2
 
3
- Current release: **9.9.9Lean Shell**.
3
+ Current release: **10.2.0Load-Bearing Fixes**.
4
4
 
5
5
  **Lattice AI is a local-first Digital Brain that keeps your knowledge durable
6
6
  across any AI model.**
package/docs/kg-schema.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Knowledge Graph Schema
2
2
 
3
- Current release: **9.9.9Lean Shell**.
3
+ Current release: **10.2.0Load-Bearing Fixes**.
4
4
 
5
5
  명세 출처: `lattice_ai_full_spec.pptx` 슬라이드 20·21·22
6
6
  구현: `lattice_brain/graph/schema.py`
@@ -26,7 +26,7 @@ from .storage import (
26
26
  storage_from_env,
27
27
  )
28
28
 
29
- __version__ = "9.9.9"
29
+ __version__ = "10.2.0"
30
30
 
31
31
  __all__ = [
32
32
  "AgentRuntime",
@@ -16,17 +16,18 @@ import shutil
16
16
  import sqlite3
17
17
  import tempfile
18
18
  import zipfile
19
+ from contextlib import closing
19
20
  from dataclasses import dataclass
20
21
  from pathlib import Path, PurePosixPath
21
22
  from typing import Any, Dict, Iterable, List, Optional
22
23
 
24
+ from cryptography.exceptions import InvalidTag
23
25
  from cryptography.hazmat.primitives import hashes
24
26
  from cryptography.hazmat.primitives.ciphers.aead import AESGCM
25
- from cryptography.exceptions import InvalidTag
26
27
  from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
27
28
 
28
- from .utils import sha256_file as _sha256_file, utc_now_iso as _now
29
-
29
+ from .utils import sha256_file as _sha256_file
30
+ from .utils import utc_now_iso as _now
30
31
 
31
32
  ARCHIVE_FORMAT = "latticebrain.encrypted"
32
33
  ARCHIVE_VERSION = 2
@@ -106,7 +107,7 @@ def _checkpoint_sqlite(db_path: Path) -> None:
106
107
  if not db_path.exists():
107
108
  return
108
109
  try:
109
- with sqlite3.connect(str(db_path)) as conn:
110
+ with closing(sqlite3.connect(str(db_path))) as conn, conn:
110
111
  conn.execute("PRAGMA wal_checkpoint(FULL)")
111
112
  except sqlite3.Error:
112
113
  return
@@ -19,8 +19,9 @@ import json
19
19
  import logging
20
20
  import sqlite3
21
21
  import threading
22
+ from contextlib import contextmanager
22
23
  from pathlib import Path
23
- from typing import Any, Dict, List, Optional
24
+ from typing import Any, Dict, Iterator, List, Optional
24
25
 
25
26
 
26
27
  def _message_hash(item: Dict[str, Any]) -> str:
@@ -40,11 +41,21 @@ class ConversationStore:
40
41
  self._lock = threading.RLock()
41
42
  self._init_db()
42
43
 
43
- def _connect(self) -> sqlite3.Connection:
44
+ @contextmanager
45
+ def _connect(self) -> Iterator[sqlite3.Connection]:
46
+ """Transactional connection that is closed when the block exits.
47
+
48
+ ``with sqlite3.connect(...)`` commits but never closes; see
49
+ :meth:`lattice_brain.storage.base.StorageEngine.session`.
50
+ """
44
51
  conn = sqlite3.connect(str(self.db_path))
45
52
  conn.row_factory = sqlite3.Row
46
53
  conn.execute("PRAGMA journal_mode=WAL")
47
- return conn
54
+ try:
55
+ with conn:
56
+ yield conn
57
+ finally:
58
+ conn.close()
48
59
 
49
60
  def _init_db(self) -> None:
50
61
  with self._lock, self._connect() as conn:
@@ -10,7 +10,6 @@ import struct
10
10
  from dataclasses import dataclass
11
11
  from typing import Any, Dict, Iterable, List, Optional
12
12
 
13
-
14
13
  DEFAULT_EMBEDDING_DIM = int(os.getenv("LATTICEAI_VECTOR_DIM", "384"))
15
14
  EMBEDDING_MODEL_ID = f"lattice-local-hash-v1:{DEFAULT_EMBEDDING_DIM}"
16
15
 
@@ -71,7 +70,18 @@ class LocalEmbeddingModel:
71
70
  return [value / norm for value in vector]
72
71
 
73
72
  def similarity(self, left: Iterable[float], right: Iterable[float]) -> float:
74
- return float(sum(a * b for a, b in zip(left, right)))
73
+ # strict=True: a dimension mismatch means the two vectors came from
74
+ # different embedding models. Truncating to the shorter one produces a
75
+ # plausible-looking similarity that is meaningless — exactly the silent
76
+ # wrongness this codebase keeps finding. Callers that can hit a model
77
+ # swap already handle failure and fall back to lexical search.
78
+ left_v, right_v = list(left), list(right)
79
+ if len(left_v) != len(right_v):
80
+ raise ValueError(
81
+ f"embedding dimension mismatch: {len(left_v)} vs {len(right_v)}; "
82
+ "the vector index was built with a different model"
83
+ )
84
+ return float(sum(a * b for a, b in zip(left_v, right_v, strict=True)))
75
85
 
76
86
  def encode(self, vector: Iterable[float]) -> bytes:
77
87
  values = list(vector)