@letta-ai/letta-agent-sdk 0.6.2 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +42 -0
- package/README.md +101 -1
- package/dist/app-server-session.d.ts.map +1 -1
- package/dist/client-base.d.ts +7 -0
- package/dist/client-base.d.ts.map +1 -1
- package/dist/client-entry.d.ts +3 -0
- package/dist/client-entry.d.ts.map +1 -1
- package/dist/client-entry.js +816 -138
- package/dist/client-entry.js.map +16 -14
- package/dist/cloud-session.d.ts +8 -1
- package/dist/cloud-session.d.ts.map +1 -1
- package/dist/computers.d.ts +67 -0
- package/dist/computers.d.ts.map +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +817 -139
- package/dist/index.js.map +17 -15
- package/dist/remote-client-session-core.d.ts +13 -16
- package/dist/remote-client-session-core.d.ts.map +1 -1
- package/dist/remote-session-protocol.d.ts +11 -0
- package/dist/remote-session-protocol.d.ts.map +1 -1
- package/dist/remote-turn-coordinator.d.ts +6 -1
- package/dist/remote-turn-coordinator.d.ts.map +1 -1
- package/dist/remote.d.ts +6 -1
- package/dist/remote.d.ts.map +1 -1
- package/dist/transcript-accumulator.d.ts +133 -0
- package/dist/transcript-accumulator.d.ts.map +1 -0
- package/dist/types.d.ts +41 -21
- package/dist/types.d.ts.map +1 -1
- package/dist/validation.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/app-server-session.ts +9 -0
- package/src/client-base.ts +66 -19
- package/src/client-entry.ts +23 -0
- package/src/cloud-session.ts +112 -45
- package/src/computers.ts +132 -0
- package/src/index.ts +24 -0
- package/src/remote-client-session-core.ts +103 -27
- package/src/remote-session-protocol.ts +24 -0
- package/src/remote-turn-coordinator.ts +11 -2
- package/src/remote.ts +41 -12
- package/src/transcript-accumulator.ts +823 -0
- package/src/types.ts +43 -18
- package/src/validation.ts +16 -0
package/dist/index.js
CHANGED
|
@@ -3091,7 +3091,7 @@ function isAppServerInfoResponseMessage(message) {
|
|
|
3091
3091
|
return false;
|
|
3092
3092
|
}
|
|
3093
3093
|
const capabilityRecord = capabilities;
|
|
3094
|
-
return candidate.type === "app_server_info_response" && typeof candidate.request_id === "string" && candidate.request_id.length > 0 && candidate.success === true && (candidate.backend === "local" || candidate.backend === "api") && typeof candidate.letta_code_version === "string" && typeof candidate.protocol_version === "number" && Number.isInteger(candidate.protocol_version) && typeof capabilityRecord.agent_management === "boolean" && typeof capabilityRecord.conversation_management === "boolean" && typeof capabilityRecord.memory_management === "boolean" && typeof capabilityRecord.runtime_start === "boolean" && typeof capabilityRecord.split_channels === "boolean";
|
|
3094
|
+
return candidate.type === "app_server_info_response" && typeof candidate.request_id === "string" && candidate.request_id.length > 0 && candidate.success === true && (candidate.backend === "local" || candidate.backend === "api") && typeof candidate.letta_code_version === "string" && typeof candidate.protocol_version === "number" && Number.isInteger(candidate.protocol_version) && typeof capabilityRecord.agent_management === "boolean" && typeof capabilityRecord.conversation_management === "boolean" && typeof capabilityRecord.memory_management === "boolean" && typeof capabilityRecord.runtime_start === "boolean" && (capabilityRecord.runtime_external_tools_update === undefined || typeof capabilityRecord.runtime_external_tools_update === "boolean") && typeof capabilityRecord.split_channels === "boolean";
|
|
3095
3095
|
}
|
|
3096
3096
|
var DEFAULT_REQUEST_TIMEOUT_MS = 30000;
|
|
3097
3097
|
var WEBSOCKET_OPEN_STATE = 1;
|
|
@@ -3334,6 +3334,16 @@ class AppServerClient {
|
|
|
3334
3334
|
predicate: (message) => message.type === "runtime_start_response"
|
|
3335
3335
|
});
|
|
3336
3336
|
}
|
|
3337
|
+
runtimeExternalToolsUpdate(command, options = {}) {
|
|
3338
|
+
return this.request({
|
|
3339
|
+
type: "runtime_external_tools_update",
|
|
3340
|
+
request_id: command.request_id ?? this.nextRequestId("runtime-external-tools"),
|
|
3341
|
+
...command
|
|
3342
|
+
}, {
|
|
3343
|
+
...options,
|
|
3344
|
+
predicate: (message) => message.type === "runtime_external_tools_update_response"
|
|
3345
|
+
});
|
|
3346
|
+
}
|
|
3337
3347
|
sync(command, options = {}) {
|
|
3338
3348
|
return this.request({
|
|
3339
3349
|
type: "sync",
|
|
@@ -3854,12 +3864,7 @@ You **MUST** be proactive in creating crons when work extends beyond the current
|
|
|
3854
3864
|
|
|
3855
3865
|
**Cost**: Self-invocation is critical, but expensive. Default to the longest interval that still serves the user. Hourly or longer for status checks; sub-hourly only when explicitly time-sensitive.
|
|
3856
3866
|
|
|
3857
|
-
|
|
3858
|
-
- One-shot follow-up: \`letta cron add --name <short-name> --description <description> --prompt <future-message> --at "in 30m"\`
|
|
3859
|
-
- Recurring monitoring/heartbeat: \`letta cron add --name <short-name> --description <description> --prompt <future-message> --every "2h"\` or \`--cron "0 9 * * *"\`
|
|
3860
|
-
Always include \`--name\`, \`--description\`, and \`--prompt\`. \`$AGENT_ID\` is automatically injected into the shell environment, and \`letta cron\` uses it by default, so you do not need to specify which agent to invoke unless overriding the current agent intentionally.
|
|
3861
|
-
|
|
3862
|
-
Where crons run: for cloud agents, schedules default to durable Cloud schedules that fire from the cloud and execute in your cloud sandbox — they survive local shutdown, so this is the right default. If the scheduled work must run on a specific computer (e.g. it needs that computer's filesystem or local services), add \`--computer <deviceId>\` (from \`letta environments list\`) to keep the durable Cloud schedule but execute on that computer, with sandbox fallback if it is offline. Use \`--runner local\` only when that fallback is unacceptable; local schedules only fire while a Letta session is running on that computer.
|
|
3867
|
+
The mechanics — flags, where schedules run and execute, timezone handling — live in the scheduling-tasks skill. Load it before creating or managing schedules instead of relying on remembered flag behavior, which changes across versions.
|
|
3863
3868
|
|
|
3864
3869
|
# Harness Architecture
|
|
3865
3870
|
|
|
@@ -4036,12 +4041,7 @@ You **MUST** be proactive in creating crons when work extends beyond the current
|
|
|
4036
4041
|
|
|
4037
4042
|
**Cost**: Self-invocation is critical, but expensive. Default to the longest interval that still serves the user. Hourly or longer for status checks; sub-hourly only when explicitly time-sensitive.
|
|
4038
4043
|
|
|
4039
|
-
|
|
4040
|
-
- One-shot follow-up: \`letta cron add --name <short-name> --description <description> --prompt <future-message> --at "in 30m"\`
|
|
4041
|
-
- Recurring monitoring/heartbeat: \`letta cron add --name <short-name> --description <description> --prompt <future-message> --every "2h"\` or \`--cron "0 9 * * *"\`
|
|
4042
|
-
Always include \`--name\`, \`--description\`, and \`--prompt\`. \`$AGENT_ID\` is automatically injected into the shell environment, and \`letta cron\` uses it by default, so you do not need to specify which agent to invoke unless overriding the current agent intentionally.
|
|
4043
|
-
|
|
4044
|
-
Where crons run: for cloud agents, schedules default to durable Cloud schedules that fire from the cloud and execute in your cloud sandbox — they survive local shutdown, so this is the right default. If the scheduled work must run on a specific computer (e.g. it needs that computer's filesystem or local services), add \`--computer <deviceId>\` (from \`letta environments list\`) to keep the durable Cloud schedule but execute on that computer, with sandbox fallback if it is offline. Use \`--runner local\` only when that fallback is unacceptable; local schedules only fire while a Letta session is running on that computer.
|
|
4044
|
+
The mechanics — flags, where schedules run and execute, timezone handling — live in the scheduling-tasks skill. Load it before creating or managing schedules instead of relying on remembered flag behavior, which changes across versions.
|
|
4045
4045
|
|
|
4046
4046
|
# Harness Architecture
|
|
4047
4047
|
|
|
@@ -9071,6 +9071,20 @@ function toSessionDeviceStatus(status) {
|
|
|
9071
9071
|
function normalizeSendMessage(message) {
|
|
9072
9072
|
return message;
|
|
9073
9073
|
}
|
|
9074
|
+
function normalizeCallerOtid(otid) {
|
|
9075
|
+
if (otid === undefined)
|
|
9076
|
+
return;
|
|
9077
|
+
if (typeof otid !== "string" || otid.trim() === "") {
|
|
9078
|
+
throw new Error("send() otid must be a non-empty string");
|
|
9079
|
+
}
|
|
9080
|
+
return otid;
|
|
9081
|
+
}
|
|
9082
|
+
function turnSendOptions(turn) {
|
|
9083
|
+
return {
|
|
9084
|
+
clientMessageId: turn.clientMessageId,
|
|
9085
|
+
...turn.otid !== undefined ? { otid: turn.otid } : {}
|
|
9086
|
+
};
|
|
9087
|
+
}
|
|
9074
9088
|
|
|
9075
9089
|
// src/remote-turn-coordinator.ts
|
|
9076
9090
|
class RemoteTurnCoordinator {
|
|
@@ -9099,11 +9113,13 @@ class RemoteTurnCoordinator {
|
|
|
9099
9113
|
hasInFlightTurn() {
|
|
9100
9114
|
return this.activeTurn !== null || this.pendingTurns.length > 0;
|
|
9101
9115
|
}
|
|
9102
|
-
trackSentTurn(runtime) {
|
|
9116
|
+
trackSentTurn(runtime, callerOtid) {
|
|
9117
|
+
const otid = normalizeCallerOtid(callerOtid);
|
|
9103
9118
|
const turn = {
|
|
9104
9119
|
id: ++this.nextTurnId,
|
|
9105
9120
|
runtime,
|
|
9106
|
-
|
|
9121
|
+
...otid !== undefined ? { otid } : {},
|
|
9122
|
+
clientMessageId: otid ?? `sdk-message-${Date.now()}-${++this.clientMessageCounter}`,
|
|
9107
9123
|
queuedAt: Date.now(),
|
|
9108
9124
|
startedAt: 0,
|
|
9109
9125
|
assistantText: "",
|
|
@@ -9514,6 +9530,9 @@ class RemoteClientSessionCore {
|
|
|
9514
9530
|
initializePromise = null;
|
|
9515
9531
|
removeMessageHandler = null;
|
|
9516
9532
|
detachTransportDisconnect = null;
|
|
9533
|
+
idleTransportDisconnected = false;
|
|
9534
|
+
transportRecoveryPromise = null;
|
|
9535
|
+
transportDisconnectGeneration = 0;
|
|
9517
9536
|
turns;
|
|
9518
9537
|
toolNames;
|
|
9519
9538
|
deviceStatusListeners = new Set;
|
|
@@ -9602,19 +9621,25 @@ class RemoteClientSessionCore {
|
|
|
9602
9621
|
this.toolNames = undefined;
|
|
9603
9622
|
this.initialized = false;
|
|
9604
9623
|
}
|
|
9605
|
-
async send(message) {
|
|
9624
|
+
async send(message, options) {
|
|
9625
|
+
if (this.closed)
|
|
9626
|
+
throw new Error("Session is closed");
|
|
9606
9627
|
if (!this.initialized) {
|
|
9607
9628
|
await this.initialize();
|
|
9608
9629
|
}
|
|
9630
|
+
await this.recoverIdleTransportIfNeeded();
|
|
9609
9631
|
if (!this.controller || !this.runtime) {
|
|
9610
9632
|
throw new Error("Session is not initialized");
|
|
9611
9633
|
}
|
|
9612
9634
|
await this.beforeTurn();
|
|
9613
|
-
const
|
|
9635
|
+
const controller = this.controller;
|
|
9636
|
+
const runtime = this.runtime;
|
|
9637
|
+
if (!controller || !runtime) {
|
|
9638
|
+
throw new Error("Session transport disconnected before the turn was sent");
|
|
9639
|
+
}
|
|
9640
|
+
const turn = this.turns.trackSentTurn(runtime, options?.otid);
|
|
9614
9641
|
try {
|
|
9615
|
-
|
|
9616
|
-
clientMessageId: turn.clientMessageId
|
|
9617
|
-
});
|
|
9642
|
+
controller.sendTurnMessage(runtime, message, turnSendOptions(turn));
|
|
9618
9643
|
} catch (error) {
|
|
9619
9644
|
this.turns.removeTrackedTurn(turn);
|
|
9620
9645
|
throw error;
|
|
@@ -9693,6 +9718,9 @@ class RemoteClientSessionCore {
|
|
|
9693
9718
|
return this.controller.listModels();
|
|
9694
9719
|
}
|
|
9695
9720
|
async updateModel(update) {
|
|
9721
|
+
if (this.mode.kind === "session" && this.mode.options.stateless === true) {
|
|
9722
|
+
throw new Error("updateModel() is unavailable in a stateless session because it changes persisted agent configuration.");
|
|
9723
|
+
}
|
|
9696
9724
|
if (!this.initialized) {
|
|
9697
9725
|
await this.initialize();
|
|
9698
9726
|
}
|
|
@@ -9887,6 +9915,7 @@ class RemoteClientSessionCore {
|
|
|
9887
9915
|
this.deviceStatusListeners.clear();
|
|
9888
9916
|
this.controller?.close();
|
|
9889
9917
|
this.controller = null;
|
|
9918
|
+
this.idleTransportDisconnected = false;
|
|
9890
9919
|
this.onCoreClose();
|
|
9891
9920
|
}
|
|
9892
9921
|
get agentId() {
|
|
@@ -9939,15 +9968,78 @@ class RemoteClientSessionCore {
|
|
|
9939
9968
|
}, { predicate: (message) => message.type === "update_toolset_response" });
|
|
9940
9969
|
ensureSuccess(response, "Failed to update toolset");
|
|
9941
9970
|
}
|
|
9942
|
-
watchTransportDisconnect(client) {
|
|
9971
|
+
watchTransportDisconnect(client, options = {}) {
|
|
9943
9972
|
this.detachTransportDisconnect?.();
|
|
9944
9973
|
this.detachTransportDisconnect = client.onDisconnect(() => {
|
|
9945
9974
|
if (this.closed)
|
|
9946
9975
|
return;
|
|
9976
|
+
this.transportDisconnectGeneration += 1;
|
|
9977
|
+
if (options.recoverWhenIdle && !this.turns.hasInFlightTurn()) {
|
|
9978
|
+
this.detachTransportDisconnect?.();
|
|
9979
|
+
this.detachTransportDisconnect = null;
|
|
9980
|
+
this.removeMessageHandler?.();
|
|
9981
|
+
this.removeMessageHandler = null;
|
|
9982
|
+
this.controller?.close();
|
|
9983
|
+
this.controller = null;
|
|
9984
|
+
const error = new Error(`${this.label} connection closed before send`);
|
|
9985
|
+
for (const cancel of [...this.deviceStatusRefreshCancels])
|
|
9986
|
+
cancel(error);
|
|
9987
|
+
this.deviceStatusRefreshCancels.clear();
|
|
9988
|
+
this.onIdleTransportDisconnect();
|
|
9989
|
+
this.idleTransportDisconnected = true;
|
|
9990
|
+
return;
|
|
9991
|
+
}
|
|
9947
9992
|
this.turns.closeWithError(`The ${this.label} connection closed unexpectedly; resume the conversation to continue.`);
|
|
9948
9993
|
this.close();
|
|
9949
9994
|
});
|
|
9950
9995
|
}
|
|
9996
|
+
async recoverIdleTransportIfNeeded() {
|
|
9997
|
+
if (!this.idleTransportDisconnected)
|
|
9998
|
+
return;
|
|
9999
|
+
if (this.transportRecoveryPromise) {
|
|
10000
|
+
await this.transportRecoveryPromise;
|
|
10001
|
+
return;
|
|
10002
|
+
}
|
|
10003
|
+
const runtime = this.runtime;
|
|
10004
|
+
if (!runtime)
|
|
10005
|
+
throw new Error("Session transport disconnected without a runtime");
|
|
10006
|
+
const generation = this.transportDisconnectGeneration;
|
|
10007
|
+
const recovery = this.recoverIdleTransport(runtime).then(async (init) => {
|
|
10008
|
+
if (this.closed || generation !== this.transportDisconnectGeneration) {
|
|
10009
|
+
init.controller.close();
|
|
10010
|
+
this.onRecoveredTransportDiscarded();
|
|
10011
|
+
throw new Error(`${this.label} connection closed during recovery`);
|
|
10012
|
+
}
|
|
10013
|
+
if (init.runtime.agent_id !== runtime.agent_id || init.runtime.conversation_id !== runtime.conversation_id) {
|
|
10014
|
+
init.controller.close();
|
|
10015
|
+
this.onRecoveredTransportDiscarded();
|
|
10016
|
+
throw new Error(`${this.label} transport recovered a different runtime`);
|
|
10017
|
+
}
|
|
10018
|
+
this.controller = init.controller;
|
|
10019
|
+
this.runtime = init.runtime;
|
|
10020
|
+
this._modelSettings = init.modelSettings ?? this._modelSettings;
|
|
10021
|
+
if (typeof init.model === "string" && init.model)
|
|
10022
|
+
this._model = init.model;
|
|
10023
|
+
if (init.tools !== undefined)
|
|
10024
|
+
this.toolNames = init.tools;
|
|
10025
|
+
this.removeMessageHandler = this.controller.onMessage((message) => {
|
|
10026
|
+
if (this.runtime)
|
|
10027
|
+
this.turns.handleProtocolMessage(message, this.runtime);
|
|
10028
|
+
});
|
|
10029
|
+
this.idleTransportDisconnected = false;
|
|
10030
|
+
await this.afterRuntimeInitialized();
|
|
10031
|
+
}).finally(() => {
|
|
10032
|
+
if (this.transportRecoveryPromise === recovery)
|
|
10033
|
+
this.transportRecoveryPromise = null;
|
|
10034
|
+
});
|
|
10035
|
+
this.transportRecoveryPromise = recovery;
|
|
10036
|
+
await recovery;
|
|
10037
|
+
}
|
|
10038
|
+
async recoverIdleTransport(_runtime) {
|
|
10039
|
+
throw new Error(`${this.label} sessions do not support idle transport recovery`);
|
|
10040
|
+
}
|
|
10041
|
+
onIdleTransportDisconnect() {}
|
|
10042
|
+
onRecoveredTransportDiscarded() {}
|
|
9951
10043
|
async afterRuntimeInitialized() {}
|
|
9952
10044
|
async beforeTurn() {}
|
|
9953
10045
|
onCoreClose() {}
|
|
@@ -10029,7 +10121,8 @@ class RemoteClientSessionCore {
|
|
|
10029
10121
|
ensureSuccess(response, "Failed to enable memfs");
|
|
10030
10122
|
}
|
|
10031
10123
|
const dreamingSettings = resolveDreamingSettings(options.dreaming);
|
|
10032
|
-
|
|
10124
|
+
const isStatelessSession = this.mode.kind === "session" && this.mode.options.stateless === true;
|
|
10125
|
+
if (dreamingSettings && !isStatelessSession) {
|
|
10033
10126
|
const response = await this.controller.request("set_reflection_settings", {
|
|
10034
10127
|
runtime: this.runtime,
|
|
10035
10128
|
settings: dreamingSettings,
|
|
@@ -10295,7 +10388,8 @@ class AppServerRuntimeController {
|
|
|
10295
10388
|
{
|
|
10296
10389
|
role: "user",
|
|
10297
10390
|
content: normalizeSendMessage(message),
|
|
10298
|
-
client_message_id: options.clientMessageId
|
|
10391
|
+
client_message_id: options.clientMessageId,
|
|
10392
|
+
...options.otid !== undefined ? { otid: options.otid } : {}
|
|
10299
10393
|
}
|
|
10300
10394
|
]
|
|
10301
10395
|
};
|
|
@@ -10522,6 +10616,9 @@ class AppServerSession extends RemoteClientSessionCore {
|
|
|
10522
10616
|
command.mode = mode;
|
|
10523
10617
|
if (options.cwd !== undefined)
|
|
10524
10618
|
command.cwd = options.cwd;
|
|
10619
|
+
if (this.mode.kind === "session" && this.mode.options.stateless === true) {
|
|
10620
|
+
command.stateless = true;
|
|
10621
|
+
}
|
|
10525
10622
|
if (options.skillSources !== undefined) {
|
|
10526
10623
|
command.skill_sources = [...new Set(options.skillSources)];
|
|
10527
10624
|
}
|
|
@@ -10691,6 +10788,132 @@ class CloudManagementTransport {
|
|
|
10691
10788
|
}
|
|
10692
10789
|
}
|
|
10693
10790
|
|
|
10791
|
+
// src/remote.ts
|
|
10792
|
+
function ensureOnline(environment, target) {
|
|
10793
|
+
if (!environment.connectionId) {
|
|
10794
|
+
const label = "deviceId" in target ? target.deviceId : ("environmentId" in target) ? target.environmentId : ("connectionName" in target) ? target.connectionName : environment.deviceId;
|
|
10795
|
+
throw new Error(`Computer is offline: ${label}`);
|
|
10796
|
+
}
|
|
10797
|
+
return {
|
|
10798
|
+
connectionId: environment.connectionId,
|
|
10799
|
+
environment,
|
|
10800
|
+
target
|
|
10801
|
+
};
|
|
10802
|
+
}
|
|
10803
|
+
|
|
10804
|
+
class RemoteEnvironmentClient {
|
|
10805
|
+
client;
|
|
10806
|
+
constructor(options = {}, client = createCloudClient({
|
|
10807
|
+
backend: "cloud",
|
|
10808
|
+
apiBaseUrl: options.baseUrl,
|
|
10809
|
+
apiKey: options.apiKey,
|
|
10810
|
+
headers: options.headers,
|
|
10811
|
+
fetch: options.fetch
|
|
10812
|
+
})) {
|
|
10813
|
+
this.client = client;
|
|
10814
|
+
}
|
|
10815
|
+
async listEnvironments(options = {}) {
|
|
10816
|
+
return await this.client.environments.list({
|
|
10817
|
+
after: options.after,
|
|
10818
|
+
limit: options.limit === undefined ? undefined : String(options.limit),
|
|
10819
|
+
onlineOnly: options.onlineOnly === undefined ? undefined : String(options.onlineOnly)
|
|
10820
|
+
});
|
|
10821
|
+
}
|
|
10822
|
+
async getEnvironmentByDeviceId(deviceId) {
|
|
10823
|
+
return await this.client.environments.retrieve(deviceId);
|
|
10824
|
+
}
|
|
10825
|
+
async resolveEnvironment(target) {
|
|
10826
|
+
if ("connectionId" in target) {
|
|
10827
|
+
return { connectionId: target.connectionId, target };
|
|
10828
|
+
}
|
|
10829
|
+
if ("deviceId" in target) {
|
|
10830
|
+
return ensureOnline(await this.getEnvironmentByDeviceId(target.deviceId), target);
|
|
10831
|
+
}
|
|
10832
|
+
const connections = [];
|
|
10833
|
+
let after;
|
|
10834
|
+
do {
|
|
10835
|
+
const page = await this.listEnvironments({ limit: 100, after });
|
|
10836
|
+
connections.push(...page.connections);
|
|
10837
|
+
if (!page.hasNextPage || page.connections.length === 0)
|
|
10838
|
+
break;
|
|
10839
|
+
after = page.connections.at(-1)?.id;
|
|
10840
|
+
} while (after !== undefined);
|
|
10841
|
+
if ("environmentId" in target) {
|
|
10842
|
+
const match = connections.find((env) => env.id === target.environmentId);
|
|
10843
|
+
if (!match) {
|
|
10844
|
+
throw new Error(`Remote environment not found: ${target.environmentId}`);
|
|
10845
|
+
}
|
|
10846
|
+
return ensureOnline(match, target);
|
|
10847
|
+
}
|
|
10848
|
+
const matches = connections.filter((env) => env.connectionName === target.connectionName);
|
|
10849
|
+
if (matches.length === 0) {
|
|
10850
|
+
throw new Error(`Computer not found: ${target.connectionName}`);
|
|
10851
|
+
}
|
|
10852
|
+
const onlineMatches = matches.filter((environment) => environment.connectionId !== null);
|
|
10853
|
+
if (onlineMatches.length === 0) {
|
|
10854
|
+
throw new Error(`Computer is offline: ${target.connectionName}`);
|
|
10855
|
+
}
|
|
10856
|
+
if (onlineMatches.length > 1) {
|
|
10857
|
+
const candidates = onlineMatches.map((environment) => `${environment.connectionName} (${environment.deviceId}, online)`).join(", ");
|
|
10858
|
+
throw new Error(`Computer name is ambiguous: ${target.connectionName}. Matches: ${candidates}. Select one by deviceId.`);
|
|
10859
|
+
}
|
|
10860
|
+
return ensureOnline(onlineMatches[0], target);
|
|
10861
|
+
}
|
|
10862
|
+
}
|
|
10863
|
+
|
|
10864
|
+
// src/computers.ts
|
|
10865
|
+
function selectorToRemoteTarget(selector) {
|
|
10866
|
+
if (typeof selector === "string")
|
|
10867
|
+
return { connectionName: selector };
|
|
10868
|
+
if ("name" in selector)
|
|
10869
|
+
return { connectionName: selector.name };
|
|
10870
|
+
if ("id" in selector)
|
|
10871
|
+
return { environmentId: selector.id };
|
|
10872
|
+
if ("connectionId" in selector) {
|
|
10873
|
+
return { connectionId: selector.connectionId };
|
|
10874
|
+
}
|
|
10875
|
+
return { deviceId: selector.deviceId };
|
|
10876
|
+
}
|
|
10877
|
+
function toComputer(environment) {
|
|
10878
|
+
return {
|
|
10879
|
+
id: environment.id,
|
|
10880
|
+
deviceId: environment.deviceId,
|
|
10881
|
+
connectionId: environment.connectionId,
|
|
10882
|
+
name: environment.connectionName,
|
|
10883
|
+
status: environment.connectionId ? "online" : "offline",
|
|
10884
|
+
connectedAt: environment.connectedAt,
|
|
10885
|
+
lastSeenAt: environment.lastSeenAt,
|
|
10886
|
+
metadata: environment.metadata
|
|
10887
|
+
};
|
|
10888
|
+
}
|
|
10889
|
+
|
|
10890
|
+
class ComputersClientImpl {
|
|
10891
|
+
environments;
|
|
10892
|
+
constructor(client) {
|
|
10893
|
+
this.environments = new RemoteEnvironmentClient({}, client);
|
|
10894
|
+
}
|
|
10895
|
+
async list(options = {}) {
|
|
10896
|
+
const result = await this.environments.listEnvironments(options);
|
|
10897
|
+
return {
|
|
10898
|
+
computers: result.connections.map(toComputer),
|
|
10899
|
+
hasNextPage: result.hasNextPage
|
|
10900
|
+
};
|
|
10901
|
+
}
|
|
10902
|
+
async get(deviceId) {
|
|
10903
|
+
if (typeof deviceId !== "string" || deviceId.trim().length === 0) {
|
|
10904
|
+
throw new Error("Invalid deviceId. Expected a non-empty string.");
|
|
10905
|
+
}
|
|
10906
|
+
return toComputer(await this.environments.getEnvironmentByDeviceId(deviceId));
|
|
10907
|
+
}
|
|
10908
|
+
async resolve(selector) {
|
|
10909
|
+
const result = await this.environments.resolveEnvironment(selectorToRemoteTarget(selector));
|
|
10910
|
+
return {
|
|
10911
|
+
connectionId: result.connectionId,
|
|
10912
|
+
computer: result.environment ? toComputer(result.environment) : undefined
|
|
10913
|
+
};
|
|
10914
|
+
}
|
|
10915
|
+
}
|
|
10916
|
+
|
|
10694
10917
|
// src/cloud-status-transport.ts
|
|
10695
10918
|
var CONNECTING = 0;
|
|
10696
10919
|
var OPEN = 1;
|
|
@@ -10907,66 +11130,6 @@ function createCloudStatusTransportConstructor(options) {
|
|
|
10907
11130
|
};
|
|
10908
11131
|
}
|
|
10909
11132
|
|
|
10910
|
-
// src/remote.ts
|
|
10911
|
-
function ensureOnline(environment, target) {
|
|
10912
|
-
if (!environment.connectionId) {
|
|
10913
|
-
const label = "deviceId" in target ? target.deviceId : ("environmentId" in target) ? target.environmentId : ("connectionName" in target) ? target.connectionName : environment.deviceId;
|
|
10914
|
-
throw new Error(`Remote environment is offline: ${label}`);
|
|
10915
|
-
}
|
|
10916
|
-
return {
|
|
10917
|
-
connectionId: environment.connectionId,
|
|
10918
|
-
environment,
|
|
10919
|
-
target
|
|
10920
|
-
};
|
|
10921
|
-
}
|
|
10922
|
-
|
|
10923
|
-
class RemoteEnvironmentClient {
|
|
10924
|
-
client;
|
|
10925
|
-
constructor(options = {}, client = createCloudClient({
|
|
10926
|
-
backend: "cloud",
|
|
10927
|
-
apiBaseUrl: options.baseUrl,
|
|
10928
|
-
apiKey: options.apiKey,
|
|
10929
|
-
headers: options.headers,
|
|
10930
|
-
fetch: options.fetch
|
|
10931
|
-
})) {
|
|
10932
|
-
this.client = client;
|
|
10933
|
-
}
|
|
10934
|
-
async listEnvironments() {
|
|
10935
|
-
return await this.client.environments.list();
|
|
10936
|
-
}
|
|
10937
|
-
async getEnvironmentByDeviceId(deviceId) {
|
|
10938
|
-
return await this.client.environments.retrieve(deviceId);
|
|
10939
|
-
}
|
|
10940
|
-
async resolveEnvironment(target) {
|
|
10941
|
-
if ("connectionId" in target) {
|
|
10942
|
-
return { connectionId: target.connectionId, target };
|
|
10943
|
-
}
|
|
10944
|
-
if ("deviceId" in target) {
|
|
10945
|
-
return ensureOnline(await this.getEnvironmentByDeviceId(target.deviceId), target);
|
|
10946
|
-
}
|
|
10947
|
-
const { connections } = await this.listEnvironments();
|
|
10948
|
-
if ("environmentId" in target) {
|
|
10949
|
-
const match2 = connections.find((env) => env.id === target.environmentId);
|
|
10950
|
-
if (!match2) {
|
|
10951
|
-
throw new Error(`Remote environment not found: ${target.environmentId}`);
|
|
10952
|
-
}
|
|
10953
|
-
return ensureOnline(match2, target);
|
|
10954
|
-
}
|
|
10955
|
-
const matches = connections.filter((env) => env.connectionName === target.connectionName);
|
|
10956
|
-
if (matches.length === 0) {
|
|
10957
|
-
throw new Error(`Remote environment not found: ${target.connectionName}`);
|
|
10958
|
-
}
|
|
10959
|
-
if (matches.length > 1) {
|
|
10960
|
-
throw new Error(`Remote environment name is ambiguous: ${target.connectionName}`);
|
|
10961
|
-
}
|
|
10962
|
-
const match = matches[0];
|
|
10963
|
-
if (!match) {
|
|
10964
|
-
throw new Error(`Remote environment not found: ${target.connectionName}`);
|
|
10965
|
-
}
|
|
10966
|
-
return ensureOnline(match, target);
|
|
10967
|
-
}
|
|
10968
|
-
}
|
|
10969
|
-
|
|
10970
11133
|
// src/cloud-sandbox.ts
|
|
10971
11134
|
var MIN_TTL_MINUTES = 1;
|
|
10972
11135
|
var MAX_TTL_MINUTES = 60;
|
|
@@ -11065,8 +11228,12 @@ function validatePositiveInteger3(value, name) {
|
|
|
11065
11228
|
function validateCloudClientOptions(options) {
|
|
11066
11229
|
validatePositiveInteger3(options.requestTimeoutMs, "requestTimeoutMs");
|
|
11067
11230
|
validateCloudSandboxOptions(options.sandbox, "sandbox");
|
|
11068
|
-
if (options.
|
|
11069
|
-
throw new Error("Letta Cloud
|
|
11231
|
+
if (options.computer !== undefined && options.environment !== undefined) {
|
|
11232
|
+
throw new Error("Letta Cloud clients cannot specify both computer and deprecated environment.");
|
|
11233
|
+
}
|
|
11234
|
+
if ((options.computer !== undefined || options.environment !== undefined) && options.sandbox !== undefined) {
|
|
11235
|
+
const field = options.computer !== undefined ? "computer" : "environment";
|
|
11236
|
+
throw new Error(`Letta Cloud sessions cannot specify both ${field} and sandbox options.`);
|
|
11070
11237
|
}
|
|
11071
11238
|
if (options.webSocketAuth !== undefined && options.webSocketAuth !== "header" && options.webSocketAuth !== "query") {
|
|
11072
11239
|
throw new Error("Invalid webSocketAuth. Valid values: header, query.");
|
|
@@ -11183,12 +11350,30 @@ class CloudEnvironmentSession extends RemoteClientSessionCore {
|
|
|
11183
11350
|
throw error;
|
|
11184
11351
|
});
|
|
11185
11352
|
this.connectionId = connection.connectionId;
|
|
11353
|
+
const options = this.currentOptions();
|
|
11354
|
+
this.mcpBridge = await connectMcpServers("mcpServers" in options ? options.mcpServers : undefined, {
|
|
11355
|
+
cwd: options.cwd,
|
|
11356
|
+
reservedToolNames: this.externalTools.keys()
|
|
11357
|
+
});
|
|
11358
|
+
for (const tool of this.mcpBridge.tools) {
|
|
11359
|
+
this.externalTools.set(tool.name, tool);
|
|
11360
|
+
}
|
|
11361
|
+
try {
|
|
11362
|
+
return await this.openCloudTransport(resolved.runtime, connection.connectionId);
|
|
11363
|
+
} catch (error) {
|
|
11364
|
+
await this.closeMcpBridge();
|
|
11365
|
+
await this.cleanupManagedSandbox();
|
|
11366
|
+
await this.cleanupSessionRepositories(resolved.runtime.agent_id, resolved.runtime.conversation_id);
|
|
11367
|
+
throw error;
|
|
11368
|
+
}
|
|
11369
|
+
}
|
|
11370
|
+
async openCloudTransport(runtime, connectionId) {
|
|
11186
11371
|
const apiKey = getCloudApiKey(this.cloudOptions);
|
|
11187
11372
|
const url = buildCloudStatusWebSocketUrl({
|
|
11188
11373
|
apiBaseUrl: this.cloudOptions.apiBaseUrl,
|
|
11189
|
-
connectionId
|
|
11190
|
-
agentId:
|
|
11191
|
-
conversationId:
|
|
11374
|
+
connectionId,
|
|
11375
|
+
agentId: runtime.agent_id,
|
|
11376
|
+
conversationId: runtime.conversation_id,
|
|
11192
11377
|
apiKey,
|
|
11193
11378
|
authMode: this.cloudOptions.webSocketAuth ?? "header"
|
|
11194
11379
|
});
|
|
@@ -11199,33 +11384,19 @@ class CloudEnvironmentSession extends RemoteClientSessionCore {
|
|
|
11199
11384
|
WebSocket: getWebSocketConstructor(this.cloudOptions.WebSocket),
|
|
11200
11385
|
...(this.cloudOptions.webSocketAuth ?? "header") === "header" ? { headers: cloudWebSocketHeaders(this.cloudOptions) } : {},
|
|
11201
11386
|
pingIntervalMs: this.cloudOptions.pingIntervalMs ?? DEFAULT_PING_INTERVAL_MS,
|
|
11202
|
-
runtime
|
|
11387
|
+
runtime
|
|
11203
11388
|
}),
|
|
11204
11389
|
requestTimeoutMs: this.cloudOptions.requestTimeoutMs ?? DEFAULT_TURN_TIMEOUT_MS
|
|
11205
11390
|
}));
|
|
11206
11391
|
const options = this.currentOptions();
|
|
11207
|
-
this.
|
|
11208
|
-
cwd: options.cwd,
|
|
11209
|
-
reservedToolNames: this.externalTools.keys()
|
|
11210
|
-
});
|
|
11211
|
-
for (const tool of this.mcpBridge.tools) {
|
|
11212
|
-
this.externalTools.set(tool.name, tool);
|
|
11213
|
-
}
|
|
11214
|
-
this.removeControlRequestHandler = registerAppServerControlRequestHandler({
|
|
11215
|
-
client,
|
|
11216
|
-
getRuntime: () => this.runtime,
|
|
11217
|
-
getOptions: () => this.currentOptions()
|
|
11218
|
-
});
|
|
11219
|
-
if (this.externalTools.size > 0) {
|
|
11220
|
-
this.removeExternalToolHandler = client.onExternalToolCall(createExternalToolCallHandler(this.externalTools));
|
|
11221
|
-
}
|
|
11392
|
+
this.installTransportHandlers(client);
|
|
11222
11393
|
try {
|
|
11223
11394
|
await client.connect();
|
|
11224
|
-
const response = await this.startCloudRuntime(client,
|
|
11395
|
+
const response = await this.startCloudRuntime(client, runtime);
|
|
11225
11396
|
if (!response.success || !response.runtime) {
|
|
11226
11397
|
throw new Error(response.error ?? "Failed to start Cloud status runtime");
|
|
11227
11398
|
}
|
|
11228
|
-
this.watchTransportDisconnect(client);
|
|
11399
|
+
this.watchTransportDisconnect(client, { recoverWhenIdle: true });
|
|
11229
11400
|
const tools = agentToolNames(response.agent);
|
|
11230
11401
|
const skillSources = options.skillSources;
|
|
11231
11402
|
const clientToolset = "toolset" in options ? options.toolset : undefined;
|
|
@@ -11243,17 +11414,51 @@ class CloudEnvironmentSession extends RemoteClientSessionCore {
|
|
|
11243
11414
|
...skillSources !== undefined ? { skillSources: [...skillSources] } : {}
|
|
11244
11415
|
};
|
|
11245
11416
|
} catch (error) {
|
|
11246
|
-
this.
|
|
11247
|
-
this.removeExternalToolHandler = null;
|
|
11248
|
-
this.removeControlRequestHandler?.();
|
|
11249
|
-
this.removeControlRequestHandler = null;
|
|
11250
|
-
await this.closeMcpBridge();
|
|
11417
|
+
this.cleanupTransportHandlers();
|
|
11251
11418
|
client.close();
|
|
11252
|
-
await this.cleanupManagedSandbox();
|
|
11253
|
-
await this.cleanupSessionRepositories(resolved.runtime.agent_id, resolved.runtime.conversation_id);
|
|
11254
11419
|
throw error;
|
|
11255
11420
|
}
|
|
11256
11421
|
}
|
|
11422
|
+
installTransportHandlers(client) {
|
|
11423
|
+
this.cleanupTransportHandlers();
|
|
11424
|
+
this.removeControlRequestHandler = registerAppServerControlRequestHandler({
|
|
11425
|
+
client,
|
|
11426
|
+
getRuntime: () => this.runtime,
|
|
11427
|
+
getOptions: () => this.currentOptions()
|
|
11428
|
+
});
|
|
11429
|
+
if (this.externalTools.size > 0) {
|
|
11430
|
+
this.removeExternalToolHandler = client.onExternalToolCall(createExternalToolCallHandler(this.externalTools));
|
|
11431
|
+
}
|
|
11432
|
+
}
|
|
11433
|
+
cleanupTransportHandlers() {
|
|
11434
|
+
this.removeExternalToolHandler?.();
|
|
11435
|
+
this.removeExternalToolHandler = null;
|
|
11436
|
+
this.removeControlRequestHandler?.();
|
|
11437
|
+
this.removeControlRequestHandler = null;
|
|
11438
|
+
}
|
|
11439
|
+
async recoverIdleTransport(runtime) {
|
|
11440
|
+
const connection = await this.resolveRecoveryConnection(runtime);
|
|
11441
|
+
this.connectionId = connection.connectionId;
|
|
11442
|
+
return this.openCloudTransport(runtime, connection.connectionId);
|
|
11443
|
+
}
|
|
11444
|
+
async resolveRecoveryConnection(runtime) {
|
|
11445
|
+
const sandbox = this.managedSandbox;
|
|
11446
|
+
if (sandbox) {
|
|
11447
|
+
await this.refreshManagedSandbox(sandbox);
|
|
11448
|
+
return this.waitForManagedSandboxConnection(sandbox);
|
|
11449
|
+
}
|
|
11450
|
+
const environment = this.effectiveEnvironment();
|
|
11451
|
+
if (!environment) {
|
|
11452
|
+
throw new Error("Cloud idle transport recovery lost its execution target");
|
|
11453
|
+
}
|
|
11454
|
+
return this.resolveExplicitConnection(environment);
|
|
11455
|
+
}
|
|
11456
|
+
onIdleTransportDisconnect() {
|
|
11457
|
+
this.cleanupTransportHandlers();
|
|
11458
|
+
}
|
|
11459
|
+
onRecoveredTransportDiscarded() {
|
|
11460
|
+
this.cleanupTransportHandlers();
|
|
11461
|
+
}
|
|
11257
11462
|
async startCloudRuntime(client, runtime) {
|
|
11258
11463
|
const options = this.currentOptions();
|
|
11259
11464
|
const command = {
|
|
@@ -11271,6 +11476,8 @@ class CloudEnvironmentSession extends RemoteClientSessionCore {
|
|
|
11271
11476
|
command.mode = mode;
|
|
11272
11477
|
if (options.cwd !== undefined)
|
|
11273
11478
|
command.cwd = options.cwd;
|
|
11479
|
+
if (this.cloudMode.options.stateless === true)
|
|
11480
|
+
command.stateless = true;
|
|
11274
11481
|
if (options.skillSources !== undefined) {
|
|
11275
11482
|
command.skill_sources = [...new Set(options.skillSources)];
|
|
11276
11483
|
}
|
|
@@ -11296,10 +11503,7 @@ class CloudEnvironmentSession extends RemoteClientSessionCore {
|
|
|
11296
11503
|
await this.refreshManagedSandbox(sandbox);
|
|
11297
11504
|
}
|
|
11298
11505
|
onCoreClose() {
|
|
11299
|
-
this.
|
|
11300
|
-
this.removeExternalToolHandler = null;
|
|
11301
|
-
this.removeControlRequestHandler?.();
|
|
11302
|
-
this.removeControlRequestHandler = null;
|
|
11506
|
+
this.cleanupTransportHandlers();
|
|
11303
11507
|
this.mcpCleanup = this.closeMcpBridge();
|
|
11304
11508
|
this.cleanupManagedSandbox();
|
|
11305
11509
|
if (this.runtime?.agent_id) {
|
|
@@ -11623,8 +11827,8 @@ class CloudEnvironmentSession extends RemoteClientSessionCore {
|
|
|
11623
11827
|
return new RemoteEnvironmentClient({}, this.apiClient);
|
|
11624
11828
|
}
|
|
11625
11829
|
effectiveEnvironment() {
|
|
11626
|
-
const
|
|
11627
|
-
return
|
|
11830
|
+
const modeComputer = this.mode.kind === "session" ? this.mode.options.computer ?? this.mode.options.environment : undefined;
|
|
11831
|
+
return modeComputer ?? this.cloudOptions.computer ?? this.cloudOptions.environment;
|
|
11628
11832
|
}
|
|
11629
11833
|
effectiveSandboxOptions() {
|
|
11630
11834
|
const modeSandbox = this.mode.kind === "session" ? this.mode.options.sandbox : undefined;
|
|
@@ -11878,6 +12082,20 @@ function validateMcpServers(servers) {
|
|
|
11878
12082
|
}
|
|
11879
12083
|
}
|
|
11880
12084
|
function validateCreateSessionOptions(options) {
|
|
12085
|
+
if (options.stateless !== undefined && typeof options.stateless !== "boolean") {
|
|
12086
|
+
throw new Error("Invalid stateless. Expected a boolean.");
|
|
12087
|
+
}
|
|
12088
|
+
if (options.stateless) {
|
|
12089
|
+
const persistedOption = [
|
|
12090
|
+
["model", options.model],
|
|
12091
|
+
["reasoningEffort", options.reasoningEffort],
|
|
12092
|
+
["dreaming", options.dreaming],
|
|
12093
|
+
["resources", options.resources]
|
|
12094
|
+
].find(([, value]) => value !== undefined);
|
|
12095
|
+
if (persistedOption) {
|
|
12096
|
+
throw new Error(`stateless sessions cannot set ${persistedOption[0]} because it changes persisted agent configuration.`);
|
|
12097
|
+
}
|
|
12098
|
+
}
|
|
11881
12099
|
validateClientToolset(options.toolset);
|
|
11882
12100
|
validateSkillSources(options.skillSources);
|
|
11883
12101
|
validateMcpServers(options.mcpServers);
|
|
@@ -11924,14 +12142,17 @@ var VALID_BACKENDS = new Set([
|
|
|
11924
12142
|
function isLettaCodeBackend(value) {
|
|
11925
12143
|
return VALID_BACKENDS.has(value);
|
|
11926
12144
|
}
|
|
11927
|
-
function
|
|
11928
|
-
if ("environment" in options)
|
|
11929
|
-
return
|
|
12145
|
+
function getOptionsComputer(options) {
|
|
12146
|
+
if (!("computer" in options) && !("environment" in options))
|
|
12147
|
+
return;
|
|
12148
|
+
if (options.computer !== undefined && options.environment !== undefined) {
|
|
12149
|
+
throw new Error('Specify either "computer" or deprecated "environment", not both.');
|
|
11930
12150
|
}
|
|
11931
|
-
return;
|
|
12151
|
+
return options.computer ?? options.environment;
|
|
11932
12152
|
}
|
|
11933
12153
|
function stripCloudExecutionOptions(options) {
|
|
11934
12154
|
const sessionOptions = { ...options };
|
|
12155
|
+
delete sessionOptions.computer;
|
|
11935
12156
|
delete sessionOptions.environment;
|
|
11936
12157
|
delete sessionOptions.sandbox;
|
|
11937
12158
|
delete sessionOptions.filesystemConfinement;
|
|
@@ -11949,12 +12170,14 @@ function looksLikeConversationId(id) {
|
|
|
11949
12170
|
|
|
11950
12171
|
class LettaAgentClientBase {
|
|
11951
12172
|
backend;
|
|
12173
|
+
computer;
|
|
11952
12174
|
environment;
|
|
11953
12175
|
agents;
|
|
11954
12176
|
conversations;
|
|
11955
12177
|
models;
|
|
11956
12178
|
options;
|
|
11957
12179
|
repositoriesClient = null;
|
|
12180
|
+
computersClient = null;
|
|
11958
12181
|
agentRepositoriesClient = null;
|
|
11959
12182
|
cloudClient = null;
|
|
11960
12183
|
managementTransport = null;
|
|
@@ -11964,16 +12187,19 @@ class LettaAgentClientBase {
|
|
|
11964
12187
|
throw new Error(`Invalid Letta Code backend '${String(backend)}'. Valid values: local, remote, cloud.`);
|
|
11965
12188
|
}
|
|
11966
12189
|
this.backend = backend;
|
|
11967
|
-
this.
|
|
12190
|
+
this.computer = getOptionsComputer(options);
|
|
12191
|
+
this.environment = this.computer;
|
|
11968
12192
|
this.options = options;
|
|
11969
12193
|
this.agents = createAgentsClient(() => this.getManagementTransport(), () => this.getAgentRepositoriesClient());
|
|
11970
12194
|
this.conversations = createConversationsClient(() => this.getManagementTransport());
|
|
11971
12195
|
this.models = createModelsClient(() => this.getManagementTransport());
|
|
11972
|
-
if (this.backend === "local" && this.
|
|
11973
|
-
|
|
12196
|
+
if (this.backend === "local" && this.computer !== undefined) {
|
|
12197
|
+
const field = "computer" in options && options.computer !== undefined ? "computer" : "environment";
|
|
12198
|
+
throw new Error(`LettaAgentClient ${field} is only valid with backend: "cloud".`);
|
|
11974
12199
|
}
|
|
11975
|
-
if (this.backend === "remote" && this.
|
|
11976
|
-
|
|
12200
|
+
if (this.backend === "remote" && this.computer !== undefined) {
|
|
12201
|
+
const field = "computer" in options && options.computer !== undefined ? "computer" : "environment";
|
|
12202
|
+
throw new Error(`LettaAgentClient ${field} is only valid with backend: "cloud"; remote url selects the app-server runtime.`);
|
|
11977
12203
|
}
|
|
11978
12204
|
if (this.backend !== "cloud" && options.sandbox !== undefined) {
|
|
11979
12205
|
throw new Error('LettaAgentClient sandbox options are only valid with backend: "cloud".');
|
|
@@ -12007,6 +12233,9 @@ class LettaAgentClientBase {
|
|
|
12007
12233
|
get repositories() {
|
|
12008
12234
|
return this.getRepositoriesClient();
|
|
12009
12235
|
}
|
|
12236
|
+
get computers() {
|
|
12237
|
+
return this.getComputersClient();
|
|
12238
|
+
}
|
|
12010
12239
|
async createAgent(options = {}) {
|
|
12011
12240
|
if (hasCreateAgentEnvironment(options)) {
|
|
12012
12241
|
throw new Error("createAgent() does not accept environment. Set a client default or pass environment to resumeSession()/createSession().");
|
|
@@ -12096,13 +12325,17 @@ class LettaAgentClientBase {
|
|
|
12096
12325
|
}
|
|
12097
12326
|
}
|
|
12098
12327
|
assertSessionBackend(action, options) {
|
|
12328
|
+
if (options.computer !== undefined && options.environment !== undefined) {
|
|
12329
|
+
throw new Error(`${action}() cannot specify both computer and deprecated environment.`);
|
|
12330
|
+
}
|
|
12099
12331
|
if (options.filesystemConfinement !== undefined && options.filesystemConfinement !== "memory") {
|
|
12100
12332
|
throw new Error(`Invalid filesystemConfinement '${String(options.filesystemConfinement)}'. Valid value: memory.`);
|
|
12101
12333
|
}
|
|
12102
|
-
const
|
|
12334
|
+
const effectiveComputer = options.computer ?? options.environment ?? this.computer;
|
|
12103
12335
|
if (this.backend === "local") {
|
|
12104
|
-
if (
|
|
12105
|
-
|
|
12336
|
+
if (effectiveComputer !== undefined) {
|
|
12337
|
+
const field = options.computer !== undefined ? "computer" : "environment";
|
|
12338
|
+
throw new Error(`${action}() ${field} overrides are only valid with backend: "cloud".`);
|
|
12106
12339
|
}
|
|
12107
12340
|
if (options.sandbox !== undefined) {
|
|
12108
12341
|
throw new Error(`${action}() sandbox options are only valid with backend: "cloud".`);
|
|
@@ -12122,8 +12355,9 @@ class LettaAgentClientBase {
|
|
|
12122
12355
|
if (options.filesystemConfinement !== undefined) {
|
|
12123
12356
|
throw new Error(`${action}() filesystemConfinement requires an SDK-owned local app-server process.`);
|
|
12124
12357
|
}
|
|
12125
|
-
if (options.environment !== undefined) {
|
|
12126
|
-
|
|
12358
|
+
if (options.computer !== undefined || options.environment !== undefined) {
|
|
12359
|
+
const field = options.computer !== undefined ? "computer" : "environment";
|
|
12360
|
+
throw new Error(`${action}() ${field} overrides are only valid with backend: "cloud"; remote url selects the app-server runtime.`);
|
|
12127
12361
|
}
|
|
12128
12362
|
if (options.sandbox !== undefined) {
|
|
12129
12363
|
throw new Error(`${action}() sandbox options are only valid with backend: "cloud"; remote url selects the app-server runtime.`);
|
|
@@ -12138,11 +12372,13 @@ class LettaAgentClientBase {
|
|
|
12138
12372
|
throw new Error(`${action}() filesystemConfinement is only supported with backend: "local".`);
|
|
12139
12373
|
}
|
|
12140
12374
|
const cloudOptions = this.cloudOptions();
|
|
12141
|
-
if (
|
|
12142
|
-
|
|
12375
|
+
if (this.computer !== undefined && options.sandbox !== undefined) {
|
|
12376
|
+
const field = cloudOptions.computer !== undefined ? "computer" : "environment";
|
|
12377
|
+
throw new Error(`Letta Cloud ${action}() cannot specify sandbox options when the client has a default ${field}.`);
|
|
12143
12378
|
}
|
|
12144
|
-
if (cloudOptions.sandbox !== undefined && options.environment !== undefined) {
|
|
12145
|
-
|
|
12379
|
+
if (cloudOptions.sandbox !== undefined && (options.computer !== undefined || options.environment !== undefined)) {
|
|
12380
|
+
const field = options.computer !== undefined ? "a computer" : "an environment";
|
|
12381
|
+
throw new Error(`Letta Cloud ${action}() cannot specify ${field} when the client has default sandbox options.`);
|
|
12146
12382
|
}
|
|
12147
12383
|
assertCloudSessionOptionsSupported(action, options);
|
|
12148
12384
|
return;
|
|
@@ -12173,6 +12409,13 @@ class LettaAgentClientBase {
|
|
|
12173
12409
|
appServerSessionOptions() {
|
|
12174
12410
|
return this.remoteOptions();
|
|
12175
12411
|
}
|
|
12412
|
+
getComputersClient() {
|
|
12413
|
+
if (this.backend !== "cloud") {
|
|
12414
|
+
throw new Error('client.computers is only available with backend: "cloud".');
|
|
12415
|
+
}
|
|
12416
|
+
this.computersClient ??= new ComputersClientImpl(this.getCloudClient());
|
|
12417
|
+
return this.computersClient;
|
|
12418
|
+
}
|
|
12176
12419
|
getRepositoriesClient() {
|
|
12177
12420
|
if (this.backend !== "cloud") {
|
|
12178
12421
|
throw new Error('client.repositories is only available with backend: "cloud".');
|
|
@@ -29431,7 +29674,7 @@ class StreamableHTTPClientTransport {
|
|
|
29431
29674
|
}
|
|
29432
29675
|
var DEFAULT_CLIENT_INFO = {
|
|
29433
29676
|
name: "letta-code",
|
|
29434
|
-
version: "0.30.
|
|
29677
|
+
version: "0.30.11"
|
|
29435
29678
|
};
|
|
29436
29679
|
async function connectMcpServer(config2, options = {}) {
|
|
29437
29680
|
let client = new Client(options.clientInfo ?? DEFAULT_CLIENT_INFO);
|
|
@@ -29749,6 +29992,440 @@ function extractStreamTextDelta(event) {
|
|
|
29749
29992
|
}
|
|
29750
29993
|
return null;
|
|
29751
29994
|
}
|
|
29995
|
+
// src/transcript-accumulator.ts
|
|
29996
|
+
var SEP = ":";
|
|
29997
|
+
var MAX_TRACKED_RUNS = 64;
|
|
29998
|
+
var ANONYMOUS_RUN = "";
|
|
29999
|
+
function familyOtidAlias(kind, otid) {
|
|
30000
|
+
return `${kind}${SEP}otid${SEP}${otid}`;
|
|
30001
|
+
}
|
|
30002
|
+
function familyUuidAlias(kind, uuid2) {
|
|
30003
|
+
return `${kind}${SEP}uuid${SEP}${uuid2}`;
|
|
30004
|
+
}
|
|
30005
|
+
function toolRowKey(toolCallId) {
|
|
30006
|
+
return `tool_call${SEP}id${SEP}${toolCallId}`;
|
|
30007
|
+
}
|
|
30008
|
+
function asRecord(value) {
|
|
30009
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
30010
|
+
}
|
|
30011
|
+
function readString(record2, field) {
|
|
30012
|
+
const value = record2[field];
|
|
30013
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
30014
|
+
}
|
|
30015
|
+
function readNumber(record2, field) {
|
|
30016
|
+
const value = record2[field];
|
|
30017
|
+
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
30018
|
+
}
|
|
30019
|
+
function parseJsonObject(raw) {
|
|
30020
|
+
const trimmed = raw.trim();
|
|
30021
|
+
if (trimmed.length === 0)
|
|
30022
|
+
return;
|
|
30023
|
+
try {
|
|
30024
|
+
return asRecord(JSON.parse(trimmed));
|
|
30025
|
+
} catch {
|
|
30026
|
+
return;
|
|
30027
|
+
}
|
|
30028
|
+
}
|
|
30029
|
+
function isRawArgumentsWrapper(input, rawArguments) {
|
|
30030
|
+
if (!input)
|
|
30031
|
+
return false;
|
|
30032
|
+
const keys = Object.keys(input);
|
|
30033
|
+
if (keys.length !== 1 || keys[0] !== "raw")
|
|
30034
|
+
return false;
|
|
30035
|
+
const wrapped = input.raw;
|
|
30036
|
+
if (typeof wrapped !== "string")
|
|
30037
|
+
return false;
|
|
30038
|
+
return rawArguments === undefined || wrapped === rawArguments;
|
|
30039
|
+
}
|
|
30040
|
+
function toolStatus(row) {
|
|
30041
|
+
if (row.result)
|
|
30042
|
+
return "complete";
|
|
30043
|
+
return row.argumentsComplete ? "ready" : "streaming";
|
|
30044
|
+
}
|
|
30045
|
+
function mergeToolArguments(previous, merge2) {
|
|
30046
|
+
let { rawArguments, toolInput, argumentsComplete } = previous;
|
|
30047
|
+
const fragment = merge2.rawArguments;
|
|
30048
|
+
const wrapped = isRawArgumentsWrapper(merge2.toolInput, fragment);
|
|
30049
|
+
if (fragment !== undefined && fragment.length > 0) {
|
|
30050
|
+
const whole = parseJsonObject(fragment);
|
|
30051
|
+
if (whole) {
|
|
30052
|
+
return { rawArguments: fragment, toolInput: whole, argumentsComplete: true };
|
|
30053
|
+
}
|
|
30054
|
+
if (argumentsComplete) {
|
|
30055
|
+
return previous;
|
|
30056
|
+
}
|
|
30057
|
+
if (merge2.mode === "whole") {
|
|
30058
|
+
return { rawArguments: rawArguments ?? fragment, toolInput, argumentsComplete };
|
|
30059
|
+
}
|
|
30060
|
+
rawArguments = (rawArguments ?? "") + fragment;
|
|
30061
|
+
const parsed = parseJsonObject(rawArguments);
|
|
30062
|
+
if (parsed) {
|
|
30063
|
+
return { rawArguments, toolInput: parsed, argumentsComplete: true };
|
|
30064
|
+
}
|
|
30065
|
+
return { rawArguments, toolInput, argumentsComplete: false };
|
|
30066
|
+
}
|
|
30067
|
+
if (!wrapped && merge2.toolInput) {
|
|
30068
|
+
const keys = Object.keys(merge2.toolInput);
|
|
30069
|
+
if (keys.length > 0) {
|
|
30070
|
+
return { rawArguments, toolInput: merge2.toolInput, argumentsComplete: true };
|
|
30071
|
+
}
|
|
30072
|
+
if (rawArguments === undefined) {
|
|
30073
|
+
return { rawArguments, toolInput, argumentsComplete: true };
|
|
30074
|
+
}
|
|
30075
|
+
}
|
|
30076
|
+
return previous;
|
|
30077
|
+
}
|
|
30078
|
+
|
|
30079
|
+
class TranscriptAccumulatorImpl {
|
|
30080
|
+
byKey = new Map;
|
|
30081
|
+
aliasByOtid = new Map;
|
|
30082
|
+
aliasByUuid = new Map;
|
|
30083
|
+
seqThresholds = new Map;
|
|
30084
|
+
snapshot = null;
|
|
30085
|
+
anonymousCounter = 0;
|
|
30086
|
+
apply(message) {
|
|
30087
|
+
switch (message.type) {
|
|
30088
|
+
case "assistant":
|
|
30089
|
+
case "reasoning":
|
|
30090
|
+
this.applyText({
|
|
30091
|
+
kind: message.type,
|
|
30092
|
+
text: message.content,
|
|
30093
|
+
uuid: message.uuid,
|
|
30094
|
+
otid: typeof message.otid === "string" ? message.otid : undefined,
|
|
30095
|
+
runId: message.runId,
|
|
30096
|
+
seqId: message.seqId
|
|
30097
|
+
});
|
|
30098
|
+
break;
|
|
30099
|
+
case "tool_call":
|
|
30100
|
+
this.mergeToolCall({
|
|
30101
|
+
toolCallId: message.toolCallId,
|
|
30102
|
+
toolName: message.toolName,
|
|
30103
|
+
toolInput: message.toolInput,
|
|
30104
|
+
rawArguments: message.rawArguments,
|
|
30105
|
+
uuid: message.uuid,
|
|
30106
|
+
runId: message.runId,
|
|
30107
|
+
mode: "fragment"
|
|
30108
|
+
});
|
|
30109
|
+
break;
|
|
30110
|
+
case "tool_result":
|
|
30111
|
+
this.mergeToolResult({
|
|
30112
|
+
toolCallId: message.toolCallId,
|
|
30113
|
+
content: message.content,
|
|
30114
|
+
isError: message.isError,
|
|
30115
|
+
uuid: message.uuid,
|
|
30116
|
+
runId: message.runId
|
|
30117
|
+
});
|
|
30118
|
+
break;
|
|
30119
|
+
case "stream_event":
|
|
30120
|
+
this.applyStreamEvent(message);
|
|
30121
|
+
break;
|
|
30122
|
+
default:
|
|
30123
|
+
break;
|
|
30124
|
+
}
|
|
30125
|
+
return this.rows();
|
|
30126
|
+
}
|
|
30127
|
+
rebase(page, options) {
|
|
30128
|
+
const messages = normalizeHistoryPage(page, options?.order);
|
|
30129
|
+
if (messages.length === 0)
|
|
30130
|
+
return this.rows();
|
|
30131
|
+
const historyKeys = [];
|
|
30132
|
+
const seen = new Set;
|
|
30133
|
+
for (const message of messages) {
|
|
30134
|
+
const key = this.applyHistoryMessage(message);
|
|
30135
|
+
if (!key || seen.has(key))
|
|
30136
|
+
continue;
|
|
30137
|
+
seen.add(key);
|
|
30138
|
+
historyKeys.push(key);
|
|
30139
|
+
}
|
|
30140
|
+
this.reorder(historyKeys);
|
|
30141
|
+
return this.rows();
|
|
30142
|
+
}
|
|
30143
|
+
rows() {
|
|
30144
|
+
if (!this.snapshot) {
|
|
30145
|
+
this.snapshot = Array.from(this.byKey.values());
|
|
30146
|
+
}
|
|
30147
|
+
return this.snapshot;
|
|
30148
|
+
}
|
|
30149
|
+
reset() {
|
|
30150
|
+
this.byKey.clear();
|
|
30151
|
+
this.aliasByOtid.clear();
|
|
30152
|
+
this.aliasByUuid.clear();
|
|
30153
|
+
this.seqThresholds.clear();
|
|
30154
|
+
this.anonymousCounter = 0;
|
|
30155
|
+
this.snapshot = null;
|
|
30156
|
+
}
|
|
30157
|
+
isReplay(runId, seqId) {
|
|
30158
|
+
if (seqId === undefined)
|
|
30159
|
+
return false;
|
|
30160
|
+
const bucket = runId ?? ANONYMOUS_RUN;
|
|
30161
|
+
const threshold = this.seqThresholds.get(bucket);
|
|
30162
|
+
if (threshold !== undefined && seqId <= threshold)
|
|
30163
|
+
return true;
|
|
30164
|
+
this.rememberSeq(bucket, seqId);
|
|
30165
|
+
return false;
|
|
30166
|
+
}
|
|
30167
|
+
rememberSeq(bucket, seqId) {
|
|
30168
|
+
const threshold = this.seqThresholds.get(bucket);
|
|
30169
|
+
if (threshold !== undefined && threshold >= seqId)
|
|
30170
|
+
return;
|
|
30171
|
+
this.seqThresholds.set(bucket, seqId);
|
|
30172
|
+
while (this.seqThresholds.size > MAX_TRACKED_RUNS) {
|
|
30173
|
+
const oldest = this.seqThresholds.keys().next();
|
|
30174
|
+
if (oldest.done)
|
|
30175
|
+
break;
|
|
30176
|
+
this.seqThresholds.delete(oldest.value);
|
|
30177
|
+
}
|
|
30178
|
+
}
|
|
30179
|
+
applyText(slice) {
|
|
30180
|
+
if (this.isReplay(slice.runId, slice.seqId))
|
|
30181
|
+
return;
|
|
30182
|
+
this.writeText(slice, "append");
|
|
30183
|
+
}
|
|
30184
|
+
writeText(slice, write) {
|
|
30185
|
+
const key = this.resolveTextKey(slice.kind, slice.uuid, slice.otid);
|
|
30186
|
+
const existing = this.byKey.get(key);
|
|
30187
|
+
const previous = existing && existing.kind === slice.kind ? existing : undefined;
|
|
30188
|
+
this.setRow(key, {
|
|
30189
|
+
kind: slice.kind,
|
|
30190
|
+
key,
|
|
30191
|
+
text: write === "append" ? (previous?.text ?? "") + slice.text : slice.text,
|
|
30192
|
+
uuid: previous?.uuid ?? slice.uuid,
|
|
30193
|
+
otid: slice.otid ?? previous?.otid,
|
|
30194
|
+
runId: slice.runId ?? previous?.runId,
|
|
30195
|
+
seqId: maxDefined(slice.seqId, previous?.seqId)
|
|
30196
|
+
});
|
|
30197
|
+
return key;
|
|
30198
|
+
}
|
|
30199
|
+
resolveTextKey(kind, uuid2, otid) {
|
|
30200
|
+
const otidAlias = otid ? familyOtidAlias(kind, otid) : undefined;
|
|
30201
|
+
const uuidAlias = uuid2 ? familyUuidAlias(kind, uuid2) : undefined;
|
|
30202
|
+
const fromOtid = otidAlias ? this.aliasByOtid.get(otidAlias) : undefined;
|
|
30203
|
+
const fromUuid = uuidAlias ? this.aliasByUuid.get(uuidAlias) : undefined;
|
|
30204
|
+
let key = fromOtid ?? fromUuid;
|
|
30205
|
+
if (key && !fromOtid && otid) {
|
|
30206
|
+
const existing = this.byKey.get(key);
|
|
30207
|
+
if (existing && existing.otid !== undefined && existing.otid !== otid) {
|
|
30208
|
+
key = undefined;
|
|
30209
|
+
}
|
|
30210
|
+
}
|
|
30211
|
+
if (!key) {
|
|
30212
|
+
key = otidAlias ?? uuidAlias ?? `${kind}${SEP}auto${SEP}${++this.anonymousCounter}`;
|
|
30213
|
+
}
|
|
30214
|
+
if (otidAlias)
|
|
30215
|
+
this.aliasByOtid.set(otidAlias, key);
|
|
30216
|
+
if (uuidAlias)
|
|
30217
|
+
this.aliasByUuid.set(uuidAlias, key);
|
|
30218
|
+
return key;
|
|
30219
|
+
}
|
|
30220
|
+
mergeToolCall(merge2) {
|
|
30221
|
+
const key = toolRowKey(merge2.toolCallId);
|
|
30222
|
+
const existing = this.byKey.get(key);
|
|
30223
|
+
const previous = existing && existing.kind === "tool_call" ? existing : undefined;
|
|
30224
|
+
const args = mergeToolArguments({
|
|
30225
|
+
rawArguments: previous?.rawArguments,
|
|
30226
|
+
toolInput: previous?.toolInput ?? {},
|
|
30227
|
+
argumentsComplete: previous?.argumentsComplete ?? false
|
|
30228
|
+
}, merge2);
|
|
30229
|
+
const toolName = merge2.toolName && merge2.toolName !== "?" ? merge2.toolName : previous?.toolName ?? merge2.toolName ?? "?";
|
|
30230
|
+
const next = {
|
|
30231
|
+
kind: "tool_call",
|
|
30232
|
+
key,
|
|
30233
|
+
toolCallId: merge2.toolCallId,
|
|
30234
|
+
toolName,
|
|
30235
|
+
toolInput: args.toolInput,
|
|
30236
|
+
...args.rawArguments !== undefined ? { rawArguments: args.rawArguments } : {},
|
|
30237
|
+
argumentsComplete: args.argumentsComplete,
|
|
30238
|
+
...previous?.result ? { result: previous.result } : {},
|
|
30239
|
+
status: "streaming",
|
|
30240
|
+
uuid: previous?.uuid ?? merge2.uuid,
|
|
30241
|
+
runId: merge2.runId ?? previous?.runId,
|
|
30242
|
+
seqId: maxDefined(merge2.seqId, previous?.seqId)
|
|
30243
|
+
};
|
|
30244
|
+
next.status = toolStatus(next);
|
|
30245
|
+
this.setRow(key, next);
|
|
30246
|
+
return key;
|
|
30247
|
+
}
|
|
30248
|
+
mergeToolResult(merge2) {
|
|
30249
|
+
const key = toolRowKey(merge2.toolCallId);
|
|
30250
|
+
const existing = this.byKey.get(key);
|
|
30251
|
+
const previous = existing && existing.kind === "tool_call" ? existing : undefined;
|
|
30252
|
+
this.setRow(key, {
|
|
30253
|
+
kind: "tool_call",
|
|
30254
|
+
key,
|
|
30255
|
+
toolCallId: merge2.toolCallId,
|
|
30256
|
+
toolName: previous?.toolName ?? "?",
|
|
30257
|
+
toolInput: previous?.toolInput ?? {},
|
|
30258
|
+
...previous?.rawArguments !== undefined ? { rawArguments: previous.rawArguments } : {},
|
|
30259
|
+
argumentsComplete: previous?.argumentsComplete ?? false,
|
|
30260
|
+
result: {
|
|
30261
|
+
content: merge2.content,
|
|
30262
|
+
isError: merge2.isError,
|
|
30263
|
+
...merge2.uuid ? { uuid: merge2.uuid } : {}
|
|
30264
|
+
},
|
|
30265
|
+
status: "complete",
|
|
30266
|
+
uuid: previous?.uuid,
|
|
30267
|
+
runId: merge2.runId ?? previous?.runId,
|
|
30268
|
+
seqId: maxDefined(merge2.seqId, previous?.seqId)
|
|
30269
|
+
});
|
|
30270
|
+
return key;
|
|
30271
|
+
}
|
|
30272
|
+
applyStreamEvent(message) {
|
|
30273
|
+
const delta = extractStreamTextDelta(message.event);
|
|
30274
|
+
if (!delta)
|
|
30275
|
+
return;
|
|
30276
|
+
const payload = asRecord(message.event);
|
|
30277
|
+
const otid = payload ? readString(payload, "otid") : undefined;
|
|
30278
|
+
const payloadId = payload ? readString(payload, "id") : undefined;
|
|
30279
|
+
const identified = otid !== undefined || payloadId !== undefined;
|
|
30280
|
+
this.applyText({
|
|
30281
|
+
kind: delta.kind,
|
|
30282
|
+
text: delta.text,
|
|
30283
|
+
uuid: identified ? payloadId : `${delta.kind}${SEP}live`,
|
|
30284
|
+
otid,
|
|
30285
|
+
runId: payload ? readString(payload, "run_id") : undefined,
|
|
30286
|
+
seqId: payload ? readNumber(payload, "seq_id") : undefined
|
|
30287
|
+
});
|
|
30288
|
+
}
|
|
30289
|
+
applyHistoryMessage(message) {
|
|
30290
|
+
const record2 = asRecord(message);
|
|
30291
|
+
if (!record2)
|
|
30292
|
+
return;
|
|
30293
|
+
const messageType = readString(record2, "message_type");
|
|
30294
|
+
if (!messageType)
|
|
30295
|
+
return;
|
|
30296
|
+
const uuid2 = readString(record2, "id");
|
|
30297
|
+
const otid = readString(record2, "otid");
|
|
30298
|
+
const runId = readString(record2, "run_id");
|
|
30299
|
+
const seqId = readNumber(record2, "seq_id");
|
|
30300
|
+
if (seqId !== undefined) {
|
|
30301
|
+
this.rememberSeq(runId ?? ANONYMOUS_RUN, seqId);
|
|
30302
|
+
}
|
|
30303
|
+
switch (messageType) {
|
|
30304
|
+
case "user_message":
|
|
30305
|
+
case "assistant_message": {
|
|
30306
|
+
const text = extractTextFromContent(record2.content);
|
|
30307
|
+
if (text === null)
|
|
30308
|
+
return;
|
|
30309
|
+
return this.writeText({
|
|
30310
|
+
kind: messageType === "user_message" ? "user" : "assistant",
|
|
30311
|
+
text,
|
|
30312
|
+
uuid: uuid2,
|
|
30313
|
+
otid,
|
|
30314
|
+
runId,
|
|
30315
|
+
seqId
|
|
30316
|
+
}, "replace");
|
|
30317
|
+
}
|
|
30318
|
+
case "reasoning_message": {
|
|
30319
|
+
const text = typeof record2.reasoning === "string" ? record2.reasoning : extractTextFromContent(record2.content);
|
|
30320
|
+
if (text === null || text === undefined)
|
|
30321
|
+
return;
|
|
30322
|
+
return this.writeText({ kind: "reasoning", text, uuid: uuid2, otid, runId, seqId }, "replace");
|
|
30323
|
+
}
|
|
30324
|
+
case "tool_call_message":
|
|
30325
|
+
case "approval_request_message": {
|
|
30326
|
+
const toolCall = firstToolCall(record2);
|
|
30327
|
+
if (!toolCall)
|
|
30328
|
+
return;
|
|
30329
|
+
const fn = asRecord(toolCall.function);
|
|
30330
|
+
const toolCallId = (typeof toolCall.tool_call_id === "string" ? toolCall.tool_call_id : undefined) ?? (typeof toolCall.id === "string" ? toolCall.id : undefined);
|
|
30331
|
+
if (!toolCallId)
|
|
30332
|
+
return;
|
|
30333
|
+
const args = toolCall.arguments ?? fn?.arguments;
|
|
30334
|
+
return this.mergeToolCall({
|
|
30335
|
+
toolCallId,
|
|
30336
|
+
toolName: (typeof toolCall.name === "string" ? toolCall.name : undefined) ?? (typeof fn?.name === "string" ? fn.name : undefined),
|
|
30337
|
+
toolInput: asRecord(args),
|
|
30338
|
+
rawArguments: typeof args === "string" ? args : undefined,
|
|
30339
|
+
uuid: uuid2,
|
|
30340
|
+
runId,
|
|
30341
|
+
seqId,
|
|
30342
|
+
mode: "whole"
|
|
30343
|
+
});
|
|
30344
|
+
}
|
|
30345
|
+
case "tool_return_message": {
|
|
30346
|
+
const toolReturn = firstToolReturn(record2) ?? record2;
|
|
30347
|
+
const toolCallId = readString(record2, "tool_call_id") ?? (typeof toolReturn.tool_call_id === "string" ? toolReturn.tool_call_id : undefined);
|
|
30348
|
+
if (!toolCallId)
|
|
30349
|
+
return;
|
|
30350
|
+
const content = extractTextFromContent(record2.tool_return ?? toolReturn.tool_return ?? toolReturn.content) ?? "";
|
|
30351
|
+
const status = readString(record2, "status") ?? (typeof toolReturn.status === "string" ? toolReturn.status : undefined);
|
|
30352
|
+
return this.mergeToolResult({
|
|
30353
|
+
toolCallId,
|
|
30354
|
+
content,
|
|
30355
|
+
isError: status === "error",
|
|
30356
|
+
uuid: uuid2,
|
|
30357
|
+
runId,
|
|
30358
|
+
seqId
|
|
30359
|
+
});
|
|
30360
|
+
}
|
|
30361
|
+
default:
|
|
30362
|
+
return;
|
|
30363
|
+
}
|
|
30364
|
+
}
|
|
30365
|
+
setRow(key, row) {
|
|
30366
|
+
this.byKey.set(key, row);
|
|
30367
|
+
this.snapshot = null;
|
|
30368
|
+
}
|
|
30369
|
+
reorder(historyKeys) {
|
|
30370
|
+
if (historyKeys.length === 0)
|
|
30371
|
+
return;
|
|
30372
|
+
const historySet = new Set(historyKeys);
|
|
30373
|
+
const reordered = new Map;
|
|
30374
|
+
for (const key of historyKeys) {
|
|
30375
|
+
const row = this.byKey.get(key);
|
|
30376
|
+
if (row)
|
|
30377
|
+
reordered.set(key, row);
|
|
30378
|
+
}
|
|
30379
|
+
for (const [key, row] of this.byKey) {
|
|
30380
|
+
if (historySet.has(key))
|
|
30381
|
+
continue;
|
|
30382
|
+
reordered.set(key, row);
|
|
30383
|
+
}
|
|
30384
|
+
this.byKey = reordered;
|
|
30385
|
+
this.snapshot = null;
|
|
30386
|
+
}
|
|
30387
|
+
}
|
|
30388
|
+
function maxDefined(next, previous) {
|
|
30389
|
+
if (next === undefined)
|
|
30390
|
+
return previous;
|
|
30391
|
+
if (previous === undefined)
|
|
30392
|
+
return next;
|
|
30393
|
+
return Math.max(next, previous);
|
|
30394
|
+
}
|
|
30395
|
+
function normalizeHistoryPage(page, order) {
|
|
30396
|
+
const messages = Array.isArray(page) ? page : page.messages ?? [];
|
|
30397
|
+
if (messages.length < 2)
|
|
30398
|
+
return messages;
|
|
30399
|
+
const descending = order ? order === "desc" : detectDescending(messages);
|
|
30400
|
+
return descending ? [...messages].reverse() : messages;
|
|
30401
|
+
}
|
|
30402
|
+
function detectDescending(messages) {
|
|
30403
|
+
const seqIds = [];
|
|
30404
|
+
const dates = [];
|
|
30405
|
+
for (const message of messages) {
|
|
30406
|
+
const record2 = asRecord(message);
|
|
30407
|
+
if (!record2)
|
|
30408
|
+
continue;
|
|
30409
|
+
const seqId = readNumber(record2, "seq_id");
|
|
30410
|
+
if (seqId !== undefined)
|
|
30411
|
+
seqIds.push(seqId);
|
|
30412
|
+
const date4 = readString(record2, "date");
|
|
30413
|
+
if (!date4)
|
|
30414
|
+
continue;
|
|
30415
|
+
const parsed = Date.parse(date4);
|
|
30416
|
+
if (!Number.isNaN(parsed))
|
|
30417
|
+
dates.push(parsed);
|
|
30418
|
+
}
|
|
30419
|
+
const ordered = seqIds.length >= 2 ? seqIds : dates;
|
|
30420
|
+
if (ordered.length < 2)
|
|
30421
|
+
return false;
|
|
30422
|
+
const first = ordered[0];
|
|
30423
|
+
const last = ordered[ordered.length - 1];
|
|
30424
|
+
return last < first;
|
|
30425
|
+
}
|
|
30426
|
+
function createTranscriptAccumulator() {
|
|
30427
|
+
return new TranscriptAccumulatorImpl;
|
|
30428
|
+
}
|
|
29752
30429
|
// src/tool-helpers.ts
|
|
29753
30430
|
function jsonResult(payload) {
|
|
29754
30431
|
return {
|
|
@@ -29921,6 +30598,7 @@ export {
|
|
|
29921
30598
|
imageFromFile,
|
|
29922
30599
|
imageFromBase64,
|
|
29923
30600
|
extractStreamTextDelta,
|
|
30601
|
+
createTranscriptAccumulator,
|
|
29924
30602
|
createSession,
|
|
29925
30603
|
createReactNativeWebSocketConstructor,
|
|
29926
30604
|
createAgent,
|
|
@@ -29929,4 +30607,4 @@ export {
|
|
|
29929
30607
|
CloudManagedSandboxExpiredError
|
|
29930
30608
|
};
|
|
29931
30609
|
|
|
29932
|
-
//# debugId=
|
|
30610
|
+
//# debugId=45035C35D5C51AFA64756E2164756E21
|