@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
package/dist/index.js CHANGED
@@ -3195,36 +3195,6 @@ function appServerSocketOptions(authToken) {
3195
3195
  }
3196
3196
  return { headers: { Authorization: `Bearer ${token}` } };
3197
3197
  }
3198
- function sameRuntime(a, b) {
3199
- return a?.agent_id === b.agent_id && a?.conversation_id === b.conversation_id;
3200
- }
3201
- function isWaitingLoopStatus(message) {
3202
- return message.loop_status.status === "WAITING_ON_INPUT";
3203
- }
3204
- function isWaitingOnApprovalLoopStatus(message) {
3205
- return message.loop_status.status === "WAITING_ON_APPROVAL";
3206
- }
3207
- function streamDeltaRunId(message) {
3208
- const runId = message.delta.run_id;
3209
- return typeof runId === "string" ? runId : null;
3210
- }
3211
- function streamDeltaMessageType(message) {
3212
- const messageType = message.delta.message_type;
3213
- return typeof messageType === "string" ? messageType : null;
3214
- }
3215
- function streamDeltaStopReason(message) {
3216
- const stopReason = message.delta.stop_reason;
3217
- return typeof stopReason === "string" ? stopReason : null;
3218
- }
3219
- function streamDeltaErrorMessage(message) {
3220
- const delta = message.delta;
3221
- const apiMessage = delta.api_error?.message ?? delta.api_error?.detail;
3222
- if (typeof apiMessage === "string" && apiMessage.length > 0)
3223
- return apiMessage;
3224
- if (typeof delta.message === "string" && delta.message.length > 0)
3225
- return delta.message;
3226
- return "App-server turn failed";
3227
- }
3228
3198
 
3229
3199
  class AppServerClient {
3230
3200
  socket;
@@ -3235,7 +3205,6 @@ class AppServerClient {
3235
3205
  messageHandlers = new Set;
3236
3206
  sendHandlers = new Set;
3237
3207
  disconnectHandlers = new Set;
3238
- activeTurnRuntimes = new Set;
3239
3208
  explicitlyClosed = false;
3240
3209
  disconnectNotified = false;
3241
3210
  nextRequestNumber = 0;
@@ -3418,111 +3387,15 @@ class AppServerClient {
3418
3387
  input(command) {
3419
3388
  this.send({ type: "input", ...command });
3420
3389
  }
3421
- runTurn(command, options = {}) {
3422
- const runtimeKey = `${command.runtime.agent_id}/${command.runtime.conversation_id}`;
3423
- if (this.activeTurnRuntimes.has(runtimeKey)) {
3424
- return Promise.reject(new Error(`A turn is already in flight for ${runtimeKey}`));
3425
- }
3426
- this.activeTurnRuntimes.add(runtimeKey);
3427
- const timeoutMs = options.timeoutMs ?? this.requestTimeoutMs;
3428
- const commandWithIds = this.withClientMessageIds(command);
3429
- const runIds = new Set;
3430
- let observedTurnEvidence = false;
3431
- let observedRequiresApprovalStop = false;
3432
- return new Promise((resolve, reject) => {
3433
- const timeout = setTimeout(() => {
3434
- cleanup();
3435
- reject(new Error(`Timed out waiting for app-server turn on ${command.runtime.agent_id}/${command.runtime.conversation_id}`));
3436
- }, timeoutMs);
3437
- const cleanup = () => {
3438
- clearTimeout(timeout);
3439
- this.activeTurnRuntimes.delete(runtimeKey);
3440
- offMessage();
3441
- };
3442
- const finish = (completedBy, terminalMessage, stopReason) => {
3443
- cleanup();
3444
- resolve({
3445
- runtime: command.runtime,
3446
- stopReason,
3447
- runIds: [...runIds],
3448
- clientMessageIds: commandWithIds.clientMessageIds,
3449
- completedBy,
3450
- terminalMessage
3451
- });
3452
- };
3453
- const fail = (error) => {
3454
- cleanup();
3455
- reject(error);
3456
- };
3457
- const offMessage = this.onMessage((message) => {
3458
- if (!sameRuntime(message.runtime, command.runtime)) {
3459
- return;
3460
- }
3461
- if (message.type === "stream_delta") {
3462
- observedTurnEvidence = true;
3463
- const runId = streamDeltaRunId(message);
3464
- if (runId)
3465
- runIds.add(runId);
3466
- const messageType = streamDeltaMessageType(message);
3467
- if (messageType === "loop_error" || messageType === "error_message") {
3468
- fail(new Error(streamDeltaErrorMessage(message)));
3469
- return;
3470
- }
3471
- if (messageType === "stop_reason") {
3472
- const stopReason = streamDeltaStopReason(message);
3473
- if (stopReason === "requires_approval") {
3474
- observedRequiresApprovalStop = true;
3475
- return;
3476
- }
3477
- finish("stop_reason", message, stopReason);
3478
- }
3479
- return;
3480
- }
3481
- if (message.type === "update_loop_status") {
3482
- const hadTurnEvidenceBeforeLoopStatus = observedTurnEvidence || observedRequiresApprovalStop;
3483
- if (!hadTurnEvidenceBeforeLoopStatus && (isWaitingOnApprovalLoopStatus(message) || options.allowLoopStatusFallback === true && isWaitingLoopStatus(message))) {
3484
- return;
3485
- }
3486
- for (const runId of message.loop_status.active_run_ids) {
3487
- observedTurnEvidence = true;
3488
- runIds.add(runId);
3489
- }
3490
- if (hadTurnEvidenceBeforeLoopStatus && isWaitingOnApprovalLoopStatus(message)) {
3491
- finish("loop_status_waiting_on_approval", message, "requires_approval");
3492
- return;
3493
- }
3494
- if (options.allowLoopStatusFallback === true && hadTurnEvidenceBeforeLoopStatus && isWaitingLoopStatus(message)) {
3495
- finish("loop_status_waiting_fallback", message, null);
3496
- }
3497
- }
3498
- });
3499
- try {
3500
- this.input(commandWithIds.command);
3501
- } catch (error) {
3502
- fail(error instanceof Error ? error : new Error(String(error)));
3503
- }
3504
- });
3505
- }
3506
- withClientMessageIds(command) {
3507
- if (command.payload.kind !== "create_message") {
3508
- return { command, clientMessageIds: [] };
3509
- }
3510
- const clientMessageIds = [];
3511
- const messages = command.payload.messages.map((message) => {
3512
- if (message.role !== "user")
3513
- return message;
3514
- const existing = message.client_message_id;
3515
- const clientMessageId = typeof existing === "string" && existing.length > 0 ? existing : this.nextRequestId("client-message");
3516
- clientMessageIds.push(clientMessageId);
3517
- return { ...message, client_message_id: clientMessageId };
3390
+ submitInput(command, options = {}) {
3391
+ return this.request({
3392
+ type: "input",
3393
+ request_id: command.request_id ?? this.nextRequestId("input"),
3394
+ ...command
3395
+ }, {
3396
+ ...options,
3397
+ predicate: (message) => message.type === "input_accepted"
3518
3398
  });
3519
- return {
3520
- command: {
3521
- ...command,
3522
- payload: { ...command.payload, messages }
3523
- },
3524
- clientMessageIds
3525
- };
3526
3399
  }
3527
3400
  handleMessage(event, channel) {
3528
3401
  const message = parseProtocolMessage(event);
@@ -6456,6 +6329,73 @@ var models_default = {
6456
6329
  parallel_tool_calls: true
6457
6330
  }
6458
6331
  },
6332
+ {
6333
+ id: "opus-5",
6334
+ handle: "anthropic/claude-opus-5",
6335
+ label: "Opus 5",
6336
+ description: "Opus 5 (high reasoning)",
6337
+ updateArgs: {
6338
+ context_window: 200000,
6339
+ max_output_tokens: 128000,
6340
+ enable_reasoner: true,
6341
+ reasoning_effort: "high",
6342
+ parallel_tool_calls: true
6343
+ }
6344
+ },
6345
+ {
6346
+ id: "opus-5-low",
6347
+ handle: "anthropic/claude-opus-5",
6348
+ label: "Opus 5",
6349
+ description: "Opus 5 (low reasoning)",
6350
+ updateArgs: {
6351
+ context_window: 200000,
6352
+ max_output_tokens: 128000,
6353
+ enable_reasoner: true,
6354
+ reasoning_effort: "low",
6355
+ max_reasoning_tokens: 4000,
6356
+ parallel_tool_calls: true
6357
+ }
6358
+ },
6359
+ {
6360
+ id: "opus-5-medium",
6361
+ handle: "anthropic/claude-opus-5",
6362
+ label: "Opus 5",
6363
+ description: "Opus 5 (med reasoning)",
6364
+ updateArgs: {
6365
+ context_window: 200000,
6366
+ max_output_tokens: 128000,
6367
+ enable_reasoner: true,
6368
+ reasoning_effort: "medium",
6369
+ max_reasoning_tokens: 12000,
6370
+ parallel_tool_calls: true
6371
+ }
6372
+ },
6373
+ {
6374
+ id: "opus-5-xhigh",
6375
+ handle: "anthropic/claude-opus-5",
6376
+ label: "Opus 5",
6377
+ description: "Opus 5 (extra-high reasoning)",
6378
+ updateArgs: {
6379
+ context_window: 200000,
6380
+ max_output_tokens: 128000,
6381
+ enable_reasoner: true,
6382
+ reasoning_effort: "xhigh",
6383
+ parallel_tool_calls: true
6384
+ }
6385
+ },
6386
+ {
6387
+ id: "opus-5-max",
6388
+ handle: "anthropic/claude-opus-5",
6389
+ label: "Opus 5",
6390
+ description: "Opus 5 (max reasoning)",
6391
+ updateArgs: {
6392
+ context_window: 200000,
6393
+ max_output_tokens: 128000,
6394
+ enable_reasoner: true,
6395
+ reasoning_effort: "max",
6396
+ parallel_tool_calls: true
6397
+ }
6398
+ },
6459
6399
  {
6460
6400
  id: "opus",
6461
6401
  handle: "anthropic/claude-opus-4-8",
@@ -6730,7 +6670,7 @@ var models_default = {
6730
6670
  id: "sonnet-5-xhigh",
6731
6671
  handle: "anthropic/claude-sonnet-5",
6732
6672
  label: "Sonnet 5",
6733
- description: "Sonnet 5 (max reasoning)",
6673
+ description: "Sonnet 5 (extra-high reasoning)",
6734
6674
  updateArgs: {
6735
6675
  context_window: 1e6,
6736
6676
  max_output_tokens: 128000,
@@ -6739,6 +6679,19 @@ var models_default = {
6739
6679
  parallel_tool_calls: true
6740
6680
  }
6741
6681
  },
6682
+ {
6683
+ id: "sonnet-5-max",
6684
+ handle: "anthropic/claude-sonnet-5",
6685
+ label: "Sonnet 5",
6686
+ description: "Sonnet 5 (max reasoning)",
6687
+ updateArgs: {
6688
+ context_window: 1e6,
6689
+ max_output_tokens: 128000,
6690
+ reasoning_effort: "max",
6691
+ enable_reasoner: true,
6692
+ parallel_tool_calls: true
6693
+ }
6694
+ },
6742
6695
  {
6743
6696
  id: "sonnet-4.6",
6744
6697
  handle: "anthropic/claude-sonnet-4-6",
@@ -8289,7 +8242,6 @@ var models_default = {
8289
8242
  ]
8290
8243
  };
8291
8244
  var models = models_default.models;
8292
- var MODEL_PRESETS = models;
8293
8245
  function resolveModel(modelIdentifier) {
8294
8246
  const byId = models.find((m) => m.id === modelIdentifier);
8295
8247
  if (byId)
@@ -8517,75 +8469,128 @@ function buildPersonalityMemoryBlocks(personalityId, defaultMemoryBlocks, enviro
8517
8469
  }
8518
8470
  var LETTA_CODE_AGENT_TYPE = "letta_v1_agent";
8519
8471
  var DEFAULT_CREATED_AGENT_BASE_TOOLS = ["web_search", "fetch_webpage"];
8520
- async function buildCreateAgentRequestForPersonality(params) {
8521
- const { personalityId, name, description, model, extraTags } = params;
8522
- const personality = getPersonalityOption(personalityId);
8523
- const modelIdentifier = model ?? personality.defaultModel;
8472
+ function mergeMemoryBlocks(base, overrides) {
8473
+ const blocks = base.map((block) => ({ ...block }));
8474
+ for (const override of overrides ?? []) {
8475
+ const index = blocks.findIndex((block) => block.label === override.label);
8476
+ if (index >= 0) {
8477
+ blocks[index] = { ...override };
8478
+ } else {
8479
+ blocks.push({ ...override });
8480
+ }
8481
+ }
8482
+ return blocks;
8483
+ }
8484
+ async function buildCreateAgentRequest(options = {}) {
8485
+ const personality = options.personalityId ? getPersonalityOption(options.personalityId) : undefined;
8486
+ const modelIdentifier = options.model ?? personality?.defaultModel;
8524
8487
  const modelHandle = modelIdentifier ? resolveModel(modelIdentifier) : getDefaultModel();
8525
8488
  if (!modelHandle) {
8526
8489
  throw new Error(`Unknown model: ${modelIdentifier}`);
8527
8490
  }
8528
- const defaultMemoryBlocks = await getDefaultMemoryBlocks();
8491
+ if (!options.isSubagent && options.enableMemfs !== undefined && options.memoryPromptMode !== undefined && options.enableMemfs !== (options.memoryPromptMode !== "standard")) {
8492
+ throw new Error("enableMemfs and memoryPromptMode must describe the same memory mode");
8493
+ }
8494
+ const enableMemfs = options.isSubagent ? false : options.enableMemfs ?? options.memoryPromptMode !== "standard";
8495
+ const memoryPromptMode = options.isSubagent ? "standard" : options.memoryPromptMode ?? (enableMemfs ? "memfs" : "standard");
8496
+ const personalityTags = options.personalityId ? getPersonalityCreationTags(options.personalityId) : [];
8497
+ const personalityBlocks = options.personalityId ? buildPersonalityMemoryBlocks(options.personalityId, await getDefaultMemoryBlocks()) : [];
8498
+ const memoryBlocks = options.isSubagent ? undefined : options.personalityId || options.memoryBlocks !== undefined ? mergeMemoryBlocks(personalityBlocks, options.memoryBlocks) : undefined;
8499
+ const blockIds = options.isSubagent ? undefined : options.blockIds;
8529
8500
  return {
8530
8501
  agent_type: LETTA_CODE_AGENT_TYPE,
8531
- name: name ?? personality.label,
8532
- description: description ?? personality.description,
8502
+ ...options.name !== undefined || personality ? { name: options.name ?? personality?.label } : {},
8503
+ ...options.description !== undefined || personality ? { description: options.description ?? personality?.description } : {},
8533
8504
  model: modelHandle,
8534
- system: buildSystemPrompt("default", "memfs"),
8535
- memory_blocks: buildPersonalityMemoryBlocks(personalityId, defaultMemoryBlocks),
8505
+ system: options.system ?? buildSystemPrompt("default", memoryPromptMode),
8506
+ ...memoryBlocks !== undefined ? { memory_blocks: memoryBlocks } : {},
8507
+ ...blockIds && blockIds.length > 0 ? { block_ids: blockIds } : {},
8536
8508
  tags: buildCreatedAgentTags({
8537
- enableMemfs: true,
8538
- tags: [
8539
- ...getPersonalityCreationTags(personalityId),
8540
- ...extraTags ?? []
8541
- ]
8509
+ enableMemfs,
8510
+ isSubagent: options.isSubagent,
8511
+ tags: [...personalityTags, ...options.extraTags ?? []]
8542
8512
  }),
8543
- tools: [...DEFAULT_CREATED_AGENT_BASE_TOOLS],
8513
+ tools: [...options.baseTools ?? DEFAULT_CREATED_AGENT_BASE_TOOLS],
8544
8514
  include_base_tools: false,
8545
8515
  include_base_tool_rules: false,
8546
8516
  initial_message_sequence: [],
8547
- parallel_tool_calls: true,
8548
- compaction_settings: { model: DEFAULT_SUMMARIZATION_MODEL }
8517
+ parallel_tool_calls: options.parallelToolCalls ?? true,
8518
+ compaction_settings: {
8519
+ model: options.compactionModel ?? DEFAULT_SUMMARIZATION_MODEL
8520
+ },
8521
+ ...options.embedding !== undefined ? { embedding: options.embedding } : {},
8522
+ ...options.isSubagent ? { hidden: true } : options.hidden !== undefined ? { hidden: options.hidden } : {}
8549
8523
  };
8550
8524
  }
8551
8525
 
8552
8526
  // src/agent-creation.ts
8553
- var DEFAULT_MODEL_ID = "auto";
8554
- var DEFAULT_SUMMARIZATION_MODEL2 = "letta/auto";
8555
- function resolveModel2(modelIdentifier) {
8556
- const preset = MODEL_PRESETS.find((model) => model.id === modelIdentifier || model.handle === modelIdentifier);
8557
- if (preset)
8558
- return preset.handle;
8559
- if (modelIdentifier.includes("/"))
8560
- return modelIdentifier;
8561
- throw new Error(`Unknown model: ${modelIdentifier}`);
8527
+ function isPresetSystemPrompt(value) {
8528
+ return [
8529
+ "default",
8530
+ "letta-claude",
8531
+ "letta-codex",
8532
+ "letta-gemini",
8533
+ "claude",
8534
+ "codex",
8535
+ "gemini"
8536
+ ].includes(value);
8562
8537
  }
8563
- async function buildInitialAgentBody(options) {
8564
- if (options.personality !== undefined) {
8565
- return {
8566
- ...await buildCreateAgentRequestForPersonality({
8567
- personalityId: options.personality,
8568
- ...options.name !== undefined ? { name: options.name } : {},
8569
- ...options.description !== undefined ? { description: options.description } : {},
8570
- ...options.model !== undefined ? { model: options.model } : {},
8571
- ...options.tags !== undefined ? { extraTags: options.tags } : {}
8572
- })
8573
- };
8538
+ function assertCreateAgentOptionsSupported(options) {
8539
+ if (options.allowedTools !== undefined || options.disallowedTools !== undefined) {
8540
+ throw new Error("App-server createAgent() does not yet support allowedTools/disallowedTools.");
8541
+ }
8542
+ if (options.canUseTool !== undefined) {
8543
+ throw new Error("App-server createAgent() does not yet support canUseTool callbacks.");
8544
+ }
8545
+ if (options.systemInfoReminder !== undefined) {
8546
+ throw new Error("App-server createAgent() does not yet support systemInfoReminder overrides.");
8547
+ }
8548
+ if (options.dreaming?.behavior !== undefined) {
8549
+ throw new Error("App-server createAgent() does not yet support dreaming.behavior overrides.");
8574
8550
  }
8575
- return {
8576
- agent_type: LETTA_CODE_AGENT_TYPE,
8577
- ...options.name !== undefined ? { name: options.name } : {},
8578
- ...options.description !== undefined ? { description: options.description } : {},
8579
- model: resolveModel2(options.model ?? DEFAULT_MODEL_ID),
8580
- system: buildSystemPrompt("default", "memfs"),
8581
- tags: buildCreatedAgentTags({
8582
- enableMemfs: true,
8583
- tags: options.tags ?? []
8584
- }),
8585
- initial_message_sequence: [],
8586
- parallel_tool_calls: true,
8587
- compaction_settings: { model: DEFAULT_SUMMARIZATION_MODEL2 }
8588
- };
8551
+ }
8552
+ async function createAgentBody(options) {
8553
+ assertCreateAgentOptionsSupported(options);
8554
+ let system;
8555
+ if (options.systemPrompt !== undefined) {
8556
+ if (typeof options.systemPrompt !== "string" || isPresetSystemPrompt(options.systemPrompt)) {
8557
+ throw new Error("createAgent() does not yet support system prompt presets for this backend.");
8558
+ }
8559
+ system = options.systemPrompt;
8560
+ }
8561
+ const memoryBlocks = [];
8562
+ const blockIds = [];
8563
+ for (const item of options.memory ?? []) {
8564
+ if (typeof item === "string") {
8565
+ throw new Error("App-server createAgent() does not yet support memory preset names.");
8566
+ }
8567
+ if ("blockId" in item) {
8568
+ blockIds.push(item.blockId);
8569
+ } else {
8570
+ memoryBlocks.push({ ...item });
8571
+ }
8572
+ }
8573
+ if (options.persona !== undefined) {
8574
+ memoryBlocks.push({ label: "persona", value: options.persona });
8575
+ }
8576
+ if (options.human !== undefined) {
8577
+ memoryBlocks.push({ label: "human", value: options.human });
8578
+ }
8579
+ const hasMemoryConfiguration = options.memory !== undefined || options.persona !== undefined || options.human !== undefined;
8580
+ return buildCreateAgentRequest({
8581
+ personalityId: options.personality,
8582
+ name: options.name,
8583
+ description: options.description,
8584
+ model: options.model,
8585
+ system,
8586
+ memoryBlocks: hasMemoryConfiguration ? memoryBlocks : undefined,
8587
+ blockIds,
8588
+ extraTags: options.tags,
8589
+ enableMemfs: options.memfs ?? true,
8590
+ baseTools: options.baseTools,
8591
+ embedding: options.embedding,
8592
+ hidden: options.hidden
8593
+ });
8589
8594
  }
8590
8595
 
8591
8596
  // src/interactiveToolPolicy.ts
@@ -8877,7 +8882,7 @@ function firstToolReturn(delta) {
8877
8882
  }
8878
8883
  return;
8879
8884
  }
8880
- function sameRuntime2(message, runtime) {
8885
+ function sameRuntime(message, runtime) {
8881
8886
  const msgRuntime = message.runtime;
8882
8887
  if (msgRuntime) {
8883
8888
  return msgRuntime.agent_id === runtime.agent_id && msgRuntime.conversation_id === runtime.conversation_id;
@@ -8896,10 +8901,10 @@ function streamDeltaRecord(message) {
8896
8901
  const delta = message.delta;
8897
8902
  return delta && typeof delta === "object" && !Array.isArray(delta) ? delta : null;
8898
8903
  }
8899
- function streamDeltaMessageType2(delta) {
8904
+ function streamDeltaMessageType(delta) {
8900
8905
  return typeof delta.message_type === "string" ? delta.message_type : undefined;
8901
8906
  }
8902
- function streamDeltaRunId2(delta) {
8907
+ function streamDeltaRunId(delta) {
8903
8908
  return typeof delta.run_id === "string" ? delta.run_id : undefined;
8904
8909
  }
8905
8910
  function streamDeltaOtid(delta) {
@@ -8908,7 +8913,7 @@ function streamDeltaOtid(delta) {
8908
8913
  function streamDeltaSeqId(delta) {
8909
8914
  return typeof delta.seq_id === "number" ? delta.seq_id : undefined;
8910
8915
  }
8911
- function streamDeltaStopReason2(delta) {
8916
+ function streamDeltaStopReason(delta) {
8912
8917
  return typeof delta.stop_reason === "string" ? delta.stop_reason : undefined;
8913
8918
  }
8914
8919
  function loopStatusRecord(message) {
@@ -9131,7 +9136,7 @@ class RemoteTurnCoordinator {
9131
9136
  this.activeTurn.abortRequested = true;
9132
9137
  }
9133
9138
  handleProtocolMessage(message, runtime) {
9134
- if (!sameRuntime2(message, runtime))
9139
+ if (!sameRuntime(message, runtime))
9135
9140
  return;
9136
9141
  const statusRecord = deviceStatusRecord(message);
9137
9142
  if (statusRecord) {
@@ -9158,7 +9163,7 @@ class RemoteTurnCoordinator {
9158
9163
  const active = this.activateNextTurnFromProtocol();
9159
9164
  if (active) {
9160
9165
  active.observedTurnEvidence = true;
9161
- const runId = streamDeltaRunId2(delta);
9166
+ const runId = streamDeltaRunId(delta);
9162
9167
  if (runId)
9163
9168
  active.runIds.add(runId);
9164
9169
  }
@@ -9177,6 +9182,24 @@ class RemoteTurnCoordinator {
9177
9182
  this.streamResolvers.push(resolve);
9178
9183
  });
9179
9184
  }
9185
+ closeWithError(detail) {
9186
+ if (this.closed)
9187
+ return;
9188
+ const active = this.activeTurn;
9189
+ if (active) {
9190
+ this.failTurn(active, detail);
9191
+ } else {
9192
+ this.enqueue({
9193
+ type: "error",
9194
+ message: detail,
9195
+ errorCode: "error",
9196
+ stopReason: "error",
9197
+ errorDetail: detail,
9198
+ recoverable: false
9199
+ });
9200
+ }
9201
+ this.close();
9202
+ }
9180
9203
  close() {
9181
9204
  if (this.closed)
9182
9205
  return;
@@ -9294,9 +9317,9 @@ class RemoteTurnCoordinator {
9294
9317
  const active = this.activeTurn;
9295
9318
  if (!active)
9296
9319
  return;
9297
- const messageType = streamDeltaMessageType2(delta);
9320
+ const messageType = streamDeltaMessageType(delta);
9298
9321
  if (messageType === "stop_reason") {
9299
- const stopReason = streamDeltaStopReason2(delta) ?? null;
9322
+ const stopReason = streamDeltaStopReason(delta) ?? null;
9300
9323
  if (stopReason === "requires_approval") {
9301
9324
  active.observedRequiresApprovalStop = true;
9302
9325
  return;
@@ -9490,6 +9513,10 @@ class RemoteClientSessionCore {
9490
9513
  requestTimeoutMs;
9491
9514
  initializePromise = null;
9492
9515
  removeMessageHandler = null;
9516
+ detachTransportDisconnect = null;
9517
+ idleTransportDisconnected = false;
9518
+ transportRecoveryPromise = null;
9519
+ transportDisconnectGeneration = 0;
9493
9520
  turns;
9494
9521
  toolNames;
9495
9522
  deviceStatusListeners = new Set;
@@ -9564,6 +9591,8 @@ class RemoteClientSessionCore {
9564
9591
  cleanupFailedInitialize() {
9565
9592
  this.removeMessageHandler?.();
9566
9593
  this.removeMessageHandler = null;
9594
+ this.detachTransportDisconnect?.();
9595
+ this.detachTransportDisconnect = null;
9567
9596
  this.controller?.close();
9568
9597
  this.controller = null;
9569
9598
  this.onCoreClose();
@@ -9577,16 +9606,24 @@ class RemoteClientSessionCore {
9577
9606
  this.initialized = false;
9578
9607
  }
9579
9608
  async send(message) {
9609
+ if (this.closed)
9610
+ throw new Error("Session is closed");
9580
9611
  if (!this.initialized) {
9581
9612
  await this.initialize();
9582
9613
  }
9614
+ await this.recoverIdleTransportIfNeeded();
9583
9615
  if (!this.controller || !this.runtime) {
9584
9616
  throw new Error("Session is not initialized");
9585
9617
  }
9586
9618
  await this.beforeTurn();
9587
- const turn = this.turns.trackSentTurn(this.runtime);
9619
+ const controller = this.controller;
9620
+ const runtime = this.runtime;
9621
+ if (!controller || !runtime) {
9622
+ throw new Error("Session transport disconnected before the turn was sent");
9623
+ }
9624
+ const turn = this.turns.trackSentTurn(runtime);
9588
9625
  try {
9589
- this.controller.sendTurnMessage(this.runtime, message, {
9626
+ controller.sendTurnMessage(runtime, message, {
9590
9627
  clientMessageId: turn.clientMessageId
9591
9628
  });
9592
9629
  } catch (error) {
@@ -9851,6 +9888,8 @@ class RemoteClientSessionCore {
9851
9888
  this.closed = true;
9852
9889
  this.removeMessageHandler?.();
9853
9890
  this.removeMessageHandler = null;
9891
+ this.detachTransportDisconnect?.();
9892
+ this.detachTransportDisconnect = null;
9854
9893
  this.turns.close();
9855
9894
  for (const cancel of [...this.deviceStatusRefreshCancels]) {
9856
9895
  cancel(new Error(`Session closed while waiting for ${this.label} device status`));
@@ -9859,6 +9898,7 @@ class RemoteClientSessionCore {
9859
9898
  this.deviceStatusListeners.clear();
9860
9899
  this.controller?.close();
9861
9900
  this.controller = null;
9901
+ this.idleTransportDisconnected = false;
9862
9902
  this.onCoreClose();
9863
9903
  }
9864
9904
  get agentId() {
@@ -9911,6 +9951,78 @@ class RemoteClientSessionCore {
9911
9951
  }, { predicate: (message) => message.type === "update_toolset_response" });
9912
9952
  ensureSuccess(response, "Failed to update toolset");
9913
9953
  }
9954
+ watchTransportDisconnect(client, options = {}) {
9955
+ this.detachTransportDisconnect?.();
9956
+ this.detachTransportDisconnect = client.onDisconnect(() => {
9957
+ if (this.closed)
9958
+ return;
9959
+ this.transportDisconnectGeneration += 1;
9960
+ if (options.recoverWhenIdle && !this.turns.hasInFlightTurn()) {
9961
+ this.detachTransportDisconnect?.();
9962
+ this.detachTransportDisconnect = null;
9963
+ this.removeMessageHandler?.();
9964
+ this.removeMessageHandler = null;
9965
+ this.controller?.close();
9966
+ this.controller = null;
9967
+ const error = new Error(`${this.label} connection closed before send`);
9968
+ for (const cancel of [...this.deviceStatusRefreshCancels])
9969
+ cancel(error);
9970
+ this.deviceStatusRefreshCancels.clear();
9971
+ this.onIdleTransportDisconnect();
9972
+ this.idleTransportDisconnected = true;
9973
+ return;
9974
+ }
9975
+ this.turns.closeWithError(`The ${this.label} connection closed unexpectedly; resume the conversation to continue.`);
9976
+ this.close();
9977
+ });
9978
+ }
9979
+ async recoverIdleTransportIfNeeded() {
9980
+ if (!this.idleTransportDisconnected)
9981
+ return;
9982
+ if (this.transportRecoveryPromise) {
9983
+ await this.transportRecoveryPromise;
9984
+ return;
9985
+ }
9986
+ const runtime = this.runtime;
9987
+ if (!runtime)
9988
+ throw new Error("Session transport disconnected without a runtime");
9989
+ const generation = this.transportDisconnectGeneration;
9990
+ const recovery = this.recoverIdleTransport(runtime).then(async (init) => {
9991
+ if (this.closed || generation !== this.transportDisconnectGeneration) {
9992
+ init.controller.close();
9993
+ this.onRecoveredTransportDiscarded();
9994
+ throw new Error(`${this.label} connection closed during recovery`);
9995
+ }
9996
+ if (init.runtime.agent_id !== runtime.agent_id || init.runtime.conversation_id !== runtime.conversation_id) {
9997
+ init.controller.close();
9998
+ this.onRecoveredTransportDiscarded();
9999
+ throw new Error(`${this.label} transport recovered a different runtime`);
10000
+ }
10001
+ this.controller = init.controller;
10002
+ this.runtime = init.runtime;
10003
+ this._modelSettings = init.modelSettings ?? this._modelSettings;
10004
+ if (typeof init.model === "string" && init.model)
10005
+ this._model = init.model;
10006
+ if (init.tools !== undefined)
10007
+ this.toolNames = init.tools;
10008
+ this.removeMessageHandler = this.controller.onMessage((message) => {
10009
+ if (this.runtime)
10010
+ this.turns.handleProtocolMessage(message, this.runtime);
10011
+ });
10012
+ this.idleTransportDisconnected = false;
10013
+ await this.afterRuntimeInitialized();
10014
+ }).finally(() => {
10015
+ if (this.transportRecoveryPromise === recovery)
10016
+ this.transportRecoveryPromise = null;
10017
+ });
10018
+ this.transportRecoveryPromise = recovery;
10019
+ await recovery;
10020
+ }
10021
+ async recoverIdleTransport(_runtime) {
10022
+ throw new Error(`${this.label} sessions do not support idle transport recovery`);
10023
+ }
10024
+ onIdleTransportDisconnect() {}
10025
+ onRecoveredTransportDiscarded() {}
9914
10026
  async afterRuntimeInitialized() {}
9915
10027
  async beforeTurn() {}
9916
10028
  onCoreClose() {}
@@ -10032,111 +10144,6 @@ function agentToolNames(agent) {
10032
10144
  return typeof name === "string" && name.length > 0 ? [name] : [];
10033
10145
  });
10034
10146
  }
10035
- function isPresetSystemPrompt(value) {
10036
- return [
10037
- "default",
10038
- "letta-claude",
10039
- "letta-codex",
10040
- "letta-gemini",
10041
- "claude",
10042
- "codex",
10043
- "gemini"
10044
- ].includes(value);
10045
- }
10046
- function assertRemoteCreateAgentOptionsSupported(options) {
10047
- if (options.allowedTools !== undefined || options.disallowedTools !== undefined) {
10048
- throw new Error("App-server createAgent() does not yet support allowedTools/disallowedTools.");
10049
- }
10050
- if (options.canUseTool !== undefined) {
10051
- throw new Error("App-server createAgent() does not yet support canUseTool callbacks.");
10052
- }
10053
- if (options.systemInfoReminder !== undefined) {
10054
- throw new Error("App-server createAgent() does not yet support systemInfoReminder overrides.");
10055
- }
10056
- if (options.dreaming?.behavior !== undefined) {
10057
- throw new Error("App-server createAgent() does not yet support dreaming.behavior overrides.");
10058
- }
10059
- }
10060
- function normalizeMemoryBlock(block) {
10061
- const normalized = { ...block };
10062
- if (normalized.value === undefined && typeof normalized.content === "string") {
10063
- normalized.value = normalized.content;
10064
- }
10065
- return normalized;
10066
- }
10067
- function upsertMemoryBlock(blocks, block) {
10068
- const label = block.label;
10069
- if (typeof label === "string") {
10070
- const existingIndex = blocks.findIndex((candidate) => candidate.label === label);
10071
- if (existingIndex >= 0) {
10072
- blocks[existingIndex] = block;
10073
- return;
10074
- }
10075
- }
10076
- blocks.push(block);
10077
- }
10078
- async function createAgentBody(options, settings = {}) {
10079
- assertRemoteCreateAgentOptionsSupported(options);
10080
- const includeOriginTag = settings.includeSdkOriginTag ?? true;
10081
- const body = await buildInitialAgentBody(options);
10082
- if (Array.isArray(body.tags)) {
10083
- body.tags = body.tags.filter((tag) => (includeOriginTag || tag !== LETTA_CODE_ORIGIN_TAG) && (options.memfs !== false || tag !== GIT_MEMORY_ENABLED_TAG));
10084
- }
10085
- if (options.embedding !== undefined)
10086
- body.embedding = options.embedding;
10087
- if (options.hidden !== undefined)
10088
- body.hidden = options.hidden;
10089
- if (options.baseTools === undefined) {
10090
- delete body.tools;
10091
- delete body.include_base_tools;
10092
- delete body.include_base_tool_rules;
10093
- } else {
10094
- body.tools = options.baseTools;
10095
- body.include_base_tools = false;
10096
- body.include_base_tool_rules = false;
10097
- }
10098
- if (options.systemPrompt === undefined) {
10099
- if (options.memfs === false) {
10100
- body.system = buildSystemPrompt("default", "standard");
10101
- }
10102
- } else {
10103
- if (typeof options.systemPrompt === "string") {
10104
- if (isPresetSystemPrompt(options.systemPrompt)) {
10105
- throw new Error("createAgent() does not yet support system prompt presets for this backend.");
10106
- }
10107
- body.system = options.systemPrompt;
10108
- } else {
10109
- throw new Error("createAgent() does not yet support system prompt preset objects for this backend.");
10110
- }
10111
- }
10112
- const hasMemoryConfiguration = Array.isArray(body.memory_blocks) || options.memory !== undefined || options.persona !== undefined || options.human !== undefined;
10113
- const memoryBlocks = Array.isArray(body.memory_blocks) ? body.memory_blocks.filter((block) => block !== null && typeof block === "object").map((block) => ({ ...block })) : [];
10114
- const blockIds = [];
10115
- for (const item of options.memory ?? []) {
10116
- if (typeof item === "string") {
10117
- throw new Error("App-server createAgent() does not yet support memory preset names.");
10118
- }
10119
- if ("blockId" in item) {
10120
- blockIds.push(item.blockId);
10121
- } else {
10122
- upsertMemoryBlock(memoryBlocks, normalizeMemoryBlock(item));
10123
- }
10124
- }
10125
- if (options.persona !== undefined) {
10126
- upsertMemoryBlock(memoryBlocks, {
10127
- label: "persona",
10128
- value: options.persona
10129
- });
10130
- }
10131
- if (options.human !== undefined) {
10132
- upsertMemoryBlock(memoryBlocks, { label: "human", value: options.human });
10133
- }
10134
- if (hasMemoryConfiguration)
10135
- body.memory_blocks = memoryBlocks;
10136
- if (blockIds.length > 0)
10137
- body.block_ids = blockIds;
10138
- return body;
10139
- }
10140
10147
  function externalToolGroups(tools) {
10141
10148
  if (!tools || tools.length === 0)
10142
10149
  return;
@@ -10515,6 +10522,7 @@ class AppServerSession extends RemoteClientSessionCore {
10515
10522
  if (!response.success || !response.runtime) {
10516
10523
  throw new Error(response.error ?? "Failed to start app-server runtime");
10517
10524
  }
10525
+ this.watchTransportDisconnect(client);
10518
10526
  const tools = agentToolNames(response.agent);
10519
10527
  const skillSources = options.skillSources;
10520
10528
  const clientToolset = "toolset" in options ? options.toolset : undefined;
@@ -10597,9 +10605,7 @@ class AppServerSession extends RemoteClientSessionCore {
10597
10605
  command.external_tools = groups;
10598
10606
  if (this.mode.kind === "create-agent") {
10599
10607
  command.create_agent = {
10600
- body: await createAgentBody(this.mode.options, {
10601
- includeSdkOriginTag: this.remoteOptions.includeSdkOriginTag
10602
- }),
10608
+ body: await createAgentBody(this.mode.options),
10603
10609
  pin_global: this.remoteOptions.pinGlobalAgent ?? this.mode.options.hidden !== true,
10604
10610
  ...this.mode.options.memfs === false ? { memfs: false } : {}
10605
10611
  };
@@ -10760,6 +10766,132 @@ class CloudManagementTransport {
10760
10766
  }
10761
10767
  }
10762
10768
 
10769
+ // src/remote.ts
10770
+ function ensureOnline(environment, target) {
10771
+ if (!environment.connectionId) {
10772
+ const label = "deviceId" in target ? target.deviceId : ("environmentId" in target) ? target.environmentId : ("connectionName" in target) ? target.connectionName : environment.deviceId;
10773
+ throw new Error(`Computer is offline: ${label}`);
10774
+ }
10775
+ return {
10776
+ connectionId: environment.connectionId,
10777
+ environment,
10778
+ target
10779
+ };
10780
+ }
10781
+
10782
+ class RemoteEnvironmentClient {
10783
+ client;
10784
+ constructor(options = {}, client = createCloudClient({
10785
+ backend: "cloud",
10786
+ apiBaseUrl: options.baseUrl,
10787
+ apiKey: options.apiKey,
10788
+ headers: options.headers,
10789
+ fetch: options.fetch
10790
+ })) {
10791
+ this.client = client;
10792
+ }
10793
+ async listEnvironments(options = {}) {
10794
+ return await this.client.environments.list({
10795
+ after: options.after,
10796
+ limit: options.limit === undefined ? undefined : String(options.limit),
10797
+ onlineOnly: options.onlineOnly === undefined ? undefined : String(options.onlineOnly)
10798
+ });
10799
+ }
10800
+ async getEnvironmentByDeviceId(deviceId) {
10801
+ return await this.client.environments.retrieve(deviceId);
10802
+ }
10803
+ async resolveEnvironment(target) {
10804
+ if ("connectionId" in target) {
10805
+ return { connectionId: target.connectionId, target };
10806
+ }
10807
+ if ("deviceId" in target) {
10808
+ return ensureOnline(await this.getEnvironmentByDeviceId(target.deviceId), target);
10809
+ }
10810
+ const connections = [];
10811
+ let after;
10812
+ do {
10813
+ const page = await this.listEnvironments({ limit: 100, after });
10814
+ connections.push(...page.connections);
10815
+ if (!page.hasNextPage || page.connections.length === 0)
10816
+ break;
10817
+ after = page.connections.at(-1)?.id;
10818
+ } while (after !== undefined);
10819
+ if ("environmentId" in target) {
10820
+ const match = connections.find((env) => env.id === target.environmentId);
10821
+ if (!match) {
10822
+ throw new Error(`Remote environment not found: ${target.environmentId}`);
10823
+ }
10824
+ return ensureOnline(match, target);
10825
+ }
10826
+ const matches = connections.filter((env) => env.connectionName === target.connectionName);
10827
+ if (matches.length === 0) {
10828
+ throw new Error(`Computer not found: ${target.connectionName}`);
10829
+ }
10830
+ const onlineMatches = matches.filter((environment) => environment.connectionId !== null);
10831
+ if (onlineMatches.length === 0) {
10832
+ throw new Error(`Computer is offline: ${target.connectionName}`);
10833
+ }
10834
+ if (onlineMatches.length > 1) {
10835
+ const candidates = onlineMatches.map((environment) => `${environment.connectionName} (${environment.deviceId}, online)`).join(", ");
10836
+ throw new Error(`Computer name is ambiguous: ${target.connectionName}. Matches: ${candidates}. Select one by deviceId.`);
10837
+ }
10838
+ return ensureOnline(onlineMatches[0], target);
10839
+ }
10840
+ }
10841
+
10842
+ // src/computers.ts
10843
+ function selectorToRemoteTarget(selector) {
10844
+ if (typeof selector === "string")
10845
+ return { connectionName: selector };
10846
+ if ("name" in selector)
10847
+ return { connectionName: selector.name };
10848
+ if ("id" in selector)
10849
+ return { environmentId: selector.id };
10850
+ if ("connectionId" in selector) {
10851
+ return { connectionId: selector.connectionId };
10852
+ }
10853
+ return { deviceId: selector.deviceId };
10854
+ }
10855
+ function toComputer(environment) {
10856
+ return {
10857
+ id: environment.id,
10858
+ deviceId: environment.deviceId,
10859
+ connectionId: environment.connectionId,
10860
+ name: environment.connectionName,
10861
+ status: environment.connectionId ? "online" : "offline",
10862
+ connectedAt: environment.connectedAt,
10863
+ lastSeenAt: environment.lastSeenAt,
10864
+ metadata: environment.metadata
10865
+ };
10866
+ }
10867
+
10868
+ class ComputersClientImpl {
10869
+ environments;
10870
+ constructor(client) {
10871
+ this.environments = new RemoteEnvironmentClient({}, client);
10872
+ }
10873
+ async list(options = {}) {
10874
+ const result = await this.environments.listEnvironments(options);
10875
+ return {
10876
+ computers: result.connections.map(toComputer),
10877
+ hasNextPage: result.hasNextPage
10878
+ };
10879
+ }
10880
+ async get(deviceId) {
10881
+ if (typeof deviceId !== "string" || deviceId.trim().length === 0) {
10882
+ throw new Error("Invalid deviceId. Expected a non-empty string.");
10883
+ }
10884
+ return toComputer(await this.environments.getEnvironmentByDeviceId(deviceId));
10885
+ }
10886
+ async resolve(selector) {
10887
+ const result = await this.environments.resolveEnvironment(selectorToRemoteTarget(selector));
10888
+ return {
10889
+ connectionId: result.connectionId,
10890
+ computer: result.environment ? toComputer(result.environment) : undefined
10891
+ };
10892
+ }
10893
+ }
10894
+
10763
10895
  // src/cloud-status-transport.ts
10764
10896
  var CONNECTING = 0;
10765
10897
  var OPEN = 1;
@@ -10976,66 +11108,6 @@ function createCloudStatusTransportConstructor(options) {
10976
11108
  };
10977
11109
  }
10978
11110
 
10979
- // src/remote.ts
10980
- function ensureOnline(environment, target) {
10981
- if (!environment.connectionId) {
10982
- const label = "deviceId" in target ? target.deviceId : ("environmentId" in target) ? target.environmentId : ("connectionName" in target) ? target.connectionName : environment.deviceId;
10983
- throw new Error(`Remote environment is offline: ${label}`);
10984
- }
10985
- return {
10986
- connectionId: environment.connectionId,
10987
- environment,
10988
- target
10989
- };
10990
- }
10991
-
10992
- class RemoteEnvironmentClient {
10993
- client;
10994
- constructor(options = {}, client = createCloudClient({
10995
- backend: "cloud",
10996
- apiBaseUrl: options.baseUrl,
10997
- apiKey: options.apiKey,
10998
- headers: options.headers,
10999
- fetch: options.fetch
11000
- })) {
11001
- this.client = client;
11002
- }
11003
- async listEnvironments() {
11004
- return await this.client.environments.list();
11005
- }
11006
- async getEnvironmentByDeviceId(deviceId) {
11007
- return await this.client.environments.retrieve(deviceId);
11008
- }
11009
- async resolveEnvironment(target) {
11010
- if ("connectionId" in target) {
11011
- return { connectionId: target.connectionId, target };
11012
- }
11013
- if ("deviceId" in target) {
11014
- return ensureOnline(await this.getEnvironmentByDeviceId(target.deviceId), target);
11015
- }
11016
- const { connections } = await this.listEnvironments();
11017
- if ("environmentId" in target) {
11018
- const match2 = connections.find((env) => env.id === target.environmentId);
11019
- if (!match2) {
11020
- throw new Error(`Remote environment not found: ${target.environmentId}`);
11021
- }
11022
- return ensureOnline(match2, target);
11023
- }
11024
- const matches = connections.filter((env) => env.connectionName === target.connectionName);
11025
- if (matches.length === 0) {
11026
- throw new Error(`Remote environment not found: ${target.connectionName}`);
11027
- }
11028
- if (matches.length > 1) {
11029
- throw new Error(`Remote environment name is ambiguous: ${target.connectionName}`);
11030
- }
11031
- const match = matches[0];
11032
- if (!match) {
11033
- throw new Error(`Remote environment not found: ${target.connectionName}`);
11034
- }
11035
- return ensureOnline(match, target);
11036
- }
11037
- }
11038
-
11039
11111
  // src/cloud-sandbox.ts
11040
11112
  var MIN_TTL_MINUTES = 1;
11041
11113
  var MAX_TTL_MINUTES = 60;
@@ -11134,8 +11206,12 @@ function validatePositiveInteger3(value, name) {
11134
11206
  function validateCloudClientOptions(options) {
11135
11207
  validatePositiveInteger3(options.requestTimeoutMs, "requestTimeoutMs");
11136
11208
  validateCloudSandboxOptions(options.sandbox, "sandbox");
11137
- if (options.environment !== undefined && options.sandbox !== undefined) {
11138
- throw new Error("Letta Cloud sessions cannot specify both environment and sandbox options.");
11209
+ if (options.computer !== undefined && options.environment !== undefined) {
11210
+ throw new Error("Letta Cloud clients cannot specify both computer and deprecated environment.");
11211
+ }
11212
+ if ((options.computer !== undefined || options.environment !== undefined) && options.sandbox !== undefined) {
11213
+ const field = options.computer !== undefined ? "computer" : "environment";
11214
+ throw new Error(`Letta Cloud sessions cannot specify both ${field} and sandbox options.`);
11139
11215
  }
11140
11216
  if (options.webSocketAuth !== undefined && options.webSocketAuth !== "header" && options.webSocketAuth !== "query") {
11141
11217
  throw new Error("Invalid webSocketAuth. Valid values: header, query.");
@@ -11252,12 +11328,30 @@ class CloudEnvironmentSession extends RemoteClientSessionCore {
11252
11328
  throw error;
11253
11329
  });
11254
11330
  this.connectionId = connection.connectionId;
11331
+ const options = this.currentOptions();
11332
+ this.mcpBridge = await connectMcpServers("mcpServers" in options ? options.mcpServers : undefined, {
11333
+ cwd: options.cwd,
11334
+ reservedToolNames: this.externalTools.keys()
11335
+ });
11336
+ for (const tool of this.mcpBridge.tools) {
11337
+ this.externalTools.set(tool.name, tool);
11338
+ }
11339
+ try {
11340
+ return await this.openCloudTransport(resolved.runtime, connection.connectionId);
11341
+ } catch (error) {
11342
+ await this.closeMcpBridge();
11343
+ await this.cleanupManagedSandbox();
11344
+ await this.cleanupSessionRepositories(resolved.runtime.agent_id, resolved.runtime.conversation_id);
11345
+ throw error;
11346
+ }
11347
+ }
11348
+ async openCloudTransport(runtime, connectionId) {
11255
11349
  const apiKey = getCloudApiKey(this.cloudOptions);
11256
11350
  const url = buildCloudStatusWebSocketUrl({
11257
11351
  apiBaseUrl: this.cloudOptions.apiBaseUrl,
11258
- connectionId: connection.connectionId,
11259
- agentId: resolved.runtime.agent_id,
11260
- conversationId: resolved.runtime.conversation_id,
11352
+ connectionId,
11353
+ agentId: runtime.agent_id,
11354
+ conversationId: runtime.conversation_id,
11261
11355
  apiKey,
11262
11356
  authMode: this.cloudOptions.webSocketAuth ?? "header"
11263
11357
  });
@@ -11268,32 +11362,19 @@ class CloudEnvironmentSession extends RemoteClientSessionCore {
11268
11362
  WebSocket: getWebSocketConstructor(this.cloudOptions.WebSocket),
11269
11363
  ...(this.cloudOptions.webSocketAuth ?? "header") === "header" ? { headers: cloudWebSocketHeaders(this.cloudOptions) } : {},
11270
11364
  pingIntervalMs: this.cloudOptions.pingIntervalMs ?? DEFAULT_PING_INTERVAL_MS,
11271
- runtime: resolved.runtime
11365
+ runtime
11272
11366
  }),
11273
11367
  requestTimeoutMs: this.cloudOptions.requestTimeoutMs ?? DEFAULT_TURN_TIMEOUT_MS
11274
11368
  }));
11275
11369
  const options = this.currentOptions();
11276
- this.mcpBridge = await connectMcpServers("mcpServers" in options ? options.mcpServers : undefined, {
11277
- cwd: options.cwd,
11278
- reservedToolNames: this.externalTools.keys()
11279
- });
11280
- for (const tool of this.mcpBridge.tools) {
11281
- this.externalTools.set(tool.name, tool);
11282
- }
11283
- this.removeControlRequestHandler = registerAppServerControlRequestHandler({
11284
- client,
11285
- getRuntime: () => this.runtime,
11286
- getOptions: () => this.currentOptions()
11287
- });
11288
- if (this.externalTools.size > 0) {
11289
- this.removeExternalToolHandler = client.onExternalToolCall(createExternalToolCallHandler(this.externalTools));
11290
- }
11370
+ this.installTransportHandlers(client);
11291
11371
  try {
11292
11372
  await client.connect();
11293
- const response = await this.startCloudRuntime(client, resolved.runtime);
11373
+ const response = await this.startCloudRuntime(client, runtime);
11294
11374
  if (!response.success || !response.runtime) {
11295
11375
  throw new Error(response.error ?? "Failed to start Cloud status runtime");
11296
11376
  }
11377
+ this.watchTransportDisconnect(client, { recoverWhenIdle: true });
11297
11378
  const tools = agentToolNames(response.agent);
11298
11379
  const skillSources = options.skillSources;
11299
11380
  const clientToolset = "toolset" in options ? options.toolset : undefined;
@@ -11311,17 +11392,51 @@ class CloudEnvironmentSession extends RemoteClientSessionCore {
11311
11392
  ...skillSources !== undefined ? { skillSources: [...skillSources] } : {}
11312
11393
  };
11313
11394
  } catch (error) {
11314
- this.removeExternalToolHandler?.();
11315
- this.removeExternalToolHandler = null;
11316
- this.removeControlRequestHandler?.();
11317
- this.removeControlRequestHandler = null;
11318
- await this.closeMcpBridge();
11395
+ this.cleanupTransportHandlers();
11319
11396
  client.close();
11320
- await this.cleanupManagedSandbox();
11321
- await this.cleanupSessionRepositories(resolved.runtime.agent_id, resolved.runtime.conversation_id);
11322
11397
  throw error;
11323
11398
  }
11324
11399
  }
11400
+ installTransportHandlers(client) {
11401
+ this.cleanupTransportHandlers();
11402
+ this.removeControlRequestHandler = registerAppServerControlRequestHandler({
11403
+ client,
11404
+ getRuntime: () => this.runtime,
11405
+ getOptions: () => this.currentOptions()
11406
+ });
11407
+ if (this.externalTools.size > 0) {
11408
+ this.removeExternalToolHandler = client.onExternalToolCall(createExternalToolCallHandler(this.externalTools));
11409
+ }
11410
+ }
11411
+ cleanupTransportHandlers() {
11412
+ this.removeExternalToolHandler?.();
11413
+ this.removeExternalToolHandler = null;
11414
+ this.removeControlRequestHandler?.();
11415
+ this.removeControlRequestHandler = null;
11416
+ }
11417
+ async recoverIdleTransport(runtime) {
11418
+ const connection = await this.resolveRecoveryConnection(runtime);
11419
+ this.connectionId = connection.connectionId;
11420
+ return this.openCloudTransport(runtime, connection.connectionId);
11421
+ }
11422
+ async resolveRecoveryConnection(runtime) {
11423
+ const sandbox = this.managedSandbox;
11424
+ if (sandbox) {
11425
+ await this.refreshManagedSandbox(sandbox);
11426
+ return this.waitForManagedSandboxConnection(sandbox);
11427
+ }
11428
+ const environment = this.effectiveEnvironment();
11429
+ if (!environment) {
11430
+ throw new Error("Cloud idle transport recovery lost its execution target");
11431
+ }
11432
+ return this.resolveExplicitConnection(environment);
11433
+ }
11434
+ onIdleTransportDisconnect() {
11435
+ this.cleanupTransportHandlers();
11436
+ }
11437
+ onRecoveredTransportDiscarded() {
11438
+ this.cleanupTransportHandlers();
11439
+ }
11325
11440
  async startCloudRuntime(client, runtime) {
11326
11441
  const options = this.currentOptions();
11327
11442
  const command = {
@@ -11364,10 +11479,7 @@ class CloudEnvironmentSession extends RemoteClientSessionCore {
11364
11479
  await this.refreshManagedSandbox(sandbox);
11365
11480
  }
11366
11481
  onCoreClose() {
11367
- this.removeExternalToolHandler?.();
11368
- this.removeExternalToolHandler = null;
11369
- this.removeControlRequestHandler?.();
11370
- this.removeControlRequestHandler = null;
11482
+ this.cleanupTransportHandlers();
11371
11483
  this.mcpCleanup = this.closeMcpBridge();
11372
11484
  this.cleanupManagedSandbox();
11373
11485
  if (this.runtime?.agent_id) {
@@ -11691,8 +11803,8 @@ class CloudEnvironmentSession extends RemoteClientSessionCore {
11691
11803
  return new RemoteEnvironmentClient({}, this.apiClient);
11692
11804
  }
11693
11805
  effectiveEnvironment() {
11694
- const modeEnvironment = this.mode.kind === "session" ? this.mode.options.environment : undefined;
11695
- return modeEnvironment ?? this.cloudOptions.environment;
11806
+ const modeComputer = this.mode.kind === "session" ? this.mode.options.computer ?? this.mode.options.environment : undefined;
11807
+ return modeComputer ?? this.cloudOptions.computer ?? this.cloudOptions.environment;
11696
11808
  }
11697
11809
  effectiveSandboxOptions() {
11698
11810
  const modeSandbox = this.mode.kind === "session" ? this.mode.options.sandbox : undefined;
@@ -11992,14 +12104,17 @@ var VALID_BACKENDS = new Set([
11992
12104
  function isLettaCodeBackend(value) {
11993
12105
  return VALID_BACKENDS.has(value);
11994
12106
  }
11995
- function getOptionsEnvironment(options) {
11996
- if ("environment" in options) {
11997
- return options.environment;
12107
+ function getOptionsComputer(options) {
12108
+ if (!("computer" in options) && !("environment" in options))
12109
+ return;
12110
+ if (options.computer !== undefined && options.environment !== undefined) {
12111
+ throw new Error('Specify either "computer" or deprecated "environment", not both.');
11998
12112
  }
11999
- return;
12113
+ return options.computer ?? options.environment;
12000
12114
  }
12001
12115
  function stripCloudExecutionOptions(options) {
12002
12116
  const sessionOptions = { ...options };
12117
+ delete sessionOptions.computer;
12003
12118
  delete sessionOptions.environment;
12004
12119
  delete sessionOptions.sandbox;
12005
12120
  delete sessionOptions.filesystemConfinement;
@@ -12017,12 +12132,14 @@ function looksLikeConversationId(id) {
12017
12132
 
12018
12133
  class LettaAgentClientBase {
12019
12134
  backend;
12135
+ computer;
12020
12136
  environment;
12021
12137
  agents;
12022
12138
  conversations;
12023
12139
  models;
12024
12140
  options;
12025
12141
  repositoriesClient = null;
12142
+ computersClient = null;
12026
12143
  agentRepositoriesClient = null;
12027
12144
  cloudClient = null;
12028
12145
  managementTransport = null;
@@ -12032,16 +12149,19 @@ class LettaAgentClientBase {
12032
12149
  throw new Error(`Invalid Letta Code backend '${String(backend)}'. Valid values: local, remote, cloud.`);
12033
12150
  }
12034
12151
  this.backend = backend;
12035
- this.environment = getOptionsEnvironment(options);
12152
+ this.computer = getOptionsComputer(options);
12153
+ this.environment = this.computer;
12036
12154
  this.options = options;
12037
12155
  this.agents = createAgentsClient(() => this.getManagementTransport(), () => this.getAgentRepositoriesClient());
12038
12156
  this.conversations = createConversationsClient(() => this.getManagementTransport());
12039
12157
  this.models = createModelsClient(() => this.getManagementTransport());
12040
- if (this.backend === "local" && this.environment !== undefined) {
12041
- throw new Error('LettaAgentClient environment is only valid with backend: "cloud".');
12158
+ if (this.backend === "local" && this.computer !== undefined) {
12159
+ const field = "computer" in options && options.computer !== undefined ? "computer" : "environment";
12160
+ throw new Error(`LettaAgentClient ${field} is only valid with backend: "cloud".`);
12042
12161
  }
12043
- if (this.backend === "remote" && this.environment !== undefined) {
12044
- throw new Error('LettaAgentClient environment is only valid with backend: "cloud"; remote url selects the app-server runtime.');
12162
+ if (this.backend === "remote" && this.computer !== undefined) {
12163
+ const field = "computer" in options && options.computer !== undefined ? "computer" : "environment";
12164
+ throw new Error(`LettaAgentClient ${field} is only valid with backend: "cloud"; remote url selects the app-server runtime.`);
12045
12165
  }
12046
12166
  if (this.backend !== "cloud" && options.sandbox !== undefined) {
12047
12167
  throw new Error('LettaAgentClient sandbox options are only valid with backend: "cloud".');
@@ -12075,6 +12195,9 @@ class LettaAgentClientBase {
12075
12195
  get repositories() {
12076
12196
  return this.getRepositoriesClient();
12077
12197
  }
12198
+ get computers() {
12199
+ return this.getComputersClient();
12200
+ }
12078
12201
  async createAgent(options = {}) {
12079
12202
  if (hasCreateAgentEnvironment(options)) {
12080
12203
  throw new Error("createAgent() does not accept environment. Set a client default or pass environment to resumeSession()/createSession().");
@@ -12164,13 +12287,17 @@ class LettaAgentClientBase {
12164
12287
  }
12165
12288
  }
12166
12289
  assertSessionBackend(action, options) {
12290
+ if (options.computer !== undefined && options.environment !== undefined) {
12291
+ throw new Error(`${action}() cannot specify both computer and deprecated environment.`);
12292
+ }
12167
12293
  if (options.filesystemConfinement !== undefined && options.filesystemConfinement !== "memory") {
12168
12294
  throw new Error(`Invalid filesystemConfinement '${String(options.filesystemConfinement)}'. Valid value: memory.`);
12169
12295
  }
12170
- const effectiveEnvironment = options.environment ?? this.environment;
12296
+ const effectiveComputer = options.computer ?? options.environment ?? this.computer;
12171
12297
  if (this.backend === "local") {
12172
- if (effectiveEnvironment !== undefined) {
12173
- throw new Error(`${action}() environment overrides are only valid with backend: "cloud".`);
12298
+ if (effectiveComputer !== undefined) {
12299
+ const field = options.computer !== undefined ? "computer" : "environment";
12300
+ throw new Error(`${action}() ${field} overrides are only valid with backend: "cloud".`);
12174
12301
  }
12175
12302
  if (options.sandbox !== undefined) {
12176
12303
  throw new Error(`${action}() sandbox options are only valid with backend: "cloud".`);
@@ -12190,8 +12317,9 @@ class LettaAgentClientBase {
12190
12317
  if (options.filesystemConfinement !== undefined) {
12191
12318
  throw new Error(`${action}() filesystemConfinement requires an SDK-owned local app-server process.`);
12192
12319
  }
12193
- if (options.environment !== undefined) {
12194
- throw new Error(`${action}() environment overrides are only valid with backend: "cloud"; remote url selects the app-server runtime.`);
12320
+ if (options.computer !== undefined || options.environment !== undefined) {
12321
+ const field = options.computer !== undefined ? "computer" : "environment";
12322
+ throw new Error(`${action}() ${field} overrides are only valid with backend: "cloud"; remote url selects the app-server runtime.`);
12195
12323
  }
12196
12324
  if (options.sandbox !== undefined) {
12197
12325
  throw new Error(`${action}() sandbox options are only valid with backend: "cloud"; remote url selects the app-server runtime.`);
@@ -12206,11 +12334,13 @@ class LettaAgentClientBase {
12206
12334
  throw new Error(`${action}() filesystemConfinement is only supported with backend: "local".`);
12207
12335
  }
12208
12336
  const cloudOptions = this.cloudOptions();
12209
- if (cloudOptions.environment !== undefined && options.sandbox !== undefined) {
12210
- throw new Error(`Letta Cloud ${action}() cannot specify sandbox options when the client has a default environment.`);
12337
+ if (this.computer !== undefined && options.sandbox !== undefined) {
12338
+ const field = cloudOptions.computer !== undefined ? "computer" : "environment";
12339
+ throw new Error(`Letta Cloud ${action}() cannot specify sandbox options when the client has a default ${field}.`);
12211
12340
  }
12212
- if (cloudOptions.sandbox !== undefined && options.environment !== undefined) {
12213
- throw new Error(`Letta Cloud ${action}() cannot specify an environment when the client has default sandbox options.`);
12341
+ if (cloudOptions.sandbox !== undefined && (options.computer !== undefined || options.environment !== undefined)) {
12342
+ const field = options.computer !== undefined ? "a computer" : "an environment";
12343
+ throw new Error(`Letta Cloud ${action}() cannot specify ${field} when the client has default sandbox options.`);
12214
12344
  }
12215
12345
  assertCloudSessionOptionsSupported(action, options);
12216
12346
  return;
@@ -12241,6 +12371,13 @@ class LettaAgentClientBase {
12241
12371
  appServerSessionOptions() {
12242
12372
  return this.remoteOptions();
12243
12373
  }
12374
+ getComputersClient() {
12375
+ if (this.backend !== "cloud") {
12376
+ throw new Error('client.computers is only available with backend: "cloud".');
12377
+ }
12378
+ this.computersClient ??= new ComputersClientImpl(this.getCloudClient());
12379
+ return this.computersClient;
12380
+ }
12244
12381
  getRepositoriesClient() {
12245
12382
  if (this.backend !== "cloud") {
12246
12383
  throw new Error('client.repositories is only available with backend: "cloud".');
@@ -29499,7 +29636,7 @@ class StreamableHTTPClientTransport {
29499
29636
  }
29500
29637
  var DEFAULT_CLIENT_INFO = {
29501
29638
  name: "letta-code",
29502
- version: "0.30.0"
29639
+ version: "0.30.5"
29503
29640
  };
29504
29641
  async function connectMcpServer(config2, options = {}) {
29505
29642
  let client = new Client(options.clientInfo ?? DEFAULT_CLIENT_INFO);
@@ -29997,4 +30134,4 @@ export {
29997
30134
  CloudManagedSandboxExpiredError
29998
30135
  };
29999
30136
 
30000
- //# debugId=240CAF4E9D69510064756E2164756E21
30137
+ //# debugId=C079C2D0B42E0C4F64756E2164756E21