@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/index.js
CHANGED
|
@@ -10198,14 +10198,6 @@ function buildCreatedAgentTags(options = {}) {
|
|
|
10198
10198
|
}
|
|
10199
10199
|
return Array.from(new Set(tags));
|
|
10200
10200
|
}
|
|
10201
|
-
var DEFAULT_SUMMARIZATION_MODEL = "letta/auto";
|
|
10202
|
-
var SYSTEM_REMINDER_TAG = "system-reminder";
|
|
10203
|
-
var SYSTEM_REMINDER_OPEN = `<${SYSTEM_REMINDER_TAG}>`;
|
|
10204
|
-
var SYSTEM_REMINDER_CLOSE = `</${SYSTEM_REMINDER_TAG}>`;
|
|
10205
|
-
var SYSTEM_ALERT_TAG = "system-alert";
|
|
10206
|
-
var SYSTEM_ALERT_OPEN = `<${SYSTEM_ALERT_TAG}>`;
|
|
10207
|
-
var SYSTEM_ALERT_CLOSE = `</${SYSTEM_ALERT_TAG}>`;
|
|
10208
|
-
var ELAPSED_DISPLAY_THRESHOLD_MS = 60 * 1000;
|
|
10209
10201
|
var READ_ONLY_BLOCK_LABELS = ["memory_filesystem"];
|
|
10210
10202
|
var human_default = `---
|
|
10211
10203
|
label: human
|
|
@@ -12245,7 +12237,6 @@ async function getDefaultMemoryBlocks() {
|
|
|
12245
12237
|
}
|
|
12246
12238
|
var models = [];
|
|
12247
12239
|
var BUILTIN_MODEL_ALIASES = new Map([
|
|
12248
|
-
["auto", "letta/auto"],
|
|
12249
12240
|
["auto-chat", "letta/auto-chat"],
|
|
12250
12241
|
["auto-fast", "letta/auto-fast"]
|
|
12251
12242
|
]);
|
|
@@ -12294,20 +12285,14 @@ function resolveModel(modelIdentifier) {
|
|
|
12294
12285
|
return builtinHandle;
|
|
12295
12286
|
return modelIdentifier.includes("/") ? modelIdentifier : null;
|
|
12296
12287
|
}
|
|
12288
|
+
var UNSELECTED_MODEL_HANDLE = "local/default";
|
|
12289
|
+
function isLettaHostedModelHandle(handle) {
|
|
12290
|
+
return handle.startsWith("letta/");
|
|
12291
|
+
}
|
|
12297
12292
|
function getDefaultModel() {
|
|
12298
|
-
|
|
12299
|
-
|
|
12300
|
-
|
|
12301
|
-
if (autoModel)
|
|
12302
|
-
return autoModel.handle;
|
|
12303
|
-
const defaultModel = models.find((model) => model.isDefault);
|
|
12304
|
-
if (defaultModel)
|
|
12305
|
-
return defaultModel.handle;
|
|
12306
|
-
const firstModel = models[0];
|
|
12307
|
-
if (!firstModel) {
|
|
12308
|
-
throw new Error("Model catalog is unavailable.");
|
|
12309
|
-
}
|
|
12310
|
-
return firstModel.handle;
|
|
12293
|
+
const candidates = models.filter((model) => !isLettaHostedModelHandle(model.handle));
|
|
12294
|
+
const defaultModel = candidates.find((model) => model.isDefault) ?? candidates[0];
|
|
12295
|
+
return defaultModel?.handle ?? UNSELECTED_MODEL_HANDLE;
|
|
12311
12296
|
}
|
|
12312
12297
|
var PERSONALITY_OPTIONS = [
|
|
12313
12298
|
{
|
|
@@ -12569,7 +12554,7 @@ async function buildCreateAgentRequest(options = {}) {
|
|
|
12569
12554
|
initial_message_sequence: [],
|
|
12570
12555
|
parallel_tool_calls: options.parallelToolCalls ?? true,
|
|
12571
12556
|
compaction_settings: {
|
|
12572
|
-
model: options.compactionModel ??
|
|
12557
|
+
model: options.compactionModel ?? modelHandle
|
|
12573
12558
|
},
|
|
12574
12559
|
...options.embedding !== undefined ? { embedding: options.embedding } : {},
|
|
12575
12560
|
...options.isSubagent ? { hidden: true } : options.hidden !== undefined ? { hidden: options.hidden } : {}
|
|
@@ -13183,7 +13168,7 @@ async function* streamStructuredTurns(config) {
|
|
|
13183
13168
|
return;
|
|
13184
13169
|
});
|
|
13185
13170
|
}
|
|
13186
|
-
if (msg.type !== "result") {
|
|
13171
|
+
if (msg.type !== "result" || msg.origin !== undefined) {
|
|
13187
13172
|
yield msg;
|
|
13188
13173
|
continue;
|
|
13189
13174
|
}
|
|
@@ -13303,9 +13288,115 @@ function expandMcpToolWildcards(allowedTools, mcpTools) {
|
|
|
13303
13288
|
return [...new Set(expanded)];
|
|
13304
13289
|
}
|
|
13305
13290
|
|
|
13291
|
+
// src/external-tools.ts
|
|
13292
|
+
function externalToolGroups(tools) {
|
|
13293
|
+
if (!tools || tools.length === 0)
|
|
13294
|
+
return;
|
|
13295
|
+
return [
|
|
13296
|
+
{
|
|
13297
|
+
tools: tools.map((tool) => ({
|
|
13298
|
+
name: tool.name,
|
|
13299
|
+
label: tool.label,
|
|
13300
|
+
description: tool.description,
|
|
13301
|
+
parameters: tool.parameters
|
|
13302
|
+
}))
|
|
13303
|
+
}
|
|
13304
|
+
];
|
|
13305
|
+
}
|
|
13306
|
+
var MAX_REMEMBERED_EXTERNAL_TOOL_CALLS = 200;
|
|
13307
|
+
function createExternalToolCallHandler(externalTools, fallbackRuntime) {
|
|
13308
|
+
const calls = new Map;
|
|
13309
|
+
const execute = async (request) => {
|
|
13310
|
+
const tool = externalTools.get(request.tool_name);
|
|
13311
|
+
if (!tool) {
|
|
13312
|
+
throw new Error(`Unknown external tool: ${request.tool_name}`);
|
|
13313
|
+
}
|
|
13314
|
+
const runtime = request.runtime ?? fallbackRuntime?.() ?? null;
|
|
13315
|
+
const context = runtime ? { toolCallId: request.tool_call_id, agentId: runtime.agent_id, conversationId: runtime.conversation_id } : undefined;
|
|
13316
|
+
const result = await tool.execute(request.tool_call_id, request.input, undefined, undefined, context);
|
|
13317
|
+
return {
|
|
13318
|
+
content: result.content.map((part) => ({
|
|
13319
|
+
type: part.type,
|
|
13320
|
+
...part.text !== undefined ? { text: part.text } : {},
|
|
13321
|
+
...part.data !== undefined ? { data: part.data } : {},
|
|
13322
|
+
...part.mimeType !== undefined ? { mimeType: part.mimeType } : {}
|
|
13323
|
+
})),
|
|
13324
|
+
...result.isError === true ? { is_error: true } : {}
|
|
13325
|
+
};
|
|
13326
|
+
};
|
|
13327
|
+
return (request) => {
|
|
13328
|
+
const requestId = request.request_id;
|
|
13329
|
+
if (typeof requestId !== "string" || requestId.length === 0) {
|
|
13330
|
+
return execute(request);
|
|
13331
|
+
}
|
|
13332
|
+
const existing = calls.get(requestId);
|
|
13333
|
+
if (existing)
|
|
13334
|
+
return existing;
|
|
13335
|
+
const call = execute(request);
|
|
13336
|
+
calls.set(requestId, call);
|
|
13337
|
+
while (calls.size > MAX_REMEMBERED_EXTERNAL_TOOL_CALLS) {
|
|
13338
|
+
const oldest = calls.keys().next().value;
|
|
13339
|
+
if (oldest === undefined)
|
|
13340
|
+
break;
|
|
13341
|
+
calls.delete(oldest);
|
|
13342
|
+
}
|
|
13343
|
+
return call;
|
|
13344
|
+
};
|
|
13345
|
+
}
|
|
13346
|
+
|
|
13347
|
+
// src/runtime-tool-policy.ts
|
|
13348
|
+
var RUNTIME_TOOL_POLICY_CAPABILITY = "runtime_tool_policy";
|
|
13349
|
+
var CAPABILITY_PROBE_TIMEOUT_MS = 5000;
|
|
13350
|
+
function clientToolsetPayload(toolset) {
|
|
13351
|
+
return {
|
|
13352
|
+
...toolset.base !== undefined ? { base: toolset.base } : {},
|
|
13353
|
+
...toolset.include !== undefined ? { include: [...new Set(toolset.include)] } : {}
|
|
13354
|
+
};
|
|
13355
|
+
}
|
|
13356
|
+
function runtimeToolPolicy(allowedTools, toolset) {
|
|
13357
|
+
return {
|
|
13358
|
+
...toolset !== undefined ? { toolset: clientToolsetPayload(toolset) } : {},
|
|
13359
|
+
...allowedTools !== undefined ? { allowlist: [...new Set(allowedTools)] } : {},
|
|
13360
|
+
exclude_interactive: true
|
|
13361
|
+
};
|
|
13362
|
+
}
|
|
13363
|
+
async function supportsRuntimeToolPolicy(client) {
|
|
13364
|
+
try {
|
|
13365
|
+
const info = await client.info({ timeoutMs: CAPABILITY_PROBE_TIMEOUT_MS });
|
|
13366
|
+
const capabilities = info.capabilities;
|
|
13367
|
+
return capabilities?.[RUNTIME_TOOL_POLICY_CAPABILITY] === true;
|
|
13368
|
+
} catch {
|
|
13369
|
+
return false;
|
|
13370
|
+
}
|
|
13371
|
+
}
|
|
13372
|
+
function mergeInheritedTools(inherited, own) {
|
|
13373
|
+
if (!inherited)
|
|
13374
|
+
return own;
|
|
13375
|
+
const tools = new Map(inherited.tools.map((tool) => [tool.name, tool]));
|
|
13376
|
+
for (const tool of own.tools)
|
|
13377
|
+
tools.set(tool.name, tool);
|
|
13378
|
+
const ownNames = new Set(own.tools.map((tool) => tool.name));
|
|
13379
|
+
const registered = inherited.allowedTools;
|
|
13380
|
+
const allowedTools = registered === undefined ? own.allowedTools : own.allowedTools === undefined ? [...new Set([...registered, ...ownNames])] : own.allowedTools.filter((name) => registered.includes(name) || ownNames.has(name));
|
|
13381
|
+
const toolset = own.toolset ?? inherited.toolset;
|
|
13382
|
+
return {
|
|
13383
|
+
tools: [...tools.values()],
|
|
13384
|
+
...allowedTools !== undefined ? { allowedTools } : {},
|
|
13385
|
+
...toolset !== undefined ? { toolset } : {}
|
|
13386
|
+
};
|
|
13387
|
+
}
|
|
13388
|
+
|
|
13306
13389
|
// src/remote-turn-coordinator.ts
|
|
13307
13390
|
var MAX_RECENTLY_SETTLED_RUN_IDS = 256;
|
|
13308
13391
|
var TRAILING_USAGE_GRACE_MS = 100;
|
|
13392
|
+
var TURN_ORIGINS = new Set([
|
|
13393
|
+
"task_notification",
|
|
13394
|
+
"cron",
|
|
13395
|
+
"subagent",
|
|
13396
|
+
"system",
|
|
13397
|
+
"channel",
|
|
13398
|
+
"user"
|
|
13399
|
+
]);
|
|
13309
13400
|
|
|
13310
13401
|
class RemoteTurnCoordinator {
|
|
13311
13402
|
label;
|
|
@@ -13317,6 +13408,8 @@ class RemoteTurnCoordinator {
|
|
|
13317
13408
|
activeTurn = null;
|
|
13318
13409
|
pendingTurns = [];
|
|
13319
13410
|
settledRunIds = new Set;
|
|
13411
|
+
queueOrigins = new Map;
|
|
13412
|
+
nextServerOrigin = "unknown";
|
|
13320
13413
|
nextTurnId = 0;
|
|
13321
13414
|
messageCounter = 0;
|
|
13322
13415
|
clientMessageCounter = 0;
|
|
@@ -13332,16 +13425,27 @@ class RemoteTurnCoordinator {
|
|
|
13332
13425
|
return this._activeTurnStartedAt;
|
|
13333
13426
|
}
|
|
13334
13427
|
hasInFlightTurn() {
|
|
13335
|
-
return this.activeTurn !== null || this.pendingTurns.length > 0;
|
|
13428
|
+
return this.activeTurn !== null && this.activeTurn.origin === undefined || this.pendingTurns.length > 0;
|
|
13336
13429
|
}
|
|
13337
13430
|
trackSentTurn(runtime, callerOtid, outputFormat) {
|
|
13338
13431
|
const otid = normalizeCallerOtid(callerOtid);
|
|
13339
|
-
const turn = {
|
|
13340
|
-
id: ++this.nextTurnId,
|
|
13341
|
-
runtime,
|
|
13432
|
+
const turn = this.newTracker(runtime, {
|
|
13342
13433
|
...otid !== undefined ? { otid } : {},
|
|
13343
13434
|
...outputFormat !== undefined ? { outputFormat } : {},
|
|
13344
|
-
clientMessageId: otid ?? `sdk-message-${Date.now()}-${++this.clientMessageCounter}
|
|
13435
|
+
clientMessageId: otid ?? `sdk-message-${Date.now()}-${++this.clientMessageCounter}`
|
|
13436
|
+
});
|
|
13437
|
+
if (this.activeTurn) {
|
|
13438
|
+
this.pendingTurns.push(turn);
|
|
13439
|
+
} else {
|
|
13440
|
+
this.activateTurn(turn);
|
|
13441
|
+
}
|
|
13442
|
+
return turn;
|
|
13443
|
+
}
|
|
13444
|
+
newTracker(runtime, identity) {
|
|
13445
|
+
return {
|
|
13446
|
+
id: ++this.nextTurnId,
|
|
13447
|
+
runtime,
|
|
13448
|
+
...identity,
|
|
13345
13449
|
queuedAt: Date.now(),
|
|
13346
13450
|
startedAt: 0,
|
|
13347
13451
|
assistantText: "",
|
|
@@ -13358,13 +13462,32 @@ class RemoteTurnCoordinator {
|
|
|
13358
13462
|
abortRequested: false,
|
|
13359
13463
|
timeout: null
|
|
13360
13464
|
};
|
|
13361
|
-
|
|
13362
|
-
|
|
13363
|
-
|
|
13364
|
-
|
|
13365
|
-
|
|
13465
|
+
}
|
|
13466
|
+
startServerTurn(runtime) {
|
|
13467
|
+
const turn = this.newTracker(runtime, { clientMessageId: "", origin: this.nextServerOrigin });
|
|
13468
|
+
this.nextServerOrigin = "unknown";
|
|
13469
|
+
this.activateTurn(turn);
|
|
13366
13470
|
return turn;
|
|
13367
13471
|
}
|
|
13472
|
+
observeQueueOrigins(message, queue) {
|
|
13473
|
+
for (const item of queue) {
|
|
13474
|
+
if (!item.clientMessageId)
|
|
13475
|
+
continue;
|
|
13476
|
+
this.queueOrigins.set(item.clientMessageId, TURN_ORIGINS.has(item.source) ? item.source : "unknown");
|
|
13477
|
+
if (this.queueOrigins.size > MAX_RECENTLY_SETTLED_RUN_IDS) {
|
|
13478
|
+
this.queueOrigins.delete(this.queueOrigins.keys().next().value);
|
|
13479
|
+
}
|
|
13480
|
+
}
|
|
13481
|
+
for (const removed of Array.isArray(message.removed) ? message.removed : []) {
|
|
13482
|
+
const { client_message_id: id, disposition, source } = removed;
|
|
13483
|
+
if (disposition !== "dequeued" || typeof id !== "string")
|
|
13484
|
+
continue;
|
|
13485
|
+
const own = [this.activeTurn, ...this.pendingTurns].some((turn) => turn?.clientMessageId === id);
|
|
13486
|
+
if (own)
|
|
13487
|
+
continue;
|
|
13488
|
+
this.nextServerOrigin = typeof source === "string" && TURN_ORIGINS.has(source) ? source : this.queueOrigins.get(id) ?? "unknown";
|
|
13489
|
+
}
|
|
13490
|
+
}
|
|
13368
13491
|
removeTrackedTurn(turn) {
|
|
13369
13492
|
if (turn.timeout)
|
|
13370
13493
|
clearTimeout(turn.timeout);
|
|
@@ -13378,6 +13501,12 @@ class RemoteTurnCoordinator {
|
|
|
13378
13501
|
if (index !== -1)
|
|
13379
13502
|
this.pendingTurns.splice(index, 1);
|
|
13380
13503
|
}
|
|
13504
|
+
failServerTurn(detail) {
|
|
13505
|
+
const active = this.activeTurn;
|
|
13506
|
+
if (active?.origin === undefined)
|
|
13507
|
+
return;
|
|
13508
|
+
this.failTurn(active, detail, { errorCode: "stream_closed", recoverable: true });
|
|
13509
|
+
}
|
|
13381
13510
|
markAbortRequested() {
|
|
13382
13511
|
if (this.activeTurn)
|
|
13383
13512
|
this.activeTurn.abortRequested = true;
|
|
@@ -13397,6 +13526,7 @@ class RemoteTurnCoordinator {
|
|
|
13397
13526
|
type: "queue_update",
|
|
13398
13527
|
queue: queueItems(message)
|
|
13399
13528
|
};
|
|
13529
|
+
this.observeQueueOrigins(message, sdkMessage2.queue);
|
|
13400
13530
|
this.enqueue(sdkMessage2);
|
|
13401
13531
|
return;
|
|
13402
13532
|
}
|
|
@@ -13439,9 +13569,9 @@ class RemoteTurnCoordinator {
|
|
|
13439
13569
|
if (!delta)
|
|
13440
13570
|
return;
|
|
13441
13571
|
const messageType = streamDeltaMessageType(delta);
|
|
13442
|
-
const active = messageType === "usage_statistics" || messageType === "stop_reason" ? this.activeTurn : this.activateNextTurnFromProtocol();
|
|
13443
13572
|
const runId = streamDeltaRunId(delta);
|
|
13444
13573
|
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");
|
|
13574
|
+
const active = messageType === "usage_statistics" || messageType === "stop_reason" ? this.activeTurn : this.activateNextTurnFromProtocol() ?? (isTurnEvidence && runId !== undefined ? this.startServerTurn(runtime) : null);
|
|
13445
13575
|
if (active && isTurnEvidence) {
|
|
13446
13576
|
active.observedTurnEvidence = true;
|
|
13447
13577
|
if (runId)
|
|
@@ -13899,7 +14029,8 @@ class RemoteTurnCoordinator {
|
|
|
13899
14029
|
stopReason,
|
|
13900
14030
|
durationMs: Date.now() - (tracker?.startedAt || this._activeTurnStartedAt),
|
|
13901
14031
|
conversationId: turn.runtime.conversation_id,
|
|
13902
|
-
runIds: turn.runIds.length > 0 ? turn.runIds : undefined
|
|
14032
|
+
runIds: turn.runIds.length > 0 ? turn.runIds : undefined,
|
|
14033
|
+
...tracker?.origin !== undefined ? { origin: tracker.origin } : {}
|
|
13903
14034
|
};
|
|
13904
14035
|
}
|
|
13905
14036
|
enqueue(message) {
|
|
@@ -14141,9 +14272,8 @@ class RemoteClientSessionCore {
|
|
|
14141
14272
|
}
|
|
14142
14273
|
await this.send(message);
|
|
14143
14274
|
for await (const msg of this.stream()) {
|
|
14144
|
-
if (msg.type === "result")
|
|
14275
|
+
if (msg.type === "result" && msg.origin === undefined)
|
|
14145
14276
|
return msg;
|
|
14146
|
-
}
|
|
14147
14277
|
}
|
|
14148
14278
|
return {
|
|
14149
14279
|
type: "result",
|
|
@@ -14179,7 +14309,7 @@ class RemoteClientSessionCore {
|
|
|
14179
14309
|
if (!msg)
|
|
14180
14310
|
break;
|
|
14181
14311
|
yield msg;
|
|
14182
|
-
if (msg.type === "result")
|
|
14312
|
+
if (msg.type === "result" && msg.origin === undefined)
|
|
14183
14313
|
break;
|
|
14184
14314
|
}
|
|
14185
14315
|
}
|
|
@@ -14489,6 +14619,7 @@ class RemoteClientSessionCore {
|
|
|
14489
14619
|
this.controller?.close();
|
|
14490
14620
|
this.controller = null;
|
|
14491
14621
|
const error = new Error(`${this.label} connection closed before send`);
|
|
14622
|
+
this.turns.failServerTurn(error.message);
|
|
14492
14623
|
for (const cancel of [...this.deviceStatusRefreshCancels])
|
|
14493
14624
|
cancel(error);
|
|
14494
14625
|
this.deviceStatusRefreshCancels.clear();
|
|
@@ -14629,38 +14760,6 @@ function agentToolNames(agent) {
|
|
|
14629
14760
|
return typeof name === "string" && name.length > 0 ? [name] : [];
|
|
14630
14761
|
});
|
|
14631
14762
|
}
|
|
14632
|
-
function externalToolGroups(tools) {
|
|
14633
|
-
if (!tools || tools.length === 0)
|
|
14634
|
-
return;
|
|
14635
|
-
return [
|
|
14636
|
-
{
|
|
14637
|
-
tools: tools.map((tool) => ({
|
|
14638
|
-
name: tool.name,
|
|
14639
|
-
label: tool.label,
|
|
14640
|
-
description: tool.description,
|
|
14641
|
-
parameters: tool.parameters
|
|
14642
|
-
}))
|
|
14643
|
-
}
|
|
14644
|
-
];
|
|
14645
|
-
}
|
|
14646
|
-
function createExternalToolCallHandler(externalTools) {
|
|
14647
|
-
return async (request) => {
|
|
14648
|
-
const tool = externalTools.get(request.tool_name);
|
|
14649
|
-
if (!tool) {
|
|
14650
|
-
throw new Error(`Unknown external tool: ${request.tool_name}`);
|
|
14651
|
-
}
|
|
14652
|
-
const result = await tool.execute(request.tool_call_id, request.input);
|
|
14653
|
-
return {
|
|
14654
|
-
content: result.content.map((part) => ({
|
|
14655
|
-
type: part.type,
|
|
14656
|
-
...part.text !== undefined ? { text: part.text } : {},
|
|
14657
|
-
...part.data !== undefined ? { data: part.data } : {},
|
|
14658
|
-
...part.mimeType !== undefined ? { mimeType: part.mimeType } : {}
|
|
14659
|
-
})),
|
|
14660
|
-
...result.isError === true ? { is_error: true } : {}
|
|
14661
|
-
};
|
|
14662
|
-
};
|
|
14663
|
-
}
|
|
14664
14763
|
async function resolveAppServerToolApproval(options, toolName, toolInput, context) {
|
|
14665
14764
|
if ("outputFormat" in options && options.outputFormat !== undefined && toolName === STRUCTURED_OUTPUT_TOOL) {
|
|
14666
14765
|
return { behavior: "allow", updatedInput: null, updatedPermissions: [] };
|
|
@@ -14837,11 +14936,26 @@ class AppServerRuntimeController {
|
|
|
14837
14936
|
options;
|
|
14838
14937
|
clientToolAllowlist;
|
|
14839
14938
|
clientToolset;
|
|
14840
|
-
|
|
14939
|
+
toolPolicyApplied;
|
|
14940
|
+
constructor(client, options, clientToolAllowlist, clientToolset, toolPolicyApplied = false) {
|
|
14841
14941
|
this.client = client;
|
|
14842
14942
|
this.options = options;
|
|
14843
14943
|
this.clientToolAllowlist = clientToolAllowlist;
|
|
14844
14944
|
this.clientToolset = clientToolset;
|
|
14945
|
+
this.toolPolicyApplied = toolPolicyApplied;
|
|
14946
|
+
}
|
|
14947
|
+
async updateToolSelection(runtime, selection) {
|
|
14948
|
+
this.clientToolAllowlist = selection.allowedTools;
|
|
14949
|
+
this.clientToolset = selection.toolset;
|
|
14950
|
+
const response = await this.client.runtimeExternalToolsUpdate({
|
|
14951
|
+
updates: [{
|
|
14952
|
+
runtimes: [runtime],
|
|
14953
|
+
external_tools: externalToolGroups(selection.tools) ?? [],
|
|
14954
|
+
...this.toolPolicyApplied ? { tool_policy: runtimeToolPolicy(selection.allowedTools, selection.toolset) } : {}
|
|
14955
|
+
}]
|
|
14956
|
+
});
|
|
14957
|
+
if (!response.success)
|
|
14958
|
+
throw new Error(response.error ?? "Failed to update session tools");
|
|
14845
14959
|
}
|
|
14846
14960
|
onMessage(handler) {
|
|
14847
14961
|
return this.client.onMessage((message, channel) => {
|
|
@@ -14863,14 +14977,11 @@ class AppServerRuntimeController {
|
|
|
14863
14977
|
}
|
|
14864
14978
|
]
|
|
14865
14979
|
};
|
|
14866
|
-
if (this.clientToolAllowlist !== undefined) {
|
|
14980
|
+
if (!this.toolPolicyApplied && this.clientToolAllowlist !== undefined) {
|
|
14867
14981
|
payload.client_tool_allowlist = [...new Set(this.clientToolAllowlist)];
|
|
14868
14982
|
}
|
|
14869
|
-
if (this.clientToolset !== undefined) {
|
|
14870
|
-
payload.client_toolset =
|
|
14871
|
-
...this.clientToolset.base !== undefined ? { base: this.clientToolset.base } : {},
|
|
14872
|
-
...this.clientToolset.include !== undefined ? { include: [...new Set(this.clientToolset.include)] } : {}
|
|
14873
|
-
};
|
|
14983
|
+
if (!this.toolPolicyApplied && this.clientToolset !== undefined) {
|
|
14984
|
+
payload.client_toolset = clientToolsetPayload(this.clientToolset);
|
|
14874
14985
|
}
|
|
14875
14986
|
if (options.outputFormat !== undefined) {
|
|
14876
14987
|
payload.response_format = {
|
|
@@ -14882,7 +14993,8 @@ class AppServerRuntimeController {
|
|
|
14882
14993
|
}
|
|
14883
14994
|
};
|
|
14884
14995
|
}
|
|
14885
|
-
|
|
14996
|
+
if (!this.toolPolicyApplied)
|
|
14997
|
+
payload.exclude_interactive_tools = true;
|
|
14886
14998
|
this.client.input({
|
|
14887
14999
|
runtime,
|
|
14888
15000
|
payload
|
|
@@ -14974,6 +15086,9 @@ class AppServerSession extends RemoteClientSessionCore {
|
|
|
14974
15086
|
mcpCleanup = Promise.resolve();
|
|
14975
15087
|
removeExternalToolHandler = null;
|
|
14976
15088
|
removeControlRequestHandler = null;
|
|
15089
|
+
inheritedTools = null;
|
|
15090
|
+
removeInheritedListener = null;
|
|
15091
|
+
ownSelection = null;
|
|
14977
15092
|
constructor(remoteOptions, mode) {
|
|
14978
15093
|
super(mode, {
|
|
14979
15094
|
label: "app-server",
|
|
@@ -14985,6 +15100,27 @@ class AppServerSession extends RemoteClientSessionCore {
|
|
|
14985
15100
|
this.externalTools.set(tool.name, tool);
|
|
14986
15101
|
}
|
|
14987
15102
|
}
|
|
15103
|
+
inheritTools(source) {
|
|
15104
|
+
this.inheritedTools = source;
|
|
15105
|
+
this.removeInheritedListener = source.onChange(() => {
|
|
15106
|
+
this.refreshInheritedTools().catch(() => {
|
|
15107
|
+
return;
|
|
15108
|
+
});
|
|
15109
|
+
});
|
|
15110
|
+
}
|
|
15111
|
+
applyToolSelection() {
|
|
15112
|
+
const selection = mergeInheritedTools(this.inheritedTools?.current() ?? null, this.ownSelection ?? { tools: [] });
|
|
15113
|
+
this.externalTools.clear();
|
|
15114
|
+
for (const tool of selection.tools)
|
|
15115
|
+
this.externalTools.set(tool.name, tool);
|
|
15116
|
+
return selection;
|
|
15117
|
+
}
|
|
15118
|
+
async refreshInheritedTools() {
|
|
15119
|
+
if (!this.ownSelection || !this.runtime || !(this.controller instanceof AppServerRuntimeController))
|
|
15120
|
+
return;
|
|
15121
|
+
const selection = this.applyToolSelection();
|
|
15122
|
+
await this.controller.updateToolSelection(this.runtime, selection);
|
|
15123
|
+
}
|
|
14988
15124
|
shouldEnableMemfs(options) {
|
|
14989
15125
|
if (this.mode.kind !== "create-agent")
|
|
14990
15126
|
return false;
|
|
@@ -15000,6 +15136,9 @@ class AppServerSession extends RemoteClientSessionCore {
|
|
|
15000
15136
|
...this.remoteOptions.requestTimeoutMs !== undefined ? { requestTimeoutMs: this.remoteOptions.requestTimeoutMs } : {}
|
|
15001
15137
|
}));
|
|
15002
15138
|
const options = this.currentOptions();
|
|
15139
|
+
this.externalTools.clear();
|
|
15140
|
+
for (const tool of this.mode.options.tools ?? [])
|
|
15141
|
+
this.externalTools.set(tool.name, tool);
|
|
15003
15142
|
this.mcpBridge = await connectMcpServers("mcpServers" in options ? options.mcpServers : undefined, {
|
|
15004
15143
|
cwd: options.cwd,
|
|
15005
15144
|
reservedToolNames: this.externalTools.keys()
|
|
@@ -15007,13 +15146,22 @@ class AppServerSession extends RemoteClientSessionCore {
|
|
|
15007
15146
|
for (const tool of this.mcpBridge.tools) {
|
|
15008
15147
|
this.externalTools.set(tool.name, tool);
|
|
15009
15148
|
}
|
|
15149
|
+
const mcpToolNames = this.mcpBridge.tools.map((tool) => tool.name);
|
|
15150
|
+
const ownToolset = "toolset" in options ? options.toolset : undefined;
|
|
15151
|
+
const ownAllowedTools = expandMcpToolWildcards(options.allowedTools, mcpToolNames);
|
|
15152
|
+
this.ownSelection = {
|
|
15153
|
+
tools: [...this.externalTools.values()],
|
|
15154
|
+
...ownAllowedTools !== undefined ? { allowedTools: ownAllowedTools } : {},
|
|
15155
|
+
...ownToolset !== undefined ? { toolset: ownToolset } : {}
|
|
15156
|
+
};
|
|
15157
|
+
const { allowedTools, toolset: clientToolset } = this.applyToolSelection();
|
|
15010
15158
|
this.removeControlRequestHandler = registerAppServerControlRequestHandler({
|
|
15011
15159
|
client,
|
|
15012
15160
|
getRuntime: () => this.runtime,
|
|
15013
15161
|
getOptions: () => this.currentOptions()
|
|
15014
15162
|
});
|
|
15015
|
-
if (this.externalTools.size > 0) {
|
|
15016
|
-
this.removeExternalToolHandler = client.onExternalToolCall(createExternalToolCallHandler(this.externalTools));
|
|
15163
|
+
if (this.externalTools.size > 0 || this.inheritedTools) {
|
|
15164
|
+
this.removeExternalToolHandler = client.onExternalToolCall(createExternalToolCallHandler(this.externalTools, () => this.runtime));
|
|
15017
15165
|
}
|
|
15018
15166
|
try {
|
|
15019
15167
|
await client.connect();
|
|
@@ -15024,19 +15172,17 @@ class AppServerSession extends RemoteClientSessionCore {
|
|
|
15024
15172
|
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.`);
|
|
15025
15173
|
}
|
|
15026
15174
|
}
|
|
15027
|
-
const response = await this.startRuntime(client);
|
|
15175
|
+
const response = await this.startRuntime(client, runtimeToolPolicy(allowedTools, clientToolset));
|
|
15028
15176
|
if (!response.success || !response.runtime) {
|
|
15029
15177
|
throw new Error(response.error ?? "Failed to start app-server runtime");
|
|
15030
15178
|
}
|
|
15031
15179
|
this.watchTransportDisconnect(client);
|
|
15180
|
+
const toolPolicyApplied = await supportsRuntimeToolPolicy(client);
|
|
15032
15181
|
const tools = agentToolNames(response.agent);
|
|
15033
15182
|
const skillSources = options.skillSources;
|
|
15034
|
-
const clientToolset = "toolset" in options ? options.toolset : undefined;
|
|
15035
|
-
const mcpToolNames = this.mcpBridge?.tools.map((tool) => tool.name) ?? [];
|
|
15036
|
-
const allowedTools = expandMcpToolWildcards(options.allowedTools, mcpToolNames);
|
|
15037
15183
|
const availableTools = tools === undefined && mcpToolNames.length === 0 ? undefined : [...tools ?? [], ...mcpToolNames];
|
|
15038
15184
|
return {
|
|
15039
|
-
controller: new AppServerRuntimeController(client, this.remoteOptions, allowedTools, clientToolset),
|
|
15185
|
+
controller: new AppServerRuntimeController(client, this.remoteOptions, allowedTools, clientToolset, toolPolicyApplied),
|
|
15040
15186
|
runtime: response.runtime,
|
|
15041
15187
|
model: typeof response.agent?.model === "string" ? response.agent.model : typeof response.conversation?.model === "string" ? response.conversation.model : "",
|
|
15042
15188
|
modelSettings: objectRecord(response.agent?.model_settings) ?? objectRecord(response.conversation?.model_settings) ?? null,
|
|
@@ -15054,6 +15200,10 @@ class AppServerSession extends RemoteClientSessionCore {
|
|
|
15054
15200
|
}
|
|
15055
15201
|
}
|
|
15056
15202
|
onCoreClose() {
|
|
15203
|
+
if (this.closed) {
|
|
15204
|
+
this.removeInheritedListener?.();
|
|
15205
|
+
this.removeInheritedListener = null;
|
|
15206
|
+
}
|
|
15057
15207
|
this.removeExternalToolHandler?.();
|
|
15058
15208
|
this.removeExternalToolHandler = null;
|
|
15059
15209
|
this.removeControlRequestHandler?.();
|
|
@@ -15084,11 +15234,11 @@ class AppServerSession extends RemoteClientSessionCore {
|
|
|
15084
15234
|
this.ownedConnection = connection;
|
|
15085
15235
|
return connection.url;
|
|
15086
15236
|
}
|
|
15087
|
-
async startRuntime(client) {
|
|
15088
|
-
const command = await this.buildRuntimeStartCommand(client);
|
|
15237
|
+
async startRuntime(client, toolPolicy) {
|
|
15238
|
+
const command = await this.buildRuntimeStartCommand(client, toolPolicy);
|
|
15089
15239
|
return client.runtimeStart(command);
|
|
15090
15240
|
}
|
|
15091
|
-
async buildRuntimeStartCommand(client) {
|
|
15241
|
+
async buildRuntimeStartCommand(client, toolPolicy) {
|
|
15092
15242
|
const options = this.mode.options;
|
|
15093
15243
|
const command = {
|
|
15094
15244
|
client_info: {
|
|
@@ -15119,6 +15269,7 @@ class AppServerSession extends RemoteClientSessionCore {
|
|
|
15119
15269
|
const groups = externalToolGroups([...this.externalTools.values()]);
|
|
15120
15270
|
if (groups)
|
|
15121
15271
|
command.external_tools = groups;
|
|
15272
|
+
command.tool_policy = toolPolicy;
|
|
15122
15273
|
if (this.mode.kind === "create-agent") {
|
|
15123
15274
|
command.create_agent = {
|
|
15124
15275
|
body: await createAgentBody(this.mode.options),
|
|
@@ -15601,6 +15752,9 @@ class CloudStatusTransport {
|
|
|
15601
15752
|
if (channel === "control" && message.type === "stream_delta") {
|
|
15602
15753
|
return false;
|
|
15603
15754
|
}
|
|
15755
|
+
if (channel === "stream" && message.type === "external_tool_call_request") {
|
|
15756
|
+
return false;
|
|
15757
|
+
}
|
|
15604
15758
|
if (this.isDuplicate(message))
|
|
15605
15759
|
return false;
|
|
15606
15760
|
this.trackEventSequence(message);
|
|
@@ -16097,7 +16251,7 @@ class CloudEnvironmentSession extends RemoteClientSessionCore {
|
|
|
16097
16251
|
getOptions: () => this.currentOptions()
|
|
16098
16252
|
});
|
|
16099
16253
|
if (this.externalTools.size > 0) {
|
|
16100
|
-
this.removeExternalToolHandler = client.onExternalToolCall(createExternalToolCallHandler(this.externalTools));
|
|
16254
|
+
this.removeExternalToolHandler = client.onExternalToolCall(createExternalToolCallHandler(this.externalTools, () => this.runtime));
|
|
16101
16255
|
}
|
|
16102
16256
|
}
|
|
16103
16257
|
cleanupTransportHandlers() {
|
|
@@ -16155,6 +16309,7 @@ class CloudEnvironmentSession extends RemoteClientSessionCore {
|
|
|
16155
16309
|
const groups = externalToolGroups([...this.externalTools.values()]);
|
|
16156
16310
|
if (groups)
|
|
16157
16311
|
command.external_tools = groups;
|
|
16312
|
+
command.tool_policy = runtimeToolPolicy(expandMcpToolWildcards(options.allowedTools, this.mcpBridge?.tools.map((tool) => tool.name) ?? []), "toolset" in options ? options.toolset : undefined);
|
|
16158
16313
|
return await client.runtimeStart(command);
|
|
16159
16314
|
}
|
|
16160
16315
|
async afterRuntimeInitialized() {
|
|
@@ -16720,6 +16875,221 @@ function createQuery(createSession, params) {
|
|
|
16720
16875
|
});
|
|
16721
16876
|
}
|
|
16722
16877
|
|
|
16878
|
+
// src/tool-registration.ts
|
|
16879
|
+
var RECONNECT_DELAYS_MS = [250, 1000, 5000, 15000];
|
|
16880
|
+
var runtimeKey = (runtime) => `${runtime.agent_id}:${runtime.conversation_id}`;
|
|
16881
|
+
|
|
16882
|
+
class ToolRegistry {
|
|
16883
|
+
options;
|
|
16884
|
+
connection = null;
|
|
16885
|
+
entries = new Set;
|
|
16886
|
+
listeners = new Set;
|
|
16887
|
+
reconnectAttempt = 0;
|
|
16888
|
+
reconnectTimer = null;
|
|
16889
|
+
closed = false;
|
|
16890
|
+
constructor(options) {
|
|
16891
|
+
this.options = options;
|
|
16892
|
+
}
|
|
16893
|
+
async register(options) {
|
|
16894
|
+
if (this.closed)
|
|
16895
|
+
throw new Error("LettaAgentClient is closed");
|
|
16896
|
+
const entry = { options, runtimes: [], call: createExternalToolCallHandler(new Map) };
|
|
16897
|
+
await this.apply(entry, options);
|
|
16898
|
+
const registry = this;
|
|
16899
|
+
return {
|
|
16900
|
+
get conversations() {
|
|
16901
|
+
return [...entry.options.conversations];
|
|
16902
|
+
},
|
|
16903
|
+
update: async (next) => {
|
|
16904
|
+
if (!registry.entries.has(entry))
|
|
16905
|
+
throw new Error("This tool registration is closed.");
|
|
16906
|
+
await registry.apply(entry, { ...entry.options, ...next });
|
|
16907
|
+
},
|
|
16908
|
+
close: () => registry.remove(entry),
|
|
16909
|
+
[Symbol.asyncDispose]: () => registry.remove(entry)
|
|
16910
|
+
};
|
|
16911
|
+
}
|
|
16912
|
+
close() {
|
|
16913
|
+
this.closed = true;
|
|
16914
|
+
this.entries.clear();
|
|
16915
|
+
this.disconnect();
|
|
16916
|
+
this.notify();
|
|
16917
|
+
}
|
|
16918
|
+
inheritFor(id, own) {
|
|
16919
|
+
const covering = this.covering(id);
|
|
16920
|
+
if (!covering)
|
|
16921
|
+
return null;
|
|
16922
|
+
const registered = new Set(covering.options.tools.map((tool) => tool.name));
|
|
16923
|
+
const clash = own.tools?.find((tool) => registered.has(tool.name));
|
|
16924
|
+
if (clash) {
|
|
16925
|
+
throw new Error(`Tool ${clash.name} is already registered for ${id} with registerTools(); don't pass it to the session again.`);
|
|
16926
|
+
}
|
|
16927
|
+
if (own.toolset !== undefined && covering.options.toolset !== undefined && JSON.stringify(own.toolset) !== JSON.stringify(covering.options.toolset)) {
|
|
16928
|
+
throw new Error(`${id} already has a toolset from registerTools(); narrow it with the session's allowedTools instead.`);
|
|
16929
|
+
}
|
|
16930
|
+
return {
|
|
16931
|
+
current: () => this.covering(id)?.options ?? null,
|
|
16932
|
+
onChange: (listener) => {
|
|
16933
|
+
this.listeners.add(listener);
|
|
16934
|
+
return () => this.listeners.delete(listener);
|
|
16935
|
+
}
|
|
16936
|
+
};
|
|
16937
|
+
}
|
|
16938
|
+
covering(id) {
|
|
16939
|
+
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));
|
|
16940
|
+
}
|
|
16941
|
+
notify() {
|
|
16942
|
+
for (const listener of [...this.listeners])
|
|
16943
|
+
listener();
|
|
16944
|
+
}
|
|
16945
|
+
async apply(entry, options) {
|
|
16946
|
+
if (!Array.isArray(options.conversations) || options.conversations.length === 0) {
|
|
16947
|
+
throw new Error("registerTools() requires at least one conversation.");
|
|
16948
|
+
}
|
|
16949
|
+
if (!Array.isArray(options.tools))
|
|
16950
|
+
throw new Error("registerTools() requires a tools array.");
|
|
16951
|
+
const connection = await this.connect();
|
|
16952
|
+
if (!connection.toolPolicy && (options.allowedTools || options.toolset)) {
|
|
16953
|
+
throw new Error("registerTools() allowedTools and toolset require a Letta Code app server that supports runtime tool policies. Upgrade @letta-ai/letta-code.");
|
|
16954
|
+
}
|
|
16955
|
+
const runtimes = await Promise.all(options.conversations.map((id) => resolveRuntime(connection.client, id)));
|
|
16956
|
+
const taken = new Set([...this.entries].filter((other) => other !== entry).flatMap((other) => other.runtimes.map(runtimeKey)));
|
|
16957
|
+
const overlap = options.conversations.find((_, index) => taken.has(runtimeKey(runtimes[index])));
|
|
16958
|
+
if (overlap)
|
|
16959
|
+
throw new Error(`Tools are already registered for ${overlap}.`);
|
|
16960
|
+
const keys = new Set(runtimes.map(runtimeKey));
|
|
16961
|
+
const dropped = entry.runtimes.filter((runtime) => !keys.has(runtimeKey(runtime)));
|
|
16962
|
+
const previous = { ...entry };
|
|
16963
|
+
const isNew = !this.entries.has(entry);
|
|
16964
|
+
entry.options = options;
|
|
16965
|
+
entry.runtimes = runtimes;
|
|
16966
|
+
entry.call = createExternalToolCallHandler(new Map(options.tools.map((tool) => [tool.name, tool])));
|
|
16967
|
+
this.entries.add(entry);
|
|
16968
|
+
try {
|
|
16969
|
+
await publish(connection.client, [
|
|
16970
|
+
group(runtimes, options, connection.toolPolicy),
|
|
16971
|
+
...dropped.length > 0 ? [{ runtimes: dropped, external_tools: [] }] : []
|
|
16972
|
+
]);
|
|
16973
|
+
} catch (error) {
|
|
16974
|
+
Object.assign(entry, previous);
|
|
16975
|
+
if (isNew)
|
|
16976
|
+
this.entries.delete(entry);
|
|
16977
|
+
throw error;
|
|
16978
|
+
}
|
|
16979
|
+
this.notify();
|
|
16980
|
+
}
|
|
16981
|
+
async remove(entry) {
|
|
16982
|
+
if (!this.entries.delete(entry))
|
|
16983
|
+
return;
|
|
16984
|
+
this.notify();
|
|
16985
|
+
if (this.entries.size === 0) {
|
|
16986
|
+
this.disconnect();
|
|
16987
|
+
return;
|
|
16988
|
+
}
|
|
16989
|
+
const connection = await this.connect();
|
|
16990
|
+
await publish(connection.client, [{ runtimes: entry.runtimes, external_tools: [] }]);
|
|
16991
|
+
}
|
|
16992
|
+
connect() {
|
|
16993
|
+
this.connection ??= this.open().catch((error) => {
|
|
16994
|
+
this.connection = null;
|
|
16995
|
+
throw error;
|
|
16996
|
+
});
|
|
16997
|
+
return this.connection;
|
|
16998
|
+
}
|
|
16999
|
+
async open() {
|
|
17000
|
+
const owned = this.options.url ? null : await this.options.connect?.();
|
|
17001
|
+
const url = this.options.url ?? owned?.url;
|
|
17002
|
+
if (!url)
|
|
17003
|
+
throw new Error("registerTools() requires an app-server url.");
|
|
17004
|
+
const client = applyUniqueRequestIds(createAppServerClient({
|
|
17005
|
+
url,
|
|
17006
|
+
...this.options.authToken !== undefined ? { authToken: this.options.authToken } : {},
|
|
17007
|
+
...this.options.WebSocket ? { WebSocket: this.options.WebSocket } : {},
|
|
17008
|
+
...this.options.requestTimeoutMs !== undefined ? { requestTimeoutMs: this.options.requestTimeoutMs } : {}
|
|
17009
|
+
}));
|
|
17010
|
+
client.onExternalToolCall(async (request) => {
|
|
17011
|
+
const runtime = request.runtime;
|
|
17012
|
+
const entry = runtime && [...this.entries].find((candidate) => candidate.runtimes.some((r) => runtimeKey(r) === runtimeKey(runtime)));
|
|
17013
|
+
if (!entry)
|
|
17014
|
+
throw new Error(`No tools registered for conversation ${runtime?.conversation_id}`);
|
|
17015
|
+
return entry.call(request);
|
|
17016
|
+
});
|
|
17017
|
+
try {
|
|
17018
|
+
await client.connect();
|
|
17019
|
+
} catch (error) {
|
|
17020
|
+
client.close();
|
|
17021
|
+
owned?.close();
|
|
17022
|
+
throw error;
|
|
17023
|
+
}
|
|
17024
|
+
client.onDisconnect(() => this.onDisconnect(client));
|
|
17025
|
+
return { client, owned: owned ?? null, toolPolicy: await supportsRuntimeToolPolicy(client) };
|
|
17026
|
+
}
|
|
17027
|
+
disconnect() {
|
|
17028
|
+
if (this.reconnectTimer)
|
|
17029
|
+
clearTimeout(this.reconnectTimer);
|
|
17030
|
+
this.reconnectTimer = null;
|
|
17031
|
+
const connection = this.connection;
|
|
17032
|
+
this.connection = null;
|
|
17033
|
+
connection?.then(({ client, owned }) => {
|
|
17034
|
+
client.close();
|
|
17035
|
+
owned?.close();
|
|
17036
|
+
}, () => {
|
|
17037
|
+
return;
|
|
17038
|
+
});
|
|
17039
|
+
}
|
|
17040
|
+
onDisconnect(client) {
|
|
17041
|
+
this.connection?.then((current) => {
|
|
17042
|
+
if (current.client !== client)
|
|
17043
|
+
return;
|
|
17044
|
+
current.owned?.close();
|
|
17045
|
+
this.connection = null;
|
|
17046
|
+
this.scheduleReconnect();
|
|
17047
|
+
}, () => {
|
|
17048
|
+
return;
|
|
17049
|
+
});
|
|
17050
|
+
}
|
|
17051
|
+
scheduleReconnect() {
|
|
17052
|
+
if (this.closed || this.entries.size === 0 || this.reconnectTimer)
|
|
17053
|
+
return;
|
|
17054
|
+
const delay = RECONNECT_DELAYS_MS[Math.min(this.reconnectAttempt, RECONNECT_DELAYS_MS.length - 1)];
|
|
17055
|
+
this.reconnectAttempt += 1;
|
|
17056
|
+
this.reconnectTimer = setTimeout(() => {
|
|
17057
|
+
this.reconnectTimer = null;
|
|
17058
|
+
this.republish().catch(() => this.scheduleReconnect());
|
|
17059
|
+
}, delay);
|
|
17060
|
+
this.reconnectTimer.unref?.();
|
|
17061
|
+
}
|
|
17062
|
+
async republish() {
|
|
17063
|
+
const connection = await this.connect();
|
|
17064
|
+
await publish(connection.client, [...this.entries].map((entry) => group(entry.runtimes, entry.options, connection.toolPolicy)));
|
|
17065
|
+
this.reconnectAttempt = 0;
|
|
17066
|
+
}
|
|
17067
|
+
}
|
|
17068
|
+
function group(runtimes, options, toolPolicy) {
|
|
17069
|
+
return {
|
|
17070
|
+
runtimes,
|
|
17071
|
+
external_tools: externalToolGroups(options.tools) ?? [],
|
|
17072
|
+
...toolPolicy ? { tool_policy: runtimeToolPolicy(options.allowedTools, options.toolset) } : {}
|
|
17073
|
+
};
|
|
17074
|
+
}
|
|
17075
|
+
async function publish(client, updates) {
|
|
17076
|
+
const response = await client.runtimeExternalToolsUpdate({ updates });
|
|
17077
|
+
if (!response.success)
|
|
17078
|
+
throw new Error(response.error ?? "Failed to register tools");
|
|
17079
|
+
}
|
|
17080
|
+
async function resolveRuntime(client, id) {
|
|
17081
|
+
if (!id.startsWith("conv-") && !id.startsWith("local-conv-")) {
|
|
17082
|
+
return { agent_id: id, conversation_id: "default" };
|
|
17083
|
+
}
|
|
17084
|
+
const response = await client.request({ type: "conversation_retrieve", request_id: client.nextRequestId("conversation_retrieve"), conversation_id: id }, {
|
|
17085
|
+
predicate: (message) => message.type === "conversation_retrieve_response"
|
|
17086
|
+
});
|
|
17087
|
+
if (!response.success || !response.conversation) {
|
|
17088
|
+
throw new Error(response.error ?? `Failed to retrieve conversation ${id}`);
|
|
17089
|
+
}
|
|
17090
|
+
return { agent_id: response.conversation.agent_id ?? null, conversation_id: id };
|
|
17091
|
+
}
|
|
17092
|
+
|
|
16723
17093
|
// src/validation.ts
|
|
16724
17094
|
var VALID_SKILL_SOURCES = [
|
|
16725
17095
|
"bundled",
|
|
@@ -16996,6 +17366,7 @@ class LettaAgentClientBase {
|
|
|
16996
17366
|
agentRepositoriesClient = null;
|
|
16997
17367
|
cloudClient = null;
|
|
16998
17368
|
managementTransport = null;
|
|
17369
|
+
toolRegistry = null;
|
|
16999
17370
|
closed = false;
|
|
17000
17371
|
closePromise = null;
|
|
17001
17372
|
constructor(options = {}) {
|
|
@@ -17114,20 +17485,21 @@ class LettaAgentClientBase {
|
|
|
17114
17485
|
const sessionOptions = stripCloudExecutionOptions(options);
|
|
17115
17486
|
validateCreateSessionOptions(sessionOptions);
|
|
17116
17487
|
this.assertSessionBackend("resumeSession", options);
|
|
17488
|
+
const inherited = this.backend === "cloud" ? null : this.toolRegistry?.inheritFor(id, sessionOptions) ?? null;
|
|
17117
17489
|
if (this.backend === "remote") {
|
|
17118
|
-
|
|
17119
|
-
|
|
17120
|
-
|
|
17121
|
-
|
|
17122
|
-
|
|
17123
|
-
});
|
|
17124
|
-
}
|
|
17125
|
-
return new AppServerSession(this.appServerSessionOptions(), {
|
|
17490
|
+
const session2 = looksLikeConversationId(id) ? new AppServerSession(this.appServerSessionOptions(), {
|
|
17491
|
+
kind: "session",
|
|
17492
|
+
conversationId: id,
|
|
17493
|
+
options
|
|
17494
|
+
}) : new AppServerSession(this.appServerSessionOptions(), {
|
|
17126
17495
|
kind: "session",
|
|
17127
17496
|
agentId: id,
|
|
17128
17497
|
defaultConversation: true,
|
|
17129
17498
|
options
|
|
17130
17499
|
});
|
|
17500
|
+
if (inherited)
|
|
17501
|
+
session2.inheritTools(inherited);
|
|
17502
|
+
return session2;
|
|
17131
17503
|
}
|
|
17132
17504
|
if (this.backend === "cloud") {
|
|
17133
17505
|
if (looksLikeConversationId(id)) {
|
|
@@ -17144,7 +17516,10 @@ class LettaAgentClientBase {
|
|
|
17144
17516
|
options
|
|
17145
17517
|
}, this.getCloudClient());
|
|
17146
17518
|
}
|
|
17147
|
-
|
|
17519
|
+
const session = this.resumeLocalSession(id, options);
|
|
17520
|
+
if (inherited && session instanceof AppServerSession)
|
|
17521
|
+
session.inheritTools(inherited);
|
|
17522
|
+
return session;
|
|
17148
17523
|
}
|
|
17149
17524
|
async prompt(message, agentId, options = {}) {
|
|
17150
17525
|
this.assertOpen();
|
|
@@ -17352,10 +17727,23 @@ class LettaAgentClientBase {
|
|
|
17352
17727
|
}
|
|
17353
17728
|
return this.managementTransport;
|
|
17354
17729
|
}
|
|
17730
|
+
registerTools(options) {
|
|
17731
|
+
this.assertOpen();
|
|
17732
|
+
if (this.backend === "cloud") {
|
|
17733
|
+
return Promise.reject(new Error('registerTools() requires backend: "local" or "remote". Cloud sessions register tools through the tools session option.'));
|
|
17734
|
+
}
|
|
17735
|
+
this.toolRegistry ??= new ToolRegistry(this.backend === "remote" ? this.remoteOptions() : this.localAppServerConnection());
|
|
17736
|
+
return this.toolRegistry.register(options);
|
|
17737
|
+
}
|
|
17738
|
+
localAppServerConnection() {
|
|
17739
|
+
throw this.localBackendUnavailableError();
|
|
17740
|
+
}
|
|
17355
17741
|
close() {
|
|
17356
17742
|
if (this.closePromise)
|
|
17357
17743
|
return this.closePromise;
|
|
17358
17744
|
this.closed = true;
|
|
17745
|
+
this.toolRegistry?.close();
|
|
17746
|
+
this.toolRegistry = null;
|
|
17359
17747
|
const transport = this.managementTransport;
|
|
17360
17748
|
this.managementTransport = null;
|
|
17361
17749
|
const close = transport?.close() ?? Promise.resolve();
|
|
@@ -18142,15 +18530,18 @@ class LettaAgentClient extends LettaAgentClientBase {
|
|
|
18142
18530
|
return { loadSkillDirectory, pushSkillSupportFiles };
|
|
18143
18531
|
}
|
|
18144
18532
|
createLocalManagementTransport() {
|
|
18533
|
+
return new AppServerManagementTransport(this.localAppServerConnection());
|
|
18534
|
+
}
|
|
18535
|
+
localAppServerConnection() {
|
|
18145
18536
|
const localOptions = this.options.appServer;
|
|
18146
18537
|
const sharedServer = this.sharedLocalServer();
|
|
18147
|
-
return
|
|
18538
|
+
return {
|
|
18148
18539
|
...localOptions?.url !== undefined ? { url: localOptions.url } : {
|
|
18149
18540
|
connect: () => sharedServer.connect()
|
|
18150
18541
|
},
|
|
18151
18542
|
...localOptions?.WebSocket !== undefined ? { WebSocket: localOptions.WebSocket } : {},
|
|
18152
18543
|
...localOptions?.requestTimeoutMs !== undefined ? { requestTimeoutMs: localOptions.requestTimeoutMs } : {}
|
|
18153
|
-
}
|
|
18544
|
+
};
|
|
18154
18545
|
}
|
|
18155
18546
|
async createLocalAgent(options) {
|
|
18156
18547
|
const localOptions = this.options;
|
|
@@ -19647,12 +20038,12 @@ var require_applicability2 = __commonJS2((exports) => {
|
|
|
19647
20038
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19648
20039
|
exports.shouldUseRule = exports.shouldUseGroup = exports.schemaHasRulesForType = undefined;
|
|
19649
20040
|
function schemaHasRulesForType({ schema, self }, type) {
|
|
19650
|
-
const
|
|
19651
|
-
return
|
|
20041
|
+
const group2 = self.RULES.types[type];
|
|
20042
|
+
return group2 && group2 !== true && shouldUseGroup(schema, group2);
|
|
19652
20043
|
}
|
|
19653
20044
|
exports.schemaHasRulesForType = schemaHasRulesForType;
|
|
19654
|
-
function shouldUseGroup(schema,
|
|
19655
|
-
return
|
|
20045
|
+
function shouldUseGroup(schema, group2) {
|
|
20046
|
+
return group2.rules.some((rule) => shouldUseRule(schema, rule));
|
|
19656
20047
|
}
|
|
19657
20048
|
exports.shouldUseGroup = shouldUseGroup;
|
|
19658
20049
|
function shouldUseRule(schema, rule) {
|
|
@@ -20644,36 +21035,36 @@ var require_validate2 = __commonJS2((exports) => {
|
|
|
20644
21035
|
if (!opts.jtd)
|
|
20645
21036
|
checkStrictTypes(it, types);
|
|
20646
21037
|
gen.block(() => {
|
|
20647
|
-
for (const
|
|
20648
|
-
groupKeywords(
|
|
21038
|
+
for (const group2 of RULES.rules)
|
|
21039
|
+
groupKeywords(group2);
|
|
20649
21040
|
groupKeywords(RULES.post);
|
|
20650
21041
|
});
|
|
20651
|
-
function groupKeywords(
|
|
20652
|
-
if (!(0, applicability_1.shouldUseGroup)(schema,
|
|
21042
|
+
function groupKeywords(group2) {
|
|
21043
|
+
if (!(0, applicability_1.shouldUseGroup)(schema, group2))
|
|
20653
21044
|
return;
|
|
20654
|
-
if (
|
|
20655
|
-
gen.if((0, dataType_2.checkDataType)(
|
|
20656
|
-
iterateKeywords(it,
|
|
20657
|
-
if (types.length === 1 && types[0] ===
|
|
21045
|
+
if (group2.type) {
|
|
21046
|
+
gen.if((0, dataType_2.checkDataType)(group2.type, data, opts.strictNumbers));
|
|
21047
|
+
iterateKeywords(it, group2);
|
|
21048
|
+
if (types.length === 1 && types[0] === group2.type && typeErrors) {
|
|
20658
21049
|
gen.else();
|
|
20659
21050
|
(0, dataType_2.reportTypeError)(it);
|
|
20660
21051
|
}
|
|
20661
21052
|
gen.endIf();
|
|
20662
21053
|
} else {
|
|
20663
|
-
iterateKeywords(it,
|
|
21054
|
+
iterateKeywords(it, group2);
|
|
20664
21055
|
}
|
|
20665
21056
|
if (!allErrors)
|
|
20666
21057
|
gen.if((0, codegen_1._)`${names_1.default.errors} === ${errsCount || 0}`);
|
|
20667
21058
|
}
|
|
20668
21059
|
}
|
|
20669
|
-
function iterateKeywords(it,
|
|
21060
|
+
function iterateKeywords(it, group2) {
|
|
20670
21061
|
const { gen, schema, opts: { useDefaults } } = it;
|
|
20671
21062
|
if (useDefaults)
|
|
20672
|
-
(0, defaults_1.assignDefaults)(it,
|
|
21063
|
+
(0, defaults_1.assignDefaults)(it, group2.type);
|
|
20673
21064
|
gen.block(() => {
|
|
20674
|
-
for (const rule of
|
|
21065
|
+
for (const rule of group2.rules) {
|
|
20675
21066
|
if ((0, applicability_1.shouldUseRule)(schema, rule)) {
|
|
20676
|
-
keywordCode(it, rule.keyword, rule.definition,
|
|
21067
|
+
keywordCode(it, rule.keyword, rule.definition, group2.type);
|
|
20677
21068
|
}
|
|
20678
21069
|
}
|
|
20679
21070
|
});
|
|
@@ -22358,10 +22749,10 @@ var require_core3 = __commonJS2((exports) => {
|
|
|
22358
22749
|
const { RULES } = this;
|
|
22359
22750
|
delete RULES.keywords[keyword];
|
|
22360
22751
|
delete RULES.all[keyword];
|
|
22361
|
-
for (const
|
|
22362
|
-
const i =
|
|
22752
|
+
for (const group2 of RULES.rules) {
|
|
22753
|
+
const i = group2.rules.findIndex((rule) => rule.keyword === keyword);
|
|
22363
22754
|
if (i >= 0)
|
|
22364
|
-
|
|
22755
|
+
group2.rules.splice(i, 1);
|
|
22365
22756
|
}
|
|
22366
22757
|
return this;
|
|
22367
22758
|
}
|
|
@@ -29615,16 +30006,16 @@ var safeDecode = /* @__PURE__ */ _safeDecode(ZodRealError);
|
|
|
29615
30006
|
var safeEncodeAsync = /* @__PURE__ */ _safeEncodeAsync(ZodRealError);
|
|
29616
30007
|
var safeDecodeAsync = /* @__PURE__ */ _safeDecodeAsync(ZodRealError);
|
|
29617
30008
|
var _installedGroups = /* @__PURE__ */ new WeakMap;
|
|
29618
|
-
function _installLazyMethods(inst,
|
|
30009
|
+
function _installLazyMethods(inst, group2, methods) {
|
|
29619
30010
|
const proto = Object.getPrototypeOf(inst);
|
|
29620
30011
|
let installed = _installedGroups.get(proto);
|
|
29621
30012
|
if (!installed) {
|
|
29622
30013
|
installed = new Set;
|
|
29623
30014
|
_installedGroups.set(proto, installed);
|
|
29624
30015
|
}
|
|
29625
|
-
if (installed.has(
|
|
30016
|
+
if (installed.has(group2))
|
|
29626
30017
|
return;
|
|
29627
|
-
installed.add(
|
|
30018
|
+
installed.add(group2);
|
|
29628
30019
|
for (const key in methods) {
|
|
29629
30020
|
const fn = methods[key];
|
|
29630
30021
|
Object.defineProperty(proto, key, {
|
|
@@ -35002,7 +35393,7 @@ class StreamableHTTPClientTransport {
|
|
|
35002
35393
|
}
|
|
35003
35394
|
var DEFAULT_CLIENT_INFO = {
|
|
35004
35395
|
name: "letta-code",
|
|
35005
|
-
version: "0.34.
|
|
35396
|
+
version: "0.34.46"
|
|
35006
35397
|
};
|
|
35007
35398
|
async function connectMcpServer(config2, options = {}) {
|
|
35008
35399
|
let client = new Client(options.clientInfo ?? DEFAULT_CLIENT_INFO);
|
|
@@ -36030,4 +36421,4 @@ export {
|
|
|
36030
36421
|
CloudManagedSandboxExpiredError
|
|
36031
36422
|
};
|
|
36032
36423
|
|
|
36033
|
-
//# debugId=
|
|
36424
|
+
//# debugId=7BDBF5BF72C0151764756E2164756E21
|