@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.
Files changed (52) hide show
  1. package/AGENTS.md +35 -0
  2. package/README.md +22 -10
  3. package/dist/agent-repositories.d.ts +15 -0
  4. package/dist/agent-repositories.d.ts.map +1 -0
  5. package/dist/app-server-management.d.ts +10 -7
  6. package/dist/app-server-management.d.ts.map +1 -1
  7. package/dist/app-server-models.d.ts +4 -0
  8. package/dist/app-server-models.d.ts.map +1 -0
  9. package/dist/app-server-session.d.ts +3 -3
  10. package/dist/app-server-session.d.ts.map +1 -1
  11. package/dist/client-base.d.ts +5 -0
  12. package/dist/client-base.d.ts.map +1 -1
  13. package/dist/client-entry.js +3386 -639
  14. package/dist/client-entry.js.map +85 -13
  15. package/dist/cloud-client.d.ts +7 -0
  16. package/dist/cloud-client.d.ts.map +1 -0
  17. package/dist/cloud-management.d.ts +21 -18
  18. package/dist/cloud-management.d.ts.map +1 -1
  19. package/dist/cloud-session.d.ts +7 -5
  20. package/dist/cloud-session.d.ts.map +1 -1
  21. package/dist/index.d.ts +1 -1
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +3400 -653
  24. package/dist/index.js.map +86 -14
  25. package/dist/management-types.d.ts +85 -64
  26. package/dist/management-types.d.ts.map +1 -1
  27. package/dist/management.d.ts +11 -9
  28. package/dist/management.d.ts.map +1 -1
  29. package/dist/remote-session-protocol.d.ts +1 -1
  30. package/dist/remote-session-protocol.d.ts.map +1 -1
  31. package/dist/remote.d.ts +4 -5
  32. package/dist/remote.d.ts.map +1 -1
  33. package/dist/repositories.d.ts +3 -2
  34. package/dist/repositories.d.ts.map +1 -1
  35. package/dist/types.d.ts +22 -34
  36. package/dist/types.d.ts.map +1 -1
  37. package/package.json +3 -2
  38. package/src/agent-repositories.ts +159 -0
  39. package/src/app-server-management.ts +56 -116
  40. package/src/app-server-models.ts +20 -0
  41. package/src/app-server-session.ts +71 -146
  42. package/src/client-base.ts +72 -20
  43. package/src/cloud-client.ts +46 -0
  44. package/src/cloud-management.ts +128 -275
  45. package/src/cloud-session.ts +168 -267
  46. package/src/index.ts +6 -0
  47. package/src/management-types.ts +107 -64
  48. package/src/management.ts +46 -39
  49. package/src/remote-session-protocol.ts +6 -2
  50. package/src/remote.ts +18 -63
  51. package/src/repositories.ts +26 -99
  52. package/src/types.ts +22 -35
@@ -1,102 +1,145 @@
1
+ import type {
2
+ AgentListParams,
3
+ AgentState,
4
+ AgentUpdateParams,
5
+ } from "@letta-ai/letta-client/resources/agents/agents";
6
+ import type { Message } from "@letta-ai/letta-client/resources/agents/messages";
7
+ import type {
8
+ Conversation,
9
+ ConversationCreateParams,
10
+ ConversationListParams,
11
+ ConversationUpdateParams,
12
+ } from "@letta-ai/letta-client/resources/conversations/conversations";
13
+ import type { MessageListParams } from "@letta-ai/letta-client/resources/conversations/messages";
1
14
  import type { ListMessagesResult, ListModelsResult } from "./types.js";
2
15
 
3
16
  /** Agent state returned by either the Cloud API or Letta Code app-server. */
4
- export type LettaAgent = Record<string, unknown> & {
5
- id: string;
6
- name: string;
7
- description?: string | null;
8
- model?: string | null;
9
- model_settings?: Record<string, unknown> | null;
10
- tags?: string[];
11
- created_at?: string | null;
12
- updated_at?: string | null;
13
- };
17
+ export type LettaAgent = AgentState;
14
18
 
15
19
  /** Conversation state returned by either the Cloud API or Letta Code app-server. */
16
- export type LettaConversation = Record<string, unknown> & {
17
- id: string;
18
- agent_id: string;
19
- summary?: string | null;
20
- description?: string | null;
21
- model?: string | null;
22
- model_settings?: Record<string, unknown> | null;
23
- archived?: boolean;
24
- created_at?: string | null;
25
- updated_at?: string | null;
26
- last_message_at?: string | null;
27
- };
20
+ export type LettaConversation = Conversation;
28
21
 
29
22
  /** Raw Letta API message returned from conversation history. */
30
- export type LettaConversationMessage = Record<string, unknown>;
23
+ export type LettaConversationMessage = Message;
24
+
25
+ type Present<T> = Exclude<T, null | undefined>;
31
26
 
32
27
  export interface ListAgentsOptions {
33
- before?: string;
34
- after?: string;
35
- limit?: number;
28
+ before?: Present<AgentListParams["before"]>;
29
+ after?: Present<AgentListParams["after"]>;
30
+ limit?: Present<AgentListParams["limit"]>;
36
31
  order?: "asc" | "desc";
37
32
  orderBy?: "createdAt" | "lastRunCompletion";
38
33
  /** Search agent names. */
39
- query?: string;
34
+ query?: Present<AgentListParams["query_text"]>;
40
35
  /** Match one exact agent name. */
41
- name?: string;
42
- tags?: string[];
43
- matchAllTags?: boolean;
36
+ name?: Present<AgentListParams["name"]>;
37
+ tags?: Present<AgentListParams["tags"]>;
38
+ matchAllTags?: Present<AgentListParams["match_all_tags"]>;
44
39
  /** Relationships to hydrate in each returned agent. */
45
- include?: string[];
40
+ include?: Present<AgentListParams["include"]>;
46
41
  }
47
42
 
48
43
  export interface UpdateAgentOptions {
49
- name?: string | null;
50
- description?: string | null;
51
- model?: string | null;
52
- modelSettings?: Record<string, unknown> | null;
53
- system?: string | null;
54
- tags?: string[] | null;
55
- hidden?: boolean | null;
56
- contextWindowLimit?: number | null;
44
+ name?: AgentUpdateParams["name"];
45
+ description?: AgentUpdateParams["description"];
46
+ model?: AgentUpdateParams["model"];
47
+ modelSettings?: AgentUpdateParams["model_settings"];
48
+ system?: AgentUpdateParams["system"];
49
+ tags?: AgentUpdateParams["tags"];
50
+ hidden?: AgentUpdateParams["hidden"];
51
+ contextWindowLimit?: AgentUpdateParams["context_window_limit"];
57
52
  }
58
53
 
59
54
  export interface ListConversationsOptions {
60
- agentId?: string;
61
- after?: string;
62
- limit?: number;
63
- order?: "asc" | "desc";
55
+ agentId?: Present<ConversationListParams["agent_id"]>;
56
+ after?: Present<ConversationListParams["after"]>;
57
+ limit?: Present<ConversationListParams["limit"]>;
58
+ order?: Present<ConversationListParams["order"]>;
64
59
  orderBy?: "createdAt" | "lastRunCompletion" | "lastMessageAt";
65
- archiveStatus?: "unarchived" | "archived" | "all";
66
- summarySearch?: string;
60
+ archiveStatus?: Present<ConversationListParams["archive_status"]>;
61
+ summarySearch?: Present<ConversationListParams["summary_search"]>;
67
62
  }
68
63
 
69
64
  export interface CreateConversationOptions {
70
- agentId: string;
71
- summary?: string | null;
72
- description?: string | null;
73
- model?: string | null;
74
- modelSettings?: Record<string, unknown> | null;
75
- contextWindowLimit?: number | null;
76
- hidden?: boolean;
65
+ agentId: ConversationCreateParams["agent_id"];
66
+ summary?: ConversationCreateParams["summary"];
67
+ description?: ConversationCreateParams["description"];
68
+ model?: ConversationCreateParams["model"];
69
+ modelSettings?: ConversationCreateParams["model_settings"];
70
+ contextWindowLimit?: ConversationCreateParams["context_window_limit"];
71
+ hidden?: ConversationCreateParams["hidden"];
77
72
  }
78
73
 
79
74
  export interface UpdateConversationOptions {
80
- summary?: string | null;
81
- description?: string | null;
82
- model?: string | null;
83
- modelSettings?: Record<string, unknown> | null;
84
- contextWindowLimit?: number | null;
85
- archived?: boolean | null;
75
+ summary?: ConversationUpdateParams["summary"];
76
+ description?: ConversationUpdateParams["description"];
77
+ model?: ConversationUpdateParams["model"];
78
+ modelSettings?: ConversationUpdateParams["model_settings"];
79
+ contextWindowLimit?: ConversationUpdateParams["context_window_limit"];
80
+ archived?: ConversationUpdateParams["archived"];
86
81
  }
87
82
 
88
83
  export interface ConversationMessagesOptions {
89
- before?: string;
90
- after?: string;
84
+ before?: Present<MessageListParams["before"]>;
85
+ after?: Present<MessageListParams["after"]>;
91
86
  order?: "asc" | "desc";
92
- limit?: number;
87
+ limit?: Present<MessageListParams["limit"]>;
88
+ }
89
+
90
+ export type ConversationMessagesResult = ListMessagesResult;
91
+
92
+ export type AgentRepositoryPermissions = "read" | "read_write";
93
+ export type AgentRepositoryRecompileTarget = "default" | false;
94
+
95
+ /** A repository relationship persisted on an agent. */
96
+ export interface AgentRepository {
97
+ id: string;
98
+ name: string;
99
+ isPrimary: boolean;
100
+ permissions: AgentRepositoryPermissions;
93
101
  }
94
102
 
95
- export type ConversationMessagesResult = ListMessagesResult & {
96
- messages: LettaConversationMessage[];
97
- };
103
+ export interface AttachAgentRepositoryOptions {
104
+ /** Access granted to the agent. Defaults to `read_write` on the server. */
105
+ permissions?: AgentRepositoryPermissions;
106
+ /** Recompile the agent's default conversation after attachment. Defaults to `default`. */
107
+ recompile?: AgentRepositoryRecompileTarget;
108
+ }
109
+
110
+ export interface DetachAgentRepositoryOptions {
111
+ /** Recompile the agent's default conversation after detachment. Defaults to `default`. */
112
+ recompile?: AgentRepositoryRecompileTarget;
113
+ }
114
+
115
+ /** Persistent agent-repository relationships. Available on the Cloud backend. */
116
+ export interface AgentRepositoriesClient {
117
+ list(agentId: string): Promise<AgentRepository[]>;
118
+ /**
119
+ * Persistently attach a repository, wait for the relationship to become
120
+ * visible, then recompile the agent's default conversation unless disabled.
121
+ * If recompilation fails, the relationship remains attached and retrying is safe.
122
+ */
123
+ attach(
124
+ agentId: string,
125
+ repositoryId: string,
126
+ options?: AttachAgentRepositoryOptions,
127
+ ): Promise<AgentRepository>;
128
+ /**
129
+ * Persistently detach a repository, wait for the relationship to disappear,
130
+ * then recompile the agent's default conversation unless disabled. If
131
+ * recompilation fails, the relationship remains detached and retrying is safe.
132
+ */
133
+ detach(
134
+ agentId: string,
135
+ repositoryId: string,
136
+ options?: DetachAgentRepositoryOptions,
137
+ ): Promise<void>;
138
+ }
98
139
 
99
140
  export interface AgentsClient {
141
+ /** Persistent repository relationships for this agent. Cloud only. */
142
+ readonly repositories: AgentRepositoriesClient;
100
143
  list(options?: ListAgentsOptions): Promise<LettaAgent[]>;
101
144
  retrieve(agentId: string): Promise<LettaAgent>;
102
145
  update(
package/src/management.ts CHANGED
@@ -1,4 +1,15 @@
1
1
  import type {
2
+ AgentListParams,
3
+ AgentUpdateParams,
4
+ } from "@letta-ai/letta-client/resources/agents/agents";
5
+ import type {
6
+ ConversationCreateParams,
7
+ ConversationListParams,
8
+ ConversationUpdateParams,
9
+ } from "@letta-ai/letta-client/resources/conversations/conversations";
10
+ import type { MessageListParams } from "@letta-ai/letta-client/resources/conversations/messages";
11
+ import type {
12
+ AgentRepositoriesClient,
2
13
  AgentsClient,
3
14
  ConversationsClient,
4
15
  ConversationMessagesResult,
@@ -14,60 +25,48 @@ import type {
14
25
  } from "./management-types.js";
15
26
  import type { ListModelsResult } from "./types.js";
16
27
 
17
- export type ManagementQuery = Record<
18
- string,
19
- string | number | boolean | string[] | null | undefined
20
- >;
21
-
22
28
  export interface ManagementTransport {
23
- listAgents(query: ManagementQuery): Promise<LettaAgent[]>;
29
+ listAgents(query: AgentListParams): Promise<LettaAgent[]>;
24
30
  retrieveAgent(agentId: string): Promise<LettaAgent>;
25
31
  updateAgent(
26
32
  agentId: string,
27
- body: Record<string, unknown>,
33
+ body: AgentUpdateParams,
28
34
  ): Promise<LettaAgent>;
29
35
  deleteAgent(agentId: string): Promise<void>;
30
36
  listModels(): Promise<ListModelsResult>;
31
37
  listConversations(
32
- query: ManagementQuery,
38
+ query: ConversationListParams,
33
39
  ): Promise<LettaConversation[]>;
34
40
  retrieveConversation(
35
41
  conversationId: string,
36
42
  ): Promise<LettaConversation>;
37
43
  createConversation(
38
- body: Record<string, unknown>,
44
+ body: ConversationCreateParams,
39
45
  ): Promise<LettaConversation>;
40
46
  updateConversation(
41
47
  conversationId: string,
42
- body: Record<string, unknown>,
48
+ body: ConversationUpdateParams,
43
49
  ): Promise<LettaConversation>;
44
50
  listConversationMessages(
45
51
  conversationId: string,
46
- query: ManagementQuery,
52
+ query: MessageListParams,
47
53
  ): Promise<ConversationMessagesResult>;
48
54
  }
49
55
 
50
56
  type TransportProvider = () => ManagementTransport;
51
57
 
52
- function definedEntries(
53
- values: Record<string, unknown>,
54
- ): Record<string, unknown> {
55
- return Object.fromEntries(
56
- Object.entries(values).filter(([, value]) => value !== undefined),
57
- );
58
- }
59
-
60
58
  function assertNonEmptyId(value: string, name: string): void {
61
59
  if (typeof value !== "string" || value.trim().length === 0) {
62
60
  throw new Error(`Invalid ${name}. Expected a non-empty string.`);
63
61
  }
64
62
  }
65
63
 
66
- function agentListQuery(options: ListAgentsOptions): ManagementQuery {
67
- const orderBy = options.orderBy?.replace(
68
- /[A-Z]/g,
69
- (character) => `_${character.toLowerCase()}`,
70
- );
64
+ function agentListQuery(options: ListAgentsOptions): AgentListParams {
65
+ const orderBy = options.orderBy === "createdAt"
66
+ ? "created_at"
67
+ : options.orderBy === "lastRunCompletion"
68
+ ? "last_run_completion"
69
+ : undefined;
71
70
  return {
72
71
  before: options.before,
73
72
  after: options.after,
@@ -82,8 +81,8 @@ function agentListQuery(options: ListAgentsOptions): ManagementQuery {
82
81
  };
83
82
  }
84
83
 
85
- function agentUpdateBody(options: UpdateAgentOptions): Record<string, unknown> {
86
- return definedEntries({
84
+ function agentUpdateBody(options: UpdateAgentOptions): AgentUpdateParams {
85
+ return {
87
86
  name: options.name,
88
87
  description: options.description,
89
88
  model: options.model,
@@ -92,16 +91,20 @@ function agentUpdateBody(options: UpdateAgentOptions): Record<string, unknown> {
92
91
  tags: options.tags,
93
92
  hidden: options.hidden,
94
93
  context_window_limit: options.contextWindowLimit,
95
- });
94
+ };
96
95
  }
97
96
 
98
97
  function conversationListQuery(
99
98
  options: ListConversationsOptions,
100
- ): ManagementQuery {
101
- const orderBy = options.orderBy?.replace(
102
- /[A-Z]/g,
103
- (character) => `_${character.toLowerCase()}`,
104
- );
99
+ ): ConversationListParams {
100
+ const orderBy: ConversationListParams["order_by"] =
101
+ options.orderBy === "createdAt"
102
+ ? "created_at"
103
+ : options.orderBy === "lastRunCompletion"
104
+ ? "last_run_completion"
105
+ : options.orderBy === "lastMessageAt"
106
+ ? "last_message_at"
107
+ : undefined;
105
108
  return {
106
109
  agent_id: options.agentId,
107
110
  after: options.after,
@@ -115,8 +118,8 @@ function conversationListQuery(
115
118
 
116
119
  function conversationCreateBody(
117
120
  options: CreateConversationOptions,
118
- ): Record<string, unknown> {
119
- return definedEntries({
121
+ ): ConversationCreateParams {
122
+ return {
120
123
  agent_id: options.agentId,
121
124
  summary: options.summary,
122
125
  description: options.description,
@@ -124,25 +127,25 @@ function conversationCreateBody(
124
127
  model_settings: options.modelSettings,
125
128
  context_window_limit: options.contextWindowLimit,
126
129
  hidden: options.hidden,
127
- });
130
+ };
128
131
  }
129
132
 
130
133
  function conversationUpdateBody(
131
134
  options: UpdateConversationOptions,
132
- ): Record<string, unknown> {
133
- return definedEntries({
135
+ ): ConversationUpdateParams {
136
+ return {
134
137
  summary: options.summary,
135
138
  description: options.description,
136
139
  model: options.model,
137
140
  model_settings: options.modelSettings,
138
141
  context_window_limit: options.contextWindowLimit,
139
142
  archived: options.archived,
140
- });
143
+ };
141
144
  }
142
145
 
143
146
  function conversationMessagesQuery(
144
147
  options: ConversationMessagesOptions,
145
- ): ManagementQuery {
148
+ ): MessageListParams {
146
149
  return {
147
150
  before: options.before,
148
151
  after: options.after,
@@ -153,8 +156,12 @@ function conversationMessagesQuery(
153
156
 
154
157
  export function createAgentsClient(
155
158
  transport: TransportProvider,
159
+ repositories: () => AgentRepositoriesClient,
156
160
  ): AgentsClient {
157
161
  return {
162
+ get repositories() {
163
+ return repositories();
164
+ },
158
165
  list: (options = {}) =>
159
166
  transport().listAgents(agentListQuery(options)),
160
167
  retrieve: (agentId) => transport().retrieveAgent(agentId),
@@ -71,11 +71,11 @@ export interface RemoteClientRuntimeController {
71
71
  options: RuntimeSendTurnOptions,
72
72
  ): void;
73
73
  abort(runtime: RuntimeScope): Promise<void>;
74
- request(
74
+ request<TResponse extends ProtocolMessage = ProtocolMessage>(
75
75
  type: string,
76
76
  body: Record<string, unknown>,
77
77
  options?: RuntimeRequestOptions,
78
- ): Promise<ProtocolMessage>;
78
+ ): Promise<TResponse>;
79
79
  recoverPendingApprovals(
80
80
  runtime: RuntimeScope,
81
81
  options?: RecoverPendingApprovalsOptions,
@@ -664,6 +664,10 @@ export function toSessionDeviceStatus(
664
664
  isProcessing: status.is_processing,
665
665
  permissionMode,
666
666
  workingDirectory: status.current_working_directory,
667
+ memoryDirectory:
668
+ typeof status.memory_directory === "string"
669
+ ? status.memory_directory
670
+ : null,
667
671
  pendingControlRequests: pendingControlRequests(status),
668
672
  raw: { ...status },
669
673
  };
package/src/remote.ts CHANGED
@@ -1,3 +1,6 @@
1
+ import type Letta from "@letta-ai/letta-client";
2
+ import { createCloudClient } from "./cloud-client.js";
3
+
1
4
  export type RemoteEnvironmentTarget =
2
5
  | { connectionId: string }
3
6
  | { environmentId: string }
@@ -43,35 +46,6 @@ export interface ResolvedRemoteEnvironment {
43
46
  target: RemoteEnvironmentTarget;
44
47
  }
45
48
 
46
- type RemoteFetch = typeof fetch;
47
-
48
- function getDefaultApiKey(): string | undefined {
49
- if (typeof process === "undefined") {
50
- return undefined;
51
- }
52
- return process.env.LETTA_API_KEY;
53
- }
54
-
55
- function createHeaders(options: RemoteEnvironmentClientOptions): Record<string, string> {
56
- const apiKey = options.apiKey ?? getDefaultApiKey();
57
- return {
58
- ...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),
59
- ...(options.headers ?? {}),
60
- };
61
- }
62
-
63
- function getFetch(options: RemoteEnvironmentClientOptions): RemoteFetch {
64
- const fetchImpl = options.fetch ?? globalThis.fetch;
65
- if (!fetchImpl) {
66
- throw new Error("Remote environments require a fetch implementation");
67
- }
68
- return fetchImpl.bind(globalThis) as RemoteFetch;
69
- }
70
-
71
- function normalizeBaseUrl(baseUrl?: string): string {
72
- return (baseUrl ?? "https://api.letta.com").replace(/\/$/, "");
73
- }
74
-
75
49
  function ensureOnline(
76
50
  environment: RemoteEnvironmentConnection,
77
51
  target: RemoteEnvironmentTarget,
@@ -95,47 +69,29 @@ function ensureOnline(
95
69
  };
96
70
  }
97
71
 
98
- async function parseJsonResponse<T>(response: Response): Promise<T> {
99
- const text = await response.text();
100
- const body = text ? JSON.parse(text) : null;
101
-
102
- if (!response.ok) {
103
- const message =
104
- body && typeof body === "object" && "message" in body
105
- ? String((body as { message: unknown }).message)
106
- : response.statusText;
107
- throw new Error(`Letta API request failed (${response.status}): ${message}`);
108
- }
109
-
110
- return body as T;
111
- }
112
-
113
72
  /**
114
- * Small Cloud API helper for resolving explicit Letta Code environments.
73
+ * Resolve explicit Letta Code environments through the generated Letta client.
115
74
  */
116
75
  export class RemoteEnvironmentClient {
117
- private readonly baseUrl: string;
118
- private readonly fetchImpl: RemoteFetch;
119
-
120
- constructor(private readonly options: RemoteEnvironmentClientOptions = {}) {
121
- this.baseUrl = normalizeBaseUrl(options.baseUrl);
122
- this.fetchImpl = getFetch(options);
123
- }
76
+ constructor(
77
+ options: RemoteEnvironmentClientOptions = {},
78
+ private readonly client: Letta = createCloudClient({
79
+ backend: "cloud",
80
+ apiBaseUrl: options.baseUrl,
81
+ apiKey: options.apiKey,
82
+ headers: options.headers,
83
+ fetch: options.fetch,
84
+ }),
85
+ ) {}
124
86
 
125
87
  async listEnvironments(): Promise<RemoteEnvironmentListResult> {
126
- const url = new URL(`${this.baseUrl}/v1/environments`);
127
- const response = await this.fetchImpl(url, {
128
- headers: createHeaders(this.options),
129
- });
130
- return parseJsonResponse<RemoteEnvironmentListResult>(response);
88
+ return await this.client.environments.list() as RemoteEnvironmentListResult;
131
89
  }
132
90
 
133
91
  async getEnvironmentByDeviceId(deviceId: string): Promise<RemoteEnvironmentConnection> {
134
- const response = await this.fetchImpl(
135
- `${this.baseUrl}/v1/environments/${encodeURIComponent(deviceId)}`,
136
- { headers: createHeaders(this.options) },
137
- );
138
- return parseJsonResponse<RemoteEnvironmentConnection>(response);
92
+ return await this.client.environments.retrieve(
93
+ deviceId,
94
+ ) as RemoteEnvironmentConnection;
139
95
  }
140
96
 
141
97
  async resolveEnvironment(target: RemoteEnvironmentTarget): Promise<ResolvedRemoteEnvironment> {
@@ -173,5 +129,4 @@ export class RemoteEnvironmentClient {
173
129
  }
174
130
  return ensureOnline(match, target);
175
131
  }
176
-
177
132
  }