@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 +947 -798
- package/package.json +3 -3
- package/scripts/latency-benchmark.ts +0 -2
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.
|
|
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.
|
|
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": "
|
|
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
|
|
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:
|
|
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/
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
45633
|
-
|
|
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
|
-
|
|
45636
|
-
if (
|
|
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
|
-
|
|
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
|
|
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
|
|
47300
|
+
if (!Number.isInteger(offset) || offset < 1) {
|
|
47301
|
+
throw new Error("offset must be a positive integer (1-indexed)");
|
|
47161
47302
|
}
|
|
47162
|
-
if (
|
|
47163
|
-
throw new Error(
|
|
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
|
|
47309
|
+
if (!Number.isInteger(depth) || depth < 1) {
|
|
47310
|
+
throw new Error("depth must be a positive integer");
|
|
47167
47311
|
}
|
|
47168
|
-
const
|
|
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
|
-
|
|
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(
|
|
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
|
|
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
|
-
|
|
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 {
|
|
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:
|
|
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
|
|
47507
|
-
|
|
47508
|
-
|
|
47509
|
-
|
|
47510
|
-
|
|
47511
|
-
|
|
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
|
-
|
|
47520
|
-
|
|
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
|
-
|
|
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 =
|
|
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,
|
|
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
|
|
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
|
|
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: [
|
|
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
|
|
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 (
|
|
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 (
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 ||
|
|
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("
|
|
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
|
-
|
|
129921
|
-
|
|
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 --
|
|
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
|
|
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.
|
|
135322
|
-
|
|
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
|
-
|
|
135325
|
-
if (
|
|
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
|
-
|
|
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
|
|
139430
|
-
createdAt =
|
|
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
|
|
139539
|
-
if (!
|
|
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
|
|
139562
|
-
if (
|
|
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
|
|
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
|
|
142531
|
-
const localAgentId2 =
|
|
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 = !!(
|
|
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.
|
|
142954
|
-
|
|
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=
|
|
143304
|
+
//# debugId=370E7E3EE35E2CB664756E2164756E21
|