@letta-ai/letta-code 0.30.5 → 0.30.7

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 (52) hide show
  1. package/dist/agent-presets.js +3 -13
  2. package/dist/agent-presets.js.map +2 -2
  3. package/dist/channels-slack.js +1 -1
  4. package/dist/channels-slack.js.map +3 -3
  5. package/dist/mcp-client.js +2 -2
  6. package/dist/mcp-client.js.map +1 -1
  7. package/dist/types/agent/subagents/manager.d.ts.map +1 -1
  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/client.d.ts +1 -0
  11. package/dist/types/backend/api/client.d.ts.map +1 -1
  12. package/dist/types/backend/dev/pi-local-endpoint-provider.d.ts.map +1 -1
  13. package/dist/types/backend/dev/pi-model-factory.d.ts.map +1 -1
  14. package/dist/types/backend/dev/pi-models-runtime.d.ts.map +1 -1
  15. package/dist/types/backend/dev/pi-openai-compatible-provider.d.ts +14 -0
  16. package/dist/types/backend/dev/pi-openai-compatible-provider.d.ts.map +1 -0
  17. package/dist/types/backend/dev/pi-provider-registry.d.ts +3 -1
  18. package/dist/types/backend/dev/pi-provider-registry.d.ts.map +1 -1
  19. package/dist/types/backend/local/local-provider-auth-store.d.ts +1 -1
  20. package/dist/types/backend/local/local-provider-auth-store.d.ts.map +1 -1
  21. package/dist/types/channels/accounts.d.ts.map +1 -1
  22. package/dist/types/channels/config.d.ts.map +1 -1
  23. package/dist/types/channels/plugin-types.d.ts +1 -0
  24. package/dist/types/channels/plugin-types.d.ts.map +1 -1
  25. package/dist/types/channels/slack/ingress-controller.d.ts.map +1 -1
  26. package/dist/types/channels/slack/ingress-policy.d.ts +1 -0
  27. package/dist/types/channels/slack/ingress-policy.d.ts.map +1 -1
  28. package/dist/types/channels/types.d.ts +2 -0
  29. package/dist/types/channels/types.d.ts.map +1 -1
  30. package/dist/types/cli/helpers/error-formatter.d.ts +1 -0
  31. package/dist/types/cli/helpers/error-formatter.d.ts.map +1 -1
  32. package/dist/types/cli/helpers/stream-processor.d.ts +6 -0
  33. package/dist/types/cli/helpers/stream-processor.d.ts.map +1 -1
  34. package/dist/types/cli/helpers/stream.d.ts +2 -7
  35. package/dist/types/cli/helpers/stream.d.ts.map +1 -1
  36. package/dist/types/providers/byok-providers.d.ts.map +1 -1
  37. package/dist/types/runtime-context.d.ts +2 -0
  38. package/dist/types/runtime-context.d.ts.map +1 -1
  39. package/dist/types/tools/impl/shell-env.d.ts.map +1 -1
  40. package/dist/types/tools/toolset.d.ts +1 -0
  41. package/dist/types/tools/toolset.d.ts.map +1 -1
  42. package/dist/types/websocket/listener/mod-adapter.d.ts +1 -0
  43. package/dist/types/websocket/listener/mod-adapter.d.ts.map +1 -1
  44. package/dist/types/websocket/listener/runtime.d.ts.map +1 -1
  45. package/dist/types/websocket/listener/types.d.ts +7 -0
  46. package/dist/types/websocket/listener/types.d.ts.map +1 -1
  47. package/letta.js +454 -203
  48. package/package.json +1 -1
  49. package/scripts/check-test-mock-isolation.js +5 -1
  50. package/scripts/isolated-unit-tests.json +15 -0
  51. package/scripts/source-file-size-baseline.json +2 -2
  52. package/skills/scheduling-tasks/SKILL.md +58 -54
package/letta.js CHANGED
@@ -5464,7 +5464,7 @@ var package_default;
5464
5464
  var init_package = __esm(() => {
5465
5465
  package_default = {
5466
5466
  name: "@letta-ai/letta-code",
5467
- version: "0.30.5",
5467
+ version: "0.30.7",
5468
5468
  description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
5469
5469
  type: "module",
5470
5470
  packageManager: "bun@1.3.0",
@@ -5746,6 +5746,7 @@ var init_memfs_git_proxy = __esm(() => {
5746
5746
  var exports_client = {};
5747
5747
  __export(exports_client, {
5748
5748
  getServerUrl: () => getServerUrl,
5749
+ getRuntimeEnvironmentDeviceId: () => getRuntimeEnvironmentDeviceId,
5749
5750
  getMemfsServerUrl: () => getMemfsServerUrl,
5750
5751
  getMemfsGitProxyRewriteConfig: () => getMemfsGitProxyRewriteConfig,
5751
5752
  getClientDefaultHeaders: () => getClientDefaultHeaders,
@@ -7125,12 +7126,7 @@ You **MUST** be proactive in creating crons when work extends beyond the current
7125
7126
 
7126
7127
  **Cost**: Self-invocation is critical, but expensive. Default to the longest interval that still serves the user. Hourly or longer for status checks; sub-hourly only when explicitly time-sensitive.
7127
7128
 
7128
- Creating crons:
7129
- - One-shot follow-up: \`letta cron add --name <short-name> --description <description> --prompt <future-message> --at "in 30m"\`
7130
- - Recurring monitoring/heartbeat: \`letta cron add --name <short-name> --description <description> --prompt <future-message> --every "2h"\` or \`--cron "0 9 * * *"\`
7131
- Always include \`--name\`, \`--description\`, and \`--prompt\`. \`$AGENT_ID\` is automatically injected into the shell environment, and \`letta cron\` uses it by default, so you do not need to specify which agent to invoke unless overriding the current agent intentionally.
7132
-
7133
- Where crons run: for cloud agents, schedules default to durable Cloud schedules that fire from the cloud and execute in your cloud sandbox — they survive local shutdown, so this is the right default. If the scheduled work must run on a specific computer (e.g. it needs that computer's filesystem or local services), add \`--computer <deviceId>\` (from \`letta environments list\`) to keep the durable Cloud schedule but execute on that computer, with sandbox fallback if it is offline. Use \`--runner local\` only when that fallback is unacceptable; local schedules only fire while a Letta session is running on that computer.
7129
+ The mechanics — flags, where schedules run and execute, timezone handling — live in the scheduling-tasks skill. Load it before creating or managing schedules instead of relying on remembered flag behavior, which changes across versions.
7134
7130
 
7135
7131
  # Harness Architecture
7136
7132
 
@@ -7310,12 +7306,7 @@ You **MUST** be proactive in creating crons when work extends beyond the current
7310
7306
 
7311
7307
  **Cost**: Self-invocation is critical, but expensive. Default to the longest interval that still serves the user. Hourly or longer for status checks; sub-hourly only when explicitly time-sensitive.
7312
7308
 
7313
- Creating crons:
7314
- - One-shot follow-up: \`letta cron add --name <short-name> --description <description> --prompt <future-message> --at "in 30m"\`
7315
- - Recurring monitoring/heartbeat: \`letta cron add --name <short-name> --description <description> --prompt <future-message> --every "2h"\` or \`--cron "0 9 * * *"\`
7316
- Always include \`--name\`, \`--description\`, and \`--prompt\`. \`$AGENT_ID\` is automatically injected into the shell environment, and \`letta cron\` uses it by default, so you do not need to specify which agent to invoke unless overriding the current agent intentionally.
7317
-
7318
- Where crons run: for cloud agents, schedules default to durable Cloud schedules that fire from the cloud and execute in your cloud sandbox — they survive local shutdown, so this is the right default. If the scheduled work must run on a specific computer (e.g. it needs that computer's filesystem or local services), add \`--computer <deviceId>\` (from \`letta environments list\`) to keep the durable Cloud schedule but execute on that computer, with sandbox fallback if it is offline. Use \`--runner local\` only when that fallback is unacceptable; local schedules only fire while a Letta session is running on that computer.
7309
+ The mechanics — flags, where schedules run and execute, timezone handling — live in the scheduling-tasks skill. Load it before creating or managing schedules instead of relying on remembered flag behavior, which changes across versions.
7319
7310
 
7320
7311
  # Harness Architecture
7321
7312
 
@@ -139326,7 +139317,7 @@ function isProviderConfigured(provider, localProviderNames) {
139326
139317
  function listConfiguredPiProviders(localProviderNames) {
139327
139318
  return PI_PROVIDER_SPECS.filter((provider) => isProviderConfigured(provider, localProviderNames)).map((provider) => provider.id);
139328
139319
  }
139329
- var LOCAL_CHATGPT_PROVIDER_NAME = "chatgpt-plus-pro", LOCAL_OPENAI_PROVIDER_NAME = "lc-openai", LOCAL_ANTHROPIC_PROVIDER_NAME = "lc-anthropic", LOCAL_OPENROUTER_PROVIDER_NAME = "lc-openrouter", LOCAL_OLLAMA_PROVIDER_NAME = "lc-ollama", LOCAL_OLLAMA_CLOUD_PROVIDER_NAME = "lc-ollama-cloud", LOCAL_LMSTUDIO_PROVIDER_NAME = "lc-lmstudio", LMSTUDIO_OPENAI_PROVIDER_TYPE = "lmstudio_openai", LEGACY_LMSTUDIO_PROVIDER_TYPE = "lmstudio", LOCAL_LLAMA_CPP_PROVIDER_NAME = "lc-llama-cpp", LOCAL_ZAI_PROVIDER_NAME = "lc-zai", LOCAL_ZAI_CODING_PROVIDER_NAME = "lc-zai-coding", LOCAL_MINIMAX_PROVIDER_NAME = "lc-minimax", LOCAL_MOONSHOT_PROVIDER_NAME = "lc-moonshot", LOCAL_KIMI_CODE_PROVIDER_NAME = "lc-kimi-code", LOCAL_GOOGLE_AI_PROVIDER_NAME = "lc-gemini", LOCAL_BEDROCK_PROVIDER_NAME = "lc-bedrock", prefixedCatalogModelHandle = (prefix) => (model) => `${prefix}${model.id}`, PI_PROVIDER_ALIASES, PI_TUI_DEFAULT_MODEL_IDS, PI_TUI_DEFAULTLESS_PROVIDER_IDS, PI_PROVIDER_OVERRIDES, LOCAL_ENDPOINT_PROVIDER_SPECS, PI_PROVIDER_SPECS, SUPPORTED_LOCAL_PROVIDER_TYPES, KNOWN_PI_PROVIDERS, PROVIDER_TYPE_TO_BASE_PROVIDER;
139320
+ var LOCAL_CHATGPT_PROVIDER_NAME = "chatgpt-plus-pro", LOCAL_OPENAI_PROVIDER_NAME = "lc-openai", LOCAL_OPENAI_COMPATIBLE_PROVIDER_NAME = "lc-openai-compatible", OPENAI_COMPATIBLE_PI_PROVIDER_ID = "openai-compatible", LOCAL_ANTHROPIC_PROVIDER_NAME = "lc-anthropic", LOCAL_OPENROUTER_PROVIDER_NAME = "lc-openrouter", LOCAL_OLLAMA_PROVIDER_NAME = "lc-ollama", LOCAL_OLLAMA_CLOUD_PROVIDER_NAME = "lc-ollama-cloud", LOCAL_LMSTUDIO_PROVIDER_NAME = "lc-lmstudio", LMSTUDIO_OPENAI_PROVIDER_TYPE = "lmstudio_openai", LEGACY_LMSTUDIO_PROVIDER_TYPE = "lmstudio", LOCAL_LLAMA_CPP_PROVIDER_NAME = "lc-llama-cpp", LOCAL_ZAI_PROVIDER_NAME = "lc-zai", LOCAL_ZAI_CODING_PROVIDER_NAME = "lc-zai-coding", LOCAL_MINIMAX_PROVIDER_NAME = "lc-minimax", LOCAL_MOONSHOT_PROVIDER_NAME = "lc-moonshot", LOCAL_KIMI_CODE_PROVIDER_NAME = "lc-kimi-code", LOCAL_GOOGLE_AI_PROVIDER_NAME = "lc-gemini", LOCAL_BEDROCK_PROVIDER_NAME = "lc-bedrock", prefixedCatalogModelHandle = (prefix) => (model) => `${prefix}${model.id}`, PI_PROVIDER_ALIASES, PI_TUI_DEFAULT_MODEL_IDS, PI_TUI_DEFAULTLESS_PROVIDER_IDS, PI_PROVIDER_OVERRIDES, LOCAL_ENDPOINT_PROVIDER_SPECS, PI_PROVIDER_SPECS, SUPPORTED_LOCAL_PROVIDER_TYPES, KNOWN_PI_PROVIDERS, PROVIDER_TYPE_TO_BASE_PROVIDER;
139330
139321
  var init_pi_provider_registry = __esm(() => {
139331
139322
  init_compat2();
139332
139323
  init_all();
@@ -139473,6 +139464,18 @@ var init_pi_provider_registry = __esm(() => {
139473
139464
  envConfigured: () => hasEnvValue(process.env.OLLAMA_API_KEY),
139474
139465
  createCustomModel: true
139475
139466
  },
139467
+ {
139468
+ id: OPENAI_COMPATIBLE_PI_PROVIDER_ID,
139469
+ providerTypes: ["openai-compatible"],
139470
+ handlePrefixes: ["openai-compatible/"],
139471
+ localProviderNames: [
139472
+ OPENAI_COMPATIBLE_PI_PROVIDER_ID,
139473
+ LOCAL_OPENAI_COMPATIBLE_PROVIDER_NAME
139474
+ ],
139475
+ fallbackApiKey: "not-needed",
139476
+ localModelDiscovery: "openai-compatible",
139477
+ createCustomModel: true
139478
+ },
139476
139479
  {
139477
139480
  id: "lmstudio",
139478
139481
  providerTypes: [
@@ -139595,6 +139598,10 @@ async function createOrUpdateLocalProvider(input) {
139595
139598
  }
139596
139599
  const file3 = readAuthFile(input.storageDir);
139597
139600
  const existing = file3.providers[input.providerName];
139601
+ const baseURL = input.baseURL ?? existing?.base_url;
139602
+ if (input.providerType === OPENAI_COMPATIBLE_PI_PROVIDER_ID && !baseURL?.trim()) {
139603
+ throw new Error("OpenAI-compatible API requires a base URL.");
139604
+ }
139598
139605
  const now = new Date().toISOString();
139599
139606
  const auth = input.providerType === "chatgpt_oauth" ? parseChatGPTOAuth(input.apiKey) : { type: "api", key: input.apiKey };
139600
139607
  const next = {
@@ -139606,7 +139613,7 @@ async function createOrUpdateLocalProvider(input) {
139606
139613
  ...input.accessKey ? { access_key: input.accessKey } : {},
139607
139614
  ...input.region ? { region: input.region } : {},
139608
139615
  ...input.profile ? { profile: input.profile } : {},
139609
- ...input.baseURL ?? existing?.base_url ? { base_url: input.baseURL ?? existing?.base_url } : {},
139616
+ ...baseURL ? { base_url: baseURL } : {},
139610
139617
  ...input.timeout !== undefined ? { timeout: input.timeout } : existing?.timeout !== undefined ? { timeout: existing.timeout } : {},
139611
139618
  created_at: existing?.created_at ?? now,
139612
139619
  updated_at: now
@@ -140225,6 +140232,20 @@ var init_byok_providers = __esm(() => {
140225
140232
  providerName: "ollama-cloud",
140226
140233
  providerNames: ["ollama-cloud", "lc-ollama-cloud"]
140227
140234
  },
140235
+ {
140236
+ id: "openai-compatible",
140237
+ displayName: "OpenAI-compatible API",
140238
+ description: "Connect an OpenAI-compatible Chat Completions endpoint",
140239
+ providerType: "openai-compatible",
140240
+ providerName: "openai-compatible",
140241
+ providerNames: ["openai-compatible", "lc-openai-compatible"],
140242
+ requiresApiKey: false,
140243
+ defaultApiKey: LOCAL_PROVIDER_NO_API_KEY,
140244
+ fields: [
140245
+ { key: "apiKey", label: "API Key", secret: true, required: false },
140246
+ { key: "baseUrl", label: "Base URL" }
140247
+ ]
140248
+ },
140228
140249
  {
140229
140250
  id: "lmstudio",
140230
140251
  displayName: "LM Studio (local)",
@@ -146165,6 +146186,7 @@ var init_config2 = __esm(() => {
146165
146186
  allowedUsers: parsed.allowed_users ?? [],
146166
146187
  transcribeVoice: parsed.transcribe_voice === true,
146167
146188
  listenMode: parsed.listen_mode === true,
146189
+ mentionOnlyChannels: Array.isArray(parsed.mention_only_channels) ? parsed.mention_only_channels.filter((channelId) => typeof channelId === "string") : [],
146168
146190
  allowBots: normalizeSlackAllowBotsMode(parsed.allow_bots)
146169
146191
  };
146170
146192
  }
@@ -150405,6 +150427,11 @@ function cloneAccount(account) {
150405
150427
  if (isTelegramChannelAccount(account)) {
150406
150428
  cloned.binding = { ...account.binding };
150407
150429
  }
150430
+ if (isSlackChannelAccount(account)) {
150431
+ cloned.mentionOnlyChannels = [
150432
+ ...account.mentionOnlyChannels ?? []
150433
+ ];
150434
+ }
150408
150435
  if (isDiscordChannelAccount(account) && account.allowedChannels) {
150409
150436
  cloned.allowedChannels = Array.isArray(account.allowedChannels) ? [...account.allowedChannels] : { ...account.allowedChannels };
150410
150437
  }
@@ -150474,6 +150501,8 @@ function normalizeLoadedAccount(account) {
150474
150501
  delete next.progress_ui;
150475
150502
  delete next.progressUi;
150476
150503
  next.listenMode = next.listenMode === true;
150504
+ const mentionOnlyChannels = next.mentionOnlyChannels;
150505
+ next.mentionOnlyChannels = Array.isArray(mentionOnlyChannels) ? [...mentionOnlyChannels] : [];
150477
150506
  next.allowBots = normalizeSlackAllowBotsMode(next.allowBots);
150478
150507
  }
150479
150508
  if (isDiscordChannelAccount(next)) {
@@ -150623,6 +150652,7 @@ function makeDefaultLegacyAccount(channelId) {
150623
150652
  defaultPermissionMode: DEFAULT_SLACK_PERMISSION_MODE,
150624
150653
  transcribeVoice: config3.transcribeVoice === true,
150625
150654
  listenMode: config3.listenMode === true,
150655
+ mentionOnlyChannels: [...config3.mentionOnlyChannels ?? []],
150626
150656
  allowBots: config3.allowBots ?? false,
150627
150657
  createdAt: now,
150628
150658
  updatedAt: now
@@ -150822,6 +150852,7 @@ var init_accounts = __esm(() => {
150822
150852
  group_mode: "groupMode",
150823
150853
  inbound_debounce_ms: "inboundDebounceMs",
150824
150854
  listen_mode: "listenMode",
150855
+ mention_only_channels: "mentionOnlyChannels",
150825
150856
  message_prefix: "messagePrefix",
150826
150857
  media_max_bytes: "mediaMaxBytes",
150827
150858
  attachment_filter: "attachmentFilter",
@@ -154533,6 +154564,9 @@ var init_inbound_debounce = __esm(() => {
154533
154564
  });
154534
154565
 
154535
154566
  // src/channels/slack/ingress-policy.ts
154567
+ function isSlackMentionOnlyChannel(channelId, mentionOnlyChannels) {
154568
+ return mentionOnlyChannels?.includes(channelId) === true;
154569
+ }
154536
154570
  function hasRecordValue(value) {
154537
154571
  return value !== null && typeof value === "object";
154538
154572
  }
@@ -154763,6 +154797,9 @@ function createSlackIngressController(params) {
154763
154797
  })) {
154764
154798
  return;
154765
154799
  }
154800
+ if (basePolicy.chatType === "channel" && !basePolicy.wasMentioned && isSlackMentionOnlyChannel(channelId, config3.mentionOnlyChannels)) {
154801
+ return;
154802
+ }
154766
154803
  const attachments = await resolveSlackInboundAttachments({
154767
154804
  accountId: config3.accountId,
154768
154805
  token: config3.botToken,
@@ -154944,6 +154981,9 @@ function createSlackIngressController(params) {
154944
154981
  return;
154945
154982
  }
154946
154983
  const chatType = resolveSlackChatType(chatId);
154984
+ if (chatType === "channel" && isSlackMentionOnlyChannel(chatId, config3.mentionOnlyChannels)) {
154985
+ return;
154986
+ }
154947
154987
  const threadId = chatType === "channel" ? knownThreadIdsByMessageId.get(targetMessageId) ?? targetMessageId : knownThreadIdsByMessageId.get(targetMessageId) ?? null;
154948
154988
  try {
154949
154989
  await adapter.onMessage({
@@ -172754,6 +172794,7 @@ function createConversationRuntime(listener, agentId, conversationId) {
172754
172794
  currentToolsetPreference: "auto",
172755
172795
  currentLoadedTools: [],
172756
172796
  pendingApprovalBatchByToolCallId: new Map,
172797
+ approvalMessageIdByToolCallId: new Map,
172757
172798
  pendingInterruptedResults: null,
172758
172799
  pendingInterruptedContext: null,
172759
172800
  continuationEpoch: 0,
@@ -172790,6 +172831,7 @@ function clearConversationRuntimeState(runtime) {
172790
172831
  conversationId: runtime.conversationId
172791
172832
  });
172792
172833
  runtime.pendingApprovalBatchByToolCallId.clear();
172834
+ runtime.approvalMessageIdByToolCallId.clear();
172793
172835
  runtime.pendingInterruptedResults = null;
172794
172836
  runtime.pendingInterruptedContext = null;
172795
172837
  runtime.pendingInterruptedToolCallIds = null;
@@ -173673,6 +173715,10 @@ function getShellEnv() {
173673
173715
  env3[pathKey] = existingPath ? `${pathPrefixes.join(shellPathDelimiter())}${shellPathDelimiter()}${existingPath}` : pathPrefixes.join(shellPathDelimiter());
173674
173716
  }
173675
173717
  env3.USER_CWD = getCurrentWorkingDirectory();
173718
+ const environmentDeviceId = getRuntimeContext()?.environmentDeviceId?.trim();
173719
+ if (environmentDeviceId) {
173720
+ env3.LETTA_RUNTIME_ENVIRONMENT_DEVICE_ID = environmentDeviceId;
173721
+ }
173676
173722
  let agentId;
173677
173723
  try {
173678
173724
  const resolvedAgentId = getCurrentAgentId();
@@ -348867,9 +348913,21 @@ import { join as join34 } from "node:path";
348867
348913
  async function getUnavailableListenerClient() {
348868
348914
  throw new Error("letta.client is not available in listener mods");
348869
348915
  }
348916
+ function resolveListenerAgentMemfsContext(agentId) {
348917
+ if (!agentId)
348918
+ return { enabled: false, memoryDir: null };
348919
+ try {
348920
+ return settingsManager.isMemfsEnabled(agentId) ? {
348921
+ enabled: true,
348922
+ memoryDir: getScopedMemoryFilesystemRoot(agentId)
348923
+ } : { enabled: false, memoryDir: null };
348924
+ } catch {
348925
+ return { enabled: false, memoryDir: null };
348926
+ }
348927
+ }
348870
348928
  function createListenerModContext(options3 = {}) {
348871
348929
  const cwd = options3.workingDirectory ?? getCurrentWorkingDirectory();
348872
- return buildModInvocationContext({
348930
+ const context3 = buildModInvocationContext({
348873
348931
  agent: options3.agent ?? null,
348874
348932
  conversationId: options3.sessionId ?? null,
348875
348933
  modelIdentifier: options3.modelIdentifier ?? null,
@@ -348877,6 +348935,13 @@ function createListenerModContext(options3 = {}) {
348877
348935
  toolset: options3.toolset ?? null,
348878
348936
  workingDirectory: cwd
348879
348937
  });
348938
+ return {
348939
+ ...context3,
348940
+ memfs: resolveListenerAgentMemfsContext(context3.agent.id)
348941
+ };
348942
+ }
348943
+ function createListenerAgentModContext(agentId) {
348944
+ return createListenerModContext({ agent: { id: agentId } });
348880
348945
  }
348881
348946
  function createListenerModAdapter(options3 = {}) {
348882
348947
  return createModAdapter({
@@ -350048,7 +350113,7 @@ function scheduleQueueEmit(runtime, scope) {
350048
350113
  }
350049
350114
  function createLifecycleMessageBase(messageType, runId) {
350050
350115
  return {
350051
- id: `message-${crypto.randomUUID()}`,
350116
+ id: `lifecycle-${crypto.randomUUID()}`,
350052
350117
  date: new Date().toISOString(),
350053
350118
  message_type: messageType,
350054
350119
  ...runId ? { run_id: runId } : {}
@@ -364865,6 +364930,7 @@ function composeSubagentChildEnv(options3) {
364865
364930
  backendMode,
364866
364931
  localBackendStorageDir,
364867
364932
  parentAgentId,
364933
+ subagentType,
364868
364934
  launchProfile,
364869
364935
  inheritedPrimaryRoot,
364870
364936
  memoryScope,
@@ -364878,6 +364944,7 @@ function composeSubagentChildEnv(options3) {
364878
364944
  ...inheritedApiKey && { LETTA_API_KEY: inheritedApiKey },
364879
364945
  ...inheritedBaseUrl && { LETTA_BASE_URL: inheritedBaseUrl },
364880
364946
  LETTA_CODE_AGENT_ROLE: "subagent",
364947
+ ...subagentType === "reflection" && { [LETTA_DISABLE_MODS_ENV]: "1" },
364881
364948
  ...parentAgentId && { LETTA_PARENT_AGENT_ID: parentAgentId },
364882
364949
  ...transcriptPath && { TRANSCRIPT_PATH: transcriptPath },
364883
364950
  ...inheritedChannelContext && {
@@ -365410,6 +365477,7 @@ async function executeSubagent(type3, config3, model, userPrompt, subagentId, is
365410
365477
  backendMode,
365411
365478
  localBackendStorageDir,
365412
365479
  parentAgentId,
365480
+ subagentType: type3,
365413
365481
  launchProfile: effectiveLaunchProfile,
365414
365482
  inheritedPrimaryRoot,
365415
365483
  memoryScope,
@@ -373761,6 +373829,7 @@ function parseInheritedChannelContextEnv() {
373761
373829
  async function prepareToolExecutionContextForScope(params) {
373762
373830
  const {
373763
373831
  connectionId,
373832
+ environmentDeviceId,
373764
373833
  agentId,
373765
373834
  conversationId,
373766
373835
  overrideModel,
@@ -373826,6 +373895,7 @@ async function prepareToolExecutionContextForScope(params) {
373826
373895
  agent: agent2,
373827
373896
  runtimeContext: {
373828
373897
  connectionId,
373898
+ environmentDeviceId,
373829
373899
  agentId,
373830
373900
  agentName: agent2.name ?? null,
373831
373901
  conversationId: scopedConversationId,
@@ -375025,13 +375095,13 @@ echo password=${token}
375025
375095
  } else {
375026
375096
  helper = `!f() { echo "username=letta"; echo "password=${token}"; }; f`;
375027
375097
  }
375028
- await gitConfig(dir, [
375029
- "config",
375030
- `credential.${normalizedBaseUrl}.helper`,
375031
- helper
375032
- ]);
375098
+ const writeHelperWithReset = async (key) => {
375099
+ await gitConfig(dir, ["config", "--local", "--replace-all", key, ""]);
375100
+ await gitConfig(dir, ["config", "--local", "--add", key, helper]);
375101
+ };
375102
+ await writeHelperWithReset(`credential.${normalizedBaseUrl}.helper`);
375033
375103
  if (rawBaseUrl !== normalizedBaseUrl) {
375034
- await gitConfig(dir, ["config", `credential.${rawBaseUrl}.helper`, helper]);
375104
+ await writeHelperWithReset(`credential.${rawBaseUrl}.helper`);
375035
375105
  }
375036
375106
  debugLog("memfs-git", `Configured local credential helper for ${normalizedBaseUrl}${rawBaseUrl !== normalizedBaseUrl ? ` (and raw ${rawBaseUrl})` : ""}`);
375037
375107
  }
@@ -379215,6 +379285,7 @@ function isEncryptedContentError(e2) {
379215
379285
  function formatErrorDetails2(e2, agentId, conversationId, options3 = {}) {
379216
379286
  let runId;
379217
379287
  const surface = options3.surface ?? "terminal";
379288
+ const fallback = (raw) => options3.unclassifiedFallback === "generic" ? GENERIC_ERROR_MESSAGE : raw;
379218
379289
  const encryptedContentMsg = checkEncryptedContentError(e2);
379219
379290
  if (encryptedContentMsg)
379220
379291
  return encryptedContentMsg;
@@ -379303,6 +379374,8 @@ Detail: ${detail}` : "";
379303
379374
  const conversationBusyMessage = formatConversationBusyErrorForDisplay(baseError, runId, options3);
379304
379375
  if (conversationBusyMessage)
379305
379376
  return conversationBusyMessage;
379377
+ if (options3.unclassifiedFallback === "generic")
379378
+ return fallback(baseError);
379306
379379
  return runId && agentId ? `${baseError}
379307
379380
  ${createAgentLink(runId, agentId, conversationId, surface)}` : baseError;
379308
379381
  }
@@ -379316,32 +379389,34 @@ ${createAgentLink(runId, agentId, conversationId, surface)}` : baseError;
379316
379389
  const conversationBusyMessage = formatConversationBusyErrorForDisplay(baseError, runId, options3);
379317
379390
  if (conversationBusyMessage)
379318
379391
  return conversationBusyMessage;
379392
+ if (options3.unclassifiedFallback === "generic")
379393
+ return fallback(baseError);
379319
379394
  return runId && agentId ? `${baseError}
379320
379395
  ${createAgentLink(runId, agentId, conversationId, surface)}` : baseError;
379321
379396
  }
379322
- return e2.message;
379397
+ return fallback(e2.message);
379323
379398
  }
379324
379399
  if (e2 instanceof Error) {
379325
- return e2.message;
379400
+ return fallback(e2.message);
379326
379401
  }
379327
379402
  if (typeof e2 === "object" && e2 !== null) {
379328
379403
  const obj = e2;
379329
379404
  if (typeof obj.message === "string") {
379330
- return obj.message;
379405
+ return fallback(obj.message);
379331
379406
  }
379332
379407
  if (typeof obj.error === "string") {
379333
- return obj.error;
379408
+ return fallback(obj.error);
379334
379409
  }
379335
379410
  if (typeof obj.detail === "string") {
379336
- return obj.detail;
379411
+ return fallback(obj.detail);
379337
379412
  }
379338
379413
  try {
379339
- return JSON.stringify(e2, null, 2);
379414
+ return fallback(JSON.stringify(e2, null, 2));
379340
379415
  } catch {
379341
- return "[Error: Unable to serialize error object]";
379416
+ return fallback("[Error: Unable to serialize error object]");
379342
379417
  }
379343
379418
  }
379344
- return String(e2);
379419
+ return fallback(String(e2));
379345
379420
  }
379346
379421
  function getRetryStatusMessage(errorDetail2) {
379347
379422
  if (!errorDetail2)
@@ -379373,7 +379448,7 @@ function createAgentLink(runId, agentId, conversationId, surface) {
379373
379448
  const agentRef = surface === "terminal" ? buildAgentTerminalLink(agentId, { conversationId }) : agentId;
379374
379449
  return `View agent: ${agentRef} (run: ${runId})`;
379375
379450
  }
379376
- var LETTA_USAGE_URL, LETTA_AGENTS_URL, CLOUDFLARE_EDGE_5XX_MARKER_PATTERN, CLOUDFLARE_EDGE_5XX_TITLE_PATTERN, CLOUDFLARE_EDGE_5XX_FORMATTED_PATTERN, CLOUDFLARE_JSON_5XX_PATTERN, CHATGPT_USAGE_LIMIT_HINT = "Switch models with /model, or connect your own provider keys with /connect.", ENCRYPTED_CONTENT_HINT, DEFAULT_RETRY_MESSAGE = "Unexpected downstream LLM API error, retrying...", ENDPOINT_TYPE_DISPLAY_NAMES;
379451
+ var LETTA_USAGE_URL, LETTA_AGENTS_URL, GENERIC_ERROR_MESSAGE = "The request failed. Please try again.", CLOUDFLARE_EDGE_5XX_MARKER_PATTERN, CLOUDFLARE_EDGE_5XX_TITLE_PATTERN, CLOUDFLARE_EDGE_5XX_FORMATTED_PATTERN, CLOUDFLARE_JSON_5XX_PATTERN, CHATGPT_USAGE_LIMIT_HINT = "Switch models with /model, or connect your own provider keys with /connect.", ENCRYPTED_CONTENT_HINT, DEFAULT_RETRY_MESSAGE = "Unexpected downstream LLM API error, retrying...", ENDPOINT_TYPE_DISPLAY_NAMES;
379377
379452
  var init_error_formatter = __esm(() => {
379378
379453
  init_error();
379379
379454
  init_conversation_busy_error();
@@ -380797,7 +380872,10 @@ function createLocalEndpointPiProvider(options3) {
380797
380872
  name: `${options3.name} API key`,
380798
380873
  resolve: async ({ credential }) => {
380799
380874
  const apiKey = credential?.key ?? options3.apiKey;
380800
- return apiKey ? { auth: { apiKey }, source: "local provider record" } : undefined;
380875
+ return apiKey ? {
380876
+ auth: apiKey === "not-needed" ? { apiKey, headers: { Authorization: null } } : { apiKey },
380877
+ source: "local provider record"
380878
+ } : undefined;
380801
380879
  }
380802
380880
  }
380803
380881
  },
@@ -381158,6 +381236,27 @@ var init_pi_ollama_provider = __esm(() => {
381158
381236
  init_pi_local_endpoint_provider();
381159
381237
  });
381160
381238
 
381239
+ // src/backend/dev/pi-openai-compatible-provider.ts
381240
+ function createOpenAICompatiblePiProvider(options3) {
381241
+ return createLocalEndpointPiProvider({
381242
+ id: OPENAI_COMPATIBLE_PI_PROVIDER_ID,
381243
+ name: "OpenAI-compatible API",
381244
+ baseURL: options3.baseURL,
381245
+ ...options3.apiKey ? { apiKey: options3.apiKey } : {},
381246
+ ...options3.fetchImpl ? { fetchImpl: options3.fetchImpl } : {},
381247
+ ...options3.discoveryTimeoutMs ? { discoveryTimeoutMs: options3.discoveryTimeoutMs } : {},
381248
+ discover: openAICompatibleDiscover
381249
+ });
381250
+ }
381251
+ var openAICompatibleDiscover = async (context3) => {
381252
+ const list = await context3.fetchJson(`${context3.openAIBaseURL}/models`);
381253
+ return modelIdsFromOpenAICompatibleList(list).map((modelId) => context3.lastKnown.get(modelId) ?? context3.buildModel({ id: modelId }));
381254
+ };
381255
+ var init_pi_openai_compatible_provider = __esm(() => {
381256
+ init_pi_local_endpoint_provider();
381257
+ init_pi_provider_registry();
381258
+ });
381259
+
381161
381260
  // src/backend/dev/pi-models-runtime.ts
381162
381261
  function aliasedAuthContext() {
381163
381262
  const base2 = defaultProviderAuthContext();
@@ -381185,7 +381284,7 @@ function resolveLocalEndpointConnection(providerId2, storageDir) {
381185
381284
  break;
381186
381285
  }
381187
381286
  const baseURL = record5?.base_url ?? spec.baseUrlEnv?.() ?? spec.defaultBaseURL ?? "";
381188
- const apiKey = localProviderApiKeyFromRecord(record5) ?? spec.apiKeyEnv?.() ?? spec.fallbackApiKey;
381287
+ const apiKey = localProviderApiKeyFromRecord(record5) ?? spec.apiKeyEnv?.() ?? (record5 !== null || spec.autoDetectLocalEndpoint === true ? spec.fallbackApiKey : undefined);
381189
381288
  return {
381190
381289
  baseURL,
381191
381290
  apiKey,
@@ -381424,6 +381523,7 @@ var init_pi_models_runtime = __esm(() => {
381424
381523
  init_pi_lmstudio_provider();
381425
381524
  init_pi_mod_provider();
381426
381525
  init_pi_ollama_provider();
381526
+ init_pi_openai_compatible_provider();
381427
381527
  init_pi_provider_mod_registry();
381428
381528
  init_pi_provider_registry();
381429
381529
  init_registered_pi_provider_runtime();
@@ -381440,7 +381540,11 @@ var init_pi_models_runtime = __esm(() => {
381440
381540
  })
381441
381541
  ],
381442
381542
  [LLAMA_CPP_PI_PROVIDER_ID, (input) => createLlamaCppPiProvider(input)],
381443
- [LMSTUDIO_PI_PROVIDER_ID, (input) => createLmStudioPiProvider(input)]
381543
+ [LMSTUDIO_PI_PROVIDER_ID, (input) => createLmStudioPiProvider(input)],
381544
+ [
381545
+ OPENAI_COMPATIBLE_PI_PROVIDER_ID,
381546
+ (input) => createOpenAICompatiblePiProvider(input)
381547
+ ]
381444
381548
  ]);
381445
381549
  });
381446
381550
 
@@ -381674,7 +381778,10 @@ async function resolvePiModelForAgent(modelHandle, modelSettings = {}, options3
381674
381778
  }
381675
381779
  const fallbackModelId = !registeredProvider && spec?.piProvider ? fallbackCatalogModelId(spec.piProvider, modelId) : undefined;
381676
381780
  const { model: publishedModel, auth: authResult } = await modelsRuntime.resolveTurn(runtimeProviderId, modelId, fallbackModelId);
381677
- connection = { ...connection, apiKey: authResult?.auth.apiKey };
381781
+ connection = {
381782
+ ...connection,
381783
+ apiKey: authResult?.auth.apiKey === LOCAL_PROVIDER_NO_API_KEY ? undefined : authResult?.auth.apiKey
381784
+ };
381678
381785
  if (authResult?.auth.baseUrl)
381679
381786
  baseURL = authResult.auth.baseUrl;
381680
381787
  if (authResult?.auth.headers) {
@@ -384635,10 +384742,12 @@ function isResumedConversation(conversation) {
384635
384742
  function approvalRequestsFromMessage(messageToCheck) {
384636
384743
  const approvalMsg = messageToCheck;
384637
384744
  const toolCalls = Array.isArray(approvalMsg.tool_calls) ? approvalMsg.tool_calls : approvalMsg.tool_call ? [approvalMsg.tool_call] : [];
384745
+ const messageId = "id" in messageToCheck && typeof messageToCheck.id === "string" ? messageToCheck.id : undefined;
384638
384746
  return toolCalls.filter((tc) => !!tc && !!tc.tool_call_id).map((tc) => ({
384639
384747
  toolCallId: tc.tool_call_id,
384640
384748
  toolName: tc.name || "",
384641
- toolArgs: tc.arguments || ""
384749
+ toolArgs: tc.arguments || "",
384750
+ ...messageId ? { messageId } : {}
384642
384751
  }));
384643
384752
  }
384644
384753
  function logPendingApprovals(pendingApprovals) {
@@ -426741,6 +426850,9 @@ function isNullableString4(value) {
426741
426850
  function isBoolean3(value) {
426742
426851
  return value === true || value === false;
426743
426852
  }
426853
+ function isStringArray5(value) {
426854
+ return Array.isArray(value) && value.every(isString3);
426855
+ }
426744
426856
  function isDefaultPermissionMode2(value) {
426745
426857
  return value === "standard" || value === "acceptEdits" || value === "unrestricted" || value === "default" || value === "bypassPermissions" || value === "fullAccess";
426746
426858
  }
@@ -426757,6 +426869,7 @@ var init_account_config4 = __esm(() => {
426757
426869
  "transcribe_voice",
426758
426870
  "show_completed_reaction",
426759
426871
  "listen_mode",
426872
+ "mention_only_channels",
426760
426873
  "allow_bots"
426761
426874
  ]);
426762
426875
  slackAccountConfigAdapter = {
@@ -426766,7 +426879,7 @@ var init_account_config4 = __esm(() => {
426766
426879
  return false;
426767
426880
  }
426768
426881
  }
426769
- return (config3.bot_token === undefined || isString3(config3.bot_token)) && (config3.app_token === undefined || isString3(config3.app_token)) && (config3.mode === undefined || config3.mode === "socket") && (config3.agent_id === undefined || isNullableString4(config3.agent_id)) && (config3.default_permission_mode === undefined || isDefaultPermissionMode2(config3.default_permission_mode)) && (config3.transcribe_voice === undefined || isBoolean3(config3.transcribe_voice)) && (config3.show_completed_reaction === undefined || isBoolean3(config3.show_completed_reaction)) && (config3.listen_mode === undefined || isBoolean3(config3.listen_mode)) && isValidSlackAllowBotsConfigValue(config3.allow_bots);
426882
+ return (config3.bot_token === undefined || isString3(config3.bot_token)) && (config3.app_token === undefined || isString3(config3.app_token)) && (config3.mode === undefined || config3.mode === "socket") && (config3.agent_id === undefined || isNullableString4(config3.agent_id)) && (config3.default_permission_mode === undefined || isDefaultPermissionMode2(config3.default_permission_mode)) && (config3.transcribe_voice === undefined || isBoolean3(config3.transcribe_voice)) && (config3.show_completed_reaction === undefined || isBoolean3(config3.show_completed_reaction)) && (config3.listen_mode === undefined || isBoolean3(config3.listen_mode)) && (config3.mention_only_channels === undefined || isStringArray5(config3.mention_only_channels)) && isValidSlackAllowBotsConfigValue(config3.allow_bots);
426770
426883
  },
426771
426884
  toAccountPatch(config3) {
426772
426885
  return {
@@ -426777,6 +426890,7 @@ var init_account_config4 = __esm(() => {
426777
426890
  defaultPermissionMode: isDefaultPermissionMode2(config3.default_permission_mode) ? migratePermissionMode(config3.default_permission_mode) : undefined,
426778
426891
  transcribeVoice: isBoolean3(config3.transcribe_voice) ? config3.transcribe_voice : undefined,
426779
426892
  listenMode: isBoolean3(config3.listen_mode) ? config3.listen_mode : undefined,
426893
+ mentionOnlyChannels: isStringArray5(config3.mention_only_channels) ? [...config3.mention_only_channels] : undefined,
426780
426894
  allowBots: config3.allow_bots !== undefined && isValidSlackAllowBotsConfigValue(config3.allow_bots) ? normalizeSlackAllowBotsMode(config3.allow_bots) : undefined
426781
426895
  };
426782
426896
  },
@@ -426789,6 +426903,7 @@ var init_account_config4 = __esm(() => {
426789
426903
  default_permission_mode: account.defaultPermissionMode ?? DEFAULT_SLACK_PERMISSION_MODE,
426790
426904
  transcribe_voice: account.transcribeVoice === true,
426791
426905
  listen_mode: account.listenMode === true,
426906
+ mention_only_channels: [...account.mentionOnlyChannels ?? []],
426792
426907
  allow_bots: account.allowBots ?? false
426793
426908
  };
426794
426909
  },
@@ -426801,6 +426916,7 @@ var init_account_config4 = __esm(() => {
426801
426916
  default_permission_mode: account.defaultPermissionMode ?? DEFAULT_SLACK_PERMISSION_MODE,
426802
426917
  transcribe_voice: account.transcribeVoice === true,
426803
426918
  listen_mode: account.listenMode === true,
426919
+ mention_only_channels: [...account.mentionOnlyChannels ?? []],
426804
426920
  allow_bots: account.allowBots ?? false
426805
426921
  };
426806
426922
  },
@@ -426890,7 +427006,7 @@ function isNullableString5(value) {
426890
427006
  function isBoolean5(value) {
426891
427007
  return typeof value === "boolean";
426892
427008
  }
426893
- function isStringArray5(value) {
427009
+ function isStringArray6(value) {
426894
427010
  return Array.isArray(value) && value.every((entry) => typeof entry === "string");
426895
427011
  }
426896
427012
  function isGroupMode3(value) {
@@ -426936,22 +427052,22 @@ var init_account_config6 = __esm(() => {
426936
427052
  return false;
426937
427053
  }
426938
427054
  }
426939
- return (config3.agent_id === undefined || isNullableString5(config3.agent_id)) && (config3.self_chat_mode === undefined || isBoolean5(config3.self_chat_mode)) && (config3.group_mode === undefined || isGroupMode3(config3.group_mode)) && (config3.allowed_groups === undefined || isStringArray5(config3.allowed_groups)) && (config3.mention_patterns === undefined || isStringArray5(config3.mention_patterns)) && (config3.transcribe_voice === undefined || isBoolean5(config3.transcribe_voice)) && (config3.download_media === undefined || isBoolean5(config3.download_media)) && (config3.media_max_bytes === undefined || isPositiveNumber2(config3.media_max_bytes)) && (config3.attachment_filter === undefined || isBoolean5(config3.attachment_filter)) && (config3.attachment_mime_types === undefined || isStringArray5(config3.attachment_mime_types)) && (config3.attachment_allowed_recipients === undefined || isStringArray5(config3.attachment_allowed_recipients)) && (config3.attachment_allowed_paths === undefined || isStringArray5(config3.attachment_allowed_paths)) && (config3.attachment_path_recursive === undefined || isBoolean5(config3.attachment_path_recursive)) && (config3.inbound_debounce_ms === undefined || isValidInboundDebounceMs(config3.inbound_debounce_ms)) && (config3.waiting_behavior === undefined || isWaitingBehavior(config3.waiting_behavior)) && (config3.message_prefix === undefined || isString5(config3.message_prefix));
427055
+ return (config3.agent_id === undefined || isNullableString5(config3.agent_id)) && (config3.self_chat_mode === undefined || isBoolean5(config3.self_chat_mode)) && (config3.group_mode === undefined || isGroupMode3(config3.group_mode)) && (config3.allowed_groups === undefined || isStringArray6(config3.allowed_groups)) && (config3.mention_patterns === undefined || isStringArray6(config3.mention_patterns)) && (config3.transcribe_voice === undefined || isBoolean5(config3.transcribe_voice)) && (config3.download_media === undefined || isBoolean5(config3.download_media)) && (config3.media_max_bytes === undefined || isPositiveNumber2(config3.media_max_bytes)) && (config3.attachment_filter === undefined || isBoolean5(config3.attachment_filter)) && (config3.attachment_mime_types === undefined || isStringArray6(config3.attachment_mime_types)) && (config3.attachment_allowed_recipients === undefined || isStringArray6(config3.attachment_allowed_recipients)) && (config3.attachment_allowed_paths === undefined || isStringArray6(config3.attachment_allowed_paths)) && (config3.attachment_path_recursive === undefined || isBoolean5(config3.attachment_path_recursive)) && (config3.inbound_debounce_ms === undefined || isValidInboundDebounceMs(config3.inbound_debounce_ms)) && (config3.waiting_behavior === undefined || isWaitingBehavior(config3.waiting_behavior)) && (config3.message_prefix === undefined || isString5(config3.message_prefix));
426940
427056
  },
426941
427057
  toAccountPatch(config3) {
426942
427058
  return {
426943
427059
  agentId: isNullableString5(config3.agent_id) ? config3.agent_id : undefined,
426944
427060
  selfChatMode: isBoolean5(config3.self_chat_mode) ? config3.self_chat_mode : undefined,
426945
427061
  groupMode: isGroupMode3(config3.group_mode) ? config3.group_mode : undefined,
426946
- allowedGroups: isStringArray5(config3.allowed_groups) ? [...config3.allowed_groups] : undefined,
426947
- mentionPatterns: isStringArray5(config3.mention_patterns) ? [...config3.mention_patterns] : undefined,
427062
+ allowedGroups: isStringArray6(config3.allowed_groups) ? [...config3.allowed_groups] : undefined,
427063
+ mentionPatterns: isStringArray6(config3.mention_patterns) ? [...config3.mention_patterns] : undefined,
426948
427064
  transcribeVoice: isBoolean5(config3.transcribe_voice) ? config3.transcribe_voice : undefined,
426949
427065
  downloadMedia: isBoolean5(config3.download_media) ? config3.download_media : undefined,
426950
427066
  mediaMaxBytes: isPositiveNumber2(config3.media_max_bytes) ? config3.media_max_bytes : undefined,
426951
427067
  attachmentFilter: isBoolean5(config3.attachment_filter) ? config3.attachment_filter : undefined,
426952
- attachmentMimeTypes: isStringArray5(config3.attachment_mime_types) ? [...config3.attachment_mime_types] : undefined,
426953
- attachmentAllowedRecipients: isStringArray5(config3.attachment_allowed_recipients) ? [...config3.attachment_allowed_recipients] : undefined,
426954
- attachmentAllowedPaths: isStringArray5(config3.attachment_allowed_paths) ? [...config3.attachment_allowed_paths] : undefined,
427068
+ attachmentMimeTypes: isStringArray6(config3.attachment_mime_types) ? [...config3.attachment_mime_types] : undefined,
427069
+ attachmentAllowedRecipients: isStringArray6(config3.attachment_allowed_recipients) ? [...config3.attachment_allowed_recipients] : undefined,
427070
+ attachmentAllowedPaths: isStringArray6(config3.attachment_allowed_paths) ? [...config3.attachment_allowed_paths] : undefined,
426955
427071
  attachmentPathRecursive: isBoolean5(config3.attachment_path_recursive) ? config3.attachment_path_recursive : undefined,
426956
427072
  inboundDebounceMs: isValidInboundDebounceMs(config3.inbound_debounce_ms) ? Math.trunc(Math.min(config3.inbound_debounce_ms, 1e4)) : undefined,
426957
427073
  waitingBehavior: isWaitingBehavior(config3.waiting_behavior) ? config3.waiting_behavior : undefined,
@@ -427307,6 +427423,7 @@ function createAccountFromPatch(channelId, accountId, patch2) {
427307
427423
  defaultPermissionMode: normalizedPatch.defaultPermissionMode ?? DEFAULT_SLACK_PERMISSION_MODE,
427308
427424
  transcribeVoice: normalizedPatch.transcribeVoice === true,
427309
427425
  listenMode: normalizedPatch.listenMode === true,
427426
+ mentionOnlyChannels: [...normalizedPatch.mentionOnlyChannels ?? []],
427310
427427
  allowBots: normalizedPatch.allowBots ?? false,
427311
427428
  dmPolicy: normalizedPatch.dmPolicy ?? "open",
427312
427429
  allowedUsers: normalizedPatch.allowedUsers ?? [],
@@ -427423,6 +427540,9 @@ function mergeAccountPatch(existing, patch2) {
427423
427540
  defaultPermissionMode: normalizedPatch.defaultPermissionMode ?? existing.defaultPermissionMode ?? DEFAULT_SLACK_PERMISSION_MODE,
427424
427541
  transcribeVoice: normalizedPatch.transcribeVoice ?? existing.transcribeVoice ?? false,
427425
427542
  listenMode: normalizedPatch.listenMode ?? existing.listenMode ?? false,
427543
+ mentionOnlyChannels: [
427544
+ ...normalizedPatch.mentionOnlyChannels ?? existing.mentionOnlyChannels ?? []
427545
+ ],
427426
427546
  allowBots: normalizedPatch.allowBots ?? existing.allowBots ?? false,
427427
427547
  dmPolicy: normalizedPatch.dmPolicy ?? existing.dmPolicy,
427428
427548
  allowedUsers: normalizedPatch.allowedUsers ?? existing.allowedUsers,
@@ -427616,6 +427736,7 @@ function toAccountSnapshot(account) {
427616
427736
  defaultPermissionMode: account.defaultPermissionMode ?? DEFAULT_SLACK_PERMISSION_MODE,
427617
427737
  transcribeVoice: account.transcribeVoice === true,
427618
427738
  listenMode: account.listenMode === true,
427739
+ mentionOnlyChannels: [...account.mentionOnlyChannels ?? []],
427619
427740
  allowBots: account.allowBots ?? false,
427620
427741
  createdAt: account.createdAt,
427621
427742
  updatedAt: account.updatedAt
@@ -427776,6 +427897,7 @@ function getChannelConfigSnapshot(channelId, accountId) {
427776
427897
  defaultPermissionMode: account.defaultPermissionMode ?? DEFAULT_SLACK_PERMISSION_MODE,
427777
427898
  transcribeVoice: account.transcribeVoice === true,
427778
427899
  listenMode: account.listenMode === true,
427900
+ mentionOnlyChannels: [...account.mentionOnlyChannels ?? []],
427779
427901
  allowBots: account.allowBots ?? false
427780
427902
  };
427781
427903
  }
@@ -428299,6 +428421,7 @@ async function setChannelConfigLive(channelId, patch2, accountId) {
428299
428421
  threadPolicyByChannel: normalizedPatch.threadPolicyByChannel,
428300
428422
  acknowledgeMessageReaction: normalizedPatch.acknowledgeMessageReaction,
428301
428423
  listenMode: normalizedPatch.listenMode,
428424
+ mentionOnlyChannels: normalizedPatch.mentionOnlyChannels,
428302
428425
  allowBots: normalizedPatch.allowBots,
428303
428426
  removeStaleRoutes: normalizedPatch.removeStaleRoutes,
428304
428427
  inboundDebounceMs: normalizedPatch.inboundDebounceMs,
@@ -428336,6 +428459,7 @@ async function setChannelConfigLive(channelId, patch2, accountId) {
428336
428459
  threadPolicyByChannel: normalizedPatch.threadPolicyByChannel,
428337
428460
  acknowledgeMessageReaction: normalizedPatch.acknowledgeMessageReaction,
428338
428461
  listenMode: normalizedPatch.listenMode,
428462
+ mentionOnlyChannels: normalizedPatch.mentionOnlyChannels,
428339
428463
  allowBots: normalizedPatch.allowBots,
428340
428464
  removeStaleRoutes: normalizedPatch.removeStaleRoutes,
428341
428465
  inboundDebounceMs: normalizedPatch.inboundDebounceMs,
@@ -429992,6 +430116,9 @@ function isConnectOAuthProvider(provider) {
429992
430116
  function isConnectBedrockProvider(provider) {
429993
430117
  return provider.byokProvider.providerType === "bedrock" || provider.byokProvider.providerType === "amazon-bedrock";
429994
430118
  }
430119
+ function isConnectBaseURLRequired(provider) {
430120
+ return provider.byokProvider.fields?.some((field) => field.key === "baseUrl" && field.required !== false) === true;
430121
+ }
429995
430122
  function isConnectApiKeyProvider(provider) {
429996
430123
  return !isConnectOAuthProvider(provider) && !isConnectBedrockProvider(provider);
429997
430124
  }
@@ -430049,6 +430176,7 @@ function formatUsage() {
430049
430176
  " letta connect codex --method device-code",
430050
430177
  " letta connect anthropic <api_key>",
430051
430178
  " letta connect openai --api-key <api_key>",
430179
+ " letta connect openai-compatible --base-url http://localhost:8000/v1 [--api-key <api_key>]",
430052
430180
  " letta connect ollama --base-url http://192.168.1.50:11434/v1",
430053
430181
  " letta connect lmstudio --base-url http://127.0.0.1:1234/v1 --timeout 600s",
430054
430182
  " letta connect llama-cpp --base-url http://localhost:8080/v1",
@@ -430262,6 +430390,10 @@ Provider '${providerName}' saved.`);
430262
430390
  io.stderr(getErrorMessage2(error54));
430263
430391
  return 1;
430264
430392
  }
430393
+ if (isConnectBaseURLRequired(provider) && !connectionOptions.baseURL?.trim()) {
430394
+ io.stderr(`Missing base URL for ${provider.canonical}. Pass --base-url <url>.`);
430395
+ return 1;
430396
+ }
430265
430397
  apiKey ||= defaultConnectApiKey(provider) ?? "";
430266
430398
  if (!apiKey && isConnectZaiBaseProvider(provider)) {
430267
430399
  io.stdout(`Do you have a Z.ai Coding plan?
@@ -438839,6 +438971,100 @@ var init_cron = __esm(async () => {
438839
438971
  await init_scheduler();
438840
438972
  });
438841
438973
 
438974
+ // src/backend/api/environments.ts
438975
+ var exports_environments2 = {};
438976
+ __export(exports_environments2, {
438977
+ sendEnvironmentMessage: () => sendEnvironmentMessage,
438978
+ resolveEnvironmentConnectionId: () => resolveEnvironmentConnectionId,
438979
+ resolveAgentSandboxConnectionId: () => resolveAgentSandboxConnectionId,
438980
+ listEnvironments: () => listEnvironments,
438981
+ isEnvironmentOnline: () => isEnvironmentOnline,
438982
+ getEnvironmentConnection: () => getEnvironmentConnection,
438983
+ describeEnvironment: () => describeEnvironment,
438984
+ createAgentSandbox: () => createAgentSandbox
438985
+ });
438986
+ async function listEnvironments(options3 = {}) {
438987
+ return apiRequest("GET", "/v1/environments", undefined, {
438988
+ query: {
438989
+ limit: options3.limit,
438990
+ after: options3.after,
438991
+ onlineOnly: options3.onlineOnly
438992
+ }
438993
+ });
438994
+ }
438995
+ async function sendEnvironmentMessage(connectionId, body3) {
438996
+ return apiRequest("POST", `/v1/environments/${encodeURIComponent(connectionId)}/messages`, body3);
438997
+ }
438998
+ async function getEnvironmentConnection(deviceId) {
438999
+ return apiRequest("GET", `/v1/environments/${encodeURIComponent(deviceId)}`);
439000
+ }
439001
+ async function createAgentSandbox(agentId) {
439002
+ return apiRequest("POST", `/v1/agents/${encodeURIComponent(agentId)}/sandboxes`, {});
439003
+ }
439004
+ function isEnvironmentOnline(environment2) {
439005
+ return typeof environment2.connectionId === "string" && environment2.connectionId.length > 0 && typeof environment2.lastHeartbeat === "number" && Date.now() - environment2.lastHeartbeat < 120000;
439006
+ }
439007
+ function describeEnvironment(environment2) {
439008
+ const status = isEnvironmentOnline(environment2) ? "online" : "offline";
439009
+ return `${environment2.connectionName} (${environment2.deviceId}, ${status})`;
439010
+ }
439011
+ async function resolveEnvironmentConnectionId(selector) {
439012
+ const trimmed = selector.trim();
439013
+ if (!trimmed) {
439014
+ throw new Error("Environment selector must not be empty");
439015
+ }
439016
+ const response = await listEnvironments({ limit: 100 });
439017
+ const matches2 = response.connections.filter((environment3) => {
439018
+ return environment3.connectionId === trimmed || environment3.id === trimmed || environment3.deviceId === trimmed || environment3.connectionName === trimmed;
439019
+ });
439020
+ if (matches2.length === 0) {
439021
+ throw new Error(`Environment "${trimmed}" not found. Run \`letta environments list\` to discover available environments.`);
439022
+ }
439023
+ const onlineMatches = matches2.filter(isEnvironmentOnline);
439024
+ if (onlineMatches.length === 0) {
439025
+ throw new Error(`Environment "${trimmed}" is offline. Matched: ${matches2.map(describeEnvironment).join(", ")}`);
439026
+ }
439027
+ if (onlineMatches.length > 1) {
439028
+ throw new Error(`Environment "${trimmed}" is ambiguous. Matched: ${onlineMatches.map(describeEnvironment).join(", ")}`);
439029
+ }
439030
+ const environment2 = onlineMatches[0];
439031
+ if (!environment2) {
439032
+ throw new Error(`Environment "${trimmed}" is offline`);
439033
+ }
439034
+ if (!environment2.connectionId) {
439035
+ throw new Error(`Environment "${trimmed}" has no active connection id`);
439036
+ }
439037
+ return { connectionId: environment2.connectionId, environment: environment2 };
439038
+ }
439039
+ async function resolveAgentSandboxConnectionId(agentId, options3 = {}) {
439040
+ const timeoutMs = options3.timeoutMs ?? 3 * 60000;
439041
+ const pollIntervalMs = options3.pollIntervalMs ?? 2000;
439042
+ const sandbox = await createAgentSandbox(agentId);
439043
+ const deviceId = sandbox.deviceId || `sandbox-${agentId}`;
439044
+ const deadline = Date.now() + timeoutMs;
439045
+ let lastEnvironment = null;
439046
+ let lastError = null;
439047
+ while (Date.now() < deadline) {
439048
+ try {
439049
+ const environment2 = await getEnvironmentConnection(deviceId);
439050
+ lastEnvironment = environment2;
439051
+ if (isEnvironmentOnline(environment2) && environment2.connectionId) {
439052
+ return { connectionId: environment2.connectionId, environment: environment2 };
439053
+ }
439054
+ } catch (error54) {
439055
+ lastError = error54;
439056
+ }
439057
+ await new Promise((resolve31) => setTimeout(resolve31, pollIntervalMs));
439058
+ }
439059
+ if (lastEnvironment) {
439060
+ throw new Error(`Timed out waiting for cloud sandbox ${sandbox.connectionName} to come online. Last status: ${describeEnvironment(lastEnvironment)}`);
439061
+ }
439062
+ throw new Error(`Timed out waiting for cloud sandbox ${sandbox.connectionName} to register${lastError instanceof Error ? `: ${lastError.message}` : ""}`);
439063
+ }
439064
+ var init_environments2 = __esm(() => {
439065
+ init_request();
439066
+ });
439067
+
438842
439068
  // src/cli/subcommands/cron-runner.ts
438843
439069
  function isLocalAgent(agentId) {
438844
439070
  return agentId.startsWith("agent-local-");
@@ -438881,7 +439107,7 @@ function validateTargetDevice(deviceId, environment2) {
438881
439107
  if (deviceId === SYNTHETIC_CLOUD_DEVICE_ID) {
438882
439108
  return {
438883
439109
  ok: false,
438884
- error: `"Cloud" is the default execution target, not a computer. Omit --computer to run in the agent's cloud sandbox.`
439110
+ error: `"Cloud" is not a computer. Pass --runner cloud to run in the agent's Cloud sandbox.`
438885
439111
  };
438886
439112
  }
438887
439113
  if (deviceId === SYNTHETIC_LOCAL_PLACEHOLDER_ID) {
@@ -438898,6 +439124,32 @@ function validateTargetDevice(deviceId, environment2) {
438898
439124
  }
438899
439125
  return { ok: true };
438900
439126
  }
439127
+ async function resolveInferredTargetDevice(deviceId, lookupEnvironment) {
439128
+ if (deviceId.startsWith("sandbox-")) {
439129
+ return { kind: "cloud-sandbox" };
439130
+ }
439131
+ const environment2 = await lookupEnvironment();
439132
+ const basicValidity = validateTargetDevice(deviceId, environment2);
439133
+ if (!basicValidity.ok) {
439134
+ return {
439135
+ kind: "local-fallback",
439136
+ reason: "this computer is not connected to your Letta account"
439137
+ };
439138
+ }
439139
+ if (!environment2) {
439140
+ return {
439141
+ kind: "local-fallback",
439142
+ reason: "this computer is not connected to your Letta account"
439143
+ };
439144
+ }
439145
+ if (!isEnvironmentOnline(environment2)) {
439146
+ return {
439147
+ kind: "local-fallback",
439148
+ reason: "this computer's connection to Letta is not currently online"
439149
+ };
439150
+ }
439151
+ return { kind: "device" };
439152
+ }
438901
439153
  function buildCloudScheduleInput(params) {
438902
439154
  const notes = [];
438903
439155
  let schedule;
@@ -438928,6 +439180,9 @@ function buildCloudScheduleInput(params) {
438928
439180
  };
438929
439181
  }
438930
439182
  var CLOUD_EXECUTION_TARGET = "cloud-sandbox", SYNTHETIC_CLOUD_DEVICE_ID = "__letta_cloud__", SYNTHETIC_LOCAL_PLACEHOLDER_ID = "local", CLOUD_CRON_UTC_NOTE = "Recurring Cloud schedules currently interpret cron expressions in UTC (timezone support is tracked in LET-9815).", CLOUD_DEVICE_FALLBACK_NOTE = "If the target computer is offline when the schedule fires, execution falls back to the agent's cloud sandbox.";
439183
+ var init_cron_runner = __esm(() => {
439184
+ init_environments2();
439185
+ });
438931
439186
 
438932
439187
  // src/cli/subcommands/cron-task-ref.ts
438933
439188
  async function ensureSettingsForCloud() {
@@ -438977,103 +439232,10 @@ function printAmbiguousTaskName(name, matches2) {
438977
439232
  var init_cron_task_ref = __esm(async () => {
438978
439233
  init_schedules2();
438979
439234
  init_backend_mode();
439235
+ init_cron_runner();
438980
439236
  await init_cron();
438981
439237
  });
438982
439238
 
438983
- // src/backend/api/environments.ts
438984
- var exports_environments2 = {};
438985
- __export(exports_environments2, {
438986
- sendEnvironmentMessage: () => sendEnvironmentMessage,
438987
- resolveEnvironmentConnectionId: () => resolveEnvironmentConnectionId,
438988
- resolveAgentSandboxConnectionId: () => resolveAgentSandboxConnectionId,
438989
- listEnvironments: () => listEnvironments,
438990
- isEnvironmentOnline: () => isEnvironmentOnline,
438991
- getEnvironmentConnection: () => getEnvironmentConnection,
438992
- describeEnvironment: () => describeEnvironment,
438993
- createAgentSandbox: () => createAgentSandbox
438994
- });
438995
- async function listEnvironments(options3 = {}) {
438996
- return apiRequest("GET", "/v1/environments", undefined, {
438997
- query: {
438998
- limit: options3.limit,
438999
- after: options3.after,
439000
- onlineOnly: options3.onlineOnly
439001
- }
439002
- });
439003
- }
439004
- async function sendEnvironmentMessage(connectionId, body3) {
439005
- return apiRequest("POST", `/v1/environments/${encodeURIComponent(connectionId)}/messages`, body3);
439006
- }
439007
- async function getEnvironmentConnection(deviceId) {
439008
- return apiRequest("GET", `/v1/environments/${encodeURIComponent(deviceId)}`);
439009
- }
439010
- async function createAgentSandbox(agentId) {
439011
- return apiRequest("POST", `/v1/agents/${encodeURIComponent(agentId)}/sandboxes`, {});
439012
- }
439013
- function isEnvironmentOnline(environment2) {
439014
- return typeof environment2.connectionId === "string" && environment2.connectionId.length > 0 && typeof environment2.lastHeartbeat === "number" && Date.now() - environment2.lastHeartbeat < 120000;
439015
- }
439016
- function describeEnvironment(environment2) {
439017
- const status = isEnvironmentOnline(environment2) ? "online" : "offline";
439018
- return `${environment2.connectionName} (${environment2.deviceId}, ${status})`;
439019
- }
439020
- async function resolveEnvironmentConnectionId(selector) {
439021
- const trimmed = selector.trim();
439022
- if (!trimmed) {
439023
- throw new Error("Environment selector must not be empty");
439024
- }
439025
- const response = await listEnvironments({ limit: 100 });
439026
- const matches2 = response.connections.filter((environment3) => {
439027
- return environment3.connectionId === trimmed || environment3.id === trimmed || environment3.deviceId === trimmed || environment3.connectionName === trimmed;
439028
- });
439029
- if (matches2.length === 0) {
439030
- throw new Error(`Environment "${trimmed}" not found. Run \`letta environments list\` to discover available environments.`);
439031
- }
439032
- const onlineMatches = matches2.filter(isEnvironmentOnline);
439033
- if (onlineMatches.length === 0) {
439034
- throw new Error(`Environment "${trimmed}" is offline. Matched: ${matches2.map(describeEnvironment).join(", ")}`);
439035
- }
439036
- if (onlineMatches.length > 1) {
439037
- throw new Error(`Environment "${trimmed}" is ambiguous. Matched: ${onlineMatches.map(describeEnvironment).join(", ")}`);
439038
- }
439039
- const environment2 = onlineMatches[0];
439040
- if (!environment2) {
439041
- throw new Error(`Environment "${trimmed}" is offline`);
439042
- }
439043
- if (!environment2.connectionId) {
439044
- throw new Error(`Environment "${trimmed}" has no active connection id`);
439045
- }
439046
- return { connectionId: environment2.connectionId, environment: environment2 };
439047
- }
439048
- async function resolveAgentSandboxConnectionId(agentId, options3 = {}) {
439049
- const timeoutMs = options3.timeoutMs ?? 3 * 60000;
439050
- const pollIntervalMs = options3.pollIntervalMs ?? 2000;
439051
- const sandbox = await createAgentSandbox(agentId);
439052
- const deviceId = sandbox.deviceId || `sandbox-${agentId}`;
439053
- const deadline = Date.now() + timeoutMs;
439054
- let lastEnvironment = null;
439055
- let lastError = null;
439056
- while (Date.now() < deadline) {
439057
- try {
439058
- const environment2 = await getEnvironmentConnection(deviceId);
439059
- lastEnvironment = environment2;
439060
- if (isEnvironmentOnline(environment2) && environment2.connectionId) {
439061
- return { connectionId: environment2.connectionId, environment: environment2 };
439062
- }
439063
- } catch (error54) {
439064
- lastError = error54;
439065
- }
439066
- await new Promise((resolve31) => setTimeout(resolve31, pollIntervalMs));
439067
- }
439068
- if (lastEnvironment) {
439069
- throw new Error(`Timed out waiting for cloud sandbox ${sandbox.connectionName} to come online. Last status: ${describeEnvironment(lastEnvironment)}`);
439070
- }
439071
- throw new Error(`Timed out waiting for cloud sandbox ${sandbox.connectionName} to register${lastError instanceof Error ? `: ${lastError.message}` : ""}`);
439072
- }
439073
- var init_environments2 = __esm(() => {
439074
- init_request();
439075
- });
439076
-
439077
439239
  // src/cli/subcommands/cron.ts
439078
439240
  import { parseArgs as parseArgs5 } from "node:util";
439079
439241
  function printUsage4() {
@@ -439096,18 +439258,25 @@ Add options:
439096
439258
  --cron <expr> Raw 5-field cron expression
439097
439259
  --agent <id> Agent ID (defaults to LETTA_AGENT_ID)
439098
439260
  --conversation <id> Conversation ID (defaults to LETTA_CONVERSATION_ID or "default")
439099
- --runner <runner> Where the schedule lives and fires:
439100
- cloud - durable Cloud schedule; executes in the
439101
- agent's managed cloud sandbox (default for
439102
- cloud agents)
439261
+ --runner <runner> Where the schedule lives and fires (normally omit:
439262
+ the default keeps the schedule running where it was
439263
+ created):
439264
+ cloud - durable Cloud schedule (default for cloud
439265
+ agents on Cloud-reachable runtimes:
439266
+ external listeners are targeted, managed
439267
+ sandboxes stay untargeted). Explicit
439268
+ --runner cloud always uses the Cloud sandbox
439103
439269
  local - this device's scheduler (~/.letta/crons.json);
439104
439270
  only fires while a session runs here (default
439105
- for local-backend agents / self-hosted)
439106
- --computer <id> (cloud runner only) Execute on one of your
439107
- connected computers (deviceId from
439108
- \`letta environments list\`) instead of the agent's
439109
- cloud sandbox. Falls back to the sandbox if the
439110
- computer is offline at fire time.
439271
+ for local-backend agents / self-hosted, and
439272
+ the fallback when Cloud scheduling cannot
439273
+ reach this computer)
439274
+ --computer <id> (cloud runner only) Override execution with a
439275
+ connected external environment (deviceId from
439276
+ \`letta environments list\`). Falls back to the Cloud
439277
+ sandbox if the computer is offline at fire time.
439278
+ Managed sandboxes and Desktop-local connections are
439279
+ not currently valid Cloud schedule targets.
439111
439280
 
439112
439281
  List/filter options:
439113
439282
  --agent <id> Filter by agent ID
@@ -439270,13 +439439,15 @@ async function handleAdd(values2) {
439270
439439
  console.error("Error: no schedule specified.");
439271
439440
  return 1;
439272
439441
  }
439273
- const targetDeviceId = values2.computer?.trim() || undefined;
439442
+ let targetDeviceId = values2.computer?.trim() || undefined;
439274
439443
  const resolved = await getRunnerForAgent(values2.runner, agentId);
439275
439444
  if ("error" in resolved) {
439276
439445
  console.error(`Error: ${resolved.error}`);
439277
439446
  return 1;
439278
439447
  }
439279
- if (targetDeviceId && resolved.runner !== "cloud") {
439448
+ let runner = resolved.runner;
439449
+ let localFallbackNote;
439450
+ if (targetDeviceId && runner !== "cloud") {
439280
439451
  console.error("Error: --computer requires the cloud runner. Run `letta cron add` on the target computer itself (with --runner local) to schedule there locally.");
439281
439452
  return 1;
439282
439453
  }
@@ -439286,8 +439457,17 @@ async function handleAdd(values2) {
439286
439457
  console.error(`Error: ${validity.error}`);
439287
439458
  return 1;
439288
439459
  }
439460
+ } else if (runner === "cloud" && values2.runner !== "cloud") {
439461
+ const inferredDeviceId = getRuntimeEnvironmentDeviceId();
439462
+ const resolution = await resolveInferredTargetDevice(inferredDeviceId, () => lookupEnvironmentForTarget(inferredDeviceId));
439463
+ if (resolution.kind === "device") {
439464
+ targetDeviceId = inferredDeviceId;
439465
+ } else if (resolution.kind === "local-fallback") {
439466
+ runner = "local";
439467
+ localFallbackNote = `This schedule is local to this computer (${resolution.reason}): it only fires while a Letta session is running here. For a schedule that fires regardless, pass --runner cloud (runs in the agent's cloud sandbox) or --computer <deviceId> (runs on a connected computer, from \`letta environments list\`).`;
439468
+ }
439289
439469
  }
439290
- if (resolved.runner === "cloud") {
439470
+ if (runner === "cloud") {
439291
439471
  return handleCloudAdd({
439292
439472
  agentId,
439293
439473
  conversationId,
@@ -439331,8 +439511,10 @@ async function handleAdd(values2) {
439331
439511
  if (note) {
439332
439512
  output.note = note;
439333
439513
  }
439334
- if (result.warning) {
439335
- output.warning = result.warning;
439514
+ const fallbackWarning = localFallbackNote && recurring ? `${localFallbackNote} Recurring schedules on an unregistered computer stop firing whenever no session is running — strongly consider a durable alternative.` : localFallbackNote;
439515
+ const warnings = [fallbackWarning, result.warning].filter(Boolean);
439516
+ if (warnings.length > 0) {
439517
+ output.warning = warnings.join(" ");
439336
439518
  }
439337
439519
  console.log(JSON.stringify(output, null, 2));
439338
439520
  console.error("Created local schedule: it only fires while a Letta session is running on this device.");
@@ -439694,9 +439876,11 @@ async function runCronSubcommand(argv) {
439694
439876
  }
439695
439877
  var CRON_OPTIONS;
439696
439878
  var init_cron2 = __esm(async () => {
439879
+ init_client2();
439697
439880
  init_request();
439698
439881
  init_schedules2();
439699
439882
  init_backend_mode();
439883
+ init_cron_runner();
439700
439884
  await __promiseAll([
439701
439885
  init_cron(),
439702
439886
  init_cron_task_ref()
@@ -448016,6 +448200,9 @@ function rememberPendingApprovalBatchIds(runtime, pendingApprovals, batchId) {
448016
448200
  for (const approval of pendingApprovals) {
448017
448201
  if (approval.toolCallId) {
448018
448202
  runtime.pendingApprovalBatchByToolCallId.set(approval.toolCallId, batchId);
448203
+ if (approval.messageId) {
448204
+ runtime.approvalMessageIdByToolCallId.set(approval.toolCallId, approval.messageId);
448205
+ }
448019
448206
  }
448020
448207
  }
448021
448208
  }
@@ -448042,6 +448229,7 @@ function resolveRecoveryBatchId(runtime, pendingApprovals) {
448042
448229
  function clearPendingApprovalBatchIds(runtime, approvals) {
448043
448230
  for (const approval of approvals) {
448044
448231
  runtime.pendingApprovalBatchByToolCallId.delete(approval.toolCallId);
448232
+ runtime.approvalMessageIdByToolCallId.delete(approval.toolCallId);
448045
448233
  }
448046
448234
  }
448047
448235
  function isValidApprovalResponseBody(value) {
@@ -448335,13 +448523,13 @@ var init_channel_service_validation = __esm(() => {
448335
448523
  function isExperimentId(value) {
448336
448524
  return typeof value === "string" && EXPERIMENT_IDS.has(value);
448337
448525
  }
448338
- function isStringArray6(value) {
448526
+ function isStringArray7(value) {
448339
448527
  return Array.isArray(value) && value.every((item) => typeof item === "string");
448340
448528
  }
448341
448529
  function isClientToolsetConfig(value) {
448342
448530
  if (!isObjectRecord2(value))
448343
448531
  return false;
448344
- return (value.base === undefined || typeof value.base === "string" && TOOLSET_PREFERENCES.has(value.base)) && (value.include === undefined || isStringArray6(value.include));
448532
+ return (value.base === undefined || typeof value.base === "string" && TOOLSET_PREFERENCES.has(value.base)) && (value.include === undefined || isStringArray7(value.include));
448345
448533
  }
448346
448534
  function isStringRecord2(value) {
448347
448535
  return !!value && typeof value === "object" && !Array.isArray(value) && Object.values(value).every((item) => typeof item === "string");
@@ -448372,7 +448560,7 @@ function isInputCommand(value) {
448372
448560
  }
448373
448561
  const payload = candidate.payload;
448374
448562
  if (payload.kind === "create_message") {
448375
- return Array.isArray(payload.messages) && (payload.image_failure_mode === undefined || payload.image_failure_mode === "strict" || payload.image_failure_mode === "drop") && (payload.client_tool_allowlist === undefined || isStringArray6(payload.client_tool_allowlist)) && (payload.client_toolset === undefined || isClientToolsetConfig(payload.client_toolset)) && (payload.external_tool_scope_ids === undefined || isStringArray6(payload.external_tool_scope_ids)) && (payload.exclude_interactive_tools === undefined || typeof payload.exclude_interactive_tools === "boolean");
448563
+ return Array.isArray(payload.messages) && (payload.image_failure_mode === undefined || payload.image_failure_mode === "strict" || payload.image_failure_mode === "drop") && (payload.client_tool_allowlist === undefined || isStringArray7(payload.client_tool_allowlist)) && (payload.client_toolset === undefined || isClientToolsetConfig(payload.client_toolset)) && (payload.external_tool_scope_ids === undefined || isStringArray7(payload.external_tool_scope_ids)) && (payload.exclude_interactive_tools === undefined || typeof payload.exclude_interactive_tools === "boolean");
448376
448564
  }
448377
448565
  if (payload.kind === "approval_response") {
448378
448566
  return isValidApprovalResponseBody(payload);
@@ -448397,9 +448585,9 @@ function legacyEnvironmentMessageToInputCommand(value) {
448397
448585
  payload: {
448398
448586
  kind: "create_message",
448399
448587
  messages: candidate.messages,
448400
- client_tool_allowlist: isStringArray6(candidate.clientToolAllowlist) ? candidate.clientToolAllowlist : undefined,
448588
+ client_tool_allowlist: isStringArray7(candidate.clientToolAllowlist) ? candidate.clientToolAllowlist : undefined,
448401
448589
  client_toolset: isClientToolsetConfig(candidate.clientToolset) ? candidate.clientToolset : undefined,
448402
- external_tool_scope_ids: isStringArray6(candidate.externalToolScopeIds) ? candidate.externalToolScopeIds : undefined
448590
+ external_tool_scope_ids: isStringArray7(candidate.externalToolScopeIds) ? candidate.externalToolScopeIds : undefined
448403
448591
  }
448404
448592
  };
448405
448593
  }
@@ -448431,7 +448619,7 @@ function getInvalidInputReason(value) {
448431
448619
  reason: "Protocol violation: input.payload.image_failure_mode must be strict or drop"
448432
448620
  };
448433
448621
  }
448434
- if (payload.client_tool_allowlist !== undefined && !isStringArray6(payload.client_tool_allowlist)) {
448622
+ if (payload.client_tool_allowlist !== undefined && !isStringArray7(payload.client_tool_allowlist)) {
448435
448623
  return {
448436
448624
  runtime: candidate.runtime,
448437
448625
  reason: "Protocol violation: input.payload.client_tool_allowlist must be string[]"
@@ -448449,7 +448637,7 @@ function getInvalidInputReason(value) {
448449
448637
  reason: "Protocol violation: input.payload.exclude_interactive_tools must be boolean"
448450
448638
  };
448451
448639
  }
448452
- if (payload.external_tool_scope_ids !== undefined && !isStringArray6(payload.external_tool_scope_ids)) {
448640
+ if (payload.external_tool_scope_ids !== undefined && !isStringArray7(payload.external_tool_scope_ids)) {
448453
448641
  return {
448454
448642
  runtime: candidate.runtime,
448455
448643
  reason: "Protocol violation: input.payload.external_tool_scope_ids must be string[]"
@@ -448811,7 +448999,7 @@ function isCreateAgentCommand(value) {
448811
448999
  if (!value || typeof value !== "object")
448812
449000
  return false;
448813
449001
  const c = value;
448814
- return c.type === "create_agent" && typeof c.request_id === "string" && (c.personality === "memo" || c.personality === "blank" || c.personality === "tutorial" || c.personality === "linus" || c.personality === "kawaii") && (c.model === undefined || typeof c.model === "string") && (c.tags === undefined || isStringArray6(c.tags)) && (c.pin_global === undefined || typeof c.pin_global === "boolean");
449002
+ return c.type === "create_agent" && typeof c.request_id === "string" && (c.personality === "memo" || c.personality === "blank" || c.personality === "tutorial" || c.personality === "linus" || c.personality === "kawaii") && (c.model === undefined || typeof c.model === "string") && (c.tags === undefined || isStringArray7(c.tags)) && (c.pin_global === undefined || typeof c.pin_global === "boolean");
448815
449003
  }
448816
449004
  function isAgentListCommand(value) {
448817
449005
  if (!value || typeof value !== "object")
@@ -451068,6 +451256,7 @@ async function applyModelUpdateForRuntime(params) {
451068
451256
  conversationId,
451069
451257
  overrideModel: model.handle,
451070
451258
  overrideProviderType: providerTypeFromModelSettings3(modelSettings) ?? inferProviderTypeFromRegistryHandle(model.handle) ?? null,
451259
+ modContext: createListenerAgentModContext(agentId),
451071
451260
  modAdapters,
451072
451261
  modEvents: createListenerModEvents(modAdapters)
451073
451262
  });
@@ -451140,6 +451329,7 @@ async function applyToolsetUpdateForRuntime(params) {
451140
451329
  const preparedToolContext = await prepareToolExecutionContextForScope({
451141
451330
  agentId,
451142
451331
  conversationId,
451332
+ modContext: createListenerAgentModContext(agentId),
451143
451333
  modAdapters,
451144
451334
  modEvents: createListenerModEvents(modAdapters)
451145
451335
  });
@@ -452274,7 +452464,7 @@ function emitInterruptToolReturnMessage(socket, runtime, approvals, runId, uuidP
452274
452464
  emitCanonicalMessageDelta(socket, runtime, {
452275
452465
  type: "message",
452276
452466
  message_type: "tool_return_message",
452277
- id: `message-${uuidPrefix}-${crypto.randomUUID()}`,
452467
+ id: `synthetic-${uuidPrefix}-${crypto.randomUUID()}`,
452278
452468
  date: new Date().toISOString(),
452279
452469
  run_id: resolvedRunId,
452280
452470
  status: toolReturn.status,
@@ -452298,8 +452488,10 @@ function emitInterruptToolReturnMessage(socket, runtime, approvals, runId, uuidP
452298
452488
  function emitToolExecutionStartedEvents(socket, runtime, params) {
452299
452489
  const toolCalls = params.toolCalls ?? (params.toolCallIds ?? []).map((toolCallId) => ({ toolCallId }));
452300
452490
  for (const toolCall of toolCalls) {
452491
+ const messageId2 = runtime.approvalMessageIdByToolCallId.get(toolCall.toolCallId);
452301
452492
  const delta2 = {
452302
452493
  ...createLifecycleMessageBase("client_tool_start", params.runId),
452494
+ ...messageId2 ? { id: messageId2 } : {},
452303
452495
  tool_call_id: toolCall.toolCallId,
452304
452496
  ...toolCall.toolName ? { tool_name: toolCall.toolName } : {},
452305
452497
  ...toolCall.toolArgs ? { tool_args: toolCall.toolArgs } : {}
@@ -452313,8 +452505,10 @@ function emitToolExecutionStartedEvents(socket, runtime, params) {
452313
452505
  function emitToolExecutionFinishedEvents(socket, runtime, params) {
452314
452506
  const toolReturns = extractInterruptToolReturns(params.approvals);
452315
452507
  for (const toolReturn of toolReturns) {
452508
+ const messageId2 = runtime.approvalMessageIdByToolCallId.get(toolReturn.tool_call_id);
452316
452509
  const delta2 = {
452317
452510
  ...createLifecycleMessageBase("client_tool_end", params.runId),
452511
+ ...messageId2 ? { id: messageId2 } : {},
452318
452512
  tool_call_id: toolReturn.tool_call_id,
452319
452513
  status: toolReturn.status
452320
452514
  };
@@ -452326,8 +452520,10 @@ function emitToolExecutionFinishedEvents(socket, runtime, params) {
452326
452520
  }
452327
452521
  function emitToolExecutionAbortedEvents(socket, runtime, params) {
452328
452522
  for (const toolCallId of params.toolCallIds) {
452523
+ const messageId2 = runtime.approvalMessageIdByToolCallId.get(toolCallId);
452329
452524
  const delta2 = {
452330
452525
  ...createLifecycleMessageBase("client_tool_end", params.runId),
452526
+ ...messageId2 ? { id: messageId2 } : {},
452331
452527
  tool_call_id: toolCallId,
452332
452528
  status: "error"
452333
452529
  };
@@ -452387,7 +452583,7 @@ function createToolExecutionOutputEmitter(socket, runtime, params) {
452387
452583
  }
452388
452584
  const existing = outputByToolCallId.get(toolCallId);
452389
452585
  const outputState = existing ?? {
452390
- messageId: `message-tool-return-stream-${toolCallId}`,
452586
+ messageId: `synthetic-tool-return-stream-${toolCallId}`,
452391
452587
  stdout: "",
452392
452588
  stderr: "",
452393
452589
  dirty: false,
@@ -452655,13 +452851,24 @@ function getLoopErrorNoticeDecision(params) {
452655
452851
  apiError
452656
452852
  };
452657
452853
  }
452658
- const formattedMessage = formatErrorDetails2(apiError ? buildStructuredFormatInput(apiError) : params.error ?? params.message, params.agentId ?? undefined, params.conversationId ?? undefined);
452854
+ const formattedMessage = formatErrorDetails2(apiError ? buildStructuredFormatInput(apiError) : params.error ?? params.message, params.agentId ?? undefined, params.conversationId ?? undefined, {
452855
+ surface: params.surface,
452856
+ unclassifiedFallback: params.unclassifiedFallback
452857
+ });
452659
452858
  return {
452660
452859
  visibility: "transcript",
452661
452860
  message: formattedMessage,
452662
452861
  apiError
452663
452862
  };
452664
452863
  }
452864
+ function getTranscriptLoopErrorMessage(params) {
452865
+ const decision = getLoopErrorNoticeDecision({
452866
+ ...params,
452867
+ surface: "plain",
452868
+ unclassifiedFallback: "generic"
452869
+ });
452870
+ return decision.visibility === "transcript" ? decision.message : undefined;
452871
+ }
452665
452872
  function emitLoopErrorNotice(socket, runtime, params) {
452666
452873
  const decision = getLoopErrorNoticeDecision(params);
452667
452874
  if (decision.visibility === "debug_only") {
@@ -454249,6 +454456,9 @@ class StreamProcessor {
454249
454456
  toolName: "",
454250
454457
  toolArgs: ""
454251
454458
  };
454459
+ if ("id" in chunk && typeof chunk.id === "string" && chunk.id) {
454460
+ existing.messageId = chunk.id;
454461
+ }
454252
454462
  if (toolCall.name) {
454253
454463
  existing.toolName = toolCall.name;
454254
454464
  }
@@ -454268,7 +454478,8 @@ class StreamProcessor {
454268
454478
  return Array.from(this.pendingApprovals.values()).map((a2) => ({
454269
454479
  toolCallId: a2.toolCallId,
454270
454480
  toolName: a2.toolName,
454271
- toolArgs: a2.toolArgs
454481
+ toolArgs: a2.toolArgs,
454482
+ ...a2.messageId ? { messageId: a2.messageId } : {}
454272
454483
  }));
454273
454484
  }
454274
454485
  }
@@ -454545,7 +454756,8 @@ async function drainStream(stream12, buffers, refresh, abortSignal, onFirstMessa
454545
454756
  const approvals = allPending.map((a2) => ({
454546
454757
  toolCallId: a2.toolCallId,
454547
454758
  toolName: a2.toolName || "",
454548
- toolArgs: a2.toolArgs || ""
454759
+ toolArgs: a2.toolArgs || "",
454760
+ ...a2.messageId ? { messageId: a2.messageId } : {}
454549
454761
  }));
454550
454762
  const approval = approvals[0] || null;
454551
454763
  streamProcessor.pendingApprovals.clear();
@@ -455231,23 +455443,27 @@ function finalizeHandledRecoveryTurn(runtime, socket, turnLease, params) {
455231
455443
  }
455232
455444
  const terminalStopReason = params.drainResult.stopReason || "error";
455233
455445
  const runId = runtime.activeRunId;
455446
+ const noticeParams = {
455447
+ message: `Recovery continuation ended unexpectedly: ${terminalStopReason}`,
455448
+ agentId: params.agentId,
455449
+ conversationId: params.conversationId
455450
+ };
455234
455451
  const transition = finishListenerTurn(runtime, turnLease, {
455235
455452
  stopReason: terminalStopReason,
455236
455453
  socket,
455237
455454
  agentId: params.agentId,
455238
455455
  conversationId: params.conversationId,
455239
- turnId: params.turnId
455456
+ turnId: params.turnId,
455457
+ error: getTranscriptLoopErrorMessage(noticeParams)
455240
455458
  });
455241
455459
  if (!transition.finished) {
455242
455460
  return transition;
455243
455461
  }
455244
455462
  emitLoopErrorNotice(socket, runtime, {
455245
- message: `Recovery continuation ended unexpectedly: ${terminalStopReason}`,
455463
+ ...noticeParams,
455246
455464
  stopReason: terminalStopReason,
455247
455465
  isTerminal: true,
455248
- runId: runId || undefined,
455249
- agentId: params.agentId ?? undefined,
455250
- conversationId: params.conversationId
455466
+ runId: runId || undefined
455251
455467
  });
455252
455468
  return transition;
455253
455469
  }
@@ -455508,6 +455724,7 @@ async function resolveRecoveredApprovalResponse(runtime, socket, response, proce
455508
455724
  conversationId: recovered.conversationId,
455509
455725
  workingDirectory,
455510
455726
  permissionModeState: getOrCreateConversationPermissionModeStateRef(runtime.listener, recovered.agentId, recovered.conversationId),
455727
+ modContext: createListenerAgentModContext(recovered.agentId),
455511
455728
  modAdapters,
455512
455729
  modEvents: createListenerModEvents(modAdapters)
455513
455730
  });
@@ -455603,13 +455820,17 @@ async function resolveRecoveredApprovalResponse(runtime, socket, response, proce
455603
455820
  recovered.pendingRequestIds = new Set(recovered.approvalsByRequestId.keys());
455604
455821
  recovered.responsesByRequestId.clear();
455605
455822
  }
455823
+ const stopReason = recoveryLease.signal.aborted ? "cancelled" : "error";
455606
455824
  finishListenerTurn(runtime, recoveryLease, {
455607
- stopReason: recoveryLease.signal.aborted ? "cancelled" : "error",
455825
+ stopReason,
455608
455826
  socket,
455609
455827
  agentId: recovered.agentId,
455610
455828
  conversationId: recovered.conversationId,
455611
455829
  turnId: `batch-recovered-${requestId}`,
455612
- error: error54 instanceof Error ? error54.message : String(error54)
455830
+ error: stopReason === "error" ? getTranscriptLoopErrorMessage({
455831
+ error: error54,
455832
+ message: error54 instanceof Error ? error54.message : String(error54)
455833
+ }) : undefined
455613
455834
  });
455614
455835
  throw error54;
455615
455836
  }
@@ -455917,6 +456138,7 @@ async function resolveStaleApprovals(runtime, socket, turnLease, deps = {}) {
455917
456138
  conversationId: recoveryConversationId,
455918
456139
  workingDirectory: recoveryWorkingDirectory,
455919
456140
  permissionModeState: getOrCreateConversationPermissionModeStateRef(runtime.listener, runtime.agentId, runtime.conversationId),
456141
+ modContext: createListenerAgentModContext(runtime.agentId),
455920
456142
  modAdapters,
455921
456143
  modEvents: createListenerModEvents(modAdapters)
455922
456144
  });
@@ -459107,7 +459329,7 @@ async function emitListenerTurnStart(options3) {
459107
459329
  sessionId: options3.conversationId,
459108
459330
  workingDirectory: options3.workingDirectory,
459109
459331
  permissionMode: options3.permissionMode ?? null,
459110
- agent: options3.cachedAgent ?? null
459332
+ agent: options3.cachedAgent ?? { id: options3.agentId }
459111
459333
  });
459112
459334
  const event2 = {
459113
459335
  agentId: options3.agentId,
@@ -459134,7 +459356,7 @@ async function emitListenerTurnEnd(options3) {
459134
459356
  sessionId: options3.conversationId,
459135
459357
  workingDirectory: options3.workingDirectory,
459136
459358
  permissionMode: options3.permissionMode ?? null,
459137
- agent: options3.cachedAgent ?? null
459359
+ agent: options3.cachedAgent ?? { id: options3.agentId }
459138
459360
  });
459139
459361
  const event2 = {
459140
459362
  agentId: options3.agentId,
@@ -460227,8 +460449,10 @@ async function prepareListenerTurn(params) {
460227
460449
  inboundUserTranscriptLines = buildInboundUserTranscriptLines(currentInput);
460228
460450
  }
460229
460451
  const modAdapters = await ensureListenerModAdaptersForAgent(runtime.listener, agentId);
460452
+ const environmentDeviceId = connectionId ? runtime.listener.connections.get(connectionId)?.options.deviceId : undefined;
460230
460453
  const preparedToolContext = await prepareToolExecutionContextForScope({
460231
460454
  connectionId,
460455
+ environmentDeviceId,
460232
460456
  agentId,
460233
460457
  conversationId,
460234
460458
  clientToolset: msg.clientToolset,
@@ -460239,6 +460463,7 @@ async function prepareListenerTurn(params) {
460239
460463
  permissionModeState,
460240
460464
  skillSources: runtime.skillSources,
460241
460465
  cachedAgent,
460466
+ modContext: createListenerAgentModContext(agentId),
460242
460467
  modAdapters,
460243
460468
  modEvents: createListenerModEvents(modAdapters)
460244
460469
  });
@@ -460359,7 +460584,8 @@ async function handleIncomingMessageInner(msg, socket, runtime, onStatusChange,
460359
460584
  if (!agentId) {
460360
460585
  finishTurn({
460361
460586
  stopReason: "error",
460362
- conversationId
460587
+ conversationId,
460588
+ error: getTranscriptLoopErrorMessage({ message: "Missing agent ID" })
460363
460589
  });
460364
460590
  return;
460365
460591
  }
@@ -460732,24 +460958,29 @@ async function handleIncomingMessageInner(msg, socket, runtime, onStatusChange,
460732
460958
  }
460733
460959
  const errorMessage2 = errorDetail2 || `Unexpected stop reason: ${stopReason}`;
460734
460960
  const terminalRunId = runId || runtime.activeRunId || runErrorInfo2?.run_id;
460961
+ const noticeParams = {
460962
+ message: errorMessage2,
460963
+ agentId,
460964
+ conversationId,
460965
+ runErrorInfo: runErrorInfo2 ?? undefined,
460966
+ cancelRequested: turnAbortSignal.aborted,
460967
+ abortSignal: turnAbortSignal
460968
+ };
460969
+ const terminalError = getTranscriptLoopErrorMessage(noticeParams);
460735
460970
  const transition = finishTurn({
460736
460971
  stopReason: effectiveStopReason,
460737
460972
  agentId,
460738
- conversationId
460973
+ conversationId,
460974
+ error: terminalError
460739
460975
  });
460740
460976
  if (!transition.finished) {
460741
460977
  break;
460742
460978
  }
460743
460979
  const formattedError = emitLoopErrorNotice(socket, runtime, {
460744
- message: errorMessage2,
460980
+ ...noticeParams,
460745
460981
  stopReason: effectiveStopReason,
460746
460982
  isTerminal: true,
460747
- runId: terminalRunId,
460748
- agentId,
460749
- conversationId,
460750
- runErrorInfo: runErrorInfo2 ?? undefined,
460751
- cancelRequested: turnAbortSignal.aborted,
460752
- abortSignal: turnAbortSignal
460983
+ runId: terminalRunId
460753
460984
  });
460754
460985
  runtime.lastTerminalLoopErrorMessage = formattedError ?? errorMessage2;
460755
460986
  runtime.lastTerminalLoopErrorRunId = terminalRunId ?? null;
@@ -460776,10 +461007,14 @@ async function handleIncomingMessageInner(msg, socket, runtime, onStatusChange,
460776
461007
  });
460777
461008
  if (approvalResult.kind === "error") {
460778
461009
  const terminalRunId = runId || runtime.activeRunId;
461010
+ const terminalError = getTranscriptLoopErrorMessage({
461011
+ message: approvalResult.message
461012
+ });
460779
461013
  const transition = finishTurn({
460780
461014
  stopReason: "error",
460781
461015
  agentId,
460782
- conversationId
461016
+ conversationId,
461017
+ error: terminalError
460783
461018
  });
460784
461019
  if (!transition.finished) {
460785
461020
  return;
@@ -460877,24 +461112,29 @@ async function handleIncomingMessageInner(msg, socket, runtime, onStatusChange,
460877
461112
  }
460878
461113
  const errorMessage2 = error54 instanceof Error ? error54.message : String(error54);
460879
461114
  const terminalRunId = runtime.activeRunId;
461115
+ const noticeParams = {
461116
+ message: errorMessage2,
461117
+ agentId,
461118
+ conversationId,
461119
+ error: error54,
461120
+ cancelRequested: turnAbortSignal.aborted,
461121
+ abortSignal: turnAbortSignal
461122
+ };
461123
+ const terminalError = getTranscriptLoopErrorMessage(noticeParams);
460880
461124
  const transition = finishTurn({
460881
461125
  stopReason: "error",
460882
461126
  agentId: agentId || null,
460883
- conversationId
461127
+ conversationId,
461128
+ error: terminalError
460884
461129
  });
460885
461130
  if (!transition.finished) {
460886
461131
  return;
460887
461132
  }
460888
461133
  const formattedError = emitLoopErrorNotice(socket, runtime, {
460889
- message: errorMessage2,
461134
+ ...noticeParams,
460890
461135
  stopReason: "error",
460891
461136
  isTerminal: true,
460892
- runId: terminalRunId,
460893
- agentId: agentId || undefined,
460894
- conversationId,
460895
- error: error54,
460896
- cancelRequested: turnAbortSignal.aborted,
460897
- abortSignal: turnAbortSignal
461137
+ runId: terminalRunId
460898
461138
  });
460899
461139
  runtime.lastTerminalLoopErrorMessage = formattedError ?? errorMessage2;
460900
461140
  runtime.lastTerminalLoopErrorRunId = terminalRunId ?? null;
@@ -460914,7 +461154,8 @@ async function handleIncomingMessageInner(msg, socket, runtime, onStatusChange,
460914
461154
  socket,
460915
461155
  runId: runtime.activeRunId || msgRunIds[msgRunIds.length - 1],
460916
461156
  agentId: agentId || null,
460917
- conversationId
461157
+ conversationId,
461158
+ error: turnAbortSignal.aborted ? undefined : getTranscriptLoopErrorMessage({ message: "Unexpected turn failure" })
460918
461159
  });
460919
461160
  }
460920
461161
  if (runtime.activeConnectionId === connectionId) {
@@ -466115,6 +466356,7 @@ async function startConnectedListenerRuntime(runtime, transport, opts, processQu
466115
466356
  if (runtime !== getActiveRuntime() || runtime.intentionallyClosed) {
466116
466357
  return;
466117
466358
  }
466359
+ installExternalToolBridge(runtime);
466118
466360
  const shouldStartHeartbeat = options3.startHeartbeat !== false;
466119
466361
  const cronSchedulerDisabledByEnv = process.env.LETTA_DISABLE_CRON_SCHEDULER === "1";
466120
466362
  const shouldStartCronScheduler = options3.startCronScheduler !== false && !cronSchedulerDisabledByEnv;
@@ -469394,6 +469636,9 @@ async function resolveListenerStartupMode(channelNames, channelsRequested = chan
469394
469636
  if (channelsRequested) {
469395
469637
  return { kind: "local-channels", serverUrl, backend: "self-hosted" };
469396
469638
  }
469639
+ if (process.env.IGNORE_SELF_HOSTED_LISTENER_ERROR === "1") {
469640
+ return { kind: "remote", serverUrl };
469641
+ }
469397
469642
  return { kind: "unsupported-self-hosted", serverUrl };
469398
469643
  }
469399
469644
  function getScopedChannelRestoreAgentScope() {
@@ -469542,7 +469787,7 @@ async function runListenSubcommand(argv) {
469542
469787
  try {
469543
469788
  const deviceId = settingsManager.getOrCreateDeviceId();
469544
469789
  const startupMode = await resolveListenerStartupMode(channelNames, channelNames.length > 0 || restoreEnabledChannels);
469545
- if (startupMode.kind === "unsupported-self-hosted" && process.env.IGNORE_SELF_HOSTED_LISTENER_ERROR !== "1") {
469790
+ if (startupMode.kind === "unsupported-self-hosted") {
469546
469791
  console.error(`Self-hosted listener registration is not available for ${startupMode.serverUrl}.`);
469547
469792
  console.error("Start with --channels to run local channel adapters, or unset LETTA_BASE_URL to use Letta API remote environments.");
469548
469793
  await flushListenerTelemetryEnd("listener_self_hosted_no_channels");
@@ -491886,7 +492131,7 @@ var init_mcp_client = __esm(() => {
491886
492131
  init_streamableHttp();
491887
492132
  DEFAULT_CLIENT_INFO = {
491888
492133
  name: "letta-code",
491889
- version: "0.30.5"
492134
+ version: "0.30.7"
491890
492135
  };
491891
492136
  });
491892
492137
 
@@ -523799,6 +524044,7 @@ function formatConnectUsage() {
523799
524044
  " /connect codex",
523800
524045
  " /connect anthropic <api_key>",
523801
524046
  " /connect openai <api_key>",
524047
+ " /connect openai-compatible --base-url http://localhost:8000/v1 [--api-key <api_key>]",
523802
524048
  " /connect lmstudio --base-url http://127.0.0.1:1234/v1 --timeout 600s",
523803
524049
  " /connect bedrock iam --access-key <id> --secret-key <key> --region <region>",
523804
524050
  " /connect bedrock profile --profile <name> --region <region>"
@@ -523876,7 +524122,7 @@ function formatApiKeyUsage(provider) {
523876
524122
  `Usage: /connect ${provider.canonical} [api_key]`,
523877
524123
  "",
523878
524124
  `Connect to ${provider.byokProvider.displayName}. API key is optional for this local provider.`,
523879
- "Optional: --base-url <url> --timeout <ms|duration|false>"
524125
+ isConnectBaseURLRequired(provider) ? "Required: --base-url <url>. Optional: --timeout <ms|duration|false>" : "Optional: --base-url <url> --timeout <ms|duration|false>"
523880
524126
  ].join(`
523881
524127
  `);
523882
524128
  }
@@ -524172,6 +524418,12 @@ Usage: /connect chatgpt [--name <provider-name>]`, false);
524172
524418
  if (parsed.error) {
524173
524419
  addCommandResult(ctx.buffersRef, ctx.refreshDerived, msg, `${parsed.error}
524174
524420
 
524421
+ ${formatApiKeyUsage(provider)}`, false);
524422
+ return;
524423
+ }
524424
+ if (isConnectBaseURLRequired(provider) && !parsed.baseURL?.trim()) {
524425
+ addCommandResult(ctx.buffersRef, ctx.refreshDerived, msg, `Missing required field: --base-url <url>.
524426
+
524175
524427
  ${formatApiKeyUsage(provider)}`, false);
524176
524428
  return;
524177
524429
  }
@@ -555619,9 +555871,8 @@ Note: Flags should use double dashes for full names (e.g., --yolo, not -yolo)`);
555619
555871
  const command = positionals[2];
555620
555872
  if (values2.help) {
555621
555873
  printHelp();
555622
- const helpDelayMs = Number.parseInt(process.env.LETTA_TEST_HELP_EXIT_DELAY_MS ?? "", 10);
555623
- if (Number.isFinite(helpDelayMs) && helpDelayMs > 0) {
555624
- await new Promise((resolve40) => setTimeout(resolve40, helpDelayMs));
555874
+ if (process.env.LETTA_TEST_WAIT_FOR_STARTUP_AUTO_UPDATE === "1") {
555875
+ await autoUpdatePromise;
555625
555876
  }
555626
555877
  process.exit(0);
555627
555878
  }
@@ -558987,4 +559238,4 @@ function registerBunOAuthFlows() {
558987
559238
  registerBunOAuthFlows();
558988
559239
  await init_src5().then(() => exports_src2);
558989
559240
 
558990
- //# debugId=AF6DD74653AB022F64756E2164756E21
559241
+ //# debugId=4343FD443B24C00264756E2164756E21