@letta-ai/letta-agent-sdk 0.6.2 → 0.7.0

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 (44) hide show
  1. package/AGENTS.md +42 -0
  2. package/README.md +101 -1
  3. package/dist/app-server-session.d.ts.map +1 -1
  4. package/dist/client-base.d.ts +7 -0
  5. package/dist/client-base.d.ts.map +1 -1
  6. package/dist/client-entry.d.ts +3 -0
  7. package/dist/client-entry.d.ts.map +1 -1
  8. package/dist/client-entry.js +816 -138
  9. package/dist/client-entry.js.map +16 -14
  10. package/dist/cloud-session.d.ts +8 -1
  11. package/dist/cloud-session.d.ts.map +1 -1
  12. package/dist/computers.d.ts +67 -0
  13. package/dist/computers.d.ts.map +1 -0
  14. package/dist/index.d.ts +4 -1
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +817 -139
  17. package/dist/index.js.map +17 -15
  18. package/dist/remote-client-session-core.d.ts +13 -16
  19. package/dist/remote-client-session-core.d.ts.map +1 -1
  20. package/dist/remote-session-protocol.d.ts +11 -0
  21. package/dist/remote-session-protocol.d.ts.map +1 -1
  22. package/dist/remote-turn-coordinator.d.ts +6 -1
  23. package/dist/remote-turn-coordinator.d.ts.map +1 -1
  24. package/dist/remote.d.ts +6 -1
  25. package/dist/remote.d.ts.map +1 -1
  26. package/dist/transcript-accumulator.d.ts +133 -0
  27. package/dist/transcript-accumulator.d.ts.map +1 -0
  28. package/dist/types.d.ts +41 -21
  29. package/dist/types.d.ts.map +1 -1
  30. package/dist/validation.d.ts.map +1 -1
  31. package/package.json +2 -2
  32. package/src/app-server-session.ts +9 -0
  33. package/src/client-base.ts +66 -19
  34. package/src/client-entry.ts +23 -0
  35. package/src/cloud-session.ts +112 -45
  36. package/src/computers.ts +132 -0
  37. package/src/index.ts +24 -0
  38. package/src/remote-client-session-core.ts +103 -27
  39. package/src/remote-session-protocol.ts +24 -0
  40. package/src/remote-turn-coordinator.ts +11 -2
  41. package/src/remote.ts +41 -12
  42. package/src/transcript-accumulator.ts +823 -0
  43. package/src/types.ts +43 -18
  44. package/src/validation.ts +16 -0
@@ -8,6 +8,11 @@ import {
8
8
  } from "./app-server-session.js";
9
9
  import { CloudManagementTransport } from "./cloud-management.js";
10
10
  import { createCloudClient } from "./cloud-client.js";
11
+ import {
12
+ ComputersClientImpl,
13
+ type ComputerSelector,
14
+ type ComputersClient,
15
+ } from "./computers.js";
11
16
  import {
12
17
  CloudEnvironmentSession,
13
18
  assertCloudSessionOptionsSupported,
@@ -55,19 +60,21 @@ function isLettaCodeBackend(value: string): value is LettaCodeBackend {
55
60
  return VALID_BACKENDS.has(value as LettaCodeBackend);
56
61
  }
57
62
 
58
- function getOptionsEnvironment(
63
+ function getOptionsComputer(
59
64
  options: LettaCodeClientOptions,
60
- ): LettaCodeEnvironment | undefined {
61
- if ("environment" in options) {
62
- return options.environment;
65
+ ): ComputerSelector | undefined {
66
+ if (!("computer" in options) && !("environment" in options)) return undefined;
67
+ if (options.computer !== undefined && options.environment !== undefined) {
68
+ throw new Error('Specify either "computer" or deprecated "environment", not both.');
63
69
  }
64
- return undefined;
70
+ return options.computer ?? options.environment;
65
71
  }
66
72
 
67
73
  function stripCloudExecutionOptions(
68
74
  options: LettaCodeClientSessionOptions,
69
75
  ): CreateSessionOptions {
70
76
  const sessionOptions = { ...options };
77
+ delete sessionOptions.computer;
71
78
  delete sessionOptions.environment;
72
79
  delete sessionOptions.sandbox;
73
80
  delete sessionOptions.filesystemConfinement;
@@ -102,12 +109,15 @@ type OneShotSession = LettaCodeSession & {
102
109
  */
103
110
  export class LettaAgentClientBase {
104
111
  readonly backend: LettaCodeBackend;
112
+ readonly computer: ComputerSelector | undefined;
113
+ /** @deprecated Use `computer`. */
105
114
  readonly environment: LettaCodeEnvironment | undefined;
106
115
  readonly agents: AgentsClient;
107
116
  readonly conversations: ConversationsClient;
108
117
  readonly models: ModelsClient;
109
118
  protected readonly options: LettaCodeClientOptions;
110
119
  private repositoriesClient: RepositoriesClient | null = null;
120
+ private computersClient: ComputersClientImpl | null = null;
111
121
  private agentRepositoriesClient: AgentRepositoriesClient | null = null;
112
122
  private cloudClient: Letta | null = null;
113
123
  private managementTransport: ManagementTransport | null = null;
@@ -121,7 +131,8 @@ export class LettaAgentClientBase {
121
131
  }
122
132
 
123
133
  this.backend = backend;
124
- this.environment = getOptionsEnvironment(options);
134
+ this.computer = getOptionsComputer(options);
135
+ this.environment = this.computer;
125
136
  this.options = options;
126
137
  this.agents = createAgentsClient(
127
138
  () => this.getManagementTransport(),
@@ -132,14 +143,20 @@ export class LettaAgentClientBase {
132
143
  );
133
144
  this.models = createModelsClient(() => this.getManagementTransport());
134
145
 
135
- if (this.backend === "local" && this.environment !== undefined) {
146
+ if (this.backend === "local" && this.computer !== undefined) {
147
+ const field = "computer" in options && options.computer !== undefined
148
+ ? "computer"
149
+ : "environment";
136
150
  throw new Error(
137
- 'LettaAgentClient environment is only valid with backend: "cloud".',
151
+ `LettaAgentClient ${field} is only valid with backend: "cloud".`,
138
152
  );
139
153
  }
140
- if (this.backend === "remote" && this.environment !== undefined) {
154
+ if (this.backend === "remote" && this.computer !== undefined) {
155
+ const field = "computer" in options && options.computer !== undefined
156
+ ? "computer"
157
+ : "environment";
141
158
  throw new Error(
142
- 'LettaAgentClient environment is only valid with backend: "cloud"; remote url selects the app-server runtime.',
159
+ `LettaAgentClient ${field} is only valid with backend: "cloud"; remote url selects the app-server runtime.`,
143
160
  );
144
161
  }
145
162
  if (this.backend !== "cloud" && (options as { sandbox?: unknown }).sandbox !== undefined) {
@@ -196,6 +213,11 @@ export class LettaAgentClientBase {
196
213
  return this.getRepositoriesClient();
197
214
  }
198
215
 
216
+ /** Discover and resolve computers registered with this Letta Cloud account. */
217
+ get computers(): ComputersClient {
218
+ return this.getComputersClient();
219
+ }
220
+
199
221
  async createAgent(options: CreateAgentOptions = {}): Promise<string> {
200
222
  if (hasCreateAgentEnvironment(options)) {
201
223
  throw new Error(
@@ -336,6 +358,11 @@ export class LettaAgentClientBase {
336
358
  action: string,
337
359
  options: LettaCodeClientSessionOptions,
338
360
  ): void {
361
+ if (options.computer !== undefined && options.environment !== undefined) {
362
+ throw new Error(
363
+ `${action}() cannot specify both computer and deprecated environment.`,
364
+ );
365
+ }
339
366
  if (
340
367
  options.filesystemConfinement !== undefined &&
341
368
  options.filesystemConfinement !== "memory"
@@ -344,11 +371,13 @@ export class LettaAgentClientBase {
344
371
  `Invalid filesystemConfinement '${String(options.filesystemConfinement)}'. Valid value: memory.`,
345
372
  );
346
373
  }
347
- const effectiveEnvironment = options.environment ?? this.environment;
374
+ const effectiveComputer =
375
+ options.computer ?? options.environment ?? this.computer;
348
376
  if (this.backend === "local") {
349
- if (effectiveEnvironment !== undefined) {
377
+ if (effectiveComputer !== undefined) {
378
+ const field = options.computer !== undefined ? "computer" : "environment";
350
379
  throw new Error(
351
- `${action}() environment overrides are only valid with backend: "cloud".`,
380
+ `${action}() ${field} overrides are only valid with backend: "cloud".`,
352
381
  );
353
382
  }
354
383
  if (options.sandbox !== undefined) {
@@ -374,9 +403,10 @@ export class LettaAgentClientBase {
374
403
  `${action}() filesystemConfinement requires an SDK-owned local app-server process.`,
375
404
  );
376
405
  }
377
- if (options.environment !== undefined) {
406
+ if (options.computer !== undefined || options.environment !== undefined) {
407
+ const field = options.computer !== undefined ? "computer" : "environment";
378
408
  throw new Error(
379
- `${action}() environment overrides are only valid with backend: "cloud"; remote url selects the app-server runtime.`,
409
+ `${action}() ${field} overrides are only valid with backend: "cloud"; remote url selects the app-server runtime.`,
380
410
  );
381
411
  }
382
412
  if (options.sandbox !== undefined) {
@@ -394,11 +424,20 @@ export class LettaAgentClientBase {
394
424
  );
395
425
  }
396
426
  const cloudOptions = this.cloudOptions();
397
- if (cloudOptions.environment !== undefined && options.sandbox !== undefined) {
398
- throw new Error(`Letta Cloud ${action}() cannot specify sandbox options when the client has a default environment.`);
427
+ if (this.computer !== undefined && options.sandbox !== undefined) {
428
+ const field = cloudOptions.computer !== undefined
429
+ ? "computer"
430
+ : "environment";
431
+ throw new Error(`Letta Cloud ${action}() cannot specify sandbox options when the client has a default ${field}.`);
399
432
  }
400
- if (cloudOptions.sandbox !== undefined && options.environment !== undefined) {
401
- throw new Error(`Letta Cloud ${action}() cannot specify an environment when the client has default sandbox options.`);
433
+ if (
434
+ cloudOptions.sandbox !== undefined &&
435
+ (options.computer !== undefined || options.environment !== undefined)
436
+ ) {
437
+ const field = options.computer !== undefined
438
+ ? "a computer"
439
+ : "an environment";
440
+ throw new Error(`Letta Cloud ${action}() cannot specify ${field} when the client has default sandbox options.`);
402
441
  }
403
442
  assertCloudSessionOptionsSupported(action, options);
404
443
  return;
@@ -447,6 +486,14 @@ export class LettaAgentClientBase {
447
486
  return this.remoteOptions();
448
487
  }
449
488
 
489
+ private getComputersClient(): ComputersClientImpl {
490
+ if (this.backend !== "cloud") {
491
+ throw new Error('client.computers is only available with backend: "cloud".');
492
+ }
493
+ this.computersClient ??= new ComputersClientImpl(this.getCloudClient());
494
+ return this.computersClient;
495
+ }
496
+
450
497
  private getRepositoriesClient(): RepositoriesClient {
451
498
  if (this.backend !== "cloud") {
452
499
  throw new Error('client.repositories is only available with backend: "cloud".');
@@ -25,7 +25,30 @@ export class LettaAgentClient extends LettaAgentClientBase {
25
25
 
26
26
  export { CloudManagedSandboxExpiredError } from "./cloud-session.js";
27
27
  export { RepositoriesClient } from "./repositories.js";
28
+ export type {
29
+ Computer,
30
+ ComputerMetadata,
31
+ ComputerSelector,
32
+ ComputersClient,
33
+ ListComputersOptions,
34
+ ListComputersResult,
35
+ ResolvedComputer,
36
+ } from "./computers.js";
28
37
  export type * from "./management-types.js";
29
38
  export { extractStreamTextDelta } from "./stream-events.js";
39
+ export { createTranscriptAccumulator } from "./transcript-accumulator.js";
40
+ export type {
41
+ TranscriptAccumulator,
42
+ TranscriptHistoryPage,
43
+ TranscriptRebaseOptions,
44
+ TranscriptRow,
45
+ TranscriptRowIdentity,
46
+ TranscriptRowKind,
47
+ TranscriptTextKind,
48
+ TranscriptTextRow,
49
+ TranscriptToolCallRow,
50
+ TranscriptToolCallStatus,
51
+ TranscriptToolResult,
52
+ } from "./transcript-accumulator.js";
30
53
  export { createReactNativeWebSocketConstructor } from "./websocket.js";
31
54
  export type * from "./types.js";
@@ -171,8 +171,19 @@ function validatePositiveInteger(value: number | undefined, name: string): void
171
171
  export function validateCloudClientOptions(options: LettaCodeCloudClientOptions): void {
172
172
  validatePositiveInteger(options.requestTimeoutMs, "requestTimeoutMs");
173
173
  validateCloudSandboxOptions(options.sandbox, "sandbox");
174
- if (options.environment !== undefined && options.sandbox !== undefined) {
175
- throw new Error("Letta Cloud sessions cannot specify both environment and sandbox options.");
174
+ if (options.computer !== undefined && options.environment !== undefined) {
175
+ throw new Error(
176
+ "Letta Cloud clients cannot specify both computer and deprecated environment.",
177
+ );
178
+ }
179
+ if (
180
+ (options.computer !== undefined || options.environment !== undefined) &&
181
+ options.sandbox !== undefined
182
+ ) {
183
+ const field = options.computer !== undefined ? "computer" : "environment";
184
+ throw new Error(
185
+ `Letta Cloud sessions cannot specify both ${field} and sandbox options.`,
186
+ );
176
187
  }
177
188
  if (
178
189
  options.webSocketAuth !== undefined &&
@@ -346,12 +357,44 @@ export class CloudEnvironmentSession extends RemoteClientSessionCore {
346
357
  );
347
358
  this.connectionId = connection.connectionId;
348
359
 
360
+ const options = this.currentOptions();
361
+ this.mcpBridge = await connectMcpServers(
362
+ "mcpServers" in options ? options.mcpServers : undefined,
363
+ {
364
+ cwd: options.cwd,
365
+ reservedToolNames: this.externalTools.keys(),
366
+ },
367
+ );
368
+ for (const tool of this.mcpBridge.tools) {
369
+ this.externalTools.set(tool.name, tool);
370
+ }
371
+
372
+ try {
373
+ return await this.openCloudTransport(
374
+ resolved.runtime,
375
+ connection.connectionId,
376
+ );
377
+ } catch (error) {
378
+ await this.closeMcpBridge();
379
+ await this.cleanupManagedSandbox();
380
+ await this.cleanupSessionRepositories(
381
+ resolved.runtime.agent_id,
382
+ resolved.runtime.conversation_id,
383
+ );
384
+ throw error;
385
+ }
386
+ }
387
+
388
+ private async openCloudTransport(
389
+ runtime: RuntimeScope,
390
+ connectionId: string,
391
+ ): Promise<RuntimeSessionInit> {
349
392
  const apiKey = getCloudApiKey(this.cloudOptions);
350
393
  const url = buildCloudStatusWebSocketUrl({
351
394
  apiBaseUrl: this.cloudOptions.apiBaseUrl,
352
- connectionId: connection.connectionId,
353
- agentId: resolved.runtime.agent_id,
354
- conversationId: resolved.runtime.conversation_id,
395
+ connectionId,
396
+ agentId: runtime.agent_id,
397
+ conversationId: runtime.conversation_id,
355
398
  apiKey,
356
399
  authMode: this.cloudOptions.webSocketAuth ?? "header",
357
400
  });
@@ -365,40 +408,21 @@ export class CloudEnvironmentSession extends RemoteClientSessionCore {
365
408
  : {}),
366
409
  pingIntervalMs:
367
410
  this.cloudOptions.pingIntervalMs ?? DEFAULT_PING_INTERVAL_MS,
368
- runtime: resolved.runtime,
411
+ runtime,
369
412
  }),
370
413
  requestTimeoutMs: this.cloudOptions.requestTimeoutMs ?? DEFAULT_TURN_TIMEOUT_MS,
371
414
  }));
372
415
  const options = this.currentOptions();
373
- this.mcpBridge = await connectMcpServers(
374
- "mcpServers" in options ? options.mcpServers : undefined,
375
- {
376
- cwd: options.cwd,
377
- reservedToolNames: this.externalTools.keys(),
378
- },
379
- );
380
- for (const tool of this.mcpBridge.tools) {
381
- this.externalTools.set(tool.name, tool);
382
- }
383
- this.removeControlRequestHandler = registerAppServerControlRequestHandler({
384
- client,
385
- getRuntime: () => this.runtime,
386
- getOptions: () => this.currentOptions(),
387
- });
388
- if (this.externalTools.size > 0) {
389
- this.removeExternalToolHandler = client.onExternalToolCall(
390
- createExternalToolCallHandler(this.externalTools),
391
- );
392
- }
416
+ this.installTransportHandlers(client);
393
417
 
394
418
  try {
395
419
  await client.connect();
396
- const response = await this.startCloudRuntime(client, resolved.runtime);
420
+ const response = await this.startCloudRuntime(client, runtime);
397
421
  if (!response.success || !response.runtime) {
398
422
  throw new Error(response.error ?? "Failed to start Cloud status runtime");
399
423
  }
400
424
 
401
- this.watchTransportDisconnect(client);
425
+ this.watchTransportDisconnect(client, { recoverWhenIdle: true });
402
426
 
403
427
  const tools = agentToolNames(response.agent);
404
428
  const skillSources = options.skillSources;
@@ -428,21 +452,64 @@ export class CloudEnvironmentSession extends RemoteClientSessionCore {
428
452
  ...(skillSources !== undefined ? { skillSources: [...skillSources] } : {}),
429
453
  };
430
454
  } catch (error) {
431
- this.removeExternalToolHandler?.();
432
- this.removeExternalToolHandler = null;
433
- this.removeControlRequestHandler?.();
434
- this.removeControlRequestHandler = null;
435
- await this.closeMcpBridge();
455
+ this.cleanupTransportHandlers();
436
456
  client.close();
437
- await this.cleanupManagedSandbox();
438
- await this.cleanupSessionRepositories(
439
- resolved.runtime.agent_id,
440
- resolved.runtime.conversation_id,
441
- );
442
457
  throw error;
443
458
  }
444
459
  }
445
460
 
461
+ private installTransportHandlers(client: AppServerClient): void {
462
+ this.cleanupTransportHandlers();
463
+ this.removeControlRequestHandler = registerAppServerControlRequestHandler({
464
+ client,
465
+ getRuntime: () => this.runtime,
466
+ getOptions: () => this.currentOptions(),
467
+ });
468
+ if (this.externalTools.size > 0) {
469
+ this.removeExternalToolHandler = client.onExternalToolCall(
470
+ createExternalToolCallHandler(this.externalTools),
471
+ );
472
+ }
473
+ }
474
+
475
+ private cleanupTransportHandlers(): void {
476
+ this.removeExternalToolHandler?.();
477
+ this.removeExternalToolHandler = null;
478
+ this.removeControlRequestHandler?.();
479
+ this.removeControlRequestHandler = null;
480
+ }
481
+
482
+ protected override async recoverIdleTransport(
483
+ runtime: RuntimeScope,
484
+ ): Promise<RuntimeSessionInit> {
485
+ const connection = await this.resolveRecoveryConnection(runtime);
486
+ this.connectionId = connection.connectionId;
487
+ return this.openCloudTransport(runtime, connection.connectionId);
488
+ }
489
+
490
+ private async resolveRecoveryConnection(
491
+ runtime: RuntimeScope,
492
+ ): Promise<ResolvedCloudConnection> {
493
+ const sandbox = this.managedSandbox;
494
+ if (sandbox) {
495
+ await this.refreshManagedSandbox(sandbox);
496
+ return this.waitForManagedSandboxConnection(sandbox);
497
+ }
498
+ const environment = this.effectiveEnvironment();
499
+ if (!environment) {
500
+ throw new Error("Cloud idle transport recovery lost its execution target");
501
+ }
502
+ return this.resolveExplicitConnection(environment);
503
+ }
504
+
505
+ protected override onIdleTransportDisconnect(): void {
506
+ this.cleanupTransportHandlers();
507
+ }
508
+
509
+ protected override onRecoveredTransportDiscarded(): void {
510
+ this.cleanupTransportHandlers();
511
+ }
512
+
446
513
  private async startCloudRuntime(
447
514
  client: AppServerClient,
448
515
  runtime: RuntimeScope,
@@ -462,6 +529,7 @@ export class CloudEnvironmentSession extends RemoteClientSessionCore {
462
529
  const mode = mapPermissionMode(options.permissionMode);
463
530
  if (mode) command.mode = mode;
464
531
  if (options.cwd !== undefined) command.cwd = options.cwd;
532
+ if (this.cloudMode.options.stateless === true) command.stateless = true;
465
533
  if (options.skillSources !== undefined) {
466
534
  command.skill_sources = [...new Set(options.skillSources)];
467
535
  }
@@ -490,10 +558,7 @@ export class CloudEnvironmentSession extends RemoteClientSessionCore {
490
558
  }
491
559
 
492
560
  protected override onCoreClose(): void {
493
- this.removeExternalToolHandler?.();
494
- this.removeExternalToolHandler = null;
495
- this.removeControlRequestHandler?.();
496
- this.removeControlRequestHandler = null;
561
+ this.cleanupTransportHandlers();
497
562
  this.mcpCleanup = this.closeMcpBridge();
498
563
  void this.cleanupManagedSandbox();
499
564
  if (this.runtime?.agent_id) {
@@ -982,10 +1047,12 @@ export class CloudEnvironmentSession extends RemoteClientSessionCore {
982
1047
  }
983
1048
 
984
1049
  private effectiveEnvironment(): LettaCodeEnvironment | undefined {
985
- const modeEnvironment = this.mode.kind === "session"
986
- ? this.mode.options.environment
1050
+ const modeComputer = this.mode.kind === "session"
1051
+ ? this.mode.options.computer ?? this.mode.options.environment
987
1052
  : undefined;
988
- return modeEnvironment ?? this.cloudOptions.environment;
1053
+ return modeComputer ??
1054
+ this.cloudOptions.computer ??
1055
+ this.cloudOptions.environment;
989
1056
  }
990
1057
 
991
1058
  private effectiveSandboxOptions(): LettaCodeCloudSandboxOptions | undefined {
@@ -0,0 +1,132 @@
1
+ import type { Letta } from "@letta-ai/letta-client";
2
+ import {
3
+ RemoteEnvironmentClient,
4
+ type RemoteEnvironmentConnection,
5
+ type RemoteEnvironmentTarget,
6
+ } from "./remote.js";
7
+
8
+ /**
9
+ * Selects the computer where a Cloud session runs tools and accesses files.
10
+ *
11
+ * Strings are treated as human-readable computer names. Prefer `deviceId` when
12
+ * persisting a selection because connection IDs can rotate after reconnects.
13
+ */
14
+ export type ComputerSelector =
15
+ | string
16
+ | { name: string }
17
+ | { id: string }
18
+ | { connectionId: string }
19
+ | { deviceId: string };
20
+
21
+ export interface ComputerMetadata {
22
+ os?: string;
23
+ lettaCodeVersion?: string;
24
+ nodeVersion?: string;
25
+ workingDirectory?: string;
26
+ gitBranch?: string;
27
+ [key: string]: unknown;
28
+ }
29
+
30
+ /** A registered computer that can host Letta agent tool execution. */
31
+ export interface Computer {
32
+ /** Environment record ID. */
33
+ id: string;
34
+ /** Stable identifier for the physical device across reconnects. */
35
+ deviceId: string;
36
+ /** Current online connection lease. This may rotate after reconnects. */
37
+ connectionId: string | null;
38
+ /** Human-readable computer name. */
39
+ name: string;
40
+ status: "online" | "offline";
41
+ connectedAt: number | null;
42
+ lastSeenAt: number;
43
+ metadata?: ComputerMetadata;
44
+ }
45
+
46
+ export interface ListComputersOptions {
47
+ limit?: number;
48
+ after?: string;
49
+ onlineOnly?: boolean;
50
+ }
51
+
52
+ export interface ListComputersResult {
53
+ computers: Computer[];
54
+ hasNextPage: boolean;
55
+ }
56
+
57
+ export interface ResolvedComputer {
58
+ connectionId: string;
59
+ /** Omitted when resolving an explicit connection ID. */
60
+ computer?: Computer;
61
+ }
62
+
63
+ export interface ComputersClient {
64
+ list(options?: ListComputersOptions): Promise<ListComputersResult>;
65
+ get(deviceId: string): Promise<Computer>;
66
+ resolve(selector: ComputerSelector): Promise<ResolvedComputer>;
67
+ }
68
+
69
+ function selectorToRemoteTarget(
70
+ selector: ComputerSelector,
71
+ ): RemoteEnvironmentTarget {
72
+ if (typeof selector === "string") return { connectionName: selector };
73
+ if ("name" in selector) return { connectionName: selector.name };
74
+ if ("id" in selector) return { environmentId: selector.id };
75
+ if ("connectionId" in selector) {
76
+ return { connectionId: selector.connectionId };
77
+ }
78
+ return { deviceId: selector.deviceId };
79
+ }
80
+
81
+ function toComputer(environment: RemoteEnvironmentConnection): Computer {
82
+ return {
83
+ id: environment.id,
84
+ deviceId: environment.deviceId,
85
+ connectionId: environment.connectionId,
86
+ name: environment.connectionName,
87
+ status: environment.connectionId ? "online" : "offline",
88
+ connectedAt: environment.connectedAt,
89
+ lastSeenAt: environment.lastSeenAt,
90
+ metadata: environment.metadata,
91
+ };
92
+ }
93
+
94
+ /** Public Cloud computer discovery and selection client. */
95
+ export class ComputersClientImpl implements ComputersClient {
96
+ private readonly environments: RemoteEnvironmentClient;
97
+
98
+ constructor(client: Letta) {
99
+ this.environments = new RemoteEnvironmentClient({}, client);
100
+ }
101
+
102
+ async list(
103
+ options: ListComputersOptions = {},
104
+ ): Promise<ListComputersResult> {
105
+ const result = await this.environments.listEnvironments(options);
106
+ return {
107
+ computers: result.connections.map(toComputer),
108
+ hasNextPage: result.hasNextPage,
109
+ };
110
+ }
111
+
112
+ async get(deviceId: string): Promise<Computer> {
113
+ if (typeof deviceId !== "string" || deviceId.trim().length === 0) {
114
+ throw new Error("Invalid deviceId. Expected a non-empty string.");
115
+ }
116
+ return toComputer(
117
+ await this.environments.getEnvironmentByDeviceId(deviceId),
118
+ );
119
+ }
120
+
121
+ async resolve(selector: ComputerSelector): Promise<ResolvedComputer> {
122
+ const result = await this.environments.resolveEnvironment(
123
+ selectorToRemoteTarget(selector),
124
+ );
125
+ return {
126
+ connectionId: result.connectionId,
127
+ computer: result.environment
128
+ ? toComputer(result.environment)
129
+ : undefined,
130
+ };
131
+ }
132
+ }
package/src/index.ts CHANGED
@@ -111,6 +111,7 @@ export type {
111
111
  ImageContent,
112
112
  MessageContentItem,
113
113
  SendMessage,
114
+ SendOptions,
114
115
  // List messages API
115
116
  ListMessagesOptions,
116
117
  ListMessagesResult,
@@ -173,11 +174,34 @@ export type {
173
174
  } from "./management-types.js";
174
175
 
175
176
  export { RepositoriesClient } from "./repositories.js";
177
+ export type {
178
+ Computer,
179
+ ComputerMetadata,
180
+ ComputerSelector,
181
+ ComputersClient,
182
+ ListComputersOptions,
183
+ ListComputersResult,
184
+ ResolvedComputer,
185
+ } from "./computers.js";
176
186
  export { LettaAgentClient } from "./client.js";
177
187
  export { CloudManagedSandboxExpiredError } from "./cloud-session.js";
178
188
  export { createReactNativeWebSocketConstructor } from "./websocket.js";
179
189
 
180
190
  export { extractStreamTextDelta } from "./stream-events.js";
191
+ export { createTranscriptAccumulator } from "./transcript-accumulator.js";
192
+ export type {
193
+ TranscriptAccumulator,
194
+ TranscriptHistoryPage,
195
+ TranscriptRebaseOptions,
196
+ TranscriptRow,
197
+ TranscriptRowIdentity,
198
+ TranscriptRowKind,
199
+ TranscriptTextKind,
200
+ TranscriptTextRow,
201
+ TranscriptToolCallRow,
202
+ TranscriptToolCallStatus,
203
+ TranscriptToolResult,
204
+ } from "./transcript-accumulator.js";
181
205
 
182
206
  // Tool helpers
183
207
  export {