ltcai 10.0.0 → 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 (211) hide show
  1. package/README.md +48 -32
  2. package/docs/CHANGELOG.md +156 -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 +5 -3
  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-BtCREeN1.js → Act-CbdGD-2i.js} +1 -1
  172. package/static/app/assets/{AdminConsole-TPeeN18T.js → AdminConsole-LgCkXpnf.js} +1 -1
  173. package/static/app/assets/{Brain-BKs6JAp0.js → Brain-CoPGJI1L.js} +1 -1
  174. package/static/app/assets/{BrainHome-BPGOvSd6.js → BrainHome-gVnaxSwp.js} +1 -1
  175. package/static/app/assets/{BrainSignals-CtzQZ15J.js → BrainSignals-ChxAYHtj.js} +1 -1
  176. package/static/app/assets/{Capture-1_NaHWqB.js → Capture-DBtgkHZg.js} +1 -1
  177. package/static/app/assets/{CommandPalette-pqvQOXe4.js → CommandPalette-Ovtv5I0Y.js} +1 -1
  178. package/static/app/assets/{Library-DhvoPvC7.js → Library-CBia2Fvm.js} +1 -1
  179. package/static/app/assets/{LivingBrain-DlQ20Q75.js → LivingBrain-CNz-6NSd.js} +1 -1
  180. package/static/app/assets/{ProductFlow-BZvGDRi_.js → ProductFlow-ePX-Y73J.js} +1 -1
  181. package/static/app/assets/{ReviewCard-BWgI0D2s.js → ReviewCard-C4mpvkwH.js} +1 -1
  182. package/static/app/assets/System-BvWNK1zc.js +1 -0
  183. package/static/app/assets/{activity-Dlfk8YC7.js → activity-CiauPV_3.js} +1 -1
  184. package/static/app/assets/{bot-CDvUB76P.js → bot-Dwct-Q1x.js} +1 -1
  185. package/static/app/assets/{brain-xczrohrt.js → brain-BKDW1F0T.js} +1 -1
  186. package/static/app/assets/{button-SOdH3Oyf.js → button-CCB9Kuw7.js} +1 -1
  187. package/static/app/assets/{circle-pause-CG1ythH4.js → circle-pause-DNa8WHC5.js} +1 -1
  188. package/static/app/assets/{circle-play-HXwvjS6W.js → circle-play-8jmxn5mf.js} +1 -1
  189. package/static/app/assets/{cpu-B0d-rGyk.js → cpu-mxvF3V1I.js} +1 -1
  190. package/static/app/assets/{download-BGIkTQL6.js → download--SmCcx0p.js} +1 -1
  191. package/static/app/assets/{folder-open-Dst_Z0_K.js → folder-open-D4Wy5roo.js} +1 -1
  192. package/static/app/assets/{hard-drive-D53MsWkV.js → hard-drive-BwQcSPlS.js} +1 -1
  193. package/static/app/assets/index-CQWdDU3z.css +2 -0
  194. package/static/app/assets/{index-C_IrlQMV.js → index-DDV2YZwM.js} +3 -3
  195. package/static/app/assets/{input-C5m0riF6.js → input-DFhSjmLS.js} +1 -1
  196. package/static/app/assets/{network-C5a-E5iS.js → network-Bts7VO94.js} +1 -1
  197. package/static/app/assets/{primitives-vNXYf58F.js → primitives-BuSMEJY8.js} +1 -1
  198. package/static/app/assets/search-DZzxhWaQ.js +1 -0
  199. package/static/app/assets/{shield-alert-Cc-WVXqN.js → shield-alert-BfTO6X_G.js} +1 -1
  200. package/static/app/assets/{textarea-BkZ0EqVO.js → textarea-BjctW1oh.js} +1 -1
  201. package/static/app/assets/{useFocusTrap-CTtKbAOU.js → useFocusTrap-CE43-LrA.js} +1 -1
  202. package/static/app/assets/{useQuery-Dx1fi4Wu.js → useQuery-Bv3ejLL5.js} +1 -1
  203. package/static/app/assets/{users-BFpQXtEF.js → users-CsNqLZAj.js} +1 -1
  204. package/static/app/assets/{utils-BA_lmW3J.js → utils-KFFdVG_t.js} +2 -2
  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/System-CSMdYLMy.js +0 -1
  209. package/static/app/assets/index-FxDusbr0.css +0 -2
  210. package/static/app/assets/search-DhbSgW6m.js +0 -1
  211. package/static/app/assets/workspace-DBPB0jkX.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
- ![v10.0.0 Living Brain walkthrough](output/release/v10.0.0/gifs/v10.0.0-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/v10.0.0/screenshots/04-brain-chat-home.png) | **See how knowledge connects** — a real relationship graph, not a file list ![Memory Graph](output/release/v10.0.0/screenshots/05-memory-graph.png) |
28
- | **Capture anything** — files, whole folders, notes, screenshots, web pages ![Capture](output/release/v10.0.0/screenshots/06-capture.png) | **Automate with review** — agent changes become proposals you approve first ![Review Center](output/release/v10.0.0/screenshots/12-review-center.png) |
29
- | **Pick a model in one click** — recommended local models for your hardware ![Recommended Models](output/release/v10.0.0/screenshots/02-recommended-models.png) | **Stay in control** — audit, roles, retention in a separate admin surface ![Admin Console](output/release/v10.0.0/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,41 +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/v10.0.0/screenshots/01-login.png) | ![Model install](output/release/v10.0.0/screenshots/03-install-load-progress.png) | ![Model library](output/release/v10.0.0/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/v10.0.0/SCREENSHOT_INDEX.md](output/release/v10.0.0/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 **10.0.0 — Plain Language**:
68
-
69
- - **The first screen is four things.** Your Brain, one input box, the autonomy
70
- dial, and the capture controls file, folder, note, web — now sit inside the
71
- composer instead of a separate panel. The knowledge graph opens by clicking
72
- the Brain, so the home shows no diagram at all.
73
- - **Korean and English, everywhere.** A language switch sits in the top bar on
74
- every screen, and the interface is fully translated in both directions:
75
- memory tiers, agent roles, automation recipes, entity types, and every
76
- backend payload label read in the language you picked.
77
- - **Numbers say what they counted.** "출처 반영률 12%" is now "출처가 남은
78
- 기억 · 35 / 291개" with the sentence that explains it. Model names read
79
- "Gemma 4 26B A4B Instruct", never `mlx-community/gemma-4-26b-a4b-it-4bit`.
80
- - **Defects found by using it with a model loaded.** The conversation header
81
- Brain rendered at 311px and covered 46% of the viewport; the sticky composer
82
- hid the end of every answer; a nested payload printed its field names where
83
- its values belonged; the folder button never opened a picker in a browser.
84
- All fixed, each with a test.
67
+ The current release is **10.2.0 — Load-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.
85
97
 
86
98
  Release notes: [RELEASE.md](RELEASE.md) · Full history: [docs/CHANGELOG.md](docs/CHANGELOG.md)
87
99
 
88
- Expected artifacts for 10.0.0 release must use exact filenames:
100
+ Expected artifacts for 10.2.0 release must use exact filenames:
89
101
 
90
- - `dist/ltcai-10.0.0-py3-none-any.whl`
91
- - `dist/ltcai-10.0.0.tar.gz`
92
- - `ltcai-10.0.0.tgz`
93
- - `dist/ltcai-10.0.0.vsix`
94
- - `src-tauri/target/release/bundle/dmg/Lattice AI_10.0.0_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`
95
107
 
96
108
  Do not use wildcard artifact uploads. Package registry publishing remains owner-run.
97
109
 
@@ -111,7 +123,7 @@ See [ARCHITECTURE.md](ARCHITECTURE.md) for details and
111
123
 
112
124
  - External package registries are owner-published and can lag behind GitHub.
113
125
  - PostgreSQL/pgvector is optional scale/migration tooling. SQLite remains the
114
- live local Brain store in 10.0.0.
126
+ live local Brain store in 10.2.0.
115
127
  - Docker, model downloads, cloud model calls, Telegram, Brain Network, and
116
128
  update checks require explicit user action.
117
129
  - Conversation does not fabricate answers when no model is loaded. Agent and
@@ -120,12 +132,16 @@ See [ARCHITECTURE.md](ARCHITECTURE.md) for details and
120
132
  model success.
121
133
  - Some backend-generated messages (for example the Postgres DSN notice) are
122
134
  produced server-side in English and are shown as-is; server-side i18n is not
123
- part of 10.0.0.
135
+ part of 10.2.0.
124
136
 
125
137
  ## Release History
126
138
 
127
139
  | Version | Theme |
128
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 |
129
145
  | 10.0.0 | Plain Language |
130
146
  | 9.9.9 | Lean Shell |
131
147
  | 9.9.8 | Autonomy Dial |
package/docs/CHANGELOG.md CHANGED
@@ -4,6 +4,162 @@ 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
+
7
163
  ## [10.0.0] - 2026-07-28
8
164
 
9
165
  ### Changed
@@ -1,6 +1,6 @@
1
1
  # Community And Plugins
2
2
 
3
- Current release: **10.0.0 — Plain Language**.
3
+ Current release: **10.2.0 — Load-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: **10.0.0 — Plain Language**.
6
+ Current release: **10.2.0 — Load-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: **10.0.0 — Plain Language**.
3
+ Current release: **10.2.0 — Load-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 (v10.0.0)
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: **10.0.0 — Plain Language**.
3
+ Current release: **10.2.0 — Load-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: **10.0.0 — Plain Language**.
3
+ Current release: **10.2.0 — Load-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: **10.0.0 — Plain Language**.
3
+ Current release: **10.2.0 — Load-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__ = "10.0.0"
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)
@@ -21,12 +21,13 @@ import sqlite3
21
21
  import time
22
22
  import zipfile
23
23
  from collections import Counter
24
+ from contextlib import contextmanager
24
25
  from datetime import datetime
25
26
  from pathlib import Path
26
- from typing import Any, Dict, Iterable, List, Optional, Tuple
27
+ from typing import Any, Dict, Iterable, Iterator, List, Optional, Tuple
27
28
 
28
29
  try:
29
- from .schema import KGStoreV2, NodeType, EdgeType, _exec_script
30
+ from .schema import EdgeType, KGStoreV2, NodeType, _exec_script
30
31
  except Exception: # pragma: no cover - v2 schema is optional at import time
31
32
  KGStoreV2 = None # type: ignore[assignment]
32
33
  NodeType = None # type: ignore[assignment]
@@ -44,6 +45,7 @@ _READ_FROM_V2_DEFAULT = os.getenv("LATTICEAI_KG_READ_V2", "1") != "0"
44
45
  # Static constants (projection/format versions, local-ingestion classification
45
46
  # tables, OS exclusion lists) live in ._kg_constants; re-exported here so every
46
47
  # existing ``from ._kg_common import <CONST>`` site is unaffected.
48
+ from ..quiet import quiet
47
49
  from ._kg_constants import ( # noqa: E402
48
50
  _KG_DB_FORMAT_KEY,
49
51
  _KG_DB_FORMAT_VERSION,
@@ -67,7 +69,6 @@ from ._kg_constants import ( # noqa: E402
67
69
  WINDOWS_EXCLUDED_NAMES,
68
70
  )
69
71
 
70
-
71
72
  # Pure fs/path/hash/classification helpers → ._kg_fsutil (re-exported so the
72
73
  # computed __all__ below still forwards them to the graph mixins).
73
74
  from ._kg_fsutil import * # noqa: E402,F401,F403
@@ -152,7 +153,7 @@ def chunk_strategy_for(filename: Any, *, content_type: str = "") -> str:
152
153
  if mime.startswith("text/html") or mime.startswith("text/plain"):
153
154
  return "prose"
154
155
  except Exception:
155
- pass
156
+ quiet()
156
157
  return "plain"
157
158
 
158
159
 
@@ -736,7 +737,6 @@ _CONCEPT_STOP: set = {
736
737
  "yes",
737
738
  "not",
738
739
  "but",
739
- "are",
740
740
  "all",
741
741
  "any",
742
742
  "out",
@@ -11,7 +11,6 @@ them to the graph mixins), so existing call sites are unaffected.
11
11
  from __future__ import annotations
12
12
 
13
13
  # ruff: noqa: F403,F405
14
-
15
14
  import hashlib
16
15
  import math
17
16
  import os
@@ -21,8 +20,10 @@ from datetime import datetime
21
20
  from pathlib import Path
22
21
  from typing import Any, Dict, List, Optional, Tuple
23
22
 
23
+ from ..quiet import quiet
24
+ from ..utils import now_iso as _now
25
+ from ..utils import parse_iso as _parse_iso
24
26
  from ._kg_constants import * # noqa: F401,F403
25
- from ..utils import now_iso as _now, parse_iso as _parse_iso
26
27
 
27
28
 
28
29
  def _recency_score(
@@ -202,7 +203,7 @@ def _excluded_directory_reason(
202
203
  ):
203
204
  return "user_library"
204
205
  except OSError:
205
- pass
206
+ quiet()
206
207
  if _prefix_blocks(MACOS_EXCLUDED_PREFIXES):
207
208
  return "system_folder"
208
209
  if os_type == "linux":