@letta-ai/letta-agent-sdk 0.6.1 → 0.6.3

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 (39) hide show
  1. package/README.md +37 -1
  2. package/dist/agent-creation.d.ts +3 -7
  3. package/dist/agent-creation.d.ts.map +1 -1
  4. package/dist/app-server-session.d.ts +0 -5
  5. package/dist/app-server-session.d.ts.map +1 -1
  6. package/dist/client-base.d.ts +7 -0
  7. package/dist/client-base.d.ts.map +1 -1
  8. package/dist/client-entry.d.ts +1 -0
  9. package/dist/client-entry.d.ts.map +1 -1
  10. package/dist/client-entry.js +557 -420
  11. package/dist/client-entry.js.map +14 -13
  12. package/dist/cloud-session.d.ts +8 -1
  13. package/dist/cloud-session.d.ts.map +1 -1
  14. package/dist/computers.d.ts +67 -0
  15. package/dist/computers.d.ts.map +1 -0
  16. package/dist/index.d.ts +1 -0
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +558 -421
  19. package/dist/index.js.map +15 -14
  20. package/dist/remote-client-session-core.d.ts +17 -0
  21. package/dist/remote-client-session-core.d.ts.map +1 -1
  22. package/dist/remote-turn-coordinator.d.ts +10 -0
  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/types.d.ts +12 -20
  27. package/dist/types.d.ts.map +1 -1
  28. package/package.json +2 -2
  29. package/src/agent-creation.ts +93 -55
  30. package/src/app-server-session.ts +4 -151
  31. package/src/client-base.ts +66 -19
  32. package/src/client-entry.ts +9 -0
  33. package/src/cloud-session.ts +113 -45
  34. package/src/computers.ts +132 -0
  35. package/src/index.ts +9 -0
  36. package/src/remote-client-session-core.ts +107 -2
  37. package/src/remote-turn-coordinator.ts +27 -0
  38. package/src/remote.ts +41 -12
  39. package/src/types.ts +12 -17
@@ -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,6 +25,15 @@ 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";
30
39
  export { createReactNativeWebSocketConstructor } from "./websocket.js";
@@ -5,10 +5,10 @@ import {
5
5
  createAppServerClient,
6
6
  type AppServerClient,
7
7
  } from "@letta-ai/letta-code/app-server-client";
8
+ import { createAgentBody } from "./agent-creation.js";
8
9
  import {
9
10
  AppServerRuntimeController,
10
11
  agentToolNames,
11
- createAgentBody,
12
12
  createExternalToolCallHandler,
13
13
  externalToolGroups,
14
14
  registerAppServerControlRequestHandler,
@@ -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,39 +408,22 @@ 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
 
425
+ this.watchTransportDisconnect(client, { recoverWhenIdle: true });
426
+
401
427
  const tools = agentToolNames(response.agent);
402
428
  const skillSources = options.skillSources;
403
429
  const clientToolset = "toolset" in options ? options.toolset : undefined;
@@ -426,21 +452,64 @@ export class CloudEnvironmentSession extends RemoteClientSessionCore {
426
452
  ...(skillSources !== undefined ? { skillSources: [...skillSources] } : {}),
427
453
  };
428
454
  } catch (error) {
429
- this.removeExternalToolHandler?.();
430
- this.removeExternalToolHandler = null;
431
- this.removeControlRequestHandler?.();
432
- this.removeControlRequestHandler = null;
433
- await this.closeMcpBridge();
455
+ this.cleanupTransportHandlers();
434
456
  client.close();
435
- await this.cleanupManagedSandbox();
436
- await this.cleanupSessionRepositories(
437
- resolved.runtime.agent_id,
438
- resolved.runtime.conversation_id,
439
- );
440
457
  throw error;
441
458
  }
442
459
  }
443
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
+
444
513
  private async startCloudRuntime(
445
514
  client: AppServerClient,
446
515
  runtime: RuntimeScope,
@@ -488,10 +557,7 @@ export class CloudEnvironmentSession extends RemoteClientSessionCore {
488
557
  }
489
558
 
490
559
  protected override onCoreClose(): void {
491
- this.removeExternalToolHandler?.();
492
- this.removeExternalToolHandler = null;
493
- this.removeControlRequestHandler?.();
494
- this.removeControlRequestHandler = null;
560
+ this.cleanupTransportHandlers();
495
561
  this.mcpCleanup = this.closeMcpBridge();
496
562
  void this.cleanupManagedSandbox();
497
563
  if (this.runtime?.agent_id) {
@@ -980,10 +1046,12 @@ export class CloudEnvironmentSession extends RemoteClientSessionCore {
980
1046
  }
981
1047
 
982
1048
  private effectiveEnvironment(): LettaCodeEnvironment | undefined {
983
- const modeEnvironment = this.mode.kind === "session"
984
- ? this.mode.options.environment
1049
+ const modeComputer = this.mode.kind === "session"
1050
+ ? this.mode.options.computer ?? this.mode.options.environment
985
1051
  : undefined;
986
- return modeEnvironment ?? this.cloudOptions.environment;
1052
+ return modeComputer ??
1053
+ this.cloudOptions.computer ??
1054
+ this.cloudOptions.environment;
987
1055
  }
988
1056
 
989
1057
  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
@@ -173,6 +173,15 @@ export type {
173
173
  } from "./management-types.js";
174
174
 
175
175
  export { RepositoriesClient } from "./repositories.js";
176
+ export type {
177
+ Computer,
178
+ ComputerMetadata,
179
+ ComputerSelector,
180
+ ComputersClient,
181
+ ListComputersOptions,
182
+ ListComputersResult,
183
+ ResolvedComputer,
184
+ } from "./computers.js";
176
185
  export { LettaAgentClient } from "./client.js";
177
186
  export { CloudManagedSandboxExpiredError } from "./cloud-session.js";
178
187
  export { createReactNativeWebSocketConstructor } from "./websocket.js";