@letta-ai/letta-code 0.24.3 → 0.24.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/letta.js
CHANGED
|
@@ -3269,7 +3269,7 @@ var package_default;
|
|
|
3269
3269
|
var init_package = __esm(() => {
|
|
3270
3270
|
package_default = {
|
|
3271
3271
|
name: "@letta-ai/letta-code",
|
|
3272
|
-
version: "0.24.
|
|
3272
|
+
version: "0.24.5",
|
|
3273
3273
|
description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
|
|
3274
3274
|
type: "module",
|
|
3275
3275
|
bin: {
|
|
@@ -4721,6 +4721,7 @@ var init_timing = __esm(() => {
|
|
|
4721
4721
|
// src/agent/client.ts
|
|
4722
4722
|
var exports_client = {};
|
|
4723
4723
|
__export(exports_client, {
|
|
4724
|
+
isDesktopLocalProxyUrl: () => isDesktopLocalProxyUrl,
|
|
4724
4725
|
getServerUrl: () => getServerUrl,
|
|
4725
4726
|
getMemfsServerUrl: () => getMemfsServerUrl,
|
|
4726
4727
|
getClient: () => getClient,
|
|
@@ -4774,12 +4775,33 @@ function getServerUrl() {
|
|
|
4774
4775
|
const settings = settingsManager.getSettings();
|
|
4775
4776
|
return process.env.LETTA_BASE_URL || settings.env?.LETTA_BASE_URL || LETTA_CLOUD_API_URL;
|
|
4776
4777
|
}
|
|
4778
|
+
function isLocalhostUrl(value) {
|
|
4779
|
+
if (!value)
|
|
4780
|
+
return false;
|
|
4781
|
+
try {
|
|
4782
|
+
const parsed = new URL(value);
|
|
4783
|
+
return ["localhost", "127.0.0.1", "::1", "[::1]"].includes(parsed.hostname);
|
|
4784
|
+
} catch {
|
|
4785
|
+
return false;
|
|
4786
|
+
}
|
|
4787
|
+
}
|
|
4788
|
+
function isDesktopLocalProxyUrl(value) {
|
|
4789
|
+
return process.env.LETTA_DESKTOP_DEBUG_PANEL === "1" && isLocalhostUrl(value);
|
|
4790
|
+
}
|
|
4777
4791
|
function getMemfsServerUrl() {
|
|
4778
4792
|
let settings = null;
|
|
4779
4793
|
try {
|
|
4780
4794
|
settings = settingsManager.getSettings();
|
|
4781
4795
|
} catch {}
|
|
4782
|
-
|
|
4796
|
+
const configuredMemfsUrl = process.env.LETTA_MEMFS_BASE_URL || settings?.env?.LETTA_MEMFS_BASE_URL;
|
|
4797
|
+
if (configuredMemfsUrl) {
|
|
4798
|
+
return configuredMemfsUrl;
|
|
4799
|
+
}
|
|
4800
|
+
const apiUrl = process.env.LETTA_BASE_URL || settings?.env?.LETTA_BASE_URL;
|
|
4801
|
+
if (apiUrl && isDesktopLocalProxyUrl(apiUrl)) {
|
|
4802
|
+
return apiUrl;
|
|
4803
|
+
}
|
|
4804
|
+
return LETTA_CLOUD_API_URL;
|
|
4783
4805
|
}
|
|
4784
4806
|
async function getClient() {
|
|
4785
4807
|
if (_testClientOverride) {
|
|
@@ -7128,6 +7150,13 @@ If worktree creation fails (locked index), retry up to 3 times with backoff (sle
|
|
|
7128
7150
|
### 2. Read existing memory
|
|
7129
7151
|
Read the memory files in your worktree, to understand what already exists in the memory filesystem.
|
|
7130
7152
|
|
|
7153
|
+
Before adding or expanding \`system/\` memory, measure its current token footprint:
|
|
7154
|
+
\`\`\`bash
|
|
7155
|
+
letta memory tokens --format json --quiet --memory-dir "$WORKTREE_DIR/$BRANCH_NAME"
|
|
7156
|
+
\`\`\`
|
|
7157
|
+
|
|
7158
|
+
This command is memory-mode safe. Treat it as measurement only: use the reported \`total_tokens\` and per-file breakdown to decide whether new findings belong in \`system/\` or external memory. Do not use custom token-counting scripts, \`npx\`, \`awk\`, or \`find -exec wc\` for this.
|
|
7159
|
+
|
|
7131
7160
|
### 3. Read and analyze history
|
|
7132
7161
|
|
|
7133
7162
|
Your prompt will specify a pre-split JSONL chunk file and its source format. Use these patterns to read it:
|
|
@@ -7765,6 +7794,11 @@ You can create, delete, or modify files — including their contents, names, and
|
|
|
7765
7794
|
- Why did the agent make the mistakes it did? What was missing from context?
|
|
7766
7795
|
- Why did the user have to make corrections?
|
|
7767
7796
|
- Does anything in memory contradict the observed conversation history, or need updating?
|
|
7797
|
+
- Check the current size of \`system/\` before adding or expanding in-context memory:
|
|
7798
|
+
\`\`\`bash
|
|
7799
|
+
letta memory tokens --format json --quiet
|
|
7800
|
+
\`\`\`
|
|
7801
|
+
This command is memory-mode safe. Treat it as measurement only: decide whether a size is concerning based on the actual context and the value of the proposed change. Prefer adding durable detail to external memory unless the information is important enough to stay in-context.
|
|
7768
7802
|
|
|
7769
7803
|
### Step 4: Update memory files (if needed)
|
|
7770
7804
|
|
|
@@ -9669,6 +9703,17 @@ var init_models2 = __esm(() => {
|
|
|
9669
9703
|
parallel_tool_calls: true
|
|
9670
9704
|
}
|
|
9671
9705
|
},
|
|
9706
|
+
{
|
|
9707
|
+
id: "deepseek-v4-pro",
|
|
9708
|
+
handle: "openrouter/deepseek/deepseek-v4-pro",
|
|
9709
|
+
label: "DeepSeek V4 Pro",
|
|
9710
|
+
description: "DeepSeek's V4 Pro model",
|
|
9711
|
+
updateArgs: {
|
|
9712
|
+
context_window: 1048576,
|
|
9713
|
+
max_output_tokens: 384000,
|
|
9714
|
+
parallel_tool_calls: true
|
|
9715
|
+
}
|
|
9716
|
+
},
|
|
9672
9717
|
{
|
|
9673
9718
|
id: "glm-5.1",
|
|
9674
9719
|
handle: "zai/glm-5.1",
|
|
@@ -38513,6 +38558,8 @@ __export(exports_memoryGit, {
|
|
|
38513
38558
|
ensureLocalMemfsGitConfig: () => ensureLocalMemfsGitConfig,
|
|
38514
38559
|
commitAndSyncMemoryWrite: () => commitAndSyncMemoryWrite,
|
|
38515
38560
|
cloneMemoryRepo: () => cloneMemoryRepo,
|
|
38561
|
+
buildNonInteractiveGitEnv: () => buildNonInteractiveGitEnv,
|
|
38562
|
+
buildGitAuthArgs: () => buildGitAuthArgs,
|
|
38516
38563
|
assertMemoryRepoReadyForWrite: () => assertMemoryRepoReadyForWrite,
|
|
38517
38564
|
addGitMemoryTag: () => addGitMemoryTag,
|
|
38518
38565
|
PRE_COMMIT_HOOK_SCRIPT: () => PRE_COMMIT_HOOK_SCRIPT,
|
|
@@ -38622,11 +38669,27 @@ async function getAuthToken() {
|
|
|
38622
38669
|
const client = await getClient();
|
|
38623
38670
|
return client._options?.apiKey ?? "";
|
|
38624
38671
|
}
|
|
38625
|
-
|
|
38626
|
-
|
|
38672
|
+
function buildGitAuthArgs(token) {
|
|
38673
|
+
return [
|
|
38674
|
+
"-c",
|
|
38675
|
+
"credential.helper=",
|
|
38676
|
+
"-c",
|
|
38677
|
+
"core.askPass=",
|
|
38627
38678
|
"-c",
|
|
38628
38679
|
`http.extraHeader=Authorization: Basic ${Buffer.from(`letta:${token}`).toString("base64")}`
|
|
38629
|
-
]
|
|
38680
|
+
];
|
|
38681
|
+
}
|
|
38682
|
+
function buildNonInteractiveGitEnv(env3 = process.env) {
|
|
38683
|
+
return {
|
|
38684
|
+
...env3,
|
|
38685
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
38686
|
+
GCM_INTERACTIVE: "never",
|
|
38687
|
+
GIT_ASKPASS: "",
|
|
38688
|
+
SSH_ASKPASS: ""
|
|
38689
|
+
};
|
|
38690
|
+
}
|
|
38691
|
+
async function runGit(cwd2, args, token) {
|
|
38692
|
+
const authArgs = token ? buildGitAuthArgs(token) : [];
|
|
38630
38693
|
const allArgs = [...authArgs, ...args];
|
|
38631
38694
|
let loggableArgs = args;
|
|
38632
38695
|
if (args[0] === "config" && typeof args[1] === "string" && args[1].includes("credential") && args[1].includes(".helper")) {
|
|
@@ -38637,6 +38700,7 @@ async function runGit(cwd2, args, token) {
|
|
|
38637
38700
|
debugLog("memfs-git", `git ${loggableArgs.join(" ")} (in ${cwd2})`);
|
|
38638
38701
|
const result = await execFile("git", allArgs, {
|
|
38639
38702
|
cwd: cwd2,
|
|
38703
|
+
env: buildNonInteractiveGitEnv(),
|
|
38640
38704
|
maxBuffer: 10 * 1024 * 1024,
|
|
38641
38705
|
timeout: 60000
|
|
38642
38706
|
});
|
|
@@ -47319,13 +47383,25 @@ function pushUnique(list, seen, entry) {
|
|
|
47319
47383
|
seen.add(key);
|
|
47320
47384
|
list.push(entry);
|
|
47321
47385
|
}
|
|
47386
|
+
function normalizePowerShellCommand(command) {
|
|
47387
|
+
const trimmed = command.trim();
|
|
47388
|
+
if (trimmed.startsWith("&") || trimmed.startsWith('"') || trimmed.startsWith("'")) {
|
|
47389
|
+
return trimmed.startsWith("&") ? trimmed : `& ${trimmed}`;
|
|
47390
|
+
}
|
|
47391
|
+
return trimmed;
|
|
47392
|
+
}
|
|
47393
|
+
function buildPowerShellCommand(command) {
|
|
47394
|
+
const powerShellCommand = normalizePowerShellCommand(command);
|
|
47395
|
+
const aliasPrelude = POWERSHELL_ENV_ALIASES.map((name) => `$${name} = $env:${name}`).join("; ");
|
|
47396
|
+
return `${aliasPrelude}; ${powerShellCommand}`;
|
|
47397
|
+
}
|
|
47322
47398
|
function windowsLaunchers(command) {
|
|
47323
47399
|
const trimmed = command.trim();
|
|
47324
47400
|
if (!trimmed)
|
|
47325
47401
|
return [];
|
|
47326
47402
|
const launchers = [];
|
|
47327
47403
|
const seen = new Set;
|
|
47328
|
-
const powerShellCommand =
|
|
47404
|
+
const powerShellCommand = buildPowerShellCommand(trimmed);
|
|
47329
47405
|
pushUnique(launchers, seen, [
|
|
47330
47406
|
"powershell.exe",
|
|
47331
47407
|
"-NoProfile",
|
|
@@ -47407,7 +47483,19 @@ function buildShellLaunchers(command, options) {
|
|
|
47407
47483
|
const login = options?.login ?? false;
|
|
47408
47484
|
return process.platform === "win32" ? windowsLaunchers(command) : unixLaunchers(command, login);
|
|
47409
47485
|
}
|
|
47410
|
-
var SEP2 = "\x00";
|
|
47486
|
+
var SEP2 = "\x00", POWERSHELL_ENV_ALIASES;
|
|
47487
|
+
var init_shellLaunchers = __esm(() => {
|
|
47488
|
+
POWERSHELL_ENV_ALIASES = [
|
|
47489
|
+
"MEMORY_DIR",
|
|
47490
|
+
"LETTA_MEMORY_DIR",
|
|
47491
|
+
"AGENT_ID",
|
|
47492
|
+
"LETTA_AGENT_ID",
|
|
47493
|
+
"LETTA_PARENT_AGENT_ID",
|
|
47494
|
+
"CONVERSATION_ID",
|
|
47495
|
+
"LETTA_CONVERSATION_ID",
|
|
47496
|
+
"USER_CWD"
|
|
47497
|
+
];
|
|
47498
|
+
});
|
|
47411
47499
|
|
|
47412
47500
|
// src/hooks/types.ts
|
|
47413
47501
|
function supportsPromptHooks(event) {
|
|
@@ -47863,6 +47951,7 @@ async function executeHooksParallel(hooks, input, workingDirectory = process.cwd
|
|
|
47863
47951
|
}
|
|
47864
47952
|
var DEFAULT_TIMEOUT_MS = 60000;
|
|
47865
47953
|
var init_executor = __esm(() => {
|
|
47954
|
+
init_shellLaunchers();
|
|
47866
47955
|
init_prompt_executor();
|
|
47867
47956
|
init_types2();
|
|
47868
47957
|
});
|
|
@@ -48895,6 +48984,7 @@ __export(exports_memoryFilesystem, {
|
|
|
48895
48984
|
renderMemoryFilesystemTree: () => renderMemoryFilesystemTree,
|
|
48896
48985
|
labelFromRelativePath: () => labelFromRelativePath,
|
|
48897
48986
|
isMemfsEnabledOnServer: () => isMemfsEnabledOnServer,
|
|
48987
|
+
isLettaMemfsServer: () => isLettaMemfsServer,
|
|
48898
48988
|
isLettaCloud: () => isLettaCloud,
|
|
48899
48989
|
getMemorySystemDir: () => getMemorySystemDir,
|
|
48900
48990
|
getMemoryFilesystemRoot: () => getMemoryFilesystemRoot,
|
|
@@ -49082,8 +49172,8 @@ function renderMemoryFilesystemTree(systemLabels, detachedLabels, options = {})
|
|
|
49082
49172
|
}
|
|
49083
49173
|
async function applyMemfsFlags(agentId, memfsFlag, noMemfsFlag, options) {
|
|
49084
49174
|
const { settingsManager: settingsManager2 } = await Promise.resolve().then(() => (init_settings_manager(), exports_settings_manager));
|
|
49085
|
-
if (memfsFlag && !await
|
|
49086
|
-
throw new Error(
|
|
49175
|
+
if (memfsFlag && !await isLettaMemfsServer()) {
|
|
49176
|
+
throw new Error(await getMemfsSyncUnavailableMessage());
|
|
49087
49177
|
}
|
|
49088
49178
|
const hasExplicitToggle = Boolean(memfsFlag || noMemfsFlag);
|
|
49089
49179
|
const localMemfsEnabled = settingsManager2.isMemfsEnabled(agentId);
|
|
@@ -49154,6 +49244,24 @@ async function isLettaCloud() {
|
|
|
49154
49244
|
const serverUrl = getServerUrl2();
|
|
49155
49245
|
return serverUrl.includes("api.letta.com") || process.env.LETTA_MEMFS_LOCAL === "1" || process.env.LETTA_API_KEY === "local-desktop";
|
|
49156
49246
|
}
|
|
49247
|
+
function getServerHostLabel(serverUrl) {
|
|
49248
|
+
const trimmed = serverUrl.trim();
|
|
49249
|
+
try {
|
|
49250
|
+
return new URL(trimmed).host || trimmed;
|
|
49251
|
+
} catch {
|
|
49252
|
+
return trimmed.replace(/^https?:\/\//, "").replace(/\/+$/, "") || trimmed;
|
|
49253
|
+
}
|
|
49254
|
+
}
|
|
49255
|
+
async function isLettaMemfsServer() {
|
|
49256
|
+
const { getMemfsServerUrl: getMemfsServerUrl2, isDesktopLocalProxyUrl: isDesktopLocalProxyUrl2 } = await Promise.resolve().then(() => (init_client2(), exports_client));
|
|
49257
|
+
const memfsServerUrl = getMemfsServerUrl2();
|
|
49258
|
+
return memfsServerUrl.includes("api.letta.com") || isDesktopLocalProxyUrl2(memfsServerUrl) || process.env.LETTA_MEMFS_LOCAL === "1" || process.env.LETTA_API_KEY === "local-desktop";
|
|
49259
|
+
}
|
|
49260
|
+
async function getMemfsSyncUnavailableMessage() {
|
|
49261
|
+
const { getMemfsServerUrl: getMemfsServerUrl2 } = await Promise.resolve().then(() => (init_client2(), exports_client));
|
|
49262
|
+
const memfsServerUrl = getMemfsServerUrl2();
|
|
49263
|
+
return `MemFS sync failed (expected api.letta.com, got ${getServerHostLabel(memfsServerUrl)})`;
|
|
49264
|
+
}
|
|
49157
49265
|
async function enableMemfsIfCloud(agentId) {
|
|
49158
49266
|
if (!await isLettaCloud())
|
|
49159
49267
|
return;
|
|
@@ -54922,13 +55030,19 @@ function validateWorktreePath(command, cwd2) {
|
|
|
54922
55030
|
}
|
|
54923
55031
|
return null;
|
|
54924
55032
|
}
|
|
55033
|
+
function rebuildCachedLauncher(command) {
|
|
55034
|
+
if (!cachedWorkingLauncher)
|
|
55035
|
+
return null;
|
|
55036
|
+
const cachedExecutable = cachedWorkingLauncher[0]?.toLowerCase();
|
|
55037
|
+
if (!cachedExecutable)
|
|
55038
|
+
return null;
|
|
55039
|
+
const launchers = buildShellLaunchers(command);
|
|
55040
|
+
return launchers.find((launcher) => launcher[0]?.toLowerCase() === cachedExecutable) ?? null;
|
|
55041
|
+
}
|
|
54925
55042
|
function getBackgroundLauncher(command) {
|
|
54926
|
-
|
|
54927
|
-
|
|
54928
|
-
|
|
54929
|
-
return [executable, ...launcherArgs.slice(0, -1), command];
|
|
54930
|
-
}
|
|
54931
|
-
}
|
|
55043
|
+
const cachedLauncher = rebuildCachedLauncher(command);
|
|
55044
|
+
if (cachedLauncher)
|
|
55045
|
+
return cachedLauncher;
|
|
54932
55046
|
const launchers = buildShellLaunchers(command);
|
|
54933
55047
|
return launchers[0] || [];
|
|
54934
55048
|
}
|
|
@@ -54944,9 +55058,8 @@ async function spawnCommand(command, options) {
|
|
|
54944
55058
|
});
|
|
54945
55059
|
}
|
|
54946
55060
|
if (cachedWorkingLauncher) {
|
|
54947
|
-
const
|
|
54948
|
-
if (
|
|
54949
|
-
const newLauncher = [executable, ...launcherArgs.slice(0, -1), command];
|
|
55061
|
+
const newLauncher = rebuildCachedLauncher(command);
|
|
55062
|
+
if (newLauncher) {
|
|
54950
55063
|
try {
|
|
54951
55064
|
const result = await spawnWithLauncher(newLauncher, {
|
|
54952
55065
|
cwd: options.cwd,
|
|
@@ -55195,6 +55308,7 @@ var init_Bash2 = __esm(() => {
|
|
|
55195
55308
|
init_worktree_ownership();
|
|
55196
55309
|
init_process_manager();
|
|
55197
55310
|
init_shellEnv();
|
|
55311
|
+
init_shellLaunchers();
|
|
55198
55312
|
init_shellRunner();
|
|
55199
55313
|
init_truncation();
|
|
55200
55314
|
});
|
|
@@ -71382,6 +71496,7 @@ var DEFAULT_TIMEOUT = 120000;
|
|
|
71382
71496
|
var init_Shell2 = __esm(() => {
|
|
71383
71497
|
init_runtime_context();
|
|
71384
71498
|
init_shellEnv();
|
|
71499
|
+
init_shellLaunchers();
|
|
71385
71500
|
init_shellRunner();
|
|
71386
71501
|
});
|
|
71387
71502
|
|
|
@@ -71527,6 +71642,7 @@ var init_ShellCommand2 = __esm(() => {
|
|
|
71527
71642
|
init_context();
|
|
71528
71643
|
init_readOnlyShell();
|
|
71529
71644
|
init_Shell2();
|
|
71645
|
+
init_shellLaunchers();
|
|
71530
71646
|
init_shellRunner();
|
|
71531
71647
|
init_truncation();
|
|
71532
71648
|
});
|
|
@@ -87563,6 +87679,60 @@ class StreamProcessor {
|
|
|
87563
87679
|
}
|
|
87564
87680
|
|
|
87565
87681
|
// src/cli/helpers/stream.ts
|
|
87682
|
+
function summarizeStreamForDebug(stream2) {
|
|
87683
|
+
if (!stream2 || typeof stream2 !== "object") {
|
|
87684
|
+
return `type=${typeof stream2}`;
|
|
87685
|
+
}
|
|
87686
|
+
const record = stream2;
|
|
87687
|
+
const ctor = stream2.constructor?.name;
|
|
87688
|
+
const controller = record.controller && typeof record.controller === "object" ? record.controller : null;
|
|
87689
|
+
const keys = Object.keys(record).slice(0, 8);
|
|
87690
|
+
return [
|
|
87691
|
+
`ctor=${ctor ?? "unknown"}`,
|
|
87692
|
+
`asyncIterator=${typeof record[Symbol.asyncIterator]}`,
|
|
87693
|
+
`controller=${typeof record.controller}`,
|
|
87694
|
+
`controllerAbort=${typeof controller?.abort}`,
|
|
87695
|
+
`controllerSignal=${typeof controller?.signal}`,
|
|
87696
|
+
keys.length > 0 ? `keys=${keys.join(",")}` : "keys=(none)"
|
|
87697
|
+
].join(" ");
|
|
87698
|
+
}
|
|
87699
|
+
function summarizeChunkForDebug(chunk) {
|
|
87700
|
+
if (!chunk) {
|
|
87701
|
+
return "none";
|
|
87702
|
+
}
|
|
87703
|
+
const record = chunk;
|
|
87704
|
+
const parts = [`message_type=${chunk.message_type ?? "unknown"}`];
|
|
87705
|
+
for (const key of ["run_id", "seq_id", "id", "otid", "tool_call_id"]) {
|
|
87706
|
+
const value = record[key];
|
|
87707
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
87708
|
+
parts.push(`${key}=${value}`);
|
|
87709
|
+
}
|
|
87710
|
+
}
|
|
87711
|
+
if (chunk.message_type === "stop_reason") {
|
|
87712
|
+
parts.push(`stop_reason=${String(record.stop_reason ?? "unknown")}`);
|
|
87713
|
+
}
|
|
87714
|
+
const toolCalls = record.tool_calls;
|
|
87715
|
+
if (Array.isArray(toolCalls)) {
|
|
87716
|
+
parts.push(`tool_calls=${toolCalls.length}`);
|
|
87717
|
+
}
|
|
87718
|
+
return parts.join(" ");
|
|
87719
|
+
}
|
|
87720
|
+
function abortStreamController(stream2, reason) {
|
|
87721
|
+
const controller = stream2.controller;
|
|
87722
|
+
if (!controller || typeof controller !== "object") {
|
|
87723
|
+
debugWarn("drainStream", "stream.controller is unavailable during %s - cannot abort HTTP request (%s)", reason, summarizeStreamForDebug(stream2));
|
|
87724
|
+
return;
|
|
87725
|
+
}
|
|
87726
|
+
const controllerRecord = controller;
|
|
87727
|
+
if (controllerRecord.signal?.aborted) {
|
|
87728
|
+
return;
|
|
87729
|
+
}
|
|
87730
|
+
if (typeof controllerRecord.abort !== "function") {
|
|
87731
|
+
debugWarn("drainStream", "stream.controller.abort is unavailable during %s - cannot abort HTTP request (%s)", reason, summarizeStreamForDebug(stream2));
|
|
87732
|
+
return;
|
|
87733
|
+
}
|
|
87734
|
+
controllerRecord.abort();
|
|
87735
|
+
}
|
|
87566
87736
|
function hasPaginatedItems(response) {
|
|
87567
87737
|
return !Array.isArray(response) && typeof response.getPaginatedItems === "function";
|
|
87568
87738
|
}
|
|
@@ -87640,28 +87810,26 @@ async function drainStream(stream2, buffers, refresh, abortSignal, onFirstMessag
|
|
|
87640
87810
|
let stopReason = null;
|
|
87641
87811
|
let hasCalledFirstMessage = false;
|
|
87642
87812
|
let fallbackError = null;
|
|
87813
|
+
let lastChunkDebugSummary = "none";
|
|
87643
87814
|
let abortedViaListener = false;
|
|
87644
87815
|
const startAbortGen = buffers.abortGeneration || 0;
|
|
87645
87816
|
const abortHandler = () => {
|
|
87646
87817
|
abortedViaListener = true;
|
|
87647
|
-
|
|
87648
|
-
debugWarn("drainStream", "stream.controller is undefined - cannot abort HTTP request");
|
|
87649
|
-
return;
|
|
87650
|
-
}
|
|
87651
|
-
if (!stream2.controller.signal.aborted) {
|
|
87652
|
-
stream2.controller.abort();
|
|
87653
|
-
}
|
|
87818
|
+
abortStreamController(stream2, "abort_signal");
|
|
87654
87819
|
};
|
|
87655
87820
|
if (abortSignal && !abortSignal.aborted) {
|
|
87656
87821
|
abortSignal.addEventListener("abort", abortHandler, { once: true });
|
|
87657
87822
|
} else if (abortSignal?.aborted) {
|
|
87658
87823
|
abortedViaListener = true;
|
|
87659
|
-
|
|
87660
|
-
stream2.controller.abort();
|
|
87661
|
-
}
|
|
87824
|
+
abortStreamController(stream2, "pre_aborted_signal");
|
|
87662
87825
|
}
|
|
87663
87826
|
try {
|
|
87827
|
+
const asyncIterator = stream2[Symbol.asyncIterator];
|
|
87828
|
+
if (typeof asyncIterator !== "function") {
|
|
87829
|
+
throw new TypeError(`Stream is not async iterable (${summarizeStreamForDebug(stream2)})`);
|
|
87830
|
+
}
|
|
87664
87831
|
for await (const chunk of stream2) {
|
|
87832
|
+
lastChunkDebugSummary = summarizeChunkForDebug(chunk);
|
|
87665
87833
|
recordTuiJsonPayload(`stream_chunk:${chunk.message_type ?? "unknown"}`, chunk);
|
|
87666
87834
|
if ((buffers.abortGeneration || 0) !== startAbortGen) {
|
|
87667
87835
|
stopReason = "cancelled";
|
|
@@ -87730,7 +87898,10 @@ async function drainStream(stream2, buffers, refresh, abortSignal, onFirstMessag
|
|
|
87730
87898
|
const errorMessage = e instanceof Error ? e.message : String(e);
|
|
87731
87899
|
const sdkDiagnostic = consumeLastSDKDiagnostic();
|
|
87732
87900
|
const errorMessageWithDiagnostic = sdkDiagnostic ? `${errorMessage} [${sdkDiagnostic}]` : errorMessage;
|
|
87733
|
-
debugWarn("drainStream", "Stream error caught:",
|
|
87901
|
+
debugWarn("drainStream", "Stream error caught: %s last_chunk=%s stream=%s", errorMessageWithDiagnostic, lastChunkDebugSummary, summarizeStreamForDebug(stream2));
|
|
87902
|
+
if (e instanceof Error && e.stack) {
|
|
87903
|
+
debugWarn("drainStream", "Stream error stack: %s", e.stack);
|
|
87904
|
+
}
|
|
87734
87905
|
if (!streamProcessor.lastRunId && e instanceof APIError2 && e.error) {
|
|
87735
87906
|
const errorObj = e.error;
|
|
87736
87907
|
if ("run_id" in errorObj && typeof errorObj.run_id === "string") {
|
|
@@ -95763,7 +95934,7 @@ function isSyncCommand(value) {
|
|
|
95763
95934
|
return false;
|
|
95764
95935
|
}
|
|
95765
95936
|
const candidate = value;
|
|
95766
|
-
return candidate.type === "sync" && isRuntimeScope(candidate.runtime);
|
|
95937
|
+
return candidate.type === "sync" && isRuntimeScope(candidate.runtime) && (candidate.recover_approvals === undefined || typeof candidate.recover_approvals === "boolean");
|
|
95767
95938
|
}
|
|
95768
95939
|
function isTerminalSpawnCommand(value) {
|
|
95769
95940
|
if (!value || typeof value !== "object")
|
|
@@ -96444,12 +96615,14 @@ function runDetachedListenerTask(commandName, task2) {
|
|
|
96444
96615
|
async function replaySyncStateForRuntime(listenerRuntime, socket, scope, opts) {
|
|
96445
96616
|
const syncScopedRuntime = getOrCreateScopedRuntime(listenerRuntime, scope.agent_id, scope.conversation_id);
|
|
96446
96617
|
const recoverFn = opts?.recoverApprovalStateForSync ?? recoverApprovalStateForSync;
|
|
96447
|
-
|
|
96448
|
-
|
|
96449
|
-
|
|
96450
|
-
|
|
96451
|
-
|
|
96452
|
-
|
|
96618
|
+
if (opts?.recoverApprovals ?? true) {
|
|
96619
|
+
try {
|
|
96620
|
+
await recoverFn(syncScopedRuntime, scope);
|
|
96621
|
+
} catch (error) {
|
|
96622
|
+
trackListenerError("listener_sync_recovery_failed", error, "listener_sync_recovery");
|
|
96623
|
+
if (isDebugEnabled()) {
|
|
96624
|
+
console.warn("[Listen] Sync approval recovery failed:", error);
|
|
96625
|
+
}
|
|
96453
96626
|
}
|
|
96454
96627
|
}
|
|
96455
96628
|
emitStateSync(socket, listenerRuntime, scope);
|
|
@@ -98796,7 +98969,9 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
|
|
|
98796
98969
|
console.log(`[Listen V2] Dropping sync: runtime mismatch or closed`);
|
|
98797
98970
|
return;
|
|
98798
98971
|
}
|
|
98799
|
-
await replaySyncStateForRuntime(runtime, socket, parsed.runtime
|
|
98972
|
+
await replaySyncStateForRuntime(runtime, socket, parsed.runtime, {
|
|
98973
|
+
recoverApprovals: parsed.recover_approvals !== false
|
|
98974
|
+
});
|
|
98800
98975
|
return;
|
|
98801
98976
|
}
|
|
98802
98977
|
if (parsed.type === "input") {
|
|
@@ -147564,7 +147739,9 @@ function runWithLauncher(launcher, inputJson, timeout, signal, workingDirectory,
|
|
|
147564
147739
|
});
|
|
147565
147740
|
}
|
|
147566
147741
|
var MAX_STDOUT_BYTES = 4096;
|
|
147567
|
-
var init_statusLineRuntime = () => {
|
|
147742
|
+
var init_statusLineRuntime = __esm(() => {
|
|
147743
|
+
init_shellLaunchers();
|
|
147744
|
+
});
|
|
147568
147745
|
|
|
147569
147746
|
// src/cli/helpers/subagentAggregation.ts
|
|
147570
147747
|
function hasInProgressTaskToolCalls(order, byId, _emittedIds) {
|
|
@@ -159073,6 +159250,7 @@ __export(exports_memoryFilesystem2, {
|
|
|
159073
159250
|
renderMemoryFilesystemTree: () => renderMemoryFilesystemTree2,
|
|
159074
159251
|
labelFromRelativePath: () => labelFromRelativePath2,
|
|
159075
159252
|
isMemfsEnabledOnServer: () => isMemfsEnabledOnServer2,
|
|
159253
|
+
isLettaMemfsServer: () => isLettaMemfsServer2,
|
|
159076
159254
|
isLettaCloud: () => isLettaCloud2,
|
|
159077
159255
|
getMemorySystemDir: () => getMemorySystemDir2,
|
|
159078
159256
|
getMemoryFilesystemRoot: () => getMemoryFilesystemRoot2,
|
|
@@ -159260,8 +159438,8 @@ function renderMemoryFilesystemTree2(systemLabels, detachedLabels, options = {})
|
|
|
159260
159438
|
}
|
|
159261
159439
|
async function applyMemfsFlags2(agentId, memfsFlag, noMemfsFlag, options) {
|
|
159262
159440
|
const { settingsManager: settingsManager3 } = await Promise.resolve().then(() => (init_settings_manager(), exports_settings_manager));
|
|
159263
|
-
if (memfsFlag && !await
|
|
159264
|
-
throw new Error(
|
|
159441
|
+
if (memfsFlag && !await isLettaMemfsServer2()) {
|
|
159442
|
+
throw new Error(await getMemfsSyncUnavailableMessage2());
|
|
159265
159443
|
}
|
|
159266
159444
|
const hasExplicitToggle = Boolean(memfsFlag || noMemfsFlag);
|
|
159267
159445
|
const localMemfsEnabled = settingsManager3.isMemfsEnabled(agentId);
|
|
@@ -159332,6 +159510,24 @@ async function isLettaCloud2() {
|
|
|
159332
159510
|
const serverUrl = getServerUrl2();
|
|
159333
159511
|
return serverUrl.includes("api.letta.com") || process.env.LETTA_MEMFS_LOCAL === "1" || process.env.LETTA_API_KEY === "local-desktop";
|
|
159334
159512
|
}
|
|
159513
|
+
function getServerHostLabel2(serverUrl) {
|
|
159514
|
+
const trimmed = serverUrl.trim();
|
|
159515
|
+
try {
|
|
159516
|
+
return new URL(trimmed).host || trimmed;
|
|
159517
|
+
} catch {
|
|
159518
|
+
return trimmed.replace(/^https?:\/\//, "").replace(/\/+$/, "") || trimmed;
|
|
159519
|
+
}
|
|
159520
|
+
}
|
|
159521
|
+
async function isLettaMemfsServer2() {
|
|
159522
|
+
const { getMemfsServerUrl: getMemfsServerUrl2, isDesktopLocalProxyUrl: isDesktopLocalProxyUrl2 } = await Promise.resolve().then(() => (init_client2(), exports_client));
|
|
159523
|
+
const memfsServerUrl = getMemfsServerUrl2();
|
|
159524
|
+
return memfsServerUrl.includes("api.letta.com") || isDesktopLocalProxyUrl2(memfsServerUrl) || process.env.LETTA_MEMFS_LOCAL === "1" || process.env.LETTA_API_KEY === "local-desktop";
|
|
159525
|
+
}
|
|
159526
|
+
async function getMemfsSyncUnavailableMessage2() {
|
|
159527
|
+
const { getMemfsServerUrl: getMemfsServerUrl2 } = await Promise.resolve().then(() => (init_client2(), exports_client));
|
|
159528
|
+
const memfsServerUrl = getMemfsServerUrl2();
|
|
159529
|
+
return `MemFS sync failed (expected api.letta.com, got ${getServerHostLabel2(memfsServerUrl)})`;
|
|
159530
|
+
}
|
|
159335
159531
|
async function enableMemfsIfCloud2(agentId) {
|
|
159336
159532
|
if (!await isLettaCloud2())
|
|
159337
159533
|
return;
|
|
@@ -168653,4 +168849,4 @@ Error during initialization: ${message}`);
|
|
|
168653
168849
|
}
|
|
168654
168850
|
main();
|
|
168655
168851
|
|
|
168656
|
-
//# debugId=
|
|
168852
|
+
//# debugId=CB5FADB2FE73B65E64756E2164756E21
|
package/package.json
CHANGED
|
@@ -24,11 +24,12 @@ Below are additional common issues with context and how they can be resolved:
|
|
|
24
24
|
#### System prompt bloat
|
|
25
25
|
Memories compiled into the system prompt (contained in `system/`) should take up about 10% of the total context size (usually ~15-20K tokens). This is a soft target, not a hard requirement.
|
|
26
26
|
|
|
27
|
-
Use the
|
|
27
|
+
Use the built-in CLI to evaluate token usage of the system prompt:
|
|
28
28
|
```bash
|
|
29
|
-
|
|
29
|
+
letta memory tokens --format json --quiet
|
|
30
30
|
```
|
|
31
|
-
|
|
31
|
+
|
|
32
|
+
The command reports `total_tokens` and per-file estimates for `system/`. It is only a measurement tool; decide whether to intervene based on the actual context and the guidance below.
|
|
32
33
|
|
|
33
34
|
**Why detail is load-bearing (read this before cutting anything)**: In-context detail does more than carry information. It does at least four things, and byte-counting sweeps only see the first:
|
|
34
35
|
1. **Information** — the literal facts stated
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env npx tsx
|
|
2
|
-
/**
|
|
3
|
-
* Estimate token usage of system prompt memory files.
|
|
4
|
-
*
|
|
5
|
-
* Self-contained — no imports from the letta-code source tree.
|
|
6
|
-
*
|
|
7
|
-
* Usage:
|
|
8
|
-
* npx tsx estimate_system_tokens.ts --memory-dir "$MEMORY_DIR"
|
|
9
|
-
* npx tsx estimate_system_tokens.ts --memory-dir ~/.letta/agents/<id>/memory
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
13
|
-
import { join } from "node:path";
|
|
14
|
-
|
|
15
|
-
// Codex heuristic: ~4 bytes per token (codex-rs/core/src/truncate.rs APPROX_BYTES_PER_TOKEN = 4)
|
|
16
|
-
const BYTES_PER_TOKEN = 4;
|
|
17
|
-
|
|
18
|
-
function estimateTokens(text: string): number {
|
|
19
|
-
return Math.ceil(Buffer.byteLength(text, "utf8") / BYTES_PER_TOKEN);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
type FileEstimate = {
|
|
23
|
-
path: string;
|
|
24
|
-
tokens: number;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
type ParsedArgs = {
|
|
28
|
-
memoryDir?: string;
|
|
29
|
-
top: number;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
function parseArgs(argv: string[]): ParsedArgs {
|
|
33
|
-
const parsed: ParsedArgs = { top: 20 };
|
|
34
|
-
|
|
35
|
-
for (let i = 0; i < argv.length; i++) {
|
|
36
|
-
const arg = argv[i];
|
|
37
|
-
if (arg === "--memory-dir") {
|
|
38
|
-
parsed.memoryDir = argv[i + 1];
|
|
39
|
-
i++;
|
|
40
|
-
continue;
|
|
41
|
-
}
|
|
42
|
-
if (arg === "--top") {
|
|
43
|
-
const raw = argv[i + 1];
|
|
44
|
-
const value = Number.parseInt(raw ?? "", 10);
|
|
45
|
-
if (!Number.isNaN(value) && value >= 0) {
|
|
46
|
-
parsed.top = value;
|
|
47
|
-
}
|
|
48
|
-
i++;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return parsed;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function normalizePath(value: string): string {
|
|
56
|
-
return value.replaceAll("\\", "/");
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function walkMarkdownFiles(dir: string): string[] {
|
|
60
|
-
if (!existsSync(dir)) {
|
|
61
|
-
return [];
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const out: string[] = [];
|
|
65
|
-
const entries = readdirSync(dir, { withFileTypes: true });
|
|
66
|
-
|
|
67
|
-
for (const entry of entries) {
|
|
68
|
-
if (entry.name.startsWith(".")) {
|
|
69
|
-
continue;
|
|
70
|
-
}
|
|
71
|
-
const full = join(dir, entry.name);
|
|
72
|
-
if (entry.isDirectory()) {
|
|
73
|
-
out.push(...walkMarkdownFiles(full));
|
|
74
|
-
continue;
|
|
75
|
-
}
|
|
76
|
-
if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
77
|
-
out.push(full);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return out;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function formatNumber(value: number): string {
|
|
85
|
-
return value.toLocaleString("en-US");
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function main(): number {
|
|
89
|
-
const args = parseArgs(process.argv.slice(2));
|
|
90
|
-
const memoryDir = args.memoryDir || process.env.MEMORY_DIR;
|
|
91
|
-
|
|
92
|
-
if (!memoryDir) {
|
|
93
|
-
console.error("Missing memory dir. Pass --memory-dir or set MEMORY_DIR.");
|
|
94
|
-
return 1;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const systemDir = join(memoryDir, "system");
|
|
98
|
-
if (!existsSync(systemDir)) {
|
|
99
|
-
console.error(`Missing system directory: ${systemDir}`);
|
|
100
|
-
return 1;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const files = walkMarkdownFiles(systemDir).sort();
|
|
104
|
-
const rows: FileEstimate[] = [];
|
|
105
|
-
|
|
106
|
-
for (const filePath of files) {
|
|
107
|
-
const text = readFileSync(filePath, "utf8");
|
|
108
|
-
const rel = normalizePath(filePath.slice(memoryDir.length + 1));
|
|
109
|
-
rows.push({ path: rel, tokens: estimateTokens(text) });
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const estimatedTotalTokens = rows.reduce((sum, row) => sum + row.tokens, 0);
|
|
113
|
-
|
|
114
|
-
console.log("Estimated total tokens");
|
|
115
|
-
console.log(` ${formatNumber(estimatedTotalTokens)}`);
|
|
116
|
-
|
|
117
|
-
console.log("\nPer-file token estimates");
|
|
118
|
-
console.log(` ${"tokens".padStart(8)} path`);
|
|
119
|
-
|
|
120
|
-
const sortedRows = [...rows].sort((a, b) => b.tokens - a.tokens);
|
|
121
|
-
for (const row of sortedRows.slice(0, Math.max(0, args.top))) {
|
|
122
|
-
console.log(` ${formatNumber(row.tokens).padStart(8)} ${row.path}`);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
return 0;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
process.exit(main());
|