@letta-ai/letta-code 0.24.2 → 0.24.4

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.2",
3272
+ version: "0.24.4",
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
- memfs: new Set(["status", "help", "backups", "export"]),
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/cli/helpers/systemPromptWarning.ts
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") / STARTUP_SYSTEM_PROMPT_ESTIMATED_BYTES_PER_TOKEN);
83500
+ return Math.ceil(Buffer.byteLength(text, "utf8") / SYSTEM_PROMPT_BYTES_PER_TOKEN);
83500
83501
  }
83501
- function estimateSystemPromptTokensFromMemoryDir(memoryDir) {
83502
- const systemDir = join29(memoryDir, "system");
83503
- if (!existsSync26(systemDir)) {
83504
- return 0;
83502
+ function normalizePath2(value) {
83503
+ return value.replaceAll("\\", "/");
83504
+ }
83505
+ function walkMarkdownFiles(dir) {
83506
+ if (!existsSync26(dir)) {
83507
+ return [];
83505
83508
  }
83506
- const walkMarkdownFiles = (dir) => {
83507
- if (!existsSync26(dir)) {
83508
- return [];
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 out = [];
83511
- const entries = readdirSync8(dir, { withFileTypes: true });
83512
- for (const entry of entries) {
83513
- if (entry.name.startsWith(".")) {
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
- return out;
83529
- };
83530
- return walkMarkdownFiles(systemDir).sort().reduce((sum, filePath) => {
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
- return sum + estimateSystemTokens(text);
83533
- }, 0);
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 };
83534
83540
  }
83541
+ function estimateSystemPromptTokensFromMemoryDir(memoryDir) {
83542
+ return estimateSystemPromptSize(memoryDir).total;
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, STARTUP_SYSTEM_PROMPT_ESTIMATED_BYTES_PER_TOKEN = 4, systemPromptDoctorStateByAgent;
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 mkdirSync20,
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
- mkdirSync20(this.agentDir, { recursive: true });
87426
+ mkdirSync21(this.agentDir, { recursive: true });
87291
87427
  }
87292
87428
  this.dirCreated = true;
87293
87429
  } catch (e) {
@@ -87427,6 +87563,60 @@ class StreamProcessor {
87427
87563
  }
87428
87564
 
87429
87565
  // src/cli/helpers/stream.ts
87566
+ function summarizeStreamForDebug(stream2) {
87567
+ if (!stream2 || typeof stream2 !== "object") {
87568
+ return `type=${typeof stream2}`;
87569
+ }
87570
+ const record = stream2;
87571
+ const ctor = stream2.constructor?.name;
87572
+ const controller = record.controller && typeof record.controller === "object" ? record.controller : null;
87573
+ const keys = Object.keys(record).slice(0, 8);
87574
+ return [
87575
+ `ctor=${ctor ?? "unknown"}`,
87576
+ `asyncIterator=${typeof record[Symbol.asyncIterator]}`,
87577
+ `controller=${typeof record.controller}`,
87578
+ `controllerAbort=${typeof controller?.abort}`,
87579
+ `controllerSignal=${typeof controller?.signal}`,
87580
+ keys.length > 0 ? `keys=${keys.join(",")}` : "keys=(none)"
87581
+ ].join(" ");
87582
+ }
87583
+ function summarizeChunkForDebug(chunk) {
87584
+ if (!chunk) {
87585
+ return "none";
87586
+ }
87587
+ const record = chunk;
87588
+ const parts = [`message_type=${chunk.message_type ?? "unknown"}`];
87589
+ for (const key of ["run_id", "seq_id", "id", "otid", "tool_call_id"]) {
87590
+ const value = record[key];
87591
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
87592
+ parts.push(`${key}=${value}`);
87593
+ }
87594
+ }
87595
+ if (chunk.message_type === "stop_reason") {
87596
+ parts.push(`stop_reason=${String(record.stop_reason ?? "unknown")}`);
87597
+ }
87598
+ const toolCalls = record.tool_calls;
87599
+ if (Array.isArray(toolCalls)) {
87600
+ parts.push(`tool_calls=${toolCalls.length}`);
87601
+ }
87602
+ return parts.join(" ");
87603
+ }
87604
+ function abortStreamController(stream2, reason) {
87605
+ const controller = stream2.controller;
87606
+ if (!controller || typeof controller !== "object") {
87607
+ debugWarn("drainStream", "stream.controller is unavailable during %s - cannot abort HTTP request (%s)", reason, summarizeStreamForDebug(stream2));
87608
+ return;
87609
+ }
87610
+ const controllerRecord = controller;
87611
+ if (controllerRecord.signal?.aborted) {
87612
+ return;
87613
+ }
87614
+ if (typeof controllerRecord.abort !== "function") {
87615
+ debugWarn("drainStream", "stream.controller.abort is unavailable during %s - cannot abort HTTP request (%s)", reason, summarizeStreamForDebug(stream2));
87616
+ return;
87617
+ }
87618
+ controllerRecord.abort();
87619
+ }
87430
87620
  function hasPaginatedItems(response) {
87431
87621
  return !Array.isArray(response) && typeof response.getPaginatedItems === "function";
87432
87622
  }
@@ -87504,28 +87694,27 @@ async function drainStream(stream2, buffers, refresh, abortSignal, onFirstMessag
87504
87694
  let stopReason = null;
87505
87695
  let hasCalledFirstMessage = false;
87506
87696
  let fallbackError = null;
87697
+ let lastChunkDebugSummary = "none";
87507
87698
  let abortedViaListener = false;
87508
87699
  const startAbortGen = buffers.abortGeneration || 0;
87509
87700
  const abortHandler = () => {
87510
87701
  abortedViaListener = true;
87511
- if (!stream2.controller) {
87512
- debugWarn("drainStream", "stream.controller is undefined - cannot abort HTTP request");
87513
- return;
87514
- }
87515
- if (!stream2.controller.signal.aborted) {
87516
- stream2.controller.abort();
87517
- }
87702
+ abortStreamController(stream2, "abort_signal");
87518
87703
  };
87519
87704
  if (abortSignal && !abortSignal.aborted) {
87520
87705
  abortSignal.addEventListener("abort", abortHandler, { once: true });
87521
87706
  } else if (abortSignal?.aborted) {
87522
87707
  abortedViaListener = true;
87523
- if (stream2.controller && !stream2.controller.signal.aborted) {
87524
- stream2.controller.abort();
87525
- }
87708
+ abortStreamController(stream2, "pre_aborted_signal");
87526
87709
  }
87527
87710
  try {
87711
+ const asyncIterator = stream2[Symbol.asyncIterator];
87712
+ if (typeof asyncIterator !== "function") {
87713
+ throw new TypeError(`Stream is not async iterable (${summarizeStreamForDebug(stream2)})`);
87714
+ }
87528
87715
  for await (const chunk of stream2) {
87716
+ lastChunkDebugSummary = summarizeChunkForDebug(chunk);
87717
+ recordTuiJsonPayload(`stream_chunk:${chunk.message_type ?? "unknown"}`, chunk);
87529
87718
  if ((buffers.abortGeneration || 0) !== startAbortGen) {
87530
87719
  stopReason = "cancelled";
87531
87720
  queueMicrotask(refresh);
@@ -87581,6 +87770,7 @@ async function drainStream(stream2, buffers, refresh, abortSignal, onFirstMessag
87581
87770
  shouldAccumulate = shouldOutputChunk;
87582
87771
  }
87583
87772
  if (shouldAccumulate) {
87773
+ recordTuiJsonPayload(`stream_accumulate:${chunk.message_type ?? "unknown"}`, chunk);
87584
87774
  onChunk(buffers, chunk, contextTracker);
87585
87775
  queueMicrotask(refresh);
87586
87776
  }
@@ -87592,7 +87782,10 @@ async function drainStream(stream2, buffers, refresh, abortSignal, onFirstMessag
87592
87782
  const errorMessage = e instanceof Error ? e.message : String(e);
87593
87783
  const sdkDiagnostic = consumeLastSDKDiagnostic();
87594
87784
  const errorMessageWithDiagnostic = sdkDiagnostic ? `${errorMessage} [${sdkDiagnostic}]` : errorMessage;
87595
- debugWarn("drainStream", "Stream error caught:", errorMessage);
87785
+ debugWarn("drainStream", "Stream error caught: %s last_chunk=%s stream=%s", errorMessageWithDiagnostic, lastChunkDebugSummary, summarizeStreamForDebug(stream2));
87786
+ if (e instanceof Error && e.stack) {
87787
+ debugWarn("drainStream", "Stream error stack: %s", e.stack);
87788
+ }
87596
87789
  if (!streamProcessor.lastRunId && e instanceof APIError2 && e.error) {
87597
87790
  const errorObj = e.error;
87598
87791
  if ("run_id" in errorObj && typeof errorObj.run_id === "string") {
@@ -87663,6 +87856,7 @@ async function drainStream(stream2, buffers, refresh, abortSignal, onFirstMessag
87663
87856
  }
87664
87857
  async function drainStreamWithResume(stream2, buffers, refresh, abortSignal, onFirstMessage, onChunkProcessed, contextTracker, seenSeqIdThreshold) {
87665
87858
  const overallStartTime = performance.now();
87859
+ recordTuiPerf("stream_lifecycle:start");
87666
87860
  const streamRequestContext = getStreamRequestContext(stream2);
87667
87861
  const streamOtid = streamRequestContext?.otid ?? null;
87668
87862
  let _client;
@@ -87785,6 +87979,9 @@ async function drainStreamWithResume(stream2, buffers, refresh, abortSignal, onF
87785
87979
  markCurrentLineAsFinished(buffers);
87786
87980
  }
87787
87981
  result.apiDurationMs = performance.now() - overallStartTime;
87982
+ recordTuiPerf(`stream_lifecycle:end:${result.stopReason}`, {
87983
+ ms: result.apiDurationMs
87984
+ });
87788
87985
  return result;
87789
87986
  }
87790
87987
  var FALLBACK_RUN_DISCOVERY_TIMEOUT_MS = 5000;
@@ -87795,6 +87992,7 @@ var init_stream = __esm(async () => {
87795
87992
  init_debug();
87796
87993
  init_streamAbortRelay();
87797
87994
  init_timing();
87995
+ init_tuiPerf();
87798
87996
  init_chunkLog();
87799
87997
  await __promiseAll([
87800
87998
  init_message(),
@@ -88893,22 +89091,12 @@ function emitToolExecutionFinishedEvents(socket, runtime, params) {
88893
89091
  }
88894
89092
  function createToolExecutionOutputEmitter(socket, runtime, params) {
88895
89093
  const outputByToolCallId = new Map;
88896
- return (toolCallId, chunk, isStderr = false) => {
88897
- if (!toolCallId || chunk.length === 0) {
89094
+ const emitToolOutput = (toolCallId, outputState) => {
89095
+ if (!outputState.dirty) {
88898
89096
  return;
88899
89097
  }
88900
- const existing = outputByToolCallId.get(toolCallId);
88901
- const outputState = existing ?? {
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);
89098
+ outputState.dirty = false;
89099
+ outputState.lastEmittedAt = Date.now();
88912
89100
  const stdout = normalizeStreamingOutputLines(outputState.stdout);
88913
89101
  const stderr = normalizeStreamingOutputLines(outputState.stderr);
88914
89102
  const toolReturn = [stdout?.join(`
@@ -88938,6 +89126,52 @@ function createToolExecutionOutputEmitter(socket, runtime, params) {
88938
89126
  conversation_id: params.conversationId
88939
89127
  });
88940
89128
  };
89129
+ const flushToolOutput = (toolCallId, outputState) => {
89130
+ if (outputState.timer) {
89131
+ clearTimeout(outputState.timer);
89132
+ outputState.timer = null;
89133
+ }
89134
+ emitToolOutput(toolCallId, outputState);
89135
+ };
89136
+ const emitter = (toolCallId, chunk, isStderr = false) => {
89137
+ if (!toolCallId || chunk.length === 0) {
89138
+ return;
89139
+ }
89140
+ const existing = outputByToolCallId.get(toolCallId);
89141
+ const outputState = existing ?? {
89142
+ messageId: `message-tool-return-stream-${toolCallId}`,
89143
+ stdout: "",
89144
+ stderr: "",
89145
+ dirty: false,
89146
+ lastEmittedAt: 0,
89147
+ timer: null
89148
+ };
89149
+ if (isStderr) {
89150
+ outputState.stderr = appendStreamingOutputWithCap(outputState.stderr, chunk);
89151
+ } else {
89152
+ outputState.stdout = appendStreamingOutputWithCap(outputState.stdout, chunk);
89153
+ }
89154
+ outputByToolCallId.set(toolCallId, outputState);
89155
+ outputState.dirty = true;
89156
+ const now = Date.now();
89157
+ const elapsed = now - outputState.lastEmittedAt;
89158
+ if (elapsed >= STREAMING_TOOL_OUTPUT_EMIT_INTERVAL_MS) {
89159
+ flushToolOutput(toolCallId, outputState);
89160
+ return;
89161
+ }
89162
+ if (!outputState.timer) {
89163
+ outputState.timer = setTimeout(() => {
89164
+ outputState.timer = null;
89165
+ emitToolOutput(toolCallId, outputState);
89166
+ }, STREAMING_TOOL_OUTPUT_EMIT_INTERVAL_MS - elapsed);
89167
+ }
89168
+ };
89169
+ emitter.flush = () => {
89170
+ for (const [toolCallId, outputState] of outputByToolCallId.entries()) {
89171
+ flushToolOutput(toolCallId, outputState);
89172
+ }
89173
+ };
89174
+ return emitter;
88941
89175
  }
88942
89176
  function getInterruptApprovalsForEmission(runtime, params) {
88943
89177
  if (params.lastExecutionResults && params.lastExecutionResults.length > 0) {
@@ -89055,7 +89289,7 @@ function stashRecoveredApprovalInterrupts(runtime, recovered) {
89055
89289
  clearRecoveredApprovalState(runtime);
89056
89290
  return true;
89057
89291
  }
89058
- var INTERRUPT_TOOL_RETURN_MAX_CHARS, STREAMING_TOOL_OUTPUT_MAX_CHARS;
89292
+ var INTERRUPT_TOOL_RETURN_MAX_CHARS, STREAMING_TOOL_OUTPUT_MAX_CHARS, STREAMING_TOOL_OUTPUT_EMIT_INTERVAL_MS = 100;
89059
89293
  var init_interrupts = __esm(async () => {
89060
89294
  init_approval_result_normalization();
89061
89295
  init_constants();
@@ -89892,16 +90126,21 @@ async function resolveRecoveredApprovalResponse(runtime, socket, response, proce
89892
90126
  runtime.currentToolsetPreference = preparedToolContext.toolsetPreference;
89893
90127
  runtime.currentLoadedTools = preparedToolContext.preparedToolContext.loadedToolNames;
89894
90128
  try {
89895
- const approvalResults = await executeApprovalBatch(decisions, undefined, {
89896
- abortSignal: recoveryAbortController.signal,
89897
- onStreamingOutput: emitToolExecutionOutput,
89898
- toolContextId: preparedToolContext.preparedToolContext.contextId,
89899
- workingDirectory,
89900
- parentScope: recovered.agentId && recovered.conversationId ? {
89901
- agentId: recovered.agentId,
89902
- conversationId: recovered.conversationId
89903
- } : undefined
89904
- });
90129
+ let approvalResults;
90130
+ try {
90131
+ approvalResults = await executeApprovalBatch(decisions, undefined, {
90132
+ abortSignal: recoveryAbortController.signal,
90133
+ onStreamingOutput: emitToolExecutionOutput,
90134
+ toolContextId: preparedToolContext.preparedToolContext.contextId,
90135
+ workingDirectory,
90136
+ parentScope: recovered.agentId && recovered.conversationId ? {
90137
+ agentId: recovered.agentId,
90138
+ conversationId: recovered.conversationId
90139
+ } : undefined
90140
+ });
90141
+ } finally {
90142
+ emitToolExecutionOutput.flush();
90143
+ }
89905
90144
  emitToolExecutionFinishedEvents(socket, runtime, {
89906
90145
  approvals: approvalResults,
89907
90146
  runId: runtime.activeRunId ?? undefined,
@@ -89915,7 +90154,8 @@ async function resolveRecoveredApprovalResponse(runtime, socket, response, proce
89915
90154
  const continuationMessages = [
89916
90155
  {
89917
90156
  type: "approval",
89918
- approvals: approvalResults
90157
+ approvals: approvalResults,
90158
+ otid: crypto.randomUUID()
89919
90159
  }
89920
90160
  ];
89921
90161
  let continuationBatchId = `batch-recovered-${crypto.randomUUID()}`;
@@ -90309,6 +90549,18 @@ async function sendApprovalContinuationWithRetry(conversationId, messages, opts,
90309
90549
  retryAfterMs
90310
90550
  });
90311
90551
  transientRetries = attempt;
90552
+ const retryMessage = getRetryStatusMessage(errorDetail);
90553
+ if (retryMessage) {
90554
+ emitRetryDelta(socket, runtime, {
90555
+ message: retryMessage,
90556
+ reason: "error",
90557
+ attempt,
90558
+ maxAttempts: LLM_API_ERROR_MAX_RETRIES,
90559
+ delayMs,
90560
+ agentId: runtime.agentId ?? undefined,
90561
+ conversationId
90562
+ });
90563
+ }
90312
90564
  await new Promise((resolve27) => setTimeout(resolve27, delayMs));
90313
90565
  if (abortSignal?.aborted) {
90314
90566
  throw new Error("Cancelled by user");
@@ -90354,6 +90606,15 @@ async function sendApprovalContinuationWithRetry(conversationId, messages, opts,
90354
90606
  category: "conversation_busy",
90355
90607
  attempt: conversationBusyRetries
90356
90608
  });
90609
+ emitRetryDelta(socket, runtime, {
90610
+ message: "Conversation is busy, waiting and retrying…",
90611
+ reason: "error",
90612
+ attempt: conversationBusyRetries,
90613
+ maxAttempts: MAX_CONVERSATION_BUSY_RETRIES,
90614
+ delayMs: retryDelayMs,
90615
+ agentId: runtime.agentId ?? undefined,
90616
+ conversationId
90617
+ });
90357
90618
  await new Promise((resolve27) => setTimeout(resolve27, retryDelayMs));
90358
90619
  if (abortSignal?.aborted) {
90359
90620
  throw new Error("Cancelled by user");
@@ -92710,14 +92971,19 @@ async function handleApprovalStop(params) {
92710
92971
  }));
92711
92972
  }
92712
92973
  };
92713
- const executionResults = await executeApprovalBatch(decisions, undefined, {
92714
- toolContextId: turnToolContextId ?? undefined,
92715
- abortSignal: abortController.signal,
92716
- onStreamingOutput: emitToolExecutionOutput,
92717
- workingDirectory: turnWorkingDirectory,
92718
- parentScope: agentId && conversationId ? { agentId, conversationId } : undefined,
92719
- onFileWrite
92720
- });
92974
+ let executionResults;
92975
+ try {
92976
+ executionResults = await executeApprovalBatch(decisions, undefined, {
92977
+ toolContextId: turnToolContextId ?? undefined,
92978
+ abortSignal: abortController.signal,
92979
+ onStreamingOutput: emitToolExecutionOutput,
92980
+ workingDirectory: turnWorkingDirectory,
92981
+ parentScope: agentId && conversationId ? { agentId, conversationId } : undefined,
92982
+ onFileWrite
92983
+ });
92984
+ } finally {
92985
+ emitToolExecutionOutput.flush();
92986
+ }
92721
92987
  const persistedExecutionResults = normalizeExecutionResultsForInterruptParity(runtime, executionResults, lastExecutingToolCallIds);
92722
92988
  validateApprovalResultIds(decisions.map((decision) => ({
92723
92989
  approval: {
@@ -92738,7 +93004,8 @@ async function handleApprovalStop(params) {
92738
93004
  const nextInput = [
92739
93005
  {
92740
93006
  type: "approval",
92741
- approvals: persistedExecutionResults
93007
+ approvals: persistedExecutionResults,
93008
+ otid: crypto.randomUUID()
92742
93009
  }
92743
93010
  ];
92744
93011
  let continuationBatchId = dequeuedBatchId;
@@ -93898,7 +94165,115 @@ var init_commands = __esm(async () => {
93898
94165
  });
93899
94166
 
93900
94167
  // src/websocket/listener/protocol-outbound.ts
94168
+ import { appendFileSync as appendFileSync4, mkdirSync as mkdirSync22 } from "node:fs";
94169
+ import { dirname as dirname15 } from "node:path";
94170
+ import { performance as performance2 } from "node:perf_hooks";
93901
94171
  import WebSocket3 from "ws";
94172
+ function getProtocolPerfKey(message) {
94173
+ if (message.type === "stream_delta" && "delta" in message) {
94174
+ const delta = message.delta;
94175
+ return `${message.type}:${String(delta.message_type ?? "unknown")}`;
94176
+ }
94177
+ return message.type;
94178
+ }
94179
+ function scheduleProtocolPerfFlush() {
94180
+ if (protocolPerfFlushTimer) {
94181
+ return;
94182
+ }
94183
+ protocolPerfFlushTimer = setTimeout(() => {
94184
+ protocolPerfFlushTimer = null;
94185
+ flushProtocolPerfTelemetry();
94186
+ }, PROTOCOL_PERF_FLUSH_INTERVAL_MS);
94187
+ const timerWithUnref = protocolPerfFlushTimer;
94188
+ timerWithUnref.unref?.();
94189
+ }
94190
+ function recordProtocolPerfTelemetry(key, sample) {
94191
+ if (protocolPerfWindowStartedAt === 0) {
94192
+ protocolPerfWindowStartedAt = Date.now();
94193
+ }
94194
+ const bucket = protocolPerfBuckets.get(key) ?? {
94195
+ count: 0,
94196
+ bytes: 0,
94197
+ stringifyMs: 0,
94198
+ sendMs: 0,
94199
+ maxBufferedBefore: 0,
94200
+ maxBufferedAfter: 0
94201
+ };
94202
+ bucket.count += 1;
94203
+ bucket.bytes += sample.bytes;
94204
+ bucket.stringifyMs += sample.stringifyMs;
94205
+ bucket.sendMs += sample.sendMs;
94206
+ bucket.maxBufferedBefore = Math.max(bucket.maxBufferedBefore, sample.bufferedBefore);
94207
+ bucket.maxBufferedAfter = Math.max(bucket.maxBufferedAfter, sample.bufferedAfter);
94208
+ protocolPerfBuckets.set(key, bucket);
94209
+ scheduleProtocolPerfFlush();
94210
+ }
94211
+ function writeProtocolPerfFile(record, fallbackLine) {
94212
+ const filePath = PROTOCOL_PERF_FILE;
94213
+ if (!filePath) {
94214
+ console.error(fallbackLine);
94215
+ return;
94216
+ }
94217
+ try {
94218
+ const dir = dirname15(filePath);
94219
+ if (protocolPerfFileDirEnsured !== dir) {
94220
+ mkdirSync22(dir, { recursive: true });
94221
+ protocolPerfFileDirEnsured = dir;
94222
+ }
94223
+ appendFileSync4(filePath, `${JSON.stringify(record)}
94224
+ `, {
94225
+ encoding: "utf8"
94226
+ });
94227
+ } catch (error) {
94228
+ if (!protocolPerfFileWarningEmitted) {
94229
+ protocolPerfFileWarningEmitted = true;
94230
+ console.error(`[Listen Perf] Failed to write LETTA_LISTENER_PERF_FILE=${filePath}`, error);
94231
+ }
94232
+ console.error(fallbackLine);
94233
+ }
94234
+ }
94235
+ function flushProtocolPerfTelemetry() {
94236
+ if (protocolPerfBuckets.size === 0) {
94237
+ protocolPerfWindowStartedAt = 0;
94238
+ return;
94239
+ }
94240
+ const windowMs = Math.max(1, Date.now() - protocolPerfWindowStartedAt);
94241
+ const totals = {
94242
+ count: 0,
94243
+ bytes: 0,
94244
+ stringifyMs: 0,
94245
+ sendMs: 0,
94246
+ maxBufferedBefore: 0,
94247
+ maxBufferedAfter: 0
94248
+ };
94249
+ const buckets = {};
94250
+ const parts = [...protocolPerfBuckets.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([key, bucket]) => {
94251
+ totals.count += bucket.count;
94252
+ totals.bytes += bucket.bytes;
94253
+ totals.stringifyMs += bucket.stringifyMs;
94254
+ totals.sendMs += bucket.sendMs;
94255
+ totals.maxBufferedBefore = Math.max(totals.maxBufferedBefore, bucket.maxBufferedBefore);
94256
+ totals.maxBufferedAfter = Math.max(totals.maxBufferedAfter, bucket.maxBufferedAfter);
94257
+ buckets[key] = {
94258
+ ...bucket,
94259
+ avg_bytes: bucket.count > 0 ? bucket.bytes / bucket.count : 0,
94260
+ avg_stringify_ms: bucket.count > 0 ? bucket.stringifyMs / bucket.count : 0,
94261
+ avg_send_ms: bucket.count > 0 ? bucket.sendMs / bucket.count : 0
94262
+ };
94263
+ const stringifyMs = bucket.stringifyMs.toFixed(2);
94264
+ const sendMs = bucket.sendMs.toFixed(2);
94265
+ return `${key}{count=${bucket.count},bytes=${bucket.bytes},stringify_ms=${stringifyMs},send_ms=${sendMs},max_buffered_before=${bucket.maxBufferedBefore},max_buffered_after=${bucket.maxBufferedAfter}}`;
94266
+ });
94267
+ writeProtocolPerfFile({
94268
+ ts: new Date().toISOString(),
94269
+ event: "protocol_emit",
94270
+ window_ms: windowMs,
94271
+ totals,
94272
+ buckets
94273
+ }, `[Listen Perf] protocol_emit window_ms=${windowMs} ${parts.join(" ")}`);
94274
+ protocolPerfBuckets.clear();
94275
+ protocolPerfWindowStartedAt = 0;
94276
+ }
93902
94277
  function getCachedDeviceGitContext(cwd2) {
93903
94278
  const now = Date.now();
93904
94279
  const cached = gitContextCache.get(cwd2);
@@ -94105,8 +94480,24 @@ function emitProtocolV2Message(socket, runtime, message, scope) {
94105
94480
  emitted_at: new Date().toISOString(),
94106
94481
  idempotency_key: `${message.type}:${eventSeq}:${crypto.randomUUID()}`
94107
94482
  };
94483
+ const perfEnabled = PROTOCOL_PERF_ENABLED;
94484
+ const stringifyStartedAt = perfEnabled ? performance2.now() : 0;
94485
+ let payload;
94108
94486
  try {
94109
- socket.send(JSON.stringify(outbound));
94487
+ payload = JSON.stringify(outbound);
94488
+ const stringifyMs = perfEnabled ? performance2.now() - stringifyStartedAt : 0;
94489
+ const bufferedBefore = perfEnabled ? socket.bufferedAmount : 0;
94490
+ const sendStartedAt = perfEnabled ? performance2.now() : 0;
94491
+ socket.send(payload);
94492
+ if (perfEnabled) {
94493
+ recordProtocolPerfTelemetry(getProtocolPerfKey(message), {
94494
+ bytes: Buffer.byteLength(payload),
94495
+ stringifyMs,
94496
+ sendMs: performance2.now() - sendStartedAt,
94497
+ bufferedBefore,
94498
+ bufferedAfter: socket.bufferedAmount
94499
+ });
94500
+ }
94110
94501
  } catch (error) {
94111
94502
  console.error(`[Listen V2] Failed to emit ${message.type} (seq=${eventSeq})`, error);
94112
94503
  safeEmitWsEvent("send", "lifecycle", {
@@ -94340,7 +94731,7 @@ function emitStreamDelta(socket, runtime, delta, scope, subagentId) {
94340
94731
  };
94341
94732
  emitProtocolV2Message(socket, runtime, message, scope);
94342
94733
  }
94343
- var GIT_CONTEXT_CACHE_TTL_MS = 15000, MAX_GIT_CONTEXT_CACHE_ENTRIES = 64, gitContextCache;
94734
+ 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
94735
  var init_protocol_outbound = __esm(async () => {
94345
94736
  init_memoryFilesystem();
94346
94737
  init_gitContext();
@@ -94356,6 +94747,10 @@ var init_protocol_outbound = __esm(async () => {
94356
94747
  init_permissionMode();
94357
94748
  init_runtime4();
94358
94749
  await init_commands();
94750
+ PROTOCOL_PERF_ENV_VALUES = new Set(["1", "true", "yes"]);
94751
+ PROTOCOL_PERF_ENABLED = PROTOCOL_PERF_ENV_VALUES.has((process.env.LETTA_LISTENER_PERF ?? "").toLowerCase());
94752
+ PROTOCOL_PERF_FILE = process.env.LETTA_LISTENER_PERF_FILE?.trim() || null;
94753
+ protocolPerfBuckets = new Map;
94359
94754
  gitContextCache = new Map;
94360
94755
  });
94361
94756
 
@@ -95423,7 +95818,7 @@ function isSyncCommand(value) {
95423
95818
  return false;
95424
95819
  }
95425
95820
  const candidate = value;
95426
- return candidate.type === "sync" && isRuntimeScope(candidate.runtime);
95821
+ return candidate.type === "sync" && isRuntimeScope(candidate.runtime) && (candidate.recover_approvals === undefined || typeof candidate.recover_approvals === "boolean");
95427
95822
  }
95428
95823
  function isTerminalSpawnCommand(value) {
95429
95824
  if (!value || typeof value !== "object")
@@ -96104,12 +96499,14 @@ function runDetachedListenerTask(commandName, task2) {
96104
96499
  async function replaySyncStateForRuntime(listenerRuntime, socket, scope, opts) {
96105
96500
  const syncScopedRuntime = getOrCreateScopedRuntime(listenerRuntime, scope.agent_id, scope.conversation_id);
96106
96501
  const recoverFn = opts?.recoverApprovalStateForSync ?? recoverApprovalStateForSync;
96107
- try {
96108
- await recoverFn(syncScopedRuntime, scope);
96109
- } catch (error) {
96110
- trackListenerError("listener_sync_recovery_failed", error, "listener_sync_recovery");
96111
- if (isDebugEnabled()) {
96112
- console.warn("[Listen] Sync approval recovery failed:", error);
96502
+ if (opts?.recoverApprovals ?? true) {
96503
+ try {
96504
+ await recoverFn(syncScopedRuntime, scope);
96505
+ } catch (error) {
96506
+ trackListenerError("listener_sync_recovery_failed", error, "listener_sync_recovery");
96507
+ if (isDebugEnabled()) {
96508
+ console.warn("[Listen] Sync approval recovery failed:", error);
96509
+ }
96113
96510
  }
96114
96511
  }
96115
96512
  emitStateSync(socket, listenerRuntime, scope);
@@ -97555,7 +97952,7 @@ async function handleSkillCommand(parsed, socket) {
97555
97952
  const {
97556
97953
  existsSync: existsSync30,
97557
97954
  lstatSync: lstatSync2,
97558
- mkdirSync: mkdirSync21,
97955
+ mkdirSync: mkdirSync23,
97559
97956
  rmdirSync,
97560
97957
  symlinkSync,
97561
97958
  unlinkSync: unlinkSync8
@@ -97586,7 +97983,7 @@ async function handleSkillCommand(parsed, socket) {
97586
97983
  }
97587
97984
  const linkName = basename13(parsed.skill_path);
97588
97985
  const linkPath = join34(globalSkillsDir, linkName);
97589
- mkdirSync21(globalSkillsDir, { recursive: true });
97986
+ mkdirSync23(globalSkillsDir, { recursive: true });
97590
97987
  if (existsSync30(linkPath)) {
97591
97988
  const stat7 = lstatSync2(linkPath);
97592
97989
  if (stat7.isSymbolicLink()) {
@@ -98456,7 +98853,9 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
98456
98853
  console.log(`[Listen V2] Dropping sync: runtime mismatch or closed`);
98457
98854
  return;
98458
98855
  }
98459
- await replaySyncStateForRuntime(runtime, socket, parsed.runtime);
98856
+ await replaySyncStateForRuntime(runtime, socket, parsed.runtime, {
98857
+ recoverApprovals: parsed.recover_approvals !== false
98858
+ });
98460
98859
  return;
98461
98860
  }
98462
98861
  if (parsed.type === "input") {
@@ -99786,9 +100185,9 @@ __export(exports_debug2, {
99786
100185
  debugLog: () => debugLog3
99787
100186
  });
99788
100187
  import {
99789
- appendFileSync as appendFileSync3,
100188
+ appendFileSync as appendFileSync5,
99790
100189
  existsSync as existsSync31,
99791
- mkdirSync as mkdirSync22,
100190
+ mkdirSync as mkdirSync24,
99792
100191
  readdirSync as readdirSync11,
99793
100192
  readFileSync as readFileSync22,
99794
100193
  unlinkSync as unlinkSync8
@@ -99809,7 +100208,7 @@ function printDebugLine2(line, level = "log") {
99809
100208
  const debugFile = getDebugFile2();
99810
100209
  if (debugFile) {
99811
100210
  try {
99812
- appendFileSync3(debugFile, line, { encoding: "utf8" });
100211
+ appendFileSync5(debugFile, line, { encoding: "utf8" });
99813
100212
  return;
99814
100213
  } catch {}
99815
100214
  }
@@ -99835,7 +100234,7 @@ class DebugLogFile2 {
99835
100234
  return;
99836
100235
  this.ensureDir();
99837
100236
  try {
99838
- appendFileSync3(this.logPath, line, { encoding: "utf8" });
100237
+ appendFileSync5(this.logPath, line, { encoding: "utf8" });
99839
100238
  } catch {}
99840
100239
  }
99841
100240
  getTail(maxLines = DEFAULT_TAIL_LINES2) {
@@ -99858,7 +100257,7 @@ class DebugLogFile2 {
99858
100257
  return;
99859
100258
  try {
99860
100259
  if (!existsSync31(this.agentDir)) {
99861
- mkdirSync22(this.agentDir, { recursive: true });
100260
+ mkdirSync24(this.agentDir, { recursive: true });
99862
100261
  }
99863
100262
  this.dirCreated = true;
99864
100263
  } catch {}
@@ -99928,10 +100327,10 @@ __export(exports_skills2, {
99928
100327
  });
99929
100328
  import { existsSync as existsSync32 } from "node:fs";
99930
100329
  import { readdir as readdir8, readFile as readFile13, realpath as realpath4, stat as stat7 } from "node:fs/promises";
99931
- import { dirname as dirname14, join as join38 } from "node:path";
100330
+ import { dirname as dirname16, join as join38 } from "node:path";
99932
100331
  import { fileURLToPath as fileURLToPath9 } from "node:url";
99933
100332
  function getBundledSkillsPath2() {
99934
- const thisDir = dirname14(fileURLToPath9(import.meta.url));
100333
+ const thisDir = dirname16(fileURLToPath9(import.meta.url));
99935
100334
  if (thisDir.includes("src/agent") || thisDir.includes("src\\agent")) {
99936
100335
  return join38(thisDir, "../skills/builtin");
99937
100336
  }
@@ -100125,16 +100524,16 @@ import {
100125
100524
  existsSync as existsSync33,
100126
100525
  readFileSync as fsReadFileSync2,
100127
100526
  writeFileSync as fsWriteFileSync2,
100128
- mkdirSync as mkdirSync23
100527
+ mkdirSync as mkdirSync25
100129
100528
  } from "node:fs";
100130
- import { dirname as dirname15 } from "node:path";
100529
+ import { dirname as dirname17 } from "node:path";
100131
100530
  async function readFile14(path26) {
100132
100531
  return fsReadFileSync2(path26, { encoding: "utf-8" });
100133
100532
  }
100134
100533
  async function writeFile12(path26, content) {
100135
- const dir = dirname15(path26);
100534
+ const dir = dirname17(path26);
100136
100535
  if (!existsSync33(dir)) {
100137
- mkdirSync23(dir, { recursive: true });
100536
+ mkdirSync25(dir, { recursive: true });
100138
100537
  }
100139
100538
  fsWriteFileSync2(path26, content, { encoding: "utf-8", flush: true });
100140
100539
  }
@@ -100142,7 +100541,7 @@ function exists2(path26) {
100142
100541
  return existsSync33(path26);
100143
100542
  }
100144
100543
  async function mkdir9(path26, options) {
100145
- mkdirSync23(path26, options);
100544
+ mkdirSync25(path26, options);
100146
100545
  }
100147
100546
  async function readJsonFile(path26) {
100148
100547
  const text = await readFile14(path26);
@@ -100275,7 +100674,7 @@ var exports_bootstrap_tools = {};
100275
100674
  __export(exports_bootstrap_tools, {
100276
100675
  bootstrapBaseToolsIfNeeded: () => bootstrapBaseToolsIfNeeded
100277
100676
  });
100278
- import { existsSync as existsSync34, mkdirSync as mkdirSync24, writeFileSync as writeFileSync17 } from "node:fs";
100677
+ import { existsSync as existsSync34, mkdirSync as mkdirSync26, writeFileSync as writeFileSync17 } from "node:fs";
100279
100678
  import { homedir as homedir27 } from "node:os";
100280
100679
  import { join as join39 } from "node:path";
100281
100680
  async function bootstrapBaseToolsIfNeeded() {
@@ -100285,7 +100684,7 @@ async function bootstrapBaseToolsIfNeeded() {
100285
100684
  try {
100286
100685
  const success = await addBaseToolsToServer();
100287
100686
  if (success) {
100288
- mkdirSync24(join39(homedir27(), ".letta"), { recursive: true });
100687
+ mkdirSync26(join39(homedir27(), ".letta"), { recursive: true });
100289
100688
  writeFileSync17(MARKER_PATH, new Date().toISOString(), "utf-8");
100290
100689
  }
100291
100690
  } catch (err) {
@@ -101989,7 +102388,7 @@ __export(exports_import, {
101989
102388
  });
101990
102389
  import { createReadStream } from "node:fs";
101991
102390
  import { chmod, mkdir as mkdir10, readFile as readFile15, writeFile as writeFile13 } from "node:fs/promises";
101992
- import { dirname as dirname16, resolve as resolve30 } from "node:path";
102391
+ import { dirname as dirname18, resolve as resolve30 } from "node:path";
101993
102392
  async function importAgentFromFile(options) {
101994
102393
  const client = await getClient();
101995
102394
  const resolvedPath = resolve30(options.filePath);
@@ -102048,7 +102447,7 @@ async function writeSkillFiles(skillDir, files) {
102048
102447
  }
102049
102448
  async function writeSkillFile(skillDir, filePath, content) {
102050
102449
  const fullPath = resolve30(skillDir, filePath);
102051
- await mkdir10(dirname16(fullPath), { recursive: true });
102450
+ await mkdir10(dirname18(fullPath), { recursive: true });
102052
102451
  await writeFile13(fullPath, content, "utf-8");
102053
102452
  const isScript = filePath.startsWith("scripts/") || content.trimStart().startsWith("#!");
102054
102453
  if (isScript) {
@@ -124703,7 +125102,7 @@ html.dark .agent-name { color: var(--text-dim); }
124703
125102
  var init_plan_viewer_template = () => {};
124704
125103
 
124705
125104
  // src/web/generate-plan-viewer.ts
124706
- import { chmodSync as chmodSync2, existsSync as existsSync37, mkdirSync as mkdirSync27, writeFileSync as writeFileSync20 } from "node:fs";
125105
+ import { chmodSync as chmodSync2, existsSync as existsSync37, mkdirSync as mkdirSync29, writeFileSync as writeFileSync20 } from "node:fs";
124707
125106
  import { homedir as homedir31 } from "node:os";
124708
125107
  import { join as join44 } from "node:path";
124709
125108
  async function generateAndOpenPlanViewer(planContent, planFilePath, options) {
@@ -124716,7 +125115,7 @@ async function generateAndOpenPlanViewer(planContent, planFilePath, options) {
124716
125115
  const jsonPayload = JSON.stringify(data).replace(/</g, "\\u003c");
124717
125116
  const html = plan_viewer_template_default.replace("<!--LETTA_PLAN_DATA_PLACEHOLDER-->", () => jsonPayload);
124718
125117
  if (!existsSync37(VIEWERS_DIR)) {
124719
- mkdirSync27(VIEWERS_DIR, { recursive: true, mode: 448 });
125118
+ mkdirSync29(VIEWERS_DIR, { recursive: true, mode: 448 });
124720
125119
  }
124721
125120
  try {
124722
125121
  chmodSync2(VIEWERS_DIR, 448);
@@ -128026,12 +128425,12 @@ __export(exports_terminalKeybindingInstaller, {
128026
128425
  import {
128027
128426
  copyFileSync,
128028
128427
  existsSync as existsSync39,
128029
- mkdirSync as mkdirSync28,
128428
+ mkdirSync as mkdirSync30,
128030
128429
  readFileSync as readFileSync25,
128031
128430
  writeFileSync as writeFileSync21
128032
128431
  } from "node:fs";
128033
128432
  import { homedir as homedir32, platform as platform6 } from "node:os";
128034
- import { dirname as dirname17, join as join46 } from "node:path";
128433
+ import { dirname as dirname19, join as join46 } from "node:path";
128035
128434
  function detectTerminalType() {
128036
128435
  if (process.env.CURSOR_TRACE_ID || process.env.CURSOR_CHANNEL) {
128037
128436
  return "cursor";
@@ -128122,9 +128521,9 @@ function installKeybinding(keybindingsPath) {
128122
128521
  if (keybindingExists(keybindingsPath)) {
128123
128522
  return { success: true, alreadyExists: true };
128124
128523
  }
128125
- const parentDir = dirname17(keybindingsPath);
128524
+ const parentDir = dirname19(keybindingsPath);
128126
128525
  if (!existsSync39(parentDir)) {
128127
- mkdirSync28(parentDir, { recursive: true });
128526
+ mkdirSync30(parentDir, { recursive: true });
128128
128527
  }
128129
128528
  let keybindings = [];
128130
128529
  let backupPath = null;
@@ -128281,9 +128680,9 @@ return config`);
128281
128680
  ${WEZTERM_DELETE_FIX}
128282
128681
  `;
128283
128682
  }
128284
- const parentDir = dirname17(configPath);
128683
+ const parentDir = dirname19(configPath);
128285
128684
  if (!existsSync39(parentDir)) {
128286
- mkdirSync28(parentDir, { recursive: true });
128685
+ mkdirSync30(parentDir, { recursive: true });
128287
128686
  }
128288
128687
  writeFileSync21(configPath, content, { encoding: "utf-8" });
128289
128688
  return {
@@ -128881,7 +129280,7 @@ __export(exports_custom, {
128881
129280
  });
128882
129281
  import { existsSync as existsSync40 } from "node:fs";
128883
129282
  import { readdir as readdir10, readFile as readFile16 } from "node:fs/promises";
128884
- import { basename as basename14, dirname as dirname18, join as join47 } from "node:path";
129283
+ import { basename as basename14, dirname as dirname20, join as join47 } from "node:path";
128885
129284
  async function getCustomCommands() {
128886
129285
  if (cachedCommands !== null) {
128887
129286
  return cachedCommands;
@@ -128942,7 +129341,7 @@ async function parseCommandFile(filePath, rootPath, source2) {
128942
129341
  const content = await readFile16(filePath, "utf-8");
128943
129342
  const { frontmatter, body } = parseFrontmatter(content);
128944
129343
  const id = basename14(filePath, ".md");
128945
- const relativePath = dirname18(filePath).slice(rootPath.length);
129344
+ const relativePath = dirname20(filePath).slice(rootPath.length);
128946
129345
  const namespace = relativePath.replace(/^[/\\]/, "") || undefined;
128947
129346
  let description = getStringField(frontmatter, "description");
128948
129347
  if (!description) {
@@ -134102,14 +134501,14 @@ var init_InputRich = __esm(async () => {
134102
134501
  import { execFileSync as execFileSync4 } from "node:child_process";
134103
134502
  import {
134104
134503
  existsSync as existsSync41,
134105
- mkdirSync as mkdirSync29,
134504
+ mkdirSync as mkdirSync31,
134106
134505
  mkdtempSync,
134107
134506
  readFileSync as readFileSync26,
134108
134507
  rmSync as rmSync4,
134109
134508
  writeFileSync as writeFileSync22
134110
134509
  } from "node:fs";
134111
134510
  import { tmpdir as tmpdir6 } from "node:os";
134112
- import { dirname as dirname19, join as join49 } from "node:path";
134511
+ import { dirname as dirname21, join as join49 } from "node:path";
134113
134512
  function runCommand(command, args, cwd2, input) {
134114
134513
  try {
134115
134514
  return execFileSync4(command, args, {
@@ -134356,8 +134755,8 @@ function runGit3(args, cwd2) {
134356
134755
  }
134357
134756
  function writeWorkflow(repoDir, workflowPath, content) {
134358
134757
  const absolutePath = join49(repoDir, workflowPath);
134359
- if (!existsSync41(dirname19(absolutePath))) {
134360
- mkdirSync29(dirname19(absolutePath), { recursive: true });
134758
+ if (!existsSync41(dirname21(absolutePath))) {
134759
+ mkdirSync31(dirname21(absolutePath), { recursive: true });
134361
134760
  }
134362
134761
  const next = `${content.trimEnd()}
134363
134762
  `;
@@ -138599,7 +138998,7 @@ __export(exports_generate_memory_viewer, {
138599
138998
  generateAndOpenMemoryViewer: () => generateAndOpenMemoryViewer
138600
138999
  });
138601
139000
  import { execFile as execFileCb3 } from "node:child_process";
138602
- import { chmodSync as chmodSync3, existsSync as existsSync42, mkdirSync as mkdirSync30, writeFileSync as writeFileSync23 } from "node:fs";
139001
+ import { chmodSync as chmodSync3, existsSync as existsSync42, mkdirSync as mkdirSync32, writeFileSync as writeFileSync23 } from "node:fs";
138603
139002
  import { homedir as homedir34 } from "node:os";
138604
139003
  import { join as join50 } from "node:path";
138605
139004
  import { promisify as promisify13 } from "node:util";
@@ -138920,7 +139319,7 @@ async function generateAndOpenMemoryViewer(agentId, options) {
138920
139319
  const jsonPayload = JSON.stringify(data).replace(/</g, "\\u003c");
138921
139320
  const html = memory_viewer_template_default.replace("<!--LETTA_DATA_PLACEHOLDER-->", () => jsonPayload);
138922
139321
  if (!existsSync42(VIEWERS_DIR2)) {
138923
- mkdirSync30(VIEWERS_DIR2, { recursive: true, mode: 448 });
139322
+ mkdirSync32(VIEWERS_DIR2, { recursive: true, mode: 448 });
138924
139323
  }
138925
139324
  try {
138926
139325
  chmodSync3(VIEWERS_DIR2, 448);
@@ -142730,7 +143129,7 @@ function formatDuration3(ms) {
142730
143129
  }
142731
143130
  return `${(ms / 1000).toFixed(1)}s`;
142732
143131
  }
142733
- function formatNumber(n) {
143132
+ function formatNumber2(n) {
142734
143133
  return n.toLocaleString();
142735
143134
  }
142736
143135
  function formatUsageStats({
@@ -142752,7 +143151,7 @@ function formatUsageStats({
142752
143151
  const monthlyCredits = Math.round(balance.monthly_credit_balance);
142753
143152
  const purchasedCredits = Math.round(balance.purchased_credit_balance);
142754
143153
  const toDollars = (credits) => (credits / 1000).toFixed(2);
142755
- outputLines.push(`Plan: [${balance.billing_tier}]`, buildAppUrl("/settings/organization/usage"), "", `Available credits: ◎${formatNumber(totalCredits)} ($${toDollars(totalCredits)})`, `Monthly credits: ◎${formatNumber(monthlyCredits)} ($${toDollars(monthlyCredits)})`, `Purchased credits: ◎${formatNumber(purchasedCredits)} ($${toDollars(purchasedCredits)})`);
143154
+ 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
143155
  }
142757
143156
  return outputLines.join(`
142758
143157
  `);
@@ -150023,6 +150422,7 @@ function App2({
150023
150422
  streamingRefreshTimeoutRef.current = setTimeout(() => {
150024
150423
  streamingRefreshTimeoutRef.current = null;
150025
150424
  if (!buffersRef.current.interrupted) {
150425
+ recordTuiPerf("ui_refresh:tool_output");
150026
150426
  refreshDerived();
150027
150427
  }
150028
150428
  }, 100);
@@ -150035,6 +150435,9 @@ function App2({
150035
150435
  };
150036
150436
  }, []);
150037
150437
  const updateStreamingOutput = import_react104.useCallback((toolCallId, chunk, isStderr = false) => {
150438
+ recordTuiPerf(`tool_output:${isStderr ? "stderr" : "stdout"}`, {
150439
+ bytes: Buffer.byteLength(chunk)
150440
+ });
150038
150441
  const lineId = buffersRef.current.toolCallIdToLineId.get(toolCallId);
150039
150442
  if (!lineId)
150040
150443
  return;
@@ -150055,6 +150458,7 @@ function App2({
150055
150458
  setTimeout(() => {
150056
150459
  buffersRef.current.pendingRefresh = false;
150057
150460
  if (!buffersRef.current.interrupted && (buffersRef.current.commitGeneration || 0) === capturedGeneration) {
150461
+ recordTuiPerf("ui_refresh:stream");
150058
150462
  refreshDerived();
150059
150463
  }
150060
150464
  }, 16);
@@ -157568,6 +157972,7 @@ var init_App2 = __esm(async () => {
157568
157972
  init_telemetry();
157569
157973
  init_toolset_labels();
157570
157974
  init_debug();
157975
+ init_tuiPerf();
157571
157976
  init_version();
157572
157977
  init_mcp();
157573
157978
  init_profile();
@@ -157748,12 +158153,12 @@ __export(exports_terminalKeybindingInstaller2, {
157748
158153
  import {
157749
158154
  copyFileSync as copyFileSync2,
157750
158155
  existsSync as existsSync46,
157751
- mkdirSync as mkdirSync31,
158156
+ mkdirSync as mkdirSync33,
157752
158157
  readFileSync as readFileSync29,
157753
158158
  writeFileSync as writeFileSync25
157754
158159
  } from "node:fs";
157755
158160
  import { homedir as homedir38, platform as platform7 } from "node:os";
157756
- import { dirname as dirname20, join as join54 } from "node:path";
158161
+ import { dirname as dirname22, join as join54 } from "node:path";
157757
158162
  function detectTerminalType2() {
157758
158163
  if (process.env.CURSOR_TRACE_ID || process.env.CURSOR_CHANNEL) {
157759
158164
  return "cursor";
@@ -157844,9 +158249,9 @@ function installKeybinding2(keybindingsPath) {
157844
158249
  if (keybindingExists2(keybindingsPath)) {
157845
158250
  return { success: true, alreadyExists: true };
157846
158251
  }
157847
- const parentDir = dirname20(keybindingsPath);
158252
+ const parentDir = dirname22(keybindingsPath);
157848
158253
  if (!existsSync46(parentDir)) {
157849
- mkdirSync31(parentDir, { recursive: true });
158254
+ mkdirSync33(parentDir, { recursive: true });
157850
158255
  }
157851
158256
  let keybindings = [];
157852
158257
  let backupPath = null;
@@ -158003,9 +158408,9 @@ return config`);
158003
158408
  ${WEZTERM_DELETE_FIX2}
158004
158409
  `;
158005
158410
  }
158006
- const parentDir = dirname20(configPath);
158411
+ const parentDir = dirname22(configPath);
158007
158412
  if (!existsSync46(parentDir)) {
158008
- mkdirSync31(parentDir, { recursive: true });
158413
+ mkdirSync33(parentDir, { recursive: true });
158009
158414
  }
158010
158415
  writeFileSync25(configPath, content, { encoding: "utf-8" });
158011
158416
  return {
@@ -158573,7 +158978,7 @@ __export(exports_import2, {
158573
158978
  });
158574
158979
  import { createReadStream as createReadStream2 } from "node:fs";
158575
158980
  import { chmod as chmod2, mkdir as mkdir11, readFile as readFile19, writeFile as writeFile14 } from "node:fs/promises";
158576
- import { dirname as dirname21, resolve as resolve35 } from "node:path";
158981
+ import { dirname as dirname23, resolve as resolve35 } from "node:path";
158577
158982
  async function importAgentFromFile2(options) {
158578
158983
  const client = await getClient();
158579
158984
  const resolvedPath = resolve35(options.filePath);
@@ -158632,7 +159037,7 @@ async function writeSkillFiles2(skillDir, files) {
158632
159037
  }
158633
159038
  async function writeSkillFile2(skillDir, filePath, content) {
158634
159039
  const fullPath = resolve35(skillDir, filePath);
158635
- await mkdir11(dirname21(fullPath), { recursive: true });
159040
+ await mkdir11(dirname23(fullPath), { recursive: true });
158636
159041
  await writeFile14(fullPath, content, "utf-8");
158637
159042
  const isScript = filePath.startsWith("scripts/") || content.trimStart().startsWith("#!");
158638
159043
  if (isScript) {
@@ -158742,7 +159147,7 @@ __export(exports_memoryFilesystem2, {
158742
159147
  MEMORY_FS_MEMORY_DIR: () => MEMORY_FS_MEMORY_DIR2,
158743
159148
  MEMORY_FS_AGENTS_DIR: () => MEMORY_FS_AGENTS_DIR2
158744
159149
  });
158745
- import { existsSync as existsSync47, mkdirSync as mkdirSync32 } from "node:fs";
159150
+ import { existsSync as existsSync47, mkdirSync as mkdirSync34 } from "node:fs";
158746
159151
  import { homedir as homedir40 } from "node:os";
158747
159152
  import { join as join56, resolve as resolve36 } from "node:path";
158748
159153
  function getMemoryFilesystemRoot2(agentId, homeDir = homedir40()) {
@@ -158778,10 +159183,10 @@ function ensureMemoryFilesystemDirs2(agentId, homeDir = homedir40()) {
158778
159183
  const root = getMemoryFilesystemRoot2(agentId, homeDir);
158779
159184
  const systemDir = getMemorySystemDir2(agentId, homeDir);
158780
159185
  if (!existsSync47(root)) {
158781
- mkdirSync32(root, { recursive: true });
159186
+ mkdirSync34(root, { recursive: true });
158782
159187
  }
158783
159188
  if (!existsSync47(systemDir)) {
158784
- mkdirSync32(systemDir, { recursive: true });
159189
+ mkdirSync34(systemDir, { recursive: true });
158785
159190
  }
158786
159191
  }
158787
159192
  async function isMemfsEnabledOnServer2(agentId) {
@@ -164102,51 +164507,141 @@ async function runListenSubcommand(argv) {
164102
164507
  }
164103
164508
  }
164104
164509
 
164105
- // src/cli/subcommands/memfs.ts
164510
+ // src/cli/subcommands/memory.ts
164106
164511
  init_memoryGit();
164107
- import { cpSync, existsSync as existsSync30, mkdirSync as mkdirSync21, rmSync as rmSync3, statSync as statSync8 } from "node:fs";
164512
+ import { cpSync, existsSync as existsSync30, mkdirSync as mkdirSync23, rmSync as rmSync3, statSync as statSync8 } from "node:fs";
164108
164513
  import { readdir as readdir7 } from "node:fs/promises";
164109
164514
  import { homedir as homedir23 } from "node:os";
164110
164515
  import { join as join34 } from "node:path";
164111
164516
  import { parseArgs as parseArgs8 } from "node:util";
164517
+
164518
+ // src/cli/subcommands/memoryTokens.ts
164519
+ init_systemPromptSize();
164520
+ var DEFAULT_TOP = 20;
164521
+ var USAGE_EXIT = 64;
164522
+ var IO_EXIT = 65;
164523
+ function parsePositiveInt(raw, fallback) {
164524
+ if (raw === undefined)
164525
+ return fallback;
164526
+ if (!/^\d+$/.test(raw))
164527
+ return null;
164528
+ return Number.parseInt(raw, 10);
164529
+ }
164530
+ function formatNumber(value) {
164531
+ return value.toLocaleString("en-US");
164532
+ }
164533
+ function printText(total, files, top, quiet) {
164534
+ console.log("System prompt token estimate");
164535
+ console.log(` Total: ${formatNumber(total)} tokens`);
164536
+ if (quiet || top <= 0 || files.length === 0) {
164537
+ return;
164538
+ }
164539
+ const ranked = [...files].sort((a, b) => b.tokens - a.tokens);
164540
+ const limited = ranked.slice(0, top);
164541
+ console.log("");
164542
+ console.log("Top files:");
164543
+ console.log(` ${"tokens".padStart(8)} path`);
164544
+ for (const row of limited) {
164545
+ console.log(` ${formatNumber(row.tokens).padStart(8)} ${row.path}`);
164546
+ }
164547
+ }
164548
+ function printJson(total, files) {
164549
+ console.log(JSON.stringify({
164550
+ total_tokens: total,
164551
+ files
164552
+ }, null, 2));
164553
+ }
164554
+ function resolveMemoryDir3(options) {
164555
+ if (options.memoryDir)
164556
+ return options.memoryDir;
164557
+ if (process.env.MEMORY_DIR)
164558
+ return process.env.MEMORY_DIR;
164559
+ if (options.agentMemoryDir)
164560
+ return options.agentMemoryDir;
164561
+ return null;
164562
+ }
164563
+ async function runMemoryTokensAction(options) {
164564
+ const format2 = options.format ?? "text";
164565
+ if (format2 !== "text" && format2 !== "json") {
164566
+ console.error(`Invalid --format: ${format2} (expected text or json)`);
164567
+ return USAGE_EXIT;
164568
+ }
164569
+ const top = parsePositiveInt(options.top, DEFAULT_TOP);
164570
+ if (top === null) {
164571
+ console.error(`Invalid --top: ${options.top} (expected non-negative integer)`);
164572
+ return USAGE_EXIT;
164573
+ }
164574
+ const memoryDir = resolveMemoryDir3(options);
164575
+ if (!memoryDir) {
164576
+ console.error("Missing memory dir. Set --memory-dir, --agent, $MEMORY_DIR, or $LETTA_AGENT_ID.");
164577
+ return USAGE_EXIT;
164578
+ }
164579
+ let estimate;
164580
+ try {
164581
+ estimate = estimateSystemPromptSize(memoryDir);
164582
+ } catch (error) {
164583
+ const message = error instanceof Error ? error.message : String(error);
164584
+ console.error(`Failed to read memory dir: ${message}`);
164585
+ return IO_EXIT;
164586
+ }
164587
+ const { total, files } = estimate;
164588
+ if (format2 === "json") {
164589
+ printJson(total, files);
164590
+ } else {
164591
+ printText(total, files, top, options.quiet);
164592
+ }
164593
+ return 0;
164594
+ }
164595
+
164596
+ // src/cli/subcommands/memory.ts
164112
164597
  function printUsage5() {
164113
164598
  console.log(`
164114
164599
  Usage:
164115
- letta memfs status [--agent <id>]
164116
- letta memfs diff [--agent <id>]
164117
- letta memfs backup [--agent <id>]
164118
- letta memfs backups [--agent <id>]
164119
- letta memfs restore --from <backup> --force [--agent <id>]
164120
- letta memfs export --agent <id> --out <dir>
164121
- letta memfs pull [--agent <id>]
164600
+ letta memory status [--agent <id>]
164601
+ letta memory diff [--agent <id>]
164602
+ letta memory backup [--agent <id>]
164603
+ letta memory backups [--agent <id>]
164604
+ letta memory restore --from <backup> --force [--agent <id>]
164605
+ letta memory export --agent <id> --out <dir>
164606
+ letta memory pull [--agent <id>]
164607
+ letta memory tokens [--memory-dir <path>] [--agent <id>] [--top <N>]
164608
+ [--format text|json] [--quiet]
164122
164609
 
164123
164610
  Notes:
164124
- - Requires agent id via --agent or LETTA_AGENT_ID.
164125
- - Output is JSON only.
164611
+ - Most actions require agent id via --agent or LETTA_AGENT_ID and output JSON.
164612
+ - \`tokens\` additionally accepts --memory-dir or $MEMORY_DIR; reports the
164613
+ estimated token size of system/. Policy (whether a size is concerning) is
164614
+ up to the caller.
164126
164615
  - Memory is git-backed. Use git commands for commit/push.
164127
164616
 
164128
164617
  Examples:
164129
- LETTA_AGENT_ID=agent-123 letta memfs status
164130
- letta memfs pull --agent agent-123
164131
- letta memfs backup --agent agent-123
164132
- letta memfs export --agent agent-123 --out /tmp/letta-memfs-agent-123
164618
+ LETTA_AGENT_ID=agent-123 letta memory status
164619
+ letta memory pull --agent agent-123
164620
+ letta memory backup --agent agent-123
164621
+ letta memory export --agent agent-123 --out /tmp/letta-memory-agent-123
164622
+ letta memory tokens
164623
+ letta memory tokens --memory-dir ~/.letta/agents/agent-123/memory --format json
164133
164624
  `.trim());
164134
164625
  }
164135
164626
  function getAgentId5(agentFromArgs, agentIdFromArgs) {
164136
164627
  return agentFromArgs || agentIdFromArgs || process.env.LETTA_AGENT_ID || "";
164137
164628
  }
164138
- var MEMFS_OPTIONS = {
164629
+ var MEMORY_OPTIONS = {
164139
164630
  help: { type: "boolean", short: "h" },
164140
164631
  agent: { type: "string" },
164141
164632
  "agent-id": { type: "string" },
164142
164633
  from: { type: "string" },
164143
164634
  force: { type: "boolean" },
164144
- out: { type: "string" }
164635
+ out: { type: "string" },
164636
+ "memory-dir": { type: "string" },
164637
+ top: { type: "string" },
164638
+ format: { type: "string" },
164639
+ quiet: { type: "boolean" }
164145
164640
  };
164146
- function parseMemfsArgs(argv) {
164641
+ function parseMemoryArgs(argv) {
164147
164642
  return parseArgs8({
164148
164643
  args: argv,
164149
- options: MEMFS_OPTIONS,
164644
+ options: MEMORY_OPTIONS,
164150
164645
  strict: true,
164151
164646
  allowPositionals: true
164152
164647
  });
@@ -164198,10 +164693,10 @@ function resolveBackupPath(agentId, from) {
164198
164693
  }
164199
164694
  return join34(getAgentRoot(agentId), from);
164200
164695
  }
164201
- async function runMemfsSubcommand(argv) {
164696
+ async function runMemorySubcommand(argv) {
164202
164697
  let parsed;
164203
164698
  try {
164204
- parsed = parseMemfsArgs(argv);
164699
+ parsed = parseMemoryArgs(argv);
164205
164700
  } catch (error) {
164206
164701
  const message = error instanceof Error ? error.message : String(error);
164207
164702
  console.error(`Error: ${message}`);
@@ -164214,6 +164709,15 @@ async function runMemfsSubcommand(argv) {
164214
164709
  return 0;
164215
164710
  }
164216
164711
  const agentId = getAgentId5(parsed.values.agent, parsed.values["agent-id"]);
164712
+ if (action === "tokens") {
164713
+ return runMemoryTokensAction({
164714
+ memoryDir: parsed.values["memory-dir"],
164715
+ agentMemoryDir: agentId ? getMemoryRoot(agentId) : undefined,
164716
+ top: parsed.values.top,
164717
+ format: parsed.values.format,
164718
+ quiet: Boolean(parsed.values.quiet)
164719
+ });
164720
+ }
164217
164721
  if (!agentId) {
164218
164722
  console.error("Missing agent id. Set LETTA_AGENT_ID or pass --agent/--agent-id.");
164219
164723
  return 1;
@@ -164326,7 +164830,7 @@ async function runMemfsSubcommand(argv) {
164326
164830
  return 1;
164327
164831
  }
164328
164832
  } else {
164329
- mkdirSync21(out, { recursive: true });
164833
+ mkdirSync23(out, { recursive: true });
164330
164834
  }
164331
164835
  cpSync(root, out, { recursive: true });
164332
164836
  console.log(JSON.stringify({ exportedFrom: root, exportedTo: out, agentId }, null, 2));
@@ -164685,8 +165189,9 @@ async function runSubcommand(argv) {
164685
165189
  return null;
164686
165190
  }
164687
165191
  switch (command) {
165192
+ case "memory":
164688
165193
  case "memfs":
164689
- return runMemfsSubcommand(rest);
165194
+ return runMemorySubcommand(rest);
164690
165195
  case "agents":
164691
165196
  return runAgentsSubcommand(rest);
164692
165197
  case "messages":
@@ -166855,7 +167360,7 @@ USAGE
166855
167360
 
166856
167361
  # maintenance
166857
167362
  letta update Manually check for updates and install if available
166858
- letta memfs ... Memory filesystem subcommands (JSON-only)
167363
+ letta memory ... Memory filesystem subcommands
166859
167364
  letta agents ... Agents subcommands (JSON-only)
166860
167365
  letta messages ... Messages subcommands (JSON-only)
166861
167366
  letta blocks ... Blocks subcommands (JSON-only)
@@ -166864,14 +167369,16 @@ USAGE
166864
167369
  OPTIONS
166865
167370
  ${renderCliOptionsHelp()}
166866
167371
 
166867
- SUBCOMMANDS (JSON-only)
166868
- letta memfs status --agent <id>
166869
- letta memfs diff --agent <id>
166870
- letta memfs resolve --agent <id> --resolutions '<JSON>'
166871
- letta memfs backup --agent <id>
166872
- letta memfs backups --agent <id>
166873
- letta memfs restore --agent <id> --from <backup> --force
166874
- letta memfs export --agent <id> --out <dir>
167372
+ SUBCOMMANDS
167373
+ letta memory status --agent <id>
167374
+ letta memory diff --agent <id>
167375
+ letta memory resolve --agent <id> --resolutions '<JSON>'
167376
+ letta memory backup --agent <id>
167377
+ letta memory backups --agent <id>
167378
+ letta memory restore --agent <id> --from <backup> --force
167379
+ letta memory export --agent <id> --out <dir>
167380
+ letta memory pull --agent <id>
167381
+ letta memory tokens [--memory-dir <path>] [--agent <id>] [--format text|json]
166875
167382
  letta agents list [--query <text> | --name <name> | --tags <tags>]
166876
167383
  letta messages search --query <text> [--all-agents]
166877
167384
  letta messages list [--agent <id>]
@@ -168205,4 +168712,4 @@ Error during initialization: ${message}`);
168205
168712
  }
168206
168713
  main();
168207
168714
 
168208
- //# debugId=876580101EC255F264756E2164756E21
168715
+ //# debugId=A2D8C37B0089C68564756E2164756E21