@letta-ai/letta-code 0.19.1 → 0.19.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
@@ -3240,7 +3240,7 @@ var package_default;
3240
3240
  var init_package = __esm(() => {
3241
3241
  package_default = {
3242
3242
  name: "@letta-ai/letta-code",
3243
- version: "0.19.1",
3243
+ version: "0.19.3",
3244
3244
  description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
3245
3245
  type: "module",
3246
3246
  bin: {
@@ -5737,7 +5737,7 @@ var explore_default = `---
5737
5737
  name: explore
5738
5738
  description: Fast agent for codebase exploration - finding files, searching code, understanding structure. (Read-Only)
5739
5739
  tools: Glob, Grep, Read, TaskOutput
5740
- model: haiku
5740
+ model: auto-fast
5741
5741
  memoryBlocks: human, persona
5742
5742
  mode: stateless
5743
5743
  ---
@@ -5772,7 +5772,7 @@ var general_purpose_default = `---
5772
5772
  name: general-purpose
5773
5773
  description: Full-capability agent for research, planning, and implementation
5774
5774
  tools: Bash, TaskOutput, Edit, Glob, Grep, KillBash, LS, MultiEdit, Read, TodoWrite, Write
5775
- model: sonnet
5775
+ model: auto
5776
5776
  memoryBlocks: all
5777
5777
  mode: stateful
5778
5778
  ---
@@ -5813,7 +5813,7 @@ name: history-analyzer
5813
5813
  description: Analyze Claude Code or Codex conversation history and directly update agent memory files with insights
5814
5814
  tools: Read, Write, Bash, Glob, Grep
5815
5815
  skills: migrating-from-codex-and-claude-code
5816
- model: sonnet
5816
+ model: auto
5817
5817
  memoryBlocks: none
5818
5818
  mode: stateless
5819
5819
  permissionMode: bypassPermissions
@@ -5943,7 +5943,7 @@ var init_default = `---
5943
5943
  name: init
5944
5944
  description: Fast initialization of agent memory — reads key project files and creates a minimal memory hierarchy
5945
5945
  tools: Read, Write, Edit, Bash, Glob
5946
- model: haiku
5946
+ model: auto-fast
5947
5947
  memoryBlocks: none
5948
5948
  permissionMode: bypassPermissions
5949
5949
  ---
@@ -6041,7 +6041,7 @@ var memory_default = `---
6041
6041
  name: memory
6042
6042
  description: Decompose and reorganize memory files into focused, single-purpose files using \`/\` naming
6043
6043
  tools: Read, Edit, Write, Glob, Grep, Bash, TaskOutput
6044
- model: sonnet
6044
+ model: auto
6045
6045
  memoryBlocks: none
6046
6046
  permissionMode: bypassPermissions
6047
6047
  ---
@@ -6365,7 +6365,7 @@ name: recall
6365
6365
  description: Search conversation history to recall past discussions, decisions, and context
6366
6366
  tools: Bash, Read, TaskOutput
6367
6367
  skills: searching-messages
6368
- model: haiku
6368
+ model: auto-fast
6369
6369
  memoryBlocks: none
6370
6370
  mode: stateless
6371
6371
  ---
@@ -6395,7 +6395,7 @@ var reflection_default = `---
6395
6395
  name: reflection
6396
6396
  description: Background agent that reflects on recent conversations and updates memory files
6397
6397
  tools: Read, Edit, Write, Glob, Grep, Bash, TaskOutput
6398
- model: sonnet
6398
+ model: auto
6399
6399
  memoryBlocks: none
6400
6400
  mode: stateless
6401
6401
  permissionMode: bypassPermissions
@@ -6965,6 +6965,7 @@ var init_models2 = __esm(() => {
6965
6965
  models: [
6966
6966
  {
6967
6967
  id: "auto",
6968
+ isDefault: true,
6968
6969
  handle: "letta/auto",
6969
6970
  label: "Auto",
6970
6971
  description: "Automatically select the best model",
@@ -6984,7 +6985,6 @@ var init_models2 = __esm(() => {
6984
6985
  handle: "anthropic/claude-sonnet-4-6",
6985
6986
  label: "Sonnet 4.6",
6986
6987
  description: "Anthropic's new Sonnet model (high reasoning)",
6987
- isDefault: true,
6988
6988
  isFeatured: true,
6989
6989
  updateArgs: {
6990
6990
  context_window: 200000,
@@ -32716,13 +32716,24 @@ var import_react16, IS_LINUX, CSI_U_WITH_TRAILING_NEWLINE_PATTERN, CSI_PROTOCOL_
32716
32716
  9: "tab",
32717
32717
  13: "return",
32718
32718
  27: "escape",
32719
+ 32: "space",
32719
32720
  127: "backspace"
32720
32721
  };
32722
+ const csiUSeqMap = {
32723
+ 9: "\t",
32724
+ 13: "\r",
32725
+ 27: "\x1B",
32726
+ 32: " ",
32727
+ 127: ""
32728
+ };
32721
32729
  let name = csiUKeyMap[keycode] || "";
32730
+ let seq2 = csiUSeqMap[keycode] || data;
32722
32731
  if (!name && keycode >= 97 && keycode <= 122) {
32723
32732
  name = String.fromCharCode(keycode);
32733
+ seq2 = String.fromCharCode(keycode);
32724
32734
  } else if (!name && keycode >= 65 && keycode <= 90) {
32725
32735
  name = String.fromCharCode(keycode + 32);
32736
+ seq2 = String.fromCharCode(keycode + 32);
32726
32737
  }
32727
32738
  if (name) {
32728
32739
  keypress = {
@@ -32731,7 +32742,7 @@ var import_react16, IS_LINUX, CSI_U_WITH_TRAILING_NEWLINE_PATTERN, CSI_PROTOCOL_
32731
32742
  meta: !!(modifier & 10),
32732
32743
  shift: !!(modifier & 1),
32733
32744
  option: false,
32734
- sequence: data,
32745
+ sequence: seq2,
32735
32746
  raw: data
32736
32747
  };
32737
32748
  }
@@ -36931,10 +36942,17 @@ var init_byok_providers = __esm(async () => {
36931
36942
  {
36932
36943
  id: "zai",
36933
36944
  displayName: "zAI API",
36934
- description: "Connect a zAI key or coding plan",
36945
+ description: "Connect a zAI API key",
36935
36946
  providerType: "zai",
36936
36947
  providerName: "lc-zai"
36937
36948
  },
36949
+ {
36950
+ id: "zai-coding",
36951
+ displayName: "zAI Coding Plan",
36952
+ description: "Connect a zAI Coding plan key",
36953
+ providerType: "zai_coding",
36954
+ providerName: "lc-zai-coding"
36955
+ },
36938
36956
  {
36939
36957
  id: "minimax",
36940
36958
  displayName: "MiniMax API",
@@ -36993,7 +37011,9 @@ var init_byok_providers = __esm(async () => {
36993
37011
 
36994
37012
  // src/cli/commands/connect-normalize.ts
36995
37013
  function canonicalToByokId(canonical) {
36996
- return canonical === "chatgpt" ? "codex" : canonical;
37014
+ if (canonical === "chatgpt")
37015
+ return "codex";
37016
+ return canonical;
36997
37017
  }
36998
37018
  function resolveConnectProvider(providerToken) {
36999
37019
  if (!providerToken) {
@@ -37039,6 +37059,9 @@ function isConnectBedrockProvider(provider) {
37039
37059
  function isConnectApiKeyProvider(provider) {
37040
37060
  return !isConnectOAuthProvider(provider) && !isConnectBedrockProvider(provider);
37041
37061
  }
37062
+ function isConnectZaiBaseProvider(provider) {
37063
+ return provider.canonical === "zai";
37064
+ }
37042
37065
  var ALIAS_TO_CANONICAL, CANONICAL_ORDER;
37043
37066
  var init_connect_normalize = __esm(async () => {
37044
37067
  await init_byok_providers();
@@ -37048,6 +37071,7 @@ var init_connect_normalize = __esm(async () => {
37048
37071
  anthropic: "anthropic",
37049
37072
  openai: "openai",
37050
37073
  zai: "zai",
37074
+ "zai-coding": "zai-coding",
37051
37075
  minimax: "minimax",
37052
37076
  gemini: "gemini",
37053
37077
  openrouter: "openrouter",
@@ -37058,6 +37082,7 @@ var init_connect_normalize = __esm(async () => {
37058
37082
  "anthropic",
37059
37083
  "openai",
37060
37084
  "zai",
37085
+ "zai-coding",
37061
37086
  "minimax",
37062
37087
  "gemini",
37063
37088
  "openrouter",
@@ -40149,7 +40174,8 @@ async function executeSingleDecision(decision, onChunk, options) {
40149
40174
  tool_return: decision.precomputedResult.toolReturn,
40150
40175
  status: decision.precomputedResult.status,
40151
40176
  stdout: decision.precomputedResult.stdout,
40152
- stderr: decision.precomputedResult.stderr
40177
+ stderr: decision.precomputedResult.stderr,
40178
+ reason: decision.reason
40153
40179
  };
40154
40180
  }
40155
40181
  try {
@@ -40187,7 +40213,8 @@ async function executeSingleDecision(decision, onChunk, options) {
40187
40213
  tool_return: toolResult.toolReturn,
40188
40214
  status: toolResult.status,
40189
40215
  stdout: toolResult.stdout,
40190
- stderr: toolResult.stderr
40216
+ stderr: toolResult.stderr,
40217
+ reason: decision.reason
40191
40218
  };
40192
40219
  } catch (e) {
40193
40220
  const isAbortError2 = e instanceof Error && (e.name === "AbortError" || e.message === "The operation was aborted");
@@ -40206,7 +40233,8 @@ async function executeSingleDecision(decision, onChunk, options) {
40206
40233
  type: "tool",
40207
40234
  tool_call_id: decision.approval.toolCallId,
40208
40235
  tool_return: errorMessage,
40209
- status: "error"
40236
+ status: "error",
40237
+ reason: decision.reason
40210
40238
  };
40211
40239
  }
40212
40240
  }
@@ -42660,6 +42688,9 @@ class PermissionModeManager {
42660
42688
  const _effectivePlanFilePath = planFilePathOverride !== undefined ? planFilePathOverride : this.getPlanFilePath();
42661
42689
  switch (effectiveMode) {
42662
42690
  case "bypassPermissions":
42691
+ if (toolName === "ExitPlanMode" || toolName === "exit_plan_mode") {
42692
+ return null;
42693
+ }
42663
42694
  return "allow";
42664
42695
  case "acceptEdits":
42665
42696
  if ([
@@ -68131,7 +68162,125 @@ var init_manager3 = __esm(async () => {
68131
68162
  });
68132
68163
 
68133
68164
  // src/websocket/terminalHandler.ts
68165
+ import * as os4 from "node:os";
68134
68166
  import WebSocket from "ws";
68167
+ function getDefaultShell() {
68168
+ if (os4.platform() === "win32") {
68169
+ return process.env.COMSPEC || "cmd.exe";
68170
+ }
68171
+ return process.env.SHELL || "/bin/zsh";
68172
+ }
68173
+ function sendTerminalMessage(socket, message) {
68174
+ if (socket.readyState === WebSocket.OPEN) {
68175
+ socket.send(JSON.stringify(message));
68176
+ }
68177
+ }
68178
+ function handleTerminalSpawn(msg, socket, cwd2) {
68179
+ const { terminal_id, cols, rows } = msg;
68180
+ killTerminal(terminal_id);
68181
+ const shell2 = getDefaultShell();
68182
+ console.log(`[Terminal] Spawning PTY: shell=${shell2}, cwd=${cwd2}, cols=${cols}, rows=${rows}`);
68183
+ try {
68184
+ const proc2 = Bun.spawn([shell2], {
68185
+ cwd: cwd2,
68186
+ env: {
68187
+ ...process.env,
68188
+ TERM: "xterm-256color",
68189
+ COLORTERM: "truecolor"
68190
+ },
68191
+ terminal: {
68192
+ cols: cols || 80,
68193
+ rows: rows || 24,
68194
+ data: (() => {
68195
+ let buffer = "";
68196
+ let flushTimer = null;
68197
+ return (_terminal, data) => {
68198
+ buffer += new TextDecoder().decode(data);
68199
+ if (!flushTimer) {
68200
+ flushTimer = setTimeout(() => {
68201
+ if (buffer.length > 0) {
68202
+ sendTerminalMessage(socket, {
68203
+ type: "terminal_output",
68204
+ terminal_id,
68205
+ data: buffer
68206
+ });
68207
+ buffer = "";
68208
+ }
68209
+ flushTimer = null;
68210
+ }, 16);
68211
+ }
68212
+ };
68213
+ })()
68214
+ }
68215
+ });
68216
+ const terminal = proc2.terminal;
68217
+ console.log(`[Terminal] proc.pid=${proc2.pid}, terminal=${typeof terminal}, keys=${Object.keys(proc2).join(",")}`);
68218
+ if (!terminal) {
68219
+ console.error("[Terminal] terminal object is undefined on proc — Bun.Terminal API may not be available");
68220
+ sendTerminalMessage(socket, {
68221
+ type: "terminal_exited",
68222
+ terminal_id,
68223
+ exitCode: 1
68224
+ });
68225
+ return;
68226
+ }
68227
+ const session = {
68228
+ process: proc2,
68229
+ terminal,
68230
+ terminalId: terminal_id,
68231
+ spawnedAt: Date.now()
68232
+ };
68233
+ terminals.set(terminal_id, session);
68234
+ console.log(`[Terminal] Session stored for terminal_id=${terminal_id}, map size=${terminals.size}`);
68235
+ const myPid = proc2.pid;
68236
+ proc2.exited.then((exitCode) => {
68237
+ const current = terminals.get(terminal_id);
68238
+ if (current && current.process.pid === myPid) {
68239
+ console.log(`[Terminal] PTY process exited: terminal_id=${terminal_id}, pid=${myPid}, exitCode=${exitCode}`);
68240
+ terminals.delete(terminal_id);
68241
+ sendTerminalMessage(socket, {
68242
+ type: "terminal_exited",
68243
+ terminal_id,
68244
+ exitCode: exitCode ?? 0
68245
+ });
68246
+ } else {
68247
+ console.log(`[Terminal] Stale PTY exit ignored: terminal_id=${terminal_id}, pid=${myPid} (current pid=${current?.process.pid})`);
68248
+ }
68249
+ });
68250
+ sendTerminalMessage(socket, {
68251
+ type: "terminal_spawned",
68252
+ terminal_id,
68253
+ pid: proc2.pid
68254
+ });
68255
+ } catch (error) {
68256
+ console.error("[Terminal] Failed to spawn PTY:", error);
68257
+ sendTerminalMessage(socket, {
68258
+ type: "terminal_exited",
68259
+ terminal_id,
68260
+ exitCode: 1
68261
+ });
68262
+ }
68263
+ }
68264
+ function handleTerminalInput(msg) {
68265
+ const session = terminals.get(msg.terminal_id);
68266
+ if (session) {
68267
+ session.terminal.write(msg.data);
68268
+ }
68269
+ }
68270
+ function handleTerminalResize(msg) {
68271
+ const session = terminals.get(msg.terminal_id);
68272
+ if (session) {
68273
+ session.terminal.resize(msg.cols, msg.rows);
68274
+ }
68275
+ }
68276
+ function handleTerminalKill(msg) {
68277
+ const session = terminals.get(msg.terminal_id);
68278
+ if (session && Date.now() - session.spawnedAt < 2000) {
68279
+ console.log(`[Terminal] Ignoring kill for recently spawned session (age=${Date.now() - session.spawnedAt}ms)`);
68280
+ return;
68281
+ }
68282
+ killTerminal(msg.terminal_id);
68283
+ }
68135
68284
  function killTerminal(terminalId) {
68136
68285
  const session = terminals.get(terminalId);
68137
68286
  if (session) {
@@ -68327,7 +68476,25 @@ function getPermissionModeScopeKey(agentId, conversationId) {
68327
68476
  }
68328
68477
  function getConversationPermissionModeState(runtime, agentId, conversationId) {
68329
68478
  const scopeKey = getPermissionModeScopeKey(agentId, conversationId);
68330
- return runtime.permissionModeByConversation.get(scopeKey) ?? {
68479
+ const normalizedConversationId = normalizeConversationId(conversationId);
68480
+ const direct = runtime.permissionModeByConversation.get(scopeKey);
68481
+ if (direct) {
68482
+ return direct;
68483
+ }
68484
+ if (normalizedConversationId === "default") {
68485
+ const legacyDefaultKey = getPermissionModeScopeKey(null, "default");
68486
+ const legacyDefault = runtime.permissionModeByConversation.get(legacyDefaultKey);
68487
+ if (legacyDefault) {
68488
+ if (normalizeCwdAgentId(agentId)) {
68489
+ runtime.permissionModeByConversation.set(scopeKey, {
68490
+ ...legacyDefault
68491
+ });
68492
+ runtime.permissionModeByConversation.delete(legacyDefaultKey);
68493
+ }
68494
+ return legacyDefault;
68495
+ }
68496
+ }
68497
+ return {
68331
68498
  mode: permissionMode.getMode(),
68332
68499
  planFilePath: null,
68333
68500
  modeBeforePlan: null
@@ -68956,9 +69123,10 @@ function isValidApprovalResponseBody(value) {
68956
69123
  }
68957
69124
  const decision = maybeResponse.decision;
68958
69125
  if (decision.behavior === "allow") {
69126
+ const hasMessage = decision.message === undefined || typeof decision.message === "string";
68959
69127
  const hasUpdatedInput = decision.updated_input === undefined || decision.updated_input === null || typeof decision.updated_input === "object";
68960
69128
  const hasUpdatedPermissions = decision.updated_permissions === undefined || Array.isArray(decision.updated_permissions) && decision.updated_permissions.every((entry) => typeof entry === "string");
68961
- return hasUpdatedInput && hasUpdatedPermissions;
69129
+ return hasMessage && hasUpdatedInput && hasUpdatedPermissions;
68962
69130
  }
68963
69131
  if (decision.behavior === "deny") {
68964
69132
  return typeof decision.message === "string";
@@ -69093,23 +69261,62 @@ function normalizeToolReturnText(value) {
69093
69261
  return String(value);
69094
69262
  }
69095
69263
  }
69264
+ function isToolReturnContent(value) {
69265
+ if (typeof value === "string")
69266
+ return true;
69267
+ if (!Array.isArray(value))
69268
+ return false;
69269
+ return value.every((part) => !!part && typeof part === "object" && ("type" in part) && (part.type === "text" && ("text" in part) && typeof part.text === "string" || part.type === "image" && ("data" in part) && typeof part.data === "string" && ("mimeType" in part) && typeof part.mimeType === "string"));
69270
+ }
69271
+ function prependApprovalComment(toolReturn, reason) {
69272
+ const trimmedReason = reason?.trim();
69273
+ if (!trimmedReason) {
69274
+ return toolReturn;
69275
+ }
69276
+ const commentPart = {
69277
+ type: "text",
69278
+ text: `${APPROVAL_COMMENT_PREFIX} "${trimmedReason}"`
69279
+ };
69280
+ if (typeof toolReturn === "string") {
69281
+ return [commentPart, { type: "text", text: toolReturn }];
69282
+ }
69283
+ return [commentPart, ...toolReturn];
69284
+ }
69096
69285
  function normalizeApprovalResultsForPersistence(approvals, options = {}) {
69097
69286
  if (!approvals || approvals.length === 0)
69098
69287
  return approvals ?? [];
69099
69288
  const interruptedSet = new Set(options.interruptedToolCallIds ?? []);
69100
69289
  return approvals.map((approval) => {
69290
+ if (approval && typeof approval === "object" && "type" in approval && approval.type === "approval" && "approve" in approval && approval.approve === true && "tool_return" in approval && isToolReturnContent(approval.tool_return)) {
69291
+ return {
69292
+ type: "tool",
69293
+ tool_call_id: "tool_call_id" in approval && typeof approval.tool_call_id === "string" ? approval.tool_call_id : "",
69294
+ tool_return: approval.tool_return,
69295
+ status: "status" in approval && approval.status === "error" ? "error" : "success",
69296
+ stdout: "stdout" in approval && Array.isArray(approval.stdout) ? approval.stdout : undefined,
69297
+ stderr: "stderr" in approval && Array.isArray(approval.stderr) ? approval.stderr : undefined
69298
+ };
69299
+ }
69101
69300
  if (!approval || typeof approval !== "object" || !("type" in approval) || approval.type !== "tool") {
69102
69301
  return approval;
69103
69302
  }
69104
69303
  const toolCallId = "tool_call_id" in approval && typeof approval.tool_call_id === "string" ? approval.tool_call_id : "";
69304
+ const toolReturn = prependApprovalComment(approval.tool_return, "reason" in approval && typeof approval.reason === "string" ? approval.reason : undefined);
69105
69305
  const interruptedByStructuredId = toolCallId.length > 0 && interruptedSet.has(toolCallId);
69106
69306
  const interruptedByLegacyText = options.allowInterruptTextFallback ? normalizeToolReturnText("tool_return" in approval ? approval.tool_return : "") === INTERRUPTED_BY_USER : false;
69107
69307
  if ((interruptedByStructuredId || interruptedByLegacyText) && "status" in approval && approval.status !== "error") {
69108
69308
  return {
69109
69309
  ...approval,
69310
+ tool_return: toolReturn,
69110
69311
  status: "error"
69111
69312
  };
69112
69313
  }
69314
+ if (toolReturn !== approval.tool_return) {
69315
+ return {
69316
+ ...approval,
69317
+ tool_return: toolReturn
69318
+ };
69319
+ }
69113
69320
  return approval;
69114
69321
  });
69115
69322
  }
@@ -69127,6 +69334,7 @@ function normalizeOutgoingApprovalMessages(messages, options = {}) {
69127
69334
  };
69128
69335
  });
69129
69336
  }
69337
+ var APPROVAL_COMMENT_PREFIX = "The user approved the tool execution with the following comment:";
69130
69338
  var init_approval_result_normalization = __esm(() => {
69131
69339
  init_constants();
69132
69340
  });
@@ -69568,11 +69776,35 @@ function isSyncCommand(value) {
69568
69776
  const candidate = value;
69569
69777
  return candidate.type === "sync" && isRuntimeScope(candidate.runtime);
69570
69778
  }
69779
+ function isTerminalSpawnCommand(value) {
69780
+ if (!value || typeof value !== "object")
69781
+ return false;
69782
+ const c = value;
69783
+ return c.type === "terminal_spawn" && typeof c.terminal_id === "string" && typeof c.cols === "number" && typeof c.rows === "number";
69784
+ }
69785
+ function isTerminalInputCommand(value) {
69786
+ if (!value || typeof value !== "object")
69787
+ return false;
69788
+ const c = value;
69789
+ return c.type === "terminal_input" && typeof c.terminal_id === "string" && typeof c.data === "string";
69790
+ }
69791
+ function isTerminalResizeCommand(value) {
69792
+ if (!value || typeof value !== "object")
69793
+ return false;
69794
+ const c = value;
69795
+ return c.type === "terminal_resize" && typeof c.terminal_id === "string" && typeof c.cols === "number" && typeof c.rows === "number";
69796
+ }
69797
+ function isTerminalKillCommand(value) {
69798
+ if (!value || typeof value !== "object")
69799
+ return false;
69800
+ const c = value;
69801
+ return c.type === "terminal_kill" && typeof c.terminal_id === "string";
69802
+ }
69571
69803
  function parseServerMessage(data) {
69572
69804
  try {
69573
69805
  const raw = typeof data === "string" ? data : data.toString();
69574
69806
  const parsed = JSON.parse(raw);
69575
- if (isInputCommand(parsed) || isChangeDeviceStateCommand(parsed) || isAbortMessageCommand(parsed) || isSyncCommand(parsed)) {
69807
+ if (isInputCommand(parsed) || isChangeDeviceStateCommand(parsed) || isAbortMessageCommand(parsed) || isSyncCommand(parsed) || isTerminalSpawnCommand(parsed) || isTerminalInputCommand(parsed) || isTerminalResizeCommand(parsed) || isTerminalKillCommand(parsed)) {
69576
69808
  return parsed;
69577
69809
  }
69578
69810
  const invalidInput = getInvalidInputReason(parsed);
@@ -71277,7 +71509,7 @@ var init_interactivePolicy = __esm(() => {
71277
71509
  "ExitPlanMode"
71278
71510
  ]);
71279
71511
  RUNTIME_USER_INPUT_TOOLS = new Set(["AskUserQuestion", "ExitPlanMode"]);
71280
- HEADLESS_AUTO_ALLOW_TOOLS = new Set(["EnterPlanMode"]);
71512
+ HEADLESS_AUTO_ALLOW_TOOLS = new Set(["EnterPlanMode", "ExitPlanMode"]);
71281
71513
  });
71282
71514
 
71283
71515
  // src/cli/helpers/toolNameMapping.ts
@@ -74425,7 +74657,8 @@ async function resolveRecoveredApprovalResponse(runtime, socket, response, proce
74425
74657
  approval: decision.updated_input ? {
74426
74658
  ...entry.approval,
74427
74659
  toolArgs: JSON.stringify(decision.updated_input)
74428
- } : entry.approval
74660
+ } : entry.approval,
74661
+ reason: decision.message
74429
74662
  });
74430
74663
  } else {
74431
74664
  decisions.push({
@@ -74737,7 +74970,8 @@ async function resolveStaleApprovals(runtime, socket, abortSignal) {
74737
74970
  approval: response.updated_input ? {
74738
74971
  ...ac.approval,
74739
74972
  toolArgs: JSON.stringify(response.updated_input)
74740
- } : ac.approval
74973
+ } : ac.approval,
74974
+ reason: response.message
74741
74975
  });
74742
74976
  } else {
74743
74977
  decisions.push({
@@ -75362,7 +75596,7 @@ function gatherGitContextSnapshot(options = {}) {
75362
75596
  var init_gitContext = () => {};
75363
75597
 
75364
75598
  // src/cli/helpers/sessionContext.ts
75365
- import { platform as platform3 } from "node:os";
75599
+ import { platform as platform4 } from "node:os";
75366
75600
  function getLocalTime() {
75367
75601
  const now = new Date;
75368
75602
  return now.toLocaleString(undefined, {
@@ -75376,7 +75610,7 @@ function getLocalTime() {
75376
75610
  });
75377
75611
  }
75378
75612
  function getDeviceType() {
75379
- const p = platform3();
75613
+ const p = platform4();
75380
75614
  switch (p) {
75381
75615
  case "darwin":
75382
75616
  return "macOS";
@@ -75449,7 +75683,7 @@ ${gitInfo.status}
75449
75683
  context3 += `- **Git repository**: No
75450
75684
  `;
75451
75685
  }
75452
- if (platform3() === "win32") {
75686
+ if (platform4() === "win32") {
75453
75687
  context3 += `
75454
75688
  ## Windows Shell Notes
75455
75689
  - The Bash tool uses PowerShell or cmd.exe on Windows
@@ -75984,7 +76218,11 @@ async function handleApprovalStop(params) {
75984
76218
  ...ac.approval,
75985
76219
  toolArgs: JSON.stringify(response.updated_input)
75986
76220
  } : ac.approval;
75987
- decisions.push({ type: "approve", approval: finalApproval });
76221
+ decisions.push({
76222
+ type: "approve",
76223
+ approval: finalApproval,
76224
+ reason: response.message
76225
+ });
75988
76226
  } else {
75989
76227
  decisions.push({
75990
76228
  type: "deny",
@@ -76573,6 +76811,10 @@ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, conne
76573
76811
  }
76574
76812
  } finally {
76575
76813
  setConversationPermissionModeState(runtime.listener, normalizedAgentId, conversationId, turnPermissionModeState);
76814
+ emitDeviceStatusIfOpen(runtime, {
76815
+ agent_id: agentId || null,
76816
+ conversation_id: conversationId
76817
+ });
76576
76818
  runtime.activeAbortController = null;
76577
76819
  runtime.cancelRequested = false;
76578
76820
  runtime.isRecoveringApprovals = false;
@@ -77092,6 +77334,22 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
77092
77334
  scheduleQueuePump(scopedRuntime, socket, opts, processQueuedTurn);
77093
77335
  return;
77094
77336
  }
77337
+ if (parsed.type === "terminal_spawn") {
77338
+ handleTerminalSpawn(parsed, socket, runtime.bootWorkingDirectory);
77339
+ return;
77340
+ }
77341
+ if (parsed.type === "terminal_input") {
77342
+ handleTerminalInput(parsed);
77343
+ return;
77344
+ }
77345
+ if (parsed.type === "terminal_resize") {
77346
+ handleTerminalResize(parsed);
77347
+ return;
77348
+ }
77349
+ if (parsed.type === "terminal_kill") {
77350
+ handleTerminalKill(parsed);
77351
+ return;
77352
+ }
77095
77353
  });
77096
77354
  socket.on("close", (code, reason) => {
77097
77355
  if (runtime !== getActiveRuntime()) {
@@ -78353,10 +78611,10 @@ __export(exports_overflow, {
78353
78611
  });
78354
78612
  import { randomUUID as randomUUID4 } from "node:crypto";
78355
78613
  import * as fs15 from "node:fs";
78356
- import * as os4 from "node:os";
78614
+ import * as os5 from "node:os";
78357
78615
  import * as path24 from "node:path";
78358
78616
  function getOverflowDirectory2(workingDirectory) {
78359
- const homeDir = os4.homedir();
78617
+ const homeDir = os5.homedir();
78360
78618
  const lettaDir = path24.join(homeDir, ".letta");
78361
78619
  const normalizedPath = path24.normalize(workingDirectory);
78362
78620
  const sanitizedPath = normalizedPath.replace(/^[/\\]/, "").replace(/[/\\:]/g, "_").replace(/\s+/g, "_");
@@ -82609,10 +82867,10 @@ var init_reconcileExistingAgentState = __esm(() => {
82609
82867
 
82610
82868
  // src/agent/sessionHistory.ts
82611
82869
  import * as fs17 from "node:fs";
82612
- import * as os5 from "node:os";
82870
+ import * as os6 from "node:os";
82613
82871
  import * as path25 from "node:path";
82614
82872
  function getHistoryDir() {
82615
- const homeDir = os5.homedir();
82873
+ const homeDir = os6.homedir();
82616
82874
  return path25.join(homeDir, ".letta-code");
82617
82875
  }
82618
82876
  function getHistoryFilePath() {
@@ -98252,6 +98510,26 @@ var init_lowlight = __esm(() => {
98252
98510
  });
98253
98511
 
98254
98512
  // src/cli/components/SyntaxHighlightedCommand.tsx
98513
+ function clipStyledSpans(spans, maxColumns) {
98514
+ if (maxColumns <= 0) {
98515
+ return { spans: [], clipped: spans.length > 0 };
98516
+ }
98517
+ let remaining = maxColumns;
98518
+ const clipped = [];
98519
+ for (const span of spans) {
98520
+ if (remaining <= 0) {
98521
+ return { spans: clipped, clipped: true };
98522
+ }
98523
+ if (span.text.length <= remaining) {
98524
+ clipped.push(span);
98525
+ remaining -= span.text.length;
98526
+ continue;
98527
+ }
98528
+ clipped.push({ text: span.text.slice(0, remaining), color: span.color });
98529
+ return { spans: clipped, clipped: true };
98530
+ }
98531
+ return { spans: clipped, clipped: false };
98532
+ }
98255
98533
  function languageFromPath(filePath) {
98256
98534
  const basename4 = filePath.split("/").pop() ?? filePath;
98257
98535
  const lower = basename4.toLowerCase();
@@ -98479,34 +98757,70 @@ var init_SyntaxHighlightedCommand = __esm(async () => {
98479
98757
  };
98480
98758
  HEREDOC_RE = /<<-?\s*['"]?(\w+)['"]?\s*$/;
98481
98759
  REDIRECT_FILE_RE = />>?\s+(\S+)/;
98482
- SyntaxHighlightedCommand = import_react34.memo(({ command, showPrompt = true, prefix, suffix }) => {
98760
+ SyntaxHighlightedCommand = import_react34.memo(({
98761
+ command,
98762
+ showPrompt = true,
98763
+ prefix,
98764
+ suffix,
98765
+ maxLines,
98766
+ maxColumns,
98767
+ showTruncationHint = false
98768
+ }) => {
98483
98769
  const palette = colors.shellSyntax;
98484
- const lines = highlightCommand(command, palette);
98770
+ const highlightedLines = highlightCommand(command, palette);
98771
+ const hasLineCap = typeof maxLines === "number" && maxLines >= 0;
98772
+ const visibleLines = hasLineCap ? highlightedLines.slice(0, maxLines) : highlightedLines;
98773
+ const hiddenLineCount = Math.max(0, highlightedLines.length - visibleLines.length);
98774
+ const renderedLines = [];
98775
+ let anyColumnClipping = false;
98776
+ for (let i = 0;i < visibleLines.length; i++) {
98777
+ const spans = visibleLines[i] ?? [];
98778
+ if (typeof maxColumns === "number") {
98779
+ const prefixLen = i === 0 && prefix ? prefix.length : 0;
98780
+ const suffixLen = i === visibleLines.length - 1 && suffix ? suffix.length : 0;
98781
+ const textBudget = Math.max(0, maxColumns - prefixLen - suffixLen);
98782
+ const clipped = clipStyledSpans(spans, textBudget);
98783
+ renderedLines.push(clipped.spans);
98784
+ anyColumnClipping = anyColumnClipping || clipped.clipped;
98785
+ } else {
98786
+ renderedLines.push(spans);
98787
+ }
98788
+ }
98485
98789
  return /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
98486
98790
  flexDirection: "column",
98487
- children: lines.map((spans, lineIdx) => {
98488
- const lineKey = spans.map((s) => s.text).join("");
98489
- return /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
98490
- children: [
98491
- showPrompt ? /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text2, {
98492
- color: palette.prompt,
98493
- children: lineIdx === 0 ? FIRST_LINE_PREFIX : " "
98494
- }, undefined, false, undefined, this) : null,
98495
- /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text2, {
98496
- color: palette.text,
98497
- children: [
98498
- lineIdx === 0 && prefix ? prefix : null,
98499
- spans.map((span, si) => /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text2, {
98500
- color: span.color,
98501
- children: span.text
98502
- }, `${si}:${span.color}`, false, undefined, this)),
98503
- lineIdx === lines.length - 1 && suffix ? suffix : null
98504
- ]
98505
- }, undefined, true, undefined, this)
98506
- ]
98507
- }, `${lineIdx}:${lineKey}`, true, undefined, this);
98508
- })
98509
- }, undefined, false, undefined, this);
98791
+ children: [
98792
+ renderedLines.map((spans, lineIdx) => {
98793
+ const lineKey = spans.map((s) => s.text).join("");
98794
+ return /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
98795
+ children: [
98796
+ showPrompt ? /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text2, {
98797
+ color: palette.prompt,
98798
+ children: lineIdx === 0 ? FIRST_LINE_PREFIX : " "
98799
+ }, undefined, false, undefined, this) : null,
98800
+ /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text2, {
98801
+ color: palette.text,
98802
+ children: [
98803
+ lineIdx === 0 && prefix ? prefix : null,
98804
+ spans.map((span, si) => /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text2, {
98805
+ color: span.color,
98806
+ children: span.text
98807
+ }, `${si}:${span.color}`, false, undefined, this)),
98808
+ lineIdx === renderedLines.length - 1 && suffix ? suffix : null
98809
+ ]
98810
+ }, undefined, true, undefined, this)
98811
+ ]
98812
+ }, `${lineIdx}:${lineKey}`, true, undefined, this);
98813
+ }),
98814
+ showTruncationHint && hiddenLineCount > 0 && /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text2, {
98815
+ dimColor: true,
98816
+ children: `… +${hiddenLineCount} more lines`
98817
+ }, undefined, false, undefined, this),
98818
+ showTruncationHint && hiddenLineCount === 0 && anyColumnClipping && /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text2, {
98819
+ dimColor: true,
98820
+ children: "… output clipped"
98821
+ }, undefined, false, undefined, this)
98822
+ ]
98823
+ }, undefined, true, undefined, this);
98510
98824
  });
98511
98825
  SyntaxHighlightedCommand.displayName = "SyntaxHighlightedCommand";
98512
98826
  });
@@ -99263,7 +99577,7 @@ var init_AdvancedDiffRenderer = __esm(async () => {
99263
99577
  });
99264
99578
 
99265
99579
  // src/cli/components/previews/BashPreview.tsx
99266
- var import_react36, jsx_dev_runtime15, SOLID_LINE3 = "─", BashPreview;
99580
+ var import_react36, jsx_dev_runtime15, SOLID_LINE3 = "─", BASH_PREVIEW_MAX_LINES = 3, BashPreview;
99267
99581
  var init_BashPreview = __esm(async () => {
99268
99582
  init_useTerminalWidth();
99269
99583
  init_colors();
@@ -99296,7 +99610,10 @@ var init_BashPreview = __esm(async () => {
99296
99610
  flexDirection: "column",
99297
99611
  children: [
99298
99612
  /* @__PURE__ */ jsx_dev_runtime15.jsxDEV(SyntaxHighlightedCommand, {
99299
- command
99613
+ command,
99614
+ maxLines: BASH_PREVIEW_MAX_LINES,
99615
+ maxColumns: Math.max(10, columns - 2),
99616
+ showTruncationHint: true
99300
99617
  }, undefined, false, undefined, this),
99301
99618
  description && /* @__PURE__ */ jsx_dev_runtime15.jsxDEV(Text2, {
99302
99619
  dimColor: true,
@@ -99698,7 +100015,7 @@ var init_useTextInputCursor = __esm(() => {
99698
100015
  });
99699
100016
 
99700
100017
  // src/cli/components/InlineBashApproval.tsx
99701
- var import_react41, jsx_dev_runtime18, SOLID_LINE6 = "─", InlineBashApproval;
100018
+ var import_react41, jsx_dev_runtime18, SOLID_LINE6 = "─", BASH_PREVIEW_MAX_LINES2 = 3, InlineBashApproval;
99702
100019
  var init_InlineBashApproval = __esm(async () => {
99703
100020
  init_useProgressIndicator();
99704
100021
  init_useTerminalWidth();
@@ -99810,7 +100127,10 @@ var init_InlineBashApproval = __esm(async () => {
99810
100127
  flexDirection: "column",
99811
100128
  children: [
99812
100129
  /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(SyntaxHighlightedCommand, {
99813
- command: bashInfo.command
100130
+ command: bashInfo.command,
100131
+ maxLines: BASH_PREVIEW_MAX_LINES2,
100132
+ maxColumns: Math.max(10, columns - 2),
100133
+ showTruncationHint: true
99814
100134
  }, undefined, false, undefined, this),
99815
100135
  bashInfo.description && /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
99816
100136
  marginTop: 1,
@@ -99822,7 +100142,7 @@ var init_InlineBashApproval = __esm(async () => {
99822
100142
  ]
99823
100143
  }, undefined, true, undefined, this)
99824
100144
  ]
99825
- }, undefined, true, undefined, this), [bashInfo.command, bashInfo.description, solidLine]);
100145
+ }, undefined, true, undefined, this), [bashInfo.command, bashInfo.description, solidLine, columns]);
99826
100146
  const hintText = isOnCustomOption ? customReason ? "Enter to submit · Esc to clear" : "Type reason · Esc to cancel" : "Enter to select · Esc to cancel";
99827
100147
  const optionsMarginTop = showPreview ? 1 : 0;
99828
100148
  return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
@@ -101796,6 +102116,7 @@ var init_StaticPlanApproval = __esm(async () => {
101796
102116
  onApproveAndAcceptEdits,
101797
102117
  onKeepPlanning,
101798
102118
  onCancel,
102119
+ showAcceptEditsOption = true,
101799
102120
  isFocused = true,
101800
102121
  planContent,
101801
102122
  planFilePath,
@@ -101823,9 +102144,10 @@ var init_StaticPlanApproval = __esm(async () => {
101823
102144
  setTimeout(() => setBrowserStatus(""), 5000);
101824
102145
  });
101825
102146
  }, [planContent, planFilePath, agentName]);
101826
- const customOptionIndex = 2;
102147
+ const customOptionIndex = showAcceptEditsOption ? 2 : 1;
101827
102148
  const maxOptionIndex = customOptionIndex;
101828
- const isOnCustomOption = selectedOption === customOptionIndex;
102149
+ const effectiveSelectedOption = Math.min(selectedOption, maxOptionIndex);
102150
+ const isOnCustomOption = effectiveSelectedOption === customOptionIndex;
101829
102151
  const customOptionPlaceholder = "Type here to tell Letta Code what to change";
101830
102152
  use_input_default((input, key) => {
101831
102153
  if (!isFocused)
@@ -101865,9 +102187,9 @@ var init_StaticPlanApproval = __esm(async () => {
101865
102187
  return;
101866
102188
  }
101867
102189
  if (key.return) {
101868
- if (selectedOption === 0) {
102190
+ if (showAcceptEditsOption && effectiveSelectedOption === 0) {
101869
102191
  onApproveAndAcceptEdits();
101870
- } else if (selectedOption === 1) {
102192
+ } else {
101871
102193
  onApprove();
101872
102194
  }
101873
102195
  return;
@@ -101877,10 +102199,14 @@ var init_StaticPlanApproval = __esm(async () => {
101877
102199
  return;
101878
102200
  }
101879
102201
  if (input === "1") {
101880
- onApproveAndAcceptEdits();
102202
+ if (showAcceptEditsOption) {
102203
+ onApproveAndAcceptEdits();
102204
+ } else {
102205
+ onApprove();
102206
+ }
101881
102207
  return;
101882
102208
  }
101883
- if (input === "2") {
102209
+ if (showAcceptEditsOption && input === "2") {
101884
102210
  onApprove();
101885
102211
  return;
101886
102212
  }
@@ -101906,9 +102232,9 @@ var init_StaticPlanApproval = __esm(async () => {
101906
102232
  width: 5,
101907
102233
  flexShrink: 0,
101908
102234
  children: /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(Text2, {
101909
- color: selectedOption === 0 ? colors.approval.header : undefined,
102235
+ color: effectiveSelectedOption === 0 ? colors.approval.header : undefined,
101910
102236
  children: [
101911
- selectedOption === 0 ? "❯" : " ",
102237
+ effectiveSelectedOption === 0 ? "❯" : " ",
101912
102238
  " 1."
101913
102239
  ]
101914
102240
  }, undefined, true, undefined, this)
@@ -101918,22 +102244,22 @@ var init_StaticPlanApproval = __esm(async () => {
101918
102244
  width: Math.max(0, columns - 5),
101919
102245
  children: /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(Text2, {
101920
102246
  wrap: "wrap",
101921
- color: selectedOption === 0 ? colors.approval.header : undefined,
101922
- children: "Yes, and auto-accept edits"
102247
+ color: effectiveSelectedOption === 0 ? colors.approval.header : undefined,
102248
+ children: showAcceptEditsOption ? "Yes, and auto-accept edits" : "Yes, proceed (bypassPermissions / yolo mode)"
101923
102249
  }, undefined, false, undefined, this)
101924
102250
  }, undefined, false, undefined, this)
101925
102251
  ]
101926
102252
  }, undefined, true, undefined, this),
101927
- /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(Box_default, {
102253
+ showAcceptEditsOption && /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(Box_default, {
101928
102254
  flexDirection: "row",
101929
102255
  children: [
101930
102256
  /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(Box_default, {
101931
102257
  width: 5,
101932
102258
  flexShrink: 0,
101933
102259
  children: /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(Text2, {
101934
- color: selectedOption === 1 ? colors.approval.header : undefined,
102260
+ color: effectiveSelectedOption === 1 ? colors.approval.header : undefined,
101935
102261
  children: [
101936
- selectedOption === 1 ? "❯" : " ",
102262
+ effectiveSelectedOption === 1 ? "❯" : " ",
101937
102263
  " 2."
101938
102264
  ]
101939
102265
  }, undefined, true, undefined, this)
@@ -101943,7 +102269,7 @@ var init_StaticPlanApproval = __esm(async () => {
101943
102269
  width: Math.max(0, columns - 5),
101944
102270
  children: /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(Text2, {
101945
102271
  wrap: "wrap",
101946
- color: selectedOption === 1 ? colors.approval.header : undefined,
102272
+ color: effectiveSelectedOption === 1 ? colors.approval.header : undefined,
101947
102273
  children: "Yes, and manually approve edits"
101948
102274
  }, undefined, false, undefined, this)
101949
102275
  }, undefined, false, undefined, this)
@@ -101959,7 +102285,9 @@ var init_StaticPlanApproval = __esm(async () => {
101959
102285
  color: isOnCustomOption ? colors.approval.header : undefined,
101960
102286
  children: [
101961
102287
  isOnCustomOption ? "❯" : " ",
101962
- " 3."
102288
+ " ",
102289
+ customOptionIndex + 1,
102290
+ "."
101963
102291
  ]
101964
102292
  }, undefined, true, undefined, this)
101965
102293
  }, undefined, false, undefined, this),
@@ -102075,6 +102403,7 @@ function getQuestions(approval) {
102075
102403
  }
102076
102404
  var import_react48, jsx_dev_runtime25, ApprovalSwitch;
102077
102405
  var init_ApprovalSwitch = __esm(async () => {
102406
+ init_mode();
102078
102407
  await __promiseAll([
102079
102408
  init_toolNameMapping(),
102080
102409
  init_InlineBashApproval(),
@@ -102111,11 +102440,13 @@ var init_ApprovalSwitch = __esm(async () => {
102111
102440
  }) => {
102112
102441
  const toolName = approval.toolName;
102113
102442
  if (toolName === "ExitPlanMode" && onPlanApprove && onPlanKeepPlanning) {
102443
+ const showAcceptEditsOption = permissionMode.getMode() === "plan" && permissionMode.getModeBeforePlan() !== "bypassPermissions";
102114
102444
  return /* @__PURE__ */ jsx_dev_runtime25.jsxDEV(StaticPlanApproval, {
102115
102445
  onApprove: () => onPlanApprove(false),
102116
102446
  onApproveAndAcceptEdits: () => onPlanApprove(true),
102117
102447
  onKeepPlanning: onPlanKeepPlanning,
102118
102448
  onCancel: onCancel ?? (() => {}),
102449
+ showAcceptEditsOption,
102119
102450
  isFocused,
102120
102451
  planContent,
102121
102452
  planFilePath,
@@ -102435,6 +102766,7 @@ var init_CollapsedOutputDisplay = __esm(async () => {
102435
102766
  // src/cli/components/StreamingOutputDisplay.tsx
102436
102767
  var import_react53, jsx_dev_runtime30, StreamingOutputDisplay;
102437
102768
  var init_StreamingOutputDisplay = __esm(async () => {
102769
+ init_useTerminalWidth();
102438
102770
  await __promiseAll([
102439
102771
  init_build2(),
102440
102772
  init_Text2()
@@ -102442,6 +102774,7 @@ var init_StreamingOutputDisplay = __esm(async () => {
102442
102774
  import_react53 = __toESM(require_react(), 1);
102443
102775
  jsx_dev_runtime30 = __toESM(require_jsx_dev_runtime(), 1);
102444
102776
  StreamingOutputDisplay = import_react53.memo(({ streaming, showInterruptHint }) => {
102777
+ const columns = useTerminalWidth();
102445
102778
  const [, forceUpdate] = import_react53.useState(0);
102446
102779
  import_react53.useEffect(() => {
102447
102780
  const interval = setInterval(() => forceUpdate((n) => n + 1), 1000);
@@ -102450,6 +102783,16 @@ var init_StreamingOutputDisplay = __esm(async () => {
102450
102783
  const elapsed = Math.floor((Date.now() - streaming.startTime) / 1000);
102451
102784
  const { tailLines, totalLineCount } = streaming;
102452
102785
  const hiddenCount = Math.max(0, totalLineCount - tailLines.length);
102786
+ const contentWidth = Math.max(10, columns - 5);
102787
+ const clipToWidth = (text) => {
102788
+ if (text.length <= contentWidth) {
102789
+ return text;
102790
+ }
102791
+ if (contentWidth <= 1) {
102792
+ return "…";
102793
+ }
102794
+ return `${text.slice(0, contentWidth - 1)}…`;
102795
+ };
102453
102796
  const firstLine = tailLines[0];
102454
102797
  const interruptHint = showInterruptHint ? " (esc to interrupt)" : "";
102455
102798
  if (!firstLine) {
@@ -102472,7 +102815,7 @@ var init_StreamingOutputDisplay = __esm(async () => {
102472
102815
  /* @__PURE__ */ jsx_dev_runtime30.jsxDEV(Text2, {
102473
102816
  dimColor: !firstLine.isStderr,
102474
102817
  color: firstLine.isStderr ? "red" : undefined,
102475
- children: firstLine.text
102818
+ children: clipToWidth(firstLine.text)
102476
102819
  }, undefined, false, undefined, this)
102477
102820
  ]
102478
102821
  }, undefined, true, undefined, this),
@@ -102481,7 +102824,7 @@ var init_StreamingOutputDisplay = __esm(async () => {
102481
102824
  color: line.isStderr ? "red" : undefined,
102482
102825
  children: [
102483
102826
  " ",
102484
- line.text
102827
+ clipToWidth(line.text)
102485
102828
  ]
102486
102829
  }, i, true, undefined, this)),
102487
102830
  hiddenCount > 0 && /* @__PURE__ */ jsx_dev_runtime30.jsxDEV(Text2, {
@@ -104460,7 +104803,7 @@ import {
104460
104803
  readFileSync as readFileSync12,
104461
104804
  writeFileSync as writeFileSync13
104462
104805
  } from "node:fs";
104463
- import { homedir as homedir28, platform as platform4 } from "node:os";
104806
+ import { homedir as homedir28, platform as platform5 } from "node:os";
104464
104807
  import { dirname as dirname13, join as join34 } from "node:path";
104465
104808
  function detectTerminalType() {
104466
104809
  if (process.env.CURSOR_TRACE_ID || process.env.CURSOR_CHANNEL) {
@@ -104491,17 +104834,17 @@ function getKeybindingsPath(terminal) {
104491
104834
  cursor: "Cursor",
104492
104835
  windsurf: "Windsurf"
104493
104836
  }[terminal];
104494
- const os6 = platform4();
104495
- if (os6 === "darwin") {
104837
+ const os7 = platform5();
104838
+ if (os7 === "darwin") {
104496
104839
  return join34(homedir28(), "Library", "Application Support", appName, "User", "keybindings.json");
104497
104840
  }
104498
- if (os6 === "win32") {
104841
+ if (os7 === "win32") {
104499
104842
  const appData = process.env.APPDATA;
104500
104843
  if (!appData)
104501
104844
  return null;
104502
104845
  return join34(appData, appName, "User", "keybindings.json");
104503
104846
  }
104504
- if (os6 === "linux") {
104847
+ if (os7 === "linux") {
104505
104848
  return join34(homedir28(), ".config", appName, "User", "keybindings.json");
104506
104849
  }
104507
104850
  return null;
@@ -107569,7 +107912,7 @@ var require_jsx_runtime = __commonJS((exports, module) => {
107569
107912
 
107570
107913
  // node_modules/supports-color/index.js
107571
107914
  import process19 from "node:process";
107572
- import os6 from "node:os";
107915
+ import os7 from "node:os";
107573
107916
  import tty2 from "node:tty";
107574
107917
  function hasFlag2(flag, argv = globalThis.Deno ? globalThis.Deno.args : process19.argv) {
107575
107918
  const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
@@ -107635,7 +107978,7 @@ function _supportsColor2(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
107635
107978
  return min;
107636
107979
  }
107637
107980
  if (process19.platform === "win32") {
107638
- const osRelease = os6.release().split(".");
107981
+ const osRelease = os7.release().split(".");
107639
107982
  if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
107640
107983
  return Number(osRelease[2]) >= 14931 ? 3 : 2;
107641
107984
  }
@@ -121055,7 +121398,7 @@ function colorizeArgs(argsStr) {
121055
121398
  children: parts
121056
121399
  }, undefined, false, undefined, this);
121057
121400
  }
121058
- var import_react94, jsx_dev_runtime74, ToolCallMessage;
121401
+ var import_react94, jsx_dev_runtime74, LIVE_SHELL_ARGS_MAX_LINES = 2, ToolCallMessage;
121059
121402
  var init_ToolCallMessageRich = __esm(async () => {
121060
121403
  init_constants();
121061
121404
  init_subagentState();
@@ -121824,7 +122167,9 @@ var init_ToolCallMessageRich = __esm(async () => {
121824
122167
  command: shellCommand,
121825
122168
  showPrompt: false,
121826
122169
  prefix: "(",
121827
- suffix: ")"
122170
+ suffix: ")",
122171
+ maxLines: LIVE_SHELL_ARGS_MAX_LINES,
122172
+ maxColumns: Math.max(10, rightWidth - displayName.length)
121828
122173
  }, undefined, false, undefined, this)
121829
122174
  }, undefined, false, undefined, this) : args ? /* @__PURE__ */ jsx_dev_runtime74.jsxDEV(Box_default, {
121830
122175
  flexGrow: 1,
@@ -124263,6 +124608,18 @@ function formatApiKeyUsage(provider) {
124263
124608
  ].join(`
124264
124609
  `);
124265
124610
  }
124611
+ function formatZaiCodingPlanPrompt(apiKey) {
124612
+ const keyHint = apiKey ? ` ${apiKey}` : " <api_key>";
124613
+ return [
124614
+ "Connect to Z.ai",
124615
+ "",
124616
+ "Do you have a Z.ai Coding plan?",
124617
+ "",
124618
+ ` • Coding plan: /connect zai-coding${keyHint}`,
124619
+ ` • Regular API: /connect zai${keyHint}`
124620
+ ].join(`
124621
+ `);
124622
+ }
124266
124623
  async function handleConnectChatGPT(ctx, msg) {
124267
124624
  const existingProvider = await isChatGPTOAuthConnected();
124268
124625
  if (existingProvider) {
@@ -124371,7 +124728,11 @@ async function handleConnect(ctx, msg) {
124371
124728
  if (isConnectApiKeyProvider(provider)) {
124372
124729
  const apiKey = parts.slice(2).join("");
124373
124730
  if (!apiKey) {
124374
- addCommandResult3(ctx.buffersRef, ctx.refreshDerived, msg, formatApiKeyUsage(provider), false);
124731
+ if (isConnectZaiBaseProvider(provider)) {
124732
+ addCommandResult3(ctx.buffersRef, ctx.refreshDerived, msg, formatZaiCodingPlanPrompt(), false);
124733
+ } else {
124734
+ addCommandResult3(ctx.buffersRef, ctx.refreshDerived, msg, formatApiKeyUsage(provider), false);
124735
+ }
124375
124736
  return;
124376
124737
  }
124377
124738
  await handleConnectApiKeyProvider(ctx, msg, provider, apiKey);
@@ -126130,7 +126491,7 @@ function App2({
126130
126491
  description = typeof args.description === "string" ? args.description : typeof args.justification === "string" ? args.justification : "";
126131
126492
  }
126132
126493
  let lines2 = 3;
126133
- lines2 += countWrappedLines(command, wrapWidth);
126494
+ lines2 += Math.min(countWrappedLines(command, wrapWidth), SHELL_PREVIEW_MAX_LINES);
126134
126495
  if (description) {
126135
126496
  lines2 += countWrappedLines(description, wrapWidth);
126136
126497
  }
@@ -130773,10 +131134,12 @@ ${SYSTEM_REMINDER_CLOSE}
130773
131134
  refreshDerived();
130774
131135
  const recoveryApprovalResults = [
130775
131136
  ...autoAllowedResults.map((ar) => ({
130776
- type: "approval",
131137
+ type: "tool",
130777
131138
  tool_call_id: ar.toolCallId,
130778
- approve: true,
130779
- tool_return: ar.result.toolReturn
131139
+ tool_return: ar.result.toolReturn,
131140
+ status: ar.result.status,
131141
+ stdout: ar.result.stdout,
131142
+ stderr: ar.result.stderr
130780
131143
  })),
130781
131144
  ...autoDeniedResults
130782
131145
  ];
@@ -132233,7 +132596,8 @@ ${guidance}`);
132233
132596
  }
132234
132597
  const currentMode = permissionMode.getMode();
132235
132598
  if (currentMode === "plan") {
132236
- const restoreMode = acceptEdits ? "acceptEdits" : permissionMode.getModeBeforePlan() ?? "default";
132599
+ const previousMode = permissionMode.getModeBeforePlan();
132600
+ const restoreMode = previousMode === "bypassPermissions" ? "bypassPermissions" : acceptEdits ? "acceptEdits" : previousMode ?? "default";
132237
132601
  permissionMode.setMode(restoreMode);
132238
132602
  setUiPermissionMode(restoreMode);
132239
132603
  } else {
@@ -132314,12 +132678,10 @@ ${guidance}`);
132314
132678
  const fallbackPlanPath = lastPlanFilePathRef.current;
132315
132679
  const hasUsablePlan = planFileExists(fallbackPlanPath);
132316
132680
  if (mode !== "plan") {
132681
+ if (hasUsablePlan) {
132682
+ return;
132683
+ }
132317
132684
  if (mode === "bypassPermissions") {
132318
- if (hasUsablePlan) {
132319
- lastAutoHandledExitPlanToolCallIdRef.current = approval.toolCallId;
132320
- handlePlanApprove();
132321
- return;
132322
- }
132323
132685
  const planFilePath = activePlanPath ?? fallbackPlanPath;
132324
132686
  const plansDir = join44(homedir34(), ".letta", "plans");
132325
132687
  handlePlanKeepPlanning(`You must write your plan to a plan file before exiting plan mode.
@@ -132327,9 +132689,6 @@ ${guidance}`);
132327
132689
  ` : "") + `Use a write tool to create your plan in ${plansDir}, then use ExitPlanMode to present the plan to the user.`);
132328
132690
  return;
132329
132691
  }
132330
- if (hasUsablePlan) {
132331
- return;
132332
- }
132333
132692
  const statusId = uid5("status");
132334
132693
  buffersRef.current.byId.set(statusId, {
132335
132694
  kind: "status",
@@ -132368,7 +132727,6 @@ ${guidance}`);
132368
132727
  }, [
132369
132728
  pendingApprovals,
132370
132729
  approvalResults.length,
132371
- handlePlanApprove,
132372
132730
  handlePlanKeepPlanning,
132373
132731
  refreshDerived,
132374
132732
  queueApprovalResults
@@ -133561,7 +133919,7 @@ Open /mcp to attach or detach tools for this server.`, true);
133561
133919
  ]
133562
133920
  }, resumeKey, true, undefined, this);
133563
133921
  }
133564
- var import_react101, jsx_dev_runtime78, CLEAR_SCREEN_AND_HOME = "\x1B[2J\x1B[H", MIN_RESIZE_DELTA = 2, RESIZE_SETTLE_MS = 250, MIN_CLEAR_INTERVAL_MS = 750, STABLE_WIDTH_SETTLE_MS = 180, TOOL_CALL_COMMIT_DEFER_MS = 50, ANIMATION_RESUME_HYSTERESIS_ROWS = 2, EAGER_CANCEL = true, LLM_API_ERROR_MAX_RETRIES3 = 3, EMPTY_RESPONSE_MAX_RETRIES3 = 2, CONVERSATION_BUSY_MAX_RETRIES2 = 3, INTERRUPT_MESSAGE = "Interrupted – tell the agent what to do differently. Something went wrong? Use /feedback to report issues.", ERROR_FEEDBACK_HINT = "Something went wrong? Use /feedback to report issues.", OPUS_BEDROCK_FALLBACK_HINT = "Downstream provider issues? Use /model to switch to Bedrock Opus 4.5", PROVIDER_FALLBACK_HINT = "Downstream provider issues? Use /model to switch to another provider", INTERACTIVE_SLASH_COMMANDS, NON_STATE_COMMANDS, APPROVAL_OPTIONS_HEIGHT = 8, APPROVAL_PREVIEW_BUFFER = 4, MIN_WRAP_WIDTH = 10, TEXT_WRAP_GUTTER = 6, DIFF_WRAP_GUTTER = 12, AUTO_REFLECTION_DESCRIPTION = "Reflect on recent conversations";
133922
+ var import_react101, jsx_dev_runtime78, CLEAR_SCREEN_AND_HOME = "\x1B[2J\x1B[H", MIN_RESIZE_DELTA = 2, RESIZE_SETTLE_MS = 250, MIN_CLEAR_INTERVAL_MS = 750, STABLE_WIDTH_SETTLE_MS = 180, TOOL_CALL_COMMIT_DEFER_MS = 50, ANIMATION_RESUME_HYSTERESIS_ROWS = 2, EAGER_CANCEL = true, LLM_API_ERROR_MAX_RETRIES3 = 3, EMPTY_RESPONSE_MAX_RETRIES3 = 2, CONVERSATION_BUSY_MAX_RETRIES2 = 3, INTERRUPT_MESSAGE = "Interrupted – tell the agent what to do differently. Something went wrong? Use /feedback to report issues.", ERROR_FEEDBACK_HINT = "Something went wrong? Use /feedback to report issues.", OPUS_BEDROCK_FALLBACK_HINT = "Downstream provider issues? Use /model to switch to Bedrock Opus 4.5", PROVIDER_FALLBACK_HINT = "Downstream provider issues? Use /model to switch to another provider", INTERACTIVE_SLASH_COMMANDS, NON_STATE_COMMANDS, APPROVAL_OPTIONS_HEIGHT = 8, APPROVAL_PREVIEW_BUFFER = 4, MIN_WRAP_WIDTH = 10, TEXT_WRAP_GUTTER = 6, DIFF_WRAP_GUTTER = 12, SHELL_PREVIEW_MAX_LINES = 3, AUTO_REFLECTION_DESCRIPTION = "Reflect on recent conversations";
133565
133923
  var init_App2 = __esm(async () => {
133566
133924
  init_error();
133567
133925
  init_check_approval();
@@ -133736,7 +134094,7 @@ import {
133736
134094
  readFileSync as readFileSync18,
133737
134095
  writeFileSync as writeFileSync17
133738
134096
  } from "node:fs";
133739
- import { homedir as homedir35, platform as platform5 } from "node:os";
134097
+ import { homedir as homedir35, platform as platform6 } from "node:os";
133740
134098
  import { dirname as dirname16, join as join45 } from "node:path";
133741
134099
  function detectTerminalType2() {
133742
134100
  if (process.env.CURSOR_TRACE_ID || process.env.CURSOR_CHANNEL) {
@@ -133767,17 +134125,17 @@ function getKeybindingsPath2(terminal) {
133767
134125
  cursor: "Cursor",
133768
134126
  windsurf: "Windsurf"
133769
134127
  }[terminal];
133770
- const os7 = platform5();
133771
- if (os7 === "darwin") {
134128
+ const os8 = platform6();
134129
+ if (os8 === "darwin") {
133772
134130
  return join45(homedir35(), "Library", "Application Support", appName, "User", "keybindings.json");
133773
134131
  }
133774
- if (os7 === "win32") {
134132
+ if (os8 === "win32") {
133775
134133
  const appData = process.env.APPDATA;
133776
134134
  if (!appData)
133777
134135
  return null;
133778
134136
  return join45(appData, appName, "User", "keybindings.json");
133779
134137
  }
133780
- if (os7 === "linux") {
134138
+ if (os8 === "linux") {
133781
134139
  return join45(homedir35(), ".config", appName, "User", "keybindings.json");
133782
134140
  }
133783
134141
  return null;
@@ -138343,6 +138701,12 @@ async function runConnectSubcommand(argv, deps = {}) {
138343
138701
  }
138344
138702
  if (isConnectApiKeyProvider(provider)) {
138345
138703
  let apiKey = readStringOption(parsed.values["api-key"]) ?? restPositionals[0] ?? "";
138704
+ if (!apiKey && isConnectZaiBaseProvider(provider)) {
138705
+ io.stdout(`Do you have a Z.ai Coding plan?
138706
+ ` + ` • Coding plan: letta connect zai-coding [--api-key <key>]
138707
+ ` + " • Regular API: letta connect zai [--api-key <key>]");
138708
+ return 0;
138709
+ }
138346
138710
  if (!apiKey) {
138347
138711
  if (!io.isTTY()) {
138348
138712
  io.stderr(`Missing API key for ${provider.canonical}. Pass as positional arg or --api-key.`);
@@ -139402,6 +139766,9 @@ class PermissionModeManager2 {
139402
139766
  const _effectivePlanFilePath = planFilePathOverride !== undefined ? planFilePathOverride : this.getPlanFilePath();
139403
139767
  switch (effectiveMode) {
139404
139768
  case "bypassPermissions":
139769
+ if (toolName === "ExitPlanMode" || toolName === "exit_plan_mode") {
139770
+ return null;
139771
+ }
139405
139772
  return "allow";
139406
139773
  case "acceptEdits":
139407
139774
  if ([
@@ -142605,4 +142972,4 @@ Error during initialization: ${message}`);
142605
142972
  }
142606
142973
  main();
142607
142974
 
142608
- //# debugId=271668920D12255A64756E2164756E21
142975
+ //# debugId=9B4D5740B8FA924E64756E2164756E21