@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.
- package/AGENTS.md +42 -0
- package/README.md +101 -1
- package/dist/app-server-session.d.ts.map +1 -1
- package/dist/client-base.d.ts +7 -0
- package/dist/client-base.d.ts.map +1 -1
- package/dist/client-entry.d.ts +3 -0
- package/dist/client-entry.d.ts.map +1 -1
- package/dist/client-entry.js +816 -138
- package/dist/client-entry.js.map +16 -14
- package/dist/cloud-session.d.ts +8 -1
- package/dist/cloud-session.d.ts.map +1 -1
- package/dist/computers.d.ts +67 -0
- package/dist/computers.d.ts.map +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +817 -139
- package/dist/index.js.map +17 -15
- package/dist/remote-client-session-core.d.ts +13 -16
- package/dist/remote-client-session-core.d.ts.map +1 -1
- package/dist/remote-session-protocol.d.ts +11 -0
- package/dist/remote-session-protocol.d.ts.map +1 -1
- package/dist/remote-turn-coordinator.d.ts +6 -1
- package/dist/remote-turn-coordinator.d.ts.map +1 -1
- package/dist/remote.d.ts +6 -1
- package/dist/remote.d.ts.map +1 -1
- package/dist/transcript-accumulator.d.ts +133 -0
- package/dist/transcript-accumulator.d.ts.map +1 -0
- package/dist/types.d.ts +41 -21
- package/dist/types.d.ts.map +1 -1
- package/dist/validation.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/app-server-session.ts +9 -0
- package/src/client-base.ts +66 -19
- package/src/client-entry.ts +23 -0
- package/src/cloud-session.ts +112 -45
- package/src/computers.ts +132 -0
- package/src/index.ts +24 -0
- package/src/remote-client-session-core.ts +103 -27
- package/src/remote-session-protocol.ts +24 -0
- package/src/remote-turn-coordinator.ts +11 -2
- package/src/remote.ts +41 -12
- package/src/transcript-accumulator.ts +823 -0
- package/src/types.ts +43 -18
- package/src/validation.ts +16 -0
package/src/client-base.ts
CHANGED
|
@@ -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
|
|
63
|
+
function getOptionsComputer(
|
|
59
64
|
options: LettaCodeClientOptions,
|
|
60
|
-
):
|
|
61
|
-
if ("environment" in options)
|
|
62
|
-
|
|
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
|
|
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.
|
|
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.
|
|
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
|
-
|
|
151
|
+
`LettaAgentClient ${field} is only valid with backend: "cloud".`,
|
|
138
152
|
);
|
|
139
153
|
}
|
|
140
|
-
if (this.backend === "remote" && this.
|
|
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
|
-
|
|
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
|
|
374
|
+
const effectiveComputer =
|
|
375
|
+
options.computer ?? options.environment ?? this.computer;
|
|
348
376
|
if (this.backend === "local") {
|
|
349
|
-
if (
|
|
377
|
+
if (effectiveComputer !== undefined) {
|
|
378
|
+
const field = options.computer !== undefined ? "computer" : "environment";
|
|
350
379
|
throw new Error(
|
|
351
|
-
`${action}()
|
|
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}()
|
|
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 (
|
|
398
|
-
|
|
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 (
|
|
401
|
-
|
|
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".');
|
package/src/client-entry.ts
CHANGED
|
@@ -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";
|
package/src/cloud-session.ts
CHANGED
|
@@ -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.
|
|
175
|
-
throw new Error(
|
|
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
|
|
353
|
-
agentId:
|
|
354
|
-
conversationId:
|
|
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
|
|
411
|
+
runtime,
|
|
369
412
|
}),
|
|
370
413
|
requestTimeoutMs: this.cloudOptions.requestTimeoutMs ?? DEFAULT_TURN_TIMEOUT_MS,
|
|
371
414
|
}));
|
|
372
415
|
const options = this.currentOptions();
|
|
373
|
-
this.
|
|
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,
|
|
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.
|
|
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.
|
|
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
|
|
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
|
|
1053
|
+
return modeComputer ??
|
|
1054
|
+
this.cloudOptions.computer ??
|
|
1055
|
+
this.cloudOptions.environment;
|
|
989
1056
|
}
|
|
990
1057
|
|
|
991
1058
|
private effectiveSandboxOptions(): LettaCodeCloudSandboxOptions | undefined {
|
package/src/computers.ts
ADDED
|
@@ -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 {
|