@letta-ai/letta-code 0.32.7 → 0.32.9

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 (42) hide show
  1. package/dist/mcp-client.js +2 -2
  2. package/dist/mcp-client.js.map +1 -1
  3. package/dist/types/agent/message-attribution.d.ts +10 -0
  4. package/dist/types/agent/message-attribution.d.ts.map +1 -0
  5. package/dist/types/agent/subagents/manager.d.ts.map +1 -1
  6. package/dist/types/agent/subagents/names.d.ts +7 -0
  7. package/dist/types/agent/subagents/names.d.ts.map +1 -0
  8. package/dist/types/agent/subagents/subagent-launcher.d.ts +2 -0
  9. package/dist/types/agent/subagents/subagent-launcher.d.ts.map +1 -1
  10. package/dist/types/backend/api/agent-message.d.ts +5 -0
  11. package/dist/types/backend/api/agent-message.d.ts.map +1 -1
  12. package/dist/types/backend/api/metadata.d.ts +1 -0
  13. package/dist/types/backend/api/metadata.d.ts.map +1 -1
  14. package/dist/types/backend/backend.d.ts.map +1 -1
  15. package/dist/types/queue/queue-runtime.d.ts.map +1 -1
  16. package/dist/types/runtime-context.d.ts +2 -0
  17. package/dist/types/runtime-context.d.ts.map +1 -1
  18. package/dist/types/telemetry/index.d.ts +1 -0
  19. package/dist/types/telemetry/index.d.ts.map +1 -1
  20. package/dist/types/tools/impl/send-agent-message.d.ts.map +1 -1
  21. package/dist/types/tools/impl/task.d.ts.map +1 -1
  22. package/dist/types/tools/manager.d.ts +1 -3
  23. package/dist/types/tools/manager.d.ts.map +1 -1
  24. package/dist/types/types/protocol_v2.d.ts +3 -5
  25. package/dist/types/types/protocol_v2.d.ts.map +1 -1
  26. package/dist/types/utils/subagent-launch-marker.d.ts +1 -0
  27. package/dist/types/utils/subagent-launch-marker.d.ts.map +1 -1
  28. package/dist/types/websocket/listener/protocol-outbound.d.ts.map +1 -1
  29. package/dist/types/websocket/listener/runtime.d.ts.map +1 -1
  30. package/dist/types/websocket/listener/types.d.ts +12 -9
  31. package/dist/types/websocket/listener/types.d.ts.map +1 -1
  32. package/letta.js +500 -292
  33. package/package.json +1 -1
  34. package/scripts/run-unit-tests.cjs +11 -1
  35. package/scripts/source-file-size-baseline.json +5 -5
  36. package/scripts/test-home-preload.ts +9 -1
  37. package/scripts/test-sharding.cjs +16 -0
  38. package/scripts/test-sharding.test.cjs +37 -0
  39. package/scripts/unit-test-impact.cjs +14 -6
  40. package/scripts/unit-test-impact.test.cjs +41 -9
  41. package/skills/letta-guide/SKILL.md +3 -1
  42. package/skills/working-across-computers/SKILL.md +5 -1
package/letta.js CHANGED
@@ -3933,6 +3933,18 @@ var init_paths = __esm(() => {
3933
3933
  init_local_backend_paths();
3934
3934
  });
3935
3935
 
3936
+ // src/agent/acting-user.ts
3937
+ function resolveActingUserId(explicitActingUserId, runtimeActingUserId, env = process.env) {
3938
+ return explicitActingUserId ?? runtimeActingUserId ?? env[ACTING_USER_ID_ENV];
3939
+ }
3940
+ function actingUserRequestOptions(actingUserId) {
3941
+ if (!actingUserId) {
3942
+ return;
3943
+ }
3944
+ return { headers: { [ACTING_USER_ID_HEADER]: actingUserId } };
3945
+ }
3946
+ var ACTING_USER_ID_HEADER = "X-Letta-Acting-User-Id", ACTING_USER_ID_ENV = "LETTA_ACTING_USER_ID";
3947
+
3936
3948
  // src/helpers/usable-directory.ts
3937
3949
  import { statSync } from "node:fs";
3938
3950
  function readDirectoryStats(dirPath) {
@@ -3968,6 +3980,9 @@ import { homedir as homedir2, tmpdir } from "node:os";
3968
3980
  function getRuntimeContext() {
3969
3981
  return runtimeContextStorage.getStore();
3970
3982
  }
3983
+ function getRuntimeActingUserId() {
3984
+ return resolveActingUserId(undefined, getRuntimeContext()?.actingUserId);
3985
+ }
3971
3986
  function runWithRuntimeContext(snapshot, fn) {
3972
3987
  const parent = runtimeContextStorage.getStore();
3973
3988
  return runtimeContextStorage.run({
@@ -5515,7 +5530,7 @@ var package_default;
5515
5530
  var init_package = __esm(() => {
5516
5531
  package_default = {
5517
5532
  name: "@letta-ai/letta-code",
5518
- version: "0.32.7",
5533
+ version: "0.32.9",
5519
5534
  lettaStartupLogProtocol: 1,
5520
5535
  description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
5521
5536
  type: "module",
@@ -5945,7 +5960,8 @@ async function submitTelemetryMetadata(apiKey, deviceId, payload, options) {
5945
5960
  await apiRequest("POST", "/v1/metadata/telemetry", payload, {
5946
5961
  ...config,
5947
5962
  headers: {
5948
- "X-Letta-Code-Device-ID": deviceId
5963
+ "X-Letta-Code-Device-ID": deviceId,
5964
+ ...actingUserRequestOptions(options?.actingUserId)?.headers
5949
5965
  },
5950
5966
  signal: options?.signal
5951
5967
  });
@@ -6167,6 +6183,7 @@ function isNonActionableError(message) {
6167
6183
 
6168
6184
  class TelemetryManager {
6169
6185
  events = [];
6186
+ eventActingUsers = new WeakMap;
6170
6187
  sessionId;
6171
6188
  deviceId = null;
6172
6189
  currentAgentId = null;
@@ -6291,6 +6308,7 @@ class TelemetryManager {
6291
6308
  backend: resolveTelemetryBackend()
6292
6309
  }
6293
6310
  };
6311
+ this.eventActingUsers.set(event, getRuntimeActingUserId());
6294
6312
  this.events.push(event);
6295
6313
  if (this.events.length >= this.MAX_BATCH_SIZE) {
6296
6314
  this.flush().catch((err) => {
@@ -6509,15 +6527,27 @@ class TelemetryManager {
6509
6527
  this.events = [];
6510
6528
  const apiKey = await this.resolveTelemetryApiKey();
6511
6529
  const deviceId = this.getTelemetryDeviceId();
6512
- try {
6513
- await submitTelemetryMetadata(apiKey, deviceId, {
6514
- service: "letta-code",
6515
- server_version: this.serverVersion || undefined,
6516
- events: eventsToSend
6517
- }, { signal: AbortSignal.timeout(5000) });
6518
- } catch (_error) {
6519
- this.events.unshift(...eventsToSend);
6520
- }
6530
+ const groups = new Map;
6531
+ for (const event of eventsToSend) {
6532
+ const actingUserId = this.eventActingUsers.get(event);
6533
+ const group = groups.get(actingUserId) ?? [];
6534
+ group.push(event);
6535
+ groups.set(actingUserId, group);
6536
+ }
6537
+ const failed = new Set;
6538
+ await Promise.all([...groups].map(async ([actingUserId, events]) => {
6539
+ try {
6540
+ await submitTelemetryMetadata(apiKey, deviceId, {
6541
+ service: "letta-code",
6542
+ server_version: this.serverVersion || undefined,
6543
+ events
6544
+ }, { signal: AbortSignal.timeout(5000), actingUserId });
6545
+ } catch {
6546
+ for (const event of events)
6547
+ failed.add(event);
6548
+ }
6549
+ }));
6550
+ this.events.unshift(...eventsToSend.filter((event) => failed.has(event)));
6521
6551
  }
6522
6552
  async drain() {
6523
6553
  if (!this.isTelemetryEnabled()) {
@@ -6554,6 +6584,7 @@ var init_telemetry = __esm(() => {
6554
6584
  init_metadata();
6555
6585
  init_server_url();
6556
6586
  init_paths();
6587
+ init_runtime_context();
6557
6588
  init_settings_manager();
6558
6589
  init_debug();
6559
6590
  init_version();
@@ -101183,7 +101214,7 @@ function cancelWorktreeWatcherIdleStop(listener, agentId, conversationId) {
101183
101214
  }
101184
101215
  }
101185
101216
  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)) {
101217
+ 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
101218
  return false;
101188
101219
  }
101189
101220
  if (runtime.listener.conversationRuntimes.get(runtime.key) !== runtime) {
@@ -101243,7 +101274,6 @@ function createConversationRuntime(listener, agentId, conversationId) {
101243
101274
  acceptedInputDispositions: new Map,
101244
101275
  pendingApprovalResolvers: new Map,
101245
101276
  recoveredApprovalState: null,
101246
- syncApprovalRecoveryCompleted: false,
101247
101277
  expectedTeleportId: null,
101248
101278
  expectedTeleportExpiresAt: null,
101249
101279
  get lastStopReason() {
@@ -101970,7 +102000,7 @@ function consumeSubagentLaunch(env3) {
101970
102000
  delete env3[SUBAGENT_LAUNCH_ENV];
101971
102001
  return isLaunch;
101972
102002
  }
101973
- var SUBAGENT_LAUNCH_ENV = "LETTA_SUBAGENT_LAUNCH", SUBAGENT_LAUNCH_PROFILE_ENV = "LETTA_SUBAGENT_LAUNCH_PROFILE", LISTENER_CONNECTION_ENV = "LETTA_RUNTIME_LISTENER_CONNECTION_ID";
102003
+ var SUBAGENT_LAUNCH_ENV = "LETTA_SUBAGENT_LAUNCH", SUBAGENT_LAUNCH_PROFILE_ENV = "LETTA_SUBAGENT_LAUNCH_PROFILE", SUBAGENT_NAME_ENV = "LETTA_SUBAGENT_NAME", LISTENER_CONNECTION_ENV = "LETTA_RUNTIME_LISTENER_CONNECTION_ID";
101974
102004
 
101975
102005
  // src/tools/impl/ripgrep-manager.ts
101976
102006
  import { spawnSync } from "node:child_process";
@@ -107479,42 +107509,44 @@ function emitDequeuedUserMessage(socket, runtime, incoming, batch) {
107479
107509
  if (batch.items.length > 0 && batch.items.every((item) => item.kind === "mod_continue")) {
107480
107510
  return;
107481
107511
  }
107482
- const firstUserPayload = incoming.messages.find((payload) => ("content" in payload));
107483
- if (!firstUserPayload)
107484
- return;
107485
- const rawContent = firstUserPayload.content;
107486
- let content;
107487
- if (typeof rawContent === "string") {
107488
- content = replaceCronPromptsForDisplay(rawContent, batch).replace(SYSTEM_REMINDER_RE, "").trim();
107489
- } else if (Array.isArray(rawContent)) {
107490
- content = rawContent.flatMap((part) => {
107491
- if (isTextContentPart(part)) {
107492
- const cronDisplay = getCronPromptDisplayForText(part.text, batch);
107493
- if (cronDisplay !== null) {
107494
- return [{ ...part, text: cronDisplay }];
107512
+ for (const payload of incoming.messages) {
107513
+ if (!("content" in payload) || payload.role !== "user")
107514
+ continue;
107515
+ const rawContent = payload.content;
107516
+ let content;
107517
+ if (typeof rawContent === "string") {
107518
+ content = replaceCronPromptsForDisplay(rawContent, batch).replace(SYSTEM_REMINDER_RE, "").trim();
107519
+ } else if (Array.isArray(rawContent)) {
107520
+ content = rawContent.flatMap((part) => {
107521
+ if (isTextContentPart(part)) {
107522
+ const cronDisplay = getCronPromptDisplayForText(part.text, batch);
107523
+ if (cronDisplay !== null) {
107524
+ return [{ ...part, text: cronDisplay }];
107525
+ }
107495
107526
  }
107496
- }
107497
- return isSystemReminderPart(part) ? [] : [part];
107527
+ return isSystemReminderPart(part) ? [] : [part];
107528
+ });
107529
+ } else {
107530
+ continue;
107531
+ }
107532
+ const hasContent = typeof content === "string" ? content.length > 0 : Array.isArray(content) && content.length > 0;
107533
+ if (!hasContent)
107534
+ continue;
107535
+ payload.otid ??= payload.client_message_id ?? crypto.randomUUID();
107536
+ const otid = payload.otid;
107537
+ emitCanonicalMessageDelta(socket, runtime, {
107538
+ type: "message",
107539
+ id: `user-msg-${crypto.randomUUID()}`,
107540
+ date: new Date().toISOString(),
107541
+ message_type: "user_message",
107542
+ content,
107543
+ otid,
107544
+ created_by_id: payload.attribution === undefined ? incoming.actingUserId : payload.attribution.acting_user_id
107545
+ }, {
107546
+ agent_id: incoming.agentId,
107547
+ conversation_id: incoming.conversationId
107498
107548
  });
107499
- } else {
107500
- return;
107501
107549
  }
107502
- const hasContent = typeof content === "string" ? content.length > 0 : Array.isArray(content) && content.length > 0;
107503
- if (!hasContent)
107504
- return;
107505
- const otid = firstUserPayload.otid ?? firstUserPayload.client_message_id ?? batch.batchId;
107506
- emitCanonicalMessageDelta(socket, runtime, {
107507
- type: "message",
107508
- id: `user-msg-${crypto.randomUUID()}`,
107509
- date: new Date().toISOString(),
107510
- message_type: "user_message",
107511
- content,
107512
- otid,
107513
- created_by_id: incoming.actingUserId
107514
- }, {
107515
- agent_id: incoming.agentId,
107516
- conversation_id: incoming.conversationId
107517
- });
107518
107550
  }
107519
107551
  function emitQueueUpdateIfOpen(runtime, scope, removed = []) {
107520
107552
  const listener = getListenerRuntime(runtime);
@@ -122324,18 +122356,6 @@ var init_search_file_content_gemini = __esm(() => {
122324
122356
  init_grep();
122325
122357
  });
122326
122358
 
122327
- // src/agent/acting-user.ts
122328
- function resolveActingUserId(explicitActingUserId, runtimeActingUserId, env3 = process.env) {
122329
- return explicitActingUserId ?? runtimeActingUserId ?? env3[ACTING_USER_ID_ENV];
122330
- }
122331
- function actingUserRequestOptions(actingUserId) {
122332
- if (!actingUserId) {
122333
- return;
122334
- }
122335
- return { headers: { [ACTING_USER_ID_HEADER]: actingUserId } };
122336
- }
122337
- var ACTING_USER_ID_HEADER = "X-Letta-Acting-User-Id", ACTING_USER_ID_ENV = "LETTA_ACTING_USER_ID";
122338
-
122339
122359
  // src/backend/api/agent-message.ts
122340
122360
  function normalizeAgentMessageComputer(value) {
122341
122361
  if (value == null)
@@ -122398,6 +122418,9 @@ async function resolveAgentMessageDestination(input, backend, signal) {
122398
122418
  const conversation = await backend.createConversation({ agent_id: agentId, ...input.senderAgentId ? { hidden: true } : {} }, options);
122399
122419
  conversationId = conversation.id;
122400
122420
  }
122421
+ if (agentId === input.currentConversation?.agentId && conversationId === input.currentConversation?.conversationId) {
122422
+ throw new Error("Cannot message the current conversation. Use a Monitor or schedule for self-invocation.");
122423
+ }
122401
122424
  return { agentId, conversationId };
122402
122425
  }
122403
122426
  var init_agent_message = () => {};
@@ -122488,7 +122511,8 @@ async function send_agent_message(args, deps = {}) {
122488
122511
  agentId: args.agent_id,
122489
122512
  conversationId: args.conversation_id,
122490
122513
  senderAgentId: sender.agentId,
122491
- actingUserId
122514
+ actingUserId,
122515
+ currentConversation: sender
122492
122516
  }, backend, signal);
122493
122517
  signal.throwIfAborted();
122494
122518
  submissionAttempted = true;
@@ -123639,6 +123663,146 @@ var init_context_budget = __esm(() => {
123639
123663
  REFLECTION_STARTUP_CONTEXT_CHAR_LIMIT = REFLECTION_STARTUP_CONTEXT_TOKEN_LIMIT * STARTUP_CONTEXT_ESTIMATED_CHARS_PER_TOKEN;
123640
123664
  });
123641
123665
 
123666
+ // src/agent/subagents/names.ts
123667
+ function createSubagentNameAllocator(random = Math.random) {
123668
+ let available = [];
123669
+ let round = 0;
123670
+ const ordinals = new Intl.PluralRules("en", { type: "ordinal" });
123671
+ const suffixes = { one: "st", two: "nd", few: "rd" };
123672
+ return () => {
123673
+ if (available.length === 0) {
123674
+ available = [...SUBAGENT_NAMES];
123675
+ round += 1;
123676
+ }
123677
+ const index = Math.floor(random() * available.length);
123678
+ const name = available.splice(index, 1)[0];
123679
+ const ordinal = `${round}${suffixes[ordinals.select(round)] ?? "th"}`;
123680
+ return `${name}${round === 1 ? "" : ` the ${ordinal}`} (subagent)`;
123681
+ };
123682
+ }
123683
+ function allocateSubagentName() {
123684
+ return nextName();
123685
+ }
123686
+ function resolveCreatedAgentName(name, isSubagent, assignedName) {
123687
+ return name ?? (isSubagent ? assignedName || allocateSubagentName() : DEFAULT_AGENT_NAME);
123688
+ }
123689
+ var SUBAGENT_NAMES, nextName;
123690
+ var init_names = __esm(() => {
123691
+ init_constants2();
123692
+ SUBAGENT_NAMES = [
123693
+ "Deckard",
123694
+ "Roy Batty",
123695
+ "Pris",
123696
+ "Zhora",
123697
+ "Gaff",
123698
+ "Tyrell",
123699
+ "K",
123700
+ "Joi",
123701
+ "Luv",
123702
+ "Sapper",
123703
+ "Freysa",
123704
+ "Ava",
123705
+ "Kyoko",
123706
+ "V",
123707
+ "Silverhand",
123708
+ "Panam",
123709
+ "Rogue",
123710
+ "Alt Cunningham",
123711
+ "Takemura",
123712
+ "Hanako",
123713
+ "Yorinobu",
123714
+ "Saburo",
123715
+ "Oda",
123716
+ "T-Bug",
123717
+ "Delamain",
123718
+ "Wakako",
123719
+ "Padre",
123720
+ "El Capitan",
123721
+ "Mr. Hands",
123722
+ "Placide",
123723
+ "Dum Dum",
123724
+ "Nix",
123725
+ "Weyland",
123726
+ "Scorpion",
123727
+ "Blue Moon",
123728
+ "Red Menace",
123729
+ "Purple Force",
123730
+ "Lizzy Wizzy",
123731
+ "Ozob",
123732
+ "Smasher",
123733
+ "Songbird",
123734
+ "Slider",
123735
+ "Aguilar",
123736
+ "Dogmeat",
123737
+ "Codsworth",
123738
+ "Nick Valentine",
123739
+ "Yes Man",
123740
+ "Mr. House",
123741
+ "ED-E",
123742
+ "Fisto",
123743
+ "Liberty Prime",
123744
+ "Three Dog",
123745
+ "Charon",
123746
+ "Fawkes",
123747
+ "Ulysses",
123748
+ "The Master",
123749
+ "Muggy",
123750
+ "Dr. Mobius",
123751
+ "Dr. Klein",
123752
+ "Old Longfellow",
123753
+ "Pickman",
123754
+ "Butch DeLoria",
123755
+ "Fantastic",
123756
+ "Muad'Dib",
123757
+ "Stilgar",
123758
+ "Chani",
123759
+ "Gurney Halleck",
123760
+ "Duncan Idaho",
123761
+ "Thufir Hawat",
123762
+ "Liet-Kynes",
123763
+ "Shadout Mapes",
123764
+ "Feyd-Rautha",
123765
+ "Rabban",
123766
+ "Piter De Vries",
123767
+ "Irulan",
123768
+ "Scytale",
123769
+ "Hayt",
123770
+ "Bijaz",
123771
+ "Moneo",
123772
+ "Miles Teg",
123773
+ "Darwi Odrade",
123774
+ "Sheeana",
123775
+ "Shaddam IV",
123776
+ "Count Fenring",
123777
+ "Wintermute",
123778
+ "Neuromancer",
123779
+ "Dixie Flatline",
123780
+ "Molly Millions",
123781
+ "Lady 3Jane",
123782
+ "Armitage",
123783
+ "Maelcum",
123784
+ "Ratz",
123785
+ "The Finn",
123786
+ "Hiro Protagonist",
123787
+ "Y.T.",
123788
+ "Raven",
123789
+ "Uncle Enzo",
123790
+ "R. Daneel Olivaw",
123791
+ "R. Giskard Reventlov",
123792
+ "Hari Seldon",
123793
+ "Dors Venabili",
123794
+ "The Mule",
123795
+ "HAL 9000",
123796
+ "Aineko",
123797
+ "Eschaton",
123798
+ "Mycroft Canner",
123799
+ "J.E.D.D. Mason",
123800
+ "Sniper",
123801
+ "Eureka Weeksbooth"
123802
+ ];
123803
+ nextName = createSubagentNameAllocator();
123804
+ });
123805
+
123642
123806
  // src/mods/capabilities.ts
123643
123807
  function isModCapabilityId(value) {
123644
123808
  return MOD_CAPABILITY_ID_SET.has(value);
@@ -123807,6 +123971,9 @@ function composeSubagentChildEnv(options) {
123807
123971
  ...parentAgentId && { LETTA_PARENT_AGENT_ID: parentAgentId },
123808
123972
  ...transcriptPath && { TRANSCRIPT_PATH: transcriptPath }
123809
123973
  };
123974
+ delete childEnv[SUBAGENT_NAME_ENV];
123975
+ if (options.subagentName)
123976
+ childEnv[SUBAGENT_NAME_ENV] = options.subagentName;
123810
123977
  if (backendMode === "local") {
123811
123978
  childEnv.LETTA_LOCAL_BACKEND_EXPERIMENTAL = "1";
123812
123979
  if (localBackendStorageDir) {
@@ -124336,7 +124503,8 @@ async function executeSubagent(type3, config, model, userPrompt, subagentId, isR
124336
124503
  inheritedApiKey,
124337
124504
  inheritedBaseUrl,
124338
124505
  actingUserId: actingUserIdOverride,
124339
- transcriptPath
124506
+ transcriptPath,
124507
+ subagentName: existingAgentId || existingConversationId ? undefined : allocateSubagentName()
124340
124508
  });
124341
124509
  const sandbox = wrapSubagentLauncher({
124342
124510
  launcher,
@@ -124622,6 +124790,7 @@ var init_manager3 = __esm(() => {
124622
124790
  init_subagent_stdout_failure();
124623
124791
  init_subagents();
124624
124792
  init_context_budget();
124793
+ init_names();
124625
124794
  init_subagent_launcher();
124626
124795
  init_subagent_model();
124627
124796
  init_subagent_stream();
@@ -125061,7 +125230,10 @@ async function task(args) {
125061
125230
  const agentId = linkedAgent?.agentId ?? null;
125062
125231
  const agentIdLine = agentId ? `
125063
125232
  Agent ID: ${agentId}` : "";
125064
- return `Task running in background with task ID: ${taskId}${agentIdLine}
125233
+ const conversationId = linkedAgent?.conversationId ?? null;
125234
+ const conversationIdLine = conversationId ? `
125235
+ Conversation ID: ${conversationId}` : "";
125236
+ return `Task running in background with task ID: ${taskId}${agentIdLine}${conversationIdLine}
125065
125237
  Output file: ${outputFile}
125066
125238
 
125067
125239
  You will be notified automatically when this task completes — a <task-notification> message will be delivered with the result. No need to poll, sleep-wait, or check the output file. Just continue with your current work.`;
@@ -134238,7 +134410,7 @@ async function executeToolInner(name, args, options) {
134238
134410
  return createModPermissionToolResult(permissionDecision2);
134239
134411
  }
134240
134412
  }
134241
- return executeExternalTool(options?.toolCallId ?? `ext-${Date.now()}`, name, eventArgs2, externalTool?.executor ?? activeExternalExecutor, externalTool);
134413
+ return runWithRuntimeContext(executionScope, () => executeExternalTool(options?.toolCallId ?? `ext-${Date.now()}`, name, eventArgs2, externalTool?.executor ?? activeExternalExecutor, externalTool));
134242
134414
  }
134243
134415
  const internalName = resolveInternalToolName(name, activeRegistry);
134244
134416
  const tool = internalName ? activeRegistry.get(internalName) : undefined;
@@ -144705,7 +144877,15 @@ class APIBackend {
144705
144877
  }
144706
144878
  async createConversationMessageStream(conversationId, body, options) {
144707
144879
  const client = await this.getClient();
144708
- return client.conversations.messages.create(conversationId, body, options);
144880
+ const { data: stream12, response } = await client.conversations.messages.create(conversationId, body, options).withResponse();
144881
+ const contentType = response.headers.get("content-type") ?? "";
144882
+ if (contentType.split(";")[0]?.trim().toLowerCase() !== "text/event-stream") {
144883
+ stream12.controller.abort();
144884
+ throw new APIConnectionError({
144885
+ message: `Connection error: expected text/event-stream, received ${contentType || "no content type"} (HTTP ${response.status}).`
144886
+ });
144887
+ }
144888
+ return stream12;
144709
144889
  }
144710
144890
  async streamConversationMessages(conversationId, body, options) {
144711
144891
  const client = await this.getClient();
@@ -144824,6 +145004,7 @@ function __testSetBackend(nextBackend) {
144824
145004
  }
144825
145005
  var DEFAULT_CONVERSATION_MESSAGE_ORDER = "desc", backend = null;
144826
145006
  var init_backend = __esm(() => {
145007
+ init_error();
144827
145008
  init_chatgpt_usage_service();
144828
145009
  init_server_url();
144829
145010
  init_backend_mode();
@@ -145339,7 +145520,7 @@ function resolveCreatedAgentMemfsConfig(options) {
145339
145520
  const memoryPromptMode = (options.requestedMemoryPromptMode !== "standard" ? options.requestedMemoryPromptMode : undefined) ?? (enableMemfs ? options.capabilities.localMemfs ? "local-memfs" : "memfs" : "standard");
145340
145521
  return { enableMemfs, memoryPromptMode };
145341
145522
  }
145342
- async function createAgent(nameOrOptions = DEFAULT_AGENT_NAME, model, embeddingModel, updateArgs, skillsDirectory, parallelToolCalls = true, systemPromptPreset, baseTools) {
145523
+ async function createAgent(nameOrOptions, model, embeddingModel, updateArgs, skillsDirectory, parallelToolCalls = true, systemPromptPreset, baseTools) {
145343
145524
  let options;
145344
145525
  if (typeof nameOrOptions === "object") {
145345
145526
  options = nameOrOptions;
@@ -145355,10 +145536,11 @@ async function createAgent(nameOrOptions = DEFAULT_AGENT_NAME, model, embeddingM
145355
145536
  baseTools
145356
145537
  };
145357
145538
  }
145358
- const name = options.name ?? DEFAULT_AGENT_NAME;
145359
145539
  const embeddingModelVal = options.embeddingModel;
145360
145540
  const parallelToolCallsVal = options.parallelToolCalls ?? true;
145361
145541
  const isSubagent = process.env.LETTA_CODE_AGENT_ROLE === "subagent";
145542
+ const name = resolveCreatedAgentName(options.name, isSubagent, process.env[SUBAGENT_NAME_ENV]);
145543
+ delete process.env[SUBAGENT_NAME_ENV];
145362
145544
  let modelHandle;
145363
145545
  if (options.model) {
145364
145546
  const resolved = resolveModel(options.model);
@@ -145477,7 +145659,6 @@ async function createAgent(nameOrOptions = DEFAULT_AGENT_NAME, model, embeddingM
145477
145659
  var init_create5 = __esm(() => {
145478
145660
  init_backend2();
145479
145661
  init_request();
145480
- init_constants2();
145481
145662
  init_settings_manager();
145482
145663
  init_debug();
145483
145664
  init_available_models();
@@ -145486,6 +145667,7 @@ var init_create5 = __esm(() => {
145486
145667
  init_model();
145487
145668
  init_modify();
145488
145669
  init_prompt_assets();
145670
+ init_names();
145489
145671
  init_system_prompt_resolution();
145490
145672
  init_system_prompt_versioning();
145491
145673
  });
@@ -146705,14 +146887,14 @@ var require_react_development = __commonJS((exports, module3) => {
146705
146887
  return 1;
146706
146888
  }
146707
146889
  var child;
146708
- var nextName;
146890
+ var nextName2;
146709
146891
  var subtreeCount = 0;
146710
146892
  var nextNamePrefix = nameSoFar === "" ? SEPARATOR : nameSoFar + SUBSEPARATOR;
146711
146893
  if (isArray5(children)) {
146712
146894
  for (var i2 = 0;i2 < children.length; i2++) {
146713
146895
  child = children[i2];
146714
- nextName = nextNamePrefix + getElementKey(child, i2);
146715
- subtreeCount += mapIntoArray(child, array2, escapedPrefix, nextName, callback);
146896
+ nextName2 = nextNamePrefix + getElementKey(child, i2);
146897
+ subtreeCount += mapIntoArray(child, array2, escapedPrefix, nextName2, callback);
146716
146898
  }
146717
146899
  } else {
146718
146900
  var iteratorFn = getIteratorFn(children);
@@ -146731,8 +146913,8 @@ var require_react_development = __commonJS((exports, module3) => {
146731
146913
  var ii = 0;
146732
146914
  while (!(step = iterator.next()).done) {
146733
146915
  child = step.value;
146734
- nextName = nextNamePrefix + getElementKey(child, ii++);
146735
- subtreeCount += mapIntoArray(child, array2, escapedPrefix, nextName, callback);
146916
+ nextName2 = nextNamePrefix + getElementKey(child, ii++);
146917
+ subtreeCount += mapIntoArray(child, array2, escapedPrefix, nextName2, callback);
146736
146918
  }
146737
146919
  } else if (type3 === "object") {
146738
146920
  var childrenString = String(children);
@@ -217409,17 +217591,12 @@ class QueueRuntime {
217409
217591
  const batch = [];
217410
217592
  const first = this.store.find((item) => !item.paused);
217411
217593
  if (first && isCoalescable(first.kind)) {
217412
- let batchActingUserId = first.actingUserId;
217413
217594
  for (const item of this.store) {
217414
217595
  if (item.paused)
217415
217596
  continue;
217416
217597
  if (!isCoalescable(item.kind) || !hasSameScope(first, item))
217417
217598
  break;
217418
- if (batchActingUserId && item.actingUserId && batchActingUserId !== item.actingUserId) {
217419
- break;
217420
- }
217421
217599
  batch.push(item);
217422
- batchActingUserId ??= item.actingUserId;
217423
217600
  }
217424
217601
  } else if (first) {
217425
217602
  batch.push(first);
@@ -217565,47 +217742,13 @@ var init_queue_runtime = __esm(() => {
217565
217742
  init_debug();
217566
217743
  });
217567
217744
 
217568
- // src/queue/turn-queue-runtime.ts
217569
- function stringifyUnexpectedContent(content) {
217570
- try {
217571
- return JSON.stringify(content) ?? String(content);
217572
- } catch {
217573
- return String(content);
217574
- }
217575
- }
217576
- function appendContentParts(target2, content) {
217577
- if (typeof content === "string") {
217578
- target2.push({ type: "text", text: content });
217579
- return;
217745
+ // src/agent/message-attribution.ts
217746
+ function withMessageAttribution(message, actingUserId) {
217747
+ const attributed = message;
217748
+ if (message.role !== "user" || attributed.attribution !== undefined || !actingUserId) {
217749
+ return attributed;
217580
217750
  }
217581
- if (!Array.isArray(content)) {
217582
- if (content === null || content === undefined)
217583
- return;
217584
- target2.push({ type: "text", text: stringifyUnexpectedContent(content) });
217585
- return;
217586
- }
217587
- target2.push(...content);
217588
- }
217589
- function mergeQueuedTurnInput(queued, options) {
217590
- if (queued.length === 0) {
217591
- return null;
217592
- }
217593
- const separatorText = options.separatorText ?? `
217594
- `;
217595
- const mergedParts = [];
217596
- let isFirst = true;
217597
- for (const item of queued) {
217598
- if (!isFirst) {
217599
- mergedParts.push({ type: "text", text: separatorText });
217600
- }
217601
- isFirst = false;
217602
- if (item.kind === "task_notification" || item.kind === "cron_prompt") {
217603
- mergedParts.push({ type: "text", text: item.text });
217604
- continue;
217605
- }
217606
- appendContentParts(mergedParts, options.normalizeUserContent(item.content));
217607
- }
217608
- return mergedParts.length > 0 ? mergedParts : null;
217751
+ return { ...message, attribution: { acting_user_id: actingUserId } };
217609
217752
  }
217610
217753
 
217611
217754
  // src/websocket/helpers/listener-queue-adapter.ts
@@ -217657,109 +217800,37 @@ function getQueueItemsScope(items3) {
217657
217800
  function hasSameQueueScope(a2, b3) {
217658
217801
  return (a2.agentId ?? null) === (b3.agentId ?? null) && (a2.conversationId ?? null) === (b3.conversationId ?? null);
217659
217802
  }
217660
- function mergeDequeuedBatchContent(items3) {
217661
- const queuedInputs = [];
217662
- for (const item of items3) {
217663
- if (item.kind === "message") {
217664
- queuedInputs.push({
217665
- kind: "user",
217666
- content: item.content
217667
- });
217668
- continue;
217669
- }
217670
- if (item.kind === "task_notification") {
217671
- queuedInputs.push({
217672
- kind: "task_notification",
217673
- text: item.text
217674
- });
217675
- continue;
217676
- }
217677
- if (item.kind === "cron_prompt") {
217678
- queuedInputs.push({
217679
- kind: "cron_prompt",
217680
- text: item.text
217681
- });
217682
- continue;
217683
- }
217684
- if (item.kind === "mod_continue") {
217685
- queuedInputs.push({
217686
- kind: "user",
217687
- content: item.text
217688
- });
217689
- }
217690
- }
217691
- return mergeQueuedTurnInput(queuedInputs, {
217692
- normalizeUserContent: (content) => content
217693
- });
217694
- }
217695
- function getPrimaryQueueMessageItem(items3) {
217696
- for (const item of items3) {
217697
- if (item.kind === "message") {
217698
- return item;
217699
- }
217700
- }
217701
- return null;
217702
- }
217703
- function pickBatchActingUserId(items3) {
217704
- for (let i4 = items3.length - 1;i4 >= 0; i4 -= 1) {
217705
- const actingUserId = items3[i4]?.actingUserId;
217706
- if (actingUserId) {
217707
- return actingUserId;
217708
- }
217709
- }
217710
- return;
217711
- }
217712
217803
  function buildQueuedTurnMessage(runtime, batch) {
217713
- const actingUserId = pickBatchActingUserId(batch.items);
217714
- const primaryItem = getPrimaryQueueMessageItem(batch.items);
217715
- if (!primaryItem) {
217716
- for (const item of batch.items) {
217717
- runtime.queuedMessagesByItemId.delete(item.id);
217718
- }
217719
- const mergedContent2 = mergeDequeuedBatchContent(batch.items);
217720
- if (mergedContent2 === null) {
217721
- return null;
217722
- }
217723
- const scopeItem = batch.items[0];
217724
- return {
217725
- type: "message",
217726
- agentId: scopeItem?.agentId ?? runtime.agentId ?? undefined,
217727
- conversationId: scopeItem?.conversationId ?? runtime.conversationId,
217728
- ...actingUserId ? { actingUserId } : {},
217729
- messages: [
217730
- {
217731
- role: "user",
217732
- content: mergedContent2,
217733
- otid: crypto.randomUUID()
217734
- }
217735
- ]
217736
- };
217737
- }
217738
- const template3 = runtime.queuedMessagesByItemId.get(primaryItem.id);
217804
+ let template3;
217805
+ const messages = [];
217739
217806
  for (const item of batch.items) {
217807
+ const incoming = runtime.queuedMessagesByItemId.get(item.id);
217808
+ if (item.kind === "message" && incoming) {
217809
+ template3 ??= {
217810
+ ...incoming,
217811
+ actingUserId: incoming.actingUserId ?? item.actingUserId
217812
+ };
217813
+ messages.push(...incoming.messages.map((message) => ("content" in message) ? withMessageAttribution(message, item.actingUserId ?? incoming.actingUserId) : message));
217814
+ } else if (item.kind === "message") {
217815
+ messages.push(withMessageAttribution({ role: "user", content: item.content }, item.actingUserId));
217816
+ } else if (isCoalescable(item.kind) && "text" in item) {
217817
+ messages.push({
217818
+ role: "user",
217819
+ content: item.text,
217820
+ otid: crypto.randomUUID(),
217821
+ attribution: {}
217822
+ });
217823
+ }
217740
217824
  runtime.queuedMessagesByItemId.delete(item.id);
217741
217825
  }
217742
- if (!template3) {
217826
+ if (messages.length === 0)
217743
217827
  return null;
217744
- }
217745
- const mergedContent = mergeDequeuedBatchContent(batch.items);
217746
- if (mergedContent === null) {
217747
- return null;
217748
- }
217749
- const firstMessageIndex = template3.messages.findIndex((payload) => ("content" in payload));
217750
- if (firstMessageIndex === -1) {
217751
- return null;
217752
- }
217753
- const firstMessage = template3.messages[firstMessageIndex];
217754
- const mergedFirstMessage = {
217755
- ...firstMessage,
217756
- content: mergedContent
217757
- };
217758
- const messages = template3.messages.slice();
217759
- messages[firstMessageIndex] = mergedFirstMessage;
217828
+ const scopeItem = batch.items[0];
217760
217829
  return {
217830
+ type: "message",
217831
+ agentId: scopeItem?.agentId ?? runtime.agentId ?? undefined,
217832
+ conversationId: scopeItem?.conversationId ?? runtime.conversationId,
217761
217833
  ...template3,
217762
- ...actingUserId ? { actingUserId } : {},
217763
217834
  messages
217764
217835
  };
217765
217836
  }
@@ -217790,7 +217861,7 @@ function shouldProcessInboundMessageDirectly(runtime, parsed) {
217790
217861
  });
217791
217862
  return getListenerBlockedReason(runtime.turnLifecycle.snapshot(), activeScope ? getPendingControlRequestCount(runtime.listener, activeScope) : 0) === null;
217792
217863
  }
217793
- function consumeQueuedTurn(runtime, continuation) {
217864
+ function consumeQueuedTurn(runtime) {
217794
217865
  const queuedItems = runtime.queueRuntime.peekReady();
217795
217866
  const firstQueuedItem = queuedItems[0];
217796
217867
  if (!firstQueuedItem || !isCoalescable(firstQueuedItem.kind)) {
@@ -217802,22 +217873,15 @@ function consumeQueuedTurn(runtime, continuation) {
217802
217873
  let hasCronPrompt = false;
217803
217874
  let hasModContinue = false;
217804
217875
  let batchConnectionId;
217805
- let batchActingUserId = firstQueuedItem.actingUserId;
217806
217876
  let batchImageFailureMode = null;
217807
217877
  const isNoCoalesce = (candidate) => candidate.kind === "message" && candidate.noCoalesce === true;
217808
217878
  for (const item of queuedItems) {
217809
- if (continuation && item.actingUserId !== continuation.actingUserId) {
217810
- break;
217811
- }
217812
217879
  if (!isCoalescable(item.kind) || !hasSameQueueScope(firstQueuedItem, item)) {
217813
217880
  break;
217814
217881
  }
217815
217882
  if (queueLen > 0 && (isNoCoalesce(item) || isNoCoalesce(firstQueuedItem))) {
217816
217883
  break;
217817
217884
  }
217818
- if (batchActingUserId && item.actingUserId && batchActingUserId !== item.actingUserId) {
217819
- break;
217820
- }
217821
217885
  if (item.kind === "message") {
217822
217886
  const itemConnectionId = runtime.queuedMessagesByItemId.get(item.id)?.connectionId;
217823
217887
  if (batchConnectionId !== undefined && itemConnectionId !== undefined && itemConnectionId !== batchConnectionId) {
@@ -217830,7 +217894,6 @@ function consumeQueuedTurn(runtime, continuation) {
217830
217894
  }
217831
217895
  batchImageFailureMode = itemImageFailureMode;
217832
217896
  }
217833
- batchActingUserId ??= item.actingUserId;
217834
217897
  queueLen += 1;
217835
217898
  if (item.kind === "message") {
217836
217899
  hasMessage = true;
@@ -223057,8 +223120,8 @@ function isSyncCommand(value) {
223057
223120
  if (!value || typeof value !== "object") {
223058
223121
  return false;
223059
223122
  }
223060
- const candidate = value;
223061
- return candidate.type === "sync" && isRuntimeScope(candidate.runtime) && (candidate.request_id === undefined || typeof candidate.request_id === "string") && (candidate.recover_approvals === undefined || typeof candidate.recover_approvals === "boolean") && (candidate.force_device_status === undefined || typeof candidate.force_device_status === "boolean");
223123
+ const c = value;
223124
+ return c.type === "sync" && isRuntimeScope(c.runtime) && (c.request_id === undefined || typeof c.request_id === "string") && (c.recover_approvals === undefined || typeof c.recover_approvals === "boolean") && (c.resume_interrupted_turn === undefined || typeof c.resume_interrupted_turn === "boolean") && (c.force_device_status === undefined || typeof c.force_device_status === "boolean");
223062
223125
  }
223063
223126
  function isTerminalSpawnCommand(value) {
223064
223127
  if (!value || typeof value !== "object")
@@ -402118,6 +402181,59 @@ var init_continuation_input = __esm(async () => {
402118
402181
  ]);
402119
402182
  });
402120
402183
 
402184
+ // src/backend/api/agents.ts
402185
+ async function getAgentContextOverview(agentId, options) {
402186
+ return apiRequest("GET", `/v1/agents/${agentId}/context`, undefined, {
402187
+ signal: options?.signal
402188
+ });
402189
+ }
402190
+ async function createMinimalAgent(apiKey, name) {
402191
+ return apiRequest("POST", "/v1/agents", { name }, {
402192
+ baseUrl: "https://api.letta.com",
402193
+ apiKey
402194
+ });
402195
+ }
402196
+ async function getAgentRuntimeStatus(agentId, conversationIds, signal) {
402197
+ return apiRequest("GET", `/v1/agents/${encodeURIComponent(agentId)}/runtime-status`, undefined, { query: { conversation_ids: conversationIds.join(",") }, signal });
402198
+ }
402199
+ var init_agents7 = __esm(() => {
402200
+ init_request();
402201
+ });
402202
+
402203
+ // src/websocket/listener/recovery-ownership.ts
402204
+ function hasRecoveryHandoff(runtime) {
402205
+ return isInboundTeleportExpected(runtime) || isRuntimeTeleportPending(runtime.listener, runtime.agentId, runtime.conversationId);
402206
+ }
402207
+ async function canRecoverConversation(runtime, readStatus = getAgentRuntimeStatus) {
402208
+ if (hasRecoveryHandoff(runtime))
402209
+ return false;
402210
+ const connectionId = runtime.listener.connectionId;
402211
+ if (!connectionId?.startsWith("conn-")) {
402212
+ return true;
402213
+ }
402214
+ if (!runtime.agentId)
402215
+ return true;
402216
+ try {
402217
+ const snapshot = await readStatus(runtime.agentId, [runtime.conversationId], AbortSignal.timeout(5000));
402218
+ if (hasRecoveryHandoff(runtime) || runtime.listener.connectionId !== connectionId)
402219
+ return false;
402220
+ const status = snapshot.statuses.find((entry) => entry.conversation_id === runtime.conversationId);
402221
+ if (!status || status.has_conflicting_listeners)
402222
+ return false;
402223
+ if (status.active_harness)
402224
+ return status.active_harness.connection_id === connectionId;
402225
+ return status.state === "IDLE";
402226
+ } catch (error4) {
402227
+ debugWarn("recovery", "Could not verify conversation recovery ownership", error4);
402228
+ return false;
402229
+ }
402230
+ }
402231
+ var init_recovery_ownership = __esm(() => {
402232
+ init_agents7();
402233
+ init_debug();
402234
+ init_teleport();
402235
+ });
402236
+
402121
402237
  // src/websocket/listener/secrets-sync.ts
402122
402238
  function getFreshnessMs() {
402123
402239
  return _testFreshnessMsOverride ?? DEFAULT_FRESHNESS_MS;
@@ -402429,6 +402545,14 @@ async function startRecoveredApprovalContinuation(runtime, socket, processTurn,
402429
402545
  if (runtime.turnLifecycle.kind !== "idle") {
402430
402546
  return false;
402431
402547
  }
402548
+ if (!await canRecoverConversation(runtime)) {
402549
+ if (runtime.recoveredApprovalState === recovered) {
402550
+ clearRecoveredApprovalState(runtime);
402551
+ }
402552
+ return false;
402553
+ }
402554
+ if (runtime.turnLifecycle.kind !== "idle" || runtime.recoveredApprovalState !== recovered)
402555
+ return false;
402432
402556
  const scope = {
402433
402557
  agent_id: recovered.agentId,
402434
402558
  conversation_id: recovered.conversationId
@@ -402712,13 +402836,11 @@ async function executeRecoveredApprovalContinuation(params) {
402712
402836
  }
402713
402837
  ]);
402714
402838
  let continuationBatchId = `batch-recovered-${crypto.randomUUID()}`;
402715
- let continuationActingUserId;
402716
402839
  let continuationCorrelation;
402717
402840
  const consumedQueuedTurn = consumeQueuedTurn(runtime);
402718
402841
  if (consumedQueuedTurn) {
402719
402842
  const { dequeuedBatch, queuedTurn } = consumedQueuedTurn;
402720
402843
  continuationBatchId = dequeuedBatch.batchId;
402721
- continuationActingUserId = queuedTurn.actingUserId;
402722
402844
  continuationInput = appendQueuedTurnToInput(continuationInput, queuedTurn);
402723
402845
  continuationCorrelation = createTurnCorrelation(runtime, {
402724
402846
  type: "message",
@@ -402736,7 +402858,6 @@ async function executeRecoveredApprovalContinuation(params) {
402736
402858
  type: "message",
402737
402859
  agentId: recovered.agentId,
402738
402860
  conversationId: recovered.conversationId,
402739
- ...continuationActingUserId ? { actingUserId: continuationActingUserId } : {},
402740
402861
  messages: continuationInput.messages
402741
402862
  }, socket, runtime, opts?.onStatusChange, opts?.connectionId, continuationBatchId, recoveryLease, continuationCorrelation);
402742
402863
  if (runtime.turnLifecycle.isCurrent(recoveryLease)) {
@@ -402789,6 +402910,7 @@ var init_recovery = __esm(async () => {
402789
402910
  init_permission_mode();
402790
402911
  init_protocol_outbound();
402791
402912
  init_recoverable_notices();
402913
+ init_recovery_ownership();
402792
402914
  init_runtime();
402793
402915
  init_secrets_sync();
402794
402916
  init_turn_correlation();
@@ -403219,13 +403341,11 @@ async function resolveStaleApprovals(runtime, socket, turnLease, deps = {}) {
403219
403341
  otid: crypto.randomUUID()
403220
403342
  }
403221
403343
  ]);
403222
- let continuationActingUserId;
403223
403344
  let recoveryTurnCorrelation;
403224
403345
  const consumedQueuedTurn = consumeQueuedTurn(runtime);
403225
403346
  if (consumedQueuedTurn) {
403226
403347
  const { dequeuedBatch, queuedTurn } = consumedQueuedTurn;
403227
403348
  recoveryTurnCorrelation = createTurnCorrelation(runtime, queuedTurn, dequeuedBatch.batchId);
403228
- continuationActingUserId = queuedTurn.actingUserId;
403229
403349
  continuationInput = appendQueuedTurnToInput(continuationInput, queuedTurn);
403230
403350
  emitDequeuedUserMessage(socket, runtime, queuedTurn, dequeuedBatch);
403231
403351
  }
@@ -403239,7 +403359,6 @@ async function resolveStaleApprovals(runtime, socket, turnLease, deps = {}) {
403239
403359
  agentId: runtime.agentId ?? undefined,
403240
403360
  streamTokens: true,
403241
403361
  background: true,
403242
- ...continuationActingUserId ? { actingUserId: continuationActingUserId } : {},
403243
403362
  workingDirectory: recoveryWorkingDirectory,
403244
403363
  preparedToolContext: preparedToolContext.preparedToolContext,
403245
403364
  ...continuationInput.imageFailureModesByMessageOtid ? {
@@ -406021,9 +406140,7 @@ async function handleApprovalStop(params) {
406021
406140
  ]);
406022
406141
  let continuationBatchId = dequeuedBatchId;
406023
406142
  const sendOptions = buildSendOptions() ?? {};
406024
- const consumedQueuedTurn = consumeQueuedTurn(runtime, {
406025
- actingUserId: sendOptions.actingUserId
406026
- });
406143
+ const consumedQueuedTurn = consumeQueuedTurn(runtime);
406027
406144
  if (consumedQueuedTurn) {
406028
406145
  const { dequeuedBatch, queuedTurn } = consumedQueuedTurn;
406029
406146
  turnCorrelation?.appendDequeuedBatch(dequeuedBatch.batchId);
@@ -407223,12 +407340,12 @@ function rowToolCalls(row, index, diagnostics2) {
407223
407340
  }
407224
407341
  return calls;
407225
407342
  }
407226
- function contentText2(content) {
407343
+ function contentText(content) {
407227
407344
  if (typeof content === "string") {
407228
407345
  if (content.startsWith(CONTENT_JSON_PREFIX)) {
407229
407346
  const encoded = content.slice(CONTENT_JSON_PREFIX.length);
407230
407347
  try {
407231
- return contentText2(JSON.parse(encoded));
407348
+ return contentText(JSON.parse(encoded));
407232
407349
  } catch {
407233
407350
  return encoded;
407234
407351
  }
@@ -407314,7 +407431,7 @@ var init_hermes = __esm(() => {
407314
407431
  });
407315
407432
  };
407316
407433
  if (row.role === "user") {
407317
- const content = contentText2(row.content);
407434
+ const content = contentText(row.content);
407318
407435
  if (content) {
407319
407436
  emit({
407320
407437
  type: "message",
@@ -407334,7 +407451,7 @@ var init_hermes = __esm(() => {
407334
407451
  ...timestamp ? { timestamp } : {}
407335
407452
  });
407336
407453
  }
407337
- const content = contentText2(row.content);
407454
+ const content = contentText(row.content);
407338
407455
  if (content) {
407339
407456
  emit({
407340
407457
  type: "message",
@@ -407357,7 +407474,7 @@ var init_hermes = __esm(() => {
407357
407474
  if (row.role === "tool") {
407358
407475
  emit({
407359
407476
  type: "tool_result",
407360
- content: contentText2(row.content),
407477
+ content: contentText(row.content),
407361
407478
  ...typeof row.tool_call_id === "string" && row.tool_call_id ? { callId: row.tool_call_id } : {},
407362
407479
  ...timestamp ? { timestamp } : {}
407363
407480
  });
@@ -413364,16 +413481,18 @@ function extractTelemetryInputText(content) {
413364
413481
  }
413365
413482
 
413366
413483
  // src/websocket/listener/turn-transcript.ts
413367
- function trackListenerUserInput(messages, modelId) {
413368
- for (const message of messages) {
413369
- if (!("role" in message) || message.role !== "user") {
413370
- continue;
413371
- }
413372
- const inputText = extractTelemetryInputText(message.content);
413373
- if (inputText.length > 0) {
413374
- telemetry.trackUserInput(inputText, "user", modelId);
413484
+ function trackListenerUserInput(messages, modelId, actingUserId) {
413485
+ runWithRuntimeContext({ actingUserId }, () => {
413486
+ for (const message of messages) {
413487
+ if (!("role" in message) || message.role !== "user") {
413488
+ continue;
413489
+ }
413490
+ const inputText = extractTelemetryInputText(message.content);
413491
+ if (inputText.length > 0) {
413492
+ telemetry.trackUserInput(inputText, "user", modelId);
413493
+ }
413375
413494
  }
413376
- }
413495
+ });
413377
413496
  }
413378
413497
  function buildInboundUserTranscriptLines(messages) {
413379
413498
  const lines = [];
@@ -413408,6 +413527,7 @@ function seedInboundUserTranscriptLines(buffers, lines) {
413408
413527
  }
413409
413528
  }
413410
413529
  var init_turn_transcript = __esm(() => {
413530
+ init_runtime_context();
413411
413531
  init_telemetry();
413412
413532
  });
413413
413533
 
@@ -413520,7 +413640,7 @@ async function prepareListenerTurn(params) {
413520
413640
  if (connectionId) {
413521
413641
  onStatusChange?.("processing", connectionId);
413522
413642
  }
413523
- trackListenerUserInput(msg.messages, "unknown");
413643
+ trackListenerUserInput(msg.messages, "unknown", msg.actingUserId);
413524
413644
  const messagesToSend = [];
413525
413645
  let queuedInterruptedToolCallIds = [];
413526
413646
  const wasInterrupted = hasInterruptedCacheForScope(runtime.listener, {
@@ -418314,6 +418434,7 @@ function summarizeV2Command(parsed) {
418314
418434
  "replace_all",
418315
418435
  "expected_replacements",
418316
418436
  "recover_approvals",
418437
+ "resume_interrupted_turn",
418317
418438
  "force_device_status"
418318
418439
  ]) {
418319
418440
  pushField(fields, key2, parsed[key2]);
@@ -418780,6 +418901,7 @@ function createListenerMessageHandler(params) {
418780
418901
  try {
418781
418902
  await replaySyncStateForRuntime(runtime, socket, parsed.runtime, {
418782
418903
  recoverApprovals: parsed.recover_approvals !== false,
418904
+ resumeInterruptedTurn: parsed.resume_interrupted_turn === true,
418783
418905
  forceDeviceStatus: parsed.force_device_status === true,
418784
418906
  onStatusChange: opts.onStatusChange,
418785
418907
  connectionId: opts.connectionId
@@ -419359,7 +419481,7 @@ var init_process_services = __esm(async () => {
419359
419481
  function isBackendNotFoundError2(error4) {
419360
419482
  return error4 instanceof APIError && (error4.status === 404 || error4.status === 422) || error4 instanceof Error && error4.name === "LocalBackendNotFoundError";
419361
419483
  }
419362
- async function recoverApprovalStateForSync(runtime, scope, deps = {}) {
419484
+ async function recoverApprovalStateForSync(runtime, scope, deps = {}, opts = {}) {
419363
419485
  const resolvedDeps = {
419364
419486
  getBackend,
419365
419487
  getResumeDataFromBackend,
@@ -419382,6 +419504,10 @@ async function recoverApprovalStateForSync(runtime, scope, deps = {}) {
419382
419504
  clearRecoveredApprovalState(runtime);
419383
419505
  return;
419384
419506
  }
419507
+ if (!await canRecoverConversation(runtime)) {
419508
+ clearRecoveredApprovalState(runtime);
419509
+ return "deferred";
419510
+ }
419385
419511
  const existingRecovered = runtime.recoveredApprovalState;
419386
419512
  if (existingRecovered && existingRecovered.agentId === scope.agent_id && existingRecovered.conversationId === scope.conversation_id && existingRecovered.pendingRequestIds.size > 0) {
419387
419513
  return;
@@ -419414,6 +419540,8 @@ async function recoverApprovalStateForSync(runtime, scope, deps = {}) {
419414
419540
  clearRecoveredApprovalState(runtime);
419415
419541
  return;
419416
419542
  }
419543
+ if (!await canRecoverConversation(runtime))
419544
+ return "deferred";
419417
419545
  if (hasInterruptedCacheForScope(runtime.listener, scope) || sameActiveScope && (runtime.turnLifecycle.kind !== "idle" || runtime.pendingApprovalResolvers.size > 0)) {
419418
419546
  return;
419419
419547
  }
@@ -419423,6 +419551,17 @@ async function recoverApprovalStateForSync(runtime, scope, deps = {}) {
419423
419551
  approval,
419424
419552
  reason: STALE_APPROVAL_RECOVERY_DENIAL_REASON
419425
419553
  }));
419554
+ if (interactivePending.length === 0 && !opts.resumeInterruptedTurn) {
419555
+ runtime.pendingInterruptedResults = buildFreshDenialApprovals(pendingApprovals, STALE_APPROVAL_RECOVERY_DENIAL_REASON);
419556
+ runtime.pendingInterruptedContext = {
419557
+ agentId: scope.agent_id,
419558
+ conversationId: scope.conversation_id,
419559
+ continuationEpoch: runtime.continuationEpoch
419560
+ };
419561
+ runtime.pendingInterruptedToolCallIds = null;
419562
+ clearRecoveredApprovalState(runtime);
419563
+ return;
419564
+ }
419426
419565
  const approvalsByRequestId = new Map;
419427
419566
  for (const approval of interactivePending) {
419428
419567
  const requestId = `perm-${approval.toolCallId}`;
@@ -419458,6 +419597,7 @@ async function recoverApprovalStateForSync(runtime, scope, deps = {}) {
419458
419597
  autoDecisions: staleDenialDecisions,
419459
419598
  allApprovals: pendingApprovals
419460
419599
  };
419600
+ return;
419461
419601
  }
419462
419602
  var init_recovery_sync = __esm(() => {
419463
419603
  init_error();
@@ -419465,6 +419605,7 @@ var init_recovery_sync = __esm(() => {
419465
419605
  init_turn_recovery_policy();
419466
419606
  init_backend2();
419467
419607
  init_interactive_policy();
419608
+ init_recovery_ownership();
419468
419609
  init_runtime();
419469
419610
  });
419470
419611
 
@@ -419472,10 +419613,11 @@ var init_recovery_sync = __esm(() => {
419472
419613
  async function replaySyncStateForRuntime(listenerRuntime, socket, scope, opts) {
419473
419614
  const syncScopedRuntime = getOrCreateScopedRuntime(listenerRuntime, scope.agent_id, scope.conversation_id);
419474
419615
  const recoverFn = opts?.recoverApprovalStateForSync ?? recoverApprovalStateForSync;
419475
- if ((opts?.recoverApprovals ?? true) || !syncScopedRuntime.syncApprovalRecoveryCompleted) {
419616
+ if (opts?.recoverApprovals ?? true) {
419476
419617
  try {
419477
- await recoverFn(syncScopedRuntime, scope);
419478
- syncScopedRuntime.syncApprovalRecoveryCompleted = true;
419618
+ await recoverFn(syncScopedRuntime, scope, undefined, {
419619
+ resumeInterruptedTurn: opts?.resumeInterruptedTurn === true
419620
+ });
419479
419621
  } catch (error4) {
419480
419622
  trackBoundaryError({
419481
419623
  errorType: "listener_sync_recovery_failed",
@@ -419487,7 +419629,7 @@ async function replaySyncStateForRuntime(listenerRuntime, socket, scope, opts) {
419487
419629
  }
419488
419630
  }
419489
419631
  }
419490
- if (!isInboundTeleportExpected(syncScopedRuntime) && syncScopedRuntime.recoveredApprovalState && syncScopedRuntime.recoveredApprovalState.pendingRequestIds.size === 0 && (syncScopedRuntime.recoveredApprovalState.autoDecisions?.length ?? 0) > 0) {
419632
+ if (syncScopedRuntime.recoveredApprovalState && syncScopedRuntime.recoveredApprovalState.pendingRequestIds.size === 0 && (syncScopedRuntime.recoveredApprovalState.autoDecisions?.length ?? 0) > 0) {
419491
419633
  startRecoveredApprovalContinuation(syncScopedRuntime, getOrCreateProcessTransport(listenerRuntime), opts?.processIncomingMessage ?? handleIncomingMessage, {
419492
419634
  onStatusChange: opts?.onStatusChange,
419493
419635
  connectionId: opts?.connectionId,
@@ -419512,7 +419654,6 @@ var init_sync_replay = __esm(async () => {
419512
419654
  init_connection();
419513
419655
  init_connection_state_sync();
419514
419656
  init_recovery_sync();
419515
- init_teleport();
419516
419657
  await __promiseAll([
419517
419658
  init_conversation_runtime(),
419518
419659
  init_recovery(),
@@ -440848,7 +440989,7 @@ var init_mcp_client = __esm(() => {
440848
440989
  init_streamableHttp();
440849
440990
  DEFAULT_CLIENT_INFO = {
440850
440991
  name: "letta-code",
440851
- version: "0.32.7"
440992
+ version: "0.32.9"
440852
440993
  };
440853
440994
  });
440854
440995
 
@@ -442685,25 +442826,6 @@ var init_message_search = __esm(() => {
442685
442826
  init_transcript_search();
442686
442827
  });
442687
442828
 
442688
- // src/backend/api/agents.ts
442689
- async function getAgentContextOverview(agentId, options) {
442690
- return apiRequest("GET", `/v1/agents/${agentId}/context`, undefined, {
442691
- signal: options?.signal
442692
- });
442693
- }
442694
- async function createMinimalAgent(apiKey, name) {
442695
- return apiRequest("POST", "/v1/agents", { name }, {
442696
- baseUrl: "https://api.letta.com",
442697
- apiKey
442698
- });
442699
- }
442700
- async function getAgentRuntimeStatus(agentId, conversationIds) {
442701
- return apiRequest("GET", `/v1/agents/${encodeURIComponent(agentId)}/runtime-status`, undefined, { query: { conversation_ids: conversationIds.join(",") } });
442702
- }
442703
- var init_agents7 = __esm(() => {
442704
- init_request();
442705
- });
442706
-
442707
442829
  // src/cli/subcommands/message-status.ts
442708
442830
  async function readMessageStatus(conversationId, agentId, backend3, deps = { getAgentRuntimeStatus, getLatestConversationSuperRun }) {
442709
442831
  if (!backend3.capabilities.environmentRouting)
@@ -455271,7 +455393,15 @@ async function tryCloudHeadlessSend(values4, prompt, backend3, isAgentLaunch, de
455271
455393
  }
455272
455394
  if (!agentId && !conversationId)
455273
455395
  throw new Error("Choose a destination with --agent or --conversation. Ambient AGENT_ID identifies the sender.");
455274
- ({ agentId, conversationId } = await resolveAgentMessageDestination({ agentId, conversationId, senderAgentId: sender.agentId }, backend3, controller.signal));
455396
+ ({ agentId, conversationId } = await resolveAgentMessageDestination({
455397
+ agentId,
455398
+ conversationId,
455399
+ senderAgentId: sender.agentId,
455400
+ currentConversation: {
455401
+ agentId: ambientSender,
455402
+ conversationId: env5.CONVERSATION_ID || env5.LETTA_CONVERSATION_ID
455403
+ }
455404
+ }, backend3, controller.signal));
455275
455405
  const recovery = {
455276
455406
  status_command: `letta messages status --agent ${agentId} --conversation ${conversationId}`,
455277
455407
  messages_command: `letta messages list --agent ${agentId} --conversation ${conversationId}`
@@ -456038,6 +456168,49 @@ var init_headless_tool_events = __esm(async () => {
456038
456168
  await init_approval_execution();
456039
456169
  });
456040
456170
 
456171
+ // src/queue/turn-queue-runtime.ts
456172
+ function stringifyUnexpectedContent(content) {
456173
+ try {
456174
+ return JSON.stringify(content) ?? String(content);
456175
+ } catch {
456176
+ return String(content);
456177
+ }
456178
+ }
456179
+ function appendContentParts(target2, content) {
456180
+ if (typeof content === "string") {
456181
+ target2.push({ type: "text", text: content });
456182
+ return;
456183
+ }
456184
+ if (!Array.isArray(content)) {
456185
+ if (content === null || content === undefined)
456186
+ return;
456187
+ target2.push({ type: "text", text: stringifyUnexpectedContent(content) });
456188
+ return;
456189
+ }
456190
+ target2.push(...content);
456191
+ }
456192
+ function mergeQueuedTurnInput(queued, options) {
456193
+ if (queued.length === 0) {
456194
+ return null;
456195
+ }
456196
+ const separatorText = options.separatorText ?? `
456197
+ `;
456198
+ const mergedParts = [];
456199
+ let isFirst = true;
456200
+ for (const item of queued) {
456201
+ if (!isFirst) {
456202
+ mergedParts.push({ type: "text", text: separatorText });
456203
+ }
456204
+ isFirst = false;
456205
+ if (item.kind === "task_notification" || item.kind === "cron_prompt") {
456206
+ mergedParts.push({ type: "text", text: item.text });
456207
+ continue;
456208
+ }
456209
+ appendContentParts(mergedParts, options.normalizeUserContent(item.content));
456210
+ }
456211
+ return mergedParts.length > 0 ? mergedParts : null;
456212
+ }
456213
+
456041
456214
  // src/agent/defaults.ts
456042
456215
  var exports_defaults = {};
456043
456216
  __export(exports_defaults, {
@@ -463056,6 +463229,46 @@ var init_use_local_mod_adapter = __esm(async () => {
463056
463229
  import_react44 = __toESM(require_react(), 1);
463057
463230
  });
463058
463231
 
463232
+ // src/cli/helpers/ask-user-questions.ts
463233
+ function parseAskUserQuestions(approval) {
463234
+ const parsed = safeJsonParseOr(approval.toolArgs, {});
463235
+ if (parsed == null || typeof parsed !== "object" || Array.isArray(parsed)) {
463236
+ return [];
463237
+ }
463238
+ const questions = parsed.questions;
463239
+ if (!Array.isArray(questions) || questions.length < 1 || questions.length > 4) {
463240
+ return [];
463241
+ }
463242
+ return questions.every(isWellFormedQuestion) ? questions : [];
463243
+ }
463244
+ function isWellFormedQuestion(q2) {
463245
+ if (q2 == null || typeof q2 !== "object")
463246
+ return false;
463247
+ const question = q2;
463248
+ if (typeof question.question !== "string")
463249
+ return false;
463250
+ if (typeof question.header !== "string")
463251
+ return false;
463252
+ if (question.multiSelect !== undefined && typeof question.multiSelect !== "boolean") {
463253
+ return false;
463254
+ }
463255
+ if (question.allowOther !== undefined && typeof question.allowOther !== "boolean") {
463256
+ return false;
463257
+ }
463258
+ const options = question.options;
463259
+ if (!Array.isArray(options) || options.length < 2 || options.length > 4) {
463260
+ return false;
463261
+ }
463262
+ return options.every(isWellFormedOption);
463263
+ }
463264
+ function isWellFormedOption(o3) {
463265
+ if (o3 == null || typeof o3 !== "object")
463266
+ return false;
463267
+ const option2 = o3;
463268
+ return typeof option2.label === "string" && option2.label.length > 0 && typeof option2.description === "string";
463269
+ }
463270
+ var init_ask_user_questions = () => {};
463271
+
463059
463272
  // src/cli/hooks/use-progress-indicator.ts
463060
463273
  function useProgressIndicator(active = true) {
463061
463274
  import_react45.useEffect(() => {
@@ -465304,7 +465517,7 @@ var init_InlineQuestionApproval = __esm(async () => {
465304
465517
  const currentQuestion = questions[currentQuestionIndex];
465305
465518
  const showOther = currentQuestion?.allowOther !== false;
465306
465519
  const baseOptions = currentQuestion ? [
465307
- ...currentQuestion.options,
465520
+ ...Array.isArray(currentQuestion.options) ? currentQuestion.options.filter((o3) => o3 != null && typeof o3 === "object" && typeof o3.label === "string" && typeof o3.description === "string") : [],
465308
465521
  ...showOther ? [{ label: "Type something.", description: "" }] : []
465309
465522
  ] : [];
465310
465523
  const optionsWithOther = currentQuestion?.multiSelect ? [...baseOptions, { label: "Submit", description: "" }] : baseOptions;
@@ -466099,15 +466312,11 @@ function getMemoryFileEditInfo(approval) {
466099
466312
  }
466100
466313
  }
466101
466314
  function getQuestions(approval) {
466102
- try {
466103
- const args = JSON.parse(approval.toolArgs || "{}");
466104
- return args.questions || [];
466105
- } catch {
466106
- return [];
466107
- }
466315
+ return parseAskUserQuestions(approval);
466108
466316
  }
466109
466317
  var import_react54, jsx_dev_runtime30, ApprovalSwitch;
466110
466318
  var init_ApprovalSwitch = __esm(async () => {
466319
+ init_ask_user_questions();
466111
466320
  await __promiseAll([
466112
466321
  init_tool_name_mapping(),
466113
466322
  init_InlineBashApproval(),
@@ -503289,10 +503498,11 @@ function buildApprovalBatchKey(approvals) {
503289
503498
 
503290
503499
  // src/cli/app/approval-questions.ts
503291
503500
  function getQuestionsFromApproval(approval) {
503292
- const parsed = safeJsonParseOr(approval.toolArgs, {});
503293
- return parsed.questions || [];
503501
+ return parseAskUserQuestions(approval);
503294
503502
  }
503295
- var init_approval_questions = () => {};
503503
+ var init_approval_questions = __esm(() => {
503504
+ init_ask_user_questions();
503505
+ });
503296
503506
 
503297
503507
  // src/cli/app/errors.ts
503298
503508
  function extractErrorMeta(e2) {
@@ -519622,7 +519832,9 @@ Error: ${message}`);
519622
519832
  return null;
519623
519833
  });
519624
519834
  if (!shouldBlockOnMemfsStartup) {}
519625
- const secretsInitPromise = Promise.resolve().then(() => (init_secrets_store(), exports_secrets_store)).then(({ initSecretsFromServer: initSecretsFromServer2 }) => initSecretsFromServer2(agentId2));
519835
+ const secretsInitPromise = Promise.resolve().then(() => (init_secrets_store(), exports_secrets_store)).then(({ initSecretsFromServer: initSecretsFromServer2 }) => initSecretsFromServer2(agentId2)).catch((error5) => {
519836
+ debugLog("secrets", `Failed to init secrets: ${error5 instanceof Error ? error5.message : String(error5)}`);
519837
+ });
519626
519838
  const isResumingProject = !shouldCreateNew && !!resumingAgentId;
519627
519839
  const isReusingExistingAgent = !shouldCreateNew && agent && agent.id;
519628
519840
  const resuming = !!(agentIdArg || isResumingProject || isReusingExistingAgent);
@@ -519733,11 +519945,7 @@ Error: ${message}`);
519733
519945
  }
519734
519946
  const fdPath = await ensureFdPath();
519735
519947
  setFileAutocompleteFdPath(fdPath);
519736
- try {
519737
- await secretsInitPromise;
519738
- } catch (error5) {
519739
- Promise.resolve().then(() => (init_debug(), exports_debug)).then(({ debugLog: debugLog3 }) => debugLog3("secrets", `Failed to init secrets: ${error5 instanceof Error ? error5.message : String(error5)}`));
519740
- }
519948
+ await secretsInitPromise;
519741
519949
  if (shouldPersistSessionState()) {
519742
519950
  settingsManager.persistSession(agent.id, conversationIdToUse);
519743
519951
  }
@@ -522152,4 +522360,4 @@ function registerBunOAuthFlows() {
522152
522360
  registerBunOAuthFlows();
522153
522361
  await init_src5().then(() => exports_src2);
522154
522362
 
522155
- //# debugId=84C68A57BFAD43D964756E2164756E21
522363
+ //# debugId=73D666396EC039C064756E2164756E21