@letta-ai/letta-code 0.25.3 → 0.25.5

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.
package/letta.js CHANGED
@@ -3143,7 +3143,7 @@ var package_default;
3143
3143
  var init_package = __esm(() => {
3144
3144
  package_default = {
3145
3145
  name: "@letta-ai/letta-code",
3146
- version: "0.25.3",
3146
+ version: "0.25.5",
3147
3147
  description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
3148
3148
  type: "module",
3149
3149
  bin: {
@@ -5756,13 +5756,16 @@ function isTruthyEnv(value) {
5756
5756
  function isLocalBackendEnvEnabled(env = process.env) {
5757
5757
  return isTruthyEnv(env[LOCAL_BACKEND_EXPERIMENTAL_ENV]);
5758
5758
  }
5759
+ function isLocalBackendNoMemfsEnvEnabled(env = process.env) {
5760
+ return isTruthyEnv(env[LOCAL_BACKEND_NO_MEMFS_ENV]);
5761
+ }
5759
5762
  function getLocalBackendStorageDir(homeDir = homedir3()) {
5760
5763
  return process.env[LOCAL_BACKEND_DIR_ENV] ?? join3(homeDir, ".letta", "lc-local-backend");
5761
5764
  }
5762
5765
  function getLocalBackendMemoryFilesystemRoot(agentId, storageDir = getLocalBackendStorageDir()) {
5763
5766
  return join3(storageDir, "memfs", agentId, "memory");
5764
5767
  }
5765
- var LOCAL_BACKEND_DIR_ENV = "LETTA_LOCAL_BACKEND_DIR", LOCAL_BACKEND_EXPERIMENTAL_ENV = "LETTA_LOCAL_BACKEND_EXPERIMENTAL";
5768
+ var LOCAL_BACKEND_DIR_ENV = "LETTA_LOCAL_BACKEND_DIR", LOCAL_BACKEND_EXPERIMENTAL_ENV = "LETTA_LOCAL_BACKEND_EXPERIMENTAL", LOCAL_BACKEND_NO_MEMFS_ENV = "LETTA_LOCAL_BACKEND_NO_MEMFS";
5766
5769
  var init_paths = () => {};
5767
5770
 
5768
5771
  // src/agent/memoryGit.ts
@@ -5918,6 +5921,11 @@ function getMemoryRemoteUrl(agentId) {
5918
5921
  return getGitRemoteUrl(agentId);
5919
5922
  }
5920
5923
  async function getAuthToken() {
5924
+ const { getBackend } = await Promise.resolve().then(() => (init_backend2(), exports_backend));
5925
+ const backend = getBackend();
5926
+ if (backend.capabilities.localMemfs && !backend.capabilities.remoteMemfs) {
5927
+ return "";
5928
+ }
5921
5929
  const client = await getClient();
5922
5930
  return client._options?.apiKey ?? "";
5923
5931
  }
@@ -5969,7 +5977,7 @@ async function runGit(cwd, args, token) {
5969
5977
  const result = await execFile("git", allArgs, {
5970
5978
  cwd,
5971
5979
  env: buildNonInteractiveGitEnv(),
5972
- maxBuffer: 10 * 1024 * 1024,
5980
+ maxBuffer: 10485760,
5973
5981
  timeout: 60000
5974
5982
  });
5975
5983
  return {
@@ -6101,9 +6109,9 @@ async function unsetLocalGitConfig(dir, key) {
6101
6109
  async function fetchAgentDisplayName(agentId) {
6102
6110
  let timeout;
6103
6111
  try {
6104
- const client = await getClient();
6112
+ const { getBackend } = await Promise.resolve().then(() => (init_backend2(), exports_backend));
6105
6113
  const agent = await Promise.race([
6106
- client.agents.retrieve(agentId),
6114
+ getBackend().retrieveAgent(agentId),
6107
6115
  new Promise((resolve2) => {
6108
6116
  timeout = setTimeout(() => resolve2(null), AGENT_DISPLAY_NAME_TIMEOUT_MS);
6109
6117
  })
@@ -6714,12 +6722,13 @@ async function getMemoryGitStatus(agentId) {
6714
6722
  };
6715
6723
  }
6716
6724
  async function addGitMemoryTag(agentId, prefetchedAgent) {
6717
- const client = await getClient();
6718
6725
  try {
6719
- const agent = prefetchedAgent ?? await client.agents.retrieve(agentId);
6726
+ const { getBackend } = await Promise.resolve().then(() => (init_backend2(), exports_backend));
6727
+ const backend = getBackend();
6728
+ const agent = prefetchedAgent ?? await backend.retrieveAgent(agentId);
6720
6729
  const tags = agent.tags || [];
6721
6730
  if (!tags.includes(GIT_MEMORY_ENABLED_TAG)) {
6722
- await client.agents.update(agentId, {
6731
+ await backend.updateAgent(agentId, {
6723
6732
  tags: [...tags, GIT_MEMORY_ENABLED_TAG]
6724
6733
  });
6725
6734
  debugLog("memfs-git", `Added ${GIT_MEMORY_ENABLED_TAG} tag`);
@@ -6729,12 +6738,13 @@ async function addGitMemoryTag(agentId, prefetchedAgent) {
6729
6738
  }
6730
6739
  }
6731
6740
  async function removeGitMemoryTag(agentId) {
6732
- const client = await getClient();
6733
6741
  try {
6734
- const agent = await client.agents.retrieve(agentId);
6742
+ const { getBackend } = await Promise.resolve().then(() => (init_backend2(), exports_backend));
6743
+ const backend = getBackend();
6744
+ const agent = await backend.retrieveAgent(agentId);
6735
6745
  const tags = agent.tags || [];
6736
6746
  if (tags.includes(GIT_MEMORY_ENABLED_TAG)) {
6737
- await client.agents.update(agentId, {
6747
+ await backend.updateAgent(agentId, {
6738
6748
  tags: tags.filter((t) => t !== GIT_MEMORY_ENABLED_TAG)
6739
6749
  });
6740
6750
  debugLog("memfs-git", `Removed ${GIT_MEMORY_ENABLED_TAG} tag`);
@@ -64368,6 +64378,8 @@ function createChatGPTFetch(options) {
64368
64378
  const auth = await getFreshAuth(options.storageDir);
64369
64379
  next.headers.delete("authorization");
64370
64380
  next.headers.set("authorization", `Bearer ${auth.access}`);
64381
+ next.headers.set("OpenAI-Beta", "responses=v1");
64382
+ next.headers.set("OpenAI-Originator", "codex");
64371
64383
  if (auth.accountId) {
64372
64384
  next.headers.set("ChatGPT-Account-Id", auth.accountId);
64373
64385
  }
@@ -69891,6 +69903,9 @@ function anthropicThinking(value, modelHandle) {
69891
69903
  function aiSDKProviderKind(modelHandle, modelSettings) {
69892
69904
  return aiSDKProviderKindFromModel(modelHandle, modelSettings);
69893
69905
  }
69906
+ function isChatGPTOAuthModel(modelHandle, modelSettings) {
69907
+ return modelHandle.startsWith("chatgpt-plus-pro/") || stringValue(modelSettings.provider_type) === "chatgpt_oauth";
69908
+ }
69894
69909
  function partProviderMetadata(part) {
69895
69910
  if (!isRecord2(part))
69896
69911
  return;
@@ -69916,22 +69931,153 @@ function shouldKeepReasoningPart(part, provider) {
69916
69931
  return hasAnthropicReasoningMetadata(part);
69917
69932
  return true;
69918
69933
  }
69919
- function sanitizeUIMessagesForProvider(messages, provider) {
69934
+ function stringArray(value) {
69935
+ return Array.isArray(value) && value.every((entry) => typeof entry === "string") ? value : undefined;
69936
+ }
69937
+ function mimeToModality(mime) {
69938
+ if (mime.startsWith("image/"))
69939
+ return "image";
69940
+ if (mime.startsWith("audio/"))
69941
+ return "audio";
69942
+ if (mime.startsWith("video/"))
69943
+ return "video";
69944
+ if (mime === "application/pdf")
69945
+ return "pdf";
69946
+ return;
69947
+ }
69948
+ function modalitiesFromModelSettings(modelSettings) {
69949
+ const modalities = isRecord2(modelSettings.modalities) ? modelSettings.modalities : undefined;
69950
+ const input = stringArray(modalities?.input);
69951
+ if (!input)
69952
+ return;
69953
+ return new Set(input.filter((entry) => ["text", "image", "audio", "video", "pdf"].includes(entry)));
69954
+ }
69955
+ function capabilitiesFromModelSettings(modelSettings) {
69956
+ const capabilities = isRecord2(modelSettings.capabilities) ? modelSettings.capabilities : undefined;
69957
+ const input = isRecord2(capabilities?.input) ? capabilities.input : undefined;
69958
+ if (!input)
69959
+ return;
69960
+ const supported = new Set(["text"]);
69961
+ for (const modality of ["image", "audio", "video", "pdf"]) {
69962
+ if (input[modality] === true)
69963
+ supported.add(modality);
69964
+ }
69965
+ return supported;
69966
+ }
69967
+ function knownModelInputModalities(modelHandle, modelSettings) {
69968
+ const explicitModalities = modalitiesFromModelSettings(modelSettings);
69969
+ if (explicitModalities)
69970
+ return explicitModalities;
69971
+ const explicitCapabilities = capabilitiesFromModelSettings(modelSettings);
69972
+ if (explicitCapabilities)
69973
+ return explicitCapabilities;
69974
+ const handle = modelHandle.toLowerCase();
69975
+ const supported = new Set(["text"]);
69976
+ if (handle.startsWith("anthropic/") || handle.startsWith("claude-pro-max/") || handle.startsWith("bedrock/") || handle.startsWith("google_ai/") || handle.startsWith("google_vertex/") || handle.includes("gemini") || handle.includes("claude") || handle.includes("gpt-4o") || handle.includes("gpt-4.1") || handle.includes("gpt-5") || handle.includes("o3") || handle.includes("o4") || handle.includes("vision") || handle.includes("qwen-vl") || handle.includes("qwen2-vl") || handle.includes("qwen2.5-vl") || handle.includes("qwen3-vl") || handle.includes("llava") || handle.includes("bakllava") || handle.includes("moondream")) {
69977
+ supported.add("image");
69978
+ }
69979
+ return supported;
69980
+ }
69981
+ function modelSupportsInputModality(modelHandle, modelSettings, modality) {
69982
+ if (modality === "text")
69983
+ return true;
69984
+ return knownModelInputModalities(modelHandle, modelSettings).has(modality);
69985
+ }
69986
+ function isEmptyBase64DataUrl(data) {
69987
+ if (typeof data !== "string" || !data.startsWith("data:"))
69988
+ return false;
69989
+ const match = data.match(/^data:([^;]+);base64,(.*)$/);
69990
+ return Boolean(match && (!match[2] || match[2].length === 0));
69991
+ }
69992
+ function filePartMediaType(part) {
69993
+ if (typeof part.mediaType === "string")
69994
+ return part.mediaType;
69995
+ if (typeof part.mime === "string")
69996
+ return part.mime;
69997
+ return;
69998
+ }
69999
+ function imagePartMediaType(part) {
70000
+ if (typeof part.image === "string" && part.image.startsWith("data:")) {
70001
+ return part.image.split(";")[0]?.replace("data:", "");
70002
+ }
70003
+ if (isRecord2(part.source) && typeof part.source.media_type === "string") {
70004
+ return part.source.media_type;
70005
+ }
70006
+ return;
70007
+ }
70008
+ function partFilename(part) {
70009
+ return stringValue(part.filename) ?? stringValue(part.name);
70010
+ }
70011
+ function replaceUnsupportedInputPart(part, agent) {
70012
+ if (!isRecord2(part))
70013
+ return part;
70014
+ const record2 = part;
70015
+ const partType = typeof record2.type === "string" ? record2.type : undefined;
70016
+ if (partType !== "file" && partType !== "image") {
70017
+ return part;
70018
+ }
70019
+ if (partType === "image" && isEmptyBase64DataUrl(record2.image)) {
70020
+ return {
70021
+ type: "text",
70022
+ text: "ERROR: Image file is empty or corrupted. Please provide a valid image."
70023
+ };
70024
+ }
70025
+ const mime = partType === "image" ? imagePartMediaType(record2) : filePartMediaType(record2);
70026
+ if (!mime)
70027
+ return part;
70028
+ const modality = mimeToModality(mime);
70029
+ if (!modality)
70030
+ return part;
70031
+ if (modelSupportsInputModality(agent.model, agent.model_settings, modality)) {
70032
+ return part;
70033
+ }
70034
+ const name25 = partFilename(record2);
70035
+ return {
70036
+ type: "text",
70037
+ text: `ERROR: Cannot read ${name25 ? `"${name25}"` : modality} (this model does not support ${modality} input). Inform the user.`
70038
+ };
70039
+ }
70040
+ function replaceUnsupportedInputParts(messages, agent) {
70041
+ let didChange = false;
70042
+ const transformed = messages.map((message) => {
70043
+ if (message.role !== "user")
70044
+ return message;
70045
+ let messageDidChange = false;
70046
+ const parts = message.parts.map((part) => {
70047
+ const replacement = replaceUnsupportedInputPart(part, agent);
70048
+ if (replacement !== part) {
70049
+ didChange = true;
70050
+ messageDidChange = true;
70051
+ }
70052
+ return replacement;
70053
+ });
70054
+ return messageDidChange ? { ...message, parts } : message;
70055
+ });
70056
+ return didChange ? transformed : messages;
70057
+ }
70058
+ function sanitizeUIMessagesForProvider(messages, provider, agent) {
70059
+ const capabilitySanitizedMessages = replaceUnsupportedInputParts(messages, agent);
69920
70060
  if (provider === "unknown")
69921
- return messages;
69922
- return messages.map((message) => {
70061
+ return capabilitySanitizedMessages;
70062
+ return capabilitySanitizedMessages.map((message) => {
69923
70063
  const parts = message.parts.filter((part) => shouldKeepReasoningPart(part, provider));
69924
70064
  return parts.length === message.parts.length ? message : { ...message, parts };
69925
70065
  }).filter((message) => message.role !== "assistant" || message.parts.length > 0);
69926
70066
  }
69927
- function buildAISDKProviderOptions(modelHandle, modelSettings) {
70067
+ function buildAISDKProviderOptions(modelHandle, modelSettings, options = {}) {
69928
70068
  const provider = aiSDKProviderKind(modelHandle, modelSettings);
69929
70069
  if (provider === "openai") {
70070
+ const chatgptOAuth = isChatGPTOAuthModel(modelHandle, modelSettings);
69930
70071
  const reasoning = isRecord2(modelSettings.reasoning) ? modelSettings.reasoning : undefined;
69931
70072
  const reasoningEffort = openAIReasoningEffort(reasoning?.reasoning_effort ?? modelSettings.reasoning_effort);
69932
70073
  const textVerbosity = openAITextVerbosity(modelSettings.verbosity);
69933
70074
  const parallelToolCalls = boolValue(modelSettings.parallel_tool_calls);
69934
70075
  const openai2 = {
70076
+ ...chatgptOAuth ? {
70077
+ instructions: options.systemPrompt,
70078
+ store: false,
70079
+ systemMessageMode: "remove"
70080
+ } : {},
69935
70081
  ...reasoningEffort !== undefined ? { reasoningEffort } : {},
69936
70082
  ...textVerbosity !== undefined ? { textVerbosity } : {},
69937
70083
  ...parallelToolCalls !== undefined ? { parallelToolCalls } : {}
@@ -70011,15 +70157,15 @@ class AISDKStreamAdapter {
70011
70157
  const tools = toToolSet(input.clientTools);
70012
70158
  const provider = aiSDKProviderKind(input.agent.model, input.agent.model_settings);
70013
70159
  const uiMessages = await validateUIMessages({
70014
- messages: sanitizeUIMessagesForProvider(input.uiMessages, provider),
70160
+ messages: sanitizeUIMessagesForProvider(input.uiMessages, provider, input.agent),
70015
70161
  tools
70016
70162
  });
70017
70163
  const result = this.runStreamText({
70018
70164
  model: this.createModel?.() ?? createAISDKModelFactoryFromAgent(input.agent.model, input.agent.model_settings, { localProviderAuthStorageDir: this.localProviderAuthStorageDir })(),
70019
- system: input.systemPrompt ?? input.agent.system,
70165
+ system: provider === "openai" && isChatGPTOAuthModel(input.agent.model, input.agent.model_settings) ? undefined : input.systemPrompt ?? input.agent.system,
70020
70166
  messages: await convertToModelMessages(uiMessages, { tools }),
70021
70167
  tools,
70022
- providerOptions: buildAISDKProviderOptions(input.agent.model, input.agent.model_settings),
70168
+ providerOptions: buildAISDKProviderOptions(input.agent.model, input.agent.model_settings, { systemPrompt: input.systemPrompt ?? input.agent.system }),
70023
70169
  maxRetries: 0,
70024
70170
  abortSignal: this.abortSignal
70025
70171
  });
@@ -70421,6 +70567,7 @@ function normalizeAgentRecord(value, defaultAgentModel) {
70421
70567
  if (modelSettings.max_tokens === undefined && (typeof legacyLlmConfig.max_tokens === "number" || legacyLlmConfig.max_tokens === null)) {
70422
70568
  modelSettings.max_tokens = legacyLlmConfig.max_tokens;
70423
70569
  }
70570
+ const compactionSettings = optionalRecordOrNull(value.compaction_settings);
70424
70571
  return {
70425
70572
  id: value.id,
70426
70573
  name: optionalString(value.name) ?? "Letta Code",
@@ -70428,7 +70575,8 @@ function normalizeAgentRecord(value, defaultAgentModel) {
70428
70575
  system: optionalString(value.system) ?? "",
70429
70576
  tags: isStringArray(value.tags) ? value.tags : [],
70430
70577
  model: optionalString(value.model) ?? optionalString(legacyLlmConfig.model) ?? defaultAgentModel,
70431
- model_settings: modelSettings
70578
+ model_settings: modelSettings,
70579
+ ...compactionSettings !== undefined ? { compaction_settings: compactionSettings } : {}
70432
70580
  };
70433
70581
  }
70434
70582
  function projectAgentState(record2, messageIds = [], inContextMessageIds = messageIds, lastRunCompletion) {
@@ -70444,6 +70592,7 @@ function projectAgentState(record2, messageIds = [], inContextMessageIds = messa
70444
70592
  tags: record2.tags,
70445
70593
  model: record2.model,
70446
70594
  model_settings: record2.model_settings,
70595
+ ...record2.compaction_settings !== undefined ? { compaction_settings: record2.compaction_settings } : {},
70447
70596
  message_ids: messageIds,
70448
70597
  in_context_message_ids: inContextMessageIds,
70449
70598
  ...lastRunCompletion ? { last_run_completion: lastRunCompletion } : {},
@@ -70472,6 +70621,39 @@ function normalizeContent(content) {
70472
70621
  function isRecord3(value) {
70473
70622
  return typeof value === "object" && value !== null && !Array.isArray(value);
70474
70623
  }
70624
+ function localFilePartFromLegacyImage(part) {
70625
+ if (part.type !== "image" || !isRecord3(part.source)) {
70626
+ return null;
70627
+ }
70628
+ const source = part.source;
70629
+ if (source.type !== "base64") {
70630
+ return null;
70631
+ }
70632
+ const mediaType = source.media_type;
70633
+ const data = source.data;
70634
+ if (typeof mediaType !== "string" || typeof data !== "string") {
70635
+ return null;
70636
+ }
70637
+ return {
70638
+ type: "file",
70639
+ mediaType,
70640
+ url: `data:${mediaType};base64,${data}`
70641
+ };
70642
+ }
70643
+ function normalizeLocalMessageForAISDK(message) {
70644
+ let didChange = false;
70645
+ const parts = message.parts.map((part) => {
70646
+ if (isRecord3(part)) {
70647
+ const filePart = localFilePartFromLegacyImage(part);
70648
+ if (filePart) {
70649
+ didChange = true;
70650
+ return filePart;
70651
+ }
70652
+ }
70653
+ return part;
70654
+ });
70655
+ return didChange ? { ...message, parts } : message;
70656
+ }
70475
70657
  function textFromContent(content) {
70476
70658
  if (typeof content === "string")
70477
70659
  return content;
@@ -70703,6 +70885,19 @@ class LocalStore {
70703
70885
  }
70704
70886
  return this.projectAgent(updated);
70705
70887
  }
70888
+ setAgentCompactionSettings(agentId, settings) {
70889
+ const existing = this.agents.get(agentId);
70890
+ if (!existing) {
70891
+ throw new LocalBackendNotFoundError("Agent", agentId);
70892
+ }
70893
+ const updated = {
70894
+ ...existing,
70895
+ compaction_settings: settings === null ? null : { ...settings }
70896
+ };
70897
+ this.agents.set(agentId, updated);
70898
+ this.persistAgent(agentId);
70899
+ return this.projectAgent(updated);
70900
+ }
70706
70901
  createAgent(body) {
70707
70902
  const agent = this.createAgentRecord(body);
70708
70903
  const agentId = agent.id;
@@ -70940,8 +71135,12 @@ class LocalStore {
70940
71135
  },
70941
71136
  parts: [{ type: "text", text: input.packedSummary }]
70942
71137
  };
70943
- this.localMessagesByConversationKey.set(key, [summaryMessage]);
70944
- conversation.in_context_message_ids = [summaryMessage.id];
71138
+ const compactedMessages = [
71139
+ summaryMessage,
71140
+ ...(input.remainingMessages ?? []).map(cloneLocalMessage)
71141
+ ];
71142
+ this.localMessagesByConversationKey.set(key, compactedMessages);
71143
+ conversation.in_context_message_ids = compactedMessages.map((message) => message.id);
70945
71144
  conversation.last_message_at = date5;
70946
71145
  conversation.updated_at = date5;
70947
71146
  this.conversations.set(key, conversation);
@@ -70949,7 +71148,7 @@ class LocalStore {
70949
71148
  this.rebuildMessageIndex();
70950
71149
  return {
70951
71150
  numMessagesBefore: previousMessages.length,
70952
- numMessagesAfter: 1,
71151
+ numMessagesAfter: compactedMessages.length,
70953
71152
  summaryMessage: cloneLocalMessage(summaryMessage)
70954
71153
  };
70955
71154
  }
@@ -71305,6 +71504,11 @@ class LocalStore {
71305
71504
  parts.push({ type: "text", text: part.text });
71306
71505
  continue;
71307
71506
  }
71507
+ const filePart = localFilePartFromLegacyImage(part);
71508
+ if (filePart) {
71509
+ parts.push(filePart);
71510
+ continue;
71511
+ }
71308
71512
  parts.push(part);
71309
71513
  }
71310
71514
  return parts.length > 0 ? parts : textContent(textFromContent(content));
@@ -71341,7 +71545,7 @@ class LocalStore {
71341
71545
  if (!conversation?.id || !conversation.agent_id)
71342
71546
  continue;
71343
71547
  const key = this.conversationKey(conversation.id, conversation.agent_id);
71344
- const localMessages = readJsonlFile(join7(conversationDir, "messages.jsonl"));
71548
+ const localMessages = readJsonlFile(join7(conversationDir, "messages.jsonl")).map(normalizeLocalMessageForAISDK);
71345
71549
  const compiledSystemPrompt = readJsonFile(join7(conversationDir, "system-prompt.json"));
71346
71550
  const messages = projectLocalMessagesToStoredMessages(localMessages, conversation.agent_id, conversation.id);
71347
71551
  this.conversations.set(key, conversation);
@@ -71892,6 +72096,9 @@ var init_HeadlessBackend = __esm(() => {
71892
72096
  });
71893
72097
 
71894
72098
  // src/backend/local/compaction.ts
72099
+ function isLocalSlidingWindowCompactionPlanningError(error51) {
72100
+ return error51 instanceof LocalSlidingWindowCompactionPlanningError;
72101
+ }
71895
72102
  function isRecord4(value) {
71896
72103
  return typeof value === "object" && value !== null && !Array.isArray(value);
71897
72104
  }
@@ -71950,24 +72157,24 @@ ${body}
71950
72157
  }
71951
72158
  return transcript;
71952
72159
  }
71953
- async function runGenerateText(input, transcript) {
72160
+ async function runGenerateText(input, transcript, defaultPrompt) {
71954
72161
  const run = input.generateText ?? generateText;
71955
72162
  return run({
71956
72163
  model: input.createModel?.() ?? createAISDKModelFactoryFromAgent(input.agent.model, input.agent.model_settings, { localProviderAuthStorageDir: input.localProviderAuthStorageDir })(),
71957
- system: input.prompt ?? LOCAL_ALL_COMPACTION_PROMPT,
72164
+ system: input.prompt ?? defaultPrompt,
71958
72165
  prompt: transcript,
71959
72166
  providerOptions: buildAISDKProviderOptions(input.agent.model, input.agent.model_settings),
71960
72167
  maxRetries: 0,
71961
72168
  abortSignal: input.abortSignal
71962
72169
  });
71963
72170
  }
71964
- async function summarizeLocalMessagesAll(input) {
72171
+ async function summarizeLocalMessagesWithPrompt(input, defaultPrompt) {
71965
72172
  if (input.messages.length === 0)
71966
72173
  return "No prior conversation messages.";
71967
72174
  const primaryTranscript = formatLocalMessagesForSummary(input.messages);
71968
72175
  let result;
71969
72176
  try {
71970
- result = await runGenerateText(input, primaryTranscript);
72177
+ result = await runGenerateText(input, primaryTranscript, defaultPrompt);
71971
72178
  } catch (error51) {
71972
72179
  if (!isContextWindowOverflowError(error51))
71973
72180
  throw error51;
@@ -71975,7 +72182,7 @@ async function summarizeLocalMessagesAll(input) {
71975
72182
  truncationChars: TOOL_TRANSCRIPT_TRUNCATION_CHARS,
71976
72183
  maxChars: TRANSCRIPT_FALLBACK_MAX_CHARS
71977
72184
  });
71978
- result = await runGenerateText(input, fallbackTranscript);
72185
+ result = await runGenerateText(input, fallbackTranscript, defaultPrompt);
71979
72186
  }
71980
72187
  let summary = result.text.trim();
71981
72188
  const clipChars = input.clipChars === undefined ? 50000 : input.clipChars;
@@ -71984,6 +72191,62 @@ async function summarizeLocalMessagesAll(input) {
71984
72191
  }
71985
72192
  return summary;
71986
72193
  }
72194
+ async function summarizeLocalMessagesAll(input) {
72195
+ return summarizeLocalMessagesWithPrompt(input, LOCAL_ALL_COMPACTION_PROMPT);
72196
+ }
72197
+ function isSettledLocalToolState2(state) {
72198
+ return state === "output-available" || state === "output-error" || state === "output-denied";
72199
+ }
72200
+ function hasPendingLocalToolPart(message) {
72201
+ return message.parts.some((part) => {
72202
+ if (!isRecord4(part))
72203
+ return false;
72204
+ const partRecord = part;
72205
+ return typeof partRecord.type === "string" && partRecord.type.startsWith("tool-") && !isSettledLocalToolState2(partRecord.state);
72206
+ });
72207
+ }
72208
+ function normalizedSlidingWindowPercentage(value) {
72209
+ if (typeof value !== "number" || !Number.isFinite(value)) {
72210
+ return LOCAL_DEFAULT_SLIDING_WINDOW_PERCENTAGE;
72211
+ }
72212
+ if (value <= 0)
72213
+ return 0.1;
72214
+ if (value > 1)
72215
+ return 1;
72216
+ return value;
72217
+ }
72218
+ function isValidSlidingWindowCutoff(messages, index, maximumCutoffIndex) {
72219
+ const message = messages[index];
72220
+ return message?.role === "assistant" && index > 1 && index < maximumCutoffIndex;
72221
+ }
72222
+ function planLocalSlidingWindowCompaction(messages, options = {}) {
72223
+ if (messages.length < 4) {
72224
+ throw new LocalSlidingWindowCompactionPlanningError("Not enough messages for sliding window compaction.");
72225
+ }
72226
+ const percentage = normalizedSlidingWindowPercentage(options.slidingWindowPercentage);
72227
+ const lastMessage = messages.at(-1);
72228
+ const maximumCutoffIndex = lastMessage && hasPendingLocalToolPart(lastMessage) ? messages.length - 2 : messages.length - 1;
72229
+ const goalTokens = typeof options.contextWindow === "number" && Number.isFinite(options.contextWindow) ? (1 - percentage) * options.contextWindow : undefined;
72230
+ for (let evictionPercentage = percentage;evictionPercentage < 1; evictionPercentage += 0.1) {
72231
+ const messageCutoffIndex = Math.min(Math.round(evictionPercentage * messages.length), messages.length - 1);
72232
+ const cutoffIndex = [...Array(messageCutoffIndex + 1).keys()].reverse().find((index) => isValidSlidingWindowCutoff(messages, index, maximumCutoffIndex));
72233
+ if (cutoffIndex === undefined)
72234
+ continue;
72235
+ const messagesToKeep = messages.slice(cutoffIndex);
72236
+ if (goalTokens !== undefined && estimateLocalMessageTokens(messagesToKeep) >= goalTokens) {
72237
+ continue;
72238
+ }
72239
+ return {
72240
+ messagesToSummarize: messages.slice(0, cutoffIndex),
72241
+ messagesToKeep,
72242
+ cutoffIndex
72243
+ };
72244
+ }
72245
+ throw new LocalSlidingWindowCompactionPlanningError("No assistant message found for sliding window compaction.");
72246
+ }
72247
+ async function summarizeLocalMessagesSlidingWindow(input) {
72248
+ return summarizeLocalMessagesWithPrompt(input, LOCAL_SLIDING_WINDOW_COMPACTION_PROMPT);
72249
+ }
71987
72250
  function estimateLocalMessageTokens(messages) {
71988
72251
  const chars = messages.reduce((total, message) => total + JSON.stringify(message).length, 0);
71989
72252
  return Math.ceil(chars / 4);
@@ -71998,12 +72261,18 @@ The following is an in-context recursive summary of the prior messages: ${summar
71998
72261
  ...stats ? { compaction_stats: stats } : {}
71999
72262
  });
72000
72263
  }
72001
- var ALL_WORD_LIMIT = 500, SUMMARY_TRUNCATION_SUFFIX = "... [summary truncated to fit]", TOOL_TRANSCRIPT_TRUNCATION_CHARS = 4000, TRANSCRIPT_FALLBACK_MAX_CHARS = 120000, LOCAL_ALL_COMPACTION_PROMPT;
72264
+ var ALL_WORD_LIMIT = 500, SLIDING_WORD_LIMIT = 300, SUMMARY_TRUNCATION_SUFFIX = "... [summary truncated to fit]", TOOL_TRANSCRIPT_TRUNCATION_CHARS = 4000, TRANSCRIPT_FALLBACK_MAX_CHARS = 120000, LOCAL_DEFAULT_COMPACTION_MODE = "sliding_window", LOCAL_DEFAULT_SLIDING_WINDOW_PERCENTAGE = 0.3, LocalSlidingWindowCompactionPlanningError, LOCAL_ALL_COMPACTION_PROMPT, LOCAL_SLIDING_WINDOW_COMPACTION_PROMPT;
72002
72265
  var init_compaction = __esm(() => {
72003
72266
  init_dist6();
72004
72267
  init_AISDKModelFactory();
72005
72268
  init_AISDKStreamAdapter();
72006
72269
  init_contextWindowOverflow();
72270
+ LocalSlidingWindowCompactionPlanningError = class LocalSlidingWindowCompactionPlanningError extends Error {
72271
+ constructor(message) {
72272
+ super(message);
72273
+ this.name = "LocalSlidingWindowCompactionPlanningError";
72274
+ }
72275
+ };
72007
72276
  LOCAL_ALL_COMPACTION_PROMPT = `Your task is to create a detailed summary of the conversation so far, paying close attention to the user's explicit requests and your previous actions.
72008
72277
  This summary should be thorough in capturing technical details, code patterns, and architectural decisions that would be essential for continuing development work without losing context. Your summary should include the following sections:
72009
72278
 
@@ -72027,6 +72296,24 @@ This summary should be thorough in capturing technical details, code patterns, a
72027
72296
  Write in first person as a factual record of what occurred. Be concise but thorough - the goal is to preserve enough context that the recent messages make sense and important information isn't lost to prevent duplicate work or repeated mistakes.
72028
72297
 
72029
72298
  Keep your summary under ${ALL_WORD_LIMIT} words. Only output the summary.`;
72299
+ LOCAL_SLIDING_WINDOW_COMPACTION_PROMPT = `The following messages are being evicted from the BEGINNING of your context window. Write a detailed summary that captures what happened in these messages to appear BEFORE the remaining recent messages in context, providing background for what comes after. Include the following sections:
72300
+
72301
+ 1.**High level goals**: What is the high level goal and ongoing task? Capture the user's explicit requests and intent in detail. If there is an existing summary in the transcript, make sure to take it into consideration to continue tracking the higher level goals and long-term progress.
72302
+
72303
+ 2. **What happened**: The conversations, tasks, and exchanges that took place. What did the user ask for? What did you do? How did things progress? If there is a previous summary being evicted, please extract a concise version of the critical info from it.
72304
+
72305
+ 3. **Important details**: Enumerate specific files and code sections examined, modified, or created, as well as important plan files, GitHub issues/PR links, and Linear ticket IDs. For each item, include why it matters and any relevant names, data, configs, or facts discussed.
72306
+ - **Preserve identifiers verbatim** (plan filename/path, exact URL, issue/PR number, ticket ID); do not paraphrase or truncate.
72307
+ - **Preserve referenced identifiers unless explicitly resolved**: Keep exact URLs/IDs from the conversation unless there is clear evidence they are no longer relevant.
72308
+ - Do not omit details likely to be referenced later.
72309
+
72310
+ 4. **Errors and fixes**: List all errors that you ran into, and how you fixed them. Pay special attention to specific user feedback that you received and record verbatim if useful.
72311
+
72312
+ 5. **Lookup hints**: For any detailed content (long lists, extensive data, specific conversations) that couldn't fit in the summary, note the topic and key terms that could be used to find it in message history later.
72313
+
72314
+ Write in first person as a factual record of what occurred. Be thorough and detailed - the goal is to preserve enough context that the recent messages make sense and important information isn't lost to prevent duplicate work or repeated mistakes.
72315
+
72316
+ Keep your summary under ${SLIDING_WORD_LIMIT} words. Only output the summary.`;
72030
72317
  });
72031
72318
 
72032
72319
  // src/backend/local/LocalModelConfig.ts
@@ -76274,6 +76561,9 @@ __export(exports_modify, {
76274
76561
  function supportsDistinctAnthropicXHighEffort(modelHandle) {
76275
76562
  return modelHandle.includes("claude-opus-4-7");
76276
76563
  }
76564
+ function isRecord5(value) {
76565
+ return typeof value === "object" && value !== null && !Array.isArray(value);
76566
+ }
76277
76567
  function buildModelSettings(modelHandle, updateArgs) {
76278
76568
  const isOpenAI = modelHandle.startsWith("openai/") || modelHandle.startsWith(`${OPENAI_CODEX_PROVIDER_NAME}/`);
76279
76569
  const isAnthropic = modelHandle.startsWith("anthropic/") || modelHandle.startsWith("claude-pro-max/") || modelHandle.startsWith("minimax/");
@@ -76396,6 +76686,12 @@ function buildModelSettings(modelHandle, updateArgs) {
76396
76686
  if (typeof updateArgs?.max_output_tokens === "number" && "provider_type" in settings) {
76397
76687
  settings.max_output_tokens = updateArgs.max_output_tokens;
76398
76688
  }
76689
+ if (isRecord5(updateArgs?.modalities)) {
76690
+ settings.modalities = updateArgs.modalities;
76691
+ }
76692
+ if (isRecord5(updateArgs?.capabilities)) {
76693
+ settings.capabilities = updateArgs.capabilities;
76694
+ }
76399
76695
  return settings;
76400
76696
  }
76401
76697
  async function updateAgentLLMConfig(agentId, modelHandle, updateArgs, options) {
@@ -78106,6 +78402,95 @@ function createTelegramAdapter(config2) {
78106
78402
  let botModule = null;
78107
78403
  let running = false;
78108
78404
  const bufferedMediaGroups = new Map;
78405
+ const typingByChatId = new Map;
78406
+ async function sendTypingAction(chatId) {
78407
+ if (!running)
78408
+ return;
78409
+ try {
78410
+ const telegramBot = await ensureBot();
78411
+ await telegramBot.api.sendChatAction(chatId, "typing");
78412
+ } catch (error51) {
78413
+ console.warn(`[Telegram] Failed to send typing action for chat ${chatId}:`, error51 instanceof Error ? error51.message : error51);
78414
+ }
78415
+ }
78416
+ function getTypingSourceKey(source) {
78417
+ const chatId = getTypingChatId(source);
78418
+ if (!chatId)
78419
+ return null;
78420
+ return [
78421
+ source.accountId ?? "",
78422
+ chatId,
78423
+ source.threadId ?? "",
78424
+ source.messageId ?? "",
78425
+ source.agentId,
78426
+ source.conversationId
78427
+ ].join(":");
78428
+ }
78429
+ function startTypingForSource(source) {
78430
+ const chatId = getTypingChatId(source);
78431
+ const sourceKey = getTypingSourceKey(source);
78432
+ if (!chatId || !sourceKey)
78433
+ return;
78434
+ const existing = typingByChatId.get(chatId);
78435
+ if (existing) {
78436
+ existing.sourceKeys.add(sourceKey);
78437
+ return;
78438
+ }
78439
+ sendTypingAction(chatId);
78440
+ const timer = setInterval(() => {
78441
+ sendTypingAction(chatId);
78442
+ }, TELEGRAM_TYPING_REFRESH_MS);
78443
+ const timeout = setTimeout(() => {
78444
+ clearTypingForChat(chatId);
78445
+ }, TELEGRAM_TYPING_MAX_MS);
78446
+ if (typeof timer.unref === "function") {
78447
+ timer.unref?.();
78448
+ }
78449
+ if (typeof timeout.unref === "function") {
78450
+ timeout.unref?.();
78451
+ }
78452
+ typingByChatId.set(chatId, {
78453
+ sourceKeys: new Set([sourceKey]),
78454
+ timer,
78455
+ timeout
78456
+ });
78457
+ }
78458
+ function stopTypingForSource(source) {
78459
+ const chatId = getTypingChatId(source);
78460
+ const sourceKey = getTypingSourceKey(source);
78461
+ if (!chatId || !sourceKey)
78462
+ return;
78463
+ const entry = typingByChatId.get(chatId);
78464
+ if (!entry)
78465
+ return;
78466
+ entry.sourceKeys.delete(sourceKey);
78467
+ if (entry.sourceKeys.size === 0) {
78468
+ clearTypingForChat(chatId);
78469
+ }
78470
+ }
78471
+ function clearTypingForChat(chatId) {
78472
+ const entry = typingByChatId.get(chatId);
78473
+ if (!entry)
78474
+ return;
78475
+ clearInterval(entry.timer);
78476
+ clearTimeout(entry.timeout);
78477
+ typingByChatId.delete(chatId);
78478
+ }
78479
+ function clearAllTyping() {
78480
+ for (const entry of typingByChatId.values()) {
78481
+ clearInterval(entry.timer);
78482
+ clearTimeout(entry.timeout);
78483
+ }
78484
+ typingByChatId.clear();
78485
+ }
78486
+ function getTypingChatId(source) {
78487
+ if (source.channel !== "telegram")
78488
+ return null;
78489
+ const chatId = source.chatId;
78490
+ if (typeof chatId !== "string" || chatId.length === 0)
78491
+ return null;
78492
+ return chatId;
78493
+ }
78109
78494
  async function ensureModule() {
78110
78495
  if (!botModule) {
78111
78496
  botModule = await loadGrammyModule();
@@ -78299,6 +78684,7 @@ function createTelegramAdapter(config2) {
78299
78684
  clearTimeout(entry.timer);
78300
78685
  }
78301
78686
  bufferedMediaGroups.clear();
78687
+ clearAllTyping();
78302
78688
  if (!running || !bot)
78303
78689
  return;
78304
78690
  await bot.stop();
@@ -78324,6 +78710,7 @@ function createTelegramAdapter(config2) {
78324
78710
  } else {
78325
78711
  await telegramBot.api.setMessageReaction(msg.chatId, Number(targetMessageId), []);
78326
78712
  }
78713
+ clearTypingForChat(msg.chatId);
78327
78714
  return { messageId: targetMessageId };
78328
78715
  }
78329
78716
  if (msg.mediaPath) {
@@ -78350,6 +78737,7 @@ function createTelegramAdapter(config2) {
78350
78737
  return await telegramBot.api.sendDocument(msg.chatId, inputFile, options);
78351
78738
  }
78352
78739
  })();
78740
+ clearTypingForChat(msg.chatId);
78353
78741
  return { messageId: String(result2.message_id) };
78354
78742
  }
78355
78743
  const opts = {};
@@ -78362,6 +78750,7 @@ function createTelegramAdapter(config2) {
78362
78750
  opts.parse_mode = msg.parseMode;
78363
78751
  }
78364
78752
  const result = await telegramBot.api.sendMessage(msg.chatId, msg.text, opts);
78753
+ clearTypingForChat(msg.chatId);
78365
78754
  return { messageId: String(result.message_id) };
78366
78755
  },
78367
78756
  async sendDirectReply(chatId, text2, options) {
@@ -78371,12 +78760,29 @@ function createTelegramAdapter(config2) {
78371
78760
  } : undefined;
78372
78761
  await telegramBot.api.sendMessage(chatId, text2, reply_parameters ? { reply_parameters } : {});
78373
78762
  },
78763
+ async handleTurnLifecycleEvent(event) {
78764
+ if (!running)
78765
+ return;
78766
+ if (event.type === "queued") {
78767
+ return;
78768
+ }
78769
+ if (event.type === "processing") {
78770
+ for (const source of event.sources) {
78771
+ startTypingForSource(source);
78772
+ }
78773
+ return;
78774
+ }
78775
+ for (const source of event.sources) {
78776
+ stopTypingForSource(source);
78777
+ }
78778
+ },
78374
78779
  async handleControlRequestEvent(event) {
78375
78780
  const telegramBot = await ensureBot();
78376
78781
  const reply_parameters = event.source.messageId || event.source.threadId ? {
78377
78782
  message_id: Number(event.source.threadId ?? event.source.messageId)
78378
78783
  } : undefined;
78379
78784
  await telegramBot.api.sendMessage(event.source.chatId, formatChannelControlRequestPrompt(event), reply_parameters ? { reply_parameters } : {});
78785
+ clearTypingForChat(event.source.chatId);
78380
78786
  },
78381
78787
  onMessage: undefined
78382
78788
  };
@@ -78393,9 +78799,11 @@ async function validateTelegramToken(token) {
78393
78799
  id: info.id
78394
78800
  };
78395
78801
  }
78802
+ var TELEGRAM_TYPING_REFRESH_MS = 4000, TELEGRAM_TYPING_MAX_MS;
78396
78803
  var init_adapter = __esm(() => {
78397
78804
  init_media();
78398
78805
  init_runtime();
78806
+ TELEGRAM_TYPING_MAX_MS = 5 * 60 * 1000;
78399
78807
  });
78400
78808
 
78401
78809
  // src/channels/telegram/messageActions.ts
@@ -78488,13 +78896,13 @@ function cloneAccount(account) {
78488
78896
  }
78489
78897
  return cloned;
78490
78898
  }
78491
- function isRecord5(value) {
78899
+ function isRecord6(value) {
78492
78900
  return !!value && typeof value === "object" && !Array.isArray(value);
78493
78901
  }
78494
78902
  function normalizeLoadedAccount(account) {
78495
78903
  const next = cloneAccount(account);
78496
78904
  if (!isFirstPartyChannelId(next.channel)) {
78497
- next.config = isRecord5(next.config) ? { ...next.config } : {};
78905
+ next.config = isRecord6(next.config) ? { ...next.config } : {};
78498
78906
  }
78499
78907
  if (isTelegramChannelAccount(next) && (next.displayName === "Telegram bot" || next.displayName === "Migrated Telegram bot") || isSlackChannelAccount(next) && (next.displayName === "Slack app" || next.displayName === "Migrated Slack app") || isDiscordChannelAccount(next) && (next.displayName === "Discord bot" || next.displayName === "Migrated Discord bot")) {
78500
78908
  next.displayName = undefined;
@@ -80995,6 +81403,12 @@ function resolveDiscordReactionEmoji(value) {
80995
81403
  };
80996
81404
  return nameMap[normalized] ?? normalized;
80997
81405
  }
81406
+ function buildDiscordIngressMessageKey(accountId, messageId) {
81407
+ if (!isNonEmptyString3(accountId) || !isNonEmptyString3(messageId)) {
81408
+ return null;
81409
+ }
81410
+ return `${accountId}:${messageId}`;
81411
+ }
80998
81412
  function buildDiscordReplyOptions(replyToMessageId, channelId) {
80999
81413
  const trimmed = replyToMessageId?.trim();
81000
81414
  if (!trimmed || trimmed === channelId) {
@@ -81002,7 +81416,8 @@ function buildDiscordReplyOptions(replyToMessageId, channelId) {
81002
81416
  }
81003
81417
  return {
81004
81418
  reply: {
81005
- messageReference: trimmed
81419
+ messageReference: trimmed,
81420
+ failIfNotExists: false
81006
81421
  }
81007
81422
  };
81008
81423
  }
@@ -81044,12 +81459,6 @@ function createDiscordAdapter(config2) {
81044
81459
  const seenIngressMessageKeys = new Map;
81045
81460
  const lifecycleStateByMessageKey = new Map;
81046
81461
  const lifecycleOperationByMessageKey = new Map;
81047
- function buildIngressMessageKey(channelId, messageId) {
81048
- if (!isNonEmptyString3(channelId) || !isNonEmptyString3(messageId)) {
81049
- return null;
81050
- }
81051
- return `${channelId}:${messageId}`;
81052
- }
81053
81462
  function pruneSeenIngressMessageKeys(now2 = Date.now()) {
81054
81463
  for (const [key, expiresAt] of seenIngressMessageKeys) {
81055
81464
  if (expiresAt <= now2) {
@@ -81068,8 +81477,8 @@ function createDiscordAdapter(config2) {
81068
81477
  }
81069
81478
  }
81070
81479
  }
81071
- function markIngressMessageSeen(channelId, messageId) {
81072
- const key = buildIngressMessageKey(channelId, messageId);
81480
+ function markIngressMessageSeen(messageId) {
81481
+ const key = buildDiscordIngressMessageKey(config2.accountId, messageId);
81073
81482
  if (!key)
81074
81483
  return false;
81075
81484
  const now2 = Date.now();
@@ -81259,7 +81668,7 @@ function createDiscordAdapter(config2) {
81259
81668
  const isThread = isThreadMessage(message);
81260
81669
  const wasMentioned = chatType === "channel" && hasBotMention(message);
81261
81670
  if (chatType === "direct") {
81262
- if (markIngressMessageSeen(message.channelId, message.id))
81671
+ if (markIngressMessageSeen(message.id))
81263
81672
  return;
81264
81673
  const attachments2 = await collectAttachments(message.attachments, message.channelId);
81265
81674
  if (!content && (!attachments2 || attachments2.length === 0))
@@ -81296,7 +81705,7 @@ function createDiscordAdapter(config2) {
81296
81705
  allowedChannels: config2.allowedChannels
81297
81706
  }))
81298
81707
  return;
81299
- if (markIngressMessageSeen(message.channelId, message.id))
81708
+ if (markIngressMessageSeen(message.id))
81300
81709
  return;
81301
81710
  let effectiveChatId = message.channelId;
81302
81711
  let effectiveThreadId = isThread ? message.channelId : null;
@@ -81767,7 +82176,7 @@ __export(exports_pluginRegistry, {
81767
82176
  import { existsSync as existsSync11, readdirSync as readdirSync3, readFileSync as readFileSync9 } from "node:fs";
81768
82177
  import { resolve as resolve6, sep } from "node:path";
81769
82178
  import { pathToFileURL as pathToFileURL2 } from "node:url";
81770
- function isRecord6(value) {
82179
+ function isRecord7(value) {
81771
82180
  return !!value && typeof value === "object" && !Array.isArray(value);
81772
82181
  }
81773
82182
  function isValidChannelId(value) {
@@ -81783,7 +82192,7 @@ function readChannelManifest(channelDir) {
81783
82192
  }
81784
82193
  try {
81785
82194
  const parsed = JSON.parse(readFileSync9(manifestPath, "utf-8"));
81786
- if (!isRecord6(parsed)) {
82195
+ if (!isRecord7(parsed)) {
81787
82196
  return null;
81788
82197
  }
81789
82198
  const id = typeof parsed.id === "string" ? parsed.id.trim() : "";
@@ -81827,8 +82236,8 @@ function createUserChannelRegistration(manifest) {
81827
82236
  throw new Error(`Channel plugin "${manifest.id}" entry escapes its directory.`);
81828
82237
  }
81829
82238
  const loadPromise = import(pathToFileURL2(entryPath).href).then((loaded) => {
81830
- const exported = (isRecord6(loaded) ? loaded.channelPlugin : undefined) ?? (isRecord6(loaded) ? loaded.default : undefined);
81831
- if (!isRecord6(exported)) {
82239
+ const exported = (isRecord7(loaded) ? loaded.channelPlugin : undefined) ?? (isRecord7(loaded) ? loaded.default : undefined);
82240
+ if (!isRecord7(exported)) {
81832
82241
  throw new Error(`Channel plugin "${manifest.id}" must export channelPlugin or default.`);
81833
82242
  }
81834
82243
  const plugin = exported;
@@ -82775,6 +83184,9 @@ function buildChannelNotificationXml(msg) {
82775
83184
  `chat_id="${escapeXmlAttribute(msg.chatId)}"`,
82776
83185
  `sender_id="${escapeXmlAttribute(msg.senderId)}"`
82777
83186
  ];
83187
+ if (msg.accountId) {
83188
+ attrs.push(`account_id="${escapeXmlAttribute(msg.accountId)}"`);
83189
+ }
82778
83190
  if (msg.senderName) {
82779
83191
  attrs.push(`sender_name="${escapeXmlAttribute(msg.senderName)}"`);
82780
83192
  }
@@ -83135,8 +83547,9 @@ class ChannelRegistry {
83135
83547
  }
83136
83548
  return matches[0] ?? null;
83137
83549
  }
83138
- getRouteForScope(channel, chatId, agentId, conversationId) {
83139
- const matches = getRoutesForChannel(channel).filter((route) => route.chatId === chatId && route.agentId === agentId && route.conversationId === conversationId && route.enabled);
83550
+ getRouteForScope(channel, chatId, agentId, conversationId, accountId) {
83551
+ const normalizedAccountId = accountId?.trim();
83552
+ const matches = getRoutesForChannel(channel).filter((route) => route.chatId === chatId && route.agentId === agentId && route.conversationId === conversationId && (!normalizedAccountId || (route.accountId ?? LEGACY_CHANNEL_ACCOUNT_ID) === normalizedAccountId) && route.enabled);
83140
83553
  if (matches.length !== 1) {
83141
83554
  return null;
83142
83555
  }
@@ -83730,6 +84143,7 @@ async function executeSingleDecision(decision, onChunk, options) {
83730
84143
  toolCallId: decision.approval.toolCallId,
83731
84144
  toolContextId: options?.toolContextId,
83732
84145
  parentScope: options?.parentScope,
84146
+ channelTurnSources: options?.channelTurnSources,
83733
84147
  onOutput: options?.onStreamingOutput ? (chunk, stream) => options.onStreamingOutput?.(decision.approval.toolCallId, chunk, stream === "stderr") : undefined,
83734
84148
  onFileWrite: options?.onFileWrite
83735
84149
  });
@@ -90339,8 +90753,7 @@ var init_readOnlyShell = __esm(() => {
90339
90753
  memory: new Set(["status", "help", "backups", "export", "tokens"]),
90340
90754
  memfs: new Set(["status", "help", "backups", "export", "tokens"]),
90341
90755
  agents: new Set(["list", "help"]),
90342
- messages: new Set(["search", "list", "help"]),
90343
- blocks: new Set(["list", "help"])
90756
+ messages: new Set(["search", "list", "help"])
90344
90757
  };
90345
90758
  SAFE_GH_COMMANDS = {
90346
90759
  pr: new Set(["list", "status", "checks", "diff", "view"]),
@@ -90817,7 +91230,7 @@ async function initSecretsFromServer(agentId, cachedAgent) {
90817
91230
  setCache(agentId, {});
90818
91231
  return;
90819
91232
  }
90820
- const agent = cachedAgent ?? await (await getClient()).agents.retrieve(agentId, {
91233
+ const agent = cachedAgent ?? await getBackend().retrieveAgent(agentId, {
90821
91234
  include: ["agent.secrets"]
90822
91235
  });
90823
91236
  const secrets2 = {};
@@ -90850,6 +91263,9 @@ async function refreshAndListSecrets(agentIdArg) {
90850
91263
  return Object.keys(cache3).sort().map((key) => ({ key, value: cache3[key] ?? "" }));
90851
91264
  }
90852
91265
  async function applySecretBatch(options, agentIdArg) {
91266
+ if (!getBackend().capabilities.serverSecrets) {
91267
+ throw new Error("Agent secrets are not supported by this backend yet");
91268
+ }
90853
91269
  const agentId = resolveSecretsAgentId(agentIdArg);
90854
91270
  if (!agentId) {
90855
91271
  throw new Error("No agent context set. Agent ID is required.");
@@ -90861,8 +91277,7 @@ async function applySecretBatch(options, agentIdArg) {
90861
91277
  for (const rawKey of options.unset ?? []) {
90862
91278
  delete next[rawKey.toUpperCase()];
90863
91279
  }
90864
- const client = await getClient();
90865
- await client.agents.update(agentId, { secrets: next });
91280
+ await getBackend().updateAgent(agentId, { secrets: next });
90866
91281
  setCache(agentId, next);
90867
91282
  return Object.keys(next).sort();
90868
91283
  }
@@ -90870,14 +91285,13 @@ async function setSecretOnServer(key, value, agentIdArg) {
90870
91285
  if (!getBackend().capabilities.serverSecrets) {
90871
91286
  throw new Error("Agent secrets are not supported by this backend yet");
90872
91287
  }
90873
- const client = await getClient();
90874
91288
  const agentId = resolveSecretsAgentId(agentIdArg);
90875
91289
  if (!agentId) {
90876
91290
  throw new Error("No agent context set. Agent ID is required.");
90877
91291
  }
90878
91292
  const secrets2 = { ...loadSecrets(agentId) };
90879
91293
  secrets2[key] = value;
90880
- await client.agents.update(agentId, { secrets: secrets2 });
91294
+ await getBackend().updateAgent(agentId, { secrets: secrets2 });
90881
91295
  setCache(agentId, secrets2);
90882
91296
  }
90883
91297
  async function deleteSecretOnServer(key, agentIdArg) {
@@ -90893,8 +91307,7 @@ async function deleteSecretOnServer(key, agentIdArg) {
90893
91307
  return false;
90894
91308
  }
90895
91309
  delete secrets2[key];
90896
- const client = await getClient();
90897
- await client.agents.update(agentId, { secrets: secrets2 });
91310
+ await getBackend().updateAgent(agentId, { secrets: secrets2 });
90898
91311
  setCache(agentId, secrets2);
90899
91312
  return true;
90900
91313
  }
@@ -90910,7 +91323,6 @@ var SECRETS_CACHE_KEY;
90910
91323
  var init_secretsStore = __esm(() => {
90911
91324
  init_context();
90912
91325
  init_backend2();
90913
- init_client2();
90914
91326
  SECRETS_CACHE_KEY = Symbol.for("@letta/secretsCache");
90915
91327
  });
90916
91328
 
@@ -93684,7 +94096,9 @@ function getShellEnv() {
93684
94096
  env.LETTA_AGENT_ID = agentId;
93685
94097
  env.AGENT_ID = agentId;
93686
94098
  try {
93687
- if (settingsManager.isMemfsEnabled(agentId) || process.env.LETTA_LOCAL_BACKEND_EXPERIMENTAL === "1" || process.env.LETTA_LOCAL_BACKEND_EXPERIMENTAL?.toLowerCase() === "true") {
94099
+ const localBackendNoMemfs = isLocalBackendNoMemfsEnvEnabled();
94100
+ const localBackendEnabled = process.env.LETTA_LOCAL_BACKEND_EXPERIMENTAL === "1" || process.env.LETTA_LOCAL_BACKEND_EXPERIMENTAL?.toLowerCase() === "true";
94101
+ if (!localBackendNoMemfs && (settingsManager.isMemfsEnabled(agentId) || localBackendEnabled)) {
93688
94102
  const memoryDir = resolveScopedMemoryDir({ agentId });
93689
94103
  if (!memoryDir) {
93690
94104
  throw new Error("Unable to resolve memory directory");
@@ -93752,6 +94166,7 @@ var init_shellEnv = __esm(() => {
93752
94166
  init_context();
93753
94167
  init_memoryFilesystem();
93754
94168
  init_client2();
94169
+ init_paths();
93755
94170
  init_runtime_context();
93756
94171
  init_settings_manager();
93757
94172
  });
@@ -96872,6 +97287,25 @@ function buildSyntheticChannelRoute(params) {
96872
97287
  updatedAt: now2
96873
97288
  };
96874
97289
  }
97290
+ function inferAccountIdFromChannelTurnSources(params) {
97291
+ const chatId = params.input.chatId;
97292
+ if (!chatId) {
97293
+ return;
97294
+ }
97295
+ const accountIds = new Set;
97296
+ for (const source of params.channelTurnSources ?? []) {
97297
+ if (source.channel !== params.input.channel || source.chatId !== chatId || source.agentId !== params.scope.agentId || source.conversationId !== params.scope.conversationId) {
97298
+ continue;
97299
+ }
97300
+ if (params.input.threadId !== undefined && (source.threadId ?? null) !== (params.input.threadId ?? null)) {
97301
+ continue;
97302
+ }
97303
+ if (source.accountId?.trim()) {
97304
+ accountIds.add(source.accountId.trim());
97305
+ }
97306
+ }
97307
+ return accountIds.size === 1 ? [...accountIds][0] : undefined;
97308
+ }
96875
97309
  async function resolveExplicitMessageChannelContext(params) {
96876
97310
  if (params.input.channel !== "slack") {
96877
97311
  return `Error: Explicit MessageChannel targets are not supported on ${params.input.channel}.`;
@@ -96922,9 +97356,14 @@ async function message_channel(args) {
96922
97356
  try {
96923
97357
  let executionContext;
96924
97358
  if (input.chatId) {
96925
- const route2 = registry2.getRouteForScope(input.channel, input.chatId, scope.agentId, scope.conversationId);
97359
+ const resolvedAccountId = input.accountId ?? inferAccountIdFromChannelTurnSources({
97360
+ input,
97361
+ scope,
97362
+ channelTurnSources: args.channelTurnSources
97363
+ });
97364
+ const route2 = registry2.getRouteForScope(input.channel, input.chatId, scope.agentId, scope.conversationId, resolvedAccountId);
96926
97365
  if (!route2) {
96927
- return `Error: No route for chat_id "${input.chatId}" on "${input.channel}" for this agent/conversation.`;
97366
+ return resolvedAccountId ? `Error: No route for chat_id "${input.chatId}" on "${input.channel}" account "${resolvedAccountId}" for this agent/conversation.` : `Error: No route for chat_id "${input.chatId}" on "${input.channel}" for this agent/conversation. If multiple channel accounts can receive this chat, pass accountId (from the channel notification's account_id) to disambiguate.`;
96928
97367
  }
96929
97368
  const adapter2 = registry2.getAdapter(input.channel, route2.accountId);
96930
97369
  if (!adapter2) {
@@ -110712,6 +111151,12 @@ var init_SearchFileContentGemini2 = __esm(() => {
110712
111151
  // src/agent/skills.ts
110713
111152
  var exports_skills = {};
110714
111153
  __export(exports_skills, {
111154
+ isUserInvocableSkill: () => isUserInvocableSkill,
111155
+ isModelInvocableSkill: () => isModelInvocableSkill,
111156
+ getLegacyAgentSkillsDir: () => getLegacyAgentSkillsDir,
111157
+ getFrontmatterStringList: () => getFrontmatterStringList,
111158
+ getFrontmatterString: () => getFrontmatterString,
111159
+ getFrontmatterBoolean: () => getFrontmatterBoolean,
110715
111160
  getBundledSkills: () => getBundledSkills,
110716
111161
  getAgentSkillsDir: () => getAgentSkillsDir,
110717
111162
  formatSkillsAsSystemReminder: () => formatSkillsAsSystemReminder,
@@ -110734,7 +111179,45 @@ function getBundledSkillsPath() {
110734
111179
  function compareSkills(a, b) {
110735
111180
  return a.id.localeCompare(b.id) || a.source.localeCompare(b.source) || a.path.localeCompare(b.path);
110736
111181
  }
111182
+ function stripSurroundingQuotes(value) {
111183
+ const trimmed = value.trim();
111184
+ if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
111185
+ return trimmed.slice(1, -1);
111186
+ }
111187
+ return trimmed;
111188
+ }
111189
+ function getFrontmatterString(frontmatter, key) {
111190
+ const value = frontmatter[key];
111191
+ return typeof value === "string" ? stripSurroundingQuotes(value) : undefined;
111192
+ }
111193
+ function getFrontmatterStringList(frontmatter, key) {
111194
+ const value = frontmatter[key];
111195
+ if (Array.isArray(value)) {
111196
+ return value.map(stripSurroundingQuotes).filter((item) => item.length > 0);
111197
+ }
111198
+ if (typeof value === "string") {
111199
+ return stripSurroundingQuotes(value).split(/\s+/).map((item) => item.trim()).filter((item) => item.length > 0);
111200
+ }
111201
+ return;
111202
+ }
111203
+ function getFrontmatterBoolean(frontmatter, key) {
111204
+ const value = getFrontmatterString(frontmatter, key)?.toLowerCase();
111205
+ if (value === "true")
111206
+ return true;
111207
+ if (value === "false")
111208
+ return false;
111209
+ return;
111210
+ }
111211
+ function isModelInvocableSkill(skill) {
111212
+ return skill.disableModelInvocation !== true;
111213
+ }
111214
+ function isUserInvocableSkill(skill) {
111215
+ return skill.userInvocable !== false;
111216
+ }
110737
111217
  function getAgentSkillsDir(agentId) {
111218
+ return join25(process.env.HOME || process.env.USERPROFILE || "~", ".letta/agents", agentId, "memory/skills");
111219
+ }
111220
+ function getLegacyAgentSkillsDir(agentId) {
110738
111221
  return join25(process.env.HOME || process.env.USERPROFILE || "~", ".letta/agents", agentId, "skills");
110739
111222
  }
110740
111223
  async function getBundledSkills() {
@@ -110777,6 +111260,12 @@ async function discoverSkills(projectSkillsPath = join25(process.cwd(), SKILLS_D
110777
111260
  }
110778
111261
  }
110779
111262
  if (agentId && includeSource("agent")) {
111263
+ const legacyDir = getLegacyAgentSkillsDir(agentId);
111264
+ const legacyResult = await discoverSkillsFromDir(legacyDir, "agent");
111265
+ allErrors.push(...legacyResult.errors);
111266
+ for (const skill of legacyResult.skills) {
111267
+ skillsById.set(skill.id, skill);
111268
+ }
110780
111269
  const agentSkillsDir = getAgentSkillsDir(agentId);
110781
111270
  const agentResult = await discoverSkillsFromDir(agentSkillsDir, "agent");
110782
111271
  allErrors.push(...agentResult.errors);
@@ -110860,7 +111349,7 @@ async function parseSkillFile(filePath, rootPath, source) {
110860
111349
  const defaultId = dirPath || "root";
110861
111350
  const id = (typeof frontmatter.id === "string" ? frontmatter.id : null) || defaultId;
110862
111351
  const name25 = (typeof frontmatter.name === "string" ? frontmatter.name : null) || (typeof frontmatter.title === "string" ? frontmatter.title : null) || (id.split("/").pop() ?? "").replace(/-/g, " ").replace(/\b\w/g, (l) => l.toUpperCase());
110863
- let description = typeof frontmatter.description === "string" ? frontmatter.description : null;
111352
+ let description = getFrontmatterString(frontmatter, "description") ?? null;
110864
111353
  if (!description) {
110865
111354
  const firstParagraph = body.trim().split(`
110866
111355
 
@@ -110868,30 +111357,31 @@ async function parseSkillFile(filePath, rootPath, source) {
110868
111357
  description = firstParagraph || "No description available";
110869
111358
  }
110870
111359
  description = description.trim();
110871
- if (description.startsWith('"') && description.endsWith('"') || description.startsWith("'") && description.endsWith("'")) {
110872
- description = description.slice(1, -1);
110873
- }
110874
- let tags;
110875
- if (Array.isArray(frontmatter.tags)) {
110876
- tags = frontmatter.tags;
110877
- } else if (typeof frontmatter.tags === "string") {
110878
- tags = [frontmatter.tags];
110879
- }
111360
+ const whenToUse = getFrontmatterString(frontmatter, "when_to_use")?.trim();
111361
+ const modelDescription = whenToUse ? `${description}
111362
+
111363
+ When to use: ${whenToUse}` : description;
111364
+ const tags = getFrontmatterStringList(frontmatter, "tags");
110880
111365
  return {
110881
111366
  id,
110882
111367
  name: name25,
110883
- description,
110884
- category: typeof frontmatter.category === "string" ? frontmatter.category : undefined,
111368
+ description: modelDescription,
111369
+ whenToUse,
111370
+ argumentHint: getFrontmatterString(frontmatter, "argument-hint"),
111371
+ arguments: getFrontmatterStringList(frontmatter, "arguments"),
111372
+ disableModelInvocation: getFrontmatterBoolean(frontmatter, "disable-model-invocation") ?? false,
111373
+ userInvocable: getFrontmatterBoolean(frontmatter, "user-invocable") ?? true,
111374
+ category: getFrontmatterString(frontmatter, "category"),
110885
111375
  tags,
110886
111376
  path: filePath,
110887
111377
  source
110888
111378
  };
110889
111379
  }
110890
111380
  function formatSkillsAsSystemReminder(skills) {
110891
- if (skills.length === 0) {
111381
+ const lines = skills.filter(isModelInvocableSkill).sort(compareSkills).map((s) => `- ${s.id} (${s.source}): ${s.description}`);
111382
+ if (lines.length === 0) {
110892
111383
  return "";
110893
111384
  }
110894
- const lines = [...skills].sort(compareSkills).map((s) => `- ${s.id} (${s.source}): ${s.description}`);
110895
111385
  return `<system-reminder>
110896
111386
  The following skills are available for use with the Skill tool:
110897
111387
 
@@ -110925,6 +111415,15 @@ var init_skillContentRegistry = __esm(() => {
110925
111415
  });
110926
111416
 
110927
111417
  // src/tools/impl/Skill.ts
111418
+ var exports_Skill = {};
111419
+ __export(exports_Skill, {
111420
+ wrapSkillContent: () => wrapSkillContent,
111421
+ skill: () => skill,
111422
+ renderSkillContent: () => renderSkillContent,
111423
+ readSkillContent: () => readSkillContent,
111424
+ loadRenderedSkillContent: () => loadRenderedSkillContent,
111425
+ getResolvedSkillsDir: () => getResolvedSkillsDir
111426
+ });
110928
111427
  import { existsSync as existsSync23, readdirSync as readdirSync7 } from "node:fs";
110929
111428
  import { readFile as readFile10 } from "node:fs/promises";
110930
111429
  import { dirname as dirname12, join as join26 } from "node:path";
@@ -111009,6 +111508,83 @@ function getResolvedAgentId(args) {
111009
111508
  return;
111010
111509
  }
111011
111510
  }
111511
+ function splitSkillArguments(args) {
111512
+ const parts = [];
111513
+ const pattern = /"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|\S+/g;
111514
+ for (const match2 of args.matchAll(pattern)) {
111515
+ const raw = match2[1] ?? match2[2] ?? match2[0];
111516
+ parts.push(raw.replace(/\\(["'\\])/g, "$1"));
111517
+ }
111518
+ return parts;
111519
+ }
111520
+ function substituteSkillArguments(content, args, argumentNames) {
111521
+ const rawArgs = args?.trim() ?? "";
111522
+ if (!rawArgs) {
111523
+ return content;
111524
+ }
111525
+ const argParts = splitSkillArguments(rawArgs);
111526
+ let result = content;
111527
+ let substituted = false;
111528
+ result = result.replace(/\$ARGUMENTS\[(\d+)\]/g, (_match, index) => {
111529
+ substituted = true;
111530
+ return argParts[Number(index)] ?? "";
111531
+ });
111532
+ result = result.replace(/\$ARGUMENTS/g, () => {
111533
+ substituted = true;
111534
+ return rawArgs;
111535
+ });
111536
+ result = result.replace(/\$(\d+)\b/g, (_match, index) => {
111537
+ substituted = true;
111538
+ return argParts[Number(index)] ?? "";
111539
+ });
111540
+ for (const [index, name25] of (argumentNames ?? []).entries()) {
111541
+ const escapedName = name25.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
111542
+ const namePattern = new RegExp(`\\$${escapedName}\\b`, "g");
111543
+ result = result.replace(namePattern, () => {
111544
+ substituted = true;
111545
+ return argParts[index] ?? "";
111546
+ });
111547
+ }
111548
+ if (!substituted) {
111549
+ result = `${result.trimEnd()}
111550
+
111551
+ ARGUMENTS: ${rawArgs}`;
111552
+ }
111553
+ return result;
111554
+ }
111555
+ function renderSkillContent(skillName, skillContent, skillPath, options = {}) {
111556
+ const { frontmatter } = parseFrontmatter(skillContent);
111557
+ if (!options.allowDisabledModelInvocation && getFrontmatterBoolean(frontmatter, "disable-model-invocation") === true) {
111558
+ throw new Error(`Skill "${skillName}" is marked disable-model-invocation and can only be invoked directly by the user.`);
111559
+ }
111560
+ const skillDir = dirname12(skillPath);
111561
+ const hasExtras = hasAdditionalFiles(skillPath);
111562
+ const argumentNames = getFrontmatterStringList(frontmatter, "arguments");
111563
+ const withSkillDir = skillContent.replace(/<SKILL_DIR>/g, skillDir).replace(/\$\{CLAUDE_SKILL_DIR\}/g, skillDir);
111564
+ const withArguments = substituteSkillArguments(withSkillDir, options.args, argumentNames);
111565
+ const dirHeader = hasExtras ? `# Skill Directory: ${skillDir}
111566
+
111567
+ ` : "";
111568
+ return `${dirHeader}${withArguments}`;
111569
+ }
111570
+ async function loadRenderedSkillContent(skillName, options = {}) {
111571
+ const skillsDir = options.skillsDir ?? await getResolvedSkillsDir();
111572
+ const { content: skillContent, path: skillPath } = await readSkillContent(skillName, skillsDir, options.agentId);
111573
+ return renderSkillContent(skillName, skillContent, skillPath, options);
111574
+ }
111575
+ function escapeXmlAttribute2(value) {
111576
+ return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
111577
+ }
111578
+ function wrapSkillContent(skillName, content) {
111579
+ if (/^[A-Za-z_][A-Za-z0-9_.-]*$/.test(skillName)) {
111580
+ return `<${skillName}>
111581
+ ${content}
111582
+ </${skillName}>`;
111583
+ }
111584
+ return `<skill name="${escapeXmlAttribute2(skillName)}">
111585
+ ${content}
111586
+ </skill>`;
111587
+ }
111012
111588
  async function skill(args) {
111013
111589
  validateRequiredParams(args, ["skill"], "Skill");
111014
111590
  const { skill: skillName, toolCallId } = args;
@@ -111018,18 +111594,13 @@ async function skill(args) {
111018
111594
  try {
111019
111595
  const agentId = getResolvedAgentId(args);
111020
111596
  const skillsDir = await getResolvedSkillsDir();
111021
- const { content: skillContent, path: skillPath } = await readSkillContent(skillName, skillsDir, agentId);
111022
- const skillDir = dirname12(skillPath);
111023
- const hasExtras = hasAdditionalFiles(skillPath);
111024
- const processedContent = hasExtras ? skillContent.replace(/<SKILL_DIR>/g, skillDir) : skillContent;
111025
- const dirHeader = hasExtras ? `# Skill Directory: ${skillDir}
111026
-
111027
- ` : "";
111028
- const fullContent = `${dirHeader}${processedContent}`;
111597
+ const fullContent = await loadRenderedSkillContent(skillName, {
111598
+ agentId,
111599
+ skillsDir,
111600
+ args: args.args
111601
+ });
111029
111602
  if (toolCallId) {
111030
- queueSkillContent(toolCallId, `<${skillName}>
111031
- ${fullContent}
111032
- </${skillName}>`);
111603
+ queueSkillContent(toolCallId, wrapSkillContent(skillName, fullContent));
111033
111604
  }
111034
111605
  return { message: `Launching skill: ${skillName}` };
111035
111606
  } catch (error51) {
@@ -111415,18 +111986,21 @@ var init_contextBudget = __esm(() => {
111415
111986
  // src/agent/subagents/manager.ts
111416
111987
  import { spawn as spawn5 } from "node:child_process";
111417
111988
  function getModelHandleFromAgent(agent) {
111989
+ const directModel = agent.model;
111990
+ if (directModel?.includes("/")) {
111991
+ return directModel;
111992
+ }
111418
111993
  const endpoint = agent.llm_config?.model_endpoint_type;
111419
111994
  const model = agent.llm_config?.model;
111420
111995
  if (endpoint && model) {
111421
111996
  return `${endpoint}/${model}`;
111422
111997
  }
111423
- return model || null;
111998
+ return directModel || model || null;
111424
111999
  }
111425
112000
  async function getPrimaryAgentModelHandle() {
111426
112001
  try {
111427
112002
  const agentId = getCurrentAgentId();
111428
- const client = await getClient();
111429
- const agent = await client.agents.retrieve(agentId);
112003
+ const agent = await getBackend().retrieveAgent(agentId);
111430
112004
  return { handle: getModelHandleFromAgent(agent), agent };
111431
112005
  } catch {
111432
112006
  return { handle: null, agent: null };
@@ -111696,6 +112270,8 @@ function resolveSubagentLauncher(cliArgs, options = {}) {
111696
112270
  function composeSubagentChildEnv(options) {
111697
112271
  const {
111698
112272
  parentProcessEnv,
112273
+ backendMode,
112274
+ localBackendStorageDir,
111699
112275
  parentAgentId,
111700
112276
  permissionMode: permissionMode2,
111701
112277
  inheritedPrimaryRoot,
@@ -111709,6 +112285,14 @@ function composeSubagentChildEnv(options) {
111709
112285
  LETTA_CODE_AGENT_ROLE: "subagent",
111710
112286
  ...parentAgentId && { LETTA_PARENT_AGENT_ID: parentAgentId }
111711
112287
  };
112288
+ if (backendMode === "local") {
112289
+ childEnv.LETTA_LOCAL_BACKEND_EXPERIMENTAL = "1";
112290
+ if (localBackendStorageDir) {
112291
+ childEnv.LETTA_LOCAL_BACKEND_DIR = localBackendStorageDir;
112292
+ }
112293
+ } else if (backendMode === "api") {
112294
+ childEnv.LETTA_LOCAL_BACKEND_EXPERIMENTAL = "0";
112295
+ }
111712
112296
  const nextScope = new Set([
111713
112297
  ...parseScopeList(parentProcessEnv.LETTA_MEMORY_SCOPE),
111714
112298
  ...cliPermissions.getMemoryScope()
@@ -111732,6 +112316,12 @@ function composeSubagentChildEnv(options) {
111732
112316
  }
111733
112317
  return childEnv;
111734
112318
  }
112319
+ function resolveSubagentInheritedPrimaryRoot(options) {
112320
+ if (options.backendMode === "local" && options.parentAgentId) {
112321
+ return getLocalBackendMemoryFilesystemRoot(options.parentAgentId, options.localBackendStorageDir ?? getLocalBackendStorageDir2());
112322
+ }
112323
+ return options.inheritedPrimaryRoot;
112324
+ }
111735
112325
  function getReflectionStartupNotice() {
111736
112326
  return `[Reflection startup context truncated: system prompt + initial message are capped at ~${REFLECTION_STARTUP_CONTEXT_TOKEN_LIMIT.toLocaleString()} estimated tokens. Some parent memory preview content was omitted; read files directly from MEMORY_DIR if needed.]`;
111737
112327
  }
@@ -111794,9 +112384,12 @@ ${userPrompt}`);
111794
112384
  }
111795
112385
  return hardTruncateReflectionPrompt(userPrompt, allowedPromptChars);
111796
112386
  }
111797
- function buildSubagentArgs(type, config2, model, userPrompt, existingAgentId, existingConversationId, maxTurns) {
112387
+ function buildSubagentArgs(type, config2, model, userPrompt, existingAgentId, existingConversationId, maxTurns, options = {}) {
111798
112388
  const args = [];
111799
112389
  const isDeployingExisting = Boolean(existingAgentId || existingConversationId);
112390
+ if (options.backendMode) {
112391
+ args.push("--backend", options.backendMode);
112392
+ }
111800
112393
  if (isDeployingExisting) {
111801
112394
  if (existingConversationId) {
111802
112395
  args.push("--conv", existingConversationId);
@@ -111868,7 +112461,9 @@ async function executeSubagent(type, config2, model, userPrompt, baseURL, subage
111868
112461
  updateSubagent(subagentId, { model });
111869
112462
  }
111870
112463
  try {
111871
- const cliArgs = buildSubagentArgs(type, config2, model, userPrompt, existingAgentId, existingConversationId, maxTurns);
112464
+ const activeBackend = getBackend();
112465
+ const backendMode = activeBackend.capabilities.localMemfs ? "local" : "api";
112466
+ const cliArgs = buildSubagentArgs(type, config2, model, userPrompt, existingAgentId, existingConversationId, maxTurns, { backendMode });
111872
112467
  const launcher = resolveSubagentLauncher(cliArgs);
111873
112468
  let parentAgentId = parentAgentIdOverride;
111874
112469
  if (!parentAgentId) {
@@ -111882,19 +112477,28 @@ async function executeSubagent(type, config2, model, userPrompt, baseURL, subage
111882
112477
  const inheritedMemoryRoots = resolveAllowedMemoryRoots({
111883
112478
  currentAgentId: parentAgentId ?? null
111884
112479
  });
112480
+ const localBackendStorageDir = backendMode === "local" ? getLocalBackendStorageDir2() : null;
112481
+ const inheritedPrimaryRoot = resolveSubagentInheritedPrimaryRoot({
112482
+ backendMode,
112483
+ parentAgentId,
112484
+ inheritedPrimaryRoot: inheritedMemoryRoots.primaryRoot,
112485
+ localBackendStorageDir
112486
+ });
111885
112487
  const subagentWorkingDirectory = resolveSubagentWorkingDirectory(process.env, getCurrentWorkingDirectory(), {
111886
112488
  subagentType: type,
111887
112489
  permissionMode: config2.permissionMode,
111888
- inheritedPrimaryRoot: inheritedMemoryRoots.primaryRoot
112490
+ inheritedPrimaryRoot
111889
112491
  });
111890
112492
  const childEnv = composeSubagentChildEnv({
111891
112493
  parentProcessEnv: {
111892
112494
  ...process.env,
111893
112495
  USER_CWD: subagentWorkingDirectory
111894
112496
  },
112497
+ backendMode,
112498
+ localBackendStorageDir,
111895
112499
  parentAgentId,
111896
112500
  permissionMode: config2.permissionMode,
111897
- inheritedPrimaryRoot: inheritedMemoryRoots.primaryRoot,
112501
+ inheritedPrimaryRoot,
111898
112502
  inheritedApiKey,
111899
112503
  inheritedBaseUrl
111900
112504
  });
@@ -112080,7 +112684,7 @@ async function spawnSubagent(type, prompt, userModel, subagentId, signal, existi
112080
112684
  let finalPrompt = prompt;
112081
112685
  if (isDeployingExisting && resolvedParentAgentId) {
112082
112686
  try {
112083
- const cachedParent = parentAgent ?? await (await getClient()).agents.retrieve(resolvedParentAgentId);
112687
+ const cachedParent = parentAgent ?? await getBackend().retrieveAgent(resolvedParentAgentId);
112084
112688
  if (forkedContext) {
112085
112689
  const systemReminder = buildForkSystemReminder(type);
112086
112690
  finalPrompt = systemReminder + prompt;
@@ -112095,8 +112699,9 @@ async function spawnSubagent(type, prompt, userModel, subagentId, signal, existi
112095
112699
  }
112096
112700
  var BYOK_PROVIDER_TO_BASE;
112097
112701
  var init_manager3 = __esm(() => {
112098
- init_client2();
112702
+ init_backend2();
112099
112703
  init_metadata();
112704
+ init_paths();
112100
112705
  init_subagentState();
112101
112706
  init_constants();
112102
112707
  init_cli();
@@ -112124,22 +112729,6 @@ var init_manager3 = __esm(() => {
112124
112729
  };
112125
112730
  });
112126
112731
 
112127
- // src/backend/api/conversations.ts
112128
- var exports_conversations = {};
112129
- __export(exports_conversations, {
112130
- forkConversation: () => forkConversation
112131
- });
112132
- async function forkConversation(conversationId, options = {}) {
112133
- const query = {
112134
- ...options.agentId ? { agent_id: options.agentId } : {},
112135
- ...options.hidden !== undefined ? { hidden: options.hidden } : {}
112136
- };
112137
- return apiRequest("POST", `/v1/conversations/${encodeURIComponent(conversationId)}/fork`, undefined, { query });
112138
- }
112139
- var init_conversations2 = __esm(() => {
112140
- init_request();
112141
- });
112142
-
112143
112732
  // src/cli/helpers/messageQueueBridge.ts
112144
112733
  function setMessageQueueAdder(fn) {
112145
112734
  queueAdder = fn;
@@ -112557,7 +113146,7 @@ async function task(args) {
112557
113146
  try {
112558
113147
  const parentAgentId = getCurrentAgentId();
112559
113148
  const parentConvId = getConversationId() ?? "default";
112560
- const forkedConv = await forkConversation(parentConvId, {
113149
+ const forkedConv = await getBackend().forkConversation(parentConvId, {
112561
113150
  ...parentConvId === "default" ? { agentId: parentAgentId } : {},
112562
113151
  hidden: true
112563
113152
  });
@@ -112655,7 +113244,7 @@ var init_Task2 = __esm(() => {
112655
113244
  init_context();
112656
113245
  init_subagents();
112657
113246
  init_manager3();
112658
- init_conversations2();
113247
+ init_backend2();
112659
113248
  init_messageQueueBridge();
112660
113249
  init_subagentState();
112661
113250
  init_hooks();
@@ -116900,7 +117489,7 @@ function detectSkillScript(command, workingDir) {
116900
117489
  if (projectSkill) {
116901
117490
  return projectSkill;
116902
117491
  }
116903
- const agentRegex = new RegExp(`^${escapeRegex4(normalizedHomeDir)}/\\.letta/agents/[^/]+/skills/(.+?)/scripts/`);
117492
+ const agentRegex = new RegExp(`^${escapeRegex4(normalizedHomeDir)}/\\.letta/agents/[^/]+/(?:memory/)?skills/(.+?)/scripts/`);
116904
117493
  const agentSkill = detect("agent-scoped", agentRegex);
116905
117494
  if (agentSkill) {
116906
117495
  return agentSkill;
@@ -117773,14 +118362,14 @@ function clipToolReturn(text2, maxLines = 3, maxChars = 300) {
117773
118362
  }
117774
118363
  return clipped;
117775
118364
  }
117776
- function isRecord7(value) {
118365
+ function isRecord8(value) {
117777
118366
  return typeof value === "object" && value !== null;
117778
118367
  }
117779
118368
  function isStringArray2(value) {
117780
118369
  return Array.isArray(value) && value.every((item) => typeof item === "string");
117781
118370
  }
117782
118371
  function isMultimodalContent(arr) {
117783
- return arr.every((item) => isRecord7(item) && (item.type === "text" || item.type === "image"));
118372
+ return arr.every((item) => isRecord8(item) && (item.type === "text" || item.type === "image"));
117784
118373
  }
117785
118374
  function flattenToolResponse(result) {
117786
118375
  if (result === null || result === undefined) {
@@ -117789,7 +118378,7 @@ function flattenToolResponse(result) {
117789
118378
  if (typeof result === "string") {
117790
118379
  return result;
117791
118380
  }
117792
- if (!isRecord7(result)) {
118381
+ if (!isRecord8(result)) {
117793
118382
  return JSON.stringify(result);
117794
118383
  }
117795
118384
  if (typeof result.message === "string") {
@@ -117806,7 +118395,7 @@ function flattenToolResponse(result) {
117806
118395
  return result.content;
117807
118396
  }
117808
118397
  if (Array.isArray(result.content)) {
117809
- const textContent2 = result.content.filter((item) => isRecord7(item) && item.type === "text" && typeof item.text === "string").map((item) => item.text).join(`
118398
+ const textContent2 = result.content.filter((item) => isRecord8(item) && item.type === "text" && typeof item.text === "string").map((item) => item.text).join(`
117810
118399
  `);
117811
118400
  if (textContent2) {
117812
118401
  return textContent2;
@@ -117921,8 +118510,16 @@ async function executeTool2(name25, args, options) {
117921
118510
  if (internalName === "Skill" && options?.parentScope) {
117922
118511
  enhancedArgs = { ...enhancedArgs, parentScope: options.parentScope };
117923
118512
  }
117924
- if (internalName === "MessageChannel" && options?.parentScope) {
117925
- enhancedArgs = { ...enhancedArgs, parentScope: options.parentScope };
118513
+ if (internalName === "MessageChannel") {
118514
+ if (options?.parentScope) {
118515
+ enhancedArgs = { ...enhancedArgs, parentScope: options.parentScope };
118516
+ }
118517
+ if (options?.channelTurnSources?.length) {
118518
+ enhancedArgs = {
118519
+ ...enhancedArgs,
118520
+ channelTurnSources: options.channelTurnSources
118521
+ };
118522
+ }
117926
118523
  }
117927
118524
  const PLAN_MODE_TOOL_NAMES = new Set([
117928
118525
  "EnterPlanMode",
@@ -117949,7 +118546,7 @@ async function executeTool2(name25, args, options) {
117949
118546
  } catch {}
117950
118547
  }
117951
118548
  }
117952
- const recordResult = isRecord7(result) ? result : undefined;
118549
+ const recordResult = isRecord8(result) ? result : undefined;
117953
118550
  const stdoutValue = recordResult?.stdout;
117954
118551
  const stderrValue = recordResult?.stderr;
117955
118552
  const stdout = isStringArray2(stdoutValue) ? stdoutValue : undefined;
@@ -118683,9 +119280,8 @@ async function hydrateMemfsSettingFromAgent(agent) {
118683
119280
  return enabled;
118684
119281
  }
118685
119282
  async function isMemfsEnabledOnServer(agentId) {
118686
- const { getClient: getClient2 } = await Promise.resolve().then(() => (init_client2(), exports_client));
118687
- const client = await getClient2();
118688
- const agent = await client.agents.retrieve(agentId, {
119283
+ const { getBackend: getBackend2 } = await Promise.resolve().then(() => (init_backend2(), exports_backend));
119284
+ const agent = await getBackend2().retrieveAgent(agentId, {
118689
119285
  include: ["agent.tags"]
118690
119286
  });
118691
119287
  const { GIT_MEMORY_ENABLED_TAG: GIT_MEMORY_ENABLED_TAG2 } = await Promise.resolve().then(() => (init_memoryGit(), exports_memoryGit));
@@ -118818,7 +119414,8 @@ async function applyMemfsFlags(agentId, memfsFlag, noMemfsFlag, options) {
118818
119414
  const backend = getBackend2();
118819
119415
  if (backend.capabilities.localMemfs) {
118820
119416
  if (noMemfsFlag) {
118821
- throw new Error("Disabling MemFS is not supported by the local backend.");
119417
+ settingsManager2.setMemfsEnabled(agentId, false);
119418
+ return { action: "disabled" };
118822
119419
  }
118823
119420
  const memoryDir = getScopedMemoryFilesystemRoot(agentId);
118824
119421
  const { initializeLocalMemoryRepo: initializeLocalMemoryRepo2 } = await Promise.resolve().then(() => (init_memoryGit(), exports_memoryGit));
@@ -119274,7 +119871,7 @@ ${skillsBlock.trimStart()}`;
119274
119871
  function compileLocalSystemPrompt(options) {
119275
119872
  const compiledAt = options.now ?? new Date;
119276
119873
  const memoryDir = options.memoryDir ?? getMemoryFilesystemRoot(options.agent.id);
119277
- const memfs = renderMemfsProjection(memoryDir);
119874
+ const memfs = options.includeMemfs === false ? { content: "", revision: undefined } : renderMemfsProjection(memoryDir);
119278
119875
  const metadata = compileMemoryMetadata({
119279
119876
  agentId: options.agent.id,
119280
119877
  conversationId: options.conversationId,
@@ -119346,6 +119943,37 @@ function initialMemoryFilesFromCreateBody(body) {
119346
119943
  }
119347
119944
  return [...files.values()].sort((a, b) => a.relativePath.localeCompare(b.relativePath));
119348
119945
  }
119946
+ function isRecord9(value) {
119947
+ return typeof value === "object" && value !== null && !Array.isArray(value);
119948
+ }
119949
+ function compactionSettingsRecord(value) {
119950
+ if (value === null)
119951
+ return null;
119952
+ return isRecord9(value) ? { ...value } : undefined;
119953
+ }
119954
+ function hasOwn2(record2, key) {
119955
+ return Object.hasOwn(record2, key);
119956
+ }
119957
+ function localCompactionMode(value) {
119958
+ if (value === "all" || value === "sliding_window")
119959
+ return value;
119960
+ return;
119961
+ }
119962
+ function validateLocalCompactionSettingsRecord(settings) {
119963
+ if (settings.mode === undefined || settings.mode === null)
119964
+ return;
119965
+ if (!localCompactionMode(settings.mode)) {
119966
+ throw new Error(`Local backend compaction currently supports only modes "all" and "sliding_window" (received "${String(settings.mode)}").`);
119967
+ }
119968
+ }
119969
+ function localCompactionSettingsForStorage(settings) {
119970
+ if (settings === undefined || settings === null)
119971
+ return settings;
119972
+ const hasLocalSetting = hasOwn2(settings, "mode") || hasOwn2(settings, "prompt") || hasOwn2(settings, "clip_chars") || hasOwn2(settings, "sliding_window_percentage");
119973
+ if (!hasLocalSetting)
119974
+ return;
119975
+ return { ...settings };
119976
+ }
119349
119977
  function createLocalExecutor(options, onContextWindowOverflow, onContextUsage) {
119350
119978
  if (options.executor)
119351
119979
  return options.executor;
@@ -119385,6 +120013,7 @@ var init_LocalBackend = __esm(() => {
119385
120013
  storageDir;
119386
120014
  createModel;
119387
120015
  generateText;
120016
+ memfsEnabledOverride;
119388
120017
  constructor(options) {
119389
120018
  const localBackendRef = {};
119390
120019
  const modelConfig = resolveLocalModelConfig(options.storageDir);
@@ -119410,19 +120039,46 @@ var init_LocalBackend = __esm(() => {
119410
120039
  this.memoryDir = options.memoryDir;
119411
120040
  this.createModel = options.createModel;
119412
120041
  this.generateText = options.generateText;
120042
+ this.memfsEnabledOverride = options.memfsEnabled;
119413
120043
  }
119414
120044
  async listModels() {
119415
120045
  return listLocalModels(this.storageDir);
119416
120046
  }
119417
120047
  async createAgent(...args) {
119418
120048
  const [body] = args;
119419
- const agent = await super.createAgent(...args);
119420
- await this.ensureLocalMemoryRepo(agent.id, initialMemoryFilesFromCreateBody(body), agent.name ?? undefined);
120049
+ const requestedCompactionSettings = compactionSettingsRecord(body.compaction_settings);
120050
+ if (requestedCompactionSettings !== undefined && requestedCompactionSettings !== null) {
120051
+ validateLocalCompactionSettingsRecord(requestedCompactionSettings);
120052
+ }
120053
+ const compactionSettingsForStorage = localCompactionSettingsForStorage(requestedCompactionSettings);
120054
+ let agent = await super.createAgent(...args);
120055
+ if (compactionSettingsForStorage !== undefined) {
120056
+ agent = this.store.setAgentCompactionSettings(agent.id, compactionSettingsForStorage);
120057
+ }
120058
+ if (this.isLocalMemfsEnabled()) {
120059
+ await this.ensureLocalMemoryRepo(agent.id, initialMemoryFilesFromCreateBody(body), agent.name ?? undefined);
120060
+ }
119421
120061
  await this.compileAndMaybePersistSystemPrompt("default", agent.id, {
119422
120062
  dryRun: false
119423
120063
  });
119424
120064
  return agent;
119425
120065
  }
120066
+ async updateAgent(...args) {
120067
+ const [agentId, body] = args;
120068
+ const bodyRecord = body;
120069
+ const settings = hasOwn2(bodyRecord, "compaction_settings") ? compactionSettingsRecord(bodyRecord.compaction_settings) : undefined;
120070
+ if (settings !== undefined && settings !== null) {
120071
+ validateLocalCompactionSettingsRecord(settings);
120072
+ }
120073
+ const compactionSettingsForStorage = localCompactionSettingsForStorage(settings);
120074
+ let agent = await super.updateAgent(...args);
120075
+ if (hasOwn2(bodyRecord, "compaction_settings")) {
120076
+ if (compactionSettingsForStorage !== undefined) {
120077
+ agent = this.store.setAgentCompactionSettings(agentId, compactionSettingsForStorage);
120078
+ }
120079
+ }
120080
+ return agent;
120081
+ }
119426
120082
  async createConversation(body) {
119427
120083
  const conversation = await super.createConversation(body);
119428
120084
  await this.compileAndMaybePersistSystemPrompt(conversation.id, conversation.agent_id, { dryRun: false });
@@ -119437,7 +120093,7 @@ var init_LocalBackend = __esm(() => {
119437
120093
  async compactConversationMessages(conversationId, body) {
119438
120094
  const bodyRecord = body ?? {};
119439
120095
  const agentId = typeof bodyRecord.agent_id === "string" && bodyRecord.agent_id.length > 0 ? bodyRecord.agent_id : this.store.resolveAgentIdForConversation(conversationId);
119440
- const result = await this.compactLocalConversationAll(conversationId, agentId, "manual", body);
120096
+ const result = await this.compactLocalConversation(conversationId, agentId, "manual", body);
119441
120097
  return {
119442
120098
  num_messages_before: result.numMessagesBefore,
119443
120099
  num_messages_after: result.numMessagesAfter,
@@ -119452,6 +120108,9 @@ var init_LocalBackend = __esm(() => {
119452
120108
  memoryDirForAgent(agentId) {
119453
120109
  return this.memoryDir ?? getLocalBackendMemoryFilesystemRoot(agentId, this.storageDir);
119454
120110
  }
120111
+ isLocalMemfsEnabled() {
120112
+ return this.memfsEnabledOverride ?? !isLocalBackendNoMemfsEnvEnabled();
120113
+ }
119455
120114
  async ensureLocalMemoryRepo(agentId, files = [], authorName) {
119456
120115
  await initializeLocalMemoryRepo({
119457
120116
  memoryDir: this.memoryDirForAgent(agentId),
@@ -119461,9 +120120,7 @@ var init_LocalBackend = __esm(() => {
119461
120120
  });
119462
120121
  }
119463
120122
  async compactAfterContextOverflow(input, _error) {
119464
- const result = await this.compactLocalConversationAll(input.conversationId, input.agentId, "context_window_overflow", {
119465
- compaction_settings: { mode: "all" }
119466
- });
120123
+ const result = await this.compactLocalConversation(input.conversationId, input.agentId, "context_window_overflow");
119467
120124
  return {
119468
120125
  uiMessages: this.store.listLocalMessages(input.conversationId, input.agentId),
119469
120126
  summary: result.summary,
@@ -119476,9 +120133,7 @@ var init_LocalBackend = __esm(() => {
119476
120133
  if (contextTokens === undefined || contextWindow === undefined || contextTokens <= contextWindow) {
119477
120134
  return null;
119478
120135
  }
119479
- const result = await this.compactLocalConversationAll(input.conversationId, input.agentId, "context_window_limit", {
119480
- compaction_settings: { mode: "all" }
119481
- });
120136
+ const result = await this.compactLocalConversation(input.conversationId, input.agentId, "context_window_limit");
119482
120137
  return {
119483
120138
  uiMessages: this.store.listLocalMessages(input.conversationId, input.agentId),
119484
120139
  summary: result.summary,
@@ -119497,24 +120152,55 @@ var init_LocalBackend = __esm(() => {
119497
120152
  const agent = this.store.retrieveAgentRecord(agentId);
119498
120153
  return typeof agent.model_settings.context_window_limit === "number" ? agent.model_settings.context_window_limit : undefined;
119499
120154
  }
119500
- async compactLocalConversationAll(conversationId, agentId, trigger, body) {
119501
- const settings = (body ?? {}).compaction_settings;
119502
- const mode = typeof settings?.mode === "string" ? settings.mode : "all";
119503
- if (mode !== "all") {
119504
- throw new Error(`Local backend compaction currently supports only mode "all" (received "${mode}").`);
119505
- }
120155
+ resolveCompactionSettings(agent, body) {
120156
+ const bodyRecord = body ?? {};
120157
+ const requestSettings = compactionSettingsRecord(bodyRecord.compaction_settings);
120158
+ if (requestSettings !== undefined && requestSettings !== null) {
120159
+ validateLocalCompactionSettingsRecord(requestSettings);
120160
+ }
120161
+ const agentSettings = compactionSettingsRecord(agent.compaction_settings);
120162
+ const baseSettings = agentSettings && agentSettings !== null ? agentSettings : {};
120163
+ const mergedSettings = requestSettings && requestSettings !== null ? { ...baseSettings, ...requestSettings } : baseSettings;
120164
+ const requestChangedMode = requestSettings !== undefined && requestSettings !== null && hasOwn2(requestSettings, "mode");
120165
+ const requestChangedPrompt = requestSettings !== undefined && requestSettings !== null && hasOwn2(requestSettings, "prompt");
120166
+ if (requestChangedMode && !requestChangedPrompt && agentSettings && agentSettings !== null && agentSettings.mode !== requestSettings.mode) {
120167
+ delete mergedSettings.prompt;
120168
+ }
120169
+ const mode = localCompactionMode(mergedSettings.mode) ?? LOCAL_DEFAULT_COMPACTION_MODE;
120170
+ return {
120171
+ mode,
120172
+ prompt: typeof mergedSettings.prompt === "string" || mergedSettings.prompt === null ? mergedSettings.prompt : undefined,
120173
+ clipChars: typeof mergedSettings.clip_chars === "number" || mergedSettings.clip_chars === null ? mergedSettings.clip_chars : undefined,
120174
+ slidingWindowPercentage: typeof mergedSettings.sliding_window_percentage === "number" ? mergedSettings.sliding_window_percentage : LOCAL_DEFAULT_SLIDING_WINDOW_PERCENTAGE
120175
+ };
120176
+ }
120177
+ async compactLocalConversation(conversationId, agentId, trigger, body) {
119506
120178
  const agent = this.store.retrieveAgentRecord(agentId);
120179
+ const settings = this.resolveCompactionSettings(agent, body);
120180
+ if (settings.mode === "sliding_window") {
120181
+ try {
120182
+ return await this.compactLocalConversationSlidingWindow(conversationId, agentId, agent, trigger, settings);
120183
+ } catch (error51) {
120184
+ if (!isLocalSlidingWindowCompactionPlanningError(error51))
120185
+ throw error51;
120186
+ }
120187
+ }
120188
+ return this.compactLocalConversationAll(conversationId, agentId, agent, trigger, {
120189
+ ...settings,
120190
+ mode: "all",
120191
+ prompt: settings.mode === "all" ? settings.prompt : undefined
120192
+ });
120193
+ }
120194
+ async compactLocalConversationAll(conversationId, agentId, agent, trigger, settings) {
119507
120195
  const messages = this.store.listLocalMessages(conversationId, agentId);
119508
120196
  const contextTokensBefore = estimateLocalMessageTokens(messages);
119509
- const prompt = typeof settings?.prompt === "string" ? settings.prompt : null;
119510
- const clipChars = typeof settings?.clip_chars === "number" || settings?.clip_chars === null ? settings.clip_chars : undefined;
119511
120197
  const summary = await summarizeLocalMessagesAll({
119512
120198
  agent,
119513
120199
  messages,
119514
120200
  createModel: this.createModel,
119515
120201
  generateText: this.generateText,
119516
- prompt,
119517
- clipChars,
120202
+ prompt: settings.prompt,
120203
+ clipChars: settings.clipChars,
119518
120204
  localProviderAuthStorageDir: this.storageDir
119519
120205
  });
119520
120206
  const stats = {
@@ -119539,10 +120225,51 @@ var init_LocalBackend = __esm(() => {
119539
120225
  stats
119540
120226
  };
119541
120227
  }
120228
+ async compactLocalConversationSlidingWindow(conversationId, agentId, agent, trigger, settings) {
120229
+ const messages = this.store.listLocalMessages(conversationId, agentId);
120230
+ const contextWindow = this.effectiveContextWindow(conversationId, agentId);
120231
+ const plan = planLocalSlidingWindowCompaction(messages, {
120232
+ slidingWindowPercentage: settings.slidingWindowPercentage,
120233
+ contextWindow
120234
+ });
120235
+ const summary = await summarizeLocalMessagesSlidingWindow({
120236
+ agent,
120237
+ messages: plan.messagesToSummarize,
120238
+ createModel: this.createModel,
120239
+ generateText: this.generateText,
120240
+ prompt: settings.prompt,
120241
+ clipChars: settings.clipChars,
120242
+ localProviderAuthStorageDir: this.storageDir
120243
+ });
120244
+ const contextTokensAfter = Math.ceil(summary.length / 4) + estimateLocalMessageTokens(plan.messagesToKeep);
120245
+ const stats = {
120246
+ trigger,
120247
+ context_tokens_before: estimateLocalMessageTokens(messages),
120248
+ context_tokens_after: contextTokensAfter,
120249
+ context_window: contextWindow,
120250
+ messages_count_before: messages.length,
120251
+ messages_count_after: 1 + plan.messagesToKeep.length
120252
+ };
120253
+ const storeResult = this.store.compactConversationAll({
120254
+ conversationId,
120255
+ agentId,
120256
+ summary,
120257
+ packedSummary: packageLocalSummaryMessage(summary, stats),
120258
+ stats,
120259
+ remainingMessages: plan.messagesToKeep
120260
+ });
120261
+ return {
120262
+ numMessagesBefore: storeResult.numMessagesBefore,
120263
+ numMessagesAfter: storeResult.numMessagesAfter,
120264
+ summary,
120265
+ stats
120266
+ };
120267
+ }
119542
120268
  async getOrCompileSystemPrompt(conversationId, agentId, agent = this.store.retrieveAgentRecord(agentId), previousMessageCount = 0) {
119543
120269
  const existing = this.store.getCompiledSystemPrompt(conversationId, agentId);
119544
- const memfsRevision = await getMemoryHeadRevision(this.memoryDirForAgent(agentId));
119545
- if (existing?.rawSystemHash === hashRawSystemPrompt(agent.system) && memfsRevision !== null && existing.memfsRevision === memfsRevision) {
120270
+ const memfsEnabled = this.isLocalMemfsEnabled();
120271
+ const memfsRevision = memfsEnabled ? await getMemoryHeadRevision(this.memoryDirForAgent(agentId)) : undefined;
120272
+ if (existing?.rawSystemHash === hashRawSystemPrompt(agent.system) && (memfsEnabled ? memfsRevision !== null && existing.memfsRevision === memfsRevision : existing.memfsRevision === undefined)) {
119546
120273
  return existing;
119547
120274
  }
119548
120275
  return this.compileAndMaybePersistSystemPrompt(conversationId, agentId, {
@@ -119552,7 +120279,10 @@ var init_LocalBackend = __esm(() => {
119552
120279
  }
119553
120280
  async compileAndMaybePersistSystemPrompt(conversationId, agentId, options) {
119554
120281
  const agent = this.store.retrieveAgentRecord(agentId);
119555
- await this.ensureLocalMemoryRepo(agentId, [], agent.name);
120282
+ const memfsEnabled = this.isLocalMemfsEnabled();
120283
+ if (memfsEnabled) {
120284
+ await this.ensureLocalMemoryRepo(agentId, [], agent.name);
120285
+ }
119556
120286
  const previousMessageCount = options.previousMessageCount ?? this.store.listConversationMessages(conversationId, {
119557
120287
  agent_id: agentId,
119558
120288
  order: "asc"
@@ -119561,7 +120291,8 @@ var init_LocalBackend = __esm(() => {
119561
120291
  agent,
119562
120292
  conversationId,
119563
120293
  previousMessageCount,
119564
- memoryDir: this.memoryDirForAgent(agentId)
120294
+ memoryDir: memfsEnabled ? this.memoryDirForAgent(agentId) : undefined,
120295
+ includeMemfs: memfsEnabled
119565
120296
  });
119566
120297
  if (!options.dryRun) {
119567
120298
  this.store.setCompiledSystemPrompt(conversationId, agentId, compiled);
@@ -119571,6 +120302,22 @@ var init_LocalBackend = __esm(() => {
119571
120302
  };
119572
120303
  });
119573
120304
 
120305
+ // src/backend/api/conversations.ts
120306
+ var exports_conversations = {};
120307
+ __export(exports_conversations, {
120308
+ forkConversation: () => forkConversation
120309
+ });
120310
+ async function forkConversation(conversationId, options = {}) {
120311
+ const query = {
120312
+ ...options.agentId ? { agent_id: options.agentId } : {},
120313
+ ...options.hidden !== undefined ? { hidden: options.hidden } : {}
120314
+ };
120315
+ return apiRequest("POST", `/v1/conversations/${encodeURIComponent(conversationId)}/fork`, undefined, { query });
120316
+ }
120317
+ var init_conversations2 = __esm(() => {
120318
+ init_request();
120319
+ });
120320
+
119574
120321
  // src/backend/backend.ts
119575
120322
  import { homedir as homedir19 } from "node:os";
119576
120323
 
@@ -119717,6 +120464,7 @@ function getBackend() {
119717
120464
  }
119718
120465
  function configureBackendMode(mode) {
119719
120466
  configuredBackendMode = mode;
120467
+ process.env[LOCAL_BACKEND_EXPERIMENTAL_ENV] = mode === "local" ? "1" : "0";
119720
120468
  backend = createBackendForMode(mode);
119721
120469
  }
119722
120470
  function isLocalBackendEnabled() {
@@ -143952,7 +144700,10 @@ function getTerminalTheme() {
143952
144700
  cachedTheme = detectTerminalThemeSync();
143953
144701
  return cachedTheme;
143954
144702
  }
143955
- var cachedTheme = null;
144703
+ function getTerminalBackgroundColor() {
144704
+ return cachedBackground;
144705
+ }
144706
+ var cachedTheme = null, cachedBackground = null;
143956
144707
 
143957
144708
  // src/cli/components/colors.ts
143958
144709
  function parseHex(hex3) {
@@ -143963,6 +144714,26 @@ function parseHex(hex3) {
143963
144714
  b: parseInt(h.slice(4, 6), 16)
143964
144715
  };
143965
144716
  }
144717
+ function rgbToHex({ r, g, b }) {
144718
+ const toHex = (value) => value.toString(16).padStart(2, "0");
144719
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
144720
+ }
144721
+ function isLightRgb({ r, g, b }) {
144722
+ return 0.299 * r + 0.587 * g + 0.114 * b > 128;
144723
+ }
144724
+ function blendRgb(fg, bg, alpha) {
144725
+ return {
144726
+ r: Math.floor(fg.r * alpha + bg.r * (1 - alpha)),
144727
+ g: Math.floor(fg.g * alpha + bg.g * (1 - alpha)),
144728
+ b: Math.floor(fg.b * alpha + bg.b * (1 - alpha))
144729
+ };
144730
+ }
144731
+ function getCodexUserMessageBackground() {
144732
+ const theme = getTerminalTheme();
144733
+ const terminalBg = getTerminalBackgroundColor() ?? (theme === "light" ? { r: 255, g: 255, b: 255 } : { r: 0, g: 0, b: 0 });
144734
+ const overlay = isLightRgb(terminalBg) ? { color: { r: 0, g: 0, b: 0 }, alpha: 0.04 } : { color: { r: 255, g: 255, b: 255 }, alpha: 0.12 };
144735
+ return rgbToHex(blendRgb(overlay.color, terminalBg, overlay.alpha));
144736
+ }
143966
144737
  function hexToBgAnsi(hex3) {
143967
144738
  const { r, g, b } = parseHex(hex3);
143968
144739
  return `\x1B[48;2;${r};${g};${b}m`;
@@ -144135,9 +144906,8 @@ var init_colors = __esm(() => {
144135
144906
  return getTerminalTheme() === "light" ? _colors.shellSyntaxLight : _colors.shellSyntaxDark;
144136
144907
  },
144137
144908
  get userMessage() {
144138
- const theme = getTerminalTheme();
144139
144909
  return {
144140
- background: theme === "light" ? "#dcddf2" : "#2d2d2d",
144910
+ background: getCodexUserMessageBackground(),
144141
144911
  text: undefined
144142
144912
  };
144143
144913
  }
@@ -145758,6 +146528,8 @@ function toTildePath(absolutePath) {
145758
146528
  return absolutePath;
145759
146529
  }
145760
146530
  function getInitialAuthMethod() {
146531
+ if (isLocalBackendEnabled())
146532
+ return "local";
145761
146533
  if (process.env.LETTA_BASE_URL)
145762
146534
  return "url";
145763
146535
  if (process.env.LETTA_API_KEY)
@@ -145765,6 +146537,8 @@ function getInitialAuthMethod() {
145765
146537
  return null;
145766
146538
  }
145767
146539
  async function getAuthMethod() {
146540
+ if (isLocalBackendEnabled())
146541
+ return "local";
145768
146542
  if (process.env.LETTA_BASE_URL) {
145769
146543
  return "url";
145770
146544
  }
@@ -145800,7 +146574,7 @@ function WelcomeScreen({
145800
146574
  getAuthMethod().then(setAuthMethod);
145801
146575
  }
145802
146576
  }, [initialAuth]);
145803
- const authDisplay = authMethod === "url" ? process.env.LETTA_BASE_URL || "Custom URL" : authMethod === "api-key" ? "API key auth" : "OAuth";
146577
+ const authDisplay = authMethod === "local" ? "Local" : authMethod === "url" ? process.env.LETTA_BASE_URL || "Custom URL" : authMethod === "api-key" ? "API key auth" : "OAuth";
145804
146578
  const memfsEnabled = agentState?.id ? settingsManager.isMemfsEnabled(agentState.id) : true;
145805
146579
  return /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Box_default, {
145806
146580
  flexDirection: "row",
@@ -145870,6 +146644,7 @@ function getLoadingMessage(loadingState, continueSession) {
145870
146644
  var import_react25, jsx_dev_runtime6;
145871
146645
  var init_WelcomeScreen = __esm(async () => {
145872
146646
  init_model();
146647
+ init_backend2();
145873
146648
  init_settings_manager();
145874
146649
  init_version();
145875
146650
  init_useTerminalWidth();
@@ -146686,7 +147461,7 @@ var init_accountConfig3 = __esm(() => {
146686
147461
  });
146687
147462
 
146688
147463
  // src/channels/accountConfig.ts
146689
- function isRecord8(value) {
147464
+ function isRecord11(value) {
146690
147465
  return !!value && typeof value === "object" && !Array.isArray(value);
146691
147466
  }
146692
147467
  function getChannelAccountConfigAdapter(channelId) {
@@ -146697,7 +147472,7 @@ function getChannelPluginConfig(input, key = "config") {
146697
147472
  if (value === undefined) {
146698
147473
  return {};
146699
147474
  }
146700
- return isRecord8(value) ? value : null;
147475
+ return isRecord11(value) ? value : null;
146701
147476
  }
146702
147477
  function isValidChannelPluginConfigPayload(channelId, input, key = "config") {
146703
147478
  const config2 = getChannelPluginConfig(input, key);
@@ -152028,6 +152803,15 @@ function shouldRetryRunMetadataError(errorType, detail) {
152028
152803
  return true;
152029
152804
  return retryable429Detail || retryableDetail;
152030
152805
  }
152806
+ function normalizeStreamErrorTypeToStopReason(errorType) {
152807
+ if (errorType === "llm_error" || errorType === "llm_api_error") {
152808
+ return "llm_api_error";
152809
+ }
152810
+ if (errorType === "internal_error" || errorType === "stream_incomplete") {
152811
+ return "error";
152812
+ }
152813
+ return "error";
152814
+ }
152031
152815
  function isEmptyResponseRetryable(errorType, detail, emptyResponseRetries, maxEmptyResponseRetries) {
152032
152816
  if (emptyResponseRetries >= maxEmptyResponseRetries)
152033
152817
  return false;
@@ -152894,6 +153678,13 @@ var init_approval_result_normalization = __esm(() => {
152894
153678
  });
152895
153679
 
152896
153680
  // src/agent/clientSkills.ts
153681
+ var exports_clientSkills = {};
153682
+ __export(exports_clientSkills, {
153683
+ invalidateClientSkillsPayloadCacheForAgent: () => invalidateClientSkillsPayloadCacheForAgent,
153684
+ invalidateClientSkillsPayloadCache: () => invalidateClientSkillsPayloadCache,
153685
+ discoverClientSideSkills: () => discoverClientSideSkills,
153686
+ buildClientSkillsPayload: () => buildClientSkillsPayload
153687
+ });
152897
153688
  import { existsSync as existsSync32, readdirSync as readdirSync11, realpathSync as realpathSync3, statSync as statSync8 } from "node:fs";
152898
153689
  import { join as join34 } from "node:path";
152899
153690
  function getCache2() {
@@ -152988,6 +153779,17 @@ function cloneResult(result) {
152988
153779
  errors: result.errors.map((e) => ({ ...e }))
152989
153780
  };
152990
153781
  }
153782
+ function invalidateClientSkillsPayloadCache() {
153783
+ getCache2().clear();
153784
+ }
153785
+ function invalidateClientSkillsPayloadCacheForAgent(agentId) {
153786
+ const cache5 = getCache2();
153787
+ for (const [k, entry] of cache5) {
153788
+ if (entry.key.startsWith(`${agentId}|`) || k.startsWith(`${agentId}|`)) {
153789
+ cache5.delete(k);
153790
+ }
153791
+ }
153792
+ }
152991
153793
  function getMemorySkillsDirs2(agentId) {
152992
153794
  const dirs = new Set;
152993
153795
  const scopedMemoryDir = resolveScopedMemoryDir({ agentId });
@@ -153043,56 +153845,28 @@ function resolveSkillDiscoveryContext(options) {
153043
153845
  function getPrimaryProjectSkillsDirectory() {
153044
153846
  return join34(process.cwd(), ".agents", "skills");
153045
153847
  }
153046
- async function buildClientSkillsPayload(options = {}) {
153047
- const { legacySkillsDirectory, skillSources } = resolveSkillDiscoveryContext(options);
153848
+ async function collectClientSideSkills(options) {
153048
153849
  const discoverSkillsFn = options.discoverSkillsFn ?? discoverSkills;
153049
- const useCache = !options.discoverSkillsFn;
153050
- const cwd2 = process.cwd();
153051
- const primaryProjectSkillsDirectory = getPrimaryProjectSkillsDirectory();
153052
- const memorySkillsDirs = getMemorySkillsDirs2(options.agentId);
153053
- const cacheComponents = {
153054
- agentId: options.agentId,
153055
- skillSources,
153056
- cwd: cwd2,
153057
- legacySkillsDirectory,
153058
- primaryProjectSkillsDirectory,
153059
- memorySkillsDirs,
153060
- skillRootRevisions: getSkillRootRevisions({
153061
- agentId: options.agentId,
153062
- skillSources,
153063
- legacySkillsDirectory,
153064
- primaryProjectSkillsDirectory,
153065
- memorySkillsDirs
153066
- })
153067
- };
153068
- const cacheKey = computeCacheKey(cacheComponents);
153069
- if (useCache) {
153070
- const cache5 = getCache2();
153071
- const cached2 = cache5.get(cacheKey);
153072
- if (cached2) {
153073
- return cloneResult(cached2.result);
153074
- }
153075
- }
153076
153850
  const skillsById = new Map;
153077
153851
  const errors4 = [];
153078
- const nonProjectSources = skillSources.filter((source) => source !== "project");
153852
+ const nonProjectSources = options.skillSources?.filter((source) => source !== "project") ?? [];
153079
153853
  const discoveryRuns = [];
153080
153854
  if (nonProjectSources.length > 0) {
153081
153855
  discoveryRuns.push({
153082
- path: primaryProjectSkillsDirectory,
153856
+ path: options.primaryProjectSkillsDirectory,
153083
153857
  sources: nonProjectSources
153084
153858
  });
153085
153859
  }
153086
- const includeProjectSource = skillSources.includes("project");
153087
- if (includeProjectSource && legacySkillsDirectory !== primaryProjectSkillsDirectory) {
153860
+ const includeProjectSource = options.skillSources?.includes("project") ?? false;
153861
+ if (includeProjectSource && options.legacySkillsDirectory !== options.primaryProjectSkillsDirectory) {
153088
153862
  discoveryRuns.push({
153089
- path: legacySkillsDirectory,
153863
+ path: options.legacySkillsDirectory,
153090
153864
  sources: ["project"]
153091
153865
  });
153092
153866
  }
153093
153867
  if (includeProjectSource) {
153094
153868
  discoveryRuns.push({
153095
- path: primaryProjectSkillsDirectory,
153869
+ path: options.primaryProjectSkillsDirectory,
153096
153870
  sources: ["project"]
153097
153871
  });
153098
153872
  }
@@ -153110,7 +153884,7 @@ async function buildClientSkillsPayload(options = {}) {
153110
153884
  errors4.push({ path: run.path, message });
153111
153885
  }
153112
153886
  }
153113
- if (skillSources.length > 0) {
153887
+ if ((options.skillSources?.length ?? 0) > 0) {
153114
153888
  const memoryDiscovery = await discoverMemorySkills(options.agentId);
153115
153889
  errors4.push(...memoryDiscovery.errors);
153116
153890
  for (const skill2 of memoryDiscovery.skills) {
@@ -153121,7 +153895,59 @@ async function buildClientSkillsPayload(options = {}) {
153121
153895
  skillsById.set(skill2.id, skill2);
153122
153896
  }
153123
153897
  }
153124
- const sortedSkills = [...skillsById.values()].sort(compareSkills);
153898
+ return {
153899
+ skills: [...skillsById.values()].sort(compareSkills),
153900
+ errors: errors4
153901
+ };
153902
+ }
153903
+ async function discoverClientSideSkills(options = {}) {
153904
+ const { legacySkillsDirectory, skillSources } = resolveSkillDiscoveryContext(options);
153905
+ return collectClientSideSkills({
153906
+ ...options,
153907
+ legacySkillsDirectory,
153908
+ skillSources,
153909
+ primaryProjectSkillsDirectory: getPrimaryProjectSkillsDirectory()
153910
+ });
153911
+ }
153912
+ async function buildClientSkillsPayload(options = {}) {
153913
+ const { legacySkillsDirectory, skillSources } = resolveSkillDiscoveryContext(options);
153914
+ const discoverSkillsFn = options.discoverSkillsFn ?? discoverSkills;
153915
+ const useCache = !options.discoverSkillsFn;
153916
+ const cwd2 = process.cwd();
153917
+ const primaryProjectSkillsDirectory = getPrimaryProjectSkillsDirectory();
153918
+ const memorySkillsDirs = getMemorySkillsDirs2(options.agentId);
153919
+ const cacheComponents = {
153920
+ agentId: options.agentId,
153921
+ skillSources,
153922
+ cwd: cwd2,
153923
+ legacySkillsDirectory,
153924
+ primaryProjectSkillsDirectory,
153925
+ memorySkillsDirs,
153926
+ skillRootRevisions: getSkillRootRevisions({
153927
+ agentId: options.agentId,
153928
+ skillSources,
153929
+ legacySkillsDirectory,
153930
+ primaryProjectSkillsDirectory,
153931
+ memorySkillsDirs
153932
+ })
153933
+ };
153934
+ const cacheKey = computeCacheKey(cacheComponents);
153935
+ if (useCache) {
153936
+ const cache5 = getCache2();
153937
+ const cached2 = cache5.get(cacheKey);
153938
+ if (cached2) {
153939
+ return cloneResult(cached2.result);
153940
+ }
153941
+ }
153942
+ const discovery = await collectClientSideSkills({
153943
+ ...options,
153944
+ legacySkillsDirectory,
153945
+ skillSources,
153946
+ primaryProjectSkillsDirectory,
153947
+ discoverSkillsFn
153948
+ });
153949
+ const errors4 = discovery.errors;
153950
+ const sortedSkills = discovery.skills.filter(isModelInvocableSkill);
153125
153951
  if (errors4.length > 0) {
153126
153952
  const summarizedErrors = errors4.map((error51) => `${error51.path}: ${error51.message}`);
153127
153953
  options.logger?.(`Failed to build some client_skills entries: ${summarizedErrors.join("; ")}`);
@@ -154008,7 +154834,7 @@ function markIncompleteToolsAsCancelled(b, setInterruptedFlag = true, reason = "
154008
154834
  }
154009
154835
  return anyToolsCancelled;
154010
154836
  }
154011
- function isRecord9(v) {
154837
+ function isRecord12(v) {
154012
154838
  return v !== null && typeof v === "object";
154013
154839
  }
154014
154840
  function getStringProp(obj, key) {
@@ -154019,9 +154845,9 @@ function extractTextPart(v) {
154019
154845
  if (typeof v === "string")
154020
154846
  return v;
154021
154847
  if (Array.isArray(v)) {
154022
- return v.map((p) => isRecord9(p) ? getStringProp(p, "text") ?? "" : "").join("");
154848
+ return v.map((p) => isRecord12(p) ? getStringProp(p, "text") ?? "" : "").join("");
154023
154849
  }
154024
- if (isRecord9(v)) {
154850
+ if (isRecord12(v)) {
154025
154851
  return getStringProp(v, "text") ?? getStringProp(v, "delta") ?? "";
154026
154852
  }
154027
154853
  return "";
@@ -155941,13 +156767,13 @@ function buildAgentInfo(options) {
155941
156767
  }
155942
156768
  const showMemoryDir = (() => {
155943
156769
  try {
155944
- return settingsManager.isMemfsEnabled(agentInfo.id);
156770
+ return isLocalBackendEnvEnabled() || settingsManager.isMemfsEnabled(agentInfo.id);
155945
156771
  } catch {
155946
156772
  return false;
155947
156773
  }
155948
156774
  })();
155949
156775
  const memoryDirLine = showMemoryDir ? `
155950
- - **Memory directory (also stored in \`MEMORY_DIR\` env var)**: \`${getMemoryFilesystemRoot(agentInfo.id)}\`` : "";
156776
+ - **Memory directory (also stored in \`MEMORY_DIR\` env var)**: \`${getScopedMemoryFilesystemRoot(agentInfo.id)}\`` : "";
155951
156777
  const convLine = conversationId ? `
155952
156778
  - **Conversation ID (also stored in \`CONVERSATION_ID\` env var)**: ${conversationId}` : "";
155953
156779
  return `${SYSTEM_REMINDER_OPEN} This is an automated message providing information about you.
@@ -155962,6 +156788,7 @@ ${SYSTEM_REMINDER_CLOSE}`;
155962
156788
  }
155963
156789
  var init_agentInfo = __esm(() => {
155964
156790
  init_memoryFilesystem();
156791
+ init_paths();
155965
156792
  init_constants();
155966
156793
  init_settings_manager();
155967
156794
  });
@@ -158144,6 +158971,9 @@ function getApprovalToolCallDesyncErrorText(errorInfo) {
158144
158971
  }
158145
158972
  return null;
158146
158973
  }
158974
+ function isBackendNotFoundError(error51) {
158975
+ return error51 instanceof APIError2 && (error51.status === 404 || error51.status === 422) || error51 instanceof Error && error51.name === "LocalBackendNotFoundError";
158976
+ }
158147
158977
  function shouldAttemptPostStopApprovalRecovery(params) {
158148
158978
  const approvalDesyncDetected = isApprovalToolCallDesyncError(params.runErrorDetail) || isApprovalToolCallDesyncError(params.latestErrorText) || isApprovalToolCallDesyncError(params.fallbackError);
158149
158979
  return shouldAttemptApprovalRecovery({
@@ -158201,7 +159031,7 @@ async function drainRecoveryStreamWithEmission(recoveryStream, socket, runtime,
158201
159031
  if (errorInfo) {
158202
159032
  emitLoopErrorNotice(socket, runtime, {
158203
159033
  message: errorInfo.message || "Stream error",
158204
- stopReason: errorInfo.error_type || "error",
159034
+ stopReason: normalizeStreamErrorTypeToStopReason(errorInfo.error_type),
158205
159035
  isTerminal: false,
158206
159036
  runId: runtime.activeRunId || errorInfo.run_id,
158207
159037
  agentId: params.agentId ?? undefined,
@@ -158275,12 +159105,12 @@ async function debugLogApprovalResumeState(runtime, params) {
158275
159105
  return;
158276
159106
  }
158277
159107
  try {
158278
- const client = await getClient();
158279
- const agent = await client.agents.retrieve(params.agentId);
159108
+ const backend4 = getBackend();
159109
+ const agent = await backend4.retrieveAgent(params.agentId);
158280
159110
  const isExplicitConversation = params.conversationId.length > 0 && params.conversationId !== "default";
158281
- const lastInContextId = isExplicitConversation ? (await client.conversations.retrieve(params.conversationId)).in_context_message_ids?.at(-1) ?? null : agent.message_ids?.at(-1) ?? null;
158282
- const lastInContextMessages = lastInContextId ? await client.messages.retrieve(lastInContextId) : [];
158283
- const resumeData = await getResumeData(client, agent, params.conversationId, {
159111
+ const lastInContextId = isExplicitConversation ? (await backend4.retrieveConversation(params.conversationId)).in_context_message_ids?.at(-1) ?? null : agent.message_ids?.at(-1) ?? null;
159112
+ const lastInContextMessages = lastInContextId ? await backend4.retrieveMessage(lastInContextId) : [];
159113
+ const resumeData = await getResumeDataFromBackend2(agent, params.conversationId, {
158284
159114
  includeMessageHistory: false
158285
159115
  });
158286
159116
  console.log("[Listen][DEBUG] Post-approval continuation resume snapshot", JSON.stringify({
@@ -158323,12 +159153,12 @@ async function recoverApprovalStateForSync(runtime, scope) {
158323
159153
  clearRecoveredApprovalState(runtime);
158324
159154
  return;
158325
159155
  }
158326
- const client = await getClient();
159156
+ const backend4 = getBackend();
158327
159157
  let agent;
158328
159158
  try {
158329
- agent = await client.agents.retrieve(scope.agent_id);
159159
+ agent = await backend4.retrieveAgent(scope.agent_id);
158330
159160
  } catch (error51) {
158331
- if (error51 instanceof APIError2 && (error51.status === 404 || error51.status === 422)) {
159161
+ if (isBackendNotFoundError(error51)) {
158332
159162
  clearRecoveredApprovalState(runtime);
158333
159163
  return;
158334
159164
  }
@@ -158336,11 +159166,11 @@ async function recoverApprovalStateForSync(runtime, scope) {
158336
159166
  }
158337
159167
  let resumeData;
158338
159168
  try {
158339
- resumeData = await getResumeData(client, agent, scope.conversation_id, {
159169
+ resumeData = await getResumeDataFromBackend2(agent, scope.conversation_id, {
158340
159170
  includeMessageHistory: false
158341
159171
  });
158342
159172
  } catch (error51) {
158343
- if (error51 instanceof APIError2 && (error51.status === 404 || error51.status === 422)) {
159173
+ if (isBackendNotFoundError(error51)) {
158344
159174
  clearRecoveredApprovalState(runtime);
158345
159175
  return;
158346
159176
  }
@@ -158496,7 +159326,8 @@ async function resolveRecoveredApprovalResponse(runtime, socket, response, proce
158496
159326
  parentScope: recovered.agentId && recovered.conversationId ? {
158497
159327
  agentId: recovered.agentId,
158498
159328
  conversationId: recovered.conversationId
158499
- } : undefined
159329
+ } : undefined,
159330
+ channelTurnSources: runtime.activeChannelTurnSources ?? undefined
158500
159331
  });
158501
159332
  } finally {
158502
159333
  emitToolExecutionOutput.flush();
@@ -158554,7 +159385,6 @@ var init_recovery = __esm(async () => {
158554
159385
  init_check_approval();
158555
159386
  init_turn_recovery_policy();
158556
159387
  init_backend2();
158557
- init_client2();
158558
159388
  init_formatDenial();
158559
159389
  init_interactivePolicy();
158560
159390
  init_constants2();
@@ -158606,16 +159436,19 @@ function markAwaitingAcceptedApprovalContinuationRunId(runtime, input) {
158606
159436
  runtime.activeRunId = null;
158607
159437
  }
158608
159438
  }
159439
+ function isBackendNotFoundError2(err) {
159440
+ return err instanceof APIError2 && (err.status === 404 || err.status === 422) || err instanceof Error && err.name === "LocalBackendNotFoundError";
159441
+ }
158609
159442
  async function resolveStaleApprovals(runtime, socket, abortSignal, deps = {}) {
158610
159443
  if (!runtime.agentId)
158611
159444
  return null;
158612
- const getResumeDataImpl = deps.getResumeData ?? getResumeData;
158613
- const client = await getClient();
159445
+ const getResumeDataImpl = deps.getResumeData ?? getResumeDataFromBackend2;
159446
+ const backend4 = getBackend();
158614
159447
  let agent;
158615
159448
  try {
158616
- agent = await client.agents.retrieve(runtime.agentId);
159449
+ agent = await backend4.retrieveAgent(runtime.agentId);
158617
159450
  } catch (err) {
158618
- if (err instanceof APIError2 && (err.status === 404 || err.status === 422)) {
159451
+ if (isBackendNotFoundError2(err)) {
158619
159452
  return null;
158620
159453
  }
158621
159454
  throw err;
@@ -158623,11 +159456,11 @@ async function resolveStaleApprovals(runtime, socket, abortSignal, deps = {}) {
158623
159456
  const requestedConversationId = runtime.conversationId !== "default" ? runtime.conversationId : undefined;
158624
159457
  let resumeData;
158625
159458
  try {
158626
- resumeData = await getResumeDataImpl(client, agent, requestedConversationId, {
159459
+ resumeData = await getResumeDataImpl(agent, requestedConversationId, {
158627
159460
  includeMessageHistory: false
158628
159461
  });
158629
159462
  } catch (err) {
158630
- if (err instanceof APIError2 && (err.status === 404 || err.status === 422)) {
159463
+ if (isBackendNotFoundError2(err)) {
158631
159464
  return null;
158632
159465
  }
158633
159466
  throw err;
@@ -158993,7 +159826,6 @@ var init_send = __esm(async () => {
158993
159826
  init_check_approval();
158994
159827
  init_turn_recovery_policy();
158995
159828
  init_backend2();
158996
- init_client2();
158997
159829
  init_errorFormatter();
158998
159830
  init_streamAbortRelay();
158999
159831
  init_constants2();
@@ -160748,7 +161580,7 @@ function formatArgsDisplay(argsJson, toolName) {
160748
161580
  try {
160749
161581
  if (argsJson?.trim()) {
160750
161582
  const p = JSON.parse(argsJson);
160751
- if (isRecord10(p)) {
161583
+ if (isRecord13(p)) {
160752
161584
  const clone2 = { ...p };
160753
161585
  if ("request_heartbeat" in clone2)
160754
161586
  delete clone2.request_heartbeat;
@@ -160869,7 +161701,7 @@ function formatArgsDisplay(argsJson, toolName) {
160869
161701
  }
160870
161702
  return { display, parsed, shellSemantic };
160871
161703
  }
160872
- var isRecord10 = (v) => typeof v === "object" && v !== null;
161704
+ var isRecord13 = (v) => typeof v === "object" && v !== null;
160873
161705
  var init_formatArgsDisplay = __esm(async () => {
160874
161706
  init_shellSemanticDisplay();
160875
161707
  await init_toolNameMapping();
@@ -161366,6 +162198,7 @@ async function handleApprovalStop(params) {
161366
162198
  onStreamingOutput: emitToolExecutionOutput,
161367
162199
  workingDirectory: turnWorkingDirectory,
161368
162200
  parentScope: agentId && conversationId ? { agentId, conversationId } : undefined,
162201
+ channelTurnSources: runtime.activeChannelTurnSources ?? undefined,
161369
162202
  onFileWrite
161370
162203
  });
161371
162204
  } finally {
@@ -161491,9 +162324,8 @@ var init_turn_approval = __esm(async () => {
161491
162324
 
161492
162325
  // src/websocket/listener/memfs-sync.ts
161493
162326
  async function syncMemfsForAgent(agentId) {
161494
- const { getClient: getClient2 } = await Promise.resolve().then(() => (init_client2(), exports_client));
161495
- const client = await getClient2();
161496
- const agent = await client.agents.retrieve(agentId);
162327
+ const { getBackend: getBackend2 } = await Promise.resolve().then(() => (init_backend2(), exports_backend));
162328
+ const agent = await getBackend2().retrieveAgent(agentId);
161497
162329
  const { GIT_MEMORY_ENABLED_TAG: GIT_MEMORY_ENABLED_TAG2 } = await Promise.resolve().then(() => (init_memoryGit(), exports_memoryGit));
161498
162330
  if (!agent.tags?.includes(GIT_MEMORY_ENABLED_TAG2)) {
161499
162331
  debugLog("memfs-sync", `Agent ${agentId} does not have memfs tag, skipping`);
@@ -161530,8 +162362,7 @@ function getAgentMetadataPromise(listener, agentId) {
161530
162362
  return listener.agentMetadataByAgent.get(agentId) ?? null;
161531
162363
  }
161532
162364
  async function fetchListenerAgentMetadata(agentId) {
161533
- const client = await getClient();
161534
- const agent = await client.agents.retrieve(agentId);
162365
+ const agent = await getBackend().retrieveAgent(agentId);
161535
162366
  return {
161536
162367
  name: agent.name ?? null,
161537
162368
  description: agent.description ?? null,
@@ -161584,7 +162415,7 @@ function clearListenerWarmState(listener) {
161584
162415
  }
161585
162416
  var defaultWarmupDeps, warmupDeps;
161586
162417
  var init_warmup = __esm(() => {
161587
- init_client2();
162418
+ init_backend2();
161588
162419
  init_debug();
161589
162420
  init_memfs_sync();
161590
162421
  init_secrets_sync();
@@ -161641,8 +162472,7 @@ function buildMaybeLaunchReflectionSubagent(params) {
161641
162472
  let systemPrompt = cachedAgent?.system ?? undefined;
161642
162473
  if (!systemPrompt) {
161643
162474
  try {
161644
- const client = await getClient();
161645
- const agent = await client.agents.retrieve(agentId);
162475
+ const agent = await getBackend().retrieveAgent(agentId);
161646
162476
  systemPrompt = agent.system ?? undefined;
161647
162477
  } catch {
161648
162478
  debugLog("memory", "Failed to fetch agent system prompt for reflection payload");
@@ -161819,8 +162649,7 @@ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, conne
161819
162649
  syncReminderStateFromContextTracker(runtime.reminderState, runtime.contextTracker);
161820
162650
  if (agentId) {
161821
162651
  try {
161822
- const client = await getClient();
161823
- cachedAgent = await client.agents.retrieve(agentId);
162652
+ cachedAgent = await getBackend().retrieveAgent(agentId);
161824
162653
  } catch {}
161825
162654
  }
161826
162655
  if (!runtime.reminderState.hasSentAgentInfo && cachedAgent) {
@@ -161943,7 +162772,7 @@ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, conne
161943
162772
  if (!recoverableApprovalErrorText) {
161944
162773
  emitLoopErrorNotice(socket, runtime, {
161945
162774
  message: errorInfo.message || "Stream error",
161946
- stopReason: errorInfo.error_type || "error",
162775
+ stopReason: normalizeStreamErrorTypeToStopReason(errorInfo.error_type),
161947
162776
  isTerminal: false,
161948
162777
  runId: runId || errorInfo.run_id,
161949
162778
  agentId,
@@ -162034,9 +162863,8 @@ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, conne
162034
162863
  conversationId
162035
162864
  });
162036
162865
  try {
162037
- const client = await getClient();
162038
- const agent = await client.agents.retrieve(agentId || "");
162039
- const { pendingApprovals: existingApprovals } = await getResumeData(client, agent, requestedConversationId);
162866
+ const agent = await getBackend().retrieveAgent(agentId || "");
162867
+ const { pendingApprovals: existingApprovals } = await getResumeDataFromBackend2(agent, requestedConversationId);
162040
162868
  currentInput = rebuildInputWithFreshDenials(currentInput, existingApprovals ?? [], "Auto-denied: stale approval from interrupted session");
162041
162869
  } catch {
162042
162870
  currentInput = rebuildInputWithFreshDenials(currentInput, [], "");
@@ -162320,7 +163148,7 @@ var init_turn = __esm(async () => {
162320
163148
  init_context();
162321
163149
  init_memoryFilesystem();
162322
163150
  init_turn_recovery_policy();
162323
- init_client2();
163151
+ init_backend2();
162324
163152
  init_errorFormatter();
162325
163153
  init_memoryReminder();
162326
163154
  init_memorySubagentCompletion();
@@ -162427,17 +163255,19 @@ function emitSlashCommandEnd(socket, runtime, scope, fields) {
162427
163255
  emitCanonicalMessageDelta(socket, runtime, endDelta, scope);
162428
163256
  }
162429
163257
  async function handleClearCommand(_socket, conversationRuntime, opts) {
162430
- const client = await getClient();
163258
+ const backend4 = getBackend();
162431
163259
  const agentId = conversationRuntime.agentId;
162432
163260
  if (!agentId) {
162433
163261
  throw new Error("No agent ID available for /clear command");
162434
163262
  }
162435
- if (conversationRuntime.conversationId === "default") {
163263
+ if (conversationRuntime.conversationId === "default" && !backend4.capabilities.localModelCatalog) {
163264
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_client2(), exports_client));
163265
+ const client = await getClient2();
162436
163266
  await client.agents.messages.reset(agentId, {
162437
163267
  add_default_initial_messages: false
162438
163268
  });
162439
163269
  }
162440
- const conversation = await client.conversations.create({
163270
+ const conversation = await backend4.createConversation({
162441
163271
  agent_id: agentId,
162442
163272
  isolated_block_labels: [...ISOLATED_BLOCK_LABELS]
162443
163273
  });
@@ -162635,7 +163465,7 @@ var init_commands = __esm(async () => {
162635
163465
  init_memory();
162636
163466
  init_memoryFilesystem();
162637
163467
  init_promptAssets();
162638
- init_client2();
163468
+ init_backend2();
162639
163469
  init_initCommand();
162640
163470
  init_constants();
162641
163471
  init_settings_manager();
@@ -164018,6 +164848,16 @@ function isExecuteCommandCommand(value) {
164018
164848
  const hasValidArgs = c.args === undefined || typeof c.args === "string";
164019
164849
  return c.type === "execute_command" && typeof c.command_id === "string" && typeof c.request_id === "string" && isRuntimeScope(c.runtime) && hasValidArgs;
164020
164850
  }
164851
+ function parseServerLifecycleMessage(data) {
164852
+ try {
164853
+ const raw = typeof data === "string" ? data : data.toString();
164854
+ const parsed = JSON.parse(raw);
164855
+ if (parsed && typeof parsed === "object" && parsed.type === "pong") {
164856
+ return { type: "pong" };
164857
+ }
164858
+ } catch {}
164859
+ return null;
164860
+ }
164021
164861
  function parseServerMessage(data) {
164022
164862
  try {
164023
164863
  const raw = typeof data === "string" ? data : data.toString();
@@ -165513,11 +166353,12 @@ function handleMemoryProtocolCommand(parsed, context4) {
165513
166353
  const buffer = encoding === "base64" ? Buffer.from(parsed.content, "base64") : Buffer.from(parsed.content, "utf-8");
165514
166354
  await mkdir9(dirname20(absolutePath), { recursive: true });
165515
166355
  await writeFile11(absolutePath, buffer);
166356
+ const { getBackend: getBackend2 } = await Promise.resolve().then(() => (init_backend2(), exports_backend));
166357
+ const backend4 = getBackend2();
166358
+ const memorySyncMode = backend4.capabilities.localMemfs && !backend4.capabilities.remoteMemfs ? "local" : undefined;
165516
166359
  let agentName = parsed.agent_id;
165517
166360
  try {
165518
- const { getClient: getClient2 } = await Promise.resolve().then(() => (init_client2(), exports_client));
165519
- const client = await getClient2();
165520
- const agent = await client.agents.retrieve(parsed.agent_id);
166361
+ const agent = await backend4.retrieveAgent(parsed.agent_id);
165521
166362
  if (agent.name && agent.name.trim().length > 0) {
165522
166363
  agentName = agent.name.trim();
165523
166364
  }
@@ -165533,6 +166374,7 @@ function handleMemoryProtocolCommand(parsed, context4) {
165533
166374
  authorName: agentName,
165534
166375
  authorEmail: `${parsed.agent_id}@letta.com`
165535
166376
  },
166377
+ ...memorySyncMode ? { syncMode: memorySyncMode } : {},
165536
166378
  replay: async () => {
165537
166379
  await mkdir9(dirname20(absolutePath), { recursive: true });
165538
166380
  await writeFile11(absolutePath, buffer);
@@ -165627,11 +166469,12 @@ function handleMemoryProtocolCommand(parsed, context4) {
165627
166469
  }, "listener_delete_memory_file_send_failed", "listener_delete_memory_file");
165628
166470
  return;
165629
166471
  }
166472
+ const { getBackend: getBackend2 } = await Promise.resolve().then(() => (init_backend2(), exports_backend));
166473
+ const backend4 = getBackend2();
166474
+ const memorySyncMode = backend4.capabilities.localMemfs && !backend4.capabilities.remoteMemfs ? "local" : undefined;
165630
166475
  let agentName = parsed.agent_id;
165631
166476
  try {
165632
- const { getClient: getClient2 } = await Promise.resolve().then(() => (init_client2(), exports_client));
165633
- const client = await getClient2();
165634
- const agent = await client.agents.retrieve(parsed.agent_id);
166477
+ const agent = await backend4.retrieveAgent(parsed.agent_id);
165635
166478
  if (agent.name && agent.name.trim().length > 0) {
165636
166479
  agentName = agent.name.trim();
165637
166480
  }
@@ -165646,6 +166489,7 @@ function handleMemoryProtocolCommand(parsed, context4) {
165646
166489
  authorName: agentName,
165647
166490
  authorEmail: `${parsed.agent_id}@letta.com`
165648
166491
  },
166492
+ ...memorySyncMode ? { syncMode: memorySyncMode } : {},
165649
166493
  replay: async () => {
165650
166494
  await removeIfPresent();
165651
166495
  return [pathspec];
@@ -168175,6 +169019,11 @@ function createListenerMessageHandler(params) {
168175
169019
  const raw = data.toString();
168176
169020
  let parsedScope = null;
168177
169021
  try {
169022
+ const lifecycleMessage = parseServerLifecycleMessage(data);
169023
+ if (lifecycleMessage) {
169024
+ safeEmitWsEvent("recv", "lifecycle", lifecycleMessage);
169025
+ return;
169026
+ }
168178
169027
  const parsed = parseServerMessage(data);
168179
169028
  parsedScope = getParsedRuntimeScope(parsed);
168180
169029
  if (parsed) {
@@ -169496,6 +170345,178 @@ var init_listen_client = __esm(async () => {
169496
170345
  await init_client4();
169497
170346
  });
169498
170347
 
170348
+ // src/backend/api/search.ts
170349
+ async function warmSearchCache(body) {
170350
+ return apiRequest("POST", "/v1/_internal_search/cache-warm", body);
170351
+ }
170352
+ async function searchMessages(body) {
170353
+ return apiRequest("POST", "/v1/messages/search", body);
170354
+ }
170355
+ var init_search = __esm(() => {
170356
+ init_request();
170357
+ });
170358
+
170359
+ // src/backend/messageSearch.ts
170360
+ function pageItems(page) {
170361
+ if (Array.isArray(page))
170362
+ return page;
170363
+ if (page && typeof page === "object") {
170364
+ const maybePage = page;
170365
+ if (typeof maybePage.getPaginatedItems === "function") {
170366
+ return maybePage.getPaginatedItems();
170367
+ }
170368
+ if (Array.isArray(maybePage.items)) {
170369
+ return maybePage.items;
170370
+ }
170371
+ }
170372
+ return [];
170373
+ }
170374
+ function textFromContent2(content) {
170375
+ if (typeof content === "string")
170376
+ return content;
170377
+ if (!Array.isArray(content))
170378
+ return "";
170379
+ return content.map((part) => {
170380
+ if (part && typeof part === "object" && "text" in part && typeof part.text === "string") {
170381
+ return part.text;
170382
+ }
170383
+ return "";
170384
+ }).filter(Boolean).join(`
170385
+ `);
170386
+ }
170387
+ function stringifySearchValue(value) {
170388
+ if (typeof value === "string")
170389
+ return value;
170390
+ if (value === null || value === undefined)
170391
+ return "";
170392
+ try {
170393
+ return JSON.stringify(value);
170394
+ } catch {
170395
+ return String(value);
170396
+ }
170397
+ }
170398
+ function searchableText(message) {
170399
+ const toolCalls = Array.isArray(message.tool_calls) ? message.tool_calls : message.tool_call ? [message.tool_call] : [];
170400
+ return [
170401
+ message.message_type,
170402
+ textFromContent2(message.content),
170403
+ message.reasoning,
170404
+ message.summary,
170405
+ ...toolCalls.flatMap((call) => [call.name, call.arguments]),
170406
+ stringifySearchValue(message.tool_return),
170407
+ stringifySearchValue(message.func_response)
170408
+ ].filter((part) => typeof part === "string" && part.length > 0).join(`
170409
+ `);
170410
+ }
170411
+ function matchesQuery(message, query) {
170412
+ const terms = query.toLowerCase().split(/\s+/).map((term) => term.trim()).filter(Boolean);
170413
+ if (terms.length === 0)
170414
+ return false;
170415
+ const haystack = searchableText(message).toLowerCase();
170416
+ return terms.every((term) => haystack.includes(term));
170417
+ }
170418
+ function withinDateRange(message, startDate, endDate) {
170419
+ if (!startDate && !endDate)
170420
+ return true;
170421
+ if (!message.date)
170422
+ return true;
170423
+ const time3 = new Date(message.date).getTime();
170424
+ if (Number.isNaN(time3))
170425
+ return true;
170426
+ const startTime = startDate ? new Date(startDate).getTime() : 0;
170427
+ const endTime = endDate ? new Date(endDate).getTime() : Number.POSITIVE_INFINITY;
170428
+ return time3 >= startTime && time3 <= endTime;
170429
+ }
170430
+ function toSearchResult(message) {
170431
+ const createdAt = message.date ?? new Date(0).toISOString();
170432
+ return {
170433
+ ...message,
170434
+ message_id: message.id ?? `${message.agent_id ?? "local"}:${createdAt}`,
170435
+ created_at: createdAt,
170436
+ agent_id: message.agent_id ?? null,
170437
+ conversation_id: message.conversation_id ?? null
170438
+ };
170439
+ }
170440
+ async function localConversationMessages(backend4, conversationId, agentId) {
170441
+ try {
170442
+ const page = await backend4.listConversationMessages(conversationId, {
170443
+ limit: LOCAL_SEARCH_SCAN_LIMIT,
170444
+ order: "desc",
170445
+ ...agentId ? { agent_id: agentId } : {}
170446
+ });
170447
+ return pageItems(page);
170448
+ } catch {
170449
+ return [];
170450
+ }
170451
+ }
170452
+ async function localAgentMessages(backend4, agentId) {
170453
+ const messages = [
170454
+ ...await localConversationMessages(backend4, "default", agentId)
170455
+ ];
170456
+ try {
170457
+ const conversationsPage = await backend4.listConversations({
170458
+ agent_id: agentId,
170459
+ limit: LOCAL_SEARCH_SCAN_LIMIT
170460
+ });
170461
+ const conversations = pageItems(conversationsPage);
170462
+ for (const conversation of conversations) {
170463
+ if (!conversation.id || conversation.id === "default")
170464
+ continue;
170465
+ messages.push(...await localConversationMessages(backend4, conversation.id, agentId));
170466
+ }
170467
+ } catch {}
170468
+ return messages;
170469
+ }
170470
+ async function localSearchMessages(backend4, body) {
170471
+ const query = typeof body.query === "string" ? body.query.trim() : "";
170472
+ if (!query)
170473
+ return [];
170474
+ const limit2 = typeof body.limit === "number" ? body.limit : 100;
170475
+ const agentId = typeof body.agent_id === "string" ? body.agent_id : undefined;
170476
+ const conversationId = typeof body.conversation_id === "string" ? body.conversation_id : undefined;
170477
+ const startDate = typeof body.start_date === "string" ? body.start_date : undefined;
170478
+ const endDate = typeof body.end_date === "string" ? body.end_date : undefined;
170479
+ let messages = [];
170480
+ if (conversationId) {
170481
+ messages = await localConversationMessages(backend4, conversationId, agentId);
170482
+ } else if (agentId) {
170483
+ messages = await localAgentMessages(backend4, agentId);
170484
+ } else {
170485
+ const agentsPage = await backend4.listAgents({
170486
+ limit: LOCAL_SEARCH_SCAN_LIMIT
170487
+ });
170488
+ const agents = pageItems(agentsPage);
170489
+ for (const agent of agents) {
170490
+ messages.push(...await localAgentMessages(backend4, agent.id));
170491
+ }
170492
+ }
170493
+ return messages.filter((message) => matchesQuery(message, query)).filter((message) => withinDateRange(message, startDate, endDate)).sort((a, b) => (b.date ?? "").localeCompare(a.date ?? "")).slice(0, limit2).map(toSearchResult);
170494
+ }
170495
+ async function searchMessagesForBackend(body, backend4 = getBackend()) {
170496
+ if (backend4.capabilities.localModelCatalog) {
170497
+ return await localSearchMessages(backend4, body);
170498
+ }
170499
+ return searchMessages({
170500
+ ...body,
170501
+ search_mode: body.search_mode === "vector" || body.search_mode === "fts" || body.search_mode === "hybrid" ? body.search_mode : body.search_mode
170502
+ });
170503
+ }
170504
+ async function warmMessageSearchCacheForBackend(body, backend4 = getBackend()) {
170505
+ if (backend4.capabilities.localModelCatalog) {
170506
+ return {
170507
+ collection: body.collection ?? "messages",
170508
+ status: "local-backend-noop",
170509
+ warmed: false
170510
+ };
170511
+ }
170512
+ return warmSearchCache(body);
170513
+ }
170514
+ var LOCAL_SEARCH_SCAN_LIMIT = 1000;
170515
+ var init_messageSearch = __esm(() => {
170516
+ init_search();
170517
+ init_backend();
170518
+ });
170519
+
169499
170520
  // src/utils/debug.ts
169500
170521
  var exports_debug2 = {};
169501
170522
  __export(exports_debug2, {
@@ -169637,6 +170658,12 @@ var init_version2 = __esm(() => {
169637
170658
  // src/agent/skills.ts
169638
170659
  var exports_skills2 = {};
169639
170660
  __export(exports_skills2, {
170661
+ isUserInvocableSkill: () => isUserInvocableSkill2,
170662
+ isModelInvocableSkill: () => isModelInvocableSkill2,
170663
+ getLegacyAgentSkillsDir: () => getLegacyAgentSkillsDir2,
170664
+ getFrontmatterStringList: () => getFrontmatterStringList2,
170665
+ getFrontmatterString: () => getFrontmatterString2,
170666
+ getFrontmatterBoolean: () => getFrontmatterBoolean2,
169640
170667
  getBundledSkills: () => getBundledSkills2,
169641
170668
  getAgentSkillsDir: () => getAgentSkillsDir2,
169642
170669
  formatSkillsAsSystemReminder: () => formatSkillsAsSystemReminder2,
@@ -169659,7 +170686,45 @@ function getBundledSkillsPath2() {
169659
170686
  function compareSkills2(a, b) {
169660
170687
  return a.id.localeCompare(b.id) || a.source.localeCompare(b.source) || a.path.localeCompare(b.path);
169661
170688
  }
170689
+ function stripSurroundingQuotes2(value) {
170690
+ const trimmed = value.trim();
170691
+ if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
170692
+ return trimmed.slice(1, -1);
170693
+ }
170694
+ return trimmed;
170695
+ }
170696
+ function getFrontmatterString2(frontmatter, key) {
170697
+ const value = frontmatter[key];
170698
+ return typeof value === "string" ? stripSurroundingQuotes2(value) : undefined;
170699
+ }
170700
+ function getFrontmatterStringList2(frontmatter, key) {
170701
+ const value = frontmatter[key];
170702
+ if (Array.isArray(value)) {
170703
+ return value.map(stripSurroundingQuotes2).filter((item) => item.length > 0);
170704
+ }
170705
+ if (typeof value === "string") {
170706
+ return stripSurroundingQuotes2(value).split(/\s+/).map((item) => item.trim()).filter((item) => item.length > 0);
170707
+ }
170708
+ return;
170709
+ }
170710
+ function getFrontmatterBoolean2(frontmatter, key) {
170711
+ const value = getFrontmatterString2(frontmatter, key)?.toLowerCase();
170712
+ if (value === "true")
170713
+ return true;
170714
+ if (value === "false")
170715
+ return false;
170716
+ return;
170717
+ }
170718
+ function isModelInvocableSkill2(skill2) {
170719
+ return skill2.disableModelInvocation !== true;
170720
+ }
170721
+ function isUserInvocableSkill2(skill2) {
170722
+ return skill2.userInvocable !== false;
170723
+ }
169662
170724
  function getAgentSkillsDir2(agentId) {
170725
+ return join42(process.env.HOME || process.env.USERPROFILE || "~", ".letta/agents", agentId, "memory/skills");
170726
+ }
170727
+ function getLegacyAgentSkillsDir2(agentId) {
169663
170728
  return join42(process.env.HOME || process.env.USERPROFILE || "~", ".letta/agents", agentId, "skills");
169664
170729
  }
169665
170730
  async function getBundledSkills2() {
@@ -169702,6 +170767,12 @@ async function discoverSkills2(projectSkillsPath = join42(process.cwd(), SKILLS_
169702
170767
  }
169703
170768
  }
169704
170769
  if (agentId && includeSource("agent")) {
170770
+ const legacyDir = getLegacyAgentSkillsDir2(agentId);
170771
+ const legacyResult = await discoverSkillsFromDir2(legacyDir, "agent");
170772
+ allErrors.push(...legacyResult.errors);
170773
+ for (const skill2 of legacyResult.skills) {
170774
+ skillsById.set(skill2.id, skill2);
170775
+ }
169705
170776
  const agentSkillsDir = getAgentSkillsDir2(agentId);
169706
170777
  const agentResult = await discoverSkillsFromDir2(agentSkillsDir, "agent");
169707
170778
  allErrors.push(...agentResult.errors);
@@ -169785,7 +170856,7 @@ async function parseSkillFile2(filePath, rootPath, source) {
169785
170856
  const defaultId = dirPath || "root";
169786
170857
  const id = (typeof frontmatter.id === "string" ? frontmatter.id : null) || defaultId;
169787
170858
  const name25 = (typeof frontmatter.name === "string" ? frontmatter.name : null) || (typeof frontmatter.title === "string" ? frontmatter.title : null) || (id.split("/").pop() ?? "").replace(/-/g, " ").replace(/\b\w/g, (l) => l.toUpperCase());
169788
- let description = typeof frontmatter.description === "string" ? frontmatter.description : null;
170859
+ let description = getFrontmatterString2(frontmatter, "description") ?? null;
169789
170860
  if (!description) {
169790
170861
  const firstParagraph = body.trim().split(`
169791
170862
 
@@ -169793,30 +170864,31 @@ async function parseSkillFile2(filePath, rootPath, source) {
169793
170864
  description = firstParagraph || "No description available";
169794
170865
  }
169795
170866
  description = description.trim();
169796
- if (description.startsWith('"') && description.endsWith('"') || description.startsWith("'") && description.endsWith("'")) {
169797
- description = description.slice(1, -1);
169798
- }
169799
- let tags;
169800
- if (Array.isArray(frontmatter.tags)) {
169801
- tags = frontmatter.tags;
169802
- } else if (typeof frontmatter.tags === "string") {
169803
- tags = [frontmatter.tags];
169804
- }
170867
+ const whenToUse = getFrontmatterString2(frontmatter, "when_to_use")?.trim();
170868
+ const modelDescription = whenToUse ? `${description}
170869
+
170870
+ When to use: ${whenToUse}` : description;
170871
+ const tags = getFrontmatterStringList2(frontmatter, "tags");
169805
170872
  return {
169806
170873
  id,
169807
170874
  name: name25,
169808
- description,
169809
- category: typeof frontmatter.category === "string" ? frontmatter.category : undefined,
170875
+ description: modelDescription,
170876
+ whenToUse,
170877
+ argumentHint: getFrontmatterString2(frontmatter, "argument-hint"),
170878
+ arguments: getFrontmatterStringList2(frontmatter, "arguments"),
170879
+ disableModelInvocation: getFrontmatterBoolean2(frontmatter, "disable-model-invocation") ?? false,
170880
+ userInvocable: getFrontmatterBoolean2(frontmatter, "user-invocable") ?? true,
170881
+ category: getFrontmatterString2(frontmatter, "category"),
169810
170882
  tags,
169811
170883
  path: filePath,
169812
170884
  source
169813
170885
  };
169814
170886
  }
169815
170887
  function formatSkillsAsSystemReminder2(skills) {
169816
- if (skills.length === 0) {
170888
+ const lines = skills.filter(isModelInvocableSkill2).sort(compareSkills2).map((s) => `- ${s.id} (${s.source}): ${s.description}`);
170889
+ if (lines.length === 0) {
169817
170890
  return "";
169818
170891
  }
169819
- const lines = [...skills].sort(compareSkills2).map((s) => `- ${s.id} (${s.source}): ${s.description}`);
169820
170892
  return `<system-reminder>
169821
170893
  The following skills are available for use with the Skill tool:
169822
170894
 
@@ -169881,6 +170953,7 @@ __export(exports_terminalTheme, {
169881
170953
  parseHexComponent: () => parseHexComponent,
169882
170954
  initTerminalTheme: () => initTerminalTheme,
169883
170955
  getTerminalTheme: () => getTerminalTheme2,
170956
+ getTerminalBackgroundColor: () => getTerminalBackgroundColor2,
169884
170957
  detectTerminalThemeSync: () => detectTerminalThemeSync2,
169885
170958
  detectTerminalThemeAsync: () => detectTerminalThemeAsync,
169886
170959
  calculateLuminance: () => calculateLuminance
@@ -169955,6 +171028,7 @@ function calculateLuminance(r, g, b) {
169955
171028
  async function detectTerminalThemeAsync() {
169956
171029
  const bg = await queryTerminalBackground(100);
169957
171030
  if (bg) {
171031
+ cachedBackground2 = bg;
169958
171032
  const luminance = calculateLuminance(bg.r, bg.g, bg.b);
169959
171033
  return luminance > 0.4 ? "light" : "dark";
169960
171034
  }
@@ -169983,11 +171057,14 @@ function getTerminalTheme2() {
169983
171057
  cachedTheme2 = detectTerminalThemeSync2();
169984
171058
  return cachedTheme2;
169985
171059
  }
171060
+ function getTerminalBackgroundColor2() {
171061
+ return cachedBackground2;
171062
+ }
169986
171063
  async function initTerminalTheme() {
169987
171064
  cachedTheme2 = await detectTerminalThemeAsync();
169988
171065
  return cachedTheme2;
169989
171066
  }
169990
- var cachedTheme2 = null;
171067
+ var cachedTheme2 = null, cachedBackground2 = null;
169991
171068
 
169992
171069
  // src/agent/bootstrap-tools.ts
169993
171070
  var exports_bootstrap_tools = {};
@@ -172182,15 +173259,19 @@ In headless mode, use:
172182
173259
  const skillSourcesRaw = values["skill-sources"];
172183
173260
  const memfsFlag = values.memfs;
172184
173261
  const noMemfsFlag = values["no-memfs"];
173262
+ const localNoMemfsRequested = Boolean(backend4.capabilities.localMemfs && (noMemfsFlag || isLocalBackendNoMemfsEnvEnabled()));
173263
+ if (localNoMemfsRequested) {
173264
+ process.env[LOCAL_BACKEND_NO_MEMFS_ENV] = "1";
173265
+ }
172185
173266
  const memfsStartupRaw = values["memfs-startup"];
172186
173267
  const memfsStartupPolicy = memfsStartupRaw === "background" || memfsStartupRaw === "skip" ? memfsStartupRaw : "blocking";
172187
- const requestedMemoryPromptMode = memfsFlag ? "memfs" : noMemfsFlag ? "standard" : undefined;
173268
+ const requestedMemoryPromptMode = memfsFlag ? "memfs" : noMemfsFlag || localNoMemfsRequested ? "standard" : undefined;
172188
173269
  if (memfsFlag && !backend4.capabilities.remoteMemfs) {
172189
173270
  trackHeadlessBoundaryError("headless_memfs_unsupported_backend", "MemFS requires a backend with remote MemFS support", "headless_startup_memfs_flags");
172190
173271
  console.error("Error: --memfs is not supported by this backend yet");
172191
173272
  process.exit(1);
172192
173273
  }
172193
- const shouldAutoEnableMemfsForNewAgent = !memfsFlag && !noMemfsFlag;
173274
+ const shouldAutoEnableMemfsForNewAgent = !memfsFlag && !noMemfsFlag && !localNoMemfsRequested;
172194
173275
  const fromAfFile = resolveImportFlagAlias2({
172195
173276
  importFlagValue: values.import,
172196
173277
  fromAfFlagValue: values["from-af"]
@@ -172470,7 +173551,7 @@ In headless mode, use:
172470
173551
  if (!agent && forceNew) {
172471
173552
  const { isLettaCloud: isLettaCloud2 } = await Promise.resolve().then(() => (init_memoryFilesystem(), exports_memoryFilesystem));
172472
173553
  const willAutoEnableMemfs = backend4.capabilities.remoteMemfs && shouldAutoEnableMemfsForNewAgent && await isLettaCloud2();
172473
- const effectiveMemoryMode = backend4.capabilities.localMemfs ? "local-memfs" : requestedMemoryPromptMode ?? (willAutoEnableMemfs ? "memfs" : undefined);
173554
+ const effectiveMemoryMode = backend4.capabilities.localMemfs ? localNoMemfsRequested ? "standard" : "local-memfs" : requestedMemoryPromptMode ?? (willAutoEnableMemfs ? "memfs" : undefined);
172474
173555
  const personalityOptions = personality ? await buildCreateAgentOptionsForPersonality2({
172475
173556
  personalityId: personality,
172476
173557
  model,
@@ -172572,12 +173653,8 @@ In headless mode, use:
172572
173653
  const secretsInitPromise = secretsAgentId ? Promise.resolve().then(() => (init_secretsStore(), exports_secretsStore)).then(({ initSecretsFromServer: initSecretsFromServer2 }) => initSecretsFromServer2(secretsAgentId, agent ?? undefined)) : Promise.resolve();
172573
173654
  if (!backend4.capabilities.remoteMemfs) {
172574
173655
  if (backend4.capabilities.localMemfs) {
172575
- if (noMemfsFlag) {
172576
- console.error("Disabling MemFS is not supported by the local backend.");
172577
- process.exit(1);
172578
- }
172579
- settingsManager.setMemfsEnabled(agent.id, true);
172580
- } else if (noMemfsFlag) {
173656
+ settingsManager.setMemfsEnabled(agent.id, !localNoMemfsRequested);
173657
+ } else if (noMemfsFlag || localNoMemfsRequested) {
172581
173658
  settingsManager.setMemfsEnabled(agent.id, false);
172582
173659
  }
172583
173660
  } else if (memfsStartupPolicy === "skip") {
@@ -174553,6 +175630,7 @@ var init_headless = __esm(async () => {
174553
175630
  init_personality();
174554
175631
  init_skillSources();
174555
175632
  init_backend2();
175633
+ init_paths();
174556
175634
  init_errorFormatter();
174557
175635
  init_memoryReminder();
174558
175636
  init_messageQueueBridge();
@@ -175395,8 +176473,7 @@ async function handleProfileSave(ctx, msg, profileName) {
175395
176473
  const cmdId = addCommandResult2(ctx.buffersRef, ctx.refreshDerived, msg, `Saving profile "${profileName}"...`, false, "running");
175396
176474
  ctx.setCommandRunning(true);
175397
176475
  try {
175398
- const client = await getClient();
175399
- await client.agents.update(ctx.agentId, { name: profileName });
176476
+ await getBackend().updateAgent(ctx.agentId, { name: profileName });
175400
176477
  ctx.updateAgentName(profileName);
175401
176478
  settingsManager.saveProfile(profileName, ctx.agentId);
175402
176479
  updateCommandResult2(ctx.buffersRef, ctx.refreshDerived, cmdId, msg, `Pinned "${profileName}" locally and globally.`, true);
@@ -175478,9 +176555,7 @@ async function handlePin(ctx, msg, argsStr) {
175478
176555
  const globalPinned = settingsManager.getGlobalPinnedAgents();
175479
176556
  if (name25 && name25 !== ctx.agentName) {
175480
176557
  try {
175481
- const { getClient: getClient2 } = await Promise.resolve().then(() => (init_client2(), exports_client));
175482
- const client = await getClient2();
175483
- await client.agents.update(ctx.agentId, { name: name25 });
176558
+ await getBackend().updateAgent(ctx.agentId, { name: name25 });
175484
176559
  ctx.updateAgentName(name25);
175485
176560
  } catch (error51) {
175486
176561
  addCommandResult2(ctx.buffersRef, ctx.refreshDerived, msg, `Failed to rename agent: ${error51}`, false);
@@ -175527,7 +176602,7 @@ function handleUnpin(ctx, msg, argsStr) {
175527
176602
  }
175528
176603
  var activeCommandId2 = null;
175529
176604
  var init_profile = __esm(() => {
175530
- init_client2();
176605
+ init_backend2();
175531
176606
  init_settings_manager();
175532
176607
  init_errorFormatter();
175533
176608
  });
@@ -190685,7 +191760,7 @@ function highlightCode(code, language) {
190685
191760
  }
190686
191761
  return lines;
190687
191762
  }
190688
- var import_react34, jsx_dev_runtime12, lowlight, BASH_LANGUAGE = "bash", FIRST_LINE_PREFIX = "$ ", EXT_TO_LANG, HEREDOC_RE, REDIRECT_FILE_RE, SyntaxHighlightedCommand;
191763
+ var import_react34, jsx_dev_runtime12, lowlight, BASH_LANGUAGE = "bash", FIRST_LINE_PROMPT = "$", PROMPT_COLUMN_WIDTH = 2, EXT_TO_LANG, HEREDOC_RE, REDIRECT_FILE_RE, SyntaxHighlightedCommand;
190689
191764
  var init_SyntaxHighlightedCommand = __esm(async () => {
190690
191765
  init_lowlight();
190691
191766
  init_colors();
@@ -190786,9 +191861,13 @@ var init_SyntaxHighlightedCommand = __esm(async () => {
190786
191861
  const lineKey = spans.map((s) => s.text).join("");
190787
191862
  return /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
190788
191863
  children: [
190789
- showPrompt ? /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text2, {
190790
- color: palette.prompt,
190791
- children: lineIdx === 0 ? FIRST_LINE_PREFIX : " "
191864
+ showPrompt ? /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Box_default, {
191865
+ width: PROMPT_COLUMN_WIDTH,
191866
+ flexShrink: 0,
191867
+ children: lineIdx === 0 ? /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text2, {
191868
+ color: palette.prompt,
191869
+ children: FIRST_LINE_PROMPT
191870
+ }, undefined, false, undefined, this) : null
190792
191871
  }, undefined, false, undefined, this) : null,
190793
191872
  /* @__PURE__ */ jsx_dev_runtime12.jsxDEV(Text2, {
190794
191873
  color: palette.text,
@@ -199500,6 +200579,7 @@ function boxBottom(width) {
199500
200579
  }
199501
200580
  var import_react67, jsx_dev_runtime44, BOX_TOP_LEFT = "╭", BOX_TOP_RIGHT = "╮", BOX_BOTTOM_LEFT = "╰", BOX_BOTTOM_RIGHT = "╯", BOX_HORIZONTAL = "─", BOX_VERTICAL = "│", HOOK_EVENTS, FALLBACK_TOOL_NAMES, SAVE_LOCATIONS, HooksManager;
199502
200581
  var init_HooksManager = __esm(async () => {
200582
+ init_backend2();
199503
200583
  init_types3();
199504
200584
  init_writer();
199505
200585
  init_settings_manager();
@@ -199572,6 +200652,9 @@ var init_HooksManager = __esm(async () => {
199572
200652
  return;
199573
200653
  const fetchAgentTools = async () => {
199574
200654
  try {
200655
+ if (!getBackend().capabilities.serverSideToolManagement) {
200656
+ return;
200657
+ }
199575
200658
  const { getClient: getClient2 } = await Promise.resolve().then(() => (init_client2(), exports_client));
199576
200659
  const client = await getClient2();
199577
200660
  const toolsPage = await client.agents.tools.list(agentId, {
@@ -202359,6 +203442,7 @@ function SlashCommandAutocomplete({
202359
203442
  }) {
202360
203443
  const columns = useTerminalWidth();
202361
203444
  const [customCommands, setCustomCommands] = import_react72.useState([]);
203445
+ const [skillCommands, setSkillCommands] = import_react72.useState([]);
202362
203446
  import_react72.useEffect(() => {
202363
203447
  Promise.resolve().then(() => (init_custom(), exports_custom)).then(({ getCustomCommands: getCustomCommands2 }) => {
202364
203448
  getCustomCommands2().then((customs) => {
@@ -202371,6 +203455,35 @@ function SlashCommandAutocomplete({
202371
203455
  });
202372
203456
  });
202373
203457
  }, []);
203458
+ import_react72.useEffect(() => {
203459
+ let cancelled = false;
203460
+ (async () => {
203461
+ try {
203462
+ const { discoverClientSideSkills: discoverClientSideSkills2 } = await Promise.resolve().then(() => (init_clientSkills(), exports_clientSkills));
203463
+ const { getSkillSources: getSkillSources2 } = await Promise.resolve().then(() => (init_context(), exports_context));
203464
+ const { isUserInvocableSkill: isUserInvocableSkill3 } = await Promise.resolve().then(() => (init_skills(), exports_skills));
203465
+ const discovery = await discoverClientSideSkills2({
203466
+ agentId,
203467
+ skillSources: getSkillSources2()
203468
+ });
203469
+ if (cancelled)
203470
+ return;
203471
+ const matches2 = discovery.skills.filter(isUserInvocableSkill3).map((skill2) => ({
203472
+ cmd: `/${skill2.id}`,
203473
+ desc: `${skill2.description}${skill2.argumentHint ? ` ${skill2.argumentHint}` : ""} (${skill2.source} skill)`,
203474
+ order: 300
203475
+ }));
203476
+ setSkillCommands(matches2);
203477
+ } catch {
203478
+ if (!cancelled) {
203479
+ setSkillCommands([]);
203480
+ }
203481
+ }
203482
+ })();
203483
+ return () => {
203484
+ cancelled = true;
203485
+ };
203486
+ }, [agentId]);
202374
203487
  const allCommands = import_react72.useMemo(() => {
202375
203488
  let builtins = _allCommands;
202376
203489
  if (agentId) {
@@ -202394,8 +203507,13 @@ function SlashCommandAutocomplete({
202394
203507
  builtins = _allCommands;
202395
203508
  }
202396
203509
  }
202397
- return [...builtins, ...customCommands].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));
202398
- }, [agentId, workingDirectory, customCommands]);
203510
+ const reservedCommands = new Set([
203511
+ ...builtins.map((cmd) => cmd.cmd),
203512
+ ...customCommands.map((cmd) => cmd.cmd)
203513
+ ]);
203514
+ const visibleSkillCommands = skillCommands.filter((cmd) => !reservedCommands.has(cmd.cmd));
203515
+ return [...builtins, ...customCommands, ...visibleSkillCommands].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));
203516
+ }, [agentId, workingDirectory, customCommands, skillCommands]);
202399
203517
  const queryInfo = import_react72.useMemo(() => extractSearchQuery2(currentInput, cursorPosition), [currentInput, cursorPosition]);
202400
203518
  const { matches, showNoMatches, hideAutocomplete } = import_react72.useMemo(() => {
202401
203519
  if (!queryInfo) {
@@ -209408,8 +210526,7 @@ function MemoryTabViewer({
209408
210526
  import_react81.useEffect(() => {
209409
210527
  const fetchBlocks = async () => {
209410
210528
  try {
209411
- const client = await getClient();
209412
- const agent = await client.agents.retrieve(agentId, {
210529
+ const agent = await getBackend().retrieveAgent(agentId, {
209413
210530
  include: ["agent.blocks"]
209414
210531
  });
209415
210532
  setFreshBlocks(agent.memory?.blocks || []);
@@ -209686,7 +210803,7 @@ function MemoryTabViewer({
209686
210803
  }
209687
210804
  var import_react81, jsx_dev_runtime58, SOLID_LINE21 = "─", VISIBLE_LINES = 12;
209688
210805
  var init_MemoryTabViewer = __esm(async () => {
209689
- init_client2();
210806
+ init_backend2();
209690
210807
  init_debug();
209691
210808
  init_useTerminalWidth();
209692
210809
  init_colors();
@@ -209700,24 +210817,13 @@ var init_MemoryTabViewer = __esm(async () => {
209700
210817
  jsx_dev_runtime58 = __toESM(require_jsx_dev_runtime(), 1);
209701
210818
  });
209702
210819
 
209703
- // src/backend/api/search.ts
209704
- async function warmSearchCache(body) {
209705
- return apiRequest("POST", "/v1/_internal_search/cache-warm", body);
209706
- }
209707
- async function searchMessages(body) {
209708
- return apiRequest("POST", "/v1/messages/search", body);
209709
- }
209710
- var init_search = __esm(() => {
209711
- init_request();
209712
- });
209713
-
209714
210820
  // src/cli/components/MessageSearch.tsx
209715
210821
  async function warmMessageSearchCache() {
209716
210822
  const body = {
209717
210823
  collection: "messages",
209718
210824
  scope: {}
209719
210825
  };
209720
- return warmSearchCache(body);
210826
+ return warmMessageSearchCacheForBackend(body);
209721
210827
  }
209722
210828
  function isSearchRangeAvailable(range2, options) {
209723
210829
  if (range2 === "agent")
@@ -209856,7 +210962,7 @@ function MessageSearch({
209856
210962
  } else if (range2 === "conv" && conversationId) {
209857
210963
  body.conversation_id = conversationId;
209858
210964
  }
209859
- return searchMessages(body);
210965
+ return searchMessagesForBackend(body);
209860
210966
  }, [agentId, conversationId]);
209861
210967
  const fetchAndCacheSearchResults = import_react82.useCallback(async (query, mode, range2) => {
209862
210968
  const cacheKey = getCacheKey(query, mode, range2);
@@ -210329,7 +211435,7 @@ function MessageSearch({
210329
211435
  }
210330
211436
  var import_react82, jsx_dev_runtime59, SOLID_LINE22 = "─", VISIBLE_ITEMS = 5, SEARCH_LIMIT = 100, SEARCH_MODES, SEARCH_RANGES;
210331
211437
  var init_MessageSearch = __esm(async () => {
210332
- init_search();
211438
+ init_messageSearch();
210333
211439
  init_useTerminalWidth();
210334
211440
  init_colors();
210335
211441
  await __promiseAll([
@@ -212657,7 +213763,7 @@ function getTabDescription(tab2, agentId) {
212657
213763
  case "project":
212658
213764
  return ".skills/";
212659
213765
  case "agent":
212660
- return `~/.letta/agents/${shortId}/skills/`;
213766
+ return `~/.letta/agents/${shortId}/memory/skills/`;
212661
213767
  case "global":
212662
213768
  return "~/.letta/skills/";
212663
213769
  case "bundled":
@@ -212697,7 +213803,7 @@ function SkillsDialog({ onClose, agentId }) {
212697
213803
  }
212698
213804
  return grouped;
212699
213805
  }, [skills]);
212700
- const availableTabs = import_react91.useMemo(() => TAB_ORDER.filter((tab2) => (skillsBySource.get(tab2)?.length ?? 0) > 0), [skillsBySource]);
213806
+ const availableTabs = import_react91.useMemo(() => [...TAB_ORDER], []);
212701
213807
  const [activeTab, setActiveTab] = import_react91.useState(null);
212702
213808
  import_react91.useEffect(() => {
212703
213809
  if (!loading && availableTabs.length > 0 && activeTab === null) {
@@ -212851,13 +213957,20 @@ function SkillsDialog({ onClose, agentId }) {
212851
213957
  }, undefined, false, undefined, this) : null
212852
213958
  ]
212853
213959
  }, undefined, true, undefined, this),
213960
+ !loading && activeTab && currentSkills.length === 0 && /* @__PURE__ */ jsx_dev_runtime68.jsxDEV(Box_default, {
213961
+ paddingLeft: 2,
213962
+ children: /* @__PURE__ */ jsx_dev_runtime68.jsxDEV(Text2, {
213963
+ dimColor: true,
213964
+ children: "No skills in this location"
213965
+ }, undefined, false, undefined, this)
213966
+ }, undefined, false, undefined, this),
212854
213967
  /* @__PURE__ */ jsx_dev_runtime68.jsxDEV(Box_default, {
212855
213968
  marginTop: 1,
212856
213969
  children: /* @__PURE__ */ jsx_dev_runtime68.jsxDEV(Text2, {
212857
213970
  dimColor: true,
212858
213971
  children: [
212859
213972
  " ",
212860
- availableTabs.length > 1 ? "↑↓ scroll · ←→/Tab switch · Esc to close" : "Esc to close"
213973
+ "↑↓ scroll · ←→/Tab switch · Esc to close"
212861
213974
  ]
212862
213975
  }, undefined, true, undefined, this)
212863
213976
  }, undefined, false, undefined, this)
@@ -215162,7 +216275,7 @@ var init_ToolCallMessageRich = __esm(async () => {
215162
216275
  let shellCommand = null;
215163
216276
  let shellSemanticKind = null;
215164
216277
  if (!isQuestionTool(rawName)) {
215165
- const parseArgs10 = () => {
216278
+ const parseArgs9 = () => {
215166
216279
  if (!argsText.trim()) {
215167
216280
  return { formatted: null, parseable: true };
215168
216281
  }
@@ -215173,7 +216286,7 @@ var init_ToolCallMessageRich = __esm(async () => {
215173
216286
  return { formatted: null, parseable: false };
215174
216287
  }
215175
216288
  };
215176
- const { formatted, parseable } = parseArgs10();
216289
+ const { formatted, parseable } = parseArgs9();
215177
216290
  const argsComplete = parseable || line.phase === "running" || line.phase === "finished" || !isStreaming;
215178
216291
  if (!argsComplete) {
215179
216292
  args = "(…)";
@@ -215286,13 +216399,13 @@ var init_ToolCallMessageRich = __esm(async () => {
215286
216399
  }, undefined, true, undefined, this);
215287
216400
  }
215288
216401
  const displayResultText = clipToolReturn(extractedText).replace(/\n+$/, "");
215289
- const isRecord11 = (v) => typeof v === "object" && v !== null;
216402
+ const isRecord14 = (v) => typeof v === "object" && v !== null;
215290
216403
  if (isTodoTool(rawName, displayName) && line.resultOk !== false && line.argsText) {
215291
216404
  try {
215292
216405
  const parsedArgs = JSON.parse(line.argsText);
215293
216406
  if (parsedArgs.todos && Array.isArray(parsedArgs.todos)) {
215294
216407
  const safeTodos = parsedArgs.todos.map((t, i) => {
215295
- const rec = isRecord11(t) ? t : {};
216408
+ const rec = isRecord14(t) ? t : {};
215296
216409
  const status = rec.status === "completed" ? "completed" : rec.status === "in_progress" ? "in_progress" : "pending";
215297
216410
  const id = typeof rec.id === "string" ? rec.id : String(i);
215298
216411
  const content = typeof rec.content === "string" ? rec.content : typeof rec.description === "string" ? rec.description : JSON.stringify(t);
@@ -215310,7 +216423,7 @@ var init_ToolCallMessageRich = __esm(async () => {
215310
216423
  const parsedArgs = JSON.parse(line.argsText);
215311
216424
  if (parsedArgs.plan && Array.isArray(parsedArgs.plan)) {
215312
216425
  const safePlan = parsedArgs.plan.map((item) => {
215313
- const rec = isRecord11(item) ? item : {};
216426
+ const rec = isRecord14(item) ? item : {};
215314
216427
  const status = rec.status === "completed" ? "completed" : rec.status === "in_progress" ? "in_progress" : "pending";
215315
216428
  const step = typeof rec.step === "string" ? rec.step : JSON.stringify(item);
215316
216429
  return { step, status };
@@ -216127,6 +217240,27 @@ var init_TrajectorySummary = __esm(async () => {
216127
217240
  });
216128
217241
 
216129
217242
  // src/cli/components/UserMessageRich.tsx
217243
+ function getCurrentStdoutColumns() {
217244
+ if (typeof process === "undefined")
217245
+ return null;
217246
+ const columns = process.stdout?.columns;
217247
+ return typeof columns === "number" && columns > 0 ? columns : null;
217248
+ }
217249
+ function isInkFullWidthCodePoint(codePoint) {
217250
+ return codePoint >= 4352 && (codePoint <= 4447 || codePoint === 9001 || codePoint === 9002 || 11904 <= codePoint && codePoint <= 12871 && codePoint !== 12351 || 12880 <= codePoint && codePoint <= 19903 || 19968 <= codePoint && codePoint <= 42182 || 43360 <= codePoint && codePoint <= 43388 || 44032 <= codePoint && codePoint <= 55203 || 63744 <= codePoint && codePoint <= 64255 || 65040 <= codePoint && codePoint <= 65049 || 65072 <= codePoint && codePoint <= 65131 || 65281 <= codePoint && codePoint <= 65376 || 65504 <= codePoint && codePoint <= 65510 || 110592 <= codePoint && codePoint <= 110593 || 127488 <= codePoint && codePoint <= 127569 || 131072 <= codePoint && codePoint <= 262141);
217251
+ }
217252
+ function inkStringWidth(text2) {
217253
+ let width = 0;
217254
+ for (let index = 0;index < text2.length; ) {
217255
+ const codePoint = text2.codePointAt(index);
217256
+ if (codePoint === undefined)
217257
+ break;
217258
+ const character = String.fromCodePoint(codePoint);
217259
+ width += isInkFullWidthCodePoint(codePoint) || character.length > 1 ? 2 : 1;
217260
+ index += character.length;
217261
+ }
217262
+ return width;
217263
+ }
216130
217264
  function wordWrap(text2, width) {
216131
217265
  if (width <= 0)
216132
217266
  return [text2];
@@ -216138,7 +217272,7 @@ function wordWrap(text2, width) {
216138
217272
  current = word;
216139
217273
  } else {
216140
217274
  const candidate = `${current} ${word}`;
216141
- if (stringWidth(candidate) <= width) {
217275
+ if (inkStringWidth(candidate) <= width) {
216142
217276
  current = candidate;
216143
217277
  } else {
216144
217278
  lines.push(current);
@@ -216186,7 +217320,11 @@ function splitSystemReminderBlocks(text2) {
216186
217320
  }
216187
217321
  return blocks;
216188
217322
  }
216189
- function renderBlock(text2, contentWidth, columns, highlighted, colorAnsi, promptPrefix, continuationPrefix) {
217323
+ function padToColumns(text2, columns) {
217324
+ const pad = Math.max(0, columns - inkStringWidth(text2));
217325
+ return `${text2}${" ".repeat(pad)}`;
217326
+ }
217327
+ function renderBlock(text2, contentWidth, columns, highlighted, promptPrefix, continuationPrefix) {
216190
217328
  const inputLines = text2.split(`
216191
217329
  `);
216192
217330
  const outputLines = [];
@@ -216202,34 +217340,39 @@ function renderBlock(text2, contentWidth, columns, highlighted, colorAnsi, promp
216202
217340
  }
216203
217341
  if (outputLines.length === 0)
216204
217342
  return [];
216205
- const isSingleLine = outputLines.length === 1;
216206
- return outputLines.map((ol, i) => {
217343
+ const renderedLines = outputLines.map((ol, i) => {
216207
217344
  const prefix = i === 0 ? promptPrefix : continuationPrefix;
217345
+ const content = `${prefix}${ol}`;
216208
217346
  if (!highlighted) {
216209
- return prefix + ol;
216210
- }
216211
- const content = i === 0 ? `${promptPrefix.slice(0, -1)}${colorAnsi} ${ol}` : `${prefix}${ol}`;
216212
- const visWidth = stringWidth(content);
216213
- if (isSingleLine) {
216214
- return `${colorAnsi}${content}${" ".repeat(COMPACT_PAD)}\x1B[0m`;
217347
+ return { text: content, highlighted: false };
216215
217348
  }
216216
- const pad = Math.max(0, columns - visWidth);
216217
- return `${colorAnsi}${content}${" ".repeat(pad)}\x1B[0m`;
217349
+ return { text: padToColumns(content, columns), highlighted: true };
216218
217350
  });
217351
+ if (!highlighted) {
217352
+ return renderedLines;
217353
+ }
217354
+ const blankLine = {
217355
+ text: " ".repeat(Math.max(0, columns)),
217356
+ highlighted: true
217357
+ };
217358
+ return [blankLine, ...renderedLines, blankLine];
216219
217359
  }
216220
- var import_react101, jsx_dev_runtime81, COMPACT_PAD = 1, UserMessage;
217360
+ var import_react101, jsx_dev_runtime81, UserMessage;
216221
217361
  var init_UserMessageRich = __esm(async () => {
216222
- init_string_width();
216223
217362
  init_constants();
216224
217363
  init_useTerminalWidth();
216225
217364
  init_colors();
216226
- await init_Text2();
217365
+ await __promiseAll([
217366
+ init_build2(),
217367
+ init_Text2()
217368
+ ]);
216227
217369
  import_react101 = __toESM(require_react(), 1);
216228
217370
  jsx_dev_runtime81 = __toESM(require_jsx_dev_runtime(), 1);
216229
217371
  UserMessage = import_react101.memo(({ line, prompt }) => {
216230
- const columns = useTerminalWidth();
217372
+ const trackedColumns2 = useTerminalWidth();
217373
+ const columns = getCurrentStdoutColumns() ?? trackedColumns2;
216231
217374
  const promptPrefix = `${prompt || ">"} `;
216232
- const prefixWidth = stringWidth(promptPrefix);
217375
+ const prefixWidth = inkStringWidth(promptPrefix);
216233
217376
  const continuationPrefix = " ".repeat(prefixWidth);
216234
217377
  const contentWidth = Math.max(1, columns - prefixWidth);
216235
217378
  const cleanedText = extractTaskNotificationsForDisplay(line.text).cleanedText;
@@ -216238,23 +217381,25 @@ var init_UserMessageRich = __esm(async () => {
216238
217381
  return null;
216239
217382
  }
216240
217383
  const { background, text: textColor } = colors.userMessage;
216241
- const bgAnsi = hexToBgAnsi(background);
216242
- const fgAnsi = textColor ? hexToFgAnsi(textColor) : "";
216243
- const colorAnsi = bgAnsi + fgAnsi;
216244
217384
  const blocks = splitSystemReminderBlocks(displayText);
216245
217385
  const allLines = [];
216246
217386
  for (const block of blocks) {
216247
217387
  if (!block.text.trim())
216248
217388
  continue;
216249
217389
  if (allLines.length > 0) {
216250
- allLines.push("");
217390
+ allLines.push({ text: "", highlighted: false });
216251
217391
  }
216252
- const blockLines = renderBlock(block.text, contentWidth, columns, !block.isSystemReminder, colorAnsi, promptPrefix, continuationPrefix);
217392
+ const blockLines = renderBlock(block.text, contentWidth, columns, !block.isSystemReminder, promptPrefix, continuationPrefix);
216253
217393
  allLines.push(...blockLines);
216254
217394
  }
216255
- return /* @__PURE__ */ jsx_dev_runtime81.jsxDEV(Text2, {
216256
- children: allLines.join(`
216257
- `)
217395
+ return /* @__PURE__ */ jsx_dev_runtime81.jsxDEV(Box_default, {
217396
+ flexDirection: "column",
217397
+ children: allLines.map((line2, index) => /* @__PURE__ */ jsx_dev_runtime81.jsxDEV(Text2, {
217398
+ backgroundColor: line2.highlighted ? background : undefined,
217399
+ color: line2.highlighted ? textColor : undefined,
217400
+ wrap: line2.highlighted ? "end" : "wrap",
217401
+ children: line2.text
217402
+ }, index, false, undefined, this))
216258
217403
  }, undefined, false, undefined, this);
216259
217404
  });
216260
217405
  UserMessage.displayName = "UserMessage";
@@ -217736,7 +218881,7 @@ function updateCommandResult3(buffersRef, refreshDerived, cmdId, input, output,
217736
218881
  buffersRef.current.byId.set(cmdId, line);
217737
218882
  refreshDerived();
217738
218883
  }
217739
- function parseArgs10(msg) {
218884
+ function parseArgs9(msg) {
217740
218885
  return msg.trim().split(/\s+/).filter(Boolean);
217741
218886
  }
217742
218887
  function formatConnectUsage() {
@@ -217936,7 +219081,7 @@ ${formatBedrockUsage2()}`, false);
217936
219081
  }
217937
219082
  }
217938
219083
  async function handleConnect(ctx, msg) {
217939
- const parts = parseArgs10(msg);
219084
+ const parts = parseArgs9(msg);
217940
219085
  const providerToken = parts[1];
217941
219086
  if (!providerToken) {
217942
219087
  addCommandResult3(ctx.buffersRef, ctx.refreshDerived, msg, formatConnectUsage(), false);
@@ -218052,7 +219197,7 @@ The '${CLAUDE_PROVIDER_NAME}' provider does not exist in your Letta account.`, f
218052
219197
  }
218053
219198
  }
218054
219199
  async function handleDisconnect(ctx, msg) {
218055
- const parts = parseArgs10(msg);
219200
+ const parts = parseArgs9(msg);
218056
219201
  const providerToken = parts[1]?.toLowerCase();
218057
219202
  if (providerToken === "help") {
218058
219203
  addCommandResult3(ctx.buffersRef, ctx.refreshDerived, msg, formatDisconnectHelp(), true);
@@ -220354,9 +221499,8 @@ function App2({
220354
221499
  return;
220355
221500
  }
220356
221501
  try {
220357
- const client = await getClient();
220358
221502
  debugLog("conversations", `retrieve(${conversationId}) [syncConversationModel]`);
220359
- const conversation = await client.conversations.retrieve(conversationId);
221503
+ const conversation = await getBackend().retrieveConversation(conversationId);
220360
221504
  if (cancelled)
220361
221505
  return;
220362
221506
  const conversationModel = conversation.model;
@@ -220788,9 +221932,8 @@ ${newState.originalPrompt}`,
220788
221932
  })) {
220789
221933
  llmApiErrorRetriesRef.current += 1;
220790
221934
  try {
220791
- const client = await getClient();
220792
- const agent = await client.agents.retrieve(agentIdRef.current);
220793
- const { pendingApprovals: existingApprovals } = await getResumeData(client, agent, conversationIdRef.current);
221935
+ const agent = await getBackend().retrieveAgent(agentIdRef.current);
221936
+ const { pendingApprovals: existingApprovals } = await getResumeDataFromBackend2(agent, conversationIdRef.current);
220794
221937
  currentInput = rebuildInputWithFreshDenials(currentInput, existingApprovals ?? [], "Auto-denied: stale approval from interrupted session");
220795
221938
  } catch {
220796
221939
  currentInput = rebuildInputWithFreshDenials(currentInput, [], "");
@@ -220939,9 +222082,8 @@ ${newState.originalPrompt}`,
220939
222082
  if (hasApprovalInPayload2) {
220940
222083
  if (isInvalidToolCallIdsError(errorDetail)) {
220941
222084
  try {
220942
- const client = await getClient();
220943
- const agent = await client.agents.retrieve(agentIdRef.current);
220944
- const { pendingApprovals: serverApprovals } = await getResumeData(client, agent, conversationIdRef.current);
222085
+ const agent = await getBackend().retrieveAgent(agentIdRef.current);
222086
+ const { pendingApprovals: serverApprovals } = await getResumeDataFromBackend2(agent, conversationIdRef.current);
220945
222087
  if (serverApprovals && serverApprovals.length > 0) {
220946
222088
  const userMessage = currentInput.find((item) => item?.type === "message");
220947
222089
  if (userMessage && "content" in userMessage) {
@@ -220988,7 +222130,7 @@ ${newState.originalPrompt}`,
220988
222130
  }
220989
222131
  const syncAgentState = async () => {
220990
222132
  try {
220991
- const agent = prefetchedAgent ?? await (await getClient()).agents.retrieve(agentIdRef.current);
222133
+ const agent = prefetchedAgent ?? await getBackend().retrieveAgent(agentIdRef.current);
220992
222134
  const currentModel = llmConfigRef.current?.model;
220993
222135
  const currentEndpoint = llmConfigRef.current?.model_endpoint_type;
220994
222136
  const currentEffort = llmConfigRef.current?.reasoning_effort;
@@ -221149,8 +222291,7 @@ ${feedback}
221149
222291
  if (!conversationTitle) {
221150
222292
  isAutoConversationTitleInFlightRef.current = false;
221151
222293
  } else {
221152
- const client = await getClient();
221153
- client.conversations.update(conversationIdRef.current, {
222294
+ getBackend().updateConversation(conversationIdRef.current, {
221154
222295
  summary: conversationTitle
221155
222296
  }).then(() => {
221156
222297
  shouldAutoGenerateConversationTitleRef.current = false;
@@ -221568,9 +222709,8 @@ ${feedback}
221568
222709
  const invalidIdsDetected = isInvalidToolCallIdsError(detailFromRun) || isInvalidToolCallIdsError(latestErrorText);
221569
222710
  if (hasApprovalInPayload && invalidIdsDetected) {
221570
222711
  try {
221571
- const client = await getClient();
221572
- const agent = await client.agents.retrieve(agentIdRef.current);
221573
- const { pendingApprovals: serverApprovals } = await getResumeData(client, agent, conversationIdRef.current);
222712
+ const agent = await getBackend().retrieveAgent(agentIdRef.current);
222713
+ const { pendingApprovals: serverApprovals } = await getResumeDataFromBackend2(agent, conversationIdRef.current);
221574
222714
  if (serverApprovals && serverApprovals.length > 0) {
221575
222715
  const userMessage = currentInput.find((item) => item?.type === "message");
221576
222716
  if (userMessage && "content" in userMessage) {
@@ -221613,9 +222753,8 @@ ${feedback}
221613
222753
  })) {
221614
222754
  llmApiErrorRetriesRef.current += 1;
221615
222755
  try {
221616
- const client = await getClient();
221617
- const agent = await client.agents.retrieve(agentIdRef.current);
221618
- const { pendingApprovals: existingApprovals } = await getResumeData(client, agent, conversationIdRef.current);
222756
+ const agent = await getBackend().retrieveAgent(agentIdRef.current);
222757
+ const { pendingApprovals: existingApprovals } = await getResumeDataFromBackend2(agent, conversationIdRef.current);
221619
222758
  currentInput = rebuildInputWithFreshDenials(currentInput, existingApprovals ?? [], "Auto-denied: stale approval from interrupted session");
221620
222759
  } catch {
221621
222760
  currentInput = rebuildInputWithFreshDenials(currentInput, [], "");
@@ -222248,7 +223387,7 @@ ${feedback}
222248
223387
  setBtwState({ status: "forking", question });
222249
223388
  try {
222250
223389
  const isDefault = conversationIdRef.current === "default";
222251
- const forked = await forkConversation(conversationIdRef.current, {
223390
+ const forked = await getBackend().forkConversation(conversationIdRef.current, {
222252
223391
  ...isDefault ? { agentId } : {}
222253
223392
  });
222254
223393
  debugLog("btw", "forked conversationId=%s", forked.id);
@@ -222289,14 +223428,13 @@ ${feedback}
222289
223428
  })) {
222290
223429
  approvalRecoveryRetries += 1;
222291
223430
  try {
222292
- const client = await getClient();
222293
223431
  const currentAgentId = agentIdRef.current ?? agentId;
222294
223432
  if (!currentAgentId) {
222295
223433
  currentInput = rebuildInputWithFreshDenials(currentInput, [], "");
222296
223434
  continue;
222297
223435
  }
222298
- const agent = await client.agents.retrieve(currentAgentId);
222299
- const { pendingApprovals: existingApprovals } = await getResumeData(client, agent, forked.id);
223436
+ const agent = await getBackend().retrieveAgent(currentAgentId);
223437
+ const { pendingApprovals: existingApprovals } = await getResumeDataFromBackend2(agent, forked.id);
222300
223438
  currentInput = rebuildInputWithFreshDenials(currentInput, existingApprovals ?? [], STALE_APPROVAL_RECOVERY_DENIAL_REASON);
222301
223439
  } catch {
222302
223440
  currentInput = rebuildInputWithFreshDenials(currentInput, [], "");
@@ -222353,8 +223491,7 @@ ${feedback}
222353
223491
  if (!agentState) {
222354
223492
  throw new Error("Agent state not available");
222355
223493
  }
222356
- const client = await getClient();
222357
- const resumeData = await getResumeData(client, agentState, conversationId2);
223494
+ const resumeData = await getResumeDataFromBackend2(agentState, conversationId2);
222358
223495
  await maybeCarryOverActiveConversationModel(conversationId2);
222359
223496
  setConversationIdAndRef(conversationId2);
222360
223497
  setConversationAutoTitleEligibility(false);
@@ -222457,8 +223594,7 @@ ${feedback}
222457
223594
  const cmd = overlayCommand ?? commandRunner.start("/agents", "Switching agent...");
222458
223595
  cmd.update({ output: "Switching agent...", phase: "running" });
222459
223596
  try {
222460
- const client = await getClient();
222461
- const agent = await client.agents.retrieve(targetAgentId);
223597
+ const agent = await getBackend().retrieveAgent(targetAgentId);
222462
223598
  const targetConversationId = opts?.conversationId ?? "default";
222463
223599
  await updateProjectSettings({ lastAgent: targetAgentId });
222464
223600
  settingsManager.persistSession(targetAgentId, targetConversationId);
@@ -222547,8 +223683,7 @@ ${feedback}
222547
223683
  const isSelfHosted = !getServerUrl().includes("api.letta.com");
222548
223684
  if (isSelfHosted) {
222549
223685
  try {
222550
- const client = await getClient();
222551
- const availableHandles = (await client.models.list()).map((model) => model.handle).filter((handle) => typeof handle === "string");
223686
+ const availableHandles = (await backend4.listModels()).map((model) => model.handle).filter((handle) => typeof handle === "string");
222552
223687
  effectiveModel = selectDefaultAgentModel({
222553
223688
  preferredModel: effectiveModel,
222554
223689
  isSelfHosted: true,
@@ -222744,9 +223879,8 @@ ${expanded.command}` : expanded.command;
222744
223879
  return { blocked: false };
222745
223880
  }
222746
223881
  try {
222747
- const client = await getClient();
222748
- const agent = await client.agents.retrieve(agentId);
222749
- const { pendingApprovals: existingApprovals } = await getResumeData(client, agent, conversationIdRef.current);
223882
+ const agent = await getBackend().retrieveAgent(agentId);
223883
+ const { pendingApprovals: existingApprovals } = await getResumeDataFromBackend2(agent, conversationIdRef.current);
222750
223884
  if (!existingApprovals || existingApprovals.length === 0) {
222751
223885
  setNeedsEagerApprovalCheck(false);
222752
223886
  return { blocked: false };
@@ -222910,6 +224044,11 @@ ${SYSTEM_REMINDER_CLOSE}` : "";
222910
224044
  return { submitted: true };
222911
224045
  }
222912
224046
  if (trimmed === "/install-github-app") {
224047
+ if (getBackend().capabilities.localModelCatalog) {
224048
+ const cmd = commandRunner.start(trimmed, "Checking GitHub App installer support...");
224049
+ cmd.fail("GitHub App installation is not supported by the local backend.");
224050
+ return { submitted: true };
224051
+ }
222913
224052
  startOverlayCommand("install-github-app", "/install-github-app", "Opening GitHub App installer...", "GitHub App installer dismissed");
222914
224053
  setActiveOverlay("install-github-app");
222915
224054
  return { submitted: true };
@@ -223013,6 +224152,11 @@ ${SYSTEM_REMINDER_CLOSE}` : "";
223013
224152
  };
223014
224153
  const afterMcp = msg.trim().slice(4).trim();
223015
224154
  const firstWord = afterMcp.split(/\s+/)[0]?.toLowerCase();
224155
+ if (firstWord !== "help" && !getBackend().capabilities.serverSideToolManagement) {
224156
+ const cmd = commandRunner.start(msg, "Checking MCP support...");
224157
+ cmd.fail("MCP server management is not supported by the local backend yet.");
224158
+ return { submitted: true };
224159
+ }
223016
224160
  if (!firstWord) {
223017
224161
  startOverlayCommand("mcp", "/mcp", "Opening MCP server manager...", "MCP dialog dismissed");
223018
224162
  setActiveOverlay("mcp");
@@ -223527,8 +224671,8 @@ Type your task to begin the loop.`, true);
223527
224671
  setCommandRunning(true);
223528
224672
  await runEndHooks();
223529
224673
  try {
223530
- const client = await getClient();
223531
- const conversation = await client.conversations.create({
224674
+ const backend4 = getBackend();
224675
+ const conversation = await backend4.createConversation({
223532
224676
  agent_id: agentId,
223533
224677
  isolated_block_labels: [...ISOLATED_BLOCK_LABELS],
223534
224678
  ...conversationName && { summary: conversationName }
@@ -223568,13 +224712,13 @@ Type your task to begin the loop.`, true);
223568
224712
  setCommandRunning(true);
223569
224713
  await runEndHooks();
223570
224714
  try {
223571
- const client = await getClient();
223572
224715
  const isDefault = conversationIdRef.current === "default";
223573
- const forked = await forkConversation(conversationIdRef.current, {
224716
+ const backend4 = getBackend();
224717
+ const forked = await backend4.forkConversation(conversationIdRef.current, {
223574
224718
  ...isDefault ? { agentId } : {}
223575
224719
  });
223576
224720
  if (conversationSummary) {
223577
- await client.conversations.update(forked.id, {
224721
+ await backend4.updateConversation(forked.id, {
223578
224722
  summary: conversationSummary
223579
224723
  });
223580
224724
  }
@@ -223623,13 +224767,14 @@ Type your task to begin the loop.`, true);
223623
224767
  setCommandRunning(true);
223624
224768
  await runEndHooks();
223625
224769
  try {
223626
- const client = await getClient();
223627
- if (conversationIdRef.current === "default") {
224770
+ const backend4 = getBackend();
224771
+ if (conversationIdRef.current === "default" && !backend4.capabilities.localModelCatalog) {
224772
+ const client = await getClient();
223628
224773
  await client.agents.messages.reset(agentId, {
223629
224774
  add_default_initial_messages: false
223630
224775
  });
223631
224776
  }
223632
- const conversation = await client.conversations.create({
224777
+ const conversation = await backend4.createConversation({
223633
224778
  agent_id: agentId,
223634
224779
  isolated_block_labels: [...ISOLATED_BLOCK_LABELS]
223635
224780
  });
@@ -223776,20 +224921,20 @@ Type your task to begin the loop.`, true);
223776
224921
  });
223777
224922
  setCommandRunning(true);
223778
224923
  try {
223779
- const client = await getClient();
224924
+ const backend4 = getBackend();
223780
224925
  if (shouldAutoGenerate) {
223781
224926
  const conversationTitle = await generateConversationTitle();
223782
224927
  if (!conversationTitle) {
223783
224928
  cmd.fail("No conversation content available to generate a title");
223784
224929
  return { submitted: true };
223785
224930
  }
223786
- await client.conversations.update(conversationId, {
224931
+ await backend4.updateConversation(conversationId, {
223787
224932
  summary: conversationTitle
223788
224933
  });
223789
224934
  setConversationAutoTitleEligibility(false);
223790
224935
  cmd.finish(`Conversation title set to "${conversationTitle}"`, true);
223791
224936
  } else {
223792
- await client.conversations.update(conversationId, {
224937
+ await backend4.updateConversation(conversationId, {
223793
224938
  summary: newValue
223794
224939
  });
223795
224940
  setConversationAutoTitleEligibility(false);
@@ -223814,8 +224959,9 @@ Type your task to begin the loop.`, true);
223814
224959
  });
223815
224960
  setCommandRunning(true);
223816
224961
  try {
223817
- const client = await getClient();
223818
- await client.agents.update(agentId, { name: newValue });
224962
+ await getBackend().updateAgent(agentId, {
224963
+ name: newValue
224964
+ });
223819
224965
  updateAgentName(newValue);
223820
224966
  cmd.agentHint = `Your name is now "${newValue}" — acknowledge this and save your new name to memory.`;
223821
224967
  cmd.finish(`Agent renamed to "${newValue}"`, true);
@@ -223852,10 +224998,11 @@ Type your task to begin the loop.`, true);
223852
224998
  cmd.update({ output: "Updating description...", phase: "running" });
223853
224999
  setCommandRunning(true);
223854
225000
  try {
223855
- const client = await getClient();
223856
- await client.agents.update(agentId, {
225001
+ await getBackend().updateAgent(agentId, {
223857
225002
  description: newDescription
223858
225003
  });
225004
+ setAgentState((prev) => prev ? { ...prev, description: newDescription } : prev);
225005
+ setAgentDescription(newDescription);
223859
225006
  cmd.finish(`Description updated to "${newDescription}"`, true);
223860
225007
  } catch (error51) {
223861
225008
  const errorDetails = formatErrorDetails2(error51, agentId);
@@ -223898,8 +225045,7 @@ Type your task to begin the loop.`, true);
223898
225045
  setCommandRunning(true);
223899
225046
  try {
223900
225047
  if (agentState) {
223901
- const client = await getClient();
223902
- const resumeData = await getResumeData(client, agentState, targetConvId);
225048
+ const resumeData = await getResumeDataFromBackend2(agentState, targetConvId);
223903
225049
  setConversationIdAndRef(targetConvId);
223904
225050
  setConversationAutoTitleEligibility(false);
223905
225051
  pendingConversationSwitchRef.current = {
@@ -224173,6 +225319,10 @@ Press Enter to continue, or type anything to cancel.`, false, "running");
224173
225319
  }
224174
225320
  if (msg.trim() === "/export" || msg.trim() === "/download") {
224175
225321
  const cmd = commandRunner.start(msg.trim(), "Exporting agent file...");
225322
+ if (!getBackend().capabilities.agentFileImportExport) {
225323
+ cmd.fail("AgentFile export is not supported by the local backend yet.");
225324
+ return { submitted: true };
225325
+ }
224176
225326
  setCommandRunning(true);
224177
225327
  try {
224178
225328
  const client = await getClient();
@@ -224459,8 +225609,7 @@ ${SYSTEM_REMINDER_CLOSE}`;
224459
225609
  const reflectionConversationId = conversationIdRef.current;
224460
225610
  let systemPrompt;
224461
225611
  try {
224462
- const client = await getClient();
224463
- const agent = await client.agents.retrieve(agentId);
225612
+ const agent = await getBackend().retrieveAgent(agentId);
224464
225613
  systemPrompt = agent.system ?? undefined;
224465
225614
  } catch {}
224466
225615
  const autoPayload = await buildAutoReflectionPayload(agentId, reflectionConversationId, systemPrompt);
@@ -224727,6 +225876,48 @@ ${SYSTEM_REMINDER_CLOSE}`),
224727
225876
  if (registryCmd) {
224728
225877
  registryCmd.fail(`Unknown command: ${registryCommandName}`);
224729
225878
  }
225879
+ const skillCommandName = registryCommandName.slice(1);
225880
+ const { discoverClientSideSkills: discoverClientSideSkills2 } = await Promise.resolve().then(() => (init_clientSkills(), exports_clientSkills));
225881
+ const { getSkillSources: getSkillSources3 } = await Promise.resolve().then(() => (init_context(), exports_context));
225882
+ const { isUserInvocableSkill: isUserInvocableSkill3 } = await Promise.resolve().then(() => (init_skills(), exports_skills));
225883
+ const skillDiscovery = await discoverClientSideSkills2({
225884
+ agentId,
225885
+ skillSources: getSkillSources3()
225886
+ });
225887
+ const matchedSkill = skillDiscovery.skills.find((skill2) => skill2.id === skillCommandName && isUserInvocableSkill3(skill2));
225888
+ if (matchedSkill) {
225889
+ const cmd = commandRunner.start(trimmed, `Running /${matchedSkill.id}...`);
225890
+ const approvalCheck = await checkPendingApprovalsForSlashCommand();
225891
+ if (approvalCheck.blocked) {
225892
+ cmd.fail(`Pending approval(s). Resolve approvals before running /${matchedSkill.id}.`);
225893
+ return { submitted: false };
225894
+ }
225895
+ const args = trimmed.slice(`/${matchedSkill.id}`.length).trim();
225896
+ setCommandRunning(true);
225897
+ try {
225898
+ const { loadRenderedSkillContent: loadRenderedSkillContent2, wrapSkillContent: wrapSkillContent2 } = await Promise.resolve().then(() => (init_Skill2(), exports_Skill));
225899
+ const skillContent = await loadRenderedSkillContent2(matchedSkill.id, {
225900
+ agentId,
225901
+ args,
225902
+ allowDisabledModelInvocation: true
225903
+ });
225904
+ cmd.finish("Running skill...", true);
225905
+ await processConversationWithQueuedApprovals([
225906
+ {
225907
+ type: "message",
225908
+ role: "user",
225909
+ content: buildTextParts(wrapSkillContent2(matchedSkill.id, skillContent)),
225910
+ otid: randomUUID18()
225911
+ }
225912
+ ]);
225913
+ } catch (error51) {
225914
+ const errorDetails = formatErrorDetails2(error51, agentId);
225915
+ cmd.fail(`Failed to run skill: ${errorDetails}`);
225916
+ } finally {
225917
+ setCommandRunning(false);
225918
+ }
225919
+ return { submitted: true };
225920
+ }
224730
225921
  } else {
224731
225922
  if (registryCmd) {
224732
225923
  registryCmd.finish(result.output, result.success);
@@ -224817,8 +226008,7 @@ ${SYSTEM_REMINDER_CLOSE}
224817
226008
  const reflectionConversationId = conversationIdRef.current;
224818
226009
  let systemPrompt;
224819
226010
  try {
224820
- const client = await getClient();
224821
- const agent = await client.agents.retrieve(agentId);
226011
+ const agent = await getBackend().retrieveAgent(agentId);
224822
226012
  systemPrompt = agent.system ?? undefined;
224823
226013
  } catch {}
224824
226014
  const autoPayload = await buildAutoReflectionPayload(agentId, reflectionConversationId, systemPrompt);
@@ -224947,9 +226137,8 @@ ${SYSTEM_REMINDER_CLOSE}
224947
226137
  buffersRef.current.order.push(eagerStatusId);
224948
226138
  refreshDerived();
224949
226139
  try {
224950
- const client = await getClient();
224951
- const agent = await client.agents.retrieve(agentId);
224952
- const { pendingApprovals: existingApprovals } = await getResumeData(client, agent, conversationIdRef.current);
226140
+ const agent = await getBackend().retrieveAgent(agentId);
226141
+ const { pendingApprovals: existingApprovals } = await getResumeDataFromBackend2(agent, conversationIdRef.current);
224953
226142
  buffersRef.current.byId.delete(eagerStatusId);
224954
226143
  buffersRef.current.order = buffersRef.current.order.filter((id) => id !== eagerStatusId);
224955
226144
  if (userCancelledRef.current || abortControllerRef.current?.signal.aborted) {
@@ -225908,16 +227097,17 @@ ${guidance}`);
225908
227097
  phase: "running"
225909
227098
  });
225910
227099
  try {
225911
- const client = await getClient();
225912
227100
  const existing = agentState?.compaction_settings;
225913
227101
  const existingModel = existing?.model?.trim();
225914
- await client.agents.update(agentId, {
225915
- compaction_settings: {
225916
- ...existing,
225917
- model: existingModel || DEFAULT_SUMMARIZATION_MODEL,
225918
- mode
225919
- }
227102
+ const nextCompactionSettings = {
227103
+ ...existing,
227104
+ model: existingModel || DEFAULT_SUMMARIZATION_MODEL,
227105
+ mode
227106
+ };
227107
+ await getBackend().updateAgent(agentId, {
227108
+ compaction_settings: nextCompactionSettings
225920
227109
  });
227110
+ setAgentState((prev) => prev ? { ...prev, compaction_settings: nextCompactionSettings } : prev);
225921
227111
  cmd.finish(`Updated compaction mode to: ${mode}`, true);
225922
227112
  } catch (error51) {
225923
227113
  const errorDetails = formatErrorDetails2(error51, agentId);
@@ -226069,9 +227259,8 @@ ${guidance}`);
226069
227259
  if (action.conversationId === conversationId) {
226070
227260
  cmd.finish("Already on this conversation", true);
226071
227261
  } else {
226072
- const client = await getClient();
226073
227262
  if (agentState) {
226074
- const resumeData = await getResumeData(client, agentState, action.conversationId);
227263
+ const resumeData = await getResumeDataFromBackend2(agentState, action.conversationId);
226075
227264
  setConversationIdAndRef(action.conversationId);
226076
227265
  setConversationAutoTitleEligibility(false);
226077
227266
  pendingConversationSwitchRef.current = {
@@ -227382,8 +228571,7 @@ If using apply_patch, use this exact relative patch path: ${applyPatchRelativePa
227382
228571
  });
227383
228572
  try {
227384
228573
  if (agentState) {
227385
- const client = await getClient();
227386
- const resumeData = await getResumeData(client, agentState, convId);
228574
+ const resumeData = await getResumeDataFromBackend2(agentState, convId);
227387
228575
  setConversationIdAndRef(convId);
227388
228576
  setConversationAutoTitleEligibility(false);
227389
228577
  pendingConversationSwitchRef.current = {
@@ -227488,8 +228676,7 @@ If using apply_patch, use this exact relative patch path: ${applyPatchRelativePa
227488
228676
  phase: "running"
227489
228677
  });
227490
228678
  try {
227491
- const client = await getClient();
227492
- const conversation = await client.conversations.create({
228679
+ const conversation = await getBackend().createConversation({
227493
228680
  agent_id: agentId,
227494
228681
  isolated_block_labels: [...ISOLATED_BLOCK_LABELS]
227495
228682
  });
@@ -227577,8 +228764,7 @@ If using apply_patch, use this exact relative patch path: ${applyPatchRelativePa
227577
228764
  });
227578
228765
  try {
227579
228766
  if (agentState) {
227580
- const client = await getClient();
227581
- const resumeData = await getResumeData(client, agentState, actualTargetConv);
228767
+ const resumeData = await getResumeDataFromBackend2(agentState, actualTargetConv);
227582
228768
  setConversationIdAndRef(actualTargetConv);
227583
228769
  setConversationAutoTitleEligibility(false);
227584
228770
  pendingConversationSwitchRef.current = {
@@ -227734,9 +228920,10 @@ Open /mcp to attach or detach tools for this server.`, true);
227734
228920
  phase: "running"
227735
228921
  });
227736
228922
  try {
227737
- const client = await getClient();
227738
228923
  if (newName && newName !== agentName) {
227739
- await client.agents.update(agentId, { name: newName });
228924
+ await getBackend().updateAgent(agentId, {
228925
+ name: newName
228926
+ });
227740
228927
  updateAgentName(newName);
227741
228928
  }
227742
228929
  if (pinDialogLocal) {
@@ -227784,7 +228971,6 @@ var init_App2 = __esm(async () => {
227784
228971
  init_backend2();
227785
228972
  init_agents5();
227786
228973
  init_client2();
227787
- init_conversations2();
227788
228974
  init_metadata();
227789
228975
  init_constants();
227790
228976
  init_manager();
@@ -229121,9 +230307,8 @@ async function hydrateMemfsSettingFromAgent2(agent) {
229121
230307
  return enabled;
229122
230308
  }
229123
230309
  async function isMemfsEnabledOnServer2(agentId) {
229124
- const { getClient: getClient2 } = await Promise.resolve().then(() => (init_client2(), exports_client));
229125
- const client = await getClient2();
229126
- const agent = await client.agents.retrieve(agentId, {
230310
+ const { getBackend: getBackend2 } = await Promise.resolve().then(() => (init_backend2(), exports_backend));
230311
+ const agent = await getBackend2().retrieveAgent(agentId, {
229127
230312
  include: ["agent.tags"]
229128
230313
  });
229129
230314
  const { GIT_MEMORY_ENABLED_TAG: GIT_MEMORY_ENABLED_TAG2 } = await Promise.resolve().then(() => (init_memoryGit(), exports_memoryGit));
@@ -229256,7 +230441,8 @@ async function applyMemfsFlags2(agentId, memfsFlag, noMemfsFlag, options) {
229256
230441
  const backend4 = getBackend2();
229257
230442
  if (backend4.capabilities.localMemfs) {
229258
230443
  if (noMemfsFlag) {
229259
- throw new Error("Disabling MemFS is not supported by the local backend.");
230444
+ settingsManager3.setMemfsEnabled(agentId, false);
230445
+ return { action: "disabled" };
229260
230446
  }
229261
230447
  const memoryDir = getScopedMemoryFilesystemRoot2(agentId);
229262
230448
  const { initializeLocalMemoryRepo: initializeLocalMemoryRepo2 } = await Promise.resolve().then(() => (init_memoryGit(), exports_memoryGit));
@@ -229433,7 +230619,7 @@ async function initSecretsFromServer2(agentId, cachedAgent) {
229433
230619
  setCache2(agentId, {});
229434
230620
  return;
229435
230621
  }
229436
- const agent = cachedAgent ?? await (await getClient()).agents.retrieve(agentId, {
230622
+ const agent = cachedAgent ?? await getBackend().retrieveAgent(agentId, {
229437
230623
  include: ["agent.secrets"]
229438
230624
  });
229439
230625
  const secrets2 = {};
@@ -229466,6 +230652,9 @@ async function refreshAndListSecrets2(agentIdArg) {
229466
230652
  return Object.keys(cache5).sort().map((key) => ({ key, value: cache5[key] ?? "" }));
229467
230653
  }
229468
230654
  async function applySecretBatch2(options, agentIdArg) {
230655
+ if (!getBackend().capabilities.serverSecrets) {
230656
+ throw new Error("Agent secrets are not supported by this backend yet");
230657
+ }
229469
230658
  const agentId = resolveSecretsAgentId2(agentIdArg);
229470
230659
  if (!agentId) {
229471
230660
  throw new Error("No agent context set. Agent ID is required.");
@@ -229477,8 +230666,7 @@ async function applySecretBatch2(options, agentIdArg) {
229477
230666
  for (const rawKey of options.unset ?? []) {
229478
230667
  delete next[rawKey.toUpperCase()];
229479
230668
  }
229480
- const client = await getClient();
229481
- await client.agents.update(agentId, { secrets: next });
230669
+ await getBackend().updateAgent(agentId, { secrets: next });
229482
230670
  setCache2(agentId, next);
229483
230671
  return Object.keys(next).sort();
229484
230672
  }
@@ -229486,14 +230674,13 @@ async function setSecretOnServer2(key, value, agentIdArg) {
229486
230674
  if (!getBackend().capabilities.serverSecrets) {
229487
230675
  throw new Error("Agent secrets are not supported by this backend yet");
229488
230676
  }
229489
- const client = await getClient();
229490
230677
  const agentId = resolveSecretsAgentId2(agentIdArg);
229491
230678
  if (!agentId) {
229492
230679
  throw new Error("No agent context set. Agent ID is required.");
229493
230680
  }
229494
230681
  const secrets2 = { ...loadSecrets2(agentId) };
229495
230682
  secrets2[key] = value;
229496
- await client.agents.update(agentId, { secrets: secrets2 });
230683
+ await getBackend().updateAgent(agentId, { secrets: secrets2 });
229497
230684
  setCache2(agentId, secrets2);
229498
230685
  }
229499
230686
  async function deleteSecretOnServer2(key, agentIdArg) {
@@ -229509,8 +230696,7 @@ async function deleteSecretOnServer2(key, agentIdArg) {
229509
230696
  return false;
229510
230697
  }
229511
230698
  delete secrets2[key];
229512
- const client = await getClient();
229513
- await client.agents.update(agentId, { secrets: secrets2 });
230699
+ await getBackend().updateAgent(agentId, { secrets: secrets2 });
229514
230700
  setCache2(agentId, secrets2);
229515
230701
  return true;
229516
230702
  }
@@ -229526,7 +230712,6 @@ var SECRETS_CACHE_KEY2;
229526
230712
  var init_secretsStore2 = __esm(() => {
229527
230713
  init_context();
229528
230714
  init_backend2();
229529
- init_client2();
229530
230715
  SECRETS_CACHE_KEY2 = Symbol.for("@letta/secretsCache");
229531
230716
  });
229532
230717
 
@@ -230059,6 +231244,9 @@ init_available_models();
230059
231244
  function supportsDistinctAnthropicXHighEffort2(modelHandle) {
230060
231245
  return modelHandle.includes("claude-opus-4-7");
230061
231246
  }
231247
+ function isRecord10(value) {
231248
+ return typeof value === "object" && value !== null && !Array.isArray(value);
231249
+ }
230062
231250
  function buildModelSettings2(modelHandle, updateArgs) {
230063
231251
  const isOpenAI = modelHandle.startsWith("openai/") || modelHandle.startsWith(`${OPENAI_CODEX_PROVIDER_NAME}/`);
230064
231252
  const isAnthropic = modelHandle.startsWith("anthropic/") || modelHandle.startsWith("claude-pro-max/") || modelHandle.startsWith("minimax/");
@@ -230181,6 +231369,12 @@ function buildModelSettings2(modelHandle, updateArgs) {
230181
231369
  if (typeof updateArgs?.max_output_tokens === "number" && "provider_type" in settings) {
230182
231370
  settings.max_output_tokens = updateArgs.max_output_tokens;
230183
231371
  }
231372
+ if (isRecord10(updateArgs?.modalities)) {
231373
+ settings.modalities = updateArgs.modalities;
231374
+ }
231375
+ if (isRecord10(updateArgs?.capabilities)) {
231376
+ settings.capabilities = updateArgs.capabilities;
231377
+ }
230184
231378
  return settings;
230185
231379
  }
230186
231380
  async function updateAgentLLMConfig2(agentId, modelHandle, updateArgs, options) {
@@ -230504,6 +231698,15 @@ async function getBillingTier2() {
230504
231698
  }
230505
231699
  }
230506
231700
 
231701
+ // src/backend/local/paths.ts
231702
+ var LOCAL_BACKEND_NO_MEMFS_ENV2 = "LETTA_LOCAL_BACKEND_NO_MEMFS";
231703
+ function isTruthyEnv3(value) {
231704
+ return value === "1" || value?.toLowerCase() === "true";
231705
+ }
231706
+ function isLocalBackendNoMemfsEnvEnabled2(env = process.env) {
231707
+ return isTruthyEnv3(env[LOCAL_BACKEND_NO_MEMFS_ENV2]);
231708
+ }
231709
+
230507
231710
  // src/cli/args.ts
230508
231711
  import { parseArgs } from "node:util";
230509
231712
  var CLI_FLAG_CATALOG = {
@@ -232841,7 +234044,7 @@ function validateRegistryHandleOrThrow(handle) {
232841
234044
  // src/cli/subcommands/agents.ts
232842
234045
  init_create();
232843
234046
  init_personality();
232844
- init_client2();
234047
+ init_backend2();
232845
234048
  init_settings_manager();
232846
234049
  import { parseArgs as parseArgs2 } from "node:util";
232847
234050
  function printUsage() {
@@ -232975,8 +234178,7 @@ async function runCreateAction(values) {
232975
234178
  if (values.pinned) {
232976
234179
  settingsManager.pinGlobal(agentId);
232977
234180
  }
232978
- const client = await getClient();
232979
- const updatedAgent = await client.agents.retrieve(agentId);
234181
+ const updatedAgent = await getBackend().retrieveAgent(agentId);
232980
234182
  console.log(JSON.stringify(updatedAgent, null, 2));
232981
234183
  return 0;
232982
234184
  } catch (error51) {
@@ -233006,8 +234208,7 @@ async function runListAction(values) {
233006
234208
  params.include = ["agent.blocks"];
233007
234209
  }
233008
234210
  try {
233009
- const client = await getClient();
233010
- const result = await client.agents.list(params);
234211
+ const result = await getBackend().listAgents(params);
233011
234212
  console.log(JSON.stringify(result, null, 2));
233012
234213
  return 0;
233013
234214
  } catch (error51) {
@@ -233016,234 +234217,6 @@ async function runListAction(values) {
233016
234217
  }
233017
234218
  }
233018
234219
 
233019
- // src/cli/subcommands/blocks.ts
233020
- init_client2();
233021
- init_settings_manager();
233022
- import { parseArgs as parseArgs3 } from "node:util";
233023
- function printUsage2() {
233024
- console.log(`
233025
- Usage:
233026
- letta blocks list --agent <id> [--limit <n>]
233027
- letta blocks copy --block-id <id> [--label <new-label>] [--agent <id>] [--override]
233028
- letta blocks attach --block-id <id> [--agent <id>] [--read-only] [--override]
233029
-
233030
- Notes:
233031
- - Output is JSON only.
233032
- - Uses CLI auth; override with LETTA_API_KEY/LETTA_BASE_URL if needed.
233033
- - Default target agent for copy/attach is LETTA_AGENT_ID.
233034
- `.trim());
233035
- }
233036
- function parseLimit2(value, fallback) {
233037
- if (typeof value !== "string" || value.length === 0)
233038
- return fallback;
233039
- const parsed = Number.parseInt(value, 10);
233040
- return Number.isNaN(parsed) ? fallback : parsed;
233041
- }
233042
- function getAgentId2(agentFromArgs, agentIdFromArgs) {
233043
- return agentFromArgs || agentIdFromArgs || process.env.LETTA_AGENT_ID || "";
233044
- }
233045
- var BLOCKS_OPTIONS = {
233046
- help: { type: "boolean", short: "h" },
233047
- agent: { type: "string" },
233048
- "agent-id": { type: "string" },
233049
- limit: { type: "string" },
233050
- "block-id": { type: "string" },
233051
- label: { type: "string" },
233052
- override: { type: "boolean" },
233053
- "read-only": { type: "boolean" }
233054
- };
233055
- function parseBlocksArgs(argv) {
233056
- return parseArgs3({
233057
- args: argv,
233058
- options: BLOCKS_OPTIONS,
233059
- strict: true,
233060
- allowPositionals: true
233061
- });
233062
- }
233063
- async function copyBlock(client, blockId, options) {
233064
- const currentAgentId = getAgentId2(options?.targetAgentId);
233065
- if (!currentAgentId) {
233066
- throw new Error("Missing agent id. Set LETTA_AGENT_ID or pass --agent/--agent-id.");
233067
- }
233068
- let detachedBlock;
233069
- const sourceBlock = await client.blocks.retrieve(blockId);
233070
- const targetLabel = options?.labelOverride || sourceBlock.label || "migrated-block";
233071
- if (options?.override) {
233072
- const currentBlocksResponse = await client.agents.blocks.list(currentAgentId);
233073
- const currentBlocks = Array.isArray(currentBlocksResponse) ? currentBlocksResponse : currentBlocksResponse.items || [];
233074
- const conflictingBlock = currentBlocks.find((b) => b.label === targetLabel);
233075
- if (conflictingBlock) {
233076
- console.error(`Detaching existing block with label "${targetLabel}" (${conflictingBlock.id})...`);
233077
- detachedBlock = conflictingBlock;
233078
- try {
233079
- await client.agents.blocks.detach(conflictingBlock.id, {
233080
- agent_id: currentAgentId
233081
- });
233082
- } catch (detachError) {
233083
- throw new Error(`Failed to detach existing block "${targetLabel}": ${detachError instanceof Error ? detachError.message : String(detachError)}`);
233084
- }
233085
- }
233086
- }
233087
- let newBlock;
233088
- try {
233089
- newBlock = await client.blocks.create({
233090
- label: targetLabel,
233091
- value: sourceBlock.value,
233092
- description: sourceBlock.description || undefined,
233093
- limit: sourceBlock.limit
233094
- });
233095
- } catch (createError) {
233096
- if (detachedBlock) {
233097
- console.error(`Create failed, reattaching original block "${detachedBlock.label}"...`);
233098
- try {
233099
- await client.agents.blocks.attach(detachedBlock.id, {
233100
- agent_id: currentAgentId
233101
- });
233102
- console.error("Original block reattached successfully.");
233103
- } catch {
233104
- console.error(`WARNING: Failed to reattach original block! Block ID: ${detachedBlock.id}`);
233105
- }
233106
- }
233107
- throw createError;
233108
- }
233109
- let attachResult;
233110
- try {
233111
- attachResult = await client.agents.blocks.attach(newBlock.id, {
233112
- agent_id: currentAgentId
233113
- });
233114
- } catch (attachError) {
233115
- if (detachedBlock) {
233116
- console.error(`Attach failed, reattaching original block "${detachedBlock.label}"...`);
233117
- try {
233118
- await client.agents.blocks.attach(detachedBlock.id, {
233119
- agent_id: currentAgentId
233120
- });
233121
- console.error("Original block reattached successfully.");
233122
- } catch {
233123
- console.error(`WARNING: Failed to reattach original block! Block ID: ${detachedBlock.id}`);
233124
- }
233125
- }
233126
- throw attachError;
233127
- }
233128
- return { sourceBlock, newBlock, attachResult, detachedBlock };
233129
- }
233130
- async function attachBlock(client, blockId, options) {
233131
- const currentAgentId = getAgentId2(options?.targetAgentId);
233132
- if (!currentAgentId) {
233133
- throw new Error("Missing agent id. Set LETTA_AGENT_ID or pass --agent/--agent-id.");
233134
- }
233135
- let detachedBlock;
233136
- if (options?.override) {
233137
- const sourceBlock = await client.blocks.retrieve(blockId);
233138
- const sourceLabel = sourceBlock.label;
233139
- const currentBlocksResponse = await client.agents.blocks.list(currentAgentId);
233140
- const currentBlocks = Array.isArray(currentBlocksResponse) ? currentBlocksResponse : currentBlocksResponse.items || [];
233141
- const conflictingBlock = currentBlocks.find((b) => b.label === sourceLabel);
233142
- if (conflictingBlock) {
233143
- console.error(`Detaching existing block with label "${sourceLabel}" (${conflictingBlock.id})...`);
233144
- detachedBlock = conflictingBlock;
233145
- try {
233146
- await client.agents.blocks.detach(conflictingBlock.id, {
233147
- agent_id: currentAgentId
233148
- });
233149
- } catch (detachError) {
233150
- throw new Error(`Failed to detach existing block "${sourceLabel}": ${detachError instanceof Error ? detachError.message : String(detachError)}`);
233151
- }
233152
- }
233153
- }
233154
- let attachResult;
233155
- try {
233156
- attachResult = await client.agents.blocks.attach(blockId, {
233157
- agent_id: currentAgentId
233158
- });
233159
- } catch (attachError) {
233160
- if (detachedBlock) {
233161
- console.error(`Attach failed, reattaching original block "${detachedBlock.label}"...`);
233162
- try {
233163
- await client.agents.blocks.attach(detachedBlock.id, {
233164
- agent_id: currentAgentId
233165
- });
233166
- console.error("Original block reattached successfully.");
233167
- } catch {
233168
- console.error(`WARNING: Failed to reattach original block! Block ID: ${detachedBlock.id}`);
233169
- }
233170
- }
233171
- throw attachError;
233172
- }
233173
- if (options?.readOnly) {
233174
- console.warn("Note: read_only flag is set on the block itself, not per-agent. " + "Use the block update API to set read_only if needed.");
233175
- }
233176
- return { attachResult, detachedBlock };
233177
- }
233178
- async function runBlocksSubcommand(argv) {
233179
- let parsed;
233180
- try {
233181
- parsed = parseBlocksArgs(argv);
233182
- } catch (error51) {
233183
- const message = error51 instanceof Error ? error51.message : String(error51);
233184
- console.error(`Error: ${message}`);
233185
- printUsage2();
233186
- return 1;
233187
- }
233188
- const [action] = parsed.positionals;
233189
- if (parsed.values.help || !action || action === "help") {
233190
- printUsage2();
233191
- return 0;
233192
- }
233193
- try {
233194
- await settingsManager.initialize();
233195
- const client = await getClient();
233196
- if (action === "list") {
233197
- const agentId = parsed.values.agent || parsed.values["agent-id"] || "";
233198
- if (!agentId || typeof agentId !== "string") {
233199
- console.error("Missing required --agent <id>.");
233200
- return 1;
233201
- }
233202
- const result = await client.agents.blocks.list(agentId, {
233203
- limit: parseLimit2(parsed.values.limit, 1000)
233204
- });
233205
- console.log(JSON.stringify(result, null, 2));
233206
- return 0;
233207
- }
233208
- if (action === "copy") {
233209
- const blockId = parsed.values["block-id"];
233210
- if (!blockId || typeof blockId !== "string") {
233211
- console.error("Missing required --block-id <id>.");
233212
- return 1;
233213
- }
233214
- const agentId = getAgentId2(parsed.values.agent, parsed.values["agent-id"]);
233215
- const result = await copyBlock(client, blockId, {
233216
- labelOverride: typeof parsed.values.label === "string" ? parsed.values.label : undefined,
233217
- targetAgentId: agentId,
233218
- override: parsed.values.override === true
233219
- });
233220
- console.log(JSON.stringify(result, null, 2));
233221
- return 0;
233222
- }
233223
- if (action === "attach") {
233224
- const blockId = parsed.values["block-id"];
233225
- if (!blockId || typeof blockId !== "string") {
233226
- console.error("Missing required --block-id <id>.");
233227
- return 1;
233228
- }
233229
- const agentId = getAgentId2(parsed.values.agent, parsed.values["agent-id"]);
233230
- const result = await attachBlock(client, blockId, {
233231
- readOnly: parsed.values["read-only"] === true,
233232
- override: parsed.values.override === true,
233233
- targetAgentId: agentId
233234
- });
233235
- console.log(JSON.stringify(result, null, 2));
233236
- return 0;
233237
- }
233238
- } catch (error51) {
233239
- console.error(error51 instanceof Error ? error51.message : String(error51));
233240
- return 1;
233241
- }
233242
- console.error(`Unknown action: ${action}`);
233243
- printUsage2();
233244
- return 1;
233245
- }
233246
-
233247
234220
  // src/cli/subcommands/channels.ts
233248
234221
  init_accounts();
233249
234222
  init_pairing();
@@ -233252,8 +234225,8 @@ init_registry();
233252
234225
  init_routing();
233253
234226
  init_runtimeDeps();
233254
234227
  await init_service();
233255
- import { parseArgs as parseArgs4 } from "node:util";
233256
- function printUsage3() {
234228
+ import { parseArgs as parseArgs3 } from "node:util";
234229
+ function printUsage2() {
233257
234230
  console.log(`
233258
234231
  Usage:
233259
234232
  letta channels install <channel> Install channel runtime dependencies
@@ -233317,14 +234290,14 @@ var CHANNELS_OPTIONS = {
233317
234290
  code: { type: "string" }
233318
234291
  };
233319
234292
  function parseChannelsArgs(argv) {
233320
- return parseArgs4({
234293
+ return parseArgs3({
233321
234294
  args: argv,
233322
234295
  options: CHANNELS_OPTIONS,
233323
234296
  strict: true,
233324
234297
  allowPositionals: true
233325
234298
  });
233326
234299
  }
233327
- function getAgentId3(fromArgs) {
234300
+ function getAgentId2(fromArgs) {
233328
234301
  return fromArgs || process.env.LETTA_AGENT_ID || "";
233329
234302
  }
233330
234303
  function getConversationId2(fromArgs) {
@@ -233461,7 +234434,7 @@ function handleRouteAdd(values) {
233461
234434
  const channelId = values.channel;
233462
234435
  const accountId = values["account-id"];
233463
234436
  const chatId = values["chat-id"];
233464
- const agentId = getAgentId3(values.agent);
234437
+ const agentId = getAgentId2(values.agent);
233465
234438
  const conversationId = getConversationId2(values.conversation);
233466
234439
  if (!channelId) {
233467
234440
  console.error("Error: --channel is required.");
@@ -233549,7 +234522,7 @@ async function handlePair(values) {
233549
234522
  const channelId = values.channel;
233550
234523
  const accountId = values["account-id"];
233551
234524
  const code = values.code;
233552
- const agentId = getAgentId3(values.agent);
234525
+ const agentId = getAgentId2(values.agent);
233553
234526
  const conversationId = getConversationId2(values.conversation);
233554
234527
  if (!channelId) {
233555
234528
  console.error("Error: --channel is required.");
@@ -233580,7 +234553,7 @@ async function handlePair(values) {
233580
234553
  function handleBind(values) {
233581
234554
  const channelId = values.channel;
233582
234555
  const accountId = values["account-id"];
233583
- const agentId = getAgentId3(values.agent);
234556
+ const agentId = getAgentId2(values.agent);
233584
234557
  if (!channelId) {
233585
234558
  console.error("Error: --channel is required.");
233586
234559
  return 1;
@@ -233620,7 +234593,7 @@ function handleBind(values) {
233620
234593
  async function runChannelsSubcommand(argv) {
233621
234594
  const { values, positionals } = parseChannelsArgs(argv);
233622
234595
  if (values.help) {
233623
- printUsage3();
234596
+ printUsage2();
233624
234597
  return 0;
233625
234598
  }
233626
234599
  const [action, ...rest] = positionals;
@@ -233663,7 +234636,7 @@ async function runChannelsSubcommand(argv) {
233663
234636
  return await handlePair(values);
233664
234637
  default:
233665
234638
  if (!action) {
233666
- printUsage3();
234639
+ printUsage2();
233667
234640
  return 0;
233668
234641
  }
233669
234642
  console.error(`Unknown channels action: "${action}". Use: install, configure, status, route, bind, pair`);
@@ -233678,7 +234651,7 @@ init_connect_normalize();
233678
234651
  init_connect_oauth_core();
233679
234652
  import { createInterface as createInterface4 } from "node:readline/promises";
233680
234653
  import { Writable } from "node:stream";
233681
- import { parseArgs as parseArgs5 } from "node:util";
234654
+ import { parseArgs as parseArgs4 } from "node:util";
233682
234655
  var CONNECT_OPTIONS = {
233683
234656
  help: { type: "boolean", short: "h" },
233684
234657
  "api-key": { type: "string" },
@@ -233769,7 +234742,7 @@ async function runConnectSubcommand(argv, deps = {}) {
233769
234742
  const io = { ...DEFAULT_DEPS2, ...deps };
233770
234743
  let parsed;
233771
234744
  try {
233772
- parsed = parseArgs5({
234745
+ parsed = parseArgs4({
233773
234746
  args: argv,
233774
234747
  options: CONNECT_OPTIONS,
233775
234748
  strict: true,
@@ -233878,8 +234851,8 @@ async function runConnectSubcommand(argv, deps = {}) {
233878
234851
 
233879
234852
  // src/cli/subcommands/cron.ts
233880
234853
  init_cron();
233881
- import { parseArgs as parseArgs6 } from "node:util";
233882
- function printUsage4() {
234854
+ import { parseArgs as parseArgs5 } from "node:util";
234855
+ function printUsage3() {
233883
234856
  console.log(`
233884
234857
  Usage:
233885
234858
  letta cron add --prompt <text> --every <interval> [options]
@@ -233923,14 +234896,14 @@ var CRON_OPTIONS = {
233923
234896
  all: { type: "boolean" }
233924
234897
  };
233925
234898
  function parseCronArgs(argv) {
233926
- return parseArgs6({
234899
+ return parseArgs5({
233927
234900
  args: argv,
233928
234901
  options: CRON_OPTIONS,
233929
234902
  strict: true,
233930
234903
  allowPositionals: true
233931
234904
  });
233932
234905
  }
233933
- function getAgentId4(fromArgs) {
234906
+ function getAgentId3(fromArgs) {
233934
234907
  return fromArgs || process.env.LETTA_AGENT_ID || "";
233935
234908
  }
233936
234909
  function getConversationId3(fromArgs) {
@@ -233952,7 +234925,7 @@ function handleAdd(values) {
233952
234925
  console.error("Error: --prompt is required.");
233953
234926
  return 1;
233954
234927
  }
233955
- const agentId = getAgentId4(values.agent);
234928
+ const agentId = getAgentId3(values.agent);
233956
234929
  if (!agentId) {
233957
234930
  console.error("Error: --agent or LETTA_AGENT_ID required.");
233958
234931
  return 1;
@@ -234073,7 +235046,7 @@ function handleGet(positionals) {
234073
235046
  }
234074
235047
  function handleDelete(values, positionals) {
234075
235048
  if (values.all) {
234076
- const agentId = getAgentId4(values.agent);
235049
+ const agentId = getAgentId3(values.agent);
234077
235050
  if (!agentId) {
234078
235051
  console.error("Error: --agent or LETTA_AGENT_ID required with --all.");
234079
235052
  return 1;
@@ -234101,12 +235074,12 @@ async function runCronSubcommand(argv) {
234101
235074
  parsed = parseCronArgs(argv);
234102
235075
  } catch (err) {
234103
235076
  console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
234104
- printUsage4();
235077
+ printUsage3();
234105
235078
  return 1;
234106
235079
  }
234107
235080
  const [action] = parsed.positionals;
234108
235081
  if (parsed.values.help || !action || action === "help") {
234109
- printUsage4();
235082
+ printUsage3();
234110
235083
  return 0;
234111
235084
  }
234112
235085
  switch (action) {
@@ -234120,7 +235093,7 @@ async function runCronSubcommand(argv) {
234120
235093
  return handleDelete(parsed.values, parsed.positionals);
234121
235094
  default:
234122
235095
  console.error(`Unknown action: ${action}`);
234123
- printUsage4();
235096
+ printUsage3();
234124
235097
  return 1;
234125
235098
  }
234126
235099
  }
@@ -234135,7 +235108,7 @@ await __promiseAll([
234135
235108
  ]);
234136
235109
  var import_react30 = __toESM(require_react(), 1);
234137
235110
  import { hostname as hostname4 } from "node:os";
234138
- import { parseArgs as parseArgs7 } from "node:util";
235111
+ import { parseArgs as parseArgs6 } from "node:util";
234139
235112
 
234140
235113
  // src/websocket/listen-log.ts
234141
235114
  import {
@@ -234454,7 +235427,7 @@ async function resolveListenerRegistrationOptions(deviceId, connectionName) {
234454
235427
  };
234455
235428
  }
234456
235429
  async function runListenSubcommand(argv) {
234457
- const { values } = parseArgs7({
235430
+ const { values } = parseArgs6({
234458
235431
  args: argv,
234459
235432
  options: {
234460
235433
  "env-name": { type: "string" },
@@ -234790,7 +235763,7 @@ import { cpSync, existsSync as existsSync35, mkdirSync as mkdirSync25, rmSync as
234790
235763
  import { readdir as readdir7 } from "node:fs/promises";
234791
235764
  import { homedir as homedir26 } from "node:os";
234792
235765
  import { join as join38 } from "node:path";
234793
- import { parseArgs as parseArgs8 } from "node:util";
235766
+ import { parseArgs as parseArgs7 } from "node:util";
234794
235767
 
234795
235768
  // src/cli/subcommands/memoryTokens.ts
234796
235769
  init_systemPromptSize();
@@ -234871,7 +235844,7 @@ async function runMemoryTokensAction(options) {
234871
235844
  }
234872
235845
 
234873
235846
  // src/cli/subcommands/memory.ts
234874
- function printUsage5() {
235847
+ function printUsage4() {
234875
235848
  console.log(`
234876
235849
  Usage:
234877
235850
  letta memory status [--agent <id>]
@@ -234900,7 +235873,7 @@ Examples:
234900
235873
  letta memory tokens --memory-dir ~/.letta/agents/agent-123/memory --format json
234901
235874
  `.trim());
234902
235875
  }
234903
- function getAgentId5(agentFromArgs, agentIdFromArgs) {
235876
+ function getAgentId4(agentFromArgs, agentIdFromArgs) {
234904
235877
  return agentFromArgs || agentIdFromArgs || process.env.LETTA_AGENT_ID || "";
234905
235878
  }
234906
235879
  var MEMORY_OPTIONS = {
@@ -234916,7 +235889,7 @@ var MEMORY_OPTIONS = {
234916
235889
  quiet: { type: "boolean" }
234917
235890
  };
234918
235891
  function parseMemoryArgs(argv) {
234919
- return parseArgs8({
235892
+ return parseArgs7({
234920
235893
  args: argv,
234921
235894
  options: MEMORY_OPTIONS,
234922
235895
  strict: true,
@@ -234977,15 +235950,15 @@ async function runMemorySubcommand(argv) {
234977
235950
  } catch (error51) {
234978
235951
  const message = error51 instanceof Error ? error51.message : String(error51);
234979
235952
  console.error(`Error: ${message}`);
234980
- printUsage5();
235953
+ printUsage4();
234981
235954
  return 1;
234982
235955
  }
234983
235956
  const [action] = parsed.positionals;
234984
235957
  if (parsed.values.help || !action || action === "help") {
234985
- printUsage5();
235958
+ printUsage4();
234986
235959
  return 0;
234987
235960
  }
234988
- const agentId = getAgentId5(parsed.values.agent, parsed.values["agent-id"]);
235961
+ const agentId = getAgentId4(parsed.values.agent, parsed.values["agent-id"]);
234989
235962
  if (action === "tokens") {
234990
235963
  return runMemoryTokensAction({
234991
235964
  memoryDir: parsed.values["memory-dir"],
@@ -235118,17 +236091,18 @@ async function runMemorySubcommand(argv) {
235118
236091
  return 1;
235119
236092
  }
235120
236093
  console.error(`Unknown action: ${action}`);
235121
- printUsage5();
236094
+ printUsage4();
235122
236095
  return 1;
235123
236096
  }
235124
236097
 
235125
236098
  // src/cli/subcommands/messages.ts
235126
- init_client2();
236099
+ init_backend2();
236100
+ init_messageSearch();
235127
236101
  init_settings_manager();
235128
236102
  import { writeFile as writeFile11 } from "node:fs/promises";
235129
236103
  import { resolve as resolve33 } from "node:path";
235130
- import { parseArgs as parseArgs9 } from "node:util";
235131
- function printUsage6() {
236104
+ import { parseArgs as parseArgs8 } from "node:util";
236105
+ function printUsage5() {
235132
236106
  console.log(`
235133
236107
  Usage:
235134
236108
  letta messages search --query <text> [options]
@@ -235171,7 +236145,7 @@ Notes:
235171
236145
  - For agent-to-agent messaging, use: letta -p --from-agent <sender-id> --agent <target-id> "message"
235172
236146
  `.trim());
235173
236147
  }
235174
- function parseLimit3(value, fallback) {
236148
+ function parseLimit2(value, fallback) {
235175
236149
  if (typeof value !== "string" || value.length === 0)
235176
236150
  return fallback;
235177
236151
  const parsed = Number.parseInt(value, 10);
@@ -235191,9 +236165,23 @@ function parseOrder(value) {
235191
236165
  }
235192
236166
  return;
235193
236167
  }
235194
- function getAgentId6(agentFromArgs, agentIdFromArgs) {
236168
+ function getAgentId5(agentFromArgs, agentIdFromArgs) {
235195
236169
  return agentFromArgs || agentIdFromArgs || process.env.LETTA_AGENT_ID || "";
235196
236170
  }
236171
+ function pageItems2(page) {
236172
+ if (Array.isArray(page))
236173
+ return page;
236174
+ if (page && typeof page === "object") {
236175
+ const maybePage = page;
236176
+ if (typeof maybePage.getPaginatedItems === "function") {
236177
+ return maybePage.getPaginatedItems();
236178
+ }
236179
+ if (Array.isArray(maybePage.items)) {
236180
+ return maybePage.items;
236181
+ }
236182
+ }
236183
+ return [];
236184
+ }
235197
236185
  var MESSAGES_OPTIONS = {
235198
236186
  help: { type: "boolean", short: "h" },
235199
236187
  query: { type: "string" },
@@ -235214,7 +236202,7 @@ var MESSAGES_OPTIONS = {
235214
236202
  output: { type: "string" }
235215
236203
  };
235216
236204
  function parseMessagesArgs(argv) {
235217
- return parseArgs9({
236205
+ return parseArgs8({
235218
236206
  args: argv,
235219
236207
  options: MESSAGES_OPTIONS,
235220
236208
  strict: true,
@@ -235228,17 +236216,17 @@ async function runMessagesSubcommand(argv) {
235228
236216
  } catch (error51) {
235229
236217
  const message = error51 instanceof Error ? error51.message : String(error51);
235230
236218
  console.error(`Error: ${message}`);
235231
- printUsage6();
236219
+ printUsage5();
235232
236220
  return 1;
235233
236221
  }
235234
236222
  const [action] = parsed.positionals;
235235
236223
  if (parsed.values.help || !action || action === "help") {
235236
- printUsage6();
236224
+ printUsage5();
235237
236225
  return 0;
235238
236226
  }
235239
236227
  try {
235240
236228
  await settingsManager.initialize();
235241
- const client = await getClient();
236229
+ const backend4 = getBackend();
235242
236230
  const renderText = (value) => {
235243
236231
  if (typeof value === "string")
235244
236232
  return value;
@@ -235322,13 +236310,13 @@ async function runMessagesSubcommand(argv) {
235322
236310
  const seenIds = new Set;
235323
236311
  let cursorBefore;
235324
236312
  for (let pageIndex = 0;pageIndex < maxPages; pageIndex += 1) {
235325
- const page = await client.conversations.messages.list(conversationId, {
236313
+ const page = await backend4.listConversationMessages(conversationId, {
235326
236314
  limit: pageLimit,
235327
236315
  order: "desc",
235328
236316
  ...conversationId === "default" && agentIdForDefault ? { agent_id: agentIdForDefault } : {},
235329
236317
  ...cursorBefore ? { before: cursorBefore } : {}
235330
236318
  });
235331
- const items = page.getPaginatedItems();
236319
+ const items = pageItems2(page);
235332
236320
  if (items.length === 0) {
235333
236321
  break;
235334
236322
  }
@@ -235355,24 +236343,24 @@ async function runMessagesSubcommand(argv) {
235355
236343
  return 1;
235356
236344
  }
235357
236345
  const allAgents = parsed.values["all-agents"] ?? false;
235358
- const agentId = getAgentId6(parsed.values.agent, parsed.values["agent-id"]);
236346
+ const agentId = getAgentId5(parsed.values.agent, parsed.values["agent-id"]);
235359
236347
  if (!allAgents && !agentId) {
235360
236348
  console.error("Missing agent id. Set LETTA_AGENT_ID or pass --agent/--agent-id.");
235361
236349
  return 1;
235362
236350
  }
235363
- const result = await client.messages.search({
236351
+ const result = await searchMessagesForBackend({
235364
236352
  query,
235365
236353
  agent_id: allAgents ? undefined : agentId,
235366
236354
  search_mode: parseMode(parsed.values.mode) ?? "hybrid",
235367
236355
  start_date: parsed.values["start-date"],
235368
236356
  end_date: parsed.values["end-date"],
235369
- limit: parseLimit3(parsed.values.limit, 10)
236357
+ limit: parseLimit2(parsed.values.limit, 10)
235370
236358
  });
235371
236359
  console.log(JSON.stringify(result, null, 2));
235372
236360
  return 0;
235373
236361
  }
235374
236362
  if (action === "list") {
235375
- const agentId = getAgentId6(parsed.values.agent, parsed.values["agent-id"]);
236363
+ const agentId = getAgentId5(parsed.values.agent, parsed.values["agent-id"]);
235376
236364
  if (!agentId) {
235377
236365
  console.error("Missing agent id. Set LETTA_AGENT_ID or pass --agent/--agent-id.");
235378
236366
  return 1;
@@ -235383,14 +236371,14 @@ async function runMessagesSubcommand(argv) {
235383
236371
  console.error(`Invalid --order "${orderRaw}". Use "asc" or "desc".`);
235384
236372
  return 1;
235385
236373
  }
235386
- const response = await client.agents.messages.list(agentId, {
236374
+ const response = await backend4.listAgentMessages(agentId, {
235387
236375
  conversation_id: "default",
235388
- limit: parseLimit3(parsed.values.limit, 20),
236376
+ limit: parseLimit2(parsed.values.limit, 20),
235389
236377
  after: parsed.values.after,
235390
236378
  before: parsed.values.before,
235391
236379
  order
235392
236380
  });
235393
- const messages = response.getPaginatedItems() ?? [];
236381
+ const messages = pageItems2(response);
235394
236382
  const startDate = parsed.values["start-date"];
235395
236383
  const endDate = parsed.values["end-date"];
235396
236384
  let filtered = messages;
@@ -235418,13 +236406,13 @@ async function runMessagesSubcommand(argv) {
235418
236406
  console.error("Missing conversation id. Pass --conversation <id> or --conversation-id <id>.");
235419
236407
  return 1;
235420
236408
  }
235421
- const agentId = getAgentId6(parsed.values.agent, parsed.values["agent-id"]);
236409
+ const agentId = getAgentId5(parsed.values.agent, parsed.values["agent-id"]);
235422
236410
  if (conversationId === "default" && !agentId) {
235423
236411
  console.error('Conversation "default" requires an agent id. Set LETTA_AGENT_ID or pass --agent/--agent-id.');
235424
236412
  return 1;
235425
236413
  }
235426
- const pageLimit = Math.max(1, parseLimit3(parsed.values.limit, 100));
235427
- const maxPages = Math.max(1, parseLimit3(parsed.values["max-pages"], 200));
236414
+ const pageLimit = Math.max(1, parseLimit2(parsed.values.limit, 100));
236415
+ const maxPages = Math.max(1, parseLimit2(parsed.values["max-pages"], 200));
235428
236416
  const outputPathRaw = parsed.values.out || parsed.values.output;
235429
236417
  const messages = await fetchConversationMessages(conversationId, agentId || undefined, pageLimit, maxPages);
235430
236418
  const transcript = messages.flatMap((msg) => formatEntry(msg)).join(`
@@ -235455,7 +236443,7 @@ async function runMessagesSubcommand(argv) {
235455
236443
  return 1;
235456
236444
  }
235457
236445
  console.error(`Unknown action: ${action}`);
235458
- printUsage6();
236446
+ printUsage5();
235459
236447
  return 1;
235460
236448
  }
235461
236449
 
@@ -235473,8 +236461,6 @@ async function runSubcommand(argv) {
235473
236461
  return runAgentsSubcommand(rest);
235474
236462
  case "messages":
235475
236463
  return runMessagesSubcommand(rest);
235476
- case "blocks":
235477
- return runBlocksSubcommand(rest);
235478
236464
  case "server":
235479
236465
  case "remote":
235480
236466
  return runListenSubcommand(rest);
@@ -237695,7 +238681,6 @@ USAGE
237695
238681
  letta memory ... Memory filesystem subcommands
237696
238682
  letta agents ... Agents subcommands (JSON-only)
237697
238683
  letta messages ... Messages subcommands (JSON-only)
237698
- letta blocks ... Blocks subcommands (JSON-only)
237699
238684
  letta connect ... Connect providers from terminal
237700
238685
 
237701
238686
  OPTIONS
@@ -237715,9 +238700,6 @@ SUBCOMMANDS
237715
238700
  letta messages search --query <text> [--all-agents]
237716
238701
  letta messages list [--agent <id>]
237717
238702
  letta messages transcript --conversation <id> [--out <path>]
237718
- letta blocks list --agent <id>
237719
- letta blocks copy --block-id <id> [--label <label>] [--agent <id>] [--override]
237720
- letta blocks attach --block-id <id> [--agent <id>] [--read-only] [--override]
237721
238703
  letta connect <provider> [options]
237722
238704
 
237723
238705
  BEHAVIOR
@@ -238006,8 +238988,13 @@ Note: Flags should use double dashes for full names (e.g., --yolo, not -yolo)`);
238006
238988
  const skillsDirectory = values.skills ?? undefined;
238007
238989
  const memfsFlag = values.memfs;
238008
238990
  const noMemfsFlag = values["no-memfs"];
238009
- const requestedMemoryPromptMode = memfsFlag ? "memfs" : noMemfsFlag ? "standard" : undefined;
238010
- const shouldAutoEnableMemfsForNewAgent = !memfsFlag && !noMemfsFlag;
238991
+ const startupBackend = getBackend();
238992
+ const localNoMemfsRequested = Boolean(startupBackend.capabilities.localMemfs && (noMemfsFlag || isLocalBackendNoMemfsEnvEnabled2()));
238993
+ if (localNoMemfsRequested) {
238994
+ process.env[LOCAL_BACKEND_NO_MEMFS_ENV2] = "1";
238995
+ }
238996
+ const requestedMemoryPromptMode = memfsFlag ? "memfs" : noMemfsFlag || localNoMemfsRequested ? "standard" : undefined;
238997
+ const shouldAutoEnableMemfsForNewAgent = !memfsFlag && !noMemfsFlag && !localNoMemfsRequested;
238011
238998
  const noSkillsFlag = values["no-skills"];
238012
238999
  const noBundledSkillsFlag = values["no-bundled-skills"];
238013
239000
  const skillSourcesRaw = values["skill-sources"];
@@ -238758,7 +239745,7 @@ Error: ${message}`);
238758
239745
  }
238759
239746
  const { isLettaCloud: isLettaCloud3 } = await Promise.resolve().then(() => (init_memoryFilesystem2(), exports_memoryFilesystem2));
238760
239747
  const willAutoEnableMemfs = shouldAutoEnableMemfsForNewAgent && await isLettaCloud3();
238761
- const effectiveMemoryMode = backend4.capabilities.localMemfs ? "local-memfs" : requestedMemoryPromptMode ?? (willAutoEnableMemfs ? "memfs" : undefined);
239748
+ const effectiveMemoryMode = backend4.capabilities.localMemfs ? localNoMemfsRequested ? "standard" : "local-memfs" : requestedMemoryPromptMode ?? (willAutoEnableMemfs ? "memfs" : undefined);
238762
239749
  const personalityOptions = personality ? await buildCreateAgentOptionsForPersonality({
238763
239750
  personalityId: personality,
238764
239751
  model: effectiveModel
@@ -238820,16 +239807,15 @@ Error: ${message}`);
238820
239807
  skipPromptUpdate: shouldCreateNew
238821
239808
  })) : Promise.resolve().then(() => {
238822
239809
  if (backend4.capabilities.localMemfs) {
238823
- if (noMemfsFlag) {
238824
- throw new Error("Disabling MemFS is not supported by the local backend.");
238825
- }
238826
- settingsManager2.setMemfsEnabled(agentId2, true);
238827
- return { action: "enabled" };
239810
+ settingsManager2.setMemfsEnabled(agentId2, !localNoMemfsRequested);
239811
+ return {
239812
+ action: localNoMemfsRequested ? "disabled" : "enabled"
239813
+ };
238828
239814
  }
238829
239815
  if (memfsFlag) {
238830
239816
  throw new Error("MemFS is not supported by the active backend.");
238831
239817
  }
238832
- if (noMemfsFlag) {
239818
+ if (noMemfsFlag || localNoMemfsRequested) {
238833
239819
  settingsManager2.setMemfsEnabled(agentId2, false);
238834
239820
  }
238835
239821
  return null;
@@ -239121,4 +240107,4 @@ Error during initialization: ${message}`);
239121
240107
  }
239122
240108
  main();
239123
240109
 
239124
- //# debugId=6C6C45F6A1ED024464756E2164756E21
240110
+ //# debugId=3EDFF39471E7DC8264756E2164756E21