@letta-ai/letta-agent-sdk 0.5.0 → 0.5.2
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/AGENTS.md +35 -0
- package/README.md +22 -10
- package/dist/agent-repositories.d.ts +15 -0
- package/dist/agent-repositories.d.ts.map +1 -0
- package/dist/app-server-management.d.ts +10 -7
- package/dist/app-server-management.d.ts.map +1 -1
- package/dist/app-server-models.d.ts +4 -0
- package/dist/app-server-models.d.ts.map +1 -0
- package/dist/app-server-session.d.ts +3 -3
- package/dist/app-server-session.d.ts.map +1 -1
- package/dist/client-base.d.ts +5 -0
- package/dist/client-base.d.ts.map +1 -1
- package/dist/client-entry.js +3386 -639
- package/dist/client-entry.js.map +85 -13
- package/dist/cloud-client.d.ts +7 -0
- package/dist/cloud-client.d.ts.map +1 -0
- package/dist/cloud-management.d.ts +21 -18
- package/dist/cloud-management.d.ts.map +1 -1
- package/dist/cloud-session.d.ts +7 -5
- package/dist/cloud-session.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3400 -653
- package/dist/index.js.map +86 -14
- package/dist/management-types.d.ts +85 -64
- package/dist/management-types.d.ts.map +1 -1
- package/dist/management.d.ts +11 -9
- package/dist/management.d.ts.map +1 -1
- package/dist/remote-session-protocol.d.ts +1 -1
- package/dist/remote-session-protocol.d.ts.map +1 -1
- package/dist/remote.d.ts +4 -5
- package/dist/remote.d.ts.map +1 -1
- package/dist/repositories.d.ts +3 -2
- package/dist/repositories.d.ts.map +1 -1
- package/dist/types.d.ts +22 -34
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -2
- package/src/agent-repositories.ts +159 -0
- package/src/app-server-management.ts +56 -116
- package/src/app-server-models.ts +20 -0
- package/src/app-server-session.ts +71 -146
- package/src/client-base.ts +72 -20
- package/src/cloud-client.ts +46 -0
- package/src/cloud-management.ts +128 -275
- package/src/cloud-session.ts +168 -267
- package/src/index.ts +6 -0
- package/src/management-types.ts +107 -64
- package/src/management.ts +46 -39
- package/src/remote-session-protocol.ts +6 -2
- package/src/remote.ts +18 -63
- package/src/repositories.ts +26 -99
- package/src/types.ts +22 -35
package/src/cloud-management.ts
CHANGED
|
@@ -1,128 +1,48 @@
|
|
|
1
|
+
import type Letta from "@letta-ai/letta-client";
|
|
2
|
+
import { APIError } from "@letta-ai/letta-client/core/error";
|
|
1
3
|
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
} from "
|
|
4
|
+
AgentListParams,
|
|
5
|
+
AgentUpdateParams,
|
|
6
|
+
} from "@letta-ai/letta-client/resources/agents/agents";
|
|
5
7
|
import type {
|
|
8
|
+
ConversationCreateParams,
|
|
9
|
+
ConversationListParams,
|
|
10
|
+
ConversationUpdateParams,
|
|
11
|
+
} from "@letta-ai/letta-client/resources/conversations/conversations";
|
|
12
|
+
import type { MessageListParams } from "@letta-ai/letta-client/resources/conversations/messages";
|
|
13
|
+
import type { ManagementTransport } from "./management.js";
|
|
14
|
+
import type {
|
|
15
|
+
AgentRepository,
|
|
16
|
+
AgentRepositoryPermissions,
|
|
6
17
|
ConversationMessagesResult,
|
|
7
18
|
LettaAgent,
|
|
8
19
|
LettaConversation,
|
|
9
20
|
} from "./management-types.js";
|
|
10
21
|
import type {
|
|
11
|
-
LettaCodeCloudClientOptions,
|
|
12
22
|
LettaCodeModelEntry,
|
|
13
23
|
ListModelsResult,
|
|
14
24
|
} from "./types.js";
|
|
15
25
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
function defaultApiKey(): string | undefined {
|
|
19
|
-
const env = (
|
|
20
|
-
globalThis as {
|
|
21
|
-
process?: { env?: Record<string, string | undefined> };
|
|
22
|
-
}
|
|
23
|
-
).process?.env;
|
|
24
|
-
return env?.LETTA_API_KEY ?? env?.LETTA_CLOUD_API_KEY;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function bearerToken(
|
|
28
|
-
headers: Record<string, string> | undefined,
|
|
29
|
-
): string | undefined {
|
|
30
|
-
const authorization = headers?.Authorization ?? headers?.authorization;
|
|
31
|
-
return authorization?.match(/^Bearer\s+(.+)$/i)?.[1];
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function apiBaseUrl(value: string | undefined): string {
|
|
35
|
-
const url = new URL(value ?? DEFAULT_CLOUD_API_BASE_URL);
|
|
36
|
-
url.pathname = url.pathname.replace(/\/+$/, "");
|
|
37
|
-
url.search = "";
|
|
38
|
-
url.hash = "";
|
|
39
|
-
return url.toString().replace(/\/$/, "");
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function requestHeaders(
|
|
43
|
-
options: LettaCodeCloudClientOptions,
|
|
44
|
-
): Record<string, string> {
|
|
45
|
-
const headers: Record<string, string> = {
|
|
46
|
-
"Content-Type": "application/json",
|
|
47
|
-
...(options.headers ?? {}),
|
|
48
|
-
};
|
|
49
|
-
const apiKey =
|
|
50
|
-
options.apiKey ?? bearerToken(options.headers) ?? defaultApiKey();
|
|
51
|
-
if (apiKey && !headers.Authorization && !headers.authorization) {
|
|
52
|
-
headers.Authorization = `Bearer ${apiKey}`;
|
|
53
|
-
}
|
|
54
|
-
return headers;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function appendQuery(url: URL, query: ManagementQuery): void {
|
|
58
|
-
for (const [key, value] of Object.entries(query)) {
|
|
59
|
-
if (value === undefined || value === null) continue;
|
|
60
|
-
if (Array.isArray(value)) {
|
|
61
|
-
for (const item of value) url.searchParams.append(key, item);
|
|
62
|
-
} else {
|
|
63
|
-
url.searchParams.set(key, String(value));
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
async function parseResponse(response: Response): Promise<unknown> {
|
|
69
|
-
const text = await response.text();
|
|
70
|
-
if (!text) return null;
|
|
71
|
-
try {
|
|
72
|
-
return JSON.parse(text) as unknown;
|
|
73
|
-
} catch {
|
|
74
|
-
return text;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function responseErrorMessage(body: unknown, fallback: string): string {
|
|
79
|
-
if (body && typeof body === "object") {
|
|
80
|
-
const record = body as Record<string, unknown>;
|
|
81
|
-
const message = record.message ?? record.error ?? record.detail;
|
|
82
|
-
if (typeof message === "string" && message.length > 0) return message;
|
|
83
|
-
}
|
|
84
|
-
return fallback;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function cloudRequestError(
|
|
88
|
-
response: Response,
|
|
89
|
-
body: unknown,
|
|
90
|
-
action: string,
|
|
91
|
-
url: URL,
|
|
92
|
-
options: LettaCodeCloudClientOptions,
|
|
93
|
-
): Error {
|
|
94
|
-
const parts = [
|
|
95
|
-
`${action} failed`,
|
|
96
|
-
responseErrorMessage(body, `HTTP ${response.status}`),
|
|
97
|
-
`URL: ${url.toString()}`,
|
|
98
|
-
];
|
|
99
|
-
if (response.status === 401 || response.status === 403) {
|
|
100
|
-
const hasApiKey = Boolean(
|
|
101
|
-
options.apiKey ??
|
|
102
|
-
bearerToken(options.headers) ??
|
|
103
|
-
defaultApiKey(),
|
|
104
|
-
);
|
|
105
|
-
parts.push(
|
|
106
|
-
hasApiKey
|
|
107
|
-
? "Authentication failed — the API key may be invalid or lack permissions for this resource."
|
|
108
|
-
: "No API key found. Set LETTA_API_KEY (or pass apiKey in client options).",
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
|
-
return new Error(parts.join(" — "));
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function asObject<T>(body: unknown, action: string): T {
|
|
26
|
+
function asAgentRepository(body: unknown, action: string): AgentRepository {
|
|
115
27
|
if (!body || typeof body !== "object" || Array.isArray(body)) {
|
|
116
|
-
throw new Error(`${action} response did not include
|
|
28
|
+
throw new Error(`${action} response did not include a repository.`);
|
|
29
|
+
}
|
|
30
|
+
const repository = body as Record<string, unknown>;
|
|
31
|
+
const permissions = repository.permissions;
|
|
32
|
+
if (
|
|
33
|
+
typeof repository.id !== "string" ||
|
|
34
|
+
typeof repository.name !== "string" ||
|
|
35
|
+
typeof repository.is_primary !== "boolean" ||
|
|
36
|
+
(permissions !== "read" && permissions !== "read_write")
|
|
37
|
+
) {
|
|
38
|
+
throw new Error(`${action} response included an invalid repository.`);
|
|
117
39
|
}
|
|
118
|
-
return
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
125
|
-
return body as T[];
|
|
40
|
+
return {
|
|
41
|
+
id: repository.id,
|
|
42
|
+
name: repository.name,
|
|
43
|
+
isPrimary: repository.is_primary,
|
|
44
|
+
permissions,
|
|
45
|
+
};
|
|
126
46
|
}
|
|
127
47
|
|
|
128
48
|
function cloudModelEntry(
|
|
@@ -150,211 +70,144 @@ function cloudModelEntry(
|
|
|
150
70
|
};
|
|
151
71
|
}
|
|
152
72
|
|
|
73
|
+
function isNotFound(error: unknown): boolean {
|
|
74
|
+
return error instanceof APIError && error.status === 404;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Cloud management backed by the canonical generated Letta TypeScript client. */
|
|
153
78
|
export class CloudManagementTransport implements ManagementTransport {
|
|
154
|
-
constructor(private readonly
|
|
79
|
+
constructor(private readonly client: Letta) {}
|
|
155
80
|
|
|
156
|
-
listAgents(query:
|
|
157
|
-
|
|
81
|
+
async listAgents(query: AgentListParams): Promise<LettaAgent[]> {
|
|
82
|
+
const page = await this.client.agents.list(query);
|
|
83
|
+
return page.items;
|
|
158
84
|
}
|
|
159
85
|
|
|
160
|
-
retrieveAgent(agentId: string): Promise<LettaAgent> {
|
|
161
|
-
return this.
|
|
162
|
-
`/v1/agents/${encodeURIComponent(agentId)}`,
|
|
163
|
-
{},
|
|
164
|
-
"Cloud retrieve agent",
|
|
165
|
-
);
|
|
86
|
+
async retrieveAgent(agentId: string): Promise<LettaAgent> {
|
|
87
|
+
return this.client.agents.retrieve(agentId);
|
|
166
88
|
}
|
|
167
89
|
|
|
168
|
-
updateAgent(
|
|
90
|
+
async updateAgent(
|
|
169
91
|
agentId: string,
|
|
170
|
-
body:
|
|
92
|
+
body: AgentUpdateParams,
|
|
171
93
|
): Promise<LettaAgent> {
|
|
172
|
-
return this.
|
|
173
|
-
`/v1/agents/${encodeURIComponent(agentId)}`,
|
|
174
|
-
"PATCH",
|
|
175
|
-
body,
|
|
176
|
-
"Cloud update agent",
|
|
177
|
-
);
|
|
94
|
+
return this.client.agents.update(agentId, body);
|
|
178
95
|
}
|
|
179
96
|
|
|
180
97
|
async deleteAgent(agentId: string): Promise<void> {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
98
|
+
await this.client.agents.delete(agentId);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async listAgentRepositories(agentId: string): Promise<AgentRepository[]> {
|
|
102
|
+
const body = await this.client.get<{ repositories: unknown[] }>(
|
|
103
|
+
`/v1/agents/${encodeURIComponent(agentId)}/repositories`,
|
|
104
|
+
);
|
|
105
|
+
if (!Array.isArray(body.repositories)) {
|
|
106
|
+
throw new Error(
|
|
107
|
+
"Cloud list agent repositories response did not include repositories.",
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
return body.repositories.map((repository) =>
|
|
111
|
+
asAgentRepository(repository, "Cloud list agent repositories")
|
|
189
112
|
);
|
|
190
113
|
}
|
|
191
114
|
|
|
192
|
-
async
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
115
|
+
async attachAgentRepository(
|
|
116
|
+
agentId: string,
|
|
117
|
+
repositoryId: string,
|
|
118
|
+
permissions: AgentRepositoryPermissions | undefined,
|
|
119
|
+
): Promise<AgentRepository> {
|
|
120
|
+
const body = await this.client.post<{
|
|
121
|
+
success: boolean;
|
|
122
|
+
repository: unknown;
|
|
123
|
+
}>(
|
|
124
|
+
`/v1/agents/${encodeURIComponent(agentId)}/repositories`,
|
|
125
|
+
{
|
|
126
|
+
body: {
|
|
127
|
+
repository_id: repositoryId,
|
|
128
|
+
...(permissions !== undefined ? { permissions } : {}),
|
|
129
|
+
},
|
|
130
|
+
},
|
|
199
131
|
);
|
|
132
|
+
return asAgentRepository(
|
|
133
|
+
body.repository,
|
|
134
|
+
"Cloud attach agent repository",
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async detachAgentRepository(
|
|
139
|
+
agentId: string,
|
|
140
|
+
repositoryId: string,
|
|
141
|
+
): Promise<void> {
|
|
142
|
+
try {
|
|
143
|
+
await this.client.delete(
|
|
144
|
+
`/v1/agents/${encodeURIComponent(agentId)}/repositories/${encodeURIComponent(repositoryId)}`,
|
|
145
|
+
);
|
|
146
|
+
} catch (error) {
|
|
147
|
+
if (!isNotFound(error)) throw error;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async recompileAgentSystemPrompt(agentId: string): Promise<void> {
|
|
152
|
+
await this.client.agents.recompile(agentId);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async recompileConversationSystemPrompt(
|
|
156
|
+
agentId: string,
|
|
157
|
+
conversationId: string,
|
|
158
|
+
): Promise<void> {
|
|
159
|
+
await this.client.conversations.recompile(conversationId, {
|
|
160
|
+
agent_id: agentId,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async listModels(): Promise<ListModelsResult> {
|
|
165
|
+
const entries = await this.client.models.list();
|
|
200
166
|
const normalized = entries.flatMap((entry) => {
|
|
201
|
-
const model = cloudModelEntry(
|
|
167
|
+
const model = cloudModelEntry(
|
|
168
|
+
entry as unknown as Record<string, unknown>,
|
|
169
|
+
);
|
|
202
170
|
return model ? [model] : [];
|
|
203
171
|
});
|
|
204
172
|
return {
|
|
205
173
|
entries: normalized,
|
|
206
|
-
// Cloud's authenticated endpoint already returns the models available to
|
|
207
|
-
// this user, so its catalog is also the authoritative availability set.
|
|
208
174
|
availableHandles: normalized.map((entry) => entry.handle),
|
|
209
175
|
};
|
|
210
176
|
}
|
|
211
177
|
|
|
212
|
-
listConversations(
|
|
213
|
-
query:
|
|
178
|
+
async listConversations(
|
|
179
|
+
query: ConversationListParams,
|
|
214
180
|
): Promise<LettaConversation[]> {
|
|
215
|
-
return this.
|
|
216
|
-
"/v1/conversations/",
|
|
217
|
-
query,
|
|
218
|
-
"Cloud list conversations",
|
|
219
|
-
);
|
|
181
|
+
return this.client.conversations.list(query);
|
|
220
182
|
}
|
|
221
183
|
|
|
222
|
-
retrieveConversation(
|
|
184
|
+
async retrieveConversation(
|
|
223
185
|
conversationId: string,
|
|
224
186
|
): Promise<LettaConversation> {
|
|
225
|
-
return this.
|
|
226
|
-
`/v1/conversations/${encodeURIComponent(conversationId)}`,
|
|
227
|
-
{},
|
|
228
|
-
"Cloud retrieve conversation",
|
|
229
|
-
);
|
|
187
|
+
return this.client.conversations.retrieve(conversationId);
|
|
230
188
|
}
|
|
231
189
|
|
|
232
|
-
createConversation(
|
|
233
|
-
body:
|
|
190
|
+
async createConversation(
|
|
191
|
+
body: ConversationCreateParams,
|
|
234
192
|
): Promise<LettaConversation> {
|
|
235
|
-
|
|
236
|
-
if (typeof agentId !== "string" || agentId.length === 0) {
|
|
237
|
-
throw new Error("createConversation() requires a non-empty agentId.");
|
|
238
|
-
}
|
|
239
|
-
const url = this.url("/v1/conversations/");
|
|
240
|
-
url.searchParams.set("agent_id", agentId);
|
|
241
|
-
return this.requestUrlObject(
|
|
242
|
-
url,
|
|
243
|
-
"POST",
|
|
244
|
-
requestBody,
|
|
245
|
-
"Cloud create conversation",
|
|
246
|
-
);
|
|
193
|
+
return this.client.conversations.create(body);
|
|
247
194
|
}
|
|
248
195
|
|
|
249
|
-
updateConversation(
|
|
196
|
+
async updateConversation(
|
|
250
197
|
conversationId: string,
|
|
251
|
-
body:
|
|
198
|
+
body: ConversationUpdateParams,
|
|
252
199
|
): Promise<LettaConversation> {
|
|
253
|
-
return this.
|
|
254
|
-
`/v1/conversations/${encodeURIComponent(conversationId)}`,
|
|
255
|
-
"PATCH",
|
|
256
|
-
body,
|
|
257
|
-
"Cloud update conversation",
|
|
258
|
-
);
|
|
200
|
+
return this.client.conversations.update(conversationId, body);
|
|
259
201
|
}
|
|
260
202
|
|
|
261
203
|
async listConversationMessages(
|
|
262
204
|
conversationId: string,
|
|
263
|
-
query:
|
|
205
|
+
query: MessageListParams,
|
|
264
206
|
): Promise<ConversationMessagesResult> {
|
|
265
|
-
const
|
|
266
|
-
|
|
207
|
+
const page = await this.client.conversations.messages.list(
|
|
208
|
+
conversationId,
|
|
267
209
|
query,
|
|
268
|
-
"Cloud list conversation messages",
|
|
269
|
-
);
|
|
270
|
-
return { messages };
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
private async getArray<T>(
|
|
274
|
-
path: string,
|
|
275
|
-
query: ManagementQuery,
|
|
276
|
-
action: string,
|
|
277
|
-
): Promise<T[]> {
|
|
278
|
-
const body = await this.get(path, query, action);
|
|
279
|
-
return asArray<T>(body, action);
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
private async getObject<T>(
|
|
283
|
-
path: string,
|
|
284
|
-
query: ManagementQuery,
|
|
285
|
-
action: string,
|
|
286
|
-
): Promise<T> {
|
|
287
|
-
const body = await this.get(path, query, action);
|
|
288
|
-
return asObject<T>(body, action);
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
private get(
|
|
292
|
-
path: string,
|
|
293
|
-
query: ManagementQuery,
|
|
294
|
-
action: string,
|
|
295
|
-
): Promise<unknown> {
|
|
296
|
-
const url = this.url(path);
|
|
297
|
-
appendQuery(url, query);
|
|
298
|
-
return this.requestUrl(url, "GET", undefined, action);
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
private requestObject<T>(
|
|
302
|
-
path: string,
|
|
303
|
-
method: string,
|
|
304
|
-
body: Record<string, unknown>,
|
|
305
|
-
action: string,
|
|
306
|
-
): Promise<T> {
|
|
307
|
-
return this.requestUrlObject(
|
|
308
|
-
this.url(path),
|
|
309
|
-
method,
|
|
310
|
-
body,
|
|
311
|
-
action,
|
|
312
|
-
);
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
private async requestUrlObject<T>(
|
|
316
|
-
url: URL,
|
|
317
|
-
method: string,
|
|
318
|
-
body: Record<string, unknown>,
|
|
319
|
-
action: string,
|
|
320
|
-
): Promise<T> {
|
|
321
|
-
return asObject<T>(
|
|
322
|
-
await this.requestUrl(url, method, body, action),
|
|
323
|
-
action,
|
|
324
|
-
);
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
private async requestUrl(
|
|
328
|
-
url: URL,
|
|
329
|
-
method: string,
|
|
330
|
-
body: Record<string, unknown> | undefined,
|
|
331
|
-
action: string,
|
|
332
|
-
): Promise<unknown> {
|
|
333
|
-
const fetchImpl = (this.options.fetch ?? globalThis.fetch)?.bind(
|
|
334
|
-
globalThis,
|
|
335
210
|
);
|
|
336
|
-
|
|
337
|
-
throw new Error("No fetch implementation available for cloud backend.");
|
|
338
|
-
}
|
|
339
|
-
const response = await fetchImpl(url, {
|
|
340
|
-
method,
|
|
341
|
-
headers: requestHeaders(this.options),
|
|
342
|
-
...(body !== undefined ? { body: JSON.stringify(body) } : {}),
|
|
343
|
-
});
|
|
344
|
-
const responseBody = await parseResponse(response);
|
|
345
|
-
if (!response.ok) {
|
|
346
|
-
throw cloudRequestError(
|
|
347
|
-
response,
|
|
348
|
-
responseBody,
|
|
349
|
-
action,
|
|
350
|
-
url,
|
|
351
|
-
this.options,
|
|
352
|
-
);
|
|
353
|
-
}
|
|
354
|
-
return responseBody;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
private url(path: string): URL {
|
|
358
|
-
return new URL(`${apiBaseUrl(this.options.apiBaseUrl)}${path}`);
|
|
211
|
+
return { messages: page.items };
|
|
359
212
|
}
|
|
360
213
|
}
|