From a13fb37d87f5392ee9b4a6cd4a1b5c077191418c Mon Sep 17 00:00:00 2001 From: Charles Packer Date: Fri, 20 Feb 2026 12:57:40 -0800 Subject: [PATCH] feat: auto-enable memfs from server-side tag on new machines (#1063) Co-authored-by: Letta --- src/agent/memoryFilesystem.ts | 43 +++++++++++++++++++++++++++-------- src/agent/memoryGit.ts | 2 +- src/headless.ts | 4 ++-- src/index.ts | 6 +++-- 4 files changed, 41 insertions(+), 14 deletions(-) diff --git a/src/agent/memoryFilesystem.ts b/src/agent/memoryFilesystem.ts index f515b00..f491485 100644 --- a/src/agent/memoryFilesystem.ts +++ b/src/agent/memoryFilesystem.ts @@ -143,6 +143,11 @@ export interface ApplyMemfsFlagsResult { pullSummary?: string; } +export interface ApplyMemfsFlagsOptions { + pullOnExistingRepo?: boolean; + agentTags?: string[]; +} + /** * Apply --memfs / --no-memfs CLI flags (or /memfs enable) to an agent. * @@ -162,7 +167,7 @@ export async function applyMemfsFlags( agentId: string, memfsFlag: boolean | undefined, noMemfsFlag: boolean | undefined, - options?: { pullOnExistingRepo?: boolean }, + options?: ApplyMemfsFlagsOptions, ): Promise { const { getServerUrl } = await import("./client"); const { settingsManager } = await import("../settings-manager"); @@ -181,14 +186,22 @@ export async function applyMemfsFlags( } const hasExplicitToggle = Boolean(memfsFlag || noMemfsFlag); + const localMemfsEnabled = settingsManager.isMemfsEnabled(agentId); + const { GIT_MEMORY_ENABLED_TAG } = await import("./memoryGit"); + const shouldAutoEnableFromTag = + !hasExplicitToggle && + !localMemfsEnabled && + Boolean(options?.agentTags?.includes(GIT_MEMORY_ENABLED_TAG)); const targetEnabled = memfsFlag ? true : noMemfsFlag ? false - : settingsManager.isMemfsEnabled(agentId); + : shouldAutoEnableFromTag + ? true + : localMemfsEnabled; // 2. Reconcile system prompt first, then persist local memfs setting. - if (hasExplicitToggle) { + if (hasExplicitToggle || shouldAutoEnableFromTag) { const { updateAgentSystemPromptMemfs } = await import("./modify"); const promptUpdate = await updateAgentSystemPromptMemfs( agentId, @@ -200,16 +213,23 @@ export async function applyMemfsFlags( settingsManager.setMemfsEnabled(agentId, targetEnabled); } - const isEnabled = hasExplicitToggle - ? targetEnabled - : settingsManager.isMemfsEnabled(agentId); + const isEnabled = + hasExplicitToggle || shouldAutoEnableFromTag + ? targetEnabled + : settingsManager.isMemfsEnabled(agentId); - // 3. Detach old API-based memory tools when explicitly enabling. - if (isEnabled && memfsFlag) { + // 3. Detach old API-based memory tools when enabling. + if (isEnabled && (memfsFlag || shouldAutoEnableFromTag)) { const { detachMemoryTools } = await import("../tools/toolset"); await detachMemoryTools(agentId); } + // Keep server-side state aligned with explicit disable. + if (noMemfsFlag) { + const { removeGitMemoryTag } = await import("./memoryGit"); + await removeGitMemoryTag(agentId); + } + // 4. Add git tag + clone/pull repo. let pullSummary: string | undefined; if (isEnabled) { @@ -224,7 +244,12 @@ export async function applyMemfsFlags( } } - const action = memfsFlag ? "enabled" : noMemfsFlag ? "disabled" : "unchanged"; + const action = + memfsFlag || shouldAutoEnableFromTag + ? "enabled" + : noMemfsFlag + ? "disabled" + : "unchanged"; return { action, memoryDir: isEnabled ? getMemoryFilesystemRoot(agentId) : undefined, diff --git a/src/agent/memoryGit.ts b/src/agent/memoryGit.ts index 001fbc1..530406b 100644 --- a/src/agent/memoryGit.ts +++ b/src/agent/memoryGit.ts @@ -26,7 +26,7 @@ import { getClient, getServerUrl } from "./client"; const execFile = promisify(execFileCb); -const GIT_MEMORY_ENABLED_TAG = "git-memory-enabled"; +export const GIT_MEMORY_ENABLED_TAG = "git-memory-enabled"; /** Get the agent root directory (~/.letta/agents/{id}/) */ export function getAgentRootDir(agentId: string): string { diff --git a/src/headless.ts b/src/headless.ts index 98c499b..688891c 100644 --- a/src/headless.ts +++ b/src/headless.ts @@ -916,14 +916,14 @@ export async function handleHeadlessCommand( const isSubagent = process.env.LETTA_CODE_AGENT_ROLE === "subagent"; - // Apply memfs flag if explicitly specified (memfs is opt-in via /memfs enable or --memfs) + // Apply memfs flags and auto-enable from server tag when local settings are missing. try { const { applyMemfsFlags } = await import("./agent/memoryFilesystem"); const memfsResult = await applyMemfsFlags( agent.id, memfsFlag, noMemfsFlag, - { pullOnExistingRepo: true }, + { pullOnExistingRepo: true, agentTags: agent.tags }, ); if (memfsResult.pullSummary?.includes("CONFLICT")) { console.error( diff --git a/src/index.ts b/src/index.ts index 08d3a1f..f38be75 100755 --- a/src/index.ts +++ b/src/index.ts @@ -1767,11 +1767,13 @@ async function main(): Promise { // Set agent context for tools that need it (e.g., Skill tool) setAgentContext(agent.id, skillsDirectory, resolvedSkillSources); - // Apply memfs flag if explicitly specified (memfs is opt-in via /memfs enable or --memfs) + // Apply memfs flags and auto-enable from server tag when local settings are missing. const isSubagent = process.env.LETTA_CODE_AGENT_ROLE === "subagent"; try { const { applyMemfsFlags } = await import("./agent/memoryFilesystem"); - await applyMemfsFlags(agent.id, memfsFlag, noMemfsFlag); + await applyMemfsFlags(agent.id, memfsFlag, noMemfsFlag, { + agentTags: agent.tags, + }); } catch (error) { console.error(error instanceof Error ? error.message : String(error)); process.exit(1);