@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.
@@ -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,GACzB,gBAAgB,CAsClB"}
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,CA+E/B"}
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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=shared-app-server-client.d.ts.map
@@ -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. Each
495
- * SDK-owned local app-server session runs in its own process, so this
496
- * scopes cleanly per session — e.g. MEMORY_DIR / LETTA_MEMORY_DIR to point
497
- * the harness's memory scoping (and its guard) at a session-specific
498
- * memory copy. Ignored on remote and cloud transports.
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.35",
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.8",
43
+ "@letta-ai/letta-code": "0.34.9",
44
44
  "ajv": "^8.20.0"
45
45
  },
46
46
  "devDependencies": {
@@ -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 and any local App Server started for that pool.
746
- * Sessions have independent lifecycles and must be closed separately.
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 { startLocalAppServer } from "./local-app-server.js";
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
- const initMsg = await session.initialize();
57
- session.close();
58
- if (!initMsg.agentId) {
59
- throw new Error("Local App Server agent creation did not return an agent id.");
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
- return new LettaAgentClient().createAgent(options);
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
- return new LettaAgentClient().createSession(agentId, options);
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
- return new LettaAgentClient().resumeSession(id, options);
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
- return new LettaAgentClient({
369
+ const client = new LettaAgentClient({
342
370
  backend: "local",
343
371
  appServer: { harnessBackend: "api" },
344
- }).query(params);
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 = new LettaAgentClient().resumeSession(agentId, {
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,
@@ -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. Each
690
- * SDK-owned local app-server session runs in its own process, so this
691
- * scopes cleanly per session — e.g. MEMORY_DIR / LETTA_MEMORY_DIR to point
692
- * the harness's memory scoping (and its guard) at a session-specific
693
- * memory copy. Ignored on remote and cloud transports.
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
  /**