@letta-ai/letta-code 0.19.2 → 0.19.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/letta.js CHANGED
@@ -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.2",
3243
+ version: "0.19.4",
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: {
@@ -3313,7 +3313,7 @@ var init_package = __esm(() => {
3313
3313
  "test:update-chain:manual": "bun run src/tests/update-chain-smoke.ts --mode manual",
3314
3314
  "test:update-chain:startup": "bun run src/tests/update-chain-smoke.ts --mode startup",
3315
3315
  prepublishOnly: "bun run build",
3316
- postinstall: "node scripts/postinstall-patches.js || echo letta: vendor patches skipped"
3316
+ postinstall: `node scripts/postinstall-patches.js || echo letta: vendor patches skipped && node -e "try{require('fs').chmodSync(require('path').join(require.resolve('node-pty/package.json'),'../prebuilds/darwin-arm64/spawn-helper'),0o755)}catch(e){}" || true`
3317
3317
  },
3318
3318
  "lint-staged": {
3319
3319
  "*.{ts,tsx,js,jsx,json}": [
@@ -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
  ---
@@ -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
@@ -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
  }
@@ -68015,6 +68043,7 @@ var init_manager3 = __esm(async () => {
68015
68043
  "Glob",
68016
68044
  "Grep",
68017
68045
  "TaskStop",
68046
+ "memory",
68018
68047
  "Read",
68019
68048
  "Skill",
68020
68049
  "Task",
@@ -68024,6 +68053,7 @@ var init_manager3 = __esm(async () => {
68024
68053
  OPENAI_DEFAULT_TOOLS = [
68025
68054
  "shell_command",
68026
68055
  "apply_patch",
68056
+ "memory",
68027
68057
  "update_plan",
68028
68058
  "view_image"
68029
68059
  ];
@@ -68033,6 +68063,7 @@ var init_manager3 = __esm(async () => {
68033
68063
  "list_directory",
68034
68064
  "glob_gemini",
68035
68065
  "search_file_content",
68066
+ "memory",
68036
68067
  "replace",
68037
68068
  "write_file_gemini",
68038
68069
  "write_todos",
@@ -68044,6 +68075,7 @@ var init_manager3 = __esm(async () => {
68044
68075
  "AskUserQuestion",
68045
68076
  "EnterPlanMode",
68046
68077
  "ExitPlanMode",
68078
+ "memory",
68047
68079
  "Task",
68048
68080
  "TaskOutput",
68049
68081
  "TaskStop",
@@ -68057,6 +68089,7 @@ var init_manager3 = __esm(async () => {
68057
68089
  "AskUserQuestion",
68058
68090
  "EnterPlanMode",
68059
68091
  "ExitPlanMode",
68092
+ "memory",
68060
68093
  "Skill",
68061
68094
  "Task",
68062
68095
  "RunShellCommand",
@@ -68147,82 +68180,157 @@ function sendTerminalMessage(socket, message) {
68147
68180
  socket.send(JSON.stringify(message));
68148
68181
  }
68149
68182
  }
68183
+ function makeOutputBatcher(onFlush) {
68184
+ let buffer = "";
68185
+ let timer = null;
68186
+ const flush = () => {
68187
+ if (timer) {
68188
+ clearTimeout(timer);
68189
+ timer = null;
68190
+ }
68191
+ if (buffer.length > 0) {
68192
+ onFlush(buffer);
68193
+ buffer = "";
68194
+ }
68195
+ };
68196
+ return (chunk) => {
68197
+ buffer += chunk;
68198
+ if (buffer.length >= MAX_BUFFER_BYTES) {
68199
+ flush();
68200
+ } else if (!timer) {
68201
+ timer = setTimeout(flush, FLUSH_INTERVAL_MS);
68202
+ }
68203
+ };
68204
+ }
68205
+ function spawnBun(shell2, cwd2, cols, rows, terminal_id, socket) {
68206
+ const handleData = makeOutputBatcher((data) => sendTerminalMessage(socket, { type: "terminal_output", terminal_id, data }));
68207
+ const proc2 = Bun.spawn([shell2], {
68208
+ cwd: cwd2,
68209
+ env: { ...process.env, TERM: "xterm-256color", COLORTERM: "truecolor" },
68210
+ terminal: {
68211
+ cols: cols || 80,
68212
+ rows: rows || 24,
68213
+ data: (_t, chunk) => handleData(new TextDecoder().decode(chunk))
68214
+ }
68215
+ });
68216
+ const terminal = proc2.terminal;
68217
+ if (!terminal) {
68218
+ throw new Error("Bun.spawn terminal object missing — API unavailable");
68219
+ }
68220
+ proc2.exited.then((exitCode) => {
68221
+ const current = terminals.get(terminal_id);
68222
+ if (current && current.pid === proc2.pid) {
68223
+ terminals.delete(terminal_id);
68224
+ sendTerminalMessage(socket, {
68225
+ type: "terminal_exited",
68226
+ terminal_id,
68227
+ exitCode: exitCode ?? 0
68228
+ });
68229
+ }
68230
+ });
68231
+ return {
68232
+ write: (d) => {
68233
+ try {
68234
+ terminal.write(d);
68235
+ } catch {}
68236
+ },
68237
+ resize: (c, r) => {
68238
+ try {
68239
+ terminal.resize(c, r);
68240
+ } catch {}
68241
+ },
68242
+ kill: () => {
68243
+ try {
68244
+ terminal.close();
68245
+ } catch {}
68246
+ try {
68247
+ proc2.kill();
68248
+ } catch {}
68249
+ },
68250
+ pid: proc2.pid,
68251
+ terminalId: terminal_id,
68252
+ spawnedAt: Date.now()
68253
+ };
68254
+ }
68255
+ function spawnNodePty(shell2, cwd2, cols, rows, terminal_id, socket) {
68256
+ const pty = __require("node-pty");
68257
+ const handleData = makeOutputBatcher((data) => sendTerminalMessage(socket, { type: "terminal_output", terminal_id, data }));
68258
+ const ptyProcess = pty.spawn(shell2, [], {
68259
+ name: "xterm-256color",
68260
+ cols: cols || 80,
68261
+ rows: rows || 24,
68262
+ cwd: cwd2,
68263
+ env: {
68264
+ ...process.env,
68265
+ TERM: "xterm-256color",
68266
+ COLORTERM: "truecolor"
68267
+ }
68268
+ });
68269
+ ptyProcess.onData(handleData);
68270
+ ptyProcess.onExit(({ exitCode }) => {
68271
+ const current = terminals.get(terminal_id);
68272
+ if (current && current.pid === ptyProcess.pid) {
68273
+ terminals.delete(terminal_id);
68274
+ sendTerminalMessage(socket, {
68275
+ type: "terminal_exited",
68276
+ terminal_id,
68277
+ exitCode: exitCode ?? 0
68278
+ });
68279
+ }
68280
+ });
68281
+ return {
68282
+ write: (d) => {
68283
+ try {
68284
+ ptyProcess.write(d);
68285
+ } catch {}
68286
+ },
68287
+ resize: (c, r) => {
68288
+ try {
68289
+ ptyProcess.resize(c, r);
68290
+ } catch {}
68291
+ },
68292
+ kill: () => {
68293
+ try {
68294
+ ptyProcess.kill();
68295
+ } catch {}
68296
+ },
68297
+ pid: ptyProcess.pid,
68298
+ terminalId: terminal_id,
68299
+ spawnedAt: Date.now()
68300
+ };
68301
+ }
68150
68302
  function handleTerminalSpawn(msg, socket, cwd2) {
68151
68303
  const { terminal_id, cols, rows } = msg;
68152
- killTerminal(terminal_id);
68153
- const shell2 = getDefaultShell();
68154
- console.log(`[Terminal] Spawning PTY: shell=${shell2}, cwd=${cwd2}, cols=${cols}, rows=${rows}`);
68155
- try {
68156
- const proc2 = Bun.spawn([shell2], {
68157
- cwd: cwd2,
68158
- env: {
68159
- ...process.env,
68160
- TERM: "xterm-256color",
68161
- COLORTERM: "truecolor"
68162
- },
68163
- terminal: {
68164
- cols: cols || 80,
68165
- rows: rows || 24,
68166
- data: (() => {
68167
- let buffer = "";
68168
- let flushTimer = null;
68169
- return (_terminal, data) => {
68170
- buffer += new TextDecoder().decode(data);
68171
- if (!flushTimer) {
68172
- flushTimer = setTimeout(() => {
68173
- if (buffer.length > 0) {
68174
- sendTerminalMessage(socket, {
68175
- type: "terminal_output",
68176
- terminal_id,
68177
- data: buffer
68178
- });
68179
- buffer = "";
68180
- }
68181
- flushTimer = null;
68182
- }, 16);
68183
- }
68184
- };
68185
- })()
68186
- }
68187
- });
68188
- const terminal = proc2.terminal;
68189
- console.log(`[Terminal] proc.pid=${proc2.pid}, terminal=${typeof terminal}, keys=${Object.keys(proc2).join(",")}`);
68190
- if (!terminal) {
68191
- console.error("[Terminal] terminal object is undefined on proc — Bun.Terminal API may not be available");
68304
+ const existing = terminals.get(terminal_id);
68305
+ if (existing && Date.now() - existing.spawnedAt < 2000) {
68306
+ let alive = true;
68307
+ try {
68308
+ existing.write("\r");
68309
+ } catch {
68310
+ alive = false;
68311
+ }
68312
+ if (alive) {
68313
+ console.log(`[Terminal] Reusing session (age=${Date.now() - existing.spawnedAt}ms), pid=${existing.pid}`);
68192
68314
  sendTerminalMessage(socket, {
68193
- type: "terminal_exited",
68315
+ type: "terminal_spawned",
68194
68316
  terminal_id,
68195
- exitCode: 1
68317
+ pid: existing.pid
68196
68318
  });
68197
68319
  return;
68198
68320
  }
68199
- const session = {
68200
- process: proc2,
68201
- terminal,
68202
- terminalId: terminal_id,
68203
- spawnedAt: Date.now()
68204
- };
68321
+ terminals.delete(terminal_id);
68322
+ }
68323
+ killTerminal(terminal_id);
68324
+ const shell2 = getDefaultShell();
68325
+ console.log(`[Terminal] Spawning PTY (${IS_BUN ? "bun" : "node-pty"}): shell=${shell2}, cwd=${cwd2}, cols=${cols}, rows=${rows}`);
68326
+ try {
68327
+ const session = IS_BUN ? spawnBun(shell2, cwd2, cols, rows, terminal_id, socket) : spawnNodePty(shell2, cwd2, cols, rows, terminal_id, socket);
68205
68328
  terminals.set(terminal_id, session);
68206
- console.log(`[Terminal] Session stored for terminal_id=${terminal_id}, map size=${terminals.size}`);
68207
- const myPid = proc2.pid;
68208
- proc2.exited.then((exitCode) => {
68209
- const current = terminals.get(terminal_id);
68210
- if (current && current.process.pid === myPid) {
68211
- console.log(`[Terminal] PTY process exited: terminal_id=${terminal_id}, pid=${myPid}, exitCode=${exitCode}`);
68212
- terminals.delete(terminal_id);
68213
- sendTerminalMessage(socket, {
68214
- type: "terminal_exited",
68215
- terminal_id,
68216
- exitCode: exitCode ?? 0
68217
- });
68218
- } else {
68219
- console.log(`[Terminal] Stale PTY exit ignored: terminal_id=${terminal_id}, pid=${myPid} (current pid=${current?.process.pid})`);
68220
- }
68221
- });
68329
+ console.log(`[Terminal] Session stored for terminal_id=${terminal_id}, pid=${session.pid}`);
68222
68330
  sendTerminalMessage(socket, {
68223
68331
  type: "terminal_spawned",
68224
68332
  terminal_id,
68225
- pid: proc2.pid
68333
+ pid: session.pid
68226
68334
  });
68227
68335
  } catch (error) {
68228
68336
  console.error("[Terminal] Failed to spawn PTY:", error);
@@ -68234,16 +68342,10 @@ function handleTerminalSpawn(msg, socket, cwd2) {
68234
68342
  }
68235
68343
  }
68236
68344
  function handleTerminalInput(msg) {
68237
- const session = terminals.get(msg.terminal_id);
68238
- if (session) {
68239
- session.terminal.write(msg.data);
68240
- }
68345
+ terminals.get(msg.terminal_id)?.write(msg.data);
68241
68346
  }
68242
68347
  function handleTerminalResize(msg) {
68243
- const session = terminals.get(msg.terminal_id);
68244
- if (session) {
68245
- session.terminal.resize(msg.cols, msg.rows);
68246
- }
68348
+ terminals.get(msg.terminal_id)?.resize(msg.cols, msg.rows);
68247
68349
  }
68248
68350
  function handleTerminalKill(msg) {
68249
68351
  const session = terminals.get(msg.terminal_id);
@@ -68256,11 +68358,8 @@ function handleTerminalKill(msg) {
68256
68358
  function killTerminal(terminalId) {
68257
68359
  const session = terminals.get(terminalId);
68258
68360
  if (session) {
68259
- console.log(`[Terminal] killTerminal: terminalId=${terminalId}, pid=${session.process.pid}`);
68260
- try {
68261
- session.terminal.close();
68262
- } catch {}
68263
- session.process.kill();
68361
+ console.log(`[Terminal] killTerminal: terminalId=${terminalId}, pid=${session.pid}`);
68362
+ session.kill();
68264
68363
  terminals.delete(terminalId);
68265
68364
  }
68266
68365
  }
@@ -68269,8 +68368,10 @@ function killAllTerminals() {
68269
68368
  killTerminal(id);
68270
68369
  }
68271
68370
  }
68272
- var terminals;
68371
+ var IS_BUN, FLUSH_INTERVAL_MS = 16, MAX_BUFFER_BYTES, terminals;
68273
68372
  var init_terminalHandler = __esm(() => {
68373
+ IS_BUN = typeof Bun !== "undefined";
68374
+ MAX_BUFFER_BYTES = 64 * 1024;
68274
68375
  terminals = new Map;
68275
68376
  });
68276
68377
 
@@ -68448,7 +68549,25 @@ function getPermissionModeScopeKey(agentId, conversationId) {
68448
68549
  }
68449
68550
  function getConversationPermissionModeState(runtime, agentId, conversationId) {
68450
68551
  const scopeKey = getPermissionModeScopeKey(agentId, conversationId);
68451
- return runtime.permissionModeByConversation.get(scopeKey) ?? {
68552
+ const normalizedConversationId = normalizeConversationId(conversationId);
68553
+ const direct = runtime.permissionModeByConversation.get(scopeKey);
68554
+ if (direct) {
68555
+ return direct;
68556
+ }
68557
+ if (normalizedConversationId === "default") {
68558
+ const legacyDefaultKey = getPermissionModeScopeKey(null, "default");
68559
+ const legacyDefault = runtime.permissionModeByConversation.get(legacyDefaultKey);
68560
+ if (legacyDefault) {
68561
+ if (normalizeCwdAgentId(agentId)) {
68562
+ runtime.permissionModeByConversation.set(scopeKey, {
68563
+ ...legacyDefault
68564
+ });
68565
+ runtime.permissionModeByConversation.delete(legacyDefaultKey);
68566
+ }
68567
+ return legacyDefault;
68568
+ }
68569
+ }
68570
+ return {
68452
68571
  mode: permissionMode.getMode(),
68453
68572
  planFilePath: null,
68454
68573
  modeBeforePlan: null
@@ -69077,9 +69196,10 @@ function isValidApprovalResponseBody(value) {
69077
69196
  }
69078
69197
  const decision = maybeResponse.decision;
69079
69198
  if (decision.behavior === "allow") {
69199
+ const hasMessage = decision.message === undefined || typeof decision.message === "string";
69080
69200
  const hasUpdatedInput = decision.updated_input === undefined || decision.updated_input === null || typeof decision.updated_input === "object";
69081
69201
  const hasUpdatedPermissions = decision.updated_permissions === undefined || Array.isArray(decision.updated_permissions) && decision.updated_permissions.every((entry) => typeof entry === "string");
69082
- return hasUpdatedInput && hasUpdatedPermissions;
69202
+ return hasMessage && hasUpdatedInput && hasUpdatedPermissions;
69083
69203
  }
69084
69204
  if (decision.behavior === "deny") {
69085
69205
  return typeof decision.message === "string";
@@ -69125,7 +69245,7 @@ function resolvePendingApprovalResolver(runtime, response) {
69125
69245
  runtime.pendingApprovalResolvers.delete(requestId);
69126
69246
  runtime.listener.approvalRuntimeKeyByRequestId.delete(requestId);
69127
69247
  if (runtime.pendingApprovalResolvers.size === 0) {
69128
- setLoopStatus(runtime, runtime.isProcessing ? "PROCESSING_API_RESPONSE" : "WAITING_ON_INPUT");
69248
+ setLoopStatus(runtime, "WAITING_ON_INPUT");
69129
69249
  }
69130
69250
  pending.resolve(response);
69131
69251
  emitLoopStatusIfOpen(runtime.listener, {
@@ -69149,7 +69269,7 @@ function rejectPendingApprovalResolvers(runtime, reason) {
69149
69269
  runtime.listener.approvalRuntimeKeyByRequestId.delete(requestId);
69150
69270
  }
69151
69271
  }
69152
- setLoopStatus(runtime, runtime.isProcessing ? "PROCESSING_API_RESPONSE" : "WAITING_ON_INPUT");
69272
+ setLoopStatus(runtime, "WAITING_ON_INPUT");
69153
69273
  emitLoopStatusIfOpen(runtime.listener, {
69154
69274
  agent_id: runtime.agentId,
69155
69275
  conversation_id: runtime.conversationId
@@ -69214,23 +69334,62 @@ function normalizeToolReturnText(value) {
69214
69334
  return String(value);
69215
69335
  }
69216
69336
  }
69337
+ function isToolReturnContent(value) {
69338
+ if (typeof value === "string")
69339
+ return true;
69340
+ if (!Array.isArray(value))
69341
+ return false;
69342
+ 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"));
69343
+ }
69344
+ function prependApprovalComment(toolReturn, reason) {
69345
+ const trimmedReason = reason?.trim();
69346
+ if (!trimmedReason) {
69347
+ return toolReturn;
69348
+ }
69349
+ const commentPart = {
69350
+ type: "text",
69351
+ text: `${APPROVAL_COMMENT_PREFIX} "${trimmedReason}"`
69352
+ };
69353
+ if (typeof toolReturn === "string") {
69354
+ return [commentPart, { type: "text", text: toolReturn }];
69355
+ }
69356
+ return [commentPart, ...toolReturn];
69357
+ }
69217
69358
  function normalizeApprovalResultsForPersistence(approvals, options = {}) {
69218
69359
  if (!approvals || approvals.length === 0)
69219
69360
  return approvals ?? [];
69220
69361
  const interruptedSet = new Set(options.interruptedToolCallIds ?? []);
69221
69362
  return approvals.map((approval) => {
69363
+ 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)) {
69364
+ return {
69365
+ type: "tool",
69366
+ tool_call_id: "tool_call_id" in approval && typeof approval.tool_call_id === "string" ? approval.tool_call_id : "",
69367
+ tool_return: approval.tool_return,
69368
+ status: "status" in approval && approval.status === "error" ? "error" : "success",
69369
+ stdout: "stdout" in approval && Array.isArray(approval.stdout) ? approval.stdout : undefined,
69370
+ stderr: "stderr" in approval && Array.isArray(approval.stderr) ? approval.stderr : undefined
69371
+ };
69372
+ }
69222
69373
  if (!approval || typeof approval !== "object" || !("type" in approval) || approval.type !== "tool") {
69223
69374
  return approval;
69224
69375
  }
69225
69376
  const toolCallId = "tool_call_id" in approval && typeof approval.tool_call_id === "string" ? approval.tool_call_id : "";
69377
+ const toolReturn = prependApprovalComment(approval.tool_return, "reason" in approval && typeof approval.reason === "string" ? approval.reason : undefined);
69226
69378
  const interruptedByStructuredId = toolCallId.length > 0 && interruptedSet.has(toolCallId);
69227
69379
  const interruptedByLegacyText = options.allowInterruptTextFallback ? normalizeToolReturnText("tool_return" in approval ? approval.tool_return : "") === INTERRUPTED_BY_USER : false;
69228
69380
  if ((interruptedByStructuredId || interruptedByLegacyText) && "status" in approval && approval.status !== "error") {
69229
69381
  return {
69230
69382
  ...approval,
69383
+ tool_return: toolReturn,
69231
69384
  status: "error"
69232
69385
  };
69233
69386
  }
69387
+ if (toolReturn !== approval.tool_return) {
69388
+ return {
69389
+ ...approval,
69390
+ tool_return: toolReturn
69391
+ };
69392
+ }
69234
69393
  return approval;
69235
69394
  });
69236
69395
  }
@@ -69248,6 +69407,7 @@ function normalizeOutgoingApprovalMessages(messages, options = {}) {
69248
69407
  };
69249
69408
  });
69250
69409
  }
69410
+ var APPROVAL_COMMENT_PREFIX = "The user approved the tool execution with the following comment:";
69251
69411
  var init_approval_result_normalization = __esm(() => {
69252
69412
  init_constants();
69253
69413
  });
@@ -69770,12 +69930,19 @@ function mergeQueuedTurnInput(queued, options) {
69770
69930
 
69771
69931
  // src/websocket/helpers/listenerQueueAdapter.ts
69772
69932
  function getListenerBlockedReason(c) {
69773
- if (c.pendingApprovalsLen > 0)
69774
- return "pending_approvals";
69775
69933
  if (c.cancelRequested)
69776
69934
  return "interrupt_in_progress";
69935
+ if (c.pendingApprovalsLen > 0)
69936
+ return "pending_approvals";
69777
69937
  if (c.isRecoveringApprovals)
69778
69938
  return "runtime_busy";
69939
+ if (c.loopStatus === "WAITING_ON_APPROVAL")
69940
+ return "pending_approvals";
69941
+ if (c.loopStatus === "EXECUTING_COMMAND")
69942
+ return "command_running";
69943
+ if (c.loopStatus === "SENDING_API_REQUEST" || c.loopStatus === "RETRYING_API_REQUEST" || c.loopStatus === "WAITING_FOR_API_RESPONSE" || c.loopStatus === "PROCESSING_API_RESPONSE" || c.loopStatus === "EXECUTING_CLIENT_SIDE_TOOL") {
69944
+ return "streaming";
69945
+ }
69779
69946
  if (c.isProcessing)
69780
69947
  return "runtime_busy";
69781
69948
  return null;
@@ -69789,7 +69956,8 @@ __export(exports_queue, {
69789
69956
  normalizeMessageContentImages: () => normalizeMessageContentImages,
69790
69957
  normalizeInboundMessages: () => normalizeInboundMessages,
69791
69958
  getQueueItemsScope: () => getQueueItemsScope,
69792
- getQueueItemScope: () => getQueueItemScope
69959
+ getQueueItemScope: () => getQueueItemScope,
69960
+ consumeQueuedTurn: () => consumeQueuedTurn
69793
69961
  });
69794
69962
  function getQueueItemScope(item) {
69795
69963
  if (!item) {
@@ -69808,6 +69976,9 @@ function getQueueItemsScope(items) {
69808
69976
  const sameScope = items.every((item) => (item.agentId ?? null) === (first.agentId ?? null) && (item.conversationId ?? null) === (first.conversationId ?? null));
69809
69977
  return sameScope ? getQueueItemScope(first) : {};
69810
69978
  }
69979
+ function hasSameQueueScope(a, b) {
69980
+ return (a.agentId ?? null) === (b.agentId ?? null) && (a.conversationId ?? null) === (b.conversationId ?? null);
69981
+ }
69811
69982
  function mergeDequeuedBatchContent(items) {
69812
69983
  const queuedInputs = [];
69813
69984
  for (const item of items) {
@@ -69925,12 +70096,46 @@ function buildQueuedTurnMessage(runtime, batch) {
69925
70096
  function shouldQueueInboundMessage(parsed) {
69926
70097
  return parsed.messages.some((payload) => ("content" in payload));
69927
70098
  }
70099
+ function consumeQueuedTurn(runtime) {
70100
+ const queuedItems = runtime.queueRuntime.peek();
70101
+ const firstQueuedItem = queuedItems[0];
70102
+ if (!firstQueuedItem || !isCoalescable(firstQueuedItem.kind)) {
70103
+ return null;
70104
+ }
70105
+ let queueLen = 0;
70106
+ let hasMessage = false;
70107
+ for (const item of queuedItems) {
70108
+ if (!isCoalescable(item.kind) || !hasSameQueueScope(firstQueuedItem, item)) {
70109
+ break;
70110
+ }
70111
+ queueLen += 1;
70112
+ if (item.kind === "message") {
70113
+ hasMessage = true;
70114
+ }
70115
+ }
70116
+ if (!hasMessage || queueLen === 0) {
70117
+ return null;
70118
+ }
70119
+ const dequeuedBatch = runtime.queueRuntime.consumeItems(queueLen);
70120
+ if (!dequeuedBatch) {
70121
+ return null;
70122
+ }
70123
+ const queuedTurn = buildQueuedTurnMessage(runtime, dequeuedBatch);
70124
+ if (!queuedTurn) {
70125
+ return null;
70126
+ }
70127
+ return {
70128
+ dequeuedBatch,
70129
+ queuedTurn
70130
+ };
70131
+ }
69928
70132
  function computeListenerQueueBlockedReason(runtime) {
69929
70133
  const activeScope = resolveRuntimeScope(runtime.listener, {
69930
70134
  agent_id: runtime.agentId,
69931
70135
  conversation_id: runtime.conversationId
69932
70136
  });
69933
70137
  return getListenerBlockedReason({
70138
+ loopStatus: runtime.loopStatus,
69934
70139
  isProcessing: runtime.isProcessing,
69935
70140
  pendingApprovalsLen: activeScope ? getPendingControlRequestCount(runtime.listener, activeScope) : 0,
69936
70141
  cancelRequested: runtime.cancelRequested,
@@ -69952,18 +70157,11 @@ async function drainQueuedMessages(runtime, socket, opts, processQueuedTurn) {
69952
70157
  runtime.queueRuntime.tryDequeue(blockedReason);
69953
70158
  return;
69954
70159
  }
69955
- const queueLen = runtime.queueRuntime.length;
69956
- if (queueLen === 0) {
69957
- return;
69958
- }
69959
- const dequeuedBatch = runtime.queueRuntime.consumeItems(queueLen);
69960
- if (!dequeuedBatch) {
70160
+ const consumedQueuedTurn = consumeQueuedTurn(runtime);
70161
+ if (!consumedQueuedTurn) {
69961
70162
  return;
69962
70163
  }
69963
- const queuedTurn = buildQueuedTurnMessage(runtime, dequeuedBatch);
69964
- if (!queuedTurn) {
69965
- continue;
69966
- }
70164
+ const { dequeuedBatch, queuedTurn } = consumedQueuedTurn;
69967
70165
  emitDequeuedUserMessage(socket, runtime, queuedTurn, dequeuedBatch);
69968
70166
  const preTurnStatus = getListenerStatus(runtime.listener) === "processing" ? "processing" : "receiving";
69969
70167
  if (opts.connectionId && runtime.listener.lastEmittedStatus !== preTurnStatus) {
@@ -69998,6 +70196,7 @@ function scheduleQueuePump(runtime, socket, opts, processQueuedTurn) {
69998
70196
  });
69999
70197
  }
70000
70198
  var init_queue = __esm(async () => {
70199
+ init_queueRuntime();
70001
70200
  init_runtime();
70002
70201
  await __promiseAll([
70003
70202
  init_imageResize(),
@@ -71422,7 +71621,7 @@ var init_interactivePolicy = __esm(() => {
71422
71621
  "ExitPlanMode"
71423
71622
  ]);
71424
71623
  RUNTIME_USER_INPUT_TOOLS = new Set(["AskUserQuestion", "ExitPlanMode"]);
71425
- HEADLESS_AUTO_ALLOW_TOOLS = new Set(["EnterPlanMode"]);
71624
+ HEADLESS_AUTO_ALLOW_TOOLS = new Set(["EnterPlanMode", "ExitPlanMode"]);
71426
71625
  });
71427
71626
 
71428
71627
  // src/cli/helpers/toolNameMapping.ts
@@ -74570,7 +74769,8 @@ async function resolveRecoveredApprovalResponse(runtime, socket, response, proce
74570
74769
  approval: decision.updated_input ? {
74571
74770
  ...entry.approval,
74572
74771
  toolArgs: JSON.stringify(decision.updated_input)
74573
- } : entry.approval
74772
+ } : entry.approval,
74773
+ reason: decision.message
74574
74774
  });
74575
74775
  } else {
74576
74776
  decisions.push({
@@ -74622,17 +74822,26 @@ async function resolveRecoveredApprovalResponse(runtime, socket, response, proce
74622
74822
  runtime.activeAbortController = null;
74623
74823
  setLoopStatus(runtime, "SENDING_API_REQUEST", scope);
74624
74824
  emitRuntimeStateUpdates(runtime, scope);
74825
+ const continuationMessages = [
74826
+ {
74827
+ type: "approval",
74828
+ approvals: approvalResults
74829
+ }
74830
+ ];
74831
+ let continuationBatchId = `batch-recovered-${crypto.randomUUID()}`;
74832
+ const consumedQueuedTurn = consumeQueuedTurn(runtime);
74833
+ if (consumedQueuedTurn) {
74834
+ const { dequeuedBatch, queuedTurn } = consumedQueuedTurn;
74835
+ continuationBatchId = dequeuedBatch.batchId;
74836
+ continuationMessages.push(...queuedTurn.messages);
74837
+ emitDequeuedUserMessage(socket, runtime, queuedTurn, dequeuedBatch);
74838
+ }
74625
74839
  await processTurn({
74626
74840
  type: "message",
74627
74841
  agentId: recovered.agentId,
74628
74842
  conversationId: recovered.conversationId,
74629
- messages: [
74630
- {
74631
- type: "approval",
74632
- approvals: approvalResults
74633
- }
74634
- ]
74635
- }, socket, runtime, opts?.onStatusChange, opts?.connectionId, `batch-recovered-${crypto.randomUUID()}`);
74843
+ messages: continuationMessages
74844
+ }, socket, runtime, opts?.onStatusChange, opts?.connectionId, continuationBatchId);
74636
74845
  clearRecoveredApprovalState(runtime);
74637
74846
  return true;
74638
74847
  } catch (error) {
@@ -74665,7 +74874,8 @@ var init_recovery = __esm(async () => {
74665
74874
  init_stream(),
74666
74875
  init_approval(),
74667
74876
  init_interrupts(),
74668
- init_protocol_outbound()
74877
+ init_protocol_outbound(),
74878
+ init_queue()
74669
74879
  ]);
74670
74880
  });
74671
74881
 
@@ -74789,9 +74999,10 @@ function markAwaitingAcceptedApprovalContinuationRunId(runtime, input) {
74789
74999
  runtime.activeRunId = null;
74790
75000
  }
74791
75001
  }
74792
- async function resolveStaleApprovals(runtime, socket, abortSignal) {
75002
+ async function resolveStaleApprovals(runtime, socket, abortSignal, deps = {}) {
74793
75003
  if (!runtime.agentId)
74794
75004
  return null;
75005
+ const getResumeDataImpl = deps.getResumeData ?? getResumeData2;
74795
75006
  const client = await getClient2();
74796
75007
  let agent;
74797
75008
  try {
@@ -74805,7 +75016,7 @@ async function resolveStaleApprovals(runtime, socket, abortSignal) {
74805
75016
  const requestedConversationId = runtime.conversationId !== "default" ? runtime.conversationId : undefined;
74806
75017
  let resumeData;
74807
75018
  try {
74808
- resumeData = await getResumeData2(client, agent, requestedConversationId, {
75019
+ resumeData = await getResumeDataImpl(client, agent, requestedConversationId, {
74809
75020
  includeMessageHistory: false
74810
75021
  });
74811
75022
  } catch (err) {
@@ -74882,7 +75093,8 @@ async function resolveStaleApprovals(runtime, socket, abortSignal) {
74882
75093
  approval: response.updated_input ? {
74883
75094
  ...ac.approval,
74884
75095
  toolArgs: JSON.stringify(response.updated_input)
74885
- } : ac.approval
75096
+ } : ac.approval,
75097
+ reason: response.message
74886
75098
  });
74887
75099
  } else {
74888
75100
  decisions.push({
@@ -74925,7 +75137,19 @@ async function resolveStaleApprovals(runtime, socket, abortSignal) {
74925
75137
  conversationId: recoveryConversationId
74926
75138
  });
74927
75139
  emitInterruptToolReturnMessage(socket, runtime, approvalResults, runtime.activeRunId ?? undefined, "tool-return");
74928
- const recoveryStream = await sendApprovalContinuationWithRetry(recoveryConversationId, [{ type: "approval", approvals: approvalResults }], {
75140
+ const continuationMessages = [
75141
+ {
75142
+ type: "approval",
75143
+ approvals: approvalResults
75144
+ }
75145
+ ];
75146
+ const consumedQueuedTurn = consumeQueuedTurn(runtime);
75147
+ if (consumedQueuedTurn) {
75148
+ const { dequeuedBatch, queuedTurn } = consumedQueuedTurn;
75149
+ continuationMessages.push(...queuedTurn.messages);
75150
+ emitDequeuedUserMessage(socket, runtime, queuedTurn, dequeuedBatch);
75151
+ }
75152
+ const recoveryStream = await sendApprovalContinuationWithRetry(recoveryConversationId, continuationMessages, {
74929
75153
  agentId: runtime.agentId ?? undefined,
74930
75154
  streamTokens: true,
74931
75155
  background: true,
@@ -74934,6 +75158,7 @@ async function resolveStaleApprovals(runtime, socket, abortSignal) {
74934
75158
  if (!recoveryStream) {
74935
75159
  throw new Error("Approval recovery send resolved without a continuation stream");
74936
75160
  }
75161
+ setLoopStatus(runtime, "PROCESSING_API_RESPONSE", scope);
74937
75162
  const drainResult = await drainRecoveryStreamWithEmission(recoveryStream, socket, runtime, {
74938
75163
  agentId: runtime.agentId ?? undefined,
74939
75164
  conversationId: recoveryConversationId,
@@ -75228,6 +75453,7 @@ var init_send = __esm(async () => {
75228
75453
  init_approval(),
75229
75454
  init_interrupts(),
75230
75455
  init_protocol_outbound(),
75456
+ init_queue(),
75231
75457
  init_recovery()
75232
75458
  ]);
75233
75459
  });
@@ -76066,6 +76292,7 @@ async function handleApprovalStop(params) {
76066
76292
  terminated: true,
76067
76293
  stream: null,
76068
76294
  currentInput,
76295
+ dequeuedBatchId,
76069
76296
  pendingNormalizationInterruptedToolCallIds: [],
76070
76297
  turnToolContextId,
76071
76298
  lastExecutionResults: null,
@@ -76129,7 +76356,11 @@ async function handleApprovalStop(params) {
76129
76356
  ...ac.approval,
76130
76357
  toolArgs: JSON.stringify(response.updated_input)
76131
76358
  } : ac.approval;
76132
- decisions.push({ type: "approve", approval: finalApproval });
76359
+ decisions.push({
76360
+ type: "approve",
76361
+ approval: finalApproval,
76362
+ reason: response.message
76363
+ });
76133
76364
  } else {
76134
76365
  decisions.push({
76135
76366
  type: "deny",
@@ -76138,11 +76369,10 @@ async function handleApprovalStop(params) {
76138
76369
  });
76139
76370
  }
76140
76371
  } else {
76141
- const denyReason = responseBody.error;
76142
76372
  decisions.push({
76143
76373
  type: "deny",
76144
76374
  approval: ac.approval,
76145
- reason: denyReason
76375
+ reason: responseBody.error
76146
76376
  });
76147
76377
  }
76148
76378
  }
@@ -76157,7 +76387,7 @@ async function handleApprovalStop(params) {
76157
76387
  agent_id: agentId,
76158
76388
  conversation_id: conversationId
76159
76389
  });
76160
- const executionRunId = runId || runtime.activeRunId || params.msgRunIds[params.msgRunIds.length - 1];
76390
+ const executionRunId = runId || runtime.activeRunId || msgRunIds[msgRunIds.length - 1];
76161
76391
  emitToolExecutionStartedEvents(socket, runtime, {
76162
76392
  toolCallIds: lastExecutingToolCallIds,
76163
76393
  runId: executionRunId,
@@ -76189,6 +76419,14 @@ async function handleApprovalStop(params) {
76189
76419
  approvals: persistedExecutionResults
76190
76420
  }
76191
76421
  ];
76422
+ let continuationBatchId = dequeuedBatchId;
76423
+ const consumedQueuedTurn = consumeQueuedTurn(runtime);
76424
+ if (consumedQueuedTurn) {
76425
+ const { dequeuedBatch, queuedTurn } = consumedQueuedTurn;
76426
+ continuationBatchId = dequeuedBatch.batchId;
76427
+ nextInput.push(...queuedTurn.messages);
76428
+ emitDequeuedUserMessage(socket, runtime, queuedTurn, dequeuedBatch);
76429
+ }
76192
76430
  setLoopStatus(runtime, "SENDING_API_REQUEST", {
76193
76431
  agent_id: agentId,
76194
76432
  conversation_id: conversationId
@@ -76199,6 +76437,7 @@ async function handleApprovalStop(params) {
76199
76437
  terminated: true,
76200
76438
  stream: null,
76201
76439
  currentInput: nextInput,
76440
+ dequeuedBatchId: continuationBatchId,
76202
76441
  pendingNormalizationInterruptedToolCallIds: [],
76203
76442
  turnToolContextId,
76204
76443
  lastExecutionResults,
@@ -76232,6 +76471,7 @@ async function handleApprovalStop(params) {
76232
76471
  terminated: false,
76233
76472
  stream: stream2,
76234
76473
  currentInput: nextInput,
76474
+ dequeuedBatchId: continuationBatchId,
76235
76475
  pendingNormalizationInterruptedToolCallIds: [],
76236
76476
  turnToolContextId: null,
76237
76477
  lastExecutionResults,
@@ -76249,6 +76489,7 @@ var init_turn_approval = __esm(async () => {
76249
76489
  init_approval(),
76250
76490
  init_interrupts(),
76251
76491
  init_protocol_outbound(),
76492
+ init_queue(),
76252
76493
  init_recovery(),
76253
76494
  init_send()
76254
76495
  ]);
@@ -76269,6 +76510,7 @@ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, conne
76269
76510
  let llmApiErrorRetries = 0;
76270
76511
  let emptyResponseRetries = 0;
76271
76512
  let lastApprovalContinuationAccepted = false;
76513
+ let activeDequeuedBatchId = dequeuedBatchId;
76272
76514
  let lastExecutionResults = null;
76273
76515
  let lastExecutingToolCallIds = [];
76274
76516
  let lastNeedsUserInputToolCallIds = [];
@@ -76630,7 +76872,7 @@ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, conne
76630
76872
  conversationId,
76631
76873
  turnWorkingDirectory,
76632
76874
  turnPermissionModeState,
76633
- dequeuedBatchId,
76875
+ dequeuedBatchId: activeDequeuedBatchId,
76634
76876
  runId,
76635
76877
  msgRunIds,
76636
76878
  currentInput,
@@ -76643,6 +76885,7 @@ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, conne
76643
76885
  }
76644
76886
  stream2 = approvalResult.stream;
76645
76887
  currentInput = approvalResult.currentInput;
76888
+ activeDequeuedBatchId = approvalResult.dequeuedBatchId;
76646
76889
  pendingNormalizationInterruptedToolCallIds = approvalResult.pendingNormalizationInterruptedToolCallIds;
76647
76890
  turnToolContextId = approvalResult.turnToolContextId;
76648
76891
  lastExecutionResults = approvalResult.lastExecutionResults;
@@ -76718,6 +76961,10 @@ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, conne
76718
76961
  }
76719
76962
  } finally {
76720
76963
  setConversationPermissionModeState(runtime.listener, normalizedAgentId, conversationId, turnPermissionModeState);
76964
+ emitDeviceStatusIfOpen(runtime, {
76965
+ agent_id: agentId || null,
76966
+ conversation_id: conversationId
76967
+ });
76721
76968
  runtime.activeAbortController = null;
76722
76969
  runtime.cancelRequested = false;
76723
76970
  runtime.isRecoveringApprovals = false;
@@ -76860,6 +77107,51 @@ async function handleApprovalResponseInput(listener, params, deps = {
76860
77107
  }
76861
77108
  return false;
76862
77109
  }
77110
+ async function handleChangeDeviceStateInput(listener, params, deps = {}) {
77111
+ const resolvedDeps = {
77112
+ getActiveRuntime,
77113
+ getOrCreateScopedRuntime,
77114
+ getPendingControlRequestCount,
77115
+ setLoopStatus,
77116
+ handleModeChange,
77117
+ handleCwdChange,
77118
+ emitDeviceStatusUpdate,
77119
+ scheduleQueuePump,
77120
+ ...deps
77121
+ };
77122
+ if (listener !== resolvedDeps.getActiveRuntime() || listener.intentionallyClosed) {
77123
+ return false;
77124
+ }
77125
+ const scope = {
77126
+ agent_id: params.command.payload.agent_id ?? params.command.runtime.agent_id ?? undefined,
77127
+ conversation_id: params.command.payload.conversation_id ?? params.command.runtime.conversation_id ?? undefined
77128
+ };
77129
+ const scopedRuntime = resolvedDeps.getOrCreateScopedRuntime(listener, scope.agent_id, scope.conversation_id);
77130
+ const shouldTrackCommand = !scopedRuntime.isProcessing && resolvedDeps.getPendingControlRequestCount(listener, scope) === 0;
77131
+ if (shouldTrackCommand) {
77132
+ resolvedDeps.setLoopStatus(scopedRuntime, "EXECUTING_COMMAND", scope);
77133
+ }
77134
+ try {
77135
+ if (params.command.payload.mode) {
77136
+ resolvedDeps.handleModeChange({ mode: params.command.payload.mode }, params.socket, listener, scope);
77137
+ }
77138
+ if (params.command.payload.cwd) {
77139
+ await resolvedDeps.handleCwdChange({
77140
+ agentId: scope.agent_id ?? null,
77141
+ conversationId: scope.conversation_id ?? null,
77142
+ cwd: params.command.payload.cwd
77143
+ }, params.socket, scopedRuntime);
77144
+ } else if (!params.command.payload.mode) {
77145
+ resolvedDeps.emitDeviceStatusUpdate(params.socket, listener, scope);
77146
+ }
77147
+ } finally {
77148
+ if (shouldTrackCommand) {
77149
+ resolvedDeps.setLoopStatus(scopedRuntime, "WAITING_ON_INPUT", scope);
77150
+ resolvedDeps.scheduleQueuePump(scopedRuntime, params.socket, params.opts, params.processQueuedTurn);
77151
+ }
77152
+ }
77153
+ return true;
77154
+ }
76863
77155
  async function handleCwdChange(msg, socket, runtime) {
76864
77156
  const conversationId = normalizeConversationId(msg.conversationId);
76865
77157
  const agentId = normalizeCwdAgentId(msg.agentId);
@@ -77144,36 +77436,15 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
77144
77436
  return;
77145
77437
  }
77146
77438
  if (parsed.type === "change_device_state") {
77147
- if (runtime !== getActiveRuntime() || runtime.intentionallyClosed) {
77148
- return;
77149
- }
77150
- const scope = {
77151
- agent_id: parsed.payload.agent_id ?? parsed.runtime.agent_id ?? undefined,
77152
- conversation_id: parsed.payload.conversation_id ?? parsed.runtime.conversation_id ?? undefined
77153
- };
77154
- const scopedRuntime = getOrCreateScopedRuntime(runtime, scope.agent_id, scope.conversation_id);
77155
- const shouldTrackCommand = !scopedRuntime.isProcessing && getPendingControlRequestCount(runtime, scope) === 0;
77156
- if (shouldTrackCommand) {
77157
- setLoopStatus(scopedRuntime, "EXECUTING_COMMAND", scope);
77158
- }
77159
- try {
77160
- if (parsed.payload.mode) {
77161
- handleModeChange({ mode: parsed.payload.mode }, socket, runtime, scope);
77162
- }
77163
- if (parsed.payload.cwd) {
77164
- await handleCwdChange({
77165
- agentId: scope.agent_id ?? null,
77166
- conversationId: scope.conversation_id ?? null,
77167
- cwd: parsed.payload.cwd
77168
- }, socket, scopedRuntime);
77169
- } else if (!parsed.payload.mode) {
77170
- emitDeviceStatusUpdate(socket, runtime, scope);
77171
- }
77172
- } finally {
77173
- if (shouldTrackCommand) {
77174
- setLoopStatus(scopedRuntime, "WAITING_ON_INPUT", scope);
77175
- }
77176
- }
77439
+ await handleChangeDeviceStateInput(runtime, {
77440
+ command: parsed,
77441
+ socket,
77442
+ opts: {
77443
+ onStatusChange: opts.onStatusChange,
77444
+ connectionId: opts.connectionId
77445
+ },
77446
+ processQueuedTurn
77447
+ });
77177
77448
  return;
77178
77449
  }
77179
77450
  if (parsed.type === "abort_message") {
@@ -77529,10 +77800,13 @@ var init_client4 = __esm(async () => {
77529
77800
  shouldAttemptPostStopApprovalRecovery,
77530
77801
  getApprovalContinuationRecoveryDisposition,
77531
77802
  markAwaitingAcceptedApprovalContinuationRunId,
77803
+ resolveStaleApprovals,
77532
77804
  normalizeMessageContentImages,
77533
77805
  normalizeInboundMessages,
77806
+ consumeQueuedTurn,
77534
77807
  handleIncomingMessage,
77535
77808
  handleApprovalResponseInput,
77809
+ handleChangeDeviceStateInput,
77536
77810
  scheduleQueuePump,
77537
77811
  recoverApprovalStateForSync,
77538
77812
  clearRecoveredApprovalStateForScope: (runtime, scope) => clearRecoveredApprovalStateForScope(asListenerRuntimeForTests(runtime), scope),
@@ -102019,6 +102293,7 @@ var init_StaticPlanApproval = __esm(async () => {
102019
102293
  onApproveAndAcceptEdits,
102020
102294
  onKeepPlanning,
102021
102295
  onCancel,
102296
+ showAcceptEditsOption = true,
102022
102297
  isFocused = true,
102023
102298
  planContent,
102024
102299
  planFilePath,
@@ -102046,9 +102321,10 @@ var init_StaticPlanApproval = __esm(async () => {
102046
102321
  setTimeout(() => setBrowserStatus(""), 5000);
102047
102322
  });
102048
102323
  }, [planContent, planFilePath, agentName]);
102049
- const customOptionIndex = 2;
102324
+ const customOptionIndex = showAcceptEditsOption ? 2 : 1;
102050
102325
  const maxOptionIndex = customOptionIndex;
102051
- const isOnCustomOption = selectedOption === customOptionIndex;
102326
+ const effectiveSelectedOption = Math.min(selectedOption, maxOptionIndex);
102327
+ const isOnCustomOption = effectiveSelectedOption === customOptionIndex;
102052
102328
  const customOptionPlaceholder = "Type here to tell Letta Code what to change";
102053
102329
  use_input_default((input, key) => {
102054
102330
  if (!isFocused)
@@ -102088,9 +102364,9 @@ var init_StaticPlanApproval = __esm(async () => {
102088
102364
  return;
102089
102365
  }
102090
102366
  if (key.return) {
102091
- if (selectedOption === 0) {
102367
+ if (showAcceptEditsOption && effectiveSelectedOption === 0) {
102092
102368
  onApproveAndAcceptEdits();
102093
- } else if (selectedOption === 1) {
102369
+ } else {
102094
102370
  onApprove();
102095
102371
  }
102096
102372
  return;
@@ -102100,10 +102376,14 @@ var init_StaticPlanApproval = __esm(async () => {
102100
102376
  return;
102101
102377
  }
102102
102378
  if (input === "1") {
102103
- onApproveAndAcceptEdits();
102379
+ if (showAcceptEditsOption) {
102380
+ onApproveAndAcceptEdits();
102381
+ } else {
102382
+ onApprove();
102383
+ }
102104
102384
  return;
102105
102385
  }
102106
- if (input === "2") {
102386
+ if (showAcceptEditsOption && input === "2") {
102107
102387
  onApprove();
102108
102388
  return;
102109
102389
  }
@@ -102129,9 +102409,9 @@ var init_StaticPlanApproval = __esm(async () => {
102129
102409
  width: 5,
102130
102410
  flexShrink: 0,
102131
102411
  children: /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(Text2, {
102132
- color: selectedOption === 0 ? colors.approval.header : undefined,
102412
+ color: effectiveSelectedOption === 0 ? colors.approval.header : undefined,
102133
102413
  children: [
102134
- selectedOption === 0 ? "❯" : " ",
102414
+ effectiveSelectedOption === 0 ? "❯" : " ",
102135
102415
  " 1."
102136
102416
  ]
102137
102417
  }, undefined, true, undefined, this)
@@ -102141,22 +102421,22 @@ var init_StaticPlanApproval = __esm(async () => {
102141
102421
  width: Math.max(0, columns - 5),
102142
102422
  children: /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(Text2, {
102143
102423
  wrap: "wrap",
102144
- color: selectedOption === 0 ? colors.approval.header : undefined,
102145
- children: "Yes, and auto-accept edits"
102424
+ color: effectiveSelectedOption === 0 ? colors.approval.header : undefined,
102425
+ children: showAcceptEditsOption ? "Yes, and auto-accept edits" : "Yes, proceed (bypassPermissions / yolo mode)"
102146
102426
  }, undefined, false, undefined, this)
102147
102427
  }, undefined, false, undefined, this)
102148
102428
  ]
102149
102429
  }, undefined, true, undefined, this),
102150
- /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(Box_default, {
102430
+ showAcceptEditsOption && /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(Box_default, {
102151
102431
  flexDirection: "row",
102152
102432
  children: [
102153
102433
  /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(Box_default, {
102154
102434
  width: 5,
102155
102435
  flexShrink: 0,
102156
102436
  children: /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(Text2, {
102157
- color: selectedOption === 1 ? colors.approval.header : undefined,
102437
+ color: effectiveSelectedOption === 1 ? colors.approval.header : undefined,
102158
102438
  children: [
102159
- selectedOption === 1 ? "❯" : " ",
102439
+ effectiveSelectedOption === 1 ? "❯" : " ",
102160
102440
  " 2."
102161
102441
  ]
102162
102442
  }, undefined, true, undefined, this)
@@ -102166,7 +102446,7 @@ var init_StaticPlanApproval = __esm(async () => {
102166
102446
  width: Math.max(0, columns - 5),
102167
102447
  children: /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(Text2, {
102168
102448
  wrap: "wrap",
102169
- color: selectedOption === 1 ? colors.approval.header : undefined,
102449
+ color: effectiveSelectedOption === 1 ? colors.approval.header : undefined,
102170
102450
  children: "Yes, and manually approve edits"
102171
102451
  }, undefined, false, undefined, this)
102172
102452
  }, undefined, false, undefined, this)
@@ -102182,7 +102462,9 @@ var init_StaticPlanApproval = __esm(async () => {
102182
102462
  color: isOnCustomOption ? colors.approval.header : undefined,
102183
102463
  children: [
102184
102464
  isOnCustomOption ? "❯" : " ",
102185
- " 3."
102465
+ " ",
102466
+ customOptionIndex + 1,
102467
+ "."
102186
102468
  ]
102187
102469
  }, undefined, true, undefined, this)
102188
102470
  }, undefined, false, undefined, this),
@@ -102298,6 +102580,7 @@ function getQuestions(approval) {
102298
102580
  }
102299
102581
  var import_react48, jsx_dev_runtime25, ApprovalSwitch;
102300
102582
  var init_ApprovalSwitch = __esm(async () => {
102583
+ init_mode();
102301
102584
  await __promiseAll([
102302
102585
  init_toolNameMapping(),
102303
102586
  init_InlineBashApproval(),
@@ -102334,11 +102617,13 @@ var init_ApprovalSwitch = __esm(async () => {
102334
102617
  }) => {
102335
102618
  const toolName = approval.toolName;
102336
102619
  if (toolName === "ExitPlanMode" && onPlanApprove && onPlanKeepPlanning) {
102620
+ const showAcceptEditsOption = permissionMode.getMode() === "plan" && permissionMode.getModeBeforePlan() !== "bypassPermissions";
102337
102621
  return /* @__PURE__ */ jsx_dev_runtime25.jsxDEV(StaticPlanApproval, {
102338
102622
  onApprove: () => onPlanApprove(false),
102339
102623
  onApproveAndAcceptEdits: () => onPlanApprove(true),
102340
102624
  onKeepPlanning: onPlanKeepPlanning,
102341
102625
  onCancel: onCancel ?? (() => {}),
102626
+ showAcceptEditsOption,
102342
102627
  isFocused,
102343
102628
  planContent,
102344
102629
  planFilePath,
@@ -124500,6 +124785,18 @@ function formatApiKeyUsage(provider) {
124500
124785
  ].join(`
124501
124786
  `);
124502
124787
  }
124788
+ function formatZaiCodingPlanPrompt(apiKey) {
124789
+ const keyHint = apiKey ? ` ${apiKey}` : " <api_key>";
124790
+ return [
124791
+ "Connect to Z.ai",
124792
+ "",
124793
+ "Do you have a Z.ai Coding plan?",
124794
+ "",
124795
+ ` • Coding plan: /connect zai-coding${keyHint}`,
124796
+ ` • Regular API: /connect zai${keyHint}`
124797
+ ].join(`
124798
+ `);
124799
+ }
124503
124800
  async function handleConnectChatGPT(ctx, msg) {
124504
124801
  const existingProvider = await isChatGPTOAuthConnected();
124505
124802
  if (existingProvider) {
@@ -124608,7 +124905,11 @@ async function handleConnect(ctx, msg) {
124608
124905
  if (isConnectApiKeyProvider(provider)) {
124609
124906
  const apiKey = parts.slice(2).join("");
124610
124907
  if (!apiKey) {
124611
- addCommandResult3(ctx.buffersRef, ctx.refreshDerived, msg, formatApiKeyUsage(provider), false);
124908
+ if (isConnectZaiBaseProvider(provider)) {
124909
+ addCommandResult3(ctx.buffersRef, ctx.refreshDerived, msg, formatZaiCodingPlanPrompt(), false);
124910
+ } else {
124911
+ addCommandResult3(ctx.buffersRef, ctx.refreshDerived, msg, formatApiKeyUsage(provider), false);
124912
+ }
124612
124913
  return;
124613
124914
  }
124614
124915
  await handleConnectApiKeyProvider(ctx, msg, provider, apiKey);
@@ -131010,10 +131311,12 @@ ${SYSTEM_REMINDER_CLOSE}
131010
131311
  refreshDerived();
131011
131312
  const recoveryApprovalResults = [
131012
131313
  ...autoAllowedResults.map((ar) => ({
131013
- type: "approval",
131314
+ type: "tool",
131014
131315
  tool_call_id: ar.toolCallId,
131015
- approve: true,
131016
- tool_return: ar.result.toolReturn
131316
+ tool_return: ar.result.toolReturn,
131317
+ status: ar.result.status,
131318
+ stdout: ar.result.stdout,
131319
+ stderr: ar.result.stderr
131017
131320
  })),
131018
131321
  ...autoDeniedResults
131019
131322
  ];
@@ -132470,7 +132773,8 @@ ${guidance}`);
132470
132773
  }
132471
132774
  const currentMode = permissionMode.getMode();
132472
132775
  if (currentMode === "plan") {
132473
- const restoreMode = acceptEdits ? "acceptEdits" : permissionMode.getModeBeforePlan() ?? "default";
132776
+ const previousMode = permissionMode.getModeBeforePlan();
132777
+ const restoreMode = previousMode === "bypassPermissions" ? "bypassPermissions" : acceptEdits ? "acceptEdits" : previousMode ?? "default";
132474
132778
  permissionMode.setMode(restoreMode);
132475
132779
  setUiPermissionMode(restoreMode);
132476
132780
  } else {
@@ -132551,12 +132855,10 @@ ${guidance}`);
132551
132855
  const fallbackPlanPath = lastPlanFilePathRef.current;
132552
132856
  const hasUsablePlan = planFileExists(fallbackPlanPath);
132553
132857
  if (mode !== "plan") {
132858
+ if (hasUsablePlan) {
132859
+ return;
132860
+ }
132554
132861
  if (mode === "bypassPermissions") {
132555
- if (hasUsablePlan) {
132556
- lastAutoHandledExitPlanToolCallIdRef.current = approval.toolCallId;
132557
- handlePlanApprove();
132558
- return;
132559
- }
132560
132862
  const planFilePath = activePlanPath ?? fallbackPlanPath;
132561
132863
  const plansDir = join44(homedir34(), ".letta", "plans");
132562
132864
  handlePlanKeepPlanning(`You must write your plan to a plan file before exiting plan mode.
@@ -132564,9 +132866,6 @@ ${guidance}`);
132564
132866
  ` : "") + `Use a write tool to create your plan in ${plansDir}, then use ExitPlanMode to present the plan to the user.`);
132565
132867
  return;
132566
132868
  }
132567
- if (hasUsablePlan) {
132568
- return;
132569
- }
132570
132869
  const statusId = uid5("status");
132571
132870
  buffersRef.current.byId.set(statusId, {
132572
132871
  kind: "status",
@@ -132605,7 +132904,6 @@ ${guidance}`);
132605
132904
  }, [
132606
132905
  pendingApprovals,
132607
132906
  approvalResults.length,
132608
- handlePlanApprove,
132609
132907
  handlePlanKeepPlanning,
132610
132908
  refreshDerived,
132611
132909
  queueApprovalResults
@@ -138580,6 +138878,12 @@ async function runConnectSubcommand(argv, deps = {}) {
138580
138878
  }
138581
138879
  if (isConnectApiKeyProvider(provider)) {
138582
138880
  let apiKey = readStringOption(parsed.values["api-key"]) ?? restPositionals[0] ?? "";
138881
+ if (!apiKey && isConnectZaiBaseProvider(provider)) {
138882
+ io.stdout(`Do you have a Z.ai Coding plan?
138883
+ ` + ` • Coding plan: letta connect zai-coding [--api-key <key>]
138884
+ ` + " • Regular API: letta connect zai [--api-key <key>]");
138885
+ return 0;
138886
+ }
138583
138887
  if (!apiKey) {
138584
138888
  if (!io.isTTY()) {
138585
138889
  io.stderr(`Missing API key for ${provider.canonical}. Pass as positional arg or --api-key.`);
@@ -141064,6 +141368,7 @@ var ANTHROPIC_DEFAULT_TOOLS2 = [
141064
141368
  "Glob",
141065
141369
  "Grep",
141066
141370
  "TaskStop",
141371
+ "memory",
141067
141372
  "Read",
141068
141373
  "Skill",
141069
141374
  "Task",
@@ -141074,6 +141379,7 @@ var OPENAI_PASCAL_TOOLS2 = [
141074
141379
  "AskUserQuestion",
141075
141380
  "EnterPlanMode",
141076
141381
  "ExitPlanMode",
141382
+ "memory",
141077
141383
  "Task",
141078
141384
  "TaskOutput",
141079
141385
  "TaskStop",
@@ -141087,6 +141393,7 @@ var GEMINI_PASCAL_TOOLS2 = [
141087
141393
  "AskUserQuestion",
141088
141394
  "EnterPlanMode",
141089
141395
  "ExitPlanMode",
141396
+ "memory",
141090
141397
  "Skill",
141091
141398
  "Task",
141092
141399
  "RunShellCommand",
@@ -142845,4 +143152,4 @@ Error during initialization: ${message}`);
142845
143152
  }
142846
143153
  main();
142847
143154
 
142848
- //# debugId=795EEAC4C0962C6964756E2164756E21
143155
+ //# debugId=D1C8E311510A3C4964756E2164756E21