@letta-ai/letta-code 0.24.5 → 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.
Files changed (2) hide show
  1. package/letta.js +131 -33
  2. package/package.json +1 -1
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.5",
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: {
@@ -4721,13 +4721,14 @@ var init_timing = __esm(() => {
4721
4721
  // src/agent/client.ts
4722
4722
  var exports_client = {};
4723
4723
  __export(exports_client, {
4724
- isDesktopLocalProxyUrl: () => isDesktopLocalProxyUrl,
4725
4724
  getServerUrl: () => getServerUrl,
4726
4725
  getMemfsServerUrl: () => getMemfsServerUrl,
4726
+ getMemfsGitProxyRewriteConfig: () => getMemfsGitProxyRewriteConfig,
4727
4727
  getClient: () => getClient,
4728
4728
  consumeLastSDKDiagnostic: () => consumeLastSDKDiagnostic,
4729
4729
  clearLastSDKDiagnostic: () => clearLastSDKDiagnostic,
4730
- __testOverrideGetClient: () => __testOverrideGetClient
4730
+ __testOverrideGetClient: () => __testOverrideGetClient,
4731
+ LETTA_MEMFS_GIT_PROXY_BASE_URL_ENV: () => LETTA_MEMFS_GIT_PROXY_BASE_URL_ENV
4731
4732
  });
4732
4733
  import { hostname } from "node:os";
4733
4734
  function __testOverrideGetClient(factory) {
@@ -4785,8 +4786,29 @@ function isLocalhostUrl(value) {
4785
4786
  return false;
4786
4787
  }
4787
4788
  }
4788
- function isDesktopLocalProxyUrl(value) {
4789
- return process.env.LETTA_DESKTOP_DEBUG_PANEL === "1" && isLocalhostUrl(value);
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
+ };
4790
4812
  }
4791
4813
  function getMemfsServerUrl() {
4792
4814
  let settings = null;
@@ -4797,10 +4819,6 @@ function getMemfsServerUrl() {
4797
4819
  if (configuredMemfsUrl) {
4798
4820
  return configuredMemfsUrl;
4799
4821
  }
4800
- const apiUrl = process.env.LETTA_BASE_URL || settings?.env?.LETTA_BASE_URL;
4801
- if (apiUrl && isDesktopLocalProxyUrl(apiUrl)) {
4802
- return apiUrl;
4803
- }
4804
4822
  return LETTA_CLOUD_API_URL;
4805
4823
  }
4806
4824
  async function getClient() {
@@ -4876,7 +4894,7 @@ If you experience this issue multiple times, move ~/.letta to ~/.letta_backup, a
4876
4894
  ...isTimingsEnabled() && { fetch: createTimingFetch(fetch) }
4877
4895
  });
4878
4896
  }
4879
- 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";
4880
4898
  var init_client2 = __esm(() => {
4881
4899
  init_letta_client();
4882
4900
  init_package();
@@ -38537,6 +38555,7 @@ var init_create = __esm(() => {
38537
38555
  var exports_memoryGit = {};
38538
38556
  __export(exports_memoryGit, {
38539
38557
  unsetMemoryRepositoryUrl: () => unsetMemoryRepositoryUrl,
38558
+ shouldConfigurePersistentMemfsCredentialHelper: () => shouldConfigurePersistentMemfsCredentialHelper,
38540
38559
  setMemoryRepositoryUrl: () => setMemoryRepositoryUrl,
38541
38560
  removeGitMemoryTag: () => removeGitMemoryTag,
38542
38561
  readMemoryRepositoryPushLog: () => readMemoryRepositoryPushLog,
@@ -38548,6 +38567,7 @@ __export(exports_memoryGit, {
38548
38567
  isRetryableGitTransientError: () => isRetryableGitTransientError,
38549
38568
  isMissingCwdGitError: () => isMissingCwdGitError,
38550
38569
  isMemfsRemoteUrlForAgent: () => isMemfsRemoteUrlForAgent,
38570
+ isMemfsGitNetworkCommand: () => isMemfsGitNetworkCommand,
38551
38571
  isGitRepo: () => isGitRepo,
38552
38572
  getMemoryRepositoryUrl: () => getMemoryRepositoryUrl,
38553
38573
  getMemoryRepoDir: () => getMemoryRepoDir,
@@ -38559,6 +38579,7 @@ __export(exports_memoryGit, {
38559
38579
  commitAndSyncMemoryWrite: () => commitAndSyncMemoryWrite,
38560
38580
  cloneMemoryRepo: () => cloneMemoryRepo,
38561
38581
  buildNonInteractiveGitEnv: () => buildNonInteractiveGitEnv,
38582
+ buildMemfsGitProxyArgs: () => buildMemfsGitProxyArgs,
38562
38583
  buildGitAuthArgs: () => buildGitAuthArgs,
38563
38584
  assertMemoryRepoReadyForWrite: () => assertMemoryRepoReadyForWrite,
38564
38585
  addGitMemoryTag: () => addGitMemoryTag,
@@ -38679,6 +38700,22 @@ function buildGitAuthArgs(token) {
38679
38700
  `http.extraHeader=Authorization: Basic ${Buffer.from(`letta:${token}`).toString("base64")}`
38680
38701
  ];
38681
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
+ }
38682
38719
  function buildNonInteractiveGitEnv(env3 = process.env) {
38683
38720
  return {
38684
38721
  ...env3,
@@ -38690,7 +38727,7 @@ function buildNonInteractiveGitEnv(env3 = process.env) {
38690
38727
  }
38691
38728
  async function runGit(cwd2, args, token) {
38692
38729
  const authArgs = token ? buildGitAuthArgs(token) : [];
38693
- const allArgs = [...authArgs, ...args];
38730
+ const allArgs = [...buildMemfsGitProxyArgs(args), ...authArgs, ...args];
38694
38731
  let loggableArgs = args;
38695
38732
  if (args[0] === "config" && typeof args[1] === "string" && args[1].includes("credential") && args[1].includes(".helper")) {
38696
38733
  loggableArgs = [args[0], args[1], "<redacted>"];
@@ -38754,6 +38791,11 @@ async function runGitWithRetry(cwd2, args, token, options) {
38754
38791
  async function configureLocalCredentialHelper(dir, token) {
38755
38792
  const rawBaseUrl = getMemfsServerUrl();
38756
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
+ }
38757
38799
  let helper;
38758
38800
  if (platform2() === "win32") {
38759
38801
  const helperScriptPath = join7(dir, ".git", "letta-credential-helper.cmd");
@@ -38777,6 +38819,17 @@ echo password=${token}
38777
38819
  }
38778
38820
  debugLog("memfs-git", `Configured local credential helper for ${normalizedBaseUrl}${rawBaseUrl !== normalizedBaseUrl ? ` (and raw ${rawBaseUrl})` : ""}`);
38779
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
+ }
38780
38833
  function installPreCommitHook(dir) {
38781
38834
  const hooksDir = join7(dir, ".git", "hooks");
38782
38835
  const hookPath = join7(hooksDir, "pre-commit");
@@ -39027,6 +39080,32 @@ async function fetchMemoryRemote(memoryDir, token) {
39027
39080
  operation: "fetch origin"
39028
39081
  });
39029
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
+ }
39030
39109
  async function resetMemoryToUpstream(memoryDir, token) {
39031
39110
  await runGit(memoryDir, ["reset", "--hard", "@{u}"], token);
39032
39111
  }
@@ -39092,19 +39171,18 @@ async function recoverMemoryPushConflict(params, token, initialSha) {
39092
39171
  rescueRef
39093
39172
  };
39094
39173
  }
39095
- async function assertMemoryRepoReadyForWrite(memoryDir) {
39174
+ async function assertMemoryRepoReadyForWrite(memoryDir, agentId) {
39096
39175
  const status = await runGit(memoryDir, ["status", "--porcelain"]);
39097
39176
  if (status.stdout.trim().length > 0) {
39098
39177
  throw new Error("Memory repo has uncommitted changes. Commit, discard, or sync them before using memory tools.");
39099
39178
  }
39179
+ if (agentId) {
39180
+ const token = await getAuthToken();
39181
+ await pushCleanPendingMemoryCommitsForWrite(memoryDir, agentId, token);
39182
+ }
39100
39183
  try {
39101
- const { stdout } = await runGit(memoryDir, [
39102
- "rev-list",
39103
- "--count",
39104
- "@{u}..HEAD"
39105
- ]);
39106
- const aheadCount = parseInt(stdout.trim(), 10);
39107
- if (aheadCount > 0) {
39184
+ const divergence = await getMemoryAheadBehind(memoryDir);
39185
+ if (divergence && divergence.ahead > 0) {
39108
39186
  throw new Error("Memory repo has local commits that are not pushed to remote. Sync the repo before using memory tools.");
39109
39187
  }
39110
39188
  } catch (error) {
@@ -49253,9 +49331,9 @@ function getServerHostLabel(serverUrl) {
49253
49331
  }
49254
49332
  }
49255
49333
  async function isLettaMemfsServer() {
49256
- const { getMemfsServerUrl: getMemfsServerUrl2, isDesktopLocalProxyUrl: isDesktopLocalProxyUrl2 } = await Promise.resolve().then(() => (init_client2(), exports_client));
49334
+ const { getMemfsServerUrl: getMemfsServerUrl2 } = await Promise.resolve().then(() => (init_client2(), exports_client));
49257
49335
  const memfsServerUrl = getMemfsServerUrl2();
49258
- return memfsServerUrl.includes("api.letta.com") || isDesktopLocalProxyUrl2(memfsServerUrl) || process.env.LETTA_MEMFS_LOCAL === "1" || process.env.LETTA_API_KEY === "local-desktop";
49336
+ return memfsServerUrl.includes("api.letta.com") || process.env.LETTA_MEMFS_LOCAL === "1" || process.env.LETTA_API_KEY === "local-desktop";
49259
49337
  }
49260
49338
  async function getMemfsSyncUnavailableMessage() {
49261
49339
  const { getMemfsServerUrl: getMemfsServerUrl2 } = await Promise.resolve().then(() => (init_client2(), exports_client));
@@ -54655,6 +54733,24 @@ exec ${commandWithArgs} "$@"
54655
54733
  });
54656
54734
  return shimDir;
54657
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
+ }
54658
54754
  function getShellEnv() {
54659
54755
  const env3 = { ...process.env };
54660
54756
  const pathKey = Object.keys(env3).find((k) => k.toUpperCase() === "PATH") || "PATH";
@@ -54742,6 +54838,7 @@ function getShellEnv() {
54742
54838
  if (!env3.TERM) {
54743
54839
  env3.TERM = "xterm-256color";
54744
54840
  }
54841
+ applyMemfsGitProxyEnv(env3);
54745
54842
  return env3;
54746
54843
  }
54747
54844
  var LETTA_BIN_ARGS_ENV = "LETTA_CODE_BIN_ARGS_JSON";
@@ -56563,14 +56660,14 @@ async function memory(args) {
56563
56660
  }
56564
56661
  const memoryDir = resolveMemoryDir();
56565
56662
  ensureMemoryRepo(memoryDir);
56566
- await assertMemoryRepoReadyForWrite(memoryDir);
56663
+ const { agentId, agentName } = await getAgentIdentity();
56664
+ await assertMemoryRepoReadyForWrite(memoryDir, agentId);
56567
56665
  const affectedPaths = await applyMemoryCommand(memoryDir, args);
56568
56666
  if (affectedPaths.length === 0) {
56569
56667
  return {
56570
56668
  message: `Memory ${args.command} completed with no changed paths.`
56571
56669
  };
56572
56670
  }
56573
- const { agentId, agentName } = await getAgentIdentity();
56574
56671
  const commitResult = await commitAndSyncMemoryWrite({
56575
56672
  memoryDir,
56576
56673
  pathspecs: affectedPaths,
@@ -56948,12 +57045,12 @@ async function memory_apply_patch(args) {
56948
57045
  }
56949
57046
  const memoryDir = resolveMemoryDir2();
56950
57047
  ensureMemoryRepo2(memoryDir);
56951
- await assertMemoryRepoReadyForWrite(memoryDir);
57048
+ const { agentId, agentName } = await getAgentIdentity2();
57049
+ await assertMemoryRepoReadyForWrite(memoryDir, agentId);
56952
57050
  const pathspecs = await applyMemoryPatch(memoryDir, input);
56953
57051
  if (pathspecs.length === 0) {
56954
57052
  return { message: "memory_apply_patch completed with no changed paths." };
56955
57053
  }
56956
- const { agentId, agentName } = await getAgentIdentity2();
56957
57054
  const commitResult = await commitAndSyncMemoryWrite({
56958
57055
  memoryDir,
56959
57056
  pathspecs,
@@ -159519,9 +159616,9 @@ function getServerHostLabel2(serverUrl) {
159519
159616
  }
159520
159617
  }
159521
159618
  async function isLettaMemfsServer2() {
159522
- const { getMemfsServerUrl: getMemfsServerUrl2, isDesktopLocalProxyUrl: isDesktopLocalProxyUrl2 } = await Promise.resolve().then(() => (init_client2(), exports_client));
159619
+ const { getMemfsServerUrl: getMemfsServerUrl2 } = await Promise.resolve().then(() => (init_client2(), exports_client));
159523
159620
  const memfsServerUrl = getMemfsServerUrl2();
159524
- return memfsServerUrl.includes("api.letta.com") || isDesktopLocalProxyUrl2(memfsServerUrl) || process.env.LETTA_MEMFS_LOCAL === "1" || process.env.LETTA_API_KEY === "local-desktop";
159621
+ return memfsServerUrl.includes("api.letta.com") || process.env.LETTA_MEMFS_LOCAL === "1" || process.env.LETTA_API_KEY === "local-desktop";
159525
159622
  }
159526
159623
  async function getMemfsSyncUnavailableMessage2() {
159527
159624
  const { getMemfsServerUrl: getMemfsServerUrl2 } = await Promise.resolve().then(() => (init_client2(), exports_client));
@@ -167981,6 +168078,12 @@ Note: Flags should use double dashes for full names (e.g., --yolo, not -yolo)`);
167981
168078
  }
167982
168079
  const apiKey = process.env.LETTA_API_KEY || settings.env?.LETTA_API_KEY;
167983
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
+ }
167984
168087
  if (!isHeadless && baseURL === LETTA_CLOUD_API_URL2 && !settings.refreshToken && !apiKey) {
167985
168088
  const { runSetup: runSetup2 } = await init_setup4().then(() => exports_setup);
167986
168089
  await runSetup2();
@@ -167996,11 +168099,6 @@ Error: ${message}`);
167996
168099
  });
167997
168100
  }
167998
168101
  if (!apiKey && baseURL === LETTA_CLOUD_API_URL2) {
167999
- if (isHeadless) {
168000
- console.error("Missing LETTA_API_KEY");
168001
- console.error("Run 'letta' in interactive mode to authenticate or export the missing environment variable");
168002
- process.exit(1);
168003
- }
168004
168102
  console.log(`No credentials found. Let's get you set up!
168005
168103
  `);
168006
168104
  const { runSetup: runSetup2 } = await init_setup4().then(() => exports_setup);
@@ -168849,4 +168947,4 @@ Error during initialization: ${message}`);
168849
168947
  }
168850
168948
  main();
168851
168949
 
168852
- //# debugId=CB5FADB2FE73B65E64756E2164756E21
168950
+ //# debugId=00A382B05A86170664756E2164756E21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letta-ai/letta-code",
3
- "version": "0.24.5",
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": {