ltcai 7.1.0 → 7.3.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 (44) hide show
  1. package/README.md +26 -19
  2. package/docs/CHANGELOG.md +41 -0
  3. package/docs/ROADMAP_RECOMMENDATIONS.md +55 -0
  4. package/frontend/src/api/client.ts +3 -0
  5. package/frontend/src/features/admin/AdminConsole.tsx +41 -1
  6. package/frontend/src/i18n.ts +24 -0
  7. package/frontend/src/styles.css +45 -0
  8. package/lattice_brain/__init__.py +1 -1
  9. package/lattice_brain/runtime/agent_runtime.py +62 -1
  10. package/lattice_brain/runtime/contracts.py +107 -0
  11. package/lattice_brain/runtime/multi_agent.py +6 -2
  12. package/latticeai/__init__.py +1 -1
  13. package/latticeai/api/agents.py +12 -0
  14. package/latticeai/api/tools.py +14 -0
  15. package/latticeai/core/agent.py +5 -0
  16. package/latticeai/core/marketplace.py +1 -1
  17. package/latticeai/core/tool_registry.py +47 -0
  18. package/latticeai/core/workspace_os.py +1 -1
  19. package/latticeai/services/tool_dispatch.py +14 -0
  20. package/package.json +1 -1
  21. package/scripts/brain_quality_eval.py +24 -1
  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 +28 -28
  26. package/static/app/assets/{Act-BHEb8bAM.js → Act-Di4tRFWY.js} +2 -2
  27. package/static/app/assets/{Act-BHEb8bAM.js.map → Act-Di4tRFWY.js.map} +1 -1
  28. package/static/app/assets/{Brain-yvR7xkrV.js → Brain-BZB3Gy9w.js} +2 -2
  29. package/static/app/assets/{Brain-yvR7xkrV.js.map → Brain-BZB3Gy9w.js.map} +1 -1
  30. package/static/app/assets/{Capture-BrAmsSEH.js → Capture-tNyYWxnh.js} +2 -2
  31. package/static/app/assets/{Capture-BrAmsSEH.js.map → Capture-tNyYWxnh.js.map} +1 -1
  32. package/static/app/assets/{Library-BOVzYfxI.js → Library-DAtDDLdg.js} +2 -2
  33. package/static/app/assets/{Library-BOVzYfxI.js.map → Library-DAtDDLdg.js.map} +1 -1
  34. package/static/app/assets/{System-D4WaN4kj.js → System-DEu0xNUc.js} +2 -2
  35. package/static/app/assets/{System-D4WaN4kj.js.map → System-DEu0xNUc.js.map} +1 -1
  36. package/static/app/assets/{index-DvLsGy-z.css → index-Bi_bpigM.css} +1 -1
  37. package/static/app/assets/{index-BOB-W1FZ.js → index-COuGp7_5.js} +3 -3
  38. package/static/app/assets/index-COuGp7_5.js.map +1 -0
  39. package/static/app/assets/{primitives-C3_BfUb8.js → primitives-CdwcE--L.js} +2 -2
  40. package/static/app/assets/{primitives-C3_BfUb8.js.map → primitives-CdwcE--L.js.map} +1 -1
  41. package/static/app/assets/{textarea-CbvhHvzg.js → textarea-CqOdBPL1.js} +2 -2
  42. package/static/app/assets/{textarea-CbvhHvzg.js.map → textarea-CqOdBPL1.js.map} +1 -1
  43. package/static/app/index.html +2 -2
  44. package/static/app/assets/index-BOB-W1FZ.js.map +0 -1
@@ -30,6 +30,7 @@ from pathlib import Path
30
30
  from typing import Any, Awaitable, Callable, Dict, FrozenSet, List, Optional
31
31
 
32
32
  from lattice_brain.runtime.hooks import dispatch_tool
33
+ from lattice_brain.runtime.contracts import single_agent_contract
33
34
  from tools import ToolError
34
35
 
35
36
 
@@ -142,6 +143,10 @@ class AgentRuntime:
142
143
  def __init__(self, deps: AgentDeps) -> None:
143
144
  self.deps = deps
144
145
 
146
+ def contract(self, ctx: AgentRunContext, req: Any, *, run_id: Optional[str] = None) -> Dict[str, Any]:
147
+ """Expose the shared agent-run contract for the single-agent loop."""
148
+ return single_agent_contract(ctx=ctx, goal=getattr(req, "message", ""), run_id=run_id)
149
+
145
150
  # ── PLAN ─────────────────────────────────────────────────────────
146
151
  async def plan(
147
152
  self, ctx: AgentRunContext, req: Any, lang_hint: str, current_user: str,
@@ -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.1.0"
14
+ MARKETPLACE_VERSION = "7.3.0"
15
15
  TEMPLATE_KINDS = ("plugin", "workflow", "agent")
16
16
 
17
17
 
@@ -130,6 +130,7 @@ TOOL_GOVERNANCE: Dict[str, ToolPolicy] = {
130
130
  "todo_read": _r(),
131
131
  "local_list": _r(sandbox="home"),
132
132
  "local_read": _r(sandbox="home"),
133
+ "read_document": _r(sandbox="home"),
133
134
  "git_status": _rs(),
134
135
  "git_diff": _rs(),
135
136
  "git_log": _rs(),
@@ -189,6 +190,7 @@ MCP_TOOL_DESCRIPTIONS: Dict[str, str] = {
189
190
  "clear_history": "Clear chat history to reduce context and speed up responses.",
190
191
  "inspect_html": "Inspect local HTML structure and assets.",
191
192
  "preview_url": "Return a server URL for a workspace file.",
193
+ "create_web_project": "Create a web project scaffold inside the workspace.",
192
194
  "create_docx": "Create a Word DOCX document in the agent workspace.",
193
195
  "create_xlsx": "Create an XLSX spreadsheet in the agent workspace.",
194
196
  "create_pptx": "Create a PPTX presentation deck in the agent workspace.",
@@ -251,6 +253,51 @@ class ToolRegistry:
251
253
  def registered_tools(self) -> frozenset[str]:
252
254
  return frozenset(self.handlers)
253
255
 
256
+ def diagnostics(self) -> Dict[str, Any]:
257
+ """Return registry drift checks without executing any tool.
258
+
259
+ This is intentionally derived from the live registry instance, not from
260
+ duplicated constants, so admin/runtime surfaces can prove whether the
261
+ dispatch, governance, and catalog projections are still aligned.
262
+ """
263
+
264
+ registered = set(self.registered_tools())
265
+ governed = set(self.governance)
266
+ described = set(self.descriptions)
267
+ return {
268
+ "ready": not (governed - registered or registered - governed or registered - described),
269
+ "registered_tools": len(registered),
270
+ "governed_tools": len(governed),
271
+ "described_tools": len(described),
272
+ "governance_without_handler": sorted(governed - registered),
273
+ "handler_without_governance": sorted(registered - governed),
274
+ "handler_without_description": sorted(registered - described),
275
+ "description_without_handler": sorted(described - registered),
276
+ }
277
+
278
+ def manifest(self) -> Dict[str, Any]:
279
+ """Serializable tool registry contract for runtime/admin inspection."""
280
+
281
+ tools = []
282
+ for name in sorted(self.registered_tools() | set(self.governance) | set(self.descriptions)):
283
+ policy = self.policy_for(name, {})
284
+ tools.append({
285
+ "name": name,
286
+ "registered": name in self.handlers,
287
+ "governed": name in self.governance,
288
+ "described": name in self.descriptions,
289
+ "description": self.descriptions.get(name, ""),
290
+ "policy": dict(policy),
291
+ "permission": dict(self.permission(name, {})),
292
+ })
293
+ diagnostics = self.diagnostics()
294
+ return {
295
+ "status": "ok" if diagnostics["ready"] else "degraded",
296
+ "catalog_brief": self.catalog_brief.strip(),
297
+ "diagnostics": diagnostics,
298
+ "tools": tools,
299
+ }
300
+
254
301
  def execute(self, action: str, args: Dict[str, Any], *, error_cls: type[Exception]) -> Dict[str, Any]:
255
302
  handler = self.handlers.get(action)
256
303
  if handler is None:
@@ -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 = "7.1.0"
22
+ WORKSPACE_OS_VERSION = "7.3.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
@@ -90,6 +90,12 @@ class ToolDispatchService:
90
90
  def permissions(self) -> list[ToolPermission]:
91
91
  return self.registry.permissions()
92
92
 
93
+ def diagnostics(self) -> Dict[str, Any]:
94
+ return self.registry.diagnostics()
95
+
96
+ def manifest(self) -> Dict[str, Any]:
97
+ return self.registry.manifest()
98
+
93
99
  def check_role(self, tool_name: str, current_user: str) -> None:
94
100
  if tool_name not in self.registry.admin_only_tools:
95
101
  return
@@ -131,6 +137,14 @@ def list_tool_permissions() -> list:
131
137
  return DEFAULT_TOOL_DISPATCH_SERVICE.permissions()
132
138
 
133
139
 
140
+ def tool_registry_diagnostics() -> Dict[str, Any]:
141
+ return DEFAULT_TOOL_DISPATCH_SERVICE.diagnostics()
142
+
143
+
144
+ def tool_registry_manifest() -> Dict[str, Any]:
145
+ return DEFAULT_TOOL_DISPATCH_SERVICE.manifest()
146
+
147
+
134
148
  def check_tool_role(tool_name: str, current_user: str) -> None:
135
149
  DEFAULT_TOOL_DISPATCH_SERVICE.check_role(tool_name, current_user)
136
150
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ltcai",
3
- "version": "7.1.0",
3
+ "version": "7.3.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": {
@@ -12,6 +12,7 @@ import sys
12
12
  import tempfile
13
13
  from pathlib import Path
14
14
 
15
+ from lattice_brain.quality import RetrievalBenchmarkRunner
15
16
  from latticeai.services.memory_service import MemoryService
16
17
 
17
18
 
@@ -110,7 +111,29 @@ def main() -> int:
110
111
  if proof.get("proofs", {}).get("vector_items", 0) < 1:
111
112
  return _fail("vector items must be counted in Brain proof")
112
113
 
113
- print("brain-quality-eval: OK")
114
+ benchmark = RetrievalBenchmarkRunner()
115
+ retrieval_metrics = benchmark.run_fixture(
116
+ "7.3.0-hybrid-recall-regression",
117
+ [
118
+ {
119
+ "query": "first five minute Brain proof",
120
+ "relevant": ["note:first-loop", "doc:first-loop"],
121
+ "retrieved": [item.get("id") for item in recall_items],
122
+ },
123
+ {
124
+ "query": "local-first memory product wedge",
125
+ "relevant": ["decision:pricing"],
126
+ "retrieved": ["decision:pricing", "note:first-loop"],
127
+ },
128
+ ],
129
+ top_k=4,
130
+ )
131
+ if retrieval_metrics.get("recall@5", 0.0) < 0.75:
132
+ return _fail(f"hybrid recall regression below threshold: {retrieval_metrics}")
133
+ if retrieval_metrics.get("precision@5", 0.0) < 0.4:
134
+ return _fail(f"hybrid precision regression below threshold: {retrieval_metrics}")
135
+
136
+ print(f"brain-quality-eval: OK {retrieval_metrics}")
114
137
  return 0
115
138
 
116
139
 
@@ -1654,7 +1654,7 @@ dependencies = [
1654
1654
 
1655
1655
  [[package]]
1656
1656
  name = "lattice-ai-desktop"
1657
- version = "7.1.0"
1657
+ version = "7.3.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 = "7.1.0"
3
+ version = "7.3.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.1.0",
4
+ "version": "7.3.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": "7.1.0",
2
+ "version": "7.3.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-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"
9
+ "_primitives-CdwcE--L.js": "/static/app/assets/primitives-CdwcE--L.js",
10
+ "_textarea-CqOdBPL1.js": "/static/app/assets/textarea-CqOdBPL1.js",
11
+ "index.html": "/static/app/assets/index-COuGp7_5.js",
12
+ "assets/index-Bi_bpigM.css": "/static/app/assets/index-Bi_bpigM.css",
13
+ "src/pages/Act.tsx": "/static/app/assets/Act-Di4tRFWY.js",
14
+ "src/pages/Brain.tsx": "/static/app/assets/Brain-BZB3Gy9w.js",
15
+ "src/pages/Capture.tsx": "/static/app/assets/Capture-tNyYWxnh.js",
16
+ "src/pages/Library.tsx": "/static/app/assets/Library-DAtDDLdg.js",
17
+ "src/pages/System.tsx": "/static/app/assets/System-DEu0xNUc.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-C3_BfUb8.js": {
27
- "file": "assets/primitives-C3_BfUb8.js",
26
+ "_primitives-CdwcE--L.js": {
27
+ "file": "assets/primitives-CdwcE--L.js",
28
28
  "name": "primitives",
29
29
  "imports": [
30
30
  "index.html"
31
31
  ]
32
32
  },
33
- "_textarea-CbvhHvzg.js": {
34
- "file": "assets/textarea-CbvhHvzg.js",
33
+ "_textarea-CqOdBPL1.js": {
34
+ "file": "assets/textarea-CqOdBPL1.js",
35
35
  "name": "textarea",
36
36
  "imports": [
37
37
  "index.html"
38
38
  ]
39
39
  },
40
40
  "index.html": {
41
- "file": "assets/index-BOB-W1FZ.js",
41
+ "file": "assets/index-COuGp7_5.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-DvLsGy-z.css"
54
+ "assets/index-Bi_bpigM.css"
55
55
  ]
56
56
  },
57
57
  "src/pages/Act.tsx": {
58
- "file": "assets/Act-BHEb8bAM.js",
58
+ "file": "assets/Act-Di4tRFWY.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-C3_BfUb8.js",
65
- "_textarea-CbvhHvzg.js"
64
+ "_primitives-CdwcE--L.js",
65
+ "_textarea-CqOdBPL1.js"
66
66
  ]
67
67
  },
68
68
  "src/pages/Brain.tsx": {
69
- "file": "assets/Brain-yvR7xkrV.js",
69
+ "file": "assets/Brain-BZB3Gy9w.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-C3_BfUb8.js",
76
- "_textarea-CbvhHvzg.js"
75
+ "_primitives-CdwcE--L.js",
76
+ "_textarea-CqOdBPL1.js"
77
77
  ]
78
78
  },
79
79
  "src/pages/Capture.tsx": {
80
- "file": "assets/Capture-BrAmsSEH.js",
80
+ "file": "assets/Capture-tNyYWxnh.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-C3_BfUb8.js"
86
+ "_primitives-CdwcE--L.js"
87
87
  ]
88
88
  },
89
89
  "src/pages/Library.tsx": {
90
- "file": "assets/Library-BOVzYfxI.js",
90
+ "file": "assets/Library-DAtDDLdg.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-C3_BfUb8.js"
96
+ "_primitives-CdwcE--L.js"
97
97
  ]
98
98
  },
99
99
  "src/pages/System.tsx": {
100
- "file": "assets/System-D4WaN4kj.js",
100
+ "file": "assets/System-DEu0xNUc.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-C3_BfUb8.js"
106
+ "_primitives-CdwcE--L.js"
107
107
  ]
108
108
  }
109
109
  }