@letta-ai/letta-code 0.19.3 → 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.
@@ -312,7 +312,7 @@ export interface QueueBatchDequeuedEvent extends MessageEnvelope {
312
312
  }
313
313
  /**
314
314
  * Why the queue cannot dequeue right now.
315
- * - streaming: Agent turn is actively streaming
315
+ * - streaming: Agent turn is actively running/streaming (request, response, or local tool execution)
316
316
  * - pending_approvals: Waiting for HITL approval decisions
317
317
  * - overlay_open: Plan mode, AskUserQuestion, or other overlay is active
318
318
  * - command_running: Slash command is executing
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.3",
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}": [
@@ -68043,6 +68043,7 @@ var init_manager3 = __esm(async () => {
68043
68043
  "Glob",
68044
68044
  "Grep",
68045
68045
  "TaskStop",
68046
+ "memory",
68046
68047
  "Read",
68047
68048
  "Skill",
68048
68049
  "Task",
@@ -68052,6 +68053,7 @@ var init_manager3 = __esm(async () => {
68052
68053
  OPENAI_DEFAULT_TOOLS = [
68053
68054
  "shell_command",
68054
68055
  "apply_patch",
68056
+ "memory",
68055
68057
  "update_plan",
68056
68058
  "view_image"
68057
68059
  ];
@@ -68061,6 +68063,7 @@ var init_manager3 = __esm(async () => {
68061
68063
  "list_directory",
68062
68064
  "glob_gemini",
68063
68065
  "search_file_content",
68066
+ "memory",
68064
68067
  "replace",
68065
68068
  "write_file_gemini",
68066
68069
  "write_todos",
@@ -68072,6 +68075,7 @@ var init_manager3 = __esm(async () => {
68072
68075
  "AskUserQuestion",
68073
68076
  "EnterPlanMode",
68074
68077
  "ExitPlanMode",
68078
+ "memory",
68075
68079
  "Task",
68076
68080
  "TaskOutput",
68077
68081
  "TaskStop",
@@ -68085,6 +68089,7 @@ var init_manager3 = __esm(async () => {
68085
68089
  "AskUserQuestion",
68086
68090
  "EnterPlanMode",
68087
68091
  "ExitPlanMode",
68092
+ "memory",
68088
68093
  "Skill",
68089
68094
  "Task",
68090
68095
  "RunShellCommand",
@@ -68175,82 +68180,157 @@ function sendTerminalMessage(socket, message) {
68175
68180
  socket.send(JSON.stringify(message));
68176
68181
  }
68177
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
+ }
68178
68302
  function handleTerminalSpawn(msg, socket, cwd2) {
68179
68303
  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");
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}`);
68220
68314
  sendTerminalMessage(socket, {
68221
- type: "terminal_exited",
68315
+ type: "terminal_spawned",
68222
68316
  terminal_id,
68223
- exitCode: 1
68317
+ pid: existing.pid
68224
68318
  });
68225
68319
  return;
68226
68320
  }
68227
- const session = {
68228
- process: proc2,
68229
- terminal,
68230
- terminalId: terminal_id,
68231
- spawnedAt: Date.now()
68232
- };
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);
68233
68328
  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
- });
68329
+ console.log(`[Terminal] Session stored for terminal_id=${terminal_id}, pid=${session.pid}`);
68250
68330
  sendTerminalMessage(socket, {
68251
68331
  type: "terminal_spawned",
68252
68332
  terminal_id,
68253
- pid: proc2.pid
68333
+ pid: session.pid
68254
68334
  });
68255
68335
  } catch (error) {
68256
68336
  console.error("[Terminal] Failed to spawn PTY:", error);
@@ -68262,16 +68342,10 @@ function handleTerminalSpawn(msg, socket, cwd2) {
68262
68342
  }
68263
68343
  }
68264
68344
  function handleTerminalInput(msg) {
68265
- const session = terminals.get(msg.terminal_id);
68266
- if (session) {
68267
- session.terminal.write(msg.data);
68268
- }
68345
+ terminals.get(msg.terminal_id)?.write(msg.data);
68269
68346
  }
68270
68347
  function handleTerminalResize(msg) {
68271
- const session = terminals.get(msg.terminal_id);
68272
- if (session) {
68273
- session.terminal.resize(msg.cols, msg.rows);
68274
- }
68348
+ terminals.get(msg.terminal_id)?.resize(msg.cols, msg.rows);
68275
68349
  }
68276
68350
  function handleTerminalKill(msg) {
68277
68351
  const session = terminals.get(msg.terminal_id);
@@ -68284,11 +68358,8 @@ function handleTerminalKill(msg) {
68284
68358
  function killTerminal(terminalId) {
68285
68359
  const session = terminals.get(terminalId);
68286
68360
  if (session) {
68287
- console.log(`[Terminal] killTerminal: terminalId=${terminalId}, pid=${session.process.pid}`);
68288
- try {
68289
- session.terminal.close();
68290
- } catch {}
68291
- session.process.kill();
68361
+ console.log(`[Terminal] killTerminal: terminalId=${terminalId}, pid=${session.pid}`);
68362
+ session.kill();
68292
68363
  terminals.delete(terminalId);
68293
68364
  }
68294
68365
  }
@@ -68297,8 +68368,10 @@ function killAllTerminals() {
68297
68368
  killTerminal(id);
68298
68369
  }
68299
68370
  }
68300
- var terminals;
68371
+ var IS_BUN, FLUSH_INTERVAL_MS = 16, MAX_BUFFER_BYTES, terminals;
68301
68372
  var init_terminalHandler = __esm(() => {
68373
+ IS_BUN = typeof Bun !== "undefined";
68374
+ MAX_BUFFER_BYTES = 64 * 1024;
68302
68375
  terminals = new Map;
68303
68376
  });
68304
68377
 
@@ -69172,7 +69245,7 @@ function resolvePendingApprovalResolver(runtime, response) {
69172
69245
  runtime.pendingApprovalResolvers.delete(requestId);
69173
69246
  runtime.listener.approvalRuntimeKeyByRequestId.delete(requestId);
69174
69247
  if (runtime.pendingApprovalResolvers.size === 0) {
69175
- setLoopStatus(runtime, runtime.isProcessing ? "PROCESSING_API_RESPONSE" : "WAITING_ON_INPUT");
69248
+ setLoopStatus(runtime, "WAITING_ON_INPUT");
69176
69249
  }
69177
69250
  pending.resolve(response);
69178
69251
  emitLoopStatusIfOpen(runtime.listener, {
@@ -69196,7 +69269,7 @@ function rejectPendingApprovalResolvers(runtime, reason) {
69196
69269
  runtime.listener.approvalRuntimeKeyByRequestId.delete(requestId);
69197
69270
  }
69198
69271
  }
69199
- setLoopStatus(runtime, runtime.isProcessing ? "PROCESSING_API_RESPONSE" : "WAITING_ON_INPUT");
69272
+ setLoopStatus(runtime, "WAITING_ON_INPUT");
69200
69273
  emitLoopStatusIfOpen(runtime.listener, {
69201
69274
  agent_id: runtime.agentId,
69202
69275
  conversation_id: runtime.conversationId
@@ -69857,12 +69930,19 @@ function mergeQueuedTurnInput(queued, options) {
69857
69930
 
69858
69931
  // src/websocket/helpers/listenerQueueAdapter.ts
69859
69932
  function getListenerBlockedReason(c) {
69860
- if (c.pendingApprovalsLen > 0)
69861
- return "pending_approvals";
69862
69933
  if (c.cancelRequested)
69863
69934
  return "interrupt_in_progress";
69935
+ if (c.pendingApprovalsLen > 0)
69936
+ return "pending_approvals";
69864
69937
  if (c.isRecoveringApprovals)
69865
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
+ }
69866
69946
  if (c.isProcessing)
69867
69947
  return "runtime_busy";
69868
69948
  return null;
@@ -69876,7 +69956,8 @@ __export(exports_queue, {
69876
69956
  normalizeMessageContentImages: () => normalizeMessageContentImages,
69877
69957
  normalizeInboundMessages: () => normalizeInboundMessages,
69878
69958
  getQueueItemsScope: () => getQueueItemsScope,
69879
- getQueueItemScope: () => getQueueItemScope
69959
+ getQueueItemScope: () => getQueueItemScope,
69960
+ consumeQueuedTurn: () => consumeQueuedTurn
69880
69961
  });
69881
69962
  function getQueueItemScope(item) {
69882
69963
  if (!item) {
@@ -69895,6 +69976,9 @@ function getQueueItemsScope(items) {
69895
69976
  const sameScope = items.every((item) => (item.agentId ?? null) === (first.agentId ?? null) && (item.conversationId ?? null) === (first.conversationId ?? null));
69896
69977
  return sameScope ? getQueueItemScope(first) : {};
69897
69978
  }
69979
+ function hasSameQueueScope(a, b) {
69980
+ return (a.agentId ?? null) === (b.agentId ?? null) && (a.conversationId ?? null) === (b.conversationId ?? null);
69981
+ }
69898
69982
  function mergeDequeuedBatchContent(items) {
69899
69983
  const queuedInputs = [];
69900
69984
  for (const item of items) {
@@ -70012,12 +70096,46 @@ function buildQueuedTurnMessage(runtime, batch) {
70012
70096
  function shouldQueueInboundMessage(parsed) {
70013
70097
  return parsed.messages.some((payload) => ("content" in payload));
70014
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
+ }
70015
70132
  function computeListenerQueueBlockedReason(runtime) {
70016
70133
  const activeScope = resolveRuntimeScope(runtime.listener, {
70017
70134
  agent_id: runtime.agentId,
70018
70135
  conversation_id: runtime.conversationId
70019
70136
  });
70020
70137
  return getListenerBlockedReason({
70138
+ loopStatus: runtime.loopStatus,
70021
70139
  isProcessing: runtime.isProcessing,
70022
70140
  pendingApprovalsLen: activeScope ? getPendingControlRequestCount(runtime.listener, activeScope) : 0,
70023
70141
  cancelRequested: runtime.cancelRequested,
@@ -70039,18 +70157,11 @@ async function drainQueuedMessages(runtime, socket, opts, processQueuedTurn) {
70039
70157
  runtime.queueRuntime.tryDequeue(blockedReason);
70040
70158
  return;
70041
70159
  }
70042
- const queueLen = runtime.queueRuntime.length;
70043
- if (queueLen === 0) {
70044
- return;
70045
- }
70046
- const dequeuedBatch = runtime.queueRuntime.consumeItems(queueLen);
70047
- if (!dequeuedBatch) {
70160
+ const consumedQueuedTurn = consumeQueuedTurn(runtime);
70161
+ if (!consumedQueuedTurn) {
70048
70162
  return;
70049
70163
  }
70050
- const queuedTurn = buildQueuedTurnMessage(runtime, dequeuedBatch);
70051
- if (!queuedTurn) {
70052
- continue;
70053
- }
70164
+ const { dequeuedBatch, queuedTurn } = consumedQueuedTurn;
70054
70165
  emitDequeuedUserMessage(socket, runtime, queuedTurn, dequeuedBatch);
70055
70166
  const preTurnStatus = getListenerStatus(runtime.listener) === "processing" ? "processing" : "receiving";
70056
70167
  if (opts.connectionId && runtime.listener.lastEmittedStatus !== preTurnStatus) {
@@ -70085,6 +70196,7 @@ function scheduleQueuePump(runtime, socket, opts, processQueuedTurn) {
70085
70196
  });
70086
70197
  }
70087
70198
  var init_queue = __esm(async () => {
70199
+ init_queueRuntime();
70088
70200
  init_runtime();
70089
70201
  await __promiseAll([
70090
70202
  init_imageResize(),
@@ -74710,17 +74822,26 @@ async function resolveRecoveredApprovalResponse(runtime, socket, response, proce
74710
74822
  runtime.activeAbortController = null;
74711
74823
  setLoopStatus(runtime, "SENDING_API_REQUEST", scope);
74712
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
+ }
74713
74839
  await processTurn({
74714
74840
  type: "message",
74715
74841
  agentId: recovered.agentId,
74716
74842
  conversationId: recovered.conversationId,
74717
- messages: [
74718
- {
74719
- type: "approval",
74720
- approvals: approvalResults
74721
- }
74722
- ]
74723
- }, socket, runtime, opts?.onStatusChange, opts?.connectionId, `batch-recovered-${crypto.randomUUID()}`);
74843
+ messages: continuationMessages
74844
+ }, socket, runtime, opts?.onStatusChange, opts?.connectionId, continuationBatchId);
74724
74845
  clearRecoveredApprovalState(runtime);
74725
74846
  return true;
74726
74847
  } catch (error) {
@@ -74753,7 +74874,8 @@ var init_recovery = __esm(async () => {
74753
74874
  init_stream(),
74754
74875
  init_approval(),
74755
74876
  init_interrupts(),
74756
- init_protocol_outbound()
74877
+ init_protocol_outbound(),
74878
+ init_queue()
74757
74879
  ]);
74758
74880
  });
74759
74881
 
@@ -74877,9 +74999,10 @@ function markAwaitingAcceptedApprovalContinuationRunId(runtime, input) {
74877
74999
  runtime.activeRunId = null;
74878
75000
  }
74879
75001
  }
74880
- async function resolveStaleApprovals(runtime, socket, abortSignal) {
75002
+ async function resolveStaleApprovals(runtime, socket, abortSignal, deps = {}) {
74881
75003
  if (!runtime.agentId)
74882
75004
  return null;
75005
+ const getResumeDataImpl = deps.getResumeData ?? getResumeData2;
74883
75006
  const client = await getClient2();
74884
75007
  let agent;
74885
75008
  try {
@@ -74893,7 +75016,7 @@ async function resolveStaleApprovals(runtime, socket, abortSignal) {
74893
75016
  const requestedConversationId = runtime.conversationId !== "default" ? runtime.conversationId : undefined;
74894
75017
  let resumeData;
74895
75018
  try {
74896
- resumeData = await getResumeData2(client, agent, requestedConversationId, {
75019
+ resumeData = await getResumeDataImpl(client, agent, requestedConversationId, {
74897
75020
  includeMessageHistory: false
74898
75021
  });
74899
75022
  } catch (err) {
@@ -75014,7 +75137,19 @@ async function resolveStaleApprovals(runtime, socket, abortSignal) {
75014
75137
  conversationId: recoveryConversationId
75015
75138
  });
75016
75139
  emitInterruptToolReturnMessage(socket, runtime, approvalResults, runtime.activeRunId ?? undefined, "tool-return");
75017
- 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, {
75018
75153
  agentId: runtime.agentId ?? undefined,
75019
75154
  streamTokens: true,
75020
75155
  background: true,
@@ -75023,6 +75158,7 @@ async function resolveStaleApprovals(runtime, socket, abortSignal) {
75023
75158
  if (!recoveryStream) {
75024
75159
  throw new Error("Approval recovery send resolved without a continuation stream");
75025
75160
  }
75161
+ setLoopStatus(runtime, "PROCESSING_API_RESPONSE", scope);
75026
75162
  const drainResult = await drainRecoveryStreamWithEmission(recoveryStream, socket, runtime, {
75027
75163
  agentId: runtime.agentId ?? undefined,
75028
75164
  conversationId: recoveryConversationId,
@@ -75317,6 +75453,7 @@ var init_send = __esm(async () => {
75317
75453
  init_approval(),
75318
75454
  init_interrupts(),
75319
75455
  init_protocol_outbound(),
75456
+ init_queue(),
75320
75457
  init_recovery()
75321
75458
  ]);
75322
75459
  });
@@ -76155,6 +76292,7 @@ async function handleApprovalStop(params) {
76155
76292
  terminated: true,
76156
76293
  stream: null,
76157
76294
  currentInput,
76295
+ dequeuedBatchId,
76158
76296
  pendingNormalizationInterruptedToolCallIds: [],
76159
76297
  turnToolContextId,
76160
76298
  lastExecutionResults: null,
@@ -76231,11 +76369,10 @@ async function handleApprovalStop(params) {
76231
76369
  });
76232
76370
  }
76233
76371
  } else {
76234
- const denyReason = responseBody.error;
76235
76372
  decisions.push({
76236
76373
  type: "deny",
76237
76374
  approval: ac.approval,
76238
- reason: denyReason
76375
+ reason: responseBody.error
76239
76376
  });
76240
76377
  }
76241
76378
  }
@@ -76250,7 +76387,7 @@ async function handleApprovalStop(params) {
76250
76387
  agent_id: agentId,
76251
76388
  conversation_id: conversationId
76252
76389
  });
76253
- const executionRunId = runId || runtime.activeRunId || params.msgRunIds[params.msgRunIds.length - 1];
76390
+ const executionRunId = runId || runtime.activeRunId || msgRunIds[msgRunIds.length - 1];
76254
76391
  emitToolExecutionStartedEvents(socket, runtime, {
76255
76392
  toolCallIds: lastExecutingToolCallIds,
76256
76393
  runId: executionRunId,
@@ -76282,6 +76419,14 @@ async function handleApprovalStop(params) {
76282
76419
  approvals: persistedExecutionResults
76283
76420
  }
76284
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
+ }
76285
76430
  setLoopStatus(runtime, "SENDING_API_REQUEST", {
76286
76431
  agent_id: agentId,
76287
76432
  conversation_id: conversationId
@@ -76292,6 +76437,7 @@ async function handleApprovalStop(params) {
76292
76437
  terminated: true,
76293
76438
  stream: null,
76294
76439
  currentInput: nextInput,
76440
+ dequeuedBatchId: continuationBatchId,
76295
76441
  pendingNormalizationInterruptedToolCallIds: [],
76296
76442
  turnToolContextId,
76297
76443
  lastExecutionResults,
@@ -76325,6 +76471,7 @@ async function handleApprovalStop(params) {
76325
76471
  terminated: false,
76326
76472
  stream: stream2,
76327
76473
  currentInput: nextInput,
76474
+ dequeuedBatchId: continuationBatchId,
76328
76475
  pendingNormalizationInterruptedToolCallIds: [],
76329
76476
  turnToolContextId: null,
76330
76477
  lastExecutionResults,
@@ -76342,6 +76489,7 @@ var init_turn_approval = __esm(async () => {
76342
76489
  init_approval(),
76343
76490
  init_interrupts(),
76344
76491
  init_protocol_outbound(),
76492
+ init_queue(),
76345
76493
  init_recovery(),
76346
76494
  init_send()
76347
76495
  ]);
@@ -76362,6 +76510,7 @@ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, conne
76362
76510
  let llmApiErrorRetries = 0;
76363
76511
  let emptyResponseRetries = 0;
76364
76512
  let lastApprovalContinuationAccepted = false;
76513
+ let activeDequeuedBatchId = dequeuedBatchId;
76365
76514
  let lastExecutionResults = null;
76366
76515
  let lastExecutingToolCallIds = [];
76367
76516
  let lastNeedsUserInputToolCallIds = [];
@@ -76723,7 +76872,7 @@ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, conne
76723
76872
  conversationId,
76724
76873
  turnWorkingDirectory,
76725
76874
  turnPermissionModeState,
76726
- dequeuedBatchId,
76875
+ dequeuedBatchId: activeDequeuedBatchId,
76727
76876
  runId,
76728
76877
  msgRunIds,
76729
76878
  currentInput,
@@ -76736,6 +76885,7 @@ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, conne
76736
76885
  }
76737
76886
  stream2 = approvalResult.stream;
76738
76887
  currentInput = approvalResult.currentInput;
76888
+ activeDequeuedBatchId = approvalResult.dequeuedBatchId;
76739
76889
  pendingNormalizationInterruptedToolCallIds = approvalResult.pendingNormalizationInterruptedToolCallIds;
76740
76890
  turnToolContextId = approvalResult.turnToolContextId;
76741
76891
  lastExecutionResults = approvalResult.lastExecutionResults;
@@ -76957,6 +77107,51 @@ async function handleApprovalResponseInput(listener, params, deps = {
76957
77107
  }
76958
77108
  return false;
76959
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
+ }
76960
77155
  async function handleCwdChange(msg, socket, runtime) {
76961
77156
  const conversationId = normalizeConversationId(msg.conversationId);
76962
77157
  const agentId = normalizeCwdAgentId(msg.agentId);
@@ -77241,36 +77436,15 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
77241
77436
  return;
77242
77437
  }
77243
77438
  if (parsed.type === "change_device_state") {
77244
- if (runtime !== getActiveRuntime() || runtime.intentionallyClosed) {
77245
- return;
77246
- }
77247
- const scope = {
77248
- agent_id: parsed.payload.agent_id ?? parsed.runtime.agent_id ?? undefined,
77249
- conversation_id: parsed.payload.conversation_id ?? parsed.runtime.conversation_id ?? undefined
77250
- };
77251
- const scopedRuntime = getOrCreateScopedRuntime(runtime, scope.agent_id, scope.conversation_id);
77252
- const shouldTrackCommand = !scopedRuntime.isProcessing && getPendingControlRequestCount(runtime, scope) === 0;
77253
- if (shouldTrackCommand) {
77254
- setLoopStatus(scopedRuntime, "EXECUTING_COMMAND", scope);
77255
- }
77256
- try {
77257
- if (parsed.payload.mode) {
77258
- handleModeChange({ mode: parsed.payload.mode }, socket, runtime, scope);
77259
- }
77260
- if (parsed.payload.cwd) {
77261
- await handleCwdChange({
77262
- agentId: scope.agent_id ?? null,
77263
- conversationId: scope.conversation_id ?? null,
77264
- cwd: parsed.payload.cwd
77265
- }, socket, scopedRuntime);
77266
- } else if (!parsed.payload.mode) {
77267
- emitDeviceStatusUpdate(socket, runtime, scope);
77268
- }
77269
- } finally {
77270
- if (shouldTrackCommand) {
77271
- setLoopStatus(scopedRuntime, "WAITING_ON_INPUT", scope);
77272
- }
77273
- }
77439
+ await handleChangeDeviceStateInput(runtime, {
77440
+ command: parsed,
77441
+ socket,
77442
+ opts: {
77443
+ onStatusChange: opts.onStatusChange,
77444
+ connectionId: opts.connectionId
77445
+ },
77446
+ processQueuedTurn
77447
+ });
77274
77448
  return;
77275
77449
  }
77276
77450
  if (parsed.type === "abort_message") {
@@ -77626,10 +77800,13 @@ var init_client4 = __esm(async () => {
77626
77800
  shouldAttemptPostStopApprovalRecovery,
77627
77801
  getApprovalContinuationRecoveryDisposition,
77628
77802
  markAwaitingAcceptedApprovalContinuationRunId,
77803
+ resolveStaleApprovals,
77629
77804
  normalizeMessageContentImages,
77630
77805
  normalizeInboundMessages,
77806
+ consumeQueuedTurn,
77631
77807
  handleIncomingMessage,
77632
77808
  handleApprovalResponseInput,
77809
+ handleChangeDeviceStateInput,
77633
77810
  scheduleQueuePump,
77634
77811
  recoverApprovalStateForSync,
77635
77812
  clearRecoveredApprovalStateForScope: (runtime, scope) => clearRecoveredApprovalStateForScope(asListenerRuntimeForTests(runtime), scope),
@@ -141191,6 +141368,7 @@ var ANTHROPIC_DEFAULT_TOOLS2 = [
141191
141368
  "Glob",
141192
141369
  "Grep",
141193
141370
  "TaskStop",
141371
+ "memory",
141194
141372
  "Read",
141195
141373
  "Skill",
141196
141374
  "Task",
@@ -141201,6 +141379,7 @@ var OPENAI_PASCAL_TOOLS2 = [
141201
141379
  "AskUserQuestion",
141202
141380
  "EnterPlanMode",
141203
141381
  "ExitPlanMode",
141382
+ "memory",
141204
141383
  "Task",
141205
141384
  "TaskOutput",
141206
141385
  "TaskStop",
@@ -141214,6 +141393,7 @@ var GEMINI_PASCAL_TOOLS2 = [
141214
141393
  "AskUserQuestion",
141215
141394
  "EnterPlanMode",
141216
141395
  "ExitPlanMode",
141396
+ "memory",
141217
141397
  "Skill",
141218
141398
  "Task",
141219
141399
  "RunShellCommand",
@@ -142972,4 +143152,4 @@ Error during initialization: ${message}`);
142972
143152
  }
142973
143153
  main();
142974
143154
 
142975
- //# debugId=9B4D5740B8FA924E64756E2164756E21
143155
+ //# debugId=D1C8E311510A3C4964756E2164756E21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letta-ai/letta-code",
3
- "version": "0.19.3",
3
+ "version": "0.19.4",
4
4
  "description": "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -73,7 +73,7 @@
73
73
  "test:update-chain:manual": "bun run src/tests/update-chain-smoke.ts --mode manual",
74
74
  "test:update-chain:startup": "bun run src/tests/update-chain-smoke.ts --mode startup",
75
75
  "prepublishOnly": "bun run build",
76
- "postinstall": "node scripts/postinstall-patches.js || echo letta: vendor patches skipped"
76
+ "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"
77
77
  },
78
78
  "lint-staged": {
79
79
  "*.{ts,tsx,js,jsx,json}": [