@letta-ai/letta-code 0.14.2 → 0.14.3

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
@@ -3108,7 +3108,7 @@ var package_default;
3108
3108
  var init_package = __esm(() => {
3109
3109
  package_default = {
3110
3110
  name: "@letta-ai/letta-code",
3111
- version: "0.14.2",
3111
+ version: "0.14.3",
3112
3112
  description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
3113
3113
  type: "module",
3114
3114
  bin: {
@@ -5671,10 +5671,10 @@ Your memory blocks are synchronized with a filesystem tree at \`~/.letta/agents/
5671
5671
  ### Sync Behavior
5672
5672
  - **Startup**: Automatic sync when the CLI starts
5673
5673
  - **After memory edits**: Automatic sync after using memory tools
5674
- - **Manual**: Run \`/memfs-sync\` to sync on demand
5674
+ - **Manual**: Run \`/memfs sync\` to sync on demand
5675
5675
  - **Conflict detection**: After each turn, the system checks for conflicts (both file and block changed since last sync)
5676
5676
  - **Agent-driven resolution**: If conflicts are detected, you'll receive a system reminder with the conflicting labels and instructions to resolve them using the \`syncing-memory-filesystem\` skill scripts
5677
- - **User fallback**: The user can also run \`/memfs-sync\` to resolve conflicts manually via an interactive prompt
5677
+ - **User fallback**: The user can also run \`/memfs sync\` to resolve conflicts manually via an interactive prompt
5678
5678
 
5679
5679
  ### How It Works
5680
5680
  1. Each \`.md\` file path maps to a block label (e.g., \`system/persona/git_safety.md\` → label \`persona/git_safety\`)
@@ -61137,6 +61137,10 @@ function ensureMemoryFilesystemDirs(agentId, homeDir = homedir10()) {
61137
61137
  function hashContent(content) {
61138
61138
  return createHash3("sha256").update(content).digest("hex");
61139
61139
  }
61140
+ function hashFileBody(content) {
61141
+ const { body } = parseMdxFrontmatter(content);
61142
+ return hashContent(body);
61143
+ }
61140
61144
  function loadSyncState(agentId, homeDir = homedir10()) {
61141
61145
  const statePath = getMemoryStatePath(agentId, homeDir);
61142
61146
  const emptyState = {
@@ -61246,6 +61250,49 @@ async function writeMemoryFile(dir, label, content) {
61246
61250
  await ensureFilePath(filePath);
61247
61251
  await writeFile5(filePath, content, "utf-8");
61248
61252
  }
61253
+ function renderBlockToFileContent(block) {
61254
+ const lines = ["---"];
61255
+ if (block.description) {
61256
+ const desc = block.description.includes(":") || block.description.includes(`
61257
+ `) ? `"${block.description.replace(/"/g, "\\\"")}"` : block.description;
61258
+ lines.push(`description: ${desc}`);
61259
+ }
61260
+ if (block.limit) {
61261
+ lines.push(`limit: ${block.limit}`);
61262
+ }
61263
+ if (block.read_only === true) {
61264
+ lines.push("read_only: true");
61265
+ }
61266
+ lines.push("---");
61267
+ lines.push("");
61268
+ lines.push(block.value || "");
61269
+ return lines.join(`
61270
+ `);
61271
+ }
61272
+ function parseBlockUpdateFromFileContent(fileContent, defaultLabel) {
61273
+ const { frontmatter, body } = parseMdxFrontmatter(fileContent);
61274
+ const label = frontmatter.label || defaultLabel;
61275
+ const hasDescription = Object.hasOwn(frontmatter, "description");
61276
+ const hasLimit = Object.hasOwn(frontmatter, "limit");
61277
+ const hasReadOnly = Object.hasOwn(frontmatter, "read_only");
61278
+ let limit2;
61279
+ if (hasLimit && frontmatter.limit) {
61280
+ const parsed = Number.parseInt(frontmatter.limit, 10);
61281
+ if (!Number.isNaN(parsed) && parsed > 0) {
61282
+ limit2 = parsed;
61283
+ }
61284
+ }
61285
+ return {
61286
+ label,
61287
+ value: body,
61288
+ ...hasDescription && { description: frontmatter.description },
61289
+ ...hasLimit && limit2 !== undefined && { limit: limit2 },
61290
+ ...hasReadOnly && { read_only: frontmatter.read_only === "true" },
61291
+ hasDescription,
61292
+ hasLimit,
61293
+ hasReadOnly
61294
+ };
61295
+ }
61249
61296
  async function deleteMemoryFile(dir, label) {
61250
61297
  const filePath = join19(dir, `${label}.md`);
61251
61298
  if (existsSync10(filePath)) {
@@ -61388,7 +61435,7 @@ async function syncMemoryFilesystem(agentId, options = {}) {
61388
61435
  const detachedBlockMap = new Map;
61389
61436
  for (const block of detachedBlocks) {
61390
61437
  if (block.label && block.id) {
61391
- if (MANAGED_BLOCK_LABELS.has(block.label)) {
61438
+ if (MEMFS_MANAGED_LABELS.has(block.label)) {
61392
61439
  continue;
61393
61440
  }
61394
61441
  if (systemBlockMap.has(block.label)) {
@@ -61408,7 +61455,7 @@ async function syncMemoryFilesystem(agentId, options = {}) {
61408
61455
  const allBlocksMap = new Map;
61409
61456
  const allFilesMap = new Map;
61410
61457
  for (const label of Array.from(allLabels).sort()) {
61411
- if (MANAGED_BLOCK_LABELS.has(label)) {
61458
+ if (MEMFS_MANAGED_LABELS.has(label)) {
61412
61459
  continue;
61413
61460
  }
61414
61461
  const systemFile = systemFiles.get(label);
@@ -61421,6 +61468,7 @@ async function syncMemoryFilesystem(agentId, options = {}) {
61421
61468
  const isAttached = !!attachedBlock;
61422
61469
  const fileDir = fileInSystem ? systemDir : detachedDir;
61423
61470
  const fileHash = fileEntry ? hashContent(fileEntry.content) : null;
61471
+ const fileBodyHash = fileEntry ? hashFileBody(fileEntry.content) : null;
61424
61472
  const blockHash = blockEntry ? hashContent(blockEntry.value || "") : null;
61425
61473
  const lastFileHash = lastState.fileHashes[label] || null;
61426
61474
  const lastBlockHash = lastState.blockHashes[label] || null;
@@ -61440,6 +61488,12 @@ async function syncMemoryFilesystem(agentId, options = {}) {
61440
61488
  allFilesMap.delete(label);
61441
61489
  continue;
61442
61490
  }
61491
+ if (READ_ONLY_BLOCK_LABELS.includes(label)) {
61492
+ await deleteMemoryFile(fileDir, label);
61493
+ deletedFiles.push(label);
61494
+ allFilesMap.delete(label);
61495
+ continue;
61496
+ }
61443
61497
  const blockData = parseBlockFromFileContent(fileEntry.content, label);
61444
61498
  const createdBlock = await client.blocks.create({
61445
61499
  ...blockData,
@@ -61460,6 +61514,14 @@ async function syncMemoryFilesystem(agentId, options = {}) {
61460
61514
  continue;
61461
61515
  }
61462
61516
  if (!fileEntry && blockEntry) {
61517
+ if (blockEntry.read_only) {
61518
+ const targetDir2 = isAttached ? systemDir : detachedDir;
61519
+ const fileContent2 = renderBlockToFileContent(blockEntry);
61520
+ await writeMemoryFile(targetDir2, label, fileContent2);
61521
+ createdFiles.push(label);
61522
+ allFilesMap.set(label, { content: fileContent2 });
61523
+ continue;
61524
+ }
61463
61525
  if (lastFileHash && !blockChanged) {
61464
61526
  if (blockEntry.id) {
61465
61527
  try {
@@ -61482,16 +61544,17 @@ async function syncMemoryFilesystem(agentId, options = {}) {
61482
61544
  continue;
61483
61545
  }
61484
61546
  const targetDir = isAttached ? systemDir : detachedDir;
61485
- await writeMemoryFile(targetDir, label, blockEntry.value || "");
61547
+ const fileContent = renderBlockToFileContent(blockEntry);
61548
+ await writeMemoryFile(targetDir, label, fileContent);
61486
61549
  createdFiles.push(label);
61487
- allFilesMap.set(label, { content: blockEntry.value || "" });
61550
+ allFilesMap.set(label, { content: fileContent });
61488
61551
  continue;
61489
61552
  }
61490
61553
  if (!fileEntry || !blockEntry) {
61491
61554
  continue;
61492
61555
  }
61493
61556
  const locationMismatch = fileInSystem && !isAttached || !fileInSystem && isAttached;
61494
- if (fileHash === blockHash) {
61557
+ if (fileBodyHash === blockHash) {
61495
61558
  if (locationMismatch && blockEntry.id) {
61496
61559
  if (fileInSystem && !isAttached) {
61497
61560
  await client.agents.blocks.attach(blockEntry.id, {
@@ -61506,9 +61569,10 @@ async function syncMemoryFilesystem(agentId, options = {}) {
61506
61569
  continue;
61507
61570
  }
61508
61571
  if (fileChanged && blockChanged && resolution && resolution.resolution === "block") {
61509
- await writeMemoryFile(fileDir, label, blockEntry.value || "");
61572
+ const fileContent = renderBlockToFileContent(blockEntry);
61573
+ await writeMemoryFile(fileDir, label, fileContent);
61510
61574
  updatedFiles.push(label);
61511
- allFilesMap.set(label, { content: blockEntry.value || "" });
61575
+ allFilesMap.set(label, { content: fileContent });
61512
61576
  if (locationMismatch && blockEntry.id) {
61513
61577
  if (fileInSystem && !isAttached) {
61514
61578
  await client.agents.blocks.attach(blockEntry.id, {
@@ -61523,9 +61587,10 @@ async function syncMemoryFilesystem(agentId, options = {}) {
61523
61587
  continue;
61524
61588
  }
61525
61589
  if (resolution?.resolution === "block") {
61526
- await writeMemoryFile(fileDir, label, blockEntry.value || "");
61590
+ const fileContent = renderBlockToFileContent(blockEntry);
61591
+ await writeMemoryFile(fileDir, label, fileContent);
61527
61592
  updatedFiles.push(label);
61528
- allFilesMap.set(label, { content: blockEntry.value || "" });
61593
+ allFilesMap.set(label, { content: fileContent });
61529
61594
  if (locationMismatch && blockEntry.id) {
61530
61595
  if (fileInSystem && !isAttached) {
61531
61596
  await client.agents.blocks.attach(blockEntry.id, {
@@ -61540,14 +61605,31 @@ async function syncMemoryFilesystem(agentId, options = {}) {
61540
61605
  continue;
61541
61606
  }
61542
61607
  if (fileChanged) {
61608
+ if (blockEntry.read_only) {
61609
+ const fileContent = renderBlockToFileContent(blockEntry);
61610
+ await writeMemoryFile(fileDir, label, fileContent);
61611
+ updatedFiles.push(label);
61612
+ allFilesMap.set(label, { content: fileContent });
61613
+ continue;
61614
+ }
61543
61615
  if (blockEntry.id) {
61544
61616
  try {
61545
- const blockData = parseBlockFromFileContent(fileEntry.content, label);
61546
- const updatePayload = isAttached ? { value: blockData.value } : { value: blockData.value, label };
61617
+ const parsed = parseBlockUpdateFromFileContent(fileEntry.content, label);
61618
+ const updatePayload = {
61619
+ value: parsed.value
61620
+ };
61621
+ if (parsed.hasDescription)
61622
+ updatePayload.description = parsed.description;
61623
+ if (parsed.hasLimit)
61624
+ updatePayload.limit = parsed.limit;
61625
+ if (parsed.hasReadOnly)
61626
+ updatePayload.read_only = parsed.read_only;
61627
+ if (!isAttached)
61628
+ updatePayload.label = label;
61547
61629
  await client.blocks.update(blockEntry.id, updatePayload);
61548
61630
  updatedBlocks.push(label);
61549
61631
  allBlocksMap.set(label, {
61550
- value: blockData.value,
61632
+ value: parsed.value,
61551
61633
  id: blockEntry.id
61552
61634
  });
61553
61635
  if (locationMismatch) {
@@ -61588,9 +61670,10 @@ async function syncMemoryFilesystem(agentId, options = {}) {
61588
61670
  continue;
61589
61671
  }
61590
61672
  if (blockChanged) {
61591
- await writeMemoryFile(fileDir, label, blockEntry.value || "");
61673
+ const fileContent = renderBlockToFileContent(blockEntry);
61674
+ await writeMemoryFile(fileDir, label, fileContent);
61592
61675
  updatedFiles.push(label);
61593
- allFilesMap.set(label, { content: blockEntry.value || "" });
61676
+ allFilesMap.set(label, { content: fileContent });
61594
61677
  if (locationMismatch && blockEntry.id) {
61595
61678
  if (fileInSystem && !isAttached) {
61596
61679
  await client.agents.blocks.attach(blockEntry.id, {
@@ -61633,8 +61716,14 @@ ${tree}`;
61633
61716
  const memfsBlock = blocks.find((block) => block.label === MEMORY_FILESYSTEM_BLOCK_LABEL);
61634
61717
  if (memfsBlock?.id) {
61635
61718
  await client.blocks.update(memfsBlock.id, { value: content });
61719
+ const fileContent = renderBlockToFileContent({
61720
+ value: content,
61721
+ description: memfsBlock.description,
61722
+ limit: memfsBlock.limit,
61723
+ read_only: memfsBlock.read_only
61724
+ });
61725
+ await writeMemoryFile(systemDir, MEMORY_FILESYSTEM_BLOCK_LABEL, fileContent);
61636
61726
  }
61637
- await writeMemoryFile(systemDir, MEMORY_FILESYSTEM_BLOCK_LABEL, content);
61638
61727
  }
61639
61728
  async function ensureMemoryFilesystemBlock(agentId) {
61640
61729
  const client = await getClient2();
@@ -61702,7 +61791,7 @@ async function checkMemoryFilesystemStatus(agentId, options) {
61702
61791
  const detachedBlockMap = new Map;
61703
61792
  for (const block of detachedBlocks) {
61704
61793
  if (block.label) {
61705
- if (MANAGED_BLOCK_LABELS.has(block.label)) {
61794
+ if (MEMFS_MANAGED_LABELS.has(block.label)) {
61706
61795
  continue;
61707
61796
  }
61708
61797
  if (systemBlockMap.has(block.label)) {
@@ -61720,7 +61809,7 @@ async function checkMemoryFilesystemStatus(agentId, options) {
61720
61809
  ...Object.keys(lastState.fileHashes)
61721
61810
  ]);
61722
61811
  for (const label of Array.from(allLabels).sort()) {
61723
- if (MANAGED_BLOCK_LABELS.has(label))
61812
+ if (MEMFS_MANAGED_LABELS.has(label))
61724
61813
  continue;
61725
61814
  const systemFile = systemFiles.get(label);
61726
61815
  const detachedFile = detachedFiles.get(label);
@@ -61758,6 +61847,9 @@ function classifyLabel(label, fileContent, blockValue, lastFileHash, lastBlockHa
61758
61847
  if (lastBlockHash && !fileChanged) {
61759
61848
  return;
61760
61849
  }
61850
+ if (READ_ONLY_BLOCK_LABELS.includes(label)) {
61851
+ return;
61852
+ }
61761
61853
  newFiles.push(label);
61762
61854
  return;
61763
61855
  }
@@ -61790,14 +61882,11 @@ async function detachMemoryFilesystemBlock(agentId) {
61790
61882
  await client.agents.blocks.detach(memfsBlock.id, { agent_id: agentId });
61791
61883
  }
61792
61884
  }
61793
- var MEMORY_FILESYSTEM_BLOCK_LABEL = "memory_filesystem", MEMORY_FS_ROOT = ".letta", MEMORY_FS_AGENTS_DIR = "agents", MEMORY_FS_MEMORY_DIR = "memory", MEMORY_SYSTEM_DIR = "system", MEMORY_FS_STATE_FILE = ".sync-state.json", MANAGED_BLOCK_LABELS;
61885
+ var MEMORY_FILESYSTEM_BLOCK_LABEL = "memory_filesystem", MEMORY_FS_ROOT = ".letta", MEMORY_FS_AGENTS_DIR = "agents", MEMORY_FS_MEMORY_DIR = "memory", MEMORY_SYSTEM_DIR = "system", MEMORY_FS_STATE_FILE = ".sync-state.json", MEMFS_MANAGED_LABELS;
61794
61886
  var init_memoryFilesystem = __esm(async () => {
61795
61887
  init_memory();
61796
61888
  await init_client2();
61797
- MANAGED_BLOCK_LABELS = new Set([
61798
- MEMORY_FILESYSTEM_BLOCK_LABEL,
61799
- ...ISOLATED_BLOCK_LABELS
61800
- ]);
61889
+ MEMFS_MANAGED_LABELS = new Set([MEMORY_FILESYSTEM_BLOCK_LABEL]);
61801
61890
  });
61802
61891
 
61803
61892
  // src/agent/message.ts
@@ -73563,17 +73652,10 @@ var init_registry = __esm(() => {
73563
73652
  return "Opening memory viewer...";
73564
73653
  }
73565
73654
  },
73566
- "/memfs-sync": {
73567
- desc: "Sync memory blocks with filesystem (requires memFS enabled)",
73568
- order: 15.5,
73569
- handler: () => {
73570
- return "Syncing memory filesystem...";
73571
- }
73572
- },
73573
73655
  "/memfs": {
73574
- desc: "Enable/disable filesystem-backed memory (/memfs [enable|disable])",
73575
- args: "[enable|disable]",
73576
- order: 15.6,
73656
+ desc: "Manage filesystem-backed memory (/memfs [enable|disable|sync|reset])",
73657
+ args: "[enable|disable|sync|reset]",
73658
+ order: 15.5,
73577
73659
  handler: () => {
73578
73660
  return "Managing memory filesystem...";
73579
73661
  }
@@ -88957,8 +89039,8 @@ var exports_App = {};
88957
89039
  __export(exports_App, {
88958
89040
  default: () => App2
88959
89041
  });
88960
- import { existsSync as existsSync16, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
88961
- import { homedir as homedir15 } from "node:os";
89042
+ import { existsSync as existsSync16, readFileSync as readFileSync8, renameSync, writeFileSync as writeFileSync5 } from "node:fs";
89043
+ import { homedir as homedir15, tmpdir as tmpdir3 } from "node:os";
88962
89044
  import { join as join28 } from "node:path";
88963
89045
  function getErrorHintForStopReason(stopReason, currentModelId) {
88964
89046
  if (currentModelId === "opus" && stopReason === "llm_api_error" && getModelInfo("bedrock-opus")) {
@@ -89355,7 +89437,7 @@ function App2({
89355
89437
  const [memorySyncConflicts, setMemorySyncConflicts] = import_react88.useState(null);
89356
89438
  const memorySyncProcessedToolCallsRef = import_react88.useRef(new Set);
89357
89439
  const memorySyncCommandIdRef = import_react88.useRef(null);
89358
- const memorySyncCommandInputRef = import_react88.useRef("/memfs-sync");
89440
+ const memorySyncCommandInputRef = import_react88.useRef("/memfs sync");
89359
89441
  const memorySyncInFlightRef = import_react88.useRef(false);
89360
89442
  const memoryFilesystemInitializedRef = import_react88.useRef(false);
89361
89443
  const pendingMemfsConflictsRef = import_react88.useRef(null);
@@ -89868,7 +89950,7 @@ function App2({
89868
89950
  });
89869
89951
  }
89870
89952
  }, [refreshDerived, currentModelId]);
89871
- const updateMemorySyncCommand = import_react88.useCallback((commandId, output, success, input = "/memfs-sync", keepRunning = false) => {
89953
+ const updateMemorySyncCommand = import_react88.useCallback((commandId, output, success, input = "/memfs sync", keepRunning = false) => {
89872
89954
  buffersRef.current.byId.set(commandId, {
89873
89955
  kind: "command",
89874
89956
  id: commandId,
@@ -89899,7 +89981,7 @@ function App2({
89899
89981
  setMemorySyncConflicts(result.conflicts);
89900
89982
  setActiveOverlay("memfs-sync");
89901
89983
  if (commandId) {
89902
- updateMemorySyncCommand(commandId, `Memory sync paused — resolve ${result.conflicts.length} conflict${result.conflicts.length === 1 ? "" : "s"} to continue.`, false, "/memfs-sync", true);
89984
+ updateMemorySyncCommand(commandId, `Memory sync paused — resolve ${result.conflicts.length} conflict${result.conflicts.length === 1 ? "" : "s"} to continue.`, false, "/memfs sync", true);
89903
89985
  }
89904
89986
  } else {
89905
89987
  debugLog("memfs", `${source} sync found ${result.conflicts.length} conflict(s), queuing for agent`);
@@ -90028,7 +90110,7 @@ function App2({
90028
90110
  const commandId = memorySyncCommandIdRef.current;
90029
90111
  const commandInput = memorySyncCommandInputRef.current;
90030
90112
  memorySyncCommandIdRef.current = null;
90031
- memorySyncCommandInputRef.current = "/memfs-sync";
90113
+ memorySyncCommandInputRef.current = "/memfs sync";
90032
90114
  const resolutions = memorySyncConflicts.map((conflict) => {
90033
90115
  const answer = answers[`Conflict for ${conflict.label}`];
90034
90116
  return {
@@ -90077,7 +90159,7 @@ ${resolutionSummary}`, true, commandInput);
90077
90159
  const commandId = memorySyncCommandIdRef.current;
90078
90160
  const commandInput = memorySyncCommandInputRef.current;
90079
90161
  memorySyncCommandIdRef.current = null;
90080
- memorySyncCommandInputRef.current = "/memfs-sync";
90162
+ memorySyncCommandInputRef.current = "/memfs sync";
90081
90163
  memorySyncInFlightRef.current = false;
90082
90164
  setMemorySyncConflicts(null);
90083
90165
  setActiveOverlay(null);
@@ -92624,46 +92706,32 @@ Press Enter to continue, or type anything to cancel.`, false, "running");
92624
92706
  }
92625
92707
  return { submitted: true };
92626
92708
  }
92627
- if (trimmed === "/memfs-sync") {
92628
- if (!settingsManager.isMemfsEnabled(agentId)) {
92629
- const cmdId2 = uid4("cmd");
92630
- buffersRef.current.byId.set(cmdId2, {
92709
+ if (trimmed.startsWith("/memfs")) {
92710
+ const [, subcommand] = trimmed.split(/\s+/);
92711
+ const cmdId = uid4("cmd");
92712
+ if (!subcommand || subcommand === "help") {
92713
+ const output = [
92714
+ "memfs commands:",
92715
+ "- /memfs status — show status",
92716
+ "- /memfs enable — enable filesystem-backed memory",
92717
+ "- /memfs disable — disable filesystem-backed memory",
92718
+ "- /memfs sync — sync blocks and files now",
92719
+ "- /memfs reset — move local memfs to /tmp and recreate dirs"
92720
+ ].join(`
92721
+ `);
92722
+ buffersRef.current.byId.set(cmdId, {
92631
92723
  kind: "command",
92632
- id: cmdId2,
92724
+ id: cmdId,
92633
92725
  input: msg,
92634
- output: "Memory filesystem is disabled. Run `/memfs enable` first.",
92726
+ output,
92635
92727
  phase: "finished",
92636
- success: false
92728
+ success: true
92637
92729
  });
92638
- buffersRef.current.order.push(cmdId2);
92730
+ buffersRef.current.order.push(cmdId);
92639
92731
  refreshDerived();
92640
92732
  return { submitted: true };
92641
92733
  }
92642
- const cmdId = uid4("cmd");
92643
- buffersRef.current.byId.set(cmdId, {
92644
- kind: "command",
92645
- id: cmdId,
92646
- input: msg,
92647
- output: "Syncing memory filesystem...",
92648
- phase: "running"
92649
- });
92650
- buffersRef.current.order.push(cmdId);
92651
- refreshDerived();
92652
- setCommandRunning(true);
92653
- try {
92654
- await runMemoryFilesystemSync("command", cmdId);
92655
- } catch (error) {
92656
- const errorText = error instanceof Error ? error.message : String(error);
92657
- updateMemorySyncCommand(cmdId, `Failed: ${errorText}`, false);
92658
- } finally {
92659
- setCommandRunning(false);
92660
- }
92661
- return { submitted: true };
92662
- }
92663
- if (trimmed.startsWith("/memfs")) {
92664
- const [, subcommand] = trimmed.split(/\s+/);
92665
- const cmdId = uid4("cmd");
92666
- if (!subcommand || subcommand === "status") {
92734
+ if (subcommand === "status") {
92667
92735
  const enabled = settingsManager.isMemfsEnabled(agentId);
92668
92736
  let output;
92669
92737
  if (enabled) {
@@ -92725,6 +92793,71 @@ ${formatMemorySyncSummary(result2)}`, true, msg);
92725
92793
  }
92726
92794
  return { submitted: true };
92727
92795
  }
92796
+ if (subcommand === "sync") {
92797
+ if (!settingsManager.isMemfsEnabled(agentId)) {
92798
+ buffersRef.current.byId.set(cmdId, {
92799
+ kind: "command",
92800
+ id: cmdId,
92801
+ input: msg,
92802
+ output: "Memory filesystem is disabled. Run `/memfs enable` first.",
92803
+ phase: "finished",
92804
+ success: false
92805
+ });
92806
+ buffersRef.current.order.push(cmdId);
92807
+ refreshDerived();
92808
+ return { submitted: true };
92809
+ }
92810
+ buffersRef.current.byId.set(cmdId, {
92811
+ kind: "command",
92812
+ id: cmdId,
92813
+ input: msg,
92814
+ output: "Syncing memory filesystem...",
92815
+ phase: "running"
92816
+ });
92817
+ buffersRef.current.order.push(cmdId);
92818
+ refreshDerived();
92819
+ setCommandRunning(true);
92820
+ try {
92821
+ await runMemoryFilesystemSync("command", cmdId);
92822
+ } catch (error) {
92823
+ const errorText = error instanceof Error ? error.message : String(error);
92824
+ updateMemorySyncCommand(cmdId, `Failed: ${errorText}`, false);
92825
+ } finally {
92826
+ setCommandRunning(false);
92827
+ }
92828
+ return { submitted: true };
92829
+ }
92830
+ if (subcommand === "reset") {
92831
+ buffersRef.current.byId.set(cmdId, {
92832
+ kind: "command",
92833
+ id: cmdId,
92834
+ input: msg,
92835
+ output: "Resetting memory filesystem...",
92836
+ phase: "running"
92837
+ });
92838
+ buffersRef.current.order.push(cmdId);
92839
+ refreshDerived();
92840
+ setCommandRunning(true);
92841
+ try {
92842
+ const memoryDir = getMemoryFilesystemRoot(agentId);
92843
+ if (!existsSync16(memoryDir)) {
92844
+ updateMemorySyncCommand(cmdId, "No local memory filesystem found to reset.", true, msg);
92845
+ return { submitted: true };
92846
+ }
92847
+ const backupDir = join28(tmpdir3(), `letta-memfs-reset-${agentId}-${Date.now()}`);
92848
+ renameSync(memoryDir, backupDir);
92849
+ ensureMemoryFilesystemDirs(agentId);
92850
+ updateMemorySyncCommand(cmdId, `Memory filesystem reset.
92851
+ Backup moved to ${backupDir}
92852
+ Run \`/memfs sync\` to repopulate from API.`, true, msg);
92853
+ } catch (error) {
92854
+ const errorText = error instanceof Error ? error.message : String(error);
92855
+ updateMemorySyncCommand(cmdId, `Failed to reset memfs: ${errorText}`, false, msg);
92856
+ } finally {
92857
+ setCommandRunning(false);
92858
+ }
92859
+ return { submitted: true };
92860
+ }
92728
92861
  if (subcommand === "disable") {
92729
92862
  buffersRef.current.byId.set(cmdId, {
92730
92863
  kind: "command",
@@ -92767,7 +92900,7 @@ Files on disk have been kept.`, true, msg);
92767
92900
  kind: "command",
92768
92901
  id: cmdId,
92769
92902
  input: msg,
92770
- output: `Unknown subcommand: ${subcommand}. Use /memfs, /memfs enable, or /memfs disable.`,
92903
+ output: `Unknown subcommand: ${subcommand}. Use /memfs, /memfs enable, /memfs disable, /memfs sync, or /memfs reset.`,
92771
92904
  phase: "finished",
92772
92905
  success: false
92773
92906
  });
@@ -100954,4 +101087,4 @@ Error during initialization: ${message}`);
100954
101087
  }
100955
101088
  main();
100956
101089
 
100957
- //# debugId=5D585D67D2BE412A64756E2164756E21
101090
+ //# debugId=B77A56113A42C53064756E2164756E21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letta-ai/letta-code",
3
- "version": "0.14.2",
3
+ "version": "0.14.3",
4
4
  "description": "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -5,7 +5,7 @@ description: Manage memory filesystem sync conflicts with git-like commands. Loa
5
5
 
6
6
  # Memory Filesystem Sync
7
7
 
8
- When memFS is enabled, your memory blocks are mirrored as `.md` files on disk at `~/.letta/agents/<agent-id>/memory/`. Changes to blocks or files are detected via content hashing and synced at startup and on manual `/memfs-sync`.
8
+ When memFS is enabled, your memory blocks are mirrored as `.md` files on disk at `~/.letta/agents/<agent-id>/memory/`. Changes to blocks or files are detected via content hashing and synced at startup and on manual `/memfs sync`.
9
9
 
10
10
  **Conflicts** occur when both the file and the block are modified since the last sync (e.g., user edits a file in their editor while the block is also updated manually by the user via the API). Non-conflicting changes (only one side changed) are resolved automatically during the next sync.
11
11
 
@@ -96,5 +96,5 @@ npx tsx <SKILL_DIR>/scripts/memfs-resolve.ts $LETTA_AGENT_ID --resolutions '[{"l
96
96
  ## Notes
97
97
 
98
98
  - Non-conflicting changes (only one side modified) are resolved automatically during the next sync — you only need to intervene for true conflicts
99
- - The `/memfs-sync` command is still available for users to manually trigger sync and resolve conflicts via the CLI overlay
99
+ - The `/memfs sync` command is still available for users to manually trigger sync and resolve conflicts via the CLI overlay
100
100
  - After resolving, the sync state is updated so the same conflicts won't reappear