feat: auto-enable memfs from server-side tag on new machines (#1063)
Co-authored-by: Letta <noreply@letta.com>
This commit is contained in:
@@ -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<ApplyMemfsFlagsResult> {
|
||||
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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -1767,11 +1767,13 @@ async function main(): Promise<void> {
|
||||
// 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);
|
||||
|
||||
Reference in New Issue
Block a user