@letta-ai/letta-code 0.32.6 → 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 (33) 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 +7 -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 +1 -1
  15. package/dist/types/tools/impl/send-agent-message.d.ts.map +1 -1
  16. package/dist/types/types/runtime-start-protocol.d.ts +7 -0
  17. package/dist/types/types/runtime-start-protocol.d.ts.map +1 -1
  18. package/dist/types/utils/subagent-launch-marker.d.ts +1 -0
  19. package/dist/types/utils/subagent-launch-marker.d.ts.map +1 -1
  20. package/dist/types/websocket/listener/protocol-outbound.d.ts.map +1 -1
  21. package/dist/types/websocket/listener/runtime.d.ts.map +1 -1
  22. package/dist/types/websocket/listener/types.d.ts +9 -1
  23. package/dist/types/websocket/listener/types.d.ts.map +1 -1
  24. package/letta.js +924 -726
  25. package/package.json +1 -1
  26. package/scripts/isolated-unit-tests.json +5 -0
  27. package/scripts/run-unit-tests.cjs +11 -1
  28. package/scripts/source-file-size-baseline.json +2 -2
  29. package/scripts/test-sharding.cjs +16 -0
  30. package/scripts/test-sharding.test.cjs +37 -0
  31. package/scripts/unit-test-impact.cjs +14 -6
  32. package/scripts/unit-test-impact.test.cjs +41 -9
  33. 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.6",
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) {
@@ -101244,6 +101244,8 @@ function createConversationRuntime(listener, agentId, conversationId) {
101244
101244
  pendingApprovalResolvers: new Map,
101245
101245
  recoveredApprovalState: null,
101246
101246
  syncApprovalRecoveryCompleted: false,
101247
+ expectedTeleportId: null,
101248
+ expectedTeleportExpiresAt: null,
101247
101249
  get lastStopReason() {
101248
101250
  return turnLifecycle.lastStopReason;
101249
101251
  },
@@ -101319,6 +101321,8 @@ function clearConversationRuntimeState(runtime) {
101319
101321
  runtime.pendingInterruptedResults = null;
101320
101322
  runtime.pendingInterruptedContext = null;
101321
101323
  runtime.pendingInterruptedToolCallIds = null;
101324
+ runtime.expectedTeleportId = null;
101325
+ runtime.expectedTeleportExpiresAt = null;
101322
101326
  runtime.dequeuedClientMessageIdsByBatchId.clear();
101323
101327
  runtime.continuationEpoch += 1;
101324
101328
  runtime.pendingTurns = 0;
@@ -101966,7 +101970,7 @@ function consumeSubagentLaunch(env3) {
101966
101970
  delete env3[SUBAGENT_LAUNCH_ENV];
101967
101971
  return isLaunch;
101968
101972
  }
101969
- 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";
101970
101974
 
101971
101975
  // src/tools/impl/ripgrep-manager.ts
101972
101976
  import { spawnSync } from "node:child_process";
@@ -107475,42 +107479,44 @@ function emitDequeuedUserMessage(socket, runtime, incoming, batch) {
107475
107479
  if (batch.items.length > 0 && batch.items.every((item) => item.kind === "mod_continue")) {
107476
107480
  return;
107477
107481
  }
107478
- const firstUserPayload = incoming.messages.find((payload) => ("content" in payload));
107479
- if (!firstUserPayload)
107480
- return;
107481
- const rawContent = firstUserPayload.content;
107482
- let content;
107483
- if (typeof rawContent === "string") {
107484
- content = replaceCronPromptsForDisplay(rawContent, batch).replace(SYSTEM_REMINDER_RE, "").trim();
107485
- } else if (Array.isArray(rawContent)) {
107486
- content = rawContent.flatMap((part) => {
107487
- if (isTextContentPart(part)) {
107488
- const cronDisplay = getCronPromptDisplayForText(part.text, batch);
107489
- if (cronDisplay !== null) {
107490
- 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
+ }
107491
107496
  }
107492
- }
107493
- 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
107494
107518
  });
107495
- } else {
107496
- return;
107497
107519
  }
107498
- const hasContent = typeof content === "string" ? content.length > 0 : Array.isArray(content) && content.length > 0;
107499
- if (!hasContent)
107500
- return;
107501
- const otid = firstUserPayload.otid ?? firstUserPayload.client_message_id ?? batch.batchId;
107502
- emitCanonicalMessageDelta(socket, runtime, {
107503
- type: "message",
107504
- id: `user-msg-${crypto.randomUUID()}`,
107505
- date: new Date().toISOString(),
107506
- message_type: "user_message",
107507
- content,
107508
- otid,
107509
- created_by_id: incoming.actingUserId
107510
- }, {
107511
- agent_id: incoming.agentId,
107512
- conversation_id: incoming.conversationId
107513
- });
107514
107520
  }
107515
107521
  function emitQueueUpdateIfOpen(runtime, scope, removed = []) {
107516
107522
  const listener = getListenerRuntime(runtime);
@@ -122333,6 +122339,17 @@ function actingUserRequestOptions(actingUserId) {
122333
122339
  var ACTING_USER_ID_HEADER = "X-Letta-Acting-User-Id", ACTING_USER_ID_ENV = "LETTA_ACTING_USER_ID";
122334
122340
 
122335
122341
  // src/backend/api/agent-message.ts
122342
+ function normalizeAgentMessageComputer(value) {
122343
+ if (value == null)
122344
+ return;
122345
+ if (typeof value !== "string") {
122346
+ throw new Error("computer must be a computer name, or omitted to use the conversation's current destination.");
122347
+ }
122348
+ const computer = value.trim();
122349
+ if (!computer)
122350
+ return;
122351
+ return ["cloud", "cloud-sandbox"].includes(computer.toLowerCase()) ? "cloud" : computer;
122352
+ }
122336
122353
  function buildAgentSendContent(sender, noWait, message) {
122337
122354
  const reminder = buildAgentSendReminder(sender, noWait);
122338
122355
  return [
@@ -122383,6 +122400,9 @@ async function resolveAgentMessageDestination(input, backend, signal) {
122383
122400
  const conversation = await backend.createConversation({ agent_id: agentId, ...input.senderAgentId ? { hidden: true } : {} }, options);
122384
122401
  conversationId = conversation.id;
122385
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
+ }
122386
122406
  return { agentId, conversationId };
122387
122407
  }
122388
122408
  var init_agent_message = () => {};
@@ -122454,9 +122474,7 @@ async function send_agent_message(args, deps = {}) {
122454
122474
  }
122455
122475
  if (!args.message?.trim())
122456
122476
  throw new Error("Message must not be empty.");
122457
- if (args.computer !== undefined && !args.computer.trim()) {
122458
- throw new Error("Computer selector must not be empty.");
122459
- }
122477
+ const computer = normalizeAgentMessageComputer(args.computer);
122460
122478
  const context3 = getRuntimeContext();
122461
122479
  const sender = {
122462
122480
  agentId: validateAddress(context3?.agentId ?? undefined, "agent"),
@@ -122475,16 +122493,16 @@ async function send_agent_message(args, deps = {}) {
122475
122493
  agentId: args.agent_id,
122476
122494
  conversationId: args.conversation_id,
122477
122495
  senderAgentId: sender.agentId,
122478
- actingUserId
122496
+ actingUserId,
122497
+ currentConversation: sender
122479
122498
  }, backend, signal);
122480
122499
  signal.throwIfAborted();
122481
122500
  submissionAttempted = true;
122482
- const computer = args.computer;
122483
122501
  const receipt = await (deps.enqueue ?? enqueueConversationMessage)({
122484
122502
  ...destination,
122485
122503
  clientMessageId,
122486
122504
  content: buildAgentSendContent(sender, true, args.message),
122487
- computer: computer && ["cloud", "cloud-sandbox"].includes(computer.trim().toLowerCase()) ? "cloud" : computer,
122505
+ computer,
122488
122506
  actingUserId
122489
122507
  }, signal);
122490
122508
  return {
@@ -123627,6 +123645,138 @@ var init_context_budget = __esm(() => {
123627
123645
  REFLECTION_STARTUP_CONTEXT_CHAR_LIMIT = REFLECTION_STARTUP_CONTEXT_TOKEN_LIMIT * STARTUP_CONTEXT_ESTIMATED_CHARS_PER_TOKEN;
123628
123646
  });
123629
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
+
123630
123780
  // src/mods/capabilities.ts
123631
123781
  function isModCapabilityId(value) {
123632
123782
  return MOD_CAPABILITY_ID_SET.has(value);
@@ -123795,6 +123945,9 @@ function composeSubagentChildEnv(options) {
123795
123945
  ...parentAgentId && { LETTA_PARENT_AGENT_ID: parentAgentId },
123796
123946
  ...transcriptPath && { TRANSCRIPT_PATH: transcriptPath }
123797
123947
  };
123948
+ delete childEnv[SUBAGENT_NAME_ENV];
123949
+ if (options.subagentName)
123950
+ childEnv[SUBAGENT_NAME_ENV] = options.subagentName;
123798
123951
  if (backendMode === "local") {
123799
123952
  childEnv.LETTA_LOCAL_BACKEND_EXPERIMENTAL = "1";
123800
123953
  if (localBackendStorageDir) {
@@ -124324,7 +124477,8 @@ async function executeSubagent(type3, config, model, userPrompt, subagentId, isR
124324
124477
  inheritedApiKey,
124325
124478
  inheritedBaseUrl,
124326
124479
  actingUserId: actingUserIdOverride,
124327
- transcriptPath
124480
+ transcriptPath,
124481
+ subagentName: existingAgentId || existingConversationId ? undefined : allocateSubagentName()
124328
124482
  });
124329
124483
  const sandbox = wrapSubagentLauncher({
124330
124484
  launcher,
@@ -124610,6 +124764,7 @@ var init_manager3 = __esm(() => {
124610
124764
  init_subagent_stdout_failure();
124611
124765
  init_subagents();
124612
124766
  init_context_budget();
124767
+ init_names();
124613
124768
  init_subagent_launcher();
124614
124769
  init_subagent_model();
124615
124770
  init_subagent_stream();
@@ -126548,8 +126703,8 @@ var init_SendAgentMessage2 = __esm(() => {
126548
126703
  description: "The existing message thread to send to. Each conversation has its own message history. A conv-... ID is sufficient; to use an agent's default thread, also supply agent_id."
126549
126704
  },
126550
126705
  computer: {
126551
- type: "string",
126552
- description: "Optional computer where the recipient should run: cloud, an online computer name, or its stable device ID. Omit to use its active computer or saved destination (a computer or Cloud sandbox). An offline saved computer does not trigger a Cloud-sandbox fallback."
126706
+ type: ["string", "null"],
126707
+ description: "Optional computer where the recipient should run: cloud, an online computer name, or its stable device ID. Omitted, null, and blank values use its active computer or saved destination (a computer or Cloud sandbox). An offline saved computer does not trigger a Cloud-sandbox fallback."
126553
126708
  }
126554
126709
  },
126555
126710
  required: ["message"],
@@ -144693,7 +144848,15 @@ class APIBackend {
144693
144848
  }
144694
144849
  async createConversationMessageStream(conversationId, body, options) {
144695
144850
  const client = await this.getClient();
144696
- 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;
144697
144860
  }
144698
144861
  async streamConversationMessages(conversationId, body, options) {
144699
144862
  const client = await this.getClient();
@@ -144812,6 +144975,7 @@ function __testSetBackend(nextBackend) {
144812
144975
  }
144813
144976
  var DEFAULT_CONVERSATION_MESSAGE_ORDER = "desc", backend = null;
144814
144977
  var init_backend = __esm(() => {
144978
+ init_error();
144815
144979
  init_chatgpt_usage_service();
144816
144980
  init_server_url();
144817
144981
  init_backend_mode();
@@ -145327,7 +145491,7 @@ function resolveCreatedAgentMemfsConfig(options) {
145327
145491
  const memoryPromptMode = (options.requestedMemoryPromptMode !== "standard" ? options.requestedMemoryPromptMode : undefined) ?? (enableMemfs ? options.capabilities.localMemfs ? "local-memfs" : "memfs" : "standard");
145328
145492
  return { enableMemfs, memoryPromptMode };
145329
145493
  }
145330
- 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) {
145331
145495
  let options;
145332
145496
  if (typeof nameOrOptions === "object") {
145333
145497
  options = nameOrOptions;
@@ -145343,10 +145507,11 @@ async function createAgent(nameOrOptions = DEFAULT_AGENT_NAME, model, embeddingM
145343
145507
  baseTools
145344
145508
  };
145345
145509
  }
145346
- const name = options.name ?? DEFAULT_AGENT_NAME;
145347
145510
  const embeddingModelVal = options.embeddingModel;
145348
145511
  const parallelToolCallsVal = options.parallelToolCalls ?? true;
145349
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];
145350
145515
  let modelHandle;
145351
145516
  if (options.model) {
145352
145517
  const resolved = resolveModel(options.model);
@@ -145465,7 +145630,6 @@ async function createAgent(nameOrOptions = DEFAULT_AGENT_NAME, model, embeddingM
145465
145630
  var init_create5 = __esm(() => {
145466
145631
  init_backend2();
145467
145632
  init_request();
145468
- init_constants2();
145469
145633
  init_settings_manager();
145470
145634
  init_debug();
145471
145635
  init_available_models();
@@ -145474,6 +145638,7 @@ var init_create5 = __esm(() => {
145474
145638
  init_model();
145475
145639
  init_modify();
145476
145640
  init_prompt_assets();
145641
+ init_names();
145477
145642
  init_system_prompt_resolution();
145478
145643
  init_system_prompt_versioning();
145479
145644
  });
@@ -146693,14 +146858,14 @@ var require_react_development = __commonJS((exports, module3) => {
146693
146858
  return 1;
146694
146859
  }
146695
146860
  var child;
146696
- var nextName;
146861
+ var nextName2;
146697
146862
  var subtreeCount = 0;
146698
146863
  var nextNamePrefix = nameSoFar === "" ? SEPARATOR : nameSoFar + SUBSEPARATOR;
146699
146864
  if (isArray5(children)) {
146700
146865
  for (var i2 = 0;i2 < children.length; i2++) {
146701
146866
  child = children[i2];
146702
- nextName = nextNamePrefix + getElementKey(child, i2);
146703
- subtreeCount += mapIntoArray(child, array2, escapedPrefix, nextName, callback);
146867
+ nextName2 = nextNamePrefix + getElementKey(child, i2);
146868
+ subtreeCount += mapIntoArray(child, array2, escapedPrefix, nextName2, callback);
146704
146869
  }
146705
146870
  } else {
146706
146871
  var iteratorFn = getIteratorFn(children);
@@ -146719,8 +146884,8 @@ var require_react_development = __commonJS((exports, module3) => {
146719
146884
  var ii = 0;
146720
146885
  while (!(step = iterator.next()).done) {
146721
146886
  child = step.value;
146722
- nextName = nextNamePrefix + getElementKey(child, ii++);
146723
- subtreeCount += mapIntoArray(child, array2, escapedPrefix, nextName, callback);
146887
+ nextName2 = nextNamePrefix + getElementKey(child, ii++);
146888
+ subtreeCount += mapIntoArray(child, array2, escapedPrefix, nextName2, callback);
146724
146889
  }
146725
146890
  } else if (type3 === "object") {
146726
146891
  var childrenString = String(children);
@@ -217397,17 +217562,12 @@ class QueueRuntime {
217397
217562
  const batch = [];
217398
217563
  const first = this.store.find((item) => !item.paused);
217399
217564
  if (first && isCoalescable(first.kind)) {
217400
- let batchActingUserId = first.actingUserId;
217401
217565
  for (const item of this.store) {
217402
217566
  if (item.paused)
217403
217567
  continue;
217404
217568
  if (!isCoalescable(item.kind) || !hasSameScope(first, item))
217405
217569
  break;
217406
- if (batchActingUserId && item.actingUserId && batchActingUserId !== item.actingUserId) {
217407
- break;
217408
- }
217409
217570
  batch.push(item);
217410
- batchActingUserId ??= item.actingUserId;
217411
217571
  }
217412
217572
  } else if (first) {
217413
217573
  batch.push(first);
@@ -217553,47 +217713,13 @@ var init_queue_runtime = __esm(() => {
217553
217713
  init_debug();
217554
217714
  });
217555
217715
 
217556
- // src/queue/turn-queue-runtime.ts
217557
- function stringifyUnexpectedContent(content) {
217558
- try {
217559
- return JSON.stringify(content) ?? String(content);
217560
- } catch {
217561
- return String(content);
217562
- }
217563
- }
217564
- function appendContentParts(target2, content) {
217565
- if (typeof content === "string") {
217566
- target2.push({ type: "text", text: content });
217567
- return;
217568
- }
217569
- if (!Array.isArray(content)) {
217570
- if (content === null || content === undefined)
217571
- return;
217572
- target2.push({ type: "text", text: stringifyUnexpectedContent(content) });
217573
- return;
217574
- }
217575
- target2.push(...content);
217576
- }
217577
- function mergeQueuedTurnInput(queued, options) {
217578
- if (queued.length === 0) {
217579
- return null;
217580
- }
217581
- const separatorText = options.separatorText ?? `
217582
- `;
217583
- const mergedParts = [];
217584
- let isFirst = true;
217585
- for (const item of queued) {
217586
- if (!isFirst) {
217587
- mergedParts.push({ type: "text", text: separatorText });
217588
- }
217589
- isFirst = false;
217590
- if (item.kind === "task_notification" || item.kind === "cron_prompt") {
217591
- mergedParts.push({ type: "text", text: item.text });
217592
- continue;
217593
- }
217594
- appendContentParts(mergedParts, options.normalizeUserContent(item.content));
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;
217595
217721
  }
217596
- return mergedParts.length > 0 ? mergedParts : null;
217722
+ return { ...message, attribution: { acting_user_id: actingUserId } };
217597
217723
  }
217598
217724
 
217599
217725
  // src/websocket/helpers/listener-queue-adapter.ts
@@ -217645,109 +217771,37 @@ function getQueueItemsScope(items3) {
217645
217771
  function hasSameQueueScope(a2, b3) {
217646
217772
  return (a2.agentId ?? null) === (b3.agentId ?? null) && (a2.conversationId ?? null) === (b3.conversationId ?? null);
217647
217773
  }
217648
- function mergeDequeuedBatchContent(items3) {
217649
- const queuedInputs = [];
217650
- for (const item of items3) {
217651
- if (item.kind === "message") {
217652
- queuedInputs.push({
217653
- kind: "user",
217654
- content: item.content
217655
- });
217656
- continue;
217657
- }
217658
- if (item.kind === "task_notification") {
217659
- queuedInputs.push({
217660
- kind: "task_notification",
217661
- text: item.text
217662
- });
217663
- continue;
217664
- }
217665
- if (item.kind === "cron_prompt") {
217666
- queuedInputs.push({
217667
- kind: "cron_prompt",
217668
- text: item.text
217669
- });
217670
- continue;
217671
- }
217672
- if (item.kind === "mod_continue") {
217673
- queuedInputs.push({
217674
- kind: "user",
217675
- content: item.text
217676
- });
217677
- }
217678
- }
217679
- return mergeQueuedTurnInput(queuedInputs, {
217680
- normalizeUserContent: (content) => content
217681
- });
217682
- }
217683
- function getPrimaryQueueMessageItem(items3) {
217684
- for (const item of items3) {
217685
- if (item.kind === "message") {
217686
- return item;
217687
- }
217688
- }
217689
- return null;
217690
- }
217691
- function pickBatchActingUserId(items3) {
217692
- for (let i4 = items3.length - 1;i4 >= 0; i4 -= 1) {
217693
- const actingUserId = items3[i4]?.actingUserId;
217694
- if (actingUserId) {
217695
- return actingUserId;
217696
- }
217697
- }
217698
- return;
217699
- }
217700
217774
  function buildQueuedTurnMessage(runtime, batch) {
217701
- const actingUserId = pickBatchActingUserId(batch.items);
217702
- const primaryItem = getPrimaryQueueMessageItem(batch.items);
217703
- if (!primaryItem) {
217704
- for (const item of batch.items) {
217705
- runtime.queuedMessagesByItemId.delete(item.id);
217706
- }
217707
- const mergedContent2 = mergeDequeuedBatchContent(batch.items);
217708
- if (mergedContent2 === null) {
217709
- return null;
217710
- }
217711
- const scopeItem = batch.items[0];
217712
- return {
217713
- type: "message",
217714
- agentId: scopeItem?.agentId ?? runtime.agentId ?? undefined,
217715
- conversationId: scopeItem?.conversationId ?? runtime.conversationId,
217716
- ...actingUserId ? { actingUserId } : {},
217717
- messages: [
217718
- {
217719
- role: "user",
217720
- content: mergedContent2,
217721
- otid: crypto.randomUUID()
217722
- }
217723
- ]
217724
- };
217725
- }
217726
- const template3 = runtime.queuedMessagesByItemId.get(primaryItem.id);
217775
+ let template3;
217776
+ const messages = [];
217727
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
+ }
217728
217795
  runtime.queuedMessagesByItemId.delete(item.id);
217729
217796
  }
217730
- if (!template3) {
217731
- return null;
217732
- }
217733
- const mergedContent = mergeDequeuedBatchContent(batch.items);
217734
- if (mergedContent === null) {
217797
+ if (messages.length === 0)
217735
217798
  return null;
217736
- }
217737
- const firstMessageIndex = template3.messages.findIndex((payload) => ("content" in payload));
217738
- if (firstMessageIndex === -1) {
217739
- return null;
217740
- }
217741
- const firstMessage = template3.messages[firstMessageIndex];
217742
- const mergedFirstMessage = {
217743
- ...firstMessage,
217744
- content: mergedContent
217745
- };
217746
- const messages = template3.messages.slice();
217747
- messages[firstMessageIndex] = mergedFirstMessage;
217799
+ const scopeItem = batch.items[0];
217748
217800
  return {
217801
+ type: "message",
217802
+ agentId: scopeItem?.agentId ?? runtime.agentId ?? undefined,
217803
+ conversationId: scopeItem?.conversationId ?? runtime.conversationId,
217749
217804
  ...template3,
217750
- ...actingUserId ? { actingUserId } : {},
217751
217805
  messages
217752
217806
  };
217753
217807
  }
@@ -217778,7 +217832,7 @@ function shouldProcessInboundMessageDirectly(runtime, parsed) {
217778
217832
  });
217779
217833
  return getListenerBlockedReason(runtime.turnLifecycle.snapshot(), activeScope ? getPendingControlRequestCount(runtime.listener, activeScope) : 0) === null;
217780
217834
  }
217781
- function consumeQueuedTurn(runtime, continuation) {
217835
+ function consumeQueuedTurn(runtime) {
217782
217836
  const queuedItems = runtime.queueRuntime.peekReady();
217783
217837
  const firstQueuedItem = queuedItems[0];
217784
217838
  if (!firstQueuedItem || !isCoalescable(firstQueuedItem.kind)) {
@@ -217790,22 +217844,15 @@ function consumeQueuedTurn(runtime, continuation) {
217790
217844
  let hasCronPrompt = false;
217791
217845
  let hasModContinue = false;
217792
217846
  let batchConnectionId;
217793
- let batchActingUserId = firstQueuedItem.actingUserId;
217794
217847
  let batchImageFailureMode = null;
217795
217848
  const isNoCoalesce = (candidate) => candidate.kind === "message" && candidate.noCoalesce === true;
217796
217849
  for (const item of queuedItems) {
217797
- if (continuation && item.actingUserId !== continuation.actingUserId) {
217798
- break;
217799
- }
217800
217850
  if (!isCoalescable(item.kind) || !hasSameQueueScope(firstQueuedItem, item)) {
217801
217851
  break;
217802
217852
  }
217803
217853
  if (queueLen > 0 && (isNoCoalesce(item) || isNoCoalesce(firstQueuedItem))) {
217804
217854
  break;
217805
217855
  }
217806
- if (batchActingUserId && item.actingUserId && batchActingUserId !== item.actingUserId) {
217807
- break;
217808
- }
217809
217856
  if (item.kind === "message") {
217810
217857
  const itemConnectionId = runtime.queuedMessagesByItemId.get(item.id)?.connectionId;
217811
217858
  if (batchConnectionId !== undefined && itemConnectionId !== undefined && itemConnectionId !== batchConnectionId) {
@@ -217818,7 +217865,6 @@ function consumeQueuedTurn(runtime, continuation) {
217818
217865
  }
217819
217866
  batchImageFailureMode = itemImageFailureMode;
217820
217867
  }
217821
- batchActingUserId ??= item.actingUserId;
217822
217868
  queueLen += 1;
217823
217869
  if (item.kind === "message") {
217824
217870
  hasMessage = true;
@@ -222814,7 +222860,7 @@ function isRuntimeStartCommand(value) {
222814
222860
  if (!value || typeof value !== "object")
222815
222861
  return false;
222816
222862
  const c = value;
222817
- return c.type === "runtime_start" && typeof c.request_id === "string" && (c.agent_id === undefined || typeof c.agent_id === "string") && (c.create_agent === undefined || isRuntimeStartCreateAgentOptions(c.create_agent)) && (c.conversation_id === undefined || typeof c.conversation_id === "string") && (c.create_conversation === undefined || isRuntimeStartCreateConversationOptions(c.create_conversation)) && (c.conversation_source_tags === undefined || isStringArray8(c.conversation_source_tags)) && (c.cwd === undefined || c.cwd === null || typeof c.cwd === "string") && (c.mode === undefined || isDevicePermissionMode(c.mode)) && (c.execution_settings === undefined || isRuntimeExecutionSettings(c.execution_settings)) && (c.workspace_sandbox === undefined || isRuntimeStartWorkspaceSandbox(c.workspace_sandbox)) && (c.skill_sources === undefined || isSkillSourceArray(c.skill_sources)) && (c.preserve_skill_sources === undefined || typeof c.preserve_skill_sources === "boolean") && (c.client_info === undefined || isRuntimeStartClientInfo(c.client_info)) && (c.recover_approvals === undefined || typeof c.recover_approvals === "boolean") && (c.force_device_status === undefined || typeof c.force_device_status === "boolean") && (c.wait_for_replay === undefined || typeof c.wait_for_replay === "boolean") && (c.external_tools === undefined || Array.isArray(c.external_tools) && c.external_tools.every(isRuntimeStartExternalToolsGroup));
222863
+ return c.type === "runtime_start" && typeof c.request_id === "string" && (c.agent_id === undefined || typeof c.agent_id === "string") && (c.create_agent === undefined || isRuntimeStartCreateAgentOptions(c.create_agent)) && (c.conversation_id === undefined || typeof c.conversation_id === "string") && (c.create_conversation === undefined || isRuntimeStartCreateConversationOptions(c.create_conversation)) && (c.conversation_source_tags === undefined || isStringArray8(c.conversation_source_tags)) && (c.cwd === undefined || c.cwd === null || typeof c.cwd === "string") && (c.mode === undefined || isDevicePermissionMode(c.mode)) && (c.execution_settings === undefined || isRuntimeExecutionSettings(c.execution_settings)) && (c.workspace_sandbox === undefined || isRuntimeStartWorkspaceSandbox(c.workspace_sandbox)) && (c.skill_sources === undefined || isSkillSourceArray(c.skill_sources)) && (c.preserve_skill_sources === undefined || typeof c.preserve_skill_sources === "boolean") && (c.client_info === undefined || isRuntimeStartClientInfo(c.client_info)) && (c.recover_approvals === undefined || typeof c.recover_approvals === "boolean") && (c.force_device_status === undefined || typeof c.force_device_status === "boolean") && (c.wait_for_replay === undefined || typeof c.wait_for_replay === "boolean") && (c.teleport_id === undefined || typeof c.teleport_id === "string") && (c.external_tools === undefined || Array.isArray(c.external_tools) && c.external_tools.every(isRuntimeStartExternalToolsGroup));
222818
222864
  }
222819
222865
  function isRuntimeStartCreateAgentOptions(value) {
222820
222866
  if (!isObjectRecord2(value))
@@ -397765,6 +397811,497 @@ function assertRuntimeWorkspaceSandboxChangeAllowed(listener, runtime, next) {
397765
397811
  }
397766
397812
  }
397767
397813
 
397814
+ // src/channels/teleport-guard.ts
397815
+ function getLocalChannelTeleportError(scope) {
397816
+ const channels = getSupportedChannelIds().filter((channelId) => {
397817
+ return readRoutes(channelId).some((route) => route.agentId === scope.agentId && route.conversationId === scope.conversationId && route.enabled !== false && route.outboundEnabled !== false);
397818
+ });
397819
+ if (channels.length === 0)
397820
+ return null;
397821
+ return `This conversation is bound to ${channels.join(", ")} on this computer. Teleport is blocked because MessageChannel cannot follow the conversation to another computer yet. Remove the channel route or continue locally.`;
397822
+ }
397823
+ var init_teleport_guard = __esm(() => {
397824
+ init_plugin_registry();
397825
+ init_routing();
397826
+ });
397827
+
397828
+ // src/websocket/listener/recoverable-notices.ts
397829
+ function getRecoverableStatusNoticeVisibility(kind) {
397830
+ switch (kind) {
397831
+ case "stale_approval_conflict_recovery":
397832
+ return "debug_only";
397833
+ default:
397834
+ return "transcript";
397835
+ }
397836
+ }
397837
+ function getRecoverableRetryNoticeVisibility(kind) {
397838
+ switch (kind) {
397839
+ case "transient_provider_retry":
397840
+ return "transcript";
397841
+ default:
397842
+ return "transcript";
397843
+ }
397844
+ }
397845
+ function isDesktopDebugPanelMirrorEnabled() {
397846
+ return process.env.LETTA_DESKTOP_MODE === "1";
397847
+ }
397848
+ function mirrorRecoverableNoticeToDesktopDebugPanel(message) {
397849
+ if (!isDesktopDebugPanelMirrorEnabled()) {
397850
+ return;
397851
+ }
397852
+ try {
397853
+ process.stderr.write(`${DESKTOP_DEBUG_PANEL_INFO_PREFIX} ${message}
397854
+ `);
397855
+ } catch {}
397856
+ }
397857
+ function toStructuredApiError(errorInfo) {
397858
+ if (!errorInfo?.error_type || !errorInfo.run_id) {
397859
+ return;
397860
+ }
397861
+ return {
397862
+ message_type: "error_message",
397863
+ message: errorInfo.message || errorInfo.detail || "An error occurred",
397864
+ error_type: errorInfo.error_type,
397865
+ run_id: errorInfo.run_id,
397866
+ ...errorInfo.detail ? { detail: errorInfo.detail } : {}
397867
+ };
397868
+ }
397869
+ function getStructuredApiErrorFromError(error4) {
397870
+ if (!(error4 instanceof Error)) {
397871
+ return;
397872
+ }
397873
+ const errorWithStructuredInfo = error4;
397874
+ return errorWithStructuredInfo.apiError ?? toStructuredApiError(errorWithStructuredInfo.runErrorInfo);
397875
+ }
397876
+ function buildStructuredFormatInput(apiError) {
397877
+ return {
397878
+ error: {
397879
+ error: {
397880
+ type: apiError.error_type,
397881
+ message: apiError.message,
397882
+ ...apiError.detail ? { detail: apiError.detail } : {}
397883
+ },
397884
+ run_id: apiError.run_id
397885
+ }
397886
+ };
397887
+ }
397888
+ function isAbortLikeError(error4) {
397889
+ if (error4 instanceof APIUserAbortError) {
397890
+ return true;
397891
+ }
397892
+ if (!(error4 instanceof Error)) {
397893
+ return false;
397894
+ }
397895
+ const errorWithCode = error4;
397896
+ return error4.name === "AbortError" || error4.message === "The operation was aborted" || errorWithCode.code === "ABORT_ERR";
397897
+ }
397898
+ function isTerminatedProcessNoise(message) {
397899
+ return message.trim().toLowerCase() === "terminated";
397900
+ }
397901
+ function isProxyTransportError(detail, error4, message) {
397902
+ if (error4 instanceof APIError && error4.status >= 500 && detail.toLowerCase().includes("trying to proxy")) {
397903
+ return true;
397904
+ }
397905
+ return detail.toLowerCase().includes("error occurred while trying to proxy") || message.toLowerCase().includes("error occurred while trying to proxy");
397906
+ }
397907
+ function getLoopErrorNoticeDecision(params) {
397908
+ const apiError = params.apiError ?? toStructuredApiError(params.errorInfo) ?? toStructuredApiError(params.runErrorInfo) ?? getStructuredApiErrorFromError(params.error);
397909
+ const detail = apiError?.detail ?? params.errorInfo?.detail ?? params.runErrorInfo?.detail ?? extractConflictDetail(params.error) ?? "";
397910
+ if (params.cancelRequested || params.abortSignal?.aborted || isAbortLikeError(params.error) || isTerminatedProcessNoise(params.message)) {
397911
+ return {
397912
+ visibility: "debug_only",
397913
+ message: params.message
397914
+ };
397915
+ }
397916
+ const cloudflareMessage = checkCloudflareEdgeError(detail) ?? checkCloudflareEdgeError(params.message);
397917
+ if (cloudflareMessage) {
397918
+ return {
397919
+ visibility: "transcript",
397920
+ message: cloudflareMessage,
397921
+ apiError
397922
+ };
397923
+ }
397924
+ if (isProxyTransportError(detail, params.error, params.message)) {
397925
+ return {
397926
+ visibility: "transcript",
397927
+ message: "Connection to Letta service failed. Please retry.",
397928
+ apiError
397929
+ };
397930
+ }
397931
+ const formattedMessage = formatErrorDetails2(apiError ? buildStructuredFormatInput(apiError) : params.error ?? params.message, params.agentId ?? undefined, params.conversationId ?? undefined, {
397932
+ surface: params.surface,
397933
+ unclassifiedFallback: params.unclassifiedFallback
397934
+ });
397935
+ return {
397936
+ visibility: "transcript",
397937
+ message: formattedMessage,
397938
+ apiError
397939
+ };
397940
+ }
397941
+ function getTranscriptLoopErrorMessage(params) {
397942
+ const decision = getLoopErrorNoticeDecision({
397943
+ ...params,
397944
+ surface: "plain",
397945
+ unclassifiedFallback: "generic"
397946
+ });
397947
+ return decision.visibility === "transcript" ? decision.message : undefined;
397948
+ }
397949
+ function getConsumerLoopErrorMessage(params) {
397950
+ const decision = getLoopErrorNoticeDecision({
397951
+ ...params,
397952
+ surface: "plain"
397953
+ });
397954
+ return decision.visibility === "transcript" ? decision.message : undefined;
397955
+ }
397956
+ function emitLoopErrorNotice(socket, runtime, params) {
397957
+ const decision = getLoopErrorNoticeDecision(params);
397958
+ if (decision.visibility === "debug_only") {
397959
+ debugLog("recovery", `Debug-only loop error (${params.stopReason}): ${params.message}`);
397960
+ mirrorRecoverableNoticeToDesktopDebugPanel(params.message);
397961
+ return null;
397962
+ }
397963
+ emitLoopErrorDelta(socket, runtime, {
397964
+ message: decision.message,
397965
+ stopReason: params.stopReason,
397966
+ isTerminal: params.isTerminal,
397967
+ runId: params.runId,
397968
+ agentId: params.agentId,
397969
+ conversationId: params.conversationId,
397970
+ apiError: decision.apiError
397971
+ });
397972
+ return decision.message;
397973
+ }
397974
+ function emitRecoverableStatusNotice(socket, runtime, params) {
397975
+ const visibility = getRecoverableStatusNoticeVisibility(params.kind);
397976
+ if (visibility === "debug_only") {
397977
+ debugLog("recovery", `Debug-only lifecycle notice (${params.kind}): ${params.message}`);
397978
+ mirrorRecoverableNoticeToDesktopDebugPanel(params.message);
397979
+ return;
397980
+ }
397981
+ emitStatusDelta(socket, runtime, {
397982
+ message: params.message,
397983
+ level: params.level,
397984
+ runId: params.runId,
397985
+ agentId: params.agentId,
397986
+ conversationId: params.conversationId
397987
+ });
397988
+ }
397989
+ function emitRecoverableRetryNotice(socket, runtime, params) {
397990
+ const visibility = getRecoverableRetryNoticeVisibility(params.kind);
397991
+ if (visibility === "debug_only") {
397992
+ debugLog("recovery", `Debug-only retry notice (${params.kind}, attempt ${params.attempt}/${params.maxAttempts}): ${params.message}`);
397993
+ mirrorRecoverableNoticeToDesktopDebugPanel(params.message);
397994
+ return;
397995
+ }
397996
+ emitRetryDelta(socket, runtime, {
397997
+ message: params.message,
397998
+ reason: params.reason,
397999
+ attempt: params.attempt,
398000
+ maxAttempts: params.maxAttempts,
398001
+ delayMs: params.delayMs,
398002
+ runId: params.runId,
398003
+ agentId: params.agentId,
398004
+ conversationId: params.conversationId
398005
+ });
398006
+ }
398007
+ var DESKTOP_DEBUG_PANEL_INFO_PREFIX = "[LETTA_DESKTOP_DEBUG_PANEL_INFO]";
398008
+ var init_recoverable_notices = __esm(() => {
398009
+ init_error();
398010
+ init_turn_recovery_policy();
398011
+ init_error_formatter();
398012
+ init_debug();
398013
+ init_protocol_outbound();
398014
+ });
398015
+
398016
+ // src/websocket/listener/teleport.ts
398017
+ function expectInboundTeleport(runtime, teleportId) {
398018
+ runtime.expectedTeleportId = teleportId;
398019
+ runtime.expectedTeleportExpiresAt = Date.now() + INBOUND_TELEPORT_CONTINUE_TTL_MS;
398020
+ }
398021
+ function isInboundTeleportExpected(runtime) {
398022
+ if (runtime.expectedTeleportId === null)
398023
+ return false;
398024
+ if (runtime.expectedTeleportExpiresAt !== null && runtime.expectedTeleportExpiresAt <= Date.now()) {
398025
+ clearExpectedInboundTeleport(runtime);
398026
+ return false;
398027
+ }
398028
+ return true;
398029
+ }
398030
+ function clearExpectedInboundTeleport(runtime) {
398031
+ runtime.expectedTeleportId = null;
398032
+ runtime.expectedTeleportExpiresAt = null;
398033
+ }
398034
+ function buildTeleportContinuationMessages(params) {
398035
+ return [
398036
+ {
398037
+ type: "approval",
398038
+ approvals: params.approvals,
398039
+ otid: params.teleportId
398040
+ },
398041
+ {
398042
+ role: "system",
398043
+ content: "<system-reminder>Teleportation to this environment is complete. Continue the existing task from this environment now.</system-reminder>",
398044
+ otid: `${params.teleportId}:continue`
398045
+ }
398046
+ ];
398047
+ }
398048
+ function escapeSystemReminderText(value) {
398049
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
398050
+ }
398051
+ function buildTeleportFailureMessages(params) {
398052
+ const messages = [];
398053
+ if (params.approvals && params.approvals.length > 0) {
398054
+ messages.push({
398055
+ type: "approval",
398056
+ approvals: params.approvals,
398057
+ otid: params.teleportId
398058
+ });
398059
+ }
398060
+ messages.push({
398061
+ role: "system",
398062
+ content: `<system-reminder>Teleportation failed.
398063
+
398064
+ Error: ${escapeSystemReminderText(params.error)}
398065
+
398066
+ Continue the existing task from this environment now.</system-reminder>`,
398067
+ otid: `${params.teleportId}:failed`
398068
+ });
398069
+ return messages;
398070
+ }
398071
+ function getPendingTeleports(runtime) {
398072
+ runtime.pendingTeleports ??= new Map;
398073
+ return runtime.pendingTeleports;
398074
+ }
398075
+ function findPendingTeleportForRuntime(runtime, agentId, conversationId) {
398076
+ for (const pending of getPendingTeleports(runtime).values()) {
398077
+ if (pending.agentId === agentId && pending.conversationId === conversationId && pending.readyAt === undefined) {
398078
+ return pending;
398079
+ }
398080
+ }
398081
+ return null;
398082
+ }
398083
+ function isRuntimeTeleportPending(runtime, agentId, conversationId) {
398084
+ if (!agentId)
398085
+ return false;
398086
+ return [...runtime.pendingTeleports?.values() ?? []].some((pending) => !pending.error && pending.agentId === agentId && pending.conversationId === conversationId);
398087
+ }
398088
+ function clearPriorReadyTeleports(params) {
398089
+ const pendingTeleports = params.listener.pendingTeleports;
398090
+ if (!pendingTeleports)
398091
+ return;
398092
+ for (const [teleportId, pending] of pendingTeleports) {
398093
+ if (teleportId === params.currentTeleportId || pending.readyAt === undefined) {
398094
+ continue;
398095
+ }
398096
+ if (pending.agentId === params.agentId && pending.conversationId === params.conversationId) {
398097
+ pendingTeleports.delete(teleportId);
398098
+ }
398099
+ }
398100
+ }
398101
+ function hasAcceptedInputsWaiting(runtime, includeQueuePump) {
398102
+ return runtime.queueRuntime.length > 0 || runtime.pendingTurns > 0 || runtime.queuedMessagesByItemId.size > 0 || includeQueuePump && (runtime.queuePumpActive || runtime.queuePumpScheduled);
398103
+ }
398104
+ function sendTeleportReady(runtime, pending, input) {
398105
+ const connection = runtime.connections.get(pending.connectionId);
398106
+ if (!connection || !isListenerTransportOpen(connection.writer))
398107
+ return false;
398108
+ const mode = getOrCreateConversationPermissionModeStateRef(runtime, pending.agentId, pending.conversationId).mode;
398109
+ const message = {
398110
+ type: "teleport_ready",
398111
+ teleport_id: pending.teleportId,
398112
+ runtime: {
398113
+ agent_id: pending.agentId,
398114
+ conversation_id: pending.conversationId
398115
+ },
398116
+ success: input.success,
398117
+ active_turn: pending.activeTurn,
398118
+ mode,
398119
+ ...pending.continuation ? { continuation: pending.continuation } : {},
398120
+ ...input.error ? { error: input.error } : {}
398121
+ };
398122
+ emitProtocolV2Message(connection.writer, runtime, message, message.runtime, toListenerConnection(pending.connectionId));
398123
+ return true;
398124
+ }
398125
+ function retainTeleportForRecovery(runtime, pending) {
398126
+ const timeout = setTimeout(() => {
398127
+ const current = runtime.pendingTeleports?.get(pending.teleportId);
398128
+ if (current === pending) {
398129
+ runtime.pendingTeleports?.delete(pending.teleportId);
398130
+ }
398131
+ }, TELEPORT_RECOVERY_TTL_MS);
398132
+ timeout.unref?.();
398133
+ }
398134
+ function handleTeleportProbe(command, socket, safeSocketSend) {
398135
+ safeSocketSend(socket, {
398136
+ type: "teleport_probe_response",
398137
+ request_id: command.request_id,
398138
+ runtime: command.runtime,
398139
+ supported: resolveBackendMode() === "api",
398140
+ drains_accepted_inputs: true,
398141
+ idempotent_continuation: true
398142
+ }, "teleport_probe_response", "teleport_probe");
398143
+ }
398144
+ function handleTeleportRequest(params) {
398145
+ const { listener, command, connectionId } = params;
398146
+ const pendingTeleports = getPendingTeleports(listener);
398147
+ const existing = pendingTeleports.get(command.teleport_id);
398148
+ if (existing) {
398149
+ if (existing.readyAt !== undefined) {
398150
+ sendTeleportReady(listener, existing, {
398151
+ success: existing.error === undefined,
398152
+ error: existing.error
398153
+ });
398154
+ }
398155
+ return;
398156
+ }
398157
+ const pending = {
398158
+ teleportId: command.teleport_id,
398159
+ connectionId,
398160
+ agentId: command.runtime.agent_id,
398161
+ conversationId: command.runtime.conversation_id,
398162
+ requestedAt: Date.now(),
398163
+ drainAcceptedInputs: false,
398164
+ activeTurn: false
398165
+ };
398166
+ const channelError = getLocalChannelTeleportError(pending);
398167
+ if (channelError) {
398168
+ pending.readyAt = Date.now();
398169
+ pending.error = channelError;
398170
+ pendingTeleports.set(pending.teleportId, pending);
398171
+ sendTeleportReady(listener, pending, {
398172
+ success: false,
398173
+ error: channelError
398174
+ });
398175
+ retainTeleportForRecovery(listener, pending);
398176
+ return;
398177
+ }
398178
+ const conflicting = findPendingTeleportForRuntime(listener, pending.agentId, pending.conversationId);
398179
+ if (conflicting) {
398180
+ pendingTeleports.set(pending.teleportId, pending);
398181
+ pending.readyAt = Date.now();
398182
+ pending.error = "Conversation already has a teleport pending";
398183
+ sendTeleportReady(listener, pending, {
398184
+ success: false,
398185
+ error: pending.error
398186
+ });
398187
+ retainTeleportForRecovery(listener, pending);
398188
+ return;
398189
+ }
398190
+ pendingTeleports.set(pending.teleportId, pending);
398191
+ const conversationRuntime = getConversationRuntime(listener, pending.agentId, pending.conversationId);
398192
+ pending.drainAcceptedInputs = conversationRuntime ? hasAcceptedInputsWaiting(conversationRuntime, true) : false;
398193
+ if (!conversationRuntime?.isProcessing && !pending.drainAcceptedInputs) {
398194
+ if (sendTeleportReady(listener, pending, { success: true })) {
398195
+ pending.readyAt = Date.now();
398196
+ retainTeleportForRecovery(listener, pending);
398197
+ }
398198
+ }
398199
+ }
398200
+ function claimPendingTeleportAtBoundary(params) {
398201
+ const pending = findPendingTeleportForRuntime(params.listener, params.agentId, params.conversationId);
398202
+ if (!pending)
398203
+ return null;
398204
+ if (pending.drainAcceptedInputs) {
398205
+ if (params.activeTurn)
398206
+ return null;
398207
+ const runtime = getConversationRuntime(params.listener, params.agentId, params.conversationId);
398208
+ if (runtime && hasAcceptedInputsWaiting(runtime, false))
398209
+ return null;
398210
+ }
398211
+ const connection = params.listener.connections.get(pending.connectionId);
398212
+ if (!connection || !isListenerTransportOpen(connection.writer))
398213
+ return null;
398214
+ pending.readyAt = Date.now();
398215
+ pending.activeTurn = params.activeTurn;
398216
+ pending.continuation = params.continuation;
398217
+ return pending;
398218
+ }
398219
+ function emitClaimedTeleportReady(listener, pending) {
398220
+ const sent = sendTeleportReady(listener, pending, { success: true });
398221
+ if (sent) {
398222
+ retainTeleportForRecovery(listener, pending);
398223
+ }
398224
+ return sent;
398225
+ }
398226
+ function finishTeleport(runtime, lease, pending) {
398227
+ const transition = runtime.turnLifecycle.finish(lease, "cancelled");
398228
+ if (!transition.finished)
398229
+ return transition;
398230
+ emitRuntimeStateUpdates(runtime, {
398231
+ agent_id: pending.agentId,
398232
+ conversation_id: pending.conversationId
398233
+ });
398234
+ emitClaimedTeleportReady(runtime.listener, pending);
398235
+ return transition;
398236
+ }
398237
+ function finishPendingTeleport(runtime) {
398238
+ if (!runtime.agentId)
398239
+ return;
398240
+ const pending = findPendingTeleportForRuntime(runtime.listener, runtime.agentId, runtime.conversationId);
398241
+ if (!pending || runtime.lastStopReason !== "end_turn" && !pending.drainAcceptedInputs) {
398242
+ return;
398243
+ }
398244
+ const claimed = claimPendingTeleportAtBoundary({
398245
+ listener: runtime.listener,
398246
+ agentId: runtime.agentId,
398247
+ conversationId: runtime.conversationId,
398248
+ activeTurn: false
398249
+ });
398250
+ if (claimed)
398251
+ emitClaimedTeleportReady(runtime.listener, claimed);
398252
+ }
398253
+ function takeFailedTeleport(params) {
398254
+ const pending = params.listener.pendingTeleports?.get(params.teleportId);
398255
+ if (!pending || pending.agentId !== params.agentId || pending.conversationId !== params.conversationId) {
398256
+ return null;
398257
+ }
398258
+ params.listener.pendingTeleports?.delete(params.teleportId);
398259
+ return pending;
398260
+ }
398261
+ function handleTeleportFailure(params) {
398262
+ const pending = takeFailedTeleport({
398263
+ listener: params.listener,
398264
+ teleportId: params.command.teleport_id,
398265
+ agentId: params.command.runtime.agent_id,
398266
+ conversationId: params.command.runtime.conversation_id
398267
+ });
398268
+ if (!pending || pending.error)
398269
+ return;
398270
+ const runtime = params.getOrCreateScopedRuntime(params.listener, pending.agentId, pending.conversationId);
398271
+ emitLoopErrorNotice(params.socket, runtime, {
398272
+ message: `Teleport failed: ${params.command.error}`,
398273
+ stopReason: "error",
398274
+ isTerminal: false,
398275
+ agentId: pending.agentId,
398276
+ conversationId: pending.conversationId
398277
+ });
398278
+ params.runDetachedListenerTask("teleport_failed", async () => {
398279
+ await params.processIncomingMessage({
398280
+ type: "message",
398281
+ connectionId: pending.connectionId,
398282
+ agentId: pending.agentId,
398283
+ conversationId: pending.conversationId,
398284
+ messages: buildTeleportFailureMessages({
398285
+ teleportId: params.command.teleport_id,
398286
+ error: params.command.error,
398287
+ approvals: pending.continuation?.approvals
398288
+ })
398289
+ }, params.socket, runtime, params.onStatusChange, pending.connectionId);
398290
+ });
398291
+ }
398292
+ var TELEPORT_RECOVERY_TTL_MS, INBOUND_TELEPORT_CONTINUE_TTL_MS = 60000;
398293
+ var init_teleport = __esm(() => {
398294
+ init_backend_mode();
398295
+ init_teleport_guard();
398296
+ init_connection();
398297
+ init_permission_mode();
398298
+ init_protocol_outbound();
398299
+ init_recoverable_notices();
398300
+ init_runtime();
398301
+ init_transport();
398302
+ TELEPORT_RECOVERY_TTL_MS = 5 * 60000;
398303
+ });
398304
+
397768
398305
  // src/websocket/listener/commands/runtime-start.ts
397769
398306
  function buildDefaultConversation(agent) {
397770
398307
  const now2 = new Date().toISOString();
@@ -397984,6 +398521,9 @@ async function handleRuntimeStartCommand(parsed, context3) {
397984
398521
  assertConnectionOpen();
397985
398522
  const scopedRuntime = context3.getOrCreateScopedRuntime(context3.runtime, runtimeScope.agent_id, runtimeScope.conversation_id);
397986
398523
  await applyRuntimeStartState(parsed, context3, runtimeScope, scopedRuntime);
398524
+ if (parsed.teleport_id) {
398525
+ expectInboundTeleport(scopedRuntime, parsed.teleport_id);
398526
+ }
397987
398527
  assertConnectionOpen();
397988
398528
  subscribeListenerConnection(context3.runtime, connectionId, runtimeScope);
397989
398529
  registerRuntimeExternalTools(context3.runtime, connectionId, runtimeScope, parsed.external_tools ?? []);
@@ -398045,6 +398585,7 @@ var init_runtime_start = __esm(async () => {
398045
398585
  init_cwd_change();
398046
398586
  init_permission_mode();
398047
398587
  init_runtime_start_validation();
398588
+ init_teleport();
398048
398589
  await init_external_tools();
398049
398590
  LEGACY_SUMMARY_PREFIX_BY_SOURCE_TAG = {
398050
398591
  "channel:discord": "discord",
@@ -398923,194 +399464,6 @@ var init_interrupts = __esm(() => {
398923
399464
  STREAMING_TOOL_OUTPUT_MAX_CHARS = LIMITS.BASH_OUTPUT_CHARS;
398924
399465
  });
398925
399466
 
398926
- // src/websocket/listener/recoverable-notices.ts
398927
- function getRecoverableStatusNoticeVisibility(kind) {
398928
- switch (kind) {
398929
- case "stale_approval_conflict_recovery":
398930
- return "debug_only";
398931
- default:
398932
- return "transcript";
398933
- }
398934
- }
398935
- function getRecoverableRetryNoticeVisibility(kind) {
398936
- switch (kind) {
398937
- case "transient_provider_retry":
398938
- return "transcript";
398939
- default:
398940
- return "transcript";
398941
- }
398942
- }
398943
- function isDesktopDebugPanelMirrorEnabled() {
398944
- return process.env.LETTA_DESKTOP_MODE === "1";
398945
- }
398946
- function mirrorRecoverableNoticeToDesktopDebugPanel(message) {
398947
- if (!isDesktopDebugPanelMirrorEnabled()) {
398948
- return;
398949
- }
398950
- try {
398951
- process.stderr.write(`${DESKTOP_DEBUG_PANEL_INFO_PREFIX} ${message}
398952
- `);
398953
- } catch {}
398954
- }
398955
- function toStructuredApiError(errorInfo) {
398956
- if (!errorInfo?.error_type || !errorInfo.run_id) {
398957
- return;
398958
- }
398959
- return {
398960
- message_type: "error_message",
398961
- message: errorInfo.message || errorInfo.detail || "An error occurred",
398962
- error_type: errorInfo.error_type,
398963
- run_id: errorInfo.run_id,
398964
- ...errorInfo.detail ? { detail: errorInfo.detail } : {}
398965
- };
398966
- }
398967
- function getStructuredApiErrorFromError(error4) {
398968
- if (!(error4 instanceof Error)) {
398969
- return;
398970
- }
398971
- const errorWithStructuredInfo = error4;
398972
- return errorWithStructuredInfo.apiError ?? toStructuredApiError(errorWithStructuredInfo.runErrorInfo);
398973
- }
398974
- function buildStructuredFormatInput(apiError) {
398975
- return {
398976
- error: {
398977
- error: {
398978
- type: apiError.error_type,
398979
- message: apiError.message,
398980
- ...apiError.detail ? { detail: apiError.detail } : {}
398981
- },
398982
- run_id: apiError.run_id
398983
- }
398984
- };
398985
- }
398986
- function isAbortLikeError(error4) {
398987
- if (error4 instanceof APIUserAbortError) {
398988
- return true;
398989
- }
398990
- if (!(error4 instanceof Error)) {
398991
- return false;
398992
- }
398993
- const errorWithCode = error4;
398994
- return error4.name === "AbortError" || error4.message === "The operation was aborted" || errorWithCode.code === "ABORT_ERR";
398995
- }
398996
- function isTerminatedProcessNoise(message) {
398997
- return message.trim().toLowerCase() === "terminated";
398998
- }
398999
- function isProxyTransportError(detail, error4, message) {
399000
- if (error4 instanceof APIError && error4.status >= 500 && detail.toLowerCase().includes("trying to proxy")) {
399001
- return true;
399002
- }
399003
- return detail.toLowerCase().includes("error occurred while trying to proxy") || message.toLowerCase().includes("error occurred while trying to proxy");
399004
- }
399005
- function getLoopErrorNoticeDecision(params) {
399006
- const apiError = params.apiError ?? toStructuredApiError(params.errorInfo) ?? toStructuredApiError(params.runErrorInfo) ?? getStructuredApiErrorFromError(params.error);
399007
- const detail = apiError?.detail ?? params.errorInfo?.detail ?? params.runErrorInfo?.detail ?? extractConflictDetail(params.error) ?? "";
399008
- if (params.cancelRequested || params.abortSignal?.aborted || isAbortLikeError(params.error) || isTerminatedProcessNoise(params.message)) {
399009
- return {
399010
- visibility: "debug_only",
399011
- message: params.message
399012
- };
399013
- }
399014
- const cloudflareMessage = checkCloudflareEdgeError(detail) ?? checkCloudflareEdgeError(params.message);
399015
- if (cloudflareMessage) {
399016
- return {
399017
- visibility: "transcript",
399018
- message: cloudflareMessage,
399019
- apiError
399020
- };
399021
- }
399022
- if (isProxyTransportError(detail, params.error, params.message)) {
399023
- return {
399024
- visibility: "transcript",
399025
- message: "Connection to Letta service failed. Please retry.",
399026
- apiError
399027
- };
399028
- }
399029
- const formattedMessage = formatErrorDetails2(apiError ? buildStructuredFormatInput(apiError) : params.error ?? params.message, params.agentId ?? undefined, params.conversationId ?? undefined, {
399030
- surface: params.surface,
399031
- unclassifiedFallback: params.unclassifiedFallback
399032
- });
399033
- return {
399034
- visibility: "transcript",
399035
- message: formattedMessage,
399036
- apiError
399037
- };
399038
- }
399039
- function getTranscriptLoopErrorMessage(params) {
399040
- const decision = getLoopErrorNoticeDecision({
399041
- ...params,
399042
- surface: "plain",
399043
- unclassifiedFallback: "generic"
399044
- });
399045
- return decision.visibility === "transcript" ? decision.message : undefined;
399046
- }
399047
- function getConsumerLoopErrorMessage(params) {
399048
- const decision = getLoopErrorNoticeDecision({
399049
- ...params,
399050
- surface: "plain"
399051
- });
399052
- return decision.visibility === "transcript" ? decision.message : undefined;
399053
- }
399054
- function emitLoopErrorNotice(socket, runtime, params) {
399055
- const decision = getLoopErrorNoticeDecision(params);
399056
- if (decision.visibility === "debug_only") {
399057
- debugLog("recovery", `Debug-only loop error (${params.stopReason}): ${params.message}`);
399058
- mirrorRecoverableNoticeToDesktopDebugPanel(params.message);
399059
- return null;
399060
- }
399061
- emitLoopErrorDelta(socket, runtime, {
399062
- message: decision.message,
399063
- stopReason: params.stopReason,
399064
- isTerminal: params.isTerminal,
399065
- runId: params.runId,
399066
- agentId: params.agentId,
399067
- conversationId: params.conversationId,
399068
- apiError: decision.apiError
399069
- });
399070
- return decision.message;
399071
- }
399072
- function emitRecoverableStatusNotice(socket, runtime, params) {
399073
- const visibility = getRecoverableStatusNoticeVisibility(params.kind);
399074
- if (visibility === "debug_only") {
399075
- debugLog("recovery", `Debug-only lifecycle notice (${params.kind}): ${params.message}`);
399076
- mirrorRecoverableNoticeToDesktopDebugPanel(params.message);
399077
- return;
399078
- }
399079
- emitStatusDelta(socket, runtime, {
399080
- message: params.message,
399081
- level: params.level,
399082
- runId: params.runId,
399083
- agentId: params.agentId,
399084
- conversationId: params.conversationId
399085
- });
399086
- }
399087
- function emitRecoverableRetryNotice(socket, runtime, params) {
399088
- const visibility = getRecoverableRetryNoticeVisibility(params.kind);
399089
- if (visibility === "debug_only") {
399090
- debugLog("recovery", `Debug-only retry notice (${params.kind}, attempt ${params.attempt}/${params.maxAttempts}): ${params.message}`);
399091
- mirrorRecoverableNoticeToDesktopDebugPanel(params.message);
399092
- return;
399093
- }
399094
- emitRetryDelta(socket, runtime, {
399095
- message: params.message,
399096
- reason: params.reason,
399097
- attempt: params.attempt,
399098
- maxAttempts: params.maxAttempts,
399099
- delayMs: params.delayMs,
399100
- runId: params.runId,
399101
- agentId: params.agentId,
399102
- conversationId: params.conversationId
399103
- });
399104
- }
399105
- var DESKTOP_DEBUG_PANEL_INFO_PREFIX = "[LETTA_DESKTOP_DEBUG_PANEL_INFO]";
399106
- var init_recoverable_notices = __esm(() => {
399107
- init_error();
399108
- init_turn_recovery_policy();
399109
- init_error_formatter();
399110
- init_debug();
399111
- init_protocol_outbound();
399112
- });
399113
-
399114
399467
  // src/cli/helpers/compaction-utils.ts
399115
399468
  function extractCompactionSummary(text2) {
399116
399469
  try {
@@ -401799,6 +402152,59 @@ var init_continuation_input = __esm(async () => {
401799
402152
  ]);
401800
402153
  });
401801
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
+
401802
402208
  // src/websocket/listener/secrets-sync.ts
401803
402209
  function getFreshnessMs() {
401804
402210
  return _testFreshnessMsOverride ?? DEFAULT_FRESHNESS_MS;
@@ -402110,6 +402516,15 @@ async function startRecoveredApprovalContinuation(runtime, socket, processTurn,
402110
402516
  if (runtime.turnLifecycle.kind !== "idle") {
402111
402517
  return false;
402112
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;
402113
402528
  const scope = {
402114
402529
  agent_id: recovered.agentId,
402115
402530
  conversation_id: recovered.conversationId
@@ -402393,13 +402808,11 @@ async function executeRecoveredApprovalContinuation(params) {
402393
402808
  }
402394
402809
  ]);
402395
402810
  let continuationBatchId = `batch-recovered-${crypto.randomUUID()}`;
402396
- let continuationActingUserId;
402397
402811
  let continuationCorrelation;
402398
402812
  const consumedQueuedTurn = consumeQueuedTurn(runtime);
402399
402813
  if (consumedQueuedTurn) {
402400
402814
  const { dequeuedBatch, queuedTurn } = consumedQueuedTurn;
402401
402815
  continuationBatchId = dequeuedBatch.batchId;
402402
- continuationActingUserId = queuedTurn.actingUserId;
402403
402816
  continuationInput = appendQueuedTurnToInput(continuationInput, queuedTurn);
402404
402817
  continuationCorrelation = createTurnCorrelation(runtime, {
402405
402818
  type: "message",
@@ -402417,7 +402830,6 @@ async function executeRecoveredApprovalContinuation(params) {
402417
402830
  type: "message",
402418
402831
  agentId: recovered.agentId,
402419
402832
  conversationId: recovered.conversationId,
402420
- ...continuationActingUserId ? { actingUserId: continuationActingUserId } : {},
402421
402833
  messages: continuationInput.messages
402422
402834
  }, socket, runtime, opts?.onStatusChange, opts?.connectionId, continuationBatchId, recoveryLease, continuationCorrelation);
402423
402835
  if (runtime.turnLifecycle.isCurrent(recoveryLease)) {
@@ -402470,6 +402882,7 @@ var init_recovery = __esm(async () => {
402470
402882
  init_permission_mode();
402471
402883
  init_protocol_outbound();
402472
402884
  init_recoverable_notices();
402885
+ init_recovery_ownership();
402473
402886
  init_runtime();
402474
402887
  init_secrets_sync();
402475
402888
  init_turn_correlation();
@@ -402900,13 +403313,11 @@ async function resolveStaleApprovals(runtime, socket, turnLease, deps = {}) {
402900
403313
  otid: crypto.randomUUID()
402901
403314
  }
402902
403315
  ]);
402903
- let continuationActingUserId;
402904
403316
  let recoveryTurnCorrelation;
402905
403317
  const consumedQueuedTurn = consumeQueuedTurn(runtime);
402906
403318
  if (consumedQueuedTurn) {
402907
403319
  const { dequeuedBatch, queuedTurn } = consumedQueuedTurn;
402908
403320
  recoveryTurnCorrelation = createTurnCorrelation(runtime, queuedTurn, dequeuedBatch.batchId);
402909
- continuationActingUserId = queuedTurn.actingUserId;
402910
403321
  continuationInput = appendQueuedTurnToInput(continuationInput, queuedTurn);
402911
403322
  emitDequeuedUserMessage(socket, runtime, queuedTurn, dequeuedBatch);
402912
403323
  }
@@ -402920,7 +403331,6 @@ async function resolveStaleApprovals(runtime, socket, turnLease, deps = {}) {
402920
403331
  agentId: runtime.agentId ?? undefined,
402921
403332
  streamTokens: true,
402922
403333
  background: true,
402923
- ...continuationActingUserId ? { actingUserId: continuationActingUserId } : {},
402924
403334
  workingDirectory: recoveryWorkingDirectory,
402925
403335
  preparedToolContext: preparedToolContext.preparedToolContext,
402926
403336
  ...continuationInput.imageFailureModesByMessageOtid ? {
@@ -403268,292 +403678,6 @@ var init_stream_recovery_status = __esm(() => {
403268
403678
  init_protocol_outbound();
403269
403679
  });
403270
403680
 
403271
- // src/channels/teleport-guard.ts
403272
- function getLocalChannelTeleportError(scope) {
403273
- const channels = getSupportedChannelIds().filter((channelId) => {
403274
- return readRoutes(channelId).some((route) => route.agentId === scope.agentId && route.conversationId === scope.conversationId && route.enabled !== false && route.outboundEnabled !== false);
403275
- });
403276
- if (channels.length === 0)
403277
- return null;
403278
- return `This conversation is bound to ${channels.join(", ")} on this computer. Teleport is blocked because MessageChannel cannot follow the conversation to another computer yet. Remove the channel route or continue locally.`;
403279
- }
403280
- var init_teleport_guard = __esm(() => {
403281
- init_plugin_registry();
403282
- init_routing();
403283
- });
403284
-
403285
- // src/websocket/listener/teleport.ts
403286
- function buildTeleportContinuationMessages(params) {
403287
- return [
403288
- {
403289
- type: "approval",
403290
- approvals: params.approvals,
403291
- otid: params.teleportId
403292
- },
403293
- {
403294
- role: "system",
403295
- content: "<system-reminder>Teleportation to this environment is complete. Continue the existing task from this environment now.</system-reminder>",
403296
- otid: `${params.teleportId}:continue`
403297
- }
403298
- ];
403299
- }
403300
- function escapeSystemReminderText(value) {
403301
- return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
403302
- }
403303
- function buildTeleportFailureMessages(params) {
403304
- const messages = [];
403305
- if (params.approvals && params.approvals.length > 0) {
403306
- messages.push({
403307
- type: "approval",
403308
- approvals: params.approvals,
403309
- otid: params.teleportId
403310
- });
403311
- }
403312
- messages.push({
403313
- role: "system",
403314
- content: `<system-reminder>Teleportation failed.
403315
-
403316
- Error: ${escapeSystemReminderText(params.error)}
403317
-
403318
- Continue the existing task from this environment now.</system-reminder>`,
403319
- otid: `${params.teleportId}:failed`
403320
- });
403321
- return messages;
403322
- }
403323
- function getPendingTeleports(runtime) {
403324
- runtime.pendingTeleports ??= new Map;
403325
- return runtime.pendingTeleports;
403326
- }
403327
- function findPendingTeleportForRuntime(runtime, agentId, conversationId) {
403328
- for (const pending of getPendingTeleports(runtime).values()) {
403329
- if (pending.agentId === agentId && pending.conversationId === conversationId && pending.readyAt === undefined) {
403330
- return pending;
403331
- }
403332
- }
403333
- return null;
403334
- }
403335
- function isRuntimeTeleportPending(runtime, agentId, conversationId) {
403336
- if (!agentId)
403337
- return false;
403338
- return [...runtime.pendingTeleports?.values() ?? []].some((pending) => !pending.error && pending.agentId === agentId && pending.conversationId === conversationId);
403339
- }
403340
- function clearPriorReadyTeleports(params) {
403341
- const pendingTeleports = params.listener.pendingTeleports;
403342
- if (!pendingTeleports)
403343
- return;
403344
- for (const [teleportId, pending] of pendingTeleports) {
403345
- if (teleportId === params.currentTeleportId || pending.readyAt === undefined) {
403346
- continue;
403347
- }
403348
- if (pending.agentId === params.agentId && pending.conversationId === params.conversationId) {
403349
- pendingTeleports.delete(teleportId);
403350
- }
403351
- }
403352
- }
403353
- function hasAcceptedInputsWaiting(runtime, includeQueuePump) {
403354
- return runtime.queueRuntime.length > 0 || runtime.pendingTurns > 0 || runtime.queuedMessagesByItemId.size > 0 || includeQueuePump && (runtime.queuePumpActive || runtime.queuePumpScheduled);
403355
- }
403356
- function sendTeleportReady(runtime, pending, input) {
403357
- const connection = runtime.connections.get(pending.connectionId);
403358
- if (!connection || !isListenerTransportOpen(connection.writer))
403359
- return false;
403360
- const mode = getOrCreateConversationPermissionModeStateRef(runtime, pending.agentId, pending.conversationId).mode;
403361
- const message = {
403362
- type: "teleport_ready",
403363
- teleport_id: pending.teleportId,
403364
- runtime: {
403365
- agent_id: pending.agentId,
403366
- conversation_id: pending.conversationId
403367
- },
403368
- success: input.success,
403369
- active_turn: pending.activeTurn,
403370
- mode,
403371
- ...pending.continuation ? { continuation: pending.continuation } : {},
403372
- ...input.error ? { error: input.error } : {}
403373
- };
403374
- emitProtocolV2Message(connection.writer, runtime, message, message.runtime, toListenerConnection(pending.connectionId));
403375
- return true;
403376
- }
403377
- function retainTeleportForRecovery(runtime, pending) {
403378
- const timeout = setTimeout(() => {
403379
- const current = runtime.pendingTeleports?.get(pending.teleportId);
403380
- if (current === pending) {
403381
- runtime.pendingTeleports?.delete(pending.teleportId);
403382
- }
403383
- }, TELEPORT_RECOVERY_TTL_MS);
403384
- timeout.unref?.();
403385
- }
403386
- function handleTeleportProbe(command, socket, safeSocketSend) {
403387
- safeSocketSend(socket, {
403388
- type: "teleport_probe_response",
403389
- request_id: command.request_id,
403390
- runtime: command.runtime,
403391
- supported: resolveBackendMode() === "api",
403392
- drains_accepted_inputs: true,
403393
- idempotent_continuation: true
403394
- }, "teleport_probe_response", "teleport_probe");
403395
- }
403396
- function handleTeleportRequest(params) {
403397
- const { listener, command, connectionId } = params;
403398
- const pendingTeleports = getPendingTeleports(listener);
403399
- const existing = pendingTeleports.get(command.teleport_id);
403400
- if (existing) {
403401
- if (existing.readyAt !== undefined) {
403402
- sendTeleportReady(listener, existing, {
403403
- success: existing.error === undefined,
403404
- error: existing.error
403405
- });
403406
- }
403407
- return;
403408
- }
403409
- const pending = {
403410
- teleportId: command.teleport_id,
403411
- connectionId,
403412
- agentId: command.runtime.agent_id,
403413
- conversationId: command.runtime.conversation_id,
403414
- requestedAt: Date.now(),
403415
- drainAcceptedInputs: false,
403416
- activeTurn: false
403417
- };
403418
- const channelError = getLocalChannelTeleportError(pending);
403419
- if (channelError) {
403420
- pending.readyAt = Date.now();
403421
- pending.error = channelError;
403422
- pendingTeleports.set(pending.teleportId, pending);
403423
- sendTeleportReady(listener, pending, {
403424
- success: false,
403425
- error: channelError
403426
- });
403427
- retainTeleportForRecovery(listener, pending);
403428
- return;
403429
- }
403430
- const conflicting = findPendingTeleportForRuntime(listener, pending.agentId, pending.conversationId);
403431
- if (conflicting) {
403432
- pendingTeleports.set(pending.teleportId, pending);
403433
- pending.readyAt = Date.now();
403434
- pending.error = "Conversation already has a teleport pending";
403435
- sendTeleportReady(listener, pending, {
403436
- success: false,
403437
- error: pending.error
403438
- });
403439
- retainTeleportForRecovery(listener, pending);
403440
- return;
403441
- }
403442
- pendingTeleports.set(pending.teleportId, pending);
403443
- const conversationRuntime = getConversationRuntime(listener, pending.agentId, pending.conversationId);
403444
- pending.drainAcceptedInputs = conversationRuntime ? hasAcceptedInputsWaiting(conversationRuntime, true) : false;
403445
- if (!conversationRuntime?.isProcessing && !pending.drainAcceptedInputs) {
403446
- if (sendTeleportReady(listener, pending, { success: true })) {
403447
- pending.readyAt = Date.now();
403448
- retainTeleportForRecovery(listener, pending);
403449
- }
403450
- }
403451
- }
403452
- function claimPendingTeleportAtBoundary(params) {
403453
- const pending = findPendingTeleportForRuntime(params.listener, params.agentId, params.conversationId);
403454
- if (!pending)
403455
- return null;
403456
- if (pending.drainAcceptedInputs) {
403457
- if (params.activeTurn)
403458
- return null;
403459
- const runtime = getConversationRuntime(params.listener, params.agentId, params.conversationId);
403460
- if (runtime && hasAcceptedInputsWaiting(runtime, false))
403461
- return null;
403462
- }
403463
- const connection = params.listener.connections.get(pending.connectionId);
403464
- if (!connection || !isListenerTransportOpen(connection.writer))
403465
- return null;
403466
- pending.readyAt = Date.now();
403467
- pending.activeTurn = params.activeTurn;
403468
- pending.continuation = params.continuation;
403469
- return pending;
403470
- }
403471
- function emitClaimedTeleportReady(listener, pending) {
403472
- const sent = sendTeleportReady(listener, pending, { success: true });
403473
- if (sent) {
403474
- retainTeleportForRecovery(listener, pending);
403475
- }
403476
- return sent;
403477
- }
403478
- function finishTeleport(runtime, lease, pending) {
403479
- const transition = runtime.turnLifecycle.finish(lease, "cancelled");
403480
- if (!transition.finished)
403481
- return transition;
403482
- emitRuntimeStateUpdates(runtime, {
403483
- agent_id: pending.agentId,
403484
- conversation_id: pending.conversationId
403485
- });
403486
- emitClaimedTeleportReady(runtime.listener, pending);
403487
- return transition;
403488
- }
403489
- function finishPendingTeleport(runtime) {
403490
- if (!runtime.agentId)
403491
- return;
403492
- const pending = findPendingTeleportForRuntime(runtime.listener, runtime.agentId, runtime.conversationId);
403493
- if (!pending || runtime.lastStopReason !== "end_turn" && !pending.drainAcceptedInputs) {
403494
- return;
403495
- }
403496
- const claimed = claimPendingTeleportAtBoundary({
403497
- listener: runtime.listener,
403498
- agentId: runtime.agentId,
403499
- conversationId: runtime.conversationId,
403500
- activeTurn: false
403501
- });
403502
- if (claimed)
403503
- emitClaimedTeleportReady(runtime.listener, claimed);
403504
- }
403505
- function takeFailedTeleport(params) {
403506
- const pending = params.listener.pendingTeleports?.get(params.teleportId);
403507
- if (!pending || pending.agentId !== params.agentId || pending.conversationId !== params.conversationId) {
403508
- return null;
403509
- }
403510
- params.listener.pendingTeleports?.delete(params.teleportId);
403511
- return pending;
403512
- }
403513
- function handleTeleportFailure(params) {
403514
- const pending = takeFailedTeleport({
403515
- listener: params.listener,
403516
- teleportId: params.command.teleport_id,
403517
- agentId: params.command.runtime.agent_id,
403518
- conversationId: params.command.runtime.conversation_id
403519
- });
403520
- if (!pending || pending.error)
403521
- return;
403522
- const runtime = params.getOrCreateScopedRuntime(params.listener, pending.agentId, pending.conversationId);
403523
- emitLoopErrorNotice(params.socket, runtime, {
403524
- message: `Teleport failed: ${params.command.error}`,
403525
- stopReason: "error",
403526
- isTerminal: false,
403527
- agentId: pending.agentId,
403528
- conversationId: pending.conversationId
403529
- });
403530
- params.runDetachedListenerTask("teleport_failed", async () => {
403531
- await params.processIncomingMessage({
403532
- type: "message",
403533
- connectionId: pending.connectionId,
403534
- agentId: pending.agentId,
403535
- conversationId: pending.conversationId,
403536
- messages: buildTeleportFailureMessages({
403537
- teleportId: params.command.teleport_id,
403538
- error: params.command.error,
403539
- approvals: pending.continuation?.approvals
403540
- })
403541
- }, params.socket, runtime, params.onStatusChange, pending.connectionId);
403542
- });
403543
- }
403544
- var TELEPORT_RECOVERY_TTL_MS;
403545
- var init_teleport = __esm(() => {
403546
- init_backend_mode();
403547
- init_teleport_guard();
403548
- init_connection();
403549
- init_permission_mode();
403550
- init_protocol_outbound();
403551
- init_recoverable_notices();
403552
- init_runtime();
403553
- init_transport();
403554
- TELEPORT_RECOVERY_TTL_MS = 5 * 60000;
403555
- });
403556
-
403557
403681
  // node_modules/diff/libesm/diff/base.js
403558
403682
  class Diff2 {
403559
403683
  diff(oldStr, newStr, options = {}) {
@@ -405988,9 +406112,7 @@ async function handleApprovalStop(params) {
405988
406112
  ]);
405989
406113
  let continuationBatchId = dequeuedBatchId;
405990
406114
  const sendOptions = buildSendOptions() ?? {};
405991
- const consumedQueuedTurn = consumeQueuedTurn(runtime, {
405992
- actingUserId: sendOptions.actingUserId
405993
- });
406115
+ const consumedQueuedTurn = consumeQueuedTurn(runtime);
405994
406116
  if (consumedQueuedTurn) {
405995
406117
  const { dequeuedBatch, queuedTurn } = consumedQueuedTurn;
405996
406118
  turnCorrelation?.appendDequeuedBatch(dequeuedBatch.batchId);
@@ -418806,6 +418928,7 @@ function createListenerMessageHandler(params) {
418806
418928
  currentTeleportId: teleportId
418807
418929
  });
418808
418930
  const scopedRuntime2 = getOrCreateScopedRuntime2(runtime, parsed.runtime.agent_id, parsed.runtime.conversation_id);
418931
+ clearExpectedInboundTeleport(scopedRuntime2);
418809
418932
  const acceptedKey = `teleport:${teleportId}`;
418810
418933
  const previousDisposition = scopedRuntime2.acceptedInputDispositions.get(acceptedKey);
418811
418934
  if (previousDisposition) {
@@ -419348,6 +419471,10 @@ async function recoverApprovalStateForSync(runtime, scope, deps = {}) {
419348
419471
  clearRecoveredApprovalState(runtime);
419349
419472
  return;
419350
419473
  }
419474
+ if (!await canRecoverConversation(runtime)) {
419475
+ clearRecoveredApprovalState(runtime);
419476
+ return "deferred";
419477
+ }
419351
419478
  const existingRecovered = runtime.recoveredApprovalState;
419352
419479
  if (existingRecovered && existingRecovered.agentId === scope.agent_id && existingRecovered.conversationId === scope.conversation_id && existingRecovered.pendingRequestIds.size > 0) {
419353
419480
  return;
@@ -419380,6 +419507,8 @@ async function recoverApprovalStateForSync(runtime, scope, deps = {}) {
419380
419507
  clearRecoveredApprovalState(runtime);
419381
419508
  return;
419382
419509
  }
419510
+ if (!await canRecoverConversation(runtime))
419511
+ return "deferred";
419383
419512
  if (hasInterruptedCacheForScope(runtime.listener, scope) || sameActiveScope && (runtime.turnLifecycle.kind !== "idle" || runtime.pendingApprovalResolvers.size > 0)) {
419384
419513
  return;
419385
419514
  }
@@ -419424,6 +419553,7 @@ async function recoverApprovalStateForSync(runtime, scope, deps = {}) {
419424
419553
  autoDecisions: staleDenialDecisions,
419425
419554
  allApprovals: pendingApprovals
419426
419555
  };
419556
+ return;
419427
419557
  }
419428
419558
  var init_recovery_sync = __esm(() => {
419429
419559
  init_error();
@@ -419431,6 +419561,7 @@ var init_recovery_sync = __esm(() => {
419431
419561
  init_turn_recovery_policy();
419432
419562
  init_backend2();
419433
419563
  init_interactive_policy();
419564
+ init_recovery_ownership();
419434
419565
  init_runtime();
419435
419566
  });
419436
419567
 
@@ -419440,8 +419571,8 @@ async function replaySyncStateForRuntime(listenerRuntime, socket, scope, opts) {
419440
419571
  const recoverFn = opts?.recoverApprovalStateForSync ?? recoverApprovalStateForSync;
419441
419572
  if ((opts?.recoverApprovals ?? true) || !syncScopedRuntime.syncApprovalRecoveryCompleted) {
419442
419573
  try {
419443
- await recoverFn(syncScopedRuntime, scope);
419444
- syncScopedRuntime.syncApprovalRecoveryCompleted = true;
419574
+ const recovered = await recoverFn(syncScopedRuntime, scope);
419575
+ syncScopedRuntime.syncApprovalRecoveryCompleted = recovered !== "deferred";
419445
419576
  } catch (error4) {
419446
419577
  trackBoundaryError({
419447
419578
  errorType: "listener_sync_recovery_failed",
@@ -440813,7 +440944,7 @@ var init_mcp_client = __esm(() => {
440813
440944
  init_streamableHttp();
440814
440945
  DEFAULT_CLIENT_INFO = {
440815
440946
  name: "letta-code",
440816
- version: "0.32.6"
440947
+ version: "0.32.8"
440817
440948
  };
440818
440949
  });
440819
440950
 
@@ -442650,25 +442781,6 @@ var init_message_search = __esm(() => {
442650
442781
  init_transcript_search();
442651
442782
  });
442652
442783
 
442653
- // src/backend/api/agents.ts
442654
- async function getAgentContextOverview(agentId, options) {
442655
- return apiRequest("GET", `/v1/agents/${agentId}/context`, undefined, {
442656
- signal: options?.signal
442657
- });
442658
- }
442659
- async function createMinimalAgent(apiKey, name) {
442660
- return apiRequest("POST", "/v1/agents", { name }, {
442661
- baseUrl: "https://api.letta.com",
442662
- apiKey
442663
- });
442664
- }
442665
- async function getAgentRuntimeStatus(agentId, conversationIds) {
442666
- return apiRequest("GET", `/v1/agents/${encodeURIComponent(agentId)}/runtime-status`, undefined, { query: { conversation_ids: conversationIds.join(",") } });
442667
- }
442668
- var init_agents7 = __esm(() => {
442669
- init_request();
442670
- });
442671
-
442672
442784
  // src/cli/subcommands/message-status.ts
442673
442785
  async function readMessageStatus(conversationId, agentId, backend3, deps = { getAgentRuntimeStatus, getLatestConversationSuperRun }) {
442674
442786
  if (!backend3.capabilities.environmentRouting)
@@ -455161,9 +455273,6 @@ function shouldEnqueueCloudSend(values4, cloud, isAgentLaunch) {
455161
455273
  function validateSendOptions(values4) {
455162
455274
  if (values4.resume)
455163
455275
  throw new Error("--resume is interactive-only; use --conversation <id> in headless mode.");
455164
- const computer = values4.computer ?? values4.environment ?? values4.env;
455165
- if (computer !== undefined && !computer.trim())
455166
- throw new Error("Computer selector must not be empty.");
455167
455276
  if (values4["input-format"])
455168
455277
  throw new Error("Cloud message delivery does not support --input-format stream-json.");
455169
455278
  if (values4.new && values4.conversation)
@@ -455221,6 +455330,7 @@ async function tryCloudHeadlessSend(values4, prompt, backend3, isAgentLaunch, de
455221
455330
  if (!["text", "json", "stream-json"].includes(format5))
455222
455331
  throw new Error(`Invalid output format: ${format5}`);
455223
455332
  validateSendOptions(values4);
455333
+ const computer = normalizeAgentMessageComputer(values4.computer ?? values4.environment ?? values4.env);
455224
455334
  const explicitSender = values4["from-agent"];
455225
455335
  const ambientSender = env5.AGENT_ID || env5.LETTA_AGENT_ID;
455226
455336
  const sender = {
@@ -455238,7 +455348,15 @@ async function tryCloudHeadlessSend(values4, prompt, backend3, isAgentLaunch, de
455238
455348
  }
455239
455349
  if (!agentId && !conversationId)
455240
455350
  throw new Error("Choose a destination with --agent or --conversation. Ambient AGENT_ID identifies the sender.");
455241
- ({ 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));
455242
455360
  const recovery = {
455243
455361
  status_command: `letta messages status --agent ${agentId} --conversation ${conversationId}`,
455244
455362
  messages_command: `letta messages list --agent ${agentId} --conversation ${conversationId}`
@@ -455253,7 +455371,7 @@ async function tryCloudHeadlessSend(values4, prompt, backend3, isAgentLaunch, de
455253
455371
  conversationId,
455254
455372
  clientMessageId,
455255
455373
  content: buildAgentSendContent(sender, noWait, prompt),
455256
- computer: isCloudEnvironmentSelector(values4.computer ?? values4.environment ?? values4.env) ? "cloud" : values4.computer ?? values4.environment ?? values4.env
455374
+ computer
455257
455375
  }, AbortSignal.any([controller.signal, AbortSignal.timeout(30000)]));
455258
455376
  clearTimeout(submissionTimeout);
455259
455377
  if (noWait) {
@@ -456005,6 +456123,49 @@ var init_headless_tool_events = __esm(async () => {
456005
456123
  await init_approval_execution();
456006
456124
  });
456007
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
+
456008
456169
  // src/agent/defaults.ts
456009
456170
  var exports_defaults = {};
456010
456171
  __export(exports_defaults, {
@@ -463023,6 +463184,46 @@ var init_use_local_mod_adapter = __esm(async () => {
463023
463184
  import_react44 = __toESM(require_react(), 1);
463024
463185
  });
463025
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
+
463026
463227
  // src/cli/hooks/use-progress-indicator.ts
463027
463228
  function useProgressIndicator(active = true) {
463028
463229
  import_react45.useEffect(() => {
@@ -465271,7 +465472,7 @@ var init_InlineQuestionApproval = __esm(async () => {
465271
465472
  const currentQuestion = questions[currentQuestionIndex];
465272
465473
  const showOther = currentQuestion?.allowOther !== false;
465273
465474
  const baseOptions = currentQuestion ? [
465274
- ...currentQuestion.options,
465475
+ ...Array.isArray(currentQuestion.options) ? currentQuestion.options.filter((o3) => o3 != null && typeof o3 === "object" && typeof o3.label === "string" && typeof o3.description === "string") : [],
465275
465476
  ...showOther ? [{ label: "Type something.", description: "" }] : []
465276
465477
  ] : [];
465277
465478
  const optionsWithOther = currentQuestion?.multiSelect ? [...baseOptions, { label: "Submit", description: "" }] : baseOptions;
@@ -466066,15 +466267,11 @@ function getMemoryFileEditInfo(approval) {
466066
466267
  }
466067
466268
  }
466068
466269
  function getQuestions(approval) {
466069
- try {
466070
- const args = JSON.parse(approval.toolArgs || "{}");
466071
- return args.questions || [];
466072
- } catch {
466073
- return [];
466074
- }
466270
+ return parseAskUserQuestions(approval);
466075
466271
  }
466076
466272
  var import_react54, jsx_dev_runtime30, ApprovalSwitch;
466077
466273
  var init_ApprovalSwitch = __esm(async () => {
466274
+ init_ask_user_questions();
466078
466275
  await __promiseAll([
466079
466276
  init_tool_name_mapping(),
466080
466277
  init_InlineBashApproval(),
@@ -503256,10 +503453,11 @@ function buildApprovalBatchKey(approvals) {
503256
503453
 
503257
503454
  // src/cli/app/approval-questions.ts
503258
503455
  function getQuestionsFromApproval(approval) {
503259
- const parsed = safeJsonParseOr(approval.toolArgs, {});
503260
- return parsed.questions || [];
503456
+ return parseAskUserQuestions(approval);
503261
503457
  }
503262
- var init_approval_questions = () => {};
503458
+ var init_approval_questions = __esm(() => {
503459
+ init_ask_user_questions();
503460
+ });
503263
503461
 
503264
503462
  // src/cli/app/errors.ts
503265
503463
  function extractErrorMeta(e2) {
@@ -522119,4 +522317,4 @@ function registerBunOAuthFlows() {
522119
522317
  registerBunOAuthFlows();
522120
522318
  await init_src5().then(() => exports_src2);
522121
522319
 
522122
- //# debugId=4B57A44583E7B14564756E2164756E21
522320
+ //# debugId=A89D538A98A9FFDD64756E2164756E21