@letta-ai/letta-code 0.24.2 → 0.24.3
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 +601 -153
- package/package.json +1 -1
- package/skills/migrating-memory/SKILL.md +15 -9
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.3",
|
|
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: {
|
|
@@ -51442,7 +51442,8 @@ var init_readOnlyShell = __esm(() => {
|
|
|
51442
51442
|
"sleep"
|
|
51443
51443
|
]);
|
|
51444
51444
|
SAFE_LETTA_COMMANDS = {
|
|
51445
|
-
|
|
51445
|
+
memory: new Set(["status", "help", "backups", "export", "tokens"]),
|
|
51446
|
+
memfs: new Set(["status", "help", "backups", "export", "tokens"]),
|
|
51446
51447
|
agents: new Set(["list", "help"]),
|
|
51447
51448
|
messages: new Set(["search", "list", "help"]),
|
|
51448
51449
|
blocks: new Set(["list", "help"])
|
|
@@ -83492,46 +83493,58 @@ var init_memoryReminder = __esm(() => {
|
|
|
83492
83493
|
};
|
|
83493
83494
|
});
|
|
83494
83495
|
|
|
83495
|
-
// src/
|
|
83496
|
+
// src/utils/systemPromptSize.ts
|
|
83496
83497
|
import { existsSync as existsSync26, readdirSync as readdirSync8, readFileSync as readFileSync19 } from "node:fs";
|
|
83497
83498
|
import { join as join29 } from "node:path";
|
|
83498
83499
|
function estimateSystemTokens(text) {
|
|
83499
|
-
return Math.ceil(Buffer.byteLength(text, "utf8") /
|
|
83500
|
+
return Math.ceil(Buffer.byteLength(text, "utf8") / SYSTEM_PROMPT_BYTES_PER_TOKEN);
|
|
83500
83501
|
}
|
|
83501
|
-
function
|
|
83502
|
-
|
|
83503
|
-
|
|
83504
|
-
|
|
83502
|
+
function normalizePath2(value) {
|
|
83503
|
+
return value.replaceAll("\\", "/");
|
|
83504
|
+
}
|
|
83505
|
+
function walkMarkdownFiles(dir) {
|
|
83506
|
+
if (!existsSync26(dir)) {
|
|
83507
|
+
return [];
|
|
83505
83508
|
}
|
|
83506
|
-
const
|
|
83507
|
-
|
|
83508
|
-
|
|
83509
|
+
const out = [];
|
|
83510
|
+
const entries = readdirSync8(dir, { withFileTypes: true });
|
|
83511
|
+
for (const entry of entries) {
|
|
83512
|
+
if (entry.name.startsWith(".")) {
|
|
83513
|
+
continue;
|
|
83509
83514
|
}
|
|
83510
|
-
const
|
|
83511
|
-
|
|
83512
|
-
|
|
83513
|
-
|
|
83514
|
-
continue;
|
|
83515
|
-
}
|
|
83516
|
-
const full = join29(dir, entry.name);
|
|
83517
|
-
if (entry.isDirectory()) {
|
|
83518
|
-
if (entry.name === ".git") {
|
|
83519
|
-
continue;
|
|
83520
|
-
}
|
|
83521
|
-
out.push(...walkMarkdownFiles(full));
|
|
83522
|
-
continue;
|
|
83523
|
-
}
|
|
83524
|
-
if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
83525
|
-
out.push(full);
|
|
83526
|
-
}
|
|
83515
|
+
const full = join29(dir, entry.name);
|
|
83516
|
+
if (entry.isDirectory()) {
|
|
83517
|
+
out.push(...walkMarkdownFiles(full));
|
|
83518
|
+
continue;
|
|
83527
83519
|
}
|
|
83528
|
-
|
|
83529
|
-
|
|
83530
|
-
|
|
83520
|
+
if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
83521
|
+
out.push(full);
|
|
83522
|
+
}
|
|
83523
|
+
}
|
|
83524
|
+
return out;
|
|
83525
|
+
}
|
|
83526
|
+
function estimateSystemPromptSize(memoryDir) {
|
|
83527
|
+
const systemDir = join29(memoryDir, "system");
|
|
83528
|
+
if (!existsSync26(systemDir)) {
|
|
83529
|
+
return { total: 0, files: [] };
|
|
83530
|
+
}
|
|
83531
|
+
const files = walkMarkdownFiles(systemDir).sort();
|
|
83532
|
+
const rows = [];
|
|
83533
|
+
for (const filePath of files) {
|
|
83531
83534
|
const text = readFileSync19(filePath, "utf8");
|
|
83532
|
-
|
|
83533
|
-
|
|
83535
|
+
const rel = normalizePath2(filePath.slice(memoryDir.length + 1));
|
|
83536
|
+
rows.push({ path: rel, tokens: estimateSystemTokens(text) });
|
|
83537
|
+
}
|
|
83538
|
+
const total = rows.reduce((sum, row) => sum + row.tokens, 0);
|
|
83539
|
+
return { total, files: rows };
|
|
83540
|
+
}
|
|
83541
|
+
function estimateSystemPromptTokensFromMemoryDir(memoryDir) {
|
|
83542
|
+
return estimateSystemPromptSize(memoryDir).total;
|
|
83534
83543
|
}
|
|
83544
|
+
var SYSTEM_PROMPT_BYTES_PER_TOKEN = 4;
|
|
83545
|
+
var init_systemPromptSize = () => {};
|
|
83546
|
+
|
|
83547
|
+
// src/cli/helpers/systemPromptWarning.ts
|
|
83535
83548
|
function setSystemPromptDoctorState(agentId, estimatedTokens) {
|
|
83536
83549
|
const nextState = {
|
|
83537
83550
|
estimated_tokens: estimatedTokens,
|
|
@@ -83574,11 +83587,12 @@ function buildStartupSystemPromptWarning(agentState) {
|
|
|
83574
83587
|
}
|
|
83575
83588
|
return null;
|
|
83576
83589
|
}
|
|
83577
|
-
var STARTUP_SYSTEM_PROMPT_WARNING_THRESHOLD_TOKENS = 30000,
|
|
83590
|
+
var STARTUP_SYSTEM_PROMPT_WARNING_THRESHOLD_TOKENS = 30000, systemPromptDoctorStateByAgent;
|
|
83578
83591
|
var init_systemPromptWarning = __esm(() => {
|
|
83579
83592
|
init_memoryFilesystem();
|
|
83580
83593
|
init_settings_manager();
|
|
83581
83594
|
init_debug();
|
|
83595
|
+
init_systemPromptSize();
|
|
83582
83596
|
systemPromptDoctorStateByAgent = new Map;
|
|
83583
83597
|
});
|
|
83584
83598
|
|
|
@@ -87183,10 +87197,132 @@ var init_reflectionTranscript = __esm(() => {
|
|
|
87183
87197
|
init_directoryLimits();
|
|
87184
87198
|
});
|
|
87185
87199
|
|
|
87200
|
+
// src/utils/tuiPerf.ts
|
|
87201
|
+
import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync20 } from "node:fs";
|
|
87202
|
+
import { dirname as dirname14 } from "node:path";
|
|
87203
|
+
function ensureExitHook() {
|
|
87204
|
+
if (tuiPerfExitHookRegistered) {
|
|
87205
|
+
return;
|
|
87206
|
+
}
|
|
87207
|
+
tuiPerfExitHookRegistered = true;
|
|
87208
|
+
process.once("beforeExit", () => {
|
|
87209
|
+
flushTuiPerfTelemetry();
|
|
87210
|
+
});
|
|
87211
|
+
}
|
|
87212
|
+
function scheduleTuiPerfFlush() {
|
|
87213
|
+
if (tuiPerfFlushTimer) {
|
|
87214
|
+
return;
|
|
87215
|
+
}
|
|
87216
|
+
tuiPerfFlushTimer = setTimeout(() => {
|
|
87217
|
+
tuiPerfFlushTimer = null;
|
|
87218
|
+
flushTuiPerfTelemetry();
|
|
87219
|
+
}, TUI_PERF_FLUSH_INTERVAL_MS);
|
|
87220
|
+
const timerWithUnref = tuiPerfFlushTimer;
|
|
87221
|
+
timerWithUnref.unref?.();
|
|
87222
|
+
}
|
|
87223
|
+
function recordTuiPerf(key, sample) {
|
|
87224
|
+
if (!TUI_PERF_ENABLED || !TUI_PERF_FILE) {
|
|
87225
|
+
return;
|
|
87226
|
+
}
|
|
87227
|
+
ensureExitHook();
|
|
87228
|
+
if (tuiPerfWindowStartedAt === 0) {
|
|
87229
|
+
tuiPerfWindowStartedAt = Date.now();
|
|
87230
|
+
}
|
|
87231
|
+
const bytes = sample?.bytes ?? 0;
|
|
87232
|
+
const ms = sample?.ms ?? 0;
|
|
87233
|
+
const bucket = tuiPerfBuckets.get(key) ?? {
|
|
87234
|
+
count: 0,
|
|
87235
|
+
bytes: 0,
|
|
87236
|
+
ms: 0,
|
|
87237
|
+
maxBytes: 0,
|
|
87238
|
+
maxMs: 0
|
|
87239
|
+
};
|
|
87240
|
+
bucket.count += 1;
|
|
87241
|
+
bucket.bytes += bytes;
|
|
87242
|
+
bucket.ms += ms;
|
|
87243
|
+
bucket.maxBytes = Math.max(bucket.maxBytes, bytes);
|
|
87244
|
+
bucket.maxMs = Math.max(bucket.maxMs, ms);
|
|
87245
|
+
tuiPerfBuckets.set(key, bucket);
|
|
87246
|
+
scheduleTuiPerfFlush();
|
|
87247
|
+
}
|
|
87248
|
+
function recordTuiJsonPayload(key, value) {
|
|
87249
|
+
if (!TUI_PERF_ENABLED || !TUI_PERF_FILE) {
|
|
87250
|
+
return;
|
|
87251
|
+
}
|
|
87252
|
+
try {
|
|
87253
|
+
recordTuiPerf(key, { bytes: Buffer.byteLength(JSON.stringify(value)) });
|
|
87254
|
+
} catch {
|
|
87255
|
+
recordTuiPerf(key);
|
|
87256
|
+
}
|
|
87257
|
+
}
|
|
87258
|
+
function flushTuiPerfTelemetry() {
|
|
87259
|
+
if (tuiPerfBuckets.size === 0) {
|
|
87260
|
+
tuiPerfWindowStartedAt = 0;
|
|
87261
|
+
return;
|
|
87262
|
+
}
|
|
87263
|
+
const filePath = TUI_PERF_FILE;
|
|
87264
|
+
if (!filePath) {
|
|
87265
|
+
tuiPerfBuckets.clear();
|
|
87266
|
+
tuiPerfWindowStartedAt = 0;
|
|
87267
|
+
return;
|
|
87268
|
+
}
|
|
87269
|
+
const windowMs = Math.max(1, Date.now() - tuiPerfWindowStartedAt);
|
|
87270
|
+
const totals = {
|
|
87271
|
+
count: 0,
|
|
87272
|
+
bytes: 0,
|
|
87273
|
+
ms: 0,
|
|
87274
|
+
maxBytes: 0,
|
|
87275
|
+
maxMs: 0
|
|
87276
|
+
};
|
|
87277
|
+
const buckets = {};
|
|
87278
|
+
for (const [key, bucket] of [...tuiPerfBuckets.entries()].sort(([a], [b]) => a.localeCompare(b))) {
|
|
87279
|
+
totals.count += bucket.count;
|
|
87280
|
+
totals.bytes += bucket.bytes;
|
|
87281
|
+
totals.ms += bucket.ms;
|
|
87282
|
+
totals.maxBytes = Math.max(totals.maxBytes, bucket.maxBytes);
|
|
87283
|
+
totals.maxMs = Math.max(totals.maxMs, bucket.maxMs);
|
|
87284
|
+
buckets[key] = {
|
|
87285
|
+
...bucket,
|
|
87286
|
+
avg_bytes: bucket.count > 0 ? bucket.bytes / bucket.count : 0,
|
|
87287
|
+
avg_ms: bucket.count > 0 ? bucket.ms / bucket.count : 0
|
|
87288
|
+
};
|
|
87289
|
+
}
|
|
87290
|
+
try {
|
|
87291
|
+
const dir = dirname14(filePath);
|
|
87292
|
+
if (tuiPerfFileDirEnsured !== dir) {
|
|
87293
|
+
mkdirSync20(dir, { recursive: true });
|
|
87294
|
+
tuiPerfFileDirEnsured = dir;
|
|
87295
|
+
}
|
|
87296
|
+
appendFileSync3(filePath, `${JSON.stringify({
|
|
87297
|
+
ts: new Date().toISOString(),
|
|
87298
|
+
event: "tui_activity",
|
|
87299
|
+
window_ms: windowMs,
|
|
87300
|
+
totals,
|
|
87301
|
+
buckets
|
|
87302
|
+
})}
|
|
87303
|
+
`, { encoding: "utf8" });
|
|
87304
|
+
} catch (error) {
|
|
87305
|
+
if (!tuiPerfWarningEmitted) {
|
|
87306
|
+
tuiPerfWarningEmitted = true;
|
|
87307
|
+
console.error(`[TUI Perf] Failed to write LETTA_TUI_PERF_FILE=${filePath}`, error);
|
|
87308
|
+
}
|
|
87309
|
+
} finally {
|
|
87310
|
+
tuiPerfBuckets.clear();
|
|
87311
|
+
tuiPerfWindowStartedAt = 0;
|
|
87312
|
+
}
|
|
87313
|
+
}
|
|
87314
|
+
var TUI_PERF_FLUSH_INTERVAL_MS = 1000, TUI_PERF_ENV_VALUES, TUI_PERF_ENABLED, TUI_PERF_FILE, tuiPerfBuckets, tuiPerfFlushTimer = null, tuiPerfWindowStartedAt = 0, tuiPerfFileDirEnsured = null, tuiPerfWarningEmitted = false, tuiPerfExitHookRegistered = false;
|
|
87315
|
+
var init_tuiPerf = __esm(() => {
|
|
87316
|
+
TUI_PERF_ENV_VALUES = new Set(["1", "true", "yes"]);
|
|
87317
|
+
TUI_PERF_ENABLED = TUI_PERF_ENV_VALUES.has((process.env.LETTA_TUI_PERF ?? "").toLowerCase());
|
|
87318
|
+
TUI_PERF_FILE = process.env.LETTA_TUI_PERF_FILE?.trim() || null;
|
|
87319
|
+
tuiPerfBuckets = new Map;
|
|
87320
|
+
});
|
|
87321
|
+
|
|
87186
87322
|
// src/cli/helpers/chunkLog.ts
|
|
87187
87323
|
import {
|
|
87188
87324
|
existsSync as existsSync28,
|
|
87189
|
-
mkdirSync as
|
|
87325
|
+
mkdirSync as mkdirSync21,
|
|
87190
87326
|
readdirSync as readdirSync9,
|
|
87191
87327
|
unlinkSync as unlinkSync7,
|
|
87192
87328
|
writeFileSync as writeFileSync16
|
|
@@ -87287,7 +87423,7 @@ class ChunkLog {
|
|
|
87287
87423
|
return;
|
|
87288
87424
|
try {
|
|
87289
87425
|
if (!existsSync28(this.agentDir)) {
|
|
87290
|
-
|
|
87426
|
+
mkdirSync21(this.agentDir, { recursive: true });
|
|
87291
87427
|
}
|
|
87292
87428
|
this.dirCreated = true;
|
|
87293
87429
|
} catch (e) {
|
|
@@ -87526,6 +87662,7 @@ async function drainStream(stream2, buffers, refresh, abortSignal, onFirstMessag
|
|
|
87526
87662
|
}
|
|
87527
87663
|
try {
|
|
87528
87664
|
for await (const chunk of stream2) {
|
|
87665
|
+
recordTuiJsonPayload(`stream_chunk:${chunk.message_type ?? "unknown"}`, chunk);
|
|
87529
87666
|
if ((buffers.abortGeneration || 0) !== startAbortGen) {
|
|
87530
87667
|
stopReason = "cancelled";
|
|
87531
87668
|
queueMicrotask(refresh);
|
|
@@ -87581,6 +87718,7 @@ async function drainStream(stream2, buffers, refresh, abortSignal, onFirstMessag
|
|
|
87581
87718
|
shouldAccumulate = shouldOutputChunk;
|
|
87582
87719
|
}
|
|
87583
87720
|
if (shouldAccumulate) {
|
|
87721
|
+
recordTuiJsonPayload(`stream_accumulate:${chunk.message_type ?? "unknown"}`, chunk);
|
|
87584
87722
|
onChunk(buffers, chunk, contextTracker);
|
|
87585
87723
|
queueMicrotask(refresh);
|
|
87586
87724
|
}
|
|
@@ -87663,6 +87801,7 @@ async function drainStream(stream2, buffers, refresh, abortSignal, onFirstMessag
|
|
|
87663
87801
|
}
|
|
87664
87802
|
async function drainStreamWithResume(stream2, buffers, refresh, abortSignal, onFirstMessage, onChunkProcessed, contextTracker, seenSeqIdThreshold) {
|
|
87665
87803
|
const overallStartTime = performance.now();
|
|
87804
|
+
recordTuiPerf("stream_lifecycle:start");
|
|
87666
87805
|
const streamRequestContext = getStreamRequestContext(stream2);
|
|
87667
87806
|
const streamOtid = streamRequestContext?.otid ?? null;
|
|
87668
87807
|
let _client;
|
|
@@ -87785,6 +87924,9 @@ async function drainStreamWithResume(stream2, buffers, refresh, abortSignal, onF
|
|
|
87785
87924
|
markCurrentLineAsFinished(buffers);
|
|
87786
87925
|
}
|
|
87787
87926
|
result.apiDurationMs = performance.now() - overallStartTime;
|
|
87927
|
+
recordTuiPerf(`stream_lifecycle:end:${result.stopReason}`, {
|
|
87928
|
+
ms: result.apiDurationMs
|
|
87929
|
+
});
|
|
87788
87930
|
return result;
|
|
87789
87931
|
}
|
|
87790
87932
|
var FALLBACK_RUN_DISCOVERY_TIMEOUT_MS = 5000;
|
|
@@ -87795,6 +87937,7 @@ var init_stream = __esm(async () => {
|
|
|
87795
87937
|
init_debug();
|
|
87796
87938
|
init_streamAbortRelay();
|
|
87797
87939
|
init_timing();
|
|
87940
|
+
init_tuiPerf();
|
|
87798
87941
|
init_chunkLog();
|
|
87799
87942
|
await __promiseAll([
|
|
87800
87943
|
init_message(),
|
|
@@ -88893,22 +89036,12 @@ function emitToolExecutionFinishedEvents(socket, runtime, params) {
|
|
|
88893
89036
|
}
|
|
88894
89037
|
function createToolExecutionOutputEmitter(socket, runtime, params) {
|
|
88895
89038
|
const outputByToolCallId = new Map;
|
|
88896
|
-
|
|
88897
|
-
if (!
|
|
89039
|
+
const emitToolOutput = (toolCallId, outputState) => {
|
|
89040
|
+
if (!outputState.dirty) {
|
|
88898
89041
|
return;
|
|
88899
89042
|
}
|
|
88900
|
-
|
|
88901
|
-
|
|
88902
|
-
messageId: `message-tool-return-stream-${toolCallId}`,
|
|
88903
|
-
stdout: "",
|
|
88904
|
-
stderr: ""
|
|
88905
|
-
};
|
|
88906
|
-
if (isStderr) {
|
|
88907
|
-
outputState.stderr = appendStreamingOutputWithCap(outputState.stderr, chunk);
|
|
88908
|
-
} else {
|
|
88909
|
-
outputState.stdout = appendStreamingOutputWithCap(outputState.stdout, chunk);
|
|
88910
|
-
}
|
|
88911
|
-
outputByToolCallId.set(toolCallId, outputState);
|
|
89043
|
+
outputState.dirty = false;
|
|
89044
|
+
outputState.lastEmittedAt = Date.now();
|
|
88912
89045
|
const stdout = normalizeStreamingOutputLines(outputState.stdout);
|
|
88913
89046
|
const stderr = normalizeStreamingOutputLines(outputState.stderr);
|
|
88914
89047
|
const toolReturn = [stdout?.join(`
|
|
@@ -88938,6 +89071,52 @@ function createToolExecutionOutputEmitter(socket, runtime, params) {
|
|
|
88938
89071
|
conversation_id: params.conversationId
|
|
88939
89072
|
});
|
|
88940
89073
|
};
|
|
89074
|
+
const flushToolOutput = (toolCallId, outputState) => {
|
|
89075
|
+
if (outputState.timer) {
|
|
89076
|
+
clearTimeout(outputState.timer);
|
|
89077
|
+
outputState.timer = null;
|
|
89078
|
+
}
|
|
89079
|
+
emitToolOutput(toolCallId, outputState);
|
|
89080
|
+
};
|
|
89081
|
+
const emitter = (toolCallId, chunk, isStderr = false) => {
|
|
89082
|
+
if (!toolCallId || chunk.length === 0) {
|
|
89083
|
+
return;
|
|
89084
|
+
}
|
|
89085
|
+
const existing = outputByToolCallId.get(toolCallId);
|
|
89086
|
+
const outputState = existing ?? {
|
|
89087
|
+
messageId: `message-tool-return-stream-${toolCallId}`,
|
|
89088
|
+
stdout: "",
|
|
89089
|
+
stderr: "",
|
|
89090
|
+
dirty: false,
|
|
89091
|
+
lastEmittedAt: 0,
|
|
89092
|
+
timer: null
|
|
89093
|
+
};
|
|
89094
|
+
if (isStderr) {
|
|
89095
|
+
outputState.stderr = appendStreamingOutputWithCap(outputState.stderr, chunk);
|
|
89096
|
+
} else {
|
|
89097
|
+
outputState.stdout = appendStreamingOutputWithCap(outputState.stdout, chunk);
|
|
89098
|
+
}
|
|
89099
|
+
outputByToolCallId.set(toolCallId, outputState);
|
|
89100
|
+
outputState.dirty = true;
|
|
89101
|
+
const now = Date.now();
|
|
89102
|
+
const elapsed = now - outputState.lastEmittedAt;
|
|
89103
|
+
if (elapsed >= STREAMING_TOOL_OUTPUT_EMIT_INTERVAL_MS) {
|
|
89104
|
+
flushToolOutput(toolCallId, outputState);
|
|
89105
|
+
return;
|
|
89106
|
+
}
|
|
89107
|
+
if (!outputState.timer) {
|
|
89108
|
+
outputState.timer = setTimeout(() => {
|
|
89109
|
+
outputState.timer = null;
|
|
89110
|
+
emitToolOutput(toolCallId, outputState);
|
|
89111
|
+
}, STREAMING_TOOL_OUTPUT_EMIT_INTERVAL_MS - elapsed);
|
|
89112
|
+
}
|
|
89113
|
+
};
|
|
89114
|
+
emitter.flush = () => {
|
|
89115
|
+
for (const [toolCallId, outputState] of outputByToolCallId.entries()) {
|
|
89116
|
+
flushToolOutput(toolCallId, outputState);
|
|
89117
|
+
}
|
|
89118
|
+
};
|
|
89119
|
+
return emitter;
|
|
88941
89120
|
}
|
|
88942
89121
|
function getInterruptApprovalsForEmission(runtime, params) {
|
|
88943
89122
|
if (params.lastExecutionResults && params.lastExecutionResults.length > 0) {
|
|
@@ -89055,7 +89234,7 @@ function stashRecoveredApprovalInterrupts(runtime, recovered) {
|
|
|
89055
89234
|
clearRecoveredApprovalState(runtime);
|
|
89056
89235
|
return true;
|
|
89057
89236
|
}
|
|
89058
|
-
var INTERRUPT_TOOL_RETURN_MAX_CHARS, STREAMING_TOOL_OUTPUT_MAX_CHARS;
|
|
89237
|
+
var INTERRUPT_TOOL_RETURN_MAX_CHARS, STREAMING_TOOL_OUTPUT_MAX_CHARS, STREAMING_TOOL_OUTPUT_EMIT_INTERVAL_MS = 100;
|
|
89059
89238
|
var init_interrupts = __esm(async () => {
|
|
89060
89239
|
init_approval_result_normalization();
|
|
89061
89240
|
init_constants();
|
|
@@ -89892,16 +90071,21 @@ async function resolveRecoveredApprovalResponse(runtime, socket, response, proce
|
|
|
89892
90071
|
runtime.currentToolsetPreference = preparedToolContext.toolsetPreference;
|
|
89893
90072
|
runtime.currentLoadedTools = preparedToolContext.preparedToolContext.loadedToolNames;
|
|
89894
90073
|
try {
|
|
89895
|
-
|
|
89896
|
-
|
|
89897
|
-
|
|
89898
|
-
|
|
89899
|
-
|
|
89900
|
-
|
|
89901
|
-
|
|
89902
|
-
|
|
89903
|
-
|
|
89904
|
-
|
|
90074
|
+
let approvalResults;
|
|
90075
|
+
try {
|
|
90076
|
+
approvalResults = await executeApprovalBatch(decisions, undefined, {
|
|
90077
|
+
abortSignal: recoveryAbortController.signal,
|
|
90078
|
+
onStreamingOutput: emitToolExecutionOutput,
|
|
90079
|
+
toolContextId: preparedToolContext.preparedToolContext.contextId,
|
|
90080
|
+
workingDirectory,
|
|
90081
|
+
parentScope: recovered.agentId && recovered.conversationId ? {
|
|
90082
|
+
agentId: recovered.agentId,
|
|
90083
|
+
conversationId: recovered.conversationId
|
|
90084
|
+
} : undefined
|
|
90085
|
+
});
|
|
90086
|
+
} finally {
|
|
90087
|
+
emitToolExecutionOutput.flush();
|
|
90088
|
+
}
|
|
89905
90089
|
emitToolExecutionFinishedEvents(socket, runtime, {
|
|
89906
90090
|
approvals: approvalResults,
|
|
89907
90091
|
runId: runtime.activeRunId ?? undefined,
|
|
@@ -89915,7 +90099,8 @@ async function resolveRecoveredApprovalResponse(runtime, socket, response, proce
|
|
|
89915
90099
|
const continuationMessages = [
|
|
89916
90100
|
{
|
|
89917
90101
|
type: "approval",
|
|
89918
|
-
approvals: approvalResults
|
|
90102
|
+
approvals: approvalResults,
|
|
90103
|
+
otid: crypto.randomUUID()
|
|
89919
90104
|
}
|
|
89920
90105
|
];
|
|
89921
90106
|
let continuationBatchId = `batch-recovered-${crypto.randomUUID()}`;
|
|
@@ -90309,6 +90494,18 @@ async function sendApprovalContinuationWithRetry(conversationId, messages, opts,
|
|
|
90309
90494
|
retryAfterMs
|
|
90310
90495
|
});
|
|
90311
90496
|
transientRetries = attempt;
|
|
90497
|
+
const retryMessage = getRetryStatusMessage(errorDetail);
|
|
90498
|
+
if (retryMessage) {
|
|
90499
|
+
emitRetryDelta(socket, runtime, {
|
|
90500
|
+
message: retryMessage,
|
|
90501
|
+
reason: "error",
|
|
90502
|
+
attempt,
|
|
90503
|
+
maxAttempts: LLM_API_ERROR_MAX_RETRIES,
|
|
90504
|
+
delayMs,
|
|
90505
|
+
agentId: runtime.agentId ?? undefined,
|
|
90506
|
+
conversationId
|
|
90507
|
+
});
|
|
90508
|
+
}
|
|
90312
90509
|
await new Promise((resolve27) => setTimeout(resolve27, delayMs));
|
|
90313
90510
|
if (abortSignal?.aborted) {
|
|
90314
90511
|
throw new Error("Cancelled by user");
|
|
@@ -90354,6 +90551,15 @@ async function sendApprovalContinuationWithRetry(conversationId, messages, opts,
|
|
|
90354
90551
|
category: "conversation_busy",
|
|
90355
90552
|
attempt: conversationBusyRetries
|
|
90356
90553
|
});
|
|
90554
|
+
emitRetryDelta(socket, runtime, {
|
|
90555
|
+
message: "Conversation is busy, waiting and retrying…",
|
|
90556
|
+
reason: "error",
|
|
90557
|
+
attempt: conversationBusyRetries,
|
|
90558
|
+
maxAttempts: MAX_CONVERSATION_BUSY_RETRIES,
|
|
90559
|
+
delayMs: retryDelayMs,
|
|
90560
|
+
agentId: runtime.agentId ?? undefined,
|
|
90561
|
+
conversationId
|
|
90562
|
+
});
|
|
90357
90563
|
await new Promise((resolve27) => setTimeout(resolve27, retryDelayMs));
|
|
90358
90564
|
if (abortSignal?.aborted) {
|
|
90359
90565
|
throw new Error("Cancelled by user");
|
|
@@ -92710,14 +92916,19 @@ async function handleApprovalStop(params) {
|
|
|
92710
92916
|
}));
|
|
92711
92917
|
}
|
|
92712
92918
|
};
|
|
92713
|
-
|
|
92714
|
-
|
|
92715
|
-
|
|
92716
|
-
|
|
92717
|
-
|
|
92718
|
-
|
|
92719
|
-
|
|
92720
|
-
|
|
92919
|
+
let executionResults;
|
|
92920
|
+
try {
|
|
92921
|
+
executionResults = await executeApprovalBatch(decisions, undefined, {
|
|
92922
|
+
toolContextId: turnToolContextId ?? undefined,
|
|
92923
|
+
abortSignal: abortController.signal,
|
|
92924
|
+
onStreamingOutput: emitToolExecutionOutput,
|
|
92925
|
+
workingDirectory: turnWorkingDirectory,
|
|
92926
|
+
parentScope: agentId && conversationId ? { agentId, conversationId } : undefined,
|
|
92927
|
+
onFileWrite
|
|
92928
|
+
});
|
|
92929
|
+
} finally {
|
|
92930
|
+
emitToolExecutionOutput.flush();
|
|
92931
|
+
}
|
|
92721
92932
|
const persistedExecutionResults = normalizeExecutionResultsForInterruptParity(runtime, executionResults, lastExecutingToolCallIds);
|
|
92722
92933
|
validateApprovalResultIds(decisions.map((decision) => ({
|
|
92723
92934
|
approval: {
|
|
@@ -92738,7 +92949,8 @@ async function handleApprovalStop(params) {
|
|
|
92738
92949
|
const nextInput = [
|
|
92739
92950
|
{
|
|
92740
92951
|
type: "approval",
|
|
92741
|
-
approvals: persistedExecutionResults
|
|
92952
|
+
approvals: persistedExecutionResults,
|
|
92953
|
+
otid: crypto.randomUUID()
|
|
92742
92954
|
}
|
|
92743
92955
|
];
|
|
92744
92956
|
let continuationBatchId = dequeuedBatchId;
|
|
@@ -93898,7 +94110,115 @@ var init_commands = __esm(async () => {
|
|
|
93898
94110
|
});
|
|
93899
94111
|
|
|
93900
94112
|
// src/websocket/listener/protocol-outbound.ts
|
|
94113
|
+
import { appendFileSync as appendFileSync4, mkdirSync as mkdirSync22 } from "node:fs";
|
|
94114
|
+
import { dirname as dirname15 } from "node:path";
|
|
94115
|
+
import { performance as performance2 } from "node:perf_hooks";
|
|
93901
94116
|
import WebSocket3 from "ws";
|
|
94117
|
+
function getProtocolPerfKey(message) {
|
|
94118
|
+
if (message.type === "stream_delta" && "delta" in message) {
|
|
94119
|
+
const delta = message.delta;
|
|
94120
|
+
return `${message.type}:${String(delta.message_type ?? "unknown")}`;
|
|
94121
|
+
}
|
|
94122
|
+
return message.type;
|
|
94123
|
+
}
|
|
94124
|
+
function scheduleProtocolPerfFlush() {
|
|
94125
|
+
if (protocolPerfFlushTimer) {
|
|
94126
|
+
return;
|
|
94127
|
+
}
|
|
94128
|
+
protocolPerfFlushTimer = setTimeout(() => {
|
|
94129
|
+
protocolPerfFlushTimer = null;
|
|
94130
|
+
flushProtocolPerfTelemetry();
|
|
94131
|
+
}, PROTOCOL_PERF_FLUSH_INTERVAL_MS);
|
|
94132
|
+
const timerWithUnref = protocolPerfFlushTimer;
|
|
94133
|
+
timerWithUnref.unref?.();
|
|
94134
|
+
}
|
|
94135
|
+
function recordProtocolPerfTelemetry(key, sample) {
|
|
94136
|
+
if (protocolPerfWindowStartedAt === 0) {
|
|
94137
|
+
protocolPerfWindowStartedAt = Date.now();
|
|
94138
|
+
}
|
|
94139
|
+
const bucket = protocolPerfBuckets.get(key) ?? {
|
|
94140
|
+
count: 0,
|
|
94141
|
+
bytes: 0,
|
|
94142
|
+
stringifyMs: 0,
|
|
94143
|
+
sendMs: 0,
|
|
94144
|
+
maxBufferedBefore: 0,
|
|
94145
|
+
maxBufferedAfter: 0
|
|
94146
|
+
};
|
|
94147
|
+
bucket.count += 1;
|
|
94148
|
+
bucket.bytes += sample.bytes;
|
|
94149
|
+
bucket.stringifyMs += sample.stringifyMs;
|
|
94150
|
+
bucket.sendMs += sample.sendMs;
|
|
94151
|
+
bucket.maxBufferedBefore = Math.max(bucket.maxBufferedBefore, sample.bufferedBefore);
|
|
94152
|
+
bucket.maxBufferedAfter = Math.max(bucket.maxBufferedAfter, sample.bufferedAfter);
|
|
94153
|
+
protocolPerfBuckets.set(key, bucket);
|
|
94154
|
+
scheduleProtocolPerfFlush();
|
|
94155
|
+
}
|
|
94156
|
+
function writeProtocolPerfFile(record, fallbackLine) {
|
|
94157
|
+
const filePath = PROTOCOL_PERF_FILE;
|
|
94158
|
+
if (!filePath) {
|
|
94159
|
+
console.error(fallbackLine);
|
|
94160
|
+
return;
|
|
94161
|
+
}
|
|
94162
|
+
try {
|
|
94163
|
+
const dir = dirname15(filePath);
|
|
94164
|
+
if (protocolPerfFileDirEnsured !== dir) {
|
|
94165
|
+
mkdirSync22(dir, { recursive: true });
|
|
94166
|
+
protocolPerfFileDirEnsured = dir;
|
|
94167
|
+
}
|
|
94168
|
+
appendFileSync4(filePath, `${JSON.stringify(record)}
|
|
94169
|
+
`, {
|
|
94170
|
+
encoding: "utf8"
|
|
94171
|
+
});
|
|
94172
|
+
} catch (error) {
|
|
94173
|
+
if (!protocolPerfFileWarningEmitted) {
|
|
94174
|
+
protocolPerfFileWarningEmitted = true;
|
|
94175
|
+
console.error(`[Listen Perf] Failed to write LETTA_LISTENER_PERF_FILE=${filePath}`, error);
|
|
94176
|
+
}
|
|
94177
|
+
console.error(fallbackLine);
|
|
94178
|
+
}
|
|
94179
|
+
}
|
|
94180
|
+
function flushProtocolPerfTelemetry() {
|
|
94181
|
+
if (protocolPerfBuckets.size === 0) {
|
|
94182
|
+
protocolPerfWindowStartedAt = 0;
|
|
94183
|
+
return;
|
|
94184
|
+
}
|
|
94185
|
+
const windowMs = Math.max(1, Date.now() - protocolPerfWindowStartedAt);
|
|
94186
|
+
const totals = {
|
|
94187
|
+
count: 0,
|
|
94188
|
+
bytes: 0,
|
|
94189
|
+
stringifyMs: 0,
|
|
94190
|
+
sendMs: 0,
|
|
94191
|
+
maxBufferedBefore: 0,
|
|
94192
|
+
maxBufferedAfter: 0
|
|
94193
|
+
};
|
|
94194
|
+
const buckets = {};
|
|
94195
|
+
const parts = [...protocolPerfBuckets.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([key, bucket]) => {
|
|
94196
|
+
totals.count += bucket.count;
|
|
94197
|
+
totals.bytes += bucket.bytes;
|
|
94198
|
+
totals.stringifyMs += bucket.stringifyMs;
|
|
94199
|
+
totals.sendMs += bucket.sendMs;
|
|
94200
|
+
totals.maxBufferedBefore = Math.max(totals.maxBufferedBefore, bucket.maxBufferedBefore);
|
|
94201
|
+
totals.maxBufferedAfter = Math.max(totals.maxBufferedAfter, bucket.maxBufferedAfter);
|
|
94202
|
+
buckets[key] = {
|
|
94203
|
+
...bucket,
|
|
94204
|
+
avg_bytes: bucket.count > 0 ? bucket.bytes / bucket.count : 0,
|
|
94205
|
+
avg_stringify_ms: bucket.count > 0 ? bucket.stringifyMs / bucket.count : 0,
|
|
94206
|
+
avg_send_ms: bucket.count > 0 ? bucket.sendMs / bucket.count : 0
|
|
94207
|
+
};
|
|
94208
|
+
const stringifyMs = bucket.stringifyMs.toFixed(2);
|
|
94209
|
+
const sendMs = bucket.sendMs.toFixed(2);
|
|
94210
|
+
return `${key}{count=${bucket.count},bytes=${bucket.bytes},stringify_ms=${stringifyMs},send_ms=${sendMs},max_buffered_before=${bucket.maxBufferedBefore},max_buffered_after=${bucket.maxBufferedAfter}}`;
|
|
94211
|
+
});
|
|
94212
|
+
writeProtocolPerfFile({
|
|
94213
|
+
ts: new Date().toISOString(),
|
|
94214
|
+
event: "protocol_emit",
|
|
94215
|
+
window_ms: windowMs,
|
|
94216
|
+
totals,
|
|
94217
|
+
buckets
|
|
94218
|
+
}, `[Listen Perf] protocol_emit window_ms=${windowMs} ${parts.join(" ")}`);
|
|
94219
|
+
protocolPerfBuckets.clear();
|
|
94220
|
+
protocolPerfWindowStartedAt = 0;
|
|
94221
|
+
}
|
|
93902
94222
|
function getCachedDeviceGitContext(cwd2) {
|
|
93903
94223
|
const now = Date.now();
|
|
93904
94224
|
const cached = gitContextCache.get(cwd2);
|
|
@@ -94105,8 +94425,24 @@ function emitProtocolV2Message(socket, runtime, message, scope) {
|
|
|
94105
94425
|
emitted_at: new Date().toISOString(),
|
|
94106
94426
|
idempotency_key: `${message.type}:${eventSeq}:${crypto.randomUUID()}`
|
|
94107
94427
|
};
|
|
94428
|
+
const perfEnabled = PROTOCOL_PERF_ENABLED;
|
|
94429
|
+
const stringifyStartedAt = perfEnabled ? performance2.now() : 0;
|
|
94430
|
+
let payload;
|
|
94108
94431
|
try {
|
|
94109
|
-
|
|
94432
|
+
payload = JSON.stringify(outbound);
|
|
94433
|
+
const stringifyMs = perfEnabled ? performance2.now() - stringifyStartedAt : 0;
|
|
94434
|
+
const bufferedBefore = perfEnabled ? socket.bufferedAmount : 0;
|
|
94435
|
+
const sendStartedAt = perfEnabled ? performance2.now() : 0;
|
|
94436
|
+
socket.send(payload);
|
|
94437
|
+
if (perfEnabled) {
|
|
94438
|
+
recordProtocolPerfTelemetry(getProtocolPerfKey(message), {
|
|
94439
|
+
bytes: Buffer.byteLength(payload),
|
|
94440
|
+
stringifyMs,
|
|
94441
|
+
sendMs: performance2.now() - sendStartedAt,
|
|
94442
|
+
bufferedBefore,
|
|
94443
|
+
bufferedAfter: socket.bufferedAmount
|
|
94444
|
+
});
|
|
94445
|
+
}
|
|
94110
94446
|
} catch (error) {
|
|
94111
94447
|
console.error(`[Listen V2] Failed to emit ${message.type} (seq=${eventSeq})`, error);
|
|
94112
94448
|
safeEmitWsEvent("send", "lifecycle", {
|
|
@@ -94340,7 +94676,7 @@ function emitStreamDelta(socket, runtime, delta, scope, subagentId) {
|
|
|
94340
94676
|
};
|
|
94341
94677
|
emitProtocolV2Message(socket, runtime, message, scope);
|
|
94342
94678
|
}
|
|
94343
|
-
var GIT_CONTEXT_CACHE_TTL_MS = 15000, MAX_GIT_CONTEXT_CACHE_ENTRIES = 64, gitContextCache;
|
|
94679
|
+
var GIT_CONTEXT_CACHE_TTL_MS = 15000, MAX_GIT_CONTEXT_CACHE_ENTRIES = 64, PROTOCOL_PERF_FLUSH_INTERVAL_MS = 1000, PROTOCOL_PERF_ENV_VALUES, PROTOCOL_PERF_ENABLED, PROTOCOL_PERF_FILE, protocolPerfBuckets, protocolPerfFlushTimer = null, protocolPerfWindowStartedAt = 0, protocolPerfFileDirEnsured = null, protocolPerfFileWarningEmitted = false, gitContextCache;
|
|
94344
94680
|
var init_protocol_outbound = __esm(async () => {
|
|
94345
94681
|
init_memoryFilesystem();
|
|
94346
94682
|
init_gitContext();
|
|
@@ -94356,6 +94692,10 @@ var init_protocol_outbound = __esm(async () => {
|
|
|
94356
94692
|
init_permissionMode();
|
|
94357
94693
|
init_runtime4();
|
|
94358
94694
|
await init_commands();
|
|
94695
|
+
PROTOCOL_PERF_ENV_VALUES = new Set(["1", "true", "yes"]);
|
|
94696
|
+
PROTOCOL_PERF_ENABLED = PROTOCOL_PERF_ENV_VALUES.has((process.env.LETTA_LISTENER_PERF ?? "").toLowerCase());
|
|
94697
|
+
PROTOCOL_PERF_FILE = process.env.LETTA_LISTENER_PERF_FILE?.trim() || null;
|
|
94698
|
+
protocolPerfBuckets = new Map;
|
|
94359
94699
|
gitContextCache = new Map;
|
|
94360
94700
|
});
|
|
94361
94701
|
|
|
@@ -97555,7 +97895,7 @@ async function handleSkillCommand(parsed, socket) {
|
|
|
97555
97895
|
const {
|
|
97556
97896
|
existsSync: existsSync30,
|
|
97557
97897
|
lstatSync: lstatSync2,
|
|
97558
|
-
mkdirSync:
|
|
97898
|
+
mkdirSync: mkdirSync23,
|
|
97559
97899
|
rmdirSync,
|
|
97560
97900
|
symlinkSync,
|
|
97561
97901
|
unlinkSync: unlinkSync8
|
|
@@ -97586,7 +97926,7 @@ async function handleSkillCommand(parsed, socket) {
|
|
|
97586
97926
|
}
|
|
97587
97927
|
const linkName = basename13(parsed.skill_path);
|
|
97588
97928
|
const linkPath = join34(globalSkillsDir, linkName);
|
|
97589
|
-
|
|
97929
|
+
mkdirSync23(globalSkillsDir, { recursive: true });
|
|
97590
97930
|
if (existsSync30(linkPath)) {
|
|
97591
97931
|
const stat7 = lstatSync2(linkPath);
|
|
97592
97932
|
if (stat7.isSymbolicLink()) {
|
|
@@ -99786,9 +100126,9 @@ __export(exports_debug2, {
|
|
|
99786
100126
|
debugLog: () => debugLog3
|
|
99787
100127
|
});
|
|
99788
100128
|
import {
|
|
99789
|
-
appendFileSync as
|
|
100129
|
+
appendFileSync as appendFileSync5,
|
|
99790
100130
|
existsSync as existsSync31,
|
|
99791
|
-
mkdirSync as
|
|
100131
|
+
mkdirSync as mkdirSync24,
|
|
99792
100132
|
readdirSync as readdirSync11,
|
|
99793
100133
|
readFileSync as readFileSync22,
|
|
99794
100134
|
unlinkSync as unlinkSync8
|
|
@@ -99809,7 +100149,7 @@ function printDebugLine2(line, level = "log") {
|
|
|
99809
100149
|
const debugFile = getDebugFile2();
|
|
99810
100150
|
if (debugFile) {
|
|
99811
100151
|
try {
|
|
99812
|
-
|
|
100152
|
+
appendFileSync5(debugFile, line, { encoding: "utf8" });
|
|
99813
100153
|
return;
|
|
99814
100154
|
} catch {}
|
|
99815
100155
|
}
|
|
@@ -99835,7 +100175,7 @@ class DebugLogFile2 {
|
|
|
99835
100175
|
return;
|
|
99836
100176
|
this.ensureDir();
|
|
99837
100177
|
try {
|
|
99838
|
-
|
|
100178
|
+
appendFileSync5(this.logPath, line, { encoding: "utf8" });
|
|
99839
100179
|
} catch {}
|
|
99840
100180
|
}
|
|
99841
100181
|
getTail(maxLines = DEFAULT_TAIL_LINES2) {
|
|
@@ -99858,7 +100198,7 @@ class DebugLogFile2 {
|
|
|
99858
100198
|
return;
|
|
99859
100199
|
try {
|
|
99860
100200
|
if (!existsSync31(this.agentDir)) {
|
|
99861
|
-
|
|
100201
|
+
mkdirSync24(this.agentDir, { recursive: true });
|
|
99862
100202
|
}
|
|
99863
100203
|
this.dirCreated = true;
|
|
99864
100204
|
} catch {}
|
|
@@ -99928,10 +100268,10 @@ __export(exports_skills2, {
|
|
|
99928
100268
|
});
|
|
99929
100269
|
import { existsSync as existsSync32 } from "node:fs";
|
|
99930
100270
|
import { readdir as readdir8, readFile as readFile13, realpath as realpath4, stat as stat7 } from "node:fs/promises";
|
|
99931
|
-
import { dirname as
|
|
100271
|
+
import { dirname as dirname16, join as join38 } from "node:path";
|
|
99932
100272
|
import { fileURLToPath as fileURLToPath9 } from "node:url";
|
|
99933
100273
|
function getBundledSkillsPath2() {
|
|
99934
|
-
const thisDir =
|
|
100274
|
+
const thisDir = dirname16(fileURLToPath9(import.meta.url));
|
|
99935
100275
|
if (thisDir.includes("src/agent") || thisDir.includes("src\\agent")) {
|
|
99936
100276
|
return join38(thisDir, "../skills/builtin");
|
|
99937
100277
|
}
|
|
@@ -100125,16 +100465,16 @@ import {
|
|
|
100125
100465
|
existsSync as existsSync33,
|
|
100126
100466
|
readFileSync as fsReadFileSync2,
|
|
100127
100467
|
writeFileSync as fsWriteFileSync2,
|
|
100128
|
-
mkdirSync as
|
|
100468
|
+
mkdirSync as mkdirSync25
|
|
100129
100469
|
} from "node:fs";
|
|
100130
|
-
import { dirname as
|
|
100470
|
+
import { dirname as dirname17 } from "node:path";
|
|
100131
100471
|
async function readFile14(path26) {
|
|
100132
100472
|
return fsReadFileSync2(path26, { encoding: "utf-8" });
|
|
100133
100473
|
}
|
|
100134
100474
|
async function writeFile12(path26, content) {
|
|
100135
|
-
const dir =
|
|
100475
|
+
const dir = dirname17(path26);
|
|
100136
100476
|
if (!existsSync33(dir)) {
|
|
100137
|
-
|
|
100477
|
+
mkdirSync25(dir, { recursive: true });
|
|
100138
100478
|
}
|
|
100139
100479
|
fsWriteFileSync2(path26, content, { encoding: "utf-8", flush: true });
|
|
100140
100480
|
}
|
|
@@ -100142,7 +100482,7 @@ function exists2(path26) {
|
|
|
100142
100482
|
return existsSync33(path26);
|
|
100143
100483
|
}
|
|
100144
100484
|
async function mkdir9(path26, options) {
|
|
100145
|
-
|
|
100485
|
+
mkdirSync25(path26, options);
|
|
100146
100486
|
}
|
|
100147
100487
|
async function readJsonFile(path26) {
|
|
100148
100488
|
const text = await readFile14(path26);
|
|
@@ -100275,7 +100615,7 @@ var exports_bootstrap_tools = {};
|
|
|
100275
100615
|
__export(exports_bootstrap_tools, {
|
|
100276
100616
|
bootstrapBaseToolsIfNeeded: () => bootstrapBaseToolsIfNeeded
|
|
100277
100617
|
});
|
|
100278
|
-
import { existsSync as existsSync34, mkdirSync as
|
|
100618
|
+
import { existsSync as existsSync34, mkdirSync as mkdirSync26, writeFileSync as writeFileSync17 } from "node:fs";
|
|
100279
100619
|
import { homedir as homedir27 } from "node:os";
|
|
100280
100620
|
import { join as join39 } from "node:path";
|
|
100281
100621
|
async function bootstrapBaseToolsIfNeeded() {
|
|
@@ -100285,7 +100625,7 @@ async function bootstrapBaseToolsIfNeeded() {
|
|
|
100285
100625
|
try {
|
|
100286
100626
|
const success = await addBaseToolsToServer();
|
|
100287
100627
|
if (success) {
|
|
100288
|
-
|
|
100628
|
+
mkdirSync26(join39(homedir27(), ".letta"), { recursive: true });
|
|
100289
100629
|
writeFileSync17(MARKER_PATH, new Date().toISOString(), "utf-8");
|
|
100290
100630
|
}
|
|
100291
100631
|
} catch (err) {
|
|
@@ -101989,7 +102329,7 @@ __export(exports_import, {
|
|
|
101989
102329
|
});
|
|
101990
102330
|
import { createReadStream } from "node:fs";
|
|
101991
102331
|
import { chmod, mkdir as mkdir10, readFile as readFile15, writeFile as writeFile13 } from "node:fs/promises";
|
|
101992
|
-
import { dirname as
|
|
102332
|
+
import { dirname as dirname18, resolve as resolve30 } from "node:path";
|
|
101993
102333
|
async function importAgentFromFile(options) {
|
|
101994
102334
|
const client = await getClient();
|
|
101995
102335
|
const resolvedPath = resolve30(options.filePath);
|
|
@@ -102048,7 +102388,7 @@ async function writeSkillFiles(skillDir, files) {
|
|
|
102048
102388
|
}
|
|
102049
102389
|
async function writeSkillFile(skillDir, filePath, content) {
|
|
102050
102390
|
const fullPath = resolve30(skillDir, filePath);
|
|
102051
|
-
await mkdir10(
|
|
102391
|
+
await mkdir10(dirname18(fullPath), { recursive: true });
|
|
102052
102392
|
await writeFile13(fullPath, content, "utf-8");
|
|
102053
102393
|
const isScript = filePath.startsWith("scripts/") || content.trimStart().startsWith("#!");
|
|
102054
102394
|
if (isScript) {
|
|
@@ -124703,7 +125043,7 @@ html.dark .agent-name { color: var(--text-dim); }
|
|
|
124703
125043
|
var init_plan_viewer_template = () => {};
|
|
124704
125044
|
|
|
124705
125045
|
// src/web/generate-plan-viewer.ts
|
|
124706
|
-
import { chmodSync as chmodSync2, existsSync as existsSync37, mkdirSync as
|
|
125046
|
+
import { chmodSync as chmodSync2, existsSync as existsSync37, mkdirSync as mkdirSync29, writeFileSync as writeFileSync20 } from "node:fs";
|
|
124707
125047
|
import { homedir as homedir31 } from "node:os";
|
|
124708
125048
|
import { join as join44 } from "node:path";
|
|
124709
125049
|
async function generateAndOpenPlanViewer(planContent, planFilePath, options) {
|
|
@@ -124716,7 +125056,7 @@ async function generateAndOpenPlanViewer(planContent, planFilePath, options) {
|
|
|
124716
125056
|
const jsonPayload = JSON.stringify(data).replace(/</g, "\\u003c");
|
|
124717
125057
|
const html = plan_viewer_template_default.replace("<!--LETTA_PLAN_DATA_PLACEHOLDER-->", () => jsonPayload);
|
|
124718
125058
|
if (!existsSync37(VIEWERS_DIR)) {
|
|
124719
|
-
|
|
125059
|
+
mkdirSync29(VIEWERS_DIR, { recursive: true, mode: 448 });
|
|
124720
125060
|
}
|
|
124721
125061
|
try {
|
|
124722
125062
|
chmodSync2(VIEWERS_DIR, 448);
|
|
@@ -128026,12 +128366,12 @@ __export(exports_terminalKeybindingInstaller, {
|
|
|
128026
128366
|
import {
|
|
128027
128367
|
copyFileSync,
|
|
128028
128368
|
existsSync as existsSync39,
|
|
128029
|
-
mkdirSync as
|
|
128369
|
+
mkdirSync as mkdirSync30,
|
|
128030
128370
|
readFileSync as readFileSync25,
|
|
128031
128371
|
writeFileSync as writeFileSync21
|
|
128032
128372
|
} from "node:fs";
|
|
128033
128373
|
import { homedir as homedir32, platform as platform6 } from "node:os";
|
|
128034
|
-
import { dirname as
|
|
128374
|
+
import { dirname as dirname19, join as join46 } from "node:path";
|
|
128035
128375
|
function detectTerminalType() {
|
|
128036
128376
|
if (process.env.CURSOR_TRACE_ID || process.env.CURSOR_CHANNEL) {
|
|
128037
128377
|
return "cursor";
|
|
@@ -128122,9 +128462,9 @@ function installKeybinding(keybindingsPath) {
|
|
|
128122
128462
|
if (keybindingExists(keybindingsPath)) {
|
|
128123
128463
|
return { success: true, alreadyExists: true };
|
|
128124
128464
|
}
|
|
128125
|
-
const parentDir =
|
|
128465
|
+
const parentDir = dirname19(keybindingsPath);
|
|
128126
128466
|
if (!existsSync39(parentDir)) {
|
|
128127
|
-
|
|
128467
|
+
mkdirSync30(parentDir, { recursive: true });
|
|
128128
128468
|
}
|
|
128129
128469
|
let keybindings = [];
|
|
128130
128470
|
let backupPath = null;
|
|
@@ -128281,9 +128621,9 @@ return config`);
|
|
|
128281
128621
|
${WEZTERM_DELETE_FIX}
|
|
128282
128622
|
`;
|
|
128283
128623
|
}
|
|
128284
|
-
const parentDir =
|
|
128624
|
+
const parentDir = dirname19(configPath);
|
|
128285
128625
|
if (!existsSync39(parentDir)) {
|
|
128286
|
-
|
|
128626
|
+
mkdirSync30(parentDir, { recursive: true });
|
|
128287
128627
|
}
|
|
128288
128628
|
writeFileSync21(configPath, content, { encoding: "utf-8" });
|
|
128289
128629
|
return {
|
|
@@ -128881,7 +129221,7 @@ __export(exports_custom, {
|
|
|
128881
129221
|
});
|
|
128882
129222
|
import { existsSync as existsSync40 } from "node:fs";
|
|
128883
129223
|
import { readdir as readdir10, readFile as readFile16 } from "node:fs/promises";
|
|
128884
|
-
import { basename as basename14, dirname as
|
|
129224
|
+
import { basename as basename14, dirname as dirname20, join as join47 } from "node:path";
|
|
128885
129225
|
async function getCustomCommands() {
|
|
128886
129226
|
if (cachedCommands !== null) {
|
|
128887
129227
|
return cachedCommands;
|
|
@@ -128942,7 +129282,7 @@ async function parseCommandFile(filePath, rootPath, source2) {
|
|
|
128942
129282
|
const content = await readFile16(filePath, "utf-8");
|
|
128943
129283
|
const { frontmatter, body } = parseFrontmatter(content);
|
|
128944
129284
|
const id = basename14(filePath, ".md");
|
|
128945
|
-
const relativePath =
|
|
129285
|
+
const relativePath = dirname20(filePath).slice(rootPath.length);
|
|
128946
129286
|
const namespace = relativePath.replace(/^[/\\]/, "") || undefined;
|
|
128947
129287
|
let description = getStringField(frontmatter, "description");
|
|
128948
129288
|
if (!description) {
|
|
@@ -134102,14 +134442,14 @@ var init_InputRich = __esm(async () => {
|
|
|
134102
134442
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
134103
134443
|
import {
|
|
134104
134444
|
existsSync as existsSync41,
|
|
134105
|
-
mkdirSync as
|
|
134445
|
+
mkdirSync as mkdirSync31,
|
|
134106
134446
|
mkdtempSync,
|
|
134107
134447
|
readFileSync as readFileSync26,
|
|
134108
134448
|
rmSync as rmSync4,
|
|
134109
134449
|
writeFileSync as writeFileSync22
|
|
134110
134450
|
} from "node:fs";
|
|
134111
134451
|
import { tmpdir as tmpdir6 } from "node:os";
|
|
134112
|
-
import { dirname as
|
|
134452
|
+
import { dirname as dirname21, join as join49 } from "node:path";
|
|
134113
134453
|
function runCommand(command, args, cwd2, input) {
|
|
134114
134454
|
try {
|
|
134115
134455
|
return execFileSync4(command, args, {
|
|
@@ -134356,8 +134696,8 @@ function runGit3(args, cwd2) {
|
|
|
134356
134696
|
}
|
|
134357
134697
|
function writeWorkflow(repoDir, workflowPath, content) {
|
|
134358
134698
|
const absolutePath = join49(repoDir, workflowPath);
|
|
134359
|
-
if (!existsSync41(
|
|
134360
|
-
|
|
134699
|
+
if (!existsSync41(dirname21(absolutePath))) {
|
|
134700
|
+
mkdirSync31(dirname21(absolutePath), { recursive: true });
|
|
134361
134701
|
}
|
|
134362
134702
|
const next = `${content.trimEnd()}
|
|
134363
134703
|
`;
|
|
@@ -138599,7 +138939,7 @@ __export(exports_generate_memory_viewer, {
|
|
|
138599
138939
|
generateAndOpenMemoryViewer: () => generateAndOpenMemoryViewer
|
|
138600
138940
|
});
|
|
138601
138941
|
import { execFile as execFileCb3 } from "node:child_process";
|
|
138602
|
-
import { chmodSync as chmodSync3, existsSync as existsSync42, mkdirSync as
|
|
138942
|
+
import { chmodSync as chmodSync3, existsSync as existsSync42, mkdirSync as mkdirSync32, writeFileSync as writeFileSync23 } from "node:fs";
|
|
138603
138943
|
import { homedir as homedir34 } from "node:os";
|
|
138604
138944
|
import { join as join50 } from "node:path";
|
|
138605
138945
|
import { promisify as promisify13 } from "node:util";
|
|
@@ -138920,7 +139260,7 @@ async function generateAndOpenMemoryViewer(agentId, options) {
|
|
|
138920
139260
|
const jsonPayload = JSON.stringify(data).replace(/</g, "\\u003c");
|
|
138921
139261
|
const html = memory_viewer_template_default.replace("<!--LETTA_DATA_PLACEHOLDER-->", () => jsonPayload);
|
|
138922
139262
|
if (!existsSync42(VIEWERS_DIR2)) {
|
|
138923
|
-
|
|
139263
|
+
mkdirSync32(VIEWERS_DIR2, { recursive: true, mode: 448 });
|
|
138924
139264
|
}
|
|
138925
139265
|
try {
|
|
138926
139266
|
chmodSync3(VIEWERS_DIR2, 448);
|
|
@@ -142730,7 +143070,7 @@ function formatDuration3(ms) {
|
|
|
142730
143070
|
}
|
|
142731
143071
|
return `${(ms / 1000).toFixed(1)}s`;
|
|
142732
143072
|
}
|
|
142733
|
-
function
|
|
143073
|
+
function formatNumber2(n) {
|
|
142734
143074
|
return n.toLocaleString();
|
|
142735
143075
|
}
|
|
142736
143076
|
function formatUsageStats({
|
|
@@ -142752,7 +143092,7 @@ function formatUsageStats({
|
|
|
142752
143092
|
const monthlyCredits = Math.round(balance.monthly_credit_balance);
|
|
142753
143093
|
const purchasedCredits = Math.round(balance.purchased_credit_balance);
|
|
142754
143094
|
const toDollars = (credits) => (credits / 1000).toFixed(2);
|
|
142755
|
-
outputLines.push(`Plan: [${balance.billing_tier}]`, buildAppUrl("/settings/organization/usage"), "", `Available credits: ◎${
|
|
143095
|
+
outputLines.push(`Plan: [${balance.billing_tier}]`, buildAppUrl("/settings/organization/usage"), "", `Available credits: ◎${formatNumber2(totalCredits)} ($${toDollars(totalCredits)})`, `Monthly credits: ◎${formatNumber2(monthlyCredits)} ($${toDollars(monthlyCredits)})`, `Purchased credits: ◎${formatNumber2(purchasedCredits)} ($${toDollars(purchasedCredits)})`);
|
|
142756
143096
|
}
|
|
142757
143097
|
return outputLines.join(`
|
|
142758
143098
|
`);
|
|
@@ -150023,6 +150363,7 @@ function App2({
|
|
|
150023
150363
|
streamingRefreshTimeoutRef.current = setTimeout(() => {
|
|
150024
150364
|
streamingRefreshTimeoutRef.current = null;
|
|
150025
150365
|
if (!buffersRef.current.interrupted) {
|
|
150366
|
+
recordTuiPerf("ui_refresh:tool_output");
|
|
150026
150367
|
refreshDerived();
|
|
150027
150368
|
}
|
|
150028
150369
|
}, 100);
|
|
@@ -150035,6 +150376,9 @@ function App2({
|
|
|
150035
150376
|
};
|
|
150036
150377
|
}, []);
|
|
150037
150378
|
const updateStreamingOutput = import_react104.useCallback((toolCallId, chunk, isStderr = false) => {
|
|
150379
|
+
recordTuiPerf(`tool_output:${isStderr ? "stderr" : "stdout"}`, {
|
|
150380
|
+
bytes: Buffer.byteLength(chunk)
|
|
150381
|
+
});
|
|
150038
150382
|
const lineId = buffersRef.current.toolCallIdToLineId.get(toolCallId);
|
|
150039
150383
|
if (!lineId)
|
|
150040
150384
|
return;
|
|
@@ -150055,6 +150399,7 @@ function App2({
|
|
|
150055
150399
|
setTimeout(() => {
|
|
150056
150400
|
buffersRef.current.pendingRefresh = false;
|
|
150057
150401
|
if (!buffersRef.current.interrupted && (buffersRef.current.commitGeneration || 0) === capturedGeneration) {
|
|
150402
|
+
recordTuiPerf("ui_refresh:stream");
|
|
150058
150403
|
refreshDerived();
|
|
150059
150404
|
}
|
|
150060
150405
|
}, 16);
|
|
@@ -157568,6 +157913,7 @@ var init_App2 = __esm(async () => {
|
|
|
157568
157913
|
init_telemetry();
|
|
157569
157914
|
init_toolset_labels();
|
|
157570
157915
|
init_debug();
|
|
157916
|
+
init_tuiPerf();
|
|
157571
157917
|
init_version();
|
|
157572
157918
|
init_mcp();
|
|
157573
157919
|
init_profile();
|
|
@@ -157748,12 +158094,12 @@ __export(exports_terminalKeybindingInstaller2, {
|
|
|
157748
158094
|
import {
|
|
157749
158095
|
copyFileSync as copyFileSync2,
|
|
157750
158096
|
existsSync as existsSync46,
|
|
157751
|
-
mkdirSync as
|
|
158097
|
+
mkdirSync as mkdirSync33,
|
|
157752
158098
|
readFileSync as readFileSync29,
|
|
157753
158099
|
writeFileSync as writeFileSync25
|
|
157754
158100
|
} from "node:fs";
|
|
157755
158101
|
import { homedir as homedir38, platform as platform7 } from "node:os";
|
|
157756
|
-
import { dirname as
|
|
158102
|
+
import { dirname as dirname22, join as join54 } from "node:path";
|
|
157757
158103
|
function detectTerminalType2() {
|
|
157758
158104
|
if (process.env.CURSOR_TRACE_ID || process.env.CURSOR_CHANNEL) {
|
|
157759
158105
|
return "cursor";
|
|
@@ -157844,9 +158190,9 @@ function installKeybinding2(keybindingsPath) {
|
|
|
157844
158190
|
if (keybindingExists2(keybindingsPath)) {
|
|
157845
158191
|
return { success: true, alreadyExists: true };
|
|
157846
158192
|
}
|
|
157847
|
-
const parentDir =
|
|
158193
|
+
const parentDir = dirname22(keybindingsPath);
|
|
157848
158194
|
if (!existsSync46(parentDir)) {
|
|
157849
|
-
|
|
158195
|
+
mkdirSync33(parentDir, { recursive: true });
|
|
157850
158196
|
}
|
|
157851
158197
|
let keybindings = [];
|
|
157852
158198
|
let backupPath = null;
|
|
@@ -158003,9 +158349,9 @@ return config`);
|
|
|
158003
158349
|
${WEZTERM_DELETE_FIX2}
|
|
158004
158350
|
`;
|
|
158005
158351
|
}
|
|
158006
|
-
const parentDir =
|
|
158352
|
+
const parentDir = dirname22(configPath);
|
|
158007
158353
|
if (!existsSync46(parentDir)) {
|
|
158008
|
-
|
|
158354
|
+
mkdirSync33(parentDir, { recursive: true });
|
|
158009
158355
|
}
|
|
158010
158356
|
writeFileSync25(configPath, content, { encoding: "utf-8" });
|
|
158011
158357
|
return {
|
|
@@ -158573,7 +158919,7 @@ __export(exports_import2, {
|
|
|
158573
158919
|
});
|
|
158574
158920
|
import { createReadStream as createReadStream2 } from "node:fs";
|
|
158575
158921
|
import { chmod as chmod2, mkdir as mkdir11, readFile as readFile19, writeFile as writeFile14 } from "node:fs/promises";
|
|
158576
|
-
import { dirname as
|
|
158922
|
+
import { dirname as dirname23, resolve as resolve35 } from "node:path";
|
|
158577
158923
|
async function importAgentFromFile2(options) {
|
|
158578
158924
|
const client = await getClient();
|
|
158579
158925
|
const resolvedPath = resolve35(options.filePath);
|
|
@@ -158632,7 +158978,7 @@ async function writeSkillFiles2(skillDir, files) {
|
|
|
158632
158978
|
}
|
|
158633
158979
|
async function writeSkillFile2(skillDir, filePath, content) {
|
|
158634
158980
|
const fullPath = resolve35(skillDir, filePath);
|
|
158635
|
-
await mkdir11(
|
|
158981
|
+
await mkdir11(dirname23(fullPath), { recursive: true });
|
|
158636
158982
|
await writeFile14(fullPath, content, "utf-8");
|
|
158637
158983
|
const isScript = filePath.startsWith("scripts/") || content.trimStart().startsWith("#!");
|
|
158638
158984
|
if (isScript) {
|
|
@@ -158742,7 +159088,7 @@ __export(exports_memoryFilesystem2, {
|
|
|
158742
159088
|
MEMORY_FS_MEMORY_DIR: () => MEMORY_FS_MEMORY_DIR2,
|
|
158743
159089
|
MEMORY_FS_AGENTS_DIR: () => MEMORY_FS_AGENTS_DIR2
|
|
158744
159090
|
});
|
|
158745
|
-
import { existsSync as existsSync47, mkdirSync as
|
|
159091
|
+
import { existsSync as existsSync47, mkdirSync as mkdirSync34 } from "node:fs";
|
|
158746
159092
|
import { homedir as homedir40 } from "node:os";
|
|
158747
159093
|
import { join as join56, resolve as resolve36 } from "node:path";
|
|
158748
159094
|
function getMemoryFilesystemRoot2(agentId, homeDir = homedir40()) {
|
|
@@ -158778,10 +159124,10 @@ function ensureMemoryFilesystemDirs2(agentId, homeDir = homedir40()) {
|
|
|
158778
159124
|
const root = getMemoryFilesystemRoot2(agentId, homeDir);
|
|
158779
159125
|
const systemDir = getMemorySystemDir2(agentId, homeDir);
|
|
158780
159126
|
if (!existsSync47(root)) {
|
|
158781
|
-
|
|
159127
|
+
mkdirSync34(root, { recursive: true });
|
|
158782
159128
|
}
|
|
158783
159129
|
if (!existsSync47(systemDir)) {
|
|
158784
|
-
|
|
159130
|
+
mkdirSync34(systemDir, { recursive: true });
|
|
158785
159131
|
}
|
|
158786
159132
|
}
|
|
158787
159133
|
async function isMemfsEnabledOnServer2(agentId) {
|
|
@@ -164102,51 +164448,141 @@ async function runListenSubcommand(argv) {
|
|
|
164102
164448
|
}
|
|
164103
164449
|
}
|
|
164104
164450
|
|
|
164105
|
-
// src/cli/subcommands/
|
|
164451
|
+
// src/cli/subcommands/memory.ts
|
|
164106
164452
|
init_memoryGit();
|
|
164107
|
-
import { cpSync, existsSync as existsSync30, mkdirSync as
|
|
164453
|
+
import { cpSync, existsSync as existsSync30, mkdirSync as mkdirSync23, rmSync as rmSync3, statSync as statSync8 } from "node:fs";
|
|
164108
164454
|
import { readdir as readdir7 } from "node:fs/promises";
|
|
164109
164455
|
import { homedir as homedir23 } from "node:os";
|
|
164110
164456
|
import { join as join34 } from "node:path";
|
|
164111
164457
|
import { parseArgs as parseArgs8 } from "node:util";
|
|
164458
|
+
|
|
164459
|
+
// src/cli/subcommands/memoryTokens.ts
|
|
164460
|
+
init_systemPromptSize();
|
|
164461
|
+
var DEFAULT_TOP = 20;
|
|
164462
|
+
var USAGE_EXIT = 64;
|
|
164463
|
+
var IO_EXIT = 65;
|
|
164464
|
+
function parsePositiveInt(raw, fallback) {
|
|
164465
|
+
if (raw === undefined)
|
|
164466
|
+
return fallback;
|
|
164467
|
+
if (!/^\d+$/.test(raw))
|
|
164468
|
+
return null;
|
|
164469
|
+
return Number.parseInt(raw, 10);
|
|
164470
|
+
}
|
|
164471
|
+
function formatNumber(value) {
|
|
164472
|
+
return value.toLocaleString("en-US");
|
|
164473
|
+
}
|
|
164474
|
+
function printText(total, files, top, quiet) {
|
|
164475
|
+
console.log("System prompt token estimate");
|
|
164476
|
+
console.log(` Total: ${formatNumber(total)} tokens`);
|
|
164477
|
+
if (quiet || top <= 0 || files.length === 0) {
|
|
164478
|
+
return;
|
|
164479
|
+
}
|
|
164480
|
+
const ranked = [...files].sort((a, b) => b.tokens - a.tokens);
|
|
164481
|
+
const limited = ranked.slice(0, top);
|
|
164482
|
+
console.log("");
|
|
164483
|
+
console.log("Top files:");
|
|
164484
|
+
console.log(` ${"tokens".padStart(8)} path`);
|
|
164485
|
+
for (const row of limited) {
|
|
164486
|
+
console.log(` ${formatNumber(row.tokens).padStart(8)} ${row.path}`);
|
|
164487
|
+
}
|
|
164488
|
+
}
|
|
164489
|
+
function printJson(total, files) {
|
|
164490
|
+
console.log(JSON.stringify({
|
|
164491
|
+
total_tokens: total,
|
|
164492
|
+
files
|
|
164493
|
+
}, null, 2));
|
|
164494
|
+
}
|
|
164495
|
+
function resolveMemoryDir3(options) {
|
|
164496
|
+
if (options.memoryDir)
|
|
164497
|
+
return options.memoryDir;
|
|
164498
|
+
if (process.env.MEMORY_DIR)
|
|
164499
|
+
return process.env.MEMORY_DIR;
|
|
164500
|
+
if (options.agentMemoryDir)
|
|
164501
|
+
return options.agentMemoryDir;
|
|
164502
|
+
return null;
|
|
164503
|
+
}
|
|
164504
|
+
async function runMemoryTokensAction(options) {
|
|
164505
|
+
const format2 = options.format ?? "text";
|
|
164506
|
+
if (format2 !== "text" && format2 !== "json") {
|
|
164507
|
+
console.error(`Invalid --format: ${format2} (expected text or json)`);
|
|
164508
|
+
return USAGE_EXIT;
|
|
164509
|
+
}
|
|
164510
|
+
const top = parsePositiveInt(options.top, DEFAULT_TOP);
|
|
164511
|
+
if (top === null) {
|
|
164512
|
+
console.error(`Invalid --top: ${options.top} (expected non-negative integer)`);
|
|
164513
|
+
return USAGE_EXIT;
|
|
164514
|
+
}
|
|
164515
|
+
const memoryDir = resolveMemoryDir3(options);
|
|
164516
|
+
if (!memoryDir) {
|
|
164517
|
+
console.error("Missing memory dir. Set --memory-dir, --agent, $MEMORY_DIR, or $LETTA_AGENT_ID.");
|
|
164518
|
+
return USAGE_EXIT;
|
|
164519
|
+
}
|
|
164520
|
+
let estimate;
|
|
164521
|
+
try {
|
|
164522
|
+
estimate = estimateSystemPromptSize(memoryDir);
|
|
164523
|
+
} catch (error) {
|
|
164524
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
164525
|
+
console.error(`Failed to read memory dir: ${message}`);
|
|
164526
|
+
return IO_EXIT;
|
|
164527
|
+
}
|
|
164528
|
+
const { total, files } = estimate;
|
|
164529
|
+
if (format2 === "json") {
|
|
164530
|
+
printJson(total, files);
|
|
164531
|
+
} else {
|
|
164532
|
+
printText(total, files, top, options.quiet);
|
|
164533
|
+
}
|
|
164534
|
+
return 0;
|
|
164535
|
+
}
|
|
164536
|
+
|
|
164537
|
+
// src/cli/subcommands/memory.ts
|
|
164112
164538
|
function printUsage5() {
|
|
164113
164539
|
console.log(`
|
|
164114
164540
|
Usage:
|
|
164115
|
-
letta
|
|
164116
|
-
letta
|
|
164117
|
-
letta
|
|
164118
|
-
letta
|
|
164119
|
-
letta
|
|
164120
|
-
letta
|
|
164121
|
-
letta
|
|
164541
|
+
letta memory status [--agent <id>]
|
|
164542
|
+
letta memory diff [--agent <id>]
|
|
164543
|
+
letta memory backup [--agent <id>]
|
|
164544
|
+
letta memory backups [--agent <id>]
|
|
164545
|
+
letta memory restore --from <backup> --force [--agent <id>]
|
|
164546
|
+
letta memory export --agent <id> --out <dir>
|
|
164547
|
+
letta memory pull [--agent <id>]
|
|
164548
|
+
letta memory tokens [--memory-dir <path>] [--agent <id>] [--top <N>]
|
|
164549
|
+
[--format text|json] [--quiet]
|
|
164122
164550
|
|
|
164123
164551
|
Notes:
|
|
164124
|
-
-
|
|
164125
|
-
-
|
|
164552
|
+
- Most actions require agent id via --agent or LETTA_AGENT_ID and output JSON.
|
|
164553
|
+
- \`tokens\` additionally accepts --memory-dir or $MEMORY_DIR; reports the
|
|
164554
|
+
estimated token size of system/. Policy (whether a size is concerning) is
|
|
164555
|
+
up to the caller.
|
|
164126
164556
|
- Memory is git-backed. Use git commands for commit/push.
|
|
164127
164557
|
|
|
164128
164558
|
Examples:
|
|
164129
|
-
LETTA_AGENT_ID=agent-123 letta
|
|
164130
|
-
letta
|
|
164131
|
-
letta
|
|
164132
|
-
letta
|
|
164559
|
+
LETTA_AGENT_ID=agent-123 letta memory status
|
|
164560
|
+
letta memory pull --agent agent-123
|
|
164561
|
+
letta memory backup --agent agent-123
|
|
164562
|
+
letta memory export --agent agent-123 --out /tmp/letta-memory-agent-123
|
|
164563
|
+
letta memory tokens
|
|
164564
|
+
letta memory tokens --memory-dir ~/.letta/agents/agent-123/memory --format json
|
|
164133
164565
|
`.trim());
|
|
164134
164566
|
}
|
|
164135
164567
|
function getAgentId5(agentFromArgs, agentIdFromArgs) {
|
|
164136
164568
|
return agentFromArgs || agentIdFromArgs || process.env.LETTA_AGENT_ID || "";
|
|
164137
164569
|
}
|
|
164138
|
-
var
|
|
164570
|
+
var MEMORY_OPTIONS = {
|
|
164139
164571
|
help: { type: "boolean", short: "h" },
|
|
164140
164572
|
agent: { type: "string" },
|
|
164141
164573
|
"agent-id": { type: "string" },
|
|
164142
164574
|
from: { type: "string" },
|
|
164143
164575
|
force: { type: "boolean" },
|
|
164144
|
-
out: { type: "string" }
|
|
164576
|
+
out: { type: "string" },
|
|
164577
|
+
"memory-dir": { type: "string" },
|
|
164578
|
+
top: { type: "string" },
|
|
164579
|
+
format: { type: "string" },
|
|
164580
|
+
quiet: { type: "boolean" }
|
|
164145
164581
|
};
|
|
164146
|
-
function
|
|
164582
|
+
function parseMemoryArgs(argv) {
|
|
164147
164583
|
return parseArgs8({
|
|
164148
164584
|
args: argv,
|
|
164149
|
-
options:
|
|
164585
|
+
options: MEMORY_OPTIONS,
|
|
164150
164586
|
strict: true,
|
|
164151
164587
|
allowPositionals: true
|
|
164152
164588
|
});
|
|
@@ -164198,10 +164634,10 @@ function resolveBackupPath(agentId, from) {
|
|
|
164198
164634
|
}
|
|
164199
164635
|
return join34(getAgentRoot(agentId), from);
|
|
164200
164636
|
}
|
|
164201
|
-
async function
|
|
164637
|
+
async function runMemorySubcommand(argv) {
|
|
164202
164638
|
let parsed;
|
|
164203
164639
|
try {
|
|
164204
|
-
parsed =
|
|
164640
|
+
parsed = parseMemoryArgs(argv);
|
|
164205
164641
|
} catch (error) {
|
|
164206
164642
|
const message = error instanceof Error ? error.message : String(error);
|
|
164207
164643
|
console.error(`Error: ${message}`);
|
|
@@ -164214,6 +164650,15 @@ async function runMemfsSubcommand(argv) {
|
|
|
164214
164650
|
return 0;
|
|
164215
164651
|
}
|
|
164216
164652
|
const agentId = getAgentId5(parsed.values.agent, parsed.values["agent-id"]);
|
|
164653
|
+
if (action === "tokens") {
|
|
164654
|
+
return runMemoryTokensAction({
|
|
164655
|
+
memoryDir: parsed.values["memory-dir"],
|
|
164656
|
+
agentMemoryDir: agentId ? getMemoryRoot(agentId) : undefined,
|
|
164657
|
+
top: parsed.values.top,
|
|
164658
|
+
format: parsed.values.format,
|
|
164659
|
+
quiet: Boolean(parsed.values.quiet)
|
|
164660
|
+
});
|
|
164661
|
+
}
|
|
164217
164662
|
if (!agentId) {
|
|
164218
164663
|
console.error("Missing agent id. Set LETTA_AGENT_ID or pass --agent/--agent-id.");
|
|
164219
164664
|
return 1;
|
|
@@ -164326,7 +164771,7 @@ async function runMemfsSubcommand(argv) {
|
|
|
164326
164771
|
return 1;
|
|
164327
164772
|
}
|
|
164328
164773
|
} else {
|
|
164329
|
-
|
|
164774
|
+
mkdirSync23(out, { recursive: true });
|
|
164330
164775
|
}
|
|
164331
164776
|
cpSync(root, out, { recursive: true });
|
|
164332
164777
|
console.log(JSON.stringify({ exportedFrom: root, exportedTo: out, agentId }, null, 2));
|
|
@@ -164685,8 +165130,9 @@ async function runSubcommand(argv) {
|
|
|
164685
165130
|
return null;
|
|
164686
165131
|
}
|
|
164687
165132
|
switch (command) {
|
|
165133
|
+
case "memory":
|
|
164688
165134
|
case "memfs":
|
|
164689
|
-
return
|
|
165135
|
+
return runMemorySubcommand(rest);
|
|
164690
165136
|
case "agents":
|
|
164691
165137
|
return runAgentsSubcommand(rest);
|
|
164692
165138
|
case "messages":
|
|
@@ -166855,7 +167301,7 @@ USAGE
|
|
|
166855
167301
|
|
|
166856
167302
|
# maintenance
|
|
166857
167303
|
letta update Manually check for updates and install if available
|
|
166858
|
-
letta
|
|
167304
|
+
letta memory ... Memory filesystem subcommands
|
|
166859
167305
|
letta agents ... Agents subcommands (JSON-only)
|
|
166860
167306
|
letta messages ... Messages subcommands (JSON-only)
|
|
166861
167307
|
letta blocks ... Blocks subcommands (JSON-only)
|
|
@@ -166864,14 +167310,16 @@ USAGE
|
|
|
166864
167310
|
OPTIONS
|
|
166865
167311
|
${renderCliOptionsHelp()}
|
|
166866
167312
|
|
|
166867
|
-
SUBCOMMANDS
|
|
166868
|
-
letta
|
|
166869
|
-
letta
|
|
166870
|
-
letta
|
|
166871
|
-
letta
|
|
166872
|
-
letta
|
|
166873
|
-
letta
|
|
166874
|
-
letta
|
|
167313
|
+
SUBCOMMANDS
|
|
167314
|
+
letta memory status --agent <id>
|
|
167315
|
+
letta memory diff --agent <id>
|
|
167316
|
+
letta memory resolve --agent <id> --resolutions '<JSON>'
|
|
167317
|
+
letta memory backup --agent <id>
|
|
167318
|
+
letta memory backups --agent <id>
|
|
167319
|
+
letta memory restore --agent <id> --from <backup> --force
|
|
167320
|
+
letta memory export --agent <id> --out <dir>
|
|
167321
|
+
letta memory pull --agent <id>
|
|
167322
|
+
letta memory tokens [--memory-dir <path>] [--agent <id>] [--format text|json]
|
|
166875
167323
|
letta agents list [--query <text> | --name <name> | --tags <tags>]
|
|
166876
167324
|
letta messages search --query <text> [--all-agents]
|
|
166877
167325
|
letta messages list [--agent <id>]
|
|
@@ -168205,4 +168653,4 @@ Error during initialization: ${message}`);
|
|
|
168205
168653
|
}
|
|
168206
168654
|
main();
|
|
168207
168655
|
|
|
168208
|
-
//# debugId=
|
|
168656
|
+
//# debugId=22CE2EBE31776A7764756E2164756E21
|
package/package.json
CHANGED
|
@@ -30,7 +30,7 @@ This is the recommended flow:
|
|
|
30
30
|
|
|
31
31
|
1. **Export the source agent's memfs to a temp directory**
|
|
32
32
|
```bash
|
|
33
|
-
letta
|
|
33
|
+
letta memory export --agent <source-agent-id> --out /tmp/letta-memory-<source-agent-id>
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
2. **Copy the files you want into your own memfs**
|
|
@@ -39,13 +39,16 @@ This is the recommended flow:
|
|
|
39
39
|
|
|
40
40
|
Example:
|
|
41
41
|
```bash
|
|
42
|
-
cp -r /tmp/letta-
|
|
43
|
-
cp /tmp/letta-
|
|
42
|
+
cp -r /tmp/letta-memory-agent-abc123/system/project ~/.letta/agents/$LETTA_AGENT_ID/memory/system/
|
|
43
|
+
cp /tmp/letta-memory-agent-abc123/notes.md ~/.letta/agents/$LETTA_AGENT_ID/memory/
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
3. **
|
|
46
|
+
3. **Commit and push the memory repo**
|
|
47
47
|
```bash
|
|
48
|
-
letta
|
|
48
|
+
cd ~/.letta/agents/$LETTA_AGENT_ID/memory
|
|
49
|
+
git add system/project notes.md
|
|
50
|
+
git commit -m "Import memory from source agent"
|
|
51
|
+
git push
|
|
49
52
|
```
|
|
50
53
|
|
|
51
54
|
This gives you full control over what you bring across and keeps everything consistent with memfs.
|
|
@@ -107,15 +110,18 @@ Scenario: You're a new agent and want to inherit memory from an existing agent "
|
|
|
107
110
|
|
|
108
111
|
2. **Export their memfs:**
|
|
109
112
|
```bash
|
|
110
|
-
letta
|
|
113
|
+
letta memory export --agent agent-abc123 --out /tmp/letta-memory-agent-abc123
|
|
111
114
|
```
|
|
112
115
|
|
|
113
116
|
3. **Copy the relevant files into your memfs:**
|
|
114
117
|
```bash
|
|
115
|
-
cp -r /tmp/letta-
|
|
118
|
+
cp -r /tmp/letta-memory-agent-abc123/system/project ~/.letta/agents/$LETTA_AGENT_ID/memory/system/
|
|
116
119
|
```
|
|
117
120
|
|
|
118
|
-
4. **
|
|
121
|
+
4. **Commit and push:**
|
|
119
122
|
```bash
|
|
120
|
-
letta
|
|
123
|
+
cd ~/.letta/agents/$LETTA_AGENT_ID/memory
|
|
124
|
+
git add system/project
|
|
125
|
+
git commit -m "Import project memory"
|
|
126
|
+
git push
|
|
121
127
|
```
|