ltcai 8.6.0 → 8.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +31 -29
  2. package/docs/CHANGELOG.md +23 -1
  3. package/docs/COMMUNITY_AND_PLUGINS.md +5 -3
  4. package/docs/DEVELOPMENT.md +11 -9
  5. package/docs/LEGACY_COMPATIBILITY.md +3 -3
  6. package/docs/ONBOARDING.md +2 -2
  7. package/docs/TRUST_MODEL.md +1 -1
  8. package/docs/WHY_LATTICE.md +2 -2
  9. package/docs/kg-schema.md +1 -1
  10. package/lattice_brain/__init__.py +1 -1
  11. package/lattice_brain/runtime/multi_agent.py +1 -1
  12. package/latticeai/__init__.py +1 -1
  13. package/latticeai/app_factory.py +2 -2
  14. package/latticeai/core/config.py +14 -2
  15. package/latticeai/core/marketplace.py +1 -1
  16. package/latticeai/core/workspace_os.py +1 -1
  17. package/latticeai/models/router.py +3 -2
  18. package/latticeai/runtime/router_registration.py +11 -5
  19. package/latticeai/services/architecture_readiness.py +2 -2
  20. package/latticeai/services/model_runtime.py +52 -28
  21. package/latticeai/services/product_readiness.py +11 -11
  22. package/package.json +3 -2
  23. package/scripts/bump_version.py +2 -0
  24. package/scripts/check_python.py +25 -12
  25. package/src-tauri/Cargo.lock +1 -1
  26. package/src-tauri/Cargo.toml +1 -1
  27. package/src-tauri/tauri.conf.json +1 -1
  28. package/static/app/asset-manifest.json +10 -10
  29. package/static/app/assets/{Act-IrojDEWY.js → Act-_U7mhXir.js} +1 -1
  30. package/static/app/assets/{Brain--VODO4HM.js → Brain-BxyTHZ21.js} +1 -1
  31. package/static/app/assets/Capture-DyDKWNh9.js +1 -0
  32. package/static/app/assets/{Library-DrVto3cO.js → Library-DJ8KioFM.js} +1 -1
  33. package/static/app/assets/{System-DNrVbcMX.js → System-C0FIb3OO.js} +1 -1
  34. package/static/app/assets/index-Bh7IIlyY.js +16 -0
  35. package/static/app/assets/{primitives-lz45ZfWY.js → primitives-BywkNS3f.js} +1 -1
  36. package/static/app/assets/{textarea-C7uwJsxy.js → textarea-CQ61Rycp.js} +1 -1
  37. package/static/app/index.html +1 -1
  38. package/static/sw.js +1 -1
  39. package/static/app/assets/Capture-BUP_wJ7s.js +0 -1
  40. package/static/app/assets/index-BaaPtsLp.js +0 -16
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Lattice AI
2
2
 
3
- **Lattice AI 8.6.0 is the local-first Digital Brain platform. This release fixes desktop folder selection for the Tauri localhost app, improves Capture feedback for folder and web source saving, and aligns the new Brain shell navigation with CI-verified visual coverage.**
3
+ **Lattice AI 8.7.0 is the local-first Digital Brain platform. This release hardens runtime state ownership, refreshes the checked-in release evidence, and synchronizes package, desktop, extension, and documentation metadata to the 8.7.0 line.**
4
4
 
5
5
  **Lattice AI는 모델이 바뀌어도 내 지식과 맥락을 보존하는 로컬 우선 AI 브레인입니다.**
6
6
 
@@ -65,9 +65,9 @@ You need Lattice AI when:
65
65
  ## Living Brain Flow
66
66
 
67
67
  The screenshots below are the latest checked-in visual evidence captures from
68
- the 8.2 Brain Home release. The 8.3 line keeps that first-run flow focused on
69
- the Brain while adding clearer onboarding, stronger workflow/runtime
70
- boundaries, and better ingestion proof behind the scenes.
68
+ the 8.7.0 release. They keep the first-run Brain flow, memory graph, source
69
+ capture, model library, system view, admin console, and review center visible as
70
+ release gates.
71
71
 
72
72
  ### 1. Wake Brain
73
73
 
@@ -79,21 +79,21 @@ confirm owner, check the computer, choose the Brain voice.
79
79
  Choose the owner of the Brain. The profile is not a SaaS account by default; it
80
80
  is the local identity for the knowledge you keep.
81
81
 
82
- ![Login](output/release/v8.2.0/screenshots/01-login.png)
82
+ ![Login](output/release/v8.7.0/screenshots/01-login.png)
83
83
 
84
84
  ### 3. Recommended Models
85
85
 
86
86
  Start with a short list: safest recommendation, faster model, stronger model.
87
87
  Advanced details stay available without overwhelming first-time users.
88
88
 
89
- ![Recommended Models](output/release/v8.2.0/screenshots/02-recommended-models.png)
89
+ ![Recommended Models](output/release/v8.7.0/screenshots/02-recommended-models.png)
90
90
 
91
91
  ### 4. Install And Load
92
92
 
93
93
  Download and load only after consent. Lattice explains model size, local
94
94
  execution, and network use before work starts.
95
95
 
96
- ![Install and Load](output/release/v8.2.0/screenshots/03-install-load-progress.png)
96
+ ![Install and Load](output/release/v8.7.0/screenshots/03-install-load-progress.png)
97
97
 
98
98
  ### 5. Brain Chat
99
99
 
@@ -101,14 +101,14 @@ Talk normally. Useful decisions and context become memory, then appear later as
101
101
  topics, relationships, graph structure, and the concentric memory rings around
102
102
  the Brain.
103
103
 
104
- ![Brain Brief Home](output/release/v8.2.0/screenshots/04-brain-chat-home.png)
104
+ ![Brain Brief Home](output/release/v8.7.0/screenshots/04-brain-chat-home.png)
105
105
 
106
106
  ### 6. Review Center
107
107
 
108
108
  Automation results are staged for review before they become durable decisions.
109
109
  Snooze, unsnooze, run now, approve, and dismiss actions stay explicit.
110
110
 
111
- ![Review Center](output/release/v8.2.0/screenshots/12-review-center.png)
111
+ ![Review Center](output/release/v8.7.0/screenshots/12-review-center.png)
112
112
 
113
113
  ## Brain Depths
114
114
 
@@ -124,10 +124,10 @@ The user travels inward from everyday memory to deeper structure:
124
124
 
125
125
  Walkthrough:
126
126
 
127
- ![v8.2.0 Living Brain walkthrough](output/release/v8.2.0/gifs/v8.2.0-living-brain-walkthrough.gif)
127
+ ![v8.7.0 Living Brain walkthrough](output/release/v8.7.0/gifs/v8.7.0-living-brain-walkthrough.gif)
128
128
 
129
129
  Screenshot index and capture notes:
130
- [output/release/v8.2.0/SCREENSHOT_INDEX.md](output/release/v8.2.0/SCREENSHOT_INDEX.md)
130
+ [output/release/v8.7.0/SCREENSHOT_INDEX.md](output/release/v8.7.0/SCREENSHOT_INDEX.md)
131
131
 
132
132
  ## Install
133
133
 
@@ -190,7 +190,6 @@ Main validation set:
190
190
 
191
191
  ```bash
192
192
  npm run check:python
193
- node scripts/run_python.mjs -m ruff check .
194
193
  npm run lint
195
194
  npm run typecheck
196
195
  npm run test:unit
@@ -200,34 +199,36 @@ npm run desktop:tauri:check
200
199
  npm run docs:check-links
201
200
  ```
202
201
 
202
+ `npm run lint` includes the Python Ruff baseline, frontend TypeScript lint
203
+ gate, visual smoke syntax checks, and i18n literal checks.
204
+
203
205
  See [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) for developer workflow details.
204
206
 
205
207
  ## Current Release
206
208
 
207
- The current release is **8.6.0 — Desktop Capture & Navigation Reliability**:
209
+ The current release is **8.7.0 — Runtime State Hygiene & Release Evidence Refresh**:
208
210
 
209
- - The desktop Capture flow can open a native folder picker from the Tauri
210
- localhost app, fill the selected path, and immediately scan/connect it.
211
- - Folder picker failures now surface an actionable message instead of silently
212
- doing nothing, while manual path entry remains available.
213
- - Web page capture keeps paste, Enter-to-save, and `https://` normalization for
214
- bare domains.
215
- - The Brain shell sidebar/navigation and admin entry points are covered by
216
- updated Visual Smoke tests, while the 8.5 ToolRegistry and Config DI
217
- hardening remains active.
211
+ - Internal model-runtime logic now reads from the typed `ModelRuntimeState`
212
+ object instead of the legacy module-global compatibility surface.
213
+ - `sync_to_module_globals()` remains available for external shims, but now emits
214
+ a `DeprecationWarning` so new code does not rebuild global-state coupling.
215
+ - The 8.7.0 release evidence refresh updates screenshots, walkthrough GIF/WebM,
216
+ package/runtime/static/Tauri metadata, and current-release documentation.
217
+ - The 8.6 desktop Capture reliability and 8.5 ToolRegistry/Config DI hardening
218
+ remain active release guarantees.
218
219
 
219
- Expected artifacts for 8.6.0 release must use exact filenames:
220
+ Expected artifacts for 8.7.0 release must use exact filenames:
220
221
 
221
- - `dist/ltcai-8.6.0-py3-none-any.whl`
222
- - `dist/ltcai-8.6.0.tar.gz`
223
- - `ltcai-8.6.0.tgz`
224
- - `dist/ltcai-8.6.0.vsix`
225
- - `src-tauri/target/release/bundle/dmg/Lattice AI_8.6.0_aarch64.dmg`
222
+ - `dist/ltcai-8.7.0-py3-none-any.whl`
223
+ - `dist/ltcai-8.7.0.tar.gz`
224
+ - `ltcai-8.7.0.tgz`
225
+ - `dist/ltcai-8.7.0.vsix`
226
+ - `src-tauri/target/release/bundle/dmg/Lattice AI_8.7.0_aarch64.dmg`
226
227
 
227
228
  Do not use wildcard artifact uploads. Package registry publishing remains owner-run.
228
229
 
229
230
  See [docs/ROADMAP_RECOMMENDATIONS.md](docs/ROADMAP_RECOMMENDATIONS.md) for the
230
- strategic roadmap slices applied through 8.6.0 and the follow-up tracks.
231
+ strategic roadmap slices applied through 8.7.0 and the follow-up tracks.
231
232
 
232
233
  ## Known Limitations
233
234
 
@@ -244,6 +245,7 @@ strategic roadmap slices applied through 8.6.0 and the follow-up tracks.
244
245
 
245
246
  | Version | Theme |
246
247
  | --- | --- |
248
+ | 8.7.0 | Runtime State Hygiene & Release Evidence Refresh: model-runtime internals prefer typed state over legacy globals, compatibility sync is deprecated, 8.7.0 visual evidence is refreshed, and all release metadata/docs are synchronized |
247
249
  | 8.6.0 | Desktop Capture & Navigation Reliability: native folder selection works from the Tauri localhost app, picker failures surface in Capture, web saving remains one-action, and the Brain shell sidebar/admin flow is CI-covered |
248
250
  | 8.5.0 | Tool Registry Readiness & Config DI: ToolRegistry drift removed, `tz_name` flows through central Config into automation runtimes, and current-release documentation is synchronized |
249
251
  | 8.4.0 | Action-Aware Brain Chat: explicit file create/write/save/edit requests from Brain Chat route into the governed workspace file tool so files are actually created instead of returned as code-only answers |
package/docs/CHANGELOG.md CHANGED
@@ -6,7 +6,29 @@ existed at that release.
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
- No unreleased changes.
9
+ ## [8.7.0] - 2026-07-05
10
+
11
+ ### Added
12
+ - Added unit test coverage for model-runtime `STATE` source-of-truth behavior
13
+ and deprecation warnings on legacy global synchronization.
14
+ - Added refreshed 8.7.0 screenshots, walkthrough GIF/WebM, and capture notes
15
+ under `output/release/v8.7.0/`.
16
+
17
+ ### Changed
18
+ - Reduced internal reliance on bare module globals in
19
+ `latticeai/services/model_runtime.py`; implementation logic now consistently
20
+ reads from the `ModelRuntimeState` instance while globals remain as a legacy
21
+ compatibility surface.
22
+ - Added `DeprecationWarning` to `sync_to_module_globals()` while preserving the
23
+ external shim behavior.
24
+ - Removed a loose `as any` cast from `frontend/src/pages/Act.tsx`.
25
+ - Version bumped to 8.7.0 across Python, npm, VS Code extension, Tauri, static
26
+ metadata, README evidence links, release notes, and current-release
27
+ documentation.
28
+
29
+ ### Fixed
30
+ - Internal model-runtime functions now prefer the typed state object per the
31
+ project preference for composition over global mutable state.
10
32
 
11
33
  ## [8.6.0] - 2026-07-05
12
34
 
@@ -1,9 +1,11 @@
1
1
  # Community And Plugins
2
2
 
3
- Current release: **8.6.0 — Desktop Capture & Navigation Reliability**.
3
+ Current release: **8.7.0 — Runtime State Hygiene & Release Evidence Refresh**.
4
4
 
5
- LatticeAI defines the path from a strong local-first framework (8.4.0 action-aware baseline, 8.5.0 registry+DI hardening, 8.6.0 capture/navigation reliability) to a
6
- product ecosystem. The immediate goal is small and practical: make it clear how
5
+ LatticeAI defines the path from a strong local-first framework (8.4.0
6
+ action-aware baseline, 8.5.0 registry+DI hardening, 8.6.0 capture/navigation
7
+ reliability, and 8.7.0 runtime-state hygiene) to a product ecosystem. The
8
+ immediate goal is small and practical: make it clear how
7
9
  contributors can extend the Brain without weakening local-first trust,
8
10
  workspace scoping, or release quality.
9
11
 
@@ -1,10 +1,10 @@
1
1
  # Lattice AI Development
2
2
 
3
- Current release: **8.6.0 — Desktop Capture & Navigation Reliability**.
3
+ Current release: **8.7.0 — Runtime State Hygiene & Release Evidence Refresh**.
4
4
 
5
5
  This document is for contributors working on the local-first Digital Brain
6
6
  codebase. Product positioning and quick start stay in `README.md`; release
7
- history is intentionally limited to 7.0.0-8.6.0 in `docs/CHANGELOG.md` and
7
+ history is intentionally limited to 7.0.0-8.7.0 in `docs/CHANGELOG.md` and
8
8
  `RELEASE.md`.
9
9
 
10
10
  ## Product Contract
@@ -49,13 +49,15 @@ API, UI, or release work, run:
49
49
 
50
50
  ```bash
51
51
  npm run check:python
52
- node scripts/run_python.mjs -m ruff check .
53
52
  npm run lint
54
53
  npm run typecheck
55
54
  npm run test:unit
56
55
  npm run docs:check-links
57
56
  ```
58
57
 
58
+ `npm run lint` runs the Python Ruff baseline, frontend TypeScript lint gate,
59
+ visual smoke syntax checks, and i18n literal checks.
60
+
59
61
  Use these when the change touches the relevant surface:
60
62
 
61
63
  ```bash
@@ -112,10 +114,10 @@ For user-facing, API, runtime, release, or packaging changes, check:
112
114
  Release/publish examples must use exact target-version filenames. Do not
113
115
  document wildcard artifact upload commands.
114
116
 
115
- For 8.6.0 release work, exact artifacts are:
117
+ For 8.7.0 release work, exact artifacts are:
116
118
 
117
- - `dist/ltcai-8.6.0-py3-none-any.whl`
118
- - `dist/ltcai-8.6.0.tar.gz`
119
- - `ltcai-8.6.0.tgz`
120
- - `dist/ltcai-8.6.0.vsix`
121
- - `src-tauri/target/release/bundle/dmg/Lattice AI_8.6.0_aarch64.dmg`
119
+ - `dist/ltcai-8.7.0-py3-none-any.whl`
120
+ - `dist/ltcai-8.7.0.tar.gz`
121
+ - `ltcai-8.7.0.tgz`
122
+ - `dist/ltcai-8.7.0.vsix`
123
+ - `src-tauri/target/release/bundle/dmg/Lattice AI_8.7.0_aarch64.dmg`
@@ -1,14 +1,14 @@
1
1
  # Legacy Compatibility Map
2
2
 
3
- Current release: **8.6.0 — Desktop Capture & Navigation Reliability**.
3
+ Current release: **8.7.0 — Runtime State Hygiene & Release Evidence Refresh**.
4
4
 
5
5
  Lattice AI is moving toward a smaller, modular architecture centered on
6
6
  `lattice_brain`, `latticeai.services`, `latticeai.api`, and `latticeai.runtime`.
7
7
  Some root-level modules remain packaged for compatibility with older imports,
8
8
  CLI entrypoints, or extension workflows. Their presence does not define the
9
- current 8.6.0 architecture.
9
+ current 8.7.0 architecture.
10
10
 
11
- 8.6.0 also tracks the inner compatibility layers that sit below the root
11
+ 8.7.0 also tracks the inner compatibility layers that sit below the root
12
12
  modules. The managed inventory lives in `latticeai.core.legacy_compatibility`
13
13
  and groups shims by layer:
14
14
 
@@ -1,6 +1,6 @@
1
1
  # Lattice AI Onboarding
2
2
 
3
- Current release: **8.6.0 — Desktop Capture & Navigation Reliability**.
3
+ Current release: **8.7.0 — Runtime State Hygiene & Release Evidence Refresh**.
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
@@ -32,7 +32,7 @@ read the docs first.
32
32
 
33
33
  ## Release Gate
34
34
 
35
- 8.6.0 treats onboarding as a release gate, not marketing copy. The current
35
+ 8.7.0 treats onboarding as a release gate, not marketing copy. The current
36
36
  machine-checkable product readiness report requires this five-minute contract,
37
37
  the Brain Home surface, setup helpers, graph ingestion tests, and exact release
38
38
  artifact documentation before the release can be called complete.
@@ -1,6 +1,6 @@
1
1
  # Lattice AI Trust Model
2
2
 
3
- Current release: **8.6.0 — Desktop Capture & Navigation Reliability**.
3
+ Current release: **8.7.0 — Runtime State Hygiene & Release Evidence Refresh**.
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: **8.6.0 — Desktop Capture & Navigation Reliability**.
3
+ Current release: **8.7.0 — Runtime State Hygiene & Release Evidence Refresh**.
4
4
 
5
5
  **Lattice AI is a local-first Digital Brain that keeps your knowledge durable
6
6
  across any AI model.**
@@ -32,7 +32,7 @@ The graph matters, but it is not the product identity. The product identity is a
32
32
  local-first Digital Brain: a place where the user can talk, add sources, watch
33
33
  memory grow, and inspect the Knowledge Graph when they need proof.
34
34
 
35
- In 8.6.0 the first screen is intentionally not a dashboard. The living Brain,
35
+ In 8.7.0 the first screen is intentionally not a dashboard. The living Brain,
36
36
  conversation composer, evidence-backed Brain Brief, and five-minute onboarding
37
37
  path appear together so the user immediately knows what matters, what to add,
38
38
  and why the answer is grounded in local memory.
package/docs/kg-schema.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Knowledge Graph Schema
2
2
 
3
- Current release: **8.6.0 — Desktop Capture & Navigation Reliability**.
3
+ Current release: **8.7.0 — Runtime State Hygiene & Release Evidence Refresh**.
4
4
 
5
5
  명세 출처: `lattice_ai_full_spec.pptx` 슬라이드 20·21·22
6
6
  구현: `kg_schema.py`
@@ -26,7 +26,7 @@ from .storage import (
26
26
  storage_from_env,
27
27
  )
28
28
 
29
- __version__ = "8.6.0"
29
+ __version__ = "8.7.0"
30
30
 
31
31
  __all__ = [
32
32
  "AgentRuntime",
@@ -21,7 +21,7 @@ from typing import Any, Callable, Dict, List, Optional
21
21
  from .contracts import multi_agent_contract
22
22
 
23
23
 
24
- MULTI_AGENT_VERSION = "8.6.0"
24
+ MULTI_AGENT_VERSION = "8.7.0"
25
25
 
26
26
  AGENT_ROLES = ("researcher", "planner", "executor", "reviewer", "release")
27
27
  CORE_PIPELINE = ("planner", "executor", "reviewer")
@@ -1,3 +1,3 @@
1
1
  """Lattice AI - modular server package."""
2
2
 
3
- __version__ = "8.6.0"
3
+ __version__ = "8.7.0"
@@ -107,7 +107,7 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
107
107
  enforce_rate_limit as _enforce_rate_limit,
108
108
  )
109
109
  from latticeai.core.audit import (
110
- get_audit_log as _get_audit_log,
110
+ get_audit_log as _get_audit_log, # noqa: F401 - legacy server_app attr exported via locals()
111
111
  classify_sensitive_message as _classify_sensitive_message,
112
112
  build_sensitivity_report as _build_sensitivity_report,
113
113
  build_admin_audit_report as _build_admin_audit_report,
@@ -992,7 +992,7 @@ def _build(config: "Optional[Config]" = None) -> Dict[str, Any]:
992
992
  create_admin_router=create_admin_router,
993
993
  require_admin=require_admin,
994
994
  get_history=get_history,
995
- get_audit_log=_get_audit_log,
995
+ get_audit_log=get_audit_log,
996
996
  audit_file=AUDIT_FILE,
997
997
  public_user=public_user,
998
998
  load_vpc_config=load_vpc_config,
@@ -43,7 +43,12 @@ def _bool(env: Mapping[str, str], key: str, default: bool = False) -> bool:
43
43
  raw = env.get(key)
44
44
  if raw is None:
45
45
  return default
46
- return raw.strip().lower() in {"1", "true", "yes", "on"}
46
+ normalized = raw.strip().lower()
47
+ if normalized in {"1", "true", "yes", "on"}:
48
+ return True
49
+ if normalized in {"0", "false", "no", "off"}:
50
+ return False
51
+ return default
47
52
 
48
53
 
49
54
  def _int(env: Mapping[str, str], key: str, default: int) -> int:
@@ -56,6 +61,13 @@ def _int(env: Mapping[str, str], key: str, default: int) -> int:
56
61
  return default
57
62
 
58
63
 
64
+ def _port(env: Mapping[str, str], key: str, default: int) -> int:
65
+ port = _int(env, key, default)
66
+ if 1 <= port <= 65535:
67
+ return port
68
+ return default
69
+
70
+
59
71
  @dataclass(frozen=True)
60
72
  class Config:
61
73
  """Everything a caller must know about app-level settings.
@@ -146,7 +158,7 @@ class Config:
146
158
  is_public = app_mode == "public"
147
159
 
148
160
  host = _value(env, "LATTICEAI_HOST", "127.0.0.1")
149
- port = _int(env, "LATTICEAI_PORT", 4825)
161
+ port = _port(env, "LATTICEAI_PORT", 4825)
150
162
  network_exposed = not host_is_loopback(host)
151
163
 
152
164
  cors_extra = [item.strip() for item in _value(env, "LATTICEAI_CORS_ALLOWED_ORIGINS", "").split(",") if item.strip()]
@@ -11,7 +11,7 @@ from copy import deepcopy
11
11
  from typing import Any, Dict, List, Optional
12
12
 
13
13
 
14
- MARKETPLACE_VERSION = "8.6.0"
14
+ MARKETPLACE_VERSION = "8.7.0"
15
15
  TEMPLATE_KINDS = ("plugin", "workflow", "agent")
16
16
 
17
17
 
@@ -49,7 +49,7 @@ __all__ = [
49
49
  "remove_skill_directory",
50
50
  ]
51
51
 
52
- WORKSPACE_OS_VERSION = "8.6.0"
52
+ WORKSPACE_OS_VERSION = "8.7.0"
53
53
 
54
54
  # Workspace types separate single-user Personal workspaces from shared
55
55
  # Organization workspaces. Both keep the same local-first JSON store; the type
@@ -13,8 +13,9 @@ import time
13
13
  from dataclasses import dataclass
14
14
  from pathlib import Path
15
15
 
16
- # Set MLX_VLM_DRAFT_KIND to 'mtp' to enable the Gemma 4 assistant MTP drafter.
17
- os.environ["MLX_VLM_DRAFT_KIND"] = "mtp"
16
+ # Default Gemma 4 assistant drafting to MTP without overriding an operator's
17
+ # explicit MLX runtime choice.
18
+ os.environ.setdefault("MLX_VLM_DRAFT_KIND", "mtp")
18
19
 
19
20
  from concurrent.futures import ThreadPoolExecutor
20
21
  from typing import AsyncIterator, Dict, Optional, Tuple, List
@@ -162,16 +162,22 @@ def build_auth_admin_security_router_bundle(
162
162
  append_audit_event=append_audit_event,
163
163
  )
164
164
 
165
- def security_audit_events_safe() -> list[dict[str, Any]]:
165
+ def audit_log_events_safe() -> list[dict[str, Any]]:
166
166
  try:
167
- return get_audit_log(audit_file)
167
+ return get_audit_log()
168
+ except TypeError:
169
+ try:
170
+ return get_audit_log(audit_file)
171
+ except Exception as exc: # pragma: no cover - defensive legacy behavior
172
+ logger.warning("security audit events load failed: %s", exc)
173
+ return []
168
174
  except Exception as exc: # pragma: no cover - defensive legacy behavior
169
175
  logger.warning("security audit events load failed: %s", exc)
170
176
  return []
171
177
 
172
178
  def security_list_uploaded_files() -> list[dict[str, Any]]:
173
179
  files: list[dict[str, Any]] = []
174
- for idx, event in enumerate(security_audit_events_safe()):
180
+ for idx, event in enumerate(audit_log_events_safe()):
175
181
  if event.get("event_type") != "document_upload":
176
182
  continue
177
183
  files.append({
@@ -191,7 +197,7 @@ def build_auth_admin_security_router_bundle(
191
197
  security_router = create_security_router(
192
198
  require_admin=require_admin,
193
199
  get_history=get_history,
194
- get_audit_events=security_audit_events_safe,
200
+ get_audit_events=audit_log_events_safe,
195
201
  classify_sensitive_message=classify_sensitive_message,
196
202
  build_sensitivity_report=build_sensitivity_report,
197
203
  list_uploaded_files=security_list_uploaded_files,
@@ -205,7 +211,7 @@ def build_auth_admin_security_router_bundle(
205
211
  "security_router": security_router,
206
212
  "_graph_stats_safe": graph_stats_safe,
207
213
  "_product_hardening_status": product_hardening_status,
208
- "_security_audit_events_safe": security_audit_events_safe,
214
+ "_security_audit_events_safe": audit_log_events_safe,
209
215
  "_security_list_uploaded_files": security_list_uploaded_files,
210
216
  }
211
217
 
@@ -1,6 +1,6 @@
1
1
  """Machine-checkable architecture readiness gates for release work.
2
2
 
3
- 8.6.0 keeps the major architecture priorities under an explicit release
3
+ 8.7.0 keeps the major architecture priorities under an explicit release
4
4
  contract while product maturity work reduces visible beta seams. AgentRuntime, ToolRegistry,
5
5
  central Config, decomposed server runtime, and Knowledge Graph stabilization
6
6
  must remain discoverable, ordered, and backed by tests before the release can be
@@ -17,7 +17,7 @@ from typing import Any, Dict, List
17
17
  from latticeai.core.legacy_compatibility import legacy_shim_report
18
18
 
19
19
 
20
- ARCHITECTURE_VERSION_TARGET = "8.6.0"
20
+ ARCHITECTURE_VERSION_TARGET = "8.7.0"
21
21
 
22
22
  PREFERRED_REFACTORING_ORDER = [
23
23
  "agent-runtime",
@@ -16,6 +16,7 @@ import shutil
16
16
  import time
17
17
  import urllib.error
18
18
  import urllib.request
19
+ import warnings
19
20
  from pathlib import Path
20
21
  from typing import AsyncIterator, Dict, List, Optional
21
22
 
@@ -100,6 +101,21 @@ class ModelRuntimeState:
100
101
  self.get_user_api_key = _missing_user_api_key
101
102
 
102
103
  def sync_to_module_globals(self):
104
+ """Sync to bare module globals for legacy external importers.
105
+
106
+ This is a compatibility surface only. Internal code should use STATE.
107
+ Emits DeprecationWarning (future removal in major after 8.x).
108
+ """
109
+ warnings.warn(
110
+ "sync_to_module_globals is a legacy compatibility shim and will be removed "
111
+ "after 8.x. Use latticeai.services.model_runtime.STATE or injected context instead.",
112
+ DeprecationWarning,
113
+ stacklevel=2,
114
+ )
115
+ self._sync_globals()
116
+
117
+ def _sync_globals(self) -> None:
118
+ """Internal no-warning sync used at init."""
103
119
  global router, APP_MODE, DEFAULT_HOST, DEFAULT_PORT, DATA_DIR, BASE_DIR
104
120
  global ENABLE_TELEGRAM, ENABLE_GRAPH, AUTOLOAD_MODELS, MODEL_IDLE_UNLOAD_SECONDS
105
121
  global ALLOW_LOCAL_MODELS, REQUIRE_AUTH, INVITE_GATE_ENABLED, ALLOW_PLAINTEXT_API_KEYS
@@ -128,7 +144,7 @@ class ModelRuntimeState:
128
144
  get_user_api_key = self.get_user_api_key
129
145
 
130
146
  STATE = ModelRuntimeState()
131
- STATE.sync_to_module_globals()
147
+ STATE._sync_globals() # initial no-warning; public API warns on explicit legacy syncs
132
148
 
133
149
  # Configured by server_app.configure_model_runtime during app assembly.
134
150
 
@@ -141,7 +157,9 @@ def _env_bool(key: str, default: bool = False) -> bool:
141
157
 
142
158
 
143
159
  def _download_allowed(allow_download: bool = False) -> bool:
144
- return bool(allow_download) or _env_bool("LATTICEAI_ALLOW_MODEL_DOWNLOADS", default=False) or bool(AUTOLOAD_MODELS)
160
+ # Prefer STATE (the source of truth) over bare module global for internal logic.
161
+ autoload = getattr(STATE, "AUTOLOAD_MODELS", AUTOLOAD_MODELS)
162
+ return bool(allow_download) or _env_bool("LATTICEAI_ALLOW_MODEL_DOWNLOADS", default=False) or bool(autoload)
145
163
 
146
164
 
147
165
  def _download_block(provider: str, model_name: str) -> None:
@@ -204,7 +222,7 @@ def configure_model_runtime(**deps) -> None:
204
222
  elif key == "get_user_api_key":
205
223
  STATE.get_user_api_key = value
206
224
 
207
- STATE.sync_to_module_globals()
225
+ STATE._sync_globals() # wiring path uses internal (no spurious deprecation in normal startup)
208
226
 
209
227
 
210
228
  # Catalog data + version-dedup helpers live in ``model_catalog``; re-exported
@@ -727,7 +745,8 @@ def engine_installed(engine: str) -> bool:
727
745
  return False
728
746
 
729
747
  def engine_status() -> List[Dict]:
730
- cloud_models = router.detected_cloud_models()
748
+ r = getattr(STATE, "router", None) or router
749
+ cloud_models = r.detected_cloud_models() if r else []
731
750
  cloud_by_provider = {}
732
751
  for model in cloud_models:
733
752
  cloud_by_provider.setdefault(model["provider"], []).append(model)
@@ -887,37 +906,41 @@ def engine_status() -> List[Dict]:
887
906
  return engines
888
907
 
889
908
  def runtime_features() -> Dict:
909
+ # Read from STATE object (central) for implementation; bare globals kept only
910
+ # for external legacy consumers who import names directly from this module.
911
+ s = STATE
912
+ r = getattr(s, "router", None) or router
890
913
  return {
891
- "mode": APP_MODE,
892
- "public": IS_PUBLIC_MODE,
893
- "host": DEFAULT_HOST,
894
- "port": DEFAULT_PORT,
895
- "data_dir": str(DATA_DIR),
896
- "telegram_enabled": ENABLE_TELEGRAM,
897
- "graph_enabled": ENABLE_GRAPH,
898
- "autoload_models": AUTOLOAD_MODELS,
899
- "model_idle_unload_seconds": MODEL_IDLE_UNLOAD_SECONDS,
900
- "model_memory_policy": router.model_memory_policy(),
901
- "allow_local_models": ALLOW_LOCAL_MODELS,
914
+ "mode": s.APP_MODE,
915
+ "public": s.IS_PUBLIC_MODE,
916
+ "host": s.DEFAULT_HOST,
917
+ "port": s.DEFAULT_PORT,
918
+ "data_dir": str(s.DATA_DIR),
919
+ "telegram_enabled": s.ENABLE_TELEGRAM,
920
+ "graph_enabled": s.ENABLE_GRAPH,
921
+ "autoload_models": s.AUTOLOAD_MODELS,
922
+ "model_idle_unload_seconds": s.MODEL_IDLE_UNLOAD_SECONDS,
923
+ "model_memory_policy": r.model_memory_policy() if r else None,
924
+ "allow_local_models": s.ALLOW_LOCAL_MODELS,
902
925
  "security": {
903
- "host": DEFAULT_HOST,
904
- "require_auth": REQUIRE_AUTH,
905
- "invite_gate_enabled": INVITE_GATE_ENABLED,
906
- "keyring_available": keyring is not None,
907
- "plaintext_api_keys_allowed": ALLOW_PLAINTEXT_API_KEYS,
908
- "cors_allow_network": CORS_ALLOW_NETWORK,
926
+ "host": s.DEFAULT_HOST,
927
+ "require_auth": s.REQUIRE_AUTH,
928
+ "invite_gate_enabled": s.INVITE_GATE_ENABLED,
929
+ "keyring_available": s.keyring is not None,
930
+ "plaintext_api_keys_allowed": s.ALLOW_PLAINTEXT_API_KEYS,
931
+ "cors_allow_network": s.CORS_ALLOW_NETWORK,
909
932
  },
910
- "default_model": PUBLIC_MODEL if IS_PUBLIC_MODE else LOCAL_MODEL,
933
+ "default_model": s.PUBLIC_MODEL if s.IS_PUBLIC_MODE else s.LOCAL_MODEL,
911
934
  "local_only_features": {
912
- "mlx": ALLOW_LOCAL_MODELS and not IS_PUBLIC_MODE,
913
- "telegram_bridge": ENABLE_TELEGRAM,
914
- "desktop_chrome_bridge": not IS_PUBLIC_MODE,
915
- "computer_use_bridge": not IS_PUBLIC_MODE,
935
+ "mlx": s.ALLOW_LOCAL_MODELS and not s.IS_PUBLIC_MODE,
936
+ "telegram_bridge": s.ENABLE_TELEGRAM,
937
+ "desktop_chrome_bridge": not s.IS_PUBLIC_MODE,
938
+ "computer_use_bridge": not s.IS_PUBLIC_MODE,
916
939
  },
917
940
  "public_features": {
918
941
  "web_ui": True,
919
942
  "openai_compatible_models": True,
920
- "persistent_data_dir": str(DATA_DIR),
943
+ "persistent_data_dir": str(s.DATA_DIR),
921
944
  },
922
945
  }
923
946
 
@@ -1099,7 +1122,8 @@ async def _probe_cloud_model(model_ref: str) -> Dict[str, object]:
1099
1122
 
1100
1123
  async def verify_cloud_models(force: bool = False, provider_filter: Optional[str] = None) -> Dict[str, Dict]:
1101
1124
  now = time.time()
1102
- cloud_items = [item for item in router.detected_cloud_models() if item.get("tag") == "cloud"]
1125
+ r = getattr(STATE, "router", None) or router
1126
+ cloud_items = [item for item in (r.detected_cloud_models() if r else []) if item.get("tag") == "cloud"]
1103
1127
  if provider_filter:
1104
1128
  cloud_items = [item for item in cloud_items if item.get("provider") == provider_filter]
1105
1129