@letta-ai/letta-agent-sdk 0.8.35 → 0.8.37
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/README.md +8 -2
- package/dist/client-base.d.ts +2 -2
- package/dist/client-entry.js.map +1 -1
- package/dist/client.d.ts +4 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +143 -26
- package/dist/index.js.map +10 -9
- package/dist/local-app-server-session.d.ts +2 -1
- package/dist/local-app-server-session.d.ts.map +1 -1
- package/dist/local-app-server.d.ts +1 -0
- package/dist/local-app-server.d.ts.map +1 -1
- package/dist/shared-local-app-server.d.ts +13 -0
- package/dist/shared-local-app-server.d.ts.map +1 -0
- package/dist/tests/fixtures/shared-app-server-client.d.ts +2 -0
- package/dist/tests/fixtures/shared-app-server-client.d.ts.map +1 -0
- package/dist/types.d.ts +5 -5
- package/package.json +2 -2
- package/src/client-base.ts +2 -2
- package/src/client.ts +39 -13
- package/src/index.ts +54 -7
- package/src/local-app-server-session.ts +6 -1
- package/src/local-app-server.ts +2 -0
- package/src/shared-local-app-server.ts +57 -0
- package/src/types.ts +5 -5
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AppServerSession, type AppServerSessionMode } from "./app-server-session.js";
|
|
2
|
+
import type { SharedLocalAppServer } from "./shared-local-app-server.js";
|
|
2
3
|
import type { LettaCodeLocalAppServerOptions } from "./types.js";
|
|
3
|
-
export declare function createLocalAppServerSession(options: LettaCodeLocalAppServerOptions | undefined, mode: AppServerSessionMode): AppServerSession;
|
|
4
|
+
export declare function createLocalAppServerSession(options: LettaCodeLocalAppServerOptions | undefined, mode: AppServerSessionMode, sharedServer?: SharedLocalAppServer): AppServerSession;
|
|
4
5
|
//# sourceMappingURL=local-app-server-session.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-app-server-session.d.ts","sourceRoot":"","sources":["../src/local-app-server-session.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,KAAK,oBAAoB,EAE1B,MAAM,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAEV,8BAA8B,EAC/B,MAAM,YAAY,CAAC;AAEpB,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,8BAA8B,GAAG,SAAS,EACnD,IAAI,EAAE,oBAAoB,
|
|
1
|
+
{"version":3,"file":"local-app-server-session.d.ts","sourceRoot":"","sources":["../src/local-app-server-session.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,KAAK,oBAAoB,EAE1B,MAAM,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACzE,OAAO,KAAK,EAEV,8BAA8B,EAC/B,MAAM,YAAY,CAAC;AAEpB,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,8BAA8B,GAAG,SAAS,EACnD,IAAI,EAAE,oBAAoB,EAC1B,YAAY,CAAC,EAAE,oBAAoB,GAClC,gBAAgB,CAyClB"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type MemoryConfinementLauncherInput, type MemoryConfinementLauncherResult } from "@letta-ai/letta-code/memory-confinement";
|
|
2
2
|
export interface LocalAppServerHandle {
|
|
3
3
|
url: string;
|
|
4
|
+
isRunning(): boolean;
|
|
4
5
|
close(): void;
|
|
5
6
|
}
|
|
6
7
|
export interface StartLocalAppServerOptions {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-app-server.d.ts","sourceRoot":"","sources":["../src/local-app-server.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,8BAA8B,EACnC,KAAK,+BAA+B,EACrC,MAAM,yCAAyC,CAAC;AAGjD,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,IAAI,IAAI,CAAC;CACf;AAED,MAAM,WAAW,0BAA0B;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,qBAAqB,CAAC,EAAE,QAAQ,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,qBAAqB;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;CACxB;AAED,KAAK,yBAAyB,GAAG,CAC/B,KAAK,EAAE,8BAA8B,KAClC,+BAA+B,CAAC;AA6DrC,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,IAAI,CAAC,0BAA0B,EAAE,SAAS,GAAG,QAAQ,CAAM,GACnE,MAAM,EAAE,CAQV;AAED,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,0BAA+B,EACxC,aAAa,GAAE,yBAA2D,GACzE,qBAAqB,CAgCvB;AAYD;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,GAAE,0BAA+B,GACvC,OAAO,CAAC,oBAAoB,CAAC,
|
|
1
|
+
{"version":3,"file":"local-app-server.d.ts","sourceRoot":"","sources":["../src/local-app-server.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,8BAA8B,EACnC,KAAK,+BAA+B,EACrC,MAAM,yCAAyC,CAAC;AAGjD,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,IAAI,OAAO,CAAC;IACrB,KAAK,IAAI,IAAI,CAAC;CACf;AAED,MAAM,WAAW,0BAA0B;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,qBAAqB,CAAC,EAAE,QAAQ,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,qBAAqB;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;CACxB;AAED,KAAK,yBAAyB,GAAG,CAC/B,KAAK,EAAE,8BAA8B,KAClC,+BAA+B,CAAC;AA6DrC,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,IAAI,CAAC,0BAA0B,EAAE,SAAS,GAAG,QAAQ,CAAM,GACnE,MAAM,EAAE,CAQV;AAED,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,0BAA+B,EACxC,aAAa,GAAE,yBAA2D,GACzE,qBAAqB,CAgCvB;AAYD;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,GAAE,0BAA+B,GACvC,OAAO,CAAC,oBAAoB,CAAC,CAgF/B"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type LocalAppServerHandle, type StartLocalAppServerOptions } from "./local-app-server.js";
|
|
2
|
+
/** One lazily started process owned by a local SDK client, not its sessions. */
|
|
3
|
+
export declare class SharedLocalAppServer {
|
|
4
|
+
private readonly options;
|
|
5
|
+
private starting;
|
|
6
|
+
private server;
|
|
7
|
+
private closed;
|
|
8
|
+
private closing;
|
|
9
|
+
constructor(options: StartLocalAppServerOptions);
|
|
10
|
+
connect(): Promise<LocalAppServerHandle>;
|
|
11
|
+
close(): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=shared-local-app-server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared-local-app-server.d.ts","sourceRoot":"","sources":["../src/shared-local-app-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,oBAAoB,EACzB,KAAK,0BAA0B,EAChC,MAAM,uBAAuB,CAAC;AAE/B,gFAAgF;AAChF,qBAAa,oBAAoB;IAMnB,OAAO,CAAC,QAAQ,CAAC,OAAO;IALpC,OAAO,CAAC,QAAQ,CAA8C;IAC9D,OAAO,CAAC,MAAM,CAAqC;IACnD,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAA8B;gBAEhB,OAAO,EAAE,0BAA0B;IAE1D,OAAO,IAAI,OAAO,CAAC,oBAAoB,CAAC;IAiC9C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAQvB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared-app-server-client.d.ts","sourceRoot":"","sources":["../../../src/tests/fixtures/shared-app-server-client.ts"],"names":[],"mappings":""}
|
package/dist/types.d.ts
CHANGED
|
@@ -491,11 +491,11 @@ export interface LettaCodeClientSessionOptions extends CreateSessionOptions {
|
|
|
491
491
|
/** Per-session SDK-managed sandbox options when no computer is selected. */
|
|
492
492
|
sandbox?: LettaCodeCloudSandboxOptions;
|
|
493
493
|
/**
|
|
494
|
-
* Extra environment variables for the session's harness process.
|
|
495
|
-
*
|
|
496
|
-
*
|
|
497
|
-
*
|
|
498
|
-
*
|
|
494
|
+
* Extra environment variables for the session's harness process. Nonempty
|
|
495
|
+
* overrides use a dedicated process rather than the client's shared server,
|
|
496
|
+
* so MEMORY_DIR / LETTA_MEMORY_DIR can select a session-specific memory copy.
|
|
497
|
+
* Close these independently owned sessions separately from the client.
|
|
498
|
+
* Ignored on remote and cloud transports.
|
|
499
499
|
*/
|
|
500
500
|
env?: Record<string, string>;
|
|
501
501
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@letta-ai/letta-agent-sdk",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.37",
|
|
4
4
|
"packageManager": "bun@1.3.14",
|
|
5
5
|
"description": "SDK for programmatic control of Letta agents",
|
|
6
6
|
"type": "module",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@letta-ai/letta-client": "^1.12.1",
|
|
43
|
-
"@letta-ai/letta-code": "0.34.
|
|
43
|
+
"@letta-ai/letta-code": "0.34.9",
|
|
44
44
|
"ajv": "^8.20.0"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
package/src/client-base.ts
CHANGED
|
@@ -742,8 +742,8 @@ export class LettaAgentClientBase implements AsyncDisposable {
|
|
|
742
742
|
|
|
743
743
|
/**
|
|
744
744
|
* Release resources owned directly by this client, including its pooled
|
|
745
|
-
* management connection
|
|
746
|
-
*
|
|
745
|
+
* management connection. The Node client also stops its shared local App
|
|
746
|
+
* Server; dedicated and externally hosted sessions remain independently owned.
|
|
747
747
|
*/
|
|
748
748
|
close(): Promise<void> {
|
|
749
749
|
if (this.closePromise) return this.closePromise;
|
package/src/client.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { LettaAgentClientBase } from "./client-base.js";
|
|
|
2
2
|
import { AppServerManagementTransport } from "./app-server-management.js";
|
|
3
3
|
import type { ManagementTransport } from "./management.js";
|
|
4
4
|
import { createLocalAppServerSession } from "./local-app-server-session.js";
|
|
5
|
-
import {
|
|
5
|
+
import { SharedLocalAppServer } from "./shared-local-app-server.js";
|
|
6
6
|
import type { QueryOptions } from "./query-types.js";
|
|
7
7
|
import type { SkillNodeSupport } from "./skill-loading.js";
|
|
8
8
|
import { loadSkillDirectory, pushSkillSupportFiles } from "./skill-node.js";
|
|
@@ -14,6 +14,29 @@ import type {
|
|
|
14
14
|
} from "./types.js";
|
|
15
15
|
|
|
16
16
|
export class LettaAgentClient extends LettaAgentClientBase {
|
|
17
|
+
private localServer: SharedLocalAppServer | null = null;
|
|
18
|
+
private localClosePromise: Promise<void> | null = null;
|
|
19
|
+
|
|
20
|
+
private sharedLocalServer(): SharedLocalAppServer {
|
|
21
|
+
if (this.localClosePromise) throw new Error("LettaAgentClient is closed");
|
|
22
|
+
const options = (this.options as LettaCodeLocalClientOptions).appServer;
|
|
23
|
+
return this.localServer ??= new SharedLocalAppServer({
|
|
24
|
+
listen: options?.listen,
|
|
25
|
+
backend: options?.harnessBackend ?? "local",
|
|
26
|
+
startupTimeoutMs: options?.startupTimeoutMs,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
override close(): Promise<void> {
|
|
31
|
+
if (!this.localClosePromise) {
|
|
32
|
+
this.localClosePromise = Promise.all([
|
|
33
|
+
super.close(),
|
|
34
|
+
this.localServer?.close(),
|
|
35
|
+
]).then(() => {});
|
|
36
|
+
}
|
|
37
|
+
return this.localClosePromise;
|
|
38
|
+
}
|
|
39
|
+
|
|
17
40
|
protected override skillNodeSupport(): SkillNodeSupport {
|
|
18
41
|
return { loadSkillDirectory, pushSkillSupportFiles };
|
|
19
42
|
}
|
|
@@ -22,16 +45,12 @@ export class LettaAgentClient extends LettaAgentClientBase {
|
|
|
22
45
|
const localOptions = (
|
|
23
46
|
this.options as LettaCodeLocalClientOptions
|
|
24
47
|
).appServer;
|
|
48
|
+
const sharedServer = this.sharedLocalServer();
|
|
25
49
|
return new AppServerManagementTransport({
|
|
26
50
|
...(localOptions?.url !== undefined
|
|
27
51
|
? { url: localOptions.url }
|
|
28
52
|
: {
|
|
29
|
-
connect: () =>
|
|
30
|
-
startLocalAppServer({
|
|
31
|
-
listen: localOptions?.listen,
|
|
32
|
-
backend: localOptions?.harnessBackend ?? "local",
|
|
33
|
-
startupTimeoutMs: localOptions?.startupTimeoutMs,
|
|
34
|
-
}),
|
|
53
|
+
connect: () => sharedServer.connect(),
|
|
35
54
|
}),
|
|
36
55
|
...(localOptions?.WebSocket !== undefined
|
|
37
56
|
? { WebSocket: localOptions.WebSocket }
|
|
@@ -52,13 +71,17 @@ export class LettaAgentClient extends LettaAgentClientBase {
|
|
|
52
71
|
kind: "create-agent",
|
|
53
72
|
options,
|
|
54
73
|
},
|
|
74
|
+
this.sharedLocalServer(),
|
|
55
75
|
);
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
76
|
+
try {
|
|
77
|
+
const initMsg = await session.initialize();
|
|
78
|
+
if (!initMsg.agentId) {
|
|
79
|
+
throw new Error("Local App Server agent creation did not return an agent id.");
|
|
80
|
+
}
|
|
81
|
+
return initMsg.agentId;
|
|
82
|
+
} finally {
|
|
83
|
+
session.close();
|
|
60
84
|
}
|
|
61
|
-
return initMsg.agentId;
|
|
62
85
|
}
|
|
63
86
|
|
|
64
87
|
protected override createLocalSession(
|
|
@@ -74,6 +97,7 @@ export class LettaAgentClient extends LettaAgentClientBase {
|
|
|
74
97
|
newConversation: true,
|
|
75
98
|
options,
|
|
76
99
|
},
|
|
100
|
+
this.sharedLocalServer(),
|
|
77
101
|
);
|
|
78
102
|
}
|
|
79
103
|
|
|
@@ -109,7 +133,7 @@ export class LettaAgentClient extends LettaAgentClientBase {
|
|
|
109
133
|
...(queryOptions.disableMemoryGuard === true
|
|
110
134
|
? { disableMemoryGuard: true }
|
|
111
135
|
: {}),
|
|
112
|
-
});
|
|
136
|
+
}, this.sharedLocalServer());
|
|
113
137
|
}
|
|
114
138
|
|
|
115
139
|
protected override resumeLocalSession(
|
|
@@ -125,6 +149,7 @@ export class LettaAgentClient extends LettaAgentClientBase {
|
|
|
125
149
|
conversationId: id,
|
|
126
150
|
options,
|
|
127
151
|
},
|
|
152
|
+
this.sharedLocalServer(),
|
|
128
153
|
);
|
|
129
154
|
}
|
|
130
155
|
return createLocalAppServerSession(
|
|
@@ -135,6 +160,7 @@ export class LettaAgentClient extends LettaAgentClientBase {
|
|
|
135
160
|
defaultConversation: true,
|
|
136
161
|
options,
|
|
137
162
|
},
|
|
163
|
+
this.sharedLocalServer(),
|
|
138
164
|
);
|
|
139
165
|
}
|
|
140
166
|
}
|
package/src/index.ts
CHANGED
|
@@ -253,7 +253,33 @@ export {
|
|
|
253
253
|
*/
|
|
254
254
|
export async function createAgent(options: CreateAgentOptions = {}): Promise<string> {
|
|
255
255
|
validateCreateAgentOptions(options);
|
|
256
|
-
|
|
256
|
+
const client = new LettaAgentClient();
|
|
257
|
+
try {
|
|
258
|
+
return await client.createAgent(options);
|
|
259
|
+
} finally {
|
|
260
|
+
await client.close();
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/** Convenience sessions own their otherwise inaccessible client as well. */
|
|
265
|
+
function ownSession(client: LettaAgentClient, session: LettaCodeSession): LettaCodeSession {
|
|
266
|
+
const close = session.close.bind(session);
|
|
267
|
+
const dispose = session[Symbol.asyncDispose].bind(session);
|
|
268
|
+
session.close = () => {
|
|
269
|
+
try {
|
|
270
|
+
close();
|
|
271
|
+
} finally {
|
|
272
|
+
void client.close();
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
session[Symbol.asyncDispose] = async () => {
|
|
276
|
+
try {
|
|
277
|
+
await dispose();
|
|
278
|
+
} finally {
|
|
279
|
+
await client.close();
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
return session;
|
|
257
283
|
}
|
|
258
284
|
|
|
259
285
|
/**
|
|
@@ -272,7 +298,8 @@ export function createSession(
|
|
|
272
298
|
options: CreateSessionOptions = {},
|
|
273
299
|
): LettaCodeSession {
|
|
274
300
|
validateCreateSessionOptions(options);
|
|
275
|
-
|
|
301
|
+
const client = new LettaAgentClient();
|
|
302
|
+
return ownSession(client, client.createSession(agentId, options));
|
|
276
303
|
}
|
|
277
304
|
|
|
278
305
|
/**
|
|
@@ -298,7 +325,8 @@ export function resumeSession(
|
|
|
298
325
|
options: CreateSessionOptions = {},
|
|
299
326
|
): LettaCodeSession {
|
|
300
327
|
validateCreateSessionOptions(options);
|
|
301
|
-
|
|
328
|
+
const client = new LettaAgentClient();
|
|
329
|
+
return ownSession(client, client.resumeSession(id, options));
|
|
302
330
|
}
|
|
303
331
|
|
|
304
332
|
/**
|
|
@@ -338,10 +366,29 @@ export async function prompt(
|
|
|
338
366
|
|
|
339
367
|
/** Run a query in a new ordinary conversation. */
|
|
340
368
|
export function query(params: QueryParams): Query {
|
|
341
|
-
|
|
369
|
+
const client = new LettaAgentClient({
|
|
342
370
|
backend: "local",
|
|
343
371
|
appServer: { harnessBackend: "api" },
|
|
344
|
-
})
|
|
372
|
+
});
|
|
373
|
+
const inner = client.query(params);
|
|
374
|
+
const result = (async function* () {
|
|
375
|
+
try {
|
|
376
|
+
yield* inner;
|
|
377
|
+
} finally {
|
|
378
|
+
await client.close();
|
|
379
|
+
}
|
|
380
|
+
})();
|
|
381
|
+
return Object.defineProperties(Object.assign(result, {
|
|
382
|
+
interrupt: () => inner.interrupt(),
|
|
383
|
+
close() {
|
|
384
|
+
inner.close();
|
|
385
|
+
void client.close();
|
|
386
|
+
void result.return();
|
|
387
|
+
},
|
|
388
|
+
}), {
|
|
389
|
+
conversationId: { get: () => inner.conversationId, enumerable: true },
|
|
390
|
+
agentId: { get: () => inner.agentId, enumerable: true },
|
|
391
|
+
}) as Query;
|
|
345
392
|
}
|
|
346
393
|
|
|
347
394
|
// ═══════════════════════════════════════════════════════════════
|
|
@@ -383,11 +430,11 @@ export async function listMessagesDirect(
|
|
|
383
430
|
): Promise<ListMessagesResult> {
|
|
384
431
|
// resumeSession uses --default which maps to the agent's default conversation.
|
|
385
432
|
// The session is transient: we only need it long enough to list messages.
|
|
386
|
-
const session =
|
|
433
|
+
const session = resumeSession(agentId, {
|
|
387
434
|
permissionMode: "unrestricted",
|
|
388
435
|
});
|
|
389
|
-
await (session as InitializableSession).initialize();
|
|
390
436
|
try {
|
|
437
|
+
await (session as InitializableSession).initialize();
|
|
391
438
|
return await session.listMessages(options);
|
|
392
439
|
} finally {
|
|
393
440
|
session.close();
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
type AppServerSessionOptions,
|
|
5
5
|
} from "./app-server-session.js";
|
|
6
6
|
import { startLocalAppServer } from "./local-app-server.js";
|
|
7
|
+
import type { SharedLocalAppServer } from "./shared-local-app-server.js";
|
|
7
8
|
import type {
|
|
8
9
|
LettaCodeClientSessionOptions,
|
|
9
10
|
LettaCodeLocalAppServerOptions,
|
|
@@ -12,8 +13,12 @@ import type {
|
|
|
12
13
|
export function createLocalAppServerSession(
|
|
13
14
|
options: LettaCodeLocalAppServerOptions | undefined,
|
|
14
15
|
mode: AppServerSessionMode,
|
|
16
|
+
sharedServer?: SharedLocalAppServer,
|
|
15
17
|
): AppServerSession {
|
|
16
18
|
const appServer = options ?? {};
|
|
19
|
+
const session = mode.options as LettaCodeClientSessionOptions;
|
|
20
|
+
const needsOwnProcess = session.filesystemConfinement === "memory" ||
|
|
21
|
+
Object.keys(session.env ?? {}).length > 0;
|
|
17
22
|
const sessionOptions: AppServerSessionOptions = {
|
|
18
23
|
...(mode.kind === "conversation" &&
|
|
19
24
|
(appServer.harnessBackend ?? "local") === "local"
|
|
@@ -23,7 +28,7 @@ export function createLocalAppServerSession(
|
|
|
23
28
|
? { url: appServer.url }
|
|
24
29
|
: {
|
|
25
30
|
connect: (sessionEnv?: Record<string, string>) =>
|
|
26
|
-
startLocalAppServer({
|
|
31
|
+
sharedServer && !needsOwnProcess ? sharedServer.connect() : startLocalAppServer({
|
|
27
32
|
listen: appServer.listen,
|
|
28
33
|
backend: appServer.harnessBackend ?? "local",
|
|
29
34
|
startupTimeoutMs: appServer.startupTimeoutMs,
|
package/src/local-app-server.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { findLettaCli } from "./cli-resolver.js";
|
|
|
10
10
|
|
|
11
11
|
export interface LocalAppServerHandle {
|
|
12
12
|
url: string;
|
|
13
|
+
isRunning(): boolean;
|
|
13
14
|
close(): void;
|
|
14
15
|
}
|
|
15
16
|
|
|
@@ -194,6 +195,7 @@ export function startLocalAppServer(
|
|
|
194
195
|
cleanup();
|
|
195
196
|
resolve({
|
|
196
197
|
url,
|
|
198
|
+
isRunning: () => child.exitCode === null && child.signalCode === null,
|
|
197
199
|
close: () => terminateProcess(child),
|
|
198
200
|
});
|
|
199
201
|
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import {
|
|
2
|
+
startLocalAppServer,
|
|
3
|
+
type LocalAppServerHandle,
|
|
4
|
+
type StartLocalAppServerOptions,
|
|
5
|
+
} from "./local-app-server.js";
|
|
6
|
+
|
|
7
|
+
/** One lazily started process owned by a local SDK client, not its sessions. */
|
|
8
|
+
export class SharedLocalAppServer {
|
|
9
|
+
private starting: Promise<LocalAppServerHandle> | null = null;
|
|
10
|
+
private server: LocalAppServerHandle | null = null;
|
|
11
|
+
private closed = false;
|
|
12
|
+
private closing: Promise<void> | null = null;
|
|
13
|
+
|
|
14
|
+
constructor(private readonly options: StartLocalAppServerOptions) {}
|
|
15
|
+
|
|
16
|
+
async connect(): Promise<LocalAppServerHandle> {
|
|
17
|
+
if (this.closed) throw new Error("Local App Server owner is closed");
|
|
18
|
+
// A later call can replace an exited process; never replay a failed turn.
|
|
19
|
+
if (this.server && !this.server.isRunning()) {
|
|
20
|
+
this.server = null;
|
|
21
|
+
this.starting = null;
|
|
22
|
+
}
|
|
23
|
+
if (!this.starting) {
|
|
24
|
+
this.starting = startLocalAppServer(this.options).then(
|
|
25
|
+
(server) => {
|
|
26
|
+
if (this.closed) {
|
|
27
|
+
server.close();
|
|
28
|
+
throw new Error("Local App Server owner closed during startup");
|
|
29
|
+
}
|
|
30
|
+
this.server = server;
|
|
31
|
+
return server;
|
|
32
|
+
},
|
|
33
|
+
(error: unknown) => {
|
|
34
|
+
this.starting = null;
|
|
35
|
+
throw error;
|
|
36
|
+
},
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
const server = await this.starting;
|
|
40
|
+
if (this.closed) throw new Error("Local App Server owner is closed");
|
|
41
|
+
return {
|
|
42
|
+
url: server.url,
|
|
43
|
+
isRunning: server.isRunning,
|
|
44
|
+
// Session/management disposal releases only its socket, not the process.
|
|
45
|
+
close() {},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
close(): Promise<void> {
|
|
50
|
+
if (this.closing) return this.closing;
|
|
51
|
+
this.closed = true;
|
|
52
|
+
this.closing = this.starting
|
|
53
|
+
? this.starting.then((server) => server.close(), () => {})
|
|
54
|
+
: Promise.resolve();
|
|
55
|
+
return this.closing;
|
|
56
|
+
}
|
|
57
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -686,11 +686,11 @@ export interface LettaCodeClientSessionOptions extends CreateSessionOptions {
|
|
|
686
686
|
/** Per-session SDK-managed sandbox options when no computer is selected. */
|
|
687
687
|
sandbox?: LettaCodeCloudSandboxOptions;
|
|
688
688
|
/**
|
|
689
|
-
* Extra environment variables for the session's harness process.
|
|
690
|
-
*
|
|
691
|
-
*
|
|
692
|
-
*
|
|
693
|
-
*
|
|
689
|
+
* Extra environment variables for the session's harness process. Nonempty
|
|
690
|
+
* overrides use a dedicated process rather than the client's shared server,
|
|
691
|
+
* so MEMORY_DIR / LETTA_MEMORY_DIR can select a session-specific memory copy.
|
|
692
|
+
* Close these independently owned sessions separately from the client.
|
|
693
|
+
* Ignored on remote and cloud transports.
|
|
694
694
|
*/
|
|
695
695
|
env?: Record<string, string>;
|
|
696
696
|
/**
|