ltcai 8.1.0 → 8.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 (123) hide show
  1. package/README.md +29 -48
  2. package/docs/CHANGELOG.md +70 -874
  3. package/docs/COMMUNITY_AND_PLUGINS.md +43 -0
  4. package/docs/DEVELOPMENT.md +26 -3
  5. package/docs/LEGACY_COMPATIBILITY.md +23 -1
  6. package/docs/ONBOARDING.md +38 -0
  7. package/docs/TRUST_MODEL.md +26 -26
  8. package/docs/WHY_LATTICE.md +29 -25
  9. package/docs/kg-schema.md +7 -5
  10. package/lattice_brain/__init__.py +1 -1
  11. package/lattice_brain/graph/ingest.py +40 -8
  12. package/lattice_brain/graph/schema.py +6 -0
  13. package/lattice_brain/runtime/agent_runtime.py +22 -37
  14. package/lattice_brain/runtime/multi_agent.py +1 -1
  15. package/lattice_brain/workflow.py +26 -1
  16. package/latticeai/__init__.py +1 -1
  17. package/latticeai/api/computer_use.py +2 -0
  18. package/latticeai/api/knowledge_graph.py +33 -0
  19. package/latticeai/api/local_files.py +2 -0
  20. package/latticeai/api/memory.py +13 -0
  21. package/latticeai/api/tools.py +1 -0
  22. package/latticeai/api/workflow_designer.py +5 -4
  23. package/latticeai/app_factory.py +30 -28
  24. package/latticeai/brain/__init__.py +1 -1
  25. package/latticeai/core/config.py +10 -0
  26. package/latticeai/core/legacy_compatibility.py +174 -0
  27. package/latticeai/core/marketplace.py +1 -1
  28. package/latticeai/core/tool_registry.py +10 -0
  29. package/latticeai/core/workspace_graph_trace.py +132 -0
  30. package/latticeai/core/workspace_memory.py +75 -0
  31. package/latticeai/core/workspace_os.py +135 -1283
  32. package/latticeai/core/workspace_os_utils.py +132 -0
  33. package/latticeai/core/workspace_permissions.py +99 -0
  34. package/latticeai/core/workspace_plugins.py +97 -0
  35. package/latticeai/core/workspace_runs.py +612 -0
  36. package/latticeai/core/workspace_skills.py +114 -0
  37. package/latticeai/core/workspace_snapshots.py +195 -0
  38. package/latticeai/core/workspace_timeline.py +107 -0
  39. package/latticeai/runtime/audit_runtime.py +64 -0
  40. package/latticeai/runtime/config_runtime.py +4 -0
  41. package/latticeai/runtime/sso_runtime.py +52 -0
  42. package/latticeai/services/architecture_readiness.py +37 -3
  43. package/latticeai/services/memory_service.py +215 -7
  44. package/latticeai/services/model_capability_registry.py +68 -0
  45. package/latticeai/services/model_engines.py +594 -0
  46. package/latticeai/services/model_loading.py +482 -0
  47. package/latticeai/services/model_recommendation.py +5 -0
  48. package/latticeai/services/model_runtime.py +131 -875
  49. package/latticeai/services/product_readiness.py +41 -18
  50. package/llm_router.py +7 -28
  51. package/mcp_registry.py +7 -24
  52. package/package.json +1 -1
  53. package/scripts/pts-claudecode-discord-bridge.mjs +2 -1
  54. package/src-tauri/Cargo.lock +1 -1
  55. package/src-tauri/Cargo.toml +1 -1
  56. package/src-tauri/tauri.conf.json +1 -1
  57. package/static/app/asset-manifest.json +11 -11
  58. package/static/app/assets/{Act-BOO66G-c.js → Act-D5mo4tE4.js} +1 -1
  59. package/static/app/assets/{Brain-C6lEYiD7.js → Brain-BVWyQw8A.js} +1 -1
  60. package/static/app/assets/{Capture-TATXBRDw.js → Capture-C1R6GT0t.js} +1 -1
  61. package/static/app/assets/{Library-DK4FIp8a.js → Library-C2wIxpTs.js} +1 -1
  62. package/static/app/assets/{System-Bgs6Ql7x.js → System-DE5GRyQR.js} +1 -1
  63. package/static/app/assets/index-CoiuIFFP.js +16 -0
  64. package/static/app/assets/index-ty1iGgZu.css +2 -0
  65. package/static/app/assets/{primitives-B70WOra0.js → primitives-BdsUNXa6.js} +1 -1
  66. package/static/app/assets/{textarea-Czrd9gwM.js → textarea-e7qaj6Hm.js} +1 -1
  67. package/static/app/index.html +2 -2
  68. package/static/sw.js +1 -1
  69. package/tools/__init__.py +2 -1
  70. package/tools/computer.py +21 -0
  71. package/docs/CARRYOVER_AUDIT_v3.6.0.md +0 -61
  72. package/docs/HANDOVER_v3.6.0.md +0 -46
  73. package/docs/RUNTIME_HOOK_COVERAGE_v3.5.0.md +0 -56
  74. package/docs/RUNTIME_HOOK_COVERAGE_v3.6.0.md +0 -49
  75. package/docs/V2_ARCHITECTURE.md +0 -561
  76. package/docs/V3_2_AUDIT.md +0 -82
  77. package/docs/V3_BACKEND_ARCHITECTURE.md +0 -138
  78. package/docs/V3_FRONTEND.md +0 -146
  79. package/docs/V4_1_FRONTEND_ARCHITECTURE_REVIEW.md +0 -65
  80. package/docs/V4_1_FRONTEND_MIGRATION_REPORT.md +0 -70
  81. package/docs/V4_1_VALIDATION_REPORT.md +0 -47
  82. package/docs/V4_2_BRAIN_CORE_ARCHITECTURE.md +0 -97
  83. package/docs/V4_2_STORAGE_MIGRATION_REPORT.md +0 -91
  84. package/docs/V4_2_VALIDATION_REPORT.md +0 -89
  85. package/docs/V4_3_2_DEADCODE_AUDIT_REPORT.md +0 -174
  86. package/docs/V4_3_2_DOCUMENTATION_CLEANUP_REPORT.md +0 -81
  87. package/docs/V4_3_2_GITHUB_VERCEL_CHECK_REPORT.md +0 -75
  88. package/docs/V4_3_2_GRAPH_UX_REPORT.md +0 -48
  89. package/docs/V4_3_2_INDEPENDENT_AUDIT_PACKAGE.md +0 -209
  90. package/docs/V4_3_2_PRODUCT_POLISH_REPORT.md +0 -57
  91. package/docs/V4_3_2_SELF_AUDIT_REPORT.md +0 -63
  92. package/docs/V4_3_2_VALIDATION_REPORT.md +0 -97
  93. package/docs/V4_3_3_VALIDATION_REPORT.md +0 -46
  94. package/docs/V4_3_PORTABILITY_ARCHITECTURE.md +0 -69
  95. package/docs/V4_3_PRIVACY_AUDIT.md +0 -60
  96. package/docs/V4_3_PRODUCT_HARDENING_REPORT.md +0 -53
  97. package/docs/V4_3_VALIDATION_REPORT.md +0 -58
  98. package/docs/V4_4_0_EXTRACTION_REPORT.md +0 -239
  99. package/docs/V4_5_0_GEMMA_RUNTIME_COMPATIBILITY_REPORT.md +0 -49
  100. package/docs/V4_5_0_GRAPH_UX_REPORT.md +0 -34
  101. package/docs/V4_5_0_MODEL_RUNTIME_UX_REPORT.md +0 -40
  102. package/docs/V4_5_0_ONBOARDING_REPORT.md +0 -31
  103. package/docs/V4_5_0_PRODUCT_EXPERIENCE_RECOVERY_REPORT.md +0 -49
  104. package/docs/V4_5_0_VALIDATION_REPORT.md +0 -60
  105. package/docs/V4_5_1_GRAPH_EXPERIENCE_REPORT.md +0 -33
  106. package/docs/V4_5_1_MODEL_EXPERIENCE_REPORT.md +0 -37
  107. package/docs/V4_5_1_NAVIGATION_REPORT.md +0 -37
  108. package/docs/V4_5_1_ONBOARDING_REPORT.md +0 -29
  109. package/docs/V4_5_1_PRODUCT_REIMAGINING_REPORT.md +0 -61
  110. package/docs/V4_5_1_RC_ARTIFACTS.md +0 -44
  111. package/docs/V4_5_1_UX_REPORT.md +0 -45
  112. package/docs/V4_5_1_VALIDATION_REPORT.md +0 -55
  113. package/docs/V4_5_1_VISUAL_DESIGN_REPORT.md +0 -30
  114. package/docs/V4_6_0_LIVING_BRAIN_EXPERIENCE_REPORT.md +0 -72
  115. package/docs/V4_6_1_RELEASE_REFRESH_REPORT.md +0 -42
  116. package/docs/V4_7_0_ADMIN_SEPARATION_REPORT.md +0 -42
  117. package/docs/V4_7_1_ADMIN_OPERATIONS_REPORT.md +0 -49
  118. package/docs/V4_7_2_INTUITIVE_BRAIN_UX_REPORT.md +0 -62
  119. package/docs/V4_BRAIN_ARCHITECTURE.md +0 -322
  120. package/docs/V4_DIGITAL_BRAIN_RECOVERY.md +0 -555
  121. package/docs/V4_IMPLEMENTATION_PLAN.md +0 -470
  122. package/static/app/assets/index-Bvv79nre.js +0 -16
  123. package/static/app/assets/index-Dslqglia.css +0 -2
@@ -0,0 +1,132 @@
1
+ """Graph answer trace persistence extracted from WorkspaceOSStore."""
2
+ from __future__ import annotations
3
+
4
+ from typing import Any, Dict, List, Optional
5
+
6
+ from .workspace_os_utils import _json_hash, _listify, _now
7
+
8
+
9
+ class WorkspaceGraphTrace:
10
+ def __init__(self, store: Any):
11
+ self._store = store
12
+
13
+ def __getattr__(self, name: str) -> Any:
14
+ return getattr(self._store, name)
15
+
16
+ def build_graph_trace(self, question: str, graph: Any, context: str = "", *, limit: int = 8) -> Dict[str, Any]:
17
+ if graph is None:
18
+ return {
19
+ "source_files": [],
20
+ "graph_nodes": [],
21
+ "graph_edges": [],
22
+ "confidence": 0.0,
23
+ "retrieval_metadata": {
24
+ "query": question,
25
+ "matched_nodes": 0,
26
+ "graph_enabled": False,
27
+ "context_chars": len(context or ""),
28
+ },
29
+ }
30
+
31
+ matches: List[Dict[str, Any]] = []
32
+ search_error = ""
33
+ try:
34
+ matches = graph.search(question, limit=limit).get("matches", [])
35
+ except Exception as exc:
36
+ search_error = str(exc)
37
+ matches = []
38
+
39
+ source_files: List[Dict[str, Any]] = []
40
+ seen_sources = set()
41
+ for match in matches:
42
+ meta = match.get("metadata") or {}
43
+ source = (
44
+ meta.get("relative_path")
45
+ or meta.get("file_path")
46
+ or meta.get("filename")
47
+ or meta.get("blob_path")
48
+ or meta.get("source")
49
+ )
50
+ if source and source not in seen_sources:
51
+ seen_sources.add(source)
52
+ source_files.append({
53
+ "source": source,
54
+ "node_id": match.get("id"),
55
+ "node_title": match.get("title"),
56
+ "node_type": match.get("type"),
57
+ "jump": {
58
+ "graph": f"/graph?node={match.get('id')}",
59
+ "source": source,
60
+ },
61
+ })
62
+
63
+ edges: List[Dict[str, Any]] = []
64
+ edge_seen = set()
65
+ for match in matches[:5]:
66
+ node_id = match.get("id")
67
+ if not node_id:
68
+ continue
69
+ try:
70
+ for edge in graph.neighbors(node_id).get("edges", []):
71
+ key = (edge.get("from"), edge.get("to"), edge.get("type"))
72
+ if key in edge_seen:
73
+ continue
74
+ edge_seen.add(key)
75
+ edges.append(edge)
76
+ if len(edges) >= 24:
77
+ break
78
+ except Exception:
79
+ continue
80
+
81
+ if matches:
82
+ confidence = min(0.95, 0.35 + min(len(matches), limit) / max(limit, 1) * 0.45 + (0.10 if edges else 0.0))
83
+ else:
84
+ confidence = 0.05 if context else 0.0
85
+
86
+ return {
87
+ "source_files": source_files,
88
+ "graph_nodes": matches,
89
+ "graph_edges": edges,
90
+ "confidence": round(confidence, 4),
91
+ "retrieval_metadata": {
92
+ "query": question,
93
+ "matched_nodes": len(matches),
94
+ "matched_edges": len(edges),
95
+ "graph_enabled": True,
96
+ "context_chars": len(context or ""),
97
+ "search_error": search_error,
98
+ },
99
+ }
100
+
101
+ def record_trace(
102
+ self,
103
+ *,
104
+ question: str,
105
+ response: str,
106
+ conversation_id: Optional[str],
107
+ user_email: Optional[str],
108
+ trace: Dict[str, Any],
109
+ workspace_id: Optional[str] = None,
110
+ ) -> Dict[str, Any]:
111
+ state = self.load_state()
112
+ trace_id = f"trace-{_json_hash([question, response, conversation_id, _now()])[:16]}"
113
+ record = {
114
+ "id": trace_id,
115
+ "question": question,
116
+ "response_preview": str(response or "")[:700],
117
+ "conversation_id": conversation_id,
118
+ "user_email": user_email,
119
+ "workspace_id": self._resolve_scope(workspace_id, state),
120
+ "created_at": _now(),
121
+ **trace,
122
+ }
123
+ state.setdefault("traces", []).append(record)
124
+ self.save_state(state)
125
+ self.record_timeline_event("graph", "answer_trace", {"trace_id": trace_id, "conversation_id": conversation_id})
126
+ return record
127
+
128
+ def list_traces(self, conversation_id: Optional[str] = None, limit: int = 50, workspace_id: Optional[str] = None) -> Dict[str, Any]:
129
+ traces = self._scoped(_listify(self.load_state().get("traces")), workspace_id)
130
+ if conversation_id:
131
+ traces = [trace for trace in traces if trace.get("conversation_id") == conversation_id]
132
+ return {"traces": list(reversed(traces[-max(1, min(limit, 200)):]))}
@@ -0,0 +1,75 @@
1
+ """Memory methods extracted from WorkspaceOSStore for decomposition.
2
+
3
+ WorkspaceMemory manager.
4
+ """
5
+ from __future__ import annotations
6
+
7
+ from typing import Any, Dict, List, Optional
8
+
9
+ from .workspace_os_utils import _json_hash, _listify, _now
10
+
11
+
12
+ class WorkspaceMemory:
13
+ def __init__(self, store: Any):
14
+ self._store = store
15
+
16
+ def upsert_memory(
17
+ self,
18
+ *,
19
+ kind: str,
20
+ content: str,
21
+ user_email: Optional[str],
22
+ tags: Optional[List[str]] = None,
23
+ memory_id: Optional[str] = None,
24
+ metadata: Optional[Dict[str, Any]] = None,
25
+ graph: Any = None,
26
+ workspace_id: Optional[str] = None,
27
+ ) -> Dict[str, Any]:
28
+ MEMORY_KINDS = {"short_term", "workspace", "preferences", "decisions", "working_style", "frequently_used_tools", "long_term"}
29
+ if kind not in MEMORY_KINDS:
30
+ raise ValueError(f"unknown memory kind: {kind}")
31
+ if not str(content or "").strip():
32
+ raise ValueError("content is required")
33
+ state = self._store.load_state()
34
+ memories = _listify(state.get("memories"))
35
+ now = _now()
36
+ memory_id = memory_id or f"memory-{_json_hash([kind, content, user_email, now])[:16]}"
37
+ existing = next((item for item in memories if item.get("id") == memory_id), None)
38
+ record = existing or {"id": memory_id, "created_at": now}
39
+ record.update({
40
+ "kind": kind,
41
+ "content": content,
42
+ "user_email": user_email,
43
+ "tags": tags or [],
44
+ "metadata": {**(metadata or {}), "memory_scope": kind},
45
+ "workspace_id": self._store._resolve_scope(workspace_id, state) if existing is None else self._store._record_workspace(record),
46
+ "updated_at": now,
47
+ })
48
+ if graph is not None:
49
+ try:
50
+ ingested = graph.ingest_event(
51
+ "Memory",
52
+ f"{kind}: {content[:80]}",
53
+ user_email=user_email,
54
+ source="workspace_os",
55
+ metadata={"memory_id": memory_id, "kind": kind, "tags": tags or []},
56
+ )
57
+ record["graph_node_id"] = ingested.get("node_id")
58
+ except Exception as exc:
59
+ record["graph_error"] = str(exc)
60
+ if existing is None:
61
+ memories.append(record)
62
+ state["memories"] = memories
63
+ self._store.save_state(state)
64
+ self._store.record_timeline_event("memory", "memory_upserted", {"memory_id": memory_id, "kind": kind}, workspace_id=record.get("workspace_id"))
65
+ return record
66
+
67
+ def list_memories(self, user_email: Optional[str] = None, kind: Optional[str] = None, workspace_id: Optional[str] = None) -> Dict[str, Any]:
68
+ memories = self._store._scoped(_listify(self._store.load_state().get("memories")), workspace_id)
69
+ if user_email:
70
+ memories = [item for item in memories if item.get("user_email") in {None, user_email}]
71
+ if kind:
72
+ memories = [item for item in memories if item.get("kind") == kind]
73
+ return {"memories": list(reversed(memories))}
74
+
75
+ # search_memories can be added similarly if needed