@letta-ai/letta-code 0.19.4 → 0.19.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
@@ -344,7 +344,7 @@ var init_values = __esm(() => {
344
344
  var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
345
345
 
346
346
  // node_modules/@letta-ai/letta-client/version.mjs
347
- var VERSION = "1.7.12";
347
+ var VERSION = "1.8.0";
348
348
 
349
349
  // node_modules/@letta-ai/letta-client/internal/detect-platform.mjs
350
350
  function getDetectedPlatform() {
@@ -3240,7 +3240,7 @@ var package_default;
3240
3240
  var init_package = __esm(() => {
3241
3241
  package_default = {
3242
3242
  name: "@letta-ai/letta-code",
3243
- version: "0.19.4",
3243
+ version: "0.19.6",
3244
3244
  description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
3245
3245
  type: "module",
3246
3246
  bin: {
@@ -3273,7 +3273,7 @@ var init_package = __esm(() => {
3273
3273
  access: "public"
3274
3274
  },
3275
3275
  dependencies: {
3276
- "@letta-ai/letta-client": "^1.7.12",
3276
+ "@letta-ai/letta-client": "1.8.0",
3277
3277
  glob: "^13.0.0",
3278
3278
  "highlight.js": "^11.11.1",
3279
3279
  "ink-link": "^5.0.0",
@@ -3308,7 +3308,7 @@ var init_package = __esm(() => {
3308
3308
  fix: "bunx --bun @biomejs/biome@2.2.5 check --write src",
3309
3309
  typecheck: "tsc --noEmit",
3310
3310
  check: "bun run scripts/check.js",
3311
- dev: "LETTA_DEBUG=1 bun --loader:.md=text --loader:.mdx=text --loader:.txt=text run src/index.ts",
3311
+ dev: "LETTA_DEBUG=${LETTA_DEBUG:-1} bun --loader:.md=text --loader:.mdx=text --loader:.txt=text run src/index.ts",
3312
3312
  build: "node scripts/postinstall-patches.js && bun run build.js",
3313
3313
  "test:update-chain:manual": "bun run src/tests/update-chain-smoke.ts --mode manual",
3314
3314
  "test:update-chain:startup": "bun run src/tests/update-chain-smoke.ts --mode startup",
@@ -4858,7 +4858,7 @@ var init_memory_check_reminder = () => {};
4858
4858
  var memory_filesystem_default = `---
4859
4859
  label: memory_filesystem
4860
4860
  description: Filesystem view of memory blocks (system + user)
4861
- limit: 20000
4861
+ limit: 8000
4862
4862
  ---
4863
4863
 
4864
4864
  /memory/
@@ -6970,7 +6970,12 @@ var init_models2 = __esm(() => {
6970
6970
  label: "Auto",
6971
6971
  description: "Automatically select the best model",
6972
6972
  isFeatured: true,
6973
- free: true
6973
+ free: true,
6974
+ updateArgs: {
6975
+ context_window: 140000,
6976
+ max_output_tokens: 28000,
6977
+ parallel_tool_calls: true
6978
+ }
6974
6979
  },
6975
6980
  {
6976
6981
  id: "auto-fast",
@@ -6978,7 +6983,12 @@ var init_models2 = __esm(() => {
6978
6983
  label: "Auto Fast",
6979
6984
  description: "Automatically select the best fast model",
6980
6985
  isFeatured: true,
6981
- free: true
6986
+ free: true,
6987
+ updateArgs: {
6988
+ context_window: 140000,
6989
+ max_output_tokens: 28000,
6990
+ parallel_tool_calls: true
6991
+ }
6982
6992
  },
6983
6993
  {
6984
6994
  id: "sonnet",
@@ -8431,12 +8441,24 @@ var init_models2 = __esm(() => {
8431
8441
  parallel_tool_calls: true
8432
8442
  }
8433
8443
  },
8444
+ {
8445
+ id: "minimax-m2.7",
8446
+ handle: "minimax/MiniMax-M2.7",
8447
+ label: "MiniMax 2.7",
8448
+ description: "MiniMax's latest coding model",
8449
+ isFeatured: true,
8450
+ free: true,
8451
+ updateArgs: {
8452
+ context_window: 160000,
8453
+ max_output_tokens: 64000,
8454
+ parallel_tool_calls: true
8455
+ }
8456
+ },
8434
8457
  {
8435
8458
  id: "minimax-m2.5",
8436
8459
  handle: "minimax/MiniMax-M2.5",
8437
8460
  label: "MiniMax 2.5",
8438
- description: "MiniMax's latest coding model",
8439
- isFeatured: true,
8461
+ description: "MiniMax's latest coding model (legacy)",
8440
8462
  free: true,
8441
8463
  updateArgs: {
8442
8464
  context_window: 160000,
@@ -39806,9 +39828,483 @@ var init_build4 = __esm(async () => {
39806
39828
  build_default2 = Spinner;
39807
39829
  });
39808
39830
 
39809
- // src/cli/helpers/planName.ts
39831
+ // src/cli/helpers/fileIndex.ts
39832
+ import { createHash as createHash2 } from "node:crypto";
39833
+ import {
39834
+ existsSync as existsSync9,
39835
+ mkdirSync as mkdirSync7,
39836
+ readdirSync as readdirSync4,
39837
+ readFileSync as readFileSync6,
39838
+ statSync as statSync2,
39839
+ writeFileSync as writeFileSync4
39840
+ } from "node:fs";
39810
39841
  import { homedir as homedir7 } from "node:os";
39811
- import { join as join8 } from "node:path";
39842
+ import { join as join8, normalize as normalize2, relative as relative2, sep as sep2 } from "node:path";
39843
+ function normalizeParent2(relativePath) {
39844
+ if (relativePath.length === 0) {
39845
+ return "";
39846
+ }
39847
+ const lastSepIndex = relativePath.lastIndexOf(sep2);
39848
+ return lastSepIndex === -1 ? "" : relativePath.slice(0, lastSepIndex);
39849
+ }
39850
+ function hashValue2(input) {
39851
+ return createHash2("sha256").update(input).digest("hex");
39852
+ }
39853
+ function lowerBound2(sorted, target) {
39854
+ let low = 0;
39855
+ let high = sorted.length;
39856
+ while (low < high) {
39857
+ const mid = low + high >> 1;
39858
+ if ((sorted[mid] ?? "") < target) {
39859
+ low = mid + 1;
39860
+ } else {
39861
+ high = mid;
39862
+ }
39863
+ }
39864
+ return low;
39865
+ }
39866
+ function findPrefixRange2(sorted, prefix) {
39867
+ const start = lowerBound2(sorted, prefix);
39868
+ let end = start;
39869
+ while (end < sorted.length) {
39870
+ const candidate = sorted[end];
39871
+ if (!candidate?.startsWith(prefix)) {
39872
+ break;
39873
+ }
39874
+ end++;
39875
+ }
39876
+ return [start, end];
39877
+ }
39878
+ function preparePreviousIndexData2(cache5) {
39879
+ const entryPaths = cache5.entries.map((entry) => entry.path);
39880
+ const merkleKeys = Object.keys(cache5.merkle).sort();
39881
+ const stats = { ...cache5.stats };
39882
+ const statsKeys = Object.keys(stats).sort();
39883
+ return {
39884
+ entries: cache5.entries,
39885
+ entryPaths,
39886
+ merkle: cache5.merkle,
39887
+ merkleKeys,
39888
+ stats,
39889
+ statsKeys
39890
+ };
39891
+ }
39892
+ function appendSubtreeEntries2(targetEntries, previous, path3) {
39893
+ if (path3 === "") {
39894
+ for (const e of previous.entries)
39895
+ targetEntries.push(e);
39896
+ return;
39897
+ }
39898
+ const { entryPaths, entries: previousEntries } = previous;
39899
+ const prefix = `${path3}/`;
39900
+ const [start, end] = findPrefixRange2(entryPaths, prefix);
39901
+ for (let i = start;i < end; i++) {
39902
+ const entry = previousEntries[i];
39903
+ if (entry !== undefined)
39904
+ targetEntries.push(entry);
39905
+ }
39906
+ }
39907
+ function copyMerkleSubtree2(previous, path3, target) {
39908
+ if (path3 !== "" && previous.merkle[path3]) {
39909
+ target[path3] = previous.merkle[path3];
39910
+ }
39911
+ const prefix = path3 === "" ? "" : `${path3}/`;
39912
+ const [start, end] = prefix === "" ? [0, previous.merkleKeys.length] : findPrefixRange2(previous.merkleKeys, prefix);
39913
+ for (let i = start;i < end; i++) {
39914
+ const key = previous.merkleKeys[i];
39915
+ if (key === undefined)
39916
+ continue;
39917
+ target[key] = previous.merkle[key] ?? "";
39918
+ }
39919
+ }
39920
+ function copyStatsSubtree2(previous, path3, target) {
39921
+ if (path3 !== "" && previous.stats[path3]) {
39922
+ target[path3] = previous.stats[path3];
39923
+ }
39924
+ const prefix = path3 === "" ? "" : `${path3}/`;
39925
+ const [start, end] = prefix === "" ? [0, previous.statsKeys.length] : findPrefixRange2(previous.statsKeys, prefix);
39926
+ for (let i = start;i < end; i++) {
39927
+ const key = previous.statsKeys[i];
39928
+ if (key === undefined)
39929
+ continue;
39930
+ const val = previous.stats[key];
39931
+ if (val !== undefined)
39932
+ target[key] = val;
39933
+ }
39934
+ }
39935
+ function collectPreviousChildNames2(previous, path3) {
39936
+ const names = new Set;
39937
+ const prefix = path3 === "" ? "" : `${path3}/`;
39938
+ const [start, end] = prefix === "" ? [0, previous.statsKeys.length] : findPrefixRange2(previous.statsKeys, prefix);
39939
+ for (let i = start;i < end; i++) {
39940
+ const key = previous.statsKeys[i];
39941
+ if (!key) {
39942
+ continue;
39943
+ }
39944
+ const remainder = key.slice(prefix.length);
39945
+ const slashIndex = remainder.indexOf("/");
39946
+ const childName = slashIndex === -1 ? remainder : remainder.slice(0, slashIndex);
39947
+ if (childName.length > 0) {
39948
+ names.add(childName);
39949
+ }
39950
+ }
39951
+ return names;
39952
+ }
39953
+ function statsMatch2(prev, current) {
39954
+ if (prev.type === "dir" && !current.isDirectory()) {
39955
+ return false;
39956
+ }
39957
+ if (prev.type === "file" && !current.isFile()) {
39958
+ return false;
39959
+ }
39960
+ if (prev.mtimeMs !== current.mtimeMs || prev.ino !== (current.ino ?? 0)) {
39961
+ return false;
39962
+ }
39963
+ if (prev.type === "file") {
39964
+ return typeof prev.size === "number" ? prev.size === current.size : true;
39965
+ }
39966
+ return true;
39967
+ }
39968
+ function shouldReuseDirectory2(previous, path3, stats, childNames, childStats) {
39969
+ if (!previous) {
39970
+ return false;
39971
+ }
39972
+ const previousStats = previous.stats[path3];
39973
+ if (!previousStats || previousStats.type !== "dir") {
39974
+ return false;
39975
+ }
39976
+ if (previousStats.mtimeMs !== stats.mtimeMs || previousStats.ino !== stats.ino) {
39977
+ return false;
39978
+ }
39979
+ const previousChildNames = collectPreviousChildNames2(previous, path3);
39980
+ const seen = new Set;
39981
+ for (const childName of childNames) {
39982
+ const childPath = path3 === "" ? childName : `${path3}/${childName}`;
39983
+ const prevStats = previous.stats[childPath];
39984
+ const currentStats = childStats.get(childName);
39985
+ if (!prevStats || !currentStats) {
39986
+ return false;
39987
+ }
39988
+ if (!statsMatch2(prevStats, currentStats)) {
39989
+ return false;
39990
+ }
39991
+ seen.add(childName);
39992
+ }
39993
+ if (seen.size !== previousChildNames.size) {
39994
+ return false;
39995
+ }
39996
+ for (const name of previousChildNames) {
39997
+ if (!seen.has(name)) {
39998
+ return false;
39999
+ }
40000
+ }
40001
+ return true;
40002
+ }
40003
+ async function buildDirectory2(dir, relativePath, entries, merkle, statsMap, previous, depth, context2) {
40004
+ let dirStats;
40005
+ try {
40006
+ dirStats = statSync2(dir);
40007
+ } catch {
40008
+ const unreadableHash = hashValue2("__unreadable__");
40009
+ merkle[relativePath] = unreadableHash;
40010
+ return unreadableHash;
40011
+ }
40012
+ const currentStats = {
40013
+ type: "dir",
40014
+ mtimeMs: dirStats.mtimeMs,
40015
+ ino: dirStats.ino ?? 0
40016
+ };
40017
+ let dirEntries;
40018
+ try {
40019
+ dirEntries = readdirSync4(dir);
40020
+ } catch {
40021
+ const unreadableHash = hashValue2("__unreadable__");
40022
+ merkle[relativePath] = unreadableHash;
40023
+ return unreadableHash;
40024
+ }
40025
+ const childNames = [];
40026
+ const childStatsMap = new Map;
40027
+ for (const entry of dirEntries) {
40028
+ const entryRelPath = relativePath === "" ? entry : `${relativePath}/${entry}`;
40029
+ if (shouldExcludeEntry(entry, entryRelPath)) {
40030
+ continue;
40031
+ }
40032
+ try {
40033
+ const childStat = statSync2(join8(dir, entry));
40034
+ childNames.push(entry);
40035
+ childStatsMap.set(entry, childStat);
40036
+ } catch {}
40037
+ }
40038
+ if (previous !== undefined && shouldReuseDirectory2(previous, relativePath, currentStats, childNames, childStatsMap)) {
40039
+ copyStatsSubtree2(previous, relativePath, statsMap);
40040
+ appendSubtreeEntries2(entries, previous, relativePath);
40041
+ copyMerkleSubtree2(previous, relativePath, merkle);
40042
+ return previous.merkle[relativePath] ?? hashValue2("__reused__");
40043
+ }
40044
+ statsMap[relativePath] = currentStats;
40045
+ if (depth >= MAX_INDEX_DEPTH2 || context2.newEntryCount >= MAX_CACHE_ENTRIES2) {
40046
+ context2.truncated = true;
40047
+ const truncatedHash = hashValue2("__truncated__");
40048
+ merkle[relativePath] = truncatedHash;
40049
+ return truncatedHash;
40050
+ }
40051
+ const childHashes = [];
40052
+ for (const entry of childNames) {
40053
+ if (context2.newEntryCount >= MAX_CACHE_ENTRIES2) {
40054
+ context2.truncated = true;
40055
+ break;
40056
+ }
40057
+ if (context2.newEntryCount > 0 && context2.newEntryCount % 500 === 0) {
40058
+ await new Promise((resolve2) => setImmediate(resolve2));
40059
+ }
40060
+ const entryStat = childStatsMap.get(entry);
40061
+ if (!entryStat) {
40062
+ continue;
40063
+ }
40064
+ const fullPath = join8(dir, entry);
40065
+ const entryPath = relative2(process.cwd(), fullPath);
40066
+ if (!entryPath) {
40067
+ continue;
40068
+ }
40069
+ if (entryStat.isDirectory()) {
40070
+ entries.push({
40071
+ path: entryPath,
40072
+ type: "dir",
40073
+ lowerPath: entryPath.toLowerCase(),
40074
+ parent: normalizeParent2(entryPath)
40075
+ });
40076
+ context2.newEntryCount++;
40077
+ const childHash = await buildDirectory2(fullPath, entryPath, entries, merkle, statsMap, previous, depth + 1, context2);
40078
+ childHashes.push(`dir:${entry}:${childHash}`);
40079
+ } else {
40080
+ const fileHash = hashValue2(`${entryPath}:${entryStat.size}:${entryStat.mtimeMs}:${entryStat.ino ?? 0}`);
40081
+ statsMap[entryPath] = {
40082
+ type: "file",
40083
+ mtimeMs: entryStat.mtimeMs,
40084
+ ino: entryStat.ino ?? 0,
40085
+ size: entryStat.size
40086
+ };
40087
+ merkle[entryPath] = fileHash;
40088
+ entries.push({
40089
+ path: entryPath,
40090
+ type: "file",
40091
+ lowerPath: entryPath.toLowerCase(),
40092
+ parent: normalizeParent2(entryPath)
40093
+ });
40094
+ context2.newEntryCount++;
40095
+ childHashes.push(`file:${entry}:${fileHash}`);
40096
+ }
40097
+ }
40098
+ const dirHash = hashValue2(childHashes.sort().join("|"));
40099
+ merkle[relativePath] = dirHash;
40100
+ return dirHash;
40101
+ }
40102
+ async function buildIndex2(previous) {
40103
+ const entries = [];
40104
+ const merkle = {};
40105
+ const statsMap = {};
40106
+ const context2 = { newEntryCount: 0, truncated: false };
40107
+ const rootHash = await buildDirectory2(process.cwd(), "", entries, merkle, statsMap, previous, 0, context2);
40108
+ entries.sort((a, b) => a.path.localeCompare(b.path));
40109
+ const seen = new Set;
40110
+ const deduped = entries.filter((e) => {
40111
+ if (seen.has(e.path))
40112
+ return false;
40113
+ seen.add(e.path);
40114
+ return true;
40115
+ });
40116
+ return {
40117
+ entries: deduped,
40118
+ merkle,
40119
+ stats: statsMap,
40120
+ rootHash,
40121
+ truncated: context2.truncated
40122
+ };
40123
+ }
40124
+ function sanitizeWorkspacePath2(workspacePath) {
40125
+ const normalizedPath = normalize2(workspacePath);
40126
+ const strippedPath = normalizedPath.replace(/^[/\\]+/, "");
40127
+ const sanitized = strippedPath.replace(/[/\\:]/g, "_").replace(/\s+/g, "_");
40128
+ return sanitized.length === 0 ? "workspace" : sanitized;
40129
+ }
40130
+ function getProjectStorageDir2() {
40131
+ const homeDir = homedir7();
40132
+ const sanitizedWorkspace = sanitizeWorkspacePath2(process.cwd());
40133
+ return join8(homeDir, ".letta", "projects", sanitizedWorkspace);
40134
+ }
40135
+ function ensureProjectStorageDir2() {
40136
+ const storageDir = getProjectStorageDir2();
40137
+ if (!existsSync9(storageDir)) {
40138
+ mkdirSync7(storageDir, { recursive: true });
40139
+ }
40140
+ return storageDir;
40141
+ }
40142
+ function getProjectIndexPath2() {
40143
+ return join8(getProjectStorageDir2(), PROJECT_INDEX_FILENAME2);
40144
+ }
40145
+ function loadCachedIndex2() {
40146
+ const indexPath = getProjectIndexPath2();
40147
+ if (!existsSync9(indexPath)) {
40148
+ return null;
40149
+ }
40150
+ try {
40151
+ const content = readFileSync6(indexPath, "utf-8");
40152
+ const parsed = JSON.parse(content);
40153
+ if (parsed?.metadata && typeof parsed.metadata.rootHash === "string" && Array.isArray(parsed.entries) && parsed.merkle && typeof parsed.merkle === "object") {
40154
+ const merkle = {};
40155
+ for (const [key, value] of Object.entries(parsed.merkle)) {
40156
+ if (typeof value === "string") {
40157
+ merkle[key] = value;
40158
+ }
40159
+ }
40160
+ const stats = {};
40161
+ if (parsed.stats && typeof parsed.stats === "object") {
40162
+ for (const [path3, rawStats] of Object.entries(parsed.stats)) {
40163
+ const sv = rawStats;
40164
+ if (sv && typeof sv.mtimeMs === "number" && typeof sv.ino === "number" && (sv.type === "file" || sv.type === "dir")) {
40165
+ stats[path3] = {
40166
+ type: sv.type,
40167
+ mtimeMs: sv.mtimeMs,
40168
+ ino: sv.ino
40169
+ };
40170
+ }
40171
+ }
40172
+ }
40173
+ return {
40174
+ metadata: {
40175
+ rootHash: parsed.metadata.rootHash
40176
+ },
40177
+ entries: parsed.entries,
40178
+ merkle,
40179
+ stats
40180
+ };
40181
+ }
40182
+ } catch {}
40183
+ return null;
40184
+ }
40185
+ function cacheProjectIndex2(result) {
40186
+ try {
40187
+ const storageDir = ensureProjectStorageDir2();
40188
+ const indexPath = join8(storageDir, PROJECT_INDEX_FILENAME2);
40189
+ const payload = {
40190
+ metadata: {
40191
+ rootHash: result.rootHash
40192
+ },
40193
+ entries: result.entries,
40194
+ merkle: result.merkle,
40195
+ stats: result.stats
40196
+ };
40197
+ writeFileSync4(indexPath, JSON.stringify(payload, null, 2), "utf-8");
40198
+ } catch {}
40199
+ }
40200
+ function buildCachedEntries2(entries, stats) {
40201
+ const sorted = [...entries].sort((a, b) => {
40202
+ if (a.type === "dir" && b.type !== "dir")
40203
+ return -1;
40204
+ if (a.type !== "dir" && b.type === "dir")
40205
+ return 1;
40206
+ const aMtime = stats[a.path]?.mtimeMs ?? 0;
40207
+ const bMtime = stats[b.path]?.mtimeMs ?? 0;
40208
+ return bMtime - aMtime;
40209
+ }).slice(0, MAX_CACHE_ENTRIES2);
40210
+ return { entries: sorted, paths: new Set(sorted.map((e) => e.path)) };
40211
+ }
40212
+ function ensureFileIndex2() {
40213
+ if (hasCompletedBuild2)
40214
+ return Promise.resolve();
40215
+ if (!buildPromise2) {
40216
+ let currentPromise;
40217
+ currentPromise = (async () => {
40218
+ let succeeded = false;
40219
+ try {
40220
+ const diskIndex = loadCachedIndex2();
40221
+ const previousData = diskIndex ? preparePreviousIndexData2(diskIndex) : undefined;
40222
+ const buildResult = await buildIndex2(previousData);
40223
+ if (diskIndex && diskIndex.metadata.rootHash === buildResult.rootHash) {
40224
+ ({ entries: cachedEntries2, paths: cachedEntryPaths2 } = buildCachedEntries2(buildResult.entries, buildResult.stats));
40225
+ succeeded = true;
40226
+ return;
40227
+ }
40228
+ if (buildResult.truncated) {
40229
+ debugLog("file-index", `Index truncated: workspace exceeds ${MAX_INDEX_DEPTH2} directory levels deep. ` + `Files beyond that depth will fall back to disk search.`);
40230
+ }
40231
+ cacheProjectIndex2(buildResult);
40232
+ ({ entries: cachedEntries2, paths: cachedEntryPaths2 } = buildCachedEntries2(buildResult.entries, buildResult.stats));
40233
+ succeeded = true;
40234
+ } finally {
40235
+ if (buildPromise2 === currentPromise)
40236
+ buildPromise2 = null;
40237
+ if (succeeded)
40238
+ hasCompletedBuild2 = true;
40239
+ }
40240
+ })();
40241
+ buildPromise2 = currentPromise;
40242
+ }
40243
+ return buildPromise2;
40244
+ }
40245
+ function refreshFileIndex() {
40246
+ hasCompletedBuild2 = false;
40247
+ buildPromise2 = null;
40248
+ return ensureFileIndex2();
40249
+ }
40250
+ function addEntriesToCache(matches) {
40251
+ const available = MAX_CACHE_ENTRIES2 - cachedEntries2.length;
40252
+ if (available <= 0)
40253
+ return;
40254
+ let added = 0;
40255
+ for (const match of matches) {
40256
+ if (added >= available)
40257
+ break;
40258
+ if (!cachedEntryPaths2.has(match.path)) {
40259
+ cachedEntries2.push({
40260
+ path: match.path,
40261
+ type: match.type,
40262
+ lowerPath: match.path.toLowerCase(),
40263
+ parent: normalizeParent2(match.path)
40264
+ });
40265
+ cachedEntryPaths2.add(match.path);
40266
+ added++;
40267
+ }
40268
+ }
40269
+ }
40270
+ function searchFileIndex(options) {
40271
+ const { searchDir, pattern, deep, maxResults } = options;
40272
+ const normalizedDir = searchDir === "." ? "" : searchDir;
40273
+ const dirWithSep = normalizedDir === "" ? "" : `${normalizedDir}${sep2}`;
40274
+ const lowerPattern = pattern.toLowerCase();
40275
+ const results = [];
40276
+ for (const entry of cachedEntries2) {
40277
+ if (normalizedDir) {
40278
+ if (entry.path !== normalizedDir && !entry.path.startsWith(dirWithSep)) {
40279
+ continue;
40280
+ }
40281
+ }
40282
+ if (!deep && entry.parent !== normalizedDir) {
40283
+ continue;
40284
+ }
40285
+ if (lowerPattern && !entry.lowerPath.includes(lowerPattern)) {
40286
+ continue;
40287
+ }
40288
+ results.push({ path: entry.path, type: entry.type });
40289
+ if (results.length >= maxResults) {
40290
+ break;
40291
+ }
40292
+ }
40293
+ return results;
40294
+ }
40295
+ var MAX_INDEX_DEPTH2 = 12, PROJECT_INDEX_FILENAME2 = "file-index.json", MAX_CACHE_ENTRIES2, cachedEntries2, cachedEntryPaths2, buildPromise2 = null, hasCompletedBuild2 = false;
40296
+ var init_fileIndex = __esm(() => {
40297
+ init_debug();
40298
+ init_lettaSettings();
40299
+ init_fileSearchConfig();
40300
+ MAX_CACHE_ENTRIES2 = readIntSetting("MAX_ENTRIES", 50000);
40301
+ cachedEntries2 = [];
40302
+ cachedEntryPaths2 = new Set;
40303
+ });
40304
+
40305
+ // src/cli/helpers/planName.ts
40306
+ import { homedir as homedir8 } from "node:os";
40307
+ import { join as join9 } from "node:path";
39812
40308
  function randomElement(arr) {
39813
40309
  return arr[Math.floor(Math.random() * arr.length)];
39814
40310
  }
@@ -39820,7 +40316,7 @@ function generatePlanName() {
39820
40316
  }
39821
40317
  function generatePlanFilePath() {
39822
40318
  const name = generatePlanName();
39823
- return join8(homedir7(), ".letta", "plans", `${name}.md`);
40319
+ return join9(homedir8(), ".letta", "plans", `${name}.md`);
39824
40320
  }
39825
40321
  var adjectives, nouns;
39826
40322
  var init_planName = __esm(() => {
@@ -40383,480 +40879,6 @@ var init_approval_execution = __esm(async () => {
40383
40879
  ]);
40384
40880
  });
40385
40881
 
40386
- // src/cli/helpers/fileIndex.ts
40387
- import { createHash as createHash2 } from "node:crypto";
40388
- import {
40389
- existsSync as existsSync9,
40390
- mkdirSync as mkdirSync7,
40391
- readdirSync as readdirSync4,
40392
- readFileSync as readFileSync6,
40393
- statSync as statSync2,
40394
- writeFileSync as writeFileSync4
40395
- } from "node:fs";
40396
- import { homedir as homedir8 } from "node:os";
40397
- import { join as join9, normalize as normalize3, relative as relative2, sep as sep2 } from "node:path";
40398
- function normalizeParent2(relativePath) {
40399
- if (relativePath.length === 0) {
40400
- return "";
40401
- }
40402
- const lastSepIndex = relativePath.lastIndexOf(sep2);
40403
- return lastSepIndex === -1 ? "" : relativePath.slice(0, lastSepIndex);
40404
- }
40405
- function hashValue2(input) {
40406
- return createHash2("sha256").update(input).digest("hex");
40407
- }
40408
- function lowerBound2(sorted, target) {
40409
- let low = 0;
40410
- let high = sorted.length;
40411
- while (low < high) {
40412
- const mid = low + high >> 1;
40413
- if ((sorted[mid] ?? "") < target) {
40414
- low = mid + 1;
40415
- } else {
40416
- high = mid;
40417
- }
40418
- }
40419
- return low;
40420
- }
40421
- function findPrefixRange2(sorted, prefix) {
40422
- const start = lowerBound2(sorted, prefix);
40423
- let end = start;
40424
- while (end < sorted.length) {
40425
- const candidate = sorted[end];
40426
- if (!candidate?.startsWith(prefix)) {
40427
- break;
40428
- }
40429
- end++;
40430
- }
40431
- return [start, end];
40432
- }
40433
- function preparePreviousIndexData2(cache5) {
40434
- const entryPaths = cache5.entries.map((entry) => entry.path);
40435
- const merkleKeys = Object.keys(cache5.merkle).sort();
40436
- const stats = { ...cache5.stats };
40437
- const statsKeys = Object.keys(stats).sort();
40438
- return {
40439
- entries: cache5.entries,
40440
- entryPaths,
40441
- merkle: cache5.merkle,
40442
- merkleKeys,
40443
- stats,
40444
- statsKeys
40445
- };
40446
- }
40447
- function appendSubtreeEntries2(targetEntries, previous, path4) {
40448
- if (path4 === "") {
40449
- for (const e of previous.entries)
40450
- targetEntries.push(e);
40451
- return;
40452
- }
40453
- const { entryPaths, entries: previousEntries } = previous;
40454
- const prefix = `${path4}/`;
40455
- const [start, end] = findPrefixRange2(entryPaths, prefix);
40456
- for (let i = start;i < end; i++) {
40457
- const entry = previousEntries[i];
40458
- if (entry !== undefined)
40459
- targetEntries.push(entry);
40460
- }
40461
- }
40462
- function copyMerkleSubtree2(previous, path4, target) {
40463
- if (path4 !== "" && previous.merkle[path4]) {
40464
- target[path4] = previous.merkle[path4];
40465
- }
40466
- const prefix = path4 === "" ? "" : `${path4}/`;
40467
- const [start, end] = prefix === "" ? [0, previous.merkleKeys.length] : findPrefixRange2(previous.merkleKeys, prefix);
40468
- for (let i = start;i < end; i++) {
40469
- const key = previous.merkleKeys[i];
40470
- if (key === undefined)
40471
- continue;
40472
- target[key] = previous.merkle[key] ?? "";
40473
- }
40474
- }
40475
- function copyStatsSubtree2(previous, path4, target) {
40476
- if (path4 !== "" && previous.stats[path4]) {
40477
- target[path4] = previous.stats[path4];
40478
- }
40479
- const prefix = path4 === "" ? "" : `${path4}/`;
40480
- const [start, end] = prefix === "" ? [0, previous.statsKeys.length] : findPrefixRange2(previous.statsKeys, prefix);
40481
- for (let i = start;i < end; i++) {
40482
- const key = previous.statsKeys[i];
40483
- if (key === undefined)
40484
- continue;
40485
- const val = previous.stats[key];
40486
- if (val !== undefined)
40487
- target[key] = val;
40488
- }
40489
- }
40490
- function collectPreviousChildNames2(previous, path4) {
40491
- const names = new Set;
40492
- const prefix = path4 === "" ? "" : `${path4}/`;
40493
- const [start, end] = prefix === "" ? [0, previous.statsKeys.length] : findPrefixRange2(previous.statsKeys, prefix);
40494
- for (let i = start;i < end; i++) {
40495
- const key = previous.statsKeys[i];
40496
- if (!key) {
40497
- continue;
40498
- }
40499
- const remainder = key.slice(prefix.length);
40500
- const slashIndex = remainder.indexOf("/");
40501
- const childName = slashIndex === -1 ? remainder : remainder.slice(0, slashIndex);
40502
- if (childName.length > 0) {
40503
- names.add(childName);
40504
- }
40505
- }
40506
- return names;
40507
- }
40508
- function statsMatch2(prev, current) {
40509
- if (prev.type === "dir" && !current.isDirectory()) {
40510
- return false;
40511
- }
40512
- if (prev.type === "file" && !current.isFile()) {
40513
- return false;
40514
- }
40515
- if (prev.mtimeMs !== current.mtimeMs || prev.ino !== (current.ino ?? 0)) {
40516
- return false;
40517
- }
40518
- if (prev.type === "file") {
40519
- return typeof prev.size === "number" ? prev.size === current.size : true;
40520
- }
40521
- return true;
40522
- }
40523
- function shouldReuseDirectory2(previous, path4, stats, childNames, childStats) {
40524
- if (!previous) {
40525
- return false;
40526
- }
40527
- const previousStats = previous.stats[path4];
40528
- if (!previousStats || previousStats.type !== "dir") {
40529
- return false;
40530
- }
40531
- if (previousStats.mtimeMs !== stats.mtimeMs || previousStats.ino !== stats.ino) {
40532
- return false;
40533
- }
40534
- const previousChildNames = collectPreviousChildNames2(previous, path4);
40535
- const seen = new Set;
40536
- for (const childName of childNames) {
40537
- const childPath = path4 === "" ? childName : `${path4}/${childName}`;
40538
- const prevStats = previous.stats[childPath];
40539
- const currentStats = childStats.get(childName);
40540
- if (!prevStats || !currentStats) {
40541
- return false;
40542
- }
40543
- if (!statsMatch2(prevStats, currentStats)) {
40544
- return false;
40545
- }
40546
- seen.add(childName);
40547
- }
40548
- if (seen.size !== previousChildNames.size) {
40549
- return false;
40550
- }
40551
- for (const name of previousChildNames) {
40552
- if (!seen.has(name)) {
40553
- return false;
40554
- }
40555
- }
40556
- return true;
40557
- }
40558
- async function buildDirectory2(dir, relativePath, entries, merkle, statsMap, previous, depth, context2) {
40559
- let dirStats;
40560
- try {
40561
- dirStats = statSync2(dir);
40562
- } catch {
40563
- const unreadableHash = hashValue2("__unreadable__");
40564
- merkle[relativePath] = unreadableHash;
40565
- return unreadableHash;
40566
- }
40567
- const currentStats = {
40568
- type: "dir",
40569
- mtimeMs: dirStats.mtimeMs,
40570
- ino: dirStats.ino ?? 0
40571
- };
40572
- let dirEntries;
40573
- try {
40574
- dirEntries = readdirSync4(dir);
40575
- } catch {
40576
- const unreadableHash = hashValue2("__unreadable__");
40577
- merkle[relativePath] = unreadableHash;
40578
- return unreadableHash;
40579
- }
40580
- const childNames = [];
40581
- const childStatsMap = new Map;
40582
- for (const entry of dirEntries) {
40583
- const entryRelPath = relativePath === "" ? entry : `${relativePath}/${entry}`;
40584
- if (shouldExcludeEntry(entry, entryRelPath)) {
40585
- continue;
40586
- }
40587
- try {
40588
- const childStat = statSync2(join9(dir, entry));
40589
- childNames.push(entry);
40590
- childStatsMap.set(entry, childStat);
40591
- } catch {}
40592
- }
40593
- if (previous !== undefined && shouldReuseDirectory2(previous, relativePath, currentStats, childNames, childStatsMap)) {
40594
- copyStatsSubtree2(previous, relativePath, statsMap);
40595
- appendSubtreeEntries2(entries, previous, relativePath);
40596
- copyMerkleSubtree2(previous, relativePath, merkle);
40597
- return previous.merkle[relativePath] ?? hashValue2("__reused__");
40598
- }
40599
- statsMap[relativePath] = currentStats;
40600
- if (depth >= MAX_INDEX_DEPTH2 || context2.newEntryCount >= MAX_CACHE_ENTRIES2) {
40601
- context2.truncated = true;
40602
- const truncatedHash = hashValue2("__truncated__");
40603
- merkle[relativePath] = truncatedHash;
40604
- return truncatedHash;
40605
- }
40606
- const childHashes = [];
40607
- for (const entry of childNames) {
40608
- if (context2.newEntryCount >= MAX_CACHE_ENTRIES2) {
40609
- context2.truncated = true;
40610
- break;
40611
- }
40612
- if (context2.newEntryCount > 0 && context2.newEntryCount % 500 === 0) {
40613
- await new Promise((resolve3) => setImmediate(resolve3));
40614
- }
40615
- const entryStat = childStatsMap.get(entry);
40616
- if (!entryStat) {
40617
- continue;
40618
- }
40619
- const fullPath = join9(dir, entry);
40620
- const entryPath = relative2(process.cwd(), fullPath);
40621
- if (!entryPath) {
40622
- continue;
40623
- }
40624
- if (entryStat.isDirectory()) {
40625
- entries.push({
40626
- path: entryPath,
40627
- type: "dir",
40628
- lowerPath: entryPath.toLowerCase(),
40629
- parent: normalizeParent2(entryPath)
40630
- });
40631
- context2.newEntryCount++;
40632
- const childHash = await buildDirectory2(fullPath, entryPath, entries, merkle, statsMap, previous, depth + 1, context2);
40633
- childHashes.push(`dir:${entry}:${childHash}`);
40634
- } else {
40635
- const fileHash = hashValue2(`${entryPath}:${entryStat.size}:${entryStat.mtimeMs}:${entryStat.ino ?? 0}`);
40636
- statsMap[entryPath] = {
40637
- type: "file",
40638
- mtimeMs: entryStat.mtimeMs,
40639
- ino: entryStat.ino ?? 0,
40640
- size: entryStat.size
40641
- };
40642
- merkle[entryPath] = fileHash;
40643
- entries.push({
40644
- path: entryPath,
40645
- type: "file",
40646
- lowerPath: entryPath.toLowerCase(),
40647
- parent: normalizeParent2(entryPath)
40648
- });
40649
- context2.newEntryCount++;
40650
- childHashes.push(`file:${entry}:${fileHash}`);
40651
- }
40652
- }
40653
- const dirHash = hashValue2(childHashes.sort().join("|"));
40654
- merkle[relativePath] = dirHash;
40655
- return dirHash;
40656
- }
40657
- async function buildIndex2(previous) {
40658
- const entries = [];
40659
- const merkle = {};
40660
- const statsMap = {};
40661
- const context2 = { newEntryCount: 0, truncated: false };
40662
- const rootHash = await buildDirectory2(process.cwd(), "", entries, merkle, statsMap, previous, 0, context2);
40663
- entries.sort((a, b) => a.path.localeCompare(b.path));
40664
- const seen = new Set;
40665
- const deduped = entries.filter((e) => {
40666
- if (seen.has(e.path))
40667
- return false;
40668
- seen.add(e.path);
40669
- return true;
40670
- });
40671
- return {
40672
- entries: deduped,
40673
- merkle,
40674
- stats: statsMap,
40675
- rootHash,
40676
- truncated: context2.truncated
40677
- };
40678
- }
40679
- function sanitizeWorkspacePath2(workspacePath) {
40680
- const normalizedPath = normalize3(workspacePath);
40681
- const strippedPath = normalizedPath.replace(/^[/\\]+/, "");
40682
- const sanitized = strippedPath.replace(/[/\\:]/g, "_").replace(/\s+/g, "_");
40683
- return sanitized.length === 0 ? "workspace" : sanitized;
40684
- }
40685
- function getProjectStorageDir2() {
40686
- const homeDir = homedir8();
40687
- const sanitizedWorkspace = sanitizeWorkspacePath2(process.cwd());
40688
- return join9(homeDir, ".letta", "projects", sanitizedWorkspace);
40689
- }
40690
- function ensureProjectStorageDir2() {
40691
- const storageDir = getProjectStorageDir2();
40692
- if (!existsSync9(storageDir)) {
40693
- mkdirSync7(storageDir, { recursive: true });
40694
- }
40695
- return storageDir;
40696
- }
40697
- function getProjectIndexPath2() {
40698
- return join9(getProjectStorageDir2(), PROJECT_INDEX_FILENAME2);
40699
- }
40700
- function loadCachedIndex2() {
40701
- const indexPath = getProjectIndexPath2();
40702
- if (!existsSync9(indexPath)) {
40703
- return null;
40704
- }
40705
- try {
40706
- const content = readFileSync6(indexPath, "utf-8");
40707
- const parsed = JSON.parse(content);
40708
- if (parsed?.metadata && typeof parsed.metadata.rootHash === "string" && Array.isArray(parsed.entries) && parsed.merkle && typeof parsed.merkle === "object") {
40709
- const merkle = {};
40710
- for (const [key, value] of Object.entries(parsed.merkle)) {
40711
- if (typeof value === "string") {
40712
- merkle[key] = value;
40713
- }
40714
- }
40715
- const stats = {};
40716
- if (parsed.stats && typeof parsed.stats === "object") {
40717
- for (const [path4, rawStats] of Object.entries(parsed.stats)) {
40718
- const sv = rawStats;
40719
- if (sv && typeof sv.mtimeMs === "number" && typeof sv.ino === "number" && (sv.type === "file" || sv.type === "dir")) {
40720
- stats[path4] = {
40721
- type: sv.type,
40722
- mtimeMs: sv.mtimeMs,
40723
- ino: sv.ino
40724
- };
40725
- }
40726
- }
40727
- }
40728
- return {
40729
- metadata: {
40730
- rootHash: parsed.metadata.rootHash
40731
- },
40732
- entries: parsed.entries,
40733
- merkle,
40734
- stats
40735
- };
40736
- }
40737
- } catch {}
40738
- return null;
40739
- }
40740
- function cacheProjectIndex2(result) {
40741
- try {
40742
- const storageDir = ensureProjectStorageDir2();
40743
- const indexPath = join9(storageDir, PROJECT_INDEX_FILENAME2);
40744
- const payload = {
40745
- metadata: {
40746
- rootHash: result.rootHash
40747
- },
40748
- entries: result.entries,
40749
- merkle: result.merkle,
40750
- stats: result.stats
40751
- };
40752
- writeFileSync4(indexPath, JSON.stringify(payload, null, 2), "utf-8");
40753
- } catch {}
40754
- }
40755
- function buildCachedEntries2(entries, stats) {
40756
- const sorted = [...entries].sort((a, b) => {
40757
- if (a.type === "dir" && b.type !== "dir")
40758
- return -1;
40759
- if (a.type !== "dir" && b.type === "dir")
40760
- return 1;
40761
- const aMtime = stats[a.path]?.mtimeMs ?? 0;
40762
- const bMtime = stats[b.path]?.mtimeMs ?? 0;
40763
- return bMtime - aMtime;
40764
- }).slice(0, MAX_CACHE_ENTRIES2);
40765
- return { entries: sorted, paths: new Set(sorted.map((e) => e.path)) };
40766
- }
40767
- function ensureFileIndex2() {
40768
- if (hasCompletedBuild2)
40769
- return Promise.resolve();
40770
- if (!buildPromise2) {
40771
- let currentPromise;
40772
- currentPromise = (async () => {
40773
- let succeeded = false;
40774
- try {
40775
- const diskIndex = loadCachedIndex2();
40776
- const previousData = diskIndex ? preparePreviousIndexData2(diskIndex) : undefined;
40777
- const buildResult = await buildIndex2(previousData);
40778
- if (diskIndex && diskIndex.metadata.rootHash === buildResult.rootHash) {
40779
- ({ entries: cachedEntries2, paths: cachedEntryPaths2 } = buildCachedEntries2(buildResult.entries, buildResult.stats));
40780
- succeeded = true;
40781
- return;
40782
- }
40783
- if (buildResult.truncated) {
40784
- debugLog("file-index", `Index truncated: workspace exceeds ${MAX_INDEX_DEPTH2} directory levels deep. ` + `Files beyond that depth will fall back to disk search.`);
40785
- }
40786
- cacheProjectIndex2(buildResult);
40787
- ({ entries: cachedEntries2, paths: cachedEntryPaths2 } = buildCachedEntries2(buildResult.entries, buildResult.stats));
40788
- succeeded = true;
40789
- } finally {
40790
- if (buildPromise2 === currentPromise)
40791
- buildPromise2 = null;
40792
- if (succeeded)
40793
- hasCompletedBuild2 = true;
40794
- }
40795
- })();
40796
- buildPromise2 = currentPromise;
40797
- }
40798
- return buildPromise2;
40799
- }
40800
- function refreshFileIndex() {
40801
- hasCompletedBuild2 = false;
40802
- buildPromise2 = null;
40803
- return ensureFileIndex2();
40804
- }
40805
- function addEntriesToCache(matches) {
40806
- const available = MAX_CACHE_ENTRIES2 - cachedEntries2.length;
40807
- if (available <= 0)
40808
- return;
40809
- let added = 0;
40810
- for (const match of matches) {
40811
- if (added >= available)
40812
- break;
40813
- if (!cachedEntryPaths2.has(match.path)) {
40814
- cachedEntries2.push({
40815
- path: match.path,
40816
- type: match.type,
40817
- lowerPath: match.path.toLowerCase(),
40818
- parent: normalizeParent2(match.path)
40819
- });
40820
- cachedEntryPaths2.add(match.path);
40821
- added++;
40822
- }
40823
- }
40824
- }
40825
- function searchFileIndex(options) {
40826
- const { searchDir, pattern, deep, maxResults } = options;
40827
- const normalizedDir = searchDir === "." ? "" : searchDir;
40828
- const dirWithSep = normalizedDir === "" ? "" : `${normalizedDir}${sep2}`;
40829
- const lowerPattern = pattern.toLowerCase();
40830
- const results = [];
40831
- for (const entry of cachedEntries2) {
40832
- if (normalizedDir) {
40833
- if (entry.path !== normalizedDir && !entry.path.startsWith(dirWithSep)) {
40834
- continue;
40835
- }
40836
- }
40837
- if (!deep && entry.parent !== normalizedDir) {
40838
- continue;
40839
- }
40840
- if (lowerPattern && !entry.lowerPath.includes(lowerPattern)) {
40841
- continue;
40842
- }
40843
- results.push({ path: entry.path, type: entry.type });
40844
- if (results.length >= maxResults) {
40845
- break;
40846
- }
40847
- }
40848
- return results;
40849
- }
40850
- var MAX_INDEX_DEPTH2 = 12, PROJECT_INDEX_FILENAME2 = "file-index.json", MAX_CACHE_ENTRIES2, cachedEntries2, cachedEntryPaths2, buildPromise2 = null, hasCompletedBuild2 = false;
40851
- var init_fileIndex = __esm(() => {
40852
- init_debug();
40853
- init_lettaSettings();
40854
- init_fileSearchConfig();
40855
- MAX_CACHE_ENTRIES2 = readIntSetting("MAX_ENTRIES", 50000);
40856
- cachedEntries2 = [];
40857
- cachedEntryPaths2 = new Set;
40858
- });
40859
-
40860
40882
  // src/permissions/session.ts
40861
40883
  var exports_session = {};
40862
40884
  __export(exports_session, {
@@ -43551,7 +43573,7 @@ If a directory has more than 1,000 entries, only the first 1,000 will be shown.`
43551
43573
  var init_LS = () => {};
43552
43574
 
43553
43575
  // src/tools/descriptions/Memory.md
43554
- var Memory_default = '# Memory\nA convinience tool for memories stored in the memory directory (`$MEMORY_DIR`) that automatically commits and pushes changes. \n\nFiles stored inside of `system/` eventually become part of the agent\'s system prompt, so are always in the context window and do not need to be re-read. Other files only have metadata in the system prompt, so may need to be explicitly read to be updated. \n\nSupported operations on memory files: \n- `str_replace`\n- `insert`\n- `delete`\n- `rename` (path rename or description update mode)\n- `create`\nMore general operations can be performanced through directory modifying the files. \n\nPath formats accepted:\n- relative memory file paths (e.g. `system/contacts.md`, `reference/project/team.md`)\n\nNote: absolute paths and `/memories/...` paths are not supported by this client-side tool.\n\nExamples:\n\n```python\n# Replace text in a memory file \nmemory(command="str_replace", reason="Update theme preference", path="system/human/preferences.md", old_string="theme: dark", new_string="theme: light")\n\n# Insert text at line 5\nmemory(command="insert", reason="Add note about meeting", path="reference/history/meeting-notes.md", insert_line=5, insert_text="New note here")\n\n# Delete a memory file \nmemory(command="delete", reason="Remove stale notes", path="reference/history/old_notes.md")\n\n# Rename a memory file \nmemory(command="rename", reason="Promote temp notes", old_path="reference/history/temp.md", new_path="reference/history/permanent.md")\n\n# Create a block with starting text\nmemory(command="create", reason="Track coding preferences", path="system/human/prefs/coding.md", description="The user\'s coding preferences.", file_text="The user seems to add type hints to all of their Python code.")\n\n# Create an empty block\nmemory(command="create", reason="Create coding preferences block", path="reference/history/coding_preferences.md", description="The user\'s coding preferences.")\n```\n';
43576
+ var Memory_default = '# Memory\nA convinience tool for memories stored in the memory directory (`$MEMORY_DIR`) that automatically commits and pushes changes. \n\nFiles stored inside of `system/` eventually become part of the agent\'s system prompt, so are always in the context window and do not need to be re-read. Other files only have metadata in the system prompt, so may need to be explicitly read to be updated. \n\nSupported operations on memory files: \n- `str_replace`\n- `insert`\n- `delete` (files, or directories recursively)\n- `rename` (path rename only)\n- `update_description`\n- `create`\nMore general operations can be performanced through directory modifying the files. \n\nPath formats accepted:\n- relative memory file paths (e.g. `system/contacts.md`, `reference/project/team.md`)\n- absolute paths only when they are inside `$MEMORY_DIR`\n\nNote: absolute paths outside `$MEMORY_DIR` are rejected.\n\nExamples:\n\n```python\n# Replace text in a memory file \nmemory(command="str_replace", reason="Update theme preference", path="system/human/preferences.md", old_string="theme: dark", new_string="theme: light")\n\n# Insert text at line 5\nmemory(command="insert", reason="Add note about meeting", path="reference/history/meeting-notes.md", insert_line=5, insert_text="New note here")\n\n# Delete a memory file \nmemory(command="delete", reason="Remove stale notes", path="reference/history/old_notes.md")\n\n# Rename a memory file \nmemory(command="rename", reason="Promote temp notes", old_path="reference/history/temp.md", new_path="reference/history/permanent.md")\n\n# Update a block description\nmemory(command="update_description", reason="Clarify coding prefs block", path="system/human/prefs/coding.md", description="Dr. Wooders\' coding preferences.")\n\n# Create a block with starting text\nmemory(command="create", reason="Track coding preferences", path="system/human/prefs/coding.md", description="The user\'s coding preferences.", file_text="The user seems to add type hints to all of their Python code.")\n\n# Create an empty block\nmemory(command="create", reason="Create coding preferences block", path="reference/history/coding_preferences.md", description="The user\'s coding preferences.")\n```\n';
43555
43577
  var init_Memory = () => {};
43556
43578
 
43557
43579
  // src/tools/descriptions/MultiEdit.md
@@ -44712,6 +44734,56 @@ var init_process_manager = __esm(() => {
44712
44734
  backgroundTasks = new Map;
44713
44735
  });
44714
44736
 
44737
+ // src/utils/directoryLimits.ts
44738
+ function parsePositiveIntEnv(value, fallback, min, max) {
44739
+ if (!value || value.trim() === "") {
44740
+ return fallback;
44741
+ }
44742
+ const parsed = Number.parseInt(value.trim(), 10);
44743
+ if (Number.isNaN(parsed)) {
44744
+ return fallback;
44745
+ }
44746
+ if (parsed < min || parsed > max) {
44747
+ return fallback;
44748
+ }
44749
+ return parsed;
44750
+ }
44751
+ function getDirectoryLimits(env3 = process.env) {
44752
+ return {
44753
+ memfsTreeMaxLines: parsePositiveIntEnv(env3[DIRECTORY_LIMIT_ENV.memfsTreeMaxLines], DIRECTORY_LIMIT_DEFAULTS.memfsTreeMaxLines, 2, 50000),
44754
+ memfsTreeMaxChars: parsePositiveIntEnv(env3[DIRECTORY_LIMIT_ENV.memfsTreeMaxChars], DIRECTORY_LIMIT_DEFAULTS.memfsTreeMaxChars, 128, 5000000),
44755
+ memfsTreeMaxChildrenPerDir: parsePositiveIntEnv(env3[DIRECTORY_LIMIT_ENV.memfsTreeMaxChildrenPerDir], DIRECTORY_LIMIT_DEFAULTS.memfsTreeMaxChildrenPerDir, 1, 5000),
44756
+ listDirMaxLimit: parsePositiveIntEnv(env3[DIRECTORY_LIMIT_ENV.listDirMaxLimit], DIRECTORY_LIMIT_DEFAULTS.listDirMaxLimit, 1, 1e4),
44757
+ listDirMaxDepth: parsePositiveIntEnv(env3[DIRECTORY_LIMIT_ENV.listDirMaxDepth], DIRECTORY_LIMIT_DEFAULTS.listDirMaxDepth, 1, 100),
44758
+ listDirMaxOffset: parsePositiveIntEnv(env3[DIRECTORY_LIMIT_ENV.listDirMaxOffset], DIRECTORY_LIMIT_DEFAULTS.listDirMaxOffset, 1, 1e6),
44759
+ listDirMaxCollectedEntries: parsePositiveIntEnv(env3[DIRECTORY_LIMIT_ENV.listDirMaxCollectedEntries], DIRECTORY_LIMIT_DEFAULTS.listDirMaxCollectedEntries, 10, 2000000),
44760
+ listDirMaxChildrenPerDir: parsePositiveIntEnv(env3[DIRECTORY_LIMIT_ENV.listDirMaxChildrenPerDir], DIRECTORY_LIMIT_DEFAULTS.listDirMaxChildrenPerDir, 1, 5000)
44761
+ };
44762
+ }
44763
+ var DIRECTORY_LIMIT_ENV, DIRECTORY_LIMIT_DEFAULTS;
44764
+ var init_directoryLimits = __esm(() => {
44765
+ DIRECTORY_LIMIT_ENV = {
44766
+ memfsTreeMaxLines: "LETTA_MEMFS_TREE_MAX_LINES",
44767
+ memfsTreeMaxChars: "LETTA_MEMFS_TREE_MAX_CHARS",
44768
+ memfsTreeMaxChildrenPerDir: "LETTA_MEMFS_TREE_MAX_CHILDREN_PER_DIR",
44769
+ listDirMaxLimit: "LETTA_LIST_DIR_MAX_LIMIT",
44770
+ listDirMaxDepth: "LETTA_LIST_DIR_MAX_DEPTH",
44771
+ listDirMaxOffset: "LETTA_LIST_DIR_MAX_OFFSET",
44772
+ listDirMaxCollectedEntries: "LETTA_LIST_DIR_MAX_COLLECTED_ENTRIES",
44773
+ listDirMaxChildrenPerDir: "LETTA_LIST_DIR_MAX_CHILDREN_PER_DIR"
44774
+ };
44775
+ DIRECTORY_LIMIT_DEFAULTS = {
44776
+ memfsTreeMaxLines: 500,
44777
+ memfsTreeMaxChars: 20000,
44778
+ memfsTreeMaxChildrenPerDir: 50,
44779
+ listDirMaxLimit: 200,
44780
+ listDirMaxDepth: 5,
44781
+ listDirMaxOffset: 1e4,
44782
+ listDirMaxCollectedEntries: 12000,
44783
+ listDirMaxChildrenPerDir: 50
44784
+ };
44785
+ });
44786
+
44715
44787
  // src/agent/memoryGit.ts
44716
44788
  var exports_memoryGit = {};
44717
44789
  __export(exports_memoryGit, {
@@ -45563,6 +45635,9 @@ __export(exports_memoryFilesystem, {
45563
45635
  ensureMemoryFilesystemDirs: () => ensureMemoryFilesystemDirs,
45564
45636
  enableMemfsIfCloud: () => enableMemfsIfCloud,
45565
45637
  applyMemfsFlags: () => applyMemfsFlags,
45638
+ MEMORY_TREE_MAX_LINES: () => MEMORY_TREE_MAX_LINES,
45639
+ MEMORY_TREE_MAX_CHILDREN_PER_DIR: () => MEMORY_TREE_MAX_CHILDREN_PER_DIR,
45640
+ MEMORY_TREE_MAX_CHARS: () => MEMORY_TREE_MAX_CHARS,
45566
45641
  MEMORY_SYSTEM_DIR: () => MEMORY_SYSTEM_DIR,
45567
45642
  MEMORY_FS_ROOT: () => MEMORY_FS_ROOT,
45568
45643
  MEMORY_FS_MEMORY_DIR: () => MEMORY_FS_MEMORY_DIR,
@@ -45591,7 +45666,7 @@ function labelFromRelativePath(relativePath) {
45591
45666
  const normalized = relativePath.replace(/\\/g, "/");
45592
45667
  return normalized.replace(/\.md$/, "");
45593
45668
  }
45594
- function renderMemoryFilesystemTree(systemLabels, detachedLabels) {
45669
+ function renderMemoryFilesystemTree(systemLabels, detachedLabels, options = {}) {
45595
45670
  const makeNode = () => ({ children: new Map, isFile: false });
45596
45671
  const root = makeNode();
45597
45672
  const insertPath = (base2, label) => {
@@ -45626,20 +45701,75 @@ function renderMemoryFilesystemTree(systemLabels, detachedLabels) {
45626
45701
  return nameA.localeCompare(nameB);
45627
45702
  });
45628
45703
  };
45629
- const lines = ["/memory/"];
45704
+ const limits = getDirectoryLimits();
45705
+ const maxLines = Math.max(2, options.maxLines ?? limits.memfsTreeMaxLines);
45706
+ const maxChars = Math.max(128, options.maxChars ?? limits.memfsTreeMaxChars);
45707
+ const maxChildrenPerDir = Math.max(1, options.maxChildrenPerDir ?? limits.memfsTreeMaxChildrenPerDir);
45708
+ const rootLine = "/memory/";
45709
+ const lines = [rootLine];
45710
+ let totalChars = rootLine.length;
45711
+ const countTreeEntries = (node) => {
45712
+ let total = 0;
45713
+ for (const [, child] of node.children) {
45714
+ total += 1;
45715
+ if (child.children.size > 0) {
45716
+ total += countTreeEntries(child);
45717
+ }
45718
+ }
45719
+ return total;
45720
+ };
45721
+ const canAppendLine = (line) => {
45722
+ const nextLineCount = lines.length + 1;
45723
+ const nextCharCount = totalChars + 1 + line.length;
45724
+ return nextLineCount <= maxLines && nextCharCount <= maxChars;
45725
+ };
45630
45726
  const render2 = (node, prefix) => {
45631
45727
  const entries = sortedEntries(node);
45632
- entries.forEach(([name, child], index) => {
45633
- const isLast = index === entries.length - 1;
45728
+ const visibleEntries = entries.slice(0, maxChildrenPerDir);
45729
+ const omittedEntries = Math.max(0, entries.length - visibleEntries.length);
45730
+ const renderItems = visibleEntries.map(([name, child]) => ({
45731
+ kind: "entry",
45732
+ name,
45733
+ child
45734
+ }));
45735
+ if (omittedEntries > 0) {
45736
+ renderItems.push({ kind: "omitted", omittedCount: omittedEntries });
45737
+ }
45738
+ for (const [index, item] of renderItems.entries()) {
45739
+ const isLast = index === renderItems.length - 1;
45634
45740
  const branch = isLast ? "└──" : "├──";
45635
- lines.push(`${prefix}${branch} ${name}${child.isFile ? "" : "/"}`);
45636
- if (child.children.size > 0) {
45741
+ const line = item.kind === "entry" ? `${prefix}${branch} ${item.name}${item.child.isFile ? "" : "/"}` : `${prefix}${branch} … (${item.omittedCount.toLocaleString()} more entries)`;
45742
+ if (!canAppendLine(line)) {
45743
+ return false;
45744
+ }
45745
+ lines.push(line);
45746
+ totalChars += 1 + line.length;
45747
+ if (item.kind === "entry" && item.child.children.size > 0) {
45637
45748
  const nextPrefix = `${prefix}${isLast ? " " : "│ "}`;
45638
- render2(child, nextPrefix);
45749
+ if (!render2(item.child, nextPrefix)) {
45750
+ return false;
45751
+ }
45639
45752
  }
45640
- });
45753
+ }
45754
+ return true;
45641
45755
  };
45642
- render2(root, "");
45756
+ const totalEntries = countTreeEntries(root);
45757
+ const fullyRendered = render2(root, "");
45758
+ if (!fullyRendered) {
45759
+ while (lines.length > 1) {
45760
+ const shownEntries = Math.max(0, lines.length - 1);
45761
+ const omittedEntries = Math.max(1, totalEntries - shownEntries);
45762
+ const notice = `[Tree truncated: showing ${shownEntries.toLocaleString()} of ${totalEntries.toLocaleString()} entries. ${omittedEntries.toLocaleString()} omitted.]`;
45763
+ if (canAppendLine(notice)) {
45764
+ lines.push(notice);
45765
+ break;
45766
+ }
45767
+ const removed = lines.pop();
45768
+ if (removed) {
45769
+ totalChars -= 1 + removed.length;
45770
+ }
45771
+ }
45772
+ }
45643
45773
  return lines.join(`
45644
45774
  `);
45645
45775
  }
@@ -45721,8 +45851,13 @@ async function enableMemfsIfCloud(agentId) {
45721
45851
  console.warn(`Warning: Could not enable memfs for new agent: ${error instanceof Error ? error.message : String(error)}`);
45722
45852
  }
45723
45853
  }
45724
- var MEMORY_FS_ROOT = ".letta", MEMORY_FS_AGENTS_DIR = "agents", MEMORY_FS_MEMORY_DIR = "memory", MEMORY_SYSTEM_DIR = "system";
45725
- var init_memoryFilesystem = () => {};
45854
+ var MEMORY_FS_ROOT = ".letta", MEMORY_FS_AGENTS_DIR = "agents", MEMORY_FS_MEMORY_DIR = "memory", MEMORY_SYSTEM_DIR = "system", MEMORY_TREE_MAX_LINES, MEMORY_TREE_MAX_CHARS, MEMORY_TREE_MAX_CHILDREN_PER_DIR;
45855
+ var init_memoryFilesystem = __esm(() => {
45856
+ init_directoryLimits();
45857
+ MEMORY_TREE_MAX_LINES = DIRECTORY_LIMIT_DEFAULTS.memfsTreeMaxLines;
45858
+ MEMORY_TREE_MAX_CHARS = DIRECTORY_LIMIT_DEFAULTS.memfsTreeMaxChars;
45859
+ MEMORY_TREE_MAX_CHILDREN_PER_DIR = DIRECTORY_LIMIT_DEFAULTS.memfsTreeMaxChildrenPerDir;
45860
+ });
45726
45861
 
45727
45862
  // src/tools/impl/shellEnv.ts
45728
45863
  var exports_shellEnv = {};
@@ -47153,32 +47288,47 @@ import { promises as fs10 } from "node:fs";
47153
47288
  import * as path10 from "node:path";
47154
47289
  async function list_dir(args) {
47155
47290
  validateRequiredParams(args, ["dir_path"], "list_dir");
47156
- const { dir_path, offset = 1, limit: limit2 = 25, depth = 2 } = args;
47291
+ const limits = getDirectoryLimits();
47292
+ const {
47293
+ dir_path,
47294
+ offset = DEFAULT_OFFSET,
47295
+ limit: limit2 = DEFAULT_LIMIT2,
47296
+ depth = DEFAULT_DEPTH
47297
+ } = args;
47157
47298
  const userCwd = process.env.USER_CWD || process.cwd();
47158
47299
  const resolvedPath = path10.isAbsolute(dir_path) ? dir_path : path10.resolve(userCwd, dir_path);
47159
- if (offset < 1) {
47160
- throw new Error("offset must be a 1-indexed entry number");
47300
+ if (!Number.isInteger(offset) || offset < 1) {
47301
+ throw new Error("offset must be a positive integer (1-indexed)");
47161
47302
  }
47162
- if (limit2 < 1) {
47163
- throw new Error("limit must be greater than zero");
47303
+ if (offset > limits.listDirMaxOffset) {
47304
+ throw new Error(`offset must be less than or equal to ${limits.listDirMaxOffset.toLocaleString()}`);
47305
+ }
47306
+ if (!Number.isInteger(limit2) || limit2 < 1) {
47307
+ throw new Error("limit must be a positive integer");
47164
47308
  }
47165
- if (depth < 1) {
47166
- throw new Error("depth must be greater than zero");
47309
+ if (!Number.isInteger(depth) || depth < 1) {
47310
+ throw new Error("depth must be a positive integer");
47167
47311
  }
47168
- const entries = await listDirSlice(resolvedPath, offset, limit2, depth);
47312
+ const effectiveLimit = Math.min(limit2, limits.listDirMaxLimit);
47313
+ const effectiveDepth = Math.min(depth, limits.listDirMaxDepth);
47314
+ const entries = await listDirSlice(resolvedPath, offset, effectiveLimit, effectiveDepth, limits.listDirMaxCollectedEntries, limits.listDirMaxChildrenPerDir);
47169
47315
  const output = [`Absolute path: ${resolvedPath}`, ...entries];
47316
+ if (effectiveLimit !== limit2 || effectiveDepth !== depth) {
47317
+ output.push(`[Request capped: limit=${limit2}->${effectiveLimit}, depth=${depth}->${effectiveDepth}]`);
47318
+ }
47170
47319
  return { content: output.join(`
47171
47320
  `) };
47172
47321
  }
47173
- async function listDirSlice(dirPath, offset, limit2, maxDepth) {
47322
+ async function listDirSlice(dirPath, offset, limit2, maxDepth, maxCollectedEntries, maxChildrenPerDir) {
47174
47323
  const entries = [];
47175
- await collectEntries(dirPath, "", maxDepth, entries);
47324
+ const maxEntriesToCollect = Math.min(offset + limit2, maxCollectedEntries);
47325
+ const { hitCollectionCap, hitFolderTruncation } = await collectEntries(dirPath, "", maxDepth, entries, maxEntriesToCollect, maxChildrenPerDir);
47176
47326
  if (entries.length === 0) {
47177
47327
  return [];
47178
47328
  }
47179
47329
  const startIndex = offset - 1;
47180
47330
  if (startIndex >= entries.length) {
47181
- throw new Error("offset exceeds directory entry count");
47331
+ throw new Error(`offset exceeds available entries in current view (max offset: ${entries.length.toLocaleString()})`);
47182
47332
  }
47183
47333
  const remainingEntries = entries.length - startIndex;
47184
47334
  const cappedLimit = Math.min(limit2, remainingEntries);
@@ -47190,15 +47340,21 @@ async function listDirSlice(dirPath, offset, limit2, maxDepth) {
47190
47340
  formatted.push(formatEntryLine(entry));
47191
47341
  }
47192
47342
  if (endIndex < entries.length) {
47193
- formatted.push(`More than ${cappedLimit} entries found`);
47343
+ formatted.push(`More entries available. Use offset=${endIndex + 1} to continue.`);
47344
+ } else if (hitCollectionCap || hitFolderTruncation) {
47345
+ formatted.push("More entries may exist beyond the current truncated view.");
47194
47346
  }
47195
47347
  return formatted;
47196
47348
  }
47197
- async function collectEntries(dirPath, relativePrefix, remainingDepth, entries) {
47349
+ async function collectEntries(dirPath, relativePrefix, remainingDepth, entries, maxEntriesToCollect, maxChildrenPerDir) {
47198
47350
  const queue = [
47199
47351
  { absPath: dirPath, prefix: relativePrefix, depth: remainingDepth }
47200
47352
  ];
47353
+ let hitFolderTruncation = false;
47201
47354
  while (queue.length > 0) {
47355
+ if (entries.length >= maxEntriesToCollect) {
47356
+ return { hitCollectionCap: true, hitFolderTruncation };
47357
+ }
47202
47358
  const current = queue.shift();
47203
47359
  if (!current)
47204
47360
  break;
@@ -47238,7 +47394,28 @@ async function collectEntries(dirPath, relativePrefix, remainingDepth, entries)
47238
47394
  throw new Error(`failed to read directory: ${err}`);
47239
47395
  }
47240
47396
  dirEntries.sort((a, b) => a.entry.name.localeCompare(b.entry.name));
47241
- for (const item of dirEntries) {
47397
+ const visibleEntries = dirEntries.slice(0, maxChildrenPerDir);
47398
+ const omittedEntries = Math.max(0, dirEntries.length - visibleEntries.length);
47399
+ if (omittedEntries > 0) {
47400
+ hitFolderTruncation = true;
47401
+ const omittedSortKey = formatEntryName(`${prefix ? `${prefix}/` : ""}￿-omitted`);
47402
+ const omittedDepth = prefix ? prefix.split(path10.sep).length : 0;
47403
+ visibleEntries.push({
47404
+ absPath,
47405
+ relativePath: prefix,
47406
+ kind: "omitted",
47407
+ entry: {
47408
+ name: omittedSortKey,
47409
+ displayName: `… (${omittedEntries.toLocaleString()} more entries)`,
47410
+ depth: omittedDepth,
47411
+ kind: "omitted"
47412
+ }
47413
+ });
47414
+ }
47415
+ for (const item of visibleEntries) {
47416
+ if (entries.length >= maxEntriesToCollect) {
47417
+ return { hitCollectionCap: true, hitFolderTruncation };
47418
+ }
47242
47419
  if (item.kind === "directory" && depth > 1) {
47243
47420
  queue.push({
47244
47421
  absPath: item.absPath,
@@ -47249,6 +47426,7 @@ async function collectEntries(dirPath, relativePrefix, remainingDepth, entries)
47249
47426
  entries.push(item.entry);
47250
47427
  }
47251
47428
  }
47429
+ return { hitCollectionCap: false, hitFolderTruncation };
47252
47430
  }
47253
47431
  function formatEntryName(filePath) {
47254
47432
  const normalized = filePath.replace(/\\/g, "/");
@@ -47276,13 +47454,17 @@ function formatEntryLine(entry) {
47276
47454
  case "other":
47277
47455
  name += "?";
47278
47456
  break;
47457
+ case "omitted":
47458
+ break;
47279
47459
  default:
47280
47460
  break;
47281
47461
  }
47282
47462
  return `${indent}${name}`;
47283
47463
  }
47284
- var MAX_ENTRY_LENGTH = 500, INDENTATION_SPACES = 2;
47285
- var init_ListDirCodex2 = () => {};
47464
+ var MAX_ENTRY_LENGTH = 500, INDENTATION_SPACES = 2, DEFAULT_OFFSET = 1, DEFAULT_LIMIT2 = 25, DEFAULT_DEPTH = 2;
47465
+ var init_ListDirCodex2 = __esm(() => {
47466
+ init_directoryLimits();
47467
+ });
47286
47468
 
47287
47469
  // src/tools/schemas/LS.json
47288
47470
  var LS_default2;
@@ -47403,7 +47585,15 @@ var init_ListDirectoryGemini2 = __esm(() => {
47403
47585
  // src/tools/impl/Memory.ts
47404
47586
  import { execFile as execFileCb2 } from "node:child_process";
47405
47587
  import { existsSync as existsSync13 } from "node:fs";
47406
- import { mkdir as mkdir2, readFile as readFile3, rename, unlink, writeFile as writeFile2 } from "node:fs/promises";
47588
+ import {
47589
+ mkdir as mkdir2,
47590
+ readFile as readFile3,
47591
+ rename,
47592
+ rm,
47593
+ stat as stat2,
47594
+ unlink,
47595
+ writeFile as writeFile2
47596
+ } from "node:fs/promises";
47407
47597
  import { homedir as homedir15 } from "node:os";
47408
47598
  import { dirname as dirname5, isAbsolute as isAbsolute8, relative as relative5, resolve as resolve12 } from "node:path";
47409
47599
  import { promisify as promisify9 } from "node:util";
@@ -47444,20 +47634,16 @@ async function memory(args) {
47444
47634
  if (command === "create") {
47445
47635
  const pathArg = requireString(args.path, "path", "create");
47446
47636
  const description = requireString(args.description, "description", "create");
47447
- const label = normalizeMemoryLabel(pathArg, "path");
47637
+ const label = normalizeMemoryLabel(memoryDir, pathArg, "path");
47448
47638
  const filePath = resolveMemoryFilePath(memoryDir, label);
47449
47639
  const relPath = toRepoRelative(memoryDir, filePath);
47450
47640
  if (existsSync13(filePath)) {
47451
47641
  throw new Error(`memory create: block already exists at ${pathArg}`);
47452
47642
  }
47453
- const limit2 = args.limit ?? DEFAULT_LIMIT2;
47454
- if (!Number.isInteger(limit2) || limit2 <= 0) {
47455
- throw new Error("memory create: 'limit' must be a positive integer");
47456
- }
47457
47643
  const body = args.file_text ?? "";
47458
47644
  const rendered = renderMemoryFile({
47459
47645
  description,
47460
- limit: limit2
47646
+ limit: DEFAULT_LIMIT3
47461
47647
  }, body);
47462
47648
  await mkdir2(dirname5(filePath), { recursive: true });
47463
47649
  await writeFile2(filePath, rendered, "utf8");
@@ -47466,7 +47652,7 @@ async function memory(args) {
47466
47652
  const pathArg = requireString(args.path, "path", "str_replace");
47467
47653
  const oldString = requireString(args.old_string, "old_string", "str_replace");
47468
47654
  const newString = requireString(args.new_string, "new_string", "str_replace");
47469
- const label = normalizeMemoryLabel(pathArg, "path");
47655
+ const label = normalizeMemoryLabel(memoryDir, pathArg, "path");
47470
47656
  const filePath = resolveMemoryFilePath(memoryDir, label);
47471
47657
  const relPath = toRepoRelative(memoryDir, filePath);
47472
47658
  const file = await loadEditableMemoryFile(filePath, pathArg);
@@ -47484,7 +47670,7 @@ async function memory(args) {
47484
47670
  if (typeof args.insert_line !== "number" || Number.isNaN(args.insert_line)) {
47485
47671
  throw new Error("memory insert: 'insert_line' must be a number");
47486
47672
  }
47487
- const label = normalizeMemoryLabel(pathArg, "path");
47673
+ const label = normalizeMemoryLabel(memoryDir, pathArg, "path");
47488
47674
  const filePath = resolveMemoryFilePath(memoryDir, label);
47489
47675
  const relPath = toRepoRelative(memoryDir, filePath);
47490
47676
  const file = await loadEditableMemoryFile(filePath, pathArg);
@@ -47502,44 +47688,48 @@ async function memory(args) {
47502
47688
  affectedPaths = [relPath];
47503
47689
  } else if (command === "delete") {
47504
47690
  const pathArg = requireString(args.path, "path", "delete");
47505
- const label = normalizeMemoryLabel(pathArg, "path");
47506
- const filePath = resolveMemoryFilePath(memoryDir, label);
47507
- const relPath = toRepoRelative(memoryDir, filePath);
47508
- await loadEditableMemoryFile(filePath, pathArg);
47509
- await unlink(filePath);
47510
- affectedPaths = [relPath];
47511
- } else if (command === "rename") {
47512
- const hasDescriptionUpdate = typeof args.path === "string" && args.path.trim().length > 0 && typeof args.description === "string" && args.description.trim().length > 0 && !args.old_path && !args.new_path;
47513
- if (hasDescriptionUpdate) {
47514
- const pathArg = requireString(args.path, "path", "rename");
47515
- const newDescription = requireString(args.description, "description", "rename description update");
47516
- const label = normalizeMemoryLabel(pathArg, "path");
47691
+ const label = normalizeMemoryLabel(memoryDir, pathArg, "path");
47692
+ const targetPath = resolveMemoryPath(memoryDir, label);
47693
+ if (existsSync13(targetPath) && (await stat2(targetPath)).isDirectory()) {
47694
+ const relPath = toRepoRelative(memoryDir, targetPath);
47695
+ await rm(targetPath, { recursive: true, force: false });
47696
+ affectedPaths = [relPath];
47697
+ } else {
47517
47698
  const filePath = resolveMemoryFilePath(memoryDir, label);
47518
47699
  const relPath = toRepoRelative(memoryDir, filePath);
47519
- const file = await loadEditableMemoryFile(filePath, pathArg);
47520
- const rendered = renderMemoryFile({
47521
- ...file.frontmatter,
47522
- description: newDescription
47523
- }, file.body);
47524
- await writeFile2(filePath, rendered, "utf8");
47700
+ await loadEditableMemoryFile(filePath, pathArg);
47701
+ await unlink(filePath);
47525
47702
  affectedPaths = [relPath];
47526
- } else {
47527
- const oldPathArg = requireString(args.old_path, "old_path", "rename");
47528
- const newPathArg = requireString(args.new_path, "new_path", "rename");
47529
- const oldLabel = normalizeMemoryLabel(oldPathArg, "old_path");
47530
- const newLabel = normalizeMemoryLabel(newPathArg, "new_path");
47531
- const oldFilePath = resolveMemoryFilePath(memoryDir, oldLabel);
47532
- const newFilePath = resolveMemoryFilePath(memoryDir, newLabel);
47533
- const oldRelPath = toRepoRelative(memoryDir, oldFilePath);
47534
- const newRelPath = toRepoRelative(memoryDir, newFilePath);
47535
- if (existsSync13(newFilePath)) {
47536
- throw new Error(`memory rename: destination already exists at ${newPathArg}`);
47537
- }
47538
- await loadEditableMemoryFile(oldFilePath, oldPathArg);
47539
- await mkdir2(dirname5(newFilePath), { recursive: true });
47540
- await rename(oldFilePath, newFilePath);
47541
- affectedPaths = [oldRelPath, newRelPath];
47542
47703
  }
47704
+ } else if (command === "rename") {
47705
+ const oldPathArg = requireString(args.old_path, "old_path", "rename");
47706
+ const newPathArg = requireString(args.new_path, "new_path", "rename");
47707
+ const oldLabel = normalizeMemoryLabel(memoryDir, oldPathArg, "old_path");
47708
+ const newLabel = normalizeMemoryLabel(memoryDir, newPathArg, "new_path");
47709
+ const oldFilePath = resolveMemoryFilePath(memoryDir, oldLabel);
47710
+ const newFilePath = resolveMemoryFilePath(memoryDir, newLabel);
47711
+ const oldRelPath = toRepoRelative(memoryDir, oldFilePath);
47712
+ const newRelPath = toRepoRelative(memoryDir, newFilePath);
47713
+ if (existsSync13(newFilePath)) {
47714
+ throw new Error(`memory rename: destination already exists at ${newPathArg}`);
47715
+ }
47716
+ await loadEditableMemoryFile(oldFilePath, oldPathArg);
47717
+ await mkdir2(dirname5(newFilePath), { recursive: true });
47718
+ await rename(oldFilePath, newFilePath);
47719
+ affectedPaths = [oldRelPath, newRelPath];
47720
+ } else if (command === "update_description") {
47721
+ const pathArg = requireString(args.path, "path", "update_description");
47722
+ const newDescription = requireString(args.description, "description", "update_description");
47723
+ const label = normalizeMemoryLabel(memoryDir, pathArg, "path");
47724
+ const filePath = resolveMemoryFilePath(memoryDir, label);
47725
+ const relPath = toRepoRelative(memoryDir, filePath);
47726
+ const file = await loadEditableMemoryFile(filePath, pathArg);
47727
+ const rendered = renderMemoryFile({
47728
+ ...file.frontmatter,
47729
+ description: newDescription
47730
+ }, file.body);
47731
+ await writeFile2(filePath, rendered, "utf8");
47732
+ affectedPaths = [relPath];
47543
47733
  } else {
47544
47734
  throw new Error(`Unsupported memory command: ${command}`);
47545
47735
  }
@@ -47583,18 +47773,31 @@ function ensureMemoryRepo(memoryDir) {
47583
47773
  throw new Error(`memory: ${memoryDir} is not a git repository. This tool requires a git-backed memory filesystem.`);
47584
47774
  }
47585
47775
  }
47586
- function normalizeMemoryLabel(inputPath, fieldName) {
47776
+ function normalizeMemoryLabel(memoryDir, inputPath, fieldName) {
47587
47777
  const raw = inputPath.trim();
47588
47778
  if (!raw) {
47589
47779
  throw new Error(`memory: '${fieldName}' must be a non-empty string`);
47590
47780
  }
47591
- const normalized = raw.replace(/\\/g, "/");
47592
- if (/^[a-zA-Z]:\//.test(normalized)) {
47593
- throw new Error(`memory: '${fieldName}' must be a memory-relative file path, not an absolute host path`);
47594
- }
47595
- if (normalized.startsWith("~/") || normalized.startsWith("$HOME/")) {
47781
+ if (raw.startsWith("~/") || raw.startsWith("$HOME/")) {
47596
47782
  throw new Error(`memory: '${fieldName}' must be a memory-relative file path, not a home-relative filesystem path`);
47597
47783
  }
47784
+ const isWindowsAbsolute = /^[a-zA-Z]:[\\/]/.test(raw);
47785
+ if (isAbsolute8(raw) || isWindowsAbsolute) {
47786
+ const absolutePath = resolve12(raw);
47787
+ const relToMemory = relative5(memoryDir, absolutePath);
47788
+ if (relToMemory && !relToMemory.startsWith("..") && !isAbsolute8(relToMemory)) {
47789
+ return normalizeRelativeMemoryLabel(relToMemory, fieldName);
47790
+ }
47791
+ throw new Error(memoryPrefixError(memoryDir));
47792
+ }
47793
+ return normalizeRelativeMemoryLabel(raw, fieldName);
47794
+ }
47795
+ function normalizeRelativeMemoryLabel(inputPath, fieldName) {
47796
+ const raw = inputPath.trim();
47797
+ if (!raw) {
47798
+ throw new Error(`memory: '${fieldName}' must be a non-empty string`);
47799
+ }
47800
+ const normalized = raw.replace(/\\/g, "/");
47598
47801
  if (normalized.startsWith("/")) {
47599
47802
  throw new Error(`memory: '${fieldName}' must be a relative path like system/contacts.md`);
47600
47803
  }
@@ -47618,8 +47821,15 @@ function normalizeMemoryLabel(inputPath, fieldName) {
47618
47821
  }
47619
47822
  return segments.join("/");
47620
47823
  }
47824
+ function memoryPrefixError(memoryDir) {
47825
+ return `The memory tool can only be used to modify files in {${memoryDir}} or provided as a relative path`;
47826
+ }
47621
47827
  function resolveMemoryFilePath(memoryDir, label) {
47622
- const absolute = resolve12(memoryDir, `${label}.md`);
47828
+ const absolute = resolveMemoryPath(memoryDir, `${label}.md`);
47829
+ return absolute;
47830
+ }
47831
+ function resolveMemoryPath(memoryDir, path11) {
47832
+ const absolute = resolve12(memoryDir, path11);
47623
47833
  const rel = relative5(memoryDir, absolute);
47624
47834
  if (rel.startsWith("..") || isAbsolute8(rel)) {
47625
47835
  throw new Error("memory: resolved path escapes memory directory");
@@ -47779,7 +47989,7 @@ function requireString(value, field, command) {
47779
47989
  }
47780
47990
  return value;
47781
47991
  }
47782
- var execFile9, DEFAULT_LIMIT2 = 2000;
47992
+ var execFile9, DEFAULT_LIMIT3 = 2000;
47783
47993
  var init_Memory2 = __esm(async () => {
47784
47994
  init_context();
47785
47995
  await init_client2();
@@ -61313,7 +61523,7 @@ __export(exports_skills, {
61313
61523
  GLOBAL_SKILLS_DIR: () => GLOBAL_SKILLS_DIR
61314
61524
  });
61315
61525
  import { existsSync as existsSync15 } from "node:fs";
61316
- import { readdir as readdir4, readFile as readFile4, realpath as realpath2, stat as stat2 } from "node:fs/promises";
61526
+ import { readdir as readdir4, readFile as readFile4, realpath as realpath2, stat as stat3 } from "node:fs/promises";
61317
61527
  import { dirname as dirname6, join as join18 } from "node:path";
61318
61528
  import { fileURLToPath as fileURLToPath7 } from "node:url";
61319
61529
  function getBundledSkillsPath() {
@@ -61410,7 +61620,7 @@ async function findSkillFiles(currentPath, rootPath, skills, errors, source, vis
61410
61620
  let isDirectory = entry.isDirectory();
61411
61621
  let isFile = entry.isFile();
61412
61622
  if (entry.isSymbolicLink()) {
61413
- const entryStat = await stat2(fullPath);
61623
+ const entryStat = await stat3(fullPath);
61414
61624
  isDirectory = entryStat.isDirectory();
61415
61625
  isFile = entryStat.isFile();
61416
61626
  }
@@ -63532,7 +63742,14 @@ var init_Memory3 = __esm(() => {
63532
63742
  properties: {
63533
63743
  command: {
63534
63744
  type: "string",
63535
- enum: ["str_replace", "insert", "delete", "rename", "create"],
63745
+ enum: [
63746
+ "str_replace",
63747
+ "insert",
63748
+ "delete",
63749
+ "rename",
63750
+ "update_description",
63751
+ "create"
63752
+ ],
63536
63753
  description: "Memory operation to perform"
63537
63754
  },
63538
63755
  reason: {
@@ -63541,15 +63758,15 @@ var init_Memory3 = __esm(() => {
63541
63758
  },
63542
63759
  path: {
63543
63760
  type: "string",
63544
- description: "Target memory file path relative to memory root (e.g. system/contacts.md)"
63761
+ description: "Target memory path (file or directory). Accepts relative paths like system/contacts.md or absolute paths under MEMORY_DIR"
63545
63762
  },
63546
63763
  old_path: {
63547
63764
  type: "string",
63548
- description: "Source memory file path for rename operations (e.g. system/temp.md)"
63765
+ description: "Source memory file path for rename operations (relative path or absolute path under MEMORY_DIR)"
63549
63766
  },
63550
63767
  new_path: {
63551
63768
  type: "string",
63552
- description: "Destination memory file path for rename operations (e.g. system/permanent.md)"
63769
+ description: "Destination memory file path for rename operations (relative path or absolute path under MEMORY_DIR)"
63553
63770
  },
63554
63771
  old_string: {
63555
63772
  type: "string",
@@ -63569,15 +63786,11 @@ var init_Memory3 = __esm(() => {
63569
63786
  },
63570
63787
  description: {
63571
63788
  type: "string",
63572
- description: "Block description (required for create, or used with rename + path to update description)"
63789
+ description: "Block description (required for create and update_description)"
63573
63790
  },
63574
63791
  file_text: {
63575
63792
  type: "string",
63576
63793
  description: "Initial block content for create"
63577
- },
63578
- limit: {
63579
- type: "number",
63580
- description: "Optional positive integer limit for create"
63581
63794
  }
63582
63795
  },
63583
63796
  required: ["command", "reason"],
@@ -69873,11 +70086,17 @@ function isTerminalKillCommand(value) {
69873
70086
  const c = value;
69874
70087
  return c.type === "terminal_kill" && typeof c.terminal_id === "string";
69875
70088
  }
70089
+ function isSearchFilesCommand(value) {
70090
+ if (!value || typeof value !== "object")
70091
+ return false;
70092
+ const c = value;
70093
+ return c.type === "search_files" && typeof c.query === "string" && typeof c.request_id === "string";
70094
+ }
69876
70095
  function parseServerMessage(data) {
69877
70096
  try {
69878
70097
  const raw = typeof data === "string" ? data : data.toString();
69879
70098
  const parsed = JSON.parse(raw);
69880
- if (isInputCommand(parsed) || isChangeDeviceStateCommand(parsed) || isAbortMessageCommand(parsed) || isSyncCommand(parsed) || isTerminalSpawnCommand(parsed) || isTerminalInputCommand(parsed) || isTerminalResizeCommand(parsed) || isTerminalKillCommand(parsed)) {
70099
+ if (isInputCommand(parsed) || isChangeDeviceStateCommand(parsed) || isAbortMessageCommand(parsed) || isSyncCommand(parsed) || isTerminalSpawnCommand(parsed) || isTerminalInputCommand(parsed) || isTerminalResizeCommand(parsed) || isTerminalKillCommand(parsed) || isSearchFilesCommand(parsed)) {
69881
70100
  return parsed;
69882
70101
  }
69883
70102
  const invalidInput = getInvalidInputReason(parsed);
@@ -76545,6 +76764,8 @@ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, conne
76545
76764
  });
76546
76765
  return;
76547
76766
  }
76767
+ setCurrentAgentId(agentId);
76768
+ setConversationId2(conversationId);
76548
76769
  if (isDebugEnabled()) {
76549
76770
  console.log(`[Listen] Handling message: agentId=${agentId}, requestedConversationId=${requestedConversationId}, conversationId=${conversationId}`);
76550
76771
  }
@@ -76974,6 +77195,7 @@ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, conne
76974
77195
  }
76975
77196
  var init_turn = __esm(async () => {
76976
77197
  init_check_approval();
77198
+ init_context();
76977
77199
  init_turn_recovery_policy();
76978
77200
  init_errorFormatter();
76979
77201
  init_listenContext();
@@ -76999,7 +77221,7 @@ var init_turn = __esm(async () => {
76999
77221
  });
77000
77222
 
77001
77223
  // src/websocket/listener/client.ts
77002
- import { realpath as realpath3, stat as stat3 } from "node:fs/promises";
77224
+ import { realpath as realpath3, stat as stat4 } from "node:fs/promises";
77003
77225
  import path22 from "node:path";
77004
77226
  import WebSocket4 from "ws";
77005
77227
  function handleModeChange(msg, socket, runtime, scope) {
@@ -77163,7 +77385,7 @@ async function handleCwdChange(msg, socket, runtime) {
77163
77385
  }
77164
77386
  const resolvedPath = path22.isAbsolute(requestedPath) ? requestedPath : path22.resolve(currentWorkingDirectory, requestedPath);
77165
77387
  const normalizedPath = await realpath3(resolvedPath);
77166
- const stats = await stat3(normalizedPath);
77388
+ const stats = await stat4(normalizedPath);
77167
77389
  if (!stats.isDirectory()) {
77168
77390
  throw new Error(`Not a directory: ${normalizedPath}`);
77169
77391
  }
@@ -77508,6 +77730,24 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
77508
77730
  scheduleQueuePump(scopedRuntime, socket, opts, processQueuedTurn);
77509
77731
  return;
77510
77732
  }
77733
+ if (isSearchFilesCommand(parsed)) {
77734
+ (async () => {
77735
+ await ensureFileIndex2();
77736
+ const files = searchFileIndex({
77737
+ searchDir: ".",
77738
+ pattern: parsed.query,
77739
+ deep: true,
77740
+ maxResults: parsed.max_results ?? 5
77741
+ });
77742
+ socket.send(JSON.stringify({
77743
+ type: "search_files_response",
77744
+ request_id: parsed.request_id,
77745
+ files,
77746
+ success: true
77747
+ }));
77748
+ })();
77749
+ return;
77750
+ }
77511
77751
  if (parsed.type === "terminal_spawn") {
77512
77752
  handleTerminalSpawn(parsed, socket, runtime.bootWorkingDirectory);
77513
77753
  return;
@@ -77741,6 +77981,7 @@ function createLegacyTestRuntime() {
77741
77981
  }
77742
77982
  var __listenClientTestUtils;
77743
77983
  var init_client4 = __esm(async () => {
77984
+ init_fileIndex();
77744
77985
  init_planName();
77745
77986
  init_constants();
77746
77987
  init_queueRuntime();
@@ -77855,7 +78096,7 @@ __export(exports_skills2, {
77855
78096
  GLOBAL_SKILLS_DIR: () => GLOBAL_SKILLS_DIR2
77856
78097
  });
77857
78098
  import { existsSync as existsSync20 } from "node:fs";
77858
- import { readdir as readdir6, readFile as readFile6, realpath as realpath4, stat as stat4 } from "node:fs/promises";
78099
+ import { readdir as readdir6, readFile as readFile6, realpath as realpath4, stat as stat5 } from "node:fs/promises";
77859
78100
  import { dirname as dirname10, join as join27 } from "node:path";
77860
78101
  import { fileURLToPath as fileURLToPath8 } from "node:url";
77861
78102
  function getBundledSkillsPath2() {
@@ -77952,7 +78193,7 @@ async function findSkillFiles2(currentPath, rootPath, skills, errors, source, vi
77952
78193
  let isDirectory = entry.isDirectory();
77953
78194
  let isFile = entry.isFile();
77954
78195
  if (entry.isSymbolicLink()) {
77955
- const entryStat = await stat4(fullPath);
78196
+ const entryStat = await stat5(fullPath);
77956
78197
  isDirectory = entryStat.isDirectory();
77957
78198
  isFile = entryStat.isFile();
77958
78199
  }
@@ -78389,7 +78630,7 @@ __export(exports_auto_update, {
78389
78630
  });
78390
78631
  import { execFile as execFile10 } from "node:child_process";
78391
78632
  import { realpathSync as realpathSync2 } from "node:fs";
78392
- import { readdir as readdir7, rm } from "node:fs/promises";
78633
+ import { readdir as readdir7, rm as rm2 } from "node:fs/promises";
78393
78634
  import { join as join28 } from "node:path";
78394
78635
  import { promisify as promisify10 } from "node:util";
78395
78636
  function debugLog3(...args) {
@@ -78557,7 +78798,7 @@ async function cleanupOrphanedDirs(globalPath) {
78557
78798
  if (entry.startsWith(".letta-code-")) {
78558
78799
  const orphanPath = join28(lettaAiDir, entry);
78559
78800
  debugLog3("Cleaning orphaned temp directory:", orphanPath);
78560
- await rm(orphanPath, { recursive: true, force: true });
78801
+ await rm2(orphanPath, { recursive: true, force: true });
78561
78802
  }
78562
78803
  }
78563
78804
  } catch {}
@@ -80477,7 +80718,6 @@ async function handleHeadlessCommand(parsedArgs, model, skillsDirectoryOverride,
80477
80718
  if (values.resume) {
80478
80719
  console.error(`Error: --resume is for interactive mode only (opens conversation selector).
80479
80720
  In headless mode, use:
80480
- --continue Resume the last session (agent + conversation)
80481
80721
  --conversation <id> Resume a specific conversation by ID`);
80482
80722
  process.exit(1);
80483
80723
  }
@@ -80487,7 +80727,6 @@ In headless mode, use:
80487
80727
  let specifiedAgentId = values.agent;
80488
80728
  const specifiedAgentName = values.name;
80489
80729
  let specifiedConversationId = values.conversation;
80490
- const shouldContinue = values.continue;
80491
80730
  const forceNew = values["new-agent"];
80492
80731
  const systemPromptPreset = values.system;
80493
80732
  const systemCustom = values["system-custom"];
@@ -80580,10 +80819,6 @@ In headless mode, use:
80580
80819
  console.error("Error: --from-agent requires --agent <id> or --conversation <id>.");
80581
80820
  process.exit(1);
80582
80821
  }
80583
- if (shouldContinue) {
80584
- console.error("Error: --from-agent cannot be used with --continue");
80585
- process.exit(1);
80586
- }
80587
80822
  if (forceNew) {
80588
80823
  console.error("Error: --from-agent cannot be used with --new-agent");
80589
80824
  process.exit(1);
@@ -80611,20 +80846,12 @@ In headless mode, use:
80611
80846
  {
80612
80847
  when: fromAfFile,
80613
80848
  message: "--conversation cannot be used with --import"
80614
- },
80615
- {
80616
- when: shouldContinue,
80617
- message: "--conversation cannot be used with --continue"
80618
80849
  }
80619
80850
  ]
80620
80851
  });
80621
80852
  validateFlagConflicts2({
80622
80853
  guard: forceNewConversation,
80623
80854
  checks: [
80624
- {
80625
- when: shouldContinue,
80626
- message: "--new cannot be used with --continue"
80627
- },
80628
80855
  {
80629
80856
  when: specifiedConversationId,
80630
80857
  message: "--new cannot be used with --conversation"
@@ -80649,10 +80876,6 @@ In headless mode, use:
80649
80876
  when: specifiedAgentName,
80650
80877
  message: "--import cannot be used with --name"
80651
80878
  },
80652
- {
80653
- when: shouldContinue,
80654
- message: "--import cannot be used with --continue"
80655
- },
80656
80879
  {
80657
80880
  when: forceNew,
80658
80881
  message: "--import cannot be used with --new-agent"
@@ -80827,11 +81050,6 @@ In headless mode, use:
80827
81050
  } catch (_error) {}
80828
81051
  }
80829
81052
  }
80830
- if (!agent && shouldContinue) {
80831
- console.error("No recent session found in .letta/ or ~/.letta.");
80832
- console.error("Run 'letta' to get started.");
80833
- process.exit(1);
80834
- }
80835
81053
  if (!agent) {
80836
81054
  const { ensureDefaultAgents: ensureDefaultAgents2 } = await init_defaults().then(() => exports_defaults);
80837
81055
  const defaultAgent = await ensureDefaultAgents2(client, {
@@ -80846,7 +81064,7 @@ In headless mode, use:
80846
81064
  process.exit(1);
80847
81065
  }
80848
81066
  markMilestone("HEADLESS_AGENT_RESOLVED");
80849
- const isResumingAgent = !!(specifiedAgentId || shouldContinue || !forceNew && !fromAfFile);
81067
+ const isResumingAgent = !!(specifiedAgentId || !forceNew && !fromAfFile);
80850
81068
  if (isResumingAgent) {
80851
81069
  if (model) {
80852
81070
  const modelHandle = resolveModel2(model);
@@ -80972,28 +81190,6 @@ In headless mode, use:
80972
81190
  process.exit(1);
80973
81191
  }
80974
81192
  }
80975
- } else if (shouldContinue) {
80976
- await settingsManager.loadLocalProjectSettings();
80977
- const lastSession = settingsManager.getLocalLastSession(process.cwd()) ?? settingsManager.getGlobalLastSession();
80978
- if (lastSession && lastSession.agentId === agent.id) {
80979
- if (lastSession.conversationId === "default") {
80980
- conversationId = "default";
80981
- } else {
80982
- try {
80983
- debugLog("conversations", `retrieve(${lastSession.conversationId}) [headless lastSession resume]`);
80984
- await client.conversations.retrieve(lastSession.conversationId);
80985
- conversationId = lastSession.conversationId;
80986
- } catch {
80987
- console.error(`Attempting to resume conversation ${lastSession.conversationId}, but conversation was not found.`);
80988
- console.error("Resume the default conversation with 'letta -p ...', view recent conversations with 'letta --resume', or start a new conversation with 'letta -p ... --new'.");
80989
- process.exit(1);
80990
- }
80991
- }
80992
- } else {
80993
- console.error("No previous session found for this agent to resume.");
80994
- console.error("Resume the default conversation with 'letta -p ...', or start a new conversation with 'letta -p ... --new'.");
80995
- process.exit(1);
80996
- }
80997
81193
  } else if (forceNewConversation) {
80998
81194
  const conversation = await client.conversations.create({
80999
81195
  agent_id: agent.id,
@@ -103516,7 +103712,9 @@ function ConversationSelector2({
103516
103712
  const result = await client.conversations.list({
103517
103713
  agent_id: agentId,
103518
103714
  limit: FETCH_PAGE_SIZE3,
103519
- ...afterCursor && { after: afterCursor }
103715
+ ...afterCursor && { after: afterCursor },
103716
+ order: "desc",
103717
+ order_by: "last_run_completion"
103520
103718
  });
103521
103719
  const enrichedConversations = await Promise.all(result.map(async (conv) => {
103522
103720
  try {
@@ -103632,20 +103830,6 @@ function ConversationSelector2({
103632
103830
  children: "⎿ "
103633
103831
  }, undefined, false, undefined, this);
103634
103832
  const indent = " ";
103635
- if (conv.summary) {
103636
- return /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Box_default, {
103637
- flexDirection: "row",
103638
- marginLeft: 2,
103639
- children: [
103640
- bracket,
103641
- /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text2, {
103642
- dimColor: true,
103643
- italic: true,
103644
- children: conv.summary.length > 57 ? `${conv.summary.slice(0, 54)}...` : conv.summary
103645
- }, undefined, false, undefined, this)
103646
- ]
103647
- }, undefined, true, undefined, this);
103648
- }
103649
103833
  if (previewLines.length > 0) {
103650
103834
  return /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(jsx_dev_runtime34.Fragment, {
103651
103835
  children: previewLines.map((line, idx) => /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Box_default, {
@@ -103718,9 +103902,9 @@ function ConversationSelector2({
103718
103902
  /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text2, {
103719
103903
  bold: isSelected,
103720
103904
  color: isSelected ? colors.selector.itemHighlighted : undefined,
103721
- children: isDefault ? "default" : conv.id
103905
+ children: conv.summary ? `${conv.summary.length > 40 ? `${conv.summary.slice(0, 37)}...` : conv.summary} (${conv.id})` : isDefault ? "default" : conv.id
103722
103906
  }, undefined, false, undefined, this),
103723
- isDefault && /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text2, {
103907
+ !conv.summary && isDefault && /* @__PURE__ */ jsx_dev_runtime34.jsxDEV(Text2, {
103724
103908
  dimColor: true,
103725
103909
  children: " (agent's default conversation)"
103726
103910
  }, undefined, false, undefined, this),
@@ -116374,7 +116558,7 @@ function MessageSearch({
116374
116558
  const [searchInput, setSearchInput] = import_react79.useState(initialQuery ?? "");
116375
116559
  const [activeQuery, setActiveQuery] = import_react79.useState(initialQuery ?? "");
116376
116560
  const [searchMode, setSearchMode] = import_react79.useState("hybrid");
116377
- const [searchRange, setSearchRange] = import_react79.useState("all");
116561
+ const [searchRange, setSearchRange] = import_react79.useState("agent");
116378
116562
  const [results, setResults] = import_react79.useState([]);
116379
116563
  const [loading, setLoading] = import_react79.useState(false);
116380
116564
  const [error, setError] = import_react79.useState(null);
@@ -129917,8 +130101,10 @@ Type your task to begin the loop.`, true);
129917
130101
  }
129918
130102
  return { submitted: true };
129919
130103
  }
129920
- if (msg.trim() === "/new") {
129921
- const cmd = commandRunner.start(msg.trim(), "Starting new conversation...");
130104
+ const newMatch = msg.trim().match(/^\/new(?:\s+(.+))?$/);
130105
+ if (newMatch) {
130106
+ const conversationName = newMatch[1]?.trim();
130107
+ const cmd = commandRunner.start(msg.trim(), conversationName ? `Starting new conversation: ${conversationName}...` : "Starting new conversation...");
129922
130108
  resetPendingReasoningCycle();
129923
130109
  setCommandRunning(true);
129924
130110
  await runEndHooks();
@@ -129926,8 +130112,12 @@ Type your task to begin the loop.`, true);
129926
130112
  const client = await getClient2();
129927
130113
  const conversation = await client.conversations.create({
129928
130114
  agent_id: agentId,
129929
- isolated_block_labels: [...ISOLATED_BLOCK_LABELS]
130115
+ isolated_block_labels: [...ISOLATED_BLOCK_LABELS],
130116
+ ...conversationName && { summary: conversationName }
129930
130117
  });
130118
+ if (conversationName) {
130119
+ hasSetConversationSummaryRef.current = true;
130120
+ }
129931
130121
  await maybeCarryOverActiveConversationModel(conversation.id);
129932
130122
  setConversationId3(conversation.id);
129933
130123
  pendingConversationSwitchRef.current = {
@@ -133700,6 +133890,9 @@ If using apply_patch, use this exact relative patch path: ${applyPatchRelativePa
133700
133890
  summary: selectorContext?.summary,
133701
133891
  messageHistory: resumeData.messageHistory
133702
133892
  };
133893
+ if (selectorContext?.summary) {
133894
+ hasSetConversationSummaryRef.current = true;
133895
+ }
133703
133896
  settingsManager.setLocalLastSession({ agentId, conversationId: convId }, process.cwd());
133704
133897
  settingsManager.setGlobalLastSession({
133705
133898
  agentId,
@@ -134702,7 +134895,7 @@ var init_release_notes = __esm(async () => {
134702
134895
  → Read more: https://docs.letta.com/letta-code/changelog#0134`,
134703
134896
  "0.13.0": `\uD83C\uDF81 **Letta Code 0.13.0: Introducing Conversations!**
134704
134897
  → Letta Code now starts a new conversation on each startup (memory is shared across all conversations)
134705
- → Use **/resume** to switch conversations, or run **letta --continue** to continue an existing conversation
134898
+ → Use **/resume** to switch conversations, or run **letta --conv <id>** to continue a specific conversation
134706
134899
  → Read more: https://docs.letta.com/letta-code/changelog#0130`
134707
134900
  };
134708
134901
  });
@@ -135252,6 +135445,9 @@ __export(exports_memoryFilesystem2, {
135252
135445
  ensureMemoryFilesystemDirs: () => ensureMemoryFilesystemDirs2,
135253
135446
  enableMemfsIfCloud: () => enableMemfsIfCloud2,
135254
135447
  applyMemfsFlags: () => applyMemfsFlags2,
135448
+ MEMORY_TREE_MAX_LINES: () => MEMORY_TREE_MAX_LINES2,
135449
+ MEMORY_TREE_MAX_CHILDREN_PER_DIR: () => MEMORY_TREE_MAX_CHILDREN_PER_DIR2,
135450
+ MEMORY_TREE_MAX_CHARS: () => MEMORY_TREE_MAX_CHARS2,
135255
135451
  MEMORY_SYSTEM_DIR: () => MEMORY_SYSTEM_DIR2,
135256
135452
  MEMORY_FS_ROOT: () => MEMORY_FS_ROOT2,
135257
135453
  MEMORY_FS_MEMORY_DIR: () => MEMORY_FS_MEMORY_DIR2,
@@ -135280,7 +135476,7 @@ function labelFromRelativePath2(relativePath) {
135280
135476
  const normalized = relativePath.replace(/\\/g, "/");
135281
135477
  return normalized.replace(/\.md$/, "");
135282
135478
  }
135283
- function renderMemoryFilesystemTree2(systemLabels, detachedLabels) {
135479
+ function renderMemoryFilesystemTree2(systemLabels, detachedLabels, options = {}) {
135284
135480
  const makeNode = () => ({ children: new Map, isFile: false });
135285
135481
  const root = makeNode();
135286
135482
  const insertPath = (base2, label) => {
@@ -135315,20 +135511,75 @@ function renderMemoryFilesystemTree2(systemLabels, detachedLabels) {
135315
135511
  return nameA.localeCompare(nameB);
135316
135512
  });
135317
135513
  };
135318
- const lines = ["/memory/"];
135514
+ const limits = getDirectoryLimits();
135515
+ const maxLines = Math.max(2, options.maxLines ?? limits.memfsTreeMaxLines);
135516
+ const maxChars = Math.max(128, options.maxChars ?? limits.memfsTreeMaxChars);
135517
+ const maxChildrenPerDir = Math.max(1, options.maxChildrenPerDir ?? limits.memfsTreeMaxChildrenPerDir);
135518
+ const rootLine = "/memory/";
135519
+ const lines = [rootLine];
135520
+ let totalChars = rootLine.length;
135521
+ const countTreeEntries = (node) => {
135522
+ let total = 0;
135523
+ for (const [, child] of node.children) {
135524
+ total += 1;
135525
+ if (child.children.size > 0) {
135526
+ total += countTreeEntries(child);
135527
+ }
135528
+ }
135529
+ return total;
135530
+ };
135531
+ const canAppendLine = (line) => {
135532
+ const nextLineCount = lines.length + 1;
135533
+ const nextCharCount = totalChars + 1 + line.length;
135534
+ return nextLineCount <= maxLines && nextCharCount <= maxChars;
135535
+ };
135319
135536
  const render2 = (node, prefix) => {
135320
135537
  const entries = sortedEntries(node);
135321
- entries.forEach(([name, child], index) => {
135322
- const isLast = index === entries.length - 1;
135538
+ const visibleEntries = entries.slice(0, maxChildrenPerDir);
135539
+ const omittedEntries = Math.max(0, entries.length - visibleEntries.length);
135540
+ const renderItems = visibleEntries.map(([name, child]) => ({
135541
+ kind: "entry",
135542
+ name,
135543
+ child
135544
+ }));
135545
+ if (omittedEntries > 0) {
135546
+ renderItems.push({ kind: "omitted", omittedCount: omittedEntries });
135547
+ }
135548
+ for (const [index, item] of renderItems.entries()) {
135549
+ const isLast = index === renderItems.length - 1;
135323
135550
  const branch = isLast ? "└──" : "├──";
135324
- lines.push(`${prefix}${branch} ${name}${child.isFile ? "" : "/"}`);
135325
- if (child.children.size > 0) {
135551
+ const line = item.kind === "entry" ? `${prefix}${branch} ${item.name}${item.child.isFile ? "" : "/"}` : `${prefix}${branch} … (${item.omittedCount.toLocaleString()} more entries)`;
135552
+ if (!canAppendLine(line)) {
135553
+ return false;
135554
+ }
135555
+ lines.push(line);
135556
+ totalChars += 1 + line.length;
135557
+ if (item.kind === "entry" && item.child.children.size > 0) {
135326
135558
  const nextPrefix = `${prefix}${isLast ? " " : "│ "}`;
135327
- render2(child, nextPrefix);
135559
+ if (!render2(item.child, nextPrefix)) {
135560
+ return false;
135561
+ }
135328
135562
  }
135329
- });
135563
+ }
135564
+ return true;
135330
135565
  };
135331
- render2(root, "");
135566
+ const totalEntries = countTreeEntries(root);
135567
+ const fullyRendered = render2(root, "");
135568
+ if (!fullyRendered) {
135569
+ while (lines.length > 1) {
135570
+ const shownEntries = Math.max(0, lines.length - 1);
135571
+ const omittedEntries = Math.max(1, totalEntries - shownEntries);
135572
+ const notice = `[Tree truncated: showing ${shownEntries.toLocaleString()} of ${totalEntries.toLocaleString()} entries. ${omittedEntries.toLocaleString()} omitted.]`;
135573
+ if (canAppendLine(notice)) {
135574
+ lines.push(notice);
135575
+ break;
135576
+ }
135577
+ const removed = lines.pop();
135578
+ if (removed) {
135579
+ totalChars -= 1 + removed.length;
135580
+ }
135581
+ }
135582
+ }
135332
135583
  return lines.join(`
135333
135584
  `);
135334
135585
  }
@@ -135410,8 +135661,13 @@ async function enableMemfsIfCloud2(agentId) {
135410
135661
  console.warn(`Warning: Could not enable memfs for new agent: ${error instanceof Error ? error.message : String(error)}`);
135411
135662
  }
135412
135663
  }
135413
- var MEMORY_FS_ROOT2 = ".letta", MEMORY_FS_AGENTS_DIR2 = "agents", MEMORY_FS_MEMORY_DIR2 = "memory", MEMORY_SYSTEM_DIR2 = "system";
135414
- var init_memoryFilesystem2 = () => {};
135664
+ var MEMORY_FS_ROOT2 = ".letta", MEMORY_FS_AGENTS_DIR2 = "agents", MEMORY_FS_MEMORY_DIR2 = "memory", MEMORY_SYSTEM_DIR2 = "system", MEMORY_TREE_MAX_LINES2, MEMORY_TREE_MAX_CHARS2, MEMORY_TREE_MAX_CHILDREN_PER_DIR2;
135665
+ var init_memoryFilesystem2 = __esm(() => {
135666
+ init_directoryLimits();
135667
+ MEMORY_TREE_MAX_LINES2 = DIRECTORY_LIMIT_DEFAULTS.memfsTreeMaxLines;
135668
+ MEMORY_TREE_MAX_CHARS2 = DIRECTORY_LIMIT_DEFAULTS.memfsTreeMaxChars;
135669
+ MEMORY_TREE_MAX_CHILDREN_PER_DIR2 = DIRECTORY_LIMIT_DEFAULTS.memfsTreeMaxChildrenPerDir;
135670
+ });
135415
135671
 
135416
135672
  // node_modules/@letta-ai/letta-client/core/error.mjs
135417
135673
  class LettaError extends Error {
@@ -136224,13 +136480,6 @@ var CLI_FLAG_CATALOG = {
136224
136480
  mode: "both",
136225
136481
  help: { description: "Show current directory, skills, and pinned agents" }
136226
136482
  },
136227
- continue: {
136228
- parser: { type: "boolean", short: "c" },
136229
- mode: "both",
136230
- help: {
136231
- description: "Resume last session (agent + conversation) directly"
136232
- }
136233
- },
136234
136483
  resume: {
136235
136484
  parser: { type: "boolean", short: "r" },
136236
136485
  mode: "interactive",
@@ -136662,7 +136911,9 @@ function ConversationSelector({
136662
136911
  const result = await client.conversations.list({
136663
136912
  agent_id: agentId,
136664
136913
  limit: FETCH_PAGE_SIZE,
136665
- ...afterCursor && { after: afterCursor }
136914
+ ...afterCursor && { after: afterCursor },
136915
+ order: "desc",
136916
+ order_by: "last_run_completion"
136666
136917
  });
136667
136918
  const enrichedConversations = await Promise.all(result.map(async (conv) => {
136668
136919
  try {
@@ -136778,20 +137029,6 @@ function ConversationSelector({
136778
137029
  children: "⎿ "
136779
137030
  }, undefined, false, undefined, this);
136780
137031
  const indent = " ";
136781
- if (conv.summary) {
136782
- return /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
136783
- flexDirection: "row",
136784
- marginLeft: 2,
136785
- children: [
136786
- bracket,
136787
- /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text2, {
136788
- dimColor: true,
136789
- italic: true,
136790
- children: conv.summary.length > 57 ? `${conv.summary.slice(0, 54)}...` : conv.summary
136791
- }, undefined, false, undefined, this)
136792
- ]
136793
- }, undefined, true, undefined, this);
136794
- }
136795
137032
  if (previewLines.length > 0) {
136796
137033
  return /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(jsx_dev_runtime4.Fragment, {
136797
137034
  children: previewLines.map((line, idx) => /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
@@ -136864,9 +137101,9 @@ function ConversationSelector({
136864
137101
  /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text2, {
136865
137102
  bold: isSelected,
136866
137103
  color: isSelected ? colors.selector.itemHighlighted : undefined,
136867
- children: isDefault ? "default" : conv.id
137104
+ children: conv.summary ? `${conv.summary.length > 40 ? `${conv.summary.slice(0, 37)}...` : conv.summary} (${conv.id})` : isDefault ? "default" : conv.id
136868
137105
  }, undefined, false, undefined, this),
136869
- isDefault && /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text2, {
137106
+ !conv.summary && isDefault && /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text2, {
136870
137107
  dimColor: true,
136871
137108
  children: " (agent's default conversation)"
136872
137109
  }, undefined, false, undefined, this),
@@ -139426,8 +139663,8 @@ async function listBackups(agentId) {
139426
139663
  const path23 = join23(agentRoot, entry.name);
139427
139664
  let createdAt = null;
139428
139665
  try {
139429
- const stat4 = statSync5(path23);
139430
- createdAt = stat4.mtime.toISOString();
139666
+ const stat5 = statSync5(path23);
139667
+ createdAt = stat5.mtime.toISOString();
139431
139668
  } catch {
139432
139669
  createdAt = null;
139433
139670
  }
@@ -139535,8 +139772,8 @@ async function runMemfsSubcommand(argv) {
139535
139772
  console.error(`Backup not found: ${backupPath}`);
139536
139773
  return 1;
139537
139774
  }
139538
- const stat4 = statSync5(backupPath);
139539
- if (!stat4.isDirectory()) {
139775
+ const stat5 = statSync5(backupPath);
139776
+ if (!stat5.isDirectory()) {
139540
139777
  console.error(`Backup path is not a directory: ${backupPath}`);
139541
139778
  return 1;
139542
139779
  }
@@ -139558,8 +139795,8 @@ async function runMemfsSubcommand(argv) {
139558
139795
  return 1;
139559
139796
  }
139560
139797
  if (existsSync18(out)) {
139561
- const stat4 = statSync5(out);
139562
- if (stat4.isDirectory()) {
139798
+ const stat5 = statSync5(out);
139799
+ if (stat5.isDirectory()) {
139563
139800
  const contents = await readdir5(out);
139564
139801
  if (contents.length > 0) {
139565
139802
  console.error(`Export directory not empty: ${out}`);
@@ -141763,9 +142000,8 @@ Letta Code is a general purpose CLI for interacting with Letta agents
141763
142000
 
141764
142001
  USAGE
141765
142002
  # interactive TUI
141766
- letta Resume default conversation (OG single-threaded experience)
142003
+ letta Resume last conversation for this project
141767
142004
  letta --new Create a new conversation (for concurrent sessions)
141768
- letta --continue Resume last session (agent + conversation) directly
141769
142005
  letta --resume Open agent selector UI to pick agent/conversation
141770
142006
  letta --new-agent Create a new agent directly (skip profile selector)
141771
142007
  letta --agent <id> Open a specific agent by ID
@@ -142034,7 +142270,6 @@ Note: Flags should use double dashes for full names (e.g., --yolo, not -yolo)`);
142034
142270
  console.log(result.message);
142035
142271
  process.exit(result.success ? 0 : 1);
142036
142272
  }
142037
- const shouldContinue = values.continue ?? false;
142038
142273
  const shouldResume = values.resume ?? false;
142039
142274
  let specifiedConversationId = values.conversation ?? null;
142040
142275
  const forceNew = values["new-agent"] ?? false;
@@ -142169,20 +142404,12 @@ Note: Flags should use double dashes for full names (e.g., --yolo, not -yolo)`);
142169
142404
  {
142170
142405
  when: shouldResume,
142171
142406
  message: "--conversation cannot be used with --resume"
142172
- },
142173
- {
142174
- when: shouldContinue,
142175
- message: "--conversation cannot be used with --continue"
142176
142407
  }
142177
142408
  ]
142178
142409
  });
142179
142410
  validateFlagConflicts({
142180
142411
  guard: forceNewConversation,
142181
142412
  checks: [
142182
- {
142183
- when: shouldContinue,
142184
- message: "--new cannot be used with --continue"
142185
- },
142186
142413
  {
142187
142414
  when: specifiedConversationId,
142188
142415
  message: "--new cannot be used with --conversation"
@@ -142373,7 +142600,6 @@ Error: ${message}`);
142373
142600
  const AppModule = await init_App2().then(() => exports_App);
142374
142601
  const App3 = AppModule.default;
142375
142602
  function LoadingApp({
142376
- continueSession,
142377
142603
  forceNew: forceNew2,
142378
142604
  initBlocks: initBlocks2,
142379
142605
  baseTools: baseTools2,
@@ -142527,8 +142753,8 @@ Error: ${message}`);
142527
142753
  }
142528
142754
  }
142529
142755
  if (shouldResume) {
142530
- const localSession = settingsManager2.getLocalLastSession(process.cwd());
142531
- const localAgentId2 = localSession?.agentId ?? localSettings.lastAgent;
142756
+ const localSession2 = settingsManager2.getLocalLastSession(process.cwd());
142757
+ const localAgentId2 = localSession2?.agentId ?? localSettings.lastAgent;
142532
142758
  if (localAgentId2) {
142533
142759
  try {
142534
142760
  const agent = await client.agents.retrieve(localAgentId2);
@@ -142555,41 +142781,6 @@ Error: ${message}`);
142555
142781
  console.error("Run 'letta' to get started.");
142556
142782
  process.exit(1);
142557
142783
  }
142558
- if (continueSession) {
142559
- const localSession = settingsManager2.getLocalLastSession(process.cwd());
142560
- const localAgentId2 = localSession?.agentId ?? localSettings.lastAgent;
142561
- if (localAgentId2) {
142562
- try {
142563
- await client.agents.retrieve(localAgentId2);
142564
- setSelectedGlobalAgentId(localAgentId2);
142565
- if (localSession?.conversationId) {
142566
- setSelectedConversationId(localSession.conversationId);
142567
- }
142568
- setLoadingState("assembling");
142569
- return;
142570
- } catch {
142571
- setFailedAgentMessage(`Unable to locate agent ${localAgentId2} in .letta/, checking global (~/.letta)`);
142572
- }
142573
- } else {
142574
- console.log("No recent agent in .letta/, using global (~/.letta)");
142575
- }
142576
- const globalSession = settingsManager2.getGlobalLastSession();
142577
- const globalAgentId2 = globalSession?.agentId;
142578
- if (globalAgentId2) {
142579
- try {
142580
- await client.agents.retrieve(globalAgentId2);
142581
- setSelectedGlobalAgentId(globalAgentId2);
142582
- if (globalSession?.conversationId) {
142583
- setSelectedConversationId(globalSession.conversationId);
142584
- }
142585
- setLoadingState("assembling");
142586
- return;
142587
- } catch {}
142588
- }
142589
- console.error("No recent session found in .letta/ or ~/.letta.");
142590
- console.error("Run 'letta' to get started.");
142591
- process.exit(1);
142592
- }
142593
142784
  if (forceNew2 || agentIdArg || fromAfFile2) {
142594
142785
  setLoadingState("assembling");
142595
142786
  return;
@@ -142617,9 +142808,10 @@ Error: ${message}`);
142617
142808
  }
142618
142809
  const mergedPinned = settingsManager2.getMergedPinnedAgents(process.cwd());
142619
142810
  const { resolveStartupTarget: resolveStartupTarget2 } = await Promise.resolve().then(() => exports_resolve_startup_agent);
142811
+ const localSession = settingsManager2.getLocalLastSession(process.cwd());
142620
142812
  const target = resolveStartupTarget2({
142621
142813
  localAgentId,
142622
- localConversationId: null,
142814
+ localConversationId: localSession?.conversationId ?? null,
142623
142815
  localAgentExists,
142624
142816
  globalAgentId,
142625
142817
  globalAgentExists,
@@ -142633,6 +142825,9 @@ Error: ${message}`);
142633
142825
  if (cachedAgent && cachedAgent.id === target.agentId) {
142634
142826
  setValidatedAgent(cachedAgent);
142635
142827
  }
142828
+ if (target.conversationId && !forceNewConversation) {
142829
+ setSelectedConversationId(target.conversationId);
142830
+ }
142636
142831
  setLoadingState("assembling");
142637
142832
  return;
142638
142833
  case "select":
@@ -142663,7 +142858,6 @@ Error: ${message}`);
142663
142858
  forceNew2,
142664
142859
  agentIdArg,
142665
142860
  fromAfFile2,
142666
- continueSession,
142667
142861
  shouldResume,
142668
142862
  specifiedConversationId
142669
142863
  ]);
@@ -142703,15 +142897,6 @@ Error: ${message}`);
142703
142897
  return;
142704
142898
  }
142705
142899
  }
142706
- if (!resumingAgentId && continueSession && settings.lastAgent) {
142707
- try {
142708
- await client.agents.retrieve(settings.lastAgent);
142709
- resumingAgentId = settings.lastAgent;
142710
- } catch {
142711
- setLoadingState("selecting_global");
142712
- return;
142713
- }
142714
- }
142715
142900
  }
142716
142901
  setIsResumingSession(!!resumingAgentId);
142717
142902
  const modelForTools = getModelForToolLoading(model, toolset);
@@ -142817,15 +143002,6 @@ Error: ${message}`);
142817
143002
  return;
142818
143003
  }
142819
143004
  }
142820
- if (!agent && continueSession && settings.lastAgent) {
142821
- try {
142822
- agent = await client.agents.retrieve(settings.lastAgent);
142823
- } catch (error) {
142824
- console.error(`Previous agent ${settings.lastAgent} not found (error: ${JSON.stringify(error)})`);
142825
- setLoadingState("selecting_global");
142826
- return;
142827
- }
142828
- }
142829
143005
  if (!agent) {
142830
143006
  console.error("No agent found. Use --new-agent to create a new agent.");
142831
143007
  process.exit(1);
@@ -142846,7 +143022,7 @@ Error: ${message}`);
142846
143022
  }));
142847
143023
  const isResumingProject = !shouldCreateNew && !!resumingAgentId;
142848
143024
  const isReusingExistingAgent = !shouldCreateNew && !fromAfFile2 && agent && agent.id;
142849
- const resuming = !!(continueSession || agentIdArg || isResumingProject || isReusingExistingAgent);
143025
+ const resuming = !!(agentIdArg || isResumingProject || isReusingExistingAgent);
142850
143026
  setIsResumingSession(resuming);
142851
143027
  if (resuming) {
142852
143028
  if (model) {
@@ -142895,7 +143071,6 @@ Error: ${message}`);
142895
143071
  });
142896
143072
  let conversationIdToUse;
142897
143073
  if (isDebugEnabled2()) {
142898
- debugLog2("startup", "shouldContinue=%o", shouldContinue);
142899
143074
  debugLog2("startup", "shouldResume=%o", shouldResume);
142900
143075
  debugLog2("startup", "specifiedConversationId=%s", specifiedConversationId);
142901
143076
  }
@@ -142913,34 +143088,6 @@ Error: ${message}`);
142913
143088
  }
142914
143089
  throw error;
142915
143090
  }
142916
- } else if (shouldContinue) {
142917
- const lastSession = settingsManager2.getLocalLastSession(process.cwd()) ?? settingsManager2.getGlobalLastSession();
142918
- if (isDebugEnabled2()) {
142919
- debugLog2("startup", "lastSession=%s", JSON.stringify(lastSession));
142920
- debugLog2("startup", "agent.id=%s", agent.id);
142921
- }
142922
- let resumedSuccessfully = false;
142923
- if (lastSession && lastSession.agentId === agent.id) {
142924
- try {
142925
- setLoadingState("checking");
142926
- const data = await getResumeData(client, agent, lastSession.conversationId);
142927
- conversationIdToUse = lastSession.conversationId;
142928
- setResumedExistingConversation(true);
142929
- setResumeData(data);
142930
- resumedSuccessfully = true;
142931
- } catch (error) {
142932
- if (error instanceof APIError && (error.status === 404 || error.status === 422)) {
142933
- console.warn(`Previous conversation ${lastSession.conversationId} not found, creating new`);
142934
- } else {
142935
- throw error;
142936
- }
142937
- }
142938
- }
142939
- if (!resumedSuccessfully) {
142940
- console.error(`Attempting to resume conversation ${lastSession?.conversationId ?? "(unknown)"}, but conversation was not found.`);
142941
- console.error("Resume the default conversation with 'letta', view recent conversations with 'letta --resume', or start a new conversation with 'letta --new'.");
142942
- process.exit(1);
142943
- }
142944
143091
  } else if (selectedConversationId) {
142945
143092
  try {
142946
143093
  setLoadingState("checking");
@@ -142950,10 +143097,15 @@ Error: ${message}`);
142950
143097
  setResumeData(data);
142951
143098
  } catch (error) {
142952
143099
  if (error instanceof APIError && (error.status === 404 || error.status === 422)) {
142953
- console.error(`Conversation ${selectedConversationId} not found`);
142954
- process.exit(1);
143100
+ console.warn(`Previous conversation ${selectedConversationId} not found, falling back to default`);
143101
+ conversationIdToUse = "default";
143102
+ setLoadingState("checking");
143103
+ const data = await getResumeData(client, agent, "default");
143104
+ setResumeData(data);
143105
+ setResumedExistingConversation(data.messageHistory.length > 0);
143106
+ } else {
143107
+ throw error;
142955
143108
  }
142956
- throw error;
142957
143109
  }
142958
143110
  } else if (forceNewConversation) {
142959
143111
  const conversation = await client.conversations.create({
@@ -143024,7 +143176,6 @@ Error during initialization: ${message}`);
143024
143176
  process.exit(1);
143025
143177
  });
143026
143178
  }, [
143027
- continueSession,
143028
143179
  forceNew2,
143029
143180
  userRequestedNewAgent,
143030
143181
  agentIdArg,
@@ -143034,7 +143185,6 @@ Error during initialization: ${message}`);
143034
143185
  loadingState,
143035
143186
  selectedGlobalAgentId,
143036
143187
  validatedAgent,
143037
- shouldContinue,
143038
143188
  resumeAgentId,
143039
143189
  selectedConversationId
143040
143190
  ]);
@@ -143135,7 +143285,6 @@ Error during initialization: ${message}`);
143135
143285
  }
143136
143286
  markMilestone2("REACT_RENDER_START");
143137
143287
  render2(React14.createElement(LoadingApp, {
143138
- continueSession: shouldContinue,
143139
143288
  forceNew,
143140
143289
  initBlocks,
143141
143290
  baseTools,
@@ -143152,4 +143301,4 @@ Error during initialization: ${message}`);
143152
143301
  }
143153
143302
  main();
143154
143303
 
143155
- //# debugId=D1C8E311510A3C4964756E2164756E21
143304
+ //# debugId=370E7E3EE35E2CB664756E2164756E21