@letta-ai/letta-code 0.24.8 → 0.24.9
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/letta.js +326 -156
- package/package.json +1 -1
package/letta.js
CHANGED
|
@@ -3269,7 +3269,7 @@ var package_default;
|
|
|
3269
3269
|
var init_package = __esm(() => {
|
|
3270
3270
|
package_default = {
|
|
3271
3271
|
name: "@letta-ai/letta-code",
|
|
3272
|
-
version: "0.24.
|
|
3272
|
+
version: "0.24.9",
|
|
3273
3273
|
description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
|
|
3274
3274
|
type: "module",
|
|
3275
3275
|
bin: {
|
|
@@ -9488,46 +9488,6 @@ var init_models2 = __esm(() => {
|
|
|
9488
9488
|
parallel_tool_calls: true
|
|
9489
9489
|
}
|
|
9490
9490
|
},
|
|
9491
|
-
{
|
|
9492
|
-
id: "gpt-5.5-pro-medium",
|
|
9493
|
-
handle: "openai/gpt-5.5-pro",
|
|
9494
|
-
label: "GPT-5.5 Pro",
|
|
9495
|
-
description: "GPT-5.5 Pro — max performance variant (med reasoning)",
|
|
9496
|
-
updateArgs: {
|
|
9497
|
-
reasoning_effort: "medium",
|
|
9498
|
-
verbosity: "medium",
|
|
9499
|
-
context_window: 272000,
|
|
9500
|
-
max_output_tokens: 128000,
|
|
9501
|
-
parallel_tool_calls: true
|
|
9502
|
-
}
|
|
9503
|
-
},
|
|
9504
|
-
{
|
|
9505
|
-
id: "gpt-5.5-pro-high",
|
|
9506
|
-
handle: "openai/gpt-5.5-pro",
|
|
9507
|
-
label: "GPT-5.5 Pro",
|
|
9508
|
-
description: "GPT-5.5 Pro — max performance variant (high reasoning)",
|
|
9509
|
-
updateArgs: {
|
|
9510
|
-
reasoning_effort: "high",
|
|
9511
|
-
verbosity: "medium",
|
|
9512
|
-
context_window: 272000,
|
|
9513
|
-
max_output_tokens: 128000,
|
|
9514
|
-
parallel_tool_calls: true
|
|
9515
|
-
},
|
|
9516
|
-
isFeatured: true
|
|
9517
|
-
},
|
|
9518
|
-
{
|
|
9519
|
-
id: "gpt-5.5-pro-xhigh",
|
|
9520
|
-
handle: "openai/gpt-5.5-pro",
|
|
9521
|
-
label: "GPT-5.5 Pro",
|
|
9522
|
-
description: "GPT-5.5 Pro — max performance variant (max reasoning)",
|
|
9523
|
-
updateArgs: {
|
|
9524
|
-
reasoning_effort: "xhigh",
|
|
9525
|
-
verbosity: "medium",
|
|
9526
|
-
context_window: 272000,
|
|
9527
|
-
max_output_tokens: 128000,
|
|
9528
|
-
parallel_tool_calls: true
|
|
9529
|
-
}
|
|
9530
|
-
},
|
|
9531
9491
|
{
|
|
9532
9492
|
id: "gpt-5.4-pro-medium",
|
|
9533
9493
|
handle: "openai/gpt-5.4-pro",
|
|
@@ -40145,12 +40105,14 @@ var init_config = __esm(() => {
|
|
|
40145
40105
|
};
|
|
40146
40106
|
discordConfigCodec = {
|
|
40147
40107
|
parse(parsed) {
|
|
40108
|
+
const rawAllowedChannels = parsed.allowed_channels;
|
|
40148
40109
|
return {
|
|
40149
40110
|
channel: "discord",
|
|
40150
40111
|
enabled: parsed.enabled !== false,
|
|
40151
40112
|
token: String(parsed.token ?? ""),
|
|
40152
40113
|
dmPolicy: parsed.dm_policy ?? "pairing",
|
|
40153
|
-
allowedUsers: parsed.allowed_users ?? []
|
|
40114
|
+
allowedUsers: parsed.allowed_users ?? [],
|
|
40115
|
+
allowedChannels: Array.isArray(rawAllowedChannels) ? rawAllowedChannels : undefined
|
|
40154
40116
|
};
|
|
40155
40117
|
}
|
|
40156
40118
|
};
|
|
@@ -40183,6 +40145,11 @@ function cloneAccount(account) {
|
|
|
40183
40145
|
if (account.channel === "telegram") {
|
|
40184
40146
|
cloned.binding = { ...account.binding };
|
|
40185
40147
|
}
|
|
40148
|
+
if (account.channel === "discord" && account.allowedChannels) {
|
|
40149
|
+
cloned.allowedChannels = [
|
|
40150
|
+
...account.allowedChannels
|
|
40151
|
+
];
|
|
40152
|
+
}
|
|
40186
40153
|
return cloned;
|
|
40187
40154
|
}
|
|
40188
40155
|
function normalizeLoadedAccount(account) {
|
|
@@ -40226,6 +40193,7 @@ function makeDefaultLegacyAccount(channelId) {
|
|
|
40226
40193
|
token: config.token,
|
|
40227
40194
|
dmPolicy: config.dmPolicy,
|
|
40228
40195
|
allowedUsers: [...config.allowedUsers],
|
|
40196
|
+
allowedChannels: config.allowedChannels ? [...config.allowedChannels] : undefined,
|
|
40229
40197
|
agentId: null,
|
|
40230
40198
|
createdAt: now,
|
|
40231
40199
|
updatedAt: now
|
|
@@ -44112,6 +44080,16 @@ var init_plugin2 = __esm(() => {
|
|
|
44112
44080
|
};
|
|
44113
44081
|
});
|
|
44114
44082
|
|
|
44083
|
+
// src/channels/discord/channelGating.ts
|
|
44084
|
+
function isDiscordGuildChannelAllowed(params) {
|
|
44085
|
+
const { channelId, parentChannelId, isThread, allowedChannels } = params;
|
|
44086
|
+
if (!allowedChannels || allowedChannels.length === 0) {
|
|
44087
|
+
return true;
|
|
44088
|
+
}
|
|
44089
|
+
const gateChannelId = isThread ? parentChannelId ?? channelId : channelId;
|
|
44090
|
+
return allowedChannels.includes(gateChannelId);
|
|
44091
|
+
}
|
|
44092
|
+
|
|
44115
44093
|
// src/channels/discord/media.ts
|
|
44116
44094
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
44117
44095
|
import { mkdirSync as mkdirSync11 } from "node:fs";
|
|
@@ -44594,6 +44572,13 @@ function createDiscordAdapter(config) {
|
|
|
44594
44572
|
}
|
|
44595
44573
|
if (!isThread && !wasMentioned)
|
|
44596
44574
|
return;
|
|
44575
|
+
if (!isDiscordGuildChannelAllowed({
|
|
44576
|
+
channelId: message.channelId,
|
|
44577
|
+
parentChannelId: message.channel.parentId ?? null,
|
|
44578
|
+
isThread,
|
|
44579
|
+
allowedChannels: config.allowedChannels
|
|
44580
|
+
}))
|
|
44581
|
+
return;
|
|
44597
44582
|
if (markIngressMessageSeen(message.channelId, message.id))
|
|
44598
44583
|
return;
|
|
44599
44584
|
let effectiveChatId = message.channelId;
|
|
@@ -44657,6 +44642,13 @@ function createDiscordAdapter(config) {
|
|
|
44657
44642
|
const isThread = msg.channel && "isThread" in msg.channel && typeof msg.channel.isThread === "function" && msg.channel.isThread();
|
|
44658
44643
|
if (chatType === "channel" && !isThread)
|
|
44659
44644
|
return;
|
|
44645
|
+
if (chatType === "channel" && isThread && !isDiscordGuildChannelAllowed({
|
|
44646
|
+
channelId,
|
|
44647
|
+
parentChannelId: msg.channel.parentId ?? null,
|
|
44648
|
+
isThread: true,
|
|
44649
|
+
allowedChannels: config.allowedChannels
|
|
44650
|
+
}))
|
|
44651
|
+
return;
|
|
44660
44652
|
const inbound = {
|
|
44661
44653
|
channel: "discord",
|
|
44662
44654
|
accountId: config.accountId,
|
|
@@ -44973,12 +44965,12 @@ Bind to agent ${envAgentId}? [Y/n]: `);
|
|
|
44973
44965
|
}
|
|
44974
44966
|
if (!agentId) {
|
|
44975
44967
|
const agentInput = await rl.question(`
|
|
44976
|
-
Agent ID to bind this bot to (required for @mention routing): `);
|
|
44968
|
+
Agent ID to bind this bot to (required for DM and @mention routing): `);
|
|
44977
44969
|
agentId = agentInput.trim() || null;
|
|
44978
44970
|
}
|
|
44979
44971
|
if (!agentId) {
|
|
44980
44972
|
console.log(`
|
|
44981
|
-
Warning: No agent bound. DM pairing will still work, but guild @mentions won't route until you bind an agent.`);
|
|
44973
|
+
Warning: No agent bound. DM pairing will still work, but open/allowlist DMs and guild @mentions won't route until you bind an agent.`);
|
|
44982
44974
|
console.log(" You can bind later: letta channels bind --channel discord --agent <id>");
|
|
44983
44975
|
console.log(` Or set agentId in ~/.letta/channels/discord/accounts.json
|
|
44984
44976
|
`);
|
|
@@ -45829,6 +45821,17 @@ function buildSlackConversationSummary(msg) {
|
|
|
45829
45821
|
}
|
|
45830
45822
|
return `[Slack] Thread${channelLabel || ` ${msg.chatId}`}`;
|
|
45831
45823
|
}
|
|
45824
|
+
function buildDiscordConversationSummary(msg) {
|
|
45825
|
+
if (msg.chatType === "direct") {
|
|
45826
|
+
return `[Discord] DM with ${msg.senderName?.trim() || msg.senderId}`;
|
|
45827
|
+
}
|
|
45828
|
+
const preview = truncateChannelSummaryPreview(msg.text);
|
|
45829
|
+
const channelLabel = msg.chatLabel && msg.chatLabel !== msg.chatId ? ` in ${msg.chatLabel}` : "";
|
|
45830
|
+
if (preview) {
|
|
45831
|
+
return `[Discord] Thread${channelLabel}: ${preview}`;
|
|
45832
|
+
}
|
|
45833
|
+
return `[Discord] Thread${channelLabel || ` ${msg.chatId}`}`;
|
|
45834
|
+
}
|
|
45832
45835
|
function buildChannelTurnSource(route, msg) {
|
|
45833
45836
|
return {
|
|
45834
45837
|
channel: msg.channel,
|
|
@@ -46212,7 +46215,7 @@ class ChannelRegistry {
|
|
|
46212
46215
|
});
|
|
46213
46216
|
return;
|
|
46214
46217
|
}
|
|
46215
|
-
if (msg.channel === "discord" && config.channel === "discord" && msg.chatType === "channel") {
|
|
46218
|
+
if (msg.channel === "discord" && config.channel === "discord" && (msg.chatType === "channel" || config.dmPolicy !== "pairing")) {
|
|
46216
46219
|
const discordResult = await this.ensureDiscordRoute(adapter, msg, config);
|
|
46217
46220
|
if (!discordResult) {
|
|
46218
46221
|
return;
|
|
@@ -46361,7 +46364,7 @@ class ChannelRegistry {
|
|
|
46361
46364
|
if (!config.agentId) {
|
|
46362
46365
|
throw new Error("Discord bot is missing an agent binding.");
|
|
46363
46366
|
}
|
|
46364
|
-
const conversationId = await this.createConversationForAgent(config.agentId,
|
|
46367
|
+
const conversationId = await this.createConversationForAgent(config.agentId, buildDiscordConversationSummary(msg));
|
|
46365
46368
|
const now = new Date().toISOString();
|
|
46366
46369
|
const route = {
|
|
46367
46370
|
accountId: config.accountId,
|
|
@@ -46384,6 +46387,10 @@ class ChannelRegistry {
|
|
|
46384
46387
|
` + "Open Channels > Discord in Letta Code, choose which agent this bot should represent, and try again.");
|
|
46385
46388
|
return null;
|
|
46386
46389
|
}
|
|
46390
|
+
if (msg.chatType === "direct" && config.dmPolicy === "allowlist" && !config.allowedUsers.includes(msg.senderId)) {
|
|
46391
|
+
await adapter.sendDirectReply(msg.chatId, "You are not on the allowed users list for this Discord bot.");
|
|
46392
|
+
return null;
|
|
46393
|
+
}
|
|
46387
46394
|
const accountId = msg.accountId ?? LEGACY_CHANNEL_ACCOUNT_ID;
|
|
46388
46395
|
const routeThreadId = msg.threadId ?? null;
|
|
46389
46396
|
let route = getRoute(msg.channel, msg.chatId, accountId, routeThreadId);
|
|
@@ -46394,7 +46401,7 @@ class ChannelRegistry {
|
|
|
46394
46401
|
if (route) {
|
|
46395
46402
|
return { route, isFirstRouteTurn: false };
|
|
46396
46403
|
}
|
|
46397
|
-
if (!msg.isMention) {
|
|
46404
|
+
if (msg.chatType === "channel" && !msg.isMention) {
|
|
46398
46405
|
return null;
|
|
46399
46406
|
}
|
|
46400
46407
|
return {
|
|
@@ -79479,6 +79486,7 @@ function toAccountSnapshot(account) {
|
|
|
79479
79486
|
running,
|
|
79480
79487
|
dmPolicy: account.dmPolicy,
|
|
79481
79488
|
allowedUsers: [...account.allowedUsers],
|
|
79489
|
+
allowedChannels: [...account.allowedChannels ?? []],
|
|
79482
79490
|
hasToken: account.token.trim().length > 0,
|
|
79483
79491
|
agentId: account.agentId,
|
|
79484
79492
|
createdAt: account.createdAt,
|
|
@@ -79533,6 +79541,7 @@ function createAccountFromPatch(channelId, accountId, patch) {
|
|
|
79533
79541
|
agentId: patch.agentId ?? null,
|
|
79534
79542
|
dmPolicy: patch.dmPolicy ?? "pairing",
|
|
79535
79543
|
allowedUsers: patch.allowedUsers ?? [],
|
|
79544
|
+
allowedChannels: patch.allowedChannels ?? [],
|
|
79536
79545
|
createdAt: now,
|
|
79537
79546
|
updatedAt: now
|
|
79538
79547
|
};
|
|
@@ -79576,6 +79585,7 @@ function mergeAccountPatch(existing, patch) {
|
|
|
79576
79585
|
agentId: patch.agentId ?? existing.agentId,
|
|
79577
79586
|
dmPolicy: patch.dmPolicy ?? existing.dmPolicy,
|
|
79578
79587
|
allowedUsers: patch.allowedUsers ?? existing.allowedUsers,
|
|
79588
|
+
allowedChannels: patch.allowedChannels ?? existing.allowedChannels,
|
|
79579
79589
|
updatedAt: nextUpdatedAt
|
|
79580
79590
|
};
|
|
79581
79591
|
}
|
|
@@ -79654,6 +79664,7 @@ function getChannelConfigSnapshot(channelId, accountId) {
|
|
|
79654
79664
|
enabled: account.enabled,
|
|
79655
79665
|
dmPolicy: account.dmPolicy,
|
|
79656
79666
|
allowedUsers: [...account.allowedUsers],
|
|
79667
|
+
allowedChannels: [...account.allowedChannels ?? []],
|
|
79657
79668
|
hasToken: account.token.trim().length > 0
|
|
79658
79669
|
};
|
|
79659
79670
|
}
|
|
@@ -79683,6 +79694,7 @@ async function setChannelConfigLive(channelId, patch, accountId) {
|
|
|
79683
79694
|
mode: patch.mode,
|
|
79684
79695
|
dmPolicy: patch.dmPolicy,
|
|
79685
79696
|
allowedUsers: patch.allowedUsers,
|
|
79697
|
+
allowedChannels: patch.allowedChannels,
|
|
79686
79698
|
displayName: existing.displayName
|
|
79687
79699
|
});
|
|
79688
79700
|
shouldRefreshDisplayName = channelId === "telegram" || channelId === "discord" ? patch.token !== undefined : patch.botToken !== undefined || patch.appToken !== undefined;
|
|
@@ -79694,7 +79706,8 @@ async function setChannelConfigLive(channelId, patch, accountId) {
|
|
|
79694
79706
|
appToken: patch.appToken,
|
|
79695
79707
|
mode: patch.mode,
|
|
79696
79708
|
dmPolicy: patch.dmPolicy,
|
|
79697
|
-
allowedUsers: patch.allowedUsers
|
|
79709
|
+
allowedUsers: patch.allowedUsers,
|
|
79710
|
+
allowedChannels: patch.allowedChannels
|
|
79698
79711
|
}, accountId ? { accountId } : undefined);
|
|
79699
79712
|
targetAccountId = created.accountId;
|
|
79700
79713
|
shouldRefreshDisplayName = true;
|
|
@@ -89192,8 +89205,29 @@ var init_cwd = __esm(() => {
|
|
|
89192
89205
|
init_remote_settings();
|
|
89193
89206
|
});
|
|
89194
89207
|
|
|
89195
|
-
// src/websocket/listener/
|
|
89208
|
+
// src/websocket/listener/transport.ts
|
|
89196
89209
|
import WebSocket from "ws";
|
|
89210
|
+
|
|
89211
|
+
class LocalListenerTransport {
|
|
89212
|
+
kind = "local";
|
|
89213
|
+
bufferedAmount = 0;
|
|
89214
|
+
isOpen() {
|
|
89215
|
+
return true;
|
|
89216
|
+
}
|
|
89217
|
+
send(_data) {}
|
|
89218
|
+
}
|
|
89219
|
+
function isListenerTransportOpen(transport) {
|
|
89220
|
+
if ("isOpen" in transport && typeof transport.isOpen === "function") {
|
|
89221
|
+
return transport.isOpen();
|
|
89222
|
+
}
|
|
89223
|
+
return transport.readyState === WebSocket.OPEN;
|
|
89224
|
+
}
|
|
89225
|
+
function getListenerTransportKind(transport) {
|
|
89226
|
+
return "kind" in transport ? transport.kind : "websocket";
|
|
89227
|
+
}
|
|
89228
|
+
var init_transport = () => {};
|
|
89229
|
+
|
|
89230
|
+
// src/websocket/listener/approval.ts
|
|
89197
89231
|
function rememberPendingApprovalBatchIds(runtime, pendingApprovals, batchId) {
|
|
89198
89232
|
for (const approval of pendingApprovals) {
|
|
89199
89233
|
if (approval.toolCallId) {
|
|
@@ -89327,7 +89361,7 @@ function rejectPendingApprovalResolvers(runtime, reason) {
|
|
|
89327
89361
|
evictConversationRuntimeIfIdle(runtime);
|
|
89328
89362
|
}
|
|
89329
89363
|
function requestApprovalOverWS(runtime, socket, requestId, controlRequest) {
|
|
89330
|
-
if (socket
|
|
89364
|
+
if (!isListenerTransportOpen(socket)) {
|
|
89331
89365
|
return Promise.reject(new Error("WebSocket not open"));
|
|
89332
89366
|
}
|
|
89333
89367
|
const abortSignal = runtime.activeAbortController?.signal ?? null;
|
|
@@ -89390,6 +89424,7 @@ function requestApprovalOverWS(runtime, socket, requestId, controlRequest) {
|
|
|
89390
89424
|
}
|
|
89391
89425
|
var init_approval = __esm(async () => {
|
|
89392
89426
|
init_runtime4();
|
|
89427
|
+
init_transport();
|
|
89393
89428
|
await init_protocol_outbound();
|
|
89394
89429
|
});
|
|
89395
89430
|
|
|
@@ -93226,7 +93261,6 @@ var init_diffPreview = __esm(() => {
|
|
|
93226
93261
|
|
|
93227
93262
|
// src/websocket/listener/turn-approval.ts
|
|
93228
93263
|
import { readFile as readFile12 } from "node:fs/promises";
|
|
93229
|
-
import WebSocket2 from "ws";
|
|
93230
93264
|
function getChannelApprovalSourceScopeKey(source) {
|
|
93231
93265
|
return [
|
|
93232
93266
|
source.channel,
|
|
@@ -93512,7 +93546,7 @@ async function handleApprovalStop(params) {
|
|
|
93512
93546
|
return interruptTermination();
|
|
93513
93547
|
}
|
|
93514
93548
|
const onFileWrite = (filePath, content) => {
|
|
93515
|
-
if (socket
|
|
93549
|
+
if (isListenerTransportOpen(socket)) {
|
|
93516
93550
|
socket.send(JSON.stringify({
|
|
93517
93551
|
type: "file_ops",
|
|
93518
93552
|
path: filePath,
|
|
@@ -93639,6 +93673,7 @@ var init_turn_approval = __esm(async () => {
|
|
|
93639
93673
|
init_formatDenial();
|
|
93640
93674
|
init_interactivePolicy();
|
|
93641
93675
|
init_skill_injection();
|
|
93676
|
+
init_transport();
|
|
93642
93677
|
await __promiseAll([
|
|
93643
93678
|
init_approval_execution(),
|
|
93644
93679
|
init_approval(),
|
|
@@ -94720,7 +94755,6 @@ var init_commands = __esm(async () => {
|
|
|
94720
94755
|
import { appendFileSync as appendFileSync4, mkdirSync as mkdirSync22 } from "node:fs";
|
|
94721
94756
|
import { dirname as dirname16 } from "node:path";
|
|
94722
94757
|
import { performance as performance2 } from "node:perf_hooks";
|
|
94723
|
-
import WebSocket3 from "ws";
|
|
94724
94758
|
function getProtocolPerfKey(message) {
|
|
94725
94759
|
if (message.type === "stream_delta" && "delta" in message) {
|
|
94726
94760
|
const delta = message.delta;
|
|
@@ -94942,10 +94976,11 @@ function buildDeviceStatus(runtime, params) {
|
|
|
94942
94976
|
}
|
|
94943
94977
|
})();
|
|
94944
94978
|
const systemPromptDoctorState = scopedAgentId ? getSystemPromptDoctorState(scopedAgentId) : null;
|
|
94979
|
+
const transport = listener.transport ?? listener.socket;
|
|
94945
94980
|
return {
|
|
94946
94981
|
current_connection_id: listener.connectionId,
|
|
94947
94982
|
connection_name: listener.connectionName,
|
|
94948
|
-
is_online:
|
|
94983
|
+
is_online: transport ? isListenerTransportOpen(transport) : false,
|
|
94949
94984
|
is_processing: !!conversationRuntime?.isProcessing,
|
|
94950
94985
|
current_permission_mode: conversationPermissionModeState.mode,
|
|
94951
94986
|
current_working_directory: resolvedCwd,
|
|
@@ -95015,7 +95050,7 @@ function setLoopStatus(runtime, status, scope) {
|
|
|
95015
95050
|
emitLoopStatusIfOpen(runtime, scope);
|
|
95016
95051
|
}
|
|
95017
95052
|
function emitProtocolV2Message(socket, runtime, message, scope) {
|
|
95018
|
-
if (socket
|
|
95053
|
+
if (!isListenerTransportOpen(socket)) {
|
|
95019
95054
|
return;
|
|
95020
95055
|
}
|
|
95021
95056
|
const listener = getListenerRuntime(runtime);
|
|
@@ -95083,14 +95118,16 @@ function emitLoopStatusUpdate(socket, runtime, scope) {
|
|
|
95083
95118
|
}
|
|
95084
95119
|
function emitLoopStatusIfOpen(runtime, scope) {
|
|
95085
95120
|
const listener = getListenerRuntime(runtime);
|
|
95086
|
-
|
|
95087
|
-
|
|
95121
|
+
const transport = listener?.transport ?? listener?.socket;
|
|
95122
|
+
if (transport && isListenerTransportOpen(transport)) {
|
|
95123
|
+
emitLoopStatusUpdate(transport, runtime, scope);
|
|
95088
95124
|
}
|
|
95089
95125
|
}
|
|
95090
95126
|
function emitDeviceStatusIfOpen(runtime, scope) {
|
|
95091
95127
|
const listener = getListenerRuntime(runtime);
|
|
95092
|
-
|
|
95093
|
-
|
|
95128
|
+
const transport = listener?.transport ?? listener?.socket;
|
|
95129
|
+
if (transport && isListenerTransportOpen(transport)) {
|
|
95130
|
+
emitDeviceStatusUpdate(transport, runtime, scope);
|
|
95094
95131
|
}
|
|
95095
95132
|
}
|
|
95096
95133
|
function emitQueueUpdate(socket, runtime, scope) {
|
|
@@ -95148,8 +95185,9 @@ function emitDequeuedUserMessage(socket, runtime, incoming, batch) {
|
|
|
95148
95185
|
}
|
|
95149
95186
|
function emitQueueUpdateIfOpen(runtime, scope) {
|
|
95150
95187
|
const listener = getListenerRuntime(runtime);
|
|
95151
|
-
|
|
95152
|
-
|
|
95188
|
+
const transport = listener?.transport ?? listener?.socket;
|
|
95189
|
+
if (transport && isListenerTransportOpen(transport)) {
|
|
95190
|
+
emitQueueUpdate(transport, runtime, scope);
|
|
95153
95191
|
}
|
|
95154
95192
|
}
|
|
95155
95193
|
function emitStateSync(socket, runtime, scope) {
|
|
@@ -95204,8 +95242,9 @@ function emitSubagentStateUpdate(socket, runtime, scope) {
|
|
|
95204
95242
|
}
|
|
95205
95243
|
function emitSubagentStateIfOpen(runtime, scope) {
|
|
95206
95244
|
const listener = getListenerRuntime(runtime);
|
|
95207
|
-
|
|
95208
|
-
|
|
95245
|
+
const transport = listener?.transport ?? listener?.socket;
|
|
95246
|
+
if (transport && isListenerTransportOpen(transport)) {
|
|
95247
|
+
emitSubagentStateUpdate(transport, runtime, scope);
|
|
95209
95248
|
}
|
|
95210
95249
|
}
|
|
95211
95250
|
function scheduleQueueEmit(runtime, scope) {
|
|
@@ -95301,6 +95340,7 @@ var init_protocol_outbound = __esm(async () => {
|
|
|
95301
95340
|
init_cwd();
|
|
95302
95341
|
init_permissionMode();
|
|
95303
95342
|
init_runtime4();
|
|
95343
|
+
init_transport();
|
|
95304
95344
|
await init_commands();
|
|
95305
95345
|
PROTOCOL_PERF_ENV_VALUES = new Set(["1", "true", "yes"]);
|
|
95306
95346
|
PROTOCOL_PERF_ENABLED = PROTOCOL_PERF_ENV_VALUES.has((process.env.LETTA_LISTENER_PERF ?? "").toLowerCase());
|
|
@@ -95873,7 +95913,7 @@ var init_toolset_labels = __esm(() => {
|
|
|
95873
95913
|
|
|
95874
95914
|
// src/websocket/terminalHandler.ts
|
|
95875
95915
|
import * as os4 from "node:os";
|
|
95876
|
-
import
|
|
95916
|
+
import WebSocket2 from "ws";
|
|
95877
95917
|
function getDefaultShell() {
|
|
95878
95918
|
if (os4.platform() === "win32") {
|
|
95879
95919
|
return process.env.COMSPEC || "cmd.exe";
|
|
@@ -95881,7 +95921,7 @@ function getDefaultShell() {
|
|
|
95881
95921
|
return process.env.SHELL || "/bin/zsh";
|
|
95882
95922
|
}
|
|
95883
95923
|
function sendTerminalMessage(socket, message) {
|
|
95884
|
-
if (socket.readyState ===
|
|
95924
|
+
if (socket.readyState === WebSocket2.OPEN) {
|
|
95885
95925
|
socket.send(JSON.stringify(message));
|
|
95886
95926
|
}
|
|
95887
95927
|
}
|
|
@@ -96596,9 +96636,10 @@ function isChannelId(value) {
|
|
|
96596
96636
|
function hasValidChannelPolicyFields(config) {
|
|
96597
96637
|
const hasValidDmPolicy = config.dm_policy === undefined || config.dm_policy === "pairing" || config.dm_policy === "allowlist" || config.dm_policy === "open";
|
|
96598
96638
|
const hasValidAllowedUsers = config.allowed_users === undefined || Array.isArray(config.allowed_users) && config.allowed_users.every((entry) => typeof entry === "string");
|
|
96639
|
+
const hasValidAllowedChannels = config.allowed_channels === undefined || Array.isArray(config.allowed_channels) && config.allowed_channels.every((entry) => typeof entry === "string");
|
|
96599
96640
|
const hasValidDisplayName = config.display_name === undefined || typeof config.display_name === "string";
|
|
96600
96641
|
const hasValidEnabled = config.enabled === undefined || typeof config.enabled === "boolean";
|
|
96601
|
-
return hasValidDmPolicy && hasValidAllowedUsers && hasValidDisplayName && hasValidEnabled;
|
|
96642
|
+
return hasValidDmPolicy && hasValidAllowedUsers && hasValidAllowedChannels && hasValidDisplayName && hasValidEnabled;
|
|
96602
96643
|
}
|
|
96603
96644
|
function isChannelsListCommand(value) {
|
|
96604
96645
|
if (!value || typeof value !== "object")
|
|
@@ -97025,7 +97066,7 @@ import { execFile as execFile13 } from "node:child_process";
|
|
|
97025
97066
|
import { lstat as lstat2, realpath as realpath3, stat as stat6 } from "node:fs/promises";
|
|
97026
97067
|
import path25 from "node:path";
|
|
97027
97068
|
import { promisify as promisify11 } from "node:util";
|
|
97028
|
-
import
|
|
97069
|
+
import WebSocket3 from "ws";
|
|
97029
97070
|
async function isGitWorktreeRoot(dir) {
|
|
97030
97071
|
try {
|
|
97031
97072
|
const stats = await lstat2(path25.join(dir, ".git"));
|
|
@@ -97048,7 +97089,7 @@ function trackListenerError(errorType, error, context3) {
|
|
|
97048
97089
|
});
|
|
97049
97090
|
}
|
|
97050
97091
|
function safeSocketSend(socket, payload, errorType, context3) {
|
|
97051
|
-
if (socket.readyState !==
|
|
97092
|
+
if (socket.readyState !== WebSocket3.OPEN) {
|
|
97052
97093
|
return false;
|
|
97053
97094
|
}
|
|
97054
97095
|
try {
|
|
@@ -97063,6 +97104,22 @@ function safeSocketSend(socket, payload, errorType, context3) {
|
|
|
97063
97104
|
return false;
|
|
97064
97105
|
}
|
|
97065
97106
|
}
|
|
97107
|
+
function safeTransportSend(transport, payload, errorType, context3) {
|
|
97108
|
+
if (!isListenerTransportOpen(transport)) {
|
|
97109
|
+
return false;
|
|
97110
|
+
}
|
|
97111
|
+
try {
|
|
97112
|
+
const serialized = typeof payload === "string" ? payload : JSON.stringify(payload);
|
|
97113
|
+
transport.send(serialized);
|
|
97114
|
+
return true;
|
|
97115
|
+
} catch (error) {
|
|
97116
|
+
trackListenerError(errorType, error, context3);
|
|
97117
|
+
if (isDebugEnabled()) {
|
|
97118
|
+
console.error(`[Listen] ${context3} send failed:`, error);
|
|
97119
|
+
}
|
|
97120
|
+
return false;
|
|
97121
|
+
}
|
|
97122
|
+
}
|
|
97066
97123
|
function runDetachedListenerTask(commandName, task2) {
|
|
97067
97124
|
task2().catch((error) => {
|
|
97068
97125
|
trackListenerError(`listener_${commandName}_failed`, error, `listener_${commandName}`);
|
|
@@ -97844,6 +97901,7 @@ async function handleChannelsProtocolCommand(parsed, socket, runtime, opts, proc
|
|
|
97844
97901
|
enabled: snapshot.enabled,
|
|
97845
97902
|
dm_policy: snapshot.dmPolicy,
|
|
97846
97903
|
allowed_users: snapshot.allowedUsers,
|
|
97904
|
+
allowed_channels: snapshot.allowedChannels,
|
|
97847
97905
|
has_token: snapshot.hasToken
|
|
97848
97906
|
};
|
|
97849
97907
|
}
|
|
@@ -97889,6 +97947,7 @@ async function handleChannelsProtocolCommand(parsed, socket, runtime, opts, proc
|
|
|
97889
97947
|
running: snapshot.running,
|
|
97890
97948
|
dm_policy: snapshot.dmPolicy,
|
|
97891
97949
|
allowed_users: snapshot.allowedUsers,
|
|
97950
|
+
allowed_channels: snapshot.allowedChannels,
|
|
97892
97951
|
has_token: snapshot.hasToken,
|
|
97893
97952
|
agent_id: snapshot.agentId,
|
|
97894
97953
|
created_at: snapshot.createdAt,
|
|
@@ -98004,7 +98063,8 @@ async function handleChannelsProtocolCommand(parsed, socket, runtime, opts, proc
|
|
|
98004
98063
|
agentId: "agent_id" in parsed.account ? parsed.account.agent_id : undefined,
|
|
98005
98064
|
defaultPermissionMode: "default_permission_mode" in parsed.account ? parsed.account.default_permission_mode : undefined,
|
|
98006
98065
|
dmPolicy: parsed.account.dm_policy,
|
|
98007
|
-
allowedUsers: parsed.account.allowed_users
|
|
98066
|
+
allowedUsers: parsed.account.allowed_users,
|
|
98067
|
+
allowedChannels: "allowed_channels" in parsed.account ? parsed.account.allowed_channels : undefined
|
|
98008
98068
|
}, {
|
|
98009
98069
|
accountId: "account_id" in parsed.account ? parsed.account.account_id : undefined
|
|
98010
98070
|
});
|
|
@@ -98045,7 +98105,8 @@ async function handleChannelsProtocolCommand(parsed, socket, runtime, opts, proc
|
|
|
98045
98105
|
agentId: "agent_id" in parsed.patch ? parsed.patch.agent_id : undefined,
|
|
98046
98106
|
defaultPermissionMode: "default_permission_mode" in parsed.patch ? parsed.patch.default_permission_mode : undefined,
|
|
98047
98107
|
dmPolicy: parsed.patch.dm_policy,
|
|
98048
|
-
allowedUsers: parsed.patch.allowed_users
|
|
98108
|
+
allowedUsers: parsed.patch.allowed_users,
|
|
98109
|
+
allowedChannels: "allowed_channels" in parsed.patch ? parsed.patch.allowed_channels : undefined
|
|
98049
98110
|
});
|
|
98050
98111
|
const shouldRefreshDisplayName = !("display_name" in parsed.patch) && (parsed.channel_id === "telegram" ? "token" in parsed.patch : ("bot_token" in parsed.patch) || ("app_token" in parsed.patch));
|
|
98051
98112
|
const account = shouldRefreshDisplayName ? await refreshChannelAccountDisplayNameLive2(parsed.channel_id, parsed.account_id, { force: true }) : updated;
|
|
@@ -98245,7 +98306,8 @@ async function handleChannelsProtocolCommand(parsed, socket, runtime, opts, proc
|
|
|
98245
98306
|
appToken: "app_token" in parsed.config ? parsed.config.app_token : undefined,
|
|
98246
98307
|
mode: "mode" in parsed.config ? parsed.config.mode : undefined,
|
|
98247
98308
|
dmPolicy: parsed.config.dm_policy,
|
|
98248
|
-
allowedUsers: parsed.config.allowed_users
|
|
98309
|
+
allowedUsers: parsed.config.allowed_users,
|
|
98310
|
+
allowedChannels: "allowed_channels" in parsed.config ? parsed.config.allowed_channels : undefined
|
|
98249
98311
|
}, parsed.account_id);
|
|
98250
98312
|
if (snapshot.enabled) {
|
|
98251
98313
|
await wireChannelIngress(runtime, socket, opts, processQueuedTurn);
|
|
@@ -98833,13 +98895,17 @@ async function wireChannelIngress(listener, socket, opts, processQueuedTurn) {
|
|
|
98833
98895
|
}
|
|
98834
98896
|
function handleChannelRegistryEvent(event, socket, runtime) {
|
|
98835
98897
|
if (event.type === "pairings_updated") {
|
|
98836
|
-
|
|
98837
|
-
|
|
98898
|
+
if (socket instanceof WebSocket3) {
|
|
98899
|
+
emitChannelPairingsUpdated(socket, event.channelId);
|
|
98900
|
+
emitChannelsUpdated(socket, event.channelId);
|
|
98901
|
+
}
|
|
98838
98902
|
return;
|
|
98839
98903
|
}
|
|
98840
98904
|
if (event.type === "targets_updated") {
|
|
98841
|
-
|
|
98842
|
-
|
|
98905
|
+
if (socket instanceof WebSocket3) {
|
|
98906
|
+
emitChannelTargetsUpdated(socket, event.channelId);
|
|
98907
|
+
emitChannelsUpdated(socket, event.channelId);
|
|
98908
|
+
}
|
|
98843
98909
|
return;
|
|
98844
98910
|
}
|
|
98845
98911
|
const permissionModeState = getOrCreateConversationPermissionModeStateRef(runtime, event.agentId, event.conversationId);
|
|
@@ -99184,6 +99250,7 @@ function createRuntime() {
|
|
|
99184
99250
|
const bootWorkingDirectory = getCurrentWorkingDirectory();
|
|
99185
99251
|
return {
|
|
99186
99252
|
socket: null,
|
|
99253
|
+
transport: null,
|
|
99187
99254
|
heartbeatInterval: null,
|
|
99188
99255
|
reconnectTimeout: null,
|
|
99189
99256
|
intentionallyClosed: false,
|
|
@@ -99232,17 +99299,100 @@ function stopRuntime(runtime, suppressCallbacks) {
|
|
|
99232
99299
|
runtime.queuedSystemPromptRecompileByConversation.clear();
|
|
99233
99300
|
stopAllWorktreeWatchers(runtime);
|
|
99234
99301
|
if (!runtime.socket) {
|
|
99302
|
+
runtime.transport = null;
|
|
99235
99303
|
return;
|
|
99236
99304
|
}
|
|
99237
99305
|
const socket = runtime.socket;
|
|
99238
99306
|
runtime.socket = null;
|
|
99307
|
+
runtime.transport = null;
|
|
99239
99308
|
if (suppressCallbacks) {
|
|
99240
99309
|
socket.removeAllListeners();
|
|
99241
99310
|
}
|
|
99242
|
-
if (socket.readyState ===
|
|
99311
|
+
if (socket.readyState === WebSocket3.OPEN || socket.readyState === WebSocket3.CONNECTING) {
|
|
99243
99312
|
socket.close();
|
|
99244
99313
|
}
|
|
99245
99314
|
}
|
|
99315
|
+
async function startConnectedListenerRuntime(runtime, transport, opts, processQueuedTurn, options = {}) {
|
|
99316
|
+
if (runtime !== getActiveRuntime() || runtime.intentionallyClosed) {
|
|
99317
|
+
return;
|
|
99318
|
+
}
|
|
99319
|
+
const shouldStartHeartbeat = options.startHeartbeat !== false;
|
|
99320
|
+
const shouldStartCronScheduler = options.startCronScheduler !== false;
|
|
99321
|
+
runtime.transport = transport;
|
|
99322
|
+
safeEmitWsEvent("recv", "lifecycle", {
|
|
99323
|
+
type: getListenerTransportKind(transport) === "websocket" ? "_ws_open" : "_local_open"
|
|
99324
|
+
});
|
|
99325
|
+
runtime.hasSuccessfulConnection = true;
|
|
99326
|
+
runtime.everConnected = true;
|
|
99327
|
+
opts.onConnected(opts.connectionId);
|
|
99328
|
+
if (runtime.conversationRuntimes.size === 0) {
|
|
99329
|
+
emitLoopStatusUpdate(transport, runtime);
|
|
99330
|
+
} else {
|
|
99331
|
+
for (const reminderState of runtime.reminderStateByConversation.values()) {
|
|
99332
|
+
resetSharedReminderState(reminderState);
|
|
99333
|
+
}
|
|
99334
|
+
for (const contextTracker of runtime.contextTrackerByConversation.values()) {
|
|
99335
|
+
resetContextHistory(contextTracker);
|
|
99336
|
+
}
|
|
99337
|
+
for (const conversationRuntime of runtime.conversationRuntimes.values()) {
|
|
99338
|
+
const scope = {
|
|
99339
|
+
agent_id: conversationRuntime.agentId,
|
|
99340
|
+
conversation_id: conversationRuntime.conversationId
|
|
99341
|
+
};
|
|
99342
|
+
emitDeviceStatusUpdate(transport, conversationRuntime, scope);
|
|
99343
|
+
emitLoopStatusUpdate(transport, conversationRuntime, scope);
|
|
99344
|
+
}
|
|
99345
|
+
}
|
|
99346
|
+
runtime._unsubscribeSubagentState?.();
|
|
99347
|
+
runtime._unsubscribeSubagentState = subscribe2(() => {
|
|
99348
|
+
if (runtime.conversationRuntimes.size === 0) {
|
|
99349
|
+
emitSubagentStateIfOpen(runtime);
|
|
99350
|
+
return;
|
|
99351
|
+
}
|
|
99352
|
+
for (const conversationRuntime of runtime.conversationRuntimes.values()) {
|
|
99353
|
+
emitSubagentStateIfOpen(runtime, {
|
|
99354
|
+
agent_id: conversationRuntime.agentId,
|
|
99355
|
+
conversation_id: conversationRuntime.conversationId
|
|
99356
|
+
});
|
|
99357
|
+
}
|
|
99358
|
+
});
|
|
99359
|
+
runtime._unsubscribeSubagentStreamEvents?.();
|
|
99360
|
+
runtime._unsubscribeSubagentStreamEvents = subscribeToStreamEvents((subagentId, event) => {
|
|
99361
|
+
if (!isListenerTransportOpen(transport))
|
|
99362
|
+
return;
|
|
99363
|
+
const subagent = getSubagents().find((entry) => entry.id === subagentId);
|
|
99364
|
+
if (subagent?.silent === true) {
|
|
99365
|
+
return;
|
|
99366
|
+
}
|
|
99367
|
+
emitStreamDelta(transport, runtime, event, subagent?.parentAgentId ? {
|
|
99368
|
+
agent_id: subagent.parentAgentId,
|
|
99369
|
+
conversation_id: subagent.parentConversationId ?? "default"
|
|
99370
|
+
} : undefined, subagentId);
|
|
99371
|
+
});
|
|
99372
|
+
setMessageQueueAdder((queuedMessage) => {
|
|
99373
|
+
const targetRuntime = queuedMessage.agentId && queuedMessage.conversationId ? getOrCreateScopedRuntime(runtime, queuedMessage.agentId, queuedMessage.conversationId) : findFallbackRuntime(runtime);
|
|
99374
|
+
if (!targetRuntime?.queueRuntime) {
|
|
99375
|
+
return;
|
|
99376
|
+
}
|
|
99377
|
+
targetRuntime.queueRuntime.enqueue({
|
|
99378
|
+
kind: "task_notification",
|
|
99379
|
+
source: "task_notification",
|
|
99380
|
+
text: queuedMessage.text,
|
|
99381
|
+
agentId: queuedMessage.agentId ?? targetRuntime.agentId ?? undefined,
|
|
99382
|
+
conversationId: queuedMessage.conversationId ?? targetRuntime.conversationId
|
|
99383
|
+
});
|
|
99384
|
+
scheduleQueuePump(targetRuntime, transport, opts, processQueuedTurn);
|
|
99385
|
+
});
|
|
99386
|
+
if (shouldStartHeartbeat) {
|
|
99387
|
+
runtime.heartbeatInterval = setInterval(() => {
|
|
99388
|
+
safeTransportSend(transport, { type: "ping" }, "listener_ping_send_failed", "listener_heartbeat");
|
|
99389
|
+
}, 30000);
|
|
99390
|
+
}
|
|
99391
|
+
if (shouldStartCronScheduler) {
|
|
99392
|
+
startScheduler(transport, opts, processQueuedTurn);
|
|
99393
|
+
}
|
|
99394
|
+
await wireChannelIngress(runtime, transport, opts, processQueuedTurn);
|
|
99395
|
+
}
|
|
99246
99396
|
async function startListenerClient(opts) {
|
|
99247
99397
|
const existingRuntime = getActiveRuntime();
|
|
99248
99398
|
if (existingRuntime) {
|
|
@@ -99257,6 +99407,34 @@ async function startListenerClient(opts) {
|
|
|
99257
99407
|
telemetry.init();
|
|
99258
99408
|
await connectWithRetry(runtime, opts);
|
|
99259
99409
|
}
|
|
99410
|
+
async function startLocalChannelListener(opts) {
|
|
99411
|
+
const existingRuntime = getActiveRuntime();
|
|
99412
|
+
if (existingRuntime) {
|
|
99413
|
+
stopRuntime(existingRuntime, true);
|
|
99414
|
+
}
|
|
99415
|
+
const runtime = createRuntime();
|
|
99416
|
+
runtime.onWsEvent = opts.onWsEvent;
|
|
99417
|
+
runtime.connectionId = opts.connectionId;
|
|
99418
|
+
runtime.connectionName = opts.connectionName;
|
|
99419
|
+
setActiveRuntime(runtime);
|
|
99420
|
+
telemetry.setSurface("websocket");
|
|
99421
|
+
telemetry.init();
|
|
99422
|
+
try {
|
|
99423
|
+
await loadTools();
|
|
99424
|
+
const transport = new LocalListenerTransport;
|
|
99425
|
+
const processQueuedTurn = async (queuedTurn, dequeuedBatch) => {
|
|
99426
|
+
const scopedRuntime = getOrCreateScopedRuntime(runtime, queuedTurn.agentId, queuedTurn.conversationId);
|
|
99427
|
+
await handleIncomingMessage(queuedTurn, transport, scopedRuntime, opts.onStatusChange, opts.connectionId, dequeuedBatch.batchId);
|
|
99428
|
+
};
|
|
99429
|
+
await startConnectedListenerRuntime(runtime, transport, opts, processQueuedTurn, { startHeartbeat: false, startCronScheduler: true });
|
|
99430
|
+
} catch (error) {
|
|
99431
|
+
stopRuntime(runtime, true);
|
|
99432
|
+
if (getActiveRuntime() === runtime) {
|
|
99433
|
+
setActiveRuntime(null);
|
|
99434
|
+
}
|
|
99435
|
+
opts.onError(error instanceof Error ? error : new Error(String(error)));
|
|
99436
|
+
}
|
|
99437
|
+
}
|
|
99260
99438
|
async function listDirectoryHybrid(absDir, indexRoot3, includeFiles) {
|
|
99261
99439
|
let indexedNames;
|
|
99262
99440
|
const indexedFolders = [];
|
|
@@ -99339,7 +99517,7 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
|
|
|
99339
99517
|
const url = new URL(opts.wsUrl);
|
|
99340
99518
|
url.searchParams.set("deviceId", opts.deviceId);
|
|
99341
99519
|
url.searchParams.set("connectionName", opts.connectionName);
|
|
99342
|
-
const socket = new
|
|
99520
|
+
const socket = new WebSocket3(url.toString(), {
|
|
99343
99521
|
headers: {
|
|
99344
99522
|
Authorization: `Bearer ${apiKey}`
|
|
99345
99523
|
}
|
|
@@ -99348,83 +99526,13 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
|
|
|
99348
99526
|
const watchDebounceTimers = new Map;
|
|
99349
99527
|
const cancelledWatches = new Set;
|
|
99350
99528
|
runtime.socket = socket;
|
|
99529
|
+
const transport = socket;
|
|
99351
99530
|
const processQueuedTurn = async (queuedTurn, dequeuedBatch) => {
|
|
99352
99531
|
const scopedRuntime = getOrCreateScopedRuntime(runtime, queuedTurn.agentId, queuedTurn.conversationId);
|
|
99353
|
-
await handleIncomingMessage(queuedTurn,
|
|
99532
|
+
await handleIncomingMessage(queuedTurn, transport, scopedRuntime, opts.onStatusChange, opts.connectionId, dequeuedBatch.batchId);
|
|
99354
99533
|
};
|
|
99355
99534
|
socket.on("open", async () => {
|
|
99356
|
-
|
|
99357
|
-
return;
|
|
99358
|
-
}
|
|
99359
|
-
safeEmitWsEvent("recv", "lifecycle", { type: "_ws_open" });
|
|
99360
|
-
runtime.hasSuccessfulConnection = true;
|
|
99361
|
-
runtime.everConnected = true;
|
|
99362
|
-
opts.onConnected(opts.connectionId);
|
|
99363
|
-
if (runtime.conversationRuntimes.size === 0) {
|
|
99364
|
-
emitLoopStatusUpdate(socket, runtime);
|
|
99365
|
-
} else {
|
|
99366
|
-
for (const reminderState of runtime.reminderStateByConversation.values()) {
|
|
99367
|
-
resetSharedReminderState(reminderState);
|
|
99368
|
-
}
|
|
99369
|
-
for (const contextTracker of runtime.contextTrackerByConversation.values()) {
|
|
99370
|
-
resetContextHistory(contextTracker);
|
|
99371
|
-
}
|
|
99372
|
-
for (const conversationRuntime of runtime.conversationRuntimes.values()) {
|
|
99373
|
-
const scope = {
|
|
99374
|
-
agent_id: conversationRuntime.agentId,
|
|
99375
|
-
conversation_id: conversationRuntime.conversationId
|
|
99376
|
-
};
|
|
99377
|
-
emitDeviceStatusUpdate(socket, conversationRuntime, scope);
|
|
99378
|
-
emitLoopStatusUpdate(socket, conversationRuntime, scope);
|
|
99379
|
-
}
|
|
99380
|
-
}
|
|
99381
|
-
runtime._unsubscribeSubagentState?.();
|
|
99382
|
-
runtime._unsubscribeSubagentState = subscribe2(() => {
|
|
99383
|
-
if (runtime.conversationRuntimes.size === 0) {
|
|
99384
|
-
emitSubagentStateIfOpen(runtime);
|
|
99385
|
-
return;
|
|
99386
|
-
}
|
|
99387
|
-
for (const conversationRuntime of runtime.conversationRuntimes.values()) {
|
|
99388
|
-
emitSubagentStateIfOpen(runtime, {
|
|
99389
|
-
agent_id: conversationRuntime.agentId,
|
|
99390
|
-
conversation_id: conversationRuntime.conversationId
|
|
99391
|
-
});
|
|
99392
|
-
}
|
|
99393
|
-
});
|
|
99394
|
-
runtime._unsubscribeSubagentStreamEvents?.();
|
|
99395
|
-
runtime._unsubscribeSubagentStreamEvents = subscribeToStreamEvents((subagentId, event) => {
|
|
99396
|
-
if (socket.readyState !== WebSocket5.OPEN)
|
|
99397
|
-
return;
|
|
99398
|
-
const subagent = getSubagents().find((entry) => entry.id === subagentId);
|
|
99399
|
-
if (subagent?.silent === true) {
|
|
99400
|
-
return;
|
|
99401
|
-
}
|
|
99402
|
-
emitStreamDelta(socket, runtime, event, subagent?.parentAgentId ? {
|
|
99403
|
-
agent_id: subagent.parentAgentId,
|
|
99404
|
-
conversation_id: subagent.parentConversationId ?? "default"
|
|
99405
|
-
} : undefined, subagentId);
|
|
99406
|
-
});
|
|
99407
|
-
setMessageQueueAdder((queuedMessage) => {
|
|
99408
|
-
const targetRuntime = queuedMessage.agentId && queuedMessage.conversationId ? getOrCreateScopedRuntime(runtime, queuedMessage.agentId, queuedMessage.conversationId) : findFallbackRuntime(runtime);
|
|
99409
|
-
if (!targetRuntime?.queueRuntime) {
|
|
99410
|
-
return;
|
|
99411
|
-
}
|
|
99412
|
-
targetRuntime.queueRuntime.enqueue({
|
|
99413
|
-
kind: "task_notification",
|
|
99414
|
-
source: "task_notification",
|
|
99415
|
-
text: queuedMessage.text,
|
|
99416
|
-
agentId: queuedMessage.agentId ?? targetRuntime.agentId ?? undefined,
|
|
99417
|
-
conversationId: queuedMessage.conversationId ?? targetRuntime.conversationId
|
|
99418
|
-
});
|
|
99419
|
-
scheduleQueuePump(targetRuntime, socket, opts, processQueuedTurn);
|
|
99420
|
-
});
|
|
99421
|
-
runtime.heartbeatInterval = setInterval(() => {
|
|
99422
|
-
if (socket.readyState === WebSocket5.OPEN) {
|
|
99423
|
-
safeSocketSend(socket, { type: "ping" }, "listener_ping_send_failed", "listener_heartbeat");
|
|
99424
|
-
}
|
|
99425
|
-
}, 30000);
|
|
99426
|
-
startScheduler(socket, opts, processQueuedTurn);
|
|
99427
|
-
await wireChannelIngress(runtime, socket, opts, processQueuedTurn);
|
|
99535
|
+
await startConnectedListenerRuntime(runtime, transport, opts, processQueuedTurn, { startHeartbeat: true, startCronScheduler: true });
|
|
99428
99536
|
});
|
|
99429
99537
|
socket.on("message", async (data) => {
|
|
99430
99538
|
const raw = data.toString();
|
|
@@ -100458,7 +100566,7 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
|
|
|
100458
100566
|
}
|
|
100459
100567
|
function isListenerActive() {
|
|
100460
100568
|
const runtime = getActiveRuntime();
|
|
100461
|
-
return runtime !== null && runtime.
|
|
100569
|
+
return runtime !== null && runtime.transport !== null;
|
|
100462
100570
|
}
|
|
100463
100571
|
function stopListenerClient() {
|
|
100464
100572
|
const runtime = getActiveRuntime();
|
|
@@ -100686,6 +100794,7 @@ var init_client4 = __esm(async () => {
|
|
|
100686
100794
|
init_grepInFiles();
|
|
100687
100795
|
init_permissionMode();
|
|
100688
100796
|
init_runtime4();
|
|
100797
|
+
init_transport();
|
|
100689
100798
|
await __promiseAll([
|
|
100690
100799
|
init_scheduler(),
|
|
100691
100800
|
init_manager4(),
|
|
@@ -100781,6 +100890,7 @@ var init_client4 = __esm(async () => {
|
|
|
100781
100890
|
var exports_listen_client = {};
|
|
100782
100891
|
__export(exports_listen_client, {
|
|
100783
100892
|
stopListenerClient: () => stopListenerClient,
|
|
100893
|
+
startLocalChannelListener: () => startLocalChannelListener,
|
|
100784
100894
|
startListenerClient: () => startListenerClient,
|
|
100785
100895
|
resolvePendingApprovalResolver: () => resolvePendingApprovalResolver,
|
|
100786
100896
|
requestApprovalOverWS: () => requestApprovalOverWS,
|
|
@@ -165010,6 +165120,23 @@ function getListenerServerUrl(settings) {
|
|
|
165010
165120
|
const oauthDeps = getListenerOAuthDeps();
|
|
165011
165121
|
return process.env.LETTA_BASE_URL || settings.env?.LETTA_BASE_URL || oauthDeps.LETTA_CLOUD_API_URL;
|
|
165012
165122
|
}
|
|
165123
|
+
function normalizeListenerBaseUrl(url) {
|
|
165124
|
+
return url.trim().replace(/\/+$/, "");
|
|
165125
|
+
}
|
|
165126
|
+
function isCloudListenerServerUrl(serverUrl) {
|
|
165127
|
+
return normalizeListenerBaseUrl(serverUrl) === normalizeListenerBaseUrl(getListenerOAuthDeps().LETTA_CLOUD_API_URL);
|
|
165128
|
+
}
|
|
165129
|
+
async function resolveListenerStartupMode(channelNames) {
|
|
165130
|
+
const settings = await settingsManager.getSettingsWithSecureTokens();
|
|
165131
|
+
const serverUrl = getListenerServerUrl(settings);
|
|
165132
|
+
if (isCloudListenerServerUrl(serverUrl)) {
|
|
165133
|
+
return { kind: "remote", serverUrl };
|
|
165134
|
+
}
|
|
165135
|
+
if (channelNames.length > 0) {
|
|
165136
|
+
return { kind: "local-channels", serverUrl };
|
|
165137
|
+
}
|
|
165138
|
+
return { kind: "unsupported-self-hosted", serverUrl };
|
|
165139
|
+
}
|
|
165013
165140
|
async function refreshListenerAccessToken(settings, deviceId, connectionName) {
|
|
165014
165141
|
const oauthDeps = getListenerOAuthDeps();
|
|
165015
165142
|
const now = Date.now();
|
|
@@ -165065,7 +165192,7 @@ async function resolveListenerRegistrationOptions(deviceId, connectionName) {
|
|
|
165065
165192
|
};
|
|
165066
165193
|
}
|
|
165067
165194
|
let apiKey = settings.env?.LETTA_API_KEY;
|
|
165068
|
-
if (serverUrl
|
|
165195
|
+
if (isCloudListenerServerUrl(serverUrl)) {
|
|
165069
165196
|
const expiresAt = settings.tokenExpiresAt;
|
|
165070
165197
|
if (settings.refreshToken && expiresAt) {
|
|
165071
165198
|
const now = Date.now();
|
|
@@ -165190,6 +165317,52 @@ async function runListenSubcommand(argv) {
|
|
|
165190
165317
|
console.log(`Log file: ${sessionLog.path}`);
|
|
165191
165318
|
try {
|
|
165192
165319
|
const deviceId = settingsManager.getOrCreateDeviceId();
|
|
165320
|
+
const startupMode = await resolveListenerStartupMode(channelNames);
|
|
165321
|
+
if (startupMode.kind === "unsupported-self-hosted") {
|
|
165322
|
+
console.error(`Self-hosted listener registration is not available for ${startupMode.serverUrl}.`);
|
|
165323
|
+
console.error("Start with --channels to run local channel adapters, or unset LETTA_BASE_URL to use Letta API remote environments.");
|
|
165324
|
+
await flushListenerTelemetryEnd("listener_self_hosted_no_channels");
|
|
165325
|
+
return 1;
|
|
165326
|
+
}
|
|
165327
|
+
sessionLog.log(`Session started (debug=${debugMode})`);
|
|
165328
|
+
sessionLog.log(`deviceId: ${deviceId}`);
|
|
165329
|
+
sessionLog.log(`connectionName: ${connectionName}`);
|
|
165330
|
+
if (startupMode.kind === "local-channels") {
|
|
165331
|
+
const connectionId2 = `local-${deviceId}`;
|
|
165332
|
+
sessionLog.log(`Starting local channel listener for ${startupMode.serverUrl}`);
|
|
165333
|
+
sessionLog.log("Skipping environment registration");
|
|
165334
|
+
console.log(`Starting local channel listener for self-hosted server ${startupMode.serverUrl}`);
|
|
165335
|
+
console.log(`Skipping environment registration. Press Ctrl+C to stop.
|
|
165336
|
+
`);
|
|
165337
|
+
const { startLocalChannelListener: startLocalChannelListener2 } = await init_listen_client().then(() => exports_listen_client);
|
|
165338
|
+
await startLocalChannelListener2({
|
|
165339
|
+
connectionId: connectionId2,
|
|
165340
|
+
deviceId,
|
|
165341
|
+
connectionName,
|
|
165342
|
+
onWsEvent: process.env.LETTA_LOG_WS_EVENTS === "1" ? (direction, label, event) => {
|
|
165343
|
+
sessionLog.wsEvent(direction, label, event);
|
|
165344
|
+
} : undefined,
|
|
165345
|
+
onStatusChange: (status) => {
|
|
165346
|
+
sessionLog.log(`status: ${status}`);
|
|
165347
|
+
if (debugMode) {
|
|
165348
|
+
console.log(`[${formatTimestamp3()}] status: ${status}`);
|
|
165349
|
+
}
|
|
165350
|
+
},
|
|
165351
|
+
onConnected: () => {
|
|
165352
|
+
sessionLog.log("Local channel listener ready.");
|
|
165353
|
+
if (debugMode) {
|
|
165354
|
+
console.log(`[${formatTimestamp3()}] Local channel listener ready.`);
|
|
165355
|
+
console.log("");
|
|
165356
|
+
}
|
|
165357
|
+
},
|
|
165358
|
+
onError: (error) => {
|
|
165359
|
+
sessionLog.log(`Error: ${error.message}`);
|
|
165360
|
+
console.error(`[${formatTimestamp3()}] Error: ${error.message}`);
|
|
165361
|
+
exitWithTelemetry(1, "listener_error");
|
|
165362
|
+
}
|
|
165363
|
+
});
|
|
165364
|
+
return new Promise(() => {});
|
|
165365
|
+
}
|
|
165193
165366
|
let registerOptions;
|
|
165194
165367
|
try {
|
|
165195
165368
|
registerOptions = await resolveListenerRegistrationOptions(deviceId, connectionName);
|
|
@@ -165204,9 +165377,6 @@ async function runListenSubcommand(argv) {
|
|
|
165204
165377
|
await flushListenerTelemetryEnd("listener_oauth_failed");
|
|
165205
165378
|
return 1;
|
|
165206
165379
|
}
|
|
165207
|
-
sessionLog.log(`Session started (debug=${debugMode})`);
|
|
165208
|
-
sessionLog.log(`deviceId: ${deviceId}`);
|
|
165209
|
-
sessionLog.log(`connectionName: ${connectionName}`);
|
|
165210
165380
|
if (debugMode) {
|
|
165211
165381
|
console.log(`[${formatTimestamp3()}] Registering with ${registerOptions.serverUrl}/v1/environments/register`);
|
|
165212
165382
|
console.log(`[${formatTimestamp3()}] deviceId: ${deviceId}`);
|
|
@@ -169599,4 +169769,4 @@ Error during initialization: ${message}`);
|
|
|
169599
169769
|
}
|
|
169600
169770
|
main();
|
|
169601
169771
|
|
|
169602
|
-
//# debugId=
|
|
169772
|
+
//# debugId=5F460A87866AF15264756E2164756E21
|