ltcai 7.9.0 → 8.1.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 (107) hide show
  1. package/README.md +35 -49
  2. package/docs/CHANGELOG.md +35 -0
  3. package/docs/V3_FRONTEND.md +11 -5
  4. package/lattice_brain/__init__.py +1 -1
  5. package/lattice_brain/embeddings.py +9 -1
  6. package/lattice_brain/graph/projection.py +19 -5
  7. package/lattice_brain/graph/provenance.py +26 -4
  8. package/lattice_brain/graph/write_master.py +30 -3
  9. package/lattice_brain/runtime/multi_agent.py +1 -1
  10. package/latticeai/__init__.py +1 -1
  11. package/latticeai/app_factory.py +13 -134
  12. package/latticeai/core/local_embeddings.py +8 -0
  13. package/latticeai/core/marketplace.py +1 -1
  14. package/latticeai/core/mcp_registry.py +140 -1
  15. package/latticeai/core/tool_registry.py +7 -0
  16. package/latticeai/core/workspace_os.py +1 -1
  17. package/latticeai/services/architecture_readiness.py +50 -6
  18. package/latticeai/services/model_runtime.py +36 -2
  19. package/latticeai/services/product_readiness.py +19 -17
  20. package/package.json +2 -2
  21. package/scripts/build_frontend_assets.mjs +11 -2
  22. package/src-tauri/Cargo.lock +1 -1
  23. package/src-tauri/Cargo.toml +1 -1
  24. package/src-tauri/tauri.conf.json +1 -1
  25. package/static/app/asset-manifest.json +11 -102
  26. package/static/app/assets/Act-BOO66G-c.js +1 -0
  27. package/static/app/assets/Brain-C6lEYiD7.js +321 -0
  28. package/static/app/assets/{Capture-B9Tlhzqr.js → Capture-TATXBRDw.js} +1 -2
  29. package/static/app/assets/Library-DK4FIp8a.js +1 -0
  30. package/static/app/assets/System-Bgs6Ql7x.js +1 -0
  31. package/static/app/assets/core-CwxXejkd.js +1 -2
  32. package/static/app/assets/index-Bvv79nre.js +16 -0
  33. package/static/app/assets/index-Dslqglia.css +2 -0
  34. package/static/app/assets/primitives-B70WOra0.js +1 -0
  35. package/static/app/assets/{textarea-BZk6ybp5.js → textarea-Czrd9gwM.js} +1 -2
  36. package/static/app/index.html +3 -10
  37. package/static/app/theme-boot.js +8 -0
  38. package/static/sw.js +1 -1
  39. package/frontend/index.html +0 -24
  40. package/frontend/openapi.json +0 -15425
  41. package/frontend/src/App.tsx +0 -243
  42. package/frontend/src/api/client.ts +0 -580
  43. package/frontend/src/api/openapi.ts +0 -17892
  44. package/frontend/src/components/AdminAccessGate.tsx +0 -70
  45. package/frontend/src/components/FeedbackState.tsx +0 -45
  46. package/frontend/src/components/LanguageSwitcher.tsx +0 -23
  47. package/frontend/src/components/LivingBrain.tsx +0 -220
  48. package/frontend/src/components/ProductFlow.tsx +0 -171
  49. package/frontend/src/components/WorkspaceProfileSwitcher.tsx +0 -176
  50. package/frontend/src/components/onboarding/AnalysisScreen.tsx +0 -135
  51. package/frontend/src/components/onboarding/DownloadConsentPanel.tsx +0 -29
  52. package/frontend/src/components/onboarding/InstallScreen.tsx +0 -263
  53. package/frontend/src/components/onboarding/LanguageChooser.tsx +0 -23
  54. package/frontend/src/components/onboarding/LoginScreen.tsx +0 -131
  55. package/frontend/src/components/onboarding/ProductFlowScreens.tsx +0 -13
  56. package/frontend/src/components/onboarding/RecommendationScreen.tsx +0 -116
  57. package/frontend/src/components/onboarding/recommendationModel.ts +0 -189
  58. package/frontend/src/components/primitives.tsx +0 -392
  59. package/frontend/src/components/ui/badge.tsx +0 -27
  60. package/frontend/src/components/ui/button.tsx +0 -37
  61. package/frontend/src/components/ui/card.tsx +0 -22
  62. package/frontend/src/components/ui/input.tsx +0 -16
  63. package/frontend/src/components/ui/textarea.tsx +0 -16
  64. package/frontend/src/features/admin/AdminConsole.tsx +0 -334
  65. package/frontend/src/features/brain/BrainCarePanel.tsx +0 -254
  66. package/frontend/src/features/brain/BrainComposer.tsx +0 -85
  67. package/frontend/src/features/brain/BrainConversation.tsx +0 -688
  68. package/frontend/src/features/brain/BrainGraphLayer.tsx +0 -365
  69. package/frontend/src/features/brain/BrainHome.tsx +0 -624
  70. package/frontend/src/features/brain/BrainMemoryLayer.tsx +0 -45
  71. package/frontend/src/features/brain/BrainOverviewPanel.tsx +0 -149
  72. package/frontend/src/features/brain/BrainRelationshipLayer.tsx +0 -43
  73. package/frontend/src/features/brain/DepthEmergence.tsx +0 -53
  74. package/frontend/src/features/brain/brainData.ts +0 -246
  75. package/frontend/src/features/brain/graphLayout.ts +0 -37
  76. package/frontend/src/features/brain/types.ts +0 -150
  77. package/frontend/src/features/review/ReviewCard.tsx +0 -100
  78. package/frontend/src/features/review/ReviewInbox.tsx +0 -127
  79. package/frontend/src/features/review/reviewHelpers.ts +0 -69
  80. package/frontend/src/i18n.ts +0 -1303
  81. package/frontend/src/lib/utils.ts +0 -33
  82. package/frontend/src/main.tsx +0 -23
  83. package/frontend/src/pages/Act.tsx +0 -458
  84. package/frontend/src/pages/Ask.tsx +0 -14
  85. package/frontend/src/pages/Brain.tsx +0 -914
  86. package/frontend/src/pages/Capture.tsx +0 -258
  87. package/frontend/src/pages/Library.tsx +0 -486
  88. package/frontend/src/pages/System.tsx +0 -621
  89. package/frontend/src/routes.ts +0 -92
  90. package/frontend/src/store/appStore.ts +0 -94
  91. package/frontend/src/styles.css +0 -6579
  92. package/static/app/assets/Act-DOvf59ru.js +0 -2
  93. package/static/app/assets/Act-DOvf59ru.js.map +0 -1
  94. package/static/app/assets/Brain-C7_0mEiI.js +0 -322
  95. package/static/app/assets/Brain-C7_0mEiI.js.map +0 -1
  96. package/static/app/assets/Capture-B9Tlhzqr.js.map +0 -1
  97. package/static/app/assets/Library-BJPEEm5O.js +0 -2
  98. package/static/app/assets/Library-BJPEEm5O.js.map +0 -1
  99. package/static/app/assets/System-D6t9jo9V.js +0 -2
  100. package/static/app/assets/System-D6t9jo9V.js.map +0 -1
  101. package/static/app/assets/core-CwxXejkd.js.map +0 -1
  102. package/static/app/assets/index-C7g26IF6.css +0 -2
  103. package/static/app/assets/index-DbcEYJQ2.js +0 -17
  104. package/static/app/assets/index-DbcEYJQ2.js.map +0 -1
  105. package/static/app/assets/primitives-CD38lt4n.js +0 -2
  106. package/static/app/assets/primitives-CD38lt4n.js.map +0 -1
  107. package/static/app/assets/textarea-BZk6ybp5.js.map +0 -1
@@ -23,6 +23,10 @@ DEFAULT_EMBEDDING_DIM = 384
23
23
  EMBEDDING_MODEL_ID = f"lattice-local-hash-v1:{DEFAULT_EMBEDDING_DIM}"
24
24
 
25
25
 
26
+ def embedding_model_id(dim: int) -> str:
27
+ return f"lattice-local-hash-v1:{int(dim)}"
28
+
29
+
26
30
  def _tokenize(text: str) -> List[str]:
27
31
  raw = str(text or "").lower()
28
32
  tokens = re.findall(r"[a-z0-9][a-z0-9_.:/+-]{1,}|[가-힣]{2,}", raw)
@@ -57,6 +61,10 @@ class LocalEmbeddingModel:
57
61
  dim: int = DEFAULT_EMBEDDING_DIM
58
62
  model_id: str = EMBEDDING_MODEL_ID
59
63
 
64
+ def __post_init__(self) -> None:
65
+ if self.model_id == EMBEDDING_MODEL_ID and self.dim != DEFAULT_EMBEDDING_DIM:
66
+ object.__setattr__(self, "model_id", embedding_model_id(self.dim))
67
+
60
68
  def embed(self, text: str) -> List[float]:
61
69
  vector = [0.0] * self.dim
62
70
  features = _tokenize(text)
@@ -11,7 +11,7 @@ from copy import deepcopy
11
11
  from typing import Any, Dict, List, Optional
12
12
 
13
13
 
14
- MARKETPLACE_VERSION = "7.9.0"
14
+ MARKETPLACE_VERSION = "8.1.0"
15
15
  TEMPLATE_KINDS = ("plugin", "workflow", "agent")
16
16
 
17
17
 
@@ -6,9 +6,12 @@ Extracted from server.py to reduce module size.
6
6
 
7
7
  import json
8
8
  import logging
9
+ import os
10
+ import subprocess
11
+ import sys
9
12
  from datetime import datetime, timedelta
10
13
  from pathlib import Path
11
- from typing import Dict, List, Optional
14
+ from typing import Callable, Dict, List, Optional
12
15
 
13
16
  import httpx
14
17
  from fastapi import HTTPException
@@ -789,3 +792,139 @@ async def install_skill(plugin: str, skill: str) -> Dict:
789
792
  "license": entry["license"],
790
793
  "author": entry["author"],
791
794
  }
795
+
796
+
797
+ def create_mcp_install_state(data_dir: Path) -> Dict[str, Callable]:
798
+ """Return bound MCP state helpers for given data_dir. No global side effects."""
799
+ MCP_FILE = Path(data_dir) / "mcp_installs.json"
800
+
801
+ def load_mcp_installs() -> Dict:
802
+ if not os.path.exists(MCP_FILE):
803
+ return {"installed": {}, "updated_at": None}
804
+ try:
805
+ with open(MCP_FILE, "r", encoding="utf-8") as f:
806
+ data = json.load(f)
807
+ if "installed" not in data:
808
+ data["installed"] = {}
809
+ return data
810
+ except Exception as e:
811
+ logging.warning("load_mcp_installs failed: %s", e)
812
+ return {"installed": {}, "updated_at": None}
813
+
814
+ def save_mcp_installs(data: Dict) -> None:
815
+ data["updated_at"] = datetime.now().isoformat()
816
+ with open(MCP_FILE, "w", encoding="utf-8") as f:
817
+ json.dump(data, f, ensure_ascii=False, indent=2)
818
+
819
+ def mcp_public_item(item: Dict, installed_state: Dict) -> Dict:
820
+ state = installed_state.get(item.get("id"), {}) or {}
821
+ installed = item.get("install_mode") in {"builtin", "bundled"} or bool(state.get("installed"))
822
+ connector_pending = item.get("install_mode") == "connector" and not state.get("authenticated")
823
+ authenticated = item.get("install_mode") != "connector" or bool(state.get("authenticated"))
824
+ return {
825
+ "id": item.get("id"),
826
+ "name": item.get("name"),
827
+ "category": item.get("category", ""),
828
+ "install_mode": item.get("install_mode"),
829
+ "description": item.get("description", ""),
830
+ "capabilities": item.get("capabilities", []),
831
+ "connector_url": item.get("connector_url"),
832
+ "external_url": item.get("external_url"),
833
+ "package": item.get("package"),
834
+ "homepage": item.get("homepage"),
835
+ "source": item.get("source", "local"),
836
+ "installed": installed,
837
+ "status": state.get("status") or ("active" if installed and not connector_pending else "needs_auth" if connector_pending else "available"),
838
+ "authenticated": authenticated,
839
+ "updated_at": state.get("updated_at"),
840
+ }
841
+
842
+ async def recommend_mcps(query: str, limit: int = 5) -> List[Dict]:
843
+ text = (query or "").lower()
844
+ installed = load_mcp_installs().get("installed", {})
845
+ registry = await _get_combined_registry()
846
+ scored: List[Dict] = []
847
+ for item in registry:
848
+ score = 0
849
+ hits: List[str] = []
850
+ for keyword in item.get("keywords", []):
851
+ if keyword.lower() in text:
852
+ score += 3 if len(keyword) > 2 else 1
853
+ hits.append(keyword)
854
+ if not hits and text:
855
+ desc_words = item.get("description", "").lower().split()
856
+ for word in text.split():
857
+ if len(word) > 2 and word in desc_words:
858
+ score += 1
859
+ hits.append(word)
860
+ if item.get("id") == "filesystem" and any(w in text for w in ["만들", "구현", "build", "deploy", "코드", "앱"]):
861
+ score += 2
862
+ if score:
863
+ public = mcp_public_item(item, installed)
864
+ public["score"] = score
865
+ public["matched_keywords"] = hits[:6]
866
+ scored.append(public)
867
+ if not scored:
868
+ fallback_ids = ["filesystem", "browser", "documents"]
869
+ scored = [
870
+ {**mcp_public_item(item, installed), "score": 1, "matched_keywords": []}
871
+ for item in registry if item.get("id") in fallback_ids
872
+ ]
873
+ return sorted(scored, key=lambda x: x["score"], reverse=True)[:max(1, min(limit, 24))]
874
+
875
+ async def install_mcp(mcp_id: str) -> Dict:
876
+ registry = await _get_combined_registry()
877
+ item = next((entry for entry in registry if entry.get("id") == mcp_id), None)
878
+ if not item:
879
+ raise HTTPException(status_code=404, detail="MCP를 찾을 수 없습니다.")
880
+ data = load_mcp_installs()
881
+ state = data.setdefault("installed", {})
882
+ status = "active"
883
+ message = "MCP가 활성화되었습니다."
884
+ if item.get("install_mode") == "connector":
885
+ status = "needs_auth"
886
+ message = "커넥터 인증이 필요합니다. Codex 앱의 connector 설정에서 계정을 연결하면 바로 사용할 수 있습니다."
887
+ elif item.get("install_mode") == "pip":
888
+ packages = item.get("pip_packages") or []
889
+ for pkg in packages:
890
+ completed = subprocess.run(
891
+ [sys.executable, "-m", "pip", "install", "--upgrade", pkg],
892
+ capture_output=True, text=True, timeout=900, check=False,
893
+ )
894
+ if completed.returncode != 0:
895
+ raise HTTPException(status_code=500, detail=(completed.stderr or "")[-2000:] or f"{pkg} 설치 실패")
896
+ message = f"필수 패키지 설치 완료: {', '.join(packages)}"
897
+ elif item.get("install_mode") == "pypi":
898
+ pkg = item.get("package", "")
899
+ version = item.get("package_version")
900
+ pkg_str = f"{pkg}=={version}" if version else pkg
901
+ completed = subprocess.run([sys.executable, "-m", "pip", "install", pkg_str], capture_output=True, text=True, timeout=300, check=False)
902
+ if completed.returncode != 0:
903
+ raise HTTPException(status_code=500, detail=(completed.stderr or "")[-2000:] or f"{pkg} 설치 실패")
904
+ message = f"pip 패키지 설치 완료: {pkg_str}"
905
+ elif item.get("install_mode") == "npm":
906
+ pkg = item.get("package", "")
907
+ version = item.get("package_version")
908
+ pkg_str = f"{pkg}@{version}" if version else pkg
909
+ completed = subprocess.run(["npm", "install", "-g", pkg_str], capture_output=True, text=True, timeout=300, check=False)
910
+ if completed.returncode != 0:
911
+ raise HTTPException(status_code=500, detail=(completed.stderr or "")[-2000:] or f"{pkg} 설치 실패")
912
+ message = f"npm 패키지 설치 완료: {pkg_str}"
913
+ state[mcp_id] = {
914
+ "installed": True,
915
+ "status": status,
916
+ "authenticated": item.get("install_mode") != "connector",
917
+ "updated_at": datetime.now().isoformat(),
918
+ }
919
+ save_mcp_installs(data)
920
+ public = mcp_public_item(item, state)
921
+ public["message"] = message
922
+ return public
923
+
924
+ return {
925
+ "load_mcp_installs": load_mcp_installs,
926
+ "save_mcp_installs": save_mcp_installs,
927
+ "mcp_public_item": mcp_public_item,
928
+ "recommend_mcps": recommend_mcps,
929
+ "install_mcp": install_mcp,
930
+ }
@@ -292,7 +292,14 @@ class ToolRegistry:
292
292
  })
293
293
  diagnostics = self.diagnostics()
294
294
  return {
295
+ "schema_version": "tool-registry-contract/v1",
295
296
  "status": "ok" if diagnostics["ready"] else "degraded",
297
+ "boundary": {
298
+ "owner": "latticeai.core.tool_registry.ToolRegistry",
299
+ "dispatch_owner": "tools.DEFAULT_TOOL_REGISTRY",
300
+ "policy_owner": "latticeai.core.tool_registry.ToolRegistry",
301
+ "permission_owner": "latticeai.services.tool_dispatch.ToolDispatchService",
302
+ },
296
303
  "catalog_brief": self.catalog_brief.strip(),
297
304
  "diagnostics": diagnostics,
298
305
  "tools": tools,
@@ -21,7 +21,7 @@ from typing import Any, Callable, Dict, Iterable, List, Optional
21
21
  from lattice_brain.runtime.contracts import realtime_event_contract, run_record_contract, workflow_run_contract
22
22
 
23
23
 
24
- WORKSPACE_OS_VERSION = "7.9.0"
24
+ WORKSPACE_OS_VERSION = "8.1.0"
25
25
 
26
26
  # Workspace types separate single-user Personal workspaces from shared
27
27
  # Organization workspaces. Both keep the same local-first JSON store; the type
@@ -1,10 +1,10 @@
1
1
  """Machine-checkable architecture readiness gates for release work.
2
2
 
3
- The 7.7 complete-product line preserves the 7.6 closure of the two local review
4
- notes by keeping their architectural claims in a small contract: AgentRuntime,
5
- ToolRegistry, central Config,
6
- decomposed API routers, and Knowledge Graph portability must all be discoverable
7
- and testable before the release can be called complete.
3
+ 8.1.0 keeps the major architecture priorities under an explicit release
4
+ contract while the product surface is refreshed. AgentRuntime, ToolRegistry,
5
+ central Config, decomposed server runtime, and Knowledge Graph stabilization
6
+ must remain discoverable, ordered, and backed by tests before the release can be
7
+ called complete.
8
8
  """
9
9
 
10
10
  from __future__ import annotations
@@ -15,6 +15,19 @@ from pathlib import Path
15
15
  from typing import Any, Dict, List
16
16
 
17
17
 
18
+ ARCHITECTURE_VERSION_TARGET = "8.1.0"
19
+
20
+ PREFERRED_REFACTORING_ORDER = [
21
+ "agent-runtime",
22
+ "tool-registry",
23
+ "config-centralization",
24
+ "server-decomposition",
25
+ "kg-hardening",
26
+ "documentation-sync",
27
+ "ui-enhancements",
28
+ ]
29
+
30
+
18
31
  @dataclass(frozen=True)
19
32
  class ArchitectureGate:
20
33
  id: str
@@ -108,12 +121,43 @@ def architecture_readiness(root: Path | None = None) -> Dict[str, Any]:
108
121
 
109
122
  api_router_count = len(list((root / "latticeai" / "api").glob("*.py")))
110
123
  runtime_module_count = len(list((root / "latticeai" / "runtime").glob("*.py")))
124
+ ordered_gate_ids = [gate.id for gate in gates]
125
+ contract = {
126
+ "schema_version": "lattice-architecture-contract/v1",
127
+ "version_target": ARCHITECTURE_VERSION_TARGET,
128
+ "refactoring_order": list(PREFERRED_REFACTORING_ORDER),
129
+ "boundaries": {
130
+ "agent-runtime": {
131
+ "owner": "lattice_brain.runtime.agent_runtime.AgentRuntime",
132
+ "surface": "/agents",
133
+ "status": "facade",
134
+ },
135
+ "tool-registry": {
136
+ "owner": "latticeai.core.tool_registry.ToolRegistry",
137
+ "surface": "/tools",
138
+ "status": "registry",
139
+ },
140
+ "config-centralization": {
141
+ "owner": "latticeai.core.config.Config",
142
+ "surface": "composition root",
143
+ "status": "typed-config",
144
+ },
145
+ "kg-hardening": {
146
+ "owner": "lattice_brain.graph.store.KnowledgeGraphStore",
147
+ "strategy": "additive reprojection with legacy read compatibility",
148
+ "rollback": "portable export/import and non-destructive migration paths",
149
+ },
150
+ },
151
+ "ordered_gate_ids": ordered_gate_ids,
152
+ }
111
153
  return {
112
154
  "status": "complete" if all(gate.status == "complete" for gate in gates) else "incomplete",
113
- "version_target": "7.9.0",
155
+ "version_target": ARCHITECTURE_VERSION_TARGET,
156
+ "contract": contract,
114
157
  "gates": [gate.__dict__ for gate in gates],
115
158
  "metrics": {
116
159
  "api_router_modules": api_router_count,
117
160
  "runtime_modules": runtime_module_count,
161
+ "architecture_gates": len(gates),
118
162
  },
119
163
  }
@@ -127,8 +127,42 @@ get_user_api_key = _missing_user_api_key
127
127
 
128
128
 
129
129
  def configure_model_runtime(**deps) -> None:
130
- """Wire app-owned runtime dependencies without importing server_app."""
131
- globals().update({key: value for key, value in deps.items() if key in globals()})
130
+ """Wire app-owned runtime dependencies without importing server_app.
131
+
132
+ Explicit per-key assignment (no blanket globals().update) so wiring is
133
+ auditable and side effects are visible. Preserves exact public module
134
+ globals and prior behavior for all callers and shims.
135
+ """
136
+ global router, APP_MODE, DEFAULT_HOST, DEFAULT_PORT, DATA_DIR, BASE_DIR
137
+ global ENABLE_TELEGRAM, ENABLE_GRAPH, AUTOLOAD_MODELS, MODEL_IDLE_UNLOAD_SECONDS
138
+ global ALLOW_LOCAL_MODELS, REQUIRE_AUTH, INVITE_GATE_ENABLED, ALLOW_PLAINTEXT_API_KEYS
139
+ global CORS_ALLOW_NETWORK, PUBLIC_MODEL, LOCAL_MODEL, IS_PUBLIC_MODE
140
+ global keyring, get_current_user, get_user_api_key
141
+
142
+ router = deps.get("router", router)
143
+ APP_MODE = deps.get("APP_MODE", APP_MODE)
144
+ DEFAULT_HOST = deps.get("DEFAULT_HOST", DEFAULT_HOST)
145
+ DEFAULT_PORT = deps.get("DEFAULT_PORT", DEFAULT_PORT)
146
+ DATA_DIR = deps.get("DATA_DIR", DATA_DIR)
147
+ BASE_DIR = deps.get("BASE_DIR", BASE_DIR)
148
+ ENABLE_TELEGRAM = deps.get("ENABLE_TELEGRAM", ENABLE_TELEGRAM)
149
+ ENABLE_GRAPH = deps.get("ENABLE_GRAPH", ENABLE_GRAPH)
150
+ AUTOLOAD_MODELS = deps.get("AUTOLOAD_MODELS", AUTOLOAD_MODELS)
151
+ MODEL_IDLE_UNLOAD_SECONDS = deps.get("MODEL_IDLE_UNLOAD_SECONDS", MODEL_IDLE_UNLOAD_SECONDS)
152
+ ALLOW_LOCAL_MODELS = deps.get("ALLOW_LOCAL_MODELS", ALLOW_LOCAL_MODELS)
153
+ REQUIRE_AUTH = deps.get("REQUIRE_AUTH", REQUIRE_AUTH)
154
+ INVITE_GATE_ENABLED = deps.get("INVITE_GATE_ENABLED", INVITE_GATE_ENABLED)
155
+ ALLOW_PLAINTEXT_API_KEYS = deps.get("ALLOW_PLAINTEXT_API_KEYS", ALLOW_PLAINTEXT_API_KEYS)
156
+ CORS_ALLOW_NETWORK = deps.get("CORS_ALLOW_NETWORK", CORS_ALLOW_NETWORK)
157
+ PUBLIC_MODEL = deps.get("PUBLIC_MODEL", PUBLIC_MODEL)
158
+ LOCAL_MODEL = deps.get("LOCAL_MODEL", LOCAL_MODEL)
159
+ IS_PUBLIC_MODE = deps.get("IS_PUBLIC_MODE", IS_PUBLIC_MODE)
160
+ if "keyring" in deps:
161
+ keyring = deps["keyring"]
162
+ if "get_current_user" in deps:
163
+ get_current_user = deps["get_current_user"]
164
+ if "get_user_api_key" in deps:
165
+ get_user_api_key = deps["get_user_api_key"]
132
166
 
133
167
 
134
168
  # Catalog data + version-dedup helpers live in ``model_catalog``; re-exported
@@ -1,12 +1,13 @@
1
- """Machine-checkable *product* readiness gates for the 7.9 line.
1
+ """Machine-checkable *product* readiness gates for the 8.1 line.
2
2
 
3
3
  Where ``architecture_readiness`` proves the internal structure is sound, this
4
- module answers the product question the 7.9 release exists to settle: *are the
5
- runtime boundaries still clear and release-ready after hardening?* It does so honestly — every
6
- gate is backed by evidence that is probed on disk, so a gate only reports
7
- ``complete`` when its evidence actually resolves. The same report can be printed
8
- by ``scripts/product_readiness.py`` and re-run after every change, which is the
9
- point: completeness is something we keep measuring, not a one-time claim.
4
+ module answers the product question the 8.1 release exists to settle: *is the
5
+ Brain experience still release-ready after the default surface becomes more
6
+ intuitive?* It does so honestly — every gate is backed by evidence that is
7
+ probed on disk, so a gate only reports ``complete`` when its evidence actually
8
+ resolves. The same report can be printed by ``scripts/product_readiness.py`` and
9
+ re-run after every change, which is the point: completeness is something we keep
10
+ measuring, not a one-time claim.
10
11
  """
11
12
 
12
13
  from __future__ import annotations
@@ -17,7 +18,7 @@ from typing import Any, Dict, List
17
18
 
18
19
  from latticeai.services.architecture_readiness import architecture_readiness
19
20
 
20
- PRODUCT_VERSION_TARGET = "7.9.0"
21
+ PRODUCT_VERSION_TARGET = "8.1.0"
21
22
 
22
23
 
23
24
  @dataclass(frozen=True)
@@ -64,10 +65,10 @@ PRODUCT_GATES: List[ProductGate] = [
64
65
  evidence=[
65
66
  "package.json::release:artifacts",
66
67
  "package.json::release:validate",
67
- "README.md::dist/ltcai-7.9.0-py3-none-any.whl",
68
- "README.md::dist/ltcai-7.9.0.tar.gz",
69
- "README.md::dist/ltcai-7.9.0.vsix",
70
- "README.md::ltcai-7.9.0.tgz",
68
+ "README.md::dist/ltcai-8.1.0-py3-none-any.whl",
69
+ "README.md::dist/ltcai-8.1.0.tar.gz",
70
+ "README.md::dist/ltcai-8.1.0.vsix",
71
+ "README.md::ltcai-8.1.0.tgz",
71
72
  "scripts/validate_release_artifacts.py",
72
73
  "scripts/release_smoke.py",
73
74
  "Dockerfile",
@@ -83,18 +84,19 @@ PRODUCT_GATES: List[ProductGate] = [
83
84
  title="Release story is documented and honest",
84
85
  evidence=[
85
86
  "README.md",
86
- "README.md::The current release is **7.9.0",
87
- "SECURITY.md::7.9.x (latest)",
88
- "vscode-extension/README.md::**7.9.0",
89
- "docs/CHANGELOG.md::## [7.9.0]",
87
+ "README.md::The current release is **8.1.0",
88
+ "SECURITY.md::8.1.x (latest)",
89
+ "vscode-extension/README.md::**8.1.0",
90
+ "docs/CHANGELOG.md::## [8.1.0]",
90
91
  "FEATURE_STATUS.md",
91
- "RELEASE_NOTES_v7.9.0.md",
92
+ "RELEASE_NOTES_v8.1.0.md",
92
93
  "latticeai/core/agent.py::SingleAgentRuntime",
93
94
  "latticeai/core/agent.py::AgentRuntime = SingleAgentRuntime",
94
95
  "lattice_brain/runtime/contracts.py::runtime-boundary/v1",
95
96
  "lattice_brain/runtime/contracts.py::RuntimeBoundaryProtocol",
96
97
  "lattice_brain/runtime/agent_runtime.py::def boundary",
97
98
  "latticeai/core/agent.py::def boundary",
99
+ "latticeai/services/architecture_readiness.py::lattice-architecture-contract/v1",
98
100
  "latticeai/services/tool_dispatch.py::rollback_file",
99
101
  ],
100
102
  ),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ltcai",
3
- "version": "7.9.0",
3
+ "version": "8.1.0",
4
4
  "description": "Lattice AI — local-first Digital Brain that keeps your knowledge durable across any AI model.",
5
5
  "homepage": "https://github.com/TaeSooPark-PTS/LatticeAI#readme",
6
6
  "repository": {
@@ -85,7 +85,6 @@
85
85
  "static/manifest.json",
86
86
  "static/sw.js",
87
87
  "static/css/",
88
- "frontend/",
89
88
  "static/app/",
90
89
  "static/icons/",
91
90
  "plugins/",
@@ -94,6 +93,7 @@
94
93
  "!docs/images/tmp_frames/",
95
94
  "!**/__pycache__/",
96
95
  "!**/*.pyc",
96
+ "!**/*.map",
97
97
  "README.md",
98
98
  "setup_wizard.py",
99
99
  "knowledge_graph_api.py",
@@ -6,6 +6,7 @@ const repo = join(import.meta.dirname, "..");
6
6
  const appDir = join(repo, "static", "app");
7
7
  const nestedViteManifest = join(appDir, ".vite", "asset-manifest.json");
8
8
  const publicManifest = join(appDir, "asset-manifest.json");
9
+ const serviceWorker = join(repo, "static", "sw.js");
9
10
  const pkg = JSON.parse(readFileSync(join(repo, "package.json"), "utf8"));
10
11
 
11
12
  const assetsDir = join(appDir, "assets");
@@ -39,11 +40,19 @@ const manifest = {
39
40
  version: pkg.version,
40
41
  generated_at: "vite",
41
42
  entrypoints: {
42
- app: assets["frontend/index.html"] || "/static/app/index.html",
43
+ app: assets["index.html"] || "/static/app/index.html",
43
44
  },
44
45
  assets,
45
- vite: raw,
46
46
  };
47
47
 
48
48
  writeFileSync(publicManifest, JSON.stringify(manifest, null, 2) + "\n", "utf8");
49
+ if (existsSync(serviceWorker)) {
50
+ const cacheVersion = pkg.version.replace(/\D/g, "");
51
+ const source = readFileSync(serviceWorker, "utf8");
52
+ const normalized = source.replace(
53
+ /const CACHE = "lattice-v[^"]+";/,
54
+ `const CACHE = "lattice-v${cacheVersion}";`,
55
+ );
56
+ if (normalized !== source) writeFileSync(serviceWorker, normalized, "utf8");
57
+ }
49
58
  console.log(`wrote static/app/asset-manifest.json with ${Object.keys(assets).length} assets`);
@@ -1584,7 +1584,7 @@ dependencies = [
1584
1584
 
1585
1585
  [[package]]
1586
1586
  name = "lattice-ai-desktop"
1587
- version = "7.9.0"
1587
+ version = "8.1.0"
1588
1588
  dependencies = [
1589
1589
  "plist",
1590
1590
  "serde",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "lattice-ai-desktop"
3
- version = "7.9.0"
3
+ version = "8.1.0"
4
4
  description = "Lattice AI Digital Brain desktop shell"
5
5
  authors = ["TaeSoo Park"]
6
6
  edition = "2021"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://schema.tauri.app/config/2",
3
3
  "productName": "Lattice AI",
4
- "version": "7.9.0",
4
+ "version": "8.1.0",
5
5
  "identifier": "ai.lattice.desktop",
6
6
  "build": {
7
7
  "beforeDevCommand": "npm run frontend:dev",
@@ -1,110 +1,19 @@
1
1
  {
2
- "version": "7.9.0",
2
+ "version": "8.1.0",
3
3
  "generated_at": "vite",
4
4
  "entrypoints": {
5
- "app": "/static/app/index.html"
5
+ "app": "/static/app/assets/index-Bvv79nre.js"
6
6
  },
7
7
  "assets": {
8
8
  "../node_modules/@tauri-apps/api/core.js": "/static/app/assets/core-CwxXejkd.js",
9
- "_primitives-CD38lt4n.js": "/static/app/assets/primitives-CD38lt4n.js",
10
- "_textarea-BZk6ybp5.js": "/static/app/assets/textarea-BZk6ybp5.js",
11
- "index.html": "/static/app/assets/index-DbcEYJQ2.js",
12
- "assets/index-C7g26IF6.css": "/static/app/assets/index-C7g26IF6.css",
13
- "src/pages/Act.tsx": "/static/app/assets/Act-DOvf59ru.js",
14
- "src/pages/Brain.tsx": "/static/app/assets/Brain-C7_0mEiI.js",
15
- "src/pages/Capture.tsx": "/static/app/assets/Capture-B9Tlhzqr.js",
16
- "src/pages/Library.tsx": "/static/app/assets/Library-BJPEEm5O.js",
17
- "src/pages/System.tsx": "/static/app/assets/System-D6t9jo9V.js"
18
- },
19
- "vite": {
20
- "../node_modules/@tauri-apps/api/core.js": {
21
- "file": "assets/core-CwxXejkd.js",
22
- "name": "core",
23
- "src": "../node_modules/@tauri-apps/api/core.js",
24
- "isDynamicEntry": true
25
- },
26
- "_primitives-CD38lt4n.js": {
27
- "file": "assets/primitives-CD38lt4n.js",
28
- "name": "primitives",
29
- "imports": [
30
- "index.html"
31
- ]
32
- },
33
- "_textarea-BZk6ybp5.js": {
34
- "file": "assets/textarea-BZk6ybp5.js",
35
- "name": "textarea",
36
- "imports": [
37
- "index.html"
38
- ]
39
- },
40
- "index.html": {
41
- "file": "assets/index-DbcEYJQ2.js",
42
- "name": "index",
43
- "src": "index.html",
44
- "isEntry": true,
45
- "dynamicImports": [
46
- "../node_modules/@tauri-apps/api/core.js",
47
- "src/pages/Act.tsx",
48
- "src/pages/Brain.tsx",
49
- "src/pages/Capture.tsx",
50
- "src/pages/Library.tsx",
51
- "src/pages/System.tsx"
52
- ],
53
- "css": [
54
- "assets/index-C7g26IF6.css"
55
- ]
56
- },
57
- "src/pages/Act.tsx": {
58
- "file": "assets/Act-DOvf59ru.js",
59
- "name": "Act",
60
- "src": "src/pages/Act.tsx",
61
- "isDynamicEntry": true,
62
- "imports": [
63
- "index.html",
64
- "_primitives-CD38lt4n.js",
65
- "_textarea-BZk6ybp5.js"
66
- ]
67
- },
68
- "src/pages/Brain.tsx": {
69
- "file": "assets/Brain-C7_0mEiI.js",
70
- "name": "Brain",
71
- "src": "src/pages/Brain.tsx",
72
- "isDynamicEntry": true,
73
- "imports": [
74
- "index.html",
75
- "_primitives-CD38lt4n.js",
76
- "_textarea-BZk6ybp5.js"
77
- ]
78
- },
79
- "src/pages/Capture.tsx": {
80
- "file": "assets/Capture-B9Tlhzqr.js",
81
- "name": "Capture",
82
- "src": "src/pages/Capture.tsx",
83
- "isDynamicEntry": true,
84
- "imports": [
85
- "index.html",
86
- "_primitives-CD38lt4n.js"
87
- ]
88
- },
89
- "src/pages/Library.tsx": {
90
- "file": "assets/Library-BJPEEm5O.js",
91
- "name": "Library",
92
- "src": "src/pages/Library.tsx",
93
- "isDynamicEntry": true,
94
- "imports": [
95
- "index.html",
96
- "_primitives-CD38lt4n.js"
97
- ]
98
- },
99
- "src/pages/System.tsx": {
100
- "file": "assets/System-D6t9jo9V.js",
101
- "name": "System",
102
- "src": "src/pages/System.tsx",
103
- "isDynamicEntry": true,
104
- "imports": [
105
- "index.html",
106
- "_primitives-CD38lt4n.js"
107
- ]
108
- }
9
+ "_primitives-B70WOra0.js": "/static/app/assets/primitives-B70WOra0.js",
10
+ "_textarea-Czrd9gwM.js": "/static/app/assets/textarea-Czrd9gwM.js",
11
+ "index.html": "/static/app/assets/index-Bvv79nre.js",
12
+ "assets/index-Dslqglia.css": "/static/app/assets/index-Dslqglia.css",
13
+ "src/pages/Act.tsx": "/static/app/assets/Act-BOO66G-c.js",
14
+ "src/pages/Brain.tsx": "/static/app/assets/Brain-C6lEYiD7.js",
15
+ "src/pages/Capture.tsx": "/static/app/assets/Capture-TATXBRDw.js",
16
+ "src/pages/Library.tsx": "/static/app/assets/Library-DK4FIp8a.js",
17
+ "src/pages/System.tsx": "/static/app/assets/System-Bgs6Ql7x.js"
109
18
  }
110
19
  }