ltcai 0.5.0 → 0.6.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 +10 -8
- package/docs/CHANGELOG.md +59 -0
- package/kg_schema.py +179 -518
- package/knowledge_graph.py +183 -80
- package/latticeai/core/agent.py +2 -2
- package/latticeai/core/agent_prompts.py +101 -0
- package/latticeai/core/tool_registry.py +288 -0
- package/latticeai/server_app.py +5806 -0
- package/package.json +2 -2
- package/server.py +13 -6259
- package/tools.py +6 -5
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
"""Tool dispatch, governance, and catalog metadata.
|
|
2
|
+
|
|
3
|
+
The registry is the single ownership point for tool names: one object exposes
|
|
4
|
+
dispatch, policy lookup, prompt catalog text, MCP descriptions, and permission
|
|
5
|
+
views. The actual tool functions still live in the top-level ``tools`` module
|
|
6
|
+
to preserve the public API and keep this module free of filesystem side
|
|
7
|
+
effects at import time.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from dataclasses import dataclass, field
|
|
13
|
+
from typing import Any, Callable, Dict, Mapping, Optional, TypedDict
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ToolPolicy(TypedDict):
|
|
17
|
+
risk: str
|
|
18
|
+
destructive: bool
|
|
19
|
+
shell: bool
|
|
20
|
+
network: bool
|
|
21
|
+
auto_approve: bool
|
|
22
|
+
sandbox: str
|
|
23
|
+
rollback: str
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ToolPermission(TypedDict):
|
|
27
|
+
tool: str
|
|
28
|
+
risk: str
|
|
29
|
+
requires_approval: bool
|
|
30
|
+
network: bool
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
TOOL_CATALOG_BRIEF = """
|
|
34
|
+
FILESYSTEM : list_dir workspace_tree read_file write_file edit_file grep search_files inspect_html preview_url
|
|
35
|
+
PLANNING : todo_read todo_write
|
|
36
|
+
PROJECT : run_command build_project deploy_project create_web_project
|
|
37
|
+
GIT (read) : git_status git_diff git_log git_show
|
|
38
|
+
LOCAL FS : local_list local_read local_write read_document
|
|
39
|
+
DOCS : create_docx create_xlsx create_pptx create_pdf
|
|
40
|
+
KNOWLEDGE : knowledge_save knowledge_search knowledge_tree
|
|
41
|
+
COMPUTER : computer_screenshot computer_open_app computer_open_url computer_click computer_type computer_key
|
|
42
|
+
MISC : network_status clear_history final
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
FILE_CREATE_ACTIONS = frozenset({
|
|
46
|
+
"create_docx",
|
|
47
|
+
"create_xlsx",
|
|
48
|
+
"create_pptx",
|
|
49
|
+
"create_pdf",
|
|
50
|
+
"write_file",
|
|
51
|
+
"edit_file",
|
|
52
|
+
"create_web_project",
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
LOCAL_WRITE_BLOCKED_PREFIXES = (
|
|
56
|
+
"/etc/",
|
|
57
|
+
"/usr/",
|
|
58
|
+
"/bin/",
|
|
59
|
+
"/sbin/",
|
|
60
|
+
"/System/",
|
|
61
|
+
"/private/etc/",
|
|
62
|
+
"/Library/LaunchDaemons/",
|
|
63
|
+
"/Library/LaunchAgents/",
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
RISK_LEVEL_MAP = {
|
|
67
|
+
"read": "low",
|
|
68
|
+
"write": "medium",
|
|
69
|
+
"exec": "high",
|
|
70
|
+
"destructive": "high",
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _r(sandbox: str = "workspace", rollback: str = "none") -> ToolPolicy:
|
|
75
|
+
return ToolPolicy(
|
|
76
|
+
risk="read", destructive=False, shell=False, network=False,
|
|
77
|
+
auto_approve=True, sandbox=sandbox, rollback=rollback,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _rs(sandbox: str = "workspace", rollback: str = "none") -> ToolPolicy:
|
|
82
|
+
return ToolPolicy(
|
|
83
|
+
risk="read", destructive=False, shell=True, network=False,
|
|
84
|
+
auto_approve=True, sandbox=sandbox, rollback=rollback,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _rn(sandbox: str = "system", rollback: str = "none") -> ToolPolicy:
|
|
89
|
+
return ToolPolicy(
|
|
90
|
+
risk="read", destructive=False, shell=True, network=True,
|
|
91
|
+
auto_approve=True, sandbox=sandbox, rollback=rollback,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _w(sandbox: str = "workspace", rollback: str = "none") -> ToolPolicy:
|
|
96
|
+
return ToolPolicy(
|
|
97
|
+
risk="write", destructive=False, shell=False, network=False,
|
|
98
|
+
auto_approve=False, sandbox=sandbox, rollback=rollback,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _e(sandbox: str = "workspace", rollback: str = "none") -> ToolPolicy:
|
|
103
|
+
return ToolPolicy(
|
|
104
|
+
risk="exec", destructive=False, shell=True, network=False,
|
|
105
|
+
auto_approve=False, sandbox=sandbox, rollback=rollback,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _en(sandbox: str = "workspace", rollback: str = "none") -> ToolPolicy:
|
|
110
|
+
return ToolPolicy(
|
|
111
|
+
risk="exec", destructive=False, shell=True, network=True,
|
|
112
|
+
auto_approve=False, sandbox=sandbox, rollback=rollback,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _ec(sandbox: str = "system", rollback: str = "none") -> ToolPolicy:
|
|
117
|
+
return ToolPolicy(
|
|
118
|
+
risk="exec", destructive=False, shell=False, network=False,
|
|
119
|
+
auto_approve=False, sandbox=sandbox, rollback=rollback,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
TOOL_GOVERNANCE: Dict[str, ToolPolicy] = {
|
|
124
|
+
"list_dir": _r(),
|
|
125
|
+
"workspace_tree": _r(),
|
|
126
|
+
"read_file": _r(),
|
|
127
|
+
"search_files": _r(),
|
|
128
|
+
"grep": _r(),
|
|
129
|
+
"inspect_html": _r(),
|
|
130
|
+
"todo_read": _r(),
|
|
131
|
+
"local_list": _r(sandbox="home"),
|
|
132
|
+
"local_read": _r(sandbox="home"),
|
|
133
|
+
"git_status": _rs(),
|
|
134
|
+
"git_diff": _rs(),
|
|
135
|
+
"git_log": _rs(),
|
|
136
|
+
"git_show": _rs(),
|
|
137
|
+
"knowledge_search": _r(sandbox="home"),
|
|
138
|
+
"knowledge_tree": _r(sandbox="home"),
|
|
139
|
+
"obsidian_search": _r(sandbox="home"),
|
|
140
|
+
"obsidian_tree": _r(sandbox="home"),
|
|
141
|
+
"computer_screenshot": _r(sandbox="system"),
|
|
142
|
+
"computer_status": _r(sandbox="system"),
|
|
143
|
+
"chrome_status": _r(sandbox="system"),
|
|
144
|
+
"computer_use_status": _r(sandbox="system"),
|
|
145
|
+
"network_status": _rn(),
|
|
146
|
+
"write_file": _w(rollback="git"),
|
|
147
|
+
"edit_file": _w(rollback="git"),
|
|
148
|
+
"create_web_project": _w(),
|
|
149
|
+
"create_docx": _w(),
|
|
150
|
+
"create_xlsx": _w(),
|
|
151
|
+
"create_pptx": _w(),
|
|
152
|
+
"create_pdf": _w(),
|
|
153
|
+
"preview_url": _w(),
|
|
154
|
+
"todo_write": _w(),
|
|
155
|
+
"knowledge_save": _w(sandbox="home"),
|
|
156
|
+
"obsidian_save": _w(sandbox="home"),
|
|
157
|
+
"local_write": _w(sandbox="home"),
|
|
158
|
+
"run_command": _e(),
|
|
159
|
+
"build_project": _e(),
|
|
160
|
+
"deploy_project": _en(),
|
|
161
|
+
"computer_click": _ec(),
|
|
162
|
+
"computer_type": _ec(),
|
|
163
|
+
"computer_key": _ec(),
|
|
164
|
+
"computer_scroll": _ec(),
|
|
165
|
+
"computer_drag": _ec(),
|
|
166
|
+
"computer_move": _ec(),
|
|
167
|
+
"computer_open_app": _ec(),
|
|
168
|
+
"computer_open_url": ToolPolicy(
|
|
169
|
+
risk="exec", destructive=False, shell=False, network=True,
|
|
170
|
+
auto_approve=False, sandbox="system", rollback="none",
|
|
171
|
+
),
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
TOOL_GOVERNANCE_DEFAULT = ToolPolicy(
|
|
175
|
+
risk="write", destructive=False, shell=False, network=False,
|
|
176
|
+
auto_approve=False, sandbox="workspace", rollback="none",
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
MCP_TOOL_DESCRIPTIONS: Dict[str, str] = {
|
|
180
|
+
"list_dir": "List files in the agent workspace.",
|
|
181
|
+
"workspace_tree": "Return a recursive workspace tree.",
|
|
182
|
+
"read_file": "Read a UTF-8 file from the workspace with optional line numbers and offset/limit slicing.",
|
|
183
|
+
"write_file": "Write a UTF-8 file inside the workspace (new files / full rewrites).",
|
|
184
|
+
"edit_file": "Precise diff-style edit: replace exact old_string with new_string. Requires unique match unless replace_all=true.",
|
|
185
|
+
"search_files": "Substring search in text files (legacy).",
|
|
186
|
+
"grep": "Regex search across the workspace with line numbers and optional context.",
|
|
187
|
+
"todo_read": "Read the agent's persistent TODO list for the current workspace.",
|
|
188
|
+
"todo_write": "Replace the agent's TODO list (id, content, status: pending/in_progress/completed).",
|
|
189
|
+
"clear_history": "Clear chat history to reduce context and speed up responses.",
|
|
190
|
+
"inspect_html": "Inspect local HTML structure and assets.",
|
|
191
|
+
"preview_url": "Return a server URL for a workspace file.",
|
|
192
|
+
"create_docx": "Create a Word DOCX document in the agent workspace.",
|
|
193
|
+
"create_xlsx": "Create an XLSX spreadsheet in the agent workspace.",
|
|
194
|
+
"create_pptx": "Create a PPTX presentation deck in the agent workspace.",
|
|
195
|
+
"create_pdf": "Create a PDF document in the agent workspace.",
|
|
196
|
+
"local_list": "List any local folder (requires user permission via UI).",
|
|
197
|
+
"local_read": "Read any local file (requires user permission via UI).",
|
|
198
|
+
"local_write": "Write any local file (requires user permission via UI).",
|
|
199
|
+
"read_document": "Extract text from PDF, DOCX, XLSX, PPTX, TXT, MD, CSV files.",
|
|
200
|
+
"computer_screenshot": "Capture the current Mac screen as base64 PNG.",
|
|
201
|
+
"computer_open_app": "Open or focus a Mac app, e.g. Google Chrome.",
|
|
202
|
+
"computer_open_url": "Open a URL in a Mac app, e.g. Google Chrome.",
|
|
203
|
+
"computer_click": "Click at screen coordinates (x, y).",
|
|
204
|
+
"computer_type": "Type text at the current focus position.",
|
|
205
|
+
"computer_key": "Press a keyboard key or shortcut (e.g. 'command+c').",
|
|
206
|
+
"computer_scroll": "Scroll at screen coordinates.",
|
|
207
|
+
"computer_move": "Move the mouse to screen coordinates.",
|
|
208
|
+
"computer_drag": "Drag from (x1,y1) to (x2,y2).",
|
|
209
|
+
"computer_status": "Check if Mac desktop control (pyautogui) is available.",
|
|
210
|
+
"chrome_status": "Report Chrome desktop bridge availability.",
|
|
211
|
+
"computer_use_status": "Report Mac desktop-control bridge availability.",
|
|
212
|
+
"knowledge_save": "Save a note into the local knowledge garden.",
|
|
213
|
+
"knowledge_search": "Search the local knowledge garden.",
|
|
214
|
+
"knowledge_tree": "List local knowledge garden markdown files.",
|
|
215
|
+
"knowledge_graph_ingest": "Ingest a message, AI answer, or connector event into the SQLite knowledge graph.",
|
|
216
|
+
"knowledge_graph_search": "Search graph nodes, summaries, and JSON metadata.",
|
|
217
|
+
"knowledge_graph_graph": "Return Obsidian-style graph nodes and edges.",
|
|
218
|
+
"knowledge_graph_context": "Return compact graph-backed RAG context for a prompt.",
|
|
219
|
+
"obsidian_save": "Save a note into the Obsidian-compatible memory vault.",
|
|
220
|
+
"obsidian_search": "Search the Obsidian-compatible memory vault.",
|
|
221
|
+
"obsidian_tree": "List Obsidian memory vault markdown files.",
|
|
222
|
+
"git_status": "Read-only local git status inside the workspace.",
|
|
223
|
+
"git_diff": "Read-only local git diff inside the workspace.",
|
|
224
|
+
"git_log": "Read-only local git log inside the workspace.",
|
|
225
|
+
"git_show": "Read-only local git show --stat inside the workspace.",
|
|
226
|
+
"network_status": "Get current local/private IP, public IP, hostname, and Wi-Fi info.",
|
|
227
|
+
"run_command": "Run an allowlisted local command inside the workspace.",
|
|
228
|
+
"build_project": "Run an allowlisted package.json build/compile/typecheck/test script to verify changes actually work.",
|
|
229
|
+
"deploy_project": "Run an allowlisted package.json deploy/preview/release/package installer script (pkg/exe).",
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
@dataclass
|
|
234
|
+
class ToolRegistry:
|
|
235
|
+
handlers: Mapping[str, Callable[[Dict[str, Any]], Dict[str, Any]]]
|
|
236
|
+
governance: Mapping[str, ToolPolicy] = field(default_factory=lambda: TOOL_GOVERNANCE)
|
|
237
|
+
default_policy: ToolPolicy = field(default_factory=lambda: TOOL_GOVERNANCE_DEFAULT)
|
|
238
|
+
descriptions: Mapping[str, str] = field(default_factory=lambda: MCP_TOOL_DESCRIPTIONS)
|
|
239
|
+
catalog_brief: str = TOOL_CATALOG_BRIEF
|
|
240
|
+
file_create_actions: frozenset[str] = FILE_CREATE_ACTIONS
|
|
241
|
+
local_write_blocked_prefixes: tuple[str, ...] = LOCAL_WRITE_BLOCKED_PREFIXES
|
|
242
|
+
risk_level_map: Mapping[str, str] = field(default_factory=lambda: RISK_LEVEL_MAP)
|
|
243
|
+
|
|
244
|
+
@property
|
|
245
|
+
def admin_only_tools(self) -> frozenset[str]:
|
|
246
|
+
return frozenset(
|
|
247
|
+
name for name, policy in self.governance.items()
|
|
248
|
+
if policy["sandbox"] == "system" or policy["risk"] in {"exec", "destructive"}
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
def registered_tools(self) -> frozenset[str]:
|
|
252
|
+
return frozenset(self.handlers)
|
|
253
|
+
|
|
254
|
+
def execute(self, action: str, args: Dict[str, Any], *, error_cls: type[Exception]) -> Dict[str, Any]:
|
|
255
|
+
handler = self.handlers.get(action)
|
|
256
|
+
if handler is None:
|
|
257
|
+
raise error_cls(f"Unknown action: {action}")
|
|
258
|
+
return handler(args or {})
|
|
259
|
+
|
|
260
|
+
def policy_for(self, action_name: str, args: Optional[dict] = None) -> ToolPolicy:
|
|
261
|
+
policy = self.governance.get(action_name, self.default_policy)
|
|
262
|
+
if action_name == "local_write":
|
|
263
|
+
path = str((args or {}).get("path", ""))
|
|
264
|
+
if any(path.startswith(prefix) for prefix in self.local_write_blocked_prefixes):
|
|
265
|
+
return ToolPolicy(
|
|
266
|
+
risk="destructive", destructive=True, shell=False, network=False,
|
|
267
|
+
auto_approve=False, sandbox="system", rollback="none",
|
|
268
|
+
)
|
|
269
|
+
return policy
|
|
270
|
+
|
|
271
|
+
def risk_level(self, policy_or_action: ToolPolicy | str, args: Optional[dict] = None) -> str:
|
|
272
|
+
if isinstance(policy_or_action, str):
|
|
273
|
+
policy = self.policy_for(policy_or_action, args or {})
|
|
274
|
+
else:
|
|
275
|
+
policy = policy_or_action
|
|
276
|
+
return self.risk_level_map.get(policy["risk"], "medium")
|
|
277
|
+
|
|
278
|
+
def permission(self, name: str, args: Optional[dict] = None) -> ToolPermission:
|
|
279
|
+
policy = self.policy_for(name, args or {})
|
|
280
|
+
return ToolPermission(
|
|
281
|
+
tool=name,
|
|
282
|
+
risk=self.risk_level(policy),
|
|
283
|
+
requires_approval=not policy["auto_approve"],
|
|
284
|
+
network=policy["network"],
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
def permissions(self) -> list[ToolPermission]:
|
|
288
|
+
return [self.permission(name) for name in sorted(self.governance.keys())]
|