@letta-ai/letta-code 0.1.6 → 0.1.8

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
@@ -115467,23 +115467,32 @@ async function isBinaryFile(filePath) {
115467
115467
  const fd = await fs4.open(filePath, "r");
115468
115468
  try {
115469
115469
  const stats = await fd.stat();
115470
- const bufferSize = Math.min(4096, stats.size);
115470
+ const bufferSize = Math.min(8192, stats.size);
115471
115471
  if (bufferSize === 0)
115472
115472
  return false;
115473
115473
  const buffer = Buffer.alloc(bufferSize);
115474
115474
  const { bytesRead } = await fd.read(buffer, 0, bufferSize, 0);
115475
115475
  if (bytesRead === 0)
115476
115476
  return false;
115477
- for (let i = 0;i < bytesRead; i++)
115477
+ for (let i = 0;i < bytesRead; i++) {
115478
115478
  if (buffer[i] === 0)
115479
115479
  return true;
115480
- let nonPrintableCount = 0;
115481
- for (let i = 0;i < bytesRead; i++) {
115482
- const byte = buffer[i];
115483
- if (byte < 9 || byte > 13 && byte < 32 || byte > 126)
115484
- nonPrintableCount++;
115485
115480
  }
115486
- return nonPrintableCount / bytesRead > 0.3;
115481
+ try {
115482
+ const text = buffer.slice(0, bytesRead).toString("utf-8");
115483
+ if (text.includes("�"))
115484
+ return true;
115485
+ let controlCharCount = 0;
115486
+ for (let i = 0;i < text.length; i++) {
115487
+ const code = text.charCodeAt(i);
115488
+ if (code < 9 || code > 13 && code < 32) {
115489
+ controlCharCount++;
115490
+ }
115491
+ }
115492
+ return controlCharCount / text.length > 0.3;
115493
+ } catch {
115494
+ return true;
115495
+ }
115487
115496
  } finally {
115488
115497
  await fd.close();
115489
115498
  }
@@ -118564,6 +118573,13 @@ function analyzeDefaultApproval(toolName) {
118564
118573
  var init_analyzer = () => {};
118565
118574
 
118566
118575
  // src/tools/manager.ts
118576
+ function getRegistry2() {
118577
+ const global2 = globalThis;
118578
+ if (!global2[REGISTRY_KEY2]) {
118579
+ global2[REGISTRY_KEY2] = new Map;
118580
+ }
118581
+ return global2[REGISTRY_KEY2];
118582
+ }
118567
118583
  async function checkToolPermission(toolName, toolArgs, workingDirectory = process.cwd()) {
118568
118584
  const { checkPermission: checkPermission2 } = await Promise.resolve().then(() => (init_checker(), exports_checker));
118569
118585
  const { loadPermissions: loadPermissions2 } = await Promise.resolve().then(() => (init_loader(), exports_loader));
@@ -118689,11 +118705,12 @@ async function executeTool(name, args) {
118689
118705
  function getToolNames() {
118690
118706
  return Array.from(toolRegistry2.keys());
118691
118707
  }
118692
- var TOOL_NAMES2, toolRegistry2;
118708
+ var TOOL_NAMES2, REGISTRY_KEY2, toolRegistry2;
118693
118709
  var init_manager = __esm(() => {
118694
118710
  init_toolDefinitions();
118695
118711
  TOOL_NAMES2 = Object.keys(TOOL_DEFINITIONS);
118696
- toolRegistry2 = new Map;
118712
+ REGISTRY_KEY2 = Symbol.for("@letta/toolRegistry");
118713
+ toolRegistry2 = getRegistry2();
118697
118714
  });
118698
118715
 
118699
118716
  // src/agent/prompts/human.mdx
@@ -119168,6 +119185,52 @@ var init_message = __esm(() => {
119168
119185
  init_client();
119169
119186
  });
119170
119187
 
119188
+ // src/agent/stats.ts
119189
+ class SessionStats {
119190
+ sessionStartMs;
119191
+ totalApiMs;
119192
+ usage;
119193
+ constructor() {
119194
+ this.sessionStartMs = performance.now();
119195
+ this.totalApiMs = 0;
119196
+ this.usage = {
119197
+ promptTokens: 0,
119198
+ completionTokens: 0,
119199
+ totalTokens: 0,
119200
+ cachedTokens: 0,
119201
+ reasoningTokens: 0,
119202
+ stepCount: 0
119203
+ };
119204
+ }
119205
+ endTurn(apiDurationMs) {
119206
+ this.totalApiMs += apiDurationMs;
119207
+ }
119208
+ updateUsageFromBuffers(buffers) {
119209
+ this.usage = { ...buffers.usage };
119210
+ }
119211
+ getSnapshot() {
119212
+ const now = performance.now();
119213
+ return {
119214
+ sessionStartMs: this.sessionStartMs,
119215
+ totalWallMs: now - this.sessionStartMs,
119216
+ totalApiMs: this.totalApiMs,
119217
+ usage: { ...this.usage }
119218
+ };
119219
+ }
119220
+ reset() {
119221
+ this.sessionStartMs = performance.now();
119222
+ this.totalApiMs = 0;
119223
+ this.usage = {
119224
+ promptTokens: 0,
119225
+ completionTokens: 0,
119226
+ totalTokens: 0,
119227
+ cachedTokens: 0,
119228
+ reasoningTokens: 0,
119229
+ stepCount: 0
119230
+ };
119231
+ }
119232
+ }
119233
+
119171
119234
  // src/cli/helpers/accumulator.ts
119172
119235
  function createBuffers() {
119173
119236
  return {
@@ -119176,7 +119239,15 @@ function createBuffers() {
119176
119239
  byId: new Map,
119177
119240
  pendingToolByRun: new Map,
119178
119241
  toolCallIdToLineId: new Map,
119179
- lastOtid: null
119242
+ lastOtid: null,
119243
+ usage: {
119244
+ promptTokens: 0,
119245
+ completionTokens: 0,
119246
+ totalTokens: 0,
119247
+ cachedTokens: 0,
119248
+ reasoningTokens: 0,
119249
+ stepCount: 0
119250
+ }
119180
119251
  };
119181
119252
  }
119182
119253
  function ensure(b, id, make) {
@@ -119340,6 +119411,21 @@ function onChunk(b, chunk) {
119340
119411
  b.byId.set(id, updatedLine);
119341
119412
  break;
119342
119413
  }
119414
+ case "usage_statistics": {
119415
+ if (chunk.promptTokens !== undefined) {
119416
+ b.usage.promptTokens += chunk.promptTokens;
119417
+ }
119418
+ if (chunk.completionTokens !== undefined) {
119419
+ b.usage.completionTokens += chunk.completionTokens;
119420
+ }
119421
+ if (chunk.totalTokens !== undefined) {
119422
+ b.usage.totalTokens += chunk.totalTokens;
119423
+ }
119424
+ if (chunk.stepCount !== undefined) {
119425
+ b.usage.stepCount += chunk.stepCount;
119426
+ }
119427
+ break;
119428
+ }
119343
119429
  default:
119344
119430
  break;
119345
119431
  }
@@ -119373,6 +119459,7 @@ function safeJsonParseOr(json, defaultValue) {
119373
119459
 
119374
119460
  // src/cli/helpers/stream.ts
119375
119461
  async function drainStream(stream, buffers, refresh) {
119462
+ const startTime = performance.now();
119376
119463
  let approvalRequestId = null;
119377
119464
  let toolCallId = null;
119378
119465
  let toolName = null;
@@ -119411,9 +119498,11 @@ async function drainStream(stream, buffers, refresh) {
119411
119498
  queueMicrotask(refresh);
119412
119499
  if (chunk.messageType === "stop_reason") {
119413
119500
  stopReason = chunk.stopReason;
119414
- break;
119415
119501
  }
119416
119502
  }
119503
+ if (!stopReason) {
119504
+ stopReason = import_letta_client4.Letta.StopReasonType.Error;
119505
+ }
119417
119506
  markCurrentLineAsFinished(buffers);
119418
119507
  queueMicrotask(refresh);
119419
119508
  const approval = toolCallId && toolName && toolArgs && approvalRequestId ? {
@@ -119421,10 +119510,8 @@ async function drainStream(stream, buffers, refresh) {
119421
119510
  toolName,
119422
119511
  toolArgs
119423
119512
  } : null;
119424
- if (!stopReason) {
119425
- stopReason = import_letta_client4.Letta.StopReasonType.Error;
119426
- }
119427
- return { stopReason, approval, lastRunId, lastSeqId };
119513
+ const apiDurationMs = performance.now() - startTime;
119514
+ return { stopReason, approval, lastRunId, lastSeqId, apiDurationMs };
119428
119515
  }
119429
119516
  var import_letta_client4;
119430
119517
  var init_stream = __esm(() => {
@@ -119443,7 +119530,8 @@ async function handleHeadlessCommand(argv) {
119443
119530
  args: argv,
119444
119531
  options: {
119445
119532
  continue: { type: "boolean", short: "c" },
119446
- agent: { type: "string", short: "a" }
119533
+ agent: { type: "string", short: "a" },
119534
+ "output-format": { type: "string" }
119447
119535
  },
119448
119536
  strict: false,
119449
119537
  allowPositionals: true
@@ -119484,7 +119572,13 @@ async function handleHeadlessCommand(argv) {
119484
119572
  agent = await createAgent();
119485
119573
  await updateSettings2({ lastAgent: agent.id });
119486
119574
  }
119575
+ const outputFormat = values["output-format"] || "text";
119576
+ if (!["text", "json", "stream-json"].includes(outputFormat)) {
119577
+ console.error(`Error: Invalid output format "${outputFormat}". Valid formats: text, json, stream-json`);
119578
+ process.exit(1);
119579
+ }
119487
119580
  const buffers = createBuffers();
119581
+ const sessionStats = new SessionStats;
119488
119582
  let currentInput = [
119489
119583
  {
119490
119584
  role: import_letta_client5.Letta.MessageCreateRole.User,
@@ -119494,7 +119588,8 @@ async function handleHeadlessCommand(argv) {
119494
119588
  try {
119495
119589
  while (true) {
119496
119590
  const stream = await sendMessageStream(agent.id, currentInput);
119497
- const { stopReason, approval } = await drainStream(stream, buffers, () => {});
119591
+ const { stopReason, approval, apiDurationMs } = await drainStream(stream, buffers, () => {});
119592
+ sessionStats.endTurn(apiDurationMs);
119498
119593
  if (stopReason === import_letta_client5.Letta.StopReasonType.EndTurn) {
119499
119594
  break;
119500
119595
  }
@@ -119554,13 +119649,36 @@ async function handleHeadlessCommand(argv) {
119554
119649
  console.error(`Error: ${error}`);
119555
119650
  process.exit(1);
119556
119651
  }
119652
+ sessionStats.updateUsageFromBuffers(buffers);
119557
119653
  const lines = toLines(buffers);
119558
119654
  const lastAssistant = [...lines].reverse().find((line) => line.kind === "assistant");
119559
- if (lastAssistant && "text" in lastAssistant) {
119560
- console.log(lastAssistant.text);
119561
- } else {
119562
- console.error("No assistant response found");
119655
+ const resultText = lastAssistant && "text" in lastAssistant ? lastAssistant.text : "No assistant response found";
119656
+ if (outputFormat === "json") {
119657
+ const stats = sessionStats.getSnapshot();
119658
+ const output = {
119659
+ type: "result",
119660
+ subtype: "success",
119661
+ is_error: false,
119662
+ duration_ms: Math.round(stats.totalWallMs),
119663
+ duration_api_ms: Math.round(stats.totalApiMs),
119664
+ num_turns: stats.usage.stepCount,
119665
+ result: resultText,
119666
+ session_id: agent.id,
119667
+ usage: {
119668
+ input_tokens: stats.usage.promptTokens,
119669
+ output_tokens: stats.usage.completionTokens
119670
+ }
119671
+ };
119672
+ console.log(JSON.stringify(output, null, 2));
119673
+ } else if (outputFormat === "stream-json") {
119674
+ console.error("stream-json format not yet implemented");
119563
119675
  process.exit(1);
119676
+ } else {
119677
+ if (!lastAssistant || !("text" in lastAssistant)) {
119678
+ console.error("No assistant response found");
119679
+ process.exit(1);
119680
+ }
119681
+ console.log(lastAssistant.text);
119564
119682
  }
119565
119683
  }
119566
119684
  var import_letta_client5;
@@ -143195,12 +143313,7 @@ function TextInput({ value: originalValue, placeholder = "", focus = true, mask,
143195
143313
  nextCursorWidth = input.length;
143196
143314
  }
143197
143315
  }
143198
- if (cursorOffset < 0) {
143199
- nextCursorOffset = 0;
143200
- }
143201
- if (cursorOffset > originalValue.length) {
143202
- nextCursorOffset = originalValue.length;
143203
- }
143316
+ nextCursorOffset = Math.max(0, Math.min(nextCursorOffset, nextValue.length));
143204
143317
  setState({ cursorOffset: nextCursorOffset, cursorWidth: nextCursorWidth });
143205
143318
  if (typeof onCursorOffsetChange === "function")
143206
143319
  onCursorOffsetChange(nextCursorOffset);
@@ -144093,6 +144206,59 @@ var init_diff2 = __esm(() => {
144093
144206
  init_libesm();
144094
144207
  });
144095
144208
 
144209
+ // src/cli/hooks/useTerminalWidth.ts
144210
+ function useTerminalWidth() {
144211
+ const [columns, setColumns] = import_react23.useState(trackedColumns);
144212
+ import_react23.useEffect(() => {
144213
+ ensureResizeHandler();
144214
+ const listener = (value) => {
144215
+ setColumns(value);
144216
+ };
144217
+ listeners.add(listener);
144218
+ return () => {
144219
+ listeners.delete(listener);
144220
+ removeResizeHandlerIfIdle();
144221
+ };
144222
+ }, []);
144223
+ return columns;
144224
+ }
144225
+ var import_react23, getStdout = () => {
144226
+ if (typeof process === "undefined")
144227
+ return;
144228
+ const stdout = process.stdout;
144229
+ return stdout && typeof stdout.on === "function" ? stdout : undefined;
144230
+ }, getTerminalWidth = () => getStdout()?.columns ?? 80, listeners, resizeHandlerRegistered = false, trackedColumns, resizeHandler = () => {
144231
+ const nextColumns = getTerminalWidth();
144232
+ if (nextColumns === trackedColumns) {
144233
+ return;
144234
+ }
144235
+ trackedColumns = nextColumns;
144236
+ for (const listener of listeners) {
144237
+ listener(nextColumns);
144238
+ }
144239
+ }, ensureResizeHandler = () => {
144240
+ if (resizeHandlerRegistered)
144241
+ return;
144242
+ const stdout = getStdout();
144243
+ if (!stdout)
144244
+ return;
144245
+ stdout.on("resize", resizeHandler);
144246
+ resizeHandlerRegistered = true;
144247
+ }, removeResizeHandlerIfIdle = () => {
144248
+ if (!resizeHandlerRegistered || listeners.size > 0)
144249
+ return;
144250
+ const stdout = getStdout();
144251
+ if (!stdout)
144252
+ return;
144253
+ stdout.off("resize", resizeHandler);
144254
+ resizeHandlerRegistered = false;
144255
+ };
144256
+ var init_useTerminalWidth = __esm(() => {
144257
+ import_react23 = __toESM(require_react2(), 1);
144258
+ listeners = new Set;
144259
+ trackedColumns = getTerminalWidth();
144260
+ });
144261
+
144096
144262
  // src/cli/components/colors.ts
144097
144263
  var brandColors, colors;
144098
144264
  var init_colors = __esm(() => {
@@ -145367,7 +145533,8 @@ function Line({
145367
145533
  }, undefined, true, undefined, this);
145368
145534
  }
145369
145535
  function AdvancedDiffRenderer(props) {
145370
- const result = import_react23.useMemo(() => {
145536
+ const columns = useTerminalWidth();
145537
+ const result = import_react24.useMemo(() => {
145371
145538
  if (props.precomputed)
145372
145539
  return props.precomputed;
145373
145540
  if (props.kind === "write") {
@@ -145498,7 +145665,6 @@ function AdvancedDiffRenderer(props) {
145498
145665
  const maxDisplayNo = rows.reduce((m, r) => Math.max(m, r.displayNo), 1);
145499
145666
  const gutterWidth = String(maxDisplayNo).length;
145500
145667
  const header = props.kind === "write" ? `Wrote changes to ${relative4}` : `Updated ${relative4}`;
145501
- const columns = typeof process !== "undefined" && process.stdout && "columns" in process.stdout ? process.stdout.columns : 80;
145502
145668
  const panelInnerWidth = Math.max(20, columns - 8);
145503
145669
  return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
145504
145670
  flexDirection: "column",
@@ -145527,16 +145693,17 @@ function AdvancedDiffRenderer(props) {
145527
145693
  ]
145528
145694
  }, undefined, true, undefined, this);
145529
145695
  }
145530
- var import_react23, jsx_dev_runtime2;
145696
+ var import_react24, jsx_dev_runtime2;
145531
145697
  var init_AdvancedDiffRenderer = __esm(async () => {
145532
145698
  init_libesm();
145533
145699
  init_diff2();
145700
+ init_useTerminalWidth();
145534
145701
  init_colors();
145535
145702
  await __promiseAll([
145536
145703
  init_build3(),
145537
145704
  init_DiffRenderer()
145538
145705
  ]);
145539
- import_react23 = __toESM(require_react2(), 1);
145706
+ import_react24 = __toESM(require_react2(), 1);
145540
145707
  jsx_dev_runtime2 = __toESM(require_jsx_dev_runtime(), 1);
145541
145708
  });
145542
145709
 
@@ -145548,10 +145715,10 @@ function ApprovalDialog({
145548
145715
  onApproveAlways,
145549
145716
  onDeny
145550
145717
  }) {
145551
- const [selectedOption, setSelectedOption] = import_react24.useState(0);
145552
- const [isEnteringReason, setIsEnteringReason] = import_react24.useState(false);
145553
- const [denyReason, setDenyReason] = import_react24.useState("");
145554
- const options = import_react24.useMemo(() => {
145718
+ const [selectedOption, setSelectedOption] = import_react25.useState(0);
145719
+ const [isEnteringReason, setIsEnteringReason] = import_react25.useState(false);
145720
+ const [denyReason, setDenyReason] = import_react25.useState("");
145721
+ const options = import_react25.useMemo(() => {
145555
145722
  const opts = [{ label: "Yes, just this once", action: onApprove }];
145556
145723
  if (approvalContext?.allowPersistence) {
145557
145724
  opts.push({
@@ -145575,6 +145742,11 @@ function ApprovalDialog({
145575
145742
  }
145576
145743
  return;
145577
145744
  }
145745
+ if (key.escape) {
145746
+ setSelectedOption(options.length - 1);
145747
+ setIsEnteringReason(true);
145748
+ return;
145749
+ }
145578
145750
  if (key.upArrow) {
145579
145751
  setSelectedOption((prev) => prev > 0 ? prev - 1 : options.length - 1);
145580
145752
  } else if (key.downArrow) {
@@ -145605,7 +145777,7 @@ function ApprovalDialog({
145605
145777
  try {
145606
145778
  parsedArgs = JSON.parse(approvalRequest.toolArgs);
145607
145779
  } catch {}
145608
- const precomputedDiff = import_react24.useMemo(() => {
145780
+ const precomputedDiff = import_react25.useMemo(() => {
145609
145781
  if (!parsedArgs)
145610
145782
  return null;
145611
145783
  const toolName = approvalRequest.toolName.toLowerCase();
@@ -145745,7 +145917,7 @@ function getHeaderLabel(toolName) {
145745
145917
  return "Update Todos";
145746
145918
  return toolName;
145747
145919
  }
145748
- var import_react24, jsx_dev_runtime3, OptionsRenderer, DynamicPreview = ({
145920
+ var import_react25, jsx_dev_runtime3, OptionsRenderer, DynamicPreview = ({
145749
145921
  toolName,
145750
145922
  toolArgs,
145751
145923
  parsedArgs,
@@ -145879,9 +146051,9 @@ var init_ApprovalDialogRich = __esm(async () => {
145879
146051
  init_build4(),
145880
146052
  init_AdvancedDiffRenderer()
145881
146053
  ]);
145882
- import_react24 = __toESM(require_react2(), 1);
146054
+ import_react25 = __toESM(require_react2(), 1);
145883
146055
  jsx_dev_runtime3 = __toESM(require_jsx_dev_runtime(), 1);
145884
- OptionsRenderer = import_react24.memo(({
146056
+ OptionsRenderer = import_react25.memo(({
145885
146057
  options,
145886
146058
  selectedOption
145887
146059
  }) => {
@@ -146204,19 +146376,20 @@ var init_MarkdownDisplay = __esm(async () => {
146204
146376
  });
146205
146377
 
146206
146378
  // src/cli/components/AssistantMessageRich.tsx
146207
- var import_react25, jsx_dev_runtime6, normalize = (s) => s.replace(/\r\n/g, `
146379
+ var import_react26, jsx_dev_runtime6, normalize = (s) => s.replace(/\r\n/g, `
146208
146380
  `).replace(/[ \t]+$/gm, "").replace(/\n{3,}/g, `
146209
146381
 
146210
146382
  `).replace(/^\n+|\n+$/g, ""), AssistantMessage;
146211
146383
  var init_AssistantMessageRich = __esm(async () => {
146384
+ init_useTerminalWidth();
146212
146385
  await __promiseAll([
146213
146386
  init_build3(),
146214
146387
  init_MarkdownDisplay()
146215
146388
  ]);
146216
- import_react25 = __toESM(require_react2(), 1);
146389
+ import_react26 = __toESM(require_react2(), 1);
146217
146390
  jsx_dev_runtime6 = __toESM(require_jsx_dev_runtime(), 1);
146218
- AssistantMessage = import_react25.memo(({ line }) => {
146219
- const columns = typeof process !== "undefined" && process.stdout && "columns" in process.stdout ? process.stdout.columns ?? 80 : 80;
146391
+ AssistantMessage = import_react26.memo(({ line }) => {
146392
+ const columns = useTerminalWidth();
146220
146393
  const contentWidth = Math.max(0, columns - 2);
146221
146394
  const normalizedText = normalize(line.text);
146222
146395
  return /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Box_default, {
@@ -146244,9 +146417,9 @@ var init_AssistantMessageRich = __esm(async () => {
146244
146417
  });
146245
146418
 
146246
146419
  // src/cli/components/CommandMessage.tsx
146247
- var import_react26, jsx_dev_runtime7, BlinkDot = ({ color = "yellow" }) => {
146248
- const [on, setOn] = import_react26.useState(true);
146249
- import_react26.useEffect(() => {
146420
+ var import_react27, jsx_dev_runtime7, BlinkDot = ({ color = "yellow" }) => {
146421
+ const [on, setOn] = import_react27.useState(true);
146422
+ import_react27.useEffect(() => {
146250
146423
  const t = setInterval(() => setOn((v) => !v), 400);
146251
146424
  return () => clearInterval(t);
146252
146425
  }, []);
@@ -146256,15 +146429,16 @@ var import_react26, jsx_dev_runtime7, BlinkDot = ({ color = "yellow" }) => {
146256
146429
  }, undefined, false, undefined, this);
146257
146430
  }, CommandMessage;
146258
146431
  var init_CommandMessage = __esm(async () => {
146432
+ init_useTerminalWidth();
146259
146433
  init_colors();
146260
146434
  await __promiseAll([
146261
146435
  init_build3(),
146262
146436
  init_MarkdownDisplay()
146263
146437
  ]);
146264
- import_react26 = __toESM(require_react2(), 1);
146438
+ import_react27 = __toESM(require_react2(), 1);
146265
146439
  jsx_dev_runtime7 = __toESM(require_jsx_dev_runtime(), 1);
146266
- CommandMessage = import_react26.memo(({ line }) => {
146267
- const columns = typeof process !== "undefined" && process.stdout && "columns" in process.stdout ? process.stdout.columns ?? 80 : 80;
146440
+ CommandMessage = import_react27.memo(({ line }) => {
146441
+ const columns = useTerminalWidth();
146268
146442
  const rightWidth = Math.max(0, columns - 2);
146269
146443
  const getDotElement = () => {
146270
146444
  if (!line.phase || line.phase === "finished") {
@@ -146339,12 +146513,12 @@ var init_CommandMessage = __esm(async () => {
146339
146513
  });
146340
146514
 
146341
146515
  // src/cli/components/ErrorMessage.tsx
146342
- var import_react27, jsx_dev_runtime8, ErrorMessage;
146516
+ var import_react28, jsx_dev_runtime8, ErrorMessage;
146343
146517
  var init_ErrorMessage = __esm(async () => {
146344
146518
  await init_build3();
146345
- import_react27 = __toESM(require_react2(), 1);
146519
+ import_react28 = __toESM(require_react2(), 1);
146346
146520
  jsx_dev_runtime8 = __toESM(require_jsx_dev_runtime(), 1);
146347
- ErrorMessage = import_react27.memo(({ line }) => {
146521
+ ErrorMessage = import_react28.memo(({ line }) => {
146348
146522
  return /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
146349
146523
  children: line.text
146350
146524
  }, undefined, false, undefined, this);
@@ -147993,9 +148167,9 @@ var require_cli_spinners = __commonJS((exports, module) => {
147993
148167
 
147994
148168
  // node_modules/ink-spinner/build/index.js
147995
148169
  function Spinner({ type = "dots" }) {
147996
- const [frame, setFrame] = import_react28.useState(0);
148170
+ const [frame, setFrame] = import_react29.useState(0);
147997
148171
  const spinner = import_cli_spinners.default[type];
147998
- import_react28.useEffect(() => {
148172
+ import_react29.useEffect(() => {
147999
148173
  const timer = setInterval(() => {
148000
148174
  setFrame((previousFrame) => {
148001
148175
  const isLastFrame = previousFrame === spinner.frames.length - 1;
@@ -148006,12 +148180,12 @@ function Spinner({ type = "dots" }) {
148006
148180
  clearInterval(timer);
148007
148181
  };
148008
148182
  }, [spinner]);
148009
- return import_react28.default.createElement(Text, null, spinner.frames[frame]);
148183
+ return import_react29.default.createElement(Text, null, spinner.frames[frame]);
148010
148184
  }
148011
- var import_react28, import_cli_spinners, build_default2;
148185
+ var import_react29, import_cli_spinners, build_default2;
148012
148186
  var init_build5 = __esm(async () => {
148013
148187
  await init_build3();
148014
- import_react28 = __toESM(require_react2(), 1);
148188
+ import_react29 = __toESM(require_react2(), 1);
148015
148189
  import_cli_spinners = __toESM(require_cli_spinners(), 1);
148016
148190
  build_default2 = Spinner;
148017
148191
  });
@@ -148067,6 +148241,12 @@ var init_registry = __esm(() => {
148067
148241
  handler: () => {
148068
148242
  return "Toggling token streaming...";
148069
148243
  }
148244
+ },
148245
+ "/exit": {
148246
+ desc: "Exit and show session stats",
148247
+ handler: () => {
148248
+ return "Exiting...";
148249
+ }
148070
148250
  }
148071
148251
  };
148072
148252
  });
@@ -148371,14 +148551,14 @@ function PasteAwareTextInput({
148371
148551
  placeholder,
148372
148552
  focus = true
148373
148553
  }) {
148374
- const [displayValue, setDisplayValue] = import_react29.useState(value);
148375
- const [actualValue, setActualValue] = import_react29.useState(value);
148376
- const lastPasteDetectedAtRef = import_react29.useRef(0);
148377
- const suppressNextChangeRef = import_react29.useRef(false);
148378
- const caretOffsetRef = import_react29.useRef((value || "").length);
148379
- const [nudgeCursorOffset, setNudgeCursorOffset] = import_react29.useState(undefined);
148554
+ const [displayValue, setDisplayValue] = import_react30.useState(value);
148555
+ const [actualValue, setActualValue] = import_react30.useState(value);
148556
+ const lastPasteDetectedAtRef = import_react30.useRef(0);
148557
+ const suppressNextChangeRef = import_react30.useRef(false);
148558
+ const caretOffsetRef = import_react30.useRef((value || "").length);
148559
+ const [nudgeCursorOffset, setNudgeCursorOffset] = import_react30.useState(undefined);
148380
148560
  const TextInputAny = build_default;
148381
- import_react29.useEffect(() => {
148561
+ import_react30.useEffect(() => {
148382
148562
  setDisplayValue(value);
148383
148563
  const resolved = resolvePlaceholders(value);
148384
148564
  setActualValue(resolved);
@@ -148492,7 +148672,7 @@ function PasteAwareTextInput({
148492
148672
  onSubmit(displayValue);
148493
148673
  }
148494
148674
  };
148495
- import_react29.useEffect(() => {
148675
+ import_react30.useEffect(() => {
148496
148676
  if (typeof nudgeCursorOffset === "number") {
148497
148677
  const t = setTimeout(() => setNudgeCursorOffset(undefined), 0);
148498
148678
  return () => clearTimeout(t);
@@ -148510,7 +148690,7 @@ function PasteAwareTextInput({
148510
148690
  focus
148511
148691
  }, undefined, false, undefined, this);
148512
148692
  }
148513
- var import_react29, jsx_dev_runtime10;
148693
+ var import_react30, jsx_dev_runtime10;
148514
148694
  var init_PasteAwareTextInput = __esm(async () => {
148515
148695
  init_clipboard();
148516
148696
  init_pasteRegistry();
@@ -148518,7 +148698,7 @@ var init_PasteAwareTextInput = __esm(async () => {
148518
148698
  init_build3(),
148519
148699
  init_build4()
148520
148700
  ]);
148521
- import_react29 = __toESM(require_react2(), 1);
148701
+ import_react30 = __toESM(require_react2(), 1);
148522
148702
  jsx_dev_runtime10 = __toESM(require_jsx_dev_runtime(), 1);
148523
148703
  });
148524
148704
 
@@ -148556,22 +148736,23 @@ function Input({
148556
148736
  thinkingMessage,
148557
148737
  onSubmit,
148558
148738
  permissionMode: externalMode,
148559
- onPermissionModeChange
148739
+ onPermissionModeChange,
148740
+ onExit
148560
148741
  }) {
148561
- const [value, setValue] = import_react30.useState("");
148562
- const [escapePressed, setEscapePressed] = import_react30.useState(false);
148563
- const escapeTimerRef = import_react30.useRef(null);
148564
- const [ctrlCPressed, setCtrlCPressed] = import_react30.useState(false);
148565
- const ctrlCTimerRef = import_react30.useRef(null);
148566
- const previousValueRef = import_react30.useRef(value);
148567
- const [currentMode, setCurrentMode] = import_react30.useState(externalMode || permissionMode2.getMode());
148568
- import_react30.useEffect(() => {
148742
+ const [value, setValue] = import_react31.useState("");
148743
+ const [escapePressed, setEscapePressed] = import_react31.useState(false);
148744
+ const escapeTimerRef = import_react31.useRef(null);
148745
+ const [ctrlCPressed, setCtrlCPressed] = import_react31.useState(false);
148746
+ const ctrlCTimerRef = import_react31.useRef(null);
148747
+ const previousValueRef = import_react31.useRef(value);
148748
+ const [currentMode, setCurrentMode] = import_react31.useState(externalMode || permissionMode2.getMode());
148749
+ import_react31.useEffect(() => {
148569
148750
  if (externalMode !== undefined) {
148570
148751
  setCurrentMode(externalMode);
148571
148752
  }
148572
148753
  }, [externalMode]);
148573
- const [shimmerOffset, setShimmerOffset] = import_react30.useState(-3);
148574
- const columns = typeof process !== "undefined" && process.stdout && "columns" in process.stdout ? process.stdout.columns ?? 80 : 80;
148754
+ const [shimmerOffset, setShimmerOffset] = import_react31.useState(-3);
148755
+ const columns = useTerminalWidth();
148575
148756
  const contentWidth = Math.max(0, columns - 2);
148576
148757
  use_input_default((_input, key) => {
148577
148758
  if (key.escape && value) {
@@ -148593,6 +148774,8 @@ function Input({
148593
148774
  use_input_default((input, key) => {
148594
148775
  if (input === "c" && key.ctrl) {
148595
148776
  if (ctrlCPressed) {
148777
+ if (onExit)
148778
+ onExit();
148596
148779
  process.exit(0);
148597
148780
  } else {
148598
148781
  setCtrlCPressed(true);
@@ -148622,7 +148805,7 @@ function Input({
148622
148805
  }
148623
148806
  }
148624
148807
  });
148625
- import_react30.useEffect(() => {
148808
+ import_react31.useEffect(() => {
148626
148809
  if (value !== previousValueRef.current && value !== "") {
148627
148810
  setEscapePressed(false);
148628
148811
  if (escapeTimerRef.current)
@@ -148633,7 +148816,7 @@ function Input({
148633
148816
  }
148634
148817
  previousValueRef.current = value;
148635
148818
  }, [value]);
148636
- import_react30.useEffect(() => {
148819
+ import_react31.useEffect(() => {
148637
148820
  return () => {
148638
148821
  if (escapeTimerRef.current)
148639
148822
  clearTimeout(escapeTimerRef.current);
@@ -148641,7 +148824,7 @@ function Input({
148641
148824
  clearTimeout(ctrlCTimerRef.current);
148642
148825
  };
148643
148826
  }, []);
148644
- import_react30.useEffect(() => {
148827
+ import_react31.useEffect(() => {
148645
148828
  if (!streaming)
148646
148829
  return;
148647
148830
  const id = setInterval(() => {
@@ -148710,7 +148893,7 @@ function Input({
148710
148893
  children: [
148711
148894
  " (",
148712
148895
  tokenCount,
148713
- "↑)"
148896
+ " ↑)"
148714
148897
  ]
148715
148898
  }, undefined, true, undefined, this)
148716
148899
  ]
@@ -148800,9 +148983,10 @@ function Input({
148800
148983
  ]
148801
148984
  }, undefined, true, undefined, this);
148802
148985
  }
148803
- var import_react30, jsx_dev_runtime12, Spinner2, COUNTER_VISIBLE_THRESHOLD = 1000;
148986
+ var import_react31, jsx_dev_runtime12, Spinner2, COUNTER_VISIBLE_THRESHOLD = 1000;
148804
148987
  var init_InputRich = __esm(async () => {
148805
148988
  init_mode2();
148989
+ init_useTerminalWidth();
148806
148990
  init_colors();
148807
148991
  await __promiseAll([
148808
148992
  init_build3(),
@@ -148811,7 +148995,7 @@ var init_InputRich = __esm(async () => {
148811
148995
  init_PasteAwareTextInput(),
148812
148996
  init_ShimmerText()
148813
148997
  ]);
148814
- import_react30 = __toESM(require_react2(), 1);
148998
+ import_react31 = __toESM(require_react2(), 1);
148815
148999
  jsx_dev_runtime12 = __toESM(require_jsx_dev_runtime(), 1);
148816
149000
  Spinner2 = build_default2;
148817
149001
  });
@@ -148926,7 +149110,7 @@ function ModelSelector({
148926
149110
  onSelect,
148927
149111
  onCancel
148928
149112
  }) {
148929
- const [selectedIndex, setSelectedIndex] = import_react31.useState(0);
149113
+ const [selectedIndex, setSelectedIndex] = import_react32.useState(0);
148930
149114
  use_input_default((_input, key) => {
148931
149115
  if (key.upArrow) {
148932
149116
  setSelectedIndex((prev) => Math.max(0, prev - 1));
@@ -148995,17 +149179,17 @@ function ModelSelector({
148995
149179
  ]
148996
149180
  }, undefined, true, undefined, this);
148997
149181
  }
148998
- var import_react31, import_models, jsx_dev_runtime13;
149182
+ var import_react32, import_models, jsx_dev_runtime13;
148999
149183
  var init_ModelSelector = __esm(async () => {
149000
149184
  init_colors();
149001
149185
  await init_build3();
149002
- import_react31 = __toESM(require_react2(), 1);
149186
+ import_react32 = __toESM(require_react2(), 1);
149003
149187
  import_models = __toESM(require_models3(), 1);
149004
149188
  jsx_dev_runtime13 = __toESM(require_jsx_dev_runtime(), 1);
149005
149189
  });
149006
149190
 
149007
149191
  // src/cli/components/PlanModeDialog.tsx
149008
- var import_react32, jsx_dev_runtime14, OptionsRenderer2, PlanModeDialog;
149192
+ var import_react33, jsx_dev_runtime14, OptionsRenderer2, PlanModeDialog;
149009
149193
  var init_PlanModeDialog = __esm(async () => {
149010
149194
  init_colors();
149011
149195
  await __promiseAll([
@@ -149013,9 +149197,9 @@ var init_PlanModeDialog = __esm(async () => {
149013
149197
  init_build4(),
149014
149198
  init_MarkdownDisplay()
149015
149199
  ]);
149016
- import_react32 = __toESM(require_react2(), 1);
149200
+ import_react33 = __toESM(require_react2(), 1);
149017
149201
  jsx_dev_runtime14 = __toESM(require_jsx_dev_runtime(), 1);
149018
- OptionsRenderer2 = import_react32.memo(({
149202
+ OptionsRenderer2 = import_react33.memo(({
149019
149203
  options,
149020
149204
  selectedOption
149021
149205
  }) => {
@@ -149041,10 +149225,10 @@ var init_PlanModeDialog = __esm(async () => {
149041
149225
  }, undefined, false, undefined, this);
149042
149226
  });
149043
149227
  OptionsRenderer2.displayName = "OptionsRenderer";
149044
- PlanModeDialog = import_react32.memo(({ plan, onApprove, onApproveAndAcceptEdits, onKeepPlanning }) => {
149045
- const [selectedOption, setSelectedOption] = import_react32.useState(0);
149046
- const [isEnteringReason, setIsEnteringReason] = import_react32.useState(false);
149047
- const [denyReason, setDenyReason] = import_react32.useState("");
149228
+ PlanModeDialog = import_react33.memo(({ plan, onApprove, onApproveAndAcceptEdits, onKeepPlanning }) => {
149229
+ const [selectedOption, setSelectedOption] = import_react33.useState(0);
149230
+ const [isEnteringReason, setIsEnteringReason] = import_react33.useState(false);
149231
+ const [denyReason, setDenyReason] = import_react33.useState("");
149048
149232
  const options = [
149049
149233
  { label: "Yes, and auto-accept edits", action: onApproveAndAcceptEdits },
149050
149234
  { label: "Yes, and manually approve edits", action: onApprove },
@@ -149164,19 +149348,20 @@ var init_PlanModeDialog = __esm(async () => {
149164
149348
  });
149165
149349
 
149166
149350
  // src/cli/components/ReasoningMessageRich.tsx
149167
- var import_react33, jsx_dev_runtime15, normalize2 = (s) => s.replace(/\r\n/g, `
149351
+ var import_react34, jsx_dev_runtime15, normalize2 = (s) => s.replace(/\r\n/g, `
149168
149352
  `).replace(/[ \t]+$/gm, "").replace(/\n{3,}/g, `
149169
149353
 
149170
149354
  `).replace(/^\n+|\n+$/g, ""), ReasoningMessage;
149171
149355
  var init_ReasoningMessageRich = __esm(async () => {
149356
+ init_useTerminalWidth();
149172
149357
  await __promiseAll([
149173
149358
  init_build3(),
149174
149359
  init_MarkdownDisplay()
149175
149360
  ]);
149176
- import_react33 = __toESM(require_react2(), 1);
149361
+ import_react34 = __toESM(require_react2(), 1);
149177
149362
  jsx_dev_runtime15 = __toESM(require_jsx_dev_runtime(), 1);
149178
- ReasoningMessage = import_react33.memo(({ line }) => {
149179
- const columns = typeof process !== "undefined" && process.stdout && "columns" in process.stdout ? process.stdout.columns ?? 80 : 80;
149363
+ ReasoningMessage = import_react34.memo(({ line }) => {
149364
+ const columns = useTerminalWidth();
149180
149365
  const contentWidth = Math.max(0, columns - 2);
149181
149366
  const normalizedText = normalize2(line.text);
149182
149367
  return /* @__PURE__ */ jsx_dev_runtime15.jsxDEV(Box_default, {
@@ -149232,6 +149417,60 @@ var init_ReasoningMessageRich = __esm(async () => {
149232
149417
  ReasoningMessage.displayName = "ReasoningMessage";
149233
149418
  });
149234
149419
 
149420
+ // src/cli/components/SessionStats.tsx
149421
+ function formatDuration(ms) {
149422
+ if (ms < 1000) {
149423
+ return `${Math.round(ms)}ms`;
149424
+ }
149425
+ return `${(ms / 1000).toFixed(1)}s`;
149426
+ }
149427
+ function formatNumber(n) {
149428
+ return n.toLocaleString();
149429
+ }
149430
+ function SessionStats2({ stats }) {
149431
+ const wallDuration = formatDuration(stats.totalWallMs);
149432
+ const apiDuration = formatDuration(stats.totalApiMs);
149433
+ return /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
149434
+ flexDirection: "column",
149435
+ paddingTop: 1,
149436
+ children: [
149437
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
149438
+ dimColor: true,
149439
+ children: [
149440
+ "Total duration (API): ",
149441
+ apiDuration
149442
+ ]
149443
+ }, undefined, true, undefined, this),
149444
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
149445
+ dimColor: true,
149446
+ children: [
149447
+ "Total duration (wall): ",
149448
+ wallDuration
149449
+ ]
149450
+ }, undefined, true, undefined, this),
149451
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
149452
+ dimColor: true,
149453
+ children: [
149454
+ "Usage: ",
149455
+ stats.usage.stepCount,
149456
+ " steps,",
149457
+ " ",
149458
+ formatNumber(stats.usage.promptTokens),
149459
+ " input,",
149460
+ " ",
149461
+ formatNumber(stats.usage.completionTokens),
149462
+ " output"
149463
+ ]
149464
+ }, undefined, true, undefined, this)
149465
+ ]
149466
+ }, undefined, true, undefined, this);
149467
+ }
149468
+ var jsx_dev_runtime16;
149469
+ var init_SessionStats = __esm(async () => {
149470
+ await init_build3();
149471
+ jsx_dev_runtime16 = __toESM(require_jsx_dev_runtime(), 1);
149472
+ });
149473
+
149235
149474
  // src/cli/helpers/formatArgsDisplay.ts
149236
149475
  function formatArgsDisplay(argsJson) {
149237
149476
  let parsed = {};
@@ -149291,14 +149530,14 @@ function formatArgsDisplay(argsJson) {
149291
149530
  var isRecord3 = (v) => typeof v === "object" && v !== null;
149292
149531
 
149293
149532
  // src/cli/components/TodoRenderer.tsx
149294
- var jsx_dev_runtime16, TodoRenderer = ({ todos }) => {
149295
- return /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
149533
+ var jsx_dev_runtime17, TodoRenderer = ({ todos }) => {
149534
+ return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
149296
149535
  flexDirection: "column",
149297
149536
  children: todos.map((todo, index) => {
149298
149537
  const checkbox = todo.status === "completed" ? "☒" : "☐";
149299
149538
  let textElement;
149300
149539
  if (todo.status === "completed") {
149301
- textElement = /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
149540
+ textElement = /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
149302
149541
  color: colors.todo.completed,
149303
149542
  strikethrough: true,
149304
149543
  children: [
@@ -149308,7 +149547,7 @@ var jsx_dev_runtime16, TodoRenderer = ({ todos }) => {
149308
149547
  ]
149309
149548
  }, undefined, true, undefined, this);
149310
149549
  } else if (todo.status === "in_progress") {
149311
- textElement = /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
149550
+ textElement = /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
149312
149551
  color: colors.todo.inProgress,
149313
149552
  bold: true,
149314
149553
  children: [
@@ -149318,7 +149557,7 @@ var jsx_dev_runtime16, TodoRenderer = ({ todos }) => {
149318
149557
  ]
149319
149558
  }, undefined, true, undefined, this);
149320
149559
  } else {
149321
- textElement = /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
149560
+ textElement = /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
149322
149561
  children: [
149323
149562
  checkbox,
149324
149563
  " ",
@@ -149327,9 +149566,9 @@ var jsx_dev_runtime16, TodoRenderer = ({ todos }) => {
149327
149566
  }, undefined, true, undefined, this);
149328
149567
  }
149329
149568
  const prefix = index === 0 ? " ⎿ " : " ";
149330
- return /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
149569
+ return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
149331
149570
  children: [
149332
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
149571
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
149333
149572
  children: prefix
149334
149573
  }, undefined, false, undefined, this),
149335
149574
  textElement
@@ -149341,35 +149580,36 @@ var jsx_dev_runtime16, TodoRenderer = ({ todos }) => {
149341
149580
  var init_TodoRenderer = __esm(async () => {
149342
149581
  init_colors();
149343
149582
  await init_build3();
149344
- jsx_dev_runtime16 = __toESM(require_jsx_dev_runtime(), 1);
149583
+ jsx_dev_runtime17 = __toESM(require_jsx_dev_runtime(), 1);
149345
149584
  });
149346
149585
 
149347
149586
  // src/cli/components/ToolCallMessageRich.tsx
149348
- var import_react34, jsx_dev_runtime17, BlinkDot2 = ({
149587
+ var import_react35, jsx_dev_runtime18, BlinkDot2 = ({
149349
149588
  color = colors.tool.pending
149350
149589
  }) => {
149351
- const [on, setOn] = import_react34.useState(true);
149352
- import_react34.useEffect(() => {
149590
+ const [on, setOn] = import_react35.useState(true);
149591
+ import_react35.useEffect(() => {
149353
149592
  const t = setInterval(() => setOn((v) => !v), 400);
149354
149593
  return () => clearInterval(t);
149355
149594
  }, []);
149356
- return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
149595
+ return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
149357
149596
  color,
149358
149597
  children: on ? "●" : " "
149359
149598
  }, undefined, false, undefined, this);
149360
149599
  }, ToolCallMessage;
149361
149600
  var init_ToolCallMessageRich = __esm(async () => {
149362
149601
  init_manager();
149602
+ init_useTerminalWidth();
149363
149603
  init_colors();
149364
149604
  await __promiseAll([
149365
149605
  init_build3(),
149366
149606
  init_MarkdownDisplay(),
149367
149607
  init_TodoRenderer()
149368
149608
  ]);
149369
- import_react34 = __toESM(require_react2(), 1);
149370
- jsx_dev_runtime17 = __toESM(require_jsx_dev_runtime(), 1);
149371
- ToolCallMessage = import_react34.memo(({ line }) => {
149372
- const columns = typeof process !== "undefined" && process.stdout && "columns" in process.stdout ? process.stdout.columns ?? 80 : 80;
149609
+ import_react35 = __toESM(require_react2(), 1);
149610
+ jsx_dev_runtime18 = __toESM(require_jsx_dev_runtime(), 1);
149611
+ ToolCallMessage = import_react35.memo(({ line }) => {
149612
+ const columns = useTerminalWidth();
149373
149613
  const rawName = line.name ?? "?";
149374
149614
  const argsText = line.argsText ?? "...";
149375
149615
  let displayName = rawName;
@@ -149398,31 +149638,31 @@ var init_ToolCallMessageRich = __esm(async () => {
149398
149638
  const getDotElement = () => {
149399
149639
  switch (line.phase) {
149400
149640
  case "streaming":
149401
- return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
149641
+ return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
149402
149642
  color: colors.tool.streaming,
149403
149643
  children: "●"
149404
149644
  }, undefined, false, undefined, this);
149405
149645
  case "ready":
149406
- return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(BlinkDot2, {
149646
+ return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(BlinkDot2, {
149407
149647
  color: colors.tool.pending
149408
149648
  }, undefined, false, undefined, this);
149409
149649
  case "running":
149410
- return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(BlinkDot2, {
149650
+ return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(BlinkDot2, {
149411
149651
  color: colors.tool.running
149412
149652
  }, undefined, false, undefined, this);
149413
149653
  case "finished":
149414
149654
  if (line.resultOk === false) {
149415
- return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
149655
+ return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
149416
149656
  color: colors.tool.error,
149417
149657
  children: "●"
149418
149658
  }, undefined, false, undefined, this);
149419
149659
  }
149420
- return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
149660
+ return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
149421
149661
  color: colors.tool.completed,
149422
149662
  children: "●"
149423
149663
  }, undefined, false, undefined, this);
149424
149664
  default:
149425
- return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
149665
+ return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
149426
149666
  children: "●"
149427
149667
  }, undefined, false, undefined, this);
149428
149668
  }
@@ -149434,20 +149674,20 @@ var init_ToolCallMessageRich = __esm(async () => {
149434
149674
  const prefixWidth = 5;
149435
149675
  const contentWidth = Math.max(0, columns - prefixWidth);
149436
149676
  if (line.resultText === "Running...") {
149437
- return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
149677
+ return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
149438
149678
  flexDirection: "row",
149439
149679
  children: [
149440
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
149680
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
149441
149681
  width: prefixWidth,
149442
149682
  flexShrink: 0,
149443
- children: /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
149683
+ children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
149444
149684
  children: prefix
149445
149685
  }, undefined, false, undefined, this)
149446
149686
  }, undefined, false, undefined, this),
149447
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
149687
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
149448
149688
  flexGrow: 1,
149449
149689
  width: contentWidth,
149450
- children: /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
149690
+ children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
149451
149691
  dimColor: true,
149452
149692
  children: "Running..."
149453
149693
  }, undefined, false, undefined, this)
@@ -149456,20 +149696,20 @@ var init_ToolCallMessageRich = __esm(async () => {
149456
149696
  }, undefined, true, undefined, this);
149457
149697
  }
149458
149698
  if (line.resultText === "Interrupted by user") {
149459
- return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
149699
+ return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
149460
149700
  flexDirection: "row",
149461
149701
  children: [
149462
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
149702
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
149463
149703
  width: prefixWidth,
149464
149704
  flexShrink: 0,
149465
- children: /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
149705
+ children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
149466
149706
  children: prefix
149467
149707
  }, undefined, false, undefined, this)
149468
149708
  }, undefined, false, undefined, this),
149469
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
149709
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
149470
149710
  flexGrow: 1,
149471
149711
  width: contentWidth,
149472
- children: /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
149712
+ children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
149473
149713
  color: colors.status.interrupt,
149474
149714
  children: "Interrupted by user"
149475
149715
  }, undefined, false, undefined, this)
@@ -149492,7 +149732,7 @@ var init_ToolCallMessageRich = __esm(async () => {
149492
149732
  const priority = rec.priority === "high" ? "high" : rec.priority === "medium" ? "medium" : rec.priority === "low" ? "low" : undefined;
149493
149733
  return { content, status, id, priority };
149494
149734
  });
149495
- return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(TodoRenderer, {
149735
+ return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(TodoRenderer, {
149496
149736
  todos: safeTodos
149497
149737
  }, undefined, false, undefined, this);
149498
149738
  }
@@ -149506,59 +149746,59 @@ var init_ToolCallMessageRich = __esm(async () => {
149506
149746
  displayText = parsed.error;
149507
149747
  }
149508
149748
  } catch {}
149509
- return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
149749
+ return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
149510
149750
  flexDirection: "row",
149511
149751
  children: [
149512
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
149752
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
149513
149753
  width: prefixWidth,
149514
149754
  flexShrink: 0,
149515
- children: /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
149755
+ children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
149516
149756
  children: prefix
149517
149757
  }, undefined, false, undefined, this)
149518
149758
  }, undefined, false, undefined, this),
149519
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
149759
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
149520
149760
  flexGrow: 1,
149521
149761
  width: contentWidth,
149522
- children: isError ? /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
149762
+ children: isError ? /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
149523
149763
  color: colors.status.error,
149524
149764
  children: displayText
149525
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(MarkdownDisplay, {
149765
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(MarkdownDisplay, {
149526
149766
  text: displayText
149527
149767
  }, undefined, false, undefined, this)
149528
149768
  }, undefined, false, undefined, this)
149529
149769
  ]
149530
149770
  }, undefined, true, undefined, this);
149531
149771
  };
149532
- return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
149772
+ return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
149533
149773
  flexDirection: "column",
149534
149774
  children: [
149535
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
149775
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
149536
149776
  flexDirection: "row",
149537
149777
  children: [
149538
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
149778
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
149539
149779
  width: 2,
149540
149780
  flexShrink: 0,
149541
149781
  children: [
149542
149782
  getDotElement(),
149543
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {}, undefined, false, undefined, this)
149783
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {}, undefined, false, undefined, this)
149544
149784
  ]
149545
149785
  }, undefined, true, undefined, this),
149546
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
149786
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
149547
149787
  flexGrow: 1,
149548
149788
  width: rightWidth,
149549
- children: fallback ? /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
149789
+ children: fallback ? /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
149550
149790
  wrap: "wrap",
149551
149791
  children: `${displayName}${args}`
149552
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
149792
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
149553
149793
  flexDirection: "row",
149554
149794
  children: [
149555
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
149795
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
149556
149796
  children: displayName
149557
149797
  }, undefined, false, undefined, this),
149558
- args ? /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
149798
+ args ? /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
149559
149799
  flexGrow: 1,
149560
149800
  width: Math.max(0, rightWidth - displayName.length),
149561
- children: /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
149801
+ children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
149562
149802
  wrap: "wrap",
149563
149803
  children: args
149564
149804
  }, undefined, false, undefined, this)
@@ -149576,34 +149816,35 @@ var init_ToolCallMessageRich = __esm(async () => {
149576
149816
  });
149577
149817
 
149578
149818
  // src/cli/components/UserMessageRich.tsx
149579
- var import_react35, jsx_dev_runtime18, UserMessage;
149819
+ var import_react36, jsx_dev_runtime19, UserMessage;
149580
149820
  var init_UserMessageRich = __esm(async () => {
149821
+ init_useTerminalWidth();
149581
149822
  await __promiseAll([
149582
149823
  init_build3(),
149583
149824
  init_MarkdownDisplay()
149584
149825
  ]);
149585
- import_react35 = __toESM(require_react2(), 1);
149586
- jsx_dev_runtime18 = __toESM(require_jsx_dev_runtime(), 1);
149587
- UserMessage = import_react35.memo(({ line }) => {
149588
- const columns = typeof process !== "undefined" && process.stdout && "columns" in process.stdout ? process.stdout.columns ?? 80 : 80;
149826
+ import_react36 = __toESM(require_react2(), 1);
149827
+ jsx_dev_runtime19 = __toESM(require_jsx_dev_runtime(), 1);
149828
+ UserMessage = import_react36.memo(({ line }) => {
149829
+ const columns = useTerminalWidth();
149589
149830
  const contentWidth = Math.max(0, columns - 2);
149590
- return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
149831
+ return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
149591
149832
  flexDirection: "row",
149592
149833
  children: [
149593
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
149834
+ /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
149594
149835
  width: 2,
149595
149836
  flexShrink: 0,
149596
- children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
149837
+ children: /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
149597
149838
  children: [
149598
149839
  ">",
149599
149840
  " "
149600
149841
  ]
149601
149842
  }, undefined, true, undefined, this)
149602
149843
  }, undefined, false, undefined, this),
149603
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
149844
+ /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
149604
149845
  flexGrow: 1,
149605
149846
  width: contentWidth,
149606
- children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(MarkdownDisplay, {
149847
+ children: /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(MarkdownDisplay, {
149607
149848
  text: line.text
149608
149849
  }, undefined, false, undefined, this)
149609
149850
  }, undefined, false, undefined, this)
@@ -149641,26 +149882,26 @@ function WelcomeScreen({
149641
149882
  checking: "Checking for pending approvals...",
149642
149883
  ready: getReadyMessage()
149643
149884
  };
149644
- return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
149885
+ return /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Box_default, {
149645
149886
  flexDirection: "column",
149646
149887
  children: [
149647
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
149888
+ /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Text, {
149648
149889
  bold: true,
149649
149890
  color: colors.welcome.accent,
149650
149891
  children: "Letta Code"
149651
149892
  }, undefined, false, undefined, this),
149652
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
149893
+ /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Text, {
149653
149894
  dimColor: true,
149654
149895
  children: stateMessages[loadingState]
149655
149896
  }, undefined, false, undefined, this)
149656
149897
  ]
149657
149898
  }, undefined, true, undefined, this);
149658
149899
  }
149659
- var jsx_dev_runtime19;
149900
+ var jsx_dev_runtime20;
149660
149901
  var init_WelcomeScreen = __esm(async () => {
149661
149902
  init_colors();
149662
149903
  await init_build3();
149663
- jsx_dev_runtime19 = __toESM(require_jsx_dev_runtime(), 1);
149904
+ jsx_dev_runtime20 = __toESM(require_jsx_dev_runtime(), 1);
149664
149905
  });
149665
149906
 
149666
149907
  // src/cli/helpers/backfill.ts
@@ -149874,20 +150115,35 @@ function App2({
149874
150115
  messageHistory = [],
149875
150116
  tokenStreaming = true
149876
150117
  }) {
149877
- const [streaming, setStreaming] = import_react36.useState(false);
149878
- const [commandRunning, setCommandRunning] = import_react36.useState(false);
149879
- const [pendingApproval, setPendingApproval] = import_react36.useState(null);
149880
- const [approvalContext, setApprovalContext] = import_react36.useState(null);
149881
- const [planApprovalPending, setPlanApprovalPending] = import_react36.useState(null);
149882
- const [modelSelectorOpen, setModelSelectorOpen] = import_react36.useState(false);
149883
- const [llmConfig, setLlmConfig] = import_react36.useState(null);
149884
- const [tokenStreamingEnabled, setTokenStreamingEnabled] = import_react36.useState(tokenStreaming);
149885
- const [tokenCount, setTokenCount] = import_react36.useState(0);
149886
- const [thinkingMessage, setThinkingMessage] = import_react36.useState(getRandomThinkingMessage());
149887
- const [staticItems, setStaticItems] = import_react36.useState([]);
149888
- const emittedIdsRef = import_react36.useRef(new Set);
149889
- const welcomeCommittedRef = import_react36.useRef(false);
149890
- const commitEligibleLines = import_react36.useCallback((b) => {
150118
+ const [streaming, setStreaming] = import_react37.useState(false);
150119
+ const [commandRunning, setCommandRunning] = import_react37.useState(false);
150120
+ const [pendingApproval, setPendingApproval] = import_react37.useState(null);
150121
+ const [approvalContext, setApprovalContext] = import_react37.useState(null);
150122
+ const [planApprovalPending, setPlanApprovalPending] = import_react37.useState(null);
150123
+ const [modelSelectorOpen, setModelSelectorOpen] = import_react37.useState(false);
150124
+ const [llmConfig, setLlmConfig] = import_react37.useState(null);
150125
+ const [tokenStreamingEnabled, setTokenStreamingEnabled] = import_react37.useState(tokenStreaming);
150126
+ const [tokenCount, setTokenCount] = import_react37.useState(0);
150127
+ const [thinkingMessage, setThinkingMessage] = import_react37.useState(getRandomThinkingMessage());
150128
+ const sessionStatsRef = import_react37.useRef(new SessionStats);
150129
+ const [showExitStats, setShowExitStats] = import_react37.useState(false);
150130
+ const [staticItems, setStaticItems] = import_react37.useState([]);
150131
+ const emittedIdsRef = import_react37.useRef(new Set);
150132
+ const welcomeCommittedRef = import_react37.useRef(false);
150133
+ const columns = useTerminalWidth();
150134
+ const prevColumnsRef = import_react37.useRef(columns);
150135
+ const [staticRenderEpoch, setStaticRenderEpoch] = import_react37.useState(0);
150136
+ import_react37.useEffect(() => {
150137
+ const prev = prevColumnsRef.current;
150138
+ if (columns === prev)
150139
+ return;
150140
+ if (columns < prev && typeof process !== "undefined" && process.stdout && "write" in process.stdout && process.stdout.isTTY) {
150141
+ process.stdout.write(CLEAR_SCREEN_AND_HOME);
150142
+ }
150143
+ setStaticRenderEpoch((epoch) => epoch + 1);
150144
+ prevColumnsRef.current = columns;
150145
+ }, [columns]);
150146
+ const commitEligibleLines = import_react37.useCallback((b) => {
149891
150147
  const newlyCommitted = [];
149892
150148
  for (const id of b.order) {
149893
150149
  if (emittedIdsRef.current.has(id))
@@ -149916,17 +150172,17 @@ function App2({
149916
150172
  setStaticItems((prev) => [...prev, ...newlyCommitted]);
149917
150173
  }
149918
150174
  }, []);
149919
- const [lines, setLines] = import_react36.useState([]);
149920
- const buffersRef = import_react36.useRef(createBuffers());
149921
- const hasBackfilledRef = import_react36.useRef(false);
149922
- const refreshDerived = import_react36.useCallback(() => {
150175
+ const [lines, setLines] = import_react37.useState([]);
150176
+ const buffersRef = import_react37.useRef(createBuffers());
150177
+ const hasBackfilledRef = import_react37.useRef(false);
150178
+ const refreshDerived = import_react37.useCallback(() => {
149923
150179
  const b = buffersRef.current;
149924
150180
  setTokenCount(b.tokenCount);
149925
150181
  const newLines = toLines(b);
149926
150182
  setLines(newLines);
149927
150183
  commitEligibleLines(b);
149928
150184
  }, [commitEligibleLines]);
149929
- const refreshDerivedThrottled = import_react36.useCallback(() => {
150185
+ const refreshDerivedThrottled = import_react37.useCallback(() => {
149930
150186
  if (!buffersRef.current.pendingRefresh) {
149931
150187
  buffersRef.current.pendingRefresh = true;
149932
150188
  setTimeout(() => {
@@ -149935,7 +150191,7 @@ function App2({
149935
150191
  }, 16);
149936
150192
  }
149937
150193
  }, [refreshDerived]);
149938
- import_react36.useEffect(() => {
150194
+ import_react37.useEffect(() => {
149939
150195
  if (loadingState === "ready" && startupApproval) {
149940
150196
  if (startupApproval.toolName === "ExitPlanMode") {
149941
150197
  const parsedArgs = safeJsonParseOr(startupApproval.toolArgs, {});
@@ -149960,7 +150216,7 @@ function App2({
149960
150216
  }
149961
150217
  }
149962
150218
  }, [loadingState, startupApproval]);
149963
- import_react36.useEffect(() => {
150219
+ import_react37.useEffect(() => {
149964
150220
  if (loadingState === "ready" && messageHistory.length > 0 && !hasBackfilledRef.current) {
149965
150221
  hasBackfilledRef.current = true;
149966
150222
  if (!welcomeCommittedRef.current) {
@@ -149989,7 +150245,7 @@ function App2({
149989
150245
  agentId,
149990
150246
  continueSession
149991
150247
  ]);
149992
- import_react36.useEffect(() => {
150248
+ import_react37.useEffect(() => {
149993
150249
  if (loadingState === "ready" && agentId && agentId !== "loading") {
149994
150250
  const fetchConfig = async () => {
149995
150251
  try {
@@ -150004,7 +150260,7 @@ function App2({
150004
150260
  fetchConfig();
150005
150261
  }
150006
150262
  }, [loadingState, agentId]);
150007
- const appendError = import_react36.useCallback((message) => {
150263
+ const appendError = import_react37.useCallback((message) => {
150008
150264
  const id = uid("err");
150009
150265
  buffersRef.current.byId.set(id, {
150010
150266
  kind: "error",
@@ -150014,13 +150270,15 @@ function App2({
150014
150270
  buffersRef.current.order.push(id);
150015
150271
  refreshDerived();
150016
150272
  }, [refreshDerived]);
150017
- const processConversation = import_react36.useCallback(async (initialInput) => {
150273
+ const processConversation = import_react37.useCallback(async (initialInput) => {
150018
150274
  let currentInput = initialInput;
150019
150275
  try {
150020
150276
  setStreaming(true);
150021
150277
  while (true) {
150022
150278
  const stream = await sendMessageStream(agentId, currentInput);
150023
- const { stopReason, approval } = await drainStream(stream, buffersRef.current, refreshDerivedThrottled);
150279
+ const { stopReason, approval, apiDurationMs } = await drainStream(stream, buffersRef.current, refreshDerivedThrottled);
150280
+ sessionStatsRef.current.endTurn(apiDurationMs);
150281
+ sessionStatsRef.current.updateUsageFromBuffers(buffersRef.current);
150024
150282
  refreshDerived();
150025
150283
  if (stopReason === import_letta_client6.Letta.StopReasonType.EndTurn) {
150026
150284
  setStreaming(false);
@@ -150100,7 +150358,13 @@ function App2({
150100
150358
  setStreaming(false);
150101
150359
  }
150102
150360
  }, [agentId, appendError, refreshDerived, refreshDerivedThrottled]);
150103
- const onSubmit = import_react36.useCallback(async (message) => {
150361
+ const handleExit = import_react37.useCallback(() => {
150362
+ setShowExitStats(true);
150363
+ setTimeout(() => {
150364
+ process.exit(0);
150365
+ }, 100);
150366
+ }, []);
150367
+ const onSubmit = import_react37.useCallback(async (message) => {
150104
150368
  const msg = message?.trim() ?? "";
150105
150369
  if (!msg || streaming || commandRunning)
150106
150370
  return;
@@ -150124,6 +150388,10 @@ function App2({
150124
150388
  refreshDerived();
150125
150389
  return;
150126
150390
  }
150391
+ if (msg.trim() === "/exit") {
150392
+ handleExit();
150393
+ return;
150394
+ }
150127
150395
  if (msg.trim() === "/stream") {
150128
150396
  const newValue = !tokenStreamingEnabled;
150129
150397
  const cmdId2 = uid("cmd");
@@ -150232,9 +150500,10 @@ function App2({
150232
150500
  processConversation,
150233
150501
  tokenStreamingEnabled,
150234
150502
  refreshDerived,
150235
- agentId
150503
+ agentId,
150504
+ handleExit
150236
150505
  ]);
150237
- const handleApprove = import_react36.useCallback(async () => {
150506
+ const handleApprove = import_react37.useCallback(async () => {
150238
150507
  if (!pendingApproval)
150239
150508
  return;
150240
150509
  const { toolCallId, toolName, toolArgs } = pendingApproval;
@@ -150274,7 +150543,7 @@ function App2({
150274
150543
  setStreaming(false);
150275
150544
  }
150276
150545
  }, [pendingApproval, processConversation, appendError, refreshDerived]);
150277
- const handleApproveAlways = import_react36.useCallback(async (scope) => {
150546
+ const handleApproveAlways = import_react37.useCallback(async (scope) => {
150278
150547
  if (!pendingApproval || !approvalContext)
150279
150548
  return;
150280
150549
  const rule = approvalContext.recommendedRule;
@@ -150293,7 +150562,7 @@ function App2({
150293
150562
  setApprovalContext(null);
150294
150563
  await handleApprove();
150295
150564
  }, [pendingApproval, approvalContext, handleApprove, refreshDerived]);
150296
- const handleDeny = import_react36.useCallback(async (reason) => {
150565
+ const handleDeny = import_react37.useCallback(async (reason) => {
150297
150566
  if (!pendingApproval)
150298
150567
  return;
150299
150568
  const { toolCallId } = pendingApproval;
@@ -150313,7 +150582,7 @@ function App2({
150313
150582
  setStreaming(false);
150314
150583
  }
150315
150584
  }, [pendingApproval, processConversation, appendError]);
150316
- const handleModelSelect = import_react36.useCallback(async (modelId) => {
150585
+ const handleModelSelect = import_react37.useCallback(async (modelId) => {
150317
150586
  setModelSelectorOpen(false);
150318
150587
  let cmdId = null;
150319
150588
  try {
@@ -150373,8 +150642,8 @@ function App2({
150373
150642
  setCommandRunning(false);
150374
150643
  }
150375
150644
  }, [agentId, refreshDerived]);
150376
- const [uiPermissionMode, setUiPermissionMode] = import_react36.useState(permissionMode2.getMode());
150377
- const handlePlanApprove = import_react36.useCallback(async (acceptEdits = false) => {
150645
+ const [uiPermissionMode, setUiPermissionMode] = import_react37.useState(permissionMode2.getMode());
150646
+ const handlePlanApprove = import_react37.useCallback(async (acceptEdits = false) => {
150378
150647
  if (!planApprovalPending)
150379
150648
  return;
150380
150649
  const { toolCallId, toolArgs } = planApprovalPending;
@@ -150417,7 +150686,7 @@ function App2({
150417
150686
  setStreaming(false);
150418
150687
  }
150419
150688
  }, [planApprovalPending, processConversation, appendError, refreshDerived]);
150420
- const handlePlanKeepPlanning = import_react36.useCallback(async (reason) => {
150689
+ const handlePlanKeepPlanning = import_react37.useCallback(async (reason) => {
150421
150690
  if (!planApprovalPending)
150422
150691
  return;
150423
150692
  const { toolCallId } = planApprovalPending;
@@ -150437,7 +150706,7 @@ function App2({
150437
150706
  setStreaming(false);
150438
150707
  }
150439
150708
  }, [planApprovalPending, processConversation, appendError]);
150440
- const liveItems = import_react36.useMemo(() => {
150709
+ const liveItems = import_react37.useMemo(() => {
150441
150710
  return lines.filter((ln) => {
150442
150711
  if (!("phase" in ln))
150443
150712
  return false;
@@ -150454,7 +150723,7 @@ function App2({
150454
150723
  return ln.phase === "streaming";
150455
150724
  });
150456
150725
  }, [lines, tokenStreamingEnabled]);
150457
- import_react36.useEffect(() => {
150726
+ import_react37.useEffect(() => {
150458
150727
  if (loadingState === "ready" && !welcomeCommittedRef.current && messageHistory.length === 0) {
150459
150728
  welcomeCommittedRef.current = true;
150460
150729
  setStaticItems((prev) => [
@@ -150470,87 +150739,91 @@ function App2({
150470
150739
  ]);
150471
150740
  }
150472
150741
  }, [loadingState, continueSession, agentId, messageHistory.length]);
150473
- return /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Box_default, {
150742
+ return /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(Box_default, {
150474
150743
  flexDirection: "column",
150475
150744
  gap: 1,
150476
150745
  children: [
150477
- /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Static, {
150746
+ /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(Static, {
150478
150747
  items: staticItems,
150479
150748
  style: { flexDirection: "column" },
150480
- children: (item, index) => /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Box_default, {
150749
+ children: (item, index) => /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(Box_default, {
150481
150750
  marginTop: index > 0 ? 1 : 0,
150482
- children: item.kind === "welcome" ? /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(WelcomeScreen, {
150751
+ children: item.kind === "welcome" ? /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(WelcomeScreen, {
150483
150752
  loadingState: "ready",
150484
150753
  ...item.snapshot
150485
- }, undefined, false, undefined, this) : item.kind === "user" ? /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(UserMessage, {
150754
+ }, undefined, false, undefined, this) : item.kind === "user" ? /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(UserMessage, {
150486
150755
  line: item
150487
- }, undefined, false, undefined, this) : item.kind === "reasoning" ? /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(ReasoningMessage, {
150756
+ }, undefined, false, undefined, this) : item.kind === "reasoning" ? /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(ReasoningMessage, {
150488
150757
  line: item
150489
- }, undefined, false, undefined, this) : item.kind === "assistant" ? /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(AssistantMessage, {
150758
+ }, undefined, false, undefined, this) : item.kind === "assistant" ? /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(AssistantMessage, {
150490
150759
  line: item
150491
- }, undefined, false, undefined, this) : item.kind === "tool_call" ? /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(ToolCallMessage, {
150760
+ }, undefined, false, undefined, this) : item.kind === "tool_call" ? /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(ToolCallMessage, {
150492
150761
  line: item
150493
- }, undefined, false, undefined, this) : item.kind === "error" ? /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(ErrorMessage, {
150762
+ }, undefined, false, undefined, this) : item.kind === "error" ? /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(ErrorMessage, {
150494
150763
  line: item
150495
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(CommandMessage, {
150764
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(CommandMessage, {
150496
150765
  line: item
150497
150766
  }, undefined, false, undefined, this)
150498
150767
  }, item.id, false, undefined, this)
150499
- }, undefined, false, undefined, this),
150500
- /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Box_default, {
150768
+ }, staticRenderEpoch, false, undefined, this),
150769
+ /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(Box_default, {
150501
150770
  flexDirection: "column",
150502
150771
  gap: 1,
150503
150772
  children: [
150504
- loadingState !== "ready" && /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(WelcomeScreen, {
150773
+ loadingState !== "ready" && /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(WelcomeScreen, {
150505
150774
  loadingState,
150506
150775
  continueSession,
150507
150776
  agentId: agentId !== "loading" ? agentId : undefined
150508
150777
  }, undefined, false, undefined, this),
150509
- loadingState === "ready" && /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(jsx_dev_runtime20.Fragment, {
150778
+ loadingState === "ready" && /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(jsx_dev_runtime21.Fragment, {
150510
150779
  children: [
150511
- liveItems.length > 0 && /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Box_default, {
150780
+ liveItems.length > 0 && /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(Box_default, {
150512
150781
  flexDirection: "column",
150513
- children: liveItems.map((ln) => /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Box_default, {
150782
+ children: liveItems.map((ln) => /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(Box_default, {
150514
150783
  marginTop: 1,
150515
- children: ln.kind === "user" ? /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(UserMessage, {
150784
+ children: ln.kind === "user" ? /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(UserMessage, {
150516
150785
  line: ln
150517
- }, undefined, false, undefined, this) : ln.kind === "reasoning" ? /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(ReasoningMessage, {
150786
+ }, undefined, false, undefined, this) : ln.kind === "reasoning" ? /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(ReasoningMessage, {
150518
150787
  line: ln
150519
- }, undefined, false, undefined, this) : ln.kind === "assistant" ? /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(AssistantMessage, {
150788
+ }, undefined, false, undefined, this) : ln.kind === "assistant" ? /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(AssistantMessage, {
150520
150789
  line: ln
150521
- }, undefined, false, undefined, this) : ln.kind === "tool_call" ? /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(ToolCallMessage, {
150790
+ }, undefined, false, undefined, this) : ln.kind === "tool_call" ? /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(ToolCallMessage, {
150522
150791
  line: ln
150523
- }, undefined, false, undefined, this) : ln.kind === "error" ? /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(ErrorMessage, {
150792
+ }, undefined, false, undefined, this) : ln.kind === "error" ? /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(ErrorMessage, {
150524
150793
  line: ln
150525
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(CommandMessage, {
150794
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(CommandMessage, {
150526
150795
  line: ln
150527
150796
  }, undefined, false, undefined, this)
150528
150797
  }, ln.id, false, undefined, this))
150529
150798
  }, undefined, false, undefined, this),
150530
- liveItems.length === 0 && /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Box_default, {
150799
+ liveItems.length === 0 && /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(Box_default, {
150531
150800
  height: 1
150532
150801
  }, undefined, false, undefined, this),
150533
- /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Input, {
150534
- visible: !pendingApproval && !modelSelectorOpen && !planApprovalPending,
150802
+ showExitStats && /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(SessionStats2, {
150803
+ stats: sessionStatsRef.current.getSnapshot()
150804
+ }, undefined, false, undefined, this),
150805
+ /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(Input, {
150806
+ visible: !showExitStats && !pendingApproval && !modelSelectorOpen && !planApprovalPending,
150535
150807
  streaming,
150536
150808
  commandRunning,
150537
150809
  tokenCount,
150538
150810
  thinkingMessage,
150539
150811
  onSubmit,
150540
150812
  permissionMode: uiPermissionMode,
150541
- onPermissionModeChange: setUiPermissionMode
150813
+ onPermissionModeChange: setUiPermissionMode,
150814
+ onExit: handleExit
150542
150815
  }, undefined, false, undefined, this),
150543
- modelSelectorOpen && /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(ModelSelector, {
150816
+ modelSelectorOpen && /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(ModelSelector, {
150544
150817
  currentModel: llmConfig?.model,
150545
150818
  onSelect: handleModelSelect,
150546
150819
  onCancel: () => setModelSelectorOpen(false)
150547
150820
  }, undefined, false, undefined, this),
150548
- planApprovalPending && /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(jsx_dev_runtime20.Fragment, {
150821
+ planApprovalPending && /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(jsx_dev_runtime21.Fragment, {
150549
150822
  children: [
150550
- /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Box_default, {
150823
+ /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(Box_default, {
150551
150824
  height: 1
150552
150825
  }, undefined, false, undefined, this),
150553
- /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(PlanModeDialog, {
150826
+ /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(PlanModeDialog, {
150554
150827
  plan: planApprovalPending.plan,
150555
150828
  onApprove: () => handlePlanApprove(false),
150556
150829
  onApproveAndAcceptEdits: () => handlePlanApprove(true),
@@ -150558,12 +150831,12 @@ function App2({
150558
150831
  }, undefined, false, undefined, this)
150559
150832
  ]
150560
150833
  }, undefined, true, undefined, this),
150561
- pendingApproval && /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(jsx_dev_runtime20.Fragment, {
150834
+ pendingApproval && /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(jsx_dev_runtime21.Fragment, {
150562
150835
  children: [
150563
- /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Box_default, {
150836
+ /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(Box_default, {
150564
150837
  height: 1
150565
150838
  }, undefined, false, undefined, this),
150566
- /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(ApprovalDialog, {
150839
+ /* @__PURE__ */ jsx_dev_runtime21.jsxDEV(ApprovalDialog, {
150567
150840
  approvalRequest: pendingApproval,
150568
150841
  approvalContext,
150569
150842
  onApprove: handleApprove,
@@ -150579,7 +150852,7 @@ function App2({
150579
150852
  ]
150580
150853
  }, undefined, true, undefined, this);
150581
150854
  }
150582
- var import_letta_client6, import_react36, jsx_dev_runtime20;
150855
+ var import_letta_client6, import_react37, jsx_dev_runtime21, CLEAR_SCREEN_AND_HOME = "\x1B[2J\x1B[H";
150583
150856
  var init_App2 = __esm(async () => {
150584
150857
  init_message();
150585
150858
  init_mode2();
@@ -150587,6 +150860,7 @@ var init_App2 = __esm(async () => {
150587
150860
  init_pasteRegistry();
150588
150861
  init_stream();
150589
150862
  init_thinkingMessages();
150863
+ init_useTerminalWidth();
150590
150864
  await __promiseAll([
150591
150865
  init_build3(),
150592
150866
  init_ApprovalDialogRich(),
@@ -150597,13 +150871,14 @@ var init_App2 = __esm(async () => {
150597
150871
  init_ModelSelector(),
150598
150872
  init_PlanModeDialog(),
150599
150873
  init_ReasoningMessageRich(),
150874
+ init_SessionStats(),
150600
150875
  init_ToolCallMessageRich(),
150601
150876
  init_UserMessageRich(),
150602
150877
  init_WelcomeScreen()
150603
150878
  ]);
150604
150879
  import_letta_client6 = __toESM(require_letta_client(), 1);
150605
- import_react36 = __toESM(require_react2(), 1);
150606
- jsx_dev_runtime20 = __toESM(require_jsx_dev_runtime(), 1);
150880
+ import_react37 = __toESM(require_react2(), 1);
150881
+ jsx_dev_runtime21 = __toESM(require_jsx_dev_runtime(), 1);
150607
150882
  });
150608
150883
 
150609
150884
  // src/agent/create.ts
@@ -150765,7 +151040,15 @@ init_settings();
150765
151040
  // src/tools/manager.ts
150766
151041
  init_toolDefinitions();
150767
151042
  var TOOL_NAMES = Object.keys(TOOL_DEFINITIONS);
150768
- var toolRegistry = new Map;
151043
+ var REGISTRY_KEY = Symbol.for("@letta/toolRegistry");
151044
+ function getRegistry() {
151045
+ const global2 = globalThis;
151046
+ if (!global2[REGISTRY_KEY]) {
151047
+ global2[REGISTRY_KEY] = new Map;
151048
+ }
151049
+ return global2[REGISTRY_KEY];
151050
+ }
151051
+ var toolRegistry = getRegistry();
150769
151052
  function generatePythonStub(name, _description, schema) {
150770
151053
  const params = schema.properties ?? {};
150771
151054
  const required = schema.required ?? [];
@@ -150845,12 +151128,17 @@ OPTIONS
150845
151128
  -c, --continue Resume previous session (uses settings.lastAgent)
150846
151129
  -a, --agent <id> Use a specific agent ID
150847
151130
  -p, --prompt Headless prompt mode
151131
+ --output-format <fmt> Output format for headless mode (text, json, stream-json)
151132
+ Default: text
150848
151133
 
150849
151134
  EXAMPLES
150850
151135
  # when installed as an executable
150851
151136
  letta --help
150852
151137
  letta --continue
150853
151138
  letta --agent agent_123
151139
+
151140
+ # headless with JSON output (includes stats)
151141
+ letta -p "hello" --output-format json
150854
151142
 
150855
151143
  `.trim();
150856
151144
  console.log(usage);
@@ -150871,7 +151159,8 @@ async function main() {
150871
151159
  allowedTools: { type: "string" },
150872
151160
  disallowedTools: { type: "string" },
150873
151161
  "permission-mode": { type: "string" },
150874
- yolo: { type: "boolean" }
151162
+ yolo: { type: "boolean" },
151163
+ "output-format": { type: "string" }
150875
151164
  },
150876
151165
  strict: true,
150877
151166
  allowPositionals: true
@@ -150946,17 +151235,17 @@ Note: Flags should use double dashes for full names (e.g., --yolo, not -yolo)`);
150946
151235
  }
150947
151236
  const React12 = await Promise.resolve().then(() => __toESM(require_react(), 1));
150948
151237
  const { render: render2 } = await init_build2().then(() => exports_build);
150949
- const { useState: useState12, useEffect: useEffect10 } = React12;
151238
+ const { useState: useState13, useEffect: useEffect11 } = React12;
150950
151239
  const AppModule = await init_App2().then(() => exports_App);
150951
151240
  const App3 = AppModule.default;
150952
151241
  function LoadingApp({
150953
151242
  continueSession,
150954
151243
  agentIdArg
150955
151244
  }) {
150956
- const [loadingState, setLoadingState] = useState12("assembling");
150957
- const [agentId, setAgentId] = useState12(null);
150958
- const [resumeData, setResumeData] = useState12(null);
150959
- useEffect10(() => {
151245
+ const [loadingState, setLoadingState] = useState13("assembling");
151246
+ const [agentId, setAgentId] = useState13(null);
151247
+ const [resumeData, setResumeData] = useState13(null);
151248
+ useEffect11(() => {
150960
151249
  async function init() {
150961
151250
  setLoadingState("assembling");
150962
151251
  await loadTools();
@@ -151024,4 +151313,4 @@ Note: Flags should use double dashes for full names (e.g., --yolo, not -yolo)`);
151024
151313
  }
151025
151314
  main();
151026
151315
 
151027
- //# debugId=498B46A1A1442B5464756E2164756E21
151316
+ //# debugId=6656CCE5EA031CEB64756E2164756E21