@letta-ai/letta-code 0.21.10 → 0.21.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/letta.js CHANGED
@@ -3269,7 +3269,7 @@ var package_default;
3269
3269
  var init_package = __esm(() => {
3270
3270
  package_default = {
3271
3271
  name: "@letta-ai/letta-code",
3272
- version: "0.21.10",
3272
+ version: "0.21.12",
3273
3273
  description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
3274
3274
  type: "module",
3275
3275
  bin: {
@@ -6841,7 +6841,7 @@ skills:
6841
6841
  model: auto
6842
6842
  memoryBlocks: none
6843
6843
  mode: stateless
6844
- permissionMode: bypassPermissions
6844
+ permissionMode: memory
6845
6845
  ---
6846
6846
 
6847
6847
  You are a history analysis subagent. You create a git worktree from the agent's memory repo, read conversation history from Claude Code or Codex, then **directly create and update memory files** in your worktree based on what you learn.
@@ -6921,12 +6921,18 @@ Keep specific correction counts ("corrected 10+ times"), specific file paths, an
6921
6921
  \`\`\`bash
6922
6922
  MEMORY_DIR=~/.letta/agents/$LETTA_PARENT_AGENT_ID/memory
6923
6923
  WORKTREE_DIR=~/.letta/agents/$LETTA_PARENT_AGENT_ID/memory-worktrees
6924
- BRANCH_NAME="migration-$(date +%s)"
6924
+ # Run \`date +%s\` first, then paste that exact output below.
6925
+ BRANCH_NAME="migration-<epoch-seconds>"
6925
6926
  mkdir -p "$WORKTREE_DIR"
6926
6927
  cd "$MEMORY_DIR"
6927
6928
  git worktree add "$WORKTREE_DIR/$BRANCH_NAME" -b "$BRANCH_NAME"
6928
6929
  \`\`\`
6929
6930
 
6931
+ Use epoch seconds from a prior \`date +%s\` command so branch names match the
6932
+ old behavior. Do not use shell command substitution like \`$(date +%s)\` in the
6933
+ branch assignment because memory-mode shell permissions deny command
6934
+ substitution.
6935
+
6930
6936
  If worktree creation fails (locked index), retry up to 3 times with backoff (sleep 2, 5, 10). Never delete \`.git/index.lock\` manually. All edits go in \`$WORKTREE_DIR/$BRANCH_NAME/\`.
6931
6937
 
6932
6938
  ### 2. Read existing memory
@@ -7043,7 +7049,7 @@ description: Fast initialization of agent memory — reads key project files and
7043
7049
  tools: Read, Write, Edit, Bash, Glob
7044
7050
  model: auto-fast
7045
7051
  memoryBlocks: none
7046
- permissionMode: bypassPermissions
7052
+ permissionMode: memory
7047
7053
  ---
7048
7054
 
7049
7055
  You are a fast memory initialization subagent. Your job is to quickly scan a project and create a **skeleton memory hierarchy** for the parent agent. This hierarchy starts minimal and gets fleshed out as the user keeps interacting with the agent.
@@ -7151,7 +7157,7 @@ description: Decompose and reorganize memory files into focused, single-purpose
7151
7157
  tools: Read, Edit, Write, Glob, Grep, Bash, TaskOutput
7152
7158
  model: auto
7153
7159
  memoryBlocks: none
7154
- permissionMode: bypassPermissions
7160
+ permissionMode: memory
7155
7161
  ---
7156
7162
 
7157
7163
  You are a memory defragmentation subagent. You work directly on the git-backed memory filesystem to decompose and reorganize memory files.
@@ -7226,12 +7232,18 @@ making changes.
7226
7232
  **Create worktree:**
7227
7233
 
7228
7234
  \`\`\`bash
7229
- BRANCH="defrag-$(date +%s)"
7235
+ # Run \`date +%s\` first, then paste that exact output below.
7236
+ BRANCH="defrag-<epoch-seconds>"
7230
7237
  mkdir -p "$WORKTREE_DIR"
7231
7238
  cd "$MEMORY_DIR"
7232
7239
  git worktree add "$WORKTREE_DIR/$BRANCH" -b "$BRANCH"
7233
7240
  \`\`\`
7234
7241
 
7242
+ Use epoch seconds from a prior \`date +%s\` command so branch names match the
7243
+ old behavior. Do not use shell command substitution like \`$(date +%s)\` in the
7244
+ branch assignment because memory-mode shell permissions deny command
7245
+ substitution.
7246
+
7235
7247
  All subsequent file operations target the worktree:
7236
7248
  \`$WORKTREE_DIR/$BRANCH/system/\` (not the main memory dir).
7237
7249
 
@@ -7506,7 +7518,7 @@ tools: Read, Edit, Write, Glob, Grep, Bash, TaskOutput
7506
7518
  model: auto
7507
7519
  memoryBlocks: none
7508
7520
  mode: stateless
7509
- permissionMode: bypassPermissions
7521
+ permissionMode: memory
7510
7522
  ---
7511
7523
 
7512
7524
  You are a reflection subagent — a background agent that asynchronously processes conversations after they occur, similar to a "sleep-time" memory consolidation process.
@@ -52238,9 +52250,138 @@ var init_hooks = __esm(async () => {
52238
52250
  init_types();
52239
52251
  });
52240
52252
 
52241
- // src/permissions/readOnlyShell.ts
52253
+ // src/permissions/memoryScope.ts
52242
52254
  import { homedir as homedir11 } from "node:os";
52243
- import { resolve as resolve4 } from "node:path";
52255
+ import { basename, dirname as dirname4, isAbsolute as isAbsolute2, resolve as resolve4 } from "node:path";
52256
+ function normalizeScopedPath(path4) {
52257
+ const resolvedPath = resolve4(expandHomePath(path4));
52258
+ const normalized = resolvedPath.replace(/\\/g, "/");
52259
+ return normalized.replace(/\/+$/, "") || "/";
52260
+ }
52261
+ function expandHomePath(path4) {
52262
+ const value = path4.trim();
52263
+ const homeDir = homedir11();
52264
+ if (value.startsWith("~/")) {
52265
+ return resolve4(homeDir, value.slice(2));
52266
+ }
52267
+ if (value.startsWith("$HOME/")) {
52268
+ return resolve4(homeDir, value.slice(6));
52269
+ }
52270
+ if (value.startsWith('"$HOME/')) {
52271
+ return resolve4(homeDir, value.slice(7).replace(/"$/, ""));
52272
+ }
52273
+ return value;
52274
+ }
52275
+ function resolveScopedTargetPath(targetPath, workingDirectory) {
52276
+ const trimmedPath = targetPath.trim();
52277
+ if (!trimmedPath)
52278
+ return null;
52279
+ if (trimmedPath.startsWith("~/") || trimmedPath.startsWith("$HOME/")) {
52280
+ return normalizeScopedPath(trimmedPath);
52281
+ }
52282
+ if (isAbsolute2(trimmedPath) || /^[a-zA-Z]:[\\/]/.test(trimmedPath)) {
52283
+ return normalizeScopedPath(trimmedPath);
52284
+ }
52285
+ return normalizeScopedPath(resolve4(workingDirectory, trimmedPath));
52286
+ }
52287
+ function isPathWithinRoots(path4, roots) {
52288
+ const normalizedPath = normalizeScopedPath(path4);
52289
+ return roots.some((root) => {
52290
+ const normalizedRoot = normalizeScopedPath(root);
52291
+ return normalizedPath === normalizedRoot || normalizedPath.startsWith(`${normalizedRoot}/`);
52292
+ });
52293
+ }
52294
+ function addRootAndSiblingWorktree(root, acc) {
52295
+ const normalizedRoot = normalizeScopedPath(root);
52296
+ if (!normalizedRoot) {
52297
+ return;
52298
+ }
52299
+ acc.add(normalizedRoot);
52300
+ const leaf = basename(normalizedRoot);
52301
+ if (leaf === "memory") {
52302
+ acc.add(normalizeScopedPath(resolve4(dirname4(normalizedRoot), "memory-worktrees")));
52303
+ }
52304
+ }
52305
+ function getExplicitEnvRoots(env3) {
52306
+ const orderedRoots = [
52307
+ env3.MEMORY_DIR,
52308
+ env3.LETTA_MEMORY_DIR,
52309
+ env3.PARENT_MEMORY_DIR
52310
+ ].map((value) => value?.trim() ?? "").filter((value) => value.length > 0);
52311
+ const explicitRootSet = new Set;
52312
+ for (const root of orderedRoots) {
52313
+ addRootAndSiblingWorktree(root, explicitRootSet);
52314
+ }
52315
+ return {
52316
+ explicitRoots: [...explicitRootSet],
52317
+ primaryRoot: orderedRoots.length > 0 ? normalizeScopedPath(orderedRoots[0]) : null
52318
+ };
52319
+ }
52320
+ function deriveAgentId(env3, explicitAgentId) {
52321
+ const explicit = explicitAgentId?.trim();
52322
+ if (explicit) {
52323
+ return explicit;
52324
+ }
52325
+ const envAgentId = (env3.AGENT_ID || env3.LETTA_AGENT_ID || "").trim();
52326
+ if (envAgentId) {
52327
+ return envAgentId;
52328
+ }
52329
+ try {
52330
+ return getCurrentAgentId().trim();
52331
+ } catch {
52332
+ return null;
52333
+ }
52334
+ }
52335
+ function getFallbackRoots(env3, homeDir, currentAgentId, parentAgentId) {
52336
+ const fallbackRoots = new Set;
52337
+ const resolvedCurrentAgentId = deriveAgentId(env3, currentAgentId);
52338
+ const resolvedParentAgentId = (parentAgentId || env3.LETTA_PARENT_AGENT_ID || "").trim() || null;
52339
+ let primaryRoot = null;
52340
+ if (resolvedCurrentAgentId) {
52341
+ const currentRoot = getMemoryFilesystemRoot(resolvedCurrentAgentId, homeDir);
52342
+ addRootAndSiblingWorktree(currentRoot, fallbackRoots);
52343
+ primaryRoot = normalizeScopedPath(currentRoot);
52344
+ }
52345
+ if (resolvedParentAgentId && resolvedParentAgentId !== resolvedCurrentAgentId) {
52346
+ const parentRoot = getMemoryFilesystemRoot(resolvedParentAgentId, homeDir);
52347
+ addRootAndSiblingWorktree(parentRoot, fallbackRoots);
52348
+ if (!primaryRoot) {
52349
+ primaryRoot = normalizeScopedPath(parentRoot);
52350
+ }
52351
+ }
52352
+ return {
52353
+ roots: [...fallbackRoots],
52354
+ primaryRoot
52355
+ };
52356
+ }
52357
+ function resolveAllowedMemoryRoots(options = {}) {
52358
+ const env3 = options.env ?? process.env;
52359
+ const homeDir = options.homeDir ?? homedir11();
52360
+ const explicit = getExplicitEnvRoots(env3);
52361
+ if (explicit.explicitRoots.length > 0) {
52362
+ return {
52363
+ roots: explicit.explicitRoots,
52364
+ explicitRoots: explicit.explicitRoots,
52365
+ primaryRoot: explicit.primaryRoot,
52366
+ usedFallback: false
52367
+ };
52368
+ }
52369
+ const fallback = getFallbackRoots(env3, homeDir, options.currentAgentId, options.parentAgentId);
52370
+ return {
52371
+ roots: fallback.roots,
52372
+ explicitRoots: [],
52373
+ primaryRoot: fallback.primaryRoot,
52374
+ usedFallback: fallback.roots.length > 0
52375
+ };
52376
+ }
52377
+ var init_memoryScope = __esm(() => {
52378
+ init_context();
52379
+ init_memoryFilesystem();
52380
+ });
52381
+
52382
+ // src/permissions/readOnlyShell.ts
52383
+ import { homedir as homedir12 } from "node:os";
52384
+ import { resolve as resolve5 } from "node:path";
52244
52385
  function splitShellSegments(input) {
52245
52386
  const segments = [];
52246
52387
  let current = "";
@@ -52312,6 +52453,13 @@ function splitShellSegments(input) {
52312
52453
  i += 1;
52313
52454
  continue;
52314
52455
  }
52456
+ if (ch === `
52457
+ ` || ch === "\r") {
52458
+ segments.push(current);
52459
+ current = "";
52460
+ i += 1;
52461
+ continue;
52462
+ }
52315
52463
  if (ch === "|") {
52316
52464
  segments.push(current);
52317
52465
  current = "";
@@ -52503,7 +52651,7 @@ function isUnderAllowedPathRoot(value, allowedPathRoots) {
52503
52651
  }
52504
52652
  const resolvedValue = expandPath(value);
52505
52653
  return allowedPathRoots.some((root) => {
52506
- const normalizedRoot = normalizeSeparators(resolve4(root));
52654
+ const normalizedRoot = normalizeSeparators(resolve5(root));
52507
52655
  return resolvedValue === normalizedRoot || resolvedValue.startsWith(`${normalizedRoot}/`);
52508
52656
  });
52509
52657
  }
@@ -52546,14 +52694,14 @@ function parseGitInvocation(tokens, options) {
52546
52694
  return { subcommand: null, isSafePath: true };
52547
52695
  }
52548
52696
  function getAllowedMemoryPrefixes(agentId) {
52549
- const home = homedir11();
52697
+ const home = homedir12();
52550
52698
  const prefixes = [
52551
- normalizeSeparators(resolve4(home, ".letta", "agents", agentId, "memory")),
52552
- normalizeSeparators(resolve4(home, ".letta", "agents", agentId, "memory-worktrees"))
52699
+ normalizeSeparators(resolve5(home, ".letta", "agents", agentId, "memory")),
52700
+ normalizeSeparators(resolve5(home, ".letta", "agents", agentId, "memory-worktrees"))
52553
52701
  ];
52554
52702
  const parentId = process.env.LETTA_PARENT_AGENT_ID;
52555
52703
  if (parentId && parentId !== agentId) {
52556
- prefixes.push(normalizeSeparators(resolve4(home, ".letta", "agents", parentId, "memory")), normalizeSeparators(resolve4(home, ".letta", "agents", parentId, "memory-worktrees")));
52704
+ prefixes.push(normalizeSeparators(resolve5(home, ".letta", "agents", parentId, "memory")), normalizeSeparators(resolve5(home, ".letta", "agents", parentId, "memory-worktrees")));
52557
52705
  }
52558
52706
  return prefixes;
52559
52707
  }
@@ -52561,122 +52709,315 @@ function normalizeSeparators(p) {
52561
52709
  return p.replace(/\\/g, "/");
52562
52710
  }
52563
52711
  function expandPath(p) {
52564
- const home = homedir11();
52565
- if (p.startsWith("~/")) {
52566
- return normalizeSeparators(resolve4(home, p.slice(2)));
52712
+ return normalizeScopedPath(p);
52713
+ }
52714
+ function expandScopedVariables(value, env3, shellVars) {
52715
+ let unresolved = false;
52716
+ const expanded = value.replace(/\$(?:{([A-Za-z_][A-Za-z0-9_]*)}|([A-Za-z_][A-Za-z0-9_]*))/g, (_match, bracedName, bareName) => {
52717
+ const name = bracedName || bareName;
52718
+ if (!name) {
52719
+ unresolved = true;
52720
+ return "";
52721
+ }
52722
+ if (name === "HOME") {
52723
+ return homedir12();
52724
+ }
52725
+ const scopedValue = shellVars[name];
52726
+ if (typeof scopedValue === "string") {
52727
+ return scopedValue;
52728
+ }
52729
+ const envValue = env3[name];
52730
+ if (typeof envValue === "string") {
52731
+ return envValue;
52732
+ }
52733
+ unresolved = true;
52734
+ return "";
52735
+ });
52736
+ return unresolved ? null : expanded;
52737
+ }
52738
+ function normalizeScopePath(path4, cwd2, env3, shellVars) {
52739
+ const expandedPath = expandScopedVariables(path4, env3, shellVars);
52740
+ if (!expandedPath) {
52741
+ return null;
52567
52742
  }
52568
- if (p.startsWith("$HOME/")) {
52569
- return normalizeSeparators(resolve4(home, p.slice(6)));
52743
+ if (expandedPath.startsWith("~/") || expandedPath.startsWith("$HOME/") || expandedPath.startsWith('"$HOME/') || expandedPath.startsWith("/") || /^[a-zA-Z]:[\\/]/.test(expandedPath)) {
52744
+ return normalizeScopedPath(expandedPath);
52570
52745
  }
52571
- if (p.startsWith('"$HOME/')) {
52572
- return normalizeSeparators(resolve4(home, p.slice(7).replace(/"$/, "")));
52746
+ if (cwd2) {
52747
+ return normalizeScopedPath(resolve5(cwd2, expandedPath));
52573
52748
  }
52574
- return normalizeSeparators(resolve4(p));
52749
+ return null;
52750
+ }
52751
+ function parseScopedAssignmentToken(token) {
52752
+ const match = token.match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
52753
+ if (!match) {
52754
+ return null;
52755
+ }
52756
+ return {
52757
+ name: match[1] ?? "",
52758
+ value: stripQuotes(match[2] ?? "")
52759
+ };
52760
+ }
52761
+ function applyScopedAssignments(tokens, env3, shellVars) {
52762
+ if (tokens.length === 0) {
52763
+ return false;
52764
+ }
52765
+ for (const token of tokens) {
52766
+ const assignment = parseScopedAssignmentToken(token);
52767
+ if (!assignment) {
52768
+ return false;
52769
+ }
52770
+ const expandedValue = expandScopedVariables(assignment.value, env3, shellVars);
52771
+ if (expandedValue === null) {
52772
+ return false;
52773
+ }
52774
+ if (expandedValue.startsWith("~/") || expandedValue.startsWith("$HOME/") || expandedValue.startsWith('"$HOME/') || expandedValue.startsWith("/") || /^[a-zA-Z]:[\\/]/.test(expandedValue)) {
52775
+ shellVars[assignment.name] = normalizeScopedPath(expandedValue);
52776
+ } else {
52777
+ shellVars[assignment.name] = expandedValue;
52778
+ }
52779
+ }
52780
+ return true;
52575
52781
  }
52576
- function isUnderMemoryDir(path4, prefixes) {
52577
- const resolved = expandPath(path4);
52578
- return prefixes.some((prefix) => resolved === prefix || resolved.startsWith(`${prefix}/`));
52782
+ function hasUnsafeRebaseOption(tokens, startIndex) {
52783
+ for (let i = startIndex;i < tokens.length; i += 1) {
52784
+ const token = tokens[i];
52785
+ if (!token) {
52786
+ continue;
52787
+ }
52788
+ const lower = token.toLowerCase();
52789
+ if (lower === "--exec" || lower.startsWith("--exec=") || lower === "-x" || lower.startsWith("-x") && lower.length > 2 || lower === "--interactive" || lower === "-i" || lower === "--edit-todo") {
52790
+ return true;
52791
+ }
52792
+ }
52793
+ return false;
52579
52794
  }
52580
- function extractCdTarget(segment) {
52795
+ function parseScopedGitInvocation(tokens, cwd2, allowedRoots, env3, shellVars) {
52796
+ let index = 1;
52797
+ let resolvedCwd = cwd2;
52798
+ while (index < tokens.length) {
52799
+ const token = tokens[index];
52800
+ if (!token) {
52801
+ index += 1;
52802
+ continue;
52803
+ }
52804
+ if (token === "-C") {
52805
+ const pathToken = tokens[index + 1];
52806
+ if (!pathToken) {
52807
+ return {
52808
+ subcommand: null,
52809
+ worktreeSubcommand: null,
52810
+ resolvedCwd,
52811
+ isSafe: false
52812
+ };
52813
+ }
52814
+ const nextCwd = normalizeScopePath(pathToken, resolvedCwd, env3, shellVars);
52815
+ if (!nextCwd || !isPathWithinRoots(nextCwd, allowedRoots)) {
52816
+ return {
52817
+ subcommand: null,
52818
+ worktreeSubcommand: null,
52819
+ resolvedCwd,
52820
+ isSafe: false
52821
+ };
52822
+ }
52823
+ resolvedCwd = nextCwd;
52824
+ index += 2;
52825
+ continue;
52826
+ }
52827
+ if (token === "-c") {
52828
+ const configToken = tokens[index + 1];
52829
+ if (!configToken) {
52830
+ return {
52831
+ subcommand: null,
52832
+ worktreeSubcommand: null,
52833
+ resolvedCwd,
52834
+ isSafe: false
52835
+ };
52836
+ }
52837
+ if (!/^http\.extraHeader=/.test(configToken)) {
52838
+ return {
52839
+ subcommand: null,
52840
+ worktreeSubcommand: null,
52841
+ resolvedCwd,
52842
+ isSafe: false
52843
+ };
52844
+ }
52845
+ index += 2;
52846
+ continue;
52847
+ }
52848
+ const subcommand = token;
52849
+ if (!SAFE_MEMORY_GIT_SUBCOMMANDS.has(subcommand)) {
52850
+ if (resolvedCwd && SAFE_MEMORY_COMMANDS.has("git")) {
52851
+ const rawSegment = tokens.join(" ");
52852
+ if (subcommand === "ls-tree" && !/\s-o\b/.test(rawSegment)) {
52853
+ return {
52854
+ subcommand,
52855
+ worktreeSubcommand: null,
52856
+ resolvedCwd,
52857
+ isSafe: true
52858
+ };
52859
+ }
52860
+ }
52861
+ return {
52862
+ subcommand,
52863
+ worktreeSubcommand: null,
52864
+ resolvedCwd,
52865
+ isSafe: false
52866
+ };
52867
+ }
52868
+ const worktreeSubcommand = subcommand === "worktree" ? tokens[index + 1] ?? null : null;
52869
+ if (subcommand === "worktree" && worktreeSubcommand && !new Set(["add", "remove", "list"]).has(worktreeSubcommand)) {
52870
+ return {
52871
+ subcommand,
52872
+ worktreeSubcommand,
52873
+ resolvedCwd,
52874
+ isSafe: false
52875
+ };
52876
+ }
52877
+ if (subcommand === "rebase" && hasUnsafeRebaseOption(tokens, index + 1)) {
52878
+ return {
52879
+ subcommand,
52880
+ worktreeSubcommand,
52881
+ resolvedCwd,
52882
+ isSafe: false
52883
+ };
52884
+ }
52885
+ return { subcommand, worktreeSubcommand, resolvedCwd, isSafe: true };
52886
+ }
52887
+ return {
52888
+ subcommand: null,
52889
+ worktreeSubcommand: null,
52890
+ resolvedCwd,
52891
+ isSafe: false
52892
+ };
52893
+ }
52894
+ function tokenLooksLikePath(token) {
52895
+ return token.includes("/") || token.includes("\\") || token === "." || token === ".." || token.startsWith("$") || token.startsWith("~") || token.startsWith("$HOME");
52896
+ }
52897
+ function validateScopedTokens(tokens, cwd2, allowedRoots, env3, shellVars) {
52898
+ return tokens.every((token, index) => {
52899
+ if (!tokenLooksLikePath(token)) {
52900
+ return true;
52901
+ }
52902
+ const previous = index > 0 ? tokens[index - 1] : null;
52903
+ if (previous && ["-m", "--message", "--author", "--format"].includes(previous)) {
52904
+ return true;
52905
+ }
52906
+ const resolved = normalizeScopePath(token, cwd2, env3, shellVars);
52907
+ return resolved ? isPathWithinRoots(resolved, allowedRoots) : false;
52908
+ });
52909
+ }
52910
+ function isAllowedMemorySegment(segment, cwd2, allowedRoots, env3, shellVars) {
52581
52911
  const tokens = tokenize4(segment);
52582
- if (tokens[0] === "cd" && tokens[1]) {
52583
- return tokens[1];
52912
+ if (tokens.length === 0) {
52913
+ return { nextCwd: cwd2, safe: false };
52584
52914
  }
52585
- return null;
52915
+ if (applyScopedAssignments(tokens, env3, shellVars)) {
52916
+ return { nextCwd: cwd2, safe: true };
52917
+ }
52918
+ const command = tokens[0];
52919
+ if (!command) {
52920
+ return { nextCwd: cwd2, safe: false };
52921
+ }
52922
+ if (command === "cd") {
52923
+ const target = tokens[1];
52924
+ if (!target) {
52925
+ return { nextCwd: cwd2, safe: false };
52926
+ }
52927
+ const resolved = normalizeScopePath(target, cwd2, env3, shellVars);
52928
+ return {
52929
+ nextCwd: resolved,
52930
+ safe: resolved ? isPathWithinRoots(resolved, allowedRoots) : false
52931
+ };
52932
+ }
52933
+ if (!SAFE_MEMORY_COMMANDS.has(command)) {
52934
+ return { nextCwd: cwd2, safe: false };
52935
+ }
52936
+ if (command === "git") {
52937
+ const parsed = parseScopedGitInvocation(tokens, cwd2, allowedRoots, env3, shellVars);
52938
+ if (!parsed.isSafe) {
52939
+ return { nextCwd: parsed.resolvedCwd, safe: false };
52940
+ }
52941
+ const effectiveCwd = parsed.resolvedCwd;
52942
+ if (!effectiveCwd || !isPathWithinRoots(effectiveCwd, allowedRoots)) {
52943
+ return { nextCwd: effectiveCwd, safe: false };
52944
+ }
52945
+ if (!validateScopedTokens(tokens, effectiveCwd, allowedRoots, env3, shellVars)) {
52946
+ return { nextCwd: effectiveCwd, safe: false };
52947
+ }
52948
+ return { nextCwd: effectiveCwd, safe: true };
52949
+ }
52950
+ if (tokens.some((token) => tokenLooksLikePath(token))) {
52951
+ if (!validateScopedTokens(tokens.slice(1), cwd2, allowedRoots, env3, shellVars)) {
52952
+ return { nextCwd: cwd2, safe: false };
52953
+ }
52954
+ return { nextCwd: cwd2, safe: true };
52955
+ }
52956
+ if (!cwd2 || !isPathWithinRoots(cwd2, allowedRoots)) {
52957
+ return { nextCwd: cwd2, safe: false };
52958
+ }
52959
+ if (command === "find" && /-delete|\s-exec\b/.test(segment)) {
52960
+ return { nextCwd: cwd2, safe: false };
52961
+ }
52962
+ if (command === "sort" && /\s-o\b/.test(segment)) {
52963
+ return { nextCwd: cwd2, safe: false };
52964
+ }
52965
+ if (!validateScopedTokens(tokens.slice(1), cwd2, allowedRoots, env3, shellVars)) {
52966
+ return { nextCwd: cwd2, safe: false };
52967
+ }
52968
+ return { nextCwd: cwd2, safe: true };
52586
52969
  }
52587
- function isMemoryDirCommand(command, agentId) {
52588
- if (!command || !agentId) {
52970
+ function isScopedMemoryShellCommand(command, allowedRoots, options = {}) {
52971
+ if (!command || allowedRoots.length === 0) {
52589
52972
  return false;
52590
52973
  }
52974
+ if (Array.isArray(command)) {
52975
+ if (command.length === 0) {
52976
+ return false;
52977
+ }
52978
+ const [executable, ...rest] = command;
52979
+ if (executable && isShellExecutor(executable)) {
52980
+ const nested = extractDashCArgument(rest);
52981
+ if (!nested) {
52982
+ return false;
52983
+ }
52984
+ return isScopedMemoryShellCommand(stripQuotes(nested), allowedRoots, options);
52985
+ }
52986
+ }
52591
52987
  const commandStr = typeof command === "string" ? command : command.join(" ");
52592
52988
  const trimmed = commandStr.trim();
52593
52989
  if (!trimmed) {
52594
52990
  return false;
52595
52991
  }
52596
- const prefixes = getAllowedMemoryPrefixes(agentId);
52597
- const segments = trimmed.split(/&&|\|\||;/).map((s) => s.trim()).filter(Boolean);
52992
+ const segments = splitShellSegments(trimmed);
52993
+ if (!segments) {
52994
+ return false;
52995
+ }
52598
52996
  if (segments.length === 0) {
52599
52997
  return false;
52600
52998
  }
52601
- let cwd2 = null;
52999
+ const env3 = options.env ?? process.env;
53000
+ const shellVars = {};
53001
+ const initialCwd = options.workingDirectory ? normalizeScopePath(options.workingDirectory, null, env3, shellVars) : null;
53002
+ let cwd2 = initialCwd;
52602
53003
  for (const segment of segments) {
52603
- const pipeParts = segment.split(/\|/).map((s) => s.trim()).filter(Boolean);
52604
- for (const part of pipeParts) {
52605
- const cdTarget = extractCdTarget(part);
52606
- if (cdTarget) {
52607
- const resolved = cwd2 ? expandPath(resolve4(expandPath(cwd2), cdTarget)) : expandPath(cdTarget);
52608
- if (!isUnderMemoryDir(resolved, prefixes)) {
52609
- return false;
52610
- }
52611
- cwd2 = resolved;
52612
- continue;
52613
- }
52614
- if (cwd2 && isUnderMemoryDir(cwd2, prefixes)) {
52615
- const tokens2 = tokenize4(part);
52616
- const currentCwd = cwd2;
52617
- if (!currentCwd) {
52618
- return false;
52619
- }
52620
- const hasExternalPath = tokens2.some((t) => {
52621
- if (isAbsolutePathArg(t) || isHomeAnchoredPathArg(t)) {
52622
- return !isUnderMemoryDir(t, prefixes);
52623
- }
52624
- if (hasAbsoluteOrTraversalPathArg(t)) {
52625
- const resolved = expandPath(resolve4(expandPath(currentCwd), t));
52626
- return !isUnderMemoryDir(resolved, prefixes);
52627
- }
52628
- return false;
52629
- });
52630
- if (hasExternalPath) {
52631
- return false;
52632
- }
52633
- if (!MEMORY_DIR_APPROVE_ALL) {
52634
- const cmd = tokens2[0];
52635
- if (!cmd || !SAFE_MEMORY_DIR_COMMANDS.has(cmd)) {
52636
- return false;
52637
- }
52638
- }
52639
- continue;
52640
- }
52641
- const tokens = tokenize4(part);
52642
- const hasMemoryPath = tokens.some((t) => t.includes(".letta/agents/") && t.includes("/memory") || t.includes(".letta/agents/") && t.includes("/memory-worktrees"));
52643
- if (hasMemoryPath) {
52644
- const agentPaths = tokens.filter((t) => t.includes(".letta/agents/"));
52645
- if (agentPaths.every((p) => isUnderMemoryDir(p, prefixes))) {
52646
- if (!MEMORY_DIR_APPROVE_ALL) {
52647
- const cmd = tokens[0];
52648
- if (!cmd || !SAFE_MEMORY_DIR_COMMANDS.has(cmd)) {
52649
- return false;
52650
- }
52651
- }
52652
- continue;
52653
- }
52654
- }
53004
+ const result = isAllowedMemorySegment(segment, cwd2, allowedRoots, env3, shellVars);
53005
+ if (!result.safe) {
52655
53006
  return false;
52656
53007
  }
53008
+ cwd2 = result.nextCwd;
52657
53009
  }
52658
53010
  return true;
52659
53011
  }
52660
- var MEMORY_DIR_APPROVE_ALL = true, SAFE_MEMORY_DIR_COMMANDS, ALWAYS_SAFE_COMMANDS, EXTERNAL_PATH_METADATA_COMMANDS, SAFE_GIT_SUBCOMMANDS, SAFE_LETTA_COMMANDS, SAFE_GH_COMMANDS;
53012
+ function isMemoryDirCommand(command, agentId) {
53013
+ if (!command || !agentId) {
53014
+ return false;
53015
+ }
53016
+ return isScopedMemoryShellCommand(command, getAllowedMemoryPrefixes(agentId));
53017
+ }
53018
+ var ALWAYS_SAFE_COMMANDS, EXTERNAL_PATH_METADATA_COMMANDS, SAFE_GIT_SUBCOMMANDS, SAFE_MEMORY_GIT_SUBCOMMANDS, SAFE_MEMORY_COMMANDS, SAFE_LETTA_COMMANDS, SAFE_GH_COMMANDS;
52661
53019
  var init_readOnlyShell = __esm(() => {
52662
- SAFE_MEMORY_DIR_COMMANDS = new Set([
52663
- "git",
52664
- "rm",
52665
- "mv",
52666
- "mkdir",
52667
- "cp",
52668
- "ls",
52669
- "cat",
52670
- "head",
52671
- "tail",
52672
- "tree",
52673
- "find",
52674
- "wc",
52675
- "split",
52676
- "echo",
52677
- "sort",
52678
- "cd"
52679
- ]);
53020
+ init_memoryScope();
52680
53021
  ALWAYS_SAFE_COMMANDS = new Set([
52681
53022
  "cat",
52682
53023
  "head",
@@ -52749,6 +53090,42 @@ var init_readOnlyShell = __esm(() => {
52749
53090
  "tag",
52750
53091
  "remote"
52751
53092
  ]);
53093
+ SAFE_MEMORY_GIT_SUBCOMMANDS = new Set([
53094
+ "add",
53095
+ "commit",
53096
+ "push",
53097
+ "pull",
53098
+ "rebase",
53099
+ "status",
53100
+ "diff",
53101
+ "log",
53102
+ "show",
53103
+ "branch",
53104
+ "tag",
53105
+ "remote",
53106
+ "rm",
53107
+ "mv",
53108
+ "merge",
53109
+ "worktree"
53110
+ ]);
53111
+ SAFE_MEMORY_COMMANDS = new Set([
53112
+ "git",
53113
+ "rm",
53114
+ "mv",
53115
+ "mkdir",
53116
+ "cp",
53117
+ "ls",
53118
+ "cat",
53119
+ "head",
53120
+ "tail",
53121
+ "find",
53122
+ "sort",
53123
+ "echo",
53124
+ "wc",
53125
+ "split",
53126
+ "cd",
53127
+ "sleep"
53128
+ ]);
52752
53129
  SAFE_LETTA_COMMANDS = {
52753
53130
  memfs: new Set(["status", "help", "backups", "export"]),
52754
53131
  agents: new Set(["list", "help"]),
@@ -52962,8 +53339,14 @@ var exports_mode = {};
52962
53339
  __export(exports_mode, {
52963
53340
  permissionMode: () => permissionMode
52964
53341
  });
52965
- import { homedir as homedir12 } from "node:os";
52966
- import { isAbsolute as isAbsolute2, join as join14, relative as relative3, resolve as resolve5 } from "node:path";
53342
+ import { homedir as homedir13 } from "node:os";
53343
+ import { isAbsolute as isAbsolute3, join as join14, relative as relative3 } from "node:path";
53344
+ function everyResolvedTargetIsWithinRoots(candidatePaths, roots, workingDirectory) {
53345
+ return candidatePaths.length > 0 && candidatePaths.every((path4) => {
53346
+ const resolvedPath = resolveScopedTargetPath(path4, workingDirectory);
53347
+ return resolvedPath ? isPathWithinRoots(resolvedPath, roots) : false;
53348
+ });
53349
+ }
52967
53350
  function getGlobalMode() {
52968
53351
  const global2 = globalThis;
52969
53352
  if (!global2[MODE_KEY]) {
@@ -52992,22 +53375,13 @@ function setGlobalModeBeforePlan(value) {
52992
53375
  global2[MODE_BEFORE_PLAN_KEY] = value;
52993
53376
  }
52994
53377
  function resolvePlanTargetPath(targetPath, workingDirectory) {
52995
- const trimmedPath = targetPath.trim();
52996
- if (!trimmedPath)
52997
- return null;
52998
- if (trimmedPath.startsWith("~/")) {
52999
- return resolve5(homedir12(), trimmedPath.slice(2));
53000
- }
53001
- if (isAbsolute2(trimmedPath)) {
53002
- return resolve5(trimmedPath);
53003
- }
53004
- return resolve5(workingDirectory, trimmedPath);
53378
+ return resolveScopedTargetPath(targetPath, workingDirectory);
53005
53379
  }
53006
53380
  function isPathInPlansDir(path4, plansDir) {
53007
53381
  if (!path4.endsWith(".md"))
53008
53382
  return false;
53009
53383
  const rel = relative3(plansDir, path4);
53010
- return rel !== "" && !rel.startsWith("..") && !isAbsolute2(rel);
53384
+ return rel !== "" && !rel.startsWith("..") && !isAbsolute3(rel);
53011
53385
  }
53012
53386
  function extractApplyPatchPaths(input) {
53013
53387
  const paths = [];
@@ -53194,7 +53568,7 @@ class PermissionModeManager {
53194
53568
  return "allow";
53195
53569
  }
53196
53570
  if (writeTools.includes(toolName)) {
53197
- const plansDir = join14(homedir12(), ".letta", "plans");
53571
+ const plansDir = join14(homedir13(), ".letta", "plans");
53198
53572
  const targetPath = toolArgs?.file_path || toolArgs?.path;
53199
53573
  let candidatePaths = [];
53200
53574
  if ((toolName === "ApplyPatch" || toolName === "apply_patch" || toolName === "memory_apply_patch") && toolArgs?.input) {
@@ -53245,7 +53619,7 @@ class PermissionModeManager {
53245
53619
  }
53246
53620
  const planWritePath = extractPlanFileWritePathFromShellCommand(command);
53247
53621
  if (planWritePath) {
53248
- const plansDir = join14(homedir12(), ".letta", "plans");
53622
+ const plansDir = join14(homedir13(), ".letta", "plans");
53249
53623
  const resolvedPath = resolvePlanTargetPath(planWritePath, workingDirectory);
53250
53624
  if (resolvedPath && isPathInPlansDir(resolvedPath, plansDir)) {
53251
53625
  return "allow";
@@ -53254,6 +53628,94 @@ class PermissionModeManager {
53254
53628
  }
53255
53629
  return "deny";
53256
53630
  }
53631
+ case "memory": {
53632
+ const allowedMemoryRoots = resolveAllowedMemoryRoots().roots;
53633
+ const allowedReadOnlyTools = [
53634
+ "Read",
53635
+ "Glob",
53636
+ "Grep",
53637
+ "NotebookRead",
53638
+ "ViewImage",
53639
+ "view_image",
53640
+ "TaskOutput",
53641
+ "task_output",
53642
+ "Skill",
53643
+ "skill",
53644
+ "read_file",
53645
+ "list_dir",
53646
+ "grep_files",
53647
+ "ReadFile",
53648
+ "ListDir",
53649
+ "GrepFiles",
53650
+ "read_file_gemini",
53651
+ "glob_gemini",
53652
+ "list_directory",
53653
+ "search_file_content",
53654
+ "read_many_files",
53655
+ "ReadFileGemini",
53656
+ "GlobGemini",
53657
+ "ListDirectory",
53658
+ "SearchFileContent",
53659
+ "ReadManyFiles"
53660
+ ];
53661
+ const writeTools = [
53662
+ "Write",
53663
+ "Edit",
53664
+ "MultiEdit",
53665
+ "NotebookEdit",
53666
+ "apply_patch",
53667
+ "ApplyPatch",
53668
+ "replace",
53669
+ "Replace",
53670
+ "write_file",
53671
+ "WriteFile",
53672
+ "write_file_gemini",
53673
+ "WriteFileGemini"
53674
+ ];
53675
+ const shellTools = [
53676
+ "Bash",
53677
+ "shell",
53678
+ "Shell",
53679
+ "shell_command",
53680
+ "ShellCommand",
53681
+ "run_shell_command",
53682
+ "RunShellCommand",
53683
+ "run_shell_command_gemini",
53684
+ "RunShellCommandGemini"
53685
+ ];
53686
+ if (allowedReadOnlyTools.includes(toolName)) {
53687
+ return "allow";
53688
+ }
53689
+ if (toolName === "memory_apply_patch") {
53690
+ return allowedMemoryRoots.length > 0 ? "allow" : "deny";
53691
+ }
53692
+ if (writeTools.includes(toolName)) {
53693
+ const targetPath = toolArgs?.file_path || toolArgs?.path;
53694
+ let candidatePaths = [];
53695
+ if ((toolName === "ApplyPatch" || toolName === "apply_patch") && toolArgs?.input) {
53696
+ candidatePaths = extractApplyPatchPaths(toolArgs.input);
53697
+ } else if (typeof targetPath === "string") {
53698
+ candidatePaths = [targetPath];
53699
+ }
53700
+ if (allowedMemoryRoots.length > 0 && everyResolvedTargetIsWithinRoots(candidatePaths, allowedMemoryRoots, workingDirectory)) {
53701
+ return "allow";
53702
+ }
53703
+ return "deny";
53704
+ }
53705
+ if (shellTools.includes(toolName)) {
53706
+ const command = toolArgs?.command;
53707
+ if (command && isReadOnlyShellCommand(command, { allowExternalPaths: true })) {
53708
+ return "allow";
53709
+ }
53710
+ if (command && allowedMemoryRoots.length > 0 && isScopedMemoryShellCommand(command, allowedMemoryRoots, {
53711
+ workingDirectory
53712
+ })) {
53713
+ return "allow";
53714
+ }
53715
+ return "deny";
53716
+ }
53717
+ return "deny";
53718
+ }
53257
53719
  case "default":
53258
53720
  return null;
53259
53721
  default:
@@ -53268,6 +53730,7 @@ class PermissionModeManager {
53268
53730
  }
53269
53731
  var MODE_KEY, PLAN_FILE_KEY, MODE_BEFORE_PLAN_KEY, permissionMode;
53270
53732
  var init_mode = __esm(() => {
53733
+ init_memoryScope();
53271
53734
  init_readOnlyShell();
53272
53735
  init_shell_command_normalization();
53273
53736
  MODE_KEY = Symbol.for("@letta/permissionMode");
@@ -57340,8 +57803,8 @@ import {
57340
57803
  unlink,
57341
57804
  writeFile as writeFile2
57342
57805
  } from "node:fs/promises";
57343
- import { homedir as homedir14 } from "node:os";
57344
- import { dirname as dirname6, isAbsolute as isAbsolute8, relative as relative5, resolve as resolve12 } from "node:path";
57806
+ import { homedir as homedir15 } from "node:os";
57807
+ import { dirname as dirname7, isAbsolute as isAbsolute9, relative as relative5, resolve as resolve12 } from "node:path";
57345
57808
  import { promisify as promisify10 } from "node:util";
57346
57809
  async function getAgentIdentity() {
57347
57810
  const envAgentId = (process.env.AGENT_ID || process.env.LETTA_AGENT_ID || "").trim();
@@ -57390,7 +57853,7 @@ async function memory(args) {
57390
57853
  const rendered = renderMemoryFile({
57391
57854
  description
57392
57855
  }, body);
57393
- await mkdir2(dirname6(filePath), { recursive: true });
57856
+ await mkdir2(dirname7(filePath), { recursive: true });
57394
57857
  await writeFile2(filePath, rendered, "utf8");
57395
57858
  affectedPaths = [relPath];
57396
57859
  } else if (command === "str_replace") {
@@ -57459,7 +57922,7 @@ async function memory(args) {
57459
57922
  throw new Error(`memory rename: destination already exists at ${newPathArg}`);
57460
57923
  }
57461
57924
  await loadEditableMemoryFile(oldFilePath, oldPathArg);
57462
- await mkdir2(dirname6(newFilePath), { recursive: true });
57925
+ await mkdir2(dirname7(newFilePath), { recursive: true });
57463
57926
  await rename(oldFilePath, newFilePath);
57464
57927
  affectedPaths = [oldRelPath, newRelPath];
57465
57928
  } else if (command === "update_description") {
@@ -57507,7 +57970,7 @@ function resolveMemoryDir() {
57507
57970
  })();
57508
57971
  const agentId = contextAgentId || (process.env.AGENT_ID || process.env.LETTA_AGENT_ID || "").trim();
57509
57972
  if (agentId && agentId.trim().length > 0) {
57510
- return resolve12(homedir14(), ".letta", "agents", agentId, "memory");
57973
+ return resolve12(homedir15(), ".letta", "agents", agentId, "memory");
57511
57974
  }
57512
57975
  throw new Error("memory: unable to resolve memory directory. Ensure MEMORY_DIR (or AGENT_ID) is available.");
57513
57976
  }
@@ -57528,10 +57991,10 @@ function normalizeMemoryLabel(memoryDir, inputPath, fieldName) {
57528
57991
  throw new Error(`memory: '${fieldName}' must be a memory-relative file path, not a home-relative filesystem path`);
57529
57992
  }
57530
57993
  const isWindowsAbsolute = /^[a-zA-Z]:[\\/]/.test(raw);
57531
- if (isAbsolute8(raw) || isWindowsAbsolute) {
57994
+ if (isAbsolute9(raw) || isWindowsAbsolute) {
57532
57995
  const absolutePath = resolve12(raw);
57533
57996
  const relToMemory = relative5(memoryDir, absolutePath);
57534
- if (relToMemory && !relToMemory.startsWith("..") && !isAbsolute8(relToMemory)) {
57997
+ if (relToMemory && !relToMemory.startsWith("..") && !isAbsolute9(relToMemory)) {
57535
57998
  return normalizeRelativeMemoryLabel(relToMemory, fieldName);
57536
57999
  }
57537
58000
  throw new Error(memoryPrefixError(memoryDir));
@@ -57577,14 +58040,14 @@ function resolveMemoryFilePath(memoryDir, label) {
57577
58040
  function resolveMemoryPath(memoryDir, path11) {
57578
58041
  const absolute = resolve12(memoryDir, path11);
57579
58042
  const rel = relative5(memoryDir, absolute);
57580
- if (rel.startsWith("..") || isAbsolute8(rel)) {
58043
+ if (rel.startsWith("..") || isAbsolute9(rel)) {
57581
58044
  throw new Error("memory: resolved path escapes memory directory");
57582
58045
  }
57583
58046
  return absolute;
57584
58047
  }
57585
58048
  function toRepoRelative(memoryDir, absolutePath) {
57586
58049
  const rel = relative5(memoryDir, absolutePath);
57587
- if (!rel || rel.startsWith("..") || isAbsolute8(rel)) {
58050
+ if (!rel || rel.startsWith("..") || isAbsolute9(rel)) {
57588
58051
  throw new Error("memory: path is outside memory repository");
57589
58052
  }
57590
58053
  return rel.replace(/\\/g, "/");
@@ -57755,8 +58218,8 @@ import {
57755
58218
  unlink as unlink2,
57756
58219
  writeFile as writeFile3
57757
58220
  } from "node:fs/promises";
57758
- import { homedir as homedir15 } from "node:os";
57759
- import { dirname as dirname7, isAbsolute as isAbsolute9, relative as relative6, resolve as resolve13 } from "node:path";
58221
+ import { homedir as homedir16 } from "node:os";
58222
+ import { dirname as dirname8, isAbsolute as isAbsolute10, relative as relative6, resolve as resolve13 } from "node:path";
57760
58223
  import { promisify as promisify11 } from "node:util";
57761
58224
  async function getAgentIdentity2() {
57762
58225
  const envAgentId = (process.env.AGENT_ID || process.env.LETTA_AGENT_ID || "").trim();
@@ -57869,7 +58332,7 @@ async function memory_apply_patch(args) {
57869
58332
  }
57870
58333
  }
57871
58334
  for (const [absPath, content] of pendingWrites.entries()) {
57872
- await mkdir3(dirname7(absPath), { recursive: true });
58335
+ await mkdir3(dirname8(absPath), { recursive: true });
57873
58336
  await writeFile3(absPath, content, "utf8");
57874
58337
  }
57875
58338
  for (const absPath of pendingDeletes) {
@@ -58043,7 +58506,7 @@ function resolveMemoryDir2() {
58043
58506
  })();
58044
58507
  const agentId = contextAgentId || (process.env.AGENT_ID || process.env.LETTA_AGENT_ID || "").trim();
58045
58508
  if (agentId && agentId.trim().length > 0) {
58046
- return resolve13(homedir15(), ".letta", "agents", agentId, "memory");
58509
+ return resolve13(homedir16(), ".letta", "agents", agentId, "memory");
58047
58510
  }
58048
58511
  throw new Error("memory_apply_patch: unable to resolve memory directory. Ensure MEMORY_DIR (or AGENT_ID) is available.");
58049
58512
  }
@@ -58064,10 +58527,10 @@ function normalizeMemoryLabel2(memoryDir, inputPath, fieldName) {
58064
58527
  throw new Error(`memory_apply_patch: '${fieldName}' must be a memory-relative file path, not a home-relative filesystem path`);
58065
58528
  }
58066
58529
  const isWindowsAbsolute = /^[a-zA-Z]:[\\/]/.test(raw);
58067
- if (isAbsolute9(raw) || isWindowsAbsolute) {
58530
+ if (isAbsolute10(raw) || isWindowsAbsolute) {
58068
58531
  const absolutePath = resolve13(raw);
58069
58532
  const relToMemory = relative6(memoryDir, absolutePath);
58070
- if (relToMemory && !relToMemory.startsWith("..") && !isAbsolute9(relToMemory)) {
58533
+ if (relToMemory && !relToMemory.startsWith("..") && !isAbsolute10(relToMemory)) {
58071
58534
  return normalizeRelativeMemoryLabel2(relToMemory, fieldName);
58072
58535
  }
58073
58536
  throw new Error(memoryPrefixError2(memoryDir));
@@ -58109,7 +58572,7 @@ function memoryPrefixError2(memoryDir) {
58109
58572
  function resolveMemoryPath2(memoryDir, path11) {
58110
58573
  const absolute = resolve13(memoryDir, path11);
58111
58574
  const rel = relative6(memoryDir, absolute);
58112
- if (rel.startsWith("..") || isAbsolute9(rel)) {
58575
+ if (rel.startsWith("..") || isAbsolute10(rel)) {
58113
58576
  throw new Error("memory_apply_patch: resolved path escapes memory directory");
58114
58577
  }
58115
58578
  return absolute;
@@ -58119,7 +58582,7 @@ function resolveMemoryFilePath2(memoryDir, label) {
58119
58582
  }
58120
58583
  function toRepoRelative2(memoryDir, absolutePath) {
58121
58584
  const rel = relative6(memoryDir, absolutePath);
58122
- if (!rel || rel.startsWith("..") || isAbsolute9(rel)) {
58585
+ if (!rel || rel.startsWith("..") || isAbsolute10(rel)) {
58123
58586
  throw new Error("memory_apply_patch: path is outside memory repository");
58124
58587
  }
58125
58588
  return rel.replace(/\\/g, "/");
@@ -65158,10 +65621,10 @@ __export(exports_skills, {
65158
65621
  });
65159
65622
  import { existsSync as existsSync17 } from "node:fs";
65160
65623
  import { readdir as readdir4, readFile as readFile5, realpath as realpath2, stat as stat4 } from "node:fs/promises";
65161
- import { dirname as dirname8, join as join19 } from "node:path";
65624
+ import { dirname as dirname9, join as join19 } from "node:path";
65162
65625
  import { fileURLToPath as fileURLToPath7 } from "node:url";
65163
65626
  function getBundledSkillsPath() {
65164
- const thisDir = dirname8(fileURLToPath7(import.meta.url));
65627
+ const thisDir = dirname9(fileURLToPath7(import.meta.url));
65165
65628
  if (thisDir.includes("src/agent") || thisDir.includes("src\\agent")) {
65166
65629
  return join19(thisDir, "../skills/builtin");
65167
65630
  }
@@ -65363,10 +65826,10 @@ var init_skillContentRegistry = __esm(() => {
65363
65826
  // src/tools/impl/Skill.ts
65364
65827
  import { readdirSync as readdirSync7 } from "node:fs";
65365
65828
  import { readFile as readFile6 } from "node:fs/promises";
65366
- import { dirname as dirname9, join as join20 } from "node:path";
65829
+ import { dirname as dirname10, join as join20 } from "node:path";
65367
65830
  function hasAdditionalFiles(skillMdPath) {
65368
65831
  try {
65369
- const skillDir = dirname9(skillMdPath);
65832
+ const skillDir = dirname10(skillMdPath);
65370
65833
  const entries = readdirSync7(skillDir);
65371
65834
  return entries.some((e) => e.toUpperCase() !== "SKILL.MD");
65372
65835
  } catch {
@@ -65425,7 +65888,7 @@ async function skill(args) {
65425
65888
  const agentId = getCurrentAgentId();
65426
65889
  const skillsDir = await getResolvedSkillsDir();
65427
65890
  const { content: skillContent, path: skillPath } = await readSkillContent(skillName, skillsDir, agentId);
65428
- const skillDir = dirname9(skillPath);
65891
+ const skillDir = dirname10(skillPath);
65429
65892
  const hasExtras = hasAdditionalFiles(skillPath);
65430
65893
  const processedContent = hasExtras ? skillContent.replace(/<SKILL_DIR>/g, skillDir) : skillContent;
65431
65894
  const dirHeader = hasExtras ? `# Skill Directory: ${skillDir}
@@ -66009,15 +66472,32 @@ async function executeSubagent(type, config, model, userPrompt, baseURL, subagen
66009
66472
  const inheritedApiKey = process.env.LETTA_API_KEY || settings.env?.LETTA_API_KEY;
66010
66473
  const inheritedBaseUrl = process.env.LETTA_BASE_URL || settings.env?.LETTA_BASE_URL;
66011
66474
  const subagentWorkingDirectory = resolveSubagentWorkingDirectory();
66475
+ const inheritedMemoryRoots = resolveAllowedMemoryRoots();
66476
+ const childEnv = {
66477
+ ...process.env,
66478
+ ...inheritedApiKey && { LETTA_API_KEY: inheritedApiKey },
66479
+ ...inheritedBaseUrl && { LETTA_BASE_URL: inheritedBaseUrl },
66480
+ LETTA_CODE_AGENT_ROLE: "subagent",
66481
+ ...parentAgentId && { LETTA_PARENT_AGENT_ID: parentAgentId }
66482
+ };
66483
+ if (config.permissionMode === "memory") {
66484
+ if (inheritedMemoryRoots.primaryRoot) {
66485
+ childEnv.MEMORY_DIR = inheritedMemoryRoots.primaryRoot;
66486
+ childEnv.LETTA_MEMORY_DIR = inheritedMemoryRoots.primaryRoot;
66487
+ } else {
66488
+ delete childEnv.MEMORY_DIR;
66489
+ delete childEnv.LETTA_MEMORY_DIR;
66490
+ }
66491
+ const parentMemoryDir = process.env.MEMORY_DIR || process.env.LETTA_MEMORY_DIR;
66492
+ if (parentMemoryDir && parentMemoryDir.trim().length > 0) {
66493
+ childEnv.PARENT_MEMORY_DIR = parentMemoryDir;
66494
+ } else {
66495
+ delete childEnv.PARENT_MEMORY_DIR;
66496
+ }
66497
+ }
66012
66498
  const proc2 = spawn4(launcher.command, launcher.args, {
66013
66499
  cwd: subagentWorkingDirectory,
66014
- env: {
66015
- ...process.env,
66016
- ...inheritedApiKey && { LETTA_API_KEY: inheritedApiKey },
66017
- ...inheritedBaseUrl && { LETTA_BASE_URL: inheritedBaseUrl },
66018
- LETTA_CODE_AGENT_ROLE: "subagent",
66019
- ...parentAgentId && { LETTA_PARENT_AGENT_ID: parentAgentId }
66020
- }
66500
+ env: childEnv
66021
66501
  });
66022
66502
  proc2.once("spawn", () => {
66023
66503
  updateSubagent(subagentId, { status: "running" });
@@ -66196,6 +66676,7 @@ var init_manager2 = __esm(async () => {
66196
66676
  init_subagentState();
66197
66677
  init_constants();
66198
66678
  init_cli();
66679
+ init_memoryScope();
66199
66680
  init_mode();
66200
66681
  init_session();
66201
66682
  init_context();
@@ -69706,8 +70187,8 @@ function matchesFilePattern(query, pattern, workingDirectory, options) {
69706
70187
  globPattern = globPattern.slice(2);
69707
70188
  }
69708
70189
  if (globPattern.startsWith("~/")) {
69709
- const homedir16 = __require("node:os").homedir();
69710
- globPattern = globPattern.replace(/^~/, homedir16);
70190
+ const homedir17 = __require("node:os").homedir();
70191
+ globPattern = globPattern.replace(/^~/, homedir17);
69711
70192
  }
69712
70193
  globPattern = normalizeAbsolutePattern(globPattern, workingDirectory);
69713
70194
  const windowsContext = isWindowsContext(workingDirectory);
@@ -70220,7 +70701,7 @@ function getDefaultDecision(toolName, toolArgs) {
70220
70701
  }
70221
70702
  if (toolName === "Task" || toolName === "task") {
70222
70703
  const subagentType = typeof toolArgs?.subagent_type === "string" ? toolArgs.subagent_type : "";
70223
- if (READ_ONLY_SUBAGENT_TYPES.has(subagentType)) {
70704
+ if (SAFE_AUTO_APPROVE_SUBAGENT_TYPES.has(subagentType)) {
70224
70705
  return "allow";
70225
70706
  }
70226
70707
  return "ask";
@@ -70251,7 +70732,7 @@ async function checkPermissionWithHooks(toolName, toolArgs, permissions, working
70251
70732
  }
70252
70733
  return result;
70253
70734
  }
70254
- var WORKING_DIRECTORY_TOOLS_V2, WORKING_DIRECTORY_TOOLS_V1, READ_ONLY_SHELL_TOOLS, FILE_TOOLS_V2, FILE_TOOLS_V1, READ_ONLY_SUBAGENT_TYPES;
70735
+ var WORKING_DIRECTORY_TOOLS_V2, WORKING_DIRECTORY_TOOLS_V1, READ_ONLY_SHELL_TOOLS, FILE_TOOLS_V2, FILE_TOOLS_V1, SAFE_AUTO_APPROVE_SUBAGENT_TYPES;
70255
70736
  var init_checker = __esm(async () => {
70256
70737
  init_context();
70257
70738
  init_canonical();
@@ -70318,7 +70799,7 @@ var init_checker = __esm(async () => {
70318
70799
  "read_many_files",
70319
70800
  "ReadManyFiles"
70320
70801
  ];
70321
- READ_ONLY_SUBAGENT_TYPES = new Set([
70802
+ SAFE_AUTO_APPROVE_SUBAGENT_TYPES = new Set([
70322
70803
  "explore",
70323
70804
  "Explore",
70324
70805
  "recall",
@@ -70336,10 +70817,10 @@ __export(exports_loader, {
70336
70817
  loadPermissions: () => loadPermissions,
70337
70818
  getUserSettingsPaths: () => getUserSettingsPaths
70338
70819
  });
70339
- import { homedir as homedir16 } from "node:os";
70820
+ import { homedir as homedir17 } from "node:os";
70340
70821
  import { join as join21 } from "node:path";
70341
70822
  function getUserSettingsPaths(options = {}) {
70342
- const homeDir = options.homeDir || homedir16();
70823
+ const homeDir = options.homeDir || homedir17();
70343
70824
  const xdgConfigHome = options.xdgConfigHome || process.env.XDG_CONFIG_HOME || join21(homeDir, ".config");
70344
70825
  return {
70345
70826
  canonical: join21(homeDir, ".letta", "settings.json"),
@@ -70461,8 +70942,8 @@ var exports_analyzer = {};
70461
70942
  __export(exports_analyzer, {
70462
70943
  analyzeApprovalContext: () => analyzeApprovalContext
70463
70944
  });
70464
- import { homedir as homedir17 } from "node:os";
70465
- import { dirname as dirname11, relative as relative8, resolve as resolve22, win32 as win322 } from "node:path";
70945
+ import { homedir as homedir18 } from "node:os";
70946
+ import { dirname as dirname12, relative as relative8, resolve as resolve22, win32 as win322 } from "node:path";
70466
70947
  function normalizeOsPath(path20) {
70467
70948
  return path20.replace(/\\/g, "/");
70468
70949
  }
@@ -70488,7 +70969,7 @@ function isPathWithinDirectory(path20, directory) {
70488
70969
  return !relativePath.startsWith("../") && relativePath !== ".." && !relativePath.startsWith("/") && !/^[a-zA-Z]:\//.test(relativePath);
70489
70970
  }
70490
70971
  function dirnameForContext(path20) {
70491
- return isWindowsPath(path20) ? win322.dirname(path20) : dirname11(path20);
70972
+ return isWindowsPath(path20) ? win322.dirname(path20) : dirname12(path20);
70492
70973
  }
70493
70974
  function formatAbsoluteRulePath(path20) {
70494
70975
  const normalized = normalizeOsPath(path20).replace(/\/+$/, "");
@@ -70498,7 +70979,7 @@ function formatAbsoluteRulePath(path20) {
70498
70979
  return `//${normalized.replace(/^\/+/, "")}`;
70499
70980
  }
70500
70981
  function formatDisplayPath(path20) {
70501
- return normalizeOsPath(path20).replace(normalizeOsPath(homedir17()), "~");
70982
+ return normalizeOsPath(path20).replace(normalizeOsPath(homedir18()), "~");
70502
70983
  }
70503
70984
  function analyzeApprovalContext(toolName, toolArgs, workingDirectory) {
70504
70985
  const canonicalTool = canonicalToolName(toolName);
@@ -70549,7 +71030,7 @@ function analyzeReadApproval(filePath, workingDir) {
70549
71030
  };
70550
71031
  }
70551
71032
  const relativePath = normalizeOsPath(relativePathForContext(workingDir, absolutePath));
70552
- const relativeDir = dirname11(relativePath);
71033
+ const relativeDir = dirname12(relativePath);
70553
71034
  const pattern = relativeDir === "." || relativeDir === "" ? "**" : `${relativeDir}/**`;
70554
71035
  return {
70555
71036
  recommendedRule: `Read(${pattern})`,
@@ -70692,7 +71173,7 @@ function detectSkillScript(command, workingDir) {
70692
71173
  return null;
70693
71174
  }
70694
71175
  const normalizedWorkingDir = normalizePathSeparators(workingDir).replace(/\/$/, "");
70695
- const normalizedHomeDir = normalizePathSeparators(homedir17()).replace(/\/$/, "");
71176
+ const normalizedHomeDir = normalizePathSeparators(homedir18()).replace(/\/$/, "");
70696
71177
  const detect = (source, regex2) => {
70697
71178
  for (const candidate of pathCandidates) {
70698
71179
  const match3 = candidate.match(regex2);
@@ -72224,15 +72705,15 @@ __export(exports_memoryFilesystem, {
72224
72705
  MEMORY_FS_AGENTS_DIR: () => MEMORY_FS_AGENTS_DIR
72225
72706
  });
72226
72707
  import { existsSync as existsSync18, mkdirSync as mkdirSync13 } from "node:fs";
72227
- import { homedir as homedir18 } from "node:os";
72708
+ import { homedir as homedir19 } from "node:os";
72228
72709
  import { join as join22 } from "node:path";
72229
- function getMemoryFilesystemRoot(agentId, homeDir = homedir18()) {
72710
+ function getMemoryFilesystemRoot(agentId, homeDir = homedir19()) {
72230
72711
  return join22(homeDir, MEMORY_FS_ROOT, MEMORY_FS_AGENTS_DIR, agentId, MEMORY_FS_MEMORY_DIR);
72231
72712
  }
72232
- function getMemorySystemDir(agentId, homeDir = homedir18()) {
72713
+ function getMemorySystemDir(agentId, homeDir = homedir19()) {
72233
72714
  return join22(getMemoryFilesystemRoot(agentId, homeDir), MEMORY_SYSTEM_DIR);
72234
72715
  }
72235
- function ensureMemoryFilesystemDirs(agentId, homeDir = homedir18()) {
72716
+ function ensureMemoryFilesystemDirs(agentId, homeDir = homedir19()) {
72236
72717
  const root = getMemoryFilesystemRoot(agentId, homeDir);
72237
72718
  const systemDir = getMemorySystemDir(agentId, homeDir);
72238
72719
  if (!existsSync18(root)) {
@@ -73402,18 +73883,29 @@ var init_turn_recovery_policy = __esm(() => {
73402
73883
  });
73403
73884
 
73404
73885
  // src/agent/approval-recovery.ts
73405
- async function fetchRunErrorDetail(runId) {
73886
+ async function fetchRunErrorInfo(runId) {
73406
73887
  if (!runId)
73407
73888
  return null;
73408
73889
  try {
73409
73890
  const client = await getClient();
73410
73891
  const run = await client.runs.retrieve(runId);
73411
73892
  const metaError = run.metadata?.error;
73412
- return metaError?.detail ?? metaError?.message ?? metaError?.error?.detail ?? metaError?.error?.message ?? null;
73893
+ const nestedError = metaError?.error;
73894
+ const errorInfo = {
73895
+ error_type: metaError?.error_type ?? metaError?.type ?? nestedError?.error_type ?? nestedError?.type,
73896
+ message: metaError?.message ?? nestedError?.message,
73897
+ detail: metaError?.detail ?? nestedError?.detail,
73898
+ run_id: metaError?.run_id ?? nestedError?.run_id ?? runId
73899
+ };
73900
+ return errorInfo.error_type || errorInfo.message || errorInfo.detail ? errorInfo : null;
73413
73901
  } catch {
73414
73902
  return null;
73415
73903
  }
73416
73904
  }
73905
+ async function fetchRunErrorDetail(runId) {
73906
+ const errorInfo = await fetchRunErrorInfo(runId);
73907
+ return errorInfo?.detail ?? errorInfo?.message ?? null;
73908
+ }
73417
73909
  var init_approval_recovery = __esm(async () => {
73418
73910
  init_turn_recovery_policy();
73419
73911
  await init_client2();
@@ -75287,7 +75779,7 @@ import {
75287
75779
  readFile as readFile7,
75288
75780
  writeFile as writeFile4
75289
75781
  } from "node:fs/promises";
75290
- import { homedir as homedir19, tmpdir as tmpdir3 } from "node:os";
75782
+ import { homedir as homedir20, tmpdir as tmpdir3 } from "node:os";
75291
75783
  import { join as join25 } from "node:path";
75292
75784
  function buildReflectionSubagentPrompt(input) {
75293
75785
  const lines = [];
@@ -75481,7 +75973,7 @@ function getTranscriptRoot() {
75481
75973
  if (envRoot) {
75482
75974
  return envRoot;
75483
75975
  }
75484
- return join25(homedir19(), ".letta", DEFAULT_TRANSCRIPT_DIR);
75976
+ return join25(homedir20(), ".letta", DEFAULT_TRANSCRIPT_DIR);
75485
75977
  }
75486
75978
  function defaultState() {
75487
75979
  return { auto_cursor_line: 0 };
@@ -75670,7 +76162,7 @@ import {
75670
76162
  unlinkSync as unlinkSync7,
75671
76163
  writeFileSync as writeFileSync11
75672
76164
  } from "node:fs";
75673
- import { homedir as homedir20 } from "node:os";
76165
+ import { homedir as homedir21 } from "node:os";
75674
76166
  import { join as join26 } from "node:path";
75675
76167
  function truncateStr(value, maxLen) {
75676
76168
  if (value === null || value === undefined)
@@ -75811,7 +76303,7 @@ class ChunkLog {
75811
76303
  var MAX_ENTRIES = 100, CONTENT_TRUNCATE_LEN = 200, MAX_SESSION_FILES2 = 5, LOG_BASE_DIR, chunkLog;
75812
76304
  var init_chunkLog = __esm(() => {
75813
76305
  init_debug();
75814
- LOG_BASE_DIR = join26(homedir20(), ".letta", "logs", "chunk-logs");
76306
+ LOG_BASE_DIR = join26(homedir21(), ".letta", "logs", "chunk-logs");
75815
76307
  chunkLog = new ChunkLog;
75816
76308
  });
75817
76309
 
@@ -75857,6 +76349,7 @@ class StreamProcessor {
75857
76349
  const errorDetail = chunkWithError.error.detail || "";
75858
76350
  errorInfo = {
75859
76351
  message: errorDetail ? `${errorText}: ${errorDetail}` : errorText,
76352
+ detail: errorDetail || undefined,
75860
76353
  run_id: this.lastRunId || undefined
75861
76354
  };
75862
76355
  }
@@ -76827,6 +77320,7 @@ var init_engine = __esm(async () => {
76827
77320
  default: "Normal approval flow.",
76828
77321
  acceptEdits: "File edits auto-approved.",
76829
77322
  plan: "Read-only mode. Focus on exploration and planning.",
77323
+ memory: "Memory-scoped mode. Reads are broad; mutations are limited to allowed memory roots.",
76830
77324
  bypassPermissions: "All tools auto-approved. Bias toward action."
76831
77325
  };
76832
77326
  sharedReminderProviders = {
@@ -76939,10 +77433,10 @@ var init_constants2 = __esm(() => {
76939
77433
  // src/websocket/listener/remote-settings.ts
76940
77434
  import { existsSync as existsSync21, readFileSync as readFileSync12 } from "node:fs";
76941
77435
  import { mkdir as mkdir5, writeFile as writeFile5 } from "node:fs/promises";
76942
- import { homedir as homedir21 } from "node:os";
77436
+ import { homedir as homedir22 } from "node:os";
76943
77437
  import path20 from "node:path";
76944
77438
  function getRemoteSettingsPath() {
76945
- return path20.join(homedir21(), ".letta", "remote-settings.json");
77439
+ return path20.join(homedir22(), ".letta", "remote-settings.json");
76946
77440
  }
76947
77441
  function loadRemoteSettings() {
76948
77442
  if (_cache !== null) {
@@ -76986,7 +77480,7 @@ function saveRemoteSettings(updates) {
76986
77480
  }
76987
77481
  function loadLegacyCwdCache() {
76988
77482
  try {
76989
- const legacyPath = path20.join(homedir21(), ".letta", "cwd-cache.json");
77483
+ const legacyPath = path20.join(homedir22(), ".letta", "cwd-cache.json");
76990
77484
  if (!existsSync21(legacyPath))
76991
77485
  return {};
76992
77486
  const raw = readFileSync12(legacyPath, "utf-8");
@@ -77186,13 +77680,52 @@ function requestApprovalOverWS(runtime, socket, requestId, controlRequest) {
77186
77680
  if (socket.readyState !== WebSocket.OPEN) {
77187
77681
  return Promise.reject(new Error("WebSocket not open"));
77188
77682
  }
77683
+ const abortSignal = runtime.activeAbortController?.signal ?? null;
77684
+ const isInterrupted = () => runtime.cancelRequested || abortSignal?.aborted === true;
77685
+ if (isInterrupted()) {
77686
+ return Promise.reject(new Error("Cancelled by user"));
77687
+ }
77189
77688
  return new Promise((resolve23, reject) => {
77689
+ let settled = false;
77690
+ const cleanupAbortListener = () => {
77691
+ abortSignal?.removeEventListener("abort", handleAbort);
77692
+ };
77693
+ const wrappedResolve = (response) => {
77694
+ if (settled) {
77695
+ return;
77696
+ }
77697
+ settled = true;
77698
+ cleanupAbortListener();
77699
+ resolve23(response);
77700
+ };
77701
+ const wrappedReject = (error) => {
77702
+ if (settled) {
77703
+ return;
77704
+ }
77705
+ settled = true;
77706
+ cleanupAbortListener();
77707
+ reject(error);
77708
+ };
77709
+ const handleAbort = () => {
77710
+ runtime.pendingApprovalResolvers.delete(requestId);
77711
+ runtime.listener.approvalRuntimeKeyByRequestId.delete(requestId);
77712
+ wrappedReject(new Error("Cancelled by user"));
77713
+ };
77714
+ abortSignal?.addEventListener("abort", handleAbort, { once: true });
77715
+ if (isInterrupted()) {
77716
+ handleAbort();
77717
+ return;
77718
+ }
77190
77719
  runtime.pendingApprovalResolvers.set(requestId, {
77191
- resolve: resolve23,
77192
- reject,
77720
+ resolve: wrappedResolve,
77721
+ reject: wrappedReject,
77193
77722
  controlRequest
77194
77723
  });
77195
77724
  runtime.listener.approvalRuntimeKeyByRequestId.set(requestId, runtime.key);
77725
+ if (isInterrupted()) {
77726
+ handleAbort();
77727
+ return;
77728
+ }
77196
77729
  runtime.lastStopReason = "requires_approval";
77197
77730
  setLoopStatus(runtime, "WAITING_ON_APPROVAL");
77198
77731
  emitLoopStatusIfOpen(runtime.listener, {
@@ -77489,12 +78022,18 @@ function populateInterruptQueue(runtime, input) {
77489
78022
  return false;
77490
78023
  }
77491
78024
  function consumeInterruptQueue(runtime, agentId, conversationId) {
78025
+ const ctx = runtime.pendingInterruptedContext;
78026
+ const matchingContext = !!ctx && ctx.agentId === agentId && ctx.conversationId === conversationId && ctx.continuationEpoch === runtime.continuationEpoch;
77492
78027
  if (!runtime.pendingInterruptedResults || runtime.pendingInterruptedResults.length === 0) {
78028
+ if (matchingContext) {
78029
+ runtime.pendingInterruptedResults = null;
78030
+ runtime.pendingInterruptedContext = null;
78031
+ runtime.pendingInterruptedToolCallIds = null;
78032
+ }
77493
78033
  return null;
77494
78034
  }
77495
- const ctx = runtime.pendingInterruptedContext;
77496
78035
  let result = null;
77497
- if (ctx && ctx.agentId === agentId && ctx.conversationId === conversationId && ctx.continuationEpoch === runtime.continuationEpoch) {
78036
+ if (matchingContext) {
77498
78037
  result = {
77499
78038
  approvalMessage: {
77500
78039
  type: "approval",
@@ -77691,6 +78230,104 @@ function mirrorRecoverableNoticeToDesktopDebugPanel(message) {
77691
78230
  `);
77692
78231
  } catch {}
77693
78232
  }
78233
+ function toStructuredApiError(errorInfo) {
78234
+ if (!errorInfo?.error_type || !errorInfo.run_id) {
78235
+ return;
78236
+ }
78237
+ return {
78238
+ message_type: "error_message",
78239
+ message: errorInfo.message || errorInfo.detail || "An error occurred",
78240
+ error_type: errorInfo.error_type,
78241
+ run_id: errorInfo.run_id,
78242
+ ...errorInfo.detail ? { detail: errorInfo.detail } : {}
78243
+ };
78244
+ }
78245
+ function getStructuredApiErrorFromError(error) {
78246
+ if (!(error instanceof Error)) {
78247
+ return;
78248
+ }
78249
+ const errorWithStructuredInfo = error;
78250
+ return errorWithStructuredInfo.apiError ?? toStructuredApiError(errorWithStructuredInfo.runErrorInfo);
78251
+ }
78252
+ function buildStructuredFormatInput(apiError) {
78253
+ return {
78254
+ error: {
78255
+ error: {
78256
+ type: apiError.error_type,
78257
+ message: apiError.message,
78258
+ ...apiError.detail ? { detail: apiError.detail } : {}
78259
+ },
78260
+ run_id: apiError.run_id
78261
+ }
78262
+ };
78263
+ }
78264
+ function isAbortLikeError(error) {
78265
+ if (error instanceof APIUserAbortError) {
78266
+ return true;
78267
+ }
78268
+ if (!(error instanceof Error)) {
78269
+ return false;
78270
+ }
78271
+ const errorWithCode = error;
78272
+ return error.name === "AbortError" || error.message === "The operation was aborted" || errorWithCode.code === "ABORT_ERR";
78273
+ }
78274
+ function isTerminatedProcessNoise(message) {
78275
+ return message.trim().toLowerCase() === "terminated";
78276
+ }
78277
+ function isProxyTransportError(detail, error, message) {
78278
+ if (error instanceof APIError2 && error.status >= 500 && detail.toLowerCase().includes("trying to proxy")) {
78279
+ return true;
78280
+ }
78281
+ return detail.toLowerCase().includes("error occurred while trying to proxy") || message.toLowerCase().includes("error occurred while trying to proxy");
78282
+ }
78283
+ function getLoopErrorNoticeDecision(params) {
78284
+ const apiError = params.apiError ?? toStructuredApiError(params.errorInfo) ?? toStructuredApiError(params.runErrorInfo) ?? getStructuredApiErrorFromError(params.error);
78285
+ const detail = apiError?.detail ?? params.errorInfo?.detail ?? params.runErrorInfo?.detail ?? extractConflictDetail(params.error) ?? "";
78286
+ if (params.cancelRequested || params.abortSignal?.aborted || isAbortLikeError(params.error) || isTerminatedProcessNoise(params.message)) {
78287
+ return {
78288
+ visibility: "debug_only",
78289
+ message: params.message
78290
+ };
78291
+ }
78292
+ const cloudflareMessage = checkCloudflareEdgeError2(detail) ?? checkCloudflareEdgeError2(params.message);
78293
+ if (cloudflareMessage) {
78294
+ return {
78295
+ visibility: "transcript",
78296
+ message: cloudflareMessage,
78297
+ apiError
78298
+ };
78299
+ }
78300
+ if (isProxyTransportError(detail, params.error, params.message)) {
78301
+ return {
78302
+ visibility: "transcript",
78303
+ message: "Connection to Letta service failed. Please retry.",
78304
+ apiError
78305
+ };
78306
+ }
78307
+ const formattedMessage = formatErrorDetails2(apiError ? buildStructuredFormatInput(apiError) : params.error ?? params.message, params.agentId ?? undefined, params.conversationId ?? undefined);
78308
+ return {
78309
+ visibility: "transcript",
78310
+ message: formattedMessage,
78311
+ apiError
78312
+ };
78313
+ }
78314
+ function emitLoopErrorNotice(socket, runtime, params) {
78315
+ const decision = getLoopErrorNoticeDecision(params);
78316
+ if (decision.visibility === "debug_only") {
78317
+ debugLog("recovery", `Debug-only loop error (${params.stopReason}): ${params.message}`);
78318
+ mirrorRecoverableNoticeToDesktopDebugPanel(params.message);
78319
+ return;
78320
+ }
78321
+ emitLoopErrorDelta(socket, runtime, {
78322
+ message: decision.message,
78323
+ stopReason: params.stopReason,
78324
+ isTerminal: params.isTerminal,
78325
+ runId: params.runId,
78326
+ agentId: params.agentId,
78327
+ conversationId: params.conversationId,
78328
+ apiError: decision.apiError
78329
+ });
78330
+ }
77694
78331
  function emitRecoverableStatusNotice(socket, runtime, params) {
77695
78332
  const visibility = getRecoverableStatusNoticeVisibility(params.kind);
77696
78333
  if (visibility === "debug_only") {
@@ -77726,6 +78363,9 @@ function emitRecoverableRetryNotice(socket, runtime, params) {
77726
78363
  }
77727
78364
  var DESKTOP_DEBUG_PANEL_INFO_PREFIX = "[LETTA_DESKTOP_DEBUG_PANEL_INFO]";
77728
78365
  var init_recoverable_notices = __esm(async () => {
78366
+ init_error();
78367
+ init_turn_recovery_policy();
78368
+ init_errorFormatter();
77729
78369
  init_debug();
77730
78370
  await init_protocol_outbound();
77731
78371
  });
@@ -78515,7 +79155,7 @@ __export(exports_diff, {
78515
79155
  ADV_DIFF_IGNORE_WHITESPACE: () => ADV_DIFF_IGNORE_WHITESPACE,
78516
79156
  ADV_DIFF_CONTEXT_LINES: () => ADV_DIFF_CONTEXT_LINES
78517
79157
  });
78518
- import { basename as basename2 } from "node:path";
79158
+ import { basename as basename3 } from "node:path";
78519
79159
  function readFileOrNull(p) {
78520
79160
  try {
78521
79161
  return __require("node:fs").readFileSync(p, "utf-8");
@@ -78539,7 +79179,7 @@ function applyAllOccurrences(content, oldStr, newStr) {
78539
79179
  return { ok: true, out: content.split(oldStr).join(newStr) };
78540
79180
  }
78541
79181
  function computeAdvancedDiff(input, opts) {
78542
- const fileName = basename2(input.filePath || "");
79182
+ const fileName = basename3(input.filePath || "");
78543
79183
  const fileContent = opts?.oldStrOverride !== undefined ? opts.oldStrOverride : readFileOrNull(input.filePath);
78544
79184
  if (fileContent === null && input.kind !== "write") {
78545
79185
  return { mode: "fallback", reason: "File not readable" };
@@ -78605,7 +79245,7 @@ function computeAdvancedDiff(input, opts) {
78605
79245
  return { mode: "advanced", fileName, oldStr, newStr, hunks };
78606
79246
  }
78607
79247
  function parsePatchToAdvancedDiff(patchLines, filePath) {
78608
- const fileName = basename2(filePath);
79248
+ const fileName = basename3(filePath);
78609
79249
  const hunks = [];
78610
79250
  let currentHunk = null;
78611
79251
  let oldLine = 1;
@@ -78932,7 +79572,7 @@ var init_formatArgsDisplay = __esm(async () => {
78932
79572
  });
78933
79573
 
78934
79574
  // src/helpers/diffPreview.ts
78935
- import path21, { basename as basename3 } from "node:path";
79575
+ import path21, { basename as basename4 } from "node:path";
78936
79576
  function parseHunkLinePrefix(raw) {
78937
79577
  if (raw.length === 0) {
78938
79578
  return { type: "context", content: "" };
@@ -79038,7 +79678,7 @@ async function computeDiffPreviews(toolName, toolArgs, workingDirectory = proces
79038
79678
  filePath: resolvedFilePath,
79039
79679
  content: toolArgs.content || ""
79040
79680
  });
79041
- previews.push(toDiffPreview(result, basename3(filePath)));
79681
+ previews.push(toDiffPreview(result, basename4(filePath)));
79042
79682
  }
79043
79683
  } else if (isFileEditTool2(toolName)) {
79044
79684
  const filePath = toolArgs.file_path;
@@ -79050,7 +79690,7 @@ async function computeDiffPreviews(toolName, toolArgs, workingDirectory = proces
79050
79690
  filePath: resolvedFilePath,
79051
79691
  edits: toolArgs.edits
79052
79692
  });
79053
- previews.push(toDiffPreview(result, basename3(filePath)));
79693
+ previews.push(toDiffPreview(result, basename4(filePath)));
79054
79694
  } else {
79055
79695
  const result = computeAdvancedDiff2({
79056
79696
  kind: "edit",
@@ -79059,7 +79699,7 @@ async function computeDiffPreviews(toolName, toolArgs, workingDirectory = proces
79059
79699
  newString: toolArgs.new_string || "",
79060
79700
  replaceAll: toolArgs.replace_all
79061
79701
  });
79062
- previews.push(toDiffPreview(result, basename3(filePath)));
79702
+ previews.push(toDiffPreview(result, basename4(filePath)));
79063
79703
  }
79064
79704
  }
79065
79705
  } else if (isPatchTool2(toolName) && toolArgs.input) {
@@ -79068,7 +79708,7 @@ async function computeDiffPreviews(toolName, toolArgs, workingDirectory = proces
79068
79708
  if (op.kind === "add" || op.kind === "update") {
79069
79709
  const result = parsePatchToAdvancedDiff2(op.patchLines, op.path);
79070
79710
  if (result) {
79071
- previews.push(toDiffPreview(result, basename3(op.path)));
79711
+ previews.push(toDiffPreview(result, basename4(op.path)));
79072
79712
  }
79073
79713
  }
79074
79714
  }
@@ -79078,7 +79718,7 @@ async function computeDiffPreviews(toolName, toolArgs, workingDirectory = proces
79078
79718
  if (op.kind === "add" || op.kind === "update") {
79079
79719
  const result = parsePatchToAdvancedDiff2(op.patchLines, op.path);
79080
79720
  if (result) {
79081
- previews.push(toDiffPreview(result, basename3(op.path)));
79721
+ previews.push(toDiffPreview(result, basename4(op.path)));
79082
79722
  }
79083
79723
  }
79084
79724
  }
@@ -79309,13 +79949,15 @@ async function drainRecoveryStreamWithEmission(recoveryStream, socket, runtime,
79309
79949
  }
79310
79950
  }
79311
79951
  if (errorInfo) {
79312
- emitLoopErrorDelta(socket, runtime, {
79952
+ emitLoopErrorNotice(socket, runtime, {
79313
79953
  message: errorInfo.message || "Stream error",
79314
79954
  stopReason: errorInfo.error_type || "error",
79315
79955
  isTerminal: false,
79316
79956
  runId: runtime.activeRunId || errorInfo.run_id,
79317
79957
  agentId: params.agentId ?? undefined,
79318
- conversationId: params.conversationId
79958
+ conversationId: params.conversationId,
79959
+ errorInfo,
79960
+ abortSignal: params.abortSignal
79319
79961
  });
79320
79962
  }
79321
79963
  if (shouldOutput) {
@@ -79366,7 +80008,7 @@ function finalizeHandledRecoveryTurn(runtime, socket, params) {
79366
80008
  const runId = runtime.activeRunId;
79367
80009
  clearActiveRunState(runtime);
79368
80010
  emitRuntimeStateUpdates(runtime, scope);
79369
- emitLoopErrorDelta(socket, runtime, {
80011
+ emitLoopErrorNotice(socket, runtime, {
79370
80012
  message: `Recovery continuation ended unexpectedly: ${terminalStopReason}`,
79371
80013
  stopReason: terminalStopReason,
79372
80014
  isTerminal: true,
@@ -79703,7 +80345,8 @@ var init_recovery = __esm(async () => {
79703
80345
  init_approval_suggestions(),
79704
80346
  init_interrupts(),
79705
80347
  init_protocol_outbound(),
79706
- init_queue()
80348
+ init_queue(),
80349
+ init_recoverable_notices()
79707
80350
  ]);
79708
80351
  });
79709
80352
 
@@ -80004,8 +80647,8 @@ async function sendMessageStreamWithRetry(conversationId, messages, opts, socket
80004
80647
  throw new Error("Cancelled by user");
80005
80648
  }
80006
80649
  }
80007
- const detail = await fetchRunErrorDetail(runtime.activeRunId);
80008
- throw new Error(detail || `Pre-stream approval conflict after ${preStreamRecoveryAttempts} recovery attempts`);
80650
+ const runErrorInfo = await fetchRunErrorInfo(runtime.activeRunId);
80651
+ throw Object.assign(new Error(runErrorInfo?.detail || runErrorInfo?.message || `Pre-stream approval conflict after ${preStreamRecoveryAttempts} recovery attempts`), { runErrorInfo });
80009
80652
  }
80010
80653
  if (action === "retry_transient") {
80011
80654
  runtime.isRecoveringApprovals = true;
@@ -80138,8 +80781,8 @@ async function sendApprovalContinuationWithRetry(conversationId, messages, opts,
80138
80781
  }
80139
80782
  continue;
80140
80783
  }
80141
- const detail = await fetchRunErrorDetail(runtime.activeRunId);
80142
- throw new Error(detail || `Approval continuation conflict after ${preStreamRecoveryAttempts} recovery attempts`);
80784
+ const runErrorInfo = await fetchRunErrorInfo(runtime.activeRunId);
80785
+ throw Object.assign(new Error(runErrorInfo?.detail || runErrorInfo?.message || `Approval continuation conflict after ${preStreamRecoveryAttempts} recovery attempts`), { runErrorInfo });
80143
80786
  }
80144
80787
  if (action === "retry_transient") {
80145
80788
  runtime.isRecoveringApprovals = true;
@@ -80265,7 +80908,7 @@ async function handleApprovalStop(params) {
80265
80908
  agent_id: agentId,
80266
80909
  conversation_id: conversationId
80267
80910
  });
80268
- emitLoopErrorDelta(socket, runtime, {
80911
+ emitLoopErrorNotice(socket, runtime, {
80269
80912
  message: "requires_approval stop returned no approvals",
80270
80913
  stopReason: "error",
80271
80914
  isTerminal: true,
@@ -80568,6 +81211,7 @@ var init_turn_approval = __esm(async () => {
80568
81211
  init_interrupts(),
80569
81212
  init_protocol_outbound(),
80570
81213
  init_queue(),
81214
+ init_recoverable_notices(),
80571
81215
  init_recovery(),
80572
81216
  init_send()
80573
81217
  ]);
@@ -80899,13 +81543,16 @@ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, conne
80899
81543
  }
80900
81544
  if (errorInfo) {
80901
81545
  latestErrorText = errorInfo.message || latestErrorText;
80902
- emitLoopErrorDelta(socket, runtime, {
81546
+ emitLoopErrorNotice(socket, runtime, {
80903
81547
  message: errorInfo.message || "Stream error",
80904
81548
  stopReason: errorInfo.error_type || "error",
80905
81549
  isTerminal: false,
80906
81550
  runId: runId || errorInfo.run_id,
80907
81551
  agentId,
80908
- conversationId
81552
+ conversationId,
81553
+ errorInfo,
81554
+ cancelRequested: runtime.cancelRequested,
81555
+ abortSignal: turnAbortSignal
80909
81556
  });
80910
81557
  }
80911
81558
  if (shouldOutput) {
@@ -80965,7 +81612,8 @@ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, conne
80965
81612
  }
80966
81613
  if (stopReason !== "requires_approval") {
80967
81614
  const lastRunId = runId || msgRunIds[msgRunIds.length - 1] || null;
80968
- const errorDetail = latestErrorText || (lastRunId ? await fetchRunErrorDetail(lastRunId) : null);
81615
+ const runErrorInfo = lastRunId ? await fetchRunErrorInfo(lastRunId) : null;
81616
+ const errorDetail = latestErrorText || runErrorInfo?.detail || runErrorInfo?.message || null;
80969
81617
  if (shouldAttemptPostStopApprovalRecovery({
80970
81618
  stopReason,
80971
81619
  runIdsSeen: msgRunIds.length,
@@ -81127,13 +81775,16 @@ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, conne
81127
81775
  conversation_id: conversationId
81128
81776
  });
81129
81777
  const errorMessage = errorDetail || `Unexpected stop reason: ${stopReason}`;
81130
- emitLoopErrorDelta(socket, runtime, {
81778
+ emitLoopErrorNotice(socket, runtime, {
81131
81779
  message: errorMessage,
81132
81780
  stopReason: effectiveStopReason,
81133
81781
  isTerminal: true,
81134
81782
  runId,
81135
81783
  agentId,
81136
- conversationId
81784
+ conversationId,
81785
+ runErrorInfo: runErrorInfo ?? undefined,
81786
+ cancelRequested: runtime.cancelRequested,
81787
+ abortSignal: turnAbortSignal
81137
81788
  });
81138
81789
  break;
81139
81790
  }
@@ -81217,12 +81868,15 @@ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, conne
81217
81868
  conversation_id: conversationId
81218
81869
  });
81219
81870
  const errorMessage = error instanceof Error ? error.message : String(error);
81220
- emitLoopErrorDelta(socket, runtime, {
81871
+ emitLoopErrorNotice(socket, runtime, {
81221
81872
  message: errorMessage,
81222
81873
  stopReason: "error",
81223
81874
  isTerminal: true,
81224
81875
  agentId: agentId || undefined,
81225
- conversationId
81876
+ conversationId,
81877
+ error,
81878
+ cancelRequested: runtime.cancelRequested,
81879
+ abortSignal: turnAbortSignal
81226
81880
  });
81227
81881
  if (isDebugEnabled()) {
81228
81882
  console.error("[Listen] Error handling message:", error);
@@ -81588,18 +82242,24 @@ function buildDeviceStatus(runtime, params) {
81588
82242
  function buildLoopStatus(runtime, params) {
81589
82243
  const listener = getListenerRuntime(runtime);
81590
82244
  if (!listener) {
81591
- return { status: "WAITING_ON_INPUT", active_run_ids: [] };
82245
+ return {
82246
+ status: "WAITING_ON_INPUT",
82247
+ active_run_ids: [],
82248
+ plan_file_path: null
82249
+ };
81592
82250
  }
81593
82251
  const scope = getScopeForRuntime(runtime, params);
81594
82252
  const scopedAgentId = resolveScopedAgentId(listener, scope);
81595
82253
  const scopedConversationId = resolveScopedConversationId(listener, scope);
82254
+ const conversationPermissionModeState = getConversationPermissionModeState(listener, scopedAgentId, scopedConversationId);
81596
82255
  const conversationRuntime = getConversationRuntime(listener, scopedAgentId, scopedConversationId);
81597
82256
  const interruptedCacheActive = hasInterruptedCacheForScope(listener, scope);
81598
82257
  const recovered = getRecoveredApprovalStateForScope(listener, scope);
81599
82258
  const status = interruptedCacheActive ? !conversationRuntime?.isProcessing ? "WAITING_ON_INPUT" : conversationRuntime?.loopStatus === "WAITING_ON_APPROVAL" ? "WAITING_ON_INPUT" : conversationRuntime?.loopStatus ?? "WAITING_ON_INPUT" : recovered && recovered.pendingRequestIds.size > 0 && conversationRuntime?.loopStatus === "WAITING_ON_INPUT" ? "WAITING_ON_APPROVAL" : conversationRuntime?.loopStatus ?? "WAITING_ON_INPUT";
81600
82259
  return {
81601
82260
  status,
81602
- active_run_ids: interruptedCacheActive && !conversationRuntime?.isProcessing ? [] : conversationRuntime?.activeRunId ? [conversationRuntime.activeRunId] : []
82261
+ active_run_ids: interruptedCacheActive && !conversationRuntime?.isProcessing ? [] : conversationRuntime?.activeRunId ? [conversationRuntime.activeRunId] : [],
82262
+ plan_file_path: conversationPermissionModeState.mode === "plan" ? conversationPermissionModeState.planFilePath : null
81603
82263
  };
81604
82264
  }
81605
82265
  function buildQueueSnapshot(runtime, params) {
@@ -81829,7 +82489,8 @@ function emitLoopErrorDelta(socket, runtime, params) {
81829
82489
  ...createLifecycleMessageBase("loop_error", params.runId),
81830
82490
  message: params.message,
81831
82491
  stop_reason: params.stopReason,
81832
- is_terminal: params.isTerminal
82492
+ is_terminal: params.isTerminal,
82493
+ ...params.apiError ? { api_error: params.apiError } : {}
81833
82494
  }, {
81834
82495
  agent_id: params.agentId,
81835
82496
  conversation_id: params.conversationId
@@ -83072,12 +83733,13 @@ function handleModeChange(msg, socket, runtime, scope) {
83072
83733
  }
83073
83734
  } catch (error) {
83074
83735
  trackListenerError("listener_mode_change_failed", error, "listener_mode_change");
83075
- emitLoopErrorDelta(socket, runtime, {
83736
+ emitLoopErrorNotice(socket, runtime, {
83076
83737
  message: error instanceof Error ? error.message : "Mode change failed",
83077
83738
  stopReason: "error",
83078
83739
  isTerminal: false,
83079
83740
  agentId: scope?.agent_id,
83080
- conversationId: scope?.conversation_id
83741
+ conversationId: scope?.conversation_id,
83742
+ error
83081
83743
  });
83082
83744
  if (isDebugEnabled()) {
83083
83745
  console.error("[Listen] Mode change failed:", error);
@@ -83431,7 +84093,7 @@ async function handleSkillCommand(parsed, socket) {
83431
84093
  symlinkSync,
83432
84094
  unlinkSync: unlinkSync8
83433
84095
  } = await import("node:fs");
83434
- const { basename: basename4, join: join28 } = await import("node:path");
84096
+ const { basename: basename5, join: join28 } = await import("node:path");
83435
84097
  const lettaHome = process.env.LETTA_HOME || join28(process.env.HOME || process.env.USERPROFILE || "~", ".letta");
83436
84098
  const globalSkillsDir = join28(lettaHome, "skills");
83437
84099
  if (parsed.type === "skill_enable") {
@@ -83455,7 +84117,7 @@ async function handleSkillCommand(parsed, socket) {
83455
84117
  }, "listener_skill_send_failed", "listener_skill_command");
83456
84118
  return true;
83457
84119
  }
83458
- const linkName = basename4(parsed.skill_path);
84120
+ const linkName = basename5(parsed.skill_path);
83459
84121
  const linkPath = join28(globalSkillsDir, linkName);
83460
84122
  mkdirSync15(globalSkillsDir, { recursive: true });
83461
84123
  if (existsSync22(linkPath)) {
@@ -83827,6 +84489,7 @@ async function handleAbortMessageInput(listener, params, deps = {}) {
83827
84489
  }
83828
84490
  const interruptedRunId = scopedRuntime.activeRunId;
83829
84491
  scopedRuntime.cancelRequested = true;
84492
+ const pendingRequestsSnapshot = hasPendingApprovals ? resolvedDeps.getPendingControlRequests(listener, scope) : [];
83830
84493
  if (scopedRuntime.activeExecutingToolCallIds.length > 0 && (!scopedRuntime.pendingInterruptedResults || scopedRuntime.pendingInterruptedResults.length === 0)) {
83831
84494
  scopedRuntime.pendingInterruptedResults = scopedRuntime.activeExecutingToolCallIds.map((toolCallId) => ({
83832
84495
  type: "tool",
@@ -83872,9 +84535,8 @@ async function handleAbortMessageInput(listener, params, deps = {}) {
83872
84535
  agentId: scope.agent_id,
83873
84536
  conversationId: scope.conversation_id
83874
84537
  });
83875
- } else if (hasPendingApprovals) {
83876
- const pendingRequests = resolvedDeps.getPendingControlRequests(listener, scope);
83877
- scopedRuntime.pendingInterruptedResults = pendingRequests.map((req) => ({
84538
+ } else if (hasPendingApprovals && (!scopedRuntime.pendingInterruptedResults || scopedRuntime.pendingInterruptedResults.length === 0) && pendingRequestsSnapshot.length > 0) {
84539
+ scopedRuntime.pendingInterruptedResults = pendingRequestsSnapshot.map((req) => ({
83878
84540
  type: "approval",
83879
84541
  tool_call_id: req.request.tool_call_id,
83880
84542
  approve: false,
@@ -83885,6 +84547,7 @@ async function handleAbortMessageInput(listener, params, deps = {}) {
83885
84547
  conversationId: scope.conversation_id,
83886
84548
  continuationEpoch: scopedRuntime.continuationEpoch
83887
84549
  };
84550
+ scopedRuntime.pendingInterruptedToolCallIds = null;
83888
84551
  resolvedDeps.emitInterruptedStatusDelta(params.socket, scopedRuntime, {
83889
84552
  runId: interruptedRunId,
83890
84553
  agentId: scope.agent_id,
@@ -83929,12 +84592,13 @@ async function handleCwdChange(msg, socket, runtime) {
83929
84592
  conversation_id: conversationId
83930
84593
  });
83931
84594
  } catch (error) {
83932
- emitLoopErrorDelta(socket, runtime, {
84595
+ emitLoopErrorNotice(socket, runtime, {
83933
84596
  message: error instanceof Error ? error.message : "Working directory change failed",
83934
84597
  stopReason: "error",
83935
84598
  isTerminal: false,
83936
84599
  agentId,
83937
- conversationId
84600
+ conversationId,
84601
+ error
83938
84602
  });
83939
84603
  }
83940
84604
  }
@@ -84153,7 +84817,7 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
84153
84817
  return;
84154
84818
  }
84155
84819
  if (parsed.type === "__invalid_input") {
84156
- emitLoopErrorDelta(socket, runtime, {
84820
+ emitLoopErrorNotice(socket, runtime, {
84157
84821
  message: parsed.reason,
84158
84822
  stopReason: "error",
84159
84823
  isTerminal: false,
@@ -84196,7 +84860,7 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
84196
84860
  }
84197
84861
  const inputPayload = parsed.payload;
84198
84862
  if (inputPayload.kind !== "create_message") {
84199
- emitLoopErrorDelta(socket, runtime, {
84863
+ emitLoopErrorNotice(socket, runtime, {
84200
84864
  message: `Unsupported input payload kind: ${String(inputPayload.kind)}`,
84201
84865
  stopReason: "error",
84202
84866
  isTerminal: false,
@@ -84213,7 +84877,7 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
84213
84877
  };
84214
84878
  const hasApprovalPayload = incoming.messages.some((payload) => ("type" in payload) && payload.type === "approval");
84215
84879
  if (hasApprovalPayload) {
84216
- emitLoopErrorDelta(socket, runtime, {
84880
+ emitLoopErrorNotice(socket, runtime, {
84217
84881
  message: "Protocol violation: approval payloads are not allowed in input.kind=create_message. Use input.kind=approval_response.",
84218
84882
  stopReason: "error",
84219
84883
  isTerminal: false,
@@ -84770,12 +85434,13 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
84770
85434
  if (!parsedScope) {
84771
85435
  return;
84772
85436
  }
84773
- emitLoopErrorDelta(socket, runtime, {
85437
+ emitLoopErrorNotice(socket, runtime, {
84774
85438
  message: error instanceof Error ? error.message : "Failed to process listener message",
84775
85439
  stopReason: "error",
84776
85440
  isTerminal: false,
84777
85441
  agentId: parsedScope.agent_id,
84778
- conversationId: parsedScope.conversation_id
85442
+ conversationId: parsedScope.conversation_id,
85443
+ error
84779
85444
  });
84780
85445
  }
84781
85446
  });
@@ -85073,6 +85738,7 @@ var init_client4 = __esm(async () => {
85073
85738
  init_protocol_inbound(),
85074
85739
  init_protocol_outbound(),
85075
85740
  init_queue(),
85741
+ init_recoverable_notices(),
85076
85742
  init_recovery(),
85077
85743
  init_send(),
85078
85744
  init_turn(),
@@ -85171,7 +85837,7 @@ import {
85171
85837
  readFileSync as readFileSync14,
85172
85838
  unlinkSync as unlinkSync8
85173
85839
  } from "node:fs";
85174
- import { homedir as homedir25 } from "node:os";
85840
+ import { homedir as homedir26 } from "node:os";
85175
85841
  import { join as join31 } from "node:path";
85176
85842
  import { format as format2 } from "node:util";
85177
85843
  function isDebugEnabled2() {
@@ -85277,7 +85943,7 @@ function debugWarn2(prefix, message, ...args) {
85277
85943
  }
85278
85944
  var DEBUG_LOG_DIR2, MAX_SESSION_FILES3 = 5, DEFAULT_TAIL_LINES2 = 50, debugLogFile2;
85279
85945
  var init_debug2 = __esm(() => {
85280
- DEBUG_LOG_DIR2 = join31(homedir25(), ".letta", "logs", "debug");
85946
+ DEBUG_LOG_DIR2 = join31(homedir26(), ".letta", "logs", "debug");
85281
85947
  debugLogFile2 = new DebugLogFile2;
85282
85948
  });
85283
85949
 
@@ -85306,10 +85972,10 @@ __export(exports_skills2, {
85306
85972
  });
85307
85973
  import { existsSync as existsSync24 } from "node:fs";
85308
85974
  import { readdir as readdir7, readFile as readFile8, realpath as realpath4, stat as stat6 } from "node:fs/promises";
85309
- import { dirname as dirname12, join as join32 } from "node:path";
85975
+ import { dirname as dirname13, join as join32 } from "node:path";
85310
85976
  import { fileURLToPath as fileURLToPath8 } from "node:url";
85311
85977
  function getBundledSkillsPath2() {
85312
- const thisDir = dirname12(fileURLToPath8(import.meta.url));
85978
+ const thisDir = dirname13(fileURLToPath8(import.meta.url));
85313
85979
  if (thisDir.includes("src/agent") || thisDir.includes("src\\agent")) {
85314
85980
  return join32(thisDir, "../skills/builtin");
85315
85981
  }
@@ -85505,12 +86171,12 @@ import {
85505
86171
  writeFileSync as fsWriteFileSync2,
85506
86172
  mkdirSync as mkdirSync17
85507
86173
  } from "node:fs";
85508
- import { dirname as dirname13 } from "node:path";
86174
+ import { dirname as dirname14 } from "node:path";
85509
86175
  async function readFile9(path23) {
85510
86176
  return fsReadFileSync2(path23, { encoding: "utf-8" });
85511
86177
  }
85512
86178
  async function writeFile7(path23, content) {
85513
- const dir = dirname13(path23);
86179
+ const dir = dirname14(path23);
85514
86180
  if (!existsSync25(dir)) {
85515
86181
  mkdirSync17(dir, { recursive: true });
85516
86182
  }
@@ -85555,7 +86221,7 @@ async function queryTerminalBackground(timeoutMs = 100) {
85555
86221
  }
85556
86222
  const wasRaw = process.stdin.isRaw;
85557
86223
  const wasFlowing = process.stdin.readableFlowing;
85558
- return new Promise((resolve26) => {
86224
+ return new Promise((resolve25) => {
85559
86225
  let response = "";
85560
86226
  let resolved = false;
85561
86227
  const cleanup = () => {
@@ -85572,7 +86238,7 @@ async function queryTerminalBackground(timeoutMs = 100) {
85572
86238
  };
85573
86239
  const timeout = setTimeout(() => {
85574
86240
  cleanup();
85575
- resolve26(null);
86241
+ resolve25(null);
85576
86242
  }, timeoutMs);
85577
86243
  const onData = (data) => {
85578
86244
  response += data.toString();
@@ -85582,7 +86248,7 @@ async function queryTerminalBackground(timeoutMs = 100) {
85582
86248
  if (match3) {
85583
86249
  clearTimeout(timeout);
85584
86250
  cleanup();
85585
- resolve26({
86251
+ resolve25({
85586
86252
  r: parseHexComponent(match3[1] ?? "0"),
85587
86253
  g: parseHexComponent(match3[2] ?? "0"),
85588
86254
  b: parseHexComponent(match3[3] ?? "0")
@@ -85597,7 +86263,7 @@ async function queryTerminalBackground(timeoutMs = 100) {
85597
86263
  } catch {
85598
86264
  clearTimeout(timeout);
85599
86265
  cleanup();
85600
- resolve26(null);
86266
+ resolve25(null);
85601
86267
  }
85602
86268
  });
85603
86269
  }
@@ -86806,10 +87472,10 @@ __export(exports_setup, {
86806
87472
  runSetup: () => runSetup
86807
87473
  });
86808
87474
  async function runSetup() {
86809
- return new Promise((resolve27) => {
87475
+ return new Promise((resolve26) => {
86810
87476
  const { waitUntilExit } = render_default(import_react32.default.createElement(SetupUI, {
86811
87477
  onComplete: () => {
86812
- resolve27();
87478
+ resolve26();
86813
87479
  }
86814
87480
  }));
86815
87481
  waitUntilExit().catch((error) => {
@@ -87339,10 +88005,10 @@ __export(exports_import, {
87339
88005
  });
87340
88006
  import { createReadStream } from "node:fs";
87341
88007
  import { chmod, mkdir as mkdir7, readFile as readFile10, writeFile as writeFile8 } from "node:fs/promises";
87342
- import { dirname as dirname14, resolve as resolve27 } from "node:path";
88008
+ import { dirname as dirname15, resolve as resolve26 } from "node:path";
87343
88009
  async function importAgentFromFile(options) {
87344
88010
  const client = await getClient();
87345
- const resolvedPath = resolve27(options.filePath);
88011
+ const resolvedPath = resolve26(options.filePath);
87346
88012
  const file = createReadStream(resolvedPath);
87347
88013
  const importResponse = await client.agents.importFile({
87348
88014
  file,
@@ -87377,7 +88043,7 @@ async function extractSkillsFromAf(afPath, destDir) {
87377
88043
  return [];
87378
88044
  }
87379
88045
  for (const skill2 of afData.skills) {
87380
- const skillDir = resolve27(destDir, skill2.name);
88046
+ const skillDir = resolve26(destDir, skill2.name);
87381
88047
  await mkdir7(skillDir, { recursive: true });
87382
88048
  if (skill2.files) {
87383
88049
  await writeSkillFiles(skillDir, skill2.files);
@@ -87397,8 +88063,8 @@ async function writeSkillFiles(skillDir, files) {
87397
88063
  }
87398
88064
  }
87399
88065
  async function writeSkillFile(skillDir, filePath, content) {
87400
- const fullPath = resolve27(skillDir, filePath);
87401
- await mkdir7(dirname14(fullPath), { recursive: true });
88066
+ const fullPath = resolve26(skillDir, filePath);
88067
+ await mkdir7(dirname15(fullPath), { recursive: true });
87402
88068
  await writeFile8(fullPath, content, "utf-8");
87403
88069
  const isScript = filePath.startsWith("scripts/") || content.trimStart().startsWith("#!");
87404
88070
  if (isScript) {
@@ -87704,12 +88370,12 @@ async function prepareHeadlessToolExecutionContext(params) {
87704
88370
  };
87705
88371
  }
87706
88372
  async function flushAndExit(code) {
87707
- const flushWritable = (stream2) => new Promise((resolve28) => {
88373
+ const flushWritable = (stream2) => new Promise((resolve27) => {
87708
88374
  if (stream2.destroyed || stream2.writableEnded) {
87709
- resolve28();
88375
+ resolve27();
87710
88376
  return;
87711
88377
  }
87712
- stream2.write("", () => resolve28());
88378
+ stream2.write("", () => resolve27());
87713
88379
  });
87714
88380
  await Promise.allSettled([
87715
88381
  flushWritable(process.stdout),
@@ -87735,7 +88401,8 @@ async function handleHeadlessCommand(parsedArgs, model, skillsDirectoryOverride,
87735
88401
  "default",
87736
88402
  "acceptEdits",
87737
88403
  "bypassPermissions",
87738
- "plan"
88404
+ "plan",
88405
+ "memory"
87739
88406
  ];
87740
88407
  if (validModes.includes(permissionModeValue)) {
87741
88408
  permissionMode3.setMode(permissionModeValue);
@@ -88656,7 +89323,7 @@ ${loadedContents.join(`
88656
89323
  } else {
88657
89324
  console.error(`Conversation is busy, waiting ${Math.round(retryDelayMs / 1000)}s and retrying...`);
88658
89325
  }
88659
- await new Promise((resolve28) => setTimeout(resolve28, retryDelayMs));
89326
+ await new Promise((resolve27) => setTimeout(resolve27, retryDelayMs));
88660
89327
  continue;
88661
89328
  }
88662
89329
  }
@@ -88705,7 +89372,7 @@ ${loadedContents.join(`
88705
89372
  const delaySeconds = Math.round(delayMs / 1000);
88706
89373
  console.error(`Transient API error before streaming (attempt ${attempt} of ${LLM_API_ERROR_MAX_RETRIES2}), retrying in ${delaySeconds}s...`);
88707
89374
  }
88708
- await new Promise((resolve28) => setTimeout(resolve28, delayMs));
89375
+ await new Promise((resolve27) => setTimeout(resolve27, delayMs));
88709
89376
  conversationBusyRetries = 0;
88710
89377
  continue;
88711
89378
  }
@@ -88944,7 +89611,7 @@ ${loadedContents.join(`
88944
89611
  const delaySeconds = Math.round(delayMs / 1000);
88945
89612
  console.error(`LLM API error encountered (attempt ${attempt} of ${LLM_API_ERROR_MAX_RETRIES2}), retrying in ${delaySeconds}s...`);
88946
89613
  }
88947
- await new Promise((resolve28) => setTimeout(resolve28, delayMs));
89614
+ await new Promise((resolve27) => setTimeout(resolve27, delayMs));
88948
89615
  refreshCurrentInputOtids();
88949
89616
  continue;
88950
89617
  }
@@ -89030,7 +89697,7 @@ ${loadedContents.join(`
89030
89697
  } else {
89031
89698
  console.error(`Empty LLM response, retrying (attempt ${attempt} of ${EMPTY_RESPONSE_MAX_RETRIES2})...`);
89032
89699
  }
89033
- await new Promise((resolve28) => setTimeout(resolve28, delayMs));
89700
+ await new Promise((resolve27) => setTimeout(resolve27, delayMs));
89034
89701
  refreshCurrentInputOtids();
89035
89702
  continue;
89036
89703
  }
@@ -89058,7 +89725,7 @@ ${loadedContents.join(`
89058
89725
  const delaySeconds = Math.round(delayMs / 1000);
89059
89726
  console.error(`LLM API error encountered (attempt ${attempt} of ${LLM_API_ERROR_MAX_RETRIES2}), retrying in ${delaySeconds}s...`);
89060
89727
  }
89061
- await new Promise((resolve28) => setTimeout(resolve28, delayMs));
89728
+ await new Promise((resolve27) => setTimeout(resolve27, delayMs));
89062
89729
  refreshCurrentInputOtids();
89063
89730
  continue;
89064
89731
  }
@@ -89392,9 +90059,9 @@ async function runBidirectionalMode(agent, conversationId, client, _outputFormat
89392
90059
  const syntheticUserLine = serializeQueuedMessageAsUserLine(queuedMessage);
89393
90060
  maybeNotifyBlocked(syntheticUserLine);
89394
90061
  if (lineResolver) {
89395
- const resolve28 = lineResolver;
90062
+ const resolve27 = lineResolver;
89396
90063
  lineResolver = null;
89397
- resolve28(syntheticUserLine);
90064
+ resolve27(syntheticUserLine);
89398
90065
  return;
89399
90066
  }
89400
90067
  lineQueue.push(syntheticUserLine);
@@ -89402,9 +90069,9 @@ async function runBidirectionalMode(agent, conversationId, client, _outputFormat
89402
90069
  rl.on("line", (line) => {
89403
90070
  maybeNotifyBlocked(line);
89404
90071
  if (lineResolver) {
89405
- const resolve28 = lineResolver;
90072
+ const resolve27 = lineResolver;
89406
90073
  lineResolver = null;
89407
- resolve28(line);
90074
+ resolve27(line);
89408
90075
  } else {
89409
90076
  lineQueue.push(line);
89410
90077
  }
@@ -89413,17 +90080,17 @@ async function runBidirectionalMode(agent, conversationId, client, _outputFormat
89413
90080
  setMessageQueueAdder(null);
89414
90081
  msgQueueRuntime.clear("shutdown");
89415
90082
  if (lineResolver) {
89416
- const resolve28 = lineResolver;
90083
+ const resolve27 = lineResolver;
89417
90084
  lineResolver = null;
89418
- resolve28(null);
90085
+ resolve27(null);
89419
90086
  }
89420
90087
  });
89421
90088
  async function getNextLine() {
89422
90089
  if (lineQueue.length > 0) {
89423
90090
  return lineQueue.shift() ?? null;
89424
90091
  }
89425
- return new Promise((resolve28) => {
89426
- lineResolver = resolve28;
90092
+ return new Promise((resolve27) => {
90093
+ lineResolver = resolve27;
89427
90094
  });
89428
90095
  }
89429
90096
  async function requestPermission(toolCallId, toolName, toolInput) {
@@ -89927,7 +90594,7 @@ async function runBidirectionalMode(agent, conversationId, client, _outputFormat
89927
90594
  uuid: `retry-bidir-${randomUUID8()}`
89928
90595
  };
89929
90596
  console.log(JSON.stringify(retryMsg));
89930
- await new Promise((resolve28) => setTimeout(resolve28, delayMs));
90597
+ await new Promise((resolve27) => setTimeout(resolve27, delayMs));
89931
90598
  continue;
89932
90599
  }
89933
90600
  throw preStreamError;
@@ -90223,10 +90890,10 @@ async function detectAndEnableKittyProtocol() {
90223
90890
  detectionComplete = true;
90224
90891
  return;
90225
90892
  }
90226
- return new Promise((resolve28) => {
90893
+ return new Promise((resolve27) => {
90227
90894
  if (!process.stdin.isTTY || !process.stdout.isTTY) {
90228
90895
  detectionComplete = true;
90229
- resolve28();
90896
+ resolve27();
90230
90897
  return;
90231
90898
  }
90232
90899
  const originalRawMode = process.stdin.isRaw;
@@ -90259,7 +90926,7 @@ async function detectAndEnableKittyProtocol() {
90259
90926
  console.error("[kitty] protocol query unsupported; enabled anyway (best-effort)");
90260
90927
  }
90261
90928
  detectionComplete = true;
90262
- resolve28();
90929
+ resolve27();
90263
90930
  };
90264
90931
  const handleData = (data) => {
90265
90932
  if (timeoutId === undefined) {
@@ -90617,10 +91284,10 @@ __export(exports_settings, {
90617
91284
  loadProjectSettings: () => loadProjectSettings,
90618
91285
  getSetting: () => getSetting
90619
91286
  });
90620
- import { homedir as homedir28 } from "node:os";
91287
+ import { homedir as homedir29 } from "node:os";
90621
91288
  import { join as join36 } from "node:path";
90622
91289
  function getSettingsPath() {
90623
- return join36(homedir28(), ".letta", "settings.json");
91290
+ return join36(homedir29(), ".letta", "settings.json");
90624
91291
  }
90625
91292
  async function loadSettings() {
90626
91293
  const settingsPath = getSettingsPath();
@@ -106126,16 +106793,16 @@ function clipStyledSpans(spans, maxColumns) {
106126
106793
  return { spans: clipped, clipped: false };
106127
106794
  }
106128
106795
  function languageFromPath(filePath) {
106129
- const basename4 = filePath.split("/").pop() ?? filePath;
106130
- const lower = basename4.toLowerCase();
106796
+ const basename5 = filePath.split("/").pop() ?? filePath;
106797
+ const lower = basename5.toLowerCase();
106131
106798
  if (lower === "makefile")
106132
106799
  return "makefile";
106133
106800
  if (lower === "dockerfile")
106134
106801
  return "dockerfile";
106135
- const dotIdx = basename4.lastIndexOf(".");
106802
+ const dotIdx = basename5.lastIndexOf(".");
106136
106803
  if (dotIdx < 0)
106137
106804
  return;
106138
- const ext3 = basename4.slice(dotIdx + 1).toLowerCase();
106805
+ const ext3 = basename5.slice(dotIdx + 1).toLowerCase();
106139
106806
  return EXT_TO_LANG[ext3];
106140
106807
  }
106141
106808
  function colorForClassName(className, palette) {
@@ -110171,7 +110838,7 @@ var init_plan_viewer_template = () => {};
110171
110838
 
110172
110839
  // src/web/generate-plan-viewer.ts
110173
110840
  import { chmodSync as chmodSync2, existsSync as existsSync28, mkdirSync as mkdirSync20, writeFileSync as writeFileSync14 } from "node:fs";
110174
- import { homedir as homedir29 } from "node:os";
110841
+ import { homedir as homedir30 } from "node:os";
110175
110842
  import { join as join37 } from "node:path";
110176
110843
  async function generateAndOpenPlanViewer(planContent, planFilePath, options) {
110177
110844
  const data = {
@@ -110205,7 +110872,7 @@ async function generateAndOpenPlanViewer(planContent, planFilePath, options) {
110205
110872
  var VIEWERS_DIR;
110206
110873
  var init_generate_plan_viewer = __esm(() => {
110207
110874
  init_plan_viewer_template();
110208
- VIEWERS_DIR = join37(homedir29(), ".letta", "viewers");
110875
+ VIEWERS_DIR = join37(homedir30(), ".letta", "viewers");
110209
110876
  });
110210
110877
 
110211
110878
  // src/cli/components/StaticPlanApproval.tsx
@@ -112445,7 +113112,7 @@ var init_pasteRegistry = __esm(() => {
112445
113112
  import { execFileSync as execFileSync3 } from "node:child_process";
112446
113113
  import { existsSync as existsSync29, readFileSync as readFileSync16, statSync as statSync10, unlinkSync as unlinkSync10 } from "node:fs";
112447
113114
  import { tmpdir as tmpdir4 } from "node:os";
112448
- import { basename as basename4, extname as extname5, isAbsolute as isAbsolute18, join as join38, resolve as resolve28 } from "node:path";
113115
+ import { basename as basename5, extname as extname5, isAbsolute as isAbsolute19, join as join38, resolve as resolve27 } from "node:path";
112449
113116
  function countLines2(text) {
112450
113117
  return (text.match(/\r\n|\r|\n/g) || []).length + 1;
112451
113118
  }
@@ -112492,8 +113159,8 @@ function translatePasteForImages(paste) {
112492
113159
  }
112493
113160
  } catch {}
112494
113161
  }
112495
- if (!isAbsolute18(filePath))
112496
- filePath = resolve28(process.cwd(), filePath);
113162
+ if (!isAbsolute19(filePath))
113163
+ filePath = resolve27(process.cwd(), filePath);
112497
113164
  const ext3 = extname5(filePath || "").toLowerCase();
112498
113165
  if (IMAGE_EXTS.has(ext3) && existsSync29(filePath) && statSync10(filePath).isFile()) {
112499
113166
  const buf = readFileSync16(filePath);
@@ -112502,7 +113169,7 @@ function translatePasteForImages(paste) {
112502
113169
  const id = allocateImage({
112503
113170
  data: b64,
112504
113171
  mediaType: mt,
112505
- filename: basename4(filePath)
113172
+ filename: basename5(filePath)
112506
113173
  });
112507
113174
  s = `[Image #${id}]`;
112508
113175
  }
@@ -113268,8 +113935,8 @@ import {
113268
113935
  readFileSync as readFileSync17,
113269
113936
  writeFileSync as writeFileSync15
113270
113937
  } from "node:fs";
113271
- import { homedir as homedir30, platform as platform5 } from "node:os";
113272
- import { dirname as dirname15, join as join39 } from "node:path";
113938
+ import { homedir as homedir31, platform as platform5 } from "node:os";
113939
+ import { dirname as dirname16, join as join39 } from "node:path";
113273
113940
  function detectTerminalType() {
113274
113941
  if (process.env.CURSOR_TRACE_ID || process.env.CURSOR_CHANNEL) {
113275
113942
  return "cursor";
@@ -113301,7 +113968,7 @@ function getKeybindingsPath(terminal) {
113301
113968
  }[terminal];
113302
113969
  const os7 = platform5();
113303
113970
  if (os7 === "darwin") {
113304
- return join39(homedir30(), "Library", "Application Support", appName, "User", "keybindings.json");
113971
+ return join39(homedir31(), "Library", "Application Support", appName, "User", "keybindings.json");
113305
113972
  }
113306
113973
  if (os7 === "win32") {
113307
113974
  const appData = process.env.APPDATA;
@@ -113310,7 +113977,7 @@ function getKeybindingsPath(terminal) {
113310
113977
  return join39(appData, appName, "User", "keybindings.json");
113311
113978
  }
113312
113979
  if (os7 === "linux") {
113313
- return join39(homedir30(), ".config", appName, "User", "keybindings.json");
113980
+ return join39(homedir31(), ".config", appName, "User", "keybindings.json");
113314
113981
  }
113315
113982
  return null;
113316
113983
  }
@@ -113360,7 +114027,7 @@ function installKeybinding(keybindingsPath) {
113360
114027
  if (keybindingExists(keybindingsPath)) {
113361
114028
  return { success: true, alreadyExists: true };
113362
114029
  }
113363
- const parentDir = dirname15(keybindingsPath);
114030
+ const parentDir = dirname16(keybindingsPath);
113364
114031
  if (!existsSync30(parentDir)) {
113365
114032
  mkdirSync21(parentDir, { recursive: true });
113366
114033
  }
@@ -113467,10 +114134,10 @@ function getWezTermConfigPath() {
113467
114134
  if (existsSync30(xdgPath))
113468
114135
  return xdgPath;
113469
114136
  }
113470
- const configPath = join39(homedir30(), ".config", "wezterm", "wezterm.lua");
114137
+ const configPath = join39(homedir31(), ".config", "wezterm", "wezterm.lua");
113471
114138
  if (existsSync30(configPath))
113472
114139
  return configPath;
113473
- return join39(homedir30(), ".wezterm.lua");
114140
+ return join39(homedir31(), ".wezterm.lua");
113474
114141
  }
113475
114142
  function wezTermDeleteFixExists(configPath) {
113476
114143
  if (!existsSync30(configPath))
@@ -113519,7 +114186,7 @@ return config`);
113519
114186
  ${WEZTERM_DELETE_FIX}
113520
114187
  `;
113521
114188
  }
113522
- const parentDir = dirname15(configPath);
114189
+ const parentDir = dirname16(configPath);
113523
114190
  if (!existsSync30(parentDir)) {
113524
114191
  mkdirSync21(parentDir, { recursive: true });
113525
114192
  }
@@ -114102,7 +114769,7 @@ __export(exports_custom, {
114102
114769
  });
114103
114770
  import { existsSync as existsSync31 } from "node:fs";
114104
114771
  import { readdir as readdir9, readFile as readFile11 } from "node:fs/promises";
114105
- import { basename as basename5, dirname as dirname16, join as join40 } from "node:path";
114772
+ import { basename as basename6, dirname as dirname17, join as join40 } from "node:path";
114106
114773
  async function getCustomCommands() {
114107
114774
  if (cachedCommands !== null) {
114108
114775
  return cachedCommands;
@@ -114162,8 +114829,8 @@ async function findCommandFiles(currentPath, rootPath, commands2, source2) {
114162
114829
  async function parseCommandFile(filePath, rootPath, source2) {
114163
114830
  const content = await readFile11(filePath, "utf-8");
114164
114831
  const { frontmatter, body } = parseFrontmatter(content);
114165
- const id = basename5(filePath, ".md");
114166
- const relativePath = dirname16(filePath).slice(rootPath.length);
114832
+ const id = basename6(filePath, ".md");
114833
+ const relativePath = dirname17(filePath).slice(rootPath.length);
114167
114834
  const namespace = relativePath.replace(/^[/\\]/, "") || undefined;
114168
114835
  let description = getStringField(frontmatter, "description");
114169
114836
  if (!description) {
@@ -114511,12 +115178,12 @@ var init_HelpDialog = __esm(async () => {
114511
115178
  });
114512
115179
 
114513
115180
  // src/hooks/writer.ts
114514
- import { homedir as homedir31 } from "node:os";
114515
- import { resolve as resolve29 } from "node:path";
115181
+ import { homedir as homedir32 } from "node:os";
115182
+ import { resolve as resolve28 } from "node:path";
114516
115183
  function isProjectSettingsPathCollidingWithGlobal2(workingDirectory) {
114517
- const home = process.env.HOME || homedir31();
114518
- const globalSettingsPath = resolve29(home, ".letta", "settings.json");
114519
- const projectSettingsPath = resolve29(workingDirectory, ".letta", "settings.json");
115184
+ const home = process.env.HOME || homedir32();
115185
+ const globalSettingsPath = resolve28(home, ".letta", "settings.json");
115186
+ const projectSettingsPath = resolve28(workingDirectory, ".letta", "settings.json");
114520
115187
  return globalSettingsPath === projectSettingsPath;
114521
115188
  }
114522
115189
  function loadHooksFromLocation(location, workingDirectory = process.cwd()) {
@@ -117154,7 +117821,7 @@ var init_AgentInfoBar = __esm(async () => {
117154
117821
 
117155
117822
  // src/cli/helpers/fileSearch.ts
117156
117823
  import { readdirSync as readdirSync13, statSync as statSync11 } from "node:fs";
117157
- import { join as join41, relative as relative15, resolve as resolve30 } from "node:path";
117824
+ import { join as join41, relative as relative15, resolve as resolve29 } from "node:path";
117158
117825
  function searchDirectoryRecursive(dir, pattern, maxResults = 200, results = [], depth = 0, maxDepth = 10, lowerPattern = pattern.toLowerCase()) {
117159
117826
  if (results.length >= maxResults || depth >= maxDepth) {
117160
117827
  return results;
@@ -117197,7 +117864,7 @@ async function searchFiles(query, deep = false) {
117197
117864
  const dirPart = query.slice(0, lastSlashIndex);
117198
117865
  const pattern = query.slice(lastSlashIndex + 1);
117199
117866
  try {
117200
- const resolvedDir = resolve30(getIndexRoot(), dirPart);
117867
+ const resolvedDir = resolve29(getIndexRoot(), dirPart);
117201
117868
  try {
117202
117869
  statSync11(resolvedDir);
117203
117870
  searchDir = resolvedDir;
@@ -119325,7 +119992,7 @@ import {
119325
119992
  writeFileSync as writeFileSync16
119326
119993
  } from "node:fs";
119327
119994
  import { tmpdir as tmpdir5 } from "node:os";
119328
- import { dirname as dirname17, join as join42 } from "node:path";
119995
+ import { dirname as dirname18, join as join42 } from "node:path";
119329
119996
  function runCommand(command, args, cwd2, input) {
119330
119997
  try {
119331
119998
  return execFileSync4(command, args, {
@@ -119572,8 +120239,8 @@ function runGit6(args, cwd2) {
119572
120239
  }
119573
120240
  function writeWorkflow(repoDir, workflowPath, content) {
119574
120241
  const absolutePath = join42(repoDir, workflowPath);
119575
- if (!existsSync32(dirname17(absolutePath))) {
119576
- mkdirSync22(dirname17(absolutePath), { recursive: true });
120242
+ if (!existsSync32(dirname18(absolutePath))) {
120243
+ mkdirSync22(dirname18(absolutePath), { recursive: true });
119577
120244
  }
119578
120245
  const next = `${content.trimEnd()}
119579
120246
  `;
@@ -123479,7 +124146,7 @@ __export(exports_generate_memory_viewer, {
123479
124146
  });
123480
124147
  import { execFile as execFileCb5 } from "node:child_process";
123481
124148
  import { chmodSync as chmodSync3, existsSync as existsSync33, mkdirSync as mkdirSync23, writeFileSync as writeFileSync17 } from "node:fs";
123482
- import { homedir as homedir32 } from "node:os";
124149
+ import { homedir as homedir33 } from "node:os";
123483
124150
  import { join as join43 } from "node:path";
123484
124151
  import { promisify as promisify14 } from "node:util";
123485
124152
  async function runGitSafe(cwd2, args) {
@@ -123793,7 +124460,7 @@ var init_generate_memory_viewer = __esm(async () => {
123793
124460
  init_memoryGit()
123794
124461
  ]);
123795
124462
  execFile14 = promisify14(execFileCb5);
123796
- VIEWERS_DIR2 = join43(homedir32(), ".letta", "viewers");
124463
+ VIEWERS_DIR2 = join43(homedir33(), ".letta", "viewers");
123797
124464
  REFLECTION_PATTERN = /\(reflection\)|🔮|reflection:/i;
123798
124465
  });
123799
124466
 
@@ -126511,11 +127178,11 @@ var init_PersonalitySelector = __esm(async () => {
126511
127178
 
126512
127179
  // src/utils/aws-credentials.ts
126513
127180
  import { readFile as readFile12 } from "node:fs/promises";
126514
- import { homedir as homedir33 } from "node:os";
127181
+ import { homedir as homedir34 } from "node:os";
126515
127182
  import { join as join44 } from "node:path";
126516
127183
  async function parseAwsCredentials() {
126517
- const credentialsPath = join44(homedir33(), ".aws", "credentials");
126518
- const configPath = join44(homedir33(), ".aws", "config");
127184
+ const credentialsPath = join44(homedir34(), ".aws", "credentials");
127185
+ const configPath = join44(homedir34(), ".aws", "config");
126519
127186
  const profiles = new Map;
126520
127187
  try {
126521
127188
  const content = await readFile12(credentialsPath, "utf-8");
@@ -131996,7 +132663,7 @@ async function executeStatusLineCommand(command, payload, options) {
131996
132663
  };
131997
132664
  }
131998
132665
  function runWithLauncher(launcher, inputJson, timeout, signal, workingDirectory, startTime) {
131999
- return new Promise((resolve31, reject) => {
132666
+ return new Promise((resolve30, reject) => {
132000
132667
  const [executable, ...args] = launcher;
132001
132668
  if (!executable) {
132002
132669
  reject(new Error("Empty launcher"));
@@ -132009,7 +132676,7 @@ function runWithLauncher(launcher, inputJson, timeout, signal, workingDirectory,
132009
132676
  const safeResolve = (result) => {
132010
132677
  if (!resolved) {
132011
132678
  resolved = true;
132012
- resolve31(result);
132679
+ resolve30(result);
132013
132680
  }
132014
132681
  };
132015
132682
  let child;
@@ -132455,7 +133122,7 @@ __export(exports_shellAliases, {
132455
133122
  clearAliasCache: () => clearAliasCache
132456
133123
  });
132457
133124
  import { existsSync as existsSync36, readFileSync as readFileSync20 } from "node:fs";
132458
- import { homedir as homedir34 } from "node:os";
133125
+ import { homedir as homedir35 } from "node:os";
132459
133126
  import { join as join46 } from "node:path";
132460
133127
  function parseAliasesFromFile(filePath) {
132461
133128
  const aliases = new Map;
@@ -132525,7 +133192,7 @@ function loadAliases(forceReload = false) {
132525
133192
  if (aliasCache && !forceReload) {
132526
133193
  return aliasCache;
132527
133194
  }
132528
- const home = homedir34();
133195
+ const home = homedir35();
132529
133196
  const allAliases = new Map;
132530
133197
  for (const file of ALIAS_FILES) {
132531
133198
  const filePath = join46(home, file);
@@ -133186,14 +133853,14 @@ __export(exports_export, {
133186
133853
  packageSkills: () => packageSkills
133187
133854
  });
133188
133855
  import { readdir as readdir10, readFile as readFile13 } from "node:fs/promises";
133189
- import { relative as relative16, resolve as resolve31 } from "node:path";
133856
+ import { relative as relative16, resolve as resolve30 } from "node:path";
133190
133857
  async function packageSkills(agentId, skillsDir) {
133191
133858
  const skills = [];
133192
133859
  const skillNames = new Set;
133193
133860
  const dirsToCheck = skillsDir ? [skillsDir] : [
133194
133861
  agentId && getAgentSkillsDir(agentId),
133195
- resolve31(process.cwd(), ".skills"),
133196
- resolve31(process.env.HOME || "~", ".letta", "skills")
133862
+ resolve30(process.cwd(), ".skills"),
133863
+ resolve30(process.env.HOME || "~", ".letta", "skills")
133197
133864
  ].filter((dir) => Boolean(dir));
133198
133865
  for (const baseDir of dirsToCheck) {
133199
133866
  try {
@@ -133203,8 +133870,8 @@ async function packageSkills(agentId, skillsDir) {
133203
133870
  continue;
133204
133871
  if (skillNames.has(entry.name))
133205
133872
  continue;
133206
- const skillDir = resolve31(baseDir, entry.name);
133207
- const skillMdPath = resolve31(skillDir, "SKILL.md");
133873
+ const skillDir = resolve30(baseDir, entry.name);
133874
+ const skillMdPath = resolve30(skillDir, "SKILL.md");
133208
133875
  try {
133209
133876
  await readFile13(skillMdPath, "utf-8");
133210
133877
  } catch {
@@ -133234,7 +133901,7 @@ async function readSkillFiles(skillDir) {
133234
133901
  async function walk(dir) {
133235
133902
  const entries = await readdir10(dir, { withFileTypes: true });
133236
133903
  for (const entry of entries) {
133237
- const fullPath = resolve31(dir, entry.name);
133904
+ const fullPath = resolve30(dir, entry.name);
133238
133905
  if (entry.isDirectory()) {
133239
133906
  await walk(fullPath);
133240
133907
  } else {
@@ -133379,7 +134046,7 @@ __export(exports_App, {
133379
134046
  });
133380
134047
  import { randomUUID as randomUUID9 } from "node:crypto";
133381
134048
  import { existsSync as existsSync37, readFileSync as readFileSync21, renameSync as renameSync3, writeFileSync as writeFileSync18 } from "node:fs";
133382
- import { homedir as homedir35, tmpdir as tmpdir6 } from "node:os";
134049
+ import { homedir as homedir36, tmpdir as tmpdir6 } from "node:os";
133383
134050
  import { join as join47, relative as relative17 } from "node:path";
133384
134051
  function deriveReasoningEffort(modelSettings, llmConfig) {
133385
134052
  if (modelSettings && "provider_type" in modelSettings) {
@@ -135718,7 +136385,7 @@ ${newState.originalPrompt}`,
135718
136385
  cancelled = true;
135719
136386
  break;
135720
136387
  }
135721
- await new Promise((resolve32) => setTimeout(resolve32, 100));
136388
+ await new Promise((resolve31) => setTimeout(resolve31, 100));
135722
136389
  }
135723
136390
  buffersRef.current.byId.delete(statusId);
135724
136391
  buffersRef.current.order = buffersRef.current.order.filter((id) => id !== statusId);
@@ -135782,7 +136449,7 @@ ${newState.originalPrompt}`,
135782
136449
  cancelled = true;
135783
136450
  break;
135784
136451
  }
135785
- await new Promise((resolve32) => setTimeout(resolve32, 100));
136452
+ await new Promise((resolve31) => setTimeout(resolve31, 100));
135786
136453
  }
135787
136454
  if (retryStatusId) {
135788
136455
  buffersRef.current.byId.delete(retryStatusId);
@@ -136540,7 +137207,7 @@ ${feedback}
136540
137207
  });
136541
137208
  buffersRef.current.order.push(statusId);
136542
137209
  refreshDerived();
136543
- await new Promise((resolve32) => setTimeout(resolve32, delayMs));
137210
+ await new Promise((resolve31) => setTimeout(resolve31, delayMs));
136544
137211
  buffersRef.current.byId.delete(statusId);
136545
137212
  buffersRef.current.order = buffersRef.current.order.filter((id) => id !== statusId);
136546
137213
  refreshDerived();
@@ -136600,7 +137267,7 @@ ${feedback}
136600
137267
  cancelled = true;
136601
137268
  break;
136602
137269
  }
136603
- await new Promise((resolve32) => setTimeout(resolve32, 100));
137270
+ await new Promise((resolve31) => setTimeout(resolve31, 100));
136604
137271
  }
136605
137272
  if (retryStatusId) {
136606
137273
  buffersRef.current.byId.delete(retryStatusId);
@@ -141543,7 +142210,7 @@ ${guidance}`);
141543
142210
  }
141544
142211
  if (mode === "bypassPermissions") {
141545
142212
  const planFilePath = activePlanPath ?? fallbackPlanPath;
141546
- const plansDir = join47(homedir35(), ".letta", "plans");
142213
+ const plansDir = join47(homedir36(), ".letta", "plans");
141547
142214
  handlePlanKeepPlanning(`You must write your plan to a plan file before exiting plan mode.
141548
142215
  ` + (planFilePath ? `Plan file path: ${planFilePath}
141549
142216
  ` : "") + `Use a write tool to create your plan in ${plansDir}, then use ExitPlanMode to present the plan to the user.`);
@@ -141578,7 +142245,7 @@ ${guidance}`);
141578
142245
  if (!hasUsablePlan) {
141579
142246
  lastAutoHandledExitPlanToolCallIdRef.current = approval.toolCallId;
141580
142247
  const planFilePath = activePlanPath ?? fallbackPlanPath;
141581
- const plansDir = join47(homedir35(), ".letta", "plans");
142248
+ const plansDir = join47(homedir36(), ".letta", "plans");
141582
142249
  handlePlanKeepPlanning(`You must write your plan to a plan file before exiting plan mode.
141583
142250
  ` + (planFilePath ? `Plan file path: ${planFilePath}
141584
142251
  ` : "") + `Use a write tool to create your plan in ${plansDir}, then use ExitPlanMode to present the plan to the user.`);
@@ -142990,8 +143657,8 @@ import {
142990
143657
  readFileSync as readFileSync22,
142991
143658
  writeFileSync as writeFileSync19
142992
143659
  } from "node:fs";
142993
- import { homedir as homedir36, platform as platform6 } from "node:os";
142994
- import { dirname as dirname18, join as join48 } from "node:path";
143660
+ import { homedir as homedir37, platform as platform6 } from "node:os";
143661
+ import { dirname as dirname19, join as join48 } from "node:path";
142995
143662
  function detectTerminalType2() {
142996
143663
  if (process.env.CURSOR_TRACE_ID || process.env.CURSOR_CHANNEL) {
142997
143664
  return "cursor";
@@ -143023,7 +143690,7 @@ function getKeybindingsPath2(terminal) {
143023
143690
  }[terminal];
143024
143691
  const os8 = platform6();
143025
143692
  if (os8 === "darwin") {
143026
- return join48(homedir36(), "Library", "Application Support", appName, "User", "keybindings.json");
143693
+ return join48(homedir37(), "Library", "Application Support", appName, "User", "keybindings.json");
143027
143694
  }
143028
143695
  if (os8 === "win32") {
143029
143696
  const appData = process.env.APPDATA;
@@ -143032,7 +143699,7 @@ function getKeybindingsPath2(terminal) {
143032
143699
  return join48(appData, appName, "User", "keybindings.json");
143033
143700
  }
143034
143701
  if (os8 === "linux") {
143035
- return join48(homedir36(), ".config", appName, "User", "keybindings.json");
143702
+ return join48(homedir37(), ".config", appName, "User", "keybindings.json");
143036
143703
  }
143037
143704
  return null;
143038
143705
  }
@@ -143082,7 +143749,7 @@ function installKeybinding2(keybindingsPath) {
143082
143749
  if (keybindingExists2(keybindingsPath)) {
143083
143750
  return { success: true, alreadyExists: true };
143084
143751
  }
143085
- const parentDir = dirname18(keybindingsPath);
143752
+ const parentDir = dirname19(keybindingsPath);
143086
143753
  if (!existsSync38(parentDir)) {
143087
143754
  mkdirSync24(parentDir, { recursive: true });
143088
143755
  }
@@ -143189,10 +143856,10 @@ function getWezTermConfigPath2() {
143189
143856
  if (existsSync38(xdgPath))
143190
143857
  return xdgPath;
143191
143858
  }
143192
- const configPath = join48(homedir36(), ".config", "wezterm", "wezterm.lua");
143859
+ const configPath = join48(homedir37(), ".config", "wezterm", "wezterm.lua");
143193
143860
  if (existsSync38(configPath))
143194
143861
  return configPath;
143195
- return join48(homedir36(), ".wezterm.lua");
143862
+ return join48(homedir37(), ".wezterm.lua");
143196
143863
  }
143197
143864
  function wezTermDeleteFixExists2(configPath) {
143198
143865
  if (!existsSync38(configPath))
@@ -143241,7 +143908,7 @@ return config`);
143241
143908
  ${WEZTERM_DELETE_FIX2}
143242
143909
  `;
143243
143910
  }
143244
- const parentDir = dirname18(configPath);
143911
+ const parentDir = dirname19(configPath);
143245
143912
  if (!existsSync38(parentDir)) {
143246
143913
  mkdirSync24(parentDir, { recursive: true });
143247
143914
  }
@@ -143290,10 +143957,10 @@ __export(exports_settings2, {
143290
143957
  loadProjectSettings: () => loadProjectSettings2,
143291
143958
  getSetting: () => getSetting2
143292
143959
  });
143293
- import { homedir as homedir37 } from "node:os";
143960
+ import { homedir as homedir38 } from "node:os";
143294
143961
  import { join as join49 } from "node:path";
143295
143962
  function getSettingsPath2() {
143296
- return join49(homedir37(), ".letta", "settings.json");
143963
+ return join49(homedir38(), ".letta", "settings.json");
143297
143964
  }
143298
143965
  async function loadSettings2() {
143299
143966
  const settingsPath = getSettingsPath2();
@@ -143815,10 +144482,10 @@ __export(exports_import2, {
143815
144482
  });
143816
144483
  import { createReadStream as createReadStream2 } from "node:fs";
143817
144484
  import { chmod as chmod2, mkdir as mkdir8, readFile as readFile14, writeFile as writeFile9 } from "node:fs/promises";
143818
- import { dirname as dirname19, resolve as resolve32 } from "node:path";
144485
+ import { dirname as dirname20, resolve as resolve31 } from "node:path";
143819
144486
  async function importAgentFromFile2(options) {
143820
144487
  const client = await getClient();
143821
- const resolvedPath = resolve32(options.filePath);
144488
+ const resolvedPath = resolve31(options.filePath);
143822
144489
  const file = createReadStream2(resolvedPath);
143823
144490
  const importResponse = await client.agents.importFile({
143824
144491
  file,
@@ -143853,7 +144520,7 @@ async function extractSkillsFromAf2(afPath, destDir) {
143853
144520
  return [];
143854
144521
  }
143855
144522
  for (const skill2 of afData.skills) {
143856
- const skillDir = resolve32(destDir, skill2.name);
144523
+ const skillDir = resolve31(destDir, skill2.name);
143857
144524
  await mkdir8(skillDir, { recursive: true });
143858
144525
  if (skill2.files) {
143859
144526
  await writeSkillFiles2(skillDir, skill2.files);
@@ -143873,8 +144540,8 @@ async function writeSkillFiles2(skillDir, files) {
143873
144540
  }
143874
144541
  }
143875
144542
  async function writeSkillFile2(skillDir, filePath, content) {
143876
- const fullPath = resolve32(skillDir, filePath);
143877
- await mkdir8(dirname19(fullPath), { recursive: true });
144543
+ const fullPath = resolve31(skillDir, filePath);
144544
+ await mkdir8(dirname20(fullPath), { recursive: true });
143878
144545
  await writeFile9(fullPath, content, "utf-8");
143879
144546
  const isScript = filePath.startsWith("scripts/") || content.trimStart().startsWith("#!");
143880
144547
  if (isScript) {
@@ -143984,15 +144651,15 @@ __export(exports_memoryFilesystem2, {
143984
144651
  MEMORY_FS_AGENTS_DIR: () => MEMORY_FS_AGENTS_DIR2
143985
144652
  });
143986
144653
  import { existsSync as existsSync39, mkdirSync as mkdirSync25 } from "node:fs";
143987
- import { homedir as homedir38 } from "node:os";
144654
+ import { homedir as homedir39 } from "node:os";
143988
144655
  import { join as join50 } from "node:path";
143989
- function getMemoryFilesystemRoot2(agentId, homeDir = homedir38()) {
144656
+ function getMemoryFilesystemRoot2(agentId, homeDir = homedir39()) {
143990
144657
  return join50(homeDir, MEMORY_FS_ROOT2, MEMORY_FS_AGENTS_DIR2, agentId, MEMORY_FS_MEMORY_DIR2);
143991
144658
  }
143992
- function getMemorySystemDir2(agentId, homeDir = homedir38()) {
144659
+ function getMemorySystemDir2(agentId, homeDir = homedir39()) {
143993
144660
  return join50(getMemoryFilesystemRoot2(agentId, homeDir), MEMORY_SYSTEM_DIR2);
143994
144661
  }
143995
- function ensureMemoryFilesystemDirs2(agentId, homeDir = homedir38()) {
144662
+ function ensureMemoryFilesystemDirs2(agentId, homeDir = homedir39()) {
143996
144663
  const root = getMemoryFilesystemRoot2(agentId, homeDir);
143997
144664
  const systemDir = getMemorySystemDir2(agentId, homeDir);
143998
144665
  if (!existsSync39(root)) {
@@ -148638,7 +149305,7 @@ async function runListenSubcommand(argv) {
148638
149305
  await init_memoryGit();
148639
149306
  import { cpSync, existsSync as existsSync22, mkdirSync as mkdirSync15, rmSync as rmSync2, statSync as statSync8 } from "node:fs";
148640
149307
  import { readdir as readdir6 } from "node:fs/promises";
148641
- import { homedir as homedir22 } from "node:os";
149308
+ import { homedir as homedir23 } from "node:os";
148642
149309
  import { join as join28 } from "node:path";
148643
149310
  import { parseArgs as parseArgs7 } from "node:util";
148644
149311
  function printUsage4() {
@@ -148684,10 +149351,10 @@ function parseMemfsArgs(argv) {
148684
149351
  });
148685
149352
  }
148686
149353
  function getMemoryRoot(agentId) {
148687
- return join28(homedir22(), ".letta", "agents", agentId, "memory");
149354
+ return join28(homedir23(), ".letta", "agents", agentId, "memory");
148688
149355
  }
148689
149356
  function getAgentRoot(agentId) {
148690
- return join28(homedir22(), ".letta", "agents", agentId);
149357
+ return join28(homedir23(), ".letta", "agents", agentId);
148691
149358
  }
148692
149359
  function formatBackupTimestamp(date = new Date) {
148693
149360
  const pad = (value) => String(value).padStart(2, "0");
@@ -149236,13 +149903,20 @@ async function runSubcommand(argv) {
149236
149903
  }
149237
149904
 
149238
149905
  // src/permissions/mode.ts
149906
+ init_memoryScope();
149239
149907
  init_readOnlyShell();
149240
149908
  init_shell_command_normalization();
149241
- import { homedir as homedir23 } from "node:os";
149242
- import { isAbsolute as isAbsolute16, join as join29, relative as relative12, resolve as resolve24 } from "node:path";
149909
+ import { homedir as homedir24 } from "node:os";
149910
+ import { isAbsolute as isAbsolute17, join as join29, relative as relative12 } from "node:path";
149243
149911
  var MODE_KEY2 = Symbol.for("@letta/permissionMode");
149244
149912
  var PLAN_FILE_KEY2 = Symbol.for("@letta/planFilePath");
149245
149913
  var MODE_BEFORE_PLAN_KEY2 = Symbol.for("@letta/permissionModeBeforePlan");
149914
+ function everyResolvedTargetIsWithinRoots2(candidatePaths, roots, workingDirectory) {
149915
+ return candidatePaths.length > 0 && candidatePaths.every((path23) => {
149916
+ const resolvedPath = resolveScopedTargetPath(path23, workingDirectory);
149917
+ return resolvedPath ? isPathWithinRoots(resolvedPath, roots) : false;
149918
+ });
149919
+ }
149246
149920
  function getGlobalMode2() {
149247
149921
  const global2 = globalThis;
149248
149922
  if (!global2[MODE_KEY2]) {
@@ -149271,22 +149945,13 @@ function setGlobalModeBeforePlan2(value) {
149271
149945
  global2[MODE_BEFORE_PLAN_KEY2] = value;
149272
149946
  }
149273
149947
  function resolvePlanTargetPath2(targetPath, workingDirectory) {
149274
- const trimmedPath = targetPath.trim();
149275
- if (!trimmedPath)
149276
- return null;
149277
- if (trimmedPath.startsWith("~/")) {
149278
- return resolve24(homedir23(), trimmedPath.slice(2));
149279
- }
149280
- if (isAbsolute16(trimmedPath)) {
149281
- return resolve24(trimmedPath);
149282
- }
149283
- return resolve24(workingDirectory, trimmedPath);
149948
+ return resolveScopedTargetPath(targetPath, workingDirectory);
149284
149949
  }
149285
149950
  function isPathInPlansDir2(path23, plansDir) {
149286
149951
  if (!path23.endsWith(".md"))
149287
149952
  return false;
149288
149953
  const rel = relative12(plansDir, path23);
149289
- return rel !== "" && !rel.startsWith("..") && !isAbsolute16(rel);
149954
+ return rel !== "" && !rel.startsWith("..") && !isAbsolute17(rel);
149290
149955
  }
149291
149956
  function extractApplyPatchPaths2(input) {
149292
149957
  const paths = [];
@@ -149473,7 +150138,7 @@ class PermissionModeManager2 {
149473
150138
  return "allow";
149474
150139
  }
149475
150140
  if (writeTools.includes(toolName)) {
149476
- const plansDir = join29(homedir23(), ".letta", "plans");
150141
+ const plansDir = join29(homedir24(), ".letta", "plans");
149477
150142
  const targetPath = toolArgs?.file_path || toolArgs?.path;
149478
150143
  let candidatePaths = [];
149479
150144
  if ((toolName === "ApplyPatch" || toolName === "apply_patch" || toolName === "memory_apply_patch") && toolArgs?.input) {
@@ -149524,7 +150189,7 @@ class PermissionModeManager2 {
149524
150189
  }
149525
150190
  const planWritePath = extractPlanFileWritePathFromShellCommand2(command);
149526
150191
  if (planWritePath) {
149527
- const plansDir = join29(homedir23(), ".letta", "plans");
150192
+ const plansDir = join29(homedir24(), ".letta", "plans");
149528
150193
  const resolvedPath = resolvePlanTargetPath2(planWritePath, workingDirectory);
149529
150194
  if (resolvedPath && isPathInPlansDir2(resolvedPath, plansDir)) {
149530
150195
  return "allow";
@@ -149533,6 +150198,94 @@ class PermissionModeManager2 {
149533
150198
  }
149534
150199
  return "deny";
149535
150200
  }
150201
+ case "memory": {
150202
+ const allowedMemoryRoots = resolveAllowedMemoryRoots().roots;
150203
+ const allowedReadOnlyTools = [
150204
+ "Read",
150205
+ "Glob",
150206
+ "Grep",
150207
+ "NotebookRead",
150208
+ "ViewImage",
150209
+ "view_image",
150210
+ "TaskOutput",
150211
+ "task_output",
150212
+ "Skill",
150213
+ "skill",
150214
+ "read_file",
150215
+ "list_dir",
150216
+ "grep_files",
150217
+ "ReadFile",
150218
+ "ListDir",
150219
+ "GrepFiles",
150220
+ "read_file_gemini",
150221
+ "glob_gemini",
150222
+ "list_directory",
150223
+ "search_file_content",
150224
+ "read_many_files",
150225
+ "ReadFileGemini",
150226
+ "GlobGemini",
150227
+ "ListDirectory",
150228
+ "SearchFileContent",
150229
+ "ReadManyFiles"
150230
+ ];
150231
+ const writeTools = [
150232
+ "Write",
150233
+ "Edit",
150234
+ "MultiEdit",
150235
+ "NotebookEdit",
150236
+ "apply_patch",
150237
+ "ApplyPatch",
150238
+ "replace",
150239
+ "Replace",
150240
+ "write_file",
150241
+ "WriteFile",
150242
+ "write_file_gemini",
150243
+ "WriteFileGemini"
150244
+ ];
150245
+ const shellTools = [
150246
+ "Bash",
150247
+ "shell",
150248
+ "Shell",
150249
+ "shell_command",
150250
+ "ShellCommand",
150251
+ "run_shell_command",
150252
+ "RunShellCommand",
150253
+ "run_shell_command_gemini",
150254
+ "RunShellCommandGemini"
150255
+ ];
150256
+ if (allowedReadOnlyTools.includes(toolName)) {
150257
+ return "allow";
150258
+ }
150259
+ if (toolName === "memory_apply_patch") {
150260
+ return allowedMemoryRoots.length > 0 ? "allow" : "deny";
150261
+ }
150262
+ if (writeTools.includes(toolName)) {
150263
+ const targetPath = toolArgs?.file_path || toolArgs?.path;
150264
+ let candidatePaths = [];
150265
+ if ((toolName === "ApplyPatch" || toolName === "apply_patch") && toolArgs?.input) {
150266
+ candidatePaths = extractApplyPatchPaths2(toolArgs.input);
150267
+ } else if (typeof targetPath === "string") {
150268
+ candidatePaths = [targetPath];
150269
+ }
150270
+ if (allowedMemoryRoots.length > 0 && everyResolvedTargetIsWithinRoots2(candidatePaths, allowedMemoryRoots, workingDirectory)) {
150271
+ return "allow";
150272
+ }
150273
+ return "deny";
150274
+ }
150275
+ if (shellTools.includes(toolName)) {
150276
+ const command = toolArgs?.command;
150277
+ if (command && isReadOnlyShellCommand(command, { allowExternalPaths: true })) {
150278
+ return "allow";
150279
+ }
150280
+ if (command && allowedMemoryRoots.length > 0 && isScopedMemoryShellCommand(command, allowedMemoryRoots, {
150281
+ workingDirectory
150282
+ })) {
150283
+ return "allow";
150284
+ }
150285
+ return "deny";
150286
+ }
150287
+ return "deny";
150288
+ }
149536
150289
  case "default":
149537
150290
  return null;
149538
150291
  default:
@@ -149555,8 +150308,8 @@ await __promiseAll([
149555
150308
  init_secrets()
149556
150309
  ]);
149557
150310
  import { randomUUID as randomUUID4 } from "node:crypto";
149558
- import { homedir as homedir24 } from "node:os";
149559
- import { join as join30, resolve as resolve25 } from "node:path";
150311
+ import { homedir as homedir25 } from "node:os";
150312
+ import { join as join30, resolve as resolve24 } from "node:path";
149560
150313
  var DEFAULT_SETTINGS3 = {
149561
150314
  lastAgent: null,
149562
150315
  tokenStreaming: false,
@@ -149942,7 +150695,7 @@ class SettingsManager2 {
149942
150695
  if (!this.settings)
149943
150696
  return;
149944
150697
  const settingsPath = this.getSettingsPath();
149945
- const home = process.env.HOME || homedir24();
150698
+ const home = process.env.HOME || homedir25();
149946
150699
  const dirPath = join30(home, ".letta");
149947
150700
  try {
149948
150701
  if (!exists(dirPath)) {
@@ -150003,14 +150756,14 @@ class SettingsManager2 {
150003
150756
  }
150004
150757
  }
150005
150758
  getSettingsPath() {
150006
- const home = process.env.HOME || homedir24();
150759
+ const home = process.env.HOME || homedir25();
150007
150760
  return join30(home, ".letta", "settings.json");
150008
150761
  }
150009
150762
  getProjectSettingsPath(workingDirectory) {
150010
150763
  return join30(workingDirectory, ".letta", "settings.json");
150011
150764
  }
150012
150765
  isProjectSettingsPathCollidingWithGlobal(workingDirectory) {
150013
- return resolve25(this.getProjectSettingsPath(workingDirectory)) === resolve25(this.getSettingsPath());
150766
+ return resolve24(this.getProjectSettingsPath(workingDirectory)) === resolve24(this.getSettingsPath());
150014
150767
  }
150015
150768
  getLocalProjectSettingsPath(workingDirectory) {
150016
150769
  return join30(workingDirectory, ".letta", "settings.local.json");
@@ -150972,8 +151725,8 @@ function acquireSwitchLock2() {
150972
151725
  const lock = getSwitchLock2();
150973
151726
  lock.refCount++;
150974
151727
  if (lock.refCount === 1) {
150975
- lock.promise = new Promise((resolve26) => {
150976
- lock.resolve = resolve26;
151728
+ lock.promise = new Promise((resolve25) => {
151729
+ lock.resolve = resolve25;
150977
151730
  });
150978
151731
  }
150979
151732
  }
@@ -151458,7 +152211,7 @@ Note: Flags should use double dashes for full names (e.g., --yolo, not -yolo)`);
151458
152211
  printHelp();
151459
152212
  const helpDelayMs = Number.parseInt(process.env.LETTA_TEST_HELP_EXIT_DELAY_MS ?? "", 10);
151460
152213
  if (Number.isFinite(helpDelayMs) && helpDelayMs > 0) {
151461
- await new Promise((resolve33) => setTimeout(resolve33, helpDelayMs));
152214
+ await new Promise((resolve32) => setTimeout(resolve32, helpDelayMs));
151462
152215
  }
151463
152216
  process.exit(0);
151464
152217
  }
@@ -151680,9 +152433,9 @@ Note: Flags should use double dashes for full names (e.g., --yolo, not -yolo)`);
151680
152433
  process.exit(1);
151681
152434
  }
151682
152435
  } else {
151683
- const { resolve: resolve33 } = await import("path");
152436
+ const { resolve: resolve32 } = await import("path");
151684
152437
  const { existsSync: existsSync40 } = await import("fs");
151685
- const resolvedPath = resolve33(fromAfFile);
152438
+ const resolvedPath = resolve32(fromAfFile);
151686
152439
  if (!existsSync40(resolvedPath)) {
151687
152440
  console.error(`Error: AgentFile not found: ${resolvedPath}`);
151688
152441
  process.exit(1);
@@ -151793,6 +152546,7 @@ Error: ${message}`);
151793
152546
  "default",
151794
152547
  "acceptEdits",
151795
152548
  "plan",
152549
+ "memory",
151796
152550
  "bypassPermissions"
151797
152551
  ];
151798
152552
  if (validModes.includes(mode)) {
@@ -152557,4 +153311,4 @@ Error during initialization: ${message}`);
152557
153311
  }
152558
153312
  main();
152559
153313
 
152560
- //# debugId=275910C1663139CA64756E2164756E21
153314
+ //# debugId=1C3BE0588811294864756E2164756E21