@letta-ai/letta-code 0.12.6 → 0.12.8
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/package.json
CHANGED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: defragmenting-memory
|
|
3
|
+
description: Defragments and cleans up agent memory blocks. Use when memory becomes messy, redundant, or poorly organized. Backs up memory, uses a subagent to clean it up, then restores the cleaned version.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Memory Defragmentation Skill
|
|
7
|
+
|
|
8
|
+
This skill helps you maintain clean, well-organized memory blocks by:
|
|
9
|
+
1. Dumping current memory to local files and backing up the agent file
|
|
10
|
+
2. Using the memory subagent to clean up the files
|
|
11
|
+
3. Restoring the cleaned files back to memory
|
|
12
|
+
|
|
13
|
+
## When to Use
|
|
14
|
+
|
|
15
|
+
- Memory blocks have redundant information
|
|
16
|
+
- Memory lacks structure (walls of text)
|
|
17
|
+
- Memory contains contradictions
|
|
18
|
+
- Memory has grown stale or outdated
|
|
19
|
+
- After major project milestones
|
|
20
|
+
- Every 50-100 conversation turns
|
|
21
|
+
|
|
22
|
+
## Workflow
|
|
23
|
+
|
|
24
|
+
### Step 1: Backup Memory to Files
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npx tsx <SKILL_DIR>/scripts/backup-memory.ts $LETTA_AGENT_ID .letta/backups/working
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
This creates:
|
|
31
|
+
- `.letta/backups/<agent-id>/<timestamp>/` - Timestamped memory blocks backup
|
|
32
|
+
- `.letta/backups/working/` - Working directory with editable files
|
|
33
|
+
- Each memory block as a `.md` file: `persona.md`, `human.md`, `project.md`, etc.
|
|
34
|
+
|
|
35
|
+
### Step 2: Spawn Memory Subagent to Clean Files
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
Task({
|
|
39
|
+
subagent_type: "memory",
|
|
40
|
+
description: "Clean up memory files",
|
|
41
|
+
prompt: `Edit the memory block files in .letta/backups/working/ to clean them up.
|
|
42
|
+
|
|
43
|
+
Focus on:
|
|
44
|
+
- Reorganize and consolidate redundant information
|
|
45
|
+
- Add clear structure with markdown headers
|
|
46
|
+
- Organize content with bullet points
|
|
47
|
+
- Resolve contradictions
|
|
48
|
+
- Improve scannability
|
|
49
|
+
|
|
50
|
+
IMPORTANT: When merging blocks, DELETE the redundant source files after consolidating their content (use Bash rm command). You have full bash access in the .letta/backups/working directory. Only delete files when: (1) you've merged their content into another block, or (2) the file contains only irrelevant/junk data with no project value.
|
|
51
|
+
|
|
52
|
+
Files to edit: persona.md, human.md, project.md
|
|
53
|
+
Do NOT edit: skills.md (auto-generated), loaded_skills.md (system-managed)
|
|
54
|
+
|
|
55
|
+
After editing, provide a report with before/after character counts and list any deleted files.`
|
|
56
|
+
})
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The memory subagent will:
|
|
60
|
+
- Read the files from `.letta/backups/working/`
|
|
61
|
+
- Edit them to reorganize and consolidate redundancy
|
|
62
|
+
- Merge related blocks together for better organization
|
|
63
|
+
- Add clear structure with markdown formatting
|
|
64
|
+
- Delete source files after merging their content into other blocks
|
|
65
|
+
- Provide a detailed report of changes (including what was merged where)
|
|
66
|
+
|
|
67
|
+
### Step 3: Restore Cleaned Files to Memory
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
npx tsx <SKILL_DIR>/scripts/restore-memory.ts $LETTA_AGENT_ID .letta/backups/working
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
This will:
|
|
74
|
+
- Compare each file to current memory blocks
|
|
75
|
+
- Update only the blocks that changed
|
|
76
|
+
- Show before/after character counts
|
|
77
|
+
- Skip unchanged blocks
|
|
78
|
+
|
|
79
|
+
## Example Complete Flow
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
// Step 1: Backup memory to files
|
|
83
|
+
Bash({
|
|
84
|
+
command: "npx tsx <SKILL_DIR>/scripts/backup-memory.ts $LETTA_AGENT_ID .letta/backups/working",
|
|
85
|
+
description: "Backup memory to files"
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
// Step 2: Clean up (subagent edits files and deletes merged ones)
|
|
89
|
+
Task({
|
|
90
|
+
subagent_type: "memory",
|
|
91
|
+
description: "Clean up memory files",
|
|
92
|
+
prompt: "Edit memory files in .letta/backups/working/ to reorganize and consolidate redundancy. Focus on persona.md, human.md, and project.md. Merge related blocks together and DELETE the source files after merging (use Bash rm command - you have full bash access). Add clear structure. Report what was merged and where, and which files were deleted."
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
// Step 3: Restore
|
|
96
|
+
Bash({
|
|
97
|
+
command: "npx tsx <SKILL_DIR>/scripts/restore-memory.ts $LETTA_AGENT_ID .letta/backups/working",
|
|
98
|
+
description: "Restore cleaned memory blocks"
|
|
99
|
+
})
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Rollback
|
|
103
|
+
|
|
104
|
+
If something goes wrong, restore from a previous backup:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
# Find the backup directory
|
|
108
|
+
ls -la .letta/backups/<agent-id>/
|
|
109
|
+
|
|
110
|
+
# Restore from specific timestamp
|
|
111
|
+
npx tsx <SKILL_DIR>/scripts/restore-memory.ts $LETTA_AGENT_ID .letta/backups/<agent-id>/<timestamp>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Dry Run
|
|
115
|
+
|
|
116
|
+
Preview changes without applying them:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
npx tsx <SKILL_DIR>/scripts/restore-memory.ts $LETTA_AGENT_ID .letta/backups/working --dry-run
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## What the Memory Subagent Does
|
|
123
|
+
|
|
124
|
+
The memory subagent focuses on cleaning up files. It:
|
|
125
|
+
- Reads files from `.letta/backups/working/`
|
|
126
|
+
- Edits files to improve structure and consolidate redundancy
|
|
127
|
+
- Merges related blocks together to reduce fragmentation
|
|
128
|
+
- Reorganizes information for better clarity and scannability
|
|
129
|
+
- Deletes source files after merging their content (using Bash `rm` command)
|
|
130
|
+
- Provides detailed before/after reports including merge operations
|
|
131
|
+
- Does NOT run backup scripts (main agent does this)
|
|
132
|
+
- Does NOT run restore scripts (main agent does this)
|
|
133
|
+
|
|
134
|
+
The memory subagent runs with `bypassPermissions` mode, giving it full Bash access to delete files after merging them. The focus is on consolidation and reorganization.
|
|
135
|
+
|
|
136
|
+
## Tips
|
|
137
|
+
|
|
138
|
+
**What to clean up:**
|
|
139
|
+
- Duplicate information (consolidate into one well-organized section)
|
|
140
|
+
- Walls of text without structure (add headers and bullets)
|
|
141
|
+
- Contradictions (resolve by clarifying or choosing the better guidance)
|
|
142
|
+
- Speculation ("probably", "maybe" - make it concrete or remove)
|
|
143
|
+
- Transient details that won't matter in a week
|
|
144
|
+
|
|
145
|
+
**Reorganization Strategy:**
|
|
146
|
+
- Consolidate duplicate information into a single, well-structured section
|
|
147
|
+
- Merge related content that's scattered across multiple blocks
|
|
148
|
+
- Add clear headers and bullet points for scannability
|
|
149
|
+
- Group similar information together logically
|
|
150
|
+
- After merging blocks, DELETE the source files to avoid duplication
|
|
151
|
+
|
|
152
|
+
**When to DELETE a file:**
|
|
153
|
+
- After merging - You've consolidated its content into another block (common and encouraged)
|
|
154
|
+
- Junk data - File contains only irrelevant test/junk data with no project connection
|
|
155
|
+
- Empty/deprecated - File is just a notice with no unique information
|
|
156
|
+
- Don't delete - If file has unique information that hasn't been merged elsewhere
|
|
157
|
+
|
|
158
|
+
**What to preserve:**
|
|
159
|
+
- User preferences (sacred - never delete)
|
|
160
|
+
- Project conventions discovered through experience
|
|
161
|
+
- Important context for future sessions
|
|
162
|
+
- Learnings from past mistakes
|
|
163
|
+
- Any information that has unique value
|
|
164
|
+
|
|
165
|
+
**Good memory structure:**
|
|
166
|
+
- Use markdown headers (##, ###)
|
|
167
|
+
- Organize with bullet points
|
|
168
|
+
- Keep related information together
|
|
169
|
+
- Make it scannable at a glance
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
|
+
/**
|
|
3
|
+
* Backup Memory Blocks to Local Files
|
|
4
|
+
*
|
|
5
|
+
* Exports all memory blocks from an agent to local files for checkpointing and editing.
|
|
6
|
+
* Creates a timestamped backup directory with:
|
|
7
|
+
* - Individual .md files for each memory block
|
|
8
|
+
* - manifest.json with metadata
|
|
9
|
+
*
|
|
10
|
+
* This script is standalone and can be run outside the CLI process.
|
|
11
|
+
* It reads auth from LETTA_API_KEY env var or ~/.letta/settings.json.
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* npx tsx backup-memory.ts <agent-id> [backup-dir]
|
|
15
|
+
*
|
|
16
|
+
* Example:
|
|
17
|
+
* npx tsx backup-memory.ts agent-abc123
|
|
18
|
+
* npx tsx backup-memory.ts $LETTA_AGENT_ID .letta/backups/working
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
22
|
+
import { createRequire } from "node:module";
|
|
23
|
+
import { homedir } from "node:os";
|
|
24
|
+
import { join } from "node:path";
|
|
25
|
+
|
|
26
|
+
// Use createRequire for @letta-ai/letta-client so NODE_PATH is respected
|
|
27
|
+
// (ES module imports don't respect NODE_PATH, but require does)
|
|
28
|
+
const require = createRequire(import.meta.url);
|
|
29
|
+
const Letta = require("@letta-ai/letta-client")
|
|
30
|
+
.default as typeof import("@letta-ai/letta-client").default;
|
|
31
|
+
type LettaClient = InstanceType<typeof Letta>;
|
|
32
|
+
|
|
33
|
+
export interface BackupManifest {
|
|
34
|
+
agent_id: string;
|
|
35
|
+
timestamp: string;
|
|
36
|
+
backup_path: string;
|
|
37
|
+
blocks: Array<{
|
|
38
|
+
id: string;
|
|
39
|
+
label: string;
|
|
40
|
+
filename: string;
|
|
41
|
+
limit: number;
|
|
42
|
+
value_length: number;
|
|
43
|
+
}>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Get API key from env var or settings file
|
|
48
|
+
*/
|
|
49
|
+
function getApiKey(): string {
|
|
50
|
+
if (process.env.LETTA_API_KEY) {
|
|
51
|
+
return process.env.LETTA_API_KEY;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const settingsPath = join(homedir(), ".letta", "settings.json");
|
|
55
|
+
try {
|
|
56
|
+
const settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
57
|
+
if (settings.env?.LETTA_API_KEY) {
|
|
58
|
+
return settings.env.LETTA_API_KEY;
|
|
59
|
+
}
|
|
60
|
+
} catch {
|
|
61
|
+
// Settings file doesn't exist or is invalid
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
throw new Error(
|
|
65
|
+
"No LETTA_API_KEY found. Set the env var or run the Letta CLI to authenticate.",
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Create a Letta client with auth from env/settings
|
|
71
|
+
*/
|
|
72
|
+
function createClient(): LettaClient {
|
|
73
|
+
return new Letta({ apiKey: getApiKey() });
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Backup memory blocks to local files
|
|
78
|
+
*/
|
|
79
|
+
async function backupMemory(
|
|
80
|
+
agentId: string,
|
|
81
|
+
backupDir?: string,
|
|
82
|
+
): Promise<string> {
|
|
83
|
+
const client = createClient();
|
|
84
|
+
|
|
85
|
+
// Create backup directory
|
|
86
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
87
|
+
const defaultBackupDir = join(
|
|
88
|
+
process.cwd(),
|
|
89
|
+
".letta",
|
|
90
|
+
"backups",
|
|
91
|
+
agentId,
|
|
92
|
+
timestamp,
|
|
93
|
+
);
|
|
94
|
+
const backupPath = backupDir || defaultBackupDir;
|
|
95
|
+
|
|
96
|
+
mkdirSync(backupPath, { recursive: true });
|
|
97
|
+
|
|
98
|
+
console.log(`Backing up memory blocks for agent ${agentId}...`);
|
|
99
|
+
console.log(`Backup location: ${backupPath}`);
|
|
100
|
+
|
|
101
|
+
// Get all memory blocks
|
|
102
|
+
const blocksResponse = await client.agents.blocks.list(agentId);
|
|
103
|
+
const blocks = Array.isArray(blocksResponse)
|
|
104
|
+
? blocksResponse
|
|
105
|
+
: (blocksResponse as { items?: unknown[] }).items ||
|
|
106
|
+
(blocksResponse as { blocks?: unknown[] }).blocks ||
|
|
107
|
+
[];
|
|
108
|
+
|
|
109
|
+
console.log(`Found ${blocks.length} memory blocks`);
|
|
110
|
+
|
|
111
|
+
// Export each block to a file
|
|
112
|
+
const manifest: BackupManifest = {
|
|
113
|
+
agent_id: agentId,
|
|
114
|
+
timestamp: new Date().toISOString(),
|
|
115
|
+
backup_path: backupPath,
|
|
116
|
+
blocks: [],
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
for (const block of blocks as Array<{
|
|
120
|
+
id: string;
|
|
121
|
+
label?: string;
|
|
122
|
+
value?: string;
|
|
123
|
+
limit?: number;
|
|
124
|
+
}>) {
|
|
125
|
+
const label = block.label || `block-${block.id}`;
|
|
126
|
+
const filename = `${label}.md`;
|
|
127
|
+
const filepath = join(backupPath, filename);
|
|
128
|
+
|
|
129
|
+
// Write block content to file
|
|
130
|
+
const content = block.value || "";
|
|
131
|
+
writeFileSync(filepath, content, "utf-8");
|
|
132
|
+
|
|
133
|
+
console.log(` ✓ ${label} -> ${filename} (${content.length} chars)`);
|
|
134
|
+
|
|
135
|
+
// Add to manifest
|
|
136
|
+
manifest.blocks.push({
|
|
137
|
+
id: block.id,
|
|
138
|
+
label,
|
|
139
|
+
filename,
|
|
140
|
+
limit: block.limit || 0,
|
|
141
|
+
value_length: content.length,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Write manifest
|
|
146
|
+
const manifestPath = join(backupPath, "manifest.json");
|
|
147
|
+
writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
|
|
148
|
+
console.log(` ✓ manifest.json`);
|
|
149
|
+
|
|
150
|
+
console.log(`\n✅ Backup complete: ${backupPath}`);
|
|
151
|
+
return backupPath;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// CLI Entry Point - check if this file is being run directly
|
|
155
|
+
const isMainModule = import.meta.url === `file://${process.argv[1]}`;
|
|
156
|
+
if (isMainModule) {
|
|
157
|
+
const args = process.argv.slice(2);
|
|
158
|
+
|
|
159
|
+
if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
|
|
160
|
+
console.log(`
|
|
161
|
+
Usage: npx tsx backup-memory.ts <agent-id> [backup-dir]
|
|
162
|
+
|
|
163
|
+
Arguments:
|
|
164
|
+
agent-id Agent ID to backup (can use $LETTA_AGENT_ID)
|
|
165
|
+
backup-dir Optional custom backup directory
|
|
166
|
+
Default: .letta/backups/<agent-id>/<timestamp>
|
|
167
|
+
|
|
168
|
+
Examples:
|
|
169
|
+
npx tsx backup-memory.ts agent-abc123
|
|
170
|
+
npx tsx backup-memory.ts $LETTA_AGENT_ID
|
|
171
|
+
npx tsx backup-memory.ts agent-abc123 .letta/backups/working
|
|
172
|
+
`);
|
|
173
|
+
process.exit(0);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const agentId = args[0];
|
|
177
|
+
const backupDir = args[1];
|
|
178
|
+
|
|
179
|
+
if (!agentId) {
|
|
180
|
+
console.error("Error: agent-id is required");
|
|
181
|
+
process.exit(1);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
backupMemory(agentId, backupDir)
|
|
185
|
+
.then((path) => {
|
|
186
|
+
// Output just the path for easy capture in scripts
|
|
187
|
+
console.log(path);
|
|
188
|
+
})
|
|
189
|
+
.catch((error) => {
|
|
190
|
+
console.error(
|
|
191
|
+
"Error backing up memory:",
|
|
192
|
+
error instanceof Error ? error.message : String(error),
|
|
193
|
+
);
|
|
194
|
+
process.exit(1);
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export { backupMemory };
|