ltcai 3.1.0 → 3.2.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 +34 -8
- package/docs/CHANGELOG.md +53 -0
- package/docs/V3_2_AUDIT.md +82 -0
- package/docs/V3_FRONTEND.md +1 -1
- package/docs/architecture.md +6 -0
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/agent_registry.py +103 -0
- package/latticeai/api/hooks.py +113 -0
- package/latticeai/api/marketplace.py +13 -0
- package/latticeai/api/memory.py +109 -0
- package/latticeai/core/agent_registry.py +234 -0
- package/latticeai/core/hooks.py +284 -0
- package/latticeai/core/marketplace.py +87 -2
- package/latticeai/core/multi_agent.py +1 -1
- package/latticeai/core/workspace_os.py +1 -1
- package/latticeai/server_app.py +41 -0
- package/latticeai/services/memory_service.py +324 -0
- package/package.json +2 -2
- package/scripts/build_v3_assets.mjs +1 -1
- package/static/v3/asset-manifest.json +16 -8
- package/static/v3/js/{app.46fb61d9.js → app.a5adc0f3.js} +1 -1
- package/static/v3/js/core/{api.22a41d42.js → api.603b978f.js} +64 -0
- package/static/v3/js/core/api.js +64 -0
- package/static/v3/js/core/{routes.f935dd50.js → routes.07ad6696.js} +11 -0
- package/static/v3/js/core/routes.js +11 -0
- package/static/v3/js/core/{shell.1b6199d6.js → shell.ea0b9ae5.js} +2 -2
- package/static/v3/js/views/{agents.14e48bdd.js → agents.c373d48c.js} +100 -0
- package/static/v3/js/views/agents.js +100 -0
- package/static/v3/js/views/hooks.f3edebca.js +99 -0
- package/static/v3/js/views/hooks.js +99 -0
- package/static/v3/js/views/marketplace.ab0583d4.js +141 -0
- package/static/v3/js/views/marketplace.js +141 -0
- package/static/v3/js/views/mcp.99b5c6a7.js +114 -0
- package/static/v3/js/views/mcp.js +114 -0
- package/static/v3/js/views/memory.d2ed7a7c.js +146 -0
- package/static/v3/js/views/memory.js +146 -0
- package/static/v3/js/views/planning.9ac3e313.js +153 -0
- package/static/v3/js/views/planning.js +153 -0
- package/static/v3/js/views/skills.c6c2f965.js +109 -0
- package/static/v3/js/views/skills.js +109 -0
- package/static/v3/js/views/tools.e4f11276.js +108 -0
- package/static/v3/js/views/tools.js +108 -0
- package/static/v3/js/views/workflows.26c57290.js +128 -0
- package/static/v3/js/views/workflows.js +128 -0
package/latticeai/server_app.py
CHANGED
|
@@ -113,6 +113,12 @@ from latticeai.api.tools import create_tools_router
|
|
|
113
113
|
from latticeai.api.static_routes import create_static_routes_router
|
|
114
114
|
from latticeai.api.garden import create_garden_router
|
|
115
115
|
from latticeai.api.setup import create_setup_router
|
|
116
|
+
from latticeai.api.hooks import create_hooks_router
|
|
117
|
+
from latticeai.core.hooks import HooksRegistry
|
|
118
|
+
from latticeai.api.agent_registry import create_agent_registry_router
|
|
119
|
+
from latticeai.core.agent_registry import AgentRegistry
|
|
120
|
+
from latticeai.api.memory import create_memory_router
|
|
121
|
+
from latticeai.services.memory_service import MemoryService
|
|
116
122
|
from latticeai.services.tool_dispatch import (
|
|
117
123
|
LOCAL_WRITE_BLOCKED_PREFIXES as _LOCAL_WRITE_BLOCKED_PREFIXES,
|
|
118
124
|
TOOL_GOVERNANCE,
|
|
@@ -122,6 +128,7 @@ from latticeai.services.tool_dispatch import (
|
|
|
122
128
|
configure_tool_dispatch,
|
|
123
129
|
get_tool_permission,
|
|
124
130
|
list_tool_permissions,
|
|
131
|
+
tool_response as _tool_response,
|
|
125
132
|
)
|
|
126
133
|
from latticeai.core.tool_registry import TOOL_CATALOG_BRIEF as _TOOL_CATALOG_BRIEF
|
|
127
134
|
from mcp_registry import (
|
|
@@ -291,6 +298,19 @@ WORKSPACE_SERVICE = WorkspaceService(WORKSPACE_OS)
|
|
|
291
298
|
PLUGINS_DIR = Path(os.getenv("LATTICEAI_PLUGINS_DIR") or (BASE_DIR / "plugins"))
|
|
292
299
|
PLUGIN_REGISTRY = PluginRegistry(PLUGINS_DIR, store=WORKSPACE_OS)
|
|
293
300
|
TEMPLATE_CATALOG = TemplateCatalog()
|
|
301
|
+
# ── v3.2 platform registries: lifecycle hooks + agent registry, persisted under
|
|
302
|
+
# DATA_DIR so the /app Hooks and Agent Registry views read/write real state.
|
|
303
|
+
HOOKS_REGISTRY = HooksRegistry(DATA_DIR / "hooks.json")
|
|
304
|
+
AGENT_REGISTRY = AgentRegistry(DATA_DIR / "agent_registry.json")
|
|
305
|
+
# Unified long-term memory platform fronting workspace memories, agent
|
|
306
|
+
# snapshots, conversation history, and the KG graph/vector index.
|
|
307
|
+
MEMORY_SERVICE = MemoryService(
|
|
308
|
+
store=WORKSPACE_OS,
|
|
309
|
+
data_dir=DATA_DIR,
|
|
310
|
+
knowledge_graph=KNOWLEDGE_GRAPH,
|
|
311
|
+
enable_graph=ENABLE_GRAPH,
|
|
312
|
+
history_file=HISTORY_FILE,
|
|
313
|
+
)
|
|
294
314
|
|
|
295
315
|
def _require_graph():
|
|
296
316
|
if not ENABLE_GRAPH or KNOWLEDGE_GRAPH is None:
|
|
@@ -1439,6 +1459,27 @@ app.include_router(create_tools_router(
|
|
|
1439
1459
|
mcp_public_item=mcp_public_item,
|
|
1440
1460
|
))
|
|
1441
1461
|
|
|
1462
|
+
app.include_router(create_hooks_router(
|
|
1463
|
+
registry=HOOKS_REGISTRY,
|
|
1464
|
+
require_user=require_user,
|
|
1465
|
+
append_audit_event=append_audit_event,
|
|
1466
|
+
))
|
|
1467
|
+
|
|
1468
|
+
app.include_router(create_agent_registry_router(
|
|
1469
|
+
registry=AGENT_REGISTRY,
|
|
1470
|
+
require_user=require_user,
|
|
1471
|
+
append_audit_event=append_audit_event,
|
|
1472
|
+
))
|
|
1473
|
+
|
|
1474
|
+
app.include_router(create_memory_router(
|
|
1475
|
+
service=MEMORY_SERVICE,
|
|
1476
|
+
require_user=require_user,
|
|
1477
|
+
get_current_user=get_current_user,
|
|
1478
|
+
gate_read=PLATFORM.gate_read,
|
|
1479
|
+
gate_write=PLATFORM.gate_write,
|
|
1480
|
+
append_audit_event=append_audit_event,
|
|
1481
|
+
))
|
|
1482
|
+
|
|
1442
1483
|
app.include_router(create_garden_router(gardener=gardener, require_user=require_user))
|
|
1443
1484
|
app.include_router(create_setup_router(model_router=router, require_user=require_user))
|
|
1444
1485
|
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
"""Long-Term Memory platform + Memory Manager (v3.2.0).
|
|
2
|
+
|
|
3
|
+
Parts 7, 8 and 13. Lattice AI already persists memory in several real stores;
|
|
4
|
+
before this service they were unrelated. ``MemoryService`` unifies them behind
|
|
5
|
+
one façade and adds a Memory Manager that reports usage / sources / health /
|
|
6
|
+
size / type and supports recall / inspect / prune / compact / rebuild / clear.
|
|
7
|
+
|
|
8
|
+
Memory tiers and their real backing store (nothing is fabricated — a tier with
|
|
9
|
+
no backing reports ``unavailable``):
|
|
10
|
+
|
|
11
|
+
* **workspace** — personal workspace memories (``WorkspaceOS`` memories)
|
|
12
|
+
* **project** — memories scoped to a non-personal (organization) workspace
|
|
13
|
+
* **agent** — agent memory snapshots captured during runs
|
|
14
|
+
* **conversation** — chat history conversations
|
|
15
|
+
* **graph** — Knowledge Graph nodes (entities + relations)
|
|
16
|
+
* **vector** — local embedding vector index
|
|
17
|
+
|
|
18
|
+
The service never invents counts or health: every number is read from the
|
|
19
|
+
underlying store, and missing stores surface as ``unavailable``.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import json
|
|
25
|
+
from datetime import datetime
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
from typing import Any, Callable, Dict, List, Optional
|
|
28
|
+
|
|
29
|
+
# Personal workspace memory kinds (from WorkspaceOS.MEMORY_KINDS).
|
|
30
|
+
WORKSPACE_KINDS = (
|
|
31
|
+
"short_term",
|
|
32
|
+
"workspace",
|
|
33
|
+
"preferences",
|
|
34
|
+
"decisions",
|
|
35
|
+
"working_style",
|
|
36
|
+
"frequently_used_tools",
|
|
37
|
+
"long_term",
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
TIERS = ("workspace", "project", "agent", "conversation", "graph", "vector")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _now() -> str:
|
|
44
|
+
return datetime.now().isoformat(timespec="seconds")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _file_size(path: Path) -> int:
|
|
48
|
+
try:
|
|
49
|
+
return path.stat().st_size if path.exists() else 0
|
|
50
|
+
except Exception:
|
|
51
|
+
return 0
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class MemoryService:
|
|
55
|
+
def __init__(
|
|
56
|
+
self,
|
|
57
|
+
*,
|
|
58
|
+
store: Any,
|
|
59
|
+
data_dir: Path,
|
|
60
|
+
knowledge_graph: Any = None,
|
|
61
|
+
enable_graph: bool = True,
|
|
62
|
+
history_file: Optional[Path] = None,
|
|
63
|
+
):
|
|
64
|
+
self._store = store
|
|
65
|
+
self._kg = knowledge_graph
|
|
66
|
+
self._enable_graph = bool(enable_graph and knowledge_graph is not None)
|
|
67
|
+
self._data_dir = Path(data_dir)
|
|
68
|
+
self._history_file = Path(history_file) if history_file else (self._data_dir / "chat_history.json")
|
|
69
|
+
|
|
70
|
+
# ── helpers over the underlying stores ────────────────────────────────
|
|
71
|
+
def _workspace_memories(self, *, user_email: Optional[str], workspace_id: Optional[str]) -> List[Dict[str, Any]]:
|
|
72
|
+
try:
|
|
73
|
+
return list(self._store.list_memories(user_email=user_email, workspace_id=workspace_id).get("memories", []))
|
|
74
|
+
except Exception:
|
|
75
|
+
return []
|
|
76
|
+
|
|
77
|
+
def _all_memories(self) -> List[Dict[str, Any]]:
|
|
78
|
+
try:
|
|
79
|
+
return list(self._store.list_memories().get("memories", []))
|
|
80
|
+
except Exception:
|
|
81
|
+
return []
|
|
82
|
+
|
|
83
|
+
def _snapshots(self, *, workspace_id: Optional[str]) -> List[Dict[str, Any]]:
|
|
84
|
+
try:
|
|
85
|
+
return list(self._store.list_memory_snapshots(workspace_id=workspace_id, limit=200).get("snapshots", []))
|
|
86
|
+
except Exception:
|
|
87
|
+
return []
|
|
88
|
+
|
|
89
|
+
def _conversations(self) -> List[Dict[str, Any]]:
|
|
90
|
+
if not self._history_file.exists():
|
|
91
|
+
return []
|
|
92
|
+
try:
|
|
93
|
+
with open(self._history_file, "r", encoding="utf-8") as fh:
|
|
94
|
+
data = json.load(fh)
|
|
95
|
+
except Exception:
|
|
96
|
+
return []
|
|
97
|
+
if isinstance(data, dict):
|
|
98
|
+
convs = data.get("conversations")
|
|
99
|
+
if isinstance(convs, list):
|
|
100
|
+
return convs
|
|
101
|
+
return [{"id": k, **(v if isinstance(v, dict) else {"messages": v})} for k, v in data.items()]
|
|
102
|
+
if isinstance(data, list):
|
|
103
|
+
return data
|
|
104
|
+
return []
|
|
105
|
+
|
|
106
|
+
def _kg_stats(self) -> Optional[Dict[str, Any]]:
|
|
107
|
+
if not self._enable_graph:
|
|
108
|
+
return None
|
|
109
|
+
try:
|
|
110
|
+
return self._kg.stats()
|
|
111
|
+
except Exception:
|
|
112
|
+
return None
|
|
113
|
+
|
|
114
|
+
def _kg_index(self) -> Optional[Dict[str, Any]]:
|
|
115
|
+
if not self._enable_graph:
|
|
116
|
+
return None
|
|
117
|
+
try:
|
|
118
|
+
return self._kg.index_status()
|
|
119
|
+
except Exception:
|
|
120
|
+
return None
|
|
121
|
+
|
|
122
|
+
# ── Memory Manager: sources / usage / health ──────────────────────────
|
|
123
|
+
def manager(self, *, user_email: Optional[str] = None, workspace_id: Optional[str] = None) -> Dict[str, Any]:
|
|
124
|
+
ws_mem = self._workspace_memories(user_email=user_email, workspace_id="personal")
|
|
125
|
+
project_mem = [m for m in self._all_memories() if (m.get("workspace_id") or "personal") != "personal"]
|
|
126
|
+
snaps = self._snapshots(workspace_id=workspace_id)
|
|
127
|
+
convs = self._conversations()
|
|
128
|
+
kg_stats = self._kg_stats()
|
|
129
|
+
kg_index = self._kg_index()
|
|
130
|
+
|
|
131
|
+
ws_bytes = _file_size(self._data_dir / "workspace_os.json")
|
|
132
|
+
kg_bytes = _file_size(self._data_dir / "knowledge_graph.sqlite")
|
|
133
|
+
conv_bytes = _file_size(self._history_file)
|
|
134
|
+
|
|
135
|
+
node_total = sum((kg_stats or {}).get("nodes", {}).values()) if kg_stats else None
|
|
136
|
+
edge_total = sum((kg_stats or {}).get("edges", {}).values()) if kg_stats else None
|
|
137
|
+
vector_total = None
|
|
138
|
+
if kg_index and isinstance(kg_index.get("vector_counts"), dict):
|
|
139
|
+
vector_total = sum(kg_index["vector_counts"].values())
|
|
140
|
+
elif kg_index:
|
|
141
|
+
vector_total = kg_index.get("indexed") or kg_index.get("ready")
|
|
142
|
+
|
|
143
|
+
sources = [
|
|
144
|
+
{
|
|
145
|
+
"id": "workspace", "type": "workspace", "label": "Workspace Memory",
|
|
146
|
+
"count": len(ws_mem), "size_bytes": ws_bytes if ws_mem else 0,
|
|
147
|
+
"health": "ok", "detail": "Personal workspace knowledge, by kind.",
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
"id": "project", "type": "project", "label": "Project Memory",
|
|
151
|
+
"count": len(project_mem), "size_bytes": 0,
|
|
152
|
+
"health": "ok", "detail": "Memory scoped to organization workspaces.",
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
"id": "agent", "type": "agent", "label": "Agent Memory",
|
|
156
|
+
"count": len(snaps), "size_bytes": 0,
|
|
157
|
+
"health": "ok", "detail": "Per-run agent memory snapshots.",
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"id": "conversation", "type": "conversation", "label": "Conversation Memory",
|
|
161
|
+
"count": len(convs), "size_bytes": conv_bytes,
|
|
162
|
+
"health": "ok" if self._history_file.exists() else "empty",
|
|
163
|
+
"detail": "Historical interaction memory from chat.",
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"id": "graph", "type": "graph", "label": "Graph Memory",
|
|
167
|
+
"count": node_total, "size_bytes": kg_bytes,
|
|
168
|
+
"health": "ok" if kg_stats else "unavailable",
|
|
169
|
+
"detail": "Knowledge Graph entities and relations." if kg_stats else "Knowledge graph disabled or unavailable.",
|
|
170
|
+
"edges": edge_total,
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
"id": "vector", "type": "vector", "label": "Vector Memory",
|
|
174
|
+
"count": vector_total, "size_bytes": 0,
|
|
175
|
+
"health": "ok" if kg_index else "unavailable",
|
|
176
|
+
"detail": "Local embedding vector index." if kg_index else "Vector index unavailable.",
|
|
177
|
+
},
|
|
178
|
+
]
|
|
179
|
+
total_items = sum((s["count"] or 0) for s in sources)
|
|
180
|
+
total_bytes = ws_bytes + kg_bytes + conv_bytes
|
|
181
|
+
healthy = sum(1 for s in sources if s["health"] == "ok")
|
|
182
|
+
overall = "ok" if healthy >= 4 else "degraded" if healthy >= 1 else "unavailable"
|
|
183
|
+
return {
|
|
184
|
+
"sources": sources,
|
|
185
|
+
"tiers": list(TIERS),
|
|
186
|
+
"usage": {"total_items": total_items, "total_bytes": total_bytes, "sources": len(sources)},
|
|
187
|
+
"health": overall,
|
|
188
|
+
"graph_enabled": self._enable_graph,
|
|
189
|
+
"generated_at": _now(),
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
def tiers(self) -> Dict[str, Any]:
|
|
193
|
+
return {"tiers": list(TIERS), "workspace_kinds": list(WORKSPACE_KINDS)}
|
|
194
|
+
|
|
195
|
+
# ── recall (unified retrieval over the memory tiers) ───────────────────
|
|
196
|
+
def recall(
|
|
197
|
+
self,
|
|
198
|
+
query: str,
|
|
199
|
+
*,
|
|
200
|
+
user_email: Optional[str] = None,
|
|
201
|
+
workspace_id: Optional[str] = None,
|
|
202
|
+
limit: int = 20,
|
|
203
|
+
) -> Dict[str, Any]:
|
|
204
|
+
q = str(query or "").strip()
|
|
205
|
+
results: List[Dict[str, Any]] = []
|
|
206
|
+
|
|
207
|
+
try:
|
|
208
|
+
mem = self._store.search_memories(q, user_email=user_email, limit=limit, workspace_id=workspace_id).get("memories", [])
|
|
209
|
+
except Exception:
|
|
210
|
+
mem = []
|
|
211
|
+
for m in mem:
|
|
212
|
+
results.append({
|
|
213
|
+
"source": "workspace",
|
|
214
|
+
"id": m.get("id"),
|
|
215
|
+
"title": (m.get("kind") or "memory"),
|
|
216
|
+
"snippet": str(m.get("content") or "")[:240],
|
|
217
|
+
"kind": m.get("kind"),
|
|
218
|
+
"score": 0.6,
|
|
219
|
+
"tags": m.get("tags") or [],
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
if self._enable_graph and q:
|
|
223
|
+
try:
|
|
224
|
+
hits = self._kg.search(q, limit).get("results", [])
|
|
225
|
+
except Exception:
|
|
226
|
+
hits = []
|
|
227
|
+
for hsit in hits[:limit]:
|
|
228
|
+
results.append({
|
|
229
|
+
"source": "graph",
|
|
230
|
+
"id": hsit.get("id") or hsit.get("node_id"),
|
|
231
|
+
"title": hsit.get("title") or hsit.get("name") or "node",
|
|
232
|
+
"snippet": str(hsit.get("summary") or hsit.get("content") or "")[:240],
|
|
233
|
+
"kind": hsit.get("type") or "node",
|
|
234
|
+
"score": float(hsit.get("score") or 0.5),
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
results.sort(key=lambda r: r.get("score", 0), reverse=True)
|
|
238
|
+
return {"query": q, "results": results[: max(1, min(limit, 100))], "count": len(results), "source": "live"}
|
|
239
|
+
|
|
240
|
+
# ── inspect a single tier ─────────────────────────────────────────────
|
|
241
|
+
def inspect(self, source: str, *, user_email: Optional[str] = None, workspace_id: Optional[str] = None, limit: int = 50) -> Dict[str, Any]:
|
|
242
|
+
if source == "workspace":
|
|
243
|
+
items = self._workspace_memories(user_email=user_email, workspace_id="personal")[:limit]
|
|
244
|
+
return {"source": source, "items": items, "count": len(items)}
|
|
245
|
+
if source == "project":
|
|
246
|
+
items = [m for m in self._all_memories() if (m.get("workspace_id") or "personal") != "personal"][:limit]
|
|
247
|
+
return {"source": source, "items": items, "count": len(items)}
|
|
248
|
+
if source == "agent":
|
|
249
|
+
items = self._snapshots(workspace_id=workspace_id)[:limit]
|
|
250
|
+
return {"source": source, "items": items, "count": len(items)}
|
|
251
|
+
if source == "conversation":
|
|
252
|
+
convs = self._conversations()
|
|
253
|
+
items = [{"id": c.get("id"), "title": c.get("title") or c.get("id"), "messages": len(c.get("messages") or [])} for c in convs[:limit]]
|
|
254
|
+
return {"source": source, "items": items, "count": len(convs)}
|
|
255
|
+
if source == "graph":
|
|
256
|
+
return {"source": source, "stats": self._kg_stats() or {}, "available": bool(self._kg_stats())}
|
|
257
|
+
if source == "vector":
|
|
258
|
+
return {"source": source, "index": self._kg_index() or {}, "available": bool(self._kg_index())}
|
|
259
|
+
raise KeyError(source)
|
|
260
|
+
|
|
261
|
+
# ── mutating operations ───────────────────────────────────────────────
|
|
262
|
+
def prune(self, *, ids: Optional[List[str]] = None, kind: Optional[str] = None, user_email: Optional[str] = None) -> Dict[str, Any]:
|
|
263
|
+
removed: List[str] = []
|
|
264
|
+
target_ids = list(ids or [])
|
|
265
|
+
if kind:
|
|
266
|
+
for m in self._workspace_memories(user_email=user_email, workspace_id=None):
|
|
267
|
+
if m.get("kind") == kind and m.get("id"):
|
|
268
|
+
target_ids.append(m["id"])
|
|
269
|
+
for mid in target_ids:
|
|
270
|
+
try:
|
|
271
|
+
self._store.delete_memory(mid)
|
|
272
|
+
removed.append(mid)
|
|
273
|
+
except Exception:
|
|
274
|
+
continue
|
|
275
|
+
return {"removed": removed, "count": len(removed)}
|
|
276
|
+
|
|
277
|
+
def compact(self, *, user_email: Optional[str] = None) -> Dict[str, Any]:
|
|
278
|
+
"""Dedupe workspace memories with identical (kind, content)."""
|
|
279
|
+
seen: set = set()
|
|
280
|
+
removed: List[str] = []
|
|
281
|
+
# Oldest first so the first occurrence (oldest) is kept.
|
|
282
|
+
memories = list(reversed(self._workspace_memories(user_email=user_email, workspace_id=None)))
|
|
283
|
+
for m in memories:
|
|
284
|
+
key = (m.get("kind"), str(m.get("content") or "").strip())
|
|
285
|
+
if key in seen:
|
|
286
|
+
if m.get("id"):
|
|
287
|
+
try:
|
|
288
|
+
self._store.delete_memory(m["id"])
|
|
289
|
+
removed.append(m["id"])
|
|
290
|
+
except Exception:
|
|
291
|
+
continue
|
|
292
|
+
else:
|
|
293
|
+
seen.add(key)
|
|
294
|
+
return {"compacted": len(removed), "removed": removed, "remaining": len(seen)}
|
|
295
|
+
|
|
296
|
+
def rebuild(self, target: str = "vector") -> Dict[str, Any]:
|
|
297
|
+
if target in {"vector", "index", "vector_index"}:
|
|
298
|
+
if not self._enable_graph:
|
|
299
|
+
return {"status": "unavailable", "detail": "Knowledge graph / vector index disabled."}
|
|
300
|
+
try:
|
|
301
|
+
result = self._kg.rebuild_vector_index()
|
|
302
|
+
return {"status": "ok", "target": "vector_index", "result": result}
|
|
303
|
+
except Exception as exc:
|
|
304
|
+
return {"status": "error", "detail": str(exc)}
|
|
305
|
+
return {"status": "error", "detail": f"Unknown rebuild target: {target}"}
|
|
306
|
+
|
|
307
|
+
def clear(self, *, scope: str, confirm: bool = False, user_email: Optional[str] = None) -> Dict[str, Any]:
|
|
308
|
+
if not confirm:
|
|
309
|
+
raise ValueError("clear requires confirm=true")
|
|
310
|
+
if scope in WORKSPACE_KINDS:
|
|
311
|
+
result = self.prune(kind=scope, user_email=user_email)
|
|
312
|
+
return {"cleared": scope, **result}
|
|
313
|
+
if scope == "workspace":
|
|
314
|
+
ids = [m["id"] for m in self._workspace_memories(user_email=user_email, workspace_id=None) if m.get("id")]
|
|
315
|
+
result = self.prune(ids=ids, user_email=user_email)
|
|
316
|
+
return {"cleared": "workspace", **result}
|
|
317
|
+
if scope == "graph":
|
|
318
|
+
if not self._enable_graph:
|
|
319
|
+
return {"status": "unavailable", "detail": "Knowledge graph disabled."}
|
|
320
|
+
try:
|
|
321
|
+
return {"cleared": "graph", "result": self._kg.clear_all()}
|
|
322
|
+
except Exception as exc:
|
|
323
|
+
return {"status": "error", "detail": str(exc)}
|
|
324
|
+
raise ValueError(f"unsupported clear scope: {scope}")
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ltcai",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"description": "Lattice AI v3 local-first AI workspace platform with knowledge graph, vector index, hybrid search, agents, and workspace modes.",
|
|
5
5
|
"homepage": "https://github.com/TaeSooPark-PTS/LatticeAI#readme",
|
|
6
6
|
"repository": {
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"build": "npm run build:assets && npm run build:python",
|
|
21
21
|
"build:assets": "node scripts/build_v3_assets.mjs",
|
|
22
22
|
"build:python": "python3 -m build",
|
|
23
|
-
"check:python": "python3 -m py_compile ltcai_cli.py server.py latticeai/server_app.py latticeai/api/auth.py latticeai/api/chat.py latticeai/api/computer_use.py latticeai/api/deps.py latticeai/api/garden.py latticeai/api/local_files.py latticeai/api/permissions.py latticeai/api/setup.py latticeai/api/static_routes.py latticeai/api/tools.py latticeai/api/plugins.py latticeai/api/workflow_designer.py latticeai/api/agents.py latticeai/api/realtime.py latticeai/api/marketplace.py latticeai/api/search.py latticeai/services/search_service.py latticeai/core/local_embeddings.py latticeai/core/embedding_providers.py latticeai/services/agent_runtime.py latticeai/core/config.py latticeai/api/admin.py latticeai/services/app_context.py latticeai/services/model_runtime.py latticeai/services/model_catalog.py latticeai/services/model_recommendation.py latticeai/services/tool_dispatch.py latticeai/services/upload_service.py latticeai/core/tool_registry.py latticeai/core/enterprise.py latticeai/core/enterprise_admin.py latticeai/core/agent_prompts.py latticeai/core/workspace_os.py latticeai/core/plugins.py latticeai/core/marketplace.py latticeai/core/workflow_engine.py latticeai/core/multi_agent.py latticeai/core/realtime.py knowledge_graph.py knowledge_graph_api.py local_knowledge_api.py llm_router.py p_reinforce.py telegram_bot.py tools.py codex_telegram_bot.py",
|
|
23
|
+
"check:python": "python3 -m py_compile ltcai_cli.py server.py latticeai/server_app.py latticeai/api/auth.py latticeai/api/chat.py latticeai/api/computer_use.py latticeai/api/deps.py latticeai/api/garden.py latticeai/api/local_files.py latticeai/api/permissions.py latticeai/api/setup.py latticeai/api/static_routes.py latticeai/api/tools.py latticeai/api/plugins.py latticeai/api/workflow_designer.py latticeai/api/agents.py latticeai/api/realtime.py latticeai/api/marketplace.py latticeai/api/search.py latticeai/services/search_service.py latticeai/core/local_embeddings.py latticeai/core/embedding_providers.py latticeai/services/agent_runtime.py latticeai/core/config.py latticeai/api/admin.py latticeai/services/app_context.py latticeai/services/model_runtime.py latticeai/services/model_catalog.py latticeai/services/model_recommendation.py latticeai/services/tool_dispatch.py latticeai/services/upload_service.py latticeai/core/tool_registry.py latticeai/core/enterprise.py latticeai/core/enterprise_admin.py latticeai/core/agent_prompts.py latticeai/core/workspace_os.py latticeai/core/plugins.py latticeai/core/marketplace.py latticeai/core/workflow_engine.py latticeai/core/multi_agent.py latticeai/core/realtime.py latticeai/api/mcp.py latticeai/core/hooks.py latticeai/api/hooks.py latticeai/core/agent_registry.py latticeai/api/agent_registry.py latticeai/services/memory_service.py latticeai/api/memory.py knowledge_graph.py knowledge_graph_api.py local_knowledge_api.py llm_router.py p_reinforce.py telegram_bot.py tools.py codex_telegram_bot.py",
|
|
24
24
|
"lint": "node --check static/scripts/account.js && node --check static/scripts/admin.js && node --check static/scripts/chat.js && node --check static/scripts/graph.js && node --check static/scripts/platform.js && node --check static/scripts/ux.js && node --check static/scripts/workspace.js && node --check tests/visual/mock_server.cjs && node --check tests/visual/v3.spec.js && npm run lint:v3",
|
|
25
25
|
"lint:v3": "node scripts/lint_v3.mjs",
|
|
26
26
|
"typecheck": "cd vscode-extension && npm run build",
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "3.
|
|
2
|
+
"version": "3.2.0",
|
|
3
3
|
"generated_at": "deterministic",
|
|
4
4
|
"entrypoints": {
|
|
5
|
-
"app": "/static/v3/js/app.
|
|
5
|
+
"app": "/static/v3/js/app.a5adc0f3.js",
|
|
6
6
|
"styles": [
|
|
7
7
|
"/static/css/tokens.5a595671.css",
|
|
8
8
|
"/static/v3/css/lattice.tokens.c597ff81.css",
|
|
@@ -19,13 +19,13 @@
|
|
|
19
19
|
"static/v3/css/lattice.components.css": "/static/v3/css/lattice.components.011e988b.css",
|
|
20
20
|
"static/v3/css/lattice.shell.css": "/static/v3/css/lattice.shell.4920f42d.css",
|
|
21
21
|
"static/v3/css/lattice.views.css": "/static/v3/css/lattice.views.3ee19d4e.css",
|
|
22
|
-
"static/v3/js/app.js": "/static/v3/js/app.
|
|
23
|
-
"static/v3/js/core/api.js": "/static/v3/js/core/api.
|
|
22
|
+
"static/v3/js/app.js": "/static/v3/js/app.a5adc0f3.js",
|
|
23
|
+
"static/v3/js/core/api.js": "/static/v3/js/core/api.603b978f.js",
|
|
24
24
|
"static/v3/js/core/components.js": "/static/v3/js/core/components.4c83e0a9.js",
|
|
25
25
|
"static/v3/js/core/dom.js": "/static/v3/js/core/dom.a2773eb0.js",
|
|
26
26
|
"static/v3/js/core/router.js": "/static/v3/js/core/router.584570f2.js",
|
|
27
|
-
"static/v3/js/core/routes.js": "/static/v3/js/core/routes.
|
|
28
|
-
"static/v3/js/core/shell.js": "/static/v3/js/core/shell.
|
|
27
|
+
"static/v3/js/core/routes.js": "/static/v3/js/core/routes.07ad6696.js",
|
|
28
|
+
"static/v3/js/core/shell.js": "/static/v3/js/core/shell.ea0b9ae5.js",
|
|
29
29
|
"static/v3/js/core/store.js": "/static/v3/js/core/store.34ebd5e6.js",
|
|
30
30
|
"static/v3/js/views/admin-audit.js": "/static/v3/js/views/admin-audit.660a1fb1.js",
|
|
31
31
|
"static/v3/js/views/admin-permissions.js": "/static/v3/js/views/admin-permissions.a7ae5f09.js",
|
|
@@ -33,15 +33,23 @@
|
|
|
33
33
|
"static/v3/js/views/admin-private-vpc.js": "/static/v3/js/views/admin-private-vpc.7d342d36.js",
|
|
34
34
|
"static/v3/js/views/admin-security.js": "/static/v3/js/views/admin-security.07c66b72.js",
|
|
35
35
|
"static/v3/js/views/admin-users.js": "/static/v3/js/views/admin-users.03bac88c.js",
|
|
36
|
-
"static/v3/js/views/agents.js": "/static/v3/js/views/agents.
|
|
36
|
+
"static/v3/js/views/agents.js": "/static/v3/js/views/agents.c373d48c.js",
|
|
37
37
|
"static/v3/js/views/chat.js": "/static/v3/js/views/chat.718144ce.js",
|
|
38
38
|
"static/v3/js/views/files.js": "/static/v3/js/views/files.4935197e.js",
|
|
39
39
|
"static/v3/js/views/home.js": "/static/v3/js/views/home.cdde3b32.js",
|
|
40
|
+
"static/v3/js/views/hooks.js": "/static/v3/js/views/hooks.f3edebca.js",
|
|
40
41
|
"static/v3/js/views/hybrid-search.js": "/static/v3/js/views/hybrid-search.b22b97e0.js",
|
|
41
42
|
"static/v3/js/views/knowledge-graph.js": "/static/v3/js/views/knowledge-graph.a14ea7e7.js",
|
|
43
|
+
"static/v3/js/views/marketplace.js": "/static/v3/js/views/marketplace.ab0583d4.js",
|
|
44
|
+
"static/v3/js/views/mcp.js": "/static/v3/js/views/mcp.99b5c6a7.js",
|
|
45
|
+
"static/v3/js/views/memory.js": "/static/v3/js/views/memory.d2ed7a7c.js",
|
|
42
46
|
"static/v3/js/views/models.js": "/static/v3/js/views/models.a1ffa147.js",
|
|
43
47
|
"static/v3/js/views/my-computer.js": "/static/v3/js/views/my-computer.1b2ff621.js",
|
|
44
48
|
"static/v3/js/views/pipeline.js": "/static/v3/js/views/pipeline.c522f1ce.js",
|
|
45
|
-
"static/v3/js/views/
|
|
49
|
+
"static/v3/js/views/planning.js": "/static/v3/js/views/planning.9ac3e313.js",
|
|
50
|
+
"static/v3/js/views/settings.js": "/static/v3/js/views/settings.4f777210.js",
|
|
51
|
+
"static/v3/js/views/skills.js": "/static/v3/js/views/skills.c6c2f965.js",
|
|
52
|
+
"static/v3/js/views/tools.js": "/static/v3/js/views/tools.e4f11276.js",
|
|
53
|
+
"static/v3/js/views/workflows.js": "/static/v3/js/views/workflows.26c57290.js"
|
|
46
54
|
}
|
|
47
55
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Boots the shell. Views are lazy-loaded by the router (see core/routes.js).
|
|
4
4
|
* ========================================================================== */
|
|
5
5
|
|
|
6
|
-
import { boot } from "./core/shell.
|
|
6
|
+
import { boot } from "./core/shell.ea0b9ae5.js";
|
|
7
7
|
|
|
8
8
|
const root = document.getElementById("app");
|
|
9
9
|
if (root) boot(root);
|
|
@@ -321,6 +321,70 @@ export const api = {
|
|
|
321
321
|
}
|
|
322
322
|
return { source: "live", text, trace, model };
|
|
323
323
|
},
|
|
324
|
+
|
|
325
|
+
/* ── v3.2 platform surfaces (all fallback-safe; never fabricate) ─────── */
|
|
326
|
+
|
|
327
|
+
// Agent Registry (Part 2)
|
|
328
|
+
agentRegistry(type) { return withFallback(`/agents/api/registry${type ? "?type=" + encodeURIComponent(type) : ""}`, {}, { agents: [], counts: {}, types: [] }); },
|
|
329
|
+
agentCapabilities() { return withFallback("/agents/api/registry/capabilities", {}, { capabilities: {} }); },
|
|
330
|
+
registerAgent(body) { return raw("/agents/api/registry", { method: "POST", body }); },
|
|
331
|
+
updateAgent(id, body) { return raw(`/agents/api/registry/${encodeURIComponent(id)}`, { method: "PATCH", body }); },
|
|
332
|
+
removeAgent(id) { return raw(`/agents/api/registry/${encodeURIComponent(id)}`, { method: "DELETE" }); },
|
|
333
|
+
agentRunDetail(runId) { return raw(`/agents/api/runs/${encodeURIComponent(runId)}`); },
|
|
334
|
+
agentRunReplay(runId) { return raw(`/agents/api/runs/${encodeURIComponent(runId)}/replay`); },
|
|
335
|
+
stopAgentRun(runId) { return raw(`/agents/api/runs/${encodeURIComponent(runId)}/stop`, { method: "POST" }); },
|
|
336
|
+
|
|
337
|
+
// Marketplace + Templates (Parts 3, 4)
|
|
338
|
+
templates(kind) { return withFallback(`/marketplace/templates${kind ? "?kind=" + encodeURIComponent(kind) : ""}`, {}, { templates: [], kinds: [] }); },
|
|
339
|
+
templateRegistry() { return withFallback("/marketplace/templates/registry", {}, { registry: [] }); },
|
|
340
|
+
exportTemplate(kind, id) { return raw(`/marketplace/templates/${encodeURIComponent(kind)}/${encodeURIComponent(id)}/export`); },
|
|
341
|
+
importTemplate(data) { return raw("/marketplace/templates/import", { method: "POST", body: { data } }); },
|
|
342
|
+
installTemplate(data) { return raw("/marketplace/templates/install", { method: "POST", body: { data } }); },
|
|
343
|
+
cloneTemplate(kind, id, name) { return raw(`/marketplace/templates/${encodeURIComponent(kind)}/${encodeURIComponent(id)}/clone`, { method: "POST", body: { name } }); },
|
|
344
|
+
pluginsRegistry() { return withFallback("/plugins/registry", {}, { plugins: [] }); },
|
|
345
|
+
pluginsDirectory() { return withFallback("/plugins/directory", {}, { plugins: [], categories: [] }); },
|
|
346
|
+
|
|
347
|
+
// Workflow Agents (Part 5)
|
|
348
|
+
workflowDefinitions() { return withFallback("/workflows/api/definitions", {}, { workflows: [] }); },
|
|
349
|
+
createWorkflow(body) { return raw("/workflows/api/definitions", { method: "POST", body }); },
|
|
350
|
+
runWorkflow(id, body = {}) { return raw(`/workflows/api/definitions/${encodeURIComponent(id)}/run`, { method: "POST", body }); },
|
|
351
|
+
workflowRuns() { return withFallback("/workflows/api/runs", {}, { runs: [] }); },
|
|
352
|
+
workflowReplay(runId) { return raw(`/workflows/api/runs/${encodeURIComponent(runId)}/replay`); },
|
|
353
|
+
|
|
354
|
+
// Long-Term Memory + Memory Manager (Parts 7, 8)
|
|
355
|
+
memoryManager() { return withFallback("/api/memory/manager", {}, { sources: [], tiers: [], usage: {} }); },
|
|
356
|
+
memoryTiers() { return withFallback("/api/memory/tiers", {}, { tiers: [], workspace_kinds: [] }); },
|
|
357
|
+
memoryInspect(source, limit = 50) { return withFallback(`/api/memory/inspect?source=${encodeURIComponent(source)}&limit=${limit}`, {}, { items: [] }); },
|
|
358
|
+
memoryRecall(query, limit = 20) { return raw("/api/memory/recall", { method: "POST", body: { query, limit } }); },
|
|
359
|
+
memoryPrune(body) { return raw("/api/memory/prune", { method: "POST", body }); },
|
|
360
|
+
memoryCompact() { return raw("/api/memory/compact", { method: "POST", body: {} }); },
|
|
361
|
+
memoryRebuild(target = "vector") { return raw("/api/memory/rebuild", { method: "POST", body: { target } }); },
|
|
362
|
+
memoryClear(scope, confirm = true) { return raw("/api/memory/clear", { method: "POST", body: { scope, confirm } }); },
|
|
363
|
+
workspaceMemories(kind) { return withFallback(`/workspace/memories${kind ? "?kind=" + encodeURIComponent(kind) : ""}`, {}, { memories: [] }); },
|
|
364
|
+
|
|
365
|
+
// Skills Registry (Part 9)
|
|
366
|
+
skills() { return withFallback("/workspace/skills", {}, { skills: [] }); },
|
|
367
|
+
skillEnable(skill) { return raw("/workspace/skills/enable", { method: "POST", body: { skill } }); },
|
|
368
|
+
skillDisable(skill) { return raw("/workspace/skills/disable", { method: "POST", body: { skill } }); },
|
|
369
|
+
skillInstall(skill, plugin) { return raw("/workspace/skills/install", { method: "POST", body: { skill, plugin: plugin || "" } }); },
|
|
370
|
+
skillUninstall(skill) { return raw("/workspace/skills/uninstall", { method: "POST", body: { skill } }); },
|
|
371
|
+
skillsMarketplace() { return withFallback("/skills/marketplace", {}, { skills: [], categories: [] }); },
|
|
372
|
+
|
|
373
|
+
// Hooks Registry (Part 10)
|
|
374
|
+
hooks(kind) { return withFallback(`/api/hooks${kind ? "?kind=" + encodeURIComponent(kind) : ""}`, {}, { hooks: [], kinds: [], counts: {} }); },
|
|
375
|
+
hookEnable(hook_id, enabled = true) { return raw("/api/hooks/enable", { method: "POST", body: { hook_id, enabled } }); },
|
|
376
|
+
hookDisable(hook_id) { return raw("/api/hooks/disable", { method: "POST", body: { hook_id, enabled: false } }); },
|
|
377
|
+
hookReorder(kind, ordered_ids) { return raw("/api/hooks/reorder", { method: "POST", body: { kind, ordered_ids } }); },
|
|
378
|
+
hookRegister(body) { return raw("/api/hooks/register", { method: "POST", body }); },
|
|
379
|
+
hookRemove(hook_id) { return raw(`/api/hooks/${encodeURIComponent(hook_id)}`, { method: "DELETE" }); },
|
|
380
|
+
|
|
381
|
+
// Tool Registry + MCP (Parts 11, 12)
|
|
382
|
+
toolPermissions() { return withFallback("/tools/permissions", {}, { permissions: [] }); },
|
|
383
|
+
mcpTools() { return withFallback("/mcp/tools", {}, { tools: [], installed_mcps: [] }); },
|
|
384
|
+
mcpInstalled() { return withFallback("/mcp/installed", {}, { installed: [] }); },
|
|
385
|
+
mcpClaudeServers() { return withFallback("/mcp/claude-code-servers", {}, { servers: [] }); },
|
|
386
|
+
mcpCustom() { return withFallback("/mcp/custom", {}, { custom: [] }); },
|
|
387
|
+
mcpRecommend(query, limit = 6) { return raw("/mcp/recommend", { method: "POST", body: { query, limit } }); },
|
|
324
388
|
};
|
|
325
389
|
|
|
326
390
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
package/static/v3/js/core/api.js
CHANGED
|
@@ -321,6 +321,70 @@ export const api = {
|
|
|
321
321
|
}
|
|
322
322
|
return { source: "live", text, trace, model };
|
|
323
323
|
},
|
|
324
|
+
|
|
325
|
+
/* ── v3.2 platform surfaces (all fallback-safe; never fabricate) ─────── */
|
|
326
|
+
|
|
327
|
+
// Agent Registry (Part 2)
|
|
328
|
+
agentRegistry(type) { return withFallback(`/agents/api/registry${type ? "?type=" + encodeURIComponent(type) : ""}`, {}, { agents: [], counts: {}, types: [] }); },
|
|
329
|
+
agentCapabilities() { return withFallback("/agents/api/registry/capabilities", {}, { capabilities: {} }); },
|
|
330
|
+
registerAgent(body) { return raw("/agents/api/registry", { method: "POST", body }); },
|
|
331
|
+
updateAgent(id, body) { return raw(`/agents/api/registry/${encodeURIComponent(id)}`, { method: "PATCH", body }); },
|
|
332
|
+
removeAgent(id) { return raw(`/agents/api/registry/${encodeURIComponent(id)}`, { method: "DELETE" }); },
|
|
333
|
+
agentRunDetail(runId) { return raw(`/agents/api/runs/${encodeURIComponent(runId)}`); },
|
|
334
|
+
agentRunReplay(runId) { return raw(`/agents/api/runs/${encodeURIComponent(runId)}/replay`); },
|
|
335
|
+
stopAgentRun(runId) { return raw(`/agents/api/runs/${encodeURIComponent(runId)}/stop`, { method: "POST" }); },
|
|
336
|
+
|
|
337
|
+
// Marketplace + Templates (Parts 3, 4)
|
|
338
|
+
templates(kind) { return withFallback(`/marketplace/templates${kind ? "?kind=" + encodeURIComponent(kind) : ""}`, {}, { templates: [], kinds: [] }); },
|
|
339
|
+
templateRegistry() { return withFallback("/marketplace/templates/registry", {}, { registry: [] }); },
|
|
340
|
+
exportTemplate(kind, id) { return raw(`/marketplace/templates/${encodeURIComponent(kind)}/${encodeURIComponent(id)}/export`); },
|
|
341
|
+
importTemplate(data) { return raw("/marketplace/templates/import", { method: "POST", body: { data } }); },
|
|
342
|
+
installTemplate(data) { return raw("/marketplace/templates/install", { method: "POST", body: { data } }); },
|
|
343
|
+
cloneTemplate(kind, id, name) { return raw(`/marketplace/templates/${encodeURIComponent(kind)}/${encodeURIComponent(id)}/clone`, { method: "POST", body: { name } }); },
|
|
344
|
+
pluginsRegistry() { return withFallback("/plugins/registry", {}, { plugins: [] }); },
|
|
345
|
+
pluginsDirectory() { return withFallback("/plugins/directory", {}, { plugins: [], categories: [] }); },
|
|
346
|
+
|
|
347
|
+
// Workflow Agents (Part 5)
|
|
348
|
+
workflowDefinitions() { return withFallback("/workflows/api/definitions", {}, { workflows: [] }); },
|
|
349
|
+
createWorkflow(body) { return raw("/workflows/api/definitions", { method: "POST", body }); },
|
|
350
|
+
runWorkflow(id, body = {}) { return raw(`/workflows/api/definitions/${encodeURIComponent(id)}/run`, { method: "POST", body }); },
|
|
351
|
+
workflowRuns() { return withFallback("/workflows/api/runs", {}, { runs: [] }); },
|
|
352
|
+
workflowReplay(runId) { return raw(`/workflows/api/runs/${encodeURIComponent(runId)}/replay`); },
|
|
353
|
+
|
|
354
|
+
// Long-Term Memory + Memory Manager (Parts 7, 8)
|
|
355
|
+
memoryManager() { return withFallback("/api/memory/manager", {}, { sources: [], tiers: [], usage: {} }); },
|
|
356
|
+
memoryTiers() { return withFallback("/api/memory/tiers", {}, { tiers: [], workspace_kinds: [] }); },
|
|
357
|
+
memoryInspect(source, limit = 50) { return withFallback(`/api/memory/inspect?source=${encodeURIComponent(source)}&limit=${limit}`, {}, { items: [] }); },
|
|
358
|
+
memoryRecall(query, limit = 20) { return raw("/api/memory/recall", { method: "POST", body: { query, limit } }); },
|
|
359
|
+
memoryPrune(body) { return raw("/api/memory/prune", { method: "POST", body }); },
|
|
360
|
+
memoryCompact() { return raw("/api/memory/compact", { method: "POST", body: {} }); },
|
|
361
|
+
memoryRebuild(target = "vector") { return raw("/api/memory/rebuild", { method: "POST", body: { target } }); },
|
|
362
|
+
memoryClear(scope, confirm = true) { return raw("/api/memory/clear", { method: "POST", body: { scope, confirm } }); },
|
|
363
|
+
workspaceMemories(kind) { return withFallback(`/workspace/memories${kind ? "?kind=" + encodeURIComponent(kind) : ""}`, {}, { memories: [] }); },
|
|
364
|
+
|
|
365
|
+
// Skills Registry (Part 9)
|
|
366
|
+
skills() { return withFallback("/workspace/skills", {}, { skills: [] }); },
|
|
367
|
+
skillEnable(skill) { return raw("/workspace/skills/enable", { method: "POST", body: { skill } }); },
|
|
368
|
+
skillDisable(skill) { return raw("/workspace/skills/disable", { method: "POST", body: { skill } }); },
|
|
369
|
+
skillInstall(skill, plugin) { return raw("/workspace/skills/install", { method: "POST", body: { skill, plugin: plugin || "" } }); },
|
|
370
|
+
skillUninstall(skill) { return raw("/workspace/skills/uninstall", { method: "POST", body: { skill } }); },
|
|
371
|
+
skillsMarketplace() { return withFallback("/skills/marketplace", {}, { skills: [], categories: [] }); },
|
|
372
|
+
|
|
373
|
+
// Hooks Registry (Part 10)
|
|
374
|
+
hooks(kind) { return withFallback(`/api/hooks${kind ? "?kind=" + encodeURIComponent(kind) : ""}`, {}, { hooks: [], kinds: [], counts: {} }); },
|
|
375
|
+
hookEnable(hook_id, enabled = true) { return raw("/api/hooks/enable", { method: "POST", body: { hook_id, enabled } }); },
|
|
376
|
+
hookDisable(hook_id) { return raw("/api/hooks/disable", { method: "POST", body: { hook_id, enabled: false } }); },
|
|
377
|
+
hookReorder(kind, ordered_ids) { return raw("/api/hooks/reorder", { method: "POST", body: { kind, ordered_ids } }); },
|
|
378
|
+
hookRegister(body) { return raw("/api/hooks/register", { method: "POST", body }); },
|
|
379
|
+
hookRemove(hook_id) { return raw(`/api/hooks/${encodeURIComponent(hook_id)}`, { method: "DELETE" }); },
|
|
380
|
+
|
|
381
|
+
// Tool Registry + MCP (Parts 11, 12)
|
|
382
|
+
toolPermissions() { return withFallback("/tools/permissions", {}, { permissions: [] }); },
|
|
383
|
+
mcpTools() { return withFallback("/mcp/tools", {}, { tools: [], installed_mcps: [] }); },
|
|
384
|
+
mcpInstalled() { return withFallback("/mcp/installed", {}, { installed: [] }); },
|
|
385
|
+
mcpClaudeServers() { return withFallback("/mcp/claude-code-servers", {}, { servers: [] }); },
|
|
386
|
+
mcpCustom() { return withFallback("/mcp/custom", {}, { custom: [] }); },
|
|
387
|
+
mcpRecommend(query, limit = 6) { return raw("/mcp/recommend", { method: "POST", body: { query, limit } }); },
|
|
324
388
|
};
|
|
325
389
|
|
|
326
390
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|