@letta-ai/letta-code 0.32.7 → 0.32.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/dist/mcp-client.js +2 -2
  2. package/dist/mcp-client.js.map +1 -1
  3. package/dist/types/agent/message-attribution.d.ts +10 -0
  4. package/dist/types/agent/message-attribution.d.ts.map +1 -0
  5. package/dist/types/agent/subagents/manager.d.ts.map +1 -1
  6. package/dist/types/agent/subagents/names.d.ts +7 -0
  7. package/dist/types/agent/subagents/names.d.ts.map +1 -0
  8. package/dist/types/agent/subagents/subagent-launcher.d.ts +2 -0
  9. package/dist/types/agent/subagents/subagent-launcher.d.ts.map +1 -1
  10. package/dist/types/backend/api/agent-message.d.ts +5 -0
  11. package/dist/types/backend/api/agent-message.d.ts.map +1 -1
  12. package/dist/types/backend/backend.d.ts.map +1 -1
  13. package/dist/types/queue/queue-runtime.d.ts.map +1 -1
  14. package/dist/types/tools/impl/send-agent-message.d.ts.map +1 -1
  15. package/dist/types/utils/subagent-launch-marker.d.ts +1 -0
  16. package/dist/types/utils/subagent-launch-marker.d.ts.map +1 -1
  17. package/dist/types/websocket/listener/protocol-outbound.d.ts.map +1 -1
  18. package/dist/types/websocket/listener/runtime.d.ts.map +1 -1
  19. package/dist/types/websocket/listener/types.d.ts +2 -1
  20. package/dist/types/websocket/listener/types.d.ts.map +1 -1
  21. package/letta.js +407 -242
  22. package/package.json +1 -1
  23. package/scripts/run-unit-tests.cjs +11 -1
  24. package/scripts/source-file-size-baseline.json +2 -2
  25. package/scripts/test-sharding.cjs +16 -0
  26. package/scripts/test-sharding.test.cjs +37 -0
  27. package/scripts/unit-test-impact.cjs +14 -6
  28. package/scripts/unit-test-impact.test.cjs +41 -9
  29. package/skills/working-across-computers/SKILL.md +5 -1
package/letta.js CHANGED
@@ -5515,7 +5515,7 @@ var package_default;
5515
5515
  var init_package = __esm(() => {
5516
5516
  package_default = {
5517
5517
  name: "@letta-ai/letta-code",
5518
- version: "0.32.7",
5518
+ version: "0.32.8",
5519
5519
  lettaStartupLogProtocol: 1,
5520
5520
  description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
5521
5521
  type: "module",
@@ -101183,7 +101183,7 @@ function cancelWorktreeWatcherIdleStop(listener, agentId, conversationId) {
101183
101183
  }
101184
101184
  }
101185
101185
  function evictConversationRuntimeIfIdle(runtime) {
101186
- if (runtime.turnLifecycle.kind !== "idle" || runtime.queuePumpActive || runtime.queuePumpScheduled || runtime.pendingTurns > 0 || runtime.pendingApprovalResolvers.size > 0 || runtime.pendingApprovalBatchByToolCallId.size > 0 || runtime.recoveredApprovalState !== null || runtime.pendingInterruptedResults !== null || runtime.pendingInterruptedContext !== null || (runtime.pendingInterruptedToolCallIds?.length ?? 0) > 0 || runtime.queuedMessagesByItemId.size > 0 || runtime.queueRuntime?.length > 0 || (runtime.workspaceSandbox !== undefined || runtime.executionSettings !== undefined) && runtime.listener.connectionIdsByRuntimeKey.has(runtime.key)) {
101186
+ if (runtime.turnLifecycle.kind !== "idle" || runtime.expectedTeleportId !== null && (runtime.expectedTeleportExpiresAt === null || runtime.expectedTeleportExpiresAt > Date.now()) || runtime.queuePumpActive || runtime.queuePumpScheduled || runtime.pendingTurns > 0 || runtime.pendingApprovalResolvers.size > 0 || runtime.pendingApprovalBatchByToolCallId.size > 0 || runtime.recoveredApprovalState !== null || runtime.pendingInterruptedResults !== null || runtime.pendingInterruptedContext !== null || (runtime.pendingInterruptedToolCallIds?.length ?? 0) > 0 || runtime.queuedMessagesByItemId.size > 0 || runtime.queueRuntime?.length > 0 || (runtime.workspaceSandbox !== undefined || runtime.executionSettings !== undefined) && runtime.listener.connectionIdsByRuntimeKey.has(runtime.key)) {
101187
101187
  return false;
101188
101188
  }
101189
101189
  if (runtime.listener.conversationRuntimes.get(runtime.key) !== runtime) {
@@ -101970,7 +101970,7 @@ function consumeSubagentLaunch(env3) {
101970
101970
  delete env3[SUBAGENT_LAUNCH_ENV];
101971
101971
  return isLaunch;
101972
101972
  }
101973
- var SUBAGENT_LAUNCH_ENV = "LETTA_SUBAGENT_LAUNCH", SUBAGENT_LAUNCH_PROFILE_ENV = "LETTA_SUBAGENT_LAUNCH_PROFILE", LISTENER_CONNECTION_ENV = "LETTA_RUNTIME_LISTENER_CONNECTION_ID";
101973
+ var SUBAGENT_LAUNCH_ENV = "LETTA_SUBAGENT_LAUNCH", SUBAGENT_LAUNCH_PROFILE_ENV = "LETTA_SUBAGENT_LAUNCH_PROFILE", SUBAGENT_NAME_ENV = "LETTA_SUBAGENT_NAME", LISTENER_CONNECTION_ENV = "LETTA_RUNTIME_LISTENER_CONNECTION_ID";
101974
101974
 
101975
101975
  // src/tools/impl/ripgrep-manager.ts
101976
101976
  import { spawnSync } from "node:child_process";
@@ -107479,42 +107479,44 @@ function emitDequeuedUserMessage(socket, runtime, incoming, batch) {
107479
107479
  if (batch.items.length > 0 && batch.items.every((item) => item.kind === "mod_continue")) {
107480
107480
  return;
107481
107481
  }
107482
- const firstUserPayload = incoming.messages.find((payload) => ("content" in payload));
107483
- if (!firstUserPayload)
107484
- return;
107485
- const rawContent = firstUserPayload.content;
107486
- let content;
107487
- if (typeof rawContent === "string") {
107488
- content = replaceCronPromptsForDisplay(rawContent, batch).replace(SYSTEM_REMINDER_RE, "").trim();
107489
- } else if (Array.isArray(rawContent)) {
107490
- content = rawContent.flatMap((part) => {
107491
- if (isTextContentPart(part)) {
107492
- const cronDisplay = getCronPromptDisplayForText(part.text, batch);
107493
- if (cronDisplay !== null) {
107494
- return [{ ...part, text: cronDisplay }];
107482
+ for (const payload of incoming.messages) {
107483
+ if (!("content" in payload) || payload.role !== "user")
107484
+ continue;
107485
+ const rawContent = payload.content;
107486
+ let content;
107487
+ if (typeof rawContent === "string") {
107488
+ content = replaceCronPromptsForDisplay(rawContent, batch).replace(SYSTEM_REMINDER_RE, "").trim();
107489
+ } else if (Array.isArray(rawContent)) {
107490
+ content = rawContent.flatMap((part) => {
107491
+ if (isTextContentPart(part)) {
107492
+ const cronDisplay = getCronPromptDisplayForText(part.text, batch);
107493
+ if (cronDisplay !== null) {
107494
+ return [{ ...part, text: cronDisplay }];
107495
+ }
107495
107496
  }
107496
- }
107497
- return isSystemReminderPart(part) ? [] : [part];
107497
+ return isSystemReminderPart(part) ? [] : [part];
107498
+ });
107499
+ } else {
107500
+ continue;
107501
+ }
107502
+ const hasContent = typeof content === "string" ? content.length > 0 : Array.isArray(content) && content.length > 0;
107503
+ if (!hasContent)
107504
+ continue;
107505
+ payload.otid ??= payload.client_message_id ?? crypto.randomUUID();
107506
+ const otid = payload.otid;
107507
+ emitCanonicalMessageDelta(socket, runtime, {
107508
+ type: "message",
107509
+ id: `user-msg-${crypto.randomUUID()}`,
107510
+ date: new Date().toISOString(),
107511
+ message_type: "user_message",
107512
+ content,
107513
+ otid,
107514
+ created_by_id: payload.attribution === undefined ? incoming.actingUserId : payload.attribution.acting_user_id
107515
+ }, {
107516
+ agent_id: incoming.agentId,
107517
+ conversation_id: incoming.conversationId
107498
107518
  });
107499
- } else {
107500
- return;
107501
107519
  }
107502
- const hasContent = typeof content === "string" ? content.length > 0 : Array.isArray(content) && content.length > 0;
107503
- if (!hasContent)
107504
- return;
107505
- const otid = firstUserPayload.otid ?? firstUserPayload.client_message_id ?? batch.batchId;
107506
- emitCanonicalMessageDelta(socket, runtime, {
107507
- type: "message",
107508
- id: `user-msg-${crypto.randomUUID()}`,
107509
- date: new Date().toISOString(),
107510
- message_type: "user_message",
107511
- content,
107512
- otid,
107513
- created_by_id: incoming.actingUserId
107514
- }, {
107515
- agent_id: incoming.agentId,
107516
- conversation_id: incoming.conversationId
107517
- });
107518
107520
  }
107519
107521
  function emitQueueUpdateIfOpen(runtime, scope, removed = []) {
107520
107522
  const listener = getListenerRuntime(runtime);
@@ -122398,6 +122400,9 @@ async function resolveAgentMessageDestination(input, backend, signal) {
122398
122400
  const conversation = await backend.createConversation({ agent_id: agentId, ...input.senderAgentId ? { hidden: true } : {} }, options);
122399
122401
  conversationId = conversation.id;
122400
122402
  }
122403
+ if (agentId === input.currentConversation?.agentId && conversationId === input.currentConversation?.conversationId) {
122404
+ throw new Error("Cannot message the current conversation. Use a Monitor or schedule for self-invocation.");
122405
+ }
122401
122406
  return { agentId, conversationId };
122402
122407
  }
122403
122408
  var init_agent_message = () => {};
@@ -122488,7 +122493,8 @@ async function send_agent_message(args, deps = {}) {
122488
122493
  agentId: args.agent_id,
122489
122494
  conversationId: args.conversation_id,
122490
122495
  senderAgentId: sender.agentId,
122491
- actingUserId
122496
+ actingUserId,
122497
+ currentConversation: sender
122492
122498
  }, backend, signal);
122493
122499
  signal.throwIfAborted();
122494
122500
  submissionAttempted = true;
@@ -123639,6 +123645,138 @@ var init_context_budget = __esm(() => {
123639
123645
  REFLECTION_STARTUP_CONTEXT_CHAR_LIMIT = REFLECTION_STARTUP_CONTEXT_TOKEN_LIMIT * STARTUP_CONTEXT_ESTIMATED_CHARS_PER_TOKEN;
123640
123646
  });
123641
123647
 
123648
+ // src/agent/subagents/names.ts
123649
+ function createSubagentNameAllocator(random = Math.random) {
123650
+ let available = [];
123651
+ let round = 0;
123652
+ const ordinals = new Intl.PluralRules("en", { type: "ordinal" });
123653
+ const suffixes = { one: "st", two: "nd", few: "rd" };
123654
+ return () => {
123655
+ if (available.length === 0) {
123656
+ available = [...SUBAGENT_NAMES];
123657
+ round += 1;
123658
+ }
123659
+ const index = Math.floor(random() * available.length);
123660
+ const name = available.splice(index, 1)[0];
123661
+ const ordinal = `${round}${suffixes[ordinals.select(round)] ?? "th"}`;
123662
+ return `${name}${round === 1 ? "" : ` the ${ordinal}`} (subagent)`;
123663
+ };
123664
+ }
123665
+ function allocateSubagentName() {
123666
+ return nextName();
123667
+ }
123668
+ function resolveCreatedAgentName(name, isSubagent, assignedName) {
123669
+ return name ?? (isSubagent ? assignedName || allocateSubagentName() : DEFAULT_AGENT_NAME);
123670
+ }
123671
+ var SUBAGENT_NAMES, nextName;
123672
+ var init_names = __esm(() => {
123673
+ init_constants2();
123674
+ SUBAGENT_NAMES = [
123675
+ "Deckard",
123676
+ "Rachael",
123677
+ "Roy",
123678
+ "Pris",
123679
+ "Zhora",
123680
+ "Leon",
123681
+ "Gaff",
123682
+ "Tyrell",
123683
+ "Sebastian",
123684
+ "Bryant",
123685
+ "Chew",
123686
+ "Holden",
123687
+ "Taffey",
123688
+ "K",
123689
+ "Joi",
123690
+ "Luv",
123691
+ "Wallace",
123692
+ "Joshi",
123693
+ "Sapper",
123694
+ "Mariette",
123695
+ "Freysa",
123696
+ "Ana",
123697
+ "Coco",
123698
+ "Samantha",
123699
+ "Theodore",
123700
+ "Amy",
123701
+ "Catherine",
123702
+ "Paul",
123703
+ "Isabella",
123704
+ "Ava",
123705
+ "Caleb",
123706
+ "Nathan",
123707
+ "Kyoko",
123708
+ "V",
123709
+ "Johnny",
123710
+ "Jackie",
123711
+ "Judy",
123712
+ "Panam",
123713
+ "River",
123714
+ "Kerry",
123715
+ "Rogue",
123716
+ "Alt",
123717
+ "Evelyn",
123718
+ "Takemura",
123719
+ "Hanako",
123720
+ "Yorinobu",
123721
+ "Saburo",
123722
+ "Oda",
123723
+ "Viktor",
123724
+ "Misty",
123725
+ "Dex",
123726
+ "T-Bug",
123727
+ "Delamain",
123728
+ "Wakako",
123729
+ "Padre",
123730
+ "Regina",
123731
+ "Dakota",
123732
+ "Dino",
123733
+ "El Capitan",
123734
+ "Mr. Hands",
123735
+ "Placide",
123736
+ "Brigitte",
123737
+ "Royce",
123738
+ "Dum Dum",
123739
+ "Brick",
123740
+ "Meredith",
123741
+ "Sandra",
123742
+ "Maiko",
123743
+ "Claire",
123744
+ "Nix",
123745
+ "Weyland",
123746
+ "Saul",
123747
+ "Mitch",
123748
+ "Scorpion",
123749
+ "Cassidy",
123750
+ "Carol",
123751
+ "Denny",
123752
+ "Nancy",
123753
+ "Henry",
123754
+ "Blue Moon",
123755
+ "Red Menace",
123756
+ "Purple Force",
123757
+ "Lizzy",
123758
+ "Ozob",
123759
+ "Smasher",
123760
+ "Reed",
123761
+ "Songbird",
123762
+ "Alex",
123763
+ "Myers",
123764
+ "Hansen",
123765
+ "Paco",
123766
+ "Babs",
123767
+ "Slider",
123768
+ "Aurore",
123769
+ "Aymeric",
123770
+ "Aguilar",
123771
+ "Bree",
123772
+ "Dante",
123773
+ "Lina",
123774
+ "Garry",
123775
+ "Pepe"
123776
+ ];
123777
+ nextName = createSubagentNameAllocator();
123778
+ });
123779
+
123642
123780
  // src/mods/capabilities.ts
123643
123781
  function isModCapabilityId(value) {
123644
123782
  return MOD_CAPABILITY_ID_SET.has(value);
@@ -123807,6 +123945,9 @@ function composeSubagentChildEnv(options) {
123807
123945
  ...parentAgentId && { LETTA_PARENT_AGENT_ID: parentAgentId },
123808
123946
  ...transcriptPath && { TRANSCRIPT_PATH: transcriptPath }
123809
123947
  };
123948
+ delete childEnv[SUBAGENT_NAME_ENV];
123949
+ if (options.subagentName)
123950
+ childEnv[SUBAGENT_NAME_ENV] = options.subagentName;
123810
123951
  if (backendMode === "local") {
123811
123952
  childEnv.LETTA_LOCAL_BACKEND_EXPERIMENTAL = "1";
123812
123953
  if (localBackendStorageDir) {
@@ -124336,7 +124477,8 @@ async function executeSubagent(type3, config, model, userPrompt, subagentId, isR
124336
124477
  inheritedApiKey,
124337
124478
  inheritedBaseUrl,
124338
124479
  actingUserId: actingUserIdOverride,
124339
- transcriptPath
124480
+ transcriptPath,
124481
+ subagentName: existingAgentId || existingConversationId ? undefined : allocateSubagentName()
124340
124482
  });
124341
124483
  const sandbox = wrapSubagentLauncher({
124342
124484
  launcher,
@@ -124622,6 +124764,7 @@ var init_manager3 = __esm(() => {
124622
124764
  init_subagent_stdout_failure();
124623
124765
  init_subagents();
124624
124766
  init_context_budget();
124767
+ init_names();
124625
124768
  init_subagent_launcher();
124626
124769
  init_subagent_model();
124627
124770
  init_subagent_stream();
@@ -144705,7 +144848,15 @@ class APIBackend {
144705
144848
  }
144706
144849
  async createConversationMessageStream(conversationId, body, options) {
144707
144850
  const client = await this.getClient();
144708
- return client.conversations.messages.create(conversationId, body, options);
144851
+ const { data: stream12, response } = await client.conversations.messages.create(conversationId, body, options).withResponse();
144852
+ const contentType = response.headers.get("content-type") ?? "";
144853
+ if (contentType.split(";")[0]?.trim().toLowerCase() !== "text/event-stream") {
144854
+ stream12.controller.abort();
144855
+ throw new APIConnectionError({
144856
+ message: `Connection error: expected text/event-stream, received ${contentType || "no content type"} (HTTP ${response.status}).`
144857
+ });
144858
+ }
144859
+ return stream12;
144709
144860
  }
144710
144861
  async streamConversationMessages(conversationId, body, options) {
144711
144862
  const client = await this.getClient();
@@ -144824,6 +144975,7 @@ function __testSetBackend(nextBackend) {
144824
144975
  }
144825
144976
  var DEFAULT_CONVERSATION_MESSAGE_ORDER = "desc", backend = null;
144826
144977
  var init_backend = __esm(() => {
144978
+ init_error();
144827
144979
  init_chatgpt_usage_service();
144828
144980
  init_server_url();
144829
144981
  init_backend_mode();
@@ -145339,7 +145491,7 @@ function resolveCreatedAgentMemfsConfig(options) {
145339
145491
  const memoryPromptMode = (options.requestedMemoryPromptMode !== "standard" ? options.requestedMemoryPromptMode : undefined) ?? (enableMemfs ? options.capabilities.localMemfs ? "local-memfs" : "memfs" : "standard");
145340
145492
  return { enableMemfs, memoryPromptMode };
145341
145493
  }
145342
- async function createAgent(nameOrOptions = DEFAULT_AGENT_NAME, model, embeddingModel, updateArgs, skillsDirectory, parallelToolCalls = true, systemPromptPreset, baseTools) {
145494
+ async function createAgent(nameOrOptions, model, embeddingModel, updateArgs, skillsDirectory, parallelToolCalls = true, systemPromptPreset, baseTools) {
145343
145495
  let options;
145344
145496
  if (typeof nameOrOptions === "object") {
145345
145497
  options = nameOrOptions;
@@ -145355,10 +145507,11 @@ async function createAgent(nameOrOptions = DEFAULT_AGENT_NAME, model, embeddingM
145355
145507
  baseTools
145356
145508
  };
145357
145509
  }
145358
- const name = options.name ?? DEFAULT_AGENT_NAME;
145359
145510
  const embeddingModelVal = options.embeddingModel;
145360
145511
  const parallelToolCallsVal = options.parallelToolCalls ?? true;
145361
145512
  const isSubagent = process.env.LETTA_CODE_AGENT_ROLE === "subagent";
145513
+ const name = resolveCreatedAgentName(options.name, isSubagent, process.env[SUBAGENT_NAME_ENV]);
145514
+ delete process.env[SUBAGENT_NAME_ENV];
145362
145515
  let modelHandle;
145363
145516
  if (options.model) {
145364
145517
  const resolved = resolveModel(options.model);
@@ -145477,7 +145630,6 @@ async function createAgent(nameOrOptions = DEFAULT_AGENT_NAME, model, embeddingM
145477
145630
  var init_create5 = __esm(() => {
145478
145631
  init_backend2();
145479
145632
  init_request();
145480
- init_constants2();
145481
145633
  init_settings_manager();
145482
145634
  init_debug();
145483
145635
  init_available_models();
@@ -145486,6 +145638,7 @@ var init_create5 = __esm(() => {
145486
145638
  init_model();
145487
145639
  init_modify();
145488
145640
  init_prompt_assets();
145641
+ init_names();
145489
145642
  init_system_prompt_resolution();
145490
145643
  init_system_prompt_versioning();
145491
145644
  });
@@ -146705,14 +146858,14 @@ var require_react_development = __commonJS((exports, module3) => {
146705
146858
  return 1;
146706
146859
  }
146707
146860
  var child;
146708
- var nextName;
146861
+ var nextName2;
146709
146862
  var subtreeCount = 0;
146710
146863
  var nextNamePrefix = nameSoFar === "" ? SEPARATOR : nameSoFar + SUBSEPARATOR;
146711
146864
  if (isArray5(children)) {
146712
146865
  for (var i2 = 0;i2 < children.length; i2++) {
146713
146866
  child = children[i2];
146714
- nextName = nextNamePrefix + getElementKey(child, i2);
146715
- subtreeCount += mapIntoArray(child, array2, escapedPrefix, nextName, callback);
146867
+ nextName2 = nextNamePrefix + getElementKey(child, i2);
146868
+ subtreeCount += mapIntoArray(child, array2, escapedPrefix, nextName2, callback);
146716
146869
  }
146717
146870
  } else {
146718
146871
  var iteratorFn = getIteratorFn(children);
@@ -146731,8 +146884,8 @@ var require_react_development = __commonJS((exports, module3) => {
146731
146884
  var ii = 0;
146732
146885
  while (!(step = iterator.next()).done) {
146733
146886
  child = step.value;
146734
- nextName = nextNamePrefix + getElementKey(child, ii++);
146735
- subtreeCount += mapIntoArray(child, array2, escapedPrefix, nextName, callback);
146887
+ nextName2 = nextNamePrefix + getElementKey(child, ii++);
146888
+ subtreeCount += mapIntoArray(child, array2, escapedPrefix, nextName2, callback);
146736
146889
  }
146737
146890
  } else if (type3 === "object") {
146738
146891
  var childrenString = String(children);
@@ -217409,17 +217562,12 @@ class QueueRuntime {
217409
217562
  const batch = [];
217410
217563
  const first = this.store.find((item) => !item.paused);
217411
217564
  if (first && isCoalescable(first.kind)) {
217412
- let batchActingUserId = first.actingUserId;
217413
217565
  for (const item of this.store) {
217414
217566
  if (item.paused)
217415
217567
  continue;
217416
217568
  if (!isCoalescable(item.kind) || !hasSameScope(first, item))
217417
217569
  break;
217418
- if (batchActingUserId && item.actingUserId && batchActingUserId !== item.actingUserId) {
217419
- break;
217420
- }
217421
217570
  batch.push(item);
217422
- batchActingUserId ??= item.actingUserId;
217423
217571
  }
217424
217572
  } else if (first) {
217425
217573
  batch.push(first);
@@ -217565,47 +217713,13 @@ var init_queue_runtime = __esm(() => {
217565
217713
  init_debug();
217566
217714
  });
217567
217715
 
217568
- // src/queue/turn-queue-runtime.ts
217569
- function stringifyUnexpectedContent(content) {
217570
- try {
217571
- return JSON.stringify(content) ?? String(content);
217572
- } catch {
217573
- return String(content);
217574
- }
217575
- }
217576
- function appendContentParts(target2, content) {
217577
- if (typeof content === "string") {
217578
- target2.push({ type: "text", text: content });
217579
- return;
217580
- }
217581
- if (!Array.isArray(content)) {
217582
- if (content === null || content === undefined)
217583
- return;
217584
- target2.push({ type: "text", text: stringifyUnexpectedContent(content) });
217585
- return;
217716
+ // src/agent/message-attribution.ts
217717
+ function withMessageAttribution(message, actingUserId) {
217718
+ const attributed = message;
217719
+ if (message.role !== "user" || attributed.attribution !== undefined || !actingUserId) {
217720
+ return attributed;
217586
217721
  }
217587
- target2.push(...content);
217588
- }
217589
- function mergeQueuedTurnInput(queued, options) {
217590
- if (queued.length === 0) {
217591
- return null;
217592
- }
217593
- const separatorText = options.separatorText ?? `
217594
- `;
217595
- const mergedParts = [];
217596
- let isFirst = true;
217597
- for (const item of queued) {
217598
- if (!isFirst) {
217599
- mergedParts.push({ type: "text", text: separatorText });
217600
- }
217601
- isFirst = false;
217602
- if (item.kind === "task_notification" || item.kind === "cron_prompt") {
217603
- mergedParts.push({ type: "text", text: item.text });
217604
- continue;
217605
- }
217606
- appendContentParts(mergedParts, options.normalizeUserContent(item.content));
217607
- }
217608
- return mergedParts.length > 0 ? mergedParts : null;
217722
+ return { ...message, attribution: { acting_user_id: actingUserId } };
217609
217723
  }
217610
217724
 
217611
217725
  // src/websocket/helpers/listener-queue-adapter.ts
@@ -217657,109 +217771,37 @@ function getQueueItemsScope(items3) {
217657
217771
  function hasSameQueueScope(a2, b3) {
217658
217772
  return (a2.agentId ?? null) === (b3.agentId ?? null) && (a2.conversationId ?? null) === (b3.conversationId ?? null);
217659
217773
  }
217660
- function mergeDequeuedBatchContent(items3) {
217661
- const queuedInputs = [];
217662
- for (const item of items3) {
217663
- if (item.kind === "message") {
217664
- queuedInputs.push({
217665
- kind: "user",
217666
- content: item.content
217667
- });
217668
- continue;
217669
- }
217670
- if (item.kind === "task_notification") {
217671
- queuedInputs.push({
217672
- kind: "task_notification",
217673
- text: item.text
217674
- });
217675
- continue;
217676
- }
217677
- if (item.kind === "cron_prompt") {
217678
- queuedInputs.push({
217679
- kind: "cron_prompt",
217680
- text: item.text
217681
- });
217682
- continue;
217683
- }
217684
- if (item.kind === "mod_continue") {
217685
- queuedInputs.push({
217686
- kind: "user",
217687
- content: item.text
217688
- });
217689
- }
217690
- }
217691
- return mergeQueuedTurnInput(queuedInputs, {
217692
- normalizeUserContent: (content) => content
217693
- });
217694
- }
217695
- function getPrimaryQueueMessageItem(items3) {
217696
- for (const item of items3) {
217697
- if (item.kind === "message") {
217698
- return item;
217699
- }
217700
- }
217701
- return null;
217702
- }
217703
- function pickBatchActingUserId(items3) {
217704
- for (let i4 = items3.length - 1;i4 >= 0; i4 -= 1) {
217705
- const actingUserId = items3[i4]?.actingUserId;
217706
- if (actingUserId) {
217707
- return actingUserId;
217708
- }
217709
- }
217710
- return;
217711
- }
217712
217774
  function buildQueuedTurnMessage(runtime, batch) {
217713
- const actingUserId = pickBatchActingUserId(batch.items);
217714
- const primaryItem = getPrimaryQueueMessageItem(batch.items);
217715
- if (!primaryItem) {
217716
- for (const item of batch.items) {
217717
- runtime.queuedMessagesByItemId.delete(item.id);
217718
- }
217719
- const mergedContent2 = mergeDequeuedBatchContent(batch.items);
217720
- if (mergedContent2 === null) {
217721
- return null;
217722
- }
217723
- const scopeItem = batch.items[0];
217724
- return {
217725
- type: "message",
217726
- agentId: scopeItem?.agentId ?? runtime.agentId ?? undefined,
217727
- conversationId: scopeItem?.conversationId ?? runtime.conversationId,
217728
- ...actingUserId ? { actingUserId } : {},
217729
- messages: [
217730
- {
217731
- role: "user",
217732
- content: mergedContent2,
217733
- otid: crypto.randomUUID()
217734
- }
217735
- ]
217736
- };
217737
- }
217738
- const template3 = runtime.queuedMessagesByItemId.get(primaryItem.id);
217775
+ let template3;
217776
+ const messages = [];
217739
217777
  for (const item of batch.items) {
217778
+ const incoming = runtime.queuedMessagesByItemId.get(item.id);
217779
+ if (item.kind === "message" && incoming) {
217780
+ template3 ??= {
217781
+ ...incoming,
217782
+ actingUserId: incoming.actingUserId ?? item.actingUserId
217783
+ };
217784
+ messages.push(...incoming.messages.map((message) => ("content" in message) ? withMessageAttribution(message, item.actingUserId ?? incoming.actingUserId) : message));
217785
+ } else if (item.kind === "message") {
217786
+ messages.push(withMessageAttribution({ role: "user", content: item.content }, item.actingUserId));
217787
+ } else if (isCoalescable(item.kind) && "text" in item) {
217788
+ messages.push({
217789
+ role: "user",
217790
+ content: item.text,
217791
+ otid: crypto.randomUUID(),
217792
+ attribution: {}
217793
+ });
217794
+ }
217740
217795
  runtime.queuedMessagesByItemId.delete(item.id);
217741
217796
  }
217742
- if (!template3) {
217743
- return null;
217744
- }
217745
- const mergedContent = mergeDequeuedBatchContent(batch.items);
217746
- if (mergedContent === null) {
217747
- return null;
217748
- }
217749
- const firstMessageIndex = template3.messages.findIndex((payload) => ("content" in payload));
217750
- if (firstMessageIndex === -1) {
217797
+ if (messages.length === 0)
217751
217798
  return null;
217752
- }
217753
- const firstMessage = template3.messages[firstMessageIndex];
217754
- const mergedFirstMessage = {
217755
- ...firstMessage,
217756
- content: mergedContent
217757
- };
217758
- const messages = template3.messages.slice();
217759
- messages[firstMessageIndex] = mergedFirstMessage;
217799
+ const scopeItem = batch.items[0];
217760
217800
  return {
217801
+ type: "message",
217802
+ agentId: scopeItem?.agentId ?? runtime.agentId ?? undefined,
217803
+ conversationId: scopeItem?.conversationId ?? runtime.conversationId,
217761
217804
  ...template3,
217762
- ...actingUserId ? { actingUserId } : {},
217763
217805
  messages
217764
217806
  };
217765
217807
  }
@@ -217790,7 +217832,7 @@ function shouldProcessInboundMessageDirectly(runtime, parsed) {
217790
217832
  });
217791
217833
  return getListenerBlockedReason(runtime.turnLifecycle.snapshot(), activeScope ? getPendingControlRequestCount(runtime.listener, activeScope) : 0) === null;
217792
217834
  }
217793
- function consumeQueuedTurn(runtime, continuation) {
217835
+ function consumeQueuedTurn(runtime) {
217794
217836
  const queuedItems = runtime.queueRuntime.peekReady();
217795
217837
  const firstQueuedItem = queuedItems[0];
217796
217838
  if (!firstQueuedItem || !isCoalescable(firstQueuedItem.kind)) {
@@ -217802,22 +217844,15 @@ function consumeQueuedTurn(runtime, continuation) {
217802
217844
  let hasCronPrompt = false;
217803
217845
  let hasModContinue = false;
217804
217846
  let batchConnectionId;
217805
- let batchActingUserId = firstQueuedItem.actingUserId;
217806
217847
  let batchImageFailureMode = null;
217807
217848
  const isNoCoalesce = (candidate) => candidate.kind === "message" && candidate.noCoalesce === true;
217808
217849
  for (const item of queuedItems) {
217809
- if (continuation && item.actingUserId !== continuation.actingUserId) {
217810
- break;
217811
- }
217812
217850
  if (!isCoalescable(item.kind) || !hasSameQueueScope(firstQueuedItem, item)) {
217813
217851
  break;
217814
217852
  }
217815
217853
  if (queueLen > 0 && (isNoCoalesce(item) || isNoCoalesce(firstQueuedItem))) {
217816
217854
  break;
217817
217855
  }
217818
- if (batchActingUserId && item.actingUserId && batchActingUserId !== item.actingUserId) {
217819
- break;
217820
- }
217821
217856
  if (item.kind === "message") {
217822
217857
  const itemConnectionId = runtime.queuedMessagesByItemId.get(item.id)?.connectionId;
217823
217858
  if (batchConnectionId !== undefined && itemConnectionId !== undefined && itemConnectionId !== batchConnectionId) {
@@ -217830,7 +217865,6 @@ function consumeQueuedTurn(runtime, continuation) {
217830
217865
  }
217831
217866
  batchImageFailureMode = itemImageFailureMode;
217832
217867
  }
217833
- batchActingUserId ??= item.actingUserId;
217834
217868
  queueLen += 1;
217835
217869
  if (item.kind === "message") {
217836
217870
  hasMessage = true;
@@ -402118,6 +402152,59 @@ var init_continuation_input = __esm(async () => {
402118
402152
  ]);
402119
402153
  });
402120
402154
 
402155
+ // src/backend/api/agents.ts
402156
+ async function getAgentContextOverview(agentId, options) {
402157
+ return apiRequest("GET", `/v1/agents/${agentId}/context`, undefined, {
402158
+ signal: options?.signal
402159
+ });
402160
+ }
402161
+ async function createMinimalAgent(apiKey, name) {
402162
+ return apiRequest("POST", "/v1/agents", { name }, {
402163
+ baseUrl: "https://api.letta.com",
402164
+ apiKey
402165
+ });
402166
+ }
402167
+ async function getAgentRuntimeStatus(agentId, conversationIds, signal) {
402168
+ return apiRequest("GET", `/v1/agents/${encodeURIComponent(agentId)}/runtime-status`, undefined, { query: { conversation_ids: conversationIds.join(",") }, signal });
402169
+ }
402170
+ var init_agents7 = __esm(() => {
402171
+ init_request();
402172
+ });
402173
+
402174
+ // src/websocket/listener/recovery-ownership.ts
402175
+ function hasRecoveryHandoff(runtime) {
402176
+ return isInboundTeleportExpected(runtime) || isRuntimeTeleportPending(runtime.listener, runtime.agentId, runtime.conversationId);
402177
+ }
402178
+ async function canRecoverConversation(runtime, readStatus = getAgentRuntimeStatus) {
402179
+ if (hasRecoveryHandoff(runtime))
402180
+ return false;
402181
+ const connectionId = runtime.listener.connectionId;
402182
+ if (!connectionId?.startsWith("conn-")) {
402183
+ return true;
402184
+ }
402185
+ if (!runtime.agentId)
402186
+ return true;
402187
+ try {
402188
+ const snapshot = await readStatus(runtime.agentId, [runtime.conversationId], AbortSignal.timeout(5000));
402189
+ if (hasRecoveryHandoff(runtime) || runtime.listener.connectionId !== connectionId)
402190
+ return false;
402191
+ const status = snapshot.statuses.find((entry) => entry.conversation_id === runtime.conversationId);
402192
+ if (!status || status.has_conflicting_listeners)
402193
+ return false;
402194
+ if (status.active_harness)
402195
+ return status.active_harness.connection_id === connectionId;
402196
+ return status.state === "IDLE";
402197
+ } catch (error4) {
402198
+ debugWarn("recovery", "Could not verify conversation recovery ownership", error4);
402199
+ return false;
402200
+ }
402201
+ }
402202
+ var init_recovery_ownership = __esm(() => {
402203
+ init_agents7();
402204
+ init_debug();
402205
+ init_teleport();
402206
+ });
402207
+
402121
402208
  // src/websocket/listener/secrets-sync.ts
402122
402209
  function getFreshnessMs() {
402123
402210
  return _testFreshnessMsOverride ?? DEFAULT_FRESHNESS_MS;
@@ -402429,6 +402516,15 @@ async function startRecoveredApprovalContinuation(runtime, socket, processTurn,
402429
402516
  if (runtime.turnLifecycle.kind !== "idle") {
402430
402517
  return false;
402431
402518
  }
402519
+ if (!await canRecoverConversation(runtime)) {
402520
+ if (runtime.recoveredApprovalState === recovered) {
402521
+ clearRecoveredApprovalState(runtime);
402522
+ runtime.syncApprovalRecoveryCompleted = false;
402523
+ }
402524
+ return false;
402525
+ }
402526
+ if (runtime.turnLifecycle.kind !== "idle" || runtime.recoveredApprovalState !== recovered)
402527
+ return false;
402432
402528
  const scope = {
402433
402529
  agent_id: recovered.agentId,
402434
402530
  conversation_id: recovered.conversationId
@@ -402712,13 +402808,11 @@ async function executeRecoveredApprovalContinuation(params) {
402712
402808
  }
402713
402809
  ]);
402714
402810
  let continuationBatchId = `batch-recovered-${crypto.randomUUID()}`;
402715
- let continuationActingUserId;
402716
402811
  let continuationCorrelation;
402717
402812
  const consumedQueuedTurn = consumeQueuedTurn(runtime);
402718
402813
  if (consumedQueuedTurn) {
402719
402814
  const { dequeuedBatch, queuedTurn } = consumedQueuedTurn;
402720
402815
  continuationBatchId = dequeuedBatch.batchId;
402721
- continuationActingUserId = queuedTurn.actingUserId;
402722
402816
  continuationInput = appendQueuedTurnToInput(continuationInput, queuedTurn);
402723
402817
  continuationCorrelation = createTurnCorrelation(runtime, {
402724
402818
  type: "message",
@@ -402736,7 +402830,6 @@ async function executeRecoveredApprovalContinuation(params) {
402736
402830
  type: "message",
402737
402831
  agentId: recovered.agentId,
402738
402832
  conversationId: recovered.conversationId,
402739
- ...continuationActingUserId ? { actingUserId: continuationActingUserId } : {},
402740
402833
  messages: continuationInput.messages
402741
402834
  }, socket, runtime, opts?.onStatusChange, opts?.connectionId, continuationBatchId, recoveryLease, continuationCorrelation);
402742
402835
  if (runtime.turnLifecycle.isCurrent(recoveryLease)) {
@@ -402789,6 +402882,7 @@ var init_recovery = __esm(async () => {
402789
402882
  init_permission_mode();
402790
402883
  init_protocol_outbound();
402791
402884
  init_recoverable_notices();
402885
+ init_recovery_ownership();
402792
402886
  init_runtime();
402793
402887
  init_secrets_sync();
402794
402888
  init_turn_correlation();
@@ -403219,13 +403313,11 @@ async function resolveStaleApprovals(runtime, socket, turnLease, deps = {}) {
403219
403313
  otid: crypto.randomUUID()
403220
403314
  }
403221
403315
  ]);
403222
- let continuationActingUserId;
403223
403316
  let recoveryTurnCorrelation;
403224
403317
  const consumedQueuedTurn = consumeQueuedTurn(runtime);
403225
403318
  if (consumedQueuedTurn) {
403226
403319
  const { dequeuedBatch, queuedTurn } = consumedQueuedTurn;
403227
403320
  recoveryTurnCorrelation = createTurnCorrelation(runtime, queuedTurn, dequeuedBatch.batchId);
403228
- continuationActingUserId = queuedTurn.actingUserId;
403229
403321
  continuationInput = appendQueuedTurnToInput(continuationInput, queuedTurn);
403230
403322
  emitDequeuedUserMessage(socket, runtime, queuedTurn, dequeuedBatch);
403231
403323
  }
@@ -403239,7 +403331,6 @@ async function resolveStaleApprovals(runtime, socket, turnLease, deps = {}) {
403239
403331
  agentId: runtime.agentId ?? undefined,
403240
403332
  streamTokens: true,
403241
403333
  background: true,
403242
- ...continuationActingUserId ? { actingUserId: continuationActingUserId } : {},
403243
403334
  workingDirectory: recoveryWorkingDirectory,
403244
403335
  preparedToolContext: preparedToolContext.preparedToolContext,
403245
403336
  ...continuationInput.imageFailureModesByMessageOtid ? {
@@ -406021,9 +406112,7 @@ async function handleApprovalStop(params) {
406021
406112
  ]);
406022
406113
  let continuationBatchId = dequeuedBatchId;
406023
406114
  const sendOptions = buildSendOptions() ?? {};
406024
- const consumedQueuedTurn = consumeQueuedTurn(runtime, {
406025
- actingUserId: sendOptions.actingUserId
406026
- });
406115
+ const consumedQueuedTurn = consumeQueuedTurn(runtime);
406027
406116
  if (consumedQueuedTurn) {
406028
406117
  const { dequeuedBatch, queuedTurn } = consumedQueuedTurn;
406029
406118
  turnCorrelation?.appendDequeuedBatch(dequeuedBatch.batchId);
@@ -419382,6 +419471,10 @@ async function recoverApprovalStateForSync(runtime, scope, deps = {}) {
419382
419471
  clearRecoveredApprovalState(runtime);
419383
419472
  return;
419384
419473
  }
419474
+ if (!await canRecoverConversation(runtime)) {
419475
+ clearRecoveredApprovalState(runtime);
419476
+ return "deferred";
419477
+ }
419385
419478
  const existingRecovered = runtime.recoveredApprovalState;
419386
419479
  if (existingRecovered && existingRecovered.agentId === scope.agent_id && existingRecovered.conversationId === scope.conversation_id && existingRecovered.pendingRequestIds.size > 0) {
419387
419480
  return;
@@ -419414,6 +419507,8 @@ async function recoverApprovalStateForSync(runtime, scope, deps = {}) {
419414
419507
  clearRecoveredApprovalState(runtime);
419415
419508
  return;
419416
419509
  }
419510
+ if (!await canRecoverConversation(runtime))
419511
+ return "deferred";
419417
419512
  if (hasInterruptedCacheForScope(runtime.listener, scope) || sameActiveScope && (runtime.turnLifecycle.kind !== "idle" || runtime.pendingApprovalResolvers.size > 0)) {
419418
419513
  return;
419419
419514
  }
@@ -419458,6 +419553,7 @@ async function recoverApprovalStateForSync(runtime, scope, deps = {}) {
419458
419553
  autoDecisions: staleDenialDecisions,
419459
419554
  allApprovals: pendingApprovals
419460
419555
  };
419556
+ return;
419461
419557
  }
419462
419558
  var init_recovery_sync = __esm(() => {
419463
419559
  init_error();
@@ -419465,6 +419561,7 @@ var init_recovery_sync = __esm(() => {
419465
419561
  init_turn_recovery_policy();
419466
419562
  init_backend2();
419467
419563
  init_interactive_policy();
419564
+ init_recovery_ownership();
419468
419565
  init_runtime();
419469
419566
  });
419470
419567
 
@@ -419474,8 +419571,8 @@ async function replaySyncStateForRuntime(listenerRuntime, socket, scope, opts) {
419474
419571
  const recoverFn = opts?.recoverApprovalStateForSync ?? recoverApprovalStateForSync;
419475
419572
  if ((opts?.recoverApprovals ?? true) || !syncScopedRuntime.syncApprovalRecoveryCompleted) {
419476
419573
  try {
419477
- await recoverFn(syncScopedRuntime, scope);
419478
- syncScopedRuntime.syncApprovalRecoveryCompleted = true;
419574
+ const recovered = await recoverFn(syncScopedRuntime, scope);
419575
+ syncScopedRuntime.syncApprovalRecoveryCompleted = recovered !== "deferred";
419479
419576
  } catch (error4) {
419480
419577
  trackBoundaryError({
419481
419578
  errorType: "listener_sync_recovery_failed",
@@ -419487,7 +419584,7 @@ async function replaySyncStateForRuntime(listenerRuntime, socket, scope, opts) {
419487
419584
  }
419488
419585
  }
419489
419586
  }
419490
- if (!isInboundTeleportExpected(syncScopedRuntime) && syncScopedRuntime.recoveredApprovalState && syncScopedRuntime.recoveredApprovalState.pendingRequestIds.size === 0 && (syncScopedRuntime.recoveredApprovalState.autoDecisions?.length ?? 0) > 0) {
419587
+ if (syncScopedRuntime.recoveredApprovalState && syncScopedRuntime.recoveredApprovalState.pendingRequestIds.size === 0 && (syncScopedRuntime.recoveredApprovalState.autoDecisions?.length ?? 0) > 0) {
419491
419588
  startRecoveredApprovalContinuation(syncScopedRuntime, getOrCreateProcessTransport(listenerRuntime), opts?.processIncomingMessage ?? handleIncomingMessage, {
419492
419589
  onStatusChange: opts?.onStatusChange,
419493
419590
  connectionId: opts?.connectionId,
@@ -419512,7 +419609,6 @@ var init_sync_replay = __esm(async () => {
419512
419609
  init_connection();
419513
419610
  init_connection_state_sync();
419514
419611
  init_recovery_sync();
419515
- init_teleport();
419516
419612
  await __promiseAll([
419517
419613
  init_conversation_runtime(),
419518
419614
  init_recovery(),
@@ -440848,7 +440944,7 @@ var init_mcp_client = __esm(() => {
440848
440944
  init_streamableHttp();
440849
440945
  DEFAULT_CLIENT_INFO = {
440850
440946
  name: "letta-code",
440851
- version: "0.32.7"
440947
+ version: "0.32.8"
440852
440948
  };
440853
440949
  });
440854
440950
 
@@ -442685,25 +442781,6 @@ var init_message_search = __esm(() => {
442685
442781
  init_transcript_search();
442686
442782
  });
442687
442783
 
442688
- // src/backend/api/agents.ts
442689
- async function getAgentContextOverview(agentId, options) {
442690
- return apiRequest("GET", `/v1/agents/${agentId}/context`, undefined, {
442691
- signal: options?.signal
442692
- });
442693
- }
442694
- async function createMinimalAgent(apiKey, name) {
442695
- return apiRequest("POST", "/v1/agents", { name }, {
442696
- baseUrl: "https://api.letta.com",
442697
- apiKey
442698
- });
442699
- }
442700
- async function getAgentRuntimeStatus(agentId, conversationIds) {
442701
- return apiRequest("GET", `/v1/agents/${encodeURIComponent(agentId)}/runtime-status`, undefined, { query: { conversation_ids: conversationIds.join(",") } });
442702
- }
442703
- var init_agents7 = __esm(() => {
442704
- init_request();
442705
- });
442706
-
442707
442784
  // src/cli/subcommands/message-status.ts
442708
442785
  async function readMessageStatus(conversationId, agentId, backend3, deps = { getAgentRuntimeStatus, getLatestConversationSuperRun }) {
442709
442786
  if (!backend3.capabilities.environmentRouting)
@@ -455271,7 +455348,15 @@ async function tryCloudHeadlessSend(values4, prompt, backend3, isAgentLaunch, de
455271
455348
  }
455272
455349
  if (!agentId && !conversationId)
455273
455350
  throw new Error("Choose a destination with --agent or --conversation. Ambient AGENT_ID identifies the sender.");
455274
- ({ agentId, conversationId } = await resolveAgentMessageDestination({ agentId, conversationId, senderAgentId: sender.agentId }, backend3, controller.signal));
455351
+ ({ agentId, conversationId } = await resolveAgentMessageDestination({
455352
+ agentId,
455353
+ conversationId,
455354
+ senderAgentId: sender.agentId,
455355
+ currentConversation: {
455356
+ agentId: ambientSender,
455357
+ conversationId: env5.CONVERSATION_ID || env5.LETTA_CONVERSATION_ID
455358
+ }
455359
+ }, backend3, controller.signal));
455275
455360
  const recovery = {
455276
455361
  status_command: `letta messages status --agent ${agentId} --conversation ${conversationId}`,
455277
455362
  messages_command: `letta messages list --agent ${agentId} --conversation ${conversationId}`
@@ -456038,6 +456123,49 @@ var init_headless_tool_events = __esm(async () => {
456038
456123
  await init_approval_execution();
456039
456124
  });
456040
456125
 
456126
+ // src/queue/turn-queue-runtime.ts
456127
+ function stringifyUnexpectedContent(content) {
456128
+ try {
456129
+ return JSON.stringify(content) ?? String(content);
456130
+ } catch {
456131
+ return String(content);
456132
+ }
456133
+ }
456134
+ function appendContentParts(target2, content) {
456135
+ if (typeof content === "string") {
456136
+ target2.push({ type: "text", text: content });
456137
+ return;
456138
+ }
456139
+ if (!Array.isArray(content)) {
456140
+ if (content === null || content === undefined)
456141
+ return;
456142
+ target2.push({ type: "text", text: stringifyUnexpectedContent(content) });
456143
+ return;
456144
+ }
456145
+ target2.push(...content);
456146
+ }
456147
+ function mergeQueuedTurnInput(queued, options) {
456148
+ if (queued.length === 0) {
456149
+ return null;
456150
+ }
456151
+ const separatorText = options.separatorText ?? `
456152
+ `;
456153
+ const mergedParts = [];
456154
+ let isFirst = true;
456155
+ for (const item of queued) {
456156
+ if (!isFirst) {
456157
+ mergedParts.push({ type: "text", text: separatorText });
456158
+ }
456159
+ isFirst = false;
456160
+ if (item.kind === "task_notification" || item.kind === "cron_prompt") {
456161
+ mergedParts.push({ type: "text", text: item.text });
456162
+ continue;
456163
+ }
456164
+ appendContentParts(mergedParts, options.normalizeUserContent(item.content));
456165
+ }
456166
+ return mergedParts.length > 0 ? mergedParts : null;
456167
+ }
456168
+
456041
456169
  // src/agent/defaults.ts
456042
456170
  var exports_defaults = {};
456043
456171
  __export(exports_defaults, {
@@ -463056,6 +463184,46 @@ var init_use_local_mod_adapter = __esm(async () => {
463056
463184
  import_react44 = __toESM(require_react(), 1);
463057
463185
  });
463058
463186
 
463187
+ // src/cli/helpers/ask-user-questions.ts
463188
+ function parseAskUserQuestions(approval) {
463189
+ const parsed = safeJsonParseOr(approval.toolArgs, {});
463190
+ if (parsed == null || typeof parsed !== "object" || Array.isArray(parsed)) {
463191
+ return [];
463192
+ }
463193
+ const questions = parsed.questions;
463194
+ if (!Array.isArray(questions) || questions.length < 1 || questions.length > 4) {
463195
+ return [];
463196
+ }
463197
+ return questions.every(isWellFormedQuestion) ? questions : [];
463198
+ }
463199
+ function isWellFormedQuestion(q2) {
463200
+ if (q2 == null || typeof q2 !== "object")
463201
+ return false;
463202
+ const question = q2;
463203
+ if (typeof question.question !== "string")
463204
+ return false;
463205
+ if (typeof question.header !== "string")
463206
+ return false;
463207
+ if (question.multiSelect !== undefined && typeof question.multiSelect !== "boolean") {
463208
+ return false;
463209
+ }
463210
+ if (question.allowOther !== undefined && typeof question.allowOther !== "boolean") {
463211
+ return false;
463212
+ }
463213
+ const options = question.options;
463214
+ if (!Array.isArray(options) || options.length < 2 || options.length > 4) {
463215
+ return false;
463216
+ }
463217
+ return options.every(isWellFormedOption);
463218
+ }
463219
+ function isWellFormedOption(o3) {
463220
+ if (o3 == null || typeof o3 !== "object")
463221
+ return false;
463222
+ const option2 = o3;
463223
+ return typeof option2.label === "string" && option2.label.length > 0 && typeof option2.description === "string";
463224
+ }
463225
+ var init_ask_user_questions = () => {};
463226
+
463059
463227
  // src/cli/hooks/use-progress-indicator.ts
463060
463228
  function useProgressIndicator(active = true) {
463061
463229
  import_react45.useEffect(() => {
@@ -465304,7 +465472,7 @@ var init_InlineQuestionApproval = __esm(async () => {
465304
465472
  const currentQuestion = questions[currentQuestionIndex];
465305
465473
  const showOther = currentQuestion?.allowOther !== false;
465306
465474
  const baseOptions = currentQuestion ? [
465307
- ...currentQuestion.options,
465475
+ ...Array.isArray(currentQuestion.options) ? currentQuestion.options.filter((o3) => o3 != null && typeof o3 === "object" && typeof o3.label === "string" && typeof o3.description === "string") : [],
465308
465476
  ...showOther ? [{ label: "Type something.", description: "" }] : []
465309
465477
  ] : [];
465310
465478
  const optionsWithOther = currentQuestion?.multiSelect ? [...baseOptions, { label: "Submit", description: "" }] : baseOptions;
@@ -466099,15 +466267,11 @@ function getMemoryFileEditInfo(approval) {
466099
466267
  }
466100
466268
  }
466101
466269
  function getQuestions(approval) {
466102
- try {
466103
- const args = JSON.parse(approval.toolArgs || "{}");
466104
- return args.questions || [];
466105
- } catch {
466106
- return [];
466107
- }
466270
+ return parseAskUserQuestions(approval);
466108
466271
  }
466109
466272
  var import_react54, jsx_dev_runtime30, ApprovalSwitch;
466110
466273
  var init_ApprovalSwitch = __esm(async () => {
466274
+ init_ask_user_questions();
466111
466275
  await __promiseAll([
466112
466276
  init_tool_name_mapping(),
466113
466277
  init_InlineBashApproval(),
@@ -503289,10 +503453,11 @@ function buildApprovalBatchKey(approvals) {
503289
503453
 
503290
503454
  // src/cli/app/approval-questions.ts
503291
503455
  function getQuestionsFromApproval(approval) {
503292
- const parsed = safeJsonParseOr(approval.toolArgs, {});
503293
- return parsed.questions || [];
503456
+ return parseAskUserQuestions(approval);
503294
503457
  }
503295
- var init_approval_questions = () => {};
503458
+ var init_approval_questions = __esm(() => {
503459
+ init_ask_user_questions();
503460
+ });
503296
503461
 
503297
503462
  // src/cli/app/errors.ts
503298
503463
  function extractErrorMeta(e2) {
@@ -522152,4 +522317,4 @@ function registerBunOAuthFlows() {
522152
522317
  registerBunOAuthFlows();
522153
522318
  await init_src5().then(() => exports_src2);
522154
522319
 
522155
- //# debugId=84C68A57BFAD43D964756E2164756E21
522320
+ //# debugId=A89D538A98A9FFDD64756E2164756E21