@letta-ai/letta-code 0.13.2 → 0.13.4
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,135 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: messaging-agents
|
|
3
|
+
description: Send messages to other agents on your server. Use when you need to communicate with, query, or delegate tasks to another agent.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Messaging Agents
|
|
7
|
+
|
|
8
|
+
This skill enables you to send messages to other agents on the same Letta server using the thread-safe conversations API.
|
|
9
|
+
|
|
10
|
+
## When to Use This Skill
|
|
11
|
+
|
|
12
|
+
- You need to ask another agent a question
|
|
13
|
+
- You want to query an agent that has specialized knowledge
|
|
14
|
+
- You need information that another agent has in their memory
|
|
15
|
+
- You want to coordinate with another agent on a task
|
|
16
|
+
|
|
17
|
+
## What the Target Agent Can and Cannot Do
|
|
18
|
+
|
|
19
|
+
**The target agent CANNOT:**
|
|
20
|
+
- Access your local environment (read/write files in your codebase)
|
|
21
|
+
- Execute shell commands on your machine
|
|
22
|
+
- Use your tools (Bash, Read, Write, Edit, etc.)
|
|
23
|
+
|
|
24
|
+
**The target agent CAN:**
|
|
25
|
+
- Use their own tools (whatever they have configured)
|
|
26
|
+
- Access their own memory blocks
|
|
27
|
+
- Make API calls if they have web/API tools
|
|
28
|
+
- Search the web if they have web search tools
|
|
29
|
+
- Respond with information from their knowledge/memory
|
|
30
|
+
|
|
31
|
+
**Important:** This skill is for *communication* with other agents, not *delegation* of local work. The target agent runs in their own environment and cannot interact with your codebase.
|
|
32
|
+
|
|
33
|
+
**Need local access?** If you need the target agent to access your local environment (read/write files, run commands), use the Task tool instead to deploy them as a subagent:
|
|
34
|
+
```typescript
|
|
35
|
+
Task({
|
|
36
|
+
agent_id: "agent-xxx", // Deploy this existing agent
|
|
37
|
+
subagent_type: "explore", // "explore" = read-only, "general-purpose" = read-write
|
|
38
|
+
prompt: "Look at the code in src/ and tell me about the architecture"
|
|
39
|
+
})
|
|
40
|
+
```
|
|
41
|
+
This gives the agent access to your codebase while running as a subagent.
|
|
42
|
+
|
|
43
|
+
## Finding an Agent to Message
|
|
44
|
+
|
|
45
|
+
If you don't have a specific agent ID, use these skills to find one:
|
|
46
|
+
|
|
47
|
+
### By Name or Tags
|
|
48
|
+
Load the `finding-agents` skill to search for agents:
|
|
49
|
+
```bash
|
|
50
|
+
npx tsx <FINDING_AGENTS_SKILL_DIR>/scripts/find-agents.ts --query "agent-name"
|
|
51
|
+
npx tsx <FINDING_AGENTS_SKILL_DIR>/scripts/find-agents.ts --tags "origin:letta-code"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### By Topic They Discussed
|
|
55
|
+
Load the `searching-messages` skill to find which agent worked on something:
|
|
56
|
+
```bash
|
|
57
|
+
npx tsx <SEARCHING_MESSAGES_SKILL_DIR>/scripts/search-messages.ts --query "topic" --all-agents
|
|
58
|
+
```
|
|
59
|
+
Results include `agent_id` for each matching message.
|
|
60
|
+
|
|
61
|
+
## Script Usage
|
|
62
|
+
|
|
63
|
+
### Starting a New Conversation
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npx tsx <SKILL_DIR>/scripts/start-conversation.ts --agent-id <id> --message "<text>"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Arguments:**
|
|
70
|
+
| Arg | Required | Description |
|
|
71
|
+
|-----|----------|-------------|
|
|
72
|
+
| `--agent-id <id>` | Yes | Target agent ID to message |
|
|
73
|
+
| `--message <text>` | Yes | Message to send |
|
|
74
|
+
| `--timeout <ms>` | No | Max wait time in ms (default: 120000) |
|
|
75
|
+
|
|
76
|
+
**Example:**
|
|
77
|
+
```bash
|
|
78
|
+
npx tsx <SKILL_DIR>/scripts/start-conversation.ts \
|
|
79
|
+
--agent-id agent-abc123 \
|
|
80
|
+
--message "What do you know about the authentication system?"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Response:**
|
|
84
|
+
```json
|
|
85
|
+
{
|
|
86
|
+
"conversation_id": "conversation-xyz789",
|
|
87
|
+
"response": "The authentication system uses JWT tokens...",
|
|
88
|
+
"agent_id": "agent-abc123",
|
|
89
|
+
"agent_name": "BackendExpert"
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Continuing a Conversation
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
npx tsx <SKILL_DIR>/scripts/continue-conversation.ts --conversation-id <id> --message "<text>"
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Arguments:**
|
|
100
|
+
| Arg | Required | Description |
|
|
101
|
+
|-----|----------|-------------|
|
|
102
|
+
| `--conversation-id <id>` | Yes | Existing conversation ID |
|
|
103
|
+
| `--message <text>` | Yes | Follow-up message to send |
|
|
104
|
+
| `--timeout <ms>` | No | Max wait time in ms (default: 120000) |
|
|
105
|
+
|
|
106
|
+
**Example:**
|
|
107
|
+
```bash
|
|
108
|
+
npx tsx <SKILL_DIR>/scripts/continue-conversation.ts \
|
|
109
|
+
--conversation-id conversation-xyz789 \
|
|
110
|
+
--message "Can you explain more about the token refresh flow?"
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Understanding the Response
|
|
114
|
+
|
|
115
|
+
- Scripts return only the **final assistant message** (not tool calls or reasoning)
|
|
116
|
+
- The target agent may use tools, think, and reason - but you only see their final response
|
|
117
|
+
- To see the full conversation transcript (including tool calls), use the `searching-messages` skill with `--agent-id` targeting the other agent
|
|
118
|
+
|
|
119
|
+
## How It Works
|
|
120
|
+
|
|
121
|
+
When you send a message, the target agent receives it with a system reminder:
|
|
122
|
+
```
|
|
123
|
+
<system-reminder>
|
|
124
|
+
This message is from "YourAgentName" (agent ID: agent-xxx), an agent currently running inside the Letta Code CLI (docs.letta.com/letta-code).
|
|
125
|
+
The sender will only see the final message you generate (not tool calls or reasoning).
|
|
126
|
+
If you need to share detailed information, include it in your response text.
|
|
127
|
+
</system-reminder>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
This helps the target agent understand the context and format their response appropriately.
|
|
131
|
+
|
|
132
|
+
## Related Skills
|
|
133
|
+
|
|
134
|
+
- **finding-agents**: Find agents by name, tags, or fuzzy search
|
|
135
|
+
- **searching-messages**: Search past messages across agents, or view full conversation transcripts
|
|
@@ -0,0 +1,222 @@
|
|
|
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
|
+
|
|
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
|
+
interface ContinueConversationOptions {
|
|
34
|
+
conversationId: string;
|
|
35
|
+
message: string;
|
|
36
|
+
timeout?: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
interface ContinueConversationResult {
|
|
40
|
+
conversation_id: string;
|
|
41
|
+
response: string;
|
|
42
|
+
agent_id: string;
|
|
43
|
+
agent_name: string;
|
|
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
|
+
* Get the sender agent ID from env var
|
|
71
|
+
*/
|
|
72
|
+
function getSenderAgentId(): string {
|
|
73
|
+
if (process.env.LETTA_AGENT_ID) {
|
|
74
|
+
return process.env.LETTA_AGENT_ID;
|
|
75
|
+
}
|
|
76
|
+
throw new Error(
|
|
77
|
+
"No LETTA_AGENT_ID found. This script should be run from within a Letta Code session.",
|
|
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
|
+
* Build the system reminder prefix for the message
|
|
90
|
+
*/
|
|
91
|
+
function buildSystemReminder(
|
|
92
|
+
senderAgentName: string,
|
|
93
|
+
senderAgentId: string,
|
|
94
|
+
): string {
|
|
95
|
+
return `<system-reminder>
|
|
96
|
+
This message is from "${senderAgentName}" (agent ID: ${senderAgentId}), an agent currently running inside the Letta Code CLI (docs.letta.com/letta-code).
|
|
97
|
+
The sender will only see the final message you generate (not tool calls or reasoning).
|
|
98
|
+
If you need to share detailed information, include it in your response text.
|
|
99
|
+
</system-reminder>
|
|
100
|
+
|
|
101
|
+
`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Continue an existing conversation by sending a follow-up message
|
|
106
|
+
* @param client - Letta client instance
|
|
107
|
+
* @param options - Options including conversation ID and message
|
|
108
|
+
* @returns Conversation result with response and metadata
|
|
109
|
+
*/
|
|
110
|
+
export async function continueConversation(
|
|
111
|
+
client: LettaClient,
|
|
112
|
+
options: ContinueConversationOptions,
|
|
113
|
+
): Promise<ContinueConversationResult> {
|
|
114
|
+
const { conversationId, message } = options;
|
|
115
|
+
|
|
116
|
+
// 1. Fetch conversation to get agent_id and validate it exists
|
|
117
|
+
const conversation = await client.conversations.retrieve(conversationId);
|
|
118
|
+
|
|
119
|
+
// 2. Fetch target agent to get name
|
|
120
|
+
const targetAgent = await client.agents.retrieve(conversation.agent_id);
|
|
121
|
+
|
|
122
|
+
// 3. Fetch sender agent to get name for system reminder
|
|
123
|
+
const senderAgentId = getSenderAgentId();
|
|
124
|
+
const senderAgent = await client.agents.retrieve(senderAgentId);
|
|
125
|
+
|
|
126
|
+
// 4. Build message with system reminder prefix
|
|
127
|
+
const systemReminder = buildSystemReminder(senderAgent.name, senderAgentId);
|
|
128
|
+
const fullMessage = systemReminder + message;
|
|
129
|
+
|
|
130
|
+
// 5. Send message and consume the stream
|
|
131
|
+
// Note: conversations.messages.create always returns a Stream
|
|
132
|
+
const stream = await client.conversations.messages.create(conversationId, {
|
|
133
|
+
input: fullMessage,
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// 6. Consume stream and extract final assistant message
|
|
137
|
+
let finalResponse = "";
|
|
138
|
+
for await (const chunk of stream) {
|
|
139
|
+
if (chunk.message_type === "assistant_message") {
|
|
140
|
+
// Content can be string or array of content parts
|
|
141
|
+
const content = chunk.content;
|
|
142
|
+
if (typeof content === "string") {
|
|
143
|
+
finalResponse += content;
|
|
144
|
+
} else if (Array.isArray(content)) {
|
|
145
|
+
for (const part of content) {
|
|
146
|
+
if (
|
|
147
|
+
typeof part === "object" &&
|
|
148
|
+
part !== null &&
|
|
149
|
+
"type" in part &&
|
|
150
|
+
part.type === "text" &&
|
|
151
|
+
"text" in part
|
|
152
|
+
) {
|
|
153
|
+
finalResponse += (part as { text: string }).text;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
conversation_id: conversationId,
|
|
162
|
+
response: finalResponse,
|
|
163
|
+
agent_id: targetAgent.id,
|
|
164
|
+
agent_name: targetAgent.name,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function parseArgs(args: string[]): ContinueConversationOptions {
|
|
169
|
+
const conversationIdIndex = args.indexOf("--conversation-id");
|
|
170
|
+
if (conversationIdIndex === -1 || conversationIdIndex + 1 >= args.length) {
|
|
171
|
+
throw new Error(
|
|
172
|
+
"Missing required argument: --conversation-id <conversation-id>",
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
const conversationId = args[conversationIdIndex + 1] as string;
|
|
176
|
+
|
|
177
|
+
const messageIndex = args.indexOf("--message");
|
|
178
|
+
if (messageIndex === -1 || messageIndex + 1 >= args.length) {
|
|
179
|
+
throw new Error("Missing required argument: --message <text>");
|
|
180
|
+
}
|
|
181
|
+
const message = args[messageIndex + 1] as string;
|
|
182
|
+
|
|
183
|
+
const options: ContinueConversationOptions = { conversationId, message };
|
|
184
|
+
|
|
185
|
+
const timeoutIndex = args.indexOf("--timeout");
|
|
186
|
+
if (timeoutIndex !== -1 && timeoutIndex + 1 < args.length) {
|
|
187
|
+
const timeout = Number.parseInt(args[timeoutIndex + 1] as string, 10);
|
|
188
|
+
if (!Number.isNaN(timeout)) {
|
|
189
|
+
options.timeout = timeout;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return options;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// CLI entry point - check if this file is being run directly
|
|
197
|
+
const isMainModule = import.meta.url === `file://${process.argv[1]}`;
|
|
198
|
+
if (isMainModule) {
|
|
199
|
+
(async () => {
|
|
200
|
+
try {
|
|
201
|
+
const options = parseArgs(process.argv.slice(2));
|
|
202
|
+
const client = createClient();
|
|
203
|
+
const result = await continueConversation(client, options);
|
|
204
|
+
console.log(JSON.stringify(result, null, 2));
|
|
205
|
+
process.exit(0);
|
|
206
|
+
} catch (error) {
|
|
207
|
+
console.error(
|
|
208
|
+
"Error:",
|
|
209
|
+
error instanceof Error ? error.message : String(error),
|
|
210
|
+
);
|
|
211
|
+
console.error(`
|
|
212
|
+
Usage: npx tsx continue-conversation.ts --conversation-id <id> --message "<text>"
|
|
213
|
+
|
|
214
|
+
Options:
|
|
215
|
+
--conversation-id <id> Existing conversation ID (required)
|
|
216
|
+
--message <text> Message to send (required)
|
|
217
|
+
--timeout <ms> Max wait time in ms (default: 120000)
|
|
218
|
+
`);
|
|
219
|
+
process.exit(1);
|
|
220
|
+
}
|
|
221
|
+
})();
|
|
222
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
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
|
+
|
|
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
|
+
interface StartConversationOptions {
|
|
34
|
+
agentId: string;
|
|
35
|
+
message: string;
|
|
36
|
+
timeout?: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
interface StartConversationResult {
|
|
40
|
+
conversation_id: string;
|
|
41
|
+
response: string;
|
|
42
|
+
agent_id: string;
|
|
43
|
+
agent_name: string;
|
|
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
|
+
* Get the sender agent ID from env var
|
|
71
|
+
*/
|
|
72
|
+
function getSenderAgentId(): string {
|
|
73
|
+
if (process.env.LETTA_AGENT_ID) {
|
|
74
|
+
return process.env.LETTA_AGENT_ID;
|
|
75
|
+
}
|
|
76
|
+
throw new Error(
|
|
77
|
+
"No LETTA_AGENT_ID found. This script should be run from within a Letta Code session.",
|
|
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
|
+
* Build the system reminder prefix for the message
|
|
90
|
+
*/
|
|
91
|
+
function buildSystemReminder(
|
|
92
|
+
senderAgentName: string,
|
|
93
|
+
senderAgentId: string,
|
|
94
|
+
): string {
|
|
95
|
+
return `<system-reminder>
|
|
96
|
+
This message is from "${senderAgentName}" (agent ID: ${senderAgentId}), an agent currently running inside the Letta Code CLI (docs.letta.com/letta-code).
|
|
97
|
+
The sender will only see the final message you generate (not tool calls or reasoning).
|
|
98
|
+
If you need to share detailed information, include it in your response text.
|
|
99
|
+
</system-reminder>
|
|
100
|
+
|
|
101
|
+
`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Start a new conversation with an agent and send a message
|
|
106
|
+
* @param client - Letta client instance
|
|
107
|
+
* @param options - Options including target agent ID and message
|
|
108
|
+
* @returns Conversation result with response and metadata
|
|
109
|
+
*/
|
|
110
|
+
export async function startConversation(
|
|
111
|
+
client: LettaClient,
|
|
112
|
+
options: StartConversationOptions,
|
|
113
|
+
): Promise<StartConversationResult> {
|
|
114
|
+
const { agentId, message } = options;
|
|
115
|
+
|
|
116
|
+
// 1. Fetch target agent to validate existence and get name
|
|
117
|
+
const targetAgent = await client.agents.retrieve(agentId);
|
|
118
|
+
|
|
119
|
+
// 2. Fetch sender agent to get name for system reminder
|
|
120
|
+
const senderAgentId = getSenderAgentId();
|
|
121
|
+
const senderAgent = await client.agents.retrieve(senderAgentId);
|
|
122
|
+
|
|
123
|
+
// 3. Create new conversation
|
|
124
|
+
const conversation = await client.conversations.create({
|
|
125
|
+
agent_id: agentId,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// 4. Build message with system reminder prefix
|
|
129
|
+
const systemReminder = buildSystemReminder(senderAgent.name, senderAgentId);
|
|
130
|
+
const fullMessage = systemReminder + message;
|
|
131
|
+
|
|
132
|
+
// 5. Send message and consume the stream
|
|
133
|
+
// Note: conversations.messages.create always returns a Stream
|
|
134
|
+
const stream = await client.conversations.messages.create(conversation.id, {
|
|
135
|
+
input: fullMessage,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// 6. Consume stream and extract final assistant message
|
|
139
|
+
let finalResponse = "";
|
|
140
|
+
for await (const chunk of stream) {
|
|
141
|
+
if (process.env.DEBUG) {
|
|
142
|
+
console.error("Chunk:", JSON.stringify(chunk, null, 2));
|
|
143
|
+
}
|
|
144
|
+
if (chunk.message_type === "assistant_message") {
|
|
145
|
+
// Content can be string or array of content parts
|
|
146
|
+
const content = chunk.content;
|
|
147
|
+
if (typeof content === "string") {
|
|
148
|
+
finalResponse += content;
|
|
149
|
+
} else if (Array.isArray(content)) {
|
|
150
|
+
for (const part of content) {
|
|
151
|
+
if (
|
|
152
|
+
typeof part === "object" &&
|
|
153
|
+
part !== null &&
|
|
154
|
+
"type" in part &&
|
|
155
|
+
part.type === "text" &&
|
|
156
|
+
"text" in part
|
|
157
|
+
) {
|
|
158
|
+
finalResponse += (part as { text: string }).text;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
conversation_id: conversation.id,
|
|
167
|
+
response: finalResponse,
|
|
168
|
+
agent_id: targetAgent.id,
|
|
169
|
+
agent_name: targetAgent.name,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function parseArgs(args: string[]): StartConversationOptions {
|
|
174
|
+
const agentIdIndex = args.indexOf("--agent-id");
|
|
175
|
+
if (agentIdIndex === -1 || agentIdIndex + 1 >= args.length) {
|
|
176
|
+
throw new Error("Missing required argument: --agent-id <agent-id>");
|
|
177
|
+
}
|
|
178
|
+
const agentId = args[agentIdIndex + 1] as string;
|
|
179
|
+
|
|
180
|
+
const messageIndex = args.indexOf("--message");
|
|
181
|
+
if (messageIndex === -1 || messageIndex + 1 >= args.length) {
|
|
182
|
+
throw new Error("Missing required argument: --message <text>");
|
|
183
|
+
}
|
|
184
|
+
const message = args[messageIndex + 1] as string;
|
|
185
|
+
|
|
186
|
+
const options: StartConversationOptions = { agentId, message };
|
|
187
|
+
|
|
188
|
+
const timeoutIndex = args.indexOf("--timeout");
|
|
189
|
+
if (timeoutIndex !== -1 && timeoutIndex + 1 < args.length) {
|
|
190
|
+
const timeout = Number.parseInt(args[timeoutIndex + 1] as string, 10);
|
|
191
|
+
if (!Number.isNaN(timeout)) {
|
|
192
|
+
options.timeout = timeout;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return options;
|
|
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 options = parseArgs(process.argv.slice(2));
|
|
205
|
+
const client = createClient();
|
|
206
|
+
const result = await startConversation(client, options);
|
|
207
|
+
console.log(JSON.stringify(result, null, 2));
|
|
208
|
+
process.exit(0);
|
|
209
|
+
} catch (error) {
|
|
210
|
+
console.error(
|
|
211
|
+
"Error:",
|
|
212
|
+
error instanceof Error ? error.message : String(error),
|
|
213
|
+
);
|
|
214
|
+
console.error(`
|
|
215
|
+
Usage: npx tsx start-conversation.ts --agent-id <id> --message "<text>"
|
|
216
|
+
|
|
217
|
+
Options:
|
|
218
|
+
--agent-id <id> Target agent ID to message (required)
|
|
219
|
+
--message <text> Message to send (required)
|
|
220
|
+
--timeout <ms> Max wait time in ms (default: 120000)
|
|
221
|
+
`);
|
|
222
|
+
process.exit(1);
|
|
223
|
+
}
|
|
224
|
+
})();
|
|
225
|
+
}
|