@letta-ai/letta-code 0.31.11 → 0.31.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +16 -14
  2. package/dist/agent-presets.js +3 -3
  3. package/dist/agent-presets.js.map +1 -1
  4. package/dist/channels-slack.js +20 -1
  5. package/dist/channels-slack.js.map +3 -3
  6. package/dist/gateway-core.js +2 -1
  7. package/dist/gateway-core.js.map +3 -3
  8. package/dist/mcp-client.js +4 -4
  9. package/dist/mcp-client.js.map +2 -2
  10. package/dist/types/agent/subagents/manager.d.ts +1 -1
  11. package/dist/types/agent/subagents/manager.d.ts.map +1 -1
  12. package/dist/types/backend/api/client.d.ts.map +1 -1
  13. package/dist/types/backend/backend.d.ts +1 -0
  14. package/dist/types/backend/backend.d.ts.map +1 -1
  15. package/dist/types/channels/message-channel-tool-definition.d.ts.map +1 -1
  16. package/dist/types/channels/plugin-types.d.ts +1 -1
  17. package/dist/types/channels/plugin-types.d.ts.map +1 -1
  18. package/dist/types/channels/slack/internal-types.d.ts +5 -0
  19. package/dist/types/channels/slack/internal-types.d.ts.map +1 -1
  20. package/dist/types/channels/slack/message-action-contract.d.ts +2 -0
  21. package/dist/types/channels/slack/message-action-contract.d.ts.map +1 -1
  22. package/dist/types/channels/types.d.ts +1 -0
  23. package/dist/types/channels/types.d.ts.map +1 -1
  24. package/dist/types/cli/helpers/git-context.d.ts +1 -1
  25. package/dist/types/cli/helpers/git-context.d.ts.map +1 -1
  26. package/dist/types/tools/impl/bash.d.ts +2 -0
  27. package/dist/types/tools/impl/bash.d.ts.map +1 -1
  28. package/dist/types/tools/impl/exec-command.d.ts +8 -0
  29. package/dist/types/tools/impl/exec-command.d.ts.map +1 -1
  30. package/dist/types/tools/impl/task.d.ts +2 -2
  31. package/dist/types/websocket/listener/cwd-change.d.ts.map +1 -1
  32. package/dist/types/websocket/listener/device-git-context.d.ts +17 -0
  33. package/dist/types/websocket/listener/device-git-context.d.ts.map +1 -0
  34. package/dist/types/websocket/listener/listener-constants.d.ts.map +1 -1
  35. package/dist/types/websocket/listener/protocol-outbound.d.ts +1 -0
  36. package/dist/types/websocket/listener/protocol-outbound.d.ts.map +1 -1
  37. package/dist/types/websocket/listener/stream-observers.d.ts.map +1 -1
  38. package/dist/types/websocket/listener/worktree-watcher.d.ts.map +1 -1
  39. package/letta.js +1008 -888
  40. package/package.json +3 -2
  41. package/scripts/codex-watch/release-analysis.test.ts +87 -0
  42. package/scripts/codex-watch/release-analysis.ts +57 -7
  43. package/scripts/postinstall-patches.js +37 -14
  44. package/scripts/source-file-size-baseline.json +3 -3
  45. package/skills/browser-use/SKILL.md +22 -61
  46. package/skills/dispatching-coding-agents/SKILL.md +6 -6
  47. package/skills/messaging-agents/SKILL.md +34 -21
  48. package/skills/scheduling-tasks/SKILL.md +1 -1
  49. package/skills/submitting-feedback/SKILL.md +4 -2
  50. package/skills/teleporting-between-environments/SKILL.md +12 -12
  51. package/dist/types/tools/impl/foreground-sleep.d.ts +0 -13
  52. package/dist/types/tools/impl/foreground-sleep.d.ts.map +0 -1
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@letta-ai/letta-code",
3
- "version": "0.31.11",
3
+ "version": "0.31.13",
4
4
  "description": "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
5
5
  "type": "module",
6
- "packageManager": "bun@1.3.10",
6
+ "packageManager": "bun@1.3.14",
7
7
  "bin": {
8
8
  "letta": "letta.js"
9
9
  },
@@ -122,6 +122,7 @@
122
122
  },
123
123
  "license": "Apache-2.0",
124
124
  "engines": {
125
+ "bun": ">=1.3.2",
125
126
  "node": ">=22.19.0"
126
127
  },
127
128
  "publishConfig": {
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  findLatestStableReleaseAfter,
3
+ listStableReleases,
3
4
  type Release,
4
5
  releaseNotesForRange,
5
6
  } from "./release-analysis.ts";
@@ -49,3 +50,89 @@ describe("Codex stable release range", () => {
49
50
  );
50
51
  });
51
52
  });
53
+
54
+ describe("Codex GitHub release fetch", () => {
55
+ test("retries a server error before succeeding", async () => {
56
+ const sleeps: number[] = [];
57
+ let requests = 0;
58
+
59
+ const releases = await listStableReleases({
60
+ fetchImpl: (async () => {
61
+ requests++;
62
+ if (requests === 1) {
63
+ return new Response("Gateway Timeout", { status: 504 });
64
+ }
65
+ return new Response(JSON.stringify(STABLES), { status: 200 });
66
+ }) as typeof fetch,
67
+ sleep: async (delayMs) => {
68
+ sleeps.push(delayMs);
69
+ },
70
+ });
71
+
72
+ expect(requests).toBe(2);
73
+ expect(sleeps).toEqual([1000]);
74
+ expect(releases).toEqual(STABLES);
75
+ });
76
+
77
+ test("retries a network error before succeeding", async () => {
78
+ const sleeps: number[] = [];
79
+ let requests = 0;
80
+
81
+ const releases = await listStableReleases({
82
+ fetchImpl: (async () => {
83
+ requests++;
84
+ if (requests === 1) throw new TypeError("fetch failed: ECONNRESET");
85
+ return new Response(JSON.stringify(STABLES), { status: 200 });
86
+ }) as typeof fetch,
87
+ sleep: async (delayMs) => {
88
+ sleeps.push(delayMs);
89
+ },
90
+ });
91
+
92
+ expect(requests).toBe(2);
93
+ expect(sleeps).toEqual([1000]);
94
+ expect(releases).toEqual(STABLES);
95
+ });
96
+
97
+ test("stops after the bounded retry budget", async () => {
98
+ const sleeps: number[] = [];
99
+ let requests = 0;
100
+
101
+ const result = listStableReleases({
102
+ fetchImpl: (async () => {
103
+ requests++;
104
+ return new Response("Gateway Timeout", { status: 504 });
105
+ }) as typeof fetch,
106
+ sleep: async (delayMs) => {
107
+ sleeps.push(delayMs);
108
+ },
109
+ });
110
+
111
+ await expect(result).rejects.toThrow(
112
+ "GitHub releases API failed (504): Gateway Timeout",
113
+ );
114
+ expect(requests).toBe(3);
115
+ expect(sleeps).toEqual([1000, 2000]);
116
+ });
117
+
118
+ test("fails immediately on a client error", async () => {
119
+ const sleeps: number[] = [];
120
+ let requests = 0;
121
+
122
+ const result = listStableReleases({
123
+ fetchImpl: (async () => {
124
+ requests++;
125
+ return new Response("Not Found", { status: 404 });
126
+ }) as typeof fetch,
127
+ sleep: async (delayMs) => {
128
+ sleeps.push(delayMs);
129
+ },
130
+ });
131
+
132
+ await expect(result).rejects.toThrow(
133
+ "GitHub releases API failed (404): Not Found",
134
+ );
135
+ expect(requests).toBe(1);
136
+ expect(sleeps).toEqual([]);
137
+ });
138
+ });
@@ -21,6 +21,8 @@ export const WATCHED_PATHS = [
21
21
  ];
22
22
 
23
23
  const MAX_COMMITS_PER_PATH = 8;
24
+ const GITHUB_RELEASES_FETCH_ATTEMPTS = 3;
25
+ const GITHUB_RELEASES_RETRY_DELAY_MS = 1_000;
24
26
 
25
27
  export interface Release {
26
28
  tag_name: string;
@@ -167,7 +169,12 @@ export async function analyzeCodexRelease(
167
169
  }
168
170
  }
169
171
 
170
- export async function listStableReleases(): Promise<Release[]> {
172
+ export async function listStableReleases(
173
+ options: {
174
+ fetchImpl?: typeof fetch;
175
+ sleep?: (delayMs: number) => Promise<void>;
176
+ } = {},
177
+ ): Promise<Release[]> {
171
178
  const releases: Release[] = [];
172
179
  const headers: Record<string, string> = {
173
180
  Accept: "application/vnd.github+json",
@@ -175,15 +182,15 @@ export async function listStableReleases(): Promise<Release[]> {
175
182
  };
176
183
  const token = process.env.GH_TOKEN || process.env.GITHUB_TOKEN;
177
184
  if (token) headers.Authorization = `Bearer ${token}`;
185
+ const fetchImpl = options.fetchImpl ?? fetch;
186
+ const sleep =
187
+ options.sleep ??
188
+ ((delayMs: number) =>
189
+ new Promise<void>((resolve) => setTimeout(resolve, delayMs)));
178
190
 
179
191
  for (let page = 1; page <= 10; page++) {
180
192
  const url = `https://api.github.com/repos/${CODEX_REPO}/releases?per_page=100&page=${page}`;
181
- const res = await fetch(url, { headers });
182
- if (!res.ok) {
183
- throw new Error(
184
- `GitHub releases API failed (${res.status}): ${await res.text()}`,
185
- );
186
- }
193
+ const res = await fetchGitHubReleasesPage(url, headers, fetchImpl, sleep);
187
194
  const batch = (await res.json()) as Release[];
188
195
  releases.push(...batch);
189
196
  if (batch.length < 100) break;
@@ -193,6 +200,49 @@ export async function listStableReleases(): Promise<Release[]> {
193
200
  .sort((a, b) => (a.published_at ?? "").localeCompare(b.published_at ?? ""));
194
201
  }
195
202
 
203
+ async function fetchGitHubReleasesPage(
204
+ url: string,
205
+ headers: Record<string, string>,
206
+ fetchImpl: typeof fetch,
207
+ sleep: (delayMs: number) => Promise<void>,
208
+ ): Promise<Response> {
209
+ for (let attempt = 1; ; attempt++) {
210
+ let response: Response;
211
+ try {
212
+ response = await fetchImpl(url, { headers });
213
+ } catch (error) {
214
+ if (attempt === GITHUB_RELEASES_FETCH_ATTEMPTS) {
215
+ throw new Error(
216
+ `GitHub releases API request failed after ${attempt} attempts: ${String(error)}`,
217
+ { cause: error },
218
+ );
219
+ }
220
+ await retryGitHubReleasesFetch(attempt, String(error), sleep);
221
+ continue;
222
+ }
223
+
224
+ if (response.ok) return response;
225
+
226
+ const message = `GitHub releases API failed (${response.status}): ${await response.text()}`;
227
+ if (response.status < 500 || attempt === GITHUB_RELEASES_FETCH_ATTEMPTS) {
228
+ throw new Error(message);
229
+ }
230
+ await retryGitHubReleasesFetch(attempt, message, sleep);
231
+ }
232
+ }
233
+
234
+ async function retryGitHubReleasesFetch(
235
+ attempt: number,
236
+ error: string,
237
+ sleep: (delayMs: number) => Promise<void>,
238
+ ): Promise<void> {
239
+ const delayMs = GITHUB_RELEASES_RETRY_DELAY_MS * 2 ** (attempt - 1);
240
+ console.warn(
241
+ `${error}; retrying GitHub releases request in ${delayMs}ms (${attempt}/${GITHUB_RELEASES_FETCH_ATTEMPTS})`,
242
+ );
243
+ await sleep(delayMs);
244
+ }
245
+
196
246
  function isStableRelease(release: Release): boolean {
197
247
  if (release.draft || release.prerelease) return false;
198
248
  return /^(rust-v|v)?\d+\.\d+\.\d+$/.test(release.tag_name);
@@ -16,6 +16,20 @@ import { fileURLToPath } from "node:url";
16
16
  const __dirname = dirname(fileURLToPath(import.meta.url));
17
17
  const pkgRoot = dirname(__dirname);
18
18
  const require = createRequire(import.meta.url);
19
+ const packageJson = JSON.parse(
20
+ readFileSync(join(pkgRoot, "package.json"), "utf-8"),
21
+ );
22
+ const minimumBunVersion = packageJson.engines.bun.replace(/^>=/, "");
23
+
24
+ function isBunVersionSupported(version) {
25
+ const current = version.split(".").map(Number);
26
+ const minimum = minimumBunVersion.split(".").map(Number);
27
+ for (let index = 0; index < minimum.length; index++) {
28
+ if ((current[index] ?? 0) > (minimum[index] ?? 0)) return true;
29
+ if ((current[index] ?? 0) < (minimum[index] ?? 0)) return false;
30
+ }
31
+ return true;
32
+ }
19
33
 
20
34
  async function copyToResolved(srcRel, targetSpecifier) {
21
35
  const src = join(pkgRoot, srcRel);
@@ -100,7 +114,10 @@ await copyToResolved(
100
114
  "ink/build/hooks/use-input.js",
101
115
  );
102
116
  await copyToResolved("vendor/ink/build/devtools.js", "ink/build/devtools.js");
103
- await copyToResolved("vendor/ink/build/log-update.js", "ink/build/log-update.js");
117
+ await copyToResolved(
118
+ "vendor/ink/build/log-update.js",
119
+ "ink/build/log-update.js",
120
+ );
104
121
  await copyToResolved("vendor/ink/build/wrap-text.js", "ink/build/wrap-text.js");
105
122
 
106
123
  // ink-text-input (optional vendor with externalCursorOffset support)
@@ -111,24 +128,30 @@ await copyToResolved(
111
128
 
112
129
  console.log("[patch] Ink runtime patched");
113
130
 
114
- // On Unix with Bun available, use polyglot shebang to prefer Bun runtime.
131
+ // On Unix with a supported Bun available, use a polyglot shebang to prefer it.
115
132
  // This enables Bun.secrets for secure keychain storage instead of fallback.
116
- // Windows always uses #!/usr/bin/env node (polyglot shebang breaks npm wrappers).
133
+ // Windows and installs with an older Bun keep the Node shebang.
117
134
  if (process.platform !== "win32") {
118
135
  try {
119
- execSync("bun --version", { stdio: "ignore" });
120
- const lettaPath = join(pkgRoot, "letta.js");
121
- if (existsSync(lettaPath)) {
122
- let content = readFileSync(lettaPath, "utf-8");
123
- if (content.startsWith("#!/usr/bin/env node")) {
124
- content = content.replace(
125
- "#!/usr/bin/env node",
126
- `#!/bin/sh
136
+ const bunVersion = execSync("bun --version", { encoding: "utf-8" }).trim();
137
+ if (isBunVersionSupported(bunVersion)) {
138
+ const lettaPath = join(pkgRoot, "letta.js");
139
+ if (existsSync(lettaPath)) {
140
+ let content = readFileSync(lettaPath, "utf-8");
141
+ if (content.startsWith("#!/usr/bin/env node")) {
142
+ content = content.replace(
143
+ "#!/usr/bin/env node",
144
+ `#!/bin/sh
127
145
  ":" //#; exec /usr/bin/env sh -c 'command -v bun >/dev/null && exec bun "$0" "$@" || exec node "$0" "$@"' "$0" "$@"`,
128
- );
129
- writeFileSync(lettaPath, content);
130
- console.log("[patch] Configured letta to prefer Bun runtime");
146
+ );
147
+ writeFileSync(lettaPath, content);
148
+ console.log("[patch] Configured letta to prefer Bun runtime");
149
+ }
131
150
  }
151
+ } else {
152
+ console.log(
153
+ `[patch] Bun ${bunVersion} is below ${minimumBunVersion}; keeping Node runtime`,
154
+ );
132
155
  }
133
156
  } catch {
134
157
  // Bun not available, keep node shebang
@@ -10,7 +10,7 @@
10
10
  "src/cli/app/use-approval-flow.ts": 1163,
11
11
  "src/cli/app/use-configuration-handlers.ts": 1421,
12
12
  "src/cli/app/use-conversation-loop.ts": 2912,
13
- "src/cli/app/use-submit-handler.ts": 4075,
13
+ "src/cli/app/use-submit-handler.ts": 4074,
14
14
  "src/cli/components/AgentSelector.tsx": 1104,
15
15
  "src/cli/components/InputRich.tsx": 2225,
16
16
  "src/cli/components/ModelSelector.tsx": 1259,
@@ -21,7 +21,7 @@
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": 4994,
24
+ "src/headless.ts": 4986,
25
25
  "src/hooks/integration.test.ts": 1147,
26
26
  "src/index.ts": 2773,
27
27
  "src/mods/learning-harness.ts": 2434,
@@ -44,6 +44,6 @@
44
44
  "src/websocket/listener/file-commands.ts": 1053,
45
45
  "src/websocket/listener/lifecycle.ts": 1048,
46
46
  "src/websocket/listener/protocol-inbound.ts": 2207,
47
- "src/websocket/listener/protocol-outbound.ts": 1047,
47
+ "src/websocket/listener/protocol-outbound.ts": 1042,
48
48
  "src/websocket/listener/turn.ts": 1058
49
49
  }
@@ -19,66 +19,30 @@ Protocol reference: https://chromedevtools.github.io/devtools-protocol/.
19
19
  The running browser's exact schema is at `http://127.0.0.1:<port>/json/protocol`;
20
20
  tip-of-tree docs can differ from the installed version.
21
21
 
22
- ## Managed cloud sandbox default: visible browser
23
-
24
- When running in a cloud sandbox, default every browser task to the visible
25
- managed desktop, even when the user did not explicitly ask to watch. Most
26
- browser tasks exist because plain HTTP is not enough; a headless browser is
27
- more likely to trigger bot protection and gives the user no way to observe or
28
- take over. This matters especially for clicking or typing, forms, sign-in,
29
- checkout/payment, CAPTCHAs or bot protection, and user handoff.
30
-
31
- For the first managed-sandbox browser window, use the skill's launcher instead
32
- of assembling Chrome, DISPLAY, or Xvfb commands yourself:
33
-
34
- ```bash
35
- /root/.letta/cloud-skills/browser-use/scripts/open-visible-browser.sh 'https://example.com'
36
- ```
37
-
38
- It starts the managed desktop and launches Chrome through the persistent Cua
39
- Driver with its required root flag. Then load `computer-use` for visible
40
- interaction. If protocol-level control is necessary, use Cua Driver's explicit
41
- `browser_prepare` flow after binding the exact visible window; do not pass
42
- remote-debugging flags through `launch_app`. The launcher exits zero only
43
- after Cua Driver reports an on-screen browser window. If it exits nonzero, stop
44
- and report the launch failure instead of claiming the browser opened.
22
+ ## Visible by default when a display exists
23
+
24
+ When the computer has a display, prefer a visible (headful) browser for any
25
+ task the user might watch or take over: clicking or typing, forms, sign-in,
26
+ checkout/payment, CAPTCHAs or bot protection, and user handoff. Most browser
27
+ tasks exist because plain HTTP is not enough; a headless browser is more
28
+ likely to trigger bot protection and gives the user no way to observe or step
29
+ in. Visible does not mean pixel-driven: keep operating the page over CDP, and
30
+ the user sees every action in the window.
31
+
32
+ Use headless mode only for work the user explicitly wants in the background
33
+ and that cannot require interaction or handoff, such as read-only scraping,
34
+ CI, or screenshot/PDF generation, or when no display exists. A headless page
35
+ does not satisfy a request to open or reopen a site in a browser the user can
36
+ see.
45
37
 
46
38
  When the user asks to review, watch, or take over, leave that browser window
47
39
  open after the task. Do not kill or close it before replying.
48
40
 
49
- 1. Run `start-letta-desktop` and use its exit status as the result. Warnings
50
- from optional services do not mean startup failed when the command exits 0.
51
- If it exits nonzero, stop and report that the managed desktop is
52
- unavailable. Never create another Xvfb, VNC server, or private display: the
53
- Computer viewer only shows the managed desktop.
54
- 2. Load `computer-use`, inspect the managed desktop, and use Cua Driver to
55
- operate an existing Chrome window or launch Chrome there. When launching,
56
- round-trip Chrome's `launch_path` from `cua-driver call list_apps '{}'
57
- instead of rebuilding it; the managed launch path carries required flags.
58
- For forms, sign-in, checkout, CAPTCHA, and bot-protected pages, keep using
59
- Cua Driver so the interaction remains visible and available for user
60
- takeover.
61
- 3. Use CDP only when protocol-level inspection or deterministic automation is
62
- needed. Bind the exact visible browser window with Cua Driver, then use its
63
- explicit `browser_prepare` flow. Do not pass remote-debugging flags
64
- through `launch_app`. Include `--no-sandbox` when running Chrome as
65
- root. Do not add `--headless` or override `DISPLAY`.
66
- 4. Use headless mode only for work the user explicitly wants in the background
67
- and that cannot require interaction or handoff, such as read-only scraping,
68
- CI, or screenshot/PDF generation.
69
- 5. Verify the result through the managed desktop window (Cua Driver window
70
- state or screenshot), not only through DOM output or a screenshot from a
71
- separate process.
72
-
73
- A headless page does not satisfy a request to open or reopen a site in the
74
- user-visible browser.
75
-
76
41
  ## Workflow
77
42
 
78
43
  1. Find a Chromium-based browser (below). If none exists, see "No Chrome installed".
79
- 2. In a managed cloud sandbox, follow the visible-browser default above.
80
- Otherwise, launch with a dedicated profile and remote debugging. Never
81
- attach to the user's normal profile unless explicitly asked.
44
+ 2. Launch with a dedicated profile and remote debugging. Never attach to the
45
+ user's normal profile unless explicitly asked.
82
46
  3. Discover targets via `/json/list`; pick the `"page"` target by URL or title.
83
47
  4. Connect to its `webSocketDebuggerUrl` and enable only the domains you need
84
48
  (usually `Page`, `Runtime`, `DOM`, `Input`; add `Network`, `Log` when debugging).
@@ -124,7 +88,7 @@ Tell the user that browser use requires Chrome or another Chromium-based
124
88
  browser and recommend either:
125
89
 
126
90
  1. Install Chrome on the current computer, then retry the browser task.
127
- 2. Teleport the conversation back to its Cloud sandbox, where the managed
91
+ 2. Teleport the conversation back to its Cloud sandbox, where a
128
92
  browser is already installed.
129
93
 
130
94
  Wait for the user to choose. Do not silently replace the browser task with
@@ -132,9 +96,8 @@ plain HTTP or claim browser automation succeeded.
132
96
 
133
97
  ## Launching
134
98
 
135
- Outside a managed cloud sandbox, use a disposable profile and a fixed port.
136
- Chrome refuses to run as root without `--no-sandbox`, so add that flag when
137
- `id -u` is 0:
99
+ Use a disposable profile and a fixed port. Chrome refuses to run as root
100
+ without `--no-sandbox`, so add that flag when `id -u` is 0:
138
101
 
139
102
  ```bash
140
103
  chrome_args=( \
@@ -149,10 +112,8 @@ chrome_args=( \
149
112
  "$CHROME" "${chrome_args[@]}" https://example.com
150
113
  ```
151
114
 
152
- Outside a managed cloud sandbox, add `--headless=new` only for explicitly
153
- invisible work or when no display exists. In a managed cloud sandbox, follow
154
- the visible-browser rule above and never replace its managed display with a
155
- private one.
115
+ Add `--headless=new` only for explicitly invisible work or when no display
116
+ exists (see "Visible by default" above).
156
117
  With `--remote-debugging-port=0`, read the chosen port from
157
118
  `<user-data-dir>/DevToolsActivePort`. Launch in the background and poll
158
119
  `http://127.0.0.1:9222/json/version` until it responds.
@@ -66,7 +66,7 @@ Different agents have different strengths. Track what works in your memory over
66
66
  - Use `--max-budget-usd N` (Claude Code) to cap spend on exploratory tasks
67
67
 
68
68
  ### Known quirks
69
- - **Claude Code can hang on large repos** with unrestricted tools — consider `--allowedTools "Read Grep Glob"` (no Bash) and shorter timeouts for research tasks
69
+ - **Claude Code can hang on large repos** with unrestricted tools — consider `--tools "Read Grep Glob"` (no Bash) and shorter timeouts for research tasks
70
70
  - **Codex compactions can destroy long trajectories** — for very long tasks, prefer multiple shorter sessions over one marathon
71
71
  - **Opus tends to over-generate** — produces more code than necessary. Good for exploration, verify before applying.
72
72
 
@@ -135,7 +135,7 @@ cd /path/to/repo && claude -p "Read /tmp/my-plan.md and critique it. What am I m
135
135
 
136
136
  ## Handling Failures
137
137
 
138
- - **Timeout**: If an agent times out (especially Claude Code on large repos), try: (1) a shorter, more focused prompt, (2) restricting tools with `--allowedTools`, (3) switching to Codex which handles large repos better
138
+ - **Timeout**: If an agent times out (especially Claude Code on large repos), try: (1) a shorter, more focused prompt, (2) restricting tools with `--tools`, (3) switching to Codex which handles large repos better
139
139
  - **Garbage output**: If results are incoherent, the prompt was probably too vague. Rewrite with more specific file paths and clearer instructions.
140
140
  - **Session errors**: Claude Code can hit "stale approval from interrupted session" — `--dangerously-skip-permissions` prevents this. If Codex errors, start a fresh `exec` session.
141
141
  - **Compaction mid-task**: If a Codex session runs long enough to compact, it may lose earlier context. Break long tasks into smaller sequential sessions.
@@ -153,12 +153,13 @@ claude -p "YOUR PROMPT" --model MODEL --dangerously-skip-permissions
153
153
  | `-p` / `--print` | Non-interactive mode, prints response and exits |
154
154
  | `--dangerously-skip-permissions` | Skip approval prompts (prevents stale approval errors on timeout) |
155
155
  | `--model MODEL` | Alias or model name accepted by the installed CLI; omit to use the configured default |
156
- | `--effort LEVEL` | `low`, `medium`, `high` — controls reasoning depth |
156
+ | `--effort LEVEL` | `low`, `medium`, `high`, `xhigh`, `max` — controls reasoning depth |
157
157
  | `--append-system-prompt "..."` | Inject additional system instructions |
158
- | `--allowedTools "Bash Edit Read"` | Restrict available tools |
158
+ | `--allowedTools "Bash Edit Read"` | Tools that execute without prompting for permission |
159
+ | `--tools "Read Grep Glob"` | Restrict which built-in tools are available |
159
160
  | `--max-budget-usd N` | Cap spend for the invocation |
160
161
  | `--add-dir DIR` | Allow access to an additional directory; does not change the working directory |
161
- | `--output-format json` | Structured output with `session_id`, `cost_usd`, `duration_ms` |
162
+ | `--output-format json` | Structured output with `session_id`, `total_cost_usd`, `duration_ms` |
162
163
 
163
164
  Set Claude Code's working directory with `cd /path/to/repo && claude ...` in the Bash command, not with a Claude flag.
164
165
 
@@ -174,7 +175,6 @@ codex exec "YOUR PROMPT" --sandbox workspace-write
174
175
  | `-m MODEL` | Model accepted by the installed CLI; omit to use the configured default |
175
176
  | `--sandbox MODE` | Select a read-only or writable sandbox |
176
177
  | `-C DIR` | Set working directory |
177
- | `--search` | Enable web search tool |
178
178
  | `review` | Native code review — `codex review --uncommitted` or `codex exec review "prompt"` |
179
179
 
180
180
  ## Session Management
@@ -66,15 +66,24 @@ Results include `agent_id` for each matching message.
66
66
  letta -p --from-agent $LETTA_AGENT_ID --agent <id> "message text"
67
67
  ```
68
68
 
69
- When no `--environment` is specified, the target agent will run in the same
70
- environment as the caller agent.
69
+ When no `--computer` is specified, the target agent will run on the same
70
+ computer as the caller agent.
71
71
 
72
- To route the target agent turn through a specific remote/local environment:
72
+ To route the target agent turn through a specific remote/local computer:
73
73
 
74
74
  ```bash
75
75
  letta -p --from-agent $LETTA_AGENT_ID \
76
76
  --agent <id> \
77
- --environment <name-or-device-id-or-connection-id> \
77
+ --computer <name-or-device-id-or-connection-id> \
78
+ "message text"
79
+ ```
80
+
81
+ Use `--computer cloud` to route through the target agent's cloud sandbox:
82
+
83
+ ```bash
84
+ letta -p --from-agent $LETTA_AGENT_ID \
85
+ --agent <id> \
86
+ --computer cloud \
78
87
  "message text"
79
88
  ```
80
89
 
@@ -83,7 +92,7 @@ letta -p --from-agent $LETTA_AGENT_ID \
83
92
  |-----|----------|-------------|
84
93
  | `--agent <id>` | Yes | Target agent ID to message |
85
94
  | `--from-agent <id>` | Yes | Sender agent ID (injects agent-to-agent system reminder) |
86
- | `--environment <selector>` | No | Route through an online environment by connection name, device ID, or connection ID |
95
+ | `--computer <selector>` | No | Route through `cloud` (target agent's cloud sandbox) or an online computer by connection name, device ID, or connection ID |
87
96
  | `"message text"` | Yes | Message body (positional after flags) |
88
97
 
89
98
  **Example:**
@@ -93,13 +102,17 @@ letta -p --from-agent $LETTA_AGENT_ID \
93
102
  "What do you know about the authentication system?"
94
103
  ```
95
104
 
96
- **Response:**
105
+ **Response (JSON format with `--output json`):**
97
106
  ```json
98
107
  {
99
- "conversation_id": "conversation-xyz789",
100
- "response": "The authentication system uses JWT tokens...",
108
+ "type": "result",
109
+ "subtype": "success",
110
+ "is_error": false,
111
+ "result": "The authentication system uses JWT tokens...",
101
112
  "agent_id": "agent-abc123",
102
- "agent_name": "BackendExpert"
113
+ "conversation_id": "conversation-xyz789",
114
+ "environment": { "source": "same-environment" },
115
+ "usage": { "prompt_tokens": 120, "completion_tokens": 80, "total_tokens": 200, "step_count": 1 }
103
116
  }
104
117
  ```
105
118
 
@@ -109,34 +122,34 @@ letta -p --from-agent $LETTA_AGENT_ID \
109
122
  letta -p --from-agent $LETTA_AGENT_ID --conversation <id> "message text"
110
123
  ```
111
124
 
112
- Add `--environment <selector>` to continue the conversation on a specific environment.
125
+ Add `--computer <selector>` to continue the conversation on a specific computer.
113
126
 
114
- ### Discovering Environments
127
+ ### Discovering Computers
115
128
 
116
129
  ```bash
117
- letta environments list --online-only
130
+ letta computers list --online-only
118
131
  # alias:
119
132
  letta envs list --online-only
120
133
  ```
121
134
 
122
135
  Use `connectionName`, `deviceId`, or `connectionId` from the JSON output as the
123
- `--environment` selector. If a name is ambiguous, prefer `deviceId` or
124
- `connectionId`. In `environments list`, the current local runtime is marked with
136
+ `--computer` selector. If a name is ambiguous, prefer `deviceId` or
137
+ `connectionId`. In `computers list`, the current local runtime is marked with
125
138
  `"isCurrent": true`.
126
139
 
127
- To force the target agent onto the current registered Letta Code environment,
128
- resolve the current environment and pass its `connectionId`:
140
+ To force the target agent onto the current registered Letta Code computer,
141
+ resolve the current computer and pass its `connectionId`:
129
142
 
130
143
  ```bash
131
- CURRENT_ENV=$(letta environments current | jq -r .connectionId)
144
+ CURRENT_COMPUTER=$(letta computers current | jq -r .connectionId)
132
145
  letta -p --from-agent $LETTA_AGENT_ID \
133
146
  --agent agent-abc123 \
134
- --environment "$CURRENT_ENV" \
135
- "Run on my same machine/environment."
147
+ --computer "$CURRENT_COMPUTER" \
148
+ "Run on my same computer."
136
149
  ```
137
150
 
138
- Omit `--environment` when you want the target agent to run in the same
139
- environment as the caller agent.
151
+ Omit `--computer` when you want the target agent to run on the same computer as
152
+ the caller agent.
140
153
 
141
154
  **Arguments:**
142
155
  | Arg | Required | Description |
@@ -21,7 +21,7 @@ This skill lets you create, list, and manage scheduled tasks using the `letta cr
21
21
  Pass a flag only when you have a requirement the default can't infer:
22
22
 
23
23
  - **`--runner cloud`** — the schedule must fire no matter which computers are online; execute in the agent's cloud sandbox.
24
- - **`--computer <deviceId>`** — the work needs a specific connected computer (its filesystem, services, or credentials). Get the deviceId from `letta environments list`. If that computer is offline at fire time, execution falls back to the cloud sandbox.
24
+ - **`--computer <deviceId>`** — the work needs a specific connected computer (its filesystem, services, or credentials). Get the deviceId from `letta computers list`. If that computer is offline at fire time, execution falls back to the cloud sandbox.
25
25
  - **`--runner local`** — the work must only ever run on the current computer, even if that means missing fires while no session is running here.
26
26
 
27
27
  The CLI reports its placement in the command output. If it warns that the schedule is local (this happens when the cloud scheduler cannot reach the current computer), the schedule only fires while a Letta session is running here — read the warning and decide whether that's acceptable.
@@ -1,11 +1,13 @@
1
1
  ---
2
2
  name: submitting-feedback
3
- description: Submits user-approved feedback about Letta Code or the current agent to the Letta team. Load when the user is upset, frustrated, dissatisfied, reports poor agent behavior, or asks to send feedback. Works with cloud-hosted and local agents. Ask before submitting unless the user already explicitly requested submission.
3
+ description: Submits user-approved product feedback and bug reports about Letta Code to the Letta team. Load when the user reports a Letta Code bug, requests a product or developer change, or explicitly asks to send feedback. Do not load for corrections to the current agent's behavior or preferences; those are memory edits. Works with cloud-hosted and local agents. Ask before submitting unless the user already explicitly requested submission.
4
4
  ---
5
5
 
6
6
  # Submitting Feedback
7
7
 
8
- When the user appears upset with the agent, acknowledge the problem and ask whether they want you to submit feedback to the Letta team. Do not submit merely because the user expressed frustration.
8
+ Use this skill for product and developer feedback about Letta Code: reproducible bugs, broken features, confusing product behavior, and requested changes to the software or its developer-facing behavior.
9
+
10
+ Do **not** use this skill when the user corrects how the current agent should behave, communicate, remember, or work with them. Treat that as learning: make the appropriate memory edit so the correction changes the agent's future behavior. A user's frustration with the agent is not by itself product feedback and is not a reason to offer feedback submission.
9
11
 
10
12
  If the user says yes, or directly asks you to submit feedback:
11
13