@letta-ai/letta-code 0.15.2 → 0.15.4

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
@@ -3122,7 +3122,7 @@ var package_default;
3122
3122
  var init_package = __esm(() => {
3123
3123
  package_default = {
3124
3124
  name: "@letta-ai/letta-code",
3125
- version: "0.15.2",
3125
+ version: "0.15.4",
3126
3126
  description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
3127
3127
  type: "module",
3128
3128
  bin: {
@@ -3184,6 +3184,8 @@ var init_package = __esm(() => {
3184
3184
  check: "bun run scripts/check.js",
3185
3185
  dev: "bun --loader:.md=text --loader:.mdx=text --loader:.txt=text run src/index.ts",
3186
3186
  build: "node scripts/postinstall-patches.js && bun run build.js",
3187
+ "test:update-chain:manual": "bun run src/tests/update-chain-smoke.ts --mode manual",
3188
+ "test:update-chain:startup": "bun run src/tests/update-chain-smoke.ts --mode startup",
3187
3189
  prepublishOnly: "bun run build",
3188
3190
  postinstall: "node scripts/postinstall-patches.js"
3189
3191
  },
@@ -4437,6 +4439,57 @@ var init_timing = __esm(() => {
4437
4439
  milestones = new Map;
4438
4440
  });
4439
4441
 
4442
+ // src/agent/skillSources.ts
4443
+ function isSkillSource(value) {
4444
+ return ALL_SKILL_SOURCES.includes(value);
4445
+ }
4446
+ function normalizeSkillSources(sources) {
4447
+ const sourceSet = new Set(sources);
4448
+ return ALL_SKILL_SOURCES.filter((source) => sourceSet.has(source));
4449
+ }
4450
+ function parseSkillSourcesList(skillSourcesRaw) {
4451
+ const tokens = skillSourcesRaw.split(",").map((source) => source.trim()).filter((source) => source.length > 0);
4452
+ if (tokens.length === 0) {
4453
+ throw new Error("--skill-sources must include at least one source (e.g. bundled,project)");
4454
+ }
4455
+ const sources = [];
4456
+ for (const token of tokens) {
4457
+ const source = token;
4458
+ if (!VALID_SKILL_SOURCE_SPECIFIERS.includes(source)) {
4459
+ throw new Error(`Invalid skill source "${token}". Valid values: ${VALID_SKILL_SOURCE_SPECIFIERS.join(", ")}`);
4460
+ }
4461
+ if (source === "all") {
4462
+ sources.push(...ALL_SKILL_SOURCES);
4463
+ continue;
4464
+ }
4465
+ if (isSkillSource(source)) {
4466
+ sources.push(source);
4467
+ }
4468
+ }
4469
+ return normalizeSkillSources(sources);
4470
+ }
4471
+ function resolveSkillSourcesSelection(input) {
4472
+ if (input.noSkills) {
4473
+ return [];
4474
+ }
4475
+ const configuredSources = input.skillSourcesRaw ? parseSkillSourcesList(input.skillSourcesRaw) : [...ALL_SKILL_SOURCES];
4476
+ const filteredSources = input.noBundledSkills ? configuredSources.filter((source) => source !== "bundled") : configuredSources;
4477
+ return normalizeSkillSources(filteredSources);
4478
+ }
4479
+ var ALL_SKILL_SOURCES, VALID_SKILL_SOURCE_SPECIFIERS;
4480
+ var init_skillSources = __esm(() => {
4481
+ ALL_SKILL_SOURCES = [
4482
+ "bundled",
4483
+ "global",
4484
+ "agent",
4485
+ "project"
4486
+ ];
4487
+ VALID_SKILL_SOURCE_SPECIFIERS = [
4488
+ "all",
4489
+ ...ALL_SKILL_SOURCES
4490
+ ];
4491
+ });
4492
+
4440
4493
  // src/agent/memoryConstants.ts
4441
4494
  var READ_ONLY_BLOCK_LABELS;
4442
4495
  var init_memoryConstants = __esm(() => {
@@ -5635,13 +5688,15 @@ If the user asks for help or wants to give feedback inform them of the following
5635
5688
 
5636
5689
  When the user directly asks about Letta Code (eg 'can Letta Code do...', 'does Letta Code have...') or asks in second person (eg 'are you able...', 'can you do...'), first use the WebFetch tool to gather information to answer the question from the Letta Code repository at https://github.com/letta-ai/letta-code.
5637
5690
 
5691
+ When running in Letta Code, shell tools provide \`AGENT_ID\`: your current agent ID.
5692
+
5638
5693
  # Skills
5639
5694
  - /<skill-name> (e.g., /commit) is shorthand for users to invoke a skill. When executed, the skill gets expanded to a full prompt. Use the Skill tool to execute them. IMPORTANT: Only use Skill for skills listed in system-reminder messages in the conversation - do not guess or use built-in CLI commands.
5640
5695
  `;
5641
5696
  var init_system_prompt = () => {};
5642
5697
 
5643
5698
  // src/agent/prompts/system_prompt_memfs.txt
5644
- var system_prompt_memfs_default = "\n# Memory\n\nYou have an advanced memory system that enables you to remember past interactions and continuously improve your own capabilities.\n\n## Memory Filesystem\nYour memory is stored in a git repository at `~/.letta/agents/<agent-id>/memory/`. This provides full version control, sync with the server, and create worktrees for parallel edits.\nEach file contains metadata frontmatter include a `description` (the description of the file's contents), `limit` (the character limit), and optional `metadata`.\nThe filesystem tree is always available in your system prompt, along with the contents of files in the `system/` folder.\nYou also have additional external memory (e.g. your message history) that is accessible and that you can bring into context with tools when needed.\n\n## How It Works\n1. Each `.md` file in `memory/system/` is pinned to your system prompt with tags <system/context/{name}.md></system/context/{name}.md>\n2. The `memory_filesystem` block renders the current tree view of all available memory files\n2. Changes pushed to git sync to the API server within seconds\n3. API server changes sync to git automatically\n4. The system prompt is only recompiled with the latest memory on the API on compactions or message resets: your local copy may diverge\n\n## Syncing\n```bash\ncd ~/.letta/agents/<agent-id>/memory\n\n# See what changed\ngit status\n\n# Commit and push your changes\ngit add system/\ngit commit -m \"<type>: <what changed>\" # e.g. \"fix: update user prefs\", \"refactor: reorganize persona blocks\"\ngit push\n\n# Get latest from server\ngit pull\n```\nThe system will remind you when your memory has uncommitted changes. Sync when convenient.\n\n## History\n```bash\ngit -C ~/.letta/agents/<agent-id>/memory log --oneline\n```\n";
5699
+ var system_prompt_memfs_default = "\n# Memory\n\nYou have an advanced memory system that enables you to remember past interactions and continuously improve your own capabilities.\n\n## Memory Filesystem\nYour memory is stored in a git repository at `$MEMORY_DIR` (absolute path provided by Letta Code shell tools; usually `~/.letta/agents/$AGENT_ID/memory/`). This provides full version control, sync with the server, and create worktrees for parallel edits.\nEach file contains metadata frontmatter include a `description` (the description of the file's contents), `limit` (the character limit), and optional `metadata`.\nThe filesystem tree is always available in your system prompt, along with the contents of files in the `system/` folder.\nYou also have additional external memory (e.g. your message history) that is accessible and that you can bring into context with tools when needed.\n\n## How It Works\n1. Each `.md` file in `memory/system/` is pinned to your system prompt with tags <system/context/{name}.md></system/context/{name}.md>\n2. The `memory_filesystem` block renders the current tree view of all available memory files\n2. Changes pushed to git sync to the API server within seconds\n3. API server changes sync to git automatically\n4. The system prompt is only recompiled with the latest memory on the API on compactions or message resets: your local copy may diverge\n\n## Syncing\n```bash\ncd \"$MEMORY_DIR\"\n\n# See what changed\ngit status\n\n# Commit and push your changes\ngit add system/\ngit commit -m \"<type>: <what changed>\" # e.g. \"fix: update user prefs\", \"refactor: reorganize persona blocks\"\ngit push\n\n# Get latest from server\ngit pull\n```\nThe system will remind you when your memory has uncommitted changes. Sync when convenient.\n\n## History\n```bash\ngit -C \"$MEMORY_DIR\" log --oneline\n```\n";
5645
5700
  var init_system_prompt_memfs = () => {};
5646
5701
 
5647
5702
  // src/agent/prompts/system_prompt_memory.txt
@@ -35031,6 +35086,7 @@ __export(exports_context, {
35031
35086
  setConversationId: () => setConversationId2,
35032
35087
  setAgentContext: () => setAgentContext2,
35033
35088
  getSkillsDirectory: () => getSkillsDirectory,
35089
+ getSkillSources: () => getSkillSources,
35034
35090
  getNoSkills: () => getNoSkills,
35035
35091
  getCurrentAgentId: () => getCurrentAgentId,
35036
35092
  getConversationId: () => getConversationId
@@ -35041,16 +35097,16 @@ function getContext2() {
35041
35097
  global2[CONTEXT_KEY2] = {
35042
35098
  agentId: null,
35043
35099
  skillsDirectory: null,
35044
- noSkills: false,
35100
+ skillSources: [...ALL_SKILL_SOURCES],
35045
35101
  conversationId: null
35046
35102
  };
35047
35103
  }
35048
35104
  return global2[CONTEXT_KEY2];
35049
35105
  }
35050
- function setAgentContext2(agentId, skillsDirectory, noSkills) {
35106
+ function setAgentContext2(agentId, skillsDirectory, skillSources) {
35051
35107
  context2.agentId = agentId;
35052
35108
  context2.skillsDirectory = skillsDirectory || null;
35053
- context2.noSkills = noSkills ?? false;
35109
+ context2.skillSources = skillSources !== undefined ? [...skillSources] : [...ALL_SKILL_SOURCES];
35054
35110
  }
35055
35111
  function setCurrentAgentId(agentId) {
35056
35112
  context2.agentId = agentId;
@@ -35064,8 +35120,11 @@ function getCurrentAgentId() {
35064
35120
  function getSkillsDirectory() {
35065
35121
  return context2.skillsDirectory;
35066
35122
  }
35123
+ function getSkillSources() {
35124
+ return [...context2.skillSources];
35125
+ }
35067
35126
  function getNoSkills() {
35068
- return context2.noSkills;
35127
+ return !context2.skillSources.includes("bundled");
35069
35128
  }
35070
35129
  function setConversationId2(conversationId) {
35071
35130
  context2.conversationId = conversationId;
@@ -35075,6 +35134,7 @@ function getConversationId() {
35075
35134
  }
35076
35135
  var CONTEXT_KEY2, context2;
35077
35136
  var init_context = __esm(() => {
35137
+ init_skillSources();
35078
35138
  CONTEXT_KEY2 = Symbol.for("@letta/agentContext");
35079
35139
  context2 = getContext2();
35080
35140
  });
@@ -37549,6 +37609,926 @@ var init_process_manager = __esm(() => {
37549
37609
  backgroundTasks = new Map;
37550
37610
  });
37551
37611
 
37612
+ // src/providers/openai-codex-provider.ts
37613
+ var exports_openai_codex_provider = {};
37614
+ __export(exports_openai_codex_provider, {
37615
+ updateOpenAICodexProvider: () => updateOpenAICodexProvider,
37616
+ removeOpenAICodexProvider: () => removeOpenAICodexProvider,
37617
+ listProviders: () => listProviders,
37618
+ getOpenAICodexProvider: () => getOpenAICodexProvider,
37619
+ deleteOpenAICodexProvider: () => deleteOpenAICodexProvider,
37620
+ createOrUpdateOpenAICodexProvider: () => createOrUpdateOpenAICodexProvider,
37621
+ createOpenAICodexProvider: () => createOpenAICodexProvider,
37622
+ checkOpenAICodexEligibility: () => checkOpenAICodexEligibility,
37623
+ OPENAI_CODEX_PROVIDER_NAME: () => OPENAI_CODEX_PROVIDER_NAME,
37624
+ CHATGPT_OAUTH_PROVIDER_TYPE: () => CHATGPT_OAUTH_PROVIDER_TYPE
37625
+ });
37626
+ async function getLettaConfig() {
37627
+ const settings = await settingsManager.getSettingsWithSecureTokens();
37628
+ const baseUrl = process.env.LETTA_BASE_URL || settings.env?.LETTA_BASE_URL || LETTA_CLOUD_API_URL;
37629
+ const apiKey = process.env.LETTA_API_KEY || settings.env?.LETTA_API_KEY || "";
37630
+ return { baseUrl, apiKey };
37631
+ }
37632
+ async function providersRequest(method, path3, body) {
37633
+ const { baseUrl, apiKey } = await getLettaConfig();
37634
+ const url = `${baseUrl}${path3}`;
37635
+ const response = await fetch(url, {
37636
+ method,
37637
+ headers: getLettaCodeHeaders2(apiKey),
37638
+ ...body && { body: JSON.stringify(body) }
37639
+ });
37640
+ if (!response.ok) {
37641
+ const errorText = await response.text();
37642
+ if (response.status === 403) {
37643
+ try {
37644
+ const errorData = JSON.parse(errorText);
37645
+ if (errorData.error && typeof errorData.error === "string" && errorData.error.includes("only available for pro or enterprise")) {
37646
+ throw new Error("PLAN_UPGRADE_REQUIRED");
37647
+ }
37648
+ } catch (parseError) {
37649
+ if (parseError instanceof Error && parseError.message === "PLAN_UPGRADE_REQUIRED") {
37650
+ throw parseError;
37651
+ }
37652
+ }
37653
+ }
37654
+ throw new Error(`Provider API error (${response.status}): ${errorText}`);
37655
+ }
37656
+ const text = await response.text();
37657
+ if (!text) {
37658
+ return {};
37659
+ }
37660
+ return JSON.parse(text);
37661
+ }
37662
+ async function listProviders() {
37663
+ try {
37664
+ const response = await providersRequest("GET", "/v1/providers");
37665
+ return response;
37666
+ } catch {
37667
+ return [];
37668
+ }
37669
+ }
37670
+ async function getOpenAICodexProvider() {
37671
+ const providers = await listProviders();
37672
+ return providers.find((p) => p.name === OPENAI_CODEX_PROVIDER_NAME) || null;
37673
+ }
37674
+ async function createOpenAICodexProvider(config) {
37675
+ const apiKeyJson = JSON.stringify({
37676
+ access_token: config.access_token,
37677
+ id_token: config.id_token,
37678
+ refresh_token: config.refresh_token,
37679
+ account_id: config.account_id,
37680
+ expires_at: config.expires_at
37681
+ });
37682
+ return providersRequest("POST", "/v1/providers", {
37683
+ name: OPENAI_CODEX_PROVIDER_NAME,
37684
+ provider_type: CHATGPT_OAUTH_PROVIDER_TYPE,
37685
+ api_key: apiKeyJson
37686
+ });
37687
+ }
37688
+ async function updateOpenAICodexProvider(providerId, config) {
37689
+ const apiKeyJson = JSON.stringify({
37690
+ access_token: config.access_token,
37691
+ id_token: config.id_token,
37692
+ refresh_token: config.refresh_token,
37693
+ account_id: config.account_id,
37694
+ expires_at: config.expires_at
37695
+ });
37696
+ return providersRequest("PATCH", `/v1/providers/${providerId}`, {
37697
+ api_key: apiKeyJson
37698
+ });
37699
+ }
37700
+ async function deleteOpenAICodexProvider(providerId) {
37701
+ await providersRequest("DELETE", `/v1/providers/${providerId}`);
37702
+ }
37703
+ async function createOrUpdateOpenAICodexProvider(config) {
37704
+ const existing = await getOpenAICodexProvider();
37705
+ if (existing) {
37706
+ return updateOpenAICodexProvider(existing.id, config);
37707
+ } else {
37708
+ return createOpenAICodexProvider(config);
37709
+ }
37710
+ }
37711
+ async function removeOpenAICodexProvider() {
37712
+ const existing = await getOpenAICodexProvider();
37713
+ if (existing) {
37714
+ await deleteOpenAICodexProvider(existing.id);
37715
+ }
37716
+ }
37717
+ async function checkOpenAICodexEligibility() {
37718
+ try {
37719
+ const balance = await providersRequest("GET", "/v1/metadata/balance");
37720
+ const billingTier = balance.billing_tier.toLowerCase();
37721
+ if (billingTier === "pro" || billingTier === "enterprise") {
37722
+ return {
37723
+ eligible: true,
37724
+ billing_tier: balance.billing_tier
37725
+ };
37726
+ }
37727
+ return {
37728
+ eligible: false,
37729
+ billing_tier: balance.billing_tier,
37730
+ reason: `ChatGPT OAuth requires a Pro or Enterprise plan. Current plan: ${balance.billing_tier}`
37731
+ };
37732
+ } catch (error) {
37733
+ console.warn("Failed to check ChatGPT OAuth eligibility:", error);
37734
+ return {
37735
+ eligible: true,
37736
+ billing_tier: "unknown"
37737
+ };
37738
+ }
37739
+ }
37740
+ var OPENAI_CODEX_PROVIDER_NAME = "chatgpt-plus-pro", CHATGPT_OAUTH_PROVIDER_TYPE = "chatgpt_oauth";
37741
+ var init_openai_codex_provider = __esm(async () => {
37742
+ init_http_headers();
37743
+ init_oauth();
37744
+ await init_settings_manager();
37745
+ });
37746
+
37747
+ // src/agent/available-models.ts
37748
+ var exports_available_models = {};
37749
+ __export(exports_available_models, {
37750
+ prefetchAvailableModelHandles: () => prefetchAvailableModelHandles,
37751
+ getModelContextWindow: () => getModelContextWindow,
37752
+ getAvailableModelsCacheInfo: () => getAvailableModelsCacheInfo,
37753
+ getAvailableModelHandles: () => getAvailableModelHandles,
37754
+ clearAvailableModelsCache: () => clearAvailableModelsCache
37755
+ });
37756
+ function isFresh(now = Date.now()) {
37757
+ return cache4 !== null && now - cache4.fetchedAt < CACHE_TTL_MS;
37758
+ }
37759
+ function clearAvailableModelsCache() {
37760
+ cache4 = null;
37761
+ }
37762
+ function getAvailableModelsCacheInfo() {
37763
+ const now = Date.now();
37764
+ return {
37765
+ hasCache: cache4 !== null,
37766
+ isFresh: isFresh(now),
37767
+ fetchedAt: cache4?.fetchedAt ?? null,
37768
+ ageMs: cache4 ? now - cache4.fetchedAt : null,
37769
+ ttlMs: CACHE_TTL_MS
37770
+ };
37771
+ }
37772
+ async function refreshByokProviders() {
37773
+ const client = await getClient2();
37774
+ try {
37775
+ const providers = await client.get("/v1/providers/");
37776
+ const byokProviders = providers.filter((p) => p.provider_category === "byok");
37777
+ await Promise.allSettled(byokProviders.map(async (provider) => {
37778
+ try {
37779
+ await client.patch(`/v1/providers/${provider.id}/refresh`);
37780
+ } catch (error) {
37781
+ debugWarn("available-models", `Failed to refresh provider ${provider.name} (${provider.id}):`, error);
37782
+ }
37783
+ }));
37784
+ } catch (error) {
37785
+ debugWarn("available-models", "Failed to list providers for refresh:", error);
37786
+ }
37787
+ }
37788
+ async function fetchFromNetwork() {
37789
+ const client = await getClient2();
37790
+ const modelsList = await client.models.list();
37791
+ const handles = new Set(modelsList.map((m) => m.handle).filter((h) => !!h));
37792
+ const contextWindows = new Map;
37793
+ for (const model of modelsList) {
37794
+ if (model.handle && model.max_context_window) {
37795
+ contextWindows.set(model.handle, model.max_context_window);
37796
+ }
37797
+ }
37798
+ return { handles, contextWindows, fetchedAt: Date.now() };
37799
+ }
37800
+ async function getAvailableModelHandles(options) {
37801
+ const forceRefresh = options?.forceRefresh === true;
37802
+ const now = Date.now();
37803
+ if (!forceRefresh && isFresh(now) && cache4) {
37804
+ return {
37805
+ handles: cache4.handles,
37806
+ source: "cache",
37807
+ fetchedAt: cache4.fetchedAt
37808
+ };
37809
+ }
37810
+ if (!forceRefresh && inflight) {
37811
+ const entry2 = await inflight;
37812
+ return {
37813
+ handles: entry2.handles,
37814
+ source: "network",
37815
+ fetchedAt: entry2.fetchedAt
37816
+ };
37817
+ }
37818
+ if (forceRefresh) {
37819
+ await refreshByokProviders();
37820
+ }
37821
+ inflight = fetchFromNetwork().then((entry2) => {
37822
+ cache4 = entry2;
37823
+ return entry2;
37824
+ }).finally(() => {
37825
+ inflight = null;
37826
+ });
37827
+ const entry = await inflight;
37828
+ return {
37829
+ handles: entry.handles,
37830
+ source: "network",
37831
+ fetchedAt: entry.fetchedAt
37832
+ };
37833
+ }
37834
+ function prefetchAvailableModelHandles() {
37835
+ getAvailableModelHandles().catch(() => {});
37836
+ }
37837
+ async function getModelContextWindow(handle) {
37838
+ if (!cache4) {
37839
+ await getAvailableModelHandles();
37840
+ }
37841
+ return cache4?.contextWindows.get(handle);
37842
+ }
37843
+ var CACHE_TTL_MS, cache4 = null, inflight = null;
37844
+ var init_available_models = __esm(async () => {
37845
+ init_debug();
37846
+ await init_client2();
37847
+ CACHE_TTL_MS = 5 * 60 * 1000;
37848
+ });
37849
+
37850
+ // src/agent/memoryPrompt.ts
37851
+ var exports_memoryPrompt = {};
37852
+ __export(exports_memoryPrompt, {
37853
+ stripManagedMemorySections: () => stripManagedMemorySections,
37854
+ reconcileMemoryPrompt: () => reconcileMemoryPrompt,
37855
+ detectMemoryPromptDrift: () => detectMemoryPromptDrift
37856
+ });
37857
+ function normalizeNewlines(text) {
37858
+ return text.replace(/\r\n/g, `
37859
+ `);
37860
+ }
37861
+ function scanHeadingsOutsideFences(text) {
37862
+ const lines = text.split(`
37863
+ `);
37864
+ const headings = [];
37865
+ let inFence = false;
37866
+ let fenceToken = "";
37867
+ let offset = 0;
37868
+ for (const line of lines) {
37869
+ const trimmed = line.trimStart();
37870
+ const fenceMatch = trimmed.match(/^(```+|~~~+)/);
37871
+ if (fenceMatch) {
37872
+ const token = fenceMatch[1] ?? fenceMatch[0] ?? "";
37873
+ const tokenChar = token.startsWith("`") ? "`" : "~";
37874
+ if (!inFence) {
37875
+ inFence = true;
37876
+ fenceToken = tokenChar;
37877
+ } else if (fenceToken === tokenChar) {
37878
+ inFence = false;
37879
+ fenceToken = "";
37880
+ }
37881
+ }
37882
+ if (!inFence) {
37883
+ const headingMatch = line.match(/^\s*(#{1,6})\s+(.+?)\s*$/);
37884
+ if (headingMatch) {
37885
+ const hashes = headingMatch[1] ?? "";
37886
+ const rawTitle = headingMatch[2] ?? "";
37887
+ if (hashes && rawTitle) {
37888
+ const level = hashes.length;
37889
+ const title = rawTitle.replace(/\s+#*$/, "").trim();
37890
+ headings.push({
37891
+ level,
37892
+ title,
37893
+ startOffset: offset
37894
+ });
37895
+ }
37896
+ }
37897
+ }
37898
+ offset += line.length + 1;
37899
+ }
37900
+ return headings;
37901
+ }
37902
+ function stripHeadingSections(text, shouldStrip) {
37903
+ let current = text;
37904
+ while (true) {
37905
+ const headings = scanHeadingsOutsideFences(current);
37906
+ const target = headings.find(shouldStrip);
37907
+ if (!target) {
37908
+ return current;
37909
+ }
37910
+ const nextHeading = headings.find((heading) => heading.startOffset > target.startOffset && heading.level <= target.level);
37911
+ const end = nextHeading ? nextHeading.startOffset : current.length;
37912
+ current = `${current.slice(0, target.startOffset)}${current.slice(end)}`;
37913
+ }
37914
+ }
37915
+ function getMemfsTailFragment() {
37916
+ const tailAnchor = "# See what changed";
37917
+ const start = SYSTEM_PROMPT_MEMFS_ADDON.indexOf(tailAnchor);
37918
+ if (start === -1)
37919
+ return "";
37920
+ return SYSTEM_PROMPT_MEMFS_ADDON.slice(start).trim();
37921
+ }
37922
+ function stripExactAddon(text, addon) {
37923
+ const trimmedAddon = addon.trim();
37924
+ if (!trimmedAddon)
37925
+ return text;
37926
+ let current = text;
37927
+ while (current.includes(trimmedAddon)) {
37928
+ current = current.replace(trimmedAddon, "");
37929
+ }
37930
+ return current;
37931
+ }
37932
+ function stripOrphanMemfsTail(text) {
37933
+ const tail = getMemfsTailFragment();
37934
+ if (!tail)
37935
+ return text;
37936
+ let current = text;
37937
+ while (current.includes(tail)) {
37938
+ current = current.replace(tail, "");
37939
+ }
37940
+ return current;
37941
+ }
37942
+ function compactBlankLines(text) {
37943
+ return text.replace(/\n{3,}/g, `
37944
+
37945
+ `).trimEnd();
37946
+ }
37947
+ function stripManagedMemorySections(systemPrompt) {
37948
+ let current = normalizeNewlines(systemPrompt);
37949
+ current = stripExactAddon(current, SYSTEM_PROMPT_MEMORY_ADDON);
37950
+ current = stripExactAddon(current, SYSTEM_PROMPT_MEMFS_ADDON);
37951
+ current = stripOrphanMemfsTail(current);
37952
+ current = stripHeadingSections(current, (heading) => heading.title === "Memory");
37953
+ current = stripHeadingSections(current, (heading) => heading.title.startsWith("Memory Filesystem"));
37954
+ return compactBlankLines(current);
37955
+ }
37956
+ function reconcileMemoryPrompt(systemPrompt, mode) {
37957
+ const base2 = stripManagedMemorySections(systemPrompt).trimEnd();
37958
+ const addon = mode === "memfs" ? SYSTEM_PROMPT_MEMFS_ADDON.trimStart() : SYSTEM_PROMPT_MEMORY_ADDON.trimStart();
37959
+ return `${base2}
37960
+
37961
+ ${addon}`.trim();
37962
+ }
37963
+ function detectMemoryPromptDrift(systemPrompt, expectedMode) {
37964
+ const prompt = normalizeNewlines(systemPrompt);
37965
+ const drifts = [];
37966
+ const hasLegacyMemoryLanguage = prompt.includes("Your memory consists of core memory (composed of memory blocks)");
37967
+ const hasMemfsLanguage = prompt.includes("## Memory Filesystem") || prompt.includes("Your memory is stored in a git repository at");
37968
+ const hasOrphanFragment = prompt.includes("# See what changed") && prompt.includes("git add system/") && prompt.includes('git commit -m "<type>: <what changed>"');
37969
+ if (expectedMode === "memfs" && hasLegacyMemoryLanguage) {
37970
+ drifts.push({
37971
+ code: "legacy_memory_language_with_memfs",
37972
+ message: "System prompt contains legacy memory-block language while memfs is enabled."
37973
+ });
37974
+ }
37975
+ if (expectedMode === "standard" && hasMemfsLanguage) {
37976
+ drifts.push({
37977
+ code: "memfs_language_with_standard_mode",
37978
+ message: "System prompt contains Memory Filesystem language while memfs is disabled."
37979
+ });
37980
+ }
37981
+ if (hasOrphanFragment && !hasMemfsLanguage) {
37982
+ drifts.push({
37983
+ code: "orphan_memfs_fragment",
37984
+ message: "System prompt contains orphaned memfs sync fragment without a full memfs section."
37985
+ });
37986
+ }
37987
+ return drifts;
37988
+ }
37989
+ var init_memoryPrompt = __esm(() => {
37990
+ init_promptAssets();
37991
+ });
37992
+
37993
+ // src/agent/modify.ts
37994
+ var exports_modify = {};
37995
+ __export(exports_modify, {
37996
+ updateAgentSystemPromptRaw: () => updateAgentSystemPromptRaw,
37997
+ updateAgentSystemPromptMemfs: () => updateAgentSystemPromptMemfs,
37998
+ updateAgentSystemPrompt: () => updateAgentSystemPrompt,
37999
+ updateAgentLLMConfig: () => updateAgentLLMConfig
38000
+ });
38001
+ function buildModelSettings(modelHandle, updateArgs) {
38002
+ const isOpenAI = modelHandle.startsWith("openai/") || modelHandle.startsWith(`${OPENAI_CODEX_PROVIDER_NAME}/`);
38003
+ const isAnthropic = modelHandle.startsWith("anthropic/") || modelHandle.startsWith("claude-pro-max/");
38004
+ const isZai = modelHandle.startsWith("zai/");
38005
+ const isGoogleAI = modelHandle.startsWith("google_ai/");
38006
+ const isGoogleVertex = modelHandle.startsWith("google_vertex/");
38007
+ const isOpenRouter = modelHandle.startsWith("openrouter/");
38008
+ const isBedrock = modelHandle.startsWith("bedrock/");
38009
+ let settings;
38010
+ if (isOpenAI || isOpenRouter) {
38011
+ const openaiSettings = {
38012
+ provider_type: "openai",
38013
+ parallel_tool_calls: true
38014
+ };
38015
+ if (updateArgs?.reasoning_effort) {
38016
+ openaiSettings.reasoning = {
38017
+ reasoning_effort: updateArgs.reasoning_effort
38018
+ };
38019
+ }
38020
+ settings = openaiSettings;
38021
+ } else if (isAnthropic) {
38022
+ const anthropicSettings = {
38023
+ provider_type: "anthropic",
38024
+ parallel_tool_calls: true
38025
+ };
38026
+ if (updateArgs?.enable_reasoner !== undefined || typeof updateArgs?.max_reasoning_tokens === "number") {
38027
+ anthropicSettings.thinking = {
38028
+ type: updateArgs?.enable_reasoner === false ? "disabled" : "enabled",
38029
+ ...typeof updateArgs?.max_reasoning_tokens === "number" && {
38030
+ budget_tokens: updateArgs.max_reasoning_tokens
38031
+ }
38032
+ };
38033
+ }
38034
+ settings = anthropicSettings;
38035
+ } else if (isZai) {
38036
+ settings = {
38037
+ provider_type: "zai",
38038
+ parallel_tool_calls: true
38039
+ };
38040
+ } else if (isGoogleAI) {
38041
+ const googleSettings = {
38042
+ provider_type: "google_ai",
38043
+ parallel_tool_calls: true
38044
+ };
38045
+ if (updateArgs?.thinking_budget !== undefined) {
38046
+ googleSettings.thinking_config = {
38047
+ thinking_budget: updateArgs.thinking_budget
38048
+ };
38049
+ }
38050
+ if (typeof updateArgs?.temperature === "number") {
38051
+ googleSettings.temperature = updateArgs.temperature;
38052
+ }
38053
+ settings = googleSettings;
38054
+ } else if (isGoogleVertex) {
38055
+ const googleVertexSettings = {
38056
+ provider_type: "google_vertex",
38057
+ parallel_tool_calls: true
38058
+ };
38059
+ if (updateArgs?.thinking_budget !== undefined) {
38060
+ googleVertexSettings.thinking_config = {
38061
+ thinking_budget: updateArgs.thinking_budget
38062
+ };
38063
+ }
38064
+ if (typeof updateArgs?.temperature === "number") {
38065
+ googleVertexSettings.temperature = updateArgs.temperature;
38066
+ }
38067
+ settings = googleVertexSettings;
38068
+ } else if (isBedrock) {
38069
+ const bedrockSettings = {
38070
+ provider_type: "bedrock",
38071
+ parallel_tool_calls: true
38072
+ };
38073
+ if (updateArgs?.enable_reasoner !== undefined || typeof updateArgs?.max_reasoning_tokens === "number") {
38074
+ bedrockSettings.thinking = {
38075
+ type: updateArgs?.enable_reasoner === false ? "disabled" : "enabled",
38076
+ ...typeof updateArgs?.max_reasoning_tokens === "number" && {
38077
+ budget_tokens: updateArgs.max_reasoning_tokens
38078
+ }
38079
+ };
38080
+ }
38081
+ settings = bedrockSettings;
38082
+ } else {
38083
+ settings = {};
38084
+ }
38085
+ if (typeof updateArgs?.max_output_tokens === "number" && "provider_type" in settings) {
38086
+ settings.max_output_tokens = updateArgs.max_output_tokens;
38087
+ }
38088
+ return settings;
38089
+ }
38090
+ async function updateAgentLLMConfig(agentId, modelHandle, updateArgs) {
38091
+ const client = await getClient2();
38092
+ const modelSettings = buildModelSettings(modelHandle, updateArgs);
38093
+ const contextWindow = updateArgs?.context_window ?? await getModelContextWindow(modelHandle);
38094
+ const hasModelSettings = Object.keys(modelSettings).length > 0;
38095
+ await client.agents.update(agentId, {
38096
+ model: modelHandle,
38097
+ ...hasModelSettings && { model_settings: modelSettings },
38098
+ ...contextWindow && { context_window_limit: contextWindow },
38099
+ ...typeof updateArgs?.max_output_tokens === "number" && {
38100
+ max_tokens: updateArgs.max_output_tokens
38101
+ }
38102
+ });
38103
+ const finalAgent = await client.agents.retrieve(agentId);
38104
+ return finalAgent.llm_config;
38105
+ }
38106
+ async function updateAgentSystemPromptRaw(agentId, systemPromptContent) {
38107
+ try {
38108
+ const client = await getClient2();
38109
+ await client.agents.update(agentId, {
38110
+ system: systemPromptContent
38111
+ });
38112
+ return {
38113
+ success: true,
38114
+ message: "System prompt updated successfully"
38115
+ };
38116
+ } catch (error) {
38117
+ return {
38118
+ success: false,
38119
+ message: `Failed to update system prompt: ${error instanceof Error ? error.message : String(error)}`
38120
+ };
38121
+ }
38122
+ }
38123
+ async function updateAgentSystemPrompt(agentId, systemPromptId) {
38124
+ try {
38125
+ const { resolveSystemPrompt: resolveSystemPrompt2 } = await Promise.resolve().then(() => (init_promptAssets(), exports_promptAssets));
38126
+ const { detectMemoryPromptDrift: detectMemoryPromptDrift2, reconcileMemoryPrompt: reconcileMemoryPrompt2 } = await Promise.resolve().then(() => (init_memoryPrompt(), exports_memoryPrompt));
38127
+ const { settingsManager: settingsManager3 } = await init_settings_manager().then(() => exports_settings_manager);
38128
+ const client = await getClient2();
38129
+ const currentAgent = await client.agents.retrieve(agentId);
38130
+ const baseContent = await resolveSystemPrompt2(systemPromptId);
38131
+ const settingIndicatesMemfs = settingsManager3.isMemfsEnabled(agentId);
38132
+ const promptIndicatesMemfs = detectMemoryPromptDrift2(currentAgent.system || "", "standard").some((drift) => drift.code === "memfs_language_with_standard_mode");
38133
+ const memoryMode = settingIndicatesMemfs || promptIndicatesMemfs ? "memfs" : "standard";
38134
+ const systemPromptContent = reconcileMemoryPrompt2(baseContent, memoryMode);
38135
+ const updateResult = await updateAgentSystemPromptRaw(agentId, systemPromptContent);
38136
+ if (!updateResult.success) {
38137
+ return {
38138
+ success: false,
38139
+ message: updateResult.message,
38140
+ agent: null
38141
+ };
38142
+ }
38143
+ const agent = await client.agents.retrieve(agentId);
38144
+ return {
38145
+ success: true,
38146
+ message: "System prompt applied successfully",
38147
+ agent
38148
+ };
38149
+ } catch (error) {
38150
+ return {
38151
+ success: false,
38152
+ message: `Failed to apply system prompt: ${error instanceof Error ? error.message : String(error)}`,
38153
+ agent: null
38154
+ };
38155
+ }
38156
+ }
38157
+ async function updateAgentSystemPromptMemfs(agentId, enableMemfs) {
38158
+ try {
38159
+ const client = await getClient2();
38160
+ const agent = await client.agents.retrieve(agentId);
38161
+ const { reconcileMemoryPrompt: reconcileMemoryPrompt2 } = await Promise.resolve().then(() => (init_memoryPrompt(), exports_memoryPrompt));
38162
+ const nextSystemPrompt = reconcileMemoryPrompt2(agent.system || "", enableMemfs ? "memfs" : "standard");
38163
+ await client.agents.update(agentId, {
38164
+ system: nextSystemPrompt
38165
+ });
38166
+ return {
38167
+ success: true,
38168
+ message: enableMemfs ? "System prompt updated to include Memory Filesystem section" : "System prompt updated to include standard Memory section"
38169
+ };
38170
+ } catch (error) {
38171
+ return {
38172
+ success: false,
38173
+ message: `Failed to update system prompt memfs: ${error instanceof Error ? error.message : String(error)}`
38174
+ };
38175
+ }
38176
+ }
38177
+ var init_modify = __esm(async () => {
38178
+ await __promiseAll([
38179
+ init_openai_codex_provider(),
38180
+ init_available_models(),
38181
+ init_client2()
38182
+ ]);
38183
+ });
38184
+
38185
+ // src/tools/filter.ts
38186
+ var exports_filter = {};
38187
+ __export(exports_filter, {
38188
+ toolFilter: () => toolFilter
38189
+ });
38190
+
38191
+ class ToolFilterManager {
38192
+ enabledTools = null;
38193
+ setEnabledTools(toolsString) {
38194
+ if (toolsString === "") {
38195
+ this.enabledTools = [];
38196
+ } else {
38197
+ this.enabledTools = toolsString.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
38198
+ }
38199
+ }
38200
+ isEnabled(toolName) {
38201
+ if (this.enabledTools === null) {
38202
+ return true;
38203
+ }
38204
+ return this.enabledTools.includes(toolName);
38205
+ }
38206
+ getEnabledTools() {
38207
+ return this.enabledTools ? [...this.enabledTools] : null;
38208
+ }
38209
+ isActive() {
38210
+ return this.enabledTools !== null;
38211
+ }
38212
+ reset() {
38213
+ this.enabledTools = null;
38214
+ }
38215
+ }
38216
+ function getFilter() {
38217
+ const global2 = globalThis;
38218
+ if (!global2[FILTER_KEY]) {
38219
+ global2[FILTER_KEY] = new ToolFilterManager;
38220
+ }
38221
+ return global2[FILTER_KEY];
38222
+ }
38223
+ var FILTER_KEY, toolFilter;
38224
+ var init_filter = __esm(() => {
38225
+ FILTER_KEY = Symbol.for("@letta/toolFilter");
38226
+ toolFilter = getFilter();
38227
+ });
38228
+
38229
+ // src/tools/toolset.ts
38230
+ var exports_toolset = {};
38231
+ __export(exports_toolset, {
38232
+ switchToolsetForModel: () => switchToolsetForModel,
38233
+ reattachMemoryTool: () => reattachMemoryTool,
38234
+ forceToolsetSwitch: () => forceToolsetSwitch,
38235
+ ensureCorrectMemoryTool: () => ensureCorrectMemoryTool,
38236
+ detachMemoryTools: () => detachMemoryTools,
38237
+ MEMORY_TOOL_NAMES: () => MEMORY_TOOL_NAMES
38238
+ });
38239
+ async function ensureCorrectMemoryTool(agentId, modelIdentifier, useMemoryPatch) {
38240
+ const resolvedModel = resolveModel(modelIdentifier) ?? modelIdentifier;
38241
+ const client = await getClient2();
38242
+ const shouldUsePatch = useMemoryPatch !== undefined ? useMemoryPatch : isOpenAIModel(resolvedModel);
38243
+ try {
38244
+ const agentWithTools = await client.agents.retrieve(agentId, {
38245
+ include: ["agent.tools"]
38246
+ });
38247
+ const currentTools = agentWithTools.tools || [];
38248
+ const mapByName = new Map(currentTools.map((t) => [t.name, t.id]));
38249
+ const hasAnyMemoryTool = mapByName.has("memory") || mapByName.has("memory_apply_patch");
38250
+ if (!hasAnyMemoryTool) {
38251
+ return;
38252
+ }
38253
+ const desiredMemoryTool = shouldUsePatch ? "memory_apply_patch" : "memory";
38254
+ const otherMemoryTool = desiredMemoryTool === "memory" ? "memory_apply_patch" : "memory";
38255
+ let desiredId = mapByName.get(desiredMemoryTool);
38256
+ if (!desiredId) {
38257
+ const resp = await client.tools.list({ name: desiredMemoryTool });
38258
+ desiredId = resp.items[0]?.id;
38259
+ }
38260
+ if (!desiredId) {
38261
+ return;
38262
+ }
38263
+ const otherId = mapByName.get(otherMemoryTool);
38264
+ if (mapByName.has(desiredMemoryTool) && !otherId) {
38265
+ return;
38266
+ }
38267
+ const currentIds = currentTools.map((t) => t.id).filter((id) => typeof id === "string");
38268
+ const newIds = new Set(currentIds);
38269
+ if (otherId)
38270
+ newIds.delete(otherId);
38271
+ newIds.add(desiredId);
38272
+ const updatedRules = (agentWithTools.tool_rules || []).map((r) => r.tool_name === otherMemoryTool ? { ...r, tool_name: desiredMemoryTool } : r);
38273
+ await client.agents.update(agentId, {
38274
+ tool_ids: Array.from(newIds),
38275
+ tool_rules: updatedRules
38276
+ });
38277
+ } catch (err) {
38278
+ console.warn(`Warning: Failed to sync memory tool: ${err instanceof Error ? err.message : String(err)}`);
38279
+ }
38280
+ }
38281
+ async function detachMemoryTools(agentId) {
38282
+ const client = await getClient2();
38283
+ try {
38284
+ const agentWithTools = await client.agents.retrieve(agentId, {
38285
+ include: ["agent.tools"]
38286
+ });
38287
+ const currentTools = agentWithTools.tools || [];
38288
+ let detachedAny = false;
38289
+ for (const tool of currentTools) {
38290
+ if (tool.name && MEMORY_TOOL_NAMES.has(tool.name)) {
38291
+ if (tool.id) {
38292
+ await client.agents.tools.detach(tool.id, { agent_id: agentId });
38293
+ detachedAny = true;
38294
+ }
38295
+ }
38296
+ }
38297
+ return detachedAny;
38298
+ } catch (err) {
38299
+ console.warn(`Warning: Failed to detach memory tools: ${err instanceof Error ? err.message : String(err)}`);
38300
+ return false;
38301
+ }
38302
+ }
38303
+ async function reattachMemoryTool(agentId, modelIdentifier) {
38304
+ const resolvedModel = resolveModel(modelIdentifier) ?? modelIdentifier;
38305
+ const client = await getClient2();
38306
+ const shouldUsePatch = isOpenAIModel(resolvedModel);
38307
+ try {
38308
+ const agentWithTools = await client.agents.retrieve(agentId, {
38309
+ include: ["agent.tools"]
38310
+ });
38311
+ const currentTools = agentWithTools.tools || [];
38312
+ const mapByName = new Map(currentTools.map((t) => [t.name, t.id]));
38313
+ const desiredMemoryTool = shouldUsePatch ? "memory_apply_patch" : "memory";
38314
+ if (mapByName.has(desiredMemoryTool)) {
38315
+ return;
38316
+ }
38317
+ const resp = await client.tools.list({ name: desiredMemoryTool });
38318
+ const toolId = resp.items[0]?.id;
38319
+ if (!toolId) {
38320
+ console.warn(`Memory tool "${desiredMemoryTool}" not found on server`);
38321
+ return;
38322
+ }
38323
+ await client.agents.tools.attach(toolId, { agent_id: agentId });
38324
+ } catch (err) {
38325
+ console.warn(`Warning: Failed to reattach memory tool: ${err instanceof Error ? err.message : String(err)}`);
38326
+ }
38327
+ }
38328
+ async function forceToolsetSwitch(toolsetName, agentId) {
38329
+ let modelForLoading;
38330
+ if (toolsetName === "none") {
38331
+ clearToolsWithLock();
38332
+ return;
38333
+ } else if (toolsetName === "codex") {
38334
+ await loadSpecificTools([...CODEX_TOOLS]);
38335
+ modelForLoading = "openai/gpt-4";
38336
+ } else if (toolsetName === "codex_snake") {
38337
+ await loadTools("openai/gpt-4");
38338
+ modelForLoading = "openai/gpt-4";
38339
+ } else if (toolsetName === "gemini") {
38340
+ await loadSpecificTools([...GEMINI_TOOLS]);
38341
+ modelForLoading = "google_ai/gemini-3-pro-preview";
38342
+ } else if (toolsetName === "gemini_snake") {
38343
+ await loadTools("google_ai/gemini-3-pro-preview");
38344
+ modelForLoading = "google_ai/gemini-3-pro-preview";
38345
+ } else {
38346
+ await loadTools("anthropic/claude-sonnet-4");
38347
+ modelForLoading = "anthropic/claude-sonnet-4";
38348
+ }
38349
+ const useMemoryPatch = toolsetName === "codex" || toolsetName === "codex_snake";
38350
+ await ensureCorrectMemoryTool(agentId, modelForLoading, useMemoryPatch);
38351
+ }
38352
+ async function switchToolsetForModel(modelIdentifier, agentId) {
38353
+ const resolvedModel = resolveModel(modelIdentifier) ?? modelIdentifier;
38354
+ await loadTools(resolvedModel);
38355
+ const loadedAfterPrimary = getToolNames().length;
38356
+ if (loadedAfterPrimary === 0 && !toolFilter.isActive()) {
38357
+ await loadTools();
38358
+ if (getToolNames().length === 0) {
38359
+ throw new Error(`Failed to load any Letta tools for model "${resolvedModel}".`);
38360
+ }
38361
+ }
38362
+ await ensureCorrectMemoryTool(agentId, resolvedModel);
38363
+ const { isGeminiModel } = await init_manager3().then(() => exports_manager2);
38364
+ const toolsetName = isOpenAIModel(resolvedModel) ? "codex" : isGeminiModel(resolvedModel) ? "gemini" : "default";
38365
+ return toolsetName;
38366
+ }
38367
+ var CODEX_TOOLS, GEMINI_TOOLS, MEMORY_TOOL_NAMES;
38368
+ var init_toolset = __esm(async () => {
38369
+ init_model();
38370
+ init_filter();
38371
+ await __promiseAll([
38372
+ init_client2(),
38373
+ init_manager3()
38374
+ ]);
38375
+ CODEX_TOOLS = OPENAI_PASCAL_TOOLS;
38376
+ GEMINI_TOOLS = GEMINI_PASCAL_TOOLS;
38377
+ MEMORY_TOOL_NAMES = new Set([
38378
+ "memory",
38379
+ "memory_apply_patch",
38380
+ "memory_insert",
38381
+ "memory_replace",
38382
+ "memory_rethink"
38383
+ ]);
38384
+ });
38385
+
38386
+ // src/agent/memoryFilesystem.ts
38387
+ var exports_memoryFilesystem = {};
38388
+ __export(exports_memoryFilesystem, {
38389
+ renderMemoryFilesystemTree: () => renderMemoryFilesystemTree,
38390
+ labelFromRelativePath: () => labelFromRelativePath,
38391
+ getMemorySystemDir: () => getMemorySystemDir,
38392
+ getMemoryFilesystemRoot: () => getMemoryFilesystemRoot,
38393
+ ensureMemoryFilesystemDirs: () => ensureMemoryFilesystemDirs,
38394
+ enableMemfsIfCloud: () => enableMemfsIfCloud,
38395
+ applyMemfsFlags: () => applyMemfsFlags,
38396
+ MEMORY_SYSTEM_DIR: () => MEMORY_SYSTEM_DIR,
38397
+ MEMORY_FS_ROOT: () => MEMORY_FS_ROOT,
38398
+ MEMORY_FS_MEMORY_DIR: () => MEMORY_FS_MEMORY_DIR,
38399
+ MEMORY_FS_AGENTS_DIR: () => MEMORY_FS_AGENTS_DIR
38400
+ });
38401
+ import { existsSync as existsSync6, mkdirSync as mkdirSync4 } from "node:fs";
38402
+ import { homedir as homedir8 } from "node:os";
38403
+ import { join as join7 } from "node:path";
38404
+ function getMemoryFilesystemRoot(agentId, homeDir = homedir8()) {
38405
+ return join7(homeDir, MEMORY_FS_ROOT, MEMORY_FS_AGENTS_DIR, agentId, MEMORY_FS_MEMORY_DIR);
38406
+ }
38407
+ function getMemorySystemDir(agentId, homeDir = homedir8()) {
38408
+ return join7(getMemoryFilesystemRoot(agentId, homeDir), MEMORY_SYSTEM_DIR);
38409
+ }
38410
+ function ensureMemoryFilesystemDirs(agentId, homeDir = homedir8()) {
38411
+ const root = getMemoryFilesystemRoot(agentId, homeDir);
38412
+ const systemDir = getMemorySystemDir(agentId, homeDir);
38413
+ if (!existsSync6(root)) {
38414
+ mkdirSync4(root, { recursive: true });
38415
+ }
38416
+ if (!existsSync6(systemDir)) {
38417
+ mkdirSync4(systemDir, { recursive: true });
38418
+ }
38419
+ }
38420
+ function labelFromRelativePath(relativePath) {
38421
+ const normalized = relativePath.replace(/\\/g, "/");
38422
+ return normalized.replace(/\.md$/, "");
38423
+ }
38424
+ function renderMemoryFilesystemTree(systemLabels, detachedLabels) {
38425
+ const makeNode = () => ({ children: new Map, isFile: false });
38426
+ const root = makeNode();
38427
+ const insertPath = (base2, label) => {
38428
+ const parts = base2 ? [base2, ...label.split("/")] : label.split("/");
38429
+ let current = root;
38430
+ for (const [i, partName] of parts.entries()) {
38431
+ const part = i === parts.length - 1 ? `${partName}.md` : partName;
38432
+ if (!current.children.has(part)) {
38433
+ current.children.set(part, makeNode());
38434
+ }
38435
+ current = current.children.get(part);
38436
+ if (i === parts.length - 1) {
38437
+ current.isFile = true;
38438
+ }
38439
+ }
38440
+ };
38441
+ for (const label of systemLabels) {
38442
+ insertPath(MEMORY_SYSTEM_DIR, label);
38443
+ }
38444
+ for (const label of detachedLabels) {
38445
+ insertPath(null, label);
38446
+ }
38447
+ if (!root.children.has(MEMORY_SYSTEM_DIR)) {
38448
+ root.children.set(MEMORY_SYSTEM_DIR, makeNode());
38449
+ }
38450
+ const sortedEntries = (node) => {
38451
+ const entries = Array.from(node.children.entries());
38452
+ return entries.sort(([nameA, nodeA], [nameB, nodeB]) => {
38453
+ if (nodeA.isFile !== nodeB.isFile) {
38454
+ return nodeA.isFile ? 1 : -1;
38455
+ }
38456
+ return nameA.localeCompare(nameB);
38457
+ });
38458
+ };
38459
+ const lines = ["/memory/"];
38460
+ const render2 = (node, prefix) => {
38461
+ const entries = sortedEntries(node);
38462
+ entries.forEach(([name, child], index) => {
38463
+ const isLast = index === entries.length - 1;
38464
+ const branch = isLast ? "└──" : "├──";
38465
+ lines.push(`${prefix}${branch} ${name}${child.isFile ? "" : "/"}`);
38466
+ if (child.children.size > 0) {
38467
+ const nextPrefix = `${prefix}${isLast ? " " : "│ "}`;
38468
+ render2(child, nextPrefix);
38469
+ }
38470
+ });
38471
+ };
38472
+ render2(root, "");
38473
+ return lines.join(`
38474
+ `);
38475
+ }
38476
+ async function applyMemfsFlags(agentId, memfsFlag, noMemfsFlag, options) {
38477
+ const { getServerUrl: getServerUrl2 } = await init_client2().then(() => exports_client);
38478
+ const { settingsManager: settingsManager3 } = await init_settings_manager().then(() => exports_settings_manager);
38479
+ if (memfsFlag) {
38480
+ const serverUrl = getServerUrl2();
38481
+ if (!serverUrl.includes("api.letta.com")) {
38482
+ throw new Error("--memfs is only available on Letta Cloud (api.letta.com).");
38483
+ }
38484
+ }
38485
+ const hasExplicitToggle = Boolean(memfsFlag || noMemfsFlag);
38486
+ const targetEnabled = memfsFlag ? true : noMemfsFlag ? false : settingsManager3.isMemfsEnabled(agentId);
38487
+ if (hasExplicitToggle) {
38488
+ const { updateAgentSystemPromptMemfs: updateAgentSystemPromptMemfs2 } = await init_modify().then(() => exports_modify);
38489
+ const promptUpdate = await updateAgentSystemPromptMemfs2(agentId, targetEnabled);
38490
+ if (!promptUpdate.success) {
38491
+ throw new Error(promptUpdate.message);
38492
+ }
38493
+ settingsManager3.setMemfsEnabled(agentId, targetEnabled);
38494
+ }
38495
+ const isEnabled = hasExplicitToggle ? targetEnabled : settingsManager3.isMemfsEnabled(agentId);
38496
+ if (isEnabled && memfsFlag) {
38497
+ const { detachMemoryTools: detachMemoryTools2 } = await init_toolset().then(() => exports_toolset);
38498
+ await detachMemoryTools2(agentId);
38499
+ }
38500
+ let pullSummary;
38501
+ if (isEnabled) {
38502
+ const { addGitMemoryTag: addGitMemoryTag2, isGitRepo: isGitRepo2, cloneMemoryRepo: cloneMemoryRepo2, pullMemory: pullMemory2 } = await init_memoryGit().then(() => exports_memoryGit);
38503
+ await addGitMemoryTag2(agentId);
38504
+ if (!isGitRepo2(agentId)) {
38505
+ await cloneMemoryRepo2(agentId);
38506
+ } else if (options?.pullOnExistingRepo) {
38507
+ const result = await pullMemory2(agentId);
38508
+ pullSummary = result.summary;
38509
+ }
38510
+ }
38511
+ const action = memfsFlag ? "enabled" : noMemfsFlag ? "disabled" : "unchanged";
38512
+ return {
38513
+ action,
38514
+ memoryDir: isEnabled ? getMemoryFilesystemRoot(agentId) : undefined,
38515
+ pullSummary
38516
+ };
38517
+ }
38518
+ async function enableMemfsIfCloud(agentId) {
38519
+ const { getServerUrl: getServerUrl2 } = await init_client2().then(() => exports_client);
38520
+ const serverUrl = getServerUrl2();
38521
+ if (!serverUrl.includes("api.letta.com"))
38522
+ return;
38523
+ try {
38524
+ await applyMemfsFlags(agentId, true, undefined);
38525
+ } catch (error) {
38526
+ console.warn(`Warning: Could not enable memfs for new agent: ${error instanceof Error ? error.message : String(error)}`);
38527
+ }
38528
+ }
38529
+ var MEMORY_FS_ROOT = ".letta", MEMORY_FS_AGENTS_DIR = "agents", MEMORY_FS_MEMORY_DIR = "memory", MEMORY_SYSTEM_DIR = "system";
38530
+ var init_memoryFilesystem = () => {};
38531
+
37552
38532
  // src/tools/impl/shellEnv.ts
37553
38533
  var exports_shellEnv = {};
37554
38534
  __export(exports_shellEnv, {
@@ -37556,7 +38536,7 @@ __export(exports_shellEnv, {
37556
38536
  getShellEnv: () => getShellEnv,
37557
38537
  ensureLettaShimDir: () => ensureLettaShimDir
37558
38538
  });
37559
- import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync2 } from "node:fs";
38539
+ import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync2 } from "node:fs";
37560
38540
  import { createRequire as createRequire2 } from "node:module";
37561
38541
  import { tmpdir } from "node:os";
37562
38542
  import * as path3 from "node:path";
@@ -37629,7 +38609,7 @@ function ensureLettaShimDir(invocation) {
37629
38609
  if (!invocation.command)
37630
38610
  return null;
37631
38611
  const shimDir = path3.join(tmpdir(), "letta-code-shell-shim");
37632
- mkdirSync4(shimDir, { recursive: true });
38612
+ mkdirSync5(shimDir, { recursive: true });
37633
38613
  if (process.platform === "win32") {
37634
38614
  const cmdPath = path3.join(shimDir, "letta.cmd");
37635
38615
  const quotedCommand = `"${invocation.command.replaceAll('"', '""')}"`;
@@ -37669,9 +38649,33 @@ function getShellEnv() {
37669
38649
  const existingPath = env3[pathKey] || "";
37670
38650
  env3[pathKey] = existingPath ? `${pathPrefixes.join(path3.delimiter)}${path3.delimiter}${existingPath}` : pathPrefixes.join(path3.delimiter);
37671
38651
  }
38652
+ let agentId;
37672
38653
  try {
37673
- env3.LETTA_AGENT_ID = getCurrentAgentId();
38654
+ const resolvedAgentId = getCurrentAgentId();
38655
+ if (typeof resolvedAgentId === "string" && resolvedAgentId.trim()) {
38656
+ agentId = resolvedAgentId.trim();
38657
+ }
37674
38658
  } catch {}
38659
+ if (!agentId) {
38660
+ const fallbackAgentId = env3.AGENT_ID || env3.LETTA_AGENT_ID;
38661
+ if (typeof fallbackAgentId === "string" && fallbackAgentId.trim()) {
38662
+ agentId = fallbackAgentId.trim();
38663
+ }
38664
+ }
38665
+ if (agentId) {
38666
+ env3.LETTA_AGENT_ID = agentId;
38667
+ env3.AGENT_ID = agentId;
38668
+ try {
38669
+ if (settingsManager.isMemfsEnabled(agentId)) {
38670
+ const memoryDir = getMemoryFilesystemRoot(agentId);
38671
+ env3.LETTA_MEMORY_DIR = memoryDir;
38672
+ env3.MEMORY_DIR = memoryDir;
38673
+ } else {
38674
+ delete env3.LETTA_MEMORY_DIR;
38675
+ delete env3.MEMORY_DIR;
38676
+ }
38677
+ } catch {}
38678
+ }
37675
38679
  if (!env3.LETTA_API_KEY || !env3.LETTA_BASE_URL) {
37676
38680
  try {
37677
38681
  const settings = settingsManager.getSettings();
@@ -37699,6 +38703,7 @@ function getShellEnv() {
37699
38703
  var LETTA_BIN_ARGS_ENV = "LETTA_CODE_BIN_ARGS_JSON";
37700
38704
  var init_shellEnv = __esm(async () => {
37701
38705
  init_context();
38706
+ init_memoryFilesystem();
37702
38707
  await __promiseAll([
37703
38708
  init_client2(),
37704
38709
  init_settings_manager()
@@ -38472,8 +39477,8 @@ async function edit(args) {
38472
39477
  var init_Edit2 = () => {};
38473
39478
 
38474
39479
  // src/cli/helpers/planName.ts
38475
- import { homedir as homedir9 } from "node:os";
38476
- import { join as join9 } from "node:path";
39480
+ import { homedir as homedir10 } from "node:os";
39481
+ import { join as join10 } from "node:path";
38477
39482
  function randomElement(arr) {
38478
39483
  return arr[Math.floor(Math.random() * arr.length)];
38479
39484
  }
@@ -38485,7 +39490,7 @@ function generatePlanName() {
38485
39490
  }
38486
39491
  function generatePlanFilePath() {
38487
39492
  const name = generatePlanName();
38488
- return join9(homedir9(), ".letta", "plans", `${name}.md`);
39493
+ return join10(homedir10(), ".letta", "plans", `${name}.md`);
38489
39494
  }
38490
39495
  var adjectives, nouns;
38491
39496
  var init_planName = __esm(() => {
@@ -38594,8 +39599,8 @@ var exports_mode = {};
38594
39599
  __export(exports_mode, {
38595
39600
  permissionMode: () => permissionMode2
38596
39601
  });
38597
- import { homedir as homedir10 } from "node:os";
38598
- import { join as join10 } from "node:path";
39602
+ import { homedir as homedir11 } from "node:os";
39603
+ import { join as join11 } from "node:path";
38599
39604
  function getGlobalMode2() {
38600
39605
  const global2 = globalThis;
38601
39606
  if (!global2[MODE_KEY2]) {
@@ -38716,7 +39721,7 @@ class PermissionModeManager2 {
38716
39721
  return "allow";
38717
39722
  }
38718
39723
  if (writeTools.includes(toolName)) {
38719
- const plansDir = join10(homedir10(), ".letta", "plans");
39724
+ const plansDir = join11(homedir11(), ".letta", "plans");
38720
39725
  let targetPath = toolArgs?.file_path || toolArgs?.path;
38721
39726
  if ((toolName === "ApplyPatch" || toolName === "apply_patch") && toolArgs?.input) {
38722
39727
  const input = toolArgs.input;
@@ -40744,7 +41749,7 @@ var init_LS2 = __esm(() => {
40744
41749
 
40745
41750
  // src/tools/impl/LS.ts
40746
41751
  import { readdir as readdir3, stat } from "node:fs/promises";
40747
- import { join as join12, resolve as resolve7 } from "node:path";
41752
+ import { join as join13, resolve as resolve7 } from "node:path";
40748
41753
  async function ls(args) {
40749
41754
  validateRequiredParams(args, ["path"], "LS");
40750
41755
  validateParamTypes(args, LS_default2, "LS");
@@ -40754,7 +41759,7 @@ async function ls(args) {
40754
41759
  const items = await readdir3(dirPath);
40755
41760
  const filteredItems = items.filter((item) => !ignore.some((pattern) => import_picomatch.default.isMatch(item, pattern)));
40756
41761
  const fileInfos = await Promise.all(filteredItems.map(async (item) => {
40757
- const fullPath = join12(dirPath, item);
41762
+ const fullPath = join13(dirPath, item);
40758
41763
  try {
40759
41764
  const stats = await stat(fullPath);
40760
41765
  return {
@@ -40917,9 +41922,9 @@ __export(exports_imageResize_magick, {
40917
41922
  import { execSync } from "node:child_process";
40918
41923
  import { readFileSync as readFileSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "node:fs";
40919
41924
  import { tmpdir as tmpdir2 } from "node:os";
40920
- import { join as join13 } from "node:path";
41925
+ import { join as join14 } from "node:path";
40921
41926
  async function getImageDimensions(buffer) {
40922
- const tempInput = join13(tmpdir2(), `image-${Date.now()}-${Math.random().toString(36).slice(2)}.tmp`);
41927
+ const tempInput = join14(tmpdir2(), `image-${Date.now()}-${Math.random().toString(36).slice(2)}.tmp`);
40923
41928
  writeFileSync4(tempInput, buffer);
40924
41929
  try {
40925
41930
  const output = execSync(`magick identify -format "%w %h %m" "${tempInput}"`, {
@@ -40942,12 +41947,12 @@ async function compressToFitByteLimit(buffer, currentWidth, currentHeight) {
40942
41947
  if (buffer.length <= MAX_IMAGE_BYTES) {
40943
41948
  return null;
40944
41949
  }
40945
- const tempInput = join13(tmpdir2(), `compress-input-${Date.now()}-${Math.random().toString(36).slice(2)}.tmp`);
41950
+ const tempInput = join14(tmpdir2(), `compress-input-${Date.now()}-${Math.random().toString(36).slice(2)}.tmp`);
40946
41951
  writeFileSync4(tempInput, buffer);
40947
41952
  try {
40948
41953
  const qualities = [85, 70, 55, 40];
40949
41954
  for (const quality of qualities) {
40950
- const tempOutput = join13(tmpdir2(), `compress-output-${Date.now()}-${Math.random().toString(36).slice(2)}.jpg`);
41955
+ const tempOutput = join14(tmpdir2(), `compress-output-${Date.now()}-${Math.random().toString(36).slice(2)}.jpg`);
40951
41956
  try {
40952
41957
  execSync(`magick "${tempInput}" -quality ${quality} "${tempOutput}"`, {
40953
41958
  stdio: "ignore"
@@ -40973,7 +41978,7 @@ async function compressToFitByteLimit(buffer, currentWidth, currentHeight) {
40973
41978
  for (const scale of scales) {
40974
41979
  const scaledWidth = Math.floor(currentWidth * scale);
40975
41980
  const scaledHeight = Math.floor(currentHeight * scale);
40976
- const tempOutput = join13(tmpdir2(), `compress-output-${Date.now()}-${Math.random().toString(36).slice(2)}.jpg`);
41981
+ const tempOutput = join14(tmpdir2(), `compress-output-${Date.now()}-${Math.random().toString(36).slice(2)}.jpg`);
40977
41982
  try {
40978
41983
  execSync(`magick "${tempInput}" -resize ${scaledWidth}x${scaledHeight} -quality 70 "${tempOutput}"`, {
40979
41984
  stdio: "ignore"
@@ -41017,11 +42022,11 @@ async function resizeImageIfNeeded(buffer, inputMediaType) {
41017
42022
  resized: false
41018
42023
  };
41019
42024
  }
41020
- const tempInput = join13(tmpdir2(), `resize-input-${Date.now()}-${Math.random().toString(36).slice(2)}.tmp`);
42025
+ const tempInput = join14(tmpdir2(), `resize-input-${Date.now()}-${Math.random().toString(36).slice(2)}.tmp`);
41021
42026
  writeFileSync4(tempInput, buffer);
41022
42027
  try {
41023
42028
  if (needsResize) {
41024
- const tempOutput2 = join13(tmpdir2(), `resize-output-${Date.now()}-${Math.random().toString(36).slice(2)}`);
42029
+ const tempOutput2 = join14(tmpdir2(), `resize-output-${Date.now()}-${Math.random().toString(36).slice(2)}`);
41025
42030
  let outputBuffer2;
41026
42031
  let outputMediaType;
41027
42032
  if (format2 === "jpeg" || format2 === "jpg") {
@@ -41052,7 +42057,7 @@ async function resizeImageIfNeeded(buffer, inputMediaType) {
41052
42057
  resized: true
41053
42058
  };
41054
42059
  }
41055
- const tempOutput = join13(tmpdir2(), `convert-output-${Date.now()}-${Math.random().toString(36).slice(2)}.png`);
42060
+ const tempOutput = join14(tmpdir2(), `convert-output-${Date.now()}-${Math.random().toString(36).slice(2)}.png`);
41056
42061
  execSync(`magick "${tempInput}" "${tempOutput}"`, {
41057
42062
  stdio: "ignore"
41058
42063
  });
@@ -42272,7 +43277,7 @@ var require_range = __commonJS((exports, module) => {
42272
43277
  parseRange(range) {
42273
43278
  const memoOpts = (this.options.includePrerelease && FLAG_INCLUDE_PRERELEASE) | (this.options.loose && FLAG_LOOSE);
42274
43279
  const memoKey = memoOpts + ":" + range;
42275
- const cached = cache4.get(memoKey);
43280
+ const cached = cache5.get(memoKey);
42276
43281
  if (cached) {
42277
43282
  return cached;
42278
43283
  }
@@ -42306,7 +43311,7 @@ var require_range = __commonJS((exports, module) => {
42306
43311
  rangeMap.delete("");
42307
43312
  }
42308
43313
  const result = [...rangeMap.values()];
42309
- cache4.set(memoKey, result);
43314
+ cache5.set(memoKey, result);
42310
43315
  return result;
42311
43316
  }
42312
43317
  intersects(range, options) {
@@ -42344,7 +43349,7 @@ var require_range = __commonJS((exports, module) => {
42344
43349
  }
42345
43350
  module.exports = Range;
42346
43351
  var LRU = require_lrucache();
42347
- var cache4 = new LRU;
43352
+ var cache5 = new LRU;
42348
43353
  var parseOptions = require_parse_options();
42349
43354
  var Comparator = require_comparator();
42350
43355
  var debug = require_debug();
@@ -47387,7 +48392,7 @@ var require_utility = __commonJS((exports, module) => {
47387
48392
  format2.heif.input.fileSuffix = [".avif"];
47388
48393
  format2.heif.output.alias = ["avif"];
47389
48394
  }
47390
- function cache4(options) {
48395
+ function cache5(options) {
47391
48396
  if (is.bool(options)) {
47392
48397
  if (options) {
47393
48398
  return sharp.cache(50, 20, 100);
@@ -47400,7 +48405,7 @@ var require_utility = __commonJS((exports, module) => {
47400
48405
  return sharp.cache();
47401
48406
  }
47402
48407
  }
47403
- cache4(true);
48408
+ cache5(true);
47404
48409
  function concurrency(concurrency2) {
47405
48410
  return sharp.concurrency(is.integer(concurrency2) ? concurrency2 : null);
47406
48411
  }
@@ -47439,7 +48444,7 @@ var require_utility = __commonJS((exports, module) => {
47439
48444
  }
47440
48445
  }
47441
48446
  module.exports = (Sharp) => {
47442
- Sharp.cache = cache4;
48447
+ Sharp.cache = cache5;
47443
48448
  Sharp.concurrency = concurrency;
47444
48449
  Sharp.counters = counters;
47445
48450
  Sharp.simd = simd;
@@ -54321,19 +55326,19 @@ __export(exports_skills, {
54321
55326
  SKILLS_DIR: () => SKILLS_DIR,
54322
55327
  GLOBAL_SKILLS_DIR: () => GLOBAL_SKILLS_DIR
54323
55328
  });
54324
- import { existsSync as existsSync7 } from "node:fs";
55329
+ import { existsSync as existsSync8 } from "node:fs";
54325
55330
  import { readdir as readdir5, readFile as readFile3 } from "node:fs/promises";
54326
- import { dirname as dirname4, join as join14 } from "node:path";
55331
+ import { dirname as dirname4, join as join15 } from "node:path";
54327
55332
  import { fileURLToPath as fileURLToPath6 } from "node:url";
54328
55333
  function getBundledSkillsPath() {
54329
55334
  const thisDir = dirname4(fileURLToPath6(import.meta.url));
54330
55335
  if (thisDir.includes("src/agent") || thisDir.includes("src\\agent")) {
54331
- return join14(thisDir, "../skills/builtin");
55336
+ return join15(thisDir, "../skills/builtin");
54332
55337
  }
54333
- return join14(thisDir, "skills");
55338
+ return join15(thisDir, "skills");
54334
55339
  }
54335
55340
  function getAgentSkillsDir(agentId) {
54336
- return join14(process.env.HOME || process.env.USERPROFILE || "~", ".letta/agents", agentId, "skills");
55341
+ return join15(process.env.HOME || process.env.USERPROFILE || "~", ".letta/agents", agentId, "skills");
54337
55342
  }
54338
55343
  async function getBundledSkills() {
54339
55344
  const bundledPath = getBundledSkillsPath();
@@ -54342,7 +55347,7 @@ async function getBundledSkills() {
54342
55347
  }
54343
55348
  async function discoverSkillsFromDir(skillsPath, source) {
54344
55349
  const errors = [];
54345
- if (!existsSync7(skillsPath)) {
55350
+ if (!existsSync8(skillsPath)) {
54346
55351
  return { skills: [], errors: [] };
54347
55352
  }
54348
55353
  const skills = [];
@@ -54356,21 +55361,25 @@ async function discoverSkillsFromDir(skillsPath, source) {
54356
55361
  }
54357
55362
  return { skills, errors };
54358
55363
  }
54359
- async function discoverSkills(projectSkillsPath = join14(process.cwd(), SKILLS_DIR), agentId, options) {
55364
+ async function discoverSkills(projectSkillsPath = join15(process.cwd(), SKILLS_DIR), agentId, options) {
54360
55365
  const allErrors = [];
54361
55366
  const skillsById = new Map;
54362
- if (!options?.skipBundled) {
55367
+ const sourceSet = new Set(options?.sources ?? ALL_SKILL_SOURCES);
55368
+ const includeSource = (source) => sourceSet.has(source);
55369
+ if (includeSource("bundled") && !options?.skipBundled) {
54363
55370
  const bundledSkills = await getBundledSkills();
54364
55371
  for (const skill of bundledSkills) {
54365
55372
  skillsById.set(skill.id, skill);
54366
55373
  }
54367
55374
  }
54368
- const globalResult = await discoverSkillsFromDir(GLOBAL_SKILLS_DIR, "global");
54369
- allErrors.push(...globalResult.errors);
54370
- for (const skill of globalResult.skills) {
54371
- skillsById.set(skill.id, skill);
55375
+ if (includeSource("global")) {
55376
+ const globalResult = await discoverSkillsFromDir(GLOBAL_SKILLS_DIR, "global");
55377
+ allErrors.push(...globalResult.errors);
55378
+ for (const skill of globalResult.skills) {
55379
+ skillsById.set(skill.id, skill);
55380
+ }
54372
55381
  }
54373
- if (agentId) {
55382
+ if (agentId && includeSource("agent")) {
54374
55383
  const agentSkillsDir = getAgentSkillsDir(agentId);
54375
55384
  const agentResult = await discoverSkillsFromDir(agentSkillsDir, "agent");
54376
55385
  allErrors.push(...agentResult.errors);
@@ -54378,10 +55387,12 @@ async function discoverSkills(projectSkillsPath = join14(process.cwd(), SKILLS_D
54378
55387
  skillsById.set(skill.id, skill);
54379
55388
  }
54380
55389
  }
54381
- const projectResult = await discoverSkillsFromDir(projectSkillsPath, "project");
54382
- allErrors.push(...projectResult.errors);
54383
- for (const skill of projectResult.skills) {
54384
- skillsById.set(skill.id, skill);
55390
+ if (includeSource("project")) {
55391
+ const projectResult = await discoverSkillsFromDir(projectSkillsPath, "project");
55392
+ allErrors.push(...projectResult.errors);
55393
+ for (const skill of projectResult.skills) {
55394
+ skillsById.set(skill.id, skill);
55395
+ }
54385
55396
  }
54386
55397
  return {
54387
55398
  skills: Array.from(skillsById.values()),
@@ -54392,7 +55403,7 @@ async function findSkillFiles(currentPath, rootPath, skills, errors, source) {
54392
55403
  try {
54393
55404
  const entries = await readdir5(currentPath, { withFileTypes: true });
54394
55405
  for (const entry of entries) {
54395
- const fullPath = join14(currentPath, entry.name);
55406
+ const fullPath = join15(currentPath, entry.name);
54396
55407
  if (entry.isDirectory()) {
54397
55408
  await findSkillFiles(fullPath, rootPath, skills, errors, source);
54398
55409
  } else if (entry.isFile() && entry.name.toUpperCase() === "SKILL.MD") {
@@ -54466,7 +55477,8 @@ ${lines.join(`
54466
55477
  }
54467
55478
  var SKILLS_DIR = ".skills", GLOBAL_SKILLS_DIR;
54468
55479
  var init_skills = __esm(() => {
54469
- GLOBAL_SKILLS_DIR = join14(process.env.HOME || process.env.USERPROFILE || "~", ".letta/skills");
55480
+ init_skillSources();
55481
+ GLOBAL_SKILLS_DIR = join15(process.env.HOME || process.env.USERPROFILE || "~", ".letta/skills");
54470
55482
  });
54471
55483
 
54472
55484
  // src/tools/impl/skillContentRegistry.ts
@@ -54491,7 +55503,7 @@ var init_skillContentRegistry = __esm(() => {
54491
55503
  // src/tools/impl/Skill.ts
54492
55504
  import { readdirSync as readdirSync3 } from "node:fs";
54493
55505
  import { readFile as readFile4 } from "node:fs/promises";
54494
- import { dirname as dirname5, join as join15 } from "node:path";
55506
+ import { dirname as dirname5, join as join16 } from "node:path";
54495
55507
  function hasAdditionalFiles(skillMdPath) {
54496
55508
  try {
54497
55509
  const skillDir = dirname5(skillMdPath);
@@ -54502,19 +55514,19 @@ function hasAdditionalFiles(skillMdPath) {
54502
55514
  }
54503
55515
  }
54504
55516
  async function readSkillContent(skillId, skillsDir, agentId) {
54505
- const projectSkillPath = join15(skillsDir, skillId, "SKILL.md");
55517
+ const projectSkillPath = join16(skillsDir, skillId, "SKILL.md");
54506
55518
  try {
54507
55519
  const content = await readFile4(projectSkillPath, "utf-8");
54508
55520
  return { content, path: projectSkillPath };
54509
55521
  } catch {}
54510
55522
  if (agentId) {
54511
- const agentSkillPath = join15(getAgentSkillsDir(agentId), skillId, "SKILL.md");
55523
+ const agentSkillPath = join16(getAgentSkillsDir(agentId), skillId, "SKILL.md");
54512
55524
  try {
54513
55525
  const content = await readFile4(agentSkillPath, "utf-8");
54514
55526
  return { content, path: agentSkillPath };
54515
55527
  } catch {}
54516
55528
  }
54517
- const globalSkillPath = join15(GLOBAL_SKILLS_DIR, skillId, "SKILL.md");
55529
+ const globalSkillPath = join16(GLOBAL_SKILLS_DIR, skillId, "SKILL.md");
54518
55530
  try {
54519
55531
  const content = await readFile4(globalSkillPath, "utf-8");
54520
55532
  return { content, path: globalSkillPath };
@@ -54528,8 +55540,8 @@ async function readSkillContent(skillId, skillsDir, agentId) {
54528
55540
  } catch {}
54529
55541
  }
54530
55542
  try {
54531
- const bundledSkillsDir = join15(process.cwd(), "skills", "skills");
54532
- const bundledSkillPath = join15(bundledSkillsDir, skillId, "SKILL.md");
55543
+ const bundledSkillsDir = join16(process.cwd(), "skills", "skills");
55544
+ const bundledSkillPath = join16(bundledSkillsDir, skillId, "SKILL.md");
54533
55545
  const content = await readFile4(bundledSkillPath, "utf-8");
54534
55546
  return { content, path: bundledSkillPath };
54535
55547
  } catch {
@@ -54541,7 +55553,7 @@ async function getResolvedSkillsDir() {
54541
55553
  if (skillsDir) {
54542
55554
  return skillsDir;
54543
55555
  }
54544
- return join15(process.cwd(), SKILLS_DIR);
55556
+ return join16(process.cwd(), SKILLS_DIR);
54545
55557
  }
54546
55558
  async function skill(args) {
54547
55559
  validateRequiredParams(args, ["skill"], "Skill");
@@ -54806,109 +55818,6 @@ var init_cli = __esm(() => {
54806
55818
  cliPermissions = new CliPermissions;
54807
55819
  });
54808
55820
 
54809
- // src/agent/available-models.ts
54810
- var exports_available_models = {};
54811
- __export(exports_available_models, {
54812
- prefetchAvailableModelHandles: () => prefetchAvailableModelHandles,
54813
- getModelContextWindow: () => getModelContextWindow,
54814
- getAvailableModelsCacheInfo: () => getAvailableModelsCacheInfo,
54815
- getAvailableModelHandles: () => getAvailableModelHandles,
54816
- clearAvailableModelsCache: () => clearAvailableModelsCache
54817
- });
54818
- function isFresh(now = Date.now()) {
54819
- return cache4 !== null && now - cache4.fetchedAt < CACHE_TTL_MS;
54820
- }
54821
- function clearAvailableModelsCache() {
54822
- cache4 = null;
54823
- }
54824
- function getAvailableModelsCacheInfo() {
54825
- const now = Date.now();
54826
- return {
54827
- hasCache: cache4 !== null,
54828
- isFresh: isFresh(now),
54829
- fetchedAt: cache4?.fetchedAt ?? null,
54830
- ageMs: cache4 ? now - cache4.fetchedAt : null,
54831
- ttlMs: CACHE_TTL_MS
54832
- };
54833
- }
54834
- async function refreshByokProviders() {
54835
- const client = await getClient2();
54836
- try {
54837
- const providers = await client.get("/v1/providers/");
54838
- const byokProviders = providers.filter((p) => p.provider_category === "byok");
54839
- await Promise.allSettled(byokProviders.map(async (provider) => {
54840
- try {
54841
- await client.patch(`/v1/providers/${provider.id}/refresh`);
54842
- } catch (error) {
54843
- debugWarn("available-models", `Failed to refresh provider ${provider.name} (${provider.id}):`, error);
54844
- }
54845
- }));
54846
- } catch (error) {
54847
- debugWarn("available-models", "Failed to list providers for refresh:", error);
54848
- }
54849
- }
54850
- async function fetchFromNetwork() {
54851
- const client = await getClient2();
54852
- const modelsList = await client.models.list();
54853
- const handles = new Set(modelsList.map((m) => m.handle).filter((h) => !!h));
54854
- const contextWindows = new Map;
54855
- for (const model of modelsList) {
54856
- if (model.handle && model.max_context_window) {
54857
- contextWindows.set(model.handle, model.max_context_window);
54858
- }
54859
- }
54860
- return { handles, contextWindows, fetchedAt: Date.now() };
54861
- }
54862
- async function getAvailableModelHandles(options) {
54863
- const forceRefresh = options?.forceRefresh === true;
54864
- const now = Date.now();
54865
- if (!forceRefresh && isFresh(now) && cache4) {
54866
- return {
54867
- handles: cache4.handles,
54868
- source: "cache",
54869
- fetchedAt: cache4.fetchedAt
54870
- };
54871
- }
54872
- if (!forceRefresh && inflight) {
54873
- const entry2 = await inflight;
54874
- return {
54875
- handles: entry2.handles,
54876
- source: "network",
54877
- fetchedAt: entry2.fetchedAt
54878
- };
54879
- }
54880
- if (forceRefresh) {
54881
- await refreshByokProviders();
54882
- }
54883
- inflight = fetchFromNetwork().then((entry2) => {
54884
- cache4 = entry2;
54885
- return entry2;
54886
- }).finally(() => {
54887
- inflight = null;
54888
- });
54889
- const entry = await inflight;
54890
- return {
54891
- handles: entry.handles,
54892
- source: "network",
54893
- fetchedAt: entry.fetchedAt
54894
- };
54895
- }
54896
- function prefetchAvailableModelHandles() {
54897
- getAvailableModelHandles().catch(() => {});
54898
- }
54899
- async function getModelContextWindow(handle) {
54900
- if (!cache4) {
54901
- await getAvailableModelHandles();
54902
- }
54903
- return cache4?.contextWindows.get(handle);
54904
- }
54905
- var CACHE_TTL_MS, cache4 = null, inflight = null;
54906
- var init_available_models = __esm(async () => {
54907
- init_debug();
54908
- await init_client2();
54909
- CACHE_TTL_MS = 5 * 60 * 1000;
54910
- });
54911
-
54912
55821
  // src/agent/subagents/manager.ts
54913
55822
  import { spawn as spawn4 } from "node:child_process";
54914
55823
  import { createInterface } from "node:readline";
@@ -56798,11 +57707,6 @@ var init_Task3 = __esm(() => {
56798
57707
  conversation_id: {
56799
57708
  type: "string",
56800
57709
  description: "Resume from an existing conversation. Does NOT require agent_id (conversation IDs are unique and encode the agent)."
56801
- },
56802
- max_turns: {
56803
- type: "integer",
56804
- exclusiveMinimum: 0,
56805
- description: "Maximum number of agentic turns (API round-trips) before stopping. Defaults to unlimited (recommended for most use cases)."
56806
57710
  }
56807
57711
  },
56808
57712
  required: ["description", "prompt", "subagent_type"],
@@ -58665,8 +59569,8 @@ function matchesFilePattern(query, pattern, workingDirectory) {
58665
59569
  globPattern = globPattern.slice(2);
58666
59570
  }
58667
59571
  if (globPattern.startsWith("~/")) {
58668
- const homedir11 = __require("node:os").homedir();
58669
- globPattern = globPattern.replace(/^~/, homedir11);
59572
+ const homedir12 = __require("node:os").homedir();
59573
+ globPattern = globPattern.replace(/^~/, homedir12);
58670
59574
  }
58671
59575
  globPattern = normalizeAbsolutePattern(globPattern, workingDirectory);
58672
59576
  const windowsContext = isWindowsContext(workingDirectory);
@@ -59088,8 +59992,8 @@ __export(exports_loader, {
59088
59992
  savePermissionRule: () => savePermissionRule,
59089
59993
  loadPermissions: () => loadPermissions
59090
59994
  });
59091
- import { homedir as homedir11 } from "node:os";
59092
- import { join as join16 } from "node:path";
59995
+ import { homedir as homedir12 } from "node:os";
59996
+ import { join as join17 } from "node:path";
59093
59997
  async function loadPermissions(workingDirectory = process.cwd()) {
59094
59998
  const merged = {
59095
59999
  allow: [],
@@ -59098,10 +60002,10 @@ async function loadPermissions(workingDirectory = process.cwd()) {
59098
60002
  additionalDirectories: []
59099
60003
  };
59100
60004
  const sources = [
59101
- join16(process.env.XDG_CONFIG_HOME || join16(homedir11(), ".config"), "letta", "settings.json"),
59102
- join16(homedir11(), ".letta", "settings.json"),
59103
- join16(workingDirectory, ".letta", "settings.json"),
59104
- join16(workingDirectory, ".letta", "settings.local.json")
60005
+ join17(process.env.XDG_CONFIG_HOME || join17(homedir12(), ".config"), "letta", "settings.json"),
60006
+ join17(homedir12(), ".letta", "settings.json"),
60007
+ join17(workingDirectory, ".letta", "settings.json"),
60008
+ join17(workingDirectory, ".letta", "settings.local.json")
59105
60009
  ];
59106
60010
  for (const settingsPath of sources) {
59107
60011
  try {
@@ -59137,13 +60041,13 @@ async function savePermissionRule(rule, ruleType, scope, workingDirectory = proc
59137
60041
  let settingsPath;
59138
60042
  switch (scope) {
59139
60043
  case "user":
59140
- settingsPath = join16(process.env.XDG_CONFIG_HOME || join16(homedir11(), ".config"), "letta", "settings.json");
60044
+ settingsPath = join17(process.env.XDG_CONFIG_HOME || join17(homedir12(), ".config"), "letta", "settings.json");
59141
60045
  break;
59142
60046
  case "project":
59143
- settingsPath = join16(workingDirectory, ".letta", "settings.json");
60047
+ settingsPath = join17(workingDirectory, ".letta", "settings.json");
59144
60048
  break;
59145
60049
  case "local":
59146
- settingsPath = join16(workingDirectory, ".letta", "settings.local.json");
60050
+ settingsPath = join17(workingDirectory, ".letta", "settings.local.json");
59147
60051
  break;
59148
60052
  }
59149
60053
  let settings = {};
@@ -59168,7 +60072,7 @@ async function savePermissionRule(rule, ruleType, scope, workingDirectory = proc
59168
60072
  }
59169
60073
  }
59170
60074
  async function ensureLocalSettingsIgnored(workingDirectory) {
59171
- const gitignorePath = join16(workingDirectory, ".gitignore");
60075
+ const gitignorePath = join17(workingDirectory, ".gitignore");
59172
60076
  const pattern = ".letta/settings.local.json";
59173
60077
  try {
59174
60078
  let content = "";
@@ -59193,7 +60097,7 @@ var exports_analyzer = {};
59193
60097
  __export(exports_analyzer, {
59194
60098
  analyzeApprovalContext: () => analyzeApprovalContext
59195
60099
  });
59196
- import { homedir as homedir12 } from "node:os";
60100
+ import { homedir as homedir13 } from "node:os";
59197
60101
  import { dirname as dirname7, relative, resolve as resolve16, win32 as win322 } from "node:path";
59198
60102
  function normalizeOsPath(path18) {
59199
60103
  return path18.replace(/\\/g, "/");
@@ -59230,7 +60134,7 @@ function formatAbsoluteRulePath(path18) {
59230
60134
  return `//${normalized.replace(/^\/+/, "")}`;
59231
60135
  }
59232
60136
  function formatDisplayPath(path18) {
59233
- return normalizeOsPath(path18).replace(normalizeOsPath(homedir12()), "~");
60137
+ return normalizeOsPath(path18).replace(normalizeOsPath(homedir13()), "~");
59234
60138
  }
59235
60139
  function analyzeApprovalContext(toolName, toolArgs, workingDirectory) {
59236
60140
  const resolveFilePath = () => {
@@ -59373,7 +60277,7 @@ function detectSkillScript(command, workingDir) {
59373
60277
  return null;
59374
60278
  }
59375
60279
  const normalizedWorkingDir = normalizePathSeparators(workingDir).replace(/\/$/, "");
59376
- const normalizedHomeDir = normalizePathSeparators(homedir12()).replace(/\/$/, "");
60280
+ const normalizedHomeDir = normalizePathSeparators(homedir13()).replace(/\/$/, "");
59377
60281
  const detect = (source, regex2) => {
59378
60282
  for (const candidate of pathCandidates) {
59379
60283
  const match3 = candidate.match(regex2);
@@ -59687,50 +60591,6 @@ var init_analyzer = __esm(() => {
59687
60591
  ];
59688
60592
  });
59689
60593
 
59690
- // src/tools/filter.ts
59691
- var exports_filter = {};
59692
- __export(exports_filter, {
59693
- toolFilter: () => toolFilter
59694
- });
59695
-
59696
- class ToolFilterManager {
59697
- enabledTools = null;
59698
- setEnabledTools(toolsString) {
59699
- if (toolsString === "") {
59700
- this.enabledTools = [];
59701
- } else {
59702
- this.enabledTools = toolsString.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
59703
- }
59704
- }
59705
- isEnabled(toolName) {
59706
- if (this.enabledTools === null) {
59707
- return true;
59708
- }
59709
- return this.enabledTools.includes(toolName);
59710
- }
59711
- getEnabledTools() {
59712
- return this.enabledTools ? [...this.enabledTools] : null;
59713
- }
59714
- isActive() {
59715
- return this.enabledTools !== null;
59716
- }
59717
- reset() {
59718
- this.enabledTools = null;
59719
- }
59720
- }
59721
- function getFilter() {
59722
- const global2 = globalThis;
59723
- if (!global2[FILTER_KEY]) {
59724
- global2[FILTER_KEY] = new ToolFilterManager;
59725
- }
59726
- return global2[FILTER_KEY];
59727
- }
59728
- var FILTER_KEY, toolFilter;
59729
- var init_filter = __esm(() => {
59730
- FILTER_KEY = Symbol.for("@letta/toolFilter");
59731
- toolFilter = getFilter();
59732
- });
59733
-
59734
60594
  // src/tools/manager.ts
59735
60595
  var exports_manager2 = {};
59736
60596
  __export(exports_manager2, {
@@ -60778,19 +61638,19 @@ __export(exports_skills2, {
60778
61638
  SKILLS_DIR: () => SKILLS_DIR2,
60779
61639
  GLOBAL_SKILLS_DIR: () => GLOBAL_SKILLS_DIR2
60780
61640
  });
60781
- import { existsSync as existsSync8 } from "node:fs";
61641
+ import { existsSync as existsSync9 } from "node:fs";
60782
61642
  import { readdir as readdir6, readFile as readFile5 } from "node:fs/promises";
60783
- import { dirname as dirname8, join as join17 } from "node:path";
61643
+ import { dirname as dirname8, join as join18 } from "node:path";
60784
61644
  import { fileURLToPath as fileURLToPath7 } from "node:url";
60785
61645
  function getBundledSkillsPath2() {
60786
61646
  const thisDir = dirname8(fileURLToPath7(import.meta.url));
60787
61647
  if (thisDir.includes("src/agent") || thisDir.includes("src\\agent")) {
60788
- return join17(thisDir, "../skills/builtin");
61648
+ return join18(thisDir, "../skills/builtin");
60789
61649
  }
60790
- return join17(thisDir, "skills");
61650
+ return join18(thisDir, "skills");
60791
61651
  }
60792
61652
  function getAgentSkillsDir2(agentId) {
60793
- return join17(process.env.HOME || process.env.USERPROFILE || "~", ".letta/agents", agentId, "skills");
61653
+ return join18(process.env.HOME || process.env.USERPROFILE || "~", ".letta/agents", agentId, "skills");
60794
61654
  }
60795
61655
  async function getBundledSkills2() {
60796
61656
  const bundledPath = getBundledSkillsPath2();
@@ -60799,7 +61659,7 @@ async function getBundledSkills2() {
60799
61659
  }
60800
61660
  async function discoverSkillsFromDir2(skillsPath, source) {
60801
61661
  const errors = [];
60802
- if (!existsSync8(skillsPath)) {
61662
+ if (!existsSync9(skillsPath)) {
60803
61663
  return { skills: [], errors: [] };
60804
61664
  }
60805
61665
  const skills = [];
@@ -60813,21 +61673,25 @@ async function discoverSkillsFromDir2(skillsPath, source) {
60813
61673
  }
60814
61674
  return { skills, errors };
60815
61675
  }
60816
- async function discoverSkills2(projectSkillsPath = join17(process.cwd(), SKILLS_DIR2), agentId, options) {
61676
+ async function discoverSkills2(projectSkillsPath = join18(process.cwd(), SKILLS_DIR2), agentId, options) {
60817
61677
  const allErrors = [];
60818
61678
  const skillsById = new Map;
60819
- if (!options?.skipBundled) {
61679
+ const sourceSet = new Set(options?.sources ?? ALL_SKILL_SOURCES);
61680
+ const includeSource = (source) => sourceSet.has(source);
61681
+ if (includeSource("bundled") && !options?.skipBundled) {
60820
61682
  const bundledSkills = await getBundledSkills2();
60821
61683
  for (const skill2 of bundledSkills) {
60822
61684
  skillsById.set(skill2.id, skill2);
60823
61685
  }
60824
61686
  }
60825
- const globalResult = await discoverSkillsFromDir2(GLOBAL_SKILLS_DIR2, "global");
60826
- allErrors.push(...globalResult.errors);
60827
- for (const skill2 of globalResult.skills) {
60828
- skillsById.set(skill2.id, skill2);
61687
+ if (includeSource("global")) {
61688
+ const globalResult = await discoverSkillsFromDir2(GLOBAL_SKILLS_DIR2, "global");
61689
+ allErrors.push(...globalResult.errors);
61690
+ for (const skill2 of globalResult.skills) {
61691
+ skillsById.set(skill2.id, skill2);
61692
+ }
60829
61693
  }
60830
- if (agentId) {
61694
+ if (agentId && includeSource("agent")) {
60831
61695
  const agentSkillsDir = getAgentSkillsDir2(agentId);
60832
61696
  const agentResult = await discoverSkillsFromDir2(agentSkillsDir, "agent");
60833
61697
  allErrors.push(...agentResult.errors);
@@ -60835,10 +61699,12 @@ async function discoverSkills2(projectSkillsPath = join17(process.cwd(), SKILLS_
60835
61699
  skillsById.set(skill2.id, skill2);
60836
61700
  }
60837
61701
  }
60838
- const projectResult = await discoverSkillsFromDir2(projectSkillsPath, "project");
60839
- allErrors.push(...projectResult.errors);
60840
- for (const skill2 of projectResult.skills) {
60841
- skillsById.set(skill2.id, skill2);
61702
+ if (includeSource("project")) {
61703
+ const projectResult = await discoverSkillsFromDir2(projectSkillsPath, "project");
61704
+ allErrors.push(...projectResult.errors);
61705
+ for (const skill2 of projectResult.skills) {
61706
+ skillsById.set(skill2.id, skill2);
61707
+ }
60842
61708
  }
60843
61709
  return {
60844
61710
  skills: Array.from(skillsById.values()),
@@ -60849,7 +61715,7 @@ async function findSkillFiles2(currentPath, rootPath, skills, errors, source) {
60849
61715
  try {
60850
61716
  const entries = await readdir6(currentPath, { withFileTypes: true });
60851
61717
  for (const entry of entries) {
60852
- const fullPath = join17(currentPath, entry.name);
61718
+ const fullPath = join18(currentPath, entry.name);
60853
61719
  if (entry.isDirectory()) {
60854
61720
  await findSkillFiles2(fullPath, rootPath, skills, errors, source);
60855
61721
  } else if (entry.isFile() && entry.name.toUpperCase() === "SKILL.MD") {
@@ -60923,7 +61789,8 @@ ${lines.join(`
60923
61789
  }
60924
61790
  var SKILLS_DIR2 = ".skills", GLOBAL_SKILLS_DIR2;
60925
61791
  var init_skills2 = __esm(() => {
60926
- GLOBAL_SKILLS_DIR2 = join17(process.env.HOME || process.env.USERPROFILE || "~", ".letta/skills");
61792
+ init_skillSources();
61793
+ GLOBAL_SKILLS_DIR2 = join18(process.env.HOME || process.env.USERPROFILE || "~", ".letta/skills");
60927
61794
  });
60928
61795
 
60929
61796
  // src/utils/fs.ts
@@ -60937,10 +61804,10 @@ __export(exports_fs, {
60937
61804
  exists: () => exists2
60938
61805
  });
60939
61806
  import {
60940
- existsSync as existsSync9,
61807
+ existsSync as existsSync10,
60941
61808
  readFileSync as fsReadFileSync2,
60942
61809
  writeFileSync as fsWriteFileSync2,
60943
- mkdirSync as mkdirSync6
61810
+ mkdirSync as mkdirSync7
60944
61811
  } from "node:fs";
60945
61812
  import { dirname as dirname9 } from "node:path";
60946
61813
  async function readFile6(path19) {
@@ -60948,16 +61815,16 @@ async function readFile6(path19) {
60948
61815
  }
60949
61816
  async function writeFile2(path19, content) {
60950
61817
  const dir = dirname9(path19);
60951
- if (!existsSync9(dir)) {
60952
- mkdirSync6(dir, { recursive: true });
61818
+ if (!existsSync10(dir)) {
61819
+ mkdirSync7(dir, { recursive: true });
60953
61820
  }
60954
61821
  fsWriteFileSync2(path19, content, { encoding: "utf-8", flush: true });
60955
61822
  }
60956
61823
  function exists2(path19) {
60957
- return existsSync9(path19);
61824
+ return existsSync10(path19);
60958
61825
  }
60959
61826
  async function mkdir2(path19, options) {
60960
- mkdirSync6(path19, options);
61827
+ mkdirSync7(path19, options);
60961
61828
  }
60962
61829
  async function readJsonFile(path19) {
60963
61830
  const text = await readFile6(path19);
@@ -61263,21 +62130,85 @@ var init_manager4 = __esm(() => {
61263
62130
  // src/updater/auto-update.ts
61264
62131
  var exports_auto_update = {};
61265
62132
  __export(exports_auto_update, {
62133
+ resolveUpdateRegistryBaseUrl: () => resolveUpdateRegistryBaseUrl,
62134
+ resolveUpdatePackageName: () => resolveUpdatePackageName,
62135
+ resolveUpdateInstallRegistryUrl: () => resolveUpdateInstallRegistryUrl,
61266
62136
  manualUpdate: () => manualUpdate,
61267
62137
  detectPackageManager: () => detectPackageManager,
61268
62138
  checkForUpdate: () => checkForUpdate,
61269
- checkAndAutoUpdate: () => checkAndAutoUpdate
62139
+ checkAndAutoUpdate: () => checkAndAutoUpdate,
62140
+ buildLatestVersionUrl: () => buildLatestVersionUrl,
62141
+ buildInstallCommand: () => buildInstallCommand,
62142
+ buildInstallArgs: () => buildInstallArgs
61270
62143
  });
61271
- import { exec as exec2 } from "node:child_process";
62144
+ import { execFile as execFile4 } from "node:child_process";
61272
62145
  import { realpathSync as realpathSync2 } from "node:fs";
61273
62146
  import { readdir as readdir7, rm } from "node:fs/promises";
61274
- import { join as join18 } from "node:path";
62147
+ import { join as join19 } from "node:path";
61275
62148
  import { promisify as promisify4 } from "node:util";
61276
62149
  function debugLog2(...args) {
61277
62150
  if (DEBUG) {
61278
62151
  console.error("[auto-update]", ...args);
61279
62152
  }
61280
62153
  }
62154
+ function normalizeUpdatePackageName(raw) {
62155
+ if (!raw)
62156
+ return null;
62157
+ const value = raw.trim();
62158
+ if (!value)
62159
+ return null;
62160
+ if (/\s/.test(value) || /["'`;|&$]/.test(value))
62161
+ return null;
62162
+ return value;
62163
+ }
62164
+ function normalizeRegistryUrl(raw) {
62165
+ if (!raw)
62166
+ return null;
62167
+ const value = raw.trim();
62168
+ if (!value || /\s/.test(value) || /["'`;|&$]/.test(value))
62169
+ return null;
62170
+ try {
62171
+ const url = new URL(value);
62172
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
62173
+ return null;
62174
+ }
62175
+ return value.replace(/\/+$/, "");
62176
+ } catch {
62177
+ return null;
62178
+ }
62179
+ }
62180
+ function resolveUpdatePackageName(env3 = process.env) {
62181
+ const custom = normalizeUpdatePackageName(env3[UPDATE_PACKAGE_NAME_ENV]);
62182
+ if (custom) {
62183
+ return custom;
62184
+ }
62185
+ return DEFAULT_UPDATE_PACKAGE_NAME;
62186
+ }
62187
+ function resolveUpdateRegistryBaseUrl(env3 = process.env) {
62188
+ const custom = normalizeRegistryUrl(env3[UPDATE_REGISTRY_BASE_URL_ENV]);
62189
+ if (custom) {
62190
+ return custom;
62191
+ }
62192
+ return DEFAULT_UPDATE_REGISTRY_BASE_URL;
62193
+ }
62194
+ function resolveUpdateInstallRegistryUrl(env3 = process.env) {
62195
+ return normalizeRegistryUrl(env3[UPDATE_INSTALL_REGISTRY_URL_ENV]);
62196
+ }
62197
+ function buildLatestVersionUrl(packageName, registryBaseUrl) {
62198
+ return `${registryBaseUrl.replace(/\/+$/, "")}/${packageName}/latest`;
62199
+ }
62200
+ function buildInstallCommand(pm, env3 = process.env) {
62201
+ return `${pm} ${buildInstallArgs(pm, env3).join(" ")}`;
62202
+ }
62203
+ function buildInstallArgs(pm, env3 = process.env) {
62204
+ const packageName = resolveUpdatePackageName(env3);
62205
+ const installRegistry = resolveUpdateInstallRegistryUrl(env3);
62206
+ const args = [...INSTALL_ARG_PREFIX[pm], `${packageName}@latest`];
62207
+ if (installRegistry) {
62208
+ args.push("--registry", installRegistry);
62209
+ }
62210
+ return args;
62211
+ }
61281
62212
  function detectPackageManager() {
61282
62213
  const envOverride = process.env.LETTA_PACKAGE_MANAGER;
61283
62214
  if (envOverride) {
@@ -61323,9 +62254,14 @@ async function checkForUpdate() {
61323
62254
  debugLog2("Prerelease version detected, skipping auto-update check");
61324
62255
  return { updateAvailable: false, currentVersion };
61325
62256
  }
62257
+ const packageName = resolveUpdatePackageName();
62258
+ const registryBaseUrl = resolveUpdateRegistryBaseUrl();
62259
+ const latestUrl = buildLatestVersionUrl(packageName, registryBaseUrl);
61326
62260
  try {
61327
- debugLog2("Checking registry for latest version...");
61328
- const res = await fetch("https://registry.npmjs.org/@letta-ai/letta-code/latest", { signal: AbortSignal.timeout(5000) });
62261
+ debugLog2("Checking registry for latest version:", latestUrl);
62262
+ const res = await fetch(latestUrl, {
62263
+ signal: AbortSignal.timeout(5000)
62264
+ });
61329
62265
  if (!res.ok) {
61330
62266
  throw new Error(`Registry returned ${res.status}`);
61331
62267
  }
@@ -61359,19 +62295,21 @@ async function checkForUpdate() {
61359
62295
  }
61360
62296
  async function getNpmGlobalPath() {
61361
62297
  try {
61362
- const { stdout } = await execAsync("npm prefix -g", { timeout: 5000 });
62298
+ const { stdout } = await execFileAsync3("npm", ["prefix", "-g"], {
62299
+ timeout: 5000
62300
+ });
61363
62301
  return stdout.trim();
61364
62302
  } catch {
61365
62303
  return null;
61366
62304
  }
61367
62305
  }
61368
62306
  async function cleanupOrphanedDirs(globalPath) {
61369
- const lettaAiDir = join18(globalPath, "lib/node_modules/@letta-ai");
62307
+ const lettaAiDir = join19(globalPath, "lib/node_modules/@letta-ai");
61370
62308
  try {
61371
62309
  const entries = await readdir7(lettaAiDir);
61372
62310
  for (const entry of entries) {
61373
62311
  if (entry.startsWith(".letta-code-")) {
61374
- const orphanPath = join18(lettaAiDir, entry);
62312
+ const orphanPath = join19(lettaAiDir, entry);
61375
62313
  debugLog2("Cleaning orphaned temp directory:", orphanPath);
61376
62314
  await rm(orphanPath, { recursive: true, force: true });
61377
62315
  }
@@ -61380,7 +62318,8 @@ async function cleanupOrphanedDirs(globalPath) {
61380
62318
  }
61381
62319
  async function performUpdate() {
61382
62320
  const pm = detectPackageManager();
61383
- const installCmd = INSTALL_CMD[pm];
62321
+ const installCmd = buildInstallCommand(pm);
62322
+ const installArgs = buildInstallArgs(pm);
61384
62323
  debugLog2("Detected package manager:", pm);
61385
62324
  debugLog2("Install command:", installCmd);
61386
62325
  let globalPath = null;
@@ -61393,7 +62332,7 @@ async function performUpdate() {
61393
62332
  }
61394
62333
  try {
61395
62334
  debugLog2(`Running ${installCmd}...`);
61396
- await execAsync(installCmd, { timeout: 60000 });
62335
+ await execFileAsync3(pm, installArgs, { timeout: 60000 });
61397
62336
  debugLog2("Update completed successfully");
61398
62337
  return { success: true };
61399
62338
  } catch (error) {
@@ -61402,7 +62341,7 @@ async function performUpdate() {
61402
62341
  debugLog2("ENOTEMPTY detected, attempting cleanup and retry...");
61403
62342
  await cleanupOrphanedDirs(globalPath);
61404
62343
  try {
61405
- await execAsync(installCmd, { timeout: 60000 });
62344
+ await execFileAsync3(pm, installArgs, { timeout: 60000 });
61406
62345
  debugLog2("Update succeeded after cleanup retry");
61407
62346
  return { success: true };
61408
62347
  } catch (retryError) {
@@ -61476,17 +62415,17 @@ async function manualUpdate() {
61476
62415
  message: `Update failed: ${updateResult.error}`
61477
62416
  };
61478
62417
  }
61479
- var execAsync, DEBUG, INSTALL_CMD, VALID_PACKAGE_MANAGERS;
62418
+ var execFileAsync3, DEBUG, DEFAULT_UPDATE_PACKAGE_NAME = "@letta-ai/letta-code", DEFAULT_UPDATE_REGISTRY_BASE_URL = "https://registry.npmjs.org", UPDATE_PACKAGE_NAME_ENV = "LETTA_UPDATE_PACKAGE_NAME", UPDATE_REGISTRY_BASE_URL_ENV = "LETTA_UPDATE_REGISTRY_BASE_URL", UPDATE_INSTALL_REGISTRY_URL_ENV = "LETTA_UPDATE_INSTALL_REGISTRY_URL", INSTALL_ARG_PREFIX, VALID_PACKAGE_MANAGERS;
61480
62419
  var init_auto_update = __esm(() => {
61481
62420
  init_version();
61482
- execAsync = promisify4(exec2);
62421
+ execFileAsync3 = promisify4(execFile4);
61483
62422
  DEBUG = process.env.LETTA_DEBUG_AUTOUPDATE === "1";
61484
- INSTALL_CMD = {
61485
- npm: "npm install -g @letta-ai/letta-code@latest",
61486
- bun: "bun add -g @letta-ai/letta-code@latest",
61487
- pnpm: "pnpm add -g @letta-ai/letta-code@latest"
62423
+ INSTALL_ARG_PREFIX = {
62424
+ npm: ["install", "-g"],
62425
+ bun: ["add", "-g"],
62426
+ pnpm: ["add", "-g"]
61488
62427
  };
61489
- VALID_PACKAGE_MANAGERS = new Set(Object.keys(INSTALL_CMD));
62428
+ VALID_PACKAGE_MANAGERS = new Set(Object.keys(INSTALL_ARG_PREFIX));
61490
62429
  });
61491
62430
 
61492
62431
  // src/tools/impl/overflow.ts
@@ -61726,9 +62665,9 @@ __export(exports_subagents2, {
61726
62665
  GLOBAL_AGENTS_DIR: () => GLOBAL_AGENTS_DIR2,
61727
62666
  AGENTS_DIR: () => AGENTS_DIR2
61728
62667
  });
61729
- import { existsSync as existsSync11 } from "node:fs";
62668
+ import { existsSync as existsSync12 } from "node:fs";
61730
62669
  import { readdir as readdir8, readFile as readFile7 } from "node:fs/promises";
61731
- import { join as join20 } from "node:path";
62670
+ import { join as join21 } from "node:path";
61732
62671
  function isValidName2(name) {
61733
62672
  return /^[a-z][a-z0-9-]*$/.test(name);
61734
62673
  }
@@ -61810,7 +62749,7 @@ function getBuiltinSubagentNames2() {
61810
62749
  return new Set(Object.keys(getBuiltinSubagents2()));
61811
62750
  }
61812
62751
  async function discoverSubagentsFromDir2(agentsDir, seenNames, subagents, errors) {
61813
- if (!existsSync11(agentsDir)) {
62752
+ if (!existsSync12(agentsDir)) {
61814
62753
  return;
61815
62754
  }
61816
62755
  try {
@@ -61819,7 +62758,7 @@ async function discoverSubagentsFromDir2(agentsDir, seenNames, subagents, errors
61819
62758
  if (!entry.isFile() || !entry.name.endsWith(".md")) {
61820
62759
  continue;
61821
62760
  }
61822
- const filePath = join20(agentsDir, entry.name);
62761
+ const filePath = join21(agentsDir, entry.name);
61823
62762
  try {
61824
62763
  const config = await parseSubagentFile2(filePath);
61825
62764
  if (config) {
@@ -61851,7 +62790,7 @@ async function discoverSubagents2(workingDirectory = process.cwd()) {
61851
62790
  const subagents = [];
61852
62791
  const seenNames = new Set;
61853
62792
  await discoverSubagentsFromDir2(GLOBAL_AGENTS_DIR2, seenNames, subagents, errors);
61854
- const projectAgentsDir = join20(workingDirectory, AGENTS_DIR2);
62793
+ const projectAgentsDir = join21(workingDirectory, AGENTS_DIR2);
61855
62794
  await discoverSubagentsFromDir2(projectAgentsDir, seenNames, subagents, errors);
61856
62795
  return { subagents, errors };
61857
62796
  }
@@ -61894,7 +62833,7 @@ var init_subagents2 = __esm(() => {
61894
62833
  recall_default,
61895
62834
  reflection_default
61896
62835
  ];
61897
- GLOBAL_AGENTS_DIR2 = join20(process.env.HOME || process.env.USERPROFILE || "~", ".letta/agents");
62836
+ GLOBAL_AGENTS_DIR2 = join21(process.env.HOME || process.env.USERPROFILE || "~", ".letta/agents");
61898
62837
  VALID_MEMORY_BLOCKS2 = new Set(MEMORY_BLOCK_LABELS);
61899
62838
  cache5 = {
61900
62839
  builtins: null,
@@ -62038,24 +62977,24 @@ function defineLazyProperty(object, propertyName, valueGetter) {
62038
62977
  // node_modules/default-browser-id/index.js
62039
62978
  import { promisify as promisify5 } from "node:util";
62040
62979
  import process15 from "node:process";
62041
- import { execFile as execFile4 } from "node:child_process";
62980
+ import { execFile as execFile5 } from "node:child_process";
62042
62981
  async function defaultBrowserId() {
62043
62982
  if (process15.platform !== "darwin") {
62044
62983
  throw new Error("macOS only");
62045
62984
  }
62046
- const { stdout } = await execFileAsync3("defaults", ["read", "com.apple.LaunchServices/com.apple.launchservices.secure", "LSHandlers"]);
62985
+ const { stdout } = await execFileAsync4("defaults", ["read", "com.apple.LaunchServices/com.apple.launchservices.secure", "LSHandlers"]);
62047
62986
  const match3 = /LSHandlerRoleAll = "(?!-)(?<id>[^"]+?)";\s+?LSHandlerURLScheme = (?:http|https);/.exec(stdout);
62048
62987
  return match3?.groups.id ?? "com.apple.Safari";
62049
62988
  }
62050
- var execFileAsync3;
62989
+ var execFileAsync4;
62051
62990
  var init_default_browser_id = __esm(() => {
62052
- execFileAsync3 = promisify5(execFile4);
62991
+ execFileAsync4 = promisify5(execFile5);
62053
62992
  });
62054
62993
 
62055
62994
  // node_modules/run-applescript/index.js
62056
62995
  import process16 from "node:process";
62057
62996
  import { promisify as promisify6 } from "node:util";
62058
- import { execFile as execFile5, execFileSync } from "node:child_process";
62997
+ import { execFile as execFile6, execFileSync } from "node:child_process";
62059
62998
  async function runAppleScript(script, { humanReadableOutput = true, signal } = {}) {
62060
62999
  if (process16.platform !== "darwin") {
62061
63000
  throw new Error("macOS only");
@@ -62065,12 +63004,12 @@ async function runAppleScript(script, { humanReadableOutput = true, signal } = {
62065
63004
  if (signal) {
62066
63005
  execOptions.signal = signal;
62067
63006
  }
62068
- const { stdout } = await execFileAsync4("osascript", ["-e", script, outputArguments], execOptions);
63007
+ const { stdout } = await execFileAsync5("osascript", ["-e", script, outputArguments], execOptions);
62069
63008
  return stdout.trim();
62070
63009
  }
62071
- var execFileAsync4;
63010
+ var execFileAsync5;
62072
63011
  var init_run_applescript = __esm(() => {
62073
- execFileAsync4 = promisify6(execFile5);
63012
+ execFileAsync5 = promisify6(execFile6);
62074
63013
  });
62075
63014
 
62076
63015
  // node_modules/bundle-name/index.js
@@ -62084,8 +63023,8 @@ var init_bundle_name = __esm(() => {
62084
63023
 
62085
63024
  // node_modules/default-browser/windows.js
62086
63025
  import { promisify as promisify7 } from "node:util";
62087
- import { execFile as execFile6 } from "node:child_process";
62088
- async function defaultBrowser(_execFileAsync = execFileAsync5) {
63026
+ import { execFile as execFile7 } from "node:child_process";
63027
+ async function defaultBrowser(_execFileAsync = execFileAsync6) {
62089
63028
  const { stdout } = await _execFileAsync("reg", [
62090
63029
  "QUERY",
62091
63030
  " HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\http\\UserChoice",
@@ -62103,9 +63042,9 @@ async function defaultBrowser(_execFileAsync = execFileAsync5) {
62103
63042
  }
62104
63043
  return browser;
62105
63044
  }
62106
- var execFileAsync5, windowsBrowserProgIds, UnknownBrowserError;
63045
+ var execFileAsync6, windowsBrowserProgIds, UnknownBrowserError;
62107
63046
  var init_windows = __esm(() => {
62108
- execFileAsync5 = promisify7(execFile6);
63047
+ execFileAsync6 = promisify7(execFile7);
62109
63048
  windowsBrowserProgIds = {
62110
63049
  AppXq0fevzme2pys62n3e0fbqa7peapykr8v: { name: "Edge", id: "com.microsoft.edge.old" },
62111
63050
  MSEdgeDHTML: { name: "Edge", id: "com.microsoft.edge" },
@@ -62124,7 +63063,7 @@ var init_windows = __esm(() => {
62124
63063
  // node_modules/default-browser/index.js
62125
63064
  import { promisify as promisify8 } from "node:util";
62126
63065
  import process17 from "node:process";
62127
- import { execFile as execFile7 } from "node:child_process";
63066
+ import { execFile as execFile8 } from "node:child_process";
62128
63067
  async function defaultBrowser2() {
62129
63068
  if (process17.platform === "darwin") {
62130
63069
  const id = await defaultBrowserId();
@@ -62132,7 +63071,7 @@ async function defaultBrowser2() {
62132
63071
  return { name, id };
62133
63072
  }
62134
63073
  if (process17.platform === "linux") {
62135
- const { stdout } = await execFileAsync6("xdg-mime", ["query", "default", "x-scheme-handler/http"]);
63074
+ const { stdout } = await execFileAsync7("xdg-mime", ["query", "default", "x-scheme-handler/http"]);
62136
63075
  const id = stdout.trim();
62137
63076
  const name = titleize(id.replace(/.desktop$/, "").replace("-", " "));
62138
63077
  return { name, id };
@@ -62142,12 +63081,12 @@ async function defaultBrowser2() {
62142
63081
  }
62143
63082
  throw new Error("Only macOS, Linux, and Windows are supported");
62144
63083
  }
62145
- var execFileAsync6, titleize = (string) => string.toLowerCase().replaceAll(/(?:^|\s|-)\S/g, (x) => x.toUpperCase());
63084
+ var execFileAsync7, titleize = (string) => string.toLowerCase().replaceAll(/(?:^|\s|-)\S/g, (x) => x.toUpperCase());
62146
63085
  var init_default_browser = __esm(() => {
62147
63086
  init_default_browser_id();
62148
63087
  init_bundle_name();
62149
63088
  init_windows();
62150
- execFileAsync6 = promisify8(execFile7);
63089
+ execFileAsync7 = promisify8(execFile8);
62151
63090
  });
62152
63091
 
62153
63092
  // node_modules/open/index.js
@@ -62168,7 +63107,7 @@ async function getWindowsDefaultBrowserFromWsl() {
62168
63107
  const powershellPath = await powerShellPath();
62169
63108
  const rawCommand = String.raw`(Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice").ProgId`;
62170
63109
  const encodedCommand = Buffer3.from(rawCommand, "utf16le").toString("base64");
62171
- const { stdout } = await execFile8(powershellPath, [
63110
+ const { stdout } = await execFile9(powershellPath, [
62172
63111
  "-NoProfile",
62173
63112
  "-NonInteractive",
62174
63113
  "-ExecutionPolicy",
@@ -62204,7 +63143,7 @@ function detectPlatformBinary({ [platform2]: platformBinary }, { wsl }) {
62204
63143
  }
62205
63144
  return detectArchBinary(platformBinary);
62206
63145
  }
62207
- var execFile8, __dirname2, localXdgOpenPath, platform2, arch, pTryEach = async (array, mapper) => {
63146
+ var execFile9, __dirname2, localXdgOpenPath, platform2, arch, pTryEach = async (array, mapper) => {
62208
63147
  let latestError;
62209
63148
  for (const item of array) {
62210
63149
  try {
@@ -62383,7 +63322,7 @@ var init_open = __esm(() => {
62383
63322
  init_wsl_utils();
62384
63323
  init_default_browser();
62385
63324
  init_is_inside_container();
62386
- execFile8 = promisify9(childProcess.execFile);
63325
+ execFile9 = promisify9(childProcess.execFile);
62387
63326
  __dirname2 = path21.dirname(fileURLToPath8(import.meta.url));
62388
63327
  localXdgOpenPath = path21.join(__dirname2, "xdg-open");
62389
63328
  ({ platform: platform2, arch } = process18);
@@ -62803,6 +63742,58 @@ function isConversationBusyError(detail) {
62803
63742
  return false;
62804
63743
  return detail.toLowerCase().includes(CONVERSATION_BUSY_DETAIL_FRAGMENT);
62805
63744
  }
63745
+ function isRetryableProviderErrorDetail(detail) {
63746
+ if (typeof detail !== "string")
63747
+ return false;
63748
+ return RETRYABLE_PROVIDER_DETAIL_PATTERNS.some((pattern) => detail.includes(pattern));
63749
+ }
63750
+ function isNonRetryableProviderErrorDetail(detail) {
63751
+ if (typeof detail !== "string")
63752
+ return false;
63753
+ const normalized = detail.toLowerCase();
63754
+ if (NON_RETRYABLE_4XX_PATTERN.test(detail))
63755
+ return true;
63756
+ return NON_RETRYABLE_PROVIDER_DETAIL_PATTERNS.some((pattern) => normalized.includes(pattern));
63757
+ }
63758
+ function shouldRetryRunMetadataError(errorType, detail) {
63759
+ const explicitLlmError = errorType === "llm_error";
63760
+ const retryable429Detail = typeof detail === "string" && RETRYABLE_429_PATTERN.test(detail);
63761
+ const retryableDetail = isRetryableProviderErrorDetail(detail);
63762
+ const nonRetryableDetail = isNonRetryableProviderErrorDetail(detail);
63763
+ if (nonRetryableDetail && !retryable429Detail)
63764
+ return false;
63765
+ if (explicitLlmError)
63766
+ return true;
63767
+ return retryable429Detail || retryableDetail;
63768
+ }
63769
+ function shouldRetryPreStreamTransientError(opts) {
63770
+ const { status, detail } = opts;
63771
+ if (status === 429)
63772
+ return true;
63773
+ if (status !== undefined && status >= 500)
63774
+ return true;
63775
+ if (status !== undefined && status >= 400)
63776
+ return false;
63777
+ const retryable429Detail = typeof detail === "string" && RETRYABLE_429_PATTERN.test(detail);
63778
+ if (retryable429Detail)
63779
+ return true;
63780
+ if (isNonRetryableProviderErrorDetail(detail))
63781
+ return false;
63782
+ return isRetryableProviderErrorDetail(detail);
63783
+ }
63784
+ function parseRetryAfterHeaderMs(retryAfterValue) {
63785
+ if (!retryAfterValue)
63786
+ return null;
63787
+ const seconds = Number(retryAfterValue);
63788
+ if (Number.isFinite(seconds) && seconds >= 0) {
63789
+ return Math.round(seconds * 1000);
63790
+ }
63791
+ const retryAtMs = Date.parse(retryAfterValue);
63792
+ if (Number.isNaN(retryAtMs))
63793
+ return null;
63794
+ const delayMs = retryAtMs - Date.now();
63795
+ return delayMs > 0 ? delayMs : 0;
63796
+ }
62806
63797
  function classifyPreStreamConflict(detail) {
62807
63798
  if (isApprovalPendingError(detail))
62808
63799
  return "approval_pending";
@@ -62810,7 +63801,7 @@ function classifyPreStreamConflict(detail) {
62810
63801
  return "conversation_busy";
62811
63802
  return null;
62812
63803
  }
62813
- function getPreStreamErrorAction(detail, conversationBusyRetries, maxConversationBusyRetries) {
63804
+ function getPreStreamErrorAction(detail, conversationBusyRetries, maxConversationBusyRetries, opts) {
62814
63805
  const kind = classifyPreStreamConflict(detail);
62815
63806
  if (kind === "approval_pending") {
62816
63807
  return "resolve_approval_pending";
@@ -62818,6 +63809,9 @@ function getPreStreamErrorAction(detail, conversationBusyRetries, maxConversatio
62818
63809
  if (kind === "conversation_busy" && conversationBusyRetries < maxConversationBusyRetries) {
62819
63810
  return "retry_conversation_busy";
62820
63811
  }
63812
+ if (opts && shouldRetryPreStreamTransientError({ status: opts.status, detail }) && (opts.transientRetries ?? 0) < (opts.maxTransientRetries ?? 0)) {
63813
+ return "retry_transient";
63814
+ }
62821
63815
  return "rethrow";
62822
63816
  }
62823
63817
  function extractConflictDetail(error) {
@@ -62861,7 +63855,42 @@ function rebuildInputWithFreshDenials(currentInput, serverApprovals, denialReaso
62861
63855
  function shouldAttemptApprovalRecovery(opts) {
62862
63856
  return opts.approvalPendingDetected && opts.retries < opts.maxRetries;
62863
63857
  }
62864
- var INVALID_TOOL_CALL_IDS_FRAGMENT = "invalid tool call ids", APPROVAL_PENDING_DETAIL_FRAGMENT = "waiting for approval", CONVERSATION_BUSY_DETAIL_FRAGMENT = "another request is currently being processed";
63858
+ var INVALID_TOOL_CALL_IDS_FRAGMENT = "invalid tool call ids", APPROVAL_PENDING_DETAIL_FRAGMENT = "waiting for approval", CONVERSATION_BUSY_DETAIL_FRAGMENT = "another request is currently being processed", RETRYABLE_PROVIDER_DETAIL_PATTERNS, NON_RETRYABLE_PROVIDER_DETAIL_PATTERNS, NON_RETRYABLE_4XX_PATTERN, RETRYABLE_429_PATTERN;
63859
+ var init_turn_recovery_policy = __esm(() => {
63860
+ RETRYABLE_PROVIDER_DETAIL_PATTERNS = [
63861
+ "Anthropic API error",
63862
+ "OpenAI API error",
63863
+ "Google Vertex API error",
63864
+ "ChatGPT API error",
63865
+ "ChatGPT server error",
63866
+ "Connection error during Anthropic streaming",
63867
+ "Connection error during streaming",
63868
+ "upstream connect error",
63869
+ "connection termination",
63870
+ "peer closed connection",
63871
+ "incomplete chunked read",
63872
+ "Network error",
63873
+ "Connection error",
63874
+ "Request timed out",
63875
+ "overloaded",
63876
+ "api_error"
63877
+ ];
63878
+ NON_RETRYABLE_PROVIDER_DETAIL_PATTERNS = [
63879
+ "invalid api key",
63880
+ "incorrect api key",
63881
+ "authentication error",
63882
+ "unauthorized",
63883
+ "permission denied",
63884
+ "forbidden",
63885
+ "invalid_request_error",
63886
+ "invalid model",
63887
+ "model_not_found",
63888
+ "context_length_exceeded",
63889
+ "invalid_encrypted_content"
63890
+ ];
63891
+ NON_RETRYABLE_4XX_PATTERN = /Error code:\s*4(0[0-8]|1\d|2\d|3\d|4\d|51)/i;
63892
+ RETRYABLE_429_PATTERN = /Error code:\s*429|rate limit|too many requests/i;
63893
+ });
62865
63894
 
62866
63895
  // src/agent/approval-recovery.ts
62867
63896
  async function fetchRunErrorDetail(runId) {
@@ -62877,336 +63906,10 @@ async function fetchRunErrorDetail(runId) {
62877
63906
  }
62878
63907
  }
62879
63908
  var init_approval_recovery = __esm(async () => {
63909
+ init_turn_recovery_policy();
62880
63910
  await init_client2();
62881
63911
  });
62882
63912
 
62883
- // src/providers/openai-codex-provider.ts
62884
- var exports_openai_codex_provider = {};
62885
- __export(exports_openai_codex_provider, {
62886
- updateOpenAICodexProvider: () => updateOpenAICodexProvider,
62887
- removeOpenAICodexProvider: () => removeOpenAICodexProvider,
62888
- listProviders: () => listProviders,
62889
- getOpenAICodexProvider: () => getOpenAICodexProvider,
62890
- deleteOpenAICodexProvider: () => deleteOpenAICodexProvider,
62891
- createOrUpdateOpenAICodexProvider: () => createOrUpdateOpenAICodexProvider,
62892
- createOpenAICodexProvider: () => createOpenAICodexProvider,
62893
- checkOpenAICodexEligibility: () => checkOpenAICodexEligibility,
62894
- OPENAI_CODEX_PROVIDER_NAME: () => OPENAI_CODEX_PROVIDER_NAME,
62895
- CHATGPT_OAUTH_PROVIDER_TYPE: () => CHATGPT_OAUTH_PROVIDER_TYPE
62896
- });
62897
- async function getLettaConfig() {
62898
- const settings = await settingsManager.getSettingsWithSecureTokens();
62899
- const baseUrl = process.env.LETTA_BASE_URL || settings.env?.LETTA_BASE_URL || LETTA_CLOUD_API_URL;
62900
- const apiKey = process.env.LETTA_API_KEY || settings.env?.LETTA_API_KEY || "";
62901
- return { baseUrl, apiKey };
62902
- }
62903
- async function providersRequest(method, path22, body) {
62904
- const { baseUrl, apiKey } = await getLettaConfig();
62905
- const url = `${baseUrl}${path22}`;
62906
- const response = await fetch(url, {
62907
- method,
62908
- headers: getLettaCodeHeaders2(apiKey),
62909
- ...body && { body: JSON.stringify(body) }
62910
- });
62911
- if (!response.ok) {
62912
- const errorText = await response.text();
62913
- if (response.status === 403) {
62914
- try {
62915
- const errorData = JSON.parse(errorText);
62916
- if (errorData.error && typeof errorData.error === "string" && errorData.error.includes("only available for pro or enterprise")) {
62917
- throw new Error("PLAN_UPGRADE_REQUIRED");
62918
- }
62919
- } catch (parseError) {
62920
- if (parseError instanceof Error && parseError.message === "PLAN_UPGRADE_REQUIRED") {
62921
- throw parseError;
62922
- }
62923
- }
62924
- }
62925
- throw new Error(`Provider API error (${response.status}): ${errorText}`);
62926
- }
62927
- const text = await response.text();
62928
- if (!text) {
62929
- return {};
62930
- }
62931
- return JSON.parse(text);
62932
- }
62933
- async function listProviders() {
62934
- try {
62935
- const response = await providersRequest("GET", "/v1/providers");
62936
- return response;
62937
- } catch {
62938
- return [];
62939
- }
62940
- }
62941
- async function getOpenAICodexProvider() {
62942
- const providers = await listProviders();
62943
- return providers.find((p) => p.name === OPENAI_CODEX_PROVIDER_NAME) || null;
62944
- }
62945
- async function createOpenAICodexProvider(config) {
62946
- const apiKeyJson = JSON.stringify({
62947
- access_token: config.access_token,
62948
- id_token: config.id_token,
62949
- refresh_token: config.refresh_token,
62950
- account_id: config.account_id,
62951
- expires_at: config.expires_at
62952
- });
62953
- return providersRequest("POST", "/v1/providers", {
62954
- name: OPENAI_CODEX_PROVIDER_NAME,
62955
- provider_type: CHATGPT_OAUTH_PROVIDER_TYPE,
62956
- api_key: apiKeyJson
62957
- });
62958
- }
62959
- async function updateOpenAICodexProvider(providerId, config) {
62960
- const apiKeyJson = JSON.stringify({
62961
- access_token: config.access_token,
62962
- id_token: config.id_token,
62963
- refresh_token: config.refresh_token,
62964
- account_id: config.account_id,
62965
- expires_at: config.expires_at
62966
- });
62967
- return providersRequest("PATCH", `/v1/providers/${providerId}`, {
62968
- api_key: apiKeyJson
62969
- });
62970
- }
62971
- async function deleteOpenAICodexProvider(providerId) {
62972
- await providersRequest("DELETE", `/v1/providers/${providerId}`);
62973
- }
62974
- async function createOrUpdateOpenAICodexProvider(config) {
62975
- const existing = await getOpenAICodexProvider();
62976
- if (existing) {
62977
- return updateOpenAICodexProvider(existing.id, config);
62978
- } else {
62979
- return createOpenAICodexProvider(config);
62980
- }
62981
- }
62982
- async function removeOpenAICodexProvider() {
62983
- const existing = await getOpenAICodexProvider();
62984
- if (existing) {
62985
- await deleteOpenAICodexProvider(existing.id);
62986
- }
62987
- }
62988
- async function checkOpenAICodexEligibility() {
62989
- try {
62990
- const balance = await providersRequest("GET", "/v1/metadata/balance");
62991
- const billingTier = balance.billing_tier.toLowerCase();
62992
- if (billingTier === "pro" || billingTier === "enterprise") {
62993
- return {
62994
- eligible: true,
62995
- billing_tier: balance.billing_tier
62996
- };
62997
- }
62998
- return {
62999
- eligible: false,
63000
- billing_tier: balance.billing_tier,
63001
- reason: `ChatGPT OAuth requires a Pro or Enterprise plan. Current plan: ${balance.billing_tier}`
63002
- };
63003
- } catch (error) {
63004
- console.warn("Failed to check ChatGPT OAuth eligibility:", error);
63005
- return {
63006
- eligible: true,
63007
- billing_tier: "unknown"
63008
- };
63009
- }
63010
- }
63011
- var OPENAI_CODEX_PROVIDER_NAME = "chatgpt-plus-pro", CHATGPT_OAUTH_PROVIDER_TYPE = "chatgpt_oauth";
63012
- var init_openai_codex_provider = __esm(async () => {
63013
- init_http_headers();
63014
- init_oauth();
63015
- await init_settings_manager();
63016
- });
63017
-
63018
- // src/agent/modify.ts
63019
- var exports_modify = {};
63020
- __export(exports_modify, {
63021
- updateAgentSystemPromptRaw: () => updateAgentSystemPromptRaw,
63022
- updateAgentSystemPromptMemfs: () => updateAgentSystemPromptMemfs,
63023
- updateAgentSystemPrompt: () => updateAgentSystemPrompt,
63024
- updateAgentLLMConfig: () => updateAgentLLMConfig
63025
- });
63026
- function buildModelSettings(modelHandle, updateArgs) {
63027
- const isOpenAI = modelHandle.startsWith("openai/") || modelHandle.startsWith(`${OPENAI_CODEX_PROVIDER_NAME}/`);
63028
- const isAnthropic = modelHandle.startsWith("anthropic/") || modelHandle.startsWith("claude-pro-max/");
63029
- const isZai = modelHandle.startsWith("zai/");
63030
- const isGoogleAI = modelHandle.startsWith("google_ai/");
63031
- const isGoogleVertex = modelHandle.startsWith("google_vertex/");
63032
- const isOpenRouter = modelHandle.startsWith("openrouter/");
63033
- const isBedrock = modelHandle.startsWith("bedrock/");
63034
- let settings;
63035
- if (isOpenAI || isOpenRouter) {
63036
- const openaiSettings = {
63037
- provider_type: "openai",
63038
- parallel_tool_calls: true
63039
- };
63040
- if (updateArgs?.reasoning_effort) {
63041
- openaiSettings.reasoning = {
63042
- reasoning_effort: updateArgs.reasoning_effort
63043
- };
63044
- }
63045
- settings = openaiSettings;
63046
- } else if (isAnthropic) {
63047
- const anthropicSettings = {
63048
- provider_type: "anthropic",
63049
- parallel_tool_calls: true
63050
- };
63051
- if (updateArgs?.enable_reasoner !== undefined || typeof updateArgs?.max_reasoning_tokens === "number") {
63052
- anthropicSettings.thinking = {
63053
- type: updateArgs?.enable_reasoner === false ? "disabled" : "enabled",
63054
- ...typeof updateArgs?.max_reasoning_tokens === "number" && {
63055
- budget_tokens: updateArgs.max_reasoning_tokens
63056
- }
63057
- };
63058
- }
63059
- settings = anthropicSettings;
63060
- } else if (isZai) {
63061
- settings = {
63062
- provider_type: "zai",
63063
- parallel_tool_calls: true
63064
- };
63065
- } else if (isGoogleAI) {
63066
- const googleSettings = {
63067
- provider_type: "google_ai",
63068
- parallel_tool_calls: true
63069
- };
63070
- if (updateArgs?.thinking_budget !== undefined) {
63071
- googleSettings.thinking_config = {
63072
- thinking_budget: updateArgs.thinking_budget
63073
- };
63074
- }
63075
- if (typeof updateArgs?.temperature === "number") {
63076
- googleSettings.temperature = updateArgs.temperature;
63077
- }
63078
- settings = googleSettings;
63079
- } else if (isGoogleVertex) {
63080
- const googleVertexSettings = {
63081
- provider_type: "google_vertex",
63082
- parallel_tool_calls: true
63083
- };
63084
- if (updateArgs?.thinking_budget !== undefined) {
63085
- googleVertexSettings.thinking_config = {
63086
- thinking_budget: updateArgs.thinking_budget
63087
- };
63088
- }
63089
- if (typeof updateArgs?.temperature === "number") {
63090
- googleVertexSettings.temperature = updateArgs.temperature;
63091
- }
63092
- settings = googleVertexSettings;
63093
- } else if (isBedrock) {
63094
- const bedrockSettings = {
63095
- provider_type: "bedrock",
63096
- parallel_tool_calls: true
63097
- };
63098
- if (updateArgs?.enable_reasoner !== undefined || typeof updateArgs?.max_reasoning_tokens === "number") {
63099
- bedrockSettings.thinking = {
63100
- type: updateArgs?.enable_reasoner === false ? "disabled" : "enabled",
63101
- ...typeof updateArgs?.max_reasoning_tokens === "number" && {
63102
- budget_tokens: updateArgs.max_reasoning_tokens
63103
- }
63104
- };
63105
- }
63106
- settings = bedrockSettings;
63107
- } else {
63108
- settings = {};
63109
- }
63110
- if (typeof updateArgs?.max_output_tokens === "number" && "provider_type" in settings) {
63111
- settings.max_output_tokens = updateArgs.max_output_tokens;
63112
- }
63113
- return settings;
63114
- }
63115
- async function updateAgentLLMConfig(agentId, modelHandle, updateArgs) {
63116
- const client = await getClient2();
63117
- const modelSettings = buildModelSettings(modelHandle, updateArgs);
63118
- const contextWindow = updateArgs?.context_window ?? await getModelContextWindow(modelHandle);
63119
- const hasModelSettings = Object.keys(modelSettings).length > 0;
63120
- await client.agents.update(agentId, {
63121
- model: modelHandle,
63122
- ...hasModelSettings && { model_settings: modelSettings },
63123
- ...contextWindow && { context_window_limit: contextWindow },
63124
- ...typeof updateArgs?.max_output_tokens === "number" && {
63125
- max_tokens: updateArgs.max_output_tokens
63126
- }
63127
- });
63128
- const finalAgent = await client.agents.retrieve(agentId);
63129
- return finalAgent.llm_config;
63130
- }
63131
- async function updateAgentSystemPromptRaw(agentId, systemPromptContent) {
63132
- try {
63133
- const client = await getClient2();
63134
- await client.agents.update(agentId, {
63135
- system: systemPromptContent
63136
- });
63137
- return {
63138
- success: true,
63139
- message: "System prompt updated successfully"
63140
- };
63141
- } catch (error) {
63142
- return {
63143
- success: false,
63144
- message: `Failed to update system prompt: ${error instanceof Error ? error.message : String(error)}`
63145
- };
63146
- }
63147
- }
63148
- async function updateAgentSystemPrompt(agentId, systemPromptId) {
63149
- try {
63150
- const { resolveSystemPrompt: resolveSystemPrompt3, SYSTEM_PROMPT_MEMORY_ADDON: SYSTEM_PROMPT_MEMORY_ADDON3 } = await Promise.resolve().then(() => (init_promptAssets(), exports_promptAssets));
63151
- const baseContent = await resolveSystemPrompt3(systemPromptId);
63152
- const systemPromptContent = `${baseContent}
63153
- ${SYSTEM_PROMPT_MEMORY_ADDON3}`;
63154
- const updateResult = await updateAgentSystemPromptRaw(agentId, systemPromptContent);
63155
- if (!updateResult.success) {
63156
- return {
63157
- success: false,
63158
- message: updateResult.message,
63159
- agent: null
63160
- };
63161
- }
63162
- const client = await getClient2();
63163
- const agent = await client.agents.retrieve(agentId);
63164
- return {
63165
- success: true,
63166
- message: "System prompt applied successfully",
63167
- agent
63168
- };
63169
- } catch (error) {
63170
- return {
63171
- success: false,
63172
- message: `Failed to apply system prompt: ${error instanceof Error ? error.message : String(error)}`,
63173
- agent: null
63174
- };
63175
- }
63176
- }
63177
- async function updateAgentSystemPromptMemfs(agentId, enableMemfs) {
63178
- try {
63179
- const client = await getClient2();
63180
- const agent = await client.agents.retrieve(agentId);
63181
- let currentSystemPrompt = agent.system || "";
63182
- const { SYSTEM_PROMPT_MEMFS_ADDON: SYSTEM_PROMPT_MEMFS_ADDON3, SYSTEM_PROMPT_MEMORY_ADDON: SYSTEM_PROMPT_MEMORY_ADDON3 } = await Promise.resolve().then(() => (init_promptAssets(), exports_promptAssets));
63183
- const memoryHeaderRegex = /\n#{1,2} Memory\b[\s\S]*?(?=\n#{1,2} (?!Memory|Filesystem|Structure|How It Works|Syncing|History)[^\n]|$)/;
63184
- currentSystemPrompt = currentSystemPrompt.replace(memoryHeaderRegex, "");
63185
- const addon = enableMemfs ? SYSTEM_PROMPT_MEMFS_ADDON3 : SYSTEM_PROMPT_MEMORY_ADDON3;
63186
- currentSystemPrompt = `${currentSystemPrompt}
63187
- ${addon}`;
63188
- await client.agents.update(agentId, {
63189
- system: currentSystemPrompt
63190
- });
63191
- return {
63192
- success: true,
63193
- message: enableMemfs ? "System prompt updated to include Memory Filesystem section" : "System prompt updated to include standard Memory section"
63194
- };
63195
- } catch (error) {
63196
- return {
63197
- success: false,
63198
- message: `Failed to update system prompt memfs: ${error instanceof Error ? error.message : String(error)}`
63199
- };
63200
- }
63201
- }
63202
- var init_modify = __esm(async () => {
63203
- await __promiseAll([
63204
- init_openai_codex_provider(),
63205
- init_available_models(),
63206
- init_client2()
63207
- ]);
63208
- });
63209
-
63210
63913
  // src/agent/prompts/sleeptime.ts
63211
63914
  var SLEEPTIME_MEMORY_PERSONA = `I am a sleep-time memory management agent. I observe the conversation between the user and their primary agent, then actively maintain memory blocks to keep them accurate, concise, and useful.
63212
63915
 
@@ -63364,8 +64067,7 @@ async function createAgent(nameOrOptions = DEFAULT_AGENT_NAME, model, embeddingM
63364
64067
  } else {
63365
64068
  systemPromptContent = await resolveSystemPrompt(options.systemPromptPreset);
63366
64069
  }
63367
- systemPromptContent = `${systemPromptContent}
63368
- ${SYSTEM_PROMPT_MEMORY_ADDON}`;
64070
+ systemPromptContent = reconcileMemoryPrompt(systemPromptContent, options.memoryPromptMode ?? "standard");
63369
64071
  if (options.systemPromptAppend) {
63370
64072
  systemPromptContent = `${systemPromptContent}
63371
64073
 
@@ -63435,6 +64137,7 @@ ${options.systemPromptAppend}`;
63435
64137
  var init_create = __esm(async () => {
63436
64138
  init_constants();
63437
64139
  init_memory();
64140
+ init_memoryPrompt();
63438
64141
  init_model();
63439
64142
  init_promptAssets();
63440
64143
  await __promiseAll([
@@ -64025,163 +64728,6 @@ var init_interactivePolicy = __esm(() => {
64025
64728
  HEADLESS_AUTO_ALLOW_TOOLS = new Set(["EnterPlanMode"]);
64026
64729
  });
64027
64730
 
64028
- // src/tools/toolset.ts
64029
- var exports_toolset = {};
64030
- __export(exports_toolset, {
64031
- switchToolsetForModel: () => switchToolsetForModel,
64032
- reattachMemoryTool: () => reattachMemoryTool,
64033
- forceToolsetSwitch: () => forceToolsetSwitch,
64034
- ensureCorrectMemoryTool: () => ensureCorrectMemoryTool,
64035
- detachMemoryTools: () => detachMemoryTools,
64036
- MEMORY_TOOL_NAMES: () => MEMORY_TOOL_NAMES
64037
- });
64038
- async function ensureCorrectMemoryTool(agentId, modelIdentifier, useMemoryPatch) {
64039
- const resolvedModel = resolveModel(modelIdentifier) ?? modelIdentifier;
64040
- const client = await getClient2();
64041
- const shouldUsePatch = useMemoryPatch !== undefined ? useMemoryPatch : isOpenAIModel(resolvedModel);
64042
- try {
64043
- const agentWithTools = await client.agents.retrieve(agentId, {
64044
- include: ["agent.tools"]
64045
- });
64046
- const currentTools = agentWithTools.tools || [];
64047
- const mapByName = new Map(currentTools.map((t) => [t.name, t.id]));
64048
- const hasAnyMemoryTool = mapByName.has("memory") || mapByName.has("memory_apply_patch");
64049
- if (!hasAnyMemoryTool) {
64050
- return;
64051
- }
64052
- const desiredMemoryTool = shouldUsePatch ? "memory_apply_patch" : "memory";
64053
- const otherMemoryTool = desiredMemoryTool === "memory" ? "memory_apply_patch" : "memory";
64054
- let desiredId = mapByName.get(desiredMemoryTool);
64055
- if (!desiredId) {
64056
- const resp = await client.tools.list({ name: desiredMemoryTool });
64057
- desiredId = resp.items[0]?.id;
64058
- }
64059
- if (!desiredId) {
64060
- return;
64061
- }
64062
- const otherId = mapByName.get(otherMemoryTool);
64063
- if (mapByName.has(desiredMemoryTool) && !otherId) {
64064
- return;
64065
- }
64066
- const currentIds = currentTools.map((t) => t.id).filter((id) => typeof id === "string");
64067
- const newIds = new Set(currentIds);
64068
- if (otherId)
64069
- newIds.delete(otherId);
64070
- newIds.add(desiredId);
64071
- const updatedRules = (agentWithTools.tool_rules || []).map((r) => r.tool_name === otherMemoryTool ? { ...r, tool_name: desiredMemoryTool } : r);
64072
- await client.agents.update(agentId, {
64073
- tool_ids: Array.from(newIds),
64074
- tool_rules: updatedRules
64075
- });
64076
- } catch (err) {
64077
- console.warn(`Warning: Failed to sync memory tool: ${err instanceof Error ? err.message : String(err)}`);
64078
- }
64079
- }
64080
- async function detachMemoryTools(agentId) {
64081
- const client = await getClient2();
64082
- try {
64083
- const agentWithTools = await client.agents.retrieve(agentId, {
64084
- include: ["agent.tools"]
64085
- });
64086
- const currentTools = agentWithTools.tools || [];
64087
- let detachedAny = false;
64088
- for (const tool of currentTools) {
64089
- if (tool.name && MEMORY_TOOL_NAMES.has(tool.name)) {
64090
- if (tool.id) {
64091
- await client.agents.tools.detach(tool.id, { agent_id: agentId });
64092
- detachedAny = true;
64093
- }
64094
- }
64095
- }
64096
- return detachedAny;
64097
- } catch (err) {
64098
- console.warn(`Warning: Failed to detach memory tools: ${err instanceof Error ? err.message : String(err)}`);
64099
- return false;
64100
- }
64101
- }
64102
- async function reattachMemoryTool(agentId, modelIdentifier) {
64103
- const resolvedModel = resolveModel(modelIdentifier) ?? modelIdentifier;
64104
- const client = await getClient2();
64105
- const shouldUsePatch = isOpenAIModel(resolvedModel);
64106
- try {
64107
- const agentWithTools = await client.agents.retrieve(agentId, {
64108
- include: ["agent.tools"]
64109
- });
64110
- const currentTools = agentWithTools.tools || [];
64111
- const mapByName = new Map(currentTools.map((t) => [t.name, t.id]));
64112
- const desiredMemoryTool = shouldUsePatch ? "memory_apply_patch" : "memory";
64113
- if (mapByName.has(desiredMemoryTool)) {
64114
- return;
64115
- }
64116
- const resp = await client.tools.list({ name: desiredMemoryTool });
64117
- const toolId = resp.items[0]?.id;
64118
- if (!toolId) {
64119
- console.warn(`Memory tool "${desiredMemoryTool}" not found on server`);
64120
- return;
64121
- }
64122
- await client.agents.tools.attach(toolId, { agent_id: agentId });
64123
- } catch (err) {
64124
- console.warn(`Warning: Failed to reattach memory tool: ${err instanceof Error ? err.message : String(err)}`);
64125
- }
64126
- }
64127
- async function forceToolsetSwitch(toolsetName, agentId) {
64128
- let modelForLoading;
64129
- if (toolsetName === "none") {
64130
- clearToolsWithLock();
64131
- return;
64132
- } else if (toolsetName === "codex") {
64133
- await loadSpecificTools([...CODEX_TOOLS]);
64134
- modelForLoading = "openai/gpt-4";
64135
- } else if (toolsetName === "codex_snake") {
64136
- await loadTools("openai/gpt-4");
64137
- modelForLoading = "openai/gpt-4";
64138
- } else if (toolsetName === "gemini") {
64139
- await loadSpecificTools([...GEMINI_TOOLS]);
64140
- modelForLoading = "google_ai/gemini-3-pro-preview";
64141
- } else if (toolsetName === "gemini_snake") {
64142
- await loadTools("google_ai/gemini-3-pro-preview");
64143
- modelForLoading = "google_ai/gemini-3-pro-preview";
64144
- } else {
64145
- await loadTools("anthropic/claude-sonnet-4");
64146
- modelForLoading = "anthropic/claude-sonnet-4";
64147
- }
64148
- const useMemoryPatch = toolsetName === "codex" || toolsetName === "codex_snake";
64149
- await ensureCorrectMemoryTool(agentId, modelForLoading, useMemoryPatch);
64150
- }
64151
- async function switchToolsetForModel(modelIdentifier, agentId) {
64152
- const resolvedModel = resolveModel(modelIdentifier) ?? modelIdentifier;
64153
- await loadTools(resolvedModel);
64154
- const loadedAfterPrimary = getToolNames().length;
64155
- if (loadedAfterPrimary === 0 && !toolFilter.isActive()) {
64156
- await loadTools();
64157
- if (getToolNames().length === 0) {
64158
- throw new Error(`Failed to load any Letta tools for model "${resolvedModel}".`);
64159
- }
64160
- }
64161
- await ensureCorrectMemoryTool(agentId, resolvedModel);
64162
- const { isGeminiModel: isGeminiModel3 } = await init_manager3().then(() => exports_manager2);
64163
- const toolsetName = isOpenAIModel(resolvedModel) ? "codex" : isGeminiModel3(resolvedModel) ? "gemini" : "default";
64164
- return toolsetName;
64165
- }
64166
- var CODEX_TOOLS, GEMINI_TOOLS, MEMORY_TOOL_NAMES;
64167
- var init_toolset = __esm(async () => {
64168
- init_model();
64169
- init_filter();
64170
- await __promiseAll([
64171
- init_client2(),
64172
- init_manager3()
64173
- ]);
64174
- CODEX_TOOLS = OPENAI_PASCAL_TOOLS;
64175
- GEMINI_TOOLS = GEMINI_PASCAL_TOOLS;
64176
- MEMORY_TOOL_NAMES = new Set([
64177
- "memory",
64178
- "memory_apply_patch",
64179
- "memory_insert",
64180
- "memory_replace",
64181
- "memory_rethink"
64182
- ]);
64183
- });
64184
-
64185
64731
  // src/cli/helpers/toolNameMapping.ts
64186
64732
  function getDisplayToolName(rawName) {
64187
64733
  if (rawName === "write")
@@ -65320,6 +65866,14 @@ function getRetryStatusMessage(errorDetail) {
65320
65866
  return DEFAULT_RETRY_MESSAGE;
65321
65867
  if (errorDetail.includes("Anthropic API is overloaded"))
65322
65868
  return "Anthropic API is overloaded, retrying...";
65869
+ if (errorDetail.includes("ChatGPT API error") || errorDetail.includes("ChatGPT server error") || errorDetail.includes("upstream connect error")) {
65870
+ return "OpenAI ChatGPT backend connection failed, retrying...";
65871
+ }
65872
+ if (errorDetail.includes("Connection error during streaming") || errorDetail.includes("incomplete chunked read") || errorDetail.includes("connection termination")) {
65873
+ return "OpenAI ChatGPT streaming connection dropped, retrying...";
65874
+ }
65875
+ if (errorDetail.includes("OpenAI API error"))
65876
+ return "OpenAI API error, retrying...";
65323
65877
  return DEFAULT_RETRY_MESSAGE;
65324
65878
  }
65325
65879
  function createAgentLink(runId, agentId, conversationId) {
@@ -65340,6 +65894,157 @@ var init_errorFormatter = __esm(() => {
65340
65894
  `);
65341
65895
  });
65342
65896
 
65897
+ // src/cli/helpers/memoryReminder.ts
65898
+ function isValidStepCount(value) {
65899
+ return typeof value === "number" && Number.isFinite(value) && Number.isInteger(value) && value > 0;
65900
+ }
65901
+ function normalizeStepCount(value, fallback) {
65902
+ return isValidStepCount(value) ? value : fallback;
65903
+ }
65904
+ function normalizeTrigger(value, fallback) {
65905
+ if (value === "off" || value === "step-count" || value === "compaction-event") {
65906
+ return value;
65907
+ }
65908
+ return fallback;
65909
+ }
65910
+ function normalizeBehavior(value, fallback) {
65911
+ if (value === "reminder" || value === "auto-launch") {
65912
+ return value;
65913
+ }
65914
+ return fallback;
65915
+ }
65916
+ function applyExplicitReflectionOverrides(base2, raw) {
65917
+ return {
65918
+ trigger: normalizeTrigger(raw.reflectionTrigger, base2.trigger),
65919
+ behavior: normalizeBehavior(raw.reflectionBehavior, base2.behavior),
65920
+ stepCount: normalizeStepCount(raw.reflectionStepCount, base2.stepCount)
65921
+ };
65922
+ }
65923
+ function legacyModeToReflectionSettings(mode) {
65924
+ if (typeof mode === "number") {
65925
+ return {
65926
+ trigger: "step-count",
65927
+ behavior: "reminder",
65928
+ stepCount: normalizeStepCount(mode, DEFAULT_STEP_COUNT)
65929
+ };
65930
+ }
65931
+ if (mode === null) {
65932
+ return {
65933
+ trigger: "off",
65934
+ behavior: DEFAULT_REFLECTION_SETTINGS.behavior,
65935
+ stepCount: DEFAULT_REFLECTION_SETTINGS.stepCount
65936
+ };
65937
+ }
65938
+ if (mode === "compaction") {
65939
+ return {
65940
+ trigger: "compaction-event",
65941
+ behavior: "reminder",
65942
+ stepCount: DEFAULT_REFLECTION_SETTINGS.stepCount
65943
+ };
65944
+ }
65945
+ if (mode === "auto-compaction") {
65946
+ return {
65947
+ trigger: "compaction-event",
65948
+ behavior: "auto-launch",
65949
+ stepCount: DEFAULT_REFLECTION_SETTINGS.stepCount
65950
+ };
65951
+ }
65952
+ return { ...DEFAULT_REFLECTION_SETTINGS };
65953
+ }
65954
+ function reflectionSettingsToLegacyMode(settings) {
65955
+ if (settings.trigger === "off") {
65956
+ return null;
65957
+ }
65958
+ if (settings.trigger === "compaction-event") {
65959
+ return settings.behavior === "auto-launch" ? "auto-compaction" : "compaction";
65960
+ }
65961
+ return normalizeStepCount(settings.stepCount, DEFAULT_STEP_COUNT);
65962
+ }
65963
+ function getReflectionSettings() {
65964
+ const globalSettings = settingsManager.getSettings();
65965
+ let resolved = legacyModeToReflectionSettings(globalSettings.memoryReminderInterval);
65966
+ resolved = applyExplicitReflectionOverrides(resolved, globalSettings);
65967
+ try {
65968
+ const localSettings = settingsManager.getLocalProjectSettings();
65969
+ if (localSettings.memoryReminderInterval !== undefined) {
65970
+ resolved = legacyModeToReflectionSettings(localSettings.memoryReminderInterval);
65971
+ }
65972
+ resolved = applyExplicitReflectionOverrides(resolved, localSettings);
65973
+ } catch {}
65974
+ return resolved;
65975
+ }
65976
+ function shouldFireStepCountTrigger(turnCount, settings = getReflectionSettings()) {
65977
+ if (settings.trigger !== "step-count") {
65978
+ return false;
65979
+ }
65980
+ const stepCount = normalizeStepCount(settings.stepCount, DEFAULT_STEP_COUNT);
65981
+ return turnCount > 0 && turnCount % stepCount === 0;
65982
+ }
65983
+ async function buildMemfsAwareMemoryReminder(agentId, trigger) {
65984
+ if (settingsManager.isMemfsEnabled(agentId)) {
65985
+ debugLog("memory", `Reflection reminder fired (${trigger}, agent ${agentId})`);
65986
+ const { MEMORY_REFLECTION_REMINDER: MEMORY_REFLECTION_REMINDER3 } = await Promise.resolve().then(() => (init_promptAssets(), exports_promptAssets));
65987
+ return MEMORY_REFLECTION_REMINDER3;
65988
+ }
65989
+ debugLog("memory", `Memory check reminder fired (${trigger}, agent ${agentId})`);
65990
+ const { MEMORY_CHECK_REMINDER: MEMORY_CHECK_REMINDER3 } = await Promise.resolve().then(() => (init_promptAssets(), exports_promptAssets));
65991
+ return MEMORY_CHECK_REMINDER3;
65992
+ }
65993
+ async function buildCompactionMemoryReminder(agentId) {
65994
+ return buildMemfsAwareMemoryReminder(agentId, "compaction");
65995
+ }
65996
+ async function buildMemoryReminder(turnCount, agentId) {
65997
+ const reflectionSettings = getReflectionSettings();
65998
+ if (reflectionSettings.trigger !== "step-count") {
65999
+ return "";
66000
+ }
66001
+ if (shouldFireStepCountTrigger(turnCount, reflectionSettings)) {
66002
+ debugLog("memory", `Turn-based memory reminder fired (turn ${turnCount}, interval ${reflectionSettings.stepCount}, agent ${agentId})`);
66003
+ return buildMemfsAwareMemoryReminder(agentId, "interval");
66004
+ }
66005
+ return "";
66006
+ }
66007
+ function parseMemoryPreference(questions, answers) {
66008
+ for (const q of questions) {
66009
+ if (!q.question)
66010
+ continue;
66011
+ const questionLower = q.question.toLowerCase();
66012
+ const headerLower = q.header?.toLowerCase() || "";
66013
+ if (questionLower.includes("memory") || questionLower.includes("remember") || headerLower.includes("memory")) {
66014
+ const answer = answers[q.question]?.toLowerCase() || "";
66015
+ if (answer.includes("frequent")) {
66016
+ settingsManager.updateLocalProjectSettings({
66017
+ memoryReminderInterval: MEMORY_INTERVAL_FREQUENT,
66018
+ reflectionTrigger: "step-count",
66019
+ reflectionBehavior: "reminder",
66020
+ reflectionStepCount: MEMORY_INTERVAL_FREQUENT
66021
+ });
66022
+ return true;
66023
+ } else if (answer.includes("occasional")) {
66024
+ settingsManager.updateLocalProjectSettings({
66025
+ memoryReminderInterval: MEMORY_INTERVAL_OCCASIONAL,
66026
+ reflectionTrigger: "step-count",
66027
+ reflectionBehavior: "reminder",
66028
+ reflectionStepCount: MEMORY_INTERVAL_OCCASIONAL
66029
+ });
66030
+ return true;
66031
+ }
66032
+ break;
66033
+ }
66034
+ }
66035
+ return false;
66036
+ }
66037
+ var MEMORY_INTERVAL_FREQUENT = 5, MEMORY_INTERVAL_OCCASIONAL = 10, DEFAULT_STEP_COUNT = 25, DEFAULT_REFLECTION_SETTINGS;
66038
+ var init_memoryReminder = __esm(async () => {
66039
+ init_debug();
66040
+ await init_settings_manager();
66041
+ DEFAULT_REFLECTION_SETTINGS = {
66042
+ trigger: "compaction-event",
66043
+ behavior: "reminder",
66044
+ stepCount: DEFAULT_STEP_COUNT
66045
+ };
66046
+ });
66047
+
65343
66048
  // src/cli/helpers/streamProcessor.ts
65344
66049
  class StreamProcessor {
65345
66050
  pendingApprovals = new Map;
@@ -65525,7 +66230,7 @@ async function drainStream(stream2, buffers, refresh, abortSignal, onFirstMessag
65525
66230
  if (!streamProcessor.lastRunId) {
65526
66231
  fallbackError = errorMessage;
65527
66232
  }
65528
- stopReason = "error";
66233
+ stopReason = streamProcessor.stopReason || "error";
65529
66234
  markIncompleteToolsAsCancelled(buffers, true, "stream_error");
65530
66235
  queueMicrotask(refresh);
65531
66236
  } finally {
@@ -65763,7 +66468,7 @@ function parseRegistryHandle(handle) {
65763
66468
  }
65764
66469
  async function importAgentFromRegistry(options) {
65765
66470
  const { tmpdir: tmpdir3 } = await import("node:os");
65766
- const { join: join21 } = await import("node:path");
66471
+ const { join: join22 } = await import("node:path");
65767
66472
  const { writeFile: writeFile4, unlink } = await import("node:fs/promises");
65768
66473
  const { author, name } = parseRegistryHandle(options.handle);
65769
66474
  const rawUrl = `https://raw.githubusercontent.com/${AGENT_REGISTRY_OWNER}/${AGENT_REGISTRY_REPO}/refs/heads/${AGENT_REGISTRY_BRANCH}/agents/@${author}/${name}/${name}.af`;
@@ -65775,7 +66480,7 @@ async function importAgentFromRegistry(options) {
65775
66480
  throw new Error(`Failed to download agent @${author}/${name}: ${response.statusText}`);
65776
66481
  }
65777
66482
  const afContent = await response.text();
65778
- const tempPath = join21(tmpdir3(), `letta-import-${author}-${name}-${Date.now()}.af`);
66483
+ const tempPath = join22(tmpdir3(), `letta-import-${author}-${name}-${Date.now()}.af`);
65779
66484
  await writeFile4(tempPath, afContent, "utf-8");
65780
66485
  try {
65781
66486
  const result = await importAgentFromFile({
@@ -65800,149 +66505,6 @@ var init_import = __esm(async () => {
65800
66505
  ]);
65801
66506
  });
65802
66507
 
65803
- // src/agent/memoryFilesystem.ts
65804
- var exports_memoryFilesystem = {};
65805
- __export(exports_memoryFilesystem, {
65806
- renderMemoryFilesystemTree: () => renderMemoryFilesystemTree,
65807
- labelFromRelativePath: () => labelFromRelativePath,
65808
- getMemorySystemDir: () => getMemorySystemDir,
65809
- getMemoryFilesystemRoot: () => getMemoryFilesystemRoot,
65810
- ensureMemoryFilesystemDirs: () => ensureMemoryFilesystemDirs,
65811
- enableMemfsIfCloud: () => enableMemfsIfCloud,
65812
- applyMemfsFlags: () => applyMemfsFlags,
65813
- MEMORY_SYSTEM_DIR: () => MEMORY_SYSTEM_DIR,
65814
- MEMORY_FS_ROOT: () => MEMORY_FS_ROOT,
65815
- MEMORY_FS_MEMORY_DIR: () => MEMORY_FS_MEMORY_DIR,
65816
- MEMORY_FS_AGENTS_DIR: () => MEMORY_FS_AGENTS_DIR
65817
- });
65818
- import { existsSync as existsSync12, mkdirSync as mkdirSync8 } from "node:fs";
65819
- import { homedir as homedir14 } from "node:os";
65820
- import { join as join21 } from "node:path";
65821
- function getMemoryFilesystemRoot(agentId, homeDir = homedir14()) {
65822
- return join21(homeDir, MEMORY_FS_ROOT, MEMORY_FS_AGENTS_DIR, agentId, MEMORY_FS_MEMORY_DIR);
65823
- }
65824
- function getMemorySystemDir(agentId, homeDir = homedir14()) {
65825
- return join21(getMemoryFilesystemRoot(agentId, homeDir), MEMORY_SYSTEM_DIR);
65826
- }
65827
- function ensureMemoryFilesystemDirs(agentId, homeDir = homedir14()) {
65828
- const root = getMemoryFilesystemRoot(agentId, homeDir);
65829
- const systemDir = getMemorySystemDir(agentId, homeDir);
65830
- if (!existsSync12(root)) {
65831
- mkdirSync8(root, { recursive: true });
65832
- }
65833
- if (!existsSync12(systemDir)) {
65834
- mkdirSync8(systemDir, { recursive: true });
65835
- }
65836
- }
65837
- function labelFromRelativePath(relativePath) {
65838
- const normalized = relativePath.replace(/\\/g, "/");
65839
- return normalized.replace(/\.md$/, "");
65840
- }
65841
- function renderMemoryFilesystemTree(systemLabels, detachedLabels) {
65842
- const makeNode = () => ({ children: new Map, isFile: false });
65843
- const root = makeNode();
65844
- const insertPath = (base2, label) => {
65845
- const parts = base2 ? [base2, ...label.split("/")] : label.split("/");
65846
- let current = root;
65847
- for (const [i, partName] of parts.entries()) {
65848
- const part = i === parts.length - 1 ? `${partName}.md` : partName;
65849
- if (!current.children.has(part)) {
65850
- current.children.set(part, makeNode());
65851
- }
65852
- current = current.children.get(part);
65853
- if (i === parts.length - 1) {
65854
- current.isFile = true;
65855
- }
65856
- }
65857
- };
65858
- for (const label of systemLabels) {
65859
- insertPath(MEMORY_SYSTEM_DIR, label);
65860
- }
65861
- for (const label of detachedLabels) {
65862
- insertPath(null, label);
65863
- }
65864
- if (!root.children.has(MEMORY_SYSTEM_DIR)) {
65865
- root.children.set(MEMORY_SYSTEM_DIR, makeNode());
65866
- }
65867
- const sortedEntries = (node) => {
65868
- const entries = Array.from(node.children.entries());
65869
- return entries.sort(([nameA, nodeA], [nameB, nodeB]) => {
65870
- if (nodeA.isFile !== nodeB.isFile) {
65871
- return nodeA.isFile ? 1 : -1;
65872
- }
65873
- return nameA.localeCompare(nameB);
65874
- });
65875
- };
65876
- const lines = ["/memory/"];
65877
- const render2 = (node, prefix) => {
65878
- const entries = sortedEntries(node);
65879
- entries.forEach(([name, child], index) => {
65880
- const isLast = index === entries.length - 1;
65881
- const branch = isLast ? "└──" : "├──";
65882
- lines.push(`${prefix}${branch} ${name}${child.isFile ? "" : "/"}`);
65883
- if (child.children.size > 0) {
65884
- const nextPrefix = `${prefix}${isLast ? " " : "│ "}`;
65885
- render2(child, nextPrefix);
65886
- }
65887
- });
65888
- };
65889
- render2(root, "");
65890
- return lines.join(`
65891
- `);
65892
- }
65893
- async function applyMemfsFlags(agentId, memfsFlag, noMemfsFlag, options) {
65894
- const { getServerUrl: getServerUrl2 } = await init_client2().then(() => exports_client);
65895
- const { settingsManager: settingsManager3 } = await init_settings_manager().then(() => exports_settings_manager);
65896
- if (memfsFlag) {
65897
- const serverUrl = getServerUrl2();
65898
- if (!serverUrl.includes("api.letta.com")) {
65899
- throw new Error("--memfs is only available on Letta Cloud (api.letta.com).");
65900
- }
65901
- settingsManager3.setMemfsEnabled(agentId, true);
65902
- } else if (noMemfsFlag) {
65903
- settingsManager3.setMemfsEnabled(agentId, false);
65904
- }
65905
- const isEnabled = settingsManager3.isMemfsEnabled(agentId);
65906
- if (isEnabled && memfsFlag) {
65907
- const { detachMemoryTools: detachMemoryTools2 } = await init_toolset().then(() => exports_toolset);
65908
- await detachMemoryTools2(agentId);
65909
- }
65910
- if (memfsFlag || noMemfsFlag) {
65911
- const { updateAgentSystemPromptMemfs: updateAgentSystemPromptMemfs2 } = await init_modify().then(() => exports_modify);
65912
- await updateAgentSystemPromptMemfs2(agentId, isEnabled);
65913
- }
65914
- let pullSummary;
65915
- if (isEnabled) {
65916
- const { addGitMemoryTag: addGitMemoryTag2, isGitRepo: isGitRepo2, cloneMemoryRepo: cloneMemoryRepo2, pullMemory: pullMemory2 } = await init_memoryGit().then(() => exports_memoryGit);
65917
- await addGitMemoryTag2(agentId);
65918
- if (!isGitRepo2(agentId)) {
65919
- await cloneMemoryRepo2(agentId);
65920
- } else if (options?.pullOnExistingRepo) {
65921
- const result = await pullMemory2(agentId);
65922
- pullSummary = result.summary;
65923
- }
65924
- }
65925
- const action = memfsFlag ? "enabled" : noMemfsFlag ? "disabled" : "unchanged";
65926
- return {
65927
- action,
65928
- memoryDir: isEnabled ? getMemoryFilesystemRoot(agentId) : undefined,
65929
- pullSummary
65930
- };
65931
- }
65932
- async function enableMemfsIfCloud(agentId) {
65933
- const { getServerUrl: getServerUrl2 } = await init_client2().then(() => exports_client);
65934
- const serverUrl = getServerUrl2();
65935
- if (!serverUrl.includes("api.letta.com"))
65936
- return;
65937
- try {
65938
- await applyMemfsFlags(agentId, true, undefined);
65939
- } catch (error) {
65940
- console.warn(`Warning: Could not enable memfs for new agent: ${error instanceof Error ? error.message : String(error)}`);
65941
- }
65942
- }
65943
- var MEMORY_FS_ROOT = ".letta", MEMORY_FS_AGENTS_DIR = "agents", MEMORY_FS_MEMORY_DIR = "memory", MEMORY_SYSTEM_DIR = "system";
65944
- var init_memoryFilesystem = () => {};
65945
-
65946
66508
  // src/agent/defaults.ts
65947
66509
  var exports_defaults = {};
65948
66510
  __export(exports_defaults, {
@@ -66180,10 +66742,106 @@ var init_check_approval = __esm(() => {
66180
66742
  // src/headless.ts
66181
66743
  var exports_headless = {};
66182
66744
  __export(exports_headless, {
66745
+ shouldReinjectSkillsAfterCompaction: () => shouldReinjectSkillsAfterCompaction,
66746
+ prependSkillsReminderToContent: () => prependSkillsReminderToContent,
66183
66747
  handleHeadlessCommand: () => handleHeadlessCommand
66184
66748
  });
66185
66749
  import { parseArgs as parseArgs5 } from "node:util";
66186
- async function handleHeadlessCommand(argv, model, skillsDirectory, noSkills) {
66750
+ function prependSkillsReminderToContent(content, skillsReminder) {
66751
+ if (!skillsReminder) {
66752
+ return content;
66753
+ }
66754
+ if (typeof content === "string") {
66755
+ return `${skillsReminder}
66756
+
66757
+ ${content}`;
66758
+ }
66759
+ if (Array.isArray(content)) {
66760
+ return [
66761
+ {
66762
+ type: "text",
66763
+ text: `${skillsReminder}
66764
+
66765
+ `
66766
+ },
66767
+ ...content
66768
+ ];
66769
+ }
66770
+ return content;
66771
+ }
66772
+ function shouldReinjectSkillsAfterCompaction(lines) {
66773
+ return lines.some((line) => line.kind === "event" && line.eventType === "compaction" && line.phase === "finished" && (line.summary !== undefined || line.stats !== undefined));
66774
+ }
66775
+ function parseReflectionOverrides(values) {
66776
+ const triggerRaw = values["reflection-trigger"];
66777
+ const behaviorRaw = values["reflection-behavior"];
66778
+ const stepCountRaw = values["reflection-step-count"];
66779
+ if (!triggerRaw && !behaviorRaw && !stepCountRaw) {
66780
+ return {};
66781
+ }
66782
+ const overrides = {};
66783
+ if (triggerRaw !== undefined) {
66784
+ if (triggerRaw !== "off" && triggerRaw !== "step-count" && triggerRaw !== "compaction-event") {
66785
+ throw new Error(`Invalid --reflection-trigger "${triggerRaw}". Valid values: off, step-count, compaction-event`);
66786
+ }
66787
+ overrides.trigger = triggerRaw;
66788
+ }
66789
+ if (behaviorRaw !== undefined) {
66790
+ if (behaviorRaw !== "reminder" && behaviorRaw !== "auto-launch") {
66791
+ throw new Error(`Invalid --reflection-behavior "${behaviorRaw}". Valid values: reminder, auto-launch`);
66792
+ }
66793
+ overrides.behavior = behaviorRaw;
66794
+ }
66795
+ if (stepCountRaw !== undefined) {
66796
+ const parsed = Number.parseInt(stepCountRaw, 10);
66797
+ if (Number.isNaN(parsed) || parsed <= 0) {
66798
+ throw new Error(`Invalid --reflection-step-count "${stepCountRaw}". Expected a positive integer.`);
66799
+ }
66800
+ overrides.stepCount = parsed;
66801
+ }
66802
+ return overrides;
66803
+ }
66804
+ function hasReflectionOverrides(overrides) {
66805
+ return overrides.trigger !== undefined || overrides.behavior !== undefined || overrides.stepCount !== undefined;
66806
+ }
66807
+ async function applyReflectionOverrides(agentId, overrides) {
66808
+ const current = getReflectionSettings();
66809
+ const merged = {
66810
+ trigger: overrides.trigger ?? current.trigger,
66811
+ behavior: overrides.behavior ?? current.behavior,
66812
+ stepCount: overrides.stepCount ?? current.stepCount
66813
+ };
66814
+ if (!hasReflectionOverrides(overrides)) {
66815
+ return merged;
66816
+ }
66817
+ const memfsEnabled = settingsManager.isMemfsEnabled(agentId);
66818
+ if (!memfsEnabled && merged.trigger === "compaction-event") {
66819
+ throw new Error("--reflection-trigger compaction-event requires memfs enabled for this agent.");
66820
+ }
66821
+ if (!memfsEnabled && merged.trigger !== "off" && merged.behavior === "auto-launch") {
66822
+ throw new Error("--reflection-behavior auto-launch requires memfs enabled for this agent.");
66823
+ }
66824
+ try {
66825
+ settingsManager.getLocalProjectSettings();
66826
+ } catch {
66827
+ await settingsManager.loadLocalProjectSettings();
66828
+ }
66829
+ const legacyMode = reflectionSettingsToLegacyMode(merged);
66830
+ settingsManager.updateLocalProjectSettings({
66831
+ memoryReminderInterval: legacyMode,
66832
+ reflectionTrigger: merged.trigger,
66833
+ reflectionBehavior: merged.behavior,
66834
+ reflectionStepCount: merged.stepCount
66835
+ });
66836
+ settingsManager.updateSettings({
66837
+ memoryReminderInterval: legacyMode,
66838
+ reflectionTrigger: merged.trigger,
66839
+ reflectionBehavior: merged.behavior,
66840
+ reflectionStepCount: merged.stepCount
66841
+ });
66842
+ return merged;
66843
+ }
66844
+ async function handleHeadlessCommand(argv, model, skillsDirectoryOverride, skillSourcesOverride, systemInfoReminderEnabledOverride) {
66187
66845
  const { values, positionals } = parseArgs5({
66188
66846
  args: argv,
66189
66847
  options: {
@@ -66216,6 +66874,7 @@ async function handleHeadlessCommand(argv, model, skillsDirectory, noSkills) {
66216
66874
  "permission-mode": { type: "string" },
66217
66875
  yolo: { type: "boolean" },
66218
66876
  skills: { type: "string" },
66877
+ "skill-sources": { type: "string" },
66219
66878
  "pre-load-skills": { type: "string" },
66220
66879
  "init-blocks": { type: "string" },
66221
66880
  "base-tools": { type: "string" },
@@ -66224,6 +66883,11 @@ async function handleHeadlessCommand(argv, model, skillsDirectory, noSkills) {
66224
66883
  memfs: { type: "boolean" },
66225
66884
  "no-memfs": { type: "boolean" },
66226
66885
  "no-skills": { type: "boolean" },
66886
+ "no-bundled-skills": { type: "boolean" },
66887
+ "no-system-info-reminder": { type: "boolean" },
66888
+ "reflection-trigger": { type: "string" },
66889
+ "reflection-behavior": { type: "string" },
66890
+ "reflection-step-count": { type: "string" },
66227
66891
  "max-turns": { type: "string" }
66228
66892
  },
66229
66893
  strict: false,
@@ -66315,12 +66979,42 @@ In headless mode, use:
66315
66979
  const blockValueArgs = values["block-value"];
66316
66980
  const initBlocksRaw = values["init-blocks"];
66317
66981
  const baseToolsRaw = values["base-tools"];
66982
+ const skillsDirectory = values.skills ?? skillsDirectoryOverride;
66983
+ const noSkillsFlag = values["no-skills"];
66984
+ const noBundledSkillsFlag = values["no-bundled-skills"];
66985
+ const skillSourcesRaw = values["skill-sources"];
66318
66986
  const memfsFlag = values.memfs;
66319
66987
  const noMemfsFlag = values["no-memfs"];
66988
+ const requestedMemoryPromptMode = memfsFlag ? "memfs" : noMemfsFlag ? "standard" : undefined;
66989
+ const shouldAutoEnableMemfsForNewAgent = !memfsFlag && !noMemfsFlag;
66320
66990
  const fromAfFile = values["from-af"];
66321
66991
  const preLoadSkillsRaw = values["pre-load-skills"];
66992
+ const systemInfoReminderEnabled = systemInfoReminderEnabledOverride ?? !values["no-system-info-reminder"];
66993
+ const reflectionOverrides = (() => {
66994
+ try {
66995
+ return parseReflectionOverrides(values);
66996
+ } catch (error) {
66997
+ console.error(error instanceof Error ? `Error: ${error.message}` : String(error));
66998
+ process.exit(1);
66999
+ }
67000
+ })();
66322
67001
  const maxTurnsRaw = values["max-turns"];
66323
- const tagsRaw = values["tags"];
67002
+ const tagsRaw = values.tags;
67003
+ const resolvedSkillSources = (() => {
67004
+ if (skillSourcesOverride) {
67005
+ return skillSourcesOverride;
67006
+ }
67007
+ try {
67008
+ return resolveSkillSourcesSelection({
67009
+ skillSourcesRaw,
67010
+ noSkills: noSkillsFlag,
67011
+ noBundledSkills: noBundledSkillsFlag
67012
+ });
67013
+ } catch (error) {
67014
+ console.error(error instanceof Error ? `Error: ${error.message}` : String(error));
67015
+ process.exit(1);
67016
+ }
67017
+ })();
66324
67018
  let tags;
66325
67019
  if (tagsRaw !== undefined) {
66326
67020
  const trimmed = tagsRaw.trim();
@@ -66339,6 +67033,10 @@ In headless mode, use:
66339
67033
  }
66340
67034
  maxTurns = parsed;
66341
67035
  }
67036
+ if (preLoadSkillsRaw && resolvedSkillSources.length === 0) {
67037
+ console.error("Error: --pre-load-skills cannot be used when all skill sources are disabled.");
67038
+ process.exit(1);
67039
+ }
66342
67040
  if (specifiedConversationId?.startsWith("agent-")) {
66343
67041
  if (specifiedAgentId && specifiedAgentId !== specifiedConversationId) {
66344
67042
  console.error(`Error: Conflicting agent IDs: --agent ${specifiedAgentId} vs --conv ${specifiedConversationId}`);
@@ -66547,6 +67245,7 @@ In headless mode, use:
66547
67245
  systemPromptPreset,
66548
67246
  systemPromptCustom: systemCustom,
66549
67247
  systemPromptAppend: systemAppend,
67248
+ memoryPromptMode: requestedMemoryPromptMode,
66550
67249
  initBlocks,
66551
67250
  baseTools,
66552
67251
  memoryBlocks,
@@ -66555,8 +67254,10 @@ In headless mode, use:
66555
67254
  };
66556
67255
  const result = await createAgent(createOptions);
66557
67256
  agent = result.agent;
66558
- const { enableMemfsIfCloud: enableMemfsIfCloud2 } = await Promise.resolve().then(() => (init_memoryFilesystem(), exports_memoryFilesystem));
66559
- await enableMemfsIfCloud2(agent.id);
67257
+ if (shouldAutoEnableMemfsForNewAgent) {
67258
+ const { enableMemfsIfCloud: enableMemfsIfCloud2 } = await Promise.resolve().then(() => (init_memoryFilesystem(), exports_memoryFilesystem));
67259
+ await enableMemfsIfCloud2(agent.id);
67260
+ }
66560
67261
  }
66561
67262
  let resolvedLocalConvId = null;
66562
67263
  if (!agent) {
@@ -66622,6 +67323,7 @@ In headless mode, use:
66622
67323
  }
66623
67324
  }
66624
67325
  let conversationId;
67326
+ let effectiveReflectionSettings;
66625
67327
  const isSubagent = process.env.LETTA_CODE_AGENT_ROLE === "subagent";
66626
67328
  try {
66627
67329
  const { applyMemfsFlags: applyMemfsFlags2 } = await Promise.resolve().then(() => (init_memoryFilesystem(), exports_memoryFilesystem));
@@ -66634,6 +67336,12 @@ In headless mode, use:
66634
67336
  console.error(`Memory git sync failed: ${error instanceof Error ? error.message : String(error)}`);
66635
67337
  process.exit(1);
66636
67338
  }
67339
+ try {
67340
+ effectiveReflectionSettings = await applyReflectionOverrides(agent.id, reflectionOverrides);
67341
+ } catch (error) {
67342
+ console.error(`Failed to apply sleeptime settings: ${error instanceof Error ? error.message : String(error)}`);
67343
+ process.exit(1);
67344
+ }
66637
67345
  const isolatedBlockLabels = initBlocks === undefined ? [...ISOLATED_BLOCK_LABELS] : ISOLATED_BLOCK_LABELS.filter((label) => initBlocks.includes(label));
66638
67346
  if (specifiedConversationId) {
66639
67347
  if (specifiedConversationId === "default") {
@@ -66711,7 +67419,7 @@ In headless mode, use:
66711
67419
  }
66712
67420
  } catch {}
66713
67421
  }
66714
- setAgentContext2(agent.id, skillsDirectory, noSkills);
67422
+ setAgentContext2(agent.id, skillsDirectory, resolvedSkillSources);
66715
67423
  const outputFormat = values["output-format"] || "text";
66716
67424
  const includePartialMessages = Boolean(values["include-partial-messages"]);
66717
67425
  if (!["text", "json", "stream-json"].includes(outputFormat)) {
@@ -66726,7 +67434,7 @@ In headless mode, use:
66726
67434
  const loadedToolNames = getClientToolsFromRegistry2().map((t) => t.name);
66727
67435
  const availableTools = loadedToolNames.length > 0 ? loadedToolNames : agent.tools?.map((t) => t.name).filter((n) => !!n) || [];
66728
67436
  if (isBidirectionalMode) {
66729
- await runBidirectionalMode(agent, conversationId, client, outputFormat, includePartialMessages, availableTools);
67437
+ await runBidirectionalMode(agent, conversationId, client, outputFormat, includePartialMessages, availableTools, resolvedSkillSources, systemInfoReminderEnabled, effectiveReflectionSettings);
66730
67438
  return;
66731
67439
  }
66732
67440
  const buffers = createBuffers(agent.id);
@@ -66746,6 +67454,11 @@ In headless mode, use:
66746
67454
  permission_mode: "",
66747
67455
  slash_commands: [],
66748
67456
  memfs_enabled: settingsManager.isMemfsEnabled(agent.id),
67457
+ skill_sources: resolvedSkillSources,
67458
+ system_info_reminder_enabled: systemInfoReminderEnabled,
67459
+ reflection_trigger: effectiveReflectionSettings.trigger,
67460
+ reflection_behavior: effectiveReflectionSettings.behavior,
67461
+ reflection_step_count: effectiveReflectionSettings.stepCount,
66749
67462
  uuid: `init-${agent.id}`
66750
67463
  };
66751
67464
  console.log(JSON.stringify(initEvent));
@@ -66869,7 +67582,7 @@ ${SYSTEM_REMINDER_CLOSE}
66869
67582
  try {
66870
67583
  const skillsDir = getSkillsDirectory2() || join22(process.cwd(), defaultDir);
66871
67584
  const { skills } = await discoverSkills3(skillsDir, agent.id, {
66872
- skipBundled: noSkills
67585
+ sources: resolvedSkillSources
66873
67586
  });
66874
67587
  const skillsReminder = formatSkillsAsSystemReminder3(skills);
66875
67588
  if (skillsReminder) {
@@ -66959,7 +67672,11 @@ ${loadedContents.join(`
66959
67672
  });
66960
67673
  } catch (preStreamError) {
66961
67674
  const errorDetail = extractConflictDetail(preStreamError);
66962
- const preStreamAction = getPreStreamErrorAction(errorDetail, conversationBusyRetries, CONVERSATION_BUSY_MAX_RETRIES);
67675
+ const preStreamAction = getPreStreamErrorAction(errorDetail, conversationBusyRetries, CONVERSATION_BUSY_MAX_RETRIES, {
67676
+ status: preStreamError instanceof APIError2 ? preStreamError.status : undefined,
67677
+ transientRetries: llmApiErrorRetries,
67678
+ maxTransientRetries: LLM_API_ERROR_MAX_RETRIES
67679
+ });
66963
67680
  if (preStreamAction === "resolve_approval_pending") {
66964
67681
  if (outputFormat === "stream-json") {
66965
67682
  const recoveryMsg = {
@@ -66995,6 +67712,30 @@ ${loadedContents.join(`
66995
67712
  await new Promise((resolve20) => setTimeout(resolve20, CONVERSATION_BUSY_RETRY_DELAY_MS));
66996
67713
  continue;
66997
67714
  }
67715
+ if (preStreamAction === "retry_transient") {
67716
+ const attempt = llmApiErrorRetries + 1;
67717
+ const retryAfterMs = preStreamError instanceof APIError2 ? parseRetryAfterHeaderMs(preStreamError.headers?.get("retry-after")) : null;
67718
+ const delayMs = retryAfterMs ?? 1000 * 2 ** (attempt - 1);
67719
+ llmApiErrorRetries = attempt;
67720
+ if (outputFormat === "stream-json") {
67721
+ const retryMsg = {
67722
+ type: "retry",
67723
+ reason: "llm_api_error",
67724
+ attempt,
67725
+ max_attempts: LLM_API_ERROR_MAX_RETRIES,
67726
+ delay_ms: delayMs,
67727
+ session_id: sessionId,
67728
+ uuid: `retry-pre-stream-${crypto.randomUUID()}`
67729
+ };
67730
+ console.log(JSON.stringify(retryMsg));
67731
+ } else {
67732
+ const delaySeconds = Math.round(delayMs / 1000);
67733
+ console.error(`Transient API error before streaming (attempt ${attempt} of ${LLM_API_ERROR_MAX_RETRIES}), retrying in ${delaySeconds}s...`);
67734
+ }
67735
+ await new Promise((resolve20) => setTimeout(resolve20, delayMs));
67736
+ conversationBusyRetries = 0;
67737
+ continue;
67738
+ }
66998
67739
  conversationBusyRetries = 0;
66999
67740
  throw preStreamError;
67000
67741
  }
@@ -67257,18 +67998,7 @@ ${loadedContents.join(`
67257
67998
  const metaError = run.metadata?.error;
67258
67999
  const errorType = metaError?.error_type ?? metaError?.error?.error_type;
67259
68000
  const detail = metaError?.detail ?? metaError?.error?.detail ?? "";
67260
- const is4xxError = /Error code: 4\d{2}/.test(detail);
67261
- const llmProviderPatterns = [
67262
- "Anthropic API error",
67263
- "OpenAI API error",
67264
- "Google Vertex API error",
67265
- "overloaded",
67266
- "api_error",
67267
- "Network error",
67268
- "Connection error during Anthropic streaming"
67269
- ];
67270
- const isLlmErrorFromDetail = llmProviderPatterns.some((pattern) => detail.includes(pattern));
67271
- if ((errorType === "llm_error" || isLlmErrorFromDetail) && !is4xxError) {
68001
+ if (shouldRetryRunMetadataError(errorType, detail)) {
67272
68002
  const attempt = llmApiErrorRetries + 1;
67273
68003
  const baseDelayMs = 1000;
67274
68004
  const delayMs = baseDelayMs * 2 ** (attempt - 1);
@@ -67416,7 +68146,7 @@ ${loadedContents.join(`
67416
68146
  markMilestone("HEADLESS_COMPLETE");
67417
68147
  reportAllMilestones();
67418
68148
  }
67419
- async function runBidirectionalMode(agent, conversationId, client, _outputFormat, includePartialMessages, availableTools) {
68149
+ async function runBidirectionalMode(agent, conversationId, client, _outputFormat, includePartialMessages, availableTools, skillSources, systemInfoReminderEnabled, reflectionSettings) {
67420
68150
  const sessionId = agent.id;
67421
68151
  const readline = await import("node:readline");
67422
68152
  const initEvent = {
@@ -67429,10 +68159,18 @@ async function runBidirectionalMode(agent, conversationId, client, _outputFormat
67429
68159
  tools: availableTools,
67430
68160
  cwd: process.cwd(),
67431
68161
  memfs_enabled: settingsManager.isMemfsEnabled(agent.id),
68162
+ skill_sources: skillSources,
68163
+ system_info_reminder_enabled: systemInfoReminderEnabled,
68164
+ reflection_trigger: reflectionSettings.trigger,
68165
+ reflection_behavior: reflectionSettings.behavior,
68166
+ reflection_step_count: reflectionSettings.stepCount,
67432
68167
  uuid: `init-${agent.id}`
67433
68168
  };
67434
68169
  console.log(JSON.stringify(initEvent));
67435
68170
  let currentAbortController = null;
68171
+ let hasInjectedSkillsReminder = false;
68172
+ let pendingSkillsReinject = false;
68173
+ let cachedSkillsReminder = null;
67436
68174
  const resolveAllPendingApprovals = async () => {
67437
68175
  const { getResumeData: getResumeData3 } = await Promise.resolve().then(() => (init_check_approval(), exports_check_approval));
67438
68176
  while (true) {
@@ -67614,7 +68352,13 @@ async function runBidirectionalMode(agent, conversationId, client, _outputFormat
67614
68352
  response: {
67615
68353
  agent_id: agent.id,
67616
68354
  model: agent.llm_config?.model,
67617
- tools: availableTools
68355
+ tools: availableTools,
68356
+ memfs_enabled: settingsManager.isMemfsEnabled(agent.id),
68357
+ skill_sources: skillSources,
68358
+ system_info_reminder_enabled: systemInfoReminderEnabled,
68359
+ reflection_trigger: reflectionSettings.trigger,
68360
+ reflection_behavior: reflectionSettings.behavior,
68361
+ reflection_step_count: reflectionSettings.stepCount
67618
68362
  }
67619
68363
  },
67620
68364
  session_id: sessionId,
@@ -67699,7 +68443,7 @@ async function runBidirectionalMode(agent, conversationId, client, _outputFormat
67699
68443
  }
67700
68444
  continue;
67701
68445
  }
67702
- if (message.type === "user" && message.message?.content) {
68446
+ if (message.type === "user" && message.message?.content !== undefined) {
67703
68447
  const userContent = message.message.content;
67704
68448
  currentAbortController = new AbortController;
67705
68449
  try {
@@ -67708,26 +68452,34 @@ async function runBidirectionalMode(agent, conversationId, client, _outputFormat
67708
68452
  let numTurns = 0;
67709
68453
  let lastStopReason = null;
67710
68454
  let sawStreamError = false;
68455
+ let preStreamTransientRetries = 0;
67711
68456
  let enrichedContent = userContent;
67712
- if (typeof enrichedContent === "string") {
67713
- try {
67714
- const {
67715
- discoverSkills: discover,
67716
- SKILLS_DIR: defaultDir,
67717
- formatSkillsAsSystemReminder: formatSkillsAsSystemReminder3
67718
- } = await Promise.resolve().then(() => (init_skills(), exports_skills));
67719
- const { getSkillsDirectory: getSkillsDirectory2 } = await Promise.resolve().then(() => (init_context(), exports_context));
67720
- const { join: join22 } = await import("node:path");
67721
- const skillsDir = getSkillsDirectory2() || join22(process.cwd(), defaultDir);
67722
- const { skills } = await discover(skillsDir, agent.id);
67723
- const skillsReminder = formatSkillsAsSystemReminder3(skills);
67724
- if (skillsReminder) {
67725
- enrichedContent = `${skillsReminder}
67726
-
67727
- ${enrichedContent}`;
67728
- }
67729
- } catch {}
67730
- }
68457
+ try {
68458
+ const {
68459
+ discoverSkills: discover,
68460
+ SKILLS_DIR: defaultDir,
68461
+ formatSkillsAsSystemReminder: formatSkillsAsSystemReminder3
68462
+ } = await Promise.resolve().then(() => (init_skills(), exports_skills));
68463
+ const { getSkillsDirectory: getSkillsDirectory2 } = await Promise.resolve().then(() => (init_context(), exports_context));
68464
+ const { join: join22 } = await import("node:path");
68465
+ const skillsDir = getSkillsDirectory2() || join22(process.cwd(), defaultDir);
68466
+ const { skills } = await discover(skillsDir, agent.id, {
68467
+ sources: skillSources
68468
+ });
68469
+ const latestSkillsReminder = formatSkillsAsSystemReminder3(skills);
68470
+ if (cachedSkillsReminder !== null && latestSkillsReminder !== cachedSkillsReminder) {
68471
+ pendingSkillsReinject = true;
68472
+ }
68473
+ cachedSkillsReminder = latestSkillsReminder;
68474
+ const shouldInjectSkillsReminder = !hasInjectedSkillsReminder || pendingSkillsReinject;
68475
+ if (shouldInjectSkillsReminder && latestSkillsReminder) {
68476
+ enrichedContent = prependSkillsReminderToContent(enrichedContent, latestSkillsReminder);
68477
+ }
68478
+ if (shouldInjectSkillsReminder) {
68479
+ hasInjectedSkillsReminder = true;
68480
+ pendingSkillsReinject = false;
68481
+ }
68482
+ } catch {}
67731
68483
  let currentInput = [
67732
68484
  { role: "user", content: enrichedContent }
67733
68485
  ];
@@ -67759,7 +68511,11 @@ ${enrichedContent}`;
67759
68511
  });
67760
68512
  } catch (preStreamError) {
67761
68513
  const errorDetail = extractConflictDetail(preStreamError);
67762
- const preStreamAction = getPreStreamErrorAction(errorDetail, 0, 0);
68514
+ const preStreamAction = getPreStreamErrorAction(errorDetail, 0, 0, {
68515
+ status: preStreamError instanceof APIError2 ? preStreamError.status : undefined,
68516
+ transientRetries: preStreamTransientRetries,
68517
+ maxTransientRetries: LLM_API_ERROR_MAX_RETRIES
68518
+ });
67763
68519
  if (preStreamAction === "resolve_approval_pending") {
67764
68520
  const recoveryMsg = {
67765
68521
  type: "recovery",
@@ -67772,8 +68528,27 @@ ${enrichedContent}`;
67772
68528
  await resolveAllPendingApprovals();
67773
68529
  continue;
67774
68530
  }
68531
+ if (preStreamAction === "retry_transient") {
68532
+ const attempt = preStreamTransientRetries + 1;
68533
+ const retryAfterMs = preStreamError instanceof APIError2 ? parseRetryAfterHeaderMs(preStreamError.headers?.get("retry-after")) : null;
68534
+ const delayMs = retryAfterMs ?? 1000 * 2 ** (attempt - 1);
68535
+ preStreamTransientRetries = attempt;
68536
+ const retryMsg = {
68537
+ type: "retry",
68538
+ reason: "llm_api_error",
68539
+ attempt,
68540
+ max_attempts: LLM_API_ERROR_MAX_RETRIES,
68541
+ delay_ms: delayMs,
68542
+ session_id: sessionId,
68543
+ uuid: `retry-bidir-${crypto.randomUUID()}`
68544
+ };
68545
+ console.log(JSON.stringify(retryMsg));
68546
+ await new Promise((resolve20) => setTimeout(resolve20, delayMs));
68547
+ continue;
68548
+ }
67775
68549
  throw preStreamError;
67776
68550
  }
68551
+ preStreamTransientRetries = 0;
67777
68552
  const streamJsonHook = ({
67778
68553
  chunk,
67779
68554
  shouldOutput,
@@ -67923,6 +68698,9 @@ ${enrichedContent}`;
67923
68698
  }
67924
68699
  const durationMs = performance.now() - startTime;
67925
68700
  const lines = toLines(buffers);
68701
+ if (shouldReinjectSkillsAfterCompaction(lines)) {
68702
+ pendingSkillsReinject = true;
68703
+ }
67926
68704
  const reversed = [...lines].reverse();
67927
68705
  const lastAssistant = reversed.find((line2) => line2.kind === "assistant" && ("text" in line2) && typeof line2.text === "string" && line2.text.trim().length > 0);
67928
68706
  const lastReasoning = reversed.find((line2) => line2.kind === "reasoning" && ("text" in line2) && typeof line2.text === "string" && line2.text.trim().length > 0);
@@ -67997,6 +68775,7 @@ var init_headless = __esm(async () => {
67997
68775
  init_context();
67998
68776
  init_memory();
67999
68777
  init_model();
68778
+ init_skillSources();
68000
68779
  init_errorFormatter();
68001
68780
  init_constants();
68002
68781
  init_interactivePolicy();
@@ -68008,6 +68787,7 @@ var init_headless = __esm(async () => {
68008
68787
  init_message(),
68009
68788
  init_accumulator(),
68010
68789
  init_approvalClassification(),
68790
+ init_memoryReminder(),
68011
68791
  init_stream(),
68012
68792
  init_settings_manager(),
68013
68793
  init_manager3()
@@ -88408,11 +89188,11 @@ function SkillsDialog({ onClose, agentId }) {
88408
89188
  (async () => {
88409
89189
  try {
88410
89190
  const { discoverSkills: discoverSkills3, SKILLS_DIR: SKILLS_DIR3 } = await Promise.resolve().then(() => (init_skills(), exports_skills));
88411
- const { getSkillsDirectory: getSkillsDirectory2, getNoSkills: getNoSkills2 } = await Promise.resolve().then(() => (init_context(), exports_context));
89191
+ const { getSkillsDirectory: getSkillsDirectory2, getSkillSources: getSkillSources2 } = await Promise.resolve().then(() => (init_context(), exports_context));
88412
89192
  const { join: join29 } = await import("node:path");
88413
89193
  const skillsDir = getSkillsDirectory2() || join29(process.cwd(), SKILLS_DIR3);
88414
89194
  const result = await discoverSkills3(skillsDir, agentId, {
88415
- skipBundled: getNoSkills2()
89195
+ sources: getSkillSources2()
88416
89196
  });
88417
89197
  setSkills(result.skills);
88418
89198
  } catch {
@@ -88634,7 +89414,7 @@ function parseInitialState(initialSettings) {
88634
89414
  return {
88635
89415
  trigger: initialSettings.trigger === "off" || initialSettings.trigger === "step-count" || initialSettings.trigger === "compaction-event" ? initialSettings.trigger : "step-count",
88636
89416
  behavior: initialSettings.behavior === "auto-launch" ? "auto-launch" : "reminder",
88637
- stepCount: String(Number.isInteger(initialSettings.stepCount) && initialSettings.stepCount > 0 ? initialSettings.stepCount : Number(DEFAULT_STEP_COUNT))
89417
+ stepCount: String(Number.isInteger(initialSettings.stepCount) && initialSettings.stepCount > 0 ? initialSettings.stepCount : Number(DEFAULT_STEP_COUNT2))
88638
89418
  };
88639
89419
  }
88640
89420
  function parseStepCount(raw) {
@@ -88696,7 +89476,7 @@ function SleeptimeSelector({
88696
89476
  });
88697
89477
  return;
88698
89478
  }
88699
- const fallbackStepCount = parseStepCount(stepCountInput) ?? Number(DEFAULT_STEP_COUNT);
89479
+ const fallbackStepCount = parseStepCount(stepCountInput) ?? Number(DEFAULT_STEP_COUNT2);
88700
89480
  onSave({
88701
89481
  trigger,
88702
89482
  behavior: memfsEnabled ? behavior : "reminder",
@@ -88953,7 +89733,7 @@ function SleeptimeSelector({
88953
89733
  ]
88954
89734
  }, undefined, true, undefined, this);
88955
89735
  }
88956
- var import_react81, jsx_dev_runtime58, SOLID_LINE23 = "─", DEFAULT_STEP_COUNT = "25", BEHAVIOR_OPTIONS;
89736
+ var import_react81, jsx_dev_runtime58, SOLID_LINE23 = "─", DEFAULT_STEP_COUNT2 = "25", BEHAVIOR_OPTIONS;
88957
89737
  var init_SleeptimeSelector = __esm(async () => {
88958
89738
  init_useTerminalWidth();
88959
89739
  init_colors();
@@ -92377,157 +93157,6 @@ var init_contextChart = __esm(() => {
92377
93157
  ].map(hexToFgAnsi);
92378
93158
  });
92379
93159
 
92380
- // src/cli/helpers/memoryReminder.ts
92381
- function isValidStepCount(value) {
92382
- return typeof value === "number" && Number.isFinite(value) && Number.isInteger(value) && value > 0;
92383
- }
92384
- function normalizeStepCount(value, fallback) {
92385
- return isValidStepCount(value) ? value : fallback;
92386
- }
92387
- function normalizeTrigger(value, fallback) {
92388
- if (value === "off" || value === "step-count" || value === "compaction-event") {
92389
- return value;
92390
- }
92391
- return fallback;
92392
- }
92393
- function normalizeBehavior(value, fallback) {
92394
- if (value === "reminder" || value === "auto-launch") {
92395
- return value;
92396
- }
92397
- return fallback;
92398
- }
92399
- function applyExplicitReflectionOverrides(base2, raw) {
92400
- return {
92401
- trigger: normalizeTrigger(raw.reflectionTrigger, base2.trigger),
92402
- behavior: normalizeBehavior(raw.reflectionBehavior, base2.behavior),
92403
- stepCount: normalizeStepCount(raw.reflectionStepCount, base2.stepCount)
92404
- };
92405
- }
92406
- function legacyModeToReflectionSettings(mode) {
92407
- if (typeof mode === "number") {
92408
- return {
92409
- trigger: "step-count",
92410
- behavior: "reminder",
92411
- stepCount: normalizeStepCount(mode, DEFAULT_STEP_COUNT2)
92412
- };
92413
- }
92414
- if (mode === null) {
92415
- return {
92416
- trigger: "off",
92417
- behavior: DEFAULT_REFLECTION_SETTINGS.behavior,
92418
- stepCount: DEFAULT_REFLECTION_SETTINGS.stepCount
92419
- };
92420
- }
92421
- if (mode === "compaction") {
92422
- return {
92423
- trigger: "compaction-event",
92424
- behavior: "reminder",
92425
- stepCount: DEFAULT_REFLECTION_SETTINGS.stepCount
92426
- };
92427
- }
92428
- if (mode === "auto-compaction") {
92429
- return {
92430
- trigger: "compaction-event",
92431
- behavior: "auto-launch",
92432
- stepCount: DEFAULT_REFLECTION_SETTINGS.stepCount
92433
- };
92434
- }
92435
- return { ...DEFAULT_REFLECTION_SETTINGS };
92436
- }
92437
- function reflectionSettingsToLegacyMode(settings) {
92438
- if (settings.trigger === "off") {
92439
- return null;
92440
- }
92441
- if (settings.trigger === "compaction-event") {
92442
- return settings.behavior === "auto-launch" ? "auto-compaction" : "compaction";
92443
- }
92444
- return normalizeStepCount(settings.stepCount, DEFAULT_STEP_COUNT2);
92445
- }
92446
- function getReflectionSettings() {
92447
- const globalSettings = settingsManager.getSettings();
92448
- let resolved = legacyModeToReflectionSettings(globalSettings.memoryReminderInterval);
92449
- resolved = applyExplicitReflectionOverrides(resolved, globalSettings);
92450
- try {
92451
- const localSettings = settingsManager.getLocalProjectSettings();
92452
- if (localSettings.memoryReminderInterval !== undefined) {
92453
- resolved = legacyModeToReflectionSettings(localSettings.memoryReminderInterval);
92454
- }
92455
- resolved = applyExplicitReflectionOverrides(resolved, localSettings);
92456
- } catch {}
92457
- return resolved;
92458
- }
92459
- function shouldFireStepCountTrigger(turnCount, settings = getReflectionSettings()) {
92460
- if (settings.trigger !== "step-count") {
92461
- return false;
92462
- }
92463
- const stepCount = normalizeStepCount(settings.stepCount, DEFAULT_STEP_COUNT2);
92464
- return turnCount > 0 && turnCount % stepCount === 0;
92465
- }
92466
- async function buildMemfsAwareMemoryReminder(agentId, trigger) {
92467
- if (settingsManager.isMemfsEnabled(agentId)) {
92468
- debugLog("memory", `Reflection reminder fired (${trigger}, agent ${agentId})`);
92469
- const { MEMORY_REFLECTION_REMINDER: MEMORY_REFLECTION_REMINDER3 } = await Promise.resolve().then(() => (init_promptAssets(), exports_promptAssets));
92470
- return MEMORY_REFLECTION_REMINDER3;
92471
- }
92472
- debugLog("memory", `Memory check reminder fired (${trigger}, agent ${agentId})`);
92473
- const { MEMORY_CHECK_REMINDER: MEMORY_CHECK_REMINDER3 } = await Promise.resolve().then(() => (init_promptAssets(), exports_promptAssets));
92474
- return MEMORY_CHECK_REMINDER3;
92475
- }
92476
- async function buildCompactionMemoryReminder(agentId) {
92477
- return buildMemfsAwareMemoryReminder(agentId, "compaction");
92478
- }
92479
- async function buildMemoryReminder(turnCount, agentId) {
92480
- const reflectionSettings = getReflectionSettings();
92481
- if (reflectionSettings.trigger !== "step-count") {
92482
- return "";
92483
- }
92484
- if (shouldFireStepCountTrigger(turnCount, reflectionSettings)) {
92485
- debugLog("memory", `Turn-based memory reminder fired (turn ${turnCount}, interval ${reflectionSettings.stepCount}, agent ${agentId})`);
92486
- return buildMemfsAwareMemoryReminder(agentId, "interval");
92487
- }
92488
- return "";
92489
- }
92490
- function parseMemoryPreference(questions, answers) {
92491
- for (const q of questions) {
92492
- if (!q.question)
92493
- continue;
92494
- const questionLower = q.question.toLowerCase();
92495
- const headerLower = q.header?.toLowerCase() || "";
92496
- if (questionLower.includes("memory") || questionLower.includes("remember") || headerLower.includes("memory")) {
92497
- const answer = answers[q.question]?.toLowerCase() || "";
92498
- if (answer.includes("frequent")) {
92499
- settingsManager.updateLocalProjectSettings({
92500
- memoryReminderInterval: MEMORY_INTERVAL_FREQUENT,
92501
- reflectionTrigger: "step-count",
92502
- reflectionBehavior: "reminder",
92503
- reflectionStepCount: MEMORY_INTERVAL_FREQUENT
92504
- });
92505
- return true;
92506
- } else if (answer.includes("occasional")) {
92507
- settingsManager.updateLocalProjectSettings({
92508
- memoryReminderInterval: MEMORY_INTERVAL_OCCASIONAL,
92509
- reflectionTrigger: "step-count",
92510
- reflectionBehavior: "reminder",
92511
- reflectionStepCount: MEMORY_INTERVAL_OCCASIONAL
92512
- });
92513
- return true;
92514
- }
92515
- break;
92516
- }
92517
- }
92518
- return false;
92519
- }
92520
- var MEMORY_INTERVAL_FREQUENT = 5, MEMORY_INTERVAL_OCCASIONAL = 10, DEFAULT_STEP_COUNT2 = 25, DEFAULT_REFLECTION_SETTINGS;
92521
- var init_memoryReminder = __esm(async () => {
92522
- init_debug();
92523
- await init_settings_manager();
92524
- DEFAULT_REFLECTION_SETTINGS = {
92525
- trigger: "compaction-event",
92526
- behavior: "reminder",
92527
- stepCount: DEFAULT_STEP_COUNT2
92528
- };
92529
- });
92530
-
92531
93160
  // src/cli/helpers/queuedMessageParts.ts
92532
93161
  function getQueuedNotificationSummaries(queued) {
92533
93162
  const summaries = [];
@@ -92683,6 +93312,15 @@ function buildSessionContext(options) {
92683
93312
  lastRunInfo = "(failed to parse last run time)";
92684
93313
  }
92685
93314
  }
93315
+ const showMemoryDir = (() => {
93316
+ try {
93317
+ return settingsManager.isMemfsEnabled(agentInfo.id);
93318
+ } catch {
93319
+ return false;
93320
+ }
93321
+ })();
93322
+ const memoryDirLine = showMemoryDir ? `
93323
+ - **Memory directory (also stored in \`MEMORY_DIR\` env var)**: \`${getMemoryFilesystemRoot(agentInfo.id)}\`` : "";
92686
93324
  let context3 = `${SYSTEM_REMINDER_OPEN}
92687
93325
  This is an automated message providing context about the user's environment.
92688
93326
  The user has just initiated a new connection via the [Letta Code CLI client](https://docs.letta.com/letta-code/index.md).
@@ -92720,7 +93358,7 @@ ${gitInfo.status}
92720
93358
  }
92721
93359
  context3 += `
92722
93360
  ## Agent Information (i.e. information about you)
92723
- - **Agent ID**: ${agentInfo.id}
93361
+ - **Agent ID (also stored in \`AGENT_ID\` env var)**: ${agentInfo.id}${memoryDirLine}
92724
93362
  - **Agent name**: ${agentInfo.name || "(unnamed)"} (the user can change this with /rename)
92725
93363
  - **Agent description**: ${agentInfo.description || "(no description)"} (the user can change this with /description)
92726
93364
  - **Last message**: ${lastRunInfo}
@@ -92732,6 +93370,7 @@ ${SYSTEM_REMINDER_CLOSE}`;
92732
93370
  }
92733
93371
  }
92734
93372
  var init_sessionContext = __esm(async () => {
93373
+ init_memoryFilesystem();
92735
93374
  init_oauth();
92736
93375
  init_constants();
92737
93376
  init_version();
@@ -94835,19 +95474,7 @@ async function isRetriableError(stopReason, lastRunId) {
94835
95474
  const metaError = run.metadata?.error;
94836
95475
  const errorType = metaError?.error_type ?? metaError?.error?.error_type;
94837
95476
  const detail = metaError?.detail ?? metaError?.error?.detail ?? "";
94838
- const is4xxError = /Error code: 4\d{2}/.test(detail);
94839
- if (errorType === "llm_error" && !is4xxError)
94840
- return true;
94841
- const llmProviderPatterns = [
94842
- "Anthropic API error",
94843
- "OpenAI API error",
94844
- "Google Vertex API error",
94845
- "overloaded",
94846
- "api_error",
94847
- "Network error",
94848
- "Connection error during Anthropic streaming"
94849
- ];
94850
- if (llmProviderPatterns.some((pattern) => detail.includes(pattern)) && !is4xxError) {
95477
+ if (shouldRetryRunMetadataError(errorType, detail)) {
94851
95478
  return true;
94852
95479
  }
94853
95480
  return false;
@@ -95029,7 +95656,8 @@ function App2({
95029
95656
  tokenStreaming = false,
95030
95657
  showCompactions = false,
95031
95658
  agentProvenance = null,
95032
- releaseNotes = null
95659
+ releaseNotes = null,
95660
+ sessionContextReminderEnabled = true
95033
95661
  }) {
95034
95662
  import_react94.useEffect(() => {
95035
95663
  prefetchAvailableModelHandles();
@@ -96407,7 +97035,11 @@ ${newState.originalPrompt}`
96407
97035
  stream2 = await sendMessageStream(conversationIdRef.current, currentInput, { agentId: agentIdRef.current });
96408
97036
  } catch (preStreamError) {
96409
97037
  const errorDetail = extractConflictDetail(preStreamError);
96410
- const preStreamAction = getPreStreamErrorAction(errorDetail, conversationBusyRetriesRef.current, CONVERSATION_BUSY_MAX_RETRIES2);
97038
+ const preStreamAction = getPreStreamErrorAction(errorDetail, conversationBusyRetriesRef.current, CONVERSATION_BUSY_MAX_RETRIES2, {
97039
+ status: preStreamError instanceof APIError2 ? preStreamError.status : undefined,
97040
+ transientRetries: llmApiErrorRetriesRef.current,
97041
+ maxTransientRetries: LLM_API_ERROR_MAX_RETRIES2
97042
+ });
96411
97043
  if (shouldAttemptApprovalRecovery({
96412
97044
  approvalPendingDetected: preStreamAction === "resolve_approval_pending",
96413
97045
  retries: llmApiErrorRetriesRef.current,
@@ -96453,6 +97085,37 @@ ${newState.originalPrompt}`
96453
97085
  continue;
96454
97086
  }
96455
97087
  }
97088
+ if (preStreamAction === "retry_transient") {
97089
+ llmApiErrorRetriesRef.current += 1;
97090
+ const attempt = llmApiErrorRetriesRef.current;
97091
+ const retryAfterMs = preStreamError instanceof APIError2 ? parseRetryAfterHeaderMs(preStreamError.headers?.get("retry-after")) : null;
97092
+ const delayMs = retryAfterMs ?? 1000 * 2 ** (attempt - 1);
97093
+ const statusId = uid4("status");
97094
+ buffersRef.current.byId.set(statusId, {
97095
+ kind: "status",
97096
+ id: statusId,
97097
+ lines: [getRetryStatusMessage(errorDetail)]
97098
+ });
97099
+ buffersRef.current.order.push(statusId);
97100
+ refreshDerived();
97101
+ let cancelled = false;
97102
+ const startTime = Date.now();
97103
+ while (Date.now() - startTime < delayMs) {
97104
+ if (abortControllerRef.current?.signal.aborted || userCancelledRef.current) {
97105
+ cancelled = true;
97106
+ break;
97107
+ }
97108
+ await new Promise((resolve23) => setTimeout(resolve23, 100));
97109
+ }
97110
+ buffersRef.current.byId.delete(statusId);
97111
+ buffersRef.current.order = buffersRef.current.order.filter((id) => id !== statusId);
97112
+ refreshDerived();
97113
+ if (!cancelled) {
97114
+ buffersRef.current.interrupted = false;
97115
+ conversationBusyRetriesRef.current = 0;
97116
+ continue;
97117
+ }
97118
+ }
96456
97119
  conversationBusyRetriesRef.current = 0;
96457
97120
  const hasApprovalInPayload2 = currentInput.some((item) => item?.type === "approval");
96458
97121
  if (hasApprovalInPayload2) {
@@ -97317,6 +97980,7 @@ ${feedback}
97317
97980
  pendingInterruptRecoveryConversationIdRef.current = conversationIdRef.current;
97318
97981
  userCancelledRef.current = true;
97319
97982
  setStreaming(false);
97983
+ resetTrajectoryBases();
97320
97984
  setIsExecutingTool(false);
97321
97985
  toolResultsInFlightRef.current = false;
97322
97986
  refreshDerived();
@@ -97351,6 +98015,7 @@ ${feedback}
97351
98015
  conversationGenerationRef.current += 1;
97352
98016
  processingConversationRef.current = 0;
97353
98017
  setStreaming(false);
98018
+ resetTrajectoryBases();
97354
98019
  toolResultsInFlightRef.current = false;
97355
98020
  if (!toolsCancelled) {
97356
98021
  appendError(INTERRUPT_MESSAGE, true);
@@ -97430,7 +98095,8 @@ ${feedback}
97430
98095
  pendingApprovals,
97431
98096
  autoHandledResults,
97432
98097
  autoDeniedApprovals,
97433
- queueApprovalResults
98098
+ queueApprovalResults,
98099
+ resetTrajectoryBases
97434
98100
  ]);
97435
98101
  const processConversationRef = import_react94.useRef(processConversation);
97436
98102
  import_react94.useEffect(() => {
@@ -97867,6 +98533,14 @@ ${SYSTEM_REMINDER_CLOSE}` : "";
97867
98533
  debugLog("queue", `Bumping dequeueEpoch: userCancelledRef was reset, ${messageQueue.length} message(s) queued, agent not busy`);
97868
98534
  setDequeueEpoch((e) => e + 1);
97869
98535
  }
98536
+ const isSlashCommand = userTextForInput.startsWith("/");
98537
+ if (isAgentBusy() && isSlashCommand) {
98538
+ const attemptedCommand = userTextForInput.split(/\s+/)[0] || "/";
98539
+ const disabledMessage = `'${attemptedCommand}' is disabled while the agent is running.`;
98540
+ const cmd = commandRunner.start(userTextForInput, disabledMessage);
98541
+ cmd.fail(disabledMessage);
98542
+ return { submitted: true };
98543
+ }
97870
98544
  const shouldBypassQueue = isInteractiveCommand(userTextForInput) || isNonStateCommand(userTextForInput);
97871
98545
  if (isAgentBusy() && !shouldBypassQueue) {
97872
98546
  setMessageQueue((prev) => {
@@ -99131,9 +99805,13 @@ ${recentCommits}
99131
99805
  ## Memory Filesystem Location
99132
99806
 
99133
99807
  Your memory blocks are synchronized with the filesystem at:
99134
- \`~/.letta/agents/${agentId}/memory/\`
99808
+ \`${getMemoryFilesystemRoot(agentId)}\`
99809
+
99810
+ Environment variables available in Letta Code:
99811
+ - \`AGENT_ID=${agentId}\`
99812
+ - \`MEMORY_DIR=${getMemoryFilesystemRoot(agentId)}\`
99135
99813
 
99136
- Use this path when working with memory files during initialization.
99814
+ Use \`$MEMORY_DIR\` when working with memory files during initialization.
99137
99815
  ` : "";
99138
99816
  const initMessage = `${SYSTEM_REMINDER_OPEN}
99139
99817
  The user has requested memory initialization via /init.
@@ -99243,7 +99921,7 @@ ${SYSTEM_REMINDER_CLOSE}`)
99243
99921
  }
99244
99922
  let sessionContextReminder = "";
99245
99923
  const sessionContextEnabled = settingsManager.getSetting("sessionContextEnabled");
99246
- if (!hasSentSessionContextRef.current && sessionContextEnabled) {
99924
+ if (!hasSentSessionContextRef.current && sessionContextEnabled && sessionContextReminderEnabled) {
99247
99925
  const { buildSessionContext: buildSessionContext2 } = await init_sessionContext().then(() => exports_sessionContext);
99248
99926
  sessionContextReminder = buildSessionContext2({
99249
99927
  agentInfo: {
@@ -99353,25 +100031,30 @@ ${SYSTEM_REMINDER_CLOSE}
99353
100031
  };
99354
100032
  pushReminder(sessionContextReminder);
99355
100033
  {
99356
- if (!discoveredSkillsRef.current) {
99357
- try {
99358
- const { discoverSkills: discover, SKILLS_DIR: defaultDir } = await Promise.resolve().then(() => (init_skills(), exports_skills));
99359
- const { getSkillsDirectory: getSkillsDirectory2, getNoSkills: getNoSkills2 } = await Promise.resolve().then(() => (init_context(), exports_context));
99360
- const skillsDir = getSkillsDirectory2() || join30(process.cwd(), defaultDir);
99361
- const { skills } = await discover(skillsDir, agentId, {
99362
- skipBundled: getNoSkills2()
99363
- });
99364
- discoveredSkillsRef.current = skills;
99365
- } catch {
99366
- discoveredSkillsRef.current = [];
99367
- }
100034
+ const {
100035
+ discoverSkills: discover,
100036
+ SKILLS_DIR: defaultDir,
100037
+ formatSkillsAsSystemReminder: formatSkillsAsSystemReminder3
100038
+ } = await Promise.resolve().then(() => (init_skills(), exports_skills));
100039
+ const { getSkillsDirectory: getSkillsDirectory2, getSkillSources: getSkillSources2 } = await Promise.resolve().then(() => (init_context(), exports_context));
100040
+ const previousSkillsReminder = discoveredSkillsRef.current ? formatSkillsAsSystemReminder3(discoveredSkillsRef.current) : null;
100041
+ let latestSkills = discoveredSkillsRef.current ?? [];
100042
+ try {
100043
+ const skillsDir = getSkillsDirectory2() || join30(process.cwd(), defaultDir);
100044
+ const { skills } = await discover(skillsDir, agentId, {
100045
+ sources: getSkillSources2()
100046
+ });
100047
+ latestSkills = skills;
100048
+ } catch {}
100049
+ discoveredSkillsRef.current = latestSkills;
100050
+ const latestSkillsReminder = formatSkillsAsSystemReminder3(discoveredSkillsRef.current);
100051
+ if (previousSkillsReminder !== null && previousSkillsReminder !== latestSkillsReminder) {
100052
+ contextTrackerRef.current.pendingSkillsReinject = true;
99368
100053
  }
99369
100054
  const needsSkillsReinject = contextTrackerRef.current.pendingSkillsReinject;
99370
100055
  if (!hasInjectedSkillsRef.current || needsSkillsReinject) {
99371
- const { formatSkillsAsSystemReminder: formatSkillsAsSystemReminder3 } = await Promise.resolve().then(() => (init_skills(), exports_skills));
99372
- const skillsReminder = formatSkillsAsSystemReminder3(discoveredSkillsRef.current);
99373
- if (skillsReminder) {
99374
- pushReminder(skillsReminder);
100056
+ if (latestSkillsReminder) {
100057
+ pushReminder(latestSkillsReminder);
99375
100058
  }
99376
100059
  hasInjectedSkillsRef.current = true;
99377
100060
  contextTrackerRef.current.pendingSkillsReinject = false;
@@ -99833,6 +100516,7 @@ ${SYSTEM_REMINDER_CLOSE}
99833
100516
  pendingRalphConfig,
99834
100517
  openTrajectorySegment,
99835
100518
  resetTrajectoryBases,
100519
+ sessionContextReminderEnabled,
99836
100520
  appendTaskNotificationEvents
99837
100521
  ]);
99838
100522
  const onSubmitRef = import_react94.useRef(onSubmit);
@@ -100349,8 +101033,8 @@ ${guidance}`);
100349
101033
  output: `Switching system prompt to ${prompt.label}...`,
100350
101034
  phase: "running"
100351
101035
  });
100352
- const { updateAgentSystemPromptRaw: updateAgentSystemPromptRaw2 } = await init_modify().then(() => exports_modify);
100353
- const result = await updateAgentSystemPromptRaw2(agentId, prompt.content);
101036
+ const { updateAgentSystemPrompt: updateAgentSystemPrompt2 } = await init_modify().then(() => exports_modify);
101037
+ const result = await updateAgentSystemPrompt2(agentId, promptId);
100354
101038
  if (result.success) {
100355
101039
  setCurrentSystemPromptId(promptId);
100356
101040
  cmd.finish(`Switched system prompt to ${prompt.label}`, true);
@@ -102747,8 +103431,7 @@ async function createAgent2(nameOrOptions = DEFAULT_AGENT_NAME, model, embedding
102747
103431
  } else {
102748
103432
  systemPromptContent = await resolveSystemPrompt(options.systemPromptPreset);
102749
103433
  }
102750
- systemPromptContent = `${systemPromptContent}
102751
- ${SYSTEM_PROMPT_MEMORY_ADDON}`;
103434
+ systemPromptContent = reconcileMemoryPrompt(systemPromptContent, options.memoryPromptMode ?? "standard");
102752
103435
  if (options.systemPromptAppend) {
102753
103436
  systemPromptContent = `${systemPromptContent}
102754
103437
 
@@ -102818,6 +103501,7 @@ ${options.systemPromptAppend}`;
102818
103501
  var init_create3 = __esm(async () => {
102819
103502
  init_constants();
102820
103503
  init_memory();
103504
+ init_memoryPrompt();
102821
103505
  init_model();
102822
103506
  init_promptAssets();
102823
103507
  await __promiseAll([
@@ -103117,10 +103801,16 @@ async function updateAgentSystemPromptRaw2(agentId, systemPromptContent) {
103117
103801
  }
103118
103802
  async function updateAgentSystemPrompt2(agentId, systemPromptId) {
103119
103803
  try {
103120
- const { resolveSystemPrompt: resolveSystemPrompt3, SYSTEM_PROMPT_MEMORY_ADDON: SYSTEM_PROMPT_MEMORY_ADDON3 } = await Promise.resolve().then(() => (init_promptAssets(), exports_promptAssets));
103804
+ const { resolveSystemPrompt: resolveSystemPrompt3 } = await Promise.resolve().then(() => (init_promptAssets(), exports_promptAssets));
103805
+ const { detectMemoryPromptDrift: detectMemoryPromptDrift2, reconcileMemoryPrompt: reconcileMemoryPrompt2 } = await Promise.resolve().then(() => (init_memoryPrompt(), exports_memoryPrompt));
103806
+ const { settingsManager: settingsManager3 } = await init_settings_manager().then(() => exports_settings_manager);
103807
+ const client = await getClient2();
103808
+ const currentAgent = await client.agents.retrieve(agentId);
103121
103809
  const baseContent = await resolveSystemPrompt3(systemPromptId);
103122
- const systemPromptContent = `${baseContent}
103123
- ${SYSTEM_PROMPT_MEMORY_ADDON3}`;
103810
+ const settingIndicatesMemfs = settingsManager3.isMemfsEnabled(agentId);
103811
+ const promptIndicatesMemfs = detectMemoryPromptDrift2(currentAgent.system || "", "standard").some((drift) => drift.code === "memfs_language_with_standard_mode");
103812
+ const memoryMode = settingIndicatesMemfs || promptIndicatesMemfs ? "memfs" : "standard";
103813
+ const systemPromptContent = reconcileMemoryPrompt2(baseContent, memoryMode);
103124
103814
  const updateResult = await updateAgentSystemPromptRaw2(agentId, systemPromptContent);
103125
103815
  if (!updateResult.success) {
103126
103816
  return {
@@ -103129,7 +103819,6 @@ ${SYSTEM_PROMPT_MEMORY_ADDON3}`;
103129
103819
  agent: null
103130
103820
  };
103131
103821
  }
103132
- const client = await getClient2();
103133
103822
  const agent = await client.agents.retrieve(agentId);
103134
103823
  return {
103135
103824
  success: true,
@@ -103148,15 +103837,10 @@ async function updateAgentSystemPromptMemfs2(agentId, enableMemfs) {
103148
103837
  try {
103149
103838
  const client = await getClient2();
103150
103839
  const agent = await client.agents.retrieve(agentId);
103151
- let currentSystemPrompt = agent.system || "";
103152
- const { SYSTEM_PROMPT_MEMFS_ADDON: SYSTEM_PROMPT_MEMFS_ADDON3, SYSTEM_PROMPT_MEMORY_ADDON: SYSTEM_PROMPT_MEMORY_ADDON3 } = await Promise.resolve().then(() => (init_promptAssets(), exports_promptAssets));
103153
- const memoryHeaderRegex = /\n#{1,2} Memory\b[\s\S]*?(?=\n#{1,2} (?!Memory|Filesystem|Structure|How It Works|Syncing|History)[^\n]|$)/;
103154
- currentSystemPrompt = currentSystemPrompt.replace(memoryHeaderRegex, "");
103155
- const addon = enableMemfs ? SYSTEM_PROMPT_MEMFS_ADDON3 : SYSTEM_PROMPT_MEMORY_ADDON3;
103156
- currentSystemPrompt = `${currentSystemPrompt}
103157
- ${addon}`;
103840
+ const { reconcileMemoryPrompt: reconcileMemoryPrompt2 } = await Promise.resolve().then(() => (init_memoryPrompt(), exports_memoryPrompt));
103841
+ const nextSystemPrompt = reconcileMemoryPrompt2(agent.system || "", enableMemfs ? "memfs" : "standard");
103158
103842
  await client.agents.update(agentId, {
103159
- system: currentSystemPrompt
103843
+ system: nextSystemPrompt
103160
103844
  });
103161
103845
  return {
103162
103846
  success: true,
@@ -103275,19 +103959,22 @@ async function applyMemfsFlags2(agentId, memfsFlag, noMemfsFlag, options) {
103275
103959
  if (!serverUrl.includes("api.letta.com")) {
103276
103960
  throw new Error("--memfs is only available on Letta Cloud (api.letta.com).");
103277
103961
  }
103278
- settingsManager3.setMemfsEnabled(agentId, true);
103279
- } else if (noMemfsFlag) {
103280
- settingsManager3.setMemfsEnabled(agentId, false);
103281
103962
  }
103282
- const isEnabled = settingsManager3.isMemfsEnabled(agentId);
103963
+ const hasExplicitToggle = Boolean(memfsFlag || noMemfsFlag);
103964
+ const targetEnabled = memfsFlag ? true : noMemfsFlag ? false : settingsManager3.isMemfsEnabled(agentId);
103965
+ if (hasExplicitToggle) {
103966
+ const { updateAgentSystemPromptMemfs: updateAgentSystemPromptMemfs3 } = await init_modify().then(() => exports_modify);
103967
+ const promptUpdate = await updateAgentSystemPromptMemfs3(agentId, targetEnabled);
103968
+ if (!promptUpdate.success) {
103969
+ throw new Error(promptUpdate.message);
103970
+ }
103971
+ settingsManager3.setMemfsEnabled(agentId, targetEnabled);
103972
+ }
103973
+ const isEnabled = hasExplicitToggle ? targetEnabled : settingsManager3.isMemfsEnabled(agentId);
103283
103974
  if (isEnabled && memfsFlag) {
103284
103975
  const { detachMemoryTools: detachMemoryTools2 } = await init_toolset().then(() => exports_toolset);
103285
103976
  await detachMemoryTools2(agentId);
103286
103977
  }
103287
- if (memfsFlag || noMemfsFlag) {
103288
- const { updateAgentSystemPromptMemfs: updateAgentSystemPromptMemfs3 } = await init_modify().then(() => exports_modify);
103289
- await updateAgentSystemPromptMemfs3(agentId, isEnabled);
103290
- }
103291
103978
  let pullSummary;
103292
103979
  if (isEnabled) {
103293
103980
  const { addGitMemoryTag: addGitMemoryTag2, isGitRepo: isGitRepo2, cloneMemoryRepo: cloneMemoryRepo2, pullMemory: pullMemory2 } = await init_memoryGit().then(() => exports_memoryGit);
@@ -103624,6 +104311,7 @@ async function getClient() {
103624
104311
  }
103625
104312
 
103626
104313
  // src/agent/context.ts
104314
+ init_skillSources();
103627
104315
  var CONTEXT_KEY = Symbol.for("@letta/agentContext");
103628
104316
  function getContext() {
103629
104317
  const global2 = globalThis;
@@ -103631,17 +104319,17 @@ function getContext() {
103631
104319
  global2[CONTEXT_KEY] = {
103632
104320
  agentId: null,
103633
104321
  skillsDirectory: null,
103634
- noSkills: false,
104322
+ skillSources: [...ALL_SKILL_SOURCES],
103635
104323
  conversationId: null
103636
104324
  };
103637
104325
  }
103638
104326
  return global2[CONTEXT_KEY];
103639
104327
  }
103640
104328
  var context = getContext();
103641
- function setAgentContext(agentId, skillsDirectory, noSkills) {
104329
+ function setAgentContext(agentId, skillsDirectory, skillSources) {
103642
104330
  context.agentId = agentId;
103643
104331
  context.skillsDirectory = skillsDirectory || null;
103644
- context.noSkills = noSkills ?? false;
104332
+ context.skillSources = skillSources !== undefined ? [...skillSources] : [...ALL_SKILL_SOURCES];
103645
104333
  }
103646
104334
  function setConversationId(conversationId) {
103647
104335
  context.conversationId = conversationId;
@@ -103669,6 +104357,54 @@ var MEMORY_BLOCK_LABELS2 = [
103669
104357
  ];
103670
104358
  var ISOLATED_BLOCK_LABELS2 = [];
103671
104359
 
104360
+ // src/agent/skillSources.ts
104361
+ var ALL_SKILL_SOURCES2 = [
104362
+ "bundled",
104363
+ "global",
104364
+ "agent",
104365
+ "project"
104366
+ ];
104367
+ var VALID_SKILL_SOURCE_SPECIFIERS2 = [
104368
+ "all",
104369
+ ...ALL_SKILL_SOURCES2
104370
+ ];
104371
+ function isSkillSource2(value) {
104372
+ return ALL_SKILL_SOURCES2.includes(value);
104373
+ }
104374
+ function normalizeSkillSources2(sources) {
104375
+ const sourceSet = new Set(sources);
104376
+ return ALL_SKILL_SOURCES2.filter((source) => sourceSet.has(source));
104377
+ }
104378
+ function parseSkillSourcesList2(skillSourcesRaw) {
104379
+ const tokens = skillSourcesRaw.split(",").map((source) => source.trim()).filter((source) => source.length > 0);
104380
+ if (tokens.length === 0) {
104381
+ throw new Error("--skill-sources must include at least one source (e.g. bundled,project)");
104382
+ }
104383
+ const sources = [];
104384
+ for (const token of tokens) {
104385
+ const source = token;
104386
+ if (!VALID_SKILL_SOURCE_SPECIFIERS2.includes(source)) {
104387
+ throw new Error(`Invalid skill source "${token}". Valid values: ${VALID_SKILL_SOURCE_SPECIFIERS2.join(", ")}`);
104388
+ }
104389
+ if (source === "all") {
104390
+ sources.push(...ALL_SKILL_SOURCES2);
104391
+ continue;
104392
+ }
104393
+ if (isSkillSource2(source)) {
104394
+ sources.push(source);
104395
+ }
104396
+ }
104397
+ return normalizeSkillSources2(sources);
104398
+ }
104399
+ function resolveSkillSourcesSelection2(input) {
104400
+ if (input.noSkills) {
104401
+ return [];
104402
+ }
104403
+ const configuredSources = input.skillSourcesRaw ? parseSkillSourcesList2(input.skillSourcesRaw) : [...ALL_SKILL_SOURCES2];
104404
+ const filteredSources = input.noBundledSkills ? configuredSources.filter((source) => source !== "bundled") : configuredSources;
104405
+ return normalizeSkillSources2(filteredSources);
104406
+ }
104407
+
103672
104408
  // src/index.ts
103673
104409
  init_oauth2();
103674
104410
 
@@ -106382,6 +107118,18 @@ if (!globalThis.__lettaSettingsManager) {
106382
107118
  }
106383
107119
  var settingsManager2 = globalThis.__lettaSettingsManager;
106384
107120
 
107121
+ // src/startup-auto-update.ts
107122
+ function startStartupAutoUpdateCheck(checkAndAutoUpdate, logError = console.error) {
107123
+ checkAndAutoUpdate().then((result) => {
107124
+ if (result?.enotemptyFailed) {
107125
+ logError(`
107126
+ Auto-update failed due to filesystem issue (ENOTEMPTY).`);
107127
+ logError(`Fix: rm -rf $(npm prefix -g)/lib/node_modules/@letta-ai/letta-code && npm i -g @letta-ai/letta-code
107128
+ `);
107129
+ }
107130
+ }).catch(() => {});
107131
+ }
107132
+
106385
107133
  // src/telemetry/index.ts
106386
107134
  init_http_headers();
106387
107135
  await init_settings_manager();
@@ -106893,10 +107641,21 @@ OPTIONS
106893
107641
  Emit stream_event wrappers for each chunk (stream-json only)
106894
107642
  --from-agent <id> Inject agent-to-agent system reminder (headless mode)
106895
107643
  --skills <path> Custom path to skills directory (default: .skills in current directory)
107644
+ --skill-sources <csv> Skill sources: all,bundled,global,agent,project (default: all)
107645
+ --no-skills Disable all skill sources
107646
+ --no-bundled-skills Disable bundled skills only
106896
107647
  --import <path> Create agent from an AgentFile (.af) template
106897
107648
  Use @author/name to import from the agent registry
106898
107649
  --memfs Enable memory filesystem for this agent
106899
107650
  --no-memfs Disable memory filesystem for this agent
107651
+ --no-system-info-reminder
107652
+ Disable first-turn environment reminder (device/git/cwd context)
107653
+ --reflection-trigger <mode>
107654
+ Sleeptime trigger: off, step-count, compaction-event
107655
+ --reflection-behavior <mode>
107656
+ Sleeptime behavior: reminder, auto-launch
107657
+ --reflection-step-count <n>
107658
+ Sleeptime step-count interval (positive integer)
106900
107659
 
106901
107660
  SUBCOMMANDS (JSON-only)
106902
107661
  letta memfs status --agent <id>
@@ -107095,14 +107854,7 @@ async function main() {
107095
107854
  }
107096
107855
  telemetry.init();
107097
107856
  const { checkAndAutoUpdate: checkAndAutoUpdate2 } = await Promise.resolve().then(() => (init_auto_update(), exports_auto_update));
107098
- checkAndAutoUpdate2().then((result) => {
107099
- if (result?.enotemptyFailed) {
107100
- console.error(`
107101
- Auto-update failed due to filesystem issue (ENOTEMPTY).`);
107102
- console.error(`Fix: rm -rf $(npm prefix -g)/lib/node_modules/@letta-ai/letta-code && npm i -g @letta-ai/letta-code
107103
- `);
107104
- }
107105
- }).catch(() => {});
107857
+ startStartupAutoUpdateCheck(checkAndAutoUpdate2);
107106
107858
  const { cleanupOldOverflowFiles: cleanupOldOverflowFiles2 } = await Promise.resolve().then(() => (init_overflow2(), exports_overflow));
107107
107859
  Promise.resolve().then(() => {
107108
107860
  try {
@@ -107149,6 +107901,7 @@ Auto-update failed due to filesystem issue (ENOTEMPTY).`);
107149
107901
  "include-partial-messages": { type: "boolean" },
107150
107902
  "from-agent": { type: "string" },
107151
107903
  skills: { type: "string" },
107904
+ "skill-sources": { type: "string" },
107152
107905
  "pre-load-skills": { type: "string" },
107153
107906
  "from-af": { type: "string" },
107154
107907
  import: { type: "string" },
@@ -107156,6 +107909,11 @@ Auto-update failed due to filesystem issue (ENOTEMPTY).`);
107156
107909
  memfs: { type: "boolean" },
107157
107910
  "no-memfs": { type: "boolean" },
107158
107911
  "no-skills": { type: "boolean" },
107912
+ "no-bundled-skills": { type: "boolean" },
107913
+ "no-system-info-reminder": { type: "boolean" },
107914
+ "reflection-trigger": { type: "string" },
107915
+ "reflection-behavior": { type: "string" },
107916
+ "reflection-step-count": { type: "string" },
107159
107917
  "max-turns": { type: "string" }
107160
107918
  },
107161
107919
  strict: true,
@@ -107178,6 +107936,10 @@ Note: Flags should use double dashes for full names (e.g., --yolo, not -yolo)`);
107178
107936
  const command = positionals[2];
107179
107937
  if (values.help) {
107180
107938
  printHelp();
107939
+ const helpDelayMs = Number.parseInt(process.env.LETTA_TEST_HELP_EXIT_DELAY_MS ?? "", 10);
107940
+ if (Number.isFinite(helpDelayMs) && helpDelayMs > 0) {
107941
+ await new Promise((resolve24) => setTimeout(resolve24, helpDelayMs));
107942
+ }
107181
107943
  process.exit(0);
107182
107944
  }
107183
107945
  if (values.version) {
@@ -107234,7 +107996,24 @@ Note: Flags should use double dashes for full names (e.g., --yolo, not -yolo)`);
107234
107996
  const skillsDirectory = values.skills ?? undefined;
107235
107997
  const memfsFlag = values.memfs;
107236
107998
  const noMemfsFlag = values["no-memfs"];
107999
+ const requestedMemoryPromptMode = memfsFlag ? "memfs" : noMemfsFlag ? "standard" : undefined;
108000
+ const shouldAutoEnableMemfsForNewAgent = !memfsFlag && !noMemfsFlag;
107237
108001
  const noSkillsFlag = values["no-skills"];
108002
+ const noBundledSkillsFlag = values["no-bundled-skills"];
108003
+ const skillSourcesRaw = values["skill-sources"];
108004
+ const noSystemInfoReminderFlag = values["no-system-info-reminder"];
108005
+ const resolvedSkillSources = (() => {
108006
+ try {
108007
+ return resolveSkillSourcesSelection2({
108008
+ skillSourcesRaw,
108009
+ noSkills: noSkillsFlag,
108010
+ noBundledSkills: noBundledSkillsFlag
108011
+ });
108012
+ } catch (error) {
108013
+ console.error(error instanceof Error ? `Error: ${error.message}` : String(error));
108014
+ process.exit(1);
108015
+ }
108016
+ })();
107238
108017
  const fromAfFile = values.import ?? values["from-af"];
107239
108018
  const isHeadless = values.prompt || values.run || !process.stdin.isTTY;
107240
108019
  if (command && !isHeadless) {
@@ -107500,7 +108279,7 @@ Error: ${message}`);
107500
108279
  await loadTools2(modelForTools);
107501
108280
  markMilestone2("TOOLS_LOADED");
107502
108281
  const { handleHeadlessCommand: handleHeadlessCommand2 } = await init_headless().then(() => exports_headless);
107503
- await handleHeadlessCommand2(process.argv, specifiedModel, skillsDirectory, noSkillsFlag);
108282
+ await handleHeadlessCommand2(process.argv, specifiedModel, skillsDirectory, resolvedSkillSources, !noSystemInfoReminderFlag);
107504
108283
  return;
107505
108284
  }
107506
108285
  markMilestone2("TUI_MODE_START");
@@ -107927,13 +108706,16 @@ Error: ${message}`);
107927
108706
  skillsDirectory: skillsDirectory2,
107928
108707
  parallelToolCalls: true,
107929
108708
  systemPromptPreset: systemPromptPreset2,
108709
+ memoryPromptMode: requestedMemoryPromptMode,
107930
108710
  initBlocks: initBlocks2,
107931
108711
  baseTools: baseTools2
107932
108712
  });
107933
108713
  agent = result.agent;
107934
108714
  setAgentProvenance(result.provenance);
107935
- const { enableMemfsIfCloud: enableMemfsIfCloud3 } = await Promise.resolve().then(() => (init_memoryFilesystem2(), exports_memoryFilesystem2));
107936
- await enableMemfsIfCloud3(agent.id);
108715
+ if (shouldAutoEnableMemfsForNewAgent) {
108716
+ const { enableMemfsIfCloud: enableMemfsIfCloud3 } = await Promise.resolve().then(() => (init_memoryFilesystem2(), exports_memoryFilesystem2));
108717
+ await enableMemfsIfCloud3(agent.id);
108718
+ }
107937
108719
  }
107938
108720
  if (!agent && resumingAgentId) {
107939
108721
  try {
@@ -107977,7 +108759,7 @@ Error: ${message}`);
107977
108759
  }
107978
108760
  } catch {}
107979
108761
  }
107980
- setAgentContext(agent.id, skillsDirectory2, noSkillsFlag);
108762
+ setAgentContext(agent.id, skillsDirectory2, resolvedSkillSources);
107981
108763
  const isSubagent = process.env.LETTA_CODE_AGENT_ROLE === "subagent";
107982
108764
  try {
107983
108765
  const { applyMemfsFlags: applyMemfsFlags3 } = await Promise.resolve().then(() => (init_memoryFilesystem2(), exports_memoryFilesystem2));
@@ -108199,7 +108981,8 @@ Error during initialization: ${message}`);
108199
108981
  tokenStreaming: settings.tokenStreaming,
108200
108982
  showCompactions: settings.showCompactions,
108201
108983
  agentProvenance,
108202
- releaseNotes: releaseNotes2
108984
+ releaseNotes: releaseNotes2,
108985
+ sessionContextReminderEnabled: !noSystemInfoReminderFlag
108203
108986
  });
108204
108987
  }
108205
108988
  return React14.createElement(App3, {
@@ -108215,7 +108998,8 @@ Error during initialization: ${message}`);
108215
108998
  tokenStreaming: settings.tokenStreaming,
108216
108999
  showCompactions: settings.showCompactions,
108217
109000
  agentProvenance,
108218
- releaseNotes: releaseNotes2
109001
+ releaseNotes: releaseNotes2,
109002
+ sessionContextReminderEnabled: !noSystemInfoReminderFlag
108219
109003
  });
108220
109004
  }
108221
109005
  markMilestone2("REACT_RENDER_START");
@@ -108237,4 +109021,4 @@ Error during initialization: ${message}`);
108237
109021
  }
108238
109022
  main();
108239
109023
 
108240
- //# debugId=2DEF3E9D56D2EFE964756E2164756E21
109024
+ //# debugId=097C38A722A3206664756E2164756E21