@letta-ai/letta-code 0.19.3 → 0.19.5

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.
@@ -312,7 +312,7 @@ export interface QueueBatchDequeuedEvent extends MessageEnvelope {
312
312
  }
313
313
  /**
314
314
  * Why the queue cannot dequeue right now.
315
- * - streaming: Agent turn is actively streaming
315
+ * - streaming: Agent turn is actively running/streaming (request, response, or local tool execution)
316
316
  * - pending_approvals: Waiting for HITL approval decisions
317
317
  * - overlay_open: Plan mode, AskUserQuestion, or other overlay is active
318
318
  * - command_running: Slash command is executing
package/letta.js CHANGED
@@ -3240,7 +3240,7 @@ var package_default;
3240
3240
  var init_package = __esm(() => {
3241
3241
  package_default = {
3242
3242
  name: "@letta-ai/letta-code",
3243
- version: "0.19.3",
3243
+ version: "0.19.5",
3244
3244
  description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
3245
3245
  type: "module",
3246
3246
  bin: {
@@ -3308,12 +3308,12 @@ var init_package = __esm(() => {
3308
3308
  fix: "bunx --bun @biomejs/biome@2.2.5 check --write src",
3309
3309
  typecheck: "tsc --noEmit",
3310
3310
  check: "bun run scripts/check.js",
3311
- dev: "LETTA_DEBUG=1 bun --loader:.md=text --loader:.mdx=text --loader:.txt=text run src/index.ts",
3311
+ dev: "LETTA_DEBUG=${LETTA_DEBUG:-1} bun --loader:.md=text --loader:.mdx=text --loader:.txt=text run src/index.ts",
3312
3312
  build: "node scripts/postinstall-patches.js && bun run build.js",
3313
3313
  "test:update-chain:manual": "bun run src/tests/update-chain-smoke.ts --mode manual",
3314
3314
  "test:update-chain:startup": "bun run src/tests/update-chain-smoke.ts --mode startup",
3315
3315
  prepublishOnly: "bun run build",
3316
- postinstall: "node scripts/postinstall-patches.js || echo letta: vendor patches skipped"
3316
+ postinstall: `node scripts/postinstall-patches.js || echo letta: vendor patches skipped && node -e "try{require('fs').chmodSync(require('path').join(require.resolve('node-pty/package.json'),'../prebuilds/darwin-arm64/spawn-helper'),0o755)}catch(e){}" || true`
3317
3317
  },
3318
3318
  "lint-staged": {
3319
3319
  "*.{ts,tsx,js,jsx,json}": [
@@ -4858,7 +4858,7 @@ var init_memory_check_reminder = () => {};
4858
4858
  var memory_filesystem_default = `---
4859
4859
  label: memory_filesystem
4860
4860
  description: Filesystem view of memory blocks (system + user)
4861
- limit: 20000
4861
+ limit: 8000
4862
4862
  ---
4863
4863
 
4864
4864
  /memory/
@@ -44712,6 +44712,56 @@ var init_process_manager = __esm(() => {
44712
44712
  backgroundTasks = new Map;
44713
44713
  });
44714
44714
 
44715
+ // src/utils/directoryLimits.ts
44716
+ function parsePositiveIntEnv(value, fallback, min, max) {
44717
+ if (!value || value.trim() === "") {
44718
+ return fallback;
44719
+ }
44720
+ const parsed = Number.parseInt(value.trim(), 10);
44721
+ if (Number.isNaN(parsed)) {
44722
+ return fallback;
44723
+ }
44724
+ if (parsed < min || parsed > max) {
44725
+ return fallback;
44726
+ }
44727
+ return parsed;
44728
+ }
44729
+ function getDirectoryLimits(env3 = process.env) {
44730
+ return {
44731
+ memfsTreeMaxLines: parsePositiveIntEnv(env3[DIRECTORY_LIMIT_ENV.memfsTreeMaxLines], DIRECTORY_LIMIT_DEFAULTS.memfsTreeMaxLines, 2, 50000),
44732
+ memfsTreeMaxChars: parsePositiveIntEnv(env3[DIRECTORY_LIMIT_ENV.memfsTreeMaxChars], DIRECTORY_LIMIT_DEFAULTS.memfsTreeMaxChars, 128, 5000000),
44733
+ memfsTreeMaxChildrenPerDir: parsePositiveIntEnv(env3[DIRECTORY_LIMIT_ENV.memfsTreeMaxChildrenPerDir], DIRECTORY_LIMIT_DEFAULTS.memfsTreeMaxChildrenPerDir, 1, 5000),
44734
+ listDirMaxLimit: parsePositiveIntEnv(env3[DIRECTORY_LIMIT_ENV.listDirMaxLimit], DIRECTORY_LIMIT_DEFAULTS.listDirMaxLimit, 1, 1e4),
44735
+ listDirMaxDepth: parsePositiveIntEnv(env3[DIRECTORY_LIMIT_ENV.listDirMaxDepth], DIRECTORY_LIMIT_DEFAULTS.listDirMaxDepth, 1, 100),
44736
+ listDirMaxOffset: parsePositiveIntEnv(env3[DIRECTORY_LIMIT_ENV.listDirMaxOffset], DIRECTORY_LIMIT_DEFAULTS.listDirMaxOffset, 1, 1e6),
44737
+ listDirMaxCollectedEntries: parsePositiveIntEnv(env3[DIRECTORY_LIMIT_ENV.listDirMaxCollectedEntries], DIRECTORY_LIMIT_DEFAULTS.listDirMaxCollectedEntries, 10, 2000000),
44738
+ listDirMaxChildrenPerDir: parsePositiveIntEnv(env3[DIRECTORY_LIMIT_ENV.listDirMaxChildrenPerDir], DIRECTORY_LIMIT_DEFAULTS.listDirMaxChildrenPerDir, 1, 5000)
44739
+ };
44740
+ }
44741
+ var DIRECTORY_LIMIT_ENV, DIRECTORY_LIMIT_DEFAULTS;
44742
+ var init_directoryLimits = __esm(() => {
44743
+ DIRECTORY_LIMIT_ENV = {
44744
+ memfsTreeMaxLines: "LETTA_MEMFS_TREE_MAX_LINES",
44745
+ memfsTreeMaxChars: "LETTA_MEMFS_TREE_MAX_CHARS",
44746
+ memfsTreeMaxChildrenPerDir: "LETTA_MEMFS_TREE_MAX_CHILDREN_PER_DIR",
44747
+ listDirMaxLimit: "LETTA_LIST_DIR_MAX_LIMIT",
44748
+ listDirMaxDepth: "LETTA_LIST_DIR_MAX_DEPTH",
44749
+ listDirMaxOffset: "LETTA_LIST_DIR_MAX_OFFSET",
44750
+ listDirMaxCollectedEntries: "LETTA_LIST_DIR_MAX_COLLECTED_ENTRIES",
44751
+ listDirMaxChildrenPerDir: "LETTA_LIST_DIR_MAX_CHILDREN_PER_DIR"
44752
+ };
44753
+ DIRECTORY_LIMIT_DEFAULTS = {
44754
+ memfsTreeMaxLines: 500,
44755
+ memfsTreeMaxChars: 20000,
44756
+ memfsTreeMaxChildrenPerDir: 50,
44757
+ listDirMaxLimit: 200,
44758
+ listDirMaxDepth: 5,
44759
+ listDirMaxOffset: 1e4,
44760
+ listDirMaxCollectedEntries: 12000,
44761
+ listDirMaxChildrenPerDir: 50
44762
+ };
44763
+ });
44764
+
44715
44765
  // src/agent/memoryGit.ts
44716
44766
  var exports_memoryGit = {};
44717
44767
  __export(exports_memoryGit, {
@@ -45563,6 +45613,9 @@ __export(exports_memoryFilesystem, {
45563
45613
  ensureMemoryFilesystemDirs: () => ensureMemoryFilesystemDirs,
45564
45614
  enableMemfsIfCloud: () => enableMemfsIfCloud,
45565
45615
  applyMemfsFlags: () => applyMemfsFlags,
45616
+ MEMORY_TREE_MAX_LINES: () => MEMORY_TREE_MAX_LINES,
45617
+ MEMORY_TREE_MAX_CHILDREN_PER_DIR: () => MEMORY_TREE_MAX_CHILDREN_PER_DIR,
45618
+ MEMORY_TREE_MAX_CHARS: () => MEMORY_TREE_MAX_CHARS,
45566
45619
  MEMORY_SYSTEM_DIR: () => MEMORY_SYSTEM_DIR,
45567
45620
  MEMORY_FS_ROOT: () => MEMORY_FS_ROOT,
45568
45621
  MEMORY_FS_MEMORY_DIR: () => MEMORY_FS_MEMORY_DIR,
@@ -45591,7 +45644,7 @@ function labelFromRelativePath(relativePath) {
45591
45644
  const normalized = relativePath.replace(/\\/g, "/");
45592
45645
  return normalized.replace(/\.md$/, "");
45593
45646
  }
45594
- function renderMemoryFilesystemTree(systemLabels, detachedLabels) {
45647
+ function renderMemoryFilesystemTree(systemLabels, detachedLabels, options = {}) {
45595
45648
  const makeNode = () => ({ children: new Map, isFile: false });
45596
45649
  const root = makeNode();
45597
45650
  const insertPath = (base2, label) => {
@@ -45626,20 +45679,75 @@ function renderMemoryFilesystemTree(systemLabels, detachedLabels) {
45626
45679
  return nameA.localeCompare(nameB);
45627
45680
  });
45628
45681
  };
45629
- const lines = ["/memory/"];
45682
+ const limits = getDirectoryLimits();
45683
+ const maxLines = Math.max(2, options.maxLines ?? limits.memfsTreeMaxLines);
45684
+ const maxChars = Math.max(128, options.maxChars ?? limits.memfsTreeMaxChars);
45685
+ const maxChildrenPerDir = Math.max(1, options.maxChildrenPerDir ?? limits.memfsTreeMaxChildrenPerDir);
45686
+ const rootLine = "/memory/";
45687
+ const lines = [rootLine];
45688
+ let totalChars = rootLine.length;
45689
+ const countTreeEntries = (node) => {
45690
+ let total = 0;
45691
+ for (const [, child] of node.children) {
45692
+ total += 1;
45693
+ if (child.children.size > 0) {
45694
+ total += countTreeEntries(child);
45695
+ }
45696
+ }
45697
+ return total;
45698
+ };
45699
+ const canAppendLine = (line) => {
45700
+ const nextLineCount = lines.length + 1;
45701
+ const nextCharCount = totalChars + 1 + line.length;
45702
+ return nextLineCount <= maxLines && nextCharCount <= maxChars;
45703
+ };
45630
45704
  const render2 = (node, prefix) => {
45631
45705
  const entries = sortedEntries(node);
45632
- entries.forEach(([name, child], index) => {
45633
- const isLast = index === entries.length - 1;
45706
+ const visibleEntries = entries.slice(0, maxChildrenPerDir);
45707
+ const omittedEntries = Math.max(0, entries.length - visibleEntries.length);
45708
+ const renderItems = visibleEntries.map(([name, child]) => ({
45709
+ kind: "entry",
45710
+ name,
45711
+ child
45712
+ }));
45713
+ if (omittedEntries > 0) {
45714
+ renderItems.push({ kind: "omitted", omittedCount: omittedEntries });
45715
+ }
45716
+ for (const [index, item] of renderItems.entries()) {
45717
+ const isLast = index === renderItems.length - 1;
45634
45718
  const branch = isLast ? "└──" : "├──";
45635
- lines.push(`${prefix}${branch} ${name}${child.isFile ? "" : "/"}`);
45636
- if (child.children.size > 0) {
45719
+ const line = item.kind === "entry" ? `${prefix}${branch} ${item.name}${item.child.isFile ? "" : "/"}` : `${prefix}${branch} … (${item.omittedCount.toLocaleString()} more entries)`;
45720
+ if (!canAppendLine(line)) {
45721
+ return false;
45722
+ }
45723
+ lines.push(line);
45724
+ totalChars += 1 + line.length;
45725
+ if (item.kind === "entry" && item.child.children.size > 0) {
45637
45726
  const nextPrefix = `${prefix}${isLast ? " " : "│ "}`;
45638
- render2(child, nextPrefix);
45727
+ if (!render2(item.child, nextPrefix)) {
45728
+ return false;
45729
+ }
45639
45730
  }
45640
- });
45731
+ }
45732
+ return true;
45641
45733
  };
45642
- render2(root, "");
45734
+ const totalEntries = countTreeEntries(root);
45735
+ const fullyRendered = render2(root, "");
45736
+ if (!fullyRendered) {
45737
+ while (lines.length > 1) {
45738
+ const shownEntries = Math.max(0, lines.length - 1);
45739
+ const omittedEntries = Math.max(1, totalEntries - shownEntries);
45740
+ const notice = `[Tree truncated: showing ${shownEntries.toLocaleString()} of ${totalEntries.toLocaleString()} entries. ${omittedEntries.toLocaleString()} omitted.]`;
45741
+ if (canAppendLine(notice)) {
45742
+ lines.push(notice);
45743
+ break;
45744
+ }
45745
+ const removed = lines.pop();
45746
+ if (removed) {
45747
+ totalChars -= 1 + removed.length;
45748
+ }
45749
+ }
45750
+ }
45643
45751
  return lines.join(`
45644
45752
  `);
45645
45753
  }
@@ -45721,8 +45829,13 @@ async function enableMemfsIfCloud(agentId) {
45721
45829
  console.warn(`Warning: Could not enable memfs for new agent: ${error instanceof Error ? error.message : String(error)}`);
45722
45830
  }
45723
45831
  }
45724
- var MEMORY_FS_ROOT = ".letta", MEMORY_FS_AGENTS_DIR = "agents", MEMORY_FS_MEMORY_DIR = "memory", MEMORY_SYSTEM_DIR = "system";
45725
- var init_memoryFilesystem = () => {};
45832
+ var MEMORY_FS_ROOT = ".letta", MEMORY_FS_AGENTS_DIR = "agents", MEMORY_FS_MEMORY_DIR = "memory", MEMORY_SYSTEM_DIR = "system", MEMORY_TREE_MAX_LINES, MEMORY_TREE_MAX_CHARS, MEMORY_TREE_MAX_CHILDREN_PER_DIR;
45833
+ var init_memoryFilesystem = __esm(() => {
45834
+ init_directoryLimits();
45835
+ MEMORY_TREE_MAX_LINES = DIRECTORY_LIMIT_DEFAULTS.memfsTreeMaxLines;
45836
+ MEMORY_TREE_MAX_CHARS = DIRECTORY_LIMIT_DEFAULTS.memfsTreeMaxChars;
45837
+ MEMORY_TREE_MAX_CHILDREN_PER_DIR = DIRECTORY_LIMIT_DEFAULTS.memfsTreeMaxChildrenPerDir;
45838
+ });
45726
45839
 
45727
45840
  // src/tools/impl/shellEnv.ts
45728
45841
  var exports_shellEnv = {};
@@ -47153,32 +47266,47 @@ import { promises as fs10 } from "node:fs";
47153
47266
  import * as path10 from "node:path";
47154
47267
  async function list_dir(args) {
47155
47268
  validateRequiredParams(args, ["dir_path"], "list_dir");
47156
- const { dir_path, offset = 1, limit: limit2 = 25, depth = 2 } = args;
47269
+ const limits = getDirectoryLimits();
47270
+ const {
47271
+ dir_path,
47272
+ offset = DEFAULT_OFFSET,
47273
+ limit: limit2 = DEFAULT_LIMIT2,
47274
+ depth = DEFAULT_DEPTH
47275
+ } = args;
47157
47276
  const userCwd = process.env.USER_CWD || process.cwd();
47158
47277
  const resolvedPath = path10.isAbsolute(dir_path) ? dir_path : path10.resolve(userCwd, dir_path);
47159
- if (offset < 1) {
47160
- throw new Error("offset must be a 1-indexed entry number");
47278
+ if (!Number.isInteger(offset) || offset < 1) {
47279
+ throw new Error("offset must be a positive integer (1-indexed)");
47161
47280
  }
47162
- if (limit2 < 1) {
47163
- throw new Error("limit must be greater than zero");
47281
+ if (offset > limits.listDirMaxOffset) {
47282
+ throw new Error(`offset must be less than or equal to ${limits.listDirMaxOffset.toLocaleString()}`);
47283
+ }
47284
+ if (!Number.isInteger(limit2) || limit2 < 1) {
47285
+ throw new Error("limit must be a positive integer");
47164
47286
  }
47165
- if (depth < 1) {
47166
- throw new Error("depth must be greater than zero");
47287
+ if (!Number.isInteger(depth) || depth < 1) {
47288
+ throw new Error("depth must be a positive integer");
47167
47289
  }
47168
- const entries = await listDirSlice(resolvedPath, offset, limit2, depth);
47290
+ const effectiveLimit = Math.min(limit2, limits.listDirMaxLimit);
47291
+ const effectiveDepth = Math.min(depth, limits.listDirMaxDepth);
47292
+ const entries = await listDirSlice(resolvedPath, offset, effectiveLimit, effectiveDepth, limits.listDirMaxCollectedEntries, limits.listDirMaxChildrenPerDir);
47169
47293
  const output = [`Absolute path: ${resolvedPath}`, ...entries];
47294
+ if (effectiveLimit !== limit2 || effectiveDepth !== depth) {
47295
+ output.push(`[Request capped: limit=${limit2}->${effectiveLimit}, depth=${depth}->${effectiveDepth}]`);
47296
+ }
47170
47297
  return { content: output.join(`
47171
47298
  `) };
47172
47299
  }
47173
- async function listDirSlice(dirPath, offset, limit2, maxDepth) {
47300
+ async function listDirSlice(dirPath, offset, limit2, maxDepth, maxCollectedEntries, maxChildrenPerDir) {
47174
47301
  const entries = [];
47175
- await collectEntries(dirPath, "", maxDepth, entries);
47302
+ const maxEntriesToCollect = Math.min(offset + limit2, maxCollectedEntries);
47303
+ const { hitCollectionCap, hitFolderTruncation } = await collectEntries(dirPath, "", maxDepth, entries, maxEntriesToCollect, maxChildrenPerDir);
47176
47304
  if (entries.length === 0) {
47177
47305
  return [];
47178
47306
  }
47179
47307
  const startIndex = offset - 1;
47180
47308
  if (startIndex >= entries.length) {
47181
- throw new Error("offset exceeds directory entry count");
47309
+ throw new Error(`offset exceeds available entries in current view (max offset: ${entries.length.toLocaleString()})`);
47182
47310
  }
47183
47311
  const remainingEntries = entries.length - startIndex;
47184
47312
  const cappedLimit = Math.min(limit2, remainingEntries);
@@ -47190,15 +47318,21 @@ async function listDirSlice(dirPath, offset, limit2, maxDepth) {
47190
47318
  formatted.push(formatEntryLine(entry));
47191
47319
  }
47192
47320
  if (endIndex < entries.length) {
47193
- formatted.push(`More than ${cappedLimit} entries found`);
47321
+ formatted.push(`More entries available. Use offset=${endIndex + 1} to continue.`);
47322
+ } else if (hitCollectionCap || hitFolderTruncation) {
47323
+ formatted.push("More entries may exist beyond the current truncated view.");
47194
47324
  }
47195
47325
  return formatted;
47196
47326
  }
47197
- async function collectEntries(dirPath, relativePrefix, remainingDepth, entries) {
47327
+ async function collectEntries(dirPath, relativePrefix, remainingDepth, entries, maxEntriesToCollect, maxChildrenPerDir) {
47198
47328
  const queue = [
47199
47329
  { absPath: dirPath, prefix: relativePrefix, depth: remainingDepth }
47200
47330
  ];
47331
+ let hitFolderTruncation = false;
47201
47332
  while (queue.length > 0) {
47333
+ if (entries.length >= maxEntriesToCollect) {
47334
+ return { hitCollectionCap: true, hitFolderTruncation };
47335
+ }
47202
47336
  const current = queue.shift();
47203
47337
  if (!current)
47204
47338
  break;
@@ -47238,7 +47372,28 @@ async function collectEntries(dirPath, relativePrefix, remainingDepth, entries)
47238
47372
  throw new Error(`failed to read directory: ${err}`);
47239
47373
  }
47240
47374
  dirEntries.sort((a, b) => a.entry.name.localeCompare(b.entry.name));
47241
- for (const item of dirEntries) {
47375
+ const visibleEntries = dirEntries.slice(0, maxChildrenPerDir);
47376
+ const omittedEntries = Math.max(0, dirEntries.length - visibleEntries.length);
47377
+ if (omittedEntries > 0) {
47378
+ hitFolderTruncation = true;
47379
+ const omittedSortKey = formatEntryName(`${prefix ? `${prefix}/` : ""}￿-omitted`);
47380
+ const omittedDepth = prefix ? prefix.split(path10.sep).length : 0;
47381
+ visibleEntries.push({
47382
+ absPath,
47383
+ relativePath: prefix,
47384
+ kind: "omitted",
47385
+ entry: {
47386
+ name: omittedSortKey,
47387
+ displayName: `… (${omittedEntries.toLocaleString()} more entries)`,
47388
+ depth: omittedDepth,
47389
+ kind: "omitted"
47390
+ }
47391
+ });
47392
+ }
47393
+ for (const item of visibleEntries) {
47394
+ if (entries.length >= maxEntriesToCollect) {
47395
+ return { hitCollectionCap: true, hitFolderTruncation };
47396
+ }
47242
47397
  if (item.kind === "directory" && depth > 1) {
47243
47398
  queue.push({
47244
47399
  absPath: item.absPath,
@@ -47249,6 +47404,7 @@ async function collectEntries(dirPath, relativePrefix, remainingDepth, entries)
47249
47404
  entries.push(item.entry);
47250
47405
  }
47251
47406
  }
47407
+ return { hitCollectionCap: false, hitFolderTruncation };
47252
47408
  }
47253
47409
  function formatEntryName(filePath) {
47254
47410
  const normalized = filePath.replace(/\\/g, "/");
@@ -47276,13 +47432,17 @@ function formatEntryLine(entry) {
47276
47432
  case "other":
47277
47433
  name += "?";
47278
47434
  break;
47435
+ case "omitted":
47436
+ break;
47279
47437
  default:
47280
47438
  break;
47281
47439
  }
47282
47440
  return `${indent}${name}`;
47283
47441
  }
47284
- var MAX_ENTRY_LENGTH = 500, INDENTATION_SPACES = 2;
47285
- var init_ListDirCodex2 = () => {};
47442
+ var MAX_ENTRY_LENGTH = 500, INDENTATION_SPACES = 2, DEFAULT_OFFSET = 1, DEFAULT_LIMIT2 = 25, DEFAULT_DEPTH = 2;
47443
+ var init_ListDirCodex2 = __esm(() => {
47444
+ init_directoryLimits();
47445
+ });
47286
47446
 
47287
47447
  // src/tools/schemas/LS.json
47288
47448
  var LS_default2;
@@ -47450,7 +47610,7 @@ async function memory(args) {
47450
47610
  if (existsSync13(filePath)) {
47451
47611
  throw new Error(`memory create: block already exists at ${pathArg}`);
47452
47612
  }
47453
- const limit2 = args.limit ?? DEFAULT_LIMIT2;
47613
+ const limit2 = args.limit ?? DEFAULT_LIMIT3;
47454
47614
  if (!Number.isInteger(limit2) || limit2 <= 0) {
47455
47615
  throw new Error("memory create: 'limit' must be a positive integer");
47456
47616
  }
@@ -47779,7 +47939,7 @@ function requireString(value, field, command) {
47779
47939
  }
47780
47940
  return value;
47781
47941
  }
47782
- var execFile9, DEFAULT_LIMIT2 = 2000;
47942
+ var execFile9, DEFAULT_LIMIT3 = 2000;
47783
47943
  var init_Memory2 = __esm(async () => {
47784
47944
  init_context();
47785
47945
  await init_client2();
@@ -68043,6 +68203,7 @@ var init_manager3 = __esm(async () => {
68043
68203
  "Glob",
68044
68204
  "Grep",
68045
68205
  "TaskStop",
68206
+ "memory",
68046
68207
  "Read",
68047
68208
  "Skill",
68048
68209
  "Task",
@@ -68052,6 +68213,7 @@ var init_manager3 = __esm(async () => {
68052
68213
  OPENAI_DEFAULT_TOOLS = [
68053
68214
  "shell_command",
68054
68215
  "apply_patch",
68216
+ "memory",
68055
68217
  "update_plan",
68056
68218
  "view_image"
68057
68219
  ];
@@ -68061,6 +68223,7 @@ var init_manager3 = __esm(async () => {
68061
68223
  "list_directory",
68062
68224
  "glob_gemini",
68063
68225
  "search_file_content",
68226
+ "memory",
68064
68227
  "replace",
68065
68228
  "write_file_gemini",
68066
68229
  "write_todos",
@@ -68072,6 +68235,7 @@ var init_manager3 = __esm(async () => {
68072
68235
  "AskUserQuestion",
68073
68236
  "EnterPlanMode",
68074
68237
  "ExitPlanMode",
68238
+ "memory",
68075
68239
  "Task",
68076
68240
  "TaskOutput",
68077
68241
  "TaskStop",
@@ -68085,6 +68249,7 @@ var init_manager3 = __esm(async () => {
68085
68249
  "AskUserQuestion",
68086
68250
  "EnterPlanMode",
68087
68251
  "ExitPlanMode",
68252
+ "memory",
68088
68253
  "Skill",
68089
68254
  "Task",
68090
68255
  "RunShellCommand",
@@ -68175,82 +68340,157 @@ function sendTerminalMessage(socket, message) {
68175
68340
  socket.send(JSON.stringify(message));
68176
68341
  }
68177
68342
  }
68343
+ function makeOutputBatcher(onFlush) {
68344
+ let buffer = "";
68345
+ let timer = null;
68346
+ const flush = () => {
68347
+ if (timer) {
68348
+ clearTimeout(timer);
68349
+ timer = null;
68350
+ }
68351
+ if (buffer.length > 0) {
68352
+ onFlush(buffer);
68353
+ buffer = "";
68354
+ }
68355
+ };
68356
+ return (chunk) => {
68357
+ buffer += chunk;
68358
+ if (buffer.length >= MAX_BUFFER_BYTES) {
68359
+ flush();
68360
+ } else if (!timer) {
68361
+ timer = setTimeout(flush, FLUSH_INTERVAL_MS);
68362
+ }
68363
+ };
68364
+ }
68365
+ function spawnBun(shell2, cwd2, cols, rows, terminal_id, socket) {
68366
+ const handleData = makeOutputBatcher((data) => sendTerminalMessage(socket, { type: "terminal_output", terminal_id, data }));
68367
+ const proc2 = Bun.spawn([shell2], {
68368
+ cwd: cwd2,
68369
+ env: { ...process.env, TERM: "xterm-256color", COLORTERM: "truecolor" },
68370
+ terminal: {
68371
+ cols: cols || 80,
68372
+ rows: rows || 24,
68373
+ data: (_t, chunk) => handleData(new TextDecoder().decode(chunk))
68374
+ }
68375
+ });
68376
+ const terminal = proc2.terminal;
68377
+ if (!terminal) {
68378
+ throw new Error("Bun.spawn terminal object missing — API unavailable");
68379
+ }
68380
+ proc2.exited.then((exitCode) => {
68381
+ const current = terminals.get(terminal_id);
68382
+ if (current && current.pid === proc2.pid) {
68383
+ terminals.delete(terminal_id);
68384
+ sendTerminalMessage(socket, {
68385
+ type: "terminal_exited",
68386
+ terminal_id,
68387
+ exitCode: exitCode ?? 0
68388
+ });
68389
+ }
68390
+ });
68391
+ return {
68392
+ write: (d) => {
68393
+ try {
68394
+ terminal.write(d);
68395
+ } catch {}
68396
+ },
68397
+ resize: (c, r) => {
68398
+ try {
68399
+ terminal.resize(c, r);
68400
+ } catch {}
68401
+ },
68402
+ kill: () => {
68403
+ try {
68404
+ terminal.close();
68405
+ } catch {}
68406
+ try {
68407
+ proc2.kill();
68408
+ } catch {}
68409
+ },
68410
+ pid: proc2.pid,
68411
+ terminalId: terminal_id,
68412
+ spawnedAt: Date.now()
68413
+ };
68414
+ }
68415
+ function spawnNodePty(shell2, cwd2, cols, rows, terminal_id, socket) {
68416
+ const pty = __require("node-pty");
68417
+ const handleData = makeOutputBatcher((data) => sendTerminalMessage(socket, { type: "terminal_output", terminal_id, data }));
68418
+ const ptyProcess = pty.spawn(shell2, [], {
68419
+ name: "xterm-256color",
68420
+ cols: cols || 80,
68421
+ rows: rows || 24,
68422
+ cwd: cwd2,
68423
+ env: {
68424
+ ...process.env,
68425
+ TERM: "xterm-256color",
68426
+ COLORTERM: "truecolor"
68427
+ }
68428
+ });
68429
+ ptyProcess.onData(handleData);
68430
+ ptyProcess.onExit(({ exitCode }) => {
68431
+ const current = terminals.get(terminal_id);
68432
+ if (current && current.pid === ptyProcess.pid) {
68433
+ terminals.delete(terminal_id);
68434
+ sendTerminalMessage(socket, {
68435
+ type: "terminal_exited",
68436
+ terminal_id,
68437
+ exitCode: exitCode ?? 0
68438
+ });
68439
+ }
68440
+ });
68441
+ return {
68442
+ write: (d) => {
68443
+ try {
68444
+ ptyProcess.write(d);
68445
+ } catch {}
68446
+ },
68447
+ resize: (c, r) => {
68448
+ try {
68449
+ ptyProcess.resize(c, r);
68450
+ } catch {}
68451
+ },
68452
+ kill: () => {
68453
+ try {
68454
+ ptyProcess.kill();
68455
+ } catch {}
68456
+ },
68457
+ pid: ptyProcess.pid,
68458
+ terminalId: terminal_id,
68459
+ spawnedAt: Date.now()
68460
+ };
68461
+ }
68178
68462
  function handleTerminalSpawn(msg, socket, cwd2) {
68179
68463
  const { terminal_id, cols, rows } = msg;
68180
- killTerminal(terminal_id);
68181
- const shell2 = getDefaultShell();
68182
- console.log(`[Terminal] Spawning PTY: shell=${shell2}, cwd=${cwd2}, cols=${cols}, rows=${rows}`);
68183
- try {
68184
- const proc2 = Bun.spawn([shell2], {
68185
- cwd: cwd2,
68186
- env: {
68187
- ...process.env,
68188
- TERM: "xterm-256color",
68189
- COLORTERM: "truecolor"
68190
- },
68191
- terminal: {
68192
- cols: cols || 80,
68193
- rows: rows || 24,
68194
- data: (() => {
68195
- let buffer = "";
68196
- let flushTimer = null;
68197
- return (_terminal, data) => {
68198
- buffer += new TextDecoder().decode(data);
68199
- if (!flushTimer) {
68200
- flushTimer = setTimeout(() => {
68201
- if (buffer.length > 0) {
68202
- sendTerminalMessage(socket, {
68203
- type: "terminal_output",
68204
- terminal_id,
68205
- data: buffer
68206
- });
68207
- buffer = "";
68208
- }
68209
- flushTimer = null;
68210
- }, 16);
68211
- }
68212
- };
68213
- })()
68214
- }
68215
- });
68216
- const terminal = proc2.terminal;
68217
- console.log(`[Terminal] proc.pid=${proc2.pid}, terminal=${typeof terminal}, keys=${Object.keys(proc2).join(",")}`);
68218
- if (!terminal) {
68219
- console.error("[Terminal] terminal object is undefined on proc — Bun.Terminal API may not be available");
68464
+ const existing = terminals.get(terminal_id);
68465
+ if (existing && Date.now() - existing.spawnedAt < 2000) {
68466
+ let alive = true;
68467
+ try {
68468
+ existing.write("\r");
68469
+ } catch {
68470
+ alive = false;
68471
+ }
68472
+ if (alive) {
68473
+ console.log(`[Terminal] Reusing session (age=${Date.now() - existing.spawnedAt}ms), pid=${existing.pid}`);
68220
68474
  sendTerminalMessage(socket, {
68221
- type: "terminal_exited",
68475
+ type: "terminal_spawned",
68222
68476
  terminal_id,
68223
- exitCode: 1
68477
+ pid: existing.pid
68224
68478
  });
68225
68479
  return;
68226
68480
  }
68227
- const session = {
68228
- process: proc2,
68229
- terminal,
68230
- terminalId: terminal_id,
68231
- spawnedAt: Date.now()
68232
- };
68481
+ terminals.delete(terminal_id);
68482
+ }
68483
+ killTerminal(terminal_id);
68484
+ const shell2 = getDefaultShell();
68485
+ console.log(`[Terminal] Spawning PTY (${IS_BUN ? "bun" : "node-pty"}): shell=${shell2}, cwd=${cwd2}, cols=${cols}, rows=${rows}`);
68486
+ try {
68487
+ const session = IS_BUN ? spawnBun(shell2, cwd2, cols, rows, terminal_id, socket) : spawnNodePty(shell2, cwd2, cols, rows, terminal_id, socket);
68233
68488
  terminals.set(terminal_id, session);
68234
- console.log(`[Terminal] Session stored for terminal_id=${terminal_id}, map size=${terminals.size}`);
68235
- const myPid = proc2.pid;
68236
- proc2.exited.then((exitCode) => {
68237
- const current = terminals.get(terminal_id);
68238
- if (current && current.process.pid === myPid) {
68239
- console.log(`[Terminal] PTY process exited: terminal_id=${terminal_id}, pid=${myPid}, exitCode=${exitCode}`);
68240
- terminals.delete(terminal_id);
68241
- sendTerminalMessage(socket, {
68242
- type: "terminal_exited",
68243
- terminal_id,
68244
- exitCode: exitCode ?? 0
68245
- });
68246
- } else {
68247
- console.log(`[Terminal] Stale PTY exit ignored: terminal_id=${terminal_id}, pid=${myPid} (current pid=${current?.process.pid})`);
68248
- }
68249
- });
68489
+ console.log(`[Terminal] Session stored for terminal_id=${terminal_id}, pid=${session.pid}`);
68250
68490
  sendTerminalMessage(socket, {
68251
68491
  type: "terminal_spawned",
68252
68492
  terminal_id,
68253
- pid: proc2.pid
68493
+ pid: session.pid
68254
68494
  });
68255
68495
  } catch (error) {
68256
68496
  console.error("[Terminal] Failed to spawn PTY:", error);
@@ -68262,16 +68502,10 @@ function handleTerminalSpawn(msg, socket, cwd2) {
68262
68502
  }
68263
68503
  }
68264
68504
  function handleTerminalInput(msg) {
68265
- const session = terminals.get(msg.terminal_id);
68266
- if (session) {
68267
- session.terminal.write(msg.data);
68268
- }
68505
+ terminals.get(msg.terminal_id)?.write(msg.data);
68269
68506
  }
68270
68507
  function handleTerminalResize(msg) {
68271
- const session = terminals.get(msg.terminal_id);
68272
- if (session) {
68273
- session.terminal.resize(msg.cols, msg.rows);
68274
- }
68508
+ terminals.get(msg.terminal_id)?.resize(msg.cols, msg.rows);
68275
68509
  }
68276
68510
  function handleTerminalKill(msg) {
68277
68511
  const session = terminals.get(msg.terminal_id);
@@ -68284,11 +68518,8 @@ function handleTerminalKill(msg) {
68284
68518
  function killTerminal(terminalId) {
68285
68519
  const session = terminals.get(terminalId);
68286
68520
  if (session) {
68287
- console.log(`[Terminal] killTerminal: terminalId=${terminalId}, pid=${session.process.pid}`);
68288
- try {
68289
- session.terminal.close();
68290
- } catch {}
68291
- session.process.kill();
68521
+ console.log(`[Terminal] killTerminal: terminalId=${terminalId}, pid=${session.pid}`);
68522
+ session.kill();
68292
68523
  terminals.delete(terminalId);
68293
68524
  }
68294
68525
  }
@@ -68297,8 +68528,10 @@ function killAllTerminals() {
68297
68528
  killTerminal(id);
68298
68529
  }
68299
68530
  }
68300
- var terminals;
68531
+ var IS_BUN, FLUSH_INTERVAL_MS = 16, MAX_BUFFER_BYTES, terminals;
68301
68532
  var init_terminalHandler = __esm(() => {
68533
+ IS_BUN = typeof Bun !== "undefined";
68534
+ MAX_BUFFER_BYTES = 64 * 1024;
68302
68535
  terminals = new Map;
68303
68536
  });
68304
68537
 
@@ -69172,7 +69405,7 @@ function resolvePendingApprovalResolver(runtime, response) {
69172
69405
  runtime.pendingApprovalResolvers.delete(requestId);
69173
69406
  runtime.listener.approvalRuntimeKeyByRequestId.delete(requestId);
69174
69407
  if (runtime.pendingApprovalResolvers.size === 0) {
69175
- setLoopStatus(runtime, runtime.isProcessing ? "PROCESSING_API_RESPONSE" : "WAITING_ON_INPUT");
69408
+ setLoopStatus(runtime, "WAITING_ON_INPUT");
69176
69409
  }
69177
69410
  pending.resolve(response);
69178
69411
  emitLoopStatusIfOpen(runtime.listener, {
@@ -69196,7 +69429,7 @@ function rejectPendingApprovalResolvers(runtime, reason) {
69196
69429
  runtime.listener.approvalRuntimeKeyByRequestId.delete(requestId);
69197
69430
  }
69198
69431
  }
69199
- setLoopStatus(runtime, runtime.isProcessing ? "PROCESSING_API_RESPONSE" : "WAITING_ON_INPUT");
69432
+ setLoopStatus(runtime, "WAITING_ON_INPUT");
69200
69433
  emitLoopStatusIfOpen(runtime.listener, {
69201
69434
  agent_id: runtime.agentId,
69202
69435
  conversation_id: runtime.conversationId
@@ -69857,12 +70090,19 @@ function mergeQueuedTurnInput(queued, options) {
69857
70090
 
69858
70091
  // src/websocket/helpers/listenerQueueAdapter.ts
69859
70092
  function getListenerBlockedReason(c) {
69860
- if (c.pendingApprovalsLen > 0)
69861
- return "pending_approvals";
69862
70093
  if (c.cancelRequested)
69863
70094
  return "interrupt_in_progress";
70095
+ if (c.pendingApprovalsLen > 0)
70096
+ return "pending_approvals";
69864
70097
  if (c.isRecoveringApprovals)
69865
70098
  return "runtime_busy";
70099
+ if (c.loopStatus === "WAITING_ON_APPROVAL")
70100
+ return "pending_approvals";
70101
+ if (c.loopStatus === "EXECUTING_COMMAND")
70102
+ return "command_running";
70103
+ if (c.loopStatus === "SENDING_API_REQUEST" || c.loopStatus === "RETRYING_API_REQUEST" || c.loopStatus === "WAITING_FOR_API_RESPONSE" || c.loopStatus === "PROCESSING_API_RESPONSE" || c.loopStatus === "EXECUTING_CLIENT_SIDE_TOOL") {
70104
+ return "streaming";
70105
+ }
69866
70106
  if (c.isProcessing)
69867
70107
  return "runtime_busy";
69868
70108
  return null;
@@ -69876,7 +70116,8 @@ __export(exports_queue, {
69876
70116
  normalizeMessageContentImages: () => normalizeMessageContentImages,
69877
70117
  normalizeInboundMessages: () => normalizeInboundMessages,
69878
70118
  getQueueItemsScope: () => getQueueItemsScope,
69879
- getQueueItemScope: () => getQueueItemScope
70119
+ getQueueItemScope: () => getQueueItemScope,
70120
+ consumeQueuedTurn: () => consumeQueuedTurn
69880
70121
  });
69881
70122
  function getQueueItemScope(item) {
69882
70123
  if (!item) {
@@ -69895,6 +70136,9 @@ function getQueueItemsScope(items) {
69895
70136
  const sameScope = items.every((item) => (item.agentId ?? null) === (first.agentId ?? null) && (item.conversationId ?? null) === (first.conversationId ?? null));
69896
70137
  return sameScope ? getQueueItemScope(first) : {};
69897
70138
  }
70139
+ function hasSameQueueScope(a, b) {
70140
+ return (a.agentId ?? null) === (b.agentId ?? null) && (a.conversationId ?? null) === (b.conversationId ?? null);
70141
+ }
69898
70142
  function mergeDequeuedBatchContent(items) {
69899
70143
  const queuedInputs = [];
69900
70144
  for (const item of items) {
@@ -70012,12 +70256,46 @@ function buildQueuedTurnMessage(runtime, batch) {
70012
70256
  function shouldQueueInboundMessage(parsed) {
70013
70257
  return parsed.messages.some((payload) => ("content" in payload));
70014
70258
  }
70259
+ function consumeQueuedTurn(runtime) {
70260
+ const queuedItems = runtime.queueRuntime.peek();
70261
+ const firstQueuedItem = queuedItems[0];
70262
+ if (!firstQueuedItem || !isCoalescable(firstQueuedItem.kind)) {
70263
+ return null;
70264
+ }
70265
+ let queueLen = 0;
70266
+ let hasMessage = false;
70267
+ for (const item of queuedItems) {
70268
+ if (!isCoalescable(item.kind) || !hasSameQueueScope(firstQueuedItem, item)) {
70269
+ break;
70270
+ }
70271
+ queueLen += 1;
70272
+ if (item.kind === "message") {
70273
+ hasMessage = true;
70274
+ }
70275
+ }
70276
+ if (!hasMessage || queueLen === 0) {
70277
+ return null;
70278
+ }
70279
+ const dequeuedBatch = runtime.queueRuntime.consumeItems(queueLen);
70280
+ if (!dequeuedBatch) {
70281
+ return null;
70282
+ }
70283
+ const queuedTurn = buildQueuedTurnMessage(runtime, dequeuedBatch);
70284
+ if (!queuedTurn) {
70285
+ return null;
70286
+ }
70287
+ return {
70288
+ dequeuedBatch,
70289
+ queuedTurn
70290
+ };
70291
+ }
70015
70292
  function computeListenerQueueBlockedReason(runtime) {
70016
70293
  const activeScope = resolveRuntimeScope(runtime.listener, {
70017
70294
  agent_id: runtime.agentId,
70018
70295
  conversation_id: runtime.conversationId
70019
70296
  });
70020
70297
  return getListenerBlockedReason({
70298
+ loopStatus: runtime.loopStatus,
70021
70299
  isProcessing: runtime.isProcessing,
70022
70300
  pendingApprovalsLen: activeScope ? getPendingControlRequestCount(runtime.listener, activeScope) : 0,
70023
70301
  cancelRequested: runtime.cancelRequested,
@@ -70039,18 +70317,11 @@ async function drainQueuedMessages(runtime, socket, opts, processQueuedTurn) {
70039
70317
  runtime.queueRuntime.tryDequeue(blockedReason);
70040
70318
  return;
70041
70319
  }
70042
- const queueLen = runtime.queueRuntime.length;
70043
- if (queueLen === 0) {
70044
- return;
70045
- }
70046
- const dequeuedBatch = runtime.queueRuntime.consumeItems(queueLen);
70047
- if (!dequeuedBatch) {
70320
+ const consumedQueuedTurn = consumeQueuedTurn(runtime);
70321
+ if (!consumedQueuedTurn) {
70048
70322
  return;
70049
70323
  }
70050
- const queuedTurn = buildQueuedTurnMessage(runtime, dequeuedBatch);
70051
- if (!queuedTurn) {
70052
- continue;
70053
- }
70324
+ const { dequeuedBatch, queuedTurn } = consumedQueuedTurn;
70054
70325
  emitDequeuedUserMessage(socket, runtime, queuedTurn, dequeuedBatch);
70055
70326
  const preTurnStatus = getListenerStatus(runtime.listener) === "processing" ? "processing" : "receiving";
70056
70327
  if (opts.connectionId && runtime.listener.lastEmittedStatus !== preTurnStatus) {
@@ -70085,6 +70356,7 @@ function scheduleQueuePump(runtime, socket, opts, processQueuedTurn) {
70085
70356
  });
70086
70357
  }
70087
70358
  var init_queue = __esm(async () => {
70359
+ init_queueRuntime();
70088
70360
  init_runtime();
70089
70361
  await __promiseAll([
70090
70362
  init_imageResize(),
@@ -74710,17 +74982,26 @@ async function resolveRecoveredApprovalResponse(runtime, socket, response, proce
74710
74982
  runtime.activeAbortController = null;
74711
74983
  setLoopStatus(runtime, "SENDING_API_REQUEST", scope);
74712
74984
  emitRuntimeStateUpdates(runtime, scope);
74985
+ const continuationMessages = [
74986
+ {
74987
+ type: "approval",
74988
+ approvals: approvalResults
74989
+ }
74990
+ ];
74991
+ let continuationBatchId = `batch-recovered-${crypto.randomUUID()}`;
74992
+ const consumedQueuedTurn = consumeQueuedTurn(runtime);
74993
+ if (consumedQueuedTurn) {
74994
+ const { dequeuedBatch, queuedTurn } = consumedQueuedTurn;
74995
+ continuationBatchId = dequeuedBatch.batchId;
74996
+ continuationMessages.push(...queuedTurn.messages);
74997
+ emitDequeuedUserMessage(socket, runtime, queuedTurn, dequeuedBatch);
74998
+ }
74713
74999
  await processTurn({
74714
75000
  type: "message",
74715
75001
  agentId: recovered.agentId,
74716
75002
  conversationId: recovered.conversationId,
74717
- messages: [
74718
- {
74719
- type: "approval",
74720
- approvals: approvalResults
74721
- }
74722
- ]
74723
- }, socket, runtime, opts?.onStatusChange, opts?.connectionId, `batch-recovered-${crypto.randomUUID()}`);
75003
+ messages: continuationMessages
75004
+ }, socket, runtime, opts?.onStatusChange, opts?.connectionId, continuationBatchId);
74724
75005
  clearRecoveredApprovalState(runtime);
74725
75006
  return true;
74726
75007
  } catch (error) {
@@ -74753,7 +75034,8 @@ var init_recovery = __esm(async () => {
74753
75034
  init_stream(),
74754
75035
  init_approval(),
74755
75036
  init_interrupts(),
74756
- init_protocol_outbound()
75037
+ init_protocol_outbound(),
75038
+ init_queue()
74757
75039
  ]);
74758
75040
  });
74759
75041
 
@@ -74877,9 +75159,10 @@ function markAwaitingAcceptedApprovalContinuationRunId(runtime, input) {
74877
75159
  runtime.activeRunId = null;
74878
75160
  }
74879
75161
  }
74880
- async function resolveStaleApprovals(runtime, socket, abortSignal) {
75162
+ async function resolveStaleApprovals(runtime, socket, abortSignal, deps = {}) {
74881
75163
  if (!runtime.agentId)
74882
75164
  return null;
75165
+ const getResumeDataImpl = deps.getResumeData ?? getResumeData2;
74883
75166
  const client = await getClient2();
74884
75167
  let agent;
74885
75168
  try {
@@ -74893,7 +75176,7 @@ async function resolveStaleApprovals(runtime, socket, abortSignal) {
74893
75176
  const requestedConversationId = runtime.conversationId !== "default" ? runtime.conversationId : undefined;
74894
75177
  let resumeData;
74895
75178
  try {
74896
- resumeData = await getResumeData2(client, agent, requestedConversationId, {
75179
+ resumeData = await getResumeDataImpl(client, agent, requestedConversationId, {
74897
75180
  includeMessageHistory: false
74898
75181
  });
74899
75182
  } catch (err) {
@@ -75014,7 +75297,19 @@ async function resolveStaleApprovals(runtime, socket, abortSignal) {
75014
75297
  conversationId: recoveryConversationId
75015
75298
  });
75016
75299
  emitInterruptToolReturnMessage(socket, runtime, approvalResults, runtime.activeRunId ?? undefined, "tool-return");
75017
- const recoveryStream = await sendApprovalContinuationWithRetry(recoveryConversationId, [{ type: "approval", approvals: approvalResults }], {
75300
+ const continuationMessages = [
75301
+ {
75302
+ type: "approval",
75303
+ approvals: approvalResults
75304
+ }
75305
+ ];
75306
+ const consumedQueuedTurn = consumeQueuedTurn(runtime);
75307
+ if (consumedQueuedTurn) {
75308
+ const { dequeuedBatch, queuedTurn } = consumedQueuedTurn;
75309
+ continuationMessages.push(...queuedTurn.messages);
75310
+ emitDequeuedUserMessage(socket, runtime, queuedTurn, dequeuedBatch);
75311
+ }
75312
+ const recoveryStream = await sendApprovalContinuationWithRetry(recoveryConversationId, continuationMessages, {
75018
75313
  agentId: runtime.agentId ?? undefined,
75019
75314
  streamTokens: true,
75020
75315
  background: true,
@@ -75023,6 +75318,7 @@ async function resolveStaleApprovals(runtime, socket, abortSignal) {
75023
75318
  if (!recoveryStream) {
75024
75319
  throw new Error("Approval recovery send resolved without a continuation stream");
75025
75320
  }
75321
+ setLoopStatus(runtime, "PROCESSING_API_RESPONSE", scope);
75026
75322
  const drainResult = await drainRecoveryStreamWithEmission(recoveryStream, socket, runtime, {
75027
75323
  agentId: runtime.agentId ?? undefined,
75028
75324
  conversationId: recoveryConversationId,
@@ -75317,6 +75613,7 @@ var init_send = __esm(async () => {
75317
75613
  init_approval(),
75318
75614
  init_interrupts(),
75319
75615
  init_protocol_outbound(),
75616
+ init_queue(),
75320
75617
  init_recovery()
75321
75618
  ]);
75322
75619
  });
@@ -76155,6 +76452,7 @@ async function handleApprovalStop(params) {
76155
76452
  terminated: true,
76156
76453
  stream: null,
76157
76454
  currentInput,
76455
+ dequeuedBatchId,
76158
76456
  pendingNormalizationInterruptedToolCallIds: [],
76159
76457
  turnToolContextId,
76160
76458
  lastExecutionResults: null,
@@ -76231,11 +76529,10 @@ async function handleApprovalStop(params) {
76231
76529
  });
76232
76530
  }
76233
76531
  } else {
76234
- const denyReason = responseBody.error;
76235
76532
  decisions.push({
76236
76533
  type: "deny",
76237
76534
  approval: ac.approval,
76238
- reason: denyReason
76535
+ reason: responseBody.error
76239
76536
  });
76240
76537
  }
76241
76538
  }
@@ -76250,7 +76547,7 @@ async function handleApprovalStop(params) {
76250
76547
  agent_id: agentId,
76251
76548
  conversation_id: conversationId
76252
76549
  });
76253
- const executionRunId = runId || runtime.activeRunId || params.msgRunIds[params.msgRunIds.length - 1];
76550
+ const executionRunId = runId || runtime.activeRunId || msgRunIds[msgRunIds.length - 1];
76254
76551
  emitToolExecutionStartedEvents(socket, runtime, {
76255
76552
  toolCallIds: lastExecutingToolCallIds,
76256
76553
  runId: executionRunId,
@@ -76282,6 +76579,14 @@ async function handleApprovalStop(params) {
76282
76579
  approvals: persistedExecutionResults
76283
76580
  }
76284
76581
  ];
76582
+ let continuationBatchId = dequeuedBatchId;
76583
+ const consumedQueuedTurn = consumeQueuedTurn(runtime);
76584
+ if (consumedQueuedTurn) {
76585
+ const { dequeuedBatch, queuedTurn } = consumedQueuedTurn;
76586
+ continuationBatchId = dequeuedBatch.batchId;
76587
+ nextInput.push(...queuedTurn.messages);
76588
+ emitDequeuedUserMessage(socket, runtime, queuedTurn, dequeuedBatch);
76589
+ }
76285
76590
  setLoopStatus(runtime, "SENDING_API_REQUEST", {
76286
76591
  agent_id: agentId,
76287
76592
  conversation_id: conversationId
@@ -76292,6 +76597,7 @@ async function handleApprovalStop(params) {
76292
76597
  terminated: true,
76293
76598
  stream: null,
76294
76599
  currentInput: nextInput,
76600
+ dequeuedBatchId: continuationBatchId,
76295
76601
  pendingNormalizationInterruptedToolCallIds: [],
76296
76602
  turnToolContextId,
76297
76603
  lastExecutionResults,
@@ -76325,6 +76631,7 @@ async function handleApprovalStop(params) {
76325
76631
  terminated: false,
76326
76632
  stream: stream2,
76327
76633
  currentInput: nextInput,
76634
+ dequeuedBatchId: continuationBatchId,
76328
76635
  pendingNormalizationInterruptedToolCallIds: [],
76329
76636
  turnToolContextId: null,
76330
76637
  lastExecutionResults,
@@ -76342,6 +76649,7 @@ var init_turn_approval = __esm(async () => {
76342
76649
  init_approval(),
76343
76650
  init_interrupts(),
76344
76651
  init_protocol_outbound(),
76652
+ init_queue(),
76345
76653
  init_recovery(),
76346
76654
  init_send()
76347
76655
  ]);
@@ -76362,6 +76670,7 @@ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, conne
76362
76670
  let llmApiErrorRetries = 0;
76363
76671
  let emptyResponseRetries = 0;
76364
76672
  let lastApprovalContinuationAccepted = false;
76673
+ let activeDequeuedBatchId = dequeuedBatchId;
76365
76674
  let lastExecutionResults = null;
76366
76675
  let lastExecutingToolCallIds = [];
76367
76676
  let lastNeedsUserInputToolCallIds = [];
@@ -76723,7 +77032,7 @@ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, conne
76723
77032
  conversationId,
76724
77033
  turnWorkingDirectory,
76725
77034
  turnPermissionModeState,
76726
- dequeuedBatchId,
77035
+ dequeuedBatchId: activeDequeuedBatchId,
76727
77036
  runId,
76728
77037
  msgRunIds,
76729
77038
  currentInput,
@@ -76736,6 +77045,7 @@ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, conne
76736
77045
  }
76737
77046
  stream2 = approvalResult.stream;
76738
77047
  currentInput = approvalResult.currentInput;
77048
+ activeDequeuedBatchId = approvalResult.dequeuedBatchId;
76739
77049
  pendingNormalizationInterruptedToolCallIds = approvalResult.pendingNormalizationInterruptedToolCallIds;
76740
77050
  turnToolContextId = approvalResult.turnToolContextId;
76741
77051
  lastExecutionResults = approvalResult.lastExecutionResults;
@@ -76957,6 +77267,51 @@ async function handleApprovalResponseInput(listener, params, deps = {
76957
77267
  }
76958
77268
  return false;
76959
77269
  }
77270
+ async function handleChangeDeviceStateInput(listener, params, deps = {}) {
77271
+ const resolvedDeps = {
77272
+ getActiveRuntime,
77273
+ getOrCreateScopedRuntime,
77274
+ getPendingControlRequestCount,
77275
+ setLoopStatus,
77276
+ handleModeChange,
77277
+ handleCwdChange,
77278
+ emitDeviceStatusUpdate,
77279
+ scheduleQueuePump,
77280
+ ...deps
77281
+ };
77282
+ if (listener !== resolvedDeps.getActiveRuntime() || listener.intentionallyClosed) {
77283
+ return false;
77284
+ }
77285
+ const scope = {
77286
+ agent_id: params.command.payload.agent_id ?? params.command.runtime.agent_id ?? undefined,
77287
+ conversation_id: params.command.payload.conversation_id ?? params.command.runtime.conversation_id ?? undefined
77288
+ };
77289
+ const scopedRuntime = resolvedDeps.getOrCreateScopedRuntime(listener, scope.agent_id, scope.conversation_id);
77290
+ const shouldTrackCommand = !scopedRuntime.isProcessing && resolvedDeps.getPendingControlRequestCount(listener, scope) === 0;
77291
+ if (shouldTrackCommand) {
77292
+ resolvedDeps.setLoopStatus(scopedRuntime, "EXECUTING_COMMAND", scope);
77293
+ }
77294
+ try {
77295
+ if (params.command.payload.mode) {
77296
+ resolvedDeps.handleModeChange({ mode: params.command.payload.mode }, params.socket, listener, scope);
77297
+ }
77298
+ if (params.command.payload.cwd) {
77299
+ await resolvedDeps.handleCwdChange({
77300
+ agentId: scope.agent_id ?? null,
77301
+ conversationId: scope.conversation_id ?? null,
77302
+ cwd: params.command.payload.cwd
77303
+ }, params.socket, scopedRuntime);
77304
+ } else if (!params.command.payload.mode) {
77305
+ resolvedDeps.emitDeviceStatusUpdate(params.socket, listener, scope);
77306
+ }
77307
+ } finally {
77308
+ if (shouldTrackCommand) {
77309
+ resolvedDeps.setLoopStatus(scopedRuntime, "WAITING_ON_INPUT", scope);
77310
+ resolvedDeps.scheduleQueuePump(scopedRuntime, params.socket, params.opts, params.processQueuedTurn);
77311
+ }
77312
+ }
77313
+ return true;
77314
+ }
76960
77315
  async function handleCwdChange(msg, socket, runtime) {
76961
77316
  const conversationId = normalizeConversationId(msg.conversationId);
76962
77317
  const agentId = normalizeCwdAgentId(msg.agentId);
@@ -77241,36 +77596,15 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
77241
77596
  return;
77242
77597
  }
77243
77598
  if (parsed.type === "change_device_state") {
77244
- if (runtime !== getActiveRuntime() || runtime.intentionallyClosed) {
77245
- return;
77246
- }
77247
- const scope = {
77248
- agent_id: parsed.payload.agent_id ?? parsed.runtime.agent_id ?? undefined,
77249
- conversation_id: parsed.payload.conversation_id ?? parsed.runtime.conversation_id ?? undefined
77250
- };
77251
- const scopedRuntime = getOrCreateScopedRuntime(runtime, scope.agent_id, scope.conversation_id);
77252
- const shouldTrackCommand = !scopedRuntime.isProcessing && getPendingControlRequestCount(runtime, scope) === 0;
77253
- if (shouldTrackCommand) {
77254
- setLoopStatus(scopedRuntime, "EXECUTING_COMMAND", scope);
77255
- }
77256
- try {
77257
- if (parsed.payload.mode) {
77258
- handleModeChange({ mode: parsed.payload.mode }, socket, runtime, scope);
77259
- }
77260
- if (parsed.payload.cwd) {
77261
- await handleCwdChange({
77262
- agentId: scope.agent_id ?? null,
77263
- conversationId: scope.conversation_id ?? null,
77264
- cwd: parsed.payload.cwd
77265
- }, socket, scopedRuntime);
77266
- } else if (!parsed.payload.mode) {
77267
- emitDeviceStatusUpdate(socket, runtime, scope);
77268
- }
77269
- } finally {
77270
- if (shouldTrackCommand) {
77271
- setLoopStatus(scopedRuntime, "WAITING_ON_INPUT", scope);
77272
- }
77273
- }
77599
+ await handleChangeDeviceStateInput(runtime, {
77600
+ command: parsed,
77601
+ socket,
77602
+ opts: {
77603
+ onStatusChange: opts.onStatusChange,
77604
+ connectionId: opts.connectionId
77605
+ },
77606
+ processQueuedTurn
77607
+ });
77274
77608
  return;
77275
77609
  }
77276
77610
  if (parsed.type === "abort_message") {
@@ -77626,10 +77960,13 @@ var init_client4 = __esm(async () => {
77626
77960
  shouldAttemptPostStopApprovalRecovery,
77627
77961
  getApprovalContinuationRecoveryDisposition,
77628
77962
  markAwaitingAcceptedApprovalContinuationRunId,
77963
+ resolveStaleApprovals,
77629
77964
  normalizeMessageContentImages,
77630
77965
  normalizeInboundMessages,
77966
+ consumeQueuedTurn,
77631
77967
  handleIncomingMessage,
77632
77968
  handleApprovalResponseInput,
77969
+ handleChangeDeviceStateInput,
77633
77970
  scheduleQueuePump,
77634
77971
  recoverApprovalStateForSync,
77635
77972
  clearRecoveredApprovalStateForScope: (runtime, scope) => clearRecoveredApprovalStateForScope(asListenerRuntimeForTests(runtime), scope),
@@ -103339,7 +103676,9 @@ function ConversationSelector2({
103339
103676
  const result = await client.conversations.list({
103340
103677
  agent_id: agentId,
103341
103678
  limit: FETCH_PAGE_SIZE3,
103342
- ...afterCursor && { after: afterCursor }
103679
+ ...afterCursor && { after: afterCursor },
103680
+ order: "desc",
103681
+ order_by: "last_run_completion"
103343
103682
  });
103344
103683
  const enrichedConversations = await Promise.all(result.map(async (conv) => {
103345
103684
  try {
@@ -135075,6 +135414,9 @@ __export(exports_memoryFilesystem2, {
135075
135414
  ensureMemoryFilesystemDirs: () => ensureMemoryFilesystemDirs2,
135076
135415
  enableMemfsIfCloud: () => enableMemfsIfCloud2,
135077
135416
  applyMemfsFlags: () => applyMemfsFlags2,
135417
+ MEMORY_TREE_MAX_LINES: () => MEMORY_TREE_MAX_LINES2,
135418
+ MEMORY_TREE_MAX_CHILDREN_PER_DIR: () => MEMORY_TREE_MAX_CHILDREN_PER_DIR2,
135419
+ MEMORY_TREE_MAX_CHARS: () => MEMORY_TREE_MAX_CHARS2,
135078
135420
  MEMORY_SYSTEM_DIR: () => MEMORY_SYSTEM_DIR2,
135079
135421
  MEMORY_FS_ROOT: () => MEMORY_FS_ROOT2,
135080
135422
  MEMORY_FS_MEMORY_DIR: () => MEMORY_FS_MEMORY_DIR2,
@@ -135103,7 +135445,7 @@ function labelFromRelativePath2(relativePath) {
135103
135445
  const normalized = relativePath.replace(/\\/g, "/");
135104
135446
  return normalized.replace(/\.md$/, "");
135105
135447
  }
135106
- function renderMemoryFilesystemTree2(systemLabels, detachedLabels) {
135448
+ function renderMemoryFilesystemTree2(systemLabels, detachedLabels, options = {}) {
135107
135449
  const makeNode = () => ({ children: new Map, isFile: false });
135108
135450
  const root = makeNode();
135109
135451
  const insertPath = (base2, label) => {
@@ -135138,20 +135480,75 @@ function renderMemoryFilesystemTree2(systemLabels, detachedLabels) {
135138
135480
  return nameA.localeCompare(nameB);
135139
135481
  });
135140
135482
  };
135141
- const lines = ["/memory/"];
135483
+ const limits = getDirectoryLimits();
135484
+ const maxLines = Math.max(2, options.maxLines ?? limits.memfsTreeMaxLines);
135485
+ const maxChars = Math.max(128, options.maxChars ?? limits.memfsTreeMaxChars);
135486
+ const maxChildrenPerDir = Math.max(1, options.maxChildrenPerDir ?? limits.memfsTreeMaxChildrenPerDir);
135487
+ const rootLine = "/memory/";
135488
+ const lines = [rootLine];
135489
+ let totalChars = rootLine.length;
135490
+ const countTreeEntries = (node) => {
135491
+ let total = 0;
135492
+ for (const [, child] of node.children) {
135493
+ total += 1;
135494
+ if (child.children.size > 0) {
135495
+ total += countTreeEntries(child);
135496
+ }
135497
+ }
135498
+ return total;
135499
+ };
135500
+ const canAppendLine = (line) => {
135501
+ const nextLineCount = lines.length + 1;
135502
+ const nextCharCount = totalChars + 1 + line.length;
135503
+ return nextLineCount <= maxLines && nextCharCount <= maxChars;
135504
+ };
135142
135505
  const render2 = (node, prefix) => {
135143
135506
  const entries = sortedEntries(node);
135144
- entries.forEach(([name, child], index) => {
135145
- const isLast = index === entries.length - 1;
135507
+ const visibleEntries = entries.slice(0, maxChildrenPerDir);
135508
+ const omittedEntries = Math.max(0, entries.length - visibleEntries.length);
135509
+ const renderItems = visibleEntries.map(([name, child]) => ({
135510
+ kind: "entry",
135511
+ name,
135512
+ child
135513
+ }));
135514
+ if (omittedEntries > 0) {
135515
+ renderItems.push({ kind: "omitted", omittedCount: omittedEntries });
135516
+ }
135517
+ for (const [index, item] of renderItems.entries()) {
135518
+ const isLast = index === renderItems.length - 1;
135146
135519
  const branch = isLast ? "└──" : "├──";
135147
- lines.push(`${prefix}${branch} ${name}${child.isFile ? "" : "/"}`);
135148
- if (child.children.size > 0) {
135520
+ const line = item.kind === "entry" ? `${prefix}${branch} ${item.name}${item.child.isFile ? "" : "/"}` : `${prefix}${branch} … (${item.omittedCount.toLocaleString()} more entries)`;
135521
+ if (!canAppendLine(line)) {
135522
+ return false;
135523
+ }
135524
+ lines.push(line);
135525
+ totalChars += 1 + line.length;
135526
+ if (item.kind === "entry" && item.child.children.size > 0) {
135149
135527
  const nextPrefix = `${prefix}${isLast ? " " : "│ "}`;
135150
- render2(child, nextPrefix);
135528
+ if (!render2(item.child, nextPrefix)) {
135529
+ return false;
135530
+ }
135151
135531
  }
135152
- });
135532
+ }
135533
+ return true;
135153
135534
  };
135154
- render2(root, "");
135535
+ const totalEntries = countTreeEntries(root);
135536
+ const fullyRendered = render2(root, "");
135537
+ if (!fullyRendered) {
135538
+ while (lines.length > 1) {
135539
+ const shownEntries = Math.max(0, lines.length - 1);
135540
+ const omittedEntries = Math.max(1, totalEntries - shownEntries);
135541
+ const notice = `[Tree truncated: showing ${shownEntries.toLocaleString()} of ${totalEntries.toLocaleString()} entries. ${omittedEntries.toLocaleString()} omitted.]`;
135542
+ if (canAppendLine(notice)) {
135543
+ lines.push(notice);
135544
+ break;
135545
+ }
135546
+ const removed = lines.pop();
135547
+ if (removed) {
135548
+ totalChars -= 1 + removed.length;
135549
+ }
135550
+ }
135551
+ }
135155
135552
  return lines.join(`
135156
135553
  `);
135157
135554
  }
@@ -135233,8 +135630,13 @@ async function enableMemfsIfCloud2(agentId) {
135233
135630
  console.warn(`Warning: Could not enable memfs for new agent: ${error instanceof Error ? error.message : String(error)}`);
135234
135631
  }
135235
135632
  }
135236
- var MEMORY_FS_ROOT2 = ".letta", MEMORY_FS_AGENTS_DIR2 = "agents", MEMORY_FS_MEMORY_DIR2 = "memory", MEMORY_SYSTEM_DIR2 = "system";
135237
- var init_memoryFilesystem2 = () => {};
135633
+ var MEMORY_FS_ROOT2 = ".letta", MEMORY_FS_AGENTS_DIR2 = "agents", MEMORY_FS_MEMORY_DIR2 = "memory", MEMORY_SYSTEM_DIR2 = "system", MEMORY_TREE_MAX_LINES2, MEMORY_TREE_MAX_CHARS2, MEMORY_TREE_MAX_CHILDREN_PER_DIR2;
135634
+ var init_memoryFilesystem2 = __esm(() => {
135635
+ init_directoryLimits();
135636
+ MEMORY_TREE_MAX_LINES2 = DIRECTORY_LIMIT_DEFAULTS.memfsTreeMaxLines;
135637
+ MEMORY_TREE_MAX_CHARS2 = DIRECTORY_LIMIT_DEFAULTS.memfsTreeMaxChars;
135638
+ MEMORY_TREE_MAX_CHILDREN_PER_DIR2 = DIRECTORY_LIMIT_DEFAULTS.memfsTreeMaxChildrenPerDir;
135639
+ });
135238
135640
 
135239
135641
  // node_modules/@letta-ai/letta-client/core/error.mjs
135240
135642
  class LettaError extends Error {
@@ -136485,7 +136887,9 @@ function ConversationSelector({
136485
136887
  const result = await client.conversations.list({
136486
136888
  agent_id: agentId,
136487
136889
  limit: FETCH_PAGE_SIZE,
136488
- ...afterCursor && { after: afterCursor }
136890
+ ...afterCursor && { after: afterCursor },
136891
+ order: "desc",
136892
+ order_by: "last_run_completion"
136489
136893
  });
136490
136894
  const enrichedConversations = await Promise.all(result.map(async (conv) => {
136491
136895
  try {
@@ -141191,6 +141595,7 @@ var ANTHROPIC_DEFAULT_TOOLS2 = [
141191
141595
  "Glob",
141192
141596
  "Grep",
141193
141597
  "TaskStop",
141598
+ "memory",
141194
141599
  "Read",
141195
141600
  "Skill",
141196
141601
  "Task",
@@ -141201,6 +141606,7 @@ var OPENAI_PASCAL_TOOLS2 = [
141201
141606
  "AskUserQuestion",
141202
141607
  "EnterPlanMode",
141203
141608
  "ExitPlanMode",
141609
+ "memory",
141204
141610
  "Task",
141205
141611
  "TaskOutput",
141206
141612
  "TaskStop",
@@ -141214,6 +141620,7 @@ var GEMINI_PASCAL_TOOLS2 = [
141214
141620
  "AskUserQuestion",
141215
141621
  "EnterPlanMode",
141216
141622
  "ExitPlanMode",
141623
+ "memory",
141217
141624
  "Skill",
141218
141625
  "Task",
141219
141626
  "RunShellCommand",
@@ -142972,4 +143379,4 @@ Error during initialization: ${message}`);
142972
143379
  }
142973
143380
  main();
142974
143381
 
142975
- //# debugId=9B4D5740B8FA924E64756E2164756E21
143382
+ //# debugId=06EC6034A250F60F64756E2164756E21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letta-ai/letta-code",
3
- "version": "0.19.3",
3
+ "version": "0.19.5",
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": {
@@ -68,12 +68,12 @@
68
68
  "fix": "bunx --bun @biomejs/biome@2.2.5 check --write src",
69
69
  "typecheck": "tsc --noEmit",
70
70
  "check": "bun run scripts/check.js",
71
- "dev": "LETTA_DEBUG=1 bun --loader:.md=text --loader:.mdx=text --loader:.txt=text run src/index.ts",
71
+ "dev": "LETTA_DEBUG=${LETTA_DEBUG:-1} bun --loader:.md=text --loader:.mdx=text --loader:.txt=text run src/index.ts",
72
72
  "build": "node scripts/postinstall-patches.js && bun run build.js",
73
73
  "test:update-chain:manual": "bun run src/tests/update-chain-smoke.ts --mode manual",
74
74
  "test:update-chain:startup": "bun run src/tests/update-chain-smoke.ts --mode startup",
75
75
  "prepublishOnly": "bun run build",
76
- "postinstall": "node scripts/postinstall-patches.js || echo letta: vendor patches skipped"
76
+ "postinstall": "node scripts/postinstall-patches.js || echo letta: vendor patches skipped && node -e \"try{require('fs').chmodSync(require('path').join(require.resolve('node-pty/package.json'),'../prebuilds/darwin-arm64/spawn-helper'),0o755)}catch(e){}\" || true"
77
77
  },
78
78
  "lint-staged": {
79
79
  "*.{ts,tsx,js,jsx,json}": [