@letta-ai/letta-code 0.32.12 → 0.32.14
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/dist/agent-presets.js +1 -1
- package/dist/agent-presets.js.map +3 -3
- package/dist/channels-public.js +7 -7
- package/dist/channels-public.js.map +3 -3
- package/dist/channels-slack.js.map +2 -2
- package/dist/gateway-core.js +7 -7
- package/dist/gateway-core.js.map +3 -3
- package/dist/mcp-client.js +2 -2
- package/dist/mcp-client.js.map +1 -1
- package/dist/types/agent/approval-execution.d.ts.map +1 -1
- package/dist/types/agent/memory-filesystem.d.ts +3 -4
- package/dist/types/agent/memory-filesystem.d.ts.map +1 -1
- package/dist/types/agent/prompt-assets.d.ts +0 -1
- package/dist/types/agent/prompt-assets.d.ts.map +1 -1
- package/dist/types/agent/subagents/manager.d.ts.map +1 -1
- package/dist/types/agent/subagents/names.d.ts +1 -1
- package/dist/types/agent/subagents/names.d.ts.map +1 -1
- package/dist/types/agent/subagents/subagent-launcher.d.ts +2 -0
- package/dist/types/agent/subagents/subagent-launcher.d.ts.map +1 -1
- package/dist/types/agent/subagents/subagent-process.d.ts +23 -0
- package/dist/types/agent/subagents/subagent-process.d.ts.map +1 -0
- package/dist/types/agent/subagents/subagent-stream.d.ts +3 -0
- package/dist/types/agent/subagents/subagent-stream.d.ts.map +1 -1
- package/dist/types/backend/api/reflection-runs.d.ts +17 -0
- package/dist/types/backend/api/reflection-runs.d.ts.map +1 -0
- package/dist/types/backend/api/reflection.d.ts +22 -0
- package/dist/types/backend/api/reflection.d.ts.map +1 -0
- package/dist/types/backend/backend.d.ts +16 -0
- package/dist/types/backend/backend.d.ts.map +1 -1
- package/dist/types/backend/dev/headless-turn-executor.d.ts +8 -0
- package/dist/types/backend/dev/headless-turn-executor.d.ts.map +1 -1
- package/dist/types/backend/dev/pi-stream-adapter.d.ts.map +1 -1
- package/dist/types/backend/dev/pi-stream-output.d.ts +3 -0
- package/dist/types/backend/dev/pi-stream-output.d.ts.map +1 -0
- package/dist/types/backend/dev/provider-turn-executor.d.ts.map +1 -1
- package/dist/types/backend/local/local-backend.d.ts.map +1 -1
- package/dist/types/backend/local/local-executor-factory.d.ts +1 -1
- package/dist/types/backend/local/local-executor-factory.d.ts.map +1 -1
- package/dist/types/backend/local/local-message.d.ts +2 -1
- package/dist/types/backend/local/local-message.d.ts.map +1 -1
- package/dist/types/backend/local/local-store.d.ts.map +1 -1
- package/dist/types/backend/local/local-stream-chunks.d.ts +13 -0
- package/dist/types/backend/local/local-stream-chunks.d.ts.map +1 -1
- package/dist/types/channels/progress-formatting.d.ts.map +1 -1
- package/dist/types/cli/helpers/stream-processor.d.ts +7 -2
- package/dist/types/cli/helpers/stream-processor.d.ts.map +1 -1
- package/dist/types/cli/helpers/stream.d.ts +7 -5
- package/dist/types/cli/helpers/stream.d.ts.map +1 -1
- package/dist/types/cli/helpers/tool-name-mapping.d.ts +1 -2
- package/dist/types/cli/helpers/tool-name-mapping.d.ts.map +1 -1
- package/dist/types/hooks/executor.d.ts +5 -0
- package/dist/types/hooks/executor.d.ts.map +1 -1
- package/dist/types/hooks/index.d.ts.map +1 -1
- package/dist/types/permissions/canonical.d.ts.map +1 -1
- package/dist/types/permissions/checker.d.ts.map +1 -1
- package/dist/types/permissions/cross-agent-guard.d.ts +1 -1
- package/dist/types/permissions/cross-agent-guard.d.ts.map +1 -1
- package/dist/types/permissions/mode.d.ts.map +1 -1
- package/dist/types/permissions/workspace-sandbox.d.ts.map +1 -1
- package/dist/types/providers/byok-providers.d.ts +1 -0
- package/dist/types/providers/byok-providers.d.ts.map +1 -1
- package/dist/types/settings-manager.d.ts.map +1 -1
- package/dist/types/telemetry/index.d.ts +16 -1
- package/dist/types/telemetry/index.d.ts.map +1 -1
- package/dist/types/tools/filter.d.ts +0 -10
- package/dist/types/tools/filter.d.ts.map +1 -1
- package/dist/types/tools/impl/bash-output.d.ts.map +1 -1
- package/dist/types/tools/impl/bash.d.ts.map +1 -1
- package/dist/types/tools/impl/enter-worktree.d.ts.map +1 -1
- package/dist/types/tools/impl/exec-command.d.ts.map +1 -1
- package/dist/types/tools/impl/process_manager.d.ts +2 -0
- package/dist/types/tools/impl/process_manager.d.ts.map +1 -1
- package/dist/types/tools/impl/shell-command.d.ts.map +1 -1
- package/dist/types/tools/impl/shell-sandbox.d.ts +1 -2
- package/dist/types/tools/impl/shell-sandbox.d.ts.map +1 -1
- package/dist/types/tools/impl/task-stop.d.ts.map +1 -1
- package/dist/types/tools/impl/task.d.ts.map +1 -1
- package/dist/types/tools/impl/tool-return-clamp.d.ts.map +1 -1
- package/dist/types/tools/impl/truncation.d.ts +5 -2
- package/dist/types/tools/impl/truncation.d.ts.map +1 -1
- package/dist/types/tools/impl/worktree-git.d.ts +14 -0
- package/dist/types/tools/impl/worktree-git.d.ts.map +1 -1
- package/dist/types/tools/manager.d.ts +4 -13
- package/dist/types/tools/manager.d.ts.map +1 -1
- package/dist/types/tools/tool-definitions.d.ts +0 -21
- package/dist/types/tools/tool-definitions.d.ts.map +1 -1
- package/dist/types/tools/tool-permissions.d.ts.map +1 -1
- package/dist/types/tools/toolset-catalog.d.ts +13 -0
- package/dist/types/tools/toolset-catalog.d.ts.map +1 -0
- package/dist/types/tools/toolset.d.ts +10 -27
- package/dist/types/tools/toolset.d.ts.map +1 -1
- package/dist/types/types/protocol_v2.d.ts +2 -2
- package/dist/types/types/protocol_v2.d.ts.map +1 -1
- package/dist/types/types/provider-oauth-config.d.ts +24 -0
- package/dist/types/types/provider-oauth-config.d.ts.map +1 -0
- package/dist/types/types/toolset-protocol.d.ts +1 -1
- package/dist/types/types/toolset-protocol.d.ts.map +1 -1
- package/dist/types/utils/frontmatter.d.ts.map +1 -1
- package/dist/types/websocket/listener/listener-constants.d.ts.map +1 -1
- package/docs/examples/mods/memory-citations.ts +0 -2
- package/letta.js +2 -2
- package/package.json +2 -2
- package/scripts/build-pypi.py +172 -0
- package/scripts/isolated-unit-tests.json +15 -5
- package/scripts/smoke-pypi.py +236 -0
- package/scripts/source-file-size-baseline.json +14 -14
- package/scripts/stage-pypi-deps.mjs +90 -0
- package/skills/creating-mods/references/plan-mode.md +3 -9
- package/skills/finding-agents/SKILL.md +6 -2
- package/skills/letta-guide/SKILL.md +21 -2
- package/skills/self-configuration/SKILL.md +1 -1
- package/skills/syncing-memory-filesystem/SKILL.md +2 -3
- package/vendor/ink-text-input/build/index.js +10 -2
- package/dist/types/tools/impl/glob-gemini.d.ts +0 -16
- package/dist/types/tools/impl/glob-gemini.d.ts.map +0 -1
- package/dist/types/tools/impl/list-directory-gemini.d.ts +0 -17
- package/dist/types/tools/impl/list-directory-gemini.d.ts.map +0 -1
- package/dist/types/tools/impl/read-file-gemini.d.ts +0 -14
- package/dist/types/tools/impl/read-file-gemini.d.ts.map +0 -1
- package/dist/types/tools/impl/read-many-files-gemini.d.ts +0 -19
- package/dist/types/tools/impl/read-many-files-gemini.d.ts.map +0 -1
- package/dist/types/tools/impl/replace-gemini.d.ts +0 -15
- package/dist/types/tools/impl/replace-gemini.d.ts.map +0 -1
- package/dist/types/tools/impl/run-shell-command-gemini.d.ts +0 -18
- package/dist/types/tools/impl/run-shell-command-gemini.d.ts.map +0 -1
- package/dist/types/tools/impl/search-file-content-gemini.d.ts +0 -14
- package/dist/types/tools/impl/search-file-content-gemini.d.ts.map +0 -1
- package/dist/types/tools/impl/write-file-gemini.d.ts +0 -13
- package/dist/types/tools/impl/write-file-gemini.d.ts.map +0 -1
- package/dist/types/tools/impl/write-todos-gemini.d.ts +0 -16
- package/dist/types/tools/impl/write-todos-gemini.d.ts.map +0 -1
- package/dist/types/tools/letta-toolset.d.ts +0 -10
- package/dist/types/tools/letta-toolset.d.ts.map +0 -1
- package/dist/types/tools/toolset-defaults.d.ts +0 -9
- package/dist/types/tools/toolset-defaults.d.ts.map +0 -1
- package/dist/types/tools/toolset-options.d.ts +0 -4
- package/dist/types/tools/toolset-options.d.ts.map +0 -1
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"""Build a self-contained letta wheel on its target OS/CPU.
|
|
2
|
+
|
|
3
|
+
Prerequisites: locked `bun install --frozen-lockfile`, Bun, npm, Python 3.9+,
|
|
4
|
+
C/C++ toolchain (node-pty). Run from any directory; artifacts go to python/dist.
|
|
5
|
+
Linux release builds MUST run in the manylinux_2_28 CI image, not a newer host.
|
|
6
|
+
No upload/publish is performed by this script.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import hashlib
|
|
10
|
+
import importlib.util
|
|
11
|
+
import json
|
|
12
|
+
import os
|
|
13
|
+
import platform
|
|
14
|
+
import re
|
|
15
|
+
import shutil
|
|
16
|
+
import subprocess
|
|
17
|
+
import tarfile
|
|
18
|
+
import tempfile
|
|
19
|
+
import urllib.request
|
|
20
|
+
import zipfile
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
|
|
23
|
+
ROOT = Path(__file__).resolve().parents[1]
|
|
24
|
+
NODE = "22.19.0"
|
|
25
|
+
# Official nodejs.org SHASUMS256.txt; pin the archive, not only the version.
|
|
26
|
+
TARGETS = {
|
|
27
|
+
("Linux", "x86_64"): (
|
|
28
|
+
"linux-x64",
|
|
29
|
+
"manylinux_2_28_x86_64",
|
|
30
|
+
"d36e56998220085782c0ca965f9d51b7726335aed2f5fc7321c6c0ad233aa96d",
|
|
31
|
+
),
|
|
32
|
+
("Linux", "aarch64"): (
|
|
33
|
+
"linux-arm64",
|
|
34
|
+
"manylinux_2_28_aarch64",
|
|
35
|
+
"d32817b937219b8f131a28546035183d79e7fd17a86e38ccb8772901a7cd9009",
|
|
36
|
+
),
|
|
37
|
+
("Darwin", "arm64"): (
|
|
38
|
+
"darwin-arm64",
|
|
39
|
+
"macosx_14_0_arm64",
|
|
40
|
+
"c59006db713c770d6ec63ae16cb3edc11f49ee093b5c415d667bb4f436c6526d",
|
|
41
|
+
),
|
|
42
|
+
("Darwin", "x86_64"): (
|
|
43
|
+
"darwin-x64",
|
|
44
|
+
"macosx_14_0_x86_64",
|
|
45
|
+
"3cfed4795cd97277559763c5f56e711852d2cc2420bda1cea30c8aa9ac77ce0c",
|
|
46
|
+
),
|
|
47
|
+
("Windows", "AMD64"): (
|
|
48
|
+
"win-x64",
|
|
49
|
+
"win_amd64",
|
|
50
|
+
"ea3fad0e67a991d8477d8c01344b56e69c676ccb733f065b22436994b1253f86",
|
|
51
|
+
),
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def run(args, **kwargs):
|
|
56
|
+
# npm.cmd requires a shell on Windows; all arguments here are build-owned.
|
|
57
|
+
if os.name == "nt" and args[0] in ("npm", "bun"):
|
|
58
|
+
args[0] += ".cmd" if args[0] == "npm" else ".exe"
|
|
59
|
+
return subprocess.check_output(args, cwd=ROOT, **kwargs)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def main():
|
|
63
|
+
key = (platform.system(), platform.machine())
|
|
64
|
+
if key not in TARGETS:
|
|
65
|
+
raise SystemExit(f"Unsupported wheel platform: {key[0]} {key[1]}")
|
|
66
|
+
target, wheel_platform, checksum = TARGETS[key]
|
|
67
|
+
if platform.system() == "Linux" and platform.libc_ver()[0] != "glibc":
|
|
68
|
+
raise SystemExit("Only glibc Linux is supported (not musl/Alpine)")
|
|
69
|
+
# Do not falsely label artifacts built on a newer glibc as manylinux_2_28.
|
|
70
|
+
if platform.system() == "Linux" and platform.libc_ver()[1] != "2.28":
|
|
71
|
+
if os.environ.get("LETTA_PYPI_LOCAL_TEST") != "1":
|
|
72
|
+
raise SystemExit(
|
|
73
|
+
"Use the manylinux_2_28 CI image, or LETTA_PYPI_LOCAL_TEST=1 for a non-publishable local wheel"
|
|
74
|
+
)
|
|
75
|
+
wheel_platform = "linux_" + platform.machine()
|
|
76
|
+
version = json.loads((ROOT / "package.json").read_text())["version"]
|
|
77
|
+
if not re.fullmatch(r"\d+\.\d+\.\d+", version):
|
|
78
|
+
raise SystemExit("PyPI builds currently require a stable X.Y.Z package version")
|
|
79
|
+
payload = ROOT / "python/letta_code/_payload"
|
|
80
|
+
shutil.rmtree(payload, ignore_errors=True)
|
|
81
|
+
(payload / "bin").mkdir(parents=True)
|
|
82
|
+
app = payload / "app"
|
|
83
|
+
app.mkdir()
|
|
84
|
+
extension = "zip" if os.name == "nt" else "tar.gz"
|
|
85
|
+
archive_name = f"node-v{NODE}-{target}.{extension}"
|
|
86
|
+
with tempfile.TemporaryDirectory() as temporary:
|
|
87
|
+
archive = Path(temporary) / archive_name
|
|
88
|
+
urllib.request.urlretrieve(
|
|
89
|
+
f"https://nodejs.org/dist/v{NODE}/{archive_name}", archive
|
|
90
|
+
)
|
|
91
|
+
if hashlib.sha256(archive.read_bytes()).hexdigest() != checksum:
|
|
92
|
+
raise SystemExit("Node archive checksum mismatch")
|
|
93
|
+
prefix = f"node-v{NODE}-{target}"
|
|
94
|
+
# Extract only trusted, explicitly named runtime/license files, never npm.
|
|
95
|
+
names = {
|
|
96
|
+
"node.exe" if os.name == "nt" else "bin/node": "bin/node.exe"
|
|
97
|
+
if os.name == "nt"
|
|
98
|
+
else "bin/node",
|
|
99
|
+
"LICENSE": "NODE-LICENSE",
|
|
100
|
+
}
|
|
101
|
+
if extension == "zip":
|
|
102
|
+
with zipfile.ZipFile(archive) as source:
|
|
103
|
+
for member, destination in names.items():
|
|
104
|
+
(payload / destination).write_bytes(
|
|
105
|
+
source.read(f"{prefix}/{member}")
|
|
106
|
+
)
|
|
107
|
+
else:
|
|
108
|
+
with tarfile.open(archive) as source:
|
|
109
|
+
for member, destination in names.items():
|
|
110
|
+
(payload / destination).write_bytes(
|
|
111
|
+
source.extractfile(f"{prefix}/{member}").read()
|
|
112
|
+
)
|
|
113
|
+
node = payload / "bin" / ("node.exe" if os.name == "nt" else "node")
|
|
114
|
+
node.chmod(0o755)
|
|
115
|
+
env = os.environ.copy()
|
|
116
|
+
env["PATH"] = str(node.parent) + os.pathsep + env.get("PATH", "")
|
|
117
|
+
run(["bun", "run", "build"], env=env)
|
|
118
|
+
packed = json.loads(
|
|
119
|
+
run(["npm", "pack", "--dry-run", "--json", "--ignore-scripts"], env=env)
|
|
120
|
+
)
|
|
121
|
+
for entry in packed[0]["files"]:
|
|
122
|
+
path = entry["path"]
|
|
123
|
+
destination = app / path
|
|
124
|
+
destination.parent.mkdir(parents=True, exist_ok=True)
|
|
125
|
+
shutil.copy2(ROOT / path, destination)
|
|
126
|
+
run([str(node), "scripts/stage-pypi-deps.mjs", str(app)], env=env)
|
|
127
|
+
# Native addons must be built with the Node ABI we ship, not Bun/host Node.
|
|
128
|
+
npm = shutil.which("npm")
|
|
129
|
+
native_env = env.copy()
|
|
130
|
+
native_env["npm_config_build_from_source"] = "true"
|
|
131
|
+
subprocess.run(
|
|
132
|
+
[npm, "rebuild", "node-pty", "--foreground-scripts"],
|
|
133
|
+
cwd=app,
|
|
134
|
+
env=native_env,
|
|
135
|
+
check=True,
|
|
136
|
+
shell=os.name == "nt",
|
|
137
|
+
)
|
|
138
|
+
subprocess.run(
|
|
139
|
+
[npm, "rebuild", "@vscode/ripgrep", "--foreground-scripts"],
|
|
140
|
+
cwd=app,
|
|
141
|
+
env=env,
|
|
142
|
+
check=True,
|
|
143
|
+
shell=os.name == "nt",
|
|
144
|
+
)
|
|
145
|
+
pty = app / "node_modules/node-pty"
|
|
146
|
+
shutil.rmtree(pty / "prebuilds", ignore_errors=True)
|
|
147
|
+
for path in ("third_party", "deps", "src", "build/Release/obj.target"):
|
|
148
|
+
shutil.rmtree(pty / path, ignore_errors=True)
|
|
149
|
+
for helper in app.rglob("spawn-helper"):
|
|
150
|
+
helper.chmod(0o755)
|
|
151
|
+
(payload / "manifest.json").write_text(
|
|
152
|
+
json.dumps(
|
|
153
|
+
{
|
|
154
|
+
"version": version,
|
|
155
|
+
"node": NODE,
|
|
156
|
+
"target": target,
|
|
157
|
+
"wheel_platform": wheel_platform,
|
|
158
|
+
},
|
|
159
|
+
indent=2,
|
|
160
|
+
)
|
|
161
|
+
+ "\n"
|
|
162
|
+
)
|
|
163
|
+
spec = importlib.util.spec_from_file_location(
|
|
164
|
+
"wheel_backend", ROOT / "python/wheel_backend.py"
|
|
165
|
+
)
|
|
166
|
+
backend = importlib.util.module_from_spec(spec)
|
|
167
|
+
spec.loader.exec_module(backend)
|
|
168
|
+
print(backend.build_wheel(str(ROOT / "python/dist")))
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
if __name__ == "__main__":
|
|
172
|
+
main()
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"tests": [
|
|
3
|
+
{
|
|
4
|
+
"path": "src/tools/impl/task-fork-receipt.test.ts",
|
|
5
|
+
"timeoutMs": 15000,
|
|
6
|
+
"reason": "Replaces billing I/O and the child process while exercising real fork launch state and task receipts with process-global backend, settings, and subagent state."
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"path": "src/tools/impl/task-computer-routing.test.ts",
|
|
10
|
+
"timeoutMs": 15000,
|
|
11
|
+
"reason": "Mocks subagent discovery and the child-process launch boundary while exercising task() with process-global backend and subagent state."
|
|
12
|
+
},
|
|
3
13
|
{
|
|
4
14
|
"path": "src/websocket/listener/turn-cleanup-shared-memory.e2e.test.ts",
|
|
5
15
|
"timeoutMs": 30000,
|
|
@@ -71,6 +81,11 @@
|
|
|
71
81
|
"timeoutMs": 15000,
|
|
72
82
|
"reason": "Renders Ink while mutating the backend mode, backend instance, provider registry, settings singleton, and model cache."
|
|
73
83
|
},
|
|
84
|
+
{
|
|
85
|
+
"path": "src/cli/app/use-configuration-handlers.test.tsx",
|
|
86
|
+
"timeoutMs": 15000,
|
|
87
|
+
"reason": "Renders the reasoning picker while replacing the shared runtime model catalog; shared batches can retain prior catalog or Ink state."
|
|
88
|
+
},
|
|
74
89
|
{
|
|
75
90
|
"path": "src/cli/message-search-cache-warm.test.ts",
|
|
76
91
|
"timeoutMs": 15000,
|
|
@@ -180,11 +195,6 @@
|
|
|
180
195
|
"timeoutMs": 15000,
|
|
181
196
|
"reason": "Uses a top-level Bun module mock for the backend client."
|
|
182
197
|
},
|
|
183
|
-
{
|
|
184
|
-
"path": "src/tools/toolset-memfs-detach.test.ts",
|
|
185
|
-
"timeoutMs": 15000,
|
|
186
|
-
"reason": "Uses a top-level Bun module mock for the backend client."
|
|
187
|
-
},
|
|
188
198
|
{
|
|
189
199
|
"path": "src/websocket/listen-client-concurrency.test.ts",
|
|
190
200
|
"timeoutMs": 30000,
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
"""Install and exercise a wheel offline, outside the checkout and without Node/npm on PATH."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import subprocess
|
|
6
|
+
import sys
|
|
7
|
+
import tempfile
|
|
8
|
+
import venv
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
ARGUMENT_PROBE = r"""
|
|
12
|
+
import assert from 'node:assert/strict';
|
|
13
|
+
assert.equal(process.env.LETTA_CODE_DISTRIBUTION, 'pypi');
|
|
14
|
+
assert.deepEqual(process.argv.slice(2), ['space argument', 'unicode-λ', '--literal=$HOME']);
|
|
15
|
+
assert.ok(process.execPath.includes('_payload'));
|
|
16
|
+
process.exit(23); // Verify the Python launcher preserves child exit status.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
RUNTIME_PROBE = r"""
|
|
20
|
+
import {createRequire as bootstrapRequire} from 'node:module';
|
|
21
|
+
const require = bootstrapRequire(import.meta.url);
|
|
22
|
+
const assert = require('node:assert/strict');
|
|
23
|
+
const {createRequire} = require('node:module');
|
|
24
|
+
const {spawnSync} = require('node:child_process');
|
|
25
|
+
const path = require('node:path');
|
|
26
|
+
const fs = require('node:fs');
|
|
27
|
+
const cli = process.argv[2];
|
|
28
|
+
const app = path.dirname(cli);
|
|
29
|
+
const req = createRequire(cli);
|
|
30
|
+
const env = {...process.env};
|
|
31
|
+
delete env.NODE_OPTIONS;
|
|
32
|
+
assert.equal(process.env.LETTA_CODE_DISTRIBUTION, 'pypi');
|
|
33
|
+
assert.ok(process.execPath.includes('_payload'));
|
|
34
|
+
const child = spawnSync('node', ['-p', 'process.execPath'], {env, encoding:'utf8'});
|
|
35
|
+
assert.equal(child.status, 0, child.stderr);
|
|
36
|
+
assert.equal(fs.realpathSync(child.stdout.trim()), fs.realpathSync(process.execPath));
|
|
37
|
+
if (process.platform !== 'win32') {
|
|
38
|
+
const direct = spawnSync(cli, ['--version'], {env, encoding:'utf8'});
|
|
39
|
+
assert.equal(direct.status, 0, direct.stderr); // Executable bits + /usr/bin/env node.
|
|
40
|
+
assert.match(direct.stdout, /Letta Code/);
|
|
41
|
+
}
|
|
42
|
+
assert.ok(fs.existsSync(path.join(app, 'skills', 'letta-guide', 'SKILL.md')));
|
|
43
|
+
assert.ok(fs.existsSync(path.join(app, 'assets', 'tutor-profile.png')));
|
|
44
|
+
const rg = spawnSync(req('@vscode/ripgrep').rgPath, ['--version'], {env, encoding:'utf8'});
|
|
45
|
+
assert.equal(rg.status, 0, rg.stderr);
|
|
46
|
+
assert.match(rg.stdout, /ripgrep/);
|
|
47
|
+
assert.equal(typeof req('ws').WebSocketServer, 'function');
|
|
48
|
+
(async () => {
|
|
49
|
+
const {Bot} = req('grammy');
|
|
50
|
+
const bot = new Bot('123:test');
|
|
51
|
+
bot.api.config.use(async () => ({ok:true, result:{id:123, is_bot:true, first_name:'wheel', username:'wheel_bot'}}));
|
|
52
|
+
await bot.init();
|
|
53
|
+
const svg = '<svg xmlns="http://www.w3.org/2000/svg" width="3000" height="2"><rect width="3000" height="2" fill="red"/></svg>';
|
|
54
|
+
const resized = spawnSync(process.execPath, [path.join(app, 'image-resize-worker.js'), 'image/svg+xml'], {input:svg, env});
|
|
55
|
+
assert.equal(resized.status, 0, resized.stderr.toString());
|
|
56
|
+
const image = JSON.parse(resized.stdout);
|
|
57
|
+
assert.ok(image.width > 0 && image.width < 3000, JSON.stringify(image));
|
|
58
|
+
const ptyCommand = process.platform === 'win32' ? process.env.ComSpec : process.execPath;
|
|
59
|
+
const ptyArgs = process.platform === 'win32' ? ['/d', '/s', '/c', 'echo LETTA_PTY_OK'] : [cli, '--help'];
|
|
60
|
+
const ptyExpected = process.platform === 'win32' ? /LETTA_PTY_OK/ : /USAGE/;
|
|
61
|
+
const pty = req('node-pty').spawn(ptyCommand, ptyArgs, {env, cols:100, rows:30});
|
|
62
|
+
let output = '';
|
|
63
|
+
pty.onData(data => { output += data; });
|
|
64
|
+
const timeout = setTimeout(() => { pty.kill(); throw new Error('PTY timed out'); }, 30000);
|
|
65
|
+
pty.onExit(({exitCode}) => {
|
|
66
|
+
clearTimeout(timeout);
|
|
67
|
+
assert.equal(exitCode, 0, output);
|
|
68
|
+
assert.match(output, ptyExpected);
|
|
69
|
+
console.log('native PTY, image worker, ripgrep, Telegram, assets and child Node passed');
|
|
70
|
+
process.exit(0);
|
|
71
|
+
});
|
|
72
|
+
})().catch(error => {console.error(error); process.exit(1)});
|
|
73
|
+
// Hold the script until the asynchronous probe exits.
|
|
74
|
+
await new Promise(() => {});
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def main():
|
|
79
|
+
wheel = Path(sys.argv[1]).resolve()
|
|
80
|
+
if wheel.stat().st_size >= 100 * 1024 * 1024:
|
|
81
|
+
raise SystemExit("Wheel exceeds the default PyPI 100 MiB artifact limit")
|
|
82
|
+
with tempfile.TemporaryDirectory(prefix="letta wheel smoke ") as temporary:
|
|
83
|
+
root = Path(temporary)
|
|
84
|
+
environment = root / "venv"
|
|
85
|
+
venv.create(environment, with_pip=True)
|
|
86
|
+
binary = environment / ("Scripts" if os.name == "nt" else "bin")
|
|
87
|
+
python = binary / ("python.exe" if os.name == "nt" else "python")
|
|
88
|
+
cli = binary / ("letta.exe" if os.name == "nt" else "letta")
|
|
89
|
+
subprocess.run(
|
|
90
|
+
[
|
|
91
|
+
str(python),
|
|
92
|
+
"-m",
|
|
93
|
+
"pip",
|
|
94
|
+
"install",
|
|
95
|
+
"--no-index",
|
|
96
|
+
"--no-deps",
|
|
97
|
+
str(wheel),
|
|
98
|
+
],
|
|
99
|
+
check=True,
|
|
100
|
+
)
|
|
101
|
+
home = root / "home"
|
|
102
|
+
home.mkdir()
|
|
103
|
+
env = {
|
|
104
|
+
k: v
|
|
105
|
+
for k, v in os.environ.items()
|
|
106
|
+
if not k.startswith(("LETTA", "NODE_", "NPM_"))
|
|
107
|
+
and k not in ("AGENT_ID", "CONVERSATION_ID", "MEMORY_DIR")
|
|
108
|
+
}
|
|
109
|
+
env.update(
|
|
110
|
+
HOME=str(home),
|
|
111
|
+
USERPROFILE=str(home),
|
|
112
|
+
PATH=str(binary),
|
|
113
|
+
DISABLE_AUTOUPDATER="0",
|
|
114
|
+
)
|
|
115
|
+
if os.name == "nt":
|
|
116
|
+
env["PATH"] += os.pathsep + str(Path(os.environ["SystemRoot"]) / "System32")
|
|
117
|
+
for args, expected, code in [
|
|
118
|
+
(["--help"], "USAGE", 0),
|
|
119
|
+
(["--version"], "Letta Code", 0),
|
|
120
|
+
(["update"], "uv tool upgrade letta", 1),
|
|
121
|
+
]:
|
|
122
|
+
result = subprocess.run(
|
|
123
|
+
[str(cli), *args],
|
|
124
|
+
cwd=root,
|
|
125
|
+
env=env,
|
|
126
|
+
check=False,
|
|
127
|
+
capture_output=True,
|
|
128
|
+
text=True,
|
|
129
|
+
timeout=45,
|
|
130
|
+
)
|
|
131
|
+
assert result.returncode == code, (args, result.stdout, result.stderr)
|
|
132
|
+
assert expected in result.stdout + result.stderr, result
|
|
133
|
+
env.update(
|
|
134
|
+
LETTA_LOCAL_BACKEND_DIR=str(root / "local-store"),
|
|
135
|
+
LETTA_LOCAL_BACKEND_EXECUTOR="deterministic",
|
|
136
|
+
LETTA_SKIP_KEYCHAIN_CHECK="1",
|
|
137
|
+
)
|
|
138
|
+
result = subprocess.run(
|
|
139
|
+
[
|
|
140
|
+
str(cli),
|
|
141
|
+
"--backend",
|
|
142
|
+
"local",
|
|
143
|
+
"--ephemeral",
|
|
144
|
+
"-m",
|
|
145
|
+
"openai/gpt-5.6-luna",
|
|
146
|
+
"-p",
|
|
147
|
+
"wheel runtime smoke",
|
|
148
|
+
"--tools=",
|
|
149
|
+
"--output-format",
|
|
150
|
+
"json",
|
|
151
|
+
],
|
|
152
|
+
cwd=root,
|
|
153
|
+
env=env,
|
|
154
|
+
check=False,
|
|
155
|
+
capture_output=True,
|
|
156
|
+
text=True,
|
|
157
|
+
timeout=60,
|
|
158
|
+
)
|
|
159
|
+
assert result.returncode == 0, (result.stdout, result.stderr)
|
|
160
|
+
response = json.loads(result.stdout)
|
|
161
|
+
assert response["conversation_id"].startswith("local-conv-"), response
|
|
162
|
+
assert response["result"], response
|
|
163
|
+
print("Installed headless deterministic local turn passed")
|
|
164
|
+
argument_probe = root / "argument-probe.mjs"
|
|
165
|
+
argument_probe.write_text(ARGUMENT_PROBE, encoding="utf-8")
|
|
166
|
+
env["NODE_OPTIONS"] = "--import=" + json.dumps(argument_probe.as_uri())
|
|
167
|
+
result = subprocess.run(
|
|
168
|
+
[str(cli), "space argument", "unicode-λ", "--literal=$HOME"],
|
|
169
|
+
cwd=root,
|
|
170
|
+
env=env,
|
|
171
|
+
check=False,
|
|
172
|
+
timeout=60,
|
|
173
|
+
)
|
|
174
|
+
assert result.returncode == 23, result
|
|
175
|
+
del env["NODE_OPTIONS"]
|
|
176
|
+
payload = Path(
|
|
177
|
+
subprocess.check_output(
|
|
178
|
+
[
|
|
179
|
+
str(python),
|
|
180
|
+
"-c",
|
|
181
|
+
"from pathlib import Path; import letta_code; print(Path(letta_code.__file__).parent / '_payload')",
|
|
182
|
+
],
|
|
183
|
+
text=True,
|
|
184
|
+
).strip()
|
|
185
|
+
)
|
|
186
|
+
node = payload / "bin" / ("node.exe" if os.name == "nt" else "node")
|
|
187
|
+
installed_cli = payload / "app" / "letta.js"
|
|
188
|
+
runtime_probe = root / "runtime-probe.mjs"
|
|
189
|
+
runtime_probe.write_text(RUNTIME_PROBE, encoding="utf-8")
|
|
190
|
+
env["PATH"] = str(node.parent) + os.pathsep + env["PATH"]
|
|
191
|
+
env["LETTA_CODE_DISTRIBUTION"] = "pypi"
|
|
192
|
+
result = subprocess.run(
|
|
193
|
+
[str(node), str(runtime_probe), str(installed_cli)],
|
|
194
|
+
cwd=root,
|
|
195
|
+
env=env,
|
|
196
|
+
check=False,
|
|
197
|
+
timeout=60,
|
|
198
|
+
)
|
|
199
|
+
assert result.returncode == 0, result
|
|
200
|
+
if os.name != "nt":
|
|
201
|
+
import selectors
|
|
202
|
+
import signal
|
|
203
|
+
|
|
204
|
+
signal_probe = root / "signal.mjs"
|
|
205
|
+
signal_probe.write_text(
|
|
206
|
+
"process.on('SIGTERM', () => process.exit(42)); console.log(process.pid); setInterval(() => {}, 1000); await new Promise(() => {});"
|
|
207
|
+
)
|
|
208
|
+
env["NODE_OPTIONS"] = "--import=" + json.dumps(signal_probe.as_uri())
|
|
209
|
+
process = subprocess.Popen(
|
|
210
|
+
[str(cli)],
|
|
211
|
+
cwd=root,
|
|
212
|
+
env=env,
|
|
213
|
+
stdout=subprocess.PIPE,
|
|
214
|
+
stderr=subprocess.PIPE,
|
|
215
|
+
text=True,
|
|
216
|
+
)
|
|
217
|
+
try:
|
|
218
|
+
with selectors.DefaultSelector() as selector:
|
|
219
|
+
selector.register(process.stdout, selectors.EVENT_READ)
|
|
220
|
+
assert selector.select(timeout=15), "Signal probe did not start"
|
|
221
|
+
assert int(process.stdout.readline()) == process.pid, (
|
|
222
|
+
"Launcher did not exec Node"
|
|
223
|
+
)
|
|
224
|
+
process.send_signal(signal.SIGTERM)
|
|
225
|
+
process.communicate(timeout=15)
|
|
226
|
+
assert process.returncode == 42, process.returncode
|
|
227
|
+
finally:
|
|
228
|
+
if process.poll() is None:
|
|
229
|
+
process.kill()
|
|
230
|
+
process.wait()
|
|
231
|
+
print("POSIX PID and SIGTERM forwarding passed")
|
|
232
|
+
print(f"Installed wheel smoke passed: {wheel.name}")
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
if __name__ == "__main__":
|
|
236
|
+
main()
|
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"src/agent/client-skills.test.ts": 1134,
|
|
3
3
|
"src/agent/memory-git.ts": 2097,
|
|
4
|
-
"src/backend/local-backend.test.ts":
|
|
5
|
-
"src/backend/local/local-backend.ts":
|
|
6
|
-
"src/backend/local/local-store.ts":
|
|
7
|
-
"src/backend/pi-stream-adapter.test.ts":
|
|
8
|
-
"src/cli/app/AppCoordinator.tsx":
|
|
4
|
+
"src/backend/local-backend.test.ts": 2473,
|
|
5
|
+
"src/backend/local/local-backend.ts": 1005,
|
|
6
|
+
"src/backend/local/local-store.ts": 3457,
|
|
7
|
+
"src/backend/pi-stream-adapter.test.ts": 1255,
|
|
8
|
+
"src/cli/app/AppCoordinator.tsx": 5138,
|
|
9
9
|
"src/cli/app/AppView.tsx": 1735,
|
|
10
10
|
"src/cli/app/use-approval-flow.ts": 1163,
|
|
11
|
-
"src/cli/app/use-configuration-handlers.ts":
|
|
11
|
+
"src/cli/app/use-configuration-handlers.ts": 1418,
|
|
12
12
|
"src/cli/app/use-conversation-loop.ts": 2903,
|
|
13
|
-
"src/cli/app/use-submit-handler.ts":
|
|
13
|
+
"src/cli/app/use-submit-handler.ts": 3804,
|
|
14
14
|
"src/cli/components/AgentSelector.tsx": 1104,
|
|
15
15
|
"src/cli/components/InputRich.tsx": 2225,
|
|
16
16
|
"src/cli/components/ModelSelector.tsx": 1259,
|
|
17
|
-
"src/cli/components/ProviderSelector.tsx":
|
|
17
|
+
"src/cli/components/ProviderSelector.tsx": 1564,
|
|
18
18
|
"src/cli/components/ToolCallMessageRich.tsx": 1013,
|
|
19
19
|
"src/cli/helpers/accumulator.ts": 1592,
|
|
20
20
|
"src/cli/helpers/reflection-transcript.ts": 1926,
|
|
21
21
|
"src/cli/mods/local-mod-loader.test.ts": 1043,
|
|
22
22
|
"src/cli/reflection-transcript.test.ts": 1084,
|
|
23
23
|
"src/cli/subcommands/skills.ts": 1264,
|
|
24
|
-
"src/headless.ts":
|
|
24
|
+
"src/headless.ts": 4808,
|
|
25
25
|
"src/hooks/integration.test.ts": 1147,
|
|
26
|
-
"src/index.ts":
|
|
26
|
+
"src/index.ts": 2606,
|
|
27
27
|
"src/mods/learning-harness.ts": 2434,
|
|
28
28
|
"src/mods/mod-engine.test.ts": 2153,
|
|
29
29
|
"src/mods/mod-engine.ts": 1838,
|
|
@@ -34,14 +34,14 @@
|
|
|
34
34
|
"src/permissions/read-only-shell.ts": 1969,
|
|
35
35
|
"src/providers/chatgpt-usage-service.ts": 1112,
|
|
36
36
|
"src/settings-manager.test.ts": 1635,
|
|
37
|
-
"src/settings-manager.ts":
|
|
38
|
-
"src/tools/manager.ts":
|
|
39
|
-
"src/tools/tool-execution-context.test.ts":
|
|
37
|
+
"src/settings-manager.ts": 2101,
|
|
38
|
+
"src/tools/manager.ts": 2798,
|
|
39
|
+
"src/tools/tool-execution-context.test.ts": 1114,
|
|
40
40
|
"src/types/protocol_v2.ts": 2593,
|
|
41
41
|
"src/websocket/listen-client-concurrency.test.ts": 2666,
|
|
42
42
|
"src/websocket/listen-client-protocol.test.ts": 5762,
|
|
43
43
|
"src/websocket/listener/commands/memory.ts": 1114,
|
|
44
44
|
"src/websocket/listener/file-commands.ts": 1053,
|
|
45
|
-
"src/websocket/listener/protocol-inbound.ts":
|
|
45
|
+
"src/websocket/listener/protocol-inbound.ts": 2126,
|
|
46
46
|
"src/websocket/listener/protocol-outbound.ts": 1022
|
|
47
47
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// Copy the locked runtime dependency closure, preserving Node's nested versions.
|
|
2
|
+
// No dependency installation happens on the user's machine.
|
|
3
|
+
import {
|
|
4
|
+
cpSync,
|
|
5
|
+
existsSync,
|
|
6
|
+
mkdirSync,
|
|
7
|
+
readdirSync,
|
|
8
|
+
readFileSync,
|
|
9
|
+
realpathSync,
|
|
10
|
+
} from "node:fs";
|
|
11
|
+
import { dirname, join, resolve } from "node:path";
|
|
12
|
+
|
|
13
|
+
const root = process.cwd();
|
|
14
|
+
const app = resolve(process.argv[2]);
|
|
15
|
+
// sharp-electron is only reachable under Electron, never our bundled Node.
|
|
16
|
+
const roots = ["ws", "@vscode/ripgrep", "node-pty", "grammy"];
|
|
17
|
+
|
|
18
|
+
function locate(name, from) {
|
|
19
|
+
for (let dir = from; ; dir = dirname(dir)) {
|
|
20
|
+
const candidate = join(dir, "node_modules", name);
|
|
21
|
+
if (existsSync(join(candidate, "package.json")))
|
|
22
|
+
return realpathSync(candidate);
|
|
23
|
+
if (dir === dirname(dir))
|
|
24
|
+
throw new Error(`Missing locked dependency ${name} from ${from}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function compatible(pkg) {
|
|
28
|
+
return (
|
|
29
|
+
(!pkg.os || pkg.os.includes(process.platform)) &&
|
|
30
|
+
(!pkg.cpu || pkg.cpu.includes(process.arch)) &&
|
|
31
|
+
(!pkg.libc || pkg.libc.includes("glibc"))
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
function copy(name, from, dest, ancestors = new Set()) {
|
|
35
|
+
const source = locate(name, from);
|
|
36
|
+
const pkg = JSON.parse(readFileSync(join(source, "package.json")));
|
|
37
|
+
if (!compatible(pkg)) return;
|
|
38
|
+
if (ancestors.has(source)) throw new Error(`Dependency cycle at ${source}`);
|
|
39
|
+
const target = join(dest, "node_modules", name);
|
|
40
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
41
|
+
cpSync(source, target, {
|
|
42
|
+
recursive: true,
|
|
43
|
+
dereference: true,
|
|
44
|
+
filter: (path) =>
|
|
45
|
+
path === source ||
|
|
46
|
+
!["node_modules", ".git"].includes(
|
|
47
|
+
path.slice(source.length + 1).split(/[\\/]/)[0],
|
|
48
|
+
),
|
|
49
|
+
});
|
|
50
|
+
const chain = new Set([...ancestors, source]);
|
|
51
|
+
const optional = pkg.optionalDependencies || {};
|
|
52
|
+
for (const dependency of Object.keys({ ...pkg.dependencies, ...optional })) {
|
|
53
|
+
try {
|
|
54
|
+
locate(dependency, source);
|
|
55
|
+
} catch (error) {
|
|
56
|
+
if (dependency in optional) continue;
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
copy(dependency, source, target, chain);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
for (const name of roots) copy(name, root, app);
|
|
63
|
+
// Bun bundles Sharp JS but leaves its computed @img native requires unresolved.
|
|
64
|
+
const sharp = locate("sharp", root);
|
|
65
|
+
const sharpPackage = JSON.parse(readFileSync(join(sharp, "package.json")));
|
|
66
|
+
for (const name of Object.keys(sharpPackage.optionalDependencies || {})) {
|
|
67
|
+
try {
|
|
68
|
+
locate(name, sharp);
|
|
69
|
+
} catch {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
copy(name, sharp, app);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Retain license notices for code in the JS bundle as well as external modules.
|
|
76
|
+
function licenses(dir, destination) {
|
|
77
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
78
|
+
const path = join(dir, entry.name);
|
|
79
|
+
if (entry.isDirectory() && entry.name !== ".bin")
|
|
80
|
+
licenses(path, join(destination, entry.name));
|
|
81
|
+
else if (
|
|
82
|
+
entry.isFile() &&
|
|
83
|
+
/^(licen[sc]e|copying|notice)([.-]|$)/i.test(entry.name)
|
|
84
|
+
) {
|
|
85
|
+
mkdirSync(destination, { recursive: true });
|
|
86
|
+
cpSync(path, join(destination, entry.name));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
licenses(join(root, "node_modules"), join(app, "third-party-licenses"));
|
|
@@ -109,7 +109,7 @@ In plan mode, you should:
|
|
|
109
109
|
Remember: DO NOT write or edit any files except the plan file. This is a read-only exploration and planning phase.
|
|
110
110
|
|
|
111
111
|
Plan file path: ${session.planFilePath}
|
|
112
|
-
If using
|
|
112
|
+
If using ApplyPatch, use this exact relative patch path: ${relativePatchPath}`;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
export default function activate(letta) {
|
|
@@ -161,7 +161,7 @@ function buildActiveReminder(session, cwd) {
|
|
|
161
161
|
Plan mode is active. The user indicated that they do not want you to execute yet -- you MUST NOT make any edits (with the exception of the plan file mentioned below), run any non-readonly tools (including changing configs or making commits), or otherwise make any changes to the system. This supersedes any other instructions you have received. Instead, you should:
|
|
162
162
|
1. Answer the user's query comprehensively, using the AskUserQuestion tool if you need to ask the user clarifying questions.
|
|
163
163
|
2. Write your implementation plan to the plan file. Plan file path: ${session.planFilePath}
|
|
164
|
-
3. If using
|
|
164
|
+
3. If using ApplyPatch, use this exact relative path in patch headers: ${relativePatchPath}
|
|
165
165
|
4. Use direct read-only tools for exploration. Do not launch coding, general-purpose, or fork subagents in plan mode; they may mutate files and should be denied. Only recall-style subagents are allowed if available.
|
|
166
166
|
5. When the plan is complete, read the plan file and present the full current plan text to the user with AskUserQuestion. The question body must include the entire plan, not a summary. The question should offer at least "Approve" and "Revise" options.
|
|
167
167
|
6. If the user approves, call exit_plan_mode immediately. If the user asks to revise, stay in plan mode, update the plan file, then read and present the full revised plan again.
|
|
@@ -180,26 +180,21 @@ if (letta.capabilities.events.turns) {
|
|
|
180
180
|
|
|
181
181
|
## Permission overlay
|
|
182
182
|
|
|
183
|
-
Use a permission overlay, not `tool_start`, for policy. Normalize tool names by family; UI display names and provider-specific tool names drift (`Read`, `read`, `read_file`, `ReadFile`,
|
|
183
|
+
Use a permission overlay, not `tool_start`, for policy. Normalize tool names by family; UI display names and provider-specific tool names drift (`Read`, `read`, `read_file`, `ReadFile`, etc.). Keep pure read-only tools separate from planning coordination tools like `AskUserQuestion` and todo/plan updates so the policy stays honest.
|
|
184
184
|
|
|
185
185
|
```ts
|
|
186
186
|
const readOnlyToolNames = new Set([
|
|
187
187
|
"glob",
|
|
188
|
-
"globgemini",
|
|
189
188
|
"grep",
|
|
190
189
|
"grepfiles",
|
|
191
190
|
"list",
|
|
192
191
|
"listdir",
|
|
193
|
-
"listdirectory",
|
|
194
192
|
"ls",
|
|
195
193
|
"notebookread",
|
|
196
194
|
"read",
|
|
197
195
|
"readfile",
|
|
198
|
-
"readfilegemini",
|
|
199
196
|
"readlsp",
|
|
200
|
-
"readmanyfiles",
|
|
201
197
|
"search",
|
|
202
|
-
"searchfilecontent",
|
|
203
198
|
"searchfiles",
|
|
204
199
|
"skill",
|
|
205
200
|
"taskoutput",
|
|
@@ -212,7 +207,6 @@ const planningToolNames = new Set([
|
|
|
212
207
|
"exitplanmode",
|
|
213
208
|
"todowrite",
|
|
214
209
|
"updateplan",
|
|
215
|
-
"writetodos",
|
|
216
210
|
]);
|
|
217
211
|
|
|
218
212
|
const readOnlySubagentTypes = new Set(["recall"]);
|