@letta-ai/letta-code 0.14.4 → 0.14.6

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.
@@ -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
- }
@@ -1,230 +0,0 @@
1
- #!/usr/bin/env npx tsx
2
-
3
- /**
4
- * Get Messages - Retrieve messages from an agent in chronological order
5
- *
6
- * This script is standalone and can be run outside the CLI process.
7
- * It reads auth from LETTA_API_KEY env var or ~/.letta/settings.json.
8
- * It reads agent ID from LETTA_AGENT_ID env var or --agent-id arg.
9
- *
10
- * Usage:
11
- * npx tsx get-messages.ts [options]
12
- *
13
- * Options:
14
- * --start-date <date> Filter messages after this date (ISO format)
15
- * --end-date <date> Filter messages before this date (ISO format)
16
- * --limit <n> Max results (default: 20)
17
- * --agent-id <id> Explicit agent ID (overrides LETTA_AGENT_ID env var)
18
- * --after <message-id> Cursor: get messages after this ID
19
- * --before <message-id> Cursor: get messages before this ID
20
- * --order <asc|desc> Sort order (default: desc = newest first)
21
- *
22
- * Use this after search-messages.ts to expand around a found needle.
23
- *
24
- * Output:
25
- * Messages in chronological order (filtered by date if specified)
26
- */
27
-
28
- import { readFileSync } from "node:fs";
29
- import { createRequire } from "node:module";
30
- import { homedir } from "node:os";
31
- import { join } from "node:path";
32
-
33
- // Use createRequire for @letta-ai/letta-client so NODE_PATH is respected
34
- // (ES module imports don't respect NODE_PATH, but require does)
35
- const require = createRequire(import.meta.url);
36
- const Letta = require("@letta-ai/letta-client")
37
- .default as typeof import("@letta-ai/letta-client").default;
38
- type LettaClient = InstanceType<typeof Letta>;
39
-
40
- interface GetMessagesOptions {
41
- startDate?: string;
42
- endDate?: string;
43
- limit?: number;
44
- agentId?: string;
45
- after?: string;
46
- before?: string;
47
- order?: "asc" | "desc";
48
- }
49
-
50
- /**
51
- * Get API key from env var or settings file
52
- */
53
- function getApiKey(): string {
54
- // First check env var (set by CLI's getShellEnv)
55
- if (process.env.LETTA_API_KEY) {
56
- return process.env.LETTA_API_KEY;
57
- }
58
-
59
- // Fall back to settings file
60
- const settingsPath = join(homedir(), ".letta", "settings.json");
61
- try {
62
- const settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
63
- if (settings.env?.LETTA_API_KEY) {
64
- return settings.env.LETTA_API_KEY;
65
- }
66
- } catch {
67
- // Settings file doesn't exist or is invalid
68
- }
69
-
70
- throw new Error(
71
- "No LETTA_API_KEY found. Set the env var or run the Letta CLI to authenticate.",
72
- );
73
- }
74
-
75
- /**
76
- * Get agent ID from CLI arg, env var, or throw
77
- */
78
- function getAgentId(cliArg?: string): string {
79
- // CLI arg takes precedence
80
- if (cliArg) return cliArg;
81
-
82
- // Then env var (set by CLI's getShellEnv)
83
- if (process.env.LETTA_AGENT_ID) {
84
- return process.env.LETTA_AGENT_ID;
85
- }
86
-
87
- throw new Error(
88
- "No agent ID provided. Use --agent-id or ensure LETTA_AGENT_ID env var is set.",
89
- );
90
- }
91
-
92
- /**
93
- * Create a Letta client with auth from env/settings
94
- */
95
- function createClient(): LettaClient {
96
- return new Letta({ apiKey: getApiKey() });
97
- }
98
-
99
- /**
100
- * Get messages from an agent, optionally filtered by date range
101
- * @param client - Letta client instance
102
- * @param options - Options for filtering
103
- * @returns Array of messages in chronological order
104
- */
105
- export async function getMessages(
106
- client: LettaClient,
107
- options: GetMessagesOptions = {},
108
- ): Promise<unknown[]> {
109
- const agentId = getAgentId(options.agentId);
110
- const limit = options.limit ?? 20;
111
-
112
- // Fetch messages from the agent
113
- const response = await client.agents.messages.list(agentId, {
114
- limit,
115
- after: options.after,
116
- before: options.before,
117
- order: options.order,
118
- });
119
-
120
- const messages = response.items ?? [];
121
-
122
- // Client-side date filtering if specified
123
- if (options.startDate || options.endDate) {
124
- const startTime = options.startDate
125
- ? new Date(options.startDate).getTime()
126
- : 0;
127
- const endTime = options.endDate
128
- ? new Date(options.endDate).getTime()
129
- : Number.POSITIVE_INFINITY;
130
-
131
- const filtered = messages.filter((msg) => {
132
- // Messages use 'date' field, not 'created_at'
133
- if (!("date" in msg) || !msg.date) return true;
134
- const msgTime = new Date(msg.date).getTime();
135
- return msgTime >= startTime && msgTime <= endTime;
136
- });
137
-
138
- // Sort chronologically (oldest first)
139
- return filtered.sort((a, b) => {
140
- const aDate = "date" in a && a.date ? new Date(a.date).getTime() : 0;
141
- const bDate = "date" in b && b.date ? new Date(b.date).getTime() : 0;
142
- return aDate - bDate;
143
- });
144
- }
145
-
146
- // Sort chronologically (oldest first)
147
- return [...messages].sort((a, b) => {
148
- const aDate = "date" in a && a.date ? new Date(a.date).getTime() : 0;
149
- const bDate = "date" in b && b.date ? new Date(b.date).getTime() : 0;
150
- return aDate - bDate;
151
- });
152
- }
153
-
154
- function parseArgs(args: string[]): GetMessagesOptions {
155
- const options: GetMessagesOptions = {};
156
-
157
- const startDateIndex = args.indexOf("--start-date");
158
- if (startDateIndex !== -1 && startDateIndex + 1 < args.length) {
159
- options.startDate = args[startDateIndex + 1];
160
- }
161
-
162
- const endDateIndex = args.indexOf("--end-date");
163
- if (endDateIndex !== -1 && endDateIndex + 1 < args.length) {
164
- options.endDate = args[endDateIndex + 1];
165
- }
166
-
167
- const limitIndex = args.indexOf("--limit");
168
- if (limitIndex !== -1 && limitIndex + 1 < args.length) {
169
- const limit = Number.parseInt(args[limitIndex + 1] as string, 10);
170
- if (!Number.isNaN(limit)) {
171
- options.limit = limit;
172
- }
173
- }
174
-
175
- const agentIdIndex = args.indexOf("--agent-id");
176
- if (agentIdIndex !== -1 && agentIdIndex + 1 < args.length) {
177
- options.agentId = args[agentIdIndex + 1];
178
- }
179
-
180
- const afterIndex = args.indexOf("--after");
181
- if (afterIndex !== -1 && afterIndex + 1 < args.length) {
182
- options.after = args[afterIndex + 1];
183
- }
184
-
185
- const beforeIndex = args.indexOf("--before");
186
- if (beforeIndex !== -1 && beforeIndex + 1 < args.length) {
187
- options.before = args[beforeIndex + 1];
188
- }
189
-
190
- const orderIndex = args.indexOf("--order");
191
- if (orderIndex !== -1 && orderIndex + 1 < args.length) {
192
- const order = args[orderIndex + 1] as string;
193
- if (order === "asc" || order === "desc") {
194
- options.order = order;
195
- }
196
- }
197
-
198
- return options;
199
- }
200
-
201
- // CLI entry point - check if this file is being run directly
202
- const isMainModule = import.meta.url === `file://${process.argv[1]}`;
203
- if (isMainModule) {
204
- (async () => {
205
- try {
206
- const options = parseArgs(process.argv.slice(2));
207
- const client = createClient();
208
- const result = await getMessages(client, options);
209
- console.log(JSON.stringify(result, null, 2));
210
- } catch (error) {
211
- console.error(
212
- "Error:",
213
- error instanceof Error ? error.message : String(error),
214
- );
215
- console.error(`
216
- Usage: npx tsx get-messages.ts [options]
217
-
218
- Options:
219
- --after <message-id> Cursor: get messages after this ID
220
- --before <message-id> Cursor: get messages before this ID
221
- --order <asc|desc> Sort order (default: desc = newest first)
222
- --limit <n> Max results (default: 20)
223
- --agent-id <id> Explicit agent ID (overrides LETTA_AGENT_ID env var)
224
- --start-date <date> Client-side filter: after this date (ISO format)
225
- --end-date <date> Client-side filter: before this date (ISO format)
226
- `);
227
- process.exit(1);
228
- }
229
- })();
230
- }