@letta-ai/letta-code 0.21.11 → 0.21.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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.11",
3272
+ version: "0.21.13",
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.
@@ -40496,7 +40508,7 @@ import {
40496
40508
  mkdirSync as mkdirSync8,
40497
40509
  readFileSync as readFileSync7,
40498
40510
  renameSync as renameSync2,
40499
- rmdirSync,
40511
+ rmSync as rmSync2,
40500
40512
  statSync as statSync2,
40501
40513
  writeFileSync as writeFileSync6
40502
40514
  } from "node:fs";
@@ -40574,7 +40586,7 @@ function isLockStale(lockDir) {
40574
40586
  }
40575
40587
  function stealLock(lockDir) {
40576
40588
  try {
40577
- rmdirSync(lockDir, { recursive: true });
40589
+ rmSync2(lockDir, { recursive: true, force: true });
40578
40590
  } catch {}
40579
40591
  }
40580
40592
  function acquireLock() {
@@ -40595,7 +40607,7 @@ function acquireLock() {
40595
40607
  try {
40596
40608
  const current = readLockOwner(lockDir);
40597
40609
  if (current && current.token === token) {
40598
- rmdirSync(lockDir, { recursive: true });
40610
+ rmSync2(lockDir, { recursive: true, force: true });
40599
40611
  }
40600
40612
  } catch {}
40601
40613
  }
@@ -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, "/");
@@ -64948,7 +65411,7 @@ async function runProcess(context3) {
64948
65411
  }
64949
65412
  async function shell(args) {
64950
65413
  validateRequiredParams(args, ["command"], "shell");
64951
- const { command, workdir, timeout_ms, signal, onOutput } = args;
65414
+ const { command, workdir, timeout_ms, env_overrides, signal, onOutput } = args;
64952
65415
  if (!Array.isArray(command) || command.length === 0) {
64953
65416
  throw new Error("command must be a non-empty array of strings");
64954
65417
  }
@@ -64959,7 +65422,10 @@ async function shell(args) {
64959
65422
  const context3 = {
64960
65423
  command,
64961
65424
  cwd: cwd2,
64962
- env: getShellEnv(),
65425
+ env: {
65426
+ ...getShellEnv(),
65427
+ ...env_overrides ?? {}
65428
+ },
64963
65429
  timeout,
64964
65430
  signal,
64965
65431
  onOutput
@@ -65080,6 +65546,7 @@ async function shell_command(args) {
65080
65546
  signal,
65081
65547
  onOutput
65082
65548
  } = args;
65549
+ const envOverrides = getMemoryGitIdentityEnvOverrides(command, workdir);
65083
65550
  const launchers = buildShellLaunchers(command, { login });
65084
65551
  if (launchers.length === 0) {
65085
65552
  throw new Error("Command must be a non-empty string");
@@ -65091,6 +65558,7 @@ async function shell_command(args) {
65091
65558
  return await shell({
65092
65559
  command: launcher,
65093
65560
  workdir,
65561
+ env_overrides: envOverrides,
65094
65562
  timeout_ms,
65095
65563
  justification,
65096
65564
  signal,
@@ -65109,7 +65577,96 @@ async function shell_command(args) {
65109
65577
  const reason = lastError?.message || "Shell unavailable";
65110
65578
  throw new Error(suffix ? `${reason} (tried: ${suffix})` : reason);
65111
65579
  }
65580
+ function getMemoryGitIdentityEnvOverrides(command, workdir) {
65581
+ const agentId = getCurrentAgentIdOrEnv();
65582
+ if (!agentId) {
65583
+ return;
65584
+ }
65585
+ if (!containsGitCommitInvocation(command)) {
65586
+ return;
65587
+ }
65588
+ const scopedToMemoryDir = isMemoryDirCommand(command, agentId) || (workdir ? isMemoryDirCommand(`cd ${shellQuote(workdir)} && ${command}`, agentId) : false);
65589
+ if (!scopedToMemoryDir) {
65590
+ return;
65591
+ }
65592
+ const agentName = (process.env.AGENT_NAME || "").trim() || agentId;
65593
+ const agentEmail = `${agentId}@letta.com`;
65594
+ return {
65595
+ GIT_AUTHOR_NAME: agentName,
65596
+ GIT_AUTHOR_EMAIL: agentEmail,
65597
+ GIT_COMMITTER_NAME: agentName,
65598
+ GIT_COMMITTER_EMAIL: agentEmail
65599
+ };
65600
+ }
65601
+ function getCurrentAgentIdOrEnv() {
65602
+ const envAgentId = (process.env.AGENT_ID || process.env.LETTA_AGENT_ID || "").trim();
65603
+ if (envAgentId) {
65604
+ return envAgentId;
65605
+ }
65606
+ try {
65607
+ const agentId = getCurrentAgentId().trim();
65608
+ if (agentId) {
65609
+ return agentId;
65610
+ }
65611
+ } catch {}
65612
+ return "";
65613
+ }
65614
+ function containsGitCommitInvocation(command) {
65615
+ const segments = command.split(/&&|\|\||;|\|/).map((segment) => segment.trim()).filter(Boolean);
65616
+ for (const segment of segments) {
65617
+ const tokens = tokenizeSegment(segment);
65618
+ if (tokens.length === 0) {
65619
+ continue;
65620
+ }
65621
+ let index = 0;
65622
+ while (index < tokens.length && /^[A-Za-z_][A-Za-z0-9_]*=.*/.test(tokens[index] ?? "")) {
65623
+ index += 1;
65624
+ }
65625
+ if (tokens[index] !== "git") {
65626
+ continue;
65627
+ }
65628
+ index += 1;
65629
+ while (index < tokens.length) {
65630
+ const token = tokens[index];
65631
+ if (!token) {
65632
+ index += 1;
65633
+ continue;
65634
+ }
65635
+ if (token === "-c" || token === "-C") {
65636
+ index += 2;
65637
+ continue;
65638
+ }
65639
+ if (token.startsWith("-")) {
65640
+ index += 1;
65641
+ continue;
65642
+ }
65643
+ if (token === "commit") {
65644
+ return true;
65645
+ }
65646
+ break;
65647
+ }
65648
+ }
65649
+ return false;
65650
+ }
65651
+ function tokenizeSegment(segment) {
65652
+ const matches = segment.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g);
65653
+ if (!matches) {
65654
+ return [];
65655
+ }
65656
+ return matches.map(stripWrappingQuotes);
65657
+ }
65658
+ function stripWrappingQuotes(token) {
65659
+ if (token.startsWith('"') && token.endsWith('"') || token.startsWith("'") && token.endsWith("'")) {
65660
+ return token.slice(1, -1);
65661
+ }
65662
+ return token;
65663
+ }
65664
+ function shellQuote(value) {
65665
+ return `'${value.replaceAll("'", `'"'"'`)}'`;
65666
+ }
65112
65667
  var init_ShellCommand2 = __esm(async () => {
65668
+ init_context();
65669
+ init_readOnlyShell();
65113
65670
  init_shellRunner();
65114
65671
  await init_Shell2();
65115
65672
  });
@@ -65158,10 +65715,10 @@ __export(exports_skills, {
65158
65715
  });
65159
65716
  import { existsSync as existsSync17 } from "node:fs";
65160
65717
  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";
65718
+ import { dirname as dirname9, join as join19 } from "node:path";
65162
65719
  import { fileURLToPath as fileURLToPath7 } from "node:url";
65163
65720
  function getBundledSkillsPath() {
65164
- const thisDir = dirname8(fileURLToPath7(import.meta.url));
65721
+ const thisDir = dirname9(fileURLToPath7(import.meta.url));
65165
65722
  if (thisDir.includes("src/agent") || thisDir.includes("src\\agent")) {
65166
65723
  return join19(thisDir, "../skills/builtin");
65167
65724
  }
@@ -65363,10 +65920,21 @@ var init_skillContentRegistry = __esm(() => {
65363
65920
  // src/tools/impl/Skill.ts
65364
65921
  import { readdirSync as readdirSync7 } from "node:fs";
65365
65922
  import { readFile as readFile6 } from "node:fs/promises";
65366
- import { dirname as dirname9, join as join20 } from "node:path";
65923
+ import { dirname as dirname10, join as join20 } from "node:path";
65924
+ function getMemorySkillsDirs(agentId) {
65925
+ const dirs = new Set;
65926
+ const memoryDir = process.env.MEMORY_DIR || process.env.LETTA_MEMORY_DIR;
65927
+ if (memoryDir && memoryDir.trim().length > 0) {
65928
+ dirs.add(join20(memoryDir.trim(), "skills"));
65929
+ }
65930
+ if (agentId) {
65931
+ dirs.add(join20(process.env.HOME || process.env.USERPROFILE || "~", ".letta/agents", agentId, "memory", "skills"));
65932
+ }
65933
+ return Array.from(dirs);
65934
+ }
65367
65935
  function hasAdditionalFiles(skillMdPath) {
65368
65936
  try {
65369
- const skillDir = dirname9(skillMdPath);
65937
+ const skillDir = dirname10(skillMdPath);
65370
65938
  const entries = readdirSync7(skillDir);
65371
65939
  return entries.some((e) => e.toUpperCase() !== "SKILL.MD");
65372
65940
  } catch {
@@ -65386,6 +65954,13 @@ async function readSkillContent(skillId, skillsDir, agentId) {
65386
65954
  return { content, path: agentSkillPath };
65387
65955
  } catch {}
65388
65956
  }
65957
+ for (const memorySkillsDir of getMemorySkillsDirs(agentId)) {
65958
+ const memorySkillPath = join20(memorySkillsDir, skillId, "SKILL.md");
65959
+ try {
65960
+ const content = await readFile6(memorySkillPath, "utf-8");
65961
+ return { content, path: memorySkillPath };
65962
+ } catch {}
65963
+ }
65389
65964
  const globalSkillPath = join20(GLOBAL_SKILLS_DIR, skillId, "SKILL.md");
65390
65965
  try {
65391
65966
  const content = await readFile6(globalSkillPath, "utf-8");
@@ -65425,7 +66000,7 @@ async function skill(args) {
65425
66000
  const agentId = getCurrentAgentId();
65426
66001
  const skillsDir = await getResolvedSkillsDir();
65427
66002
  const { content: skillContent, path: skillPath } = await readSkillContent(skillName, skillsDir, agentId);
65428
- const skillDir = dirname9(skillPath);
66003
+ const skillDir = dirname10(skillPath);
65429
66004
  const hasExtras = hasAdditionalFiles(skillPath);
65430
66005
  const processedContent = hasExtras ? skillContent.replace(/<SKILL_DIR>/g, skillDir) : skillContent;
65431
66006
  const dirHeader = hasExtras ? `# Skill Directory: ${skillDir}
@@ -66009,15 +66584,32 @@ async function executeSubagent(type, config, model, userPrompt, baseURL, subagen
66009
66584
  const inheritedApiKey = process.env.LETTA_API_KEY || settings.env?.LETTA_API_KEY;
66010
66585
  const inheritedBaseUrl = process.env.LETTA_BASE_URL || settings.env?.LETTA_BASE_URL;
66011
66586
  const subagentWorkingDirectory = resolveSubagentWorkingDirectory();
66587
+ const inheritedMemoryRoots = resolveAllowedMemoryRoots();
66588
+ const childEnv = {
66589
+ ...process.env,
66590
+ ...inheritedApiKey && { LETTA_API_KEY: inheritedApiKey },
66591
+ ...inheritedBaseUrl && { LETTA_BASE_URL: inheritedBaseUrl },
66592
+ LETTA_CODE_AGENT_ROLE: "subagent",
66593
+ ...parentAgentId && { LETTA_PARENT_AGENT_ID: parentAgentId }
66594
+ };
66595
+ if (config.permissionMode === "memory") {
66596
+ if (inheritedMemoryRoots.primaryRoot) {
66597
+ childEnv.MEMORY_DIR = inheritedMemoryRoots.primaryRoot;
66598
+ childEnv.LETTA_MEMORY_DIR = inheritedMemoryRoots.primaryRoot;
66599
+ } else {
66600
+ delete childEnv.MEMORY_DIR;
66601
+ delete childEnv.LETTA_MEMORY_DIR;
66602
+ }
66603
+ const parentMemoryDir = process.env.MEMORY_DIR || process.env.LETTA_MEMORY_DIR;
66604
+ if (parentMemoryDir && parentMemoryDir.trim().length > 0) {
66605
+ childEnv.PARENT_MEMORY_DIR = parentMemoryDir;
66606
+ } else {
66607
+ delete childEnv.PARENT_MEMORY_DIR;
66608
+ }
66609
+ }
66012
66610
  const proc2 = spawn4(launcher.command, launcher.args, {
66013
66611
  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
- }
66612
+ env: childEnv
66021
66613
  });
66022
66614
  proc2.once("spawn", () => {
66023
66615
  updateSubagent(subagentId, { status: "running" });
@@ -66196,6 +66788,7 @@ var init_manager2 = __esm(async () => {
66196
66788
  init_subagentState();
66197
66789
  init_constants();
66198
66790
  init_cli();
66791
+ init_memoryScope();
66199
66792
  init_mode();
66200
66793
  init_session();
66201
66794
  init_context();
@@ -69706,8 +70299,8 @@ function matchesFilePattern(query, pattern, workingDirectory, options) {
69706
70299
  globPattern = globPattern.slice(2);
69707
70300
  }
69708
70301
  if (globPattern.startsWith("~/")) {
69709
- const homedir16 = __require("node:os").homedir();
69710
- globPattern = globPattern.replace(/^~/, homedir16);
70302
+ const homedir17 = __require("node:os").homedir();
70303
+ globPattern = globPattern.replace(/^~/, homedir17);
69711
70304
  }
69712
70305
  globPattern = normalizeAbsolutePattern(globPattern, workingDirectory);
69713
70306
  const windowsContext = isWindowsContext(workingDirectory);
@@ -70220,7 +70813,7 @@ function getDefaultDecision(toolName, toolArgs) {
70220
70813
  }
70221
70814
  if (toolName === "Task" || toolName === "task") {
70222
70815
  const subagentType = typeof toolArgs?.subagent_type === "string" ? toolArgs.subagent_type : "";
70223
- if (READ_ONLY_SUBAGENT_TYPES.has(subagentType)) {
70816
+ if (SAFE_AUTO_APPROVE_SUBAGENT_TYPES.has(subagentType)) {
70224
70817
  return "allow";
70225
70818
  }
70226
70819
  return "ask";
@@ -70251,7 +70844,7 @@ async function checkPermissionWithHooks(toolName, toolArgs, permissions, working
70251
70844
  }
70252
70845
  return result;
70253
70846
  }
70254
- var WORKING_DIRECTORY_TOOLS_V2, WORKING_DIRECTORY_TOOLS_V1, READ_ONLY_SHELL_TOOLS, FILE_TOOLS_V2, FILE_TOOLS_V1, READ_ONLY_SUBAGENT_TYPES;
70847
+ 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
70848
  var init_checker = __esm(async () => {
70256
70849
  init_context();
70257
70850
  init_canonical();
@@ -70318,7 +70911,7 @@ var init_checker = __esm(async () => {
70318
70911
  "read_many_files",
70319
70912
  "ReadManyFiles"
70320
70913
  ];
70321
- READ_ONLY_SUBAGENT_TYPES = new Set([
70914
+ SAFE_AUTO_APPROVE_SUBAGENT_TYPES = new Set([
70322
70915
  "explore",
70323
70916
  "Explore",
70324
70917
  "recall",
@@ -70336,10 +70929,10 @@ __export(exports_loader, {
70336
70929
  loadPermissions: () => loadPermissions,
70337
70930
  getUserSettingsPaths: () => getUserSettingsPaths
70338
70931
  });
70339
- import { homedir as homedir16 } from "node:os";
70932
+ import { homedir as homedir17 } from "node:os";
70340
70933
  import { join as join21 } from "node:path";
70341
70934
  function getUserSettingsPaths(options = {}) {
70342
- const homeDir = options.homeDir || homedir16();
70935
+ const homeDir = options.homeDir || homedir17();
70343
70936
  const xdgConfigHome = options.xdgConfigHome || process.env.XDG_CONFIG_HOME || join21(homeDir, ".config");
70344
70937
  return {
70345
70938
  canonical: join21(homeDir, ".letta", "settings.json"),
@@ -70461,8 +71054,8 @@ var exports_analyzer = {};
70461
71054
  __export(exports_analyzer, {
70462
71055
  analyzeApprovalContext: () => analyzeApprovalContext
70463
71056
  });
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";
71057
+ import { homedir as homedir18 } from "node:os";
71058
+ import { dirname as dirname12, relative as relative8, resolve as resolve22, win32 as win322 } from "node:path";
70466
71059
  function normalizeOsPath(path20) {
70467
71060
  return path20.replace(/\\/g, "/");
70468
71061
  }
@@ -70488,7 +71081,7 @@ function isPathWithinDirectory(path20, directory) {
70488
71081
  return !relativePath.startsWith("../") && relativePath !== ".." && !relativePath.startsWith("/") && !/^[a-zA-Z]:\//.test(relativePath);
70489
71082
  }
70490
71083
  function dirnameForContext(path20) {
70491
- return isWindowsPath(path20) ? win322.dirname(path20) : dirname11(path20);
71084
+ return isWindowsPath(path20) ? win322.dirname(path20) : dirname12(path20);
70492
71085
  }
70493
71086
  function formatAbsoluteRulePath(path20) {
70494
71087
  const normalized = normalizeOsPath(path20).replace(/\/+$/, "");
@@ -70498,7 +71091,7 @@ function formatAbsoluteRulePath(path20) {
70498
71091
  return `//${normalized.replace(/^\/+/, "")}`;
70499
71092
  }
70500
71093
  function formatDisplayPath(path20) {
70501
- return normalizeOsPath(path20).replace(normalizeOsPath(homedir17()), "~");
71094
+ return normalizeOsPath(path20).replace(normalizeOsPath(homedir18()), "~");
70502
71095
  }
70503
71096
  function analyzeApprovalContext(toolName, toolArgs, workingDirectory) {
70504
71097
  const canonicalTool = canonicalToolName(toolName);
@@ -70549,7 +71142,7 @@ function analyzeReadApproval(filePath, workingDir) {
70549
71142
  };
70550
71143
  }
70551
71144
  const relativePath = normalizeOsPath(relativePathForContext(workingDir, absolutePath));
70552
- const relativeDir = dirname11(relativePath);
71145
+ const relativeDir = dirname12(relativePath);
70553
71146
  const pattern = relativeDir === "." || relativeDir === "" ? "**" : `${relativeDir}/**`;
70554
71147
  return {
70555
71148
  recommendedRule: `Read(${pattern})`,
@@ -70692,7 +71285,7 @@ function detectSkillScript(command, workingDir) {
70692
71285
  return null;
70693
71286
  }
70694
71287
  const normalizedWorkingDir = normalizePathSeparators(workingDir).replace(/\/$/, "");
70695
- const normalizedHomeDir = normalizePathSeparators(homedir17()).replace(/\/$/, "");
71288
+ const normalizedHomeDir = normalizePathSeparators(homedir18()).replace(/\/$/, "");
70696
71289
  const detect = (source, regex2) => {
70697
71290
  for (const candidate of pathCandidates) {
70698
71291
  const match3 = candidate.match(regex2);
@@ -72224,15 +72817,15 @@ __export(exports_memoryFilesystem, {
72224
72817
  MEMORY_FS_AGENTS_DIR: () => MEMORY_FS_AGENTS_DIR
72225
72818
  });
72226
72819
  import { existsSync as existsSync18, mkdirSync as mkdirSync13 } from "node:fs";
72227
- import { homedir as homedir18 } from "node:os";
72820
+ import { homedir as homedir19 } from "node:os";
72228
72821
  import { join as join22 } from "node:path";
72229
- function getMemoryFilesystemRoot(agentId, homeDir = homedir18()) {
72822
+ function getMemoryFilesystemRoot(agentId, homeDir = homedir19()) {
72230
72823
  return join22(homeDir, MEMORY_FS_ROOT, MEMORY_FS_AGENTS_DIR, agentId, MEMORY_FS_MEMORY_DIR);
72231
72824
  }
72232
- function getMemorySystemDir(agentId, homeDir = homedir18()) {
72825
+ function getMemorySystemDir(agentId, homeDir = homedir19()) {
72233
72826
  return join22(getMemoryFilesystemRoot(agentId, homeDir), MEMORY_SYSTEM_DIR);
72234
72827
  }
72235
- function ensureMemoryFilesystemDirs(agentId, homeDir = homedir18()) {
72828
+ function ensureMemoryFilesystemDirs(agentId, homeDir = homedir19()) {
72236
72829
  const root = getMemoryFilesystemRoot(agentId, homeDir);
72237
72830
  const systemDir = getMemorySystemDir(agentId, homeDir);
72238
72831
  if (!existsSync18(root)) {
@@ -75298,7 +75891,7 @@ import {
75298
75891
  readFile as readFile7,
75299
75892
  writeFile as writeFile4
75300
75893
  } from "node:fs/promises";
75301
- import { homedir as homedir19, tmpdir as tmpdir3 } from "node:os";
75894
+ import { homedir as homedir20, tmpdir as tmpdir3 } from "node:os";
75302
75895
  import { join as join25 } from "node:path";
75303
75896
  function buildReflectionSubagentPrompt(input) {
75304
75897
  const lines = [];
@@ -75492,7 +76085,7 @@ function getTranscriptRoot() {
75492
76085
  if (envRoot) {
75493
76086
  return envRoot;
75494
76087
  }
75495
- return join25(homedir19(), ".letta", DEFAULT_TRANSCRIPT_DIR);
76088
+ return join25(homedir20(), ".letta", DEFAULT_TRANSCRIPT_DIR);
75496
76089
  }
75497
76090
  function defaultState() {
75498
76091
  return { auto_cursor_line: 0 };
@@ -75681,7 +76274,7 @@ import {
75681
76274
  unlinkSync as unlinkSync7,
75682
76275
  writeFileSync as writeFileSync11
75683
76276
  } from "node:fs";
75684
- import { homedir as homedir20 } from "node:os";
76277
+ import { homedir as homedir21 } from "node:os";
75685
76278
  import { join as join26 } from "node:path";
75686
76279
  function truncateStr(value, maxLen) {
75687
76280
  if (value === null || value === undefined)
@@ -75822,7 +76415,7 @@ class ChunkLog {
75822
76415
  var MAX_ENTRIES = 100, CONTENT_TRUNCATE_LEN = 200, MAX_SESSION_FILES2 = 5, LOG_BASE_DIR, chunkLog;
75823
76416
  var init_chunkLog = __esm(() => {
75824
76417
  init_debug();
75825
- LOG_BASE_DIR = join26(homedir20(), ".letta", "logs", "chunk-logs");
76418
+ LOG_BASE_DIR = join26(homedir21(), ".letta", "logs", "chunk-logs");
75826
76419
  chunkLog = new ChunkLog;
75827
76420
  });
75828
76421
 
@@ -76839,6 +77432,7 @@ var init_engine = __esm(async () => {
76839
77432
  default: "Normal approval flow.",
76840
77433
  acceptEdits: "File edits auto-approved.",
76841
77434
  plan: "Read-only mode. Focus on exploration and planning.",
77435
+ memory: "Memory-scoped mode. Reads are broad; mutations are limited to allowed memory roots.",
76842
77436
  bypassPermissions: "All tools auto-approved. Bias toward action."
76843
77437
  };
76844
77438
  sharedReminderProviders = {
@@ -76951,10 +77545,10 @@ var init_constants2 = __esm(() => {
76951
77545
  // src/websocket/listener/remote-settings.ts
76952
77546
  import { existsSync as existsSync21, readFileSync as readFileSync12 } from "node:fs";
76953
77547
  import { mkdir as mkdir5, writeFile as writeFile5 } from "node:fs/promises";
76954
- import { homedir as homedir21 } from "node:os";
77548
+ import { homedir as homedir22 } from "node:os";
76955
77549
  import path20 from "node:path";
76956
77550
  function getRemoteSettingsPath() {
76957
- return path20.join(homedir21(), ".letta", "remote-settings.json");
77551
+ return path20.join(homedir22(), ".letta", "remote-settings.json");
76958
77552
  }
76959
77553
  function loadRemoteSettings() {
76960
77554
  if (_cache !== null) {
@@ -76998,7 +77592,7 @@ function saveRemoteSettings(updates) {
76998
77592
  }
76999
77593
  function loadLegacyCwdCache() {
77000
77594
  try {
77001
- const legacyPath = path20.join(homedir21(), ".letta", "cwd-cache.json");
77595
+ const legacyPath = path20.join(homedir22(), ".letta", "cwd-cache.json");
77002
77596
  if (!existsSync21(legacyPath))
77003
77597
  return {};
77004
77598
  const raw = readFileSync12(legacyPath, "utf-8");
@@ -78673,7 +79267,7 @@ __export(exports_diff, {
78673
79267
  ADV_DIFF_IGNORE_WHITESPACE: () => ADV_DIFF_IGNORE_WHITESPACE,
78674
79268
  ADV_DIFF_CONTEXT_LINES: () => ADV_DIFF_CONTEXT_LINES
78675
79269
  });
78676
- import { basename as basename2 } from "node:path";
79270
+ import { basename as basename3 } from "node:path";
78677
79271
  function readFileOrNull(p) {
78678
79272
  try {
78679
79273
  return __require("node:fs").readFileSync(p, "utf-8");
@@ -78697,7 +79291,7 @@ function applyAllOccurrences(content, oldStr, newStr) {
78697
79291
  return { ok: true, out: content.split(oldStr).join(newStr) };
78698
79292
  }
78699
79293
  function computeAdvancedDiff(input, opts) {
78700
- const fileName = basename2(input.filePath || "");
79294
+ const fileName = basename3(input.filePath || "");
78701
79295
  const fileContent = opts?.oldStrOverride !== undefined ? opts.oldStrOverride : readFileOrNull(input.filePath);
78702
79296
  if (fileContent === null && input.kind !== "write") {
78703
79297
  return { mode: "fallback", reason: "File not readable" };
@@ -78763,7 +79357,7 @@ function computeAdvancedDiff(input, opts) {
78763
79357
  return { mode: "advanced", fileName, oldStr, newStr, hunks };
78764
79358
  }
78765
79359
  function parsePatchToAdvancedDiff(patchLines, filePath) {
78766
- const fileName = basename2(filePath);
79360
+ const fileName = basename3(filePath);
78767
79361
  const hunks = [];
78768
79362
  let currentHunk = null;
78769
79363
  let oldLine = 1;
@@ -79090,7 +79684,7 @@ var init_formatArgsDisplay = __esm(async () => {
79090
79684
  });
79091
79685
 
79092
79686
  // src/helpers/diffPreview.ts
79093
- import path21, { basename as basename3 } from "node:path";
79687
+ import path21, { basename as basename4 } from "node:path";
79094
79688
  function parseHunkLinePrefix(raw) {
79095
79689
  if (raw.length === 0) {
79096
79690
  return { type: "context", content: "" };
@@ -79196,7 +79790,7 @@ async function computeDiffPreviews(toolName, toolArgs, workingDirectory = proces
79196
79790
  filePath: resolvedFilePath,
79197
79791
  content: toolArgs.content || ""
79198
79792
  });
79199
- previews.push(toDiffPreview(result, basename3(filePath)));
79793
+ previews.push(toDiffPreview(result, basename4(filePath)));
79200
79794
  }
79201
79795
  } else if (isFileEditTool2(toolName)) {
79202
79796
  const filePath = toolArgs.file_path;
@@ -79208,7 +79802,7 @@ async function computeDiffPreviews(toolName, toolArgs, workingDirectory = proces
79208
79802
  filePath: resolvedFilePath,
79209
79803
  edits: toolArgs.edits
79210
79804
  });
79211
- previews.push(toDiffPreview(result, basename3(filePath)));
79805
+ previews.push(toDiffPreview(result, basename4(filePath)));
79212
79806
  } else {
79213
79807
  const result = computeAdvancedDiff2({
79214
79808
  kind: "edit",
@@ -79217,7 +79811,7 @@ async function computeDiffPreviews(toolName, toolArgs, workingDirectory = proces
79217
79811
  newString: toolArgs.new_string || "",
79218
79812
  replaceAll: toolArgs.replace_all
79219
79813
  });
79220
- previews.push(toDiffPreview(result, basename3(filePath)));
79814
+ previews.push(toDiffPreview(result, basename4(filePath)));
79221
79815
  }
79222
79816
  }
79223
79817
  } else if (isPatchTool2(toolName) && toolArgs.input) {
@@ -79226,7 +79820,7 @@ async function computeDiffPreviews(toolName, toolArgs, workingDirectory = proces
79226
79820
  if (op.kind === "add" || op.kind === "update") {
79227
79821
  const result = parsePatchToAdvancedDiff2(op.patchLines, op.path);
79228
79822
  if (result) {
79229
- previews.push(toDiffPreview(result, basename3(op.path)));
79823
+ previews.push(toDiffPreview(result, basename4(op.path)));
79230
79824
  }
79231
79825
  }
79232
79826
  }
@@ -79236,7 +79830,7 @@ async function computeDiffPreviews(toolName, toolArgs, workingDirectory = proces
79236
79830
  if (op.kind === "add" || op.kind === "update") {
79237
79831
  const result = parsePatchToAdvancedDiff2(op.patchLines, op.path);
79238
79832
  if (result) {
79239
- previews.push(toDiffPreview(result, basename3(op.path)));
79833
+ previews.push(toDiffPreview(result, basename4(op.path)));
79240
79834
  }
79241
79835
  }
79242
79836
  }
@@ -81760,18 +82354,24 @@ function buildDeviceStatus(runtime, params) {
81760
82354
  function buildLoopStatus(runtime, params) {
81761
82355
  const listener = getListenerRuntime(runtime);
81762
82356
  if (!listener) {
81763
- return { status: "WAITING_ON_INPUT", active_run_ids: [] };
82357
+ return {
82358
+ status: "WAITING_ON_INPUT",
82359
+ active_run_ids: [],
82360
+ plan_file_path: null
82361
+ };
81764
82362
  }
81765
82363
  const scope = getScopeForRuntime(runtime, params);
81766
82364
  const scopedAgentId = resolveScopedAgentId(listener, scope);
81767
82365
  const scopedConversationId = resolveScopedConversationId(listener, scope);
82366
+ const conversationPermissionModeState = getConversationPermissionModeState(listener, scopedAgentId, scopedConversationId);
81768
82367
  const conversationRuntime = getConversationRuntime(listener, scopedAgentId, scopedConversationId);
81769
82368
  const interruptedCacheActive = hasInterruptedCacheForScope(listener, scope);
81770
82369
  const recovered = getRecoveredApprovalStateForScope(listener, scope);
81771
82370
  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";
81772
82371
  return {
81773
82372
  status,
81774
- active_run_ids: interruptedCacheActive && !conversationRuntime?.isProcessing ? [] : conversationRuntime?.activeRunId ? [conversationRuntime.activeRunId] : []
82373
+ active_run_ids: interruptedCacheActive && !conversationRuntime?.isProcessing ? [] : conversationRuntime?.activeRunId ? [conversationRuntime.activeRunId] : [],
82374
+ plan_file_path: conversationPermissionModeState.mode === "plan" ? conversationPermissionModeState.planFilePath : null
81775
82375
  };
81776
82376
  }
81777
82377
  function buildQueueSnapshot(runtime, params) {
@@ -83239,7 +83839,7 @@ function handleModeChange(msg, socket, runtime, scope) {
83239
83839
  current.modeBeforePlan = null;
83240
83840
  }
83241
83841
  persistPermissionModeMapForRuntime(runtime);
83242
- emitDeviceStatusUpdate(socket, runtime, scope);
83842
+ emitRuntimeStateUpdates(runtime, scope);
83243
83843
  if (isDebugEnabled()) {
83244
83844
  console.log(`[Listen] Mode changed to: ${msg.mode}`);
83245
83845
  }
@@ -83601,11 +84201,11 @@ async function handleSkillCommand(parsed, socket) {
83601
84201
  existsSync: existsSync22,
83602
84202
  lstatSync: lstatSync2,
83603
84203
  mkdirSync: mkdirSync15,
83604
- rmdirSync: rmdirSync2,
84204
+ rmdirSync,
83605
84205
  symlinkSync,
83606
84206
  unlinkSync: unlinkSync8
83607
84207
  } = await import("node:fs");
83608
- const { basename: basename4, join: join28 } = await import("node:path");
84208
+ const { basename: basename5, join: join28 } = await import("node:path");
83609
84209
  const lettaHome = process.env.LETTA_HOME || join28(process.env.HOME || process.env.USERPROFILE || "~", ".letta");
83610
84210
  const globalSkillsDir = join28(lettaHome, "skills");
83611
84211
  if (parsed.type === "skill_enable") {
@@ -83629,14 +84229,14 @@ async function handleSkillCommand(parsed, socket) {
83629
84229
  }, "listener_skill_send_failed", "listener_skill_command");
83630
84230
  return true;
83631
84231
  }
83632
- const linkName = basename4(parsed.skill_path);
84232
+ const linkName = basename5(parsed.skill_path);
83633
84233
  const linkPath = join28(globalSkillsDir, linkName);
83634
84234
  mkdirSync15(globalSkillsDir, { recursive: true });
83635
84235
  if (existsSync22(linkPath)) {
83636
84236
  const stat6 = lstatSync2(linkPath);
83637
84237
  if (stat6.isSymbolicLink()) {
83638
84238
  if (process.platform === "win32") {
83639
- rmdirSync2(linkPath);
84239
+ rmdirSync(linkPath);
83640
84240
  } else {
83641
84241
  unlinkSync8(linkPath);
83642
84242
  }
@@ -83694,7 +84294,7 @@ async function handleSkillCommand(parsed, socket) {
83694
84294
  return true;
83695
84295
  }
83696
84296
  if (process.platform === "win32") {
83697
- rmdirSync2(linkPath);
84297
+ rmdirSync(linkPath);
83698
84298
  } else {
83699
84299
  unlinkSync8(linkPath);
83700
84300
  }
@@ -85261,6 +85861,7 @@ var init_client4 = __esm(async () => {
85261
85861
  __listenClientTestUtils = {
85262
85862
  createRuntime: createLegacyTestRuntime,
85263
85863
  createListenerRuntime: createRuntime,
85864
+ handleModeChange,
85264
85865
  getOrCreateScopedRuntime,
85265
85866
  buildListModelsEntries,
85266
85867
  buildListModelsResponse,
@@ -85349,7 +85950,7 @@ import {
85349
85950
  readFileSync as readFileSync14,
85350
85951
  unlinkSync as unlinkSync8
85351
85952
  } from "node:fs";
85352
- import { homedir as homedir25 } from "node:os";
85953
+ import { homedir as homedir26 } from "node:os";
85353
85954
  import { join as join31 } from "node:path";
85354
85955
  import { format as format2 } from "node:util";
85355
85956
  function isDebugEnabled2() {
@@ -85455,7 +86056,7 @@ function debugWarn2(prefix, message, ...args) {
85455
86056
  }
85456
86057
  var DEBUG_LOG_DIR2, MAX_SESSION_FILES3 = 5, DEFAULT_TAIL_LINES2 = 50, debugLogFile2;
85457
86058
  var init_debug2 = __esm(() => {
85458
- DEBUG_LOG_DIR2 = join31(homedir25(), ".letta", "logs", "debug");
86059
+ DEBUG_LOG_DIR2 = join31(homedir26(), ".letta", "logs", "debug");
85459
86060
  debugLogFile2 = new DebugLogFile2;
85460
86061
  });
85461
86062
 
@@ -85484,10 +86085,10 @@ __export(exports_skills2, {
85484
86085
  });
85485
86086
  import { existsSync as existsSync24 } from "node:fs";
85486
86087
  import { readdir as readdir7, readFile as readFile8, realpath as realpath4, stat as stat6 } from "node:fs/promises";
85487
- import { dirname as dirname12, join as join32 } from "node:path";
86088
+ import { dirname as dirname13, join as join32 } from "node:path";
85488
86089
  import { fileURLToPath as fileURLToPath8 } from "node:url";
85489
86090
  function getBundledSkillsPath2() {
85490
- const thisDir = dirname12(fileURLToPath8(import.meta.url));
86091
+ const thisDir = dirname13(fileURLToPath8(import.meta.url));
85491
86092
  if (thisDir.includes("src/agent") || thisDir.includes("src\\agent")) {
85492
86093
  return join32(thisDir, "../skills/builtin");
85493
86094
  }
@@ -85683,12 +86284,12 @@ import {
85683
86284
  writeFileSync as fsWriteFileSync2,
85684
86285
  mkdirSync as mkdirSync17
85685
86286
  } from "node:fs";
85686
- import { dirname as dirname13 } from "node:path";
86287
+ import { dirname as dirname14 } from "node:path";
85687
86288
  async function readFile9(path23) {
85688
86289
  return fsReadFileSync2(path23, { encoding: "utf-8" });
85689
86290
  }
85690
86291
  async function writeFile7(path23, content) {
85691
- const dir = dirname13(path23);
86292
+ const dir = dirname14(path23);
85692
86293
  if (!existsSync25(dir)) {
85693
86294
  mkdirSync17(dir, { recursive: true });
85694
86295
  }
@@ -85733,7 +86334,7 @@ async function queryTerminalBackground(timeoutMs = 100) {
85733
86334
  }
85734
86335
  const wasRaw = process.stdin.isRaw;
85735
86336
  const wasFlowing = process.stdin.readableFlowing;
85736
- return new Promise((resolve26) => {
86337
+ return new Promise((resolve25) => {
85737
86338
  let response = "";
85738
86339
  let resolved = false;
85739
86340
  const cleanup = () => {
@@ -85750,7 +86351,7 @@ async function queryTerminalBackground(timeoutMs = 100) {
85750
86351
  };
85751
86352
  const timeout = setTimeout(() => {
85752
86353
  cleanup();
85753
- resolve26(null);
86354
+ resolve25(null);
85754
86355
  }, timeoutMs);
85755
86356
  const onData = (data) => {
85756
86357
  response += data.toString();
@@ -85760,7 +86361,7 @@ async function queryTerminalBackground(timeoutMs = 100) {
85760
86361
  if (match3) {
85761
86362
  clearTimeout(timeout);
85762
86363
  cleanup();
85763
- resolve26({
86364
+ resolve25({
85764
86365
  r: parseHexComponent(match3[1] ?? "0"),
85765
86366
  g: parseHexComponent(match3[2] ?? "0"),
85766
86367
  b: parseHexComponent(match3[3] ?? "0")
@@ -85775,7 +86376,7 @@ async function queryTerminalBackground(timeoutMs = 100) {
85775
86376
  } catch {
85776
86377
  clearTimeout(timeout);
85777
86378
  cleanup();
85778
- resolve26(null);
86379
+ resolve25(null);
85779
86380
  }
85780
86381
  });
85781
86382
  }
@@ -86984,10 +87585,10 @@ __export(exports_setup, {
86984
87585
  runSetup: () => runSetup
86985
87586
  });
86986
87587
  async function runSetup() {
86987
- return new Promise((resolve27) => {
87588
+ return new Promise((resolve26) => {
86988
87589
  const { waitUntilExit } = render_default(import_react32.default.createElement(SetupUI, {
86989
87590
  onComplete: () => {
86990
- resolve27();
87591
+ resolve26();
86991
87592
  }
86992
87593
  }));
86993
87594
  waitUntilExit().catch((error) => {
@@ -87517,10 +88118,10 @@ __export(exports_import, {
87517
88118
  });
87518
88119
  import { createReadStream } from "node:fs";
87519
88120
  import { chmod, mkdir as mkdir7, readFile as readFile10, writeFile as writeFile8 } from "node:fs/promises";
87520
- import { dirname as dirname14, resolve as resolve27 } from "node:path";
88121
+ import { dirname as dirname15, resolve as resolve26 } from "node:path";
87521
88122
  async function importAgentFromFile(options) {
87522
88123
  const client = await getClient();
87523
- const resolvedPath = resolve27(options.filePath);
88124
+ const resolvedPath = resolve26(options.filePath);
87524
88125
  const file = createReadStream(resolvedPath);
87525
88126
  const importResponse = await client.agents.importFile({
87526
88127
  file,
@@ -87555,7 +88156,7 @@ async function extractSkillsFromAf(afPath, destDir) {
87555
88156
  return [];
87556
88157
  }
87557
88158
  for (const skill2 of afData.skills) {
87558
- const skillDir = resolve27(destDir, skill2.name);
88159
+ const skillDir = resolve26(destDir, skill2.name);
87559
88160
  await mkdir7(skillDir, { recursive: true });
87560
88161
  if (skill2.files) {
87561
88162
  await writeSkillFiles(skillDir, skill2.files);
@@ -87575,8 +88176,8 @@ async function writeSkillFiles(skillDir, files) {
87575
88176
  }
87576
88177
  }
87577
88178
  async function writeSkillFile(skillDir, filePath, content) {
87578
- const fullPath = resolve27(skillDir, filePath);
87579
- await mkdir7(dirname14(fullPath), { recursive: true });
88179
+ const fullPath = resolve26(skillDir, filePath);
88180
+ await mkdir7(dirname15(fullPath), { recursive: true });
87580
88181
  await writeFile8(fullPath, content, "utf-8");
87581
88182
  const isScript = filePath.startsWith("scripts/") || content.trimStart().startsWith("#!");
87582
88183
  if (isScript) {
@@ -87882,12 +88483,12 @@ async function prepareHeadlessToolExecutionContext(params) {
87882
88483
  };
87883
88484
  }
87884
88485
  async function flushAndExit(code) {
87885
- const flushWritable = (stream2) => new Promise((resolve28) => {
88486
+ const flushWritable = (stream2) => new Promise((resolve27) => {
87886
88487
  if (stream2.destroyed || stream2.writableEnded) {
87887
- resolve28();
88488
+ resolve27();
87888
88489
  return;
87889
88490
  }
87890
- stream2.write("", () => resolve28());
88491
+ stream2.write("", () => resolve27());
87891
88492
  });
87892
88493
  await Promise.allSettled([
87893
88494
  flushWritable(process.stdout),
@@ -87913,7 +88514,8 @@ async function handleHeadlessCommand(parsedArgs, model, skillsDirectoryOverride,
87913
88514
  "default",
87914
88515
  "acceptEdits",
87915
88516
  "bypassPermissions",
87916
- "plan"
88517
+ "plan",
88518
+ "memory"
87917
88519
  ];
87918
88520
  if (validModes.includes(permissionModeValue)) {
87919
88521
  permissionMode3.setMode(permissionModeValue);
@@ -88834,7 +89436,7 @@ ${loadedContents.join(`
88834
89436
  } else {
88835
89437
  console.error(`Conversation is busy, waiting ${Math.round(retryDelayMs / 1000)}s and retrying...`);
88836
89438
  }
88837
- await new Promise((resolve28) => setTimeout(resolve28, retryDelayMs));
89439
+ await new Promise((resolve27) => setTimeout(resolve27, retryDelayMs));
88838
89440
  continue;
88839
89441
  }
88840
89442
  }
@@ -88883,7 +89485,7 @@ ${loadedContents.join(`
88883
89485
  const delaySeconds = Math.round(delayMs / 1000);
88884
89486
  console.error(`Transient API error before streaming (attempt ${attempt} of ${LLM_API_ERROR_MAX_RETRIES2}), retrying in ${delaySeconds}s...`);
88885
89487
  }
88886
- await new Promise((resolve28) => setTimeout(resolve28, delayMs));
89488
+ await new Promise((resolve27) => setTimeout(resolve27, delayMs));
88887
89489
  conversationBusyRetries = 0;
88888
89490
  continue;
88889
89491
  }
@@ -89122,7 +89724,7 @@ ${loadedContents.join(`
89122
89724
  const delaySeconds = Math.round(delayMs / 1000);
89123
89725
  console.error(`LLM API error encountered (attempt ${attempt} of ${LLM_API_ERROR_MAX_RETRIES2}), retrying in ${delaySeconds}s...`);
89124
89726
  }
89125
- await new Promise((resolve28) => setTimeout(resolve28, delayMs));
89727
+ await new Promise((resolve27) => setTimeout(resolve27, delayMs));
89126
89728
  refreshCurrentInputOtids();
89127
89729
  continue;
89128
89730
  }
@@ -89208,7 +89810,7 @@ ${loadedContents.join(`
89208
89810
  } else {
89209
89811
  console.error(`Empty LLM response, retrying (attempt ${attempt} of ${EMPTY_RESPONSE_MAX_RETRIES2})...`);
89210
89812
  }
89211
- await new Promise((resolve28) => setTimeout(resolve28, delayMs));
89813
+ await new Promise((resolve27) => setTimeout(resolve27, delayMs));
89212
89814
  refreshCurrentInputOtids();
89213
89815
  continue;
89214
89816
  }
@@ -89236,7 +89838,7 @@ ${loadedContents.join(`
89236
89838
  const delaySeconds = Math.round(delayMs / 1000);
89237
89839
  console.error(`LLM API error encountered (attempt ${attempt} of ${LLM_API_ERROR_MAX_RETRIES2}), retrying in ${delaySeconds}s...`);
89238
89840
  }
89239
- await new Promise((resolve28) => setTimeout(resolve28, delayMs));
89841
+ await new Promise((resolve27) => setTimeout(resolve27, delayMs));
89240
89842
  refreshCurrentInputOtids();
89241
89843
  continue;
89242
89844
  }
@@ -89570,9 +90172,9 @@ async function runBidirectionalMode(agent, conversationId, client, _outputFormat
89570
90172
  const syntheticUserLine = serializeQueuedMessageAsUserLine(queuedMessage);
89571
90173
  maybeNotifyBlocked(syntheticUserLine);
89572
90174
  if (lineResolver) {
89573
- const resolve28 = lineResolver;
90175
+ const resolve27 = lineResolver;
89574
90176
  lineResolver = null;
89575
- resolve28(syntheticUserLine);
90177
+ resolve27(syntheticUserLine);
89576
90178
  return;
89577
90179
  }
89578
90180
  lineQueue.push(syntheticUserLine);
@@ -89580,9 +90182,9 @@ async function runBidirectionalMode(agent, conversationId, client, _outputFormat
89580
90182
  rl.on("line", (line) => {
89581
90183
  maybeNotifyBlocked(line);
89582
90184
  if (lineResolver) {
89583
- const resolve28 = lineResolver;
90185
+ const resolve27 = lineResolver;
89584
90186
  lineResolver = null;
89585
- resolve28(line);
90187
+ resolve27(line);
89586
90188
  } else {
89587
90189
  lineQueue.push(line);
89588
90190
  }
@@ -89591,17 +90193,17 @@ async function runBidirectionalMode(agent, conversationId, client, _outputFormat
89591
90193
  setMessageQueueAdder(null);
89592
90194
  msgQueueRuntime.clear("shutdown");
89593
90195
  if (lineResolver) {
89594
- const resolve28 = lineResolver;
90196
+ const resolve27 = lineResolver;
89595
90197
  lineResolver = null;
89596
- resolve28(null);
90198
+ resolve27(null);
89597
90199
  }
89598
90200
  });
89599
90201
  async function getNextLine() {
89600
90202
  if (lineQueue.length > 0) {
89601
90203
  return lineQueue.shift() ?? null;
89602
90204
  }
89603
- return new Promise((resolve28) => {
89604
- lineResolver = resolve28;
90205
+ return new Promise((resolve27) => {
90206
+ lineResolver = resolve27;
89605
90207
  });
89606
90208
  }
89607
90209
  async function requestPermission(toolCallId, toolName, toolInput) {
@@ -90105,7 +90707,7 @@ async function runBidirectionalMode(agent, conversationId, client, _outputFormat
90105
90707
  uuid: `retry-bidir-${randomUUID8()}`
90106
90708
  };
90107
90709
  console.log(JSON.stringify(retryMsg));
90108
- await new Promise((resolve28) => setTimeout(resolve28, delayMs));
90710
+ await new Promise((resolve27) => setTimeout(resolve27, delayMs));
90109
90711
  continue;
90110
90712
  }
90111
90713
  throw preStreamError;
@@ -90401,10 +91003,10 @@ async function detectAndEnableKittyProtocol() {
90401
91003
  detectionComplete = true;
90402
91004
  return;
90403
91005
  }
90404
- return new Promise((resolve28) => {
91006
+ return new Promise((resolve27) => {
90405
91007
  if (!process.stdin.isTTY || !process.stdout.isTTY) {
90406
91008
  detectionComplete = true;
90407
- resolve28();
91009
+ resolve27();
90408
91010
  return;
90409
91011
  }
90410
91012
  const originalRawMode = process.stdin.isRaw;
@@ -90437,7 +91039,7 @@ async function detectAndEnableKittyProtocol() {
90437
91039
  console.error("[kitty] protocol query unsupported; enabled anyway (best-effort)");
90438
91040
  }
90439
91041
  detectionComplete = true;
90440
- resolve28();
91042
+ resolve27();
90441
91043
  };
90442
91044
  const handleData = (data) => {
90443
91045
  if (timeoutId === undefined) {
@@ -90795,10 +91397,10 @@ __export(exports_settings, {
90795
91397
  loadProjectSettings: () => loadProjectSettings,
90796
91398
  getSetting: () => getSetting
90797
91399
  });
90798
- import { homedir as homedir28 } from "node:os";
91400
+ import { homedir as homedir29 } from "node:os";
90799
91401
  import { join as join36 } from "node:path";
90800
91402
  function getSettingsPath() {
90801
- return join36(homedir28(), ".letta", "settings.json");
91403
+ return join36(homedir29(), ".letta", "settings.json");
90802
91404
  }
90803
91405
  async function loadSettings() {
90804
91406
  const settingsPath = getSettingsPath();
@@ -106304,16 +106906,16 @@ function clipStyledSpans(spans, maxColumns) {
106304
106906
  return { spans: clipped, clipped: false };
106305
106907
  }
106306
106908
  function languageFromPath(filePath) {
106307
- const basename4 = filePath.split("/").pop() ?? filePath;
106308
- const lower = basename4.toLowerCase();
106909
+ const basename5 = filePath.split("/").pop() ?? filePath;
106910
+ const lower = basename5.toLowerCase();
106309
106911
  if (lower === "makefile")
106310
106912
  return "makefile";
106311
106913
  if (lower === "dockerfile")
106312
106914
  return "dockerfile";
106313
- const dotIdx = basename4.lastIndexOf(".");
106915
+ const dotIdx = basename5.lastIndexOf(".");
106314
106916
  if (dotIdx < 0)
106315
106917
  return;
106316
- const ext3 = basename4.slice(dotIdx + 1).toLowerCase();
106918
+ const ext3 = basename5.slice(dotIdx + 1).toLowerCase();
106317
106919
  return EXT_TO_LANG[ext3];
106318
106920
  }
106319
106921
  function colorForClassName(className, palette) {
@@ -110349,7 +110951,7 @@ var init_plan_viewer_template = () => {};
110349
110951
 
110350
110952
  // src/web/generate-plan-viewer.ts
110351
110953
  import { chmodSync as chmodSync2, existsSync as existsSync28, mkdirSync as mkdirSync20, writeFileSync as writeFileSync14 } from "node:fs";
110352
- import { homedir as homedir29 } from "node:os";
110954
+ import { homedir as homedir30 } from "node:os";
110353
110955
  import { join as join37 } from "node:path";
110354
110956
  async function generateAndOpenPlanViewer(planContent, planFilePath, options) {
110355
110957
  const data = {
@@ -110383,7 +110985,7 @@ async function generateAndOpenPlanViewer(planContent, planFilePath, options) {
110383
110985
  var VIEWERS_DIR;
110384
110986
  var init_generate_plan_viewer = __esm(() => {
110385
110987
  init_plan_viewer_template();
110386
- VIEWERS_DIR = join37(homedir29(), ".letta", "viewers");
110988
+ VIEWERS_DIR = join37(homedir30(), ".letta", "viewers");
110387
110989
  });
110388
110990
 
110389
110991
  // src/cli/components/StaticPlanApproval.tsx
@@ -112623,7 +113225,7 @@ var init_pasteRegistry = __esm(() => {
112623
113225
  import { execFileSync as execFileSync3 } from "node:child_process";
112624
113226
  import { existsSync as existsSync29, readFileSync as readFileSync16, statSync as statSync10, unlinkSync as unlinkSync10 } from "node:fs";
112625
113227
  import { tmpdir as tmpdir4 } from "node:os";
112626
- import { basename as basename4, extname as extname5, isAbsolute as isAbsolute18, join as join38, resolve as resolve28 } from "node:path";
113228
+ import { basename as basename5, extname as extname5, isAbsolute as isAbsolute19, join as join38, resolve as resolve27 } from "node:path";
112627
113229
  function countLines2(text) {
112628
113230
  return (text.match(/\r\n|\r|\n/g) || []).length + 1;
112629
113231
  }
@@ -112670,8 +113272,8 @@ function translatePasteForImages(paste) {
112670
113272
  }
112671
113273
  } catch {}
112672
113274
  }
112673
- if (!isAbsolute18(filePath))
112674
- filePath = resolve28(process.cwd(), filePath);
113275
+ if (!isAbsolute19(filePath))
113276
+ filePath = resolve27(process.cwd(), filePath);
112675
113277
  const ext3 = extname5(filePath || "").toLowerCase();
112676
113278
  if (IMAGE_EXTS.has(ext3) && existsSync29(filePath) && statSync10(filePath).isFile()) {
112677
113279
  const buf = readFileSync16(filePath);
@@ -112680,7 +113282,7 @@ function translatePasteForImages(paste) {
112680
113282
  const id = allocateImage({
112681
113283
  data: b64,
112682
113284
  mediaType: mt,
112683
- filename: basename4(filePath)
113285
+ filename: basename5(filePath)
112684
113286
  });
112685
113287
  s = `[Image #${id}]`;
112686
113288
  }
@@ -113446,8 +114048,8 @@ import {
113446
114048
  readFileSync as readFileSync17,
113447
114049
  writeFileSync as writeFileSync15
113448
114050
  } from "node:fs";
113449
- import { homedir as homedir30, platform as platform5 } from "node:os";
113450
- import { dirname as dirname15, join as join39 } from "node:path";
114051
+ import { homedir as homedir31, platform as platform5 } from "node:os";
114052
+ import { dirname as dirname16, join as join39 } from "node:path";
113451
114053
  function detectTerminalType() {
113452
114054
  if (process.env.CURSOR_TRACE_ID || process.env.CURSOR_CHANNEL) {
113453
114055
  return "cursor";
@@ -113479,7 +114081,7 @@ function getKeybindingsPath(terminal) {
113479
114081
  }[terminal];
113480
114082
  const os7 = platform5();
113481
114083
  if (os7 === "darwin") {
113482
- return join39(homedir30(), "Library", "Application Support", appName, "User", "keybindings.json");
114084
+ return join39(homedir31(), "Library", "Application Support", appName, "User", "keybindings.json");
113483
114085
  }
113484
114086
  if (os7 === "win32") {
113485
114087
  const appData = process.env.APPDATA;
@@ -113488,7 +114090,7 @@ function getKeybindingsPath(terminal) {
113488
114090
  return join39(appData, appName, "User", "keybindings.json");
113489
114091
  }
113490
114092
  if (os7 === "linux") {
113491
- return join39(homedir30(), ".config", appName, "User", "keybindings.json");
114093
+ return join39(homedir31(), ".config", appName, "User", "keybindings.json");
113492
114094
  }
113493
114095
  return null;
113494
114096
  }
@@ -113538,7 +114140,7 @@ function installKeybinding(keybindingsPath) {
113538
114140
  if (keybindingExists(keybindingsPath)) {
113539
114141
  return { success: true, alreadyExists: true };
113540
114142
  }
113541
- const parentDir = dirname15(keybindingsPath);
114143
+ const parentDir = dirname16(keybindingsPath);
113542
114144
  if (!existsSync30(parentDir)) {
113543
114145
  mkdirSync21(parentDir, { recursive: true });
113544
114146
  }
@@ -113645,10 +114247,10 @@ function getWezTermConfigPath() {
113645
114247
  if (existsSync30(xdgPath))
113646
114248
  return xdgPath;
113647
114249
  }
113648
- const configPath = join39(homedir30(), ".config", "wezterm", "wezterm.lua");
114250
+ const configPath = join39(homedir31(), ".config", "wezterm", "wezterm.lua");
113649
114251
  if (existsSync30(configPath))
113650
114252
  return configPath;
113651
- return join39(homedir30(), ".wezterm.lua");
114253
+ return join39(homedir31(), ".wezterm.lua");
113652
114254
  }
113653
114255
  function wezTermDeleteFixExists(configPath) {
113654
114256
  if (!existsSync30(configPath))
@@ -113697,7 +114299,7 @@ return config`);
113697
114299
  ${WEZTERM_DELETE_FIX}
113698
114300
  `;
113699
114301
  }
113700
- const parentDir = dirname15(configPath);
114302
+ const parentDir = dirname16(configPath);
113701
114303
  if (!existsSync30(parentDir)) {
113702
114304
  mkdirSync21(parentDir, { recursive: true });
113703
114305
  }
@@ -114280,7 +114882,7 @@ __export(exports_custom, {
114280
114882
  });
114281
114883
  import { existsSync as existsSync31 } from "node:fs";
114282
114884
  import { readdir as readdir9, readFile as readFile11 } from "node:fs/promises";
114283
- import { basename as basename5, dirname as dirname16, join as join40 } from "node:path";
114885
+ import { basename as basename6, dirname as dirname17, join as join40 } from "node:path";
114284
114886
  async function getCustomCommands() {
114285
114887
  if (cachedCommands !== null) {
114286
114888
  return cachedCommands;
@@ -114340,8 +114942,8 @@ async function findCommandFiles(currentPath, rootPath, commands2, source2) {
114340
114942
  async function parseCommandFile(filePath, rootPath, source2) {
114341
114943
  const content = await readFile11(filePath, "utf-8");
114342
114944
  const { frontmatter, body } = parseFrontmatter(content);
114343
- const id = basename5(filePath, ".md");
114344
- const relativePath = dirname16(filePath).slice(rootPath.length);
114945
+ const id = basename6(filePath, ".md");
114946
+ const relativePath = dirname17(filePath).slice(rootPath.length);
114345
114947
  const namespace = relativePath.replace(/^[/\\]/, "") || undefined;
114346
114948
  let description = getStringField(frontmatter, "description");
114347
114949
  if (!description) {
@@ -114689,12 +115291,12 @@ var init_HelpDialog = __esm(async () => {
114689
115291
  });
114690
115292
 
114691
115293
  // src/hooks/writer.ts
114692
- import { homedir as homedir31 } from "node:os";
114693
- import { resolve as resolve29 } from "node:path";
115294
+ import { homedir as homedir32 } from "node:os";
115295
+ import { resolve as resolve28 } from "node:path";
114694
115296
  function isProjectSettingsPathCollidingWithGlobal2(workingDirectory) {
114695
- const home = process.env.HOME || homedir31();
114696
- const globalSettingsPath = resolve29(home, ".letta", "settings.json");
114697
- const projectSettingsPath = resolve29(workingDirectory, ".letta", "settings.json");
115297
+ const home = process.env.HOME || homedir32();
115298
+ const globalSettingsPath = resolve28(home, ".letta", "settings.json");
115299
+ const projectSettingsPath = resolve28(workingDirectory, ".letta", "settings.json");
114698
115300
  return globalSettingsPath === projectSettingsPath;
114699
115301
  }
114700
115302
  function loadHooksFromLocation(location, workingDirectory = process.cwd()) {
@@ -117332,7 +117934,7 @@ var init_AgentInfoBar = __esm(async () => {
117332
117934
 
117333
117935
  // src/cli/helpers/fileSearch.ts
117334
117936
  import { readdirSync as readdirSync13, statSync as statSync11 } from "node:fs";
117335
- import { join as join41, relative as relative15, resolve as resolve30 } from "node:path";
117937
+ import { join as join41, relative as relative15, resolve as resolve29 } from "node:path";
117336
117938
  function searchDirectoryRecursive(dir, pattern, maxResults = 200, results = [], depth = 0, maxDepth = 10, lowerPattern = pattern.toLowerCase()) {
117337
117939
  if (results.length >= maxResults || depth >= maxDepth) {
117338
117940
  return results;
@@ -117375,7 +117977,7 @@ async function searchFiles(query, deep = false) {
117375
117977
  const dirPart = query.slice(0, lastSlashIndex);
117376
117978
  const pattern = query.slice(lastSlashIndex + 1);
117377
117979
  try {
117378
- const resolvedDir = resolve30(getIndexRoot(), dirPart);
117980
+ const resolvedDir = resolve29(getIndexRoot(), dirPart);
117379
117981
  try {
117380
117982
  statSync11(resolvedDir);
117381
117983
  searchDir = resolvedDir;
@@ -119499,11 +120101,11 @@ import {
119499
120101
  mkdirSync as mkdirSync22,
119500
120102
  mkdtempSync,
119501
120103
  readFileSync as readFileSync18,
119502
- rmSync as rmSync3,
120104
+ rmSync as rmSync4,
119503
120105
  writeFileSync as writeFileSync16
119504
120106
  } from "node:fs";
119505
120107
  import { tmpdir as tmpdir5 } from "node:os";
119506
- import { dirname as dirname17, join as join42 } from "node:path";
120108
+ import { dirname as dirname18, join as join42 } from "node:path";
119507
120109
  function runCommand(command, args, cwd2, input) {
119508
120110
  try {
119509
120111
  return execFileSync4(command, args, {
@@ -119750,8 +120352,8 @@ function runGit6(args, cwd2) {
119750
120352
  }
119751
120353
  function writeWorkflow(repoDir, workflowPath, content) {
119752
120354
  const absolutePath = join42(repoDir, workflowPath);
119753
- if (!existsSync32(dirname17(absolutePath))) {
119754
- mkdirSync22(dirname17(absolutePath), { recursive: true });
120355
+ if (!existsSync32(dirname18(absolutePath))) {
120356
+ mkdirSync22(dirname18(absolutePath), { recursive: true });
119755
120357
  }
119756
120358
  const next = `${content.trimEnd()}
119757
120359
  `;
@@ -119882,7 +120484,7 @@ async function installGithubApp(options) {
119882
120484
  agentUrl: resolvedAgentId ? buildChatUrl(resolvedAgentId) : null
119883
120485
  };
119884
120486
  } finally {
119885
- rmSync3(tempDir, { recursive: true, force: true });
120487
+ rmSync4(tempDir, { recursive: true, force: true });
119886
120488
  }
119887
120489
  }
119888
120490
  var DEFAULT_WORKFLOW_PATH = ".github/workflows/letta.yml", ALTERNATE_WORKFLOW_PATH = ".github/workflows/letta-code.yml";
@@ -123657,7 +124259,7 @@ __export(exports_generate_memory_viewer, {
123657
124259
  });
123658
124260
  import { execFile as execFileCb5 } from "node:child_process";
123659
124261
  import { chmodSync as chmodSync3, existsSync as existsSync33, mkdirSync as mkdirSync23, writeFileSync as writeFileSync17 } from "node:fs";
123660
- import { homedir as homedir32 } from "node:os";
124262
+ import { homedir as homedir33 } from "node:os";
123661
124263
  import { join as join43 } from "node:path";
123662
124264
  import { promisify as promisify14 } from "node:util";
123663
124265
  async function runGitSafe(cwd2, args) {
@@ -123971,7 +124573,7 @@ var init_generate_memory_viewer = __esm(async () => {
123971
124573
  init_memoryGit()
123972
124574
  ]);
123973
124575
  execFile14 = promisify14(execFileCb5);
123974
- VIEWERS_DIR2 = join43(homedir32(), ".letta", "viewers");
124576
+ VIEWERS_DIR2 = join43(homedir33(), ".letta", "viewers");
123975
124577
  REFLECTION_PATTERN = /\(reflection\)|🔮|reflection:/i;
123976
124578
  });
123977
124579
 
@@ -126689,11 +127291,11 @@ var init_PersonalitySelector = __esm(async () => {
126689
127291
 
126690
127292
  // src/utils/aws-credentials.ts
126691
127293
  import { readFile as readFile12 } from "node:fs/promises";
126692
- import { homedir as homedir33 } from "node:os";
127294
+ import { homedir as homedir34 } from "node:os";
126693
127295
  import { join as join44 } from "node:path";
126694
127296
  async function parseAwsCredentials() {
126695
- const credentialsPath = join44(homedir33(), ".aws", "credentials");
126696
- const configPath = join44(homedir33(), ".aws", "config");
127297
+ const credentialsPath = join44(homedir34(), ".aws", "credentials");
127298
+ const configPath = join44(homedir34(), ".aws", "config");
126697
127299
  const profiles = new Map;
126698
127300
  try {
126699
127301
  const content = await readFile12(credentialsPath, "utf-8");
@@ -132174,7 +132776,7 @@ async function executeStatusLineCommand(command, payload, options) {
132174
132776
  };
132175
132777
  }
132176
132778
  function runWithLauncher(launcher, inputJson, timeout, signal, workingDirectory, startTime) {
132177
- return new Promise((resolve31, reject) => {
132779
+ return new Promise((resolve30, reject) => {
132178
132780
  const [executable, ...args] = launcher;
132179
132781
  if (!executable) {
132180
132782
  reject(new Error("Empty launcher"));
@@ -132187,7 +132789,7 @@ function runWithLauncher(launcher, inputJson, timeout, signal, workingDirectory,
132187
132789
  const safeResolve = (result) => {
132188
132790
  if (!resolved) {
132189
132791
  resolved = true;
132190
- resolve31(result);
132792
+ resolve30(result);
132191
132793
  }
132192
132794
  };
132193
132795
  let child;
@@ -132633,7 +133235,7 @@ __export(exports_shellAliases, {
132633
133235
  clearAliasCache: () => clearAliasCache
132634
133236
  });
132635
133237
  import { existsSync as existsSync36, readFileSync as readFileSync20 } from "node:fs";
132636
- import { homedir as homedir34 } from "node:os";
133238
+ import { homedir as homedir35 } from "node:os";
132637
133239
  import { join as join46 } from "node:path";
132638
133240
  function parseAliasesFromFile(filePath) {
132639
133241
  const aliases = new Map;
@@ -132703,7 +133305,7 @@ function loadAliases(forceReload = false) {
132703
133305
  if (aliasCache && !forceReload) {
132704
133306
  return aliasCache;
132705
133307
  }
132706
- const home = homedir34();
133308
+ const home = homedir35();
132707
133309
  const allAliases = new Map;
132708
133310
  for (const file of ALIAS_FILES) {
132709
133311
  const filePath = join46(home, file);
@@ -133364,14 +133966,14 @@ __export(exports_export, {
133364
133966
  packageSkills: () => packageSkills
133365
133967
  });
133366
133968
  import { readdir as readdir10, readFile as readFile13 } from "node:fs/promises";
133367
- import { relative as relative16, resolve as resolve31 } from "node:path";
133969
+ import { relative as relative16, resolve as resolve30 } from "node:path";
133368
133970
  async function packageSkills(agentId, skillsDir) {
133369
133971
  const skills = [];
133370
133972
  const skillNames = new Set;
133371
133973
  const dirsToCheck = skillsDir ? [skillsDir] : [
133372
133974
  agentId && getAgentSkillsDir(agentId),
133373
- resolve31(process.cwd(), ".skills"),
133374
- resolve31(process.env.HOME || "~", ".letta", "skills")
133975
+ resolve30(process.cwd(), ".skills"),
133976
+ resolve30(process.env.HOME || "~", ".letta", "skills")
133375
133977
  ].filter((dir) => Boolean(dir));
133376
133978
  for (const baseDir of dirsToCheck) {
133377
133979
  try {
@@ -133381,8 +133983,8 @@ async function packageSkills(agentId, skillsDir) {
133381
133983
  continue;
133382
133984
  if (skillNames.has(entry.name))
133383
133985
  continue;
133384
- const skillDir = resolve31(baseDir, entry.name);
133385
- const skillMdPath = resolve31(skillDir, "SKILL.md");
133986
+ const skillDir = resolve30(baseDir, entry.name);
133987
+ const skillMdPath = resolve30(skillDir, "SKILL.md");
133386
133988
  try {
133387
133989
  await readFile13(skillMdPath, "utf-8");
133388
133990
  } catch {
@@ -133412,7 +134014,7 @@ async function readSkillFiles(skillDir) {
133412
134014
  async function walk(dir) {
133413
134015
  const entries = await readdir10(dir, { withFileTypes: true });
133414
134016
  for (const entry of entries) {
133415
- const fullPath = resolve31(dir, entry.name);
134017
+ const fullPath = resolve30(dir, entry.name);
133416
134018
  if (entry.isDirectory()) {
133417
134019
  await walk(fullPath);
133418
134020
  } else {
@@ -133557,7 +134159,7 @@ __export(exports_App, {
133557
134159
  });
133558
134160
  import { randomUUID as randomUUID9 } from "node:crypto";
133559
134161
  import { existsSync as existsSync37, readFileSync as readFileSync21, renameSync as renameSync3, writeFileSync as writeFileSync18 } from "node:fs";
133560
- import { homedir as homedir35, tmpdir as tmpdir6 } from "node:os";
134162
+ import { homedir as homedir36, tmpdir as tmpdir6 } from "node:os";
133561
134163
  import { join as join47, relative as relative17 } from "node:path";
133562
134164
  function deriveReasoningEffort(modelSettings, llmConfig) {
133563
134165
  if (modelSettings && "provider_type" in modelSettings) {
@@ -135896,7 +136498,7 @@ ${newState.originalPrompt}`,
135896
136498
  cancelled = true;
135897
136499
  break;
135898
136500
  }
135899
- await new Promise((resolve32) => setTimeout(resolve32, 100));
136501
+ await new Promise((resolve31) => setTimeout(resolve31, 100));
135900
136502
  }
135901
136503
  buffersRef.current.byId.delete(statusId);
135902
136504
  buffersRef.current.order = buffersRef.current.order.filter((id) => id !== statusId);
@@ -135960,7 +136562,7 @@ ${newState.originalPrompt}`,
135960
136562
  cancelled = true;
135961
136563
  break;
135962
136564
  }
135963
- await new Promise((resolve32) => setTimeout(resolve32, 100));
136565
+ await new Promise((resolve31) => setTimeout(resolve31, 100));
135964
136566
  }
135965
136567
  if (retryStatusId) {
135966
136568
  buffersRef.current.byId.delete(retryStatusId);
@@ -136718,7 +137320,7 @@ ${feedback}
136718
137320
  });
136719
137321
  buffersRef.current.order.push(statusId);
136720
137322
  refreshDerived();
136721
- await new Promise((resolve32) => setTimeout(resolve32, delayMs));
137323
+ await new Promise((resolve31) => setTimeout(resolve31, delayMs));
136722
137324
  buffersRef.current.byId.delete(statusId);
136723
137325
  buffersRef.current.order = buffersRef.current.order.filter((id) => id !== statusId);
136724
137326
  refreshDerived();
@@ -136778,7 +137380,7 @@ ${feedback}
136778
137380
  cancelled = true;
136779
137381
  break;
136780
137382
  }
136781
- await new Promise((resolve32) => setTimeout(resolve32, 100));
137383
+ await new Promise((resolve31) => setTimeout(resolve31, 100));
136782
137384
  }
136783
137385
  if (retryStatusId) {
136784
137386
  buffersRef.current.byId.delete(retryStatusId);
@@ -141721,7 +142323,7 @@ ${guidance}`);
141721
142323
  }
141722
142324
  if (mode === "bypassPermissions") {
141723
142325
  const planFilePath = activePlanPath ?? fallbackPlanPath;
141724
- const plansDir = join47(homedir35(), ".letta", "plans");
142326
+ const plansDir = join47(homedir36(), ".letta", "plans");
141725
142327
  handlePlanKeepPlanning(`You must write your plan to a plan file before exiting plan mode.
141726
142328
  ` + (planFilePath ? `Plan file path: ${planFilePath}
141727
142329
  ` : "") + `Use a write tool to create your plan in ${plansDir}, then use ExitPlanMode to present the plan to the user.`);
@@ -141756,7 +142358,7 @@ ${guidance}`);
141756
142358
  if (!hasUsablePlan) {
141757
142359
  lastAutoHandledExitPlanToolCallIdRef.current = approval.toolCallId;
141758
142360
  const planFilePath = activePlanPath ?? fallbackPlanPath;
141759
- const plansDir = join47(homedir35(), ".letta", "plans");
142361
+ const plansDir = join47(homedir36(), ".letta", "plans");
141760
142362
  handlePlanKeepPlanning(`You must write your plan to a plan file before exiting plan mode.
141761
142363
  ` + (planFilePath ? `Plan file path: ${planFilePath}
141762
142364
  ` : "") + `Use a write tool to create your plan in ${plansDir}, then use ExitPlanMode to present the plan to the user.`);
@@ -143168,8 +143770,8 @@ import {
143168
143770
  readFileSync as readFileSync22,
143169
143771
  writeFileSync as writeFileSync19
143170
143772
  } from "node:fs";
143171
- import { homedir as homedir36, platform as platform6 } from "node:os";
143172
- import { dirname as dirname18, join as join48 } from "node:path";
143773
+ import { homedir as homedir37, platform as platform6 } from "node:os";
143774
+ import { dirname as dirname19, join as join48 } from "node:path";
143173
143775
  function detectTerminalType2() {
143174
143776
  if (process.env.CURSOR_TRACE_ID || process.env.CURSOR_CHANNEL) {
143175
143777
  return "cursor";
@@ -143201,7 +143803,7 @@ function getKeybindingsPath2(terminal) {
143201
143803
  }[terminal];
143202
143804
  const os8 = platform6();
143203
143805
  if (os8 === "darwin") {
143204
- return join48(homedir36(), "Library", "Application Support", appName, "User", "keybindings.json");
143806
+ return join48(homedir37(), "Library", "Application Support", appName, "User", "keybindings.json");
143205
143807
  }
143206
143808
  if (os8 === "win32") {
143207
143809
  const appData = process.env.APPDATA;
@@ -143210,7 +143812,7 @@ function getKeybindingsPath2(terminal) {
143210
143812
  return join48(appData, appName, "User", "keybindings.json");
143211
143813
  }
143212
143814
  if (os8 === "linux") {
143213
- return join48(homedir36(), ".config", appName, "User", "keybindings.json");
143815
+ return join48(homedir37(), ".config", appName, "User", "keybindings.json");
143214
143816
  }
143215
143817
  return null;
143216
143818
  }
@@ -143260,7 +143862,7 @@ function installKeybinding2(keybindingsPath) {
143260
143862
  if (keybindingExists2(keybindingsPath)) {
143261
143863
  return { success: true, alreadyExists: true };
143262
143864
  }
143263
- const parentDir = dirname18(keybindingsPath);
143865
+ const parentDir = dirname19(keybindingsPath);
143264
143866
  if (!existsSync38(parentDir)) {
143265
143867
  mkdirSync24(parentDir, { recursive: true });
143266
143868
  }
@@ -143367,10 +143969,10 @@ function getWezTermConfigPath2() {
143367
143969
  if (existsSync38(xdgPath))
143368
143970
  return xdgPath;
143369
143971
  }
143370
- const configPath = join48(homedir36(), ".config", "wezterm", "wezterm.lua");
143972
+ const configPath = join48(homedir37(), ".config", "wezterm", "wezterm.lua");
143371
143973
  if (existsSync38(configPath))
143372
143974
  return configPath;
143373
- return join48(homedir36(), ".wezterm.lua");
143975
+ return join48(homedir37(), ".wezterm.lua");
143374
143976
  }
143375
143977
  function wezTermDeleteFixExists2(configPath) {
143376
143978
  if (!existsSync38(configPath))
@@ -143419,7 +144021,7 @@ return config`);
143419
144021
  ${WEZTERM_DELETE_FIX2}
143420
144022
  `;
143421
144023
  }
143422
- const parentDir = dirname18(configPath);
144024
+ const parentDir = dirname19(configPath);
143423
144025
  if (!existsSync38(parentDir)) {
143424
144026
  mkdirSync24(parentDir, { recursive: true });
143425
144027
  }
@@ -143468,10 +144070,10 @@ __export(exports_settings2, {
143468
144070
  loadProjectSettings: () => loadProjectSettings2,
143469
144071
  getSetting: () => getSetting2
143470
144072
  });
143471
- import { homedir as homedir37 } from "node:os";
144073
+ import { homedir as homedir38 } from "node:os";
143472
144074
  import { join as join49 } from "node:path";
143473
144075
  function getSettingsPath2() {
143474
- return join49(homedir37(), ".letta", "settings.json");
144076
+ return join49(homedir38(), ".letta", "settings.json");
143475
144077
  }
143476
144078
  async function loadSettings2() {
143477
144079
  const settingsPath = getSettingsPath2();
@@ -143993,10 +144595,10 @@ __export(exports_import2, {
143993
144595
  });
143994
144596
  import { createReadStream as createReadStream2 } from "node:fs";
143995
144597
  import { chmod as chmod2, mkdir as mkdir8, readFile as readFile14, writeFile as writeFile9 } from "node:fs/promises";
143996
- import { dirname as dirname19, resolve as resolve32 } from "node:path";
144598
+ import { dirname as dirname20, resolve as resolve31 } from "node:path";
143997
144599
  async function importAgentFromFile2(options) {
143998
144600
  const client = await getClient();
143999
- const resolvedPath = resolve32(options.filePath);
144601
+ const resolvedPath = resolve31(options.filePath);
144000
144602
  const file = createReadStream2(resolvedPath);
144001
144603
  const importResponse = await client.agents.importFile({
144002
144604
  file,
@@ -144031,7 +144633,7 @@ async function extractSkillsFromAf2(afPath, destDir) {
144031
144633
  return [];
144032
144634
  }
144033
144635
  for (const skill2 of afData.skills) {
144034
- const skillDir = resolve32(destDir, skill2.name);
144636
+ const skillDir = resolve31(destDir, skill2.name);
144035
144637
  await mkdir8(skillDir, { recursive: true });
144036
144638
  if (skill2.files) {
144037
144639
  await writeSkillFiles2(skillDir, skill2.files);
@@ -144051,8 +144653,8 @@ async function writeSkillFiles2(skillDir, files) {
144051
144653
  }
144052
144654
  }
144053
144655
  async function writeSkillFile2(skillDir, filePath, content) {
144054
- const fullPath = resolve32(skillDir, filePath);
144055
- await mkdir8(dirname19(fullPath), { recursive: true });
144656
+ const fullPath = resolve31(skillDir, filePath);
144657
+ await mkdir8(dirname20(fullPath), { recursive: true });
144056
144658
  await writeFile9(fullPath, content, "utf-8");
144057
144659
  const isScript = filePath.startsWith("scripts/") || content.trimStart().startsWith("#!");
144058
144660
  if (isScript) {
@@ -144162,15 +144764,15 @@ __export(exports_memoryFilesystem2, {
144162
144764
  MEMORY_FS_AGENTS_DIR: () => MEMORY_FS_AGENTS_DIR2
144163
144765
  });
144164
144766
  import { existsSync as existsSync39, mkdirSync as mkdirSync25 } from "node:fs";
144165
- import { homedir as homedir38 } from "node:os";
144767
+ import { homedir as homedir39 } from "node:os";
144166
144768
  import { join as join50 } from "node:path";
144167
- function getMemoryFilesystemRoot2(agentId, homeDir = homedir38()) {
144769
+ function getMemoryFilesystemRoot2(agentId, homeDir = homedir39()) {
144168
144770
  return join50(homeDir, MEMORY_FS_ROOT2, MEMORY_FS_AGENTS_DIR2, agentId, MEMORY_FS_MEMORY_DIR2);
144169
144771
  }
144170
- function getMemorySystemDir2(agentId, homeDir = homedir38()) {
144772
+ function getMemorySystemDir2(agentId, homeDir = homedir39()) {
144171
144773
  return join50(getMemoryFilesystemRoot2(agentId, homeDir), MEMORY_SYSTEM_DIR2);
144172
144774
  }
144173
- function ensureMemoryFilesystemDirs2(agentId, homeDir = homedir38()) {
144775
+ function ensureMemoryFilesystemDirs2(agentId, homeDir = homedir39()) {
144174
144776
  const root = getMemoryFilesystemRoot2(agentId, homeDir);
144175
144777
  const systemDir = getMemorySystemDir2(agentId, homeDir);
144176
144778
  if (!existsSync39(root)) {
@@ -148814,9 +149416,9 @@ async function runListenSubcommand(argv) {
148814
149416
 
148815
149417
  // src/cli/subcommands/memfs.ts
148816
149418
  await init_memoryGit();
148817
- import { cpSync, existsSync as existsSync22, mkdirSync as mkdirSync15, rmSync as rmSync2, statSync as statSync8 } from "node:fs";
149419
+ import { cpSync, existsSync as existsSync22, mkdirSync as mkdirSync15, rmSync as rmSync3, statSync as statSync8 } from "node:fs";
148818
149420
  import { readdir as readdir6 } from "node:fs/promises";
148819
- import { homedir as homedir22 } from "node:os";
149421
+ import { homedir as homedir23 } from "node:os";
148820
149422
  import { join as join28 } from "node:path";
148821
149423
  import { parseArgs as parseArgs7 } from "node:util";
148822
149424
  function printUsage4() {
@@ -148862,10 +149464,10 @@ function parseMemfsArgs(argv) {
148862
149464
  });
148863
149465
  }
148864
149466
  function getMemoryRoot(agentId) {
148865
- return join28(homedir22(), ".letta", "agents", agentId, "memory");
149467
+ return join28(homedir23(), ".letta", "agents", agentId, "memory");
148866
149468
  }
148867
149469
  function getAgentRoot(agentId) {
148868
- return join28(homedir22(), ".letta", "agents", agentId);
149470
+ return join28(homedir23(), ".letta", "agents", agentId);
148869
149471
  }
148870
149472
  function formatBackupTimestamp(date = new Date) {
148871
149473
  const pad = (value) => String(value).padStart(2, "0");
@@ -149007,7 +149609,7 @@ async function runMemfsSubcommand(argv) {
149007
149609
  return 1;
149008
149610
  }
149009
149611
  const root = getMemoryRoot(agentId);
149010
- rmSync2(root, { recursive: true, force: true });
149612
+ rmSync3(root, { recursive: true, force: true });
149011
149613
  cpSync(backupPath, root, { recursive: true });
149012
149614
  console.log(JSON.stringify({ restoredFrom: backupPath }, null, 2));
149013
149615
  return 0;
@@ -149414,13 +150016,20 @@ async function runSubcommand(argv) {
149414
150016
  }
149415
150017
 
149416
150018
  // src/permissions/mode.ts
150019
+ init_memoryScope();
149417
150020
  init_readOnlyShell();
149418
150021
  init_shell_command_normalization();
149419
- import { homedir as homedir23 } from "node:os";
149420
- import { isAbsolute as isAbsolute16, join as join29, relative as relative12, resolve as resolve24 } from "node:path";
150022
+ import { homedir as homedir24 } from "node:os";
150023
+ import { isAbsolute as isAbsolute17, join as join29, relative as relative12 } from "node:path";
149421
150024
  var MODE_KEY2 = Symbol.for("@letta/permissionMode");
149422
150025
  var PLAN_FILE_KEY2 = Symbol.for("@letta/planFilePath");
149423
150026
  var MODE_BEFORE_PLAN_KEY2 = Symbol.for("@letta/permissionModeBeforePlan");
150027
+ function everyResolvedTargetIsWithinRoots2(candidatePaths, roots, workingDirectory) {
150028
+ return candidatePaths.length > 0 && candidatePaths.every((path23) => {
150029
+ const resolvedPath = resolveScopedTargetPath(path23, workingDirectory);
150030
+ return resolvedPath ? isPathWithinRoots(resolvedPath, roots) : false;
150031
+ });
150032
+ }
149424
150033
  function getGlobalMode2() {
149425
150034
  const global2 = globalThis;
149426
150035
  if (!global2[MODE_KEY2]) {
@@ -149449,22 +150058,13 @@ function setGlobalModeBeforePlan2(value) {
149449
150058
  global2[MODE_BEFORE_PLAN_KEY2] = value;
149450
150059
  }
149451
150060
  function resolvePlanTargetPath2(targetPath, workingDirectory) {
149452
- const trimmedPath = targetPath.trim();
149453
- if (!trimmedPath)
149454
- return null;
149455
- if (trimmedPath.startsWith("~/")) {
149456
- return resolve24(homedir23(), trimmedPath.slice(2));
149457
- }
149458
- if (isAbsolute16(trimmedPath)) {
149459
- return resolve24(trimmedPath);
149460
- }
149461
- return resolve24(workingDirectory, trimmedPath);
150061
+ return resolveScopedTargetPath(targetPath, workingDirectory);
149462
150062
  }
149463
150063
  function isPathInPlansDir2(path23, plansDir) {
149464
150064
  if (!path23.endsWith(".md"))
149465
150065
  return false;
149466
150066
  const rel = relative12(plansDir, path23);
149467
- return rel !== "" && !rel.startsWith("..") && !isAbsolute16(rel);
150067
+ return rel !== "" && !rel.startsWith("..") && !isAbsolute17(rel);
149468
150068
  }
149469
150069
  function extractApplyPatchPaths2(input) {
149470
150070
  const paths = [];
@@ -149651,7 +150251,7 @@ class PermissionModeManager2 {
149651
150251
  return "allow";
149652
150252
  }
149653
150253
  if (writeTools.includes(toolName)) {
149654
- const plansDir = join29(homedir23(), ".letta", "plans");
150254
+ const plansDir = join29(homedir24(), ".letta", "plans");
149655
150255
  const targetPath = toolArgs?.file_path || toolArgs?.path;
149656
150256
  let candidatePaths = [];
149657
150257
  if ((toolName === "ApplyPatch" || toolName === "apply_patch" || toolName === "memory_apply_patch") && toolArgs?.input) {
@@ -149702,7 +150302,7 @@ class PermissionModeManager2 {
149702
150302
  }
149703
150303
  const planWritePath = extractPlanFileWritePathFromShellCommand2(command);
149704
150304
  if (planWritePath) {
149705
- const plansDir = join29(homedir23(), ".letta", "plans");
150305
+ const plansDir = join29(homedir24(), ".letta", "plans");
149706
150306
  const resolvedPath = resolvePlanTargetPath2(planWritePath, workingDirectory);
149707
150307
  if (resolvedPath && isPathInPlansDir2(resolvedPath, plansDir)) {
149708
150308
  return "allow";
@@ -149711,6 +150311,94 @@ class PermissionModeManager2 {
149711
150311
  }
149712
150312
  return "deny";
149713
150313
  }
150314
+ case "memory": {
150315
+ const allowedMemoryRoots = resolveAllowedMemoryRoots().roots;
150316
+ const allowedReadOnlyTools = [
150317
+ "Read",
150318
+ "Glob",
150319
+ "Grep",
150320
+ "NotebookRead",
150321
+ "ViewImage",
150322
+ "view_image",
150323
+ "TaskOutput",
150324
+ "task_output",
150325
+ "Skill",
150326
+ "skill",
150327
+ "read_file",
150328
+ "list_dir",
150329
+ "grep_files",
150330
+ "ReadFile",
150331
+ "ListDir",
150332
+ "GrepFiles",
150333
+ "read_file_gemini",
150334
+ "glob_gemini",
150335
+ "list_directory",
150336
+ "search_file_content",
150337
+ "read_many_files",
150338
+ "ReadFileGemini",
150339
+ "GlobGemini",
150340
+ "ListDirectory",
150341
+ "SearchFileContent",
150342
+ "ReadManyFiles"
150343
+ ];
150344
+ const writeTools = [
150345
+ "Write",
150346
+ "Edit",
150347
+ "MultiEdit",
150348
+ "NotebookEdit",
150349
+ "apply_patch",
150350
+ "ApplyPatch",
150351
+ "replace",
150352
+ "Replace",
150353
+ "write_file",
150354
+ "WriteFile",
150355
+ "write_file_gemini",
150356
+ "WriteFileGemini"
150357
+ ];
150358
+ const shellTools = [
150359
+ "Bash",
150360
+ "shell",
150361
+ "Shell",
150362
+ "shell_command",
150363
+ "ShellCommand",
150364
+ "run_shell_command",
150365
+ "RunShellCommand",
150366
+ "run_shell_command_gemini",
150367
+ "RunShellCommandGemini"
150368
+ ];
150369
+ if (allowedReadOnlyTools.includes(toolName)) {
150370
+ return "allow";
150371
+ }
150372
+ if (toolName === "memory_apply_patch") {
150373
+ return allowedMemoryRoots.length > 0 ? "allow" : "deny";
150374
+ }
150375
+ if (writeTools.includes(toolName)) {
150376
+ const targetPath = toolArgs?.file_path || toolArgs?.path;
150377
+ let candidatePaths = [];
150378
+ if ((toolName === "ApplyPatch" || toolName === "apply_patch") && toolArgs?.input) {
150379
+ candidatePaths = extractApplyPatchPaths2(toolArgs.input);
150380
+ } else if (typeof targetPath === "string") {
150381
+ candidatePaths = [targetPath];
150382
+ }
150383
+ if (allowedMemoryRoots.length > 0 && everyResolvedTargetIsWithinRoots2(candidatePaths, allowedMemoryRoots, workingDirectory)) {
150384
+ return "allow";
150385
+ }
150386
+ return "deny";
150387
+ }
150388
+ if (shellTools.includes(toolName)) {
150389
+ const command = toolArgs?.command;
150390
+ if (command && isReadOnlyShellCommand(command, { allowExternalPaths: true })) {
150391
+ return "allow";
150392
+ }
150393
+ if (command && allowedMemoryRoots.length > 0 && isScopedMemoryShellCommand(command, allowedMemoryRoots, {
150394
+ workingDirectory
150395
+ })) {
150396
+ return "allow";
150397
+ }
150398
+ return "deny";
150399
+ }
150400
+ return "deny";
150401
+ }
149714
150402
  case "default":
149715
150403
  return null;
149716
150404
  default:
@@ -149733,8 +150421,8 @@ await __promiseAll([
149733
150421
  init_secrets()
149734
150422
  ]);
149735
150423
  import { randomUUID as randomUUID4 } from "node:crypto";
149736
- import { homedir as homedir24 } from "node:os";
149737
- import { join as join30, resolve as resolve25 } from "node:path";
150424
+ import { homedir as homedir25 } from "node:os";
150425
+ import { join as join30, resolve as resolve24 } from "node:path";
149738
150426
  var DEFAULT_SETTINGS3 = {
149739
150427
  lastAgent: null,
149740
150428
  tokenStreaming: false,
@@ -150120,7 +150808,7 @@ class SettingsManager2 {
150120
150808
  if (!this.settings)
150121
150809
  return;
150122
150810
  const settingsPath = this.getSettingsPath();
150123
- const home = process.env.HOME || homedir24();
150811
+ const home = process.env.HOME || homedir25();
150124
150812
  const dirPath = join30(home, ".letta");
150125
150813
  try {
150126
150814
  if (!exists(dirPath)) {
@@ -150181,14 +150869,14 @@ class SettingsManager2 {
150181
150869
  }
150182
150870
  }
150183
150871
  getSettingsPath() {
150184
- const home = process.env.HOME || homedir24();
150872
+ const home = process.env.HOME || homedir25();
150185
150873
  return join30(home, ".letta", "settings.json");
150186
150874
  }
150187
150875
  getProjectSettingsPath(workingDirectory) {
150188
150876
  return join30(workingDirectory, ".letta", "settings.json");
150189
150877
  }
150190
150878
  isProjectSettingsPathCollidingWithGlobal(workingDirectory) {
150191
- return resolve25(this.getProjectSettingsPath(workingDirectory)) === resolve25(this.getSettingsPath());
150879
+ return resolve24(this.getProjectSettingsPath(workingDirectory)) === resolve24(this.getSettingsPath());
150192
150880
  }
150193
150881
  getLocalProjectSettingsPath(workingDirectory) {
150194
150882
  return join30(workingDirectory, ".letta", "settings.local.json");
@@ -151150,8 +151838,8 @@ function acquireSwitchLock2() {
151150
151838
  const lock = getSwitchLock2();
151151
151839
  lock.refCount++;
151152
151840
  if (lock.refCount === 1) {
151153
- lock.promise = new Promise((resolve26) => {
151154
- lock.resolve = resolve26;
151841
+ lock.promise = new Promise((resolve25) => {
151842
+ lock.resolve = resolve25;
151155
151843
  });
151156
151844
  }
151157
151845
  }
@@ -151636,7 +152324,7 @@ Note: Flags should use double dashes for full names (e.g., --yolo, not -yolo)`);
151636
152324
  printHelp();
151637
152325
  const helpDelayMs = Number.parseInt(process.env.LETTA_TEST_HELP_EXIT_DELAY_MS ?? "", 10);
151638
152326
  if (Number.isFinite(helpDelayMs) && helpDelayMs > 0) {
151639
- await new Promise((resolve33) => setTimeout(resolve33, helpDelayMs));
152327
+ await new Promise((resolve32) => setTimeout(resolve32, helpDelayMs));
151640
152328
  }
151641
152329
  process.exit(0);
151642
152330
  }
@@ -151858,9 +152546,9 @@ Note: Flags should use double dashes for full names (e.g., --yolo, not -yolo)`);
151858
152546
  process.exit(1);
151859
152547
  }
151860
152548
  } else {
151861
- const { resolve: resolve33 } = await import("path");
152549
+ const { resolve: resolve32 } = await import("path");
151862
152550
  const { existsSync: existsSync40 } = await import("fs");
151863
- const resolvedPath = resolve33(fromAfFile);
152551
+ const resolvedPath = resolve32(fromAfFile);
151864
152552
  if (!existsSync40(resolvedPath)) {
151865
152553
  console.error(`Error: AgentFile not found: ${resolvedPath}`);
151866
152554
  process.exit(1);
@@ -151971,6 +152659,7 @@ Error: ${message}`);
151971
152659
  "default",
151972
152660
  "acceptEdits",
151973
152661
  "plan",
152662
+ "memory",
151974
152663
  "bypassPermissions"
151975
152664
  ];
151976
152665
  if (validModes.includes(mode)) {
@@ -152735,4 +153424,4 @@ Error during initialization: ${message}`);
152735
153424
  }
152736
153425
  main();
152737
153426
 
152738
- //# debugId=AA1793542E1A081B64756E2164756E21
153427
+ //# debugId=9A1A4B9B002D45C364756E2164756E21