@letta-ai/letta-agent-sdk 0.3.2 → 0.3.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.
- package/AGENTS.md +47 -0
- package/README.md +17 -0
- package/dist/client-entry.js +800 -732
- package/dist/client-entry.js.map +8 -5
- package/dist/cloud-sandbox.d.ts +33 -0
- package/dist/cloud-sandbox.d.ts.map +1 -0
- package/dist/cloud-session.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +799 -732
- package/dist/index.js.map +9 -6
- package/dist/remote-client-session-core.d.ts +6 -93
- package/dist/remote-client-session-core.d.ts.map +1 -1
- package/dist/remote-session-protocol.d.ts +130 -0
- package/dist/remote-session-protocol.d.ts.map +1 -0
- package/dist/remote-turn-coordinator.d.ts +49 -0
- package/dist/remote-turn-coordinator.d.ts.map +1 -0
- package/dist/types.d.ts +2 -20
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -2
- package/src/app-server-management.ts +641 -0
- package/src/app-server-session.ts +948 -0
- package/src/cli-resolver.ts +46 -0
- package/src/client-base.ts +482 -0
- package/src/client-entry.ts +31 -0
- package/src/client.ts +138 -0
- package/src/cloud-management.ts +360 -0
- package/src/cloud-sandbox.ts +117 -0
- package/src/cloud-session.ts +1313 -0
- package/src/index.ts +440 -0
- package/src/interactiveToolPolicy.ts +62 -0
- package/src/local-app-server-session.ts +39 -0
- package/src/local-app-server.ts +137 -0
- package/src/management-types.ts +133 -0
- package/src/management.ts +206 -0
- package/src/protocol.ts +249 -0
- package/src/remote-client-session-core.ts +786 -0
- package/src/remote-session-protocol.ts +660 -0
- package/src/remote-turn-coordinator.ts +505 -0
- package/src/remote.ts +177 -0
- package/src/repositories.ts +340 -0
- package/src/request-ids.ts +33 -0
- package/src/session.ts +1638 -0
- package/src/stream-events.ts +88 -0
- package/src/tool-helpers.ts +147 -0
- package/src/transport.ts +484 -0
- package/src/types.ts +1328 -0
- package/src/validation.ts +223 -0
- package/src/websocket.ts +22 -0
package/dist/client-entry.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// Generated bundle. Canonical modules are published under src/ and maintained in the letta-agent-sdk repository.
|
|
2
|
+
|
|
1
3
|
// src/repositories.ts
|
|
2
4
|
var DEFAULT_CLOUD_API_BASE_URL = "https://api.letta.com";
|
|
3
5
|
function getDefaultApiKey() {
|
|
@@ -5711,7 +5713,7 @@ function buildCanUseToolContext(request, requestId) {
|
|
|
5711
5713
|
return context;
|
|
5712
5714
|
}
|
|
5713
5715
|
|
|
5714
|
-
// src/remote-
|
|
5716
|
+
// src/remote-session-protocol.ts
|
|
5715
5717
|
var FAILURE_STOP_REASONS = new Set([
|
|
5716
5718
|
"error",
|
|
5717
5719
|
"llm_api_error",
|
|
@@ -6106,218 +6108,606 @@ function normalizeSendMessage(message) {
|
|
|
6106
6108
|
return message;
|
|
6107
6109
|
}
|
|
6108
6110
|
|
|
6109
|
-
|
|
6110
|
-
|
|
6111
|
-
controller = null;
|
|
6112
|
-
runtime = null;
|
|
6113
|
-
initialized = false;
|
|
6114
|
-
closed = false;
|
|
6115
|
-
activeTurnStartedAt = 0;
|
|
6116
|
-
_agentId = null;
|
|
6117
|
-
_sessionId = null;
|
|
6118
|
-
_conversationId = null;
|
|
6119
|
-
_model = "";
|
|
6120
|
-
_modelSettings = null;
|
|
6111
|
+
// src/remote-turn-coordinator.ts
|
|
6112
|
+
class RemoteTurnCoordinator {
|
|
6121
6113
|
label;
|
|
6122
6114
|
requestTimeoutMs;
|
|
6123
|
-
|
|
6115
|
+
onDeviceStatus;
|
|
6124
6116
|
streamQueue = [];
|
|
6125
6117
|
streamResolvers = [];
|
|
6126
|
-
removeMessageHandler = null;
|
|
6127
6118
|
activeTurn = null;
|
|
6128
6119
|
pendingTurns = [];
|
|
6129
6120
|
nextTurnId = 0;
|
|
6130
6121
|
messageCounter = 0;
|
|
6131
6122
|
clientMessageCounter = 0;
|
|
6132
|
-
|
|
6133
|
-
|
|
6134
|
-
|
|
6135
|
-
constructor(mode, config) {
|
|
6136
|
-
this.mode = mode;
|
|
6123
|
+
closed = false;
|
|
6124
|
+
_activeTurnStartedAt = 0;
|
|
6125
|
+
constructor(config) {
|
|
6137
6126
|
this.label = config.label;
|
|
6138
6127
|
this.requestTimeoutMs = config.requestTimeoutMs;
|
|
6128
|
+
this.onDeviceStatus = config.onDeviceStatus;
|
|
6139
6129
|
}
|
|
6140
|
-
|
|
6141
|
-
|
|
6142
|
-
throw new Error("Session is closed");
|
|
6143
|
-
}
|
|
6144
|
-
if (this.initializePromise) {
|
|
6145
|
-
return this.initializePromise;
|
|
6146
|
-
}
|
|
6147
|
-
if (this.initialized) {
|
|
6148
|
-
throw new Error("Session already initialized");
|
|
6149
|
-
}
|
|
6150
|
-
const attempt = this.performInitialize();
|
|
6151
|
-
const memo = attempt.catch((error) => {
|
|
6152
|
-
this.cleanupFailedInitialize();
|
|
6153
|
-
throw error;
|
|
6154
|
-
}).finally(() => {
|
|
6155
|
-
if (this.initializePromise === memo) {
|
|
6156
|
-
this.initializePromise = null;
|
|
6157
|
-
}
|
|
6158
|
-
});
|
|
6159
|
-
this.initializePromise = memo;
|
|
6160
|
-
return memo;
|
|
6161
|
-
}
|
|
6162
|
-
async performInitialize() {
|
|
6163
|
-
const init = await this.initializeRuntimeController();
|
|
6164
|
-
this.controller = init.controller;
|
|
6165
|
-
this.runtime = init.runtime;
|
|
6166
|
-
this._agentId = init.runtime.agent_id;
|
|
6167
|
-
this._conversationId = init.runtime.conversation_id;
|
|
6168
|
-
this._sessionId = `${init.runtime.agent_id}:${init.runtime.conversation_id}`;
|
|
6169
|
-
this._modelSettings = init.modelSettings ?? null;
|
|
6170
|
-
this._model = typeof init.model === "string" ? init.model : typeof this._modelSettings?.model === "string" ? this._modelSettings.model : "";
|
|
6171
|
-
this.toolNames = init.tools;
|
|
6172
|
-
this.removeMessageHandler = this.controller.onMessage(this.handleProtocolMessage);
|
|
6173
|
-
await this.afterRuntimeInitialized();
|
|
6174
|
-
await this.applyPostInitializeOptions();
|
|
6175
|
-
if (this.closed) {
|
|
6176
|
-
throw new Error("Session is closed");
|
|
6177
|
-
}
|
|
6178
|
-
this.initialized = true;
|
|
6179
|
-
const initMessage = {
|
|
6180
|
-
type: "init",
|
|
6181
|
-
agentId: init.runtime.agent_id,
|
|
6182
|
-
sessionId: this._sessionId,
|
|
6183
|
-
conversationId: init.runtime.conversation_id,
|
|
6184
|
-
model: this._model
|
|
6185
|
-
};
|
|
6186
|
-
if (this.toolNames !== undefined)
|
|
6187
|
-
initMessage.tools = this.toolNames;
|
|
6188
|
-
if (init.skillSources !== undefined) {
|
|
6189
|
-
initMessage.skillSources = init.skillSources;
|
|
6190
|
-
}
|
|
6191
|
-
return initMessage;
|
|
6192
|
-
}
|
|
6193
|
-
cleanupFailedInitialize() {
|
|
6194
|
-
this.removeMessageHandler?.();
|
|
6195
|
-
this.removeMessageHandler = null;
|
|
6196
|
-
this.controller?.close();
|
|
6197
|
-
this.controller = null;
|
|
6198
|
-
this.onCoreClose();
|
|
6199
|
-
this.runtime = null;
|
|
6200
|
-
this._agentId = null;
|
|
6201
|
-
this._conversationId = null;
|
|
6202
|
-
this._sessionId = null;
|
|
6203
|
-
this._modelSettings = null;
|
|
6204
|
-
this._model = "";
|
|
6205
|
-
this.toolNames = undefined;
|
|
6206
|
-
this.initialized = false;
|
|
6130
|
+
get activeTurnStartedAt() {
|
|
6131
|
+
return this._activeTurnStartedAt;
|
|
6207
6132
|
}
|
|
6208
|
-
|
|
6209
|
-
|
|
6210
|
-
await this.initialize();
|
|
6211
|
-
}
|
|
6212
|
-
if (!this.controller || !this.runtime) {
|
|
6213
|
-
throw new Error("Session is not initialized");
|
|
6214
|
-
}
|
|
6215
|
-
await this.beforeTurn();
|
|
6216
|
-
const turn = this.trackSentTurn(this.runtime);
|
|
6217
|
-
try {
|
|
6218
|
-
this.controller.sendTurnMessage(this.runtime, message, {
|
|
6219
|
-
clientMessageId: turn.clientMessageId
|
|
6220
|
-
});
|
|
6221
|
-
} catch (error) {
|
|
6222
|
-
this.removeTrackedTurn(turn);
|
|
6223
|
-
throw error;
|
|
6224
|
-
}
|
|
6133
|
+
hasInFlightTurn() {
|
|
6134
|
+
return this.activeTurn !== null || this.pendingTurns.length > 0;
|
|
6225
6135
|
}
|
|
6226
|
-
|
|
6227
|
-
|
|
6228
|
-
|
|
6229
|
-
|
|
6230
|
-
|
|
6231
|
-
|
|
6232
|
-
|
|
6233
|
-
|
|
6234
|
-
|
|
6235
|
-
|
|
6236
|
-
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
error: "stream_closed",
|
|
6240
|
-
errorCode: "stream_closed",
|
|
6241
|
-
recoverable: false,
|
|
6242
|
-
errorDetail: "Stream ended before terminal result",
|
|
6243
|
-
durationMs: Date.now() - this.activeTurnStartedAt,
|
|
6244
|
-
conversationId: this._conversationId
|
|
6136
|
+
trackSentTurn(runtime) {
|
|
6137
|
+
const turn = {
|
|
6138
|
+
id: ++this.nextTurnId,
|
|
6139
|
+
runtime,
|
|
6140
|
+
clientMessageId: `sdk-message-${Date.now()}-${++this.clientMessageCounter}`,
|
|
6141
|
+
queuedAt: Date.now(),
|
|
6142
|
+
startedAt: 0,
|
|
6143
|
+
assistantText: "",
|
|
6144
|
+
runIds: new Set,
|
|
6145
|
+
observedTurnEvidence: false,
|
|
6146
|
+
observedRequiresApprovalStop: false,
|
|
6147
|
+
abortRequested: false,
|
|
6148
|
+
timeout: null
|
|
6245
6149
|
};
|
|
6246
|
-
|
|
6247
|
-
|
|
6248
|
-
|
|
6249
|
-
|
|
6250
|
-
if (!msg)
|
|
6251
|
-
break;
|
|
6252
|
-
yield msg;
|
|
6253
|
-
if (msg.type === "result")
|
|
6254
|
-
break;
|
|
6150
|
+
if (this.activeTurn) {
|
|
6151
|
+
this.pendingTurns.push(turn);
|
|
6152
|
+
} else {
|
|
6153
|
+
this.activateTurn(turn);
|
|
6255
6154
|
}
|
|
6155
|
+
return turn;
|
|
6256
6156
|
}
|
|
6257
|
-
|
|
6258
|
-
if (
|
|
6259
|
-
|
|
6260
|
-
if (
|
|
6157
|
+
removeTrackedTurn(turn) {
|
|
6158
|
+
if (turn.timeout)
|
|
6159
|
+
clearTimeout(turn.timeout);
|
|
6160
|
+
if (this.activeTurn === turn) {
|
|
6161
|
+
this.activeTurn = null;
|
|
6261
6162
|
return;
|
|
6163
|
+
}
|
|
6164
|
+
const index = this.pendingTurns.indexOf(turn);
|
|
6165
|
+
if (index !== -1)
|
|
6166
|
+
this.pendingTurns.splice(index, 1);
|
|
6167
|
+
}
|
|
6168
|
+
markAbortRequested() {
|
|
6262
6169
|
if (this.activeTurn)
|
|
6263
6170
|
this.activeTurn.abortRequested = true;
|
|
6264
|
-
await this.controller.abort(this.runtime);
|
|
6265
6171
|
}
|
|
6266
|
-
|
|
6267
|
-
if (!
|
|
6268
|
-
|
|
6269
|
-
|
|
6270
|
-
if (
|
|
6271
|
-
|
|
6172
|
+
handleProtocolMessage(message, runtime) {
|
|
6173
|
+
if (!sameRuntime2(message, runtime))
|
|
6174
|
+
return;
|
|
6175
|
+
const statusRecord = deviceStatusRecord(message);
|
|
6176
|
+
if (statusRecord) {
|
|
6177
|
+
const status = toSessionDeviceStatus(statusRecord);
|
|
6178
|
+
if (status)
|
|
6179
|
+
this.onDeviceStatus(status);
|
|
6180
|
+
return;
|
|
6272
6181
|
}
|
|
6273
|
-
if (
|
|
6274
|
-
|
|
6182
|
+
if (message.type === "update_queue") {
|
|
6183
|
+
const sdkMessage2 = {
|
|
6184
|
+
type: "queue_update",
|
|
6185
|
+
queue: queueItems(message)
|
|
6186
|
+
};
|
|
6187
|
+
this.enqueue(sdkMessage2);
|
|
6188
|
+
return;
|
|
6275
6189
|
}
|
|
6276
|
-
if (
|
|
6277
|
-
|
|
6190
|
+
if (message.type === "update_loop_status") {
|
|
6191
|
+
this.handleLoopStatusMessage(message);
|
|
6192
|
+
return;
|
|
6278
6193
|
}
|
|
6279
|
-
|
|
6280
|
-
|
|
6194
|
+
const delta = streamDeltaRecord(message);
|
|
6195
|
+
if (!delta)
|
|
6281
6196
|
return;
|
|
6197
|
+
const active = this.activateNextTurnFromProtocol();
|
|
6198
|
+
if (active) {
|
|
6199
|
+
active.observedTurnEvidence = true;
|
|
6200
|
+
const runId = streamDeltaRunId2(delta);
|
|
6201
|
+
if (runId)
|
|
6202
|
+
active.runIds.add(runId);
|
|
6282
6203
|
}
|
|
6283
|
-
const
|
|
6284
|
-
|
|
6285
|
-
|
|
6286
|
-
|
|
6204
|
+
const sdkMessage = this.transformStreamDelta(delta);
|
|
6205
|
+
if (sdkMessage)
|
|
6206
|
+
this.enqueue(sdkMessage);
|
|
6207
|
+
this.handleTurnTerminalDelta(delta, sdkMessage);
|
|
6208
|
+
}
|
|
6209
|
+
nextMessage() {
|
|
6210
|
+
const next = this.streamQueue.shift();
|
|
6211
|
+
if (next)
|
|
6212
|
+
return Promise.resolve(next);
|
|
6213
|
+
if (this.closed)
|
|
6214
|
+
return Promise.resolve(null);
|
|
6215
|
+
return new Promise((resolve) => {
|
|
6216
|
+
this.streamResolvers.push(resolve);
|
|
6287
6217
|
});
|
|
6288
|
-
return response;
|
|
6289
6218
|
}
|
|
6290
|
-
|
|
6291
|
-
if (
|
|
6292
|
-
|
|
6293
|
-
|
|
6294
|
-
if (
|
|
6295
|
-
|
|
6219
|
+
close() {
|
|
6220
|
+
if (this.closed)
|
|
6221
|
+
return;
|
|
6222
|
+
this.closed = true;
|
|
6223
|
+
if (this.activeTurn?.timeout)
|
|
6224
|
+
clearTimeout(this.activeTurn.timeout);
|
|
6225
|
+
for (const turn of this.pendingTurns) {
|
|
6226
|
+
if (turn.timeout)
|
|
6227
|
+
clearTimeout(turn.timeout);
|
|
6296
6228
|
}
|
|
6297
|
-
|
|
6229
|
+
this.activeTurn = null;
|
|
6230
|
+
this.pendingTurns.length = 0;
|
|
6231
|
+
this.resolveAll(null);
|
|
6298
6232
|
}
|
|
6299
|
-
|
|
6300
|
-
|
|
6301
|
-
|
|
6302
|
-
|
|
6303
|
-
if (
|
|
6304
|
-
|
|
6233
|
+
activateTurn(turn) {
|
|
6234
|
+
this.activeTurn = turn;
|
|
6235
|
+
turn.startedAt = Date.now();
|
|
6236
|
+
this._activeTurnStartedAt = turn.startedAt;
|
|
6237
|
+
if (this.requestTimeoutMs !== undefined) {
|
|
6238
|
+
turn.timeout = setTimeout(() => {
|
|
6239
|
+
this.failTurn(turn, `Timed out waiting for ${this.label} turn`);
|
|
6240
|
+
}, this.requestTimeoutMs);
|
|
6241
|
+
turn.timeout.unref?.();
|
|
6305
6242
|
}
|
|
6306
|
-
return this.applyModelUpdate(update);
|
|
6307
6243
|
}
|
|
6308
|
-
|
|
6309
|
-
if (
|
|
6310
|
-
|
|
6311
|
-
|
|
6312
|
-
|
|
6313
|
-
|
|
6314
|
-
|
|
6315
|
-
|
|
6316
|
-
|
|
6317
|
-
|
|
6318
|
-
|
|
6319
|
-
|
|
6320
|
-
|
|
6244
|
+
activateNextTurnFromProtocol() {
|
|
6245
|
+
if (this.activeTurn)
|
|
6246
|
+
return this.activeTurn;
|
|
6247
|
+
const next = this.pendingTurns.shift();
|
|
6248
|
+
if (!next)
|
|
6249
|
+
return null;
|
|
6250
|
+
this.activateTurn(next);
|
|
6251
|
+
return next;
|
|
6252
|
+
}
|
|
6253
|
+
failTurn(turn, detail) {
|
|
6254
|
+
if (this.activeTurn !== turn)
|
|
6255
|
+
return;
|
|
6256
|
+
this.enqueue({
|
|
6257
|
+
type: "error",
|
|
6258
|
+
message: detail,
|
|
6259
|
+
errorCode: "error",
|
|
6260
|
+
stopReason: "error",
|
|
6261
|
+
errorDetail: detail,
|
|
6262
|
+
recoverable: false
|
|
6263
|
+
});
|
|
6264
|
+
this.completeActiveTurn({
|
|
6265
|
+
runtime: turn.runtime,
|
|
6266
|
+
stopReason: "error",
|
|
6267
|
+
runIds: [...turn.runIds],
|
|
6268
|
+
success: false,
|
|
6269
|
+
detail,
|
|
6270
|
+
errorCode: "error"
|
|
6271
|
+
});
|
|
6272
|
+
}
|
|
6273
|
+
completeActiveTurn(turn) {
|
|
6274
|
+
const active = this.activeTurn;
|
|
6275
|
+
if (!active)
|
|
6276
|
+
return;
|
|
6277
|
+
if (active.timeout) {
|
|
6278
|
+
clearTimeout(active.timeout);
|
|
6279
|
+
active.timeout = null;
|
|
6280
|
+
}
|
|
6281
|
+
this.enqueue(this.resultFromTurn(turn, active));
|
|
6282
|
+
this.activeTurn = null;
|
|
6283
|
+
}
|
|
6284
|
+
handleLoopStatusMessage(message) {
|
|
6285
|
+
const status = loopStatusValue(message);
|
|
6286
|
+
if (!status)
|
|
6287
|
+
return;
|
|
6288
|
+
const activeRunIds = loopStatusRunIds(message);
|
|
6289
|
+
const sdkMessage = {
|
|
6290
|
+
type: "loop_status",
|
|
6291
|
+
status,
|
|
6292
|
+
activeRunIds
|
|
6293
|
+
};
|
|
6294
|
+
this.enqueue(sdkMessage);
|
|
6295
|
+
const active = this.activeTurn;
|
|
6296
|
+
if (!active)
|
|
6297
|
+
return;
|
|
6298
|
+
for (const runId of activeRunIds)
|
|
6299
|
+
active.runIds.add(runId);
|
|
6300
|
+
const hadTurnEvidence = active.observedTurnEvidence || active.observedRequiresApprovalStop;
|
|
6301
|
+
if (!hadTurnEvidence)
|
|
6302
|
+
return;
|
|
6303
|
+
if (status === "WAITING_ON_APPROVAL") {
|
|
6304
|
+
this.completeActiveTurn({
|
|
6305
|
+
runtime: active.runtime,
|
|
6306
|
+
stopReason: "requires_approval",
|
|
6307
|
+
runIds: [...active.runIds]
|
|
6308
|
+
});
|
|
6309
|
+
return;
|
|
6310
|
+
}
|
|
6311
|
+
if (status === "WAITING_ON_INPUT" && active.abortRequested) {
|
|
6312
|
+
this.completeActiveTurn({
|
|
6313
|
+
runtime: active.runtime,
|
|
6314
|
+
stopReason: "interrupted",
|
|
6315
|
+
runIds: [...active.runIds],
|
|
6316
|
+
success: false,
|
|
6317
|
+
detail: "Interrupted",
|
|
6318
|
+
errorCode: "interrupted"
|
|
6319
|
+
});
|
|
6320
|
+
return;
|
|
6321
|
+
}
|
|
6322
|
+
if (status === "WAITING_ON_INPUT" && active.observedTurnEvidence) {
|
|
6323
|
+
this.completeActiveTurn({
|
|
6324
|
+
runtime: active.runtime,
|
|
6325
|
+
stopReason: null,
|
|
6326
|
+
runIds: [...active.runIds]
|
|
6327
|
+
});
|
|
6328
|
+
}
|
|
6329
|
+
}
|
|
6330
|
+
handleTurnTerminalDelta(delta, sdkMessage) {
|
|
6331
|
+
const active = this.activeTurn;
|
|
6332
|
+
if (!active)
|
|
6333
|
+
return;
|
|
6334
|
+
const messageType = streamDeltaMessageType2(delta);
|
|
6335
|
+
if (messageType === "stop_reason") {
|
|
6336
|
+
const stopReason = streamDeltaStopReason2(delta) ?? null;
|
|
6337
|
+
if (stopReason === "requires_approval") {
|
|
6338
|
+
active.observedRequiresApprovalStop = true;
|
|
6339
|
+
return;
|
|
6340
|
+
}
|
|
6341
|
+
this.completeActiveTurn({
|
|
6342
|
+
runtime: active.runtime,
|
|
6343
|
+
stopReason,
|
|
6344
|
+
runIds: [...active.runIds]
|
|
6345
|
+
});
|
|
6346
|
+
return;
|
|
6347
|
+
}
|
|
6348
|
+
if (sdkMessage?.type === "error") {
|
|
6349
|
+
this.completeActiveTurn({
|
|
6350
|
+
runtime: active.runtime,
|
|
6351
|
+
stopReason: sdkMessage.stopReason,
|
|
6352
|
+
runIds: [...active.runIds],
|
|
6353
|
+
success: false,
|
|
6354
|
+
detail: sdkMessage.errorDetail ?? sdkMessage.message,
|
|
6355
|
+
errorCode: sdkMessage.errorCode
|
|
6356
|
+
});
|
|
6357
|
+
}
|
|
6358
|
+
}
|
|
6359
|
+
transformStreamDelta(delta) {
|
|
6360
|
+
const messageType = typeof delta.message_type === "string" ? delta.message_type : undefined;
|
|
6361
|
+
const runId = typeof delta.run_id === "string" ? delta.run_id : undefined;
|
|
6362
|
+
const uuid = typeof delta.id === "string" ? delta.id : `${this.label}-${++this.messageCounter}`;
|
|
6363
|
+
if (messageType === "assistant_message") {
|
|
6364
|
+
const content = extractTextFromContent(delta.content);
|
|
6365
|
+
if (!content)
|
|
6366
|
+
return null;
|
|
6367
|
+
if (this.activeTurn)
|
|
6368
|
+
this.activeTurn.assistantText += content;
|
|
6369
|
+
return { type: "assistant", content, uuid, runId };
|
|
6370
|
+
}
|
|
6371
|
+
if (messageType === "reasoning_message") {
|
|
6372
|
+
const content = typeof delta.reasoning === "string" ? delta.reasoning : extractTextFromContent(delta.content);
|
|
6373
|
+
if (!content)
|
|
6374
|
+
return null;
|
|
6375
|
+
return { type: "reasoning", content, uuid, runId };
|
|
6376
|
+
}
|
|
6377
|
+
if (messageType === "tool_call_message" || messageType === "approval_request_message") {
|
|
6378
|
+
const toolCall = firstToolCall(delta);
|
|
6379
|
+
if (!toolCall)
|
|
6380
|
+
return null;
|
|
6381
|
+
const fn = toolCall.function && typeof toolCall.function === "object" ? toolCall.function : undefined;
|
|
6382
|
+
const toolCallId = (typeof toolCall.tool_call_id === "string" ? toolCall.tool_call_id : undefined) ?? (typeof toolCall.id === "string" ? toolCall.id : undefined);
|
|
6383
|
+
if (!toolCallId) {
|
|
6384
|
+
const detail = `Missing tool_call_id in ${messageType} (uuid=${uuid})`;
|
|
6385
|
+
return {
|
|
6386
|
+
type: "error",
|
|
6387
|
+
message: detail,
|
|
6388
|
+
errorCode: "protocol_error",
|
|
6389
|
+
stopReason: "protocol_error",
|
|
6390
|
+
runId,
|
|
6391
|
+
recoverable: false,
|
|
6392
|
+
errorDetail: detail
|
|
6393
|
+
};
|
|
6394
|
+
}
|
|
6395
|
+
const toolName = (typeof toolCall.name === "string" ? toolCall.name : undefined) ?? (typeof fn?.name === "string" ? fn.name : undefined) ?? "?";
|
|
6396
|
+
const { input, raw } = toolInputFromArguments(toolCall.arguments ?? fn?.arguments);
|
|
6397
|
+
return {
|
|
6398
|
+
type: "tool_call",
|
|
6399
|
+
toolCallId,
|
|
6400
|
+
toolName,
|
|
6401
|
+
toolInput: input,
|
|
6402
|
+
rawArguments: raw,
|
|
6403
|
+
uuid,
|
|
6404
|
+
runId
|
|
6405
|
+
};
|
|
6406
|
+
}
|
|
6407
|
+
if (messageType === "tool_return_message") {
|
|
6408
|
+
const toolReturn = firstToolReturn(delta) ?? delta;
|
|
6409
|
+
const toolCallId = (typeof delta.tool_call_id === "string" ? delta.tool_call_id : undefined) ?? (typeof toolReturn.tool_call_id === "string" ? toolReturn.tool_call_id : undefined);
|
|
6410
|
+
if (!toolCallId)
|
|
6411
|
+
return null;
|
|
6412
|
+
const content = extractTextFromContent(delta.tool_return ?? toolReturn.tool_return ?? toolReturn.content) ?? "";
|
|
6413
|
+
const status = typeof delta.status === "string" ? delta.status : toolReturn.status;
|
|
6414
|
+
return {
|
|
6415
|
+
type: "tool_result",
|
|
6416
|
+
toolCallId,
|
|
6417
|
+
content,
|
|
6418
|
+
isError: status === "error",
|
|
6419
|
+
uuid,
|
|
6420
|
+
runId
|
|
6421
|
+
};
|
|
6422
|
+
}
|
|
6423
|
+
if (messageType === "error_message" || messageType === "loop_error") {
|
|
6424
|
+
const detail = (typeof delta.detail === "string" ? delta.detail : undefined) ?? (typeof delta.message === "string" ? delta.message : undefined) ?? `${this.label} turn failed`;
|
|
6425
|
+
const stopReason = (typeof delta.stop_reason === "string" ? delta.stop_reason : undefined) ?? (typeof delta.error_type === "string" ? delta.error_type : undefined) ?? "error";
|
|
6426
|
+
const approvalConflict = isApprovalConflictSignal({
|
|
6427
|
+
detail,
|
|
6428
|
+
message: typeof delta.message === "string" ? delta.message : undefined,
|
|
6429
|
+
stopReason
|
|
6430
|
+
});
|
|
6431
|
+
return {
|
|
6432
|
+
type: "error",
|
|
6433
|
+
message: detail,
|
|
6434
|
+
errorCode: approvalConflict ? "approval_conflict" : toSdkErrorCode(stopReason),
|
|
6435
|
+
approvalConflict: approvalConflict || undefined,
|
|
6436
|
+
recoverable: approvalConflict ? true : false,
|
|
6437
|
+
errorDetail: detail,
|
|
6438
|
+
stopReason,
|
|
6439
|
+
runId
|
|
6440
|
+
};
|
|
6441
|
+
}
|
|
6442
|
+
if (messageType === "retry") {
|
|
6443
|
+
return {
|
|
6444
|
+
type: "retry",
|
|
6445
|
+
reason: typeof delta.reason === "string" ? delta.reason : "error",
|
|
6446
|
+
attempt: typeof delta.attempt === "number" ? delta.attempt : 0,
|
|
6447
|
+
maxAttempts: typeof delta.max_attempts === "number" ? delta.max_attempts : 0,
|
|
6448
|
+
delayMs: typeof delta.delay_ms === "number" ? delta.delay_ms : 0,
|
|
6449
|
+
runId
|
|
6450
|
+
};
|
|
6451
|
+
}
|
|
6452
|
+
if (messageType === "stop_reason" || messageType === "ping") {
|
|
6453
|
+
return null;
|
|
6454
|
+
}
|
|
6455
|
+
return {
|
|
6456
|
+
type: "stream_event",
|
|
6457
|
+
event: delta,
|
|
6458
|
+
uuid
|
|
6459
|
+
};
|
|
6460
|
+
}
|
|
6461
|
+
resultFromTurn(turn, tracker) {
|
|
6462
|
+
const stopReason = turn.stopReason ?? (turn.success === false ? "error" : undefined);
|
|
6463
|
+
const approvalConflict = isApprovalConflictSignal({
|
|
6464
|
+
detail: turn.detail,
|
|
6465
|
+
stopReason
|
|
6466
|
+
});
|
|
6467
|
+
const success = turn.success !== undefined ? turn.success && !approvalConflict && !FAILURE_STOP_REASONS.has(stopReason ?? "") : !approvalConflict && !FAILURE_STOP_REASONS.has(stopReason ?? "");
|
|
6468
|
+
const errorCode = approvalConflict ? "approval_conflict" : turn.errorCode ?? toSdkErrorCode(stopReason);
|
|
6469
|
+
return {
|
|
6470
|
+
type: "result",
|
|
6471
|
+
success,
|
|
6472
|
+
result: success ? tracker?.assistantText || undefined : undefined,
|
|
6473
|
+
error: success ? undefined : errorCode ?? stopReason ?? "error",
|
|
6474
|
+
errorCode: success ? undefined : errorCode ?? "error",
|
|
6475
|
+
approvalConflict: approvalConflict || undefined,
|
|
6476
|
+
recoverable: approvalConflict ? true : success ? undefined : false,
|
|
6477
|
+
errorDetail: success ? undefined : turn.detail,
|
|
6478
|
+
stopReason,
|
|
6479
|
+
durationMs: Date.now() - (tracker?.startedAt || this._activeTurnStartedAt),
|
|
6480
|
+
conversationId: turn.runtime.conversation_id,
|
|
6481
|
+
runIds: turn.runIds.length > 0 ? turn.runIds : undefined
|
|
6482
|
+
};
|
|
6483
|
+
}
|
|
6484
|
+
enqueue(message) {
|
|
6485
|
+
const resolver = this.streamResolvers.shift();
|
|
6486
|
+
if (resolver) {
|
|
6487
|
+
resolver(message);
|
|
6488
|
+
return;
|
|
6489
|
+
}
|
|
6490
|
+
this.streamQueue.push(message);
|
|
6491
|
+
}
|
|
6492
|
+
resolveAll(value) {
|
|
6493
|
+
for (const resolve of this.streamResolvers.splice(0)) {
|
|
6494
|
+
resolve(value);
|
|
6495
|
+
}
|
|
6496
|
+
}
|
|
6497
|
+
}
|
|
6498
|
+
// src/remote-client-session-core.ts
|
|
6499
|
+
class RemoteClientSessionCore {
|
|
6500
|
+
mode;
|
|
6501
|
+
controller = null;
|
|
6502
|
+
runtime = null;
|
|
6503
|
+
initialized = false;
|
|
6504
|
+
closed = false;
|
|
6505
|
+
_agentId = null;
|
|
6506
|
+
_sessionId = null;
|
|
6507
|
+
_conversationId = null;
|
|
6508
|
+
_model = "";
|
|
6509
|
+
_modelSettings = null;
|
|
6510
|
+
label;
|
|
6511
|
+
requestTimeoutMs;
|
|
6512
|
+
initializePromise = null;
|
|
6513
|
+
removeMessageHandler = null;
|
|
6514
|
+
turns;
|
|
6515
|
+
toolNames;
|
|
6516
|
+
deviceStatusListeners = new Set;
|
|
6517
|
+
deviceStatusRefreshCancels = new Set;
|
|
6518
|
+
constructor(mode, config) {
|
|
6519
|
+
this.mode = mode;
|
|
6520
|
+
this.label = config.label;
|
|
6521
|
+
this.requestTimeoutMs = config.requestTimeoutMs;
|
|
6522
|
+
this.turns = new RemoteTurnCoordinator({
|
|
6523
|
+
label: config.label,
|
|
6524
|
+
requestTimeoutMs: config.requestTimeoutMs,
|
|
6525
|
+
onDeviceStatus: (status) => this.emitDeviceStatus(status)
|
|
6526
|
+
});
|
|
6527
|
+
}
|
|
6528
|
+
async initialize() {
|
|
6529
|
+
if (this.closed) {
|
|
6530
|
+
throw new Error("Session is closed");
|
|
6531
|
+
}
|
|
6532
|
+
if (this.initializePromise) {
|
|
6533
|
+
return this.initializePromise;
|
|
6534
|
+
}
|
|
6535
|
+
if (this.initialized) {
|
|
6536
|
+
throw new Error("Session already initialized");
|
|
6537
|
+
}
|
|
6538
|
+
const attempt = this.performInitialize();
|
|
6539
|
+
const memo = attempt.catch((error) => {
|
|
6540
|
+
this.cleanupFailedInitialize();
|
|
6541
|
+
throw error;
|
|
6542
|
+
}).finally(() => {
|
|
6543
|
+
if (this.initializePromise === memo) {
|
|
6544
|
+
this.initializePromise = null;
|
|
6545
|
+
}
|
|
6546
|
+
});
|
|
6547
|
+
this.initializePromise = memo;
|
|
6548
|
+
return memo;
|
|
6549
|
+
}
|
|
6550
|
+
async performInitialize() {
|
|
6551
|
+
const init = await this.initializeRuntimeController();
|
|
6552
|
+
this.controller = init.controller;
|
|
6553
|
+
this.runtime = init.runtime;
|
|
6554
|
+
this._agentId = init.runtime.agent_id;
|
|
6555
|
+
this._conversationId = init.runtime.conversation_id;
|
|
6556
|
+
this._sessionId = `${init.runtime.agent_id}:${init.runtime.conversation_id}`;
|
|
6557
|
+
this._modelSettings = init.modelSettings ?? null;
|
|
6558
|
+
this._model = typeof init.model === "string" ? init.model : typeof this._modelSettings?.model === "string" ? this._modelSettings.model : "";
|
|
6559
|
+
this.toolNames = init.tools;
|
|
6560
|
+
this.removeMessageHandler = this.controller.onMessage((message) => {
|
|
6561
|
+
if (this.runtime)
|
|
6562
|
+
this.turns.handleProtocolMessage(message, this.runtime);
|
|
6563
|
+
});
|
|
6564
|
+
await this.afterRuntimeInitialized();
|
|
6565
|
+
await this.applyPostInitializeOptions();
|
|
6566
|
+
if (this.closed) {
|
|
6567
|
+
throw new Error("Session is closed");
|
|
6568
|
+
}
|
|
6569
|
+
this.initialized = true;
|
|
6570
|
+
const initMessage = {
|
|
6571
|
+
type: "init",
|
|
6572
|
+
agentId: init.runtime.agent_id,
|
|
6573
|
+
sessionId: this._sessionId,
|
|
6574
|
+
conversationId: init.runtime.conversation_id,
|
|
6575
|
+
model: this._model
|
|
6576
|
+
};
|
|
6577
|
+
if (this.toolNames !== undefined)
|
|
6578
|
+
initMessage.tools = this.toolNames;
|
|
6579
|
+
if (init.skillSources !== undefined) {
|
|
6580
|
+
initMessage.skillSources = init.skillSources;
|
|
6581
|
+
}
|
|
6582
|
+
return initMessage;
|
|
6583
|
+
}
|
|
6584
|
+
cleanupFailedInitialize() {
|
|
6585
|
+
this.removeMessageHandler?.();
|
|
6586
|
+
this.removeMessageHandler = null;
|
|
6587
|
+
this.controller?.close();
|
|
6588
|
+
this.controller = null;
|
|
6589
|
+
this.onCoreClose();
|
|
6590
|
+
this.runtime = null;
|
|
6591
|
+
this._agentId = null;
|
|
6592
|
+
this._conversationId = null;
|
|
6593
|
+
this._sessionId = null;
|
|
6594
|
+
this._modelSettings = null;
|
|
6595
|
+
this._model = "";
|
|
6596
|
+
this.toolNames = undefined;
|
|
6597
|
+
this.initialized = false;
|
|
6598
|
+
}
|
|
6599
|
+
async send(message) {
|
|
6600
|
+
if (!this.initialized) {
|
|
6601
|
+
await this.initialize();
|
|
6602
|
+
}
|
|
6603
|
+
if (!this.controller || !this.runtime) {
|
|
6604
|
+
throw new Error("Session is not initialized");
|
|
6605
|
+
}
|
|
6606
|
+
await this.beforeTurn();
|
|
6607
|
+
const turn = this.turns.trackSentTurn(this.runtime);
|
|
6608
|
+
try {
|
|
6609
|
+
this.controller.sendTurnMessage(this.runtime, message, {
|
|
6610
|
+
clientMessageId: turn.clientMessageId
|
|
6611
|
+
});
|
|
6612
|
+
} catch (error) {
|
|
6613
|
+
this.turns.removeTrackedTurn(turn);
|
|
6614
|
+
throw error;
|
|
6615
|
+
}
|
|
6616
|
+
}
|
|
6617
|
+
async runTurn(message, _options = {}) {
|
|
6618
|
+
if (this.turns.hasInFlightTurn()) {
|
|
6619
|
+
throw new Error(`A turn is already in flight for this ${this.label} session. Use send() and stream() to let the listener queue messages.`);
|
|
6620
|
+
}
|
|
6621
|
+
await this.send(message);
|
|
6622
|
+
for await (const msg of this.stream()) {
|
|
6623
|
+
if (msg.type === "result") {
|
|
6624
|
+
return msg;
|
|
6625
|
+
}
|
|
6626
|
+
}
|
|
6627
|
+
return {
|
|
6628
|
+
type: "result",
|
|
6629
|
+
success: false,
|
|
6630
|
+
error: "stream_closed",
|
|
6631
|
+
errorCode: "stream_closed",
|
|
6632
|
+
recoverable: false,
|
|
6633
|
+
errorDetail: "Stream ended before terminal result",
|
|
6634
|
+
durationMs: Date.now() - this.turns.activeTurnStartedAt,
|
|
6635
|
+
conversationId: this._conversationId
|
|
6636
|
+
};
|
|
6637
|
+
}
|
|
6638
|
+
async* stream() {
|
|
6639
|
+
while (true) {
|
|
6640
|
+
const msg = await this.turns.nextMessage();
|
|
6641
|
+
if (!msg)
|
|
6642
|
+
break;
|
|
6643
|
+
yield msg;
|
|
6644
|
+
if (msg.type === "result")
|
|
6645
|
+
break;
|
|
6646
|
+
}
|
|
6647
|
+
}
|
|
6648
|
+
async abort() {
|
|
6649
|
+
if (!this.initialized)
|
|
6650
|
+
return;
|
|
6651
|
+
if (!this.controller || !this.runtime)
|
|
6652
|
+
return;
|
|
6653
|
+
this.turns.markAbortRequested();
|
|
6654
|
+
await this.controller.abort(this.runtime);
|
|
6655
|
+
}
|
|
6656
|
+
async sendCommand(command, options) {
|
|
6657
|
+
if (!this.initialized) {
|
|
6658
|
+
await this.initialize();
|
|
6659
|
+
}
|
|
6660
|
+
if (!this.controller) {
|
|
6661
|
+
throw new Error("Session is not initialized");
|
|
6662
|
+
}
|
|
6663
|
+
if (!command || typeof command !== "object" || Array.isArray(command)) {
|
|
6664
|
+
throw new Error("Invalid command. Expected a protocol command object.");
|
|
6665
|
+
}
|
|
6666
|
+
if (typeof command.type !== "string" || command.type.length === 0) {
|
|
6667
|
+
throw new Error("Invalid command. Expected a non-empty type.");
|
|
6668
|
+
}
|
|
6669
|
+
if (!options || !options.responseType && !options.predicate && options.timeoutMs === undefined) {
|
|
6670
|
+
this.controller.send(command);
|
|
6671
|
+
return;
|
|
6672
|
+
}
|
|
6673
|
+
const { type, ...body } = command;
|
|
6674
|
+
const response = await this.controller.request(type, body, {
|
|
6675
|
+
...options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {},
|
|
6676
|
+
predicate: options.predicate ? (message) => options.predicate?.(message) === true : options.responseType ? (message) => message.type === options.responseType : undefined
|
|
6677
|
+
});
|
|
6678
|
+
return response;
|
|
6679
|
+
}
|
|
6680
|
+
async listModels() {
|
|
6681
|
+
if (!this.initialized) {
|
|
6682
|
+
await this.initialize();
|
|
6683
|
+
}
|
|
6684
|
+
if (!this.controller) {
|
|
6685
|
+
throw new Error("Session is not initialized");
|
|
6686
|
+
}
|
|
6687
|
+
return this.controller.listModels();
|
|
6688
|
+
}
|
|
6689
|
+
async updateModel(update) {
|
|
6690
|
+
if (!this.initialized) {
|
|
6691
|
+
await this.initialize();
|
|
6692
|
+
}
|
|
6693
|
+
if (!this.controller || !this.runtime) {
|
|
6694
|
+
throw new Error("Session is not initialized");
|
|
6695
|
+
}
|
|
6696
|
+
return this.applyModelUpdate(update);
|
|
6697
|
+
}
|
|
6698
|
+
async applyModelUpdate(update) {
|
|
6699
|
+
if (!this.controller || !this.runtime) {
|
|
6700
|
+
throw new Error("Session is not initialized");
|
|
6701
|
+
}
|
|
6702
|
+
const normalized = normalizeUpdateModelInput(update);
|
|
6703
|
+
const payload = await this.resolveUpdateModelPayload(normalized);
|
|
6704
|
+
const result = await this.controller.updateModel(this.runtime, payload);
|
|
6705
|
+
if (result.modelHandle !== undefined) {
|
|
6706
|
+
this._model = result.modelHandle;
|
|
6707
|
+
} else if (payload.model_handle !== undefined) {
|
|
6708
|
+
this._model = payload.model_handle;
|
|
6709
|
+
} else if (typeof result.modelSettings?.model === "string") {
|
|
6710
|
+
this._model = result.modelSettings.model;
|
|
6321
6711
|
}
|
|
6322
6712
|
if ("modelSettings" in result) {
|
|
6323
6713
|
this._modelSettings = result.modelSettings ?? null;
|
|
@@ -6436,562 +6826,212 @@ class RemoteClientSessionCore {
|
|
|
6436
6826
|
rejectOnce(error instanceof Error ? error : new Error(String(error)));
|
|
6437
6827
|
});
|
|
6438
6828
|
});
|
|
6439
|
-
}
|
|
6440
|
-
async listMessages(options = {}) {
|
|
6441
|
-
if (!this.initialized) {
|
|
6442
|
-
await this.initialize();
|
|
6443
|
-
}
|
|
6444
|
-
if (!this.controller) {
|
|
6445
|
-
throw new Error("Session is not initialized");
|
|
6446
|
-
}
|
|
6447
|
-
const conversationId = options.conversationId ?? this._conversationId;
|
|
6448
|
-
if (!conversationId) {
|
|
6449
|
-
throw new Error("No conversation id available for listMessages()");
|
|
6450
|
-
}
|
|
6451
|
-
return this.controller.listMessages(conversationId, options);
|
|
6452
|
-
}
|
|
6453
|
-
async bootstrapState(options = {}) {
|
|
6454
|
-
if (!this.initialized) {
|
|
6455
|
-
await this.initialize();
|
|
6456
|
-
}
|
|
6457
|
-
const page = await this.listMessages({
|
|
6458
|
-
limit: options.limit,
|
|
6459
|
-
order: options.order
|
|
6460
|
-
});
|
|
6461
|
-
const state = {
|
|
6462
|
-
agentId: this._agentId ?? "",
|
|
6463
|
-
conversationId: this._conversationId ?? "",
|
|
6464
|
-
model: this._model,
|
|
6465
|
-
messages: page.messages
|
|
6466
|
-
};
|
|
6467
|
-
if (this.toolNames !== undefined) {
|
|
6468
|
-
state.tools = this.toolNames;
|
|
6469
|
-
}
|
|
6470
|
-
if (page.nextBefore !== undefined) {
|
|
6471
|
-
state.nextBefore = page.nextBefore;
|
|
6472
|
-
}
|
|
6473
|
-
if (page.hasMore !== undefined) {
|
|
6474
|
-
state.hasMore = page.hasMore;
|
|
6475
|
-
}
|
|
6476
|
-
return state;
|
|
6477
|
-
}
|
|
6478
|
-
close() {
|
|
6479
|
-
if (this.closed)
|
|
6480
|
-
return;
|
|
6481
|
-
this.closed = true;
|
|
6482
|
-
this.removeMessageHandler?.();
|
|
6483
|
-
this.removeMessageHandler = null;
|
|
6484
|
-
if (this.activeTurn?.timeout)
|
|
6485
|
-
clearTimeout(this.activeTurn.timeout);
|
|
6486
|
-
for (const turn of this.pendingTurns) {
|
|
6487
|
-
if (turn.timeout)
|
|
6488
|
-
clearTimeout(turn.timeout);
|
|
6489
|
-
}
|
|
6490
|
-
this.activeTurn = null;
|
|
6491
|
-
this.pendingTurns.length = 0;
|
|
6492
|
-
for (const cancel of [...this.deviceStatusRefreshCancels]) {
|
|
6493
|
-
cancel(new Error(`Session closed while waiting for ${this.label} device status`));
|
|
6494
|
-
}
|
|
6495
|
-
this.deviceStatusRefreshCancels.clear();
|
|
6496
|
-
this.deviceStatusListeners.clear();
|
|
6497
|
-
this.controller?.close();
|
|
6498
|
-
this.controller = null;
|
|
6499
|
-
this.onCoreClose();
|
|
6500
|
-
this.resolveAll(null);
|
|
6501
|
-
}
|
|
6502
|
-
get agentId() {
|
|
6503
|
-
return this._agentId;
|
|
6504
|
-
}
|
|
6505
|
-
get sessionId() {
|
|
6506
|
-
return this._sessionId;
|
|
6507
|
-
}
|
|
6508
|
-
get conversationId() {
|
|
6509
|
-
return this._conversationId;
|
|
6510
|
-
}
|
|
6511
|
-
async[Symbol.asyncDispose]() {
|
|
6512
|
-
this.close();
|
|
6513
|
-
}
|
|
6514
|
-
async changeDeviceState(updates) {
|
|
6515
|
-
if (!this.initialized) {
|
|
6516
|
-
await this.initialize();
|
|
6517
|
-
}
|
|
6518
|
-
if (!this.controller || !this.runtime) {
|
|
6519
|
-
throw new Error("Session is not initialized");
|
|
6520
|
-
}
|
|
6521
|
-
const payload = {};
|
|
6522
|
-
if (updates.cwd !== undefined)
|
|
6523
|
-
payload.cwd = updates.cwd;
|
|
6524
|
-
if (updates.permissionMode !== undefined) {
|
|
6525
|
-
const mode = mapPermissionMode(updates.permissionMode);
|
|
6526
|
-
if (mode !== undefined)
|
|
6527
|
-
payload.mode = mode;
|
|
6528
|
-
}
|
|
6529
|
-
if (Object.keys(payload).length === 0) {
|
|
6530
|
-
throw new Error("Invalid device state update. Expected cwd or permissionMode.");
|
|
6531
|
-
}
|
|
6532
|
-
this.controller.send({
|
|
6533
|
-
type: "change_device_state",
|
|
6534
|
-
runtime: this.runtime,
|
|
6535
|
-
payload
|
|
6536
|
-
});
|
|
6537
|
-
}
|
|
6538
|
-
async updateToolset(toolsetPreference) {
|
|
6539
|
-
if (!this.initialized) {
|
|
6540
|
-
await this.initialize();
|
|
6541
|
-
}
|
|
6542
|
-
if (!this.controller || !this.runtime) {
|
|
6543
|
-
throw new Error("Session is not initialized");
|
|
6544
|
-
}
|
|
6545
|
-
const response = await this.controller.request("update_toolset", {
|
|
6546
|
-
runtime: this.runtime,
|
|
6547
|
-
toolset_preference: toolsetPreference
|
|
6548
|
-
}, { predicate: (message) => message.type === "update_toolset_response" });
|
|
6549
|
-
ensureSuccess(response, "Failed to update toolset");
|
|
6550
|
-
}
|
|
6551
|
-
async afterRuntimeInitialized() {}
|
|
6552
|
-
async beforeTurn() {}
|
|
6553
|
-
onCoreClose() {}
|
|
6554
|
-
currentOptions() {
|
|
6555
|
-
return this.mode.options;
|
|
6556
|
-
}
|
|
6557
|
-
shouldEnableMemfs(options) {
|
|
6558
|
-
return false;
|
|
6559
|
-
}
|
|
6560
|
-
enableMemfsBody() {
|
|
6561
|
-
if (!this.runtime)
|
|
6562
|
-
return {};
|
|
6563
|
-
return { agent_id: this.runtime.agent_id };
|
|
6564
|
-
}
|
|
6565
|
-
setModel(model) {
|
|
6566
|
-
this._model = model;
|
|
6567
|
-
}
|
|
6568
|
-
trackSentTurn(runtime) {
|
|
6569
|
-
const turn = {
|
|
6570
|
-
id: ++this.nextTurnId,
|
|
6571
|
-
runtime,
|
|
6572
|
-
clientMessageId: `sdk-message-${Date.now()}-${++this.clientMessageCounter}`,
|
|
6573
|
-
queuedAt: Date.now(),
|
|
6574
|
-
startedAt: 0,
|
|
6575
|
-
assistantText: "",
|
|
6576
|
-
runIds: new Set,
|
|
6577
|
-
observedTurnEvidence: false,
|
|
6578
|
-
observedRequiresApprovalStop: false,
|
|
6579
|
-
abortRequested: false,
|
|
6580
|
-
timeout: null
|
|
6581
|
-
};
|
|
6582
|
-
if (this.activeTurn) {
|
|
6583
|
-
this.pendingTurns.push(turn);
|
|
6584
|
-
} else {
|
|
6585
|
-
this.activateTurn(turn);
|
|
6586
|
-
}
|
|
6587
|
-
return turn;
|
|
6588
|
-
}
|
|
6589
|
-
activateTurn(turn) {
|
|
6590
|
-
this.activeTurn = turn;
|
|
6591
|
-
turn.startedAt = Date.now();
|
|
6592
|
-
this.activeTurnStartedAt = turn.startedAt;
|
|
6593
|
-
if (this.requestTimeoutMs !== undefined) {
|
|
6594
|
-
turn.timeout = setTimeout(() => {
|
|
6595
|
-
this.failTurn(turn, `Timed out waiting for ${this.label} turn`);
|
|
6596
|
-
}, this.requestTimeoutMs);
|
|
6597
|
-
turn.timeout.unref?.();
|
|
6598
|
-
}
|
|
6599
|
-
}
|
|
6600
|
-
activateNextTurnFromProtocol() {
|
|
6601
|
-
if (this.activeTurn)
|
|
6602
|
-
return this.activeTurn;
|
|
6603
|
-
const next = this.pendingTurns.shift();
|
|
6604
|
-
if (!next)
|
|
6605
|
-
return null;
|
|
6606
|
-
this.activateTurn(next);
|
|
6607
|
-
return next;
|
|
6608
|
-
}
|
|
6609
|
-
removeTrackedTurn(turn) {
|
|
6610
|
-
if (turn.timeout)
|
|
6611
|
-
clearTimeout(turn.timeout);
|
|
6612
|
-
if (this.activeTurn === turn) {
|
|
6613
|
-
this.activeTurn = null;
|
|
6614
|
-
return;
|
|
6615
|
-
}
|
|
6616
|
-
const index = this.pendingTurns.indexOf(turn);
|
|
6617
|
-
if (index !== -1)
|
|
6618
|
-
this.pendingTurns.splice(index, 1);
|
|
6619
|
-
}
|
|
6620
|
-
failTurn(turn, detail) {
|
|
6621
|
-
if (this.activeTurn !== turn)
|
|
6622
|
-
return;
|
|
6623
|
-
this.enqueue({
|
|
6624
|
-
type: "error",
|
|
6625
|
-
message: detail,
|
|
6626
|
-
errorCode: "error",
|
|
6627
|
-
stopReason: "error",
|
|
6628
|
-
errorDetail: detail,
|
|
6629
|
-
recoverable: false
|
|
6630
|
-
});
|
|
6631
|
-
this.completeActiveTurn({
|
|
6632
|
-
runtime: turn.runtime,
|
|
6633
|
-
stopReason: "error",
|
|
6634
|
-
runIds: [...turn.runIds],
|
|
6635
|
-
success: false,
|
|
6636
|
-
detail,
|
|
6637
|
-
errorCode: "error"
|
|
6638
|
-
});
|
|
6639
|
-
}
|
|
6640
|
-
completeActiveTurn(turn) {
|
|
6641
|
-
const active = this.activeTurn;
|
|
6642
|
-
if (!active)
|
|
6643
|
-
return;
|
|
6644
|
-
if (active.timeout) {
|
|
6645
|
-
clearTimeout(active.timeout);
|
|
6646
|
-
active.timeout = null;
|
|
6647
|
-
}
|
|
6648
|
-
this.enqueue(this.resultFromTurn(turn, active));
|
|
6649
|
-
this.activeTurn = null;
|
|
6650
|
-
}
|
|
6651
|
-
async resolveUpdateModelPayload(input) {
|
|
6652
|
-
if (input.reasoningEffort === undefined) {
|
|
6653
|
-
return modelPayloadWithoutReasoning(input);
|
|
6829
|
+
}
|
|
6830
|
+
async listMessages(options = {}) {
|
|
6831
|
+
if (!this.initialized) {
|
|
6832
|
+
await this.initialize();
|
|
6654
6833
|
}
|
|
6655
6834
|
if (!this.controller) {
|
|
6656
6835
|
throw new Error("Session is not initialized");
|
|
6657
6836
|
}
|
|
6658
|
-
const
|
|
6659
|
-
|
|
6660
|
-
|
|
6661
|
-
let baseEntry;
|
|
6662
|
-
let explicitHandle;
|
|
6663
|
-
let targetHandle;
|
|
6664
|
-
if (input.modelId !== undefined) {
|
|
6665
|
-
baseEntry = byId.get(input.modelId);
|
|
6666
|
-
explicitHandle = input.modelHandle;
|
|
6667
|
-
targetHandle = baseEntry?.handle ?? toBaseModelHandle(input.modelHandle, aliases);
|
|
6668
|
-
} else if (input.modelHandle !== undefined) {
|
|
6669
|
-
explicitHandle = input.modelHandle;
|
|
6670
|
-
targetHandle = toBaseModelHandle(input.modelHandle, aliases);
|
|
6671
|
-
} else if (input.model !== undefined) {
|
|
6672
|
-
baseEntry = byId.get(input.model);
|
|
6673
|
-
if (baseEntry) {
|
|
6674
|
-
targetHandle = baseEntry.handle;
|
|
6675
|
-
} else {
|
|
6676
|
-
explicitHandle = input.model;
|
|
6677
|
-
targetHandle = toBaseModelHandle(input.model, aliases);
|
|
6678
|
-
}
|
|
6679
|
-
} else {
|
|
6680
|
-
explicitHandle = this._model || undefined;
|
|
6681
|
-
targetHandle = toBaseModelHandle(this._model || undefined, aliases);
|
|
6837
|
+
const conversationId = options.conversationId ?? this._conversationId;
|
|
6838
|
+
if (!conversationId) {
|
|
6839
|
+
throw new Error("No conversation id available for listMessages()");
|
|
6682
6840
|
}
|
|
6683
|
-
|
|
6684
|
-
|
|
6841
|
+
return this.controller.listMessages(conversationId, options);
|
|
6842
|
+
}
|
|
6843
|
+
async bootstrapState(options = {}) {
|
|
6844
|
+
if (!this.initialized) {
|
|
6845
|
+
await this.initialize();
|
|
6685
6846
|
}
|
|
6686
|
-
const
|
|
6687
|
-
|
|
6688
|
-
|
|
6847
|
+
const page = await this.listMessages({
|
|
6848
|
+
limit: options.limit,
|
|
6849
|
+
order: options.order
|
|
6850
|
+
});
|
|
6851
|
+
const state = {
|
|
6852
|
+
agentId: this._agentId ?? "",
|
|
6853
|
+
conversationId: this._conversationId ?? "",
|
|
6854
|
+
model: this._model,
|
|
6855
|
+
messages: page.messages
|
|
6856
|
+
};
|
|
6857
|
+
if (this.toolNames !== undefined) {
|
|
6858
|
+
state.tools = this.toolNames;
|
|
6689
6859
|
}
|
|
6690
|
-
|
|
6691
|
-
|
|
6692
|
-
const matchingEntry = scopedCandidates.find((entry) => getReasoningEffort(entry) === input.reasoningEffort) ?? candidates.find((entry) => getReasoningEffort(entry) === input.reasoningEffort);
|
|
6693
|
-
if (!matchingEntry) {
|
|
6694
|
-
throw new Error(`No ${input.reasoningEffort} reasoning tier found for model ${targetHandle}.`);
|
|
6860
|
+
if (page.nextBefore !== undefined) {
|
|
6861
|
+
state.nextBefore = page.nextBefore;
|
|
6695
6862
|
}
|
|
6696
|
-
|
|
6697
|
-
|
|
6698
|
-
payload.model_handle = explicitHandle;
|
|
6863
|
+
if (page.hasMore !== undefined) {
|
|
6864
|
+
state.hasMore = page.hasMore;
|
|
6699
6865
|
}
|
|
6700
|
-
return
|
|
6866
|
+
return state;
|
|
6701
6867
|
}
|
|
6702
|
-
|
|
6703
|
-
if (
|
|
6704
|
-
return;
|
|
6705
|
-
const options = this.currentOptions();
|
|
6706
|
-
if (this.shouldEnableMemfs(options)) {
|
|
6707
|
-
const response = await this.controller.request("enable_memfs", this.enableMemfsBody(), {
|
|
6708
|
-
predicate: (message) => message.type === "enable_memfs_response",
|
|
6709
|
-
timeoutMs: 180000
|
|
6710
|
-
});
|
|
6711
|
-
ensureSuccess(response, "Failed to enable memfs");
|
|
6712
|
-
}
|
|
6713
|
-
const dreamingSettings = resolveDreamingSettings(options.dreaming);
|
|
6714
|
-
if (dreamingSettings) {
|
|
6715
|
-
const response = await this.controller.request("set_reflection_settings", {
|
|
6716
|
-
runtime: this.runtime,
|
|
6717
|
-
settings: dreamingSettings,
|
|
6718
|
-
scope: "both"
|
|
6719
|
-
}, { predicate: (message) => message.type === "set_reflection_settings_response" });
|
|
6720
|
-
ensureSuccess(response, "Failed to update dreaming settings");
|
|
6721
|
-
}
|
|
6722
|
-
if (this.mode.kind !== "session")
|
|
6868
|
+
close() {
|
|
6869
|
+
if (this.closed)
|
|
6723
6870
|
return;
|
|
6724
|
-
|
|
6725
|
-
|
|
6726
|
-
|
|
6727
|
-
|
|
6728
|
-
|
|
6871
|
+
this.closed = true;
|
|
6872
|
+
this.removeMessageHandler?.();
|
|
6873
|
+
this.removeMessageHandler = null;
|
|
6874
|
+
this.turns.close();
|
|
6875
|
+
for (const cancel of [...this.deviceStatusRefreshCancels]) {
|
|
6876
|
+
cancel(new Error(`Session closed while waiting for ${this.label} device status`));
|
|
6729
6877
|
}
|
|
6878
|
+
this.deviceStatusRefreshCancels.clear();
|
|
6879
|
+
this.deviceStatusListeners.clear();
|
|
6880
|
+
this.controller?.close();
|
|
6881
|
+
this.controller = null;
|
|
6882
|
+
this.onCoreClose();
|
|
6730
6883
|
}
|
|
6731
|
-
|
|
6732
|
-
|
|
6733
|
-
|
|
6734
|
-
|
|
6735
|
-
|
|
6736
|
-
|
|
6737
|
-
|
|
6738
|
-
|
|
6739
|
-
|
|
6740
|
-
|
|
6741
|
-
|
|
6742
|
-
|
|
6743
|
-
|
|
6744
|
-
|
|
6745
|
-
|
|
6746
|
-
if (message.type === "update_queue") {
|
|
6747
|
-
const sdkMessage2 = {
|
|
6748
|
-
type: "queue_update",
|
|
6749
|
-
queue: queueItems(message)
|
|
6750
|
-
};
|
|
6751
|
-
this.enqueue(sdkMessage2);
|
|
6752
|
-
return;
|
|
6753
|
-
}
|
|
6754
|
-
if (message.type === "update_loop_status") {
|
|
6755
|
-
this.handleLoopStatusMessage(message);
|
|
6756
|
-
return;
|
|
6757
|
-
}
|
|
6758
|
-
const delta = streamDeltaRecord(message);
|
|
6759
|
-
if (!delta)
|
|
6760
|
-
return;
|
|
6761
|
-
const active = this.activateNextTurnFromProtocol();
|
|
6762
|
-
if (active) {
|
|
6763
|
-
active.observedTurnEvidence = true;
|
|
6764
|
-
const runId = streamDeltaRunId2(delta);
|
|
6765
|
-
if (runId)
|
|
6766
|
-
active.runIds.add(runId);
|
|
6767
|
-
}
|
|
6768
|
-
const sdkMessage = this.transformStreamDelta(delta);
|
|
6769
|
-
if (sdkMessage) {
|
|
6770
|
-
this.enqueue(sdkMessage);
|
|
6884
|
+
get agentId() {
|
|
6885
|
+
return this._agentId;
|
|
6886
|
+
}
|
|
6887
|
+
get sessionId() {
|
|
6888
|
+
return this._sessionId;
|
|
6889
|
+
}
|
|
6890
|
+
get conversationId() {
|
|
6891
|
+
return this._conversationId;
|
|
6892
|
+
}
|
|
6893
|
+
async[Symbol.asyncDispose]() {
|
|
6894
|
+
this.close();
|
|
6895
|
+
}
|
|
6896
|
+
async changeDeviceState(updates) {
|
|
6897
|
+
if (!this.initialized) {
|
|
6898
|
+
await this.initialize();
|
|
6771
6899
|
}
|
|
6772
|
-
this.
|
|
6773
|
-
|
|
6774
|
-
handleLoopStatusMessage(message) {
|
|
6775
|
-
const status = loopStatusValue(message);
|
|
6776
|
-
if (!status)
|
|
6777
|
-
return;
|
|
6778
|
-
const activeRunIds = loopStatusRunIds(message);
|
|
6779
|
-
const sdkMessage = {
|
|
6780
|
-
type: "loop_status",
|
|
6781
|
-
status,
|
|
6782
|
-
activeRunIds
|
|
6783
|
-
};
|
|
6784
|
-
this.enqueue(sdkMessage);
|
|
6785
|
-
const active = this.activeTurn;
|
|
6786
|
-
if (!active)
|
|
6787
|
-
return;
|
|
6788
|
-
for (const runId of activeRunIds)
|
|
6789
|
-
active.runIds.add(runId);
|
|
6790
|
-
const hadTurnEvidence = active.observedTurnEvidence || active.observedRequiresApprovalStop;
|
|
6791
|
-
if (!hadTurnEvidence)
|
|
6792
|
-
return;
|
|
6793
|
-
if (status === "WAITING_ON_APPROVAL") {
|
|
6794
|
-
this.completeActiveTurn({
|
|
6795
|
-
runtime: active.runtime,
|
|
6796
|
-
stopReason: "requires_approval",
|
|
6797
|
-
runIds: [...active.runIds]
|
|
6798
|
-
});
|
|
6799
|
-
return;
|
|
6900
|
+
if (!this.controller || !this.runtime) {
|
|
6901
|
+
throw new Error("Session is not initialized");
|
|
6800
6902
|
}
|
|
6801
|
-
|
|
6802
|
-
|
|
6803
|
-
|
|
6804
|
-
|
|
6805
|
-
|
|
6806
|
-
|
|
6807
|
-
|
|
6808
|
-
errorCode: "interrupted"
|
|
6809
|
-
});
|
|
6810
|
-
return;
|
|
6903
|
+
const payload = {};
|
|
6904
|
+
if (updates.cwd !== undefined)
|
|
6905
|
+
payload.cwd = updates.cwd;
|
|
6906
|
+
if (updates.permissionMode !== undefined) {
|
|
6907
|
+
const mode = mapPermissionMode(updates.permissionMode);
|
|
6908
|
+
if (mode !== undefined)
|
|
6909
|
+
payload.mode = mode;
|
|
6811
6910
|
}
|
|
6812
|
-
if (
|
|
6813
|
-
|
|
6814
|
-
runtime: active.runtime,
|
|
6815
|
-
stopReason: null,
|
|
6816
|
-
runIds: [...active.runIds]
|
|
6817
|
-
});
|
|
6911
|
+
if (Object.keys(payload).length === 0) {
|
|
6912
|
+
throw new Error("Invalid device state update. Expected cwd or permissionMode.");
|
|
6818
6913
|
}
|
|
6914
|
+
this.controller.send({
|
|
6915
|
+
type: "change_device_state",
|
|
6916
|
+
runtime: this.runtime,
|
|
6917
|
+
payload
|
|
6918
|
+
});
|
|
6819
6919
|
}
|
|
6820
|
-
|
|
6821
|
-
|
|
6822
|
-
|
|
6823
|
-
return;
|
|
6824
|
-
const messageType = streamDeltaMessageType2(delta);
|
|
6825
|
-
if (messageType === "stop_reason") {
|
|
6826
|
-
const stopReason = streamDeltaStopReason2(delta) ?? null;
|
|
6827
|
-
if (stopReason === "requires_approval") {
|
|
6828
|
-
active.observedRequiresApprovalStop = true;
|
|
6829
|
-
return;
|
|
6830
|
-
}
|
|
6831
|
-
this.completeActiveTurn({
|
|
6832
|
-
runtime: active.runtime,
|
|
6833
|
-
stopReason,
|
|
6834
|
-
runIds: [...active.runIds]
|
|
6835
|
-
});
|
|
6836
|
-
return;
|
|
6920
|
+
async updateToolset(toolsetPreference) {
|
|
6921
|
+
if (!this.initialized) {
|
|
6922
|
+
await this.initialize();
|
|
6837
6923
|
}
|
|
6838
|
-
if (
|
|
6839
|
-
|
|
6840
|
-
runtime: active.runtime,
|
|
6841
|
-
stopReason: sdkMessage.stopReason,
|
|
6842
|
-
runIds: [...active.runIds],
|
|
6843
|
-
success: false,
|
|
6844
|
-
detail: sdkMessage.errorDetail ?? sdkMessage.message,
|
|
6845
|
-
errorCode: sdkMessage.errorCode
|
|
6846
|
-
});
|
|
6924
|
+
if (!this.controller || !this.runtime) {
|
|
6925
|
+
throw new Error("Session is not initialized");
|
|
6847
6926
|
}
|
|
6927
|
+
const response = await this.controller.request("update_toolset", {
|
|
6928
|
+
runtime: this.runtime,
|
|
6929
|
+
toolset_preference: toolsetPreference
|
|
6930
|
+
}, { predicate: (message) => message.type === "update_toolset_response" });
|
|
6931
|
+
ensureSuccess(response, "Failed to update toolset");
|
|
6932
|
+
}
|
|
6933
|
+
async afterRuntimeInitialized() {}
|
|
6934
|
+
async beforeTurn() {}
|
|
6935
|
+
onCoreClose() {}
|
|
6936
|
+
currentOptions() {
|
|
6937
|
+
return this.mode.options;
|
|
6938
|
+
}
|
|
6939
|
+
shouldEnableMemfs(options) {
|
|
6940
|
+
return false;
|
|
6941
|
+
}
|
|
6942
|
+
enableMemfsBody() {
|
|
6943
|
+
if (!this.runtime)
|
|
6944
|
+
return {};
|
|
6945
|
+
return { agent_id: this.runtime.agent_id };
|
|
6848
6946
|
}
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
if (
|
|
6854
|
-
|
|
6855
|
-
if (!content)
|
|
6856
|
-
return null;
|
|
6857
|
-
if (this.activeTurn)
|
|
6858
|
-
this.activeTurn.assistantText += content;
|
|
6859
|
-
return { type: "assistant", content, uuid, runId };
|
|
6947
|
+
setModel(model) {
|
|
6948
|
+
this._model = model;
|
|
6949
|
+
}
|
|
6950
|
+
async resolveUpdateModelPayload(input) {
|
|
6951
|
+
if (input.reasoningEffort === undefined) {
|
|
6952
|
+
return modelPayloadWithoutReasoning(input);
|
|
6860
6953
|
}
|
|
6861
|
-
if (
|
|
6862
|
-
|
|
6863
|
-
if (!content)
|
|
6864
|
-
return null;
|
|
6865
|
-
return { type: "reasoning", content, uuid, runId };
|
|
6954
|
+
if (!this.controller) {
|
|
6955
|
+
throw new Error("Session is not initialized");
|
|
6866
6956
|
}
|
|
6867
|
-
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
|
|
6872
|
-
|
|
6873
|
-
|
|
6874
|
-
|
|
6875
|
-
|
|
6876
|
-
|
|
6877
|
-
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
6881
|
-
|
|
6882
|
-
|
|
6883
|
-
|
|
6957
|
+
const catalog = await this.controller.listModels();
|
|
6958
|
+
const byId = new Map(catalog.entries.map((entry) => [entry.id, entry]));
|
|
6959
|
+
const aliases = catalog.byokProviderAliases;
|
|
6960
|
+
let baseEntry;
|
|
6961
|
+
let explicitHandle;
|
|
6962
|
+
let targetHandle;
|
|
6963
|
+
if (input.modelId !== undefined) {
|
|
6964
|
+
baseEntry = byId.get(input.modelId);
|
|
6965
|
+
explicitHandle = input.modelHandle;
|
|
6966
|
+
targetHandle = baseEntry?.handle ?? toBaseModelHandle(input.modelHandle, aliases);
|
|
6967
|
+
} else if (input.modelHandle !== undefined) {
|
|
6968
|
+
explicitHandle = input.modelHandle;
|
|
6969
|
+
targetHandle = toBaseModelHandle(input.modelHandle, aliases);
|
|
6970
|
+
} else if (input.model !== undefined) {
|
|
6971
|
+
baseEntry = byId.get(input.model);
|
|
6972
|
+
if (baseEntry) {
|
|
6973
|
+
targetHandle = baseEntry.handle;
|
|
6974
|
+
} else {
|
|
6975
|
+
explicitHandle = input.model;
|
|
6976
|
+
targetHandle = toBaseModelHandle(input.model, aliases);
|
|
6884
6977
|
}
|
|
6885
|
-
|
|
6886
|
-
|
|
6887
|
-
|
|
6888
|
-
type: "tool_call",
|
|
6889
|
-
toolCallId,
|
|
6890
|
-
toolName,
|
|
6891
|
-
toolInput: input,
|
|
6892
|
-
rawArguments: raw,
|
|
6893
|
-
uuid,
|
|
6894
|
-
runId
|
|
6895
|
-
};
|
|
6978
|
+
} else {
|
|
6979
|
+
explicitHandle = this._model || undefined;
|
|
6980
|
+
targetHandle = toBaseModelHandle(this._model || undefined, aliases);
|
|
6896
6981
|
}
|
|
6897
|
-
if (
|
|
6898
|
-
|
|
6899
|
-
const toolCallId = (typeof delta.tool_call_id === "string" ? delta.tool_call_id : undefined) ?? (typeof toolReturn.tool_call_id === "string" ? toolReturn.tool_call_id : undefined);
|
|
6900
|
-
if (!toolCallId)
|
|
6901
|
-
return null;
|
|
6902
|
-
const content = extractTextFromContent(delta.tool_return ?? toolReturn.tool_return ?? toolReturn.content) ?? "";
|
|
6903
|
-
const status = typeof delta.status === "string" ? delta.status : toolReturn.status;
|
|
6904
|
-
return {
|
|
6905
|
-
type: "tool_result",
|
|
6906
|
-
toolCallId,
|
|
6907
|
-
content,
|
|
6908
|
-
isError: status === "error",
|
|
6909
|
-
uuid,
|
|
6910
|
-
runId
|
|
6911
|
-
};
|
|
6982
|
+
if (!targetHandle) {
|
|
6983
|
+
throw new Error("reasoningEffort requires a current model or explicit model/modelId/modelHandle.");
|
|
6912
6984
|
}
|
|
6913
|
-
|
|
6914
|
-
|
|
6915
|
-
|
|
6916
|
-
const approvalConflict = isApprovalConflictSignal({
|
|
6917
|
-
detail,
|
|
6918
|
-
message: typeof delta.message === "string" ? delta.message : undefined,
|
|
6919
|
-
stopReason
|
|
6920
|
-
});
|
|
6921
|
-
return {
|
|
6922
|
-
type: "error",
|
|
6923
|
-
message: detail,
|
|
6924
|
-
errorCode: approvalConflict ? "approval_conflict" : toSdkErrorCode(stopReason),
|
|
6925
|
-
approvalConflict: approvalConflict || undefined,
|
|
6926
|
-
recoverable: approvalConflict ? true : false,
|
|
6927
|
-
errorDetail: detail,
|
|
6928
|
-
stopReason,
|
|
6929
|
-
runId
|
|
6930
|
-
};
|
|
6985
|
+
const candidates = catalog.entries.filter((entry) => entry.handle === targetHandle || entry.handle === explicitHandle);
|
|
6986
|
+
if (candidates.length === 0) {
|
|
6987
|
+
throw new Error(`reasoningEffort requires a model from listModels(); no catalog entry found for ${targetHandle}.`);
|
|
6931
6988
|
}
|
|
6932
|
-
|
|
6933
|
-
|
|
6934
|
-
|
|
6935
|
-
|
|
6936
|
-
|
|
6937
|
-
maxAttempts: typeof delta.max_attempts === "number" ? delta.max_attempts : 0,
|
|
6938
|
-
delayMs: typeof delta.delay_ms === "number" ? delta.delay_ms : 0,
|
|
6939
|
-
runId
|
|
6940
|
-
};
|
|
6989
|
+
const contextWindow = getContextWindow(baseEntry?.updateArgs) ?? getContextWindow(this._modelSettings);
|
|
6990
|
+
const scopedCandidates = sameContextCandidates(candidates, contextWindow);
|
|
6991
|
+
const matchingEntry = scopedCandidates.find((entry) => getReasoningEffort(entry) === input.reasoningEffort) ?? candidates.find((entry) => getReasoningEffort(entry) === input.reasoningEffort);
|
|
6992
|
+
if (!matchingEntry) {
|
|
6993
|
+
throw new Error(`No ${input.reasoningEffort} reasoning tier found for model ${targetHandle}.`);
|
|
6941
6994
|
}
|
|
6942
|
-
|
|
6943
|
-
|
|
6995
|
+
const payload = { model_id: matchingEntry.id };
|
|
6996
|
+
if (explicitHandle !== undefined) {
|
|
6997
|
+
payload.model_handle = explicitHandle;
|
|
6944
6998
|
}
|
|
6945
|
-
return
|
|
6946
|
-
type: "stream_event",
|
|
6947
|
-
event: delta,
|
|
6948
|
-
uuid
|
|
6949
|
-
};
|
|
6950
|
-
}
|
|
6951
|
-
resultFromTurn(turn, tracker) {
|
|
6952
|
-
const stopReason = turn.stopReason ?? (turn.success === false ? "error" : undefined);
|
|
6953
|
-
const approvalConflict = isApprovalConflictSignal({
|
|
6954
|
-
detail: turn.detail,
|
|
6955
|
-
stopReason
|
|
6956
|
-
});
|
|
6957
|
-
const success = turn.success !== undefined ? turn.success && !approvalConflict && !FAILURE_STOP_REASONS.has(stopReason ?? "") : !approvalConflict && !FAILURE_STOP_REASONS.has(stopReason ?? "");
|
|
6958
|
-
const errorCode = approvalConflict ? "approval_conflict" : turn.errorCode ?? toSdkErrorCode(stopReason);
|
|
6959
|
-
return {
|
|
6960
|
-
type: "result",
|
|
6961
|
-
success,
|
|
6962
|
-
result: success ? tracker?.assistantText || undefined : undefined,
|
|
6963
|
-
error: success ? undefined : errorCode ?? stopReason ?? "error",
|
|
6964
|
-
errorCode: success ? undefined : errorCode ?? "error",
|
|
6965
|
-
approvalConflict: approvalConflict || undefined,
|
|
6966
|
-
recoverable: approvalConflict ? true : success ? undefined : false,
|
|
6967
|
-
errorDetail: success ? undefined : turn.detail,
|
|
6968
|
-
stopReason,
|
|
6969
|
-
durationMs: Date.now() - (tracker?.startedAt || this.activeTurnStartedAt),
|
|
6970
|
-
conversationId: turn.runtime.conversation_id,
|
|
6971
|
-
runIds: turn.runIds.length > 0 ? turn.runIds : undefined
|
|
6972
|
-
};
|
|
6999
|
+
return payload;
|
|
6973
7000
|
}
|
|
6974
|
-
|
|
6975
|
-
|
|
6976
|
-
if (resolver) {
|
|
6977
|
-
resolver(message);
|
|
7001
|
+
async applyPostInitializeOptions() {
|
|
7002
|
+
if (!this.controller || !this.runtime)
|
|
6978
7003
|
return;
|
|
7004
|
+
const options = this.currentOptions();
|
|
7005
|
+
if (this.shouldEnableMemfs(options)) {
|
|
7006
|
+
const response = await this.controller.request("enable_memfs", this.enableMemfsBody(), {
|
|
7007
|
+
predicate: (message) => message.type === "enable_memfs_response",
|
|
7008
|
+
timeoutMs: 180000
|
|
7009
|
+
});
|
|
7010
|
+
ensureSuccess(response, "Failed to enable memfs");
|
|
7011
|
+
}
|
|
7012
|
+
const dreamingSettings = resolveDreamingSettings(options.dreaming);
|
|
7013
|
+
if (dreamingSettings) {
|
|
7014
|
+
const response = await this.controller.request("set_reflection_settings", {
|
|
7015
|
+
runtime: this.runtime,
|
|
7016
|
+
settings: dreamingSettings,
|
|
7017
|
+
scope: "both"
|
|
7018
|
+
}, { predicate: (message) => message.type === "set_reflection_settings_response" });
|
|
7019
|
+
ensureSuccess(response, "Failed to update dreaming settings");
|
|
7020
|
+
}
|
|
7021
|
+
if (this.mode.kind !== "session")
|
|
7022
|
+
return;
|
|
7023
|
+
if (this.mode.options.model !== undefined || this.mode.options.reasoningEffort !== undefined) {
|
|
7024
|
+
await this.applyModelUpdate({
|
|
7025
|
+
...this.mode.options.model !== undefined ? { model: this.mode.options.model } : {},
|
|
7026
|
+
...this.mode.options.reasoningEffort !== undefined ? { reasoningEffort: this.mode.options.reasoningEffort } : {}
|
|
7027
|
+
});
|
|
6979
7028
|
}
|
|
6980
|
-
this.streamQueue.push(message);
|
|
6981
|
-
}
|
|
6982
|
-
nextMessage() {
|
|
6983
|
-
const next = this.streamQueue.shift();
|
|
6984
|
-
if (next)
|
|
6985
|
-
return Promise.resolve(next);
|
|
6986
|
-
if (this.closed)
|
|
6987
|
-
return Promise.resolve(null);
|
|
6988
|
-
return new Promise((resolve) => {
|
|
6989
|
-
this.streamResolvers.push(resolve);
|
|
6990
|
-
});
|
|
6991
7029
|
}
|
|
6992
|
-
|
|
6993
|
-
for (const
|
|
6994
|
-
|
|
7030
|
+
emitDeviceStatus(status) {
|
|
7031
|
+
for (const listener of [...this.deviceStatusListeners]) {
|
|
7032
|
+
try {
|
|
7033
|
+
listener(status);
|
|
7034
|
+
} catch {}
|
|
6995
7035
|
}
|
|
6996
7036
|
}
|
|
6997
7037
|
}
|
|
@@ -7892,13 +7932,58 @@ class RemoteEnvironmentClient {
|
|
|
7892
7932
|
}
|
|
7893
7933
|
}
|
|
7894
7934
|
|
|
7935
|
+
// src/cloud-sandbox.ts
|
|
7936
|
+
var MIN_TTL_MINUTES = 1;
|
|
7937
|
+
var MAX_TTL_MINUTES = 60;
|
|
7938
|
+
var MAX_GITHUB_REPOSITORIES = 10;
|
|
7939
|
+
var GITHUB_OWNER_PATTERN = /^[A-Za-z0-9-]+$/;
|
|
7940
|
+
var GITHUB_REPOSITORY_PATTERN = /^[A-Za-z0-9._-]+$/;
|
|
7941
|
+
function validatePositiveInteger(value, name) {
|
|
7942
|
+
if (value !== undefined && (!Number.isInteger(value) || value <= 0)) {
|
|
7943
|
+
throw new Error(`Invalid ${name}. Expected a positive integer.`);
|
|
7944
|
+
}
|
|
7945
|
+
}
|
|
7946
|
+
function validateCloudSandboxOptions(options, name) {
|
|
7947
|
+
if (options === undefined)
|
|
7948
|
+
return;
|
|
7949
|
+
if (options === null || typeof options !== "object" || Array.isArray(options)) {
|
|
7950
|
+
throw new Error(`Invalid ${name}. Expected an object.`);
|
|
7951
|
+
}
|
|
7952
|
+
if (options.ttlMinutes !== undefined && (!Number.isInteger(options.ttlMinutes) || options.ttlMinutes < MIN_TTL_MINUTES || options.ttlMinutes > MAX_TTL_MINUTES)) {
|
|
7953
|
+
throw new Error(`Invalid ${name}.ttlMinutes. Expected an integer between ${MIN_TTL_MINUTES} and ${MAX_TTL_MINUTES}.`);
|
|
7954
|
+
}
|
|
7955
|
+
validatePositiveInteger(options.readyTimeoutMs, `${name}.readyTimeoutMs`);
|
|
7956
|
+
validatePositiveInteger(options.readyPollIntervalMs, `${name}.readyPollIntervalMs`);
|
|
7957
|
+
validatePositiveInteger(options.refreshIntervalMs, `${name}.refreshIntervalMs`);
|
|
7958
|
+
if (options.githubRepositories !== undefined) {
|
|
7959
|
+
if (!Array.isArray(options.githubRepositories)) {
|
|
7960
|
+
throw new Error(`Invalid ${name}.githubRepositories. Expected an array.`);
|
|
7961
|
+
}
|
|
7962
|
+
if (options.githubRepositories.length > MAX_GITHUB_REPOSITORIES) {
|
|
7963
|
+
throw new Error(`Invalid ${name}.githubRepositories. Expected at most ${MAX_GITHUB_REPOSITORIES} repositories.`);
|
|
7964
|
+
}
|
|
7965
|
+
for (const [index, repository] of options.githubRepositories.entries()) {
|
|
7966
|
+
if (repository === null || typeof repository !== "object" || Array.isArray(repository)) {
|
|
7967
|
+
throw new Error(`Invalid ${name}.githubRepositories[${index}]. Expected an object.`);
|
|
7968
|
+
}
|
|
7969
|
+
if (typeof repository.owner !== "string" || !GITHUB_OWNER_PATTERN.test(repository.owner)) {
|
|
7970
|
+
throw new Error(`Invalid ${name}.githubRepositories[${index}].owner.`);
|
|
7971
|
+
}
|
|
7972
|
+
if (typeof repository.repo !== "string" || !GITHUB_REPOSITORY_PATTERN.test(repository.repo)) {
|
|
7973
|
+
throw new Error(`Invalid ${name}.githubRepositories[${index}].repo.`);
|
|
7974
|
+
}
|
|
7975
|
+
}
|
|
7976
|
+
}
|
|
7977
|
+
if (options.terminateOnClose !== undefined && typeof options.terminateOnClose !== "boolean") {
|
|
7978
|
+
throw new Error(`Invalid ${name}.terminateOnClose. Expected a boolean.`);
|
|
7979
|
+
}
|
|
7980
|
+
}
|
|
7981
|
+
|
|
7895
7982
|
// src/cloud-session.ts
|
|
7896
7983
|
var DEFAULT_CLOUD_API_BASE_URL3 = "https://api.letta.com";
|
|
7897
7984
|
var DEFAULT_TURN_TIMEOUT_MS = 120000;
|
|
7898
7985
|
var DEFAULT_PING_INTERVAL_MS = 30000;
|
|
7899
7986
|
var DEFAULT_SANDBOX_TTL_MINUTES = 5;
|
|
7900
|
-
var MIN_SANDBOX_TTL_MINUTES = 1;
|
|
7901
|
-
var MAX_SANDBOX_TTL_MINUTES = 60;
|
|
7902
7987
|
var DEFAULT_SANDBOX_READY_TIMEOUT_MS = 120000;
|
|
7903
7988
|
var DEFAULT_SANDBOX_READY_POLL_INTERVAL_MS = 1000;
|
|
7904
7989
|
var DEFAULT_REPOSITORY_ATTACH_TIMEOUT_MS = 1e4;
|
|
@@ -8009,34 +8094,13 @@ function assertOkResponse2(response, body, action, url) {
|
|
|
8009
8094
|
throw new Error(parts.join(" — "));
|
|
8010
8095
|
}
|
|
8011
8096
|
}
|
|
8012
|
-
function
|
|
8097
|
+
function validatePositiveInteger2(value, name) {
|
|
8013
8098
|
if (value !== undefined && (!Number.isInteger(value) || value <= 0)) {
|
|
8014
8099
|
throw new Error(`Invalid ${name}. Expected a positive integer.`);
|
|
8015
8100
|
}
|
|
8016
8101
|
}
|
|
8017
|
-
function validateIntegerRange(value, name, min, max) {
|
|
8018
|
-
if (value === undefined)
|
|
8019
|
-
return;
|
|
8020
|
-
if (!Number.isInteger(value) || value < min || value > max) {
|
|
8021
|
-
throw new Error(`Invalid ${name}. Expected an integer between ${min} and ${max}.`);
|
|
8022
|
-
}
|
|
8023
|
-
}
|
|
8024
|
-
function validateCloudSandboxOptions(options, name) {
|
|
8025
|
-
if (options === undefined)
|
|
8026
|
-
return;
|
|
8027
|
-
if (options === null || typeof options !== "object" || Array.isArray(options)) {
|
|
8028
|
-
throw new Error(`Invalid ${name}. Expected an object.`);
|
|
8029
|
-
}
|
|
8030
|
-
validateIntegerRange(options.ttlMinutes, `${name}.ttlMinutes`, MIN_SANDBOX_TTL_MINUTES, MAX_SANDBOX_TTL_MINUTES);
|
|
8031
|
-
validatePositiveInteger(options.readyTimeoutMs, `${name}.readyTimeoutMs`);
|
|
8032
|
-
validatePositiveInteger(options.readyPollIntervalMs, `${name}.readyPollIntervalMs`);
|
|
8033
|
-
validatePositiveInteger(options.refreshIntervalMs, `${name}.refreshIntervalMs`);
|
|
8034
|
-
if (options.terminateOnClose !== undefined && typeof options.terminateOnClose !== "boolean") {
|
|
8035
|
-
throw new Error(`Invalid ${name}.terminateOnClose. Expected a boolean.`);
|
|
8036
|
-
}
|
|
8037
|
-
}
|
|
8038
8102
|
function validateCloudClientOptions(options) {
|
|
8039
|
-
|
|
8103
|
+
validatePositiveInteger2(options.requestTimeoutMs, "requestTimeoutMs");
|
|
8040
8104
|
validateCloudSandboxOptions(options.sandbox, "sandbox");
|
|
8041
8105
|
if (options.environment !== undefined && options.sandbox !== undefined) {
|
|
8042
8106
|
throw new Error("Letta Cloud sessions cannot specify both environment and sandbox options.");
|
|
@@ -8639,10 +8703,15 @@ class CloudEnvironmentSession extends RemoteClientSessionCore {
|
|
|
8639
8703
|
async createManagedSandbox(agentId, conversationId) {
|
|
8640
8704
|
const fetchImpl = getFetch3(this.cloudOptions.fetch);
|
|
8641
8705
|
const baseUrl = normalizeCloudApiBaseUrl2(this.cloudOptions.apiBaseUrl);
|
|
8706
|
+
const sandboxOptions = this.resolvedSandboxOptions();
|
|
8707
|
+
const githubRepositories = sandboxOptions.githubRepositories;
|
|
8642
8708
|
const response = await fetchImpl(`${baseUrl}/v1/agents/${encodeURIComponent(agentId)}/sandboxes`, {
|
|
8643
8709
|
method: "POST",
|
|
8644
8710
|
headers: cloudHeaders2(this.cloudOptions),
|
|
8645
|
-
body: JSON.stringify(
|
|
8711
|
+
body: JSON.stringify({
|
|
8712
|
+
...conversationId ? { conversationId } : {},
|
|
8713
|
+
...githubRepositories && githubRepositories.length > 0 ? { githubRepositories } : {}
|
|
8714
|
+
})
|
|
8646
8715
|
});
|
|
8647
8716
|
const body = await parseJsonResponse3(response);
|
|
8648
8717
|
assertOkResponse2(response, body, "Cloud create managed sandbox", `${baseUrl}/v1/agents/${encodeURIComponent(agentId)}/sandboxes`);
|
|
@@ -8653,7 +8722,6 @@ class CloudEnvironmentSession extends RemoteClientSessionCore {
|
|
|
8653
8722
|
if (responseConversationId !== null && responseConversationId !== conversationId) {
|
|
8654
8723
|
throw new Error(`Cloud managed sandbox response conversation mismatch: expected ${conversationId ?? "none"}, got ${responseConversationId}.`);
|
|
8655
8724
|
}
|
|
8656
|
-
const sandboxOptions = this.resolvedSandboxOptions();
|
|
8657
8725
|
const ttlMinutes = sandboxOptions.ttlMinutes ?? DEFAULT_SANDBOX_TTL_MINUTES;
|
|
8658
8726
|
const readyTimeoutMs = sandboxOptions.readyTimeoutMs ?? DEFAULT_SANDBOX_READY_TIMEOUT_MS;
|
|
8659
8727
|
const readyPollIntervalMs = sandboxOptions.readyPollIntervalMs ?? DEFAULT_SANDBOX_READY_POLL_INTERVAL_MS;
|
|
@@ -9419,4 +9487,4 @@ export {
|
|
|
9419
9487
|
CloudManagedSandboxExpiredError
|
|
9420
9488
|
};
|
|
9421
9489
|
|
|
9422
|
-
//# debugId=
|
|
9490
|
+
//# debugId=0186899B952456A764756E2164756E21
|