@letta-ai/letta-code 0.23.8 → 0.23.9

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.23.8",
3272
+ version: "0.23.9",
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: {
@@ -4751,7 +4751,10 @@ If you experience this issue multiple times, move ~/.letta to ~/.letta_backup, a
4751
4751
  timeout: Number(process.env.LETTA_REQUEST_TIMEOUT_MS) || 10 * 60 * 1000,
4752
4752
  defaultHeaders: {
4753
4753
  "X-Letta-Source": "letta-code",
4754
- "User-Agent": `letta-code/${package_default.version}`
4754
+ "User-Agent": `letta-code/${package_default.version}`,
4755
+ ...process.env.LETTA_NODE === "1" && {
4756
+ "x-letta-node": "1"
4757
+ }
4755
4758
  },
4756
4759
  ...isTimingsEnabled() && { fetch: createTimingFetch(fetch) }
4757
4760
  });
@@ -9936,6 +9939,17 @@ var init_models2 = __esm(() => {
9936
9939
  parallel_tool_calls: true
9937
9940
  }
9938
9941
  },
9942
+ {
9943
+ id: "kimi-k2.6",
9944
+ handle: "openrouter/moonshotai/kimi-k2.6",
9945
+ label: "Kimi K2.6",
9946
+ description: "Moonshot AI's next-gen multimodal coding and agent model",
9947
+ isFeatured: true,
9948
+ updateArgs: {
9949
+ context_window: 200000,
9950
+ parallel_tool_calls: true
9951
+ }
9952
+ },
9939
9953
  {
9940
9954
  id: "deepseek-chat-v3.1",
9941
9955
  handle: "openrouter/deepseek/deepseek-chat-v3.1",
@@ -38755,7 +38769,9 @@ __export(exports_memoryGit, {
38755
38769
  getGitRemoteUrl: () => getGitRemoteUrl,
38756
38770
  getAgentRootDir: () => getAgentRootDir,
38757
38771
  formatGitCredentialHelperPath: () => formatGitCredentialHelperPath,
38772
+ commitAndSyncMemoryWrite: () => commitAndSyncMemoryWrite,
38758
38773
  cloneMemoryRepo: () => cloneMemoryRepo,
38774
+ assertMemoryRepoReadyForWrite: () => assertMemoryRepoReadyForWrite,
38759
38775
  addGitMemoryTag: () => addGitMemoryTag,
38760
38776
  PRE_COMMIT_HOOK_SCRIPT: () => PRE_COMMIT_HOOK_SCRIPT,
38761
38777
  GIT_MEMORY_ENABLED_TAG: () => GIT_MEMORY_ENABLED_TAG
@@ -38930,6 +38946,185 @@ function installPreCommitHook(dir) {
38930
38946
  chmodSync(hookPath, 493);
38931
38947
  debugLog("memfs-git", "Installed pre-commit hook");
38932
38948
  }
38949
+ function normalizePathspecs(pathspecs) {
38950
+ return Array.from(new Set(pathspecs)).filter((path2) => path2.trim().length > 0);
38951
+ }
38952
+ function isNonFastForwardPushError(error) {
38953
+ const message = error instanceof Error ? error.message : String(error);
38954
+ return NON_FAST_FORWARD_PUSH_ERROR_RE.test(message);
38955
+ }
38956
+ async function prepareMemoryRepoForGitOps(memoryDir, agentId, token) {
38957
+ await maybeUpdateMemoryRemoteOrigin(memoryDir, agentId);
38958
+ await configureLocalCredentialHelper(memoryDir, token);
38959
+ installPreCommitHook(memoryDir);
38960
+ }
38961
+ async function stageMemoryPaths(memoryDir, pathspecs) {
38962
+ if (pathspecs.length === 0) {
38963
+ return;
38964
+ }
38965
+ await runGit(memoryDir, ["add", "-A", "--", ...pathspecs]);
38966
+ }
38967
+ async function hasStagedMemoryChanges(memoryDir, pathspecs) {
38968
+ if (pathspecs.length === 0) {
38969
+ return false;
38970
+ }
38971
+ const status = await runGit(memoryDir, [
38972
+ "status",
38973
+ "--porcelain",
38974
+ "--",
38975
+ ...pathspecs
38976
+ ]);
38977
+ return status.stdout.trim().length > 0;
38978
+ }
38979
+ async function commitMemoryPaths(memoryDir, pathspecs, reason, author) {
38980
+ const normalizedPathspecs = normalizePathspecs(pathspecs);
38981
+ await stageMemoryPaths(memoryDir, normalizedPathspecs);
38982
+ if (!await hasStagedMemoryChanges(memoryDir, normalizedPathspecs)) {
38983
+ return { committed: false };
38984
+ }
38985
+ try {
38986
+ await runGit(memoryDir, [
38987
+ "-c",
38988
+ `user.name=${author.authorName.trim() || author.agentId}`,
38989
+ "-c",
38990
+ `user.email=${author.authorEmail}`,
38991
+ "commit",
38992
+ "-m",
38993
+ reason
38994
+ ]);
38995
+ } catch (error) {
38996
+ await unstageMemoryPaths(memoryDir, normalizedPathspecs);
38997
+ throw error;
38998
+ }
38999
+ const head = await runGit(memoryDir, ["rev-parse", "HEAD"]);
39000
+ return {
39001
+ committed: true,
39002
+ sha: head.stdout.trim()
39003
+ };
39004
+ }
39005
+ async function unstageMemoryPaths(memoryDir, pathspecs) {
39006
+ if (pathspecs.length === 0) {
39007
+ return;
39008
+ }
39009
+ try {
39010
+ await runGit(memoryDir, ["reset", "HEAD", "--", ...pathspecs]);
39011
+ } catch {}
39012
+ }
39013
+ async function fetchMemoryRemote(memoryDir, token) {
39014
+ await runGitWithRetry(memoryDir, ["fetch", "origin"], token, {
39015
+ operation: "fetch origin"
39016
+ });
39017
+ }
39018
+ async function resetMemoryToUpstream(memoryDir, token) {
39019
+ await runGit(memoryDir, ["reset", "--hard", "@{u}"], token);
39020
+ }
39021
+ function buildMemoryConflictRef(sha) {
39022
+ const timestamp = new Date().toISOString().replace(/[-:TZ.]/g, "").slice(0, 14);
39023
+ return `refs/letta-conflicts/${timestamp}-${sha.slice(0, 7)}`;
39024
+ }
39025
+ async function preserveMemoryCommit(memoryDir, sha) {
39026
+ const ref = buildMemoryConflictRef(sha);
39027
+ await runGit(memoryDir, ["update-ref", ref, sha]);
39028
+ return ref;
39029
+ }
39030
+ function formatCommittedButPushFailed(sha, error) {
39031
+ const message = error instanceof Error ? error.message : String(error);
39032
+ return `Memory changes were committed (${sha.slice(0, 7)}) but push failed: ${message}`;
39033
+ }
39034
+ function formatReplayConflict(sha, rescueRef, error) {
39035
+ const message = error instanceof Error ? error.message : String(error);
39036
+ return `Memory changes conflicted with newer remote memory and could not be replayed safely. Preserved local commit ${sha.slice(0, 7)} at ${rescueRef}; local branch was reset to upstream. Replay error: ${message}`;
39037
+ }
39038
+ function formatReplayPushFailure(originalSha, originalRef, replaySha, replayRef, error) {
39039
+ const message = error instanceof Error ? error.message : String(error);
39040
+ const replaySummary = replaySha && replayRef ? ` Replayed commit ${replaySha.slice(0, 7)} was preserved at ${replayRef}.` : "";
39041
+ return `Memory changes conflicted with newer remote memory and the replayed update could not be pushed safely. Original commit ${originalSha.slice(0, 7)} was preserved at ${originalRef}.${replaySummary} Local branch was reset to upstream. Push error: ${message}`;
39042
+ }
39043
+ async function recoverMemoryPushConflict(params, token, initialSha) {
39044
+ const rescueRef = await preserveMemoryCommit(params.memoryDir, initialSha);
39045
+ await fetchMemoryRemote(params.memoryDir, token);
39046
+ await resetMemoryToUpstream(params.memoryDir, token);
39047
+ let replayedPathspecs = [];
39048
+ try {
39049
+ replayedPathspecs = normalizePathspecs(await params.replay?.() ?? []);
39050
+ } catch (error) {
39051
+ await resetMemoryToUpstream(params.memoryDir, token);
39052
+ throw new Error(formatReplayConflict(initialSha, rescueRef, error));
39053
+ }
39054
+ let replayCommit;
39055
+ try {
39056
+ replayCommit = await commitMemoryPaths(params.memoryDir, replayedPathspecs, params.reason, params.author);
39057
+ } catch (error) {
39058
+ await resetMemoryToUpstream(params.memoryDir, token);
39059
+ throw new Error(formatReplayConflict(initialSha, rescueRef, error));
39060
+ }
39061
+ if (!replayCommit.committed) {
39062
+ return {
39063
+ committed: true,
39064
+ replayed: true,
39065
+ replayNoop: true,
39066
+ rescueRef
39067
+ };
39068
+ }
39069
+ try {
39070
+ await runGit(params.memoryDir, ["push"], token);
39071
+ } catch (error) {
39072
+ const replayRef = replayCommit.sha ? await preserveMemoryCommit(params.memoryDir, replayCommit.sha) : undefined;
39073
+ await resetMemoryToUpstream(params.memoryDir, token);
39074
+ throw new Error(formatReplayPushFailure(initialSha, rescueRef, replayCommit.sha, replayRef, error));
39075
+ }
39076
+ return {
39077
+ committed: true,
39078
+ sha: replayCommit.sha,
39079
+ replayed: true,
39080
+ rescueRef
39081
+ };
39082
+ }
39083
+ async function assertMemoryRepoReadyForWrite(memoryDir) {
39084
+ const status = await runGit(memoryDir, ["status", "--porcelain"]);
39085
+ if (status.stdout.trim().length > 0) {
39086
+ throw new Error("Memory repo has uncommitted changes. Commit, discard, or sync them before using memory tools.");
39087
+ }
39088
+ try {
39089
+ const { stdout } = await runGit(memoryDir, [
39090
+ "rev-list",
39091
+ "--count",
39092
+ "@{u}..HEAD"
39093
+ ]);
39094
+ const aheadCount = parseInt(stdout.trim(), 10);
39095
+ if (aheadCount > 0) {
39096
+ throw new Error("Memory repo has local commits that are not pushed to remote. Sync the repo before using memory tools.");
39097
+ }
39098
+ } catch (error) {
39099
+ if (error instanceof Error && error.message.includes("not pushed to remote")) {
39100
+ throw error;
39101
+ }
39102
+ }
39103
+ }
39104
+ async function commitAndSyncMemoryWrite(params) {
39105
+ const normalizedPathspecs = normalizePathspecs(params.pathspecs);
39106
+ if (normalizedPathspecs.length === 0) {
39107
+ return { committed: false };
39108
+ }
39109
+ const token = await getAuthToken();
39110
+ await prepareMemoryRepoForGitOps(params.memoryDir, params.author.agentId, token);
39111
+ const commitResult = await commitMemoryPaths(params.memoryDir, normalizedPathspecs, params.reason, params.author);
39112
+ if (!commitResult.committed || !commitResult.sha) {
39113
+ return { committed: false };
39114
+ }
39115
+ try {
39116
+ await runGit(params.memoryDir, ["push"], token);
39117
+ } catch (error) {
39118
+ if (!params.replay || !isNonFastForwardPushError(error)) {
39119
+ throw new Error(formatCommittedButPushFailed(commitResult.sha, error));
39120
+ }
39121
+ return recoverMemoryPushConflict(params, token, commitResult.sha);
39122
+ }
39123
+ return {
39124
+ committed: true,
39125
+ sha: commitResult.sha
39126
+ };
39127
+ }
38933
39128
  function isGitRepo(agentId) {
38934
39129
  return existsSync8(join7(getMemoryRepoDir(agentId), ".git"));
38935
39130
  }
@@ -39000,25 +39195,8 @@ Hint: verify remote and auth:
39000
39195
  async function pushMemory(agentId) {
39001
39196
  const token = await getAuthToken();
39002
39197
  const dir = getMemoryRepoDir(agentId);
39003
- await maybeUpdateMemoryRemoteOrigin(dir, agentId);
39004
- await configureLocalCredentialHelper(dir, token);
39005
- try {
39006
- await runGit(dir, ["push"], token);
39007
- return;
39008
- } catch (pushError) {
39009
- debugWarn("memfs-git", `Push failed, attempting pull --rebase: ${pushError instanceof Error ? pushError.message : String(pushError)}`);
39010
- }
39011
- try {
39012
- await runGit(dir, ["pull", "--rebase"], token);
39013
- } catch (pullError) {
39014
- debugWarn("memfs-git", `Pull --rebase also failed (remote may be empty): ${pullError instanceof Error ? pullError.message : String(pullError)}`);
39015
- return;
39016
- }
39017
- try {
39018
- await runGit(dir, ["push"], token);
39019
- } catch (retryError) {
39020
- debugWarn("memfs-git", `Push failed after rebase: ${retryError instanceof Error ? retryError.message : String(retryError)}`);
39021
- }
39198
+ await prepareMemoryRepoForGitOps(dir, agentId, token);
39199
+ await runGit(dir, ["push"], token);
39022
39200
  }
39023
39201
  async function getMemoryGitStatus(agentId) {
39024
39202
  const dir = getMemoryRepoDir(agentId);
@@ -39079,7 +39257,7 @@ async function removeGitMemoryTag(agentId) {
39079
39257
  debugWarn("memfs-git", `Failed to remove git-memory tag: ${err instanceof Error ? err.message : String(err)}`);
39080
39258
  }
39081
39259
  }
39082
- var execFile, GIT_MEMORY_ENABLED_TAG = "git-memory-enabled", RETRYABLE_GIT_HTTP_ERROR_RE, RETRYABLE_GIT_NETWORK_ERROR_RE, MISSING_CWD_GIT_ERROR_RE, PRE_COMMIT_HOOK_SCRIPT = `#!/usr/bin/env bash
39260
+ var execFile, GIT_MEMORY_ENABLED_TAG = "git-memory-enabled", RETRYABLE_GIT_HTTP_ERROR_RE, RETRYABLE_GIT_NETWORK_ERROR_RE, MISSING_CWD_GIT_ERROR_RE, NON_FAST_FORWARD_PUSH_ERROR_RE, PRE_COMMIT_HOOK_SCRIPT = `#!/usr/bin/env bash
39083
39261
  # Validate frontmatter in staged memory .md files
39084
39262
  # Installed by Letta Code CLI
39085
39263
 
@@ -39224,6 +39402,7 @@ var init_memoryGit = __esm(() => {
39224
39402
  RETRYABLE_GIT_HTTP_ERROR_RE = /(?:\bHTTP\s+(?:520|521|522|523|524)\b|The requested URL returned error:\s*(?:520|521|522|523|524))/i;
39225
39403
  RETRYABLE_GIT_NETWORK_ERROR_RE = /(remote end hung up unexpectedly|connection reset by peer|operation timed out|timed out)/i;
39226
39404
  MISSING_CWD_GIT_ERROR_RE = /(Unable to read current working directory: No such file or directory|\buv_cwd\b|\bcwd\b.*\bENOENT\b)/i;
39405
+ NON_FAST_FORWARD_PUSH_ERROR_RE = /(non-fast-forward|fetch first|failed to push some refs|updates were rejected|remote contains work that you do not have locally|tip of your current branch is behind)/i;
39227
39406
  });
39228
39407
 
39229
39408
  // src/agent/personality.ts
@@ -39492,33 +39671,36 @@ function detectPersonalityFromPersonaFile(personaFileContent) {
39492
39671
  }
39493
39672
  return null;
39494
39673
  }
39495
- function isExitCode(error, expectedCode) {
39496
- if (!error || typeof error !== "object")
39497
- return false;
39498
- const code = error.code;
39499
- return code === expectedCode;
39500
- }
39501
- async function runGit2(cwd2, args) {
39502
- const result = await execFile2("git", args, {
39503
- cwd: cwd2,
39504
- maxBuffer: 10485760,
39505
- timeout: 60000
39506
- });
39507
- return result.stdout?.toString() ?? "";
39508
- }
39509
- async function hasStagedChanges(cwd2, relativePaths) {
39510
- if (relativePaths.length === 0) {
39511
- return false;
39512
- }
39674
+ async function getMemoryCommitAuthor(agentId) {
39675
+ let authorName = agentId;
39513
39676
  try {
39514
- await runGit2(cwd2, ["diff", "--cached", "--quiet", "--", ...relativePaths]);
39515
- return false;
39516
- } catch (error) {
39517
- if (isExitCode(error, 1)) {
39518
- return true;
39677
+ const client = await getClient();
39678
+ const agent = await client.agents.retrieve(agentId);
39679
+ if (agent.name?.trim()) {
39680
+ authorName = agent.name.trim();
39519
39681
  }
39520
- throw error;
39682
+ } catch {}
39683
+ return {
39684
+ agentId,
39685
+ authorName,
39686
+ authorEmail: `${agentId}@letta.com`
39687
+ };
39688
+ }
39689
+ function applyPersonalityFiles(filesToUpdate) {
39690
+ const changedPaths = [];
39691
+ for (const file of filesToUpdate) {
39692
+ const existingContent = existsSync9(file.absolutePath) ? readFileSync6(file.absolutePath, "utf-8") : null;
39693
+ const nextContent = existingContent ? replaceBodyPreservingFrontmatter(existingContent, file.content, {
39694
+ description: file.description
39695
+ }) : buildDefaultMemoryFile(file.templatePromptAssetName, file.content, file.description);
39696
+ if (existingContent !== null && normalizeComparableContent(existingContent) === normalizeComparableContent(nextContent)) {
39697
+ continue;
39698
+ }
39699
+ mkdirSync7(dirname3(file.absolutePath), { recursive: true });
39700
+ writeFileSync5(file.absolutePath, nextContent, "utf-8");
39701
+ changedPaths.push(file.relativePath);
39521
39702
  }
39703
+ return changedPaths;
39522
39704
  }
39523
39705
  async function applyPersonalityToMemory(params) {
39524
39706
  const personality = getPersonalityOption(params.personalityId);
@@ -39552,19 +39734,7 @@ async function applyPersonalityToMemory(params) {
39552
39734
  description: blockDefinitions.human.description
39553
39735
  }
39554
39736
  ];
39555
- const changedPaths = [];
39556
- for (const file of filesToUpdate) {
39557
- const existingContent = existsSync9(file.absolutePath) ? readFileSync6(file.absolutePath, "utf-8") : null;
39558
- const nextContent = existingContent ? replaceBodyPreservingFrontmatter(existingContent, file.content, {
39559
- description: file.description
39560
- }) : buildDefaultMemoryFile(file.templatePromptAssetName, file.content, file.description);
39561
- if (existingContent !== null && normalizeComparableContent(existingContent) === normalizeComparableContent(nextContent)) {
39562
- continue;
39563
- }
39564
- mkdirSync7(dirname3(file.absolutePath), { recursive: true });
39565
- writeFileSync5(file.absolutePath, nextContent, "utf-8");
39566
- changedPaths.push(file.relativePath);
39567
- }
39737
+ const changedPaths = applyPersonalityFiles(filesToUpdate);
39568
39738
  if (changedPaths.length === 0) {
39569
39739
  return {
39570
39740
  changed: false,
@@ -39573,8 +39743,16 @@ async function applyPersonalityToMemory(params) {
39573
39743
  humanRelativePath
39574
39744
  };
39575
39745
  }
39576
- await runGit2(repoDir, ["add", "--", ...changedPaths]);
39577
- if (!await hasStagedChanges(repoDir, changedPaths)) {
39746
+ const commitMessage = params.commitMessage ?? `chore(personality): switch to ${personality.label}`;
39747
+ const author = await getMemoryCommitAuthor(params.agentId);
39748
+ const commitResult = await commitAndSyncMemoryWrite({
39749
+ memoryDir: repoDir,
39750
+ pathspecs: changedPaths,
39751
+ reason: commitMessage,
39752
+ author,
39753
+ replay: async () => applyPersonalityFiles(filesToUpdate)
39754
+ });
39755
+ if (!commitResult.committed) {
39578
39756
  return {
39579
39757
  changed: false,
39580
39758
  personality,
@@ -39582,16 +39760,6 @@ async function applyPersonalityToMemory(params) {
39582
39760
  humanRelativePath
39583
39761
  };
39584
39762
  }
39585
- const commitMessage = params.commitMessage ?? `chore(personality): switch to ${personality.label}`;
39586
- await runGit2(repoDir, [
39587
- "commit",
39588
- "--only",
39589
- "-m",
39590
- commitMessage,
39591
- "--",
39592
- ...changedPaths
39593
- ]);
39594
- await pushMemory(params.agentId);
39595
39763
  return {
39596
39764
  changed: true,
39597
39765
  personality,
@@ -39603,6 +39771,7 @@ async function applyPersonalityToMemory(params) {
39603
39771
  var execFile2, PRIMARY_PERSONA_RELATIVE_PATH = "system/persona.md", LEGACY_PERSONA_RELATIVE_PATH = "memory/system/persona.md", PRIMARY_HUMAN_RELATIVE_PATH = "system/human.md", LEGACY_HUMAN_RELATIVE_PATH = "memory/system/human.md", PERSONALITY_OPTIONS, DEFAULT_CREATE_AGENT_PERSONALITIES, PERSONALITY_ALIASES, FRONTMATTER_REGEX, EDITABLE_FRONTMATTER_KEYS;
39604
39772
  var init_personality = __esm(() => {
39605
39773
  init_settings_manager();
39774
+ init_client2();
39606
39775
  init_memory();
39607
39776
  init_memoryGit();
39608
39777
  init_promptAssets();
@@ -43368,7 +43537,7 @@ var init_targets = __esm(() => {
43368
43537
 
43369
43538
  // src/cli/helpers/gitContext.ts
43370
43539
  import { execFileSync } from "node:child_process";
43371
- function runGit3(args, cwd2) {
43540
+ function runGit2(args, cwd2) {
43372
43541
  try {
43373
43542
  return execFileSync("git", args, {
43374
43543
  cwd: cwd2,
@@ -43401,7 +43570,7 @@ function formatGitUser(name, email) {
43401
43570
  function gatherGitContextSnapshot(options = {}) {
43402
43571
  const cwd2 = options.cwd ?? process.cwd();
43403
43572
  const recentCommitLimit = options.recentCommitLimit ?? 3;
43404
- if (!runGit3(["rev-parse", "--git-dir"], cwd2)) {
43573
+ if (!runGit2(["rev-parse", "--git-dir"], cwd2)) {
43405
43574
  return {
43406
43575
  isGitRepo: false,
43407
43576
  branch: null,
@@ -43410,16 +43579,16 @@ function gatherGitContextSnapshot(options = {}) {
43410
43579
  gitUser: null
43411
43580
  };
43412
43581
  }
43413
- const branch = runGit3(["branch", "--show-current"], cwd2);
43414
- const fullStatus = runGit3(["status", "--short"], cwd2);
43582
+ const branch = runGit2(["branch", "--show-current"], cwd2);
43583
+ const fullStatus = runGit2(["status", "--short"], cwd2);
43415
43584
  const status = typeof fullStatus === "string" && options.statusLineLimit ? truncateLines(fullStatus, options.statusLineLimit) : fullStatus;
43416
- const recentCommits = options.recentCommitFormat ? runGit3([
43585
+ const recentCommits = options.recentCommitFormat ? runGit2([
43417
43586
  "log",
43418
43587
  `--format=${options.recentCommitFormat}`,
43419
43588
  "-n",
43420
43589
  String(recentCommitLimit)
43421
- ], cwd2) : runGit3(["log", "--oneline", "-n", String(recentCommitLimit)], cwd2);
43422
- const userConfig = runGit3(["config", "--get-regexp", "^user\\.(name|email)$"], cwd2);
43590
+ ], cwd2) : runGit2(["log", "--oneline", "-n", String(recentCommitLimit)], cwd2);
43591
+ const userConfig = runGit2(["config", "--get-regexp", "^user\\.(name|email)$"], cwd2);
43423
43592
  let userName = null;
43424
43593
  let userEmail = null;
43425
43594
  if (userConfig) {
@@ -43441,11 +43610,11 @@ function gatherGitContextSnapshot(options = {}) {
43441
43610
  };
43442
43611
  }
43443
43612
  function getGitContext(cwd2) {
43444
- if (!runGit3(["rev-parse", "--git-dir"], cwd2)) {
43613
+ if (!runGit2(["rev-parse", "--git-dir"], cwd2)) {
43445
43614
  return null;
43446
43615
  }
43447
- const branch = runGit3(["branch", "--show-current"], cwd2);
43448
- const branchList = runGit3([
43616
+ const branch = runGit2(["branch", "--show-current"], cwd2);
43617
+ const branchList = runGit2([
43449
43618
  "branch",
43450
43619
  "--sort=-committerdate",
43451
43620
  "--format=%(refname:short)",
@@ -46325,6 +46494,100 @@ var init_hooks = __esm(() => {
46325
46494
  init_types2();
46326
46495
  });
46327
46496
 
46497
+ // src/permissions/canonical.ts
46498
+ function canonicalToolName(toolName) {
46499
+ if (SHELL_TOOL_NAMES.has(toolName))
46500
+ return "Bash";
46501
+ if (READ_TOOL_NAMES.has(toolName))
46502
+ return "Read";
46503
+ if (WRITE_TOOL_NAMES.has(toolName))
46504
+ return "Write";
46505
+ if (EDIT_TOOL_NAMES.has(toolName))
46506
+ return "Edit";
46507
+ if (GLOB_TOOL_NAMES.has(toolName))
46508
+ return "Glob";
46509
+ if (GREP_TOOL_NAMES.has(toolName))
46510
+ return "Grep";
46511
+ if (LIST_TOOL_NAMES.has(toolName))
46512
+ return "ListDir";
46513
+ if (TASK_TOOL_NAMES.has(toolName))
46514
+ return "Task";
46515
+ return toolName;
46516
+ }
46517
+ function isShellToolName(toolName) {
46518
+ return canonicalToolName(toolName) === "Bash";
46519
+ }
46520
+ function isFileToolName(toolName) {
46521
+ return FILE_TOOL_FAMILIES.has(canonicalToolName(toolName));
46522
+ }
46523
+ function canonicalizePathLike(value) {
46524
+ let normalized = value.replace(/\\/g, "/").trim();
46525
+ if (/^\/+[a-zA-Z]:\//.test(normalized)) {
46526
+ normalized = normalized.replace(/^\/+/, "");
46527
+ }
46528
+ if (/^[a-zA-Z]:\//.test(normalized)) {
46529
+ normalized = `${normalized[0]?.toUpperCase() ?? ""}${normalized.slice(1)}`;
46530
+ }
46531
+ return normalized;
46532
+ }
46533
+ var SHELL_TOOL_NAMES, READ_TOOL_NAMES, WRITE_TOOL_NAMES, EDIT_TOOL_NAMES, GLOB_TOOL_NAMES, GREP_TOOL_NAMES, LIST_TOOL_NAMES, TASK_TOOL_NAMES, FILE_TOOL_FAMILIES;
46534
+ var init_canonical = __esm(() => {
46535
+ SHELL_TOOL_NAMES = new Set([
46536
+ "Bash",
46537
+ "shell",
46538
+ "Shell",
46539
+ "shell_command",
46540
+ "ShellCommand",
46541
+ "run_shell_command",
46542
+ "RunShellCommand"
46543
+ ]);
46544
+ READ_TOOL_NAMES = new Set([
46545
+ "Read",
46546
+ "read_file",
46547
+ "ReadFile",
46548
+ "read_file_gemini",
46549
+ "ReadFileGemini"
46550
+ ]);
46551
+ WRITE_TOOL_NAMES = new Set([
46552
+ "Write",
46553
+ "write_file",
46554
+ "WriteFile",
46555
+ "write_file_gemini",
46556
+ "WriteFileGemini"
46557
+ ]);
46558
+ EDIT_TOOL_NAMES = new Set([
46559
+ "Edit",
46560
+ "MultiEdit",
46561
+ "NotebookEdit",
46562
+ "replace",
46563
+ "Replace"
46564
+ ]);
46565
+ GLOB_TOOL_NAMES = new Set(["Glob", "glob_gemini", "GlobGemini"]);
46566
+ GREP_TOOL_NAMES = new Set([
46567
+ "Grep",
46568
+ "grep_files",
46569
+ "GrepFiles",
46570
+ "search_file_content",
46571
+ "SearchFileContent"
46572
+ ]);
46573
+ LIST_TOOL_NAMES = new Set([
46574
+ "list_dir",
46575
+ "ListDir",
46576
+ "list_directory",
46577
+ "ListDirectory",
46578
+ "LS"
46579
+ ]);
46580
+ TASK_TOOL_NAMES = new Set(["Task", "task"]);
46581
+ FILE_TOOL_FAMILIES = new Set([
46582
+ "Read",
46583
+ "Write",
46584
+ "Edit",
46585
+ "Glob",
46586
+ "Grep",
46587
+ "ListDir"
46588
+ ]);
46589
+ });
46590
+
46328
46591
  // src/utils/directoryLimits.ts
46329
46592
  function parsePositiveIntEnv(value, fallback, min, max) {
46330
46593
  if (!value || value.trim() === "") {
@@ -47133,32 +47396,39 @@ function addRootAndSiblingWorktree(root, acc) {
47133
47396
  acc.add(normalizeScopedPath(resolve4(dirname5(normalizedRoot), "memory-worktrees")));
47134
47397
  }
47135
47398
  }
47136
- function getExplicitEnvRoots(env3) {
47137
- const orderedRoots = [
47138
- env3.MEMORY_DIR,
47139
- env3.LETTA_MEMORY_DIR,
47140
- env3.PARENT_MEMORY_DIR
47141
- ].map((value) => value?.trim() ?? "").filter((value) => value.length > 0);
47142
- const explicitRootSet = new Set;
47399
+ function parseScopeList(value) {
47400
+ if (!value)
47401
+ return [];
47402
+ return value.split(/[\s,]+/).map((id) => id.trim()).filter((id) => id.length > 0);
47403
+ }
47404
+ function getExplicitEnvRoots(env3, homeDir) {
47405
+ const orderedRoots = [env3.MEMORY_DIR, env3.LETTA_MEMORY_DIR].map((value) => value?.trim() ?? "").filter((value) => value.length > 0);
47406
+ const rootSet = new Set;
47143
47407
  for (const root of orderedRoots) {
47144
- addRootAndSiblingWorktree(root, explicitRootSet);
47408
+ addRootAndSiblingWorktree(root, rootSet);
47409
+ }
47410
+ const scopeIds = new Set([
47411
+ ...parseScopeList(env3.LETTA_MEMORY_SCOPE),
47412
+ ...cliPermissions.getMemoryScope()
47413
+ ]);
47414
+ for (const id of scopeIds) {
47415
+ addRootAndSiblingWorktree(getMemoryFilesystemRoot(id, homeDir), rootSet);
47145
47416
  }
47146
47417
  return {
47147
- explicitRoots: [...explicitRootSet],
47418
+ roots: [...rootSet],
47148
47419
  primaryRoot: orderedRoots.length > 0 ? normalizeScopedPath(orderedRoots[0]) : null
47149
47420
  };
47150
47421
  }
47151
47422
  function deriveAgentId(env3, explicitAgentId) {
47152
47423
  const explicit = explicitAgentId?.trim();
47153
- if (explicit) {
47424
+ if (explicit)
47154
47425
  return explicit;
47155
- }
47156
47426
  const envAgentId = (env3.AGENT_ID || env3.LETTA_AGENT_ID || "").trim();
47157
- if (envAgentId) {
47427
+ if (envAgentId)
47158
47428
  return envAgentId;
47159
- }
47160
47429
  try {
47161
- return getCurrentAgentId().trim();
47430
+ const fromContext = getCurrentAgentId().trim();
47431
+ return fromContext || null;
47162
47432
  } catch {
47163
47433
  return null;
47164
47434
  }
@@ -47188,11 +47458,10 @@ function getFallbackRoots(env3, homeDir, currentAgentId, parentAgentId) {
47188
47458
  function resolveAllowedMemoryRoots(options = {}) {
47189
47459
  const env3 = options.env ?? process.env;
47190
47460
  const homeDir = options.homeDir ?? homedir11();
47191
- const explicit = getExplicitEnvRoots(env3);
47192
- if (explicit.explicitRoots.length > 0) {
47461
+ const explicit = getExplicitEnvRoots(env3, homeDir);
47462
+ if (explicit.roots.length > 0) {
47193
47463
  return {
47194
- roots: explicit.explicitRoots,
47195
- explicitRoots: explicit.explicitRoots,
47464
+ roots: explicit.roots,
47196
47465
  primaryRoot: explicit.primaryRoot,
47197
47466
  usedFallback: false
47198
47467
  };
@@ -47200,7 +47469,6 @@ function resolveAllowedMemoryRoots(options = {}) {
47200
47469
  const fallback = getFallbackRoots(env3, homeDir, options.currentAgentId, options.parentAgentId);
47201
47470
  return {
47202
47471
  roots: fallback.roots,
47203
- explicitRoots: [],
47204
47472
  primaryRoot: fallback.primaryRoot,
47205
47473
  usedFallback: fallback.roots.length > 0
47206
47474
  };
@@ -47208,6 +47476,320 @@ function resolveAllowedMemoryRoots(options = {}) {
47208
47476
  var init_memoryScope = __esm(() => {
47209
47477
  init_context();
47210
47478
  init_memoryFilesystem();
47479
+ init_cli();
47480
+ });
47481
+
47482
+ // src/permissions/shell-command-normalization.ts
47483
+ function trimMatchingQuotes(value) {
47484
+ const trimmed = value.trim();
47485
+ if (trimmed.length < 2) {
47486
+ return trimmed;
47487
+ }
47488
+ const first = trimmed[0];
47489
+ const last = trimmed[trimmed.length - 1];
47490
+ if ((first === '"' || first === "'") && last === first) {
47491
+ return trimmed.slice(1, -1);
47492
+ }
47493
+ return trimmed;
47494
+ }
47495
+ function normalizeExecutableToken(token) {
47496
+ const normalized = trimMatchingQuotes(token).replace(/\\/g, "/");
47497
+ const parts = normalized.split("/").filter(Boolean);
47498
+ const executable = parts[parts.length - 1] ?? normalized;
47499
+ return executable.toLowerCase();
47500
+ }
47501
+ function tokenizeShell(input) {
47502
+ const tokens = [];
47503
+ let current = "";
47504
+ let quote = null;
47505
+ let escaping = false;
47506
+ const flush = () => {
47507
+ if (current.length > 0) {
47508
+ tokens.push(current);
47509
+ current = "";
47510
+ }
47511
+ };
47512
+ for (let i = 0;i < input.length; i += 1) {
47513
+ const ch = input[i];
47514
+ if (ch === undefined) {
47515
+ continue;
47516
+ }
47517
+ if (escaping) {
47518
+ current += ch;
47519
+ escaping = false;
47520
+ continue;
47521
+ }
47522
+ if (ch === "\\" && quote !== "single") {
47523
+ escaping = true;
47524
+ continue;
47525
+ }
47526
+ if (quote === "single") {
47527
+ if (ch === "'") {
47528
+ quote = null;
47529
+ } else {
47530
+ current += ch;
47531
+ }
47532
+ continue;
47533
+ }
47534
+ if (quote === "double") {
47535
+ if (ch === '"') {
47536
+ quote = null;
47537
+ } else {
47538
+ current += ch;
47539
+ }
47540
+ continue;
47541
+ }
47542
+ if (ch === "'") {
47543
+ quote = "single";
47544
+ continue;
47545
+ }
47546
+ if (ch === '"') {
47547
+ quote = "double";
47548
+ continue;
47549
+ }
47550
+ if (/\s/.test(ch)) {
47551
+ flush();
47552
+ continue;
47553
+ }
47554
+ current += ch;
47555
+ }
47556
+ if (escaping) {
47557
+ current += "\\";
47558
+ }
47559
+ flush();
47560
+ return tokens;
47561
+ }
47562
+ function isDashCFlag(token) {
47563
+ return token === "-c" || /^-[a-zA-Z]*c[a-zA-Z]*$/.test(token);
47564
+ }
47565
+ function extractInnerShellCommand(tokens) {
47566
+ if (tokens.length === 0) {
47567
+ return null;
47568
+ }
47569
+ let index = 0;
47570
+ if (normalizeExecutableToken(tokens[0] ?? "") === "env") {
47571
+ index += 1;
47572
+ while (index < tokens.length) {
47573
+ const token = tokens[index] ?? "";
47574
+ if (!token) {
47575
+ index += 1;
47576
+ continue;
47577
+ }
47578
+ if (/^-[A-Za-z]+$/.test(token)) {
47579
+ index += 1;
47580
+ continue;
47581
+ }
47582
+ if (/^[A-Za-z_][A-Za-z0-9_]*=/.test(token)) {
47583
+ index += 1;
47584
+ continue;
47585
+ }
47586
+ break;
47587
+ }
47588
+ }
47589
+ const executableToken = tokens[index];
47590
+ if (!executableToken) {
47591
+ return null;
47592
+ }
47593
+ if (!SHELL_EXECUTORS.has(normalizeExecutableToken(executableToken))) {
47594
+ return null;
47595
+ }
47596
+ for (let i = index + 1;i < tokens.length; i += 1) {
47597
+ const token = tokens[i];
47598
+ if (!token) {
47599
+ continue;
47600
+ }
47601
+ if (!isDashCFlag(token)) {
47602
+ continue;
47603
+ }
47604
+ const innerCommand = tokens[i + 1];
47605
+ if (!innerCommand) {
47606
+ return null;
47607
+ }
47608
+ return trimMatchingQuotes(innerCommand);
47609
+ }
47610
+ return null;
47611
+ }
47612
+ function unwrapShellLauncherCommand(command) {
47613
+ let current = command.trim();
47614
+ for (let depth = 0;depth < 5; depth += 1) {
47615
+ if (!current) {
47616
+ break;
47617
+ }
47618
+ const tokens = tokenizeShell(current);
47619
+ const inner = extractInnerShellCommand(tokens);
47620
+ if (!inner || inner === current) {
47621
+ break;
47622
+ }
47623
+ current = inner.trim();
47624
+ }
47625
+ return current;
47626
+ }
47627
+ function normalizeBashRulePayload(payload) {
47628
+ const trimmed = payload.trim();
47629
+ if (!trimmed) {
47630
+ return "";
47631
+ }
47632
+ const hasWildcardSuffix = trimmed.endsWith(":*");
47633
+ const withoutWildcard = hasWildcardSuffix ? trimmed.slice(0, -2).trimEnd() : trimmed;
47634
+ const unwrapped = unwrapShellLauncherCommand(withoutWildcard);
47635
+ const normalized = normalizeGitCommandPrefix(unwrapped);
47636
+ if (hasWildcardSuffix) {
47637
+ return `${normalized}:*`;
47638
+ }
47639
+ return normalized;
47640
+ }
47641
+ function normalizeGitCommandPrefix(command) {
47642
+ const trimmed = command.trim();
47643
+ if (!trimmed.startsWith("git ")) {
47644
+ return trimmed;
47645
+ }
47646
+ const tokens = tokenizeShell(trimmed);
47647
+ if (tokens[0] !== "git") {
47648
+ return trimmed;
47649
+ }
47650
+ const normalizedTokens = ["git"];
47651
+ let index = 1;
47652
+ while (index < tokens.length) {
47653
+ const token = tokens[index];
47654
+ if (!token) {
47655
+ index += 1;
47656
+ continue;
47657
+ }
47658
+ if (token === "-C") {
47659
+ index += 2;
47660
+ continue;
47661
+ }
47662
+ normalizedTokens.push(...tokens.slice(index));
47663
+ return normalizedTokens.join(" ");
47664
+ }
47665
+ return normalizedTokens.join(" ");
47666
+ }
47667
+ var SHELL_EXECUTORS;
47668
+ var init_shell_command_normalization = __esm(() => {
47669
+ SHELL_EXECUTORS = new Set(["bash", "sh", "zsh", "dash", "ksh"]);
47670
+ });
47671
+
47672
+ // src/permissions/rule-normalization.ts
47673
+ function splitRule(rule) {
47674
+ const match = rule.trim().match(/^([^(]+)(?:\(([\s\S]*)\))?$/);
47675
+ if (!match?.[1]) {
47676
+ return { tool: rule.trim(), payload: null };
47677
+ }
47678
+ return {
47679
+ tool: match[1].trim(),
47680
+ payload: match[2] !== undefined ? match[2] : null
47681
+ };
47682
+ }
47683
+ function normalizePermissionRule(rule) {
47684
+ const { tool, payload } = splitRule(rule);
47685
+ const canonicalTool = canonicalToolName(tool);
47686
+ if (payload === null) {
47687
+ return canonicalTool;
47688
+ }
47689
+ if (isShellToolName(canonicalTool)) {
47690
+ return `Bash(${normalizeBashRulePayload(payload)})`;
47691
+ }
47692
+ if (isFileToolName(canonicalTool)) {
47693
+ return `${canonicalTool}(${canonicalizePathLike(payload)})`;
47694
+ }
47695
+ return `${canonicalTool}(${payload.trim()})`;
47696
+ }
47697
+ function permissionRulesEquivalent(left, right) {
47698
+ return normalizePermissionRule(left) === normalizePermissionRule(right);
47699
+ }
47700
+ var init_rule_normalization = __esm(() => {
47701
+ init_canonical();
47702
+ init_shell_command_normalization();
47703
+ });
47704
+
47705
+ // src/permissions/cli.ts
47706
+ var exports_cli = {};
47707
+ __export(exports_cli, {
47708
+ cliPermissions: () => cliPermissions
47709
+ });
47710
+
47711
+ class CliPermissions {
47712
+ allowedTools = [];
47713
+ disallowedTools = [];
47714
+ memoryScope = [];
47715
+ setAllowedTools(toolsString) {
47716
+ this.allowedTools = this.parseToolList(toolsString);
47717
+ }
47718
+ setDisallowedTools(toolsString) {
47719
+ this.disallowedTools = this.parseToolList(toolsString);
47720
+ }
47721
+ setMemoryScope(scopeString) {
47722
+ this.memoryScope = parseScopeList(scopeString);
47723
+ }
47724
+ parseToolList(toolsString) {
47725
+ if (!toolsString)
47726
+ return [];
47727
+ const tools = [];
47728
+ let current = "";
47729
+ let depth = 0;
47730
+ for (let i = 0;i < toolsString.length; i++) {
47731
+ const char = toolsString[i];
47732
+ if (char === "(") {
47733
+ depth++;
47734
+ current += char;
47735
+ } else if (char === ")") {
47736
+ depth--;
47737
+ current += char;
47738
+ } else if (char === "," && depth === 0) {
47739
+ if (current.trim()) {
47740
+ tools.push(this.normalizePattern(current.trim()));
47741
+ }
47742
+ current = "";
47743
+ } else {
47744
+ current += char;
47745
+ }
47746
+ }
47747
+ if (current.trim()) {
47748
+ tools.push(this.normalizePattern(current.trim()));
47749
+ }
47750
+ return tools;
47751
+ }
47752
+ normalizePattern(pattern) {
47753
+ const trimmed = pattern.trim();
47754
+ if (trimmed.includes("(")) {
47755
+ return normalizePermissionRule(trimmed);
47756
+ }
47757
+ const canonicalTool = canonicalToolName(trimmed);
47758
+ if (isShellToolName(canonicalTool)) {
47759
+ return "Bash(:*)";
47760
+ }
47761
+ if (isFileToolName(canonicalTool)) {
47762
+ return `${canonicalTool}(**)`;
47763
+ }
47764
+ return canonicalTool;
47765
+ }
47766
+ getAllowedTools() {
47767
+ return [...this.allowedTools];
47768
+ }
47769
+ getDisallowedTools() {
47770
+ return [...this.disallowedTools];
47771
+ }
47772
+ getMemoryScope() {
47773
+ return [...this.memoryScope];
47774
+ }
47775
+ hasMemoryScope() {
47776
+ return this.memoryScope.length > 0;
47777
+ }
47778
+ hasOverrides() {
47779
+ return this.allowedTools.length > 0 || this.disallowedTools.length > 0 || this.memoryScope.length > 0;
47780
+ }
47781
+ clear() {
47782
+ this.allowedTools = [];
47783
+ this.disallowedTools = [];
47784
+ this.memoryScope = [];
47785
+ }
47786
+ }
47787
+ var cliPermissions;
47788
+ var init_cli = __esm(() => {
47789
+ init_canonical();
47790
+ init_memoryScope();
47791
+ init_rule_normalization();
47792
+ cliPermissions = new CliPermissions;
47211
47793
  });
47212
47794
 
47213
47795
  // src/permissions/shellAnalysis.ts
@@ -47533,8 +48115,315 @@ function parseShellAnalysis(command) {
47533
48115
  return parseShellAnalysisSegments(segments);
47534
48116
  }
47535
48117
 
47536
- // src/permissions/readOnlyShell.ts
48118
+ // src/permissions/crossAgentGuard.ts
47537
48119
  import { homedir as homedir12 } from "node:os";
48120
+ function resolveAllowedAgents(options = {}) {
48121
+ const env3 = options.env ?? process.env;
48122
+ const self = deriveAgentId(env3, options.currentAgentId);
48123
+ const envScope = parseScopeList(env3.LETTA_MEMORY_SCOPE);
48124
+ const cliScope = options.cliMemoryScope ?? cliPermissions.getMemoryScope();
48125
+ const ids = new Set;
48126
+ if (self)
48127
+ ids.add(self);
48128
+ for (const id of envScope)
48129
+ ids.add(id);
48130
+ for (const id of cliScope)
48131
+ ids.add(id);
48132
+ return {
48133
+ ids,
48134
+ sources: {
48135
+ self,
48136
+ env: envScope,
48137
+ cli: [...cliScope]
48138
+ }
48139
+ };
48140
+ }
48141
+ function escapeRegex2(value) {
48142
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
48143
+ }
48144
+ function getAgentsTreeRoot(homeDir) {
48145
+ const normalizedHome = homeDir.replace(/\\/g, "/").replace(/\/+$/, "");
48146
+ return `${normalizedHome}/.letta/agents`;
48147
+ }
48148
+ function normalizePathForCompare(path3) {
48149
+ const normalized = path3.replace(/\\/g, "/").replace(/\/+$/, "");
48150
+ return normalized.length === 0 ? "/" : normalized;
48151
+ }
48152
+ function classifyAgentsTreePath(path3, homeDir) {
48153
+ const root = getAgentsTreeRoot(homeDir);
48154
+ const normalized = normalizePathForCompare(path3);
48155
+ if (normalized === root) {
48156
+ return { kind: "agents-root" };
48157
+ }
48158
+ if (normalized.startsWith(`${root}/`)) {
48159
+ const rest = normalized.slice(root.length + 1);
48160
+ const slash = rest.indexOf("/");
48161
+ const id = slash === -1 ? rest : rest.slice(0, slash);
48162
+ return { kind: "agent", id };
48163
+ }
48164
+ const prefix = normalized === "/" ? "/" : `${normalized}/`;
48165
+ if (root.startsWith(prefix)) {
48166
+ return { kind: "ancestor" };
48167
+ }
48168
+ return { kind: "outside" };
48169
+ }
48170
+ function extractApplyPatchPaths(input) {
48171
+ const paths = [];
48172
+ const fileDirectivePattern = /\*\*\* (?:Add|Update|Delete) File:\s*(.+)/g;
48173
+ const moveDirectivePattern = /\*\*\* Move to:\s*(.+)/g;
48174
+ for (const match of input.matchAll(fileDirectivePattern)) {
48175
+ const matchPath = match[1]?.trim();
48176
+ if (matchPath)
48177
+ paths.push(matchPath);
48178
+ }
48179
+ for (const match of input.matchAll(moveDirectivePattern)) {
48180
+ const matchPath = match[1]?.trim();
48181
+ if (matchPath)
48182
+ paths.push(matchPath);
48183
+ }
48184
+ return paths;
48185
+ }
48186
+ function extractFilePath(toolArgs) {
48187
+ if (typeof toolArgs.file_path === "string" && toolArgs.file_path.length > 0) {
48188
+ return toolArgs.file_path;
48189
+ }
48190
+ if (typeof toolArgs.path === "string" && toolArgs.path.length > 0) {
48191
+ return toolArgs.path;
48192
+ }
48193
+ if (typeof toolArgs.notebook_path === "string" && toolArgs.notebook_path.length > 0) {
48194
+ return toolArgs.notebook_path;
48195
+ }
48196
+ return null;
48197
+ }
48198
+ function extractMultiEditPaths(toolArgs) {
48199
+ const single = extractFilePath(toolArgs);
48200
+ return single ? [single] : [];
48201
+ }
48202
+ function extractShellCommand(toolArgs) {
48203
+ const command = toolArgs.command;
48204
+ if (typeof command === "string")
48205
+ return command;
48206
+ if (Array.isArray(command)) {
48207
+ return command.map((c) => String(c)).join(" ");
48208
+ }
48209
+ return null;
48210
+ }
48211
+ function lookupShellVar(name, env3, homeDir) {
48212
+ if (name === "HOME")
48213
+ return homeDir;
48214
+ const value = env3[name];
48215
+ return typeof value === "string" ? value : undefined;
48216
+ }
48217
+ function expandShellToken(token, env3, homeDir) {
48218
+ let result = token;
48219
+ if (result.startsWith("~/")) {
48220
+ result = `${homeDir}/${result.slice(2)}`;
48221
+ } else if (result === "~") {
48222
+ result = homeDir;
48223
+ }
48224
+ let unresolved = false;
48225
+ result = result.replace(SHELL_VAR_REGEX, (_match, bracedName, bareName) => {
48226
+ const name = bracedName || bareName;
48227
+ if (!name) {
48228
+ unresolved = true;
48229
+ return "";
48230
+ }
48231
+ const value = lookupShellVar(name, env3, homeDir);
48232
+ if (value === undefined) {
48233
+ unresolved = true;
48234
+ return "";
48235
+ }
48236
+ return value;
48237
+ });
48238
+ return unresolved ? null : result;
48239
+ }
48240
+ function collectShellAgentTargets(command, env3, homeDir) {
48241
+ const targets = new Map;
48242
+ const segments = splitShellSegments(command) ?? [command];
48243
+ for (const segment of segments) {
48244
+ for (const token of tokenizeShellWords(segment)) {
48245
+ scanToken(token, env3, homeDir, targets);
48246
+ }
48247
+ }
48248
+ return targets;
48249
+ }
48250
+ function stripAllOuterQuotes(value) {
48251
+ let result = value;
48252
+ while (result.length >= 2 && (result.startsWith('"') && result.endsWith('"') || result.startsWith("'") && result.endsWith("'"))) {
48253
+ result = result.slice(1, -1);
48254
+ }
48255
+ return result;
48256
+ }
48257
+ function scanToken(rawToken, env3, homeDir, out) {
48258
+ if (!rawToken)
48259
+ return;
48260
+ const assignmentMatch = rawToken.match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
48261
+ const candidateValue = assignmentMatch ? assignmentMatch[2] ?? "" : rawToken;
48262
+ const candidates = [rawToken, candidateValue].filter((v) => v.length > 0).map((v) => stripAllOuterQuotes(v));
48263
+ for (const value of candidates) {
48264
+ const expanded = expandShellToken(value, env3, homeDir);
48265
+ if (expanded === null)
48266
+ continue;
48267
+ const normalized = normalizeScopedPath(expanded);
48268
+ const classification = classifyAgentsTreePath(normalized, homeDir);
48269
+ if (classification.kind === "agent") {
48270
+ out.set(value, classification.id);
48271
+ } else if (classification.kind === "agents-root") {
48272
+ out.set(value, UNRESOLVED_AGENT_ID);
48273
+ }
48274
+ }
48275
+ }
48276
+ function scanRawCommandForUnresolvedAgentRefs(rawCommand, allowedAgentIds, env3, homeDir) {
48277
+ const expanded = expandCommandVariables(rawCommand, env3, homeDir);
48278
+ const escapedRoot = escapeRegex2(getAgentsTreeRoot(homeDir));
48279
+ const pattern = new RegExp(`${escapedRoot}(?:/([^/\\s"'\`$(){}\\[\\]|&;,#]*))?`, "g");
48280
+ const unresolved = [];
48281
+ for (const match of expanded.matchAll(pattern)) {
48282
+ const candidate = (match[1] ?? "").trim();
48283
+ if (candidate.length === 0) {
48284
+ unresolved.push(UNRESOLVED_AGENT_ID);
48285
+ continue;
48286
+ }
48287
+ if (!allowedAgentIds.has(candidate)) {
48288
+ unresolved.push(candidate);
48289
+ }
48290
+ }
48291
+ return unresolved;
48292
+ }
48293
+ function expandCommandVariables(command, env3, homeDir) {
48294
+ let result = command.replace(/(^|[\s="'`:])~\//g, (_match, prefix) => `${prefix}${homeDir}/`);
48295
+ result = result.replace(SHELL_VAR_REGEX, (match, bracedName, bareName) => {
48296
+ const name = bracedName || bareName;
48297
+ if (!name)
48298
+ return match;
48299
+ return lookupShellVar(name, env3, homeDir) ?? match;
48300
+ });
48301
+ return result;
48302
+ }
48303
+ function isRecursivePathTool(toolName) {
48304
+ if (RECURSIVE_PATH_TOOLS.has(toolName))
48305
+ return true;
48306
+ const canonical = canonicalToolName(toolName);
48307
+ return RECURSIVE_PATH_TOOLS.has(canonical);
48308
+ }
48309
+ function extractTargetAgentPaths(toolName, toolArgs, workingDirectory, env3 = process.env, homeDir = homedir12()) {
48310
+ const agentIds = new Set;
48311
+ let anyAgentScoped = false;
48312
+ const recursive = isRecursivePathTool(toolName);
48313
+ const addFromPath = (rawPath) => {
48314
+ if (!rawPath || typeof rawPath !== "string")
48315
+ return;
48316
+ const resolvedPath = resolveScopedTargetPath(rawPath, workingDirectory);
48317
+ if (!resolvedPath)
48318
+ return;
48319
+ const classification = classifyAgentsTreePath(resolvedPath, homeDir);
48320
+ switch (classification.kind) {
48321
+ case "outside":
48322
+ return;
48323
+ case "agents-root":
48324
+ anyAgentScoped = true;
48325
+ agentIds.add(UNRESOLVED_AGENT_ID);
48326
+ return;
48327
+ case "ancestor":
48328
+ if (recursive) {
48329
+ anyAgentScoped = true;
48330
+ agentIds.add(UNRESOLVED_AGENT_ID);
48331
+ }
48332
+ return;
48333
+ case "agent":
48334
+ anyAgentScoped = true;
48335
+ agentIds.add(classification.id);
48336
+ return;
48337
+ }
48338
+ };
48339
+ const canonical = canonicalToolName(toolName);
48340
+ if (toolName === "ApplyPatch" || toolName === "apply_patch" || toolName === "memory_apply_patch") {
48341
+ if (typeof toolArgs.input === "string") {
48342
+ for (const p of extractApplyPatchPaths(toolArgs.input)) {
48343
+ addFromPath(p);
48344
+ }
48345
+ }
48346
+ return { agentIds, anyAgentScoped };
48347
+ }
48348
+ if (isShellToolName(toolName) || canonical === "Bash") {
48349
+ const command = extractShellCommand(toolArgs);
48350
+ if (command) {
48351
+ const hits = collectShellAgentTargets(command, env3, homeDir);
48352
+ for (const id of hits.values()) {
48353
+ anyAgentScoped = true;
48354
+ agentIds.add(id);
48355
+ }
48356
+ }
48357
+ return { agentIds, anyAgentScoped };
48358
+ }
48359
+ if (toolName === "MultiEdit") {
48360
+ for (const p of extractMultiEditPaths(toolArgs)) {
48361
+ addFromPath(p);
48362
+ }
48363
+ return { agentIds, anyAgentScoped };
48364
+ }
48365
+ addFromPath(extractFilePath(toolArgs));
48366
+ if (recursive && typeof toolArgs.pattern === "string") {
48367
+ addFromPath(toolArgs.pattern);
48368
+ }
48369
+ return { agentIds, anyAgentScoped };
48370
+ }
48371
+ function buildReason(offending, allowed) {
48372
+ const offendingDesc = offending.join(", ");
48373
+ const allowedList = [...allowed.ids];
48374
+ const allowedDesc = allowedList.length > 0 ? allowedList.join(", ") : "(none)";
48375
+ return `Permission denied by cross-agent memory guard (${offendingDesc}). ` + `Allowed: ${allowedDesc}. ` + `Set LETTA_MEMORY_SCOPE or pass --memory-scope to opt in.`;
48376
+ }
48377
+ function evaluateCrossAgentGuard(toolName, toolArgs, workingDirectory, options = {}) {
48378
+ const env3 = options.env ?? process.env;
48379
+ const homeDir = env3.HOME ?? homedir12();
48380
+ const targets = extractTargetAgentPaths(toolName, toolArgs, workingDirectory, env3, homeDir);
48381
+ const allowed = resolveAllowedAgents(options);
48382
+ const offending = new Set;
48383
+ for (const id of targets.agentIds) {
48384
+ if (!allowed.ids.has(id)) {
48385
+ offending.add(id);
48386
+ }
48387
+ }
48388
+ const canonical = canonicalToolName(toolName);
48389
+ if (isShellToolName(toolName) || canonical === "Bash") {
48390
+ const command = extractShellCommand(toolArgs);
48391
+ if (command) {
48392
+ const unresolved = scanRawCommandForUnresolvedAgentRefs(command, allowed.ids, env3, homeDir);
48393
+ for (const id of unresolved) {
48394
+ offending.add(id);
48395
+ }
48396
+ }
48397
+ }
48398
+ if (offending.size === 0) {
48399
+ return null;
48400
+ }
48401
+ const offendingList = [...offending];
48402
+ return {
48403
+ matchedRule: "cross-agent guard",
48404
+ reason: buildReason(offendingList, allowed),
48405
+ offendingAgentIds: offendingList
48406
+ };
48407
+ }
48408
+ var UNRESOLVED_AGENT_ID = "<unresolved>", SHELL_VAR_REGEX, RECURSIVE_PATH_TOOLS;
48409
+ var init_crossAgentGuard = __esm(() => {
48410
+ init_canonical();
48411
+ init_cli();
48412
+ init_memoryScope();
48413
+ SHELL_VAR_REGEX = /\$(?:\{([A-Za-z_][A-Za-z0-9_]*)\}|([A-Za-z_][A-Za-z0-9_]*))/g;
48414
+ RECURSIVE_PATH_TOOLS = new Set([
48415
+ "Grep",
48416
+ "Glob",
48417
+ "ListDir",
48418
+ "LS",
48419
+ "list_dir",
48420
+ "grep",
48421
+ "glob"
48422
+ ]);
48423
+ });
48424
+
48425
+ // src/permissions/readOnlyShell.ts
48426
+ import { homedir as homedir13 } from "node:os";
47538
48427
  import { resolve as resolve5 } from "node:path";
47539
48428
  function isSafeConditionalTest(condition, options) {
47540
48429
  let tokens = tokenizeShellWords(condition);
@@ -47997,7 +48886,7 @@ function parseGitInvocation(tokens, options) {
47997
48886
  return { subcommand: null, subcommandIndex: -1, isSafePath: true };
47998
48887
  }
47999
48888
  function getAllowedMemoryPrefixes(agentId) {
48000
- const home = homedir12();
48889
+ const home = homedir13();
48001
48890
  const prefixes = [
48002
48891
  normalizeSeparators(resolve5(home, ".letta", "agents", agentId, "memory")),
48003
48892
  normalizeSeparators(resolve5(home, ".letta", "agents", agentId, "memory-worktrees"))
@@ -48023,7 +48912,7 @@ function expandScopedVariables(value, env3, shellVars) {
48023
48912
  return "";
48024
48913
  }
48025
48914
  if (name === "HOME") {
48026
- return homedir12();
48915
+ return homedir13();
48027
48916
  }
48028
48917
  const scopedValue = shellVars[name];
48029
48918
  if (typeof scopedValue === "string") {
@@ -48481,202 +49370,12 @@ var init_readOnlyShell = __esm(() => {
48481
49370
  SAFE_FILE_TEST_FLAGS = new Set(["-e", "-f", "-d", "-s", "-L"]);
48482
49371
  });
48483
49372
 
48484
- // src/permissions/shell-command-normalization.ts
48485
- function trimMatchingQuotes(value) {
48486
- const trimmed = value.trim();
48487
- if (trimmed.length < 2) {
48488
- return trimmed;
48489
- }
48490
- const first = trimmed[0];
48491
- const last = trimmed[trimmed.length - 1];
48492
- if ((first === '"' || first === "'") && last === first) {
48493
- return trimmed.slice(1, -1);
48494
- }
48495
- return trimmed;
48496
- }
48497
- function normalizeExecutableToken(token) {
48498
- const normalized = trimMatchingQuotes(token).replace(/\\/g, "/");
48499
- const parts = normalized.split("/").filter(Boolean);
48500
- const executable = parts[parts.length - 1] ?? normalized;
48501
- return executable.toLowerCase();
48502
- }
48503
- function tokenizeShell(input) {
48504
- const tokens = [];
48505
- let current = "";
48506
- let quote = null;
48507
- let escaping = false;
48508
- const flush = () => {
48509
- if (current.length > 0) {
48510
- tokens.push(current);
48511
- current = "";
48512
- }
48513
- };
48514
- for (let i = 0;i < input.length; i += 1) {
48515
- const ch = input[i];
48516
- if (ch === undefined) {
48517
- continue;
48518
- }
48519
- if (escaping) {
48520
- current += ch;
48521
- escaping = false;
48522
- continue;
48523
- }
48524
- if (ch === "\\" && quote !== "single") {
48525
- escaping = true;
48526
- continue;
48527
- }
48528
- if (quote === "single") {
48529
- if (ch === "'") {
48530
- quote = null;
48531
- } else {
48532
- current += ch;
48533
- }
48534
- continue;
48535
- }
48536
- if (quote === "double") {
48537
- if (ch === '"') {
48538
- quote = null;
48539
- } else {
48540
- current += ch;
48541
- }
48542
- continue;
48543
- }
48544
- if (ch === "'") {
48545
- quote = "single";
48546
- continue;
48547
- }
48548
- if (ch === '"') {
48549
- quote = "double";
48550
- continue;
48551
- }
48552
- if (/\s/.test(ch)) {
48553
- flush();
48554
- continue;
48555
- }
48556
- current += ch;
48557
- }
48558
- if (escaping) {
48559
- current += "\\";
48560
- }
48561
- flush();
48562
- return tokens;
48563
- }
48564
- function isDashCFlag(token) {
48565
- return token === "-c" || /^-[a-zA-Z]*c[a-zA-Z]*$/.test(token);
48566
- }
48567
- function extractInnerShellCommand(tokens) {
48568
- if (tokens.length === 0) {
48569
- return null;
48570
- }
48571
- let index = 0;
48572
- if (normalizeExecutableToken(tokens[0] ?? "") === "env") {
48573
- index += 1;
48574
- while (index < tokens.length) {
48575
- const token = tokens[index] ?? "";
48576
- if (!token) {
48577
- index += 1;
48578
- continue;
48579
- }
48580
- if (/^-[A-Za-z]+$/.test(token)) {
48581
- index += 1;
48582
- continue;
48583
- }
48584
- if (/^[A-Za-z_][A-Za-z0-9_]*=/.test(token)) {
48585
- index += 1;
48586
- continue;
48587
- }
48588
- break;
48589
- }
48590
- }
48591
- const executableToken = tokens[index];
48592
- if (!executableToken) {
48593
- return null;
48594
- }
48595
- if (!SHELL_EXECUTORS.has(normalizeExecutableToken(executableToken))) {
48596
- return null;
48597
- }
48598
- for (let i = index + 1;i < tokens.length; i += 1) {
48599
- const token = tokens[i];
48600
- if (!token) {
48601
- continue;
48602
- }
48603
- if (!isDashCFlag(token)) {
48604
- continue;
48605
- }
48606
- const innerCommand = tokens[i + 1];
48607
- if (!innerCommand) {
48608
- return null;
48609
- }
48610
- return trimMatchingQuotes(innerCommand);
48611
- }
48612
- return null;
48613
- }
48614
- function unwrapShellLauncherCommand(command) {
48615
- let current = command.trim();
48616
- for (let depth = 0;depth < 5; depth += 1) {
48617
- if (!current) {
48618
- break;
48619
- }
48620
- const tokens = tokenizeShell(current);
48621
- const inner = extractInnerShellCommand(tokens);
48622
- if (!inner || inner === current) {
48623
- break;
48624
- }
48625
- current = inner.trim();
48626
- }
48627
- return current;
48628
- }
48629
- function normalizeBashRulePayload(payload) {
48630
- const trimmed = payload.trim();
48631
- if (!trimmed) {
48632
- return "";
48633
- }
48634
- const hasWildcardSuffix = trimmed.endsWith(":*");
48635
- const withoutWildcard = hasWildcardSuffix ? trimmed.slice(0, -2).trimEnd() : trimmed;
48636
- const unwrapped = unwrapShellLauncherCommand(withoutWildcard);
48637
- const normalized = normalizeGitCommandPrefix(unwrapped);
48638
- if (hasWildcardSuffix) {
48639
- return `${normalized}:*`;
48640
- }
48641
- return normalized;
48642
- }
48643
- function normalizeGitCommandPrefix(command) {
48644
- const trimmed = command.trim();
48645
- if (!trimmed.startsWith("git ")) {
48646
- return trimmed;
48647
- }
48648
- const tokens = tokenizeShell(trimmed);
48649
- if (tokens[0] !== "git") {
48650
- return trimmed;
48651
- }
48652
- const normalizedTokens = ["git"];
48653
- let index = 1;
48654
- while (index < tokens.length) {
48655
- const token = tokens[index];
48656
- if (!token) {
48657
- index += 1;
48658
- continue;
48659
- }
48660
- if (token === "-C") {
48661
- index += 2;
48662
- continue;
48663
- }
48664
- normalizedTokens.push(...tokens.slice(index));
48665
- return normalizedTokens.join(" ");
48666
- }
48667
- return normalizedTokens.join(" ");
48668
- }
48669
- var SHELL_EXECUTORS;
48670
- var init_shell_command_normalization = __esm(() => {
48671
- SHELL_EXECUTORS = new Set(["bash", "sh", "zsh", "dash", "ksh"]);
48672
- });
48673
-
48674
49373
  // src/permissions/mode.ts
48675
49374
  var exports_mode = {};
48676
49375
  __export(exports_mode, {
48677
49376
  permissionMode: () => permissionMode
48678
49377
  });
48679
- import { homedir as homedir13 } from "node:os";
49378
+ import { homedir as homedir14 } from "node:os";
48680
49379
  import { isAbsolute as isAbsolute3, join as join15, relative as relative3 } from "node:path";
48681
49380
  function everyResolvedTargetIsWithinRoots(candidatePaths, roots, workingDirectory) {
48682
49381
  return candidatePaths.length > 0 && candidatePaths.every((path3) => {
@@ -48720,22 +49419,6 @@ function isPathInPlansDir(path3, plansDir) {
48720
49419
  const rel = relative3(plansDir, path3);
48721
49420
  return rel !== "" && !rel.startsWith("..") && !isAbsolute3(rel);
48722
49421
  }
48723
- function extractApplyPatchPaths(input) {
48724
- const paths = [];
48725
- const fileDirectivePattern = /\*\*\* (?:Add|Update|Delete) File:\s*(.+)/g;
48726
- const moveDirectivePattern = /\*\*\* Move to:\s*(.+)/g;
48727
- for (const match of input.matchAll(fileDirectivePattern)) {
48728
- const matchPath = match[1]?.trim();
48729
- if (matchPath)
48730
- paths.push(matchPath);
48731
- }
48732
- for (const match of input.matchAll(moveDirectivePattern)) {
48733
- const matchPath = match[1]?.trim();
48734
- if (matchPath)
48735
- paths.push(matchPath);
48736
- }
48737
- return paths;
48738
- }
48739
49422
  function stripMatchingQuotes(value) {
48740
49423
  const trimmed = value.trim();
48741
49424
  if (trimmed.length < 2) {
@@ -48906,7 +49589,7 @@ class PermissionModeManager {
48906
49589
  return "allow";
48907
49590
  }
48908
49591
  if (writeTools.includes(toolName)) {
48909
- const plansDir = join15(homedir13(), ".letta", "plans");
49592
+ const plansDir = join15(homedir14(), ".letta", "plans");
48910
49593
  const targetPath = toolArgs?.file_path || toolArgs?.path;
48911
49594
  let candidatePaths = [];
48912
49595
  if ((toolName === "ApplyPatch" || toolName === "apply_patch" || toolName === "memory_apply_patch") && toolArgs?.input) {
@@ -48957,7 +49640,7 @@ class PermissionModeManager {
48957
49640
  }
48958
49641
  const planWritePath = extractPlanFileWritePathFromShellCommand(command);
48959
49642
  if (planWritePath) {
48960
- const plansDir = join15(homedir13(), ".letta", "plans");
49643
+ const plansDir = join15(homedir14(), ".letta", "plans");
48961
49644
  const resolvedPath = resolvePlanTargetPath(planWritePath, workingDirectory);
48962
49645
  if (resolvedPath && isPathInPlansDir(resolvedPath, plansDir)) {
48963
49646
  return "allow";
@@ -49068,6 +49751,7 @@ class PermissionModeManager {
49068
49751
  }
49069
49752
  var MODE_KEY, PLAN_FILE_KEY, MODE_BEFORE_PLAN_KEY, permissionMode;
49070
49753
  var init_mode = __esm(() => {
49754
+ init_crossAgentGuard();
49071
49755
  init_memoryScope();
49072
49756
  init_readOnlyShell();
49073
49757
  init_shell_command_normalization();
@@ -52202,7 +52886,7 @@ async function edit(args) {
52202
52886
  var init_Edit2 = () => {};
52203
52887
 
52204
52888
  // src/cli/helpers/planName.ts
52205
- import { homedir as homedir15 } from "node:os";
52889
+ import { homedir as homedir16 } from "node:os";
52206
52890
  import { join as join18 } from "node:path";
52207
52891
  function randomElement(arr) {
52208
52892
  return arr[Math.floor(Math.random() * arr.length)];
@@ -52215,7 +52899,7 @@ function generatePlanName() {
52215
52899
  }
52216
52900
  function generatePlanFilePath() {
52217
52901
  const name = generatePlanName();
52218
- return join18(homedir15(), ".letta", "plans", `${name}.md`);
52902
+ return join18(homedir16(), ".letta", "plans", `${name}.md`);
52219
52903
  }
52220
52904
  var adjectives, nouns;
52221
52905
  var init_planName = __esm(() => {
@@ -53363,7 +54047,6 @@ var activeRuntime = null;
53363
54047
  var init_runtime3 = () => {};
53364
54048
 
53365
54049
  // src/tools/impl/Memory.ts
53366
- import { execFile as execFileCb3 } from "node:child_process";
53367
54050
  import { existsSync as existsSync19 } from "node:fs";
53368
54051
  import {
53369
54052
  mkdir as mkdir5,
@@ -53374,9 +54057,8 @@ import {
53374
54057
  unlink,
53375
54058
  writeFile as writeFile5
53376
54059
  } from "node:fs/promises";
53377
- import { homedir as homedir16 } from "node:os";
54060
+ import { homedir as homedir17 } from "node:os";
53378
54061
  import { dirname as dirname8, isAbsolute as isAbsolute9, relative as relative5, resolve as resolve13 } from "node:path";
53379
- import { promisify as promisify6 } from "node:util";
53380
54062
  async function getAgentIdentity() {
53381
54063
  const envAgentId = (process.env.AGENT_ID || process.env.LETTA_AGENT_ID || "").trim();
53382
54064
  const contextAgentId = (() => {
@@ -53401,6 +54083,10 @@ async function getAgentIdentity() {
53401
54083
  }
53402
54084
  return { agentId, agentName };
53403
54085
  }
54086
+ function normalizeComparableContent2(content) {
54087
+ return content.replace(/\r\n/g, `
54088
+ `).trim();
54089
+ }
53404
54090
  async function memory(args) {
53405
54091
  validateRequiredParams(args, ["command", "reason"], "memory");
53406
54092
  const reason = args.reason.trim();
@@ -53409,7 +54095,46 @@ async function memory(args) {
53409
54095
  }
53410
54096
  const memoryDir = resolveMemoryDir();
53411
54097
  ensureMemoryRepo(memoryDir);
53412
- let affectedPaths = [];
54098
+ await assertMemoryRepoReadyForWrite(memoryDir);
54099
+ const affectedPaths = await applyMemoryCommand(memoryDir, args);
54100
+ if (affectedPaths.length === 0) {
54101
+ return {
54102
+ message: `Memory ${args.command} completed with no changed paths.`
54103
+ };
54104
+ }
54105
+ const { agentId, agentName } = await getAgentIdentity();
54106
+ const commitResult = await commitAndSyncMemoryWrite({
54107
+ memoryDir,
54108
+ pathspecs: affectedPaths,
54109
+ reason,
54110
+ author: {
54111
+ agentId,
54112
+ authorName: agentName.trim() || agentId,
54113
+ authorEmail: `${agentId}@letta.com`
54114
+ },
54115
+ replay: async () => applyMemoryCommand(memoryDir, args, { replaying: true })
54116
+ });
54117
+ if (!commitResult.committed) {
54118
+ return {
54119
+ message: `Memory ${args.command} made no effective changes; skipped commit and push.`
54120
+ };
54121
+ }
54122
+ emitMemoryUpdated(affectedPaths);
54123
+ if (commitResult.replayed && commitResult.replayNoop) {
54124
+ return {
54125
+ message: `Memory ${args.command} matched newer remote memory; skipped an extra commit.`
54126
+ };
54127
+ }
54128
+ if (commitResult.replayed) {
54129
+ return {
54130
+ message: `Memory ${args.command} reapplied on top of newer remote memory and pushed (${commitResult.sha?.slice(0, 7) ?? "unknown"}).`
54131
+ };
54132
+ }
54133
+ return {
54134
+ message: `Memory ${args.command} applied and pushed (${commitResult.sha?.slice(0, 7) ?? "unknown"}).`
54135
+ };
54136
+ }
54137
+ async function applyMemoryCommand(memoryDir, args, options) {
53413
54138
  const command = args.command;
53414
54139
  if (command === "create") {
53415
54140
  const pathArg = requireString(args.file_path, "file_path", "create");
@@ -53417,17 +54142,25 @@ async function memory(args) {
53417
54142
  const label = normalizeMemoryLabel(memoryDir, pathArg, "file_path");
53418
54143
  const filePath = resolveMemoryFilePath(memoryDir, label);
53419
54144
  const relPath = toRepoRelative(memoryDir, filePath);
53420
- if (existsSync19(filePath)) {
53421
- throw new Error(`memory create: block already exists at ${pathArg}`);
53422
- }
53423
54145
  const body = args.file_text ?? "";
53424
54146
  const rendered = renderMemoryFile({
53425
54147
  description
53426
54148
  }, body);
54149
+ if (existsSync19(filePath)) {
54150
+ if (!options?.replaying) {
54151
+ throw new Error(`memory create: block already exists at ${pathArg}`);
54152
+ }
54153
+ const existingContent = await readFile5(filePath, "utf8");
54154
+ if (normalizeComparableContent2(existingContent) === normalizeComparableContent2(rendered)) {
54155
+ return [relPath];
54156
+ }
54157
+ throw new Error(`memory create: block already exists at ${pathArg}`);
54158
+ }
53427
54159
  await mkdir5(dirname8(filePath), { recursive: true });
53428
54160
  await writeFile5(filePath, rendered, "utf8");
53429
- affectedPaths = [relPath];
53430
- } else if (command === "str_replace") {
54161
+ return [relPath];
54162
+ }
54163
+ if (command === "str_replace") {
53431
54164
  const pathArg = requireString(args.file_path, "file_path", "str_replace");
53432
54165
  const oldString = requireString(args.old_string, "old_string", "str_replace");
53433
54166
  const newString = requireString(args.new_string, "new_string", "str_replace");
@@ -53442,8 +54175,9 @@ async function memory(args) {
53442
54175
  const nextBody = `${file.body.slice(0, idx)}${newString}${file.body.slice(idx + oldString.length)}`;
53443
54176
  const rendered = renderMemoryFile(file.frontmatter, nextBody);
53444
54177
  await writeFile5(filePath, rendered, "utf8");
53445
- affectedPaths = [relPath];
53446
- } else if (command === "insert") {
54178
+ return [relPath];
54179
+ }
54180
+ if (command === "insert") {
53447
54181
  const pathArg = requireString(args.file_path, "file_path", "insert");
53448
54182
  const insertText = requireString(args.insert_text, "insert_text", "insert");
53449
54183
  if (typeof args.insert_line !== "number" || Number.isNaN(args.insert_line)) {
@@ -53464,23 +54198,27 @@ async function memory(args) {
53464
54198
  `);
53465
54199
  const rendered = renderMemoryFile(file.frontmatter, nextBody);
53466
54200
  await writeFile5(filePath, rendered, "utf8");
53467
- affectedPaths = [relPath];
53468
- } else if (command === "delete") {
54201
+ return [relPath];
54202
+ }
54203
+ if (command === "delete") {
54204
+ if (options?.replaying) {
54205
+ throw new Error("memory delete could not be replayed safely after remote changes");
54206
+ }
53469
54207
  const pathArg = requireString(args.file_path, "file_path", "delete");
53470
54208
  const label = normalizeMemoryLabel(memoryDir, pathArg, "file_path");
53471
54209
  const targetPath = resolveMemoryPath(memoryDir, label);
53472
54210
  if (existsSync19(targetPath) && (await stat2(targetPath)).isDirectory()) {
53473
- const relPath = toRepoRelative(memoryDir, targetPath);
54211
+ const relPath2 = toRepoRelative(memoryDir, targetPath);
53474
54212
  await rm(targetPath, { recursive: true, force: false });
53475
- affectedPaths = [relPath];
53476
- } else {
53477
- const filePath = resolveMemoryFilePath(memoryDir, label);
53478
- const relPath = toRepoRelative(memoryDir, filePath);
53479
- await loadEditableMemoryFile(filePath, pathArg);
53480
- await unlink(filePath);
53481
- affectedPaths = [relPath];
54213
+ return [relPath2];
53482
54214
  }
53483
- } else if (command === "rename") {
54215
+ const filePath = resolveMemoryFilePath(memoryDir, label);
54216
+ const relPath = toRepoRelative(memoryDir, filePath);
54217
+ await loadEditableMemoryFile(filePath, pathArg);
54218
+ await unlink(filePath);
54219
+ return [relPath];
54220
+ }
54221
+ if (command === "rename") {
53484
54222
  const oldPathArg = requireString(args.old_path, "old_path", "rename");
53485
54223
  const newPathArg = requireString(args.new_path, "new_path", "rename");
53486
54224
  const oldLabel = normalizeMemoryLabel(memoryDir, oldPathArg, "old_path");
@@ -53495,8 +54233,9 @@ async function memory(args) {
53495
54233
  await loadEditableMemoryFile(oldFilePath, oldPathArg);
53496
54234
  await mkdir5(dirname8(newFilePath), { recursive: true });
53497
54235
  await rename(oldFilePath, newFilePath);
53498
- affectedPaths = [oldRelPath, newRelPath];
53499
- } else if (command === "update_description") {
54236
+ return [oldRelPath, newRelPath];
54237
+ }
54238
+ if (command === "update_description") {
53500
54239
  const pathArg = requireString(args.file_path, "file_path", "update_description");
53501
54240
  const newDescription = requireString(args.description, "description", "update_description");
53502
54241
  const label = normalizeMemoryLabel(memoryDir, pathArg, "file_path");
@@ -53508,24 +54247,9 @@ async function memory(args) {
53508
54247
  description: newDescription
53509
54248
  }, file.body);
53510
54249
  await writeFile5(filePath, rendered, "utf8");
53511
- affectedPaths = [relPath];
53512
- } else {
53513
- throw new Error(`Unsupported memory command: ${command}`);
53514
- }
53515
- affectedPaths = Array.from(new Set(affectedPaths)).filter((p) => p.length > 0);
53516
- if (affectedPaths.length === 0) {
53517
- return { message: `Memory ${command} completed with no changed paths.` };
54250
+ return [relPath];
53518
54251
  }
53519
- const commitResult = await commitAndPush(memoryDir, affectedPaths, reason);
53520
- if (!commitResult.committed) {
53521
- return {
53522
- message: `Memory ${command} made no effective changes; skipped commit and push.`
53523
- };
53524
- }
53525
- emitMemoryUpdated(affectedPaths);
53526
- return {
53527
- message: `Memory ${command} applied and pushed (${commitResult.sha?.slice(0, 7) ?? "unknown"}).`
53528
- };
54252
+ throw new Error(`Unsupported memory command: ${command}`);
53529
54253
  }
53530
54254
  function resolveMemoryDir() {
53531
54255
  const direct = process.env.MEMORY_DIR || process.env.LETTA_MEMORY_DIR;
@@ -53541,7 +54265,7 @@ function resolveMemoryDir() {
53541
54265
  })();
53542
54266
  const agentId = contextAgentId || (process.env.AGENT_ID || process.env.LETTA_AGENT_ID || "").trim();
53543
54267
  if (agentId && agentId.trim().length > 0) {
53544
- return resolve13(homedir16(), ".letta", "agents", agentId, "memory");
54268
+ return resolve13(homedir17(), ".letta", "agents", agentId, "memory");
53545
54269
  }
53546
54270
  throw new Error("memory: unable to resolve memory directory. Ensure MEMORY_DIR (or AGENT_ID) is available.");
53547
54271
  }
@@ -53691,78 +54415,6 @@ ${body}`;
53691
54415
  function sanitizeFrontmatterValue(value) {
53692
54416
  return value.replace(/\r?\n/g, " ").trim();
53693
54417
  }
53694
- async function runGit4(memoryDir, args) {
53695
- try {
53696
- const result = await execFile6("git", args, {
53697
- cwd: memoryDir,
53698
- maxBuffer: 10 * 1024 * 1024,
53699
- env: {
53700
- ...process.env,
53701
- PAGER: "cat",
53702
- GIT_PAGER: "cat"
53703
- }
53704
- });
53705
- return {
53706
- stdout: result.stdout?.toString() ?? "",
53707
- stderr: result.stderr?.toString() ?? ""
53708
- };
53709
- } catch (error) {
53710
- const stderr = typeof error === "object" && error !== null && "stderr" in error ? String(error.stderr ?? "") : "";
53711
- const stdout = typeof error === "object" && error !== null && "stdout" in error ? String(error.stdout ?? "") : "";
53712
- const message = error instanceof Error ? error.message : String(error);
53713
- throw new Error(`git ${args.join(" ")} failed: ${stderr || stdout || message}`.trim());
53714
- }
53715
- }
53716
- async function commitAndPush(memoryDir, pathspecs, reason) {
53717
- await runGit4(memoryDir, ["add", "-A", "--", ...pathspecs]);
53718
- const status = await runGit4(memoryDir, [
53719
- "status",
53720
- "--porcelain",
53721
- "--",
53722
- ...pathspecs
53723
- ]);
53724
- if (!status.stdout.trim()) {
53725
- return { committed: false };
53726
- }
53727
- const { agentId, agentName } = await getAgentIdentity();
53728
- const authorName = agentName.trim() || agentId;
53729
- const authorEmail = `${agentId}@letta.com`;
53730
- try {
53731
- await runGit4(memoryDir, [
53732
- "-c",
53733
- `user.name=${authorName}`,
53734
- "-c",
53735
- `user.email=${authorEmail}`,
53736
- "commit",
53737
- "-m",
53738
- reason
53739
- ]);
53740
- } catch (error) {
53741
- await unstagePaths(memoryDir, pathspecs);
53742
- throw error;
53743
- }
53744
- const head = await runGit4(memoryDir, ["rev-parse", "HEAD"]);
53745
- const sha = head.stdout.trim();
53746
- await maybeUpdateMemoryRemoteOrigin(memoryDir, agentId);
53747
- try {
53748
- await runGit4(memoryDir, ["push"]);
53749
- } catch (error) {
53750
- const message = error instanceof Error ? error.message : String(error);
53751
- throw new Error(`Memory changes were committed (${sha.slice(0, 7)}) but push failed: ${message}`);
53752
- }
53753
- return {
53754
- committed: true,
53755
- sha
53756
- };
53757
- }
53758
- async function unstagePaths(memoryDir, pathspecs) {
53759
- if (pathspecs.length === 0) {
53760
- return;
53761
- }
53762
- try {
53763
- await runGit4(memoryDir, ["reset", "HEAD", "--", ...pathspecs]);
53764
- } catch {}
53765
- }
53766
54418
  function requireString(value, field, command) {
53767
54419
  if (typeof value !== "string" || value.trim().length === 0) {
53768
54420
  throw new Error(`memory ${command}: '${field}' must be a non-empty string`);
@@ -53784,16 +54436,13 @@ function emitMemoryUpdated(affectedPaths) {
53784
54436
  }));
53785
54437
  } catch {}
53786
54438
  }
53787
- var execFile6;
53788
54439
  var init_Memory2 = __esm(() => {
53789
54440
  init_client2();
53790
54441
  init_context();
53791
54442
  init_memoryGit();
53792
- execFile6 = promisify6(execFileCb3);
53793
54443
  });
53794
54444
 
53795
54445
  // src/tools/impl/MemoryApplyPatch.ts
53796
- import { execFile as execFileCb4 } from "node:child_process";
53797
54446
  import { existsSync as existsSync20 } from "node:fs";
53798
54447
  import {
53799
54448
  access,
@@ -53804,9 +54453,8 @@ import {
53804
54453
  unlink as unlink2,
53805
54454
  writeFile as writeFile6
53806
54455
  } from "node:fs/promises";
53807
- import { homedir as homedir17 } from "node:os";
54456
+ import { homedir as homedir18 } from "node:os";
53808
54457
  import { dirname as dirname9, isAbsolute as isAbsolute10, relative as relative6, resolve as resolve14 } from "node:path";
53809
- import { promisify as promisify7 } from "node:util";
53810
54458
  async function getAgentIdentity2() {
53811
54459
  const envAgentId = (process.env.AGENT_ID || process.env.LETTA_AGENT_ID || "").trim();
53812
54460
  const contextAgentId = (() => {
@@ -53843,6 +54491,43 @@ async function memory_apply_patch(args) {
53843
54491
  }
53844
54492
  const memoryDir = resolveMemoryDir2();
53845
54493
  ensureMemoryRepo2(memoryDir);
54494
+ await assertMemoryRepoReadyForWrite(memoryDir);
54495
+ const pathspecs = await applyMemoryPatch(memoryDir, input);
54496
+ if (pathspecs.length === 0) {
54497
+ return { message: "memory_apply_patch completed with no changed paths." };
54498
+ }
54499
+ const { agentId, agentName } = await getAgentIdentity2();
54500
+ const commitResult = await commitAndSyncMemoryWrite({
54501
+ memoryDir,
54502
+ pathspecs,
54503
+ reason,
54504
+ author: {
54505
+ agentId,
54506
+ authorName: agentName.trim() || agentId,
54507
+ authorEmail: `${agentId}@letta.com`
54508
+ },
54509
+ replay: async () => applyMemoryPatch(memoryDir, input)
54510
+ });
54511
+ if (!commitResult.committed) {
54512
+ return {
54513
+ message: "memory_apply_patch made no effective changes; skipped commit and push."
54514
+ };
54515
+ }
54516
+ if (commitResult.replayed && commitResult.replayNoop) {
54517
+ return {
54518
+ message: "memory_apply_patch matched newer remote memory; skipped an extra commit."
54519
+ };
54520
+ }
54521
+ if (commitResult.replayed) {
54522
+ return {
54523
+ message: `memory_apply_patch reapplied on top of newer remote memory and pushed (${commitResult.sha?.slice(0, 7) ?? "unknown"}).`
54524
+ };
54525
+ }
54526
+ return {
54527
+ message: `memory_apply_patch applied and pushed (${commitResult.sha?.slice(0, 7) ?? "unknown"}).`
54528
+ };
54529
+ }
54530
+ async function applyMemoryPatch(memoryDir, input) {
53846
54531
  const ops = parsePatchOperations(memoryDir, input);
53847
54532
  if (ops.length === 0) {
53848
54533
  throw new Error("memory_apply_patch: no file operations found in patch");
@@ -53934,18 +54619,7 @@ async function memory_apply_patch(args) {
53934
54619
  }
53935
54620
  }
53936
54621
  const pathspecs = Array.from(affectedPaths).filter((p) => p.length > 0);
53937
- if (pathspecs.length === 0) {
53938
- return { message: "memory_apply_patch completed with no changed paths." };
53939
- }
53940
- const commitResult = await commitAndPush2(memoryDir, pathspecs, reason);
53941
- if (!commitResult.committed) {
53942
- return {
53943
- message: "memory_apply_patch made no effective changes; skipped commit and push."
53944
- };
53945
- }
53946
- return {
53947
- message: `memory_apply_patch applied and pushed (${commitResult.sha?.slice(0, 7) ?? "unknown"}).`
53948
- };
54622
+ return pathspecs;
53949
54623
  }
53950
54624
  function parsePatchOperations(memoryDir, input) {
53951
54625
  const lines = input.split(/\r?\n/);
@@ -54092,7 +54766,7 @@ function resolveMemoryDir2() {
54092
54766
  })();
54093
54767
  const agentId = contextAgentId || (process.env.AGENT_ID || process.env.LETTA_AGENT_ID || "").trim();
54094
54768
  if (agentId && agentId.trim().length > 0) {
54095
- return resolve14(homedir17(), ".letta", "agents", agentId, "memory");
54769
+ return resolve14(homedir18(), ".letta", "agents", agentId, "memory");
54096
54770
  }
54097
54771
  throw new Error("memory_apply_patch: unable to resolve memory directory. Ensure MEMORY_DIR (or AGENT_ID) is available.");
54098
54772
  }
@@ -54241,65 +54915,6 @@ ${body}`;
54241
54915
  function sanitizeFrontmatterValue2(value) {
54242
54916
  return value.replace(/\r?\n/g, " ").trim();
54243
54917
  }
54244
- async function runGit5(memoryDir, args) {
54245
- try {
54246
- const result = await execFile7("git", args, {
54247
- cwd: memoryDir,
54248
- maxBuffer: 10 * 1024 * 1024,
54249
- env: {
54250
- ...process.env,
54251
- PAGER: "cat",
54252
- GIT_PAGER: "cat"
54253
- }
54254
- });
54255
- return {
54256
- stdout: result.stdout?.toString() ?? "",
54257
- stderr: result.stderr?.toString() ?? ""
54258
- };
54259
- } catch (error) {
54260
- const stderr = typeof error === "object" && error !== null && "stderr" in error ? String(error.stderr ?? "") : "";
54261
- const stdout = typeof error === "object" && error !== null && "stdout" in error ? String(error.stdout ?? "") : "";
54262
- const message = error instanceof Error ? error.message : String(error);
54263
- throw new Error(`git ${args.join(" ")} failed: ${stderr || stdout || message}`.trim());
54264
- }
54265
- }
54266
- async function commitAndPush2(memoryDir, pathspecs, reason) {
54267
- await runGit5(memoryDir, ["add", "-A", "--", ...pathspecs]);
54268
- const status = await runGit5(memoryDir, [
54269
- "status",
54270
- "--porcelain",
54271
- "--",
54272
- ...pathspecs
54273
- ]);
54274
- if (!status.stdout.trim()) {
54275
- return { committed: false };
54276
- }
54277
- const { agentId, agentName } = await getAgentIdentity2();
54278
- const authorName = agentName.trim() || agentId;
54279
- const authorEmail = `${agentId}@letta.com`;
54280
- await runGit5(memoryDir, [
54281
- "-c",
54282
- `user.name=${authorName}`,
54283
- "-c",
54284
- `user.email=${authorEmail}`,
54285
- "commit",
54286
- "-m",
54287
- reason
54288
- ]);
54289
- const head = await runGit5(memoryDir, ["rev-parse", "HEAD"]);
54290
- const sha = head.stdout.trim();
54291
- await maybeUpdateMemoryRemoteOrigin(memoryDir, agentId);
54292
- try {
54293
- await runGit5(memoryDir, ["push"]);
54294
- } catch (error) {
54295
- const message = error instanceof Error ? error.message : String(error);
54296
- throw new Error(`Memory changes were committed (${sha.slice(0, 7)}) but push failed: ${message}`);
54297
- }
54298
- return {
54299
- committed: true,
54300
- sha
54301
- };
54302
- }
54303
54918
  async function isMissing(filePath) {
54304
54919
  try {
54305
54920
  await access(filePath);
@@ -54360,12 +54975,10 @@ function buildOldNewChunks(lines) {
54360
54975
  newChunk: newParts.join("")
54361
54976
  };
54362
54977
  }
54363
- var execFile7;
54364
54978
  var init_MemoryApplyPatch2 = __esm(() => {
54365
54979
  init_client2();
54366
54980
  init_context();
54367
54981
  init_memoryGit();
54368
- execFile7 = promisify7(execFileCb4);
54369
54982
  });
54370
54983
 
54371
54984
  // src/tools/impl/MessageChannel.ts
@@ -68955,211 +69568,6 @@ var init_subagentState = __esm(() => {
68955
69568
  streamEventListeners = new Set;
68956
69569
  });
68957
69570
 
68958
- // src/permissions/canonical.ts
68959
- function canonicalToolName(toolName) {
68960
- if (SHELL_TOOL_NAMES.has(toolName))
68961
- return "Bash";
68962
- if (READ_TOOL_NAMES.has(toolName))
68963
- return "Read";
68964
- if (WRITE_TOOL_NAMES.has(toolName))
68965
- return "Write";
68966
- if (EDIT_TOOL_NAMES.has(toolName))
68967
- return "Edit";
68968
- if (GLOB_TOOL_NAMES.has(toolName))
68969
- return "Glob";
68970
- if (GREP_TOOL_NAMES.has(toolName))
68971
- return "Grep";
68972
- if (LIST_TOOL_NAMES.has(toolName))
68973
- return "ListDir";
68974
- if (TASK_TOOL_NAMES.has(toolName))
68975
- return "Task";
68976
- return toolName;
68977
- }
68978
- function isShellToolName(toolName) {
68979
- return canonicalToolName(toolName) === "Bash";
68980
- }
68981
- function isFileToolName(toolName) {
68982
- return FILE_TOOL_FAMILIES.has(canonicalToolName(toolName));
68983
- }
68984
- function canonicalizePathLike(value) {
68985
- let normalized = value.replace(/\\/g, "/").trim();
68986
- if (/^\/+[a-zA-Z]:\//.test(normalized)) {
68987
- normalized = normalized.replace(/^\/+/, "");
68988
- }
68989
- if (/^[a-zA-Z]:\//.test(normalized)) {
68990
- normalized = `${normalized[0]?.toUpperCase() ?? ""}${normalized.slice(1)}`;
68991
- }
68992
- return normalized;
68993
- }
68994
- var SHELL_TOOL_NAMES, READ_TOOL_NAMES, WRITE_TOOL_NAMES, EDIT_TOOL_NAMES, GLOB_TOOL_NAMES, GREP_TOOL_NAMES, LIST_TOOL_NAMES, TASK_TOOL_NAMES, FILE_TOOL_FAMILIES;
68995
- var init_canonical = __esm(() => {
68996
- SHELL_TOOL_NAMES = new Set([
68997
- "Bash",
68998
- "shell",
68999
- "Shell",
69000
- "shell_command",
69001
- "ShellCommand",
69002
- "run_shell_command",
69003
- "RunShellCommand"
69004
- ]);
69005
- READ_TOOL_NAMES = new Set([
69006
- "Read",
69007
- "read_file",
69008
- "ReadFile",
69009
- "read_file_gemini",
69010
- "ReadFileGemini"
69011
- ]);
69012
- WRITE_TOOL_NAMES = new Set([
69013
- "Write",
69014
- "write_file",
69015
- "WriteFile",
69016
- "write_file_gemini",
69017
- "WriteFileGemini"
69018
- ]);
69019
- EDIT_TOOL_NAMES = new Set([
69020
- "Edit",
69021
- "MultiEdit",
69022
- "NotebookEdit",
69023
- "replace",
69024
- "Replace"
69025
- ]);
69026
- GLOB_TOOL_NAMES = new Set(["Glob", "glob_gemini", "GlobGemini"]);
69027
- GREP_TOOL_NAMES = new Set([
69028
- "Grep",
69029
- "grep_files",
69030
- "GrepFiles",
69031
- "search_file_content",
69032
- "SearchFileContent"
69033
- ]);
69034
- LIST_TOOL_NAMES = new Set([
69035
- "list_dir",
69036
- "ListDir",
69037
- "list_directory",
69038
- "ListDirectory",
69039
- "LS"
69040
- ]);
69041
- TASK_TOOL_NAMES = new Set(["Task", "task"]);
69042
- FILE_TOOL_FAMILIES = new Set([
69043
- "Read",
69044
- "Write",
69045
- "Edit",
69046
- "Glob",
69047
- "Grep",
69048
- "ListDir"
69049
- ]);
69050
- });
69051
-
69052
- // src/permissions/rule-normalization.ts
69053
- function splitRule(rule) {
69054
- const match2 = rule.trim().match(/^([^(]+)(?:\(([\s\S]*)\))?$/);
69055
- if (!match2?.[1]) {
69056
- return { tool: rule.trim(), payload: null };
69057
- }
69058
- return {
69059
- tool: match2[1].trim(),
69060
- payload: match2[2] !== undefined ? match2[2] : null
69061
- };
69062
- }
69063
- function normalizePermissionRule(rule) {
69064
- const { tool, payload } = splitRule(rule);
69065
- const canonicalTool = canonicalToolName(tool);
69066
- if (payload === null) {
69067
- return canonicalTool;
69068
- }
69069
- if (isShellToolName(canonicalTool)) {
69070
- return `Bash(${normalizeBashRulePayload(payload)})`;
69071
- }
69072
- if (isFileToolName(canonicalTool)) {
69073
- return `${canonicalTool}(${canonicalizePathLike(payload)})`;
69074
- }
69075
- return `${canonicalTool}(${payload.trim()})`;
69076
- }
69077
- function permissionRulesEquivalent(left, right) {
69078
- return normalizePermissionRule(left) === normalizePermissionRule(right);
69079
- }
69080
- var init_rule_normalization = __esm(() => {
69081
- init_canonical();
69082
- init_shell_command_normalization();
69083
- });
69084
-
69085
- // src/permissions/cli.ts
69086
- var exports_cli = {};
69087
- __export(exports_cli, {
69088
- cliPermissions: () => cliPermissions
69089
- });
69090
-
69091
- class CliPermissions {
69092
- allowedTools = [];
69093
- disallowedTools = [];
69094
- setAllowedTools(toolsString) {
69095
- this.allowedTools = this.parseToolList(toolsString);
69096
- }
69097
- setDisallowedTools(toolsString) {
69098
- this.disallowedTools = this.parseToolList(toolsString);
69099
- }
69100
- parseToolList(toolsString) {
69101
- if (!toolsString)
69102
- return [];
69103
- const tools = [];
69104
- let current = "";
69105
- let depth = 0;
69106
- for (let i = 0;i < toolsString.length; i++) {
69107
- const char = toolsString[i];
69108
- if (char === "(") {
69109
- depth++;
69110
- current += char;
69111
- } else if (char === ")") {
69112
- depth--;
69113
- current += char;
69114
- } else if (char === "," && depth === 0) {
69115
- if (current.trim()) {
69116
- tools.push(this.normalizePattern(current.trim()));
69117
- }
69118
- current = "";
69119
- } else {
69120
- current += char;
69121
- }
69122
- }
69123
- if (current.trim()) {
69124
- tools.push(this.normalizePattern(current.trim()));
69125
- }
69126
- return tools;
69127
- }
69128
- normalizePattern(pattern) {
69129
- const trimmed = pattern.trim();
69130
- if (trimmed.includes("(")) {
69131
- return normalizePermissionRule(trimmed);
69132
- }
69133
- const canonicalTool = canonicalToolName(trimmed);
69134
- if (isShellToolName(canonicalTool)) {
69135
- return "Bash(:*)";
69136
- }
69137
- if (isFileToolName(canonicalTool)) {
69138
- return `${canonicalTool}(**)`;
69139
- }
69140
- return canonicalTool;
69141
- }
69142
- getAllowedTools() {
69143
- return [...this.allowedTools];
69144
- }
69145
- getDisallowedTools() {
69146
- return [...this.disallowedTools];
69147
- }
69148
- hasOverrides() {
69149
- return this.allowedTools.length > 0 || this.disallowedTools.length > 0;
69150
- }
69151
- clear() {
69152
- this.allowedTools = [];
69153
- this.disallowedTools = [];
69154
- }
69155
- }
69156
- var cliPermissions;
69157
- var init_cli = __esm(() => {
69158
- init_canonical();
69159
- init_rule_normalization();
69160
- cliPermissions = new CliPermissions;
69161
- });
69162
-
69163
69571
  // src/agent/prompts/recall_subagent.md
69164
69572
  var recall_subagent_default = `Your task is to recall past experience based on a query. Use the CLI commands and search strategies below to search conversation history. Try to finish quickly with just 1-2 tool calls if possible (but use more if needed).
69165
69573
 
@@ -69639,11 +70047,14 @@ async function executeSubagent(type, config, model, userPrompt, baseURL, subagen
69639
70047
  delete childEnv.MEMORY_DIR;
69640
70048
  delete childEnv.LETTA_MEMORY_DIR;
69641
70049
  }
69642
- const parentMemoryDir = process.env.MEMORY_DIR || process.env.LETTA_MEMORY_DIR;
69643
- if (parentMemoryDir && parentMemoryDir.trim().length > 0) {
69644
- childEnv.PARENT_MEMORY_DIR = parentMemoryDir;
70050
+ const nextScope = new Set(parseScopeList(process.env.LETTA_MEMORY_SCOPE));
70051
+ if (parentAgentId) {
70052
+ nextScope.add(parentAgentId);
70053
+ }
70054
+ if (nextScope.size > 0) {
70055
+ childEnv.LETTA_MEMORY_SCOPE = [...nextScope].join(",");
69645
70056
  } else {
69646
- delete childEnv.PARENT_MEMORY_DIR;
70057
+ delete childEnv.LETTA_MEMORY_SCOPE;
69647
70058
  }
69648
70059
  }
69649
70060
  const proc2 = spawn5(launcher.command, launcher.args, {
@@ -70247,7 +70658,10 @@ async function task(args) {
70247
70658
  const parentAgentId = getCurrentAgentId();
70248
70659
  const parentConvId = getConversationId() ?? "default";
70249
70660
  const forkedConv = await client.post(`/v1/conversations/${encodeURIComponent(parentConvId)}/fork`, {
70250
- query: parentConvId === "default" ? { agent_id: parentAgentId } : {}
70661
+ query: {
70662
+ ...parentConvId === "default" ? { agent_id: parentAgentId } : {},
70663
+ hidden: true
70664
+ }
70251
70665
  });
70252
70666
  effectiveAgentId = parentAgentId;
70253
70667
  effectiveConversationId = forkedConv.id;
@@ -73459,8 +73873,8 @@ function matchesFilePattern(query, pattern, workingDirectory, options) {
73459
73873
  globPattern = globPattern.slice(2);
73460
73874
  }
73461
73875
  if (globPattern.startsWith("~/")) {
73462
- const homedir18 = __require("node:os").homedir();
73463
- globPattern = globPattern.replace(/^~/, homedir18);
73876
+ const homedir19 = __require("node:os").homedir();
73877
+ globPattern = globPattern.replace(/^~/, homedir19);
73464
73878
  }
73465
73879
  globPattern = normalizeAbsolutePattern(globPattern, workingDirectory);
73466
73880
  const windowsContext = isWindowsContext(workingDirectory);
@@ -73655,6 +74069,18 @@ function checkPermissionForEngine(engine, toolName, toolArgs, permissions, worki
73655
74069
  const trace = createTrace(engine, toolName, canonicalTool, query);
73656
74070
  const sessionRules = sessionPermissions.getRules();
73657
74071
  const workingDirectoryTools = engine === "v2" ? WORKING_DIRECTORY_TOOLS_V2 : WORKING_DIRECTORY_TOOLS_V1;
74072
+ const guardResult = evaluateCrossAgentGuard(toolName, toolArgs, workingDirectory);
74073
+ if (guardResult) {
74074
+ traceEvent(trace, "cross-agent-guard", guardResult.reason);
74075
+ return {
74076
+ result: {
74077
+ decision: "deny",
74078
+ matchedRule: guardResult.matchedRule,
74079
+ reason: guardResult.reason
74080
+ },
74081
+ trace
74082
+ };
74083
+ }
73658
74084
  if (permissions.deny) {
73659
74085
  for (const pattern of permissions.deny) {
73660
74086
  const matched = matchesPattern(toolName, query, pattern, workingDirectory, engine);
@@ -73731,7 +74157,7 @@ function checkPermissionForEngine(engine, toolName, toolArgs, permissions, worki
73731
74157
  };
73732
74158
  }
73733
74159
  if (READ_ONLY_SHELL_TOOLS.has(toolName) || isShellToolName(canonicalTool)) {
73734
- const shellCommand = extractShellCommand(toolArgs);
74160
+ const shellCommand = extractShellCommand2(toolArgs);
73735
74161
  if (shellCommand && isReadOnlyShellCommand(shellCommand, {
73736
74162
  allowedPathRoots: getAllowedShellPathRoots(permissions, workingDirectory)
73737
74163
  })) {
@@ -73833,18 +74259,6 @@ function checkPermissionForEngine(engine, toolName, toolArgs, permissions, worki
73833
74259
  trace
73834
74260
  };
73835
74261
  }
73836
- function extractFilePath(toolArgs) {
73837
- if (typeof toolArgs.file_path === "string" && toolArgs.file_path.length > 0) {
73838
- return toolArgs.file_path;
73839
- }
73840
- if (typeof toolArgs.path === "string" && toolArgs.path.length > 0) {
73841
- return toolArgs.path;
73842
- }
73843
- if (typeof toolArgs.notebook_path === "string" && toolArgs.notebook_path.length > 0) {
73844
- return toolArgs.notebook_path;
73845
- }
73846
- return null;
73847
- }
73848
74262
  function isWithinAllowedDirectories(filePath, permissions, workingDirectory) {
73849
74263
  const absolutePath = resolve22(workingDirectory, filePath);
73850
74264
  if (absolutePath.startsWith(workingDirectory)) {
@@ -73918,7 +74332,7 @@ function buildPermissionQuery(toolName, toolArgs, engine) {
73918
74332
  return toolName;
73919
74333
  }
73920
74334
  }
73921
- function extractShellCommand(toolArgs) {
74335
+ function extractShellCommand2(toolArgs) {
73922
74336
  const command = toolArgs.command;
73923
74337
  if (typeof command === "string" || Array.isArray(command)) {
73924
74338
  return command;
@@ -74013,6 +74427,7 @@ var init_checker = __esm(() => {
74013
74427
  init_hooks();
74014
74428
  init_canonical();
74015
74429
  init_cli();
74430
+ init_crossAgentGuard();
74016
74431
  init_matcher();
74017
74432
  init_mode();
74018
74433
  init_readOnlyShell();
@@ -74092,10 +74507,10 @@ __export(exports_loader, {
74092
74507
  loadPermissions: () => loadPermissions,
74093
74508
  getUserSettingsPaths: () => getUserSettingsPaths
74094
74509
  });
74095
- import { homedir as homedir18 } from "node:os";
74510
+ import { homedir as homedir19 } from "node:os";
74096
74511
  import { join as join24 } from "node:path";
74097
74512
  function getUserSettingsPaths(options = {}) {
74098
- const homeDir = options.homeDir || homedir18();
74513
+ const homeDir = options.homeDir || homedir19();
74099
74514
  const xdgConfigHome = options.xdgConfigHome || process.env.XDG_CONFIG_HOME || join24(homeDir, ".config");
74100
74515
  return {
74101
74516
  canonical: join24(homeDir, ".letta", "settings.json"),
@@ -74217,7 +74632,7 @@ var exports_analyzer = {};
74217
74632
  __export(exports_analyzer, {
74218
74633
  analyzeApprovalContext: () => analyzeApprovalContext
74219
74634
  });
74220
- import { homedir as homedir19 } from "node:os";
74635
+ import { homedir as homedir20 } from "node:os";
74221
74636
  import { dirname as dirname13, relative as relative8, resolve as resolve23, win32 as win322 } from "node:path";
74222
74637
  function normalizeOsPath(path19) {
74223
74638
  return path19.replace(/\\/g, "/");
@@ -74254,7 +74669,7 @@ function formatAbsoluteRulePath(path19) {
74254
74669
  return `//${normalized.replace(/^\/+/, "")}`;
74255
74670
  }
74256
74671
  function formatDisplayPath(path19) {
74257
- return normalizeOsPath(path19).replace(normalizeOsPath(homedir19()), "~");
74672
+ return normalizeOsPath(path19).replace(normalizeOsPath(homedir20()), "~");
74258
74673
  }
74259
74674
  function analyzeApprovalContext(toolName, toolArgs, workingDirectory) {
74260
74675
  const canonicalTool = canonicalToolName(toolName);
@@ -74416,7 +74831,7 @@ function containsDangerousCommand(command) {
74416
74831
  }
74417
74832
  return false;
74418
74833
  }
74419
- function escapeRegex2(text) {
74834
+ function escapeRegex3(text) {
74420
74835
  return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
74421
74836
  }
74422
74837
  function normalizePathSeparators(path19) {
@@ -74448,7 +74863,7 @@ function detectSkillScript(command, workingDir) {
74448
74863
  return null;
74449
74864
  }
74450
74865
  const normalizedWorkingDir = normalizePathSeparators(workingDir).replace(/\/$/, "");
74451
- const normalizedHomeDir = normalizePathSeparators(homedir19()).replace(/\/$/, "");
74866
+ const normalizedHomeDir = normalizePathSeparators(homedir20()).replace(/\/$/, "");
74452
74867
  const detect = (source, regex2) => {
74453
74868
  for (const candidate of pathCandidates) {
74454
74869
  const match3 = candidate.match(regex2);
@@ -74461,17 +74876,17 @@ function detectSkillScript(command, workingDir) {
74461
74876
  }
74462
74877
  return null;
74463
74878
  };
74464
- const projectRegex = new RegExp(`^${escapeRegex2(normalizedWorkingDir)}/\\.skills/(.+?)/scripts/`);
74879
+ const projectRegex = new RegExp(`^${escapeRegex3(normalizedWorkingDir)}/\\.skills/(.+?)/scripts/`);
74465
74880
  const projectSkill = detect("project", projectRegex);
74466
74881
  if (projectSkill) {
74467
74882
  return projectSkill;
74468
74883
  }
74469
- const agentRegex = new RegExp(`^${escapeRegex2(normalizedHomeDir)}/\\.letta/agents/[^/]+/skills/(.+?)/scripts/`);
74884
+ const agentRegex = new RegExp(`^${escapeRegex3(normalizedHomeDir)}/\\.letta/agents/[^/]+/skills/(.+?)/scripts/`);
74470
74885
  const agentSkill = detect("agent-scoped", agentRegex);
74471
74886
  if (agentSkill) {
74472
74887
  return agentSkill;
74473
74888
  }
74474
- const globalRegex = new RegExp(`^${escapeRegex2(normalizedHomeDir)}/\\.letta/skills/(.+?)/scripts/`);
74889
+ const globalRegex = new RegExp(`^${escapeRegex3(normalizedHomeDir)}/\\.letta/skills/(.+?)/scripts/`);
74475
74890
  const globalSkill = detect("global", globalRegex);
74476
74891
  if (globalSkill) {
74477
74892
  return globalSkill;
@@ -77164,9 +77579,9 @@ function defineLazyProperty(object, propertyName, valueGetter) {
77164
77579
  }
77165
77580
 
77166
77581
  // node_modules/default-browser-id/index.js
77167
- import { promisify as promisify8 } from "node:util";
77582
+ import { promisify as promisify6 } from "node:util";
77168
77583
  import process15 from "node:process";
77169
- import { execFile as execFile8 } from "node:child_process";
77584
+ import { execFile as execFile6 } from "node:child_process";
77170
77585
  async function defaultBrowserId() {
77171
77586
  if (process15.platform !== "darwin") {
77172
77587
  throw new Error("macOS only");
@@ -77177,13 +77592,13 @@ async function defaultBrowserId() {
77177
77592
  }
77178
77593
  var execFileAsync4;
77179
77594
  var init_default_browser_id = __esm(() => {
77180
- execFileAsync4 = promisify8(execFile8);
77595
+ execFileAsync4 = promisify6(execFile6);
77181
77596
  });
77182
77597
 
77183
77598
  // node_modules/run-applescript/index.js
77184
77599
  import process16 from "node:process";
77185
- import { promisify as promisify9 } from "node:util";
77186
- import { execFile as execFile9, execFileSync as execFileSync2 } from "node:child_process";
77600
+ import { promisify as promisify7 } from "node:util";
77601
+ import { execFile as execFile7, execFileSync as execFileSync2 } from "node:child_process";
77187
77602
  async function runAppleScript(script, { humanReadableOutput = true, signal } = {}) {
77188
77603
  if (process16.platform !== "darwin") {
77189
77604
  throw new Error("macOS only");
@@ -77198,7 +77613,7 @@ async function runAppleScript(script, { humanReadableOutput = true, signal } = {
77198
77613
  }
77199
77614
  var execFileAsync5;
77200
77615
  var init_run_applescript = __esm(() => {
77201
- execFileAsync5 = promisify9(execFile9);
77616
+ execFileAsync5 = promisify7(execFile7);
77202
77617
  });
77203
77618
 
77204
77619
  // node_modules/bundle-name/index.js
@@ -77211,8 +77626,8 @@ var init_bundle_name = __esm(() => {
77211
77626
  });
77212
77627
 
77213
77628
  // node_modules/default-browser/windows.js
77214
- import { promisify as promisify10 } from "node:util";
77215
- import { execFile as execFile10 } from "node:child_process";
77629
+ import { promisify as promisify8 } from "node:util";
77630
+ import { execFile as execFile8 } from "node:child_process";
77216
77631
  async function defaultBrowser(_execFileAsync = execFileAsync6) {
77217
77632
  const { stdout } = await _execFileAsync("reg", [
77218
77633
  "QUERY",
@@ -77233,7 +77648,7 @@ async function defaultBrowser(_execFileAsync = execFileAsync6) {
77233
77648
  }
77234
77649
  var execFileAsync6, windowsBrowserProgIds, UnknownBrowserError;
77235
77650
  var init_windows = __esm(() => {
77236
- execFileAsync6 = promisify10(execFile10);
77651
+ execFileAsync6 = promisify8(execFile8);
77237
77652
  windowsBrowserProgIds = {
77238
77653
  AppXq0fevzme2pys62n3e0fbqa7peapykr8v: { name: "Edge", id: "com.microsoft.edge.old" },
77239
77654
  MSEdgeDHTML: { name: "Edge", id: "com.microsoft.edge" },
@@ -77250,9 +77665,9 @@ var init_windows = __esm(() => {
77250
77665
  });
77251
77666
 
77252
77667
  // node_modules/default-browser/index.js
77253
- import { promisify as promisify11 } from "node:util";
77668
+ import { promisify as promisify9 } from "node:util";
77254
77669
  import process17 from "node:process";
77255
- import { execFile as execFile11 } from "node:child_process";
77670
+ import { execFile as execFile9 } from "node:child_process";
77256
77671
  async function defaultBrowser2() {
77257
77672
  if (process17.platform === "darwin") {
77258
77673
  const id = await defaultBrowserId();
@@ -77275,7 +77690,7 @@ var init_default_browser = __esm(() => {
77275
77690
  init_default_browser_id();
77276
77691
  init_bundle_name();
77277
77692
  init_windows();
77278
- execFileAsync7 = promisify11(execFile11);
77693
+ execFileAsync7 = promisify9(execFile9);
77279
77694
  });
77280
77695
 
77281
77696
  // node_modules/open/index.js
@@ -77289,14 +77704,14 @@ import process18 from "node:process";
77289
77704
  import { Buffer as Buffer3 } from "node:buffer";
77290
77705
  import path19 from "node:path";
77291
77706
  import { fileURLToPath as fileURLToPath7 } from "node:url";
77292
- import { promisify as promisify12 } from "node:util";
77707
+ import { promisify as promisify10 } from "node:util";
77293
77708
  import childProcess from "node:child_process";
77294
77709
  import fs14, { constants as fsConstants2 } from "node:fs/promises";
77295
77710
  async function getWindowsDefaultBrowserFromWsl() {
77296
77711
  const powershellPath = await powerShellPath();
77297
77712
  const rawCommand = String.raw`(Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice").ProgId`;
77298
77713
  const encodedCommand = Buffer3.from(rawCommand, "utf16le").toString("base64");
77299
- const { stdout } = await execFile12(powershellPath, [
77714
+ const { stdout } = await execFile10(powershellPath, [
77300
77715
  "-NoProfile",
77301
77716
  "-NonInteractive",
77302
77717
  "-ExecutionPolicy",
@@ -77332,7 +77747,7 @@ function detectPlatformBinary({ [platform4]: platformBinary }, { wsl }) {
77332
77747
  }
77333
77748
  return detectArchBinary(platformBinary);
77334
77749
  }
77335
- var execFile12, __dirname2, localXdgOpenPath, platform4, arch, pTryEach = async (array, mapper) => {
77750
+ var execFile10, __dirname2, localXdgOpenPath, platform4, arch, pTryEach = async (array, mapper) => {
77336
77751
  let latestError;
77337
77752
  for (const item of array) {
77338
77753
  try {
@@ -77511,7 +77926,7 @@ var init_open = __esm(() => {
77511
77926
  init_wsl_utils();
77512
77927
  init_default_browser();
77513
77928
  init_is_inside_container();
77514
- execFile12 = promisify12(childProcess.execFile);
77929
+ execFile10 = promisify10(childProcess.execFile);
77515
77930
  __dirname2 = path19.dirname(fileURLToPath7(import.meta.url));
77516
77931
  localXdgOpenPath = path19.join(__dirname2, "xdg-open");
77517
77932
  ({ platform: platform4, arch } = process18);
@@ -83385,7 +83800,7 @@ import {
83385
83800
  readFile as readFile10,
83386
83801
  writeFile as writeFile7
83387
83802
  } from "node:fs/promises";
83388
- import { homedir as homedir21, tmpdir as tmpdir3 } from "node:os";
83803
+ import { homedir as homedir22, tmpdir as tmpdir3 } from "node:os";
83389
83804
  import { join as join28 } from "node:path";
83390
83805
  function buildReflectionSubagentPrompt(input) {
83391
83806
  const lines = [];
@@ -83579,7 +83994,7 @@ function getTranscriptRoot() {
83579
83994
  if (envRoot) {
83580
83995
  return envRoot;
83581
83996
  }
83582
- return join28(homedir21(), ".letta", DEFAULT_TRANSCRIPT_DIR);
83997
+ return join28(homedir22(), ".letta", DEFAULT_TRANSCRIPT_DIR);
83583
83998
  }
83584
83999
  function defaultState() {
83585
84000
  return { auto_cursor_line: 0 };
@@ -83819,7 +84234,7 @@ import {
83819
84234
  unlinkSync as unlinkSync7,
83820
84235
  writeFileSync as writeFileSync16
83821
84236
  } from "node:fs";
83822
- import { homedir as homedir22 } from "node:os";
84237
+ import { homedir as homedir23 } from "node:os";
83823
84238
  import { join as join29 } from "node:path";
83824
84239
  function truncateStr(value, maxLen) {
83825
84240
  if (value === null || value === undefined)
@@ -83960,7 +84375,7 @@ class ChunkLog {
83960
84375
  var MAX_ENTRIES = 100, CONTENT_TRUNCATE_LEN = 200, MAX_SESSION_FILES2 = 5, LOG_BASE_DIR, chunkLog;
83961
84376
  var init_chunkLog = __esm(() => {
83962
84377
  init_debug();
83963
- LOG_BASE_DIR = join29(homedir22(), ".letta", "logs", "chunk-logs");
84378
+ LOG_BASE_DIR = join29(homedir23(), ".letta", "logs", "chunk-logs");
83964
84379
  chunkLog = new ChunkLog;
83965
84380
  });
83966
84381
 
@@ -84960,10 +85375,10 @@ var init_constants2 = __esm(() => {
84960
85375
  // src/websocket/listener/remote-settings.ts
84961
85376
  import { existsSync as existsSync26, readFileSync as readFileSync17 } from "node:fs";
84962
85377
  import { mkdir as mkdir8, writeFile as writeFile8 } from "node:fs/promises";
84963
- import { homedir as homedir23 } from "node:os";
85378
+ import { homedir as homedir24 } from "node:os";
84964
85379
  import path20 from "node:path";
84965
85380
  function getRemoteSettingsPath() {
84966
- return path20.join(homedir23(), ".letta", "remote-settings.json");
85381
+ return path20.join(homedir24(), ".letta", "remote-settings.json");
84967
85382
  }
84968
85383
  function loadRemoteSettings() {
84969
85384
  if (_cache !== null) {
@@ -85007,7 +85422,7 @@ function saveRemoteSettings(updates) {
85007
85422
  }
85008
85423
  function loadLegacyCwdCache() {
85009
85424
  try {
85010
- const legacyPath = path20.join(homedir23(), ".letta", "cwd-cache.json");
85425
+ const legacyPath = path20.join(homedir24(), ".letta", "cwd-cache.json");
85011
85426
  if (!existsSync26(legacyPath))
85012
85427
  return {};
85013
85428
  const raw = readFileSync17(legacyPath, "utf-8");
@@ -92475,6 +92890,9 @@ async function handleNewWorktree(params) {
92475
92890
  const newWorktreePath = path22.join(worktreesDir, filename);
92476
92891
  if (!await directoryExists(newWorktreePath))
92477
92892
  return;
92893
+ const conversationRuntime = getConversationRuntime(runtime, agentId, conversationId);
92894
+ if (!conversationRuntime?.isProcessing)
92895
+ return;
92478
92896
  const currentCwd = getConversationWorkingDirectory(runtime, agentId, conversationId);
92479
92897
  if (currentCwd === newWorktreePath)
92480
92898
  return;
@@ -92519,6 +92937,7 @@ async function safeReaddir(dir) {
92519
92937
  var WORKTREES_DIR = ".letta/worktrees", DEBOUNCE_MS = 500;
92520
92938
  var init_worktree_watcher = __esm(async () => {
92521
92939
  init_cwd();
92940
+ init_runtime3();
92522
92941
  await init_protocol_outbound();
92523
92942
  });
92524
92943
 
@@ -92602,10 +93021,10 @@ function readFileContent(fullPath) {
92602
93021
  var init_memoryScanner = () => {};
92603
93022
 
92604
93023
  // src/websocket/listener/client.ts
92605
- import { execFile as execFile13 } from "node:child_process";
93024
+ import { execFile as execFile11 } from "node:child_process";
92606
93025
  import { realpath as realpath3, stat as stat6 } from "node:fs/promises";
92607
93026
  import path23 from "node:path";
92608
- import { promisify as promisify13 } from "node:util";
93027
+ import { promisify as promisify11 } from "node:util";
92609
93028
  import WebSocket5 from "ws";
92610
93029
  async function loadChannelsService() {
92611
93030
  if (channelsServiceLoaderOverride) {
@@ -95491,9 +95910,9 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
95491
95910
  if (isMemoryHistoryCommand(parsed)) {
95492
95911
  runDetachedListenerTask("memory_history", async () => {
95493
95912
  const { getMemoryFilesystemRoot: getMemoryFilesystemRoot2 } = await Promise.resolve().then(() => (init_memoryFilesystem(), exports_memoryFilesystem));
95494
- const { execFile: execFileCb5 } = await import("node:child_process");
95495
- const { promisify: promisify14 } = await import("node:util");
95496
- const execFileAsync8 = promisify14(execFileCb5);
95913
+ const { execFile: execFileCb3 } = await import("node:child_process");
95914
+ const { promisify: promisify12 } = await import("node:util");
95915
+ const execFileAsync8 = promisify12(execFileCb3);
95497
95916
  const memoryRoot = getMemoryFilesystemRoot2(parsed.agent_id);
95498
95917
  const limit2 = parsed.limit ?? 50;
95499
95918
  const { stdout } = await execFileAsync8("git", [
@@ -95526,9 +95945,9 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
95526
95945
  if (isMemoryFileAtRefCommand(parsed)) {
95527
95946
  runDetachedListenerTask("memory_file_at_ref", async () => {
95528
95947
  const { getMemoryFilesystemRoot: getMemoryFilesystemRoot2 } = await Promise.resolve().then(() => (init_memoryFilesystem(), exports_memoryFilesystem));
95529
- const { execFile: execFileCb5 } = await import("node:child_process");
95530
- const { promisify: promisify14 } = await import("node:util");
95531
- const execFileAsync8 = promisify14(execFileCb5);
95948
+ const { execFile: execFileCb3 } = await import("node:child_process");
95949
+ const { promisify: promisify12 } = await import("node:util");
95950
+ const execFileAsync8 = promisify12(execFileCb3);
95532
95951
  const memoryRoot = getMemoryFilesystemRoot2(parsed.agent_id);
95533
95952
  try {
95534
95953
  const { stdout } = await execFileAsync8("git", ["show", `${parsed.ref}:${parsed.file_path}`], { cwd: memoryRoot, timeout: 1e4 });
@@ -95599,7 +96018,7 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
95599
96018
  try {
95600
96019
  const cwd2 = parsed.cwd ?? runtime.bootWorkingDirectory;
95601
96020
  const maxResults = parsed.max_results ?? 20;
95602
- const execFileAsync8 = promisify13(execFile13);
96021
+ const execFileAsync8 = promisify11(execFile11);
95603
96022
  const { stdout } = await execFileAsync8("git", ["branch", "-a", "--format=%(refname:short)\t%(HEAD)"], {
95604
96023
  cwd: cwd2,
95605
96024
  encoding: "utf-8",
@@ -95639,7 +96058,7 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
95639
96058
  runDetachedListenerTask("checkout_branch", async () => {
95640
96059
  try {
95641
96060
  const cwd2 = parsed.cwd ?? runtime.bootWorkingDirectory;
95642
- const execFileAsync8 = promisify13(execFile13);
96061
+ const execFileAsync8 = promisify11(execFile11);
95643
96062
  const args = parsed.create ? ["checkout", "-b", parsed.branch] : ["checkout", parsed.branch];
95644
96063
  await execFileAsync8("git", args, {
95645
96064
  cwd: cwd2,
@@ -96127,7 +96546,7 @@ import {
96127
96546
  readFileSync as readFileSync19,
96128
96547
  unlinkSync as unlinkSync8
96129
96548
  } from "node:fs";
96130
- import { homedir as homedir27 } from "node:os";
96549
+ import { homedir as homedir28 } from "node:os";
96131
96550
  import { join as join34 } from "node:path";
96132
96551
  import { format as format2 } from "node:util";
96133
96552
  function isDebugEnabled2() {
@@ -96233,7 +96652,7 @@ function debugWarn2(prefix, message, ...args) {
96233
96652
  }
96234
96653
  var DEBUG_LOG_DIR2, MAX_SESSION_FILES3 = 5, DEFAULT_TAIL_LINES2 = 50, debugLogFile2;
96235
96654
  var init_debug2 = __esm(() => {
96236
- DEBUG_LOG_DIR2 = join34(homedir27(), ".letta", "logs", "debug");
96655
+ DEBUG_LOG_DIR2 = join34(homedir28(), ".letta", "logs", "debug");
96237
96656
  debugLogFile2 = new DebugLogFile2;
96238
96657
  });
96239
96658
 
@@ -96610,7 +97029,7 @@ __export(exports_bootstrap_tools, {
96610
97029
  bootstrapBaseToolsIfNeeded: () => bootstrapBaseToolsIfNeeded
96611
97030
  });
96612
97031
  import { existsSync as existsSync31, mkdirSync as mkdirSync23, writeFileSync as writeFileSync17 } from "node:fs";
96613
- import { homedir as homedir28 } from "node:os";
97032
+ import { homedir as homedir29 } from "node:os";
96614
97033
  import { join as join36 } from "node:path";
96615
97034
  async function bootstrapBaseToolsIfNeeded() {
96616
97035
  if (existsSync31(MARKER_PATH))
@@ -96619,7 +97038,7 @@ async function bootstrapBaseToolsIfNeeded() {
96619
97038
  try {
96620
97039
  const success = await addBaseToolsToServer();
96621
97040
  if (success) {
96622
- mkdirSync23(join36(homedir28(), ".letta"), { recursive: true });
97041
+ mkdirSync23(join36(homedir29(), ".letta"), { recursive: true });
96623
97042
  writeFileSync17(MARKER_PATH, new Date().toISOString(), "utf-8");
96624
97043
  }
96625
97044
  } catch (err) {
@@ -96630,7 +97049,7 @@ var MARKER_PATH;
96630
97049
  var init_bootstrap_tools = __esm(() => {
96631
97050
  init_debug();
96632
97051
  init_create();
96633
- MARKER_PATH = join36(homedir28(), ".letta", ".bootstrapped");
97052
+ MARKER_PATH = join36(homedir29(), ".letta", ".bootstrapped");
96634
97053
  });
96635
97054
 
96636
97055
  // src/lsp/manager.ts
@@ -96822,11 +97241,11 @@ __export(exports_auto_update, {
96822
97241
  buildInstallCommand: () => buildInstallCommand,
96823
97242
  buildInstallArgs: () => buildInstallArgs
96824
97243
  });
96825
- import { execFile as execFile14 } from "node:child_process";
97244
+ import { execFile as execFile12 } from "node:child_process";
96826
97245
  import { realpathSync as realpathSync3 } from "node:fs";
96827
97246
  import { readdir as readdir9, rm as rm3 } from "node:fs/promises";
96828
97247
  import { join as join37 } from "node:path";
96829
- import { promisify as promisify14 } from "node:util";
97248
+ import { promisify as promisify12 } from "node:util";
96830
97249
  function debugLog4(...args) {
96831
97250
  if (DEBUG2) {
96832
97251
  console.error("[auto-update]", ...args);
@@ -97151,7 +97570,7 @@ var execFileAsync8, DEBUG2, DEFAULT_UPDATE_PACKAGE_NAME = "@letta-ai/letta-code"
97151
97570
  var init_auto_update2 = __esm(() => {
97152
97571
  init_errorReporting();
97153
97572
  init_version();
97154
- execFileAsync8 = promisify14(execFile14);
97573
+ execFileAsync8 = promisify12(execFile12);
97155
97574
  DEBUG2 = process.env.LETTA_DEBUG_AUTOUPDATE === "1";
97156
97575
  INSTALL_ARG_PREFIX2 = {
97157
97576
  npm: ["install", "-g"],
@@ -97860,12 +98279,16 @@ __export(exports_cli2, {
97860
98279
  class CliPermissions2 {
97861
98280
  allowedTools = [];
97862
98281
  disallowedTools = [];
98282
+ memoryScope = [];
97863
98283
  setAllowedTools(toolsString) {
97864
98284
  this.allowedTools = this.parseToolList(toolsString);
97865
98285
  }
97866
98286
  setDisallowedTools(toolsString) {
97867
98287
  this.disallowedTools = this.parseToolList(toolsString);
97868
98288
  }
98289
+ setMemoryScope(scopeString) {
98290
+ this.memoryScope = parseScopeList(scopeString);
98291
+ }
97869
98292
  parseToolList(toolsString) {
97870
98293
  if (!toolsString)
97871
98294
  return [];
@@ -97914,17 +98337,25 @@ class CliPermissions2 {
97914
98337
  getDisallowedTools() {
97915
98338
  return [...this.disallowedTools];
97916
98339
  }
98340
+ getMemoryScope() {
98341
+ return [...this.memoryScope];
98342
+ }
98343
+ hasMemoryScope() {
98344
+ return this.memoryScope.length > 0;
98345
+ }
97917
98346
  hasOverrides() {
97918
- return this.allowedTools.length > 0 || this.disallowedTools.length > 0;
98347
+ return this.allowedTools.length > 0 || this.disallowedTools.length > 0 || this.memoryScope.length > 0;
97919
98348
  }
97920
98349
  clear() {
97921
98350
  this.allowedTools = [];
97922
98351
  this.disallowedTools = [];
98352
+ this.memoryScope = [];
97923
98353
  }
97924
98354
  }
97925
98355
  var cliPermissions2;
97926
98356
  var init_cli2 = __esm(() => {
97927
98357
  init_canonical();
98358
+ init_memoryScope();
97928
98359
  init_rule_normalization();
97929
98360
  cliPermissions2 = new CliPermissions2;
97930
98361
  });
@@ -98748,7 +99179,7 @@ async function handleHeadlessCommand(parsedArgs, model, skillsDirectoryOverride,
98748
99179
  }
98749
99180
  }
98750
99181
  }
98751
- if (values.allowedTools || values.disallowedTools) {
99182
+ if (values.allowedTools || values.disallowedTools || values["memory-scope"]) {
98752
99183
  const { cliPermissions: cliPermissions3 } = await Promise.resolve().then(() => (init_cli(), exports_cli));
98753
99184
  if (values.allowedTools) {
98754
99185
  cliPermissions3.setAllowedTools(values.allowedTools);
@@ -98756,6 +99187,9 @@ async function handleHeadlessCommand(parsedArgs, model, skillsDirectoryOverride,
98756
99187
  if (values.disallowedTools) {
98757
99188
  cliPermissions3.setDisallowedTools(values.disallowedTools);
98758
99189
  }
99190
+ if (values["memory-scope"]) {
99191
+ cliPermissions3.setMemoryScope(values["memory-scope"]);
99192
+ }
98759
99193
  }
98760
99194
  const inputFormat = values["input-format"];
98761
99195
  const isBidirectionalMode = inputFormat === "stream-json";
@@ -99272,10 +99706,14 @@ In headless mode, use:
99272
99706
  }
99273
99707
  }
99274
99708
  } else if (forceNewConversation) {
99275
- const conversation = await client.conversations.create({
99709
+ const createParams = {
99276
99710
  agent_id: agent.id,
99277
99711
  isolated_block_labels: isolatedBlockLabels
99278
- });
99712
+ };
99713
+ if (fromAgentId) {
99714
+ createParams.hidden = true;
99715
+ }
99716
+ const conversation = await client.conversations.create(createParams);
99279
99717
  conversationId = conversation.id;
99280
99718
  } else if (isSubagent) {
99281
99719
  conversationId = "default";
@@ -101657,10 +102095,10 @@ __export(exports_settings, {
101657
102095
  loadProjectSettings: () => loadProjectSettings,
101658
102096
  getSetting: () => getSetting
101659
102097
  });
101660
- import { homedir as homedir31 } from "node:os";
102098
+ import { homedir as homedir32 } from "node:os";
101661
102099
  import { join as join40 } from "node:path";
101662
102100
  function getSettingsPath() {
101663
- return join40(homedir31(), ".letta", "settings.json");
102101
+ return join40(homedir32(), ".letta", "settings.json");
101664
102102
  }
101665
102103
  async function loadSettings() {
101666
102104
  const settingsPath = getSettingsPath();
@@ -121209,7 +121647,7 @@ var init_plan_viewer_template = () => {};
121209
121647
 
121210
121648
  // src/web/generate-plan-viewer.ts
121211
121649
  import { chmodSync as chmodSync2, existsSync as existsSync34, mkdirSync as mkdirSync26, writeFileSync as writeFileSync20 } from "node:fs";
121212
- import { homedir as homedir32 } from "node:os";
121650
+ import { homedir as homedir33 } from "node:os";
121213
121651
  import { join as join41 } from "node:path";
121214
121652
  async function generateAndOpenPlanViewer(planContent, planFilePath, options) {
121215
121653
  const data = {
@@ -121243,7 +121681,7 @@ async function generateAndOpenPlanViewer(planContent, planFilePath, options) {
121243
121681
  var VIEWERS_DIR;
121244
121682
  var init_generate_plan_viewer = __esm(() => {
121245
121683
  init_plan_viewer_template();
121246
- VIEWERS_DIR = join41(homedir32(), ".letta", "viewers");
121684
+ VIEWERS_DIR = join41(homedir33(), ".letta", "viewers");
121247
121685
  });
121248
121686
 
121249
121687
  // src/cli/components/StaticPlanApproval.tsx
@@ -124408,7 +124846,7 @@ import {
124408
124846
  readFileSync as readFileSync22,
124409
124847
  writeFileSync as writeFileSync21
124410
124848
  } from "node:fs";
124411
- import { homedir as homedir33, platform as platform6 } from "node:os";
124849
+ import { homedir as homedir34, platform as platform6 } from "node:os";
124412
124850
  import { dirname as dirname17, join as join43 } from "node:path";
124413
124851
  function detectTerminalType() {
124414
124852
  if (process.env.CURSOR_TRACE_ID || process.env.CURSOR_CHANNEL) {
@@ -124441,7 +124879,7 @@ function getKeybindingsPath(terminal) {
124441
124879
  }[terminal];
124442
124880
  const os7 = platform6();
124443
124881
  if (os7 === "darwin") {
124444
- return join43(homedir33(), "Library", "Application Support", appName, "User", "keybindings.json");
124882
+ return join43(homedir34(), "Library", "Application Support", appName, "User", "keybindings.json");
124445
124883
  }
124446
124884
  if (os7 === "win32") {
124447
124885
  const appData = process.env.APPDATA;
@@ -124450,7 +124888,7 @@ function getKeybindingsPath(terminal) {
124450
124888
  return join43(appData, appName, "User", "keybindings.json");
124451
124889
  }
124452
124890
  if (os7 === "linux") {
124453
- return join43(homedir33(), ".config", appName, "User", "keybindings.json");
124891
+ return join43(homedir34(), ".config", appName, "User", "keybindings.json");
124454
124892
  }
124455
124893
  return null;
124456
124894
  }
@@ -124607,10 +125045,10 @@ function getWezTermConfigPath() {
124607
125045
  if (existsSync36(xdgPath))
124608
125046
  return xdgPath;
124609
125047
  }
124610
- const configPath = join43(homedir33(), ".config", "wezterm", "wezterm.lua");
125048
+ const configPath = join43(homedir34(), ".config", "wezterm", "wezterm.lua");
124611
125049
  if (existsSync36(configPath))
124612
125050
  return configPath;
124613
- return join43(homedir33(), ".wezterm.lua");
125051
+ return join43(homedir34(), ".wezterm.lua");
124614
125052
  }
124615
125053
  function wezTermDeleteFixExists(configPath) {
124616
125054
  if (!existsSync36(configPath))
@@ -125658,10 +126096,10 @@ var init_HelpDialog = __esm(async () => {
125658
126096
  });
125659
126097
 
125660
126098
  // src/hooks/writer.ts
125661
- import { homedir as homedir34 } from "node:os";
126099
+ import { homedir as homedir35 } from "node:os";
125662
126100
  import { resolve as resolve30 } from "node:path";
125663
126101
  function isProjectSettingsPathCollidingWithGlobal2(workingDirectory) {
125664
- const home = process.env.HOME || homedir34();
126102
+ const home = process.env.HOME || homedir35();
125665
126103
  const globalSettingsPath = resolve30(home, ".letta", "settings.json");
125666
126104
  const projectSettingsPath = resolve30(workingDirectory, ".letta", "settings.json");
125667
126105
  return globalSettingsPath === projectSettingsPath;
@@ -128559,6 +128997,11 @@ function FileAutocomplete({
128559
128997
  const [isLoading, setIsLoading] = import_react70.useState(false);
128560
128998
  const [lastValidQuery, setLastValidQuery] = import_react70.useState("");
128561
128999
  const debounceTimeout = import_react70.useRef(null);
129000
+ const searchGenRef = import_react70.useRef(0);
129001
+ const lastValidQueryRef = import_react70.useRef(lastValidQuery);
129002
+ lastValidQueryRef.current = lastValidQuery;
129003
+ const matchesRef = import_react70.useRef(matches);
129004
+ matchesRef.current = matches;
128562
129005
  const { selectedIndex } = useAutocompleteNavigation({
128563
129006
  matches,
128564
129007
  maxVisible: 10,
@@ -128591,8 +129034,8 @@ function FileAutocomplete({
128591
129034
  onActiveChange?.(false);
128592
129035
  return;
128593
129036
  }
128594
- if (query === lastValidQuery && lastValidQuery.length > 0) {
128595
- if (matches[0]?.path !== query) {
129037
+ if (query === lastValidQueryRef.current && lastValidQueryRef.current.length > 0) {
129038
+ if (matchesRef.current[0]?.path !== query) {
128596
129039
  setMatches([{ path: query, type: "file" }]);
128597
129040
  }
128598
129041
  setIsLoading(false);
@@ -128604,14 +129047,19 @@ function FileAutocomplete({
128604
129047
  onActiveChange?.(false);
128605
129048
  return;
128606
129049
  }
129050
+ const gen = ++searchGenRef.current;
128607
129051
  if (query.length === 0) {
128608
129052
  setIsLoading(true);
128609
129053
  onActiveChange?.(true);
128610
129054
  searchFiles("", false).then((results) => {
129055
+ if (searchGenRef.current !== gen)
129056
+ return;
128611
129057
  setMatches(results);
128612
129058
  setIsLoading(false);
128613
129059
  onActiveChange?.(results.length > 0);
128614
129060
  }).catch(() => {
129061
+ if (searchGenRef.current !== gen)
129062
+ return;
128615
129063
  setMatches([]);
128616
129064
  setIsLoading(false);
128617
129065
  onActiveChange?.(false);
@@ -128628,6 +129076,8 @@ function FileAutocomplete({
128628
129076
  onActiveChange?.(true);
128629
129077
  debounceTimeout.current = setTimeout(() => {
128630
129078
  searchFiles(query, true).then((results) => {
129079
+ if (searchGenRef.current !== gen)
129080
+ return;
128631
129081
  setMatches(results);
128632
129082
  setIsLoading(false);
128633
129083
  onActiveChange?.(results.length > 0);
@@ -128635,6 +129085,8 @@ function FileAutocomplete({
128635
129085
  setLastValidQuery(query);
128636
129086
  }
128637
129087
  }).catch(() => {
129088
+ if (searchGenRef.current !== gen)
129089
+ return;
128638
129090
  setMatches([]);
128639
129091
  setIsLoading(false);
128640
129092
  onActiveChange?.(false);
@@ -128645,13 +129097,7 @@ function FileAutocomplete({
128645
129097
  clearTimeout(debounceTimeout.current);
128646
129098
  }
128647
129099
  };
128648
- }, [
128649
- currentInput,
128650
- cursorPosition,
128651
- onActiveChange,
128652
- lastValidQuery,
128653
- matches[0]?.path
128654
- ]);
129100
+ }, [currentInput, cursorPosition, onActiveChange]);
128655
129101
  if (!currentInput.includes("@")) {
128656
129102
  return null;
128657
129103
  }
@@ -130719,7 +131165,7 @@ function cloneRepoToTemp(repo) {
130719
131165
  function createBranchName() {
130720
131166
  return `letta/install-github-app-${Date.now().toString(36)}`;
130721
131167
  }
130722
- function runGit6(args, cwd2) {
131168
+ function runGit3(args, cwd2) {
130723
131169
  return runCommand("git", args, cwd2);
130724
131170
  }
130725
131171
  function writeWorkflow(repoDir, workflowPath, content) {
@@ -130740,7 +131186,7 @@ function writeWorkflow(repoDir, workflowPath, content) {
130740
131186
  }
130741
131187
  function getDefaultBaseBranch(repoDir) {
130742
131188
  try {
130743
- const headRef = runGit6(["symbolic-ref", "refs/remotes/origin/HEAD"], repoDir);
131189
+ const headRef = runGit3(["symbolic-ref", "refs/remotes/origin/HEAD"], repoDir);
130744
131190
  return headRef.replace("refs/remotes/origin/", "").trim() || "main";
130745
131191
  } catch {
130746
131192
  return "main";
@@ -130816,7 +131262,7 @@ async function installGithubApp(options) {
130816
131262
  includeAgentId: resolvedAgentId != null
130817
131263
  });
130818
131264
  const branchName = createBranchName();
130819
- runGit6(["checkout", "-b", branchName], repoDir);
131265
+ runGit3(["checkout", "-b", branchName], repoDir);
130820
131266
  progress(onProgress, "Creating workflow files");
130821
131267
  const changed = writeWorkflow(repoDir, workflowPath, workflowContent);
130822
131268
  progress(onProgress, "Setting up LETTA_API_KEY secret");
@@ -130839,10 +131285,10 @@ async function installGithubApp(options) {
130839
131285
  agentUrl: resolvedAgentId ? buildChatUrl(resolvedAgentId) : null
130840
131286
  };
130841
131287
  }
130842
- runGit6(["add", workflowPath], repoDir);
130843
- runGit6(["commit", "-m", "Add Letta Code GitHub Workflow"], repoDir);
131288
+ runGit3(["add", workflowPath], repoDir);
131289
+ runGit3(["commit", "-m", "Add Letta Code GitHub Workflow"], repoDir);
130844
131290
  progress(onProgress, "Opening pull request page");
130845
- runGit6(["push", "-u", "origin", branchName], repoDir);
131291
+ runGit3(["push", "-u", "origin", branchName], repoDir);
130846
131292
  const pullRequest = createPullRequest(repo, branchName, workflowPath, repoDir);
130847
131293
  return {
130848
131294
  repo,
@@ -134966,14 +135412,14 @@ var exports_generate_memory_viewer = {};
134966
135412
  __export(exports_generate_memory_viewer, {
134967
135413
  generateAndOpenMemoryViewer: () => generateAndOpenMemoryViewer
134968
135414
  });
134969
- import { execFile as execFileCb5 } from "node:child_process";
135415
+ import { execFile as execFileCb3 } from "node:child_process";
134970
135416
  import { chmodSync as chmodSync3, existsSync as existsSync39, mkdirSync as mkdirSync29, writeFileSync as writeFileSync23 } from "node:fs";
134971
- import { homedir as homedir35 } from "node:os";
135417
+ import { homedir as homedir36 } from "node:os";
134972
135418
  import { join as join47 } from "node:path";
134973
- import { promisify as promisify15 } from "node:util";
135419
+ import { promisify as promisify13 } from "node:util";
134974
135420
  async function runGitSafe(cwd2, args) {
134975
135421
  try {
134976
- const { stdout } = await execFile15("git", args, {
135422
+ const { stdout } = await execFile13("git", args, {
134977
135423
  cwd: cwd2,
134978
135424
  maxBuffer: 10 * 1024 * 1024,
134979
135425
  timeout: 60000
@@ -135307,15 +135753,15 @@ async function generateAndOpenMemoryViewer(agentId, options) {
135307
135753
  }
135308
135754
  return { filePath, opened: !skipOpen };
135309
135755
  }
135310
- var execFile15, VIEWERS_DIR2, MAX_COMMITS = 500, RECENT_DIFF_COUNT = 50, PER_DIFF_CAP = 1e5, TOTAL_PAYLOAD_CAP = 5000000, RECORD_SEP = "\x1E", REFLECTION_PATTERN;
135756
+ var execFile13, VIEWERS_DIR2, MAX_COMMITS = 500, RECENT_DIFF_COUNT = 50, PER_DIFF_CAP = 1e5, TOTAL_PAYLOAD_CAP = 5000000, RECORD_SEP = "\x1E", REFLECTION_PATTERN;
135311
135757
  var init_generate_memory_viewer = __esm(() => {
135312
135758
  init_client2();
135313
135759
  init_memoryFilesystem();
135314
135760
  init_memoryGit();
135315
135761
  init_memoryScanner();
135316
135762
  init_memory_viewer_template();
135317
- execFile15 = promisify15(execFileCb5);
135318
- VIEWERS_DIR2 = join47(homedir35(), ".letta", "viewers");
135763
+ execFile13 = promisify13(execFileCb3);
135764
+ VIEWERS_DIR2 = join47(homedir36(), ".letta", "viewers");
135319
135765
  REFLECTION_PATTERN = /\(reflection\)|🔮|reflection:/i;
135320
135766
  });
135321
135767
 
@@ -138040,11 +138486,11 @@ var init_PersonalitySelector = __esm(async () => {
138040
138486
 
138041
138487
  // src/utils/aws-credentials.ts
138042
138488
  import { readFile as readFile16 } from "node:fs/promises";
138043
- import { homedir as homedir36 } from "node:os";
138489
+ import { homedir as homedir37 } from "node:os";
138044
138490
  import { join as join48 } from "node:path";
138045
138491
  async function parseAwsCredentials() {
138046
- const credentialsPath = join48(homedir36(), ".aws", "credentials");
138047
- const configPath = join48(homedir36(), ".aws", "config");
138492
+ const credentialsPath = join48(homedir37(), ".aws", "credentials");
138493
+ const configPath = join48(homedir37(), ".aws", "config");
138048
138494
  const profiles = new Map;
138049
138495
  try {
138050
138496
  const content = await readFile16(credentialsPath, "utf-8");
@@ -143990,7 +144436,7 @@ __export(exports_shellAliases, {
143990
144436
  clearAliasCache: () => clearAliasCache
143991
144437
  });
143992
144438
  import { existsSync as existsSync42, readFileSync as readFileSync25 } from "node:fs";
143993
- import { homedir as homedir37 } from "node:os";
144439
+ import { homedir as homedir38 } from "node:os";
143994
144440
  import { join as join50 } from "node:path";
143995
144441
  function parseAliasesFromFile(filePath) {
143996
144442
  const aliases = new Map;
@@ -144060,7 +144506,7 @@ function loadAliases(forceReload = false) {
144060
144506
  if (aliasCache && !forceReload) {
144061
144507
  return aliasCache;
144062
144508
  }
144063
- const home = homedir37();
144509
+ const home = homedir38();
144064
144510
  const allAliases = new Map;
144065
144511
  for (const file of ALIAS_FILES) {
144066
144512
  const filePath = join50(home, file);
@@ -144910,7 +145356,7 @@ __export(exports_App, {
144910
145356
  });
144911
145357
  import { randomUUID as randomUUID14 } from "node:crypto";
144912
145358
  import { existsSync as existsSync43, readFileSync as readFileSync26, renameSync as renameSync3, writeFileSync as writeFileSync24 } from "node:fs";
144913
- import { homedir as homedir38, tmpdir as tmpdir6 } from "node:os";
145359
+ import { homedir as homedir39, tmpdir as tmpdir6 } from "node:os";
144914
145360
  import { join as join51, relative as relative18 } from "node:path";
144915
145361
  function deriveReasoningEffort(modelSettings, llmConfig) {
144916
145362
  if (modelSettings && "provider_type" in modelSettings) {
@@ -153276,7 +153722,7 @@ ${guidance}`);
153276
153722
  }
153277
153723
  if (mode === "bypassPermissions") {
153278
153724
  const planFilePath = activePlanPath ?? fallbackPlanPath;
153279
- const plansDir = join51(homedir38(), ".letta", "plans");
153725
+ const plansDir = join51(homedir39(), ".letta", "plans");
153280
153726
  handlePlanKeepPlanning(`You must write your plan to a plan file before exiting plan mode.
153281
153727
  ` + (planFilePath ? `Plan file path: ${planFilePath}
153282
153728
  ` : "") + `Use a write tool to create your plan in ${plansDir}, then use ExitPlanMode to present the plan to the user.`);
@@ -153311,7 +153757,7 @@ ${guidance}`);
153311
153757
  if (!hasUsablePlan) {
153312
153758
  lastAutoHandledExitPlanToolCallIdRef.current = approval.toolCallId;
153313
153759
  const planFilePath = activePlanPath ?? fallbackPlanPath;
153314
- const plansDir = join51(homedir38(), ".letta", "plans");
153760
+ const plansDir = join51(homedir39(), ".letta", "plans");
153315
153761
  handlePlanKeepPlanning(`You must write your plan to a plan file before exiting plan mode.
153316
153762
  ` + (planFilePath ? `Plan file path: ${planFilePath}
153317
153763
  ` : "") + `Use a write tool to create your plan in ${plansDir}, then use ExitPlanMode to present the plan to the user.`);
@@ -154727,7 +155173,7 @@ import {
154727
155173
  readFileSync as readFileSync27,
154728
155174
  writeFileSync as writeFileSync25
154729
155175
  } from "node:fs";
154730
- import { homedir as homedir39, platform as platform7 } from "node:os";
155176
+ import { homedir as homedir40, platform as platform7 } from "node:os";
154731
155177
  import { dirname as dirname20, join as join52 } from "node:path";
154732
155178
  function detectTerminalType2() {
154733
155179
  if (process.env.CURSOR_TRACE_ID || process.env.CURSOR_CHANNEL) {
@@ -154760,7 +155206,7 @@ function getKeybindingsPath2(terminal) {
154760
155206
  }[terminal];
154761
155207
  const os8 = platform7();
154762
155208
  if (os8 === "darwin") {
154763
- return join52(homedir39(), "Library", "Application Support", appName, "User", "keybindings.json");
155209
+ return join52(homedir40(), "Library", "Application Support", appName, "User", "keybindings.json");
154764
155210
  }
154765
155211
  if (os8 === "win32") {
154766
155212
  const appData = process.env.APPDATA;
@@ -154769,7 +155215,7 @@ function getKeybindingsPath2(terminal) {
154769
155215
  return join52(appData, appName, "User", "keybindings.json");
154770
155216
  }
154771
155217
  if (os8 === "linux") {
154772
- return join52(homedir39(), ".config", appName, "User", "keybindings.json");
155218
+ return join52(homedir40(), ".config", appName, "User", "keybindings.json");
154773
155219
  }
154774
155220
  return null;
154775
155221
  }
@@ -154926,10 +155372,10 @@ function getWezTermConfigPath2() {
154926
155372
  if (existsSync44(xdgPath))
154927
155373
  return xdgPath;
154928
155374
  }
154929
- const configPath = join52(homedir39(), ".config", "wezterm", "wezterm.lua");
155375
+ const configPath = join52(homedir40(), ".config", "wezterm", "wezterm.lua");
154930
155376
  if (existsSync44(configPath))
154931
155377
  return configPath;
154932
- return join52(homedir39(), ".wezterm.lua");
155378
+ return join52(homedir40(), ".wezterm.lua");
154933
155379
  }
154934
155380
  function wezTermDeleteFixExists2(configPath) {
154935
155381
  if (!existsSync44(configPath))
@@ -155027,10 +155473,10 @@ __export(exports_settings2, {
155027
155473
  loadProjectSettings: () => loadProjectSettings2,
155028
155474
  getSetting: () => getSetting2
155029
155475
  });
155030
- import { homedir as homedir40 } from "node:os";
155476
+ import { homedir as homedir41 } from "node:os";
155031
155477
  import { join as join53 } from "node:path";
155032
155478
  function getSettingsPath2() {
155033
- return join53(homedir40(), ".letta", "settings.json");
155479
+ return join53(homedir41(), ".letta", "settings.json");
155034
155480
  }
155035
155481
  async function loadSettings2() {
155036
155482
  const settingsPath = getSettingsPath2();
@@ -155717,15 +156163,15 @@ __export(exports_memoryFilesystem2, {
155717
156163
  MEMORY_FS_AGENTS_DIR: () => MEMORY_FS_AGENTS_DIR2
155718
156164
  });
155719
156165
  import { existsSync as existsSync45, mkdirSync as mkdirSync31 } from "node:fs";
155720
- import { homedir as homedir41 } from "node:os";
156166
+ import { homedir as homedir42 } from "node:os";
155721
156167
  import { join as join54 } from "node:path";
155722
- function getMemoryFilesystemRoot2(agentId, homeDir = homedir41()) {
156168
+ function getMemoryFilesystemRoot2(agentId, homeDir = homedir42()) {
155723
156169
  return join54(homeDir, MEMORY_FS_ROOT2, MEMORY_FS_AGENTS_DIR2, agentId, MEMORY_FS_MEMORY_DIR2);
155724
156170
  }
155725
- function getMemorySystemDir2(agentId, homeDir = homedir41()) {
156171
+ function getMemorySystemDir2(agentId, homeDir = homedir42()) {
155726
156172
  return join54(getMemoryFilesystemRoot2(agentId, homeDir), MEMORY_SYSTEM_DIR2);
155727
156173
  }
155728
- function ensureMemoryFilesystemDirs2(agentId, homeDir = homedir41()) {
156174
+ function ensureMemoryFilesystemDirs2(agentId, homeDir = homedir42()) {
155729
156175
  const root = getMemoryFilesystemRoot2(agentId, homeDir);
155730
156176
  const systemDir = getMemorySystemDir2(agentId, homeDir);
155731
156177
  if (!existsSync45(root)) {
@@ -156533,7 +156979,10 @@ If you experience this issue multiple times, move ~/.letta to ~/.letta_backup, a
156533
156979
  timeout: Number(process.env.LETTA_REQUEST_TIMEOUT_MS) || 10 * 60 * 1000,
156534
156980
  defaultHeaders: {
156535
156981
  "X-Letta-Source": "letta-code",
156536
- "User-Agent": `letta-code/${package_default.version}`
156982
+ "User-Agent": `letta-code/${package_default.version}`,
156983
+ ...process.env.LETTA_NODE === "1" && {
156984
+ "x-letta-node": "1"
156985
+ }
156537
156986
  },
156538
156987
  ...isTimingsEnabled() && { fetch: createTimingFetch(fetch) }
156539
156988
  });
@@ -156949,6 +157398,18 @@ var CLI_FLAG_CATALOG = {
156949
157398
  allowedTools: { parser: { type: "string" }, mode: "both" },
156950
157399
  disallowedTools: { parser: { type: "string" }, mode: "both" },
156951
157400
  "permission-mode": { parser: { type: "string" }, mode: "both" },
157401
+ "memory-scope": {
157402
+ parser: { type: "string" },
157403
+ mode: "both",
157404
+ help: {
157405
+ argLabel: "<ids>",
157406
+ description: "Comma-separated agent IDs this session may access in addition to self.",
157407
+ continuationLines: [
157408
+ "Example: --memory-scope agent-abc,agent-def",
157409
+ "Required to read or write another agent's memory directory."
157410
+ ]
157411
+ }
157412
+ },
156952
157413
  yolo: { parser: { type: "boolean" }, mode: "both" },
156953
157414
  "output-format": {
156954
157415
  parser: { type: "string" },
@@ -160391,9 +160852,9 @@ import {
160391
160852
  readdirSync as readdirSync7,
160392
160853
  unlinkSync as unlinkSync6
160393
160854
  } from "node:fs";
160394
- import { homedir as homedir20 } from "node:os";
160855
+ import { homedir as homedir21 } from "node:os";
160395
160856
  import { join as join26 } from "node:path";
160396
- var REMOTE_LOG_DIR = join26(homedir20(), ".letta", "logs", "remote");
160857
+ var REMOTE_LOG_DIR = join26(homedir21(), ".letta", "logs", "remote");
160397
160858
  var MAX_LOG_FILES = 10;
160398
160859
  function formatTimestamp2() {
160399
160860
  const now = new Date;
@@ -160956,7 +161417,7 @@ async function runListenSubcommand(argv) {
160956
161417
  init_memoryGit();
160957
161418
  import { cpSync, existsSync as existsSync27, mkdirSync as mkdirSync20, rmSync as rmSync3, statSync as statSync8 } from "node:fs";
160958
161419
  import { readdir as readdir7 } from "node:fs/promises";
160959
- import { homedir as homedir24 } from "node:os";
161420
+ import { homedir as homedir25 } from "node:os";
160960
161421
  import { join as join31 } from "node:path";
160961
161422
  import { parseArgs as parseArgs8 } from "node:util";
160962
161423
  function printUsage5() {
@@ -161002,10 +161463,10 @@ function parseMemfsArgs(argv) {
161002
161463
  });
161003
161464
  }
161004
161465
  function getMemoryRoot(agentId) {
161005
- return join31(homedir24(), ".letta", "agents", agentId, "memory");
161466
+ return join31(homedir25(), ".letta", "agents", agentId, "memory");
161006
161467
  }
161007
161468
  function getAgentRoot(agentId) {
161008
- return join31(homedir24(), ".letta", "agents", agentId);
161469
+ return join31(homedir25(), ".letta", "agents", agentId);
161009
161470
  }
161010
161471
  function formatBackupTimestamp(date = new Date) {
161011
161472
  const pad = (value) => String(value).padStart(2, "0");
@@ -161083,11 +161544,11 @@ async function runMemfsSubcommand(argv) {
161083
161544
  console.error("Not a git repo. Enable git-backed memory first.");
161084
161545
  return 1;
161085
161546
  }
161086
- const { execFile: execFileCb5 } = await import("node:child_process");
161087
- const { promisify: promisify14 } = await import("node:util");
161088
- const execFile14 = promisify14(execFileCb5);
161547
+ const { execFile: execFileCb3 } = await import("node:child_process");
161548
+ const { promisify: promisify12 } = await import("node:util");
161549
+ const execFile12 = promisify12(execFileCb3);
161089
161550
  const dir = getMemoryRepoDir(agentId);
161090
- const { stdout } = await execFile14("git", ["diff"], { cwd: dir });
161551
+ const { stdout } = await execFile12("git", ["diff"], { cwd: dir });
161091
161552
  if (stdout.trim()) {
161092
161553
  console.log(stdout);
161093
161554
  return 2;
@@ -161558,10 +162019,11 @@ async function runSubcommand(argv) {
161558
162019
  }
161559
162020
 
161560
162021
  // src/permissions/mode.ts
162022
+ init_crossAgentGuard();
161561
162023
  init_memoryScope();
161562
162024
  init_readOnlyShell();
161563
162025
  init_shell_command_normalization();
161564
- import { homedir as homedir25 } from "node:os";
162026
+ import { homedir as homedir26 } from "node:os";
161565
162027
  import { isAbsolute as isAbsolute18, join as join32, relative as relative13 } from "node:path";
161566
162028
  var MODE_KEY2 = Symbol.for("@letta/permissionMode");
161567
162029
  var PLAN_FILE_KEY2 = Symbol.for("@letta/planFilePath");
@@ -161608,22 +162070,6 @@ function isPathInPlansDir2(path24, plansDir) {
161608
162070
  const rel = relative13(plansDir, path24);
161609
162071
  return rel !== "" && !rel.startsWith("..") && !isAbsolute18(rel);
161610
162072
  }
161611
- function extractApplyPatchPaths2(input) {
161612
- const paths = [];
161613
- const fileDirectivePattern = /\*\*\* (?:Add|Update|Delete) File:\s*(.+)/g;
161614
- const moveDirectivePattern = /\*\*\* Move to:\s*(.+)/g;
161615
- for (const match3 of input.matchAll(fileDirectivePattern)) {
161616
- const matchPath = match3[1]?.trim();
161617
- if (matchPath)
161618
- paths.push(matchPath);
161619
- }
161620
- for (const match3 of input.matchAll(moveDirectivePattern)) {
161621
- const matchPath = match3[1]?.trim();
161622
- if (matchPath)
161623
- paths.push(matchPath);
161624
- }
161625
- return paths;
161626
- }
161627
162073
  function stripMatchingQuotes2(value) {
161628
162074
  const trimmed = value.trim();
161629
162075
  if (trimmed.length < 2) {
@@ -161794,12 +162240,12 @@ class PermissionModeManager2 {
161794
162240
  return "allow";
161795
162241
  }
161796
162242
  if (writeTools.includes(toolName)) {
161797
- const plansDir = join32(homedir25(), ".letta", "plans");
162243
+ const plansDir = join32(homedir26(), ".letta", "plans");
161798
162244
  const targetPath = toolArgs?.file_path || toolArgs?.path;
161799
162245
  let candidatePaths = [];
161800
162246
  if ((toolName === "ApplyPatch" || toolName === "apply_patch" || toolName === "memory_apply_patch") && toolArgs?.input) {
161801
162247
  const input = toolArgs.input;
161802
- candidatePaths = extractApplyPatchPaths2(input);
162248
+ candidatePaths = extractApplyPatchPaths(input);
161803
162249
  } else if (typeof targetPath === "string") {
161804
162250
  candidatePaths = [targetPath];
161805
162251
  }
@@ -161845,7 +162291,7 @@ class PermissionModeManager2 {
161845
162291
  }
161846
162292
  const planWritePath = extractPlanFileWritePathFromShellCommand2(command);
161847
162293
  if (planWritePath) {
161848
- const plansDir = join32(homedir25(), ".letta", "plans");
162294
+ const plansDir = join32(homedir26(), ".letta", "plans");
161849
162295
  const resolvedPath = resolvePlanTargetPath2(planWritePath, workingDirectory);
161850
162296
  if (resolvedPath && isPathInPlansDir2(resolvedPath, plansDir)) {
161851
162297
  return "allow";
@@ -161919,7 +162365,7 @@ class PermissionModeManager2 {
161919
162365
  const targetPath = toolArgs?.file_path || toolArgs?.path;
161920
162366
  let candidatePaths = [];
161921
162367
  if ((toolName === "ApplyPatch" || toolName === "apply_patch") && toolArgs?.input) {
161922
- candidatePaths = extractApplyPatchPaths2(toolArgs.input);
162368
+ candidatePaths = extractApplyPatchPaths(toolArgs.input);
161923
162369
  } else if (typeof targetPath === "string") {
161924
162370
  candidatePaths = [targetPath];
161925
162371
  }
@@ -161962,7 +162408,7 @@ init_debug();
161962
162408
  init_fs();
161963
162409
  init_secrets();
161964
162410
  import { randomUUID as randomUUID9 } from "node:crypto";
161965
- import { homedir as homedir26 } from "node:os";
162411
+ import { homedir as homedir27 } from "node:os";
161966
162412
  import { join as join33, resolve as resolve26 } from "node:path";
161967
162413
  var DEFAULT_SETTINGS3 = {
161968
162414
  lastAgent: null,
@@ -162349,7 +162795,7 @@ class SettingsManager2 {
162349
162795
  if (!this.settings)
162350
162796
  return;
162351
162797
  const settingsPath = this.getSettingsPath();
162352
- const home = process.env.HOME || homedir26();
162798
+ const home = process.env.HOME || homedir27();
162353
162799
  const dirPath = join33(home, ".letta");
162354
162800
  try {
162355
162801
  if (!exists(dirPath)) {
@@ -162410,7 +162856,7 @@ class SettingsManager2 {
162410
162856
  }
162411
162857
  }
162412
162858
  getSettingsPath() {
162413
- const home = process.env.HOME || homedir26();
162859
+ const home = process.env.HOME || homedir27();
162414
162860
  return join33(home, ".letta", "settings.json");
162415
162861
  }
162416
162862
  getProjectSettingsPath(workingDirectory) {
@@ -164253,7 +164699,7 @@ Error: ${message}`);
164253
164699
  const { toolFilter: toolFilter3 } = await Promise.resolve().then(() => (init_filter2(), exports_filter2));
164254
164700
  toolFilter3.setEnabledTools(values.tools);
164255
164701
  }
164256
- if (values.allowedTools || values.disallowedTools) {
164702
+ if (values.allowedTools || values.disallowedTools || values["memory-scope"]) {
164257
164703
  const { cliPermissions: cliPermissions3 } = await Promise.resolve().then(() => (init_cli2(), exports_cli2));
164258
164704
  if (values.allowedTools) {
164259
164705
  cliPermissions3.setAllowedTools(values.allowedTools);
@@ -164261,6 +164707,9 @@ Error: ${message}`);
164261
164707
  if (values.disallowedTools) {
164262
164708
  cliPermissions3.setDisallowedTools(values.disallowedTools);
164263
164709
  }
164710
+ if (values["memory-scope"]) {
164711
+ cliPermissions3.setMemoryScope(values["memory-scope"]);
164712
+ }
164264
164713
  }
164265
164714
  const permissionModeValue = values["permission-mode"];
164266
164715
  const yoloMode = values.yolo;
@@ -165038,4 +165487,4 @@ Error during initialization: ${message}`);
165038
165487
  }
165039
165488
  main();
165040
165489
 
165041
- //# debugId=D837155EA6C28D1764756E2164756E21
165490
+ //# debugId=1D3F2BB144427E1064756E2164756E21