@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
@@ -3177,36 +3177,6 @@ function appServerSocketOptions(authToken) {
3177
3177
  }
3178
3178
  return { headers: { Authorization: `Bearer ${token}` } };
3179
3179
  }
3180
- function sameRuntime(a, b) {
3181
- return a?.agent_id === b.agent_id && a?.conversation_id === b.conversation_id;
3182
- }
3183
- function isWaitingLoopStatus(message) {
3184
- return message.loop_status.status === "WAITING_ON_INPUT";
3185
- }
3186
- function isWaitingOnApprovalLoopStatus(message) {
3187
- return message.loop_status.status === "WAITING_ON_APPROVAL";
3188
- }
3189
- function streamDeltaRunId(message) {
3190
- const runId = message.delta.run_id;
3191
- return typeof runId === "string" ? runId : null;
3192
- }
3193
- function streamDeltaMessageType(message) {
3194
- const messageType = message.delta.message_type;
3195
- return typeof messageType === "string" ? messageType : null;
3196
- }
3197
- function streamDeltaStopReason(message) {
3198
- const stopReason = message.delta.stop_reason;
3199
- return typeof stopReason === "string" ? stopReason : null;
3200
- }
3201
- function streamDeltaErrorMessage(message) {
3202
- const delta = message.delta;
3203
- const apiMessage = delta.api_error?.message ?? delta.api_error?.detail;
3204
- if (typeof apiMessage === "string" && apiMessage.length > 0)
3205
- return apiMessage;
3206
- if (typeof delta.message === "string" && delta.message.length > 0)
3207
- return delta.message;
3208
- return "App-server turn failed";
3209
- }
3210
3180
 
3211
3181
  class AppServerClient {
3212
3182
  socket;
@@ -3217,7 +3187,6 @@ class AppServerClient {
3217
3187
  messageHandlers = new Set;
3218
3188
  sendHandlers = new Set;
3219
3189
  disconnectHandlers = new Set;
3220
- activeTurnRuntimes = new Set;
3221
3190
  explicitlyClosed = false;
3222
3191
  disconnectNotified = false;
3223
3192
  nextRequestNumber = 0;
@@ -3400,111 +3369,15 @@ class AppServerClient {
3400
3369
  input(command) {
3401
3370
  this.send({ type: "input", ...command });
3402
3371
  }
3403
- runTurn(command, options = {}) {
3404
- const runtimeKey = `${command.runtime.agent_id}/${command.runtime.conversation_id}`;
3405
- if (this.activeTurnRuntimes.has(runtimeKey)) {
3406
- return Promise.reject(new Error(`A turn is already in flight for ${runtimeKey}`));
3407
- }
3408
- this.activeTurnRuntimes.add(runtimeKey);
3409
- const timeoutMs = options.timeoutMs ?? this.requestTimeoutMs;
3410
- const commandWithIds = this.withClientMessageIds(command);
3411
- const runIds = new Set;
3412
- let observedTurnEvidence = false;
3413
- let observedRequiresApprovalStop = false;
3414
- return new Promise((resolve, reject) => {
3415
- const timeout = setTimeout(() => {
3416
- cleanup();
3417
- reject(new Error(`Timed out waiting for app-server turn on ${command.runtime.agent_id}/${command.runtime.conversation_id}`));
3418
- }, timeoutMs);
3419
- const cleanup = () => {
3420
- clearTimeout(timeout);
3421
- this.activeTurnRuntimes.delete(runtimeKey);
3422
- offMessage();
3423
- };
3424
- const finish = (completedBy, terminalMessage, stopReason) => {
3425
- cleanup();
3426
- resolve({
3427
- runtime: command.runtime,
3428
- stopReason,
3429
- runIds: [...runIds],
3430
- clientMessageIds: commandWithIds.clientMessageIds,
3431
- completedBy,
3432
- terminalMessage
3433
- });
3434
- };
3435
- const fail = (error) => {
3436
- cleanup();
3437
- reject(error);
3438
- };
3439
- const offMessage = this.onMessage((message) => {
3440
- if (!sameRuntime(message.runtime, command.runtime)) {
3441
- return;
3442
- }
3443
- if (message.type === "stream_delta") {
3444
- observedTurnEvidence = true;
3445
- const runId = streamDeltaRunId(message);
3446
- if (runId)
3447
- runIds.add(runId);
3448
- const messageType = streamDeltaMessageType(message);
3449
- if (messageType === "loop_error" || messageType === "error_message") {
3450
- fail(new Error(streamDeltaErrorMessage(message)));
3451
- return;
3452
- }
3453
- if (messageType === "stop_reason") {
3454
- const stopReason = streamDeltaStopReason(message);
3455
- if (stopReason === "requires_approval") {
3456
- observedRequiresApprovalStop = true;
3457
- return;
3458
- }
3459
- finish("stop_reason", message, stopReason);
3460
- }
3461
- return;
3462
- }
3463
- if (message.type === "update_loop_status") {
3464
- const hadTurnEvidenceBeforeLoopStatus = observedTurnEvidence || observedRequiresApprovalStop;
3465
- if (!hadTurnEvidenceBeforeLoopStatus && (isWaitingOnApprovalLoopStatus(message) || options.allowLoopStatusFallback === true && isWaitingLoopStatus(message))) {
3466
- return;
3467
- }
3468
- for (const runId of message.loop_status.active_run_ids) {
3469
- observedTurnEvidence = true;
3470
- runIds.add(runId);
3471
- }
3472
- if (hadTurnEvidenceBeforeLoopStatus && isWaitingOnApprovalLoopStatus(message)) {
3473
- finish("loop_status_waiting_on_approval", message, "requires_approval");
3474
- return;
3475
- }
3476
- if (options.allowLoopStatusFallback === true && hadTurnEvidenceBeforeLoopStatus && isWaitingLoopStatus(message)) {
3477
- finish("loop_status_waiting_fallback", message, null);
3478
- }
3479
- }
3480
- });
3481
- try {
3482
- this.input(commandWithIds.command);
3483
- } catch (error) {
3484
- fail(error instanceof Error ? error : new Error(String(error)));
3485
- }
3486
- });
3487
- }
3488
- withClientMessageIds(command) {
3489
- if (command.payload.kind !== "create_message") {
3490
- return { command, clientMessageIds: [] };
3491
- }
3492
- const clientMessageIds = [];
3493
- const messages = command.payload.messages.map((message) => {
3494
- if (message.role !== "user")
3495
- return message;
3496
- const existing = message.client_message_id;
3497
- const clientMessageId = typeof existing === "string" && existing.length > 0 ? existing : this.nextRequestId("client-message");
3498
- clientMessageIds.push(clientMessageId);
3499
- return { ...message, client_message_id: clientMessageId };
3372
+ submitInput(command, options = {}) {
3373
+ return this.request({
3374
+ type: "input",
3375
+ request_id: command.request_id ?? this.nextRequestId("input"),
3376
+ ...command
3377
+ }, {
3378
+ ...options,
3379
+ predicate: (message) => message.type === "input_accepted"
3500
3380
  });
3501
- return {
3502
- command: {
3503
- ...command,
3504
- payload: { ...command.payload, messages }
3505
- },
3506
- clientMessageIds
3507
- };
3508
3381
  }
3509
3382
  handleMessage(event, channel) {
3510
3383
  const message = parseProtocolMessage(event);
@@ -6438,6 +6311,73 @@ var models_default = {
6438
6311
  parallel_tool_calls: true
6439
6312
  }
6440
6313
  },
6314
+ {
6315
+ id: "opus-5",
6316
+ handle: "anthropic/claude-opus-5",
6317
+ label: "Opus 5",
6318
+ description: "Opus 5 (high reasoning)",
6319
+ updateArgs: {
6320
+ context_window: 200000,
6321
+ max_output_tokens: 128000,
6322
+ enable_reasoner: true,
6323
+ reasoning_effort: "high",
6324
+ parallel_tool_calls: true
6325
+ }
6326
+ },
6327
+ {
6328
+ id: "opus-5-low",
6329
+ handle: "anthropic/claude-opus-5",
6330
+ label: "Opus 5",
6331
+ description: "Opus 5 (low reasoning)",
6332
+ updateArgs: {
6333
+ context_window: 200000,
6334
+ max_output_tokens: 128000,
6335
+ enable_reasoner: true,
6336
+ reasoning_effort: "low",
6337
+ max_reasoning_tokens: 4000,
6338
+ parallel_tool_calls: true
6339
+ }
6340
+ },
6341
+ {
6342
+ id: "opus-5-medium",
6343
+ handle: "anthropic/claude-opus-5",
6344
+ label: "Opus 5",
6345
+ description: "Opus 5 (med reasoning)",
6346
+ updateArgs: {
6347
+ context_window: 200000,
6348
+ max_output_tokens: 128000,
6349
+ enable_reasoner: true,
6350
+ reasoning_effort: "medium",
6351
+ max_reasoning_tokens: 12000,
6352
+ parallel_tool_calls: true
6353
+ }
6354
+ },
6355
+ {
6356
+ id: "opus-5-xhigh",
6357
+ handle: "anthropic/claude-opus-5",
6358
+ label: "Opus 5",
6359
+ description: "Opus 5 (extra-high reasoning)",
6360
+ updateArgs: {
6361
+ context_window: 200000,
6362
+ max_output_tokens: 128000,
6363
+ enable_reasoner: true,
6364
+ reasoning_effort: "xhigh",
6365
+ parallel_tool_calls: true
6366
+ }
6367
+ },
6368
+ {
6369
+ id: "opus-5-max",
6370
+ handle: "anthropic/claude-opus-5",
6371
+ label: "Opus 5",
6372
+ description: "Opus 5 (max reasoning)",
6373
+ updateArgs: {
6374
+ context_window: 200000,
6375
+ max_output_tokens: 128000,
6376
+ enable_reasoner: true,
6377
+ reasoning_effort: "max",
6378
+ parallel_tool_calls: true
6379
+ }
6380
+ },
6441
6381
  {
6442
6382
  id: "opus",
6443
6383
  handle: "anthropic/claude-opus-4-8",
@@ -6712,7 +6652,7 @@ var models_default = {
6712
6652
  id: "sonnet-5-xhigh",
6713
6653
  handle: "anthropic/claude-sonnet-5",
6714
6654
  label: "Sonnet 5",
6715
- description: "Sonnet 5 (max reasoning)",
6655
+ description: "Sonnet 5 (extra-high reasoning)",
6716
6656
  updateArgs: {
6717
6657
  context_window: 1e6,
6718
6658
  max_output_tokens: 128000,
@@ -6721,6 +6661,19 @@ var models_default = {
6721
6661
  parallel_tool_calls: true
6722
6662
  }
6723
6663
  },
6664
+ {
6665
+ id: "sonnet-5-max",
6666
+ handle: "anthropic/claude-sonnet-5",
6667
+ label: "Sonnet 5",
6668
+ description: "Sonnet 5 (max reasoning)",
6669
+ updateArgs: {
6670
+ context_window: 1e6,
6671
+ max_output_tokens: 128000,
6672
+ reasoning_effort: "max",
6673
+ enable_reasoner: true,
6674
+ parallel_tool_calls: true
6675
+ }
6676
+ },
6724
6677
  {
6725
6678
  id: "sonnet-4.6",
6726
6679
  handle: "anthropic/claude-sonnet-4-6",
@@ -8271,7 +8224,6 @@ var models_default = {
8271
8224
  ]
8272
8225
  };
8273
8226
  var models = models_default.models;
8274
- var MODEL_PRESETS = models;
8275
8227
  function resolveModel(modelIdentifier) {
8276
8228
  const byId = models.find((m) => m.id === modelIdentifier);
8277
8229
  if (byId)
@@ -8499,75 +8451,128 @@ function buildPersonalityMemoryBlocks(personalityId, defaultMemoryBlocks, enviro
8499
8451
  }
8500
8452
  var LETTA_CODE_AGENT_TYPE = "letta_v1_agent";
8501
8453
  var DEFAULT_CREATED_AGENT_BASE_TOOLS = ["web_search", "fetch_webpage"];
8502
- async function buildCreateAgentRequestForPersonality(params) {
8503
- const { personalityId, name, description, model, extraTags } = params;
8504
- const personality = getPersonalityOption(personalityId);
8505
- const modelIdentifier = model ?? personality.defaultModel;
8454
+ function mergeMemoryBlocks(base, overrides) {
8455
+ const blocks = base.map((block) => ({ ...block }));
8456
+ for (const override of overrides ?? []) {
8457
+ const index = blocks.findIndex((block) => block.label === override.label);
8458
+ if (index >= 0) {
8459
+ blocks[index] = { ...override };
8460
+ } else {
8461
+ blocks.push({ ...override });
8462
+ }
8463
+ }
8464
+ return blocks;
8465
+ }
8466
+ async function buildCreateAgentRequest(options = {}) {
8467
+ const personality = options.personalityId ? getPersonalityOption(options.personalityId) : undefined;
8468
+ const modelIdentifier = options.model ?? personality?.defaultModel;
8506
8469
  const modelHandle = modelIdentifier ? resolveModel(modelIdentifier) : getDefaultModel();
8507
8470
  if (!modelHandle) {
8508
8471
  throw new Error(`Unknown model: ${modelIdentifier}`);
8509
8472
  }
8510
- const defaultMemoryBlocks = await getDefaultMemoryBlocks();
8473
+ if (!options.isSubagent && options.enableMemfs !== undefined && options.memoryPromptMode !== undefined && options.enableMemfs !== (options.memoryPromptMode !== "standard")) {
8474
+ throw new Error("enableMemfs and memoryPromptMode must describe the same memory mode");
8475
+ }
8476
+ const enableMemfs = options.isSubagent ? false : options.enableMemfs ?? options.memoryPromptMode !== "standard";
8477
+ const memoryPromptMode = options.isSubagent ? "standard" : options.memoryPromptMode ?? (enableMemfs ? "memfs" : "standard");
8478
+ const personalityTags = options.personalityId ? getPersonalityCreationTags(options.personalityId) : [];
8479
+ const personalityBlocks = options.personalityId ? buildPersonalityMemoryBlocks(options.personalityId, await getDefaultMemoryBlocks()) : [];
8480
+ const memoryBlocks = options.isSubagent ? undefined : options.personalityId || options.memoryBlocks !== undefined ? mergeMemoryBlocks(personalityBlocks, options.memoryBlocks) : undefined;
8481
+ const blockIds = options.isSubagent ? undefined : options.blockIds;
8511
8482
  return {
8512
8483
  agent_type: LETTA_CODE_AGENT_TYPE,
8513
- name: name ?? personality.label,
8514
- description: description ?? personality.description,
8484
+ ...options.name !== undefined || personality ? { name: options.name ?? personality?.label } : {},
8485
+ ...options.description !== undefined || personality ? { description: options.description ?? personality?.description } : {},
8515
8486
  model: modelHandle,
8516
- system: buildSystemPrompt("default", "memfs"),
8517
- memory_blocks: buildPersonalityMemoryBlocks(personalityId, defaultMemoryBlocks),
8487
+ system: options.system ?? buildSystemPrompt("default", memoryPromptMode),
8488
+ ...memoryBlocks !== undefined ? { memory_blocks: memoryBlocks } : {},
8489
+ ...blockIds && blockIds.length > 0 ? { block_ids: blockIds } : {},
8518
8490
  tags: buildCreatedAgentTags({
8519
- enableMemfs: true,
8520
- tags: [
8521
- ...getPersonalityCreationTags(personalityId),
8522
- ...extraTags ?? []
8523
- ]
8491
+ enableMemfs,
8492
+ isSubagent: options.isSubagent,
8493
+ tags: [...personalityTags, ...options.extraTags ?? []]
8524
8494
  }),
8525
- tools: [...DEFAULT_CREATED_AGENT_BASE_TOOLS],
8495
+ tools: [...options.baseTools ?? DEFAULT_CREATED_AGENT_BASE_TOOLS],
8526
8496
  include_base_tools: false,
8527
8497
  include_base_tool_rules: false,
8528
8498
  initial_message_sequence: [],
8529
- parallel_tool_calls: true,
8530
- compaction_settings: { model: DEFAULT_SUMMARIZATION_MODEL }
8499
+ parallel_tool_calls: options.parallelToolCalls ?? true,
8500
+ compaction_settings: {
8501
+ model: options.compactionModel ?? DEFAULT_SUMMARIZATION_MODEL
8502
+ },
8503
+ ...options.embedding !== undefined ? { embedding: options.embedding } : {},
8504
+ ...options.isSubagent ? { hidden: true } : options.hidden !== undefined ? { hidden: options.hidden } : {}
8531
8505
  };
8532
8506
  }
8533
8507
 
8534
8508
  // src/agent-creation.ts
8535
- var DEFAULT_MODEL_ID = "auto";
8536
- var DEFAULT_SUMMARIZATION_MODEL2 = "letta/auto";
8537
- function resolveModel2(modelIdentifier) {
8538
- const preset = MODEL_PRESETS.find((model) => model.id === modelIdentifier || model.handle === modelIdentifier);
8539
- if (preset)
8540
- return preset.handle;
8541
- if (modelIdentifier.includes("/"))
8542
- return modelIdentifier;
8543
- throw new Error(`Unknown model: ${modelIdentifier}`);
8509
+ function isPresetSystemPrompt(value) {
8510
+ return [
8511
+ "default",
8512
+ "letta-claude",
8513
+ "letta-codex",
8514
+ "letta-gemini",
8515
+ "claude",
8516
+ "codex",
8517
+ "gemini"
8518
+ ].includes(value);
8544
8519
  }
8545
- async function buildInitialAgentBody(options) {
8546
- if (options.personality !== undefined) {
8547
- return {
8548
- ...await buildCreateAgentRequestForPersonality({
8549
- personalityId: options.personality,
8550
- ...options.name !== undefined ? { name: options.name } : {},
8551
- ...options.description !== undefined ? { description: options.description } : {},
8552
- ...options.model !== undefined ? { model: options.model } : {},
8553
- ...options.tags !== undefined ? { extraTags: options.tags } : {}
8554
- })
8555
- };
8520
+ function assertCreateAgentOptionsSupported(options) {
8521
+ if (options.allowedTools !== undefined || options.disallowedTools !== undefined) {
8522
+ throw new Error("App-server createAgent() does not yet support allowedTools/disallowedTools.");
8523
+ }
8524
+ if (options.canUseTool !== undefined) {
8525
+ throw new Error("App-server createAgent() does not yet support canUseTool callbacks.");
8526
+ }
8527
+ if (options.systemInfoReminder !== undefined) {
8528
+ throw new Error("App-server createAgent() does not yet support systemInfoReminder overrides.");
8529
+ }
8530
+ if (options.dreaming?.behavior !== undefined) {
8531
+ throw new Error("App-server createAgent() does not yet support dreaming.behavior overrides.");
8556
8532
  }
8557
- return {
8558
- agent_type: LETTA_CODE_AGENT_TYPE,
8559
- ...options.name !== undefined ? { name: options.name } : {},
8560
- ...options.description !== undefined ? { description: options.description } : {},
8561
- model: resolveModel2(options.model ?? DEFAULT_MODEL_ID),
8562
- system: buildSystemPrompt("default", "memfs"),
8563
- tags: buildCreatedAgentTags({
8564
- enableMemfs: true,
8565
- tags: options.tags ?? []
8566
- }),
8567
- initial_message_sequence: [],
8568
- parallel_tool_calls: true,
8569
- compaction_settings: { model: DEFAULT_SUMMARIZATION_MODEL2 }
8570
- };
8533
+ }
8534
+ async function createAgentBody(options) {
8535
+ assertCreateAgentOptionsSupported(options);
8536
+ let system;
8537
+ if (options.systemPrompt !== undefined) {
8538
+ if (typeof options.systemPrompt !== "string" || isPresetSystemPrompt(options.systemPrompt)) {
8539
+ throw new Error("createAgent() does not yet support system prompt presets for this backend.");
8540
+ }
8541
+ system = options.systemPrompt;
8542
+ }
8543
+ const memoryBlocks = [];
8544
+ const blockIds = [];
8545
+ for (const item of options.memory ?? []) {
8546
+ if (typeof item === "string") {
8547
+ throw new Error("App-server createAgent() does not yet support memory preset names.");
8548
+ }
8549
+ if ("blockId" in item) {
8550
+ blockIds.push(item.blockId);
8551
+ } else {
8552
+ memoryBlocks.push({ ...item });
8553
+ }
8554
+ }
8555
+ if (options.persona !== undefined) {
8556
+ memoryBlocks.push({ label: "persona", value: options.persona });
8557
+ }
8558
+ if (options.human !== undefined) {
8559
+ memoryBlocks.push({ label: "human", value: options.human });
8560
+ }
8561
+ const hasMemoryConfiguration = options.memory !== undefined || options.persona !== undefined || options.human !== undefined;
8562
+ return buildCreateAgentRequest({
8563
+ personalityId: options.personality,
8564
+ name: options.name,
8565
+ description: options.description,
8566
+ model: options.model,
8567
+ system,
8568
+ memoryBlocks: hasMemoryConfiguration ? memoryBlocks : undefined,
8569
+ blockIds,
8570
+ extraTags: options.tags,
8571
+ enableMemfs: options.memfs ?? true,
8572
+ baseTools: options.baseTools,
8573
+ embedding: options.embedding,
8574
+ hidden: options.hidden
8575
+ });
8571
8576
  }
8572
8577
 
8573
8578
  // src/interactiveToolPolicy.ts
@@ -8856,7 +8861,7 @@ function firstToolReturn(delta) {
8856
8861
  }
8857
8862
  return;
8858
8863
  }
8859
- function sameRuntime2(message, runtime) {
8864
+ function sameRuntime(message, runtime) {
8860
8865
  const msgRuntime = message.runtime;
8861
8866
  if (msgRuntime) {
8862
8867
  return msgRuntime.agent_id === runtime.agent_id && msgRuntime.conversation_id === runtime.conversation_id;
@@ -8875,10 +8880,10 @@ function streamDeltaRecord(message) {
8875
8880
  const delta = message.delta;
8876
8881
  return delta && typeof delta === "object" && !Array.isArray(delta) ? delta : null;
8877
8882
  }
8878
- function streamDeltaMessageType2(delta) {
8883
+ function streamDeltaMessageType(delta) {
8879
8884
  return typeof delta.message_type === "string" ? delta.message_type : undefined;
8880
8885
  }
8881
- function streamDeltaRunId2(delta) {
8886
+ function streamDeltaRunId(delta) {
8882
8887
  return typeof delta.run_id === "string" ? delta.run_id : undefined;
8883
8888
  }
8884
8889
  function streamDeltaOtid(delta) {
@@ -8887,7 +8892,7 @@ function streamDeltaOtid(delta) {
8887
8892
  function streamDeltaSeqId(delta) {
8888
8893
  return typeof delta.seq_id === "number" ? delta.seq_id : undefined;
8889
8894
  }
8890
- function streamDeltaStopReason2(delta) {
8895
+ function streamDeltaStopReason(delta) {
8891
8896
  return typeof delta.stop_reason === "string" ? delta.stop_reason : undefined;
8892
8897
  }
8893
8898
  function loopStatusRecord(message) {
@@ -9110,7 +9115,7 @@ class RemoteTurnCoordinator {
9110
9115
  this.activeTurn.abortRequested = true;
9111
9116
  }
9112
9117
  handleProtocolMessage(message, runtime) {
9113
- if (!sameRuntime2(message, runtime))
9118
+ if (!sameRuntime(message, runtime))
9114
9119
  return;
9115
9120
  const statusRecord = deviceStatusRecord(message);
9116
9121
  if (statusRecord) {
@@ -9137,7 +9142,7 @@ class RemoteTurnCoordinator {
9137
9142
  const active = this.activateNextTurnFromProtocol();
9138
9143
  if (active) {
9139
9144
  active.observedTurnEvidence = true;
9140
- const runId = streamDeltaRunId2(delta);
9145
+ const runId = streamDeltaRunId(delta);
9141
9146
  if (runId)
9142
9147
  active.runIds.add(runId);
9143
9148
  }
@@ -9156,6 +9161,24 @@ class RemoteTurnCoordinator {
9156
9161
  this.streamResolvers.push(resolve);
9157
9162
  });
9158
9163
  }
9164
+ closeWithError(detail) {
9165
+ if (this.closed)
9166
+ return;
9167
+ const active = this.activeTurn;
9168
+ if (active) {
9169
+ this.failTurn(active, detail);
9170
+ } else {
9171
+ this.enqueue({
9172
+ type: "error",
9173
+ message: detail,
9174
+ errorCode: "error",
9175
+ stopReason: "error",
9176
+ errorDetail: detail,
9177
+ recoverable: false
9178
+ });
9179
+ }
9180
+ this.close();
9181
+ }
9159
9182
  close() {
9160
9183
  if (this.closed)
9161
9184
  return;
@@ -9273,9 +9296,9 @@ class RemoteTurnCoordinator {
9273
9296
  const active = this.activeTurn;
9274
9297
  if (!active)
9275
9298
  return;
9276
- const messageType = streamDeltaMessageType2(delta);
9299
+ const messageType = streamDeltaMessageType(delta);
9277
9300
  if (messageType === "stop_reason") {
9278
- const stopReason = streamDeltaStopReason2(delta) ?? null;
9301
+ const stopReason = streamDeltaStopReason(delta) ?? null;
9279
9302
  if (stopReason === "requires_approval") {
9280
9303
  active.observedRequiresApprovalStop = true;
9281
9304
  return;
@@ -9469,6 +9492,10 @@ class RemoteClientSessionCore {
9469
9492
  requestTimeoutMs;
9470
9493
  initializePromise = null;
9471
9494
  removeMessageHandler = null;
9495
+ detachTransportDisconnect = null;
9496
+ idleTransportDisconnected = false;
9497
+ transportRecoveryPromise = null;
9498
+ transportDisconnectGeneration = 0;
9472
9499
  turns;
9473
9500
  toolNames;
9474
9501
  deviceStatusListeners = new Set;
@@ -9543,6 +9570,8 @@ class RemoteClientSessionCore {
9543
9570
  cleanupFailedInitialize() {
9544
9571
  this.removeMessageHandler?.();
9545
9572
  this.removeMessageHandler = null;
9573
+ this.detachTransportDisconnect?.();
9574
+ this.detachTransportDisconnect = null;
9546
9575
  this.controller?.close();
9547
9576
  this.controller = null;
9548
9577
  this.onCoreClose();
@@ -9556,16 +9585,24 @@ class RemoteClientSessionCore {
9556
9585
  this.initialized = false;
9557
9586
  }
9558
9587
  async send(message) {
9588
+ if (this.closed)
9589
+ throw new Error("Session is closed");
9559
9590
  if (!this.initialized) {
9560
9591
  await this.initialize();
9561
9592
  }
9593
+ await this.recoverIdleTransportIfNeeded();
9562
9594
  if (!this.controller || !this.runtime) {
9563
9595
  throw new Error("Session is not initialized");
9564
9596
  }
9565
9597
  await this.beforeTurn();
9566
- const turn = this.turns.trackSentTurn(this.runtime);
9598
+ const controller = this.controller;
9599
+ const runtime = this.runtime;
9600
+ if (!controller || !runtime) {
9601
+ throw new Error("Session transport disconnected before the turn was sent");
9602
+ }
9603
+ const turn = this.turns.trackSentTurn(runtime);
9567
9604
  try {
9568
- this.controller.sendTurnMessage(this.runtime, message, {
9605
+ controller.sendTurnMessage(runtime, message, {
9569
9606
  clientMessageId: turn.clientMessageId
9570
9607
  });
9571
9608
  } catch (error) {
@@ -9830,6 +9867,8 @@ class RemoteClientSessionCore {
9830
9867
  this.closed = true;
9831
9868
  this.removeMessageHandler?.();
9832
9869
  this.removeMessageHandler = null;
9870
+ this.detachTransportDisconnect?.();
9871
+ this.detachTransportDisconnect = null;
9833
9872
  this.turns.close();
9834
9873
  for (const cancel of [...this.deviceStatusRefreshCancels]) {
9835
9874
  cancel(new Error(`Session closed while waiting for ${this.label} device status`));
@@ -9838,6 +9877,7 @@ class RemoteClientSessionCore {
9838
9877
  this.deviceStatusListeners.clear();
9839
9878
  this.controller?.close();
9840
9879
  this.controller = null;
9880
+ this.idleTransportDisconnected = false;
9841
9881
  this.onCoreClose();
9842
9882
  }
9843
9883
  get agentId() {
@@ -9890,6 +9930,78 @@ class RemoteClientSessionCore {
9890
9930
  }, { predicate: (message) => message.type === "update_toolset_response" });
9891
9931
  ensureSuccess(response, "Failed to update toolset");
9892
9932
  }
9933
+ watchTransportDisconnect(client, options = {}) {
9934
+ this.detachTransportDisconnect?.();
9935
+ this.detachTransportDisconnect = client.onDisconnect(() => {
9936
+ if (this.closed)
9937
+ return;
9938
+ this.transportDisconnectGeneration += 1;
9939
+ if (options.recoverWhenIdle && !this.turns.hasInFlightTurn()) {
9940
+ this.detachTransportDisconnect?.();
9941
+ this.detachTransportDisconnect = null;
9942
+ this.removeMessageHandler?.();
9943
+ this.removeMessageHandler = null;
9944
+ this.controller?.close();
9945
+ this.controller = null;
9946
+ const error = new Error(`${this.label} connection closed before send`);
9947
+ for (const cancel of [...this.deviceStatusRefreshCancels])
9948
+ cancel(error);
9949
+ this.deviceStatusRefreshCancels.clear();
9950
+ this.onIdleTransportDisconnect();
9951
+ this.idleTransportDisconnected = true;
9952
+ return;
9953
+ }
9954
+ this.turns.closeWithError(`The ${this.label} connection closed unexpectedly; resume the conversation to continue.`);
9955
+ this.close();
9956
+ });
9957
+ }
9958
+ async recoverIdleTransportIfNeeded() {
9959
+ if (!this.idleTransportDisconnected)
9960
+ return;
9961
+ if (this.transportRecoveryPromise) {
9962
+ await this.transportRecoveryPromise;
9963
+ return;
9964
+ }
9965
+ const runtime = this.runtime;
9966
+ if (!runtime)
9967
+ throw new Error("Session transport disconnected without a runtime");
9968
+ const generation = this.transportDisconnectGeneration;
9969
+ const recovery = this.recoverIdleTransport(runtime).then(async (init) => {
9970
+ if (this.closed || generation !== this.transportDisconnectGeneration) {
9971
+ init.controller.close();
9972
+ this.onRecoveredTransportDiscarded();
9973
+ throw new Error(`${this.label} connection closed during recovery`);
9974
+ }
9975
+ if (init.runtime.agent_id !== runtime.agent_id || init.runtime.conversation_id !== runtime.conversation_id) {
9976
+ init.controller.close();
9977
+ this.onRecoveredTransportDiscarded();
9978
+ throw new Error(`${this.label} transport recovered a different runtime`);
9979
+ }
9980
+ this.controller = init.controller;
9981
+ this.runtime = init.runtime;
9982
+ this._modelSettings = init.modelSettings ?? this._modelSettings;
9983
+ if (typeof init.model === "string" && init.model)
9984
+ this._model = init.model;
9985
+ if (init.tools !== undefined)
9986
+ this.toolNames = init.tools;
9987
+ this.removeMessageHandler = this.controller.onMessage((message) => {
9988
+ if (this.runtime)
9989
+ this.turns.handleProtocolMessage(message, this.runtime);
9990
+ });
9991
+ this.idleTransportDisconnected = false;
9992
+ await this.afterRuntimeInitialized();
9993
+ }).finally(() => {
9994
+ if (this.transportRecoveryPromise === recovery)
9995
+ this.transportRecoveryPromise = null;
9996
+ });
9997
+ this.transportRecoveryPromise = recovery;
9998
+ await recovery;
9999
+ }
10000
+ async recoverIdleTransport(_runtime) {
10001
+ throw new Error(`${this.label} sessions do not support idle transport recovery`);
10002
+ }
10003
+ onIdleTransportDisconnect() {}
10004
+ onRecoveredTransportDiscarded() {}
9893
10005
  async afterRuntimeInitialized() {}
9894
10006
  async beforeTurn() {}
9895
10007
  onCoreClose() {}
@@ -10011,111 +10123,6 @@ function agentToolNames(agent) {
10011
10123
  return typeof name === "string" && name.length > 0 ? [name] : [];
10012
10124
  });
10013
10125
  }
10014
- function isPresetSystemPrompt(value) {
10015
- return [
10016
- "default",
10017
- "letta-claude",
10018
- "letta-codex",
10019
- "letta-gemini",
10020
- "claude",
10021
- "codex",
10022
- "gemini"
10023
- ].includes(value);
10024
- }
10025
- function assertRemoteCreateAgentOptionsSupported(options) {
10026
- if (options.allowedTools !== undefined || options.disallowedTools !== undefined) {
10027
- throw new Error("App-server createAgent() does not yet support allowedTools/disallowedTools.");
10028
- }
10029
- if (options.canUseTool !== undefined) {
10030
- throw new Error("App-server createAgent() does not yet support canUseTool callbacks.");
10031
- }
10032
- if (options.systemInfoReminder !== undefined) {
10033
- throw new Error("App-server createAgent() does not yet support systemInfoReminder overrides.");
10034
- }
10035
- if (options.dreaming?.behavior !== undefined) {
10036
- throw new Error("App-server createAgent() does not yet support dreaming.behavior overrides.");
10037
- }
10038
- }
10039
- function normalizeMemoryBlock(block) {
10040
- const normalized = { ...block };
10041
- if (normalized.value === undefined && typeof normalized.content === "string") {
10042
- normalized.value = normalized.content;
10043
- }
10044
- return normalized;
10045
- }
10046
- function upsertMemoryBlock(blocks, block) {
10047
- const label = block.label;
10048
- if (typeof label === "string") {
10049
- const existingIndex = blocks.findIndex((candidate) => candidate.label === label);
10050
- if (existingIndex >= 0) {
10051
- blocks[existingIndex] = block;
10052
- return;
10053
- }
10054
- }
10055
- blocks.push(block);
10056
- }
10057
- async function createAgentBody(options, settings = {}) {
10058
- assertRemoteCreateAgentOptionsSupported(options);
10059
- const includeOriginTag = settings.includeSdkOriginTag ?? true;
10060
- const body = await buildInitialAgentBody(options);
10061
- if (Array.isArray(body.tags)) {
10062
- body.tags = body.tags.filter((tag) => (includeOriginTag || tag !== LETTA_CODE_ORIGIN_TAG) && (options.memfs !== false || tag !== GIT_MEMORY_ENABLED_TAG));
10063
- }
10064
- if (options.embedding !== undefined)
10065
- body.embedding = options.embedding;
10066
- if (options.hidden !== undefined)
10067
- body.hidden = options.hidden;
10068
- if (options.baseTools === undefined) {
10069
- delete body.tools;
10070
- delete body.include_base_tools;
10071
- delete body.include_base_tool_rules;
10072
- } else {
10073
- body.tools = options.baseTools;
10074
- body.include_base_tools = false;
10075
- body.include_base_tool_rules = false;
10076
- }
10077
- if (options.systemPrompt === undefined) {
10078
- if (options.memfs === false) {
10079
- body.system = buildSystemPrompt("default", "standard");
10080
- }
10081
- } else {
10082
- if (typeof options.systemPrompt === "string") {
10083
- if (isPresetSystemPrompt(options.systemPrompt)) {
10084
- throw new Error("createAgent() does not yet support system prompt presets for this backend.");
10085
- }
10086
- body.system = options.systemPrompt;
10087
- } else {
10088
- throw new Error("createAgent() does not yet support system prompt preset objects for this backend.");
10089
- }
10090
- }
10091
- const hasMemoryConfiguration = Array.isArray(body.memory_blocks) || options.memory !== undefined || options.persona !== undefined || options.human !== undefined;
10092
- const memoryBlocks = Array.isArray(body.memory_blocks) ? body.memory_blocks.filter((block) => block !== null && typeof block === "object").map((block) => ({ ...block })) : [];
10093
- const blockIds = [];
10094
- for (const item of options.memory ?? []) {
10095
- if (typeof item === "string") {
10096
- throw new Error("App-server createAgent() does not yet support memory preset names.");
10097
- }
10098
- if ("blockId" in item) {
10099
- blockIds.push(item.blockId);
10100
- } else {
10101
- upsertMemoryBlock(memoryBlocks, normalizeMemoryBlock(item));
10102
- }
10103
- }
10104
- if (options.persona !== undefined) {
10105
- upsertMemoryBlock(memoryBlocks, {
10106
- label: "persona",
10107
- value: options.persona
10108
- });
10109
- }
10110
- if (options.human !== undefined) {
10111
- upsertMemoryBlock(memoryBlocks, { label: "human", value: options.human });
10112
- }
10113
- if (hasMemoryConfiguration)
10114
- body.memory_blocks = memoryBlocks;
10115
- if (blockIds.length > 0)
10116
- body.block_ids = blockIds;
10117
- return body;
10118
- }
10119
10126
  function externalToolGroups(tools) {
10120
10127
  if (!tools || tools.length === 0)
10121
10128
  return;
@@ -10494,6 +10501,7 @@ class AppServerSession extends RemoteClientSessionCore {
10494
10501
  if (!response.success || !response.runtime) {
10495
10502
  throw new Error(response.error ?? "Failed to start app-server runtime");
10496
10503
  }
10504
+ this.watchTransportDisconnect(client);
10497
10505
  const tools = agentToolNames(response.agent);
10498
10506
  const skillSources = options.skillSources;
10499
10507
  const clientToolset = "toolset" in options ? options.toolset : undefined;
@@ -10576,9 +10584,7 @@ class AppServerSession extends RemoteClientSessionCore {
10576
10584
  command.external_tools = groups;
10577
10585
  if (this.mode.kind === "create-agent") {
10578
10586
  command.create_agent = {
10579
- body: await createAgentBody(this.mode.options, {
10580
- includeSdkOriginTag: this.remoteOptions.includeSdkOriginTag
10581
- }),
10587
+ body: await createAgentBody(this.mode.options),
10582
10588
  pin_global: this.remoteOptions.pinGlobalAgent ?? this.mode.options.hidden !== true,
10583
10589
  ...this.mode.options.memfs === false ? { memfs: false } : {}
10584
10590
  };
@@ -10739,6 +10745,132 @@ class CloudManagementTransport {
10739
10745
  }
10740
10746
  }
10741
10747
 
10748
+ // src/remote.ts
10749
+ function ensureOnline(environment, target) {
10750
+ if (!environment.connectionId) {
10751
+ const label = "deviceId" in target ? target.deviceId : ("environmentId" in target) ? target.environmentId : ("connectionName" in target) ? target.connectionName : environment.deviceId;
10752
+ throw new Error(`Computer is offline: ${label}`);
10753
+ }
10754
+ return {
10755
+ connectionId: environment.connectionId,
10756
+ environment,
10757
+ target
10758
+ };
10759
+ }
10760
+
10761
+ class RemoteEnvironmentClient {
10762
+ client;
10763
+ constructor(options = {}, client = createCloudClient({
10764
+ backend: "cloud",
10765
+ apiBaseUrl: options.baseUrl,
10766
+ apiKey: options.apiKey,
10767
+ headers: options.headers,
10768
+ fetch: options.fetch
10769
+ })) {
10770
+ this.client = client;
10771
+ }
10772
+ async listEnvironments(options = {}) {
10773
+ return await this.client.environments.list({
10774
+ after: options.after,
10775
+ limit: options.limit === undefined ? undefined : String(options.limit),
10776
+ onlineOnly: options.onlineOnly === undefined ? undefined : String(options.onlineOnly)
10777
+ });
10778
+ }
10779
+ async getEnvironmentByDeviceId(deviceId) {
10780
+ return await this.client.environments.retrieve(deviceId);
10781
+ }
10782
+ async resolveEnvironment(target) {
10783
+ if ("connectionId" in target) {
10784
+ return { connectionId: target.connectionId, target };
10785
+ }
10786
+ if ("deviceId" in target) {
10787
+ return ensureOnline(await this.getEnvironmentByDeviceId(target.deviceId), target);
10788
+ }
10789
+ const connections = [];
10790
+ let after;
10791
+ do {
10792
+ const page = await this.listEnvironments({ limit: 100, after });
10793
+ connections.push(...page.connections);
10794
+ if (!page.hasNextPage || page.connections.length === 0)
10795
+ break;
10796
+ after = page.connections.at(-1)?.id;
10797
+ } while (after !== undefined);
10798
+ if ("environmentId" in target) {
10799
+ const match = connections.find((env) => env.id === target.environmentId);
10800
+ if (!match) {
10801
+ throw new Error(`Remote environment not found: ${target.environmentId}`);
10802
+ }
10803
+ return ensureOnline(match, target);
10804
+ }
10805
+ const matches = connections.filter((env) => env.connectionName === target.connectionName);
10806
+ if (matches.length === 0) {
10807
+ throw new Error(`Computer not found: ${target.connectionName}`);
10808
+ }
10809
+ const onlineMatches = matches.filter((environment) => environment.connectionId !== null);
10810
+ if (onlineMatches.length === 0) {
10811
+ throw new Error(`Computer is offline: ${target.connectionName}`);
10812
+ }
10813
+ if (onlineMatches.length > 1) {
10814
+ const candidates = onlineMatches.map((environment) => `${environment.connectionName} (${environment.deviceId}, online)`).join(", ");
10815
+ throw new Error(`Computer name is ambiguous: ${target.connectionName}. Matches: ${candidates}. Select one by deviceId.`);
10816
+ }
10817
+ return ensureOnline(onlineMatches[0], target);
10818
+ }
10819
+ }
10820
+
10821
+ // src/computers.ts
10822
+ function selectorToRemoteTarget(selector) {
10823
+ if (typeof selector === "string")
10824
+ return { connectionName: selector };
10825
+ if ("name" in selector)
10826
+ return { connectionName: selector.name };
10827
+ if ("id" in selector)
10828
+ return { environmentId: selector.id };
10829
+ if ("connectionId" in selector) {
10830
+ return { connectionId: selector.connectionId };
10831
+ }
10832
+ return { deviceId: selector.deviceId };
10833
+ }
10834
+ function toComputer(environment) {
10835
+ return {
10836
+ id: environment.id,
10837
+ deviceId: environment.deviceId,
10838
+ connectionId: environment.connectionId,
10839
+ name: environment.connectionName,
10840
+ status: environment.connectionId ? "online" : "offline",
10841
+ connectedAt: environment.connectedAt,
10842
+ lastSeenAt: environment.lastSeenAt,
10843
+ metadata: environment.metadata
10844
+ };
10845
+ }
10846
+
10847
+ class ComputersClientImpl {
10848
+ environments;
10849
+ constructor(client) {
10850
+ this.environments = new RemoteEnvironmentClient({}, client);
10851
+ }
10852
+ async list(options = {}) {
10853
+ const result = await this.environments.listEnvironments(options);
10854
+ return {
10855
+ computers: result.connections.map(toComputer),
10856
+ hasNextPage: result.hasNextPage
10857
+ };
10858
+ }
10859
+ async get(deviceId) {
10860
+ if (typeof deviceId !== "string" || deviceId.trim().length === 0) {
10861
+ throw new Error("Invalid deviceId. Expected a non-empty string.");
10862
+ }
10863
+ return toComputer(await this.environments.getEnvironmentByDeviceId(deviceId));
10864
+ }
10865
+ async resolve(selector) {
10866
+ const result = await this.environments.resolveEnvironment(selectorToRemoteTarget(selector));
10867
+ return {
10868
+ connectionId: result.connectionId,
10869
+ computer: result.environment ? toComputer(result.environment) : undefined
10870
+ };
10871
+ }
10872
+ }
10873
+
10742
10874
  // src/cloud-status-transport.ts
10743
10875
  var CONNECTING = 0;
10744
10876
  var OPEN = 1;
@@ -10955,66 +11087,6 @@ function createCloudStatusTransportConstructor(options) {
10955
11087
  };
10956
11088
  }
10957
11089
 
10958
- // src/remote.ts
10959
- function ensureOnline(environment, target) {
10960
- if (!environment.connectionId) {
10961
- const label = "deviceId" in target ? target.deviceId : ("environmentId" in target) ? target.environmentId : ("connectionName" in target) ? target.connectionName : environment.deviceId;
10962
- throw new Error(`Remote environment is offline: ${label}`);
10963
- }
10964
- return {
10965
- connectionId: environment.connectionId,
10966
- environment,
10967
- target
10968
- };
10969
- }
10970
-
10971
- class RemoteEnvironmentClient {
10972
- client;
10973
- constructor(options = {}, client = createCloudClient({
10974
- backend: "cloud",
10975
- apiBaseUrl: options.baseUrl,
10976
- apiKey: options.apiKey,
10977
- headers: options.headers,
10978
- fetch: options.fetch
10979
- })) {
10980
- this.client = client;
10981
- }
10982
- async listEnvironments() {
10983
- return await this.client.environments.list();
10984
- }
10985
- async getEnvironmentByDeviceId(deviceId) {
10986
- return await this.client.environments.retrieve(deviceId);
10987
- }
10988
- async resolveEnvironment(target) {
10989
- if ("connectionId" in target) {
10990
- return { connectionId: target.connectionId, target };
10991
- }
10992
- if ("deviceId" in target) {
10993
- return ensureOnline(await this.getEnvironmentByDeviceId(target.deviceId), target);
10994
- }
10995
- const { connections } = await this.listEnvironments();
10996
- if ("environmentId" in target) {
10997
- const match2 = connections.find((env) => env.id === target.environmentId);
10998
- if (!match2) {
10999
- throw new Error(`Remote environment not found: ${target.environmentId}`);
11000
- }
11001
- return ensureOnline(match2, target);
11002
- }
11003
- const matches = connections.filter((env) => env.connectionName === target.connectionName);
11004
- if (matches.length === 0) {
11005
- throw new Error(`Remote environment not found: ${target.connectionName}`);
11006
- }
11007
- if (matches.length > 1) {
11008
- throw new Error(`Remote environment name is ambiguous: ${target.connectionName}`);
11009
- }
11010
- const match = matches[0];
11011
- if (!match) {
11012
- throw new Error(`Remote environment not found: ${target.connectionName}`);
11013
- }
11014
- return ensureOnline(match, target);
11015
- }
11016
- }
11017
-
11018
11090
  // src/cloud-sandbox.ts
11019
11091
  var MIN_TTL_MINUTES = 1;
11020
11092
  var MAX_TTL_MINUTES = 60;
@@ -11113,8 +11185,12 @@ function validatePositiveInteger3(value, name) {
11113
11185
  function validateCloudClientOptions(options) {
11114
11186
  validatePositiveInteger3(options.requestTimeoutMs, "requestTimeoutMs");
11115
11187
  validateCloudSandboxOptions(options.sandbox, "sandbox");
11116
- if (options.environment !== undefined && options.sandbox !== undefined) {
11117
- throw new Error("Letta Cloud sessions cannot specify both environment and sandbox options.");
11188
+ if (options.computer !== undefined && options.environment !== undefined) {
11189
+ throw new Error("Letta Cloud clients cannot specify both computer and deprecated environment.");
11190
+ }
11191
+ if ((options.computer !== undefined || options.environment !== undefined) && options.sandbox !== undefined) {
11192
+ const field = options.computer !== undefined ? "computer" : "environment";
11193
+ throw new Error(`Letta Cloud sessions cannot specify both ${field} and sandbox options.`);
11118
11194
  }
11119
11195
  if (options.webSocketAuth !== undefined && options.webSocketAuth !== "header" && options.webSocketAuth !== "query") {
11120
11196
  throw new Error("Invalid webSocketAuth. Valid values: header, query.");
@@ -11231,12 +11307,30 @@ class CloudEnvironmentSession extends RemoteClientSessionCore {
11231
11307
  throw error;
11232
11308
  });
11233
11309
  this.connectionId = connection.connectionId;
11310
+ const options = this.currentOptions();
11311
+ this.mcpBridge = await connectMcpServers("mcpServers" in options ? options.mcpServers : undefined, {
11312
+ cwd: options.cwd,
11313
+ reservedToolNames: this.externalTools.keys()
11314
+ });
11315
+ for (const tool of this.mcpBridge.tools) {
11316
+ this.externalTools.set(tool.name, tool);
11317
+ }
11318
+ try {
11319
+ return await this.openCloudTransport(resolved.runtime, connection.connectionId);
11320
+ } catch (error) {
11321
+ await this.closeMcpBridge();
11322
+ await this.cleanupManagedSandbox();
11323
+ await this.cleanupSessionRepositories(resolved.runtime.agent_id, resolved.runtime.conversation_id);
11324
+ throw error;
11325
+ }
11326
+ }
11327
+ async openCloudTransport(runtime, connectionId) {
11234
11328
  const apiKey = getCloudApiKey(this.cloudOptions);
11235
11329
  const url = buildCloudStatusWebSocketUrl({
11236
11330
  apiBaseUrl: this.cloudOptions.apiBaseUrl,
11237
- connectionId: connection.connectionId,
11238
- agentId: resolved.runtime.agent_id,
11239
- conversationId: resolved.runtime.conversation_id,
11331
+ connectionId,
11332
+ agentId: runtime.agent_id,
11333
+ conversationId: runtime.conversation_id,
11240
11334
  apiKey,
11241
11335
  authMode: this.cloudOptions.webSocketAuth ?? "header"
11242
11336
  });
@@ -11247,32 +11341,19 @@ class CloudEnvironmentSession extends RemoteClientSessionCore {
11247
11341
  WebSocket: getWebSocketConstructor(this.cloudOptions.WebSocket),
11248
11342
  ...(this.cloudOptions.webSocketAuth ?? "header") === "header" ? { headers: cloudWebSocketHeaders(this.cloudOptions) } : {},
11249
11343
  pingIntervalMs: this.cloudOptions.pingIntervalMs ?? DEFAULT_PING_INTERVAL_MS,
11250
- runtime: resolved.runtime
11344
+ runtime
11251
11345
  }),
11252
11346
  requestTimeoutMs: this.cloudOptions.requestTimeoutMs ?? DEFAULT_TURN_TIMEOUT_MS
11253
11347
  }));
11254
11348
  const options = this.currentOptions();
11255
- this.mcpBridge = await connectMcpServers("mcpServers" in options ? options.mcpServers : undefined, {
11256
- cwd: options.cwd,
11257
- reservedToolNames: this.externalTools.keys()
11258
- });
11259
- for (const tool of this.mcpBridge.tools) {
11260
- this.externalTools.set(tool.name, tool);
11261
- }
11262
- this.removeControlRequestHandler = registerAppServerControlRequestHandler({
11263
- client,
11264
- getRuntime: () => this.runtime,
11265
- getOptions: () => this.currentOptions()
11266
- });
11267
- if (this.externalTools.size > 0) {
11268
- this.removeExternalToolHandler = client.onExternalToolCall(createExternalToolCallHandler(this.externalTools));
11269
- }
11349
+ this.installTransportHandlers(client);
11270
11350
  try {
11271
11351
  await client.connect();
11272
- const response = await this.startCloudRuntime(client, resolved.runtime);
11352
+ const response = await this.startCloudRuntime(client, runtime);
11273
11353
  if (!response.success || !response.runtime) {
11274
11354
  throw new Error(response.error ?? "Failed to start Cloud status runtime");
11275
11355
  }
11356
+ this.watchTransportDisconnect(client, { recoverWhenIdle: true });
11276
11357
  const tools = agentToolNames(response.agent);
11277
11358
  const skillSources = options.skillSources;
11278
11359
  const clientToolset = "toolset" in options ? options.toolset : undefined;
@@ -11290,17 +11371,51 @@ class CloudEnvironmentSession extends RemoteClientSessionCore {
11290
11371
  ...skillSources !== undefined ? { skillSources: [...skillSources] } : {}
11291
11372
  };
11292
11373
  } catch (error) {
11293
- this.removeExternalToolHandler?.();
11294
- this.removeExternalToolHandler = null;
11295
- this.removeControlRequestHandler?.();
11296
- this.removeControlRequestHandler = null;
11297
- await this.closeMcpBridge();
11374
+ this.cleanupTransportHandlers();
11298
11375
  client.close();
11299
- await this.cleanupManagedSandbox();
11300
- await this.cleanupSessionRepositories(resolved.runtime.agent_id, resolved.runtime.conversation_id);
11301
11376
  throw error;
11302
11377
  }
11303
11378
  }
11379
+ installTransportHandlers(client) {
11380
+ this.cleanupTransportHandlers();
11381
+ this.removeControlRequestHandler = registerAppServerControlRequestHandler({
11382
+ client,
11383
+ getRuntime: () => this.runtime,
11384
+ getOptions: () => this.currentOptions()
11385
+ });
11386
+ if (this.externalTools.size > 0) {
11387
+ this.removeExternalToolHandler = client.onExternalToolCall(createExternalToolCallHandler(this.externalTools));
11388
+ }
11389
+ }
11390
+ cleanupTransportHandlers() {
11391
+ this.removeExternalToolHandler?.();
11392
+ this.removeExternalToolHandler = null;
11393
+ this.removeControlRequestHandler?.();
11394
+ this.removeControlRequestHandler = null;
11395
+ }
11396
+ async recoverIdleTransport(runtime) {
11397
+ const connection = await this.resolveRecoveryConnection(runtime);
11398
+ this.connectionId = connection.connectionId;
11399
+ return this.openCloudTransport(runtime, connection.connectionId);
11400
+ }
11401
+ async resolveRecoveryConnection(runtime) {
11402
+ const sandbox = this.managedSandbox;
11403
+ if (sandbox) {
11404
+ await this.refreshManagedSandbox(sandbox);
11405
+ return this.waitForManagedSandboxConnection(sandbox);
11406
+ }
11407
+ const environment = this.effectiveEnvironment();
11408
+ if (!environment) {
11409
+ throw new Error("Cloud idle transport recovery lost its execution target");
11410
+ }
11411
+ return this.resolveExplicitConnection(environment);
11412
+ }
11413
+ onIdleTransportDisconnect() {
11414
+ this.cleanupTransportHandlers();
11415
+ }
11416
+ onRecoveredTransportDiscarded() {
11417
+ this.cleanupTransportHandlers();
11418
+ }
11304
11419
  async startCloudRuntime(client, runtime) {
11305
11420
  const options = this.currentOptions();
11306
11421
  const command = {
@@ -11343,10 +11458,7 @@ class CloudEnvironmentSession extends RemoteClientSessionCore {
11343
11458
  await this.refreshManagedSandbox(sandbox);
11344
11459
  }
11345
11460
  onCoreClose() {
11346
- this.removeExternalToolHandler?.();
11347
- this.removeExternalToolHandler = null;
11348
- this.removeControlRequestHandler?.();
11349
- this.removeControlRequestHandler = null;
11461
+ this.cleanupTransportHandlers();
11350
11462
  this.mcpCleanup = this.closeMcpBridge();
11351
11463
  this.cleanupManagedSandbox();
11352
11464
  if (this.runtime?.agent_id) {
@@ -11670,8 +11782,8 @@ class CloudEnvironmentSession extends RemoteClientSessionCore {
11670
11782
  return new RemoteEnvironmentClient({}, this.apiClient);
11671
11783
  }
11672
11784
  effectiveEnvironment() {
11673
- const modeEnvironment = this.mode.kind === "session" ? this.mode.options.environment : undefined;
11674
- return modeEnvironment ?? this.cloudOptions.environment;
11785
+ const modeComputer = this.mode.kind === "session" ? this.mode.options.computer ?? this.mode.options.environment : undefined;
11786
+ return modeComputer ?? this.cloudOptions.computer ?? this.cloudOptions.environment;
11675
11787
  }
11676
11788
  effectiveSandboxOptions() {
11677
11789
  const modeSandbox = this.mode.kind === "session" ? this.mode.options.sandbox : undefined;
@@ -11971,14 +12083,17 @@ var VALID_BACKENDS = new Set([
11971
12083
  function isLettaCodeBackend(value) {
11972
12084
  return VALID_BACKENDS.has(value);
11973
12085
  }
11974
- function getOptionsEnvironment(options) {
11975
- if ("environment" in options) {
11976
- return options.environment;
12086
+ function getOptionsComputer(options) {
12087
+ if (!("computer" in options) && !("environment" in options))
12088
+ return;
12089
+ if (options.computer !== undefined && options.environment !== undefined) {
12090
+ throw new Error('Specify either "computer" or deprecated "environment", not both.');
11977
12091
  }
11978
- return;
12092
+ return options.computer ?? options.environment;
11979
12093
  }
11980
12094
  function stripCloudExecutionOptions(options) {
11981
12095
  const sessionOptions = { ...options };
12096
+ delete sessionOptions.computer;
11982
12097
  delete sessionOptions.environment;
11983
12098
  delete sessionOptions.sandbox;
11984
12099
  delete sessionOptions.filesystemConfinement;
@@ -11996,12 +12111,14 @@ function looksLikeConversationId(id) {
11996
12111
 
11997
12112
  class LettaAgentClientBase {
11998
12113
  backend;
12114
+ computer;
11999
12115
  environment;
12000
12116
  agents;
12001
12117
  conversations;
12002
12118
  models;
12003
12119
  options;
12004
12120
  repositoriesClient = null;
12121
+ computersClient = null;
12005
12122
  agentRepositoriesClient = null;
12006
12123
  cloudClient = null;
12007
12124
  managementTransport = null;
@@ -12011,16 +12128,19 @@ class LettaAgentClientBase {
12011
12128
  throw new Error(`Invalid Letta Code backend '${String(backend)}'. Valid values: local, remote, cloud.`);
12012
12129
  }
12013
12130
  this.backend = backend;
12014
- this.environment = getOptionsEnvironment(options);
12131
+ this.computer = getOptionsComputer(options);
12132
+ this.environment = this.computer;
12015
12133
  this.options = options;
12016
12134
  this.agents = createAgentsClient(() => this.getManagementTransport(), () => this.getAgentRepositoriesClient());
12017
12135
  this.conversations = createConversationsClient(() => this.getManagementTransport());
12018
12136
  this.models = createModelsClient(() => this.getManagementTransport());
12019
- if (this.backend === "local" && this.environment !== undefined) {
12020
- throw new Error('LettaAgentClient environment is only valid with backend: "cloud".');
12137
+ if (this.backend === "local" && this.computer !== undefined) {
12138
+ const field = "computer" in options && options.computer !== undefined ? "computer" : "environment";
12139
+ throw new Error(`LettaAgentClient ${field} is only valid with backend: "cloud".`);
12021
12140
  }
12022
- if (this.backend === "remote" && this.environment !== undefined) {
12023
- throw new Error('LettaAgentClient environment is only valid with backend: "cloud"; remote url selects the app-server runtime.');
12141
+ if (this.backend === "remote" && this.computer !== undefined) {
12142
+ const field = "computer" in options && options.computer !== undefined ? "computer" : "environment";
12143
+ throw new Error(`LettaAgentClient ${field} is only valid with backend: "cloud"; remote url selects the app-server runtime.`);
12024
12144
  }
12025
12145
  if (this.backend !== "cloud" && options.sandbox !== undefined) {
12026
12146
  throw new Error('LettaAgentClient sandbox options are only valid with backend: "cloud".');
@@ -12054,6 +12174,9 @@ class LettaAgentClientBase {
12054
12174
  get repositories() {
12055
12175
  return this.getRepositoriesClient();
12056
12176
  }
12177
+ get computers() {
12178
+ return this.getComputersClient();
12179
+ }
12057
12180
  async createAgent(options = {}) {
12058
12181
  if (hasCreateAgentEnvironment(options)) {
12059
12182
  throw new Error("createAgent() does not accept environment. Set a client default or pass environment to resumeSession()/createSession().");
@@ -12143,13 +12266,17 @@ class LettaAgentClientBase {
12143
12266
  }
12144
12267
  }
12145
12268
  assertSessionBackend(action, options) {
12269
+ if (options.computer !== undefined && options.environment !== undefined) {
12270
+ throw new Error(`${action}() cannot specify both computer and deprecated environment.`);
12271
+ }
12146
12272
  if (options.filesystemConfinement !== undefined && options.filesystemConfinement !== "memory") {
12147
12273
  throw new Error(`Invalid filesystemConfinement '${String(options.filesystemConfinement)}'. Valid value: memory.`);
12148
12274
  }
12149
- const effectiveEnvironment = options.environment ?? this.environment;
12275
+ const effectiveComputer = options.computer ?? options.environment ?? this.computer;
12150
12276
  if (this.backend === "local") {
12151
- if (effectiveEnvironment !== undefined) {
12152
- throw new Error(`${action}() environment overrides are only valid with backend: "cloud".`);
12277
+ if (effectiveComputer !== undefined) {
12278
+ const field = options.computer !== undefined ? "computer" : "environment";
12279
+ throw new Error(`${action}() ${field} overrides are only valid with backend: "cloud".`);
12153
12280
  }
12154
12281
  if (options.sandbox !== undefined) {
12155
12282
  throw new Error(`${action}() sandbox options are only valid with backend: "cloud".`);
@@ -12169,8 +12296,9 @@ class LettaAgentClientBase {
12169
12296
  if (options.filesystemConfinement !== undefined) {
12170
12297
  throw new Error(`${action}() filesystemConfinement requires an SDK-owned local app-server process.`);
12171
12298
  }
12172
- if (options.environment !== undefined) {
12173
- throw new Error(`${action}() environment overrides are only valid with backend: "cloud"; remote url selects the app-server runtime.`);
12299
+ if (options.computer !== undefined || options.environment !== undefined) {
12300
+ const field = options.computer !== undefined ? "computer" : "environment";
12301
+ throw new Error(`${action}() ${field} overrides are only valid with backend: "cloud"; remote url selects the app-server runtime.`);
12174
12302
  }
12175
12303
  if (options.sandbox !== undefined) {
12176
12304
  throw new Error(`${action}() sandbox options are only valid with backend: "cloud"; remote url selects the app-server runtime.`);
@@ -12185,11 +12313,13 @@ class LettaAgentClientBase {
12185
12313
  throw new Error(`${action}() filesystemConfinement is only supported with backend: "local".`);
12186
12314
  }
12187
12315
  const cloudOptions = this.cloudOptions();
12188
- if (cloudOptions.environment !== undefined && options.sandbox !== undefined) {
12189
- throw new Error(`Letta Cloud ${action}() cannot specify sandbox options when the client has a default environment.`);
12316
+ if (this.computer !== undefined && options.sandbox !== undefined) {
12317
+ const field = cloudOptions.computer !== undefined ? "computer" : "environment";
12318
+ throw new Error(`Letta Cloud ${action}() cannot specify sandbox options when the client has a default ${field}.`);
12190
12319
  }
12191
- if (cloudOptions.sandbox !== undefined && options.environment !== undefined) {
12192
- throw new Error(`Letta Cloud ${action}() cannot specify an environment when the client has default sandbox options.`);
12320
+ if (cloudOptions.sandbox !== undefined && (options.computer !== undefined || options.environment !== undefined)) {
12321
+ const field = options.computer !== undefined ? "a computer" : "an environment";
12322
+ throw new Error(`Letta Cloud ${action}() cannot specify ${field} when the client has default sandbox options.`);
12193
12323
  }
12194
12324
  assertCloudSessionOptionsSupported(action, options);
12195
12325
  return;
@@ -12220,6 +12350,13 @@ class LettaAgentClientBase {
12220
12350
  appServerSessionOptions() {
12221
12351
  return this.remoteOptions();
12222
12352
  }
12353
+ getComputersClient() {
12354
+ if (this.backend !== "cloud") {
12355
+ throw new Error('client.computers is only available with backend: "cloud".');
12356
+ }
12357
+ this.computersClient ??= new ComputersClientImpl(this.getCloudClient());
12358
+ return this.computersClient;
12359
+ }
12223
12360
  getRepositoriesClient() {
12224
12361
  if (this.backend !== "cloud") {
12225
12362
  throw new Error('client.repositories is only available with backend: "cloud".');
@@ -12355,4 +12492,4 @@ export {
12355
12492
  CloudManagedSandboxExpiredError
12356
12493
  };
12357
12494
 
12358
- //# debugId=F422AB0A96BFDBF164756E2164756E21
12495
+ //# debugId=6E490415C5923C8964756E2164756E21