@letta-ai/letta-code 0.24.1 → 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 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.1",
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: {
@@ -9157,6 +9157,72 @@ var init_models2 = __esm(() => {
9157
9157
  parallel_tool_calls: true
9158
9158
  }
9159
9159
  },
9160
+ {
9161
+ id: "gpt-5.5-none",
9162
+ handle: "openai/gpt-5.5",
9163
+ label: "GPT-5.5",
9164
+ description: "OpenAI's most capable model (no reasoning)",
9165
+ updateArgs: {
9166
+ reasoning_effort: "none",
9167
+ verbosity: "medium",
9168
+ context_window: 272000,
9169
+ max_output_tokens: 128000,
9170
+ parallel_tool_calls: true
9171
+ }
9172
+ },
9173
+ {
9174
+ id: "gpt-5.5-low",
9175
+ handle: "openai/gpt-5.5",
9176
+ label: "GPT-5.5",
9177
+ description: "OpenAI's most capable model (low reasoning)",
9178
+ updateArgs: {
9179
+ reasoning_effort: "low",
9180
+ verbosity: "medium",
9181
+ context_window: 272000,
9182
+ max_output_tokens: 128000,
9183
+ parallel_tool_calls: true
9184
+ }
9185
+ },
9186
+ {
9187
+ id: "gpt-5.5-medium",
9188
+ handle: "openai/gpt-5.5",
9189
+ label: "GPT-5.5",
9190
+ description: "OpenAI's most capable model (med reasoning)",
9191
+ updateArgs: {
9192
+ reasoning_effort: "medium",
9193
+ verbosity: "medium",
9194
+ context_window: 272000,
9195
+ max_output_tokens: 128000,
9196
+ parallel_tool_calls: true
9197
+ }
9198
+ },
9199
+ {
9200
+ id: "gpt-5.5-high",
9201
+ handle: "openai/gpt-5.5",
9202
+ label: "GPT-5.5",
9203
+ description: "OpenAI's most capable model (high reasoning)",
9204
+ isFeatured: true,
9205
+ updateArgs: {
9206
+ reasoning_effort: "high",
9207
+ verbosity: "medium",
9208
+ context_window: 272000,
9209
+ max_output_tokens: 128000,
9210
+ parallel_tool_calls: true
9211
+ }
9212
+ },
9213
+ {
9214
+ id: "gpt-5.5-xhigh",
9215
+ handle: "openai/gpt-5.5",
9216
+ label: "GPT-5.5",
9217
+ description: "OpenAI's most capable model (max reasoning)",
9218
+ updateArgs: {
9219
+ reasoning_effort: "xhigh",
9220
+ verbosity: "medium",
9221
+ context_window: 272000,
9222
+ max_output_tokens: 128000,
9223
+ parallel_tool_calls: true
9224
+ }
9225
+ },
9160
9226
  {
9161
9227
  id: "gpt-5.4-none",
9162
9228
  handle: "openai/gpt-5.4",
@@ -9288,6 +9354,46 @@ var init_models2 = __esm(() => {
9288
9354
  parallel_tool_calls: true
9289
9355
  }
9290
9356
  },
9357
+ {
9358
+ id: "gpt-5.5-pro-medium",
9359
+ handle: "openai/gpt-5.5-pro",
9360
+ label: "GPT-5.5 Pro",
9361
+ description: "GPT-5.5 Pro — max performance variant (med reasoning)",
9362
+ updateArgs: {
9363
+ reasoning_effort: "medium",
9364
+ verbosity: "medium",
9365
+ context_window: 272000,
9366
+ max_output_tokens: 128000,
9367
+ parallel_tool_calls: true
9368
+ }
9369
+ },
9370
+ {
9371
+ id: "gpt-5.5-pro-high",
9372
+ handle: "openai/gpt-5.5-pro",
9373
+ label: "GPT-5.5 Pro",
9374
+ description: "GPT-5.5 Pro — max performance variant (high reasoning)",
9375
+ updateArgs: {
9376
+ reasoning_effort: "high",
9377
+ verbosity: "medium",
9378
+ context_window: 272000,
9379
+ max_output_tokens: 128000,
9380
+ parallel_tool_calls: true
9381
+ },
9382
+ isFeatured: true
9383
+ },
9384
+ {
9385
+ id: "gpt-5.5-pro-xhigh",
9386
+ handle: "openai/gpt-5.5-pro",
9387
+ label: "GPT-5.5 Pro",
9388
+ description: "GPT-5.5 Pro — max performance variant (max reasoning)",
9389
+ updateArgs: {
9390
+ reasoning_effort: "xhigh",
9391
+ verbosity: "medium",
9392
+ context_window: 272000,
9393
+ max_output_tokens: 128000,
9394
+ parallel_tool_calls: true
9395
+ }
9396
+ },
9291
9397
  {
9292
9398
  id: "gpt-5.4-pro-medium",
9293
9399
  handle: "openai/gpt-5.4-pro",
@@ -51336,7 +51442,8 @@ var init_readOnlyShell = __esm(() => {
51336
51442
  "sleep"
51337
51443
  ]);
51338
51444
  SAFE_LETTA_COMMANDS = {
51339
- 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"]),
51340
51447
  agents: new Set(["list", "help"]),
51341
51448
  messages: new Set(["search", "list", "help"]),
51342
51449
  blocks: new Set(["list", "help"])
@@ -79649,6 +79756,20 @@ var init_byok_providers = __esm(() => {
79649
79756
  providerType: "google_ai",
79650
79757
  providerName: "lc-gemini"
79651
79758
  },
79759
+ {
79760
+ id: "moonshot",
79761
+ displayName: "Moonshot AI",
79762
+ description: "Connect a Moonshot AI API key",
79763
+ providerType: "moonshot",
79764
+ providerName: "lc-moonshot"
79765
+ },
79766
+ {
79767
+ id: "kimi-code",
79768
+ displayName: "Kimi Code",
79769
+ description: "Connect a Kimi Code API key",
79770
+ providerType: "moonshot_coding",
79771
+ providerName: "lc-kimi-code"
79772
+ },
79652
79773
  {
79653
79774
  id: "openrouter",
79654
79775
  displayName: "OpenRouter API",
@@ -79699,6 +79820,8 @@ var init_byok_providers = __esm(() => {
79699
79820
  google_ai: "google_ai",
79700
79821
  google_vertex: "google_vertex",
79701
79822
  minimax: "minimax",
79823
+ moonshot: "moonshot",
79824
+ moonshot_coding: "moonshot_coding",
79702
79825
  openrouter: "openrouter",
79703
79826
  bedrock: "bedrock"
79704
79827
  };
@@ -79768,6 +79891,8 @@ var init_connect_normalize = __esm(() => {
79768
79891
  zai: "zai",
79769
79892
  "zai-coding": "zai-coding",
79770
79893
  minimax: "minimax",
79894
+ moonshot: "moonshot",
79895
+ "kimi-code": "kimi-code",
79771
79896
  gemini: "gemini",
79772
79897
  openrouter: "openrouter",
79773
79898
  bedrock: "bedrock"
@@ -79779,6 +79904,8 @@ var init_connect_normalize = __esm(() => {
79779
79904
  "zai",
79780
79905
  "zai-coding",
79781
79906
  "minimax",
79907
+ "moonshot",
79908
+ "kimi-code",
79782
79909
  "gemini",
79783
79910
  "openrouter",
79784
79911
  "bedrock"
@@ -83366,46 +83493,58 @@ var init_memoryReminder = __esm(() => {
83366
83493
  };
83367
83494
  });
83368
83495
 
83369
- // src/cli/helpers/systemPromptWarning.ts
83496
+ // src/utils/systemPromptSize.ts
83370
83497
  import { existsSync as existsSync26, readdirSync as readdirSync8, readFileSync as readFileSync19 } from "node:fs";
83371
83498
  import { join as join29 } from "node:path";
83372
83499
  function estimateSystemTokens(text) {
83373
- 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);
83374
83501
  }
83375
- function estimateSystemPromptTokensFromMemoryDir(memoryDir) {
83376
- const systemDir = join29(memoryDir, "system");
83377
- if (!existsSync26(systemDir)) {
83378
- return 0;
83502
+ function normalizePath2(value) {
83503
+ return value.replaceAll("\\", "/");
83504
+ }
83505
+ function walkMarkdownFiles(dir) {
83506
+ if (!existsSync26(dir)) {
83507
+ return [];
83379
83508
  }
83380
- const walkMarkdownFiles = (dir) => {
83381
- if (!existsSync26(dir)) {
83382
- return [];
83509
+ const out = [];
83510
+ const entries = readdirSync8(dir, { withFileTypes: true });
83511
+ for (const entry of entries) {
83512
+ if (entry.name.startsWith(".")) {
83513
+ continue;
83383
83514
  }
83384
- const out = [];
83385
- const entries = readdirSync8(dir, { withFileTypes: true });
83386
- for (const entry of entries) {
83387
- if (entry.name.startsWith(".")) {
83388
- continue;
83389
- }
83390
- const full = join29(dir, entry.name);
83391
- if (entry.isDirectory()) {
83392
- if (entry.name === ".git") {
83393
- continue;
83394
- }
83395
- out.push(...walkMarkdownFiles(full));
83396
- continue;
83397
- }
83398
- if (entry.isFile() && entry.name.endsWith(".md")) {
83399
- out.push(full);
83400
- }
83515
+ const full = join29(dir, entry.name);
83516
+ if (entry.isDirectory()) {
83517
+ out.push(...walkMarkdownFiles(full));
83518
+ continue;
83401
83519
  }
83402
- return out;
83403
- };
83404
- 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) {
83405
83534
  const text = readFileSync19(filePath, "utf8");
83406
- return sum + estimateSystemTokens(text);
83407
- }, 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 };
83540
+ }
83541
+ function estimateSystemPromptTokensFromMemoryDir(memoryDir) {
83542
+ return estimateSystemPromptSize(memoryDir).total;
83408
83543
  }
83544
+ var SYSTEM_PROMPT_BYTES_PER_TOKEN = 4;
83545
+ var init_systemPromptSize = () => {};
83546
+
83547
+ // src/cli/helpers/systemPromptWarning.ts
83409
83548
  function setSystemPromptDoctorState(agentId, estimatedTokens) {
83410
83549
  const nextState = {
83411
83550
  estimated_tokens: estimatedTokens,
@@ -83448,11 +83587,12 @@ function buildStartupSystemPromptWarning(agentState) {
83448
83587
  }
83449
83588
  return null;
83450
83589
  }
83451
- 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;
83452
83591
  var init_systemPromptWarning = __esm(() => {
83453
83592
  init_memoryFilesystem();
83454
83593
  init_settings_manager();
83455
83594
  init_debug();
83595
+ init_systemPromptSize();
83456
83596
  systemPromptDoctorStateByAgent = new Map;
83457
83597
  });
83458
83598
 
@@ -87057,10 +87197,132 @@ var init_reflectionTranscript = __esm(() => {
87057
87197
  init_directoryLimits();
87058
87198
  });
87059
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
+
87060
87322
  // src/cli/helpers/chunkLog.ts
87061
87323
  import {
87062
87324
  existsSync as existsSync28,
87063
- mkdirSync as mkdirSync20,
87325
+ mkdirSync as mkdirSync21,
87064
87326
  readdirSync as readdirSync9,
87065
87327
  unlinkSync as unlinkSync7,
87066
87328
  writeFileSync as writeFileSync16
@@ -87161,7 +87423,7 @@ class ChunkLog {
87161
87423
  return;
87162
87424
  try {
87163
87425
  if (!existsSync28(this.agentDir)) {
87164
- mkdirSync20(this.agentDir, { recursive: true });
87426
+ mkdirSync21(this.agentDir, { recursive: true });
87165
87427
  }
87166
87428
  this.dirCreated = true;
87167
87429
  } catch (e) {
@@ -87400,6 +87662,7 @@ async function drainStream(stream2, buffers, refresh, abortSignal, onFirstMessag
87400
87662
  }
87401
87663
  try {
87402
87664
  for await (const chunk of stream2) {
87665
+ recordTuiJsonPayload(`stream_chunk:${chunk.message_type ?? "unknown"}`, chunk);
87403
87666
  if ((buffers.abortGeneration || 0) !== startAbortGen) {
87404
87667
  stopReason = "cancelled";
87405
87668
  queueMicrotask(refresh);
@@ -87455,6 +87718,7 @@ async function drainStream(stream2, buffers, refresh, abortSignal, onFirstMessag
87455
87718
  shouldAccumulate = shouldOutputChunk;
87456
87719
  }
87457
87720
  if (shouldAccumulate) {
87721
+ recordTuiJsonPayload(`stream_accumulate:${chunk.message_type ?? "unknown"}`, chunk);
87458
87722
  onChunk(buffers, chunk, contextTracker);
87459
87723
  queueMicrotask(refresh);
87460
87724
  }
@@ -87537,6 +87801,7 @@ async function drainStream(stream2, buffers, refresh, abortSignal, onFirstMessag
87537
87801
  }
87538
87802
  async function drainStreamWithResume(stream2, buffers, refresh, abortSignal, onFirstMessage, onChunkProcessed, contextTracker, seenSeqIdThreshold) {
87539
87803
  const overallStartTime = performance.now();
87804
+ recordTuiPerf("stream_lifecycle:start");
87540
87805
  const streamRequestContext = getStreamRequestContext(stream2);
87541
87806
  const streamOtid = streamRequestContext?.otid ?? null;
87542
87807
  let _client;
@@ -87659,6 +87924,9 @@ async function drainStreamWithResume(stream2, buffers, refresh, abortSignal, onF
87659
87924
  markCurrentLineAsFinished(buffers);
87660
87925
  }
87661
87926
  result.apiDurationMs = performance.now() - overallStartTime;
87927
+ recordTuiPerf(`stream_lifecycle:end:${result.stopReason}`, {
87928
+ ms: result.apiDurationMs
87929
+ });
87662
87930
  return result;
87663
87931
  }
87664
87932
  var FALLBACK_RUN_DISCOVERY_TIMEOUT_MS = 5000;
@@ -87669,6 +87937,7 @@ var init_stream = __esm(async () => {
87669
87937
  init_debug();
87670
87938
  init_streamAbortRelay();
87671
87939
  init_timing();
87940
+ init_tuiPerf();
87672
87941
  init_chunkLog();
87673
87942
  await __promiseAll([
87674
87943
  init_message(),
@@ -88767,22 +89036,12 @@ function emitToolExecutionFinishedEvents(socket, runtime, params) {
88767
89036
  }
88768
89037
  function createToolExecutionOutputEmitter(socket, runtime, params) {
88769
89038
  const outputByToolCallId = new Map;
88770
- return (toolCallId, chunk, isStderr = false) => {
88771
- if (!toolCallId || chunk.length === 0) {
89039
+ const emitToolOutput = (toolCallId, outputState) => {
89040
+ if (!outputState.dirty) {
88772
89041
  return;
88773
89042
  }
88774
- const existing = outputByToolCallId.get(toolCallId);
88775
- const outputState = existing ?? {
88776
- messageId: `message-tool-return-stream-${toolCallId}`,
88777
- stdout: "",
88778
- stderr: ""
88779
- };
88780
- if (isStderr) {
88781
- outputState.stderr = appendStreamingOutputWithCap(outputState.stderr, chunk);
88782
- } else {
88783
- outputState.stdout = appendStreamingOutputWithCap(outputState.stdout, chunk);
88784
- }
88785
- outputByToolCallId.set(toolCallId, outputState);
89043
+ outputState.dirty = false;
89044
+ outputState.lastEmittedAt = Date.now();
88786
89045
  const stdout = normalizeStreamingOutputLines(outputState.stdout);
88787
89046
  const stderr = normalizeStreamingOutputLines(outputState.stderr);
88788
89047
  const toolReturn = [stdout?.join(`
@@ -88812,6 +89071,52 @@ function createToolExecutionOutputEmitter(socket, runtime, params) {
88812
89071
  conversation_id: params.conversationId
88813
89072
  });
88814
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;
88815
89120
  }
88816
89121
  function getInterruptApprovalsForEmission(runtime, params) {
88817
89122
  if (params.lastExecutionResults && params.lastExecutionResults.length > 0) {
@@ -88929,7 +89234,7 @@ function stashRecoveredApprovalInterrupts(runtime, recovered) {
88929
89234
  clearRecoveredApprovalState(runtime);
88930
89235
  return true;
88931
89236
  }
88932
- 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;
88933
89238
  var init_interrupts = __esm(async () => {
88934
89239
  init_approval_result_normalization();
88935
89240
  init_constants();
@@ -89766,16 +90071,21 @@ async function resolveRecoveredApprovalResponse(runtime, socket, response, proce
89766
90071
  runtime.currentToolsetPreference = preparedToolContext.toolsetPreference;
89767
90072
  runtime.currentLoadedTools = preparedToolContext.preparedToolContext.loadedToolNames;
89768
90073
  try {
89769
- const approvalResults = await executeApprovalBatch(decisions, undefined, {
89770
- abortSignal: recoveryAbortController.signal,
89771
- onStreamingOutput: emitToolExecutionOutput,
89772
- toolContextId: preparedToolContext.preparedToolContext.contextId,
89773
- workingDirectory,
89774
- parentScope: recovered.agentId && recovered.conversationId ? {
89775
- agentId: recovered.agentId,
89776
- conversationId: recovered.conversationId
89777
- } : undefined
89778
- });
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
+ }
89779
90089
  emitToolExecutionFinishedEvents(socket, runtime, {
89780
90090
  approvals: approvalResults,
89781
90091
  runId: runtime.activeRunId ?? undefined,
@@ -89789,7 +90099,8 @@ async function resolveRecoveredApprovalResponse(runtime, socket, response, proce
89789
90099
  const continuationMessages = [
89790
90100
  {
89791
90101
  type: "approval",
89792
- approvals: approvalResults
90102
+ approvals: approvalResults,
90103
+ otid: crypto.randomUUID()
89793
90104
  }
89794
90105
  ];
89795
90106
  let continuationBatchId = `batch-recovered-${crypto.randomUUID()}`;
@@ -90183,6 +90494,18 @@ async function sendApprovalContinuationWithRetry(conversationId, messages, opts,
90183
90494
  retryAfterMs
90184
90495
  });
90185
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
+ }
90186
90509
  await new Promise((resolve27) => setTimeout(resolve27, delayMs));
90187
90510
  if (abortSignal?.aborted) {
90188
90511
  throw new Error("Cancelled by user");
@@ -90228,6 +90551,15 @@ async function sendApprovalContinuationWithRetry(conversationId, messages, opts,
90228
90551
  category: "conversation_busy",
90229
90552
  attempt: conversationBusyRetries
90230
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
+ });
90231
90563
  await new Promise((resolve27) => setTimeout(resolve27, retryDelayMs));
90232
90564
  if (abortSignal?.aborted) {
90233
90565
  throw new Error("Cancelled by user");
@@ -92584,14 +92916,19 @@ async function handleApprovalStop(params) {
92584
92916
  }));
92585
92917
  }
92586
92918
  };
92587
- const executionResults = await executeApprovalBatch(decisions, undefined, {
92588
- toolContextId: turnToolContextId ?? undefined,
92589
- abortSignal: abortController.signal,
92590
- onStreamingOutput: emitToolExecutionOutput,
92591
- workingDirectory: turnWorkingDirectory,
92592
- parentScope: agentId && conversationId ? { agentId, conversationId } : undefined,
92593
- onFileWrite
92594
- });
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
+ }
92595
92932
  const persistedExecutionResults = normalizeExecutionResultsForInterruptParity(runtime, executionResults, lastExecutingToolCallIds);
92596
92933
  validateApprovalResultIds(decisions.map((decision) => ({
92597
92934
  approval: {
@@ -92612,7 +92949,8 @@ async function handleApprovalStop(params) {
92612
92949
  const nextInput = [
92613
92950
  {
92614
92951
  type: "approval",
92615
- approvals: persistedExecutionResults
92952
+ approvals: persistedExecutionResults,
92953
+ otid: crypto.randomUUID()
92616
92954
  }
92617
92955
  ];
92618
92956
  let continuationBatchId = dequeuedBatchId;
@@ -93772,7 +94110,115 @@ var init_commands = __esm(async () => {
93772
94110
  });
93773
94111
 
93774
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";
93775
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
+ }
93776
94222
  function getCachedDeviceGitContext(cwd2) {
93777
94223
  const now = Date.now();
93778
94224
  const cached = gitContextCache.get(cwd2);
@@ -93979,8 +94425,24 @@ function emitProtocolV2Message(socket, runtime, message, scope) {
93979
94425
  emitted_at: new Date().toISOString(),
93980
94426
  idempotency_key: `${message.type}:${eventSeq}:${crypto.randomUUID()}`
93981
94427
  };
94428
+ const perfEnabled = PROTOCOL_PERF_ENABLED;
94429
+ const stringifyStartedAt = perfEnabled ? performance2.now() : 0;
94430
+ let payload;
93982
94431
  try {
93983
- socket.send(JSON.stringify(outbound));
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
+ }
93984
94446
  } catch (error) {
93985
94447
  console.error(`[Listen V2] Failed to emit ${message.type} (seq=${eventSeq})`, error);
93986
94448
  safeEmitWsEvent("send", "lifecycle", {
@@ -94214,7 +94676,7 @@ function emitStreamDelta(socket, runtime, delta, scope, subagentId) {
94214
94676
  };
94215
94677
  emitProtocolV2Message(socket, runtime, message, scope);
94216
94678
  }
94217
- 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;
94218
94680
  var init_protocol_outbound = __esm(async () => {
94219
94681
  init_memoryFilesystem();
94220
94682
  init_gitContext();
@@ -94230,6 +94692,10 @@ var init_protocol_outbound = __esm(async () => {
94230
94692
  init_permissionMode();
94231
94693
  init_runtime4();
94232
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;
94233
94699
  gitContextCache = new Map;
94234
94700
  });
94235
94701
 
@@ -97429,7 +97895,7 @@ async function handleSkillCommand(parsed, socket) {
97429
97895
  const {
97430
97896
  existsSync: existsSync30,
97431
97897
  lstatSync: lstatSync2,
97432
- mkdirSync: mkdirSync21,
97898
+ mkdirSync: mkdirSync23,
97433
97899
  rmdirSync,
97434
97900
  symlinkSync,
97435
97901
  unlinkSync: unlinkSync8
@@ -97460,7 +97926,7 @@ async function handleSkillCommand(parsed, socket) {
97460
97926
  }
97461
97927
  const linkName = basename13(parsed.skill_path);
97462
97928
  const linkPath = join34(globalSkillsDir, linkName);
97463
- mkdirSync21(globalSkillsDir, { recursive: true });
97929
+ mkdirSync23(globalSkillsDir, { recursive: true });
97464
97930
  if (existsSync30(linkPath)) {
97465
97931
  const stat7 = lstatSync2(linkPath);
97466
97932
  if (stat7.isSymbolicLink()) {
@@ -99660,9 +100126,9 @@ __export(exports_debug2, {
99660
100126
  debugLog: () => debugLog3
99661
100127
  });
99662
100128
  import {
99663
- appendFileSync as appendFileSync3,
100129
+ appendFileSync as appendFileSync5,
99664
100130
  existsSync as existsSync31,
99665
- mkdirSync as mkdirSync22,
100131
+ mkdirSync as mkdirSync24,
99666
100132
  readdirSync as readdirSync11,
99667
100133
  readFileSync as readFileSync22,
99668
100134
  unlinkSync as unlinkSync8
@@ -99683,7 +100149,7 @@ function printDebugLine2(line, level = "log") {
99683
100149
  const debugFile = getDebugFile2();
99684
100150
  if (debugFile) {
99685
100151
  try {
99686
- appendFileSync3(debugFile, line, { encoding: "utf8" });
100152
+ appendFileSync5(debugFile, line, { encoding: "utf8" });
99687
100153
  return;
99688
100154
  } catch {}
99689
100155
  }
@@ -99709,7 +100175,7 @@ class DebugLogFile2 {
99709
100175
  return;
99710
100176
  this.ensureDir();
99711
100177
  try {
99712
- appendFileSync3(this.logPath, line, { encoding: "utf8" });
100178
+ appendFileSync5(this.logPath, line, { encoding: "utf8" });
99713
100179
  } catch {}
99714
100180
  }
99715
100181
  getTail(maxLines = DEFAULT_TAIL_LINES2) {
@@ -99732,7 +100198,7 @@ class DebugLogFile2 {
99732
100198
  return;
99733
100199
  try {
99734
100200
  if (!existsSync31(this.agentDir)) {
99735
- mkdirSync22(this.agentDir, { recursive: true });
100201
+ mkdirSync24(this.agentDir, { recursive: true });
99736
100202
  }
99737
100203
  this.dirCreated = true;
99738
100204
  } catch {}
@@ -99802,10 +100268,10 @@ __export(exports_skills2, {
99802
100268
  });
99803
100269
  import { existsSync as existsSync32 } from "node:fs";
99804
100270
  import { readdir as readdir8, readFile as readFile13, realpath as realpath4, stat as stat7 } from "node:fs/promises";
99805
- import { dirname as dirname14, join as join38 } from "node:path";
100271
+ import { dirname as dirname16, join as join38 } from "node:path";
99806
100272
  import { fileURLToPath as fileURLToPath9 } from "node:url";
99807
100273
  function getBundledSkillsPath2() {
99808
- const thisDir = dirname14(fileURLToPath9(import.meta.url));
100274
+ const thisDir = dirname16(fileURLToPath9(import.meta.url));
99809
100275
  if (thisDir.includes("src/agent") || thisDir.includes("src\\agent")) {
99810
100276
  return join38(thisDir, "../skills/builtin");
99811
100277
  }
@@ -99999,16 +100465,16 @@ import {
99999
100465
  existsSync as existsSync33,
100000
100466
  readFileSync as fsReadFileSync2,
100001
100467
  writeFileSync as fsWriteFileSync2,
100002
- mkdirSync as mkdirSync23
100468
+ mkdirSync as mkdirSync25
100003
100469
  } from "node:fs";
100004
- import { dirname as dirname15 } from "node:path";
100470
+ import { dirname as dirname17 } from "node:path";
100005
100471
  async function readFile14(path26) {
100006
100472
  return fsReadFileSync2(path26, { encoding: "utf-8" });
100007
100473
  }
100008
100474
  async function writeFile12(path26, content) {
100009
- const dir = dirname15(path26);
100475
+ const dir = dirname17(path26);
100010
100476
  if (!existsSync33(dir)) {
100011
- mkdirSync23(dir, { recursive: true });
100477
+ mkdirSync25(dir, { recursive: true });
100012
100478
  }
100013
100479
  fsWriteFileSync2(path26, content, { encoding: "utf-8", flush: true });
100014
100480
  }
@@ -100016,7 +100482,7 @@ function exists2(path26) {
100016
100482
  return existsSync33(path26);
100017
100483
  }
100018
100484
  async function mkdir9(path26, options) {
100019
- mkdirSync23(path26, options);
100485
+ mkdirSync25(path26, options);
100020
100486
  }
100021
100487
  async function readJsonFile(path26) {
100022
100488
  const text = await readFile14(path26);
@@ -100149,7 +100615,7 @@ var exports_bootstrap_tools = {};
100149
100615
  __export(exports_bootstrap_tools, {
100150
100616
  bootstrapBaseToolsIfNeeded: () => bootstrapBaseToolsIfNeeded
100151
100617
  });
100152
- import { existsSync as existsSync34, mkdirSync as mkdirSync24, writeFileSync as writeFileSync17 } from "node:fs";
100618
+ import { existsSync as existsSync34, mkdirSync as mkdirSync26, writeFileSync as writeFileSync17 } from "node:fs";
100153
100619
  import { homedir as homedir27 } from "node:os";
100154
100620
  import { join as join39 } from "node:path";
100155
100621
  async function bootstrapBaseToolsIfNeeded() {
@@ -100159,7 +100625,7 @@ async function bootstrapBaseToolsIfNeeded() {
100159
100625
  try {
100160
100626
  const success = await addBaseToolsToServer();
100161
100627
  if (success) {
100162
- mkdirSync24(join39(homedir27(), ".letta"), { recursive: true });
100628
+ mkdirSync26(join39(homedir27(), ".letta"), { recursive: true });
100163
100629
  writeFileSync17(MARKER_PATH, new Date().toISOString(), "utf-8");
100164
100630
  }
100165
100631
  } catch (err) {
@@ -101863,7 +102329,7 @@ __export(exports_import, {
101863
102329
  });
101864
102330
  import { createReadStream } from "node:fs";
101865
102331
  import { chmod, mkdir as mkdir10, readFile as readFile15, writeFile as writeFile13 } from "node:fs/promises";
101866
- import { dirname as dirname16, resolve as resolve30 } from "node:path";
102332
+ import { dirname as dirname18, resolve as resolve30 } from "node:path";
101867
102333
  async function importAgentFromFile(options) {
101868
102334
  const client = await getClient();
101869
102335
  const resolvedPath = resolve30(options.filePath);
@@ -101922,7 +102388,7 @@ async function writeSkillFiles(skillDir, files) {
101922
102388
  }
101923
102389
  async function writeSkillFile(skillDir, filePath, content) {
101924
102390
  const fullPath = resolve30(skillDir, filePath);
101925
- await mkdir10(dirname16(fullPath), { recursive: true });
102391
+ await mkdir10(dirname18(fullPath), { recursive: true });
101926
102392
  await writeFile13(fullPath, content, "utf-8");
101927
102393
  const isScript = filePath.startsWith("scripts/") || content.trimStart().startsWith("#!");
101928
102394
  if (isScript) {
@@ -102276,6 +102742,15 @@ async function flushAndExit(code) {
102276
102742
  ]);
102277
102743
  process.exit(code);
102278
102744
  }
102745
+ async function writeFinalHeadlessStdout(text) {
102746
+ await new Promise((resolve31) => {
102747
+ if (process.stdout.destroyed || process.stdout.writableEnded) {
102748
+ resolve31();
102749
+ return;
102750
+ }
102751
+ process.stdout.write(text, () => resolve31());
102752
+ });
102753
+ }
102279
102754
  async function handleHeadlessCommand(parsedArgs, model, skillsDirectoryOverride, skillSourcesOverride, systemInfoReminderEnabledOverride) {
102280
102755
  const { values, positionals } = parsedArgs;
102281
102756
  telemetry.setSurface("headless");
@@ -103734,7 +104209,8 @@ ${loadedContents.join(`
103734
104209
  conversation_id: conversationId,
103735
104210
  usage
103736
104211
  };
103737
- console.log(JSON.stringify(output, null, 2));
104212
+ await writeFinalHeadlessStdout(`${JSON.stringify(output, null, 2)}
104213
+ `);
103738
104214
  } else if (outputFormat === "stream-json") {
103739
104215
  const allRunIds = new Set;
103740
104216
  for (const line of toLines(buffers)) {
@@ -103763,7 +104239,8 @@ ${loadedContents.join(`
103763
104239
  console.error("No assistant response found");
103764
104240
  await exitHeadless(1, "headless_missing_result_text");
103765
104241
  }
103766
- console.log(resultText);
104242
+ await writeFinalHeadlessStdout(`${resultText}
104243
+ `);
103767
104244
  }
103768
104245
  markMilestone("HEADLESS_COMPLETE");
103769
104246
  reportAllMilestones();
@@ -124566,7 +125043,7 @@ html.dark .agent-name { color: var(--text-dim); }
124566
125043
  var init_plan_viewer_template = () => {};
124567
125044
 
124568
125045
  // src/web/generate-plan-viewer.ts
124569
- import { chmodSync as chmodSync2, existsSync as existsSync37, mkdirSync as mkdirSync27, writeFileSync as writeFileSync20 } from "node:fs";
125046
+ import { chmodSync as chmodSync2, existsSync as existsSync37, mkdirSync as mkdirSync29, writeFileSync as writeFileSync20 } from "node:fs";
124570
125047
  import { homedir as homedir31 } from "node:os";
124571
125048
  import { join as join44 } from "node:path";
124572
125049
  async function generateAndOpenPlanViewer(planContent, planFilePath, options) {
@@ -124579,7 +125056,7 @@ async function generateAndOpenPlanViewer(planContent, planFilePath, options) {
124579
125056
  const jsonPayload = JSON.stringify(data).replace(/</g, "\\u003c");
124580
125057
  const html = plan_viewer_template_default.replace("<!--LETTA_PLAN_DATA_PLACEHOLDER-->", () => jsonPayload);
124581
125058
  if (!existsSync37(VIEWERS_DIR)) {
124582
- mkdirSync27(VIEWERS_DIR, { recursive: true, mode: 448 });
125059
+ mkdirSync29(VIEWERS_DIR, { recursive: true, mode: 448 });
124583
125060
  }
124584
125061
  try {
124585
125062
  chmodSync2(VIEWERS_DIR, 448);
@@ -127889,12 +128366,12 @@ __export(exports_terminalKeybindingInstaller, {
127889
128366
  import {
127890
128367
  copyFileSync,
127891
128368
  existsSync as existsSync39,
127892
- mkdirSync as mkdirSync28,
128369
+ mkdirSync as mkdirSync30,
127893
128370
  readFileSync as readFileSync25,
127894
128371
  writeFileSync as writeFileSync21
127895
128372
  } from "node:fs";
127896
128373
  import { homedir as homedir32, platform as platform6 } from "node:os";
127897
- import { dirname as dirname17, join as join46 } from "node:path";
128374
+ import { dirname as dirname19, join as join46 } from "node:path";
127898
128375
  function detectTerminalType() {
127899
128376
  if (process.env.CURSOR_TRACE_ID || process.env.CURSOR_CHANNEL) {
127900
128377
  return "cursor";
@@ -127985,9 +128462,9 @@ function installKeybinding(keybindingsPath) {
127985
128462
  if (keybindingExists(keybindingsPath)) {
127986
128463
  return { success: true, alreadyExists: true };
127987
128464
  }
127988
- const parentDir = dirname17(keybindingsPath);
128465
+ const parentDir = dirname19(keybindingsPath);
127989
128466
  if (!existsSync39(parentDir)) {
127990
- mkdirSync28(parentDir, { recursive: true });
128467
+ mkdirSync30(parentDir, { recursive: true });
127991
128468
  }
127992
128469
  let keybindings = [];
127993
128470
  let backupPath = null;
@@ -128144,9 +128621,9 @@ return config`);
128144
128621
  ${WEZTERM_DELETE_FIX}
128145
128622
  `;
128146
128623
  }
128147
- const parentDir = dirname17(configPath);
128624
+ const parentDir = dirname19(configPath);
128148
128625
  if (!existsSync39(parentDir)) {
128149
- mkdirSync28(parentDir, { recursive: true });
128626
+ mkdirSync30(parentDir, { recursive: true });
128150
128627
  }
128151
128628
  writeFileSync21(configPath, content, { encoding: "utf-8" });
128152
128629
  return {
@@ -128744,7 +129221,7 @@ __export(exports_custom, {
128744
129221
  });
128745
129222
  import { existsSync as existsSync40 } from "node:fs";
128746
129223
  import { readdir as readdir10, readFile as readFile16 } from "node:fs/promises";
128747
- import { basename as basename14, dirname as dirname18, join as join47 } from "node:path";
129224
+ import { basename as basename14, dirname as dirname20, join as join47 } from "node:path";
128748
129225
  async function getCustomCommands() {
128749
129226
  if (cachedCommands !== null) {
128750
129227
  return cachedCommands;
@@ -128805,7 +129282,7 @@ async function parseCommandFile(filePath, rootPath, source2) {
128805
129282
  const content = await readFile16(filePath, "utf-8");
128806
129283
  const { frontmatter, body } = parseFrontmatter(content);
128807
129284
  const id = basename14(filePath, ".md");
128808
- const relativePath = dirname18(filePath).slice(rootPath.length);
129285
+ const relativePath = dirname20(filePath).slice(rootPath.length);
128809
129286
  const namespace = relativePath.replace(/^[/\\]/, "") || undefined;
128810
129287
  let description = getStringField(frontmatter, "description");
128811
129288
  if (!description) {
@@ -133965,14 +134442,14 @@ var init_InputRich = __esm(async () => {
133965
134442
  import { execFileSync as execFileSync4 } from "node:child_process";
133966
134443
  import {
133967
134444
  existsSync as existsSync41,
133968
- mkdirSync as mkdirSync29,
134445
+ mkdirSync as mkdirSync31,
133969
134446
  mkdtempSync,
133970
134447
  readFileSync as readFileSync26,
133971
134448
  rmSync as rmSync4,
133972
134449
  writeFileSync as writeFileSync22
133973
134450
  } from "node:fs";
133974
134451
  import { tmpdir as tmpdir6 } from "node:os";
133975
- import { dirname as dirname19, join as join49 } from "node:path";
134452
+ import { dirname as dirname21, join as join49 } from "node:path";
133976
134453
  function runCommand(command, args, cwd2, input) {
133977
134454
  try {
133978
134455
  return execFileSync4(command, args, {
@@ -134219,8 +134696,8 @@ function runGit3(args, cwd2) {
134219
134696
  }
134220
134697
  function writeWorkflow(repoDir, workflowPath, content) {
134221
134698
  const absolutePath = join49(repoDir, workflowPath);
134222
- if (!existsSync41(dirname19(absolutePath))) {
134223
- mkdirSync29(dirname19(absolutePath), { recursive: true });
134699
+ if (!existsSync41(dirname21(absolutePath))) {
134700
+ mkdirSync31(dirname21(absolutePath), { recursive: true });
134224
134701
  }
134225
134702
  const next = `${content.trimEnd()}
134226
134703
  `;
@@ -138462,7 +138939,7 @@ __export(exports_generate_memory_viewer, {
138462
138939
  generateAndOpenMemoryViewer: () => generateAndOpenMemoryViewer
138463
138940
  });
138464
138941
  import { execFile as execFileCb3 } from "node:child_process";
138465
- import { chmodSync as chmodSync3, existsSync as existsSync42, mkdirSync as mkdirSync30, writeFileSync as writeFileSync23 } from "node:fs";
138942
+ import { chmodSync as chmodSync3, existsSync as existsSync42, mkdirSync as mkdirSync32, writeFileSync as writeFileSync23 } from "node:fs";
138466
138943
  import { homedir as homedir34 } from "node:os";
138467
138944
  import { join as join50 } from "node:path";
138468
138945
  import { promisify as promisify13 } from "node:util";
@@ -138783,7 +139260,7 @@ async function generateAndOpenMemoryViewer(agentId, options) {
138783
139260
  const jsonPayload = JSON.stringify(data).replace(/</g, "\\u003c");
138784
139261
  const html = memory_viewer_template_default.replace("<!--LETTA_DATA_PLACEHOLDER-->", () => jsonPayload);
138785
139262
  if (!existsSync42(VIEWERS_DIR2)) {
138786
- mkdirSync30(VIEWERS_DIR2, { recursive: true, mode: 448 });
139263
+ mkdirSync32(VIEWERS_DIR2, { recursive: true, mode: 448 });
138787
139264
  }
138788
139265
  try {
138789
139266
  chmodSync3(VIEWERS_DIR2, 448);
@@ -142593,7 +143070,7 @@ function formatDuration3(ms) {
142593
143070
  }
142594
143071
  return `${(ms / 1000).toFixed(1)}s`;
142595
143072
  }
142596
- function formatNumber(n) {
143073
+ function formatNumber2(n) {
142597
143074
  return n.toLocaleString();
142598
143075
  }
142599
143076
  function formatUsageStats({
@@ -142615,7 +143092,7 @@ function formatUsageStats({
142615
143092
  const monthlyCredits = Math.round(balance.monthly_credit_balance);
142616
143093
  const purchasedCredits = Math.round(balance.purchased_credit_balance);
142617
143094
  const toDollars = (credits) => (credits / 1000).toFixed(2);
142618
- 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)})`);
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)})`);
142619
143096
  }
142620
143097
  return outputLines.join(`
142621
143098
  `);
@@ -149886,6 +150363,7 @@ function App2({
149886
150363
  streamingRefreshTimeoutRef.current = setTimeout(() => {
149887
150364
  streamingRefreshTimeoutRef.current = null;
149888
150365
  if (!buffersRef.current.interrupted) {
150366
+ recordTuiPerf("ui_refresh:tool_output");
149889
150367
  refreshDerived();
149890
150368
  }
149891
150369
  }, 100);
@@ -149898,6 +150376,9 @@ function App2({
149898
150376
  };
149899
150377
  }, []);
149900
150378
  const updateStreamingOutput = import_react104.useCallback((toolCallId, chunk, isStderr = false) => {
150379
+ recordTuiPerf(`tool_output:${isStderr ? "stderr" : "stdout"}`, {
150380
+ bytes: Buffer.byteLength(chunk)
150381
+ });
149901
150382
  const lineId = buffersRef.current.toolCallIdToLineId.get(toolCallId);
149902
150383
  if (!lineId)
149903
150384
  return;
@@ -149918,6 +150399,7 @@ function App2({
149918
150399
  setTimeout(() => {
149919
150400
  buffersRef.current.pendingRefresh = false;
149920
150401
  if (!buffersRef.current.interrupted && (buffersRef.current.commitGeneration || 0) === capturedGeneration) {
150402
+ recordTuiPerf("ui_refresh:stream");
149921
150403
  refreshDerived();
149922
150404
  }
149923
150405
  }, 16);
@@ -157431,6 +157913,7 @@ var init_App2 = __esm(async () => {
157431
157913
  init_telemetry();
157432
157914
  init_toolset_labels();
157433
157915
  init_debug();
157916
+ init_tuiPerf();
157434
157917
  init_version();
157435
157918
  init_mcp();
157436
157919
  init_profile();
@@ -157611,12 +158094,12 @@ __export(exports_terminalKeybindingInstaller2, {
157611
158094
  import {
157612
158095
  copyFileSync as copyFileSync2,
157613
158096
  existsSync as existsSync46,
157614
- mkdirSync as mkdirSync31,
158097
+ mkdirSync as mkdirSync33,
157615
158098
  readFileSync as readFileSync29,
157616
158099
  writeFileSync as writeFileSync25
157617
158100
  } from "node:fs";
157618
158101
  import { homedir as homedir38, platform as platform7 } from "node:os";
157619
- import { dirname as dirname20, join as join54 } from "node:path";
158102
+ import { dirname as dirname22, join as join54 } from "node:path";
157620
158103
  function detectTerminalType2() {
157621
158104
  if (process.env.CURSOR_TRACE_ID || process.env.CURSOR_CHANNEL) {
157622
158105
  return "cursor";
@@ -157707,9 +158190,9 @@ function installKeybinding2(keybindingsPath) {
157707
158190
  if (keybindingExists2(keybindingsPath)) {
157708
158191
  return { success: true, alreadyExists: true };
157709
158192
  }
157710
- const parentDir = dirname20(keybindingsPath);
158193
+ const parentDir = dirname22(keybindingsPath);
157711
158194
  if (!existsSync46(parentDir)) {
157712
- mkdirSync31(parentDir, { recursive: true });
158195
+ mkdirSync33(parentDir, { recursive: true });
157713
158196
  }
157714
158197
  let keybindings = [];
157715
158198
  let backupPath = null;
@@ -157866,9 +158349,9 @@ return config`);
157866
158349
  ${WEZTERM_DELETE_FIX2}
157867
158350
  `;
157868
158351
  }
157869
- const parentDir = dirname20(configPath);
158352
+ const parentDir = dirname22(configPath);
157870
158353
  if (!existsSync46(parentDir)) {
157871
- mkdirSync31(parentDir, { recursive: true });
158354
+ mkdirSync33(parentDir, { recursive: true });
157872
158355
  }
157873
158356
  writeFileSync25(configPath, content, { encoding: "utf-8" });
157874
158357
  return {
@@ -158436,7 +158919,7 @@ __export(exports_import2, {
158436
158919
  });
158437
158920
  import { createReadStream as createReadStream2 } from "node:fs";
158438
158921
  import { chmod as chmod2, mkdir as mkdir11, readFile as readFile19, writeFile as writeFile14 } from "node:fs/promises";
158439
- import { dirname as dirname21, resolve as resolve35 } from "node:path";
158922
+ import { dirname as dirname23, resolve as resolve35 } from "node:path";
158440
158923
  async function importAgentFromFile2(options) {
158441
158924
  const client = await getClient();
158442
158925
  const resolvedPath = resolve35(options.filePath);
@@ -158495,7 +158978,7 @@ async function writeSkillFiles2(skillDir, files) {
158495
158978
  }
158496
158979
  async function writeSkillFile2(skillDir, filePath, content) {
158497
158980
  const fullPath = resolve35(skillDir, filePath);
158498
- await mkdir11(dirname21(fullPath), { recursive: true });
158981
+ await mkdir11(dirname23(fullPath), { recursive: true });
158499
158982
  await writeFile14(fullPath, content, "utf-8");
158500
158983
  const isScript = filePath.startsWith("scripts/") || content.trimStart().startsWith("#!");
158501
158984
  if (isScript) {
@@ -158605,7 +159088,7 @@ __export(exports_memoryFilesystem2, {
158605
159088
  MEMORY_FS_MEMORY_DIR: () => MEMORY_FS_MEMORY_DIR2,
158606
159089
  MEMORY_FS_AGENTS_DIR: () => MEMORY_FS_AGENTS_DIR2
158607
159090
  });
158608
- import { existsSync as existsSync47, mkdirSync as mkdirSync32 } from "node:fs";
159091
+ import { existsSync as existsSync47, mkdirSync as mkdirSync34 } from "node:fs";
158609
159092
  import { homedir as homedir40 } from "node:os";
158610
159093
  import { join as join56, resolve as resolve36 } from "node:path";
158611
159094
  function getMemoryFilesystemRoot2(agentId, homeDir = homedir40()) {
@@ -158641,10 +159124,10 @@ function ensureMemoryFilesystemDirs2(agentId, homeDir = homedir40()) {
158641
159124
  const root = getMemoryFilesystemRoot2(agentId, homeDir);
158642
159125
  const systemDir = getMemorySystemDir2(agentId, homeDir);
158643
159126
  if (!existsSync47(root)) {
158644
- mkdirSync32(root, { recursive: true });
159127
+ mkdirSync34(root, { recursive: true });
158645
159128
  }
158646
159129
  if (!existsSync47(systemDir)) {
158647
- mkdirSync32(systemDir, { recursive: true });
159130
+ mkdirSync34(systemDir, { recursive: true });
158648
159131
  }
158649
159132
  }
158650
159133
  async function isMemfsEnabledOnServer2(agentId) {
@@ -163965,51 +164448,141 @@ async function runListenSubcommand(argv) {
163965
164448
  }
163966
164449
  }
163967
164450
 
163968
- // src/cli/subcommands/memfs.ts
164451
+ // src/cli/subcommands/memory.ts
163969
164452
  init_memoryGit();
163970
- import { cpSync, existsSync as existsSync30, mkdirSync as mkdirSync21, rmSync as rmSync3, statSync as statSync8 } from "node:fs";
164453
+ import { cpSync, existsSync as existsSync30, mkdirSync as mkdirSync23, rmSync as rmSync3, statSync as statSync8 } from "node:fs";
163971
164454
  import { readdir as readdir7 } from "node:fs/promises";
163972
164455
  import { homedir as homedir23 } from "node:os";
163973
164456
  import { join as join34 } from "node:path";
163974
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
163975
164538
  function printUsage5() {
163976
164539
  console.log(`
163977
164540
  Usage:
163978
- letta memfs status [--agent <id>]
163979
- letta memfs diff [--agent <id>]
163980
- letta memfs backup [--agent <id>]
163981
- letta memfs backups [--agent <id>]
163982
- letta memfs restore --from <backup> --force [--agent <id>]
163983
- letta memfs export --agent <id> --out <dir>
163984
- letta memfs pull [--agent <id>]
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]
163985
164550
 
163986
164551
  Notes:
163987
- - Requires agent id via --agent or LETTA_AGENT_ID.
163988
- - Output is JSON only.
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.
163989
164556
  - Memory is git-backed. Use git commands for commit/push.
163990
164557
 
163991
164558
  Examples:
163992
- LETTA_AGENT_ID=agent-123 letta memfs status
163993
- letta memfs pull --agent agent-123
163994
- letta memfs backup --agent agent-123
163995
- letta memfs export --agent agent-123 --out /tmp/letta-memfs-agent-123
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
163996
164565
  `.trim());
163997
164566
  }
163998
164567
  function getAgentId5(agentFromArgs, agentIdFromArgs) {
163999
164568
  return agentFromArgs || agentIdFromArgs || process.env.LETTA_AGENT_ID || "";
164000
164569
  }
164001
- var MEMFS_OPTIONS = {
164570
+ var MEMORY_OPTIONS = {
164002
164571
  help: { type: "boolean", short: "h" },
164003
164572
  agent: { type: "string" },
164004
164573
  "agent-id": { type: "string" },
164005
164574
  from: { type: "string" },
164006
164575
  force: { type: "boolean" },
164007
- out: { type: "string" }
164576
+ out: { type: "string" },
164577
+ "memory-dir": { type: "string" },
164578
+ top: { type: "string" },
164579
+ format: { type: "string" },
164580
+ quiet: { type: "boolean" }
164008
164581
  };
164009
- function parseMemfsArgs(argv) {
164582
+ function parseMemoryArgs(argv) {
164010
164583
  return parseArgs8({
164011
164584
  args: argv,
164012
- options: MEMFS_OPTIONS,
164585
+ options: MEMORY_OPTIONS,
164013
164586
  strict: true,
164014
164587
  allowPositionals: true
164015
164588
  });
@@ -164061,10 +164634,10 @@ function resolveBackupPath(agentId, from) {
164061
164634
  }
164062
164635
  return join34(getAgentRoot(agentId), from);
164063
164636
  }
164064
- async function runMemfsSubcommand(argv) {
164637
+ async function runMemorySubcommand(argv) {
164065
164638
  let parsed;
164066
164639
  try {
164067
- parsed = parseMemfsArgs(argv);
164640
+ parsed = parseMemoryArgs(argv);
164068
164641
  } catch (error) {
164069
164642
  const message = error instanceof Error ? error.message : String(error);
164070
164643
  console.error(`Error: ${message}`);
@@ -164077,6 +164650,15 @@ async function runMemfsSubcommand(argv) {
164077
164650
  return 0;
164078
164651
  }
164079
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
+ }
164080
164662
  if (!agentId) {
164081
164663
  console.error("Missing agent id. Set LETTA_AGENT_ID or pass --agent/--agent-id.");
164082
164664
  return 1;
@@ -164189,7 +164771,7 @@ async function runMemfsSubcommand(argv) {
164189
164771
  return 1;
164190
164772
  }
164191
164773
  } else {
164192
- mkdirSync21(out, { recursive: true });
164774
+ mkdirSync23(out, { recursive: true });
164193
164775
  }
164194
164776
  cpSync(root, out, { recursive: true });
164195
164777
  console.log(JSON.stringify({ exportedFrom: root, exportedTo: out, agentId }, null, 2));
@@ -164548,8 +165130,9 @@ async function runSubcommand(argv) {
164548
165130
  return null;
164549
165131
  }
164550
165132
  switch (command) {
165133
+ case "memory":
164551
165134
  case "memfs":
164552
- return runMemfsSubcommand(rest);
165135
+ return runMemorySubcommand(rest);
164553
165136
  case "agents":
164554
165137
  return runAgentsSubcommand(rest);
164555
165138
  case "messages":
@@ -166718,7 +167301,7 @@ USAGE
166718
167301
 
166719
167302
  # maintenance
166720
167303
  letta update Manually check for updates and install if available
166721
- letta memfs ... Memory filesystem subcommands (JSON-only)
167304
+ letta memory ... Memory filesystem subcommands
166722
167305
  letta agents ... Agents subcommands (JSON-only)
166723
167306
  letta messages ... Messages subcommands (JSON-only)
166724
167307
  letta blocks ... Blocks subcommands (JSON-only)
@@ -166727,14 +167310,16 @@ USAGE
166727
167310
  OPTIONS
166728
167311
  ${renderCliOptionsHelp()}
166729
167312
 
166730
- SUBCOMMANDS (JSON-only)
166731
- letta memfs status --agent <id>
166732
- letta memfs diff --agent <id>
166733
- letta memfs resolve --agent <id> --resolutions '<JSON>'
166734
- letta memfs backup --agent <id>
166735
- letta memfs backups --agent <id>
166736
- letta memfs restore --agent <id> --from <backup> --force
166737
- letta memfs export --agent <id> --out <dir>
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]
166738
167323
  letta agents list [--query <text> | --name <name> | --tags <tags>]
166739
167324
  letta messages search --query <text> [--all-agents]
166740
167325
  letta messages list [--agent <id>]
@@ -168068,4 +168653,4 @@ Error during initialization: ${message}`);
168068
168653
  }
168069
168654
  main();
168070
168655
 
168071
- //# debugId=E3F9508D8DDEF15664756E2164756E21
168656
+ //# debugId=22CE2EBE31776A7764756E2164756E21