@letta-ai/letta-agent-sdk 0.8.73 → 0.8.75
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/README.md +69 -0
- package/dist/app-server-session.d.ts +28 -7
- package/dist/app-server-session.d.ts.map +1 -1
- package/dist/client-base.d.ts +17 -0
- package/dist/client-base.d.ts.map +1 -1
- package/dist/client-entry.d.ts +1 -0
- package/dist/client-entry.d.ts.map +1 -1
- package/dist/client-entry.js +488 -100
- package/dist/client-entry.js.map +16 -13
- package/dist/client.d.ts +9 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/cloud-session.d.ts.map +1 -1
- package/dist/cloud-status-transport.d.ts.map +1 -1
- package/dist/external-tools.d.ts +11 -0
- package/dist/external-tools.d.ts.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +517 -126
- package/dist/index.js.map +18 -15
- package/dist/remote-client-session-core.d.ts.map +1 -1
- package/dist/remote-session-protocol.d.ts +3 -1
- package/dist/remote-session-protocol.d.ts.map +1 -1
- package/dist/remote-turn-coordinator.d.ts +14 -0
- package/dist/remote-turn-coordinator.d.ts.map +1 -1
- package/dist/runtime-tool-policy.d.ts +57 -0
- package/dist/runtime-tool-policy.d.ts.map +1 -0
- package/dist/structured-output-session.d.ts.map +1 -1
- package/dist/tool-registration.d.ts +67 -0
- package/dist/tool-registration.d.ts.map +1 -0
- package/dist/types.d.ts +30 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/app-server-session.ts +104 -60
- package/src/client-base.ts +57 -14
- package/src/client-entry.ts +1 -0
- package/src/client.ts +6 -2
- package/src/cloud-session.ts +12 -3
- package/src/cloud-status-transport.ts +4 -0
- package/src/external-tools.ts +69 -0
- package/src/index.ts +3 -0
- package/src/remote-client-session-core.ts +4 -4
- package/src/remote-session-protocol.ts +3 -0
- package/src/remote-turn-coordinator.ts +80 -15
- package/src/runtime-tool-policy.ts +114 -0
- package/src/structured-output-session.ts +2 -1
- package/src/tool-registration.ts +311 -0
- package/src/types.ts +39 -1
package/dist/client-entry.js
CHANGED
|
@@ -10143,14 +10143,6 @@ function buildCreatedAgentTags(options = {}) {
|
|
|
10143
10143
|
}
|
|
10144
10144
|
return Array.from(new Set(tags));
|
|
10145
10145
|
}
|
|
10146
|
-
var DEFAULT_SUMMARIZATION_MODEL = "letta/auto";
|
|
10147
|
-
var SYSTEM_REMINDER_TAG = "system-reminder";
|
|
10148
|
-
var SYSTEM_REMINDER_OPEN = `<${SYSTEM_REMINDER_TAG}>`;
|
|
10149
|
-
var SYSTEM_REMINDER_CLOSE = `</${SYSTEM_REMINDER_TAG}>`;
|
|
10150
|
-
var SYSTEM_ALERT_TAG = "system-alert";
|
|
10151
|
-
var SYSTEM_ALERT_OPEN = `<${SYSTEM_ALERT_TAG}>`;
|
|
10152
|
-
var SYSTEM_ALERT_CLOSE = `</${SYSTEM_ALERT_TAG}>`;
|
|
10153
|
-
var ELAPSED_DISPLAY_THRESHOLD_MS = 60 * 1000;
|
|
10154
10146
|
var READ_ONLY_BLOCK_LABELS = ["memory_filesystem"];
|
|
10155
10147
|
var human_default = `---
|
|
10156
10148
|
label: human
|
|
@@ -12190,7 +12182,6 @@ async function getDefaultMemoryBlocks() {
|
|
|
12190
12182
|
}
|
|
12191
12183
|
var models = [];
|
|
12192
12184
|
var BUILTIN_MODEL_ALIASES = new Map([
|
|
12193
|
-
["auto", "letta/auto"],
|
|
12194
12185
|
["auto-chat", "letta/auto-chat"],
|
|
12195
12186
|
["auto-fast", "letta/auto-fast"]
|
|
12196
12187
|
]);
|
|
@@ -12239,20 +12230,14 @@ function resolveModel(modelIdentifier) {
|
|
|
12239
12230
|
return builtinHandle;
|
|
12240
12231
|
return modelIdentifier.includes("/") ? modelIdentifier : null;
|
|
12241
12232
|
}
|
|
12233
|
+
var UNSELECTED_MODEL_HANDLE = "local/default";
|
|
12234
|
+
function isLettaHostedModelHandle(handle) {
|
|
12235
|
+
return handle.startsWith("letta/");
|
|
12236
|
+
}
|
|
12242
12237
|
function getDefaultModel() {
|
|
12243
|
-
|
|
12244
|
-
|
|
12245
|
-
|
|
12246
|
-
if (autoModel)
|
|
12247
|
-
return autoModel.handle;
|
|
12248
|
-
const defaultModel = models.find((model) => model.isDefault);
|
|
12249
|
-
if (defaultModel)
|
|
12250
|
-
return defaultModel.handle;
|
|
12251
|
-
const firstModel = models[0];
|
|
12252
|
-
if (!firstModel) {
|
|
12253
|
-
throw new Error("Model catalog is unavailable.");
|
|
12254
|
-
}
|
|
12255
|
-
return firstModel.handle;
|
|
12238
|
+
const candidates = models.filter((model) => !isLettaHostedModelHandle(model.handle));
|
|
12239
|
+
const defaultModel = candidates.find((model) => model.isDefault) ?? candidates[0];
|
|
12240
|
+
return defaultModel?.handle ?? UNSELECTED_MODEL_HANDLE;
|
|
12256
12241
|
}
|
|
12257
12242
|
var PERSONALITY_OPTIONS = [
|
|
12258
12243
|
{
|
|
@@ -12514,7 +12499,7 @@ async function buildCreateAgentRequest(options = {}) {
|
|
|
12514
12499
|
initial_message_sequence: [],
|
|
12515
12500
|
parallel_tool_calls: options.parallelToolCalls ?? true,
|
|
12516
12501
|
compaction_settings: {
|
|
12517
|
-
model: options.compactionModel ??
|
|
12502
|
+
model: options.compactionModel ?? modelHandle
|
|
12518
12503
|
},
|
|
12519
12504
|
...options.embedding !== undefined ? { embedding: options.embedding } : {},
|
|
12520
12505
|
...options.isSubagent ? { hidden: true } : options.hidden !== undefined ? { hidden: options.hidden } : {}
|
|
@@ -13128,7 +13113,7 @@ async function* streamStructuredTurns(config) {
|
|
|
13128
13113
|
return;
|
|
13129
13114
|
});
|
|
13130
13115
|
}
|
|
13131
|
-
if (msg.type !== "result") {
|
|
13116
|
+
if (msg.type !== "result" || msg.origin !== undefined) {
|
|
13132
13117
|
yield msg;
|
|
13133
13118
|
continue;
|
|
13134
13119
|
}
|
|
@@ -13245,9 +13230,115 @@ function expandMcpToolWildcards(allowedTools, mcpTools) {
|
|
|
13245
13230
|
return [...new Set(expanded)];
|
|
13246
13231
|
}
|
|
13247
13232
|
|
|
13233
|
+
// src/external-tools.ts
|
|
13234
|
+
function externalToolGroups(tools) {
|
|
13235
|
+
if (!tools || tools.length === 0)
|
|
13236
|
+
return;
|
|
13237
|
+
return [
|
|
13238
|
+
{
|
|
13239
|
+
tools: tools.map((tool) => ({
|
|
13240
|
+
name: tool.name,
|
|
13241
|
+
label: tool.label,
|
|
13242
|
+
description: tool.description,
|
|
13243
|
+
parameters: tool.parameters
|
|
13244
|
+
}))
|
|
13245
|
+
}
|
|
13246
|
+
];
|
|
13247
|
+
}
|
|
13248
|
+
var MAX_REMEMBERED_EXTERNAL_TOOL_CALLS = 200;
|
|
13249
|
+
function createExternalToolCallHandler(externalTools, fallbackRuntime) {
|
|
13250
|
+
const calls = new Map;
|
|
13251
|
+
const execute = async (request) => {
|
|
13252
|
+
const tool = externalTools.get(request.tool_name);
|
|
13253
|
+
if (!tool) {
|
|
13254
|
+
throw new Error(`Unknown external tool: ${request.tool_name}`);
|
|
13255
|
+
}
|
|
13256
|
+
const runtime = request.runtime ?? fallbackRuntime?.() ?? null;
|
|
13257
|
+
const context = runtime ? { toolCallId: request.tool_call_id, agentId: runtime.agent_id, conversationId: runtime.conversation_id } : undefined;
|
|
13258
|
+
const result = await tool.execute(request.tool_call_id, request.input, undefined, undefined, context);
|
|
13259
|
+
return {
|
|
13260
|
+
content: result.content.map((part) => ({
|
|
13261
|
+
type: part.type,
|
|
13262
|
+
...part.text !== undefined ? { text: part.text } : {},
|
|
13263
|
+
...part.data !== undefined ? { data: part.data } : {},
|
|
13264
|
+
...part.mimeType !== undefined ? { mimeType: part.mimeType } : {}
|
|
13265
|
+
})),
|
|
13266
|
+
...result.isError === true ? { is_error: true } : {}
|
|
13267
|
+
};
|
|
13268
|
+
};
|
|
13269
|
+
return (request) => {
|
|
13270
|
+
const requestId = request.request_id;
|
|
13271
|
+
if (typeof requestId !== "string" || requestId.length === 0) {
|
|
13272
|
+
return execute(request);
|
|
13273
|
+
}
|
|
13274
|
+
const existing = calls.get(requestId);
|
|
13275
|
+
if (existing)
|
|
13276
|
+
return existing;
|
|
13277
|
+
const call = execute(request);
|
|
13278
|
+
calls.set(requestId, call);
|
|
13279
|
+
while (calls.size > MAX_REMEMBERED_EXTERNAL_TOOL_CALLS) {
|
|
13280
|
+
const oldest = calls.keys().next().value;
|
|
13281
|
+
if (oldest === undefined)
|
|
13282
|
+
break;
|
|
13283
|
+
calls.delete(oldest);
|
|
13284
|
+
}
|
|
13285
|
+
return call;
|
|
13286
|
+
};
|
|
13287
|
+
}
|
|
13288
|
+
|
|
13289
|
+
// src/runtime-tool-policy.ts
|
|
13290
|
+
var RUNTIME_TOOL_POLICY_CAPABILITY = "runtime_tool_policy";
|
|
13291
|
+
var CAPABILITY_PROBE_TIMEOUT_MS = 5000;
|
|
13292
|
+
function clientToolsetPayload(toolset) {
|
|
13293
|
+
return {
|
|
13294
|
+
...toolset.base !== undefined ? { base: toolset.base } : {},
|
|
13295
|
+
...toolset.include !== undefined ? { include: [...new Set(toolset.include)] } : {}
|
|
13296
|
+
};
|
|
13297
|
+
}
|
|
13298
|
+
function runtimeToolPolicy(allowedTools, toolset) {
|
|
13299
|
+
return {
|
|
13300
|
+
...toolset !== undefined ? { toolset: clientToolsetPayload(toolset) } : {},
|
|
13301
|
+
...allowedTools !== undefined ? { allowlist: [...new Set(allowedTools)] } : {},
|
|
13302
|
+
exclude_interactive: true
|
|
13303
|
+
};
|
|
13304
|
+
}
|
|
13305
|
+
async function supportsRuntimeToolPolicy(client) {
|
|
13306
|
+
try {
|
|
13307
|
+
const info = await client.info({ timeoutMs: CAPABILITY_PROBE_TIMEOUT_MS });
|
|
13308
|
+
const capabilities = info.capabilities;
|
|
13309
|
+
return capabilities?.[RUNTIME_TOOL_POLICY_CAPABILITY] === true;
|
|
13310
|
+
} catch {
|
|
13311
|
+
return false;
|
|
13312
|
+
}
|
|
13313
|
+
}
|
|
13314
|
+
function mergeInheritedTools(inherited, own) {
|
|
13315
|
+
if (!inherited)
|
|
13316
|
+
return own;
|
|
13317
|
+
const tools = new Map(inherited.tools.map((tool) => [tool.name, tool]));
|
|
13318
|
+
for (const tool of own.tools)
|
|
13319
|
+
tools.set(tool.name, tool);
|
|
13320
|
+
const ownNames = new Set(own.tools.map((tool) => tool.name));
|
|
13321
|
+
const registered = inherited.allowedTools;
|
|
13322
|
+
const allowedTools = registered === undefined ? own.allowedTools : own.allowedTools === undefined ? [...new Set([...registered, ...ownNames])] : own.allowedTools.filter((name) => registered.includes(name) || ownNames.has(name));
|
|
13323
|
+
const toolset = own.toolset ?? inherited.toolset;
|
|
13324
|
+
return {
|
|
13325
|
+
tools: [...tools.values()],
|
|
13326
|
+
...allowedTools !== undefined ? { allowedTools } : {},
|
|
13327
|
+
...toolset !== undefined ? { toolset } : {}
|
|
13328
|
+
};
|
|
13329
|
+
}
|
|
13330
|
+
|
|
13248
13331
|
// src/remote-turn-coordinator.ts
|
|
13249
13332
|
var MAX_RECENTLY_SETTLED_RUN_IDS = 256;
|
|
13250
13333
|
var TRAILING_USAGE_GRACE_MS = 100;
|
|
13334
|
+
var TURN_ORIGINS = new Set([
|
|
13335
|
+
"task_notification",
|
|
13336
|
+
"cron",
|
|
13337
|
+
"subagent",
|
|
13338
|
+
"system",
|
|
13339
|
+
"channel",
|
|
13340
|
+
"user"
|
|
13341
|
+
]);
|
|
13251
13342
|
|
|
13252
13343
|
class RemoteTurnCoordinator {
|
|
13253
13344
|
label;
|
|
@@ -13259,6 +13350,8 @@ class RemoteTurnCoordinator {
|
|
|
13259
13350
|
activeTurn = null;
|
|
13260
13351
|
pendingTurns = [];
|
|
13261
13352
|
settledRunIds = new Set;
|
|
13353
|
+
queueOrigins = new Map;
|
|
13354
|
+
nextServerOrigin = "unknown";
|
|
13262
13355
|
nextTurnId = 0;
|
|
13263
13356
|
messageCounter = 0;
|
|
13264
13357
|
clientMessageCounter = 0;
|
|
@@ -13274,16 +13367,27 @@ class RemoteTurnCoordinator {
|
|
|
13274
13367
|
return this._activeTurnStartedAt;
|
|
13275
13368
|
}
|
|
13276
13369
|
hasInFlightTurn() {
|
|
13277
|
-
return this.activeTurn !== null || this.pendingTurns.length > 0;
|
|
13370
|
+
return this.activeTurn !== null && this.activeTurn.origin === undefined || this.pendingTurns.length > 0;
|
|
13278
13371
|
}
|
|
13279
13372
|
trackSentTurn(runtime, callerOtid, outputFormat) {
|
|
13280
13373
|
const otid = normalizeCallerOtid(callerOtid);
|
|
13281
|
-
const turn = {
|
|
13282
|
-
id: ++this.nextTurnId,
|
|
13283
|
-
runtime,
|
|
13374
|
+
const turn = this.newTracker(runtime, {
|
|
13284
13375
|
...otid !== undefined ? { otid } : {},
|
|
13285
13376
|
...outputFormat !== undefined ? { outputFormat } : {},
|
|
13286
|
-
clientMessageId: otid ?? `sdk-message-${Date.now()}-${++this.clientMessageCounter}
|
|
13377
|
+
clientMessageId: otid ?? `sdk-message-${Date.now()}-${++this.clientMessageCounter}`
|
|
13378
|
+
});
|
|
13379
|
+
if (this.activeTurn) {
|
|
13380
|
+
this.pendingTurns.push(turn);
|
|
13381
|
+
} else {
|
|
13382
|
+
this.activateTurn(turn);
|
|
13383
|
+
}
|
|
13384
|
+
return turn;
|
|
13385
|
+
}
|
|
13386
|
+
newTracker(runtime, identity) {
|
|
13387
|
+
return {
|
|
13388
|
+
id: ++this.nextTurnId,
|
|
13389
|
+
runtime,
|
|
13390
|
+
...identity,
|
|
13287
13391
|
queuedAt: Date.now(),
|
|
13288
13392
|
startedAt: 0,
|
|
13289
13393
|
assistantText: "",
|
|
@@ -13300,13 +13404,32 @@ class RemoteTurnCoordinator {
|
|
|
13300
13404
|
abortRequested: false,
|
|
13301
13405
|
timeout: null
|
|
13302
13406
|
};
|
|
13303
|
-
|
|
13304
|
-
|
|
13305
|
-
|
|
13306
|
-
|
|
13307
|
-
|
|
13407
|
+
}
|
|
13408
|
+
startServerTurn(runtime) {
|
|
13409
|
+
const turn = this.newTracker(runtime, { clientMessageId: "", origin: this.nextServerOrigin });
|
|
13410
|
+
this.nextServerOrigin = "unknown";
|
|
13411
|
+
this.activateTurn(turn);
|
|
13308
13412
|
return turn;
|
|
13309
13413
|
}
|
|
13414
|
+
observeQueueOrigins(message, queue) {
|
|
13415
|
+
for (const item of queue) {
|
|
13416
|
+
if (!item.clientMessageId)
|
|
13417
|
+
continue;
|
|
13418
|
+
this.queueOrigins.set(item.clientMessageId, TURN_ORIGINS.has(item.source) ? item.source : "unknown");
|
|
13419
|
+
if (this.queueOrigins.size > MAX_RECENTLY_SETTLED_RUN_IDS) {
|
|
13420
|
+
this.queueOrigins.delete(this.queueOrigins.keys().next().value);
|
|
13421
|
+
}
|
|
13422
|
+
}
|
|
13423
|
+
for (const removed of Array.isArray(message.removed) ? message.removed : []) {
|
|
13424
|
+
const { client_message_id: id, disposition, source } = removed;
|
|
13425
|
+
if (disposition !== "dequeued" || typeof id !== "string")
|
|
13426
|
+
continue;
|
|
13427
|
+
const own = [this.activeTurn, ...this.pendingTurns].some((turn) => turn?.clientMessageId === id);
|
|
13428
|
+
if (own)
|
|
13429
|
+
continue;
|
|
13430
|
+
this.nextServerOrigin = typeof source === "string" && TURN_ORIGINS.has(source) ? source : this.queueOrigins.get(id) ?? "unknown";
|
|
13431
|
+
}
|
|
13432
|
+
}
|
|
13310
13433
|
removeTrackedTurn(turn) {
|
|
13311
13434
|
if (turn.timeout)
|
|
13312
13435
|
clearTimeout(turn.timeout);
|
|
@@ -13320,6 +13443,12 @@ class RemoteTurnCoordinator {
|
|
|
13320
13443
|
if (index !== -1)
|
|
13321
13444
|
this.pendingTurns.splice(index, 1);
|
|
13322
13445
|
}
|
|
13446
|
+
failServerTurn(detail) {
|
|
13447
|
+
const active = this.activeTurn;
|
|
13448
|
+
if (active?.origin === undefined)
|
|
13449
|
+
return;
|
|
13450
|
+
this.failTurn(active, detail, { errorCode: "stream_closed", recoverable: true });
|
|
13451
|
+
}
|
|
13323
13452
|
markAbortRequested() {
|
|
13324
13453
|
if (this.activeTurn)
|
|
13325
13454
|
this.activeTurn.abortRequested = true;
|
|
@@ -13339,6 +13468,7 @@ class RemoteTurnCoordinator {
|
|
|
13339
13468
|
type: "queue_update",
|
|
13340
13469
|
queue: queueItems(message)
|
|
13341
13470
|
};
|
|
13471
|
+
this.observeQueueOrigins(message, sdkMessage2.queue);
|
|
13342
13472
|
this.enqueue(sdkMessage2);
|
|
13343
13473
|
return;
|
|
13344
13474
|
}
|
|
@@ -13381,9 +13511,9 @@ class RemoteTurnCoordinator {
|
|
|
13381
13511
|
if (!delta)
|
|
13382
13512
|
return;
|
|
13383
13513
|
const messageType = streamDeltaMessageType(delta);
|
|
13384
|
-
const active = messageType === "usage_statistics" || messageType === "stop_reason" ? this.activeTurn : this.activateNextTurnFromProtocol();
|
|
13385
13514
|
const runId = streamDeltaRunId(delta);
|
|
13386
13515
|
const isTurnEvidence = messageType === "assistant_message" || messageType === "reasoning_message" || messageType === "tool_call_message" || messageType === "approval_request_message" || messageType === "tool_return_message" || messageType === "error_message" || messageType === "loop_error" || runId !== undefined && (messageType === "usage_statistics" || messageType === "stop_reason");
|
|
13516
|
+
const active = messageType === "usage_statistics" || messageType === "stop_reason" ? this.activeTurn : this.activateNextTurnFromProtocol() ?? (isTurnEvidence && runId !== undefined ? this.startServerTurn(runtime) : null);
|
|
13387
13517
|
if (active && isTurnEvidence) {
|
|
13388
13518
|
active.observedTurnEvidence = true;
|
|
13389
13519
|
if (runId)
|
|
@@ -13841,7 +13971,8 @@ class RemoteTurnCoordinator {
|
|
|
13841
13971
|
stopReason,
|
|
13842
13972
|
durationMs: Date.now() - (tracker?.startedAt || this._activeTurnStartedAt),
|
|
13843
13973
|
conversationId: turn.runtime.conversation_id,
|
|
13844
|
-
runIds: turn.runIds.length > 0 ? turn.runIds : undefined
|
|
13974
|
+
runIds: turn.runIds.length > 0 ? turn.runIds : undefined,
|
|
13975
|
+
...tracker?.origin !== undefined ? { origin: tracker.origin } : {}
|
|
13845
13976
|
};
|
|
13846
13977
|
}
|
|
13847
13978
|
enqueue(message) {
|
|
@@ -14083,9 +14214,8 @@ class RemoteClientSessionCore {
|
|
|
14083
14214
|
}
|
|
14084
14215
|
await this.send(message);
|
|
14085
14216
|
for await (const msg of this.stream()) {
|
|
14086
|
-
if (msg.type === "result")
|
|
14217
|
+
if (msg.type === "result" && msg.origin === undefined)
|
|
14087
14218
|
return msg;
|
|
14088
|
-
}
|
|
14089
14219
|
}
|
|
14090
14220
|
return {
|
|
14091
14221
|
type: "result",
|
|
@@ -14121,7 +14251,7 @@ class RemoteClientSessionCore {
|
|
|
14121
14251
|
if (!msg)
|
|
14122
14252
|
break;
|
|
14123
14253
|
yield msg;
|
|
14124
|
-
if (msg.type === "result")
|
|
14254
|
+
if (msg.type === "result" && msg.origin === undefined)
|
|
14125
14255
|
break;
|
|
14126
14256
|
}
|
|
14127
14257
|
}
|
|
@@ -14431,6 +14561,7 @@ class RemoteClientSessionCore {
|
|
|
14431
14561
|
this.controller?.close();
|
|
14432
14562
|
this.controller = null;
|
|
14433
14563
|
const error = new Error(`${this.label} connection closed before send`);
|
|
14564
|
+
this.turns.failServerTurn(error.message);
|
|
14434
14565
|
for (const cancel of [...this.deviceStatusRefreshCancels])
|
|
14435
14566
|
cancel(error);
|
|
14436
14567
|
this.deviceStatusRefreshCancels.clear();
|
|
@@ -14571,38 +14702,6 @@ function agentToolNames(agent) {
|
|
|
14571
14702
|
return typeof name === "string" && name.length > 0 ? [name] : [];
|
|
14572
14703
|
});
|
|
14573
14704
|
}
|
|
14574
|
-
function externalToolGroups(tools) {
|
|
14575
|
-
if (!tools || tools.length === 0)
|
|
14576
|
-
return;
|
|
14577
|
-
return [
|
|
14578
|
-
{
|
|
14579
|
-
tools: tools.map((tool) => ({
|
|
14580
|
-
name: tool.name,
|
|
14581
|
-
label: tool.label,
|
|
14582
|
-
description: tool.description,
|
|
14583
|
-
parameters: tool.parameters
|
|
14584
|
-
}))
|
|
14585
|
-
}
|
|
14586
|
-
];
|
|
14587
|
-
}
|
|
14588
|
-
function createExternalToolCallHandler(externalTools) {
|
|
14589
|
-
return async (request) => {
|
|
14590
|
-
const tool = externalTools.get(request.tool_name);
|
|
14591
|
-
if (!tool) {
|
|
14592
|
-
throw new Error(`Unknown external tool: ${request.tool_name}`);
|
|
14593
|
-
}
|
|
14594
|
-
const result = await tool.execute(request.tool_call_id, request.input);
|
|
14595
|
-
return {
|
|
14596
|
-
content: result.content.map((part) => ({
|
|
14597
|
-
type: part.type,
|
|
14598
|
-
...part.text !== undefined ? { text: part.text } : {},
|
|
14599
|
-
...part.data !== undefined ? { data: part.data } : {},
|
|
14600
|
-
...part.mimeType !== undefined ? { mimeType: part.mimeType } : {}
|
|
14601
|
-
})),
|
|
14602
|
-
...result.isError === true ? { is_error: true } : {}
|
|
14603
|
-
};
|
|
14604
|
-
};
|
|
14605
|
-
}
|
|
14606
14705
|
async function resolveAppServerToolApproval(options, toolName, toolInput, context) {
|
|
14607
14706
|
if ("outputFormat" in options && options.outputFormat !== undefined && toolName === STRUCTURED_OUTPUT_TOOL) {
|
|
14608
14707
|
return { behavior: "allow", updatedInput: null, updatedPermissions: [] };
|
|
@@ -14779,11 +14878,26 @@ class AppServerRuntimeController {
|
|
|
14779
14878
|
options;
|
|
14780
14879
|
clientToolAllowlist;
|
|
14781
14880
|
clientToolset;
|
|
14782
|
-
|
|
14881
|
+
toolPolicyApplied;
|
|
14882
|
+
constructor(client, options, clientToolAllowlist, clientToolset, toolPolicyApplied = false) {
|
|
14783
14883
|
this.client = client;
|
|
14784
14884
|
this.options = options;
|
|
14785
14885
|
this.clientToolAllowlist = clientToolAllowlist;
|
|
14786
14886
|
this.clientToolset = clientToolset;
|
|
14887
|
+
this.toolPolicyApplied = toolPolicyApplied;
|
|
14888
|
+
}
|
|
14889
|
+
async updateToolSelection(runtime, selection) {
|
|
14890
|
+
this.clientToolAllowlist = selection.allowedTools;
|
|
14891
|
+
this.clientToolset = selection.toolset;
|
|
14892
|
+
const response = await this.client.runtimeExternalToolsUpdate({
|
|
14893
|
+
updates: [{
|
|
14894
|
+
runtimes: [runtime],
|
|
14895
|
+
external_tools: externalToolGroups(selection.tools) ?? [],
|
|
14896
|
+
...this.toolPolicyApplied ? { tool_policy: runtimeToolPolicy(selection.allowedTools, selection.toolset) } : {}
|
|
14897
|
+
}]
|
|
14898
|
+
});
|
|
14899
|
+
if (!response.success)
|
|
14900
|
+
throw new Error(response.error ?? "Failed to update session tools");
|
|
14787
14901
|
}
|
|
14788
14902
|
onMessage(handler) {
|
|
14789
14903
|
return this.client.onMessage((message, channel) => {
|
|
@@ -14805,14 +14919,11 @@ class AppServerRuntimeController {
|
|
|
14805
14919
|
}
|
|
14806
14920
|
]
|
|
14807
14921
|
};
|
|
14808
|
-
if (this.clientToolAllowlist !== undefined) {
|
|
14922
|
+
if (!this.toolPolicyApplied && this.clientToolAllowlist !== undefined) {
|
|
14809
14923
|
payload.client_tool_allowlist = [...new Set(this.clientToolAllowlist)];
|
|
14810
14924
|
}
|
|
14811
|
-
if (this.clientToolset !== undefined) {
|
|
14812
|
-
payload.client_toolset =
|
|
14813
|
-
...this.clientToolset.base !== undefined ? { base: this.clientToolset.base } : {},
|
|
14814
|
-
...this.clientToolset.include !== undefined ? { include: [...new Set(this.clientToolset.include)] } : {}
|
|
14815
|
-
};
|
|
14925
|
+
if (!this.toolPolicyApplied && this.clientToolset !== undefined) {
|
|
14926
|
+
payload.client_toolset = clientToolsetPayload(this.clientToolset);
|
|
14816
14927
|
}
|
|
14817
14928
|
if (options.outputFormat !== undefined) {
|
|
14818
14929
|
payload.response_format = {
|
|
@@ -14824,7 +14935,8 @@ class AppServerRuntimeController {
|
|
|
14824
14935
|
}
|
|
14825
14936
|
};
|
|
14826
14937
|
}
|
|
14827
|
-
|
|
14938
|
+
if (!this.toolPolicyApplied)
|
|
14939
|
+
payload.exclude_interactive_tools = true;
|
|
14828
14940
|
this.client.input({
|
|
14829
14941
|
runtime,
|
|
14830
14942
|
payload
|
|
@@ -14916,6 +15028,9 @@ class AppServerSession extends RemoteClientSessionCore {
|
|
|
14916
15028
|
mcpCleanup = Promise.resolve();
|
|
14917
15029
|
removeExternalToolHandler = null;
|
|
14918
15030
|
removeControlRequestHandler = null;
|
|
15031
|
+
inheritedTools = null;
|
|
15032
|
+
removeInheritedListener = null;
|
|
15033
|
+
ownSelection = null;
|
|
14919
15034
|
constructor(remoteOptions, mode) {
|
|
14920
15035
|
super(mode, {
|
|
14921
15036
|
label: "app-server",
|
|
@@ -14927,6 +15042,27 @@ class AppServerSession extends RemoteClientSessionCore {
|
|
|
14927
15042
|
this.externalTools.set(tool.name, tool);
|
|
14928
15043
|
}
|
|
14929
15044
|
}
|
|
15045
|
+
inheritTools(source) {
|
|
15046
|
+
this.inheritedTools = source;
|
|
15047
|
+
this.removeInheritedListener = source.onChange(() => {
|
|
15048
|
+
this.refreshInheritedTools().catch(() => {
|
|
15049
|
+
return;
|
|
15050
|
+
});
|
|
15051
|
+
});
|
|
15052
|
+
}
|
|
15053
|
+
applyToolSelection() {
|
|
15054
|
+
const selection = mergeInheritedTools(this.inheritedTools?.current() ?? null, this.ownSelection ?? { tools: [] });
|
|
15055
|
+
this.externalTools.clear();
|
|
15056
|
+
for (const tool of selection.tools)
|
|
15057
|
+
this.externalTools.set(tool.name, tool);
|
|
15058
|
+
return selection;
|
|
15059
|
+
}
|
|
15060
|
+
async refreshInheritedTools() {
|
|
15061
|
+
if (!this.ownSelection || !this.runtime || !(this.controller instanceof AppServerRuntimeController))
|
|
15062
|
+
return;
|
|
15063
|
+
const selection = this.applyToolSelection();
|
|
15064
|
+
await this.controller.updateToolSelection(this.runtime, selection);
|
|
15065
|
+
}
|
|
14930
15066
|
shouldEnableMemfs(options) {
|
|
14931
15067
|
if (this.mode.kind !== "create-agent")
|
|
14932
15068
|
return false;
|
|
@@ -14942,6 +15078,9 @@ class AppServerSession extends RemoteClientSessionCore {
|
|
|
14942
15078
|
...this.remoteOptions.requestTimeoutMs !== undefined ? { requestTimeoutMs: this.remoteOptions.requestTimeoutMs } : {}
|
|
14943
15079
|
}));
|
|
14944
15080
|
const options = this.currentOptions();
|
|
15081
|
+
this.externalTools.clear();
|
|
15082
|
+
for (const tool of this.mode.options.tools ?? [])
|
|
15083
|
+
this.externalTools.set(tool.name, tool);
|
|
14945
15084
|
this.mcpBridge = await connectMcpServers("mcpServers" in options ? options.mcpServers : undefined, {
|
|
14946
15085
|
cwd: options.cwd,
|
|
14947
15086
|
reservedToolNames: this.externalTools.keys()
|
|
@@ -14949,13 +15088,22 @@ class AppServerSession extends RemoteClientSessionCore {
|
|
|
14949
15088
|
for (const tool of this.mcpBridge.tools) {
|
|
14950
15089
|
this.externalTools.set(tool.name, tool);
|
|
14951
15090
|
}
|
|
15091
|
+
const mcpToolNames = this.mcpBridge.tools.map((tool) => tool.name);
|
|
15092
|
+
const ownToolset = "toolset" in options ? options.toolset : undefined;
|
|
15093
|
+
const ownAllowedTools = expandMcpToolWildcards(options.allowedTools, mcpToolNames);
|
|
15094
|
+
this.ownSelection = {
|
|
15095
|
+
tools: [...this.externalTools.values()],
|
|
15096
|
+
...ownAllowedTools !== undefined ? { allowedTools: ownAllowedTools } : {},
|
|
15097
|
+
...ownToolset !== undefined ? { toolset: ownToolset } : {}
|
|
15098
|
+
};
|
|
15099
|
+
const { allowedTools, toolset: clientToolset } = this.applyToolSelection();
|
|
14952
15100
|
this.removeControlRequestHandler = registerAppServerControlRequestHandler({
|
|
14953
15101
|
client,
|
|
14954
15102
|
getRuntime: () => this.runtime,
|
|
14955
15103
|
getOptions: () => this.currentOptions()
|
|
14956
15104
|
});
|
|
14957
|
-
if (this.externalTools.size > 0) {
|
|
14958
|
-
this.removeExternalToolHandler = client.onExternalToolCall(createExternalToolCallHandler(this.externalTools));
|
|
15105
|
+
if (this.externalTools.size > 0 || this.inheritedTools) {
|
|
15106
|
+
this.removeExternalToolHandler = client.onExternalToolCall(createExternalToolCallHandler(this.externalTools, () => this.runtime));
|
|
14959
15107
|
}
|
|
14960
15108
|
try {
|
|
14961
15109
|
await client.connect();
|
|
@@ -14966,19 +15114,17 @@ class AppServerSession extends RemoteClientSessionCore {
|
|
|
14966
15114
|
throw new Error(`Local query() requires a Letta Code app-server with local conversation support (connected version: ${info.letta_code_version}). Upgrade @letta-ai/letta-code.`);
|
|
14967
15115
|
}
|
|
14968
15116
|
}
|
|
14969
|
-
const response = await this.startRuntime(client);
|
|
15117
|
+
const response = await this.startRuntime(client, runtimeToolPolicy(allowedTools, clientToolset));
|
|
14970
15118
|
if (!response.success || !response.runtime) {
|
|
14971
15119
|
throw new Error(response.error ?? "Failed to start app-server runtime");
|
|
14972
15120
|
}
|
|
14973
15121
|
this.watchTransportDisconnect(client);
|
|
15122
|
+
const toolPolicyApplied = await supportsRuntimeToolPolicy(client);
|
|
14974
15123
|
const tools = agentToolNames(response.agent);
|
|
14975
15124
|
const skillSources = options.skillSources;
|
|
14976
|
-
const clientToolset = "toolset" in options ? options.toolset : undefined;
|
|
14977
|
-
const mcpToolNames = this.mcpBridge?.tools.map((tool) => tool.name) ?? [];
|
|
14978
|
-
const allowedTools = expandMcpToolWildcards(options.allowedTools, mcpToolNames);
|
|
14979
15125
|
const availableTools = tools === undefined && mcpToolNames.length === 0 ? undefined : [...tools ?? [], ...mcpToolNames];
|
|
14980
15126
|
return {
|
|
14981
|
-
controller: new AppServerRuntimeController(client, this.remoteOptions, allowedTools, clientToolset),
|
|
15127
|
+
controller: new AppServerRuntimeController(client, this.remoteOptions, allowedTools, clientToolset, toolPolicyApplied),
|
|
14982
15128
|
runtime: response.runtime,
|
|
14983
15129
|
model: typeof response.agent?.model === "string" ? response.agent.model : typeof response.conversation?.model === "string" ? response.conversation.model : "",
|
|
14984
15130
|
modelSettings: objectRecord(response.agent?.model_settings) ?? objectRecord(response.conversation?.model_settings) ?? null,
|
|
@@ -14996,6 +15142,10 @@ class AppServerSession extends RemoteClientSessionCore {
|
|
|
14996
15142
|
}
|
|
14997
15143
|
}
|
|
14998
15144
|
onCoreClose() {
|
|
15145
|
+
if (this.closed) {
|
|
15146
|
+
this.removeInheritedListener?.();
|
|
15147
|
+
this.removeInheritedListener = null;
|
|
15148
|
+
}
|
|
14999
15149
|
this.removeExternalToolHandler?.();
|
|
15000
15150
|
this.removeExternalToolHandler = null;
|
|
15001
15151
|
this.removeControlRequestHandler?.();
|
|
@@ -15026,11 +15176,11 @@ class AppServerSession extends RemoteClientSessionCore {
|
|
|
15026
15176
|
this.ownedConnection = connection;
|
|
15027
15177
|
return connection.url;
|
|
15028
15178
|
}
|
|
15029
|
-
async startRuntime(client) {
|
|
15030
|
-
const command = await this.buildRuntimeStartCommand(client);
|
|
15179
|
+
async startRuntime(client, toolPolicy) {
|
|
15180
|
+
const command = await this.buildRuntimeStartCommand(client, toolPolicy);
|
|
15031
15181
|
return client.runtimeStart(command);
|
|
15032
15182
|
}
|
|
15033
|
-
async buildRuntimeStartCommand(client) {
|
|
15183
|
+
async buildRuntimeStartCommand(client, toolPolicy) {
|
|
15034
15184
|
const options = this.mode.options;
|
|
15035
15185
|
const command = {
|
|
15036
15186
|
client_info: {
|
|
@@ -15061,6 +15211,7 @@ class AppServerSession extends RemoteClientSessionCore {
|
|
|
15061
15211
|
const groups = externalToolGroups([...this.externalTools.values()]);
|
|
15062
15212
|
if (groups)
|
|
15063
15213
|
command.external_tools = groups;
|
|
15214
|
+
command.tool_policy = toolPolicy;
|
|
15064
15215
|
if (this.mode.kind === "create-agent") {
|
|
15065
15216
|
command.create_agent = {
|
|
15066
15217
|
body: await createAgentBody(this.mode.options),
|
|
@@ -15543,6 +15694,9 @@ class CloudStatusTransport {
|
|
|
15543
15694
|
if (channel === "control" && message.type === "stream_delta") {
|
|
15544
15695
|
return false;
|
|
15545
15696
|
}
|
|
15697
|
+
if (channel === "stream" && message.type === "external_tool_call_request") {
|
|
15698
|
+
return false;
|
|
15699
|
+
}
|
|
15546
15700
|
if (this.isDuplicate(message))
|
|
15547
15701
|
return false;
|
|
15548
15702
|
this.trackEventSequence(message);
|
|
@@ -16039,7 +16193,7 @@ class CloudEnvironmentSession extends RemoteClientSessionCore {
|
|
|
16039
16193
|
getOptions: () => this.currentOptions()
|
|
16040
16194
|
});
|
|
16041
16195
|
if (this.externalTools.size > 0) {
|
|
16042
|
-
this.removeExternalToolHandler = client.onExternalToolCall(createExternalToolCallHandler(this.externalTools));
|
|
16196
|
+
this.removeExternalToolHandler = client.onExternalToolCall(createExternalToolCallHandler(this.externalTools, () => this.runtime));
|
|
16043
16197
|
}
|
|
16044
16198
|
}
|
|
16045
16199
|
cleanupTransportHandlers() {
|
|
@@ -16097,6 +16251,7 @@ class CloudEnvironmentSession extends RemoteClientSessionCore {
|
|
|
16097
16251
|
const groups = externalToolGroups([...this.externalTools.values()]);
|
|
16098
16252
|
if (groups)
|
|
16099
16253
|
command.external_tools = groups;
|
|
16254
|
+
command.tool_policy = runtimeToolPolicy(expandMcpToolWildcards(options.allowedTools, this.mcpBridge?.tools.map((tool) => tool.name) ?? []), "toolset" in options ? options.toolset : undefined);
|
|
16100
16255
|
return await client.runtimeStart(command);
|
|
16101
16256
|
}
|
|
16102
16257
|
async afterRuntimeInitialized() {
|
|
@@ -16662,6 +16817,221 @@ function createQuery(createSession, params) {
|
|
|
16662
16817
|
});
|
|
16663
16818
|
}
|
|
16664
16819
|
|
|
16820
|
+
// src/tool-registration.ts
|
|
16821
|
+
var RECONNECT_DELAYS_MS = [250, 1000, 5000, 15000];
|
|
16822
|
+
var runtimeKey = (runtime) => `${runtime.agent_id}:${runtime.conversation_id}`;
|
|
16823
|
+
|
|
16824
|
+
class ToolRegistry {
|
|
16825
|
+
options;
|
|
16826
|
+
connection = null;
|
|
16827
|
+
entries = new Set;
|
|
16828
|
+
listeners = new Set;
|
|
16829
|
+
reconnectAttempt = 0;
|
|
16830
|
+
reconnectTimer = null;
|
|
16831
|
+
closed = false;
|
|
16832
|
+
constructor(options) {
|
|
16833
|
+
this.options = options;
|
|
16834
|
+
}
|
|
16835
|
+
async register(options) {
|
|
16836
|
+
if (this.closed)
|
|
16837
|
+
throw new Error("LettaAgentClient is closed");
|
|
16838
|
+
const entry = { options, runtimes: [], call: createExternalToolCallHandler(new Map) };
|
|
16839
|
+
await this.apply(entry, options);
|
|
16840
|
+
const registry = this;
|
|
16841
|
+
return {
|
|
16842
|
+
get conversations() {
|
|
16843
|
+
return [...entry.options.conversations];
|
|
16844
|
+
},
|
|
16845
|
+
update: async (next) => {
|
|
16846
|
+
if (!registry.entries.has(entry))
|
|
16847
|
+
throw new Error("This tool registration is closed.");
|
|
16848
|
+
await registry.apply(entry, { ...entry.options, ...next });
|
|
16849
|
+
},
|
|
16850
|
+
close: () => registry.remove(entry),
|
|
16851
|
+
[Symbol.asyncDispose]: () => registry.remove(entry)
|
|
16852
|
+
};
|
|
16853
|
+
}
|
|
16854
|
+
close() {
|
|
16855
|
+
this.closed = true;
|
|
16856
|
+
this.entries.clear();
|
|
16857
|
+
this.disconnect();
|
|
16858
|
+
this.notify();
|
|
16859
|
+
}
|
|
16860
|
+
inheritFor(id, own) {
|
|
16861
|
+
const covering = this.covering(id);
|
|
16862
|
+
if (!covering)
|
|
16863
|
+
return null;
|
|
16864
|
+
const registered = new Set(covering.options.tools.map((tool) => tool.name));
|
|
16865
|
+
const clash = own.tools?.find((tool) => registered.has(tool.name));
|
|
16866
|
+
if (clash) {
|
|
16867
|
+
throw new Error(`Tool ${clash.name} is already registered for ${id} with registerTools(); don't pass it to the session again.`);
|
|
16868
|
+
}
|
|
16869
|
+
if (own.toolset !== undefined && covering.options.toolset !== undefined && JSON.stringify(own.toolset) !== JSON.stringify(covering.options.toolset)) {
|
|
16870
|
+
throw new Error(`${id} already has a toolset from registerTools(); narrow it with the session's allowedTools instead.`);
|
|
16871
|
+
}
|
|
16872
|
+
return {
|
|
16873
|
+
current: () => this.covering(id)?.options ?? null,
|
|
16874
|
+
onChange: (listener) => {
|
|
16875
|
+
this.listeners.add(listener);
|
|
16876
|
+
return () => this.listeners.delete(listener);
|
|
16877
|
+
}
|
|
16878
|
+
};
|
|
16879
|
+
}
|
|
16880
|
+
covering(id) {
|
|
16881
|
+
return [...this.entries].find((entry) => entry.options.conversations.includes(id) || entry.runtimes.some((runtime) => runtime.conversation_id === id || runtime.conversation_id === "default" && runtime.agent_id === id));
|
|
16882
|
+
}
|
|
16883
|
+
notify() {
|
|
16884
|
+
for (const listener of [...this.listeners])
|
|
16885
|
+
listener();
|
|
16886
|
+
}
|
|
16887
|
+
async apply(entry, options) {
|
|
16888
|
+
if (!Array.isArray(options.conversations) || options.conversations.length === 0) {
|
|
16889
|
+
throw new Error("registerTools() requires at least one conversation.");
|
|
16890
|
+
}
|
|
16891
|
+
if (!Array.isArray(options.tools))
|
|
16892
|
+
throw new Error("registerTools() requires a tools array.");
|
|
16893
|
+
const connection = await this.connect();
|
|
16894
|
+
if (!connection.toolPolicy && (options.allowedTools || options.toolset)) {
|
|
16895
|
+
throw new Error("registerTools() allowedTools and toolset require a Letta Code app server that supports runtime tool policies. Upgrade @letta-ai/letta-code.");
|
|
16896
|
+
}
|
|
16897
|
+
const runtimes = await Promise.all(options.conversations.map((id) => resolveRuntime(connection.client, id)));
|
|
16898
|
+
const taken = new Set([...this.entries].filter((other) => other !== entry).flatMap((other) => other.runtimes.map(runtimeKey)));
|
|
16899
|
+
const overlap = options.conversations.find((_, index) => taken.has(runtimeKey(runtimes[index])));
|
|
16900
|
+
if (overlap)
|
|
16901
|
+
throw new Error(`Tools are already registered for ${overlap}.`);
|
|
16902
|
+
const keys = new Set(runtimes.map(runtimeKey));
|
|
16903
|
+
const dropped = entry.runtimes.filter((runtime) => !keys.has(runtimeKey(runtime)));
|
|
16904
|
+
const previous = { ...entry };
|
|
16905
|
+
const isNew = !this.entries.has(entry);
|
|
16906
|
+
entry.options = options;
|
|
16907
|
+
entry.runtimes = runtimes;
|
|
16908
|
+
entry.call = createExternalToolCallHandler(new Map(options.tools.map((tool) => [tool.name, tool])));
|
|
16909
|
+
this.entries.add(entry);
|
|
16910
|
+
try {
|
|
16911
|
+
await publish(connection.client, [
|
|
16912
|
+
group(runtimes, options, connection.toolPolicy),
|
|
16913
|
+
...dropped.length > 0 ? [{ runtimes: dropped, external_tools: [] }] : []
|
|
16914
|
+
]);
|
|
16915
|
+
} catch (error) {
|
|
16916
|
+
Object.assign(entry, previous);
|
|
16917
|
+
if (isNew)
|
|
16918
|
+
this.entries.delete(entry);
|
|
16919
|
+
throw error;
|
|
16920
|
+
}
|
|
16921
|
+
this.notify();
|
|
16922
|
+
}
|
|
16923
|
+
async remove(entry) {
|
|
16924
|
+
if (!this.entries.delete(entry))
|
|
16925
|
+
return;
|
|
16926
|
+
this.notify();
|
|
16927
|
+
if (this.entries.size === 0) {
|
|
16928
|
+
this.disconnect();
|
|
16929
|
+
return;
|
|
16930
|
+
}
|
|
16931
|
+
const connection = await this.connect();
|
|
16932
|
+
await publish(connection.client, [{ runtimes: entry.runtimes, external_tools: [] }]);
|
|
16933
|
+
}
|
|
16934
|
+
connect() {
|
|
16935
|
+
this.connection ??= this.open().catch((error) => {
|
|
16936
|
+
this.connection = null;
|
|
16937
|
+
throw error;
|
|
16938
|
+
});
|
|
16939
|
+
return this.connection;
|
|
16940
|
+
}
|
|
16941
|
+
async open() {
|
|
16942
|
+
const owned = this.options.url ? null : await this.options.connect?.();
|
|
16943
|
+
const url = this.options.url ?? owned?.url;
|
|
16944
|
+
if (!url)
|
|
16945
|
+
throw new Error("registerTools() requires an app-server url.");
|
|
16946
|
+
const client = applyUniqueRequestIds(createAppServerClient({
|
|
16947
|
+
url,
|
|
16948
|
+
...this.options.authToken !== undefined ? { authToken: this.options.authToken } : {},
|
|
16949
|
+
...this.options.WebSocket ? { WebSocket: this.options.WebSocket } : {},
|
|
16950
|
+
...this.options.requestTimeoutMs !== undefined ? { requestTimeoutMs: this.options.requestTimeoutMs } : {}
|
|
16951
|
+
}));
|
|
16952
|
+
client.onExternalToolCall(async (request) => {
|
|
16953
|
+
const runtime = request.runtime;
|
|
16954
|
+
const entry = runtime && [...this.entries].find((candidate) => candidate.runtimes.some((r) => runtimeKey(r) === runtimeKey(runtime)));
|
|
16955
|
+
if (!entry)
|
|
16956
|
+
throw new Error(`No tools registered for conversation ${runtime?.conversation_id}`);
|
|
16957
|
+
return entry.call(request);
|
|
16958
|
+
});
|
|
16959
|
+
try {
|
|
16960
|
+
await client.connect();
|
|
16961
|
+
} catch (error) {
|
|
16962
|
+
client.close();
|
|
16963
|
+
owned?.close();
|
|
16964
|
+
throw error;
|
|
16965
|
+
}
|
|
16966
|
+
client.onDisconnect(() => this.onDisconnect(client));
|
|
16967
|
+
return { client, owned: owned ?? null, toolPolicy: await supportsRuntimeToolPolicy(client) };
|
|
16968
|
+
}
|
|
16969
|
+
disconnect() {
|
|
16970
|
+
if (this.reconnectTimer)
|
|
16971
|
+
clearTimeout(this.reconnectTimer);
|
|
16972
|
+
this.reconnectTimer = null;
|
|
16973
|
+
const connection = this.connection;
|
|
16974
|
+
this.connection = null;
|
|
16975
|
+
connection?.then(({ client, owned }) => {
|
|
16976
|
+
client.close();
|
|
16977
|
+
owned?.close();
|
|
16978
|
+
}, () => {
|
|
16979
|
+
return;
|
|
16980
|
+
});
|
|
16981
|
+
}
|
|
16982
|
+
onDisconnect(client) {
|
|
16983
|
+
this.connection?.then((current) => {
|
|
16984
|
+
if (current.client !== client)
|
|
16985
|
+
return;
|
|
16986
|
+
current.owned?.close();
|
|
16987
|
+
this.connection = null;
|
|
16988
|
+
this.scheduleReconnect();
|
|
16989
|
+
}, () => {
|
|
16990
|
+
return;
|
|
16991
|
+
});
|
|
16992
|
+
}
|
|
16993
|
+
scheduleReconnect() {
|
|
16994
|
+
if (this.closed || this.entries.size === 0 || this.reconnectTimer)
|
|
16995
|
+
return;
|
|
16996
|
+
const delay = RECONNECT_DELAYS_MS[Math.min(this.reconnectAttempt, RECONNECT_DELAYS_MS.length - 1)];
|
|
16997
|
+
this.reconnectAttempt += 1;
|
|
16998
|
+
this.reconnectTimer = setTimeout(() => {
|
|
16999
|
+
this.reconnectTimer = null;
|
|
17000
|
+
this.republish().catch(() => this.scheduleReconnect());
|
|
17001
|
+
}, delay);
|
|
17002
|
+
this.reconnectTimer.unref?.();
|
|
17003
|
+
}
|
|
17004
|
+
async republish() {
|
|
17005
|
+
const connection = await this.connect();
|
|
17006
|
+
await publish(connection.client, [...this.entries].map((entry) => group(entry.runtimes, entry.options, connection.toolPolicy)));
|
|
17007
|
+
this.reconnectAttempt = 0;
|
|
17008
|
+
}
|
|
17009
|
+
}
|
|
17010
|
+
function group(runtimes, options, toolPolicy) {
|
|
17011
|
+
return {
|
|
17012
|
+
runtimes,
|
|
17013
|
+
external_tools: externalToolGroups(options.tools) ?? [],
|
|
17014
|
+
...toolPolicy ? { tool_policy: runtimeToolPolicy(options.allowedTools, options.toolset) } : {}
|
|
17015
|
+
};
|
|
17016
|
+
}
|
|
17017
|
+
async function publish(client, updates) {
|
|
17018
|
+
const response = await client.runtimeExternalToolsUpdate({ updates });
|
|
17019
|
+
if (!response.success)
|
|
17020
|
+
throw new Error(response.error ?? "Failed to register tools");
|
|
17021
|
+
}
|
|
17022
|
+
async function resolveRuntime(client, id) {
|
|
17023
|
+
if (!id.startsWith("conv-") && !id.startsWith("local-conv-")) {
|
|
17024
|
+
return { agent_id: id, conversation_id: "default" };
|
|
17025
|
+
}
|
|
17026
|
+
const response = await client.request({ type: "conversation_retrieve", request_id: client.nextRequestId("conversation_retrieve"), conversation_id: id }, {
|
|
17027
|
+
predicate: (message) => message.type === "conversation_retrieve_response"
|
|
17028
|
+
});
|
|
17029
|
+
if (!response.success || !response.conversation) {
|
|
17030
|
+
throw new Error(response.error ?? `Failed to retrieve conversation ${id}`);
|
|
17031
|
+
}
|
|
17032
|
+
return { agent_id: response.conversation.agent_id ?? null, conversation_id: id };
|
|
17033
|
+
}
|
|
17034
|
+
|
|
16665
17035
|
// src/validation.ts
|
|
16666
17036
|
var VALID_SKILL_SOURCES = [
|
|
16667
17037
|
"bundled",
|
|
@@ -16938,6 +17308,7 @@ class LettaAgentClientBase {
|
|
|
16938
17308
|
agentRepositoriesClient = null;
|
|
16939
17309
|
cloudClient = null;
|
|
16940
17310
|
managementTransport = null;
|
|
17311
|
+
toolRegistry = null;
|
|
16941
17312
|
closed = false;
|
|
16942
17313
|
closePromise = null;
|
|
16943
17314
|
constructor(options = {}) {
|
|
@@ -17056,20 +17427,21 @@ class LettaAgentClientBase {
|
|
|
17056
17427
|
const sessionOptions = stripCloudExecutionOptions(options);
|
|
17057
17428
|
validateCreateSessionOptions(sessionOptions);
|
|
17058
17429
|
this.assertSessionBackend("resumeSession", options);
|
|
17430
|
+
const inherited = this.backend === "cloud" ? null : this.toolRegistry?.inheritFor(id, sessionOptions) ?? null;
|
|
17059
17431
|
if (this.backend === "remote") {
|
|
17060
|
-
|
|
17061
|
-
|
|
17062
|
-
|
|
17063
|
-
|
|
17064
|
-
|
|
17065
|
-
});
|
|
17066
|
-
}
|
|
17067
|
-
return new AppServerSession(this.appServerSessionOptions(), {
|
|
17432
|
+
const session2 = looksLikeConversationId(id) ? new AppServerSession(this.appServerSessionOptions(), {
|
|
17433
|
+
kind: "session",
|
|
17434
|
+
conversationId: id,
|
|
17435
|
+
options
|
|
17436
|
+
}) : new AppServerSession(this.appServerSessionOptions(), {
|
|
17068
17437
|
kind: "session",
|
|
17069
17438
|
agentId: id,
|
|
17070
17439
|
defaultConversation: true,
|
|
17071
17440
|
options
|
|
17072
17441
|
});
|
|
17442
|
+
if (inherited)
|
|
17443
|
+
session2.inheritTools(inherited);
|
|
17444
|
+
return session2;
|
|
17073
17445
|
}
|
|
17074
17446
|
if (this.backend === "cloud") {
|
|
17075
17447
|
if (looksLikeConversationId(id)) {
|
|
@@ -17086,7 +17458,10 @@ class LettaAgentClientBase {
|
|
|
17086
17458
|
options
|
|
17087
17459
|
}, this.getCloudClient());
|
|
17088
17460
|
}
|
|
17089
|
-
|
|
17461
|
+
const session = this.resumeLocalSession(id, options);
|
|
17462
|
+
if (inherited && session instanceof AppServerSession)
|
|
17463
|
+
session.inheritTools(inherited);
|
|
17464
|
+
return session;
|
|
17090
17465
|
}
|
|
17091
17466
|
async prompt(message, agentId, options = {}) {
|
|
17092
17467
|
this.assertOpen();
|
|
@@ -17294,10 +17669,23 @@ class LettaAgentClientBase {
|
|
|
17294
17669
|
}
|
|
17295
17670
|
return this.managementTransport;
|
|
17296
17671
|
}
|
|
17672
|
+
registerTools(options) {
|
|
17673
|
+
this.assertOpen();
|
|
17674
|
+
if (this.backend === "cloud") {
|
|
17675
|
+
return Promise.reject(new Error('registerTools() requires backend: "local" or "remote". Cloud sessions register tools through the tools session option.'));
|
|
17676
|
+
}
|
|
17677
|
+
this.toolRegistry ??= new ToolRegistry(this.backend === "remote" ? this.remoteOptions() : this.localAppServerConnection());
|
|
17678
|
+
return this.toolRegistry.register(options);
|
|
17679
|
+
}
|
|
17680
|
+
localAppServerConnection() {
|
|
17681
|
+
throw this.localBackendUnavailableError();
|
|
17682
|
+
}
|
|
17297
17683
|
close() {
|
|
17298
17684
|
if (this.closePromise)
|
|
17299
17685
|
return this.closePromise;
|
|
17300
17686
|
this.closed = true;
|
|
17687
|
+
this.toolRegistry?.close();
|
|
17688
|
+
this.toolRegistry = null;
|
|
17301
17689
|
const transport = this.managementTransport;
|
|
17302
17690
|
this.managementTransport = null;
|
|
17303
17691
|
const close = transport?.close() ?? Promise.resolve();
|
|
@@ -17842,4 +18230,4 @@ export {
|
|
|
17842
18230
|
CloudManagedSandboxExpiredError
|
|
17843
18231
|
};
|
|
17844
18232
|
|
|
17845
|
-
//# debugId=
|
|
18233
|
+
//# debugId=214DC938EA1FA61564756E2164756E21
|