@letta-ai/letta-code 0.12.1 → 0.12.2

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 (2) hide show
  1. package/letta.js +612 -144
  2. package/package.json +1 -1
package/letta.js CHANGED
@@ -3237,7 +3237,7 @@ var package_default;
3237
3237
  var init_package = __esm(() => {
3238
3238
  package_default = {
3239
3239
  name: "@letta-ai/letta-code",
3240
- version: "0.12.1",
3240
+ version: "0.12.2",
3241
3241
  description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
3242
3242
  type: "module",
3243
3243
  bin: {
@@ -33487,6 +33487,82 @@ var init_shellEnv = __esm(async () => {
33487
33487
  await init_settings_manager();
33488
33488
  });
33489
33489
 
33490
+ // src/tools/impl/shellLaunchers.ts
33491
+ function pushUnique(list, seen, entry) {
33492
+ if (!entry.length || !entry[0])
33493
+ return;
33494
+ const key = entry.join(SEP2);
33495
+ if (seen.has(key))
33496
+ return;
33497
+ seen.add(key);
33498
+ list.push(entry);
33499
+ }
33500
+ function windowsLaunchers(command) {
33501
+ const trimmed = command.trim();
33502
+ if (!trimmed)
33503
+ return [];
33504
+ const launchers = [];
33505
+ const seen = new Set;
33506
+ pushUnique(launchers, seen, [
33507
+ "powershell.exe",
33508
+ "-NoProfile",
33509
+ "-Command",
33510
+ trimmed
33511
+ ]);
33512
+ pushUnique(launchers, seen, ["pwsh", "-NoProfile", "-Command", trimmed]);
33513
+ const envComSpecRaw = process.env.ComSpec || process.env.COMSPEC;
33514
+ const envComSpec = envComSpecRaw?.trim();
33515
+ if (envComSpec) {
33516
+ pushUnique(launchers, seen, [envComSpec, "/d", "/s", "/c", trimmed]);
33517
+ }
33518
+ pushUnique(launchers, seen, ["cmd.exe", "/d", "/s", "/c", trimmed]);
33519
+ return launchers;
33520
+ }
33521
+ function unixLaunchers(command) {
33522
+ const trimmed = command.trim();
33523
+ if (!trimmed)
33524
+ return [];
33525
+ const launchers = [];
33526
+ const seen = new Set;
33527
+ if (process.platform === "darwin") {
33528
+ pushUnique(launchers, seen, ["/bin/zsh", "-c", trimmed]);
33529
+ }
33530
+ const envShell = process.env.SHELL?.trim();
33531
+ if (envShell) {
33532
+ pushUnique(launchers, seen, [envShell, "-c", trimmed]);
33533
+ }
33534
+ const defaults2 = process.platform === "darwin" ? [
33535
+ ["/bin/zsh", "-c", trimmed],
33536
+ ["bash", "-c", trimmed],
33537
+ ["/bin/bash", "-c", trimmed],
33538
+ ["/usr/bin/bash", "-c", trimmed],
33539
+ ["/bin/sh", "-c", trimmed],
33540
+ ["/bin/ash", "-c", trimmed],
33541
+ ["/usr/bin/env", "zsh", "-c", trimmed],
33542
+ ["/usr/bin/env", "bash", "-c", trimmed],
33543
+ ["/usr/bin/env", "sh", "-c", trimmed],
33544
+ ["/usr/bin/env", "ash", "-c", trimmed]
33545
+ ] : [
33546
+ ["/bin/bash", "-c", trimmed],
33547
+ ["/usr/bin/bash", "-c", trimmed],
33548
+ ["/bin/zsh", "-c", trimmed],
33549
+ ["/bin/sh", "-c", trimmed],
33550
+ ["/bin/ash", "-c", trimmed],
33551
+ ["/usr/bin/env", "bash", "-c", trimmed],
33552
+ ["/usr/bin/env", "zsh", "-c", trimmed],
33553
+ ["/usr/bin/env", "sh", "-c", trimmed],
33554
+ ["/usr/bin/env", "ash", "-c", trimmed]
33555
+ ];
33556
+ for (const entry of defaults2) {
33557
+ pushUnique(launchers, seen, entry);
33558
+ }
33559
+ return launchers;
33560
+ }
33561
+ function buildShellLaunchers(command) {
33562
+ return process.platform === "win32" ? windowsLaunchers(command) : unixLaunchers(command);
33563
+ }
33564
+ var SEP2 = "\x00";
33565
+
33490
33566
  // src/tools/impl/truncation.ts
33491
33567
  function truncateByChars(text, maxChars, _toolName = "output") {
33492
33568
  if (text.length <= maxChars) {
@@ -33520,35 +33596,24 @@ __export(exports_Bash, {
33520
33596
  bash: () => bash
33521
33597
  });
33522
33598
  import { spawn } from "node:child_process";
33523
- import { existsSync as existsSync4 } from "node:fs";
33524
- function getShellConfig() {
33525
- if (cachedShellConfig) {
33526
- return cachedShellConfig;
33527
- }
33528
- if (process.platform === "win32") {
33529
- cachedShellConfig = {
33530
- executable: "powershell.exe",
33531
- args: (cmd) => ["-NoProfile", "-Command", cmd]
33532
- };
33533
- return cachedShellConfig;
33534
- }
33535
- if (process.platform === "darwin" && existsSync4("/bin/zsh")) {
33536
- cachedShellConfig = {
33537
- executable: "/bin/zsh",
33538
- args: (cmd) => ["-c", cmd]
33539
- };
33540
- return cachedShellConfig;
33599
+ function getBackgroundLauncher(command) {
33600
+ if (cachedWorkingLauncher) {
33601
+ const [executable, ...launcherArgs] = cachedWorkingLauncher;
33602
+ if (executable) {
33603
+ return [executable, ...launcherArgs.slice(0, -1), command];
33604
+ }
33541
33605
  }
33542
- cachedShellConfig = {
33543
- executable: "bash",
33544
- args: (cmd) => ["-c", cmd]
33545
- };
33546
- return cachedShellConfig;
33606
+ const launchers = buildShellLaunchers(command);
33607
+ return launchers[0] || [];
33547
33608
  }
33548
- function spawnCommand(command, options) {
33609
+ function spawnWithLauncher(launcher, options) {
33549
33610
  return new Promise((resolve2, reject) => {
33550
- const { executable, args } = getShellConfig();
33551
- const childProcess = spawn(executable, args(command), {
33611
+ const [executable, ...args] = launcher;
33612
+ if (!executable) {
33613
+ reject(new Error("Empty launcher"));
33614
+ return;
33615
+ }
33616
+ const childProcess = spawn(executable, args, {
33552
33617
  cwd: options.cwd,
33553
33618
  env: options.env,
33554
33619
  shell: false,
@@ -33610,6 +33675,52 @@ function spawnCommand(command, options) {
33610
33675
  });
33611
33676
  });
33612
33677
  }
33678
+ async function spawnCommand(command, options) {
33679
+ if (process.platform !== "win32") {
33680
+ const executable = process.platform === "darwin" ? "/bin/zsh" : "bash";
33681
+ return spawnWithLauncher([executable, "-c", command], options);
33682
+ }
33683
+ if (cachedWorkingLauncher) {
33684
+ const [executable, ...launcherArgs] = cachedWorkingLauncher;
33685
+ if (executable) {
33686
+ const newLauncher = [executable, ...launcherArgs.slice(0, -1), command];
33687
+ try {
33688
+ const result = await spawnWithLauncher(newLauncher, options);
33689
+ return result;
33690
+ } catch (error) {
33691
+ const err = error;
33692
+ if (err.code !== "ENOENT") {
33693
+ throw error;
33694
+ }
33695
+ cachedWorkingLauncher = null;
33696
+ }
33697
+ }
33698
+ }
33699
+ const launchers = buildShellLaunchers(command);
33700
+ if (launchers.length === 0) {
33701
+ throw new Error("No shell launchers available");
33702
+ }
33703
+ const tried = [];
33704
+ let lastError = null;
33705
+ for (const launcher of launchers) {
33706
+ try {
33707
+ const result = await spawnWithLauncher(launcher, options);
33708
+ cachedWorkingLauncher = launcher;
33709
+ return result;
33710
+ } catch (error) {
33711
+ const err = error;
33712
+ if (err.code === "ENOENT") {
33713
+ tried.push(launcher[0] || "unknown");
33714
+ lastError = err;
33715
+ continue;
33716
+ }
33717
+ throw error;
33718
+ }
33719
+ }
33720
+ const suffix = tried.filter(Boolean).join(", ");
33721
+ const reason = lastError?.message || "Shell unavailable";
33722
+ throw new Error(suffix ? `${reason} (tried: ${suffix})` : reason);
33723
+ }
33613
33724
  async function bash(args) {
33614
33725
  validateRequiredParams(args, ["command"], "Bash");
33615
33726
  const {
@@ -33641,8 +33752,15 @@ async function bash(args) {
33641
33752
  }
33642
33753
  if (run_in_background) {
33643
33754
  const bashId = getNextBashId();
33644
- const { executable, args: args2 } = getShellConfig();
33645
- const childProcess = spawn(executable, args2(command), {
33755
+ const launcher = getBackgroundLauncher(command);
33756
+ const [executable, ...launcherArgs] = launcher;
33757
+ if (!executable) {
33758
+ return {
33759
+ content: [{ type: "text", text: "No shell available" }],
33760
+ status: "error"
33761
+ };
33762
+ }
33763
+ const childProcess = spawn(executable, launcherArgs, {
33646
33764
  shell: false,
33647
33765
  cwd: userCwd,
33648
33766
  env: getShellEnv()
@@ -33755,7 +33873,7 @@ ${errorMessage}`;
33755
33873
  };
33756
33874
  }
33757
33875
  }
33758
- var cachedShellConfig = null;
33876
+ var cachedWorkingLauncher = null;
33759
33877
  var init_Bash2 = __esm(async () => {
33760
33878
  init_constants();
33761
33879
  init_process_manager();
@@ -34080,6 +34198,20 @@ class PermissionModeManager2 {
34080
34198
  return "allow";
34081
34199
  }
34082
34200
  }
34201
+ const readOnlySubagentTypes = new Set([
34202
+ "explore",
34203
+ "Explore",
34204
+ "plan",
34205
+ "Plan",
34206
+ "recall",
34207
+ "Recall"
34208
+ ]);
34209
+ if (toolName === "Task" || toolName === "task") {
34210
+ const subagentType = toolArgs?.subagent_type;
34211
+ if (subagentType && readOnlySubagentTypes.has(subagentType)) {
34212
+ return "allow";
34213
+ }
34214
+ }
34083
34215
  const shellTools = [
34084
34216
  "Bash",
34085
34217
  "shell",
@@ -42159,69 +42291,6 @@ var init_SearchFileContentGemini2 = __esm(() => {
42159
42291
  init_Grep2();
42160
42292
  });
42161
42293
 
42162
- // src/tools/impl/shellLaunchers.ts
42163
- function pushUnique(list, seen, entry) {
42164
- if (!entry.length || !entry[0])
42165
- return;
42166
- const key = entry.join(SEP2);
42167
- if (seen.has(key))
42168
- return;
42169
- seen.add(key);
42170
- list.push(entry);
42171
- }
42172
- function windowsLaunchers(command) {
42173
- const trimmed = command.trim();
42174
- if (!trimmed)
42175
- return [];
42176
- const launchers = [];
42177
- const seen = new Set;
42178
- pushUnique(launchers, seen, [
42179
- "powershell.exe",
42180
- "-NoProfile",
42181
- "-Command",
42182
- trimmed
42183
- ]);
42184
- pushUnique(launchers, seen, ["pwsh", "-NoProfile", "-Command", trimmed]);
42185
- const envComSpecRaw = process.env.ComSpec || process.env.COMSPEC;
42186
- const envComSpec = envComSpecRaw?.trim();
42187
- if (envComSpec) {
42188
- pushUnique(launchers, seen, [envComSpec, "/d", "/s", "/c", trimmed]);
42189
- }
42190
- pushUnique(launchers, seen, ["cmd.exe", "/d", "/s", "/c", trimmed]);
42191
- return launchers;
42192
- }
42193
- function unixLaunchers(command) {
42194
- const trimmed = command.trim();
42195
- if (!trimmed)
42196
- return [];
42197
- const launchers = [];
42198
- const seen = new Set;
42199
- const envShell = process.env.SHELL?.trim();
42200
- if (envShell) {
42201
- pushUnique(launchers, seen, [envShell, "-lc", trimmed]);
42202
- pushUnique(launchers, seen, [envShell, "-c", trimmed]);
42203
- }
42204
- const defaults3 = [
42205
- ["/bin/bash", "-lc", trimmed],
42206
- ["/usr/bin/bash", "-lc", trimmed],
42207
- ["/bin/zsh", "-lc", trimmed],
42208
- ["/bin/sh", "-c", trimmed],
42209
- ["/bin/ash", "-c", trimmed],
42210
- ["/usr/bin/env", "bash", "-lc", trimmed],
42211
- ["/usr/bin/env", "zsh", "-lc", trimmed],
42212
- ["/usr/bin/env", "sh", "-c", trimmed],
42213
- ["/usr/bin/env", "ash", "-c", trimmed]
42214
- ];
42215
- for (const entry of defaults3) {
42216
- pushUnique(launchers, seen, entry);
42217
- }
42218
- return launchers;
42219
- }
42220
- function buildShellLaunchers(command) {
42221
- return process.platform === "win32" ? windowsLaunchers(command) : unixLaunchers(command);
42222
- }
42223
- var SEP2 = "\x00";
42224
-
42225
42294
  // src/tools/impl/Shell.ts
42226
42295
  import { spawn as spawn2 } from "node:child_process";
42227
42296
  import * as path12 from "node:path";
@@ -42426,7 +42495,7 @@ __export(exports_skills, {
42426
42495
  SKILLS_DIR: () => SKILLS_DIR,
42427
42496
  GLOBAL_SKILLS_DIR: () => GLOBAL_SKILLS_DIR
42428
42497
  });
42429
- import { existsSync as existsSync5 } from "node:fs";
42498
+ import { existsSync as existsSync4 } from "node:fs";
42430
42499
  import { readdir as readdir4, readFile as readFile3 } from "node:fs/promises";
42431
42500
  import { dirname as dirname4, join as join7 } from "node:path";
42432
42501
  import { fileURLToPath as fileURLToPath6 } from "node:url";
@@ -42444,7 +42513,7 @@ async function getBundledSkills() {
42444
42513
  }
42445
42514
  async function discoverSkillsFromDir(skillsPath, source) {
42446
42515
  const errors = [];
42447
- if (!existsSync5(skillsPath)) {
42516
+ if (!existsSync4(skillsPath)) {
42448
42517
  return { skills: [], errors: [] };
42449
42518
  }
42450
42519
  const skills = [];
@@ -45011,7 +45080,7 @@ __export(exports_skills2, {
45011
45080
  SKILLS_DIR: () => SKILLS_DIR2,
45012
45081
  GLOBAL_SKILLS_DIR: () => GLOBAL_SKILLS_DIR2
45013
45082
  });
45014
- import { existsSync as existsSync6 } from "node:fs";
45083
+ import { existsSync as existsSync5 } from "node:fs";
45015
45084
  import { readdir as readdir5, readFile as readFile5 } from "node:fs/promises";
45016
45085
  import { dirname as dirname7, join as join9 } from "node:path";
45017
45086
  import { fileURLToPath as fileURLToPath7 } from "node:url";
@@ -45029,7 +45098,7 @@ async function getBundledSkills2() {
45029
45098
  }
45030
45099
  async function discoverSkillsFromDir2(skillsPath, source) {
45031
45100
  const errors = [];
45032
- if (!existsSync6(skillsPath)) {
45101
+ if (!existsSync5(skillsPath)) {
45033
45102
  return { skills: [], errors: [] };
45034
45103
  }
45035
45104
  const skills = [];
@@ -45269,7 +45338,7 @@ __export(exports_fs, {
45269
45338
  exists: () => exists2
45270
45339
  });
45271
45340
  import {
45272
- existsSync as existsSync7,
45341
+ existsSync as existsSync6,
45273
45342
  readFileSync as fsReadFileSync2,
45274
45343
  writeFileSync as fsWriteFileSync2,
45275
45344
  mkdirSync as mkdirSync2
@@ -45280,13 +45349,13 @@ async function readFile6(path14) {
45280
45349
  }
45281
45350
  async function writeFile2(path14, content) {
45282
45351
  const dir = dirname8(path14);
45283
- if (!existsSync7(dir)) {
45352
+ if (!existsSync6(dir)) {
45284
45353
  mkdirSync2(dir, { recursive: true });
45285
45354
  }
45286
45355
  fsWriteFileSync2(path14, content, { encoding: "utf-8", flush: true });
45287
45356
  }
45288
45357
  function exists2(path14) {
45289
- return existsSync7(path14);
45358
+ return existsSync6(path14);
45290
45359
  }
45291
45360
  async function mkdir2(path14, options) {
45292
45361
  mkdirSync2(path14, options);
@@ -45558,7 +45627,7 @@ __export(exports_subagents2, {
45558
45627
  GLOBAL_AGENTS_DIR: () => GLOBAL_AGENTS_DIR2,
45559
45628
  AGENTS_DIR: () => AGENTS_DIR2
45560
45629
  });
45561
- import { existsSync as existsSync8 } from "node:fs";
45630
+ import { existsSync as existsSync7 } from "node:fs";
45562
45631
  import { readdir as readdir6, readFile as readFile7 } from "node:fs/promises";
45563
45632
  import { join as join10 } from "node:path";
45564
45633
  function isValidName2(name) {
@@ -45641,7 +45710,7 @@ function getBuiltinSubagentNames2() {
45641
45710
  return new Set(Object.keys(getBuiltinSubagents2()));
45642
45711
  }
45643
45712
  async function discoverSubagentsFromDir2(agentsDir, seenNames, subagents, errors) {
45644
- if (!existsSync8(agentsDir)) {
45713
+ if (!existsSync7(agentsDir)) {
45645
45714
  return;
45646
45715
  }
45647
45716
  try {
@@ -52170,6 +52239,89 @@ var init_available_models = __esm(async () => {
52170
52239
  CACHE_TTL_MS = 5 * 60 * 1000;
52171
52240
  });
52172
52241
 
52242
+ // src/ralph/mode.ts
52243
+ function getDefaultState() {
52244
+ return {
52245
+ isActive: false,
52246
+ isYolo: false,
52247
+ originalPrompt: "",
52248
+ completionPromise: null,
52249
+ maxIterations: 0,
52250
+ currentIteration: 0
52251
+ };
52252
+ }
52253
+ function getGlobalState() {
52254
+ const global2 = globalThis;
52255
+ if (!global2[RALPH_KEY]) {
52256
+ global2[RALPH_KEY] = getDefaultState();
52257
+ }
52258
+ return global2[RALPH_KEY];
52259
+ }
52260
+ function setGlobalState(state) {
52261
+ const global2 = globalThis;
52262
+ global2[RALPH_KEY] = state;
52263
+ }
52264
+
52265
+ class RalphModeManager {
52266
+ activate(prompt, completionPromise, maxIterations, isYolo) {
52267
+ let resolvedPromise;
52268
+ if (completionPromise === undefined) {
52269
+ resolvedPromise = DEFAULT_COMPLETION_PROMISE;
52270
+ } else if (completionPromise === null || completionPromise === "" || completionPromise.toLowerCase() === "none") {
52271
+ resolvedPromise = null;
52272
+ } else {
52273
+ resolvedPromise = completionPromise;
52274
+ }
52275
+ setGlobalState({
52276
+ isActive: true,
52277
+ isYolo,
52278
+ originalPrompt: prompt,
52279
+ completionPromise: resolvedPromise,
52280
+ maxIterations,
52281
+ currentIteration: 1
52282
+ });
52283
+ }
52284
+ deactivate() {
52285
+ setGlobalState(getDefaultState());
52286
+ }
52287
+ getState() {
52288
+ return getGlobalState();
52289
+ }
52290
+ incrementIteration() {
52291
+ const state = getGlobalState();
52292
+ setGlobalState({
52293
+ ...state,
52294
+ currentIteration: state.currentIteration + 1
52295
+ });
52296
+ }
52297
+ checkForPromise(text) {
52298
+ const state = getGlobalState();
52299
+ if (!state.completionPromise)
52300
+ return false;
52301
+ const match3 = text.match(/<promise>([\s\S]*?)<\/promise>/i);
52302
+ if (!match3 || match3[1] === undefined)
52303
+ return false;
52304
+ const promiseText = match3[1].trim().replace(/\s+/g, " ");
52305
+ const expected = state.completionPromise.trim().replace(/\s+/g, " ");
52306
+ return promiseText === expected;
52307
+ }
52308
+ shouldContinue() {
52309
+ const state = getGlobalState();
52310
+ if (!state.isActive)
52311
+ return false;
52312
+ if (state.maxIterations > 0 && state.currentIteration >= state.maxIterations) {
52313
+ return false;
52314
+ }
52315
+ return true;
52316
+ }
52317
+ }
52318
+ var DEFAULT_COMPLETION_PROMISE, RALPH_KEY, ralphMode;
52319
+ var init_mode2 = __esm(() => {
52320
+ DEFAULT_COMPLETION_PROMISE = "The task is complete. All requirements have been implemented and verified working. " + "Any tests that were relevant have been run and are passing. The implementation is " + "clean and production-ready. I have not taken any shortcuts or faked anything to " + "meet these requirements.";
52321
+ RALPH_KEY = Symbol.for("@letta/ralphMode");
52322
+ ralphMode = new RalphModeManager;
52323
+ });
52324
+
52173
52325
  // src/settings.ts
52174
52326
  var exports_settings = {};
52175
52327
  __export(exports_settings, {
@@ -55501,9 +55653,9 @@ function getFileEditHeader(toolName, toolArgs) {
55501
55653
  const relPath = relative4(cwd2, filePath);
55502
55654
  const displayPath = relPath.startsWith("..") ? filePath : relPath;
55503
55655
  if (t === "write" || t === "write_file" || t === "writefile" || t === "write_file_gemini" || t === "writefilegemini") {
55504
- const { existsSync: existsSync9 } = __require("node:fs");
55656
+ const { existsSync: existsSync8 } = __require("node:fs");
55505
55657
  try {
55506
- if (existsSync9(filePath)) {
55658
+ if (existsSync8(filePath)) {
55507
55659
  return `Overwrite ${displayPath}?`;
55508
55660
  }
55509
55661
  } catch {}
@@ -56273,7 +56425,7 @@ var init_pasteRegistry = __esm(() => {
56273
56425
 
56274
56426
  // src/cli/helpers/clipboard.ts
56275
56427
  import { execFileSync as execFileSync2 } from "node:child_process";
56276
- import { existsSync as existsSync9, readFileSync as readFileSync2, statSync } from "node:fs";
56428
+ import { existsSync as existsSync8, readFileSync as readFileSync2, statSync } from "node:fs";
56277
56429
  import { basename as basename2, extname, isAbsolute as isAbsolute10, resolve as resolve16 } from "node:path";
56278
56430
  function countLines2(text) {
56279
56431
  return (text.match(/\r\n|\r|\n/g) || []).length + 1;
@@ -56324,7 +56476,7 @@ function translatePasteForImages(paste) {
56324
56476
  if (!isAbsolute10(filePath))
56325
56477
  filePath = resolve16(process.cwd(), filePath);
56326
56478
  const ext3 = extname(filePath || "").toLowerCase();
56327
- if (IMAGE_EXTS.has(ext3) && existsSync9(filePath) && statSync(filePath).isFile()) {
56479
+ if (IMAGE_EXTS.has(ext3) && existsSync8(filePath) && statSync(filePath).isFile()) {
56328
56480
  const buf = readFileSync2(filePath);
56329
56481
  const b64 = buf.toString("base64");
56330
56482
  const mt = ext3 === ".png" ? "image/png" : ext3 === ".jpg" || ext3 === ".jpeg" ? "image/jpeg" : ext3 === ".gif" ? "image/gif" : ext3 === ".webp" ? "image/webp" : ext3 === ".bmp" ? "image/bmp" : ext3 === ".svg" ? "image/svg+xml" : ext3 === ".tif" || ext3 === ".tiff" ? "image/tiff" : ext3 === ".heic" ? "image/heic" : ext3 === ".heif" ? "image/heif" : ext3 === ".avif" ? "image/avif" : "application/octet-stream";
@@ -56912,7 +57064,7 @@ __export(exports_terminalKeybindingInstaller, {
56912
57064
  });
56913
57065
  import {
56914
57066
  copyFileSync,
56915
- existsSync as existsSync10,
57067
+ existsSync as existsSync9,
56916
57068
  mkdirSync as mkdirSync3,
56917
57069
  readFileSync as readFileSync3,
56918
57070
  writeFileSync
@@ -56981,7 +57133,7 @@ function parseKeybindings(content) {
56981
57133
  }
56982
57134
  }
56983
57135
  function keybindingExists(keybindingsPath) {
56984
- if (!existsSync10(keybindingsPath))
57136
+ if (!existsSync9(keybindingsPath))
56985
57137
  return false;
56986
57138
  try {
56987
57139
  const content = readFileSync3(keybindingsPath, { encoding: "utf-8" });
@@ -56994,7 +57146,7 @@ function keybindingExists(keybindingsPath) {
56994
57146
  }
56995
57147
  }
56996
57148
  function createBackup(keybindingsPath) {
56997
- if (!existsSync10(keybindingsPath))
57149
+ if (!existsSync9(keybindingsPath))
56998
57150
  return null;
56999
57151
  const backupPath = `${keybindingsPath}.letta-backup`;
57000
57152
  try {
@@ -57010,12 +57162,12 @@ function installKeybinding(keybindingsPath) {
57010
57162
  return { success: true, alreadyExists: true };
57011
57163
  }
57012
57164
  const parentDir = dirname10(keybindingsPath);
57013
- if (!existsSync10(parentDir)) {
57165
+ if (!existsSync9(parentDir)) {
57014
57166
  mkdirSync3(parentDir, { recursive: true });
57015
57167
  }
57016
57168
  let keybindings = [];
57017
57169
  let backupPath = null;
57018
- if (existsSync10(keybindingsPath)) {
57170
+ if (existsSync9(keybindingsPath)) {
57019
57171
  backupPath = createBackup(keybindingsPath);
57020
57172
  const content = readFileSync3(keybindingsPath, { encoding: "utf-8" });
57021
57173
  const parsed = parseKeybindings(content);
@@ -57045,7 +57197,7 @@ function installKeybinding(keybindingsPath) {
57045
57197
  }
57046
57198
  function removeKeybinding(keybindingsPath) {
57047
57199
  try {
57048
- if (!existsSync10(keybindingsPath)) {
57200
+ if (!existsSync9(keybindingsPath)) {
57049
57201
  return { success: true };
57050
57202
  }
57051
57203
  const content = readFileSync3(keybindingsPath, { encoding: "utf-8" });
@@ -57113,16 +57265,16 @@ function getWezTermConfigPath() {
57113
57265
  const xdgConfig = process.env.XDG_CONFIG_HOME;
57114
57266
  if (xdgConfig) {
57115
57267
  const xdgPath = join14(xdgConfig, "wezterm", "wezterm.lua");
57116
- if (existsSync10(xdgPath))
57268
+ if (existsSync9(xdgPath))
57117
57269
  return xdgPath;
57118
57270
  }
57119
57271
  const configPath = join14(homedir7(), ".config", "wezterm", "wezterm.lua");
57120
- if (existsSync10(configPath))
57272
+ if (existsSync9(configPath))
57121
57273
  return configPath;
57122
57274
  return join14(homedir7(), ".wezterm.lua");
57123
57275
  }
57124
57276
  function wezTermDeleteFixExists(configPath) {
57125
- if (!existsSync10(configPath))
57277
+ if (!existsSync9(configPath))
57126
57278
  return false;
57127
57279
  try {
57128
57280
  const content = readFileSync3(configPath, { encoding: "utf-8" });
@@ -57139,7 +57291,7 @@ function installWezTermDeleteFix() {
57139
57291
  }
57140
57292
  let content = "";
57141
57293
  let backupPath = null;
57142
- if (existsSync10(configPath)) {
57294
+ if (existsSync9(configPath)) {
57143
57295
  backupPath = `${configPath}.letta-backup`;
57144
57296
  copyFileSync(configPath, backupPath);
57145
57297
  content = readFileSync3(configPath, { encoding: "utf-8" });
@@ -57169,7 +57321,7 @@ ${WEZTERM_DELETE_FIX}
57169
57321
  `;
57170
57322
  }
57171
57323
  const parentDir = dirname10(configPath);
57172
- if (!existsSync10(parentDir)) {
57324
+ if (!existsSync9(parentDir)) {
57173
57325
  mkdirSync3(parentDir, { recursive: true });
57174
57326
  }
57175
57327
  writeFileSync(configPath, content, { encoding: "utf-8" });
@@ -57475,6 +57627,20 @@ Location: ${keybindingsPath}`;
57475
57627
  return "Clearing credentials...";
57476
57628
  }
57477
57629
  },
57630
+ "/ralph": {
57631
+ desc: 'Start Ralph Wiggum loop (/ralph [prompt] [--completion-promise "X"] [--max-iterations N])',
57632
+ order: 45,
57633
+ handler: () => {
57634
+ return "Activating ralph mode...";
57635
+ }
57636
+ },
57637
+ "/yolo-ralph": {
57638
+ desc: "Start Ralph loop with bypass permissions (yolo + ralph)",
57639
+ order: 46,
57640
+ handler: () => {
57641
+ return "Activating yolo-ralph mode...";
57642
+ }
57643
+ },
57478
57644
  "/stream": {
57479
57645
  desc: "Toggle token streaming on/off",
57480
57646
  hidden: true,
@@ -57539,7 +57705,7 @@ __export(exports_custom, {
57539
57705
  GLOBAL_COMMANDS_DIR: () => GLOBAL_COMMANDS_DIR,
57540
57706
  COMMANDS_DIR: () => COMMANDS_DIR
57541
57707
  });
57542
- import { existsSync as existsSync11 } from "node:fs";
57708
+ import { existsSync as existsSync10 } from "node:fs";
57543
57709
  import { readdir as readdir7, readFile as readFile8 } from "node:fs/promises";
57544
57710
  import { basename as basename3, dirname as dirname11, join as join15 } from "node:path";
57545
57711
  async function getCustomCommands() {
@@ -57573,7 +57739,7 @@ async function discoverCustomCommands(projectPath = join15(process.cwd(), COMMAN
57573
57739
  return result;
57574
57740
  }
57575
57741
  async function discoverFromDirectory(dirPath, source) {
57576
- if (!existsSync11(dirPath)) {
57742
+ if (!existsSync10(dirPath)) {
57577
57743
  return [];
57578
57744
  }
57579
57745
  const commands2 = [];
@@ -58321,9 +58487,9 @@ function getHeaderText(fileEdit) {
58321
58487
  const relPath = relative4(cwd2, fileEdit.filePath);
58322
58488
  const displayPath = relPath.startsWith("..") ? fileEdit.filePath : relPath;
58323
58489
  if (t === "write" || t === "write_file" || t === "writefile" || t === "write_file_gemini" || t === "writefilegemini") {
58324
- const { existsSync: existsSync12 } = __require("node:fs");
58490
+ const { existsSync: existsSync11 } = __require("node:fs");
58325
58491
  try {
58326
- if (existsSync12(fileEdit.filePath)) {
58492
+ if (existsSync11(fileEdit.filePath)) {
58327
58493
  return `Overwrite ${displayPath}?`;
58328
58494
  }
58329
58495
  } catch {}
@@ -63290,7 +63456,11 @@ function Input({
63290
63456
  currentModelProvider,
63291
63457
  messageQueue,
63292
63458
  onEnterQueueEditMode,
63293
- onEscapeCancel
63459
+ onEscapeCancel,
63460
+ ralphActive = false,
63461
+ ralphPending = false,
63462
+ ralphPendingYolo = false,
63463
+ onRalphExit
63294
63464
  }) {
63295
63465
  const [value, setValue] = import_react54.useState("");
63296
63466
  const [escapePressed, setEscapePressed] = import_react54.useState(false);
@@ -63412,6 +63582,10 @@ function Input({
63412
63582
  console.error(`[debug:InputRich] shift=${key.shift} tab=${key.tab} visible=${visible}`);
63413
63583
  }
63414
63584
  if (key.shift && key.tab) {
63585
+ if (ralphActive && onRalphExit) {
63586
+ onRalphExit();
63587
+ return;
63588
+ }
63415
63589
  const modes = [
63416
63590
  "default",
63417
63591
  "acceptEdits",
@@ -63628,6 +63802,32 @@ function Input({
63628
63802
  setCursorPos(selectedCommand.length);
63629
63803
  };
63630
63804
  const getModeInfo = () => {
63805
+ if (ralphPending) {
63806
+ if (ralphPendingYolo) {
63807
+ return {
63808
+ name: "yolo-ralph (waiting)",
63809
+ color: "#FF8C00"
63810
+ };
63811
+ }
63812
+ return {
63813
+ name: "ralph (waiting)",
63814
+ color: "#FEE19C"
63815
+ };
63816
+ }
63817
+ if (ralphActive) {
63818
+ const ralph = ralphMode.getState();
63819
+ const iterDisplay = ralph.maxIterations > 0 ? `${ralph.currentIteration}/${ralph.maxIterations}` : `${ralph.currentIteration}`;
63820
+ if (ralph.isYolo) {
63821
+ return {
63822
+ name: `yolo-ralph (iter ${iterDisplay})`,
63823
+ color: "#FF8C00"
63824
+ };
63825
+ }
63826
+ return {
63827
+ name: `ralph (iter ${iterDisplay})`,
63828
+ color: "#FEE19C"
63829
+ };
63830
+ }
63631
63831
  switch (currentMode) {
63632
63832
  case "acceptEdits":
63633
63833
  return { name: "accept edits", color: colors.status.processing };
@@ -63792,7 +63992,9 @@ function Input({
63792
63992
  dimColor: true,
63793
63993
  children: [
63794
63994
  " ",
63795
- "(shift+tab to cycle)"
63995
+ "(shift+tab to ",
63996
+ ralphActive || ralphPending ? "exit" : "cycle",
63997
+ ")"
63796
63998
  ]
63797
63999
  }, undefined, true, undefined, this)
63798
64000
  ]
@@ -63826,6 +64028,7 @@ var init_InputRich = __esm(async () => {
63826
64028
  init_oauth();
63827
64029
  init_constants();
63828
64030
  init_mode();
64031
+ init_mode2();
63829
64032
  init_useTerminalWidth();
63830
64033
  init_colors();
63831
64034
  await __promiseAll([
@@ -70454,7 +70657,7 @@ var exports_App = {};
70454
70657
  __export(exports_App, {
70455
70658
  default: () => App2
70456
70659
  });
70457
- import { existsSync as existsSync12, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "node:fs";
70660
+ import { existsSync as existsSync11, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "node:fs";
70458
70661
  function uid4(prefix) {
70459
70662
  return `${prefix}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
70460
70663
  }
@@ -70552,7 +70755,7 @@ NOTE: At any point in time through this workflow you should feel free to ask the
70552
70755
  }
70553
70756
  function planFileExists() {
70554
70757
  const planFilePath = permissionMode2.getPlanFilePath();
70555
- return !!planFilePath && existsSync12(planFilePath);
70758
+ return !!planFilePath && existsSync11(planFilePath);
70556
70759
  }
70557
70760
  function getQuestionsFromApproval(approval) {
70558
70761
  const parsed = safeJsonParseOr(approval.toolArgs, {});
@@ -70566,6 +70769,71 @@ function getSkillUnloadReminder() {
70566
70769
  }
70567
70770
  return "";
70568
70771
  }
70772
+ function parseRalphArgs(input) {
70773
+ let rest = input.replace(/^\/(yolo-)?ralph\s*/, "");
70774
+ let completionPromise;
70775
+ const promiseMatch = rest.match(/--completion-promise\s+["']([^"']*)["']/);
70776
+ if (promiseMatch) {
70777
+ const val = promiseMatch[1] ?? "";
70778
+ completionPromise = val === "" || val.toLowerCase() === "none" ? null : val;
70779
+ rest = rest.replace(/--completion-promise\s+["'][^"']*["']\s*/, "");
70780
+ }
70781
+ const maxMatch = rest.match(/--max-iterations\s+(\d+)/);
70782
+ const maxIterations = maxMatch?.[1] ? parseInt(maxMatch[1], 10) : 0;
70783
+ rest = rest.replace(/--max-iterations\s+\d+\s*/, "");
70784
+ const prompt = rest.trim().replace(/^["']|["']$/g, "") || null;
70785
+ return { prompt, completionPromise, maxIterations };
70786
+ }
70787
+ function buildRalphFirstTurnReminder(state) {
70788
+ const iterInfo = state.maxIterations > 0 ? `${state.currentIteration}/${state.maxIterations}` : `${state.currentIteration}`;
70789
+ let reminder = `<system-reminder>
70790
+ \uD83D\uDD04 Ralph Wiggum mode activated (iteration ${iterInfo})
70791
+ `;
70792
+ if (state.completionPromise) {
70793
+ reminder += `
70794
+ ═══════════════════════════════════════════════════════════
70795
+ RALPH LOOP COMPLETION PROMISE
70796
+ ═══════════════════════════════════════════════════════════
70797
+
70798
+ To complete this loop, output this EXACT text:
70799
+ <promise>${state.completionPromise}</promise>
70800
+
70801
+ STRICT REQUIREMENTS (DO NOT VIOLATE):
70802
+ ✓ Use <promise> XML tags EXACTLY as shown above
70803
+ ✓ The statement MUST be completely and unequivocally TRUE
70804
+ ✓ Do NOT output false statements to exit the loop
70805
+ ✓ Do NOT lie even if you think you should exit
70806
+
70807
+ IMPORTANT - Do not circumvent the loop:
70808
+ Even if you believe you're stuck, the task is impossible,
70809
+ or you've been running too long - you MUST NOT output a
70810
+ false promise statement. The loop is designed to continue
70811
+ until the promise is GENUINELY TRUE. Trust the process.
70812
+
70813
+ If the loop should stop, the promise statement will become
70814
+ true naturally. Do not force it by lying.
70815
+ ═══════════════════════════════════════════════════════════
70816
+ `;
70817
+ } else {
70818
+ reminder += `
70819
+ No completion promise set - loop runs until --max-iterations or ESC/Shift+Tab to exit.
70820
+ `;
70821
+ }
70822
+ reminder += `</system-reminder>`;
70823
+ return reminder;
70824
+ }
70825
+ function buildRalphContinuationReminder(state) {
70826
+ const iterInfo = state.maxIterations > 0 ? `${state.currentIteration}/${state.maxIterations}` : `${state.currentIteration}`;
70827
+ if (state.completionPromise) {
70828
+ return `<system-reminder>
70829
+ \uD83D\uDD04 Ralph iteration ${iterInfo} | To stop: output <promise>${state.completionPromise}</promise> (ONLY when statement is TRUE - do not lie to exit!)
70830
+ </system-reminder>`;
70831
+ } else {
70832
+ return `<system-reminder>
70833
+ \uD83D\uDD04 Ralph iteration ${iterInfo} | No completion promise set - loop runs infinitely
70834
+ </system-reminder>`;
70835
+ }
70836
+ }
70569
70837
  function App2({
70570
70838
  agentId: initialAgentId,
70571
70839
  agentState: initialAgentState,
@@ -70623,6 +70891,8 @@ function App2({
70623
70891
  const [autoHandledResults, setAutoHandledResults] = import_react76.useState([]);
70624
70892
  const [autoDeniedApprovals, setAutoDeniedApprovals] = import_react76.useState([]);
70625
70893
  const bashCommandCacheRef = import_react76.useRef([]);
70894
+ const [pendingRalphConfig, setPendingRalphConfig] = import_react76.useState(null);
70895
+ const [uiRalphActive, setUiRalphActive] = import_react76.useState(ralphMode.getState().isActive);
70626
70896
  const currentApproval = pendingApprovals[approvalResults.length];
70627
70897
  const currentApprovalContext = approvalContexts[approvalResults.length];
70628
70898
  const activeApprovalId = currentApproval?.toolCallId ?? null;
@@ -70874,8 +71144,8 @@ function App2({
70874
71144
  if (!planFilePath)
70875
71145
  return;
70876
71146
  try {
70877
- const { readFileSync: readFileSync5, existsSync: existsSync13 } = __require("node:fs");
70878
- if (!existsSync13(planFilePath))
71147
+ const { readFileSync: readFileSync5, existsSync: existsSync12 } = __require("node:fs");
71148
+ if (!existsSync12(planFilePath))
70879
71149
  return;
70880
71150
  const planContent = readFileSync5(planFilePath, "utf-8");
70881
71151
  const previewItem = {
@@ -70998,6 +71268,64 @@ function App2({
70998
71268
  }
70999
71269
  }, [refreshDerived, currentModelId]);
71000
71270
  const processConversation = import_react76.useCallback(async (initialInput, options) => {
71271
+ const handleRalphContinuation = () => {
71272
+ const ralphState = ralphMode.getState();
71273
+ const lines2 = toLines(buffersRef.current);
71274
+ const assistantLines = lines2.filter((l) => l.kind === "assistant");
71275
+ const lastAssistantText = assistantLines.length > 0 ? assistantLines[assistantLines.length - 1]?.text ?? "" : "";
71276
+ if (ralphMode.checkForPromise(lastAssistantText)) {
71277
+ const wasYolo = ralphState.isYolo;
71278
+ ralphMode.deactivate();
71279
+ setUiRalphActive(false);
71280
+ if (wasYolo) {
71281
+ permissionMode2.setMode("default");
71282
+ }
71283
+ const statusId = uid4("status");
71284
+ buffersRef.current.byId.set(statusId, {
71285
+ kind: "status",
71286
+ id: statusId,
71287
+ lines: [
71288
+ `✅ Ralph loop complete: promise detected after ${ralphState.currentIteration} iteration(s)`
71289
+ ]
71290
+ });
71291
+ buffersRef.current.order.push(statusId);
71292
+ refreshDerived();
71293
+ return;
71294
+ }
71295
+ if (!ralphMode.shouldContinue()) {
71296
+ const wasYolo = ralphState.isYolo;
71297
+ ralphMode.deactivate();
71298
+ setUiRalphActive(false);
71299
+ if (wasYolo) {
71300
+ permissionMode2.setMode("default");
71301
+ }
71302
+ const statusId = uid4("status");
71303
+ buffersRef.current.byId.set(statusId, {
71304
+ kind: "status",
71305
+ id: statusId,
71306
+ lines: [
71307
+ `\uD83D\uDED1 Ralph loop: Max iterations (${ralphState.maxIterations}) reached`
71308
+ ]
71309
+ });
71310
+ buffersRef.current.order.push(statusId);
71311
+ refreshDerived();
71312
+ return;
71313
+ }
71314
+ ralphMode.incrementIteration();
71315
+ const newState = ralphMode.getState();
71316
+ const systemMsg = buildRalphContinuationReminder(newState);
71317
+ setTimeout(() => {
71318
+ processConversation([
71319
+ {
71320
+ type: "message",
71321
+ role: "user",
71322
+ content: `${systemMsg}
71323
+
71324
+ ${newState.originalPrompt}`
71325
+ }
71326
+ ], { allowReentry: true });
71327
+ }, 0);
71328
+ };
71001
71329
  const currentInput = [...initialInput];
71002
71330
  const allowReentry = options?.allowReentry ?? false;
71003
71331
  const myGeneration = options?.submissionGeneration ?? conversationGenerationRef.current;
@@ -71113,6 +71441,10 @@ function App2({
71113
71441
  waitingForQueueCancelRef.current = false;
71114
71442
  queueSnapshotRef.current = [];
71115
71443
  }
71444
+ if (ralphMode.getState().isActive) {
71445
+ handleRalphContinuation();
71446
+ return;
71447
+ }
71116
71448
  return;
71117
71449
  }
71118
71450
  if (stopReasonToHandle === "cancelled") {
@@ -71134,6 +71466,18 @@ function App2({
71134
71466
  if (!EAGER_CANCEL) {
71135
71467
  appendError(INTERRUPT_MESSAGE, true);
71136
71468
  }
71469
+ if (ralphMode.getState().isActive) {
71470
+ const statusId = uid4("status");
71471
+ buffersRef.current.byId.set(statusId, {
71472
+ kind: "status",
71473
+ id: statusId,
71474
+ lines: [
71475
+ `⏸️ Ralph loop paused - type to continue or shift+tab to exit`
71476
+ ]
71477
+ });
71478
+ buffersRef.current.order.push(statusId);
71479
+ refreshDerived();
71480
+ }
71137
71481
  }
71138
71482
  return;
71139
71483
  }
@@ -72029,6 +72373,30 @@ function App2({
72029
72373
  });
72030
72374
  return { submitted: true };
72031
72375
  }
72376
+ let justActivatedRalph = false;
72377
+ if (pendingRalphConfig && !msg.startsWith("/")) {
72378
+ const { completionPromise, maxIterations, isYolo } = pendingRalphConfig;
72379
+ ralphMode.activate(msg, completionPromise, maxIterations, isYolo);
72380
+ setUiRalphActive(true);
72381
+ setPendingRalphConfig(null);
72382
+ justActivatedRalph = true;
72383
+ if (isYolo) {
72384
+ permissionMode2.setMode("bypassPermissions");
72385
+ }
72386
+ const ralphState = ralphMode.getState();
72387
+ const statusId = uid4("status");
72388
+ const promiseDisplay = ralphState.completionPromise ? `"${ralphState.completionPromise.slice(0, 50)}${ralphState.completionPromise.length > 50 ? "..." : ""}"` : "(none)";
72389
+ buffersRef.current.byId.set(statusId, {
72390
+ kind: "status",
72391
+ id: statusId,
72392
+ lines: [
72393
+ `\uD83D\uDD04 ${isYolo ? "yolo-ralph" : "ralph"} mode started (iter 1/${maxIterations || "∞"})`,
72394
+ `Promise: ${promiseDisplay}`
72395
+ ]
72396
+ });
72397
+ buffersRef.current.order.push(statusId);
72398
+ refreshDerived();
72399
+ }
72032
72400
  let aliasedMsg = msg;
72033
72401
  if (msg === "exit" || msg === "quit") {
72034
72402
  aliasedMsg = "/exit";
@@ -72249,6 +72617,59 @@ Tip: Use /clear instead to clear the current message buffer.`;
72249
72617
  }
72250
72618
  return { submitted: true };
72251
72619
  }
72620
+ if (trimmed.startsWith("/yolo-ralph") || trimmed.startsWith("/ralph")) {
72621
+ const isYolo = trimmed.startsWith("/yolo-ralph");
72622
+ const { prompt, completionPromise, maxIterations } = parseRalphArgs(trimmed);
72623
+ const cmdId2 = uid4("cmd");
72624
+ if (prompt) {
72625
+ ralphMode.activate(prompt, completionPromise, maxIterations, isYolo);
72626
+ setUiRalphActive(true);
72627
+ if (isYolo) {
72628
+ permissionMode2.setMode("bypassPermissions");
72629
+ }
72630
+ const ralphState = ralphMode.getState();
72631
+ const promiseDisplay = ralphState.completionPromise ? `"${ralphState.completionPromise.slice(0, 50)}${ralphState.completionPromise.length > 50 ? "..." : ""}"` : "(none)";
72632
+ buffersRef.current.byId.set(cmdId2, {
72633
+ kind: "command",
72634
+ id: cmdId2,
72635
+ input: trimmed,
72636
+ output: `\uD83D\uDD04 ${isYolo ? "yolo-ralph" : "ralph"} mode activated (iter 1/${maxIterations || "∞"})
72637
+ Promise: ${promiseDisplay}`,
72638
+ phase: "finished",
72639
+ success: true
72640
+ });
72641
+ buffersRef.current.order.push(cmdId2);
72642
+ refreshDerived();
72643
+ const systemMsg = buildRalphFirstTurnReminder(ralphState);
72644
+ processConversation([
72645
+ {
72646
+ type: "message",
72647
+ role: "user",
72648
+ content: `${systemMsg}
72649
+
72650
+ ${prompt}`
72651
+ }
72652
+ ]);
72653
+ } else {
72654
+ setPendingRalphConfig({ completionPromise, maxIterations, isYolo });
72655
+ const defaultPromisePreview = DEFAULT_COMPLETION_PROMISE.slice(0, 40);
72656
+ buffersRef.current.byId.set(cmdId2, {
72657
+ kind: "command",
72658
+ id: cmdId2,
72659
+ input: trimmed,
72660
+ output: `\uD83D\uDD04 ${isYolo ? "yolo-ralph" : "ralph"} mode ready (waiting for task)
72661
+ Max iterations: ${maxIterations || "unlimited"}
72662
+ Promise: ${completionPromise === null ? "(none)" : completionPromise ?? `"${defaultPromisePreview}..." (default)`}
72663
+
72664
+ Type your task to begin the loop.`,
72665
+ phase: "finished",
72666
+ success: true
72667
+ });
72668
+ buffersRef.current.order.push(cmdId2);
72669
+ refreshDerived();
72670
+ }
72671
+ return { submitted: true };
72672
+ }
72252
72673
  if (msg.trim() === "/stream") {
72253
72674
  const newValue = !tokenStreamingEnabled;
72254
72675
  const cmdId2 = uid4("cmd");
@@ -73035,6 +73456,21 @@ ${prompt}
73035
73456
  }
73036
73457
  const contentParts = buildMessageContentFromDisplay(msg);
73037
73458
  const planModeReminder = getPlanModeReminder();
73459
+ let ralphModeReminder = "";
73460
+ if (ralphMode.getState().isActive) {
73461
+ if (justActivatedRalph) {
73462
+ const ralphState = ralphMode.getState();
73463
+ ralphModeReminder = `${buildRalphFirstTurnReminder(ralphState)}
73464
+
73465
+ `;
73466
+ } else {
73467
+ ralphMode.incrementIteration();
73468
+ const ralphState = ralphMode.getState();
73469
+ ralphModeReminder = `${buildRalphContinuationReminder(ralphState)}
73470
+
73471
+ `;
73472
+ }
73473
+ }
73038
73474
  const skillUnloadReminder = getSkillUnloadReminder();
73039
73475
  let sessionContextReminder = "";
73040
73476
  const sessionContextEnabled = settingsManager.getSetting("sessionContextEnabled");
@@ -73066,7 +73502,7 @@ DO NOT respond to these messages or otherwise consider them in your response unl
73066
73502
  }
73067
73503
  const memoryReminderContent = await buildMemoryReminder(turnCountRef.current);
73068
73504
  turnCountRef.current += 1;
73069
- const allReminders = sessionContextReminder + planModeReminder + skillUnloadReminder + bashCommandPrefix + memoryReminderContent;
73505
+ const allReminders = sessionContextReminder + planModeReminder + ralphModeReminder + skillUnloadReminder + bashCommandPrefix + memoryReminderContent;
73070
73506
  const messageContent = allReminders && typeof contentParts === "string" ? allReminders + contentParts : Array.isArray(contentParts) && allReminders ? [{ type: "text", text: allReminders }, ...contentParts] : contentParts;
73071
73507
  const userId = uid4("user");
73072
73508
  buffersRef.current.byId.set(userId, {
@@ -73365,7 +73801,8 @@ DO NOT respond to these messages or otherwise consider them in your response unl
73365
73801
  tokenStreamingEnabled,
73366
73802
  isAgentBusy,
73367
73803
  setStreaming,
73368
- setCommandRunning
73804
+ setCommandRunning,
73805
+ pendingRalphConfig
73369
73806
  ]);
73370
73807
  const onSubmitRef = import_react76.useRef(onSubmit);
73371
73808
  import_react76.useEffect(() => {
@@ -73965,6 +74402,18 @@ Consider switching to a different system prompt using /system to match.` : null;
73965
74402
  }
73966
74403
  }, [profileConfirmPending, refreshDerived]);
73967
74404
  const [uiPermissionMode, setUiPermissionMode] = import_react76.useState(permissionMode2.getMode());
74405
+ const handleRalphExit = import_react76.useCallback(() => {
74406
+ const ralph = ralphMode.getState();
74407
+ if (ralph.isActive) {
74408
+ const wasYolo = ralph.isYolo;
74409
+ ralphMode.deactivate();
74410
+ setUiRalphActive(false);
74411
+ if (wasYolo) {
74412
+ permissionMode2.setMode("default");
74413
+ setUiPermissionMode("default");
74414
+ }
74415
+ }
74416
+ }, []);
73968
74417
  const handlePermissionModeChange = import_react76.useCallback((mode) => {
73969
74418
  if (mode === "plan") {
73970
74419
  const planPath = generatePlanFilePath();
@@ -74497,7 +74946,11 @@ Plan file path: ${planFilePath}`;
74497
74946
  currentModelProvider,
74498
74947
  messageQueue,
74499
74948
  onEnterQueueEditMode: handleEnterQueueEditMode,
74500
- onEscapeCancel: profileConfirmPending ? handleProfileEscapeCancel : undefined
74949
+ onEscapeCancel: profileConfirmPending ? handleProfileEscapeCancel : undefined,
74950
+ ralphActive: uiRalphActive,
74951
+ ralphPending: pendingRalphConfig !== null,
74952
+ ralphPendingYolo: pendingRalphConfig?.isYolo ?? false,
74953
+ onRalphExit: handleRalphExit
74501
74954
  }, undefined, false, undefined, this)
74502
74955
  }, undefined, false, undefined, this),
74503
74956
  activeOverlay === "model" && /* @__PURE__ */ jsx_dev_runtime56.jsxDEV(ModelSelector, {
@@ -74662,6 +75115,7 @@ var init_App2 = __esm(async () => {
74662
75115
  init_context();
74663
75116
  init_model();
74664
75117
  init_mode();
75118
+ init_mode2();
74665
75119
  init_settings();
74666
75120
  init_colors();
74667
75121
  init_SessionStats();
@@ -74750,7 +75204,7 @@ __export(exports_terminalKeybindingInstaller2, {
74750
75204
  });
74751
75205
  import {
74752
75206
  copyFileSync as copyFileSync2,
74753
- existsSync as existsSync13,
75207
+ existsSync as existsSync12,
74754
75208
  mkdirSync as mkdirSync4,
74755
75209
  readFileSync as readFileSync5,
74756
75210
  writeFileSync as writeFileSync3
@@ -74819,7 +75273,7 @@ function parseKeybindings2(content) {
74819
75273
  }
74820
75274
  }
74821
75275
  function keybindingExists2(keybindingsPath) {
74822
- if (!existsSync13(keybindingsPath))
75276
+ if (!existsSync12(keybindingsPath))
74823
75277
  return false;
74824
75278
  try {
74825
75279
  const content = readFileSync5(keybindingsPath, { encoding: "utf-8" });
@@ -74832,7 +75286,7 @@ function keybindingExists2(keybindingsPath) {
74832
75286
  }
74833
75287
  }
74834
75288
  function createBackup2(keybindingsPath) {
74835
- if (!existsSync13(keybindingsPath))
75289
+ if (!existsSync12(keybindingsPath))
74836
75290
  return null;
74837
75291
  const backupPath = `${keybindingsPath}.letta-backup`;
74838
75292
  try {
@@ -74848,12 +75302,12 @@ function installKeybinding2(keybindingsPath) {
74848
75302
  return { success: true, alreadyExists: true };
74849
75303
  }
74850
75304
  const parentDir = dirname12(keybindingsPath);
74851
- if (!existsSync13(parentDir)) {
75305
+ if (!existsSync12(parentDir)) {
74852
75306
  mkdirSync4(parentDir, { recursive: true });
74853
75307
  }
74854
75308
  let keybindings = [];
74855
75309
  let backupPath = null;
74856
- if (existsSync13(keybindingsPath)) {
75310
+ if (existsSync12(keybindingsPath)) {
74857
75311
  backupPath = createBackup2(keybindingsPath);
74858
75312
  const content = readFileSync5(keybindingsPath, { encoding: "utf-8" });
74859
75313
  const parsed = parseKeybindings2(content);
@@ -74883,7 +75337,7 @@ function installKeybinding2(keybindingsPath) {
74883
75337
  }
74884
75338
  function removeKeybinding2(keybindingsPath) {
74885
75339
  try {
74886
- if (!existsSync13(keybindingsPath)) {
75340
+ if (!existsSync12(keybindingsPath)) {
74887
75341
  return { success: true };
74888
75342
  }
74889
75343
  const content = readFileSync5(keybindingsPath, { encoding: "utf-8" });
@@ -74951,16 +75405,16 @@ function getWezTermConfigPath2() {
74951
75405
  const xdgConfig = process.env.XDG_CONFIG_HOME;
74952
75406
  if (xdgConfig) {
74953
75407
  const xdgPath = join17(xdgConfig, "wezterm", "wezterm.lua");
74954
- if (existsSync13(xdgPath))
75408
+ if (existsSync12(xdgPath))
74955
75409
  return xdgPath;
74956
75410
  }
74957
75411
  const configPath = join17(homedir8(), ".config", "wezterm", "wezterm.lua");
74958
- if (existsSync13(configPath))
75412
+ if (existsSync12(configPath))
74959
75413
  return configPath;
74960
75414
  return join17(homedir8(), ".wezterm.lua");
74961
75415
  }
74962
75416
  function wezTermDeleteFixExists2(configPath) {
74963
- if (!existsSync13(configPath))
75417
+ if (!existsSync12(configPath))
74964
75418
  return false;
74965
75419
  try {
74966
75420
  const content = readFileSync5(configPath, { encoding: "utf-8" });
@@ -74977,7 +75431,7 @@ function installWezTermDeleteFix2() {
74977
75431
  }
74978
75432
  let content = "";
74979
75433
  let backupPath = null;
74980
- if (existsSync13(configPath)) {
75434
+ if (existsSync12(configPath)) {
74981
75435
  backupPath = `${configPath}.letta-backup`;
74982
75436
  copyFileSync2(configPath, backupPath);
74983
75437
  content = readFileSync5(configPath, { encoding: "utf-8" });
@@ -75007,7 +75461,7 @@ ${WEZTERM_DELETE_FIX2}
75007
75461
  `;
75008
75462
  }
75009
75463
  const parentDir = dirname12(configPath);
75010
- if (!existsSync13(parentDir)) {
75464
+ if (!existsSync12(parentDir)) {
75011
75465
  mkdirSync4(parentDir, { recursive: true });
75012
75466
  }
75013
75467
  writeFileSync3(configPath, content, { encoding: "utf-8" });
@@ -76177,6 +76631,20 @@ class PermissionModeManager {
76177
76631
  return "allow";
76178
76632
  }
76179
76633
  }
76634
+ const readOnlySubagentTypes = new Set([
76635
+ "explore",
76636
+ "Explore",
76637
+ "plan",
76638
+ "Plan",
76639
+ "recall",
76640
+ "Recall"
76641
+ ]);
76642
+ if (toolName === "Task" || toolName === "task") {
76643
+ const subagentType = toolArgs?.subagent_type;
76644
+ if (subagentType && readOnlySubagentTypes.has(subagentType)) {
76645
+ return "allow";
76646
+ }
76647
+ }
76180
76648
  const shellTools = [
76181
76649
  "Bash",
76182
76650
  "shell",
@@ -77550,9 +78018,9 @@ Note: Flags should use double dashes for full names (e.g., --yolo, not -yolo)`);
77550
78018
  process.exit(1);
77551
78019
  }
77552
78020
  const { resolve: resolve19 } = await import("path");
77553
- const { existsSync: existsSync14 } = await import("fs");
78021
+ const { existsSync: existsSync13 } = await import("fs");
77554
78022
  const resolvedPath = resolve19(fromAfFile);
77555
- if (!existsSync14(resolvedPath)) {
78023
+ if (!existsSync13(resolvedPath)) {
77556
78024
  console.error(`Error: AgentFile not found: ${resolvedPath}`);
77557
78025
  process.exit(1);
77558
78026
  }
@@ -78037,4 +78505,4 @@ Error during initialization: ${message}`);
78037
78505
  }
78038
78506
  main();
78039
78507
 
78040
- //# debugId=D2E37E4C34BABD1E64756E2164756E21
78508
+ //# debugId=D15B3A68114566C264756E2164756E21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letta-ai/letta-code",
3
- "version": "0.12.1",
3
+ "version": "0.12.2",
4
4
  "description": "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
5
5
  "type": "module",
6
6
  "bin": {