ltcai 6.7.0 → 7.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 (65) hide show
  1. package/README.md +29 -31
  2. package/docs/CHANGELOG.md +48 -0
  3. package/frontend/src/App.tsx +80 -0
  4. package/frontend/src/api/client.ts +11 -0
  5. package/frontend/src/components/AdminAccessGate.tsx +70 -0
  6. package/frontend/src/components/BrainConversation.tsx +232 -8
  7. package/frontend/src/components/FeedbackState.tsx +45 -0
  8. package/frontend/src/components/WorkspaceProfileSwitcher.tsx +176 -0
  9. package/frontend/src/components/onboarding/AnalysisScreen.tsx +20 -12
  10. package/frontend/src/components/onboarding/InstallScreen.tsx +55 -0
  11. package/frontend/src/components/onboarding/RecommendationScreen.tsx +55 -5
  12. package/frontend/src/components/onboarding/recommendationModel.ts +59 -2
  13. package/frontend/src/features/brain/BrainConversation.tsx +382 -7
  14. package/frontend/src/features/brain/BrainGraphLayer.tsx +258 -25
  15. package/frontend/src/features/brain/BrainHome.tsx +300 -1
  16. package/frontend/src/features/brain/brainData.ts +25 -1
  17. package/frontend/src/features/brain/graphLayout.ts +12 -1
  18. package/frontend/src/features/brain/types.ts +54 -0
  19. package/frontend/src/i18n.ts +374 -0
  20. package/frontend/src/store/appStore.ts +15 -0
  21. package/frontend/src/styles.css +1383 -41
  22. package/lattice_brain/__init__.py +1 -1
  23. package/lattice_brain/runtime/multi_agent.py +1 -1
  24. package/latticeai/__init__.py +1 -1
  25. package/latticeai/api/workspace.py +59 -0
  26. package/latticeai/core/marketplace.py +1 -1
  27. package/latticeai/core/workspace_os.py +1 -1
  28. package/package.json +1 -1
  29. package/scripts/brain_quality_eval.py +118 -0
  30. package/src-tauri/Cargo.lock +1 -1
  31. package/src-tauri/Cargo.toml +1 -1
  32. package/src-tauri/tauri.conf.json +1 -1
  33. package/static/app/asset-manifest.json +28 -28
  34. package/static/app/assets/Act-BHEb8bAM.js +2 -0
  35. package/static/app/assets/{Act-DGN37eR4.js.map → Act-BHEb8bAM.js.map} +1 -1
  36. package/static/app/assets/Brain-yvR7xkrV.js +322 -0
  37. package/static/app/assets/Brain-yvR7xkrV.js.map +1 -0
  38. package/static/app/assets/Capture-BrAmsSEH.js +2 -0
  39. package/static/app/assets/Capture-BrAmsSEH.js.map +1 -0
  40. package/static/app/assets/Library-BOVzYfxI.js +2 -0
  41. package/static/app/assets/{Library-BbagRFyd.js.map → Library-BOVzYfxI.js.map} +1 -1
  42. package/static/app/assets/System-D4WaN4kj.js +2 -0
  43. package/static/app/assets/System-D4WaN4kj.js.map +1 -0
  44. package/static/app/assets/index-BOB-W1FZ.js +17 -0
  45. package/static/app/assets/index-BOB-W1FZ.js.map +1 -0
  46. package/static/app/assets/index-DvLsGy-z.css +2 -0
  47. package/static/app/assets/primitives-C3_BfUb8.js +2 -0
  48. package/static/app/assets/primitives-C3_BfUb8.js.map +1 -0
  49. package/static/app/assets/textarea-CbvhHvzg.js +2 -0
  50. package/static/app/assets/{textarea-C2PLu6_O.js.map → textarea-CbvhHvzg.js.map} +1 -1
  51. package/static/app/index.html +2 -2
  52. package/static/app/assets/Act-DGN37eR4.js +0 -2
  53. package/static/app/assets/Brain-HDcIwPHW.js +0 -322
  54. package/static/app/assets/Brain-HDcIwPHW.js.map +0 -1
  55. package/static/app/assets/Capture--eZfb_Ex.js +0 -2
  56. package/static/app/assets/Capture--eZfb_Ex.js.map +0 -1
  57. package/static/app/assets/Library-BbagRFyd.js +0 -2
  58. package/static/app/assets/System-C6pd3Mp_.js +0 -2
  59. package/static/app/assets/System-C6pd3Mp_.js.map +0 -1
  60. package/static/app/assets/index-C950aES_.js +0 -17
  61. package/static/app/assets/index-C950aES_.js.map +0 -1
  62. package/static/app/assets/index-ztppMg0c.css +0 -2
  63. package/static/app/assets/primitives-Bcm5lZRu.js +0 -2
  64. package/static/app/assets/primitives-Bcm5lZRu.js.map +0 -1
  65. package/static/app/assets/textarea-C2PLu6_O.js +0 -2
@@ -26,7 +26,7 @@ from .storage import (
26
26
  storage_from_env,
27
27
  )
28
28
 
29
- __version__ = "6.7.0"
29
+ __version__ = "7.1.0"
30
30
 
31
31
  __all__ = [
32
32
  "AgentRuntime",
@@ -19,7 +19,7 @@ from datetime import datetime
19
19
  from typing import Any, Callable, Dict, List, Optional
20
20
 
21
21
 
22
- MULTI_AGENT_VERSION = "6.7.0"
22
+ MULTI_AGENT_VERSION = "7.1.0"
23
23
 
24
24
  AGENT_ROLES = ("researcher", "planner", "executor", "reviewer", "release")
25
25
  CORE_PIPELINE = ("planner", "executor", "reviewer")
@@ -1,3 +1,3 @@
1
1
  """Lattice AI - modular server package."""
2
2
 
3
- __version__ = "6.7.0"
3
+ __version__ = "7.1.0"
@@ -99,6 +99,25 @@ class WorkspaceVSCodeRequest(BaseModel):
99
99
  content: str = ""
100
100
  selection: str = ""
101
101
  prompt: str = ""
102
+ extension_version: str = ""
103
+ workspace_folder: str = ""
104
+
105
+
106
+ class WorkspaceVSCodeStatusRequest(BaseModel):
107
+ status: str = "connected"
108
+ index_status: str = "unknown"
109
+ workspace_folder: str = ""
110
+ extension_version: str = ""
111
+ active_file: str = ""
112
+ detail: str = ""
113
+
114
+
115
+ _VSCODE_STATUS: Dict[str, object] = {
116
+ "connected": False,
117
+ "status": "offline",
118
+ "index_status": "unknown",
119
+ "last_seen_ms": 0,
120
+ }
102
121
 
103
122
 
104
123
  class WorkspaceCreateRequest(BaseModel):
@@ -630,9 +649,49 @@ def create_workspace_router(context: AppContext) -> APIRouter:
630
649
 
631
650
  # ── VS Code workflow bridge ───────────────────────────────────────────
632
651
 
652
+ @router.get("/workspace/vscode/status")
653
+ async def workspace_vscode_status(request: Request):
654
+ require_user(request)
655
+ last_seen = int(_VSCODE_STATUS.get("last_seen_ms") or 0)
656
+ now_ms = int(datetime.utcnow().timestamp() * 1000)
657
+ connected = last_seen > 0 and (now_ms - last_seen) < 60000
658
+ return {
659
+ **_VSCODE_STATUS,
660
+ "connected": connected,
661
+ "status": _VSCODE_STATUS.get("status") if connected else "offline",
662
+ }
663
+
664
+ @router.post("/workspace/vscode/status")
665
+ async def workspace_vscode_status_update(req: WorkspaceVSCodeStatusRequest, request: Request):
666
+ current_user = require_user(request)
667
+ now_ms = int(datetime.utcnow().timestamp() * 1000)
668
+ _VSCODE_STATUS.update({
669
+ "connected": True,
670
+ "status": req.status or "connected",
671
+ "index_status": req.index_status or "unknown",
672
+ "workspace_folder": req.workspace_folder,
673
+ "extension_version": req.extension_version,
674
+ "active_file": req.active_file,
675
+ "detail": req.detail,
676
+ "last_seen_ms": now_ms,
677
+ "user_email": current_user,
678
+ })
679
+ return {"status": "ok", **_VSCODE_STATUS}
680
+
633
681
  @router.post("/workspace/vscode/send")
634
682
  async def workspace_vscode_send(req: WorkspaceVSCodeRequest, request: Request):
635
683
  current_user = require_user(request)
684
+ now_ms = int(datetime.utcnow().timestamp() * 1000)
685
+ _VSCODE_STATUS.update({
686
+ "connected": True,
687
+ "status": "synced",
688
+ "index_status": "synced",
689
+ "workspace_folder": req.workspace_folder,
690
+ "extension_version": req.extension_version,
691
+ "active_file": req.file_path or "",
692
+ "last_seen_ms": now_ms,
693
+ "user_email": current_user,
694
+ })
636
695
  content = req.selection or req.content or req.prompt
637
696
  workflow = WORKSPACE_OS.create_workflow(
638
697
  name=f"VS Code: {req.action}",
@@ -11,7 +11,7 @@ from copy import deepcopy
11
11
  from typing import Any, Dict, List, Optional
12
12
 
13
13
 
14
- MARKETPLACE_VERSION = "6.7.0"
14
+ MARKETPLACE_VERSION = "7.1.0"
15
15
  TEMPLATE_KINDS = ("plugin", "workflow", "agent")
16
16
 
17
17
 
@@ -19,7 +19,7 @@ from pathlib import Path
19
19
  from typing import Any, Callable, Dict, Iterable, List, Optional
20
20
 
21
21
 
22
- WORKSPACE_OS_VERSION = "6.7.0"
22
+ WORKSPACE_OS_VERSION = "7.1.0"
23
23
 
24
24
  # Workspace types separate single-user Personal workspaces from shared
25
25
  # Organization workspaces. Both keep the same local-first JSON store; the type
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ltcai",
3
- "version": "6.7.0",
3
+ "version": "7.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": {
@@ -0,0 +1,118 @@
1
+ #!/usr/bin/env python3
2
+ """Deterministic Brain recall/KG quality gate for CI.
3
+
4
+ This is intentionally small and fixture-driven. It guards the product promise
5
+ that the first Brain loop can save context, recall it with source evidence, and
6
+ show model-independent proof without relying on a live model.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import sys
12
+ import tempfile
13
+ from pathlib import Path
14
+
15
+ from latticeai.services.memory_service import MemoryService
16
+
17
+
18
+ class _EvalStore:
19
+ def __init__(self) -> None:
20
+ self.rows = [
21
+ {
22
+ "id": "decision:pricing",
23
+ "kind": "decision",
24
+ "content": "Use local-first memory as the product wedge for early access customers.",
25
+ "workspace_id": "personal",
26
+ "tags": ["strategy"],
27
+ },
28
+ {
29
+ "id": "note:first-loop",
30
+ "kind": "note",
31
+ "content": "The first five minute loop is add source, ask question, see Brain proof.",
32
+ "workspace_id": "personal",
33
+ "tags": ["onboarding"],
34
+ },
35
+ ]
36
+
37
+ def list_memories(self, user_email=None, kind=None, workspace_id=None):
38
+ rows = [row for row in self.rows if kind is None or row["kind"] == kind]
39
+ if workspace_id is not None:
40
+ rows = [row for row in rows if (row.get("workspace_id") or "personal") == workspace_id]
41
+ return {"memories": rows}
42
+
43
+ def list_memory_snapshots(self, workspace_id=None, limit=50):
44
+ return {"snapshots": [{"snapshot_id": "eval-snapshot"}]}
45
+
46
+ def search_memories(self, q, user_email=None, limit=20, workspace_id=None):
47
+ terms = {term for term in (q or "").lower().split() if len(term) > 2}
48
+ rows = [
49
+ row for row in self.rows
50
+ if (workspace_id is None or (row.get("workspace_id") or "personal") == workspace_id)
51
+ and terms.intersection(row["content"].lower().split())
52
+ ]
53
+ return {"memories": rows[:limit]}
54
+
55
+
56
+ class _EvalGraph:
57
+ def stats(self):
58
+ return {"nodes": {"Document": 1, "Decision": 1, "Topic": 1}, "edges": {"mentions": 2}}
59
+
60
+ def index_status(self):
61
+ return {"vector_counts": {"node": 3, "chunk": 2}}
62
+
63
+ def search(self, q, limit=20):
64
+ return {
65
+ "matches": [
66
+ {
67
+ "id": "doc:first-loop",
68
+ "type": "Document",
69
+ "title": "First Brain Loop",
70
+ "summary": "Users add a source, ask a question, and see proof with citations.",
71
+ "score": 0.94,
72
+ "metadata": {"source": "eval:first-loop.md", "workspace_id": "personal"},
73
+ }
74
+ ][:limit]
75
+ }
76
+
77
+
78
+ def _fail(message: str) -> int:
79
+ print(f"brain-quality-eval: FAIL: {message}", file=sys.stderr)
80
+ return 1
81
+
82
+
83
+ def main() -> int:
84
+ with tempfile.TemporaryDirectory() as tmp:
85
+ service = MemoryService(
86
+ store=_EvalStore(),
87
+ data_dir=Path(tmp),
88
+ knowledge_graph=_EvalGraph(),
89
+ enable_graph=True,
90
+ )
91
+ proof = service.brain_proof(
92
+ recall_query="first five minute Brain proof",
93
+ user_email="eval@example.com",
94
+ workspace_id="personal",
95
+ active_model="eval-model-a",
96
+ limit=4,
97
+ )
98
+
99
+ recall_items = proof.get("recall", {}).get("items", [])
100
+ if proof.get("model_continuity", {}).get("proven") is not True:
101
+ return _fail("model continuity proof must be true when durable evidence exists")
102
+ if proof.get("claims", {}).get("keeps_context_across_models") is not True:
103
+ return _fail("model-independent context claim must be backed by durable evidence")
104
+ if not recall_items:
105
+ return _fail("recall must return at least one memory or KG item")
106
+ if not any(str(item.get("source") or "").lower() in {"workspace", "graph"} for item in recall_items):
107
+ return _fail("recall items must expose a source for citation UI")
108
+ if proof.get("proofs", {}).get("graph_concepts", 0) < 1:
109
+ return _fail("KG concepts must be counted in Brain proof")
110
+ if proof.get("proofs", {}).get("vector_items", 0) < 1:
111
+ return _fail("vector items must be counted in Brain proof")
112
+
113
+ print("brain-quality-eval: OK")
114
+ return 0
115
+
116
+
117
+ if __name__ == "__main__":
118
+ raise SystemExit(main())
@@ -1654,7 +1654,7 @@ dependencies = [
1654
1654
 
1655
1655
  [[package]]
1656
1656
  name = "lattice-ai-desktop"
1657
- version = "6.7.0"
1657
+ version = "7.1.0"
1658
1658
  dependencies = [
1659
1659
  "plist",
1660
1660
  "serde",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "lattice-ai-desktop"
3
- version = "6.7.0"
3
+ version = "7.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": "6.7.0",
4
+ "version": "7.1.0",
5
5
  "identifier": "ai.lattice.desktop",
6
6
  "build": {
7
7
  "beforeDevCommand": "npm run frontend:dev",
@@ -1,20 +1,20 @@
1
1
  {
2
- "version": "6.7.0",
2
+ "version": "7.1.0",
3
3
  "generated_at": "vite",
4
4
  "entrypoints": {
5
5
  "app": "/static/app/index.html"
6
6
  },
7
7
  "assets": {
8
8
  "../node_modules/@tauri-apps/api/core.js": "/static/app/assets/core-CwxXejkd.js",
9
- "_primitives-Bcm5lZRu.js": "/static/app/assets/primitives-Bcm5lZRu.js",
10
- "_textarea-C2PLu6_O.js": "/static/app/assets/textarea-C2PLu6_O.js",
11
- "index.html": "/static/app/assets/index-C950aES_.js",
12
- "assets/index-ztppMg0c.css": "/static/app/assets/index-ztppMg0c.css",
13
- "src/pages/Act.tsx": "/static/app/assets/Act-DGN37eR4.js",
14
- "src/pages/Brain.tsx": "/static/app/assets/Brain-HDcIwPHW.js",
15
- "src/pages/Capture.tsx": "/static/app/assets/Capture--eZfb_Ex.js",
16
- "src/pages/Library.tsx": "/static/app/assets/Library-BbagRFyd.js",
17
- "src/pages/System.tsx": "/static/app/assets/System-C6pd3Mp_.js"
9
+ "_primitives-C3_BfUb8.js": "/static/app/assets/primitives-C3_BfUb8.js",
10
+ "_textarea-CbvhHvzg.js": "/static/app/assets/textarea-CbvhHvzg.js",
11
+ "index.html": "/static/app/assets/index-BOB-W1FZ.js",
12
+ "assets/index-DvLsGy-z.css": "/static/app/assets/index-DvLsGy-z.css",
13
+ "src/pages/Act.tsx": "/static/app/assets/Act-BHEb8bAM.js",
14
+ "src/pages/Brain.tsx": "/static/app/assets/Brain-yvR7xkrV.js",
15
+ "src/pages/Capture.tsx": "/static/app/assets/Capture-BrAmsSEH.js",
16
+ "src/pages/Library.tsx": "/static/app/assets/Library-BOVzYfxI.js",
17
+ "src/pages/System.tsx": "/static/app/assets/System-D4WaN4kj.js"
18
18
  },
19
19
  "vite": {
20
20
  "../node_modules/@tauri-apps/api/core.js": {
@@ -23,22 +23,22 @@
23
23
  "src": "../node_modules/@tauri-apps/api/core.js",
24
24
  "isDynamicEntry": true
25
25
  },
26
- "_primitives-Bcm5lZRu.js": {
27
- "file": "assets/primitives-Bcm5lZRu.js",
26
+ "_primitives-C3_BfUb8.js": {
27
+ "file": "assets/primitives-C3_BfUb8.js",
28
28
  "name": "primitives",
29
29
  "imports": [
30
30
  "index.html"
31
31
  ]
32
32
  },
33
- "_textarea-C2PLu6_O.js": {
34
- "file": "assets/textarea-C2PLu6_O.js",
33
+ "_textarea-CbvhHvzg.js": {
34
+ "file": "assets/textarea-CbvhHvzg.js",
35
35
  "name": "textarea",
36
36
  "imports": [
37
37
  "index.html"
38
38
  ]
39
39
  },
40
40
  "index.html": {
41
- "file": "assets/index-C950aES_.js",
41
+ "file": "assets/index-BOB-W1FZ.js",
42
42
  "name": "index",
43
43
  "src": "index.html",
44
44
  "isEntry": true,
@@ -51,59 +51,59 @@
51
51
  "src/pages/System.tsx"
52
52
  ],
53
53
  "css": [
54
- "assets/index-ztppMg0c.css"
54
+ "assets/index-DvLsGy-z.css"
55
55
  ]
56
56
  },
57
57
  "src/pages/Act.tsx": {
58
- "file": "assets/Act-DGN37eR4.js",
58
+ "file": "assets/Act-BHEb8bAM.js",
59
59
  "name": "Act",
60
60
  "src": "src/pages/Act.tsx",
61
61
  "isDynamicEntry": true,
62
62
  "imports": [
63
63
  "index.html",
64
- "_primitives-Bcm5lZRu.js",
65
- "_textarea-C2PLu6_O.js"
64
+ "_primitives-C3_BfUb8.js",
65
+ "_textarea-CbvhHvzg.js"
66
66
  ]
67
67
  },
68
68
  "src/pages/Brain.tsx": {
69
- "file": "assets/Brain-HDcIwPHW.js",
69
+ "file": "assets/Brain-yvR7xkrV.js",
70
70
  "name": "Brain",
71
71
  "src": "src/pages/Brain.tsx",
72
72
  "isDynamicEntry": true,
73
73
  "imports": [
74
74
  "index.html",
75
- "_primitives-Bcm5lZRu.js",
76
- "_textarea-C2PLu6_O.js"
75
+ "_primitives-C3_BfUb8.js",
76
+ "_textarea-CbvhHvzg.js"
77
77
  ]
78
78
  },
79
79
  "src/pages/Capture.tsx": {
80
- "file": "assets/Capture--eZfb_Ex.js",
80
+ "file": "assets/Capture-BrAmsSEH.js",
81
81
  "name": "Capture",
82
82
  "src": "src/pages/Capture.tsx",
83
83
  "isDynamicEntry": true,
84
84
  "imports": [
85
85
  "index.html",
86
- "_primitives-Bcm5lZRu.js"
86
+ "_primitives-C3_BfUb8.js"
87
87
  ]
88
88
  },
89
89
  "src/pages/Library.tsx": {
90
- "file": "assets/Library-BbagRFyd.js",
90
+ "file": "assets/Library-BOVzYfxI.js",
91
91
  "name": "Library",
92
92
  "src": "src/pages/Library.tsx",
93
93
  "isDynamicEntry": true,
94
94
  "imports": [
95
95
  "index.html",
96
- "_primitives-Bcm5lZRu.js"
96
+ "_primitives-C3_BfUb8.js"
97
97
  ]
98
98
  },
99
99
  "src/pages/System.tsx": {
100
- "file": "assets/System-C6pd3Mp_.js",
100
+ "file": "assets/System-D4WaN4kj.js",
101
101
  "name": "System",
102
102
  "src": "src/pages/System.tsx",
103
103
  "isDynamicEntry": true,
104
104
  "imports": [
105
105
  "index.html",
106
- "_primitives-Bcm5lZRu.js"
106
+ "_primitives-C3_BfUb8.js"
107
107
  ]
108
108
  }
109
109
  }