@letta-ai/letta-code 0.14.3 → 0.14.5
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 +10772 -9218
- package/package.json +2 -2
- package/skills/defragmenting-memory/SKILL.md +4 -5
- package/skills/finding-agents/SKILL.md +12 -12
- package/skills/messaging-agents/SKILL.md +19 -19
- package/skills/migrating-memory/SKILL.md +47 -81
- package/skills/searching-messages/SKILL.md +14 -16
- package/skills/syncing-memory-filesystem/SKILL.md +27 -46
- package/skills/finding-agents/scripts/find-agents.ts +0 -177
- package/skills/messaging-agents/scripts/continue-conversation.ts +0 -226
- package/skills/messaging-agents/scripts/start-conversation.ts +0 -229
- package/skills/migrating-memory/scripts/attach-block.ts +0 -230
- package/skills/migrating-memory/scripts/copy-block.ts +0 -261
- package/skills/migrating-memory/scripts/get-agent-blocks.ts +0 -103
- package/skills/searching-messages/scripts/get-messages.ts +0 -230
- package/skills/searching-messages/scripts/search-messages.ts +0 -200
- package/skills/syncing-memory-filesystem/scripts/memfs-diff.ts +0 -361
- package/skills/syncing-memory-filesystem/scripts/memfs-resolve.ts +0 -412
- package/skills/syncing-memory-filesystem/scripts/memfs-status.ts +0 -354
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env npx tsx
|
|
2
|
-
/**
|
|
3
|
-
* Attach Block - Attaches an existing memory block to an agent (sharing)
|
|
4
|
-
*
|
|
5
|
-
* This script is standalone and can be run outside the CLI process.
|
|
6
|
-
* It reads auth from LETTA_API_KEY env var or ~/.letta/settings.json.
|
|
7
|
-
* It reads agent ID from LETTA_AGENT_ID env var or --agent-id arg.
|
|
8
|
-
*
|
|
9
|
-
* Usage:
|
|
10
|
-
* npx tsx attach-block.ts --block-id <block-id> [--agent-id <agent-id>] [--read-only] [--override]
|
|
11
|
-
*
|
|
12
|
-
* This attaches an existing block to another agent, making it shared.
|
|
13
|
-
* Changes to the block will be visible to all agents that have it attached.
|
|
14
|
-
*
|
|
15
|
-
* Options:
|
|
16
|
-
* --agent-id Target agent ID (overrides LETTA_AGENT_ID env var)
|
|
17
|
-
* --read-only Target agent can read but not modify the block
|
|
18
|
-
* --override If you already have a block with the same label, detach it first
|
|
19
|
-
* (on error, the original block is reattached)
|
|
20
|
-
*
|
|
21
|
-
* Output:
|
|
22
|
-
* Raw API response from the attach operation
|
|
23
|
-
*/
|
|
24
|
-
|
|
25
|
-
import { readFileSync } from "node:fs";
|
|
26
|
-
import { createRequire } from "node:module";
|
|
27
|
-
import { homedir } from "node:os";
|
|
28
|
-
import { join } from "node:path";
|
|
29
|
-
|
|
30
|
-
// Use createRequire for @letta-ai/letta-client so NODE_PATH is respected
|
|
31
|
-
// (ES module imports don't respect NODE_PATH, but require does)
|
|
32
|
-
const require = createRequire(import.meta.url);
|
|
33
|
-
const Letta = require("@letta-ai/letta-client")
|
|
34
|
-
.default as typeof import("@letta-ai/letta-client").default;
|
|
35
|
-
type LettaClient = InstanceType<typeof Letta>;
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Get API key from env var or settings file
|
|
39
|
-
*/
|
|
40
|
-
function getApiKey(): string {
|
|
41
|
-
if (process.env.LETTA_API_KEY) {
|
|
42
|
-
return process.env.LETTA_API_KEY;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const settingsPath = join(homedir(), ".letta", "settings.json");
|
|
46
|
-
try {
|
|
47
|
-
const settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
48
|
-
if (settings.env?.LETTA_API_KEY) {
|
|
49
|
-
return settings.env.LETTA_API_KEY;
|
|
50
|
-
}
|
|
51
|
-
} catch {
|
|
52
|
-
// Settings file doesn't exist or is invalid
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
throw new Error(
|
|
56
|
-
"No LETTA_API_KEY found. Set the env var or run the Letta CLI to authenticate.",
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Get agent ID from CLI arg, env var, or throw
|
|
62
|
-
*/
|
|
63
|
-
function getAgentId(cliArg?: string): string {
|
|
64
|
-
if (cliArg) return cliArg;
|
|
65
|
-
if (process.env.LETTA_AGENT_ID) {
|
|
66
|
-
return process.env.LETTA_AGENT_ID;
|
|
67
|
-
}
|
|
68
|
-
throw new Error(
|
|
69
|
-
"No agent ID provided. Use --agent-id or ensure LETTA_AGENT_ID env var is set.",
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Create a Letta client with auth from env/settings
|
|
75
|
-
*/
|
|
76
|
-
function createClient(): LettaClient {
|
|
77
|
-
return new Letta({ apiKey: getApiKey() });
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
interface AttachBlockResult {
|
|
81
|
-
attachResult: Awaited<ReturnType<LettaClient["agents"]["blocks"]["attach"]>>;
|
|
82
|
-
detachedBlock?: Awaited<ReturnType<LettaClient["blocks"]["retrieve"]>>;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Attach an existing block to the current agent (sharing it)
|
|
87
|
-
* @param client - Letta client instance
|
|
88
|
-
* @param blockId - The block ID to attach
|
|
89
|
-
* @param options - readOnly, targetAgentId, override (detach existing block with same label)
|
|
90
|
-
* @returns API response from the attach operation
|
|
91
|
-
*/
|
|
92
|
-
export async function attachBlock(
|
|
93
|
-
client: LettaClient,
|
|
94
|
-
blockId: string,
|
|
95
|
-
options?: { readOnly?: boolean; targetAgentId?: string; override?: boolean },
|
|
96
|
-
): Promise<AttachBlockResult> {
|
|
97
|
-
const currentAgentId = getAgentId(options?.targetAgentId);
|
|
98
|
-
let detachedBlock:
|
|
99
|
-
| Awaited<ReturnType<LettaClient["blocks"]["retrieve"]>>
|
|
100
|
-
| undefined;
|
|
101
|
-
|
|
102
|
-
// If override is requested, check for existing block with same label and detach it
|
|
103
|
-
if (options?.override) {
|
|
104
|
-
// Get the block we're trying to attach to find its label
|
|
105
|
-
const sourceBlock = await client.blocks.retrieve(blockId);
|
|
106
|
-
const sourceLabel = sourceBlock.label;
|
|
107
|
-
|
|
108
|
-
// Get current agent's blocks to check for label conflict
|
|
109
|
-
const currentBlocksResponse =
|
|
110
|
-
await client.agents.blocks.list(currentAgentId);
|
|
111
|
-
// The response may be paginated or an array depending on SDK version
|
|
112
|
-
const currentBlocks = Array.isArray(currentBlocksResponse)
|
|
113
|
-
? currentBlocksResponse
|
|
114
|
-
: (currentBlocksResponse as { items?: unknown[] }).items || [];
|
|
115
|
-
const conflictingBlock = currentBlocks.find(
|
|
116
|
-
(b: { label?: string }) => b.label === sourceLabel,
|
|
117
|
-
);
|
|
118
|
-
|
|
119
|
-
if (conflictingBlock) {
|
|
120
|
-
console.error(
|
|
121
|
-
`Detaching existing block with label "${sourceLabel}" (${conflictingBlock.id})...`,
|
|
122
|
-
);
|
|
123
|
-
detachedBlock = conflictingBlock;
|
|
124
|
-
try {
|
|
125
|
-
await client.agents.blocks.detach(conflictingBlock.id, {
|
|
126
|
-
agent_id: currentAgentId,
|
|
127
|
-
});
|
|
128
|
-
} catch (detachError) {
|
|
129
|
-
throw new Error(
|
|
130
|
-
`Failed to detach existing block "${sourceLabel}": ${detachError instanceof Error ? detachError.message : String(detachError)}`,
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Attempt to attach the new block
|
|
137
|
-
let attachResult: Awaited<ReturnType<typeof client.agents.blocks.attach>>;
|
|
138
|
-
try {
|
|
139
|
-
attachResult = await client.agents.blocks.attach(blockId, {
|
|
140
|
-
agent_id: currentAgentId,
|
|
141
|
-
});
|
|
142
|
-
} catch (attachError) {
|
|
143
|
-
// If attach failed and we detached a block, try to reattach it
|
|
144
|
-
if (detachedBlock) {
|
|
145
|
-
console.error(
|
|
146
|
-
`Attach failed, reattaching original block "${detachedBlock.label}"...`,
|
|
147
|
-
);
|
|
148
|
-
try {
|
|
149
|
-
await client.agents.blocks.attach(detachedBlock.id, {
|
|
150
|
-
agent_id: currentAgentId,
|
|
151
|
-
});
|
|
152
|
-
console.error("Original block reattached successfully.");
|
|
153
|
-
} catch {
|
|
154
|
-
console.error(
|
|
155
|
-
`WARNING: Failed to reattach original block! Block ID: ${detachedBlock.id}`,
|
|
156
|
-
);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
throw attachError;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// If read-only is requested, note the limitation
|
|
163
|
-
if (options?.readOnly) {
|
|
164
|
-
console.warn(
|
|
165
|
-
"Note: read_only flag is set on the block itself, not per-agent. " +
|
|
166
|
-
"Use the block update API to set read_only if needed.",
|
|
167
|
-
);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
return { attachResult, detachedBlock };
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
function parseArgs(args: string[]): {
|
|
174
|
-
blockId: string;
|
|
175
|
-
readOnly: boolean;
|
|
176
|
-
override: boolean;
|
|
177
|
-
agentId?: string;
|
|
178
|
-
} {
|
|
179
|
-
const blockIdIndex = args.indexOf("--block-id");
|
|
180
|
-
const agentIdIndex = args.indexOf("--agent-id");
|
|
181
|
-
const readOnly = args.includes("--read-only");
|
|
182
|
-
const override = args.includes("--override");
|
|
183
|
-
|
|
184
|
-
if (blockIdIndex === -1 || blockIdIndex + 1 >= args.length) {
|
|
185
|
-
throw new Error("Missing required argument: --block-id <block-id>");
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
return {
|
|
189
|
-
blockId: args[blockIdIndex + 1] as string,
|
|
190
|
-
readOnly,
|
|
191
|
-
override,
|
|
192
|
-
agentId:
|
|
193
|
-
agentIdIndex !== -1 && agentIdIndex + 1 < args.length
|
|
194
|
-
? (args[agentIdIndex + 1] as string)
|
|
195
|
-
: undefined,
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// CLI entry point - check if this file is being run directly
|
|
200
|
-
const isMainModule = import.meta.url === `file://${process.argv[1]}`;
|
|
201
|
-
if (isMainModule) {
|
|
202
|
-
(async () => {
|
|
203
|
-
try {
|
|
204
|
-
const { blockId, readOnly, override, agentId } = parseArgs(
|
|
205
|
-
process.argv.slice(2),
|
|
206
|
-
);
|
|
207
|
-
const client = createClient();
|
|
208
|
-
const result = await attachBlock(client, blockId, {
|
|
209
|
-
readOnly,
|
|
210
|
-
override,
|
|
211
|
-
targetAgentId: agentId,
|
|
212
|
-
});
|
|
213
|
-
console.log(JSON.stringify(result, null, 2));
|
|
214
|
-
} catch (error) {
|
|
215
|
-
console.error(
|
|
216
|
-
"Error:",
|
|
217
|
-
error instanceof Error ? error.message : String(error),
|
|
218
|
-
);
|
|
219
|
-
if (
|
|
220
|
-
error instanceof Error &&
|
|
221
|
-
error.message.includes("Missing required argument")
|
|
222
|
-
) {
|
|
223
|
-
console.error(
|
|
224
|
-
"\nUsage: npx tsx attach-block.ts --block-id <block-id> [--agent-id <agent-id>] [--read-only] [--override]",
|
|
225
|
-
);
|
|
226
|
-
}
|
|
227
|
-
process.exit(1);
|
|
228
|
-
}
|
|
229
|
-
})();
|
|
230
|
-
}
|
|
@@ -1,261 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env npx tsx
|
|
2
|
-
/**
|
|
3
|
-
* Copy Block - Copies a memory block to create a new independent block for the current agent
|
|
4
|
-
*
|
|
5
|
-
* This script is standalone and can be run outside the CLI process.
|
|
6
|
-
* It reads auth from LETTA_API_KEY env var or ~/.letta/settings.json.
|
|
7
|
-
* It reads agent ID from LETTA_AGENT_ID env var or --agent-id arg.
|
|
8
|
-
*
|
|
9
|
-
* Usage:
|
|
10
|
-
* npx tsx copy-block.ts --block-id <block-id> [--label <new-label>] [--agent-id <agent-id>] [--override]
|
|
11
|
-
*
|
|
12
|
-
* Options:
|
|
13
|
-
* --label Override the block label (useful to avoid duplicate label errors)
|
|
14
|
-
* --agent-id Target agent ID (overrides LETTA_AGENT_ID env var)
|
|
15
|
-
* --override If you already have a block with the same label, detach it first
|
|
16
|
-
* (on error, the original block is reattached)
|
|
17
|
-
*
|
|
18
|
-
* This creates a new block with the same content as the source block,
|
|
19
|
-
* then attaches it to the current agent. Changes to the new block
|
|
20
|
-
* won't affect the original.
|
|
21
|
-
*
|
|
22
|
-
* Output:
|
|
23
|
-
* Raw API response from each step (retrieve, create, attach)
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
import { readFileSync } from "node:fs";
|
|
27
|
-
import { createRequire } from "node:module";
|
|
28
|
-
import { homedir } from "node:os";
|
|
29
|
-
import { join } from "node:path";
|
|
30
|
-
|
|
31
|
-
// Use createRequire for @letta-ai/letta-client so NODE_PATH is respected
|
|
32
|
-
// (ES module imports don't respect NODE_PATH, but require does)
|
|
33
|
-
const require = createRequire(import.meta.url);
|
|
34
|
-
const Letta = require("@letta-ai/letta-client")
|
|
35
|
-
.default as typeof import("@letta-ai/letta-client").default;
|
|
36
|
-
type LettaClient = InstanceType<typeof Letta>;
|
|
37
|
-
|
|
38
|
-
interface CopyBlockResult {
|
|
39
|
-
sourceBlock: Awaited<ReturnType<LettaClient["blocks"]["retrieve"]>>;
|
|
40
|
-
newBlock: Awaited<ReturnType<LettaClient["blocks"]["create"]>>;
|
|
41
|
-
attachResult: Awaited<ReturnType<LettaClient["agents"]["blocks"]["attach"]>>;
|
|
42
|
-
detachedBlock?: Awaited<ReturnType<LettaClient["blocks"]["retrieve"]>>;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Get API key from env var or settings file
|
|
47
|
-
*/
|
|
48
|
-
function getApiKey(): string {
|
|
49
|
-
if (process.env.LETTA_API_KEY) {
|
|
50
|
-
return process.env.LETTA_API_KEY;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const settingsPath = join(homedir(), ".letta", "settings.json");
|
|
54
|
-
try {
|
|
55
|
-
const settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
56
|
-
if (settings.env?.LETTA_API_KEY) {
|
|
57
|
-
return settings.env.LETTA_API_KEY;
|
|
58
|
-
}
|
|
59
|
-
} catch {
|
|
60
|
-
// Settings file doesn't exist or is invalid
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
throw new Error(
|
|
64
|
-
"No LETTA_API_KEY found. Set the env var or run the Letta CLI to authenticate.",
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Get agent ID from CLI arg, env var, or throw
|
|
70
|
-
*/
|
|
71
|
-
function getAgentId(cliArg?: string): string {
|
|
72
|
-
if (cliArg) return cliArg;
|
|
73
|
-
if (process.env.LETTA_AGENT_ID) {
|
|
74
|
-
return process.env.LETTA_AGENT_ID;
|
|
75
|
-
}
|
|
76
|
-
throw new Error(
|
|
77
|
-
"No agent ID provided. Use --agent-id or ensure LETTA_AGENT_ID env var is set.",
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Create a Letta client with auth from env/settings
|
|
83
|
-
*/
|
|
84
|
-
function createClient(): LettaClient {
|
|
85
|
-
return new Letta({ apiKey: getApiKey() });
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Copy a block's content to a new block and attach to the current agent
|
|
90
|
-
* @param client - Letta client instance
|
|
91
|
-
* @param blockId - The source block ID to copy from
|
|
92
|
-
* @param options - Optional settings: labelOverride, targetAgentId, override
|
|
93
|
-
* @returns Object containing source block, new block, and attach result
|
|
94
|
-
*/
|
|
95
|
-
export async function copyBlock(
|
|
96
|
-
client: LettaClient,
|
|
97
|
-
blockId: string,
|
|
98
|
-
options?: {
|
|
99
|
-
labelOverride?: string;
|
|
100
|
-
targetAgentId?: string;
|
|
101
|
-
override?: boolean;
|
|
102
|
-
},
|
|
103
|
-
): Promise<CopyBlockResult> {
|
|
104
|
-
const currentAgentId = getAgentId(options?.targetAgentId);
|
|
105
|
-
let detachedBlock:
|
|
106
|
-
| Awaited<ReturnType<LettaClient["blocks"]["retrieve"]>>
|
|
107
|
-
| undefined;
|
|
108
|
-
|
|
109
|
-
// 1. Get source block details
|
|
110
|
-
const sourceBlock = await client.blocks.retrieve(blockId);
|
|
111
|
-
const targetLabel =
|
|
112
|
-
options?.labelOverride || sourceBlock.label || "migrated-block";
|
|
113
|
-
|
|
114
|
-
// 2. If override is requested, check for existing block with same label and detach it
|
|
115
|
-
if (options?.override) {
|
|
116
|
-
const currentBlocksResponse =
|
|
117
|
-
await client.agents.blocks.list(currentAgentId);
|
|
118
|
-
// The response may be paginated or an array depending on SDK version
|
|
119
|
-
const currentBlocks = Array.isArray(currentBlocksResponse)
|
|
120
|
-
? currentBlocksResponse
|
|
121
|
-
: (currentBlocksResponse as { items?: unknown[] }).items || [];
|
|
122
|
-
const conflictingBlock = currentBlocks.find(
|
|
123
|
-
(b: { label?: string }) => b.label === targetLabel,
|
|
124
|
-
);
|
|
125
|
-
|
|
126
|
-
if (conflictingBlock) {
|
|
127
|
-
console.error(
|
|
128
|
-
`Detaching existing block with label "${targetLabel}" (${conflictingBlock.id})...`,
|
|
129
|
-
);
|
|
130
|
-
detachedBlock = conflictingBlock;
|
|
131
|
-
try {
|
|
132
|
-
await client.agents.blocks.detach(conflictingBlock.id, {
|
|
133
|
-
agent_id: currentAgentId,
|
|
134
|
-
});
|
|
135
|
-
} catch (detachError) {
|
|
136
|
-
throw new Error(
|
|
137
|
-
`Failed to detach existing block "${targetLabel}": ${detachError instanceof Error ? detachError.message : String(detachError)}`,
|
|
138
|
-
);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// 3. Create new block with same content
|
|
144
|
-
let newBlock: Awaited<ReturnType<LettaClient["blocks"]["create"]>>;
|
|
145
|
-
try {
|
|
146
|
-
newBlock = await client.blocks.create({
|
|
147
|
-
label: targetLabel,
|
|
148
|
-
value: sourceBlock.value,
|
|
149
|
-
description: sourceBlock.description || undefined,
|
|
150
|
-
limit: sourceBlock.limit,
|
|
151
|
-
});
|
|
152
|
-
} catch (createError) {
|
|
153
|
-
// If create failed and we detached a block, try to reattach it
|
|
154
|
-
if (detachedBlock) {
|
|
155
|
-
console.error(
|
|
156
|
-
`Create failed, reattaching original block "${detachedBlock.label}"...`,
|
|
157
|
-
);
|
|
158
|
-
try {
|
|
159
|
-
await client.agents.blocks.attach(detachedBlock.id, {
|
|
160
|
-
agent_id: currentAgentId,
|
|
161
|
-
});
|
|
162
|
-
console.error("Original block reattached successfully.");
|
|
163
|
-
} catch {
|
|
164
|
-
console.error(
|
|
165
|
-
`WARNING: Failed to reattach original block! Block ID: ${detachedBlock.id}`,
|
|
166
|
-
);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
throw createError;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// 4. Attach new block to current agent
|
|
173
|
-
let attachResult: Awaited<ReturnType<typeof client.agents.blocks.attach>>;
|
|
174
|
-
try {
|
|
175
|
-
attachResult = await client.agents.blocks.attach(newBlock.id, {
|
|
176
|
-
agent_id: currentAgentId,
|
|
177
|
-
});
|
|
178
|
-
} catch (attachError) {
|
|
179
|
-
// If attach failed and we detached a block, try to reattach it
|
|
180
|
-
if (detachedBlock) {
|
|
181
|
-
console.error(
|
|
182
|
-
`Attach failed, reattaching original block "${detachedBlock.label}"...`,
|
|
183
|
-
);
|
|
184
|
-
try {
|
|
185
|
-
await client.agents.blocks.attach(detachedBlock.id, {
|
|
186
|
-
agent_id: currentAgentId,
|
|
187
|
-
});
|
|
188
|
-
console.error("Original block reattached successfully.");
|
|
189
|
-
} catch {
|
|
190
|
-
console.error(
|
|
191
|
-
`WARNING: Failed to reattach original block! Block ID: ${detachedBlock.id}`,
|
|
192
|
-
);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
throw attachError;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
return { sourceBlock, newBlock, attachResult, detachedBlock };
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
function parseArgs(args: string[]): {
|
|
202
|
-
blockId: string;
|
|
203
|
-
label?: string;
|
|
204
|
-
agentId?: string;
|
|
205
|
-
override: boolean;
|
|
206
|
-
} {
|
|
207
|
-
const blockIdIndex = args.indexOf("--block-id");
|
|
208
|
-
const labelIndex = args.indexOf("--label");
|
|
209
|
-
const agentIdIndex = args.indexOf("--agent-id");
|
|
210
|
-
const override = args.includes("--override");
|
|
211
|
-
|
|
212
|
-
if (blockIdIndex === -1 || blockIdIndex + 1 >= args.length) {
|
|
213
|
-
throw new Error("Missing required argument: --block-id <block-id>");
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
return {
|
|
217
|
-
blockId: args[blockIdIndex + 1] as string,
|
|
218
|
-
label:
|
|
219
|
-
labelIndex !== -1 && labelIndex + 1 < args.length
|
|
220
|
-
? (args[labelIndex + 1] as string)
|
|
221
|
-
: undefined,
|
|
222
|
-
agentId:
|
|
223
|
-
agentIdIndex !== -1 && agentIdIndex + 1 < args.length
|
|
224
|
-
? (args[agentIdIndex + 1] as string)
|
|
225
|
-
: undefined,
|
|
226
|
-
override,
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// CLI entry point - check if this file is being run directly
|
|
231
|
-
const isMainModule = import.meta.url === `file://${process.argv[1]}`;
|
|
232
|
-
if (isMainModule) {
|
|
233
|
-
(async () => {
|
|
234
|
-
try {
|
|
235
|
-
const { blockId, label, agentId, override } = parseArgs(
|
|
236
|
-
process.argv.slice(2),
|
|
237
|
-
);
|
|
238
|
-
const client = createClient();
|
|
239
|
-
const result = await copyBlock(client, blockId, {
|
|
240
|
-
labelOverride: label,
|
|
241
|
-
targetAgentId: agentId,
|
|
242
|
-
override,
|
|
243
|
-
});
|
|
244
|
-
console.log(JSON.stringify(result, null, 2));
|
|
245
|
-
} catch (error) {
|
|
246
|
-
console.error(
|
|
247
|
-
"Error:",
|
|
248
|
-
error instanceof Error ? error.message : String(error),
|
|
249
|
-
);
|
|
250
|
-
if (
|
|
251
|
-
error instanceof Error &&
|
|
252
|
-
error.message.includes("Missing required argument")
|
|
253
|
-
) {
|
|
254
|
-
console.error(
|
|
255
|
-
"\nUsage: npx tsx copy-block.ts --block-id <block-id> [--label <new-label>] [--agent-id <agent-id>] [--override]",
|
|
256
|
-
);
|
|
257
|
-
}
|
|
258
|
-
process.exit(1);
|
|
259
|
-
}
|
|
260
|
-
})();
|
|
261
|
-
}
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env npx tsx
|
|
2
|
-
/**
|
|
3
|
-
* Get Agent Blocks - Retrieves memory blocks from a specific agent
|
|
4
|
-
*
|
|
5
|
-
* This script is standalone and can be run outside the CLI process.
|
|
6
|
-
* It reads auth from LETTA_API_KEY env var or ~/.letta/settings.json.
|
|
7
|
-
*
|
|
8
|
-
* Usage:
|
|
9
|
-
* npx tsx get-agent-blocks.ts --agent-id <agent-id>
|
|
10
|
-
*
|
|
11
|
-
* Output:
|
|
12
|
-
* Raw API response from GET /v1/agents/{id}/core-memory/blocks
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import { readFileSync } from "node:fs";
|
|
16
|
-
import { createRequire } from "node:module";
|
|
17
|
-
import { homedir } from "node:os";
|
|
18
|
-
import { join } from "node:path";
|
|
19
|
-
|
|
20
|
-
// Use createRequire for @letta-ai/letta-client so NODE_PATH is respected
|
|
21
|
-
// (ES module imports don't respect NODE_PATH, but require does)
|
|
22
|
-
const require = createRequire(import.meta.url);
|
|
23
|
-
const Letta = require("@letta-ai/letta-client")
|
|
24
|
-
.default as typeof import("@letta-ai/letta-client").default;
|
|
25
|
-
type LettaClient = InstanceType<typeof Letta>;
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Get API key from env var or settings file
|
|
29
|
-
*/
|
|
30
|
-
function getApiKey(): string {
|
|
31
|
-
if (process.env.LETTA_API_KEY) {
|
|
32
|
-
return process.env.LETTA_API_KEY;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const settingsPath = join(homedir(), ".letta", "settings.json");
|
|
36
|
-
try {
|
|
37
|
-
const settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
38
|
-
if (settings.env?.LETTA_API_KEY) {
|
|
39
|
-
return settings.env.LETTA_API_KEY;
|
|
40
|
-
}
|
|
41
|
-
} catch {
|
|
42
|
-
// Settings file doesn't exist or is invalid
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
throw new Error(
|
|
46
|
-
"No LETTA_API_KEY found. Set the env var or run the Letta CLI to authenticate.",
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Create a Letta client with auth from env/settings
|
|
52
|
-
*/
|
|
53
|
-
function createClient(): LettaClient {
|
|
54
|
-
return new Letta({ apiKey: getApiKey() });
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Get memory blocks for a specific agent
|
|
59
|
-
* @param client - Letta client instance
|
|
60
|
-
* @param agentId - The agent ID to get blocks from
|
|
61
|
-
* @returns Array of block objects from the API
|
|
62
|
-
*/
|
|
63
|
-
export async function getAgentBlocks(
|
|
64
|
-
client: LettaClient,
|
|
65
|
-
agentId: string,
|
|
66
|
-
): Promise<Awaited<ReturnType<typeof client.agents.blocks.list>>> {
|
|
67
|
-
return await client.agents.blocks.list(agentId);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function parseArgs(args: string[]): { agentId: string } {
|
|
71
|
-
const agentIdIndex = args.indexOf("--agent-id");
|
|
72
|
-
if (agentIdIndex === -1 || agentIdIndex + 1 >= args.length) {
|
|
73
|
-
throw new Error("Missing required argument: --agent-id <agent-id>");
|
|
74
|
-
}
|
|
75
|
-
return { agentId: args[agentIdIndex + 1] as string };
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// CLI entry point - check if this file is being run directly
|
|
79
|
-
const isMainModule = import.meta.url === `file://${process.argv[1]}`;
|
|
80
|
-
if (isMainModule) {
|
|
81
|
-
(async () => {
|
|
82
|
-
try {
|
|
83
|
-
const { agentId } = parseArgs(process.argv.slice(2));
|
|
84
|
-
const client = createClient();
|
|
85
|
-
const result = await getAgentBlocks(client, agentId);
|
|
86
|
-
console.log(JSON.stringify(result, null, 2));
|
|
87
|
-
} catch (error) {
|
|
88
|
-
console.error(
|
|
89
|
-
"Error:",
|
|
90
|
-
error instanceof Error ? error.message : String(error),
|
|
91
|
-
);
|
|
92
|
-
if (
|
|
93
|
-
error instanceof Error &&
|
|
94
|
-
error.message.includes("Missing required argument")
|
|
95
|
-
) {
|
|
96
|
-
console.error(
|
|
97
|
-
"\nUsage: npx tsx get-agent-blocks.ts --agent-id <agent-id>",
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
process.exit(1);
|
|
101
|
-
}
|
|
102
|
-
})();
|
|
103
|
-
}
|