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