@letta-ai/letta-code 0.24.4 → 0.24.6

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.24.4",
3272
+ version: "0.24.6",
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: {
@@ -4723,10 +4723,12 @@ var exports_client = {};
4723
4723
  __export(exports_client, {
4724
4724
  getServerUrl: () => getServerUrl,
4725
4725
  getMemfsServerUrl: () => getMemfsServerUrl,
4726
+ getMemfsGitProxyRewriteConfig: () => getMemfsGitProxyRewriteConfig,
4726
4727
  getClient: () => getClient,
4727
4728
  consumeLastSDKDiagnostic: () => consumeLastSDKDiagnostic,
4728
4729
  clearLastSDKDiagnostic: () => clearLastSDKDiagnostic,
4729
- __testOverrideGetClient: () => __testOverrideGetClient
4730
+ __testOverrideGetClient: () => __testOverrideGetClient,
4731
+ LETTA_MEMFS_GIT_PROXY_BASE_URL_ENV: () => LETTA_MEMFS_GIT_PROXY_BASE_URL_ENV
4730
4732
  });
4731
4733
  import { hostname } from "node:os";
4732
4734
  function __testOverrideGetClient(factory) {
@@ -4774,12 +4776,50 @@ function getServerUrl() {
4774
4776
  const settings = settingsManager.getSettings();
4775
4777
  return process.env.LETTA_BASE_URL || settings.env?.LETTA_BASE_URL || LETTA_CLOUD_API_URL;
4776
4778
  }
4779
+ function isLocalhostUrl(value) {
4780
+ if (!value)
4781
+ return false;
4782
+ try {
4783
+ const parsed = new URL(value);
4784
+ return ["localhost", "127.0.0.1", "::1", "[::1]"].includes(parsed.hostname);
4785
+ } catch {
4786
+ return false;
4787
+ }
4788
+ }
4789
+ function trimBaseUrl(value) {
4790
+ return value.trim().replace(/\/+$/, "");
4791
+ }
4792
+ function getMemfsGitProxyRewriteConfig(env = process.env) {
4793
+ const rawProxyBaseUrl = env[LETTA_MEMFS_GIT_PROXY_BASE_URL_ENV]?.trim();
4794
+ if (!rawProxyBaseUrl || !isLocalhostUrl(rawProxyBaseUrl)) {
4795
+ return null;
4796
+ }
4797
+ const memfsBaseUrl = trimBaseUrl(getMemfsServerUrl());
4798
+ if (!memfsBaseUrl.includes("api.letta.com")) {
4799
+ return null;
4800
+ }
4801
+ const proxyBaseUrl = trimBaseUrl(rawProxyBaseUrl);
4802
+ const proxyPrefix = `${proxyBaseUrl}/v1/git/`;
4803
+ const memfsPrefix = `${memfsBaseUrl}/v1/git/`;
4804
+ return {
4805
+ proxyBaseUrl,
4806
+ memfsBaseUrl,
4807
+ proxyPrefix,
4808
+ memfsPrefix,
4809
+ configKey: `url.${proxyPrefix}.insteadOf`,
4810
+ configValue: memfsPrefix
4811
+ };
4812
+ }
4777
4813
  function getMemfsServerUrl() {
4778
4814
  let settings = null;
4779
4815
  try {
4780
4816
  settings = settingsManager.getSettings();
4781
4817
  } catch {}
4782
- return process.env.LETTA_MEMFS_BASE_URL || settings?.env?.LETTA_MEMFS_BASE_URL || LETTA_CLOUD_API_URL;
4818
+ const configuredMemfsUrl = process.env.LETTA_MEMFS_BASE_URL || settings?.env?.LETTA_MEMFS_BASE_URL;
4819
+ if (configuredMemfsUrl) {
4820
+ return configuredMemfsUrl;
4821
+ }
4822
+ return LETTA_CLOUD_API_URL;
4783
4823
  }
4784
4824
  async function getClient() {
4785
4825
  if (_testClientOverride) {
@@ -4854,7 +4894,7 @@ If you experience this issue multiple times, move ~/.letta to ~/.letta_backup, a
4854
4894
  ...isTimingsEnabled() && { fetch: createTimingFetch(fetch) }
4855
4895
  });
4856
4896
  }
4857
- var SDK_DIAGNOSTIC_MAX_LEN = 400, SDK_DIAGNOSTIC_MAX_LINES = 4, lastSDKDiagnostic = null, _cachedApiKey, _testClientOverride = null, sdkLogger;
4897
+ var SDK_DIAGNOSTIC_MAX_LEN = 400, SDK_DIAGNOSTIC_MAX_LINES = 4, lastSDKDiagnostic = null, _cachedApiKey, _testClientOverride = null, sdkLogger, LETTA_MEMFS_GIT_PROXY_BASE_URL_ENV = "LETTA_MEMFS_GIT_PROXY_BASE_URL";
4858
4898
  var init_client2 = __esm(() => {
4859
4899
  init_letta_client();
4860
4900
  init_package();
@@ -7128,6 +7168,13 @@ If worktree creation fails (locked index), retry up to 3 times with backoff (sle
7128
7168
  ### 2. Read existing memory
7129
7169
  Read the memory files in your worktree, to understand what already exists in the memory filesystem.
7130
7170
 
7171
+ Before adding or expanding \`system/\` memory, measure its current token footprint:
7172
+ \`\`\`bash
7173
+ letta memory tokens --format json --quiet --memory-dir "$WORKTREE_DIR/$BRANCH_NAME"
7174
+ \`\`\`
7175
+
7176
+ This command is memory-mode safe. Treat it as measurement only: use the reported \`total_tokens\` and per-file breakdown to decide whether new findings belong in \`system/\` or external memory. Do not use custom token-counting scripts, \`npx\`, \`awk\`, or \`find -exec wc\` for this.
7177
+
7131
7178
  ### 3. Read and analyze history
7132
7179
 
7133
7180
  Your prompt will specify a pre-split JSONL chunk file and its source format. Use these patterns to read it:
@@ -7765,6 +7812,11 @@ You can create, delete, or modify files — including their contents, names, and
7765
7812
  - Why did the agent make the mistakes it did? What was missing from context?
7766
7813
  - Why did the user have to make corrections?
7767
7814
  - Does anything in memory contradict the observed conversation history, or need updating?
7815
+ - Check the current size of \`system/\` before adding or expanding in-context memory:
7816
+ \`\`\`bash
7817
+ letta memory tokens --format json --quiet
7818
+ \`\`\`
7819
+ This command is memory-mode safe. Treat it as measurement only: decide whether a size is concerning based on the actual context and the value of the proposed change. Prefer adding durable detail to external memory unless the information is important enough to stay in-context.
7768
7820
 
7769
7821
  ### Step 4: Update memory files (if needed)
7770
7822
 
@@ -9669,6 +9721,17 @@ var init_models2 = __esm(() => {
9669
9721
  parallel_tool_calls: true
9670
9722
  }
9671
9723
  },
9724
+ {
9725
+ id: "deepseek-v4-pro",
9726
+ handle: "openrouter/deepseek/deepseek-v4-pro",
9727
+ label: "DeepSeek V4 Pro",
9728
+ description: "DeepSeek's V4 Pro model",
9729
+ updateArgs: {
9730
+ context_window: 1048576,
9731
+ max_output_tokens: 384000,
9732
+ parallel_tool_calls: true
9733
+ }
9734
+ },
9672
9735
  {
9673
9736
  id: "glm-5.1",
9674
9737
  handle: "zai/glm-5.1",
@@ -38492,6 +38555,7 @@ var init_create = __esm(() => {
38492
38555
  var exports_memoryGit = {};
38493
38556
  __export(exports_memoryGit, {
38494
38557
  unsetMemoryRepositoryUrl: () => unsetMemoryRepositoryUrl,
38558
+ shouldConfigurePersistentMemfsCredentialHelper: () => shouldConfigurePersistentMemfsCredentialHelper,
38495
38559
  setMemoryRepositoryUrl: () => setMemoryRepositoryUrl,
38496
38560
  removeGitMemoryTag: () => removeGitMemoryTag,
38497
38561
  readMemoryRepositoryPushLog: () => readMemoryRepositoryPushLog,
@@ -38503,6 +38567,7 @@ __export(exports_memoryGit, {
38503
38567
  isRetryableGitTransientError: () => isRetryableGitTransientError,
38504
38568
  isMissingCwdGitError: () => isMissingCwdGitError,
38505
38569
  isMemfsRemoteUrlForAgent: () => isMemfsRemoteUrlForAgent,
38570
+ isMemfsGitNetworkCommand: () => isMemfsGitNetworkCommand,
38506
38571
  isGitRepo: () => isGitRepo,
38507
38572
  getMemoryRepositoryUrl: () => getMemoryRepositoryUrl,
38508
38573
  getMemoryRepoDir: () => getMemoryRepoDir,
@@ -38513,6 +38578,9 @@ __export(exports_memoryGit, {
38513
38578
  ensureLocalMemfsGitConfig: () => ensureLocalMemfsGitConfig,
38514
38579
  commitAndSyncMemoryWrite: () => commitAndSyncMemoryWrite,
38515
38580
  cloneMemoryRepo: () => cloneMemoryRepo,
38581
+ buildNonInteractiveGitEnv: () => buildNonInteractiveGitEnv,
38582
+ buildMemfsGitProxyArgs: () => buildMemfsGitProxyArgs,
38583
+ buildGitAuthArgs: () => buildGitAuthArgs,
38516
38584
  assertMemoryRepoReadyForWrite: () => assertMemoryRepoReadyForWrite,
38517
38585
  addGitMemoryTag: () => addGitMemoryTag,
38518
38586
  PRE_COMMIT_HOOK_SCRIPT: () => PRE_COMMIT_HOOK_SCRIPT,
@@ -38622,12 +38690,44 @@ async function getAuthToken() {
38622
38690
  const client = await getClient();
38623
38691
  return client._options?.apiKey ?? "";
38624
38692
  }
38625
- async function runGit(cwd2, args, token) {
38626
- const authArgs = token ? [
38693
+ function buildGitAuthArgs(token) {
38694
+ return [
38695
+ "-c",
38696
+ "credential.helper=",
38697
+ "-c",
38698
+ "core.askPass=",
38627
38699
  "-c",
38628
38700
  `http.extraHeader=Authorization: Basic ${Buffer.from(`letta:${token}`).toString("base64")}`
38629
- ] : [];
38630
- const allArgs = [...authArgs, ...args];
38701
+ ];
38702
+ }
38703
+ function isMemfsGitNetworkCommand(args) {
38704
+ return ["clone", "fetch", "pull", "push"].includes(args[0] ?? "");
38705
+ }
38706
+ function buildMemfsGitProxyArgs(args, env3 = process.env) {
38707
+ if (!isMemfsGitNetworkCommand(args)) {
38708
+ return [];
38709
+ }
38710
+ const rewrite = getMemfsGitProxyRewriteConfig(env3);
38711
+ if (!rewrite) {
38712
+ return [];
38713
+ }
38714
+ return ["-c", `${rewrite.configKey}=${rewrite.configValue}`];
38715
+ }
38716
+ function shouldConfigurePersistentMemfsCredentialHelper(env3 = process.env) {
38717
+ return getMemfsGitProxyRewriteConfig(env3) === null;
38718
+ }
38719
+ function buildNonInteractiveGitEnv(env3 = process.env) {
38720
+ return {
38721
+ ...env3,
38722
+ GIT_TERMINAL_PROMPT: "0",
38723
+ GCM_INTERACTIVE: "never",
38724
+ GIT_ASKPASS: "",
38725
+ SSH_ASKPASS: ""
38726
+ };
38727
+ }
38728
+ async function runGit(cwd2, args, token) {
38729
+ const authArgs = token ? buildGitAuthArgs(token) : [];
38730
+ const allArgs = [...buildMemfsGitProxyArgs(args), ...authArgs, ...args];
38631
38731
  let loggableArgs = args;
38632
38732
  if (args[0] === "config" && typeof args[1] === "string" && args[1].includes("credential") && args[1].includes(".helper")) {
38633
38733
  loggableArgs = [args[0], args[1], "<redacted>"];
@@ -38637,6 +38737,7 @@ async function runGit(cwd2, args, token) {
38637
38737
  debugLog("memfs-git", `git ${loggableArgs.join(" ")} (in ${cwd2})`);
38638
38738
  const result = await execFile("git", allArgs, {
38639
38739
  cwd: cwd2,
38740
+ env: buildNonInteractiveGitEnv(),
38640
38741
  maxBuffer: 10 * 1024 * 1024,
38641
38742
  timeout: 60000
38642
38743
  });
@@ -38690,6 +38791,11 @@ async function runGitWithRetry(cwd2, args, token, options) {
38690
38791
  async function configureLocalCredentialHelper(dir, token) {
38691
38792
  const rawBaseUrl = getMemfsServerUrl();
38692
38793
  const normalizedBaseUrl = normalizeCredentialBaseUrl(rawBaseUrl);
38794
+ if (!shouldConfigurePersistentMemfsCredentialHelper()) {
38795
+ await clearLocalCredentialHelper(dir, rawBaseUrl, normalizedBaseUrl);
38796
+ debugLog("memfs-git", `Skipped persistent credential helper for ${normalizedBaseUrl}; using transient MemFS git proxy transport`);
38797
+ return;
38798
+ }
38693
38799
  let helper;
38694
38800
  if (platform2() === "win32") {
38695
38801
  const helperScriptPath = join7(dir, ".git", "letta-credential-helper.cmd");
@@ -38713,6 +38819,17 @@ echo password=${token}
38713
38819
  }
38714
38820
  debugLog("memfs-git", `Configured local credential helper for ${normalizedBaseUrl}${rawBaseUrl !== normalizedBaseUrl ? ` (and raw ${rawBaseUrl})` : ""}`);
38715
38821
  }
38822
+ async function clearLocalCredentialHelper(dir, rawBaseUrl, normalizedBaseUrl) {
38823
+ const keys = new Set([
38824
+ `credential.${normalizedBaseUrl}.helper`,
38825
+ `credential.${rawBaseUrl}.helper`
38826
+ ]);
38827
+ for (const key of keys) {
38828
+ try {
38829
+ await runGit(dir, ["config", "--local", "--unset-all", key]);
38830
+ } catch {}
38831
+ }
38832
+ }
38716
38833
  function installPreCommitHook(dir) {
38717
38834
  const hooksDir = join7(dir, ".git", "hooks");
38718
38835
  const hookPath = join7(hooksDir, "pre-commit");
@@ -38963,6 +39080,32 @@ async function fetchMemoryRemote(memoryDir, token) {
38963
39080
  operation: "fetch origin"
38964
39081
  });
38965
39082
  }
39083
+ async function getMemoryAheadBehind(memoryDir) {
39084
+ try {
39085
+ const { stdout } = await runGit(memoryDir, [
39086
+ "rev-list",
39087
+ "--left-right",
39088
+ "--count",
39089
+ "HEAD...@{u}"
39090
+ ]);
39091
+ const [aheadRaw, behindRaw] = stdout.trim().split(/\s+/);
39092
+ return {
39093
+ ahead: Number.parseInt(aheadRaw ?? "0", 10) || 0,
39094
+ behind: Number.parseInt(behindRaw ?? "0", 10) || 0
39095
+ };
39096
+ } catch {
39097
+ return null;
39098
+ }
39099
+ }
39100
+ async function pushCleanPendingMemoryCommitsForWrite(memoryDir, agentId, token) {
39101
+ await prepareMemoryRepoForGitOps(memoryDir, agentId, token);
39102
+ const divergence = await getMemoryAheadBehind(memoryDir);
39103
+ if (divergence && divergence.ahead > 0) {
39104
+ await runGitWithRetry(memoryDir, ["push"], token, {
39105
+ operation: "push pending memory commits"
39106
+ });
39107
+ }
39108
+ }
38966
39109
  async function resetMemoryToUpstream(memoryDir, token) {
38967
39110
  await runGit(memoryDir, ["reset", "--hard", "@{u}"], token);
38968
39111
  }
@@ -39028,19 +39171,18 @@ async function recoverMemoryPushConflict(params, token, initialSha) {
39028
39171
  rescueRef
39029
39172
  };
39030
39173
  }
39031
- async function assertMemoryRepoReadyForWrite(memoryDir) {
39174
+ async function assertMemoryRepoReadyForWrite(memoryDir, agentId) {
39032
39175
  const status = await runGit(memoryDir, ["status", "--porcelain"]);
39033
39176
  if (status.stdout.trim().length > 0) {
39034
39177
  throw new Error("Memory repo has uncommitted changes. Commit, discard, or sync them before using memory tools.");
39035
39178
  }
39179
+ if (agentId) {
39180
+ const token = await getAuthToken();
39181
+ await pushCleanPendingMemoryCommitsForWrite(memoryDir, agentId, token);
39182
+ }
39036
39183
  try {
39037
- const { stdout } = await runGit(memoryDir, [
39038
- "rev-list",
39039
- "--count",
39040
- "@{u}..HEAD"
39041
- ]);
39042
- const aheadCount = parseInt(stdout.trim(), 10);
39043
- if (aheadCount > 0) {
39184
+ const divergence = await getMemoryAheadBehind(memoryDir);
39185
+ if (divergence && divergence.ahead > 0) {
39044
39186
  throw new Error("Memory repo has local commits that are not pushed to remote. Sync the repo before using memory tools.");
39045
39187
  }
39046
39188
  } catch (error) {
@@ -47319,13 +47461,25 @@ function pushUnique(list, seen, entry) {
47319
47461
  seen.add(key);
47320
47462
  list.push(entry);
47321
47463
  }
47464
+ function normalizePowerShellCommand(command) {
47465
+ const trimmed = command.trim();
47466
+ if (trimmed.startsWith("&") || trimmed.startsWith('"') || trimmed.startsWith("'")) {
47467
+ return trimmed.startsWith("&") ? trimmed : `& ${trimmed}`;
47468
+ }
47469
+ return trimmed;
47470
+ }
47471
+ function buildPowerShellCommand(command) {
47472
+ const powerShellCommand = normalizePowerShellCommand(command);
47473
+ const aliasPrelude = POWERSHELL_ENV_ALIASES.map((name) => `$${name} = $env:${name}`).join("; ");
47474
+ return `${aliasPrelude}; ${powerShellCommand}`;
47475
+ }
47322
47476
  function windowsLaunchers(command) {
47323
47477
  const trimmed = command.trim();
47324
47478
  if (!trimmed)
47325
47479
  return [];
47326
47480
  const launchers = [];
47327
47481
  const seen = new Set;
47328
- const powerShellCommand = trimmed.startsWith("&") || trimmed.startsWith('"') || trimmed.startsWith("'") ? trimmed.startsWith("&") ? trimmed : `& ${trimmed}` : trimmed;
47482
+ const powerShellCommand = buildPowerShellCommand(trimmed);
47329
47483
  pushUnique(launchers, seen, [
47330
47484
  "powershell.exe",
47331
47485
  "-NoProfile",
@@ -47407,7 +47561,19 @@ function buildShellLaunchers(command, options) {
47407
47561
  const login = options?.login ?? false;
47408
47562
  return process.platform === "win32" ? windowsLaunchers(command) : unixLaunchers(command, login);
47409
47563
  }
47410
- var SEP2 = "\x00";
47564
+ var SEP2 = "\x00", POWERSHELL_ENV_ALIASES;
47565
+ var init_shellLaunchers = __esm(() => {
47566
+ POWERSHELL_ENV_ALIASES = [
47567
+ "MEMORY_DIR",
47568
+ "LETTA_MEMORY_DIR",
47569
+ "AGENT_ID",
47570
+ "LETTA_AGENT_ID",
47571
+ "LETTA_PARENT_AGENT_ID",
47572
+ "CONVERSATION_ID",
47573
+ "LETTA_CONVERSATION_ID",
47574
+ "USER_CWD"
47575
+ ];
47576
+ });
47411
47577
 
47412
47578
  // src/hooks/types.ts
47413
47579
  function supportsPromptHooks(event) {
@@ -47863,6 +48029,7 @@ async function executeHooksParallel(hooks, input, workingDirectory = process.cwd
47863
48029
  }
47864
48030
  var DEFAULT_TIMEOUT_MS = 60000;
47865
48031
  var init_executor = __esm(() => {
48032
+ init_shellLaunchers();
47866
48033
  init_prompt_executor();
47867
48034
  init_types2();
47868
48035
  });
@@ -48895,6 +49062,7 @@ __export(exports_memoryFilesystem, {
48895
49062
  renderMemoryFilesystemTree: () => renderMemoryFilesystemTree,
48896
49063
  labelFromRelativePath: () => labelFromRelativePath,
48897
49064
  isMemfsEnabledOnServer: () => isMemfsEnabledOnServer,
49065
+ isLettaMemfsServer: () => isLettaMemfsServer,
48898
49066
  isLettaCloud: () => isLettaCloud,
48899
49067
  getMemorySystemDir: () => getMemorySystemDir,
48900
49068
  getMemoryFilesystemRoot: () => getMemoryFilesystemRoot,
@@ -49082,8 +49250,8 @@ function renderMemoryFilesystemTree(systemLabels, detachedLabels, options = {})
49082
49250
  }
49083
49251
  async function applyMemfsFlags(agentId, memfsFlag, noMemfsFlag, options) {
49084
49252
  const { settingsManager: settingsManager2 } = await Promise.resolve().then(() => (init_settings_manager(), exports_settings_manager));
49085
- if (memfsFlag && !await isLettaCloud()) {
49086
- throw new Error("--memfs is only available on Letta Cloud (api.letta.com).");
49253
+ if (memfsFlag && !await isLettaMemfsServer()) {
49254
+ throw new Error(await getMemfsSyncUnavailableMessage());
49087
49255
  }
49088
49256
  const hasExplicitToggle = Boolean(memfsFlag || noMemfsFlag);
49089
49257
  const localMemfsEnabled = settingsManager2.isMemfsEnabled(agentId);
@@ -49154,6 +49322,24 @@ async function isLettaCloud() {
49154
49322
  const serverUrl = getServerUrl2();
49155
49323
  return serverUrl.includes("api.letta.com") || process.env.LETTA_MEMFS_LOCAL === "1" || process.env.LETTA_API_KEY === "local-desktop";
49156
49324
  }
49325
+ function getServerHostLabel(serverUrl) {
49326
+ const trimmed = serverUrl.trim();
49327
+ try {
49328
+ return new URL(trimmed).host || trimmed;
49329
+ } catch {
49330
+ return trimmed.replace(/^https?:\/\//, "").replace(/\/+$/, "") || trimmed;
49331
+ }
49332
+ }
49333
+ async function isLettaMemfsServer() {
49334
+ const { getMemfsServerUrl: getMemfsServerUrl2 } = await Promise.resolve().then(() => (init_client2(), exports_client));
49335
+ const memfsServerUrl = getMemfsServerUrl2();
49336
+ return memfsServerUrl.includes("api.letta.com") || process.env.LETTA_MEMFS_LOCAL === "1" || process.env.LETTA_API_KEY === "local-desktop";
49337
+ }
49338
+ async function getMemfsSyncUnavailableMessage() {
49339
+ const { getMemfsServerUrl: getMemfsServerUrl2 } = await Promise.resolve().then(() => (init_client2(), exports_client));
49340
+ const memfsServerUrl = getMemfsServerUrl2();
49341
+ return `MemFS sync failed (expected api.letta.com, got ${getServerHostLabel(memfsServerUrl)})`;
49342
+ }
49157
49343
  async function enableMemfsIfCloud(agentId) {
49158
49344
  if (!await isLettaCloud())
49159
49345
  return;
@@ -54547,6 +54733,24 @@ exec ${commandWithArgs} "$@"
54547
54733
  });
54548
54734
  return shimDir;
54549
54735
  }
54736
+ function appendGitConfigEnv(env3, key, value) {
54737
+ const rawCount = env3.GIT_CONFIG_COUNT;
54738
+ const count = rawCount && /^\d+$/.test(rawCount) ? Number(rawCount) : 0;
54739
+ env3[`GIT_CONFIG_KEY_${count}`] = key;
54740
+ env3[`GIT_CONFIG_VALUE_${count}`] = value;
54741
+ env3.GIT_CONFIG_COUNT = String(count + 1);
54742
+ }
54743
+ function applyMemfsGitProxyEnv(env3) {
54744
+ const rewrite = getMemfsGitProxyRewriteConfig(env3);
54745
+ if (!rewrite) {
54746
+ return;
54747
+ }
54748
+ appendGitConfigEnv(env3, rewrite.configKey, rewrite.configValue);
54749
+ env3.GIT_TERMINAL_PROMPT = "0";
54750
+ env3.GCM_INTERACTIVE = "never";
54751
+ env3.GIT_ASKPASS = "";
54752
+ env3.SSH_ASKPASS = "";
54753
+ }
54550
54754
  function getShellEnv() {
54551
54755
  const env3 = { ...process.env };
54552
54756
  const pathKey = Object.keys(env3).find((k) => k.toUpperCase() === "PATH") || "PATH";
@@ -54634,6 +54838,7 @@ function getShellEnv() {
54634
54838
  if (!env3.TERM) {
54635
54839
  env3.TERM = "xterm-256color";
54636
54840
  }
54841
+ applyMemfsGitProxyEnv(env3);
54637
54842
  return env3;
54638
54843
  }
54639
54844
  var LETTA_BIN_ARGS_ENV = "LETTA_CODE_BIN_ARGS_JSON";
@@ -54922,13 +55127,19 @@ function validateWorktreePath(command, cwd2) {
54922
55127
  }
54923
55128
  return null;
54924
55129
  }
55130
+ function rebuildCachedLauncher(command) {
55131
+ if (!cachedWorkingLauncher)
55132
+ return null;
55133
+ const cachedExecutable = cachedWorkingLauncher[0]?.toLowerCase();
55134
+ if (!cachedExecutable)
55135
+ return null;
55136
+ const launchers = buildShellLaunchers(command);
55137
+ return launchers.find((launcher) => launcher[0]?.toLowerCase() === cachedExecutable) ?? null;
55138
+ }
54925
55139
  function getBackgroundLauncher(command) {
54926
- if (cachedWorkingLauncher) {
54927
- const [executable, ...launcherArgs] = cachedWorkingLauncher;
54928
- if (executable) {
54929
- return [executable, ...launcherArgs.slice(0, -1), command];
54930
- }
54931
- }
55140
+ const cachedLauncher = rebuildCachedLauncher(command);
55141
+ if (cachedLauncher)
55142
+ return cachedLauncher;
54932
55143
  const launchers = buildShellLaunchers(command);
54933
55144
  return launchers[0] || [];
54934
55145
  }
@@ -54944,9 +55155,8 @@ async function spawnCommand(command, options) {
54944
55155
  });
54945
55156
  }
54946
55157
  if (cachedWorkingLauncher) {
54947
- const [executable, ...launcherArgs] = cachedWorkingLauncher;
54948
- if (executable) {
54949
- const newLauncher = [executable, ...launcherArgs.slice(0, -1), command];
55158
+ const newLauncher = rebuildCachedLauncher(command);
55159
+ if (newLauncher) {
54950
55160
  try {
54951
55161
  const result = await spawnWithLauncher(newLauncher, {
54952
55162
  cwd: options.cwd,
@@ -55195,6 +55405,7 @@ var init_Bash2 = __esm(() => {
55195
55405
  init_worktree_ownership();
55196
55406
  init_process_manager();
55197
55407
  init_shellEnv();
55408
+ init_shellLaunchers();
55198
55409
  init_shellRunner();
55199
55410
  init_truncation();
55200
55411
  });
@@ -56449,14 +56660,14 @@ async function memory(args) {
56449
56660
  }
56450
56661
  const memoryDir = resolveMemoryDir();
56451
56662
  ensureMemoryRepo(memoryDir);
56452
- await assertMemoryRepoReadyForWrite(memoryDir);
56663
+ const { agentId, agentName } = await getAgentIdentity();
56664
+ await assertMemoryRepoReadyForWrite(memoryDir, agentId);
56453
56665
  const affectedPaths = await applyMemoryCommand(memoryDir, args);
56454
56666
  if (affectedPaths.length === 0) {
56455
56667
  return {
56456
56668
  message: `Memory ${args.command} completed with no changed paths.`
56457
56669
  };
56458
56670
  }
56459
- const { agentId, agentName } = await getAgentIdentity();
56460
56671
  const commitResult = await commitAndSyncMemoryWrite({
56461
56672
  memoryDir,
56462
56673
  pathspecs: affectedPaths,
@@ -56834,12 +57045,12 @@ async function memory_apply_patch(args) {
56834
57045
  }
56835
57046
  const memoryDir = resolveMemoryDir2();
56836
57047
  ensureMemoryRepo2(memoryDir);
56837
- await assertMemoryRepoReadyForWrite(memoryDir);
57048
+ const { agentId, agentName } = await getAgentIdentity2();
57049
+ await assertMemoryRepoReadyForWrite(memoryDir, agentId);
56838
57050
  const pathspecs = await applyMemoryPatch(memoryDir, input);
56839
57051
  if (pathspecs.length === 0) {
56840
57052
  return { message: "memory_apply_patch completed with no changed paths." };
56841
57053
  }
56842
- const { agentId, agentName } = await getAgentIdentity2();
56843
57054
  const commitResult = await commitAndSyncMemoryWrite({
56844
57055
  memoryDir,
56845
57056
  pathspecs,
@@ -71382,6 +71593,7 @@ var DEFAULT_TIMEOUT = 120000;
71382
71593
  var init_Shell2 = __esm(() => {
71383
71594
  init_runtime_context();
71384
71595
  init_shellEnv();
71596
+ init_shellLaunchers();
71385
71597
  init_shellRunner();
71386
71598
  });
71387
71599
 
@@ -71527,6 +71739,7 @@ var init_ShellCommand2 = __esm(() => {
71527
71739
  init_context();
71528
71740
  init_readOnlyShell();
71529
71741
  init_Shell2();
71742
+ init_shellLaunchers();
71530
71743
  init_shellRunner();
71531
71744
  init_truncation();
71532
71745
  });
@@ -147623,7 +147836,9 @@ function runWithLauncher(launcher, inputJson, timeout, signal, workingDirectory,
147623
147836
  });
147624
147837
  }
147625
147838
  var MAX_STDOUT_BYTES = 4096;
147626
- var init_statusLineRuntime = () => {};
147839
+ var init_statusLineRuntime = __esm(() => {
147840
+ init_shellLaunchers();
147841
+ });
147627
147842
 
147628
147843
  // src/cli/helpers/subagentAggregation.ts
147629
147844
  function hasInProgressTaskToolCalls(order, byId, _emittedIds) {
@@ -159132,6 +159347,7 @@ __export(exports_memoryFilesystem2, {
159132
159347
  renderMemoryFilesystemTree: () => renderMemoryFilesystemTree2,
159133
159348
  labelFromRelativePath: () => labelFromRelativePath2,
159134
159349
  isMemfsEnabledOnServer: () => isMemfsEnabledOnServer2,
159350
+ isLettaMemfsServer: () => isLettaMemfsServer2,
159135
159351
  isLettaCloud: () => isLettaCloud2,
159136
159352
  getMemorySystemDir: () => getMemorySystemDir2,
159137
159353
  getMemoryFilesystemRoot: () => getMemoryFilesystemRoot2,
@@ -159319,8 +159535,8 @@ function renderMemoryFilesystemTree2(systemLabels, detachedLabels, options = {})
159319
159535
  }
159320
159536
  async function applyMemfsFlags2(agentId, memfsFlag, noMemfsFlag, options) {
159321
159537
  const { settingsManager: settingsManager3 } = await Promise.resolve().then(() => (init_settings_manager(), exports_settings_manager));
159322
- if (memfsFlag && !await isLettaCloud2()) {
159323
- throw new Error("--memfs is only available on Letta Cloud (api.letta.com).");
159538
+ if (memfsFlag && !await isLettaMemfsServer2()) {
159539
+ throw new Error(await getMemfsSyncUnavailableMessage2());
159324
159540
  }
159325
159541
  const hasExplicitToggle = Boolean(memfsFlag || noMemfsFlag);
159326
159542
  const localMemfsEnabled = settingsManager3.isMemfsEnabled(agentId);
@@ -159391,6 +159607,24 @@ async function isLettaCloud2() {
159391
159607
  const serverUrl = getServerUrl2();
159392
159608
  return serverUrl.includes("api.letta.com") || process.env.LETTA_MEMFS_LOCAL === "1" || process.env.LETTA_API_KEY === "local-desktop";
159393
159609
  }
159610
+ function getServerHostLabel2(serverUrl) {
159611
+ const trimmed = serverUrl.trim();
159612
+ try {
159613
+ return new URL(trimmed).host || trimmed;
159614
+ } catch {
159615
+ return trimmed.replace(/^https?:\/\//, "").replace(/\/+$/, "") || trimmed;
159616
+ }
159617
+ }
159618
+ async function isLettaMemfsServer2() {
159619
+ const { getMemfsServerUrl: getMemfsServerUrl2 } = await Promise.resolve().then(() => (init_client2(), exports_client));
159620
+ const memfsServerUrl = getMemfsServerUrl2();
159621
+ return memfsServerUrl.includes("api.letta.com") || process.env.LETTA_MEMFS_LOCAL === "1" || process.env.LETTA_API_KEY === "local-desktop";
159622
+ }
159623
+ async function getMemfsSyncUnavailableMessage2() {
159624
+ const { getMemfsServerUrl: getMemfsServerUrl2 } = await Promise.resolve().then(() => (init_client2(), exports_client));
159625
+ const memfsServerUrl = getMemfsServerUrl2();
159626
+ return `MemFS sync failed (expected api.letta.com, got ${getServerHostLabel2(memfsServerUrl)})`;
159627
+ }
159394
159628
  async function enableMemfsIfCloud2(agentId) {
159395
159629
  if (!await isLettaCloud2())
159396
159630
  return;
@@ -167844,6 +168078,12 @@ Note: Flags should use double dashes for full names (e.g., --yolo, not -yolo)`);
167844
168078
  }
167845
168079
  const apiKey = process.env.LETTA_API_KEY || settings.env?.LETTA_API_KEY;
167846
168080
  const baseURL = process.env.LETTA_BASE_URL || settings.env?.LETTA_BASE_URL || LETTA_CLOUD_API_URL2;
168081
+ if (isHeadless && baseURL === LETTA_CLOUD_API_URL2 && !process.env.LETTA_API_KEY) {
168082
+ console.error("Missing LETTA_API_KEY");
168083
+ console.error("Headless mode requires an API key set via the LETTA_API_KEY environment variable.");
168084
+ console.error("Get an API key at https://app.letta.com/api-keys");
168085
+ process.exit(1);
168086
+ }
167847
168087
  if (!isHeadless && baseURL === LETTA_CLOUD_API_URL2 && !settings.refreshToken && !apiKey) {
167848
168088
  const { runSetup: runSetup2 } = await init_setup4().then(() => exports_setup);
167849
168089
  await runSetup2();
@@ -167859,11 +168099,6 @@ Error: ${message}`);
167859
168099
  });
167860
168100
  }
167861
168101
  if (!apiKey && baseURL === LETTA_CLOUD_API_URL2) {
167862
- if (isHeadless) {
167863
- console.error("Missing LETTA_API_KEY");
167864
- console.error("Run 'letta' in interactive mode to authenticate or export the missing environment variable");
167865
- process.exit(1);
167866
- }
167867
168102
  console.log(`No credentials found. Let's get you set up!
167868
168103
  `);
167869
168104
  const { runSetup: runSetup2 } = await init_setup4().then(() => exports_setup);
@@ -168712,4 +168947,4 @@ Error during initialization: ${message}`);
168712
168947
  }
168713
168948
  main();
168714
168949
 
168715
- //# debugId=A2D8C37B0089C68564756E2164756E21
168950
+ //# debugId=00A382B05A86170664756E2164756E21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letta-ai/letta-code",
3
- "version": "0.24.4",
3
+ "version": "0.24.6",
4
4
  "description": "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -24,11 +24,12 @@ Below are additional common issues with context and how they can be resolved:
24
24
  #### System prompt bloat
25
25
  Memories compiled into the system prompt (contained in `system/`) should take up about 10% of the total context size (usually ~15-20K tokens). This is a soft target, not a hard requirement.
26
26
 
27
- Use the following script to evaluate the token usage of the system prompt:
27
+ Use the built-in CLI to evaluate token usage of the system prompt:
28
28
  ```bash
29
- npx tsx <SKILL_DIR>/scripts/estimate_system_tokens.ts --memory-dir "$MEMORY_DIR"
29
+ letta memory tokens --format json --quiet
30
30
  ```
31
- Where `<SKILL_DIR>` is the Skill Directory shown when the skill was loaded (visible in the injection header).
31
+
32
+ The command reports `total_tokens` and per-file estimates for `system/`. It is only a measurement tool; decide whether to intervene based on the actual context and the guidance below.
32
33
 
33
34
  **Why detail is load-bearing (read this before cutting anything)**: In-context detail does more than carry information. It does at least four things, and byte-counting sweeps only see the first:
34
35
  1. **Information** — the literal facts stated
@@ -1,128 +0,0 @@
1
- #!/usr/bin/env npx tsx
2
- /**
3
- * Estimate token usage of system prompt memory files.
4
- *
5
- * Self-contained — no imports from the letta-code source tree.
6
- *
7
- * Usage:
8
- * npx tsx estimate_system_tokens.ts --memory-dir "$MEMORY_DIR"
9
- * npx tsx estimate_system_tokens.ts --memory-dir ~/.letta/agents/<id>/memory
10
- */
11
-
12
- import { existsSync, readdirSync, readFileSync } from "node:fs";
13
- import { join } from "node:path";
14
-
15
- // Codex heuristic: ~4 bytes per token (codex-rs/core/src/truncate.rs APPROX_BYTES_PER_TOKEN = 4)
16
- const BYTES_PER_TOKEN = 4;
17
-
18
- function estimateTokens(text: string): number {
19
- return Math.ceil(Buffer.byteLength(text, "utf8") / BYTES_PER_TOKEN);
20
- }
21
-
22
- type FileEstimate = {
23
- path: string;
24
- tokens: number;
25
- };
26
-
27
- type ParsedArgs = {
28
- memoryDir?: string;
29
- top: number;
30
- };
31
-
32
- function parseArgs(argv: string[]): ParsedArgs {
33
- const parsed: ParsedArgs = { top: 20 };
34
-
35
- for (let i = 0; i < argv.length; i++) {
36
- const arg = argv[i];
37
- if (arg === "--memory-dir") {
38
- parsed.memoryDir = argv[i + 1];
39
- i++;
40
- continue;
41
- }
42
- if (arg === "--top") {
43
- const raw = argv[i + 1];
44
- const value = Number.parseInt(raw ?? "", 10);
45
- if (!Number.isNaN(value) && value >= 0) {
46
- parsed.top = value;
47
- }
48
- i++;
49
- }
50
- }
51
-
52
- return parsed;
53
- }
54
-
55
- function normalizePath(value: string): string {
56
- return value.replaceAll("\\", "/");
57
- }
58
-
59
- function walkMarkdownFiles(dir: string): string[] {
60
- if (!existsSync(dir)) {
61
- return [];
62
- }
63
-
64
- const out: string[] = [];
65
- const entries = readdirSync(dir, { withFileTypes: true });
66
-
67
- for (const entry of entries) {
68
- if (entry.name.startsWith(".")) {
69
- continue;
70
- }
71
- const full = join(dir, entry.name);
72
- if (entry.isDirectory()) {
73
- out.push(...walkMarkdownFiles(full));
74
- continue;
75
- }
76
- if (entry.isFile() && entry.name.endsWith(".md")) {
77
- out.push(full);
78
- }
79
- }
80
-
81
- return out;
82
- }
83
-
84
- function formatNumber(value: number): string {
85
- return value.toLocaleString("en-US");
86
- }
87
-
88
- function main(): number {
89
- const args = parseArgs(process.argv.slice(2));
90
- const memoryDir = args.memoryDir || process.env.MEMORY_DIR;
91
-
92
- if (!memoryDir) {
93
- console.error("Missing memory dir. Pass --memory-dir or set MEMORY_DIR.");
94
- return 1;
95
- }
96
-
97
- const systemDir = join(memoryDir, "system");
98
- if (!existsSync(systemDir)) {
99
- console.error(`Missing system directory: ${systemDir}`);
100
- return 1;
101
- }
102
-
103
- const files = walkMarkdownFiles(systemDir).sort();
104
- const rows: FileEstimate[] = [];
105
-
106
- for (const filePath of files) {
107
- const text = readFileSync(filePath, "utf8");
108
- const rel = normalizePath(filePath.slice(memoryDir.length + 1));
109
- rows.push({ path: rel, tokens: estimateTokens(text) });
110
- }
111
-
112
- const estimatedTotalTokens = rows.reduce((sum, row) => sum + row.tokens, 0);
113
-
114
- console.log("Estimated total tokens");
115
- console.log(` ${formatNumber(estimatedTotalTokens)}`);
116
-
117
- console.log("\nPer-file token estimates");
118
- console.log(` ${"tokens".padStart(8)} path`);
119
-
120
- const sortedRows = [...rows].sort((a, b) => b.tokens - a.tokens);
121
- for (const row of sortedRows.slice(0, Math.max(0, args.top))) {
122
- console.log(` ${formatNumber(row.tokens).padStart(8)} ${row.path}`);
123
- }
124
-
125
- return 0;
126
- }
127
-
128
- process.exit(main());