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.
- package/README.md +35 -49
- package/docs/CHANGELOG.md +35 -0
- package/docs/V3_FRONTEND.md +11 -5
- package/lattice_brain/__init__.py +1 -1
- package/lattice_brain/embeddings.py +9 -1
- package/lattice_brain/graph/projection.py +19 -5
- package/lattice_brain/graph/provenance.py +26 -4
- package/lattice_brain/graph/write_master.py +30 -3
- package/lattice_brain/runtime/multi_agent.py +1 -1
- package/latticeai/__init__.py +1 -1
- package/latticeai/app_factory.py +13 -134
- package/latticeai/core/local_embeddings.py +8 -0
- package/latticeai/core/marketplace.py +1 -1
- package/latticeai/core/mcp_registry.py +140 -1
- package/latticeai/core/tool_registry.py +7 -0
- package/latticeai/core/workspace_os.py +1 -1
- package/latticeai/services/architecture_readiness.py +50 -6
- package/latticeai/services/model_runtime.py +36 -2
- package/latticeai/services/product_readiness.py +19 -17
- package/package.json +2 -2
- package/scripts/build_frontend_assets.mjs +11 -2
- package/src-tauri/Cargo.lock +1 -1
- package/src-tauri/Cargo.toml +1 -1
- package/src-tauri/tauri.conf.json +1 -1
- package/static/app/asset-manifest.json +11 -102
- package/static/app/assets/Act-BOO66G-c.js +1 -0
- package/static/app/assets/Brain-C6lEYiD7.js +321 -0
- package/static/app/assets/{Capture-B9Tlhzqr.js → Capture-TATXBRDw.js} +1 -2
- package/static/app/assets/Library-DK4FIp8a.js +1 -0
- package/static/app/assets/System-Bgs6Ql7x.js +1 -0
- package/static/app/assets/core-CwxXejkd.js +1 -2
- package/static/app/assets/index-Bvv79nre.js +16 -0
- package/static/app/assets/index-Dslqglia.css +2 -0
- package/static/app/assets/primitives-B70WOra0.js +1 -0
- package/static/app/assets/{textarea-BZk6ybp5.js → textarea-Czrd9gwM.js} +1 -2
- package/static/app/index.html +3 -10
- package/static/app/theme-boot.js +8 -0
- package/static/sw.js +1 -1
- package/frontend/index.html +0 -24
- package/frontend/openapi.json +0 -15425
- package/frontend/src/App.tsx +0 -243
- package/frontend/src/api/client.ts +0 -580
- package/frontend/src/api/openapi.ts +0 -17892
- package/frontend/src/components/AdminAccessGate.tsx +0 -70
- package/frontend/src/components/FeedbackState.tsx +0 -45
- package/frontend/src/components/LanguageSwitcher.tsx +0 -23
- package/frontend/src/components/LivingBrain.tsx +0 -220
- package/frontend/src/components/ProductFlow.tsx +0 -171
- package/frontend/src/components/WorkspaceProfileSwitcher.tsx +0 -176
- package/frontend/src/components/onboarding/AnalysisScreen.tsx +0 -135
- package/frontend/src/components/onboarding/DownloadConsentPanel.tsx +0 -29
- package/frontend/src/components/onboarding/InstallScreen.tsx +0 -263
- package/frontend/src/components/onboarding/LanguageChooser.tsx +0 -23
- package/frontend/src/components/onboarding/LoginScreen.tsx +0 -131
- package/frontend/src/components/onboarding/ProductFlowScreens.tsx +0 -13
- package/frontend/src/components/onboarding/RecommendationScreen.tsx +0 -116
- package/frontend/src/components/onboarding/recommendationModel.ts +0 -189
- package/frontend/src/components/primitives.tsx +0 -392
- package/frontend/src/components/ui/badge.tsx +0 -27
- package/frontend/src/components/ui/button.tsx +0 -37
- package/frontend/src/components/ui/card.tsx +0 -22
- package/frontend/src/components/ui/input.tsx +0 -16
- package/frontend/src/components/ui/textarea.tsx +0 -16
- package/frontend/src/features/admin/AdminConsole.tsx +0 -334
- package/frontend/src/features/brain/BrainCarePanel.tsx +0 -254
- package/frontend/src/features/brain/BrainComposer.tsx +0 -85
- package/frontend/src/features/brain/BrainConversation.tsx +0 -688
- package/frontend/src/features/brain/BrainGraphLayer.tsx +0 -365
- package/frontend/src/features/brain/BrainHome.tsx +0 -624
- package/frontend/src/features/brain/BrainMemoryLayer.tsx +0 -45
- package/frontend/src/features/brain/BrainOverviewPanel.tsx +0 -149
- package/frontend/src/features/brain/BrainRelationshipLayer.tsx +0 -43
- package/frontend/src/features/brain/DepthEmergence.tsx +0 -53
- package/frontend/src/features/brain/brainData.ts +0 -246
- package/frontend/src/features/brain/graphLayout.ts +0 -37
- package/frontend/src/features/brain/types.ts +0 -150
- package/frontend/src/features/review/ReviewCard.tsx +0 -100
- package/frontend/src/features/review/ReviewInbox.tsx +0 -127
- package/frontend/src/features/review/reviewHelpers.ts +0 -69
- package/frontend/src/i18n.ts +0 -1303
- package/frontend/src/lib/utils.ts +0 -33
- package/frontend/src/main.tsx +0 -23
- package/frontend/src/pages/Act.tsx +0 -458
- package/frontend/src/pages/Ask.tsx +0 -14
- package/frontend/src/pages/Brain.tsx +0 -914
- package/frontend/src/pages/Capture.tsx +0 -258
- package/frontend/src/pages/Library.tsx +0 -486
- package/frontend/src/pages/System.tsx +0 -621
- package/frontend/src/routes.ts +0 -92
- package/frontend/src/store/appStore.ts +0 -94
- package/frontend/src/styles.css +0 -6579
- package/static/app/assets/Act-DOvf59ru.js +0 -2
- package/static/app/assets/Act-DOvf59ru.js.map +0 -1
- package/static/app/assets/Brain-C7_0mEiI.js +0 -322
- package/static/app/assets/Brain-C7_0mEiI.js.map +0 -1
- package/static/app/assets/Capture-B9Tlhzqr.js.map +0 -1
- package/static/app/assets/Library-BJPEEm5O.js +0 -2
- package/static/app/assets/Library-BJPEEm5O.js.map +0 -1
- package/static/app/assets/System-D6t9jo9V.js +0 -2
- package/static/app/assets/System-D6t9jo9V.js.map +0 -1
- package/static/app/assets/core-CwxXejkd.js.map +0 -1
- package/static/app/assets/index-C7g26IF6.css +0 -2
- package/static/app/assets/index-DbcEYJQ2.js +0 -17
- package/static/app/assets/index-DbcEYJQ2.js.map +0 -1
- package/static/app/assets/primitives-CD38lt4n.js +0 -2
- package/static/app/assets/primitives-CD38lt4n.js.map +0 -1
- 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)
|
|
@@ -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 = "
|
|
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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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":
|
|
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
|
-
|
|
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
|
|
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
|
|
5
|
-
|
|
6
|
-
gate is backed by evidence that is
|
|
7
|
-
``complete`` when its evidence actually
|
|
8
|
-
by ``scripts/product_readiness.py`` and
|
|
9
|
-
point: completeness is something we keep
|
|
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 = "
|
|
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-
|
|
68
|
-
"README.md::dist/ltcai-
|
|
69
|
-
"README.md::dist/ltcai-
|
|
70
|
-
"README.md::ltcai-
|
|
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 **
|
|
87
|
-
"SECURITY.md::
|
|
88
|
-
"vscode-extension/README.md::**
|
|
89
|
-
"docs/CHANGELOG.md::## [
|
|
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
|
-
"
|
|
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": "
|
|
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["
|
|
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`);
|
package/src-tauri/Cargo.lock
CHANGED
package/src-tauri/Cargo.toml
CHANGED
|
@@ -1,110 +1,19 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "
|
|
2
|
+
"version": "8.1.0",
|
|
3
3
|
"generated_at": "vite",
|
|
4
4
|
"entrypoints": {
|
|
5
|
-
"app": "/static/app/index.
|
|
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-
|
|
10
|
-
"_textarea-
|
|
11
|
-
"index.html": "/static/app/assets/index-
|
|
12
|
-
"assets/index-
|
|
13
|
-
"src/pages/Act.tsx": "/static/app/assets/Act-
|
|
14
|
-
"src/pages/Brain.tsx": "/static/app/assets/Brain-
|
|
15
|
-
"src/pages/Capture.tsx": "/static/app/assets/Capture-
|
|
16
|
-
"src/pages/Library.tsx": "/static/app/assets/Library-
|
|
17
|
-
"src/pages/System.tsx": "/static/app/assets/System-
|
|
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
|
}
|