@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/repositories.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type Letta from "@letta-ai/letta-client";
|
|
2
|
+
import { createCloudClient } from "./cloud-client.js";
|
|
1
3
|
import type {
|
|
2
4
|
CreateRepositoryFileParams,
|
|
3
5
|
CreateRepositoryParams,
|
|
@@ -17,81 +19,6 @@ import type {
|
|
|
17
19
|
UpdateRepositoryFileParams,
|
|
18
20
|
} from "./types.js";
|
|
19
21
|
|
|
20
|
-
const DEFAULT_CLOUD_API_BASE_URL = "https://api.letta.com";
|
|
21
|
-
|
|
22
|
-
type FetchLike = typeof fetch;
|
|
23
|
-
|
|
24
|
-
function getDefaultApiKey(): string | undefined {
|
|
25
|
-
const env = (globalThis as { process?: { env?: Record<string, string | undefined> } })
|
|
26
|
-
.process?.env;
|
|
27
|
-
return env?.LETTA_API_KEY ?? env?.LETTA_CLOUD_API_KEY;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function bearerTokenFromHeaders(headers: Record<string, string> | undefined): string | undefined {
|
|
31
|
-
const authorization = headers?.Authorization ?? headers?.authorization;
|
|
32
|
-
if (!authorization) return undefined;
|
|
33
|
-
const match = /^Bearer\s+(.+)$/i.exec(authorization);
|
|
34
|
-
return match?.[1];
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function getCloudApiKey(options: LettaCodeCloudClientOptions): string | undefined {
|
|
38
|
-
return options.apiKey ?? bearerTokenFromHeaders(options.headers) ?? getDefaultApiKey();
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function getFetch(fetchOverride?: FetchLike): FetchLike {
|
|
42
|
-
const resolved = fetchOverride ?? globalThis.fetch;
|
|
43
|
-
if (!resolved) throw new Error("No fetch implementation available for cloud backend.");
|
|
44
|
-
return resolved.bind(globalThis) as FetchLike;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function normalizeCloudApiBaseUrl(url: string | undefined): string {
|
|
48
|
-
const parsed = new URL(url ?? DEFAULT_CLOUD_API_BASE_URL);
|
|
49
|
-
parsed.pathname = parsed.pathname.replace(/\/+$/, "");
|
|
50
|
-
parsed.search = "";
|
|
51
|
-
parsed.hash = "";
|
|
52
|
-
return parsed.toString().replace(/\/$/, "");
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function cloudHeaders(options: LettaCodeCloudClientOptions): Record<string, string> {
|
|
56
|
-
const headers: Record<string, string> = {
|
|
57
|
-
"Content-Type": "application/json",
|
|
58
|
-
...(options.headers ?? {}),
|
|
59
|
-
};
|
|
60
|
-
const apiKey = getCloudApiKey(options);
|
|
61
|
-
if (apiKey && !headers.Authorization && !headers.authorization) {
|
|
62
|
-
headers.Authorization = `Bearer ${apiKey}`;
|
|
63
|
-
}
|
|
64
|
-
return headers;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
async function parseJsonResponse(response: Response): Promise<unknown> {
|
|
68
|
-
const text = await response.text();
|
|
69
|
-
if (!text) return null;
|
|
70
|
-
try {
|
|
71
|
-
return JSON.parse(text) as unknown;
|
|
72
|
-
} catch {
|
|
73
|
-
return text;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function responseErrorMessage(body: unknown, fallback: string): string {
|
|
78
|
-
if (body && typeof body === "object") {
|
|
79
|
-
const record = body as Record<string, unknown>;
|
|
80
|
-
const message = record.message ?? record.error ?? record.detail;
|
|
81
|
-
const reasonText = record.reason_text;
|
|
82
|
-
const pieces = [message, reasonText]
|
|
83
|
-
.filter((value): value is string => typeof value === "string" && value.length > 0);
|
|
84
|
-
if (pieces.length > 0) return pieces.join(": ");
|
|
85
|
-
}
|
|
86
|
-
return fallback;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function assertOkResponse(response: Response, body: unknown, action: string): void {
|
|
90
|
-
if (!response.ok) {
|
|
91
|
-
throw new Error(responseErrorMessage(body, `${action} failed with HTTP ${response.status}`));
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
22
|
function optionalString(value: unknown): string | undefined {
|
|
96
23
|
return typeof value === "string" ? value : undefined;
|
|
97
24
|
}
|
|
@@ -131,17 +58,20 @@ function addOptionalSearchParam(url: URL, key: string, value: string | number |
|
|
|
131
58
|
}
|
|
132
59
|
|
|
133
60
|
export class RepositoriesClient {
|
|
134
|
-
constructor(
|
|
61
|
+
constructor(
|
|
62
|
+
options: LettaCodeCloudClientOptions,
|
|
63
|
+
private readonly client: Letta = createCloudClient(options),
|
|
64
|
+
) {}
|
|
135
65
|
|
|
136
66
|
async create(params: CreateRepositoryParams): Promise<Repository> {
|
|
137
|
-
return toRepository(await this.request("/v1/repositories", "POST", { name: params.name }
|
|
67
|
+
return toRepository(await this.request("/v1/repositories", "POST", { name: params.name }));
|
|
138
68
|
}
|
|
139
69
|
|
|
140
70
|
async list(params: ListRepositoriesParams = {}): Promise<ListRepositoriesResult> {
|
|
141
71
|
const url = this.url("/v1/repositories");
|
|
142
72
|
addOptionalSearchParam(url, "limit", params.limit);
|
|
143
73
|
addOptionalSearchParam(url, "offset", params.offset);
|
|
144
|
-
const body = await this.requestUrl(url, "GET", undefined
|
|
74
|
+
const body = await this.requestUrl(url, "GET", undefined);
|
|
145
75
|
if (!body || typeof body !== "object") {
|
|
146
76
|
throw new Error("Cloud list repositories response did not include repositories.");
|
|
147
77
|
}
|
|
@@ -156,7 +86,7 @@ export class RepositoriesClient {
|
|
|
156
86
|
}
|
|
157
87
|
|
|
158
88
|
async get(repositoryId: string): Promise<Repository> {
|
|
159
|
-
return toRepository(await this.request(`/v1/repositories/${encodeURIComponent(repositoryId)}`, "GET", undefined
|
|
89
|
+
return toRepository(await this.request(`/v1/repositories/${encodeURIComponent(repositoryId)}`, "GET", undefined));
|
|
160
90
|
}
|
|
161
91
|
|
|
162
92
|
/**
|
|
@@ -169,7 +99,6 @@ export class RepositoriesClient {
|
|
|
169
99
|
`/v1/repositories/${encodeURIComponent(repositoryId)}`,
|
|
170
100
|
"DELETE",
|
|
171
101
|
undefined,
|
|
172
|
-
"Cloud delete repository",
|
|
173
102
|
);
|
|
174
103
|
}
|
|
175
104
|
|
|
@@ -182,7 +111,7 @@ export class RepositoriesClient {
|
|
|
182
111
|
addOptionalSearchParam(url, "path_prefix", params.pathPrefix);
|
|
183
112
|
addOptionalSearchParam(url, "depth", params.depth);
|
|
184
113
|
addOptionalSearchParam(url, "ref", params.ref);
|
|
185
|
-
const body = await this.requestUrl(url, "GET", undefined
|
|
114
|
+
const body = await this.requestUrl(url, "GET", undefined);
|
|
186
115
|
if (!body || typeof body !== "object") {
|
|
187
116
|
throw new Error("Cloud list repository files response did not include files.");
|
|
188
117
|
}
|
|
@@ -210,14 +139,13 @@ export class RepositoriesClient {
|
|
|
210
139
|
`/v1/repositories/${encodeURIComponent(repositoryId)}/files`,
|
|
211
140
|
"POST",
|
|
212
141
|
{ path: params.path, content: params.content },
|
|
213
|
-
"Cloud create repository file",
|
|
214
142
|
)),
|
|
215
143
|
|
|
216
144
|
read: async (repositoryId: string, params: { path: string; ref?: string }): Promise<RepositoryFile> => {
|
|
217
145
|
const url = this.url(`/v1/repositories/${encodeURIComponent(repositoryId)}/files/content`);
|
|
218
146
|
url.searchParams.set("path", params.path);
|
|
219
147
|
addOptionalSearchParam(url, "ref", params.ref);
|
|
220
|
-
const body = await this.requestUrl(url, "GET", undefined
|
|
148
|
+
const body = await this.requestUrl(url, "GET", undefined);
|
|
221
149
|
if (!body || typeof body !== "object") {
|
|
222
150
|
throw new Error("Cloud read repository file response did not include file content.");
|
|
223
151
|
}
|
|
@@ -249,7 +177,6 @@ export class RepositoriesClient {
|
|
|
249
177
|
}
|
|
250
178
|
: {}),
|
|
251
179
|
},
|
|
252
|
-
"Cloud update repository file",
|
|
253
180
|
)),
|
|
254
181
|
|
|
255
182
|
delete: async (
|
|
@@ -260,7 +187,6 @@ export class RepositoriesClient {
|
|
|
260
187
|
`/v1/repositories/${encodeURIComponent(repositoryId)}/files/content`,
|
|
261
188
|
"DELETE",
|
|
262
189
|
{ path: params.path },
|
|
263
|
-
"Cloud delete repository file",
|
|
264
190
|
);
|
|
265
191
|
if (!body || typeof body !== "object") {
|
|
266
192
|
throw new Error("Cloud delete repository file response did not include delete details.");
|
|
@@ -278,7 +204,7 @@ export class RepositoriesClient {
|
|
|
278
204
|
const url = this.url(`/v1/repositories/${encodeURIComponent(repositoryId)}/versions`);
|
|
279
205
|
addOptionalSearchParam(url, "path", params.path);
|
|
280
206
|
addOptionalSearchParam(url, "limit", params.limit);
|
|
281
|
-
const body = await this.requestUrl(url, "GET", undefined
|
|
207
|
+
const body = await this.requestUrl(url, "GET", undefined);
|
|
282
208
|
if (Array.isArray(body)) return body as RepositoryVersion[];
|
|
283
209
|
if (body && typeof body === "object") {
|
|
284
210
|
const record = body as Record<string, unknown>;
|
|
@@ -295,7 +221,7 @@ export class RepositoriesClient {
|
|
|
295
221
|
): Promise<RepositoryFile> => {
|
|
296
222
|
const url = this.url(`/v1/repositories/${encodeURIComponent(repositoryId)}/versions/${encodeURIComponent(sha)}`);
|
|
297
223
|
url.searchParams.set("path", params.path);
|
|
298
|
-
const body = await this.requestUrl(url, "GET", undefined
|
|
224
|
+
const body = await this.requestUrl(url, "GET", undefined);
|
|
299
225
|
if (!body || typeof body !== "object") {
|
|
300
226
|
throw new Error("Cloud get repository version response did not include file content.");
|
|
301
227
|
}
|
|
@@ -310,31 +236,32 @@ export class RepositoriesClient {
|
|
|
310
236
|
};
|
|
311
237
|
|
|
312
238
|
private url(path: string): URL {
|
|
313
|
-
return new URL(`${
|
|
239
|
+
return new URL(`${this.client.baseURL}${path}`);
|
|
314
240
|
}
|
|
315
241
|
|
|
316
242
|
private async request(
|
|
317
243
|
path: string,
|
|
318
244
|
method: string,
|
|
319
245
|
body: unknown,
|
|
320
|
-
action: string,
|
|
321
246
|
): Promise<unknown> {
|
|
322
|
-
return this.requestUrl(this.url(path), method, body
|
|
247
|
+
return this.requestUrl(this.url(path), method, body);
|
|
323
248
|
}
|
|
324
249
|
|
|
325
250
|
private async requestUrl(
|
|
326
251
|
url: URL,
|
|
327
252
|
method: string,
|
|
328
253
|
body: unknown,
|
|
329
|
-
action: string,
|
|
330
254
|
): Promise<unknown> {
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
255
|
+
const options = body !== undefined ? { body } : undefined;
|
|
256
|
+
switch (method) {
|
|
257
|
+
case "GET":
|
|
258
|
+
return this.client.get(url.toString(), options);
|
|
259
|
+
case "POST":
|
|
260
|
+
return this.client.post(url.toString(), options);
|
|
261
|
+
case "DELETE":
|
|
262
|
+
return this.client.delete(url.toString(), options);
|
|
263
|
+
default:
|
|
264
|
+
throw new Error(`Unsupported repository request method: ${method}`);
|
|
265
|
+
}
|
|
339
266
|
}
|
|
340
267
|
}
|
package/src/types.ts
CHANGED
|
@@ -6,7 +6,11 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type { PersonalityId } from "@letta-ai/letta-code/agent-presets";
|
|
9
|
+
import type { ListModelsResponseModelEntry } from "@letta-ai/letta-code/app-server-protocol";
|
|
10
|
+
import type { Message as LettaMessage } from "@letta-ai/letta-client/resources/agents/messages";
|
|
11
|
+
import type { CreateBlock } from "@letta-ai/letta-client/resources/blocks/blocks";
|
|
9
12
|
import type { LettaCodeCloudSandboxOptions } from "./cloud-sandbox.js";
|
|
13
|
+
export type { CreateBlock } from "@letta-ai/letta-client/resources/blocks/blocks";
|
|
10
14
|
export type {
|
|
11
15
|
GitHubRepositoryRef,
|
|
12
16
|
LettaCodeCloudSandboxOptions,
|
|
@@ -15,26 +19,6 @@ export type {
|
|
|
15
19
|
/** Letta Code personality preset used to seed a new agent. */
|
|
16
20
|
export type LettaCodePersonalityId = PersonalityId;
|
|
17
21
|
|
|
18
|
-
/** Custom memory block definition accepted when creating agents. */
|
|
19
|
-
export interface CreateBlock {
|
|
20
|
-
label: string;
|
|
21
|
-
value: string;
|
|
22
|
-
base_template_id?: string | null;
|
|
23
|
-
deployment_id?: string | null;
|
|
24
|
-
description?: string | null;
|
|
25
|
-
entity_id?: string | null;
|
|
26
|
-
hidden?: boolean | null;
|
|
27
|
-
is_template?: boolean;
|
|
28
|
-
limit?: number;
|
|
29
|
-
metadata?: Record<string, unknown> | null;
|
|
30
|
-
preserve_on_migration?: boolean | null;
|
|
31
|
-
project_id?: string | null;
|
|
32
|
-
read_only?: boolean;
|
|
33
|
-
tags?: string[] | null;
|
|
34
|
-
template_id?: string | null;
|
|
35
|
-
template_name?: string | null;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
22
|
export interface LettaCodeSocketLike {
|
|
39
23
|
readyState: number;
|
|
40
24
|
send(data: string): void;
|
|
@@ -408,6 +392,12 @@ export interface ListRepositoriesResult {
|
|
|
408
392
|
export interface RepositoryResource {
|
|
409
393
|
type: "repository";
|
|
410
394
|
repositoryId: string;
|
|
395
|
+
/**
|
|
396
|
+
* Whether to trigger a system-prompt recompile after attaching (and after
|
|
397
|
+
* detaching on cleanup) so the session's conversation does not retain
|
|
398
|
+
* stale repository projections. Defaults to `true`.
|
|
399
|
+
*/
|
|
400
|
+
recompile?: boolean;
|
|
411
401
|
}
|
|
412
402
|
|
|
413
403
|
export interface RepositoryFileEntry {
|
|
@@ -558,16 +548,7 @@ export type ReasoningEffort =
|
|
|
558
548
|
| "high"
|
|
559
549
|
| "xhigh";
|
|
560
550
|
|
|
561
|
-
export type LettaCodeModelEntry =
|
|
562
|
-
id: string;
|
|
563
|
-
handle: string;
|
|
564
|
-
label: string;
|
|
565
|
-
description: string;
|
|
566
|
-
isDefault?: boolean;
|
|
567
|
-
isFeatured?: boolean;
|
|
568
|
-
free?: boolean;
|
|
569
|
-
updateArgs?: Record<string, unknown>;
|
|
570
|
-
};
|
|
551
|
+
export type LettaCodeModelEntry = ListModelsResponseModelEntry;
|
|
571
552
|
|
|
572
553
|
export interface ListModelsResult {
|
|
573
554
|
entries: LettaCodeModelEntry[];
|
|
@@ -668,7 +649,11 @@ export interface CreateSessionOptions {
|
|
|
668
649
|
*/
|
|
669
650
|
approvalRecoveryTimeoutMs?: number;
|
|
670
651
|
|
|
671
|
-
/**
|
|
652
|
+
/**
|
|
653
|
+
* Cloud repository resources to attach for the lifetime of the SDK session.
|
|
654
|
+
* For relationships that should outlive the session, use
|
|
655
|
+
* `client.agents.repositories.attach()` instead.
|
|
656
|
+
*/
|
|
672
657
|
resources?: RepositoryResource[];
|
|
673
658
|
|
|
674
659
|
}
|
|
@@ -845,6 +830,8 @@ export interface SessionDeviceStatus {
|
|
|
845
830
|
permissionMode: PermissionMode;
|
|
846
831
|
/** Working directory currently applied to this runtime scope. */
|
|
847
832
|
workingDirectory: string | null;
|
|
833
|
+
/** Agent memory checkout on the computer executing this session. */
|
|
834
|
+
memoryDirectory: string | null;
|
|
848
835
|
/** Approvals the device is still waiting on (foreground-resume UI). */
|
|
849
836
|
pendingControlRequests: SessionPendingControlRequest[];
|
|
850
837
|
/** Full wire `device_status` payload as an escape hatch. */
|
|
@@ -985,7 +972,7 @@ export interface SDKInitMessage {
|
|
|
985
972
|
export interface SDKAssistantMessage {
|
|
986
973
|
type: "assistant";
|
|
987
974
|
content: string;
|
|
988
|
-
/**
|
|
975
|
+
/** Top-level message ID when provided by the stream; otherwise an SDK-generated identifier. */
|
|
989
976
|
uuid: string;
|
|
990
977
|
/** Stable lineage key for this typed message slice, when provided. */
|
|
991
978
|
otid?: string | null;
|
|
@@ -1020,7 +1007,7 @@ export interface SDKToolResultMessage {
|
|
|
1020
1007
|
export interface SDKReasoningMessage {
|
|
1021
1008
|
type: "reasoning";
|
|
1022
1009
|
content: string;
|
|
1023
|
-
/**
|
|
1010
|
+
/** Top-level message ID when provided by the stream; otherwise an SDK-generated identifier. */
|
|
1024
1011
|
uuid: string;
|
|
1025
1012
|
/** Stable lineage key for this typed message slice, when provided. */
|
|
1026
1013
|
otid?: string | null;
|
|
@@ -1211,7 +1198,7 @@ export interface ListMessagesOptions {
|
|
|
1211
1198
|
* authoritative pagination answer.
|
|
1212
1199
|
*/
|
|
1213
1200
|
export interface ListMessagesResult {
|
|
1214
|
-
messages:
|
|
1201
|
+
messages: LettaMessage[];
|
|
1215
1202
|
/** ID of the oldest message in this page; use as `before` for the next page when present. */
|
|
1216
1203
|
nextBefore?: string | null;
|
|
1217
1204
|
/** Whether more pages exist in the requested direction, when known. */
|
|
@@ -1251,7 +1238,7 @@ export interface BootstrapStateResult {
|
|
|
1251
1238
|
/** Whether memfs (git-backed memory) is enabled, when known. */
|
|
1252
1239
|
memfsEnabled?: boolean;
|
|
1253
1240
|
/** Initial history page (same shape as listMessages.messages). */
|
|
1254
|
-
messages:
|
|
1241
|
+
messages: LettaMessage[];
|
|
1255
1242
|
/** Cursor to fetch older messages. Null when the backend knows there are no more pages. */
|
|
1256
1243
|
nextBefore?: string | null;
|
|
1257
1244
|
/** Whether more history pages exist, when known. */
|