@letta-ai/letta-agent-sdk 0.7.8 → 0.8.1

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 (63) hide show
  1. package/README.md +23 -0
  2. package/dist/agent-creation.d.ts +2 -1
  3. package/dist/agent-creation.d.ts.map +1 -1
  4. package/dist/app-server-management.d.ts +1 -0
  5. package/dist/app-server-management.d.ts.map +1 -1
  6. package/dist/app-server-session.d.ts.map +1 -1
  7. package/dist/client-base.d.ts +12 -0
  8. package/dist/client-base.d.ts.map +1 -1
  9. package/dist/client-entry.d.ts +1 -0
  10. package/dist/client-entry.d.ts.map +1 -1
  11. package/dist/client-entry.js +515 -2679
  12. package/dist/client-entry.js.map +17 -15
  13. package/dist/client.d.ts +4 -0
  14. package/dist/client.d.ts.map +1 -1
  15. package/dist/cloud-management.d.ts +3 -2
  16. package/dist/cloud-management.d.ts.map +1 -1
  17. package/dist/cloud-session.d.ts +4 -1
  18. package/dist/cloud-session.d.ts.map +1 -1
  19. package/dist/index.d.ts +6 -2
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +643 -2639
  22. package/dist/index.js.map +21 -18
  23. package/dist/management-types.d.ts +47 -1
  24. package/dist/management-types.d.ts.map +1 -1
  25. package/dist/management.d.ts +4 -2
  26. package/dist/management.d.ts.map +1 -1
  27. package/dist/query-types.d.ts +25 -0
  28. package/dist/query-types.d.ts.map +1 -0
  29. package/dist/query.d.ts +7 -0
  30. package/dist/query.d.ts.map +1 -0
  31. package/dist/remote-session-protocol.d.ts +11 -1
  32. package/dist/remote-session-protocol.d.ts.map +1 -1
  33. package/dist/remote-turn-coordinator.d.ts.map +1 -1
  34. package/dist/repository-types.d.ts +89 -0
  35. package/dist/repository-types.d.ts.map +1 -0
  36. package/dist/skill-loading.d.ts +84 -0
  37. package/dist/skill-loading.d.ts.map +1 -0
  38. package/dist/skill-node.d.ts +28 -0
  39. package/dist/skill-node.d.ts.map +1 -0
  40. package/dist/types.d.ts +17 -84
  41. package/dist/types.d.ts.map +1 -1
  42. package/package.json +2 -2
  43. package/src/agent-creation.ts +37 -1
  44. package/src/app-server-management.ts +8 -0
  45. package/src/app-server-session.ts +35 -2
  46. package/src/client-base.ts +138 -1
  47. package/src/client-entry.ts +1 -0
  48. package/src/client.ts +39 -0
  49. package/src/cloud-management.ts +60 -0
  50. package/src/cloud-session.ts +84 -16
  51. package/src/index.ts +18 -0
  52. package/src/local-app-server.ts +1 -1
  53. package/src/management-types.ts +58 -1
  54. package/src/management.ts +28 -1
  55. package/src/query-types.ts +40 -0
  56. package/src/query.ts +43 -0
  57. package/src/remote-client-session-core.ts +1 -1
  58. package/src/remote-session-protocol.ts +12 -1
  59. package/src/remote-turn-coordinator.ts +5 -1
  60. package/src/repository-types.ts +105 -0
  61. package/src/skill-loading.ts +194 -0
  62. package/src/skill-node.ts +139 -0
  63. package/src/types.ts +42 -100
@@ -6,6 +6,11 @@ import {
6
6
  type AppServerClient,
7
7
  } from "@letta-ai/letta-code/app-server-client";
8
8
  import { createAgentBody } from "./agent-creation.js";
9
+ import {
10
+ resolveSkillItems,
11
+ skillsHaveSupportFiles,
12
+ type SkillFilesPusher,
13
+ } from "./skill-loading.js";
9
14
  import {
10
15
  AppServerRuntimeController,
11
16
  agentToolNames,
@@ -78,7 +83,12 @@ type CloudRuntimeStartResponse = ProtocolMessage & {
78
83
  model?: string | null;
79
84
  model_settings?: Record<string, unknown> | null;
80
85
  }) | null;
81
- conversation: (Record<string, unknown> & { id?: string; agent_id?: string }) | null;
86
+ conversation: (Record<string, unknown> & {
87
+ id?: string;
88
+ agent_id?: string | null;
89
+ model?: string | null;
90
+ model_settings?: Record<string, unknown> | null;
91
+ }) | null;
82
92
  error?: string;
83
93
  };
84
94
 
@@ -143,7 +153,10 @@ export class CloudManagedSandboxExpiredError extends Error {
143
153
  }
144
154
  }
145
155
 
146
- type CloudSessionMode = Extract<RuntimeSessionMode, { kind: "session" }>;
156
+ type CloudSessionMode = Extract<
157
+ RuntimeSessionMode,
158
+ { kind: "session" } | { kind: "agent-free" }
159
+ >;
147
160
 
148
161
  function getWebSocketConstructor(
149
162
  websocketOverride?: LettaCodeSocketConstructor,
@@ -226,7 +239,7 @@ function environmentToRemoteTarget(
226
239
  function buildCloudStatusWebSocketUrl(params: {
227
240
  apiBaseUrl?: string;
228
241
  connectionId: string;
229
- agentId: string;
242
+ agentId: string | null;
230
243
  conversationId: string;
231
244
  apiKey?: string;
232
245
  authMode: "header" | "query";
@@ -240,7 +253,7 @@ function buildCloudStatusWebSocketUrl(params: {
240
253
  throw new Error(`Unsupported cloud apiBaseUrl protocol: ${base.protocol}`);
241
254
  }
242
255
  base.pathname = `/v1/environments/${encodeURIComponent(params.connectionId)}/status/ws`;
243
- base.searchParams.set("agentId", params.agentId);
256
+ if (params.agentId) base.searchParams.set("agentId", params.agentId);
244
257
  base.searchParams.set("conversationId", params.conversationId);
245
258
  base.searchParams.set("channel", "stream");
246
259
  if (params.authMode === "query" && params.apiKey) {
@@ -301,12 +314,38 @@ function externalToolsByName(tools: AnyAgentTool[] | undefined): Map<string, Any
301
314
  export async function createCloudAgent(
302
315
  client: Letta,
303
316
  agentOptions: CreateAgentOptions,
317
+ pushSkillSupportFiles?: SkillFilesPusher,
304
318
  ): Promise<string> {
305
- const body = await createAgentBody(agentOptions);
319
+ // Directory paths were already resolved to inline skills by the client
320
+ // facade; this validates inline skills passed directly.
321
+ const skills = await resolveSkillItems(agentOptions.skills);
322
+ const hasSupportFiles = skillsHaveSupportFiles(skills);
323
+ if (hasSupportFiles && pushSkillSupportFiles === undefined) {
324
+ throw new Error(
325
+ "Skill support files (scripts/, references/) require the Node.js " +
326
+ "package root; the portable client seeds SKILL.md-only skills.",
327
+ );
328
+ }
329
+ const body = await createAgentBody(agentOptions, skills);
306
330
  const agent = await client.agents.create(body as AgentCreateParams);
307
331
  if (typeof agent.id !== "string" || agent.id.length === 0) {
308
332
  throw new Error("Cloud create agent response did not include an agent id.");
309
333
  }
334
+ // SKILL.md contents rode the create request as memory blocks; support
335
+ // files (scripts, references) have no block representation and go to the
336
+ // agent's memory git repo in one follow-up commit.
337
+ if (hasSupportFiles && pushSkillSupportFiles) {
338
+ if (typeof client.apiKey !== "string" || client.apiKey.length === 0) {
339
+ throw new Error(
340
+ `Agent ${agent.id} was created, but skill support files need an API ` +
341
+ "key to push to the agent's memory repo and none is configured.",
342
+ );
343
+ }
344
+ await pushSkillSupportFiles(
345
+ { apiBaseUrl: client.baseURL, apiKey: client.apiKey, agentId: agent.id },
346
+ skills,
347
+ );
348
+ }
310
349
  return agent.id;
311
350
  }
312
351
 
@@ -364,10 +403,12 @@ export class CloudEnvironmentSession extends RemoteClientSessionCore {
364
403
  const resolved = await this.resolveRuntime();
365
404
  const connection = await this.resolveConnectionForRuntime(resolved.runtime).catch(
366
405
  async (error: unknown) => {
367
- await this.cleanupSessionRepositories(
368
- resolved.runtime.agent_id,
369
- resolved.runtime.conversation_id,
370
- );
406
+ if (resolved.runtime.agent_id) {
407
+ await this.cleanupSessionRepositories(
408
+ resolved.runtime.agent_id,
409
+ resolved.runtime.conversation_id,
410
+ );
411
+ }
371
412
  throw error;
372
413
  },
373
414
  );
@@ -393,10 +434,12 @@ export class CloudEnvironmentSession extends RemoteClientSessionCore {
393
434
  } catch (error) {
394
435
  await this.closeMcpBridge();
395
436
  await this.cleanupManagedSandbox();
396
- await this.cleanupSessionRepositories(
397
- resolved.runtime.agent_id,
398
- resolved.runtime.conversation_id,
399
- );
437
+ if (resolved.runtime.agent_id) {
438
+ await this.cleanupSessionRepositories(
439
+ resolved.runtime.agent_id,
440
+ resolved.runtime.conversation_id,
441
+ );
442
+ }
400
443
  throw error;
401
444
  }
402
445
  }
@@ -438,8 +481,16 @@ export class CloudEnvironmentSession extends RemoteClientSessionCore {
438
481
  clientToolset,
439
482
  ),
440
483
  runtime: response.runtime,
441
- model: typeof response.agent?.model === "string" ? response.agent.model : "",
442
- modelSettings: response.agent?.model_settings ?? null,
484
+ model:
485
+ typeof response.agent?.model === "string"
486
+ ? response.agent.model
487
+ : typeof response.conversation?.model === "string"
488
+ ? response.conversation.model
489
+ : "",
490
+ modelSettings:
491
+ response.agent?.model_settings ??
492
+ response.conversation?.model_settings ??
493
+ null,
443
494
  ...(availableTools !== undefined ? { tools: availableTools } : {}),
444
495
  ...(skillSources !== undefined ? { skillSources: [...skillSources] } : {}),
445
496
  };
@@ -590,11 +641,11 @@ export class CloudEnvironmentSession extends RemoteClientSessionCore {
590
641
  name: SDK_AGENT_ORIGIN,
591
642
  title: "Letta Agent SDK",
592
643
  },
593
- agent_id: runtime.agent_id,
594
644
  conversation_id: runtime.conversation_id,
595
645
  recover_approvals: false,
596
646
  force_device_status: true,
597
647
  };
648
+ if (runtime.agent_id) command.agent_id = runtime.agent_id;
598
649
 
599
650
  const mode = mapPermissionMode(options.permissionMode);
600
651
  if (mode) command.mode = mode;
@@ -676,6 +727,18 @@ export class CloudEnvironmentSession extends RemoteClientSessionCore {
676
727
  }
677
728
 
678
729
  private async resolveRuntime(): Promise<{ runtime: RuntimeScope }> {
730
+ if (this.cloudMode.kind === "agent-free") {
731
+ if (!this.cloudMode.conversationId) {
732
+ throw new Error("Cloud agent-free sessions require a conversation id.");
733
+ }
734
+ return {
735
+ runtime: {
736
+ agent_id: null,
737
+ conversation_id: this.cloudMode.conversationId,
738
+ },
739
+ };
740
+ }
741
+
679
742
  let agentId = this.cloudMode.agentId;
680
743
  let conversationId = this.cloudMode.conversationId;
681
744
 
@@ -890,6 +953,11 @@ export class CloudEnvironmentSession extends RemoteClientSessionCore {
890
953
  private async createManagedSandboxConnection(
891
954
  runtime: RuntimeScope,
892
955
  ): Promise<ResolvedCloudConnection> {
956
+ if (!runtime.agent_id) {
957
+ throw new Error(
958
+ "Agent-free queries require an explicit Cloud computer; managed sandboxes are agent-scoped.",
959
+ );
960
+ }
893
961
  // Scope the managed sandbox to the conversation when one exists: the
894
962
  // server treats create-with-conversationId as create-or-resume, so
895
963
  // sessions of the same conversation share one sandbox and sessions of
package/src/index.ts CHANGED
@@ -39,6 +39,7 @@ import type {
39
39
  SDKResultMessage,
40
40
  SendMessage,
41
41
  } from "./types.js";
42
+ import type { Query, QueryParams } from "./query-types.js";
42
43
  import { validateCreateSessionOptions, validateCreateAgentOptions } from "./validation.js";
43
44
 
44
45
  // Re-export types
@@ -94,6 +95,8 @@ export type {
94
95
  SessionDiffHunk,
95
96
  SessionDiffPreview,
96
97
  SkillSource,
98
+ SkillItem,
99
+ AgentSkill,
97
100
  DreamingOptions,
98
101
  SessionDreamingOptions,
99
102
  DreamingTrigger,
@@ -152,6 +155,11 @@ export type {
152
155
  McpSseServerConfig,
153
156
  McpServers,
154
157
  } from "./types.js";
158
+ export type {
159
+ AgentFreeQueryOptions,
160
+ Query,
161
+ QueryParams,
162
+ } from "./query-types.js";
155
163
  export type * from "./sandbox-files.js";
156
164
  export type {
157
165
  AgentRepositoriesClient,
@@ -174,6 +182,8 @@ export type {
174
182
  ConversationMessagesResult,
175
183
  DetachAgentRepositoryOptions,
176
184
  AgentRepositoryPermissions,
185
+ EnqueueMessageOptions,
186
+ EnqueueMessageResult,
177
187
  } from "./management-types.js";
178
188
 
179
189
  export { RepositoriesClient } from "./repositories.js";
@@ -323,6 +333,14 @@ export async function prompt(
323
333
  }
324
334
  }
325
335
 
336
+ /** Run an agent-free query in a new ephemeral conversation. */
337
+ export function query(params: QueryParams): Query {
338
+ return new LettaAgentClient({
339
+ backend: "local",
340
+ appServer: { harnessBackend: "api" },
341
+ }).query(params);
342
+ }
343
+
326
344
  // ═══════════════════════════════════════════════════════════════
327
345
  // SESSIONLESS APIs
328
346
  // ═══════════════════════════════════════════════════════════════
@@ -99,7 +99,7 @@ export function buildLocalAppServerArgs(
99
99
  return [
100
100
  cliPath,
101
101
  ...(options.backend !== undefined ? ["--backend", options.backend] : []),
102
- "app-server",
102
+ "server",
103
103
  "--listen",
104
104
  options.listen ?? DEFAULT_LISTEN_URL,
105
105
  ];
@@ -12,7 +12,12 @@ import type {
12
12
  ConversationUpdateParams,
13
13
  } from "@letta-ai/letta-client/resources/conversations/conversations";
14
14
  import type { MessageListParams } from "@letta-ai/letta-client/resources/conversations/messages";
15
- import type { ListMessagesResult, ListModelsResult } from "./types.js";
15
+ import type {
16
+ ListMessagesResult,
17
+ ListModelsResult,
18
+ PermissionMode,
19
+ SendMessage,
20
+ } from "./types.js";
16
21
 
17
22
  /** Agent state returned by either the Cloud API or Letta Code app-server. */
18
23
  export type LettaAgent = AgentState;
@@ -100,6 +105,34 @@ export interface ConversationMessagesOptions {
100
105
 
101
106
  export type ConversationMessagesResult = ListMessagesResult;
102
107
 
108
+ export interface EnqueueMessageOptions {
109
+ /**
110
+ * Required when `conversationId` is `"default"`; identifies the agent whose
111
+ * default conversation receives the message. Ignored otherwise.
112
+ */
113
+ agentId?: string;
114
+ /**
115
+ * Correlation id for this message. Reuse the same value when retrying an
116
+ * enqueue so the listener can deduplicate delivery; the SDK generates one
117
+ * when omitted. Matches the `otid` contract of `session.send()`.
118
+ */
119
+ clientMessageId?: string;
120
+ /** Permission mode the listener applies before the turn when the runtime is cold. */
121
+ permissionMode?: PermissionMode;
122
+ /** Working directory for the runtime; `null` resets to the listener boot cwd. */
123
+ workingDirectory?: string | null;
124
+ }
125
+
126
+ /** The Cloud API's 202 acceptance of an enqueued message. */
127
+ export interface EnqueueMessageResult {
128
+ /** The correlation id the message was accepted under (caller-supplied or SDK-generated). */
129
+ clientMessageId: string;
130
+ /** Temporal workflow id of the conversation queue that owns delivery. */
131
+ workflowId: string;
132
+ /** Super run id tracking the message from queue through delivery and completion. */
133
+ superRunId: string;
134
+ }
135
+
103
136
  export type AgentRepositoryPermissions = "read" | "read_write";
104
137
  export type AgentRepositoryRecompileTarget = "default" | false;
105
138
 
@@ -193,4 +226,28 @@ export interface ConversationsClient {
193
226
  conversationId: string,
194
227
  options?: ConversationMessagesOptions,
195
228
  ): Promise<ConversationMessagesResult>;
229
+ /**
230
+ * Hand a user message to the server for delivery instead of sending it over
231
+ * a session. Cloud backend only.
232
+ *
233
+ * The returned promise resolves once the Cloud API has durably accepted the
234
+ * message (HTTP 202): from that point the server owns delivery. It ensures
235
+ * or resumes the conversation's sandbox, delivers the message to the
236
+ * listener with acknowledgement and retries, and tracks the whole lifecycle
237
+ * as a super run. The caller may exit immediately; the turn runs
238
+ * server-side. This is the send path chat.letta.com uses.
239
+ *
240
+ * Use `enqueue()` when you cannot hold a connection open for the length of
241
+ * a turn, such as serverless webhook handlers. Use a session's `send()` and
242
+ * `stream()` when you want to stream the turn's output; a session delivers
243
+ * input over its open socket and does not survive the caller.
244
+ *
245
+ * Pass `"default"` as `conversationId` with `options.agentId` to target an
246
+ * agent's default conversation.
247
+ */
248
+ enqueue(
249
+ conversationId: string,
250
+ message: SendMessage,
251
+ options?: EnqueueMessageOptions,
252
+ ): Promise<EnqueueMessageResult>;
196
253
  }
package/src/management.ts CHANGED
@@ -15,6 +15,8 @@ import type {
15
15
  ConversationsClient,
16
16
  ConversationMessagesResult,
17
17
  CreateConversationOptions,
18
+ EnqueueMessageOptions,
19
+ EnqueueMessageResult,
18
20
  ForkConversationOptions,
19
21
  LettaAgent,
20
22
  LettaConversation,
@@ -25,7 +27,7 @@ import type {
25
27
  UpdateConversationOptions,
26
28
  ConversationMessagesOptions,
27
29
  } from "./management-types.js";
28
- import type { ListModelsResult } from "./types.js";
30
+ import type { ListModelsResult, SendMessage } from "./types.js";
29
31
 
30
32
  export interface ManagementTransport {
31
33
  listAgents(query: AgentListParams): Promise<LettaAgent[]>;
@@ -57,6 +59,12 @@ export interface ManagementTransport {
57
59
  conversationId: string,
58
60
  query: MessageListParams,
59
61
  ): Promise<ConversationMessagesResult>;
62
+ /** Cloud only. Non-cloud transports reject with a backend error. */
63
+ enqueueConversationMessage(
64
+ conversationId: string,
65
+ message: SendMessage,
66
+ options: EnqueueMessageOptions,
67
+ ): Promise<EnqueueMessageResult>;
60
68
  }
61
69
 
62
70
  type TransportProvider = () => ManagementTransport;
@@ -227,5 +235,24 @@ export function createConversationsClient(
227
235
  conversationId,
228
236
  conversationMessagesQuery(options),
229
237
  ),
238
+ enqueue: async (conversationId, message, options = {}) => {
239
+ assertNonEmptyId(conversationId, "conversation id");
240
+ if (conversationId.trim() === "default" && !options.agentId) {
241
+ throw new Error(
242
+ 'enqueue("default", ...) requires options.agentId to identify the agent.',
243
+ );
244
+ }
245
+ if (options.agentId !== undefined) {
246
+ assertNonEmptyId(options.agentId, "agent id");
247
+ }
248
+ if (options.clientMessageId !== undefined) {
249
+ assertNonEmptyId(options.clientMessageId, "client message id");
250
+ }
251
+ return await transport().enqueueConversationMessage(
252
+ conversationId,
253
+ message,
254
+ options,
255
+ );
256
+ },
230
257
  };
231
258
  }
@@ -0,0 +1,40 @@
1
+ import type {
2
+ LettaCodeClientSessionOptions,
3
+ SDKMessage,
4
+ SendMessage,
5
+ } from "./types.js";
6
+
7
+ /** Options for an agent-free query backed by an ephemeral conversation. */
8
+ export interface AgentFreeQueryOptions
9
+ extends Omit<
10
+ LettaCodeClientSessionOptions,
11
+ | "model"
12
+ | "reasoningEffort"
13
+ | "stateless"
14
+ | "dreaming"
15
+ | "resources"
16
+ | "filesystemConfinement"
17
+ > {
18
+ /** Model handle persisted on the ephemeral conversation. */
19
+ model: string;
20
+ /** Complete system prompt for the ephemeral conversation. */
21
+ system: string;
22
+ /** Provider model settings persisted on the ephemeral conversation. */
23
+ modelSettings?: Record<string, unknown>;
24
+ /** Optional context-window limit persisted on the ephemeral conversation. */
25
+ contextWindowLimit?: number | null;
26
+ }
27
+
28
+ /** Input accepted by query(). */
29
+ export interface QueryParams {
30
+ prompt: SendMessage;
31
+ options: AgentFreeQueryOptions;
32
+ }
33
+
34
+ /** Stream returned by query(), with controls for long-running execution. */
35
+ export interface Query extends AsyncGenerator<SDKMessage, void, unknown> {
36
+ /** Interrupt the active turn without closing the query session. */
37
+ interrupt(): Promise<void>;
38
+ /** Close the query and release its underlying session. */
39
+ close(): void;
40
+ }
package/src/query.ts ADDED
@@ -0,0 +1,43 @@
1
+ import type { AgentFreeQueryOptions, Query, QueryParams } from "./query-types.js";
2
+ import type { LettaCodeSession, SDKMessage } from "./types.js";
3
+
4
+ type CreateAgentFreeSession = (
5
+ options: AgentFreeQueryOptions,
6
+ ) => Promise<LettaCodeSession>;
7
+
8
+ /** Create one agent-free ephemeral conversation and stream its query result. */
9
+ export function createQuery(
10
+ createSession: CreateAgentFreeSession,
11
+ params: QueryParams,
12
+ ): Query {
13
+ let session: LettaCodeSession | null = null;
14
+ let closed = false;
15
+
16
+ const iterator = (async function* runQuery(): AsyncGenerator<
17
+ SDKMessage,
18
+ void,
19
+ unknown
20
+ > {
21
+ try {
22
+ session = await createSession(params.options);
23
+ if (closed) return;
24
+ await session.send(params.prompt);
25
+ yield* session.stream();
26
+ } finally {
27
+ closed = true;
28
+ session?.close();
29
+ }
30
+ })();
31
+
32
+ return Object.assign(iterator, {
33
+ async interrupt(): Promise<void> {
34
+ await session?.abort();
35
+ },
36
+ close(): void {
37
+ if (closed) return;
38
+ closed = true;
39
+ session?.close();
40
+ void iterator.return();
41
+ },
42
+ });
43
+ }
@@ -166,7 +166,7 @@ export abstract class RemoteClientSessionCore implements LettaCodeSession {
166
166
  this.runtime = init.runtime;
167
167
  this._agentId = init.runtime.agent_id;
168
168
  this._conversationId = init.runtime.conversation_id;
169
- this._sessionId = `${init.runtime.agent_id}:${init.runtime.conversation_id}`;
169
+ this._sessionId = init.runtime.agent_id ? `${init.runtime.agent_id}:${init.runtime.conversation_id}` : init.runtime.conversation_id;
170
170
  this._modelSettings = init.modelSettings ?? null;
171
171
  this._model =
172
172
  typeof init.model === "string"
@@ -23,7 +23,7 @@ import type {
23
23
  } from "./types.js";
24
24
 
25
25
  export type RuntimeScope = {
26
- agent_id: string;
26
+ agent_id: string | null;
27
27
  conversation_id: string;
28
28
  };
29
29
 
@@ -35,6 +35,17 @@ export type ProtocolMessage = Record<string, unknown> & {
35
35
 
36
36
  export type RuntimeSessionMode =
37
37
  | { kind: "create-agent"; options: CreateAgentOptions }
38
+ | {
39
+ kind: "agent-free";
40
+ conversationId?: string;
41
+ createConversation?: {
42
+ model: string;
43
+ system: string;
44
+ modelSettings?: Record<string, unknown>;
45
+ contextWindowLimit?: number | null;
46
+ };
47
+ options: LettaCodeClientSessionOptions;
48
+ }
38
49
  | {
39
50
  kind: "session";
40
51
  agentId?: string;
@@ -614,12 +614,16 @@ export class RemoteTurnCoordinator {
614
614
  const errorCode = approvalConflict
615
615
  ? "approval_conflict"
616
616
  : (turn.errorCode ?? toSdkErrorCode(stopReason));
617
+ const publicError =
618
+ errorCode && errorCode !== "error"
619
+ ? errorCode
620
+ : (turn.detail ?? stopReason ?? "error");
617
621
 
618
622
  return {
619
623
  type: "result",
620
624
  success,
621
625
  result: success ? tracker?.assistantText || undefined : undefined,
622
- error: success ? undefined : (errorCode ?? stopReason ?? "error"),
626
+ error: success ? undefined : publicError,
623
627
  errorCode: success ? undefined : (errorCode ?? "error"),
624
628
  approvalConflict: approvalConflict || undefined,
625
629
  recoverable: approvalConflict
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Letta-hosted repository API types, used by RepositoriesClient and
3
+ * AgentRepositoriesClient. Kept beside their implementation
4
+ * (repositories.ts) and re-exported from types.ts.
5
+ */
6
+
7
+ export interface Repository {
8
+ id: string;
9
+ name: string;
10
+ createdAt: string;
11
+ updatedAt: string;
12
+ }
13
+
14
+ export interface CreateRepositoryParams {
15
+ name: string;
16
+ }
17
+
18
+ export interface ListRepositoriesParams {
19
+ limit?: number;
20
+ offset?: number;
21
+ }
22
+
23
+ export interface ListRepositoriesResult {
24
+ repositories: Repository[];
25
+ hasNextPage: boolean;
26
+ }
27
+
28
+ export interface RepositoryResource {
29
+ type: "repository";
30
+ repositoryId: string;
31
+ /**
32
+ * Whether to trigger a system-prompt recompile after attaching (and after
33
+ * detaching on cleanup) so the session's conversation does not retain
34
+ * stale repository projections. Defaults to `true`.
35
+ */
36
+ recompile?: boolean;
37
+ }
38
+
39
+ export interface RepositoryFileEntry {
40
+ path: string;
41
+ type: "file" | "directory";
42
+ }
43
+
44
+ export interface ListRepositoryFilesParams {
45
+ pathPrefix?: string;
46
+ depth?: number;
47
+ ref?: string;
48
+ }
49
+
50
+ export interface ListRepositoryFilesResult {
51
+ files: RepositoryFileEntry[];
52
+ ref: string;
53
+ }
54
+
55
+ export interface CreateRepositoryFileParams {
56
+ path: string;
57
+ content: string;
58
+ }
59
+
60
+ export interface RepositoryFile {
61
+ path: string;
62
+ content: string;
63
+ contentSha256: string;
64
+ ref?: string;
65
+ }
66
+
67
+ export interface UpdateRepositoryFileParams {
68
+ path: string;
69
+ content?: string;
70
+ newPath?: string;
71
+ precondition?: {
72
+ contentSha256: string;
73
+ };
74
+ }
75
+
76
+ export interface RepositoryFileMutationResult {
77
+ path: string;
78
+ contentSha256: string;
79
+ commitSha: string;
80
+ }
81
+
82
+ export interface DeleteRepositoryFileParams {
83
+ path: string;
84
+ }
85
+
86
+ export interface DeleteRepositoryFileResult {
87
+ success: boolean;
88
+ commitSha: string;
89
+ }
90
+
91
+ export interface RepositoryVersion {
92
+ sha: string;
93
+ message: string;
94
+ timestamp: string;
95
+ author_name: string | null;
96
+ }
97
+
98
+ export interface ListRepositoryVersionsParams {
99
+ path?: string;
100
+ limit?: number;
101
+ }
102
+
103
+ export interface GetRepositoryVersionParams {
104
+ path: string;
105
+ }