@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.
@@ -1,177 +0,0 @@
1
- #!/usr/bin/env npx tsx
2
- /**
3
- * Find Agents - Search for agents with various filters
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 find-agents.ts [options]
10
- *
11
- * Options:
12
- * --name <name> Exact name match
13
- * --query <text> Fuzzy search by name
14
- * --tags <tag1,tag2> Filter by tags (comma-separated)
15
- * --match-all-tags Require ALL tags (default: ANY)
16
- * --include-blocks Include agent.blocks in response
17
- * --limit <n> Max results (default: 20)
18
- *
19
- * Output:
20
- * Raw API response from GET /v1/agents
21
- */
22
-
23
- import { readFileSync } from "node:fs";
24
- import { createRequire } from "node:module";
25
- import { homedir } from "node:os";
26
- import { join } from "node:path";
27
-
28
- // Use createRequire for @letta-ai/letta-client so NODE_PATH is respected
29
- // (ES module imports don't respect NODE_PATH, but require does)
30
- const require = createRequire(import.meta.url);
31
- const Letta = require("@letta-ai/letta-client")
32
- .default as typeof import("@letta-ai/letta-client").default;
33
- type LettaClient = InstanceType<typeof Letta>;
34
-
35
- interface FindAgentsOptions {
36
- name?: string;
37
- query?: string;
38
- tags?: string[];
39
- matchAllTags?: boolean;
40
- includeBlocks?: boolean;
41
- limit?: number;
42
- }
43
-
44
- /**
45
- * Get API key from env var or settings file
46
- */
47
- function getApiKey(): string {
48
- // First check env var (set by CLI's getShellEnv)
49
- if (process.env.LETTA_API_KEY) {
50
- return process.env.LETTA_API_KEY;
51
- }
52
-
53
- // Fall back to settings file
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
- * Find agents matching the given criteria
78
- * @param client - Letta client instance
79
- * @param options - Search options
80
- * @returns Array of agent objects from the API
81
- */
82
- export async function findAgents(
83
- client: LettaClient,
84
- options: FindAgentsOptions = {},
85
- ): Promise<Awaited<ReturnType<typeof client.agents.list>>> {
86
- const params: Parameters<typeof client.agents.list>[0] = {
87
- limit: options.limit ?? 20,
88
- };
89
-
90
- if (options.name) {
91
- params.name = options.name;
92
- }
93
-
94
- if (options.query) {
95
- params.query_text = options.query;
96
- }
97
-
98
- if (options.tags && options.tags.length > 0) {
99
- params.tags = options.tags;
100
- if (options.matchAllTags) {
101
- params.match_all_tags = true;
102
- }
103
- }
104
-
105
- if (options.includeBlocks) {
106
- params.include = ["agent.blocks"];
107
- }
108
-
109
- return await client.agents.list(params);
110
- }
111
-
112
- function parseArgs(args: string[]): FindAgentsOptions {
113
- const options: FindAgentsOptions = {};
114
-
115
- const nameIndex = args.indexOf("--name");
116
- if (nameIndex !== -1 && nameIndex + 1 < args.length) {
117
- options.name = args[nameIndex + 1];
118
- }
119
-
120
- const queryIndex = args.indexOf("--query");
121
- if (queryIndex !== -1 && queryIndex + 1 < args.length) {
122
- options.query = args[queryIndex + 1];
123
- }
124
-
125
- const tagsIndex = args.indexOf("--tags");
126
- if (tagsIndex !== -1 && tagsIndex + 1 < args.length) {
127
- options.tags = args[tagsIndex + 1]?.split(",").map((t) => t.trim());
128
- }
129
-
130
- if (args.includes("--match-all-tags")) {
131
- options.matchAllTags = true;
132
- }
133
-
134
- if (args.includes("--include-blocks")) {
135
- options.includeBlocks = true;
136
- }
137
-
138
- const limitIndex = args.indexOf("--limit");
139
- if (limitIndex !== -1 && limitIndex + 1 < args.length) {
140
- const limit = Number.parseInt(args[limitIndex + 1] as string, 10);
141
- if (!Number.isNaN(limit)) {
142
- options.limit = limit;
143
- }
144
- }
145
-
146
- return options;
147
- }
148
-
149
- // CLI entry point - check if this file is being run directly
150
- const isMainModule = import.meta.url === `file://${process.argv[1]}`;
151
- if (isMainModule) {
152
- (async () => {
153
- try {
154
- const options = parseArgs(process.argv.slice(2));
155
- const client = createClient();
156
- const result = await findAgents(client, options);
157
- console.log(JSON.stringify(result, null, 2));
158
- } catch (error) {
159
- console.error(
160
- "Error:",
161
- error instanceof Error ? error.message : String(error),
162
- );
163
- console.error(`
164
- Usage: npx tsx find-agents.ts [options]
165
-
166
- Options:
167
- --name <name> Exact name match
168
- --query <text> Fuzzy search by name
169
- --tags <tag1,tag2> Filter by tags (comma-separated)
170
- --match-all-tags Require ALL tags (default: ANY)
171
- --include-blocks Include agent.blocks in response
172
- --limit <n> Max results (default: 20)
173
- `);
174
- process.exit(1);
175
- }
176
- })();
177
- }
@@ -1,226 +0,0 @@
1
- #!/usr/bin/env npx tsx
2
- /**
3
- * Continue Conversation - Send a follow-up message to an existing conversation
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 sender agent ID from LETTA_AGENT_ID env var.
8
- *
9
- * Usage:
10
- * npx tsx continue-conversation.ts --conversation-id <id> --message "<text>"
11
- *
12
- * Options:
13
- * --conversation-id <id> Existing conversation ID (required)
14
- * --message <text> Message to send (required)
15
- * --timeout <ms> Max wait time in ms (default: 120000)
16
- *
17
- * Output:
18
- * JSON with conversation_id, response, agent_id, agent_name
19
- */
20
-
21
- import { readFileSync } from "node:fs";
22
- import { createRequire } from "node:module";
23
- import { homedir } from "node:os";
24
- import { join } from "node:path";
25
- import {
26
- SYSTEM_REMINDER_CLOSE,
27
- SYSTEM_REMINDER_OPEN,
28
- } from "../../../../constants";
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
- interface ContinueConversationOptions {
38
- conversationId: string;
39
- message: string;
40
- timeout?: number;
41
- }
42
-
43
- interface ContinueConversationResult {
44
- conversation_id: string;
45
- response: string;
46
- agent_id: string;
47
- agent_name: string;
48
- }
49
-
50
- /**
51
- * Get API key from env var or settings file
52
- */
53
- function getApiKey(): string {
54
- if (process.env.LETTA_API_KEY) {
55
- return process.env.LETTA_API_KEY;
56
- }
57
-
58
- const settingsPath = join(homedir(), ".letta", "settings.json");
59
- try {
60
- const settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
61
- if (settings.env?.LETTA_API_KEY) {
62
- return settings.env.LETTA_API_KEY;
63
- }
64
- } catch {
65
- // Settings file doesn't exist or is invalid
66
- }
67
-
68
- throw new Error(
69
- "No LETTA_API_KEY found. Set the env var or run the Letta CLI to authenticate.",
70
- );
71
- }
72
-
73
- /**
74
- * Get the sender agent ID from env var
75
- */
76
- function getSenderAgentId(): string {
77
- if (process.env.LETTA_AGENT_ID) {
78
- return process.env.LETTA_AGENT_ID;
79
- }
80
- throw new Error(
81
- "No LETTA_AGENT_ID found. This script should be run from within a Letta Code session.",
82
- );
83
- }
84
-
85
- /**
86
- * Create a Letta client with auth from env/settings
87
- */
88
- function createClient(): LettaClient {
89
- return new Letta({ apiKey: getApiKey() });
90
- }
91
-
92
- /**
93
- * Build the system reminder prefix for the message
94
- */
95
- function buildSystemReminder(
96
- senderAgentName: string,
97
- senderAgentId: string,
98
- ): string {
99
- return `${SYSTEM_REMINDER_OPEN}
100
- This message is from "${senderAgentName}" (agent ID: ${senderAgentId}), an agent currently running inside the Letta Code CLI (docs.letta.com/letta-code).
101
- The sender will only see the final message you generate (not tool calls or reasoning).
102
- If you need to share detailed information, include it in your response text.
103
- ${SYSTEM_REMINDER_CLOSE}
104
-
105
- `;
106
- }
107
-
108
- /**
109
- * Continue an existing conversation by sending a follow-up message
110
- * @param client - Letta client instance
111
- * @param options - Options including conversation ID and message
112
- * @returns Conversation result with response and metadata
113
- */
114
- export async function continueConversation(
115
- client: LettaClient,
116
- options: ContinueConversationOptions,
117
- ): Promise<ContinueConversationResult> {
118
- const { conversationId, message } = options;
119
-
120
- // 1. Fetch conversation to get agent_id and validate it exists
121
- const conversation = await client.conversations.retrieve(conversationId);
122
-
123
- // 2. Fetch target agent to get name
124
- const targetAgent = await client.agents.retrieve(conversation.agent_id);
125
-
126
- // 3. Fetch sender agent to get name for system reminder
127
- const senderAgentId = getSenderAgentId();
128
- const senderAgent = await client.agents.retrieve(senderAgentId);
129
-
130
- // 4. Build message with system reminder prefix
131
- const systemReminder = buildSystemReminder(senderAgent.name, senderAgentId);
132
- const fullMessage = systemReminder + message;
133
-
134
- // 5. Send message and consume the stream
135
- // Note: conversations.messages.create always returns a Stream
136
- const stream = await client.conversations.messages.create(conversationId, {
137
- input: fullMessage,
138
- });
139
-
140
- // 6. Consume stream and extract final assistant message
141
- let finalResponse = "";
142
- for await (const chunk of stream) {
143
- if (chunk.message_type === "assistant_message") {
144
- // Content can be string or array of content parts
145
- const content = chunk.content;
146
- if (typeof content === "string") {
147
- finalResponse += content;
148
- } else if (Array.isArray(content)) {
149
- for (const part of content) {
150
- if (
151
- typeof part === "object" &&
152
- part !== null &&
153
- "type" in part &&
154
- part.type === "text" &&
155
- "text" in part
156
- ) {
157
- finalResponse += (part as { text: string }).text;
158
- }
159
- }
160
- }
161
- }
162
- }
163
-
164
- return {
165
- conversation_id: conversationId,
166
- response: finalResponse,
167
- agent_id: targetAgent.id,
168
- agent_name: targetAgent.name,
169
- };
170
- }
171
-
172
- function parseArgs(args: string[]): ContinueConversationOptions {
173
- const conversationIdIndex = args.indexOf("--conversation-id");
174
- if (conversationIdIndex === -1 || conversationIdIndex + 1 >= args.length) {
175
- throw new Error(
176
- "Missing required argument: --conversation-id <conversation-id>",
177
- );
178
- }
179
- const conversationId = args[conversationIdIndex + 1] as string;
180
-
181
- const messageIndex = args.indexOf("--message");
182
- if (messageIndex === -1 || messageIndex + 1 >= args.length) {
183
- throw new Error("Missing required argument: --message <text>");
184
- }
185
- const message = args[messageIndex + 1] as string;
186
-
187
- const options: ContinueConversationOptions = { conversationId, message };
188
-
189
- const timeoutIndex = args.indexOf("--timeout");
190
- if (timeoutIndex !== -1 && timeoutIndex + 1 < args.length) {
191
- const timeout = Number.parseInt(args[timeoutIndex + 1] as string, 10);
192
- if (!Number.isNaN(timeout)) {
193
- options.timeout = timeout;
194
- }
195
- }
196
-
197
- return options;
198
- }
199
-
200
- // CLI entry point - check if this file is being run directly
201
- const isMainModule = import.meta.url === `file://${process.argv[1]}`;
202
- if (isMainModule) {
203
- (async () => {
204
- try {
205
- const options = parseArgs(process.argv.slice(2));
206
- const client = createClient();
207
- const result = await continueConversation(client, options);
208
- console.log(JSON.stringify(result, null, 2));
209
- process.exit(0);
210
- } catch (error) {
211
- console.error(
212
- "Error:",
213
- error instanceof Error ? error.message : String(error),
214
- );
215
- console.error(`
216
- Usage: npx tsx continue-conversation.ts --conversation-id <id> --message "<text>"
217
-
218
- Options:
219
- --conversation-id <id> Existing conversation ID (required)
220
- --message <text> Message to send (required)
221
- --timeout <ms> Max wait time in ms (default: 120000)
222
- `);
223
- process.exit(1);
224
- }
225
- })();
226
- }
@@ -1,229 +0,0 @@
1
- #!/usr/bin/env npx tsx
2
- /**
3
- * Start Conversation - Start a new conversation with an agent and send a message
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 sender agent ID from LETTA_AGENT_ID env var.
8
- *
9
- * Usage:
10
- * npx tsx start-conversation.ts --agent-id <id> --message "<text>"
11
- *
12
- * Options:
13
- * --agent-id <id> Target agent ID to message (required)
14
- * --message <text> Message to send (required)
15
- * --timeout <ms> Max wait time in ms (default: 120000)
16
- *
17
- * Output:
18
- * JSON with conversation_id, response, agent_id, agent_name
19
- */
20
-
21
- import { readFileSync } from "node:fs";
22
- import { createRequire } from "node:module";
23
- import { homedir } from "node:os";
24
- import { join } from "node:path";
25
- import {
26
- SYSTEM_REMINDER_CLOSE,
27
- SYSTEM_REMINDER_OPEN,
28
- } from "../../../../constants";
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
- interface StartConversationOptions {
38
- agentId: string;
39
- message: string;
40
- timeout?: number;
41
- }
42
-
43
- interface StartConversationResult {
44
- conversation_id: string;
45
- response: string;
46
- agent_id: string;
47
- agent_name: string;
48
- }
49
-
50
- /**
51
- * Get API key from env var or settings file
52
- */
53
- function getApiKey(): string {
54
- if (process.env.LETTA_API_KEY) {
55
- return process.env.LETTA_API_KEY;
56
- }
57
-
58
- const settingsPath = join(homedir(), ".letta", "settings.json");
59
- try {
60
- const settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
61
- if (settings.env?.LETTA_API_KEY) {
62
- return settings.env.LETTA_API_KEY;
63
- }
64
- } catch {
65
- // Settings file doesn't exist or is invalid
66
- }
67
-
68
- throw new Error(
69
- "No LETTA_API_KEY found. Set the env var or run the Letta CLI to authenticate.",
70
- );
71
- }
72
-
73
- /**
74
- * Get the sender agent ID from env var
75
- */
76
- function getSenderAgentId(): string {
77
- if (process.env.LETTA_AGENT_ID) {
78
- return process.env.LETTA_AGENT_ID;
79
- }
80
- throw new Error(
81
- "No LETTA_AGENT_ID found. This script should be run from within a Letta Code session.",
82
- );
83
- }
84
-
85
- /**
86
- * Create a Letta client with auth from env/settings
87
- */
88
- function createClient(): LettaClient {
89
- return new Letta({ apiKey: getApiKey() });
90
- }
91
-
92
- /**
93
- * Build the system reminder prefix for the message
94
- */
95
- function buildSystemReminder(
96
- senderAgentName: string,
97
- senderAgentId: string,
98
- ): string {
99
- return `${SYSTEM_REMINDER_OPEN}
100
- This message is from "${senderAgentName}" (agent ID: ${senderAgentId}), an agent currently running inside the Letta Code CLI (docs.letta.com/letta-code).
101
- The sender will only see the final message you generate (not tool calls or reasoning).
102
- If you need to share detailed information, include it in your response text.
103
- ${SYSTEM_REMINDER_CLOSE}
104
-
105
- `;
106
- }
107
-
108
- /**
109
- * Start a new conversation with an agent and send a message
110
- * @param client - Letta client instance
111
- * @param options - Options including target agent ID and message
112
- * @returns Conversation result with response and metadata
113
- */
114
- export async function startConversation(
115
- client: LettaClient,
116
- options: StartConversationOptions,
117
- ): Promise<StartConversationResult> {
118
- const { agentId, message } = options;
119
-
120
- // 1. Fetch target agent to validate existence and get name
121
- const targetAgent = await client.agents.retrieve(agentId);
122
-
123
- // 2. Fetch sender agent to get name for system reminder
124
- const senderAgentId = getSenderAgentId();
125
- const senderAgent = await client.agents.retrieve(senderAgentId);
126
-
127
- // 3. Create new conversation
128
- const conversation = await client.conversations.create({
129
- agent_id: agentId,
130
- });
131
-
132
- // 4. Build message with system reminder prefix
133
- const systemReminder = buildSystemReminder(senderAgent.name, senderAgentId);
134
- const fullMessage = systemReminder + message;
135
-
136
- // 5. Send message and consume the stream
137
- // Note: conversations.messages.create always returns a Stream
138
- const stream = await client.conversations.messages.create(conversation.id, {
139
- input: fullMessage,
140
- });
141
-
142
- // 6. Consume stream and extract final assistant message
143
- let finalResponse = "";
144
- for await (const chunk of stream) {
145
- if (process.env.DEBUG) {
146
- console.error("Chunk:", JSON.stringify(chunk, null, 2));
147
- }
148
- if (chunk.message_type === "assistant_message") {
149
- // Content can be string or array of content parts
150
- const content = chunk.content;
151
- if (typeof content === "string") {
152
- finalResponse += content;
153
- } else if (Array.isArray(content)) {
154
- for (const part of content) {
155
- if (
156
- typeof part === "object" &&
157
- part !== null &&
158
- "type" in part &&
159
- part.type === "text" &&
160
- "text" in part
161
- ) {
162
- finalResponse += (part as { text: string }).text;
163
- }
164
- }
165
- }
166
- }
167
- }
168
-
169
- return {
170
- conversation_id: conversation.id,
171
- response: finalResponse,
172
- agent_id: targetAgent.id,
173
- agent_name: targetAgent.name,
174
- };
175
- }
176
-
177
- function parseArgs(args: string[]): StartConversationOptions {
178
- const agentIdIndex = args.indexOf("--agent-id");
179
- if (agentIdIndex === -1 || agentIdIndex + 1 >= args.length) {
180
- throw new Error("Missing required argument: --agent-id <agent-id>");
181
- }
182
- const agentId = args[agentIdIndex + 1] as string;
183
-
184
- const messageIndex = args.indexOf("--message");
185
- if (messageIndex === -1 || messageIndex + 1 >= args.length) {
186
- throw new Error("Missing required argument: --message <text>");
187
- }
188
- const message = args[messageIndex + 1] as string;
189
-
190
- const options: StartConversationOptions = { agentId, message };
191
-
192
- const timeoutIndex = args.indexOf("--timeout");
193
- if (timeoutIndex !== -1 && timeoutIndex + 1 < args.length) {
194
- const timeout = Number.parseInt(args[timeoutIndex + 1] as string, 10);
195
- if (!Number.isNaN(timeout)) {
196
- options.timeout = timeout;
197
- }
198
- }
199
-
200
- return options;
201
- }
202
-
203
- // CLI entry point - check if this file is being run directly
204
- const isMainModule = import.meta.url === `file://${process.argv[1]}`;
205
- if (isMainModule) {
206
- (async () => {
207
- try {
208
- const options = parseArgs(process.argv.slice(2));
209
- const client = createClient();
210
- const result = await startConversation(client, options);
211
- console.log(JSON.stringify(result, null, 2));
212
- process.exit(0);
213
- } catch (error) {
214
- console.error(
215
- "Error:",
216
- error instanceof Error ? error.message : String(error),
217
- );
218
- console.error(`
219
- Usage: npx tsx start-conversation.ts --agent-id <id> --message "<text>"
220
-
221
- Options:
222
- --agent-id <id> Target agent ID to message (required)
223
- --message <text> Message to send (required)
224
- --timeout <ms> Max wait time in ms (default: 120000)
225
- `);
226
- process.exit(1);
227
- }
228
- })();
229
- }