ltcai 3.4.1 → 3.5.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 +173 -248
- package/docs/RUNTIME_HOOK_COVERAGE_v3.5.0.md +56 -0
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/auth.py +37 -9
- package/latticeai/api/chat.py +4 -1
- package/latticeai/api/computer_use.py +21 -8
- package/latticeai/api/tools.py +29 -26
- package/latticeai/core/config.py +3 -0
- package/latticeai/core/marketplace.py +1 -1
- package/latticeai/core/multi_agent.py +1 -1
- package/latticeai/core/oidc.py +205 -0
- package/latticeai/core/security.py +59 -5
- package/latticeai/core/workspace_os.py +1 -1
- package/latticeai/server_app.py +7 -0
- package/package.json +3 -3
- package/requirements.txt +1 -0
- package/scripts/check_python.py +87 -0
- package/static/css/reference/account.css +1 -1
- package/static/css/reference/admin.css +1 -1
- package/static/css/reference/base.css +8 -5
- package/static/css/reference/chat.css +8 -8
- package/static/css/reference/graph.css +2 -2
- package/static/css/responsive.css +2 -2
- package/static/v3/asset-manifest.json +3 -3
- package/static/v3/css/{lattice.shell.6ceea7c8.css → lattice.shell.8fcc9d33.css} +2 -1
- package/static/v3/css/lattice.shell.css +2 -1
- package/static/workspace.css +1 -1
- package/tools/__init__.py +276 -0
- package/tools/commands.py +188 -0
- package/tools/computer.py +185 -0
- package/tools/documents.py +243 -0
- package/tools/filesystem.py +560 -0
- package/tools/knowledge.py +97 -0
- package/tools/local_files.py +69 -0
- package/tools/network.py +66 -0
- package/tools.py +0 -1525
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Discover-and-compile every first-party Python module.
|
|
3
|
+
|
|
4
|
+
Replaces the hand-maintained ``py_compile`` enumeration in CI and
|
|
5
|
+
``package.json``: walks the repository, skips vendored / virtualenv / build /
|
|
6
|
+
cache / generated directories, and byte-compiles everything that remains. New
|
|
7
|
+
modules are picked up automatically — there is nothing to update when a file is
|
|
8
|
+
added, so the syntax gate can never silently fall behind the codebase.
|
|
9
|
+
|
|
10
|
+
Usage::
|
|
11
|
+
|
|
12
|
+
python scripts/check_python.py # compile all discovered modules
|
|
13
|
+
python scripts/check_python.py --list # just print what would be compiled
|
|
14
|
+
|
|
15
|
+
Exit code is non-zero if any module fails to compile.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import py_compile
|
|
21
|
+
import sys
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
ROOT = Path(__file__).resolve().parent.parent
|
|
25
|
+
|
|
26
|
+
# Directory names excluded anywhere in the tree: virtualenvs, build/cache
|
|
27
|
+
# artifacts, generated agent output, and vendored snapshots of older releases.
|
|
28
|
+
EXCLUDE_DIRS = {
|
|
29
|
+
".git",
|
|
30
|
+
".venv",
|
|
31
|
+
"venv",
|
|
32
|
+
"env",
|
|
33
|
+
".build-venv",
|
|
34
|
+
".npm-cache",
|
|
35
|
+
"build",
|
|
36
|
+
"dist",
|
|
37
|
+
"node_modules",
|
|
38
|
+
"__pycache__",
|
|
39
|
+
".pytest_cache",
|
|
40
|
+
".mypy_cache",
|
|
41
|
+
".ruff_cache",
|
|
42
|
+
"agent_workspace",
|
|
43
|
+
"outputs",
|
|
44
|
+
"playwright-report",
|
|
45
|
+
"test-results",
|
|
46
|
+
"ltcai.egg-info",
|
|
47
|
+
".ltcai",
|
|
48
|
+
".ltcai-brain",
|
|
49
|
+
".ltcai-test",
|
|
50
|
+
# Vendored snapshot of an older packaged release — not part of the build.
|
|
51
|
+
"ltcai-0.3.1",
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def iter_modules():
|
|
56
|
+
for path in ROOT.rglob("*.py"):
|
|
57
|
+
parts = path.relative_to(ROOT).parts
|
|
58
|
+
if any(part in EXCLUDE_DIRS for part in parts):
|
|
59
|
+
continue
|
|
60
|
+
yield path
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def main(argv: list[str]) -> int:
|
|
64
|
+
modules = sorted(iter_modules())
|
|
65
|
+
if "--list" in argv:
|
|
66
|
+
for path in modules:
|
|
67
|
+
print(path.relative_to(ROOT))
|
|
68
|
+
return 0
|
|
69
|
+
|
|
70
|
+
failures: list[str] = []
|
|
71
|
+
for path in modules:
|
|
72
|
+
try:
|
|
73
|
+
py_compile.compile(str(path), doraise=True)
|
|
74
|
+
except py_compile.PyCompileError as exc:
|
|
75
|
+
failures.append(str(exc))
|
|
76
|
+
|
|
77
|
+
if failures:
|
|
78
|
+
print("\n".join(failures))
|
|
79
|
+
print(f"check:python FAILED — {len(failures)} of {len(modules)} module(s) did not compile")
|
|
80
|
+
return 1
|
|
81
|
+
|
|
82
|
+
print(f"check:python OK — compiled {len(modules)} modules")
|
|
83
|
+
return 0
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
if __name__ == "__main__":
|
|
87
|
+
raise SystemExit(main(sys.argv[1:]))
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
background: var(--card);
|
|
47
47
|
border-color: var(--accent-soft);
|
|
48
48
|
box-shadow: 0 20px 64px rgba(102, 82, 168, 0.20), inset 0 1px 0 rgba(255,255,255,0.86);
|
|
49
|
-
backdrop-filter:
|
|
49
|
+
backdrop-filter: none; /* glass removed v3.5.0 */
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
.lattice-ref-auth .card::before {
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
color: var(--ref-ink);
|
|
66
66
|
background: var(--surface);
|
|
67
67
|
border-bottom: 1px solid rgba(111,66,232,0.08);
|
|
68
|
-
backdrop-filter:
|
|
68
|
+
backdrop-filter: none; /* glass removed v3.5.0 */
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
.auth-window-brand {
|
|
@@ -712,7 +712,10 @@
|
|
|
712
712
|
font-weight: 700;
|
|
713
713
|
background: transparent;
|
|
714
714
|
cursor: pointer;
|
|
715
|
-
transition
|
|
715
|
+
/* Animate surface affordances, but never `color`: a color transition on
|
|
716
|
+
theme switch would briefly render the rail link dark-on-dark (the v226
|
|
717
|
+
dark-mode contrast gate caught this on slow CI). Color flips instantly. */
|
|
718
|
+
transition: background-color 160ms ease, border-color 160ms ease, transform 160ms ease;
|
|
716
719
|
}
|
|
717
720
|
|
|
718
721
|
.reference-rail a.active,
|
|
@@ -842,7 +845,7 @@
|
|
|
842
845
|
background: var(--sidebar);
|
|
843
846
|
border-bottom: 1px solid rgba(111,66,232,0.11);
|
|
844
847
|
box-shadow: 0 2px 12px rgba(88,72,150,0.06);
|
|
845
|
-
backdrop-filter:
|
|
848
|
+
backdrop-filter: none; /* glass removed v3.5.0 */
|
|
846
849
|
}
|
|
847
850
|
|
|
848
851
|
.lattice-ref-chat .messages-viewport {
|
|
@@ -1295,7 +1298,7 @@
|
|
|
1295
1298
|
height: 96px;
|
|
1296
1299
|
background: var(--sidebar);
|
|
1297
1300
|
border-bottom: 1px solid rgba(111,66,232,0.10);
|
|
1298
|
-
backdrop-filter:
|
|
1301
|
+
backdrop-filter: none; /* glass removed v3.5.0 */
|
|
1299
1302
|
padding: 22px 34px 8px;
|
|
1300
1303
|
position: sticky;
|
|
1301
1304
|
}
|
|
@@ -81,8 +81,8 @@
|
|
|
81
81
|
display: flex;
|
|
82
82
|
flex-direction: column;
|
|
83
83
|
min-width: 240px;
|
|
84
|
-
backdrop-filter:
|
|
85
|
-
-webkit-backdrop-filter:
|
|
84
|
+
backdrop-filter: none; /* glass removed v3.5.0 */
|
|
85
|
+
-webkit-backdrop-filter: none; /* glass removed v3.5.0 */
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
.sidebar-header {
|
|
@@ -342,8 +342,8 @@
|
|
|
342
342
|
padding: 10px 22px;
|
|
343
343
|
border-bottom: 1px solid rgba(111,66,232,0.10);
|
|
344
344
|
background: var(--sidebar);
|
|
345
|
-
backdrop-filter:
|
|
346
|
-
-webkit-backdrop-filter:
|
|
345
|
+
backdrop-filter: none; /* glass removed v3.5.0 */
|
|
346
|
+
-webkit-backdrop-filter: none; /* glass removed v3.5.0 */
|
|
347
347
|
position: relative;
|
|
348
348
|
z-index: 50;
|
|
349
349
|
}
|
|
@@ -636,7 +636,7 @@
|
|
|
636
636
|
justify-content: space-between;
|
|
637
637
|
gap: 12px;
|
|
638
638
|
box-shadow: var(--shadow-sm), inset 0 1px 0 rgba(255,255,255,0.80);
|
|
639
|
-
backdrop-filter:
|
|
639
|
+
backdrop-filter: none; /* glass removed v3.5.0 */
|
|
640
640
|
transition: all .2s;
|
|
641
641
|
}
|
|
642
642
|
|
|
@@ -1323,7 +1323,7 @@
|
|
|
1323
1323
|
box-shadow: var(--shadow), 0 0 0 1px rgba(111,66,232,0.05);
|
|
1324
1324
|
position: relative;
|
|
1325
1325
|
z-index: 1;
|
|
1326
|
-
backdrop-filter:
|
|
1326
|
+
backdrop-filter: none; /* glass removed v3.5.0 */
|
|
1327
1327
|
}
|
|
1328
1328
|
|
|
1329
1329
|
.auth-card::before {
|
|
@@ -2754,7 +2754,7 @@
|
|
|
2754
2754
|
.admin-panel {
|
|
2755
2755
|
background: var(--card);
|
|
2756
2756
|
border-left: 1px solid var(--border);
|
|
2757
|
-
backdrop-filter:
|
|
2757
|
+
backdrop-filter: none; /* glass removed v3.5.0 */
|
|
2758
2758
|
color: var(--text);
|
|
2759
2759
|
}
|
|
2760
2760
|
.admin-header {
|
|
@@ -4254,7 +4254,7 @@
|
|
|
4254
4254
|
background: var(--sidebar);
|
|
4255
4255
|
border-bottom: 1px solid var(--line);
|
|
4256
4256
|
box-shadow: none;
|
|
4257
|
-
backdrop-filter:
|
|
4257
|
+
backdrop-filter: none; /* glass removed v3.5.0 */
|
|
4258
4258
|
}
|
|
4259
4259
|
|
|
4260
4260
|
/* Messages viewport */
|
|
@@ -120,7 +120,7 @@
|
|
|
120
120
|
z-index: 20;
|
|
121
121
|
border: 1px solid var(--line);
|
|
122
122
|
background: var(--panel);
|
|
123
|
-
backdrop-filter:
|
|
123
|
+
backdrop-filter: none; /* glass removed v3.5.0 */
|
|
124
124
|
box-shadow: var(--shadow);
|
|
125
125
|
}
|
|
126
126
|
|
|
@@ -904,7 +904,7 @@
|
|
|
904
904
|
border-radius: 10px;
|
|
905
905
|
background: var(--surface-2);
|
|
906
906
|
box-shadow: var(--shadow);
|
|
907
|
-
backdrop-filter:
|
|
907
|
+
backdrop-filter: none; /* glass removed v3.5.0 */
|
|
908
908
|
}
|
|
909
909
|
|
|
910
910
|
.focus-chip span {
|
|
@@ -319,8 +319,8 @@ select {
|
|
|
319
319
|
z-index: 99;
|
|
320
320
|
display: none;
|
|
321
321
|
background: rgba(15, 12, 30, 0.42);
|
|
322
|
-
-webkit-backdrop-filter:
|
|
323
|
-
backdrop-filter:
|
|
322
|
+
-webkit-backdrop-filter: none; /* glass removed v3.5.0 */
|
|
323
|
+
backdrop-filter: none; /* glass removed v3.5.0 */
|
|
324
324
|
}
|
|
325
325
|
body.sidebar-open .sidebar-overlay { display: block; }
|
|
326
326
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "3.
|
|
2
|
+
"version": "3.5.0",
|
|
3
3
|
"generated_at": "deterministic",
|
|
4
4
|
"entrypoints": {
|
|
5
5
|
"app": "/static/v3/js/app.d086489d.js",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"/static/v3/css/lattice.tokens.e7018963.css",
|
|
9
9
|
"/static/v3/css/lattice.base.e4cdd05d.css",
|
|
10
10
|
"/static/v3/css/lattice.components.9b49d614.css",
|
|
11
|
-
"/static/v3/css/lattice.shell.
|
|
11
|
+
"/static/v3/css/lattice.shell.8fcc9d33.css",
|
|
12
12
|
"/static/v3/css/lattice.views.22f69117.css"
|
|
13
13
|
]
|
|
14
14
|
},
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"static/v3/css/lattice.tokens.css": "/static/v3/css/lattice.tokens.e7018963.css",
|
|
18
18
|
"static/v3/css/lattice.base.css": "/static/v3/css/lattice.base.e4cdd05d.css",
|
|
19
19
|
"static/v3/css/lattice.components.css": "/static/v3/css/lattice.components.9b49d614.css",
|
|
20
|
-
"static/v3/css/lattice.shell.css": "/static/v3/css/lattice.shell.
|
|
20
|
+
"static/v3/css/lattice.shell.css": "/static/v3/css/lattice.shell.8fcc9d33.css",
|
|
21
21
|
"static/v3/css/lattice.views.css": "/static/v3/css/lattice.views.22f69117.css",
|
|
22
22
|
"static/v3/js/app.js": "/static/v3/js/app.d086489d.js",
|
|
23
23
|
"static/v3/js/core/api.js": "/static/v3/js/core/api.12b568ad.js",
|
|
@@ -349,8 +349,9 @@
|
|
|
349
349
|
.lt3-scrim {
|
|
350
350
|
position: fixed; inset: 0;
|
|
351
351
|
z-index: var(--lt3-z-scrim);
|
|
352
|
+
/* Solid dim scrim — no backdrop blur (glassmorphism removed in v3.5.0 for a
|
|
353
|
+
crisp, stable surface). */
|
|
352
354
|
background: var(--overlay);
|
|
353
|
-
backdrop-filter: blur(2px);
|
|
354
355
|
opacity: 0;
|
|
355
356
|
animation: lt3-fade var(--lt3-dur-2) var(--lt3-ease) forwards;
|
|
356
357
|
}
|
|
@@ -349,8 +349,9 @@
|
|
|
349
349
|
.lt3-scrim {
|
|
350
350
|
position: fixed; inset: 0;
|
|
351
351
|
z-index: var(--lt3-z-scrim);
|
|
352
|
+
/* Solid dim scrim — no backdrop blur (glassmorphism removed in v3.5.0 for a
|
|
353
|
+
crisp, stable surface). */
|
|
352
354
|
background: var(--overlay);
|
|
353
|
-
backdrop-filter: blur(2px);
|
|
354
355
|
opacity: 0;
|
|
355
356
|
animation: lt3-fade var(--lt3-dur-2) var(--lt3-ease) forwards;
|
|
356
357
|
}
|
package/static/workspace.css
CHANGED
|
@@ -873,7 +873,7 @@ main {
|
|
|
873
873
|
padding: 16px clamp(16px, 2vw, 28px);
|
|
874
874
|
background: color-mix(in srgb, var(--surface-elevated) 94%, transparent);
|
|
875
875
|
border-bottom: 1px solid var(--line);
|
|
876
|
-
backdrop-filter:
|
|
876
|
+
backdrop-filter: none; /* glass removed v3.5.0 */
|
|
877
877
|
}
|
|
878
878
|
|
|
879
879
|
.topbar-subtitle {
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
"""Safe local tools for Lattice AI agent mode.
|
|
2
|
+
|
|
3
|
+
All filesystem operations are confined to LATTICEAI_AGENT_ROOT, defaulting to
|
|
4
|
+
./agent_workspace. Command execution runs without a shell and from inside that
|
|
5
|
+
workspace.
|
|
6
|
+
|
|
7
|
+
v3.5.0 splits the historical flat ``tools.py`` into focused submodules
|
|
8
|
+
(computer, filesystem, documents, local_files, knowledge, network, commands)
|
|
9
|
+
while keeping the exact import surface: this package re-exports every public name
|
|
10
|
+
and ``execute_tool``/``DEFAULT_TOOL_REGISTRY``, so ``import tools`` and
|
|
11
|
+
``from tools import X`` behave identically to before. ``AGENT_ROOT`` and the path
|
|
12
|
+
helpers live here as the single source of truth (tests monkeypatch
|
|
13
|
+
``tools.AGENT_ROOT``); submodules read it dynamically.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import base64
|
|
17
|
+
import json
|
|
18
|
+
import os
|
|
19
|
+
import platform
|
|
20
|
+
import re
|
|
21
|
+
import shlex
|
|
22
|
+
import socket
|
|
23
|
+
import subprocess
|
|
24
|
+
import tempfile
|
|
25
|
+
from html.parser import HTMLParser
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
from typing import Any, Callable, Dict, List, Optional
|
|
28
|
+
|
|
29
|
+
from latticeai.core.tool_registry import ToolRegistry
|
|
30
|
+
from p_reinforce import BRAIN_DIR, STRUCTURE
|
|
31
|
+
|
|
32
|
+
_PLATFORM = platform.system() # "Darwin" | "Windows" | "Linux"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# ── base: agent-root sandbox, shared constants, path helpers ──────────────────
|
|
36
|
+
AGENT_ROOT = Path(os.getenv("LATTICEAI_AGENT_ROOT") or "agent_workspace").resolve()
|
|
37
|
+
MAX_FILE_BYTES = 512_000
|
|
38
|
+
MAX_COMMAND_SECONDS = 30
|
|
39
|
+
MAX_BUILD_SECONDS = 180
|
|
40
|
+
MAX_DEPLOY_SECONDS = 300
|
|
41
|
+
MAX_COMMAND_OUTPUT = 12_000
|
|
42
|
+
|
|
43
|
+
BLOCKED_COMMANDS = {
|
|
44
|
+
"rm",
|
|
45
|
+
"rmdir",
|
|
46
|
+
"sudo",
|
|
47
|
+
"su",
|
|
48
|
+
"chmod",
|
|
49
|
+
"chown",
|
|
50
|
+
"curl",
|
|
51
|
+
"wget",
|
|
52
|
+
"ssh",
|
|
53
|
+
"scp",
|
|
54
|
+
"rsync",
|
|
55
|
+
"dd",
|
|
56
|
+
"mkfs",
|
|
57
|
+
"diskutil",
|
|
58
|
+
"launchctl",
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
ALLOWED_COMMANDS = {
|
|
62
|
+
"pwd",
|
|
63
|
+
"ls",
|
|
64
|
+
"find",
|
|
65
|
+
"cat",
|
|
66
|
+
"sed",
|
|
67
|
+
"head",
|
|
68
|
+
"tail",
|
|
69
|
+
"wc",
|
|
70
|
+
"rg",
|
|
71
|
+
"python",
|
|
72
|
+
"python3",
|
|
73
|
+
"node",
|
|
74
|
+
"npm",
|
|
75
|
+
"npx",
|
|
76
|
+
"git",
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
BUILD_SCRIPT_NAMES = {"build", "compile", "typecheck", "test"}
|
|
80
|
+
DEPLOY_SCRIPT_NAMES = {
|
|
81
|
+
"deploy",
|
|
82
|
+
"preview",
|
|
83
|
+
"release",
|
|
84
|
+
"package",
|
|
85
|
+
"dist",
|
|
86
|
+
"make",
|
|
87
|
+
"build:installer",
|
|
88
|
+
"build:pkg",
|
|
89
|
+
"build:exe",
|
|
90
|
+
"package:mac",
|
|
91
|
+
"package:win",
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
ALLOWED_GIT_SUBCOMMANDS = {"status", "diff", "log", "show"}
|
|
95
|
+
|
|
96
|
+
TEXT_EXTENSIONS = {
|
|
97
|
+
".css",
|
|
98
|
+
".csv",
|
|
99
|
+
".html",
|
|
100
|
+
".js",
|
|
101
|
+
".json",
|
|
102
|
+
".jsx",
|
|
103
|
+
".md",
|
|
104
|
+
".py",
|
|
105
|
+
".ts",
|
|
106
|
+
".tsx",
|
|
107
|
+
".txt",
|
|
108
|
+
".xml",
|
|
109
|
+
".yaml",
|
|
110
|
+
".yml",
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
DOCUMENT_OUTPUT_DIR = "generated_documents"
|
|
114
|
+
PRESENTATION_OUTPUT_DIR = "generated_presentations"
|
|
115
|
+
SPREADSHEET_OUTPUT_DIR = "generated_spreadsheets"
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class ToolError(ValueError):
|
|
119
|
+
pass
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def ensure_agent_root() -> Path:
|
|
123
|
+
AGENT_ROOT.mkdir(parents=True, exist_ok=True)
|
|
124
|
+
return AGENT_ROOT
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _resolve_path(path: str = "") -> Path:
|
|
128
|
+
ensure_agent_root()
|
|
129
|
+
if not path:
|
|
130
|
+
return AGENT_ROOT
|
|
131
|
+
candidate = (AGENT_ROOT / path).resolve()
|
|
132
|
+
if candidate != AGENT_ROOT and AGENT_ROOT not in candidate.parents:
|
|
133
|
+
raise ToolError("Path escapes the agent workspace.")
|
|
134
|
+
return candidate
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _relative(path: Path) -> str:
|
|
138
|
+
return str(path.relative_to(AGENT_ROOT))
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
# ── document / local / read constants (shared by submodules) ──────────────────
|
|
142
|
+
PDF_OUTPUT_DIR = "generated_pdfs"
|
|
143
|
+
LOCAL_MAX_FILE_BYTES = 2_000_000 # 2 MB cap for local reads
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
# CJK-capable fonts (Korean + Chinese + Japanese)
|
|
147
|
+
_CJK_FONT_CANDIDATES = [
|
|
148
|
+
"/System/Library/Fonts/AppleSDGothicNeo.ttc", # Korean (macOS)
|
|
149
|
+
"/System/Library/Fonts/STHeiti Light.ttc", # Chinese (macOS)
|
|
150
|
+
"/System/Library/Fonts/PingFang.ttc", # Chinese (macOS)
|
|
151
|
+
"/Library/Fonts/NanumGothic.ttf", # Korean
|
|
152
|
+
"/usr/share/fonts/truetype/nanum/NanumGothic.ttf",
|
|
153
|
+
]
|
|
154
|
+
|
|
155
|
+
_SUPPORTED_READ_EXTENSIONS = {".pdf", ".docx", ".xlsx", ".pptx", ".txt", ".md", ".csv"}
|
|
156
|
+
DOCUMENT_MAX_READ_BYTES = 10_000_000 # 10 MB
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
# ── focused tool submodules (re-exported flat for import compatibility) ───────
|
|
160
|
+
from tools.computer import * # noqa: E402,F401,F403
|
|
161
|
+
from tools.filesystem import * # noqa: E402,F401,F403
|
|
162
|
+
from tools.documents import * # noqa: E402,F401,F403
|
|
163
|
+
from tools.local_files import * # noqa: E402,F401,F403
|
|
164
|
+
from tools.knowledge import * # noqa: E402,F401,F403
|
|
165
|
+
from tools.network import * # noqa: E402,F401,F403
|
|
166
|
+
from tools.commands import * # noqa: E402,F401,F403
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
# ── tool registry: the single name → invocation source of truth ───────────────
|
|
170
|
+
def _h_create_xlsx(args: Dict[str, Any]) -> Dict[str, Any]:
|
|
171
|
+
rows = args.get("rows", [])
|
|
172
|
+
if isinstance(rows, str):
|
|
173
|
+
rows = json.loads(rows)
|
|
174
|
+
return create_xlsx(rows, args.get("filename", "spreadsheet.xlsx"), args.get("sheet_name", "Sheet1"))
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _h_create_pptx(args: Dict[str, Any]) -> Dict[str, Any]:
|
|
178
|
+
slides = args.get("slides", [])
|
|
179
|
+
if isinstance(slides, str):
|
|
180
|
+
slides = json.loads(slides)
|
|
181
|
+
return create_pptx(args.get("title", ""), slides, args.get("filename", "presentation.pptx"))
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
# ── Tool registry: the single source of truth for name → invocation ───────────
|
|
185
|
+
# Each entry binds the args dict to a tool function. ``execute_tool`` is a
|
|
186
|
+
# lookup over this table — adding a tool means adding one entry here, not
|
|
187
|
+
# editing an if/elif chain. server.py's governance map and catalog brief are
|
|
188
|
+
# checked against ``registered_tools()`` so the three never silently drift.
|
|
189
|
+
TOOL_HANDLERS: Dict[str, Callable[[Dict[str, Any]], Dict[str, Any]]] = {
|
|
190
|
+
# filesystem
|
|
191
|
+
"list_dir": lambda a: list_dir(a.get("path", ".")),
|
|
192
|
+
"workspace_tree": lambda a: workspace_tree(a.get("path", "."), a.get("max_depth", 3)),
|
|
193
|
+
"read_file": lambda a: read_file(a["path"], offset=a.get("offset", 0), limit=a.get("limit", 0), line_numbers=a.get("line_numbers", True)),
|
|
194
|
+
"write_file": lambda a: write_file(a["path"], a.get("content", "")),
|
|
195
|
+
"edit_file": lambda a: edit_file(a["path"], a["old_string"], a["new_string"], replace_all=bool(a.get("replace_all", False))),
|
|
196
|
+
"grep": lambda a: grep(a["pattern"], path=a.get("path", "."), glob=a.get("glob"), max_results=a.get("max_results", 50), case_insensitive=bool(a.get("case_insensitive", False)), context_lines=a.get("context_lines", 0)),
|
|
197
|
+
"search_files": lambda a: search_files(a["query"], a.get("path", "."), a.get("max_results", 20)),
|
|
198
|
+
"inspect_html": lambda a: inspect_html(a["path"]),
|
|
199
|
+
"preview_url": lambda a: preview_url(a.get("path", "index.html")),
|
|
200
|
+
# planning
|
|
201
|
+
"todo_read": lambda a: todo_read(),
|
|
202
|
+
"todo_write": lambda a: todo_write(a.get("todos") or []),
|
|
203
|
+
# documents
|
|
204
|
+
"create_docx": lambda a: create_docx(a.get("title", ""), a.get("body", ""), a.get("filename", "document.docx")),
|
|
205
|
+
"create_xlsx": _h_create_xlsx,
|
|
206
|
+
"create_pptx": _h_create_pptx,
|
|
207
|
+
"create_pdf": lambda a: create_pdf(a.get("title", ""), a.get("body", ""), a.get("filename", "document.pdf")),
|
|
208
|
+
"create_web_project": lambda a: create_web_project(a.get("path", ""), a.get("framework", "react"), a.get("template", "vite")),
|
|
209
|
+
# local filesystem
|
|
210
|
+
"local_list": lambda a: local_list(a["path"]),
|
|
211
|
+
"local_read": lambda a: local_read(a["path"]),
|
|
212
|
+
"local_write": lambda a: local_write(a["path"], a.get("content", "")),
|
|
213
|
+
"read_document": lambda a: read_document(a["path"]),
|
|
214
|
+
"network_status": lambda a: network_status(),
|
|
215
|
+
# computer use
|
|
216
|
+
"computer_screenshot": lambda a: computer_screenshot(),
|
|
217
|
+
"computer_open_app": lambda a: computer_open_app(a.get("app", "Google Chrome")),
|
|
218
|
+
"computer_open_url": lambda a: computer_open_url(a["url"], a.get("app", "Google Chrome")),
|
|
219
|
+
"computer_click": lambda a: computer_click(a.get("x", 0), a.get("y", 0), a.get("button", "left"), a.get("double", False)),
|
|
220
|
+
"computer_type": lambda a: computer_type(a["text"], a.get("interval", 0.04)),
|
|
221
|
+
"computer_key": lambda a: computer_key(a["key"]),
|
|
222
|
+
"computer_scroll": lambda a: computer_scroll(a.get("x", 0), a.get("y", 0), a.get("direction", "down"), a.get("clicks", 3)),
|
|
223
|
+
"computer_move": lambda a: computer_move(a.get("x", 0), a.get("y", 0)),
|
|
224
|
+
"computer_drag": lambda a: computer_drag(a.get("x1", 0), a.get("y1", 0), a.get("x2", 0), a.get("y2", 0)),
|
|
225
|
+
"computer_status": lambda a: computer_status(),
|
|
226
|
+
"chrome_status": lambda a: desktop_bridge_status(),
|
|
227
|
+
"computer_use_status": lambda a: desktop_bridge_status(),
|
|
228
|
+
# knowledge / obsidian
|
|
229
|
+
"knowledge_save": lambda a: knowledge_save(a["content"], a.get("folder", "00_Raw"), a.get("title")),
|
|
230
|
+
"knowledge_search": lambda a: knowledge_search(a["query"], a.get("max_results", 5)),
|
|
231
|
+
"knowledge_tree": lambda a: knowledge_tree(),
|
|
232
|
+
"obsidian_save": lambda a: obsidian_save(a["content"], a.get("folder", "00_Raw"), a.get("title")),
|
|
233
|
+
"obsidian_search": lambda a: obsidian_search(a["query"], a.get("max_results", 5)),
|
|
234
|
+
"obsidian_tree": lambda a: obsidian_tree(),
|
|
235
|
+
# git (read-only)
|
|
236
|
+
"git_status": lambda a: git_status(a.get("cwd")),
|
|
237
|
+
"git_diff": lambda a: git_diff(a.get("path"), a.get("cwd")),
|
|
238
|
+
"git_log": lambda a: git_log(a.get("max_count", 5), a.get("cwd")),
|
|
239
|
+
"git_show": lambda a: git_show(a.get("revision", "HEAD"), a.get("cwd")),
|
|
240
|
+
# exec
|
|
241
|
+
"run_command": lambda a: run_command(a["command"], a.get("cwd")),
|
|
242
|
+
"build_project": lambda a: build_project(a.get("cwd"), a.get("script", "build")),
|
|
243
|
+
"deploy_project": lambda a: deploy_project(a.get("cwd"), a.get("script", "deploy")),
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
DEFAULT_TOOL_REGISTRY = ToolRegistry(TOOL_HANDLERS)
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def registered_tools() -> frozenset:
|
|
251
|
+
"""Names dispatchable through ``execute_tool`` — the seam other modules verify against."""
|
|
252
|
+
return DEFAULT_TOOL_REGISTRY.registered_tools()
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def execute_tool(action: str, args: Dict[str, Any]) -> Dict[str, Any]:
|
|
256
|
+
return DEFAULT_TOOL_REGISTRY.execute(action, args, error_cls=ToolError)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
__all__ = [
|
|
260
|
+
"AGENT_ROOT", "ToolError", "ensure_agent_root",
|
|
261
|
+
"list_dir", "workspace_tree", "read_file", "write_file", "edit_file", "grep",
|
|
262
|
+
"search_files", "inspect_html", "preview_url", "create_web_project",
|
|
263
|
+
"todo_read", "todo_write",
|
|
264
|
+
"create_docx", "create_xlsx", "create_pptx", "create_pdf", "read_document",
|
|
265
|
+
"local_list", "local_read", "local_write", "desktop_bridge_status",
|
|
266
|
+
"knowledge_save", "knowledge_search", "knowledge_tree",
|
|
267
|
+
"obsidian_save", "obsidian_search", "obsidian_tree",
|
|
268
|
+
"network_status",
|
|
269
|
+
"computer_screenshot", "computer_open_app", "computer_open_url",
|
|
270
|
+
"computer_click", "computer_type", "computer_key", "computer_scroll",
|
|
271
|
+
"computer_move", "computer_drag", "computer_status",
|
|
272
|
+
"run_command", "build_project", "deploy_project",
|
|
273
|
+
"git_status", "git_diff", "git_log", "git_show",
|
|
274
|
+
"TOOL_HANDLERS", "DEFAULT_TOOL_REGISTRY", "registered_tools", "execute_tool",
|
|
275
|
+
"BRAIN_DIR", "STRUCTURE",
|
|
276
|
+
]
|